]> git.kernelconcepts.de Git - karo-tx-uboot.git/blobdiff - board/karo/tx6/flash.c
karo: tx6ul: disable the PMIC for '_noenv' U-Boot
[karo-tx-uboot.git] / board / karo / tx6 / flash.c
index d57c9947073a9e0ac24fd0f7cdb604f9fc32c8a6..c46d28bd3092fd36431f92fd98421899b6bc2f71 100644 (file)
@@ -25,7 +25,7 @@
 
 #include <asm/io.h>
 #include <linux/sizes.h>
-#include <asm/arch/imx-regs.h>
+#include <asm/arch/sys_proto.h>
 #include <asm/imx-common/regs-gpmi.h>
 #include <asm/imx-common/regs-bch.h>
 
@@ -76,39 +76,8 @@ struct mx6_fcb {
        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];
-};
-
-struct mx6_ivt {
-       u32 magic;
-       u32 app_start_addr;
-       u32 rsrvd1;
-       void *dcd;
-       void *boot_data;
-       void *self;
-       void *csf;
-       u32 rsrvd2;
-};
-
-struct mx6_boot_data {
-       void *start;
-       u32 length;
-       u32 plugin;
-};
-
 #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];
 static bool doit;
@@ -137,6 +106,40 @@ static void encode_hamming_13_8(void *_src, void *_ecc, size_t size)
                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;
@@ -149,41 +152,6 @@ static u32 calc_chksum(void *buf, size_t size)
        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
@@ -221,11 +189,8 @@ 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;
 
@@ -237,14 +202,19 @@ static struct mx6_fcb *create_fcb(void *buf, int fw1_start_block,
        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);
+       } else {
+               fcb = buf;
+               fcb_offs = 0;
+       }
 
-       memset(buf, 0xff, fcb_offs);
-       memset(fcb, 0x00, sizeof(*fcb));
-       memset(fcb + 1, 0xff, mtd->erasesize - fcb_offs - sizeof(*fcb));
+       memset(fcb, 0, mtd->erasesize - fcb_offs);
 
        strncpy((char *)&fcb->fingerprint, "FCB ", 4);
        fcb->version = cpu_to_be32(1);
@@ -281,7 +251,8 @@ static struct mx6_fcb *create_fcb(void *buf, int fw1_start_block,
        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);
@@ -290,17 +261,6 @@ static struct mx6_fcb *create_fcb(void *buf, int fw1_start_block,
 
        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;
 }
@@ -355,9 +315,18 @@ static int write_fcb(void *buf, int block)
        printf("Writing FCB to block %d @ %08llx\n", block,
                (u64)block * mtd->erasesize);
        if (doit) {
-               chip->select_chip(mtd, 0);
-               ret = chip->write_page(mtd, chip, 0, mtd->writesize,
-                               buf, 1, page, 0, 1);
+               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);
                }
@@ -417,7 +386,7 @@ static int tx6_prog_uboot(void *addr, int start_block, int skip,
        if (doit) {
                actual = size;
                ret = nand_write_skip_bad(mtd, prg_start, &actual, NULL,
-                                       prg_length, addr, WITH_DROP_FFS);
+                                       prg_length, addr, 0);
                if (ret) {
                        printf("Failed to program flash: %d\n", ret);
                        return ret;
@@ -449,7 +418,7 @@ int do_update(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[])
        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 +
@@ -462,7 +431,7 @@ int do_update(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[])
        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;
@@ -586,12 +555,12 @@ int do_update(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[])
                                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 = max_len1 / mtd->erasesize;
+                       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) {
@@ -602,26 +571,26 @@ int do_update(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[])
                                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 = max_len2 / mtd->erasesize;
+                       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 in blocks %lu..%lu\n",
+               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 + max_len1 / mtd->erasesize - 1);
+                       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) {
@@ -648,11 +617,11 @@ int do_update(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[])
                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 in blocks %lu..%lu\n",
+                       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 + max_len2 / mtd->erasesize - 1);
+                               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);
@@ -697,7 +666,8 @@ int do_update(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[])
                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);
        if (ret) {