xref: /openbmc/linux/drivers/mtd/maps/sa1100-flash.c (revision 171f1bc7)
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");
414