/*
- * Copyright (C) 2012-2014 Lothar Waßmann <LW@KARO-electronics.de>
+ * Copyright (C) 2012-2015 Lothar Waßmann <LW@KARO-electronics.de>
*
* See file CREDITS for list of people who contributed to this
* project.
#include <jffs2/load_kernel.h>
#include <asm/io.h>
-#include <asm/sizes.h>
-#include <asm/arch/imx-regs.h>
+#include <linux/sizes.h>
+#include <asm/arch/sys_proto.h>
#include <asm/imx-common/regs-gpmi.h>
#include <asm/imx-common/regs-bch.h>
u32 disbb_search;
};
-struct mx6_dbbt_header {
- u32 checksum;
- u32 fingerprint;
- u32 version;
- u32 number_bb;
- u32 number_pages;
- u8 spare[492];
-};
-
-struct mx6_dbbt {
- u32 nand_number;
- u32 number_bb;
- u32 bb_num[2040 / 4];
-};
-
#define BF_VAL(v, bf) (((v) & bf##_MASK) >> bf##_OFFSET)
+#define BF_SET_VAL(r, v, bf) r = ((r) & ~bf##_MASK) | (((v) << bf##_OFFSET) & bf##_MASK)
static nand_info_t *mtd = &nand_info[0];
-
-extern void *_start;
+static bool doit;
#define BIT(v,n) (((v) >> (n)) & 0x1)
ecc[i] = calculate_parity_13_8(src[i]);
}
+static struct bch_regs bch_save;
+static struct bch_regs *bch_base = (void *)BCH_BASE_ADDRESS;
+
+/*
+ * Reprogram BCH engine for 40bit ECC on chunks of 128 byte
+ * and 32 byte of metadata as required by the i.MX6UL ROM code.
+ */
+static void tx6_init_bch(void)
+{
+ u32 fl0 = readl(&bch_base->hw_bch_flash0layout0);
+ u32 fl1 = readl(&bch_base->hw_bch_flash0layout1);
+
+ bch_save.hw_bch_flash0layout0 = fl0;
+ bch_save.hw_bch_flash0layout1 = fl1;
+
+ BF_SET_VAL(fl0, 32, BCH_FLASHLAYOUT0_META_SIZE);
+ BF_SET_VAL(fl0, 7, BCH_FLASHLAYOUT0_NBLOCKS);
+
+ BF_SET_VAL(fl0, 0x14, BCH_FLASHLAYOUT0_ECC0);
+ BF_SET_VAL(fl0, 128 / 4, BCH_FLASHLAYOUT0_DATA0_SIZE);
+
+ BF_SET_VAL(fl1, 0x14, BCH_FLASHLAYOUT1_ECCN);
+ BF_SET_VAL(fl1, 128 / 4, BCH_FLASHLAYOUT1_DATAN_SIZE);
+
+ writel(fl0, &bch_base->hw_bch_flash0layout0);
+ writel(fl1, &bch_base->hw_bch_flash0layout1);
+}
+
+static void tx6_restore_bch(void)
+{
+ writel(bch_save.hw_bch_flash0layout0, &bch_base->hw_bch_flash0layout0);
+ writel(bch_save.hw_bch_flash0layout1, &bch_base->hw_bch_flash0layout1);
+}
+
static u32 calc_chksum(void *buf, size_t size)
{
u32 chksum = 0;
return ~chksum;
}
-/*
- Physical organisation of data in NAND flash:
- metadata
- payload chunk 0 (may be empty)
- ecc for metadata + payload chunk 0
- payload chunk 1
- ecc for payload chunk 1
-...
- payload chunk n
- ecc for payload chunk n
- */
-
-static inline int calc_bb_offset(nand_info_t *mtd, struct mx6_fcb *fcb)
-{
- int bb_mark_offset;
- int chunk_data_size = fcb->ecc_blockn_size * 8;
- int chunk_ecc_size = (fcb->ecc_blockn_type << 1) * 13;
- int chunk_total_size = chunk_data_size + chunk_ecc_size;
- int bb_mark_chunk, bb_mark_chunk_offs;
-
- bb_mark_offset = (mtd->writesize - fcb->metadata_size) * 8;
- if (fcb->ecc_block0_size == 0)
- bb_mark_offset -= (fcb->ecc_block0_type << 1) * 13;
-
- bb_mark_chunk = bb_mark_offset / chunk_total_size;
- bb_mark_chunk_offs = bb_mark_offset - (bb_mark_chunk * chunk_total_size);
- if (bb_mark_chunk_offs > chunk_data_size) {
- printf("Unsupported ECC layout; BB mark resides in ECC data: %u\n",
- bb_mark_chunk_offs);
- return -EINVAL;
- }
- bb_mark_offset -= bb_mark_chunk * chunk_ecc_size;
- return bb_mark_offset;
-}
-
/*
* return number of blocks to skip for a contiguous partition
* of given # blocks
return -ENOSPC;
}
-#define pr_fcb_val(p, n) debug("%s=%08x(%d)\n", #n, (p)->n, (p)->n)
+#define offset_of(p, m) ((void *)&(p)->m - (void *)(p))
+#define pr_fcb_val(p, n) debug("%-24s[%02x]=%08x(%d)\n", #n, offset_of(p, n), (p)->n, (p)->n)
static struct mx6_fcb *create_fcb(void *buf, int fw1_start_block,
int fw2_start_block, int fw_num_blocks)
{
struct gpmi_regs *gpmi_base = (void *)GPMI_BASE_ADDRESS;
- struct bch_regs *bch_base = (void *)BCH_BASE_ADDRESS;
u32 fl0, fl1;
u32 t0;
- int metadata_size;
- int bb_mark_bit_offs;
struct mx6_fcb *fcb;
int fcb_offs;
fl1 = readl(&bch_base->hw_bch_flash0layout1);
t0 = readl(&gpmi_base->hw_gpmi_timing0);
- metadata_size = BF_VAL(fl0, BCH_FLASHLAYOUT0_META_SIZE);
+ if (!is_cpu_type(MXC_CPU_MX6UL)) {
+ int metadata_size = BF_VAL(fl0, BCH_FLASHLAYOUT0_META_SIZE);
- fcb = buf + ALIGN(metadata_size, 4);
- fcb_offs = (void *)fcb - buf;
+ fcb = buf + ALIGN(metadata_size, 4);
+ fcb_offs = (void *)fcb - buf;
- memset(buf, 0xff, fcb_offs);
- memset(fcb, 0x00, sizeof(*fcb));
- memset(fcb + 1, 0xff, mtd->erasesize - fcb_offs - sizeof(*fcb));
+ memset(buf, 0xff, fcb_offs);
+ } else {
+ fcb = buf;
+ fcb_offs = 0;
+ }
+
+ memset(fcb, 0, mtd->erasesize - fcb_offs);
strncpy((char *)&fcb->fingerprint, "FCB ", 4);
fcb->version = cpu_to_be32(1);
pr_fcb_val(fcb, fw1_start_page);
pr_fcb_val(fcb, fw1_sectors);
- if (fw2_start_block != 0 && fw2_start_block < mtd->size / mtd->erasesize) {
+ if (fw2_start_block != 0 &&
+ fw2_start_block < lldiv(mtd->size, mtd->erasesize)) {
fcb->fw2_start_page = fw2_start_block * fcb->sectors_per_block;
fcb->fw2_sectors = fcb->fw1_sectors;
pr_fcb_val(fcb, fw2_start_page);
fcb->dbbt_search_area = 0;
- bb_mark_bit_offs = calc_bb_offset(mtd, fcb);
- if (bb_mark_bit_offs < 0)
- return ERR_PTR(bb_mark_bit_offs);
- fcb->bb_mark_byte = bb_mark_bit_offs / 8;
- fcb->bb_mark_startbit = bb_mark_bit_offs % 8;
- fcb->bb_mark_phys_offset = mtd->writesize;
-
- pr_fcb_val(fcb, bb_mark_byte);
- pr_fcb_val(fcb, bb_mark_startbit);
- pr_fcb_val(fcb, bb_mark_phys_offset);
-
fcb->checksum = calc_chksum(&fcb->fingerprint, 512 - 4);
return fcb;
}
return 0;
}
- ret = nand_erase(mtd, block * mtd->erasesize, mtd->erasesize);
- if (ret) {
- printf("Failed to erase FCB block %u\n", block);
- return ret;
+ if (doit) {
+ ret = nand_erase(mtd, block * mtd->erasesize, mtd->erasesize);
+ if (ret) {
+ printf("Failed to erase FCB block %u\n", block);
+ return ret;
+ }
}
printf("Writing FCB to block %d @ %08llx\n", block,
(u64)block * mtd->erasesize);
- chip->select_chip(mtd, 0);
- ret = chip->write_page(mtd, chip, buf, 1, page, 0, 1);
- if (ret) {
- printf("Failed to write FCB to block %u: %d\n", block, ret);
+ if (doit) {
+ if (is_cpu_type(MXC_CPU_MX6UL)) {
+ size_t len = mtd->writesize;
+
+ tx6_init_bch();
+ printf("writing block %u from buffer %p\n", block, buf);
+ ret = nand_write(mtd, block * mtd->erasesize, &len, buf);
+ tx6_restore_bch();
+ } else {
+ chip->select_chip(mtd, 0);
+ ret = chip->write_page(mtd, chip, 0, mtd->writesize,
+ buf, 1, page, 0, 1);
+ }
+ if (ret) {
+ printf("Failed to write FCB to block %u: %d\n", block, ret);
+ }
+ chip->select_chip(mtd, -1);
}
- chip->select_chip(mtd, -1);
return ret;
}
-struct mx6_ivt {
- u32 magic;
- u32 entry;
- u32 rsrvd1;
- void *dcd;
- void *boot_data;
- void *self;
- void *csf;
- u32 rsrvd2;
-};
-
-struct mx6_boot_data {
- u32 start;
- u32 length;
- u32 plugin;
-};
-
-static int find_ivt(void *buf)
-{
- struct mx6_ivt *ivt_hdr = buf + 0x400;
-
- if ((ivt_hdr->magic & 0xff0000ff) != 0x400000d1)
- return 0;
-
- return 1;
-}
-
-static inline void *reloc(void *dst, void *base, void *ptr)
-{
- return dst + (ptr - base);
-}
-
-static int patch_ivt(void *buf, size_t fsize)
-{
- struct mx6_ivt *ivt_hdr = buf + 0x400;
- struct mx6_boot_data *boot_data;
-
- if (!find_ivt(buf)) {
- printf("No IVT found in image at %p\n", buf);
- return -EINVAL;
- }
- boot_data = reloc(ivt_hdr, ivt_hdr->self, ivt_hdr->boot_data);
- boot_data->length = fsize;
-
- return 0;
-}
-
#define chk_overlap(a,b) \
((a##_start_block <= b##_end_block && \
a##_end_block >= b##_start_block) || \
(b##_start_block <= a##_end_block && \
b##_end_block >= a##_start_block))
-#define fail_if_overlap(a,b,m1,m2) do { \
- if (chk_overlap(a, b)) { \
+#define fail_if_overlap(a,b,m1,m2) do { \
+ if (!doit) \
+ printf("check: %s[%lu..%lu] <> %s[%lu..%lu]\n", \
+ m1, a##_start_block, a##_end_block, \
+ m2, b##_start_block, b##_end_block); \
+ if (a##_end_block < a##_start_block) \
+ printf("Invalid start/end block # for %s\n", m1); \
+ if (b##_end_block < b##_start_block) \
+ printf("Invalid start/end block # for %s\n", m2); \
+ if (chk_overlap(a, b)) { \
printf("%s blocks %lu..%lu overlap %s in blocks %lu..%lu!\n", \
- m1, a##_start_block, a##_end_block, \
- m2, b##_start_block, b##_end_block); \
- return -EINVAL; \
- } \
+ m1, a##_start_block, a##_end_block, \
+ m2, b##_start_block, b##_end_block); \
+ return -EINVAL; \
+ } \
} while (0)
static int tx6_prog_uboot(void *addr, int start_block, int skip,
printf("Erasing flash @ %08llx..%08llx\n", erase_opts.offset,
erase_opts.offset + erase_opts.length - 1);
- ret = nand_erase_opts(mtd, &erase_opts);
- if (ret) {
- printf("Failed to erase flash: %d\n", ret);
- return ret;
+ if (doit) {
+ ret = nand_erase_opts(mtd, &erase_opts);
+ if (ret) {
+ printf("Failed to erase flash: %d\n", ret);
+ return ret;
+ }
}
- printf("Programming flash @ %08llx..%08llx from %p\n",
- (u64)start_block * mtd->erasesize,
- (u64)start_block * mtd->erasesize + size - 1, addr);
- actual = size;
- ret = nand_write_skip_bad(mtd, prg_start, &actual, NULL,
- prg_length, addr, WITH_DROP_FFS);
- if (ret) {
- printf("Failed to program flash: %d\n", ret);
- return ret;
- }
- if (actual < size) {
- printf("Could only write %u of %u bytes\n", actual, size);
- return -EIO;
+ printf("Programming flash @ %08x..%08x from %p\n",
+ prg_start, prg_start + size - 1, addr);
+ if (doit) {
+ actual = size;
+ ret = nand_write_skip_bad(mtd, prg_start, &actual, NULL,
+ prg_length, addr, 0);
+ if (ret) {
+ printf("Failed to program flash: %d\n", ret);
+ return ret;
+ }
+ if (actual < size) {
+ printf("Could only write %u of %u bytes\n", actual, size);
+ return -EIO;
+ }
}
return 0;
}
int ret;
const unsigned long fcb_start_block = 0, fcb_end_block = 0;
int erase_size = mtd->erasesize;
- int page_size = mtd->writesize;
void *buf;
char *load_addr;
char *file_size;
size_t size = 0;
void *addr = NULL;
struct mx6_fcb *fcb;
- unsigned long mtd_num_blocks = mtd->size / mtd->erasesize;
+ unsigned long mtd_num_blocks = lldiv(mtd->size, mtd->erasesize);
#ifdef CONFIG_ENV_IS_IN_NAND
unsigned long env_start_block = CONFIG_ENV_OFFSET / mtd->erasesize;
unsigned long env_end_block = env_start_block +
unsigned long fw_num_blocks;
int fw1_skip, fw2_skip;
unsigned long extra_blocks = 0;
- size_t max_len1, max_len2;
+ u64 max_len1, max_len2;
struct mtd_device *dev;
struct part_info *part_info;
struct part_info *redund_part_info;
if (ret)
return ret;
+ doit = true;
for (optind = 1; optind < argc; optind++) {
char *endp;
mtd_num_blocks - 1);
return -EINVAL;
}
+ } else if (strcmp(argv[optind], "-n") == 0) {
+ doit = false;
} else if (argv[optind][0] == '-') {
printf("Unrecognized option %s\n", argv[optind]);
return -EINVAL;
return -EINVAL;
}
if (argc - optind < 2 && file_size == NULL) {
- printf("WARNING: Image size not specified; overwriting whole uboot partition\n");
+ if (uboot_part) {
+ printf("WARNING: Image size not specified; overwriting whole '%s' partition\n",
+ uboot_part);
+ printf("This will only work, if there are no bad blocks inside this partition!\n");
+ } else {
+ printf("ERROR: Image size must be specified\n");
+ return -EINVAL;
+ }
}
if (argc > optind) {
load_addr = NULL;
size = simple_strtoul(file_size, NULL, 16);
printf("Using default file size %08x\n", size);
}
- if (size > 0) {
+ if (size > 0)
fw_num_blocks = DIV_ROUND_UP(size, mtd->erasesize);
- } else {
- fw_num_blocks = part_info->size / mtd->erasesize -
- extra_blocks;
- size = fw_num_blocks * mtd->erasesize;
- }
+ else
+ fw_num_blocks = 0;
if (uboot_part) {
ret = find_dev_and_part(uboot_part, &dev, &part_num,
uboot_part, ret);
return ret;
}
- fw1_start_block = part_info->offset / mtd->erasesize;
+ fw1_start_block = lldiv(part_info->offset, mtd->erasesize);
max_len1 = part_info->size;
+ if (size == 0)
+ fw_num_blocks = lldiv(max_len1, mtd->erasesize);
} else {
- max_len1 = (fw_num_blocks + extra_blocks) * mtd->erasesize;
+ max_len1 = (u64)(fw_num_blocks + extra_blocks) * mtd->erasesize;
}
if (redund_part) {
redund_part, ret);
return ret;
}
- fw2_start_block = redund_part_info->offset / mtd->erasesize;
+ fw2_start_block = lldiv(redund_part_info->offset, mtd->erasesize);
max_len2 = redund_part_info->size;
+ if (fw2_start_block == fcb_start_block) {
+ fw2_start_block++;
+ max_len2 -= mtd->erasesize;
+ }
+ if (size == 0)
+ fw_num_blocks = lldiv(max_len2, mtd->erasesize);
} else if (fw2_set) {
- max_len2 = (fw_num_blocks + extra_blocks) * mtd->erasesize;
+ max_len2 = (u64)(fw_num_blocks + extra_blocks) * mtd->erasesize;
} else {
max_len2 = 0;
}
fw1_skip = find_contig_space(fw1_start_block, fw_num_blocks,
- max_len1 / mtd->erasesize);
+ lldiv(max_len1, mtd->erasesize));
if (fw1_skip < 0) {
- printf("Could not find %lu contiguous good blocks for fw image\n",
- fw_num_blocks);
+ printf("Could not find %lu contiguous good blocks for fw image in blocks %lu..%llu\n",
+ fw_num_blocks, fw1_start_block,
+ fw1_start_block + lldiv(max_len1, mtd->erasesize) - 1);
if (uboot_part) {
#ifdef CONFIG_ENV_IS_IN_NAND
if (part_info->offset <= CONFIG_ENV_OFFSET + TOTAL_ENV_SIZE) {
}
return -ENOSPC;
}
- fw1_end_block = fw1_start_block + fw1_skip + fw_num_blocks - 1;
+ if (extra_blocks)
+ fw1_end_block = fw1_start_block + fw_num_blocks + extra_blocks - 1;
+ else
+ fw1_end_block = fw1_start_block + fw_num_blocks + fw1_skip - 1;
if (fw2_set && fw2_start_block == 0)
fw2_start_block = fw1_end_block + 1;
if (fw2_start_block > 0) {
fw2_skip = find_contig_space(fw2_start_block, fw_num_blocks,
- max_len2 / mtd->erasesize);
+ lldiv(max_len2, mtd->erasesize));
if (fw2_skip < 0) {
- printf("Could not find %lu contiguous good blocks for redundant fw image\n",
- fw_num_blocks);
+ printf("Could not find %lu contiguous good blocks for redundant fw image in blocks %lu..%llu\n",
+ fw_num_blocks, fw2_start_block,
+ fw2_start_block + lldiv(max_len2, mtd->erasesize) - 1);
if (redund_part) {
printf("Increase the size of the '%s' partition or use a different partition\n",
redund_part);
} else {
fw2_skip = 0;
}
- fw2_end_block = fw2_start_block + fw2_skip + fw_num_blocks - 1;
+ if (extra_blocks)
+ fw2_end_block = fw2_start_block + fw_num_blocks + extra_blocks - 1;
+ else
+ fw2_end_block = fw2_start_block + fw_num_blocks + fw2_skip - 1;
#ifdef CONFIG_ENV_IS_IN_NAND
fail_if_overlap(fcb, env, "FCB", "Environment");
#endif
fail_if_overlap(fw1, fw2, "FW1", "FW2");
}
+ fw1_start_block += fw1_skip;
+ fw2_start_block += fw2_skip;
- buf = malloc(erase_size);
+ buf = memalign(SZ_128K, erase_size);
if (buf == NULL) {
printf("Failed to allocate buffer\n");
return -ENOMEM;
}
- fcb = create_fcb(buf, fw1_start_block + fw1_skip,
- fw2_start_block + fw2_skip, fw_num_blocks);
+ fcb = create_fcb(buf, fw1_start_block,
+ fw2_start_block, fw_num_blocks);
if (IS_ERR(fcb)) {
printf("Failed to initialize FCB: %ld\n", PTR_ERR(fcb));
- free(buf);
- return PTR_ERR(fcb);
+ ret = PTR_ERR(fcb);
+ goto out;
}
- encode_hamming_13_8(fcb, (void *)fcb + 512, 512);
+ if (!is_cpu_type(MXC_CPU_MX6UL))
+ encode_hamming_13_8(fcb, (void *)fcb + 512, 512);
ret = write_fcb(buf, fcb_start_block);
- free(buf);
if (ret) {
printf("Failed to write FCB to block %lu\n", fcb_start_block);
return ret;
}
- ret = patch_ivt(addr, size);
- if (ret) {
- return ret;
- }
-
- if (size & (page_size - 1)) {
- memset(addr + size, 0xff, size & (page_size - 1));
- size = ALIGN(size, page_size);
- }
printf("Programming U-Boot image from %p to block %lu @ %08llx\n",
- addr, fw1_start_block + fw1_skip,
- (u64)(fw1_start_block + fw1_skip) * mtd->erasesize);
+ buf, fw1_start_block, (u64)fw1_start_block * mtd->erasesize);
ret = tx6_prog_uboot(addr, fw1_start_block, fw1_skip, size,
max_len1);
- if (fw2_start_block == 0) {
- return ret;
- }
+ if (ret || fw2_start_block == 0)
+ goto out;
printf("Programming redundant U-Boot image to block %lu @ %08llx\n",
- fw2_start_block + fw2_skip,
- (u64)(fw2_start_block + fw2_skip) * mtd->erasesize);
- ret = tx6_prog_uboot(addr, fw2_start_block, fw2_skip, fw_num_blocks,
+ fw2_start_block, (u64)fw2_start_block * mtd->erasesize);
+ ret = tx6_prog_uboot(addr, fw2_start_block, fw2_skip, size,
max_len2);
+out:
+ free(buf);
return ret;
}
"Creates an FCB data structure and writes an U-Boot image to flash",
"[-f {<part>|block#}] [-r [{<part>|block#}]] [-e #] [<address>] [<length>]\n"
"\t-f <part>\twrite bootloader image to partition <part>\n"
- "\t-f #\twrite bootloader image at block # (decimal)\n"
- "\t-r\twrite redundant bootloader image at next free block after first image\n"
+ "\t-f #\t\twrite bootloader image at block # (decimal)\n"
+ "\t-r\t\twrite redundant bootloader image at next free block after first image\n"
"\t-r <part>\twrite redundant bootloader image to partition <part>\n"
- "\t-r #\twrite redundant bootloader image at block # (decimal)\n"
- "\t-e #\tspecify number of redundant blocks per boot loader image\n"
- "\t\tonly valid if -f or -r specify a flash address rather than a partition name\n"
- "\t<address>\tRAM address of bootloader image (default: ${fileaddr}\n"
- "\t<length>\tlength of bootloader image in RAM (default: ${filesize}"
+ "\t-r #\t\twrite redundant bootloader image at block # (decimal)\n"
+ "\t-e #\t\tspecify number of redundant blocks per boot loader image\n"
+ "\t\t\t(only valid if -f or -r specify a flash address rather than a partition name)\n"
+ "\t-n\t\tshow what would be done without actually updating the flash\n"
+ "\t<address>\tRAM address of bootloader image (default: ${fileaddr})\n"
+ "\t<length>\tlength of bootloader image in RAM (default: ${filesize})"
);