blob: da875908ea8e04e20e08edb2785ecac37ef96a31 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * Flash memory access on SA11x0 based devices
Thomas Gleixner69f34c92005-11-07 11:15:40 +00003 *
Nicolas Pitre2f82af02009-09-14 03:25:28 -04004 * (C) 2000 Nicolas Pitre <nico@fluxnic.net>
Linus Torvalds1da177e2005-04-16 15:20:36 -07005 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07006#include <linux/module.h>
7#include <linux/types.h>
8#include <linux/ioport.h>
9#include <linux/kernel.h>
10#include <linux/init.h>
11#include <linux/errno.h>
12#include <linux/slab.h>
Russell Kingd052d1b2005-10-29 19:07:23 +010013#include <linux/platform_device.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070014#include <linux/err.h>
Russell King99730222009-03-25 10:21:35 +000015#include <linux/io.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070016
17#include <linux/mtd/mtd.h>
18#include <linux/mtd/map.h>
19#include <linux/mtd/partitions.h>
20#include <linux/mtd/concat.h>
21
Russell Kinga09e64f2008-08-05 16:14:15 +010022#include <mach/hardware.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070023#include <asm/sizes.h>
24#include <asm/mach/flash.h>
25
26#if 0
27/*
28 * This is here for documentation purposes only - until these people
29 * submit their machine types. It will be gone January 2005.
30 */
31static struct mtd_partition consus_partitions[] = {
32 {
33 .name = "Consus boot firmware",
34 .offset = 0,
35 .size = 0x00040000,
36 .mask_flags = MTD_WRITABLE, /* force read-only */
37 }, {
38 .name = "Consus kernel",
39 .offset = 0x00040000,
40 .size = 0x00100000,
41 .mask_flags = 0,
42 }, {
43 .name = "Consus disk",
44 .offset = 0x00140000,
45 /* The rest (up to 16M) for jffs. We could put 0 and
46 make it find the size automatically, but right now
47 i have 32 megs. jffs will use all 32 megs if given
48 the chance, and this leads to horrible problems
49 when you try to re-flash the image because blob
50 won't erase the whole partition. */
51 .size = 0x01000000 - 0x00140000,
52 .mask_flags = 0,
53 }, {
54 /* this disk is a secondary disk, which can be used as
55 needed, for simplicity, make it the size of the other
56 consus partition, although realistically it could be
57 the remainder of the disk (depending on the file
58 system used) */
59 .name = "Consus disk2",
60 .offset = 0x01000000,
61 .size = 0x01000000 - 0x00140000,
62 .mask_flags = 0,
63 }
64};
65
66/* Frodo has 2 x 16M 28F128J3A flash chips in bank 0: */
67static struct mtd_partition frodo_partitions[] =
68{
69 {
70 .name = "bootloader",
71 .size = 0x00040000,
72 .offset = 0x00000000,
73 .mask_flags = MTD_WRITEABLE
74 }, {
75 .name = "bootloader params",
76 .size = 0x00040000,
77 .offset = MTDPART_OFS_APPEND,
78 .mask_flags = MTD_WRITEABLE
79 }, {
80 .name = "kernel",
81 .size = 0x00100000,
82 .offset = MTDPART_OFS_APPEND,
83 .mask_flags = MTD_WRITEABLE
84 }, {
85 .name = "ramdisk",
86 .size = 0x00400000,
87 .offset = MTDPART_OFS_APPEND,
88 .mask_flags = MTD_WRITEABLE
89 }, {
90 .name = "file system",
91 .size = MTDPART_SIZ_FULL,
92 .offset = MTDPART_OFS_APPEND
93 }
94};
95
96static struct mtd_partition jornada56x_partitions[] = {
97 {
98 .name = "bootldr",
99 .size = 0x00040000,
100 .offset = 0,
101 .mask_flags = MTD_WRITEABLE,
102 }, {
103 .name = "rootfs",
104 .size = MTDPART_SIZ_FULL,
105 .offset = MTDPART_OFS_APPEND,
106 }
107};
108
109static void jornada56x_set_vpp(int vpp)
110{
111 if (vpp)
112 GPSR = GPIO_GPIO26;
113 else
114 GPCR = GPIO_GPIO26;
115 GPDR |= GPIO_GPIO26;
116}
117
118/*
119 * Machine Phys Size set_vpp
120 * Consus : SA1100_CS0_PHYS SZ_32M
121 * Frodo : SA1100_CS0_PHYS SZ_32M
122 * Jornada56x: SA1100_CS0_PHYS SZ_32M jornada56x_set_vpp
123 */
124#endif
125
126struct sa_subdev_info {
127 char name[16];
128 struct map_info map;
129 struct mtd_info *mtd;
Russell King57725f02005-10-29 15:51:14 +0100130 struct flash_platform_data *plat;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700131};
132
133struct sa_info {
134 struct mtd_partition *parts;
135 struct mtd_info *mtd;
136 int num_subdev;
Russell King822e5e72005-10-29 16:03:24 +0100137 unsigned int nr_parts;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700138 struct sa_subdev_info subdev[0];
139};
140
141static void sa1100_set_vpp(struct map_info *map, int on)
142{
143 struct sa_subdev_info *subdev = container_of(map, struct sa_subdev_info, map);
Russell King57725f02005-10-29 15:51:14 +0100144 subdev->plat->set_vpp(on);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700145}
146
147static void sa1100_destroy_subdev(struct sa_subdev_info *subdev)
148{
149 if (subdev->mtd)
150 map_destroy(subdev->mtd);
151 if (subdev->map.virt)
152 iounmap(subdev->map.virt);
153 release_mem_region(subdev->map.phys, subdev->map.size);
154}
155
156static int sa1100_probe_subdev(struct sa_subdev_info *subdev, struct resource *res)
157{
158 unsigned long phys;
159 unsigned int size;
160 int ret;
161
162 phys = res->start;
163 size = res->end - phys + 1;
164
165 /*
166 * Retrieve the bankwidth from the MSC registers.
167 * We currently only implement CS0 and CS1 here.
168 */
169 switch (phys) {
170 default:
171 printk(KERN_WARNING "SA1100 flash: unknown base address "
172 "0x%08lx, assuming CS0\n", phys);
173
174 case SA1100_CS0_PHYS:
175 subdev->map.bankwidth = (MSC0 & MSC_RBW) ? 2 : 4;
176 break;
177
178 case SA1100_CS1_PHYS:
179 subdev->map.bankwidth = ((MSC0 >> 16) & MSC_RBW) ? 2 : 4;
180 break;
181 }
182
183 if (!request_mem_region(phys, size, subdev->name)) {
184 ret = -EBUSY;
185 goto out;
186 }
187
Russell King57725f02005-10-29 15:51:14 +0100188 if (subdev->plat->set_vpp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700189 subdev->map.set_vpp = sa1100_set_vpp;
190
191 subdev->map.phys = phys;
192 subdev->map.size = size;
193 subdev->map.virt = ioremap(phys, size);
194 if (!subdev->map.virt) {
195 ret = -ENOMEM;
196 goto err;
197 }
198
199 simple_map_init(&subdev->map);
200
201 /*
202 * Now let's probe for the actual flash. Do it here since
203 * specific machine settings might have been set above.
204 */
Russell King57725f02005-10-29 15:51:14 +0100205 subdev->mtd = do_map_probe(subdev->plat->map_name, &subdev->map);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700206 if (subdev->mtd == NULL) {
207 ret = -ENXIO;
208 goto err;
209 }
210 subdev->mtd->owner = THIS_MODULE;
211
Russell King794d5792009-09-27 23:51:04 +0100212 printk(KERN_INFO "SA1100 flash: CFI device at 0x%08lx, %uMiB, %d-bit\n",
213 phys, (unsigned)(subdev->mtd->size >> 20),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700214 subdev->map.bankwidth * 8);
215
216 return 0;
217
218 err:
219 sa1100_destroy_subdev(subdev);
220 out:
221 return ret;
222}
223
Russell King0d2ef7d2005-10-29 15:57:20 +0100224static void sa1100_destroy(struct sa_info *info, struct flash_platform_data *plat)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700225{
226 int i;
227
228 if (info->mtd) {
Russell King822e5e72005-10-29 16:03:24 +0100229 if (info->nr_parts == 0)
230 del_mtd_device(info->mtd);
231#ifdef CONFIG_MTD_PARTITIONS
232 else
233 del_mtd_partitions(info->mtd);
234#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700235 if (info->mtd != info->subdev[0].mtd)
236 mtd_concat_destroy(info->mtd);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700237 }
238
Jesper Juhlfa671642005-11-07 01:01:27 -0800239 kfree(info->parts);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700240
241 for (i = info->num_subdev - 1; i >= 0; i--)
242 sa1100_destroy_subdev(&info->subdev[i]);
243 kfree(info);
Russell King0d2ef7d2005-10-29 15:57:20 +0100244
245 if (plat->exit)
246 plat->exit();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700247}
248
Dmitry Artamonow5a134232009-10-09 12:18:49 +0400249static struct sa_info *__devinit
Russell King57725f02005-10-29 15:51:14 +0100250sa1100_setup_mtd(struct platform_device *pdev, struct flash_platform_data *plat)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700251{
252 struct sa_info *info;
253 int nr, size, i, ret = 0;
254
255 /*
256 * Count number of devices.
257 */
258 for (nr = 0; ; nr++)
259 if (!platform_get_resource(pdev, IORESOURCE_MEM, nr))
260 break;
261
262 if (nr == 0) {
263 ret = -ENODEV;
264 goto out;
265 }
266
267 size = sizeof(struct sa_info) + sizeof(struct sa_subdev_info) * nr;
268
269 /*
270 * Allocate the map_info structs in one go.
271 */
Burman Yan95b93a02006-11-15 21:10:29 +0200272 info = kzalloc(size, GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700273 if (!info) {
274 ret = -ENOMEM;
275 goto out;
276 }
277
Russell King0d2ef7d2005-10-29 15:57:20 +0100278 if (plat->init) {
279 ret = plat->init();
280 if (ret)
281 goto err;
282 }
283
Linus Torvalds1da177e2005-04-16 15:20:36 -0700284 /*
285 * Claim and then map the memory regions.
286 */
287 for (i = 0; i < nr; i++) {
288 struct sa_subdev_info *subdev = &info->subdev[i];
289 struct resource *res;
290
291 res = platform_get_resource(pdev, IORESOURCE_MEM, i);
292 if (!res)
293 break;
294
295 subdev->map.name = subdev->name;
Russell King14e66f72005-10-29 16:08:31 +0100296 sprintf(subdev->name, "%s-%d", plat->name, i);
Russell King57725f02005-10-29 15:51:14 +0100297 subdev->plat = plat;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700298
299 ret = sa1100_probe_subdev(subdev, res);
300 if (ret)
301 break;
302 }
303
304 info->num_subdev = i;
305
306 /*
307 * ENXIO is special. It means we didn't find a chip when we probed.
308 */
309 if (ret != 0 && !(ret == -ENXIO && info->num_subdev > 0))
310 goto err;
311
312 /*
313 * If we found one device, don't bother with concat support. If
314 * we found multiple devices, use concat if we have it available,
315 * otherwise fail. Either way, it'll be called "sa1100".
316 */
317 if (info->num_subdev == 1) {
Russell King14e66f72005-10-29 16:08:31 +0100318 strcpy(info->subdev[0].name, plat->name);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700319 info->mtd = info->subdev[0].mtd;
320 ret = 0;
321 } else if (info->num_subdev > 1) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700322 struct mtd_info *cdev[nr];
323 /*
324 * We detected multiple devices. Concatenate them together.
325 */
326 for (i = 0; i < info->num_subdev; i++)
327 cdev[i] = info->subdev[i].mtd;
328
329 info->mtd = mtd_concat_create(cdev, info->num_subdev,
Russell King14e66f72005-10-29 16:08:31 +0100330 plat->name);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700331 if (info->mtd == NULL)
332 ret = -ENXIO;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700333 }
334
335 if (ret == 0)
336 return info;
337
338 err:
Russell King0d2ef7d2005-10-29 15:57:20 +0100339 sa1100_destroy(info, plat);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700340 out:
341 return ERR_PTR(ret);
342}
343
344static const char *part_probes[] = { "cmdlinepart", "RedBoot", NULL };
345
Uwe Kleine-Königf0b1e582009-03-28 00:27:03 +0100346static int __devinit sa1100_mtd_probe(struct platform_device *pdev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700347{
Russell King57725f02005-10-29 15:51:14 +0100348 struct flash_platform_data *plat = pdev->dev.platform_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700349 struct mtd_partition *parts;
350 const char *part_type = NULL;
351 struct sa_info *info;
352 int err, nr_parts = 0;
353
Russell King57725f02005-10-29 15:51:14 +0100354 if (!plat)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700355 return -ENODEV;
356
Russell King57725f02005-10-29 15:51:14 +0100357 info = sa1100_setup_mtd(pdev, plat);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700358 if (IS_ERR(info)) {
359 err = PTR_ERR(info);
360 goto out;
361 }
362
363 /*
364 * Partition selection stuff.
365 */
366#ifdef CONFIG_MTD_PARTITIONS
367 nr_parts = parse_mtd_partitions(info->mtd, part_probes, &parts, 0);
368 if (nr_parts > 0) {
369 info->parts = parts;
370 part_type = "dynamic";
371 } else
372#endif
373 {
Russell King57725f02005-10-29 15:51:14 +0100374 parts = plat->parts;
375 nr_parts = plat->nr_parts;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700376 part_type = "static";
377 }
378
379 if (nr_parts == 0) {
380 printk(KERN_NOTICE "SA1100 flash: no partition info "
381 "available, registering whole flash\n");
382 add_mtd_device(info->mtd);
383 } else {
384 printk(KERN_NOTICE "SA1100 flash: using %s partition "
385 "definition\n", part_type);
386 add_mtd_partitions(info->mtd, parts, nr_parts);
387 }
388
Russell King822e5e72005-10-29 16:03:24 +0100389 info->nr_parts = nr_parts;
390
Russell King3ae5eae2005-11-09 22:32:44 +0000391 platform_set_drvdata(pdev, info);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700392 err = 0;
393
394 out:
395 return err;
396}
397
Russell King3ae5eae2005-11-09 22:32:44 +0000398static int __exit sa1100_mtd_remove(struct platform_device *pdev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700399{
Russell King3ae5eae2005-11-09 22:32:44 +0000400 struct sa_info *info = platform_get_drvdata(pdev);
401 struct flash_platform_data *plat = pdev->dev.platform_data;
Russell King0d2ef7d2005-10-29 15:57:20 +0100402
Russell King3ae5eae2005-11-09 22:32:44 +0000403 platform_set_drvdata(pdev, NULL);
Russell King0d2ef7d2005-10-29 15:57:20 +0100404 sa1100_destroy(info, plat);
405
Linus Torvalds1da177e2005-04-16 15:20:36 -0700406 return 0;
407}
408
409#ifdef CONFIG_PM
Russell King3ae5eae2005-11-09 22:32:44 +0000410static void sa1100_mtd_shutdown(struct platform_device *dev)
Russell King13bfb342005-10-29 16:14:08 +0100411{
Russell King3ae5eae2005-11-09 22:32:44 +0000412 struct sa_info *info = platform_get_drvdata(dev);
Russell King13bfb342005-10-29 16:14:08 +0100413 if (info && info->mtd->suspend(info->mtd) == 0)
414 info->mtd->resume(info->mtd);
415}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700416#else
Russell King13bfb342005-10-29 16:14:08 +0100417#define sa1100_mtd_shutdown NULL
Linus Torvalds1da177e2005-04-16 15:20:36 -0700418#endif
419
Russell King3ae5eae2005-11-09 22:32:44 +0000420static struct platform_driver sa1100_mtd_driver = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700421 .probe = sa1100_mtd_probe,
422 .remove = __exit_p(sa1100_mtd_remove),
Russell King13bfb342005-10-29 16:14:08 +0100423 .shutdown = sa1100_mtd_shutdown,
Russell King3ae5eae2005-11-09 22:32:44 +0000424 .driver = {
Uwe Kleine-Königbcc8f3e2009-01-31 01:21:58 +0100425 .name = "sa1100-mtd",
Kay Sievers41d867c2008-04-18 13:44:26 -0700426 .owner = THIS_MODULE,
Russell King3ae5eae2005-11-09 22:32:44 +0000427 },
Linus Torvalds1da177e2005-04-16 15:20:36 -0700428};
429
430static int __init sa1100_mtd_init(void)
431{
Russell King3ae5eae2005-11-09 22:32:44 +0000432 return platform_driver_register(&sa1100_mtd_driver);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700433}
434
435static void __exit sa1100_mtd_exit(void)
436{
Russell King3ae5eae2005-11-09 22:32:44 +0000437 platform_driver_unregister(&sa1100_mtd_driver);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700438}
439
440module_init(sa1100_mtd_init);
441module_exit(sa1100_mtd_exit);
442
443MODULE_AUTHOR("Nicolas Pitre");
444MODULE_DESCRIPTION("SA1100 CFI map driver");
445MODULE_LICENSE("GPL");
Uwe Kleine-Königbcc8f3e2009-01-31 01:21:58 +0100446MODULE_ALIAS("platform:sa1100-mtd");