blob: d25c24b08b394c686b313e410d7edca929f53b4e [file] [log] [blame]
Zong Lib99fc942021-06-30 23:23:45 +08001// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Copyright (C) 2020 SiFive, Inc.
4 *
5 * Based on board/freescale/common/sys_eeprom.c:
6 * Copyright 2006, 2008-2009, 2011 Freescale Semiconductor
7 * York Sun (yorksun@freescale.com)
8 * Haiying Wang (haiying.wang@freescale.com)
9 * Timur Tabi (timur@freescale.com)
10 */
11
Zong Lib99fc942021-06-30 23:23:45 +080012#include <command.h>
13#include <env.h>
14#include <i2c.h>
15#include <init.h>
16#include <linux/ctype.h>
17#include <linux/delay.h>
18#include <u-boot/crc.h>
19
20#ifndef CONFIG_SYS_EEPROM_BUS_NUM
21#error Requires CONFIG_SYS_EEPROM_BUS_NUM to be defined
22#endif
23
24#define FORMAT_VERSION 0x1
25
26/* Options for the manuf_test_status field */
27#define SIFIVE_MANUF_TEST_STATUS_UNKNOWN 0
28#define SIFIVE_MANUF_TEST_STATUS_PASS 1
29#define SIFIVE_MANUF_TEST_STATUS_FAIL 2
30
31/*
32 * BYTES_PER_EEPROM_PAGE: the AT24C02 datasheet says that data can
33 * only be written in page mode, which means 8 bytes at a time
34 */
35#define BYTES_PER_EEPROM_PAGE 8
36
37/*
38 * EEPROM_WRITE_DELAY_MS: the AT24C02 datasheet says it takes up to
39 * 5ms to complete a given write
40 */
41#define EEPROM_WRITE_DELAY_MS 5000
42
43/*
44 * MAGIC_NUMBER_BYTES: number of bytes used by the magic number
45 */
46#define MAGIC_NUMBER_BYTES 4
47
48/*
49 * SERIAL_NUMBER_BYTES: number of bytes used by the board serial
50 * number
51 */
52#define SERIAL_NUMBER_BYTES 16
53
54/*
55 * MAC_ADDR_BYTES: number of bytes used by the Ethernet MAC address
56 */
57#define MAC_ADDR_BYTES 6
58
59/*
60 * MAC_ADDR_STRLEN: length of mac address string
61 */
62#define MAC_ADDR_STRLEN 17
63
64/*
65 * SiFive OUI. Registration Date is 2018-02-15
66 */
67#define SIFIVE_OUI_PREFIX "70:B3:D5:92:F"
68
69/**
70 * static eeprom: EEPROM layout for the SiFive platform I2C format
71 */
72static struct __attribute__ ((__packed__)) sifive_eeprom {
73 u8 magic[MAGIC_NUMBER_BYTES];
74 u8 format_ver;
75 u16 product_id;
76 u8 pcb_revision;
77 u8 bom_revision;
78 u8 bom_variant;
79 u8 serial[SERIAL_NUMBER_BYTES];
80 u8 manuf_test_status;
81 u8 mac_addr[MAC_ADDR_BYTES];
82 u32 crc;
83} e;
84
85struct sifive_product {
86 u16 id;
87 const char *name;
88};
89
90/* Set to 1 if we've read EEPROM into memory */
91static int has_been_read;
92
93/* Magic number at the first four bytes of EEPROM */
94static const unsigned char magic[MAGIC_NUMBER_BYTES] = { 0xf1, 0x5e, 0x50, 0x45 };
95
96/* Does the magic number match that of a SiFive EEPROM? */
97static inline int is_match_magic(void)
98{
99 return (memcmp(&e.magic, &magic, MAGIC_NUMBER_BYTES) == 0);
100}
101
102/* Calculate the current CRC */
103static inline u32 calculate_crc32(void)
104{
105 return crc32(0, (void *)&e, sizeof(struct sifive_eeprom) - sizeof(e.crc));
106}
107
108/* This function should be called after each update to the EEPROM structure */
109static inline void update_crc(void)
110{
111 e.crc = calculate_crc32();
112}
113
114static struct sifive_product sifive_products[] = {
115 { 0, "Unknown"},
116 { 2, "HiFive Unmatched" },
117};
118
119/**
120 * dump_raw_eeprom - display the raw contents of the EEPROM
121 */
122static void dump_raw_eeprom(void)
123{
124 unsigned int i;
125
126 printf("EEPROM dump: (0x%lx bytes)\n", sizeof(e));
127 for (i = 0; i < sizeof(e); i++) {
128 if ((i % 16) == 0)
129 printf("%02X: ", i);
130 printf("%02X ", ((u8 *)&e)[i]);
131 if (((i % 16) == 15) || (i == sizeof(e) - 1))
132 printf("\n");
133 }
134}
135
136/**
137 * show_eeprom - display the contents of the EEPROM
138 */
139static void show_eeprom(void)
140{
141 unsigned int i;
142 u32 crc;
143 const char *product_name = "Unknown";
144 char board_serial[SERIAL_NUMBER_BYTES + 1] = { 0 };
145
146 if (!is_match_magic()) {
147 printf("Not a SiFive HiFive EEPROM data format - magic bytes don't match\n");
148 dump_raw_eeprom();
149 return;
150 };
151
152 snprintf(board_serial, sizeof(board_serial), "%s", e.serial);
153
154 for (i = 0; i < ARRAY_SIZE(sifive_products); i++) {
155 if (sifive_products[i].id == e.product_id) {
156 product_name = sifive_products[i].name;
157 break;
158 }
159 };
160
161 printf("SiFive PCB EEPROM format v%u\n", e.format_ver);
162 printf("Product ID: %04hx (%s)\n", e.product_id, product_name);
163 printf("PCB revision: %x\n", e.pcb_revision);
164 printf("BOM revision: %c\n", e.bom_revision);
165 printf("BOM variant: %x\n", e.bom_variant);
166 printf("Serial number: %s\n", board_serial);
167 printf("Ethernet MAC address: %02x:%02x:%02x:%02x:%02x:%02x\n",
168 e.mac_addr[0], e.mac_addr[1], e.mac_addr[2],
169 e.mac_addr[3], e.mac_addr[4], e.mac_addr[5]);
170
171 crc = calculate_crc32();
172 if (crc == e.crc) {
173 printf("CRC: %08x\n", e.crc);
174 } else {
175 printf("CRC: %08x (should be %08x)\n", e.crc, crc);
176 dump_raw_eeprom();
177 }
178}
179
180/**
181 * read_eeprom() - read the EEPROM into memory, if it hasn't been read already
182 */
183static int read_eeprom(void)
184{
185 int ret;
186 struct udevice *dev;
187
188 if (has_been_read)
189 return 0;
190
191 ret = i2c_get_chip_for_busnum(CONFIG_SYS_EEPROM_BUS_NUM,
192 CONFIG_SYS_I2C_EEPROM_ADDR,
193 1,
194 &dev);
195 if (!ret)
196 dm_i2c_read(dev, 0, (void *)&e,
197 sizeof(struct sifive_eeprom));
198
199 show_eeprom();
200
201 has_been_read = (ret == 0) ? 1 : 0;
202
203 return ret;
204}
205
206/**
207 * prog_eeprom() - write the EEPROM from memory
208 */
209static int prog_eeprom(void)
210{
211 int ret = 0;
212 unsigned int i;
213 void *p;
214
215 if (!is_match_magic()) {
216 printf("Please read the EEPROM ('read_eeprom') and/or initialize the EEPROM ('initialize') first.\n");
217 return 0;
218 }
219
220 for (i = 0, p = &e; i < sizeof(e);
221 i += BYTES_PER_EEPROM_PAGE, p += BYTES_PER_EEPROM_PAGE) {
222 struct udevice *dev;
223
224 ret = i2c_get_chip_for_busnum(CONFIG_SYS_EEPROM_BUS_NUM,
225 CONFIG_SYS_I2C_EEPROM_ADDR,
226 CONFIG_SYS_I2C_EEPROM_ADDR_LEN,
227 &dev);
228 if (!ret)
229 ret = dm_i2c_write(dev, i, p,
230 min((int)(sizeof(e) - i),
231 BYTES_PER_EEPROM_PAGE));
232
233 if (ret)
234 break;
235
236 udelay(EEPROM_WRITE_DELAY_MS);
237 }
238
239 if (!ret) {
240 /* Verify the write by reading back the EEPROM and comparing */
241 struct sifive_eeprom e2;
242 struct udevice *dev;
243
244 ret = i2c_get_chip_for_busnum(CONFIG_SYS_EEPROM_BUS_NUM,
245 CONFIG_SYS_I2C_EEPROM_ADDR,
246 CONFIG_SYS_I2C_EEPROM_ADDR_LEN,
247 &dev);
248 if (!ret)
249 ret = dm_i2c_read(dev, 0, (void *)&e2, sizeof(e2));
250 if (!ret && memcmp(&e, &e2, sizeof(e)))
251 ret = -1;
252 }
253
254 if (ret) {
255 printf("Programming failed.\n");
256 has_been_read = 0;
257 return -1;
258 }
259
260 printf("Programming passed.\n");
261 return 0;
262}
263
264/**
265 * set_mac_address() - stores a MAC address into the local EEPROM copy
266 *
267 * This function takes a pointer to MAC address string
268 * (i.e."XX:XX:XX:XX:XX:XX", where "XX" is a two-digit hex number),
269 * stores it in the MAC address field of the EEPROM local copy, and
270 * updates the local copy of the CRC.
271 */
272static void set_mac_address(char *string)
273{
274 unsigned int i;
275
276 if (strncasecmp(SIFIVE_OUI_PREFIX, string, 13)) {
277 printf("The MAC address doesn't match SiFive OUI %s\n",
278 SIFIVE_OUI_PREFIX);
279 return;
280 }
281
282 for (i = 0; *string && (i < MAC_ADDR_BYTES); i++) {
Simon Glass3ff49ec2021-07-24 09:03:29 -0600283 e.mac_addr[i] = hextoul(string, &string);
Zong Lib99fc942021-06-30 23:23:45 +0800284 if (*string == ':')
285 string++;
286 }
287
288 update_crc();
289}
290
291/**
292 * set_manuf_test_status() - stores a test status byte into the in-memory copy
293 *
294 * Takes a pointer to a manufacturing test status string ("unknown",
295 * "pass", "fail") and stores the corresponding numeric ID to the
296 * manuf_test_status field of the EEPROM local copy, and updates the
297 * CRC of the local copy.
298 */
299static void set_manuf_test_status(char *string)
300{
301 if (!strcasecmp(string, "unknown")) {
302 e.manuf_test_status = SIFIVE_MANUF_TEST_STATUS_UNKNOWN;
303 } else if (!strcasecmp(string, "pass")) {
304 e.manuf_test_status = SIFIVE_MANUF_TEST_STATUS_PASS;
305 } else if (!strcasecmp(string, "fail")) {
306 e.manuf_test_status = SIFIVE_MANUF_TEST_STATUS_FAIL;
307 } else {
308 printf("Usage: mac manuf_test_status (unknown|pass|fail)\n");
309 return;
310 }
311
312 update_crc();
313}
314
315/**
316 * set_pcb_revision() - stores a SiFive PCB revision into the local EEPROM copy
317 *
318 * Takes a pointer to a string representing the numeric PCB revision in
319 * decimal ("0" - "255"), stores it in the pcb_revision field of the
320 * EEPROM local copy, and updates the CRC of the local copy.
321 */
322static void set_pcb_revision(char *string)
323{
324 unsigned long p;
325
Simon Glassff9b9032021-07-24 09:03:30 -0600326 p = dectoul(string, &string);
Zong Lib99fc942021-06-30 23:23:45 +0800327 if (p > U8_MAX) {
328 printf("%s must not be greater than %d\n", "PCB revision",
329 U8_MAX);
330 return;
331 }
332
333 e.pcb_revision = p;
334
335 update_crc();
336}
337
338/**
339 * set_bom_revision() - stores a SiFive BOM revision into the local EEPROM copy
340 *
341 * Takes a pointer to a uppercase ASCII character representing the BOM
342 * revision ("A" - "Z"), stores it in the bom_revision field of the
343 * EEPROM local copy, and updates the CRC of the local copy.
344 */
345static void set_bom_revision(char *string)
346{
347 if (string[0] < 'A' || string[0] > 'Z') {
348 printf("BOM revision must be an uppercase letter between A and Z\n");
349 return;
350 }
351
352 e.bom_revision = string[0];
353
354 update_crc();
355}
356
357/**
358 * set_bom_variant() - stores a SiFive BOM variant into the local EEPROM copy
359 *
360 * Takes a pointer to a string representing the numeric BOM variant in
361 * decimal ("0" - "255"), stores it in the bom_variant field of the
362 * EEPROM local copy, and updates the CRC of the local copy.
363 */
364static void set_bom_variant(char *string)
365{
366 unsigned long p;
367
Simon Glassff9b9032021-07-24 09:03:30 -0600368 p = dectoul(string, &string);
Zong Lib99fc942021-06-30 23:23:45 +0800369 if (p > U8_MAX) {
370 printf("%s must not be greater than %d\n", "BOM variant",
371 U8_MAX);
372 return;
373 }
374
375 e.bom_variant = p;
376
377 update_crc();
378}
379
380/**
381 * set_product_id() - stores a SiFive product ID into the local EEPROM copy
382 *
383 * Takes a pointer to a string representing the numeric product ID in
384 * decimal ("0" - "65535"), stores it in the product ID field of the
385 * EEPROM local copy, and updates the CRC of the local copy.
386 */
387static void set_product_id(char *string)
388{
389 unsigned long p;
390
Simon Glassff9b9032021-07-24 09:03:30 -0600391 p = dectoul(string, &string);
Zong Lib99fc942021-06-30 23:23:45 +0800392 if (p > U16_MAX) {
393 printf("%s must not be greater than %d\n", "Product ID",
394 U16_MAX);
395 return;
396 }
397
398 e.product_id = p;
399
400 update_crc();
401}
402
403/**
Zong Lib99fc942021-06-30 23:23:45 +0800404 * init_local_copy() - initialize the in-memory EEPROM copy
405 *
406 * Initialize the in-memory EEPROM copy with the magic number. Must
407 * be done when preparing to initialize a blank EEPROM, or overwrite
408 * one with a corrupted magic number.
409 */
410static void init_local_copy(void)
411{
412 memset(&e, 0, sizeof(e));
413 memcpy(e.magic, magic, sizeof(e.magic));
414 e.format_ver = FORMAT_VERSION;
415 update_crc();
416}
417
418int do_mac(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
419{
420 char *cmd;
421
422 if (argc == 1) {
423 show_eeprom();
424 return 0;
425 }
426
427 if (argc > 3)
Heinrich Schuchardtb125efc2023-09-30 14:01:44 +0200428 return CMD_RET_USAGE;
Zong Lib99fc942021-06-30 23:23:45 +0800429
430 cmd = argv[1];
431
432 /* Commands with no argument */
433 if (!strcmp(cmd, "read_eeprom")) {
434 read_eeprom();
435 return 0;
436 } else if (!strcmp(cmd, "initialize")) {
437 init_local_copy();
438 return 0;
439 } else if (!strcmp(cmd, "write_eeprom")) {
440 prog_eeprom();
441 return 0;
442 }
443
444 if (argc != 3)
Heinrich Schuchardtb125efc2023-09-30 14:01:44 +0200445 return CMD_RET_USAGE;
Zong Lib99fc942021-06-30 23:23:45 +0800446
447 if (!is_match_magic()) {
448 printf("Please read the EEPROM ('read_eeprom') and/or initialize the EEPROM ('initialize') first.\n");
449 return 0;
450 }
451
Zong Li54010a72021-07-09 16:26:35 +0800452 if (!strcmp(cmd, "manuf_test_status")) {
Zong Lib99fc942021-06-30 23:23:45 +0800453 set_manuf_test_status(argv[2]);
454 return 0;
455 } else if (!strcmp(cmd, "mac_address")) {
456 set_mac_address(argv[2]);
457 return 0;
458 } else if (!strcmp(cmd, "pcb_revision")) {
459 set_pcb_revision(argv[2]);
460 return 0;
461 } else if (!strcmp(cmd, "bom_variant")) {
462 set_bom_variant(argv[2]);
463 return 0;
464 } else if (!strcmp(cmd, "bom_revision")) {
465 set_bom_revision(argv[2]);
466 return 0;
467 } else if (!strcmp(cmd, "product_id")) {
468 set_product_id(argv[2]);
469 return 0;
470 }
471
Heinrich Schuchardtb125efc2023-09-30 14:01:44 +0200472 return CMD_RET_USAGE;
Zong Lib99fc942021-06-30 23:23:45 +0800473}
474
475/**
476 * mac_read_from_eeprom() - read the MAC address from EEPROM
477 *
478 * This function reads the MAC address from EEPROM and sets the
479 * appropriate environment variables for each one read.
480 *
481 * The environment variables are only set if they haven't been set already.
482 * This ensures that any user-saved variables are never overwritten.
483 *
484 * This function must be called after relocation.
485 */
486int mac_read_from_eeprom(void)
487{
488 u32 crc;
489 char board_serial[SERIAL_NUMBER_BYTES + 1] = { 0 };
490
491 puts("EEPROM: ");
492
493 if (read_eeprom()) {
494 printf("Read failed.\n");
495 return 0;
496 }
497
498 if (!is_match_magic()) {
499 printf("Invalid ID (%02x %02x %02x %02x)\n",
500 e.magic[0], e.magic[1], e.magic[2], e.magic[3]);
501 dump_raw_eeprom();
502 return 0;
503 }
504
505 crc = calculate_crc32();
506 if (crc != e.crc) {
507 printf("CRC mismatch (%08x != %08x)\n", crc, e.crc);
508 dump_raw_eeprom();
509 return 0;
510 }
511
512 eth_env_set_enetaddr("ethaddr", e.mac_addr);
513
514 if (!env_get("serial#")) {
515 snprintf(board_serial, sizeof(board_serial), "%s", e.serial);
516 env_set("serial#", board_serial);
517 }
518
519 return 0;
520}
Zong Lidab3e8e2021-06-30 23:23:48 +0800521
522/**
523 * get_pcb_revision_from_eeprom - get the PCB revision
524 *
525 * Read the EEPROM to determine the board revision.
526 *
527 * This function is called before relocation, so we need to read a private
528 * copy of the EEPROM into a local variable on the stack.
529 */
530u8 get_pcb_revision_from_eeprom(void)
531{
532 struct __attribute__ ((__packed__)) board_eeprom {
533 u8 magic[MAGIC_NUMBER_BYTES];
534 u8 format_ver;
535 u16 product_id;
536 u8 pcb_revision;
537 } be;
538
539 int ret;
540 struct udevice *dev;
541
542 ret = i2c_get_chip_for_busnum(CONFIG_SYS_EEPROM_BUS_NUM,
543 CONFIG_SYS_I2C_EEPROM_ADDR,
544 1,
545 &dev);
546
547 if (!ret)
548 dm_i2c_read(dev, 0, (void *)&be,
549 sizeof(struct board_eeprom));
550
551 return be.pcb_revision;
552}
Heinrich Schuchardtb125efc2023-09-30 14:01:44 +0200553
Tom Rini03f146c2023-10-07 15:13:08 -0400554U_BOOT_LONGHELP(mac,
Heinrich Schuchardtb125efc2023-09-30 14:01:44 +0200555 "- displays memory copy of EEPROM\n"
556 "mac read_eeprom - reads EEPROM into memory\n"
557 "mac initialize - initializes memory copy with magic number\n"
558 "mac write_eeprom - writes the EEPROM from memory\n"
559 "mac manuf_test_status [unknown|pass|fail] - sets test status in memory\n"
560 "mac_address <addr> - sets MAC address in memory\n"
561 "mac pcb_revision <rev> - sets PCB revision in memory\n"
562 "mac bom_variant <var> - sets BOM variant in memory\n"
Tom Rini03f146c2023-10-07 15:13:08 -0400563 "mac bom_revision <rev> - sets BOM revision in memory\n");
Heinrich Schuchardtb125efc2023-09-30 14:01:44 +0200564
565U_BOOT_CMD(
566 mac, 3, 1, do_mac,
567 "display and program the board revision and MAC address in EEPROM",
Tom Rini03f146c2023-10-07 15:13:08 -0400568 mac_help_text);