| 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 |
| @@ -228,4 +228,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 |
| @@ -32,3 +32,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"); |