From mboxrd@z Thu Jan 1 00:00:00 1970 From: Jean-Jacques Hiblot Date: Tue, 22 Oct 2019 14:40:47 +0200 Subject: [U-Boot] [PATCH 1/3] cmd: fru: Add support for FRU commands In-Reply-To: <299fbc3c72c46ceb1b03fb2843876ff3e2f255ca.1571059750.git.michal.simek@xilinx.com> References: <299fbc3c72c46ceb1b03fb2843876ff3e2f255ca.1571059750.git.michal.simek@xilinx.com> Message-ID: List-Id: MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit To: u-boot@lists.denx.de On 14/10/2019 15:29, Michal Simek wrote: > From: Siva Durga Prasad Paladugu > > 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 format. > > 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 > Signed-off-by: Michal Simek > --- > > MAINTAINERS | 3 + > cmd/Kconfig | 8 ++ > cmd/Makefile | 1 + > cmd/fru.c | 68 +++++++++++++ > common/Makefile | 1 + > common/fru_ops.c | 247 +++++++++++++++++++++++++++++++++++++++++++++++ > include/fru.h | 64 ++++++++++++ > 7 files changed, 392 insertions(+) > create mode 100644 cmd/fru.c > create mode 100644 common/fru_ops.c > create mode 100644 include/fru.h > > diff --git a/MAINTAINERS b/MAINTAINERS > index 2ef29768555c..420ce26cd0aa 100644 > --- a/MAINTAINERS > +++ b/MAINTAINERS > @@ -404,6 +404,9 @@ M: Michal Simek > S: Maintained > T: git https://gitlab.denx.de/u-boot/custodians/u-boot-microblaze.git > F: arch/arm/mach-versal/ > +F: cmd/fru.c > +F: common/fru_ops.c > +F: include/fru.h > N: (? > ARM VERSATILE EXPRESS DRIVERS > diff --git a/cmd/Kconfig b/cmd/Kconfig > index 07060c63a7e6..68815e42641e 100644 > --- a/cmd/Kconfig > +++ b/cmd/Kconfig > @@ -1688,6 +1688,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" > diff --git a/cmd/Makefile b/cmd/Makefile > index ac843b4b16ad..f790217dd628 100644 > --- a/cmd/Makefile > +++ b/cmd/Makefile > @@ -43,6 +43,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 > index 000000000000..d5e96cfe8984 > --- /dev/null > +++ b/cmd/fru.c > @@ -0,0 +1,68 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * (C) Copyright 2019 Xilinx, Inc. > + */ > + > +#include > +#include > +#include > +#include > + > +static int do_fru_capture(cmd_tbl_t *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(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, "", ""), > +}; Why not do the capture in "fru display" itself ? And maybe provide the ability to display a FRU read from an EEPROM? > + > +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 - 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/common/Makefile b/common/Makefile > index 302d8beaf356..10cbe9a15ea1 100644 > --- a/common/Makefile > +++ b/common/Makefile > @@ -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 > index 000000000000..dd54bd9c0214 > --- /dev/null > +++ b/common/fru_ops.c > @@ -0,0 +1,247 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * (C) Copyright 2019 Xilinx, Inc. > + */ > + > +#include > +#include > +#include > +#include > +#include > +#include > + > +struct fru_table fru_data __attribute__((section(".data"))); Isn't it restrictive to have only a single instance of struct fru_table ? Could there be multiple ? JJ > + > +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 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) { > + *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) > + break; > + > + /* > + * 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); > + 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(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", > + "Serial No", > + "Part Number", > + "File ID" > + }; > + > + 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.manufacturer_type_len; > + > + for (u8 i = 0; ; i++) { > + len = fru_check_type_len(*data++, fru_data.brd.lang_code, > + &type); > + if (len == -EINVAL) { > + printf("**** EOF for Board Area ****\n"); > + break; > + } > + > + 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 (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 > index 000000000000..38fdfad409c4 > --- /dev/null > +++ b/include/fru.h > @@ -0,0 +1,64 @@ > +/* SPDX-License-Identifier: GPL-2.0+ */ > +/* > + * (C) Copyright 2019 Xilinx, Inc. > + * Siva Durga Prasad Paladugu > + */ > + > +#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_ASCII8 3 > + > +int fru_display(void); > +int fru_capture(unsigned long addr); > + > +extern struct fru_table fru_data; > + > +#endif /* FRU_H */