1// 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 <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++) { 283 e.mac_addr[i] = hextoul(string, &string); 284 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 326 p = dectoul(string, &string); 327 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 368 p = dectoul(string, &string); 369 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 391 p = dectoul(string, &string); 392 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/** 404 * 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) 428 return CMD_RET_USAGE; 429 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) 445 return CMD_RET_USAGE; 446 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 452 if (!strcmp(cmd, "manuf_test_status")) { 453 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 472 return CMD_RET_USAGE; 473} 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} 521 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} 553 554U_BOOT_LONGHELP(mac, 555 "- 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" 563 "mac bom_revision <rev> - sets BOM revision in memory\n"); 564 565U_BOOT_CMD( 566 mac, 3, 1, do_mac, 567 "display and program the board revision and MAC address in EEPROM", 568 mac_help_text); 569