1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Generic on-chip SRAM allocation driver |
4 | * |
5 | * Copyright (C) 2012 Philipp Zabel, Pengutronix |
6 | */ |
7 | |
8 | #include <linux/clk.h> |
9 | #include <linux/delay.h> |
10 | #include <linux/genalloc.h> |
11 | #include <linux/io.h> |
12 | #include <linux/list_sort.h> |
13 | #include <linux/of.h> |
14 | #include <linux/of_address.h> |
15 | #include <linux/platform_device.h> |
16 | #include <linux/regmap.h> |
17 | #include <linux/slab.h> |
18 | #include <linux/mfd/syscon.h> |
19 | #include <soc/at91/atmel-secumod.h> |
20 | |
21 | #include "sram.h" |
22 | |
23 | #define SRAM_GRANULARITY 32 |
24 | |
25 | static ssize_t sram_read(struct file *filp, struct kobject *kobj, |
26 | struct bin_attribute *attr, |
27 | char *buf, loff_t pos, size_t count) |
28 | { |
29 | struct sram_partition *part; |
30 | |
31 | part = container_of(attr, struct sram_partition, battr); |
32 | |
33 | mutex_lock(&part->lock); |
34 | memcpy_fromio(buf, part->base + pos, count); |
35 | mutex_unlock(lock: &part->lock); |
36 | |
37 | return count; |
38 | } |
39 | |
40 | static ssize_t sram_write(struct file *filp, struct kobject *kobj, |
41 | struct bin_attribute *attr, |
42 | char *buf, loff_t pos, size_t count) |
43 | { |
44 | struct sram_partition *part; |
45 | |
46 | part = container_of(attr, struct sram_partition, battr); |
47 | |
48 | mutex_lock(&part->lock); |
49 | memcpy_toio(part->base + pos, buf, count); |
50 | mutex_unlock(lock: &part->lock); |
51 | |
52 | return count; |
53 | } |
54 | |
55 | static int sram_add_pool(struct sram_dev *sram, struct sram_reserve *block, |
56 | phys_addr_t start, struct sram_partition *part) |
57 | { |
58 | int ret; |
59 | |
60 | part->pool = devm_gen_pool_create(dev: sram->dev, ilog2(SRAM_GRANULARITY), |
61 | NUMA_NO_NODE, name: block->label); |
62 | if (IS_ERR(ptr: part->pool)) |
63 | return PTR_ERR(ptr: part->pool); |
64 | |
65 | ret = gen_pool_add_virt(pool: part->pool, addr: (unsigned long)part->base, phys: start, |
66 | size: block->size, NUMA_NO_NODE); |
67 | if (ret < 0) { |
68 | dev_err(sram->dev, "failed to register subpool: %d\n" , ret); |
69 | return ret; |
70 | } |
71 | |
72 | return 0; |
73 | } |
74 | |
75 | static int sram_add_export(struct sram_dev *sram, struct sram_reserve *block, |
76 | phys_addr_t start, struct sram_partition *part) |
77 | { |
78 | sysfs_bin_attr_init(&part->battr); |
79 | part->battr.attr.name = devm_kasprintf(dev: sram->dev, GFP_KERNEL, |
80 | fmt: "%llx.sram" , |
81 | (unsigned long long)start); |
82 | if (!part->battr.attr.name) |
83 | return -ENOMEM; |
84 | |
85 | part->battr.attr.mode = S_IRUSR | S_IWUSR; |
86 | part->battr.read = sram_read; |
87 | part->battr.write = sram_write; |
88 | part->battr.size = block->size; |
89 | |
90 | return device_create_bin_file(dev: sram->dev, attr: &part->battr); |
91 | } |
92 | |
93 | static int sram_add_partition(struct sram_dev *sram, struct sram_reserve *block, |
94 | phys_addr_t start) |
95 | { |
96 | int ret; |
97 | struct sram_partition *part = &sram->partition[sram->partitions]; |
98 | |
99 | mutex_init(&part->lock); |
100 | |
101 | if (sram->config && sram->config->map_only_reserved) { |
102 | void __iomem *virt_base; |
103 | |
104 | if (sram->no_memory_wc) |
105 | virt_base = devm_ioremap_resource(dev: sram->dev, res: &block->res); |
106 | else |
107 | virt_base = devm_ioremap_resource_wc(dev: sram->dev, res: &block->res); |
108 | |
109 | if (IS_ERR(ptr: virt_base)) { |
110 | dev_err(sram->dev, "could not map SRAM at %pr\n" , &block->res); |
111 | return PTR_ERR(ptr: virt_base); |
112 | } |
113 | |
114 | part->base = virt_base; |
115 | } else { |
116 | part->base = sram->virt_base + block->start; |
117 | } |
118 | |
119 | if (block->pool) { |
120 | ret = sram_add_pool(sram, block, start, part); |
121 | if (ret) |
122 | return ret; |
123 | } |
124 | if (block->export) { |
125 | ret = sram_add_export(sram, block, start, part); |
126 | if (ret) |
127 | return ret; |
128 | } |
129 | if (block->protect_exec) { |
130 | ret = sram_check_protect_exec(sram, block, part); |
131 | if (ret) |
132 | return ret; |
133 | |
134 | ret = sram_add_pool(sram, block, start, part); |
135 | if (ret) |
136 | return ret; |
137 | |
138 | sram_add_protect_exec(part); |
139 | } |
140 | |
141 | sram->partitions++; |
142 | |
143 | return 0; |
144 | } |
145 | |
146 | static void sram_free_partitions(struct sram_dev *sram) |
147 | { |
148 | struct sram_partition *part; |
149 | |
150 | if (!sram->partitions) |
151 | return; |
152 | |
153 | part = &sram->partition[sram->partitions - 1]; |
154 | for (; sram->partitions; sram->partitions--, part--) { |
155 | if (part->battr.size) |
156 | device_remove_bin_file(dev: sram->dev, attr: &part->battr); |
157 | |
158 | if (part->pool && |
159 | gen_pool_avail(part->pool) < gen_pool_size(part->pool)) |
160 | dev_err(sram->dev, "removed pool while SRAM allocated\n" ); |
161 | } |
162 | } |
163 | |
164 | static int sram_reserve_cmp(void *priv, const struct list_head *a, |
165 | const struct list_head *b) |
166 | { |
167 | struct sram_reserve *ra = list_entry(a, struct sram_reserve, list); |
168 | struct sram_reserve *rb = list_entry(b, struct sram_reserve, list); |
169 | |
170 | return ra->start - rb->start; |
171 | } |
172 | |
173 | static int sram_reserve_regions(struct sram_dev *sram, struct resource *res) |
174 | { |
175 | struct device_node *np = sram->dev->of_node, *child; |
176 | unsigned long size, cur_start, cur_size; |
177 | struct sram_reserve *rblocks, *block; |
178 | struct list_head reserve_list; |
179 | unsigned int nblocks, exports = 0; |
180 | const char *label; |
181 | int ret = 0; |
182 | |
183 | INIT_LIST_HEAD(list: &reserve_list); |
184 | |
185 | size = resource_size(res); |
186 | |
187 | /* |
188 | * We need an additional block to mark the end of the memory region |
189 | * after the reserved blocks from the dt are processed. |
190 | */ |
191 | nblocks = (np) ? of_get_available_child_count(np) + 1 : 1; |
192 | rblocks = kcalloc(n: nblocks, size: sizeof(*rblocks), GFP_KERNEL); |
193 | if (!rblocks) |
194 | return -ENOMEM; |
195 | |
196 | block = &rblocks[0]; |
197 | for_each_available_child_of_node(np, child) { |
198 | struct resource child_res; |
199 | |
200 | ret = of_address_to_resource(dev: child, index: 0, r: &child_res); |
201 | if (ret < 0) { |
202 | dev_err(sram->dev, |
203 | "could not get address for node %pOF\n" , |
204 | child); |
205 | goto err_chunks; |
206 | } |
207 | |
208 | if (child_res.start < res->start || child_res.end > res->end) { |
209 | dev_err(sram->dev, |
210 | "reserved block %pOF outside the sram area\n" , |
211 | child); |
212 | ret = -EINVAL; |
213 | goto err_chunks; |
214 | } |
215 | |
216 | block->start = child_res.start - res->start; |
217 | block->size = resource_size(res: &child_res); |
218 | block->res = child_res; |
219 | list_add_tail(new: &block->list, head: &reserve_list); |
220 | |
221 | block->export = of_property_read_bool(np: child, propname: "export" ); |
222 | block->pool = of_property_read_bool(np: child, propname: "pool" ); |
223 | block->protect_exec = of_property_read_bool(np: child, propname: "protect-exec" ); |
224 | |
225 | if ((block->export || block->pool || block->protect_exec) && |
226 | block->size) { |
227 | exports++; |
228 | |
229 | label = NULL; |
230 | ret = of_property_read_string(np: child, propname: "label" , out_string: &label); |
231 | if (ret && ret != -EINVAL) { |
232 | dev_err(sram->dev, |
233 | "%pOF has invalid label name\n" , |
234 | child); |
235 | goto err_chunks; |
236 | } |
237 | if (!label) |
238 | block->label = devm_kasprintf(dev: sram->dev, GFP_KERNEL, |
239 | fmt: "%s" , of_node_full_name(np: child)); |
240 | else |
241 | block->label = devm_kstrdup(dev: sram->dev, |
242 | s: label, GFP_KERNEL); |
243 | if (!block->label) { |
244 | ret = -ENOMEM; |
245 | goto err_chunks; |
246 | } |
247 | |
248 | dev_dbg(sram->dev, "found %sblock '%s' 0x%x-0x%x\n" , |
249 | block->export ? "exported " : "" , block->label, |
250 | block->start, block->start + block->size); |
251 | } else { |
252 | dev_dbg(sram->dev, "found reserved block 0x%x-0x%x\n" , |
253 | block->start, block->start + block->size); |
254 | } |
255 | |
256 | block++; |
257 | } |
258 | child = NULL; |
259 | |
260 | /* the last chunk marks the end of the region */ |
261 | rblocks[nblocks - 1].start = size; |
262 | rblocks[nblocks - 1].size = 0; |
263 | list_add_tail(new: &rblocks[nblocks - 1].list, head: &reserve_list); |
264 | |
265 | list_sort(NULL, head: &reserve_list, cmp: sram_reserve_cmp); |
266 | |
267 | if (exports) { |
268 | sram->partition = devm_kcalloc(dev: sram->dev, |
269 | n: exports, size: sizeof(*sram->partition), |
270 | GFP_KERNEL); |
271 | if (!sram->partition) { |
272 | ret = -ENOMEM; |
273 | goto err_chunks; |
274 | } |
275 | } |
276 | |
277 | cur_start = 0; |
278 | list_for_each_entry(block, &reserve_list, list) { |
279 | /* can only happen if sections overlap */ |
280 | if (block->start < cur_start) { |
281 | dev_err(sram->dev, |
282 | "block at 0x%x starts after current offset 0x%lx\n" , |
283 | block->start, cur_start); |
284 | ret = -EINVAL; |
285 | sram_free_partitions(sram); |
286 | goto err_chunks; |
287 | } |
288 | |
289 | if ((block->export || block->pool || block->protect_exec) && |
290 | block->size) { |
291 | ret = sram_add_partition(sram, block, |
292 | start: res->start + block->start); |
293 | if (ret) { |
294 | sram_free_partitions(sram); |
295 | goto err_chunks; |
296 | } |
297 | } |
298 | |
299 | /* current start is in a reserved block, so continue after it */ |
300 | if (block->start == cur_start) { |
301 | cur_start = block->start + block->size; |
302 | continue; |
303 | } |
304 | |
305 | /* |
306 | * allocate the space between the current starting |
307 | * address and the following reserved block, or the |
308 | * end of the region. |
309 | */ |
310 | cur_size = block->start - cur_start; |
311 | |
312 | if (sram->pool) { |
313 | dev_dbg(sram->dev, "adding chunk 0x%lx-0x%lx\n" , |
314 | cur_start, cur_start + cur_size); |
315 | |
316 | ret = gen_pool_add_virt(pool: sram->pool, |
317 | addr: (unsigned long)sram->virt_base + cur_start, |
318 | phys: res->start + cur_start, size: cur_size, nid: -1); |
319 | if (ret < 0) { |
320 | sram_free_partitions(sram); |
321 | goto err_chunks; |
322 | } |
323 | } |
324 | |
325 | /* next allocation after this reserved block */ |
326 | cur_start = block->start + block->size; |
327 | } |
328 | |
329 | err_chunks: |
330 | of_node_put(node: child); |
331 | kfree(objp: rblocks); |
332 | |
333 | return ret; |
334 | } |
335 | |
336 | static int atmel_securam_wait(void) |
337 | { |
338 | struct regmap *regmap; |
339 | u32 val; |
340 | |
341 | regmap = syscon_regmap_lookup_by_compatible(s: "atmel,sama5d2-secumod" ); |
342 | if (IS_ERR(ptr: regmap)) |
343 | return -ENODEV; |
344 | |
345 | return regmap_read_poll_timeout(regmap, AT91_SECUMOD_RAMRDY, val, |
346 | val & AT91_SECUMOD_RAMRDY_READY, |
347 | 10000, 500000); |
348 | } |
349 | |
350 | static const struct sram_config atmel_securam_config = { |
351 | .init = atmel_securam_wait, |
352 | }; |
353 | |
354 | /* |
355 | * SYSRAM contains areas that are not accessible by the |
356 | * kernel, such as the first 256K that is reserved for TZ. |
357 | * Accesses to those areas (including speculative accesses) |
358 | * trigger SErrors. As such we must map only the areas of |
359 | * SYSRAM specified in the device tree. |
360 | */ |
361 | static const struct sram_config tegra_sysram_config = { |
362 | .map_only_reserved = true, |
363 | }; |
364 | |
365 | static const struct of_device_id sram_dt_ids[] = { |
366 | { .compatible = "mmio-sram" }, |
367 | { .compatible = "atmel,sama5d2-securam" , .data = &atmel_securam_config }, |
368 | { .compatible = "nvidia,tegra186-sysram" , .data = &tegra_sysram_config }, |
369 | { .compatible = "nvidia,tegra194-sysram" , .data = &tegra_sysram_config }, |
370 | { .compatible = "nvidia,tegra234-sysram" , .data = &tegra_sysram_config }, |
371 | {} |
372 | }; |
373 | |
374 | static int sram_probe(struct platform_device *pdev) |
375 | { |
376 | const struct sram_config *config; |
377 | struct sram_dev *sram; |
378 | int ret; |
379 | struct resource *res; |
380 | struct clk *clk; |
381 | |
382 | config = of_device_get_match_data(dev: &pdev->dev); |
383 | |
384 | sram = devm_kzalloc(dev: &pdev->dev, size: sizeof(*sram), GFP_KERNEL); |
385 | if (!sram) |
386 | return -ENOMEM; |
387 | |
388 | sram->dev = &pdev->dev; |
389 | sram->no_memory_wc = of_property_read_bool(np: pdev->dev.of_node, propname: "no-memory-wc" ); |
390 | sram->config = config; |
391 | |
392 | if (!config || !config->map_only_reserved) { |
393 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
394 | if (sram->no_memory_wc) |
395 | sram->virt_base = devm_ioremap_resource(dev: &pdev->dev, res); |
396 | else |
397 | sram->virt_base = devm_ioremap_resource_wc(dev: &pdev->dev, res); |
398 | if (IS_ERR(ptr: sram->virt_base)) { |
399 | dev_err(&pdev->dev, "could not map SRAM registers\n" ); |
400 | return PTR_ERR(ptr: sram->virt_base); |
401 | } |
402 | |
403 | sram->pool = devm_gen_pool_create(dev: sram->dev, ilog2(SRAM_GRANULARITY), |
404 | NUMA_NO_NODE, NULL); |
405 | if (IS_ERR(ptr: sram->pool)) |
406 | return PTR_ERR(ptr: sram->pool); |
407 | } |
408 | |
409 | clk = devm_clk_get_optional_enabled(dev: sram->dev, NULL); |
410 | if (IS_ERR(ptr: clk)) |
411 | return PTR_ERR(ptr: clk); |
412 | |
413 | ret = sram_reserve_regions(sram, |
414 | res: platform_get_resource(pdev, IORESOURCE_MEM, 0)); |
415 | if (ret) |
416 | return ret; |
417 | |
418 | platform_set_drvdata(pdev, data: sram); |
419 | |
420 | if (config && config->init) { |
421 | ret = config->init(); |
422 | if (ret) |
423 | goto err_free_partitions; |
424 | } |
425 | |
426 | if (sram->pool) |
427 | dev_dbg(sram->dev, "SRAM pool: %zu KiB @ 0x%p\n" , |
428 | gen_pool_size(sram->pool) / 1024, sram->virt_base); |
429 | |
430 | return 0; |
431 | |
432 | err_free_partitions: |
433 | sram_free_partitions(sram); |
434 | |
435 | return ret; |
436 | } |
437 | |
438 | static void sram_remove(struct platform_device *pdev) |
439 | { |
440 | struct sram_dev *sram = platform_get_drvdata(pdev); |
441 | |
442 | sram_free_partitions(sram); |
443 | |
444 | if (sram->pool && gen_pool_avail(sram->pool) < gen_pool_size(sram->pool)) |
445 | dev_err(sram->dev, "removed while SRAM allocated\n" ); |
446 | } |
447 | |
448 | static struct platform_driver sram_driver = { |
449 | .driver = { |
450 | .name = "sram" , |
451 | .of_match_table = sram_dt_ids, |
452 | }, |
453 | .probe = sram_probe, |
454 | .remove_new = sram_remove, |
455 | }; |
456 | |
457 | static int __init sram_init(void) |
458 | { |
459 | return platform_driver_register(&sram_driver); |
460 | } |
461 | |
462 | postcore_initcall(sram_init); |
463 | |