kernel: add driver for virtual mtd_concat devices
authorBernhard Frauendienst <openwrt@nospam.obeliks.de>
Sat, 25 Aug 2018 10:48:54 +0000 (12:48 +0200)
committerJohn Crispin <john@phrozen.org>
Mon, 10 Sep 2018 07:35:07 +0000 (09:35 +0200)
Some systems require multiple flash chips to be concatenated and read as
a single mtd device. The ar71xx target provides custom code to create
such mtdconcat devices. When porting devices to ath79, however, there is
no way to create such devices from within the device tree.

This commit adds a driver for creating virtual mtd-concat devices to the
ath79 target. Nodes must have a compatible = "virtual,mtd-concat" line,
and define a list of devices to concat in the 'devices' property,
for example:

flash {
compatible = "virtual,mtd-concat";

devices = <&flash0 &flash1>;
};

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 which might in turn
cause other problems (like failure to load MAC addresses from art because
the partitions are not loaded yet).

Signed-off-by: Bernhard Frauendienst <openwrt@nospam.obeliks.de>
target/linux/generic/pending-4.14/495-mtd-core-add-get_mtd_device_by_node.patch [new file with mode: 0644]
target/linux/generic/pending-4.14/496-dt-bindings-add-bindings-for-mtd-concat-devices.patch [new file with mode: 0644]
target/linux/generic/pending-4.14/497-mtd-mtdconcat-add-dt-driver-for-concat-devices.patch [new file with mode: 0644]

diff --git a/target/linux/generic/pending-4.14/495-mtd-core-add-get_mtd_device_by_node.patch b/target/linux/generic/pending-4.14/495-mtd-core-add-get_mtd_device_by_node.patch
new file mode 100644 (file)
index 0000000..32a87d3
--- /dev/null
@@ -0,0 +1,82 @@
+From 1bd1b740f208d1cf4071932cc51860d37266c402 Mon Sep 17 00:00:00 2001
+From: Bernhard Frauendienst <kernel@nospam.obeliks.de>
+Date: Sat, 1 Sep 2018 00:30:11 +0200
+Subject: [PATCH 495/497] mtd: core: add get_mtd_device_by_node
+
+Add function to retrieve a mtd device by its OF node. Since drivers can
+assign arbitrary names to mtd devices in the absence of a label
+property, there is no other reliable way to retrieve a mtd device for a
+given OF node.
+
+Signed-off-by: Bernhard Frauendienst <kernel@nospam.obeliks.de>
+Reviewed-by: Miquel Raynal <miquel.raynal@bootlin.com>
+---
+ drivers/mtd/mtdcore.c   | 38 ++++++++++++++++++++++++++++++++++++++
+ include/linux/mtd/mtd.h |  2 ++
+ 2 files changed, 40 insertions(+)
+
+diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c
+index e7ea842ba3db..1134bb81d2e5 100644
+--- a/drivers/mtd/mtdcore.c
++++ b/drivers/mtd/mtdcore.c
+@@ -936,6 +936,44 @@ struct mtd_info *get_mtd_device_nm(const char *name)
+ }
+ EXPORT_SYMBOL_GPL(get_mtd_device_nm);
++/**
++ *    get_mtd_device_by_node - obtain a validated handle for an MTD device
++ *    by of_node
++ *    @of_node: OF node of MTD device to open
++ *
++ *    This function returns MTD device description structure in case of
++ *    success and an error code in case of failure.
++ */
++struct mtd_info *get_mtd_device_by_node(const struct device_node *of_node)
++{
++      int err = -ENODEV;
++      struct mtd_info *mtd = NULL, *other;
++
++      mutex_lock(&mtd_table_mutex);
++
++      mtd_for_each_device(other) {
++              if (of_node == other->dev.of_node) {
++                      mtd = other;
++                      break;
++              }
++      }
++
++      if (!mtd)
++              goto out_unlock;
++
++      err = __get_mtd_device(mtd);
++      if (err)
++              goto out_unlock;
++
++      mutex_unlock(&mtd_table_mutex);
++      return mtd;
++
++out_unlock:
++      mutex_unlock(&mtd_table_mutex);
++      return ERR_PTR(err);
++}
++EXPORT_SYMBOL_GPL(get_mtd_device_by_node);
++
+ void put_mtd_device(struct mtd_info *mtd)
+ {
+       mutex_lock(&mtd_table_mutex);
+diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h
+index 6cd0f6b7658b..cf7c8030cd8e 100644
+--- a/include/linux/mtd/mtd.h
++++ b/include/linux/mtd/mtd.h
+@@ -557,6 +557,8 @@ extern struct mtd_info *get_mtd_device(struct mtd_info *mtd, int num);
+ extern int __get_mtd_device(struct mtd_info *mtd);
+ extern void __put_mtd_device(struct mtd_info *mtd);
+ extern struct mtd_info *get_mtd_device_nm(const char *name);
++extern struct mtd_info *get_mtd_device_by_node(
++              const struct device_node *of_node);
+ extern void put_mtd_device(struct mtd_info *mtd);
+-- 
+2.18.0
+
diff --git a/target/linux/generic/pending-4.14/496-dt-bindings-add-bindings-for-mtd-concat-devices.patch b/target/linux/generic/pending-4.14/496-dt-bindings-add-bindings-for-mtd-concat-devices.patch
new file mode 100644 (file)
index 0000000..99fdb0d
--- /dev/null
@@ -0,0 +1,58 @@
+From 5734c6669fba7ddb5ef491ccff7159d15dba0b59 Mon Sep 17 00:00:00 2001
+From: Bernhard Frauendienst <kernel@nospam.obeliks.de>
+Date: Wed, 5 Sep 2018 01:32:51 +0200
+Subject: [PATCH 496/497] dt-bindings: add bindings for mtd-concat devices
+
+Document virtual mtd-concat device bindings.
+
+Signed-off-by: Bernhard Frauendienst <kernel@nospam.obeliks.de>
+---
+ .../devicetree/bindings/mtd/mtd-concat.txt    | 36 +++++++++++++++++++
+ 1 file changed, 36 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/mtd/mtd-concat.txt
+
+diff --git a/Documentation/devicetree/bindings/mtd/mtd-concat.txt b/Documentation/devicetree/bindings/mtd/mtd-concat.txt
+new file mode 100644
+index 000000000000..2daf3157b163
+--- /dev/null
++++ b/Documentation/devicetree/bindings/mtd/mtd-concat.txt
+@@ -0,0 +1,36 @@
++Virtual MTD concat device
++
++Requires properties:
++- devices: list of phandles to mtd nodes that should be concatenated
++
++Example:
++
++&spi {
++      flash0: flash@0 {
++              ...
++      };
++      flash1: flash@1 {
++              ...
++      };
++};
++
++flash {
++      compatible = "mtd-concat";
++
++      devices = <&flash0 &flash1>;
++
++      partitions {
++              compatible = "fixed-partitions";
++
++              partition@0 {
++                      label = "boot";
++                      reg = <0x0000000 0x0040000>;
++                      read-only;
++              };
++
++              partition@40000 {
++                      label = "firmware";
++                      reg = <0x0040000 0x1fc0000>;
++              };
++      }
++}
+-- 
+2.18.0
+
diff --git a/target/linux/generic/pending-4.14/497-mtd-mtdconcat-add-dt-driver-for-concat-devices.patch b/target/linux/generic/pending-4.14/497-mtd-mtdconcat-add-dt-driver-for-concat-devices.patch
new file mode 100644 (file)
index 0000000..0c5ac5b
--- /dev/null
@@ -0,0 +1,232 @@
+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
+
+diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig
+index 5a2d71729b9a..de18bc568e3e 100644
+--- a/drivers/mtd/Kconfig
++++ b/drivers/mtd/Kconfig
+@@ -342,4 +342,6 @@ source "drivers/mtd/spi-nor/Kconfig"
+ source "drivers/mtd/ubi/Kconfig"
++source "drivers/mtd/composite/Kconfig"
++
+ endif # MTD
+diff --git a/drivers/mtd/Makefile b/drivers/mtd/Makefile
+index d6f8f625e1ff..a6c5f134c35d 100644
+--- a/drivers/mtd/Makefile
++++ b/drivers/mtd/Makefile
+@@ -36,3 +36,6 @@ obj-y                += chips/ lpddr/ maps/ devices/ nand/ onenand/ tests/
+ obj-$(CONFIG_MTD_SPI_NOR)     += spi-nor/
+ obj-$(CONFIG_MTD_UBI)         += ubi/
++
++# Composite drivers must be loaded last
++obj-y         += composite/
+diff --git a/drivers/mtd/composite/Kconfig b/drivers/mtd/composite/Kconfig
+new file mode 100644
+index 000000000000..0490fc0284bb
+--- /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
+diff --git a/drivers/mtd/composite/Makefile b/drivers/mtd/composite/Makefile
+new file mode 100644
+index 000000000000..8421a0a30606
+--- /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
+diff --git a/drivers/mtd/composite/virt_concat.c b/drivers/mtd/composite/virt_concat.c
+new file mode 100644
+index 000000000000..bfd432188c35
+--- /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");
+-- 
+2.18.0
+