]> git.ipfire.org Git - thirdparty/u-boot.git/commitdiff
cmd: fru: Add support for FRU commands
authorSiva Durga Prasad Paladugu <siva.durga.paladugu@xilinx.com>
Wed, 10 Apr 2019 07:08:10 +0000 (12:38 +0530)
committerMichal Simek <michal.simek@xilinx.com>
Thu, 6 Jun 2019 11:46:52 +0000 (13:46 +0200)
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, its supports only English language code and ASCII8 format.

Signed-off-by: Siva Durga Prasad Paladugu <siva.durga.paladugu@xilinx.com>
Signed-off-by: Michal Simek <michal.simek@xilinx.com>
cmd/Kconfig
cmd/Makefile
cmd/fru.c [new file with mode: 0644]
common/Makefile
common/fru_ops.c [new file with mode: 0644]
include/fru.h [new file with mode: 0644]

index ea1a325eb3018218bac3f124a31cd8cdbcceccdb..416e0ec95304403dd2a5dc69ce49f83074bc0f72 100644 (file)
@@ -1485,6 +1485,14 @@ config CMD_UUID
          The two commands are very similar except for the endianness of the
          output.
 
+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.
+
 endmenu
 
 source "cmd/ti/Kconfig"
index 15ae4d250f50a06a9b61d44dcee958475bd91160..b7e41388791d50893d7286323a694138a859500b 100644 (file)
@@ -41,6 +41,7 @@ obj-$(CONFIG_DATAFLASH_MMC_SELECT) += dataflash_mmc_mux.o
 obj-$(CONFIG_CMD_DATE) += date.o
 obj-$(CONFIG_CMD_DEMO) += demo.o
 obj-$(CONFIG_CMD_DM) += dm.o
+obj-$(CONFIG_CMD_FRU) += fru.o
 obj-$(CONFIG_CMD_SOUND) += sound.o
 ifdef CONFIG_POST
 obj-$(CONFIG_CMD_DIAG) += diag.o
diff --git a/cmd/fru.c b/cmd/fru.c
new file mode 100644 (file)
index 0000000..10b6e6f
--- /dev/null
+++ b/cmd/fru.c
@@ -0,0 +1,73 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * (C) Copyright 2019 Xilinx, Inc.
+ *
+ */
+
+#include <common.h>
+#include <fdtdec.h>
+#include <malloc.h>
+#include <fru.h>
+
+static int do_fru_capture(cmd_tbl_t *cmdtp, int flag, int argc,
+                         char *const argv[])
+{
+       unsigned long addr;
+       char *endp;
+       int ret;
+
+       if (argc < 3)
+               return CMD_RET_USAGE;
+
+       addr = simple_strtoul(argv[2], &endp, 16);
+       if (*argv[1] == 0 || *endp != 0)
+               return -1;
+
+       ret = fru_capture(addr);
+
+       return ret;
+}
+
+static int do_fru_display(cmd_tbl_t *cmdtp, int flag, int argc,
+                         char *const argv[])
+{
+       fru_display();
+       return CMD_RET_SUCCESS;
+}
+
+static cmd_tbl_t 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(cmd_tbl_t *cmdtp, int flag, int argc,
+                 char *const argv[])
+{
+       cmd_tbl_t *c;
+
+       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 c->cmd(c, flag, argc, argv);
+
+       return CMD_RET_USAGE;
+}
+
+/***************************************************/
+#ifdef CONFIG_SYS_LONGHELP
+static char fru_help_text[] =
+       "capture <addr>- Parse and capture FRU table present at address.\n"
+       "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
+)
index 0de60b3ced2c626d737791af326f286719c67c2a..bffa974d65c4788c7057a0f922cf05eddea500c1 100644 (file)
@@ -57,6 +57,7 @@ obj-$(CONFIG_UPDATE_TFTP) += update.o
 obj-$(CONFIG_DFU_TFTP) += update.o
 obj-$(CONFIG_USB_KEYBOARD) += usb_kbd.o
 obj-$(CONFIG_CMDLINE) += cli_readline.o cli_simple.o
+obj-$(CONFIG_CMD_FRU) += fru_ops.o
 
 endif # !CONFIG_SPL_BUILD
 
diff --git a/common/fru_ops.c b/common/fru_ops.c
new file mode 100644 (file)
index 0000000..baa37a8
--- /dev/null
@@ -0,0 +1,286 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * (C) Copyright 2019 Xilinx, Inc.
+ *
+ */
+
+#include <common.h>
+#include <fdtdec.h>
+#include <malloc.h>
+#include <asm/io.h>
+#include <asm/arch/hardware.h>
+#include "linux/crc8.h"
+
+DECLARE_GLOBAL_DATA_PTR;
+
+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 manuf_type_len;
+       u8 manuf_name[FRU_BOARD_MAX_LEN];
+       u8 prd_name_type_len;
+       u8 product_name[FRU_BOARD_MAX_LEN];
+       u8 prd_part_type_len;
+       u8 product_part[FRU_BOARD_MAX_LEN];
+       u8 prd_ver_type_len;
+       u8 product_ver[FRU_BOARD_MAX_LEN];
+       u8 prd_serial_type_len;
+       u8 product_serial[FRU_BOARD_MAX_LEN];
+       u8 asset_tag_type_len;
+       u8 asset_tag[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
+
+#define FRU_BOARD_AREA_TOTAL_FIELDS    6
+#define FRU_TYPELEN_TYPE_SHIFT         6
+#define FRU_TYPELEN_TYPE_ASCII8                3
+
+struct fru_table fru_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 code;
+}
+
+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;
+
+       memcpy(&fru_data.brd.ver, (void *)addr, 6);
+       addr += 6;
+       data = (u8 *)&fru_data.brd.manuf_type_len;
+
+       for (i = 0; i < FRU_BOARD_AREA_TOTAL_FIELDS; i++,
+            data += FRU_BOARD_MAX_LEN) {
+               *data++ = *(u8 *)addr;
+               len = fru_check_type_len(*(u8 *)addr, fru_data.brd.lang_code,
+                                        &type);
+               /*
+                * Stop cature if it end of fields
+                */
+               if (len == -EINVAL)
+                       return 0;
+
+               /*
+                * Dont capture data if type is not ASCII8
+                */
+               if (type != FRU_TYPELEN_TYPE_ASCII8)
+                       return 0;
+
+               addr += 1;
+               if (!len)
+                       continue;
+               memcpy(data, (u8 *)addr, len);
+               addr += len;
+       }
+
+       return 0;
+}
+
+int fru_capture(unsigned long addr)
+{
+       struct fru_common_hdr *hdr;
+       u8 crc = 0;
+       u8 len;
+       u8 *temp = (u8 *)addr;
+
+       hdr = (struct fru_common_hdr *)addr;
+       len = sizeof(struct fru_common_hdr);
+
+       while(len--) {
+               crc += *temp;
+               temp++;
+       }
+
+       if (crc) {
+               printf("%s Common header CRC error\n", __func__);
+               return -EINVAL;
+       }
+
+       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);
+       }
+
+       return 0;
+}
+
+static int fru_display_board(void)
+{
+       u32 time = 0;
+       u8 type;
+       int len;
+       u8 *data;
+       const char *typecode[] = {
+               "Binary/Unspecified",
+               "BCD plus",
+               "6-bit ASCII",
+               "8-bit ASCII",
+               "2-byte UNICODE"
+       };
+       const char *boardinfo[] = {
+               "Manufacturer Name",
+               "Product Name",
+               "Product Model/Part No",
+               "Product Version",
+               "Product Serial No",
+               "Asset Tag"
+       };
+
+       printf("*****BOARD INFO*****\n");
+       printf("Version:%d\n", fru_version(fru_data.brd.ver));
+       printf("Board Area Length:%d\n", fru_cal_area_len(fru_data.brd.len));
+
+       if (fru_check_language(fru_data.brd.lang_code))
+               return 0;
+
+       time = fru_data.brd.time[2] << 16 | fru_data.brd.time[1] << 8 |
+              fru_data.brd.time[0];
+       printf("Time in Minutes from 0:00hrs 1/1/96 %d\n", time);
+
+       data = (u8 *)&fru_data.brd.manuf_type_len;
+
+       for (u8 i = 0; i < FRU_BOARD_AREA_TOTAL_FIELDS; i++) {
+               len = fru_check_type_len(*data++, fru_data.brd.lang_code,
+                                        &type);
+
+               if (type <= FRU_TYPELEN_TYPE_ASCII8 &&
+                   (fru_data.brd.lang_code == FRU_LANG_CODE_ENGLISH ||
+                    fru_data.brd.lang_code == FRU_LANG_CODE_ENGLISH_1))
+                       printf("Type code: %s\n", typecode[type]);
+               else
+                       printf("Type code: %s\n", typecode[type + 1]);
+               if (len == -EINVAL) {
+                       printf("**** EOF for Board Area ****\n");
+                       return 0;
+               }
+               if (type != FRU_TYPELEN_TYPE_ASCII8) {
+                       printf("FRU_ERROR: Only ASCII8 type is supported\n");
+                       return 0;
+               }
+               if (!len) {
+                       printf("%s not found\n", boardinfo[i]);
+                       continue;
+               }
+
+               printf("Length: %d\n", len);
+               printf("%s: %s\n", boardinfo[i], data);
+               data += FRU_BOARD_MAX_LEN;
+       }
+
+       return 0;
+}
+
+static void fru_display_common_hdr(void)
+{
+       struct fru_common_hdr *hdr = &fru_data.hdr;
+
+       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(void)
+{
+       if (!fru_data.captured) {
+               printf("FRU data not available please run fru parse\n");
+               return -EINVAL;
+       }
+
+       fru_display_common_hdr();
+       fru_display_board();
+
+       return 0;
+}
diff --git a/include/fru.h b/include/fru.h
new file mode 100644 (file)
index 0000000..92db810
--- /dev/null
@@ -0,0 +1,14 @@
+/* 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
+
+int fru_display(void);
+int fru_capture(unsigned long addr);
+
+#endif /* FRU_H */