blob: 50a1d434906a7154e6b2786c09cb679f87c756fb [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;
140 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) {
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);
Russell King0d2ef7d2005-10-29 15:57:20 +0100245
246 if (plat->exit)
247 plat->exit();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700248}
249
250static struct sa_info *__init
Russell King57725f02005-10-29 15:51:14 +0100251sa1100_setup_mtd(struct platform_device *pdev, struct flash_platform_data *plat)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700252{
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
Russell King0d2ef7d2005-10-29 15:57:20 +0100281 if (plat->init) {
282 ret = plat->init();
283 if (ret)
284 goto err;
285 }
286
Linus Torvalds1da177e2005-04-16 15:20:36 -0700287 /*
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);
Russell King57725f02005-10-29 15:51:14 +0100300 subdev->plat = plat;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700301
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:
Russell King0d2ef7d2005-10-29 15:57:20 +0100348 sa1100_destroy(info, plat);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700349 out:
350 return ERR_PTR(ret);
351}
352
353static const char *part_probes[] = { "cmdlinepart", "RedBoot", NULL };
354
355static int __init sa1100_mtd_probe(struct device *dev)
356{
357 struct platform_device *pdev = to_platform_device(dev);
Russell King57725f02005-10-29 15:51:14 +0100358 struct flash_platform_data *plat = pdev->dev.platform_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700359 struct mtd_partition *parts;
360 const char *part_type = NULL;
361 struct sa_info *info;
362 int err, nr_parts = 0;
363
Russell King57725f02005-10-29 15:51:14 +0100364 if (!plat)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700365 return -ENODEV;
366
Russell King57725f02005-10-29 15:51:14 +0100367 info = sa1100_setup_mtd(pdev, plat);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700368 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 {
Russell King57725f02005-10-29 15:51:14 +0100384 parts = plat->parts;
385 nr_parts = plat->nr_parts;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700386 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
406static int __exit sa1100_mtd_remove(struct device *dev)
407{
408 struct sa_info *info = dev_get_drvdata(dev);
Russell King0d2ef7d2005-10-29 15:57:20 +0100409 struct flash_platform_data *plat = dev->platform_data;
410
Linus Torvalds1da177e2005-04-16 15:20:36 -0700411 dev_set_drvdata(dev, NULL);
Russell King0d2ef7d2005-10-29 15:57:20 +0100412 sa1100_destroy(info, plat);
413
Linus Torvalds1da177e2005-04-16 15:20:36 -0700414 return 0;
415}
416
417#ifdef CONFIG_PM
Russell King9480e302005-10-28 09:52:56 -0700418static int sa1100_mtd_suspend(struct device *dev, pm_message_t state)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700419{
420 struct sa_info *info = dev_get_drvdata(dev);
421 int ret = 0;
422
Russell King9480e302005-10-28 09:52:56 -0700423 if (info)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700424 ret = info->mtd->suspend(info->mtd);
425
426 return ret;
427}
428
Russell King9480e302005-10-28 09:52:56 -0700429static int sa1100_mtd_resume(struct device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700430{
431 struct sa_info *info = dev_get_drvdata(dev);
Russell King9480e302005-10-28 09:52:56 -0700432 if (info)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700433 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
441static 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
450static int __init sa1100_mtd_init(void)
451{
452 return driver_register(&sa1100_mtd_driver);
453}
454
455static void __exit sa1100_mtd_exit(void)
456{
457 driver_unregister(&sa1100_mtd_driver);
458}
459
460module_init(sa1100_mtd_init);
461module_exit(sa1100_mtd_exit);
462
463MODULE_AUTHOR("Nicolas Pitre");
464MODULE_DESCRIPTION("SA1100 CFI map driver");
465MODULE_LICENSE("GPL");