Blame view

drivers/misc/sram.c 10.3 KB
162163332   Thomas Gleixner   treewide: Replace...
1
  // SPDX-License-Identifier: GPL-2.0-or-later
4984c6f5e   Philipp Zabel   misc: generic on-...
2
3
4
5
  /*
   * Generic on-chip SRAM allocation driver
   *
   * Copyright (C) 2012 Philipp Zabel, Pengutronix
4984c6f5e   Philipp Zabel   misc: generic on-...
6
   */
4984c6f5e   Philipp Zabel   misc: generic on-...
7
  #include <linux/clk.h>
2ae2e2885   Alexandre Belloni   misc: sram: add A...
8
  #include <linux/delay.h>
98ce2d27a   Vladimir Zapolskiy   misc: sram: sort ...
9
  #include <linux/genalloc.h>
4984c6f5e   Philipp Zabel   misc: generic on-...
10
  #include <linux/io.h>
2da19688f   Heiko Stübner   misc: sram: imple...
11
  #include <linux/list_sort.h>
98ce2d27a   Vladimir Zapolskiy   misc: sram: sort ...
12
  #include <linux/of_address.h>
2ae2e2885   Alexandre Belloni   misc: sram: add A...
13
  #include <linux/of_device.h>
4984c6f5e   Philipp Zabel   misc: generic on-...
14
  #include <linux/platform_device.h>
2ae2e2885   Alexandre Belloni   misc: sram: add A...
15
  #include <linux/regmap.h>
4984c6f5e   Philipp Zabel   misc: generic on-...
16
  #include <linux/slab.h>
2ae2e2885   Alexandre Belloni   misc: sram: add A...
17
18
  #include <linux/mfd/syscon.h>
  #include <soc/at91/atmel-secumod.h>
4984c6f5e   Philipp Zabel   misc: generic on-...
19

cdd1737cb   Dave Gerlach   misc: sram: Split...
20
  #include "sram.h"
665d82fbb   Vladimir Zapolskiy   misc: sram: add p...
21

cdd1737cb   Dave Gerlach   misc: sram: Split...
22
  #define SRAM_GRANULARITY	32
2da19688f   Heiko Stübner   misc: sram: imple...
23

b4c3fcb3c   Vladimir Zapolskiy   misc: sram: exten...
24
25
26
27
28
29
30
31
32
  static ssize_t sram_read(struct file *filp, struct kobject *kobj,
  			 struct bin_attribute *attr,
  			 char *buf, loff_t pos, size_t count)
  {
  	struct sram_partition *part;
  
  	part = container_of(attr, struct sram_partition, battr);
  
  	mutex_lock(&part->lock);
525d12f27   Vladimir Zapolskiy   misc: sram: parti...
33
  	memcpy_fromio(buf, part->base + pos, count);
b4c3fcb3c   Vladimir Zapolskiy   misc: sram: exten...
34
35
36
37
38
39
40
41
42
43
44
45
46
47
  	mutex_unlock(&part->lock);
  
  	return count;
  }
  
  static ssize_t sram_write(struct file *filp, struct kobject *kobj,
  			  struct bin_attribute *attr,
  			  char *buf, loff_t pos, size_t count)
  {
  	struct sram_partition *part;
  
  	part = container_of(attr, struct sram_partition, battr);
  
  	mutex_lock(&part->lock);
525d12f27   Vladimir Zapolskiy   misc: sram: parti...
48
  	memcpy_toio(part->base + pos, buf, count);
b4c3fcb3c   Vladimir Zapolskiy   misc: sram: exten...
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
  	mutex_unlock(&part->lock);
  
  	return count;
  }
  
  static int sram_add_pool(struct sram_dev *sram, struct sram_reserve *block,
  			 phys_addr_t start, struct sram_partition *part)
  {
  	int ret;
  
  	part->pool = devm_gen_pool_create(sram->dev, ilog2(SRAM_GRANULARITY),
  					  NUMA_NO_NODE, block->label);
  	if (IS_ERR(part->pool))
  		return PTR_ERR(part->pool);
  
  	ret = gen_pool_add_virt(part->pool, (unsigned long)part->base, start,
  				block->size, NUMA_NO_NODE);
  	if (ret < 0) {
  		dev_err(sram->dev, "failed to register subpool: %d
  ", ret);
  		return ret;
  	}
  
  	return 0;
  }
  
  static int sram_add_export(struct sram_dev *sram, struct sram_reserve *block,
  			   phys_addr_t start, struct sram_partition *part)
  {
  	sysfs_bin_attr_init(&part->battr);
  	part->battr.attr.name = devm_kasprintf(sram->dev, GFP_KERNEL,
  					       "%llx.sram",
  					       (unsigned long long)start);
  	if (!part->battr.attr.name)
  		return -ENOMEM;
  
  	part->battr.attr.mode = S_IRUSR | S_IWUSR;
  	part->battr.read = sram_read;
  	part->battr.write = sram_write;
  	part->battr.size = block->size;
  
  	return device_create_bin_file(sram->dev, &part->battr);
  }
  
  static int sram_add_partition(struct sram_dev *sram, struct sram_reserve *block,
  			      phys_addr_t start)
  {
  	int ret;
  	struct sram_partition *part = &sram->partition[sram->partitions];
  
  	mutex_init(&part->lock);
  	part->base = sram->virt_base + block->start;
  
  	if (block->pool) {
  		ret = sram_add_pool(sram, block, start, part);
  		if (ret)
  			return ret;
  	}
  	if (block->export) {
  		ret = sram_add_export(sram, block, start, part);
  		if (ret)
  			return ret;
  	}
37afff0d8   Dave Gerlach   misc: sram: Integ...
112
113
114
115
116
117
118
119
120
121
122
  	if (block->protect_exec) {
  		ret = sram_check_protect_exec(sram, block, part);
  		if (ret)
  			return ret;
  
  		ret = sram_add_pool(sram, block, start, part);
  		if (ret)
  			return ret;
  
  		sram_add_protect_exec(part);
  	}
b4c3fcb3c   Vladimir Zapolskiy   misc: sram: exten...
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
  	sram->partitions++;
  
  	return 0;
  }
  
  static void sram_free_partitions(struct sram_dev *sram)
  {
  	struct sram_partition *part;
  
  	if (!sram->partitions)
  		return;
  
  	part = &sram->partition[sram->partitions - 1];
  	for (; sram->partitions; sram->partitions--, part--) {
  		if (part->battr.size)
  			device_remove_bin_file(sram->dev, &part->battr);
  
  		if (part->pool &&
  		    gen_pool_avail(part->pool) < gen_pool_size(part->pool))
  			dev_err(sram->dev, "removed pool while SRAM allocated
  ");
  	}
  }
2da19688f   Heiko Stübner   misc: sram: imple...
146
147
148
149
150
151
152
153
  static int sram_reserve_cmp(void *priv, struct list_head *a,
  					struct list_head *b)
  {
  	struct sram_reserve *ra = list_entry(a, struct sram_reserve, list);
  	struct sram_reserve *rb = list_entry(b, struct sram_reserve, list);
  
  	return ra->start - rb->start;
  }
a0a5be0b3   Vladimir Zapolskiy   misc: sram: move ...
154
  static int sram_reserve_regions(struct sram_dev *sram, struct resource *res)
4984c6f5e   Philipp Zabel   misc: generic on-...
155
  {
a0a5be0b3   Vladimir Zapolskiy   misc: sram: move ...
156
  	struct device_node *np = sram->dev->of_node, *child;
2da19688f   Heiko Stübner   misc: sram: imple...
157
158
159
  	unsigned long size, cur_start, cur_size;
  	struct sram_reserve *rblocks, *block;
  	struct list_head reserve_list;
b4c3fcb3c   Vladimir Zapolskiy   misc: sram: exten...
160
161
  	unsigned int nblocks, exports = 0;
  	const char *label;
a0a5be0b3   Vladimir Zapolskiy   misc: sram: move ...
162
  	int ret = 0;
4984c6f5e   Philipp Zabel   misc: generic on-...
163

2da19688f   Heiko Stübner   misc: sram: imple...
164
  	INIT_LIST_HEAD(&reserve_list);
f3cbfa5d6   Alexander Shiyan   Misc: sram: Remov...
165
  	size = resource_size(res);
4984c6f5e   Philipp Zabel   misc: generic on-...
166

2da19688f   Heiko Stübner   misc: sram: imple...
167
168
169
170
171
  	/*
  	 * We need an additional block to mark the end of the memory region
  	 * after the reserved blocks from the dt are processed.
  	 */
  	nblocks = (np) ? of_get_available_child_count(np) + 1 : 1;
6396bb221   Kees Cook   treewide: kzalloc...
172
  	rblocks = kcalloc(nblocks, sizeof(*rblocks), GFP_KERNEL);
ee895ccdf   Vladimir Zapolskiy   misc: sram: fix e...
173
174
  	if (!rblocks)
  		return -ENOMEM;
4984c6f5e   Philipp Zabel   misc: generic on-...
175

2da19688f   Heiko Stübner   misc: sram: imple...
176
177
178
179
180
181
  	block = &rblocks[0];
  	for_each_available_child_of_node(np, child) {
  		struct resource child_res;
  
  		ret = of_address_to_resource(child, 0, &child_res);
  		if (ret < 0) {
665d82fbb   Vladimir Zapolskiy   misc: sram: add p...
182
  			dev_err(sram->dev,
34d0eb50b   Rob Herring   misc: Convert to ...
183
184
185
  				"could not get address for node %pOF
  ",
  				child);
2da19688f   Heiko Stübner   misc: sram: imple...
186
187
188
189
  			goto err_chunks;
  		}
  
  		if (child_res.start < res->start || child_res.end > res->end) {
665d82fbb   Vladimir Zapolskiy   misc: sram: add p...
190
  			dev_err(sram->dev,
34d0eb50b   Rob Herring   misc: Convert to ...
191
192
193
  				"reserved block %pOF outside the sram area
  ",
  				child);
2da19688f   Heiko Stübner   misc: sram: imple...
194
195
196
197
198
199
200
  			ret = -EINVAL;
  			goto err_chunks;
  		}
  
  		block->start = child_res.start - res->start;
  		block->size = resource_size(&child_res);
  		list_add_tail(&block->list, &reserve_list);
b4c3fcb3c   Vladimir Zapolskiy   misc: sram: exten...
201
202
203
204
205
  		if (of_find_property(child, "export", NULL))
  			block->export = true;
  
  		if (of_find_property(child, "pool", NULL))
  			block->pool = true;
37afff0d8   Dave Gerlach   misc: sram: Integ...
206
207
208
209
210
  		if (of_find_property(child, "protect-exec", NULL))
  			block->protect_exec = true;
  
  		if ((block->export || block->pool || block->protect_exec) &&
  		    block->size) {
b4c3fcb3c   Vladimir Zapolskiy   misc: sram: exten...
211
212
213
214
215
216
  			exports++;
  
  			label = NULL;
  			ret = of_property_read_string(child, "label", &label);
  			if (ret && ret != -EINVAL) {
  				dev_err(sram->dev,
34d0eb50b   Rob Herring   misc: Convert to ...
217
218
219
  					"%pOF has invalid label name
  ",
  					child);
b4c3fcb3c   Vladimir Zapolskiy   misc: sram: exten...
220
221
222
223
224
225
226
  				goto err_chunks;
  			}
  			if (!label)
  				label = child->name;
  
  			block->label = devm_kstrdup(sram->dev,
  						    label, GFP_KERNEL);
ddc5c9a37   Pan Bian   misc: set error c...
227
228
  			if (!block->label) {
  				ret = -ENOMEM;
b4c3fcb3c   Vladimir Zapolskiy   misc: sram: exten...
229
  				goto err_chunks;
ddc5c9a37   Pan Bian   misc: set error c...
230
  			}
b4c3fcb3c   Vladimir Zapolskiy   misc: sram: exten...
231
232
233
234
235
236
237
238
239
240
  
  			dev_dbg(sram->dev, "found %sblock '%s' 0x%x-0x%x
  ",
  				block->export ? "exported " : "", block->label,
  				block->start, block->start + block->size);
  		} else {
  			dev_dbg(sram->dev, "found reserved block 0x%x-0x%x
  ",
  				block->start, block->start + block->size);
  		}
2da19688f   Heiko Stübner   misc: sram: imple...
241
242
243
  
  		block++;
  	}
b4c3fcb3c   Vladimir Zapolskiy   misc: sram: exten...
244
  	child = NULL;
2da19688f   Heiko Stübner   misc: sram: imple...
245
246
247
248
249
250
251
  
  	/* the last chunk marks the end of the region */
  	rblocks[nblocks - 1].start = size;
  	rblocks[nblocks - 1].size = 0;
  	list_add_tail(&rblocks[nblocks - 1].list, &reserve_list);
  
  	list_sort(NULL, &reserve_list, sram_reserve_cmp);
b4c3fcb3c   Vladimir Zapolskiy   misc: sram: exten...
252
  	if (exports) {
a86854d0c   Kees Cook   treewide: devm_kz...
253
254
  		sram->partition = devm_kcalloc(sram->dev,
  				       exports, sizeof(*sram->partition),
b4c3fcb3c   Vladimir Zapolskiy   misc: sram: exten...
255
256
257
258
259
260
  				       GFP_KERNEL);
  		if (!sram->partition) {
  			ret = -ENOMEM;
  			goto err_chunks;
  		}
  	}
2da19688f   Heiko Stübner   misc: sram: imple...
261

b4c3fcb3c   Vladimir Zapolskiy   misc: sram: exten...
262
  	cur_start = 0;
2da19688f   Heiko Stübner   misc: sram: imple...
263
264
265
  	list_for_each_entry(block, &reserve_list, list) {
  		/* can only happen if sections overlap */
  		if (block->start < cur_start) {
665d82fbb   Vladimir Zapolskiy   misc: sram: add p...
266
  			dev_err(sram->dev,
2da19688f   Heiko Stübner   misc: sram: imple...
267
268
269
270
  				"block at 0x%x starts after current offset 0x%lx
  ",
  				block->start, cur_start);
  			ret = -EINVAL;
b4c3fcb3c   Vladimir Zapolskiy   misc: sram: exten...
271
  			sram_free_partitions(sram);
2da19688f   Heiko Stübner   misc: sram: imple...
272
273
  			goto err_chunks;
  		}
37afff0d8   Dave Gerlach   misc: sram: Integ...
274
275
  		if ((block->export || block->pool || block->protect_exec) &&
  		    block->size) {
b4c3fcb3c   Vladimir Zapolskiy   misc: sram: exten...
276
277
278
279
280
281
282
  			ret = sram_add_partition(sram, block,
  						 res->start + block->start);
  			if (ret) {
  				sram_free_partitions(sram);
  				goto err_chunks;
  			}
  		}
2da19688f   Heiko Stübner   misc: sram: imple...
283
284
285
286
287
288
289
290
291
292
293
294
  		/* current start is in a reserved block, so continue after it */
  		if (block->start == cur_start) {
  			cur_start = block->start + block->size;
  			continue;
  		}
  
  		/*
  		 * allocate the space between the current starting
  		 * address and the following reserved block, or the
  		 * end of the region.
  		 */
  		cur_size = block->start - cur_start;
665d82fbb   Vladimir Zapolskiy   misc: sram: add p...
295
296
  		dev_dbg(sram->dev, "adding chunk 0x%lx-0x%lx
  ",
2da19688f   Heiko Stübner   misc: sram: imple...
297
  			cur_start, cur_start + cur_size);
665d82fbb   Vladimir Zapolskiy   misc: sram: add p...
298

2da19688f   Heiko Stübner   misc: sram: imple...
299
  		ret = gen_pool_add_virt(sram->pool,
665d82fbb   Vladimir Zapolskiy   misc: sram: add p...
300
  				(unsigned long)sram->virt_base + cur_start,
2da19688f   Heiko Stübner   misc: sram: imple...
301
  				res->start + cur_start, cur_size, -1);
b4c3fcb3c   Vladimir Zapolskiy   misc: sram: exten...
302
303
  		if (ret < 0) {
  			sram_free_partitions(sram);
2da19688f   Heiko Stübner   misc: sram: imple...
304
  			goto err_chunks;
b4c3fcb3c   Vladimir Zapolskiy   misc: sram: exten...
305
  		}
2da19688f   Heiko Stübner   misc: sram: imple...
306
307
308
309
  
  		/* next allocation after this reserved block */
  		cur_start = block->start + block->size;
  	}
3104389ed   zhong jiang   misc: sram: remov...
310
311
  err_chunks:
  	of_node_put(child);
2da19688f   Heiko Stübner   misc: sram: imple...
312
  	kfree(rblocks);
a0a5be0b3   Vladimir Zapolskiy   misc: sram: move ...
313
314
  	return ret;
  }
2ae2e2885   Alexandre Belloni   misc: sram: add A...
315
316
317
318
319
320
321
322
323
324
325
326
327
  static int atmel_securam_wait(void)
  {
  	struct regmap *regmap;
  	u32 val;
  
  	regmap = syscon_regmap_lookup_by_compatible("atmel,sama5d2-secumod");
  	if (IS_ERR(regmap))
  		return -ENODEV;
  
  	return regmap_read_poll_timeout(regmap, AT91_SECUMOD_RAMRDY, val,
  					val & AT91_SECUMOD_RAMRDY_READY,
  					10000, 500000);
  }
2ae2e2885   Alexandre Belloni   misc: sram: add A...
328
329
330
331
332
  static const struct of_device_id sram_dt_ids[] = {
  	{ .compatible = "mmio-sram" },
  	{ .compatible = "atmel,sama5d2-securam", .data = atmel_securam_wait },
  	{}
  };
2ae2e2885   Alexandre Belloni   misc: sram: add A...
333

a0a5be0b3   Vladimir Zapolskiy   misc: sram: move ...
334
335
336
337
338
339
  static int sram_probe(struct platform_device *pdev)
  {
  	struct sram_dev *sram;
  	struct resource *res;
  	size_t size;
  	int ret;
2ae2e2885   Alexandre Belloni   misc: sram: add A...
340
  	int (*init_func)(void);
a0a5be0b3   Vladimir Zapolskiy   misc: sram: move ...
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
  
  	sram = devm_kzalloc(&pdev->dev, sizeof(*sram), GFP_KERNEL);
  	if (!sram)
  		return -ENOMEM;
  
  	sram->dev = &pdev->dev;
  
  	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
  	if (!res) {
  		dev_err(sram->dev, "found no memory resource
  ");
  		return -EINVAL;
  	}
  
  	size = resource_size(res);
  
  	if (!devm_request_mem_region(sram->dev, res->start, size, pdev->name)) {
  		dev_err(sram->dev, "could not request region for resource
  ");
  		return -EBUSY;
  	}
eb43e0231   Marcin Wojtas   misc: sram: add o...
362
363
364
365
  	if (of_property_read_bool(pdev->dev.of_node, "no-memory-wc"))
  		sram->virt_base = devm_ioremap(sram->dev, res->start, size);
  	else
  		sram->virt_base = devm_ioremap_wc(sram->dev, res->start, size);
d449d69d2   Vladimir Zapolskiy   misc: sram: fix c...
366
367
  	if (!sram->virt_base)
  		return -ENOMEM;
a0a5be0b3   Vladimir Zapolskiy   misc: sram: move ...
368

738581735   Vladimir Zapolskiy   genalloc: add nam...
369
370
371
372
  	sram->pool = devm_gen_pool_create(sram->dev, ilog2(SRAM_GRANULARITY),
  					  NUMA_NO_NODE, NULL);
  	if (IS_ERR(sram->pool))
  		return PTR_ERR(sram->pool);
a0a5be0b3   Vladimir Zapolskiy   misc: sram: move ...
373

665d82fbb   Vladimir Zapolskiy   misc: sram: add p...
374
  	sram->clk = devm_clk_get(sram->dev, NULL);
ee895ccdf   Vladimir Zapolskiy   misc: sram: fix e...
375
376
377
378
  	if (IS_ERR(sram->clk))
  		sram->clk = NULL;
  	else
  		clk_prepare_enable(sram->clk);
d5b9653dd   Johan Hovold   misc: sram: enabl...
379
380
381
  	ret = sram_reserve_regions(sram, res);
  	if (ret)
  		goto err_disable_clk;
4984c6f5e   Philipp Zabel   misc: generic on-...
382
  	platform_set_drvdata(pdev, sram);
2ae2e2885   Alexandre Belloni   misc: sram: add A...
383
384
385
386
  	init_func = of_device_get_match_data(&pdev->dev);
  	if (init_func) {
  		ret = init_func();
  		if (ret)
d5b9653dd   Johan Hovold   misc: sram: enabl...
387
  			goto err_free_partitions;
2ae2e2885   Alexandre Belloni   misc: sram: add A...
388
  	}
665d82fbb   Vladimir Zapolskiy   misc: sram: add p...
389
390
391
  	dev_dbg(sram->dev, "SRAM pool: %zu KiB @ 0x%p
  ",
  		gen_pool_size(sram->pool) / 1024, sram->virt_base);
4984c6f5e   Philipp Zabel   misc: generic on-...
392
393
  
  	return 0;
f294d0096   Johan Hovold   misc: sram: fix r...
394

d5b9653dd   Johan Hovold   misc: sram: enabl...
395
396
  err_free_partitions:
  	sram_free_partitions(sram);
f294d0096   Johan Hovold   misc: sram: fix r...
397
398
399
  err_disable_clk:
  	if (sram->clk)
  		clk_disable_unprepare(sram->clk);
f294d0096   Johan Hovold   misc: sram: fix r...
400
401
  
  	return ret;
4984c6f5e   Philipp Zabel   misc: generic on-...
402
403
404
405
406
  }
  
  static int sram_remove(struct platform_device *pdev)
  {
  	struct sram_dev *sram = platform_get_drvdata(pdev);
b4c3fcb3c   Vladimir Zapolskiy   misc: sram: exten...
407
  	sram_free_partitions(sram);
4984c6f5e   Philipp Zabel   misc: generic on-...
408
  	if (gen_pool_avail(sram->pool) < gen_pool_size(sram->pool))
665d82fbb   Vladimir Zapolskiy   misc: sram: add p...
409
410
  		dev_err(sram->dev, "removed while SRAM allocated
  ");
4984c6f5e   Philipp Zabel   misc: generic on-...
411

4984c6f5e   Philipp Zabel   misc: generic on-...
412
413
414
415
416
  	if (sram->clk)
  		clk_disable_unprepare(sram->clk);
  
  	return 0;
  }
4984c6f5e   Philipp Zabel   misc: generic on-...
417
418
419
  static struct platform_driver sram_driver = {
  	.driver = {
  		.name = "sram",
2aa488a6b   Arnd Bergmann   misc: sram: remov...
420
  		.of_match_table = sram_dt_ids,
4984c6f5e   Philipp Zabel   misc: generic on-...
421
422
423
424
425
426
427
428
429
430
431
  	},
  	.probe = sram_probe,
  	.remove = sram_remove,
  };
  
  static int __init sram_init(void)
  {
  	return platform_driver_register(&sram_driver);
  }
  
  postcore_initcall(sram_init);