* [PATCH] i.MX6: bbu: Barebox update support for NAND.
@ 2014-03-12 7:54 Dmitry Lavnikevich
2014-03-13 6:54 ` Sascha Hauer
0 siblings, 1 reply; 7+ messages in thread
From: Dmitry Lavnikevich @ 2014-03-12 7:54 UTC (permalink / raw)
To: barebox; +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 <d.lavnikevich@sam-solutions.com>
Signed-off-by: Grigory Milev <g.milev@sam-solutions.com>
---
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
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH] i.MX6: bbu: Barebox update support for NAND.
2014-03-12 7:54 [PATCH] i.MX6: bbu: Barebox update support for NAND Dmitry Lavnikevich
@ 2014-03-13 6:54 ` Sascha Hauer
2014-03-13 7:50 ` Dmitry Lavnikevich
` (2 more replies)
0 siblings, 3 replies; 7+ messages in thread
From: Sascha Hauer @ 2014-03-13 6:54 UTC (permalink / raw)
To: Dmitry Lavnikevich; +Cc: Grigory Milev, barebox
Hi Dmitry,
On Wed, Mar 12, 2014 at 10:54:11AM +0300, Dmitry Lavnikevich wrote:
> 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.
I have the very same patch in the queue, it seems we have duplicated
some work. As this version has some shortcomings I'll post my version as
an alternative implementation. Also I'll try your version on a board I
have here on which my version doesn't work (which is the reason I
haven't posted my patch yet)
> +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;
This is not worth issueing a warning. Bad blocks just happen.
> + } 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);
You should use mtd_erase and friends rather than dereferencing struct
mtd_info.
> + 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;
> + }
No Need to read the image, data->image contains a pointer to it.
> +
> + 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);
Error checking?
> +
> + 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;
This doesn't seem to take bad blocks into account. What happens when the
first firmware image gets bigger due to bad blocks? It will be
overwritten by the 2nd image.
Sascha
--
Pengutronix e.K. | |
Industrial Linux Solutions | http://www.pengutronix.de/ |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply [flat|nested] 7+ messages in thread
* RE: [PATCH] i.MX6: bbu: Barebox update support for NAND.
2014-03-13 6:54 ` Sascha Hauer
@ 2014-03-13 7:50 ` Dmitry Lavnikevich
2014-03-20 10:28 ` Dmitry Lavnikevich
2014-03-31 8:42 ` Sascha Hauer
2 siblings, 0 replies; 7+ messages in thread
From: Dmitry Lavnikevich @ 2014-03-13 7:50 UTC (permalink / raw)
To: Sascha Hauer; +Cc: Grigory Milev, barebox
Hi Sascha,
thanks for the remarks. Currently I'm ill at home and don't have access to
any hardware, so I will make this corrections if there will be need when
I will come to work.
Best regards,
Lavnikevich Dmitry
________________________________________
From: Sascha Hauer [s.hauer@pengutronix.de]
Sent: Thursday, March 13, 2014 9:54 AM
To: Dmitry Lavnikevich
Cc: barebox@lists.infradead.org; Grigory Milev
Subject: Re: [PATCH] i.MX6: bbu: Barebox update support for NAND.
Hi Dmitry,
On Wed, Mar 12, 2014 at 10:54:11AM +0300, Dmitry Lavnikevich wrote:
> 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.
I have the very same patch in the queue, it seems we have duplicated
some work. As this version has some shortcomings I'll post my version as
an alternative implementation. Also I'll try your version on a board I
have here on which my version doesn't work (which is the reason I
haven't posted my patch yet)
> +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;
This is not worth issueing a warning. Bad blocks just happen.
> + } 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);
You should use mtd_erase and friends rather than dereferencing struct
mtd_info.
> + 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;
> + }
No Need to read the image, data->image contains a pointer to it.
> +
> + 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);
Error checking?
> +
> + 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;
This doesn't seem to take bad blocks into account. What happens when the
first firmware image gets bigger due to bad blocks? It will be
overwritten by the 2nd image.
Sascha
--
Pengutronix e.K. | |
Industrial Linux Solutions | http://www.pengutronix.de/ |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH] i.MX6: bbu: Barebox update support for NAND.
2014-03-13 6:54 ` Sascha Hauer
2014-03-13 7:50 ` Dmitry Lavnikevich
@ 2014-03-20 10:28 ` Dmitry Lavnikevich
2014-03-22 7:06 ` Sascha Hauer
2014-03-31 8:42 ` Sascha Hauer
2 siblings, 1 reply; 7+ messages in thread
From: Dmitry Lavnikevich @ 2014-03-20 10:28 UTC (permalink / raw)
To: Sascha Hauer; +Cc: Grigory Milev, barebox
Hello Sascha,
have you tried my patch on your board yet?
I will correct mentioned points and resent it to mail list later.
I'm also interested to see your implementation of this functionality.
Particulary regarding the
> This doesn't seem to take bad blocks into account. What happens when the
> first firmware image gets bigger due to bad blocks? It will be
> overwritten by the 2nd image.
part.
Best regards,
Lavnikevich Dmitry
On 03/13/14 09:54, Sascha Hauer wrote:
> Hi Dmitry,
>
> On Wed, Mar 12, 2014 at 10:54:11AM +0300, Dmitry Lavnikevich wrote:
>> 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.
> I have the very same patch in the queue, it seems we have duplicated
> some work. As this version has some shortcomings I'll post my version as
> an alternative implementation. Also I'll try your version on a board I
> have here on which my version doesn't work (which is the reason I
> haven't posted my patch yet)
>
>> +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;
> This is not worth issueing a warning. Bad blocks just happen.
>
>> + } 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);
> You should use mtd_erase and friends rather than dereferencing struct
> mtd_info.
>
>> + 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;
>> + }
> No Need to read the image, data->image contains a pointer to it.
>
>> +
>> + 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);
> Error checking?
>
>> +
>> + 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;
> This doesn't seem to take bad blocks into account. What happens when the
> first firmware image gets bigger due to bad blocks? It will be
> overwritten by the 2nd image.
>
> Sascha
>
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH] i.MX6: bbu: Barebox update support for NAND.
2014-03-20 10:28 ` Dmitry Lavnikevich
@ 2014-03-22 7:06 ` Sascha Hauer
0 siblings, 0 replies; 7+ messages in thread
From: Sascha Hauer @ 2014-03-22 7:06 UTC (permalink / raw)
To: Dmitry Lavnikevich; +Cc: Grigory Milev, barebox
Hi Dmitry,
On Thu, Mar 20, 2014 at 01:28:59PM +0300, Dmitry Lavnikevich wrote:
> Hello Sascha,
>
> have you tried my patch on your board yet?
> I will correct mentioned points and resent it to mail list later.
> I'm also interested to see your implementation of this functionality.
> Particulary regarding the
>
> >This doesn't seem to take bad blocks into account. What happens when the
> >first firmware image gets bigger due to bad blocks? It will be
> >overwritten by the 2nd image.
>
> part.
We still didn't manage to make that board boot. Here's my version of the
patch in the meantime.
Sascha
--------------------------------8<-----------------------------
From 86d47e0f5664f4d5115dcfe74fb1e124decf01cd Mon Sep 17 00:00:00 2001
From: Sascha Hauer <s.hauer@pengutronix.de>
Date: Thu, 6 Mar 2014 10:03:33 +0100
Subject: [PATCH] ARM: i.MX6: Add Nand boot bbu handler
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
arch/arm/mach-imx/Makefile | 2 +-
arch/arm/mach-imx/imx6-bbu-nand.c | 489 +++++++++++++++++++++++++++++++++++
arch/arm/mach-imx/include/mach/bbu.h | 6 +
3 files changed, 496 insertions(+), 1 deletion(-)
create mode 100644 arch/arm/mach-imx/imx6-bbu-nand.c
diff --git a/arch/arm/mach-imx/Makefile b/arch/arm/mach-imx/Makefile
index b3f00f9..e7997fb 100644
--- a/arch/arm/mach-imx/Makefile
+++ b/arch/arm/mach-imx/Makefile
@@ -18,6 +18,6 @@ lwl-$(CONFIG_ARCH_IMX_EXTERNAL_BOOT_NAND) += external-nand-boot.o
obj-$(CONFIG_COMMON_CLK) += clk-pllv1.o clk-pllv2.o clk-pllv3.o clk-pfd.o
obj-y += devices.o imx.o esdctl.o
obj-y += boot.o
-obj-$(CONFIG_BAREBOX_UPDATE) += imx-bbu-internal.o
+obj-$(CONFIG_BAREBOX_UPDATE) += imx-bbu-internal.o imx6-bbu-nand.o
obj-$(CONFIG_BAREBOX_UPDATE_IMX_EXTERNAL_NAND) += imx-bbu-external-nand.o
pbl-y += esdctl.o
diff --git a/arch/arm/mach-imx/imx6-bbu-nand.c b/arch/arm/mach-imx/imx6-bbu-nand.c
new file mode 100644
index 0000000..f6d16e7
--- /dev/null
+++ b/arch/arm/mach-imx/imx6-bbu-nand.c
@@ -0,0 +1,489 @@
+/*
+ * Copyright (C) 2014 Sascha Hauer, Pengutronix
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation.
+ *
+ */
+
+#define pr_fmt(fmt) "imx6-bbu-nand: " fmt
+
+#include <filetype.h>
+#include <common.h>
+#include <malloc.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <ioctl.h>
+#include <sizes.h>
+#include <bbu.h>
+#include <fs.h>
+#include <mach/bbu.h>
+#include <linux/mtd/mtd-abi.h>
+#include <linux/mtd/mtd.h>
+#include <linux/stat.h>
+
+struct dbbt_block {
+ uint32_t Checksum;
+ uint32_t FingerPrint;
+ uint32_t Version;
+ uint32_t reserved;
+ uint32_t DBBTNumOfPages;
+};
+
+struct fcb_block {
+ uint32_t Checksum; /* First fingerprint in first byte */
+ uint32_t FingerPrint; /* 2nd fingerprint at byte 4 */
+ uint32_t Version; /* 3rd fingerprint at byte 8 */
+ uint8_t DataSetup;
+ uint8_t DataHold;
+ uint8_t AddressSetup;
+ uint8_t DSAMPLE_TIME;
+ /* These are for application use only and not for ROM. */
+ uint8_t NandTimingState;
+ uint8_t REA;
+ uint8_t RLOH;
+ uint8_t RHOH;
+ uint32_t PageDataSize; /* 2048 for 2K pages, 4096 for 4K pages */
+ uint32_t TotalPageSize; /* 2112 for 2K pages, 4314 for 4K pages */
+ uint32_t SectorsPerBlock; /* Number of 2K sections per block */
+ uint32_t NumberOfNANDs; /* Total Number of NANDs - not used by ROM */
+ uint32_t TotalInternalDie; /* Number of separate chips in this NAND */
+ uint32_t CellType; /* MLC or SLC */
+ uint32_t EccBlockNEccType; /* Type of ECC, can be one of BCH-0-20 */
+ uint32_t EccBlock0Size; /* Number of bytes for Block0 - BCH */
+ uint32_t EccBlockNSize; /* Block size in bytes for all blocks other than Block0 - BCH */
+ uint32_t EccBlock0EccType; /* Ecc level for Block 0 - BCH */
+ uint32_t MetadataBytes; /* Metadata size - BCH */
+ uint32_t NumEccBlocksPerPage; /* Number of blocks per page for ROM use - BCH */
+ uint32_t EccBlockNEccLevelSDK; /* Type of ECC, can be one of BCH-0-20 */
+ uint32_t EccBlock0SizeSDK; /* Number of bytes for Block0 - BCH */
+ uint32_t EccBlockNSizeSDK; /* Block size in bytes for all blocks other than Block0 - BCH */
+ uint32_t EccBlock0EccLevelSDK; /* Ecc level for Block 0 - BCH */
+ uint32_t NumEccBlocksPerPageSDK;/* Number of blocks per page for SDK use - BCH */
+ uint32_t MetadataBytesSDK; /* Metadata size - BCH */
+ uint32_t EraseThreshold; /* To set into BCH_MODE register */
+ uint32_t BootPatch; /* 0 for normal boot and 1 to load patch starting next to FCB */
+ uint32_t PatchSectors; /* Size of patch in sectors */
+ uint32_t Firmware1_startingPage;/* Firmware image starts on this sector */
+ uint32_t Firmware2_startingPage;/* Secondary FW Image starting Sector */
+ uint32_t PagesInFirmware1; /* Number of sectors in firmware image */
+ uint32_t PagesInFirmware2; /* Number of sector in secondary FW image */
+ uint32_t DBBTSearchAreaStartAddress; /* Page address where dbbt search area begins */
+ uint32_t BadBlockMarkerByte; /* Byte in page data that have manufacturer marked bad block marker, */
+ /* this will be swapped with metadata[0] to complete page data. */
+ uint32_t BadBlockMarkerStartBit;/* For BCH ECC sizes other than 8 and 16 the bad block marker does not */
+ /* start at 0th bit of BadBlockMarkerByte. This field is used to get to */
+ /* the start bit of bad block marker byte with in BadBlockMarkerByte */
+ uint32_t BBMarkerPhysicalOffset;/* FCB value that gives byte offset for bad block marker on physical NAND page */
+ uint32_t BCHType;
+
+ uint32_t TMTiming2_ReadLatency;
+ uint32_t TMTiming2_PreambleDelay;
+ uint32_t TMTiming2_CEDelay;
+ uint32_t TMTiming2_PostambleDelay;
+ uint32_t TMTiming2_CmdAddPause;
+ uint32_t TMTiming2_DataPause;
+ uint32_t TMSpeed;
+ uint32_t TMTiming1_BusyTimeout;
+
+ uint32_t DISBBM; /* the flag to enable (1)/disable(0) bi swap */
+ uint32_t BBMarkerPhysicalOffsetInSpareData; /* The swap position of main area in spare area */
+};
+
+#define BF_VAL(v, bf) (((v) & bf##_MASK) >> bf##_OFFSET)
+#define GETBIT(v,n) (((v) >> (n)) & 0x1)
+
+static uint8_t calculate_parity_13_8(uint8_t d)
+{
+ uint8_t 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;
+ uint8_t *src = _src;
+ uint8_t *ecc = _ecc;
+
+ for (i = 0; i < size; i++)
+ ecc[i] = calculate_parity_13_8(src[i]);
+}
+
+static uint32_t 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;
+}
+
+static __maybe_unused void dump_fcb(void *buf)
+{
+ struct fcb_block *fcb = buf;
+
+ pr_debug("Checksum: 0x%08x\n", fcb->Checksum);
+ pr_debug("FingerPrint: 0x%08x\n", fcb->FingerPrint);
+ pr_debug("Version: 0x%08x\n", fcb->Version);
+ pr_debug("DataSetup: 0x%02x\n", fcb->DataSetup);
+ pr_debug("DataHold: 0x%02x\n", fcb->DataHold);
+ pr_debug("AddressSetup: 0x%02x\n", fcb->AddressSetup);
+ pr_debug("DSAMPLE_TIME: 0x%02x\n", fcb->DSAMPLE_TIME);
+ pr_debug("NandTimingState: 0x%02x\n", fcb->NandTimingState);
+ pr_debug("REA: 0x%02x\n", fcb->REA);
+ pr_debug("RLOH: 0x%02x\n", fcb->RLOH);
+ pr_debug("RHOH: 0x%02x\n", fcb->RHOH);
+ pr_debug("PageDataSize: 0x%08x\n", fcb->PageDataSize);
+ pr_debug("TotalPageSize: 0x%08x\n", fcb->TotalPageSize);
+ pr_debug("SectorsPerBlock: 0x%08x\n", fcb->SectorsPerBlock);
+ pr_debug("NumberOfNANDs: 0x%08x\n", fcb->NumberOfNANDs);
+ pr_debug("TotalInternalDie: 0x%08x\n", fcb->TotalInternalDie);
+ pr_debug("CellType: 0x%08x\n", fcb->CellType);
+ pr_debug("EccBlockNEccType: 0x%08x\n", fcb->EccBlockNEccType);
+ pr_debug("EccBlock0Size: 0x%08x\n", fcb->EccBlock0Size);
+ pr_debug("EccBlockNSize: 0x%08x\n", fcb->EccBlockNSize);
+ pr_debug("EccBlock0EccType: 0x%08x\n", fcb->EccBlock0EccType);
+ pr_debug("MetadataBytes: 0x%08x\n", fcb->MetadataBytes);
+ pr_debug("NumEccBlocksPerPage: 0x%08x\n", fcb->NumEccBlocksPerPage);
+ pr_debug("EccBlockNEccLevelSDK: 0x%08x\n", fcb->EccBlockNEccLevelSDK);
+ pr_debug("EccBlock0SizeSDK: 0x%08x\n", fcb->EccBlock0SizeSDK);
+ pr_debug("EccBlockNSizeSDK: 0x%08x\n", fcb->EccBlockNSizeSDK);
+ pr_debug("EccBlock0EccLevelSDK: 0x%08x\n", fcb->EccBlock0EccLevelSDK);
+ pr_debug("NumEccBlocksPerPageSDK: 0x%08x\n", fcb->NumEccBlocksPerPageSDK);
+ pr_debug("MetadataBytesSDK: 0x%08x\n", fcb->MetadataBytesSDK);
+ pr_debug("EraseThreshold: 0x%08x\n", fcb->EraseThreshold);
+ pr_debug("BootPatch: 0x%08x\n", fcb->BootPatch);
+ pr_debug("PatchSectors: 0x%08x\n", fcb->PatchSectors);
+ pr_debug("Firmware1_startingPage: 0x%08x\n", fcb->Firmware1_startingPage);
+ pr_debug("Firmware2_startingPage: 0x%08x\n", fcb->Firmware2_startingPage);
+ pr_debug("PagesInFirmware1: 0x%08x\n", fcb->PagesInFirmware1);
+ pr_debug("PagesInFirmware2: 0x%08x\n", fcb->PagesInFirmware2);
+ pr_debug("DBBTSearchAreaStartAddress: 0x%08x\n", fcb->DBBTSearchAreaStartAddress);
+ pr_debug("BadBlockMarkerByte: 0x%08x\n", fcb->BadBlockMarkerByte);
+ pr_debug("BadBlockMarkerStartBit: 0x%08x\n", fcb->BadBlockMarkerStartBit);
+ pr_debug("BBMarkerPhysicalOffset: 0x%08x\n", fcb->BBMarkerPhysicalOffset);
+ pr_debug("BCHType: 0x%08x\n", fcb->BCHType);
+ pr_debug("TMTiming2_ReadLatency: 0x%08x\n", fcb->TMTiming2_ReadLatency);
+ pr_debug("TMTiming2_PreambleDelay: 0x%08x\n", fcb->TMTiming2_PreambleDelay);
+ pr_debug("TMTiming2_CEDelay: 0x%08x\n", fcb->TMTiming2_CEDelay);
+ pr_debug("TMTiming2_PostambleDelay: 0x%08x\n", fcb->TMTiming2_PostambleDelay);
+ pr_debug("TMTiming2_CmdAddPause: 0x%08x\n", fcb->TMTiming2_CmdAddPause);
+ pr_debug("TMTiming2_DataPause: 0x%08x\n", fcb->TMTiming2_DataPause);
+ pr_debug("TMSpeed: 0x%08x\n", fcb->TMSpeed);
+ pr_debug("TMTiming1_BusyTimeout: 0x%08x\n", fcb->TMTiming1_BusyTimeout);
+ pr_debug("DISBBM: 0x%08x\n", fcb->DISBBM);
+ pr_debug("BBMarkerPhysOfsInSpareData: 0x%08x\n", fcb->BBMarkerPhysicalOffsetInSpareData);
+}
+
+static __maybe_unused ssize_t raw_read_page(struct mtd_info *mtd, void *dst, loff_t offset)
+{
+ struct mtd_oob_ops ops;
+ ssize_t ret;
+
+ ops.mode = MTD_OPS_RAW;
+ ops.ooboffs = 0;
+ ops.datbuf = dst;
+ ops.len = mtd->writesize;
+ ops.oobbuf = dst + mtd->writesize;
+ ops.ooblen = mtd->oobsize;
+ ret = mtd_read_oob(mtd, offset, &ops);
+
+ return ret;
+}
+
+static ssize_t raw_write_page(struct mtd_info *mtd, void *buf, loff_t offset)
+{
+ struct mtd_oob_ops ops;
+ ssize_t ret;
+
+ ops.mode = MTD_OPS_RAW;
+ ops.ooboffs = 0;
+ ops.datbuf = buf;
+ ops.len = mtd->writesize;
+ ops.oobbuf = buf + mtd->writesize;
+ ops.ooblen = mtd->oobsize;
+ ret = mtd_write_oob(mtd, offset, &ops);
+
+ return ret;
+}
+
+static int fcb_create(struct fcb_block *fcb, struct mtd_info *mtd)
+{
+ fcb->FingerPrint = 0x20424346;
+ fcb->Version = 0x01000000;
+ fcb->PageDataSize = mtd->writesize;
+ fcb->TotalPageSize = mtd->writesize + mtd->oobsize;
+ fcb->SectorsPerBlock = mtd->erasesize / mtd->writesize;
+
+ if (mtd->writesize == 2048) {
+ fcb->EccBlock0EccType = 4;
+ } else if (mtd->writesize == 4096) {
+ if (mtd->oobsize == 218) {
+ fcb->EccBlock0EccType = 8;
+ } else if (mtd->oobsize == 128) {
+ fcb->EccBlock0EccType = 4;
+ } else {
+ pr_err("Illegal oobsize %d\n", mtd->oobsize);
+ return -EINVAL;
+ }
+ } else {
+ pr_err("Illegal writesize %d\n", mtd->writesize);
+ return -EINVAL;
+ }
+
+ fcb->EccBlockNEccType = fcb->EccBlock0EccType;
+
+ /* Also hardcoded in kobs-ng */
+ fcb->DataSetup = 80;
+ fcb->DataHold = 60;
+ fcb->AddressSetup = 25;
+ fcb->DSAMPLE_TIME = 6;
+ fcb->MetadataBytes = 0x0000000a;
+ fcb->EccBlock0Size = 0x00000200;
+ fcb->EccBlockNSize = 0x00000200;
+
+ fcb->NumEccBlocksPerPage = mtd->writesize / fcb->EccBlock0Size - 1;
+
+ /* DBBT search area starts at third block */
+ fcb->DBBTSearchAreaStartAddress = mtd->erasesize / mtd->writesize * 2;
+
+ if (mtd->writesize == 2048) {
+ fcb->BadBlockMarkerByte = 0x000007cf;
+ } else {
+ pr_err("BadBlockMarkerByte unknown for writesize %d\n", mtd->writesize);
+ return -EINVAL;
+ }
+
+ fcb->BBMarkerPhysicalOffset = mtd->writesize;
+
+ fcb->Checksum = calc_chksum((void *)fcb + 4, sizeof(*fcb) - 4);
+
+ return 0;
+}
+
+static int imx6_bbu_erase(struct mtd_info *mtd)
+{
+ uint64_t offset = 0;
+ int len = SZ_2M;
+ struct erase_info erase;
+ int ret;
+
+ while (len > 0) {
+ pr_debug("erasing at 0x%08llx\n", offset);
+ if (mtd_block_isbad(mtd, offset)) {
+ offset += mtd->erasesize;
+ pr_debug("erase skip block @ 0x%08llx\n", offset);
+ continue;
+ }
+
+ memset(&erase, 0, sizeof(erase));
+ erase.addr = offset;
+ erase.len = mtd->erasesize;
+
+ ret = mtd_erase(mtd, &erase);
+ if (ret)
+ return ret;
+
+ offset += mtd->erasesize;
+ len -= mtd->erasesize;
+ }
+
+ return 0;
+}
+
+static int imx6_bbu_write_firmware(struct mtd_info *mtd, int block, void *buf, size_t len)
+{
+ uint64_t offset = block * mtd->erasesize;
+ int ret;
+ size_t written;
+
+ while (len > 0) {
+ int now = min(len, mtd->erasesize);
+
+ pr_debug("writing %p at 0x%08llx, left 0x%08x\n",
+ buf, offset, len);
+
+ if (mtd_block_isbad(mtd, offset)) {
+ offset += mtd->erasesize;
+ block++;
+ pr_debug("write skip block @ 0x%08llx\n", offset);
+ continue;
+ }
+
+ ret = mtd_write(mtd, offset, now, &written, buf);
+ if (ret)
+ return ret;
+
+ offset += now;
+ len -= now;
+ buf += now;
+ block++;
+ }
+
+ return block;
+}
+
+static int dbbt_data_create(struct mtd_info *mtd, void *buf, int block_last)
+{
+ int n;
+ int n_bad_blocks = 0;
+ uint32_t *bb = buf + 0x8;
+ uint32_t *n_bad_blocksp = buf + 0x4;
+
+ for (n = 0; n <= block_last; n++) {
+ loff_t offset = n * mtd->erasesize;
+ if (mtd_block_isbad(mtd, offset)) {
+ n_bad_blocks++;
+ *bb = n;
+ bb++;
+ }
+ }
+
+ *n_bad_blocksp = n_bad_blocks;
+
+ return n_bad_blocks;
+}
+
+static int imx6_bbu_nand_update(struct bbu_handler *handler, struct bbu_data *data)
+{
+ struct cdev *bcb_cdev;
+ struct mtd_info *mtd;
+ int ret, block_fw1, block_fw2, block_last;
+ struct fcb_block *fcb;
+ struct dbbt_block *dbbt;
+ void *fcb_raw_page, *dbbt_page, *dbbt_data_page;
+ void *ecc;
+ int written;
+ void *fw;
+ unsigned fw_size;
+ int i;
+
+ bcb_cdev = cdev_by_name("nand0");
+ if (!bcb_cdev) {
+ pr_err("%s: No FCB device!\n", __func__);
+ return -ENODEV;
+ }
+
+ mtd = bcb_cdev->mtd;
+
+ fcb_raw_page = xzalloc(mtd->writesize + mtd->oobsize);
+
+ fcb = fcb_raw_page + 12;
+ ecc = fcb_raw_page + 512 + 12;
+
+ dbbt_page = xzalloc(mtd->writesize);
+ dbbt_data_page = xzalloc(mtd->writesize);
+ dbbt = dbbt_page;
+
+ /*
+ * We have to write one additional page to make the ROM happy.
+ * Maybe the PagesInFirmwarex fields are really the number of pages - 1.
+ * kobs-ng has the same.
+ */
+ fw_size = ALIGN(data->len + mtd->writesize, mtd->writesize);
+ fw = xzalloc(fw_size);
+ memcpy(fw, data->image, data->len);
+
+ block_fw1 = 4;
+
+ ret = imx6_bbu_erase(mtd);
+ if (ret)
+ goto out;
+
+ ret = imx6_bbu_write_firmware(mtd, block_fw1, fw, fw_size);
+ if (ret < 0)
+ goto out;
+
+ block_fw2 = ret;
+
+ ret = imx6_bbu_write_firmware(mtd, block_fw2, fw, fw_size);
+ if (ret < 0)
+ goto out;
+
+ block_last = ret;
+
+ fcb->Firmware1_startingPage = block_fw1 * mtd->erasesize / mtd->writesize;
+ fcb->Firmware2_startingPage = block_fw2 * mtd->erasesize / mtd->writesize;
+ fcb->PagesInFirmware1 = ALIGN(data->len, mtd->writesize) / mtd->writesize;
+ fcb->PagesInFirmware2 = fcb->PagesInFirmware1;
+
+ fcb_create(fcb, mtd);
+ encode_hamming_13_8(fcb, ecc, 512);
+ ret = raw_write_page(mtd, fcb_raw_page, 0);
+ if (ret)
+ goto out;
+
+ ret = raw_write_page(mtd, fcb_raw_page, mtd->erasesize);
+ if (ret)
+ goto out;
+
+ dbbt->Checksum = 0;
+ dbbt->FingerPrint = 0x54424244;
+ dbbt->Version = 0x01000000;
+
+ ret = dbbt_data_create(mtd, dbbt_data_page, block_last);
+ if (ret < 0)
+ goto out;
+
+ if (ret > 0)
+ dbbt->DBBTNumOfPages = 1;
+
+ for (i = 2; i < 4; i++) {
+ ret = mtd_write(mtd, mtd->erasesize * i, 2048, &written, dbbt_page);
+ if (ret)
+ goto out;
+
+ if (dbbt->DBBTNumOfPages > 0) {
+ ret = mtd_write(mtd, mtd->erasesize * i + mtd->writesize * 4,
+ 2048, &written, dbbt_data_page);
+ if (ret)
+ goto out;
+ }
+ }
+
+out:
+ free(dbbt_page);
+ free(dbbt_data_page);
+ free(fcb_raw_page);
+ free(fw);
+
+ return ret;
+}
+
+int imx6_bbu_nand_register_handler(const char *name, unsigned long flags)
+{
+ struct bbu_handler *handler;
+ int ret;
+
+ handler = xzalloc(sizeof(*handler));
+ handler->devicefile = "/dev/nand0";
+ handler->name = name;
+ handler->flags = flags;
+ handler->handler = imx6_bbu_nand_update;
+
+ ret = bbu_register_handler(handler);
+ if (ret)
+ free(handler);
+
+ return ret;
+}
diff --git a/arch/arm/mach-imx/include/mach/bbu.h b/arch/arm/mach-imx/include/mach/bbu.h
index 3cd3b1e..1644d85 100644
--- a/arch/arm/mach-imx/include/mach/bbu.h
+++ b/arch/arm/mach-imx/include/mach/bbu.h
@@ -33,6 +33,8 @@ 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_nand_register_handler(const char *name, unsigned long flags);
+
#else
static inline int imx51_bbu_internal_mmc_register_handler(const char *name, char *devicefile,
@@ -77,6 +79,10 @@ static inline int imx6_bbu_internal_spi_i2c_register_handler(const char *name, c
return -ENOSYS;
}
+static inline int imx6_bbu_nand_register_handler(const char *name, unsigned long flags)
+{
+ return -ENOSYS;
+}
#endif
#if defined(CONFIG_BAREBOX_UPDATE_IMX_EXTERNAL_NAND)
--
1.9.0
--
Pengutronix e.K. | |
Industrial Linux Solutions | http://www.pengutronix.de/ |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH] i.MX6: bbu: Barebox update support for NAND.
2014-03-13 6:54 ` Sascha Hauer
2014-03-13 7:50 ` Dmitry Lavnikevich
2014-03-20 10:28 ` Dmitry Lavnikevich
@ 2014-03-31 8:42 ` Sascha Hauer
2014-04-01 7:36 ` Christian Hemp
2 siblings, 1 reply; 7+ messages in thread
From: Sascha Hauer @ 2014-03-31 8:42 UTC (permalink / raw)
To: Dmitry Lavnikevich; +Cc: Grigory Milev, barebox
On Thu, Mar 13, 2014 at 07:54:58AM +0100, Sascha Hauer wrote:
> Hi Dmitry,
>
> On Wed, Mar 12, 2014 at 10:54:11AM +0300, Dmitry Lavnikevich wrote:
> > 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.
>
> I have the very same patch in the queue, it seems we have duplicated
> some work. As this version has some shortcomings I'll post my version as
> an alternative implementation. Also I'll try your version on a board I
> have here on which my version doesn't work (which is the reason I
> haven't posted my patch yet)
The NAND boot on our board finally works. It turned out to be a silly
bug in the DCD table. The DCD table turned off the GPMI clocks which
of course prevented the ROM from loading the rest of the image.
I just resent my version of this patch, but included the missing check
for a valid image and the confirmation question from your version.
Maybe you could give this a test on your board.
Sascha
--
Pengutronix e.K. | |
Industrial Linux Solutions | http://www.pengutronix.de/ |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH] i.MX6: bbu: Barebox update support for NAND.
2014-03-31 8:42 ` Sascha Hauer
@ 2014-04-01 7:36 ` Christian Hemp
0 siblings, 0 replies; 7+ messages in thread
From: Christian Hemp @ 2014-04-01 7:36 UTC (permalink / raw)
To: Sascha Hauer; +Cc: Grigory Milev, barebox, Dmitry Lavnikevich
Hello Sascha,
I have tested the patch on three phyCARD-i.MX6 and three phyFLEX-i.MX6
modules without problems.
Christian
Am Montag, den 31.03.2014, 10:42 +0200 schrieb Sascha Hauer:
> On Thu, Mar 13, 2014 at 07:54:58AM +0100, Sascha Hauer wrote:
> > Hi Dmitry,
> >
> > On Wed, Mar 12, 2014 at 10:54:11AM +0300, Dmitry Lavnikevich wrote:
> > > 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.
> >
> > I have the very same patch in the queue, it seems we have duplicated
> > some work. As this version has some shortcomings I'll post my version as
> > an alternative implementation. Also I'll try your version on a board I
> > have here on which my version doesn't work (which is the reason I
> > haven't posted my patch yet)
>
> The NAND boot on our board finally works. It turned out to be a silly
> bug in the DCD table. The DCD table turned off the GPMI clocks which
> of course prevented the ROM from loading the rest of the image.
>
> I just resent my version of this patch, but included the missing check
> for a valid image and the confirmation question from your version.
>
> Maybe you could give this a test on your board.
>
> Sascha
>
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2014-04-01 7:36 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-03-12 7:54 [PATCH] i.MX6: bbu: Barebox update support for NAND Dmitry Lavnikevich
2014-03-13 6:54 ` Sascha Hauer
2014-03-13 7:50 ` Dmitry Lavnikevich
2014-03-20 10:28 ` Dmitry Lavnikevich
2014-03-22 7:06 ` Sascha Hauer
2014-03-31 8:42 ` Sascha Hauer
2014-04-01 7:36 ` Christian Hemp
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox