[PATCH 5/8] common: add TQ EEPROM support

Sascha Hauer s.hauer at pengutronix.de
Fri Feb 2 07:31:10 PST 2024


Many TQ boards have an EEPROM with useful board information equipped.
Add support for reading this EEPROM. The code is based on the
corresponding U-Boot code from the TQ downstream U-Boot.

Right now not much of this information is actually used, the main
motivation for porting this code was to detect the DDR type on the
TQ i.MX93 boards. Distributing and printing the information is left
for a future excercise.

Signed-off-by: Sascha Hauer <s.hauer at pengutronix.de>
---
 common/Kconfig      |   3 +
 common/Makefile     |   1 +
 common/tq_eeprom.c  | 140 +++++++++++++++++++++++++++++++
 include/tq_eeprom.h | 196 ++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 340 insertions(+)
 create mode 100644 common/tq_eeprom.c
 create mode 100644 include/tq_eeprom.h

diff --git a/common/Kconfig b/common/Kconfig
index ddca1e913b..69a53f5e07 100644
--- a/common/Kconfig
+++ b/common/Kconfig
@@ -105,6 +105,9 @@ config BOOT
 config FASTBOOT_BASE
 	bool
 
+config TQ_EEPROM
+	bool
+
 menu "General Settings"
 
 config LOCALVERSION
diff --git a/common/Makefile b/common/Makefile
index c31cbab9e4..4ca1478425 100644
--- a/common/Makefile
+++ b/common/Makefile
@@ -74,6 +74,7 @@ obj-$(CONFIG_EFI)		+= efi/
 lwl-$(CONFIG_IMD)		+= imd-barebox.o
 obj-$(CONFIG_IMD)		+= imd.o
 obj-y				+= file-list.o
+obj-pbl-$(CONFIG_TQ_EEPROM)	+= tq_eeprom.o
 obj-$(CONFIG_FIRMWARE)		+= firmware.o
 obj-$(CONFIG_UBIFORMAT)		+= ubiformat.o
 obj-$(CONFIG_BAREBOX_UPDATE_IMX_NAND_FCB) += imx-bbu-nand-fcb.o
diff --git a/common/tq_eeprom.c b/common/tq_eeprom.c
new file mode 100644
index 0000000000..83a24bbb04
--- /dev/null
+++ b/common/tq_eeprom.c
@@ -0,0 +1,140 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2014-2023 TQ-Systems GmbH <u-boot at ew.tq-group.com>,
+ * D-82229 Seefeld, Germany.
+ * Author: Markus Niebel
+ */
+
+#include <common.h>
+#include <net.h>
+#include <linux/ctype.h>
+#include <crc.h>
+#include <pbl/i2c.h>
+#include <pbl/eeprom.h>
+
+#include "tq_eeprom.h"
+
+/*
+ * static EEPROM layout
+ */
+#define TQ_EE_HRCW_BYTES		0x20
+#define TQ_EE_RSV1_BYTES		10
+#define TQ_EE_RSV2_BYTES		8
+
+struct __packed tq_eeprom_data {
+	union {
+		struct tq_vard vard;
+		_Static_assert(sizeof(struct tq_vard) == TQ_EE_HRCW_BYTES, \
+			"struct tq_vard has incorrect size");
+		u8 hrcw_primary[TQ_EE_HRCW_BYTES];
+	} tq_hw_data;
+	u8 mac[TQ_EE_MAC_BYTES];		/* 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 bool tq_vard_valid(const struct tq_vard *vard)
+{
+	const unsigned char *start = (const unsigned char *)(vard) +
+		sizeof(vard->crc);
+	u16 crc;
+
+	crc = crc_itu_t(0, start, sizeof(*vard) - sizeof(vard->crc));
+
+	return vard->crc == crc;
+}
+
+phys_size_t tq_vard_memsize(u8 val, unsigned int multiply, unsigned int tmask)
+{
+	phys_size_t result = 0;
+
+	if (val != VARD_MEMSIZE_DEFAULT) {
+		result = 1 << (size_t)(val & VARD_MEMSIZE_MASK_EXP);
+		if (val & tmask)
+			result *= 3;
+		result *= multiply;
+	}
+
+	return result;
+}
+
+void tq_vard_show(const struct tq_vard *vard)
+{
+	/* display data anyway to support developer */
+	printf("HW\tREV.%02uxx\n", (unsigned int)vard->hwrev);
+	printf("RAM\ttype %u, %lu MiB, %s\n",
+	       (unsigned int)(vard->memtype & VARD_MEMTYPE_MASK_TYPE),
+	       (unsigned long)(tq_vard_ramsize(vard) / (SZ_1M)),
+	       (tq_vard_has_ramecc(vard) ? "ECC" : "no ECC"));
+	printf("RTC\t%c\nSPINOR\t%c\ne-MMC\t%c\nSE\t%c\nEEPROM\t%c\n",
+	       (tq_vard_has_rtc(vard) ? 'y' : 'n'),
+	       (tq_vard_has_spinor(vard) ? 'y' : 'n'),
+	       (tq_vard_has_emmc(vard) ? 'y' : 'n'),
+	       (tq_vard_has_secelem(vard) ? 'y' : 'n'),
+	       (tq_vard_has_eeprom(vard) ? 'y' : 'n'));
+
+	if (tq_vard_has_eeprom(vard))
+		printf("EEPROM\ttype %u, %lu KiB, page %lu\n",
+		       (unsigned int)(vard->eepromtype & VARD_EETYPE_MASK_MFR) >> 4,
+		       (unsigned long)(tq_vard_eepromsize(vard) / (SZ_1K)),
+		       tq_vard_eeprom_pgsize(vard));
+
+	printf("FORMFACTOR: ");
+
+	switch (tq_vard_get_formfactor(vard)) {
+	case VARD_FORMFACTOR_TYPE_LGA:
+		printf("LGA\n");
+		break;
+	case VARD_FORMFACTOR_TYPE_CONNECTOR:
+		printf("CONNECTOR\n");
+		break;
+	case VARD_FORMFACTOR_TYPE_SMARC2:
+		printf("SMARC-2\n");
+		break;
+	case VARD_FORMFACTOR_TYPE_NONE:
+		/*
+		 * applies to boards with no variants or older boards
+		 * where this field is not written
+		 */
+		printf("UNSPECIFIED\n");
+		break;
+	default:
+		/*
+		 * generic fall trough
+		 * unhandled form factor or invalid data
+		 */
+		printf("UNKNOWN\n");
+		break;
+	}
+}
+
+static void tq_read_string(const char *src, char *dst, int len)
+{
+	int i;
+
+	for (i = 0; i < len && isprint(src[i]) && isascii(src[i]); ++i)
+		dst[i] = src[i];
+	dst[i] = '\0';
+}
+
+struct tq_eeprom *pbl_tq_read_eeprom(struct pbl_i2c *i2c, u8 addr)
+{
+	struct tq_eeprom_data raw;
+	static struct tq_eeprom eeprom;
+	int ret;
+
+	ret = eeprom_read(i2c, addr, 0, &raw, sizeof(raw));
+	if (ret)
+		return NULL;
+
+	if (tq_vard_valid(&eeprom.vard))
+		eeprom.vard = raw.tq_hw_data.vard;
+
+	memcpy(eeprom.mac, raw.mac, TQ_EE_MAC_BYTES);
+	tq_read_string(raw.serial, eeprom.serial, TQ_EE_SERIAL_BYTES);
+	tq_read_string(raw.id, eeprom.id, TQ_EE_BDID_BYTES);
+
+	return &eeprom;
+}
diff --git a/include/tq_eeprom.h b/include/tq_eeprom.h
new file mode 100644
index 0000000000..9a81e6e61d
--- /dev/null
+++ b/include/tq_eeprom.h
@@ -0,0 +1,196 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (c) 2014-2023 TQ-Systems GmbH <u-boot at ew.tq-group.com>,
+ * D-82229 Seefeld, Germany.
+ * Author: Markus Niebel
+ */
+
+#ifndef __TQ_EEPROM_H__
+#define __TQ_EEPROM_H__
+
+#include <linux/sizes.h>
+
+#define VARD_FEATURE_BYTES	8
+
+/*
+ * VARD - variant and revision detection
+ * must have an exact size of 32 bytes to fit in EEPROM just before
+ * the module data
+ */
+struct __packed tq_vard {
+	u16 crc;		/* checksum of vard data - CRC16 XMODEM */
+	u8 hwrev;		/* hardware major revision */
+	u8 memsize;		/* RAM size */
+	u8 memtype;		/* RAM Type + ECC */
+	u8 features[VARD_FEATURE_BYTES];	/* feature bitmask */
+	u8 eepromsize;		/* user eeprom size (feature EEPROM) */
+	u8 eepromtype;		/* user eeprom type (feature EEPROM) */
+	u8 formfactor;		/* SOM Form factor. mask 0xf0 */
+	u8 rsv[0x10];		/* for future use */
+};
+
+#define VARD_MEMTYPE_MASK_TYPE		0x7f /* board specific RAM Type */
+#define VARD_MEMTYPE_MASK_ECC		0x80 /* extra ECC RAM assembled */
+#define VARD_MEMTYPE_DEFAULT		0xff /* use board specific default */
+
+#define VARD_MEMSIZE_MASK_EXP		0x1f /* 2^n MBytes */
+#define VARD_MEMSIZE_MASK_FACTOR	0x20 /* x3 */
+#define VARD_MEMSIZE_DEFAULT		0xff /* use board specific default */
+
+/* feature is present if bit is zero */
+#define VARD_FEATURE_0_RESERVED		0xf0 /* Do not use */
+#define VARD_FEATURE_0_EMMC		0x08 /* e-MMC assembled */
+#define VARD_FEATURE_0_EEPROM		0x04 /* user EEPROM assembled */
+#define VARD_FEATURE_0_SPINOR		0x02 /* [Q,O]SPI-NOR assembled */
+#define VARD_FEATURE_0_SECELEM		0x01 /* secure element assembled */
+
+#define VARD_FEATURE_4_RESERVED		0xf0 /* Do not use */
+#define VARD_FEATURE_4_RTC		0x08 /* RTC assembled */
+
+#define VARD_EESIZE_MASK_EXP		0x1f /* 2^n Bytes */
+#define VARD_EETYPE_DEFAULT		0xff /* use board specific default */
+#define VARD_EETYPE_MASK_MFR		0xf0 /* manufacturer / type mask */
+#define VARD_EETYPE_MASK_PGSIZE		0x0f /* page size */
+
+#define VARD_FORMFACTOR_MASK_TYPE	0xf0 /* SOM type mask */
+#define VARD_FORMFACTOR_TYPE_CONNECTOR	0x00 /* SOM with connector, no board standard */
+#define VARD_FORMFACTOR_TYPE_LGA	0x10 /* LGA SOM, no board standard */
+#define VARD_FORMFACTOR_TYPE_SMARC2	0x20 /* SOM conforms to SMARC-2 standard */
+#define VARD_FORMFACTOR_TYPE_NONE	0xf0 /* unspecified SOM type */
+
+/*
+ * all data should only be handled as valid, if CRC is OKAY
+ */
+static inline
+bool tq_vard_has_ramecc(const struct tq_vard *vard)
+{
+	return (vard->memtype & VARD_MEMTYPE_MASK_ECC);
+}
+
+/*
+ * Calculate size in byte using byte from vard
+ * This works as long as coding for EEPROM / RAM size is the same
+ * val - memsize byte from tq_vard structure
+ * multiply - multiplier, aka 1 / SZ_1K / SZ_1M
+ * tmask - mask for triple factor (use only for RAM sizes)
+ *
+ * return size in bytes or zero in case the val is equal to VARD_MEMSIZE_DEFAULT
+ */
+phys_size_t tq_vard_memsize(u8 val, unsigned int multiply, unsigned int tmask);
+
+static inline
+phys_size_t tq_vard_ramsize(const struct tq_vard *vard)
+{
+	return tq_vard_memsize(vard->memsize, SZ_1M, VARD_MEMSIZE_MASK_FACTOR);
+}
+
+static inline
+size_t tq_vard_eepromsize(const struct tq_vard *vard)
+{
+	return tq_vard_memsize(vard->eepromsize, 1, 0x0);
+}
+
+static inline
+size_t tq_vard_eeprom_pgsize(const struct tq_vard *vard)
+{
+	return 1 << (size_t)(vard->eepromtype & VARD_EETYPE_MASK_PGSIZE);
+}
+
+static inline
+int tq_vard_has_feature(const struct tq_vard *vard, unsigned int fbyte,
+			unsigned int fbit)
+{
+	if (fbyte < VARD_FEATURE_BYTES && fbit < 8)
+		return !(vard->features[fbyte] & BIT(fbit));
+	else
+		return -ERANGE;
+}
+
+static inline
+bool tq_vard_has_emmc(const struct tq_vard *vard)
+{
+	return (tq_vard_has_feature(vard, 0, 3) > 0);
+}
+
+static inline
+bool tq_vard_has_eeprom(const struct tq_vard *vard)
+{
+	return (tq_vard_has_feature(vard, 0, 2) > 0);
+}
+
+static inline
+bool tq_vard_has_spinor(const struct tq_vard *vard)
+{
+	return (tq_vard_has_feature(vard, 0, 1) > 0);
+}
+
+static inline
+bool tq_vard_has_secelem(const struct tq_vard *vard)
+{
+	return (tq_vard_has_feature(vard, 0, 0) > 0);
+}
+
+static inline
+bool tq_vard_has_rtc(const struct tq_vard *vard)
+{
+	return (tq_vard_has_feature(vard, 4, 3) > 0);
+}
+
+static inline u32 tq_vard_get_formfactor(const struct tq_vard *vard)
+{
+	return (u32)(vard->formfactor & VARD_FORMFACTOR_MASK_TYPE);
+};
+
+static inline
+bool tq_vard_is_lga(const struct tq_vard *vard)
+{
+	return (tq_vard_get_formfactor(vard) == VARD_FORMFACTOR_TYPE_LGA);
+}
+
+static inline
+bool tq_vard_is_connector(const struct tq_vard *vard)
+{
+	return (tq_vard_get_formfactor(vard) == VARD_FORMFACTOR_TYPE_CONNECTOR);
+}
+
+static inline
+bool tq_vard_is_smarc2(const struct tq_vard *vard)
+{
+	return (tq_vard_get_formfactor(vard) == VARD_FORMFACTOR_TYPE_SMARC2);
+}
+
+void tq_vard_show(const struct tq_vard *vard);
+
+struct tq_som_feature_list;
+
+/**
+ * fill in presence information from VARD.
+ *
+ * @param[in] vard pointer to VARD structure from SOM EEPROM
+ * @param[in] features SOM specific feature list
+ *
+ * @return 0 on success
+ *
+ * Must be called after data was read to vard. The function checks
+ * if vard is valid, goes through the list and sets the present flag
+ * for each entry depending on the flags in vard.
+ */
+int tq_vard_detect_features(const struct tq_vard *vard,
+			     struct tq_som_feature_list *features);
+
+#define TQ_EE_MAC_BYTES		6
+#define TQ_EE_SERIAL_BYTES	8
+#define TQ_EE_BDID_BYTES	0x40
+
+struct tq_eeprom {
+	struct tq_vard vard;
+	u8 mac[TQ_EE_MAC_BYTES];
+	u8 serial[TQ_EE_SERIAL_BYTES + 1];
+	u8 id[TQ_EE_BDID_BYTES + 1];
+};
+
+struct pbl_i2c;
+
+struct tq_eeprom *pbl_tq_read_eeprom(struct pbl_i2c *i2c, u8 addr);
+
+#endif
-- 
2.39.2




More information about the barebox mailing list