diff options
author | Marek Szyprowski <m.szyprowski@samsung.com> | 2014-02-28 08:42:48 -0500 |
---|---|---|
committer | Grant Likely <grant.likely@linaro.org> | 2014-03-11 13:26:58 -0400 |
commit | 3f0c8206644836e4f10a6b9fc47cda6a9a372f9b (patch) | |
tree | c85e72e465074dd94761f8a685a2110a3b7f8db9 /drivers/of | |
parent | e8d9d1f5485b52ec3c4d7af839e6914438f6c285 (diff) |
drivers: of: add initialization code for dynamic reserved memory
This patch adds support for dynamically allocated reserved memory regions
declared in device tree. Such regions are defined by 'size', 'alignment'
and 'alloc-ranges' properties.
Based on previous code provided by Josh Cartwright <joshc@codeaurora.org>
Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
Signed-off-by: Grant Likely <grant.likely@linaro.org>
Diffstat (limited to 'drivers/of')
-rw-r--r-- | drivers/of/Kconfig | 6 | ||||
-rw-r--r-- | drivers/of/Makefile | 1 | ||||
-rw-r--r-- | drivers/of/fdt.c | 13 | ||||
-rw-r--r-- | drivers/of/of_reserved_mem.c | 188 |
4 files changed, 206 insertions, 2 deletions
diff --git a/drivers/of/Kconfig b/drivers/of/Kconfig index ffdcb11f75fb..c144b8f990ff 100644 --- a/drivers/of/Kconfig +++ b/drivers/of/Kconfig | |||
@@ -79,4 +79,10 @@ config OF_MTD | |||
79 | depends on MTD | 79 | depends on MTD |
80 | def_bool y | 80 | def_bool y |
81 | 81 | ||
82 | config OF_RESERVED_MEM | ||
83 | depends on OF_EARLY_FLATTREE | ||
84 | bool | ||
85 | help | ||
86 | Helpers to allow for reservation of memory regions | ||
87 | |||
82 | endmenu # OF | 88 | endmenu # OF |
diff --git a/drivers/of/Makefile b/drivers/of/Makefile index efd05102c405..ed9660adad77 100644 --- a/drivers/of/Makefile +++ b/drivers/of/Makefile | |||
@@ -9,3 +9,4 @@ obj-$(CONFIG_OF_MDIO) += of_mdio.o | |||
9 | obj-$(CONFIG_OF_PCI) += of_pci.o | 9 | obj-$(CONFIG_OF_PCI) += of_pci.o |
10 | obj-$(CONFIG_OF_PCI_IRQ) += of_pci_irq.o | 10 | obj-$(CONFIG_OF_PCI_IRQ) += of_pci_irq.o |
11 | obj-$(CONFIG_OF_MTD) += of_mtd.o | 11 | obj-$(CONFIG_OF_MTD) += of_mtd.o |
12 | obj-$(CONFIG_OF_RESERVED_MEM) += of_reserved_mem.o | ||
diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c index 819e11209718..510c0d8de8a0 100644 --- a/drivers/of/fdt.c +++ b/drivers/of/fdt.c | |||
@@ -15,6 +15,7 @@ | |||
15 | #include <linux/module.h> | 15 | #include <linux/module.h> |
16 | #include <linux/of.h> | 16 | #include <linux/of.h> |
17 | #include <linux/of_fdt.h> | 17 | #include <linux/of_fdt.h> |
18 | #include <linux/of_reserved_mem.h> | ||
18 | #include <linux/sizes.h> | 19 | #include <linux/sizes.h> |
19 | #include <linux/string.h> | 20 | #include <linux/string.h> |
20 | #include <linux/errno.h> | 21 | #include <linux/errno.h> |
@@ -450,7 +451,7 @@ static int __init __reserved_mem_reserve_reg(unsigned long node, | |||
450 | phys_addr_t base, size; | 451 | phys_addr_t base, size; |
451 | unsigned long len; | 452 | unsigned long len; |
452 | __be32 *prop; | 453 | __be32 *prop; |
453 | int nomap; | 454 | int nomap, first = 1; |
454 | 455 | ||
455 | prop = of_get_flat_dt_prop(node, "reg", &len); | 456 | prop = of_get_flat_dt_prop(node, "reg", &len); |
456 | if (!prop) | 457 | if (!prop) |
@@ -477,6 +478,10 @@ static int __init __reserved_mem_reserve_reg(unsigned long node, | |||
477 | uname, &base, (unsigned long)size / SZ_1M); | 478 | uname, &base, (unsigned long)size / SZ_1M); |
478 | 479 | ||
479 | len -= t_len; | 480 | len -= t_len; |
481 | if (first) { | ||
482 | fdt_reserved_mem_save_node(node, uname, base, size); | ||
483 | first = 0; | ||
484 | } | ||
480 | } | 485 | } |
481 | return 0; | 486 | return 0; |
482 | } | 487 | } |
@@ -512,6 +517,7 @@ static int __init __fdt_scan_reserved_mem(unsigned long node, const char *uname, | |||
512 | { | 517 | { |
513 | static int found; | 518 | static int found; |
514 | const char *status; | 519 | const char *status; |
520 | int err; | ||
515 | 521 | ||
516 | if (!found && depth == 1 && strcmp(uname, "reserved-memory") == 0) { | 522 | if (!found && depth == 1 && strcmp(uname, "reserved-memory") == 0) { |
517 | if (__reserved_mem_check_root(node) != 0) { | 523 | if (__reserved_mem_check_root(node) != 0) { |
@@ -534,7 +540,9 @@ static int __init __fdt_scan_reserved_mem(unsigned long node, const char *uname, | |||
534 | if (status && strcmp(status, "okay") != 0 && strcmp(status, "ok") != 0) | 540 | if (status && strcmp(status, "okay") != 0 && strcmp(status, "ok") != 0) |
535 | return 0; | 541 | return 0; |
536 | 542 | ||
537 | __reserved_mem_reserve_reg(node, uname); | 543 | err = __reserved_mem_reserve_reg(node, uname); |
544 | if (err == -ENOENT && of_get_flat_dt_prop(node, "size", NULL)) | ||
545 | fdt_reserved_mem_save_node(node, uname, 0, 0); | ||
538 | 546 | ||
539 | /* scan next node */ | 547 | /* scan next node */ |
540 | return 0; | 548 | return 0; |
@@ -550,6 +558,7 @@ static int __init __fdt_scan_reserved_mem(unsigned long node, const char *uname, | |||
550 | void __init early_init_fdt_scan_reserved_mem(void) | 558 | void __init early_init_fdt_scan_reserved_mem(void) |
551 | { | 559 | { |
552 | of_scan_flat_dt(__fdt_scan_reserved_mem, NULL); | 560 | of_scan_flat_dt(__fdt_scan_reserved_mem, NULL); |
561 | fdt_init_reserved_mem(); | ||
553 | } | 562 | } |
554 | 563 | ||
555 | /** | 564 | /** |
diff --git a/drivers/of/of_reserved_mem.c b/drivers/of/of_reserved_mem.c new file mode 100644 index 000000000000..69b811779585 --- /dev/null +++ b/drivers/of/of_reserved_mem.c | |||
@@ -0,0 +1,188 @@ | |||
1 | /* | ||
2 | * Device tree based initialization code for reserved memory. | ||
3 | * | ||
4 | * Copyright (c) 2013, The Linux Foundation. All Rights Reserved. | ||
5 | * Copyright (c) 2013,2014 Samsung Electronics Co., Ltd. | ||
6 | * http://www.samsung.com | ||
7 | * Author: Marek Szyprowski <m.szyprowski@samsung.com> | ||
8 | * Author: Josh Cartwright <joshc@codeaurora.org> | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or | ||
11 | * modify it under the terms of the GNU General Public License as | ||
12 | * published by the Free Software Foundation; either version 2 of the | ||
13 | * License or (at your optional) any later version of the license. | ||
14 | */ | ||
15 | |||
16 | #include <linux/err.h> | ||
17 | #include <linux/of.h> | ||
18 | #include <linux/of_fdt.h> | ||
19 | #include <linux/of_platform.h> | ||
20 | #include <linux/mm.h> | ||
21 | #include <linux/sizes.h> | ||
22 | #include <linux/of_reserved_mem.h> | ||
23 | |||
24 | #define MAX_RESERVED_REGIONS 16 | ||
25 | static struct reserved_mem reserved_mem[MAX_RESERVED_REGIONS]; | ||
26 | static int reserved_mem_count; | ||
27 | |||
28 | #if defined(CONFIG_HAVE_MEMBLOCK) | ||
29 | #include <linux/memblock.h> | ||
30 | int __init __weak early_init_dt_alloc_reserved_memory_arch(phys_addr_t size, | ||
31 | phys_addr_t align, phys_addr_t start, phys_addr_t end, bool nomap, | ||
32 | phys_addr_t *res_base) | ||
33 | { | ||
34 | /* | ||
35 | * We use __memblock_alloc_base() because memblock_alloc_base() | ||
36 | * panic()s on allocation failure. | ||
37 | */ | ||
38 | phys_addr_t base = __memblock_alloc_base(size, align, end); | ||
39 | if (!base) | ||
40 | return -ENOMEM; | ||
41 | |||
42 | /* | ||
43 | * Check if the allocated region fits in to start..end window | ||
44 | */ | ||
45 | if (base < start) { | ||
46 | memblock_free(base, size); | ||
47 | return -ENOMEM; | ||
48 | } | ||
49 | |||
50 | *res_base = base; | ||
51 | if (nomap) | ||
52 | return memblock_remove(base, size); | ||
53 | return 0; | ||
54 | } | ||
55 | #else | ||
56 | int __init __weak early_init_dt_alloc_reserved_memory_arch(phys_addr_t size, | ||
57 | phys_addr_t align, phys_addr_t start, phys_addr_t end, bool nomap, | ||
58 | phys_addr_t *res_base) | ||
59 | { | ||
60 | pr_err("Reserved memory not supported, ignoring region 0x%llx%s\n", | ||
61 | size, nomap ? " (nomap)" : ""); | ||
62 | return -ENOSYS; | ||
63 | } | ||
64 | #endif | ||
65 | |||
66 | /** | ||
67 | * res_mem_save_node() - save fdt node for second pass initialization | ||
68 | */ | ||
69 | void __init fdt_reserved_mem_save_node(unsigned long node, const char *uname, | ||
70 | phys_addr_t base, phys_addr_t size) | ||
71 | { | ||
72 | struct reserved_mem *rmem = &reserved_mem[reserved_mem_count]; | ||
73 | |||
74 | if (reserved_mem_count == ARRAY_SIZE(reserved_mem)) { | ||
75 | pr_err("Reserved memory: not enough space all defined regions.\n"); | ||
76 | return; | ||
77 | } | ||
78 | |||
79 | rmem->fdt_node = node; | ||
80 | rmem->name = uname; | ||
81 | rmem->base = base; | ||
82 | rmem->size = size; | ||
83 | |||
84 | reserved_mem_count++; | ||
85 | return; | ||
86 | } | ||
87 | |||
88 | /** | ||
89 | * res_mem_alloc_size() - allocate reserved memory described by 'size', 'align' | ||
90 | * and 'alloc-ranges' properties | ||
91 | */ | ||
92 | static int __init __reserved_mem_alloc_size(unsigned long node, | ||
93 | const char *uname, phys_addr_t *res_base, phys_addr_t *res_size) | ||
94 | { | ||
95 | int t_len = (dt_root_addr_cells + dt_root_size_cells) * sizeof(__be32); | ||
96 | phys_addr_t start = 0, end = 0; | ||
97 | phys_addr_t base = 0, align = 0, size; | ||
98 | unsigned long len; | ||
99 | __be32 *prop; | ||
100 | int nomap; | ||
101 | int ret; | ||
102 | |||
103 | prop = of_get_flat_dt_prop(node, "size", &len); | ||
104 | if (!prop) | ||
105 | return -EINVAL; | ||
106 | |||
107 | if (len != dt_root_size_cells * sizeof(__be32)) { | ||
108 | pr_err("Reserved memory: invalid size property in '%s' node.\n", | ||
109 | uname); | ||
110 | return -EINVAL; | ||
111 | } | ||
112 | size = dt_mem_next_cell(dt_root_size_cells, &prop); | ||
113 | |||
114 | nomap = of_get_flat_dt_prop(node, "no-map", NULL) != NULL; | ||
115 | |||
116 | prop = of_get_flat_dt_prop(node, "alignment", &len); | ||
117 | if (prop) { | ||
118 | if (len != dt_root_addr_cells * sizeof(__be32)) { | ||
119 | pr_err("Reserved memory: invalid alignment property in '%s' node.\n", | ||
120 | uname); | ||
121 | return -EINVAL; | ||
122 | } | ||
123 | align = dt_mem_next_cell(dt_root_addr_cells, &prop); | ||
124 | } | ||
125 | |||
126 | prop = of_get_flat_dt_prop(node, "alloc-ranges", &len); | ||
127 | if (prop) { | ||
128 | |||
129 | if (len % t_len != 0) { | ||
130 | pr_err("Reserved memory: invalid alloc-ranges property in '%s', skipping node.\n", | ||
131 | uname); | ||
132 | return -EINVAL; | ||
133 | } | ||
134 | |||
135 | base = 0; | ||
136 | |||
137 | while (len > 0) { | ||
138 | start = dt_mem_next_cell(dt_root_addr_cells, &prop); | ||
139 | end = start + dt_mem_next_cell(dt_root_size_cells, | ||
140 | &prop); | ||
141 | |||
142 | ret = early_init_dt_alloc_reserved_memory_arch(size, | ||
143 | align, start, end, nomap, &base); | ||
144 | if (ret == 0) { | ||
145 | pr_debug("Reserved memory: allocated memory for '%s' node: base %pa, size %ld MiB\n", | ||
146 | uname, &base, | ||
147 | (unsigned long)size / SZ_1M); | ||
148 | break; | ||
149 | } | ||
150 | len -= t_len; | ||
151 | } | ||
152 | |||
153 | } else { | ||
154 | ret = early_init_dt_alloc_reserved_memory_arch(size, align, | ||
155 | 0, 0, nomap, &base); | ||
156 | if (ret == 0) | ||
157 | pr_debug("Reserved memory: allocated memory for '%s' node: base %pa, size %ld MiB\n", | ||
158 | uname, &base, (unsigned long)size / SZ_1M); | ||
159 | } | ||
160 | |||
161 | if (base == 0) { | ||
162 | pr_info("Reserved memory: failed to allocate memory for node '%s'\n", | ||
163 | uname); | ||
164 | return -ENOMEM; | ||
165 | } | ||
166 | |||
167 | *res_base = base; | ||
168 | *res_size = size; | ||
169 | |||
170 | return 0; | ||
171 | } | ||
172 | |||
173 | /** | ||
174 | * fdt_init_reserved_mem - allocate and init all saved reserved memory regions | ||
175 | */ | ||
176 | void __init fdt_init_reserved_mem(void) | ||
177 | { | ||
178 | int i; | ||
179 | for (i = 0; i < reserved_mem_count; i++) { | ||
180 | struct reserved_mem *rmem = &reserved_mem[i]; | ||
181 | unsigned long node = rmem->fdt_node; | ||
182 | int err = 0; | ||
183 | |||
184 | if (rmem->size == 0) | ||
185 | err = __reserved_mem_alloc_size(node, rmem->name, | ||
186 | &rmem->base, &rmem->size); | ||
187 | } | ||
188 | } | ||