blob: acf01ef9b5751242e389561642260d1ddb0c3c00 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
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
Russell King674c0452005-10-28 14:25:28 +010024#include <asm/hardware.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070025#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 */
34static 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: */
70static 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
99static 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
112static 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
129struct sa_subdev_info {
130 char name[16];
131 struct map_info map;
132 struct mtd_info *mtd;
Russell King57725f02005-10-29 15:51:14 +0100133 struct flash_platform_data *plat;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700134};
135
136struct sa_info {
137 struct mtd_partition *parts;
138 struct mtd_info *mtd;
139 int num_subdev;
Russell King822e5e72005-10-29 16:03:24 +0100140 unsigned int nr_parts;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700141 struct sa_subdev_info subdev[0];
142};
143
144static void sa1100_set_vpp(struct map_info *map, int on)
145{
146 struct sa_subdev_info *subdev = container_of(map, struct sa_subdev_info, map);
Russell King57725f02005-10-29 15:51:14 +0100147 subdev->plat->set_vpp(on);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700148}
149
150static void sa1100_destroy_subdev(struct sa_subdev_info *subdev)
151{
152 if (subdev->mtd)
153 map_destroy(subdev->mtd);
154 if (subdev->map.virt)
155 iounmap(subdev->map.virt);
156 release_mem_region(subdev->map.phys, subdev->map.size);
157}
158
159static int sa1100_probe_subdev(struct sa_subdev_info *subdev, struct resource *res)
160{
161 unsigned long phys;
162 unsigned int size;
163 int ret;
164
165 phys = res->start;
166 size = res->end - phys + 1;
167
168 /*
169 * Retrieve the bankwidth from the MSC registers.
170 * We currently only implement CS0 and CS1 here.
171 */
172 switch (phys) {
173 default:
174 printk(KERN_WARNING "SA1100 flash: unknown base address "
175 "0x%08lx, assuming CS0\n", phys);
176
177 case SA1100_CS0_PHYS:
178 subdev->map.bankwidth = (MSC0 & MSC_RBW) ? 2 : 4;
179 break;
180
181 case SA1100_CS1_PHYS:
182 subdev->map.bankwidth = ((MSC0 >> 16) & MSC_RBW) ? 2 : 4;
183 break;
184 }
185
186 if (!request_mem_region(phys, size, subdev->name)) {
187 ret = -EBUSY;
188 goto out;
189 }
190
Russell King57725f02005-10-29 15:51:14 +0100191 if (subdev->plat->set_vpp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700192 subdev->map.set_vpp = sa1100_set_vpp;
193
194 subdev->map.phys = phys;
195 subdev->map.size = size;
196 subdev->map.virt = ioremap(phys, size);
197 if (!subdev->map.virt) {
198 ret = -ENOMEM;
199 goto err;
200 }
201
202 simple_map_init(&subdev->map);
203
204 /*
205 * Now let's probe for the actual flash. Do it here since
206 * specific machine settings might have been set above.
207 */
Russell King57725f02005-10-29 15:51:14 +0100208 subdev->mtd = do_map_probe(subdev->plat->map_name, &subdev->map);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700209 if (subdev->mtd == NULL) {
210 ret = -ENXIO;
211 goto err;
212 }
213 subdev->mtd->owner = THIS_MODULE;
214
215 printk(KERN_INFO "SA1100 flash: CFI device at 0x%08lx, %dMiB, "
216 "%d-bit\n", phys, subdev->mtd->size >> 20,
217 subdev->map.bankwidth * 8);
218
219 return 0;
220
221 err:
222 sa1100_destroy_subdev(subdev);
223 out:
224 return ret;
225}
226
Russell King0d2ef7d2005-10-29 15:57:20 +0100227static void sa1100_destroy(struct sa_info *info, struct flash_platform_data *plat)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700228{
229 int i;
230
231 if (info->mtd) {
Russell King822e5e72005-10-29 16:03:24 +0100232 if (info->nr_parts == 0)
233 del_mtd_device(info->mtd);
234#ifdef CONFIG_MTD_PARTITIONS
235 else
236 del_mtd_partitions(info->mtd);
237#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700238#ifdef CONFIG_MTD_CONCAT
239 if (info->mtd != info->subdev[0].mtd)
240 mtd_concat_destroy(info->mtd);
241#endif
242 }
243
244 if (info->parts)
245 kfree(info->parts);
246
247 for (i = info->num_subdev - 1; i >= 0; i--)
248 sa1100_destroy_subdev(&info->subdev[i]);
249 kfree(info);
Russell King0d2ef7d2005-10-29 15:57:20 +0100250
251 if (plat->exit)
252 plat->exit();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700253}
254
255static struct sa_info *__init
Russell King57725f02005-10-29 15:51:14 +0100256sa1100_setup_mtd(struct platform_device *pdev, struct flash_platform_data *plat)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700257{
258 struct sa_info *info;
259 int nr, size, i, ret = 0;
260
261 /*
262 * Count number of devices.
263 */
264 for (nr = 0; ; nr++)
265 if (!platform_get_resource(pdev, IORESOURCE_MEM, nr))
266 break;
267
268 if (nr == 0) {
269 ret = -ENODEV;
270 goto out;
271 }
272
273 size = sizeof(struct sa_info) + sizeof(struct sa_subdev_info) * nr;
274
275 /*
276 * Allocate the map_info structs in one go.
277 */
278 info = kmalloc(size, GFP_KERNEL);
279 if (!info) {
280 ret = -ENOMEM;
281 goto out;
282 }
283
284 memset(info, 0, size);
285
Russell King0d2ef7d2005-10-29 15:57:20 +0100286 if (plat->init) {
287 ret = plat->init();
288 if (ret)
289 goto err;
290 }
291
Linus Torvalds1da177e2005-04-16 15:20:36 -0700292 /*
293 * Claim and then map the memory regions.
294 */
295 for (i = 0; i < nr; i++) {
296 struct sa_subdev_info *subdev = &info->subdev[i];
297 struct resource *res;
298
299 res = platform_get_resource(pdev, IORESOURCE_MEM, i);
300 if (!res)
301 break;
302
303 subdev->map.name = subdev->name;
Russell King14e66f72005-10-29 16:08:31 +0100304 sprintf(subdev->name, "%s-%d", plat->name, i);
Russell King57725f02005-10-29 15:51:14 +0100305 subdev->plat = plat;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700306
307 ret = sa1100_probe_subdev(subdev, res);
308 if (ret)
309 break;
310 }
311
312 info->num_subdev = i;
313
314 /*
315 * ENXIO is special. It means we didn't find a chip when we probed.
316 */
317 if (ret != 0 && !(ret == -ENXIO && info->num_subdev > 0))
318 goto err;
319
320 /*
321 * If we found one device, don't bother with concat support. If
322 * we found multiple devices, use concat if we have it available,
323 * otherwise fail. Either way, it'll be called "sa1100".
324 */
325 if (info->num_subdev == 1) {
Russell King14e66f72005-10-29 16:08:31 +0100326 strcpy(info->subdev[0].name, plat->name);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700327 info->mtd = info->subdev[0].mtd;
328 ret = 0;
329 } else if (info->num_subdev > 1) {
330#ifdef CONFIG_MTD_CONCAT
331 struct mtd_info *cdev[nr];
332 /*
333 * We detected multiple devices. Concatenate them together.
334 */
335 for (i = 0; i < info->num_subdev; i++)
336 cdev[i] = info->subdev[i].mtd;
337
338 info->mtd = mtd_concat_create(cdev, info->num_subdev,
Russell King14e66f72005-10-29 16:08:31 +0100339 plat->name);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700340 if (info->mtd == NULL)
341 ret = -ENXIO;
342#else
343 printk(KERN_ERR "SA1100 flash: multiple devices "
344 "found but MTD concat support disabled.\n");
345 ret = -ENXIO;
346#endif
347 }
348
349 if (ret == 0)
350 return info;
351
352 err:
Russell King0d2ef7d2005-10-29 15:57:20 +0100353 sa1100_destroy(info, plat);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700354 out:
355 return ERR_PTR(ret);
356}
357
358static const char *part_probes[] = { "cmdlinepart", "RedBoot", NULL };
359
360static int __init sa1100_mtd_probe(struct device *dev)
361{
362 struct platform_device *pdev = to_platform_device(dev);
Russell King57725f02005-10-29 15:51:14 +0100363 struct flash_platform_data *plat = pdev->dev.platform_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700364 struct mtd_partition *parts;
365 const char *part_type = NULL;
366 struct sa_info *info;
367 int err, nr_parts = 0;
368
Russell King57725f02005-10-29 15:51:14 +0100369 if (!plat)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700370 return -ENODEV;
371
Russell King57725f02005-10-29 15:51:14 +0100372 info = sa1100_setup_mtd(pdev, plat);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700373 if (IS_ERR(info)) {
374 err = PTR_ERR(info);
375 goto out;
376 }
377
378 /*
379 * Partition selection stuff.
380 */
381#ifdef CONFIG_MTD_PARTITIONS
382 nr_parts = parse_mtd_partitions(info->mtd, part_probes, &parts, 0);
383 if (nr_parts > 0) {
384 info->parts = parts;
385 part_type = "dynamic";
386 } else
387#endif
388 {
Russell King57725f02005-10-29 15:51:14 +0100389 parts = plat->parts;
390 nr_parts = plat->nr_parts;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700391 part_type = "static";
392 }
393
394 if (nr_parts == 0) {
395 printk(KERN_NOTICE "SA1100 flash: no partition info "
396 "available, registering whole flash\n");
397 add_mtd_device(info->mtd);
398 } else {
399 printk(KERN_NOTICE "SA1100 flash: using %s partition "
400 "definition\n", part_type);
401 add_mtd_partitions(info->mtd, parts, nr_parts);
402 }
403
Russell King822e5e72005-10-29 16:03:24 +0100404 info->nr_parts = nr_parts;
405
Linus Torvalds1da177e2005-04-16 15:20:36 -0700406 dev_set_drvdata(dev, info);
407 err = 0;
408
409 out:
410 return err;
411}
412
413static int __exit sa1100_mtd_remove(struct device *dev)
414{
415 struct sa_info *info = dev_get_drvdata(dev);
Russell King0d2ef7d2005-10-29 15:57:20 +0100416 struct flash_platform_data *plat = dev->platform_data;
417
Linus Torvalds1da177e2005-04-16 15:20:36 -0700418 dev_set_drvdata(dev, NULL);
Russell King0d2ef7d2005-10-29 15:57:20 +0100419 sa1100_destroy(info, plat);
420
Linus Torvalds1da177e2005-04-16 15:20:36 -0700421 return 0;
422}
423
424#ifdef CONFIG_PM
Russell King9480e302005-10-28 09:52:56 -0700425static int sa1100_mtd_suspend(struct device *dev, pm_message_t state)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700426{
427 struct sa_info *info = dev_get_drvdata(dev);
428 int ret = 0;
429
Russell King9480e302005-10-28 09:52:56 -0700430 if (info)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700431 ret = info->mtd->suspend(info->mtd);
432
433 return ret;
434}
435
Russell King9480e302005-10-28 09:52:56 -0700436static int sa1100_mtd_resume(struct device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700437{
438 struct sa_info *info = dev_get_drvdata(dev);
Russell King9480e302005-10-28 09:52:56 -0700439 if (info)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700440 info->mtd->resume(info->mtd);
441 return 0;
442}
443#else
444#define sa1100_mtd_suspend NULL
445#define sa1100_mtd_resume NULL
446#endif
447
448static struct device_driver sa1100_mtd_driver = {
449 .name = "flash",
450 .bus = &platform_bus_type,
451 .probe = sa1100_mtd_probe,
452 .remove = __exit_p(sa1100_mtd_remove),
453 .suspend = sa1100_mtd_suspend,
454 .resume = sa1100_mtd_resume,
455};
456
457static int __init sa1100_mtd_init(void)
458{
459 return driver_register(&sa1100_mtd_driver);
460}
461
462static void __exit sa1100_mtd_exit(void)
463{
464 driver_unregister(&sa1100_mtd_driver);
465}
466
467module_init(sa1100_mtd_init);
468module_exit(sa1100_mtd_exit);
469
470MODULE_AUTHOR("Nicolas Pitre");
471MODULE_DESCRIPTION("SA1100 CFI map driver");
472MODULE_LICENSE("GPL");