xref: /linux/drivers/mtd/maps/sa1100-flash.c (revision f0b1e5892478d5bd683ff01bd95dc7bfc87f8e42)
11da177e4SLinus Torvalds /*
21da177e4SLinus Torvalds  * Flash memory access on SA11x0 based devices
31da177e4SLinus Torvalds  *
41da177e4SLinus Torvalds  * (C) 2000 Nicolas Pitre <nico@cam.org>
51da177e4SLinus Torvalds  */
61da177e4SLinus Torvalds #include <linux/module.h>
71da177e4SLinus Torvalds #include <linux/types.h>
81da177e4SLinus Torvalds #include <linux/ioport.h>
91da177e4SLinus Torvalds #include <linux/kernel.h>
101da177e4SLinus Torvalds #include <linux/init.h>
111da177e4SLinus Torvalds #include <linux/errno.h>
121da177e4SLinus Torvalds #include <linux/slab.h>
13d052d1beSRussell King #include <linux/platform_device.h>
141da177e4SLinus Torvalds #include <linux/err.h>
151da177e4SLinus Torvalds 
161da177e4SLinus Torvalds #include <linux/mtd/mtd.h>
171da177e4SLinus Torvalds #include <linux/mtd/map.h>
181da177e4SLinus Torvalds #include <linux/mtd/partitions.h>
191da177e4SLinus Torvalds #include <linux/mtd/concat.h>
201da177e4SLinus Torvalds 
21a09e64fbSRussell King #include <mach/hardware.h>
221da177e4SLinus Torvalds #include <asm/io.h>
231da177e4SLinus Torvalds #include <asm/sizes.h>
241da177e4SLinus Torvalds #include <asm/mach/flash.h>
251da177e4SLinus Torvalds 
261da177e4SLinus Torvalds #if 0
271da177e4SLinus Torvalds /*
281da177e4SLinus Torvalds  * This is here for documentation purposes only - until these people
291da177e4SLinus Torvalds  * submit their machine types.  It will be gone January 2005.
301da177e4SLinus Torvalds  */
311da177e4SLinus Torvalds static struct mtd_partition consus_partitions[] = {
321da177e4SLinus Torvalds 	{
331da177e4SLinus Torvalds 		.name		= "Consus boot firmware",
341da177e4SLinus Torvalds 		.offset		= 0,
351da177e4SLinus Torvalds 		.size		= 0x00040000,
361da177e4SLinus Torvalds 		.mask_flags	= MTD_WRITABLE, /* force read-only */
371da177e4SLinus Torvalds 	}, {
381da177e4SLinus Torvalds 		.name		= "Consus kernel",
391da177e4SLinus Torvalds 		.offset		= 0x00040000,
401da177e4SLinus Torvalds 		.size		= 0x00100000,
411da177e4SLinus Torvalds 		.mask_flags	= 0,
421da177e4SLinus Torvalds 	}, {
431da177e4SLinus Torvalds 		.name		= "Consus disk",
441da177e4SLinus Torvalds 		.offset		= 0x00140000,
451da177e4SLinus Torvalds 		/* The rest (up to 16M) for jffs.  We could put 0 and
461da177e4SLinus Torvalds 		   make it find the size automatically, but right now
471da177e4SLinus Torvalds 		   i have 32 megs.  jffs will use all 32 megs if given
481da177e4SLinus Torvalds 		   the chance, and this leads to horrible problems
491da177e4SLinus Torvalds 		   when you try to re-flash the image because blob
501da177e4SLinus Torvalds 		   won't erase the whole partition. */
511da177e4SLinus Torvalds 		.size		= 0x01000000 - 0x00140000,
521da177e4SLinus Torvalds 		.mask_flags	= 0,
531da177e4SLinus Torvalds 	}, {
541da177e4SLinus Torvalds 		/* this disk is a secondary disk, which can be used as
551da177e4SLinus Torvalds 		   needed, for simplicity, make it the size of the other
561da177e4SLinus Torvalds 		   consus partition, although realistically it could be
571da177e4SLinus Torvalds 		   the remainder of the disk (depending on the file
581da177e4SLinus Torvalds 		   system used) */
591da177e4SLinus Torvalds 		 .name		= "Consus disk2",
601da177e4SLinus Torvalds 		 .offset	= 0x01000000,
611da177e4SLinus Torvalds 		 .size		= 0x01000000 - 0x00140000,
621da177e4SLinus Torvalds 		 .mask_flags	= 0,
631da177e4SLinus Torvalds 	}
641da177e4SLinus Torvalds };
651da177e4SLinus Torvalds 
661da177e4SLinus Torvalds /* Frodo has 2 x 16M 28F128J3A flash chips in bank 0: */
671da177e4SLinus Torvalds static struct mtd_partition frodo_partitions[] =
681da177e4SLinus Torvalds {
691da177e4SLinus Torvalds 	{
701da177e4SLinus Torvalds 		.name		= "bootloader",
711da177e4SLinus Torvalds 		.size		= 0x00040000,
721da177e4SLinus Torvalds 		.offset		= 0x00000000,
731da177e4SLinus Torvalds 		.mask_flags	= MTD_WRITEABLE
741da177e4SLinus Torvalds 	}, {
751da177e4SLinus Torvalds 		.name		= "bootloader params",
761da177e4SLinus Torvalds 		.size		= 0x00040000,
771da177e4SLinus Torvalds 		.offset		= MTDPART_OFS_APPEND,
781da177e4SLinus Torvalds 		.mask_flags	= MTD_WRITEABLE
791da177e4SLinus Torvalds 	}, {
801da177e4SLinus Torvalds 		.name		= "kernel",
811da177e4SLinus Torvalds 		.size		= 0x00100000,
821da177e4SLinus Torvalds 		.offset		= MTDPART_OFS_APPEND,
831da177e4SLinus Torvalds 		.mask_flags	= MTD_WRITEABLE
841da177e4SLinus Torvalds 	}, {
851da177e4SLinus Torvalds 		.name		= "ramdisk",
861da177e4SLinus Torvalds 		.size		= 0x00400000,
871da177e4SLinus Torvalds 		.offset		= MTDPART_OFS_APPEND,
881da177e4SLinus Torvalds 		.mask_flags	= MTD_WRITEABLE
891da177e4SLinus Torvalds 	}, {
901da177e4SLinus Torvalds 		.name		= "file system",
911da177e4SLinus Torvalds 		.size		= MTDPART_SIZ_FULL,
921da177e4SLinus Torvalds 		.offset		= MTDPART_OFS_APPEND
931da177e4SLinus Torvalds 	}
941da177e4SLinus Torvalds };
951da177e4SLinus Torvalds 
961da177e4SLinus Torvalds static struct mtd_partition jornada56x_partitions[] = {
971da177e4SLinus Torvalds 	{
981da177e4SLinus Torvalds 		.name		= "bootldr",
991da177e4SLinus Torvalds 		.size		= 0x00040000,
1001da177e4SLinus Torvalds 		.offset		= 0,
1011da177e4SLinus Torvalds 		.mask_flags	= MTD_WRITEABLE,
1021da177e4SLinus Torvalds 	}, {
1031da177e4SLinus Torvalds 		.name		= "rootfs",
1041da177e4SLinus Torvalds 		.size		= MTDPART_SIZ_FULL,
1051da177e4SLinus Torvalds 		.offset		= MTDPART_OFS_APPEND,
1061da177e4SLinus Torvalds 	}
1071da177e4SLinus Torvalds };
1081da177e4SLinus Torvalds 
1091da177e4SLinus Torvalds static void jornada56x_set_vpp(int vpp)
1101da177e4SLinus Torvalds {
1111da177e4SLinus Torvalds 	if (vpp)
1121da177e4SLinus Torvalds 		GPSR = GPIO_GPIO26;
1131da177e4SLinus Torvalds 	else
1141da177e4SLinus Torvalds 		GPCR = GPIO_GPIO26;
1151da177e4SLinus Torvalds 	GPDR |= GPIO_GPIO26;
1161da177e4SLinus Torvalds }
1171da177e4SLinus Torvalds 
1181da177e4SLinus Torvalds /*
1191da177e4SLinus Torvalds  * Machine        Phys          Size    set_vpp
1201da177e4SLinus Torvalds  * Consus    : SA1100_CS0_PHYS SZ_32M
1211da177e4SLinus Torvalds  * Frodo     : SA1100_CS0_PHYS SZ_32M
1221da177e4SLinus Torvalds  * Jornada56x: SA1100_CS0_PHYS SZ_32M jornada56x_set_vpp
1231da177e4SLinus Torvalds  */
1241da177e4SLinus Torvalds #endif
1251da177e4SLinus Torvalds 
1261da177e4SLinus Torvalds struct sa_subdev_info {
1271da177e4SLinus Torvalds 	char name[16];
1281da177e4SLinus Torvalds 	struct map_info map;
1291da177e4SLinus Torvalds 	struct mtd_info *mtd;
13057725f0aSRussell King 	struct flash_platform_data *plat;
1311da177e4SLinus Torvalds };
1321da177e4SLinus Torvalds 
1331da177e4SLinus Torvalds struct sa_info {
1341da177e4SLinus Torvalds 	struct mtd_partition	*parts;
1351da177e4SLinus Torvalds 	struct mtd_info		*mtd;
1361da177e4SLinus Torvalds 	int			num_subdev;
137822e5e72SRussell King 	unsigned int		nr_parts;
1381da177e4SLinus Torvalds 	struct sa_subdev_info	subdev[0];
1391da177e4SLinus Torvalds };
1401da177e4SLinus Torvalds 
1411da177e4SLinus Torvalds static void sa1100_set_vpp(struct map_info *map, int on)
1421da177e4SLinus Torvalds {
1431da177e4SLinus Torvalds 	struct sa_subdev_info *subdev = container_of(map, struct sa_subdev_info, map);
14457725f0aSRussell King 	subdev->plat->set_vpp(on);
1451da177e4SLinus Torvalds }
1461da177e4SLinus Torvalds 
1471da177e4SLinus Torvalds static void sa1100_destroy_subdev(struct sa_subdev_info *subdev)
1481da177e4SLinus Torvalds {
1491da177e4SLinus Torvalds 	if (subdev->mtd)
1501da177e4SLinus Torvalds 		map_destroy(subdev->mtd);
1511da177e4SLinus Torvalds 	if (subdev->map.virt)
1521da177e4SLinus Torvalds 		iounmap(subdev->map.virt);
1531da177e4SLinus Torvalds 	release_mem_region(subdev->map.phys, subdev->map.size);
1541da177e4SLinus Torvalds }
1551da177e4SLinus Torvalds 
1561da177e4SLinus Torvalds static int sa1100_probe_subdev(struct sa_subdev_info *subdev, struct resource *res)
1571da177e4SLinus Torvalds {
1581da177e4SLinus Torvalds 	unsigned long phys;
1591da177e4SLinus Torvalds 	unsigned int size;
1601da177e4SLinus Torvalds 	int ret;
1611da177e4SLinus Torvalds 
1621da177e4SLinus Torvalds 	phys = res->start;
1631da177e4SLinus Torvalds 	size = res->end - phys + 1;
1641da177e4SLinus Torvalds 
1651da177e4SLinus Torvalds 	/*
1661da177e4SLinus Torvalds 	 * Retrieve the bankwidth from the MSC registers.
1671da177e4SLinus Torvalds 	 * We currently only implement CS0 and CS1 here.
1681da177e4SLinus Torvalds 	 */
1691da177e4SLinus Torvalds 	switch (phys) {
1701da177e4SLinus Torvalds 	default:
1711da177e4SLinus Torvalds 		printk(KERN_WARNING "SA1100 flash: unknown base address "
1721da177e4SLinus Torvalds 		       "0x%08lx, assuming CS0\n", phys);
1731da177e4SLinus Torvalds 
1741da177e4SLinus Torvalds 	case SA1100_CS0_PHYS:
1751da177e4SLinus Torvalds 		subdev->map.bankwidth = (MSC0 & MSC_RBW) ? 2 : 4;
1761da177e4SLinus Torvalds 		break;
1771da177e4SLinus Torvalds 
1781da177e4SLinus Torvalds 	case SA1100_CS1_PHYS:
1791da177e4SLinus Torvalds 		subdev->map.bankwidth = ((MSC0 >> 16) & MSC_RBW) ? 2 : 4;
1801da177e4SLinus Torvalds 		break;
1811da177e4SLinus Torvalds 	}
1821da177e4SLinus Torvalds 
1831da177e4SLinus Torvalds 	if (!request_mem_region(phys, size, subdev->name)) {
1841da177e4SLinus Torvalds 		ret = -EBUSY;
1851da177e4SLinus Torvalds 		goto out;
1861da177e4SLinus Torvalds 	}
1871da177e4SLinus Torvalds 
18857725f0aSRussell King 	if (subdev->plat->set_vpp)
1891da177e4SLinus Torvalds 		subdev->map.set_vpp = sa1100_set_vpp;
1901da177e4SLinus Torvalds 
1911da177e4SLinus Torvalds 	subdev->map.phys = phys;
1921da177e4SLinus Torvalds 	subdev->map.size = size;
1931da177e4SLinus Torvalds 	subdev->map.virt = ioremap(phys, size);
1941da177e4SLinus Torvalds 	if (!subdev->map.virt) {
1951da177e4SLinus Torvalds 		ret = -ENOMEM;
1961da177e4SLinus Torvalds 		goto err;
1971da177e4SLinus Torvalds 	}
1981da177e4SLinus Torvalds 
1991da177e4SLinus Torvalds 	simple_map_init(&subdev->map);
2001da177e4SLinus Torvalds 
2011da177e4SLinus Torvalds 	/*
2021da177e4SLinus Torvalds 	 * Now let's probe for the actual flash.  Do it here since
2031da177e4SLinus Torvalds 	 * specific machine settings might have been set above.
2041da177e4SLinus Torvalds 	 */
20557725f0aSRussell King 	subdev->mtd = do_map_probe(subdev->plat->map_name, &subdev->map);
2061da177e4SLinus Torvalds 	if (subdev->mtd == NULL) {
2071da177e4SLinus Torvalds 		ret = -ENXIO;
2081da177e4SLinus Torvalds 		goto err;
2091da177e4SLinus Torvalds 	}
2101da177e4SLinus Torvalds 	subdev->mtd->owner = THIS_MODULE;
2111da177e4SLinus Torvalds 
2121da177e4SLinus Torvalds 	printk(KERN_INFO "SA1100 flash: CFI device at 0x%08lx, %dMiB, "
2131da177e4SLinus Torvalds 		"%d-bit\n", phys, subdev->mtd->size >> 20,
2141da177e4SLinus Torvalds 		subdev->map.bankwidth * 8);
2151da177e4SLinus Torvalds 
2161da177e4SLinus Torvalds 	return 0;
2171da177e4SLinus Torvalds 
2181da177e4SLinus Torvalds  err:
2191da177e4SLinus Torvalds 	sa1100_destroy_subdev(subdev);
2201da177e4SLinus Torvalds  out:
2211da177e4SLinus Torvalds 	return ret;
2221da177e4SLinus Torvalds }
2231da177e4SLinus Torvalds 
2240d2ef7d7SRussell King static void sa1100_destroy(struct sa_info *info, struct flash_platform_data *plat)
2251da177e4SLinus Torvalds {
2261da177e4SLinus Torvalds 	int i;
2271da177e4SLinus Torvalds 
2281da177e4SLinus Torvalds 	if (info->mtd) {
229822e5e72SRussell King 		if (info->nr_parts == 0)
230822e5e72SRussell King 			del_mtd_device(info->mtd);
231822e5e72SRussell King #ifdef CONFIG_MTD_PARTITIONS
232822e5e72SRussell King 		else
2331da177e4SLinus Torvalds 			del_mtd_partitions(info->mtd);
234822e5e72SRussell King #endif
2351da177e4SLinus Torvalds #ifdef CONFIG_MTD_CONCAT
2361da177e4SLinus Torvalds 		if (info->mtd != info->subdev[0].mtd)
2371da177e4SLinus Torvalds 			mtd_concat_destroy(info->mtd);
2381da177e4SLinus Torvalds #endif
2391da177e4SLinus Torvalds 	}
2401da177e4SLinus Torvalds 
2411da177e4SLinus Torvalds 	kfree(info->parts);
2421da177e4SLinus Torvalds 
2431da177e4SLinus Torvalds 	for (i = info->num_subdev - 1; i >= 0; i--)
2441da177e4SLinus Torvalds 		sa1100_destroy_subdev(&info->subdev[i]);
2451da177e4SLinus Torvalds 	kfree(info);
2460d2ef7d7SRussell King 
2470d2ef7d7SRussell King 	if (plat->exit)
2480d2ef7d7SRussell King 		plat->exit();
2491da177e4SLinus Torvalds }
2501da177e4SLinus Torvalds 
2511da177e4SLinus Torvalds static struct sa_info *__init
25257725f0aSRussell King sa1100_setup_mtd(struct platform_device *pdev, struct flash_platform_data *plat)
2531da177e4SLinus Torvalds {
2541da177e4SLinus Torvalds 	struct sa_info *info;
2551da177e4SLinus Torvalds 	int nr, size, i, ret = 0;
2561da177e4SLinus Torvalds 
2571da177e4SLinus Torvalds 	/*
2581da177e4SLinus Torvalds 	 * Count number of devices.
2591da177e4SLinus Torvalds 	 */
2601da177e4SLinus Torvalds 	for (nr = 0; ; nr++)
2611da177e4SLinus Torvalds 		if (!platform_get_resource(pdev, IORESOURCE_MEM, nr))
2621da177e4SLinus Torvalds 			break;
2631da177e4SLinus Torvalds 
2641da177e4SLinus Torvalds 	if (nr == 0) {
2651da177e4SLinus Torvalds 		ret = -ENODEV;
2661da177e4SLinus Torvalds 		goto out;
2671da177e4SLinus Torvalds 	}
2681da177e4SLinus Torvalds 
2691da177e4SLinus Torvalds 	size = sizeof(struct sa_info) + sizeof(struct sa_subdev_info) * nr;
2701da177e4SLinus Torvalds 
2711da177e4SLinus Torvalds 	/*
2721da177e4SLinus Torvalds 	 * Allocate the map_info structs in one go.
2731da177e4SLinus Torvalds 	 */
27495b93a0cSBurman Yan 	info = kzalloc(size, GFP_KERNEL);
2751da177e4SLinus Torvalds 	if (!info) {
2761da177e4SLinus Torvalds 		ret = -ENOMEM;
2771da177e4SLinus Torvalds 		goto out;
2781da177e4SLinus Torvalds 	}
2791da177e4SLinus Torvalds 
2800d2ef7d7SRussell King 	if (plat->init) {
2810d2ef7d7SRussell King 		ret = plat->init();
2820d2ef7d7SRussell King 		if (ret)
2830d2ef7d7SRussell King 			goto err;
2840d2ef7d7SRussell King 	}
2850d2ef7d7SRussell King 
2861da177e4SLinus Torvalds 	/*
2871da177e4SLinus Torvalds 	 * Claim and then map the memory regions.
2881da177e4SLinus Torvalds 	 */
2891da177e4SLinus Torvalds 	for (i = 0; i < nr; i++) {
2901da177e4SLinus Torvalds 		struct sa_subdev_info *subdev = &info->subdev[i];
2911da177e4SLinus Torvalds 		struct resource *res;
2921da177e4SLinus Torvalds 
2931da177e4SLinus Torvalds 		res = platform_get_resource(pdev, IORESOURCE_MEM, i);
2941da177e4SLinus Torvalds 		if (!res)
2951da177e4SLinus Torvalds 			break;
2961da177e4SLinus Torvalds 
2971da177e4SLinus Torvalds 		subdev->map.name = subdev->name;
29814e66f76SRussell King 		sprintf(subdev->name, "%s-%d", plat->name, i);
29957725f0aSRussell King 		subdev->plat = plat;
3001da177e4SLinus Torvalds 
3011da177e4SLinus Torvalds 		ret = sa1100_probe_subdev(subdev, res);
3021da177e4SLinus Torvalds 		if (ret)
3031da177e4SLinus Torvalds 			break;
3041da177e4SLinus Torvalds 	}
3051da177e4SLinus Torvalds 
3061da177e4SLinus Torvalds 	info->num_subdev = i;
3071da177e4SLinus Torvalds 
3081da177e4SLinus Torvalds 	/*
3091da177e4SLinus Torvalds 	 * ENXIO is special.  It means we didn't find a chip when we probed.
3101da177e4SLinus Torvalds 	 */
3111da177e4SLinus Torvalds 	if (ret != 0 && !(ret == -ENXIO && info->num_subdev > 0))
3121da177e4SLinus Torvalds 		goto err;
3131da177e4SLinus Torvalds 
3141da177e4SLinus Torvalds 	/*
3151da177e4SLinus Torvalds 	 * If we found one device, don't bother with concat support.  If
3161da177e4SLinus Torvalds 	 * we found multiple devices, use concat if we have it available,
3171da177e4SLinus Torvalds 	 * otherwise fail.  Either way, it'll be called "sa1100".
3181da177e4SLinus Torvalds 	 */
3191da177e4SLinus Torvalds 	if (info->num_subdev == 1) {
32014e66f76SRussell King 		strcpy(info->subdev[0].name, plat->name);
3211da177e4SLinus Torvalds 		info->mtd = info->subdev[0].mtd;
3221da177e4SLinus Torvalds 		ret = 0;
3231da177e4SLinus Torvalds 	} else if (info->num_subdev > 1) {
3241da177e4SLinus Torvalds #ifdef CONFIG_MTD_CONCAT
3251da177e4SLinus Torvalds 		struct mtd_info *cdev[nr];
3261da177e4SLinus Torvalds 		/*
3271da177e4SLinus Torvalds 		 * We detected multiple devices.  Concatenate them together.
3281da177e4SLinus Torvalds 		 */
3291da177e4SLinus Torvalds 		for (i = 0; i < info->num_subdev; i++)
3301da177e4SLinus Torvalds 			cdev[i] = info->subdev[i].mtd;
3311da177e4SLinus Torvalds 
3321da177e4SLinus Torvalds 		info->mtd = mtd_concat_create(cdev, info->num_subdev,
33314e66f76SRussell King 					      plat->name);
3341da177e4SLinus Torvalds 		if (info->mtd == NULL)
3351da177e4SLinus Torvalds 			ret = -ENXIO;
3361da177e4SLinus Torvalds #else
3371da177e4SLinus Torvalds 		printk(KERN_ERR "SA1100 flash: multiple devices "
3381da177e4SLinus Torvalds 		       "found but MTD concat support disabled.\n");
3391da177e4SLinus Torvalds 		ret = -ENXIO;
3401da177e4SLinus Torvalds #endif
3411da177e4SLinus Torvalds 	}
3421da177e4SLinus Torvalds 
3431da177e4SLinus Torvalds 	if (ret == 0)
3441da177e4SLinus Torvalds 		return info;
3451da177e4SLinus Torvalds 
3461da177e4SLinus Torvalds  err:
3470d2ef7d7SRussell King 	sa1100_destroy(info, plat);
3481da177e4SLinus Torvalds  out:
3491da177e4SLinus Torvalds 	return ERR_PTR(ret);
3501da177e4SLinus Torvalds }
3511da177e4SLinus Torvalds 
3521da177e4SLinus Torvalds static const char *part_probes[] = { "cmdlinepart", "RedBoot", NULL };
3531da177e4SLinus Torvalds 
354*f0b1e589SUwe Kleine-König static int __devinit sa1100_mtd_probe(struct platform_device *pdev)
3551da177e4SLinus Torvalds {
35657725f0aSRussell King 	struct flash_platform_data *plat = pdev->dev.platform_data;
3571da177e4SLinus Torvalds 	struct mtd_partition *parts;
3581da177e4SLinus Torvalds 	const char *part_type = NULL;
3591da177e4SLinus Torvalds 	struct sa_info *info;
3601da177e4SLinus Torvalds 	int err, nr_parts = 0;
3611da177e4SLinus Torvalds 
36257725f0aSRussell King 	if (!plat)
3631da177e4SLinus Torvalds 		return -ENODEV;
3641da177e4SLinus Torvalds 
36557725f0aSRussell King 	info = sa1100_setup_mtd(pdev, plat);
3661da177e4SLinus Torvalds 	if (IS_ERR(info)) {
3671da177e4SLinus Torvalds 		err = PTR_ERR(info);
3681da177e4SLinus Torvalds 		goto out;
3691da177e4SLinus Torvalds 	}
3701da177e4SLinus Torvalds 
3711da177e4SLinus Torvalds 	/*
3721da177e4SLinus Torvalds 	 * Partition selection stuff.
3731da177e4SLinus Torvalds 	 */
3741da177e4SLinus Torvalds #ifdef CONFIG_MTD_PARTITIONS
3751da177e4SLinus Torvalds 	nr_parts = parse_mtd_partitions(info->mtd, part_probes, &parts, 0);
3761da177e4SLinus Torvalds 	if (nr_parts > 0) {
3771da177e4SLinus Torvalds 		info->parts = parts;
3781da177e4SLinus Torvalds 		part_type = "dynamic";
3791da177e4SLinus Torvalds 	} else
3801da177e4SLinus Torvalds #endif
3811da177e4SLinus Torvalds 	{
38257725f0aSRussell King 		parts = plat->parts;
38357725f0aSRussell King 		nr_parts = plat->nr_parts;
3841da177e4SLinus Torvalds 		part_type = "static";
3851da177e4SLinus Torvalds 	}
3861da177e4SLinus Torvalds 
3871da177e4SLinus Torvalds 	if (nr_parts == 0) {
3881da177e4SLinus Torvalds 		printk(KERN_NOTICE "SA1100 flash: no partition info "
3891da177e4SLinus Torvalds 			"available, registering whole flash\n");
3901da177e4SLinus Torvalds 		add_mtd_device(info->mtd);
3911da177e4SLinus Torvalds 	} else {
3921da177e4SLinus Torvalds 		printk(KERN_NOTICE "SA1100 flash: using %s partition "
3931da177e4SLinus Torvalds 			"definition\n", part_type);
3941da177e4SLinus Torvalds 		add_mtd_partitions(info->mtd, parts, nr_parts);
3951da177e4SLinus Torvalds 	}
3961da177e4SLinus Torvalds 
397822e5e72SRussell King 	info->nr_parts = nr_parts;
398822e5e72SRussell King 
3993ae5eaecSRussell King 	platform_set_drvdata(pdev, info);
4001da177e4SLinus Torvalds 	err = 0;
4011da177e4SLinus Torvalds 
4021da177e4SLinus Torvalds  out:
4031da177e4SLinus Torvalds 	return err;
4041da177e4SLinus Torvalds }
4051da177e4SLinus Torvalds 
4063ae5eaecSRussell King static int __exit sa1100_mtd_remove(struct platform_device *pdev)
4071da177e4SLinus Torvalds {
4083ae5eaecSRussell King 	struct sa_info *info = platform_get_drvdata(pdev);
4093ae5eaecSRussell King 	struct flash_platform_data *plat = pdev->dev.platform_data;
4100d2ef7d7SRussell King 
4113ae5eaecSRussell King 	platform_set_drvdata(pdev, NULL);
4120d2ef7d7SRussell King 	sa1100_destroy(info, plat);
4130d2ef7d7SRussell King 
4141da177e4SLinus Torvalds 	return 0;
4151da177e4SLinus Torvalds }
4161da177e4SLinus Torvalds 
4171da177e4SLinus Torvalds #ifdef CONFIG_PM
4183ae5eaecSRussell King static int sa1100_mtd_suspend(struct platform_device *dev, pm_message_t state)
4191da177e4SLinus Torvalds {
4203ae5eaecSRussell King 	struct sa_info *info = platform_get_drvdata(dev);
4211da177e4SLinus Torvalds 	int ret = 0;
4221da177e4SLinus Torvalds 
4239480e307SRussell King 	if (info)
4241da177e4SLinus Torvalds 		ret = info->mtd->suspend(info->mtd);
4251da177e4SLinus Torvalds 
4261da177e4SLinus Torvalds 	return ret;
4271da177e4SLinus Torvalds }
4281da177e4SLinus Torvalds 
4293ae5eaecSRussell King static int sa1100_mtd_resume(struct platform_device *dev)
4301da177e4SLinus Torvalds {
4313ae5eaecSRussell King 	struct sa_info *info = platform_get_drvdata(dev);
4329480e307SRussell King 	if (info)
4331da177e4SLinus Torvalds 		info->mtd->resume(info->mtd);
4341da177e4SLinus Torvalds 	return 0;
4351da177e4SLinus Torvalds }
43613bfb34cSRussell King 
4373ae5eaecSRussell King static void sa1100_mtd_shutdown(struct platform_device *dev)
43813bfb34cSRussell King {
4393ae5eaecSRussell King 	struct sa_info *info = platform_get_drvdata(dev);
44013bfb34cSRussell King 	if (info && info->mtd->suspend(info->mtd) == 0)
44113bfb34cSRussell King 		info->mtd->resume(info->mtd);
44213bfb34cSRussell King }
4431da177e4SLinus Torvalds #else
4441da177e4SLinus Torvalds #define sa1100_mtd_suspend NULL
4451da177e4SLinus Torvalds #define sa1100_mtd_resume  NULL
44613bfb34cSRussell King #define sa1100_mtd_shutdown NULL
4471da177e4SLinus Torvalds #endif
4481da177e4SLinus Torvalds 
4493ae5eaecSRussell King static struct platform_driver sa1100_mtd_driver = {
4501da177e4SLinus Torvalds 	.probe		= sa1100_mtd_probe,
4511da177e4SLinus Torvalds 	.remove		= __exit_p(sa1100_mtd_remove),
4521da177e4SLinus Torvalds 	.suspend	= sa1100_mtd_suspend,
4531da177e4SLinus Torvalds 	.resume		= sa1100_mtd_resume,
45413bfb34cSRussell King 	.shutdown	= sa1100_mtd_shutdown,
4553ae5eaecSRussell King 	.driver		= {
456bcc8f3e0SUwe Kleine-König 		.name	= "sa1100-mtd",
45741d867c9SKay Sievers 		.owner	= THIS_MODULE,
4583ae5eaecSRussell King 	},
4591da177e4SLinus Torvalds };
4601da177e4SLinus Torvalds 
4611da177e4SLinus Torvalds static int __init sa1100_mtd_init(void)
4621da177e4SLinus Torvalds {
4633ae5eaecSRussell King 	return platform_driver_register(&sa1100_mtd_driver);
4641da177e4SLinus Torvalds }
4651da177e4SLinus Torvalds 
4661da177e4SLinus Torvalds static void __exit sa1100_mtd_exit(void)
4671da177e4SLinus Torvalds {
4683ae5eaecSRussell King 	platform_driver_unregister(&sa1100_mtd_driver);
4691da177e4SLinus Torvalds }
4701da177e4SLinus Torvalds 
4711da177e4SLinus Torvalds module_init(sa1100_mtd_init);
4721da177e4SLinus Torvalds module_exit(sa1100_mtd_exit);
4731da177e4SLinus Torvalds 
4741da177e4SLinus Torvalds MODULE_AUTHOR("Nicolas Pitre");
4751da177e4SLinus Torvalds MODULE_DESCRIPTION("SA1100 CFI map driver");
4761da177e4SLinus Torvalds MODULE_LICENSE("GPL");
477bcc8f3e0SUwe Kleine-König MODULE_ALIAS("platform:sa1100-mtd");
478