The eeprom command includes functions for reading and writing
tlv-formatted data from an eeprom, as well as an implementation of the
cli command tlv_eeprom.

Split off the parsing, read and write into a standalone tlv library.

Signed-off-by: Josua Mayer <jo...@solid-run.com>
---
 cmd/Kconfig          |   2 +
 cmd/tlv_eeprom.c     | 742 +-----------------------------------------
 lib/Kconfig          |   2 +
 lib/Makefile         |   2 +
 lib/tlv/Kconfig      |  15 +
 lib/tlv/Makefile     |   5 +
 lib/tlv/tlv_eeprom.c | 750 +++++++++++++++++++++++++++++++++++++++++++
 7 files changed, 786 insertions(+), 732 deletions(-)
 create mode 100644 lib/tlv/Kconfig
 create mode 100644 lib/tlv/Makefile
 create mode 100644 lib/tlv/tlv_eeprom.c

diff --git a/cmd/Kconfig b/cmd/Kconfig
index 2b575a2b42..821b5e9d6b 100644
--- a/cmd/Kconfig
+++ b/cmd/Kconfig
@@ -166,6 +166,7 @@ config CMD_REGINFO
 
 config CMD_TLV_EEPROM
        bool "tlv_eeprom"
+       select EEPROM_TLV_LIB
        depends on I2C_EEPROM
        help
          Display and program the system EEPROM data block in ONIE Tlvinfo
@@ -173,6 +174,7 @@ config CMD_TLV_EEPROM
 
 config SPL_CMD_TLV_EEPROM
        bool "tlv_eeprom for SPL"
+       select SPL_EEPROM_TLV_LIB
        depends on SPL_I2C_EEPROM
        select SPL_DRIVERS_MISC
        help
diff --git a/cmd/tlv_eeprom.c b/cmd/tlv_eeprom.c
index c66116b2c4..99b79cad8b 100644
--- a/cmd/tlv_eeprom.c
+++ b/cmd/tlv_eeprom.c
@@ -10,131 +10,26 @@
  * Copyright (C) 2022 Josua Mayer <jo...@solid-run.com>
  */
 
-#include <common.h>
 #include <command.h>
-#include <dm.h>
-#include <i2c.h>
-#include <i2c_eeprom.h>
-#include <env.h>
-#include <init.h>
-#include <net.h>
-#include <asm/global_data.h>
-#include <linux/ctype.h>
-#include <u-boot/crc.h>
-
-#include "tlv_eeprom.h"
-
-DECLARE_GLOBAL_DATA_PTR;
+#include <linux/kernel.h>
+#include <stdio.h>
+#include <tlv_eeprom.h>
+#include <vsprintf.h>
 
 /* File scope function prototypes */
-static int read_eeprom(int devnum, u8 *eeprom);
 static void show_eeprom(int devnum, u8 *eeprom);
-static void decode_tlv(struct tlvinfo_tlv *tlv);
-static int set_mac(char *buf, const char *string);
-static int set_date(char *buf, const char *string);
-static int set_bytes(char *buf, const char *string, int *converted_accum);
 static void show_tlv_devices(int current_dev);
+static inline const char *tlv_type2name(u8 type);
+static void decode_tlv(struct tlvinfo_tlv *tlv);
+static int do_tlv_eeprom(struct cmd_tbl *cmdtp, int flag, int argc, char 
*const argv[]);
+static void show_tlv_code_list(void);
 
 /* The EERPOM contents after being read into memory */
 static u8 eeprom[TLV_INFO_MAX_LEN];
 
-static struct udevice *tlv_devices[TLV_MAX_DEVICES];
-
 #define to_header(p) ((struct tlvinfo_header *)p)
 #define to_entry(p) ((struct tlvinfo_tlv *)p)
 
-/**
- * Check whether eeprom device exists.
- */
-bool exists_tlv_eeprom(int dev)
-{
-       return dev < TLV_MAX_DEVICES && tlv_devices[dev] != 0;
-}
-
-static inline bool is_digit(char c)
-{
-       return (c >= '0' && c <= '9');
-}
-
-/**
- *  is_hex
- *
- *  Tests if character is an ASCII hex digit
- */
-static inline u8 is_hex(char p)
-{
-       return (((p >= '0') && (p <= '9')) ||
-               ((p >= 'A') && (p <= 'F')) ||
-               ((p >= 'a') && (p <= 'f')));
-}
-
-/**
- *  Validate the checksum in the provided TlvInfo EEPROM data. First,
- *  verify that the TlvInfo header is valid, then make sure the last
- *  TLV is a CRC-32 TLV. Then calculate the CRC over the EEPROM data
- *  and compare it to the value stored in the EEPROM CRC-32 TLV.
- */
-bool tlvinfo_check_crc(u8 *eeprom)
-{
-       struct tlvinfo_header *eeprom_hdr = to_header(eeprom);
-       struct tlvinfo_tlv    *eeprom_crc;
-       unsigned int       calc_crc;
-       unsigned int       stored_crc;
-
-       // Is the eeprom header valid?
-       if (!is_valid_tlvinfo_header(eeprom_hdr))
-               return false;
-
-       // Is the last TLV a CRC?
-       eeprom_crc = to_entry(&eeprom[TLV_INFO_HEADER_SIZE +
-               be16_to_cpu(eeprom_hdr->totallen) - (TLV_INFO_ENTRY_SIZE + 4)]);
-       if (eeprom_crc->type != TLV_CODE_CRC_32 || eeprom_crc->length != 4)
-               return false;
-
-       // Calculate the checksum
-       calc_crc = crc32(0, (void *)eeprom,
-                        TLV_INFO_HEADER_SIZE + 
be16_to_cpu(eeprom_hdr->totallen) - 4);
-       stored_crc = (eeprom_crc->value[0] << 24) |
-               (eeprom_crc->value[1] << 16) |
-               (eeprom_crc->value[2] <<  8) |
-               eeprom_crc->value[3];
-       return calc_crc == stored_crc;
-}
-
-/**
- *  read_eeprom
- *
- *  Read the EEPROM into memory, if it hasn't already been read.
- */
-static int read_eeprom(int devnum, u8 *eeprom)
-{
-       int ret;
-       struct tlvinfo_header *eeprom_hdr = to_header(eeprom);
-       struct tlvinfo_tlv *eeprom_tlv = 
to_entry(&eeprom[TLV_INFO_HEADER_SIZE]);
-
-       /* Read the header */
-       ret = read_tlv_eeprom((void *)eeprom_hdr, 0, TLV_INFO_HEADER_SIZE, 
devnum);
-       /* If the header was successfully read, read the TLVs */
-       if (ret == 0 && is_valid_tlvinfo_header(eeprom_hdr))
-               ret = read_tlv_eeprom((void *)eeprom_tlv, TLV_INFO_HEADER_SIZE,
-                                     be16_to_cpu(eeprom_hdr->totallen), 
devnum);
-
-       // If the contents are invalid, start over with default contents
-       if (!is_valid_tlvinfo_header(eeprom_hdr) ||
-           !tlvinfo_check_crc(eeprom)) {
-               strcpy(eeprom_hdr->signature, TLV_INFO_ID_STRING);
-               eeprom_hdr->version = TLV_INFO_VERSION;
-               eeprom_hdr->totallen = cpu_to_be16(0);
-               tlvinfo_update_crc(eeprom);
-       }
-
-#ifdef DEBUG
-       show_eeprom(devnum, eeprom);
-#endif
-
-       return ret;
-}
-
 /**
  *  show_eeprom
  *
@@ -323,70 +218,10 @@ static void decode_tlv(struct tlvinfo_tlv *tlv)
        printf("%-20s 0x%02X %3d %s\n", name, tlv->type, tlv->length, value);
 }
 
-/**
- *  tlvinfo_update_crc
- *
- *  This function updates the CRC-32 TLV. If there is no CRC-32 TLV, then
- *  one is added. This function should be called after each update to the
- *  EEPROM structure, to make sure the CRC is always correct.
- */
-void tlvinfo_update_crc(u8 *eeprom)
-{
-       struct tlvinfo_header *eeprom_hdr = to_header(eeprom);
-       struct tlvinfo_tlv    *eeprom_crc;
-       unsigned int      calc_crc;
-       int               eeprom_index;
-
-       // Discover the CRC TLV
-       if (!tlvinfo_find_tlv(eeprom, TLV_CODE_CRC_32, &eeprom_index)) {
-               unsigned int totallen = be16_to_cpu(eeprom_hdr->totallen);
-
-               if ((totallen + TLV_INFO_ENTRY_SIZE + 4) > TLV_TOTAL_LEN_MAX)
-                       return;
-               eeprom_index = TLV_INFO_HEADER_SIZE + totallen;
-               eeprom_hdr->totallen = cpu_to_be16(totallen + 
TLV_INFO_ENTRY_SIZE + 4);
-       }
-       eeprom_crc = to_entry(&eeprom[eeprom_index]);
-       eeprom_crc->type = TLV_CODE_CRC_32;
-       eeprom_crc->length = 4;
-
-       // Calculate the checksum
-       calc_crc = crc32(0, (void *)eeprom,
-                        TLV_INFO_HEADER_SIZE + 
be16_to_cpu(eeprom_hdr->totallen) - 4);
-       eeprom_crc->value[0] = (calc_crc >> 24) & 0xFF;
-       eeprom_crc->value[1] = (calc_crc >> 16) & 0xFF;
-       eeprom_crc->value[2] = (calc_crc >>  8) & 0xFF;
-       eeprom_crc->value[3] = (calc_crc >>  0) & 0xFF;
-}
-
-/**
- *  write_tlvinfo_tlv_eeprom
- *
- *  Write the TLV data from CPU memory to the hardware.
- */
-int write_tlvinfo_tlv_eeprom(void *eeprom, int dev)
-{
-       int ret = 0;
-       struct tlvinfo_header *eeprom_hdr = to_header(eeprom);
-       int eeprom_len;
-
-       tlvinfo_update_crc(eeprom);
-
-       eeprom_len = TLV_INFO_HEADER_SIZE + be16_to_cpu(eeprom_hdr->totallen);
-       ret = write_tlv_eeprom(eeprom, eeprom_len, dev);
-       if (ret) {
-               printf("Programming failed.\n");
-               return -1;
-       }
-
-       printf("Programming passed.\n");
-       return 0;
-}
-
 /**
  *  show_tlv_code_list - Display the list of TLV codes and names
  */
-void show_tlv_code_list(void)
+static void show_tlv_code_list(void)
 {
        int i;
 
@@ -404,7 +239,7 @@ void show_tlv_code_list(void)
  *
  *  This function implements the tlv_eeprom command.
  */
-int do_tlv_eeprom(struct cmd_tbl *cmdtp, int flag, int argc, char *const 
argv[])
+static int do_tlv_eeprom(struct cmd_tbl *cmdtp, int flag, int argc, char 
*const argv[])
 {
        char cmd;
        struct tlvinfo_header *eeprom_hdr = to_header(eeprom);
@@ -526,348 +361,6 @@ U_BOOT_CMD(tlv_eeprom, 4, 1,  do_tlv_eeprom,
           "    - List the understood TLV codes and names.\n"
        );
 
-/**
- *  tlvinfo_find_tlv
- *
- *  This function finds the TLV with the supplied code in the EERPOM.
- *  An offset from the beginning of the EEPROM is returned in the
- *  eeprom_index parameter if the TLV is found.
- */
-bool tlvinfo_find_tlv(u8 *eeprom, u8 tcode, int *eeprom_index)
-{
-       struct tlvinfo_header *eeprom_hdr = to_header(eeprom);
-       struct tlvinfo_tlv    *eeprom_tlv;
-       int eeprom_end;
-
-       // Search through the TLVs, looking for the first one which matches the
-       // supplied type code.
-       *eeprom_index = TLV_INFO_HEADER_SIZE;
-       eeprom_end = TLV_INFO_HEADER_SIZE + be16_to_cpu(eeprom_hdr->totallen);
-       while (*eeprom_index < eeprom_end) {
-               eeprom_tlv = to_entry(&eeprom[*eeprom_index]);
-               if (!is_valid_tlvinfo_entry(eeprom_tlv))
-                       return false;
-               if (eeprom_tlv->type == tcode)
-                       return true;
-               *eeprom_index += TLV_INFO_ENTRY_SIZE + eeprom_tlv->length;
-       }
-       return(false);
-}
-
-/**
- *  tlvinfo_delete_tlv
- *
- *  This function deletes the TLV with the specified type code from the
- *  EEPROM.
- */
-bool tlvinfo_delete_tlv(u8 *eeprom, u8 code)
-{
-       int eeprom_index;
-       int tlength;
-       struct tlvinfo_header *eeprom_hdr = to_header(eeprom);
-       struct tlvinfo_tlv *eeprom_tlv;
-
-       // Find the TLV and then move all following TLVs "forward"
-       if (tlvinfo_find_tlv(eeprom, code, &eeprom_index)) {
-               eeprom_tlv = to_entry(&eeprom[eeprom_index]);
-               tlength = TLV_INFO_ENTRY_SIZE + eeprom_tlv->length;
-               memcpy(&eeprom[eeprom_index], &eeprom[eeprom_index + tlength],
-                      TLV_INFO_HEADER_SIZE +
-                      be16_to_cpu(eeprom_hdr->totallen) - eeprom_index -
-                      tlength);
-               eeprom_hdr->totallen =
-                       cpu_to_be16(be16_to_cpu(eeprom_hdr->totallen) -
-                                   tlength);
-               tlvinfo_update_crc(eeprom);
-               return true;
-       }
-       return false;
-}
-
-/**
- *  tlvinfo_add_tlv
- *
- *  This function adds a TLV to the EEPROM, converting the value (a string) to
- *  the format in which it will be stored in the EEPROM.
- */
-#define MAX_TLV_VALUE_LEN   256
-bool tlvinfo_add_tlv(u8 *eeprom, int tcode, char *strval)
-{
-       struct tlvinfo_header *eeprom_hdr = to_header(eeprom);
-       struct tlvinfo_tlv *eeprom_tlv;
-       int new_tlv_len = 0;
-       u32 value;
-       char data[MAX_TLV_VALUE_LEN];
-       int eeprom_index;
-
-       // Encode each TLV type into the format to be stored in the EERPOM
-       switch (tcode) {
-       case TLV_CODE_PRODUCT_NAME:
-       case TLV_CODE_PART_NUMBER:
-       case TLV_CODE_SERIAL_NUMBER:
-       case TLV_CODE_LABEL_REVISION:
-       case TLV_CODE_PLATFORM_NAME:
-       case TLV_CODE_ONIE_VERSION:
-       case TLV_CODE_MANUF_NAME:
-       case TLV_CODE_MANUF_COUNTRY:
-       case TLV_CODE_VENDOR_NAME:
-       case TLV_CODE_DIAG_VERSION:
-       case TLV_CODE_SERVICE_TAG:
-               strncpy(data, strval, MAX_TLV_VALUE_LEN);
-               new_tlv_len = min_t(size_t, MAX_TLV_VALUE_LEN, strlen(strval));
-               break;
-       case TLV_CODE_DEVICE_VERSION:
-               value = simple_strtoul(strval, NULL, 0);
-               if (value >= 256) {
-                       printf("ERROR: Device version must be 255 or less. 
Value supplied: %u",
-                              value);
-                       return false;
-               }
-               data[0] = value & 0xFF;
-               new_tlv_len = 1;
-               break;
-       case TLV_CODE_MAC_SIZE:
-               value = simple_strtoul(strval, NULL, 0);
-               if (value >= 65536) {
-                       printf("ERROR: MAC Size must be 65535 or less. Value 
supplied: %u",
-                              value);
-                       return false;
-               }
-               data[0] = (value >> 8) & 0xFF;
-               data[1] = value & 0xFF;
-               new_tlv_len = 2;
-               break;
-       case TLV_CODE_MANUF_DATE:
-               if (set_date(data, strval) != 0)
-                       return false;
-               new_tlv_len = 19;
-               break;
-       case TLV_CODE_MAC_BASE:
-               if (set_mac(data, strval) != 0)
-                       return false;
-               new_tlv_len = 6;
-               break;
-       case TLV_CODE_CRC_32:
-               printf("WARNING: The CRC TLV is set automatically and cannot be 
set manually.\n");
-               return false;
-       case TLV_CODE_VENDOR_EXT:
-       default:
-               if (set_bytes(data, strval, &new_tlv_len) != 0)
-                       return false;
-               break;
-       }
-
-       // Is there room for this TLV?
-       if ((be16_to_cpu(eeprom_hdr->totallen) + TLV_INFO_ENTRY_SIZE + 
new_tlv_len) >
-                       TLV_TOTAL_LEN_MAX) {
-               printf("ERROR: There is not enough room in the EERPOM to save 
data.\n");
-               return false;
-       }
-
-       // Add TLV at the end, overwriting CRC TLV if it exists
-       if (tlvinfo_find_tlv(eeprom, TLV_CODE_CRC_32, &eeprom_index))
-               eeprom_hdr->totallen =
-                       cpu_to_be16(be16_to_cpu(eeprom_hdr->totallen) -
-                                       TLV_INFO_ENTRY_SIZE - 4);
-       else
-               eeprom_index = TLV_INFO_HEADER_SIZE + 
be16_to_cpu(eeprom_hdr->totallen);
-       eeprom_tlv = to_entry(&eeprom[eeprom_index]);
-       eeprom_tlv->type = tcode;
-       eeprom_tlv->length = new_tlv_len;
-       memcpy(eeprom_tlv->value, data, new_tlv_len);
-
-       // Update the total length and calculate (add) a new CRC-32 TLV
-       eeprom_hdr->totallen = cpu_to_be16(be16_to_cpu(eeprom_hdr->totallen) +
-                       TLV_INFO_ENTRY_SIZE + new_tlv_len);
-       tlvinfo_update_crc(eeprom);
-
-       return true;
-}
-
-/**
- *  set_mac
- *
- *  Converts a string MAC address into a binary buffer.
- *
- *  This function takes a pointer to a MAC address string
- *  (i.e."XX:XX:XX:XX:XX:XX", where "XX" is a two-digit hex number).
- *  The string format is verified and then converted to binary and
- *  stored in a buffer.
- */
-static int set_mac(char *buf, const char *string)
-{
-       char *p = (char *)string;
-       int   i;
-       int   err = 0;
-       char *end;
-
-       if (!p) {
-               printf("ERROR: NULL mac addr string passed in.\n");
-               return -1;
-       }
-
-       if (strlen(p) != 17) {
-               printf("ERROR: MAC address strlen() != 17 -- %zu\n", strlen(p));
-               printf("ERROR: Bad MAC address format: %s\n", string);
-               return -1;
-       }
-
-       for (i = 0; i < 17; i++) {
-               if ((i % 3) == 2) {
-                       if (p[i] != ':') {
-                               err++;
-                               printf("ERROR: mac: p[%i] != :, found: `%c'\n",
-                                      i, p[i]);
-                               break;
-                       }
-                       continue;
-               } else if (!is_hex(p[i])) {
-                       err++;
-                       printf("ERROR: mac: p[%i] != hex digit, found: `%c'\n",
-                              i, p[i]);
-                       break;
-               }
-       }
-
-       if (err != 0) {
-               printf("ERROR: Bad MAC address format: %s\n", string);
-               return -1;
-       }
-
-       /* Convert string to binary */
-       for (i = 0, p = (char *)string; i < 6; i++) {
-               buf[i] = p ? hextoul(p, &end) : 0;
-               if (p)
-                       p = (*end) ? end + 1 : end;
-       }
-
-       if (!is_valid_ethaddr((u8 *)buf)) {
-               printf("ERROR: MAC address must not be 00:00:00:00:00:00, a 
multicast address or FF:FF:FF:FF:FF:FF.\n");
-               printf("ERROR: Bad MAC address format: %s\n", string);
-               return -1;
-       }
-
-       return 0;
-}
-
-/**
- *  set_date
- *
- *  Validates the format of the data string
- *
- *  This function takes a pointer to a date string (i.e. MM/DD/YYYY hh:mm:ss)
- *  and validates that the format is correct. If so the string is copied
- *  to the supplied buffer.
- */
-static int set_date(char *buf, const char *string)
-{
-       int i;
-
-       if (!string) {
-               printf("ERROR: NULL date string passed in.\n");
-               return -1;
-       }
-
-       if (strlen(string) != 19) {
-               printf("ERROR: Date strlen() != 19 -- %zu\n", strlen(string));
-               printf("ERROR: Bad date format (MM/DD/YYYY hh:mm:ss): %s\n",
-                      string);
-               return -1;
-       }
-
-       for (i = 0; string[i] != 0; i++) {
-               switch (i) {
-               case 2:
-               case 5:
-                       if (string[i] != '/') {
-                               printf("ERROR: Bad date format (MM/DD/YYYY 
hh:mm:ss): %s\n",
-                                      string);
-                               return -1;
-                       }
-                       break;
-               case 10:
-                       if (string[i] != ' ') {
-                               printf("ERROR: Bad date format (MM/DD/YYYY 
hh:mm:ss): %s\n",
-                                      string);
-                               return -1;
-                       }
-                       break;
-               case 13:
-               case 16:
-                       if (string[i] != ':') {
-                               printf("ERROR: Bad date format (MM/DD/YYYY 
hh:mm:ss): %s\n",
-                                      string);
-                               return -1;
-                       }
-                       break;
-               default:
-                       if (!is_digit(string[i])) {
-                               printf("ERROR: Bad date format (MM/DD/YYYY 
hh:mm:ss): %s\n",
-                                      string);
-                               return -1;
-                       }
-                       break;
-               }
-       }
-
-       strcpy(buf, string);
-       return 0;
-}
-
-/**
- *  set_bytes
- *
- *  Converts a space-separated string of decimal numbers into a
- *  buffer of bytes.
- *
- *  This function takes a pointer to a space-separated string of decimal
- *  numbers (i.e. "128 0x55 0321") with "C" standard radix specifiers
- *  and converts them to an array of bytes.
- */
-static int set_bytes(char *buf, const char *string, int *converted_accum)
-{
-       char *p = (char *)string;
-       int   i;
-       uint  byte;
-
-       if (!p) {
-               printf("ERROR: NULL string passed in.\n");
-               return -1;
-       }
-
-       /* Convert string to bytes */
-       for (i = 0, p = (char *)string; (i < TLV_VALUE_MAX_LEN) && (*p != 0);
-                       i++) {
-               while ((*p == ' ') || (*p == '\t') || (*p == ',') ||
-                      (*p == ';')) {
-                       p++;
-               }
-               if (*p != 0) {
-                       if (!is_digit(*p)) {
-                               printf("ERROR: Non-digit found in byte string: 
(%s)\n",
-                                      string);
-                               return -1;
-                       }
-                       byte = simple_strtoul(p, &p, 0);
-                       if (byte >= 256) {
-                               printf("ERROR: The value specified is greater 
than 255: (%u) in string: %s\n",
-                                      byte, string);
-                               return -1;
-                       }
-                       buf[i] = byte & 0xFF;
-               }
-       }
-
-       if (i == TLV_VALUE_MAX_LEN && (*p != 0)) {
-               printf("ERROR: Trying to assign too many bytes (max: %d) in 
string: %s\n",
-                      TLV_VALUE_MAX_LEN, string);
-               return -1;
-       }
-
-       *converted_accum = i;
-       return 0;
-}
-
 static void show_tlv_devices(int current_dev)
 {
        unsigned int dev;
@@ -877,218 +370,3 @@ static void show_tlv_devices(int current_dev)
                        printf("TLV: %u%s\n", dev,
                               (dev == current_dev) ? " (*)" : "");
 }
-
-static int find_tlv_devices(struct udevice **tlv_devices_p)
-{
-       int ret;
-       int count_dev = 0;
-       struct udevice *dev;
-
-       for (ret = uclass_first_device_check(UCLASS_I2C_EEPROM, &dev);
-                       dev;
-                       ret = uclass_next_device_check(&dev)) {
-               if (ret == 0)
-                       tlv_devices_p[count_dev++] = dev;
-               if (count_dev >= TLV_MAX_DEVICES)
-                       break;
-       }
-
-       return (count_dev == 0) ? -ENODEV : 0;
-}
-
-static struct udevice *find_tlv_device_by_index(int dev_num)
-{
-       struct udevice *local_tlv_devices[TLV_MAX_DEVICES] = {};
-       struct udevice **tlv_devices_p;
-       int ret;
-
-       if (gd->flags & (GD_FLG_RELOC | GD_FLG_SPL_INIT)) {
-               /* Assume BSS is initialized; use static data */
-               if (tlv_devices[dev_num])
-                       return tlv_devices[dev_num];
-               tlv_devices_p = tlv_devices;
-       } else {
-               tlv_devices_p = local_tlv_devices;
-       }
-
-       ret = find_tlv_devices(tlv_devices_p);
-       if (ret == 0 && tlv_devices_p[dev_num])
-               return tlv_devices_p[dev_num];
-
-       return NULL;
-}
-
-/**
- * read_tlv_eeprom - read the hwinfo from i2c EEPROM
- */
-int read_tlv_eeprom(void *eeprom, int offset, int len, int dev_num)
-{
-       struct udevice *dev;
-
-       if (dev_num >= TLV_MAX_DEVICES)
-               return -EINVAL;
-
-       dev = find_tlv_device_by_index(dev_num);
-       if (!dev)
-               return -ENODEV;
-
-       return i2c_eeprom_read(dev, offset, eeprom, len);
-}
-
-/**
- * write_tlv_eeprom - write the hwinfo to i2c EEPROM
- */
-int write_tlv_eeprom(void *eeprom, int len, int dev)
-{
-       if (!(gd->flags & GD_FLG_RELOC))
-               return -ENODEV;
-       if (!tlv_devices[dev])
-               return -ENODEV;
-
-       return i2c_eeprom_write(tlv_devices[dev], 0, eeprom, len);
-}
-
-int read_tlvinfo_tlv_eeprom(void *eeprom, struct tlvinfo_header **hdr,
-                           struct tlvinfo_tlv **first_entry, int dev_num)
-{
-       int ret;
-       struct tlvinfo_header *tlv_hdr;
-       struct tlvinfo_tlv *tlv_ent;
-
-       /* Read TLV header */
-       ret = read_tlv_eeprom(eeprom, 0, TLV_INFO_HEADER_SIZE, dev_num);
-       if (ret < 0)
-               return ret;
-
-       tlv_hdr = eeprom;
-       if (!is_valid_tlvinfo_header(tlv_hdr))
-               return -EINVAL;
-
-       /* Read TLV entries */
-       tlv_ent = to_entry(&tlv_hdr[1]);
-       ret = read_tlv_eeprom(tlv_ent, TLV_INFO_HEADER_SIZE,
-                             be16_to_cpu(tlv_hdr->totallen), dev_num);
-       if (ret < 0)
-               return ret;
-       if (!tlvinfo_check_crc(eeprom))
-               return -EINVAL;
-
-       *hdr = tlv_hdr;
-       *first_entry = tlv_ent;
-
-       return 0;
-}
-
-/**
- *  mac_read_from_eeprom
- *
- *  Read the MAC addresses from EEPROM
- *
- *  This function reads the MAC addresses from EEPROM and sets the
- *  appropriate environment variables for each one read.
- *
- *  The environment variables are only set if they haven't been set already.
- *  This ensures that any user-saved variables are never overwritten.
- *
- *  This function must be called after relocation.
- */
-int mac_read_from_eeprom(void)
-{
-       unsigned int i;
-       int eeprom_index;
-       struct tlvinfo_tlv *eeprom_tlv;
-       int maccount;
-       u8 macbase[6];
-       struct tlvinfo_header *eeprom_hdr = to_header(eeprom);
-       int devnum = 0; // TODO: support multiple EEPROMs
-
-       puts("EEPROM: ");
-
-       if (read_eeprom(devnum, eeprom)) {
-               printf("Read failed.\n");
-               return -1;
-       }
-
-       maccount = 1;
-       if (tlvinfo_find_tlv(eeprom, TLV_CODE_MAC_SIZE, &eeprom_index)) {
-               eeprom_tlv = to_entry(&eeprom[eeprom_index]);
-               maccount = (eeprom_tlv->value[0] << 8) | eeprom_tlv->value[1];
-       }
-
-       memcpy(macbase, "\0\0\0\0\0\0", 6);
-       if (tlvinfo_find_tlv(eeprom, TLV_CODE_MAC_BASE, &eeprom_index)) {
-               eeprom_tlv = to_entry(&eeprom[eeprom_index]);
-               memcpy(macbase, eeprom_tlv->value, 6);
-       }
-
-       for (i = 0; i < maccount; i++) {
-               if (is_valid_ethaddr(macbase)) {
-                       char ethaddr[18];
-                       char enetvar[11];
-
-                       sprintf(ethaddr, "%02X:%02X:%02X:%02X:%02X:%02X",
-                               macbase[0], macbase[1], macbase[2],
-                               macbase[3], macbase[4], macbase[5]);
-                       sprintf(enetvar, i ? "eth%daddr" : "ethaddr", i);
-                       /* Only initialize environment variables that are blank
-                        * (i.e. have not yet been set)
-                        */
-                       if (!env_get(enetvar))
-                               env_set(enetvar, ethaddr);
-
-                       macbase[5]++;
-                       if (macbase[5] == 0) {
-                               macbase[4]++;
-                               if (macbase[4] == 0) {
-                                       macbase[3]++;
-                                       if (macbase[3] == 0) {
-                                               macbase[0] = 0;
-                                               macbase[1] = 0;
-                                               macbase[2] = 0;
-                                       }
-                               }
-                       }
-               }
-       }
-
-       printf("%s v%u len=%u\n", eeprom_hdr->signature, eeprom_hdr->version,
-              be16_to_cpu(eeprom_hdr->totallen));
-
-       return 0;
-}
-
-/**
- *  populate_serial_number - read the serial number from EEPROM
- *
- *  This function reads the serial number from the EEPROM and sets the
- *  appropriate environment variable.
- *
- *  The environment variable is only set if it has not been set
- *  already.  This ensures that any user-saved variables are never
- *  overwritten.
- *
- *  This function must be called after relocation.
- */
-int populate_serial_number(int devnum)
-{
-       char serialstr[257];
-       int eeprom_index;
-       struct tlvinfo_tlv *eeprom_tlv;
-
-       if (env_get("serial#"))
-               return 0;
-
-       if (read_eeprom(devnum, eeprom)) {
-               printf("Read failed.\n");
-               return -1;
-       }
-
-       if (tlvinfo_find_tlv(eeprom, TLV_CODE_SERIAL_NUMBER, &eeprom_index)) {
-               eeprom_tlv = to_entry(&eeprom[eeprom_index]);
-               memcpy(serialstr, eeprom_tlv->value, eeprom_tlv->length);
-               serialstr[eeprom_tlv->length] = 0;
-               env_set("serial#", serialstr);
-       }
-
-       return 0;
-}
diff --git a/lib/Kconfig b/lib/Kconfig
index 858be14f09..4497efbbf4 100644
--- a/lib/Kconfig
+++ b/lib/Kconfig
@@ -909,3 +909,5 @@ config PHANDLE_CHECK_SEQ
          phandles in fdtdec_get_alias_seq() function.
 
 endmenu
+
+source lib/tlv/Kconfig
diff --git a/lib/Makefile b/lib/Makefile
index d9b1811f75..1bc6dad8ac 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -91,6 +91,8 @@ obj-$(CONFIG_LIBAVB) += libavb/
 obj-$(CONFIG_$(SPL_TPL_)OF_LIBFDT) += libfdt/
 obj-$(CONFIG_$(SPL_TPL_)OF_REAL) += fdtdec_common.o fdtdec.o
 
+obj-$(CONFIG_$(SPL_)EEPROM_TLV_LIB) += tlv/
+
 ifdef CONFIG_SPL_BUILD
 obj-$(CONFIG_SPL_YMODEM_SUPPORT) += crc16-ccitt.o
 obj-$(CONFIG_$(SPL_TPL_)HASH) += crc16-ccitt.o
diff --git a/lib/tlv/Kconfig b/lib/tlv/Kconfig
new file mode 100644
index 0000000000..b3912ada78
--- /dev/null
+++ b/lib/tlv/Kconfig
@@ -0,0 +1,15 @@
+config EEPROM_TLV_LIB
+       bool "Enable EEPROM TLV library"
+       depends on I2C_EEPROM
+       help
+         Selecting this option will enable the shared EEPROM TLV library code.
+         It provides functions for reading, writing and parsing of
+         TLV-encoded data from EEPROMs.
+
+config SPL_EEPROM_TLV_LIB
+       bool "Enable EEPROM TLV library for SPL"
+       depends on SPL_I2C_EEPROM
+       help
+         Selecting this option will enable the shared EEPROM TLV library code.
+         It provides functions for reading, writing and parsing of
+         TLV-encoded data from EEPROMs.
diff --git a/lib/tlv/Makefile b/lib/tlv/Makefile
new file mode 100644
index 0000000000..8e96752e75
--- /dev/null
+++ b/lib/tlv/Makefile
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0+
+#
+# (C) Copyright 2017 Linaro
+
+obj-$(CONFIG_EEPROM_TLV_LIB) += tlv_eeprom.o
diff --git a/lib/tlv/tlv_eeprom.c b/lib/tlv/tlv_eeprom.c
new file mode 100644
index 0000000000..464f0aa1fa
--- /dev/null
+++ b/lib/tlv/tlv_eeprom.c
@@ -0,0 +1,750 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * Copyright (C) 2013 Curt Brune <c...@cumulusnetworks.com>
+ * Copyright (C) 2014 Srideep <srideep_devire...@dell.com>
+ * Copyright (C) 2013 Miles Tseng <miles_ts...@accton.com>
+ * Copyright (C) 2014,2016 david_yang <david_y...@accton.com>
+ * Copyright (C) 2022 Josua Mayer <jo...@solid-run.com>
+ */
+
+#include <common.h>
+#include <command.h>
+#include <dm.h>
+#include <i2c.h>
+#include <i2c_eeprom.h>
+#include <env.h>
+#include <init.h>
+#include <net.h>
+#include <asm/global_data.h>
+#include <linux/ctype.h>
+#include <u-boot/crc.h>
+
+#include "tlv_eeprom.h"
+
+DECLARE_GLOBAL_DATA_PTR;
+
+/* File scope function prototypes */
+static int read_eeprom(int devnum, u8 *eeprom);
+static int set_mac(char *buf, const char *string);
+static int set_date(char *buf, const char *string);
+static int set_bytes(char *buf, const char *string, int *converted_accum);
+
+/* The EERPOM contents after being read into memory */
+static u8 eeprom[TLV_INFO_MAX_LEN];
+
+static struct udevice *tlv_devices[TLV_MAX_DEVICES];
+
+#define to_header(p) ((struct tlvinfo_header *)p)
+#define to_entry(p) ((struct tlvinfo_tlv *)p)
+
+/**
+ * Check whether eeprom device exists.
+ */
+bool exists_tlv_eeprom(int dev)
+{
+       return dev < TLV_MAX_DEVICES && tlv_devices[dev] != 0;
+}
+
+static inline bool is_digit(char c)
+{
+       return (c >= '0' && c <= '9');
+}
+
+/**
+ *  is_hex
+ *
+ *  Tests if character is an ASCII hex digit
+ */
+static inline u8 is_hex(char p)
+{
+       return (((p >= '0') && (p <= '9')) ||
+               ((p >= 'A') && (p <= 'F')) ||
+               ((p >= 'a') && (p <= 'f')));
+}
+
+/**
+ *  Validate the checksum in the provided TlvInfo EEPROM data. First,
+ *  verify that the TlvInfo header is valid, then make sure the last
+ *  TLV is a CRC-32 TLV. Then calculate the CRC over the EEPROM data
+ *  and compare it to the value stored in the EEPROM CRC-32 TLV.
+ */
+bool tlvinfo_check_crc(u8 *eeprom)
+{
+       struct tlvinfo_header *eeprom_hdr = to_header(eeprom);
+       struct tlvinfo_tlv    *eeprom_crc;
+       unsigned int       calc_crc;
+       unsigned int       stored_crc;
+
+       // Is the eeprom header valid?
+       if (!is_valid_tlvinfo_header(eeprom_hdr))
+               return false;
+
+       // Is the last TLV a CRC?
+       eeprom_crc = to_entry(&eeprom[TLV_INFO_HEADER_SIZE +
+               be16_to_cpu(eeprom_hdr->totallen) - (TLV_INFO_ENTRY_SIZE + 4)]);
+       if (eeprom_crc->type != TLV_CODE_CRC_32 || eeprom_crc->length != 4)
+               return false;
+
+       // Calculate the checksum
+       calc_crc = crc32(0, (void *)eeprom,
+                        TLV_INFO_HEADER_SIZE + 
be16_to_cpu(eeprom_hdr->totallen) - 4);
+       stored_crc = (eeprom_crc->value[0] << 24) |
+               (eeprom_crc->value[1] << 16) |
+               (eeprom_crc->value[2] <<  8) |
+               eeprom_crc->value[3];
+       return calc_crc == stored_crc;
+}
+
+/**
+ *  read_eeprom
+ *
+ *  Read the EEPROM into memory, if it hasn't already been read.
+ */
+static int read_eeprom(int devnum, u8 *eeprom)
+{
+       int ret;
+       struct tlvinfo_header *eeprom_hdr = to_header(eeprom);
+       struct tlvinfo_tlv *eeprom_tlv = 
to_entry(&eeprom[TLV_INFO_HEADER_SIZE]);
+
+       /* Read the header */
+       ret = read_tlv_eeprom((void *)eeprom_hdr, 0, TLV_INFO_HEADER_SIZE, 
devnum);
+       /* If the header was successfully read, read the TLVs */
+       if (ret == 0 && is_valid_tlvinfo_header(eeprom_hdr))
+               ret = read_tlv_eeprom((void *)eeprom_tlv, TLV_INFO_HEADER_SIZE,
+                                     be16_to_cpu(eeprom_hdr->totallen), 
devnum);
+
+       // If the contents are invalid, start over with default contents
+       if (!is_valid_tlvinfo_header(eeprom_hdr) ||
+           !tlvinfo_check_crc(eeprom)) {
+               strcpy(eeprom_hdr->signature, TLV_INFO_ID_STRING);
+               eeprom_hdr->version = TLV_INFO_VERSION;
+               eeprom_hdr->totallen = cpu_to_be16(0);
+               tlvinfo_update_crc(eeprom);
+       }
+
+#ifdef DEBUG
+       show_eeprom(devnum, eeprom);
+#endif
+
+       return ret;
+}
+
+/**
+ *  tlvinfo_update_crc
+ *
+ *  This function updates the CRC-32 TLV. If there is no CRC-32 TLV, then
+ *  one is added. This function should be called after each update to the
+ *  EEPROM structure, to make sure the CRC is always correct.
+ */
+void tlvinfo_update_crc(u8 *eeprom)
+{
+       struct tlvinfo_header *eeprom_hdr = to_header(eeprom);
+       struct tlvinfo_tlv    *eeprom_crc;
+       unsigned int      calc_crc;
+       int               eeprom_index;
+
+       // Discover the CRC TLV
+       if (!tlvinfo_find_tlv(eeprom, TLV_CODE_CRC_32, &eeprom_index)) {
+               unsigned int totallen = be16_to_cpu(eeprom_hdr->totallen);
+
+               if ((totallen + TLV_INFO_ENTRY_SIZE + 4) > TLV_TOTAL_LEN_MAX)
+                       return;
+               eeprom_index = TLV_INFO_HEADER_SIZE + totallen;
+               eeprom_hdr->totallen = cpu_to_be16(totallen + 
TLV_INFO_ENTRY_SIZE + 4);
+       }
+       eeprom_crc = to_entry(&eeprom[eeprom_index]);
+       eeprom_crc->type = TLV_CODE_CRC_32;
+       eeprom_crc->length = 4;
+
+       // Calculate the checksum
+       calc_crc = crc32(0, (void *)eeprom,
+                        TLV_INFO_HEADER_SIZE + 
be16_to_cpu(eeprom_hdr->totallen) - 4);
+       eeprom_crc->value[0] = (calc_crc >> 24) & 0xFF;
+       eeprom_crc->value[1] = (calc_crc >> 16) & 0xFF;
+       eeprom_crc->value[2] = (calc_crc >>  8) & 0xFF;
+       eeprom_crc->value[3] = (calc_crc >>  0) & 0xFF;
+}
+
+/**
+ *  write_tlvinfo_tlv_eeprom
+ *
+ *  Write the TLV data from CPU memory to the hardware.
+ */
+int write_tlvinfo_tlv_eeprom(void *eeprom, int dev)
+{
+       int ret = 0;
+       struct tlvinfo_header *eeprom_hdr = to_header(eeprom);
+       int eeprom_len;
+
+       tlvinfo_update_crc(eeprom);
+
+       eeprom_len = TLV_INFO_HEADER_SIZE + be16_to_cpu(eeprom_hdr->totallen);
+       ret = write_tlv_eeprom(eeprom, eeprom_len, dev);
+       if (ret) {
+               printf("Programming failed.\n");
+               return -1;
+       }
+
+       printf("Programming passed.\n");
+       return 0;
+}
+
+/**
+ *  tlvinfo_find_tlv
+ *
+ *  This function finds the TLV with the supplied code in the EERPOM.
+ *  An offset from the beginning of the EEPROM is returned in the
+ *  eeprom_index parameter if the TLV is found.
+ */
+bool tlvinfo_find_tlv(u8 *eeprom, u8 tcode, int *eeprom_index)
+{
+       struct tlvinfo_header *eeprom_hdr = to_header(eeprom);
+       struct tlvinfo_tlv    *eeprom_tlv;
+       int eeprom_end;
+
+       // Search through the TLVs, looking for the first one which matches the
+       // supplied type code.
+       *eeprom_index = TLV_INFO_HEADER_SIZE;
+       eeprom_end = TLV_INFO_HEADER_SIZE + be16_to_cpu(eeprom_hdr->totallen);
+       while (*eeprom_index < eeprom_end) {
+               eeprom_tlv = to_entry(&eeprom[*eeprom_index]);
+               if (!is_valid_tlvinfo_entry(eeprom_tlv))
+                       return false;
+               if (eeprom_tlv->type == tcode)
+                       return true;
+               *eeprom_index += TLV_INFO_ENTRY_SIZE + eeprom_tlv->length;
+       }
+       return(false);
+}
+
+/**
+ *  tlvinfo_delete_tlv
+ *
+ *  This function deletes the TLV with the specified type code from the
+ *  EEPROM.
+ */
+bool tlvinfo_delete_tlv(u8 *eeprom, u8 code)
+{
+       int eeprom_index;
+       int tlength;
+       struct tlvinfo_header *eeprom_hdr = to_header(eeprom);
+       struct tlvinfo_tlv *eeprom_tlv;
+
+       // Find the TLV and then move all following TLVs "forward"
+       if (tlvinfo_find_tlv(eeprom, code, &eeprom_index)) {
+               eeprom_tlv = to_entry(&eeprom[eeprom_index]);
+               tlength = TLV_INFO_ENTRY_SIZE + eeprom_tlv->length;
+               memcpy(&eeprom[eeprom_index], &eeprom[eeprom_index + tlength],
+                      TLV_INFO_HEADER_SIZE +
+                      be16_to_cpu(eeprom_hdr->totallen) - eeprom_index -
+                      tlength);
+               eeprom_hdr->totallen =
+                       cpu_to_be16(be16_to_cpu(eeprom_hdr->totallen) -
+                                   tlength);
+               tlvinfo_update_crc(eeprom);
+               return true;
+       }
+       return false;
+}
+
+/**
+ *  tlvinfo_add_tlv
+ *
+ *  This function adds a TLV to the EEPROM, converting the value (a string) to
+ *  the format in which it will be stored in the EEPROM.
+ */
+#define MAX_TLV_VALUE_LEN   256
+bool tlvinfo_add_tlv(u8 *eeprom, int tcode, char *strval)
+{
+       struct tlvinfo_header *eeprom_hdr = to_header(eeprom);
+       struct tlvinfo_tlv *eeprom_tlv;
+       int new_tlv_len = 0;
+       u32 value;
+       char data[MAX_TLV_VALUE_LEN];
+       int eeprom_index;
+
+       // Encode each TLV type into the format to be stored in the EERPOM
+       switch (tcode) {
+       case TLV_CODE_PRODUCT_NAME:
+       case TLV_CODE_PART_NUMBER:
+       case TLV_CODE_SERIAL_NUMBER:
+       case TLV_CODE_LABEL_REVISION:
+       case TLV_CODE_PLATFORM_NAME:
+       case TLV_CODE_ONIE_VERSION:
+       case TLV_CODE_MANUF_NAME:
+       case TLV_CODE_MANUF_COUNTRY:
+       case TLV_CODE_VENDOR_NAME:
+       case TLV_CODE_DIAG_VERSION:
+       case TLV_CODE_SERVICE_TAG:
+               strncpy(data, strval, MAX_TLV_VALUE_LEN);
+               new_tlv_len = min_t(size_t, MAX_TLV_VALUE_LEN, strlen(strval));
+               break;
+       case TLV_CODE_DEVICE_VERSION:
+               value = simple_strtoul(strval, NULL, 0);
+               if (value >= 256) {
+                       printf("ERROR: Device version must be 255 or less. 
Value supplied: %u",
+                              value);
+                       return false;
+               }
+               data[0] = value & 0xFF;
+               new_tlv_len = 1;
+               break;
+       case TLV_CODE_MAC_SIZE:
+               value = simple_strtoul(strval, NULL, 0);
+               if (value >= 65536) {
+                       printf("ERROR: MAC Size must be 65535 or less. Value 
supplied: %u",
+                              value);
+                       return false;
+               }
+               data[0] = (value >> 8) & 0xFF;
+               data[1] = value & 0xFF;
+               new_tlv_len = 2;
+               break;
+       case TLV_CODE_MANUF_DATE:
+               if (set_date(data, strval) != 0)
+                       return false;
+               new_tlv_len = 19;
+               break;
+       case TLV_CODE_MAC_BASE:
+               if (set_mac(data, strval) != 0)
+                       return false;
+               new_tlv_len = 6;
+               break;
+       case TLV_CODE_CRC_32:
+               printf("WARNING: The CRC TLV is set automatically and cannot be 
set manually.\n");
+               return false;
+       case TLV_CODE_VENDOR_EXT:
+       default:
+               if (set_bytes(data, strval, &new_tlv_len) != 0)
+                       return false;
+               break;
+       }
+
+       // Is there room for this TLV?
+       if ((be16_to_cpu(eeprom_hdr->totallen) + TLV_INFO_ENTRY_SIZE + 
new_tlv_len) >
+                       TLV_TOTAL_LEN_MAX) {
+               printf("ERROR: There is not enough room in the EERPOM to save 
data.\n");
+               return false;
+       }
+
+       // Add TLV at the end, overwriting CRC TLV if it exists
+       if (tlvinfo_find_tlv(eeprom, TLV_CODE_CRC_32, &eeprom_index))
+               eeprom_hdr->totallen =
+                       cpu_to_be16(be16_to_cpu(eeprom_hdr->totallen) -
+                                       TLV_INFO_ENTRY_SIZE - 4);
+       else
+               eeprom_index = TLV_INFO_HEADER_SIZE + 
be16_to_cpu(eeprom_hdr->totallen);
+       eeprom_tlv = to_entry(&eeprom[eeprom_index]);
+       eeprom_tlv->type = tcode;
+       eeprom_tlv->length = new_tlv_len;
+       memcpy(eeprom_tlv->value, data, new_tlv_len);
+
+       // Update the total length and calculate (add) a new CRC-32 TLV
+       eeprom_hdr->totallen = cpu_to_be16(be16_to_cpu(eeprom_hdr->totallen) +
+                       TLV_INFO_ENTRY_SIZE + new_tlv_len);
+       tlvinfo_update_crc(eeprom);
+
+       return true;
+}
+
+/**
+ *  set_mac
+ *
+ *  Converts a string MAC address into a binary buffer.
+ *
+ *  This function takes a pointer to a MAC address string
+ *  (i.e."XX:XX:XX:XX:XX:XX", where "XX" is a two-digit hex number).
+ *  The string format is verified and then converted to binary and
+ *  stored in a buffer.
+ */
+static int set_mac(char *buf, const char *string)
+{
+       char *p = (char *)string;
+       int   i;
+       int   err = 0;
+       char *end;
+
+       if (!p) {
+               printf("ERROR: NULL mac addr string passed in.\n");
+               return -1;
+       }
+
+       if (strlen(p) != 17) {
+               printf("ERROR: MAC address strlen() != 17 -- %zu\n", strlen(p));
+               printf("ERROR: Bad MAC address format: %s\n", string);
+               return -1;
+       }
+
+       for (i = 0; i < 17; i++) {
+               if ((i % 3) == 2) {
+                       if (p[i] != ':') {
+                               err++;
+                               printf("ERROR: mac: p[%i] != :, found: `%c'\n",
+                                      i, p[i]);
+                               break;
+                       }
+                       continue;
+               } else if (!is_hex(p[i])) {
+                       err++;
+                       printf("ERROR: mac: p[%i] != hex digit, found: `%c'\n",
+                              i, p[i]);
+                       break;
+               }
+       }
+
+       if (err != 0) {
+               printf("ERROR: Bad MAC address format: %s\n", string);
+               return -1;
+       }
+
+       /* Convert string to binary */
+       for (i = 0, p = (char *)string; i < 6; i++) {
+               buf[i] = p ? hextoul(p, &end) : 0;
+               if (p)
+                       p = (*end) ? end + 1 : end;
+       }
+
+       if (!is_valid_ethaddr((u8 *)buf)) {
+               printf("ERROR: MAC address must not be 00:00:00:00:00:00, a 
multicast address or FF:FF:FF:FF:FF:FF.\n");
+               printf("ERROR: Bad MAC address format: %s\n", string);
+               return -1;
+       }
+
+       return 0;
+}
+
+/**
+ *  set_date
+ *
+ *  Validates the format of the data string
+ *
+ *  This function takes a pointer to a date string (i.e. MM/DD/YYYY hh:mm:ss)
+ *  and validates that the format is correct. If so the string is copied
+ *  to the supplied buffer.
+ */
+static int set_date(char *buf, const char *string)
+{
+       int i;
+
+       if (!string) {
+               printf("ERROR: NULL date string passed in.\n");
+               return -1;
+       }
+
+       if (strlen(string) != 19) {
+               printf("ERROR: Date strlen() != 19 -- %zu\n", strlen(string));
+               printf("ERROR: Bad date format (MM/DD/YYYY hh:mm:ss): %s\n",
+                      string);
+               return -1;
+       }
+
+       for (i = 0; string[i] != 0; i++) {
+               switch (i) {
+               case 2:
+               case 5:
+                       if (string[i] != '/') {
+                               printf("ERROR: Bad date format (MM/DD/YYYY 
hh:mm:ss): %s\n",
+                                      string);
+                               return -1;
+                       }
+                       break;
+               case 10:
+                       if (string[i] != ' ') {
+                               printf("ERROR: Bad date format (MM/DD/YYYY 
hh:mm:ss): %s\n",
+                                      string);
+                               return -1;
+                       }
+                       break;
+               case 13:
+               case 16:
+                       if (string[i] != ':') {
+                               printf("ERROR: Bad date format (MM/DD/YYYY 
hh:mm:ss): %s\n",
+                                      string);
+                               return -1;
+                       }
+                       break;
+               default:
+                       if (!is_digit(string[i])) {
+                               printf("ERROR: Bad date format (MM/DD/YYYY 
hh:mm:ss): %s\n",
+                                      string);
+                               return -1;
+                       }
+                       break;
+               }
+       }
+
+       strcpy(buf, string);
+       return 0;
+}
+
+/**
+ *  set_bytes
+ *
+ *  Converts a space-separated string of decimal numbers into a
+ *  buffer of bytes.
+ *
+ *  This function takes a pointer to a space-separated string of decimal
+ *  numbers (i.e. "128 0x55 0321") with "C" standard radix specifiers
+ *  and converts them to an array of bytes.
+ */
+static int set_bytes(char *buf, const char *string, int *converted_accum)
+{
+       char *p = (char *)string;
+       int   i;
+       uint  byte;
+
+       if (!p) {
+               printf("ERROR: NULL string passed in.\n");
+               return -1;
+       }
+
+       /* Convert string to bytes */
+       for (i = 0, p = (char *)string; (i < TLV_VALUE_MAX_LEN) && (*p != 0);
+                       i++) {
+               while ((*p == ' ') || (*p == '\t') || (*p == ',') ||
+                      (*p == ';')) {
+                       p++;
+               }
+               if (*p != 0) {
+                       if (!is_digit(*p)) {
+                               printf("ERROR: Non-digit found in byte string: 
(%s)\n",
+                                      string);
+                               return -1;
+                       }
+                       byte = simple_strtoul(p, &p, 0);
+                       if (byte >= 256) {
+                               printf("ERROR: The value specified is greater 
than 255: (%u) in string: %s\n",
+                                      byte, string);
+                               return -1;
+                       }
+                       buf[i] = byte & 0xFF;
+               }
+       }
+
+       if (i == TLV_VALUE_MAX_LEN && (*p != 0)) {
+               printf("ERROR: Trying to assign too many bytes (max: %d) in 
string: %s\n",
+                      TLV_VALUE_MAX_LEN, string);
+               return -1;
+       }
+
+       *converted_accum = i;
+       return 0;
+}
+
+static int find_tlv_devices(struct udevice **tlv_devices_p)
+{
+       int ret;
+       int count_dev = 0;
+       struct udevice *dev;
+
+       for (ret = uclass_first_device_check(UCLASS_I2C_EEPROM, &dev);
+                       dev;
+                       ret = uclass_next_device_check(&dev)) {
+               if (ret == 0)
+                       tlv_devices_p[count_dev++] = dev;
+               if (count_dev >= TLV_MAX_DEVICES)
+                       break;
+       }
+
+       return (count_dev == 0) ? -ENODEV : 0;
+}
+
+static struct udevice *find_tlv_device_by_index(int dev_num)
+{
+       struct udevice *local_tlv_devices[TLV_MAX_DEVICES] = {};
+       struct udevice **tlv_devices_p;
+       int ret;
+
+       if (gd->flags & (GD_FLG_RELOC | GD_FLG_SPL_INIT)) {
+               /* Assume BSS is initialized; use static data */
+               if (tlv_devices[dev_num])
+                       return tlv_devices[dev_num];
+               tlv_devices_p = tlv_devices;
+       } else {
+               tlv_devices_p = local_tlv_devices;
+       }
+
+       ret = find_tlv_devices(tlv_devices_p);
+       if (ret == 0 && tlv_devices_p[dev_num])
+               return tlv_devices_p[dev_num];
+
+       return NULL;
+}
+
+/**
+ * read_tlv_eeprom - read the hwinfo from i2c EEPROM
+ */
+int read_tlv_eeprom(void *eeprom, int offset, int len, int dev_num)
+{
+       struct udevice *dev;
+
+       if (dev_num >= TLV_MAX_DEVICES)
+               return -EINVAL;
+
+       dev = find_tlv_device_by_index(dev_num);
+       if (!dev)
+               return -ENODEV;
+
+       return i2c_eeprom_read(dev, offset, eeprom, len);
+}
+
+/**
+ * write_tlv_eeprom - write the hwinfo to i2c EEPROM
+ */
+int write_tlv_eeprom(void *eeprom, int len, int dev)
+{
+       if (!(gd->flags & GD_FLG_RELOC))
+               return -ENODEV;
+       if (!tlv_devices[dev])
+               return -ENODEV;
+
+       return i2c_eeprom_write(tlv_devices[dev], 0, eeprom, len);
+}
+
+int read_tlvinfo_tlv_eeprom(void *eeprom, struct tlvinfo_header **hdr,
+                           struct tlvinfo_tlv **first_entry, int dev_num)
+{
+       int ret;
+       struct tlvinfo_header *tlv_hdr;
+       struct tlvinfo_tlv *tlv_ent;
+
+       /* Read TLV header */
+       ret = read_tlv_eeprom(eeprom, 0, TLV_INFO_HEADER_SIZE, dev_num);
+       if (ret < 0)
+               return ret;
+
+       tlv_hdr = eeprom;
+       if (!is_valid_tlvinfo_header(tlv_hdr))
+               return -EINVAL;
+
+       /* Read TLV entries */
+       tlv_ent = to_entry(&tlv_hdr[1]);
+       ret = read_tlv_eeprom(tlv_ent, TLV_INFO_HEADER_SIZE,
+                             be16_to_cpu(tlv_hdr->totallen), dev_num);
+       if (ret < 0)
+               return ret;
+       if (!tlvinfo_check_crc(eeprom))
+               return -EINVAL;
+
+       *hdr = tlv_hdr;
+       *first_entry = tlv_ent;
+
+       return 0;
+}
+
+/**
+ *  mac_read_from_eeprom
+ *
+ *  Read the MAC addresses from EEPROM
+ *
+ *  This function reads the MAC addresses from EEPROM and sets the
+ *  appropriate environment variables for each one read.
+ *
+ *  The environment variables are only set if they haven't been set already.
+ *  This ensures that any user-saved variables are never overwritten.
+ *
+ *  This function must be called after relocation.
+ */
+int mac_read_from_eeprom(void)
+{
+       unsigned int i;
+       int eeprom_index;
+       struct tlvinfo_tlv *eeprom_tlv;
+       int maccount;
+       u8 macbase[6];
+       struct tlvinfo_header *eeprom_hdr = to_header(eeprom);
+       int devnum = 0; // TODO: support multiple EEPROMs
+
+       puts("EEPROM: ");
+
+       if (read_eeprom(devnum, eeprom)) {
+               printf("Read failed.\n");
+               return -1;
+       }
+
+       maccount = 1;
+       if (tlvinfo_find_tlv(eeprom, TLV_CODE_MAC_SIZE, &eeprom_index)) {
+               eeprom_tlv = to_entry(&eeprom[eeprom_index]);
+               maccount = (eeprom_tlv->value[0] << 8) | eeprom_tlv->value[1];
+       }
+
+       memcpy(macbase, "\0\0\0\0\0\0", 6);
+       if (tlvinfo_find_tlv(eeprom, TLV_CODE_MAC_BASE, &eeprom_index)) {
+               eeprom_tlv = to_entry(&eeprom[eeprom_index]);
+               memcpy(macbase, eeprom_tlv->value, 6);
+       }
+
+       for (i = 0; i < maccount; i++) {
+               if (is_valid_ethaddr(macbase)) {
+                       char ethaddr[18];
+                       char enetvar[11];
+
+                       sprintf(ethaddr, "%02X:%02X:%02X:%02X:%02X:%02X",
+                               macbase[0], macbase[1], macbase[2],
+                               macbase[3], macbase[4], macbase[5]);
+                       sprintf(enetvar, i ? "eth%daddr" : "ethaddr", i);
+                       /* Only initialize environment variables that are blank
+                        * (i.e. have not yet been set)
+                        */
+                       if (!env_get(enetvar))
+                               env_set(enetvar, ethaddr);
+
+                       macbase[5]++;
+                       if (macbase[5] == 0) {
+                               macbase[4]++;
+                               if (macbase[4] == 0) {
+                                       macbase[3]++;
+                                       if (macbase[3] == 0) {
+                                               macbase[0] = 0;
+                                               macbase[1] = 0;
+                                               macbase[2] = 0;
+                                       }
+                               }
+                       }
+               }
+       }
+
+       printf("%s v%u len=%u\n", eeprom_hdr->signature, eeprom_hdr->version,
+              be16_to_cpu(eeprom_hdr->totallen));
+
+       return 0;
+}
+
+/**
+ *  populate_serial_number - read the serial number from EEPROM
+ *
+ *  This function reads the serial number from the EEPROM and sets the
+ *  appropriate environment variable.
+ *
+ *  The environment variable is only set if it has not been set
+ *  already.  This ensures that any user-saved variables are never
+ *  overwritten.
+ *
+ *  This function must be called after relocation.
+ */
+int populate_serial_number(int devnum)
+{
+       char serialstr[257];
+       int eeprom_index;
+       struct tlvinfo_tlv *eeprom_tlv;
+
+       if (env_get("serial#"))
+               return 0;
+
+       if (read_eeprom(devnum, eeprom)) {
+               printf("Read failed.\n");
+               return -1;
+       }
+
+       if (tlvinfo_find_tlv(eeprom, TLV_CODE_SERIAL_NUMBER, &eeprom_index)) {
+               eeprom_tlv = to_entry(&eeprom[eeprom_index]);
+               memcpy(serialstr, eeprom_tlv->value, eeprom_tlv->length);
+               serialstr[eeprom_tlv->length] = 0;
+               env_set("serial#", serialstr);
+       }
+
+       return 0;
+}
-- 
2.34.1

Reply via email to