| // SPDX-License-Identifier: GPL-2.0+ | |
| /* | |
| * Copyright (C) 2023 StarFive Technology Co., Ltd. | |
| * Author: Yanhong Wang<yanhong.wang@starfivetech.com> | |
| */ | |
| #include <command.h> | |
| #include <env.h> | |
| #include <i2c.h> | |
| #include <init.h> | |
| #include <u-boot/crc.h> | |
| #include <linux/delay.h> | |
| #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); | |
| dump_raw_eeprom(); | |
| } | |
| 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(); | |
| } | |
| /** | |
| * set_vendor() - set vendor name | |
| * | |
| * Takes a pointer to a string representing the vendor name, e.g. | |
| * "StarFive Technology Co., Ltd.", stores it in the vendor field | |
| * of the EEPROM local copy, and updates the CRC of the local copy. | |
| */ | |
| static void set_vendor(char *string) | |
| { | |
| memset(pbuf.eeprom.atom1.data.vstr, 0, | |
| sizeof(pbuf.eeprom.atom1.data.vstr)); | |
| strncpy(pbuf.eeprom.atom1.data.vstr, | |
| string, sizeof(pbuf.eeprom.atom1.data.vstr) - 1); | |
| update_crc(); | |
| } | |
| const char *get_product_id_from_eeprom(void) | |
| { | |
| if (read_eeprom()) | |
| return NULL; | |
| return pbuf.eeprom.atom1.data.pstr; | |
| } | |
| 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 CMD_RET_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); | |
| } else if (!strcmp(cmd, "raw")) { | |
| dump_raw_eeprom(); | |
| return 0; | |
| } | |
| if (argc != 3) | |
| return CMD_RET_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; | |
| } else if (!strcmp(cmd, "vendor")) { | |
| set_vendor(argv[2]); | |
| return 0; | |
| } | |
| return CMD_RET_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("ethaddr", 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;" | |
| * "<product>-<date>-<DDR&eMMC>-<serial_number>" | |
| * <date>: 4Byte, should be the output of `date +%y%W` | |
| * <DDR&eMMC>: 8Byte, "D008" means 8GB, "D01T" means 1TB; | |
| * "E000" means no eMMC, "E032" means 32GB, "E01T" means 1TB. | |
| * <serial_number>: 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); | |
| } | |
| u32 get_mmc_size_from_eeprom(void) | |
| { | |
| u32 size; | |
| if (IS_ENABLED(CONFIG_STARFIVE_NO_EMMC)) | |
| return 0; | |
| if (read_eeprom()) | |
| return 0; | |
| size = dectoul(&pbuf.eeprom.atom1.data.pstr[19], NULL); | |
| if (pbuf.eeprom.atom1.data.pstr[21] == 'T') | |
| size <<= 10; | |
| return size; | |
| } | |
| U_BOOT_LONGHELP(mac, | |
| "\n" | |
| " - display EEPROM content\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 raw\n" | |
| " - hexdump memory data structure\n" | |
| "mac mac0_address <xx:xx:xx:xx:xx:xx>\n" | |
| " - stores a MAC0 address into the local EEPROM copy\n" | |
| "mac mac1_address <xx:xx:xx:xx:xx:xx>\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 <A>\n" | |
| " - stores a StarFive BOM revision into the local EEPROM copy\n" | |
| "mac product_id <VF7110A1-2228-D008E000-xxxxxxxx>\n" | |
| " - stores a StarFive product ID into the local EEPROM copy\n" | |
| "mac vendor <Vendor Name>\n" | |
| " - set vendor string\n"); | |
| U_BOOT_CMD( | |
| mac, 3, 1, do_mac, | |
| "display and program the board revision and MAC address in EEPROM", | |
| mac_help_text); |