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/of_reserved_mem.c | |
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/of_reserved_mem.c')
-rw-r--r-- | drivers/of/of_reserved_mem.c | 188 |
1 files changed, 188 insertions, 0 deletions
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 | } | ||