[PATCH 3/6] sysinfo: tq_eeprom: new driver

Alexander Feilke Alexander.Feilke at ew.tq-group.com
Fri May 8 10:48:31 CEST 2026


From: Nora Schiffer <nora.schiffer at ew.tq-group.com>

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 at ew.tq-group.com>
Signed-off-by: Max Merchel <Max.Merchel at ew.tq-group.com>
Signed-off-by: Alexander Feilke <alexander.feilke at ew.tq-group.com>
---
 drivers/sysinfo/Kconfig     |   8 ++
 drivers/sysinfo/Makefile    |   1 +
 drivers/sysinfo/tq_eeprom.c | 223 ++++++++++++++++++++++++++++++++++++
 include/sysinfo/tq_eeprom.h |  24 ++++
 4 files changed, 256 insertions(+)
 create mode 100644 drivers/sysinfo/tq_eeprom.c
 create mode 100644 include/sysinfo/tq_eeprom.h

diff --git a/drivers/sysinfo/Kconfig b/drivers/sysinfo/Kconfig
index df83df69ffb..6922dac9170 100644
--- a/drivers/sysinfo/Kconfig
+++ b/drivers/sysinfo/Kconfig
@@ -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
diff --git a/drivers/sysinfo/Makefile b/drivers/sysinfo/Makefile
index 26ca3150999..d21fb3c2270 100644
--- a/drivers/sysinfo/Makefile
+++ b/drivers/sysinfo/Makefile
@@ -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
index 00000000000..b10803f9b5a
--- /dev/null
+++ b/drivers/sysinfo/tq_eeprom.c
@@ -0,0 +1,223 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2014-2026 TQ-Systems GmbH <u-boot at ew.tq-group.com>,
+ * D-82229 Seefeld, Germany.
+ * Author: Nora Schiffer
+ */
+
+#include <display_options.h>
+#include <dm.h>
+#include <i2c_eeprom.h>
+#include <log.h>
+#include <net.h>
+#include <dm/device_compat.h>
+#include <linux/ctype.h>
+#include <linux/sizes.h>
+#include <sysinfo/tq_eeprom.h>
+
+#define TQ_EE_RSV0_BYTES		0x20
+#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 rsv0[TQ_EE_RSV0_BYTES];
+	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) == 0x80,
+	      "struct tq_eeprom_data has incorrect size");
+
+/**
+ * struct sysinfo_tq_eeprom_priv - sysinfo private data
+ */
+struct sysinfo_tq_eeprom_priv {
+	struct udevice *i2c_eeprom;
+	int offset;
+
+	/* 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_old(const struct tq_eeprom_data *data)
+{
+	int i;
+
+	for (i = 0; i < sizeof(data->serial); i++) {
+		if (!isdigit(data->serial[i]))
+			break;
+	}
+
+	return i;
+}
+
+static int tq_eeprom_serial_len_new(const struct tq_eeprom_data *data)
+{
+	int i;
+
+	for (i = 0; i < sizeof(data->serial); i++) {
+		if (!(isdigit(data->serial[i]) || 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);
+	int len;
+
+	if (data->serial[0] == 'T' && data->serial[1] == 'Q')
+		len = tq_eeprom_serial_len_new(data);
+	else
+		len = tq_eeprom_serial_len_old(data);
+
+	/* 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 = i2c_eeprom_read(priv->i2c_eeprom, priv->offset,
+			      (u8 *)&data, sizeof(data));
+	if (ret < 0) {
+		dev_err(dev, "EEPROM read failed: %d\n", ret);
+		return -EIO;
+	}
+
+	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 = uclass_get_device_by_phandle(UCLASS_I2C_EEPROM, dev, "i2c-eeprom",
+					   &priv->i2c_eeprom);
+	if (ret) {
+		dev_err(dev, "i2c-eeprom backing device not found: %d\n", ret);
+		return ret;
+	}
+
+	priv->offset = dev_read_u32_default(dev, "offset", 0);
+
+	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
index 00000000000..6b1bddd7ce0
--- /dev/null
+++ b/include/sysinfo/tq_eeprom.h
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (c) 2023-2026 TQ-Systems GmbH <u-boot at 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__ */
-- 
2.34.1



More information about the U-Boot mailing list