1 From 3b4e1fd666cc156bf6d56da60d62dc063f75b7c4 Mon Sep 17 00:00:00 2001
2 From: Zong Li <zong.li@sifive.com>
3 Date: Wed, 30 Jun 2021 23:23:45 +0800
4 Subject: [PATCH 03/16] board: sifive: unmatched: add initial support for a
7 Add initial support for the PCB description EEPROM for SiFive HiFive
10 This implementation is refactored based on Paul Walmsley's porting and
11 adopt the suggestions from David Abdurachmanov.
13 Signed-off-by: Paul Walmsley <paul.walmsley@sifive.com>
14 Signed-off-by: David Abdurachmanov <david.abdurachmanov@sifive.com>
15 Signed-off-by: Zong Li <zong.li@sifive.com>
16 Reviewed-by: Leo Yu-Chi Liang <ycliang@andestech.com>
18 board/sifive/unmatched/Makefile | 1 +
19 .../sifive/unmatched/hifive-platform-i2c-eeprom.c | 542 +++++++++++++++++++++
20 include/configs/sifive-unmatched.h | 6 +
21 3 files changed, 549 insertions(+)
22 create mode 100644 board/sifive/unmatched/hifive-platform-i2c-eeprom.c
24 diff --git a/board/sifive/unmatched/Makefile b/board/sifive/unmatched/Makefile
25 index 6308c80..e00b330 100644
26 --- a/board/sifive/unmatched/Makefile
27 +++ b/board/sifive/unmatched/Makefile
29 # Copyright (c) 2020-2021 SiFive, Inc
32 +obj-$(CONFIG_ID_EEPROM) += hifive-platform-i2c-eeprom.o
34 ifdef CONFIG_SPL_BUILD
36 diff --git a/board/sifive/unmatched/hifive-platform-i2c-eeprom.c b/board/sifive/unmatched/hifive-platform-i2c-eeprom.c
38 index 0000000..9a62d32
40 +++ b/board/sifive/unmatched/hifive-platform-i2c-eeprom.c
42 +// SPDX-License-Identifier: GPL-2.0+
44 + * Copyright (C) 2020 SiFive, Inc.
46 + * Based on board/freescale/common/sys_eeprom.c:
47 + * Copyright 2006, 2008-2009, 2011 Freescale Semiconductor
48 + * York Sun (yorksun@freescale.com)
49 + * Haiying Wang (haiying.wang@freescale.com)
50 + * Timur Tabi (timur@freescale.com)
58 +#include <linux/ctype.h>
59 +#include <linux/delay.h>
60 +#include <u-boot/crc.h>
62 +#ifndef CONFIG_SYS_EEPROM_BUS_NUM
63 +#error Requires CONFIG_SYS_EEPROM_BUS_NUM to be defined
66 +#define FORMAT_VERSION 0x1
68 +/* Options for the manuf_test_status field */
69 +#define SIFIVE_MANUF_TEST_STATUS_UNKNOWN 0
70 +#define SIFIVE_MANUF_TEST_STATUS_PASS 1
71 +#define SIFIVE_MANUF_TEST_STATUS_FAIL 2
74 + * BYTES_PER_EEPROM_PAGE: the AT24C02 datasheet says that data can
75 + * only be written in page mode, which means 8 bytes at a time
77 +#define BYTES_PER_EEPROM_PAGE 8
80 + * EEPROM_WRITE_DELAY_MS: the AT24C02 datasheet says it takes up to
81 + * 5ms to complete a given write
83 +#define EEPROM_WRITE_DELAY_MS 5000
86 + * MAGIC_NUMBER_BYTES: number of bytes used by the magic number
88 +#define MAGIC_NUMBER_BYTES 4
91 + * SERIAL_NUMBER_BYTES: number of bytes used by the board serial
94 +#define SERIAL_NUMBER_BYTES 16
97 + * MAC_ADDR_BYTES: number of bytes used by the Ethernet MAC address
99 +#define MAC_ADDR_BYTES 6
102 + * MAC_ADDR_STRLEN: length of mac address string
104 +#define MAC_ADDR_STRLEN 17
107 + * SiFive OUI. Registration Date is 2018-02-15
109 +#define SIFIVE_OUI_PREFIX "70:B3:D5:92:F"
112 + * static eeprom: EEPROM layout for the SiFive platform I2C format
114 +static struct __attribute__ ((__packed__)) sifive_eeprom {
115 + u8 magic[MAGIC_NUMBER_BYTES];
121 + u8 serial[SERIAL_NUMBER_BYTES];
122 + u8 manuf_test_status;
123 + u8 mac_addr[MAC_ADDR_BYTES];
127 +struct sifive_product {
132 +/* Set to 1 if we've read EEPROM into memory */
133 +static int has_been_read;
135 +/* Magic number at the first four bytes of EEPROM */
136 +static const unsigned char magic[MAGIC_NUMBER_BYTES] = { 0xf1, 0x5e, 0x50, 0x45 };
138 +/* Does the magic number match that of a SiFive EEPROM? */
139 +static inline int is_match_magic(void)
141 + return (memcmp(&e.magic, &magic, MAGIC_NUMBER_BYTES) == 0);
144 +/* Calculate the current CRC */
145 +static inline u32 calculate_crc32(void)
147 + return crc32(0, (void *)&e, sizeof(struct sifive_eeprom) - sizeof(e.crc));
150 +/* This function should be called after each update to the EEPROM structure */
151 +static inline void update_crc(void)
153 + e.crc = calculate_crc32();
156 +static struct sifive_product sifive_products[] = {
158 + { 2, "HiFive Unmatched" },
162 + * dump_raw_eeprom - display the raw contents of the EEPROM
164 +static void dump_raw_eeprom(void)
168 + printf("EEPROM dump: (0x%lx bytes)\n", sizeof(e));
169 + for (i = 0; i < sizeof(e); i++) {
171 + printf("%02X: ", i);
172 + printf("%02X ", ((u8 *)&e)[i]);
173 + if (((i % 16) == 15) || (i == sizeof(e) - 1))
179 + * show_eeprom - display the contents of the EEPROM
181 +static void show_eeprom(void)
185 + const char *product_name = "Unknown";
186 + char board_serial[SERIAL_NUMBER_BYTES + 1] = { 0 };
188 + if (!is_match_magic()) {
189 + printf("Not a SiFive HiFive EEPROM data format - magic bytes don't match\n");
194 + snprintf(board_serial, sizeof(board_serial), "%s", e.serial);
196 + for (i = 0; i < ARRAY_SIZE(sifive_products); i++) {
197 + if (sifive_products[i].id == e.product_id) {
198 + product_name = sifive_products[i].name;
203 + printf("SiFive PCB EEPROM format v%u\n", e.format_ver);
204 + printf("Product ID: %04hx (%s)\n", e.product_id, product_name);
205 + printf("PCB revision: %x\n", e.pcb_revision);
206 + printf("BOM revision: %c\n", e.bom_revision);
207 + printf("BOM variant: %x\n", e.bom_variant);
208 + printf("Serial number: %s\n", board_serial);
209 + printf("Ethernet MAC address: %02x:%02x:%02x:%02x:%02x:%02x\n",
210 + e.mac_addr[0], e.mac_addr[1], e.mac_addr[2],
211 + e.mac_addr[3], e.mac_addr[4], e.mac_addr[5]);
213 + crc = calculate_crc32();
214 + if (crc == e.crc) {
215 + printf("CRC: %08x\n", e.crc);
217 + printf("CRC: %08x (should be %08x)\n", e.crc, crc);
223 + * read_eeprom() - read the EEPROM into memory, if it hasn't been read already
225 +static int read_eeprom(void)
228 + struct udevice *dev;
233 + ret = i2c_get_chip_for_busnum(CONFIG_SYS_EEPROM_BUS_NUM,
234 + CONFIG_SYS_I2C_EEPROM_ADDR,
238 + dm_i2c_read(dev, 0, (void *)&e,
239 + sizeof(struct sifive_eeprom));
243 + has_been_read = (ret == 0) ? 1 : 0;
249 + * prog_eeprom() - write the EEPROM from memory
251 +static int prog_eeprom(void)
257 + if (!is_match_magic()) {
258 + printf("Please read the EEPROM ('read_eeprom') and/or initialize the EEPROM ('initialize') first.\n");
262 + for (i = 0, p = &e; i < sizeof(e);
263 + i += BYTES_PER_EEPROM_PAGE, p += BYTES_PER_EEPROM_PAGE) {
264 + struct udevice *dev;
266 + ret = i2c_get_chip_for_busnum(CONFIG_SYS_EEPROM_BUS_NUM,
267 + CONFIG_SYS_I2C_EEPROM_ADDR,
268 + CONFIG_SYS_I2C_EEPROM_ADDR_LEN,
271 + ret = dm_i2c_write(dev, i, p,
272 + min((int)(sizeof(e) - i),
273 + BYTES_PER_EEPROM_PAGE));
278 + udelay(EEPROM_WRITE_DELAY_MS);
282 + /* Verify the write by reading back the EEPROM and comparing */
283 + struct sifive_eeprom e2;
284 + struct udevice *dev;
286 + ret = i2c_get_chip_for_busnum(CONFIG_SYS_EEPROM_BUS_NUM,
287 + CONFIG_SYS_I2C_EEPROM_ADDR,
288 + CONFIG_SYS_I2C_EEPROM_ADDR_LEN,
291 + ret = dm_i2c_read(dev, 0, (void *)&e2, sizeof(e2));
292 + if (!ret && memcmp(&e, &e2, sizeof(e)))
297 + printf("Programming failed.\n");
302 + printf("Programming passed.\n");
307 + * set_mac_address() - stores a MAC address into the local EEPROM copy
309 + * This function takes a pointer to MAC address string
310 + * (i.e."XX:XX:XX:XX:XX:XX", where "XX" is a two-digit hex number),
311 + * stores it in the MAC address field of the EEPROM local copy, and
312 + * updates the local copy of the CRC.
314 +static void set_mac_address(char *string)
318 + if (strncasecmp(SIFIVE_OUI_PREFIX, string, 13)) {
319 + printf("The MAC address doesn't match SiFive OUI %s\n",
320 + SIFIVE_OUI_PREFIX);
324 + for (i = 0; *string && (i < MAC_ADDR_BYTES); i++) {
325 + e.mac_addr[i] = simple_strtoul(string, &string, 16);
326 + if (*string == ':')
334 + * set_manuf_test_status() - stores a test status byte into the in-memory copy
336 + * Takes a pointer to a manufacturing test status string ("unknown",
337 + * "pass", "fail") and stores the corresponding numeric ID to the
338 + * manuf_test_status field of the EEPROM local copy, and updates the
339 + * CRC of the local copy.
341 +static void set_manuf_test_status(char *string)
343 + if (!strcasecmp(string, "unknown")) {
344 + e.manuf_test_status = SIFIVE_MANUF_TEST_STATUS_UNKNOWN;
345 + } else if (!strcasecmp(string, "pass")) {
346 + e.manuf_test_status = SIFIVE_MANUF_TEST_STATUS_PASS;
347 + } else if (!strcasecmp(string, "fail")) {
348 + e.manuf_test_status = SIFIVE_MANUF_TEST_STATUS_FAIL;
350 + printf("Usage: mac manuf_test_status (unknown|pass|fail)\n");
358 + * set_pcb_revision() - stores a SiFive PCB revision into the local EEPROM copy
360 + * Takes a pointer to a string representing the numeric PCB revision in
361 + * decimal ("0" - "255"), stores it in the pcb_revision field of the
362 + * EEPROM local copy, and updates the CRC of the local copy.
364 +static void set_pcb_revision(char *string)
368 + p = simple_strtoul(string, &string, 10);
370 + printf("%s must not be greater than %d\n", "PCB revision",
375 + e.pcb_revision = p;
381 + * set_bom_revision() - stores a SiFive BOM revision into the local EEPROM copy
383 + * Takes a pointer to a uppercase ASCII character representing the BOM
384 + * revision ("A" - "Z"), stores it in the bom_revision field of the
385 + * EEPROM local copy, and updates the CRC of the local copy.
387 +static void set_bom_revision(char *string)
389 + if (string[0] < 'A' || string[0] > 'Z') {
390 + printf("BOM revision must be an uppercase letter between A and Z\n");
394 + e.bom_revision = string[0];
400 + * set_bom_variant() - stores a SiFive BOM variant into the local EEPROM copy
402 + * Takes a pointer to a string representing the numeric BOM variant in
403 + * decimal ("0" - "255"), stores it in the bom_variant field of the
404 + * EEPROM local copy, and updates the CRC of the local copy.
406 +static void set_bom_variant(char *string)
410 + p = simple_strtoul(string, &string, 10);
412 + printf("%s must not be greater than %d\n", "BOM variant",
423 + * set_product_id() - stores a SiFive product ID into the local EEPROM copy
425 + * Takes a pointer to a string representing the numeric product ID in
426 + * decimal ("0" - "65535"), stores it in the product ID field of the
427 + * EEPROM local copy, and updates the CRC of the local copy.
429 +static void set_product_id(char *string)
433 + p = simple_strtoul(string, &string, 10);
435 + printf("%s must not be greater than %d\n", "Product ID",
446 + * set_serial_number() - set the PCB serial number in the in-memory copy
448 + * Set the board serial number in the in-memory EEPROM copy from the supplied
449 + * string argument, and update the CRC.
451 +static void set_serial_number(char *string)
453 + if (strlen(string) > SERIAL_NUMBER_BYTES) {
454 + printf("Serial number must not be greater than 16 bytes\n");
458 + memset(e.serial, 0, sizeof(e.serial));
459 + strncpy((char *)e.serial, string, sizeof(e.serial));
464 + * init_local_copy() - initialize the in-memory EEPROM copy
466 + * Initialize the in-memory EEPROM copy with the magic number. Must
467 + * be done when preparing to initialize a blank EEPROM, or overwrite
468 + * one with a corrupted magic number.
470 +static void init_local_copy(void)
472 + memset(&e, 0, sizeof(e));
473 + memcpy(e.magic, magic, sizeof(e.magic));
474 + e.format_ver = FORMAT_VERSION;
478 +int do_mac(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
488 + return cmd_usage(cmdtp);
492 + /* Commands with no argument */
493 + if (!strcmp(cmd, "read_eeprom")) {
496 + } else if (!strcmp(cmd, "initialize")) {
499 + } else if (!strcmp(cmd, "write_eeprom")) {
505 + return cmd_usage(cmdtp);
507 + if (!is_match_magic()) {
508 + printf("Please read the EEPROM ('read_eeprom') and/or initialize the EEPROM ('initialize') first.\n");
512 + if (!strcmp(cmd, "serial_number")) {
513 + set_serial_number(argv[2]);
515 + } else if (!strcmp(cmd, "manuf_test_status")) {
516 + set_manuf_test_status(argv[2]);
518 + } else if (!strcmp(cmd, "mac_address")) {
519 + set_mac_address(argv[2]);
521 + } else if (!strcmp(cmd, "pcb_revision")) {
522 + set_pcb_revision(argv[2]);
524 + } else if (!strcmp(cmd, "bom_variant")) {
525 + set_bom_variant(argv[2]);
527 + } else if (!strcmp(cmd, "bom_revision")) {
528 + set_bom_revision(argv[2]);
530 + } else if (!strcmp(cmd, "product_id")) {
531 + set_product_id(argv[2]);
535 + return cmd_usage(cmdtp);
539 + * mac_read_from_eeprom() - read the MAC address from EEPROM
541 + * This function reads the MAC address from EEPROM and sets the
542 + * appropriate environment variables for each one read.
544 + * The environment variables are only set if they haven't been set already.
545 + * This ensures that any user-saved variables are never overwritten.
547 + * This function must be called after relocation.
549 +int mac_read_from_eeprom(void)
552 + char board_serial[SERIAL_NUMBER_BYTES + 1] = { 0 };
556 + if (read_eeprom()) {
557 + printf("Read failed.\n");
561 + if (!is_match_magic()) {
562 + printf("Invalid ID (%02x %02x %02x %02x)\n",
563 + e.magic[0], e.magic[1], e.magic[2], e.magic[3]);
568 + crc = calculate_crc32();
569 + if (crc != e.crc) {
570 + printf("CRC mismatch (%08x != %08x)\n", crc, e.crc);
575 + eth_env_set_enetaddr("ethaddr", e.mac_addr);
577 + if (!env_get("serial#")) {
578 + snprintf(board_serial, sizeof(board_serial), "%s", e.serial);
579 + env_set("serial#", board_serial);
584 diff --git a/include/configs/sifive-unmatched.h b/include/configs/sifive-unmatched.h
585 index 4fad69b..9e1859c 100644
586 --- a/include/configs/sifive-unmatched.h
587 +++ b/include/configs/sifive-unmatched.h
589 "fdt addr ${fdtcontroladdr};"
590 #endif /* CONFIG_SPL_BUILD */
592 +#define CONFIG_SYS_EEPROM_BUS_NUM 0
593 +#define CONFIG_SYS_I2C_EEPROM_ADDR 0x54
594 +#define CONFIG_SYS_I2C_EEPROM_ADDR_LEN 0x1
596 +#define CONFIG_ID_EEPROM
598 #endif /* __SIFIVE_UNMATCHED_H */