ti: common: am6: Add support for board description EEPROM
authorAndreas Dannenberg <dannenberg@ti.com>
Tue, 4 Jun 2019 23:08:24 +0000 (18:08 -0500)
committerTom Rini <trini@konsulko.com>
Wed, 17 Jul 2019 15:13:18 +0000 (11:13 -0400)
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 <dannenberg@ti.com>
Reviewed-by: Lokesh Vutla <lokeshvutla@ti.com>
board/ti/common/board_detect.c
board/ti/common/board_detect.h

index e258e22f3714c6c8052d8ddf80956cc7a89f779e..fea39f21e8ce0d114aeb9e2250f437298be98d71 100644 (file)
@@ -8,6 +8,7 @@
  */
 
 #include <common.h>
+#include <asm/arch/hardware.h>
 #include <asm/omap_common.h>
 #include <dm/uclass.h>
 #include <i2c.h>
@@ -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;
index f8495a7a7cf128f55b765d4e7cd370070f1bd28e..bf563c84c8f6b14921e0bace8f1a11aea031d36a 100644 (file)
@@ -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 eth<index>addr environment variable to set