--- /dev/null
+From 995a6e0d3fdd1e4fb38465f224db8a4c7b1e279d Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Mon, 3 Feb 2025 00:10:18 +0100
+Subject: [PATCH 1/2] nvmem: core: generalize "mac-base" cells handling
+
+Generalize support of "mac-base" nvmem cells and provide a GPL symbol to
+permit also other NVMEM layout driver to parse mac-base cells.
+
+It's VERY COMMON for some specially formatted NVMEM to expose a mac
+address in ASCII format or HEX format hence prevent code duplication by
+exposing a common helper.
+
+Such helper will change the nvmem_info_cell and apply the correct post
+process function to correctly parse the mac address.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+---
+ drivers/nvmem/core.c | 41 +++++++++++++++++++---------------
+ include/linux/nvmem-provider.h | 4 ++++
+ 2 files changed, 27 insertions(+), 18 deletions(-)
+
+--- a/drivers/nvmem/core.c
++++ b/drivers/nvmem/core.c
+@@ -855,6 +855,27 @@ static int nvmem_mac_base_hex_read(void
+ return 0;
+ }
+
++void nvmem_layout_parse_mac_base(struct nvmem_cell_info *info)
++{
++ if (!of_device_is_compatible(info->np, "mac-base"))
++ return;
++
++ if (info->bytes == ETH_ALEN) {
++ info->raw_len = info->bytes;
++ info->bytes = ETH_ALEN;
++ info->read_post_process = nvmem_mac_base_raw_read;
++ } else if (info->bytes == 2 * ETH_ALEN) {
++ info->raw_len = info->bytes;
++ info->bytes = ETH_ALEN;
++ info->read_post_process = nvmem_mac_base_hex_read;
++ } else if (info->bytes == 3 * ETH_ALEN - 1) {
++ info->raw_len = info->bytes;
++ info->bytes = ETH_ALEN;
++ info->read_post_process = nvmem_mac_base_ascii_read;
++ }
++}
++EXPORT_SYMBOL_GPL(nvmem_layout_parse_mac_base);
++
+ static int nvmem_add_cells_from_dt(struct nvmem_device *nvmem, struct device_node *np)
+ {
+ struct device *dev = &nvmem->dev;
+@@ -894,24 +915,8 @@ 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")) {
+- if (info.bytes == ETH_ALEN) {
+- info.raw_len = info.bytes;
+- info.bytes = ETH_ALEN;
+- info.read_post_process = nvmem_mac_base_raw_read;
+- } else if (info.bytes == 2 * ETH_ALEN) {
+- info.raw_len = info.bytes;
+- info.bytes = ETH_ALEN;
+- info.read_post_process = nvmem_mac_base_hex_read;
+- } else if (info.bytes == 3 * ETH_ALEN - 1) {
+- info.raw_len = info.bytes;
+- info.bytes = ETH_ALEN;
+- info.read_post_process = nvmem_mac_base_ascii_read;
+- }
+-
+- }
+- }
++ if (of_device_is_compatible(np, "fixed-layout"))
++ nvmem_layout_parse_mac_base(&info);
+
+ ret = nvmem_add_one_cell(nvmem, &info);
+ kfree(info.name);
+--- a/include/linux/nvmem-provider.h
++++ b/include/linux/nvmem-provider.h
+@@ -242,6 +242,8 @@ static inline void nvmem_layout_unregist
+
+ #if IS_ENABLED(CONFIG_NVMEM) && IS_ENABLED(CONFIG_OF)
+
++void nvmem_layout_parse_mac_base(struct nvmem_cell_info *info);
++
+ /**
+ * of_nvmem_layout_get_container() - Get OF node of layout container
+ *
+@@ -254,6 +256,8 @@ struct device_node *of_nvmem_layout_get_
+
+ #else /* CONFIG_NVMEM && CONFIG_OF */
+
++static inline void nvmem_layout_parse_mac_base(void) {}
++
+ static inline struct device_node *of_nvmem_layout_get_container(struct nvmem_device *nvmem)
+ {
+ return NULL;
--- /dev/null
+From 38287e8ec5c0281377fc70f11f20bcd9986a05f5 Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Mon, 3 Feb 2025 00:36:12 +0100
+Subject: [PATCH 2/2] nvmem: layouts: add support for ascii-env driver
+
+Add support for simple ASCII envirorment driver for NVMEM layouts.
+
+It's very common for devices to store simple text file format in
+partition for environment varibles. The most common pattern is variable
+name, a delimiter and variable value all separated by a new line
+character (\n).
+
+This driver adds support for exporting such data and expose NVMEM cells
+so they can be referenced by other drivers. This driver also supports
+parsing mac-base NVMEM cells to parse ASCII or HEX mac address.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+---
+ drivers/nvmem/layouts/Kconfig | 13 +++
+ drivers/nvmem/layouts/Makefile | 1 +
+ drivers/nvmem/layouts/ascii-env.c | 131 ++++++++++++++++++++++++++++++
+ 3 files changed, 145 insertions(+)
+ create mode 100644 drivers/nvmem/layouts/ascii-env.c
+
+--- a/drivers/nvmem/layouts/Kconfig
++++ b/drivers/nvmem/layouts/Kconfig
+@@ -37,6 +37,19 @@ config NVMEM_LAYOUT_U_BOOT_ENV
+
+ If unsure, say N.
+
++config NVMEM_LAYOUT_ASCII_ENV
++ tristate "ASCII environment variables layout"
++ help
++ It's very common for devices to store simple text file format in
++ partition for environment varibles. The most common pattern is variable
++ name, a delimiter and variable value all separated by a new line
++ character (\n).
++ This driver adds support for exporting such data and expose NVMEM cells
++ so they can be referenced by other drivers. This driver also supports
++ parsing mac-base NVMEM cells to parse ASCII or HEX mac address.
++
++ If unsure, say N.
++
+ endmenu
+
+ endif
+--- a/drivers/nvmem/layouts/Makefile
++++ b/drivers/nvmem/layouts/Makefile
+@@ -6,3 +6,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
++obj-$(CONFIG_NVMEM_LAYOUT_ASCII_ENV) += ascii-env.o
+--- /dev/null
++++ b/drivers/nvmem/layouts/ascii-env.c
+@@ -0,0 +1,131 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/*
++ * Copyright (C) 2024 Christian Marangi <ansuelsmth@gmail.com>
++ *
++ * This borrow some parse logic from u-boot-env.
++ */
++#include <linux/nvmem-consumer.h>
++#include <linux/nvmem-provider.h>
++#include <linux/of.h>
++#include <linux/slab.h>
++
++/*
++ * Parse a buffer as an ASCII text with name delimiter value and each pattern separated
++ * with a new line char '\n'
++ * Example: (delimiter '=')
++ * name=value\nname2=value2\n
++ * 2 Cell:
++ * - name: value
++ * - name2: value2
++ */
++static int ascii_env_parse_cells(struct device *dev, struct nvmem_device *nvmem, uint8_t *buf,
++ size_t data_len, const char delim)
++{
++ char *var, *value, *eq, *lf;
++ char *data = buf;
++
++ /*
++ * Warning the inner loop take care of replacing '\n'
++ * with '\0', hence we can use strlen on value.
++ */
++ for (var = data; var < data + data_len && *var;
++ var = value + strlen(value) + 1) {
++ struct nvmem_cell_info info = {};
++
++ eq = strchr(var, delim);
++ if (!eq)
++ break;
++ *eq = '\0';
++ value = eq + 1;
++
++ /* Replace '\n' with '\0' to use strlen for value */
++ lf = strchr(value, '\n');
++ if (!lf)
++ break;
++ *lf = '\0';
++
++ info.name = devm_kstrdup(dev, var, GFP_KERNEL);
++ if (!info.name)
++ return -ENOMEM;
++ info.offset = value - data;
++ info.bytes = strlen(value);
++ info.np = of_get_child_by_name(dev->of_node, info.name);
++
++ nvmem_layout_parse_mac_base(&info);
++
++ nvmem_add_one_cell(nvmem, &info);
++ }
++
++ return 0;
++}
++
++static int ascii_env_add_cells(struct nvmem_layout *layout)
++{
++ struct nvmem_device *nvmem = layout->nvmem;
++ struct device *dev = &layout->dev;
++ size_t dev_size;
++ uint8_t *buf;
++ char delim;
++ int bytes;
++ int ret;
++
++ /* Get the delimiter for name value pattern */
++ delim = device_get_match_data(dev);
++
++ dev_size = nvmem_dev_size(nvmem);
++
++ buf = kzalloc(dev_size, GFP_KERNEL);
++ if (!buf) {
++ ret = -ENOMEM;
++ goto err_out;
++ }
++
++ bytes = nvmem_device_read(nvmem, 0, dev_size, buf);
++ if (bytes < 0) {
++ ret = bytes;
++ goto err_kfree;
++ } else if (bytes != dev_size) {
++ ret = -EIO;
++ goto err_kfree;
++ }
++
++ buf[dev_size - 1] = '\0';
++ ret = ascii_env_parse_cells(dev, nvmem, buf, dev_size, delim);
++
++err_kfree:
++ kfree(buf);
++err_out:
++ return ret;
++}
++
++static int ascii_env_probe(struct nvmem_layout *layout)
++{
++ layout->add_cells = ascii_env_add_cells;
++
++ return nvmem_layout_register(layout);
++}
++
++static void ascii_env_remove(struct nvmem_layout *layout)
++{
++ nvmem_layout_unregister(layout);
++}
++
++static const struct of_device_id ascii_env_of_match_table[] = {
++ { .compatible = "ascii-eq-delim-env", .data = (void *)'=', },
++ {},
++};
++
++static struct nvmem_layout_driver ascii_env_layout = {
++ .driver = {
++ .name = "ascii-env-layout",
++ .of_match_table = ascii_env_of_match_table,
++ },
++ .probe = ascii_env_probe,
++ .remove = ascii_env_remove,
++};
++module_nvmem_layout_driver(ascii_env_layout);
++
++MODULE_AUTHOR("Christian Marangi <ansuelsmth@gmail.com>");
++MODULE_LICENSE("GPL");
++MODULE_DEVICE_TABLE(of, ascii_env_of_match_table);
++MODULE_DESCRIPTION("NVMEM layout driver for ASCII environment variables");