Commit 9fd1e8f9 authored by Catalin Marinas's avatar Catalin Marinas Committed by David Woodhouse
Browse files

mtd: Add armflash support for multiple blocks of flash



This patch adds MTD concatenation support to integrator-flash.c for
platforms with more than one block of flash memory (e.g. RealView
PB11MPCore). The implementation is based on the sa1100-flash.c one.

Signed-off-by: default avatarCatalin Marinas <catalin.marinas@arm.com>
Acked-by: default avatarRussell King <rmk+kernel@arm.linux.org.uk>
Signed-off-by: default avatarDavid Woodhouse <David.Woodhouse@intel.com>
parent 8d42b524
Loading
Loading
Loading
Loading
+164 −62
Original line number Original line Diff line number Diff line
@@ -36,27 +36,33 @@
#include <linux/mtd/mtd.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/map.h>
#include <linux/mtd/map.h>
#include <linux/mtd/partitions.h>
#include <linux/mtd/partitions.h>
#include <linux/mtd/concat.h>


#include <asm/mach/flash.h>
#include <asm/mach/flash.h>
#include <mach/hardware.h>
#include <mach/hardware.h>
#include <asm/system.h>
#include <asm/system.h>


#ifdef CONFIG_ARCH_P720T
#define SUBDEV_NAME_SIZE	(BUS_ID_SIZE + 2)
#define FLASH_BASE		(0x04000000)
#define FLASH_SIZE		(64*1024*1024)
#endif


struct armflash_info {
struct armflash_subdev_info {
	char			name[SUBDEV_NAME_SIZE];
	struct mtd_info		*mtd;
	struct map_info		map;
	struct flash_platform_data *plat;
	struct flash_platform_data *plat;
};

struct armflash_info {
	struct resource		*res;
	struct resource		*res;
	struct mtd_partition	*parts;
	struct mtd_partition	*parts;
	struct mtd_info		*mtd;
	struct mtd_info		*mtd;
	struct map_info		map;
	int			nr_subdev;
	struct armflash_subdev_info subdev[0];
};
};


static void armflash_set_vpp(struct map_info *map, int on)
static void armflash_set_vpp(struct map_info *map, int on)
{
{
	struct armflash_info *info = container_of(map, struct armflash_info, map);
	struct armflash_subdev_info *info =
		container_of(map, struct armflash_subdev_info, map);


	if (info->plat && info->plat->set_vpp)
	if (info->plat && info->plat->set_vpp)
		info->plat->set_vpp(on);
		info->plat->set_vpp(on);
@@ -64,32 +70,17 @@ static void armflash_set_vpp(struct map_info *map, int on)


static const char *probes[] = { "cmdlinepart", "RedBoot", "afs", NULL };
static const char *probes[] = { "cmdlinepart", "RedBoot", "afs", NULL };


static int armflash_probe(struct platform_device *dev)
static int armflash_subdev_probe(struct armflash_subdev_info *subdev,
				 struct resource *res)
{
{
	struct flash_platform_data *plat = dev->dev.platform_data;
	struct flash_platform_data *plat = subdev->plat;
	struct resource *res = dev->resource;
	resource_size_t size = res->end - res->start + 1;
	unsigned int size = res->end - res->start + 1;
	struct armflash_info *info;
	int err;
	void __iomem *base;
	void __iomem *base;
	int err = 0;


	info = kzalloc(sizeof(struct armflash_info), GFP_KERNEL);
	if (!request_mem_region(res->start, size, subdev->name)) {
	if (!info) {
		err = -ENOMEM;
		goto out;
	}

	info->plat = plat;
	if (plat && plat->init) {
		err = plat->init();
		if (err)
			goto no_resource;
	}

	info->res = request_mem_region(res->start, size, "armflash");
	if (!info->res) {
		err = -EBUSY;
		err = -EBUSY;
		goto no_resource;
		goto out;
	}
	}


	base = ioremap(res->start, size);
	base = ioremap(res->start, size);
@@ -101,27 +92,132 @@ static int armflash_probe(struct platform_device *dev)
	/*
	/*
	 * look for CFI based flash parts fitted to this board
	 * look for CFI based flash parts fitted to this board
	 */
	 */
	info->map.size		= size;
	subdev->map.size	= size;
	info->map.bankwidth	= plat->width;
	subdev->map.bankwidth	= plat->width;
	info->map.phys		= res->start;
	subdev->map.phys	= res->start;
	info->map.virt		= base;
	subdev->map.virt	= base;
	info->map.name		= dev_name(&dev->dev);
	subdev->map.name	= subdev->name;
	info->map.set_vpp	= armflash_set_vpp;
	subdev->map.set_vpp	= armflash_set_vpp;


	simple_map_init(&info->map);
	simple_map_init(&subdev->map);


	/*
	/*
	 * Also, the CFI layer automatically works out what size
	 * Also, the CFI layer automatically works out what size
	 * of chips we have, and does the necessary identification
	 * of chips we have, and does the necessary identification
	 * for us automatically.
	 * for us automatically.
	 */
	 */
	info->mtd = do_map_probe(plat->map_name, &info->map);
	subdev->mtd = do_map_probe(plat->map_name, &subdev->map);
	if (!info->mtd) {
	if (!subdev->mtd) {
		err = -ENXIO;
		err = -ENXIO;
		goto no_device;
		goto no_device;
	}
	}


	info->mtd->owner = THIS_MODULE;
	subdev->mtd->owner = THIS_MODULE;

	/* Successful? */
	if (err == 0)
		return err;

	if (subdev->mtd)
		map_destroy(subdev->mtd);
 no_device:
	iounmap(base);
 no_mem:
	release_mem_region(res->start, size);
 out:
	return err;
}

static void armflash_subdev_remove(struct armflash_subdev_info *subdev)
{
	if (subdev->mtd)
		map_destroy(subdev->mtd);
	if (subdev->map.virt)
		iounmap(subdev->map.virt);
	release_mem_region(subdev->map.phys, subdev->map.size);
}

static int armflash_probe(struct platform_device *dev)
{
	struct flash_platform_data *plat = dev->dev.platform_data;
	unsigned int size;
	struct armflash_info *info;
	int i, nr, err;

	/* Count the number of devices */
	for (nr = 0; ; nr++)
		if (!platform_get_resource(dev, IORESOURCE_MEM, nr))
			break;
	if (nr == 0) {
		err = -ENODEV;
		goto out;
	}

	size = sizeof(struct armflash_info) +
		sizeof(struct armflash_subdev_info) * nr;
	info = kzalloc(size, GFP_KERNEL);
	if (!info) {
		err = -ENOMEM;
		goto out;
	}

	if (plat && plat->init) {
		err = plat->init();
		if (err)
			goto no_resource;
	}

	for (i = 0; i < nr; i++) {
		struct armflash_subdev_info *subdev = &info->subdev[i];
		struct resource *res;

		res = platform_get_resource(dev, IORESOURCE_MEM, i);
		if (!res)
			break;

		if (nr == 1)
			/* No MTD concatenation, just use the default name */
			snprintf(subdev->name, SUBDEV_NAME_SIZE, "%s",
				 dev_name(&dev->dev));
		else
			snprintf(subdev->name, SUBDEV_NAME_SIZE, "%s-%d",
				 dev_name(&dev->dev), i);
		subdev->plat = plat;

		err = armflash_subdev_probe(subdev, res);
		if (err)
			break;
	}
	info->nr_subdev = i;

	if (err)
		goto subdev_err;

	if (info->nr_subdev == 1)
		info->mtd = info->subdev[0].mtd;
	else if (info->nr_subdev > 1) {
#ifdef CONFIG_MTD_CONCAT
		struct mtd_info *cdev[info->nr_subdev];

		/*
		 * We detected multiple devices.  Concatenate them together.
		 */
		for (i = 0; i < info->nr_subdev; i++)
			cdev[i] = info->subdev[i].mtd;

		info->mtd = mtd_concat_create(cdev, info->nr_subdev,
					      dev_name(&dev->dev));
		if (info->mtd == NULL)
			err = -ENXIO;
#else
		printk(KERN_ERR "armflash: multiple devices found but "
		       "MTD concat support disabled.\n");
		err = -ENXIO;
#endif
	}

	if (err < 0)
		goto cleanup;


	err = parse_mtd_partitions(info->mtd, probes, &info->parts, 0);
	err = parse_mtd_partitions(info->mtd, probes, &info->parts, 0);
	if (err > 0) {
	if (err > 0) {
@@ -131,28 +227,30 @@ static int armflash_probe(struct platform_device *dev)
			       "mtd partition registration failed: %d\n", err);
			       "mtd partition registration failed: %d\n", err);
	}
	}


	if (err == 0)
	if (err == 0) {
		platform_set_drvdata(dev, info);
		platform_set_drvdata(dev, info);
		return err;
	}


	/*
	/*
	 * If we got an error, free all resources.
	 * We got an error, free all resources.
	 */
	 */
	if (err < 0) {
 cleanup:
	if (info->mtd) {
	if (info->mtd) {
		del_mtd_partitions(info->mtd);
		del_mtd_partitions(info->mtd);
			map_destroy(info->mtd);
#ifdef CONFIG_MTD_CONCAT
		if (info->mtd != info->subdev[0].mtd)
			mtd_concat_destroy(info->mtd);
#endif
	}
	}
	kfree(info->parts);
	kfree(info->parts);

 subdev_err:
 no_device:
	for (i = info->nr_subdev - 1; i >= 0; i--)
		iounmap(base);
		armflash_subdev_remove(&info->subdev[i]);
 no_mem:
		release_mem_region(res->start, size);
 no_resource:
 no_resource:
	if (plat && plat->exit)
	if (plat && plat->exit)
		plat->exit();
		plat->exit();
	kfree(info);
	kfree(info);
	}
 out:
 out:
	return err;
	return err;
}
}
@@ -160,22 +258,26 @@ static int armflash_probe(struct platform_device *dev)
static int armflash_remove(struct platform_device *dev)
static int armflash_remove(struct platform_device *dev)
{
{
	struct armflash_info *info = platform_get_drvdata(dev);
	struct armflash_info *info = platform_get_drvdata(dev);
	struct flash_platform_data *plat = dev->dev.platform_data;
	int i;


	platform_set_drvdata(dev, NULL);
	platform_set_drvdata(dev, NULL);


	if (info) {
	if (info) {
		if (info->mtd) {
		if (info->mtd) {
			del_mtd_partitions(info->mtd);
			del_mtd_partitions(info->mtd);
			map_destroy(info->mtd);
#ifdef CONFIG_MTD_CONCAT
			if (info->mtd != info->subdev[0].mtd)
				mtd_concat_destroy(info->mtd);
#endif
		}
		}
		kfree(info->parts);
		kfree(info->parts);


		iounmap(info->map.virt);
		for (i = info->nr_subdev - 1; i >= 0; i--)
		release_resource(info->res);
			armflash_subdev_remove(&info->subdev[i]);
		kfree(info->res);


		if (info->plat && info->plat->exit)
		if (plat && plat->exit)
			info->plat->exit();
			plat->exit();


		kfree(info);
		kfree(info);
	}
	}