From mboxrd@z Thu Jan 1 00:00:00 1970 Return-path: Received: from s250.sam-solutions.net ([217.21.49.219]) by merlin.infradead.org with esmtps (Exim 4.80.1 #2 (Red Hat Linux)) id 1WNe9u-00012c-K0 for barebox@lists.infradead.org; Wed, 12 Mar 2014 08:05:05 +0000 Received: from s246.sam-solutions.net ([217.21.35.55]) by s250.sam-solutions.net with esmtps (TLSv1:RC4-MD5:128) (Exim 4.77) (envelope-from ) id 1WNe9V-0007dB-T5 for barebox@lists.infradead.org; Wed, 12 Mar 2014 11:04:37 +0300 From: Dmitry Lavnikevich Date: Wed, 12 Mar 2014 10:54:11 +0300 Message-ID: <1394610851-721-1-git-send-email-d.lavnikevich@sam-solutions.com> MIME-Version: 1.0 List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Sender: "barebox" Errors-To: barebox-bounces+u.kleine-koenig=pengutronix.de@lists.infradead.org Subject: [PATCH] i.MX6: bbu: Barebox update support for NAND. To: barebox@lists.infradead.org Cc: Grigory Milev , Dmitry Lavnikevich 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 Signed-off-by: Grigory Milev --- 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 +#include +#include #include -#include -#include +#include +#include #include -#include +#include #include -#include -#include -#include +#include +#include #include #include #include +#include +#include + +#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 barebox@lists.infradead.org http://lists.infradead.org/mailman/listinfo/barebox