This patch implements updating barebox on i.MX6 NAND. In userspace
similar task is performed by freescale kobs-ng utility.
To use this bbu profile nand handler should be registered in board
code with 'imx6_bbu_internal_nand_register_handler' function.

Signed-off-by: Dmitry Lavnikevich <[email protected]>
Signed-off-by: Grigory Milev <[email protected]>
---
 arch/arm/mach-imx/imx-bbu-internal.c       | 745 ++++++++++++++++++++++++++++-
 arch/arm/mach-imx/include/mach/bbu.h       |   9 +
 arch/arm/mach-imx/include/mach/imx6-regs.h |   1 +
 3 files changed, 747 insertions(+), 8 deletions(-)

diff --git a/arch/arm/mach-imx/imx-bbu-internal.c 
b/arch/arm/mach-imx/imx-bbu-internal.c
index 9861c07..4443575 100644
--- a/arch/arm/mach-imx/imx-bbu-internal.c
+++ b/arch/arm/mach-imx/imx-bbu-internal.c
@@ -16,28 +16,67 @@
  * GNU General Public License for more details.
  */
 
-#define IMX_INTERNAL_NAND_BBU
-
 #include <common.h>
+#include <command.h>
+#include <environment.h>
 #include <malloc.h>
-#include <bbu.h>
-#include <filetype.h>
+#include <nand.h>
+#include <sizes.h>
 #include <errno.h>
-#include <fs.h>
+#include <io.h>
 #include <fcntl.h>
-#include <sizes.h>
-#include <linux/mtd/mtd-abi.h>
-#include <linux/stat.h>
+#include <libbb.h>
+#include <fs.h>
 #include <ioctl.h>
 #include <mach/bbu.h>
 #include <mach/imx-flash-header.h>
 
+#include <linux/err.h>
+#include <linux/mtd/nand.h>
+
+#define GPMI_TIMING0                           0x00000070
+#define        GPMI_TIMING0_ADDRESS_SETUP_MASK                 (0xff << 16)
+#define        GPMI_TIMING0_ADDRESS_SETUP_OFFSET               16
+#define        GPMI_TIMING0_DATA_HOLD_MASK                     (0xff << 8)
+#define        GPMI_TIMING0_DATA_HOLD_OFFSET                   8
+#define        GPMI_TIMING0_DATA_SETUP_MASK                    0xff
+#define        GPMI_TIMING0_DATA_SETUP_OFFSET                  0
+
+#define BCH_FLASH0LAYOUT0                      0x00000080
+#define        BCH_FLASHLAYOUT0_NBLOCKS_MASK                   (0xff << 24)
+#define        BCH_FLASHLAYOUT0_NBLOCKS_OFFSET                 24
+#define        BCH_FLASHLAYOUT0_META_SIZE_MASK                 (0xff << 16)
+#define        BCH_FLASHLAYOUT0_META_SIZE_OFFSET               16
+#define        BCH_FLASHLAYOUT0_ECC0_MASK                      (0x1f << 11)
+#define        BCH_FLASHLAYOUT0_ECC0_OFFSET                    11
+#define        BCH_FLASHLAYOUT0_DATA0_SIZE_MASK                0x3ff
+#define        BCH_FLASHLAYOUT0_DATA0_SIZE_OFFSET              0
+
+#define BCH_FLASH0LAYOUT1                      0x00000090
+#define        BCH_FLASHLAYOUT1_PAGE_SIZE_MASK                 (0xffff << 16)
+#define        BCH_FLASHLAYOUT1_PAGE_SIZE_OFFSET               16
+#define        BCH_FLASHLAYOUT1_ECCN_MASK                      (0x1f << 11)
+#define        BCH_FLASHLAYOUT1_ECCN_OFFSET                    11
+#define        BCH_FLASHLAYOUT1_DATAN_SIZE_MASK                0x3ff
+#define        BCH_FLASHLAYOUT1_DATAN_SIZE_OFFSET              0
+
+#define ECC_BLOCK_SIZE 512
+#define SEARCH_EXPONENT 2
+#define PAGES_PER_STRIDE 64
+#define SEARCH_AREA_SIZE_IN_STRIDES (1 << SEARCH_EXPONENT)
+#define SEARCH_AREA_SIZE_IN_PAGES      \
+       (SEARCH_AREA_SIZE_IN_STRIDES * PAGES_PER_STRIDE)
+
+#define IMX_INTERNAL_NAND_BBU
+
 #define FLASH_HEADER_OFFSET_MMC                0x400
 
 #define IMX_INTERNAL_FLAG_NAND         (1 << 0)
 #define IMX_INTERNAL_FLAG_KEEP_DOSPART (1 << 1)
 #define IMX_INTERNAL_FLAG_ERASE                (1 << 2)
 
+#define TYPICAL_NAND_READ_SIZE 2048
+
 struct imx_internal_bbu_handler {
        struct bbu_handler handler;
        const void *dcd;
@@ -48,6 +87,140 @@ struct imx_internal_bbu_handler {
        unsigned long flags;
 };
 
+struct mx6_nand_timing {
+       u8 data_setup;
+       u8 data_hold;
+       u8 address_setup;
+       u8 dsample_time;
+       u8 nand_timing_state;
+       u8 tREA;
+       u8 tRLOH;
+       u8 tRHOH;
+};
+
+struct mx6_tm_timing2 {
+       u32 read_latency;
+       u32 preamble_delay;
+       u32 ce_delay;
+       u32 postamble_delay;
+       u32 cmd_add_pause;
+       u32 data_pause;
+};
+
+struct mx6_onfi {
+       u32 onfi_sync_enable;
+       u32 onfi_sync_speed;
+       u32 read_latency;
+       u32 ce_delay;
+       u32 preamble_delay;
+       u32 postamble_delay;
+       u32 cmdadd_pause;
+       u32 data_pause;
+       u32 busy_timeout;
+};
+
+struct mx6_fcb {
+       u32 checksum;
+       u32 fingerprint;
+       u32 version;
+       struct mx6_nand_timing timing;
+       u32 page_data_size;
+       u32 total_page_size;
+       u32 sectors_per_block;
+       u32 number_of_nands;    /* not used by ROM code */
+       u32 total_internal_die; /* not used by ROM code */
+       u32 cell_type;          /* not used by ROM code */
+       u32 ecc_blockn_type;
+       u32 ecc_block0_size;
+       u32 ecc_blockn_size;
+       u32 ecc_block0_type;
+       u32 metadata_size;
+       u32 ecc_blocks_per_page;
+       u32 rsrvd[7];            /* not used by ROM code */
+       u32 rsrvd_unspec[2]; /* not specified in datasheet */
+       u32 fw1_start_page;
+       u32 fw2_start_page;
+       u32 fw1_sectors;
+       u32 fw2_sectors;
+       u32 dbbt_search_area;
+       u32 bb_mark_byte;
+       u32 bb_mark_startbit;
+       u32 bb_mark_phys_offset;
+       /* p.442 */
+       u32 bch_type;
+       struct mx6_tm_timing2 timing2;
+       u32 tm_speed;
+       u32 tm_timing1_busy_timeout;
+       u32 disbbm;
+       u32 bbmark_spare_offset;
+       struct mx6_onfi onfi;
+       u32 disbb_search;
+};
+
+struct mx6_dbbt_header {
+       u32 checksum;
+       u32 fingerprint;
+       u32 version;
+       u32 number_bb;
+       u32 number_pages;
+       u8 spare[492];
+};
+
+struct mx6_bbtn {
+       uint32_t nand;
+       uint32_t number_bb;
+       /* Divide by 4 because of 32 bit words.  Subtract 2 because of the
+        * 2 above 32 bit words.
+        */
+       uint32_t bad_block[(TYPICAL_NAND_READ_SIZE / 4) - 2];
+};
+
+#define BF_VAL(v, bf)          (((v) & bf##_MASK) >> bf##_OFFSET)
+#define GETBIT(v, n)   (((v) >> (n)) & 0x1)
+
+static u8 calculate_parity_13_8(u8 d)
+{
+       u8 p = 0;
+
+       p |= (GETBIT(d, 6) ^ GETBIT(d, 5) ^
+                 GETBIT(d, 3) ^ GETBIT(d, 2)) << 0;
+       p |= (GETBIT(d, 7) ^ GETBIT(d, 5) ^
+                 GETBIT(d, 4) ^ GETBIT(d, 2) ^
+                 GETBIT(d, 1)) << 1;
+       p |= (GETBIT(d, 7) ^ GETBIT(d, 6) ^
+                 GETBIT(d, 5) ^ GETBIT(d, 1) ^
+                 GETBIT(d, 0)) << 2;
+       p |= (GETBIT(d, 7) ^ GETBIT(d, 4) ^
+                 GETBIT(d, 3) ^ GETBIT(d, 0)) << 3;
+       p |= (GETBIT(d, 6) ^ GETBIT(d, 4) ^
+                 GETBIT(d, 3) ^ GETBIT(d, 2) ^
+                 GETBIT(d, 1) ^ GETBIT(d, 0)) << 4;
+
+       return p;
+}
+
+static void encode_hamming_13_8(void *_src, void *_ecc, size_t size)
+{
+       int i;
+       u8 *src = _src;
+       u8 *ecc = _ecc;
+
+       for (i = 0; i < size; i++)
+               ecc[i] = calculate_parity_13_8(src[i]);
+}
+
+static u32 calc_chksum(void *buf, size_t size)
+{
+       u32 chksum = 0;
+       u8 *bp = buf;
+       size_t i;
+
+       for (i = 0; i < size; i++)
+               chksum += bp[i];
+
+       return ~chksum;
+}
+
 /*
  * Actually write an image to the target device, eventually keeping a
  * DOS partition table on the device
@@ -442,6 +615,544 @@ out_free_buf:
 }
 
 /*
+  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 int calc_bb_offset(struct mtd_info *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;
+}
+
+static struct mx6_fcb *create_fcb(struct mtd_info *mtd, void *buf,
+       unsigned fw1_start_block, size_t fw_size, unsigned fw2_start_block)
+{
+       u32 fl0, fl1, t0;
+       int metadata_size;
+       int bb_mark_bit_offs;
+       struct mx6_fcb *fcb;
+       int fcb_offs;
+       void __iomem *bch_regs = dev_get_mem_region_by_name(mtd->parent, "bch");
+       void __iomem *gpmi_regs =
+               dev_get_mem_region_by_name(mtd->parent, "gpmi-nand");
+
+       fl0 = readl(bch_regs + BCH_FLASH0LAYOUT0);
+       fl1 = readl(bch_regs + BCH_FLASH0LAYOUT1);
+       t0 = readl(gpmi_regs + GPMI_TIMING0);
+       metadata_size = BF_VAL(fl0, BCH_FLASHLAYOUT0_META_SIZE);
+
+       fcb = buf + ALIGN(metadata_size, 4);
+       fcb_offs = (void *)fcb - buf;
+
+       memset(buf, 0x00, fcb_offs);
+       memset(fcb, 0x00, sizeof(*fcb));
+       memset(fcb + 1, 0xff, mtd->erasesize - fcb_offs - sizeof(*fcb));
+
+       strncpy((char *)&fcb->fingerprint, "FCB ", 4);
+       fcb->version = cpu_to_be32(1);
+
+       fcb->timing.data_setup = BF_VAL(t0, GPMI_TIMING0_DATA_SETUP);
+       fcb->timing.data_hold = BF_VAL(t0, GPMI_TIMING0_DATA_HOLD);
+       fcb->timing.address_setup = BF_VAL(t0, GPMI_TIMING0_ADDRESS_SETUP);
+
+       fcb->page_data_size = mtd->writesize;
+       fcb->total_page_size = mtd->writesize + mtd->oobsize;
+       fcb->sectors_per_block = mtd->erasesize / mtd->writesize;
+
+       /* Should be working without hardcode. Hardcoede values are
+        * taken from kobs-ng
+        */
+       fcb->ecc_block0_type = BF_VAL(fl0, BCH_FLASHLAYOUT0_ECC0);
+       fcb->ecc_blockn_type = BF_VAL(fl1, BCH_FLASHLAYOUT1_ECCN);
+       fcb->ecc_block0_size = ECC_BLOCK_SIZE;
+       fcb->ecc_blockn_size = ECC_BLOCK_SIZE;
+
+       fcb->metadata_size = BF_VAL(fl0, BCH_FLASHLAYOUT0_META_SIZE);
+       fcb->ecc_blocks_per_page = BF_VAL(fl0, BCH_FLASHLAYOUT0_NBLOCKS);
+
+       fcb->fw1_start_page = fw1_start_block / mtd->writesize;
+       /* Subtract safe guard page from number of fw sectors */
+       fcb->fw1_sectors = DIV_ROUND_UP(fw_size, mtd->writesize) - 1;
+       if (fw2_start_block) {
+               fcb->fw2_start_page = fw2_start_block / mtd->writesize;
+               fcb->fw2_sectors = fcb->fw1_sectors;
+       }
+
+       fcb->dbbt_search_area = SEARCH_AREA_SIZE_IN_PAGES;
+       fcb->number_of_nands = 0;    /* not used */
+       fcb->total_internal_die = 0; /* not used */
+       fcb->cell_type = 0;          /* not used */
+
+       /* p.442 */
+       fcb->bch_type = 0;
+       fcb->timing2.read_latency = 0;
+       fcb->timing2.preamble_delay = 0;
+       fcb->timing2.ce_delay = 0;
+       fcb->timing2.postamble_delay = 0;
+       fcb->timing2.cmd_add_pause = 0;
+       fcb->timing2.data_pause = 0;
+
+       fcb->tm_speed = 0;
+       fcb->tm_timing1_busy_timeout = 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;
+
+       fcb->checksum = calc_chksum(&fcb->fingerprint, 512 - 4);
+       return fcb;
+}
+
+static int erase_part(struct mtd_info *mtd)
+{
+       loff_t ofs;
+       int ret = 0;
+       struct erase_info erase_opts = {
+               .mtd = mtd,
+               .addr = 0,
+               .len = mtd->erasesize,
+               .callback = NULL,
+       };
+
+       for (ofs = 0; ofs < mtd->size; ofs += mtd->erasesize) {
+               ret = mtd_block_isbad(mtd, ofs);
+               if (ret > 0) {
+                       printf("Warning: bad block at 0x%08llx. Skipping\n",
+                                  ofs);
+                       continue;
+               } else if (ret < 0) {
+                       printf("Error: checking bad block at 0x%llx failed\n",
+                                  ofs);
+                       ret = 1;
+                       goto err;
+               }
+
+               erase_opts.addr = ofs;
+               ret = mtd->erase(mtd, &erase_opts);
+               if (ret) {
+                       printf("Error: failed to erase block at 0x%08llx\n",
+                                  ofs);
+                       return ret;
+               }
+       }
+
+err:
+
+       return ret;
+}
+
+static int write_fcb(struct mtd_info *mtd, void *buf, int block)
+{
+       int ret;
+       struct nand_chip *chip = mtd->priv;
+       int page = block / mtd->writesize;
+       uint32_t offset = 0;
+       int data_len = mtd->writesize;
+       struct erase_info erase_opts = {
+               .mtd = mtd,
+               .addr = block,
+               .len = mtd->erasesize,
+               .callback = NULL,
+       };
+
+       ret = mtd->erase(mtd, &erase_opts);
+       if (ret) {
+               printf("Failed to erase FCB block %08x\n", block);
+               return ret;
+       }
+
+       printf("Writing FCB to block %08x\n", block);
+       chip->select_chip(mtd, 0);
+       ret = chip->write_page(mtd, chip,
+                                                  offset, data_len,
+                                                  buf,
+                                                  0,
+                                                  page, 0, 1);
+
+       if (ret)
+               printf("Failed to write FCB to block %08x: %d\n", block, ret);
+
+       chip->select_chip(mtd, -1);
+       return ret;
+}
+
+static int write_image(struct mtd_info *mtd, void *buf, uint32_t addr,
+       uint32_t size)
+{
+       size_t retlen;
+       int ret = 0;
+       loff_t ofs = addr;
+       size_t ofs_img = 0;
+       uint32_t size_remain;
+       int write_size;
+
+       size_remain = size;
+       printf("Writing firmware to block %08x\n", addr);
+       while (size_remain > 0) {
+               write_size = min(size_remain, mtd->erasesize);
+
+               ret = mtd_block_isbad(mtd, ofs);
+               if (ret > 0) {
+                       printf("Warning: bad block at 0x%08llx. Skipping\n",
+                                  ofs);
+                       ofs += mtd->erasesize;
+                       continue;
+               } else if (ret < 0) {
+                       printf("Error: checking bad block at 0x%llx failed\n",
+                                  ofs);
+                       ret = 1;
+                       goto err;
+               }
+
+               ret = mtd_write(mtd, ofs, write_size, &retlen, buf + ofs_img);
+               if (ret) {
+                       printf("Error: failed to write firmware block 
at0x%08llx\n",
+                                  ofs);
+                       ret = 1;
+                       goto err;
+               }
+
+               ofs += mtd->erasesize;
+               ofs_img += mtd->erasesize;
+               size_remain -= write_size;
+       }
+
+err:
+
+       return ret;
+}
+
+static void *read_imagefile(struct mtd_info *mtd, const char *filename,
+       size_t *size)
+{
+       int fd;
+       struct stat s;
+       void *buf = NULL;
+
+       if (stat(filename, &s))
+               return NULL;
+
+       if (!size)
+               return NULL;
+
+       *size = ((size_t)s.st_size + mtd->writesize - 1) / mtd->writesize;
+       *size += 1;         /* Sage guard page */
+       *size *= mtd->writesize;
+
+       /* Safe guard page is added */
+       buf = xzalloc(*size);
+
+       fd = open(filename, O_RDONLY);
+       if (fd < 0)
+               goto err_out;
+
+       if (read(fd, buf, s.st_size) < s.st_size)
+               goto err_out1;
+
+       close(fd);
+
+       return buf;
+
+err_out1:
+       close(fd);
+err_out:
+       free(buf);
+
+       return NULL;
+}
+
+struct mx6_dbbt_header *create_dbbt(struct mtd_info *mtd,
+       struct mx6_bbtn **bbtn_out)
+{
+       int ret = 0;
+       int no, nrbad = 0;
+       loff_t ofs;
+       int num_of_eb;
+       struct mx6_dbbt_header *dbbt;
+       int bb_found;
+       struct mx6_bbtn *bbtn;
+       int badmax = ARRAY_SIZE(bbtn->bad_block);
+       int search_area_size_in_bytes =
+               SEARCH_AREA_SIZE_IN_PAGES * mtd->writesize;
+
+       dbbt = malloc(sizeof(*dbbt));
+       if (!dbbt) {
+               printf("Error: failed to allocate DBBT header\n");
+               ret = 1;
+               goto end;
+       }
+       memset(dbbt, 0, sizeof(*dbbt));
+       bbtn = malloc(TYPICAL_NAND_READ_SIZE);
+       if (!bbtn) {
+               printf("Error: failed to allocate BBTN\n");
+               ret = 1;
+               if (dbbt)
+                       free(dbbt);
+               goto end;
+       }
+       *bbtn_out = bbtn;
+       memset(bbtn, 0, TYPICAL_NAND_READ_SIZE);
+
+       num_of_eb = mtd_div_by_eb(mtd->size, mtd);
+
+       strncpy((char *)&dbbt->fingerprint, "DBBT", 4);
+       dbbt->version = cpu_to_be32(1);
+       dbbt->number_pages = 1;
+
+       /* Compose BBTN */
+       nrbad = 0;
+       for (ofs = 0; ofs < mtd->size; ofs += mtd->erasesize) {
+               /* skip the two NCB areas (where ECC does not apply) */
+               if (ofs < search_area_size_in_bytes * 2)
+                       continue;
+
+               no = mtd_div_by_eb(ofs, mtd);
+
+               bb_found = mtd_block_isbad(mtd, ofs);
+               if (bb_found < 0) {
+                       printf("Error: checking bad block 0x%08llx failed\n",
+                                  ofs);
+                       ret = 1;
+                       goto err;
+               }
+
+               /* not bad */
+               if (bb_found == 0)
+                       continue;
+
+               if (nrbad < badmax)
+                       bbtn->bad_block[nrbad++] = no;
+       }
+
+       bbtn->nand = 0;
+       bbtn->number_bb = nrbad;
+
+end:
+       return dbbt;
+
+err:
+       free(dbbt);
+       free(bbtn);
+
+       return 0;
+}
+
+static int write_dbbt(struct mtd_info *mtd, struct mx6_dbbt_header *dbbt,
+       void *buf, int addr, uint32_t size)
+{
+       size_t retlen;
+       int ret = 0;
+
+       memset(buf, 0, mtd->erasesize);
+       memcpy(buf, dbbt, sizeof(*dbbt));
+
+       printf("Writing DBBT to block %08x\n", addr);
+       ret = mtd_write(mtd, addr, mtd->writesize, &retlen, buf);
+       if (ret) {
+               printf("Error: failed to write DBBT by addr %08x: %d\n",
+                          addr, ret);
+               return 1;
+       }
+
+       return ret;
+}
+
+static int write_bbtn(struct mtd_info *mtd, struct mx6_bbtn *bbtn,
+       void *buf, int addr, uint32_t size)
+{
+       size_t retlen;
+       int ret = 0;
+
+       memset(buf, 0, mtd->erasesize);
+       memcpy(buf, bbtn, size);
+
+       printf("Writing BBTN to block %08x\n", addr);
+       ret = mtd_write(mtd, addr, mtd->writesize, &retlen, buf);
+       if (ret) {
+               printf("Error: failed to write BBTN by addr %08x: %d\n",
+                          addr, ret);
+               return 1;
+       }
+
+       return ret;
+}
+
+/*
+ * Update barebox on a v3 type internal boot (i.MX6)
+ *
+ * This constructs a FCB header and writes the firmware image to the
+ * device. It is based on code from bcb utility for i.MX28 and
+ * dedicated for handling NAND devices.
+ */
+static int imx_bbu_internal_v3_update(struct bbu_handler *handler,
+       struct bbu_data *data)
+{
+       int ret = 0;
+       int block;
+       int stride_size_in_bytes;
+       void *image;
+       void *buf;
+       struct mx6_fcb *fcb;
+       struct mx6_dbbt_header *dbbt = 0;
+       struct mx6_bbtn *bbtn = 0;
+       struct cdev *bcb_cdev = 0;
+       unsigned long fw1_offset = 0;
+       unsigned long fw2_offset = 0;
+       size_t fw_size;
+       struct mtd_info *mtd;
+       unsigned fcb_written = 0;
+       int max_boot_stream_in_bytes;
+       int search_area_size_in_bytes;
+       int i;
+
+       if (file_detect_type(data->image, data->len) != filetype_arm_barebox) {
+               if (!bbu_force(data, "Not an ARM barebox image")) {
+                       ret = -EINVAL;
+                       goto err_out;
+               }
+       }
+
+       ret = bbu_confirm(data);
+       if (ret)
+               goto err_out;
+
+       printf("updating to %s\n", data->devicefile);
+
+       if (!strncmp(data->devicefile, "/dev/", 5))
+               bcb_cdev = cdev_by_name(data->devicefile + 5);
+
+       if (!bcb_cdev) {
+               pr_err("%s: No NAND barebox device!\n", __func__);
+               ret = -ENODEV;
+               goto err_out;
+       }
+
+       mtd = bcb_cdev->mtd;
+
+       buf = malloc(mtd->erasesize);
+       if (!buf) {
+               ret = -ENOMEM;
+               goto err_out;
+       }
+
+       image = read_imagefile(mtd, data->imagefile, &fw_size);
+       if (!image) {
+               ret = -errno;
+               goto err_out1;
+       }
+
+       search_area_size_in_bytes =
+               (1 << SEARCH_EXPONENT) * PAGES_PER_STRIDE * mtd->writesize;
+       max_boot_stream_in_bytes =
+               (mtd->size - search_area_size_in_bytes * 2) / 2;
+
+       fw1_offset = 2 * search_area_size_in_bytes;
+       fw2_offset = fw1_offset + max_boot_stream_in_bytes;
+
+       fcb = create_fcb(mtd, buf, fw1_offset, fw_size, fw2_offset);
+       if (IS_ERR(fcb)) {
+               printf("Failed to initialize FCB: %ld\n", PTR_ERR(fcb));
+               ret = PTR_ERR(fcb);
+               goto err_out2;
+       }
+       encode_hamming_13_8(fcb, (void *)fcb + 512, 512);
+
+       dbbt = create_dbbt(mtd, &bbtn);
+       if (!dbbt)
+               goto err_out2;
+
+       erase_part(mtd);
+
+       block = bcb_cdev->offset;
+       for (fcb_written = 0; fcb_written < 4; fcb_written++) {
+
+               if (nand_isbad_bbt(mtd, block, false))
+                       continue;
+
+               ret = write_fcb(mtd, buf, block);
+               if (ret) {
+                       printf("Failed to write FCB to block %u\n", block);
+                       goto err_out2;
+               }
+               block += mtd->erasesize;
+       }
+       ret = fcb_written ? 0 : -ENOSPC;
+       if (ret)
+               goto err_out2;
+
+       stride_size_in_bytes = PAGES_PER_STRIDE * mtd->writesize;
+       block = SEARCH_AREA_SIZE_IN_PAGES * mtd->writesize;
+       i = 0;
+       while (i < SEARCH_AREA_SIZE_IN_STRIDES) {
+               ret = write_dbbt(mtd, dbbt, buf, block, sizeof(*dbbt));
+               if (ret)
+                       goto err_out2;
+
+               block += stride_size_in_bytes;
+               i++;
+       }
+
+       block = SEARCH_AREA_SIZE_IN_PAGES * mtd->writesize + 4 * mtd->writesize;
+       i = 0;
+       while (i < SEARCH_AREA_SIZE_IN_STRIDES) {
+               ret = write_bbtn(mtd, bbtn, buf, block, sizeof(*bbtn));
+               if (ret)
+                       goto err_out2;
+
+               block += stride_size_in_bytes;
+               i++;
+       }
+
+       ret = write_image(mtd, image, fw1_offset, fw_size);
+       if (ret)
+               goto err_out2;
+       ret = write_image(mtd, image, fw2_offset, fw_size);
+       if (ret)
+               goto err_out2;
+
+err_out2:
+       if (dbbt)
+               free(dbbt);
+       if (bbtn)
+               free(bbtn);
+       free(image);
+
+err_out1:
+       free(buf);
+
+err_out:
+       return ret;
+}
+
+/*
  * On the i.MX53 the dcd data can contain several commands. Each of them must
  * have its length encoded into it. We can't express that during compile time,
  * so use this function if you are using multiple dcd commands and wish to
@@ -667,3 +1378,21 @@ int imx6_bbu_internal_spi_i2c_register_handler(const char 
*name, char *devicefil
        return imx53_bbu_internal_spi_i2c_register_handler(name, devicefile,
                flags, dcd, dcdsize, app_dest);
 }
+
+/*
+ * Register a i.MX6 internal boot update handler for NAND
+ * flashes. Implementation is adapted and ported from bcb utility for
+ * i.MX28.
+ */
+int imx6_bbu_internal_nand_register_handler(const char *name, char *devicefile,
+               unsigned long flags)
+{
+       struct imx_internal_bbu_handler *imx_handler;
+
+       imx_handler = __init_handler(name, NULL, flags);
+       imx_handler->handler.handler = imx_bbu_internal_v3_update;
+       imx_handler->flags = IMX_INTERNAL_FLAG_NAND;
+       imx_handler->handler.devicefile = devicefile;
+
+       return __register_handler(imx_handler);
+}
diff --git a/arch/arm/mach-imx/include/mach/bbu.h 
b/arch/arm/mach-imx/include/mach/bbu.h
index 3cd3b1e..b7306d7 100644
--- a/arch/arm/mach-imx/include/mach/bbu.h
+++ b/arch/arm/mach-imx/include/mach/bbu.h
@@ -33,6 +33,9 @@ int imx6_bbu_internal_spi_i2c_register_handler(const char 
*name, char *devicefil
                unsigned long flags, struct imx_dcd_v2_entry *dcd, int dcdsize,
                unsigned long app_dest);
 
+int imx6_bbu_internal_nand_register_handler(const char *name, char *devicefile,
+               unsigned long flags);
+
 #else
 
 static inline int imx51_bbu_internal_mmc_register_handler(const char *name, 
char *devicefile,
@@ -77,6 +80,12 @@ static inline int 
imx6_bbu_internal_spi_i2c_register_handler(const char *name, c
        return -ENOSYS;
 }
 
+static inline int imx6_bbu_internal_nand_register_handler(const char *name,
+               char *devicefile, unsigned long flags)
+{
+       return -ENOSYS;
+}
+
 #endif
 
 #if defined(CONFIG_BAREBOX_UPDATE_IMX_EXTERNAL_NAND)
diff --git a/arch/arm/mach-imx/include/mach/imx6-regs.h 
b/arch/arm/mach-imx/include/mach/imx6-regs.h
index 833280a..9b04142 100644
--- a/arch/arm/mach-imx/include/mach/imx6-regs.h
+++ b/arch/arm/mach-imx/include/mach/imx6-regs.h
@@ -2,6 +2,7 @@
 #define __MACH_IMX6_REGS_H
 
 #define MX6_GPMI_BASE_ADDR             0x00112000
+#define MX6_BCH_BASE_ADDR              0x00114000
 
 #define MX6_AIPS1_ARB_BASE_ADDR                0x02000000
 #define MX6_AIPS2_ARB_BASE_ADDR                0x02100000
-- 
1.9.0


_______________________________________________
barebox mailing list
[email protected]
http://lists.infradead.org/mailman/listinfo/barebox

Reply via email to