Merge git://git.infradead.org/mtd-2.6
[pandora-kernel.git] / drivers / mtd / maps / sa1100-flash.c
1 /*
2  * Flash memory access on SA11x0 based devices
3  *
4  * (C) 2000 Nicolas Pitre <nico@cam.org>
5  */
6 #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>
13 #include <linux/platform_device.h>
14 #include <linux/err.h>
15 #include <linux/io.h>
16
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
22 #include <mach/hardware.h>
23 #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  */
31 static 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: */
67 static 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
96 static 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
109 static 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
126 struct sa_subdev_info {
127         char name[16];
128         struct map_info map;
129         struct mtd_info *mtd;
130         struct flash_platform_data *plat;
131 };
132
133 struct sa_info {
134         struct mtd_partition    *parts;
135         struct mtd_info         *mtd;
136         int                     num_subdev;
137         unsigned int            nr_parts;
138         struct sa_subdev_info   subdev[0];
139 };
140
141 static 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);
144         subdev->plat->set_vpp(on);
145 }
146
147 static 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
156 static 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
188         if (subdev->plat->set_vpp)
189                 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          */
205         subdev->mtd = do_map_probe(subdev->plat->map_name, &subdev->map);
206         if (subdev->mtd == NULL) {
207                 ret = -ENXIO;
208                 goto err;
209         }
210         subdev->mtd->owner = THIS_MODULE;
211
212         printk(KERN_INFO "SA1100 flash: CFI device at 0x%08lx, %dMiB, "
213                 "%d-bit\n", phys, subdev->mtd->size >> 20,
214                 subdev->map.bankwidth * 8);
215
216         return 0;
217
218  err:
219         sa1100_destroy_subdev(subdev);
220  out:
221         return ret;
222 }
223
224 static void sa1100_destroy(struct sa_info *info, struct flash_platform_data *plat)
225 {
226         int i;
227
228         if (info->mtd) {
229                 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
235 #ifdef CONFIG_MTD_CONCAT
236                 if (info->mtd != info->subdev[0].mtd)
237                         mtd_concat_destroy(info->mtd);
238 #endif
239         }
240
241         kfree(info->parts);
242
243         for (i = info->num_subdev - 1; i >= 0; i--)
244                 sa1100_destroy_subdev(&info->subdev[i]);
245         kfree(info);
246
247         if (plat->exit)
248                 plat->exit();
249 }
250
251 static struct sa_info *__init
252 sa1100_setup_mtd(struct platform_device *pdev, struct flash_platform_data *plat)
253 {
254         struct sa_info *info;
255         int nr, size, i, ret = 0;
256
257         /*
258          * Count number of devices.
259          */
260         for (nr = 0; ; nr++)
261                 if (!platform_get_resource(pdev, IORESOURCE_MEM, nr))
262                         break;
263
264         if (nr == 0) {
265                 ret = -ENODEV;
266                 goto out;
267         }
268
269         size = sizeof(struct sa_info) + sizeof(struct sa_subdev_info) * nr;
270
271         /*
272          * Allocate the map_info structs in one go.
273          */
274         info = kzalloc(size, GFP_KERNEL);
275         if (!info) {
276                 ret = -ENOMEM;
277                 goto out;
278         }
279
280         if (plat->init) {
281                 ret = plat->init();
282                 if (ret)
283                         goto err;
284         }
285
286         /*
287          * Claim and then map the memory regions.
288          */
289         for (i = 0; i < nr; i++) {
290                 struct sa_subdev_info *subdev = &info->subdev[i];
291                 struct resource *res;
292
293                 res = platform_get_resource(pdev, IORESOURCE_MEM, i);
294                 if (!res)
295                         break;
296
297                 subdev->map.name = subdev->name;
298                 sprintf(subdev->name, "%s-%d", plat->name, i);
299                 subdev->plat = plat;
300
301                 ret = sa1100_probe_subdev(subdev, res);
302                 if (ret)
303                         break;
304         }
305
306         info->num_subdev = i;
307
308         /*
309          * ENXIO is special.  It means we didn't find a chip when we probed.
310          */
311         if (ret != 0 && !(ret == -ENXIO && info->num_subdev > 0))
312                 goto err;
313
314         /*
315          * If we found one device, don't bother with concat support.  If
316          * we found multiple devices, use concat if we have it available,
317          * otherwise fail.  Either way, it'll be called "sa1100".
318          */
319         if (info->num_subdev == 1) {
320                 strcpy(info->subdev[0].name, plat->name);
321                 info->mtd = info->subdev[0].mtd;
322                 ret = 0;
323         } else if (info->num_subdev > 1) {
324 #ifdef CONFIG_MTD_CONCAT
325                 struct mtd_info *cdev[nr];
326                 /*
327                  * We detected multiple devices.  Concatenate them together.
328                  */
329                 for (i = 0; i < info->num_subdev; i++)
330                         cdev[i] = info->subdev[i].mtd;
331
332                 info->mtd = mtd_concat_create(cdev, info->num_subdev,
333                                               plat->name);
334                 if (info->mtd == NULL)
335                         ret = -ENXIO;
336 #else
337                 printk(KERN_ERR "SA1100 flash: multiple devices "
338                        "found but MTD concat support disabled.\n");
339                 ret = -ENXIO;
340 #endif
341         }
342
343         if (ret == 0)
344                 return info;
345
346  err:
347         sa1100_destroy(info, plat);
348  out:
349         return ERR_PTR(ret);
350 }
351
352 static const char *part_probes[] = { "cmdlinepart", "RedBoot", NULL };
353
354 static int __devinit sa1100_mtd_probe(struct platform_device *pdev)
355 {
356         struct flash_platform_data *plat = pdev->dev.platform_data;
357         struct mtd_partition *parts;
358         const char *part_type = NULL;
359         struct sa_info *info;
360         int err, nr_parts = 0;
361
362         if (!plat)
363                 return -ENODEV;
364
365         info = sa1100_setup_mtd(pdev, plat);
366         if (IS_ERR(info)) {
367                 err = PTR_ERR(info);
368                 goto out;
369         }
370
371         /*
372          * Partition selection stuff.
373          */
374 #ifdef CONFIG_MTD_PARTITIONS
375         nr_parts = parse_mtd_partitions(info->mtd, part_probes, &parts, 0);
376         if (nr_parts > 0) {
377                 info->parts = parts;
378                 part_type = "dynamic";
379         } else
380 #endif
381         {
382                 parts = plat->parts;
383                 nr_parts = plat->nr_parts;
384                 part_type = "static";
385         }
386
387         if (nr_parts == 0) {
388                 printk(KERN_NOTICE "SA1100 flash: no partition info "
389                         "available, registering whole flash\n");
390                 add_mtd_device(info->mtd);
391         } else {
392                 printk(KERN_NOTICE "SA1100 flash: using %s partition "
393                         "definition\n", part_type);
394                 add_mtd_partitions(info->mtd, parts, nr_parts);
395         }
396
397         info->nr_parts = nr_parts;
398
399         platform_set_drvdata(pdev, info);
400         err = 0;
401
402  out:
403         return err;
404 }
405
406 static int __exit sa1100_mtd_remove(struct platform_device *pdev)
407 {
408         struct sa_info *info = platform_get_drvdata(pdev);
409         struct flash_platform_data *plat = pdev->dev.platform_data;
410
411         platform_set_drvdata(pdev, NULL);
412         sa1100_destroy(info, plat);
413
414         return 0;
415 }
416
417 #ifdef CONFIG_PM
418 static int sa1100_mtd_suspend(struct platform_device *dev, pm_message_t state)
419 {
420         struct sa_info *info = platform_get_drvdata(dev);
421         int ret = 0;
422
423         if (info)
424                 ret = info->mtd->suspend(info->mtd);
425
426         return ret;
427 }
428
429 static int sa1100_mtd_resume(struct platform_device *dev)
430 {
431         struct sa_info *info = platform_get_drvdata(dev);
432         if (info)
433                 info->mtd->resume(info->mtd);
434         return 0;
435 }
436
437 static void sa1100_mtd_shutdown(struct platform_device *dev)
438 {
439         struct sa_info *info = platform_get_drvdata(dev);
440         if (info && info->mtd->suspend(info->mtd) == 0)
441                 info->mtd->resume(info->mtd);
442 }
443 #else
444 #define sa1100_mtd_suspend NULL
445 #define sa1100_mtd_resume  NULL
446 #define sa1100_mtd_shutdown NULL
447 #endif
448
449 static struct platform_driver sa1100_mtd_driver = {
450         .probe          = sa1100_mtd_probe,
451         .remove         = __exit_p(sa1100_mtd_remove),
452         .suspend        = sa1100_mtd_suspend,
453         .resume         = sa1100_mtd_resume,
454         .shutdown       = sa1100_mtd_shutdown,
455         .driver         = {
456                 .name   = "sa1100-mtd",
457                 .owner  = THIS_MODULE,
458         },
459 };
460
461 static int __init sa1100_mtd_init(void)
462 {
463         return platform_driver_register(&sa1100_mtd_driver);
464 }
465
466 static void __exit sa1100_mtd_exit(void)
467 {
468         platform_driver_unregister(&sa1100_mtd_driver);
469 }
470
471 module_init(sa1100_mtd_init);
472 module_exit(sa1100_mtd_exit);
473
474 MODULE_AUTHOR("Nicolas Pitre");
475 MODULE_DESCRIPTION("SA1100 CFI map driver");
476 MODULE_LICENSE("GPL");
477 MODULE_ALIAS("platform:sa1100-mtd");