summaryrefslogtreecommitdiff
path: root/arch
diff options
context:
space:
mode:
Diffstat (limited to 'arch')
-rw-r--r--arch/arm/imx-common/Makefile1
-rw-r--r--arch/arm/imx-common/cmd_writebcb_mx7.c520
2 files changed, 521 insertions, 0 deletions
diff --git a/arch/arm/imx-common/Makefile b/arch/arm/imx-common/Makefile
index f77fcd58995..b6e72e55d96 100644
--- a/arch/arm/imx-common/Makefile
+++ b/arch/arm/imx-common/Makefile
@@ -62,6 +62,7 @@ obj-$(CONFIG_CMD_HDMIDETECT) += cmd_hdmidet.o
obj-$(CONFIG_CMD_DEKBLOB) += cmd_dek.o
obj-$(CONFIG_CMD_M4BOOT) += cmd_m4boot.o
obj-$(CONFIG_CMD_WRITEBCB) += cmd_writebcb.o
+obj-$(CONFIG_CMD_WRITEBCB_MX7) += cmd_writebcb_mx7.o
quiet_cmd_cpp_cfg = CFGS $@
cmd_cpp_cfg = $(CPP) $(cpp_flags) -x c -o $@ $<
diff --git a/arch/arm/imx-common/cmd_writebcb_mx7.c b/arch/arm/imx-common/cmd_writebcb_mx7.c
new file mode 100644
index 00000000000..83becf7e539
--- /dev/null
+++ b/arch/arm/imx-common/cmd_writebcb_mx7.c
@@ -0,0 +1,520 @@
+/*
+ * Copyright (c) 2013, Toradex AG. All rights reserved.
+ *
+ * Derived from downstream U-Boot (drivers/mtd/nand/fsl_nfc.c)
+ * and mxsboot
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+/* NAND LAYOUT
+ *
+ * Content FCB CFG_BLOCK .. FCB CFG_BLOCK DBBT .. DBBT
+ * Block/Page 0/0 0/1 .. 0/(n-1) 1/(n-1) 0/n .. 0/n + (n-1)
+ *
+ * With n = BOOT_SEARCH_COUNT
+ *
+ * With n = 2 the NAND is used starting at 0 with a length of 0x80000
+ *
+ * Usually one will use fw_start1 at 0x80000 and fw_start2 at 0x200000
+ * allowing for at least 6 bad blocks in either bootloader
+ */
+
+#include <common.h>
+#include <linux/bch.h>
+#include <malloc.h>
+#include <nand.h>
+#include "rand.h"
+
+#define PAGES_PER_STRIDE 64
+#define RAND_16K (16 * 1024)
+
+#define BOOTLOADER_MAXSIZE (640 * 1024)
+#define BOOT_SEARCH_COUNT 2 /* match with BOOT_CFG_FUSES [6:5] */
+
+extern int raw_access(nand_info_t *nand, ulong addr, loff_t off, ulong count,
+ int read);
+
+struct mxs_nand_fcb {
+ uint32_t checksum;
+ uint32_t fingerprint;
+ uint32_t version;
+ struct {
+ uint8_t data_setup;
+ uint8_t data_hold;
+ uint8_t address_setup;
+ uint8_t dsample_time;
+ uint8_t nand_timing_state;
+ uint8_t rea;
+ uint8_t rloh;
+ uint8_t rhoh;
+ } timing;
+ uint32_t page_data_size;
+ uint32_t total_page_size;
+ uint32_t sectors_per_block;
+ uint32_t number_of_nands; /* Ignored */
+ uint32_t total_internal_die; /* Ignored */
+ uint32_t cell_type; /* Ignored */
+ uint32_t ecc_block_n_ecc_type;
+ uint32_t ecc_block_0_size;
+ uint32_t ecc_block_n_size;
+ uint32_t ecc_block_0_ecc_type;
+ uint32_t metadata_bytes;
+ uint32_t num_ecc_blocks_per_page;
+ uint32_t ecc_block_n_ecc_level_sdk; /* Ignored */
+ uint32_t ecc_block_0_size_sdk; /* Ignored */
+ uint32_t ecc_block_n_size_sdk; /* Ignored */
+ uint32_t ecc_block_0_ecc_level_sdk; /* Ignored */
+ uint32_t num_ecc_blocks_per_page_sdk; /* Ignored */
+ uint32_t metadata_bytes_sdk; /* Ignored */
+ uint32_t erase_threshold;
+ uint32_t boot_patch; /* not iMX7 */
+ uint32_t patch_sectors; /* not iMX7 */
+ uint32_t firmware1_starting_sector;
+ uint32_t firmware2_starting_sector;
+ uint32_t sectors_in_firmware1;
+ uint32_t sectors_in_firmware2;
+ uint32_t dbbt_search_area_start_address;
+ uint32_t badblock_marker_byte;
+ uint32_t badblock_marker_start_bit;
+ uint32_t bb_marker_physical_offset;
+ uint32_t bch_type; /* only iMX7 */
+ uint32_t tm_timing2_read_latency; /* only iMX7 */
+ uint32_t tm_timing2_preambel_delay; /* only iMX7 */
+ uint32_t tm_timing2_ce_delay; /* only iMX7 */
+ uint32_t tm_timing2_postamble_delay; /* only iMX7 */
+ uint32_t tm_timing2_cmd_add_pause; /* only iMX7 */
+ uint32_t tm_timing2_data_pause; /* only iMX7 */
+ uint32_t tm_speed; /* only iMX7 */
+ uint32_t tm_timing1_busy_timeout; /* only iMX7 */
+ uint32_t disbbm;
+ uint32_t bbmark_spare_offset; /* only iMX7 */
+ uint32_t onfi_sync_enable; /* only iMX7 */
+ uint32_t onfi_sync_speed; /* only iMX7 */
+ uint32_t onfi_sync_nand_data; /* only iMX7 */
+ uint32_t reserved2[6];
+ uint32_t disbbm_search;
+ uint32_t disbbm_search_limit;
+ uint32_t reserved3[15]; /* only iMX7 */
+ uint32_t read_retry_enable; /* only iMX7 */
+ uint32_t reserved4[1]; /* only iMX7 */
+ uint32_t fill_to_1024[183];
+};
+
+struct mxs_nand_dbbt {
+ uint32_t checksum;
+ uint32_t fingerprint;
+ uint32_t version;
+ uint32_t number_bb;
+ uint32_t number_2k_pages_bb;
+};
+
+struct mxs_nand_bbt {
+ uint32_t nand;
+ uint32_t number_bb;
+ uint32_t badblock[510];
+};
+
+
+#ifdef DEBUG
+#include <linux/ctype.h>
+/* static */
+void dump(const void *data, int size)
+{
+ int i, j;
+ const uint8_t *s;
+
+ s = data;
+ for (i = j = 0; i < size; i += 16) {
+ if (i)
+ printf("\n");
+ printf("[%04x]", i);
+
+ for (j = i; j < i + 16; j++) {
+ if (j < size)
+ printf(" %02x", s[j]);
+ else
+ printf(" ");
+ if (j == i + 7)
+ printf(" ");
+ }
+
+
+ printf(" | ");
+
+ for (j = i; j < i + 16; j ++) {
+ if (j < size)
+ printf("%c", isprint(s[j]) ? s[j] : '.');
+ else
+ printf(" ");
+ if (j == i + 7)
+ printf("-");
+ }
+ }
+ printf("\n");
+}
+#endif
+
+/*
+ * reverse bit for byte
+ */
+static uint8_t reverse_bit(uint8_t in_byte)
+{
+ int i;
+ uint8_t out_byte = 0;
+
+ for (i = 0; i < 8; i++) {
+ if (in_byte & ((0x80) >> i)) {
+ out_byte |= 1 << i;
+ }
+ }
+
+ return out_byte;
+}
+
+int encode_bch_ecc(void *source_block, size_t source_size, void *target_block,
+ size_t target_size)
+{
+ struct bch_control *bch;
+ unsigned char *ecc_buf;
+ int ecc_buf_size;
+ unsigned char *tmp_buf;
+ int tmp_buf_size;
+ int real_buf_size;
+ int i, j;
+ int ecc_bit_off;
+ int data_ecc_blk_size;
+ int low_byte_off, low_bit_off;
+ int high_byte_off, high_bit_off;
+ unsigned char byte_low, byte_high;
+
+ /* define the variables for bch algorithm*/
+ /* m: METADATABYTE */
+ /* b0: BLOCK0BYTE */
+ /* e0: BLOCK0ECC */
+ /* bn: BLOCKNBYTE */
+ /* en: BLOCKNECC */
+ /* n : NUMOFBLOCKN */
+ /* gf: FCB_GF */
+ int m, b0, e0, bn, en, n, gf;
+
+ /* 62 bit BCH, for i.MX6SX and i.MX7D */
+ m = 32;
+ b0 = 128;
+ e0 = 62;
+ bn = 128;
+ en = 62;
+ n = 7;
+ gf = 13;
+
+ /* sanity check */
+ /* nand data block must be large enough for FCB structure */
+ if(source_size > b0 + n * bn)
+ return -EINVAL;
+ /* nand page need to be large enough to contain Meta, FCB and ECC */
+ if(target_size < m + b0 + e0 * gf / 8 + n * bn + n * en * gf / 8)
+ return -EINVAL;
+
+ /* init bch, using default polynomial */
+ bch = init_bch(gf, en, 0);
+ if(!bch)
+ return -EINVAL;
+
+ /* buffer for ecc */
+ ecc_buf_size = (gf * en + 7) / 8;
+ ecc_buf = malloc(ecc_buf_size);
+ if(!ecc_buf)
+ return -EINVAL;
+
+ /* temp buffer to store data and ecc */
+ tmp_buf_size = b0 + (e0 * gf + 7) / 8 + (bn + (en * gf + 7) / 8) * n;
+ tmp_buf = malloc(tmp_buf_size);
+ if(!tmp_buf)
+ return -EINVAL;
+ memset(tmp_buf, 0, tmp_buf_size);
+
+ /* generate ecc code for each data block and store in temp buffer */
+
+ for(i = 0; i < n + 1; i++) {
+ memset(ecc_buf, 0, ecc_buf_size);
+ encode_bch(bch, source_block + i * bn, bn, ecc_buf);
+
+ memcpy(tmp_buf + i * (bn + ecc_buf_size), source_block + i * bn,
+ bn);
+
+ /* reverse ecc bit */
+ for(j = 0; j < ecc_buf_size; j++) {
+ ecc_buf[j] = reverse_bit(ecc_buf[j]);
+ }
+
+ memcpy(tmp_buf + (i + 1) * bn + i * ecc_buf_size, ecc_buf,
+ ecc_buf_size);
+ }
+
+ /* store Metadata for taget block with randomizer*/
+ /*memcpy(target_block, RandData, m);*/
+ memset(target_block, 0, m);
+
+ /* shift the bit to combine the source data and ecc */
+ real_buf_size = (b0 * 8 + gf * e0 + (bn * 8 + gf * en) * n) / 8;
+
+ if(!((gf * en) % 8)) {
+ /* ecc data is byte aligned, just copy it. */
+ memcpy(target_block + m, tmp_buf, real_buf_size);
+ } else {
+ /* bit offset for each ecc block */
+ ecc_bit_off = 8 - (gf * en) % 8;
+ /* size of a data block plus ecc block */
+ data_ecc_blk_size = bn + (gf * en + 7) / 8;
+
+ for(i = 0; i < real_buf_size; i++) {
+ low_bit_off = ((i / data_ecc_blk_size) * ecc_bit_off)
+ % 8;
+ low_byte_off = ((i / data_ecc_blk_size) * ecc_bit_off)
+ / 8;
+ high_bit_off = (((i + 1) / data_ecc_blk_size)
+ * ecc_bit_off) % 8;
+ high_byte_off = (((i + 1) / data_ecc_blk_size)
+ * ecc_bit_off) / 8;
+
+ byte_low = tmp_buf[i + low_byte_off] >> low_bit_off;
+ byte_high = tmp_buf[i + 1 + high_byte_off]
+ << (8 - high_bit_off);
+
+ *(uint8_t *)(target_block + i + m) = (byte_low
+ | byte_high);
+ }
+ }
+
+ free(ecc_buf);
+ free(tmp_buf);
+ return 0;
+}
+
+static void create_fcb(nand_info_t *nand, uint8_t *buf, int fw1_start_address,
+ int fw2_start_address)
+{
+ int i;
+ struct mxs_nand_fcb _fcb;
+ struct mxs_nand_fcb *fcb = &_fcb;
+ int fw_size = BOOTLOADER_MAXSIZE;
+ memset (fcb, 0, sizeof(struct mxs_nand_fcb));
+
+ fcb->fingerprint = 0x20424346;
+ fcb->version = 0x01000000;
+ fcb->timing.data_setup = 10;
+ fcb->timing.data_hold = 7;
+ fcb->timing.address_setup = 15;
+ fcb->timing.dsample_time = 6;
+
+ fcb->page_data_size = nand->writesize;
+ fcb->total_page_size = nand->writesize + nand->oobsize;
+ fcb->sectors_per_block = nand->erasesize / nand->writesize;
+
+ fcb->ecc_block_n_ecc_type = 4;
+ fcb->ecc_block_0_size = 512;
+ fcb->ecc_block_n_size = 512;
+ fcb->ecc_block_0_ecc_type = 4;
+ fcb->metadata_bytes = 10;
+ fcb->num_ecc_blocks_per_page = 2;
+
+ fcb->firmware1_starting_sector = fw1_start_address / nand->writesize;
+ fcb->firmware2_starting_sector = fw2_start_address / nand->writesize;
+
+ fcb->sectors_in_firmware1 = DIV_ROUND_UP(fw_size, 3 * 512);
+ fcb->sectors_in_firmware2 = DIV_ROUND_UP(fw_size, 3 * 512);
+ fcb->dbbt_search_area_start_address = 256;
+ fcb->badblock_marker_byte = 1999;
+ fcb->bb_marker_physical_offset = 2048;
+ /* This is typically the first byte of the pages OOB area */
+ fcb->bb_marker_physical_offset = nand->writesize;
+ /* workaround bug in bootrom, see errata */
+ fcb->disbbm = 1;
+ fcb->disbbm_search = 0;
+
+ /* compute checksum, ~(sum of bytes starting with offset 4) */
+ for (i = 4; i < sizeof(struct mxs_nand_fcb); i++)
+ fcb->checksum += *((char *) fcb + i);
+ fcb->checksum ^= 0xffffffff;
+ encode_bch_ecc(fcb, sizeof(struct mxs_nand_fcb), buf,
+ fcb->total_page_size);
+#ifdef DEBUG
+ printf("FCB\n"); dump(fcb, sizeof(struct mxs_nand_fcb));
+ printf("Encoded\n"); dump(buf, 512);
+#endif
+}
+
+static void create_dbbt(nand_info_t *nand, uint8_t *buf)
+{
+ int i;
+ struct mxs_nand_dbbt *dbbt = (struct mxs_nand_dbbt *)buf;
+
+ memset (buf, 0, sizeof(struct mxs_nand_dbbt));
+ dbbt->fingerprint = 0x54424244;
+ dbbt->version = 0x01000000;
+ dbbt->number_bb = 0;
+ dbbt->number_2k_pages_bb = 0;
+
+ /* compute checksum, ~(sum of bytes starting with offset 4) */
+ for (i = 4; i < sizeof(struct mxs_nand_dbbt); i++)
+ dbbt->checksum += *((char *) dbbt + i);
+ dbbt->checksum ^= 0xffffffff;
+
+#ifdef DEBUG
+ printf("DBBT\n"); dump(dbbt, sizeof(struct mxs_nand_dbbt));
+#endif
+}
+
+/* workaround for i.MX 7 errata e9609, use only 3/4
+ * of the available chunks in a block and have an
+ * FCB with a matching ecc layout and DISBBM set to 1
+*/
+static void write_bootloader(nand_info_t *nand, uint8_t * addr, loff_t off,
+ ulong fw_size)
+{
+ int i, j, ret;
+ size_t maxsize;
+ unsigned used_page_size, used_page_size_tmp;
+
+ ret = 0;
+ used_page_size = 3 * nand->writesize / 4;
+ maxsize = nand->writesize;
+ for (i = 0, j = 0; i < fw_size;
+ i += used_page_size, j += nand->writesize) {
+ used_page_size_tmp = used_page_size;
+ ret |= nand_write_skip_bad(nand, off + j, &used_page_size_tmp,
+ NULL, maxsize, (u_char *)addr + i,
+ WITH_WR_VERIFY);
+ }
+ printf("Bootloader %d bytes written to 0x%x: %s\n", (int)fw_size,
+ (int) off, ret ? "ERROR" : "OK");
+
+}
+
+static int do_write_bcb(cmd_tbl_t *cmdtp, int flag, int argc,
+ char * const argv[])
+{
+ int j, k, ret;
+ uint8_t *buf;
+ size_t rwsize, maxsize;
+ ulong fw1_off, fw2_off;
+ ulong off;
+ nand_info_t *nand;
+
+ int dev = nand_curr_device;
+
+ if (argc < 2)
+ return -1;
+
+ fw1_off = simple_strtoul(argv[2], NULL, 16);
+ if (argc > 2)
+ fw2_off = simple_strtoul(argv[3], NULL, 16);
+ else
+ fw2_off = 0;
+
+ nand = &nand_info[dev];
+
+ /* Allocate one page, should be enought */
+ rwsize = nand->writesize;
+ buf = malloc(nand->writesize + nand->oobsize);
+
+ /* Set only the first page empty... */
+ memset(buf, 0xff, nand->writesize + nand->oobsize);
+
+ create_fcb(nand, buf, fw1_off, fw2_off);
+
+ puts("Write FCB...\n");
+ rwsize = maxsize = nand->writesize;
+
+ off = 0;
+ for(j = 0; j < BOOT_SEARCH_COUNT; j++) {
+ for (k = 0; k < nand->writesize + nand->oobsize; k++) {
+ *(uint8_t *)(buf + k) ^=
+ RandData[k + ((j * PAGES_PER_STRIDE) % 256)
+ / 64 * RAND_16K];
+ }
+ /* set BBM to good so we don't need nand scrub */
+ *(uint8_t *)(buf + nand->writesize) = 0xff;
+#ifdef DEBUG
+ printf("Randomized\n"); dump(buf, 512);
+#endif
+ ret = raw_access(nand, (ulong) buf, off, 1, 0);
+ /* revert randomizer */
+ for (k = 0; k < nand->writesize + nand->oobsize; k++) {
+ *(uint8_t *)(buf + k) ^=
+ RandData[k + ((j * PAGES_PER_STRIDE) % 256)
+ / 64 * RAND_16K];
+ }
+
+ printf("FCB %d bytes written to 0x%x: %s\n", rwsize,
+ (unsigned) off, ret ? "ERROR" : "OK");
+ off += nand->erasesize;
+ }
+ memset (buf, 0xff, nand->writesize + nand->oobsize);
+ create_dbbt(nand, buf);
+
+ puts("Write DBBT...\n");
+ off = BOOT_SEARCH_COUNT * nand->erasesize;
+ for(j = 0; j < BOOT_SEARCH_COUNT; j++) {
+ ret = nand_write(nand, off, &rwsize, (unsigned char *)buf);
+ printf("DBBT %d bytes written to 0x%x: %s\n", rwsize,
+ (unsigned) off, ret ? "ERROR" : "OK");
+ off += nand->erasesize;
+ }
+
+ return 0;
+}
+
+static int do_write_boot(cmd_tbl_t *cmdtp, int flag, int argc,
+ char * const argv[])
+{
+ uint8_t *addr;
+ ulong fw1_off, fw2_off, fw_size;
+ nand_info_t *nand;
+
+ int dev = nand_curr_device;
+
+ if (argc < 4)
+ return -1;
+
+ addr = (uint8_t *)simple_strtoul(argv[1], NULL, 16);
+ fw1_off = simple_strtoul(argv[2], NULL, 16);
+ if (argc > 3) {
+ fw2_off = simple_strtoul(argv[3], NULL, 16);
+ fw_size = simple_strtoul(argv[4], NULL, 16);
+ } else {
+ fw2_off = 0;
+ fw_size = simple_strtoul(argv[3], NULL, 16);
+ }
+
+ /* The FCB copies BOOTLOADER_MAXSIZE into RAM, so we must not allow
+ * a bigger bootloader */
+ if (fw_size > BOOTLOADER_MAXSIZE) {
+ printf("ERROR: Only %d bytes are copied by bootrom to RAM, your bootloader is %d\n",
+ BOOTLOADER_MAXSIZE, (int) fw_size);
+ return 1;
+ }
+ else
+ fw_size = BOOTLOADER_MAXSIZE;
+
+ nand = &nand_info[dev];
+
+ puts("Write bootloader...\n");
+ write_bootloader(nand, addr, fw1_off, fw_size);
+ if(fw2_off)
+ write_bootloader(nand, addr, fw2_off, fw_size);
+
+ return 0;
+}
+
+U_BOOT_CMD(
+ writebcb, 3, 0, do_write_bcb,
+ "Write Boot Control Block (FCB and DBBT)",
+ "fw1-off [fw2-off]"
+);
+
+U_BOOT_CMD(
+ writeboot, 5, 0, do_write_boot,
+ "Write bootloadder",
+ "addr fw1-off [fw2-off] fw_size"
+);