mirror of
				git://git.openwrt.org/openwrt/openwrt.git
				synced 2025-11-04 06:54:27 -05:00 
			
		
		
		
	kernel: backport NVMEM changes queued for v6.8
Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
This commit is contained in:
		
							parent
							
								
									facaa13a3f
								
							
						
					
					
						commit
						493f7f5eee
					
				@ -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 */
 | 
			
		||||
@ -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)
 | 
			
		||||
@ -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 */
 | 
			
		||||
@ -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);
 | 
			
		||||
@ -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;
 | 
			
		||||
@ -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 */
 | 
			
		||||
@ -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)
 | 
			
		||||
@ -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 */ },
 | 
			
		||||
 };
 | 
			
		||||
@ -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 */
 | 
			
		||||
@ -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)
 | 
			
		||||
@ -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 */
 | 
			
		||||
@ -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);
 | 
			
		||||
@ -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;
 | 
			
		||||
@ -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 */
 | 
			
		||||
@ -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)
 | 
			
		||||
@ -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 */ },
 | 
			
		||||
 };
 | 
			
		||||
@ -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")) {
 | 
			
		||||
 | 
			
		||||
@ -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")) {
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user