From: Mehmet Fide <[email protected]> The Vybrid boot ROM reads the same Firmware Configuration Block as the i.MX6 ROM from the first NAND page, with a few differences: the block starts the page instead of following 12 reserved bytes, the fingerprint and the version are byte swapped, the ECC type is the BCH level of the NFC, one copy at page 0 is enough and the ROM reads no DBBT, a zero search area tells it that there are no bad blocks (VFxxxRM rev. 8, 7.5.5.6.2 to 7.5.5.6.4). The Hamming 13/8 parity over the first 512 bytes is the i.MX6 one.
Give the command a vf610 platform entry: the fingerprint and the version come from the platform, FCB_NO_DBBT drops the DBBT and puts the firmware right after the FCB area, BT_SEARCH_SINGLE_FCB writes one copy and erases one block, and the ECC fields come from the NFC configuration when the MXS controller is not there. The BCH paths stay with the MXS driver. The ROM searches past a bad firmware block itself, the limit of 8 blocks is the one the Toradex tooling has always written. Tested on a Colibri VF50 V1.2A and a Colibri VF61 V1.2B: "nandbcb dump 0" decodes the FCB the Toradex tooling wrote, "nandbcb bcbonly 0 0x20000 0x160000 0x20000 0x160000" rewrites it and the ROM boots U-Boot from NAND; the fields the ROM reads equal the original ones on both modules. Signed-off-by: Mehmet Fide <[email protected]> --- arch/arm/include/asm/mach-imx/imx-nandbcb.h | 3 + arch/arm/mach-imx/Kconfig | 5 +- arch/arm/mach-imx/cmd_nandbcb.c | 137 +++++++++++++++----- 3 files changed, 110 insertions(+), 35 deletions(-) diff --git a/arch/arm/include/asm/mach-imx/imx-nandbcb.h b/arch/arm/include/asm/mach-imx/imx-nandbcb.h index 74c9031d4e7..3a2b2118bcc 100644 --- a/arch/arm/include/asm/mach-imx/imx-nandbcb.h +++ b/arch/arm/include/asm/mach-imx/imx-nandbcb.h @@ -9,6 +9,9 @@ #define FCB_FINGERPRINT 0x20424346 /* 'FCB' */ #define FCB_VERSION_1 0x01000000 +/* the Vybrid ROM reads both the other way round */ +#define FCB_FINGERPRINT_VF610 0x46434220 +#define FCB_VERSION_VF610 0x00000001 #define FCB_FINGERPRINT_OFF 0x4 /* FCB fingerprint offset*/ #define DBBT_FINGERPRINT 0x54424244 /* 'DBBT' */ diff --git a/arch/arm/mach-imx/Kconfig b/arch/arm/mach-imx/Kconfig index b59d39a5f6d..fbdabae7e9d 100644 --- a/arch/arm/mach-imx/Kconfig +++ b/arch/arm/mach-imx/Kconfig @@ -166,13 +166,14 @@ config CMD_HDMIDETECT is connected. config CMD_NANDBCB - bool "i.MX6 NAND Boot Control Block(BCB) command" + bool "i.MX and Vybrid NAND Boot Control Block(BCB) command" depends on MTD_RAW_NAND && CMD_MTDPARTS select BCH if MX6UL || MX6ULL default y if ((ARCH_MX6 || ARCH_MX7 || ARCH_IMX8M) && NAND_MXS) help Unlike normal 'nand write/erase' commands, this command update - Boot Control Block(BCB) for i.MX6 platform NAND IP's. + Boot Control Block(BCB) for i.MX6 platform NAND IP's and the + FCB the Vybrid boot ROM reads from the first NAND page. This is similar to kobs-ng, which is used in Linux as separate rootfs package. diff --git a/arch/arm/mach-imx/cmd_nandbcb.c b/arch/arm/mach-imx/cmd_nandbcb.c index c2e452b6927..c8efc86b48c 100644 --- a/arch/arm/mach-imx/cmd_nandbcb.c +++ b/arch/arm/mach-imx/cmd_nandbcb.c @@ -24,7 +24,7 @@ #include <linux/mtd/mtd.h> #include <linux/mtd/rawnand.h> -#include <asm/arch/sys_proto.h> +#include <asm/mach-imx/sys_proto.h> #include <asm/mach-imx/imx-nandbcb.h> #include <asm/mach-imx/imximage.cfg> #include <mxs_nand.h> @@ -49,6 +49,8 @@ #define FCB_ENCODE_BCH (FCB_ENCODE_BCH_40b | FCB_ENCODE_BCH_62b) /* FCB data was randomized */ #define FCB_RANDON_ENABLED BIT(5) +/* No DBBT */ +#define FCB_NO_DBBT BIT(6) /* Firmware related flags */ /* No 1K padding */ @@ -60,9 +62,13 @@ /* Boot search related flags */ #define BT_SEARCH_CNT_FROM_FUSE BIT(16) +/* One FCB copy at page 0 */ +#define BT_SEARCH_SINGLE_FCB BIT(17) struct platform_config { int misc_flags; + u32 fcb_fingerprint; + u32 fcb_version; }; static struct platform_config plat_config; @@ -129,6 +135,16 @@ static struct platform_config imx8q_plat_config = { BT_SEARCH_CNT_FROM_FUSE, }; +/* vf610 */ +static struct platform_config vf610_plat_config = { + .misc_flags = FCB_ENCODE_HAMMING | + FCB_NO_DBBT | + FIRMWARE_NEED_PADDING | + BT_SEARCH_SINGLE_FCB, + .fcb_fingerprint = FCB_FINGERPRINT_VF610, + .fcb_version = FCB_VERSION_VF610, +}; + /* boot search related variables and definitions */ static int g_boot_search_count = 4; static int g_boot_secondary_offset; @@ -271,7 +287,9 @@ static int nandbcb_set_boot_config(int argc, char * const argv[], maxsize = ((u32)(maxsize + mtd->writesize - 1)) / (u32)mtd->writesize * mtd->writesize; - boot_stream1_address = 2 * boot_cfg->search_area_size_in_bytes; + boot_stream1_address = boot_cfg->search_area_size_in_bytes; + if (!(plat_config.misc_flags & FCB_NO_DBBT)) + boot_stream1_address += boot_cfg->search_area_size_in_bytes; boot_stream2_address = ((maxsize - boot_stream1_address) / 2 + boot_stream1_address); @@ -323,6 +341,11 @@ static int nandbcb_check_space(struct boot_config *boot_cfg) return CMD_RET_SUCCESS; } +static int fcb_raw_offset(void) +{ + return plat_config.misc_flags & FCB_LAYOUT_RESV_12B ? 12 : 0; +} + #if defined(CONFIG_MX6UL) || defined(CONFIG_MX6ULL) static uint8_t reverse_bit(uint8_t b) { @@ -410,6 +433,14 @@ static void encode_hamming_13_8(void *_src, void *_ecc, size_t size) for (i = 0; i < size; i++) ecc[i] = calculate_parity_13_8(src[i]); } + +static void fcb_raw_hamming(void *raw, struct fcb_block *fcb) +{ + int off = fcb_raw_offset(); + + memcpy(raw + off, fcb, sizeof(*fcb)); + encode_hamming_13_8(raw + off, raw + off + 512, 512); +} #endif static u32 calc_chksum(void *buf, size_t size) @@ -424,17 +455,42 @@ static u32 calc_chksum(void *buf, size_t size) return ~chksum; } -static void fill_fcb(struct fcb_block *fcb, struct boot_config *boot_cfg) +static void fill_fcb_ecc(struct fcb_block *fcb, struct mtd_info *mtd) { - struct mtd_info *mtd = boot_cfg->mtd; - struct nand_chip *chip = mtd_to_nand(mtd); - struct mxs_nand_info *nand_info = nand_get_controller_data(chip); + struct nand_chip *chip; + struct mxs_nand_info *nand_info; struct mxs_nand_layout l; + if (IS_ENABLED(CONFIG_NAND_VF610_NFC)) { + fcb->ecc_level = 6; + if (IS_ENABLED(CONFIG_SYS_NAND_VF610_NFC_60_ECC_BYTES)) + fcb->ecc_level = 7; + fcb->ecc_type = fcb->ecc_level; + return; + } + + chip = mtd_to_nand(mtd); + nand_info = nand_get_controller_data(chip); mxs_nand_get_layout(mtd, &l); - fcb->fingerprint = FCB_FINGERPRINT; - fcb->version = FCB_VERSION_1; + fcb->meta_size = l.meta_size; + fcb->nr_blocks = l.nblocks; + fcb->ecc_nr = l.data0_size; + fcb->ecc_level = l.ecc0; + fcb->ecc_size = l.datan_size; + fcb->ecc_type = l.eccn; + fcb->bchtype = l.gf_len; + + fcb->bb_byte = nand_info->bch_geometry.block_mark_byte_offset; + fcb->bb_start_bit = nand_info->bch_geometry.block_mark_bit_offset; +} + +static void fill_fcb(struct fcb_block *fcb, struct boot_config *boot_cfg) +{ + struct mtd_info *mtd = boot_cfg->mtd; + + fcb->fingerprint = plat_config.fcb_fingerprint ?: FCB_FINGERPRINT; + fcb->version = plat_config.fcb_version ?: FCB_VERSION_1; fcb->datasetup = 80; fcb->datahold = 60; @@ -445,19 +501,14 @@ static void fill_fcb(struct fcb_block *fcb, struct boot_config *boot_cfg) fcb->oob_pagesize = mtd->writesize + mtd->oobsize; fcb->sectors = mtd->erasesize / mtd->writesize; - fcb->meta_size = l.meta_size; - fcb->nr_blocks = l.nblocks; - fcb->ecc_nr = l.data0_size; - fcb->ecc_level = l.ecc0; - fcb->ecc_size = l.datan_size; - fcb->ecc_type = l.eccn; - fcb->bchtype = l.gf_len; - - /* DBBT search area starts from the next block after all FCB */ - fcb->dbbt_start = boot_cfg->search_area_size_in_pages; + fill_fcb_ecc(fcb, mtd); - fcb->bb_byte = nand_info->bch_geometry.block_mark_byte_offset; - fcb->bb_start_bit = nand_info->bch_geometry.block_mark_bit_offset; + if (plat_config.misc_flags & FCB_NO_DBBT) { + fcb->disbbm_search_limit = 8; + } else { + /* DBBT search area starts from the next block after all FCB */ + fcb->dbbt_start = boot_cfg->search_area_size_in_pages; + } fcb->phy_offset = mtd->writesize; @@ -516,7 +567,8 @@ static int read_fcb(struct boot_config *boot_cfg, struct fcb_block *fcb, /* * User BCH hardware to decode ECC for FCB */ - if (plat_config.misc_flags & FCB_ENCODE_BCH) { + if (IS_ENABLED(CONFIG_NAND_MXS) && + (plat_config.misc_flags & FCB_ENCODE_BCH)) { size = sizeof(struct fcb_block); /* switch nand BCH to FCB compatible settings */ @@ -550,9 +602,9 @@ static int read_fcb(struct boot_config *boot_cfg, struct fcb_block *fcb, if (ret) goto fcb_raw_page_err; - if ((plat_config.misc_flags & FCB_ENCODE_HAMMING) && - (plat_config.misc_flags & FCB_LAYOUT_RESV_12B)) - memcpy(fcb, fcb_raw_page + 12, sizeof(struct fcb_block)); + if (plat_config.misc_flags & FCB_ENCODE_HAMMING) + memcpy(fcb, fcb_raw_page + fcb_raw_offset(), + sizeof(struct fcb_block)); /* TODO: check if it can pass Hamming check */ @@ -573,11 +625,10 @@ static int write_fcb(struct boot_config *boot_cfg, struct fcb_block *fcb) mtd = boot_cfg->mtd; /* - * We prepare raw page only for i.MX6, for i.MX7 we + * We prepare raw page only for i.MX6 and vf610, for i.MX7 we * leverage BCH hw module instead */ - if ((plat_config.misc_flags & FCB_ENCODE_HAMMING) && - (plat_config.misc_flags & FCB_LAYOUT_RESV_12B)) { + if (plat_config.misc_flags & FCB_ENCODE_HAMMING) { fcb_raw_page = kzalloc(mtd->writesize + mtd->oobsize, GFP_KERNEL); if (!fcb_raw_page) { @@ -590,9 +641,7 @@ static int write_fcb(struct boot_config *boot_cfg, struct fcb_block *fcb) /* 40 bit BCH, for i.MX6UL(L) */ encode_bch_ecc(fcb_raw_page + 32, fcb, 40); #else - memcpy(fcb_raw_page + 12, fcb, sizeof(struct fcb_block)); - encode_hamming_13_8(fcb_raw_page + 12, - fcb_raw_page + 12 + 512, 512); + fcb_raw_hamming(fcb_raw_page, fcb); #endif /* * Set the first and second byte of OOB data to 0xFF, @@ -619,7 +668,8 @@ static int write_fcb(struct boot_config *boot_cfg, struct fcb_block *fcb) /* * User BCH hardware module to generate ECC for FCB */ - if (plat_config.misc_flags & FCB_ENCODE_BCH) { + if (IS_ENABLED(CONFIG_NAND_MXS) && + (plat_config.misc_flags & FCB_ENCODE_BCH)) { size = sizeof(struct fcb_block); /* switch nand BCH to FCB compatible settings */ @@ -1008,6 +1058,8 @@ static int nandbcb_init(struct boot_config *boot_cfg, u_char *buf) fill_fcb(fcb, boot_cfg); ret = write_fcb(boot_cfg, fcb); + if (plat_config.misc_flags & FCB_NO_DBBT) + goto fcb_err; /* fill dbbt */ dbbt_page = kzalloc(mtd->writesize, GFP_KERNEL); @@ -1098,6 +1150,8 @@ static int do_nandbcb_bcbonly(int argc, char *const argv[]) memset(&opts, 0, sizeof(opts)); opts.offset = off; opts.length = g_boot_search_stride * 2; + if (plat_config.misc_flags & BT_SEARCH_SINGLE_FCB) + opts.length = g_boot_search_stride; ret = nand_erase_opts(mtd, &opts); if (ret) { printf("%s: erase failed (ret = %d)\n", __func__, ret); @@ -1116,6 +1170,8 @@ static int do_nandbcb_bcbonly(int argc, char *const argv[]) /* write fcb */ ret = write_fcb(&cfg, fcb); + if (plat_config.misc_flags & FCB_NO_DBBT) + goto fcb_err; /* fill dbbt */ dbbt_page = kzalloc(mtd->writesize, GFP_KERNEL); @@ -1228,6 +1284,10 @@ void dump_structure(struct boot_config *boot_cfg, struct fcb_block *fcb, P1(read_retry_enable); #endif #undef P1 + if (plat_config.misc_flags & FCB_NO_DBBT) { + printf("DBBT : none\n"); + goto firmware; + } #define P1(x) printf(" %s = 0x%08x\n", #x, dbbt->x) printf("DBBT :\n"); P1(checksum); @@ -1241,6 +1301,8 @@ void dump_structure(struct boot_config *boot_cfg, struct fcb_block *fcb, for (i = 0; i < dbbt->dbbtpages; ++i) printf("%d ", *((u32 *)(dbbt_data_page + i))); +firmware: + if (!(plat_config.misc_flags & FIRMWARE_EXTRA_ONE)) { printf("Firmware: image #0 @ 0x%x size 0x%x\n", fcb->fw1_start, fcb->fw1_pages * mtd->writesize); @@ -1330,6 +1392,7 @@ static int nandbcb_dump(struct boot_config *boot_cfg) struct dbbt_block dbbt, dbbt_copy; void *dbbt_data_page, *dbbt_data_page_copy; bool fcb_not_found, dbbt_not_found; + u32 fingerprint = plat_config.fcb_fingerprint ?: FCB_FINGERPRINT; int ret = 0; dbbt_data_page = kzalloc(mtd->writesize, GFP_KERNEL); @@ -1358,7 +1421,7 @@ static int nandbcb_dump(struct boot_config *boot_cfg) else if (ret == 1) continue; else if (ret == 0) - if (check_fingerprint(&fcb, FCB_FINGERPRINT)) + if (check_fingerprint(&fcb, fingerprint)) fcb_not_found = 0; } else { ret = read_fcb(boot_cfg, &fcb_copy, off); @@ -1380,7 +1443,10 @@ static int nandbcb_dump(struct boot_config *boot_cfg) /* read dbbt*/ dbbt_not_found = 1; off = boot_cfg->search_area_size_in_bytes; + memset(&dbbt, 0, sizeof(dbbt)); for (i = 0; i < g_boot_search_count; ++i) { + if (plat_config.misc_flags & FCB_NO_DBBT) + break; if (dbbt_not_found) { ret = read_dbbt(boot_cfg, &dbbt, dbbt_data_page, off); @@ -1515,11 +1581,16 @@ static int do_nandbcb(struct cmd_tbl *cmdtp, int flag, int argc, plat_config = imx8mn_plat_config; } else if (is_imx8qm() || is_imx8qxp()) { plat_config = imx8q_plat_config; + } else if (is_cpu_type(MXC_CPU_VF610)) { + plat_config = vf610_plat_config; } else { printf("ERROR: Unknown platform\n"); return CMD_RET_FAILURE; } + if (plat_config.misc_flags & BT_SEARCH_SINGLE_FCB) + g_boot_search_count = 1; + if ((plat_config.misc_flags) & BT_SEARCH_CNT_FROM_FUSE) { if (is_imx8qxp()) g_boot_search_count = fuse_to_search_count(0, 720, 0xc0, 6); @@ -1573,6 +1644,6 @@ U_BOOT_LONGHELP(nandbcb, "nandbcb dump off|partition - dump/verify boot structures\n"); U_BOOT_CMD(nandbcb, 7, 1, do_nandbcb, - "i.MX NAND Boot Control Blocks write", + "i.MX and Vybrid NAND Boot Control Blocks write", nandbcb_help_text ); -- 2.55.0
