diff mbox series

[U-Boot,v2,2/5] nand: mxs_nand: add API for switching different BCH layouts

Message ID 20191021133859.23824-3-igor.opaniuk@gmail.com
State Changes Requested
Delegated to: Stefano Babic
Headers show
Series imx: nandbcb: support for i.MX7 and bcb only updates | expand

Commit Message

Igor Opaniuk Oct. 21, 2019, 1:38 p.m. UTC
From: Igor Opaniuk <igor.opaniuk@toradex.com>

On i.MX7 in a sake of reducing the disturbances caused by a neighboring
cells in the FCB page in the NAND chip, a randomizer is enabled when
reading the FCB page by ROM bootloader.

Add API for setting BCH to specific layout (and restoring it back) used by
ROM bootloader to be able to burn it in a proper way to NAND using
nandbcb command.

Signed-off-by: Igor Opaniuk <igor.opaniuk@toradex.com>
Signed-off-by: Anti Sullin <anti.sullin@artecdesign.ee>
---

 drivers/mtd/nand/raw/mxs_nand.c | 116 ++++++++++++++++++++++++++++++++
 include/mxs_nand.h              |  22 ++++++
 2 files changed, 138 insertions(+)

Comments

Oleksandr Suvorov Oct. 23, 2019, 11:23 a.m. UTC | #1
On Mon, Oct 21, 2019 at 4:41 PM Igor Opaniuk <igor.opaniuk@gmail.com> wrote:
>
> From: Igor Opaniuk <igor.opaniuk@toradex.com>
>
> On i.MX7 in a sake of reducing the disturbances caused by a neighboring
> cells in the FCB page in the NAND chip, a randomizer is enabled when
> reading the FCB page by ROM bootloader.
>
> Add API for setting BCH to specific layout (and restoring it back) used by
> ROM bootloader to be able to burn it in a proper way to NAND using
> nandbcb command.
>
> Signed-off-by: Igor Opaniuk <igor.opaniuk@toradex.com>
> Signed-off-by: Anti Sullin <anti.sullin@artecdesign.ee>

Reviewed-by: Oleksandr Suvorov <oleksandr.suvorov@toradex.com>

> ---
>
>  drivers/mtd/nand/raw/mxs_nand.c | 116 ++++++++++++++++++++++++++++++++
>  include/mxs_nand.h              |  22 ++++++
>  2 files changed, 138 insertions(+)
>
> diff --git a/drivers/mtd/nand/raw/mxs_nand.c b/drivers/mtd/nand/raw/mxs_nand.c
> index a41b9620d0..ad7b644886 100644
> --- a/drivers/mtd/nand/raw/mxs_nand.c
> +++ b/drivers/mtd/nand/raw/mxs_nand.c
> @@ -740,6 +740,19 @@ static int mxs_nand_ecc_write_page(struct mtd_info *mtd,
>         d->cmd.pio_words[4] = (dma_addr_t)nand_info->data_buf;
>         d->cmd.pio_words[5] = (dma_addr_t)nand_info->oob_buf;
>
> +       if (is_mx7() && nand_info->en_randomizer) {
> +               d->cmd.pio_words[2] |= GPMI_ECCCTRL_RANDOMIZER_ENABLE |
> +                                      GPMI_ECCCTRL_RANDOMIZER_TYPE2;
> +               /*
> +                * Write NAND page number needed to be randomized
> +                * to GPMI_ECCCOUNT register.
> +                *
> +                * The value is between 0-255. For additional details
> +                * check 9.6.6.4 of i.MX7D Applications Processor reference
> +                */
> +               d->cmd.pio_words[3] |= (page % 255) << 16;
> +       }
> +
>         mxs_dma_desc_append(channel, d);
>
>         /* Flush caches */
> @@ -1003,6 +1016,10 @@ int mxs_nand_setup_ecc(struct mtd_info *mtd)
>         uint32_t tmp;
>         int ret;
>
> +       nand_info->en_randomizer = 0;
> +       nand_info->oobsize = mtd->oobsize;
> +       nand_info->writesize = mtd->writesize;
> +
>         ret = mxs_nand_set_geometry(mtd, geo);
>         if (ret)
>                 return ret;
> @@ -1020,6 +1037,7 @@ int mxs_nand_setup_ecc(struct mtd_info *mtd)
>         tmp |= (geo->gf_len == 14 ? 1 : 0) <<
>                 BCH_FLASHLAYOUT0_GF13_0_GF14_1_OFFSET;
>         writel(tmp, &bch_regs->hw_bch_flash0layout0);
> +       nand_info->bch_flash0layout0 = tmp;
>
>         tmp = (mtd->writesize + mtd->oobsize)
>                 << BCH_FLASHLAYOUT1_PAGE_SIZE_OFFSET;
> @@ -1028,6 +1046,7 @@ int mxs_nand_setup_ecc(struct mtd_info *mtd)
>         tmp |= (geo->gf_len == 14 ? 1 : 0) <<
>                 BCH_FLASHLAYOUT1_GF13_0_GF14_1_OFFSET;
>         writel(tmp, &bch_regs->hw_bch_flash0layout1);
> +       nand_info->bch_flash0layout1 = tmp;
>
>         /* Set *all* chip selects to use layout 0 */
>         writel(0, &bch_regs->hw_bch_layoutselect);
> @@ -1303,3 +1322,100 @@ err:
>         free(nand_info);
>  }
>  #endif
> +
> +/*
> + * Read NAND layout for FCB block generation.
> + */
> +void mxs_nand_get_layout(struct mtd_info *mtd, struct mxs_nand_layout *l)
> +{
> +       struct mxs_bch_regs *bch_regs = (struct mxs_bch_regs *)MXS_BCH_BASE;
> +       u32 tmp;
> +
> +       tmp = readl(&bch_regs->hw_bch_flash0layout0);
> +       l->nblocks = (tmp & BCH_FLASHLAYOUT0_NBLOCKS_MASK) >>
> +                       BCH_FLASHLAYOUT0_NBLOCKS_OFFSET;
> +       l->meta_size = (tmp & BCH_FLASHLAYOUT0_META_SIZE_MASK) >>
> +                       BCH_FLASHLAYOUT0_META_SIZE_OFFSET;
> +
> +       tmp = readl(&bch_regs->hw_bch_flash0layout1);
> +       l->data0_size = 4 * ((tmp & BCH_FLASHLAYOUT0_DATA0_SIZE_MASK) >>
> +                       BCH_FLASHLAYOUT0_DATA0_SIZE_OFFSET);
> +       l->ecc0 = (tmp & BCH_FLASHLAYOUT0_ECC0_MASK) >>
> +                       BCH_FLASHLAYOUT0_ECC0_OFFSET;
> +       l->datan_size = 4 * ((tmp & BCH_FLASHLAYOUT1_DATAN_SIZE_MASK) >>
> +                       BCH_FLASHLAYOUT1_DATAN_SIZE_OFFSET);
> +       l->eccn = (tmp & BCH_FLASHLAYOUT1_ECCN_MASK) >>
> +                       BCH_FLASHLAYOUT1_ECCN_OFFSET;
> +}
> +
> +/*
> + * Set BCH to specific layout used by ROM bootloader to read FCB.
> + */
> +void mxs_nand_mode_fcb(struct mtd_info *mtd)
> +{
> +       u32 tmp;
> +       struct mxs_bch_regs *bch_regs = (struct mxs_bch_regs *)MXS_BCH_BASE;
> +       struct nand_chip *nand = mtd_to_nand(mtd);
> +       struct mxs_nand_info *nand_info = nand_get_controller_data(nand);
> +
> +       nand_info->en_randomizer = 1;
> +
> +       mtd->writesize = 1024;
> +       mtd->oobsize = 1862 - 1024;
> +
> +       /* 8 ecc_chunks_*/
> +       tmp = 7 << BCH_FLASHLAYOUT0_NBLOCKS_OFFSET;
> +       /* 32 bytes for metadata */
> +       tmp |= 32 << BCH_FLASHLAYOUT0_META_SIZE_OFFSET;
> +       /* using ECC62 level to be performed */
> +       tmp |= 0x1F << BCH_FLASHLAYOUT0_ECC0_OFFSET;
> +       /* 0x20 * 4 bytes of the data0 block */
> +       tmp |= 0x20 << BCH_FLASHLAYOUT0_DATA0_SIZE_OFFSET;
> +       tmp |= 0 << BCH_FLASHLAYOUT0_GF13_0_GF14_1_OFFSET;
> +       writel(tmp, &bch_regs->hw_bch_flash0layout0);
> +
> +       /* 1024 for data + 838 for OOB */
> +       tmp = 1862 << BCH_FLASHLAYOUT1_PAGE_SIZE_OFFSET;
> +       /* using ECC62 level to be performed */
> +       tmp |= 0x1F << BCH_FLASHLAYOUT1_ECCN_OFFSET;
> +       /* 0x20 * 4 bytes of the data0 block */
> +       tmp |= 0x20 << BCH_FLASHLAYOUT1_DATAN_SIZE_OFFSET;
> +       tmp |= 0 << BCH_FLASHLAYOUT1_GF13_0_GF14_1_OFFSET;
> +       writel(tmp, &bch_regs->hw_bch_flash0layout1);
> +}
> +
> +/*
> + * Restore BCH to normal settings.
> + */
> +void mxs_nand_mode_normal(struct mtd_info *mtd)
> +{
> +       struct mxs_bch_regs *bch_regs = (struct mxs_bch_regs *)MXS_BCH_BASE;
> +       struct nand_chip *nand = mtd_to_nand(mtd);
> +       struct mxs_nand_info *nand_info = nand_get_controller_data(nand);
> +
> +       nand_info->en_randomizer = 0;
> +
> +       mtd->writesize = nand_info->writesize;
> +       mtd->oobsize = nand_info->oobsize;
> +
> +       writel(nand_info->bch_flash0layout0, &bch_regs->hw_bch_flash0layout0);
> +       writel(nand_info->bch_flash0layout1, &bch_regs->hw_bch_flash0layout1);
> +}
> +
> +uint32_t mxs_nand_mark_byte_offset(struct mtd_info *mtd)
> +{
> +       struct nand_chip *chip = mtd_to_nand(mtd);
> +       struct mxs_nand_info *nand_info = nand_get_controller_data(chip);
> +       struct bch_geometry *geo = &nand_info->bch_geometry;
> +
> +       return geo->block_mark_byte_offset;
> +}
> +
> +uint32_t mxs_nand_mark_bit_offset(struct mtd_info *mtd)
> +{
> +       struct nand_chip *chip = mtd_to_nand(mtd);
> +       struct mxs_nand_info *nand_info = nand_get_controller_data(chip);
> +       struct bch_geometry *geo = &nand_info->bch_geometry;
> +
> +       return geo->block_mark_bit_offset;
> +}
> diff --git a/include/mxs_nand.h b/include/mxs_nand.h
> index 4bd65cded9..ada20483d0 100644
> --- a/include/mxs_nand.h
> +++ b/include/mxs_nand.h
> @@ -66,8 +66,30 @@ struct mxs_nand_info {
>         /* DMA descriptors */
>         struct mxs_dma_desc     **desc;
>         uint32_t                desc_index;
> +
> +       /* Hardware BCH interface and randomizer */
> +       u32 en_randomizer;
> +       u32 writesize;
> +       u32 oobsize;
> +       u32 bch_flash0layout0;
> +       u32 bch_flash0layout1;
> +};
> +
> +struct mxs_nand_layout {
> +       u32 nblocks;
> +       u32 meta_size;
> +       u32 data0_size;
> +       u32 ecc0;
> +       u32 datan_size;
> +       u32 eccn;
>  };
>
>  int mxs_nand_init_ctrl(struct mxs_nand_info *nand_info);
>  int mxs_nand_init_spl(struct nand_chip *nand);
>  int mxs_nand_setup_ecc(struct mtd_info *mtd);
> +
> +void mxs_nand_mode_fcb(struct mtd_info *mtd);
> +void mxs_nand_mode_normal(struct mtd_info *mtd);
> +u32 mxs_nand_mark_byte_offset(struct mtd_info *mtd);
> +u32 mxs_nand_mark_bit_offset(struct mtd_info *mtd);
> +void mxs_nand_get_layout(struct mtd_info *mtd, struct mxs_nand_layout *l);
> --
> 2.17.1
>
> _______________________________________________
> U-Boot mailing list
> U-Boot@lists.denx.de
> https://lists.denx.de/listinfo/u-boot
Max Krummenacher Oct. 23, 2019, 5:53 p.m. UTC | #2
On Mon, 2019-10-21 at 16:38 +0300, Igor Opaniuk wrote:
> From: Igor Opaniuk <igor.opaniuk@toradex.com>
> 
> On i.MX7 in a sake of reducing the disturbances caused by a neighboring
> cells in the FCB page in the NAND chip, a randomizer is enabled when
> reading the FCB page by ROM bootloader.
> 
> Add API for setting BCH to specific layout (and restoring it back) used by
> ROM bootloader to be able to burn it in a proper way to NAND using
> nandbcb command.
> 
> Signed-off-by: Igor Opaniuk <igor.opaniuk@toradex.com>
> Signed-off-by: Anti Sullin <anti.sullin@artecdesign.ee>

Tested-by: Max Krummenacher <max.krummenacher@toradex.com>

> ---
> 
>  drivers/mtd/nand/raw/mxs_nand.c | 116 ++++++++++++++++++++++++++++++++
>  include/mxs_nand.h              |  22 ++++++
>  2 files changed, 138 insertions(+)
> 
> diff --git a/drivers/mtd/nand/raw/mxs_nand.c b/drivers/mtd/nand/raw/mxs_nand.c
> index a41b9620d0..ad7b644886 100644
> --- a/drivers/mtd/nand/raw/mxs_nand.c
> +++ b/drivers/mtd/nand/raw/mxs_nand.c
> @@ -740,6 +740,19 @@ static int mxs_nand_ecc_write_page(struct mtd_info *mtd,
>  	d->cmd.pio_words[4] = (dma_addr_t)nand_info->data_buf;
>  	d->cmd.pio_words[5] = (dma_addr_t)nand_info->oob_buf;
>  
> +	if (is_mx7() && nand_info->en_randomizer) {
> +		d->cmd.pio_words[2] |= GPMI_ECCCTRL_RANDOMIZER_ENABLE |
> +				       GPMI_ECCCTRL_RANDOMIZER_TYPE2;
> +		/*
> +		 * Write NAND page number needed to be randomized
> +		 * to GPMI_ECCCOUNT register.
> +		 *
> +		 * The value is between 0-255. For additional details
> +		 * check 9.6.6.4 of i.MX7D Applications Processor reference
> +		 */
> +		d->cmd.pio_words[3] |= (page % 255) << 16;
> +	}
> +
>  	mxs_dma_desc_append(channel, d);
>  
>  	/* Flush caches */
> @@ -1003,6 +1016,10 @@ int mxs_nand_setup_ecc(struct mtd_info *mtd)
>  	uint32_t tmp;
>  	int ret;
>  
> +	nand_info->en_randomizer = 0;
> +	nand_info->oobsize = mtd->oobsize;
> +	nand_info->writesize = mtd->writesize;
> +
>  	ret = mxs_nand_set_geometry(mtd, geo);
>  	if (ret)
>  		return ret;
> @@ -1020,6 +1037,7 @@ int mxs_nand_setup_ecc(struct mtd_info *mtd)
>  	tmp |= (geo->gf_len == 14 ? 1 : 0) <<
>  		BCH_FLASHLAYOUT0_GF13_0_GF14_1_OFFSET;
>  	writel(tmp, &bch_regs->hw_bch_flash0layout0);
> +	nand_info->bch_flash0layout0 = tmp;
>  
>  	tmp = (mtd->writesize + mtd->oobsize)
>  		<< BCH_FLASHLAYOUT1_PAGE_SIZE_OFFSET;
> @@ -1028,6 +1046,7 @@ int mxs_nand_setup_ecc(struct mtd_info *mtd)
>  	tmp |= (geo->gf_len == 14 ? 1 : 0) <<
>  		BCH_FLASHLAYOUT1_GF13_0_GF14_1_OFFSET;
>  	writel(tmp, &bch_regs->hw_bch_flash0layout1);
> +	nand_info->bch_flash0layout1 = tmp;
>  
>  	/* Set *all* chip selects to use layout 0 */
>  	writel(0, &bch_regs->hw_bch_layoutselect);
> @@ -1303,3 +1322,100 @@ err:
>  	free(nand_info);
>  }
>  #endif
> +
> +/*
> + * Read NAND layout for FCB block generation.
> + */
> +void mxs_nand_get_layout(struct mtd_info *mtd, struct mxs_nand_layout *l)
> +{
> +	struct mxs_bch_regs *bch_regs = (struct mxs_bch_regs *)MXS_BCH_BASE;
> +	u32 tmp;
> +
> +	tmp = readl(&bch_regs->hw_bch_flash0layout0);
> +	l->nblocks = (tmp & BCH_FLASHLAYOUT0_NBLOCKS_MASK) >>
> +			BCH_FLASHLAYOUT0_NBLOCKS_OFFSET;
> +	l->meta_size = (tmp & BCH_FLASHLAYOUT0_META_SIZE_MASK) >>
> +			BCH_FLASHLAYOUT0_META_SIZE_OFFSET;
> +
> +	tmp = readl(&bch_regs->hw_bch_flash0layout1);
> +	l->data0_size = 4 * ((tmp & BCH_FLASHLAYOUT0_DATA0_SIZE_MASK) >>
> +			BCH_FLASHLAYOUT0_DATA0_SIZE_OFFSET);
> +	l->ecc0 = (tmp & BCH_FLASHLAYOUT0_ECC0_MASK) >>
> +			BCH_FLASHLAYOUT0_ECC0_OFFSET;
> +	l->datan_size = 4 * ((tmp & BCH_FLASHLAYOUT1_DATAN_SIZE_MASK) >>
> +			BCH_FLASHLAYOUT1_DATAN_SIZE_OFFSET);
> +	l->eccn = (tmp & BCH_FLASHLAYOUT1_ECCN_MASK) >>
> +			BCH_FLASHLAYOUT1_ECCN_OFFSET;
> +}
> +
> +/*
> + * Set BCH to specific layout used by ROM bootloader to read FCB.
> + */
> +void mxs_nand_mode_fcb(struct mtd_info *mtd)
> +{
> +	u32 tmp;
> +	struct mxs_bch_regs *bch_regs = (struct mxs_bch_regs *)MXS_BCH_BASE;
> +	struct nand_chip *nand = mtd_to_nand(mtd);
> +	struct mxs_nand_info *nand_info = nand_get_controller_data(nand);
> +
> +	nand_info->en_randomizer = 1;
> +
> +	mtd->writesize = 1024;
> +	mtd->oobsize = 1862 - 1024;
> +
> +	/* 8 ecc_chunks_*/
> +	tmp = 7	<< BCH_FLASHLAYOUT0_NBLOCKS_OFFSET;
> +	/* 32 bytes for metadata */
> +	tmp |= 32 << BCH_FLASHLAYOUT0_META_SIZE_OFFSET;
> +	/* using ECC62 level to be performed */
> +	tmp |= 0x1F << BCH_FLASHLAYOUT0_ECC0_OFFSET;
> +	/* 0x20 * 4 bytes of the data0 block */
> +	tmp |= 0x20 << BCH_FLASHLAYOUT0_DATA0_SIZE_OFFSET;
> +	tmp |= 0 << BCH_FLASHLAYOUT0_GF13_0_GF14_1_OFFSET;
> +	writel(tmp, &bch_regs->hw_bch_flash0layout0);
> +
> +	/* 1024 for data + 838 for OOB */
> +	tmp = 1862 << BCH_FLASHLAYOUT1_PAGE_SIZE_OFFSET;
> +	/* using ECC62 level to be performed */
> +	tmp |= 0x1F << BCH_FLASHLAYOUT1_ECCN_OFFSET;
> +	/* 0x20 * 4 bytes of the data0 block */
> +	tmp |= 0x20 << BCH_FLASHLAYOUT1_DATAN_SIZE_OFFSET;
> +	tmp |= 0 << BCH_FLASHLAYOUT1_GF13_0_GF14_1_OFFSET;
> +	writel(tmp, &bch_regs->hw_bch_flash0layout1);
> +}
> +
> +/*
> + * Restore BCH to normal settings.
> + */
> +void mxs_nand_mode_normal(struct mtd_info *mtd)
> +{
> +	struct mxs_bch_regs *bch_regs = (struct mxs_bch_regs *)MXS_BCH_BASE;
> +	struct nand_chip *nand = mtd_to_nand(mtd);
> +	struct mxs_nand_info *nand_info = nand_get_controller_data(nand);
> +
> +	nand_info->en_randomizer = 0;
> +
> +	mtd->writesize = nand_info->writesize;
> +	mtd->oobsize = nand_info->oobsize;
> +
> +	writel(nand_info->bch_flash0layout0, &bch_regs->hw_bch_flash0layout0);
> +	writel(nand_info->bch_flash0layout1, &bch_regs->hw_bch_flash0layout1);
> +}
> +
> +uint32_t mxs_nand_mark_byte_offset(struct mtd_info *mtd)
> +{
> +	struct nand_chip *chip = mtd_to_nand(mtd);
> +	struct mxs_nand_info *nand_info = nand_get_controller_data(chip);
> +	struct bch_geometry *geo = &nand_info->bch_geometry;
> +
> +	return geo->block_mark_byte_offset;
> +}
> +
> +uint32_t mxs_nand_mark_bit_offset(struct mtd_info *mtd)
> +{
> +	struct nand_chip *chip = mtd_to_nand(mtd);
> +	struct mxs_nand_info *nand_info = nand_get_controller_data(chip);
> +	struct bch_geometry *geo = &nand_info->bch_geometry;
> +
> +	return geo->block_mark_bit_offset;
> +}
> diff --git a/include/mxs_nand.h b/include/mxs_nand.h
> index 4bd65cded9..ada20483d0 100644
> --- a/include/mxs_nand.h
> +++ b/include/mxs_nand.h
> @@ -66,8 +66,30 @@ struct mxs_nand_info {
>  	/* DMA descriptors */
>  	struct mxs_dma_desc	**desc;
>  	uint32_t		desc_index;
> +
> +	/* Hardware BCH interface and randomizer */
> +	u32 en_randomizer;
> +	u32 writesize;
> +	u32 oobsize;
> +	u32 bch_flash0layout0;
> +	u32 bch_flash0layout1;
> +};
> +
> +struct mxs_nand_layout {
> +	u32 nblocks;
> +	u32 meta_size;
> +	u32 data0_size;
> +	u32 ecc0;
> +	u32 datan_size;
> +	u32 eccn;
>  };
>  
>  int mxs_nand_init_ctrl(struct mxs_nand_info *nand_info);
>  int mxs_nand_init_spl(struct nand_chip *nand);
>  int mxs_nand_setup_ecc(struct mtd_info *mtd);
> +
> +void mxs_nand_mode_fcb(struct mtd_info *mtd);
> +void mxs_nand_mode_normal(struct mtd_info *mtd);
> +u32 mxs_nand_mark_byte_offset(struct mtd_info *mtd);
> +u32 mxs_nand_mark_bit_offset(struct mtd_info *mtd);
> +void mxs_nand_get_layout(struct mtd_info *mtd, struct mxs_nand_layout *l);
diff mbox series

Patch

diff --git a/drivers/mtd/nand/raw/mxs_nand.c b/drivers/mtd/nand/raw/mxs_nand.c
index a41b9620d0..ad7b644886 100644
--- a/drivers/mtd/nand/raw/mxs_nand.c
+++ b/drivers/mtd/nand/raw/mxs_nand.c
@@ -740,6 +740,19 @@  static int mxs_nand_ecc_write_page(struct mtd_info *mtd,
 	d->cmd.pio_words[4] = (dma_addr_t)nand_info->data_buf;
 	d->cmd.pio_words[5] = (dma_addr_t)nand_info->oob_buf;
 
+	if (is_mx7() && nand_info->en_randomizer) {
+		d->cmd.pio_words[2] |= GPMI_ECCCTRL_RANDOMIZER_ENABLE |
+				       GPMI_ECCCTRL_RANDOMIZER_TYPE2;
+		/*
+		 * Write NAND page number needed to be randomized
+		 * to GPMI_ECCCOUNT register.
+		 *
+		 * The value is between 0-255. For additional details
+		 * check 9.6.6.4 of i.MX7D Applications Processor reference
+		 */
+		d->cmd.pio_words[3] |= (page % 255) << 16;
+	}
+
 	mxs_dma_desc_append(channel, d);
 
 	/* Flush caches */
@@ -1003,6 +1016,10 @@  int mxs_nand_setup_ecc(struct mtd_info *mtd)
 	uint32_t tmp;
 	int ret;
 
+	nand_info->en_randomizer = 0;
+	nand_info->oobsize = mtd->oobsize;
+	nand_info->writesize = mtd->writesize;
+
 	ret = mxs_nand_set_geometry(mtd, geo);
 	if (ret)
 		return ret;
@@ -1020,6 +1037,7 @@  int mxs_nand_setup_ecc(struct mtd_info *mtd)
 	tmp |= (geo->gf_len == 14 ? 1 : 0) <<
 		BCH_FLASHLAYOUT0_GF13_0_GF14_1_OFFSET;
 	writel(tmp, &bch_regs->hw_bch_flash0layout0);
+	nand_info->bch_flash0layout0 = tmp;
 
 	tmp = (mtd->writesize + mtd->oobsize)
 		<< BCH_FLASHLAYOUT1_PAGE_SIZE_OFFSET;
@@ -1028,6 +1046,7 @@  int mxs_nand_setup_ecc(struct mtd_info *mtd)
 	tmp |= (geo->gf_len == 14 ? 1 : 0) <<
 		BCH_FLASHLAYOUT1_GF13_0_GF14_1_OFFSET;
 	writel(tmp, &bch_regs->hw_bch_flash0layout1);
+	nand_info->bch_flash0layout1 = tmp;
 
 	/* Set *all* chip selects to use layout 0 */
 	writel(0, &bch_regs->hw_bch_layoutselect);
@@ -1303,3 +1322,100 @@  err:
 	free(nand_info);
 }
 #endif
+
+/*
+ * Read NAND layout for FCB block generation.
+ */
+void mxs_nand_get_layout(struct mtd_info *mtd, struct mxs_nand_layout *l)
+{
+	struct mxs_bch_regs *bch_regs = (struct mxs_bch_regs *)MXS_BCH_BASE;
+	u32 tmp;
+
+	tmp = readl(&bch_regs->hw_bch_flash0layout0);
+	l->nblocks = (tmp & BCH_FLASHLAYOUT0_NBLOCKS_MASK) >>
+			BCH_FLASHLAYOUT0_NBLOCKS_OFFSET;
+	l->meta_size = (tmp & BCH_FLASHLAYOUT0_META_SIZE_MASK) >>
+			BCH_FLASHLAYOUT0_META_SIZE_OFFSET;
+
+	tmp = readl(&bch_regs->hw_bch_flash0layout1);
+	l->data0_size = 4 * ((tmp & BCH_FLASHLAYOUT0_DATA0_SIZE_MASK) >>
+			BCH_FLASHLAYOUT0_DATA0_SIZE_OFFSET);
+	l->ecc0 = (tmp & BCH_FLASHLAYOUT0_ECC0_MASK) >>
+			BCH_FLASHLAYOUT0_ECC0_OFFSET;
+	l->datan_size = 4 * ((tmp & BCH_FLASHLAYOUT1_DATAN_SIZE_MASK) >>
+			BCH_FLASHLAYOUT1_DATAN_SIZE_OFFSET);
+	l->eccn = (tmp & BCH_FLASHLAYOUT1_ECCN_MASK) >>
+			BCH_FLASHLAYOUT1_ECCN_OFFSET;
+}
+
+/*
+ * Set BCH to specific layout used by ROM bootloader to read FCB.
+ */
+void mxs_nand_mode_fcb(struct mtd_info *mtd)
+{
+	u32 tmp;
+	struct mxs_bch_regs *bch_regs = (struct mxs_bch_regs *)MXS_BCH_BASE;
+	struct nand_chip *nand = mtd_to_nand(mtd);
+	struct mxs_nand_info *nand_info = nand_get_controller_data(nand);
+
+	nand_info->en_randomizer = 1;
+
+	mtd->writesize = 1024;
+	mtd->oobsize = 1862 - 1024;
+
+	/* 8 ecc_chunks_*/
+	tmp = 7	<< BCH_FLASHLAYOUT0_NBLOCKS_OFFSET;
+	/* 32 bytes for metadata */
+	tmp |= 32 << BCH_FLASHLAYOUT0_META_SIZE_OFFSET;
+	/* using ECC62 level to be performed */
+	tmp |= 0x1F << BCH_FLASHLAYOUT0_ECC0_OFFSET;
+	/* 0x20 * 4 bytes of the data0 block */
+	tmp |= 0x20 << BCH_FLASHLAYOUT0_DATA0_SIZE_OFFSET;
+	tmp |= 0 << BCH_FLASHLAYOUT0_GF13_0_GF14_1_OFFSET;
+	writel(tmp, &bch_regs->hw_bch_flash0layout0);
+
+	/* 1024 for data + 838 for OOB */
+	tmp = 1862 << BCH_FLASHLAYOUT1_PAGE_SIZE_OFFSET;
+	/* using ECC62 level to be performed */
+	tmp |= 0x1F << BCH_FLASHLAYOUT1_ECCN_OFFSET;
+	/* 0x20 * 4 bytes of the data0 block */
+	tmp |= 0x20 << BCH_FLASHLAYOUT1_DATAN_SIZE_OFFSET;
+	tmp |= 0 << BCH_FLASHLAYOUT1_GF13_0_GF14_1_OFFSET;
+	writel(tmp, &bch_regs->hw_bch_flash0layout1);
+}
+
+/*
+ * Restore BCH to normal settings.
+ */
+void mxs_nand_mode_normal(struct mtd_info *mtd)
+{
+	struct mxs_bch_regs *bch_regs = (struct mxs_bch_regs *)MXS_BCH_BASE;
+	struct nand_chip *nand = mtd_to_nand(mtd);
+	struct mxs_nand_info *nand_info = nand_get_controller_data(nand);
+
+	nand_info->en_randomizer = 0;
+
+	mtd->writesize = nand_info->writesize;
+	mtd->oobsize = nand_info->oobsize;
+
+	writel(nand_info->bch_flash0layout0, &bch_regs->hw_bch_flash0layout0);
+	writel(nand_info->bch_flash0layout1, &bch_regs->hw_bch_flash0layout1);
+}
+
+uint32_t mxs_nand_mark_byte_offset(struct mtd_info *mtd)
+{
+	struct nand_chip *chip = mtd_to_nand(mtd);
+	struct mxs_nand_info *nand_info = nand_get_controller_data(chip);
+	struct bch_geometry *geo = &nand_info->bch_geometry;
+
+	return geo->block_mark_byte_offset;
+}
+
+uint32_t mxs_nand_mark_bit_offset(struct mtd_info *mtd)
+{
+	struct nand_chip *chip = mtd_to_nand(mtd);
+	struct mxs_nand_info *nand_info = nand_get_controller_data(chip);
+	struct bch_geometry *geo = &nand_info->bch_geometry;
+
+	return geo->block_mark_bit_offset;
+}
diff --git a/include/mxs_nand.h b/include/mxs_nand.h
index 4bd65cded9..ada20483d0 100644
--- a/include/mxs_nand.h
+++ b/include/mxs_nand.h
@@ -66,8 +66,30 @@  struct mxs_nand_info {
 	/* DMA descriptors */
 	struct mxs_dma_desc	**desc;
 	uint32_t		desc_index;
+
+	/* Hardware BCH interface and randomizer */
+	u32 en_randomizer;
+	u32 writesize;
+	u32 oobsize;
+	u32 bch_flash0layout0;
+	u32 bch_flash0layout1;
+};
+
+struct mxs_nand_layout {
+	u32 nblocks;
+	u32 meta_size;
+	u32 data0_size;
+	u32 ecc0;
+	u32 datan_size;
+	u32 eccn;
 };
 
 int mxs_nand_init_ctrl(struct mxs_nand_info *nand_info);
 int mxs_nand_init_spl(struct nand_chip *nand);
 int mxs_nand_setup_ecc(struct mtd_info *mtd);
+
+void mxs_nand_mode_fcb(struct mtd_info *mtd);
+void mxs_nand_mode_normal(struct mtd_info *mtd);
+u32 mxs_nand_mark_byte_offset(struct mtd_info *mtd);
+u32 mxs_nand_mark_bit_offset(struct mtd_info *mtd);
+void mxs_nand_get_layout(struct mtd_info *mtd, struct mxs_nand_layout *l);