aboutsummaryrefslogtreecommitdiff
path: root/board
diff options
context:
space:
mode:
authorSiva Durga Prasad Paladugu <siva.durga.paladugu@xilinx.com>2019-04-10 12:38:10 +0530
committerMichal Simek <michal.simek@xilinx.com>2020-10-27 08:13:32 +0100
commitf1b97b5f18a8e03c534cb7dbde22cd23d5d753f7 (patch)
treedc3359215948a7c309d56c5ab16394bcee3e4e42 /board
parent3e315f31cc74e897aebb180aa43d572f5d289949 (diff)
downloadu-boot-f1b97b5f18a8e03c534cb7dbde22cd23d5d753f7.zip
u-boot-f1b97b5f18a8e03c534cb7dbde22cd23d5d753f7.tar.gz
u-boot-f1b97b5f18a8e03c534cb7dbde22cd23d5d753f7.tar.bz2
xilinx: cmd: Add support for FRU commands
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>
Diffstat (limited to 'board')
-rw-r--r--board/xilinx/Kconfig8
-rw-r--r--board/xilinx/common/Makefile3
-rw-r--r--board/xilinx/common/fru.c73
-rw-r--r--board/xilinx/common/fru.h66
-rw-r--r--board/xilinx/common/fru_ops.c263
-rw-r--r--board/xilinx/zynqmp/MAINTAINERS1
6 files changed, 414 insertions, 0 deletions
diff --git a/board/xilinx/Kconfig b/board/xilinx/Kconfig
index 01d7f8e..51f6d2b 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 3600da4..2120284 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 0000000..0ab9f2a
--- /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 0000000..a0413cf
--- /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 0000000..491e92a
--- /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 04fc7f3..9cd4f3f 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*