blob: c36de1a5125b56285fa3d4b6de3d858782347b85 [file] [log] [blame]
Yanhong Wang39331e42023-06-15 17:36:48 +08001// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Copyright (C) 2023 StarFive Technology Co., Ltd.
4 * Author: Yanhong Wang<yanhong.wang@starfivetech.com>
5 */
6
7#include <common.h>
8#include <command.h>
9#include <env.h>
10#include <i2c.h>
11#include <init.h>
12#include <u-boot/crc.h>
13#include <linux/delay.h>
14
15#define FORMAT_VERSION 0x2
16#define PCB_VERSION 0xB1
17#define BOM_VERSION 'A'
18/*
19 * BYTES_PER_EEPROM_PAGE: the 24FC04H datasheet says that data can
20 * only be written in page mode, which means 16 bytes at a time:
21 * 16-Byte Page Write Buffer
22 */
23#define BYTES_PER_EEPROM_PAGE 16
24
25/*
26 * EEPROM_WRITE_DELAY_MS: the 24FC04H datasheet says it takes up to
27 * 5ms to complete a given write:
28 * Write Cycle Time (byte or page) ro Page Write Time 5 ms, Maximum
29 */
30#define EEPROM_WRITE_DELAY_MS 5000
31/*
32 * StarFive OUI. Registration Date is 20xx-xx-xx
33 */
34#define STARFIVE_OUI_PREFIX "6C:CF:39:"
35#define STARFIVE_DEFAULT_MAC0 "6C:CF:39:6C:DE:AD"
36#define STARFIVE_DEFAULT_MAC1 "6C:CF:39:6C:DE:AE"
37
38/* Magic number at the first four bytes of EEPROM HATs */
39#define STARFIVE_EEPROM_HATS_SIG "SFVF" /* StarFive VisionFive */
40
41#define STARFIVE_EEPROM_HATS_SIZE_MAX 256 /* Header + Atom1&4(v1) */
42#define STARFIVE_EEPROM_WP_OFFSET 0 /* Read only field */
43#define STARFIVE_EEPROM_ATOM1_PSTR "VF7110A1-2228-D008E000-00000001\0"
44#define STARFIVE_EEPROM_ATOM1_PSTR_SIZE 32
45#define STARFIVE_EEPROM_ATOM1_SN_OFFSET 23
46#define STARFIVE_EEPROM_ATOM1_VSTR "StarFive Technology Co., Ltd.\0\0\0"
47#define STARFIVE_EEPROM_ATOM1_VSTR_SIZE 32
48
49#define MAGIC_NUMBER_BYTES 4
50#define MAC_ADDR_BYTES 6
51#define MAC_ADDR_STRLEN 17
52
53/*
54 * Atom Types
55 * 0x0000 = invalid
56 * 0x0001 = vendor info
57 * 0x0002 = GPIO map
58 * 0x0003 = Linux device tree blob
59 * 0x0004 = manufacturer custom data
60 * 0x0005-0xfffe = reserved for future use
61 * 0xffff = invalid
62 */
63
64#define HATS_ATOM_INVALID 0x0000
65#define HATS_ATOM_VENDOR 0x0001
66#define HATS_ATOM_GPIO 0x0002
67#define HATS_ATOM_DTB 0x0003
68#define HATS_ATOM_CUSTOM 0x0004
69#define HATS_ATOM_INVALID_END 0xffff
70
71struct eeprom_header {
72 char signature[MAGIC_NUMBER_BYTES]; /* ASCII table signature */
73 u8 version; /* EEPROM data format version */
74 /* (0x00 reserved, 0x01 = first version) */
75 u8 reversed; /* 0x00, Reserved field */
76 u16 numatoms; /* total atoms in EEPROM */
77 u32 eeplen; /* total length in bytes of all eeprom data */
78 /* (including this header) */
79};
80
81struct eeprom_atom_header {
82 u16 type;
83 u16 count;
84 u32 dlen;
85};
86
87struct eeprom_atom1_data {
88 u8 uuid[16];
89 u16 pid;
90 u16 pver;
91 u8 vslen;
92 u8 pslen;
93 uchar vstr[STARFIVE_EEPROM_ATOM1_VSTR_SIZE];
94 uchar pstr[STARFIVE_EEPROM_ATOM1_PSTR_SIZE]; /* product SN */
95};
96
97struct starfive_eeprom_atom1 {
98 struct eeprom_atom_header header;
99 struct eeprom_atom1_data data;
100 u16 crc;
101};
102
103struct eeprom_atom4_data {
104 u16 version;
105 u8 pcb_revision; /* PCB version */
106 u8 bom_revision; /* BOM version */
107 u8 mac0_addr[MAC_ADDR_BYTES]; /* Ethernet0 MAC */
108 u8 mac1_addr[MAC_ADDR_BYTES]; /* Ethernet1 MAC */
109 u8 reserved[2];
110};
111
112struct starfive_eeprom_atom4 {
113 struct eeprom_atom_header header;
114 struct eeprom_atom4_data data;
115 u16 crc;
116};
117
118struct starfive_eeprom {
119 struct eeprom_header header;
120 struct starfive_eeprom_atom1 atom1;
121 struct starfive_eeprom_atom4 atom4;
122};
123
124static union {
125 struct starfive_eeprom eeprom;
126 uchar buf[STARFIVE_EEPROM_HATS_SIZE_MAX];
127} pbuf __section(".data");
128
129/* Set to 1 if we've read EEPROM into memory */
130static int has_been_read __section(".data");
131
132static inline int is_match_magic(void)
133{
134 return strncmp(pbuf.eeprom.header.signature, STARFIVE_EEPROM_HATS_SIG,
135 MAGIC_NUMBER_BYTES);
136}
137
138/* Calculate the current CRC */
139static inline u32 calculate_crc16(struct eeprom_atom_header *head)
140{
141 uint len = sizeof(struct eeprom_atom_header) + head->dlen - sizeof(u16);
142
143 return crc16(0, (void *)head, len);
144}
145
146/* This function should be called after each update to the EEPROM structure */
147static inline void update_crc(void)
148{
149 pbuf.eeprom.atom1.crc = calculate_crc16(&pbuf.eeprom.atom1.header);
150 pbuf.eeprom.atom4.crc = calculate_crc16(&pbuf.eeprom.atom4.header);
151}
152
153static void dump_raw_eeprom(void)
154{
155 unsigned int i;
156 u32 len;
157
158 len = sizeof(struct starfive_eeprom);
159 for (i = 0; i < len; i++) {
160 if ((i % 16) == 0)
161 printf("%02X: ", i);
162 printf("%02X ", ((u8 *)pbuf.buf)[i]);
163 if (((i % 16) == 15) || (i == len - 1))
164 printf("\n");
165 }
166}
167
168/**
169 * show_eeprom - display the contents of the EEPROM
170 */
171static void show_eeprom(void)
172{
173 if (has_been_read != 1)
174 return;
175
176 printf("\n--------EEPROM INFO--------\n");
177 printf("Vendor : %s\n", pbuf.eeprom.atom1.data.vstr);
178 printf("Product full SN: %s\n", pbuf.eeprom.atom1.data.pstr);
179 printf("data version: 0x%x\n", pbuf.eeprom.atom4.data.version);
180 if (pbuf.eeprom.atom4.data.version == 2) {
181 printf("PCB revision: 0x%x\n", pbuf.eeprom.atom4.data.pcb_revision);
182 printf("BOM revision: %c\n", pbuf.eeprom.atom4.data.bom_revision);
183 printf("Ethernet MAC0 address: %02x:%02x:%02x:%02x:%02x:%02x\n",
184 pbuf.eeprom.atom4.data.mac0_addr[0], pbuf.eeprom.atom4.data.mac0_addr[1],
185 pbuf.eeprom.atom4.data.mac0_addr[2], pbuf.eeprom.atom4.data.mac0_addr[3],
186 pbuf.eeprom.atom4.data.mac0_addr[4], pbuf.eeprom.atom4.data.mac0_addr[5]);
187 printf("Ethernet MAC1 address: %02x:%02x:%02x:%02x:%02x:%02x\n",
188 pbuf.eeprom.atom4.data.mac1_addr[0], pbuf.eeprom.atom4.data.mac1_addr[1],
189 pbuf.eeprom.atom4.data.mac1_addr[2], pbuf.eeprom.atom4.data.mac1_addr[3],
190 pbuf.eeprom.atom4.data.mac1_addr[4], pbuf.eeprom.atom4.data.mac1_addr[5]);
191 } else {
192 printf("Custom data v%d is not Supported\n", pbuf.eeprom.atom4.data.version);
Heinrich Schuchardtab23ee42023-09-30 14:01:46 +0200193 dump_raw_eeprom();
Yanhong Wang39331e42023-06-15 17:36:48 +0800194 }
195 printf("--------EEPROM INFO--------\n\n");
196}
197
198/**
199 * set_mac_address() - stores a MAC address into the local EEPROM copy
200 *
201 * This function takes a pointer to MAC address string
202 * (i.e."XX:XX:XX:XX:XX:XX", where "XX" is a two-digit hex number),
203 * stores it in the MAC address field of the EEPROM local copy, and
204 * updates the local copy of the CRC.
205 */
206static void set_mac_address(char *string, int index)
207{
208 u8 i;
209 u8 *mac;
210
211 if (strncasecmp(STARFIVE_OUI_PREFIX, string,
212 strlen(STARFIVE_OUI_PREFIX))) {
213 printf("The MAC address doesn't match StarFive OUI %s\n",
214 STARFIVE_OUI_PREFIX);
215 return;
216 }
217 mac = (index == 0) ? pbuf.eeprom.atom4.data.mac0_addr :
218 pbuf.eeprom.atom4.data.mac1_addr;
219
220 for (i = 0; *string && (i < MAC_ADDR_BYTES); i++) {
221 mac[i] = hextoul(string, &string);
222
223 if (*string == ':')
224 string++;
225 }
226
227 update_crc();
228}
229
230/**
231 * init_local_copy() - initialize the in-memory EEPROM copy
232 *
233 * Initialize the in-memory EEPROM copy with the magic number. Must
234 * be done when preparing to initialize a blank EEPROM, or overwrite
235 * one with a corrupted magic number.
236 */
237static void init_local_copy(void)
238{
239 memset((void *)pbuf.buf, 0, sizeof(struct starfive_eeprom));
240 memcpy(pbuf.eeprom.header.signature, STARFIVE_EEPROM_HATS_SIG,
241 strlen(STARFIVE_EEPROM_HATS_SIG));
242 pbuf.eeprom.header.version = FORMAT_VERSION;
243 pbuf.eeprom.header.numatoms = 2;
244 pbuf.eeprom.header.eeplen = sizeof(struct starfive_eeprom);
245
246 pbuf.eeprom.atom1.header.type = HATS_ATOM_VENDOR;
247 pbuf.eeprom.atom1.header.count = 1;
248 pbuf.eeprom.atom1.header.dlen = sizeof(struct eeprom_atom1_data) + sizeof(u16);
249 pbuf.eeprom.atom1.data.vslen = STARFIVE_EEPROM_ATOM1_VSTR_SIZE;
250 pbuf.eeprom.atom1.data.pslen = STARFIVE_EEPROM_ATOM1_PSTR_SIZE;
251 memcpy(pbuf.eeprom.atom1.data.vstr, STARFIVE_EEPROM_ATOM1_VSTR,
252 strlen(STARFIVE_EEPROM_ATOM1_VSTR));
253 memcpy(pbuf.eeprom.atom1.data.pstr, STARFIVE_EEPROM_ATOM1_PSTR,
254 strlen(STARFIVE_EEPROM_ATOM1_PSTR));
255
256 pbuf.eeprom.atom4.header.type = HATS_ATOM_CUSTOM;
257 pbuf.eeprom.atom4.header.count = 2;
258 pbuf.eeprom.atom4.header.dlen = sizeof(struct eeprom_atom4_data) + sizeof(u16);
259 pbuf.eeprom.atom4.data.version = FORMAT_VERSION;
260 pbuf.eeprom.atom4.data.pcb_revision = PCB_VERSION;
261 pbuf.eeprom.atom4.data.bom_revision = BOM_VERSION;
262 set_mac_address(STARFIVE_DEFAULT_MAC0, 0);
263 set_mac_address(STARFIVE_DEFAULT_MAC1, 1);
264}
265
266/**
267 * prog_eeprom() - write the EEPROM from memory
268 */
269static int prog_eeprom(unsigned int size)
270{
271 unsigned int i;
272 void *p;
273 uchar tmp_buff[STARFIVE_EEPROM_HATS_SIZE_MAX];
274 struct udevice *dev;
275 int ret;
276
277 if (is_match_magic()) {
278 printf("MAGIC ERROR, Please check the data@%p.\n", pbuf.buf);
279 return -1;
280 }
281
282 ret = i2c_get_chip_for_busnum(CONFIG_SYS_EEPROM_BUS_NUM,
283 CONFIG_SYS_I2C_EEPROM_ADDR,
284 CONFIG_SYS_I2C_EEPROM_ADDR_LEN,
285 &dev);
286 if (ret) {
287 printf("Get i2c bus:%d addr:%d fail.\n", CONFIG_SYS_EEPROM_BUS_NUM,
288 CONFIG_SYS_I2C_EEPROM_ADDR);
289 return ret;
290 }
291
292 for (i = 0, p = (u8 *)pbuf.buf; i < size; ) {
293 if (!ret)
294 ret = dm_i2c_write(dev, i, p, min((int)(size - i),
295 BYTES_PER_EEPROM_PAGE));
296 if (ret)
297 break;
298
299 udelay(EEPROM_WRITE_DELAY_MS);
300 i += BYTES_PER_EEPROM_PAGE;
301 p += BYTES_PER_EEPROM_PAGE;
302 }
303
304 if (!ret) {
305 /* Verify the write by reading back the EEPROM and comparing */
306 ret = dm_i2c_read(dev,
307 STARFIVE_EEPROM_WP_OFFSET,
308 tmp_buff,
309 STARFIVE_EEPROM_HATS_SIZE_MAX);
310 if (!ret && memcmp((void *)pbuf.buf, (void *)tmp_buff,
311 STARFIVE_EEPROM_HATS_SIZE_MAX))
312 ret = -1;
313 }
314
315 if (ret) {
316 has_been_read = -1;
317 printf("Programming failed.\n");
318 return -1;
319 }
320
321 printf("Programming passed.\n");
322 return 0;
323}
324
325/**
326 * read_eeprom() - read the EEPROM into memory, if it hasn't been read already
327 */
328static int read_eeprom(void)
329{
330 int ret;
331 struct udevice *dev;
332
333 if (has_been_read == 1)
334 return 0;
335
336 ret = i2c_get_chip_for_busnum(CONFIG_SYS_EEPROM_BUS_NUM,
337 CONFIG_SYS_I2C_EEPROM_ADDR, 1, &dev);
338 if (!ret)
339 ret = dm_i2c_read(dev, 0, (u8 *)pbuf.buf,
340 STARFIVE_EEPROM_HATS_SIZE_MAX);
341
342 has_been_read = (ret == 0) ? 1 : 0;
343
344 return ret;
345}
346
347/**
348 * set_pcb_revision() - stores a StarFive PCB revision into the local EEPROM copy
349 *
350 * Takes a pointer to a string representing the numeric PCB revision in
351 * decimal ("0" - "255"), stores it in the pcb_revision field of the
352 * EEPROM local copy, and updates the CRC of the local copy.
353 */
354static void set_pcb_revision(char *string)
355{
356 u32 p;
357
358 p = simple_strtoul(string, &string, 16);
359 if (p > U8_MAX) {
360 printf("%s must not be greater than %d\n", "PCB revision",
361 U8_MAX);
362 return;
363 }
364
365 pbuf.eeprom.atom4.data.pcb_revision = p;
366
367 update_crc();
368}
369
370/**
371 * set_bom_revision() - stores a StarFive BOM revision into the local EEPROM copy
372 *
373 * Takes a pointer to a uppercase ASCII character representing the BOM
374 * revision ("A" - "Z"), stores it in the bom_revision field of the
375 * EEPROM local copy, and updates the CRC of the local copy.
376 */
377static void set_bom_revision(char *string)
378{
379 if (string[0] < 'A' || string[0] > 'Z') {
380 printf("BOM revision must be an uppercase letter between A and Z\n");
381 return;
382 }
383
384 pbuf.eeprom.atom4.data.bom_revision = string[0];
385
386 update_crc();
387}
388
389/**
390 * set_product_id() - stores a StarFive product ID into the local EEPROM copy
391 *
392 * Takes a pointer to a string representing the numeric product ID in
393 * string ("VF7100A1-2150-D008E000-00000001\0"), stores it in the product string
394 * field of the EEPROM local copy, and updates the CRC of the local copy.
395 */
396static void set_product_id(char *string)
397{
398 u32 len;
399
400 len = (strlen(string) > STARFIVE_EEPROM_ATOM1_PSTR_SIZE) ?
401 STARFIVE_EEPROM_ATOM1_PSTR_SIZE : strlen(string);
402
403 memcpy((void *)pbuf.eeprom.atom1.data.pstr, (void *)string, len);
404
405 update_crc();
406}
407
Yanhong Wang39331e42023-06-15 17:36:48 +0800408int do_mac(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
409{
410 char *cmd;
411
412 if (argc == 1) {
413 show_eeprom();
414 return 0;
415 }
416
417 if (argc > 3)
Heinrich Schuchardta3cf2362023-09-30 14:01:45 +0200418 return CMD_RET_USAGE;
Yanhong Wang39331e42023-06-15 17:36:48 +0800419
420 cmd = argv[1];
421
422 /* Commands with no argument */
423 if (!strcmp(cmd, "read_eeprom")) {
424 has_been_read = 0;
425 return read_eeprom();
426 } else if (!strcmp(cmd, "initialize")) {
427 init_local_copy();
428 return 0;
429 } else if (!strcmp(cmd, "write_eeprom")) {
430 return prog_eeprom(STARFIVE_EEPROM_HATS_SIZE_MAX);
Heinrich Schuchardt6b4bb9a2023-09-30 14:01:47 +0200431 } else if (!strcmp(cmd, "raw")) {
432 dump_raw_eeprom();
433 return 0;
Yanhong Wang39331e42023-06-15 17:36:48 +0800434 }
435
436 if (argc != 3)
Heinrich Schuchardta3cf2362023-09-30 14:01:45 +0200437 return CMD_RET_USAGE;
Yanhong Wang39331e42023-06-15 17:36:48 +0800438
439 if (is_match_magic()) {
440 printf("Please read the EEPROM ('read_eeprom') and/or initialize the EEPROM ('initialize') first.\n");
441 return 0;
442 }
443
444 if (!strcmp(cmd, "mac0_address")) {
445 set_mac_address(argv[2], 0);
446 return 0;
447 } else if (!strcmp(cmd, "mac1_address")) {
448 set_mac_address(argv[2], 1);
449 return 0;
450 } else if (!strcmp(cmd, "pcb_revision")) {
451 set_pcb_revision(argv[2]);
452 return 0;
453 } else if (!strcmp(cmd, "bom_revision")) {
454 set_bom_revision(argv[2]);
455 return 0;
456 } else if (!strcmp(cmd, "product_id")) {
457 set_product_id(argv[2]);
458 return 0;
459 }
460
Heinrich Schuchardta3cf2362023-09-30 14:01:45 +0200461 return CMD_RET_USAGE;
Yanhong Wang39331e42023-06-15 17:36:48 +0800462}
463
464/**
465 * mac_read_from_eeprom() - read the MAC address & the serial number in EEPROM
466 *
467 * This function reads the MAC address and the serial number from EEPROM and
468 * sets the appropriate environment variables for each one read.
469 *
470 * The environment variables are only set if they haven't been set already.
471 * This ensures that any user-saved variables are never overwritten.
472 *
473 * If CONFIG_ID_EEPROM is enabled, this function will be called in
474 * "static init_fnc_t init_sequence_r[]" of u-boot/common/board_r.c.
475 */
476int mac_read_from_eeprom(void)
477{
478 /**
479 * try to fill the buff from EEPROM,
480 * always return SUCCESS, even some error happens.
481 */
482 if (read_eeprom()) {
483 dump_raw_eeprom();
484 return 0;
485 }
486
487 // 1, setup ethaddr env
Seung-Woo Kimd414b7b2023-08-11 16:12:25 +0900488 eth_env_set_enetaddr("ethaddr", pbuf.eeprom.atom4.data.mac0_addr);
Yanhong Wang39331e42023-06-15 17:36:48 +0800489 eth_env_set_enetaddr("eth1addr", pbuf.eeprom.atom4.data.mac1_addr);
490
491 /**
492 * 2, setup serial# env, reference to hifive-platform-i2c-eeprom.c,
493 * serial# can be a ASCII string, but not just a hex number, so we
494 * setup serial# in the 32Byte format:
495 * "VF7100A1-2201-D008E000-00000001;"
496 * "<product>-<date>-<DDR&eMMC>-<serial_number>"
497 * <date>: 4Byte, should be the output of `date +%y%W`
498 * <DDR&eMMC>: 8Byte, "D008" means 8GB, "D01T" means 1TB;
499 * "E000" means no eMMC,"E032" means 32GB, "E01T" means 1TB.
500 * <serial_number>: 8Byte, the Unique Identifier of board in hex.
501 */
502 if (!env_get("serial#"))
503 env_set("serial#", pbuf.eeprom.atom1.data.pstr);
504
505 printf("StarFive EEPROM format v%u\n", pbuf.eeprom.header.version);
506 show_eeprom();
507 return 0;
508}
509
510/**
511 * get_pcb_revision_from_eeprom - get the PCB revision
512 *
513 * 1.2A return 'A'/'a', 1.3B return 'B'/'b',other values are illegal
514 */
515u8 get_pcb_revision_from_eeprom(void)
516{
517 u8 pv = 0xFF;
518
519 if (read_eeprom())
520 return pv;
521
522 return pbuf.eeprom.atom1.data.pstr[6];
523}
524
525/**
526 * get_ddr_size_from_eeprom - get the DDR size
527 * pstr: VF7110A1-2228-D008E000-00000001
528 * VF7110A1/VF7110B1 : VisionFive JH7110A /VisionFive JH7110B
529 * D008: 8GB LPDDR4
530 * E000: No emmc device, ECxx: include emmc device, xx: Capacity size[GB]
531 * return: the field of 'D008E000'
532 */
533
534u32 get_ddr_size_from_eeprom(void)
535{
536 u32 pv = 0xFFFFFFFF;
537
538 if (read_eeprom())
539 return pv;
540
541 return hextoul(&pbuf.eeprom.atom1.data.pstr[14], NULL);
542}
Heinrich Schuchardta3cf2362023-09-30 14:01:45 +0200543
Tom Rini03f146c2023-10-07 15:13:08 -0400544U_BOOT_LONGHELP(mac,
Heinrich Schuchardta3cf2362023-09-30 14:01:45 +0200545 "\n"
546 " - display EEPROM content\n"
547 "mac read_eeprom\n"
548 " - read EEPROM content into memory data structure\n"
549 "mac write_eeprom\n"
550 " - save memory data structure to the EEPROM\n"
551 "mac initialize\n"
552 " - initialize the in-memory EEPROM copy with default data\n"
Heinrich Schuchardt6b4bb9a2023-09-30 14:01:47 +0200553 "mac raw\n"
554 " - hexdump memory data structure\n"
Heinrich Schuchardta3cf2362023-09-30 14:01:45 +0200555 "mac mac0_address <xx:xx:xx:xx:xx:xx>\n"
556 " - stores a MAC0 address into the local EEPROM copy\n"
557 "mac mac1_address <xx:xx:xx:xx:xx:xx>\n"
558 " - stores a MAC1 address into the local EEPROM copy\n"
559 "mac pcb_revision <?>\n"
560 " - stores a StarFive PCB revision into the local EEPROM copy\n"
561 "mac bom_revision <A>\n"
562 " - stores a StarFive BOM revision into the local EEPROM copy\n"
563 "mac product_id <VF7110A1-2228-D008E000-xxxxxxxx>\n"
Tom Rini03f146c2023-10-07 15:13:08 -0400564 " - stores a StarFive product ID into the local EEPROM copy\n");
Heinrich Schuchardta3cf2362023-09-30 14:01:45 +0200565
566U_BOOT_CMD(
567 mac, 3, 1, do_mac,
568 "display and program the board revision and MAC address in EEPROM",
Tom Rini03f146c2023-10-07 15:13:08 -0400569 mac_help_text);