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 int list_size;
38 struct of_flash_list list[0];
39};
40
41static int of_flash_remove(struct platform_device *dev)
42{
43 struct of_flash *info;
44 int i;
45
46 info = dev_get_drvdata(&dev->dev);
47 if (!info)
48 return 0;
49 dev_set_drvdata(&dev->dev, NULL);
50
51 if (info->cmtd != info->list[0].mtd) {
52 mtd_device_unregister(info->cmtd);
53 mtd_concat_destroy(info->cmtd);
54 }
55
56 if (info->cmtd)
57 mtd_device_unregister(info->cmtd);
58
59 for (i = 0; i < info->list_size; i++) {
60 if (info->list[i].mtd)
61 map_destroy(info->list[i].mtd);
62
63 if (info->list[i].map.virt)
64 iounmap(info->list[i].map.virt);
65
66 if (info->list[i].res) {
67 release_resource(info->list[i].res);
68 kfree(info->list[i].res);
69 }
70 }
71
72 kfree(info);
73
74 return 0;
75}
76
77
78
79
80static struct mtd_info * __devinit obsolete_probe(struct platform_device *dev,
81 struct map_info *map)
82{
83 struct device_node *dp = dev->dev.of_node;
84 const char *of_probe;
85 struct mtd_info *mtd;
86 static const char *rom_probe_types[]
87 = { "cfi_probe", "jedec_probe", "map_rom"};
88 int i;
89
90 dev_warn(&dev->dev, "Device tree uses obsolete \"direct-mapped\" "
91 "flash binding\n");
92
93 of_probe = of_get_property(dp, "probe-type", NULL);
94 if (!of_probe) {
95 for (i = 0; i < ARRAY_SIZE(rom_probe_types); i++) {
96 mtd = do_map_probe(rom_probe_types[i], map);
97 if (mtd)
98 return mtd;
99 }
100 return NULL;
101 } else if (strcmp(of_probe, "CFI") == 0) {
102 return do_map_probe("cfi_probe", map);
103 } else if (strcmp(of_probe, "JEDEC") == 0) {
104 return do_map_probe("jedec_probe", map);
105 } else {
106 if (strcmp(of_probe, "ROM") != 0)
107 dev_warn(&dev->dev, "obsolete_probe: don't know probe "
108 "type '%s', mapping as rom\n", of_probe);
109 return do_map_probe("mtd_rom", map);
110 }
111}
112
113
114
115
116
117static const char *part_probe_types_def[] = { "cmdlinepart", "RedBoot",
118 "ofpart", "ofoldpart", NULL };
119static const char ** __devinit of_get_probes(struct device_node *dp)
120{
121 const char *cp;
122 int cplen;
123 unsigned int l;
124 unsigned int count;
125 const char **res;
126
127 cp = of_get_property(dp, "linux,part-probe", &cplen);
128 if (cp == NULL)
129 return part_probe_types_def;
130
131 count = 0;
132 for (l = 0; l != cplen; l++)
133 if (cp[l] == 0)
134 count++;
135
136 res = kzalloc((count + 1)*sizeof(*res), GFP_KERNEL);
137 count = 0;
138 while (cplen > 0) {
139 res[count] = cp;
140 l = strlen(cp) + 1;
141 cp += l;
142 cplen -= l;
143 count++;
144 }
145 return res;
146}
147
148static void __devinit of_free_probes(const char **probes)
149{
150 if (probes != part_probe_types_def)
151 kfree(probes);
152}
153
154static struct of_device_id of_flash_match[];
155static int __devinit of_flash_probe(struct platform_device *dev)
156{
157 const char **part_probe_types;
158 const struct of_device_id *match;
159 struct device_node *dp = dev->dev.of_node;
160 struct resource res;
161 struct of_flash *info;
162 const char *probe_type;
163 const __be32 *width;
164 int err;
165 int i;
166 int count;
167 const __be32 *p;
168 int reg_tuple_size;
169 struct mtd_info **mtd_list = NULL;
170 resource_size_t res_size;
171 struct mtd_part_parser_data ppdata;
172
173 match = of_match_device(of_flash_match, &dev->dev);
174 if (!match)
175 return -EINVAL;
176 probe_type = match->data;
177
178 reg_tuple_size = (of_n_addr_cells(dp) + of_n_size_cells(dp)) * sizeof(u32);
179
180
181
182
183
184
185
186 p = of_get_property(dp, "reg", &count);
187 if (count % reg_tuple_size != 0) {
188 dev_err(&dev->dev, "Malformed reg property on %s\n",
189 dev->dev.of_node->full_name);
190 err = -EINVAL;
191 goto err_flash_remove;
192 }
193 count /= reg_tuple_size;
194
195 err = -ENOMEM;
196 info = kzalloc(sizeof(struct of_flash) +
197 sizeof(struct of_flash_list) * count, GFP_KERNEL);
198 if (!info)
199 goto err_flash_remove;
200
201 dev_set_drvdata(&dev->dev, info);
202
203 mtd_list = kzalloc(sizeof(*mtd_list) * count, GFP_KERNEL);
204 if (!mtd_list)
205 goto err_flash_remove;
206
207 for (i = 0; i < count; i++) {
208 err = -ENXIO;
209 if (of_address_to_resource(dp, i, &res)) {
210
211
212
213
214 continue;
215 }
216
217 dev_dbg(&dev->dev, "of_flash device: %pR\n", &res);
218
219 err = -EBUSY;
220 res_size = resource_size(&res);
221 info->list[i].res = request_mem_region(res.start, res_size,
222 dev_name(&dev->dev));
223 if (!info->list[i].res)
224 goto err_out;
225
226 err = -ENXIO;
227 width = of_get_property(dp, "bank-width", NULL);
228 if (!width) {
229 dev_err(&dev->dev, "Can't get bank width from device"
230 " tree\n");
231 goto err_out;
232 }
233
234 info->list[i].map.name = dev_name(&dev->dev);
235 info->list[i].map.phys = res.start;
236 info->list[i].map.size = res_size;
237 info->list[i].map.bankwidth = be32_to_cpup(width);
238
239 err = -ENOMEM;
240 info->list[i].map.virt = ioremap(info->list[i].map.phys,
241 info->list[i].map.size);
242 if (!info->list[i].map.virt) {
243 dev_err(&dev->dev, "Failed to ioremap() flash"
244 " region\n");
245 goto err_out;
246 }
247
248 simple_map_init(&info->list[i].map);
249
250 if (probe_type) {
251 info->list[i].mtd = do_map_probe(probe_type,
252 &info->list[i].map);
253 } else {
254 info->list[i].mtd = obsolete_probe(dev,
255 &info->list[i].map);
256 }
257 mtd_list[i] = info->list[i].mtd;
258
259 err = -ENXIO;
260 if (!info->list[i].mtd) {
261 dev_err(&dev->dev, "do_map_probe() failed\n");
262 goto err_out;
263 } else {
264 info->list_size++;
265 }
266 info->list[i].mtd->owner = THIS_MODULE;
267 info->list[i].mtd->dev.parent = &dev->dev;
268 }
269
270 err = 0;
271 if (info->list_size == 1) {
272 info->cmtd = info->list[0].mtd;
273 } else if (info->list_size > 1) {
274
275
276
277 info->cmtd = mtd_concat_create(mtd_list, info->list_size,
278 dev_name(&dev->dev));
279 if (info->cmtd == NULL)
280 err = -ENXIO;
281 }
282 if (err)
283 goto err_out;
284
285 ppdata.of_node = dp;
286 part_probe_types = of_get_probes(dp);
287 mtd_device_parse_register(info->cmtd, part_probe_types, &ppdata,
288 NULL, 0);
289 of_free_probes(part_probe_types);
290
291 kfree(mtd_list);
292
293 return 0;
294
295err_out:
296 kfree(mtd_list);
297err_flash_remove:
298 of_flash_remove(dev);
299
300 return err;
301}
302
303static struct of_device_id of_flash_match[] = {
304 {
305 .compatible = "cfi-flash",
306 .data = (void *)"cfi_probe",
307 },
308 {
309
310
311
312
313
314
315
316 .compatible = "jedec-flash",
317 .data = (void *)"jedec_probe",
318 },
319 {
320 .compatible = "mtd-ram",
321 .data = (void *)"map_ram",
322 },
323 {
324 .type = "rom",
325 .compatible = "direct-mapped"
326 },
327 { },
328};
329MODULE_DEVICE_TABLE(of, of_flash_match);
330
331static struct platform_driver of_flash_driver = {
332 .driver = {
333 .name = "of-flash",
334 .owner = THIS_MODULE,
335 .of_match_table = of_flash_match,
336 },
337 .probe = of_flash_probe,
338 .remove = of_flash_remove,
339};
340
341static int __init of_flash_init(void)
342{
343 return platform_driver_register(&of_flash_driver);
344}
345
346static void __exit of_flash_exit(void)
347{
348 platform_driver_unregister(&of_flash_driver);
349}
350
351module_init(of_flash_init);
352module_exit(of_flash_exit);
353
354MODULE_LICENSE("GPL");
355MODULE_AUTHOR("Vitaly Wool <vwool@ru.mvista.com>");
356MODULE_DESCRIPTION("Device tree based MTD map driver");
357