summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRafał Miłecki <rafal@milecki.pl>2024-09-12 06:47:59 +0200
committerRafał Miłecki <rafal@milecki.pl>2024-09-12 08:59:36 +0200
commite39ae96f88b75929c6c951feed2d52b1aebcc081 (patch)
treec434b88b069e7063957146db7a89ce8e57d49b03
parentc7ba5574f52f25334add66c15190b578ac873739 (diff)
downloadopenwrt-e39ae96f88b75929c6c951feed2d52b1aebcc081.tar.gz
openwrt-e39ae96f88b75929c6c951feed2d52b1aebcc081.tar.bz2
openwrt-e39ae96f88b75929c6c951feed2d52b1aebcc081.zip
kernel: backport nvmem changes from v6.12
Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
-rw-r--r--target/linux/generic/backport-5.15/837-v6.10-0001-nvmem-layouts-store-owner-from-modules-with-nvmem_la.patch2
-rw-r--r--target/linux/generic/backport-5.15/839-v6.12-0001-nvmem-imx-ocotp-ele-support-i.MX95.patch73
-rw-r--r--target/linux/generic/backport-5.15/839-v6.12-0002-nvmem-sunplus-ocotp-Use-devm_platform_ioremap_resour.patch44
-rw-r--r--target/linux/generic/backport-5.15/839-v6.12-0003-nvmem-layouts-add-U-Boot-env-layout.patch515
-rw-r--r--target/linux/generic/backport-6.1/829-v6.10-0001-nvmem-layouts-store-owner-from-modules-with-nvmem_la.patch2
-rw-r--r--target/linux/generic/backport-6.1/841-v6.12-0001-nvmem-imx-ocotp-ele-support-i.MX95.patch73
-rw-r--r--target/linux/generic/backport-6.1/841-v6.12-0002-nvmem-sunplus-ocotp-Use-devm_platform_ioremap_resour.patch44
-rw-r--r--target/linux/generic/backport-6.1/841-v6.12-0003-nvmem-layouts-add-U-Boot-env-layout.patch525
-rw-r--r--target/linux/generic/backport-6.6/823-v6.12-0001-nvmem-imx-ocotp-ele-support-i.MX95.patch73
-rw-r--r--target/linux/generic/backport-6.6/823-v6.12-0002-nvmem-sunplus-ocotp-Use-devm_platform_ioremap_resour.patch44
-rw-r--r--target/linux/generic/backport-6.6/823-v6.12-0003-nvmem-layouts-add-U-Boot-env-layout.patch525
-rw-r--r--target/linux/generic/config-5.151
-rw-r--r--target/linux/generic/config-6.11
-rw-r--r--target/linux/generic/config-6.61
-rw-r--r--target/linux/generic/pending-5.15/802-nvmem-u-boot-env-align-endianness-of-crc32-values.patch10
-rw-r--r--target/linux/generic/pending-6.1/510-block-add-uImage.FIT-subimage-block-driver.patch2
-rw-r--r--target/linux/generic/pending-6.1/802-nvmem-u-boot-env-align-endianness-of-crc32-values.patch10
-rw-r--r--target/linux/generic/pending-6.6/510-block-add-uImage.FIT-subimage-block-driver.patch2
-rw-r--r--target/linux/generic/pending-6.6/802-nvmem-u-boot-env-align-endianness-of-crc32-values.patch10
19 files changed, 1935 insertions, 22 deletions
diff --git a/target/linux/generic/backport-5.15/837-v6.10-0001-nvmem-layouts-store-owner-from-modules-with-nvmem_la.patch b/target/linux/generic/backport-5.15/837-v6.10-0001-nvmem-layouts-store-owner-from-modules-with-nvmem_la.patch
index 418d2c82b2..c15b4b2265 100644
--- a/target/linux/generic/backport-5.15/837-v6.10-0001-nvmem-layouts-store-owner-from-modules-with-nvmem_la.patch
+++ b/target/linux/generic/backport-5.15/837-v6.10-0001-nvmem-layouts-store-owner-from-modules-with-nvmem_la.patch
@@ -27,7 +27,7 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
--- a/drivers/nvmem/layouts.c
+++ b/drivers/nvmem/layouts.c
-@@ -52,13 +52,15 @@ static const struct bus_type nvmem_layou
+@@ -52,13 +52,15 @@ static struct bus_type nvmem_layout_bus_
.remove = nvmem_layout_bus_remove,
};
diff --git a/target/linux/generic/backport-5.15/839-v6.12-0001-nvmem-imx-ocotp-ele-support-i.MX95.patch b/target/linux/generic/backport-5.15/839-v6.12-0001-nvmem-imx-ocotp-ele-support-i.MX95.patch
new file mode 100644
index 0000000000..c19931b3fa
--- /dev/null
+++ b/target/linux/generic/backport-5.15/839-v6.12-0001-nvmem-imx-ocotp-ele-support-i.MX95.patch
@@ -0,0 +1,73 @@
+From c3f9b7b4e5f9de319d00784577cda42036ff243a Mon Sep 17 00:00:00 2001
+From: Peng Fan <peng.fan@nxp.com>
+Date: Mon, 2 Sep 2024 15:29:45 +0100
+Subject: [PATCH] nvmem: imx-ocotp-ele: support i.MX95
+
+i.MX95 OCOTP has same accessing method, so add an entry for i.MX95, but
+some fuse has ECC feature, so only read out the lower 16bits for ECC fuses.
+
+Signed-off-by: Peng Fan <peng.fan@nxp.com>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20240902142952.71639-3-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/imx-ocotp-ele.c | 32 +++++++++++++++++++++++++++++---
+ 1 file changed, 29 insertions(+), 3 deletions(-)
+
+--- a/drivers/nvmem/imx-ocotp-ele.c
++++ b/drivers/nvmem/imx-ocotp-ele.c
+@@ -14,8 +14,9 @@
+ #include <linux/slab.h>
+
+ enum fuse_type {
+- FUSE_FSB = 1,
+- FUSE_ELE = 2,
++ FUSE_FSB = BIT(0),
++ FUSE_ELE = BIT(1),
++ FUSE_ECC = BIT(2),
+ FUSE_INVALID = -1
+ };
+
+@@ -93,7 +94,10 @@ static int imx_ocotp_reg_read(void *cont
+ continue;
+ }
+
+- *buf++ = readl_relaxed(reg + (i << 2));
++ if (type & FUSE_ECC)
++ *buf++ = readl_relaxed(reg + (i << 2)) & GENMASK(15, 0);
++ else
++ *buf++ = readl_relaxed(reg + (i << 2));
+ }
+
+ memcpy(val, (u8 *)p, bytes);
+@@ -155,8 +159,30 @@ static const struct ocotp_devtype_data i
+ },
+ };
+
++static const struct ocotp_devtype_data imx95_ocotp_data = {
++ .reg_off = 0x8000,
++ .reg_read = imx_ocotp_reg_read,
++ .size = 2048,
++ .num_entry = 12,
++ .entry = {
++ { 0, 1, FUSE_FSB | FUSE_ECC },
++ { 7, 1, FUSE_FSB | FUSE_ECC },
++ { 9, 3, FUSE_FSB | FUSE_ECC },
++ { 12, 24, FUSE_FSB },
++ { 36, 2, FUSE_FSB | FUSE_ECC },
++ { 38, 14, FUSE_FSB },
++ { 63, 1, FUSE_ELE },
++ { 128, 16, FUSE_ELE },
++ { 188, 1, FUSE_ELE },
++ { 317, 2, FUSE_FSB | FUSE_ECC },
++ { 320, 7, FUSE_FSB },
++ { 328, 184, FUSE_FSB }
++ },
++};
++
+ static const struct of_device_id imx_ele_ocotp_dt_ids[] = {
+ { .compatible = "fsl,imx93-ocotp", .data = &imx93_ocotp_data, },
++ { .compatible = "fsl,imx95-ocotp", .data = &imx95_ocotp_data, },
+ {},
+ };
+ MODULE_DEVICE_TABLE(of, imx_ele_ocotp_dt_ids);
diff --git a/target/linux/generic/backport-5.15/839-v6.12-0002-nvmem-sunplus-ocotp-Use-devm_platform_ioremap_resour.patch b/target/linux/generic/backport-5.15/839-v6.12-0002-nvmem-sunplus-ocotp-Use-devm_platform_ioremap_resour.patch
new file mode 100644
index 0000000000..13ef50b157
--- /dev/null
+++ b/target/linux/generic/backport-5.15/839-v6.12-0002-nvmem-sunplus-ocotp-Use-devm_platform_ioremap_resour.patch
@@ -0,0 +1,44 @@
+From 98ee46391baf35987227236d0c3bb30ab6e758c8 Mon Sep 17 00:00:00 2001
+From: Zhang Zekun <zhangzekun11@huawei.com>
+Date: Mon, 2 Sep 2024 15:29:50 +0100
+Subject: [PATCH] nvmem: sunplus-ocotp: Use
+ devm_platform_ioremap_resource_byname() helper function
+
+platform_get_resource_byname() and devm_ioremap_resource() can be
+replaced by devm_platform_ioremap_resource_byname(), which can
+simplify the code logic a bit, No functional change here.
+
+Signed-off-by: Zhang Zekun <zhangzekun11@huawei.com>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20240902142952.71639-8-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/sunplus-ocotp.c | 7 ++-----
+ 1 file changed, 2 insertions(+), 5 deletions(-)
+
+--- a/drivers/nvmem/sunplus-ocotp.c
++++ b/drivers/nvmem/sunplus-ocotp.c
+@@ -159,7 +159,6 @@ static int sp_ocotp_probe(struct platfor
+ struct device *dev = &pdev->dev;
+ struct nvmem_device *nvmem;
+ struct sp_ocotp_priv *otp;
+- struct resource *res;
+ int ret;
+
+ otp = devm_kzalloc(dev, sizeof(*otp), GFP_KERNEL);
+@@ -168,13 +167,11 @@ static int sp_ocotp_probe(struct platfor
+
+ otp->dev = dev;
+
+- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "hb_gpio");
+- otp->base[HB_GPIO] = devm_ioremap_resource(dev, res);
++ otp->base[HB_GPIO] = devm_platform_ioremap_resource_byname(pdev, "hb_gpio");
+ if (IS_ERR(otp->base[HB_GPIO]))
+ return PTR_ERR(otp->base[HB_GPIO]);
+
+- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "otprx");
+- otp->base[OTPRX] = devm_ioremap_resource(dev, res);
++ otp->base[OTPRX] = devm_platform_ioremap_resource_byname(pdev, "otprx");
+ if (IS_ERR(otp->base[OTPRX]))
+ return PTR_ERR(otp->base[OTPRX]);
+
diff --git a/target/linux/generic/backport-5.15/839-v6.12-0003-nvmem-layouts-add-U-Boot-env-layout.patch b/target/linux/generic/backport-5.15/839-v6.12-0003-nvmem-layouts-add-U-Boot-env-layout.patch
new file mode 100644
index 0000000000..138e602a54
--- /dev/null
+++ b/target/linux/generic/backport-5.15/839-v6.12-0003-nvmem-layouts-add-U-Boot-env-layout.patch
@@ -0,0 +1,515 @@
+From 5f15811286aff4664bf275a7ede64e1b8858151b Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
+Date: Mon, 2 Sep 2024 15:29:47 +0100
+Subject: [PATCH] nvmem: layouts: add U-Boot env layout
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+U-Boot environment variables are stored in a specific format. Actual
+data can be placed in various storage sources (MTD, UBI volume, EEPROM,
+NVRAM, etc.).
+
+Move all generic (NVMEM device independent) code from NVMEM device
+driver to an NVMEM layout driver. Then add a simple NVMEM layout code on
+top of it.
+
+This allows using NVMEM layout for parsing U-Boot env data stored in any
+kind of NVMEM device.
+
+The old NVMEM glue driver stays in place for handling bindings in the
+MTD context. To avoid code duplication it uses exported layout parsing
+function. Please note that handling MTD & NVMEM layout bindings may be
+refactored in the future.
+
+Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
+Reviewed-by: Miquel Raynal <miquel.raynal@bootlin.com>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20240902142952.71639-5-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ MAINTAINERS | 1 +
+ drivers/nvmem/Kconfig | 3 +-
+ drivers/nvmem/layouts/Kconfig | 11 ++
+ drivers/nvmem/layouts/Makefile | 1 +
+ drivers/nvmem/layouts/u-boot-env.c | 211 +++++++++++++++++++++++++++++
+ drivers/nvmem/layouts/u-boot-env.h | 15 ++
+ drivers/nvmem/u-boot-env.c | 165 +---------------------
+ 7 files changed, 242 insertions(+), 165 deletions(-)
+ create mode 100644 drivers/nvmem/layouts/u-boot-env.c
+ create mode 100644 drivers/nvmem/layouts/u-boot-env.h
+
+--- a/drivers/nvmem/Kconfig
++++ b/drivers/nvmem/Kconfig
+@@ -363,8 +363,7 @@ config NVMEM_SUNXI_SID
+ config NVMEM_U_BOOT_ENV
+ tristate "U-Boot environment variables support"
+ depends on OF && MTD
+- select CRC32
+- select GENERIC_NET_UTILS
++ select NVMEM_LAYOUT_U_BOOT_ENV
+ help
+ U-Boot stores its setup as environment variables. This driver adds
+ support for verifying & exporting such data. It also exposes variables
+--- a/drivers/nvmem/layouts/Kconfig
++++ b/drivers/nvmem/layouts/Kconfig
+@@ -26,6 +26,17 @@ config NVMEM_LAYOUT_ONIE_TLV
+
+ If unsure, say N.
+
++config NVMEM_LAYOUT_U_BOOT_ENV
++ tristate "U-Boot environment variables layout"
++ select CRC32
++ select GENERIC_NET_UTILS
++ help
++ U-Boot stores its setup as environment variables. This driver adds
++ support for verifying & exporting such data. It also exposes variables
++ as NVMEM cells so they can be referenced by other drivers.
++
++ If unsure, say N.
++
+ endmenu
+
+ endif
+--- a/drivers/nvmem/layouts/Makefile
++++ b/drivers/nvmem/layouts/Makefile
+@@ -5,3 +5,4 @@
+
+ obj-$(CONFIG_NVMEM_LAYOUT_SL28_VPD) += sl28vpd.o
+ obj-$(CONFIG_NVMEM_LAYOUT_ONIE_TLV) += onie-tlv.o
++obj-$(CONFIG_NVMEM_LAYOUT_U_BOOT_ENV) += u-boot-env.o
+--- /dev/null
++++ b/drivers/nvmem/layouts/u-boot-env.c
+@@ -0,0 +1,211 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/*
++ * Copyright (C) 2022 - 2023 Rafał Miłecki <rafal@milecki.pl>
++ */
++
++#include <linux/crc32.h>
++#include <linux/etherdevice.h>
++#include <linux/export.h>
++#include <linux/if_ether.h>
++#include <linux/nvmem-consumer.h>
++#include <linux/nvmem-provider.h>
++#include <linux/of.h>
++#include <linux/slab.h>
++
++#include "u-boot-env.h"
++
++struct u_boot_env_image_single {
++ __le32 crc32;
++ uint8_t data[];
++} __packed;
++
++struct u_boot_env_image_redundant {
++ __le32 crc32;
++ u8 mark;
++ uint8_t data[];
++} __packed;
++
++struct u_boot_env_image_broadcom {
++ __le32 magic;
++ __le32 len;
++ __le32 crc32;
++ DECLARE_FLEX_ARRAY(uint8_t, data);
++} __packed;
++
++static int u_boot_env_read_post_process_ethaddr(void *context, const char *id, int index,
++ unsigned int offset, void *buf, size_t bytes)
++{
++ u8 mac[ETH_ALEN];
++
++ if (bytes != 3 * ETH_ALEN - 1)
++ return -EINVAL;
++
++ if (!mac_pton(buf, mac))
++ return -EINVAL;
++
++ if (index)
++ eth_addr_add(mac, index);
++
++ ether_addr_copy(buf, mac);
++
++ return 0;
++}
++
++static int u_boot_env_parse_cells(struct device *dev, struct nvmem_device *nvmem, uint8_t *buf,
++ size_t data_offset, size_t data_len)
++{
++ char *data = buf + data_offset;
++ char *var, *value, *eq;
++
++ for (var = data;
++ var < data + data_len && *var;
++ var = value + strlen(value) + 1) {
++ struct nvmem_cell_info info = {};
++
++ eq = strchr(var, '=');
++ if (!eq)
++ break;
++ *eq = '\0';
++ value = eq + 1;
++
++ info.name = devm_kstrdup(dev, var, GFP_KERNEL);
++ if (!info.name)
++ return -ENOMEM;
++ info.offset = data_offset + value - data;
++ info.bytes = strlen(value);
++ info.np = of_get_child_by_name(dev->of_node, info.name);
++ if (!strcmp(var, "ethaddr")) {
++ info.raw_len = strlen(value);
++ info.bytes = ETH_ALEN;
++ info.read_post_process = u_boot_env_read_post_process_ethaddr;
++ }
++
++ nvmem_add_one_cell(nvmem, &info);
++ }
++
++ return 0;
++}
++
++int u_boot_env_parse(struct device *dev, struct nvmem_device *nvmem,
++ enum u_boot_env_format format)
++{
++ size_t crc32_data_offset;
++ size_t crc32_data_len;
++ size_t crc32_offset;
++ __le32 *crc32_addr;
++ size_t data_offset;
++ size_t data_len;
++ size_t dev_size;
++ uint32_t crc32;
++ uint32_t calc;
++ uint8_t *buf;
++ int bytes;
++ int err;
++
++ dev_size = nvmem_dev_size(nvmem);
++
++ buf = kzalloc(dev_size, GFP_KERNEL);
++ if (!buf) {
++ err = -ENOMEM;
++ goto err_out;
++ }
++
++ bytes = nvmem_device_read(nvmem, 0, dev_size, buf);
++ if (bytes < 0) {
++ err = bytes;
++ goto err_kfree;
++ } else if (bytes != dev_size) {
++ err = -EIO;
++ goto err_kfree;
++ }
++
++ switch (format) {
++ case U_BOOT_FORMAT_SINGLE:
++ crc32_offset = offsetof(struct u_boot_env_image_single, crc32);
++ crc32_data_offset = offsetof(struct u_boot_env_image_single, data);
++ data_offset = offsetof(struct u_boot_env_image_single, data);
++ break;
++ case U_BOOT_FORMAT_REDUNDANT:
++ crc32_offset = offsetof(struct u_boot_env_image_redundant, crc32);
++ crc32_data_offset = offsetof(struct u_boot_env_image_redundant, data);
++ data_offset = offsetof(struct u_boot_env_image_redundant, data);
++ break;
++ case U_BOOT_FORMAT_BROADCOM:
++ crc32_offset = offsetof(struct u_boot_env_image_broadcom, crc32);
++ crc32_data_offset = offsetof(struct u_boot_env_image_broadcom, data);
++ data_offset = offsetof(struct u_boot_env_image_broadcom, data);
++ break;
++ }
++
++ if (dev_size < data_offset) {
++ dev_err(dev, "Device too small for u-boot-env\n");
++ err = -EIO;
++ goto err_kfree;
++ }
++
++ crc32_addr = (__le32 *)(buf + crc32_offset);
++ crc32 = le32_to_cpu(*crc32_addr);
++ crc32_data_len = dev_size - crc32_data_offset;
++ data_len = dev_size - data_offset;
++
++ calc = crc32(~0, buf + crc32_data_offset, crc32_data_len) ^ ~0L;
++ if (calc != crc32) {
++ dev_err(dev, "Invalid calculated CRC32: 0x%08x (expected: 0x%08x)\n", calc, crc32);
++ err = -EINVAL;
++ goto err_kfree;
++ }
++
++ buf[dev_size - 1] = '\0';
++ err = u_boot_env_parse_cells(dev, nvmem, buf, data_offset, data_len);
++
++err_kfree:
++ kfree(buf);
++err_out:
++ return err;
++}
++EXPORT_SYMBOL_GPL(u_boot_env_parse);
++
++static int u_boot_env_add_cells(struct nvmem_layout *layout)
++{
++ struct device *dev = &layout->dev;
++ enum u_boot_env_format format;
++
++ format = (uintptr_t)device_get_match_data(dev);
++
++ return u_boot_env_parse(dev, layout->nvmem, format);
++}
++
++static int u_boot_env_probe(struct nvmem_layout *layout)
++{
++ layout->add_cells = u_boot_env_add_cells;
++
++ return nvmem_layout_register(layout);
++}
++
++static void u_boot_env_remove(struct nvmem_layout *layout)
++{
++ nvmem_layout_unregister(layout);
++}
++
++static const struct of_device_id u_boot_env_of_match_table[] = {
++ { .compatible = "u-boot,env", .data = (void *)U_BOOT_FORMAT_SINGLE, },
++ { .compatible = "u-boot,env-redundant-bool", .data = (void *)U_BOOT_FORMAT_REDUNDANT, },
++ { .compatible = "u-boot,env-redundant-count", .data = (void *)U_BOOT_FORMAT_REDUNDANT, },
++ { .compatible = "brcm,env", .data = (void *)U_BOOT_FORMAT_BROADCOM, },
++ {},
++};
++
++static struct nvmem_layout_driver u_boot_env_layout = {
++ .driver = {
++ .name = "u-boot-env-layout",
++ .of_match_table = u_boot_env_of_match_table,
++ },
++ .probe = u_boot_env_probe,
++ .remove = u_boot_env_remove,
++};
++module_nvmem_layout_driver(u_boot_env_layout);
++
++MODULE_AUTHOR("Rafał Miłecki");
++MODULE_LICENSE("GPL");
++MODULE_DEVICE_TABLE(of, u_boot_env_of_match_table);
++MODULE_DESCRIPTION("NVMEM layout driver for U-Boot environment variables");
+--- /dev/null
++++ b/drivers/nvmem/layouts/u-boot-env.h
+@@ -0,0 +1,15 @@
++/* SPDX-License-Identifier: GPL-2.0-only */
++
++#ifndef _LINUX_NVMEM_LAYOUTS_U_BOOT_ENV_H
++#define _LINUX_NVMEM_LAYOUTS_U_BOOT_ENV_H
++
++enum u_boot_env_format {
++ U_BOOT_FORMAT_SINGLE,
++ U_BOOT_FORMAT_REDUNDANT,
++ U_BOOT_FORMAT_BROADCOM,
++};
++
++int u_boot_env_parse(struct device *dev, struct nvmem_device *nvmem,
++ enum u_boot_env_format format);
++
++#endif /* ifndef _LINUX_NVMEM_LAYOUTS_U_BOOT_ENV_H */
+--- a/drivers/nvmem/u-boot-env.c
++++ b/drivers/nvmem/u-boot-env.c
+@@ -3,23 +3,15 @@
+ * Copyright (C) 2022 Rafał Miłecki <rafal@milecki.pl>
+ */
+
+-#include <linux/crc32.h>
+-#include <linux/etherdevice.h>
+-#include <linux/if_ether.h>
+ #include <linux/mod_devicetable.h>
+ #include <linux/module.h>
+ #include <linux/mtd/mtd.h>
+-#include <linux/nvmem-consumer.h>
+ #include <linux/nvmem-provider.h>
+ #include <linux/of_device.h>
+ #include <linux/platform_device.h>
+ #include <linux/slab.h>
+
+-enum u_boot_env_format {
+- U_BOOT_FORMAT_SINGLE,
+- U_BOOT_FORMAT_REDUNDANT,
+- U_BOOT_FORMAT_BROADCOM,
+-};
++#include "layouts/u-boot-env.h"
+
+ struct u_boot_env {
+ struct device *dev;
+@@ -29,24 +21,6 @@ struct u_boot_env {
+ struct mtd_info *mtd;
+ };
+
+-struct u_boot_env_image_single {
+- __le32 crc32;
+- uint8_t data[];
+-} __packed;
+-
+-struct u_boot_env_image_redundant {
+- __le32 crc32;
+- u8 mark;
+- uint8_t data[];
+-} __packed;
+-
+-struct u_boot_env_image_broadcom {
+- __le32 magic;
+- __le32 len;
+- __le32 crc32;
+- DECLARE_FLEX_ARRAY(uint8_t, data);
+-} __packed;
+-
+ static int u_boot_env_read(void *context, unsigned int offset, void *val,
+ size_t bytes)
+ {
+@@ -69,141 +43,6 @@ static int u_boot_env_read(void *context
+ return 0;
+ }
+
+-static int u_boot_env_read_post_process_ethaddr(void *context, const char *id, int index,
+- unsigned int offset, void *buf, size_t bytes)
+-{
+- u8 mac[ETH_ALEN];
+-
+- if (bytes != 3 * ETH_ALEN - 1)
+- return -EINVAL;
+-
+- if (!mac_pton(buf, mac))
+- return -EINVAL;
+-
+- if (index)
+- eth_addr_add(mac, index);
+-
+- ether_addr_copy(buf, mac);
+-
+- return 0;
+-}
+-
+-static int u_boot_env_add_cells(struct u_boot_env *priv, uint8_t *buf,
+- size_t data_offset, size_t data_len)
+-{
+- struct nvmem_device *nvmem = priv->nvmem;
+- struct device *dev = priv->dev;
+- char *data = buf + data_offset;
+- char *var, *value, *eq;
+-
+- for (var = data;
+- var < data + data_len && *var;
+- var = value + strlen(value) + 1) {
+- struct nvmem_cell_info info = {};
+-
+- eq = strchr(var, '=');
+- if (!eq)
+- break;
+- *eq = '\0';
+- value = eq + 1;
+-
+- info.name = devm_kstrdup(dev, var, GFP_KERNEL);
+- if (!info.name)
+- return -ENOMEM;
+- info.offset = data_offset + value - data;
+- info.bytes = strlen(value);
+- info.np = of_get_child_by_name(dev->of_node, info.name);
+- if (!strcmp(var, "ethaddr")) {
+- info.raw_len = strlen(value);
+- info.bytes = ETH_ALEN;
+- info.read_post_process = u_boot_env_read_post_process_ethaddr;
+- }
+-
+- nvmem_add_one_cell(nvmem, &info);
+- }
+-
+- return 0;
+-}
+-
+-static int u_boot_env_parse(struct u_boot_env *priv)
+-{
+- struct nvmem_device *nvmem = priv->nvmem;
+- struct device *dev = priv->dev;
+- size_t crc32_data_offset;
+- size_t crc32_data_len;
+- size_t crc32_offset;
+- __le32 *crc32_addr;
+- size_t data_offset;
+- size_t data_len;
+- size_t dev_size;
+- uint32_t crc32;
+- uint32_t calc;
+- uint8_t *buf;
+- int bytes;
+- int err;
+-
+- dev_size = nvmem_dev_size(nvmem);
+-
+- buf = kzalloc(dev_size, GFP_KERNEL);
+- if (!buf) {
+- err = -ENOMEM;
+- goto err_out;
+- }
+-
+- bytes = nvmem_device_read(nvmem, 0, dev_size, buf);
+- if (bytes < 0) {
+- err = bytes;
+- goto err_kfree;
+- } else if (bytes != dev_size) {
+- err = -EIO;
+- goto err_kfree;
+- }
+-
+- switch (priv->format) {
+- case U_BOOT_FORMAT_SINGLE:
+- crc32_offset = offsetof(struct u_boot_env_image_single, crc32);
+- crc32_data_offset = offsetof(struct u_boot_env_image_single, data);
+- data_offset = offsetof(struct u_boot_env_image_single, data);
+- break;
+- case U_BOOT_FORMAT_REDUNDANT:
+- crc32_offset = offsetof(struct u_boot_env_image_redundant, crc32);
+- crc32_data_offset = offsetof(struct u_boot_env_image_redundant, data);
+- data_offset = offsetof(struct u_boot_env_image_redundant, data);
+- break;
+- case U_BOOT_FORMAT_BROADCOM:
+- crc32_offset = offsetof(struct u_boot_env_image_broadcom, crc32);
+- crc32_data_offset = offsetof(struct u_boot_env_image_broadcom, data);
+- data_offset = offsetof(struct u_boot_env_image_broadcom, data);
+- break;
+- }
+-
+- if (dev_size < data_offset) {
+- dev_err(dev, "Device too small for u-boot-env\n");
+- err = -EIO;
+- goto err_kfree;
+- }
+-
+- crc32_addr = (__le32 *)(buf + crc32_offset);
+- crc32 = le32_to_cpu(*crc32_addr);
+- crc32_data_len = dev_size - crc32_data_offset;
+- data_len = dev_size - data_offset;
+-
+- calc = crc32(~0, buf + crc32_data_offset, crc32_data_len) ^ ~0L;
+- if (calc != crc32) {
+- dev_err(dev, "Invalid calculated CRC32: 0x%08x (expected: 0x%08x)\n", calc, crc32);
+- err = -EINVAL;
+- goto err_kfree;
+- }
+-
+- buf[dev_size - 1] = '\0';
+- err = u_boot_env_add_cells(priv, buf, data_offset, data_len);
+-
+-err_kfree:
+- kfree(buf);
+-err_out:
+- return err;
+-}
+-
+ static int u_boot_env_probe(struct platform_device *pdev)
+ {
+ struct nvmem_config config = {
+@@ -235,7 +74,7 @@ static int u_boot_env_probe(struct platf
+ if (IS_ERR(priv->nvmem))
+ return PTR_ERR(priv->nvmem);
+
+- return u_boot_env_parse(priv);
++ return u_boot_env_parse(dev, priv->nvmem, priv->format);
+ }
+
+ static const struct of_device_id u_boot_env_of_match_table[] = {
diff --git a/target/linux/generic/backport-6.1/829-v6.10-0001-nvmem-layouts-store-owner-from-modules-with-nvmem_la.patch b/target/linux/generic/backport-6.1/829-v6.10-0001-nvmem-layouts-store-owner-from-modules-with-nvmem_la.patch
index 418d2c82b2..c15b4b2265 100644
--- a/target/linux/generic/backport-6.1/829-v6.10-0001-nvmem-layouts-store-owner-from-modules-with-nvmem_la.patch
+++ b/target/linux/generic/backport-6.1/829-v6.10-0001-nvmem-layouts-store-owner-from-modules-with-nvmem_la.patch
@@ -27,7 +27,7 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
--- a/drivers/nvmem/layouts.c
+++ b/drivers/nvmem/layouts.c
-@@ -52,13 +52,15 @@ static const struct bus_type nvmem_layou
+@@ -52,13 +52,15 @@ static struct bus_type nvmem_layout_bus_
.remove = nvmem_layout_bus_remove,
};
diff --git a/target/linux/generic/backport-6.1/841-v6.12-0001-nvmem-imx-ocotp-ele-support-i.MX95.patch b/target/linux/generic/backport-6.1/841-v6.12-0001-nvmem-imx-ocotp-ele-support-i.MX95.patch
new file mode 100644
index 0000000000..c19931b3fa
--- /dev/null
+++ b/target/linux/generic/backport-6.1/841-v6.12-0001-nvmem-imx-ocotp-ele-support-i.MX95.patch
@@ -0,0 +1,73 @@
+From c3f9b7b4e5f9de319d00784577cda42036ff243a Mon Sep 17 00:00:00 2001
+From: Peng Fan <peng.fan@nxp.com>
+Date: Mon, 2 Sep 2024 15:29:45 +0100
+Subject: [PATCH] nvmem: imx-ocotp-ele: support i.MX95
+
+i.MX95 OCOTP has same accessing method, so add an entry for i.MX95, but
+some fuse has ECC feature, so only read out the lower 16bits for ECC fuses.
+
+Signed-off-by: Peng Fan <peng.fan@nxp.com>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20240902142952.71639-3-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/imx-ocotp-ele.c | 32 +++++++++++++++++++++++++++++---
+ 1 file changed, 29 insertions(+), 3 deletions(-)
+
+--- a/drivers/nvmem/imx-ocotp-ele.c
++++ b/drivers/nvmem/imx-ocotp-ele.c
+@@ -14,8 +14,9 @@
+ #include <linux/slab.h>
+
+ enum fuse_type {
+- FUSE_FSB = 1,
+- FUSE_ELE = 2,
++ FUSE_FSB = BIT(0),
++ FUSE_ELE = BIT(1),
++ FUSE_ECC = BIT(2),
+ FUSE_INVALID = -1
+ };
+
+@@ -93,7 +94,10 @@ static int imx_ocotp_reg_read(void *cont
+ continue;
+ }
+
+- *buf++ = readl_relaxed(reg + (i << 2));
++ if (type & FUSE_ECC)
++ *buf++ = readl_relaxed(reg + (i << 2)) & GENMASK(15, 0);
++ else
++ *buf++ = readl_relaxed(reg + (i << 2));
+ }
+
+ memcpy(val, (u8 *)p, bytes);
+@@ -155,8 +159,30 @@ static const struct ocotp_devtype_data i
+ },
+ };
+
++static const struct ocotp_devtype_data imx95_ocotp_data = {
++ .reg_off = 0x8000,
++ .reg_read = imx_ocotp_reg_read,
++ .size = 2048,
++ .num_entry = 12,
++ .entry = {
++ { 0, 1, FUSE_FSB | FUSE_ECC },
++ { 7, 1, FUSE_FSB | FUSE_ECC },
++ { 9, 3, FUSE_FSB | FUSE_ECC },
++ { 12, 24, FUSE_FSB },
++ { 36, 2, FUSE_FSB | FUSE_ECC },
++ { 38, 14, FUSE_FSB },
++ { 63, 1, FUSE_ELE },
++ { 128, 16, FUSE_ELE },
++ { 188, 1, FUSE_ELE },
++ { 317, 2, FUSE_FSB | FUSE_ECC },
++ { 320, 7, FUSE_FSB },
++ { 328, 184, FUSE_FSB }
++ },
++};
++
+ static const struct of_device_id imx_ele_ocotp_dt_ids[] = {
+ { .compatible = "fsl,imx93-ocotp", .data = &imx93_ocotp_data, },
++ { .compatible = "fsl,imx95-ocotp", .data = &imx95_ocotp_data, },
+ {},
+ };
+ MODULE_DEVICE_TABLE(of, imx_ele_ocotp_dt_ids);
diff --git a/target/linux/generic/backport-6.1/841-v6.12-0002-nvmem-sunplus-ocotp-Use-devm_platform_ioremap_resour.patch b/target/linux/generic/backport-6.1/841-v6.12-0002-nvmem-sunplus-ocotp-Use-devm_platform_ioremap_resour.patch
new file mode 100644
index 0000000000..13ef50b157
--- /dev/null
+++ b/target/linux/generic/backport-6.1/841-v6.12-0002-nvmem-sunplus-ocotp-Use-devm_platform_ioremap_resour.patch
@@ -0,0 +1,44 @@
+From 98ee46391baf35987227236d0c3bb30ab6e758c8 Mon Sep 17 00:00:00 2001
+From: Zhang Zekun <zhangzekun11@huawei.com>
+Date: Mon, 2 Sep 2024 15:29:50 +0100
+Subject: [PATCH] nvmem: sunplus-ocotp: Use
+ devm_platform_ioremap_resource_byname() helper function
+
+platform_get_resource_byname() and devm_ioremap_resource() can be
+replaced by devm_platform_ioremap_resource_byname(), which can
+simplify the code logic a bit, No functional change here.
+
+Signed-off-by: Zhang Zekun <zhangzekun11@huawei.com>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20240902142952.71639-8-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/sunplus-ocotp.c | 7 ++-----
+ 1 file changed, 2 insertions(+), 5 deletions(-)
+
+--- a/drivers/nvmem/sunplus-ocotp.c
++++ b/drivers/nvmem/sunplus-ocotp.c
+@@ -159,7 +159,6 @@ static int sp_ocotp_probe(struct platfor
+ struct device *dev = &pdev->dev;
+ struct nvmem_device *nvmem;
+ struct sp_ocotp_priv *otp;
+- struct resource *res;
+ int ret;
+
+ otp = devm_kzalloc(dev, sizeof(*otp), GFP_KERNEL);
+@@ -168,13 +167,11 @@ static int sp_ocotp_probe(struct platfor
+
+ otp->dev = dev;
+
+- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "hb_gpio");
+- otp->base[HB_GPIO] = devm_ioremap_resource(dev, res);
++ otp->base[HB_GPIO] = devm_platform_ioremap_resource_byname(pdev, "hb_gpio");
+ if (IS_ERR(otp->base[HB_GPIO]))
+ return PTR_ERR(otp->base[HB_GPIO]);
+
+- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "otprx");
+- otp->base[OTPRX] = devm_ioremap_resource(dev, res);
++ otp->base[OTPRX] = devm_platform_ioremap_resource_byname(pdev, "otprx");
+ if (IS_ERR(otp->base[OTPRX]))
+ return PTR_ERR(otp->base[OTPRX]);
+
diff --git a/target/linux/generic/backport-6.1/841-v6.12-0003-nvmem-layouts-add-U-Boot-env-layout.patch b/target/linux/generic/backport-6.1/841-v6.12-0003-nvmem-layouts-add-U-Boot-env-layout.patch
new file mode 100644
index 0000000000..e711c84c8d
--- /dev/null
+++ b/target/linux/generic/backport-6.1/841-v6.12-0003-nvmem-layouts-add-U-Boot-env-layout.patch
@@ -0,0 +1,525 @@
+From 5f15811286aff4664bf275a7ede64e1b8858151b Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
+Date: Mon, 2 Sep 2024 15:29:47 +0100
+Subject: [PATCH] nvmem: layouts: add U-Boot env layout
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+U-Boot environment variables are stored in a specific format. Actual
+data can be placed in various storage sources (MTD, UBI volume, EEPROM,
+NVRAM, etc.).
+
+Move all generic (NVMEM device independent) code from NVMEM device
+driver to an NVMEM layout driver. Then add a simple NVMEM layout code on
+top of it.
+
+This allows using NVMEM layout for parsing U-Boot env data stored in any
+kind of NVMEM device.
+
+The old NVMEM glue driver stays in place for handling bindings in the
+MTD context. To avoid code duplication it uses exported layout parsing
+function. Please note that handling MTD & NVMEM layout bindings may be
+refactored in the future.
+
+Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
+Reviewed-by: Miquel Raynal <miquel.raynal@bootlin.com>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20240902142952.71639-5-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ MAINTAINERS | 1 +
+ drivers/nvmem/Kconfig | 3 +-
+ drivers/nvmem/layouts/Kconfig | 11 ++
+ drivers/nvmem/layouts/Makefile | 1 +
+ drivers/nvmem/layouts/u-boot-env.c | 211 +++++++++++++++++++++++++++++
+ drivers/nvmem/layouts/u-boot-env.h | 15 ++
+ drivers/nvmem/u-boot-env.c | 165 +---------------------
+ 7 files changed, 242 insertions(+), 165 deletions(-)
+ create mode 100644 drivers/nvmem/layouts/u-boot-env.c
+ create mode 100644 drivers/nvmem/layouts/u-boot-env.h
+
+--- a/MAINTAINERS
++++ b/MAINTAINERS
+@@ -21033,6 +21033,7 @@ U-BOOT ENVIRONMENT VARIABLES
+ M: Rafał Miłecki <rafal@milecki.pl>
+ S: Maintained
+ F: Documentation/devicetree/bindings/nvmem/u-boot,env.yaml
++F: drivers/nvmem/layouts/u-boot-env.c
+ F: drivers/nvmem/u-boot-env.c
+
+ UACCE ACCELERATOR FRAMEWORK
+--- a/drivers/nvmem/Kconfig
++++ b/drivers/nvmem/Kconfig
+@@ -363,8 +363,7 @@ config NVMEM_SUNXI_SID
+ config NVMEM_U_BOOT_ENV
+ tristate "U-Boot environment variables support"
+ depends on OF && MTD
+- select CRC32
+- select GENERIC_NET_UTILS
++ select NVMEM_LAYOUT_U_BOOT_ENV
+ help
+ U-Boot stores its setup as environment variables. This driver adds
+ support for verifying & exporting such data. It also exposes variables
+--- a/drivers/nvmem/layouts/Kconfig
++++ b/drivers/nvmem/layouts/Kconfig
+@@ -26,6 +26,17 @@ config NVMEM_LAYOUT_ONIE_TLV
+
+ If unsure, say N.
+
++config NVMEM_LAYOUT_U_BOOT_ENV
++ tristate "U-Boot environment variables layout"
++ select CRC32
++ select GENERIC_NET_UTILS
++ help
++ U-Boot stores its setup as environment variables. This driver adds
++ support for verifying & exporting such data. It also exposes variables
++ as NVMEM cells so they can be referenced by other drivers.
++
++ If unsure, say N.
++
+ endmenu
+
+ endif
+--- a/drivers/nvmem/layouts/Makefile
++++ b/drivers/nvmem/layouts/Makefile
+@@ -5,3 +5,4 @@
+
+ obj-$(CONFIG_NVMEM_LAYOUT_SL28_VPD) += sl28vpd.o
+ obj-$(CONFIG_NVMEM_LAYOUT_ONIE_TLV) += onie-tlv.o
++obj-$(CONFIG_NVMEM_LAYOUT_U_BOOT_ENV) += u-boot-env.o
+--- /dev/null
++++ b/drivers/nvmem/layouts/u-boot-env.c
+@@ -0,0 +1,211 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/*
++ * Copyright (C) 2022 - 2023 Rafał Miłecki <rafal@milecki.pl>
++ */
++
++#include <linux/crc32.h>
++#include <linux/etherdevice.h>
++#include <linux/export.h>
++#include <linux/if_ether.h>
++#include <linux/nvmem-consumer.h>
++#include <linux/nvmem-provider.h>
++#include <linux/of.h>
++#include <linux/slab.h>
++
++#include "u-boot-env.h"
++
++struct u_boot_env_image_single {
++ __le32 crc32;
++ uint8_t data[];
++} __packed;
++
++struct u_boot_env_image_redundant {
++ __le32 crc32;
++ u8 mark;
++ uint8_t data[];
++} __packed;
++
++struct u_boot_env_image_broadcom {
++ __le32 magic;
++ __le32 len;
++ __le32 crc32;
++ DECLARE_FLEX_ARRAY(uint8_t, data);
++} __packed;
++
++static int u_boot_env_read_post_process_ethaddr(void *context, const char *id, int index,
++ unsigned int offset, void *buf, size_t bytes)
++{
++ u8 mac[ETH_ALEN];
++
++ if (bytes != 3 * ETH_ALEN - 1)
++ return -EINVAL;
++
++ if (!mac_pton(buf, mac))
++ return -EINVAL;
++
++ if (index)
++ eth_addr_add(mac, index);
++
++ ether_addr_copy(buf, mac);
++
++ return 0;
++}
++
++static int u_boot_env_parse_cells(struct device *dev, struct nvmem_device *nvmem, uint8_t *buf,
++ size_t data_offset, size_t data_len)
++{
++ char *data = buf + data_offset;
++ char *var, *value, *eq;
++
++ for (var = data;
++ var < data + data_len && *var;
++ var = value + strlen(value) + 1) {
++ struct nvmem_cell_info info = {};
++
++ eq = strchr(var, '=');
++ if (!eq)
++ break;
++ *eq = '\0';
++ value = eq + 1;
++
++ info.name = devm_kstrdup(dev, var, GFP_KERNEL);
++ if (!info.name)
++ return -ENOMEM;
++ info.offset = data_offset + value - data;
++ info.bytes = strlen(value);
++ info.np = of_get_child_by_name(dev->of_node, info.name);
++ if (!strcmp(var, "ethaddr")) {
++ info.raw_len = strlen(value);
++ info.bytes = ETH_ALEN;
++ info.read_post_process = u_boot_env_read_post_process_ethaddr;
++ }
++
++ nvmem_add_one_cell(nvmem, &info);
++ }
++
++ return 0;
++}
++
++int u_boot_env_parse(struct device *dev, struct nvmem_device *nvmem,
++ enum u_boot_env_format format)
++{
++ size_t crc32_data_offset;
++ size_t crc32_data_len;
++ size_t crc32_offset;
++ __le32 *crc32_addr;
++ size_t data_offset;
++ size_t data_len;
++ size_t dev_size;
++ uint32_t crc32;
++ uint32_t calc;
++ uint8_t *buf;
++ int bytes;
++ int err;
++
++ dev_size = nvmem_dev_size(nvmem);
++
++ buf = kzalloc(dev_size, GFP_KERNEL);
++ if (!buf) {
++ err = -ENOMEM;
++ goto err_out;
++ }
++
++ bytes = nvmem_device_read(nvmem, 0, dev_size, buf);
++ if (bytes < 0) {
++ err = bytes;
++ goto err_kfree;
++ } else if (bytes != dev_size) {
++ err = -EIO;
++ goto err_kfree;
++ }
++
++ switch (format) {
++ case U_BOOT_FORMAT_SINGLE:
++ crc32_offset = offsetof(struct u_boot_env_image_single, crc32);
++ crc32_data_offset = offsetof(struct u_boot_env_image_single, data);
++ data_offset = offsetof(struct u_boot_env_image_single, data);
++ break;
++ case U_BOOT_FORMAT_REDUNDANT:
++ crc32_offset = offsetof(struct u_boot_env_image_redundant, crc32);
++ crc32_data_offset = offsetof(struct u_boot_env_image_redundant, data);
++ data_offset = offsetof(struct u_boot_env_image_redundant, data);
++ break;
++ case U_BOOT_FORMAT_BROADCOM:
++ crc32_offset = offsetof(struct u_boot_env_image_broadcom, crc32);
++ crc32_data_offset = offsetof(struct u_boot_env_image_broadcom, data);
++ data_offset = offsetof(struct u_boot_env_image_broadcom, data);
++ break;
++ }
++
++ if (dev_size < data_offset) {
++ dev_err(dev, "Device too small for u-boot-env\n");
++ err = -EIO;
++ goto err_kfree;
++ }
++
++ crc32_addr = (__le32 *)(buf + crc32_offset);
++ crc32 = le32_to_cpu(*crc32_addr);
++ crc32_data_len = dev_size - crc32_data_offset;
++ data_len = dev_size - data_offset;
++
++ calc = crc32(~0, buf + crc32_data_offset, crc32_data_len) ^ ~0L;
++ if (calc != crc32) {
++ dev_err(dev, "Invalid calculated CRC32: 0x%08x (expected: 0x%08x)\n", calc, crc32);
++ err = -EINVAL;
++ goto err_kfree;
++ }
++
++ buf[dev_size - 1] = '\0';
++ err = u_boot_env_parse_cells(dev, nvmem, buf, data_offset, data_len);
++
++err_kfree:
++ kfree(buf);
++err_out:
++ return err;
++}
++EXPORT_SYMBOL_GPL(u_boot_env_parse);
++
++static int u_boot_env_add_cells(struct nvmem_layout *layout)
++{
++ struct device *dev = &layout->dev;
++ enum u_boot_env_format format;
++
++ format = (uintptr_t)device_get_match_data(dev);
++
++ return u_boot_env_parse(dev, layout->nvmem, format);
++}
++
++static int u_boot_env_probe(struct nvmem_layout *layout)
++{
++ layout->add_cells = u_boot_env_add_cells;
++
++ return nvmem_layout_register(layout);
++}
++
++static void u_boot_env_remove(struct nvmem_layout *layout)
++{
++ nvmem_layout_unregister(layout);
++}
++
++static const struct of_device_id u_boot_env_of_match_table[] = {
++ { .compatible = "u-boot,env", .data = (void *)U_BOOT_FORMAT_SINGLE, },
++ { .compatible = "u-boot,env-redundant-bool", .data = (void *)U_BOOT_FORMAT_REDUNDANT, },
++ { .compatible = "u-boot,env-redundant-count", .data = (void *)U_BOOT_FORMAT_REDUNDANT, },
++ { .compatible = "brcm,env", .data = (void *)U_BOOT_FORMAT_BROADCOM, },
++ {},
++};
++
++static struct nvmem_layout_driver u_boot_env_layout = {
++ .driver = {
++ .name = "u-boot-env-layout",
++ .of_match_table = u_boot_env_of_match_table,
++ },
++ .probe = u_boot_env_probe,
++ .remove = u_boot_env_remove,
++};
++module_nvmem_layout_driver(u_boot_env_layout);
++
++MODULE_AUTHOR("Rafał Miłecki");
++MODULE_LICENSE("GPL");
++MODULE_DEVICE_TABLE(of, u_boot_env_of_match_table);
++MODULE_DESCRIPTION("NVMEM layout driver for U-Boot environment variables");
+--- /dev/null
++++ b/drivers/nvmem/layouts/u-boot-env.h
+@@ -0,0 +1,15 @@
++/* SPDX-License-Identifier: GPL-2.0-only */
++
++#ifndef _LINUX_NVMEM_LAYOUTS_U_BOOT_ENV_H
++#define _LINUX_NVMEM_LAYOUTS_U_BOOT_ENV_H
++
++enum u_boot_env_format {
++ U_BOOT_FORMAT_SINGLE,
++ U_BOOT_FORMAT_REDUNDANT,
++ U_BOOT_FORMAT_BROADCOM,
++};
++
++int u_boot_env_parse(struct device *dev, struct nvmem_device *nvmem,
++ enum u_boot_env_format format);
++
++#endif /* ifndef _LINUX_NVMEM_LAYOUTS_U_BOOT_ENV_H */
+--- a/drivers/nvmem/u-boot-env.c
++++ b/drivers/nvmem/u-boot-env.c
+@@ -3,23 +3,15 @@
+ * Copyright (C) 2022 Rafał Miłecki <rafal@milecki.pl>
+ */
+
+-#include <linux/crc32.h>
+-#include <linux/etherdevice.h>
+-#include <linux/if_ether.h>
+ #include <linux/mod_devicetable.h>
+ #include <linux/module.h>
+ #include <linux/mtd/mtd.h>
+-#include <linux/nvmem-consumer.h>
+ #include <linux/nvmem-provider.h>
+ #include <linux/of_device.h>
+ #include <linux/platform_device.h>
+ #include <linux/slab.h>
+
+-enum u_boot_env_format {
+- U_BOOT_FORMAT_SINGLE,
+- U_BOOT_FORMAT_REDUNDANT,
+- U_BOOT_FORMAT_BROADCOM,
+-};
++#include "layouts/u-boot-env.h"
+
+ struct u_boot_env {
+ struct device *dev;
+@@ -29,24 +21,6 @@ struct u_boot_env {
+ struct mtd_info *mtd;
+ };
+
+-struct u_boot_env_image_single {
+- __le32 crc32;
+- uint8_t data[];
+-} __packed;
+-
+-struct u_boot_env_image_redundant {
+- __le32 crc32;
+- u8 mark;
+- uint8_t data[];
+-} __packed;
+-
+-struct u_boot_env_image_broadcom {
+- __le32 magic;
+- __le32 len;
+- __le32 crc32;
+- DECLARE_FLEX_ARRAY(uint8_t, data);
+-} __packed;
+-
+ static int u_boot_env_read(void *context, unsigned int offset, void *val,
+ size_t bytes)
+ {
+@@ -69,141 +43,6 @@ static int u_boot_env_read(void *context
+ return 0;
+ }
+
+-static int u_boot_env_read_post_process_ethaddr(void *context, const char *id, int index,
+- unsigned int offset, void *buf, size_t bytes)
+-{
+- u8 mac[ETH_ALEN];
+-
+- if (bytes != 3 * ETH_ALEN - 1)
+- return -EINVAL;
+-
+- if (!mac_pton(buf, mac))
+- return -EINVAL;
+-
+- if (index)
+- eth_addr_add(mac, index);
+-
+- ether_addr_copy(buf, mac);
+-
+- return 0;
+-}
+-
+-static int u_boot_env_add_cells(struct u_boot_env *priv, uint8_t *buf,
+- size_t data_offset, size_t data_len)
+-{
+- struct nvmem_device *nvmem = priv->nvmem;
+- struct device *dev = priv->dev;
+- char *data = buf + data_offset;
+- char *var, *value, *eq;
+-
+- for (var = data;
+- var < data + data_len && *var;
+- var = value + strlen(value) + 1) {
+- struct nvmem_cell_info info = {};
+-
+- eq = strchr(var, '=');
+- if (!eq)
+- break;
+- *eq = '\0';
+- value = eq + 1;
+-
+- info.name = devm_kstrdup(dev, var, GFP_KERNEL);
+- if (!info.name)
+- return -ENOMEM;
+- info.offset = data_offset + value - data;
+- info.bytes = strlen(value);
+- info.np = of_get_child_by_name(dev->of_node, info.name);
+- if (!strcmp(var, "ethaddr")) {
+- info.raw_len = strlen(value);
+- info.bytes = ETH_ALEN;
+- info.read_post_process = u_boot_env_read_post_process_ethaddr;
+- }
+-
+- nvmem_add_one_cell(nvmem, &info);
+- }
+-
+- return 0;
+-}
+-
+-static int u_boot_env_parse(struct u_boot_env *priv)
+-{
+- struct nvmem_device *nvmem = priv->nvmem;
+- struct device *dev = priv->dev;
+- size_t crc32_data_offset;
+- size_t crc32_data_len;
+- size_t crc32_offset;
+- __le32 *crc32_addr;
+- size_t data_offset;
+- size_t data_len;
+- size_t dev_size;
+- uint32_t crc32;
+- uint32_t calc;
+- uint8_t *buf;
+- int bytes;
+- int err;
+-
+- dev_size = nvmem_dev_size(nvmem);
+-
+- buf = kzalloc(dev_size, GFP_KERNEL);
+- if (!buf) {
+- err = -ENOMEM;
+- goto err_out;
+- }
+-
+- bytes = nvmem_device_read(nvmem, 0, dev_size, buf);
+- if (bytes < 0) {
+- err = bytes;
+- goto err_kfree;
+- } else if (bytes != dev_size) {
+- err = -EIO;
+- goto err_kfree;
+- }
+-
+- switch (priv->format) {
+- case U_BOOT_FORMAT_SINGLE:
+- crc32_offset = offsetof(struct u_boot_env_image_single, crc32);
+- crc32_data_offset = offsetof(struct u_boot_env_image_single, data);
+- data_offset = offsetof(struct u_boot_env_image_single, data);
+- break;
+- case U_BOOT_FORMAT_REDUNDANT:
+- crc32_offset = offsetof(struct u_boot_env_image_redundant, crc32);
+- crc32_data_offset = offsetof(struct u_boot_env_image_redundant, data);
+- data_offset = offsetof(struct u_boot_env_image_redundant, data);
+- break;
+- case U_BOOT_FORMAT_BROADCOM:
+- crc32_offset = offsetof(struct u_boot_env_image_broadcom, crc32);
+- crc32_data_offset = offsetof(struct u_boot_env_image_broadcom, data);
+- data_offset = offsetof(struct u_boot_env_image_broadcom, data);
+- break;
+- }
+-
+- if (dev_size < data_offset) {
+- dev_err(dev, "Device too small for u-boot-env\n");
+- err = -EIO;
+- goto err_kfree;
+- }
+-
+- crc32_addr = (__le32 *)(buf + crc32_offset);
+- crc32 = le32_to_cpu(*crc32_addr);
+- crc32_data_len = dev_size - crc32_data_offset;
+- data_len = dev_size - data_offset;
+-
+- calc = crc32(~0, buf + crc32_data_offset, crc32_data_len) ^ ~0L;
+- if (calc != crc32) {
+- dev_err(dev, "Invalid calculated CRC32: 0x%08x (expected: 0x%08x)\n", calc, crc32);
+- err = -EINVAL;
+- goto err_kfree;
+- }
+-
+- buf[dev_size - 1] = '\0';
+- err = u_boot_env_add_cells(priv, buf, data_offset, data_len);
+-
+-err_kfree:
+- kfree(buf);
+-err_out:
+- return err;
+-}
+-
+ static int u_boot_env_probe(struct platform_device *pdev)
+ {
+ struct nvmem_config config = {
+@@ -235,7 +74,7 @@ static int u_boot_env_probe(struct platf
+ if (IS_ERR(priv->nvmem))
+ return PTR_ERR(priv->nvmem);
+
+- return u_boot_env_parse(priv);
++ return u_boot_env_parse(dev, priv->nvmem, priv->format);
+ }
+
+ static const struct of_device_id u_boot_env_of_match_table[] = {
diff --git a/target/linux/generic/backport-6.6/823-v6.12-0001-nvmem-imx-ocotp-ele-support-i.MX95.patch b/target/linux/generic/backport-6.6/823-v6.12-0001-nvmem-imx-ocotp-ele-support-i.MX95.patch
new file mode 100644
index 0000000000..c19931b3fa
--- /dev/null
+++ b/target/linux/generic/backport-6.6/823-v6.12-0001-nvmem-imx-ocotp-ele-support-i.MX95.patch
@@ -0,0 +1,73 @@
+From c3f9b7b4e5f9de319d00784577cda42036ff243a Mon Sep 17 00:00:00 2001
+From: Peng Fan <peng.fan@nxp.com>
+Date: Mon, 2 Sep 2024 15:29:45 +0100
+Subject: [PATCH] nvmem: imx-ocotp-ele: support i.MX95
+
+i.MX95 OCOTP has same accessing method, so add an entry for i.MX95, but
+some fuse has ECC feature, so only read out the lower 16bits for ECC fuses.
+
+Signed-off-by: Peng Fan <peng.fan@nxp.com>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20240902142952.71639-3-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/imx-ocotp-ele.c | 32 +++++++++++++++++++++++++++++---
+ 1 file changed, 29 insertions(+), 3 deletions(-)
+
+--- a/drivers/nvmem/imx-ocotp-ele.c
++++ b/drivers/nvmem/imx-ocotp-ele.c
+@@ -14,8 +14,9 @@
+ #include <linux/slab.h>
+
+ enum fuse_type {
+- FUSE_FSB = 1,
+- FUSE_ELE = 2,
++ FUSE_FSB = BIT(0),
++ FUSE_ELE = BIT(1),
++ FUSE_ECC = BIT(2),
+ FUSE_INVALID = -1
+ };
+
+@@ -93,7 +94,10 @@ static int imx_ocotp_reg_read(void *cont
+ continue;
+ }
+
+- *buf++ = readl_relaxed(reg + (i << 2));
++ if (type & FUSE_ECC)
++ *buf++ = readl_relaxed(reg + (i << 2)) & GENMASK(15, 0);
++ else
++ *buf++ = readl_relaxed(reg + (i << 2));
+ }
+
+ memcpy(val, (u8 *)p, bytes);
+@@ -155,8 +159,30 @@ static const struct ocotp_devtype_data i
+ },
+ };
+
++static const struct ocotp_devtype_data imx95_ocotp_data = {
++ .reg_off = 0x8000,
++ .reg_read = imx_ocotp_reg_read,
++ .size = 2048,
++ .num_entry = 12,
++ .entry = {
++ { 0, 1, FUSE_FSB | FUSE_ECC },
++ { 7, 1, FUSE_FSB | FUSE_ECC },
++ { 9, 3, FUSE_FSB | FUSE_ECC },
++ { 12, 24, FUSE_FSB },
++ { 36, 2, FUSE_FSB | FUSE_ECC },
++ { 38, 14, FUSE_FSB },
++ { 63, 1, FUSE_ELE },
++ { 128, 16, FUSE_ELE },
++ { 188, 1, FUSE_ELE },
++ { 317, 2, FUSE_FSB | FUSE_ECC },
++ { 320, 7, FUSE_FSB },
++ { 328, 184, FUSE_FSB }
++ },
++};
++
+ static const struct of_device_id imx_ele_ocotp_dt_ids[] = {
+ { .compatible = "fsl,imx93-ocotp", .data = &imx93_ocotp_data, },
++ { .compatible = "fsl,imx95-ocotp", .data = &imx95_ocotp_data, },
+ {},
+ };
+ MODULE_DEVICE_TABLE(of, imx_ele_ocotp_dt_ids);
diff --git a/target/linux/generic/backport-6.6/823-v6.12-0002-nvmem-sunplus-ocotp-Use-devm_platform_ioremap_resour.patch b/target/linux/generic/backport-6.6/823-v6.12-0002-nvmem-sunplus-ocotp-Use-devm_platform_ioremap_resour.patch
new file mode 100644
index 0000000000..13ef50b157
--- /dev/null
+++ b/target/linux/generic/backport-6.6/823-v6.12-0002-nvmem-sunplus-ocotp-Use-devm_platform_ioremap_resour.patch
@@ -0,0 +1,44 @@
+From 98ee46391baf35987227236d0c3bb30ab6e758c8 Mon Sep 17 00:00:00 2001
+From: Zhang Zekun <zhangzekun11@huawei.com>
+Date: Mon, 2 Sep 2024 15:29:50 +0100
+Subject: [PATCH] nvmem: sunplus-ocotp: Use
+ devm_platform_ioremap_resource_byname() helper function
+
+platform_get_resource_byname() and devm_ioremap_resource() can be
+replaced by devm_platform_ioremap_resource_byname(), which can
+simplify the code logic a bit, No functional change here.
+
+Signed-off-by: Zhang Zekun <zhangzekun11@huawei.com>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20240902142952.71639-8-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/sunplus-ocotp.c | 7 ++-----
+ 1 file changed, 2 insertions(+), 5 deletions(-)
+
+--- a/drivers/nvmem/sunplus-ocotp.c
++++ b/drivers/nvmem/sunplus-ocotp.c
+@@ -159,7 +159,6 @@ static int sp_ocotp_probe(struct platfor
+ struct device *dev = &pdev->dev;
+ struct nvmem_device *nvmem;
+ struct sp_ocotp_priv *otp;
+- struct resource *res;
+ int ret;
+
+ otp = devm_kzalloc(dev, sizeof(*otp), GFP_KERNEL);
+@@ -168,13 +167,11 @@ static int sp_ocotp_probe(struct platfor
+
+ otp->dev = dev;
+
+- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "hb_gpio");
+- otp->base[HB_GPIO] = devm_ioremap_resource(dev, res);
++ otp->base[HB_GPIO] = devm_platform_ioremap_resource_byname(pdev, "hb_gpio");
+ if (IS_ERR(otp->base[HB_GPIO]))
+ return PTR_ERR(otp->base[HB_GPIO]);
+
+- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "otprx");
+- otp->base[OTPRX] = devm_ioremap_resource(dev, res);
++ otp->base[OTPRX] = devm_platform_ioremap_resource_byname(pdev, "otprx");
+ if (IS_ERR(otp->base[OTPRX]))
+ return PTR_ERR(otp->base[OTPRX]);
+
diff --git a/target/linux/generic/backport-6.6/823-v6.12-0003-nvmem-layouts-add-U-Boot-env-layout.patch b/target/linux/generic/backport-6.6/823-v6.12-0003-nvmem-layouts-add-U-Boot-env-layout.patch
new file mode 100644
index 0000000000..15a0f77bb3
--- /dev/null
+++ b/target/linux/generic/backport-6.6/823-v6.12-0003-nvmem-layouts-add-U-Boot-env-layout.patch
@@ -0,0 +1,525 @@
+From 5f15811286aff4664bf275a7ede64e1b8858151b Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
+Date: Mon, 2 Sep 2024 15:29:47 +0100
+Subject: [PATCH] nvmem: layouts: add U-Boot env layout
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+U-Boot environment variables are stored in a specific format. Actual
+data can be placed in various storage sources (MTD, UBI volume, EEPROM,
+NVRAM, etc.).
+
+Move all generic (NVMEM device independent) code from NVMEM device
+driver to an NVMEM layout driver. Then add a simple NVMEM layout code on
+top of it.
+
+This allows using NVMEM layout for parsing U-Boot env data stored in any
+kind of NVMEM device.
+
+The old NVMEM glue driver stays in place for handling bindings in the
+MTD context. To avoid code duplication it uses exported layout parsing
+function. Please note that handling MTD & NVMEM layout bindings may be
+refactored in the future.
+
+Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
+Reviewed-by: Miquel Raynal <miquel.raynal@bootlin.com>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20240902142952.71639-5-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ MAINTAINERS | 1 +
+ drivers/nvmem/Kconfig | 3 +-
+ drivers/nvmem/layouts/Kconfig | 11 ++
+ drivers/nvmem/layouts/Makefile | 1 +
+ drivers/nvmem/layouts/u-boot-env.c | 211 +++++++++++++++++++++++++++++
+ drivers/nvmem/layouts/u-boot-env.h | 15 ++
+ drivers/nvmem/u-boot-env.c | 165 +---------------------
+ 7 files changed, 242 insertions(+), 165 deletions(-)
+ create mode 100644 drivers/nvmem/layouts/u-boot-env.c
+ create mode 100644 drivers/nvmem/layouts/u-boot-env.h
+
+--- a/MAINTAINERS
++++ b/MAINTAINERS
+@@ -21988,6 +21988,7 @@ U-BOOT ENVIRONMENT VARIABLES
+ M: Rafał Miłecki <rafal@milecki.pl>
+ S: Maintained
+ F: Documentation/devicetree/bindings/nvmem/u-boot,env.yaml
++F: drivers/nvmem/layouts/u-boot-env.c
+ F: drivers/nvmem/u-boot-env.c
+
+ UACCE ACCELERATOR FRAMEWORK
+--- a/drivers/nvmem/Kconfig
++++ b/drivers/nvmem/Kconfig
+@@ -363,8 +363,7 @@ config NVMEM_SUNXI_SID
+ config NVMEM_U_BOOT_ENV
+ tristate "U-Boot environment variables support"
+ depends on OF && MTD
+- select CRC32
+- select GENERIC_NET_UTILS
++ select NVMEM_LAYOUT_U_BOOT_ENV
+ help
+ U-Boot stores its setup as environment variables. This driver adds
+ support for verifying & exporting such data. It also exposes variables
+--- a/drivers/nvmem/layouts/Kconfig
++++ b/drivers/nvmem/layouts/Kconfig
+@@ -26,6 +26,17 @@ config NVMEM_LAYOUT_ONIE_TLV
+
+ If unsure, say N.
+
++config NVMEM_LAYOUT_U_BOOT_ENV
++ tristate "U-Boot environment variables layout"
++ select CRC32
++ select GENERIC_NET_UTILS
++ help
++ U-Boot stores its setup as environment variables. This driver adds
++ support for verifying & exporting such data. It also exposes variables
++ as NVMEM cells so they can be referenced by other drivers.
++
++ If unsure, say N.
++
+ endmenu
+
+ endif
+--- a/drivers/nvmem/layouts/Makefile
++++ b/drivers/nvmem/layouts/Makefile
+@@ -5,3 +5,4 @@
+
+ obj-$(CONFIG_NVMEM_LAYOUT_SL28_VPD) += sl28vpd.o
+ obj-$(CONFIG_NVMEM_LAYOUT_ONIE_TLV) += onie-tlv.o
++obj-$(CONFIG_NVMEM_LAYOUT_U_BOOT_ENV) += u-boot-env.o
+--- /dev/null
++++ b/drivers/nvmem/layouts/u-boot-env.c
+@@ -0,0 +1,211 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/*
++ * Copyright (C) 2022 - 2023 Rafał Miłecki <rafal@milecki.pl>
++ */
++
++#include <linux/crc32.h>
++#include <linux/etherdevice.h>
++#include <linux/export.h>
++#include <linux/if_ether.h>
++#include <linux/nvmem-consumer.h>
++#include <linux/nvmem-provider.h>
++#include <linux/of.h>
++#include <linux/slab.h>
++
++#include "u-boot-env.h"
++
++struct u_boot_env_image_single {
++ __le32 crc32;
++ uint8_t data[];
++} __packed;
++
++struct u_boot_env_image_redundant {
++ __le32 crc32;
++ u8 mark;
++ uint8_t data[];
++} __packed;
++
++struct u_boot_env_image_broadcom {
++ __le32 magic;
++ __le32 len;
++ __le32 crc32;
++ DECLARE_FLEX_ARRAY(uint8_t, data);
++} __packed;
++
++static int u_boot_env_read_post_process_ethaddr(void *context, const char *id, int index,
++ unsigned int offset, void *buf, size_t bytes)
++{
++ u8 mac[ETH_ALEN];
++
++ if (bytes != 3 * ETH_ALEN - 1)
++ return -EINVAL;
++
++ if (!mac_pton(buf, mac))
++ return -EINVAL;
++
++ if (index)
++ eth_addr_add(mac, index);
++
++ ether_addr_copy(buf, mac);
++
++ return 0;
++}
++
++static int u_boot_env_parse_cells(struct device *dev, struct nvmem_device *nvmem, uint8_t *buf,
++ size_t data_offset, size_t data_len)
++{
++ char *data = buf + data_offset;
++ char *var, *value, *eq;
++
++ for (var = data;
++ var < data + data_len && *var;
++ var = value + strlen(value) + 1) {
++ struct nvmem_cell_info info = {};
++
++ eq = strchr(var, '=');
++ if (!eq)
++ break;
++ *eq = '\0';
++ value = eq + 1;
++
++ info.name = devm_kstrdup(dev, var, GFP_KERNEL);
++ if (!info.name)
++ return -ENOMEM;
++ info.offset = data_offset + value - data;
++ info.bytes = strlen(value);
++ info.np = of_get_child_by_name(dev->of_node, info.name);
++ if (!strcmp(var, "ethaddr")) {
++ info.raw_len = strlen(value);
++ info.bytes = ETH_ALEN;
++ info.read_post_process = u_boot_env_read_post_process_ethaddr;
++ }
++
++ nvmem_add_one_cell(nvmem, &info);
++ }
++
++ return 0;
++}
++
++int u_boot_env_parse(struct device *dev, struct nvmem_device *nvmem,
++ enum u_boot_env_format format)
++{
++ size_t crc32_data_offset;
++ size_t crc32_data_len;
++ size_t crc32_offset;
++ __le32 *crc32_addr;
++ size_t data_offset;
++ size_t data_len;
++ size_t dev_size;
++ uint32_t crc32;
++ uint32_t calc;
++ uint8_t *buf;
++ int bytes;
++ int err;
++
++ dev_size = nvmem_dev_size(nvmem);
++
++ buf = kzalloc(dev_size, GFP_KERNEL);
++ if (!buf) {
++ err = -ENOMEM;
++ goto err_out;
++ }
++
++ bytes = nvmem_device_read(nvmem, 0, dev_size, buf);
++ if (bytes < 0) {
++ err = bytes;
++ goto err_kfree;
++ } else if (bytes != dev_size) {
++ err = -EIO;
++ goto err_kfree;
++ }
++
++ switch (format) {
++ case U_BOOT_FORMAT_SINGLE:
++ crc32_offset = offsetof(struct u_boot_env_image_single, crc32);
++ crc32_data_offset = offsetof(struct u_boot_env_image_single, data);
++ data_offset = offsetof(struct u_boot_env_image_single, data);
++ break;
++ case U_BOOT_FORMAT_REDUNDANT:
++ crc32_offset = offsetof(struct u_boot_env_image_redundant, crc32);
++ crc32_data_offset = offsetof(struct u_boot_env_image_redundant, data);
++ data_offset = offsetof(struct u_boot_env_image_redundant, data);
++ break;
++ case U_BOOT_FORMAT_BROADCOM:
++ crc32_offset = offsetof(struct u_boot_env_image_broadcom, crc32);
++ crc32_data_offset = offsetof(struct u_boot_env_image_broadcom, data);
++ data_offset = offsetof(struct u_boot_env_image_broadcom, data);
++ break;
++ }
++
++ if (dev_size < data_offset) {
++ dev_err(dev, "Device too small for u-boot-env\n");
++ err = -EIO;
++ goto err_kfree;
++ }
++
++ crc32_addr = (__le32 *)(buf + crc32_offset);
++ crc32 = le32_to_cpu(*crc32_addr);
++ crc32_data_len = dev_size - crc32_data_offset;
++ data_len = dev_size - data_offset;
++
++ calc = crc32(~0, buf + crc32_data_offset, crc32_data_len) ^ ~0L;
++ if (calc != crc32) {
++ dev_err(dev, "Invalid calculated CRC32: 0x%08x (expected: 0x%08x)\n", calc, crc32);
++ err = -EINVAL;
++ goto err_kfree;
++ }
++
++ buf[dev_size - 1] = '\0';
++ err = u_boot_env_parse_cells(dev, nvmem, buf, data_offset, data_len);
++
++err_kfree:
++ kfree(buf);
++err_out:
++ return err;
++}
++EXPORT_SYMBOL_GPL(u_boot_env_parse);
++
++static int u_boot_env_add_cells(struct nvmem_layout *layout)
++{
++ struct device *dev = &layout->dev;
++ enum u_boot_env_format format;
++
++ format = (uintptr_t)device_get_match_data(dev);
++
++ return u_boot_env_parse(dev, layout->nvmem, format);
++}
++
++static int u_boot_env_probe(struct nvmem_layout *layout)
++{
++ layout->add_cells = u_boot_env_add_cells;
++
++ return nvmem_layout_register(layout);
++}
++
++static void u_boot_env_remove(struct nvmem_layout *layout)
++{
++ nvmem_layout_unregister(layout);
++}
++
++static const struct of_device_id u_boot_env_of_match_table[] = {
++ { .compatible = "u-boot,env", .data = (void *)U_BOOT_FORMAT_SINGLE, },
++ { .compatible = "u-boot,env-redundant-bool", .data = (void *)U_BOOT_FORMAT_REDUNDANT, },
++ { .compatible = "u-boot,env-redundant-count", .data = (void *)U_BOOT_FORMAT_REDUNDANT, },
++ { .compatible = "brcm,env", .data = (void *)U_BOOT_FORMAT_BROADCOM, },
++ {},
++};
++
++static struct nvmem_layout_driver u_boot_env_layout = {
++ .driver = {
++ .name = "u-boot-env-layout",
++ .of_match_table = u_boot_env_of_match_table,
++ },
++ .probe = u_boot_env_probe,
++ .remove = u_boot_env_remove,
++};
++module_nvmem_layout_driver(u_boot_env_layout);
++
++MODULE_AUTHOR("Rafał Miłecki");
++MODULE_LICENSE("GPL");
++MODULE_DEVICE_TABLE(of, u_boot_env_of_match_table);
++MODULE_DESCRIPTION("NVMEM layout driver for U-Boot environment variables");
+--- /dev/null
++++ b/drivers/nvmem/layouts/u-boot-env.h
+@@ -0,0 +1,15 @@
++/* SPDX-License-Identifier: GPL-2.0-only */
++
++#ifndef _LINUX_NVMEM_LAYOUTS_U_BOOT_ENV_H
++#define _LINUX_NVMEM_LAYOUTS_U_BOOT_ENV_H
++
++enum u_boot_env_format {
++ U_BOOT_FORMAT_SINGLE,
++ U_BOOT_FORMAT_REDUNDANT,
++ U_BOOT_FORMAT_BROADCOM,
++};
++
++int u_boot_env_parse(struct device *dev, struct nvmem_device *nvmem,
++ enum u_boot_env_format format);
++
++#endif /* ifndef _LINUX_NVMEM_LAYOUTS_U_BOOT_ENV_H */
+--- a/drivers/nvmem/u-boot-env.c
++++ b/drivers/nvmem/u-boot-env.c
+@@ -3,23 +3,15 @@
+ * Copyright (C) 2022 Rafał Miłecki <rafal@milecki.pl>
+ */
+
+-#include <linux/crc32.h>
+-#include <linux/etherdevice.h>
+-#include <linux/if_ether.h>
+ #include <linux/mod_devicetable.h>
+ #include <linux/module.h>
+ #include <linux/mtd/mtd.h>
+-#include <linux/nvmem-consumer.h>
+ #include <linux/nvmem-provider.h>
+ #include <linux/of.h>
+ #include <linux/platform_device.h>
+ #include <linux/slab.h>
+
+-enum u_boot_env_format {
+- U_BOOT_FORMAT_SINGLE,
+- U_BOOT_FORMAT_REDUNDANT,
+- U_BOOT_FORMAT_BROADCOM,
+-};
++#include "layouts/u-boot-env.h"
+
+ struct u_boot_env {
+ struct device *dev;
+@@ -29,24 +21,6 @@ struct u_boot_env {
+ struct mtd_info *mtd;
+ };
+
+-struct u_boot_env_image_single {
+- __le32 crc32;
+- uint8_t data[];
+-} __packed;
+-
+-struct u_boot_env_image_redundant {
+- __le32 crc32;
+- u8 mark;
+- uint8_t data[];
+-} __packed;
+-
+-struct u_boot_env_image_broadcom {
+- __le32 magic;
+- __le32 len;
+- __le32 crc32;
+- DECLARE_FLEX_ARRAY(uint8_t, data);
+-} __packed;
+-
+ static int u_boot_env_read(void *context, unsigned int offset, void *val,
+ size_t bytes)
+ {
+@@ -69,141 +43,6 @@ static int u_boot_env_read(void *context
+ return 0;
+ }
+
+-static int u_boot_env_read_post_process_ethaddr(void *context, const char *id, int index,
+- unsigned int offset, void *buf, size_t bytes)
+-{
+- u8 mac[ETH_ALEN];
+-
+- if (bytes != 3 * ETH_ALEN - 1)
+- return -EINVAL;
+-
+- if (!mac_pton(buf, mac))
+- return -EINVAL;
+-
+- if (index)
+- eth_addr_add(mac, index);
+-
+- ether_addr_copy(buf, mac);
+-
+- return 0;
+-}
+-
+-static int u_boot_env_add_cells(struct u_boot_env *priv, uint8_t *buf,
+- size_t data_offset, size_t data_len)
+-{
+- struct nvmem_device *nvmem = priv->nvmem;
+- struct device *dev = priv->dev;
+- char *data = buf + data_offset;
+- char *var, *value, *eq;
+-
+- for (var = data;
+- var < data + data_len && *var;
+- var = value + strlen(value) + 1) {
+- struct nvmem_cell_info info = {};
+-
+- eq = strchr(var, '=');
+- if (!eq)
+- break;
+- *eq = '\0';
+- value = eq + 1;
+-
+- info.name = devm_kstrdup(dev, var, GFP_KERNEL);
+- if (!info.name)
+- return -ENOMEM;
+- info.offset = data_offset + value - data;
+- info.bytes = strlen(value);
+- info.np = of_get_child_by_name(dev->of_node, info.name);
+- if (!strcmp(var, "ethaddr")) {
+- info.raw_len = strlen(value);
+- info.bytes = ETH_ALEN;
+- info.read_post_process = u_boot_env_read_post_process_ethaddr;
+- }
+-
+- nvmem_add_one_cell(nvmem, &info);
+- }
+-
+- return 0;
+-}
+-
+-static int u_boot_env_parse(struct u_boot_env *priv)
+-{
+- struct nvmem_device *nvmem = priv->nvmem;
+- struct device *dev = priv->dev;
+- size_t crc32_data_offset;
+- size_t crc32_data_len;
+- size_t crc32_offset;
+- __le32 *crc32_addr;
+- size_t data_offset;
+- size_t data_len;
+- size_t dev_size;
+- uint32_t crc32;
+- uint32_t calc;
+- uint8_t *buf;
+- int bytes;
+- int err;
+-
+- dev_size = nvmem_dev_size(nvmem);
+-
+- buf = kzalloc(dev_size, GFP_KERNEL);
+- if (!buf) {
+- err = -ENOMEM;
+- goto err_out;
+- }
+-
+- bytes = nvmem_device_read(nvmem, 0, dev_size, buf);
+- if (bytes < 0) {
+- err = bytes;
+- goto err_kfree;
+- } else if (bytes != dev_size) {
+- err = -EIO;
+- goto err_kfree;
+- }
+-
+- switch (priv->format) {
+- case U_BOOT_FORMAT_SINGLE:
+- crc32_offset = offsetof(struct u_boot_env_image_single, crc32);
+- crc32_data_offset = offsetof(struct u_boot_env_image_single, data);
+- data_offset = offsetof(struct u_boot_env_image_single, data);
+- break;
+- case U_BOOT_FORMAT_REDUNDANT:
+- crc32_offset = offsetof(struct u_boot_env_image_redundant, crc32);
+- crc32_data_offset = offsetof(struct u_boot_env_image_redundant, data);
+- data_offset = offsetof(struct u_boot_env_image_redundant, data);
+- break;
+- case U_BOOT_FORMAT_BROADCOM:
+- crc32_offset = offsetof(struct u_boot_env_image_broadcom, crc32);
+- crc32_data_offset = offsetof(struct u_boot_env_image_broadcom, data);
+- data_offset = offsetof(struct u_boot_env_image_broadcom, data);
+- break;
+- }
+-
+- if (dev_size < data_offset) {
+- dev_err(dev, "Device too small for u-boot-env\n");
+- err = -EIO;
+- goto err_kfree;
+- }
+-
+- crc32_addr = (__le32 *)(buf + crc32_offset);
+- crc32 = le32_to_cpu(*crc32_addr);
+- crc32_data_len = dev_size - crc32_data_offset;
+- data_len = dev_size - data_offset;
+-
+- calc = crc32(~0, buf + crc32_data_offset, crc32_data_len) ^ ~0L;
+- if (calc != crc32) {
+- dev_err(dev, "Invalid calculated CRC32: 0x%08x (expected: 0x%08x)\n", calc, crc32);
+- err = -EINVAL;
+- goto err_kfree;
+- }
+-
+- buf[dev_size - 1] = '\0';
+- err = u_boot_env_add_cells(priv, buf, data_offset, data_len);
+-
+-err_kfree:
+- kfree(buf);
+-err_out:
+- return err;
+-}
+-
+ static int u_boot_env_probe(struct platform_device *pdev)
+ {
+ struct nvmem_config config = {
+@@ -235,7 +74,7 @@ static int u_boot_env_probe(struct platf
+ if (IS_ERR(priv->nvmem))
+ return PTR_ERR(priv->nvmem);
+
+- return u_boot_env_parse(priv);
++ return u_boot_env_parse(dev, priv->nvmem, priv->format);
+ }
+
+ static const struct of_device_id u_boot_env_of_match_table[] = {
diff --git a/target/linux/generic/config-5.15 b/target/linux/generic/config-5.15
index 9711b8372f..a34ba551be 100644
--- a/target/linux/generic/config-5.15
+++ b/target/linux/generic/config-5.15
@@ -4453,6 +4453,7 @@ CONFIG_NMI_LOG_BUF_SHIFT=13
# CONFIG_NVMEM_IMX_OCOTP is not set
# CONFIG_NVMEM_LAYOUT_ONIE_TLV is not set
# CONFIG_NVMEM_LAYOUT_SL28_VPD is not set
+# CONFIG_NVMEM_LAYOUT_U_BOOT_ENV is not set
# CONFIG_NVMEM_REBOOT_MODE is not set
# CONFIG_NVMEM_RMEM is not set
# CONFIG_NVMEM_SYSFS is not set
diff --git a/target/linux/generic/config-6.1 b/target/linux/generic/config-6.1
index ef4ab7bac1..ef61717d78 100644
--- a/target/linux/generic/config-6.1
+++ b/target/linux/generic/config-6.1
@@ -4624,6 +4624,7 @@ CONFIG_NMI_LOG_BUF_SHIFT=13
# CONFIG_NVMEM_IMX_OCOTP is not set
# CONFIG_NVMEM_LAYOUT_ONIE_TLV is not set
# CONFIG_NVMEM_LAYOUT_SL28_VPD is not set
+# CONFIG_NVMEM_LAYOUT_U_BOOT_ENV is not set
# CONFIG_NVMEM_REBOOT_MODE is not set
# CONFIG_NVMEM_RMEM is not set
# CONFIG_NVMEM_SYSFS is not set
diff --git a/target/linux/generic/config-6.6 b/target/linux/generic/config-6.6
index 0e41607260..71fdf59d33 100644
--- a/target/linux/generic/config-6.6
+++ b/target/linux/generic/config-6.6
@@ -4335,6 +4335,7 @@ CONFIG_NLS_DEFAULT="iso8859-1"
# CONFIG_NVMEM_IMX_OCOTP is not set
# CONFIG_NVMEM_LAYOUT_ONIE_TLV is not set
# CONFIG_NVMEM_LAYOUT_SL28_VPD is not set
+# CONFIG_NVMEM_LAYOUT_U_BOOT_ENV is not set
# CONFIG_NVMEM_REBOOT_MODE is not set
# CONFIG_NVMEM_RMEM is not set
# CONFIG_NVMEM_SYSFS is not set
diff --git a/target/linux/generic/pending-5.15/802-nvmem-u-boot-env-align-endianness-of-crc32-values.patch b/target/linux/generic/pending-5.15/802-nvmem-u-boot-env-align-endianness-of-crc32-values.patch
index 46acd47f63..29fe668f8d 100644
--- a/target/linux/generic/pending-5.15/802-nvmem-u-boot-env-align-endianness-of-crc32-values.patch
+++ b/target/linux/generic/pending-5.15/802-nvmem-u-boot-env-align-endianness-of-crc32-values.patch
@@ -1,7 +1,7 @@
From 0e71cac033bb7689c4dfa2e6814191337ef770f5 Mon Sep 17 00:00:00 2001
From: INAGAKI Hiroshi <musashino.open@gmail.com>
Date: Thu, 13 Oct 2022 00:51:33 +0900
-Subject: [PATCH] nvmem: u-boot-env: align endianness of crc32 values
+Subject: [PATCH] nvmem: layouts: u-boot-env: align endianness of crc32 values
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
@@ -31,12 +31,10 @@ Acked-by: Rafał Miłecki <rafal@milecki.pl>
Tested-by: Christian Lamparter <chunkeey@gmail.com>
Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
---
- drivers/nvmem/u-boot-env.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
---- a/drivers/nvmem/u-boot-env.c
-+++ b/drivers/nvmem/u-boot-env.c
-@@ -188,7 +188,7 @@ static int u_boot_env_parse(struct u_boo
+--- a/drivers/nvmem/layouts/u-boot-env.c
++++ b/drivers/nvmem/layouts/u-boot-env.c
+@@ -148,7 +148,7 @@ int u_boot_env_parse(struct device *dev,
crc32_data_len = dev_size - crc32_data_offset;
data_len = dev_size - data_offset;
diff --git a/target/linux/generic/pending-6.1/510-block-add-uImage.FIT-subimage-block-driver.patch b/target/linux/generic/pending-6.1/510-block-add-uImage.FIT-subimage-block-driver.patch
index 700eda1c8e..befc505dbc 100644
--- a/target/linux/generic/pending-6.1/510-block-add-uImage.FIT-subimage-block-driver.patch
+++ b/target/linux/generic/pending-6.1/510-block-add-uImage.FIT-subimage-block-driver.patch
@@ -36,7 +36,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
--- a/MAINTAINERS
+++ b/MAINTAINERS
-@@ -21059,6 +21059,12 @@ F: Documentation/filesystems/ubifs-authe
+@@ -21060,6 +21060,12 @@ F: Documentation/filesystems/ubifs-authe
F: Documentation/filesystems/ubifs.rst
F: fs/ubifs/
diff --git a/target/linux/generic/pending-6.1/802-nvmem-u-boot-env-align-endianness-of-crc32-values.patch b/target/linux/generic/pending-6.1/802-nvmem-u-boot-env-align-endianness-of-crc32-values.patch
index 46acd47f63..29fe668f8d 100644
--- a/target/linux/generic/pending-6.1/802-nvmem-u-boot-env-align-endianness-of-crc32-values.patch
+++ b/target/linux/generic/pending-6.1/802-nvmem-u-boot-env-align-endianness-of-crc32-values.patch
@@ -1,7 +1,7 @@
From 0e71cac033bb7689c4dfa2e6814191337ef770f5 Mon Sep 17 00:00:00 2001
From: INAGAKI Hiroshi <musashino.open@gmail.com>
Date: Thu, 13 Oct 2022 00:51:33 +0900
-Subject: [PATCH] nvmem: u-boot-env: align endianness of crc32 values
+Subject: [PATCH] nvmem: layouts: u-boot-env: align endianness of crc32 values
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
@@ -31,12 +31,10 @@ Acked-by: Rafał Miłecki <rafal@milecki.pl>
Tested-by: Christian Lamparter <chunkeey@gmail.com>
Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
---
- drivers/nvmem/u-boot-env.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
---- a/drivers/nvmem/u-boot-env.c
-+++ b/drivers/nvmem/u-boot-env.c
-@@ -188,7 +188,7 @@ static int u_boot_env_parse(struct u_boo
+--- a/drivers/nvmem/layouts/u-boot-env.c
++++ b/drivers/nvmem/layouts/u-boot-env.c
+@@ -148,7 +148,7 @@ int u_boot_env_parse(struct device *dev,
crc32_data_len = dev_size - crc32_data_offset;
data_len = dev_size - data_offset;
diff --git a/target/linux/generic/pending-6.6/510-block-add-uImage.FIT-subimage-block-driver.patch b/target/linux/generic/pending-6.6/510-block-add-uImage.FIT-subimage-block-driver.patch
index 5cc85083d2..ff38964ece 100644
--- a/target/linux/generic/pending-6.6/510-block-add-uImage.FIT-subimage-block-driver.patch
+++ b/target/linux/generic/pending-6.6/510-block-add-uImage.FIT-subimage-block-driver.patch
@@ -36,7 +36,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
--- a/MAINTAINERS
+++ b/MAINTAINERS
-@@ -22014,6 +22014,12 @@ F: Documentation/filesystems/ubifs-authe
+@@ -22015,6 +22015,12 @@ F: Documentation/filesystems/ubifs-authe
F: Documentation/filesystems/ubifs.rst
F: fs/ubifs/
diff --git a/target/linux/generic/pending-6.6/802-nvmem-u-boot-env-align-endianness-of-crc32-values.patch b/target/linux/generic/pending-6.6/802-nvmem-u-boot-env-align-endianness-of-crc32-values.patch
index 46acd47f63..29fe668f8d 100644
--- a/target/linux/generic/pending-6.6/802-nvmem-u-boot-env-align-endianness-of-crc32-values.patch
+++ b/target/linux/generic/pending-6.6/802-nvmem-u-boot-env-align-endianness-of-crc32-values.patch
@@ -1,7 +1,7 @@
From 0e71cac033bb7689c4dfa2e6814191337ef770f5 Mon Sep 17 00:00:00 2001
From: INAGAKI Hiroshi <musashino.open@gmail.com>
Date: Thu, 13 Oct 2022 00:51:33 +0900
-Subject: [PATCH] nvmem: u-boot-env: align endianness of crc32 values
+Subject: [PATCH] nvmem: layouts: u-boot-env: align endianness of crc32 values
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
@@ -31,12 +31,10 @@ Acked-by: Rafał Miłecki <rafal@milecki.pl>
Tested-by: Christian Lamparter <chunkeey@gmail.com>
Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
---
- drivers/nvmem/u-boot-env.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
---- a/drivers/nvmem/u-boot-env.c
-+++ b/drivers/nvmem/u-boot-env.c
-@@ -188,7 +188,7 @@ static int u_boot_env_parse(struct u_boo
+--- a/drivers/nvmem/layouts/u-boot-env.c
++++ b/drivers/nvmem/layouts/u-boot-env.c
+@@ -148,7 +148,7 @@ int u_boot_env_parse(struct device *dev,
crc32_data_len = dev_size - crc32_data_offset;
data_len = dev_size - data_offset;