Merge branch 'fixes' of http://ftp.arm.linux.org.uk/pub/linux/arm/kernel/git-cur...
[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@fluxnic.net>
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_info         *mtd;
135         int                     num_subdev;
136         struct sa_subdev_info   subdev[0];
137 };
138
139 static void sa1100_set_vpp(struct map_info *map, int on)
140 {
141         struct sa_subdev_info *subdev = container_of(map, struct sa_subdev_info, map);
142         subdev->plat->set_vpp(on);
143 }
144
145 static void sa1100_destroy_subdev(struct sa_subdev_info *subdev)
146 {
147         if (subdev->mtd)
148                 map_destroy(subdev->mtd);
149         if (subdev->map.virt)
150                 iounmap(subdev->map.virt);
151         release_mem_region(subdev->map.phys, subdev->map.size);
152 }
153
154 static int sa1100_probe_subdev(struct sa_subdev_info *subdev, struct resource *res)
155 {
156         unsigned long phys;
157         unsigned int size;
158         int ret;
159
160         phys = res->start;
161         size = res->end - phys + 1;
162
163         /*
164          * Retrieve the bankwidth from the MSC registers.
165          * We currently only implement CS0 and CS1 here.
166          */
167         switch (phys) {
168         default:
169                 printk(KERN_WARNING "SA1100 flash: unknown base address "
170                        "0x%08lx, assuming CS0\n", phys);
171
172         case SA1100_CS0_PHYS:
173                 subdev->map.bankwidth = (MSC0 & MSC_RBW) ? 2 : 4;
174                 break;
175
176         case SA1100_CS1_PHYS:
177                 subdev->map.bankwidth = ((MSC0 >> 16) & MSC_RBW) ? 2 : 4;
178                 break;
179         }
180
181         if (!request_mem_region(phys, size, subdev->name)) {
182                 ret = -EBUSY;
183                 goto out;
184         }
185
186         if (subdev->plat->set_vpp)
187                 subdev->map.set_vpp = sa1100_set_vpp;
188
189         subdev->map.phys = phys;
190         subdev->map.size = size;
191         subdev->map.virt = ioremap(phys, size);
192         if (!subdev->map.virt) {
193                 ret = -ENOMEM;
194                 goto err;
195         }
196
197         simple_map_init(&subdev->map);
198
199         /*
200          * Now let's probe for the actual flash.  Do it here since
201          * specific machine settings might have been set above.
202          */
203         subdev->mtd = do_map_probe(subdev->plat->map_name, &subdev->map);
204         if (subdev->mtd == NULL) {
205                 ret = -ENXIO;
206                 goto err;
207         }
208         subdev->mtd->owner = THIS_MODULE;
209
210         printk(KERN_INFO "SA1100 flash: CFI device at 0x%08lx, %uMiB, %d-bit\n",
211                 phys, (unsigned)(subdev->mtd->size >> 20),
212                 subdev->map.bankwidth * 8);
213
214         return 0;
215
216  err:
217         sa1100_destroy_subdev(subdev);
218  out:
219         return ret;
220 }
221
222 static void sa1100_destroy(struct sa_info *info, struct flash_platform_data *plat)
223 {
224         int i;
225
226         if (info->mtd) {
227                 mtd_device_unregister(info->mtd);
228                 if (info->mtd != info->subdev[0].mtd)
229                         mtd_concat_destroy(info->mtd);
230         }
231
232         for (i = info->num_subdev - 1; i >= 0; i--)
233                 sa1100_destroy_subdev(&info->subdev[i]);
234         kfree(info);
235
236         if (plat->exit)
237                 plat->exit();
238 }
239
240 static struct sa_info *__devinit
241 sa1100_setup_mtd(struct platform_device *pdev, struct flash_platform_data *plat)
242 {
243         struct sa_info *info;
244         int nr, size, i, ret = 0;
245
246         /*
247          * Count number of devices.
248          */
249         for (nr = 0; ; nr++)
250                 if (!platform_get_resource(pdev, IORESOURCE_MEM, nr))
251                         break;
252
253         if (nr == 0) {
254                 ret = -ENODEV;
255                 goto out;
256         }
257
258         size = sizeof(struct sa_info) + sizeof(struct sa_subdev_info) * nr;
259
260         /*
261          * Allocate the map_info structs in one go.
262          */
263         info = kzalloc(size, GFP_KERNEL);
264         if (!info) {
265                 ret = -ENOMEM;
266                 goto out;
267         }
268
269         if (plat->init) {
270                 ret = plat->init();
271                 if (ret)
272                         goto err;
273         }
274
275         /*
276          * Claim and then map the memory regions.
277          */
278         for (i = 0; i < nr; i++) {
279                 struct sa_subdev_info *subdev = &info->subdev[i];
280                 struct resource *res;
281
282                 res = platform_get_resource(pdev, IORESOURCE_MEM, i);
283                 if (!res)
284                         break;
285
286                 subdev->map.name = subdev->name;
287                 sprintf(subdev->name, "%s-%d", plat->name, i);
288                 subdev->plat = plat;
289
290                 ret = sa1100_probe_subdev(subdev, res);
291                 if (ret)
292                         break;
293         }
294
295         info->num_subdev = i;
296
297         /*
298          * ENXIO is special.  It means we didn't find a chip when we probed.
299          */
300         if (ret != 0 && !(ret == -ENXIO && info->num_subdev > 0))
301                 goto err;
302
303         /*
304          * If we found one device, don't bother with concat support.  If
305          * we found multiple devices, use concat if we have it available,
306          * otherwise fail.  Either way, it'll be called "sa1100".
307          */
308         if (info->num_subdev == 1) {
309                 strcpy(info->subdev[0].name, plat->name);
310                 info->mtd = info->subdev[0].mtd;
311                 ret = 0;
312         } else if (info->num_subdev > 1) {
313                 struct mtd_info *cdev[nr];
314                 /*
315                  * We detected multiple devices.  Concatenate them together.
316                  */
317                 for (i = 0; i < info->num_subdev; i++)
318                         cdev[i] = info->subdev[i].mtd;
319
320                 info->mtd = mtd_concat_create(cdev, info->num_subdev,
321                                               plat->name);
322                 if (info->mtd == NULL)
323                         ret = -ENXIO;
324         }
325
326         if (ret == 0)
327                 return info;
328
329  err:
330         sa1100_destroy(info, plat);
331  out:
332         return ERR_PTR(ret);
333 }
334
335 static const char *part_probes[] = { "cmdlinepart", "RedBoot", NULL };
336
337 static int __devinit sa1100_mtd_probe(struct platform_device *pdev)
338 {
339         struct flash_platform_data *plat = pdev->dev.platform_data;
340         struct sa_info *info;
341         int err;
342
343         if (!plat)
344                 return -ENODEV;
345
346         info = sa1100_setup_mtd(pdev, plat);
347         if (IS_ERR(info)) {
348                 err = PTR_ERR(info);
349                 goto out;
350         }
351
352         /*
353          * Partition selection stuff.
354          */
355         mtd_device_parse_register(info->mtd, part_probes, 0,
356                         plat->parts, plat->nr_parts);
357
358         platform_set_drvdata(pdev, info);
359         err = 0;
360
361  out:
362         return err;
363 }
364
365 static int __exit sa1100_mtd_remove(struct platform_device *pdev)
366 {
367         struct sa_info *info = platform_get_drvdata(pdev);
368         struct flash_platform_data *plat = pdev->dev.platform_data;
369
370         platform_set_drvdata(pdev, NULL);
371         sa1100_destroy(info, plat);
372
373         return 0;
374 }
375
376 #ifdef CONFIG_PM
377 static void sa1100_mtd_shutdown(struct platform_device *dev)
378 {
379         struct sa_info *info = platform_get_drvdata(dev);
380         if (info && info->mtd->suspend(info->mtd) == 0)
381                 info->mtd->resume(info->mtd);
382 }
383 #else
384 #define sa1100_mtd_shutdown NULL
385 #endif
386
387 static struct platform_driver sa1100_mtd_driver = {
388         .probe          = sa1100_mtd_probe,
389         .remove         = __exit_p(sa1100_mtd_remove),
390         .shutdown       = sa1100_mtd_shutdown,
391         .driver         = {
392                 .name   = "sa1100-mtd",
393                 .owner  = THIS_MODULE,
394         },
395 };
396
397 static int __init sa1100_mtd_init(void)
398 {
399         return platform_driver_register(&sa1100_mtd_driver);
400 }
401
402 static void __exit sa1100_mtd_exit(void)
403 {
404         platform_driver_unregister(&sa1100_mtd_driver);
405 }
406
407 module_init(sa1100_mtd_init);
408 module_exit(sa1100_mtd_exit);
409
410 MODULE_AUTHOR("Nicolas Pitre");
411 MODULE_DESCRIPTION("SA1100 CFI map driver");
412 MODULE_LICENSE("GPL");
413 MODULE_ALIAS("platform:sa1100-mtd");