From: Rafał Miłecki Date: Sun, 17 Dec 2023 21:35:13 +0000 (+0100) Subject: kernel: backport NVMEM changes queued for v6.8 X-Git-Url: http://git.openwrt.org/?a=commitdiff_plain;h=493f7f5eeec4e97ff9da9cfb23c26d7c273b44c9;p=openwrt%2Fstaging%2Fjow.git kernel: backport NVMEM changes queued for v6.8 Signed-off-by: Rafał Miłecki --- diff --git a/target/linux/generic/backport-5.15/832-v6.8-of-device-Export-of_device_make_bus_id.patch b/target/linux/generic/backport-5.15/832-v6.8-of-device-Export-of_device_make_bus_id.patch new file mode 100644 index 0000000000..d097c1b0f4 --- /dev/null +++ b/target/linux/generic/backport-5.15/832-v6.8-of-device-Export-of_device_make_bus_id.patch @@ -0,0 +1,128 @@ +From 7f38b70042fcaa49219045bd1a9a2836e27a58ac Mon Sep 17 00:00:00 2001 +From: Miquel Raynal +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 +Acked-by: Rob Herring +Signed-off-by: Srinivas Kandagatla +Link: https://lore.kernel.org/r/20231215111536.316972-2-srinivas.kandagatla@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- + drivers/of/device.c | 41 +++++++++++++++++++++++++++++++++++++++ + drivers/of/platform.c | 40 -------------------------------------- + include/linux/of_device.h | 6 ++++++ + 3 files changed, 47 insertions(+), 40 deletions(-) + +--- a/drivers/of/device.c ++++ b/drivers/of/device.c +@@ -337,3 +337,38 @@ int of_device_uevent_modalias(struct dev + return 0; + } + EXPORT_SYMBOL_GPL(of_device_uevent_modalias); ++ ++/** ++ * of_device_make_bus_id - Use the device node data to assign a unique name ++ * @dev: pointer to device structure that is linked to a device tree node ++ * ++ * This routine will first try using the translated bus address to ++ * derive a unique name. If it cannot, then it will prepend names from ++ * parent nodes until a unique name can be derived. ++ */ ++void of_device_make_bus_id(struct device *dev) ++{ ++ struct device_node *node = dev->of_node; ++ const __be32 *reg; ++ u64 addr; ++ ++ /* Construct the name, using parent nodes if necessary to ensure uniqueness */ ++ while (node->parent) { ++ /* ++ * If the address can be translated, then that is as much ++ * uniqueness as we need. Make it the first component and return ++ */ ++ reg = of_get_property(node, "reg", NULL); ++ if (reg && (addr = of_translate_address(node, reg)) != OF_BAD_ADDR) { ++ dev_set_name(dev, dev_name(dev) ? "%llx.%pOFn:%s" : "%llx.%pOFn", ++ addr, node, dev_name(dev)); ++ return; ++ } ++ ++ /* format arguments only used if dev_name() resolves to NULL */ ++ dev_set_name(dev, dev_name(dev) ? "%s:%s" : "%s", ++ kbasename(node->full_name), dev_name(dev)); ++ node = node->parent; ++ } ++} ++EXPORT_SYMBOL_GPL(of_device_make_bus_id); +--- a/drivers/of/platform.c ++++ b/drivers/of/platform.c +@@ -64,40 +64,6 @@ EXPORT_SYMBOL(of_find_device_by_node); + */ + + /** +- * of_device_make_bus_id - Use the device node data to assign a unique name +- * @dev: pointer to device structure that is linked to a device tree node +- * +- * This routine will first try using the translated bus address to +- * derive a unique name. If it cannot, then it will prepend names from +- * parent nodes until a unique name can be derived. +- */ +-static void of_device_make_bus_id(struct device *dev) +-{ +- struct device_node *node = dev->of_node; +- const __be32 *reg; +- u64 addr; +- +- /* Construct the name, using parent nodes if necessary to ensure uniqueness */ +- while (node->parent) { +- /* +- * If the address can be translated, then that is as much +- * uniqueness as we need. Make it the first component and return +- */ +- reg = of_get_property(node, "reg", NULL); +- if (reg && (addr = of_translate_address(node, reg)) != OF_BAD_ADDR) { +- dev_set_name(dev, dev_name(dev) ? "%llx.%pOFn:%s" : "%llx.%pOFn", +- addr, node, dev_name(dev)); +- return; +- } +- +- /* format arguments only used if dev_name() resolves to NULL */ +- dev_set_name(dev, dev_name(dev) ? "%s:%s" : "%s", +- kbasename(node->full_name), dev_name(dev)); +- node = node->parent; +- } +-} +- +-/** + * of_device_alloc - Allocate and initialize an of_device + * @np: device node to assign to device + * @bus_id: Name to assign to the device. May be null to use default name. +--- a/include/linux/of_device.h ++++ b/include/linux/of_device.h +@@ -56,6 +56,9 @@ static inline int of_dma_configure(struc + { + return of_dma_configure_id(dev, np, force_dma, NULL); + } ++ ++void of_device_make_bus_id(struct device *dev); ++ + #else /* CONFIG_OF */ + + static inline int of_driver_match_device(struct device *dev, +@@ -113,6 +116,9 @@ static inline int of_dma_configure(struc + { + return 0; + } ++ ++static inline void of_device_make_bus_id(struct device *dev) {} ++ + #endif /* CONFIG_OF */ + + #endif /* _LINUX_OF_DEVICE_H */ diff --git a/target/linux/generic/backport-5.15/834-v6.8-0001-nvmem-Move-of_nvmem_layout_get_container-in-another-.patch b/target/linux/generic/backport-5.15/834-v6.8-0001-nvmem-Move-of_nvmem_layout_get_container-in-another-.patch new file mode 100644 index 0000000000..2093fac8a1 --- /dev/null +++ b/target/linux/generic/backport-5.15/834-v6.8-0001-nvmem-Move-of_nvmem_layout_get_container-in-another-.patch @@ -0,0 +1,95 @@ +From 4a1a40233b4a9fc159a5c7a27dc34c5c7bc5be55 Mon Sep 17 00:00:00 2001 +From: Miquel Raynal +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 +Signed-off-by: Srinivas Kandagatla +Link: https://lore.kernel.org/r/20231215111536.316972-3-srinivas.kandagatla@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- + drivers/nvmem/core.c | 8 -------- + include/linux/nvmem-consumer.h | 7 ------- + include/linux/nvmem-provider.h | 21 +++++++++++++++++++++ + 3 files changed, 21 insertions(+), 15 deletions(-) + +--- a/drivers/nvmem/core.c ++++ b/drivers/nvmem/core.c +@@ -848,14 +848,6 @@ static int nvmem_add_cells_from_layout(s + } + + #if IS_ENABLED(CONFIG_OF) +-/** +- * of_nvmem_layout_get_container() - Get OF node to layout container. +- * +- * @nvmem: nvmem device. +- * +- * Return: a node pointer with refcount incremented or NULL if no +- * container exists. Use of_node_put() on it when done. +- */ + struct device_node *of_nvmem_layout_get_container(struct nvmem_device *nvmem) + { + return of_get_child_by_name(nvmem->dev.of_node, "nvmem-layout"); +--- a/include/linux/nvmem-consumer.h ++++ b/include/linux/nvmem-consumer.h +@@ -241,7 +241,6 @@ struct nvmem_cell *of_nvmem_cell_get(str + const char *id); + struct nvmem_device *of_nvmem_device_get(struct device_node *np, + const char *name); +-struct device_node *of_nvmem_layout_get_container(struct nvmem_device *nvmem); + #else + static inline struct nvmem_cell *of_nvmem_cell_get(struct device_node *np, + const char *id) +@@ -254,12 +253,6 @@ static inline struct nvmem_device *of_nv + { + return ERR_PTR(-EOPNOTSUPP); + } +- +-static inline struct device_node * +-of_nvmem_layout_get_container(struct nvmem_device *nvmem) +-{ +- return NULL; +-} + #endif /* CONFIG_NVMEM && CONFIG_OF */ + + #endif /* ifndef _LINUX_NVMEM_CONSUMER_H */ +--- a/include/linux/nvmem-provider.h ++++ b/include/linux/nvmem-provider.h +@@ -244,6 +244,27 @@ nvmem_layout_get_match_data(struct nvmem + + #endif /* CONFIG_NVMEM */ + ++#if IS_ENABLED(CONFIG_NVMEM) && IS_ENABLED(CONFIG_OF) ++ ++/** ++ * of_nvmem_layout_get_container() - Get OF node of layout container ++ * ++ * @nvmem: nvmem device ++ * ++ * Return: a node pointer with refcount incremented or NULL if no ++ * container exists. Use of_node_put() on it when done. ++ */ ++struct device_node *of_nvmem_layout_get_container(struct nvmem_device *nvmem); ++ ++#else /* CONFIG_NVMEM && CONFIG_OF */ ++ ++static inline struct device_node *of_nvmem_layout_get_container(struct nvmem_device *nvmem) ++{ ++ return NULL; ++} ++ ++#endif /* CONFIG_NVMEM && CONFIG_OF */ ++ + #define module_nvmem_layout_driver(__layout_driver) \ + module_driver(__layout_driver, nvmem_layout_register, \ + nvmem_layout_unregister) diff --git a/target/linux/generic/backport-5.15/834-v6.8-0002-nvmem-Create-a-header-for-internal-sharing.patch b/target/linux/generic/backport-5.15/834-v6.8-0002-nvmem-Create-a-header-for-internal-sharing.patch new file mode 100644 index 0000000000..e722109f91 --- /dev/null +++ b/target/linux/generic/backport-5.15/834-v6.8-0002-nvmem-Create-a-header-for-internal-sharing.patch @@ -0,0 +1,91 @@ +From ec9c08a1cb8dc5e8e003f95f5f62de41dde235bb Mon Sep 17 00:00:00 2001 +From: Miquel Raynal +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 +Signed-off-by: Srinivas Kandagatla +Link: https://lore.kernel.org/r/20231215111536.316972-4-srinivas.kandagatla@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- + 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 + #include + +-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 ++#include ++#include ++ ++struct nvmem_device { ++ struct module *owner; ++ struct device dev; ++ struct list_head node; ++ int stride; ++ int word_size; ++ int id; ++ struct kref refcnt; ++ size_t size; ++ bool read_only; ++ bool root_only; ++ int flags; ++ enum nvmem_type type; ++ struct bin_attribute eeprom; ++ struct device *base_dev; ++ struct list_head cells; ++ const struct nvmem_keepout *keepout; ++ unsigned int nkeepout; ++ nvmem_reg_read_t reg_read; ++ nvmem_reg_write_t reg_write; ++ struct gpio_desc *wp_gpio; ++ struct nvmem_layout *layout; ++ void *priv; ++}; ++ ++#endif /* ifndef _LINUX_NVMEM_INTERNALS_H */ diff --git a/target/linux/generic/backport-5.15/834-v6.8-0003-nvmem-Simplify-the-add_cells-hook.patch b/target/linux/generic/backport-5.15/834-v6.8-0003-nvmem-Simplify-the-add_cells-hook.patch new file mode 100644 index 0000000000..db2d8c1b46 --- /dev/null +++ b/target/linux/generic/backport-5.15/834-v6.8-0003-nvmem-Simplify-the-add_cells-hook.patch @@ -0,0 +1,79 @@ +From 1b7c298a4ecbc28cc6ee94005734bff55eb83d22 Mon Sep 17 00:00:00 2001 +From: Miquel Raynal +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 +Signed-off-by: Srinivas Kandagatla +Link: https://lore.kernel.org/r/20231215111536.316972-5-srinivas.kandagatla@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- + drivers/nvmem/core.c | 2 +- + drivers/nvmem/layouts/onie-tlv.c | 3 +-- + drivers/nvmem/layouts/sl28vpd.c | 3 +-- + include/linux/nvmem-provider.h | 8 +++----- + 4 files changed, 6 insertions(+), 10 deletions(-) + +--- a/drivers/nvmem/core.c ++++ b/drivers/nvmem/core.c +@@ -817,7 +817,7 @@ static int nvmem_add_cells_from_layout(s + int ret; + + if (layout && layout->add_cells) { +- ret = layout->add_cells(&nvmem->dev, nvmem, layout); ++ ret = layout->add_cells(&nvmem->dev, nvmem); + if (ret) + return ret; + } +--- a/drivers/nvmem/layouts/onie-tlv.c ++++ b/drivers/nvmem/layouts/onie-tlv.c +@@ -182,8 +182,7 @@ static bool onie_tlv_crc_is_valid(struct + return true; + } + +-static int onie_tlv_parse_table(struct device *dev, struct nvmem_device *nvmem, +- struct nvmem_layout *layout) ++static int onie_tlv_parse_table(struct device *dev, struct nvmem_device *nvmem) + { + struct onie_tlv_hdr hdr; + size_t table_len, data_len, hdr_len; +--- a/drivers/nvmem/layouts/sl28vpd.c ++++ b/drivers/nvmem/layouts/sl28vpd.c +@@ -80,8 +80,7 @@ static int sl28vpd_v1_check_crc(struct d + return 0; + } + +-static int sl28vpd_add_cells(struct device *dev, struct nvmem_device *nvmem, +- struct nvmem_layout *layout) ++static int sl28vpd_add_cells(struct device *dev, struct nvmem_device *nvmem) + { + const struct nvmem_cell_info *pinfo; + struct nvmem_cell_info info = {0}; +--- a/include/linux/nvmem-provider.h ++++ b/include/linux/nvmem-provider.h +@@ -156,9 +156,8 @@ struct nvmem_cell_table { + * + * @name: Layout name. + * @of_match_table: Open firmware match table. +- * @add_cells: Will be called if a nvmem device is found which +- * has this layout. The function will add layout +- * specific cells with nvmem_add_one_cell(). ++ * @add_cells: Called to populate the layout using ++ * nvmem_add_one_cell(). + * @fixup_cell_info: Will be called before a cell is added. Can be + * used to modify the nvmem_cell_info. + * @owner: Pointer to struct module. +@@ -172,8 +171,7 @@ struct nvmem_cell_table { + struct nvmem_layout { + const char *name; + const struct of_device_id *of_match_table; +- int (*add_cells)(struct device *dev, struct nvmem_device *nvmem, +- struct nvmem_layout *layout); ++ int (*add_cells)(struct device *dev, struct nvmem_device *nvmem); + void (*fixup_cell_info)(struct nvmem_device *nvmem, + struct nvmem_layout *layout, + struct nvmem_cell_info *cell); diff --git a/target/linux/generic/backport-5.15/834-v6.8-0004-nvmem-Move-and-rename-fixup_cell_info.patch b/target/linux/generic/backport-5.15/834-v6.8-0004-nvmem-Move-and-rename-fixup_cell_info.patch new file mode 100644 index 0000000000..65aa37f834 --- /dev/null +++ b/target/linux/generic/backport-5.15/834-v6.8-0004-nvmem-Move-and-rename-fixup_cell_info.patch @@ -0,0 +1,169 @@ +From 1172460e716784ac7e1049a537bdca8edbf97360 Mon Sep 17 00:00:00 2001 +From: Miquel Raynal +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 +Signed-off-by: Srinivas Kandagatla +Link: https://lore.kernel.org/r/20231215111536.316972-6-srinivas.kandagatla@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- + drivers/nvmem/core.c | 6 +++--- + drivers/nvmem/imx-ocotp.c | 11 +++-------- + drivers/nvmem/internals.h | 2 ++ + drivers/nvmem/mtk-efuse.c | 11 +++-------- + include/linux/nvmem-provider.h | 9 ++++----- + 5 files changed, 15 insertions(+), 24 deletions(-) + +--- a/drivers/nvmem/core.c ++++ b/drivers/nvmem/core.c +@@ -676,7 +676,6 @@ static int nvmem_validate_keepouts(struc + + static int nvmem_add_cells_from_dt(struct nvmem_device *nvmem, struct device_node *np) + { +- struct nvmem_layout *layout = nvmem->layout; + struct device *dev = &nvmem->dev; + struct device_node *child; + const __be32 *addr; +@@ -706,8 +705,8 @@ static int nvmem_add_cells_from_dt(struc + + info.np = of_node_get(child); + +- if (layout && layout->fixup_cell_info) +- layout->fixup_cell_info(nvmem, layout, &info); ++ if (nvmem->fixup_dt_cell_info) ++ nvmem->fixup_dt_cell_info(nvmem, &info); + + ret = nvmem_add_one_cell(nvmem, &info); + kfree(info.name); +@@ -896,6 +895,7 @@ struct nvmem_device *nvmem_register(cons + + kref_init(&nvmem->refcnt); + INIT_LIST_HEAD(&nvmem->cells); ++ nvmem->fixup_dt_cell_info = config->fixup_dt_cell_info; + + nvmem->owner = config->owner; + if (!nvmem->owner && config->dev->driver) +--- a/drivers/nvmem/imx-ocotp.c ++++ b/drivers/nvmem/imx-ocotp.c +@@ -584,17 +584,12 @@ static const struct of_device_id imx_oco + }; + MODULE_DEVICE_TABLE(of, imx_ocotp_dt_ids); + +-static void imx_ocotp_fixup_cell_info(struct nvmem_device *nvmem, +- struct nvmem_layout *layout, +- struct nvmem_cell_info *cell) ++static void imx_ocotp_fixup_dt_cell_info(struct nvmem_device *nvmem, ++ struct nvmem_cell_info *cell) + { + cell->read_post_process = imx_ocotp_cell_pp; + } + +-static struct nvmem_layout imx_ocotp_layout = { +- .fixup_cell_info = imx_ocotp_fixup_cell_info, +-}; +- + static int imx_ocotp_probe(struct platform_device *pdev) + { + struct device *dev = &pdev->dev; +@@ -620,7 +615,7 @@ static int imx_ocotp_probe(struct platfo + imx_ocotp_nvmem_config.size = 4 * priv->params->nregs; + imx_ocotp_nvmem_config.dev = dev; + imx_ocotp_nvmem_config.priv = priv; +- imx_ocotp_nvmem_config.layout = &imx_ocotp_layout; ++ imx_ocotp_nvmem_config.fixup_dt_cell_info = &imx_ocotp_fixup_dt_cell_info; + + priv->config = &imx_ocotp_nvmem_config; + +--- a/drivers/nvmem/internals.h ++++ b/drivers/nvmem/internals.h +@@ -23,6 +23,8 @@ struct nvmem_device { + struct bin_attribute eeprom; + struct device *base_dev; + struct list_head cells; ++ void (*fixup_dt_cell_info)(struct nvmem_device *nvmem, ++ struct nvmem_cell_info *cell); + const struct nvmem_keepout *keepout; + unsigned int nkeepout; + nvmem_reg_read_t reg_read; +--- a/drivers/nvmem/mtk-efuse.c ++++ b/drivers/nvmem/mtk-efuse.c +@@ -45,9 +45,8 @@ static int mtk_efuse_gpu_speedbin_pp(voi + return 0; + } + +-static void mtk_efuse_fixup_cell_info(struct nvmem_device *nvmem, +- struct nvmem_layout *layout, +- struct nvmem_cell_info *cell) ++static void mtk_efuse_fixup_dt_cell_info(struct nvmem_device *nvmem, ++ struct nvmem_cell_info *cell) + { + size_t sz = strlen(cell->name); + +@@ -61,10 +60,6 @@ static void mtk_efuse_fixup_cell_info(st + cell->read_post_process = mtk_efuse_gpu_speedbin_pp; + } + +-static struct nvmem_layout mtk_efuse_layout = { +- .fixup_cell_info = mtk_efuse_fixup_cell_info, +-}; +- + static int mtk_efuse_probe(struct platform_device *pdev) + { + struct device *dev = &pdev->dev; +@@ -91,7 +86,7 @@ static int mtk_efuse_probe(struct platfo + econfig.priv = priv; + econfig.dev = dev; + if (pdata->uses_post_processing) +- econfig.layout = &mtk_efuse_layout; ++ econfig.fixup_dt_cell_info = &mtk_efuse_fixup_dt_cell_info; + nvmem = devm_nvmem_register(dev, &econfig); + + return PTR_ERR_OR_ZERO(nvmem); +--- a/include/linux/nvmem-provider.h ++++ b/include/linux/nvmem-provider.h +@@ -83,6 +83,8 @@ struct nvmem_cell_info { + * @cells: Optional array of pre-defined NVMEM cells. + * @ncells: Number of elements in cells. + * @add_legacy_fixed_of_cells: Read fixed NVMEM cells from old OF syntax. ++ * @fixup_dt_cell_info: Will be called before a cell is added. Can be ++ * used to modify the nvmem_cell_info. + * @keepout: Optional array of keepout ranges (sorted ascending by start). + * @nkeepout: Number of elements in the keepout array. + * @type: Type of the nvmem storage +@@ -113,6 +115,8 @@ struct nvmem_config { + const struct nvmem_cell_info *cells; + int ncells; + bool add_legacy_fixed_of_cells; ++ void (*fixup_dt_cell_info)(struct nvmem_device *nvmem, ++ struct nvmem_cell_info *cell); + const struct nvmem_keepout *keepout; + unsigned int nkeepout; + enum nvmem_type type; +@@ -158,8 +162,6 @@ struct nvmem_cell_table { + * @of_match_table: Open firmware match table. + * @add_cells: Called to populate the layout using + * nvmem_add_one_cell(). +- * @fixup_cell_info: Will be called before a cell is added. Can be +- * used to modify the nvmem_cell_info. + * @owner: Pointer to struct module. + * @node: List node. + * +@@ -172,9 +174,6 @@ struct nvmem_layout { + const char *name; + const struct of_device_id *of_match_table; + int (*add_cells)(struct device *dev, struct nvmem_device *nvmem); +- void (*fixup_cell_info)(struct nvmem_device *nvmem, +- struct nvmem_layout *layout, +- struct nvmem_cell_info *cell); + + /* private */ + struct module *owner; diff --git a/target/linux/generic/backport-5.15/834-v6.8-0005-nvmem-core-Rework-layouts-to-become-regular-devices.patch b/target/linux/generic/backport-5.15/834-v6.8-0005-nvmem-core-Rework-layouts-to-become-regular-devices.patch new file mode 100644 index 0000000000..1881332340 --- /dev/null +++ b/target/linux/generic/backport-5.15/834-v6.8-0005-nvmem-core-Rework-layouts-to-become-regular-devices.patch @@ -0,0 +1,763 @@ +From fc29fd821d9ac2ae3d32a722fac39ce874efb883 Mon Sep 17 00:00:00 2001 +From: Miquel Raynal +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 +Tested-by: Rafał Miłecki +Signed-off-by: Srinivas Kandagatla +Link: https://lore.kernel.org/r/20231215111536.316972-7-srinivas.kandagatla@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- + 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 ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#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 + #include + #include + #include +@@ -158,12 +159,11 @@ struct nvmem_cell_table { + /** + * struct nvmem_layout - NVMEM layout definitions + * +- * @name: Layout name. +- * @of_match_table: Open firmware match table. +- * @add_cells: Called to populate the layout using +- * nvmem_add_one_cell(). +- * @owner: Pointer to struct module. +- * @node: List node. ++ * @dev: Device-model layout device. ++ * @nvmem: The underlying NVMEM device ++ * @add_cells: Will be called if a nvmem device is found which ++ * has this layout. The function will add layout ++ * specific cells with nvmem_add_one_cell(). + * + * A nvmem device can hold a well defined structure which can just be + * evaluated during runtime. For example a TLV list, or a list of "name=val" +@@ -171,13 +171,15 @@ struct nvmem_cell_table { + * cells. + */ + struct nvmem_layout { +- const char *name; +- const struct of_device_id *of_match_table; ++ struct device dev; ++ struct nvmem_device *nvmem; + int (*add_cells)(struct device *dev, struct nvmem_device *nvmem); ++}; + +- /* private */ +- struct module *owner; +- struct list_head node; ++struct nvmem_layout_driver { ++ struct device_driver driver; ++ int (*probe)(struct nvmem_layout *layout); ++ void (*remove)(struct nvmem_layout *layout); + }; + + #if IS_ENABLED(CONFIG_NVMEM) +@@ -194,11 +196,15 @@ void nvmem_del_cell_table(struct nvmem_c + int nvmem_add_one_cell(struct nvmem_device *nvmem, + const struct nvmem_cell_info *info); + +-int __nvmem_layout_register(struct nvmem_layout *layout, struct module *owner); +-#define nvmem_layout_register(layout) \ +- __nvmem_layout_register(layout, THIS_MODULE) ++int nvmem_layout_register(struct nvmem_layout *layout); + void nvmem_layout_unregister(struct nvmem_layout *layout); + ++int nvmem_layout_driver_register(struct nvmem_layout_driver *drv); ++void nvmem_layout_driver_unregister(struct nvmem_layout_driver *drv); ++#define module_nvmem_layout_driver(__nvmem_layout_driver) \ ++ module_driver(__nvmem_layout_driver, nvmem_layout_driver_register, \ ++ nvmem_layout_driver_unregister) ++ + const void *nvmem_layout_get_match_data(struct nvmem_device *nvmem, + struct nvmem_layout *layout); + +@@ -262,8 +268,4 @@ static inline struct device_node *of_nvm + + #endif /* CONFIG_NVMEM && CONFIG_OF */ + +-#define module_nvmem_layout_driver(__layout_driver) \ +- module_driver(__layout_driver, nvmem_layout_register, \ +- nvmem_layout_unregister) +- + #endif /* ifndef _LINUX_NVMEM_PROVIDER_H */ diff --git a/target/linux/generic/backport-5.15/834-v6.8-0006-nvmem-core-Expose-cells-through-sysfs.patch b/target/linux/generic/backport-5.15/834-v6.8-0006-nvmem-core-Expose-cells-through-sysfs.patch new file mode 100644 index 0000000000..89872bec2e --- /dev/null +++ b/target/linux/generic/backport-5.15/834-v6.8-0006-nvmem-core-Expose-cells-through-sysfs.patch @@ -0,0 +1,240 @@ +From 0331c611949fffdf486652450901a4dc52bc5cca Mon Sep 17 00:00:00 2001 +From: Miquel Raynal +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 +Tested-by: Rafał Miłecki +Tested-by: Chen-Yu Tsai +Signed-off-by: Srinivas Kandagatla +Link: https://lore.kernel.org/r/20231215111536.316972-9-srinivas.kandagatla@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- + drivers/nvmem/core.c | 135 +++++++++++++++++++++++++++++++++++++- + drivers/nvmem/internals.h | 1 + + 2 files changed, 135 insertions(+), 1 deletion(-) + +--- a/drivers/nvmem/core.c ++++ b/drivers/nvmem/core.c +@@ -300,6 +300,43 @@ static umode_t nvmem_bin_attr_is_visible + return nvmem_bin_attr_get_umode(nvmem); + } + ++static struct nvmem_cell *nvmem_create_cell(struct nvmem_cell_entry *entry, ++ const char *id, int index); ++ ++static ssize_t nvmem_cell_attr_read(struct file *filp, struct kobject *kobj, ++ struct bin_attribute *attr, char *buf, ++ loff_t pos, size_t count) ++{ ++ struct nvmem_cell_entry *entry; ++ struct nvmem_cell *cell = NULL; ++ size_t cell_sz, read_len; ++ void *content; ++ ++ entry = attr->private; ++ cell = nvmem_create_cell(entry, entry->name, 0); ++ if (IS_ERR(cell)) ++ return PTR_ERR(cell); ++ ++ if (!cell) ++ return -EINVAL; ++ ++ content = nvmem_cell_read(cell, &cell_sz); ++ if (IS_ERR(content)) { ++ read_len = PTR_ERR(content); ++ goto destroy_cell; ++ } ++ ++ read_len = min_t(unsigned int, cell_sz - pos, count); ++ memcpy(buf, content + pos, read_len); ++ kfree(content); ++ ++destroy_cell: ++ kfree_const(cell->id); ++ kfree(cell); ++ ++ return read_len; ++} ++ + /* default read/write permissions */ + static struct bin_attribute bin_attr_rw_nvmem = { + .attr = { +@@ -321,11 +358,21 @@ static const struct attribute_group nvme + .is_bin_visible = nvmem_bin_attr_is_visible, + }; + ++/* Cell attributes will be dynamically allocated */ ++static struct attribute_group nvmem_cells_group = { ++ .name = "cells", ++}; ++ + static const struct attribute_group *nvmem_dev_groups[] = { + &nvmem_bin_group, + NULL, + }; + ++static const struct attribute_group *nvmem_cells_groups[] = { ++ &nvmem_cells_group, ++ NULL, ++}; ++ + static struct bin_attribute bin_attr_nvmem_eeprom_compat = { + .attr = { + .name = "eeprom", +@@ -381,6 +428,68 @@ static void nvmem_sysfs_remove_compat(st + device_remove_bin_file(nvmem->base_dev, &nvmem->eeprom); + } + ++static int nvmem_populate_sysfs_cells(struct nvmem_device *nvmem) ++{ ++ struct bin_attribute **cells_attrs, *attrs; ++ struct nvmem_cell_entry *entry; ++ unsigned int ncells = 0, i = 0; ++ int ret = 0; ++ ++ mutex_lock(&nvmem_mutex); ++ ++ if (list_empty(&nvmem->cells) || nvmem->sysfs_cells_populated) { ++ nvmem_cells_group.bin_attrs = NULL; ++ goto unlock_mutex; ++ } ++ ++ /* Allocate an array of attributes with a sentinel */ ++ ncells = list_count_nodes(&nvmem->cells); ++ cells_attrs = devm_kcalloc(&nvmem->dev, ncells + 1, ++ sizeof(struct bin_attribute *), GFP_KERNEL); ++ if (!cells_attrs) { ++ ret = -ENOMEM; ++ goto unlock_mutex; ++ } ++ ++ attrs = devm_kcalloc(&nvmem->dev, ncells, sizeof(struct bin_attribute), GFP_KERNEL); ++ if (!attrs) { ++ ret = -ENOMEM; ++ goto unlock_mutex; ++ } ++ ++ /* Initialize each attribute to take the name and size of the cell */ ++ list_for_each_entry(entry, &nvmem->cells, node) { ++ sysfs_bin_attr_init(&attrs[i]); ++ attrs[i].attr.name = devm_kasprintf(&nvmem->dev, GFP_KERNEL, ++ "%s@%x", entry->name, ++ entry->offset); ++ attrs[i].attr.mode = 0444; ++ attrs[i].size = entry->bytes; ++ attrs[i].read = &nvmem_cell_attr_read; ++ attrs[i].private = entry; ++ if (!attrs[i].attr.name) { ++ ret = -ENOMEM; ++ goto unlock_mutex; ++ } ++ ++ cells_attrs[i] = &attrs[i]; ++ i++; ++ } ++ ++ nvmem_cells_group.bin_attrs = cells_attrs; ++ ++ ret = devm_device_add_groups(&nvmem->dev, nvmem_cells_groups); ++ if (ret) ++ goto unlock_mutex; ++ ++ nvmem->sysfs_cells_populated = true; ++ ++unlock_mutex: ++ mutex_unlock(&nvmem_mutex); ++ ++ return ret; ++} ++ + #else /* CONFIG_NVMEM_SYSFS */ + + static int nvmem_sysfs_setup_compat(struct nvmem_device *nvmem, +@@ -740,11 +849,25 @@ static int nvmem_add_cells_from_fixed_la + + int nvmem_layout_register(struct nvmem_layout *layout) + { ++ int ret; ++ + if (!layout->add_cells) + return -EINVAL; + + /* Populate the cells */ +- return layout->add_cells(&layout->nvmem->dev, layout->nvmem); ++ ret = layout->add_cells(&layout->nvmem->dev, layout->nvmem); ++ if (ret) ++ return ret; ++ ++#ifdef CONFIG_NVMEM_SYSFS ++ ret = nvmem_populate_sysfs_cells(layout->nvmem); ++ if (ret) { ++ nvmem_device_remove_all_cells(layout->nvmem); ++ return ret; ++ } ++#endif ++ ++ return 0; + } + EXPORT_SYMBOL_GPL(nvmem_layout_register); + +@@ -903,10 +1026,20 @@ struct nvmem_device *nvmem_register(cons + if (rval) + goto err_remove_dev; + ++#ifdef CONFIG_NVMEM_SYSFS ++ rval = nvmem_populate_sysfs_cells(nvmem); ++ if (rval) ++ goto err_destroy_layout; ++#endif ++ + blocking_notifier_call_chain(&nvmem_notifier, NVMEM_ADD, nvmem); + + return nvmem; + ++#ifdef CONFIG_NVMEM_SYSFS ++err_destroy_layout: ++ nvmem_destroy_layout(nvmem); ++#endif + err_remove_dev: + device_del(&nvmem->dev); + err_remove_cells: +--- a/drivers/nvmem/internals.h ++++ b/drivers/nvmem/internals.h +@@ -32,6 +32,7 @@ struct nvmem_device { + struct gpio_desc *wp_gpio; + struct nvmem_layout *layout; + void *priv; ++ bool sysfs_cells_populated; + }; + + #if IS_ENABLED(CONFIG_OF) diff --git a/target/linux/generic/backport-5.15/834-v6.8-0007-nvmem-stm32-add-support-for-STM32MP25-BSEC-to-contro.patch b/target/linux/generic/backport-5.15/834-v6.8-0007-nvmem-stm32-add-support-for-STM32MP25-BSEC-to-contro.patch new file mode 100644 index 0000000000..f686222f88 --- /dev/null +++ b/target/linux/generic/backport-5.15/834-v6.8-0007-nvmem-stm32-add-support-for-STM32MP25-BSEC-to-contro.patch @@ -0,0 +1,65 @@ +From f0ac5b23039610619ca4a4805528553ecb6bc815 Mon Sep 17 00:00:00 2001 +From: Patrick Delaunay +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 +Signed-off-by: Srinivas Kandagatla +Link: https://lore.kernel.org/r/20231215111536.316972-11-srinivas.kandagatla@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- + drivers/nvmem/stm32-romem.c | 16 ++++++++++++++++ + 1 file changed, 16 insertions(+) + +--- a/drivers/nvmem/stm32-romem.c ++++ b/drivers/nvmem/stm32-romem.c +@@ -269,6 +269,19 @@ static const struct stm32_romem_cfg stm3 + .ta = true, + }; + ++/* ++ * STM32MP25 BSEC OTP: 3 regions of 32-bits data words ++ * lower OTP (OTP0 to OTP127), bitwise (1-bit) programmable ++ * mid OTP (OTP128 to OTP255), bulk (32-bit) programmable ++ * upper OTP (OTP256 to OTP383), bulk (32-bit) programmable ++ * but no access to HWKEY and ECIES key: limited at OTP367 ++ */ ++static const struct stm32_romem_cfg stm32mp25_bsec_cfg = { ++ .size = 368 * 4, ++ .lower = 127, ++ .ta = true, ++}; ++ + static const struct of_device_id stm32_romem_of_match[] __maybe_unused = { + { .compatible = "st,stm32f4-otp", }, { + .compatible = "st,stm32mp15-bsec", +@@ -276,6 +289,9 @@ static const struct of_device_id stm32_r + }, { + .compatible = "st,stm32mp13-bsec", + .data = (void *)&stm32mp13_bsec_cfg, ++ }, { ++ .compatible = "st,stm32mp25-bsec", ++ .data = (void *)&stm32mp25_bsec_cfg, + }, + { /* sentinel */ }, + }; diff --git a/target/linux/generic/backport-6.1/818-v6.8-of-device-Export-of_device_make_bus_id.patch b/target/linux/generic/backport-6.1/818-v6.8-of-device-Export-of_device_make_bus_id.patch new file mode 100644 index 0000000000..564fe9822d --- /dev/null +++ b/target/linux/generic/backport-6.1/818-v6.8-of-device-Export-of_device_make_bus_id.patch @@ -0,0 +1,140 @@ +From 7f38b70042fcaa49219045bd1a9a2836e27a58ac Mon Sep 17 00:00:00 2001 +From: Miquel Raynal +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 +Acked-by: Rob Herring +Signed-off-by: Srinivas Kandagatla +Link: https://lore.kernel.org/r/20231215111536.316972-2-srinivas.kandagatla@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- + drivers/of/device.c | 41 +++++++++++++++++++++++++++++++++++++++ + drivers/of/platform.c | 40 -------------------------------------- + include/linux/of_device.h | 6 ++++++ + 3 files changed, 47 insertions(+), 40 deletions(-) + +--- a/drivers/of/device.c ++++ b/drivers/of/device.c +@@ -395,3 +395,44 @@ int of_device_uevent_modalias(struct dev + return 0; + } + EXPORT_SYMBOL_GPL(of_device_uevent_modalias); ++ ++/** ++ * of_device_make_bus_id - Use the device node data to assign a unique name ++ * @dev: pointer to device structure that is linked to a device tree node ++ * ++ * This routine will first try using the translated bus address to ++ * derive a unique name. If it cannot, then it will prepend names from ++ * parent nodes until a unique name can be derived. ++ */ ++void of_device_make_bus_id(struct device *dev) ++{ ++ struct device_node *node = dev->of_node; ++ const __be32 *reg; ++ u64 addr; ++ u32 mask; ++ ++ /* Construct the name, using parent nodes if necessary to ensure uniqueness */ ++ while (node->parent) { ++ /* ++ * If the address can be translated, then that is as much ++ * uniqueness as we need. Make it the first component and return ++ */ ++ reg = of_get_property(node, "reg", NULL); ++ if (reg && (addr = of_translate_address(node, reg)) != OF_BAD_ADDR) { ++ if (!of_property_read_u32(node, "mask", &mask)) ++ dev_set_name(dev, dev_name(dev) ? "%llx.%x.%pOFn:%s" : "%llx.%x.%pOFn", ++ addr, ffs(mask) - 1, node, dev_name(dev)); ++ ++ else ++ dev_set_name(dev, dev_name(dev) ? "%llx.%pOFn:%s" : "%llx.%pOFn", ++ addr, node, dev_name(dev)); ++ return; ++ } ++ ++ /* format arguments only used if dev_name() resolves to NULL */ ++ dev_set_name(dev, dev_name(dev) ? "%s:%s" : "%s", ++ kbasename(node->full_name), dev_name(dev)); ++ node = node->parent; ++ } ++} ++EXPORT_SYMBOL_GPL(of_device_make_bus_id); +--- a/drivers/of/platform.c ++++ b/drivers/of/platform.c +@@ -64,46 +64,6 @@ EXPORT_SYMBOL(of_find_device_by_node); + */ + + /** +- * of_device_make_bus_id - Use the device node data to assign a unique name +- * @dev: pointer to device structure that is linked to a device tree node +- * +- * This routine will first try using the translated bus address to +- * derive a unique name. If it cannot, then it will prepend names from +- * parent nodes until a unique name can be derived. +- */ +-static void of_device_make_bus_id(struct device *dev) +-{ +- struct device_node *node = dev->of_node; +- const __be32 *reg; +- u64 addr; +- u32 mask; +- +- /* Construct the name, using parent nodes if necessary to ensure uniqueness */ +- while (node->parent) { +- /* +- * If the address can be translated, then that is as much +- * uniqueness as we need. Make it the first component and return +- */ +- reg = of_get_property(node, "reg", NULL); +- if (reg && (addr = of_translate_address(node, reg)) != OF_BAD_ADDR) { +- if (!of_property_read_u32(node, "mask", &mask)) +- dev_set_name(dev, dev_name(dev) ? "%llx.%x.%pOFn:%s" : "%llx.%x.%pOFn", +- addr, ffs(mask) - 1, node, dev_name(dev)); +- +- else +- dev_set_name(dev, dev_name(dev) ? "%llx.%pOFn:%s" : "%llx.%pOFn", +- addr, node, dev_name(dev)); +- return; +- } +- +- /* format arguments only used if dev_name() resolves to NULL */ +- dev_set_name(dev, dev_name(dev) ? "%s:%s" : "%s", +- kbasename(node->full_name), dev_name(dev)); +- node = node->parent; +- } +-} +- +-/** + * of_device_alloc - Allocate and initialize an of_device + * @np: device node to assign to device + * @bus_id: Name to assign to the device. May be null to use default name. +--- a/include/linux/of_device.h ++++ b/include/linux/of_device.h +@@ -56,6 +56,9 @@ static inline int of_dma_configure(struc + { + return of_dma_configure_id(dev, np, force_dma, NULL); + } ++ ++void of_device_make_bus_id(struct device *dev); ++ + #else /* CONFIG_OF */ + + static inline int of_driver_match_device(struct device *dev, +@@ -113,6 +116,9 @@ static inline int of_dma_configure(struc + { + return 0; + } ++ ++static inline void of_device_make_bus_id(struct device *dev) {} ++ + #endif /* CONFIG_OF */ + + #endif /* _LINUX_OF_DEVICE_H */ diff --git a/target/linux/generic/backport-6.1/819-v6.8-0001-nvmem-Move-of_nvmem_layout_get_container-in-another-.patch b/target/linux/generic/backport-6.1/819-v6.8-0001-nvmem-Move-of_nvmem_layout_get_container-in-another-.patch new file mode 100644 index 0000000000..2093fac8a1 --- /dev/null +++ b/target/linux/generic/backport-6.1/819-v6.8-0001-nvmem-Move-of_nvmem_layout_get_container-in-another-.patch @@ -0,0 +1,95 @@ +From 4a1a40233b4a9fc159a5c7a27dc34c5c7bc5be55 Mon Sep 17 00:00:00 2001 +From: Miquel Raynal +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 +Signed-off-by: Srinivas Kandagatla +Link: https://lore.kernel.org/r/20231215111536.316972-3-srinivas.kandagatla@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- + drivers/nvmem/core.c | 8 -------- + include/linux/nvmem-consumer.h | 7 ------- + include/linux/nvmem-provider.h | 21 +++++++++++++++++++++ + 3 files changed, 21 insertions(+), 15 deletions(-) + +--- a/drivers/nvmem/core.c ++++ b/drivers/nvmem/core.c +@@ -848,14 +848,6 @@ static int nvmem_add_cells_from_layout(s + } + + #if IS_ENABLED(CONFIG_OF) +-/** +- * of_nvmem_layout_get_container() - Get OF node to layout container. +- * +- * @nvmem: nvmem device. +- * +- * Return: a node pointer with refcount incremented or NULL if no +- * container exists. Use of_node_put() on it when done. +- */ + struct device_node *of_nvmem_layout_get_container(struct nvmem_device *nvmem) + { + return of_get_child_by_name(nvmem->dev.of_node, "nvmem-layout"); +--- a/include/linux/nvmem-consumer.h ++++ b/include/linux/nvmem-consumer.h +@@ -241,7 +241,6 @@ struct nvmem_cell *of_nvmem_cell_get(str + const char *id); + struct nvmem_device *of_nvmem_device_get(struct device_node *np, + const char *name); +-struct device_node *of_nvmem_layout_get_container(struct nvmem_device *nvmem); + #else + static inline struct nvmem_cell *of_nvmem_cell_get(struct device_node *np, + const char *id) +@@ -254,12 +253,6 @@ static inline struct nvmem_device *of_nv + { + return ERR_PTR(-EOPNOTSUPP); + } +- +-static inline struct device_node * +-of_nvmem_layout_get_container(struct nvmem_device *nvmem) +-{ +- return NULL; +-} + #endif /* CONFIG_NVMEM && CONFIG_OF */ + + #endif /* ifndef _LINUX_NVMEM_CONSUMER_H */ +--- a/include/linux/nvmem-provider.h ++++ b/include/linux/nvmem-provider.h +@@ -244,6 +244,27 @@ nvmem_layout_get_match_data(struct nvmem + + #endif /* CONFIG_NVMEM */ + ++#if IS_ENABLED(CONFIG_NVMEM) && IS_ENABLED(CONFIG_OF) ++ ++/** ++ * of_nvmem_layout_get_container() - Get OF node of layout container ++ * ++ * @nvmem: nvmem device ++ * ++ * Return: a node pointer with refcount incremented or NULL if no ++ * container exists. Use of_node_put() on it when done. ++ */ ++struct device_node *of_nvmem_layout_get_container(struct nvmem_device *nvmem); ++ ++#else /* CONFIG_NVMEM && CONFIG_OF */ ++ ++static inline struct device_node *of_nvmem_layout_get_container(struct nvmem_device *nvmem) ++{ ++ return NULL; ++} ++ ++#endif /* CONFIG_NVMEM && CONFIG_OF */ ++ + #define module_nvmem_layout_driver(__layout_driver) \ + module_driver(__layout_driver, nvmem_layout_register, \ + nvmem_layout_unregister) diff --git a/target/linux/generic/backport-6.1/819-v6.8-0002-nvmem-Create-a-header-for-internal-sharing.patch b/target/linux/generic/backport-6.1/819-v6.8-0002-nvmem-Create-a-header-for-internal-sharing.patch new file mode 100644 index 0000000000..e722109f91 --- /dev/null +++ b/target/linux/generic/backport-6.1/819-v6.8-0002-nvmem-Create-a-header-for-internal-sharing.patch @@ -0,0 +1,91 @@ +From ec9c08a1cb8dc5e8e003f95f5f62de41dde235bb Mon Sep 17 00:00:00 2001 +From: Miquel Raynal +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 +Signed-off-by: Srinivas Kandagatla +Link: https://lore.kernel.org/r/20231215111536.316972-4-srinivas.kandagatla@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- + 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 + #include + +-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 ++#include ++#include ++ ++struct nvmem_device { ++ struct module *owner; ++ struct device dev; ++ struct list_head node; ++ int stride; ++ int word_size; ++ int id; ++ struct kref refcnt; ++ size_t size; ++ bool read_only; ++ bool root_only; ++ int flags; ++ enum nvmem_type type; ++ struct bin_attribute eeprom; ++ struct device *base_dev; ++ struct list_head cells; ++ const struct nvmem_keepout *keepout; ++ unsigned int nkeepout; ++ nvmem_reg_read_t reg_read; ++ nvmem_reg_write_t reg_write; ++ struct gpio_desc *wp_gpio; ++ struct nvmem_layout *layout; ++ void *priv; ++}; ++ ++#endif /* ifndef _LINUX_NVMEM_INTERNALS_H */ diff --git a/target/linux/generic/backport-6.1/819-v6.8-0003-nvmem-Simplify-the-add_cells-hook.patch b/target/linux/generic/backport-6.1/819-v6.8-0003-nvmem-Simplify-the-add_cells-hook.patch new file mode 100644 index 0000000000..db2d8c1b46 --- /dev/null +++ b/target/linux/generic/backport-6.1/819-v6.8-0003-nvmem-Simplify-the-add_cells-hook.patch @@ -0,0 +1,79 @@ +From 1b7c298a4ecbc28cc6ee94005734bff55eb83d22 Mon Sep 17 00:00:00 2001 +From: Miquel Raynal +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 +Signed-off-by: Srinivas Kandagatla +Link: https://lore.kernel.org/r/20231215111536.316972-5-srinivas.kandagatla@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- + drivers/nvmem/core.c | 2 +- + drivers/nvmem/layouts/onie-tlv.c | 3 +-- + drivers/nvmem/layouts/sl28vpd.c | 3 +-- + include/linux/nvmem-provider.h | 8 +++----- + 4 files changed, 6 insertions(+), 10 deletions(-) + +--- a/drivers/nvmem/core.c ++++ b/drivers/nvmem/core.c +@@ -817,7 +817,7 @@ static int nvmem_add_cells_from_layout(s + int ret; + + if (layout && layout->add_cells) { +- ret = layout->add_cells(&nvmem->dev, nvmem, layout); ++ ret = layout->add_cells(&nvmem->dev, nvmem); + if (ret) + return ret; + } +--- a/drivers/nvmem/layouts/onie-tlv.c ++++ b/drivers/nvmem/layouts/onie-tlv.c +@@ -182,8 +182,7 @@ static bool onie_tlv_crc_is_valid(struct + return true; + } + +-static int onie_tlv_parse_table(struct device *dev, struct nvmem_device *nvmem, +- struct nvmem_layout *layout) ++static int onie_tlv_parse_table(struct device *dev, struct nvmem_device *nvmem) + { + struct onie_tlv_hdr hdr; + size_t table_len, data_len, hdr_len; +--- a/drivers/nvmem/layouts/sl28vpd.c ++++ b/drivers/nvmem/layouts/sl28vpd.c +@@ -80,8 +80,7 @@ static int sl28vpd_v1_check_crc(struct d + return 0; + } + +-static int sl28vpd_add_cells(struct device *dev, struct nvmem_device *nvmem, +- struct nvmem_layout *layout) ++static int sl28vpd_add_cells(struct device *dev, struct nvmem_device *nvmem) + { + const struct nvmem_cell_info *pinfo; + struct nvmem_cell_info info = {0}; +--- a/include/linux/nvmem-provider.h ++++ b/include/linux/nvmem-provider.h +@@ -156,9 +156,8 @@ struct nvmem_cell_table { + * + * @name: Layout name. + * @of_match_table: Open firmware match table. +- * @add_cells: Will be called if a nvmem device is found which +- * has this layout. The function will add layout +- * specific cells with nvmem_add_one_cell(). ++ * @add_cells: Called to populate the layout using ++ * nvmem_add_one_cell(). + * @fixup_cell_info: Will be called before a cell is added. Can be + * used to modify the nvmem_cell_info. + * @owner: Pointer to struct module. +@@ -172,8 +171,7 @@ struct nvmem_cell_table { + struct nvmem_layout { + const char *name; + const struct of_device_id *of_match_table; +- int (*add_cells)(struct device *dev, struct nvmem_device *nvmem, +- struct nvmem_layout *layout); ++ int (*add_cells)(struct device *dev, struct nvmem_device *nvmem); + void (*fixup_cell_info)(struct nvmem_device *nvmem, + struct nvmem_layout *layout, + struct nvmem_cell_info *cell); diff --git a/target/linux/generic/backport-6.1/819-v6.8-0004-nvmem-Move-and-rename-fixup_cell_info.patch b/target/linux/generic/backport-6.1/819-v6.8-0004-nvmem-Move-and-rename-fixup_cell_info.patch new file mode 100644 index 0000000000..65aa37f834 --- /dev/null +++ b/target/linux/generic/backport-6.1/819-v6.8-0004-nvmem-Move-and-rename-fixup_cell_info.patch @@ -0,0 +1,169 @@ +From 1172460e716784ac7e1049a537bdca8edbf97360 Mon Sep 17 00:00:00 2001 +From: Miquel Raynal +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 +Signed-off-by: Srinivas Kandagatla +Link: https://lore.kernel.org/r/20231215111536.316972-6-srinivas.kandagatla@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- + drivers/nvmem/core.c | 6 +++--- + drivers/nvmem/imx-ocotp.c | 11 +++-------- + drivers/nvmem/internals.h | 2 ++ + drivers/nvmem/mtk-efuse.c | 11 +++-------- + include/linux/nvmem-provider.h | 9 ++++----- + 5 files changed, 15 insertions(+), 24 deletions(-) + +--- a/drivers/nvmem/core.c ++++ b/drivers/nvmem/core.c +@@ -676,7 +676,6 @@ static int nvmem_validate_keepouts(struc + + static int nvmem_add_cells_from_dt(struct nvmem_device *nvmem, struct device_node *np) + { +- struct nvmem_layout *layout = nvmem->layout; + struct device *dev = &nvmem->dev; + struct device_node *child; + const __be32 *addr; +@@ -706,8 +705,8 @@ static int nvmem_add_cells_from_dt(struc + + info.np = of_node_get(child); + +- if (layout && layout->fixup_cell_info) +- layout->fixup_cell_info(nvmem, layout, &info); ++ if (nvmem->fixup_dt_cell_info) ++ nvmem->fixup_dt_cell_info(nvmem, &info); + + ret = nvmem_add_one_cell(nvmem, &info); + kfree(info.name); +@@ -896,6 +895,7 @@ struct nvmem_device *nvmem_register(cons + + kref_init(&nvmem->refcnt); + INIT_LIST_HEAD(&nvmem->cells); ++ nvmem->fixup_dt_cell_info = config->fixup_dt_cell_info; + + nvmem->owner = config->owner; + if (!nvmem->owner && config->dev->driver) +--- a/drivers/nvmem/imx-ocotp.c ++++ b/drivers/nvmem/imx-ocotp.c +@@ -584,17 +584,12 @@ static const struct of_device_id imx_oco + }; + MODULE_DEVICE_TABLE(of, imx_ocotp_dt_ids); + +-static void imx_ocotp_fixup_cell_info(struct nvmem_device *nvmem, +- struct nvmem_layout *layout, +- struct nvmem_cell_info *cell) ++static void imx_ocotp_fixup_dt_cell_info(struct nvmem_device *nvmem, ++ struct nvmem_cell_info *cell) + { + cell->read_post_process = imx_ocotp_cell_pp; + } + +-static struct nvmem_layout imx_ocotp_layout = { +- .fixup_cell_info = imx_ocotp_fixup_cell_info, +-}; +- + static int imx_ocotp_probe(struct platform_device *pdev) + { + struct device *dev = &pdev->dev; +@@ -620,7 +615,7 @@ static int imx_ocotp_probe(struct platfo + imx_ocotp_nvmem_config.size = 4 * priv->params->nregs; + imx_ocotp_nvmem_config.dev = dev; + imx_ocotp_nvmem_config.priv = priv; +- imx_ocotp_nvmem_config.layout = &imx_ocotp_layout; ++ imx_ocotp_nvmem_config.fixup_dt_cell_info = &imx_ocotp_fixup_dt_cell_info; + + priv->config = &imx_ocotp_nvmem_config; + +--- a/drivers/nvmem/internals.h ++++ b/drivers/nvmem/internals.h +@@ -23,6 +23,8 @@ struct nvmem_device { + struct bin_attribute eeprom; + struct device *base_dev; + struct list_head cells; ++ void (*fixup_dt_cell_info)(struct nvmem_device *nvmem, ++ struct nvmem_cell_info *cell); + const struct nvmem_keepout *keepout; + unsigned int nkeepout; + nvmem_reg_read_t reg_read; +--- a/drivers/nvmem/mtk-efuse.c ++++ b/drivers/nvmem/mtk-efuse.c +@@ -45,9 +45,8 @@ static int mtk_efuse_gpu_speedbin_pp(voi + return 0; + } + +-static void mtk_efuse_fixup_cell_info(struct nvmem_device *nvmem, +- struct nvmem_layout *layout, +- struct nvmem_cell_info *cell) ++static void mtk_efuse_fixup_dt_cell_info(struct nvmem_device *nvmem, ++ struct nvmem_cell_info *cell) + { + size_t sz = strlen(cell->name); + +@@ -61,10 +60,6 @@ static void mtk_efuse_fixup_cell_info(st + cell->read_post_process = mtk_efuse_gpu_speedbin_pp; + } + +-static struct nvmem_layout mtk_efuse_layout = { +- .fixup_cell_info = mtk_efuse_fixup_cell_info, +-}; +- + static int mtk_efuse_probe(struct platform_device *pdev) + { + struct device *dev = &pdev->dev; +@@ -91,7 +86,7 @@ static int mtk_efuse_probe(struct platfo + econfig.priv = priv; + econfig.dev = dev; + if (pdata->uses_post_processing) +- econfig.layout = &mtk_efuse_layout; ++ econfig.fixup_dt_cell_info = &mtk_efuse_fixup_dt_cell_info; + nvmem = devm_nvmem_register(dev, &econfig); + + return PTR_ERR_OR_ZERO(nvmem); +--- a/include/linux/nvmem-provider.h ++++ b/include/linux/nvmem-provider.h +@@ -83,6 +83,8 @@ struct nvmem_cell_info { + * @cells: Optional array of pre-defined NVMEM cells. + * @ncells: Number of elements in cells. + * @add_legacy_fixed_of_cells: Read fixed NVMEM cells from old OF syntax. ++ * @fixup_dt_cell_info: Will be called before a cell is added. Can be ++ * used to modify the nvmem_cell_info. + * @keepout: Optional array of keepout ranges (sorted ascending by start). + * @nkeepout: Number of elements in the keepout array. + * @type: Type of the nvmem storage +@@ -113,6 +115,8 @@ struct nvmem_config { + const struct nvmem_cell_info *cells; + int ncells; + bool add_legacy_fixed_of_cells; ++ void (*fixup_dt_cell_info)(struct nvmem_device *nvmem, ++ struct nvmem_cell_info *cell); + const struct nvmem_keepout *keepout; + unsigned int nkeepout; + enum nvmem_type type; +@@ -158,8 +162,6 @@ struct nvmem_cell_table { + * @of_match_table: Open firmware match table. + * @add_cells: Called to populate the layout using + * nvmem_add_one_cell(). +- * @fixup_cell_info: Will be called before a cell is added. Can be +- * used to modify the nvmem_cell_info. + * @owner: Pointer to struct module. + * @node: List node. + * +@@ -172,9 +174,6 @@ struct nvmem_layout { + const char *name; + const struct of_device_id *of_match_table; + int (*add_cells)(struct device *dev, struct nvmem_device *nvmem); +- void (*fixup_cell_info)(struct nvmem_device *nvmem, +- struct nvmem_layout *layout, +- struct nvmem_cell_info *cell); + + /* private */ + struct module *owner; diff --git a/target/linux/generic/backport-6.1/819-v6.8-0005-nvmem-core-Rework-layouts-to-become-regular-devices.patch b/target/linux/generic/backport-6.1/819-v6.8-0005-nvmem-core-Rework-layouts-to-become-regular-devices.patch new file mode 100644 index 0000000000..1881332340 --- /dev/null +++ b/target/linux/generic/backport-6.1/819-v6.8-0005-nvmem-core-Rework-layouts-to-become-regular-devices.patch @@ -0,0 +1,763 @@ +From fc29fd821d9ac2ae3d32a722fac39ce874efb883 Mon Sep 17 00:00:00 2001 +From: Miquel Raynal +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 +Tested-by: Rafał Miłecki +Signed-off-by: Srinivas Kandagatla +Link: https://lore.kernel.org/r/20231215111536.316972-7-srinivas.kandagatla@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- + 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 ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#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 + #include + #include + #include +@@ -158,12 +159,11 @@ struct nvmem_cell_table { + /** + * struct nvmem_layout - NVMEM layout definitions + * +- * @name: Layout name. +- * @of_match_table: Open firmware match table. +- * @add_cells: Called to populate the layout using +- * nvmem_add_one_cell(). +- * @owner: Pointer to struct module. +- * @node: List node. ++ * @dev: Device-model layout device. ++ * @nvmem: The underlying NVMEM device ++ * @add_cells: Will be called if a nvmem device is found which ++ * has this layout. The function will add layout ++ * specific cells with nvmem_add_one_cell(). + * + * A nvmem device can hold a well defined structure which can just be + * evaluated during runtime. For example a TLV list, or a list of "name=val" +@@ -171,13 +171,15 @@ struct nvmem_cell_table { + * cells. + */ + struct nvmem_layout { +- const char *name; +- const struct of_device_id *of_match_table; ++ struct device dev; ++ struct nvmem_device *nvmem; + int (*add_cells)(struct device *dev, struct nvmem_device *nvmem); ++}; + +- /* private */ +- struct module *owner; +- struct list_head node; ++struct nvmem_layout_driver { ++ struct device_driver driver; ++ int (*probe)(struct nvmem_layout *layout); ++ void (*remove)(struct nvmem_layout *layout); + }; + + #if IS_ENABLED(CONFIG_NVMEM) +@@ -194,11 +196,15 @@ void nvmem_del_cell_table(struct nvmem_c + int nvmem_add_one_cell(struct nvmem_device *nvmem, + const struct nvmem_cell_info *info); + +-int __nvmem_layout_register(struct nvmem_layout *layout, struct module *owner); +-#define nvmem_layout_register(layout) \ +- __nvmem_layout_register(layout, THIS_MODULE) ++int nvmem_layout_register(struct nvmem_layout *layout); + void nvmem_layout_unregister(struct nvmem_layout *layout); + ++int nvmem_layout_driver_register(struct nvmem_layout_driver *drv); ++void nvmem_layout_driver_unregister(struct nvmem_layout_driver *drv); ++#define module_nvmem_layout_driver(__nvmem_layout_driver) \ ++ module_driver(__nvmem_layout_driver, nvmem_layout_driver_register, \ ++ nvmem_layout_driver_unregister) ++ + const void *nvmem_layout_get_match_data(struct nvmem_device *nvmem, + struct nvmem_layout *layout); + +@@ -262,8 +268,4 @@ static inline struct device_node *of_nvm + + #endif /* CONFIG_NVMEM && CONFIG_OF */ + +-#define module_nvmem_layout_driver(__layout_driver) \ +- module_driver(__layout_driver, nvmem_layout_register, \ +- nvmem_layout_unregister) +- + #endif /* ifndef _LINUX_NVMEM_PROVIDER_H */ diff --git a/target/linux/generic/backport-6.1/819-v6.8-0006-nvmem-core-Expose-cells-through-sysfs.patch b/target/linux/generic/backport-6.1/819-v6.8-0006-nvmem-core-Expose-cells-through-sysfs.patch new file mode 100644 index 0000000000..89872bec2e --- /dev/null +++ b/target/linux/generic/backport-6.1/819-v6.8-0006-nvmem-core-Expose-cells-through-sysfs.patch @@ -0,0 +1,240 @@ +From 0331c611949fffdf486652450901a4dc52bc5cca Mon Sep 17 00:00:00 2001 +From: Miquel Raynal +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 +Tested-by: Rafał Miłecki +Tested-by: Chen-Yu Tsai +Signed-off-by: Srinivas Kandagatla +Link: https://lore.kernel.org/r/20231215111536.316972-9-srinivas.kandagatla@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- + drivers/nvmem/core.c | 135 +++++++++++++++++++++++++++++++++++++- + drivers/nvmem/internals.h | 1 + + 2 files changed, 135 insertions(+), 1 deletion(-) + +--- a/drivers/nvmem/core.c ++++ b/drivers/nvmem/core.c +@@ -300,6 +300,43 @@ static umode_t nvmem_bin_attr_is_visible + return nvmem_bin_attr_get_umode(nvmem); + } + ++static struct nvmem_cell *nvmem_create_cell(struct nvmem_cell_entry *entry, ++ const char *id, int index); ++ ++static ssize_t nvmem_cell_attr_read(struct file *filp, struct kobject *kobj, ++ struct bin_attribute *attr, char *buf, ++ loff_t pos, size_t count) ++{ ++ struct nvmem_cell_entry *entry; ++ struct nvmem_cell *cell = NULL; ++ size_t cell_sz, read_len; ++ void *content; ++ ++ entry = attr->private; ++ cell = nvmem_create_cell(entry, entry->name, 0); ++ if (IS_ERR(cell)) ++ return PTR_ERR(cell); ++ ++ if (!cell) ++ return -EINVAL; ++ ++ content = nvmem_cell_read(cell, &cell_sz); ++ if (IS_ERR(content)) { ++ read_len = PTR_ERR(content); ++ goto destroy_cell; ++ } ++ ++ read_len = min_t(unsigned int, cell_sz - pos, count); ++ memcpy(buf, content + pos, read_len); ++ kfree(content); ++ ++destroy_cell: ++ kfree_const(cell->id); ++ kfree(cell); ++ ++ return read_len; ++} ++ + /* default read/write permissions */ + static struct bin_attribute bin_attr_rw_nvmem = { + .attr = { +@@ -321,11 +358,21 @@ static const struct attribute_group nvme + .is_bin_visible = nvmem_bin_attr_is_visible, + }; + ++/* Cell attributes will be dynamically allocated */ ++static struct attribute_group nvmem_cells_group = { ++ .name = "cells", ++}; ++ + static const struct attribute_group *nvmem_dev_groups[] = { + &nvmem_bin_group, + NULL, + }; + ++static const struct attribute_group *nvmem_cells_groups[] = { ++ &nvmem_cells_group, ++ NULL, ++}; ++ + static struct bin_attribute bin_attr_nvmem_eeprom_compat = { + .attr = { + .name = "eeprom", +@@ -381,6 +428,68 @@ static void nvmem_sysfs_remove_compat(st + device_remove_bin_file(nvmem->base_dev, &nvmem->eeprom); + } + ++static int nvmem_populate_sysfs_cells(struct nvmem_device *nvmem) ++{ ++ struct bin_attribute **cells_attrs, *attrs; ++ struct nvmem_cell_entry *entry; ++ unsigned int ncells = 0, i = 0; ++ int ret = 0; ++ ++ mutex_lock(&nvmem_mutex); ++ ++ if (list_empty(&nvmem->cells) || nvmem->sysfs_cells_populated) { ++ nvmem_cells_group.bin_attrs = NULL; ++ goto unlock_mutex; ++ } ++ ++ /* Allocate an array of attributes with a sentinel */ ++ ncells = list_count_nodes(&nvmem->cells); ++ cells_attrs = devm_kcalloc(&nvmem->dev, ncells + 1, ++ sizeof(struct bin_attribute *), GFP_KERNEL); ++ if (!cells_attrs) { ++ ret = -ENOMEM; ++ goto unlock_mutex; ++ } ++ ++ attrs = devm_kcalloc(&nvmem->dev, ncells, sizeof(struct bin_attribute), GFP_KERNEL); ++ if (!attrs) { ++ ret = -ENOMEM; ++ goto unlock_mutex; ++ } ++ ++ /* Initialize each attribute to take the name and size of the cell */ ++ list_for_each_entry(entry, &nvmem->cells, node) { ++ sysfs_bin_attr_init(&attrs[i]); ++ attrs[i].attr.name = devm_kasprintf(&nvmem->dev, GFP_KERNEL, ++ "%s@%x", entry->name, ++ entry->offset); ++ attrs[i].attr.mode = 0444; ++ attrs[i].size = entry->bytes; ++ attrs[i].read = &nvmem_cell_attr_read; ++ attrs[i].private = entry; ++ if (!attrs[i].attr.name) { ++ ret = -ENOMEM; ++ goto unlock_mutex; ++ } ++ ++ cells_attrs[i] = &attrs[i]; ++ i++; ++ } ++ ++ nvmem_cells_group.bin_attrs = cells_attrs; ++ ++ ret = devm_device_add_groups(&nvmem->dev, nvmem_cells_groups); ++ if (ret) ++ goto unlock_mutex; ++ ++ nvmem->sysfs_cells_populated = true; ++ ++unlock_mutex: ++ mutex_unlock(&nvmem_mutex); ++ ++ return ret; ++} ++ + #else /* CONFIG_NVMEM_SYSFS */ + + static int nvmem_sysfs_setup_compat(struct nvmem_device *nvmem, +@@ -740,11 +849,25 @@ static int nvmem_add_cells_from_fixed_la + + int nvmem_layout_register(struct nvmem_layout *layout) + { ++ int ret; ++ + if (!layout->add_cells) + return -EINVAL; + + /* Populate the cells */ +- return layout->add_cells(&layout->nvmem->dev, layout->nvmem); ++ ret = layout->add_cells(&layout->nvmem->dev, layout->nvmem); ++ if (ret) ++ return ret; ++ ++#ifdef CONFIG_NVMEM_SYSFS ++ ret = nvmem_populate_sysfs_cells(layout->nvmem); ++ if (ret) { ++ nvmem_device_remove_all_cells(layout->nvmem); ++ return ret; ++ } ++#endif ++ ++ return 0; + } + EXPORT_SYMBOL_GPL(nvmem_layout_register); + +@@ -903,10 +1026,20 @@ struct nvmem_device *nvmem_register(cons + if (rval) + goto err_remove_dev; + ++#ifdef CONFIG_NVMEM_SYSFS ++ rval = nvmem_populate_sysfs_cells(nvmem); ++ if (rval) ++ goto err_destroy_layout; ++#endif ++ + blocking_notifier_call_chain(&nvmem_notifier, NVMEM_ADD, nvmem); + + return nvmem; + ++#ifdef CONFIG_NVMEM_SYSFS ++err_destroy_layout: ++ nvmem_destroy_layout(nvmem); ++#endif + err_remove_dev: + device_del(&nvmem->dev); + err_remove_cells: +--- a/drivers/nvmem/internals.h ++++ b/drivers/nvmem/internals.h +@@ -32,6 +32,7 @@ struct nvmem_device { + struct gpio_desc *wp_gpio; + struct nvmem_layout *layout; + void *priv; ++ bool sysfs_cells_populated; + }; + + #if IS_ENABLED(CONFIG_OF) diff --git a/target/linux/generic/backport-6.1/819-v6.8-0007-nvmem-stm32-add-support-for-STM32MP25-BSEC-to-contro.patch b/target/linux/generic/backport-6.1/819-v6.8-0007-nvmem-stm32-add-support-for-STM32MP25-BSEC-to-contro.patch new file mode 100644 index 0000000000..f686222f88 --- /dev/null +++ b/target/linux/generic/backport-6.1/819-v6.8-0007-nvmem-stm32-add-support-for-STM32MP25-BSEC-to-contro.patch @@ -0,0 +1,65 @@ +From f0ac5b23039610619ca4a4805528553ecb6bc815 Mon Sep 17 00:00:00 2001 +From: Patrick Delaunay +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 +Signed-off-by: Srinivas Kandagatla +Link: https://lore.kernel.org/r/20231215111536.316972-11-srinivas.kandagatla@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- + drivers/nvmem/stm32-romem.c | 16 ++++++++++++++++ + 1 file changed, 16 insertions(+) + +--- a/drivers/nvmem/stm32-romem.c ++++ b/drivers/nvmem/stm32-romem.c +@@ -269,6 +269,19 @@ static const struct stm32_romem_cfg stm3 + .ta = true, + }; + ++/* ++ * STM32MP25 BSEC OTP: 3 regions of 32-bits data words ++ * lower OTP (OTP0 to OTP127), bitwise (1-bit) programmable ++ * mid OTP (OTP128 to OTP255), bulk (32-bit) programmable ++ * upper OTP (OTP256 to OTP383), bulk (32-bit) programmable ++ * but no access to HWKEY and ECIES key: limited at OTP367 ++ */ ++static const struct stm32_romem_cfg stm32mp25_bsec_cfg = { ++ .size = 368 * 4, ++ .lower = 127, ++ .ta = true, ++}; ++ + static const struct of_device_id stm32_romem_of_match[] __maybe_unused = { + { .compatible = "st,stm32f4-otp", }, { + .compatible = "st,stm32mp15-bsec", +@@ -276,6 +289,9 @@ static const struct of_device_id stm32_r + }, { + .compatible = "st,stm32mp13-bsec", + .data = (void *)&stm32mp13_bsec_cfg, ++ }, { ++ .compatible = "st,stm32mp25-bsec", ++ .data = (void *)&stm32mp25_bsec_cfg, + }, + { /* sentinel */ }, + }; diff --git a/target/linux/generic/pending-5.15/804-nvmem-core-support-mac-base-fixed-layout-cells.patch b/target/linux/generic/pending-5.15/804-nvmem-core-support-mac-base-fixed-layout-cells.patch index 95f29b1865..9bb94a28b5 100644 --- a/target/linux/generic/pending-5.15/804-nvmem-core-support-mac-base-fixed-layout-cells.patch +++ b/target/linux/generic/pending-5.15/804-nvmem-core-support-mac-base-fixed-layout-cells.patch @@ -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 #include #include -@@ -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")) { diff --git a/target/linux/generic/pending-6.1/804-nvmem-core-support-mac-base-fixed-layout-cells.patch b/target/linux/generic/pending-6.1/804-nvmem-core-support-mac-base-fixed-layout-cells.patch index 95f29b1865..9bb94a28b5 100644 --- a/target/linux/generic/pending-6.1/804-nvmem-core-support-mac-base-fixed-layout-cells.patch +++ b/target/linux/generic/pending-6.1/804-nvmem-core-support-mac-base-fixed-layout-cells.patch @@ -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 #include #include -@@ -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")) {