From: Marcello Sylvester Bauer <[email protected]>

Add driver providing access to the memory-mapped system information
area on HPE GXP BMC SoCs. The 256-byte region at 0xa0017f00 contains
board data organized in two 128-byte blocks, each protected by a
16-bit checksum.

Signed-off-by: Marcello Sylvester Bauer <[email protected]>
Signed-off-by: Tan Siewert <[email protected]>
---
 MAINTAINERS               |   1 +
 drivers/misc/Kconfig      |   9 ++++
 drivers/misc/Makefile     |   1 +
 drivers/misc/gxp_eeprom.c | 107 ++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 118 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index 3ecbb50e03a..81ebed7e865 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -344,6 +344,7 @@ F:  arch/arm/mach-hpe/
 F:     board/hpe/
 F:     configs/gxp_defconfig
 F:     doc/device-tree-bindings/spi/hpe,gxp-spi.yaml
+F:     drivers/misc/gxp_eeprom.c
 F:     drivers/timer/gxp-timer.c
 F:     drivers/spi/gxp_spi.c
 
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 44415f24ae1..5f22bcb330e 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -83,6 +83,15 @@ config GATEWORKS_SC
          boards to provide a boot watchdog, power control, temperature monitor,
          voltage ADCs, and EEPROM.
 
+config GXP_EEPROM
+       bool "HPE GXP EEPROM driver"
+       depends on MISC
+       select NVMEM
+       help
+         Enable access to the memory-mapped system information area
+         on HPE GXP BMC SoCs. This region contains board data such as
+         MAC addresses, serial number, and part number.
+
 config QCOM_GENI
        bool "Qualcomm Generic Interface (GENI) driver"
        depends on MISC
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index e2170212e5a..9ddbe9cd114 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -38,6 +38,7 @@ obj-$(CONFIG_FSL_IIM) += fsl_iim.o
 obj-$(CONFIG_FSL_SEC_MON) += fsl_sec_mon.o
 obj-$(CONFIG_$(PHASE_)FS_LOADER) += fs_loader.o
 obj-$(CONFIG_GATEWORKS_SC) += gsc.o
+obj-$(CONFIG_GXP_EEPROM) += gxp_eeprom.o
 obj-$(CONFIG_GDSYS_IOEP) += gdsys_ioep.o
 obj-$(CONFIG_GDSYS_RXAUI_CTRL) += gdsys_rxaui_ctrl.o
 obj-$(CONFIG_GDSYS_SOC) += gdsys_soc.o
diff --git a/drivers/misc/gxp_eeprom.c b/drivers/misc/gxp_eeprom.c
new file mode 100644
index 00000000000..0d8c7ede6e4
--- /dev/null
+++ b/drivers/misc/gxp_eeprom.c
@@ -0,0 +1,107 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * HPE GXP EEPROM driver
+ *
+ * Copyright (C) 2026 9elements GmbH
+ */
+
+#include <dm.h>
+#include <misc.h>
+#include <asm/io.h>
+#include <dm/device_compat.h>
+
+#define GXP_EEPROM_BLOCK_SIZE          128
+
+struct gxp_eeprom_priv {
+       void __iomem *base;
+       size_t size;
+};
+
+static bool gxp_eeprom_validate_checksum(void __iomem *block)
+{
+       u8 version;
+       u16 sum = 0;
+       int i;
+
+       version = readb(block);
+       if (version != 2 && version != 3)
+               return false;
+
+       for (i = 0; i < GXP_EEPROM_BLOCK_SIZE / sizeof(u16); i++)
+               sum += readw(block + i * sizeof(u16));
+
+       return sum == 0;
+}
+
+static int gxp_eeprom_read(struct udevice *dev, int offset, void *buf,
+                          int size)
+{
+       struct gxp_eeprom_priv *priv = dev_get_priv(dev);
+       u8 *dst = buf;
+       int i;
+
+       if (offset < 0 || size <= 0 || offset + size > priv->size)
+               return -EINVAL;
+
+       for (i = 0; i < size; i++)
+               dst[i] = readb(priv->base + offset + i);
+
+       return size;
+}
+
+static int gxp_eeprom_probe(struct udevice *dev)
+{
+       struct gxp_eeprom_priv *priv = dev_get_priv(dev);
+       fdt_addr_t addr;
+       fdt_size_t size;
+       int i, num_blocks;
+
+       addr = dev_read_addr_size(dev, &size);
+       if (addr == FDT_ADDR_T_NONE) {
+               dev_err(dev, "failed to get address\n");
+               return -EINVAL;
+       }
+
+       if (size == 0 || size % GXP_EEPROM_BLOCK_SIZE != 0) {
+               dev_err(dev, "size must be a multiple of %d bytes\n",
+                       GXP_EEPROM_BLOCK_SIZE);
+               return -EINVAL;
+       }
+
+       priv->base = map_physmem(addr, size, MAP_NOCACHE);
+       if (!priv->base) {
+               dev_err(dev, "failed to map memory\n");
+               return -ENOMEM;
+       }
+       priv->size = size;
+
+       num_blocks = size / GXP_EEPROM_BLOCK_SIZE;
+       for (i = 0; i < num_blocks; i++) {
+               if (!gxp_eeprom_validate_checksum(priv->base +
+                                              i * GXP_EEPROM_BLOCK_SIZE)) {
+                       dev_err(dev, "checksum failed for block %d\n", i);
+                       return -EBADMSG;
+               }
+       }
+
+       return 0;
+}
+
+static const struct misc_ops gxp_eeprom_ops = {
+       .read = gxp_eeprom_read,
+};
+
+static const struct udevice_id gxp_eeprom_ids[] = {
+       { .compatible = "hpe,gxp-eeprom" },
+       { }
+};
+
+U_BOOT_DRIVER(gxp_eeprom) = {
+       .name           = "gxp_eeprom",
+       .id             = UCLASS_MISC,
+       .of_match       = gxp_eeprom_ids,
+       .probe          = gxp_eeprom_probe,
+       .ops            = &gxp_eeprom_ops,
+       .priv_auto      = sizeof(struct gxp_eeprom_priv),
+       .flags          = DM_FLAG_PRE_RELOC,
+};

-- 
2.53.0

Reply via email to