Linux device trees describe calibration and configuration values as
cells in the Allwinner Security ID block. U-Boot has an NVMEM consumer
API but no provider for the direct-read SID variants.

Add a read-only misc driver for the A10, A20, A83T, D1, A64, H5
and H6 register layouts so firmware consumers can use those standard
nvmem-cells descriptions.

Signed-off-by: James Hilliard <[email protected]>
---
 MAINTAINERS              |   1 +
 drivers/misc/Kconfig     |  10 ++++
 drivers/misc/Makefile    |   1 +
 drivers/misc/sunxi_sid.c | 118 +++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 130 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index 95b1f3b5ab3..e724357419b 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -702,6 +702,7 @@ F:  arch/arm/include/asm/arch-sunxi/
 F:     arch/arm/mach-sunxi/
 F:     board/sunxi/
 F:     drivers/clk/sunxi/
+F:     drivers/misc/sunxi_sid.c
 F:     drivers/phy/allwinner/
 F:     drivers/pinctrl/sunxi/
 F:     drivers/video/sunxi/
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 681d5f28ea4..6f6456120d0 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -51,6 +51,16 @@ config NVMEM
          up hardware configuration data such as MAC addresses and calibration
          settings.
 
+config SUNXI_SID
+       bool "Allwinner Security ID NVMEM support"
+       depends on ARCH_SUNXI
+       depends on MISC
+       select NVMEM
+       help
+         Enable read-only access to the Security ID efuse area found in
+         Allwinner SoCs. This allows consumers to use the standard nvmem-cells
+         properties from Linux-compatible device trees.
+
 config SPL_NVMEM
        bool "NVMEM support in SPL"
        help
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index f20c989ffc6..3ec2bc2b201 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -78,6 +78,7 @@ obj-$(CONFIG_SMSC_LPC47M) += smsc_lpc47m.o
 obj-$(CONFIG_SMSC_SIO1007) += smsc_sio1007.o
 obj-$(CONFIG_STM32MP_FUSE) += stm32mp_fuse.o
 obj-$(CONFIG_STM32_RCC) += stm32_rcc.o
+obj-$(CONFIG_SUNXI_SID) += sunxi_sid.o
 obj-$(CONFIG_SYS_DPAA_QBMAN) += fsl_portals.o
 obj-$(CONFIG_TEGRA186_BPMP) += tegra186_bpmp.o
 obj-$(CONFIG_TEGRA_CAR) += tegra_car.o
diff --git a/drivers/misc/sunxi_sid.c b/drivers/misc/sunxi_sid.c
new file mode 100644
index 00000000000..86a1379d07e
--- /dev/null
+++ b/drivers/misc/sunxi_sid.c
@@ -0,0 +1,118 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Allwinner Security ID NVMEM provider
+ *
+ * Copyright (C) 2026 James Hilliard <[email protected]>
+ */
+
+#include <dm.h>
+#include <dm/read.h>
+#include <misc.h>
+#include <asm/io.h>
+
+struct sunxi_sid_data {
+       unsigned int value_offset;
+       unsigned int size;
+};
+
+struct sunxi_sid_priv {
+       const struct sunxi_sid_data *data;
+       u8 __iomem *base;
+};
+
+static int sunxi_sid_read(struct udevice *dev, int offset, void *buf, int size)
+{
+       struct sunxi_sid_priv *priv = dev_get_priv(dev);
+       u8 *out = buf;
+       int i;
+
+       if (offset < 0 || !buf || size < 0 || offset > priv->data->size ||
+           size > priv->data->size - offset)
+               return -EINVAL;
+
+       for (i = 0; i < size; i++) {
+               unsigned int byte_offset = offset + i;
+               u32 value;
+
+               value = readl(priv->base + priv->data->value_offset +
+                             (byte_offset & ~3));
+               out[i] = value >> (8 * (byte_offset & 3));
+       }
+
+       return size;
+}
+
+static int sunxi_sid_probe(struct udevice *dev)
+{
+       struct sunxi_sid_priv *priv = dev_get_priv(dev);
+
+       priv->data = (const void *)dev_get_driver_data(dev);
+       priv->base = dev_read_addr_ptr(dev);
+       if (!priv->base)
+               return -EINVAL;
+
+       return 0;
+}
+
+static const struct misc_ops sunxi_sid_ops = {
+       .read = sunxi_sid_read,
+};
+
+static const struct sunxi_sid_data sun4i_a10_sid_data = {
+       .size = 0x10,
+};
+
+static const struct sunxi_sid_data sun7i_a20_sid_data = {
+       .size = 0x200,
+};
+
+static const struct sunxi_sid_data sun50i_a64_sid_data = {
+       .value_offset = 0x200,
+       .size = 0x100,
+};
+
+static const struct sunxi_sid_data sun50i_h6_sid_data = {
+       .value_offset = 0x200,
+       .size = 0x200,
+};
+
+static const struct udevice_id sunxi_sid_ids[] = {
+       {
+               .compatible = "allwinner,sun4i-a10-sid",
+               .data = (ulong)&sun4i_a10_sid_data,
+       },
+       {
+               .compatible = "allwinner,sun7i-a20-sid",
+               .data = (ulong)&sun7i_a20_sid_data,
+       },
+       {
+               .compatible = "allwinner,sun8i-a83t-sid",
+               .data = (ulong)&sun50i_a64_sid_data,
+       },
+       {
+               .compatible = "allwinner,sun20i-d1-sid",
+               .data = (ulong)&sun50i_a64_sid_data,
+       },
+       {
+               .compatible = "allwinner,sun50i-a64-sid",
+               .data = (ulong)&sun50i_a64_sid_data,
+       },
+       {
+               .compatible = "allwinner,sun50i-h5-sid",
+               .data = (ulong)&sun50i_a64_sid_data,
+       },
+       {
+               .compatible = "allwinner,sun50i-h6-sid",
+               .data = (ulong)&sun50i_h6_sid_data,
+       },
+       { }
+};
+
+U_BOOT_DRIVER(sunxi_sid) = {
+       .name = "sunxi_sid",
+       .id = UCLASS_MISC,
+       .of_match = sunxi_sid_ids,
+       .probe = sunxi_sid_probe,
+       .ops = &sunxi_sid_ops,
+       .priv_auto = sizeof(struct sunxi_sid_priv),
+};

-- 
2.53.0

Reply via email to