[PATCH 3/4] nvmem: layouts: Add Rockchip OTP CPUID layout driver
From: Alexey Charkov
Date: Tue Sep 01 2026 - 11:46:05 EST
Rockchip SoCs customarily use the CPU ID programmed into their on-chip OTP
memory to derive stable Ethernet MAC addresses even when a board doesn't
otherwise have dedicated storage for those. The derivation depends on the
particular bootloader implementation (e.g. it is done by upstream U-Boot,
which patches the derived MAC addresses at runtime into the device tree it
hands to the kernel). Using less featureful bootloaders, such as direct
boot to Linux from SPL, leaves the kernel with only a random MAC address
instead, even though everything required for the derivation is equally
available to Linux as it is to U-Boot.
Reproduce the same derivation in the kernel and expose the result as an
nvmem cell named "mac-address", so that of_get_mac_address() picks it up
through its standard nvmem fallback. The address index comes from the DT
phandle argument, which lets both interfaces of a dual Ethernet board
share a single cell.
Enable the layout by default on Rockchip, as consumers of its cells would
otherwise defer their probe indefinitely.
Signed-off-by: Alexey Charkov <alchark@xxxxxxxxxxx>
---
MAINTAINERS | 1 +
drivers/nvmem/layouts/Kconfig | 13 ++++
drivers/nvmem/layouts/Makefile | 1 +
drivers/nvmem/layouts/rockchip-otp-cpuid.c | 119 +++++++++++++++++++++++++++++
4 files changed, 134 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index b35b3677bf0b..ae98e1fc6121 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -23718,6 +23718,7 @@ M: Alexey Charkov <alchark@xxxxxxxxxxx>
L: linux-rockchip@xxxxxxxxxxxxxxxxxxx
S: Maintained
F: Documentation/devicetree/bindings/nvmem/layouts/rockchip,rk3576-otp-cpuid.yaml
+F: drivers/nvmem/layouts/rockchip-otp-cpuid.c
ROCKCHIP RK3568 RANDOM NUMBER GENERATOR SUPPORT
M: Daniel Golle <daniel@xxxxxxxxxxxxxx>
diff --git a/drivers/nvmem/layouts/Kconfig b/drivers/nvmem/layouts/Kconfig
index 5e586dfebe47..6e2511ac6f7e 100644
--- a/drivers/nvmem/layouts/Kconfig
+++ b/drivers/nvmem/layouts/Kconfig
@@ -26,6 +26,19 @@ config NVMEM_LAYOUT_ONIE_TLV
If unsure, say N.
+config NVMEM_LAYOUT_ROCKCHIP_OTP_CPUID
+ tristate "Rockchip OTP CPU ID layout support"
+ default ARCH_ROCKCHIP
+ select CRYPTO_LIB_SHA256
+ help
+ Say Y here if you want to expose the MAC addresses that Rockchip
+ bootloaders derive from the CPU ID programmed into the OTP memory of
+ Rockchip SoCs. Boards which have no other source of MAC addresses
+ need this to get stable ones when the bootloader does not patch them
+ into the device tree.
+
+ 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..a0ade4c22c79 100644
--- a/drivers/nvmem/layouts/Makefile
+++ b/drivers/nvmem/layouts/Makefile
@@ -6,4 +6,5 @@
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_ROCKCHIP_OTP_CPUID) += rockchip-otp-cpuid.o
obj-$(CONFIG_NVMEM_LAYOUT_U_BOOT_ENV) += u-boot-env.o
diff --git a/drivers/nvmem/layouts/rockchip-otp-cpuid.c b/drivers/nvmem/layouts/rockchip-otp-cpuid.c
new file mode 100644
index 000000000000..61f509c4a8cf
--- /dev/null
+++ b/drivers/nvmem/layouts/rockchip-otp-cpuid.c
@@ -0,0 +1,119 @@
+// SPDX-License-Identifier: GPL-2.0
+// SPDX-FileCopyrightText: 2026 Flipper FZCO
+/*
+ * NVMEM layout for the CPU ID in Rockchip OTP memory
+ */
+
+#include <crypto/sha2.h>
+#include <linux/device-id/of.h>
+#include <linux/etherdevice.h>
+#include <linux/hex.h>
+#include <linux/module.h>
+#include <linux/nvmem-provider.h>
+#include <linux/of.h>
+#include <uapi/linux/if_ether.h>
+
+#define ROCKCHIP_CPUID_LEN 16
+
+struct rockchip_cpuid_data {
+ unsigned int offset;
+};
+
+static int rockchip_cpuid_mac_pp(void *priv, const char *id, int index,
+ unsigned int offset, void *buf, size_t bytes)
+{
+ char cpuid[ROCKCHIP_CPUID_LEN * 2];
+ u8 digest[SHA256_DIGEST_SIZE];
+ u8 *mac = buf;
+
+ if (bytes != ROCKCHIP_CPUID_LEN)
+ return -EINVAL;
+
+ if (index < 0 || index > 1)
+ return -EINVAL;
+
+ /* The buffer still holds the raw CPU ID at this point */
+ bin2hex(cpuid, mac, ROCKCHIP_CPUID_LEN);
+
+ sha256(cpuid, sizeof(cpuid), digest);
+
+ memcpy(mac, digest, ETH_ALEN);
+ mac[0] &= 0xfe; /* clear the multicast bit */
+ mac[0] |= 0x02; /* set the locally administered bit */
+ mac[5] ^= index;
+
+ if (!is_valid_ether_addr(mac))
+ return -EINVAL;
+
+ return 0;
+}
+
+static int rockchip_cpuid_add_cells(struct nvmem_layout *layout)
+{
+ const struct rockchip_cpuid_data *data;
+ struct nvmem_cell_info info = {0};
+ struct device_node *layout_np;
+ int ret;
+
+ data = of_device_get_match_data(&layout->dev);
+ if (!data)
+ return -EINVAL;
+
+ layout_np = of_nvmem_layout_get_container(layout->nvmem);
+ if (!layout_np)
+ return -ENOENT;
+
+ info.name = "mac-address";
+ info.offset = data->offset;
+ info.raw_len = ROCKCHIP_CPUID_LEN;
+ info.bytes = ETH_ALEN;
+ info.read_post_process = rockchip_cpuid_mac_pp;
+ info.np = of_get_child_by_name(layout_np, info.name);
+
+ of_node_put(layout_np);
+
+ ret = nvmem_add_one_cell(layout->nvmem, &info);
+ if (ret)
+ of_node_put(info.np);
+
+ return ret;
+}
+
+static int rockchip_cpuid_probe(struct nvmem_layout *layout)
+{
+ layout->add_cells = rockchip_cpuid_add_cells;
+
+ return nvmem_layout_register(layout);
+}
+
+static void rockchip_cpuid_remove(struct nvmem_layout *layout)
+{
+ nvmem_layout_unregister(layout);
+}
+
+static const struct rockchip_cpuid_data rk3576_cpuid_data = {
+ .offset = 0x0a,
+};
+
+static const struct of_device_id rockchip_cpuid_of_match_table[] = {
+ {
+ .compatible = "rockchip,rk3576-otp-cpuid",
+ .data = &rk3576_cpuid_data,
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(of, rockchip_cpuid_of_match_table);
+
+static struct nvmem_layout_driver rockchip_cpuid_layout = {
+ .driver = {
+ .name = "rockchip-otp-cpuid-layout",
+ .of_match_table = rockchip_cpuid_of_match_table,
+ },
+ .probe = rockchip_cpuid_probe,
+ .remove = rockchip_cpuid_remove,
+};
+module_nvmem_layout_driver(rockchip_cpuid_layout);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Alexey Charkov <alchark@xxxxxxxxxxx>");
+MODULE_DESCRIPTION("NVMEM layout driver for the CPU ID in Rockchip OTP memory");
--
2.54.0