All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/4] Add support for I2C Legacy/FRU decoding
@ 2020-10-20 14:50 Michal Simek
  2020-10-20 14:50 ` [PATCH 1/4] xilinx: common: Add Makefile to common folder Michal Simek
                   ` (4 more replies)
  0 siblings, 5 replies; 6+ messages in thread
From: Michal Simek @ 2020-10-20 14:50 UTC (permalink / raw)
  To: u-boot

Hi,

the whole series add support for I2C based FRU eeprom board identification.
EEPROMs are referenced by nvmem alias which was suggested by Rob Herring
(dt maintainer). Previous code was using chosen xlnx,eeprom property.
And because no platform is setting up nvmem alias the code is doing nothing
on all boards.

If you want to try it and test it just choose board and define nvmem
aliases and you should see similar logs like this.

zcu104 with legacy format and FRU on FMC

U-Boot 2020.10-00835-g819dc03ff784 (Oct 20 2020 - 12:48:12 +0200)

Model: ZynqMP ZCU104 RevC
Board: Xilinx ZynqMP
DRAM:  2 GiB
PMUFW:  v1.1
Xilinx I2C Legacy format at nvmem0:
 Board name:    zcu104
 Board rev:     c
 Board SN:      895527361843-94820
 Ethernet mac:  00:0a:35:04:eb:93
Xilinx I2C FRU format at nvmem1:
 Manufacturer Name: XILINX
 Product Name: XXX-XXX
 Serial No: 1231
 Part Number: dd
 File ID: U-Boot generator
 Revision Number: rev_A
EL Level:       EL2
Chip ID:        zu7e
WDT:   Started with servicing (60s timeout)
NAND:  0 MiB
MMC:   mmc at ff170000: 0
Loading Environment from FAT... *** Warning - bad CRC, using default environment

In:    serial at ff000000
Out:   serial at ff000000
Err:   serial at ff000000
Bootmode: LVL_SHFT_SD_MODE1
Reset reason:   EXTERNAL
Net:
ZYNQ GEM: ff0e0000, mdio bus ff0e0000, phyaddr 12, interface rgmii-id
eth0: ethernet at ff0e0000
Hit any key to stop autoboot:  0
ZynqMP>

Based on https://lists.denx.de/pipermail/u-boot/2020-October/429382.html

Thanks,
Michal


Michal Simek (3):
  xilinx: common: Add Makefile to common folder
  xilinx: cmd: Add basic fru format generator
  xilinx: board: Add FRU decoder support

Siva Durga Prasad Paladugu (1):
  xilinx: cmd: Add support for FRU commands

 board/xilinx/Kconfig            |   8 +
 board/xilinx/common/Makefile    |  10 +
 board/xilinx/common/board.c     |  83 +++++++-
 board/xilinx/common/fru.c       |  91 ++++++++
 board/xilinx/common/fru.h       |  83 ++++++++
 board/xilinx/common/fru_ops.c   | 362 ++++++++++++++++++++++++++++++++
 board/xilinx/versal/Makefile    |   1 -
 board/xilinx/zynq/Makefile      |   1 -
 board/xilinx/zynqmp/MAINTAINERS |   1 +
 board/xilinx/zynqmp/Makefile    |   1 -
 10 files changed, 637 insertions(+), 4 deletions(-)
 create mode 100644 board/xilinx/common/Makefile
 create mode 100644 board/xilinx/common/fru.c
 create mode 100644 board/xilinx/common/fru.h
 create mode 100644 board/xilinx/common/fru_ops.c

-- 
2.28.0

^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH 1/4] xilinx: common: Add Makefile to common folder
  2020-10-20 14:50 [PATCH 0/4] Add support for I2C Legacy/FRU decoding Michal Simek
@ 2020-10-20 14:50 ` Michal Simek
  2020-10-20 14:50 ` [PATCH 2/4] xilinx: cmd: Add support for FRU commands Michal Simek
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 6+ messages in thread
From: Michal Simek @ 2020-10-20 14:50 UTC (permalink / raw)
  To: u-boot

There is no need to reference files in common folder back. Simply adding
Makefile to this folder does the job because this "common" location is
already wired in main Makefile.

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

 board/xilinx/common/Makefile | 7 +++++++
 board/xilinx/versal/Makefile | 1 -
 board/xilinx/zynq/Makefile   | 1 -
 board/xilinx/zynqmp/Makefile | 1 -
 4 files changed, 7 insertions(+), 3 deletions(-)
 create mode 100644 board/xilinx/common/Makefile

diff --git a/board/xilinx/common/Makefile b/board/xilinx/common/Makefile
new file mode 100644
index 000000000000..3600da464b87
--- /dev/null
+++ b/board/xilinx/common/Makefile
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# (C) Copyright 2020 Xilinx, Inc.
+# Michal Simek <michal.simek@xilinx.com>
+#
+
+obj-y	+= board.o
diff --git a/board/xilinx/versal/Makefile b/board/xilinx/versal/Makefile
index 90e034315406..4a46ca02d705 100644
--- a/board/xilinx/versal/Makefile
+++ b/board/xilinx/versal/Makefile
@@ -6,4 +6,3 @@
 
 obj-y	:= board.o
 obj-$(CONFIG_CMD_VERSAL)	+= cmds.o
-obj-y	+= ../common/board.o
diff --git a/board/xilinx/zynq/Makefile b/board/xilinx/zynq/Makefile
index 096a7aceb939..856617158917 100644
--- a/board/xilinx/zynq/Makefile
+++ b/board/xilinx/zynq/Makefile
@@ -4,7 +4,6 @@
 # Wolfgang Denk, DENX Software Engineering, wd at denx.de.
 
 obj-y	:= board.o
-obj-y	+= ../common/board.o
 
 ifneq ($(CONFIG_XILINX_PS_INIT_FILE),"")
 PS_INIT_FILE := $(shell cd $(srctree); readlink -f $(CONFIG_XILINX_PS_INIT_FILE))
diff --git a/board/xilinx/zynqmp/Makefile b/board/xilinx/zynqmp/Makefile
index 398c6aaa452a..7d8277ca4004 100644
--- a/board/xilinx/zynqmp/Makefile
+++ b/board/xilinx/zynqmp/Makefile
@@ -4,7 +4,6 @@
 # Michal Simek <michal.simek@xilinx.com>
 
 obj-y	:= zynqmp.o
-obj-y	+= ../common/board.o
 
 ifneq ($(CONFIG_XILINX_PS_INIT_FILE),"")
 PS_INIT_FILE := $(shell cd $(srctree); readlink -f $(CONFIG_XILINX_PS_INIT_FILE))
-- 
2.28.0

^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH 2/4] xilinx: cmd: Add support for FRU commands
  2020-10-20 14:50 [PATCH 0/4] Add support for I2C Legacy/FRU decoding Michal Simek
  2020-10-20 14:50 ` [PATCH 1/4] xilinx: common: Add Makefile to common folder Michal Simek
@ 2020-10-20 14:50 ` Michal Simek
  2020-10-20 14:50 ` [PATCH 3/4] xilinx: cmd: Add basic fru format generator Michal Simek
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 6+ messages in thread
From: Michal Simek @ 2020-10-20 14:50 UTC (permalink / raw)
  To: u-boot

From: Siva Durga Prasad Paladugu <siva.durga.paladugu@xilinx.com>

This patch adds support for fru commands "fru capture" and "fru display".
The fru capture parses the FRU table present at an address and stores in a
structure for later use. The fru display prints the content of captured
structured in a readable format.

As of now, it supports only common header and board area of FRU. Also, it
supports only English language code and ASCII8/BINARY formats.

fru_data variable is placed to data section because fru parser can be
called very early before bss is initialized. And also information needs to
be shared that's why it is exported via header.

Signed-off-by: Siva Durga Prasad Paladugu <siva.durga.paladugu@xilinx.com>
Signed-off-by: Michal Simek <michal.simek@xilinx.com>
---

 board/xilinx/Kconfig            |   8 +
 board/xilinx/common/Makefile    |   3 +
 board/xilinx/common/fru.c       |  73 +++++++++
 board/xilinx/common/fru.h       |  66 ++++++++
 board/xilinx/common/fru_ops.c   | 263 ++++++++++++++++++++++++++++++++
 board/xilinx/zynqmp/MAINTAINERS |   1 +
 6 files changed, 414 insertions(+)
 create mode 100644 board/xilinx/common/fru.c
 create mode 100644 board/xilinx/common/fru.h
 create mode 100644 board/xilinx/common/fru_ops.c

diff --git a/board/xilinx/Kconfig b/board/xilinx/Kconfig
index 01d7f8eac1c9..51f6d2bac8ad 100644
--- a/board/xilinx/Kconfig
+++ b/board/xilinx/Kconfig
@@ -73,3 +73,11 @@ config ZYNQ_GEM_I2C_MAC_OFFSET
 	  Set the MAC offset for i2C.
 
 endif
+
+config CMD_FRU
+	bool "FRU information for product"
+	help
+	  This option enables FRU commands to capture and display FRU
+	  information present in the device. The FRU Information is used
+	  to primarily to provide "inventory" information about the boards
+	  that the FRU Information Device is located on.
diff --git a/board/xilinx/common/Makefile b/board/xilinx/common/Makefile
index 3600da464b87..212028478c0e 100644
--- a/board/xilinx/common/Makefile
+++ b/board/xilinx/common/Makefile
@@ -5,3 +5,6 @@
 #
 
 obj-y	+= board.o
+ifndef CONFIG_SPL_BUILD
+obj-$(CONFIG_CMD_FRU) += fru.o fru_ops.o
+endif
diff --git a/board/xilinx/common/fru.c b/board/xilinx/common/fru.c
new file mode 100644
index 000000000000..0ab9f2a78bd9
--- /dev/null
+++ b/board/xilinx/common/fru.c
@@ -0,0 +1,73 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * (C) Copyright 2019 - 2020 Xilinx, Inc.
+ */
+
+#include <common.h>
+#include <command.h>
+#include <fdtdec.h>
+#include <malloc.h>
+
+#include "fru.h"
+
+static int do_fru_capture(struct cmd_tbl *cmdtp, int flag, int argc,
+			  char *const argv[])
+{
+	unsigned long addr;
+	char *endp;
+
+	if (argc < cmdtp->maxargs)
+		return CMD_RET_USAGE;
+
+	addr = simple_strtoul(argv[2], &endp, 16);
+	if (*argv[1] == 0 || *endp != 0)
+		return -1;
+
+	return fru_capture(addr);
+}
+
+static int do_fru_display(struct cmd_tbl *cmdtp, int flag, int argc,
+			  char *const argv[])
+{
+	fru_display(1);
+	return CMD_RET_SUCCESS;
+}
+
+static struct cmd_tbl cmd_fru_sub[] = {
+	U_BOOT_CMD_MKENT(capture, 3, 0, do_fru_capture, "", ""),
+	U_BOOT_CMD_MKENT(display, 2, 0, do_fru_display, "", ""),
+};
+
+static int do_fru(struct cmd_tbl *cmdtp, int flag, int argc,
+		  char *const argv[])
+{
+	struct cmd_tbl *c;
+	int ret;
+
+	if (argc < 2)
+		return CMD_RET_USAGE;
+
+	c = find_cmd_tbl(argv[1], &cmd_fru_sub[0],
+			 ARRAY_SIZE(cmd_fru_sub));
+	if (!c)
+		return CMD_RET_USAGE;
+
+	ret = c->cmd(c, flag, argc, argv);
+
+	return cmd_process_error(c, ret);
+}
+
+/***************************************************/
+#ifdef CONFIG_SYS_LONGHELP
+static char fru_help_text[] =
+	"capture <addr> - Parse and capture FRU table present at address.\n"
+	"fru display - Displays content of FRU table that was captured using\n"
+	"              fru capture command\n"
+	;
+#endif
+
+U_BOOT_CMD(
+	fru, 3, 1, do_fru,
+	"FRU table info",
+	fru_help_text
+)
diff --git a/board/xilinx/common/fru.h b/board/xilinx/common/fru.h
new file mode 100644
index 000000000000..a0413cf7f698
--- /dev/null
+++ b/board/xilinx/common/fru.h
@@ -0,0 +1,66 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * (C) Copyright 2019 Xilinx, Inc.
+ * Siva Durga Prasad Paladugu <siva.durga.paladugu@xilinx.com>
+ */
+
+#ifndef __FRU_H
+#define __FRU_H
+
+struct fru_common_hdr {
+	u8 version;
+	u8 off_internal;
+	u8 off_chassis;
+	u8 off_board;
+	u8 off_product;
+	u8 off_multirec;
+	u8 pad;
+	u8 crc;
+};
+
+#define FRU_BOARD_MAX_LEN	32
+
+struct fru_board_data {
+	u8 ver;
+	u8 len;
+	u8 lang_code;
+	u8 time[3];
+	u8 manufacturer_type_len;
+	u8 manufacturer_name[FRU_BOARD_MAX_LEN];
+	u8 product_name_type_len;
+	u8 product_name[FRU_BOARD_MAX_LEN];
+	u8 serial_number_type_len;
+	u8 serial_number[FRU_BOARD_MAX_LEN];
+	u8 part_number_type_len;
+	u8 part_number[FRU_BOARD_MAX_LEN];
+	u8 file_id_type_len;
+	u8 file_id[FRU_BOARD_MAX_LEN];
+};
+
+struct fru_table {
+	bool captured;
+	struct fru_common_hdr hdr;
+	struct fru_board_data brd;
+};
+
+#define FRU_TYPELEN_CODE_MASK	0xC0
+#define FRU_TYPELEN_LEN_MASK	0x3F
+#define FRU_COMMON_HDR_VER_MASK		0xF
+#define FRU_COMMON_HDR_LEN_MULTIPLIER	8
+#define FRU_LANG_CODE_ENGLISH		0
+#define FRU_LANG_CODE_ENGLISH_1		25
+#define FRU_TYPELEN_EOF			0xC1
+
+/* This should be minimum of fields */
+#define FRU_BOARD_AREA_TOTAL_FIELDS	5
+#define FRU_TYPELEN_TYPE_SHIFT		6
+#define FRU_TYPELEN_TYPE_BINARY		0
+#define FRU_TYPELEN_TYPE_ASCII8		3
+
+int fru_display(int verbose);
+int fru_capture(unsigned long addr);
+u8 fru_checksum(u8 *addr, u8 len);
+
+extern struct fru_table fru_data;
+
+#endif /* FRU_H */
diff --git a/board/xilinx/common/fru_ops.c b/board/xilinx/common/fru_ops.c
new file mode 100644
index 000000000000..491e92a7afba
--- /dev/null
+++ b/board/xilinx/common/fru_ops.c
@@ -0,0 +1,263 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * (C) Copyright 2019 - 2020 Xilinx, Inc.
+ */
+
+#include <common.h>
+#include <cpu_func.h>
+#include <env.h>
+#include <fdtdec.h>
+#include <log.h>
+#include <malloc.h>
+#include <asm/io.h>
+#include <asm/arch/hardware.h>
+
+#include "fru.h"
+
+struct fru_table fru_data __section(.data);
+
+static u16 fru_cal_area_len(u8 len)
+{
+	return len * FRU_COMMON_HDR_LEN_MULTIPLIER;
+}
+
+static u8 fru_version(u8 ver)
+{
+	return ver & FRU_COMMON_HDR_VER_MASK;
+}
+
+static int fru_check_language(u8 code)
+{
+	if (code != FRU_LANG_CODE_ENGLISH && code != FRU_LANG_CODE_ENGLISH_1) {
+		printf("FRU_ERROR: Only English Language is supported\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+u8 fru_checksum(u8 *addr, u8 len)
+{
+	u8 checksum = 0;
+
+	while (len--) {
+		checksum += *addr;
+		addr++;
+	}
+
+	return checksum;
+}
+
+static int fru_check_type_len(u8 type_len, u8 language, u8 *type)
+{
+	int len;
+
+	if (type_len == FRU_TYPELEN_EOF)
+		return -EINVAL;
+
+	*type = (type_len & FRU_TYPELEN_CODE_MASK) >> FRU_TYPELEN_TYPE_SHIFT;
+
+	len = type_len & FRU_TYPELEN_LEN_MASK;
+
+	return len;
+}
+
+static int fru_parse_board(unsigned long addr)
+{
+	u8 i, type;
+	int len;
+	u8 *data, *term;
+
+	memcpy(&fru_data.brd.ver, (void *)addr, 6);
+	addr += 6;
+	data = (u8 *)&fru_data.brd.manufacturer_type_len;
+
+	for (i = 0; ; i++, data += FRU_BOARD_MAX_LEN) {
+		len = fru_check_type_len(*(u8 *)addr, fru_data.brd.lang_code,
+					 &type);
+		/*
+		 * Stop cature if it end of fields
+		 */
+		if (len == -EINVAL)
+			break;
+
+		/* This record type/len field */
+		*data++ = *(u8 *)addr;
+
+		/* Add offset to match data */
+		addr += 1;
+
+		/* If len is 0 it means empty field that's why skip writing */
+		if (!len)
+			continue;
+
+		/* Record data field */
+		memcpy(data, (u8 *)addr, len);
+		term = data + (u8)len;
+		*term = 0;
+		addr += len;
+	}
+
+	if (i < FRU_BOARD_AREA_TOTAL_FIELDS) {
+		printf("Board area require minimum %d fields\n",
+		       FRU_BOARD_AREA_TOTAL_FIELDS);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+int fru_capture(unsigned long addr)
+{
+	struct fru_common_hdr *hdr;
+	u8 checksum = 0;
+
+	checksum = fru_checksum((u8 *)addr, sizeof(struct fru_common_hdr));
+	if (checksum) {
+		printf("%s Common header CRC error\n", __func__);
+		return -EINVAL;
+	}
+
+	hdr = (struct fru_common_hdr *)addr;
+
+	memcpy((void *)&fru_data.hdr, (void *)hdr,
+	       sizeof(struct fru_common_hdr));
+
+	fru_data.captured = true;
+
+	if (hdr->off_board) {
+		addr += fru_cal_area_len(hdr->off_board);
+		fru_parse_board(addr);
+	}
+
+	env_set_hex("fru_addr", addr);
+
+	return 0;
+}
+
+static int fru_display_board(struct fru_board_data *brd, int verbose)
+{
+	u32 time = 0;
+	u8 type;
+	int len;
+	u8 *data;
+	static const char * const typecode[] = {
+		"Binary/Unspecified",
+		"BCD plus",
+		"6-bit ASCII",
+		"8-bit ASCII",
+		"2-byte UNICODE"
+	};
+	static const char * const boardinfo[] = {
+		"Manufacturer Name",
+		"Product Name",
+		"Serial No",
+		"Part Number",
+		"File ID"
+	};
+
+	if (verbose) {
+		printf("*****BOARD INFO*****\n");
+		printf("Version:%d\n", fru_version(brd->ver));
+		printf("Board Area Length:%d\n", fru_cal_area_len(brd->len));
+	}
+
+	if (fru_check_language(brd->lang_code))
+		return -EINVAL;
+
+	time = brd->time[2] << 16 | brd->time[1] << 8 |
+	       brd->time[0];
+
+	if (verbose)
+		printf("Time in Minutes from 0:00hrs 1/1/96: %d\n", time);
+
+	data = (u8 *)&brd->manufacturer_type_len;
+
+	for (u8 i = 0; i < (sizeof(boardinfo) / sizeof(*boardinfo)); i++) {
+		len = fru_check_type_len(*data++, brd->lang_code,
+					 &type);
+		if (len == -EINVAL) {
+			printf("**** EOF for Board Area ****\n");
+			break;
+		}
+
+		if (type <= FRU_TYPELEN_TYPE_ASCII8 &&
+		    (brd->lang_code == FRU_LANG_CODE_ENGLISH ||
+		     brd->lang_code == FRU_LANG_CODE_ENGLISH_1))
+			debug("Type code: %s\n", typecode[type]);
+		else
+			debug("Type code: %s\n", typecode[type + 1]);
+
+		if (!len) {
+			debug("%s not found\n", boardinfo[i]);
+			continue;
+		}
+
+		switch (type) {
+		case FRU_TYPELEN_TYPE_BINARY:
+			debug("Length: %d\n", len);
+			printf(" %s: 0x%x\n", boardinfo[i], *data);
+			break;
+		case FRU_TYPELEN_TYPE_ASCII8:
+			debug("Length: %d\n", len);
+			printf(" %s: %s\n", boardinfo[i], data);
+			break;
+		default:
+			debug("Unsupported type %x\n", type);
+		}
+
+		data += FRU_BOARD_MAX_LEN;
+	}
+
+	return 0;
+}
+
+static void fru_display_common_hdr(struct fru_common_hdr *hdr, int verbose)
+{
+	if (!verbose)
+		return;
+
+	printf("*****COMMON HEADER*****\n");
+	printf("Version:%d\n", fru_version(hdr->version));
+	if (hdr->off_internal)
+		printf("Internal Use Area Offset:%d\n",
+		       fru_cal_area_len(hdr->off_internal));
+	else
+		printf("*** No Internal Area ***\n");
+
+	if (hdr->off_chassis)
+		printf("Chassis Info Area Offset:%d\n",
+		       fru_cal_area_len(hdr->off_chassis));
+	else
+		printf("*** No Chassis Info Area ***\n");
+
+	if (hdr->off_board)
+		printf("Board Area Offset:%d\n",
+		       fru_cal_area_len(hdr->off_board));
+	else
+		printf("*** No Board Area ***\n");
+
+	if (hdr->off_product)
+		printf("Product Info Area Offset:%d\n",
+		       fru_cal_area_len(hdr->off_product));
+	else
+		printf("*** No Product Info Area ***\n");
+
+	if (hdr->off_multirec)
+		printf("MultiRecord Area Offset:%d\n",
+		       fru_cal_area_len(hdr->off_multirec));
+	else
+		printf("*** No MultiRecord Area ***\n");
+}
+
+int fru_display(int verbose)
+{
+	if (!fru_data.captured) {
+		printf("FRU data not available please run fru parse\n");
+		return -EINVAL;
+	}
+
+	fru_display_common_hdr(&fru_data.hdr, verbose);
+
+	return fru_display_board(&fru_data.brd, verbose);
+}
diff --git a/board/xilinx/zynqmp/MAINTAINERS b/board/xilinx/zynqmp/MAINTAINERS
index 04fc7f32fe86..9cd4f3f53efd 100644
--- a/board/xilinx/zynqmp/MAINTAINERS
+++ b/board/xilinx/zynqmp/MAINTAINERS
@@ -3,6 +3,7 @@ M:	Michal Simek <michal.simek@xilinx.com>
 S:	Maintained
 F:	arch/arm/dts/zynqmp-*
 F:	arch/arm/dts/avnet-ultra96*
+F:	board/xilinx/common/
 F:	board/xilinx/zynqmp/
 F:	include/configs/xilinx_zynqmp*
 F:	configs/xilinx_zynqmp*
-- 
2.28.0

^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH 3/4] xilinx: cmd: Add basic fru format generator
  2020-10-20 14:50 [PATCH 0/4] Add support for I2C Legacy/FRU decoding Michal Simek
  2020-10-20 14:50 ` [PATCH 1/4] xilinx: common: Add Makefile to common folder Michal Simek
  2020-10-20 14:50 ` [PATCH 2/4] xilinx: cmd: Add support for FRU commands Michal Simek
@ 2020-10-20 14:50 ` Michal Simek
  2020-10-20 14:50 ` [PATCH 4/4] xilinx: board: Add FRU decoder support Michal Simek
  2020-10-27  7:25 ` [PATCH 0/4] Add support for I2C Legacy/FRU decoding Michal Simek
  4 siblings, 0 replies; 6+ messages in thread
From: Michal Simek @ 2020-10-20 14:50 UTC (permalink / raw)
  To: u-boot

Idea is to have something what can be used for board bringup from
generic board perspective.

There is a violation compare to spec that FRU ID is ASCII8 instead of
binary format but this is really for having something to pass boot and
boot to OS which has better generating options.
Also time should be filled properly.

For example:
fru board_gen 1000 XILINX versal-x-prc-01-revA serialX partX

There is also support for revision field which is Xilinx specific field.

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

 board/xilinx/common/fru.c     |  20 ++++++-
 board/xilinx/common/fru.h     |  17 ++++++
 board/xilinx/common/fru_ops.c | 101 +++++++++++++++++++++++++++++++++-
 3 files changed, 136 insertions(+), 2 deletions(-)

diff --git a/board/xilinx/common/fru.c b/board/xilinx/common/fru.c
index 0ab9f2a78bd9..ccf48723ff89 100644
--- a/board/xilinx/common/fru.c
+++ b/board/xilinx/common/fru.c
@@ -33,9 +33,23 @@ static int do_fru_display(struct cmd_tbl *cmdtp, int flag, int argc,
 	return CMD_RET_SUCCESS;
 }
 
+static int do_fru_generate(struct cmd_tbl *cmdtp, int flag, int argc,
+			   char *const argv[])
+{
+	unsigned long addr;
+
+	if (argc < cmdtp->maxargs)
+		return CMD_RET_USAGE;
+
+	addr = simple_strtoul(argv[2], NULL, 16);
+
+	return fru_generate(addr, argv[3], argv[4], argv[5], argv[6], argv[7]);
+}
+
 static struct cmd_tbl cmd_fru_sub[] = {
 	U_BOOT_CMD_MKENT(capture, 3, 0, do_fru_capture, "", ""),
 	U_BOOT_CMD_MKENT(display, 2, 0, do_fru_display, "", ""),
+	U_BOOT_CMD_MKENT(board_gen, 8, 0, do_fru_generate, "", ""),
 };
 
 static int do_fru(struct cmd_tbl *cmdtp, int flag, int argc,
@@ -63,11 +77,15 @@ static char fru_help_text[] =
 	"capture <addr> - Parse and capture FRU table present at address.\n"
 	"fru display - Displays content of FRU table that was captured using\n"
 	"              fru capture command\n"
+	"fru board_gen <addr> <manufacturer> <board name> <serial number>\n"
+	"              <part number> <revision> - Generate FRU format with\n"
+	"              board info area filled based on parameters. <addr> is\n"
+	"              pointing to place where FRU is generated.\n"
 	;
 #endif
 
 U_BOOT_CMD(
-	fru, 3, 1, do_fru,
+	fru, 8, 1, do_fru,
 	"FRU table info",
 	fru_help_text
 )
diff --git a/board/xilinx/common/fru.h b/board/xilinx/common/fru.h
index a0413cf7f698..a3e652025714 100644
--- a/board/xilinx/common/fru.h
+++ b/board/xilinx/common/fru.h
@@ -20,6 +20,18 @@ struct fru_common_hdr {
 
 #define FRU_BOARD_MAX_LEN	32
 
+struct __packed fru_board_info_header {
+	u8 ver;
+	u8 len;
+	u8 lang_code;
+	u8 time[3];
+};
+
+struct __packed fru_board_info_member {
+	u8 type_len;
+	u8 *name;
+};
+
 struct fru_board_data {
 	u8 ver;
 	u8 len;
@@ -35,6 +47,9 @@ struct fru_board_data {
 	u8 part_number[FRU_BOARD_MAX_LEN];
 	u8 file_id_type_len;
 	u8 file_id[FRU_BOARD_MAX_LEN];
+	/* Xilinx custom fields */
+	u8 rev_type_len;
+	u8 rev[FRU_BOARD_MAX_LEN];
 };
 
 struct fru_table {
@@ -59,6 +74,8 @@ struct fru_table {
 
 int fru_display(int verbose);
 int fru_capture(unsigned long addr);
+int fru_generate(unsigned long addr, char *manufacturer, char *board_name,
+		 char *serial_no, char *part_no, char *revision);
 u8 fru_checksum(u8 *addr, u8 len);
 
 extern struct fru_table fru_data;
diff --git a/board/xilinx/common/fru_ops.c b/board/xilinx/common/fru_ops.c
index 491e92a7afba..fc3add7d93da 100644
--- a/board/xilinx/common/fru_ops.c
+++ b/board/xilinx/common/fru_ops.c
@@ -62,6 +62,103 @@ static int fru_check_type_len(u8 type_len, u8 language, u8 *type)
 	return len;
 }
 
+/* Return len */
+static u8 fru_gen_type_len(u8 *addr, char *name)
+{
+	int len = strlen(name);
+	struct fru_board_info_member *member;
+
+	member = (struct fru_board_info_member *)addr;
+	member->type_len = FRU_TYPELEN_TYPE_ASCII8 << FRU_TYPELEN_TYPE_SHIFT;
+	member->type_len |= len;
+
+	debug("%lx/%lx: Add %s to 0x%lx (len 0x%x)\n", (ulong)addr,
+	      (ulong)&member->type_len,  name, (ulong)&member->name, len);
+	memcpy(&member->name, name, len);
+
+	/* Add +1 for type_len parameter */
+	return 1 + len;
+}
+
+int fru_generate(unsigned long addr, char *manufacturer, char *board_name,
+		 char *serial_no, char *part_no, char *revision)
+{
+	struct fru_common_hdr *header = (struct fru_common_hdr *)addr;
+	struct fru_board_info_header *board_info;
+	u8 *member;
+	u8 len, pad, modulo;
+
+	header->version = 1; /* Only version 1.0 is supported now */
+	header->off_internal = 0; /* not present */
+	header->off_chassis = 0; /* not present */
+	header->off_board = (sizeof(*header)) / 8; /* Starting offset 8 */
+	header->off_product = 0; /* not present */
+	header->off_multirec = 0; /* not present */
+	header->pad = 0;
+	/*
+	 * This unsigned byte can be used to calculate a zero checksum
+	 * for the data area following the header. I.e. the modulo 256 sum of
+	 * the record data bytes plus the checksum byte equals zero.
+	 */
+	header->crc = 0; /* Clear before calculation */
+	header->crc = 0 - fru_checksum((u8 *)header, sizeof(*header));
+
+	/* board info is just right after header */
+	board_info = (void *)((u8 *)header + sizeof(*header));
+
+	debug("header %lx, board_info %lx\n", (ulong)header, (ulong)board_info);
+
+	board_info->ver = 1; /* 1.0 spec */
+	board_info->lang_code = 0; /* English */
+	board_info->time[0] = 0; /* unspecified */
+	board_info->time[1] = 0; /* unspecified */
+	board_info->time[2] = 0; /* unspecified */
+
+	/* Member fields are just after board_info header */
+	member = (u8 *)board_info + sizeof(*board_info);
+
+	len = fru_gen_type_len(member, manufacturer); /* Board Manufacturer */
+	member += len;
+	len = fru_gen_type_len(member, board_name); /* Board Product name */
+	member += len;
+	len = fru_gen_type_len(member, serial_no); /* Board Serial number */
+	member += len;
+	len = fru_gen_type_len(member, part_no); /* Board part number */
+	member += len;
+	len = fru_gen_type_len(member, "U-Boot generator"); /* File ID */
+	member += len;
+	len = fru_gen_type_len(member, revision); /* Revision */
+	member += len;
+
+	*member++ = 0xc1; /* Indication of no more fields */
+
+	len = member - (u8 *)board_info; /* Find current length */
+	len += 1; /* Add checksum there too for calculation */
+
+	modulo = len % 8;
+
+	if (modulo) {
+		/* Do not fill last item which is checksum */
+		for (pad = 0; pad < 8 - modulo; pad++)
+			*member++ = 0;
+
+		/* Increase structure size */
+		len += 8 - modulo;
+	}
+
+	board_info->len = len / 8; /* Size in multiples of 8 bytes */
+
+	*member = 0; /* Clear before calculation */
+	*member = 0 - fru_checksum((u8 *)board_info, len);
+
+	debug("checksum %x(addr %x)\n", *member, len);
+
+	env_set_hex("fru_addr", addr);
+	env_set_hex("filesize", (unsigned long)member - addr + 1);
+
+	return 0;
+}
+
 static int fru_parse_board(unsigned long addr)
 {
 	u8 i, type;
@@ -153,7 +250,9 @@ static int fru_display_board(struct fru_board_data *brd, int verbose)
 		"Product Name",
 		"Serial No",
 		"Part Number",
-		"File ID"
+		"File ID",
+		/* Xilinx spec */
+		"Revision Number",
 	};
 
 	if (verbose) {
-- 
2.28.0

^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH 4/4] xilinx: board: Add FRU decoder support
  2020-10-20 14:50 [PATCH 0/4] Add support for I2C Legacy/FRU decoding Michal Simek
                   ` (2 preceding siblings ...)
  2020-10-20 14:50 ` [PATCH 3/4] xilinx: cmd: Add basic fru format generator Michal Simek
@ 2020-10-20 14:50 ` Michal Simek
  2020-10-27  7:25 ` [PATCH 0/4] Add support for I2C Legacy/FRU decoding Michal Simek
  4 siblings, 0 replies; 6+ messages in thread
From: Michal Simek @ 2020-10-20 14:50 UTC (permalink / raw)
  To: u-boot

FMC cards are using FRU format for card identification. That's why add
support for this format.

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

 board/xilinx/common/board.c | 83 ++++++++++++++++++++++++++++++++++++-
 1 file changed, 82 insertions(+), 1 deletion(-)

diff --git a/board/xilinx/common/board.c b/board/xilinx/common/board.c
index 0d0c21ca3d40..74e81c18bd5a 100644
--- a/board/xilinx/common/board.c
+++ b/board/xilinx/common/board.c
@@ -17,6 +17,8 @@
 #include <i2c_eeprom.h>
 #include <net.h>
 
+#include "fru.h"
+
 #if defined(CONFIG_ZYNQ_GEM_I2C_MAC_OFFSET)
 int zynq_board_read_rom_ethaddr(unsigned char *ethaddr)
 {
@@ -65,7 +67,7 @@ struct xilinx_board_description {
 static int highest_id = -1;
 static struct xilinx_board_description **board_info;
 
-#define XILINX_I2C_DETECTION_BITS	8
+#define XILINX_I2C_DETECTION_BITS	sizeof(struct fru_common_hdr)
 
 /* Variable which stores pointer to array which stores eeprom content */
 struct xilinx_legacy_format {
@@ -160,6 +162,82 @@ static bool xilinx_detect_legacy(u8 *buffer)
 	return true;
 }
 
+static int xilinx_read_eeprom_fru(struct udevice *dev, char *name,
+				  struct xilinx_board_description *desc)
+{
+	int ret, eeprom_size;
+	u8 *fru_content;
+
+	/* FIXME this is shortcut - if eeprom type is wrong it will fail */
+	eeprom_size = i2c_eeprom_size(dev);
+
+	fru_content = calloc(1, eeprom_size);
+	if (!fru_content)
+		return -ENOMEM;
+
+	debug("%s: I2C EEPROM read pass data at %p\n", __func__,
+	      fru_content);
+
+	ret = dm_i2c_read(dev, 0, (uchar *)fru_content,
+			  eeprom_size);
+	if (ret) {
+		debug("%s: I2C EEPROM read failed\n", __func__);
+		free(fru_content);
+		return ret;
+	}
+
+	printf("Xilinx I2C FRU format at %s:\n", name);
+	fru_capture((unsigned long)fru_content);
+	ret = fru_display(0);
+	if (ret) {
+		printf("FRU format decoding failed.\n");
+		return ret;
+	}
+
+	if (desc->header == EEPROM_HEADER_MAGIC) {
+		debug("Information already filled\n");
+		return -EINVAL;
+	}
+
+	/* It is clear that FRU was captured and structures were filled */
+	strncpy(desc->manufacturer, (char *)fru_data.brd.manufacturer_name,
+		sizeof(desc->manufacturer));
+	strncpy(desc->name, (char *)fru_data.brd.product_name,
+		sizeof(desc->name));
+	strncpy(desc->revision, (char *)fru_data.brd.rev,
+		sizeof(desc->revision));
+	strncpy(desc->serial, (char *)fru_data.brd.serial_number,
+		sizeof(desc->serial));
+	desc->header = EEPROM_HEADER_MAGIC;
+
+	return 0;
+}
+
+static bool xilinx_detect_fru(u8 *buffer)
+{
+	u8 checksum = 0;
+	int i;
+
+	checksum = fru_checksum((u8 *)buffer, sizeof(struct fru_common_hdr));
+	if (checksum) {
+		debug("%s Common header CRC FAIL\n", __func__);
+		return false;
+	}
+
+	bool all_zeros = true;
+	/* Checksum over all zeros is also zero that's why detect this case */
+	for (i = 0; i < sizeof(struct fru_common_hdr); i++) {
+		if (buffer[i] != 0)
+			all_zeros = false;
+	}
+
+	if (all_zeros)
+		return false;
+
+	debug("%s Common header CRC PASS\n", __func__);
+	return true;
+}
+
 static int xilinx_read_eeprom_single(char *name,
 				     struct xilinx_board_description *desc)
 {
@@ -184,6 +262,9 @@ static int xilinx_read_eeprom_single(char *name,
 
 	debug("%s: i2c memory detected: %s\n", __func__, name);
 
+	if (CONFIG_IS_ENABLED(CMD_FRU) && xilinx_detect_fru(buffer))
+		return xilinx_read_eeprom_fru(dev, name, desc);
+
 	if (xilinx_detect_legacy(buffer))
 		return xilinx_read_eeprom_legacy(dev, name, desc);
 
-- 
2.28.0

^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH 0/4] Add support for I2C Legacy/FRU decoding
  2020-10-20 14:50 [PATCH 0/4] Add support for I2C Legacy/FRU decoding Michal Simek
                   ` (3 preceding siblings ...)
  2020-10-20 14:50 ` [PATCH 4/4] xilinx: board: Add FRU decoder support Michal Simek
@ 2020-10-27  7:25 ` Michal Simek
  4 siblings, 0 replies; 6+ messages in thread
From: Michal Simek @ 2020-10-27  7:25 UTC (permalink / raw)
  To: u-boot

?t 20. 10. 2020 v 16:50 odes?latel Michal Simek
<michal.simek@xilinx.com> napsal:
>
> Hi,
>
> the whole series add support for I2C based FRU eeprom board identification.
> EEPROMs are referenced by nvmem alias which was suggested by Rob Herring
> (dt maintainer). Previous code was using chosen xlnx,eeprom property.
> And because no platform is setting up nvmem alias the code is doing nothing
> on all boards.
>
> If you want to try it and test it just choose board and define nvmem
> aliases and you should see similar logs like this.
>
> zcu104 with legacy format and FRU on FMC
>
> U-Boot 2020.10-00835-g819dc03ff784 (Oct 20 2020 - 12:48:12 +0200)
>
> Model: ZynqMP ZCU104 RevC
> Board: Xilinx ZynqMP
> DRAM:  2 GiB
> PMUFW:  v1.1
> Xilinx I2C Legacy format at nvmem0:
>  Board name:    zcu104
>  Board rev:     c
>  Board SN:      895527361843-94820
>  Ethernet mac:  00:0a:35:04:eb:93
> Xilinx I2C FRU format at nvmem1:
>  Manufacturer Name: XILINX
>  Product Name: XXX-XXX
>  Serial No: 1231
>  Part Number: dd
>  File ID: U-Boot generator
>  Revision Number: rev_A
> EL Level:       EL2
> Chip ID:        zu7e
> WDT:   Started with servicing (60s timeout)
> NAND:  0 MiB
> MMC:   mmc at ff170000: 0
> Loading Environment from FAT... *** Warning - bad CRC, using default environment
>
> In:    serial at ff000000
> Out:   serial at ff000000
> Err:   serial at ff000000
> Bootmode: LVL_SHFT_SD_MODE1
> Reset reason:   EXTERNAL
> Net:
> ZYNQ GEM: ff0e0000, mdio bus ff0e0000, phyaddr 12, interface rgmii-id
> eth0: ethernet at ff0e0000
> Hit any key to stop autoboot:  0
> ZynqMP>
>
> Based on https://lists.denx.de/pipermail/u-boot/2020-October/429382.html
>
> Thanks,
> Michal
>
>
> Michal Simek (3):
>   xilinx: common: Add Makefile to common folder
>   xilinx: cmd: Add basic fru format generator
>   xilinx: board: Add FRU decoder support
>
> Siva Durga Prasad Paladugu (1):
>   xilinx: cmd: Add support for FRU commands
>
>  board/xilinx/Kconfig            |   8 +
>  board/xilinx/common/Makefile    |  10 +
>  board/xilinx/common/board.c     |  83 +++++++-
>  board/xilinx/common/fru.c       |  91 ++++++++
>  board/xilinx/common/fru.h       |  83 ++++++++
>  board/xilinx/common/fru_ops.c   | 362 ++++++++++++++++++++++++++++++++
>  board/xilinx/versal/Makefile    |   1 -
>  board/xilinx/zynq/Makefile      |   1 -
>  board/xilinx/zynqmp/MAINTAINERS |   1 +
>  board/xilinx/zynqmp/Makefile    |   1 -
>  10 files changed, 637 insertions(+), 4 deletions(-)
>  create mode 100644 board/xilinx/common/Makefile
>  create mode 100644 board/xilinx/common/fru.c
>  create mode 100644 board/xilinx/common/fru.h
>  create mode 100644 board/xilinx/common/fru_ops.c
>
> --
> 2.28.0
>

Applied.
M


-- 
Michal Simek, Ing. (M.Eng), OpenPGP -> KeyID: FE3D1F91
w: www.monstr.eu p: +42-0-721842854
Maintainer of Linux kernel - Xilinx Microblaze
Maintainer of Linux kernel - Xilinx Zynq ARM and ZynqMP ARM64 SoCs
U-Boot custodian - Xilinx Microblaze/Zynq/ZynqMP/Versal SoCs

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2020-10-27  7:25 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-10-20 14:50 [PATCH 0/4] Add support for I2C Legacy/FRU decoding Michal Simek
2020-10-20 14:50 ` [PATCH 1/4] xilinx: common: Add Makefile to common folder Michal Simek
2020-10-20 14:50 ` [PATCH 2/4] xilinx: cmd: Add support for FRU commands Michal Simek
2020-10-20 14:50 ` [PATCH 3/4] xilinx: cmd: Add basic fru format generator Michal Simek
2020-10-20 14:50 ` [PATCH 4/4] xilinx: board: Add FRU decoder support Michal Simek
2020-10-27  7:25 ` [PATCH 0/4] Add support for I2C Legacy/FRU decoding Michal Simek

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.