blob: 950bf1c578417f0d2ba3d5b035fd0adb8e5b99ac [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 *
Linus Torvalds1da177e2005-04-16 15:20:36 -07004 * (C) 2000 Nicolas Pitre <nico@cam.org>
Thomas Gleixner69f34c92005-11-07 11:15:40 +00005 *
6 * $Id: sa1100-flash.c,v 1.51 2005/11/07 11:14:28 gleixner Exp $
Linus Torvalds1da177e2005-04-16 15:20:36 -07007 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07008#include <linux/module.h>
9#include <linux/types.h>
10#include <linux/ioport.h>
11#include <linux/kernel.h>
12#include <linux/init.h>
13#include <linux/errno.h>
14#include <linux/slab.h>
Russell Kingd052d1b2005-10-29 19:07:23 +010015#include <linux/platform_device.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070016#include <linux/err.h>
17
18#include <linux/mtd/mtd.h>
19#include <linux/mtd/map.h>
20#include <linux/mtd/partitions.h>
21#include <linux/mtd/concat.h>
22
Russell King674c0452005-10-28 14:25:28 +010023#include <asm/hardware.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070024#include <asm/io.h>
25#include <asm/sizes.h>
26#include <asm/mach/flash.h>
27
28#if 0
29/*
30 * This is here for documentation purposes only - until these people
31 * submit their machine types. It will be gone January 2005.
32 */
33static struct mtd_partition consus_partitions[] = {
34 {
35 .name = "Consus boot firmware",
36 .offset = 0,
37 .size = 0x00040000,
38 .mask_flags = MTD_WRITABLE, /* force read-only */
39 }, {
40 .name = "Consus kernel",
41 .offset = 0x00040000,
42 .size = 0x00100000,
43 .mask_flags = 0,
44 }, {
45 .name = "Consus disk",
46 .offset = 0x00140000,
47 /* The rest (up to 16M) for jffs. We could put 0 and
48 make it find the size automatically, but right now
49 i have 32 megs. jffs will use all 32 megs if given
50 the chance, and this leads to horrible problems
51 when you try to re-flash the image because blob
52 won't erase the whole partition. */
53 .size = 0x01000000 - 0x00140000,
54 .mask_flags = 0,
55 }, {
56 /* this disk is a secondary disk, which can be used as
57 needed, for simplicity, make it the size of the other
58 consus partition, although realistically it could be
59 the remainder of the disk (depending on the file
60 system used) */
61 .name = "Consus disk2",
62 .offset = 0x01000000,
63 .size = 0x01000000 - 0x00140000,
64 .mask_flags = 0,
65 }
66};
67
68/* Frodo has 2 x 16M 28F128J3A flash chips in bank 0: */
69static struct mtd_partition frodo_partitions[] =
70{
71 {
72 .name = "bootloader",
73 .size = 0x00040000,
74 .offset = 0x00000000,
75 .mask_flags = MTD_WRITEABLE
76 }, {
77 .name = "bootloader params",
78 .size = 0x00040000,
79 .offset = MTDPART_OFS_APPEND,
80 .mask_flags = MTD_WRITEABLE
81 }, {
82 .name = "kernel",
83 .size = 0x00100000,
84 .offset = MTDPART_OFS_APPEND,
85 .mask_flags = MTD_WRITEABLE
86 }, {
87 .name = "ramdisk",
88 .size = 0x00400000,
89 .offset = MTDPART_OFS_APPEND,
90 .mask_flags = MTD_WRITEABLE
91 }, {
92 .name = "file system",
93 .size = MTDPART_SIZ_FULL,
94 .offset = MTDPART_OFS_APPEND
95 }
96};
97
98static struct mtd_partition jornada56x_partitions[] = {
99 {
100 .name = "bootldr",
101 .size = 0x00040000,
102 .offset = 0,
103 .mask_flags = MTD_WRITEABLE,
104 }, {
105 .name = "rootfs",
106 .size = MTDPART_SIZ_FULL,
107 .offset = MTDPART_OFS_APPEND,
108 }
109};
110
111static void jornada56x_set_vpp(int vpp)
112{
113 if (vpp)
114 GPSR = GPIO_GPIO26;
115 else
116 GPCR = GPIO_GPIO26;
117 GPDR |= GPIO_GPIO26;
118}
119
120/*
121 * Machine Phys Size set_vpp
122 * Consus : SA1100_CS0_PHYS SZ_32M
123 * Frodo : SA1100_CS0_PHYS SZ_32M
124 * Jornada56x: SA1100_CS0_PHYS SZ_32M jornada56x_set_vpp
125 */
126#endif
127
128struct sa_subdev_info {
129 char name[16];
130 struct map_info map;
131 struct mtd_info *mtd;
Russell King57725f02005-10-29 15:51:14 +0100132 struct flash_platform_data *plat;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700133};
134
135struct sa_info {
136 struct mtd_partition *parts;
137 struct mtd_info *mtd;
138 int num_subdev;
Russell King822e5e72005-10-29 16:03:24 +0100139 unsigned int nr_parts;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700140 struct sa_subdev_info subdev[0];
141};
142
143static 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);
Russell King57725f02005-10-29 15:51:14 +0100146 subdev->plat->set_vpp(on);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700147}
148
149static 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
158static 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
Russell King57725f02005-10-29 15:51:14 +0100190 if (subdev->plat->set_vpp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700191 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 */
Russell King57725f02005-10-29 15:51:14 +0100207 subdev->mtd = do_map_probe(subdev->plat->map_name, &subdev->map);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700208 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
Russell King0d2ef7d2005-10-29 15:57:20 +0100226static void sa1100_destroy(struct sa_info *info, struct flash_platform_data *plat)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700227{
228 int i;
229
230 if (info->mtd) {
Russell King822e5e72005-10-29 16:03:24 +0100231 if (info->nr_parts == 0)
232 del_mtd_device(info->mtd);
233#ifdef CONFIG_MTD_PARTITIONS
234 else
235 del_mtd_partitions(info->mtd);
236#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700237#ifdef CONFIG_MTD_CONCAT
238 if (info->mtd != info->subdev[0].mtd)
239 mtd_concat_destroy(info->mtd);
240#endif
241 }
242
Jesper Juhlfa671642005-11-07 01:01:27 -0800243 kfree(info->parts);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700244
245 for (i = info->num_subdev - 1; i >= 0; i--)
246 sa1100_destroy_subdev(&info->subdev[i]);
247 kfree(info);
Russell King0d2ef7d2005-10-29 15:57:20 +0100248
249 if (plat->exit)
250 plat->exit();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700251}
252
253static struct sa_info *__init
Russell King57725f02005-10-29 15:51:14 +0100254sa1100_setup_mtd(struct platform_device *pdev, struct flash_platform_data *plat)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700255{
256 struct sa_info *info;
257 int nr, size, i, ret = 0;
258
259 /*
260 * Count number of devices.
261 */
262 for (nr = 0; ; nr++)
263 if (!platform_get_resource(pdev, IORESOURCE_MEM, nr))
264 break;
265
266 if (nr == 0) {
267 ret = -ENODEV;
268 goto out;
269 }
270
271 size = sizeof(struct sa_info) + sizeof(struct sa_subdev_info) * nr;
272
273 /*
274 * Allocate the map_info structs in one go.
275 */
276 info = kmalloc(size, GFP_KERNEL);
277 if (!info) {
278 ret = -ENOMEM;
279 goto out;
280 }
281
282 memset(info, 0, size);
283
Russell King0d2ef7d2005-10-29 15:57:20 +0100284 if (plat->init) {
285 ret = plat->init();
286 if (ret)
287 goto err;
288 }
289
Linus Torvalds1da177e2005-04-16 15:20:36 -0700290 /*
291 * Claim and then map the memory regions.
292 */
293 for (i = 0; i < nr; i++) {
294 struct sa_subdev_info *subdev = &info->subdev[i];
295 struct resource *res;
296
297 res = platform_get_resource(pdev, IORESOURCE_MEM, i);
298 if (!res)
299 break;
300
301 subdev->map.name = subdev->name;
Russell King14e66f72005-10-29 16:08:31 +0100302 sprintf(subdev->name, "%s-%d", plat->name, i);
Russell King57725f02005-10-29 15:51:14 +0100303 subdev->plat = plat;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700304
305 ret = sa1100_probe_subdev(subdev, res);
306 if (ret)
307 break;
308 }
309
310 info->num_subdev = i;
311
312 /*
313 * ENXIO is special. It means we didn't find a chip when we probed.
314 */
315 if (ret != 0 && !(ret == -ENXIO && info->num_subdev > 0))
316 goto err;
317
318 /*
319 * If we found one device, don't bother with concat support. If
320 * we found multiple devices, use concat if we have it available,
321 * otherwise fail. Either way, it'll be called "sa1100".
322 */
323 if (info->num_subdev == 1) {
Russell King14e66f72005-10-29 16:08:31 +0100324 strcpy(info->subdev[0].name, plat->name);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700325 info->mtd = info->subdev[0].mtd;
326 ret = 0;
327 } else if (info->num_subdev > 1) {
328#ifdef CONFIG_MTD_CONCAT
329 struct mtd_info *cdev[nr];
330 /*
331 * We detected multiple devices. Concatenate them together.
332 */
333 for (i = 0; i < info->num_subdev; i++)
334 cdev[i] = info->subdev[i].mtd;
335
336 info->mtd = mtd_concat_create(cdev, info->num_subdev,
Russell King14e66f72005-10-29 16:08:31 +0100337 plat->name);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700338 if (info->mtd == NULL)
339 ret = -ENXIO;
340#else
341 printk(KERN_ERR "SA1100 flash: multiple devices "
342 "found but MTD concat support disabled.\n");
343 ret = -ENXIO;
344#endif
345 }
346
347 if (ret == 0)
348 return info;
349
350 err:
Russell King0d2ef7d2005-10-29 15:57:20 +0100351 sa1100_destroy(info, plat);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700352 out:
353 return ERR_PTR(ret);
354}
355
356static const char *part_probes[] = { "cmdlinepart", "RedBoot", NULL };
357
Russell King3ae5eae2005-11-09 22:32:44 +0000358static int __init sa1100_mtd_probe(struct platform_device *pdev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700359{
Russell King57725f02005-10-29 15:51:14 +0100360 struct flash_platform_data *plat = pdev->dev.platform_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700361 struct mtd_partition *parts;
362 const char *part_type = NULL;
363 struct sa_info *info;
364 int err, nr_parts = 0;
365
Russell King57725f02005-10-29 15:51:14 +0100366 if (!plat)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700367 return -ENODEV;
368
Russell King57725f02005-10-29 15:51:14 +0100369 info = sa1100_setup_mtd(pdev, plat);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700370 if (IS_ERR(info)) {
371 err = PTR_ERR(info);
372 goto out;
373 }
374
375 /*
376 * Partition selection stuff.
377 */
378#ifdef CONFIG_MTD_PARTITIONS
379 nr_parts = parse_mtd_partitions(info->mtd, part_probes, &parts, 0);
380 if (nr_parts > 0) {
381 info->parts = parts;
382 part_type = "dynamic";
383 } else
384#endif
385 {
Russell King57725f02005-10-29 15:51:14 +0100386 parts = plat->parts;
387 nr_parts = plat->nr_parts;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700388 part_type = "static";
389 }
390
391 if (nr_parts == 0) {
392 printk(KERN_NOTICE "SA1100 flash: no partition info "
393 "available, registering whole flash\n");
394 add_mtd_device(info->mtd);
395 } else {
396 printk(KERN_NOTICE "SA1100 flash: using %s partition "
397 "definition\n", part_type);
398 add_mtd_partitions(info->mtd, parts, nr_parts);
399 }
400
Russell King822e5e72005-10-29 16:03:24 +0100401 info->nr_parts = nr_parts;
402
Russell King3ae5eae2005-11-09 22:32:44 +0000403 platform_set_drvdata(pdev, info);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700404 err = 0;
405
406 out:
407 return err;
408}
409
Russell King3ae5eae2005-11-09 22:32:44 +0000410static int __exit sa1100_mtd_remove(struct platform_device *pdev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700411{
Russell King3ae5eae2005-11-09 22:32:44 +0000412 struct sa_info *info = platform_get_drvdata(pdev);
413 struct flash_platform_data *plat = pdev->dev.platform_data;
Russell King0d2ef7d2005-10-29 15:57:20 +0100414
Russell King3ae5eae2005-11-09 22:32:44 +0000415 platform_set_drvdata(pdev, NULL);
Russell King0d2ef7d2005-10-29 15:57:20 +0100416 sa1100_destroy(info, plat);
417
Linus Torvalds1da177e2005-04-16 15:20:36 -0700418 return 0;
419}
420
421#ifdef CONFIG_PM
Russell King3ae5eae2005-11-09 22:32:44 +0000422static int sa1100_mtd_suspend(struct platform_device *dev, pm_message_t state)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700423{
Russell King3ae5eae2005-11-09 22:32:44 +0000424 struct sa_info *info = platform_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700425 int ret = 0;
426
Russell King9480e302005-10-28 09:52:56 -0700427 if (info)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700428 ret = info->mtd->suspend(info->mtd);
429
430 return ret;
431}
432
Russell King3ae5eae2005-11-09 22:32:44 +0000433static int sa1100_mtd_resume(struct platform_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700434{
Russell King3ae5eae2005-11-09 22:32:44 +0000435 struct sa_info *info = platform_get_drvdata(dev);
Russell King9480e302005-10-28 09:52:56 -0700436 if (info)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700437 info->mtd->resume(info->mtd);
438 return 0;
439}
Russell King13bfb342005-10-29 16:14:08 +0100440
Russell King3ae5eae2005-11-09 22:32:44 +0000441static void sa1100_mtd_shutdown(struct platform_device *dev)
Russell King13bfb342005-10-29 16:14:08 +0100442{
Russell King3ae5eae2005-11-09 22:32:44 +0000443 struct sa_info *info = platform_get_drvdata(dev);
Russell King13bfb342005-10-29 16:14:08 +0100444 if (info && info->mtd->suspend(info->mtd) == 0)
445 info->mtd->resume(info->mtd);
446}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700447#else
448#define sa1100_mtd_suspend NULL
449#define sa1100_mtd_resume NULL
Russell King13bfb342005-10-29 16:14:08 +0100450#define sa1100_mtd_shutdown NULL
Linus Torvalds1da177e2005-04-16 15:20:36 -0700451#endif
452
Russell King3ae5eae2005-11-09 22:32:44 +0000453static struct platform_driver sa1100_mtd_driver = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700454 .probe = sa1100_mtd_probe,
455 .remove = __exit_p(sa1100_mtd_remove),
456 .suspend = sa1100_mtd_suspend,
457 .resume = sa1100_mtd_resume,
Russell King13bfb342005-10-29 16:14:08 +0100458 .shutdown = sa1100_mtd_shutdown,
Russell King3ae5eae2005-11-09 22:32:44 +0000459 .driver = {
460 .name = "flash",
461 },
Linus Torvalds1da177e2005-04-16 15:20:36 -0700462};
463
464static int __init sa1100_mtd_init(void)
465{
Russell King3ae5eae2005-11-09 22:32:44 +0000466 return platform_driver_register(&sa1100_mtd_driver);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700467}
468
469static void __exit sa1100_mtd_exit(void)
470{
Russell King3ae5eae2005-11-09 22:32:44 +0000471 platform_driver_unregister(&sa1100_mtd_driver);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700472}
473
474module_init(sa1100_mtd_init);
475module_exit(sa1100_mtd_exit);
476
477MODULE_AUTHOR("Nicolas Pitre");
478MODULE_DESCRIPTION("SA1100 CFI map driver");
479MODULE_LICENSE("GPL");