diff options
Diffstat (limited to 'roms/u-boot/board/xilinx/common')
-rw-r--r-- | roms/u-boot/board/xilinx/common/Makefile | 10 | ||||
-rw-r--r-- | roms/u-boot/board/xilinx/common/board.c | 442 | ||||
-rw-r--r-- | roms/u-boot/board/xilinx/common/board.h | 14 | ||||
-rw-r--r-- | roms/u-boot/board/xilinx/common/fru.c | 91 | ||||
-rw-r--r-- | roms/u-boot/board/xilinx/common/fru.h | 87 | ||||
-rw-r--r-- | roms/u-boot/board/xilinx/common/fru_ops.c | 368 |
6 files changed, 1012 insertions, 0 deletions
diff --git a/roms/u-boot/board/xilinx/common/Makefile b/roms/u-boot/board/xilinx/common/Makefile new file mode 100644 index 000000000..212028478 --- /dev/null +++ b/roms/u-boot/board/xilinx/common/Makefile @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# (C) Copyright 2020 Xilinx, Inc. +# Michal Simek <michal.simek@xilinx.com> +# + +obj-y += board.o +ifndef CONFIG_SPL_BUILD +obj-$(CONFIG_CMD_FRU) += fru.o fru_ops.o +endif diff --git a/roms/u-boot/board/xilinx/common/board.c b/roms/u-boot/board/xilinx/common/board.c new file mode 100644 index 000000000..92b61d83c --- /dev/null +++ b/roms/u-boot/board/xilinx/common/board.c @@ -0,0 +1,442 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2014 - 2020 Xilinx, Inc. + * Michal Simek <michal.simek@xilinx.com> + */ + +#include <common.h> +#include <env.h> +#include <log.h> +#include <asm/global_data.h> +#include <asm/sections.h> +#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> +#include <generated/dt.h> + +#include "fru.h" + +#if defined(CONFIG_ZYNQ_GEM_I2C_MAC_OFFSET) +int zynq_board_read_rom_ethaddr(unsigned char *ethaddr) +{ + int ret = -EINVAL; + struct udevice *dev; + ofnode eeprom; + + eeprom = ofnode_get_chosen_node("xlnx,eeprom"); + if (!ofnode_valid(eeprom)) + return -ENODEV; + + debug("%s: Path to EEPROM %s\n", __func__, + ofnode_read_chosen_string("xlnx,eeprom")); + + ret = uclass_get_device_by_ofnode(UCLASS_I2C_EEPROM, eeprom, &dev); + if (ret) + return ret; + + ret = dm_i2c_read(dev, CONFIG_ZYNQ_GEM_I2C_MAC_OFFSET, ethaddr, 6); + if (ret) + debug("%s: I2C EEPROM MAC address read failed\n", __func__); + else + debug("%s: I2C EEPROM MAC %pM\n", __func__, ethaddr); + + return ret; +} +#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 int highest_id = -1; +static struct xilinx_board_description **board_info; + +#define XILINX_I2C_DETECTION_BITS sizeof(struct fru_common_hdr) + +/* 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_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) +{ + 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 (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); + + return -ENODEV; +} + +__maybe_unused int xilinx_read_eeprom(void) +{ + int id, ret; + char name_buf[8]; /* 8 bytes should be enough for nvmem+number */ + struct xilinx_board_description *desc; + + highest_id = dev_read_alias_highest_id("nvmem"); + /* No nvmem aliases present */ + if (highest_id < 0) + return -EINVAL; + + board_info = calloc(1, sizeof(desc) * highest_id); + 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); + + /* Alloc structure */ + desc = board_info[id]; + if (!desc) { + desc = calloc(1, sizeof(*desc)); + if (!desc) + return -ENOMEM; + + board_info[id] = desc; + } + + /* Ignoring return value for supporting multiple chips */ + ret = xilinx_read_eeprom_single(name_buf, desc); + if (ret) { + free(desc); + board_info[id] = NULL; + } + } + + /* + * Consider to clean board_info structure when board/cards are not + * detected. + */ + + return 0; +} + +#if defined(CONFIG_OF_BOARD) || defined(CONFIG_OF_SEPARATE) +void *board_fdt_blob_setup(void) +{ + void *fdt_blob; + + if (!IS_ENABLED(CONFIG_SPL_BUILD) && + !IS_ENABLED(CONFIG_VERSAL_NO_DDR) && + !IS_ENABLED(CONFIG_ZYNQMP_NO_DDR)) { + fdt_blob = (void *)CONFIG_XILINX_OF_BOARD_DTB_ADDR; + + if (fdt_magic(fdt_blob) == FDT_MAGIC) + return fdt_blob; + + debug("DTB is not passed via %p\n", fdt_blob); + } + + if (IS_ENABLED(CONFIG_SPL_BUILD)) { + /* + * FDT is at end of BSS unless it is in a different memory + * region + */ + if (IS_ENABLED(CONFIG_SPL_SEPARATE_BSS)) + fdt_blob = (ulong *)&_image_binary_end; + else + fdt_blob = (ulong *)&__bss_end; + } else { + /* FDT is at end of image */ + fdt_blob = (ulong *)&_end; + } + + if (fdt_magic(fdt_blob) == FDT_MAGIC) + return fdt_blob; + + debug("DTB is also not passed via %p\n", fdt_blob); + + return NULL; +} +#endif + +#if defined(CONFIG_BOARD_LATE_INIT) +static int env_set_by_index(const char *name, int index, char *data) +{ + char var[32]; + + if (!index) + sprintf(var, "board_%s", name); + else + sprintf(var, "card%d_%s", index, name); + + return env_set(var, data); +} + +int board_late_init_xilinx(void) +{ + u32 ret = 0; + int i, id, macid = 0; + struct xilinx_board_description *desc; + phys_size_t bootm_size = gd->ram_size; + + if (!CONFIG_IS_ENABLED(MICROBLAZE)) { + ulong scriptaddr; + + scriptaddr = env_get_hex("scriptaddr", 0); + ret |= env_set_hex("scriptaddr", gd->ram_base + scriptaddr); + } + + if (CONFIG_IS_ENABLED(ARCH_ZYNQ) || CONFIG_IS_ENABLED(MICROBLAZE)) + bootm_size = min(bootm_size, (phys_size_t)(SZ_512M + SZ_256M)); + + ret |= env_set_hex("script_offset_f", CONFIG_BOOT_SCRIPT_OFFSET); + + ret |= env_set_addr("bootm_low", (void *)gd->ram_base); + ret |= env_set_addr("bootm_size", (void *)bootm_size); + + for (id = 0; id <= highest_id; id++) { + desc = board_info[id]; + if (desc && desc->header == EEPROM_HEADER_MAGIC) { + if (desc->manufacturer[0]) + ret |= env_set_by_index("manufacturer", id, + desc->manufacturer); + if (desc->name[0]) + ret |= env_set_by_index("name", id, + desc->name); + if (desc->revision[0]) + ret |= env_set_by_index("rev", id, + desc->revision); + if (desc->serial[0]) + ret |= env_set_by_index("serial", id, + desc->serial); + + if (!CONFIG_IS_ENABLED(NET)) + continue; + + for (i = 0; i < EEPROM_HDR_NO_OF_MAC_ADDR; i++) { + if (!desc->mac_addr[i]) + continue; + + if (is_valid_ethaddr((const u8 *)desc->mac_addr[i])) + ret |= eth_env_set_enetaddr_by_index("eth", + macid++, desc->mac_addr[i]); + } + } + } + + if (ret) + printf("%s: Saving run time variables FAILED\n", __func__); + + return 0; +} +#endif + +int __maybe_unused board_fit_config_name_match(const char *name) +{ + debug("%s: Check %s, default %s\n", __func__, name, DEVICE_TREE); + + if (!strcmp(name, DEVICE_TREE)) + return 0; + + return -1; +} diff --git a/roms/u-boot/board/xilinx/common/board.h b/roms/u-boot/board/xilinx/common/board.h new file mode 100644 index 000000000..69e642429 --- /dev/null +++ b/roms/u-boot/board/xilinx/common/board.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * (C) Copyright 2020 Xilinx, Inc. + * Michal Simek <michal.simek@xilinx.com> + */ + +#ifndef _BOARD_XILINX_COMMON_BOARD_H +#define _BOARD_XILINX_COMMON_BOARD_H + +int board_late_init_xilinx(void); + +int xilinx_read_eeprom(void); + +#endif /* BOARD_XILINX_COMMON_BOARD_H */ diff --git a/roms/u-boot/board/xilinx/common/fru.c b/roms/u-boot/board/xilinx/common/fru.c new file mode 100644 index 000000000..ccf48723f --- /dev/null +++ b/roms/u-boot/board/xilinx/common/fru.c @@ -0,0 +1,91 @@ +// 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 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, + 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" + "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, 8, 1, do_fru, + "FRU table info", + fru_help_text +) diff --git a/roms/u-boot/board/xilinx/common/fru.h b/roms/u-boot/board/xilinx/common/fru.h new file mode 100644 index 000000000..e7284709d --- /dev/null +++ b/roms/u-boot/board/xilinx/common/fru.h @@ -0,0 +1,87 @@ +/* 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 __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; + 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]; + /* Xilinx custom fields */ + u8 rev_type_len; + u8 rev[FRU_BOARD_MAX_LEN]; + u8 pcie_type_len; + u8 pcie[FRU_BOARD_MAX_LEN]; + u8 uuid_type_len; + u8 uuid[FRU_BOARD_MAX_LEN]; +}; + +struct fru_table { + struct fru_common_hdr hdr; + struct fru_board_data brd; + bool captured; +}; + +#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); +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; + +#endif /* FRU_H */ diff --git a/roms/u-boot/board/xilinx/common/fru_ops.c b/roms/u-boot/board/xilinx/common/fru_ops.c new file mode 100644 index 000000000..6ed63bb7e --- /dev/null +++ b/roms/u-boot/board/xilinx/common/fru_ops.c @@ -0,0 +1,368 @@ +// 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; +} + +/* 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; + int len; + u8 *data, *term, *limit; + + memcpy(&fru_data.brd.ver, (void *)addr, 6); + addr += 6; + data = (u8 *)&fru_data.brd.manufacturer_type_len; + + /* Record max structure limit not to write data over allocated space */ + limit = (u8 *)&fru_data.brd + sizeof(struct fru_board_data); + + 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; + + /* Stop when amount of chars is more then fields to record */ + if (data + len > limit) + 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, (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", + /* Xilinx spec */ + "Revision Number", + }; + + 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); +} |