217 lines
		
	
	
		
			5.8 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
			
		
		
	
	
			217 lines
		
	
	
		
			5.8 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
| From e53f712d8eac71f54399b61038ccf87d2cee99d7 Mon Sep 17 00:00:00 2001
 | |
| From: Bernhard Frauendienst <kernel@nospam.obeliks.de>
 | |
| Date: Sat, 25 Aug 2018 12:35:22 +0200
 | |
| Subject: [PATCH 497/497] mtd: mtdconcat: add dt driver for concat devices
 | |
| 
 | |
| Some mtd drivers like physmap variants have support for concatenating
 | |
| multiple mtd devices, but there is no generic way to define such a
 | |
| concat device from within the device tree.
 | |
| 
 | |
| This is useful for some SoC boards that use multiple flash chips as
 | |
| memory banks of a single mtd device, with partitions spanning chip
 | |
| borders.
 | |
| 
 | |
| This commit adds a driver for creating virtual mtd-concat devices. They
 | |
| must have a compatible = "mtd-concat" line, and define a list of devices
 | |
| to concat in the 'devices' property, for example:
 | |
| 
 | |
| flash {
 | |
|   compatible = "mtd-concat";
 | |
| 
 | |
|   devices = <&flash0 &flash1>;
 | |
| 
 | |
|   partitions {
 | |
|     ...
 | |
|   };
 | |
| };
 | |
| 
 | |
| The driver is added to the very end of the mtd Makefile to increase the
 | |
| likelyhood of all child devices already being loaded at the time of
 | |
| probing, preventing unnecessary deferred probes.
 | |
| 
 | |
| Signed-off-by: Bernhard Frauendienst <kernel@nospam.obeliks.de>
 | |
| ---
 | |
|  drivers/mtd/Kconfig                 |   2 +
 | |
|  drivers/mtd/Makefile                |   3 +
 | |
|  drivers/mtd/composite/Kconfig       |  12 +++
 | |
|  drivers/mtd/composite/Makefile      |   6 ++
 | |
|  drivers/mtd/composite/virt_concat.c | 128 ++++++++++++++++++++++++++++
 | |
|  5 files changed, 151 insertions(+)
 | |
|  create mode 100644 drivers/mtd/composite/Kconfig
 | |
|  create mode 100644 drivers/mtd/composite/Makefile
 | |
|  create mode 100644 drivers/mtd/composite/virt_concat.c
 | |
| 
 | |
| --- a/drivers/mtd/Kconfig
 | |
| +++ b/drivers/mtd/Kconfig
 | |
| @@ -238,4 +238,6 @@ source "drivers/mtd/ubi/Kconfig"
 | |
|  
 | |
|  source "drivers/mtd/hyperbus/Kconfig"
 | |
|  
 | |
| +source "drivers/mtd/composite/Kconfig"
 | |
| +
 | |
|  endif # MTD
 | |
| --- a/drivers/mtd/Makefile
 | |
| +++ b/drivers/mtd/Makefile
 | |
| @@ -33,3 +33,6 @@ obj-y		+= chips/ lpddr/ maps/ devices/ n
 | |
|  obj-$(CONFIG_MTD_SPI_NOR)	+= spi-nor/
 | |
|  obj-$(CONFIG_MTD_UBI)		+= ubi/
 | |
|  obj-$(CONFIG_MTD_HYPERBUS)	+= hyperbus/
 | |
| +
 | |
| +# Composite drivers must be loaded last
 | |
| +obj-y				+= composite/
 | |
| --- /dev/null
 | |
| +++ b/drivers/mtd/composite/Kconfig
 | |
| @@ -0,0 +1,12 @@
 | |
| +menu "Composite MTD device drivers"
 | |
| +	depends on MTD!=n
 | |
| +
 | |
| +config MTD_VIRT_CONCAT
 | |
| +	tristate "Virtual concat MTD device"
 | |
| +	help
 | |
| +	  This driver allows creation of a virtual MTD concat device, which
 | |
| +	  concatenates multiple underlying MTD devices to a single device.
 | |
| +	  This is required by some SoC boards where multiple memory banks are
 | |
| +	  used as one device with partitions spanning across device boundaries.
 | |
| +
 | |
| +endmenu
 | |
| --- /dev/null
 | |
| +++ b/drivers/mtd/composite/Makefile
 | |
| @@ -0,0 +1,6 @@
 | |
| +# SPDX-License-Identifier: GPL-2.0
 | |
| +#
 | |
| +# linux/drivers/mtd/composite/Makefile
 | |
| +#
 | |
| +
 | |
| +obj-$(CONFIG_MTD_VIRT_CONCAT)   += virt_concat.o
 | |
| --- /dev/null
 | |
| +++ b/drivers/mtd/composite/virt_concat.c
 | |
| @@ -0,0 +1,128 @@
 | |
| +// SPDX-License-Identifier: GPL-2.0+
 | |
| +/*
 | |
| + * Virtual concat MTD device driver
 | |
| + *
 | |
| + * Copyright (C) 2018 Bernhard Frauendienst
 | |
| + * Author: Bernhard Frauendienst, kernel@nospam.obeliks.de
 | |
| + */
 | |
| +
 | |
| +#include <linux/module.h>
 | |
| +#include <linux/device.h>
 | |
| +#include <linux/mtd/concat.h>
 | |
| +#include <linux/mtd/mtd.h>
 | |
| +#include <linux/mtd/partitions.h>
 | |
| +#include <linux/of.h>
 | |
| +#include <linux/of_platform.h>
 | |
| +#include <linux/slab.h>
 | |
| +
 | |
| +/*
 | |
| + * struct of_virt_concat - platform device driver data.
 | |
| + * @cmtd the final mtd_concat device
 | |
| + * @num_devices the number of devices in @devices
 | |
| + * @devices points to an array of devices already loaded
 | |
| + */
 | |
| +struct of_virt_concat {
 | |
| +	struct mtd_info	*cmtd;
 | |
| +	int num_devices;
 | |
| +	struct mtd_info	**devices;
 | |
| +};
 | |
| +
 | |
| +static int virt_concat_remove(struct platform_device *pdev)
 | |
| +{
 | |
| +	struct of_virt_concat *info;
 | |
| +	int i;
 | |
| +
 | |
| +	info = platform_get_drvdata(pdev);
 | |
| +	if (!info)
 | |
| +		return 0;
 | |
| +
 | |
| +	// unset data for when this is called after a probe error
 | |
| +	platform_set_drvdata(pdev, NULL);
 | |
| +
 | |
| +	if (info->cmtd) {
 | |
| +		mtd_device_unregister(info->cmtd);
 | |
| +		mtd_concat_destroy(info->cmtd);
 | |
| +	}
 | |
| +
 | |
| +	if (info->devices) {
 | |
| +		for (i = 0; i < info->num_devices; i++)
 | |
| +			put_mtd_device(info->devices[i]);
 | |
| +	}
 | |
| +
 | |
| +	return 0;
 | |
| +}
 | |
| +
 | |
| +static int virt_concat_probe(struct platform_device *pdev)
 | |
| +{
 | |
| +	struct device_node *node = pdev->dev.of_node;
 | |
| +	struct of_phandle_iterator it;
 | |
| +	struct of_virt_concat *info;
 | |
| +	struct mtd_info *mtd;
 | |
| +	int err = 0, count;
 | |
| +
 | |
| +	count = of_count_phandle_with_args(node, "devices", NULL);
 | |
| +	if (count <= 0)
 | |
| +		return -EINVAL;
 | |
| +
 | |
| +	info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
 | |
| +	if (!info)
 | |
| +		return -ENOMEM;
 | |
| +	info->devices = devm_kcalloc(&pdev->dev, count,
 | |
| +				     sizeof(*(info->devices)), GFP_KERNEL);
 | |
| +	if (!info->devices) {
 | |
| +		err = -ENOMEM;
 | |
| +		goto err_remove;
 | |
| +	}
 | |
| +
 | |
| +	platform_set_drvdata(pdev, info);
 | |
| +
 | |
| +	of_for_each_phandle(&it, err, node, "devices", NULL, 0) {
 | |
| +		mtd = get_mtd_device_by_node(it.node);
 | |
| +		if (IS_ERR(mtd)) {
 | |
| +			of_node_put(it.node);
 | |
| +			err = -EPROBE_DEFER;
 | |
| +			goto err_remove;
 | |
| +		}
 | |
| +
 | |
| +		info->devices[info->num_devices++] = mtd;
 | |
| +	}
 | |
| +
 | |
| +	info->cmtd = mtd_concat_create(info->devices, info->num_devices,
 | |
| +				       dev_name(&pdev->dev));
 | |
| +	if (!info->cmtd) {
 | |
| +		err = -ENXIO;
 | |
| +		goto err_remove;
 | |
| +	}
 | |
| +
 | |
| +	info->cmtd->dev.parent = &pdev->dev;
 | |
| +	mtd_set_of_node(info->cmtd, node);
 | |
| +	mtd_device_register(info->cmtd, NULL, 0);
 | |
| +
 | |
| +	return 0;
 | |
| +
 | |
| +err_remove:
 | |
| +	virt_concat_remove(pdev);
 | |
| +
 | |
| +	return err;
 | |
| +}
 | |
| +
 | |
| +static const struct of_device_id virt_concat_of_match[] = {
 | |
| +	{ .compatible = "mtd-concat", },
 | |
| +	{ /* sentinel */ }
 | |
| +};
 | |
| +MODULE_DEVICE_TABLE(of, virt_concat_of_match);
 | |
| +
 | |
| +static struct platform_driver virt_concat_driver = {
 | |
| +	.probe = virt_concat_probe,
 | |
| +	.remove = virt_concat_remove,
 | |
| +	.driver	 = {
 | |
| +		.name   = "virt-mtdconcat",
 | |
| +		.of_match_table = virt_concat_of_match,
 | |
| +	},
 | |
| +};
 | |
| +
 | |
| +module_platform_driver(virt_concat_driver);
 | |
| +
 | |
| +MODULE_LICENSE("GPL v2");
 | |
| +MODULE_AUTHOR("Bernhard Frauendienst <kernel@nospam.obeliks.de>");
 | |
| +MODULE_DESCRIPTION("Virtual concat MTD device driver");
 | 
