uboot/board/sifive/unmatched/hifive-platform-i2c-eeprom.c
<<
>>
Prefs
   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 <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++) {
 284                e.mac_addr[i] = hextoul(string, &string);
 285                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
 327        p = dectoul(string, &string);
 328        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
 369        p = dectoul(string, &string);
 370        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
 392        p = dectoul(string, &string);
 393        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/**
 405 * 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
 453        if (!strcmp(cmd, "manuf_test_status")) {
 454                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}
 522
 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}
 554