From: Andreas Dannenberg Date: Tue, 4 Jun 2019 23:08:24 +0000 (-0500) Subject: ti: common: am6: Add support for board description EEPROM X-Git-Tag: v2019.10-rc1~25^2~5 X-Git-Url: https://git.librecmc.org/?a=commitdiff_plain;h=361a53308ddfa7401223a523a8c38fc1dcc75481;p=oweals%2Fu-boot.git ti: common: am6: Add support for board description EEPROM The AM654x EVM based on the TI K3 family of SoCs have an updated board detection EEPROM structure, now comprising variable-sized TLV-type records, containing a superset of what is already being provided on earlier platforms such as DRA7. Add basic support for parsing the new data structures contained on the base board into the common TI EEPROM structure while also providing infrastructure that can be used later on to parse data from additional EEPROMs such as the ones that are used on daughtercards for this platform. Signed-off-by: Andreas Dannenberg Reviewed-by: Lokesh Vutla --- diff --git a/board/ti/common/board_detect.c b/board/ti/common/board_detect.c index e258e22f37..fea39f21e8 100644 --- a/board/ti/common/board_detect.c +++ b/board/ti/common/board_detect.c @@ -8,6 +8,7 @@ */ #include +#include #include #include #include @@ -284,6 +285,191 @@ int __maybe_unused ti_i2c_eeprom_dra7_get(int bus_addr, int dev_addr) return 0; } +static int ti_i2c_eeprom_am6_parse_record(struct ti_am6_eeprom_record *record, + struct ti_am6_eeprom *ep, + char **mac_addr, + u8 mac_addr_max_cnt, + u8 *mac_addr_cnt) +{ + switch (record->header.id) { + case TI_AM6_EEPROM_RECORD_BOARD_INFO: + if (record->header.len != sizeof(record->data.board_info)) + return -EINVAL; + + if (!ep) + break; + + /* Populate (and clean, if needed) the board name */ + strlcpy(ep->name, record->data.board_info.name, + sizeof(ep->name)); + ti_eeprom_string_cleanup(ep->name); + + /* Populate selected other fields from the board info record */ + strlcpy(ep->version, record->data.board_info.version, + sizeof(ep->version)); + strlcpy(ep->software_revision, + record->data.board_info.software_revision, + sizeof(ep->software_revision)); + strlcpy(ep->serial, record->data.board_info.serial, + sizeof(ep->serial)); + break; + case TI_AM6_EEPROM_RECORD_MAC_INFO: + if (record->header.len != sizeof(record->data.mac_info)) + return -EINVAL; + + if (!mac_addr || !mac_addr_max_cnt) + break; + + *mac_addr_cnt = ((record->data.mac_info.mac_control & + TI_AM6_EEPROM_MAC_ADDR_COUNT_MASK) >> + TI_AM6_EEPROM_MAC_ADDR_COUNT_SHIFT) + 1; + + /* + * The EEPROM can (but may not) hold a very large amount + * of MAC addresses, by far exceeding what we want/can store + * in the common memory array, so only grab what we can fit. + * Note that a value of 0 means 1 MAC address, and so on. + */ + *mac_addr_cnt = min(*mac_addr_cnt, mac_addr_max_cnt); + + memcpy(mac_addr, record->data.mac_info.mac_addr, + *mac_addr_cnt * TI_EEPROM_HDR_ETH_ALEN); + break; + case 0x00: + /* Illegal value... Fall through... */ + case 0xFF: + /* Illegal value... Something went horribly wrong... */ + return -EINVAL; + default: + pr_warn("%s: Ignoring record id %u\n", __func__, + record->header.id); + } + + return 0; +} + +int __maybe_unused ti_i2c_eeprom_am6_get(int bus_addr, int dev_addr, + struct ti_am6_eeprom *ep, + char **mac_addr, + u8 mac_addr_max_cnt, + u8 *mac_addr_cnt) +{ + struct udevice *dev; + struct udevice *bus; + unsigned int eeprom_addr; + struct ti_am6_eeprom_record_board_id board_id; + struct ti_am6_eeprom_record record; + int rc; + + /* Initialize with a known bad marker for i2c fails.. */ + memset(ep, 0, sizeof(*ep)); + ep->header = TI_DEAD_EEPROM_MAGIC; + + /* Read the board ID record which is always the first EEPROM record */ + rc = ti_i2c_eeprom_get(bus_addr, dev_addr, TI_EEPROM_HEADER_MAGIC, + sizeof(board_id), (uint8_t *)&board_id); + if (rc) + return rc; + + if (board_id.header.id != TI_AM6_EEPROM_RECORD_BOARD_ID) { + pr_err("%s: Invalid board ID record!\n", __func__); + return -EINVAL; + } + + /* Establish DM handle to board config EEPROM */ + rc = uclass_get_device_by_seq(UCLASS_I2C, bus_addr, &bus); + if (rc) + return rc; + rc = i2c_get_chip(bus, dev_addr, 1, &dev); + if (rc) + return rc; + + ep->header = TI_EEPROM_HEADER_MAGIC; + + /* Ready to parse TLV structure. Initialize variables... */ + *mac_addr_cnt = 0; + + /* + * After the all-encompassing board ID record all other records follow + * a TLV-type scheme. Point to the first such record and then start + * parsing those one by one. + */ + eeprom_addr = sizeof(board_id); + + while (true) { + rc = dm_i2c_read(dev, eeprom_addr, (uint8_t *)&record.header, + sizeof(record.header)); + if (rc) + return rc; + + /* + * Check for end of list marker. If we reached it don't go + * any further and stop parsing right here. + */ + if (record.header.id == TI_AM6_EEPROM_RECORD_END_LIST) + break; + + eeprom_addr += sizeof(record.header); + + debug("%s: dev_addr=0x%02x header.id=%u header.len=%u\n", + __func__, dev_addr, record.header.id, + record.header.len); + + /* Read record into memory if it fits */ + if (record.header.len <= sizeof(record.data)) { + rc = dm_i2c_read(dev, eeprom_addr, + (uint8_t *)&record.data, + record.header.len); + if (rc) + return rc; + + /* Process record */ + rc = ti_i2c_eeprom_am6_parse_record(&record, ep, + mac_addr, + mac_addr_max_cnt, + mac_addr_cnt); + if (rc) { + pr_err("%s: EEPROM parsing error!\n", __func__); + return rc; + } + } else { + /* + * We may get here in case of larger records which + * are not yet understood. + */ + pr_err("%s: Ignoring record id %u\n", __func__, + record.header.id); + } + + eeprom_addr += record.header.len; + } + + return 0; +} + +int __maybe_unused ti_i2c_eeprom_am6_get_base(int bus_addr, int dev_addr) +{ + struct ti_am6_eeprom *ep = TI_AM6_EEPROM_DATA; + int ret; + + /* + * Always execute EEPROM read by not allowing to bypass it during the + * first invocation of SPL which happens on the R5 core. + */ +#if !(defined(CONFIG_SPL_BUILD) && defined(CONFIG_CPU_V7R)) + if (ep->header == TI_EEPROM_HEADER_MAGIC) { + debug("%s: EEPROM has already been read\n", __func__); + return 0; + } +#endif + + ret = ti_i2c_eeprom_am6_get(bus_addr, dev_addr, ep, + (char **)ep->mac_addr, + AM6_EEPROM_HDR_NO_OF_MAC_ADDR, + &ep->mac_addr_cnt); + return ret; +} + bool __maybe_unused board_ti_is(char *name_tag) { struct ti_common_eeprom *ep = TI_EEPROM_DATA; @@ -391,6 +577,34 @@ void __maybe_unused set_board_info_env(char *name) env_set("board_serial", unknown); } +void __maybe_unused set_board_info_env_am6(char *name) +{ + char *unknown = "unknown"; + struct ti_am6_eeprom *ep = TI_AM6_EEPROM_DATA; + + if (name) + env_set("board_name", name); + else if (ep->name) + env_set("board_name", ep->name); + else + env_set("board_name", unknown); + + if (ep->version) + env_set("board_rev", ep->version); + else + env_set("board_rev", unknown); + + if (ep->software_revision) + env_set("board_software_revision", ep->software_revision); + else + env_set("board_software_revision", unknown); + + if (ep->serial) + env_set("board_serial", ep->serial); + else + env_set("board_serial", unknown); +} + static u64 mac_to_u64(u8 mac[6]) { int i; diff --git a/board/ti/common/board_detect.h b/board/ti/common/board_detect.h index f8495a7a7c..bf563c84c8 100644 --- a/board/ti/common/board_detect.h +++ b/board/ti/common/board_detect.h @@ -43,6 +43,133 @@ struct ti_am_eeprom { char mac_addr[TI_EEPROM_HDR_NO_OF_MAC_ADDR][TI_EEPROM_HDR_ETH_ALEN]; } __attribute__ ((__packed__)); +/* AM6x TI EVM EEPROM Definitions */ +#define TI_AM6_EEPROM_RECORD_BOARD_ID 0x01 +#define TI_AM6_EEPROM_RECORD_BOARD_INFO 0x10 +#define TI_AM6_EEPROM_RECORD_DDR_INFO 0x11 +#define TI_AM6_EEPROM_RECORD_DDR_SPD 0x12 +#define TI_AM6_EEPROM_RECORD_MAC_INFO 0x13 +#define TI_AM6_EEPROM_RECORD_END_LIST 0xFE + +/* + * Common header for AM6x TI EVM EEPROM records. Used to encapsulate the config + * EEPROM in its entirety as well as for individual records contained within. + */ +struct ti_am6_eeprom_record_header { + u8 id; + u16 len; +} __attribute__ ((__packed__)); + +/* AM6x TI EVM EEPROM board ID structure */ +struct ti_am6_eeprom_record_board_id { + u32 magic_number; + struct ti_am6_eeprom_record_header header; +} __attribute__ ((__packed__)); + +/* AM6x TI EVM EEPROM board info structure */ +#define AM6_EEPROM_HDR_NAME_LEN 16 +#define AM6_EEPROM_HDR_VERSION_LEN 2 +#define AM6_EEPROM_HDR_PROC_NR_LEN 4 +#define AM6_EEPROM_HDR_VARIANT_LEN 2 +#define AM6_EEPROM_HDR_PCB_REV_LEN 2 +#define AM6_EEPROM_HDR_SCH_BOM_REV_LEN 2 +#define AM6_EEPROM_HDR_SW_REV_LEN 2 +#define AM6_EEPROM_HDR_VID_LEN 2 +#define AM6_EEPROM_HDR_BLD_WK_LEN 2 +#define AM6_EEPROM_HDR_BLD_YR_LEN 2 +#define AM6_EEPROM_HDR_4P_NR_LEN 6 +#define AM6_EEPROM_HDR_SERIAL_LEN 4 + +struct ti_am6_eeprom_record_board_info { + char name[AM6_EEPROM_HDR_NAME_LEN]; + char version[AM6_EEPROM_HDR_VERSION_LEN]; + char proc_number[AM6_EEPROM_HDR_PROC_NR_LEN]; + char variant[AM6_EEPROM_HDR_VARIANT_LEN]; + char pcb_revision[AM6_EEPROM_HDR_PCB_REV_LEN]; + char schematic_bom_revision[AM6_EEPROM_HDR_SCH_BOM_REV_LEN]; + char software_revision[AM6_EEPROM_HDR_SW_REV_LEN]; + char vendor_id[AM6_EEPROM_HDR_VID_LEN]; + char build_week[AM6_EEPROM_HDR_BLD_WK_LEN]; + char build_year[AM6_EEPROM_HDR_BLD_YR_LEN]; + char board_4p_number[AM6_EEPROM_HDR_4P_NR_LEN]; + char serial[AM6_EEPROM_HDR_SERIAL_LEN]; +} __attribute__ ((__packed__)); + +/* Memory location to keep a copy of the AM6 board info record */ +#define TI_AM6_EEPROM_BD_INFO_DATA ((struct ti_am6_eeprom_record_board_info *) \ + TI_SRAM_SCRATCH_BOARD_EEPROM_START) + +/* AM6x TI EVM EEPROM DDR info structure */ +#define TI_AM6_EEPROM_DDR_CTRL_INSTANCE_MASK GENMASK(1, 0) +#define TI_AM6_EEPROM_DDR_CTRL_INSTANCE_SHIFT 0 +#define TI_AM6_EEPROM_DDR_CTRL_SPD_DATA_LOC_MASK GENMASK(3, 2) +#define TI_AM6_EEPROM_DDR_CTRL_SPD_DATA_LOC_NA (0 << 2) +#define TI_AM6_EEPROM_DDR_CTRL_SPD_DATA_LOC_BOARDID (2 << 2) +#define TI_AM6_EEPROM_DDR_CTRL_SPD_DATA_LOC_I2C51 (3 << 2) +#define TI_AM6_EEPROM_DDR_CTRL_MEM_TYPE_MASK GENMASK(5, 4) +#define TI_AM6_EEPROM_DDR_CTRL_MEM_TYPE_DDR3 (0 << 4) +#define TI_AM6_EEPROM_DDR_CTRL_MEM_TYPE_DDR4 (1 << 4) +#define TI_AM6_EEPROM_DDR_CTRL_MEM_TYPE_LPDDR4 (2 << 4) +#define TI_AM6_EEPROM_DDR_CTRL_IF_DATA_WIDTH_MASK GENMASK(7, 6) +#define TI_AM6_EEPROM_DDR_CTRL_IF_DATA_WIDTH_16 (0 << 6) +#define TI_AM6_EEPROM_DDR_CTRL_IF_DATA_WIDTH_32 (1 << 6) +#define TI_AM6_EEPROM_DDR_CTRL_IF_DATA_WIDTH_64 (2 << 6) +#define TI_AM6_EEPROM_DDR_CTRL_DEV_DATA_WIDTH_MASK GENMASK(9, 8) +#define TI_AM6_EEPROM_DDR_CTRL_DEV_DATA_WIDTH_8 (0 << 8) +#define TI_AM6_EEPROM_DDR_CTRL_DEV_DATA_WIDTH_16 (1 << 8) +#define TI_AM6_EEPROM_DDR_CTRL_DEV_DATA_WIDTH_32 (2 << 8) +#define TI_AM6_EEPROM_DDR_CTRL_RANKS_2 BIT(10) +#define TI_AM6_EEPROM_DDR_CTRL_DENS_MASK GENMASK(13, 11) +#define TI_AM6_EEPROM_DDR_CTRL_DENS_1GB (0 << 11) +#define TI_AM6_EEPROM_DDR_CTRL_DENS_2GB (1 << 11) +#define TI_AM6_EEPROM_DDR_CTRL_DENS_4GB (2 << 11) +#define TI_AM6_EEPROM_DDR_CTRL_DENS_8GB (3 << 11) +#define TI_AM6_EEPROM_DDR_CTRL_DENS_12GB (4 << 11) +#define TI_AM6_EEPROM_DDR_CTRL_DENS_16GB (5 << 11) +#define TI_AM6_EEPROM_DDR_CTRL_DENS_24GB (6 << 11) +#define TI_AM6_EEPROM_DDR_CTRL_DENS_32GB (7 << 11) +#define TI_AM6_EEPROM_DDR_CTRL_ECC BIT(14) + +struct ti_am6_eeprom_record_ddr_info { + u16 ddr_control; +} __attribute__ ((__packed__)); + +/* AM6x TI EVM EEPROM DDR SPD structure */ +#define TI_AM6_EEPROM_DDR_SPD_INSTANCE_MASK GENMASK(1, 0) +#define TI_AM6_EEPROM_DDR_SPD_INSTANCE_SHIFT 0 +#define TI_AM6_EEPROM_DDR_SPD_MEM_TYPE_MASK GENMASK(4, 3) +#define TI_AM6_EEPROM_DDR_SPD_MEM_TYPE_DDR3 (0 << 3) +#define TI_AM6_EEPROM_DDR_SPD_MEM_TYPE_DDR4 (1 << 3) +#define TI_AM6_EEPROM_DDR_SPD_MEM_TYPE_LPDDR4 (2 << 3) +#define TI_AM6_EEPROM_DDR_SPD_DATA_LEN 512 + +struct ti_am6_eeprom_record_ddr_spd { + u16 spd_control; + u8 data[TI_AM6_EEPROM_DDR_SPD_DATA_LEN]; +} __attribute__ ((__packed__)); + +/* AM6x TI EVM EEPROM MAC info structure */ +#define TI_AM6_EEPROM_MAC_INFO_INSTANCE_MASK GENMASK(2, 0) +#define TI_AM6_EEPROM_MAC_INFO_INSTANCE_SHIFT 0 +#define TI_AM6_EEPROM_MAC_ADDR_COUNT_MASK GENMASK(7, 3) +#define TI_AM6_EEPROM_MAC_ADDR_COUNT_SHIFT 3 +#define TI_AM6_EEPROM_MAC_ADDR_MAX_COUNT 32 + +struct ti_am6_eeprom_record_mac_info { + u16 mac_control; + u8 mac_addr[TI_AM6_EEPROM_MAC_ADDR_MAX_COUNT][TI_EEPROM_HDR_ETH_ALEN]; +} __attribute__ ((__packed__)); + +struct ti_am6_eeprom_record { + struct ti_am6_eeprom_record_header header; + union { + struct ti_am6_eeprom_record_board_info board_info; + struct ti_am6_eeprom_record_ddr_info ddr_info; + struct ti_am6_eeprom_record_ddr_spd ddr_spd; + struct ti_am6_eeprom_record_mac_info mac_info; + } data; +} __attribute__ ((__packed__)); + /* DRA7 EEPROM MAGIC Header identifier */ #define DRA7_EEPROM_HEADER_MAGIC 0xAA5533EE #define DRA7_EEPROM_HDR_NAME_LEN 16 @@ -99,6 +226,37 @@ struct ti_common_eeprom { #define TI_EEPROM_DATA ((struct ti_common_eeprom *)\ TI_SRAM_SCRATCH_BOARD_EEPROM_START) +/* + * Maximum number of Ethernet MAC addresses extracted from the AM6x on-board + * EEPROM during the initial probe and carried forward in SRAM. + */ +#define AM6_EEPROM_HDR_NO_OF_MAC_ADDR 8 + +/** + * struct ti_am6_eeprom - Null terminated, usable EEPROM contents, as extracted + * from the AM6 on-board EEPROM. Note that we only carry a subset of data + * at this time to be considerate about memory consumption. + * @header: Magic number for data validity indication + * @name: NULL terminated name + * @version: NULL terminated version + * @software_revision: NULL terminated software revision + * @serial: Board serial number + * @mac_addr_cnt: Number of MAC addresses stored in this object + * @mac_addr: MAC addresses + */ +struct ti_am6_eeprom { + u32 header; + char name[AM6_EEPROM_HDR_NAME_LEN + 1]; + char version[AM6_EEPROM_HDR_VERSION_LEN + 1]; + char software_revision[AM6_EEPROM_HDR_SW_REV_LEN + 1]; + char serial[AM6_EEPROM_HDR_SERIAL_LEN + 1]; + u8 mac_addr_cnt; + char mac_addr[AM6_EEPROM_HDR_NO_OF_MAC_ADDR][TI_EEPROM_HDR_ETH_ALEN]; +}; + +#define TI_AM6_EEPROM_DATA ((struct ti_am6_eeprom *) \ + TI_SRAM_SCRATCH_BOARD_EEPROM_START) + /** * ti_i2c_eeprom_am_get() - Consolidated eeprom data collection for AM* TI EVMs * @bus_addr: I2C bus address @@ -116,6 +274,33 @@ int ti_i2c_eeprom_am_get(int bus_addr, int dev_addr); */ int ti_i2c_eeprom_dra7_get(int bus_addr, int dev_addr); +/** + * ti_i2c_eeprom_am6_get() - Consolidated eeprom data for AM6x TI EVMs and + * associated daughter cards, parsed into user- + * provided data structures + * @bus_addr: I2C bus address + * @dev_addr: I2C slave address + * @ep: Pointer to structure receiving AM6-specific header data + * @mac_addr: Pointer to memory receiving parsed MAC addresses. May be + * NULL to skip MAC parsing. + * @mac_addr_max_cnt: Maximum number of MAC addresses that can be stored into + * mac_addr. May be NULL to skip MAC parsing. + * @mac_addr_cnt: Pointer to a location returning how many MAC addressed got + * actually parsed. + */ +int __maybe_unused ti_i2c_eeprom_am6_get(int bus_addr, int dev_addr, + struct ti_am6_eeprom *ep, + char **mac_addr, + u8 mac_addr_max_cnt, + u8 *mac_addr_cnt); + +/** + * ti_i2c_eeprom_am6_get_base() - Consolidated eeprom data for AM6x TI EVMs + * @bus_addr: I2C bus address + * @dev_addr: I2C slave address + */ +int __maybe_unused ti_i2c_eeprom_am6_get_base(int bus_addr, int dev_addr); + /** * board_ti_is() - Board detection logic for TI EVMs * @name_tag: Tag used in eeprom for the board @@ -192,6 +377,15 @@ u64 board_ti_get_emif2_size(void); */ void set_board_info_env(char *name); +/** + * set_board_info_env_am6() - Setup commonly used board information environment + * vars for AM6-type boards + * @name: Name of the board + * + * If name is NULL, default_name is used. + */ +void set_board_info_env_am6(char *name); + /** * board_ti_set_ethaddr- Sets the ethaddr environment from EEPROM * @index: The first ethaddr environment variable to set