From 489b965564ad73c7a2e8f8743908347775c5df3b Mon Sep 17 00:00:00 2001 From: Christoph Niedermaier Date: Sat, 7 Dec 2024 00:04:18 +0100 Subject: arm64: imx8mp: Read values from M24C32-D write-lockable page on DHCOM i.MX8MP The new i.MX8M Plus DHCOM rev.200 is populated with M24C32-D EEPROM that contains an additional write-lockable page called ID page, which is populated with a structure containing ethernet MAC addresses, DH item number and DH serial number. Because the write-lockable page is not present on rev.100 i.MX8MP DHCOM SoM, test whether EEPROM ID page exists by setting up the i2c driver. There may be multiple EEPROMs with an ID page on this platform, always use the first one. The evaluation of the EEPROM ID page is done in two steps. First, the content is read and checked. This is done to cache the content of the EEPROM ID page. Second, the content is extracted from the EEPROM buffer by requesting it. For the ethernet MAC address the i.MX8M Plus DHCOM currently supports parsing address from multiple sources in the following priority order: 1) U-Boot environment 'ethaddr'/'eth1addr' environment variable 2) SoC OTP fuses 3) On-SoM EEPROM Add support for parsing the content of this new EEPROM ID page and place it between 2) and 3) on the priority list. The new entry is 2.5) On-SoM EEPROM write-lockable page. Signed-off-by: Christoph Niedermaier Reviewed-by: Marek Vasut --- board/dhelectronics/common/dh_common.c | 149 ++++++++++++++++++++++++++++++++- 1 file changed, 148 insertions(+), 1 deletion(-) (limited to 'board/dhelectronics/common/dh_common.c') diff --git a/board/dhelectronics/common/dh_common.c b/board/dhelectronics/common/dh_common.c index 32c50b4f0f5..6a942909ccf 100644 --- a/board/dhelectronics/common/dh_common.c +++ b/board/dhelectronics/common/dh_common.c @@ -7,6 +7,7 @@ #include #include #include +#include #include "dh_common.h" @@ -30,6 +31,149 @@ int dh_get_mac_is_enabled(const char *alias) return 0; } +int dh_read_eeprom_id_page(u8 *eeprom_buffer, const char *alias) +{ + struct eeprom_id_page *eip = (struct eeprom_id_page *)eeprom_buffer; + struct udevice *dev; + size_t payload_len; + int eeprom_size; + u16 crc16_calc; + u16 crc16_eip; + u8 crc8_calc; + ofnode node; + int ret; + + node = ofnode_path(alias); + + ret = uclass_get_device_by_ofnode(UCLASS_I2C_EEPROM, node, &dev); + if (ret) + return ret; + + eeprom_size = i2c_eeprom_size(dev); + if (eeprom_size < 0) { + printf("%s: Error getting EEPROM ID page size! ret = %d\n", __func__, ret); + return eeprom_size; + } + + if (eeprom_size == 0 || eeprom_size > DH_EEPROM_ID_PAGE_MAX_SIZE) { + eeprom_size = DH_EEPROM_ID_PAGE_MAX_SIZE; + printf("Get invalid EEPROM ID page size %d bytes! Try to read %d bytes.\n", + eeprom_size, DH_EEPROM_ID_PAGE_MAX_SIZE); + } + + ret = i2c_eeprom_read(dev, 0x0, eeprom_buffer, eeprom_size); + if (ret) { + printf("%s: Error reading EEPROM ID page! ret = %d\n", __func__, ret); + return ret; + } + + /* Validate header ID */ + if (eip->hdr.id[0] != 'D' || eip->hdr.id[1] != 'H' || eip->hdr.id[2] != 'E') { + printf("%s: Error validating header ID! (got %c%c%c (0x%02x 0x%02x 0x%02x) != expected DHE)\n", + __func__, isprint(eip->hdr.id[0]) ? eip->hdr.id[0] : '.', + isprint(eip->hdr.id[1]) ? eip->hdr.id[1] : '.', + isprint(eip->hdr.id[2]) ? eip->hdr.id[2] : '.', + eip->hdr.id[0], eip->hdr.id[1], eip->hdr.id[2]); + return -EINVAL; + } + + /* Validate header checksum */ + crc8_calc = crc8(0xff, eeprom_buffer, offsetof(struct eeprom_id_page, hdr.crc8_hdr)); + if (eip->hdr.crc8_hdr != crc8_calc) { + printf("%s: Error validating header checksum! (got 0x%02x != calc 0x%02x)\n", + __func__, eip->hdr.crc8_hdr, crc8_calc); + return -EINVAL; + } + + /* + * Validate header version + * The payload is defined by the version specified in the header. + * Currently only version 0x10 is defined, so take the length of + * the only defined payload as the payload length. + */ + if (eip->hdr.version != DH_EEPROM_ID_PAGE_V1_0) { + printf("%s: Error validating version! (0x%02X is not supported)\n", + __func__, eip->hdr.version); + return -EINVAL; + } + payload_len = sizeof(eip->pl); + + /* Validate payload checksum */ + crc16_eip = (eip->hdr.crc16_pl[1] << 8) | eip->hdr.crc16_pl[0]; + crc16_calc = crc16(0xffff, eeprom_buffer + sizeof(eip->hdr), payload_len); + if (crc16_eip != crc16_calc) { + printf("%s: Error validating data checksum! (got 0x%02x != calc 0x%02x)\n", + __func__, crc16_eip, crc16_calc); + return -EINVAL; + } + + return 0; +} + +int dh_get_value_from_eeprom_buffer(enum eip_request_values request, u8 *data, int data_len, + struct eeprom_id_page *eip) +{ + const char fin_chr = (eip->pl.item_prefix & DH_ITEM_PREFIX_FIN_BIT) ? + DH_ITEM_PREFIX_FIN_FLASHED_CHR : DH_ITEM_PREFIX_FIN_HALF_CHR; + const u8 soc_coded = eip->pl.item_prefix & 0xf; + char soc_chr; + + if (!eip) + return -EINVAL; + + /* Copy requested data */ + switch (request) { + case DH_MAC0: + if (!is_valid_ethaddr(eip->pl.mac0)) + return -EINVAL; + + if (data_len >= sizeof(eip->pl.mac0)) + memcpy(data, eip->pl.mac0, sizeof(eip->pl.mac0)); + else + return -EINVAL; + break; + case DH_MAC1: + if (!is_valid_ethaddr(eip->pl.mac1)) + return -EINVAL; + + if (data_len >= sizeof(eip->pl.mac1)) + memcpy(data, eip->pl.mac1, sizeof(eip->pl.mac1)); + else + return -EINVAL; + break; + case DH_ITEM_NUMBER: + if (data_len < 8) /* String length must be 7 characters + string termination */ + return -EINVAL; + + if (soc_coded == DH_ITEM_PREFIX_NXP) + soc_chr = DH_ITEM_PREFIX_NXP_CHR; + else if (soc_coded == DH_ITEM_PREFIX_ST) + soc_chr = DH_ITEM_PREFIX_ST_CHR; + else + return -EINVAL; + + snprintf(data, data_len, "%c%c%05d", fin_chr, soc_chr, + (eip->pl.item_num[0] << 16) | (eip->pl.item_num[1] << 8) | + eip->pl.item_num[2]); + break; + case DH_SERIAL_NUMBER: + /* + * data_len must be greater than the size of eip->pl.serial, + * because there is a string termination needed. + */ + if (data_len <= sizeof(eip->pl.serial)) + return -EINVAL; + + data[sizeof(eip->pl.serial)] = 0; + memcpy(data, eip->pl.serial, sizeof(eip->pl.serial)); + break; + default: + return -EINVAL; + } + + return 0; +} + int dh_get_mac_from_eeprom(unsigned char *enetaddr, const char *alias) { struct udevice *dev; @@ -62,7 +206,7 @@ int dh_get_mac_from_eeprom(unsigned char *enetaddr, const char *alias) return 0; } -__weak int dh_setup_mac_address(void) +__weak int dh_setup_mac_address(struct eeprom_id_page *eip) { unsigned char enetaddr[6]; @@ -72,6 +216,9 @@ __weak int dh_setup_mac_address(void) if (dh_get_mac_is_enabled("ethernet0")) return 0; + if (!dh_get_value_from_eeprom_buffer(DH_MAC0, enetaddr, sizeof(enetaddr), eip)) + return eth_env_set_enetaddr("ethaddr", enetaddr); + if (!dh_get_mac_from_eeprom(enetaddr, "eeprom0")) return eth_env_set_enetaddr("ethaddr", enetaddr); -- cgit v1.2.3 From 1c9fe756edba755679e8e4dc525d12e7deda3808 Mon Sep 17 00:00:00 2001 From: Christoph Niedermaier Date: Sat, 7 Dec 2024 00:04:20 +0100 Subject: board: dhelectronics: Sync env variable dh_som_serial_number with SN The env variable "SN" is used to store the serial number on DH electronics SoMs. New SoMs will use the variable "dh_som_serial_number". To ensure compatibility, these env variables are synchronized. This is achieved using callback functions. Signed-off-by: Christoph Niedermaier Reviewed-by: Marek Vasut --- board/dhelectronics/common/dh_common.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) (limited to 'board/dhelectronics/common/dh_common.c') diff --git a/board/dhelectronics/common/dh_common.c b/board/dhelectronics/common/dh_common.c index 6a942909ccf..71010803f55 100644 --- a/board/dhelectronics/common/dh_common.c +++ b/board/dhelectronics/common/dh_common.c @@ -11,6 +11,23 @@ #include "dh_common.h" +static int on_dh_som_serial_number(const char *name, const char *value, enum env_op op, + int flags) +{ + env_set("SN", value); + return 0; +} + +U_BOOT_ENV_CALLBACK(dh_som_serial_number, on_dh_som_serial_number); + +static int on_SN(const char *name, const char *value, enum env_op op, int flags) +{ + env_set("dh_som_serial_number", value); + return 0; +} + +U_BOOT_ENV_CALLBACK(SN, on_SN); + bool dh_mac_is_in_env(const char *env) { unsigned char enetaddr[6]; -- cgit v1.2.3