1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16#include <linux/module.h>
17#include <linux/types.h>
18#include <linux/init.h>
19#include <linux/device.h>
20#include <linux/mtd/mtd.h>
21#include <linux/mtd/map.h>
22#include <linux/mtd/partitions.h>
23#include <linux/mtd/concat.h>
24#include <linux/of.h>
25#include <linux/of_address.h>
26#include <linux/of_platform.h>
27#include <linux/slab.h>
28
29struct of_flash_list {
30 struct mtd_info *mtd;
31 struct map_info map;
32 struct resource *res;
33};
34
35struct of_flash {
36 struct mtd_info *cmtd;
37#ifdef CONFIG_MTD_PARTITIONS
38 struct mtd_partition *parts;
39#endif
40 int list_size;
41 struct of_flash_list list[0];
42};
43
44#ifdef CONFIG_MTD_PARTITIONS
45#define OF_FLASH_PARTS(info) ((info)->parts)
46
47static int parse_obsolete_partitions(struct platform_device *dev,
48 struct of_flash *info,
49 struct device_node *dp)
50{
51 int i, plen, nr_parts;
52 const struct {
53 __be32 offset, len;
54 } *part;
55 const char *names;
56
57 part = of_get_property(dp, "partitions", &plen);
58 if (!part)
59 return 0;
60
61 dev_warn(&dev->dev, "Device tree uses obsolete partition map binding\n");
62
63 nr_parts = plen / sizeof(part[0]);
64
65 info->parts = kzalloc(nr_parts * sizeof(*info->parts), GFP_KERNEL);
66 if (!info->parts)
67 return -ENOMEM;
68
69 names = of_get_property(dp, "partition-names", &plen);
70
71 for (i = 0; i < nr_parts; i++) {
72 info->parts[i].offset = be32_to_cpu(part->offset);
73 info->parts[i].size = be32_to_cpu(part->len) & ~1;
74 if (be32_to_cpu(part->len) & 1)
75 info->parts[i].mask_flags = MTD_WRITEABLE;
76
77 if (names && (plen > 0)) {
78 int len = strlen(names) + 1;
79
80 info->parts[i].name = (char *)names;
81 plen -= len;
82 names += len;
83 } else {
84 info->parts[i].name = "unnamed";
85 }
86
87 part++;
88 }
89
90 return nr_parts;
91}
92#else
93#define OF_FLASH_PARTS(info) (0)
94#define parse_partitions(info, dev) (0)
95#endif
96
97static int of_flash_remove(struct platform_device *dev)
98{
99 struct of_flash *info;
100 int i;
101
102 info = dev_get_drvdata(&dev->dev);
103 if (!info)
104 return 0;
105 dev_set_drvdata(&dev->dev, NULL);
106
107#ifdef CONFIG_MTD_CONCAT
108 if (info->cmtd != info->list[0].mtd) {
109 del_mtd_device(info->cmtd);
110 mtd_concat_destroy(info->cmtd);
111 }
112#endif
113
114 if (info->cmtd) {
115 if (OF_FLASH_PARTS(info)) {
116 del_mtd_partitions(info->cmtd);
117 kfree(OF_FLASH_PARTS(info));
118 } else {
119 del_mtd_device(info->cmtd);
120 }
121 }
122
123 for (i = 0; i < info->list_size; i++) {
124 if (info->list[i].mtd)
125 map_destroy(info->list[i].mtd);
126
127 if (info->list[i].map.virt)
128 iounmap(info->list[i].map.virt);
129
130 if (info->list[i].res) {
131 release_resource(info->list[i].res);
132 kfree(info->list[i].res);
133 }
134 }
135
136 kfree(info);
137
138 return 0;
139}
140
141
142
143
144static struct mtd_info * __devinit obsolete_probe(struct platform_device *dev,
145 struct map_info *map)
146{
147 struct device_node *dp = dev->dev.of_node;
148 const char *of_probe;
149 struct mtd_info *mtd;
150 static const char *rom_probe_types[]
151 = { "cfi_probe", "jedec_probe", "map_rom"};
152 int i;
153
154 dev_warn(&dev->dev, "Device tree uses obsolete \"direct-mapped\" "
155 "flash binding\n");
156
157 of_probe = of_get_property(dp, "probe-type", NULL);
158 if (!of_probe) {
159 for (i = 0; i < ARRAY_SIZE(rom_probe_types); i++) {
160 mtd = do_map_probe(rom_probe_types[i], map);
161 if (mtd)
162 return mtd;
163 }
164 return NULL;
165 } else if (strcmp(of_probe, "CFI") == 0) {
166 return do_map_probe("cfi_probe", map);
167 } else if (strcmp(of_probe, "JEDEC") == 0) {
168 return do_map_probe("jedec_probe", map);
169 } else {
170 if (strcmp(of_probe, "ROM") != 0)
171 dev_warn(&dev->dev, "obsolete_probe: don't know probe "
172 "type '%s', mapping as rom\n", of_probe);
173 return do_map_probe("mtd_rom", map);
174 }
175}
176
177#ifdef CONFIG_MTD_PARTITIONS
178
179
180
181
182static const char *part_probe_types_def[] = { "cmdlinepart", "RedBoot", NULL };
183static const char ** __devinit of_get_probes(struct device_node *dp)
184{
185 const char *cp;
186 int cplen;
187 unsigned int l;
188 unsigned int count;
189 const char **res;
190
191 cp = of_get_property(dp, "linux,part-probe", &cplen);
192 if (cp == NULL)
193 return part_probe_types_def;
194
195 count = 0;
196 for (l = 0; l != cplen; l++)
197 if (cp[l] == 0)
198 count++;
199
200 res = kzalloc((count + 1)*sizeof(*res), GFP_KERNEL);
201 count = 0;
202 while (cplen > 0) {
203 res[count] = cp;
204 l = strlen(cp) + 1;
205 cp += l;
206 cplen -= l;
207 count++;
208 }
209 return res;
210}
211
212static void __devinit of_free_probes(const char **probes)
213{
214 if (probes != part_probe_types_def)
215 kfree(probes);
216}
217#endif
218
219static int __devinit of_flash_probe(struct platform_device *dev,
220 const struct of_device_id *match)
221{
222#ifdef CONFIG_MTD_PARTITIONS
223 const char **part_probe_types;
224#endif
225 struct device_node *dp = dev->dev.of_node;
226 struct resource res;
227 struct of_flash *info;
228 const char *probe_type = match->data;
229 const __be32 *width;
230 int err;
231 int i;
232 int count;
233 const __be32 *p;
234 int reg_tuple_size;
235 struct mtd_info **mtd_list = NULL;
236 resource_size_t res_size;
237
238 reg_tuple_size = (of_n_addr_cells(dp) + of_n_size_cells(dp)) * sizeof(u32);
239
240
241
242
243
244
245
246 p = of_get_property(dp, "reg", &count);
247 if (count % reg_tuple_size != 0) {
248 dev_err(&dev->dev, "Malformed reg property on %s\n",
249 dev->dev.of_node->full_name);
250 err = -EINVAL;
251 goto err_flash_remove;
252 }
253 count /= reg_tuple_size;
254
255 err = -ENOMEM;
256 info = kzalloc(sizeof(struct of_flash) +
257 sizeof(struct of_flash_list) * count, GFP_KERNEL);
258 if (!info)
259 goto err_flash_remove;
260
261 dev_set_drvdata(&dev->dev, info);
262
263 mtd_list = kzalloc(sizeof(*mtd_list) * count, GFP_KERNEL);
264 if (!mtd_list)
265 goto err_flash_remove;
266
267 for (i = 0; i < count; i++) {
268 err = -ENXIO;
269 if (of_address_to_resource(dp, i, &res)) {
270
271
272
273
274 continue;
275 }
276
277 dev_dbg(&dev->dev, "of_flash device: %pR\n", &res);
278
279 err = -EBUSY;
280 res_size = resource_size(&res);
281 info->list[i].res = request_mem_region(res.start, res_size,
282 dev_name(&dev->dev));
283 if (!info->list[i].res)
284 goto err_out;
285
286 err = -ENXIO;
287 width = of_get_property(dp, "bank-width", NULL);
288 if (!width) {
289 dev_err(&dev->dev, "Can't get bank width from device"
290 " tree\n");
291 goto err_out;
292 }
293
294 info->list[i].map.name = dev_name(&dev->dev);
295 info->list[i].map.phys = res.start;
296 info->list[i].map.size = res_size;
297 info->list[i].map.bankwidth = be32_to_cpup(width);
298
299 err = -ENOMEM;
300 info->list[i].map.virt = ioremap(info->list[i].map.phys,
301 info->list[i].map.size);
302 if (!info->list[i].map.virt) {
303 dev_err(&dev->dev, "Failed to ioremap() flash"
304 " region\n");
305 goto err_out;
306 }
307
308 simple_map_init(&info->list[i].map);
309
310 if (probe_type) {
311 info->list[i].mtd = do_map_probe(probe_type,
312 &info->list[i].map);
313 } else {
314 info->list[i].mtd = obsolete_probe(dev,
315 &info->list[i].map);
316 }
317 mtd_list[i] = info->list[i].mtd;
318
319 err = -ENXIO;
320 if (!info->list[i].mtd) {
321 dev_err(&dev->dev, "do_map_probe() failed\n");
322 goto err_out;
323 } else {
324 info->list_size++;
325 }
326 info->list[i].mtd->owner = THIS_MODULE;
327 info->list[i].mtd->dev.parent = &dev->dev;
328 }
329
330 err = 0;
331 if (info->list_size == 1) {
332 info->cmtd = info->list[0].mtd;
333 } else if (info->list_size > 1) {
334
335
336
337#ifdef CONFIG_MTD_CONCAT
338 info->cmtd = mtd_concat_create(mtd_list, info->list_size,
339 dev_name(&dev->dev));
340 if (info->cmtd == NULL)
341 err = -ENXIO;
342#else
343 printk(KERN_ERR "physmap_of: multiple devices "
344 "found but MTD concat support disabled.\n");
345 err = -ENXIO;
346#endif
347 }
348 if (err)
349 goto err_out;
350
351#ifdef CONFIG_MTD_PARTITIONS
352 part_probe_types = of_get_probes(dp);
353 err = parse_mtd_partitions(info->cmtd, part_probe_types,
354 &info->parts, 0);
355 if (err < 0) {
356 of_free_probes(part_probe_types);
357 goto err_out;
358 }
359 of_free_probes(part_probe_types);
360
361#ifdef CONFIG_MTD_OF_PARTS
362 if (err == 0) {
363 err = of_mtd_parse_partitions(&dev->dev, dp, &info->parts);
364 if (err < 0)
365 goto err_out;
366 }
367#endif
368
369 if (err == 0) {
370 err = parse_obsolete_partitions(dev, info, dp);
371 if (err < 0)
372 goto err_out;
373 }
374
375 if (err > 0)
376 add_mtd_partitions(info->cmtd, info->parts, err);
377 else
378#endif
379 add_mtd_device(info->cmtd);
380
381 kfree(mtd_list);
382
383 return 0;
384
385err_out:
386 kfree(mtd_list);
387err_flash_remove:
388 of_flash_remove(dev);
389
390 return err;
391}
392
393static struct of_device_id of_flash_match[] = {
394 {
395 .compatible = "cfi-flash",
396 .data = (void *)"cfi_probe",
397 },
398 {
399
400
401
402
403
404
405
406 .compatible = "jedec-flash",
407 .data = (void *)"jedec_probe",
408 },
409 {
410 .compatible = "mtd-ram",
411 .data = (void *)"map_ram",
412 },
413 {
414 .type = "rom",
415 .compatible = "direct-mapped"
416 },
417 { },
418};
419MODULE_DEVICE_TABLE(of, of_flash_match);
420
421static struct of_platform_driver of_flash_driver = {
422 .driver = {
423 .name = "of-flash",
424 .owner = THIS_MODULE,
425 .of_match_table = of_flash_match,
426 },
427 .probe = of_flash_probe,
428 .remove = of_flash_remove,
429};
430
431static int __init of_flash_init(void)
432{
433 return of_register_platform_driver(&of_flash_driver);
434}
435
436static void __exit of_flash_exit(void)
437{
438 of_unregister_platform_driver(&of_flash_driver);
439}
440
441module_init(of_flash_init);
442module_exit(of_flash_exit);
443
444MODULE_LICENSE("GPL");
445MODULE_AUTHOR("Vitaly Wool <vwool@ru.mvista.com>");
446MODULE_DESCRIPTION("Device tree based MTD map driver");
447