diff options
Diffstat (limited to 'drivers/mtd/maps/physmap_of.c')
-rw-r--r-- | drivers/mtd/maps/physmap_of.c | 224 |
1 files changed, 99 insertions, 125 deletions
diff --git a/drivers/mtd/maps/physmap_of.c b/drivers/mtd/maps/physmap_of.c index 096dd47b5d5d..cf75a566442e 100644 --- a/drivers/mtd/maps/physmap_of.c +++ b/drivers/mtd/maps/physmap_of.c | |||
@@ -1,5 +1,5 @@ | |||
1 | /* | 1 | /* |
2 | * Normal mappings of chips in physical memory for OF devices | 2 | * Flash mappings described by the OF (or flattened) device tree |
3 | * | 3 | * |
4 | * Copyright (C) 2006 MontaVista Software Inc. | 4 | * Copyright (C) 2006 MontaVista Software Inc. |
5 | * Author: Vitaly Wool <vwool@ru.mvista.com> | 5 | * Author: Vitaly Wool <vwool@ru.mvista.com> |
@@ -15,20 +15,15 @@ | |||
15 | 15 | ||
16 | #include <linux/module.h> | 16 | #include <linux/module.h> |
17 | #include <linux/types.h> | 17 | #include <linux/types.h> |
18 | #include <linux/kernel.h> | ||
19 | #include <linux/init.h> | 18 | #include <linux/init.h> |
20 | #include <linux/slab.h> | ||
21 | #include <linux/device.h> | 19 | #include <linux/device.h> |
22 | #include <linux/mtd/mtd.h> | 20 | #include <linux/mtd/mtd.h> |
23 | #include <linux/mtd/map.h> | 21 | #include <linux/mtd/map.h> |
24 | #include <linux/mtd/partitions.h> | 22 | #include <linux/mtd/partitions.h> |
25 | #include <linux/mtd/physmap.h> | 23 | #include <linux/of.h> |
26 | #include <asm/io.h> | 24 | #include <linux/of_platform.h> |
27 | #include <asm/prom.h> | ||
28 | #include <asm/of_device.h> | ||
29 | #include <asm/of_platform.h> | ||
30 | 25 | ||
31 | struct physmap_flash_info { | 26 | struct of_flash { |
32 | struct mtd_info *mtd; | 27 | struct mtd_info *mtd; |
33 | struct map_info map; | 28 | struct map_info map; |
34 | struct resource *res; | 29 | struct resource *res; |
@@ -38,8 +33,10 @@ struct physmap_flash_info { | |||
38 | }; | 33 | }; |
39 | 34 | ||
40 | #ifdef CONFIG_MTD_PARTITIONS | 35 | #ifdef CONFIG_MTD_PARTITIONS |
36 | #define OF_FLASH_PARTS(info) ((info)->parts) | ||
37 | |||
41 | static int parse_obsolete_partitions(struct of_device *dev, | 38 | static int parse_obsolete_partitions(struct of_device *dev, |
42 | struct physmap_flash_info *info, | 39 | struct of_flash *info, |
43 | struct device_node *dp) | 40 | struct device_node *dp) |
44 | { | 41 | { |
45 | int i, plen, nr_parts; | 42 | int i, plen, nr_parts; |
@@ -50,17 +47,15 @@ static int parse_obsolete_partitions(struct of_device *dev, | |||
50 | 47 | ||
51 | part = of_get_property(dp, "partitions", &plen); | 48 | part = of_get_property(dp, "partitions", &plen); |
52 | if (!part) | 49 | if (!part) |
53 | return -ENOENT; | 50 | return 0; /* No partitions found */ |
54 | 51 | ||
55 | dev_warn(&dev->dev, "Device tree uses obsolete partition map binding\n"); | 52 | dev_warn(&dev->dev, "Device tree uses obsolete partition map binding\n"); |
56 | 53 | ||
57 | nr_parts = plen / sizeof(part[0]); | 54 | nr_parts = plen / sizeof(part[0]); |
58 | 55 | ||
59 | info->parts = kzalloc(nr_parts * sizeof(struct mtd_partition), GFP_KERNEL); | 56 | info->parts = kzalloc(nr_parts * sizeof(*info->parts), GFP_KERNEL); |
60 | if (!info->parts) { | 57 | if (!info->parts) |
61 | printk(KERN_ERR "Can't allocate the flash partition data!\n"); | ||
62 | return -ENOMEM; | 58 | return -ENOMEM; |
63 | } | ||
64 | 59 | ||
65 | names = of_get_property(dp, "partition-names", &plen); | 60 | names = of_get_property(dp, "partition-names", &plen); |
66 | 61 | ||
@@ -86,8 +81,8 @@ static int parse_obsolete_partitions(struct of_device *dev, | |||
86 | return nr_parts; | 81 | return nr_parts; |
87 | } | 82 | } |
88 | 83 | ||
89 | static int __devinit process_partitions(struct physmap_flash_info *info, | 84 | static int __devinit parse_partitions(struct of_flash *info, |
90 | struct of_device *dev) | 85 | struct of_device *dev) |
91 | { | 86 | { |
92 | const char *partname; | 87 | const char *partname; |
93 | static const char *part_probe_types[] | 88 | static const char *part_probe_types[] |
@@ -109,89 +104,68 @@ static int __devinit process_partitions(struct physmap_flash_info *info, | |||
109 | for (pp = dp->child; pp; pp = pp->sibling) | 104 | for (pp = dp->child; pp; pp = pp->sibling) |
110 | nr_parts++; | 105 | nr_parts++; |
111 | 106 | ||
112 | if (nr_parts) { | 107 | if (nr_parts == 0) |
113 | info->parts = kzalloc(nr_parts * sizeof(struct mtd_partition), | 108 | return parse_obsolete_partitions(dev, info, dp); |
114 | GFP_KERNEL); | 109 | |
115 | if (!info->parts) { | 110 | info->parts = kzalloc(nr_parts * sizeof(*info->parts), |
116 | printk(KERN_ERR "Can't allocate the flash partition data!\n"); | 111 | GFP_KERNEL); |
117 | return -ENOMEM; | 112 | if (!info->parts) |
118 | } | 113 | return -ENOMEM; |
119 | 114 | ||
120 | for (pp = dp->child, i = 0 ; pp; pp = pp->sibling, i++) { | 115 | for (pp = dp->child, i = 0; pp; pp = pp->sibling, i++) { |
121 | const u32 *reg; | 116 | const u32 *reg; |
122 | int len; | 117 | int len; |
123 | 118 | ||
124 | reg = of_get_property(pp, "reg", &len); | 119 | reg = of_get_property(pp, "reg", &len); |
125 | if (!reg || (len != 2*sizeof(u32))) { | 120 | if (!reg || (len != 2*sizeof(u32))) { |
126 | dev_err(&dev->dev, "Invalid 'reg' on %s\n", | 121 | dev_err(&dev->dev, "Invalid 'reg' on %s\n", |
127 | dp->full_name); | 122 | dp->full_name); |
128 | kfree(info->parts); | 123 | kfree(info->parts); |
129 | info->parts = NULL; | 124 | info->parts = NULL; |
130 | return -EINVAL; | 125 | return -EINVAL; |
131 | } | ||
132 | info->parts[i].offset = reg[0]; | ||
133 | info->parts[i].size = reg[1]; | ||
134 | |||
135 | partname = of_get_property(pp, "label", &len); | ||
136 | if (!partname) | ||
137 | partname = of_get_property(pp, "name", &len); | ||
138 | info->parts[i].name = (char *)partname; | ||
139 | |||
140 | if (of_get_property(pp, "read-only", &len)) | ||
141 | info->parts[i].mask_flags = MTD_WRITEABLE; | ||
142 | } | 126 | } |
143 | } else { | 127 | info->parts[i].offset = reg[0]; |
144 | nr_parts = parse_obsolete_partitions(dev, info, dp); | 128 | info->parts[i].size = reg[1]; |
145 | if (nr_parts == -ENOENT) | ||
146 | nr_parts = 0; | ||
147 | } | ||
148 | 129 | ||
149 | if (nr_parts < 0) | 130 | partname = of_get_property(pp, "label", &len); |
150 | return nr_parts; | 131 | if (!partname) |
132 | partname = of_get_property(pp, "name", &len); | ||
133 | info->parts[i].name = (char *)partname; | ||
151 | 134 | ||
152 | if (nr_parts > 0) | 135 | if (of_get_property(pp, "read-only", &len)) |
153 | add_mtd_partitions(info->mtd, info->parts, nr_parts); | 136 | info->parts[i].mask_flags = MTD_WRITEABLE; |
154 | else | 137 | } |
155 | add_mtd_device(info->mtd); | ||
156 | 138 | ||
157 | return 0; | 139 | return nr_parts; |
158 | } | 140 | } |
159 | #else /* MTD_PARTITIONS */ | 141 | #else /* MTD_PARTITIONS */ |
160 | static int __devinit process_partitions(struct physmap_flash_info *info, | 142 | #define OF_FLASH_PARTS(info) (0) |
161 | struct device_node *dev) | 143 | #define parse_partitions(info, dev) (0) |
162 | { | ||
163 | add_mtd_device(info->mtd); | ||
164 | return 0; | ||
165 | } | ||
166 | #endif /* MTD_PARTITIONS */ | 144 | #endif /* MTD_PARTITIONS */ |
167 | 145 | ||
168 | static int of_physmap_remove(struct of_device *dev) | 146 | static int of_flash_remove(struct of_device *dev) |
169 | { | 147 | { |
170 | struct physmap_flash_info *info; | 148 | struct of_flash *info; |
171 | 149 | ||
172 | info = dev_get_drvdata(&dev->dev); | 150 | info = dev_get_drvdata(&dev->dev); |
173 | if (info == NULL) | 151 | if (!info) |
174 | return 0; | 152 | return 0; |
175 | dev_set_drvdata(&dev->dev, NULL); | 153 | dev_set_drvdata(&dev->dev, NULL); |
176 | 154 | ||
177 | if (info->mtd != NULL) { | 155 | if (info->mtd) { |
178 | #ifdef CONFIG_MTD_PARTITIONS | 156 | if (OF_FLASH_PARTS(info)) { |
179 | if (info->parts) { | ||
180 | del_mtd_partitions(info->mtd); | 157 | del_mtd_partitions(info->mtd); |
181 | kfree(info->parts); | 158 | kfree(OF_FLASH_PARTS(info)); |
182 | } else { | 159 | } else { |
183 | del_mtd_device(info->mtd); | 160 | del_mtd_device(info->mtd); |
184 | } | 161 | } |
185 | #else | ||
186 | del_mtd_device(info->mtd); | ||
187 | #endif | ||
188 | map_destroy(info->mtd); | 162 | map_destroy(info->mtd); |
189 | } | 163 | } |
190 | 164 | ||
191 | if (info->map.virt != NULL) | 165 | if (info->map.virt) |
192 | iounmap(info->map.virt); | 166 | iounmap(info->map.virt); |
193 | 167 | ||
194 | if (info->res != NULL) { | 168 | if (info->res) { |
195 | release_resource(info->res); | 169 | release_resource(info->res); |
196 | kfree(info->res); | 170 | kfree(info->res); |
197 | } | 171 | } |
@@ -229,52 +203,49 @@ static struct mtd_info * __devinit obsolete_probe(struct of_device *dev, | |||
229 | return do_map_probe("jedec_probe", map); | 203 | return do_map_probe("jedec_probe", map); |
230 | } else { | 204 | } else { |
231 | if (strcmp(of_probe, "ROM") != 0) | 205 | if (strcmp(of_probe, "ROM") != 0) |
232 | dev_dbg(&dev->dev, "obsolete_probe: don't know probe type " | 206 | dev_warn(&dev->dev, "obsolete_probe: don't know probe " |
233 | "'%s', mapping as rom\n", of_probe); | 207 | "type '%s', mapping as rom\n", of_probe); |
234 | return do_map_probe("mtd_rom", map); | 208 | return do_map_probe("mtd_rom", map); |
235 | } | 209 | } |
236 | } | 210 | } |
237 | 211 | ||
238 | static int __devinit of_physmap_probe(struct of_device *dev, const struct of_device_id *match) | 212 | static int __devinit of_flash_probe(struct of_device *dev, |
213 | const struct of_device_id *match) | ||
239 | { | 214 | { |
240 | struct device_node *dp = dev->node; | 215 | struct device_node *dp = dev->node; |
241 | struct resource res; | 216 | struct resource res; |
242 | struct physmap_flash_info *info; | 217 | struct of_flash *info; |
243 | const char *probe_type = (const char *)match->data; | 218 | const char *probe_type = match->data; |
244 | const u32 *width; | 219 | const u32 *width; |
245 | int err; | 220 | int err; |
246 | 221 | ||
222 | err = -ENXIO; | ||
247 | if (of_address_to_resource(dp, 0, &res)) { | 223 | if (of_address_to_resource(dp, 0, &res)) { |
248 | dev_err(&dev->dev, "Can't get the flash mapping!\n"); | 224 | dev_err(&dev->dev, "Can't get IO address from device tree\n"); |
249 | err = -EINVAL; | ||
250 | goto err_out; | 225 | goto err_out; |
251 | } | 226 | } |
252 | 227 | ||
253 | dev_dbg(&dev->dev, "physmap flash device: %.8llx at %.8llx\n", | 228 | dev_dbg(&dev->dev, "of_flash device: %.8llx-%.8llx\n", |
254 | (unsigned long long)res.end - res.start + 1, | 229 | (unsigned long long)res.start, (unsigned long long)res.end); |
255 | (unsigned long long)res.start); | ||
256 | 230 | ||
257 | info = kzalloc(sizeof(struct physmap_flash_info), GFP_KERNEL); | 231 | err = -ENOMEM; |
258 | if (info == NULL) { | 232 | info = kzalloc(sizeof(*info), GFP_KERNEL); |
259 | err = -ENOMEM; | 233 | if (!info) |
260 | goto err_out; | 234 | goto err_out; |
261 | } | ||
262 | memset(info, 0, sizeof(*info)); | 235 | memset(info, 0, sizeof(*info)); |
263 | 236 | ||
264 | dev_set_drvdata(&dev->dev, info); | 237 | dev_set_drvdata(&dev->dev, info); |
265 | 238 | ||
239 | err = -EBUSY; | ||
266 | info->res = request_mem_region(res.start, res.end - res.start + 1, | 240 | info->res = request_mem_region(res.start, res.end - res.start + 1, |
267 | dev->dev.bus_id); | 241 | dev->dev.bus_id); |
268 | if (info->res == NULL) { | 242 | if (!info->res) |
269 | dev_err(&dev->dev, "Could not reserve memory region\n"); | ||
270 | err = -ENOMEM; | ||
271 | goto err_out; | 243 | goto err_out; |
272 | } | ||
273 | 244 | ||
245 | err = -ENXIO; | ||
274 | width = of_get_property(dp, "bank-width", NULL); | 246 | width = of_get_property(dp, "bank-width", NULL); |
275 | if (width == NULL) { | 247 | if (!width) { |
276 | dev_err(&dev->dev, "Can't get the flash bank width!\n"); | 248 | dev_err(&dev->dev, "Can't get bank width from device tree\n"); |
277 | err = -EINVAL; | ||
278 | goto err_out; | 249 | goto err_out; |
279 | } | 250 | } |
280 | 251 | ||
@@ -283,10 +254,10 @@ static int __devinit of_physmap_probe(struct of_device *dev, const struct of_dev | |||
283 | info->map.size = res.end - res.start + 1; | 254 | info->map.size = res.end - res.start + 1; |
284 | info->map.bankwidth = *width; | 255 | info->map.bankwidth = *width; |
285 | 256 | ||
257 | err = -ENOMEM; | ||
286 | info->map.virt = ioremap(info->map.phys, info->map.size); | 258 | info->map.virt = ioremap(info->map.phys, info->map.size); |
287 | if (info->map.virt == NULL) { | 259 | if (!info->map.virt) { |
288 | dev_err(&dev->dev, "Failed to ioremap flash region\n"); | 260 | dev_err(&dev->dev, "Failed to ioremap() flash region\n"); |
289 | err = EIO; | ||
290 | goto err_out; | 261 | goto err_out; |
291 | } | 262 | } |
292 | 263 | ||
@@ -297,25 +268,30 @@ static int __devinit of_physmap_probe(struct of_device *dev, const struct of_dev | |||
297 | else | 268 | else |
298 | info->mtd = obsolete_probe(dev, &info->map); | 269 | info->mtd = obsolete_probe(dev, &info->map); |
299 | 270 | ||
300 | if (info->mtd == NULL) { | 271 | err = -ENXIO; |
301 | dev_err(&dev->dev, "map_probe failed\n"); | 272 | if (!info->mtd) { |
302 | err = -ENXIO; | 273 | dev_err(&dev->dev, "do_map_probe() failed\n"); |
303 | goto err_out; | 274 | goto err_out; |
304 | } | 275 | } |
305 | info->mtd->owner = THIS_MODULE; | 276 | info->mtd->owner = THIS_MODULE; |
306 | 277 | ||
307 | return process_partitions(info, dev); | 278 | err = parse_partitions(info, dev); |
279 | if (err < 0) | ||
280 | goto err_out; | ||
308 | 281 | ||
309 | err_out: | 282 | if (err > 0) |
310 | of_physmap_remove(dev); | 283 | add_mtd_partitions(info->mtd, OF_FLASH_PARTS(info), err); |
311 | return err; | 284 | else |
285 | add_mtd_device(info->mtd); | ||
312 | 286 | ||
313 | return 0; | 287 | return 0; |
314 | 288 | ||
315 | 289 | err_out: | |
290 | of_flash_remove(dev); | ||
291 | return err; | ||
316 | } | 292 | } |
317 | 293 | ||
318 | static struct of_device_id of_physmap_match[] = { | 294 | static struct of_device_id of_flash_match[] = { |
319 | { | 295 | { |
320 | .compatible = "cfi-flash", | 296 | .compatible = "cfi-flash", |
321 | .data = (void *)"cfi_probe", | 297 | .data = (void *)"cfi_probe", |
@@ -337,30 +313,28 @@ static struct of_device_id of_physmap_match[] = { | |||
337 | }, | 313 | }, |
338 | { }, | 314 | { }, |
339 | }; | 315 | }; |
316 | MODULE_DEVICE_TABLE(of, of_flash_match); | ||
340 | 317 | ||
341 | MODULE_DEVICE_TABLE(of, of_physmap_match); | 318 | static struct of_platform_driver of_flash_driver = { |
342 | 319 | .name = "of-flash", | |
343 | 320 | .match_table = of_flash_match, | |
344 | static struct of_platform_driver of_physmap_flash_driver = { | 321 | .probe = of_flash_probe, |
345 | .name = "physmap-flash", | 322 | .remove = of_flash_remove, |
346 | .match_table = of_physmap_match, | ||
347 | .probe = of_physmap_probe, | ||
348 | .remove = of_physmap_remove, | ||
349 | }; | 323 | }; |
350 | 324 | ||
351 | static int __init of_physmap_init(void) | 325 | static int __init of_flash_init(void) |
352 | { | 326 | { |
353 | return of_register_platform_driver(&of_physmap_flash_driver); | 327 | return of_register_platform_driver(&of_flash_driver); |
354 | } | 328 | } |
355 | 329 | ||
356 | static void __exit of_physmap_exit(void) | 330 | static void __exit of_flash_exit(void) |
357 | { | 331 | { |
358 | of_unregister_platform_driver(&of_physmap_flash_driver); | 332 | of_unregister_platform_driver(&of_flash_driver); |
359 | } | 333 | } |
360 | 334 | ||
361 | module_init(of_physmap_init); | 335 | module_init(of_flash_init); |
362 | module_exit(of_physmap_exit); | 336 | module_exit(of_flash_exit); |
363 | 337 | ||
364 | MODULE_LICENSE("GPL"); | 338 | MODULE_LICENSE("GPL"); |
365 | MODULE_AUTHOR("Vitaly Wool <vwool@ru.mvista.com>"); | 339 | MODULE_AUTHOR("Vitaly Wool <vwool@ru.mvista.com>"); |
366 | MODULE_DESCRIPTION("Configurable MTD map driver for OF"); | 340 | MODULE_DESCRIPTION("Device tree based MTD map driver"); |