diff mbox series

[2/3] xilinx: board: Read the whole eeprom not just offset

Message ID f8b808cc114d3019cb81b4d3830c766ea50db4fa.1602683746.git.michal.simek@xilinx.com
State Accepted
Commit d61728c8e87e4a318497a4b0873e8b656aa30a08
Delegated to: Michal Simek
Headers show
Series xilinx: board: Add support for board eeproms | expand

Commit Message

Michal Simek Oct. 14, 2020, 1:55 p.m. UTC
Starts to use new way how eeproms should be referenced.
Reference is done via nvmem alias nodes. When this new way is specified
code itself read the eeprom and decode xilinx legacy format and fill struct
xilinx_board_description. Then based on information present there board_*
variables are setup.
If variables are saved and content can't be changed information is just
shown on console.

Signed-off-by: Michal Simek <michal.simek@xilinx.com>
---

 board/xilinx/common/board.c  | 205 ++++++++++++++++++++++++++++++++++-
 board/xilinx/common/board.h  |   2 +
 board/xilinx/versal/board.c  |   3 +
 board/xilinx/zynqmp/zynqmp.c |   3 +
 4 files changed, 212 insertions(+), 1 deletion(-)
diff mbox series

Patch

diff --git a/board/xilinx/common/board.c b/board/xilinx/common/board.c
index b0f60c40a5c8..0aed84546d41 100644
--- a/board/xilinx/common/board.c
+++ b/board/xilinx/common/board.c
@@ -1,6 +1,6 @@ 
 // SPDX-License-Identifier: GPL-2.0+
 /*
- * (C) Copyright 2014 - 2019 Xilinx, Inc.
+ * (C) Copyright 2014 - 2020 Xilinx, Inc.
  * Michal Simek <michal.simek@xilinx.com>
  */
 
@@ -11,7 +11,11 @@ 
 #include <dm/uclass.h>
 #include <i2c.h>
 #include <linux/sizes.h>
+#include <malloc.h>
 #include "board.h"
+#include <dm.h>
+#include <i2c_eeprom.h>
+#include <net.h>
 
 #if defined(CONFIG_ZYNQ_GEM_I2C_MAC_OFFSET)
 int zynq_board_read_rom_ethaddr(unsigned char *ethaddr)
@@ -41,6 +45,180 @@  int zynq_board_read_rom_ethaddr(unsigned char *ethaddr)
 }
 #endif
 
+#define EEPROM_HEADER_MAGIC		0xdaaddeed
+#define EEPROM_HDR_MANUFACTURER_LEN	16
+#define EEPROM_HDR_NAME_LEN		16
+#define EEPROM_HDR_REV_LEN		8
+#define EEPROM_HDR_SERIAL_LEN		20
+#define EEPROM_HDR_NO_OF_MAC_ADDR	4
+#define EEPROM_HDR_ETH_ALEN		ETH_ALEN
+
+struct xilinx_board_description {
+	u32 header;
+	char manufacturer[EEPROM_HDR_MANUFACTURER_LEN + 1];
+	char name[EEPROM_HDR_NAME_LEN + 1];
+	char revision[EEPROM_HDR_REV_LEN + 1];
+	char serial[EEPROM_HDR_SERIAL_LEN + 1];
+	u8 mac_addr[EEPROM_HDR_NO_OF_MAC_ADDR][EEPROM_HDR_ETH_ALEN + 1];
+};
+
+static struct xilinx_board_description *board_info;
+
+#define XILINX_I2C_DETECTION_BITS	8
+
+/* Variable which stores pointer to array which stores eeprom content */
+struct xilinx_legacy_format {
+	char board_sn[18]; /* 0x0 */
+	char unused0[14]; /* 0x12 */
+	char eth_mac[6]; /* 0x20 */
+	char unused1[170]; /* 0x26 */
+	char board_name[11]; /* 0xd0 */
+	char unused2[5]; /* 0xdc */
+	char board_revision[3]; /* 0xe0 */
+	char unused3[29]; /* 0xe3 */
+};
+
+static void xilinx_eeprom_legacy_cleanup(char *eeprom, int size)
+{
+	int i;
+	char byte;
+
+	for (i = 0; i < size; i++) {
+		byte = eeprom[i];
+
+		/* Remove all ffs and spaces */
+		if (byte == 0xff || byte == ' ')
+			eeprom[i] = 0;
+
+		/* Convert strings to lower case */
+		if (byte >= 'A' && byte <= 'Z')
+			eeprom[i] = byte + 'a' - 'A';
+	}
+}
+
+static int xilinx_read_eeprom_legacy(struct udevice *dev, char *name,
+				     struct xilinx_board_description *desc)
+{
+	int ret, size;
+	struct xilinx_legacy_format *eeprom_content;
+	bool eth_valid = false;
+
+	size = sizeof(*eeprom_content);
+
+	eeprom_content = calloc(1, size);
+	if (!eeprom_content)
+		return -ENOMEM;
+
+	debug("%s: I2C EEPROM read pass data at %p\n", __func__,
+	      eeprom_content);
+
+	ret = dm_i2c_read(dev, 0, (uchar *)eeprom_content, size);
+	if (ret) {
+		debug("%s: I2C EEPROM read failed\n", __func__);
+		free(eeprom_content);
+		return ret;
+	}
+
+	xilinx_eeprom_legacy_cleanup((char *)eeprom_content, size);
+
+	printf("Xilinx I2C Legacy format at %s:\n", name);
+	printf(" Board name:\t%s\n", eeprom_content->board_name);
+	printf(" Board rev:\t%s\n", eeprom_content->board_revision);
+	printf(" Board SN:\t%s\n", eeprom_content->board_sn);
+
+	eth_valid = is_valid_ethaddr((const u8 *)eeprom_content->eth_mac);
+	if (eth_valid)
+		printf(" Ethernet mac:\t%pM\n", eeprom_content->eth_mac);
+
+	/* Terminating \0 chars ensure end of string */
+	strcpy(desc->name, eeprom_content->board_name);
+	strcpy(desc->revision, eeprom_content->board_revision);
+	strcpy(desc->serial, eeprom_content->board_sn);
+	if (eth_valid)
+		memcpy(desc->mac_addr[0], eeprom_content->eth_mac, ETH_ALEN);
+
+	desc->header = EEPROM_HEADER_MAGIC;
+
+	free(eeprom_content);
+
+	return ret;
+}
+
+static bool xilinx_detect_legacy(u8 *buffer)
+{
+	int i;
+	char c;
+
+	for (i = 0; i < XILINX_I2C_DETECTION_BITS; i++) {
+		c = buffer[i];
+
+		if (c < '0' || c > '9')
+			return false;
+	}
+
+	return true;
+}
+
+static int xilinx_read_eeprom_single(char *name,
+				     struct xilinx_board_description *desc)
+{
+	int ret;
+	struct udevice *dev;
+	ofnode eeprom;
+	u8 buffer[XILINX_I2C_DETECTION_BITS];
+
+	eeprom = ofnode_get_aliases_node(name);
+	if (!ofnode_valid(eeprom))
+		return -ENODEV;
+
+	ret = uclass_get_device_by_ofnode(UCLASS_I2C_EEPROM, eeprom, &dev);
+	if (ret)
+		return ret;
+
+	ret = dm_i2c_read(dev, 0, buffer, sizeof(buffer));
+	if (ret) {
+		debug("%s: I2C EEPROM read failed\n", __func__);
+		return ret;
+	}
+
+	debug("%s: i2c memory detected: %s\n", __func__, name);
+
+	if (xilinx_detect_legacy(buffer))
+		return xilinx_read_eeprom_legacy(dev, name, desc);
+
+	return -ENODEV;
+}
+
+__maybe_unused int xilinx_read_eeprom(void)
+{
+	int id, highest_id;
+	char name_buf[8]; /* 8 bytes should be enough for nvmem+number */
+
+	highest_id = dev_read_alias_highest_id("nvmem");
+	/* No nvmem aliases present */
+	if (highest_id < 0)
+		return -EINVAL;
+
+	board_info = calloc(1, sizeof(*board_info));
+	if (!board_info)
+		return -ENOMEM;
+
+	debug("%s: Highest ID %d, board_info %p\n", __func__,
+	      highest_id, board_info);
+
+	for (id = 0; id <= highest_id; id++) {
+		snprintf(name_buf, sizeof(name_buf), "nvmem%d", id);
+
+		/* Ignoring return value for supporting multiple chips */
+		xilinx_read_eeprom_single(name_buf, board_info);
+	}
+
+	if (board_info->header != EEPROM_HEADER_MAGIC)
+		free(board_info);
+
+	return 0;
+}
+
 #if defined(CONFIG_OF_BOARD) || defined(CONFIG_OF_SEPARATE)
 void *board_fdt_blob_setup(void)
 {
@@ -78,6 +256,7 @@  void *board_fdt_blob_setup(void)
 int board_late_init_xilinx(void)
 {
 	u32 ret = 0;
+	int i;
 	phys_size_t bootm_size = gd->ram_size;
 
 	if (CONFIG_IS_ENABLED(ARCH_ZYNQ))
@@ -88,6 +267,30 @@  int board_late_init_xilinx(void)
 	ret |= env_set_addr("bootm_low", (void *)gd->ram_base);
 	ret |= env_set_addr("bootm_size", (void *)bootm_size);
 
+	if (board_info) {
+		if (board_info->manufacturer)
+			ret |= env_set("manufacturer",
+				       board_info->manufacturer);
+		if (board_info->name)
+			ret |= env_set("board_name", board_info->name);
+		if (board_info->revision)
+			ret |= env_set("board_rev", board_info->revision);
+		if (board_info->serial)
+			ret |= env_set("board_serial", board_info->serial);
+
+		for (i = 0; i < EEPROM_HDR_NO_OF_MAC_ADDR; i++) {
+			if (!board_info->mac_addr[i])
+				continue;
+
+			if (!CONFIG_IS_ENABLED(NET))
+				continue;
+
+			if (is_valid_ethaddr((const u8 *)board_info->mac_addr[i]))
+				ret |= eth_env_set_enetaddr_by_index("eth", i,
+						board_info->mac_addr[i]);
+		}
+	}
+
 	if (ret)
 		printf("%s: Saving run time variables FAILED\n", __func__);
 
diff --git a/board/xilinx/common/board.h b/board/xilinx/common/board.h
index 180dfbca1082..69e642429b85 100644
--- a/board/xilinx/common/board.h
+++ b/board/xilinx/common/board.h
@@ -9,4 +9,6 @@ 
 
 int board_late_init_xilinx(void);
 
+int xilinx_read_eeprom(void);
+
 #endif /* BOARD_XILINX_COMMON_BOARD_H */
diff --git a/board/xilinx/versal/board.c b/board/xilinx/versal/board.c
index a5ca4ca87401..912c1143a8ad 100644
--- a/board/xilinx/versal/board.c
+++ b/board/xilinx/versal/board.c
@@ -36,6 +36,9 @@  int board_init(void)
 	fpga_add(fpga_xilinx, &versalpl);
 #endif
 
+	if (CONFIG_IS_ENABLED(DM_I2C) && CONFIG_IS_ENABLED(I2C_EEPROM))
+		xilinx_read_eeprom();
+
 	return 0;
 }
 
diff --git a/board/xilinx/zynqmp/zynqmp.c b/board/xilinx/zynqmp/zynqmp.c
index 362c6e3dd6fb..db3f5978a928 100644
--- a/board/xilinx/zynqmp/zynqmp.c
+++ b/board/xilinx/zynqmp/zynqmp.c
@@ -324,6 +324,9 @@  int board_init(void)
 	if (sizeof(CONFIG_ZYNQMP_SPL_PM_CFG_OBJ_FILE) > 1)
 		zynqmp_pmufw_load_config_object(zynqmp_pm_cfg_obj,
 						zynqmp_pm_cfg_obj_size);
+#else
+	if (CONFIG_IS_ENABLED(DM_I2C) && CONFIG_IS_ENABLED(I2C_EEPROM))
+		xilinx_read_eeprom();
 #endif
 
 	printf("EL Level:\tEL%d\n", current_el());