]> git.ipfire.org Git - thirdparty/u-boot.git/commitdiff
sysinfo: tq_eeprom: new driver
authorNora Schiffer <nora.schiffer@ew.tq-group.com>
Tue, 2 Jun 2026 11:57:51 +0000 (13:57 +0200)
committerFabio Estevam <festevam@gmail.com>
Fri, 5 Jun 2026 15:56:32 +0000 (12:56 -0300)
Introduce a sysinfo driver that can be instantiated from the device,
which will provide information from the EEPROM found on all TQ-Systems
SoMs.

Signed-off-by: Nora Schiffer <nora.schiffer@ew.tq-group.com>
Signed-off-by: Max Merchel <Max.Merchel@ew.tq-group.com>
Reviewed-by: Simon Glass <sjg@chromium.org>
Signed-off-by: Alexander Feilke <alexander.feilke@ew.tq-group.com>
MAINTAINERS
configs/tqma7_common.config
doc/device-tree-bindings/sysinfo/tq,eeprom-sysinfo.txt [new file with mode: 0644]
drivers/sysinfo/Kconfig
drivers/sysinfo/Makefile
drivers/sysinfo/tq_eeprom.c [new file with mode: 0644]
include/sysinfo/tq_eeprom.h [new file with mode: 0644]

index a55742a0baff07e35ea7f4caa2d6e2c4c9155e0d..ec7217f39f4dc74b923a1e7d517f14c3a873ad2c 100644 (file)
@@ -1865,6 +1865,7 @@ S:        Maintained
 W:     https://www.tq-group.com/en/products/tq-embedded/
 F:     board/tq/*
 F:     doc/board/tq/*
+F:     drivers/sysinfo/tq_eeprom.c
 F:     include/configs/tq*.h
 F:     include/env/tq/*
 
index 7e5e31a14abca95e177e68f6277625097671993b..9b055849ad1f705ce8b7e484873ba53f004a3ec3 100644 (file)
@@ -77,6 +77,7 @@ CONFIG_I2C_DEFAULT_BUS_NUMBER=0x3
 CONFIG_SYS_I2C_MXC=y
 CONFIG_LED=y
 CONFIG_LED_GPIO=y
+CONFIG_NVMEM=y
 CONFIG_SUPPORT_EMMC_BOOT=y
 CONFIG_FSL_USDHC=y
 CONFIG_MTD=y
diff --git a/doc/device-tree-bindings/sysinfo/tq,eeprom-sysinfo.txt b/doc/device-tree-bindings/sysinfo/tq,eeprom-sysinfo.txt
new file mode 100644 (file)
index 0000000..678fff0
--- /dev/null
@@ -0,0 +1,36 @@
+TQ EEPROM Sysinfo Driver
+------------------------
+
+This binding describes a sysinfo provider which retrieves system
+identification information from an I2C EEPROM device.
+
+Required properties:
+
+- compatible: "tq,eeprom-sysinfo"
+- nvmem-cells: phandle referencing the nvmem cell
+- nvmem-cell-names: string, should be "device_info"
+
+Example:
+
+&i2c1 {
+       eeprom@50 {
+               compatible = "atmel,24c64";
+               reg = <0x50>;
+
+               nvmem-layout {
+                       compatible = "fixed-layout";
+                       #address-cells = <1>;
+                       #size-cells = <1>;
+
+                       module_info: module-info@20 {
+                               reg = <0x20 0x60>;
+                       };
+               };
+       };
+};
+
+sysinfo {
+       compatible = "tq,eeprom-sysinfo";
+       nvmem-cells = <&module_info>;
+       nvmem-cell-names = "device_info";
+};
index df83df69ffb3b27ea290805eff79cf69674492f9..6922dac917007962837bb128ecd85a75f18bee46 100644 (file)
@@ -59,4 +59,12 @@ config SYSINFO_GPIO
          This ternary number is then mapped to a board revision name using
          device tree properties.
 
+config SYSINFO_TQ_EEPROM
+       bool "Enable TQ-Systems EEPROM sysinfo driver"
+       depends on I2C_EEPROM
+       depends on SPL_I2C_EEPROM || !SPL_SYSINFO
+       help
+         Support querying EEPROM of TQ-Systems SOMs to determine board
+         information.
+
 endif
index 26ca3150999730d0fc57390d317895c04f49e952..d21fb3c2270317a4c05152e1110d651ad274ce8b 100644 (file)
@@ -9,3 +9,4 @@ obj-$(CONFIG_SYSINFO_IOT2050) += iot2050.o
 obj-$(CONFIG_SYSINFO_RCAR3) += rcar3.o
 obj-$(CONFIG_SYSINFO_SANDBOX) += sandbox.o
 obj-$(CONFIG_SYSINFO_SMBIOS) += smbios.o
+obj-$(CONFIG_SYSINFO_TQ_EEPROM) += tq_eeprom.o
diff --git a/drivers/sysinfo/tq_eeprom.c b/drivers/sysinfo/tq_eeprom.c
new file mode 100644 (file)
index 0000000..63be07b
--- /dev/null
@@ -0,0 +1,203 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2014-2026 TQ-Systems GmbH <u-boot@ew.tq-group.com>,
+ * D-82229 Seefeld, Germany.
+ * Author: Nora Schiffer
+ */
+
+#include <dm.h>
+#include <log.h>
+#include <net.h>
+#include <dm/device_compat.h>
+#include <linux/build_bug.h>
+#include <linux/ctype.h>
+#include <nvmem.h>
+#include <sysinfo/tq_eeprom.h>
+
+#define TQ_EE_RSV1_BYTES               10
+#define TQ_EE_SERIAL_BYTES             8
+#define TQ_EE_RSV2_BYTES               8
+#define TQ_EE_BDID_BYTES               0x40
+
+struct tq_eeprom_data {
+       u8 mac[ETH_ALEN];               /* 0x20 ... 0x25 */
+       u8 rsv1[TQ_EE_RSV1_BYTES];
+       u8 serial[TQ_EE_SERIAL_BYTES];  /* 0x30 ... 0x37 */
+       u8 rsv2[TQ_EE_RSV2_BYTES];
+       u8 id[TQ_EE_BDID_BYTES];        /* 0x40 ... 0x7f */
+};
+
+static_assert(sizeof(struct tq_eeprom_data) == 0x60,
+             "struct tq_eeprom_data has incorrect size");
+
+/**
+ * struct sysinfo_tq_eeprom_priv - sysinfo private data
+ */
+struct sysinfo_tq_eeprom_priv {
+       struct nvmem_cell device_info_cell;
+
+       /* Reserve extra space for \0 in id and serial */
+       char id[TQ_EE_BDID_BYTES + 1];
+       char serial[TQ_EE_SERIAL_BYTES + 1];
+       u8 mac[ETH_ALEN];
+};
+
+static void tq_eeprom_parse_id(struct udevice *dev, const struct tq_eeprom_data *data)
+{
+       struct sysinfo_tq_eeprom_priv *priv = dev_get_priv(dev);
+       int i;
+
+       for (i = 0; i < sizeof(data->id); i++) {
+               if (!(isprint(data->id[i]) && isascii(data->id[i])))
+                       break;
+       }
+
+       if (i == 0)
+               dev_warn(dev, "no valid model name in EEPROM\n");
+
+       snprintf(priv->id, sizeof(priv->id), "%.*s", i, data->id);
+}
+
+static int tq_eeprom_serial_len(const struct tq_eeprom_data *data, bool allow_upper)
+{
+       int i;
+
+       for (i = 0; i < sizeof(data->serial); i++) {
+               if (!(isdigit(data->serial[i]) || (allow_upper && isupper(data->serial[i]))))
+                       break;
+       }
+
+       return i;
+}
+
+static void tq_eeprom_parse_serial(struct udevice *dev, const struct tq_eeprom_data *data)
+{
+       struct sysinfo_tq_eeprom_priv *priv = dev_get_priv(dev);
+       bool use_new_format;
+       int len;
+
+       use_new_format = data->serial[0] == 'T' && data->serial[1] == 'Q';
+
+       len = tq_eeprom_serial_len(data, use_new_format);
+
+       /* For now, only serial numbers with the exact size of the field are accepted */
+       if (len != sizeof(data->serial)) {
+               dev_warn(dev, "no valid serial number in EEPROM\n");
+               len = 0;
+       }
+
+       snprintf(priv->serial, sizeof(priv->serial), "%.*s", len, data->serial);
+}
+
+static int tq_eeprom_dump(const struct sysinfo_tq_eeprom_priv *priv)
+{
+       printf("TQ EEPROM:\n");
+       printf("  ID:  %s\n", priv->id[0] ? priv->id : "<invalid>");
+       printf("  SN:  %s\n", priv->serial[0] ? priv->serial : "<invalid>");
+       printf("  MAC: ");
+       if (is_valid_ethaddr(priv->mac))
+               printf("%pM\n", priv->mac);
+       else
+               printf("<invalid>\n");
+
+       return 0;
+}
+
+static int sysinfo_tq_eeprom_detect(struct udevice *dev)
+{
+       struct sysinfo_tq_eeprom_priv *priv = dev_get_priv(dev);
+       struct tq_eeprom_data data;
+       int ret;
+
+       ret = nvmem_cell_read(&priv->device_info_cell, (u8 *)&data, sizeof(data));
+       if (ret < 0) {
+               dev_err(dev, "EEPROM read failed: %d\n", ret);
+               return ret;
+       }
+
+       tq_eeprom_parse_id(dev, &data);
+       tq_eeprom_parse_serial(dev, &data);
+       memcpy(priv->mac, data.mac, ETH_ALEN);
+
+       if (!IS_ENABLED(CONFIG_SPL_BUILD))
+               tq_eeprom_dump(priv);
+
+       return 0;
+}
+
+static int sysinfo_tq_eeprom_get_str(struct udevice *dev, int id, size_t size, char *val)
+{
+       struct sysinfo_tq_eeprom_priv *priv = dev_get_priv(dev);
+
+       switch (id) {
+       case SYSID_TQ_MODEL:
+               if (!priv->id[0])
+                       return -ENODATA;
+
+               strlcpy(val, priv->id, size);
+               return 0;
+
+       case SYSID_TQ_SERIAL:
+               if (!priv->serial[0])
+                       return -ENODATA;
+
+               strlcpy(val, priv->serial, size);
+               return 0;
+
+       default:
+               return -EINVAL;
+       }
+}
+
+static int sysinfo_tq_eeprom_get_data(struct udevice *dev, int id, void **data, size_t *size)
+{
+       struct sysinfo_tq_eeprom_priv *priv = dev_get_priv(dev);
+
+       switch (id) {
+       case SYSID_TQ_MAC_ADDR:
+               if (!is_valid_ethaddr(priv->mac))
+                       return -ENODATA;
+
+               *data = priv->mac;
+               *size = sizeof(priv->mac);
+
+               return 0;
+
+       default:
+               return -EINVAL;
+       }
+}
+
+static const struct sysinfo_ops sysinfo_tq_eeprom_ops = {
+       .detect = sysinfo_tq_eeprom_detect,
+       .get_str = sysinfo_tq_eeprom_get_str,
+       .get_data = sysinfo_tq_eeprom_get_data,
+};
+
+static int sysinfo_tq_eeprom_probe(struct udevice *dev)
+{
+       struct sysinfo_tq_eeprom_priv *priv = dev_get_priv(dev);
+       int ret;
+
+       ret = nvmem_cell_get_by_name(dev, "device_info", &priv->device_info_cell);
+       if (ret) {
+               dev_err(dev, "device_info not found: %d\n", ret);
+               return ret;
+       }
+
+       return 0;
+}
+
+static const struct udevice_id sysinfo_tq_eeprom_ids[] = {
+       { .compatible = "tq,eeprom-sysinfo" },
+       { /* sentinel */ }
+};
+
+U_BOOT_DRIVER(sysinfo_tq_eeprom) = {
+       .name           = "sysinfo_tq_eeprom",
+       .id             = UCLASS_SYSINFO,
+       .of_match       = sysinfo_tq_eeprom_ids,
+       .ops            = &sysinfo_tq_eeprom_ops,
+       .priv_auto      = sizeof(struct sysinfo_tq_eeprom_priv),
+       .probe          = sysinfo_tq_eeprom_probe,
+};
diff --git a/include/sysinfo/tq_eeprom.h b/include/sysinfo/tq_eeprom.h
new file mode 100644 (file)
index 0000000..6b1bddd
--- /dev/null
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (c) 2023-2026 TQ-Systems GmbH <u-boot@ew.tq-group.com>,
+ * D-82229 Seefeld, Germany.
+ * Author: Nora Schiffer
+ */
+
+#ifndef __SYSINFO_TQ_EEPROM_H__
+#define __SYSINFO_TQ_EEPROM_H__
+
+#include <sysinfo.h>
+
+enum {
+       /* Model string of TQ-Systems SOM. This is different from BOARD_MODEL,
+        * which usually combines SOM and baseboard names for TQ hardware
+        */
+       SYSID_TQ_MODEL = SYSID_USER,
+       /* SOM serial number */
+       SYSID_TQ_SERIAL,
+       /* MAC address */
+       SYSID_TQ_MAC_ADDR,
+};
+
+#endif /* __SYSINFO_TQ_EEPROM_H__ */