aboutsummaryrefslogtreecommitdiffstats
path: root/target/linux/generic/backport-5.15/811-v6.4-0002-nvmem-core-introduce-NVMEM-layouts.patch
diff options
context:
space:
mode:
authorRafał Miłecki <rafal@milecki.pl>2023-04-06 09:15:49 +0200
committerRafał Miłecki <rafal@milecki.pl>2023-04-06 12:13:22 +0200
commit323072f3a6fb709d5a20dbd1375816e8c041a85b (patch)
tree5ec6de04cfb3fd1ca4a36fa817dd9ce2b9dcd1bf /target/linux/generic/backport-5.15/811-v6.4-0002-nvmem-core-introduce-NVMEM-layouts.patch
parentb67ba02fc848cee5175cfdefe81e10adf195c435 (diff)
downloadupstream-323072f3a6fb709d5a20dbd1375816e8c041a85b.tar.gz
upstream-323072f3a6fb709d5a20dbd1375816e8c041a85b.tar.bz2
upstream-323072f3a6fb709d5a20dbd1375816e8c041a85b.zip
kernel: backport NVMEM patches queued for the v6.4
They add NVMEM layouts support. It allows handling NVMEM content independently of NVMEM device access. Skip U-Boot env data patch for now as it break our downstream MAC hacks. Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
Diffstat (limited to 'target/linux/generic/backport-5.15/811-v6.4-0002-nvmem-core-introduce-NVMEM-layouts.patch')
-rw-r--r--target/linux/generic/backport-5.15/811-v6.4-0002-nvmem-core-introduce-NVMEM-layouts.patch387
1 files changed, 387 insertions, 0 deletions
diff --git a/target/linux/generic/backport-5.15/811-v6.4-0002-nvmem-core-introduce-NVMEM-layouts.patch b/target/linux/generic/backport-5.15/811-v6.4-0002-nvmem-core-introduce-NVMEM-layouts.patch
new file mode 100644
index 0000000000..23518d21f7
--- /dev/null
+++ b/target/linux/generic/backport-5.15/811-v6.4-0002-nvmem-core-introduce-NVMEM-layouts.patch
@@ -0,0 +1,387 @@
+From 266570f496b90dea8fda893c2cf7c28d63ae2bd9 Mon Sep 17 00:00:00 2001
+From: Michael Walle <michael@walle.cc>
+Date: Tue, 4 Apr 2023 18:21:21 +0100
+Subject: [PATCH] nvmem: core: introduce NVMEM layouts
+
+NVMEM layouts are used to generate NVMEM cells during runtime. Think of
+an EEPROM with a well-defined conent. For now, the content can be
+described by a device tree or a board file. But this only works if the
+offsets and lengths are static and don't change. One could also argue
+that putting the layout of the EEPROM in the device tree is the wrong
+place. Instead, the device tree should just have a specific compatible
+string.
+
+Right now there are two use cases:
+ (1) The NVMEM cell needs special processing. E.g. if it only specifies
+ a base MAC address offset and you need to add an offset, or it
+ needs to parse a MAC from ASCII format or some proprietary format.
+ (Post processing of cells is added in a later commit).
+ (2) u-boot environment parsing. The cells don't have a particular
+ offset but it needs parsing the content to determine the offsets
+ and length.
+
+Co-developed-by: Miquel Raynal <miquel.raynal@bootlin.com>
+Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
+Signed-off-by: Michael Walle <michael@walle.cc>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20230404172148.82422-14-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ Documentation/driver-api/nvmem.rst | 15 ++++
+ drivers/nvmem/Kconfig | 4 +
+ drivers/nvmem/Makefile | 1 +
+ drivers/nvmem/core.c | 120 +++++++++++++++++++++++++++++
+ drivers/nvmem/layouts/Kconfig | 5 ++
+ drivers/nvmem/layouts/Makefile | 4 +
+ include/linux/nvmem-consumer.h | 7 ++
+ include/linux/nvmem-provider.h | 51 ++++++++++++
+ 8 files changed, 207 insertions(+)
+ create mode 100644 drivers/nvmem/layouts/Kconfig
+ create mode 100644 drivers/nvmem/layouts/Makefile
+
+--- a/Documentation/driver-api/nvmem.rst
++++ b/Documentation/driver-api/nvmem.rst
+@@ -189,3 +189,18 @@ ex::
+ =====================
+
+ See Documentation/devicetree/bindings/nvmem/nvmem.txt
++
++8. NVMEM layouts
++================
++
++NVMEM layouts are yet another mechanism to create cells. With the device
++tree binding it is possible to specify simple cells by using an offset
++and a length. Sometimes, the cells doesn't have a static offset, but
++the content is still well defined, e.g. tag-length-values. In this case,
++the NVMEM device content has to be first parsed and the cells need to
++be added accordingly. Layouts let you read the content of the NVMEM device
++and let you add cells dynamically.
++
++Another use case for layouts is the post processing of cells. With layouts,
++it is possible to associate a custom post processing hook to a cell. It
++even possible to add this hook to cells not created by the layout itself.
+--- a/drivers/nvmem/Kconfig
++++ b/drivers/nvmem/Kconfig
+@@ -21,6 +21,10 @@ config NVMEM_SYSFS
+ This interface is mostly used by userspace applications to
+ read/write directly into nvmem.
+
++# Layouts
++
++source "drivers/nvmem/layouts/Kconfig"
++
+ # Devices
+
+ config NVMEM_APPLE_EFUSES
+--- a/drivers/nvmem/Makefile
++++ b/drivers/nvmem/Makefile
+@@ -5,6 +5,7 @@
+
+ obj-$(CONFIG_NVMEM) += nvmem_core.o
+ nvmem_core-y := core.o
++obj-y += layouts/
+
+ # Devices
+ obj-$(CONFIG_NVMEM_APPLE_EFUSES) += nvmem-apple-efuses.o
+--- a/drivers/nvmem/core.c
++++ b/drivers/nvmem/core.c
+@@ -40,6 +40,7 @@ struct nvmem_device {
+ nvmem_reg_write_t reg_write;
+ nvmem_cell_post_process_t cell_post_process;
+ struct gpio_desc *wp_gpio;
++ struct nvmem_layout *layout;
+ void *priv;
+ };
+
+@@ -74,6 +75,9 @@ 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)
+ {
+@@ -728,6 +732,101 @@ static int nvmem_add_cells_from_of(struc
+ return 0;
+ }
+
++int __nvmem_layout_register(struct nvmem_layout *layout, struct module *owner)
++{
++ layout->owner = owner;
++
++ spin_lock(&nvmem_layout_lock);
++ list_add(&layout->node, &nvmem_layouts);
++ spin_unlock(&nvmem_layout_lock);
++
++ return 0;
++}
++EXPORT_SYMBOL_GPL(__nvmem_layout_register);
++
++void nvmem_layout_unregister(struct nvmem_layout *layout)
++{
++ spin_lock(&nvmem_layout_lock);
++ list_del(&layout->node);
++ spin_unlock(&nvmem_layout_lock);
++}
++EXPORT_SYMBOL_GPL(nvmem_layout_unregister);
++
++static struct nvmem_layout *nvmem_layout_get(struct nvmem_device *nvmem)
++{
++ struct device_node *layout_np, *np = nvmem->dev.of_node;
++ struct nvmem_layout *l, *layout = NULL;
++
++ layout_np = of_get_child_by_name(np, "nvmem-layout");
++ if (!layout_np)
++ return NULL;
++
++ 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, layout);
++ if (ret)
++ return ret;
++ }
++
++ return 0;
++}
++
++#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");
++}
++EXPORT_SYMBOL_GPL(of_nvmem_layout_get_container);
++#endif
++
++const void *nvmem_layout_get_match_data(struct nvmem_device *nvmem,
++ struct nvmem_layout *layout)
++{
++ struct device_node __maybe_unused *layout_np;
++ const struct of_device_id *match;
++
++ layout_np = of_nvmem_layout_get_container(nvmem);
++ match = of_match_node(layout->of_match_table, layout_np);
++
++ return match ? match->data : NULL;
++}
++EXPORT_SYMBOL_GPL(nvmem_layout_get_match_data);
++
+ /**
+ * nvmem_register() - Register a nvmem device for given nvmem_config.
+ * Also creates a binary entry in /sys/bus/nvmem/devices/dev-name/nvmem
+@@ -834,6 +933,12 @@ 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 (config->cells) {
+ rval = nvmem_add_cells(nvmem, config->cells, config->ncells);
+ if (rval)
+@@ -854,12 +959,17 @@ 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;
++
+ blocking_notifier_call_chain(&nvmem_notifier, NVMEM_ADD, nvmem);
+
+ return nvmem;
+
+ err_remove_cells:
+ nvmem_device_remove_all_cells(nvmem);
++ nvmem_layout_put(nvmem->layout);
+ if (config->compat)
+ nvmem_sysfs_remove_compat(nvmem, config);
+ err_put_device:
+@@ -881,6 +991,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);
+ device_unregister(&nvmem->dev);
+ }
+
+@@ -1246,6 +1357,15 @@ struct nvmem_cell *of_nvmem_cell_get(str
+ return ERR_PTR(-EINVAL);
+ }
+
++ /* nvmem layouts produce cells within the nvmem-layout container */
++ if (of_node_name_eq(nvmem_np, "nvmem-layout")) {
++ nvmem_np = of_get_next_parent(nvmem_np);
++ if (!nvmem_np) {
++ of_node_put(cell_np);
++ return ERR_PTR(-EINVAL);
++ }
++ }
++
+ nvmem = __nvmem_device_get(nvmem_np, device_match_of_node);
+ of_node_put(nvmem_np);
+ if (IS_ERR(nvmem)) {
+--- /dev/null
++++ b/drivers/nvmem/layouts/Kconfig
+@@ -0,0 +1,5 @@
++# SPDX-License-Identifier: GPL-2.0
++
++menu "Layout Types"
++
++endmenu
+--- /dev/null
++++ b/drivers/nvmem/layouts/Makefile
+@@ -0,0 +1,4 @@
++# SPDX-License-Identifier: GPL-2.0
++#
++# Makefile for nvmem layouts.
++#
+--- a/include/linux/nvmem-consumer.h
++++ b/include/linux/nvmem-consumer.h
+@@ -239,6 +239,7 @@ 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)
+@@ -251,6 +252,12 @@ 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 ERR_PTR(-EOPNOTSUPP);
++}
+ #endif /* CONFIG_NVMEM && CONFIG_OF */
+
+ #endif /* ifndef _LINUX_NVMEM_CONSUMER_H */
+--- a/include/linux/nvmem-provider.h
++++ b/include/linux/nvmem-provider.h
+@@ -88,6 +88,7 @@ struct nvmem_cell_info {
+ * @stride: Minimum read/write access stride.
+ * @priv: User context passed to read/write callbacks.
+ * @ignore_wp: Write Protect pin is managed by the provider.
++ * @layout: Fixed layout associated with this nvmem device.
+ *
+ * Note: A default "nvmem<id>" name will be assigned to the device if
+ * no name is specified in its configuration. In such case "<id>" is
+@@ -109,6 +110,7 @@ struct nvmem_config {
+ bool read_only;
+ bool root_only;
+ bool ignore_wp;
++ struct nvmem_layout *layout;
+ struct device_node *of_node;
+ bool no_of_node;
+ nvmem_reg_read_t reg_read;
+@@ -142,6 +144,33 @@ struct nvmem_cell_table {
+ struct list_head node;
+ };
+
++/**
++ * struct nvmem_layout - NVMEM layout definitions
++ *
++ * @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().
++ * @owner: Pointer to struct module.
++ * @node: List node.
++ *
++ * 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"
++ * pairs. A nvmem layout can parse the nvmem device and add appropriate
++ * cells.
++ */
++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);
++
++ /* private */
++ struct module *owner;
++ struct list_head node;
++};
++
+ #if IS_ENABLED(CONFIG_NVMEM)
+
+ struct nvmem_device *nvmem_register(const struct nvmem_config *cfg);
+@@ -156,6 +185,14 @@ 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)
++void nvmem_layout_unregister(struct nvmem_layout *layout);
++
++const void *nvmem_layout_get_match_data(struct nvmem_device *nvmem,
++ struct nvmem_layout *layout);
++
+ #else
+
+ static inline struct nvmem_device *nvmem_register(const struct nvmem_config *c)
+@@ -179,5 +216,19 @@ static inline int nvmem_add_one_cell(str
+ return -EOPNOTSUPP;
+ }
+
++static inline int nvmem_layout_register(struct nvmem_layout *layout)
++{
++ return -EOPNOTSUPP;
++}
++
++static inline void nvmem_layout_unregister(struct nvmem_layout *layout) {}
++
++static inline const void *
++nvmem_layout_get_match_data(struct nvmem_device *nvmem,
++ struct nvmem_layout *layout)
++{
++ return NULL;
++}
++
+ #endif /* CONFIG_NVMEM */
+ #endif /* ifndef _LINUX_NVMEM_PROVIDER_H */