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

Sascha Hauer s.hauer at pengutronix.de
Mon Feb 5 00:18:19 PST 2024


On Mon, Feb 05, 2024 at 09:11:38AM +0100, Marco Felsch wrote:
> Hi Sascha,
> 
> On 24-02-05, Sascha Hauer wrote:
> > 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/boards/Kconfig        |   3 +
> >  common/boards/Makefile       |   1 +
> >  common/boards/tq/Makefile    |   1 +
> >  common/boards/tq/tq_eeprom.c | 140 +++++++++++++++++++++++++
> >  include/tq_eeprom.h          | 196 +++++++++++++++++++++++++++++++++++
> 
> should we bundle such includes under 'include/boards/' or
> 'include/boards/tq'?

There already is a include/boards/phytec/ containing includes
for code in common/boards/phytec. Following this pattern for new code is
a good idea. Will change that.

Sascha

> 
> Regards,
>   Marco
> 
> >  5 files changed, 341 insertions(+)
> >  create mode 100644 common/boards/tq/Makefile
> >  create mode 100644 common/boards/tq/tq_eeprom.c
> >  create mode 100644 include/tq_eeprom.h
> > 
> > diff --git a/common/boards/Kconfig b/common/boards/Kconfig
> > index a1d87c0215..fe3a60d508 100644
> > --- a/common/boards/Kconfig
> > +++ b/common/boards/Kconfig
> > @@ -10,3 +10,6 @@ config BOARD_PHYTEC_SOM_DETECTION
> >  config BOARD_PHYTEC_SOM_IMX8M_DETECTION
> >  	bool
> >  	select BOARD_PHYTEC_SOM_DETECTION
> > +
> > +config BOARD_TQ
> > +	bool
> > diff --git a/common/boards/Makefile b/common/boards/Makefile
> > index c1e1b78df3..147c36643d 100644
> > --- a/common/boards/Makefile
> > +++ b/common/boards/Makefile
> > @@ -2,3 +2,4 @@
> >  
> >  obj-$(CONFIG_BOARD_QEMU_VIRT)	+= qemu-virt/
> >  obj-$(CONFIG_BOARD_PHYTEC_SOM_DETECTION) += phytec/
> > +obj-$(CONFIG_BOARD_TQ) += tq/
> > diff --git a/common/boards/tq/Makefile b/common/boards/tq/Makefile
> > new file mode 100644
> > index 0000000000..9950cbdb00
> > --- /dev/null
> > +++ b/common/boards/tq/Makefile
> > @@ -0,0 +1 @@
> > +obj-pbl-y += tq_eeprom.o
> > diff --git a/common/boards/tq/tq_eeprom.c b/common/boards/tq/tq_eeprom.c
> > new file mode 100644
> > index 0000000000..83a24bbb04
> > --- /dev/null
> > +++ b/common/boards/tq/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
> > 
> > 
> > 
> 

-- 
Pengutronix e.K.                           |                             |
Steuerwalder Str. 21                       | http://www.pengutronix.de/  |
31137 Hildesheim, Germany                  | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |



More information about the barebox mailing list