diff --git a/arch/arm/include/asm/mach-imx/imx-nandbcb.h b/arch/arm/include/asm/mach-imx/imx-nandbcb.h
new file mode 100644
index 0000000000..033659a038
--- /dev/null
+++ b/arch/arm/include/asm/mach-imx/imx-nandbcb.h
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2017 Jagan Teki <jagan@amarulasolutions.com>
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#ifndef _IMX_NAND_BCB_H_
+#define _IMX_NAND_BCB_H_
+
+#define FCB_FINGERPRINT		0x20424346      /* 'FCB' */
+#define FCB_VERSION_1		0x01000000
+
+#define DBBT_FINGERPRINT2	0x54424244	/* 'DBBT' */
+#define DBBT_VERSION_1		0x01000000
+
+struct dbbt_block {
+	u32 checksum;	/* reserved on i.MX6 */
+	u32 fingerprint;
+	u32 version;
+	u32 numberbb;	/* reserved on i.MX6 */
+	u32 dbbtpages;
+};
+
+struct fcb_block {
+	u32 checksum;		/* First fingerprint in first byte */
+	u32 fingerprint;	/* 2nd fingerprint at byte 4 */
+	u32 version;		/* 3rd fingerprint at byte 8 */
+	u8 datasetup;
+	u8 datahold;
+	u8 addr_setup;
+	u8 dsample_time;
+
+	/* These are for application use only and not for ROM. */
+	u8 nandtiming;
+	u8 rea;
+	u8 rloh;
+	u8 rhoh;
+	u32 pagesize;		/* 2048 for 2K pages, 4096 for 4K pages */
+	u32 oob_pagesize;	/* 2112 for 2K pages, 4314 for 4K pages */
+	u32 sectors;		/* Number of 2K sections per block */
+	u32 nr_nand;		/* Total Number of NANDs - not used by ROM */
+	u32 nr_die;		/* Number of separate chips in this NAND */
+	u32 celltype;		/* MLC or SLC */
+	u32 ecc_type;		/* Type of ECC, can be one of BCH-0-20 */
+	u32 ecc_nr;		/* Number of bytes for Block0 - BCH */
+
+	/* Block size in bytes for all blocks other than Block0 - BCH */
+	u32 ecc_size;
+	u32 ecc_level;		/* Ecc level for Block 0 - BCH */
+	u32 meta_size;		/* Metadata size - BCH */
+	/* Number of blocks per page for ROM use - BCH */
+	u32 nr_blocks;
+	u32 ecc_type_sdk;	/* Type of ECC, can be one of BCH-0-20 */
+	u32 ecc_nr_sdk;		/* Number of bytes for Block0 - BCH */
+	/* Block size in bytes for all blocks other than Block0 - BCH */
+	u32 ecc_size_sdk;
+	u32 ecc_level_sdk;	/* Ecc level for Block 0 - BCH */
+	/* Number of blocks per page for SDK use - BCH */
+	u32 nr_blocks_sdk;
+	u32 meta_size_sdk;	/* Metadata size - BCH */
+	u32 erase_th;		/* To set into BCH_MODE register */
+
+	/*
+	 * 0: normal boot
+	 * 1: to load patch starting next to FCB
+	 */
+	u32 bootpatch;
+	u32 patch_size;	/* Size of patch in sectors */
+	u32 fw1_start;	/* Firmware image starts on this sector */
+	u32 fw2_start;	/* Secondary FW Image starting Sector */
+	u32 fw1_pages;	/* Number of sectors in firmware image */
+	u32 fw2_pages;	/* Number of sector in secondary FW image */
+	u32 dbbt_start; /* Page address where dbbt search area begins */
+
+	/*
+	 * Byte in page data that have manufacturer marked bad block marker,
+	 * this will be swapped with metadata[0] to complete page data.
+	 */
+	u32 bb_byte;
+
+	/*
+	 * For BCH ECC sizes other than 8 and 16 the bad block marker does not
+	 * start at 0th bit of bb_byte. This field is used to get to
+	 * the start bit of bad block marker byte with in bb_byte
+	 */
+	u32 bb_start_bit;
+
+	/*
+	 * FCB value that gives byte offset for
+	 * bad block marker on physical NAND page
+	 */
+	u32 phy_offset;
+	u32 bchtype;
+
+	u32 readlatency;
+	u32 predelay;
+	u32 cedelay;
+	u32 postdelay;
+	u32 cmdaddpause;
+	u32 datapause;
+	u32 tmspeed;
+	u32 busytimeout;
+
+	/* the flag to enable (1)/disable(0) bi swap */
+	u32 disbbm;
+
+	/* The swap position of main area in spare area */
+	u32 spare_offset;
+};
+
+#endif	/* _IMX_NAND_BCB_H_ */
diff --git a/drivers/mtd/nand/raw/mxs_nand.h b/arch/arm/include/asm/mach-imx/mxs-nand.h
similarity index 100%
rename from drivers/mtd/nand/raw/mxs_nand.h
rename to arch/arm/include/asm/mach-imx/mxs-nand.h
diff --git a/arch/arm/mach-imx/Makefile b/arch/arm/mach-imx/Makefile
index 37675d0558..e740d0c1e8 100644
--- a/arch/arm/mach-imx/Makefile
+++ b/arch/arm/mach-imx/Makefile
@@ -59,6 +59,7 @@ ifneq ($(CONFIG_SPL_BUILD),y)
 obj-$(CONFIG_CMD_BMODE) += cmd_bmode.o
 obj-$(CONFIG_CMD_HDMIDETECT) += cmd_hdmidet.o
 obj-$(CONFIG_CMD_DEKBLOB) += cmd_dek.o
+obj-$(CONFIG_CMD_NANDBCB) += cmd_nandbcb.o
 endif
 
 ifneq ($(CONFIG_BOARD_SIZE_LIMIT),)
diff --git a/arch/arm/mach-imx/cmd_nandbcb.c b/arch/arm/mach-imx/cmd_nandbcb.c
new file mode 100644
index 0000000000..fd3df78fb4
--- /dev/null
+++ b/arch/arm/mach-imx/cmd_nandbcb.c
@@ -0,0 +1,369 @@
+/*
+ * i.MX6 nand boot control block(bcb).
+ *
+ * Based on the common/imx-bbu-nand-fcb.c from barebox and imx kobs-ng
+ *
+ * Copyright (C) 2017 Jagan Teki <jagan@amarulasolutions.com>
+ * Copyright (C) 2016 Sergey Kubushyn <ksi@koi8.net>
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <common.h>
+#include <nand.h>
+
+#include <asm/io.h>
+#include <jffs2/jffs2.h>
+#include <linux/mtd/mtd.h>
+
+#include <asm/mach-imx/imx-nandbcb.h>
+#include <asm/mach-imx/imximage.cfg>
+#include <asm/mach-imx/mxs-nand.h>
+#include <linux/mtd/mtd.h>
+#include <nand.h>
+
+#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;
+}
+
+static void fill_fcb(struct fcb_block *fcb, struct mtd_info *mtd)
+{
+	struct nand_chip *chip = mtd_to_nand(mtd);
+	struct mxs_nand_info *nand_info = nand_get_controller_data(chip);
+
+	fcb->fingerprint = FCB_FINGERPRINT;
+	fcb->version = FCB_VERSION_1;
+	fcb->pagesize = mtd->writesize;
+	fcb->oob_pagesize = mtd->writesize + mtd->oobsize;
+	fcb->sectors = mtd->erasesize / mtd->writesize;
+
+	/* Divide ECC strength by two and save the value into FCB structure. */
+	fcb->ecc_level = nand_info->bch_geometry.ecc_strength >> 1;
+
+	fcb->ecc_type = fcb->ecc_level;
+
+	/* Also hardcoded in kobs-ng */
+	fcb->ecc_nr = 0x00000200;
+	fcb->ecc_size = 0x00000200;
+	fcb->datasetup = 80;
+	fcb->datahold = 60;
+	fcb->addr_setup = 25;
+	fcb->dsample_time = 6;
+	fcb->meta_size = 10;
+
+	/* DBBT search area starts at second page on first block */
+	fcb->dbbt_start = 1;
+
+	fcb->bb_byte = nand_info->bch_geometry.block_mark_byte_offset;
+	fcb->bb_start_bit = nand_info->bch_geometry.block_mark_bit_offset;
+
+	fcb->phy_offset = mtd->writesize;
+
+	fcb->nr_blocks = mtd->writesize / fcb->ecc_nr - 1;
+
+	fcb->checksum = calc_chksum((void *)fcb + 4, sizeof(*fcb) - 4);
+}
+
+static int dbbt_fill_data(struct mtd_info *mtd, void *buf, int num_blocks)
+{
+	int n, n_bad_blocks = 0;
+	u32 *bb = buf + 0x8;
+	u32 *n_bad_blocksp = buf + 0x4;
+
+	for (n = 0; n < num_blocks; 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 nandbcb_update(struct mtd_info *mtd, loff_t off, size_t size,
+			  size_t maxsize, const u_char *buf)
+{
+	nand_erase_options_t opts;
+	struct fcb_block *fcb;
+	struct dbbt_block *dbbt;
+	loff_t fw1_off;
+	void *fwbuf, *fcb_raw_page, *dbbt_page, *dbbt_data_page;
+	int nr_blks, nr_blks_fcb, fw1_blk;
+	size_t fwsize, dummy;
+	int i, ret;
+
+	/* erase */
+	memset(&opts, 0, sizeof(opts));
+	opts.offset = off;
+	opts.length = maxsize - 1;
+	ret = nand_erase_opts(mtd, &opts);
+	if (ret) {
+		printf("%s: erase failed (ret = %d)\n", __func__, ret);
+		return ret;
+	}
+
+	/*
+	 * Reference documentation from i.MX6DQRM section 8.5.2.2
+	 *
+	 * Nand Boot Control Block(BCB) contains two data structures,
+	 * - Firmware Configuration Block(FCB)
+	 * - Discovered Bad Block Table(DBBT)
+	 *
+	 * FCB contains,
+	 * - nand timings
+	 * - DBBT search page address,
+	 * - start page address of primary firmware
+	 * - start page address of secondary firmware
+	 *
+	 * setup fcb:
+	 * - number of blocks = mtd partition size / mtd erasesize
+	 * - two firmware blocks, primary and secondary
+	 * - first 4 block for FCB/DBBT
+	 * - rest split in half for primary and secondary firmware
+	 * - same firmware will write two times
+	 */
+	nr_blks_fcb = 2;
+	nr_blks = maxsize / mtd->erasesize;
+	fw1_blk = nr_blks_fcb;
+
+	/* write fw */
+	fwsize = ALIGN(size + FLASH_OFFSET_STANDARD + mtd->writesize,
+		       mtd->writesize);
+	fwbuf = kzalloc(fwsize, GFP_KERNEL);
+	if (!fwbuf) {
+		debug("failed to allocate fwbuf\n");
+		ret = -ENOMEM;
+		goto err;
+	}
+
+	memcpy(fwbuf + FLASH_OFFSET_STANDARD, buf, size);
+	fw1_off = fw1_blk * mtd->erasesize;
+	ret = nand_write_skip_bad(mtd, fw1_off, &fwsize, NULL, maxsize,
+				  (u_char *)fwbuf, WITH_WR_VERIFY);
+	printf("NAND fw write: 0x%llx offset, 0x%x bytes written: %s\n",
+	       fw1_off, fwsize, ret ? "ERROR" : "OK");
+	if (ret)
+		goto fwbuf_err;
+
+	/* fill fcb */
+	fcb = kzalloc(sizeof(*fcb), GFP_KERNEL);
+	if (!fcb) {
+		debug("failed to allocate fcb\n");
+		ret = -ENOMEM;
+		goto fwbuf_err;
+	}
+
+	fcb->fw1_start = (fw1_blk * mtd->erasesize) / mtd->writesize;
+	fcb->fw1_pages = size / mtd->writesize + 1;
+	fill_fcb(fcb, mtd);
+
+	/* fill dbbt */
+	dbbt_page = kzalloc(mtd->writesize, GFP_KERNEL);
+	if (!dbbt_page) {
+		debug("failed to allocate dbbt_page\n");
+		ret = -ENOMEM;
+		goto fcb_err;
+	}
+
+	dbbt_data_page = kzalloc(mtd->writesize, GFP_KERNEL);
+	if (!dbbt_data_page) {
+		debug("failed to allocate dbbt_data_page\n");
+		ret = -ENOMEM;
+		goto dbbt_page_err;
+	}
+
+	dbbt = dbbt_page;
+	dbbt->checksum = 0;
+	dbbt->fingerprint = DBBT_FINGERPRINT2;
+	dbbt->version = DBBT_VERSION_1;
+	ret = dbbt_fill_data(mtd, dbbt_data_page, nr_blks);
+	if (ret < 0)
+		goto dbbt_data_page_err;
+	else if (ret > 0)
+		dbbt->dbbtpages = 1;
+
+	/* write fcb/dbbt */
+	fcb_raw_page = kzalloc(mtd->writesize + mtd->oobsize, GFP_KERNEL);
+	if (!fcb_raw_page) {
+		debug("failed to allocate fcb_raw_page\n");
+		ret = -ENOMEM;
+		goto dbbt_data_page_err;
+	}
+
+	memcpy(fcb_raw_page + 12, fcb, sizeof(struct fcb_block));
+	encode_hamming_13_8(fcb_raw_page + 12, fcb_raw_page + 12 + 512, 512);
+	/*
+	 * Set the first and second byte of OOB data to 0xFF, not 0x00. These
+	 * bytes are used as the Manufacturers Bad Block Marker (MBBM). Since
+	 * the FCB is mostly written to the first page in a block, a scan for
+	 * factory bad blocks will detect these blocks as bad, e.g. when
+	 * function nand_scan_bbt() is executed to build a new bad block table.
+	 */
+	memset(fcb_raw_page + mtd->writesize, 0xFF, 2);
+
+	for (i = 0; i < nr_blks_fcb; i++) {
+		if (mtd_block_isbad(mtd, off)) {
+			printf("Block %d is bad, skipped\n", i);
+			continue;
+		}
+
+		/* raw write */
+		mtd_oob_ops_t ops = {
+			.datbuf = (u8 *)fcb_raw_page,
+			.oobbuf = ((u8 *)fcb_raw_page) + mtd->writesize,
+			.len = mtd->writesize,
+			.ooblen = mtd->oobsize,
+			.mode = MTD_OPS_RAW
+		};
+
+		ret = mtd_write_oob(mtd, mtd->erasesize * i, &ops);
+		if (ret)
+			goto fcb_raw_page_err;
+		debug("NAND fcb write: 0x%x offset, 0x%x bytes written: %s\n",
+		      mtd->erasesize * i, ops.len, ret ? "ERROR" : "OK");
+
+		ret = mtd_write(mtd, mtd->erasesize * i + mtd->writesize,
+				mtd->writesize, &dummy, dbbt_page);
+		if (ret)
+			goto fcb_raw_page_err;
+		debug("NAND dbbt write: 0x%x offset, 0x%x bytes written: %s\n",
+		      mtd->erasesize * i + mtd->writesize, dummy,
+		      ret ? "ERROR" : "OK");
+
+		/* dbbtpages == 0 if no bad blocks */
+		if (dbbt->dbbtpages > 0) {
+			loff_t to = (mtd->erasesize * i + mtd->writesize * 5);
+
+			ret = mtd_write(mtd, to, mtd->writesize, &dummy,
+					dbbt_data_page);
+			if (ret)
+				goto fcb_raw_page_err;
+		}
+	}
+
+fcb_raw_page_err:
+	kfree(fcb_raw_page);
+dbbt_data_page_err:
+	kfree(dbbt_data_page);
+dbbt_page_err:
+	kfree(dbbt_page);
+fcb_err:
+	kfree(fcb);
+fwbuf_err:
+	kfree(fwbuf);
+err:
+	return ret;
+}
+
+static int do_nandbcb_update(int argc, char * const argv[])
+{
+	struct mtd_info *mtd;
+	loff_t addr, offset, size, maxsize;
+	char *endp;
+	u_char *buf;
+	int dev;
+	int ret;
+
+	if (argc != 4)
+		return CMD_RET_USAGE;
+
+	dev = nand_curr_device;
+	if (dev < 0) {
+		printf("failed to get nand_curr_device, run nand device");
+		return CMD_RET_FAILURE;
+	}
+
+	addr = simple_strtoul(argv[1], &endp, 16);
+	if (*argv[1] == 0 || *endp != 0)
+		return CMD_RET_FAILURE;
+
+	mtd = get_nand_dev_by_index(dev);
+	if (mtd_arg_off_size(argc - 2, argv + 2, &dev, &offset, &size,
+			     &maxsize, MTD_DEV_TYPE_NAND, mtd->size))
+		return CMD_RET_FAILURE;
+
+	buf = map_physmem(addr, size, MAP_WRBACK);
+	if (!buf) {
+		puts("failed to map physical memory\n");
+		return CMD_RET_FAILURE;
+	}
+
+	ret = nandbcb_update(mtd, offset, size, maxsize, buf);
+
+	return ret == 0 ? CMD_RET_SUCCESS : CMD_RET_FAILURE;
+}
+
+static int do_nandbcb(cmd_tbl_t *cmdtp, int flag, int argc,
+		      char * const argv[])
+{
+	const char *cmd;
+	int ret = 0;
+
+	if (argc < 5)
+		goto usage;
+
+	cmd = argv[1];
+	--argc;
+	++argv;
+
+	if (strcmp(cmd, "update") == 0) {
+		ret = do_nandbcb_update(argc, argv);
+		goto done;
+	}
+
+done:
+	if (ret != -1)
+		return ret;
+usage:
+	return CMD_RET_USAGE;
+}
+
+static char nandbcb_help_text[] =
+	"update addr off|partition len	- update 'len' bytes starting at\n"
+	"	'off|part' to memory address 'addr', skipping  bad blocks";
+
+U_BOOT_CMD(nandbcb, 5, 1, do_nandbcb,
+	   "i.MX6 Nand BCB",
+	   nandbcb_help_text
+);
diff --git a/doc/imx/common/imx6.txt b/doc/imx/common/imx6.txt
index eab88353f6..5a354005f8 100644
--- a/doc/imx/common/imx6.txt
+++ b/doc/imx/common/imx6.txt
@@ -88,3 +88,78 @@ Reading bank 4:
 
 Word 0x00000002: 9f027772 00000004
 
+
+NAND Boot on i.MX6 with SPL support
+--------------------------------------
+
+Writing/updating boot image in nand device is not straight forward in
+i.MX6 platform and it requires boot control block(BCB) to be configured.
+
+BCB contains two data structures, Firmware Configuration Block(FCB) and
+Discovered Bad Block Table(DBBT). FCB has nand timings, DBBT search area,
+and firmware. See IMX6DQRM Section 8.5.2.2
+for more information.
+
+We can't use 'nand write' command to write SPL/firmware image directly
+like other platforms does. So we need special setup to write BCB block
+as per IMX6QDL reference manual 'nandbcb update' command do that job.
+
+for nand boot, up on reset bootrom look for FCB structure in
+first block's if FCB found the nand timings are loaded for
+further reads. once FCB read done, DTTB will be loaded and
+finally firmware will be loaded which is boot image.
+
+cmd_nandbcb will create FCB these structures
+by taking mtd partition as an example.
+- initial code will erase entire partition
+- followed by FCB setup, like first 2 blocks for FCB/DBBT write,
+  and next block for FW1/SPL
+- write firmware at FW1 block and
+- finally write fcb/dttb in first 2 block.
+
+Typical NAND BCB layout:
+=======================
+
+   no.of blocks = partition size / erasesize
+   no.of fcb/dbbt blocks = 2
+   FW1 offset = no.of fcb/dbbt
+
+block  0          1          2
+        -------------------------------
+       |FCB/DBBT 0|FCB/DBBT 1|  FW 1  |
+       --------------------------------
+
+On summary, nandbcb update will
+- erase the entire partition
+- create BCB by creating 2 FCB/BDDT block followed by
+  1 FW blocks based on partition size and erasesize.
+- fill FCB/DBBT structures
+- write FW/SPL in FW1
+- write FCB/DBBT in first 2 blocks
+
+step-1: write SPL
+
+icorem6qdl> fatload mmc 0:1 $loadaddr SPL
+reading SPL
+31744 bytes read in 15 ms (2 MiB/s)
+
+icorem6qdl> nandbcb update $loadaddr spl $filesize
+device 0 offset 0x0, size 0x7c00
+Erasing at 0x1c0000 -- 100% complete.
+NAND fw write: 0x100000 offset, 0x9000 bytes written: OK
+NAND fw write: 0x180000 offset, 0x9000 bytes written: OK
+
+step-2: write u-boot-dtb.img
+
+icorem6qdl> nand erase.part uboot
+NAND erase.part: device 0 offset 0x200000, size 0x200000
+Erasing at 0x3c0000 -- 100% complete.
+OK
+
+icorem6qdl> fatload mmc 0:1 $loadaddr u-boot-dtb.img
+reading u-boot-dtb.img
+508967 bytes read in 48 ms (10.1 MiB/s)
+
+icorem6qdl> nand write ${loadaddr} uboot ${filesize}
+NAND write: device 0 offset 0x200000, size 0x7c427
+508967 bytes written: OK
diff --git a/drivers/mtd/nand/raw/mxs_nand.c b/drivers/mtd/nand/raw/mxs_nand.c
index b93d77a395..91dcbc4d22 100644
--- a/drivers/mtd/nand/raw/mxs_nand.c
+++ b/drivers/mtd/nand/raw/mxs_nand.c
@@ -25,7 +25,7 @@
 #include <asm/mach-imx/regs-bch.h>
 #include <asm/mach-imx/regs-gpmi.h>
 #include <asm/arch/sys_proto.h>
-#include "mxs_nand.h"
+#include <asm/mach-imx/mxs-nand.h>
 
 #define	MXS_NAND_DMA_DESCRIPTOR_COUNT		4
 
diff --git a/drivers/mtd/nand/raw/mxs_nand_spl.c b/drivers/mtd/nand/raw/mxs_nand_spl.c
index ee7d9cb957..1f072721aa 100644
--- a/drivers/mtd/nand/raw/mxs_nand_spl.c
+++ b/drivers/mtd/nand/raw/mxs_nand_spl.c
@@ -6,7 +6,8 @@
 #include <common.h>
 #include <nand.h>
 #include <malloc.h>
-#include "mxs_nand.h"
+#include <asm/mach-imx/mxs-nand.h>
+
 
 static struct mtd_info *mtd;
 static struct nand_chip nand_chip;
