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