kernel: backport NVMEM changes queued for v6.8
authorRafał Miłecki <rafal@milecki.pl>
Sun, 17 Dec 2023 21:35:13 +0000 (22:35 +0100)
committerRafał Miłecki <rafal@milecki.pl>
Mon, 18 Dec 2023 07:33:04 +0000 (08:33 +0100)
Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
18 files changed:
target/linux/generic/backport-5.15/832-v6.8-of-device-Export-of_device_make_bus_id.patch [new file with mode: 0644]
target/linux/generic/backport-5.15/834-v6.8-0001-nvmem-Move-of_nvmem_layout_get_container-in-another-.patch [new file with mode: 0644]
target/linux/generic/backport-5.15/834-v6.8-0002-nvmem-Create-a-header-for-internal-sharing.patch [new file with mode: 0644]
target/linux/generic/backport-5.15/834-v6.8-0003-nvmem-Simplify-the-add_cells-hook.patch [new file with mode: 0644]
target/linux/generic/backport-5.15/834-v6.8-0004-nvmem-Move-and-rename-fixup_cell_info.patch [new file with mode: 0644]
target/linux/generic/backport-5.15/834-v6.8-0005-nvmem-core-Rework-layouts-to-become-regular-devices.patch [new file with mode: 0644]
target/linux/generic/backport-5.15/834-v6.8-0006-nvmem-core-Expose-cells-through-sysfs.patch [new file with mode: 0644]
target/linux/generic/backport-5.15/834-v6.8-0007-nvmem-stm32-add-support-for-STM32MP25-BSEC-to-contro.patch [new file with mode: 0644]
target/linux/generic/backport-6.1/818-v6.8-of-device-Export-of_device_make_bus_id.patch [new file with mode: 0644]
target/linux/generic/backport-6.1/819-v6.8-0001-nvmem-Move-of_nvmem_layout_get_container-in-another-.patch [new file with mode: 0644]
target/linux/generic/backport-6.1/819-v6.8-0002-nvmem-Create-a-header-for-internal-sharing.patch [new file with mode: 0644]
target/linux/generic/backport-6.1/819-v6.8-0003-nvmem-Simplify-the-add_cells-hook.patch [new file with mode: 0644]
target/linux/generic/backport-6.1/819-v6.8-0004-nvmem-Move-and-rename-fixup_cell_info.patch [new file with mode: 0644]
target/linux/generic/backport-6.1/819-v6.8-0005-nvmem-core-Rework-layouts-to-become-regular-devices.patch [new file with mode: 0644]
target/linux/generic/backport-6.1/819-v6.8-0006-nvmem-core-Expose-cells-through-sysfs.patch [new file with mode: 0644]
target/linux/generic/backport-6.1/819-v6.8-0007-nvmem-stm32-add-support-for-STM32MP25-BSEC-to-contro.patch [new file with mode: 0644]
target/linux/generic/pending-5.15/804-nvmem-core-support-mac-base-fixed-layout-cells.patch
target/linux/generic/pending-6.1/804-nvmem-core-support-mac-base-fixed-layout-cells.patch

diff --git a/target/linux/generic/backport-5.15/832-v6.8-of-device-Export-of_device_make_bus_id.patch b/target/linux/generic/backport-5.15/832-v6.8-of-device-Export-of_device_make_bus_id.patch
new file mode 100644 (file)
index 0000000..d097c1b
--- /dev/null
@@ -0,0 +1,128 @@
+From 7f38b70042fcaa49219045bd1a9a2836e27a58ac Mon Sep 17 00:00:00 2001
+From: Miquel Raynal <miquel.raynal@bootlin.com>
+Date: Fri, 15 Dec 2023 11:15:27 +0000
+Subject: [PATCH] of: device: Export of_device_make_bus_id()
+
+This helper is really handy to create unique device names based on their
+device tree path, we may need it outside of the OF core (in the NVMEM
+subsystem) so let's export it. As this helper has nothing patform
+specific, let's move it to of/device.c instead of of/platform.c so we
+can add its prototype to of_device.h.
+
+Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
+Acked-by: Rob Herring <robh@kernel.org>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20231215111536.316972-2-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/of/device.c       | 41 +++++++++++++++++++++++++++++++++++++++
+ drivers/of/platform.c     | 40 --------------------------------------
+ include/linux/of_device.h |  6 ++++++
+ 3 files changed, 47 insertions(+), 40 deletions(-)
+
+--- a/drivers/of/device.c
++++ b/drivers/of/device.c
+@@ -337,3 +337,38 @@ int of_device_uevent_modalias(struct dev
+       return 0;
+ }
+ EXPORT_SYMBOL_GPL(of_device_uevent_modalias);
++
++/**
++ * of_device_make_bus_id - Use the device node data to assign a unique name
++ * @dev: pointer to device structure that is linked to a device tree node
++ *
++ * This routine will first try using the translated bus address to
++ * derive a unique name. If it cannot, then it will prepend names from
++ * parent nodes until a unique name can be derived.
++ */
++void of_device_make_bus_id(struct device *dev)
++{
++      struct device_node *node = dev->of_node;
++      const __be32 *reg;
++      u64 addr;
++
++      /* Construct the name, using parent nodes if necessary to ensure uniqueness */
++      while (node->parent) {
++              /*
++               * If the address can be translated, then that is as much
++               * uniqueness as we need. Make it the first component and return
++               */
++              reg = of_get_property(node, "reg", NULL);
++              if (reg && (addr = of_translate_address(node, reg)) != OF_BAD_ADDR) {
++                      dev_set_name(dev, dev_name(dev) ? "%llx.%pOFn:%s" : "%llx.%pOFn",
++                                   addr, node, dev_name(dev));
++                      return;
++              }
++
++              /* format arguments only used if dev_name() resolves to NULL */
++              dev_set_name(dev, dev_name(dev) ? "%s:%s" : "%s",
++                           kbasename(node->full_name), dev_name(dev));
++              node = node->parent;
++      }
++}
++EXPORT_SYMBOL_GPL(of_device_make_bus_id);
+--- a/drivers/of/platform.c
++++ b/drivers/of/platform.c
+@@ -64,40 +64,6 @@ EXPORT_SYMBOL(of_find_device_by_node);
+  */
+ /**
+- * of_device_make_bus_id - Use the device node data to assign a unique name
+- * @dev: pointer to device structure that is linked to a device tree node
+- *
+- * This routine will first try using the translated bus address to
+- * derive a unique name. If it cannot, then it will prepend names from
+- * parent nodes until a unique name can be derived.
+- */
+-static void of_device_make_bus_id(struct device *dev)
+-{
+-      struct device_node *node = dev->of_node;
+-      const __be32 *reg;
+-      u64 addr;
+-
+-      /* Construct the name, using parent nodes if necessary to ensure uniqueness */
+-      while (node->parent) {
+-              /*
+-               * If the address can be translated, then that is as much
+-               * uniqueness as we need. Make it the first component and return
+-               */
+-              reg = of_get_property(node, "reg", NULL);
+-              if (reg && (addr = of_translate_address(node, reg)) != OF_BAD_ADDR) {
+-                      dev_set_name(dev, dev_name(dev) ? "%llx.%pOFn:%s" : "%llx.%pOFn",
+-                                   addr, node, dev_name(dev));
+-                      return;
+-              }
+-
+-              /* format arguments only used if dev_name() resolves to NULL */
+-              dev_set_name(dev, dev_name(dev) ? "%s:%s" : "%s",
+-                           kbasename(node->full_name), dev_name(dev));
+-              node = node->parent;
+-      }
+-}
+-
+-/**
+  * of_device_alloc - Allocate and initialize an of_device
+  * @np: device node to assign to device
+  * @bus_id: Name to assign to the device.  May be null to use default name.
+--- a/include/linux/of_device.h
++++ b/include/linux/of_device.h
+@@ -56,6 +56,9 @@ static inline int of_dma_configure(struc
+ {
+       return of_dma_configure_id(dev, np, force_dma, NULL);
+ }
++
++void of_device_make_bus_id(struct device *dev);
++
+ #else /* CONFIG_OF */
+ static inline int of_driver_match_device(struct device *dev,
+@@ -113,6 +116,9 @@ static inline int of_dma_configure(struc
+ {
+       return 0;
+ }
++
++static inline void of_device_make_bus_id(struct device *dev) {}
++
+ #endif /* CONFIG_OF */
+ #endif /* _LINUX_OF_DEVICE_H */
diff --git a/target/linux/generic/backport-5.15/834-v6.8-0001-nvmem-Move-of_nvmem_layout_get_container-in-another-.patch b/target/linux/generic/backport-5.15/834-v6.8-0001-nvmem-Move-of_nvmem_layout_get_container-in-another-.patch
new file mode 100644 (file)
index 0000000..2093fac
--- /dev/null
@@ -0,0 +1,95 @@
+From 4a1a40233b4a9fc159a5c7a27dc34c5c7bc5be55 Mon Sep 17 00:00:00 2001
+From: Miquel Raynal <miquel.raynal@bootlin.com>
+Date: Fri, 15 Dec 2023 11:15:28 +0000
+Subject: [PATCH] nvmem: Move of_nvmem_layout_get_container() in another header
+
+nvmem-consumer.h is included by consumer devices, extracting data from
+NVMEM devices whereas nvmem-provider.h is included by devices providing
+NVMEM content.
+
+The only users of of_nvmem_layout_get_container() outside of the core
+are layout drivers, so better move its prototype to nvmem-provider.h.
+
+While we do so, we also move the kdoc associated with the function to
+the header rather than the .c file.
+
+Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20231215111536.316972-3-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/core.c           |  8 --------
+ include/linux/nvmem-consumer.h |  7 -------
+ include/linux/nvmem-provider.h | 21 +++++++++++++++++++++
+ 3 files changed, 21 insertions(+), 15 deletions(-)
+
+--- a/drivers/nvmem/core.c
++++ b/drivers/nvmem/core.c
+@@ -848,14 +848,6 @@ static int nvmem_add_cells_from_layout(s
+ }
+ #if IS_ENABLED(CONFIG_OF)
+-/**
+- * of_nvmem_layout_get_container() - Get OF node to layout container.
+- *
+- * @nvmem: nvmem device.
+- *
+- * Return: a node pointer with refcount incremented or NULL if no
+- * container exists. Use of_node_put() on it when done.
+- */
+ struct device_node *of_nvmem_layout_get_container(struct nvmem_device *nvmem)
+ {
+       return of_get_child_by_name(nvmem->dev.of_node, "nvmem-layout");
+--- a/include/linux/nvmem-consumer.h
++++ b/include/linux/nvmem-consumer.h
+@@ -241,7 +241,6 @@ struct nvmem_cell *of_nvmem_cell_get(str
+                                    const char *id);
+ struct nvmem_device *of_nvmem_device_get(struct device_node *np,
+                                        const char *name);
+-struct device_node *of_nvmem_layout_get_container(struct nvmem_device *nvmem);
+ #else
+ static inline struct nvmem_cell *of_nvmem_cell_get(struct device_node *np,
+                                                  const char *id)
+@@ -254,12 +253,6 @@ static inline struct nvmem_device *of_nv
+ {
+       return ERR_PTR(-EOPNOTSUPP);
+ }
+-
+-static inline struct device_node *
+-of_nvmem_layout_get_container(struct nvmem_device *nvmem)
+-{
+-      return NULL;
+-}
+ #endif /* CONFIG_NVMEM && CONFIG_OF */
+ #endif  /* ifndef _LINUX_NVMEM_CONSUMER_H */
+--- a/include/linux/nvmem-provider.h
++++ b/include/linux/nvmem-provider.h
+@@ -244,6 +244,27 @@ nvmem_layout_get_match_data(struct nvmem
+ #endif /* CONFIG_NVMEM */
++#if IS_ENABLED(CONFIG_NVMEM) && IS_ENABLED(CONFIG_OF)
++
++/**
++ * of_nvmem_layout_get_container() - Get OF node of layout container
++ *
++ * @nvmem: nvmem device
++ *
++ * Return: a node pointer with refcount incremented or NULL if no
++ * container exists. Use of_node_put() on it when done.
++ */
++struct device_node *of_nvmem_layout_get_container(struct nvmem_device *nvmem);
++
++#else  /* CONFIG_NVMEM && CONFIG_OF */
++
++static inline struct device_node *of_nvmem_layout_get_container(struct nvmem_device *nvmem)
++{
++      return NULL;
++}
++
++#endif /* CONFIG_NVMEM && CONFIG_OF */
++
+ #define module_nvmem_layout_driver(__layout_driver)           \
+       module_driver(__layout_driver, nvmem_layout_register,   \
+                     nvmem_layout_unregister)
diff --git a/target/linux/generic/backport-5.15/834-v6.8-0002-nvmem-Create-a-header-for-internal-sharing.patch b/target/linux/generic/backport-5.15/834-v6.8-0002-nvmem-Create-a-header-for-internal-sharing.patch
new file mode 100644 (file)
index 0000000..e722109
--- /dev/null
@@ -0,0 +1,91 @@
+From ec9c08a1cb8dc5e8e003f95f5f62de41dde235bb Mon Sep 17 00:00:00 2001
+From: Miquel Raynal <miquel.raynal@bootlin.com>
+Date: Fri, 15 Dec 2023 11:15:29 +0000
+Subject: [PATCH] nvmem: Create a header for internal sharing
+
+Before adding all the NVMEM layout bus infrastructure to the core, let's
+move the main nvmem_device structure in an internal header, only
+available to the core. This way all the additional code can be added in
+a dedicated file in order to keep the current core file tidy.
+
+Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20231215111536.316972-4-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/core.c      | 24 +-----------------------
+ drivers/nvmem/internals.h | 35 +++++++++++++++++++++++++++++++++++
+ 2 files changed, 36 insertions(+), 23 deletions(-)
+ create mode 100644 drivers/nvmem/internals.h
+
+--- a/drivers/nvmem/core.c
++++ b/drivers/nvmem/core.c
+@@ -20,29 +20,7 @@
+ #include <linux/of_device.h>
+ #include <linux/slab.h>
+-struct nvmem_device {
+-      struct module           *owner;
+-      struct device           dev;
+-      int                     stride;
+-      int                     word_size;
+-      int                     id;
+-      struct kref             refcnt;
+-      size_t                  size;
+-      bool                    read_only;
+-      bool                    root_only;
+-      int                     flags;
+-      enum nvmem_type         type;
+-      struct bin_attribute    eeprom;
+-      struct device           *base_dev;
+-      struct list_head        cells;
+-      const struct nvmem_keepout *keepout;
+-      unsigned int            nkeepout;
+-      nvmem_reg_read_t        reg_read;
+-      nvmem_reg_write_t       reg_write;
+-      struct gpio_desc        *wp_gpio;
+-      struct nvmem_layout     *layout;
+-      void *priv;
+-};
++#include "internals.h"
+ #define to_nvmem_device(d) container_of(d, struct nvmem_device, dev)
+--- /dev/null
++++ b/drivers/nvmem/internals.h
+@@ -0,0 +1,35 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++
++#ifndef _LINUX_NVMEM_INTERNALS_H
++#define _LINUX_NVMEM_INTERNALS_H
++
++#include <linux/device.h>
++#include <linux/nvmem-consumer.h>
++#include <linux/nvmem-provider.h>
++
++struct nvmem_device {
++      struct module           *owner;
++      struct device           dev;
++      struct list_head        node;
++      int                     stride;
++      int                     word_size;
++      int                     id;
++      struct kref             refcnt;
++      size_t                  size;
++      bool                    read_only;
++      bool                    root_only;
++      int                     flags;
++      enum nvmem_type         type;
++      struct bin_attribute    eeprom;
++      struct device           *base_dev;
++      struct list_head        cells;
++      const struct nvmem_keepout *keepout;
++      unsigned int            nkeepout;
++      nvmem_reg_read_t        reg_read;
++      nvmem_reg_write_t       reg_write;
++      struct gpio_desc        *wp_gpio;
++      struct nvmem_layout     *layout;
++      void *priv;
++};
++
++#endif  /* ifndef _LINUX_NVMEM_INTERNALS_H */
diff --git a/target/linux/generic/backport-5.15/834-v6.8-0003-nvmem-Simplify-the-add_cells-hook.patch b/target/linux/generic/backport-5.15/834-v6.8-0003-nvmem-Simplify-the-add_cells-hook.patch
new file mode 100644 (file)
index 0000000..db2d8c1
--- /dev/null
@@ -0,0 +1,79 @@
+From 1b7c298a4ecbc28cc6ee94005734bff55eb83d22 Mon Sep 17 00:00:00 2001
+From: Miquel Raynal <miquel.raynal@bootlin.com>
+Date: Fri, 15 Dec 2023 11:15:30 +0000
+Subject: [PATCH] nvmem: Simplify the ->add_cells() hook
+
+The layout entry is not used and will anyway be made useless by the new
+layout bus infrastructure coming next, so drop it. While at it, clarify
+the kdoc entry.
+
+Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20231215111536.316972-5-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/core.c             | 2 +-
+ drivers/nvmem/layouts/onie-tlv.c | 3 +--
+ drivers/nvmem/layouts/sl28vpd.c  | 3 +--
+ include/linux/nvmem-provider.h   | 8 +++-----
+ 4 files changed, 6 insertions(+), 10 deletions(-)
+
+--- a/drivers/nvmem/core.c
++++ b/drivers/nvmem/core.c
+@@ -817,7 +817,7 @@ static int nvmem_add_cells_from_layout(s
+       int ret;
+       if (layout && layout->add_cells) {
+-              ret = layout->add_cells(&nvmem->dev, nvmem, layout);
++              ret = layout->add_cells(&nvmem->dev, nvmem);
+               if (ret)
+                       return ret;
+       }
+--- a/drivers/nvmem/layouts/onie-tlv.c
++++ b/drivers/nvmem/layouts/onie-tlv.c
+@@ -182,8 +182,7 @@ static bool onie_tlv_crc_is_valid(struct
+       return true;
+ }
+-static int onie_tlv_parse_table(struct device *dev, struct nvmem_device *nvmem,
+-                              struct nvmem_layout *layout)
++static int onie_tlv_parse_table(struct device *dev, struct nvmem_device *nvmem)
+ {
+       struct onie_tlv_hdr hdr;
+       size_t table_len, data_len, hdr_len;
+--- a/drivers/nvmem/layouts/sl28vpd.c
++++ b/drivers/nvmem/layouts/sl28vpd.c
+@@ -80,8 +80,7 @@ static int sl28vpd_v1_check_crc(struct d
+       return 0;
+ }
+-static int sl28vpd_add_cells(struct device *dev, struct nvmem_device *nvmem,
+-                           struct nvmem_layout *layout)
++static int sl28vpd_add_cells(struct device *dev, struct nvmem_device *nvmem)
+ {
+       const struct nvmem_cell_info *pinfo;
+       struct nvmem_cell_info info = {0};
+--- a/include/linux/nvmem-provider.h
++++ b/include/linux/nvmem-provider.h
+@@ -156,9 +156,8 @@ struct nvmem_cell_table {
+  *
+  * @name:             Layout name.
+  * @of_match_table:   Open firmware match table.
+- * @add_cells:                Will be called if a nvmem device is found which
+- *                    has this layout. The function will add layout
+- *                    specific cells with nvmem_add_one_cell().
++ * @add_cells:                Called to populate the layout using
++ *                    nvmem_add_one_cell().
+  * @fixup_cell_info:  Will be called before a cell is added. Can be
+  *                    used to modify the nvmem_cell_info.
+  * @owner:            Pointer to struct module.
+@@ -172,8 +171,7 @@ struct nvmem_cell_table {
+ struct nvmem_layout {
+       const char *name;
+       const struct of_device_id *of_match_table;
+-      int (*add_cells)(struct device *dev, struct nvmem_device *nvmem,
+-                       struct nvmem_layout *layout);
++      int (*add_cells)(struct device *dev, struct nvmem_device *nvmem);
+       void (*fixup_cell_info)(struct nvmem_device *nvmem,
+                               struct nvmem_layout *layout,
+                               struct nvmem_cell_info *cell);
diff --git a/target/linux/generic/backport-5.15/834-v6.8-0004-nvmem-Move-and-rename-fixup_cell_info.patch b/target/linux/generic/backport-5.15/834-v6.8-0004-nvmem-Move-and-rename-fixup_cell_info.patch
new file mode 100644 (file)
index 0000000..65aa37f
--- /dev/null
@@ -0,0 +1,169 @@
+From 1172460e716784ac7e1049a537bdca8edbf97360 Mon Sep 17 00:00:00 2001
+From: Miquel Raynal <miquel.raynal@bootlin.com>
+Date: Fri, 15 Dec 2023 11:15:31 +0000
+Subject: [PATCH] nvmem: Move and rename ->fixup_cell_info()
+
+This hook is meant to be used by any provider and instantiating a layout
+just for this is useless. Let's instead move this hook to the nvmem
+device and add it to the config structure to be easily shared by the
+providers.
+
+While at moving this hook, rename it ->fixup_dt_cell_info() to clarify
+its main intended purpose.
+
+Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20231215111536.316972-6-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/core.c           |  6 +++---
+ drivers/nvmem/imx-ocotp.c      | 11 +++--------
+ drivers/nvmem/internals.h      |  2 ++
+ drivers/nvmem/mtk-efuse.c      | 11 +++--------
+ include/linux/nvmem-provider.h |  9 ++++-----
+ 5 files changed, 15 insertions(+), 24 deletions(-)
+
+--- a/drivers/nvmem/core.c
++++ b/drivers/nvmem/core.c
+@@ -676,7 +676,6 @@ static int nvmem_validate_keepouts(struc
+ static int nvmem_add_cells_from_dt(struct nvmem_device *nvmem, struct device_node *np)
+ {
+-      struct nvmem_layout *layout = nvmem->layout;
+       struct device *dev = &nvmem->dev;
+       struct device_node *child;
+       const __be32 *addr;
+@@ -706,8 +705,8 @@ static int nvmem_add_cells_from_dt(struc
+               info.np = of_node_get(child);
+-              if (layout && layout->fixup_cell_info)
+-                      layout->fixup_cell_info(nvmem, layout, &info);
++              if (nvmem->fixup_dt_cell_info)
++                      nvmem->fixup_dt_cell_info(nvmem, &info);
+               ret = nvmem_add_one_cell(nvmem, &info);
+               kfree(info.name);
+@@ -896,6 +895,7 @@ struct nvmem_device *nvmem_register(cons
+       kref_init(&nvmem->refcnt);
+       INIT_LIST_HEAD(&nvmem->cells);
++      nvmem->fixup_dt_cell_info = config->fixup_dt_cell_info;
+       nvmem->owner = config->owner;
+       if (!nvmem->owner && config->dev->driver)
+--- a/drivers/nvmem/imx-ocotp.c
++++ b/drivers/nvmem/imx-ocotp.c
+@@ -584,17 +584,12 @@ static const struct of_device_id imx_oco
+ };
+ MODULE_DEVICE_TABLE(of, imx_ocotp_dt_ids);
+-static void imx_ocotp_fixup_cell_info(struct nvmem_device *nvmem,
+-                                    struct nvmem_layout *layout,
+-                                    struct nvmem_cell_info *cell)
++static void imx_ocotp_fixup_dt_cell_info(struct nvmem_device *nvmem,
++                                       struct nvmem_cell_info *cell)
+ {
+       cell->read_post_process = imx_ocotp_cell_pp;
+ }
+-static struct nvmem_layout imx_ocotp_layout = {
+-      .fixup_cell_info = imx_ocotp_fixup_cell_info,
+-};
+-
+ static int imx_ocotp_probe(struct platform_device *pdev)
+ {
+       struct device *dev = &pdev->dev;
+@@ -620,7 +615,7 @@ static int imx_ocotp_probe(struct platfo
+       imx_ocotp_nvmem_config.size = 4 * priv->params->nregs;
+       imx_ocotp_nvmem_config.dev = dev;
+       imx_ocotp_nvmem_config.priv = priv;
+-      imx_ocotp_nvmem_config.layout = &imx_ocotp_layout;
++      imx_ocotp_nvmem_config.fixup_dt_cell_info = &imx_ocotp_fixup_dt_cell_info;
+       priv->config = &imx_ocotp_nvmem_config;
+--- a/drivers/nvmem/internals.h
++++ b/drivers/nvmem/internals.h
+@@ -23,6 +23,8 @@ struct nvmem_device {
+       struct bin_attribute    eeprom;
+       struct device           *base_dev;
+       struct list_head        cells;
++      void (*fixup_dt_cell_info)(struct nvmem_device *nvmem,
++                                 struct nvmem_cell_info *cell);
+       const struct nvmem_keepout *keepout;
+       unsigned int            nkeepout;
+       nvmem_reg_read_t        reg_read;
+--- a/drivers/nvmem/mtk-efuse.c
++++ b/drivers/nvmem/mtk-efuse.c
+@@ -45,9 +45,8 @@ static int mtk_efuse_gpu_speedbin_pp(voi
+       return 0;
+ }
+-static void mtk_efuse_fixup_cell_info(struct nvmem_device *nvmem,
+-                                    struct nvmem_layout *layout,
+-                                    struct nvmem_cell_info *cell)
++static void mtk_efuse_fixup_dt_cell_info(struct nvmem_device *nvmem,
++                                       struct nvmem_cell_info *cell)
+ {
+       size_t sz = strlen(cell->name);
+@@ -61,10 +60,6 @@ static void mtk_efuse_fixup_cell_info(st
+               cell->read_post_process = mtk_efuse_gpu_speedbin_pp;
+ }
+-static struct nvmem_layout mtk_efuse_layout = {
+-      .fixup_cell_info = mtk_efuse_fixup_cell_info,
+-};
+-
+ static int mtk_efuse_probe(struct platform_device *pdev)
+ {
+       struct device *dev = &pdev->dev;
+@@ -91,7 +86,7 @@ static int mtk_efuse_probe(struct platfo
+       econfig.priv = priv;
+       econfig.dev = dev;
+       if (pdata->uses_post_processing)
+-              econfig.layout = &mtk_efuse_layout;
++              econfig.fixup_dt_cell_info = &mtk_efuse_fixup_dt_cell_info;
+       nvmem = devm_nvmem_register(dev, &econfig);
+       return PTR_ERR_OR_ZERO(nvmem);
+--- a/include/linux/nvmem-provider.h
++++ b/include/linux/nvmem-provider.h
+@@ -83,6 +83,8 @@ struct nvmem_cell_info {
+  * @cells:    Optional array of pre-defined NVMEM cells.
+  * @ncells:   Number of elements in cells.
+  * @add_legacy_fixed_of_cells:        Read fixed NVMEM cells from old OF syntax.
++ * @fixup_dt_cell_info: Will be called before a cell is added. Can be
++ *            used to modify the nvmem_cell_info.
+  * @keepout:  Optional array of keepout ranges (sorted ascending by start).
+  * @nkeepout: Number of elements in the keepout array.
+  * @type:     Type of the nvmem storage
+@@ -113,6 +115,8 @@ struct nvmem_config {
+       const struct nvmem_cell_info    *cells;
+       int                     ncells;
+       bool                    add_legacy_fixed_of_cells;
++      void (*fixup_dt_cell_info)(struct nvmem_device *nvmem,
++                                 struct nvmem_cell_info *cell);
+       const struct nvmem_keepout *keepout;
+       unsigned int            nkeepout;
+       enum nvmem_type         type;
+@@ -158,8 +162,6 @@ struct nvmem_cell_table {
+  * @of_match_table:   Open firmware match table.
+  * @add_cells:                Called to populate the layout using
+  *                    nvmem_add_one_cell().
+- * @fixup_cell_info:  Will be called before a cell is added. Can be
+- *                    used to modify the nvmem_cell_info.
+  * @owner:            Pointer to struct module.
+  * @node:             List node.
+  *
+@@ -172,9 +174,6 @@ struct nvmem_layout {
+       const char *name;
+       const struct of_device_id *of_match_table;
+       int (*add_cells)(struct device *dev, struct nvmem_device *nvmem);
+-      void (*fixup_cell_info)(struct nvmem_device *nvmem,
+-                              struct nvmem_layout *layout,
+-                              struct nvmem_cell_info *cell);
+       /* private */
+       struct module *owner;
diff --git a/target/linux/generic/backport-5.15/834-v6.8-0005-nvmem-core-Rework-layouts-to-become-regular-devices.patch b/target/linux/generic/backport-5.15/834-v6.8-0005-nvmem-core-Rework-layouts-to-become-regular-devices.patch
new file mode 100644 (file)
index 0000000..1881332
--- /dev/null
@@ -0,0 +1,763 @@
+From fc29fd821d9ac2ae3d32a722fac39ce874efb883 Mon Sep 17 00:00:00 2001
+From: Miquel Raynal <miquel.raynal@bootlin.com>
+Date: Fri, 15 Dec 2023 11:15:32 +0000
+Subject: [PATCH] nvmem: core: Rework layouts to become regular devices
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Current layout support was initially written without modules support in
+mind. When the requirement for module support rose, the existing base
+was improved to adopt modularization support, but kind of a design flaw
+was introduced. With the existing implementation, when a storage device
+registers into NVMEM, the core tries to hook a layout (if any) and
+populates its cells immediately. This means, if the hardware description
+expects a layout to be hooked up, but no driver was provided for that,
+the storage medium will fail to probe and try later from
+scratch. Even if we consider that the hardware description shall be
+correct, we could still probe the storage device (especially if it
+contains the rootfs).
+
+One way to overcome this situation is to consider the layouts as
+devices, and leverage the native notifier mechanism. When a new NVMEM
+device is registered, we can populate its nvmem-layout child, if any,
+and wait for the matching to be done in order to get the cells (the
+waiting can be easily done with the NVMEM notifiers). If the layout
+driver is compiled as a module, it should automatically be loaded. This
+way, there is no strong order to enforce, any NVMEM device creation
+or NVMEM layout driver insertion will be observed as a new event which
+may lead to the creation of additional cells, without disturbing the
+probes with costly (and sometimes endless) deferrals.
+
+In order to achieve that goal we create a new bus for the nvmem-layouts
+with minimal logic to match nvmem-layout devices with nvmem-layout
+drivers. All this infrastructure code is created in the layouts.c file.
+
+Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
+Tested-by: Rafał Miłecki <rafal@milecki.pl>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20231215111536.316972-7-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/Kconfig            |   1 +
+ drivers/nvmem/Makefile           |   2 +
+ drivers/nvmem/core.c             | 170 ++++++++++----------------
+ drivers/nvmem/internals.h        |  21 ++++
+ drivers/nvmem/layouts.c          | 201 +++++++++++++++++++++++++++++++
+ drivers/nvmem/layouts/Kconfig    |   8 ++
+ drivers/nvmem/layouts/onie-tlv.c |  24 +++-
+ drivers/nvmem/layouts/sl28vpd.c  |  24 +++-
+ include/linux/nvmem-provider.h   |  38 +++---
+ 9 files changed, 354 insertions(+), 135 deletions(-)
+ create mode 100644 drivers/nvmem/layouts.c
+
+--- a/drivers/nvmem/Kconfig
++++ b/drivers/nvmem/Kconfig
+@@ -1,6 +1,7 @@
+ # SPDX-License-Identifier: GPL-2.0-only
+ menuconfig NVMEM
+       bool "NVMEM Support"
++      imply NVMEM_LAYOUTS
+       help
+         Support for NVMEM(Non Volatile Memory) devices like EEPROM, EFUSES...
+--- a/drivers/nvmem/Makefile
++++ b/drivers/nvmem/Makefile
+@@ -5,6 +5,8 @@
+ obj-$(CONFIG_NVMEM)           += nvmem_core.o
+ nvmem_core-y                  := core.o
++obj-$(CONFIG_NVMEM_LAYOUTS)   += nvmem_layouts.o
++nvmem_layouts-y                       := layouts.o
+ obj-y                         += layouts/
+ # Devices
+--- a/drivers/nvmem/core.c
++++ b/drivers/nvmem/core.c
+@@ -56,9 +56,6 @@ static LIST_HEAD(nvmem_lookup_list);
+ static BLOCKING_NOTIFIER_HEAD(nvmem_notifier);
+-static DEFINE_SPINLOCK(nvmem_layout_lock);
+-static LIST_HEAD(nvmem_layouts);
+-
+ static int __nvmem_reg_read(struct nvmem_device *nvmem, unsigned int offset,
+                           void *val, size_t bytes)
+ {
+@@ -741,97 +738,22 @@ static int nvmem_add_cells_from_fixed_la
+       return err;
+ }
+-int __nvmem_layout_register(struct nvmem_layout *layout, struct module *owner)
++int nvmem_layout_register(struct nvmem_layout *layout)
+ {
+-      layout->owner = owner;
+-
+-      spin_lock(&nvmem_layout_lock);
+-      list_add(&layout->node, &nvmem_layouts);
+-      spin_unlock(&nvmem_layout_lock);
+-
+-      blocking_notifier_call_chain(&nvmem_notifier, NVMEM_LAYOUT_ADD, layout);
++      if (!layout->add_cells)
++              return -EINVAL;
+-      return 0;
++      /* Populate the cells */
++      return layout->add_cells(&layout->nvmem->dev, layout->nvmem);
+ }
+-EXPORT_SYMBOL_GPL(__nvmem_layout_register);
++EXPORT_SYMBOL_GPL(nvmem_layout_register);
+ void nvmem_layout_unregister(struct nvmem_layout *layout)
+ {
+-      blocking_notifier_call_chain(&nvmem_notifier, NVMEM_LAYOUT_REMOVE, layout);
+-
+-      spin_lock(&nvmem_layout_lock);
+-      list_del(&layout->node);
+-      spin_unlock(&nvmem_layout_lock);
++      /* Keep the API even with an empty stub in case we need it later */
+ }
+ EXPORT_SYMBOL_GPL(nvmem_layout_unregister);
+-static struct nvmem_layout *nvmem_layout_get(struct nvmem_device *nvmem)
+-{
+-      struct device_node *layout_np;
+-      struct nvmem_layout *l, *layout = ERR_PTR(-EPROBE_DEFER);
+-
+-      layout_np = of_nvmem_layout_get_container(nvmem);
+-      if (!layout_np)
+-              return NULL;
+-
+-      /* Fixed layouts don't have a matching driver */
+-      if (of_device_is_compatible(layout_np, "fixed-layout")) {
+-              of_node_put(layout_np);
+-              return NULL;
+-      }
+-
+-      /*
+-       * In case the nvmem device was built-in while the layout was built as a
+-       * module, we shall manually request the layout driver loading otherwise
+-       * we'll never have any match.
+-       */
+-      of_request_module(layout_np);
+-
+-      spin_lock(&nvmem_layout_lock);
+-
+-      list_for_each_entry(l, &nvmem_layouts, node) {
+-              if (of_match_node(l->of_match_table, layout_np)) {
+-                      if (try_module_get(l->owner))
+-                              layout = l;
+-
+-                      break;
+-              }
+-      }
+-
+-      spin_unlock(&nvmem_layout_lock);
+-      of_node_put(layout_np);
+-
+-      return layout;
+-}
+-
+-static void nvmem_layout_put(struct nvmem_layout *layout)
+-{
+-      if (layout)
+-              module_put(layout->owner);
+-}
+-
+-static int nvmem_add_cells_from_layout(struct nvmem_device *nvmem)
+-{
+-      struct nvmem_layout *layout = nvmem->layout;
+-      int ret;
+-
+-      if (layout && layout->add_cells) {
+-              ret = layout->add_cells(&nvmem->dev, nvmem);
+-              if (ret)
+-                      return ret;
+-      }
+-
+-      return 0;
+-}
+-
+-#if IS_ENABLED(CONFIG_OF)
+-struct device_node *of_nvmem_layout_get_container(struct nvmem_device *nvmem)
+-{
+-      return of_get_child_by_name(nvmem->dev.of_node, "nvmem-layout");
+-}
+-EXPORT_SYMBOL_GPL(of_nvmem_layout_get_container);
+-#endif
+-
+ const void *nvmem_layout_get_match_data(struct nvmem_device *nvmem,
+                                       struct nvmem_layout *layout)
+ {
+@@ -839,7 +761,7 @@ const void *nvmem_layout_get_match_data(
+       const struct of_device_id *match;
+       layout_np = of_nvmem_layout_get_container(nvmem);
+-      match = of_match_node(layout->of_match_table, layout_np);
++      match = of_match_node(layout->dev.driver->of_match_table, layout_np);
+       return match ? match->data : NULL;
+ }
+@@ -951,19 +873,6 @@ struct nvmem_device *nvmem_register(cons
+                       goto err_put_device;
+       }
+-      /*
+-       * If the driver supplied a layout by config->layout, the module
+-       * pointer will be NULL and nvmem_layout_put() will be a noop.
+-       */
+-      nvmem->layout = config->layout ?: nvmem_layout_get(nvmem);
+-      if (IS_ERR(nvmem->layout)) {
+-              rval = PTR_ERR(nvmem->layout);
+-              nvmem->layout = NULL;
+-
+-              if (rval == -EPROBE_DEFER)
+-                      goto err_teardown_compat;
+-      }
+-
+       if (config->cells) {
+               rval = nvmem_add_cells(nvmem, config->cells, config->ncells);
+               if (rval)
+@@ -984,24 +893,24 @@ struct nvmem_device *nvmem_register(cons
+       if (rval)
+               goto err_remove_cells;
+-      rval = nvmem_add_cells_from_layout(nvmem);
+-      if (rval)
+-              goto err_remove_cells;
+-
+       dev_dbg(&nvmem->dev, "Registering nvmem device %s\n", config->name);
+       rval = device_add(&nvmem->dev);
+       if (rval)
+               goto err_remove_cells;
++      rval = nvmem_populate_layout(nvmem);
++      if (rval)
++              goto err_remove_dev;
++
+       blocking_notifier_call_chain(&nvmem_notifier, NVMEM_ADD, nvmem);
+       return nvmem;
++err_remove_dev:
++      device_del(&nvmem->dev);
+ err_remove_cells:
+       nvmem_device_remove_all_cells(nvmem);
+-      nvmem_layout_put(nvmem->layout);
+-err_teardown_compat:
+       if (config->compat)
+               nvmem_sysfs_remove_compat(nvmem, config);
+ err_put_device:
+@@ -1023,7 +932,7 @@ static void nvmem_device_release(struct
+               device_remove_bin_file(nvmem->base_dev, &nvmem->eeprom);
+       nvmem_device_remove_all_cells(nvmem);
+-      nvmem_layout_put(nvmem->layout);
++      nvmem_destroy_layout(nvmem);
+       device_unregister(&nvmem->dev);
+ }
+@@ -1325,6 +1234,12 @@ nvmem_cell_get_from_lookup(struct device
+       return cell;
+ }
++static void nvmem_layout_module_put(struct nvmem_device *nvmem)
++{
++      if (nvmem->layout && nvmem->layout->dev.driver)
++              module_put(nvmem->layout->dev.driver->owner);
++}
++
+ #if IS_ENABLED(CONFIG_OF)
+ static struct nvmem_cell_entry *
+ nvmem_find_cell_entry_by_node(struct nvmem_device *nvmem, struct device_node *np)
+@@ -1343,6 +1258,18 @@ nvmem_find_cell_entry_by_node(struct nvm
+       return cell;
+ }
++static int nvmem_layout_module_get_optional(struct nvmem_device *nvmem)
++{
++      if (!nvmem->layout)
++              return 0;
++
++      if (!nvmem->layout->dev.driver ||
++          !try_module_get(nvmem->layout->dev.driver->owner))
++              return -EPROBE_DEFER;
++
++      return 0;
++}
++
+ /**
+  * of_nvmem_cell_get() - Get a nvmem cell from given device node and cell id
+  *
+@@ -1405,16 +1332,29 @@ struct nvmem_cell *of_nvmem_cell_get(str
+               return ERR_CAST(nvmem);
+       }
++      ret = nvmem_layout_module_get_optional(nvmem);
++      if (ret) {
++              of_node_put(cell_np);
++              __nvmem_device_put(nvmem);
++              return ERR_PTR(ret);
++      }
++
+       cell_entry = nvmem_find_cell_entry_by_node(nvmem, cell_np);
+       of_node_put(cell_np);
+       if (!cell_entry) {
+               __nvmem_device_put(nvmem);
+-              return ERR_PTR(-ENOENT);
++              nvmem_layout_module_put(nvmem);
++              if (nvmem->layout)
++                      return ERR_PTR(-EPROBE_DEFER);
++              else
++                      return ERR_PTR(-ENOENT);
+       }
+       cell = nvmem_create_cell(cell_entry, id, cell_index);
+-      if (IS_ERR(cell))
++      if (IS_ERR(cell)) {
+               __nvmem_device_put(nvmem);
++              nvmem_layout_module_put(nvmem);
++      }
+       return cell;
+ }
+@@ -1528,6 +1468,7 @@ void nvmem_cell_put(struct nvmem_cell *c
+       kfree(cell);
+       __nvmem_device_put(nvmem);
++      nvmem_layout_module_put(nvmem);
+ }
+ EXPORT_SYMBOL_GPL(nvmem_cell_put);
+@@ -2105,11 +2046,22 @@ EXPORT_SYMBOL_GPL(nvmem_dev_name);
+ static int __init nvmem_init(void)
+ {
+-      return bus_register(&nvmem_bus_type);
++      int ret;
++
++      ret = bus_register(&nvmem_bus_type);
++      if (ret)
++              return ret;
++
++      ret = nvmem_layout_bus_register();
++      if (ret)
++              bus_unregister(&nvmem_bus_type);
++
++      return ret;
+ }
+ static void __exit nvmem_exit(void)
+ {
++      nvmem_layout_bus_unregister();
+       bus_unregister(&nvmem_bus_type);
+ }
+--- a/drivers/nvmem/internals.h
++++ b/drivers/nvmem/internals.h
+@@ -34,4 +34,25 @@ struct nvmem_device {
+       void *priv;
+ };
++#if IS_ENABLED(CONFIG_OF)
++int nvmem_layout_bus_register(void);
++void nvmem_layout_bus_unregister(void);
++int nvmem_populate_layout(struct nvmem_device *nvmem);
++void nvmem_destroy_layout(struct nvmem_device *nvmem);
++#else /* CONFIG_OF */
++static inline int nvmem_layout_bus_register(void)
++{
++      return 0;
++}
++
++static inline void nvmem_layout_bus_unregister(void) {}
++
++static inline int nvmem_populate_layout(struct nvmem_device *nvmem)
++{
++      return 0;
++}
++
++static inline void nvmem_destroy_layout(struct nvmem_device *nvmem) { }
++#endif /* CONFIG_OF */
++
+ #endif  /* ifndef _LINUX_NVMEM_INTERNALS_H */
+--- /dev/null
++++ b/drivers/nvmem/layouts.c
+@@ -0,0 +1,201 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * NVMEM layout bus handling
++ *
++ * Copyright (C) 2023 Bootlin
++ * Author: Miquel Raynal <miquel.raynal@bootlin.com
++ */
++
++#include <linux/device.h>
++#include <linux/dma-mapping.h>
++#include <linux/nvmem-consumer.h>
++#include <linux/nvmem-provider.h>
++#include <linux/of.h>
++#include <linux/of_device.h>
++#include <linux/of_irq.h>
++
++#include "internals.h"
++
++#define to_nvmem_layout_driver(drv) \
++      (container_of((drv), struct nvmem_layout_driver, driver))
++#define to_nvmem_layout_device(_dev) \
++      container_of((_dev), struct nvmem_layout, dev)
++
++static int nvmem_layout_bus_match(struct device *dev, struct device_driver *drv)
++{
++      return of_driver_match_device(dev, drv);
++}
++
++static int nvmem_layout_bus_probe(struct device *dev)
++{
++      struct nvmem_layout_driver *drv = to_nvmem_layout_driver(dev->driver);
++      struct nvmem_layout *layout = to_nvmem_layout_device(dev);
++
++      if (!drv->probe || !drv->remove)
++              return -EINVAL;
++
++      return drv->probe(layout);
++}
++
++static void nvmem_layout_bus_remove(struct device *dev)
++{
++      struct nvmem_layout_driver *drv = to_nvmem_layout_driver(dev->driver);
++      struct nvmem_layout *layout = to_nvmem_layout_device(dev);
++
++      return drv->remove(layout);
++}
++
++static struct bus_type nvmem_layout_bus_type = {
++      .name           = "nvmem-layout",
++      .match          = nvmem_layout_bus_match,
++      .probe          = nvmem_layout_bus_probe,
++      .remove         = nvmem_layout_bus_remove,
++};
++
++int nvmem_layout_driver_register(struct nvmem_layout_driver *drv)
++{
++      drv->driver.bus = &nvmem_layout_bus_type;
++
++      return driver_register(&drv->driver);
++}
++EXPORT_SYMBOL_GPL(nvmem_layout_driver_register);
++
++void nvmem_layout_driver_unregister(struct nvmem_layout_driver *drv)
++{
++      driver_unregister(&drv->driver);
++}
++EXPORT_SYMBOL_GPL(nvmem_layout_driver_unregister);
++
++static void nvmem_layout_release_device(struct device *dev)
++{
++      struct nvmem_layout *layout = to_nvmem_layout_device(dev);
++
++      of_node_put(layout->dev.of_node);
++      kfree(layout);
++}
++
++static int nvmem_layout_create_device(struct nvmem_device *nvmem,
++                                    struct device_node *np)
++{
++      struct nvmem_layout *layout;
++      struct device *dev;
++      int ret;
++
++      layout = kzalloc(sizeof(*layout), GFP_KERNEL);
++      if (!layout)
++              return -ENOMEM;
++
++      /* Create a bidirectional link */
++      layout->nvmem = nvmem;
++      nvmem->layout = layout;
++
++      /* Device model registration */
++      dev = &layout->dev;
++      device_initialize(dev);
++      dev->parent = &nvmem->dev;
++      dev->bus = &nvmem_layout_bus_type;
++      dev->release = nvmem_layout_release_device;
++      dev->coherent_dma_mask = DMA_BIT_MASK(32);
++      dev->dma_mask = &dev->coherent_dma_mask;
++      device_set_node(dev, of_fwnode_handle(of_node_get(np)));
++      of_device_make_bus_id(dev);
++      of_msi_configure(dev, dev->of_node);
++
++      ret = device_add(dev);
++      if (ret) {
++              put_device(dev);
++              return ret;
++      }
++
++      return 0;
++}
++
++static const struct of_device_id of_nvmem_layout_skip_table[] = {
++      { .compatible = "fixed-layout", },
++      {}
++};
++
++static int nvmem_layout_bus_populate(struct nvmem_device *nvmem,
++                                   struct device_node *layout_dn)
++{
++      int ret;
++
++      /* Make sure it has a compatible property */
++      if (!of_get_property(layout_dn, "compatible", NULL)) {
++              pr_debug("%s() - skipping %pOF, no compatible prop\n",
++                       __func__, layout_dn);
++              return 0;
++      }
++
++      /* Fixed layouts are parsed manually somewhere else for now */
++      if (of_match_node(of_nvmem_layout_skip_table, layout_dn)) {
++              pr_debug("%s() - skipping %pOF node\n", __func__, layout_dn);
++              return 0;
++      }
++
++      if (of_node_check_flag(layout_dn, OF_POPULATED_BUS)) {
++              pr_debug("%s() - skipping %pOF, already populated\n",
++                       __func__, layout_dn);
++
++              return 0;
++      }
++
++      /* NVMEM layout buses expect only a single device representing the layout */
++      ret = nvmem_layout_create_device(nvmem, layout_dn);
++      if (ret)
++              return ret;
++
++      of_node_set_flag(layout_dn, OF_POPULATED_BUS);
++
++      return 0;
++}
++
++struct device_node *of_nvmem_layout_get_container(struct nvmem_device *nvmem)
++{
++      return of_get_child_by_name(nvmem->dev.of_node, "nvmem-layout");
++}
++EXPORT_SYMBOL_GPL(of_nvmem_layout_get_container);
++
++/*
++ * Returns the number of devices populated, 0 if the operation was not relevant
++ * for this nvmem device, an error code otherwise.
++ */
++int nvmem_populate_layout(struct nvmem_device *nvmem)
++{
++      struct device_node *layout_dn;
++      int ret;
++
++      layout_dn = of_nvmem_layout_get_container(nvmem);
++      if (!layout_dn)
++              return 0;
++
++      /* Populate the layout device */
++      device_links_supplier_sync_state_pause();
++      ret = nvmem_layout_bus_populate(nvmem, layout_dn);
++      device_links_supplier_sync_state_resume();
++
++      of_node_put(layout_dn);
++      return ret;
++}
++
++void nvmem_destroy_layout(struct nvmem_device *nvmem)
++{
++      struct device *dev;
++
++      if (!nvmem->layout)
++              return;
++
++      dev = &nvmem->layout->dev;
++      of_node_clear_flag(dev->of_node, OF_POPULATED_BUS);
++      device_unregister(dev);
++}
++
++int nvmem_layout_bus_register(void)
++{
++      return bus_register(&nvmem_layout_bus_type);
++}
++
++void nvmem_layout_bus_unregister(void)
++{
++      bus_unregister(&nvmem_layout_bus_type);
++}
+--- a/drivers/nvmem/layouts/Kconfig
++++ b/drivers/nvmem/layouts/Kconfig
+@@ -1,5 +1,11 @@
+ # SPDX-License-Identifier: GPL-2.0
++config NVMEM_LAYOUTS
++      bool
++      depends on OF
++
++if NVMEM_LAYOUTS
++
+ menu "Layout Types"
+ config NVMEM_LAYOUT_SL28_VPD
+@@ -21,3 +27,5 @@ config NVMEM_LAYOUT_ONIE_TLV
+         If unsure, say N.
+ endmenu
++
++endif
+--- a/drivers/nvmem/layouts/onie-tlv.c
++++ b/drivers/nvmem/layouts/onie-tlv.c
+@@ -225,16 +225,32 @@ static int onie_tlv_parse_table(struct d
+       return 0;
+ }
++static int onie_tlv_probe(struct nvmem_layout *layout)
++{
++      layout->add_cells = onie_tlv_parse_table;
++
++      return nvmem_layout_register(layout);
++}
++
++static void onie_tlv_remove(struct nvmem_layout *layout)
++{
++      nvmem_layout_unregister(layout);
++}
++
+ static const struct of_device_id onie_tlv_of_match_table[] = {
+       { .compatible = "onie,tlv-layout", },
+       {},
+ };
+ MODULE_DEVICE_TABLE(of, onie_tlv_of_match_table);
+-static struct nvmem_layout onie_tlv_layout = {
+-      .name = "ONIE tlv layout",
+-      .of_match_table = onie_tlv_of_match_table,
+-      .add_cells = onie_tlv_parse_table,
++static struct nvmem_layout_driver onie_tlv_layout = {
++      .driver = {
++              .owner = THIS_MODULE,
++              .name = "onie-tlv-layout",
++              .of_match_table = onie_tlv_of_match_table,
++      },
++      .probe = onie_tlv_probe,
++      .remove = onie_tlv_remove,
+ };
+ module_nvmem_layout_driver(onie_tlv_layout);
+--- a/drivers/nvmem/layouts/sl28vpd.c
++++ b/drivers/nvmem/layouts/sl28vpd.c
+@@ -134,16 +134,32 @@ static int sl28vpd_add_cells(struct devi
+       return 0;
+ }
++static int sl28vpd_probe(struct nvmem_layout *layout)
++{
++      layout->add_cells = sl28vpd_add_cells;
++
++      return nvmem_layout_register(layout);
++}
++
++static void sl28vpd_remove(struct nvmem_layout *layout)
++{
++      nvmem_layout_unregister(layout);
++}
++
+ static const struct of_device_id sl28vpd_of_match_table[] = {
+       { .compatible = "kontron,sl28-vpd" },
+       {},
+ };
+ MODULE_DEVICE_TABLE(of, sl28vpd_of_match_table);
+-static struct nvmem_layout sl28vpd_layout = {
+-      .name = "sl28-vpd",
+-      .of_match_table = sl28vpd_of_match_table,
+-      .add_cells = sl28vpd_add_cells,
++static struct nvmem_layout_driver sl28vpd_layout = {
++      .driver = {
++              .owner = THIS_MODULE,
++              .name = "kontron-sl28vpd-layout",
++              .of_match_table = sl28vpd_of_match_table,
++      },
++      .probe = sl28vpd_probe,
++      .remove = sl28vpd_remove,
+ };
+ module_nvmem_layout_driver(sl28vpd_layout);
+--- a/include/linux/nvmem-provider.h
++++ b/include/linux/nvmem-provider.h
+@@ -9,6 +9,7 @@
+ #ifndef _LINUX_NVMEM_PROVIDER_H
+ #define _LINUX_NVMEM_PROVIDER_H
++#include <linux/device.h>
+ #include <linux/device/driver.h>
+ #include <linux/err.h>
+ #include <linux/errno.h>
+@@ -158,12 +159,11 @@ struct nvmem_cell_table {
+ /**
+  * struct nvmem_layout - NVMEM layout definitions
+  *
+- * @name:             Layout name.
+- * @of_match_table:   Open firmware match table.
+- * @add_cells:                Called to populate the layout using
+- *                    nvmem_add_one_cell().
+- * @owner:            Pointer to struct module.
+- * @node:             List node.
++ * @dev:              Device-model layout device.
++ * @nvmem:            The underlying NVMEM device
++ * @add_cells:                Will be called if a nvmem device is found which
++ *                    has this layout. The function will add layout
++ *                    specific cells with nvmem_add_one_cell().
+  *
+  * A nvmem device can hold a well defined structure which can just be
+  * evaluated during runtime. For example a TLV list, or a list of "name=val"
+@@ -171,13 +171,15 @@ struct nvmem_cell_table {
+  * cells.
+  */
+ struct nvmem_layout {
+-      const char *name;
+-      const struct of_device_id *of_match_table;
++      struct device dev;
++      struct nvmem_device *nvmem;
+       int (*add_cells)(struct device *dev, struct nvmem_device *nvmem);
++};
+-      /* private */
+-      struct module *owner;
+-      struct list_head node;
++struct nvmem_layout_driver {
++      struct device_driver driver;
++      int (*probe)(struct nvmem_layout *layout);
++      void (*remove)(struct nvmem_layout *layout);
+ };
+ #if IS_ENABLED(CONFIG_NVMEM)
+@@ -194,11 +196,15 @@ void nvmem_del_cell_table(struct nvmem_c
+ int nvmem_add_one_cell(struct nvmem_device *nvmem,
+                      const struct nvmem_cell_info *info);
+-int __nvmem_layout_register(struct nvmem_layout *layout, struct module *owner);
+-#define nvmem_layout_register(layout) \
+-      __nvmem_layout_register(layout, THIS_MODULE)
++int nvmem_layout_register(struct nvmem_layout *layout);
+ void nvmem_layout_unregister(struct nvmem_layout *layout);
++int nvmem_layout_driver_register(struct nvmem_layout_driver *drv);
++void nvmem_layout_driver_unregister(struct nvmem_layout_driver *drv);
++#define module_nvmem_layout_driver(__nvmem_layout_driver)             \
++      module_driver(__nvmem_layout_driver, nvmem_layout_driver_register, \
++                    nvmem_layout_driver_unregister)
++
+ const void *nvmem_layout_get_match_data(struct nvmem_device *nvmem,
+                                       struct nvmem_layout *layout);
+@@ -262,8 +268,4 @@ static inline struct device_node *of_nvm
+ #endif /* CONFIG_NVMEM && CONFIG_OF */
+-#define module_nvmem_layout_driver(__layout_driver)           \
+-      module_driver(__layout_driver, nvmem_layout_register,   \
+-                    nvmem_layout_unregister)
+-
+ #endif  /* ifndef _LINUX_NVMEM_PROVIDER_H */
diff --git a/target/linux/generic/backport-5.15/834-v6.8-0006-nvmem-core-Expose-cells-through-sysfs.patch b/target/linux/generic/backport-5.15/834-v6.8-0006-nvmem-core-Expose-cells-through-sysfs.patch
new file mode 100644 (file)
index 0000000..89872be
--- /dev/null
@@ -0,0 +1,240 @@
+From 0331c611949fffdf486652450901a4dc52bc5cca Mon Sep 17 00:00:00 2001
+From: Miquel Raynal <miquel.raynal@bootlin.com>
+Date: Fri, 15 Dec 2023 11:15:34 +0000
+Subject: [PATCH] nvmem: core: Expose cells through sysfs
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+The binary content of nvmem devices is available to the user so in the
+easiest cases, finding the content of a cell is rather easy as it is
+just a matter of looking at a known and fixed offset. However, nvmem
+layouts have been recently introduced to cope with more advanced
+situations, where the offset and size of the cells is not known in
+advance or is dynamic. When using layouts, more advanced parsers are
+used by the kernel in order to give direct access to the content of each
+cell, regardless of its position/size in the underlying
+device. Unfortunately, these information are not accessible by users,
+unless by fully re-implementing the parser logic in userland.
+
+Let's expose the cells and their content through sysfs to avoid these
+situations. Of course the relevant NVMEM sysfs Kconfig option must be
+enabled for this support to be available.
+
+Not all nvmem devices expose cells. Indeed, the .bin_attrs attribute
+group member will be filled at runtime only when relevant and will
+remain empty otherwise. In this case, as the cells attribute group will
+be empty, it will not lead to any additional folder/file creation.
+
+Exposed cells are read-only. There is, in practice, everything in the
+core to support a write path, but as I don't see any need for that, I
+prefer to keep the interface simple (and probably safer). The interface
+is documented as being in the "testing" state which means we can later
+add a write attribute if though relevant.
+
+Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
+Tested-by: Rafał Miłecki <rafal@milecki.pl>
+Tested-by: Chen-Yu Tsai <wenst@chromium.org>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20231215111536.316972-9-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/core.c      | 135 +++++++++++++++++++++++++++++++++++++-
+ drivers/nvmem/internals.h |   1 +
+ 2 files changed, 135 insertions(+), 1 deletion(-)
+
+--- a/drivers/nvmem/core.c
++++ b/drivers/nvmem/core.c
+@@ -300,6 +300,43 @@ static umode_t nvmem_bin_attr_is_visible
+       return nvmem_bin_attr_get_umode(nvmem);
+ }
++static struct nvmem_cell *nvmem_create_cell(struct nvmem_cell_entry *entry,
++                                          const char *id, int index);
++
++static ssize_t nvmem_cell_attr_read(struct file *filp, struct kobject *kobj,
++                                  struct bin_attribute *attr, char *buf,
++                                  loff_t pos, size_t count)
++{
++      struct nvmem_cell_entry *entry;
++      struct nvmem_cell *cell = NULL;
++      size_t cell_sz, read_len;
++      void *content;
++
++      entry = attr->private;
++      cell = nvmem_create_cell(entry, entry->name, 0);
++      if (IS_ERR(cell))
++              return PTR_ERR(cell);
++
++      if (!cell)
++              return -EINVAL;
++
++      content = nvmem_cell_read(cell, &cell_sz);
++      if (IS_ERR(content)) {
++              read_len = PTR_ERR(content);
++              goto destroy_cell;
++      }
++
++      read_len = min_t(unsigned int, cell_sz - pos, count);
++      memcpy(buf, content + pos, read_len);
++      kfree(content);
++
++destroy_cell:
++      kfree_const(cell->id);
++      kfree(cell);
++
++      return read_len;
++}
++
+ /* default read/write permissions */
+ static struct bin_attribute bin_attr_rw_nvmem = {
+       .attr   = {
+@@ -321,11 +358,21 @@ static const struct attribute_group nvme
+       .is_bin_visible = nvmem_bin_attr_is_visible,
+ };
++/* Cell attributes will be dynamically allocated */
++static struct attribute_group nvmem_cells_group = {
++      .name           = "cells",
++};
++
+ static const struct attribute_group *nvmem_dev_groups[] = {
+       &nvmem_bin_group,
+       NULL,
+ };
++static const struct attribute_group *nvmem_cells_groups[] = {
++      &nvmem_cells_group,
++      NULL,
++};
++
+ static struct bin_attribute bin_attr_nvmem_eeprom_compat = {
+       .attr   = {
+               .name   = "eeprom",
+@@ -381,6 +428,68 @@ static void nvmem_sysfs_remove_compat(st
+               device_remove_bin_file(nvmem->base_dev, &nvmem->eeprom);
+ }
++static int nvmem_populate_sysfs_cells(struct nvmem_device *nvmem)
++{
++      struct bin_attribute **cells_attrs, *attrs;
++      struct nvmem_cell_entry *entry;
++      unsigned int ncells = 0, i = 0;
++      int ret = 0;
++
++      mutex_lock(&nvmem_mutex);
++
++      if (list_empty(&nvmem->cells) || nvmem->sysfs_cells_populated) {
++              nvmem_cells_group.bin_attrs = NULL;
++              goto unlock_mutex;
++      }
++
++      /* Allocate an array of attributes with a sentinel */
++      ncells = list_count_nodes(&nvmem->cells);
++      cells_attrs = devm_kcalloc(&nvmem->dev, ncells + 1,
++                                 sizeof(struct bin_attribute *), GFP_KERNEL);
++      if (!cells_attrs) {
++              ret = -ENOMEM;
++              goto unlock_mutex;
++      }
++
++      attrs = devm_kcalloc(&nvmem->dev, ncells, sizeof(struct bin_attribute), GFP_KERNEL);
++      if (!attrs) {
++              ret = -ENOMEM;
++              goto unlock_mutex;
++      }
++
++      /* Initialize each attribute to take the name and size of the cell */
++      list_for_each_entry(entry, &nvmem->cells, node) {
++              sysfs_bin_attr_init(&attrs[i]);
++              attrs[i].attr.name = devm_kasprintf(&nvmem->dev, GFP_KERNEL,
++                                                  "%s@%x", entry->name,
++                                                  entry->offset);
++              attrs[i].attr.mode = 0444;
++              attrs[i].size = entry->bytes;
++              attrs[i].read = &nvmem_cell_attr_read;
++              attrs[i].private = entry;
++              if (!attrs[i].attr.name) {
++                      ret = -ENOMEM;
++                      goto unlock_mutex;
++              }
++
++              cells_attrs[i] = &attrs[i];
++              i++;
++      }
++
++      nvmem_cells_group.bin_attrs = cells_attrs;
++
++      ret = devm_device_add_groups(&nvmem->dev, nvmem_cells_groups);
++      if (ret)
++              goto unlock_mutex;
++
++      nvmem->sysfs_cells_populated = true;
++
++unlock_mutex:
++      mutex_unlock(&nvmem_mutex);
++
++      return ret;
++}
++
+ #else /* CONFIG_NVMEM_SYSFS */
+ static int nvmem_sysfs_setup_compat(struct nvmem_device *nvmem,
+@@ -740,11 +849,25 @@ static int nvmem_add_cells_from_fixed_la
+ int nvmem_layout_register(struct nvmem_layout *layout)
+ {
++      int ret;
++
+       if (!layout->add_cells)
+               return -EINVAL;
+       /* Populate the cells */
+-      return layout->add_cells(&layout->nvmem->dev, layout->nvmem);
++      ret = layout->add_cells(&layout->nvmem->dev, layout->nvmem);
++      if (ret)
++              return ret;
++
++#ifdef CONFIG_NVMEM_SYSFS
++      ret = nvmem_populate_sysfs_cells(layout->nvmem);
++      if (ret) {
++              nvmem_device_remove_all_cells(layout->nvmem);
++              return ret;
++      }
++#endif
++
++      return 0;
+ }
+ EXPORT_SYMBOL_GPL(nvmem_layout_register);
+@@ -903,10 +1026,20 @@ struct nvmem_device *nvmem_register(cons
+       if (rval)
+               goto err_remove_dev;
++#ifdef CONFIG_NVMEM_SYSFS
++      rval = nvmem_populate_sysfs_cells(nvmem);
++      if (rval)
++              goto err_destroy_layout;
++#endif
++
+       blocking_notifier_call_chain(&nvmem_notifier, NVMEM_ADD, nvmem);
+       return nvmem;
++#ifdef CONFIG_NVMEM_SYSFS
++err_destroy_layout:
++      nvmem_destroy_layout(nvmem);
++#endif
+ err_remove_dev:
+       device_del(&nvmem->dev);
+ err_remove_cells:
+--- a/drivers/nvmem/internals.h
++++ b/drivers/nvmem/internals.h
+@@ -32,6 +32,7 @@ struct nvmem_device {
+       struct gpio_desc        *wp_gpio;
+       struct nvmem_layout     *layout;
+       void *priv;
++      bool                    sysfs_cells_populated;
+ };
+ #if IS_ENABLED(CONFIG_OF)
diff --git a/target/linux/generic/backport-5.15/834-v6.8-0007-nvmem-stm32-add-support-for-STM32MP25-BSEC-to-contro.patch b/target/linux/generic/backport-5.15/834-v6.8-0007-nvmem-stm32-add-support-for-STM32MP25-BSEC-to-contro.patch
new file mode 100644 (file)
index 0000000..f686222
--- /dev/null
@@ -0,0 +1,65 @@
+From f0ac5b23039610619ca4a4805528553ecb6bc815 Mon Sep 17 00:00:00 2001
+From: Patrick Delaunay <patrick.delaunay@foss.st.com>
+Date: Fri, 15 Dec 2023 11:15:36 +0000
+Subject: [PATCH] nvmem: stm32: add support for STM32MP25 BSEC to control OTP
+ data
+
+On STM32MP25, OTP area may be read/written by using BSEC (boot, security
+and OTP control). The BSEC internal peripheral is only managed by the
+secure world.
+
+The 12 Kbits of OTP (effective) are organized into the following regions:
+- lower OTP (OTP0 to OTP127) = 4096 lower OTP bits,
+  bitwise (1-bit) programmable
+- mid OTP (OTP128 to OTP255) = 4096 middle OTP bits,
+  bulk (32-bit) programmable
+- upper OTP (OTP256 to OTP383) = 4096 upper OTP bits,
+  bulk (32-bit) programmable,
+  only accessible when BSEC is in closed state.
+
+As HWKEY and ECIES key are only accessible by ROM code;
+only 368 OTP words are managed in this driver (OTP0 to OTP267).
+
+This patch adds the STM32MP25 configuration for reading and writing
+the OTP data using the OP-TEE BSEC TA services.
+
+Signed-off-by: Patrick Delaunay <patrick.delaunay@foss.st.com>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20231215111536.316972-11-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/stm32-romem.c | 16 ++++++++++++++++
+ 1 file changed, 16 insertions(+)
+
+--- a/drivers/nvmem/stm32-romem.c
++++ b/drivers/nvmem/stm32-romem.c
+@@ -269,6 +269,19 @@ static const struct stm32_romem_cfg stm3
+       .ta = true,
+ };
++/*
++ * STM32MP25 BSEC OTP: 3 regions of 32-bits data words
++ *   lower OTP (OTP0 to OTP127), bitwise (1-bit) programmable
++ *   mid OTP (OTP128 to OTP255), bulk (32-bit) programmable
++ *   upper OTP (OTP256 to OTP383), bulk (32-bit) programmable
++ *              but no access to HWKEY and ECIES key: limited at OTP367
++ */
++static const struct stm32_romem_cfg stm32mp25_bsec_cfg = {
++      .size = 368 * 4,
++      .lower = 127,
++      .ta = true,
++};
++
+ static const struct of_device_id stm32_romem_of_match[] __maybe_unused = {
+       { .compatible = "st,stm32f4-otp", }, {
+               .compatible = "st,stm32mp15-bsec",
+@@ -276,6 +289,9 @@ static const struct of_device_id stm32_r
+       }, {
+               .compatible = "st,stm32mp13-bsec",
+               .data = (void *)&stm32mp13_bsec_cfg,
++      }, {
++              .compatible = "st,stm32mp25-bsec",
++              .data = (void *)&stm32mp25_bsec_cfg,
+       },
+       { /* sentinel */ },
+ };
diff --git a/target/linux/generic/backport-6.1/818-v6.8-of-device-Export-of_device_make_bus_id.patch b/target/linux/generic/backport-6.1/818-v6.8-of-device-Export-of_device_make_bus_id.patch
new file mode 100644 (file)
index 0000000..564fe98
--- /dev/null
@@ -0,0 +1,140 @@
+From 7f38b70042fcaa49219045bd1a9a2836e27a58ac Mon Sep 17 00:00:00 2001
+From: Miquel Raynal <miquel.raynal@bootlin.com>
+Date: Fri, 15 Dec 2023 11:15:27 +0000
+Subject: [PATCH] of: device: Export of_device_make_bus_id()
+
+This helper is really handy to create unique device names based on their
+device tree path, we may need it outside of the OF core (in the NVMEM
+subsystem) so let's export it. As this helper has nothing patform
+specific, let's move it to of/device.c instead of of/platform.c so we
+can add its prototype to of_device.h.
+
+Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
+Acked-by: Rob Herring <robh@kernel.org>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20231215111536.316972-2-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/of/device.c       | 41 +++++++++++++++++++++++++++++++++++++++
+ drivers/of/platform.c     | 40 --------------------------------------
+ include/linux/of_device.h |  6 ++++++
+ 3 files changed, 47 insertions(+), 40 deletions(-)
+
+--- a/drivers/of/device.c
++++ b/drivers/of/device.c
+@@ -395,3 +395,44 @@ int of_device_uevent_modalias(struct dev
+       return 0;
+ }
+ EXPORT_SYMBOL_GPL(of_device_uevent_modalias);
++
++/**
++ * of_device_make_bus_id - Use the device node data to assign a unique name
++ * @dev: pointer to device structure that is linked to a device tree node
++ *
++ * This routine will first try using the translated bus address to
++ * derive a unique name. If it cannot, then it will prepend names from
++ * parent nodes until a unique name can be derived.
++ */
++void of_device_make_bus_id(struct device *dev)
++{
++      struct device_node *node = dev->of_node;
++      const __be32 *reg;
++      u64 addr;
++      u32 mask;
++
++      /* Construct the name, using parent nodes if necessary to ensure uniqueness */
++      while (node->parent) {
++              /*
++               * If the address can be translated, then that is as much
++               * uniqueness as we need. Make it the first component and return
++               */
++              reg = of_get_property(node, "reg", NULL);
++              if (reg && (addr = of_translate_address(node, reg)) != OF_BAD_ADDR) {
++                      if (!of_property_read_u32(node, "mask", &mask))
++                              dev_set_name(dev, dev_name(dev) ? "%llx.%x.%pOFn:%s" : "%llx.%x.%pOFn",
++                                           addr, ffs(mask) - 1, node, dev_name(dev));
++
++                      else
++                              dev_set_name(dev, dev_name(dev) ? "%llx.%pOFn:%s" : "%llx.%pOFn",
++                                           addr, node, dev_name(dev));
++                      return;
++              }
++
++              /* format arguments only used if dev_name() resolves to NULL */
++              dev_set_name(dev, dev_name(dev) ? "%s:%s" : "%s",
++                           kbasename(node->full_name), dev_name(dev));
++              node = node->parent;
++      }
++}
++EXPORT_SYMBOL_GPL(of_device_make_bus_id);
+--- a/drivers/of/platform.c
++++ b/drivers/of/platform.c
+@@ -64,46 +64,6 @@ EXPORT_SYMBOL(of_find_device_by_node);
+  */
+ /**
+- * of_device_make_bus_id - Use the device node data to assign a unique name
+- * @dev: pointer to device structure that is linked to a device tree node
+- *
+- * This routine will first try using the translated bus address to
+- * derive a unique name. If it cannot, then it will prepend names from
+- * parent nodes until a unique name can be derived.
+- */
+-static void of_device_make_bus_id(struct device *dev)
+-{
+-      struct device_node *node = dev->of_node;
+-      const __be32 *reg;
+-      u64 addr;
+-      u32 mask;
+-
+-      /* Construct the name, using parent nodes if necessary to ensure uniqueness */
+-      while (node->parent) {
+-              /*
+-               * If the address can be translated, then that is as much
+-               * uniqueness as we need. Make it the first component and return
+-               */
+-              reg = of_get_property(node, "reg", NULL);
+-              if (reg && (addr = of_translate_address(node, reg)) != OF_BAD_ADDR) {
+-                      if (!of_property_read_u32(node, "mask", &mask))
+-                              dev_set_name(dev, dev_name(dev) ? "%llx.%x.%pOFn:%s" : "%llx.%x.%pOFn",
+-                                           addr, ffs(mask) - 1, node, dev_name(dev));
+-
+-                      else
+-                              dev_set_name(dev, dev_name(dev) ? "%llx.%pOFn:%s" : "%llx.%pOFn",
+-                                           addr, node, dev_name(dev));
+-                      return;
+-              }
+-
+-              /* format arguments only used if dev_name() resolves to NULL */
+-              dev_set_name(dev, dev_name(dev) ? "%s:%s" : "%s",
+-                           kbasename(node->full_name), dev_name(dev));
+-              node = node->parent;
+-      }
+-}
+-
+-/**
+  * of_device_alloc - Allocate and initialize an of_device
+  * @np: device node to assign to device
+  * @bus_id: Name to assign to the device.  May be null to use default name.
+--- a/include/linux/of_device.h
++++ b/include/linux/of_device.h
+@@ -56,6 +56,9 @@ static inline int of_dma_configure(struc
+ {
+       return of_dma_configure_id(dev, np, force_dma, NULL);
+ }
++
++void of_device_make_bus_id(struct device *dev);
++
+ #else /* CONFIG_OF */
+ static inline int of_driver_match_device(struct device *dev,
+@@ -113,6 +116,9 @@ static inline int of_dma_configure(struc
+ {
+       return 0;
+ }
++
++static inline void of_device_make_bus_id(struct device *dev) {}
++
+ #endif /* CONFIG_OF */
+ #endif /* _LINUX_OF_DEVICE_H */
diff --git a/target/linux/generic/backport-6.1/819-v6.8-0001-nvmem-Move-of_nvmem_layout_get_container-in-another-.patch b/target/linux/generic/backport-6.1/819-v6.8-0001-nvmem-Move-of_nvmem_layout_get_container-in-another-.patch
new file mode 100644 (file)
index 0000000..2093fac
--- /dev/null
@@ -0,0 +1,95 @@
+From 4a1a40233b4a9fc159a5c7a27dc34c5c7bc5be55 Mon Sep 17 00:00:00 2001
+From: Miquel Raynal <miquel.raynal@bootlin.com>
+Date: Fri, 15 Dec 2023 11:15:28 +0000
+Subject: [PATCH] nvmem: Move of_nvmem_layout_get_container() in another header
+
+nvmem-consumer.h is included by consumer devices, extracting data from
+NVMEM devices whereas nvmem-provider.h is included by devices providing
+NVMEM content.
+
+The only users of of_nvmem_layout_get_container() outside of the core
+are layout drivers, so better move its prototype to nvmem-provider.h.
+
+While we do so, we also move the kdoc associated with the function to
+the header rather than the .c file.
+
+Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20231215111536.316972-3-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/core.c           |  8 --------
+ include/linux/nvmem-consumer.h |  7 -------
+ include/linux/nvmem-provider.h | 21 +++++++++++++++++++++
+ 3 files changed, 21 insertions(+), 15 deletions(-)
+
+--- a/drivers/nvmem/core.c
++++ b/drivers/nvmem/core.c
+@@ -848,14 +848,6 @@ static int nvmem_add_cells_from_layout(s
+ }
+ #if IS_ENABLED(CONFIG_OF)
+-/**
+- * of_nvmem_layout_get_container() - Get OF node to layout container.
+- *
+- * @nvmem: nvmem device.
+- *
+- * Return: a node pointer with refcount incremented or NULL if no
+- * container exists. Use of_node_put() on it when done.
+- */
+ struct device_node *of_nvmem_layout_get_container(struct nvmem_device *nvmem)
+ {
+       return of_get_child_by_name(nvmem->dev.of_node, "nvmem-layout");
+--- a/include/linux/nvmem-consumer.h
++++ b/include/linux/nvmem-consumer.h
+@@ -241,7 +241,6 @@ struct nvmem_cell *of_nvmem_cell_get(str
+                                    const char *id);
+ struct nvmem_device *of_nvmem_device_get(struct device_node *np,
+                                        const char *name);
+-struct device_node *of_nvmem_layout_get_container(struct nvmem_device *nvmem);
+ #else
+ static inline struct nvmem_cell *of_nvmem_cell_get(struct device_node *np,
+                                                  const char *id)
+@@ -254,12 +253,6 @@ static inline struct nvmem_device *of_nv
+ {
+       return ERR_PTR(-EOPNOTSUPP);
+ }
+-
+-static inline struct device_node *
+-of_nvmem_layout_get_container(struct nvmem_device *nvmem)
+-{
+-      return NULL;
+-}
+ #endif /* CONFIG_NVMEM && CONFIG_OF */
+ #endif  /* ifndef _LINUX_NVMEM_CONSUMER_H */
+--- a/include/linux/nvmem-provider.h
++++ b/include/linux/nvmem-provider.h
+@@ -244,6 +244,27 @@ nvmem_layout_get_match_data(struct nvmem
+ #endif /* CONFIG_NVMEM */
++#if IS_ENABLED(CONFIG_NVMEM) && IS_ENABLED(CONFIG_OF)
++
++/**
++ * of_nvmem_layout_get_container() - Get OF node of layout container
++ *
++ * @nvmem: nvmem device
++ *
++ * Return: a node pointer with refcount incremented or NULL if no
++ * container exists. Use of_node_put() on it when done.
++ */
++struct device_node *of_nvmem_layout_get_container(struct nvmem_device *nvmem);
++
++#else  /* CONFIG_NVMEM && CONFIG_OF */
++
++static inline struct device_node *of_nvmem_layout_get_container(struct nvmem_device *nvmem)
++{
++      return NULL;
++}
++
++#endif /* CONFIG_NVMEM && CONFIG_OF */
++
+ #define module_nvmem_layout_driver(__layout_driver)           \
+       module_driver(__layout_driver, nvmem_layout_register,   \
+                     nvmem_layout_unregister)
diff --git a/target/linux/generic/backport-6.1/819-v6.8-0002-nvmem-Create-a-header-for-internal-sharing.patch b/target/linux/generic/backport-6.1/819-v6.8-0002-nvmem-Create-a-header-for-internal-sharing.patch
new file mode 100644 (file)
index 0000000..e722109
--- /dev/null
@@ -0,0 +1,91 @@
+From ec9c08a1cb8dc5e8e003f95f5f62de41dde235bb Mon Sep 17 00:00:00 2001
+From: Miquel Raynal <miquel.raynal@bootlin.com>
+Date: Fri, 15 Dec 2023 11:15:29 +0000
+Subject: [PATCH] nvmem: Create a header for internal sharing
+
+Before adding all the NVMEM layout bus infrastructure to the core, let's
+move the main nvmem_device structure in an internal header, only
+available to the core. This way all the additional code can be added in
+a dedicated file in order to keep the current core file tidy.
+
+Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20231215111536.316972-4-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/core.c      | 24 +-----------------------
+ drivers/nvmem/internals.h | 35 +++++++++++++++++++++++++++++++++++
+ 2 files changed, 36 insertions(+), 23 deletions(-)
+ create mode 100644 drivers/nvmem/internals.h
+
+--- a/drivers/nvmem/core.c
++++ b/drivers/nvmem/core.c
+@@ -20,29 +20,7 @@
+ #include <linux/of_device.h>
+ #include <linux/slab.h>
+-struct nvmem_device {
+-      struct module           *owner;
+-      struct device           dev;
+-      int                     stride;
+-      int                     word_size;
+-      int                     id;
+-      struct kref             refcnt;
+-      size_t                  size;
+-      bool                    read_only;
+-      bool                    root_only;
+-      int                     flags;
+-      enum nvmem_type         type;
+-      struct bin_attribute    eeprom;
+-      struct device           *base_dev;
+-      struct list_head        cells;
+-      const struct nvmem_keepout *keepout;
+-      unsigned int            nkeepout;
+-      nvmem_reg_read_t        reg_read;
+-      nvmem_reg_write_t       reg_write;
+-      struct gpio_desc        *wp_gpio;
+-      struct nvmem_layout     *layout;
+-      void *priv;
+-};
++#include "internals.h"
+ #define to_nvmem_device(d) container_of(d, struct nvmem_device, dev)
+--- /dev/null
++++ b/drivers/nvmem/internals.h
+@@ -0,0 +1,35 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++
++#ifndef _LINUX_NVMEM_INTERNALS_H
++#define _LINUX_NVMEM_INTERNALS_H
++
++#include <linux/device.h>
++#include <linux/nvmem-consumer.h>
++#include <linux/nvmem-provider.h>
++
++struct nvmem_device {
++      struct module           *owner;
++      struct device           dev;
++      struct list_head        node;
++      int                     stride;
++      int                     word_size;
++      int                     id;
++      struct kref             refcnt;
++      size_t                  size;
++      bool                    read_only;
++      bool                    root_only;
++      int                     flags;
++      enum nvmem_type         type;
++      struct bin_attribute    eeprom;
++      struct device           *base_dev;
++      struct list_head        cells;
++      const struct nvmem_keepout *keepout;
++      unsigned int            nkeepout;
++      nvmem_reg_read_t        reg_read;
++      nvmem_reg_write_t       reg_write;
++      struct gpio_desc        *wp_gpio;
++      struct nvmem_layout     *layout;
++      void *priv;
++};
++
++#endif  /* ifndef _LINUX_NVMEM_INTERNALS_H */
diff --git a/target/linux/generic/backport-6.1/819-v6.8-0003-nvmem-Simplify-the-add_cells-hook.patch b/target/linux/generic/backport-6.1/819-v6.8-0003-nvmem-Simplify-the-add_cells-hook.patch
new file mode 100644 (file)
index 0000000..db2d8c1
--- /dev/null
@@ -0,0 +1,79 @@
+From 1b7c298a4ecbc28cc6ee94005734bff55eb83d22 Mon Sep 17 00:00:00 2001
+From: Miquel Raynal <miquel.raynal@bootlin.com>
+Date: Fri, 15 Dec 2023 11:15:30 +0000
+Subject: [PATCH] nvmem: Simplify the ->add_cells() hook
+
+The layout entry is not used and will anyway be made useless by the new
+layout bus infrastructure coming next, so drop it. While at it, clarify
+the kdoc entry.
+
+Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20231215111536.316972-5-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/core.c             | 2 +-
+ drivers/nvmem/layouts/onie-tlv.c | 3 +--
+ drivers/nvmem/layouts/sl28vpd.c  | 3 +--
+ include/linux/nvmem-provider.h   | 8 +++-----
+ 4 files changed, 6 insertions(+), 10 deletions(-)
+
+--- a/drivers/nvmem/core.c
++++ b/drivers/nvmem/core.c
+@@ -817,7 +817,7 @@ static int nvmem_add_cells_from_layout(s
+       int ret;
+       if (layout && layout->add_cells) {
+-              ret = layout->add_cells(&nvmem->dev, nvmem, layout);
++              ret = layout->add_cells(&nvmem->dev, nvmem);
+               if (ret)
+                       return ret;
+       }
+--- a/drivers/nvmem/layouts/onie-tlv.c
++++ b/drivers/nvmem/layouts/onie-tlv.c
+@@ -182,8 +182,7 @@ static bool onie_tlv_crc_is_valid(struct
+       return true;
+ }
+-static int onie_tlv_parse_table(struct device *dev, struct nvmem_device *nvmem,
+-                              struct nvmem_layout *layout)
++static int onie_tlv_parse_table(struct device *dev, struct nvmem_device *nvmem)
+ {
+       struct onie_tlv_hdr hdr;
+       size_t table_len, data_len, hdr_len;
+--- a/drivers/nvmem/layouts/sl28vpd.c
++++ b/drivers/nvmem/layouts/sl28vpd.c
+@@ -80,8 +80,7 @@ static int sl28vpd_v1_check_crc(struct d
+       return 0;
+ }
+-static int sl28vpd_add_cells(struct device *dev, struct nvmem_device *nvmem,
+-                           struct nvmem_layout *layout)
++static int sl28vpd_add_cells(struct device *dev, struct nvmem_device *nvmem)
+ {
+       const struct nvmem_cell_info *pinfo;
+       struct nvmem_cell_info info = {0};
+--- a/include/linux/nvmem-provider.h
++++ b/include/linux/nvmem-provider.h
+@@ -156,9 +156,8 @@ struct nvmem_cell_table {
+  *
+  * @name:             Layout name.
+  * @of_match_table:   Open firmware match table.
+- * @add_cells:                Will be called if a nvmem device is found which
+- *                    has this layout. The function will add layout
+- *                    specific cells with nvmem_add_one_cell().
++ * @add_cells:                Called to populate the layout using
++ *                    nvmem_add_one_cell().
+  * @fixup_cell_info:  Will be called before a cell is added. Can be
+  *                    used to modify the nvmem_cell_info.
+  * @owner:            Pointer to struct module.
+@@ -172,8 +171,7 @@ struct nvmem_cell_table {
+ struct nvmem_layout {
+       const char *name;
+       const struct of_device_id *of_match_table;
+-      int (*add_cells)(struct device *dev, struct nvmem_device *nvmem,
+-                       struct nvmem_layout *layout);
++      int (*add_cells)(struct device *dev, struct nvmem_device *nvmem);
+       void (*fixup_cell_info)(struct nvmem_device *nvmem,
+                               struct nvmem_layout *layout,
+                               struct nvmem_cell_info *cell);
diff --git a/target/linux/generic/backport-6.1/819-v6.8-0004-nvmem-Move-and-rename-fixup_cell_info.patch b/target/linux/generic/backport-6.1/819-v6.8-0004-nvmem-Move-and-rename-fixup_cell_info.patch
new file mode 100644 (file)
index 0000000..65aa37f
--- /dev/null
@@ -0,0 +1,169 @@
+From 1172460e716784ac7e1049a537bdca8edbf97360 Mon Sep 17 00:00:00 2001
+From: Miquel Raynal <miquel.raynal@bootlin.com>
+Date: Fri, 15 Dec 2023 11:15:31 +0000
+Subject: [PATCH] nvmem: Move and rename ->fixup_cell_info()
+
+This hook is meant to be used by any provider and instantiating a layout
+just for this is useless. Let's instead move this hook to the nvmem
+device and add it to the config structure to be easily shared by the
+providers.
+
+While at moving this hook, rename it ->fixup_dt_cell_info() to clarify
+its main intended purpose.
+
+Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20231215111536.316972-6-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/core.c           |  6 +++---
+ drivers/nvmem/imx-ocotp.c      | 11 +++--------
+ drivers/nvmem/internals.h      |  2 ++
+ drivers/nvmem/mtk-efuse.c      | 11 +++--------
+ include/linux/nvmem-provider.h |  9 ++++-----
+ 5 files changed, 15 insertions(+), 24 deletions(-)
+
+--- a/drivers/nvmem/core.c
++++ b/drivers/nvmem/core.c
+@@ -676,7 +676,6 @@ static int nvmem_validate_keepouts(struc
+ static int nvmem_add_cells_from_dt(struct nvmem_device *nvmem, struct device_node *np)
+ {
+-      struct nvmem_layout *layout = nvmem->layout;
+       struct device *dev = &nvmem->dev;
+       struct device_node *child;
+       const __be32 *addr;
+@@ -706,8 +705,8 @@ static int nvmem_add_cells_from_dt(struc
+               info.np = of_node_get(child);
+-              if (layout && layout->fixup_cell_info)
+-                      layout->fixup_cell_info(nvmem, layout, &info);
++              if (nvmem->fixup_dt_cell_info)
++                      nvmem->fixup_dt_cell_info(nvmem, &info);
+               ret = nvmem_add_one_cell(nvmem, &info);
+               kfree(info.name);
+@@ -896,6 +895,7 @@ struct nvmem_device *nvmem_register(cons
+       kref_init(&nvmem->refcnt);
+       INIT_LIST_HEAD(&nvmem->cells);
++      nvmem->fixup_dt_cell_info = config->fixup_dt_cell_info;
+       nvmem->owner = config->owner;
+       if (!nvmem->owner && config->dev->driver)
+--- a/drivers/nvmem/imx-ocotp.c
++++ b/drivers/nvmem/imx-ocotp.c
+@@ -584,17 +584,12 @@ static const struct of_device_id imx_oco
+ };
+ MODULE_DEVICE_TABLE(of, imx_ocotp_dt_ids);
+-static void imx_ocotp_fixup_cell_info(struct nvmem_device *nvmem,
+-                                    struct nvmem_layout *layout,
+-                                    struct nvmem_cell_info *cell)
++static void imx_ocotp_fixup_dt_cell_info(struct nvmem_device *nvmem,
++                                       struct nvmem_cell_info *cell)
+ {
+       cell->read_post_process = imx_ocotp_cell_pp;
+ }
+-static struct nvmem_layout imx_ocotp_layout = {
+-      .fixup_cell_info = imx_ocotp_fixup_cell_info,
+-};
+-
+ static int imx_ocotp_probe(struct platform_device *pdev)
+ {
+       struct device *dev = &pdev->dev;
+@@ -620,7 +615,7 @@ static int imx_ocotp_probe(struct platfo
+       imx_ocotp_nvmem_config.size = 4 * priv->params->nregs;
+       imx_ocotp_nvmem_config.dev = dev;
+       imx_ocotp_nvmem_config.priv = priv;
+-      imx_ocotp_nvmem_config.layout = &imx_ocotp_layout;
++      imx_ocotp_nvmem_config.fixup_dt_cell_info = &imx_ocotp_fixup_dt_cell_info;
+       priv->config = &imx_ocotp_nvmem_config;
+--- a/drivers/nvmem/internals.h
++++ b/drivers/nvmem/internals.h
+@@ -23,6 +23,8 @@ struct nvmem_device {
+       struct bin_attribute    eeprom;
+       struct device           *base_dev;
+       struct list_head        cells;
++      void (*fixup_dt_cell_info)(struct nvmem_device *nvmem,
++                                 struct nvmem_cell_info *cell);
+       const struct nvmem_keepout *keepout;
+       unsigned int            nkeepout;
+       nvmem_reg_read_t        reg_read;
+--- a/drivers/nvmem/mtk-efuse.c
++++ b/drivers/nvmem/mtk-efuse.c
+@@ -45,9 +45,8 @@ static int mtk_efuse_gpu_speedbin_pp(voi
+       return 0;
+ }
+-static void mtk_efuse_fixup_cell_info(struct nvmem_device *nvmem,
+-                                    struct nvmem_layout *layout,
+-                                    struct nvmem_cell_info *cell)
++static void mtk_efuse_fixup_dt_cell_info(struct nvmem_device *nvmem,
++                                       struct nvmem_cell_info *cell)
+ {
+       size_t sz = strlen(cell->name);
+@@ -61,10 +60,6 @@ static void mtk_efuse_fixup_cell_info(st
+               cell->read_post_process = mtk_efuse_gpu_speedbin_pp;
+ }
+-static struct nvmem_layout mtk_efuse_layout = {
+-      .fixup_cell_info = mtk_efuse_fixup_cell_info,
+-};
+-
+ static int mtk_efuse_probe(struct platform_device *pdev)
+ {
+       struct device *dev = &pdev->dev;
+@@ -91,7 +86,7 @@ static int mtk_efuse_probe(struct platfo
+       econfig.priv = priv;
+       econfig.dev = dev;
+       if (pdata->uses_post_processing)
+-              econfig.layout = &mtk_efuse_layout;
++              econfig.fixup_dt_cell_info = &mtk_efuse_fixup_dt_cell_info;
+       nvmem = devm_nvmem_register(dev, &econfig);
+       return PTR_ERR_OR_ZERO(nvmem);
+--- a/include/linux/nvmem-provider.h
++++ b/include/linux/nvmem-provider.h
+@@ -83,6 +83,8 @@ struct nvmem_cell_info {
+  * @cells:    Optional array of pre-defined NVMEM cells.
+  * @ncells:   Number of elements in cells.
+  * @add_legacy_fixed_of_cells:        Read fixed NVMEM cells from old OF syntax.
++ * @fixup_dt_cell_info: Will be called before a cell is added. Can be
++ *            used to modify the nvmem_cell_info.
+  * @keepout:  Optional array of keepout ranges (sorted ascending by start).
+  * @nkeepout: Number of elements in the keepout array.
+  * @type:     Type of the nvmem storage
+@@ -113,6 +115,8 @@ struct nvmem_config {
+       const struct nvmem_cell_info    *cells;
+       int                     ncells;
+       bool                    add_legacy_fixed_of_cells;
++      void (*fixup_dt_cell_info)(struct nvmem_device *nvmem,
++                                 struct nvmem_cell_info *cell);
+       const struct nvmem_keepout *keepout;
+       unsigned int            nkeepout;
+       enum nvmem_type         type;
+@@ -158,8 +162,6 @@ struct nvmem_cell_table {
+  * @of_match_table:   Open firmware match table.
+  * @add_cells:                Called to populate the layout using
+  *                    nvmem_add_one_cell().
+- * @fixup_cell_info:  Will be called before a cell is added. Can be
+- *                    used to modify the nvmem_cell_info.
+  * @owner:            Pointer to struct module.
+  * @node:             List node.
+  *
+@@ -172,9 +174,6 @@ struct nvmem_layout {
+       const char *name;
+       const struct of_device_id *of_match_table;
+       int (*add_cells)(struct device *dev, struct nvmem_device *nvmem);
+-      void (*fixup_cell_info)(struct nvmem_device *nvmem,
+-                              struct nvmem_layout *layout,
+-                              struct nvmem_cell_info *cell);
+       /* private */
+       struct module *owner;
diff --git a/target/linux/generic/backport-6.1/819-v6.8-0005-nvmem-core-Rework-layouts-to-become-regular-devices.patch b/target/linux/generic/backport-6.1/819-v6.8-0005-nvmem-core-Rework-layouts-to-become-regular-devices.patch
new file mode 100644 (file)
index 0000000..1881332
--- /dev/null
@@ -0,0 +1,763 @@
+From fc29fd821d9ac2ae3d32a722fac39ce874efb883 Mon Sep 17 00:00:00 2001
+From: Miquel Raynal <miquel.raynal@bootlin.com>
+Date: Fri, 15 Dec 2023 11:15:32 +0000
+Subject: [PATCH] nvmem: core: Rework layouts to become regular devices
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Current layout support was initially written without modules support in
+mind. When the requirement for module support rose, the existing base
+was improved to adopt modularization support, but kind of a design flaw
+was introduced. With the existing implementation, when a storage device
+registers into NVMEM, the core tries to hook a layout (if any) and
+populates its cells immediately. This means, if the hardware description
+expects a layout to be hooked up, but no driver was provided for that,
+the storage medium will fail to probe and try later from
+scratch. Even if we consider that the hardware description shall be
+correct, we could still probe the storage device (especially if it
+contains the rootfs).
+
+One way to overcome this situation is to consider the layouts as
+devices, and leverage the native notifier mechanism. When a new NVMEM
+device is registered, we can populate its nvmem-layout child, if any,
+and wait for the matching to be done in order to get the cells (the
+waiting can be easily done with the NVMEM notifiers). If the layout
+driver is compiled as a module, it should automatically be loaded. This
+way, there is no strong order to enforce, any NVMEM device creation
+or NVMEM layout driver insertion will be observed as a new event which
+may lead to the creation of additional cells, without disturbing the
+probes with costly (and sometimes endless) deferrals.
+
+In order to achieve that goal we create a new bus for the nvmem-layouts
+with minimal logic to match nvmem-layout devices with nvmem-layout
+drivers. All this infrastructure code is created in the layouts.c file.
+
+Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
+Tested-by: Rafał Miłecki <rafal@milecki.pl>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20231215111536.316972-7-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/Kconfig            |   1 +
+ drivers/nvmem/Makefile           |   2 +
+ drivers/nvmem/core.c             | 170 ++++++++++----------------
+ drivers/nvmem/internals.h        |  21 ++++
+ drivers/nvmem/layouts.c          | 201 +++++++++++++++++++++++++++++++
+ drivers/nvmem/layouts/Kconfig    |   8 ++
+ drivers/nvmem/layouts/onie-tlv.c |  24 +++-
+ drivers/nvmem/layouts/sl28vpd.c  |  24 +++-
+ include/linux/nvmem-provider.h   |  38 +++---
+ 9 files changed, 354 insertions(+), 135 deletions(-)
+ create mode 100644 drivers/nvmem/layouts.c
+
+--- a/drivers/nvmem/Kconfig
++++ b/drivers/nvmem/Kconfig
+@@ -1,6 +1,7 @@
+ # SPDX-License-Identifier: GPL-2.0-only
+ menuconfig NVMEM
+       bool "NVMEM Support"
++      imply NVMEM_LAYOUTS
+       help
+         Support for NVMEM(Non Volatile Memory) devices like EEPROM, EFUSES...
+--- a/drivers/nvmem/Makefile
++++ b/drivers/nvmem/Makefile
+@@ -5,6 +5,8 @@
+ obj-$(CONFIG_NVMEM)           += nvmem_core.o
+ nvmem_core-y                  := core.o
++obj-$(CONFIG_NVMEM_LAYOUTS)   += nvmem_layouts.o
++nvmem_layouts-y                       := layouts.o
+ obj-y                         += layouts/
+ # Devices
+--- a/drivers/nvmem/core.c
++++ b/drivers/nvmem/core.c
+@@ -56,9 +56,6 @@ static LIST_HEAD(nvmem_lookup_list);
+ static BLOCKING_NOTIFIER_HEAD(nvmem_notifier);
+-static DEFINE_SPINLOCK(nvmem_layout_lock);
+-static LIST_HEAD(nvmem_layouts);
+-
+ static int __nvmem_reg_read(struct nvmem_device *nvmem, unsigned int offset,
+                           void *val, size_t bytes)
+ {
+@@ -741,97 +738,22 @@ static int nvmem_add_cells_from_fixed_la
+       return err;
+ }
+-int __nvmem_layout_register(struct nvmem_layout *layout, struct module *owner)
++int nvmem_layout_register(struct nvmem_layout *layout)
+ {
+-      layout->owner = owner;
+-
+-      spin_lock(&nvmem_layout_lock);
+-      list_add(&layout->node, &nvmem_layouts);
+-      spin_unlock(&nvmem_layout_lock);
+-
+-      blocking_notifier_call_chain(&nvmem_notifier, NVMEM_LAYOUT_ADD, layout);
++      if (!layout->add_cells)
++              return -EINVAL;
+-      return 0;
++      /* Populate the cells */
++      return layout->add_cells(&layout->nvmem->dev, layout->nvmem);
+ }
+-EXPORT_SYMBOL_GPL(__nvmem_layout_register);
++EXPORT_SYMBOL_GPL(nvmem_layout_register);
+ void nvmem_layout_unregister(struct nvmem_layout *layout)
+ {
+-      blocking_notifier_call_chain(&nvmem_notifier, NVMEM_LAYOUT_REMOVE, layout);
+-
+-      spin_lock(&nvmem_layout_lock);
+-      list_del(&layout->node);
+-      spin_unlock(&nvmem_layout_lock);
++      /* Keep the API even with an empty stub in case we need it later */
+ }
+ EXPORT_SYMBOL_GPL(nvmem_layout_unregister);
+-static struct nvmem_layout *nvmem_layout_get(struct nvmem_device *nvmem)
+-{
+-      struct device_node *layout_np;
+-      struct nvmem_layout *l, *layout = ERR_PTR(-EPROBE_DEFER);
+-
+-      layout_np = of_nvmem_layout_get_container(nvmem);
+-      if (!layout_np)
+-              return NULL;
+-
+-      /* Fixed layouts don't have a matching driver */
+-      if (of_device_is_compatible(layout_np, "fixed-layout")) {
+-              of_node_put(layout_np);
+-              return NULL;
+-      }
+-
+-      /*
+-       * In case the nvmem device was built-in while the layout was built as a
+-       * module, we shall manually request the layout driver loading otherwise
+-       * we'll never have any match.
+-       */
+-      of_request_module(layout_np);
+-
+-      spin_lock(&nvmem_layout_lock);
+-
+-      list_for_each_entry(l, &nvmem_layouts, node) {
+-              if (of_match_node(l->of_match_table, layout_np)) {
+-                      if (try_module_get(l->owner))
+-                              layout = l;
+-
+-                      break;
+-              }
+-      }
+-
+-      spin_unlock(&nvmem_layout_lock);
+-      of_node_put(layout_np);
+-
+-      return layout;
+-}
+-
+-static void nvmem_layout_put(struct nvmem_layout *layout)
+-{
+-      if (layout)
+-              module_put(layout->owner);
+-}
+-
+-static int nvmem_add_cells_from_layout(struct nvmem_device *nvmem)
+-{
+-      struct nvmem_layout *layout = nvmem->layout;
+-      int ret;
+-
+-      if (layout && layout->add_cells) {
+-              ret = layout->add_cells(&nvmem->dev, nvmem);
+-              if (ret)
+-                      return ret;
+-      }
+-
+-      return 0;
+-}
+-
+-#if IS_ENABLED(CONFIG_OF)
+-struct device_node *of_nvmem_layout_get_container(struct nvmem_device *nvmem)
+-{
+-      return of_get_child_by_name(nvmem->dev.of_node, "nvmem-layout");
+-}
+-EXPORT_SYMBOL_GPL(of_nvmem_layout_get_container);
+-#endif
+-
+ const void *nvmem_layout_get_match_data(struct nvmem_device *nvmem,
+                                       struct nvmem_layout *layout)
+ {
+@@ -839,7 +761,7 @@ const void *nvmem_layout_get_match_data(
+       const struct of_device_id *match;
+       layout_np = of_nvmem_layout_get_container(nvmem);
+-      match = of_match_node(layout->of_match_table, layout_np);
++      match = of_match_node(layout->dev.driver->of_match_table, layout_np);
+       return match ? match->data : NULL;
+ }
+@@ -951,19 +873,6 @@ struct nvmem_device *nvmem_register(cons
+                       goto err_put_device;
+       }
+-      /*
+-       * If the driver supplied a layout by config->layout, the module
+-       * pointer will be NULL and nvmem_layout_put() will be a noop.
+-       */
+-      nvmem->layout = config->layout ?: nvmem_layout_get(nvmem);
+-      if (IS_ERR(nvmem->layout)) {
+-              rval = PTR_ERR(nvmem->layout);
+-              nvmem->layout = NULL;
+-
+-              if (rval == -EPROBE_DEFER)
+-                      goto err_teardown_compat;
+-      }
+-
+       if (config->cells) {
+               rval = nvmem_add_cells(nvmem, config->cells, config->ncells);
+               if (rval)
+@@ -984,24 +893,24 @@ struct nvmem_device *nvmem_register(cons
+       if (rval)
+               goto err_remove_cells;
+-      rval = nvmem_add_cells_from_layout(nvmem);
+-      if (rval)
+-              goto err_remove_cells;
+-
+       dev_dbg(&nvmem->dev, "Registering nvmem device %s\n", config->name);
+       rval = device_add(&nvmem->dev);
+       if (rval)
+               goto err_remove_cells;
++      rval = nvmem_populate_layout(nvmem);
++      if (rval)
++              goto err_remove_dev;
++
+       blocking_notifier_call_chain(&nvmem_notifier, NVMEM_ADD, nvmem);
+       return nvmem;
++err_remove_dev:
++      device_del(&nvmem->dev);
+ err_remove_cells:
+       nvmem_device_remove_all_cells(nvmem);
+-      nvmem_layout_put(nvmem->layout);
+-err_teardown_compat:
+       if (config->compat)
+               nvmem_sysfs_remove_compat(nvmem, config);
+ err_put_device:
+@@ -1023,7 +932,7 @@ static void nvmem_device_release(struct
+               device_remove_bin_file(nvmem->base_dev, &nvmem->eeprom);
+       nvmem_device_remove_all_cells(nvmem);
+-      nvmem_layout_put(nvmem->layout);
++      nvmem_destroy_layout(nvmem);
+       device_unregister(&nvmem->dev);
+ }
+@@ -1325,6 +1234,12 @@ nvmem_cell_get_from_lookup(struct device
+       return cell;
+ }
++static void nvmem_layout_module_put(struct nvmem_device *nvmem)
++{
++      if (nvmem->layout && nvmem->layout->dev.driver)
++              module_put(nvmem->layout->dev.driver->owner);
++}
++
+ #if IS_ENABLED(CONFIG_OF)
+ static struct nvmem_cell_entry *
+ nvmem_find_cell_entry_by_node(struct nvmem_device *nvmem, struct device_node *np)
+@@ -1343,6 +1258,18 @@ nvmem_find_cell_entry_by_node(struct nvm
+       return cell;
+ }
++static int nvmem_layout_module_get_optional(struct nvmem_device *nvmem)
++{
++      if (!nvmem->layout)
++              return 0;
++
++      if (!nvmem->layout->dev.driver ||
++          !try_module_get(nvmem->layout->dev.driver->owner))
++              return -EPROBE_DEFER;
++
++      return 0;
++}
++
+ /**
+  * of_nvmem_cell_get() - Get a nvmem cell from given device node and cell id
+  *
+@@ -1405,16 +1332,29 @@ struct nvmem_cell *of_nvmem_cell_get(str
+               return ERR_CAST(nvmem);
+       }
++      ret = nvmem_layout_module_get_optional(nvmem);
++      if (ret) {
++              of_node_put(cell_np);
++              __nvmem_device_put(nvmem);
++              return ERR_PTR(ret);
++      }
++
+       cell_entry = nvmem_find_cell_entry_by_node(nvmem, cell_np);
+       of_node_put(cell_np);
+       if (!cell_entry) {
+               __nvmem_device_put(nvmem);
+-              return ERR_PTR(-ENOENT);
++              nvmem_layout_module_put(nvmem);
++              if (nvmem->layout)
++                      return ERR_PTR(-EPROBE_DEFER);
++              else
++                      return ERR_PTR(-ENOENT);
+       }
+       cell = nvmem_create_cell(cell_entry, id, cell_index);
+-      if (IS_ERR(cell))
++      if (IS_ERR(cell)) {
+               __nvmem_device_put(nvmem);
++              nvmem_layout_module_put(nvmem);
++      }
+       return cell;
+ }
+@@ -1528,6 +1468,7 @@ void nvmem_cell_put(struct nvmem_cell *c
+       kfree(cell);
+       __nvmem_device_put(nvmem);
++      nvmem_layout_module_put(nvmem);
+ }
+ EXPORT_SYMBOL_GPL(nvmem_cell_put);
+@@ -2105,11 +2046,22 @@ EXPORT_SYMBOL_GPL(nvmem_dev_name);
+ static int __init nvmem_init(void)
+ {
+-      return bus_register(&nvmem_bus_type);
++      int ret;
++
++      ret = bus_register(&nvmem_bus_type);
++      if (ret)
++              return ret;
++
++      ret = nvmem_layout_bus_register();
++      if (ret)
++              bus_unregister(&nvmem_bus_type);
++
++      return ret;
+ }
+ static void __exit nvmem_exit(void)
+ {
++      nvmem_layout_bus_unregister();
+       bus_unregister(&nvmem_bus_type);
+ }
+--- a/drivers/nvmem/internals.h
++++ b/drivers/nvmem/internals.h
+@@ -34,4 +34,25 @@ struct nvmem_device {
+       void *priv;
+ };
++#if IS_ENABLED(CONFIG_OF)
++int nvmem_layout_bus_register(void);
++void nvmem_layout_bus_unregister(void);
++int nvmem_populate_layout(struct nvmem_device *nvmem);
++void nvmem_destroy_layout(struct nvmem_device *nvmem);
++#else /* CONFIG_OF */
++static inline int nvmem_layout_bus_register(void)
++{
++      return 0;
++}
++
++static inline void nvmem_layout_bus_unregister(void) {}
++
++static inline int nvmem_populate_layout(struct nvmem_device *nvmem)
++{
++      return 0;
++}
++
++static inline void nvmem_destroy_layout(struct nvmem_device *nvmem) { }
++#endif /* CONFIG_OF */
++
+ #endif  /* ifndef _LINUX_NVMEM_INTERNALS_H */
+--- /dev/null
++++ b/drivers/nvmem/layouts.c
+@@ -0,0 +1,201 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * NVMEM layout bus handling
++ *
++ * Copyright (C) 2023 Bootlin
++ * Author: Miquel Raynal <miquel.raynal@bootlin.com
++ */
++
++#include <linux/device.h>
++#include <linux/dma-mapping.h>
++#include <linux/nvmem-consumer.h>
++#include <linux/nvmem-provider.h>
++#include <linux/of.h>
++#include <linux/of_device.h>
++#include <linux/of_irq.h>
++
++#include "internals.h"
++
++#define to_nvmem_layout_driver(drv) \
++      (container_of((drv), struct nvmem_layout_driver, driver))
++#define to_nvmem_layout_device(_dev) \
++      container_of((_dev), struct nvmem_layout, dev)
++
++static int nvmem_layout_bus_match(struct device *dev, struct device_driver *drv)
++{
++      return of_driver_match_device(dev, drv);
++}
++
++static int nvmem_layout_bus_probe(struct device *dev)
++{
++      struct nvmem_layout_driver *drv = to_nvmem_layout_driver(dev->driver);
++      struct nvmem_layout *layout = to_nvmem_layout_device(dev);
++
++      if (!drv->probe || !drv->remove)
++              return -EINVAL;
++
++      return drv->probe(layout);
++}
++
++static void nvmem_layout_bus_remove(struct device *dev)
++{
++      struct nvmem_layout_driver *drv = to_nvmem_layout_driver(dev->driver);
++      struct nvmem_layout *layout = to_nvmem_layout_device(dev);
++
++      return drv->remove(layout);
++}
++
++static struct bus_type nvmem_layout_bus_type = {
++      .name           = "nvmem-layout",
++      .match          = nvmem_layout_bus_match,
++      .probe          = nvmem_layout_bus_probe,
++      .remove         = nvmem_layout_bus_remove,
++};
++
++int nvmem_layout_driver_register(struct nvmem_layout_driver *drv)
++{
++      drv->driver.bus = &nvmem_layout_bus_type;
++
++      return driver_register(&drv->driver);
++}
++EXPORT_SYMBOL_GPL(nvmem_layout_driver_register);
++
++void nvmem_layout_driver_unregister(struct nvmem_layout_driver *drv)
++{
++      driver_unregister(&drv->driver);
++}
++EXPORT_SYMBOL_GPL(nvmem_layout_driver_unregister);
++
++static void nvmem_layout_release_device(struct device *dev)
++{
++      struct nvmem_layout *layout = to_nvmem_layout_device(dev);
++
++      of_node_put(layout->dev.of_node);
++      kfree(layout);
++}
++
++static int nvmem_layout_create_device(struct nvmem_device *nvmem,
++                                    struct device_node *np)
++{
++      struct nvmem_layout *layout;
++      struct device *dev;
++      int ret;
++
++      layout = kzalloc(sizeof(*layout), GFP_KERNEL);
++      if (!layout)
++              return -ENOMEM;
++
++      /* Create a bidirectional link */
++      layout->nvmem = nvmem;
++      nvmem->layout = layout;
++
++      /* Device model registration */
++      dev = &layout->dev;
++      device_initialize(dev);
++      dev->parent = &nvmem->dev;
++      dev->bus = &nvmem_layout_bus_type;
++      dev->release = nvmem_layout_release_device;
++      dev->coherent_dma_mask = DMA_BIT_MASK(32);
++      dev->dma_mask = &dev->coherent_dma_mask;
++      device_set_node(dev, of_fwnode_handle(of_node_get(np)));
++      of_device_make_bus_id(dev);
++      of_msi_configure(dev, dev->of_node);
++
++      ret = device_add(dev);
++      if (ret) {
++              put_device(dev);
++              return ret;
++      }
++
++      return 0;
++}
++
++static const struct of_device_id of_nvmem_layout_skip_table[] = {
++      { .compatible = "fixed-layout", },
++      {}
++};
++
++static int nvmem_layout_bus_populate(struct nvmem_device *nvmem,
++                                   struct device_node *layout_dn)
++{
++      int ret;
++
++      /* Make sure it has a compatible property */
++      if (!of_get_property(layout_dn, "compatible", NULL)) {
++              pr_debug("%s() - skipping %pOF, no compatible prop\n",
++                       __func__, layout_dn);
++              return 0;
++      }
++
++      /* Fixed layouts are parsed manually somewhere else for now */
++      if (of_match_node(of_nvmem_layout_skip_table, layout_dn)) {
++              pr_debug("%s() - skipping %pOF node\n", __func__, layout_dn);
++              return 0;
++      }
++
++      if (of_node_check_flag(layout_dn, OF_POPULATED_BUS)) {
++              pr_debug("%s() - skipping %pOF, already populated\n",
++                       __func__, layout_dn);
++
++              return 0;
++      }
++
++      /* NVMEM layout buses expect only a single device representing the layout */
++      ret = nvmem_layout_create_device(nvmem, layout_dn);
++      if (ret)
++              return ret;
++
++      of_node_set_flag(layout_dn, OF_POPULATED_BUS);
++
++      return 0;
++}
++
++struct device_node *of_nvmem_layout_get_container(struct nvmem_device *nvmem)
++{
++      return of_get_child_by_name(nvmem->dev.of_node, "nvmem-layout");
++}
++EXPORT_SYMBOL_GPL(of_nvmem_layout_get_container);
++
++/*
++ * Returns the number of devices populated, 0 if the operation was not relevant
++ * for this nvmem device, an error code otherwise.
++ */
++int nvmem_populate_layout(struct nvmem_device *nvmem)
++{
++      struct device_node *layout_dn;
++      int ret;
++
++      layout_dn = of_nvmem_layout_get_container(nvmem);
++      if (!layout_dn)
++              return 0;
++
++      /* Populate the layout device */
++      device_links_supplier_sync_state_pause();
++      ret = nvmem_layout_bus_populate(nvmem, layout_dn);
++      device_links_supplier_sync_state_resume();
++
++      of_node_put(layout_dn);
++      return ret;
++}
++
++void nvmem_destroy_layout(struct nvmem_device *nvmem)
++{
++      struct device *dev;
++
++      if (!nvmem->layout)
++              return;
++
++      dev = &nvmem->layout->dev;
++      of_node_clear_flag(dev->of_node, OF_POPULATED_BUS);
++      device_unregister(dev);
++}
++
++int nvmem_layout_bus_register(void)
++{
++      return bus_register(&nvmem_layout_bus_type);
++}
++
++void nvmem_layout_bus_unregister(void)
++{
++      bus_unregister(&nvmem_layout_bus_type);
++}
+--- a/drivers/nvmem/layouts/Kconfig
++++ b/drivers/nvmem/layouts/Kconfig
+@@ -1,5 +1,11 @@
+ # SPDX-License-Identifier: GPL-2.0
++config NVMEM_LAYOUTS
++      bool
++      depends on OF
++
++if NVMEM_LAYOUTS
++
+ menu "Layout Types"
+ config NVMEM_LAYOUT_SL28_VPD
+@@ -21,3 +27,5 @@ config NVMEM_LAYOUT_ONIE_TLV
+         If unsure, say N.
+ endmenu
++
++endif
+--- a/drivers/nvmem/layouts/onie-tlv.c
++++ b/drivers/nvmem/layouts/onie-tlv.c
+@@ -225,16 +225,32 @@ static int onie_tlv_parse_table(struct d
+       return 0;
+ }
++static int onie_tlv_probe(struct nvmem_layout *layout)
++{
++      layout->add_cells = onie_tlv_parse_table;
++
++      return nvmem_layout_register(layout);
++}
++
++static void onie_tlv_remove(struct nvmem_layout *layout)
++{
++      nvmem_layout_unregister(layout);
++}
++
+ static const struct of_device_id onie_tlv_of_match_table[] = {
+       { .compatible = "onie,tlv-layout", },
+       {},
+ };
+ MODULE_DEVICE_TABLE(of, onie_tlv_of_match_table);
+-static struct nvmem_layout onie_tlv_layout = {
+-      .name = "ONIE tlv layout",
+-      .of_match_table = onie_tlv_of_match_table,
+-      .add_cells = onie_tlv_parse_table,
++static struct nvmem_layout_driver onie_tlv_layout = {
++      .driver = {
++              .owner = THIS_MODULE,
++              .name = "onie-tlv-layout",
++              .of_match_table = onie_tlv_of_match_table,
++      },
++      .probe = onie_tlv_probe,
++      .remove = onie_tlv_remove,
+ };
+ module_nvmem_layout_driver(onie_tlv_layout);
+--- a/drivers/nvmem/layouts/sl28vpd.c
++++ b/drivers/nvmem/layouts/sl28vpd.c
+@@ -134,16 +134,32 @@ static int sl28vpd_add_cells(struct devi
+       return 0;
+ }
++static int sl28vpd_probe(struct nvmem_layout *layout)
++{
++      layout->add_cells = sl28vpd_add_cells;
++
++      return nvmem_layout_register(layout);
++}
++
++static void sl28vpd_remove(struct nvmem_layout *layout)
++{
++      nvmem_layout_unregister(layout);
++}
++
+ static const struct of_device_id sl28vpd_of_match_table[] = {
+       { .compatible = "kontron,sl28-vpd" },
+       {},
+ };
+ MODULE_DEVICE_TABLE(of, sl28vpd_of_match_table);
+-static struct nvmem_layout sl28vpd_layout = {
+-      .name = "sl28-vpd",
+-      .of_match_table = sl28vpd_of_match_table,
+-      .add_cells = sl28vpd_add_cells,
++static struct nvmem_layout_driver sl28vpd_layout = {
++      .driver = {
++              .owner = THIS_MODULE,
++              .name = "kontron-sl28vpd-layout",
++              .of_match_table = sl28vpd_of_match_table,
++      },
++      .probe = sl28vpd_probe,
++      .remove = sl28vpd_remove,
+ };
+ module_nvmem_layout_driver(sl28vpd_layout);
+--- a/include/linux/nvmem-provider.h
++++ b/include/linux/nvmem-provider.h
+@@ -9,6 +9,7 @@
+ #ifndef _LINUX_NVMEM_PROVIDER_H
+ #define _LINUX_NVMEM_PROVIDER_H
++#include <linux/device.h>
+ #include <linux/device/driver.h>
+ #include <linux/err.h>
+ #include <linux/errno.h>
+@@ -158,12 +159,11 @@ struct nvmem_cell_table {
+ /**
+  * struct nvmem_layout - NVMEM layout definitions
+  *
+- * @name:             Layout name.
+- * @of_match_table:   Open firmware match table.
+- * @add_cells:                Called to populate the layout using
+- *                    nvmem_add_one_cell().
+- * @owner:            Pointer to struct module.
+- * @node:             List node.
++ * @dev:              Device-model layout device.
++ * @nvmem:            The underlying NVMEM device
++ * @add_cells:                Will be called if a nvmem device is found which
++ *                    has this layout. The function will add layout
++ *                    specific cells with nvmem_add_one_cell().
+  *
+  * A nvmem device can hold a well defined structure which can just be
+  * evaluated during runtime. For example a TLV list, or a list of "name=val"
+@@ -171,13 +171,15 @@ struct nvmem_cell_table {
+  * cells.
+  */
+ struct nvmem_layout {
+-      const char *name;
+-      const struct of_device_id *of_match_table;
++      struct device dev;
++      struct nvmem_device *nvmem;
+       int (*add_cells)(struct device *dev, struct nvmem_device *nvmem);
++};
+-      /* private */
+-      struct module *owner;
+-      struct list_head node;
++struct nvmem_layout_driver {
++      struct device_driver driver;
++      int (*probe)(struct nvmem_layout *layout);
++      void (*remove)(struct nvmem_layout *layout);
+ };
+ #if IS_ENABLED(CONFIG_NVMEM)
+@@ -194,11 +196,15 @@ void nvmem_del_cell_table(struct nvmem_c
+ int nvmem_add_one_cell(struct nvmem_device *nvmem,
+                      const struct nvmem_cell_info *info);
+-int __nvmem_layout_register(struct nvmem_layout *layout, struct module *owner);
+-#define nvmem_layout_register(layout) \
+-      __nvmem_layout_register(layout, THIS_MODULE)
++int nvmem_layout_register(struct nvmem_layout *layout);
+ void nvmem_layout_unregister(struct nvmem_layout *layout);
++int nvmem_layout_driver_register(struct nvmem_layout_driver *drv);
++void nvmem_layout_driver_unregister(struct nvmem_layout_driver *drv);
++#define module_nvmem_layout_driver(__nvmem_layout_driver)             \
++      module_driver(__nvmem_layout_driver, nvmem_layout_driver_register, \
++                    nvmem_layout_driver_unregister)
++
+ const void *nvmem_layout_get_match_data(struct nvmem_device *nvmem,
+                                       struct nvmem_layout *layout);
+@@ -262,8 +268,4 @@ static inline struct device_node *of_nvm
+ #endif /* CONFIG_NVMEM && CONFIG_OF */
+-#define module_nvmem_layout_driver(__layout_driver)           \
+-      module_driver(__layout_driver, nvmem_layout_register,   \
+-                    nvmem_layout_unregister)
+-
+ #endif  /* ifndef _LINUX_NVMEM_PROVIDER_H */
diff --git a/target/linux/generic/backport-6.1/819-v6.8-0006-nvmem-core-Expose-cells-through-sysfs.patch b/target/linux/generic/backport-6.1/819-v6.8-0006-nvmem-core-Expose-cells-through-sysfs.patch
new file mode 100644 (file)
index 0000000..89872be
--- /dev/null
@@ -0,0 +1,240 @@
+From 0331c611949fffdf486652450901a4dc52bc5cca Mon Sep 17 00:00:00 2001
+From: Miquel Raynal <miquel.raynal@bootlin.com>
+Date: Fri, 15 Dec 2023 11:15:34 +0000
+Subject: [PATCH] nvmem: core: Expose cells through sysfs
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+The binary content of nvmem devices is available to the user so in the
+easiest cases, finding the content of a cell is rather easy as it is
+just a matter of looking at a known and fixed offset. However, nvmem
+layouts have been recently introduced to cope with more advanced
+situations, where the offset and size of the cells is not known in
+advance or is dynamic. When using layouts, more advanced parsers are
+used by the kernel in order to give direct access to the content of each
+cell, regardless of its position/size in the underlying
+device. Unfortunately, these information are not accessible by users,
+unless by fully re-implementing the parser logic in userland.
+
+Let's expose the cells and their content through sysfs to avoid these
+situations. Of course the relevant NVMEM sysfs Kconfig option must be
+enabled for this support to be available.
+
+Not all nvmem devices expose cells. Indeed, the .bin_attrs attribute
+group member will be filled at runtime only when relevant and will
+remain empty otherwise. In this case, as the cells attribute group will
+be empty, it will not lead to any additional folder/file creation.
+
+Exposed cells are read-only. There is, in practice, everything in the
+core to support a write path, but as I don't see any need for that, I
+prefer to keep the interface simple (and probably safer). The interface
+is documented as being in the "testing" state which means we can later
+add a write attribute if though relevant.
+
+Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
+Tested-by: Rafał Miłecki <rafal@milecki.pl>
+Tested-by: Chen-Yu Tsai <wenst@chromium.org>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20231215111536.316972-9-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/core.c      | 135 +++++++++++++++++++++++++++++++++++++-
+ drivers/nvmem/internals.h |   1 +
+ 2 files changed, 135 insertions(+), 1 deletion(-)
+
+--- a/drivers/nvmem/core.c
++++ b/drivers/nvmem/core.c
+@@ -300,6 +300,43 @@ static umode_t nvmem_bin_attr_is_visible
+       return nvmem_bin_attr_get_umode(nvmem);
+ }
++static struct nvmem_cell *nvmem_create_cell(struct nvmem_cell_entry *entry,
++                                          const char *id, int index);
++
++static ssize_t nvmem_cell_attr_read(struct file *filp, struct kobject *kobj,
++                                  struct bin_attribute *attr, char *buf,
++                                  loff_t pos, size_t count)
++{
++      struct nvmem_cell_entry *entry;
++      struct nvmem_cell *cell = NULL;
++      size_t cell_sz, read_len;
++      void *content;
++
++      entry = attr->private;
++      cell = nvmem_create_cell(entry, entry->name, 0);
++      if (IS_ERR(cell))
++              return PTR_ERR(cell);
++
++      if (!cell)
++              return -EINVAL;
++
++      content = nvmem_cell_read(cell, &cell_sz);
++      if (IS_ERR(content)) {
++              read_len = PTR_ERR(content);
++              goto destroy_cell;
++      }
++
++      read_len = min_t(unsigned int, cell_sz - pos, count);
++      memcpy(buf, content + pos, read_len);
++      kfree(content);
++
++destroy_cell:
++      kfree_const(cell->id);
++      kfree(cell);
++
++      return read_len;
++}
++
+ /* default read/write permissions */
+ static struct bin_attribute bin_attr_rw_nvmem = {
+       .attr   = {
+@@ -321,11 +358,21 @@ static const struct attribute_group nvme
+       .is_bin_visible = nvmem_bin_attr_is_visible,
+ };
++/* Cell attributes will be dynamically allocated */
++static struct attribute_group nvmem_cells_group = {
++      .name           = "cells",
++};
++
+ static const struct attribute_group *nvmem_dev_groups[] = {
+       &nvmem_bin_group,
+       NULL,
+ };
++static const struct attribute_group *nvmem_cells_groups[] = {
++      &nvmem_cells_group,
++      NULL,
++};
++
+ static struct bin_attribute bin_attr_nvmem_eeprom_compat = {
+       .attr   = {
+               .name   = "eeprom",
+@@ -381,6 +428,68 @@ static void nvmem_sysfs_remove_compat(st
+               device_remove_bin_file(nvmem->base_dev, &nvmem->eeprom);
+ }
++static int nvmem_populate_sysfs_cells(struct nvmem_device *nvmem)
++{
++      struct bin_attribute **cells_attrs, *attrs;
++      struct nvmem_cell_entry *entry;
++      unsigned int ncells = 0, i = 0;
++      int ret = 0;
++
++      mutex_lock(&nvmem_mutex);
++
++      if (list_empty(&nvmem->cells) || nvmem->sysfs_cells_populated) {
++              nvmem_cells_group.bin_attrs = NULL;
++              goto unlock_mutex;
++      }
++
++      /* Allocate an array of attributes with a sentinel */
++      ncells = list_count_nodes(&nvmem->cells);
++      cells_attrs = devm_kcalloc(&nvmem->dev, ncells + 1,
++                                 sizeof(struct bin_attribute *), GFP_KERNEL);
++      if (!cells_attrs) {
++              ret = -ENOMEM;
++              goto unlock_mutex;
++      }
++
++      attrs = devm_kcalloc(&nvmem->dev, ncells, sizeof(struct bin_attribute), GFP_KERNEL);
++      if (!attrs) {
++              ret = -ENOMEM;
++              goto unlock_mutex;
++      }
++
++      /* Initialize each attribute to take the name and size of the cell */
++      list_for_each_entry(entry, &nvmem->cells, node) {
++              sysfs_bin_attr_init(&attrs[i]);
++              attrs[i].attr.name = devm_kasprintf(&nvmem->dev, GFP_KERNEL,
++                                                  "%s@%x", entry->name,
++                                                  entry->offset);
++              attrs[i].attr.mode = 0444;
++              attrs[i].size = entry->bytes;
++              attrs[i].read = &nvmem_cell_attr_read;
++              attrs[i].private = entry;
++              if (!attrs[i].attr.name) {
++                      ret = -ENOMEM;
++                      goto unlock_mutex;
++              }
++
++              cells_attrs[i] = &attrs[i];
++              i++;
++      }
++
++      nvmem_cells_group.bin_attrs = cells_attrs;
++
++      ret = devm_device_add_groups(&nvmem->dev, nvmem_cells_groups);
++      if (ret)
++              goto unlock_mutex;
++
++      nvmem->sysfs_cells_populated = true;
++
++unlock_mutex:
++      mutex_unlock(&nvmem_mutex);
++
++      return ret;
++}
++
+ #else /* CONFIG_NVMEM_SYSFS */
+ static int nvmem_sysfs_setup_compat(struct nvmem_device *nvmem,
+@@ -740,11 +849,25 @@ static int nvmem_add_cells_from_fixed_la
+ int nvmem_layout_register(struct nvmem_layout *layout)
+ {
++      int ret;
++
+       if (!layout->add_cells)
+               return -EINVAL;
+       /* Populate the cells */
+-      return layout->add_cells(&layout->nvmem->dev, layout->nvmem);
++      ret = layout->add_cells(&layout->nvmem->dev, layout->nvmem);
++      if (ret)
++              return ret;
++
++#ifdef CONFIG_NVMEM_SYSFS
++      ret = nvmem_populate_sysfs_cells(layout->nvmem);
++      if (ret) {
++              nvmem_device_remove_all_cells(layout->nvmem);
++              return ret;
++      }
++#endif
++
++      return 0;
+ }
+ EXPORT_SYMBOL_GPL(nvmem_layout_register);
+@@ -903,10 +1026,20 @@ struct nvmem_device *nvmem_register(cons
+       if (rval)
+               goto err_remove_dev;
++#ifdef CONFIG_NVMEM_SYSFS
++      rval = nvmem_populate_sysfs_cells(nvmem);
++      if (rval)
++              goto err_destroy_layout;
++#endif
++
+       blocking_notifier_call_chain(&nvmem_notifier, NVMEM_ADD, nvmem);
+       return nvmem;
++#ifdef CONFIG_NVMEM_SYSFS
++err_destroy_layout:
++      nvmem_destroy_layout(nvmem);
++#endif
+ err_remove_dev:
+       device_del(&nvmem->dev);
+ err_remove_cells:
+--- a/drivers/nvmem/internals.h
++++ b/drivers/nvmem/internals.h
+@@ -32,6 +32,7 @@ struct nvmem_device {
+       struct gpio_desc        *wp_gpio;
+       struct nvmem_layout     *layout;
+       void *priv;
++      bool                    sysfs_cells_populated;
+ };
+ #if IS_ENABLED(CONFIG_OF)
diff --git a/target/linux/generic/backport-6.1/819-v6.8-0007-nvmem-stm32-add-support-for-STM32MP25-BSEC-to-contro.patch b/target/linux/generic/backport-6.1/819-v6.8-0007-nvmem-stm32-add-support-for-STM32MP25-BSEC-to-contro.patch
new file mode 100644 (file)
index 0000000..f686222
--- /dev/null
@@ -0,0 +1,65 @@
+From f0ac5b23039610619ca4a4805528553ecb6bc815 Mon Sep 17 00:00:00 2001
+From: Patrick Delaunay <patrick.delaunay@foss.st.com>
+Date: Fri, 15 Dec 2023 11:15:36 +0000
+Subject: [PATCH] nvmem: stm32: add support for STM32MP25 BSEC to control OTP
+ data
+
+On STM32MP25, OTP area may be read/written by using BSEC (boot, security
+and OTP control). The BSEC internal peripheral is only managed by the
+secure world.
+
+The 12 Kbits of OTP (effective) are organized into the following regions:
+- lower OTP (OTP0 to OTP127) = 4096 lower OTP bits,
+  bitwise (1-bit) programmable
+- mid OTP (OTP128 to OTP255) = 4096 middle OTP bits,
+  bulk (32-bit) programmable
+- upper OTP (OTP256 to OTP383) = 4096 upper OTP bits,
+  bulk (32-bit) programmable,
+  only accessible when BSEC is in closed state.
+
+As HWKEY and ECIES key are only accessible by ROM code;
+only 368 OTP words are managed in this driver (OTP0 to OTP267).
+
+This patch adds the STM32MP25 configuration for reading and writing
+the OTP data using the OP-TEE BSEC TA services.
+
+Signed-off-by: Patrick Delaunay <patrick.delaunay@foss.st.com>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20231215111536.316972-11-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/stm32-romem.c | 16 ++++++++++++++++
+ 1 file changed, 16 insertions(+)
+
+--- a/drivers/nvmem/stm32-romem.c
++++ b/drivers/nvmem/stm32-romem.c
+@@ -269,6 +269,19 @@ static const struct stm32_romem_cfg stm3
+       .ta = true,
+ };
++/*
++ * STM32MP25 BSEC OTP: 3 regions of 32-bits data words
++ *   lower OTP (OTP0 to OTP127), bitwise (1-bit) programmable
++ *   mid OTP (OTP128 to OTP255), bulk (32-bit) programmable
++ *   upper OTP (OTP256 to OTP383), bulk (32-bit) programmable
++ *              but no access to HWKEY and ECIES key: limited at OTP367
++ */
++static const struct stm32_romem_cfg stm32mp25_bsec_cfg = {
++      .size = 368 * 4,
++      .lower = 127,
++      .ta = true,
++};
++
+ static const struct of_device_id stm32_romem_of_match[] __maybe_unused = {
+       { .compatible = "st,stm32f4-otp", }, {
+               .compatible = "st,stm32mp15-bsec",
+@@ -276,6 +289,9 @@ static const struct of_device_id stm32_r
+       }, {
+               .compatible = "st,stm32mp13-bsec",
+               .data = (void *)&stm32mp13_bsec_cfg,
++      }, {
++              .compatible = "st,stm32mp25-bsec",
++              .data = (void *)&stm32mp25_bsec_cfg,
+       },
+       { /* sentinel */ },
+ };
index 95f29b1865dd5f0d3734198bbbda6b12e2a97778..9bb94a28b54b6ac07d79dfc65188290709bc5a24 100644 (file)
@@ -15,9 +15,9 @@ string.
  menuconfig NVMEM
        bool "NVMEM Support"
 +      select GENERIC_NET_UTILS
+       imply NVMEM_LAYOUTS
        help
          Support for NVMEM(Non Volatile Memory) devices like EEPROM, EFUSES...
 --- a/drivers/nvmem/core.c
 +++ b/drivers/nvmem/core.c
 @@ -7,9 +7,12 @@
@@ -33,7 +33,7 @@ string.
  #include <linux/init.h>
  #include <linux/kref.h>
  #include <linux/module.h>
-@@ -696,6 +699,62 @@ static int nvmem_validate_keepouts(struc
+@@ -780,6 +783,62 @@ static int nvmem_validate_keepouts(struc
        return 0;
  }
  
@@ -95,10 +95,10 @@ string.
 +
  static int nvmem_add_cells_from_dt(struct nvmem_device *nvmem, struct device_node *np)
  {
-       struct nvmem_layout *layout = nvmem->layout;
-@@ -731,6 +790,25 @@ static int nvmem_add_cells_from_dt(struc
-               if (layout && layout->fixup_cell_info)
-                       layout->fixup_cell_info(nvmem, layout, &info);
+       struct device *dev = &nvmem->dev;
+@@ -814,6 +873,25 @@ static int nvmem_add_cells_from_dt(struc
+               if (nvmem->fixup_dt_cell_info)
+                       nvmem->fixup_dt_cell_info(nvmem, &info);
  
 +              if (of_device_is_compatible(np, "fixed-layout")) {
 +                      if (of_device_is_compatible(child, "mac-base")) {
index 95f29b1865dd5f0d3734198bbbda6b12e2a97778..9bb94a28b54b6ac07d79dfc65188290709bc5a24 100644 (file)
@@ -15,9 +15,9 @@ string.
  menuconfig NVMEM
        bool "NVMEM Support"
 +      select GENERIC_NET_UTILS
+       imply NVMEM_LAYOUTS
        help
          Support for NVMEM(Non Volatile Memory) devices like EEPROM, EFUSES...
 --- a/drivers/nvmem/core.c
 +++ b/drivers/nvmem/core.c
 @@ -7,9 +7,12 @@
@@ -33,7 +33,7 @@ string.
  #include <linux/init.h>
  #include <linux/kref.h>
  #include <linux/module.h>
-@@ -696,6 +699,62 @@ static int nvmem_validate_keepouts(struc
+@@ -780,6 +783,62 @@ static int nvmem_validate_keepouts(struc
        return 0;
  }
  
@@ -95,10 +95,10 @@ string.
 +
  static int nvmem_add_cells_from_dt(struct nvmem_device *nvmem, struct device_node *np)
  {
-       struct nvmem_layout *layout = nvmem->layout;
-@@ -731,6 +790,25 @@ static int nvmem_add_cells_from_dt(struc
-               if (layout && layout->fixup_cell_info)
-                       layout->fixup_cell_info(nvmem, layout, &info);
+       struct device *dev = &nvmem->dev;
+@@ -814,6 +873,25 @@ static int nvmem_add_cells_from_dt(struc
+               if (nvmem->fixup_dt_cell_info)
+                       nvmem->fixup_dt_cell_info(nvmem, &info);
  
 +              if (of_device_is_compatible(np, "fixed-layout")) {
 +                      if (of_device_is_compatible(child, "mac-base")) {