mirror of
				https://github.com/smaeul/u-boot.git
				synced 2025-10-31 03:58:17 +00:00 
			
		
		
		
	As boards from multiple vendors (Milk-V, StarFive, Pine64) use the mac command provide a sub-command to set the vendor string. Reported-by: E. Shattow <lucent@gmail.com> Signed-off-by: Heinrich Schuchardt <heinrich.schuchardt@canonical.com>
		
			
				
	
	
		
			618 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			618 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| // 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);
 |