diff mbox

[3/4] ARM: OMAP2: gpmc: Add support for BCH ECC scheme

Message ID 1349274589-11389-4-git-send-email-avinashphilip@ti.com
State Not Applicable
Headers show

Commit Message

Philip, Avinash Oct. 3, 2012, 2:29 p.m. UTC
Add support for BCH ECC scheme to gpmc driver and also enabling multi
sector read/write. This helps in doing single shot NAND page read and
write.

ECC engine configurations
BCH 4 bit support
1. write => ECC engine configured in wrap mode 6 and with ecc_size0 as 32.
2. read  => ECC engine configured in wrap mode 1 and with ecc_size0 as
13 and ecc_size1 as 1.

BCH 8 bit support
1. write => ECC engine configured in wrap mode 6 and with ecc_size0 as 32.
2. read  => ECC engine configured in wrap mode 1 and with ecc_size0 as
26 and ecc_size1 as 2.

Note: For BCH8 ECC bytes set to 14 to make compatible with RBL.

Signed-off-by: Philip, Avinash <avinashphilip@ti.com>
---
:100644 100644 72428bd... c9bc3cf... M	arch/arm/mach-omap2/gpmc.c
:100644 100644 2e6e259... c916510... M	arch/arm/plat-omap/include/plat/gpmc.h
 arch/arm/mach-omap2/gpmc.c             |  120 +++++++++++++++++++++++++++++---
 arch/arm/plat-omap/include/plat/gpmc.h |    1 +
 2 files changed, 112 insertions(+), 9 deletions(-)

Comments

Ivan Djelic Oct. 3, 2012, 6:54 p.m. UTC | #1
On Wed, Oct 03, 2012 at 03:29:48PM +0100, Philip, Avinash wrote:
> Add support for BCH ECC scheme to gpmc driver and also enabling multi
> sector read/write. This helps in doing single shot NAND page read and
> write.
> 
> ECC engine configurations
> BCH 4 bit support
> 1. write => ECC engine configured in wrap mode 6 and with ecc_size0 as 32.
> 2. read  => ECC engine configured in wrap mode 1 and with ecc_size0 as
> 13 and ecc_size1 as 1.
> 
> BCH 8 bit support
> 1. write => ECC engine configured in wrap mode 6 and with ecc_size0 as 32.
> 2. read  => ECC engine configured in wrap mode 1 and with ecc_size0 as
> 26 and ecc_size1 as 2.
> 
> Note: For BCH8 ECC bytes set to 14 to make compatible with RBL.
> 

Hi Philip,

I have a few comments/questions below,

(...)
> diff --git a/arch/arm/mach-omap2/gpmc.c b/arch/arm/mach-omap2/gpmc.c
> index 72428bd..c9bc3cf 100644
> --- a/arch/arm/mach-omap2/gpmc.c
> +++ b/arch/arm/mach-omap2/gpmc.c
> @@ -24,6 +24,7 @@
>  #include <linux/io.h>
>  #include <linux/module.h>
>  #include <linux/interrupt.h>
> +#include <linux/mtd/nand.h>
>  
>  #include <asm/mach-types.h>
>  #include <plat/gpmc.h>
> @@ -83,6 +84,18 @@
>  #define ENABLE_PREFETCH		(0x1 << 7)
>  #define DMA_MPU_MODE		2
>  
> +/* GPMC ecc engine settings for read */
> +#define BCH_WRAPMODE_1		1	/* BCH wrap mode 6 */

Comment should say "mode 1".

(...)
>  /**
> + * gpmc_calculate_ecc_bch	- Generate ecc bytes per block of 512 data bytes for entire page
> + * @cs:  chip select number
> + * @dat: The pointer to data on which ECC is computed
> + * @ecc: The ECC output buffer
> + */
> +int gpmc_calculate_ecc_bch(int cs, const u_char *dat, u_char *ecc)
> +{
> +	int i, eccbchtsel;
> +	u32 nsectors, reg, bch_val1, bch_val2, bch_val3, bch_val4;
> +
> +	if (gpmc_ecc_used != cs)
> +		return -EINVAL;
> +
> +	/* read number of sectors for ecc to be calculated */
> +	nsectors = ((gpmc_read_reg(GPMC_ECC_CONFIG) >> 4) & 0x7) + 1;
> +	/*
> +	 * find BCH scheme used
> +	 * 0 -> BCH4
> +	 * 1 -> BCH8
> +	 */
> +	eccbchtsel = ((gpmc_read_reg(GPMC_ECC_CONFIG) >> 12) & 0x3);
> +
> +	/* update ecc bytes for entire page */
> +	for (i = 0; i < nsectors; i++) {
> +
> +		reg = GPMC_ECC_BCH_RESULT_0 + 16 * i;
> +
> +		/* Read hw-computed remainder */
> +		bch_val1 = gpmc_read_reg(reg + 0);
> +		bch_val2 = gpmc_read_reg(reg + 4);
> +		if (eccbchtsel) {
> +			bch_val3 = gpmc_read_reg(reg + 8);
> +			bch_val4 = gpmc_read_reg(reg + 12);
> +		}
> +
> +		if (eccbchtsel) {
> +			/* BCH8 ecc scheme */
> +			*ecc++ = (bch_val4 & 0xFF);
> +			*ecc++ = ((bch_val3 >> 24) & 0xFF);
> +			*ecc++ = ((bch_val3 >> 16) & 0xFF);
> +			*ecc++ = ((bch_val3 >> 8) & 0xFF);
> +			*ecc++ = (bch_val3 & 0xFF);
> +			*ecc++ = ((bch_val2 >> 24) & 0xFF);
> +			*ecc++ = ((bch_val2 >> 16) & 0xFF);
> +			*ecc++ = ((bch_val2 >> 8) & 0xFF);
> +			*ecc++ = (bch_val2 & 0xFF);
> +			*ecc++ = ((bch_val1 >> 24) & 0xFF);
> +			*ecc++ = ((bch_val1 >> 16) & 0xFF);
> +			*ecc++ = ((bch_val1 >> 8) & 0xFF);
> +			*ecc++ = (bch_val1 & 0xFF);
> +			/* 14th byte of ecc not used */
> +			*ecc++ = 0;
> +		} else {
> +			/* BCH4 ecc scheme */
> +			*ecc++ = ((bch_val2 >> 12) & 0xFF);
> +			*ecc++ = ((bch_val2 >> 4) & 0xFF);
> +			*ecc++ = (((bch_val2 & 0xF) << 4) |
> +					((bch_val1 >> 28) & 0xF));
> +			*ecc++ = ((bch_val1 >> 20) & 0xFF);
> +			*ecc++ = ((bch_val1 >> 12) & 0xFF);
> +			*ecc++ = ((bch_val1 >> 4) & 0xFF);
> +			*ecc++ = ((bch_val1 & 0xF) << 4);
> +		}
> +	}
> +
> +	gpmc_ecc_used = -EINVAL;
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(gpmc_calculate_ecc_bch);

Here you introduce a function very similar to gpmc_calculate_ecc_bch4 and
gpmc_calculate_ecc_bch8, but without the added benefit (IMHO) of the constant
polynomial that allows to get an ecc sequence of 0xFFs for a buffer filled with
0xFFs. Why ?
If using the ELM prevents you from reusing gpmc_calculate_ecc_bch[48], could you explain in which way ?

Best regards,
--
Ivan
Philip, Avinash Oct. 4, 2012, 8:03 a.m. UTC | #2
On Thu, Oct 04, 2012 at 00:24:58, Ivan Djelic wrote:
> On Wed, Oct 03, 2012 at 03:29:48PM +0100, Philip, Avinash wrote:
> > Add support for BCH ECC scheme to gpmc driver and also enabling multi
> > sector read/write. This helps in doing single shot NAND page read and
> > write.
> > 
> > ECC engine configurations
> > BCH 4 bit support
> > 1. write => ECC engine configured in wrap mode 6 and with ecc_size0 as 32.
> > 2. read  => ECC engine configured in wrap mode 1 and with ecc_size0 as
> > 13 and ecc_size1 as 1.
> > 
> > BCH 8 bit support
> > 1. write => ECC engine configured in wrap mode 6 and with ecc_size0 as 32.
> > 2. read  => ECC engine configured in wrap mode 1 and with ecc_size0 as
> > 26 and ecc_size1 as 2.
> > 
> > Note: For BCH8 ECC bytes set to 14 to make compatible with RBL.
> > 
> 
> Hi Philip,
> 
> I have a few comments/questions below,
> 
> (...)
> > diff --git a/arch/arm/mach-omap2/gpmc.c b/arch/arm/mach-omap2/gpmc.c
> > index 72428bd..c9bc3cf 100644
> > --- a/arch/arm/mach-omap2/gpmc.c
> > +++ b/arch/arm/mach-omap2/gpmc.c
> > @@ -24,6 +24,7 @@
> >  #include <linux/io.h>
> >  #include <linux/module.h>
> >  #include <linux/interrupt.h>
> > +#include <linux/mtd/nand.h>
> >  
> >  #include <asm/mach-types.h>
> >  #include <plat/gpmc.h>
> > @@ -83,6 +84,18 @@
> >  #define ENABLE_PREFETCH		(0x1 << 7)
> >  #define DMA_MPU_MODE		2
> >  
> > +/* GPMC ecc engine settings for read */
> > +#define BCH_WRAPMODE_1		1	/* BCH wrap mode 6 */
> 
> Comment should say "mode 1".

Ok I will correct it.

> 
> (...)
> >  /**
> > + * gpmc_calculate_ecc_bch	- Generate ecc bytes per block of 512 data bytes for entire page
> > + * @cs:  chip select number
> > + * @dat: The pointer to data on which ECC is computed
> > + * @ecc: The ECC output buffer
> > + */
> > +int gpmc_calculate_ecc_bch(int cs, const u_char *dat, u_char *ecc)
> > +{
> > +	int i, eccbchtsel;
> > +	u32 nsectors, reg, bch_val1, bch_val2, bch_val3, bch_val4;
> > +
> > +	if (gpmc_ecc_used != cs)
> > +		return -EINVAL;
> > +
> > +	/* read number of sectors for ecc to be calculated */
> > +	nsectors = ((gpmc_read_reg(GPMC_ECC_CONFIG) >> 4) & 0x7) + 1;
> > +	/*
> > +	 * find BCH scheme used
> > +	 * 0 -> BCH4
> > +	 * 1 -> BCH8
> > +	 */
> > +	eccbchtsel = ((gpmc_read_reg(GPMC_ECC_CONFIG) >> 12) & 0x3);
> > +
> > +	/* update ecc bytes for entire page */
> > +	for (i = 0; i < nsectors; i++) {
> > +
> > +		reg = GPMC_ECC_BCH_RESULT_0 + 16 * i;
> > +
> > +		/* Read hw-computed remainder */
> > +		bch_val1 = gpmc_read_reg(reg + 0);
> > +		bch_val2 = gpmc_read_reg(reg + 4);
> > +		if (eccbchtsel) {
> > +			bch_val3 = gpmc_read_reg(reg + 8);
> > +			bch_val4 = gpmc_read_reg(reg + 12);
> > +		}
> > +
> > +		if (eccbchtsel) {
> > +			/* BCH8 ecc scheme */
> > +			*ecc++ = (bch_val4 & 0xFF);
> > +			*ecc++ = ((bch_val3 >> 24) & 0xFF);
> > +			*ecc++ = ((bch_val3 >> 16) & 0xFF);
> > +			*ecc++ = ((bch_val3 >> 8) & 0xFF);
> > +			*ecc++ = (bch_val3 & 0xFF);
> > +			*ecc++ = ((bch_val2 >> 24) & 0xFF);
> > +			*ecc++ = ((bch_val2 >> 16) & 0xFF);
> > +			*ecc++ = ((bch_val2 >> 8) & 0xFF);
> > +			*ecc++ = (bch_val2 & 0xFF);
> > +			*ecc++ = ((bch_val1 >> 24) & 0xFF);
> > +			*ecc++ = ((bch_val1 >> 16) & 0xFF);
> > +			*ecc++ = ((bch_val1 >> 8) & 0xFF);
> > +			*ecc++ = (bch_val1 & 0xFF);
> > +			/* 14th byte of ecc not used */
> > +			*ecc++ = 0;
> > +		} else {
> > +			/* BCH4 ecc scheme */
> > +			*ecc++ = ((bch_val2 >> 12) & 0xFF);
> > +			*ecc++ = ((bch_val2 >> 4) & 0xFF);
> > +			*ecc++ = (((bch_val2 & 0xF) << 4) |
> > +					((bch_val1 >> 28) & 0xF));
> > +			*ecc++ = ((bch_val1 >> 20) & 0xFF);
> > +			*ecc++ = ((bch_val1 >> 12) & 0xFF);
> > +			*ecc++ = ((bch_val1 >> 4) & 0xFF);
> > +			*ecc++ = ((bch_val1 & 0xF) << 4);
> > +		}
> > +	}
> > +
> > +	gpmc_ecc_used = -EINVAL;
> > +	return 0;
> > +}
> > +EXPORT_SYMBOL_GPL(gpmc_calculate_ecc_bch);
> 
> Here you introduce a function very similar to gpmc_calculate_ecc_bch4 and
> gpmc_calculate_ecc_bch8, but without the added benefit (IMHO) of the constant
> polynomial that allows to get an ecc sequence of 0xFFs for a buffer filled with
> 0xFFs. Why ?

I don't exactly understand what we benefitted/achieve. In my observation,
this API does spare area also written with 0xFF if data area is 0xFFs.
So the area looks like erased page again.

> If using the ELM prevents you from reusing gpmc_calculate_ecc_bch[48], could you explain in which way ?

When using gpmc_calculate_ecc_bch[48], calculated ecc values modified.
The read sequence we following is
Read 512 byte -> read ECC bytes from spare area
Now the calculated ECC will be zero if no error is reported. In case of error, a syndrome
Polynomial is reported. In either case modifying will corrupt the data.

This is valid if we are writing a page with 0xFF also. But this time we were filling a valid
ecc in spare area not 0xFF as in gpmc_calculate_ecc_bch[48].

Additionally to make compatible with RBL ECC layout (14 byte), we were setting ecc[13] as zero.

Thanks
Avinash  

> 
> Best regards,
> --
> Ivan
>
Ivan Djelic Oct. 4, 2012, 12:04 p.m. UTC | #3
On Thu, Oct 04, 2012 at 09:03:42AM +0100, Philip, Avinash wrote:
(...)
> > > +int gpmc_calculate_ecc_bch(int cs, const u_char *dat, u_char *ecc)
> > > +{
> > > +	int i, eccbchtsel;
> > > +	u32 nsectors, reg, bch_val1, bch_val2, bch_val3, bch_val4;
> > > +
> > > +	if (gpmc_ecc_used != cs)
> > > +		return -EINVAL;
> > > +
> > > +	/* read number of sectors for ecc to be calculated */
> > > +	nsectors = ((gpmc_read_reg(GPMC_ECC_CONFIG) >> 4) & 0x7) + 1;
> > > +	/*
> > > +	 * find BCH scheme used
> > > +	 * 0 -> BCH4
> > > +	 * 1 -> BCH8
> > > +	 */
> > > +	eccbchtsel = ((gpmc_read_reg(GPMC_ECC_CONFIG) >> 12) & 0x3);
> > > +
> > > +	/* update ecc bytes for entire page */
> > > +	for (i = 0; i < nsectors; i++) {
> > > +
> > > +		reg = GPMC_ECC_BCH_RESULT_0 + 16 * i;
> > > +
> > > +		/* Read hw-computed remainder */
> > > +		bch_val1 = gpmc_read_reg(reg + 0);
> > > +		bch_val2 = gpmc_read_reg(reg + 4);
> > > +		if (eccbchtsel) {
> > > +			bch_val3 = gpmc_read_reg(reg + 8);
> > > +			bch_val4 = gpmc_read_reg(reg + 12);
> > > +		}
> > > +
> > > +		if (eccbchtsel) {
> > > +			/* BCH8 ecc scheme */
> > > +			*ecc++ = (bch_val4 & 0xFF);
> > > +			*ecc++ = ((bch_val3 >> 24) & 0xFF);
> > > +			*ecc++ = ((bch_val3 >> 16) & 0xFF);
> > > +			*ecc++ = ((bch_val3 >> 8) & 0xFF);
> > > +			*ecc++ = (bch_val3 & 0xFF);
> > > +			*ecc++ = ((bch_val2 >> 24) & 0xFF);
> > > +			*ecc++ = ((bch_val2 >> 16) & 0xFF);
> > > +			*ecc++ = ((bch_val2 >> 8) & 0xFF);
> > > +			*ecc++ = (bch_val2 & 0xFF);
> > > +			*ecc++ = ((bch_val1 >> 24) & 0xFF);
> > > +			*ecc++ = ((bch_val1 >> 16) & 0xFF);
> > > +			*ecc++ = ((bch_val1 >> 8) & 0xFF);
> > > +			*ecc++ = (bch_val1 & 0xFF);
> > > +			/* 14th byte of ecc not used */
> > > +			*ecc++ = 0;
> > > +		} else {
> > > +			/* BCH4 ecc scheme */
> > > +			*ecc++ = ((bch_val2 >> 12) & 0xFF);
> > > +			*ecc++ = ((bch_val2 >> 4) & 0xFF);
> > > +			*ecc++ = (((bch_val2 & 0xF) << 4) |
> > > +					((bch_val1 >> 28) & 0xF));
> > > +			*ecc++ = ((bch_val1 >> 20) & 0xFF);
> > > +			*ecc++ = ((bch_val1 >> 12) & 0xFF);
> > > +			*ecc++ = ((bch_val1 >> 4) & 0xFF);
> > > +			*ecc++ = ((bch_val1 & 0xF) << 4);
> > > +		}
> > > +	}
> > > +
> > > +	gpmc_ecc_used = -EINVAL;
> > > +	return 0;
> > > +}
> > > +EXPORT_SYMBOL_GPL(gpmc_calculate_ecc_bch);
> > 
> > Here you introduce a function very similar to gpmc_calculate_ecc_bch4 and
> > gpmc_calculate_ecc_bch8, but without the added benefit (IMHO) of the constant
> > polynomial that allows to get an ecc sequence of 0xFFs for a buffer filled with
> > 0xFFs. Why ?
> 
> I don't exactly understand what we benefitted/achieve. In my observation,
> this API does spare area also written with 0xFF if data area is 0xFFs.
> So the area looks like erased page again.

Precisely. It means you can read the page with ECC enabled without having to check if
the page has been programmed; it also enables bitflip correction on erased pages.

> > If using the ELM prevents you from reusing gpmc_calculate_ecc_bch[48], could you explain in which way ?
> 
> When using gpmc_calculate_ecc_bch[48], calculated ecc values modified.
> The read sequence we following is
> Read 512 byte -> read ECC bytes from spare area
> Now the calculated ECC will be zero if no error is reported. In case of error, a syndrome
> Polynomial is reported. In either case modifying will corrupt the data.

It is still possible to retrieve your original error syndrome, even using the technique transforming ECC on erased pages into 0xFFs.
But I guess you're not interested if you need RBL compatibility.

BR,
--
Ivan
Peter Korsgaard Oct. 15, 2012, 6:48 p.m. UTC | #4
>>>>> Philip, Avinash <avinashphilip@ti.com> writes:

 > Add support for BCH ECC scheme to gpmc driver and also enabling multi
 > sector read/write. This helps in doing single shot NAND page read and
 > write.

 > ECC engine configurations
 > BCH 4 bit support
 > 1. write => ECC engine configured in wrap mode 6 and with ecc_size0 as 32.
 > 2. read  => ECC engine configured in wrap mode 1 and with ecc_size0 as
 > 13 and ecc_size1 as 1.

 > BCH 8 bit support
 > 1. write => ECC engine configured in wrap mode 6 and with ecc_size0 as 32.
 > 2. read  => ECC engine configured in wrap mode 1 and with ecc_size0 as
 > 26 and ecc_size1 as 2.

 > Note: For BCH8 ECC bytes set to 14 to make compatible with RBL.

On what device? In the am335x TRM (spruh73f.pdf) figure 26-15 (pg 4273)
the rom code is documented to not use any padding on the ECC bytes
(E.G. oob 2..53):

http://www.ti.com/litv/pdf/spruh73f

I see the driver in the u-boot-am33x tree (ti81xx_nand.c) seems to use
4x14 bytes as well though, so perhaps that's a bug in the documentation
instead?
Philip, Avinash Oct. 23, 2012, 10:18 a.m. UTC | #5
On Tue, Oct 16, 2012 at 00:18:30, Peter Korsgaard wrote:
> >>>>> Philip, Avinash <avinashphilip@ti.com> writes:
> 
>  > Add support for BCH ECC scheme to gpmc driver and also enabling multi
>  > sector read/write. This helps in doing single shot NAND page read and
>  > write.
> 
>  > ECC engine configurations
>  > BCH 4 bit support
>  > 1. write => ECC engine configured in wrap mode 6 and with ecc_size0 as 32.
>  > 2. read  => ECC engine configured in wrap mode 1 and with ecc_size0 as
>  > 13 and ecc_size1 as 1.
> 
>  > BCH 8 bit support
>  > 1. write => ECC engine configured in wrap mode 6 and with ecc_size0 as 32.
>  > 2. read  => ECC engine configured in wrap mode 1 and with ecc_size0 as
>  > 26 and ecc_size1 as 2.
> 
>  > Note: For BCH8 ECC bytes set to 14 to make compatible with RBL.
> 
> On what device? In the am335x TRM (spruh73f.pdf) figure 26-15 (pg 4273)
> the rom code is documented to not use any padding on the ECC bytes
> (E.G. oob 2..53):
> 
> http://www.ti.com/litv/pdf/spruh73f
> 
> I see the driver in the u-boot-am33x tree (ti81xx_nand.c) seems to use
> 4x14 bytes as well though, so perhaps that's a bug in the documentation
> instead?

Yes, RBL uses 4x14 bytes.

Thanks
Avinash

> 
> -- 
> Bye, Peter Korsgaard
>
diff mbox

Patch

diff --git a/arch/arm/mach-omap2/gpmc.c b/arch/arm/mach-omap2/gpmc.c
index 72428bd..c9bc3cf 100644
--- a/arch/arm/mach-omap2/gpmc.c
+++ b/arch/arm/mach-omap2/gpmc.c
@@ -24,6 +24,7 @@ 
 #include <linux/io.h>
 #include <linux/module.h>
 #include <linux/interrupt.h>
+#include <linux/mtd/nand.h>
 
 #include <asm/mach-types.h>
 #include <plat/gpmc.h>
@@ -83,6 +84,18 @@ 
 #define ENABLE_PREFETCH		(0x1 << 7)
 #define DMA_MPU_MODE		2
 
+/* GPMC ecc engine settings for read */
+#define BCH_WRAPMODE_1		1	/* BCH wrap mode 6 */
+#define BCH8R_ECC_SIZE0		0x1a	/* ecc_size0 = 26 */
+#define BCH8R_ECC_SIZE1		0x2	/* ecc_size1 = 2 */
+#define BCH4R_ECC_SIZE0		0xd	/* ecc_size0 = 13 */
+#define BCH4R_ECC_SIZE1		0x1	/* ecc_size1 = 1 4bit padding in BCH4 */
+
+/* GPMC ecc engine settings for write */
+#define BCH_WRAPMODE_6		6	/* BCH wrap mode 6 */
+#define BCH_ECC_SIZE0		0x0	/* ecc_size0 = 0, no oob protection */
+#define BCH_ECC_SIZE1		0x20	/* ecc_size1 = 32 */
+
 /* XXX: Only NAND irq has been considered,currently these are the only ones used
  */
 #define	GPMC_NR_IRQ		2
@@ -1119,7 +1132,8 @@  EXPORT_SYMBOL_GPL(gpmc_init_hwecc_bch);
 int gpmc_enable_hwecc_bch(int cs, int mode, int dev_width, int nsectors,
 			  int nerrors)
 {
-	unsigned int val;
+	unsigned int val, wr_mode;
+	unsigned int ecc_size1, ecc_size0;
 
 	/* check if ecc module is in use */
 	if (gpmc_ecc_used != -EINVAL)
@@ -1130,18 +1144,35 @@  int gpmc_enable_hwecc_bch(int cs, int mode, int dev_width, int nsectors,
 	/* clear ecc and enable bits */
 	gpmc_write_reg(GPMC_ECC_CONTROL, 0x1);
 
-	/*
-	 * When using BCH, sector size is hardcoded to 512 bytes.
-	 * Here we are using wrapping mode 6 both for reading and writing, with:
-	 *  size0 = 0  (no additional protected byte in spare area)
-	 *  size1 = 32 (skip 32 nibbles = 16 bytes per sector in spare area)
-	 */
-	gpmc_write_reg(GPMC_ECC_SIZE_CONFIG, (32 << 22) | (0 << 12));
+	/*  When using BCH, sector size is hard coded to 512 bytes. */
+	if ((mode == NAND_ECC_READ) && (nsectors != 1)) {
+		/*
+		 * Here we are using wrapping mode 1 for reading, for
+		 * supporting multi sector reading.
+		 * Read mode, ECC engine enabled for valid ecc_size0 nibbles
+		 * & disabled for ecc_size1 nibbles.
+		 */
+		ecc_size0 = (nerrors == 8) ? BCH8R_ECC_SIZE0 : BCH4R_ECC_SIZE0;
+		ecc_size1 = (nerrors == 8) ? BCH8R_ECC_SIZE1 : BCH4R_ECC_SIZE1;
+		wr_mode = BCH_WRAPMODE_1;
+	} else {
+		/*
+		 * Here we are using wrapping mode 6 for writing,
+		 * ECC engine enabled for valid ecc_size0 nibbles
+		 * & disabled for ecc_size1 nibbles.
+		 */
+		ecc_size0 = BCH_ECC_SIZE0;
+		ecc_size1 = BCH_ECC_SIZE1;
+		wr_mode = BCH_WRAPMODE_6;
+	}
+
+	gpmc_write_reg(GPMC_ECC_SIZE_CONFIG, (ecc_size1 << 22) |
+			(ecc_size0 << 12));
 
 	/* BCH configuration */
 	val = ((1                        << 16) | /* enable BCH */
 	       (((nerrors == 8) ? 1 : 0) << 12) | /* 8 or 4 bits */
-	       (0x06                     <<  8) | /* wrap mode = 6 */
+	       (wr_mode                  <<  8) | /* wrap mode = 6 or 1 */
 	       (dev_width                <<  7) | /* bus width */
 	       (((nsectors-1) & 0x7)     <<  4) | /* number of sectors */
 	       (cs                       <<  1) | /* ECC CS */
@@ -1154,6 +1185,77 @@  int gpmc_enable_hwecc_bch(int cs, int mode, int dev_width, int nsectors,
 EXPORT_SYMBOL_GPL(gpmc_enable_hwecc_bch);
 
 /**
+ * gpmc_calculate_ecc_bch	- Generate ecc bytes per block of 512 data bytes for entire page
+ * @cs:  chip select number
+ * @dat: The pointer to data on which ECC is computed
+ * @ecc: The ECC output buffer
+ */
+int gpmc_calculate_ecc_bch(int cs, const u_char *dat, u_char *ecc)
+{
+	int i, eccbchtsel;
+	u32 nsectors, reg, bch_val1, bch_val2, bch_val3, bch_val4;
+
+	if (gpmc_ecc_used != cs)
+		return -EINVAL;
+
+	/* read number of sectors for ecc to be calculated */
+	nsectors = ((gpmc_read_reg(GPMC_ECC_CONFIG) >> 4) & 0x7) + 1;
+	/*
+	 * find BCH scheme used
+	 * 0 -> BCH4
+	 * 1 -> BCH8
+	 */
+	eccbchtsel = ((gpmc_read_reg(GPMC_ECC_CONFIG) >> 12) & 0x3);
+
+	/* update ecc bytes for entire page */
+	for (i = 0; i < nsectors; i++) {
+
+		reg = GPMC_ECC_BCH_RESULT_0 + 16 * i;
+
+		/* Read hw-computed remainder */
+		bch_val1 = gpmc_read_reg(reg + 0);
+		bch_val2 = gpmc_read_reg(reg + 4);
+		if (eccbchtsel) {
+			bch_val3 = gpmc_read_reg(reg + 8);
+			bch_val4 = gpmc_read_reg(reg + 12);
+		}
+
+		if (eccbchtsel) {
+			/* BCH8 ecc scheme */
+			*ecc++ = (bch_val4 & 0xFF);
+			*ecc++ = ((bch_val3 >> 24) & 0xFF);
+			*ecc++ = ((bch_val3 >> 16) & 0xFF);
+			*ecc++ = ((bch_val3 >> 8) & 0xFF);
+			*ecc++ = (bch_val3 & 0xFF);
+			*ecc++ = ((bch_val2 >> 24) & 0xFF);
+			*ecc++ = ((bch_val2 >> 16) & 0xFF);
+			*ecc++ = ((bch_val2 >> 8) & 0xFF);
+			*ecc++ = (bch_val2 & 0xFF);
+			*ecc++ = ((bch_val1 >> 24) & 0xFF);
+			*ecc++ = ((bch_val1 >> 16) & 0xFF);
+			*ecc++ = ((bch_val1 >> 8) & 0xFF);
+			*ecc++ = (bch_val1 & 0xFF);
+			/* 14th byte of ecc not used */
+			*ecc++ = 0;
+		} else {
+			/* BCH4 ecc scheme */
+			*ecc++ = ((bch_val2 >> 12) & 0xFF);
+			*ecc++ = ((bch_val2 >> 4) & 0xFF);
+			*ecc++ = (((bch_val2 & 0xF) << 4) |
+					((bch_val1 >> 28) & 0xF));
+			*ecc++ = ((bch_val1 >> 20) & 0xFF);
+			*ecc++ = ((bch_val1 >> 12) & 0xFF);
+			*ecc++ = ((bch_val1 >> 4) & 0xFF);
+			*ecc++ = ((bch_val1 & 0xF) << 4);
+		}
+	}
+
+	gpmc_ecc_used = -EINVAL;
+	return 0;
+}
+EXPORT_SYMBOL_GPL(gpmc_calculate_ecc_bch);
+
+/**
  * gpmc_calculate_ecc_bch4 - Generate 7 ecc bytes per sector of 512 data bytes
  * @cs:  chip select number
  * @dat: The pointer to data on which ecc is computed
diff --git a/arch/arm/plat-omap/include/plat/gpmc.h b/arch/arm/plat-omap/include/plat/gpmc.h
index 2e6e259..c916510 100644
--- a/arch/arm/plat-omap/include/plat/gpmc.h
+++ b/arch/arm/plat-omap/include/plat/gpmc.h
@@ -185,6 +185,7 @@  int gpmc_enable_hwecc_bch(int cs, int mode, int dev_width, int nsectors,
 			  int nerrors);
 int gpmc_calculate_ecc_bch4(int cs, const u_char *dat, u_char *ecc);
 int gpmc_calculate_ecc_bch8(int cs, const u_char *dat, u_char *ecc);
+int gpmc_calculate_ecc_bch(int cs, const u_char *dat, u_char *ecc);
 #endif /* CONFIG_ARCH_OMAP3 */
 
 #endif