blob: 20944f054867291776143f3a6a37965c771799b8 [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 *
Nicolas Pitre2f82af02009-09-14 03:25:28 -04004 * (C) 2000 Nicolas Pitre <nico@fluxnic.net>
Linus Torvalds1da177e2005-04-16 15:20:36 -07005 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07006#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>
Russell Kingd052d1b2005-10-29 19:07:23 +010013#include <linux/platform_device.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070014#include <linux/err.h>
Russell King99730222009-03-25 10:21:35 +000015#include <linux/io.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070016
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
Russell Kinga09e64f2008-08-05 16:14:15 +010022#include <mach/hardware.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070023#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 */
31static 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: */
67static 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
96static 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
109static 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
126struct sa_subdev_info {
127 char name[16];
128 struct map_info map;
129 struct mtd_info *mtd;
Russell King57725f02005-10-29 15:51:14 +0100130 struct flash_platform_data *plat;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700131};
132
133struct sa_info {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700134 struct mtd_info *mtd;
135 int num_subdev;
136 struct sa_subdev_info subdev[0];
137};
138
139static 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);
Russell King57725f02005-10-29 15:51:14 +0100142 subdev->plat->set_vpp(on);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700143}
144
145static 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
154static 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
Russell King57725f02005-10-29 15:51:14 +0100186 if (subdev->plat->set_vpp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700187 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 */
Russell King57725f02005-10-29 15:51:14 +0100203 subdev->mtd = do_map_probe(subdev->plat->map_name, &subdev->map);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700204 if (subdev->mtd == NULL) {
205 ret = -ENXIO;
206 goto err;
207 }
208 subdev->mtd->owner = THIS_MODULE;
209
Russell King794d5792009-09-27 23:51:04 +0100210 printk(KERN_INFO "SA1100 flash: CFI device at 0x%08lx, %uMiB, %d-bit\n",
211 phys, (unsigned)(subdev->mtd->size >> 20),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700212 subdev->map.bankwidth * 8);
213
214 return 0;
215
216 err:
217 sa1100_destroy_subdev(subdev);
218 out:
219 return ret;
220}
221
Russell King0d2ef7d2005-10-29 15:57:20 +0100222static void sa1100_destroy(struct sa_info *info, struct flash_platform_data *plat)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700223{
224 int i;
225
226 if (info->mtd) {
Jamie Iles2fe2e242011-05-23 10:23:09 +0100227 mtd_device_unregister(info->mtd);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700228 if (info->mtd != info->subdev[0].mtd)
229 mtd_concat_destroy(info->mtd);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700230 }
231
Linus Torvalds1da177e2005-04-16 15:20:36 -0700232 for (i = info->num_subdev - 1; i >= 0; i--)
233 sa1100_destroy_subdev(&info->subdev[i]);
234 kfree(info);
Russell King0d2ef7d2005-10-29 15:57:20 +0100235
236 if (plat->exit)
237 plat->exit();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700238}
239
Dmitry Artamonow5a134232009-10-09 12:18:49 +0400240static struct sa_info *__devinit
Russell King57725f02005-10-29 15:51:14 +0100241sa1100_setup_mtd(struct platform_device *pdev, struct flash_platform_data *plat)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700242{
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 */
Burman Yan95b93a02006-11-15 21:10:29 +0200263 info = kzalloc(size, GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700264 if (!info) {
265 ret = -ENOMEM;
266 goto out;
267 }
268
Russell King0d2ef7d2005-10-29 15:57:20 +0100269 if (plat->init) {
270 ret = plat->init();
271 if (ret)
272 goto err;
273 }
274
Linus Torvalds1da177e2005-04-16 15:20:36 -0700275 /*
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;
Russell King14e66f72005-10-29 16:08:31 +0100287 sprintf(subdev->name, "%s-%d", plat->name, i);
Russell King57725f02005-10-29 15:51:14 +0100288 subdev->plat = plat;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700289
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) {
Russell King14e66f72005-10-29 16:08:31 +0100309 strcpy(info->subdev[0].name, plat->name);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700310 info->mtd = info->subdev[0].mtd;
311 ret = 0;
312 } else if (info->num_subdev > 1) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700313 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,
Russell King14e66f72005-10-29 16:08:31 +0100321 plat->name);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700322 if (info->mtd == NULL)
323 ret = -ENXIO;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700324 }
325
326 if (ret == 0)
327 return info;
328
329 err:
Russell King0d2ef7d2005-10-29 15:57:20 +0100330 sa1100_destroy(info, plat);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700331 out:
332 return ERR_PTR(ret);
333}
334
335static const char *part_probes[] = { "cmdlinepart", "RedBoot", NULL };
336
Uwe Kleine-Königf0b1e582009-03-28 00:27:03 +0100337static int __devinit sa1100_mtd_probe(struct platform_device *pdev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700338{
Russell King57725f02005-10-29 15:51:14 +0100339 struct flash_platform_data *plat = pdev->dev.platform_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700340 struct sa_info *info;
Dmitry Eremin-Solenikov769dc432011-06-02 18:00:06 +0400341 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700342
Russell King57725f02005-10-29 15:51:14 +0100343 if (!plat)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700344 return -ENODEV;
345
Russell King57725f02005-10-29 15:51:14 +0100346 info = sa1100_setup_mtd(pdev, plat);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700347 if (IS_ERR(info)) {
348 err = PTR_ERR(info);
349 goto out;
350 }
351
352 /*
353 * Partition selection stuff.
354 */
Dmitry Eremin-Solenikov769dc432011-06-02 18:00:06 +0400355 mtd_device_parse_register(info->mtd, part_probes, 0,
356 plat->parts, plat->nr_parts);
Russell King822e5e72005-10-29 16:03:24 +0100357
Russell King3ae5eae2005-11-09 22:32:44 +0000358 platform_set_drvdata(pdev, info);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700359 err = 0;
360
361 out:
362 return err;
363}
364
Russell King3ae5eae2005-11-09 22:32:44 +0000365static int __exit sa1100_mtd_remove(struct platform_device *pdev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700366{
Russell King3ae5eae2005-11-09 22:32:44 +0000367 struct sa_info *info = platform_get_drvdata(pdev);
368 struct flash_platform_data *plat = pdev->dev.platform_data;
Russell King0d2ef7d2005-10-29 15:57:20 +0100369
Russell King3ae5eae2005-11-09 22:32:44 +0000370 platform_set_drvdata(pdev, NULL);
Russell King0d2ef7d2005-10-29 15:57:20 +0100371 sa1100_destroy(info, plat);
372
Linus Torvalds1da177e2005-04-16 15:20:36 -0700373 return 0;
374}
375
376#ifdef CONFIG_PM
Russell King3ae5eae2005-11-09 22:32:44 +0000377static void sa1100_mtd_shutdown(struct platform_device *dev)
Russell King13bfb342005-10-29 16:14:08 +0100378{
Russell King3ae5eae2005-11-09 22:32:44 +0000379 struct sa_info *info = platform_get_drvdata(dev);
Artem Bityutskiy3fe4bae2011-12-23 19:25:16 +0200380 if (info && mtd_suspend(info->mtd) == 0)
Russell King13bfb342005-10-29 16:14:08 +0100381 info->mtd->resume(info->mtd);
382}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700383#else
Russell King13bfb342005-10-29 16:14:08 +0100384#define sa1100_mtd_shutdown NULL
Linus Torvalds1da177e2005-04-16 15:20:36 -0700385#endif
386
Russell King3ae5eae2005-11-09 22:32:44 +0000387static struct platform_driver sa1100_mtd_driver = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700388 .probe = sa1100_mtd_probe,
389 .remove = __exit_p(sa1100_mtd_remove),
Russell King13bfb342005-10-29 16:14:08 +0100390 .shutdown = sa1100_mtd_shutdown,
Russell King3ae5eae2005-11-09 22:32:44 +0000391 .driver = {
Uwe Kleine-Königbcc8f3e2009-01-31 01:21:58 +0100392 .name = "sa1100-mtd",
Kay Sievers41d867c2008-04-18 13:44:26 -0700393 .owner = THIS_MODULE,
Russell King3ae5eae2005-11-09 22:32:44 +0000394 },
Linus Torvalds1da177e2005-04-16 15:20:36 -0700395};
396
Axel Linf99640d2011-11-27 20:45:03 +0800397module_platform_driver(sa1100_mtd_driver);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700398
399MODULE_AUTHOR("Nicolas Pitre");
400MODULE_DESCRIPTION("SA1100 CFI map driver");
401MODULE_LICENSE("GPL");
Uwe Kleine-Königbcc8f3e2009-01-31 01:21:58 +0100402MODULE_ALIAS("platform:sa1100-mtd");