diff --git a/board/xilinx/common/fru.c b/board/xilinx/common/fru.c index 0ab9f2a78b..ccf48723ff 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 - 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 \n" + " - Generate FRU format with\n" + " board info area filled based on parameters. 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 a0413cf7f6..a3e6520257 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 491e92a7af..fc3add7d93 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) {