Add an NVMEM layout that exposes the factory-programmed EUI-48 identifier from the Microchip/SST vendor SFDP parameter table (e.g. SST26VF064BEUI) as a "mac-address" cell, for use as a network MAC address. The vendor table is located at runtime via the SFDP NVMEM device (no offset in DT). The EUI-48 is stored in canonical MAC order, the 24-bit OUI followed by the 24-bit extension identifier, so the cell is exposed as-is. Binds to an "nvmem-layout" node with compatible "microchip,sst26vf-sfdp-eui". Signed-off-by: Manikandan Muralidharan --- MAINTAINERS | 6 + drivers/nvmem/layouts/Kconfig | 10 ++ drivers/nvmem/layouts/Makefile | 1 + drivers/nvmem/layouts/sst26vf-sfdp-eui.c | 158 +++++++++++++++++++++++ 4 files changed, 175 insertions(+) create mode 100644 drivers/nvmem/layouts/sst26vf-sfdp-eui.c diff --git a/MAINTAINERS b/MAINTAINERS index 93abbb315065..fec148278068 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -18413,6 +18413,12 @@ F: Documentation/devicetree/bindings/sound/atmel,at91-ssc.yaml F: drivers/misc/atmel-ssc.c F: include/linux/atmel-ssc.h +MICROCHIP SST SFDP EUI NVMEM LAYOUT DRIVER +M: Manikandan Muralidharan +S: Maintained +F: Documentation/devicetree/bindings/nvmem/layouts/microchip,sst26vf-sfdp-eui.yaml +F: drivers/nvmem/layouts/sst26vf-sfdp-eui.c + Microchip Timer Counter Block (TCB) Capture Driver M: Kamel Bouhara L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) diff --git a/drivers/nvmem/layouts/Kconfig b/drivers/nvmem/layouts/Kconfig index 5e586dfebe47..855c7db530da 100644 --- a/drivers/nvmem/layouts/Kconfig +++ b/drivers/nvmem/layouts/Kconfig @@ -26,6 +26,16 @@ config NVMEM_LAYOUT_ONIE_TLV If unsure, say N. +config NVMEM_LAYOUT_SST26VF_SFDP_EUI + tristate "Microchip/SST SFDP EUI-48 layout support" + help + Say Y here if you want to expose the factory-programmed EUI-48 + identifier stored in the Microchip/SST vendor-specific SFDP parameter + table (e.g. SST26VF064BEUI) as NVMEM cells, so that network drivers + can use them as a MAC address. + + If unsure, say N. + config NVMEM_LAYOUT_U_BOOT_ENV tristate "U-Boot environment variables layout" select CRC32 diff --git a/drivers/nvmem/layouts/Makefile b/drivers/nvmem/layouts/Makefile index dd6c6c70b1a9..3f5e01e40448 100644 --- a/drivers/nvmem/layouts/Makefile +++ b/drivers/nvmem/layouts/Makefile @@ -7,3 +7,4 @@ obj-$(CONFIG_NVMEM_LAYOUTS) += fixed-layout.o 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_SST26VF_SFDP_EUI) += sst26vf-sfdp-eui.o diff --git a/drivers/nvmem/layouts/sst26vf-sfdp-eui.c b/drivers/nvmem/layouts/sst26vf-sfdp-eui.c new file mode 100644 index 000000000000..c4fdda35b64c --- /dev/null +++ b/drivers/nvmem/layouts/sst26vf-sfdp-eui.c @@ -0,0 +1,158 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * NVMEM layout for the factory-programmed EUI-48 identifier stored in the + * Microchip/SST vendor-specific SFDP parameter table (e.g. SST26VF064BEUI). + * + * The whole SFDP is exposed as a read-only NVMEM device by the SPI NOR core. + * This layout locates the Microchip vendor parameter table at runtime and + * registers the EUI-48 address as an NVMEM cell, so that a network driver can + * consume it as a MAC address. No offset is hardcoded in the device tree. + * + * Copyright (C) 2026 Microchip Technology Inc. and its subsidiaries + * + * Author: Manikandan Muralidharan + */ + +#include +#include +#include +#include +#include + +/* SFDP header and parameter header, as laid out on the flash. */ +struct sfdp_header { + u8 signature[4]; + u8 minor; + u8 major; + u8 nph; + u8 unused; +}; + +struct sfdp_parameter_header { + u8 id_lsb; + u8 minor; + u8 major; + u8 length; + u8 parameter_table_pointer[3]; + u8 id_msb; +}; + +#define SFDP_SIGNATURE 0x50444653U + +#define SFDP_PARAM_HEADER_ID(h) (((h)->id_msb << 8) | (h)->id_lsb) +#define SFDP_PARAM_HEADER_PTP(h) get_unaligned_le24((h)->parameter_table_pointer) + +#define SFDP_MCHP_VENDOR_ID 0x01bf + +#define SFDP_MCHP_EUI48_MARKER_OFFSET 0x60 +#define SFDP_MCHP_EUI48_MARKER 0x30 +#define SFDP_MCHP_EUI48_OFFSET 0x61 + +static int sfdp_eui_find_vendor_table(struct nvmem_device *nvmem, u32 *ptp) +{ + struct sfdp_parameter_header ph; + struct sfdp_header hdr; + int nph, i, ret; + + ret = nvmem_device_read(nvmem, 0, sizeof(hdr), &hdr); + if (ret < 0) + return ret; + + if (get_unaligned_le32(hdr.signature) != SFDP_SIGNATURE) + return -EINVAL; + + nph = hdr.nph + 1; + + for (i = 0; i < nph; i++) { + ret = nvmem_device_read(nvmem, sizeof(hdr) + i * sizeof(ph), + sizeof(ph), &ph); + if (ret < 0) + return ret; + + if (SFDP_PARAM_HEADER_ID(&ph) != SFDP_MCHP_VENDOR_ID) + continue; + + *ptp = SFDP_PARAM_HEADER_PTP(&ph); + return 0; + } + + return -ENOENT; +} + +static int sfdp_eui_add_cells(struct nvmem_layout *layout) +{ + struct nvmem_device *nvmem = layout->nvmem; + struct device *dev = &layout->dev; + struct nvmem_cell_info info = { }; + struct device_node *layout_np; + u32 base = 0; + u8 marker; + int ret; + + ret = sfdp_eui_find_vendor_table(nvmem, &base); + if (ret == -ENOENT) { + dev_dbg(dev, "no Microchip SFDP vendor table found\n"); + return 0; + } + if (ret) + return ret; + + /* The EUI-48 is present only if its marker byte is programmed. */ + ret = nvmem_device_read(nvmem, base + SFDP_MCHP_EUI48_MARKER_OFFSET, + 1, &marker); + if (ret < 0) + return ret; + if (marker != SFDP_MCHP_EUI48_MARKER) { + dev_dbg(dev, "EUI-48 not programmed (marker 0x%02x)\n", marker); + return 0; + } + + layout_np = of_nvmem_layout_get_container(nvmem); + if (!layout_np) + return -ENOENT; + + info.name = "mac-address"; + info.offset = base + SFDP_MCHP_EUI48_OFFSET; + info.bytes = ETH_ALEN; + info.np = of_get_child_by_name(layout_np, "mac-address"); + + ret = nvmem_add_one_cell(nvmem, &info); + if (ret) + of_node_put(info.np); + + of_node_put(layout_np); + + return ret; +} + +static int sfdp_eui_probe(struct nvmem_layout *layout) +{ + layout->add_cells = sfdp_eui_add_cells; + + return nvmem_layout_register(layout); +} + +static void sfdp_eui_remove(struct nvmem_layout *layout) +{ + nvmem_layout_unregister(layout); +} + +static const struct of_device_id sfdp_eui_of_match_table[] = { + { .compatible = "microchip,sst26vf-sfdp-eui" }, + {} +}; +MODULE_DEVICE_TABLE(of, sfdp_eui_of_match_table); + +static struct nvmem_layout_driver sfdp_eui_layout = { + .driver = { + .name = "microchip-sst26vf-sfdp-eui-layout", + .of_match_table = sfdp_eui_of_match_table, + }, + .probe = sfdp_eui_probe, + .remove = sfdp_eui_remove, +}; +module_nvmem_layout_driver(sfdp_eui_layout); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Manikandan Muralidharan "); +MODULE_DESCRIPTION("NVMEM layout for the EUI-48 in the Microchip/SST SFDP vendor table"); -- 2.43.0