From 96a4361b92455982ea3e9c71aae436a65a7a6a3e Mon Sep 17 00:00:00 2001 From: "Zhao.Chen" Date: 10 Sep 2024 14:37:07 +0000 Read the MAC address from EEPROM and set it to env. Tested: printenv ethaddr=88:88:88:88:88:8b eth1addr=88:88:88:88:88:8d %% original patch: 0002-read-mac-address-from-eeprom.patch --- board/aspeed/evb_ast2600/Kconfig | 5 + board/aspeed/evb_ast2600/evb_ast2600.c | 79 ++++++++++ common/board_r.c | 3 +- drivers/misc/Kconfig | 6 + drivers/misc/Makefile | 1 + drivers/misc/at24.c | 204 +++++++++++++++++++++++++ 6 files changed, 297 insertions(+), 1 deletion(-) create mode 100644 drivers/misc/at24.c diff --git a/board/aspeed/evb_ast2600/Kconfig b/board/aspeed/evb_ast2600/Kconfig index a5d105199f..839cd0e649 100644 --- a/board/aspeed/evb_ast2600/Kconfig +++ b/board/aspeed/evb_ast2600/Kconfig @@ -10,4 +10,9 @@ config SYS_CONFIG_NAME string "board configuration name" default "evb_ast2600" +config MAC_ADDR_IN_EEPROM + bool "MAC address in EEPROM" + help + Enable this option to read the MAC from the EEPROM + endif diff --git a/board/aspeed/evb_ast2600/evb_ast2600.c b/board/aspeed/evb_ast2600/evb_ast2600.c index 72ecb18c15..7244686bb1 100644 --- a/board/aspeed/evb_ast2600/evb_ast2600.c +++ b/board/aspeed/evb_ast2600/evb_ast2600.c @@ -131,3 +131,82 @@ int board_early_init_f(void) espi_init(); return 0; } + +#if defined(CONFIG_MAC_ADDR_IN_EEPROM) + +#include +#include +#include + +#define MAC_NUM 2 +#define MAC_BYTE_SIZE 6 + +static int get_ethaddr_from_eeprom(u8 *addr) +{ + struct udevice *dev; + u32 mac_offset[MAC_NUM]; + int i, ret, mac_read; + + uclass_first_device_check(UCLASS_I2C_EEPROM, &dev); + if (!dev) { + printf("No eeprom found.\n"); + } else { + do { + if (dev_read_bool(dev, "ethaddr-in-eeprom")) { + char prop_mac_offset[32]; + mac_read = 0; + for (i = 0; i < MAC_NUM; i++) { + snprintf(prop_mac_offset, 32, "eth%d-offset", i); + ret = dev_read_u32(dev, prop_mac_offset, &mac_offset[i]); + if (0 == ret) { + ret = i2c_eeprom_read(dev, mac_offset[i], + addr+i*MAC_BYTE_SIZE, MAC_BYTE_SIZE); + if (ret != 0) { + printf("get eth%d from eeprom failed, offset=0x%x\n", + i, mac_offset[i]); + return -1; + } else { + mac_read++; + } + } + } + if (mac_read > 0) + return 0; + } + uclass_next_device_check(&dev); + } while (dev); + } + return -1; +} + +int mac_read_from_eeprom(void) +{ + u8 ethaddr[MAC_NUM*MAC_BYTE_SIZE]; + char addr_str[100]; + + memset(ethaddr, 0, sizeof(ethaddr)); + + if (get_ethaddr_from_eeprom(ethaddr)) { + printf("get ethaddr from eeprom failed.\n"); + return 0; + } + for (int i = 0; i < MAC_NUM; i++) { + if (is_valid_ethaddr(ðaddr[MAC_BYTE_SIZE*i])) { + memset(addr_str, 0, sizeof(addr_str)); + if (i == 0) { + snprintf(addr_str, sizeof(addr_str), "ethaddr"); + } else { + snprintf(addr_str, sizeof(addr_str), "eth%daddr", i); + } + eth_env_set_enetaddr(addr_str, ðaddr[MAC_BYTE_SIZE*i]); + env_save(); + } else { + printf("eeprom mac%d address invaild:%pM\n", i, \ + ðaddr[MAC_BYTE_SIZE*i]); + } + } + + return 0; +} +#endif + diff --git a/common/board_r.c b/common/board_r.c index 429b9a2833..f1c98362ac 100644 --- a/common/board_r.c +++ b/common/board_r.c @@ -744,7 +744,8 @@ static init_fnc_t init_sequence_r[] = { #endif INIT_FUNC_WATCHDOG_RESET initr_secondary_cpu, -#if defined(CONFIG_ID_EEPROM) || defined(CONFIG_SYS_I2C_MAC_OFFSET) +#if defined(CONFIG_ID_EEPROM) || defined(CONFIG_SYS_I2C_MAC_OFFSET) \ + || defined(CONFIG_MAC_ADDR_IN_EEPROM) mac_read_from_eeprom, #endif INIT_FUNC_WATCHDOG_RESET diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index c2a188ac29..69eeaf51fa 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -260,6 +260,12 @@ config SPL_I2C_EEPROM This option is an SPL-variant of the I2C_EEPROM option. See the help of I2C_EEPROM for details. +config EEPROM_AT24 + bool "Enable driver for AT24 EEPROMs" + depends on MISC + help + Enable a driver for AT24 EEPROMs attached via I2C. + config ZYNQ_GEM_I2C_MAC_OFFSET hex "Set the I2C MAC offset" default 0x0 diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index bee53a3559..4e76c7d398 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -39,6 +39,7 @@ obj-$(CONFIG_GDSYS_IOEP) += gdsys_ioep.o obj-$(CONFIG_GDSYS_RXAUI_CTRL) += gdsys_rxaui_ctrl.o obj-$(CONFIG_GDSYS_SOC) += gdsys_soc.o obj-$(CONFIG_$(SPL_)I2C_EEPROM) += i2c_eeprom.o +obj-$(CONFIG_$(SPL_)EEPROM_AT24) += at24.o obj-$(CONFIG_IHS_FPGA) += ihs_fpga.o obj-$(CONFIG_IMX8) += imx8/ obj-$(CONFIG_LED_STATUS) += status_led.o diff --git a/drivers/misc/at24.c b/drivers/misc/at24.c new file mode 100644 index 0000000000..e117098766 --- /dev/null +++ b/drivers/misc/at24.c @@ -0,0 +1,204 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2014 Google, Inc + */ + +#include +#include +#include +#include +#include + +/* Address pointer is 16 bit. */ +#define AT24_FLAG_ADDR16 BIT(7) +/* sysfs-entry will be read-only. */ +#define AT24_FLAG_READONLY BIT(6) +/* sysfs-entry will be world-readable. */ +#define AT24_FLAG_IRUGO BIT(5) +/* Take always 8 addresses (24c00). */ +#define AT24_FLAG_TAKE8ADDR BIT(4) +/* Factory-programmed serial number. */ +#define AT24_FLAG_SERIAL BIT(3) +/* Factory-programmed mac address. */ +#define AT24_FLAG_MAC BIT(2) +/* Does not auto-rollover reads to the next slave address. */ +#define AT24_FLAG_NO_RDROL BIT(1) + +struct at24_chip_data { + struct udevice *dev; + u32 byte_len; + u8 flags; + void (*read_post)(unsigned int off, char *buf, size_t count); +}; + +#define AT24_CHIP_DATA(_name, _len, _flags) \ + static const struct at24_chip_data _name = { \ + .byte_len = _len, .flags = _flags, \ + } + +AT24_CHIP_DATA(at24_data_24c08, 8192 / 8, AT24_FLAG_NO_RDROL); +AT24_CHIP_DATA(at24_data_24c16, 16384 / 8, AT24_FLAG_NO_RDROL); +AT24_CHIP_DATA(at24_data_24c32, 32768 / 8, AT24_FLAG_ADDR16); +AT24_CHIP_DATA(at24_data_24c64, 65536 / 8, AT24_FLAG_ADDR16); + +int i2c_eeprom_read(struct udevice *dev, int offset, uint8_t *buf, int size) +{ + const struct i2c_eeprom_ops *ops = device_get_ops(dev); + + if (!ops->read) + return -ENOSYS; + + return ops->read(dev, offset, buf, size); +} + +int i2c_eeprom_write(struct udevice *dev, int offset, uint8_t *buf, int size) +{ + const struct i2c_eeprom_ops *ops = device_get_ops(dev); + + if (!ops->write) + return -ENOSYS; + + return ops->write(dev, offset, buf, size); +} + +/* + * This routine supports chips which consume multiple I2C addresses. It + * computes the addressing information to be used for a given r/w request. + * Assumes that sanity checks for offset happened at sysfs-layer. + * + * Slave address and byte offset derive from the offset. Always + * set the byte address; on a multi-master board, another master + * may have changed the chip's "current" address pointer. + */ +static unsigned int at24_translate_offset(struct at24_chip_data *at24, + unsigned int *offset) +{ + unsigned int i = 0; + + if (at24->flags & AT24_FLAG_ADDR16) { + i = *offset >> 16; + *offset &= 0xffff; + } else { + i = *offset >> 8; + *offset &= 0xff; + } + + return i; +} + +static size_t at24_adjust_read_count(struct at24_chip_data *at24, + unsigned int offset, size_t count) +{ + unsigned int bits; + size_t remainder; + + /* + * In case of multi-address chips that don't rollover reads to + * the next slave address: truncate the count to the slave boundary, + * so that the read never straddles slaves. + */ + if (at24->flags & AT24_FLAG_NO_RDROL) { + bits = (at24->flags & AT24_FLAG_ADDR16) ? 16 : 8; + remainder = BIT(bits) - offset; + if (count > remainder) + count = remainder; + } + + return count; +} + +static ssize_t at24_read(struct at24_chip_data *at24, uint8_t *buf, + unsigned int offset, size_t count) +{ + int ret; + struct dm_i2c_chip *chip = dev_get_parent_platdata(at24->dev); + u8 chip_addr = chip->chip_addr; + int chip_addr_offset = at24_translate_offset(at24, &offset); + + count = at24_adjust_read_count(at24, offset, count); + chip->chip_addr += chip_addr_offset; + ret = dm_i2c_read(at24->dev, offset, buf, count); + chip->chip_addr = chip_addr; + + if (0 == ret) { + return count; + } else { + return -ETIMEDOUT; + } +} + +static int i2c_eeprom_std_read(struct udevice *dev, int off, uint8_t *buf, + int size) +{ + struct at24_chip_data *at24; + int i, ret; + + at24 = (struct at24_chip_data *)dev_get_driver_data(dev); + + if (off + size > at24->byte_len) + return -EINVAL; + + for (i = 0; size; i += ret, size -= ret) { + ret = at24_read(at24, buf + i, off + i, size); + if (ret < 0) { + return -ETIMEDOUT; + } + } + + return 0; +} + +static int i2c_eeprom_std_write(struct udevice *dev, int offset, + const uint8_t *buf, int size) +{ + return -ENODEV; +} + +static const struct i2c_eeprom_ops i2c_eeprom_std_ops = { + .read = i2c_eeprom_std_read, + .write = i2c_eeprom_std_write, +}; + +static int i2c_eeprom_std_ofdata_to_platdata(struct udevice *dev) +{ + struct at24_chip_data *data; + struct dm_i2c_chip *chip = dev_get_parent_platdata(dev); + + data = (struct at24_chip_data *)dev_get_driver_data(dev); + data->dev = dev; + + if (data->flags & AT24_FLAG_ADDR16) + chip->offset_len = 2; + else + chip->offset_len = 1; + + return 0; +} + +static int i2c_eeprom_std_probe(struct udevice *dev) +{ + return 0; +} + +static const struct udevice_id i2c_eeprom_std_ids[] = { + { .compatible = "atmel,24c08", .data = (ulong)&at24_data_24c08 }, + { .compatible = "atmel,24c16", .data = (ulong)&at24_data_24c16 }, + { .compatible = "atmel,24c32", .data = (ulong)&at24_data_24c32 }, + { .compatible = "atmel,24c64", .data = (ulong)&at24_data_24c64 }, + { /* END OF LIST */ }, +}; + +U_BOOT_DRIVER(i2c_eeprom_std) = { + .name = "i2c_eeprom_at24", + .id = UCLASS_I2C_EEPROM, + .of_match = i2c_eeprom_std_ids, + .probe = i2c_eeprom_std_probe, + .ofdata_to_platdata = i2c_eeprom_std_ofdata_to_platdata, + .priv_auto_alloc_size = sizeof(struct i2c_eeprom), + .ops = &i2c_eeprom_std_ops, +}; + +UCLASS_DRIVER(i2c_eeprom) = { + .id = UCLASS_I2C_EEPROM, + .name = "i2c_eeprom_at24", +}; -- 2.35.3