From aea1bd95b61e5596000e2aa71b32b32801ac51d4 Mon Sep 17 00:00:00 2001 From: Yanhong Wang Date: Thu, 15 Jun 2023 17:36:48 +0800 Subject: [PATCH] eeprom: starfive: Enable ID EEPROM configuration Enabled ID_EEPROM configuration for StarFive VisionFive2 board. Signed-off-by: Yanhong Wang Reviewed-by: Leo Yu-Chi Liang --- arch/riscv/include/asm/arch-jh7110/eeprom.h | 13 + board/starfive/visionfive2/Makefile | 1 + .../visionfive2/visionfive2-i2c-eeprom.c | 561 ++++++++++++++++++ 3 files changed, 575 insertions(+) create mode 100644 arch/riscv/include/asm/arch-jh7110/eeprom.h create mode 100644 board/starfive/visionfive2/visionfive2-i2c-eeprom.c diff --git a/arch/riscv/include/asm/arch-jh7110/eeprom.h b/arch/riscv/include/asm/arch-jh7110/eeprom.h new file mode 100644 index 0000000000..f354d5c60c --- /dev/null +++ b/arch/riscv/include/asm/arch-jh7110/eeprom.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2023 StarFive Technology Co., Ltd. + * Author: Yanhong Wang + */ + +#ifndef _ASM_RISCV_EEPROM_H +#define _ASM_RISCV_EEPROM_H + +u8 get_pcb_revision_from_eeprom(void); +u32 get_ddr_size_from_eeprom(void); + +#endif /* _ASM_RISCV_EEPROM_H */ diff --git a/board/starfive/visionfive2/Makefile b/board/starfive/visionfive2/Makefile index 66c854df39..c7ba4f7ed6 100644 --- a/board/starfive/visionfive2/Makefile +++ b/board/starfive/visionfive2/Makefile @@ -5,3 +5,4 @@ obj-y := starfive_visionfive2.o obj-$(CONFIG_SPL_BUILD) += spl.o +obj-$(CONFIG_ID_EEPROM) += visionfive2-i2c-eeprom.o diff --git a/board/starfive/visionfive2/visionfive2-i2c-eeprom.c b/board/starfive/visionfive2/visionfive2-i2c-eeprom.c new file mode 100644 index 0000000000..befe7888c4 --- /dev/null +++ b/board/starfive/visionfive2/visionfive2-i2c-eeprom.c @@ -0,0 +1,561 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2023 StarFive Technology Co., Ltd. + * Author: Yanhong Wang + */ + +#include +#include +#include +#include +#include +#include +#include + +#define FORMAT_VERSION 0x2 +#define PCB_VERSION 0xB1 +#define BOM_VERSION 'A' +/* + * BYTES_PER_EEPROM_PAGE: the 24FC04H datasheet says that data can + * only be written in page mode, which means 16 bytes at a time: + * 16-Byte Page Write Buffer + */ +#define BYTES_PER_EEPROM_PAGE 16 + +/* + * EEPROM_WRITE_DELAY_MS: the 24FC04H datasheet says it takes up to + * 5ms to complete a given write: + * Write Cycle Time (byte or page) ro Page Write Time 5 ms, Maximum + */ +#define EEPROM_WRITE_DELAY_MS 5000 +/* + * StarFive OUI. Registration Date is 20xx-xx-xx + */ +#define STARFIVE_OUI_PREFIX "6C:CF:39:" +#define STARFIVE_DEFAULT_MAC0 "6C:CF:39:6C:DE:AD" +#define STARFIVE_DEFAULT_MAC1 "6C:CF:39:6C:DE:AE" + +/* Magic number at the first four bytes of EEPROM HATs */ +#define STARFIVE_EEPROM_HATS_SIG "SFVF" /* StarFive VisionFive */ + +#define STARFIVE_EEPROM_HATS_SIZE_MAX 256 /* Header + Atom1&4(v1) */ +#define STARFIVE_EEPROM_WP_OFFSET 0 /* Read only field */ +#define STARFIVE_EEPROM_ATOM1_PSTR "VF7110A1-2228-D008E000-00000001\0" +#define STARFIVE_EEPROM_ATOM1_PSTR_SIZE 32 +#define STARFIVE_EEPROM_ATOM1_SN_OFFSET 23 +#define STARFIVE_EEPROM_ATOM1_VSTR "StarFive Technology Co., Ltd.\0\0\0" +#define STARFIVE_EEPROM_ATOM1_VSTR_SIZE 32 + +#define MAGIC_NUMBER_BYTES 4 +#define MAC_ADDR_BYTES 6 +#define MAC_ADDR_STRLEN 17 + +/* + * Atom Types + * 0x0000 = invalid + * 0x0001 = vendor info + * 0x0002 = GPIO map + * 0x0003 = Linux device tree blob + * 0x0004 = manufacturer custom data + * 0x0005-0xfffe = reserved for future use + * 0xffff = invalid + */ + +#define HATS_ATOM_INVALID 0x0000 +#define HATS_ATOM_VENDOR 0x0001 +#define HATS_ATOM_GPIO 0x0002 +#define HATS_ATOM_DTB 0x0003 +#define HATS_ATOM_CUSTOM 0x0004 +#define HATS_ATOM_INVALID_END 0xffff + +struct eeprom_header { + char signature[MAGIC_NUMBER_BYTES]; /* ASCII table signature */ + u8 version; /* EEPROM data format version */ + /* (0x00 reserved, 0x01 = first version) */ + u8 reversed; /* 0x00, Reserved field */ + u16 numatoms; /* total atoms in EEPROM */ + u32 eeplen; /* total length in bytes of all eeprom data */ + /* (including this header) */ +}; + +struct eeprom_atom_header { + u16 type; + u16 count; + u32 dlen; +}; + +struct eeprom_atom1_data { + u8 uuid[16]; + u16 pid; + u16 pver; + u8 vslen; + u8 pslen; + uchar vstr[STARFIVE_EEPROM_ATOM1_VSTR_SIZE]; + uchar pstr[STARFIVE_EEPROM_ATOM1_PSTR_SIZE]; /* product SN */ +}; + +struct starfive_eeprom_atom1 { + struct eeprom_atom_header header; + struct eeprom_atom1_data data; + u16 crc; +}; + +struct eeprom_atom4_data { + u16 version; + u8 pcb_revision; /* PCB version */ + u8 bom_revision; /* BOM version */ + u8 mac0_addr[MAC_ADDR_BYTES]; /* Ethernet0 MAC */ + u8 mac1_addr[MAC_ADDR_BYTES]; /* Ethernet1 MAC */ + u8 reserved[2]; +}; + +struct starfive_eeprom_atom4 { + struct eeprom_atom_header header; + struct eeprom_atom4_data data; + u16 crc; +}; + +struct starfive_eeprom { + struct eeprom_header header; + struct starfive_eeprom_atom1 atom1; + struct starfive_eeprom_atom4 atom4; +}; + +static union { + struct starfive_eeprom eeprom; + uchar buf[STARFIVE_EEPROM_HATS_SIZE_MAX]; +} pbuf __section(".data"); + +/* Set to 1 if we've read EEPROM into memory */ +static int has_been_read __section(".data"); + +static inline int is_match_magic(void) +{ + return strncmp(pbuf.eeprom.header.signature, STARFIVE_EEPROM_HATS_SIG, + MAGIC_NUMBER_BYTES); +} + +/* Calculate the current CRC */ +static inline u32 calculate_crc16(struct eeprom_atom_header *head) +{ + uint len = sizeof(struct eeprom_atom_header) + head->dlen - sizeof(u16); + + return crc16(0, (void *)head, len); +} + +/* This function should be called after each update to the EEPROM structure */ +static inline void update_crc(void) +{ + pbuf.eeprom.atom1.crc = calculate_crc16(&pbuf.eeprom.atom1.header); + pbuf.eeprom.atom4.crc = calculate_crc16(&pbuf.eeprom.atom4.header); +} + +static void dump_raw_eeprom(void) +{ + unsigned int i; + u32 len; + + len = sizeof(struct starfive_eeprom); + for (i = 0; i < len; i++) { + if ((i % 16) == 0) + printf("%02X: ", i); + printf("%02X ", ((u8 *)pbuf.buf)[i]); + if (((i % 16) == 15) || (i == len - 1)) + printf("\n"); + } +} + +/** + * show_eeprom - display the contents of the EEPROM + */ +static void show_eeprom(void) +{ + if (has_been_read != 1) + return; + + printf("\n--------EEPROM INFO--------\n"); + printf("Vendor : %s\n", pbuf.eeprom.atom1.data.vstr); + printf("Product full SN: %s\n", pbuf.eeprom.atom1.data.pstr); + printf("data version: 0x%x\n", pbuf.eeprom.atom4.data.version); + if (pbuf.eeprom.atom4.data.version == 2) { + printf("PCB revision: 0x%x\n", pbuf.eeprom.atom4.data.pcb_revision); + printf("BOM revision: %c\n", pbuf.eeprom.atom4.data.bom_revision); + printf("Ethernet MAC0 address: %02x:%02x:%02x:%02x:%02x:%02x\n", + pbuf.eeprom.atom4.data.mac0_addr[0], pbuf.eeprom.atom4.data.mac0_addr[1], + pbuf.eeprom.atom4.data.mac0_addr[2], pbuf.eeprom.atom4.data.mac0_addr[3], + pbuf.eeprom.atom4.data.mac0_addr[4], pbuf.eeprom.atom4.data.mac0_addr[5]); + printf("Ethernet MAC1 address: %02x:%02x:%02x:%02x:%02x:%02x\n", + pbuf.eeprom.atom4.data.mac1_addr[0], pbuf.eeprom.atom4.data.mac1_addr[1], + pbuf.eeprom.atom4.data.mac1_addr[2], pbuf.eeprom.atom4.data.mac1_addr[3], + pbuf.eeprom.atom4.data.mac1_addr[4], pbuf.eeprom.atom4.data.mac1_addr[5]); + } else { + printf("Custom data v%d is not Supported\n", pbuf.eeprom.atom4.data.version); + } + printf("--------EEPROM INFO--------\n\n"); +} + +/** + * set_mac_address() - stores a MAC address into the local EEPROM copy + * + * This function takes a pointer to MAC address string + * (i.e."XX:XX:XX:XX:XX:XX", where "XX" is a two-digit hex number), + * stores it in the MAC address field of the EEPROM local copy, and + * updates the local copy of the CRC. + */ +static void set_mac_address(char *string, int index) +{ + u8 i; + u8 *mac; + + if (strncasecmp(STARFIVE_OUI_PREFIX, string, + strlen(STARFIVE_OUI_PREFIX))) { + printf("The MAC address doesn't match StarFive OUI %s\n", + STARFIVE_OUI_PREFIX); + return; + } + mac = (index == 0) ? pbuf.eeprom.atom4.data.mac0_addr : + pbuf.eeprom.atom4.data.mac1_addr; + + for (i = 0; *string && (i < MAC_ADDR_BYTES); i++) { + mac[i] = hextoul(string, &string); + + if (*string == ':') + string++; + } + + update_crc(); +} + +/** + * init_local_copy() - initialize the in-memory EEPROM copy + * + * Initialize the in-memory EEPROM copy with the magic number. Must + * be done when preparing to initialize a blank EEPROM, or overwrite + * one with a corrupted magic number. + */ +static void init_local_copy(void) +{ + memset((void *)pbuf.buf, 0, sizeof(struct starfive_eeprom)); + memcpy(pbuf.eeprom.header.signature, STARFIVE_EEPROM_HATS_SIG, + strlen(STARFIVE_EEPROM_HATS_SIG)); + pbuf.eeprom.header.version = FORMAT_VERSION; + pbuf.eeprom.header.numatoms = 2; + pbuf.eeprom.header.eeplen = sizeof(struct starfive_eeprom); + + pbuf.eeprom.atom1.header.type = HATS_ATOM_VENDOR; + pbuf.eeprom.atom1.header.count = 1; + pbuf.eeprom.atom1.header.dlen = sizeof(struct eeprom_atom1_data) + sizeof(u16); + pbuf.eeprom.atom1.data.vslen = STARFIVE_EEPROM_ATOM1_VSTR_SIZE; + pbuf.eeprom.atom1.data.pslen = STARFIVE_EEPROM_ATOM1_PSTR_SIZE; + memcpy(pbuf.eeprom.atom1.data.vstr, STARFIVE_EEPROM_ATOM1_VSTR, + strlen(STARFIVE_EEPROM_ATOM1_VSTR)); + memcpy(pbuf.eeprom.atom1.data.pstr, STARFIVE_EEPROM_ATOM1_PSTR, + strlen(STARFIVE_EEPROM_ATOM1_PSTR)); + + pbuf.eeprom.atom4.header.type = HATS_ATOM_CUSTOM; + pbuf.eeprom.atom4.header.count = 2; + pbuf.eeprom.atom4.header.dlen = sizeof(struct eeprom_atom4_data) + sizeof(u16); + pbuf.eeprom.atom4.data.version = FORMAT_VERSION; + pbuf.eeprom.atom4.data.pcb_revision = PCB_VERSION; + pbuf.eeprom.atom4.data.bom_revision = BOM_VERSION; + set_mac_address(STARFIVE_DEFAULT_MAC0, 0); + set_mac_address(STARFIVE_DEFAULT_MAC1, 1); +} + +/** + * prog_eeprom() - write the EEPROM from memory + */ +static int prog_eeprom(unsigned int size) +{ + unsigned int i; + void *p; + uchar tmp_buff[STARFIVE_EEPROM_HATS_SIZE_MAX]; + struct udevice *dev; + int ret; + + if (is_match_magic()) { + printf("MAGIC ERROR, Please check the data@%p.\n", pbuf.buf); + return -1; + } + + ret = i2c_get_chip_for_busnum(CONFIG_SYS_EEPROM_BUS_NUM, + CONFIG_SYS_I2C_EEPROM_ADDR, + CONFIG_SYS_I2C_EEPROM_ADDR_LEN, + &dev); + if (ret) { + printf("Get i2c bus:%d addr:%d fail.\n", CONFIG_SYS_EEPROM_BUS_NUM, + CONFIG_SYS_I2C_EEPROM_ADDR); + return ret; + } + + for (i = 0, p = (u8 *)pbuf.buf; i < size; ) { + if (!ret) + ret = dm_i2c_write(dev, i, p, min((int)(size - i), + BYTES_PER_EEPROM_PAGE)); + if (ret) + break; + + udelay(EEPROM_WRITE_DELAY_MS); + i += BYTES_PER_EEPROM_PAGE; + p += BYTES_PER_EEPROM_PAGE; + } + + if (!ret) { + /* Verify the write by reading back the EEPROM and comparing */ + ret = dm_i2c_read(dev, + STARFIVE_EEPROM_WP_OFFSET, + tmp_buff, + STARFIVE_EEPROM_HATS_SIZE_MAX); + if (!ret && memcmp((void *)pbuf.buf, (void *)tmp_buff, + STARFIVE_EEPROM_HATS_SIZE_MAX)) + ret = -1; + } + + if (ret) { + has_been_read = -1; + printf("Programming failed.\n"); + return -1; + } + + printf("Programming passed.\n"); + return 0; +} + +/** + * read_eeprom() - read the EEPROM into memory, if it hasn't been read already + */ +static int read_eeprom(void) +{ + int ret; + struct udevice *dev; + + if (has_been_read == 1) + return 0; + + ret = i2c_get_chip_for_busnum(CONFIG_SYS_EEPROM_BUS_NUM, + CONFIG_SYS_I2C_EEPROM_ADDR, 1, &dev); + if (!ret) + ret = dm_i2c_read(dev, 0, (u8 *)pbuf.buf, + STARFIVE_EEPROM_HATS_SIZE_MAX); + + has_been_read = (ret == 0) ? 1 : 0; + + return ret; +} + +/** + * set_pcb_revision() - stores a StarFive PCB revision into the local EEPROM copy + * + * Takes a pointer to a string representing the numeric PCB revision in + * decimal ("0" - "255"), stores it in the pcb_revision field of the + * EEPROM local copy, and updates the CRC of the local copy. + */ +static void set_pcb_revision(char *string) +{ + u32 p; + + p = simple_strtoul(string, &string, 16); + if (p > U8_MAX) { + printf("%s must not be greater than %d\n", "PCB revision", + U8_MAX); + return; + } + + pbuf.eeprom.atom4.data.pcb_revision = p; + + update_crc(); +} + +/** + * set_bom_revision() - stores a StarFive BOM revision into the local EEPROM copy + * + * Takes a pointer to a uppercase ASCII character representing the BOM + * revision ("A" - "Z"), stores it in the bom_revision field of the + * EEPROM local copy, and updates the CRC of the local copy. + */ +static void set_bom_revision(char *string) +{ + if (string[0] < 'A' || string[0] > 'Z') { + printf("BOM revision must be an uppercase letter between A and Z\n"); + return; + } + + pbuf.eeprom.atom4.data.bom_revision = string[0]; + + update_crc(); +} + +/** + * set_product_id() - stores a StarFive product ID into the local EEPROM copy + * + * Takes a pointer to a string representing the numeric product ID in + * string ("VF7100A1-2150-D008E000-00000001\0"), stores it in the product string + * field of the EEPROM local copy, and updates the CRC of the local copy. + */ +static void set_product_id(char *string) +{ + u32 len; + + len = (strlen(string) > STARFIVE_EEPROM_ATOM1_PSTR_SIZE) ? + STARFIVE_EEPROM_ATOM1_PSTR_SIZE : strlen(string); + + memcpy((void *)pbuf.eeprom.atom1.data.pstr, (void *)string, len); + + update_crc(); +} + +static int print_usage(void) +{ + printf("display and program the system ID and MAC addresses in EEPROM\n" + "[read_eeprom|initialize|write_eeprom|mac_address|pcb_revision|bom_revision|product_id]\n" + "mac read_eeprom\n" + " - read EEPROM content into memory data structure\n" + "mac write_eeprom\n" + " - save memory data structure to the EEPROM\n" + "mac initialize\n" + " - initialize the in-memory EEPROM copy with default data\n" + "mac mac0_address \n" + " - stores a MAC0 address into the local EEPROM copy\n" + "mac mac1_address \n" + " - stores a MAC1 address into the local EEPROM copy\n" + "mac pcb_revision \n" + " - stores a StarFive PCB revision into the local EEPROM copy\n" + "mac bom_revision \n" + " - stores a StarFive BOM revision into the local EEPROM copy\n" + "mac product_id \n" + " - stores a StarFive product ID into the local EEPROM copy\n"); + return 0; +} + +int do_mac(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + char *cmd; + + if (argc == 1) { + show_eeprom(); + return 0; + } + + if (argc > 3) + return print_usage(); + + cmd = argv[1]; + + /* Commands with no argument */ + if (!strcmp(cmd, "read_eeprom")) { + has_been_read = 0; + return read_eeprom(); + } else if (!strcmp(cmd, "initialize")) { + init_local_copy(); + return 0; + } else if (!strcmp(cmd, "write_eeprom")) { + return prog_eeprom(STARFIVE_EEPROM_HATS_SIZE_MAX); + } + + if (argc != 3) + return print_usage(); + + if (is_match_magic()) { + printf("Please read the EEPROM ('read_eeprom') and/or initialize the EEPROM ('initialize') first.\n"); + return 0; + } + + if (!strcmp(cmd, "mac0_address")) { + set_mac_address(argv[2], 0); + return 0; + } else if (!strcmp(cmd, "mac1_address")) { + set_mac_address(argv[2], 1); + return 0; + } else if (!strcmp(cmd, "pcb_revision")) { + set_pcb_revision(argv[2]); + return 0; + } else if (!strcmp(cmd, "bom_revision")) { + set_bom_revision(argv[2]); + return 0; + } else if (!strcmp(cmd, "product_id")) { + set_product_id(argv[2]); + return 0; + } + + return print_usage(); +} + +/** + * mac_read_from_eeprom() - read the MAC address & the serial number in EEPROM + * + * This function reads the MAC address and the serial number from EEPROM and + * sets the appropriate environment variables for each one read. + * + * The environment variables are only set if they haven't been set already. + * This ensures that any user-saved variables are never overwritten. + * + * If CONFIG_ID_EEPROM is enabled, this function will be called in + * "static init_fnc_t init_sequence_r[]" of u-boot/common/board_r.c. + */ +int mac_read_from_eeprom(void) +{ + /** + * try to fill the buff from EEPROM, + * always return SUCCESS, even some error happens. + */ + if (read_eeprom()) { + dump_raw_eeprom(); + return 0; + } + + // 1, setup ethaddr env + eth_env_set_enetaddr("eth0addr", pbuf.eeprom.atom4.data.mac0_addr); + eth_env_set_enetaddr("eth1addr", pbuf.eeprom.atom4.data.mac1_addr); + + /** + * 2, setup serial# env, reference to hifive-platform-i2c-eeprom.c, + * serial# can be a ASCII string, but not just a hex number, so we + * setup serial# in the 32Byte format: + * "VF7100A1-2201-D008E000-00000001;" + * "---" + * : 4Byte, should be the output of `date +%y%W` + * : 8Byte, "D008" means 8GB, "D01T" means 1TB; + * "E000" means no eMMC,"E032" means 32GB, "E01T" means 1TB. + * : 8Byte, the Unique Identifier of board in hex. + */ + if (!env_get("serial#")) + env_set("serial#", pbuf.eeprom.atom1.data.pstr); + + printf("StarFive EEPROM format v%u\n", pbuf.eeprom.header.version); + show_eeprom(); + return 0; +} + +/** + * get_pcb_revision_from_eeprom - get the PCB revision + * + * 1.2A return 'A'/'a', 1.3B return 'B'/'b',other values are illegal + */ +u8 get_pcb_revision_from_eeprom(void) +{ + u8 pv = 0xFF; + + if (read_eeprom()) + return pv; + + return pbuf.eeprom.atom1.data.pstr[6]; +} + +/** + * get_ddr_size_from_eeprom - get the DDR size + * pstr: VF7110A1-2228-D008E000-00000001 + * VF7110A1/VF7110B1 : VisionFive JH7110A /VisionFive JH7110B + * D008: 8GB LPDDR4 + * E000: No emmc device, ECxx: include emmc device, xx: Capacity size[GB] + * return: the field of 'D008E000' + */ + +u32 get_ddr_size_from_eeprom(void) +{ + u32 pv = 0xFFFFFFFF; + + if (read_eeprom()) + return pv; + + return hextoul(&pbuf.eeprom.atom1.data.pstr[14], NULL); +} -- 2.39.5