[ARM] Add support for init/exit methods in sa1100 MTD map driver
[linux-2.6] / 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  * $Id: sa1100-flash.c,v 1.47 2004/11/01 13:44:36 rmk Exp $
7  */
8 #include <linux/config.h>
9 #include <linux/module.h>
10 #include <linux/types.h>
11 #include <linux/ioport.h>
12 #include <linux/kernel.h>
13 #include <linux/init.h>
14 #include <linux/errno.h>
15 #include <linux/slab.h>
16 #include <linux/device.h>
17 #include <linux/err.h>
18
19 #include <linux/mtd/mtd.h>
20 #include <linux/mtd/map.h>
21 #include <linux/mtd/partitions.h>
22 #include <linux/mtd/concat.h>
23
24 #include <asm/hardware.h>
25 #include <asm/io.h>
26 #include <asm/sizes.h>
27 #include <asm/mach/flash.h>
28
29 #if 0
30 /*
31  * This is here for documentation purposes only - until these people
32  * submit their machine types.  It will be gone January 2005.
33  */
34 static struct mtd_partition consus_partitions[] = {
35         {
36                 .name           = "Consus boot firmware",
37                 .offset         = 0,
38                 .size           = 0x00040000,
39                 .mask_flags     = MTD_WRITABLE, /* force read-only */
40         }, {
41                 .name           = "Consus kernel",
42                 .offset         = 0x00040000,
43                 .size           = 0x00100000,
44                 .mask_flags     = 0,
45         }, {
46                 .name           = "Consus disk",
47                 .offset         = 0x00140000,
48                 /* The rest (up to 16M) for jffs.  We could put 0 and
49                    make it find the size automatically, but right now
50                    i have 32 megs.  jffs will use all 32 megs if given
51                    the chance, and this leads to horrible problems
52                    when you try to re-flash the image because blob
53                    won't erase the whole partition. */
54                 .size           = 0x01000000 - 0x00140000,
55                 .mask_flags     = 0,
56         }, {
57                 /* this disk is a secondary disk, which can be used as
58                    needed, for simplicity, make it the size of the other
59                    consus partition, although realistically it could be
60                    the remainder of the disk (depending on the file
61                    system used) */
62                  .name          = "Consus disk2",
63                  .offset        = 0x01000000,
64                  .size          = 0x01000000 - 0x00140000,
65                  .mask_flags    = 0,
66         }
67 };
68
69 /* Frodo has 2 x 16M 28F128J3A flash chips in bank 0: */
70 static struct mtd_partition frodo_partitions[] =
71 {
72         {
73                 .name           = "bootloader",
74                 .size           = 0x00040000,
75                 .offset         = 0x00000000,
76                 .mask_flags     = MTD_WRITEABLE
77         }, {
78                 .name           = "bootloader params",
79                 .size           = 0x00040000,
80                 .offset         = MTDPART_OFS_APPEND,
81                 .mask_flags     = MTD_WRITEABLE
82         }, {
83                 .name           = "kernel",
84                 .size           = 0x00100000,
85                 .offset         = MTDPART_OFS_APPEND,
86                 .mask_flags     = MTD_WRITEABLE
87         }, {
88                 .name           = "ramdisk",
89                 .size           = 0x00400000,
90                 .offset         = MTDPART_OFS_APPEND,
91                 .mask_flags     = MTD_WRITEABLE
92         }, {
93                 .name           = "file system",
94                 .size           = MTDPART_SIZ_FULL,
95                 .offset         = MTDPART_OFS_APPEND
96         }
97 };
98
99 static struct mtd_partition jornada56x_partitions[] = {
100         {
101                 .name           = "bootldr",
102                 .size           = 0x00040000,
103                 .offset         = 0,
104                 .mask_flags     = MTD_WRITEABLE,
105         }, {
106                 .name           = "rootfs",
107                 .size           = MTDPART_SIZ_FULL,
108                 .offset         = MTDPART_OFS_APPEND,
109         }
110 };
111
112 static void jornada56x_set_vpp(int vpp)
113 {
114         if (vpp)
115                 GPSR = GPIO_GPIO26;
116         else
117                 GPCR = GPIO_GPIO26;
118         GPDR |= GPIO_GPIO26;
119 }
120
121 /*
122  * Machine        Phys          Size    set_vpp
123  * Consus    : SA1100_CS0_PHYS SZ_32M
124  * Frodo     : SA1100_CS0_PHYS SZ_32M
125  * Jornada56x: SA1100_CS0_PHYS SZ_32M jornada56x_set_vpp
126  */
127 #endif
128
129 struct sa_subdev_info {
130         char name[16];
131         struct map_info map;
132         struct mtd_info *mtd;
133         struct flash_platform_data *plat;
134 };
135
136 struct sa_info {
137         struct mtd_partition    *parts;
138         struct mtd_info         *mtd;
139         int                     num_subdev;
140         struct sa_subdev_info   subdev[0];
141 };
142
143 static void sa1100_set_vpp(struct map_info *map, int on)
144 {
145         struct sa_subdev_info *subdev = container_of(map, struct sa_subdev_info, map);
146         subdev->plat->set_vpp(on);
147 }
148
149 static void sa1100_destroy_subdev(struct sa_subdev_info *subdev)
150 {
151         if (subdev->mtd)
152                 map_destroy(subdev->mtd);
153         if (subdev->map.virt)
154                 iounmap(subdev->map.virt);
155         release_mem_region(subdev->map.phys, subdev->map.size);
156 }
157
158 static int sa1100_probe_subdev(struct sa_subdev_info *subdev, struct resource *res)
159 {
160         unsigned long phys;
161         unsigned int size;
162         int ret;
163
164         phys = res->start;
165         size = res->end - phys + 1;
166
167         /*
168          * Retrieve the bankwidth from the MSC registers.
169          * We currently only implement CS0 and CS1 here.
170          */
171         switch (phys) {
172         default:
173                 printk(KERN_WARNING "SA1100 flash: unknown base address "
174                        "0x%08lx, assuming CS0\n", phys);
175
176         case SA1100_CS0_PHYS:
177                 subdev->map.bankwidth = (MSC0 & MSC_RBW) ? 2 : 4;
178                 break;
179
180         case SA1100_CS1_PHYS:
181                 subdev->map.bankwidth = ((MSC0 >> 16) & MSC_RBW) ? 2 : 4;
182                 break;
183         }
184
185         if (!request_mem_region(phys, size, subdev->name)) {
186                 ret = -EBUSY;
187                 goto out;
188         }
189
190         if (subdev->plat->set_vpp)
191                 subdev->map.set_vpp = sa1100_set_vpp;
192
193         subdev->map.phys = phys;
194         subdev->map.size = size;
195         subdev->map.virt = ioremap(phys, size);
196         if (!subdev->map.virt) {
197                 ret = -ENOMEM;
198                 goto err;
199         }
200
201         simple_map_init(&subdev->map);
202
203         /*
204          * Now let's probe for the actual flash.  Do it here since
205          * specific machine settings might have been set above.
206          */
207         subdev->mtd = do_map_probe(subdev->plat->map_name, &subdev->map);
208         if (subdev->mtd == NULL) {
209                 ret = -ENXIO;
210                 goto err;
211         }
212         subdev->mtd->owner = THIS_MODULE;
213
214         printk(KERN_INFO "SA1100 flash: CFI device at 0x%08lx, %dMiB, "
215                 "%d-bit\n", phys, subdev->mtd->size >> 20,
216                 subdev->map.bankwidth * 8);
217
218         return 0;
219
220  err:
221         sa1100_destroy_subdev(subdev);
222  out:
223         return ret;
224 }
225
226 static void sa1100_destroy(struct sa_info *info, struct flash_platform_data *plat)
227 {
228         int i;
229
230         if (info->mtd) {
231                 del_mtd_partitions(info->mtd);
232
233 #ifdef CONFIG_MTD_CONCAT
234                 if (info->mtd != info->subdev[0].mtd)
235                         mtd_concat_destroy(info->mtd);
236 #endif
237         }
238
239         if (info->parts)
240                 kfree(info->parts);
241
242         for (i = info->num_subdev - 1; i >= 0; i--)
243                 sa1100_destroy_subdev(&info->subdev[i]);
244         kfree(info);
245
246         if (plat->exit)
247                 plat->exit();
248 }
249
250 static struct sa_info *__init
251 sa1100_setup_mtd(struct platform_device *pdev, struct flash_platform_data *plat)
252 {
253         struct sa_info *info;
254         int nr, size, i, ret = 0;
255
256         /*
257          * Count number of devices.
258          */
259         for (nr = 0; ; nr++)
260                 if (!platform_get_resource(pdev, IORESOURCE_MEM, nr))
261                         break;
262
263         if (nr == 0) {
264                 ret = -ENODEV;
265                 goto out;
266         }
267
268         size = sizeof(struct sa_info) + sizeof(struct sa_subdev_info) * nr;
269
270         /*
271          * Allocate the map_info structs in one go.
272          */
273         info = kmalloc(size, GFP_KERNEL);
274         if (!info) {
275                 ret = -ENOMEM;
276                 goto out;
277         }
278
279         memset(info, 0, size);
280
281         if (plat->init) {
282                 ret = plat->init();
283                 if (ret)
284                         goto err;
285         }
286
287         /*
288          * Claim and then map the memory regions.
289          */
290         for (i = 0; i < nr; i++) {
291                 struct sa_subdev_info *subdev = &info->subdev[i];
292                 struct resource *res;
293
294                 res = platform_get_resource(pdev, IORESOURCE_MEM, i);
295                 if (!res)
296                         break;
297
298                 subdev->map.name = subdev->name;
299                 sprintf(subdev->name, "sa1100-%d", i);
300                 subdev->plat = plat;
301
302                 ret = sa1100_probe_subdev(subdev, res);
303                 if (ret)
304                         break;
305         }
306
307         info->num_subdev = i;
308
309         /*
310          * ENXIO is special.  It means we didn't find a chip when we probed.
311          */
312         if (ret != 0 && !(ret == -ENXIO && info->num_subdev > 0))
313                 goto err;
314
315         /*
316          * If we found one device, don't bother with concat support.  If
317          * we found multiple devices, use concat if we have it available,
318          * otherwise fail.  Either way, it'll be called "sa1100".
319          */
320         if (info->num_subdev == 1) {
321                 strcpy(info->subdev[0].name, "sa1100");
322                 info->mtd = info->subdev[0].mtd;
323                 ret = 0;
324         } else if (info->num_subdev > 1) {
325 #ifdef CONFIG_MTD_CONCAT
326                 struct mtd_info *cdev[nr];
327                 /*
328                  * We detected multiple devices.  Concatenate them together.
329                  */
330                 for (i = 0; i < info->num_subdev; i++)
331                         cdev[i] = info->subdev[i].mtd;
332
333                 info->mtd = mtd_concat_create(cdev, info->num_subdev,
334                                               "sa1100");
335                 if (info->mtd == NULL)
336                         ret = -ENXIO;
337 #else
338                 printk(KERN_ERR "SA1100 flash: multiple devices "
339                        "found but MTD concat support disabled.\n");
340                 ret = -ENXIO;
341 #endif
342         }
343
344         if (ret == 0)
345                 return info;
346
347  err:
348         sa1100_destroy(info, plat);
349  out:
350         return ERR_PTR(ret);
351 }
352
353 static const char *part_probes[] = { "cmdlinepart", "RedBoot", NULL };
354
355 static int __init sa1100_mtd_probe(struct device *dev)
356 {
357         struct platform_device *pdev = to_platform_device(dev);
358         struct flash_platform_data *plat = pdev->dev.platform_data;
359         struct mtd_partition *parts;
360         const char *part_type = NULL;
361         struct sa_info *info;
362         int err, nr_parts = 0;
363
364         if (!plat)
365                 return -ENODEV;
366
367         info = sa1100_setup_mtd(pdev, plat);
368         if (IS_ERR(info)) {
369                 err = PTR_ERR(info);
370                 goto out;
371         }
372
373         /*
374          * Partition selection stuff.
375          */
376 #ifdef CONFIG_MTD_PARTITIONS
377         nr_parts = parse_mtd_partitions(info->mtd, part_probes, &parts, 0);
378         if (nr_parts > 0) {
379                 info->parts = parts;
380                 part_type = "dynamic";
381         } else
382 #endif
383         {
384                 parts = plat->parts;
385                 nr_parts = plat->nr_parts;
386                 part_type = "static";
387         }
388
389         if (nr_parts == 0) {
390                 printk(KERN_NOTICE "SA1100 flash: no partition info "
391                         "available, registering whole flash\n");
392                 add_mtd_device(info->mtd);
393         } else {
394                 printk(KERN_NOTICE "SA1100 flash: using %s partition "
395                         "definition\n", part_type);
396                 add_mtd_partitions(info->mtd, parts, nr_parts);
397         }
398
399         dev_set_drvdata(dev, info);
400         err = 0;
401
402  out:
403         return err;
404 }
405
406 static int __exit sa1100_mtd_remove(struct device *dev)
407 {
408         struct sa_info *info = dev_get_drvdata(dev);
409         struct flash_platform_data *plat = dev->platform_data;
410
411         dev_set_drvdata(dev, NULL);
412         sa1100_destroy(info, plat);
413
414         return 0;
415 }
416
417 #ifdef CONFIG_PM
418 static int sa1100_mtd_suspend(struct device *dev, pm_message_t state)
419 {
420         struct sa_info *info = dev_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 device *dev)
430 {
431         struct sa_info *info = dev_get_drvdata(dev);
432         if (info)
433                 info->mtd->resume(info->mtd);
434         return 0;
435 }
436 #else
437 #define sa1100_mtd_suspend NULL
438 #define sa1100_mtd_resume  NULL
439 #endif
440
441 static struct device_driver sa1100_mtd_driver = {
442         .name           = "flash",
443         .bus            = &platform_bus_type,
444         .probe          = sa1100_mtd_probe,
445         .remove         = __exit_p(sa1100_mtd_remove),
446         .suspend        = sa1100_mtd_suspend,
447         .resume         = sa1100_mtd_resume,
448 };
449
450 static int __init sa1100_mtd_init(void)
451 {
452         return driver_register(&sa1100_mtd_driver);
453 }
454
455 static void __exit sa1100_mtd_exit(void)
456 {
457         driver_unregister(&sa1100_mtd_driver);
458 }
459
460 module_init(sa1100_mtd_init);
461 module_exit(sa1100_mtd_exit);
462
463 MODULE_AUTHOR("Nicolas Pitre");
464 MODULE_DESCRIPTION("SA1100 CFI map driver");
465 MODULE_LICENSE("GPL");