PageRenderTime 54ms CodeModel.GetById 31ms app.highlight 19ms RepoModel.GetById 0ms app.codeStats 1ms

/drivers/mtd/maps/sa1100-flash.c

https://bitbucket.org/slukk/jb-tsm-kernel-4.2
C | 437 lines | 315 code | 64 blank | 58 comment | 47 complexity | 304824efbadf5c9558e4e63d93407262 MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.0, AGPL-1.0
  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 */
 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;
130	struct flash_platform_data *plat;
131};
132
133struct sa_info {
134	struct mtd_partition	*parts;
135	struct mtd_info		*mtd;
136	int			num_subdev;
137	unsigned int		nr_parts;
138	struct sa_subdev_info	subdev[0];
139};
140
141static void sa1100_set_vpp(struct map_info *map, int on)
142{
143	struct sa_subdev_info *subdev = container_of(map, struct sa_subdev_info, map);
144	subdev->plat->set_vpp(on);
145}
146
147static void sa1100_destroy_subdev(struct sa_subdev_info *subdev)
148{
149	if (subdev->mtd)
150		map_destroy(subdev->mtd);
151	if (subdev->map.virt)
152		iounmap(subdev->map.virt);
153	release_mem_region(subdev->map.phys, subdev->map.size);
154}
155
156static int sa1100_probe_subdev(struct sa_subdev_info *subdev, struct resource *res)
157{
158	unsigned long phys;
159	unsigned int size;
160	int ret;
161
162	phys = res->start;
163	size = res->end - phys + 1;
164
165	/*
166	 * Retrieve the bankwidth from the MSC registers.
167	 * We currently only implement CS0 and CS1 here.
168	 */
169	switch (phys) {
170	default:
171		printk(KERN_WARNING "SA1100 flash: unknown base address "
172		       "0x%08lx, assuming CS0\n", phys);
173
174	case SA1100_CS0_PHYS:
175		subdev->map.bankwidth = (MSC0 & MSC_RBW) ? 2 : 4;
176		break;
177
178	case SA1100_CS1_PHYS:
179		subdev->map.bankwidth = ((MSC0 >> 16) & MSC_RBW) ? 2 : 4;
180		break;
181	}
182
183	if (!request_mem_region(phys, size, subdev->name)) {
184		ret = -EBUSY;
185		goto out;
186	}
187
188	if (subdev->plat->set_vpp)
189		subdev->map.set_vpp = sa1100_set_vpp;
190
191	subdev->map.phys = phys;
192	subdev->map.size = size;
193	subdev->map.virt = ioremap(phys, size);
194	if (!subdev->map.virt) {
195		ret = -ENOMEM;
196		goto err;
197	}
198
199	simple_map_init(&subdev->map);
200
201	/*
202	 * Now let's probe for the actual flash.  Do it here since
203	 * specific machine settings might have been set above.
204	 */
205	subdev->mtd = do_map_probe(subdev->plat->map_name, &subdev->map);
206	if (subdev->mtd == NULL) {
207		ret = -ENXIO;
208		goto err;
209	}
210	subdev->mtd->owner = THIS_MODULE;
211
212	printk(KERN_INFO "SA1100 flash: CFI device at 0x%08lx, %uMiB, %d-bit\n",
213		phys, (unsigned)(subdev->mtd->size >> 20),
214		subdev->map.bankwidth * 8);
215
216	return 0;
217
218 err:
219	sa1100_destroy_subdev(subdev);
220 out:
221	return ret;
222}
223
224static void sa1100_destroy(struct sa_info *info, struct flash_platform_data *plat)
225{
226	int i;
227
228	if (info->mtd) {
229		mtd_device_unregister(info->mtd);
230		if (info->mtd != info->subdev[0].mtd)
231			mtd_concat_destroy(info->mtd);
232	}
233
234	kfree(info->parts);
235
236	for (i = info->num_subdev - 1; i >= 0; i--)
237		sa1100_destroy_subdev(&info->subdev[i]);
238	kfree(info);
239
240	if (plat->exit)
241		plat->exit();
242}
243
244static struct sa_info *__devinit
245sa1100_setup_mtd(struct platform_device *pdev, struct flash_platform_data *plat)
246{
247	struct sa_info *info;
248	int nr, size, i, ret = 0;
249
250	/*
251	 * Count number of devices.
252	 */
253	for (nr = 0; ; nr++)
254		if (!platform_get_resource(pdev, IORESOURCE_MEM, nr))
255			break;
256
257	if (nr == 0) {
258		ret = -ENODEV;
259		goto out;
260	}
261
262	size = sizeof(struct sa_info) + sizeof(struct sa_subdev_info) * nr;
263
264	/*
265	 * Allocate the map_info structs in one go.
266	 */
267	info = kzalloc(size, GFP_KERNEL);
268	if (!info) {
269		ret = -ENOMEM;
270		goto out;
271	}
272
273	if (plat->init) {
274		ret = plat->init();
275		if (ret)
276			goto err;
277	}
278
279	/*
280	 * Claim and then map the memory regions.
281	 */
282	for (i = 0; i < nr; i++) {
283		struct sa_subdev_info *subdev = &info->subdev[i];
284		struct resource *res;
285
286		res = platform_get_resource(pdev, IORESOURCE_MEM, i);
287		if (!res)
288			break;
289
290		subdev->map.name = subdev->name;
291		sprintf(subdev->name, "%s-%d", plat->name, i);
292		subdev->plat = plat;
293
294		ret = sa1100_probe_subdev(subdev, res);
295		if (ret)
296			break;
297	}
298
299	info->num_subdev = i;
300
301	/*
302	 * ENXIO is special.  It means we didn't find a chip when we probed.
303	 */
304	if (ret != 0 && !(ret == -ENXIO && info->num_subdev > 0))
305		goto err;
306
307	/*
308	 * If we found one device, don't bother with concat support.  If
309	 * we found multiple devices, use concat if we have it available,
310	 * otherwise fail.  Either way, it'll be called "sa1100".
311	 */
312	if (info->num_subdev == 1) {
313		strcpy(info->subdev[0].name, plat->name);
314		info->mtd = info->subdev[0].mtd;
315		ret = 0;
316	} else if (info->num_subdev > 1) {
317		struct mtd_info *cdev[nr];
318		/*
319		 * We detected multiple devices.  Concatenate them together.
320		 */
321		for (i = 0; i < info->num_subdev; i++)
322			cdev[i] = info->subdev[i].mtd;
323
324		info->mtd = mtd_concat_create(cdev, info->num_subdev,
325					      plat->name);
326		if (info->mtd == NULL)
327			ret = -ENXIO;
328	}
329
330	if (ret == 0)
331		return info;
332
333 err:
334	sa1100_destroy(info, plat);
335 out:
336	return ERR_PTR(ret);
337}
338
339static const char *part_probes[] = { "cmdlinepart", "RedBoot", NULL };
340
341static int __devinit sa1100_mtd_probe(struct platform_device *pdev)
342{
343	struct flash_platform_data *plat = pdev->dev.platform_data;
344	struct mtd_partition *parts;
345	const char *part_type = NULL;
346	struct sa_info *info;
347	int err, nr_parts = 0;
348
349	if (!plat)
350		return -ENODEV;
351
352	info = sa1100_setup_mtd(pdev, plat);
353	if (IS_ERR(info)) {
354		err = PTR_ERR(info);
355		goto out;
356	}
357
358	/*
359	 * Partition selection stuff.
360	 */
361	nr_parts = parse_mtd_partitions(info->mtd, part_probes, &parts, 0);
362	if (nr_parts > 0) {
363		info->parts = parts;
364		part_type = "dynamic";
365	} else {
366		parts = plat->parts;
367		nr_parts = plat->nr_parts;
368		part_type = "static";
369	}
370
371	if (nr_parts == 0)
372		printk(KERN_NOTICE "SA1100 flash: no partition info "
373			"available, registering whole flash\n");
374	else
375		printk(KERN_NOTICE "SA1100 flash: using %s partition "
376			"definition\n", part_type);
377
378	mtd_device_register(info->mtd, parts, nr_parts);
379
380	info->nr_parts = nr_parts;
381
382	platform_set_drvdata(pdev, info);
383	err = 0;
384
385 out:
386	return err;
387}
388
389static int __exit sa1100_mtd_remove(struct platform_device *pdev)
390{
391	struct sa_info *info = platform_get_drvdata(pdev);
392	struct flash_platform_data *plat = pdev->dev.platform_data;
393
394	platform_set_drvdata(pdev, NULL);
395	sa1100_destroy(info, plat);
396
397	return 0;
398}
399
400#ifdef CONFIG_PM
401static void sa1100_mtd_shutdown(struct platform_device *dev)
402{
403	struct sa_info *info = platform_get_drvdata(dev);
404	if (info && info->mtd->suspend(info->mtd) == 0)
405		info->mtd->resume(info->mtd);
406}
407#else
408#define sa1100_mtd_shutdown NULL
409#endif
410
411static struct platform_driver sa1100_mtd_driver = {
412	.probe		= sa1100_mtd_probe,
413	.remove		= __exit_p(sa1100_mtd_remove),
414	.shutdown	= sa1100_mtd_shutdown,
415	.driver		= {
416		.name	= "sa1100-mtd",
417		.owner	= THIS_MODULE,
418	},
419};
420
421static int __init sa1100_mtd_init(void)
422{
423	return platform_driver_register(&sa1100_mtd_driver);
424}
425
426static void __exit sa1100_mtd_exit(void)
427{
428	platform_driver_unregister(&sa1100_mtd_driver);
429}
430
431module_init(sa1100_mtd_init);
432module_exit(sa1100_mtd_exit);
433
434MODULE_AUTHOR("Nicolas Pitre");
435MODULE_DESCRIPTION("SA1100 CFI map driver");
436MODULE_LICENSE("GPL");
437MODULE_ALIAS("platform:sa1100-mtd");