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@xxxxxxxxxxxxxx> --- 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 +++++++++++++++++++++++++++++++++++ 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@xxxxxxxxxxxxxxx>, + * 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@xxxxxxxxxxxxxxx>, + * 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