]> git.karo-electronics.de Git - karo-tx-uboot.git/blobdiff - drivers/mtd/nand/omap_gpmc.c
karo: merge with Ka-Ro specific tree for secure boot support
[karo-tx-uboot.git] / drivers / mtd / nand / omap_gpmc.c
index 790d5385e0bdc18ec5172f21735f8232265b46e5..f809a783a5bbe9c2fde767484e91f51c67802469 100644 (file)
 #include <asm/io.h>
 #include <asm/errno.h>
 #include <asm/arch/mem.h>
-#include <asm/arch/cpu.h>
-#include <asm/omap_gpmc.h>
+#include <linux/mtd/omap_gpmc.h>
 #include <linux/mtd/nand_ecc.h>
 #include <linux/bch.h>
 #include <linux/compiler.h>
 #include <nand.h>
-#include <asm/omap_elm.h>
+#include <linux/mtd/omap_elm.h>
 
 #define BADBLOCK_MARKER_LENGTH 2
 #define SECTOR_BYTES           512
+#define ECCCLEAR               (0x1 << 8)
+#define ECCRESULTREG1          (0x1 << 0)
+/* 4 bit padding to make byte aligned, 56 = 52 + 4 */
+#define BCH4_BIT_PAD           4
 
-static uint8_t cs;
+#ifdef CONFIG_BCH
+static u8  bch8_polynomial[] = {0xef, 0x51, 0x2e, 0x09, 0xed, 0x93, 0x9a, 0xc2,
+                               0x97, 0x79, 0xe5, 0x24, 0xb5};
+#endif
+static uint8_t cs_next;
 static __maybe_unused struct nand_ecclayout omap_ecclayout;
 
+/*
+ * Driver configurations
+ */
+struct omap_nand_info {
+       struct bch_control *control;
+       enum omap_ecc ecc_scheme;
+       int cs;
+};
+
+/* We are wasting a bit of memory but al least we are safe */
+static struct omap_nand_info omap_nand_info[GPMC_MAX_CS];
+
+static struct gpmc __iomem *gpmc_cfg = (void __iomem *)GPMC_BASE;
+
+#ifdef CONFIG_SYS_NAND_USE_FLASH_BBT
+static uint8_t bbt_pattern[] = {'B', 'b', 't', '0' };
+static uint8_t mirror_pattern[] = {'1', 't', 'b', 'B' };
+
+static struct nand_bbt_descr bbt_main_descr = {
+       .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE |
+               NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP,
+       .offs = 0, /* may be overwritten depending on ECC layout */
+       .len = 4,
+       .veroffs = 4, /* may be overwritten depending on ECC layout */
+       .maxblocks = 4,
+       .pattern = bbt_pattern,
+};
+
+static struct nand_bbt_descr bbt_mirror_descr = {
+       .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE |
+               NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP,
+       .offs = 0, /* may be overwritten depending on ECC layout */
+       .len = 4,
+       .veroffs = 4, /* may be overwritten depending on ECC layout */
+       .maxblocks = 4,
+       .pattern = mirror_pattern,
+};
+#endif
+
+#define PREFETCH_FIFOTHRESHOLD_MAX             0x40
+#define PREFETCH_FIFOTHRESHOLD(val)            ((val) << 8)
+
+#define PREFETCH_ENABLEOPTIMIZEDACCESS         (0x1 << 27)
+
+#define GPMC_PREFETCH_STATUS_FIFO_CNT(val)     (((val) >> 24) & 0x7F)
+#define GPMC_PREFETCH_STATUS_COUNT(val)                ((val) & 0x00003fff)
+
+#define CS_NUM_SHIFT                           24
+#define ENABLE_PREFETCH                                (0x1 << 7)
+#define DMA_MPU_MODE                           2
+
+#define OMAP_NAND_TIMEOUT_MS                   5000
+
+#define PRINT_REG(x) debug("+++ %.15s (0x%08x)=0x%08x\n", #x, &gpmc_cfg->x, readl(&gpmc_cfg->x))
+
+#ifdef CONFIG_SYS_GPMC_PREFETCH_ENABLE
+/**
+ * gpmc_prefetch_enable - configures and starts prefetch transfer
+ * @cs: cs (chip select) number
+ * @fifo_th: fifo threshold to be used for read/ write
+ * @count: number of bytes to be transferred
+ * @is_write: prefetch read(0) or write post(1) mode
+ */
+static inline void gpmc_prefetch_enable(int cs, int fifo_th,
+                                       unsigned int count, int is_write)
+{
+       writel(count, &gpmc_cfg->pref_config2);
+
+       /* Set the prefetch read / post write and enable the engine.
+        * Set which cs is has requested for.
+        */
+       uint32_t val = (cs << CS_NUM_SHIFT) |
+               PREFETCH_ENABLEOPTIMIZEDACCESS |
+               PREFETCH_FIFOTHRESHOLD(fifo_th) |
+               ENABLE_PREFETCH |
+               !!is_write;
+       writel(val, &gpmc_cfg->pref_config1);
+
+       /*  Start the prefetch engine */
+       writel(0x1, &gpmc_cfg->pref_control);
+}
+
+/**
+ * gpmc_prefetch_reset - disables and stops the prefetch engine
+ */
+static inline void gpmc_prefetch_reset(void)
+{
+       /* Stop the PFPW engine */
+       writel(0x0, &gpmc_cfg->pref_control);
+
+       /* Reset/disable the PFPW engine */
+       writel(0x0, &gpmc_cfg->pref_config1);
+}
+
+//#define FIFO_IOADDR          (nand->IO_ADDR_R)
+#define FIFO_IOADDR            PISMO1_NAND_BASE
+
+/**
+ * read_buf_pref - read data from NAND controller into buffer
+ * @mtd: MTD device structure
+ * @buf: buffer to store date
+ * @len: number of bytes to read
+ */
+static void read_buf_pref(struct mtd_info *mtd, u_char *buf, int len)
+{
+       gpmc_prefetch_enable(cs, PREFETCH_FIFOTHRESHOLD_MAX, len, 0);
+       do {
+               // Get number of bytes waiting in the FIFO
+               uint32_t read_bytes = GPMC_PREFETCH_STATUS_FIFO_CNT(readl(&gpmc_cfg->pref_status));
+
+               if (read_bytes == 0)
+                       continue;
+               // Alignment of Destination Buffer
+               while (read_bytes && ((unsigned int)buf & 3)) {
+                       *buf++ = readb(FIFO_IOADDR);
+                       read_bytes--;
+                       len--;
+               }
+               // Use maximum word size (32bit) inside this loop, because speed is limited by
+               // GPMC bus arbitration with a maximum transfer rate of 3.000.000/sec.
+               len -= read_bytes & ~3;
+               while (read_bytes >= 4) {
+                       *((uint32_t*)buf) = readl(FIFO_IOADDR);
+                       buf += 4;
+                       read_bytes -= 4;
+               }
+               // Transfer the last (non-aligned) bytes only at the last iteration,
+               // to maintain full speed up to the end of the transfer.
+               if (read_bytes == len) {
+                       while (read_bytes) {
+                               *buf++ = readb(FIFO_IOADDR);
+                               read_bytes--;
+                       }
+                       len = 0;
+               }
+       } while (len > 0);
+       gpmc_prefetch_reset();
+}
+
+/*
+ * write_buf_pref - write buffer to NAND controller
+ * @mtd: MTD device structure
+ * @buf: data buffer
+ * @len: number of bytes to write
+ */
+static void write_buf_pref(struct mtd_info *mtd, const u_char *buf, int len)
+{
+       /*  configure and start prefetch transfer */
+       gpmc_prefetch_enable(cs, PREFETCH_FIFOTHRESHOLD_MAX, len, 1);
+
+       while (len) {
+               // Get number of free bytes in the FIFO
+               uint32_t write_bytes = GPMC_PREFETCH_STATUS_FIFO_CNT(readl(&gpmc_cfg->pref_status));
+
+               // don't write more bytes than requested
+               if (write_bytes > len)
+                       write_bytes = len;
+
+               // Alignment of Source Buffer
+               while (write_bytes && ((unsigned int)buf & 3)) {
+                       writeb(*buf++, FIFO_IOADDR);
+                       write_bytes--;
+                       len--;
+               }
+
+               // Use maximum word size (32bit) inside this loop, because speed is limited by
+               // GPMC bus arbitration with a maximum transfer rate of 3.000.000/sec.
+               len -= write_bytes & ~3;
+               while (write_bytes >= 4) {
+                       writel(*((uint32_t*)buf), FIFO_IOADDR);
+                       buf += 4;
+                       write_bytes -= 4;
+               }
+
+               // Transfer the last (non-aligned) bytes only at the last iteration,
+               // to maintain full speed up to the end of the transfer.
+               if (write_bytes == len) {
+                       while (write_bytes) {
+                               writeb(*buf++, FIFO_IOADDR);
+                               write_bytes--;
+                       }
+                       len = 0;
+               }
+       }
+
+       /* wait for data to be flushed out before resetting the prefetch */
+       while ((len = GPMC_PREFETCH_STATUS_COUNT(readl(&gpmc_cfg->pref_status)))) {
+               debug("%u bytes still in FIFO\n", PREFETCH_FIFOTHRESHOLD_MAX - len);
+               ndelay(1);
+       }
+
+       /* disable and stop the PFPW engine */
+       gpmc_prefetch_reset();
+}
+#endif /* CONFIG_SYS_GPMC_PREFETCH_ENABLE */
+
 /*
  * omap_nand_hwcontrol - Set the address pointers corretly for the
  *                     following address/data/command operation
@@ -31,6 +234,8 @@ static void omap_nand_hwcontrol(struct mtd_info *mtd, int32_t cmd,
                                uint32_t ctrl)
 {
        register struct nand_chip *this = mtd->priv;
+       struct omap_nand_info *info = this->priv;
+       int cs = info->cs;
 
        /*
         * Point the IO_ADDR to DATA and ADDRESS registers instead
@@ -52,28 +257,10 @@ static void omap_nand_hwcontrol(struct mtd_info *mtd, int32_t cmd,
                writeb(cmd, this->IO_ADDR_W);
 }
 
-#ifdef CONFIG_SPL_BUILD
 /* Check wait pin as dev ready indicator */
-int omap_spl_dev_ready(struct mtd_info *mtd)
-{
-       return gpmc_cfg->status & (1 << 8);
-}
-#endif
-
-/*
- * omap_hwecc_init - Initialize the Hardware ECC for NAND flash in
- *                   GPMC controller
- * @mtd:        MTD device structure
- *
- */
-static void __maybe_unused omap_hwecc_init(struct nand_chip *chip)
+static int omap_dev_ready(struct mtd_info *mtd)
 {
-       /*
-        * Init ECC Control Register
-        * Clear all ECC | Enable Reg1
-        */
-       writel(ECCCLEAR | ECCRESULTREG1, &gpmc_cfg->ecc_control);
-       writel(ECCSIZE1 | ECCSIZE0 | ECCSIZE0SEL, &gpmc_cfg->ecc_size_config);
+       return readl(&gpmc_cfg->status) & (1 << 8);
 }
 
 /*
@@ -156,363 +343,191 @@ static int __maybe_unused omap_correct_data(struct mtd_info *mtd, uint8_t *dat,
 }
 
 /*
- *  omap_calculate_ecc - Generate non-inverted ECC bytes.
- *
- *  Using noninverted ECC can be considered ugly since writing a blank
- *  page ie. padding will clear the ECC bytes. This is no problem as
- *  long nobody is trying to write data on the seemingly unused page.
- *  Reading an erased page will produce an ECC mismatch between
- *  generated and read ECC bytes that has to be dealt with separately.
- *  E.g. if page is 0xFF (fresh erased), and if HW ECC engine within GPMC
- *  is used, the result of read will be 0x0 while the ECC offsets of the
- *  spare area will be 0xFF which will result in an ECC mismatch.
- *  @mtd:      MTD structure
- *  @dat:      unused
- *  @ecc_code: ecc_code buffer
- */
-static int __maybe_unused omap_calculate_ecc(struct mtd_info *mtd,
-               const uint8_t *dat, uint8_t *ecc_code)
-{
-       u_int32_t val;
-
-       /* Start Reading from HW ECC1_Result = 0x200 */
-       val = readl(&gpmc_cfg->ecc1_result);
-
-       ecc_code[0] = val & 0xFF;
-       ecc_code[1] = (val >> 16) & 0xFF;
-       ecc_code[2] = ((val >> 8) & 0x0F) | ((val >> 20) & 0xF0);
-
-       /*
-        * Stop reading anymore ECC vals and clear old results
-        * enable will be called if more reads are required
-        */
-       writel(0x000, &gpmc_cfg->ecc_config);
-
-       return 0;
-}
-
-/*
- * omap_enable_ecc - This function enables the hardware ecc functionality
- * @mtd:        MTD device structure
- * @mode:       Read/Write mode
- */
-static void __maybe_unused omap_enable_hwecc(struct mtd_info *mtd, int32_t mode)
-{
-       struct nand_chip *chip = mtd->priv;
-       uint32_t val, dev_width = (chip->options & NAND_BUSWIDTH_16) >> 1;
-
-       switch (mode) {
-       case NAND_ECC_READ:
-       case NAND_ECC_WRITE:
-               /* Clear the ecc result registers, select ecc reg as 1 */
-               writel(ECCCLEAR | ECCRESULTREG1, &gpmc_cfg->ecc_control);
-
-               /*
-                * Size 0 = 0xFF, Size1 is 0xFF - both are 512 bytes
-                * tell all regs to generate size0 sized regs
-                * we just have a single ECC engine for all CS
-                */
-               writel(ECCSIZE1 | ECCSIZE0 | ECCSIZE0SEL,
-                       &gpmc_cfg->ecc_size_config);
-               val = (dev_width << 7) | (cs << 1) | (0x1);
-               writel(val, &gpmc_cfg->ecc_config);
-               break;
-       default:
-               printf("Error: Unrecognized Mode[%d]!\n", mode);
-               break;
-       }
-}
-
-/*
- * Generic BCH interface
- */
-struct nand_bch_priv {
-       uint8_t mode;
-       uint8_t type;
-       uint8_t nibbles;
-       struct bch_control *control;
-       enum omap_ecc ecc_scheme;
-};
-
-/* bch types */
-#define ECC_BCH4       0
-#define ECC_BCH8       1
-#define ECC_BCH16      2
-
-/* GPMC ecc engine settings */
-#define BCH_WRAPMODE_1         1       /* BCH wrap mode 1 */
-#define BCH_WRAPMODE_6         6       /* BCH wrap mode 6 */
-
-/* BCH nibbles for diff bch levels */
-#define NAND_ECC_HW_BCH ((uint8_t)(NAND_ECC_HW_OOB_FIRST) + 1)
-#define ECC_BCH4_NIBBLES       13
-#define ECC_BCH8_NIBBLES       26
-#define ECC_BCH16_NIBBLES      52
-
-/*
- * This can be a single instance cause all current users have only one NAND
- * with nearly the same setup (BCH8, some with ELM and others with sw BCH
- * library).
- * When some users with other BCH strength will exists this have to change!
- */
-static __maybe_unused struct nand_bch_priv bch_priv = {
-       .mode = NAND_ECC_HW_BCH,
-       .type = ECC_BCH8,
-       .nibbles = ECC_BCH8_NIBBLES,
-       .control = NULL
-};
-
-/*
- * omap_hwecc_init_bch - Initialize the BCH Hardware ECC for NAND flash in
- *                             GPMC controller
+ * omap_enable_hwecc - configures GPMC as per ECC scheme before read/write
  * @mtd:       MTD device structure
  * @mode:      Read/Write mode
  */
 __maybe_unused
-static void omap_hwecc_init_bch(struct nand_chip *chip, int32_t mode)
+static void omap_enable_hwecc(struct mtd_info *mtd, int32_t mode)
 {
-       uint32_t val;
-       uint32_t dev_width = (chip->options & NAND_BUSWIDTH_16) >> 1;
-       uint32_t unused_length = 0;
-       uint32_t wr_mode = BCH_WRAPMODE_6;
-       struct nand_bch_priv *bch = chip->priv;
-
-       /* Clear the ecc result registers, select ecc reg as 1 */
-       writel(ECCCLEAR | ECCRESULTREG1, &gpmc_cfg->ecc_control);
-
-       if (bch->ecc_scheme == OMAP_ECC_BCH8_CODE_HW) {
-               wr_mode = BCH_WRAPMODE_1;
-
-               switch (bch->nibbles) {
-               case ECC_BCH4_NIBBLES:
-                       unused_length = 3;
-                       break;
-               case ECC_BCH8_NIBBLES:
-                       unused_length = 2;
-                       break;
-               case ECC_BCH16_NIBBLES:
-                       unused_length = 0;
-                       break;
+       struct nand_chip        *nand   = mtd->priv;
+       struct omap_nand_info   *info   = nand->priv;
+       unsigned int dev_width = (nand->options & NAND_BUSWIDTH_16) ? 1 : 0;
+       unsigned int ecc_algo = 0;
+       unsigned int bch_type = 0;
+       unsigned int eccsize1 = 0x00, eccsize0 = 0x00, bch_wrapmode = 0x00;
+       u32 ecc_size_config_val = 0;
+       u32 ecc_config_val = 0;
+       int cs = info->cs;
+
+       /* configure GPMC for specific ecc-scheme */
+       switch (info->ecc_scheme) {
+       case OMAP_ECC_HAM1_CODE_SW:
+               return;
+       case OMAP_ECC_HAM1_CODE_HW:
+               ecc_algo = 0x0;
+               bch_type = 0x0;
+               bch_wrapmode = 0x00;
+               eccsize0 = 0xFF;
+               eccsize1 = 0xFF;
+               break;
+       case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW:
+       case OMAP_ECC_BCH8_CODE_HW:
+               ecc_algo = 0x1;
+               bch_type = 0x1;
+               if (mode == NAND_ECC_WRITE) {
+                       bch_wrapmode = 0x01;
+                       eccsize0 = 0;  /* extra bits in nibbles per sector */
+                       eccsize1 = 28; /* OOB bits in nibbles per sector */
+               } else {
+                       bch_wrapmode = 0x01;
+                       eccsize0 = 26; /* ECC bits in nibbles per sector */
+                       eccsize1 = 2;  /* non-ECC bits in nibbles per sector */
                }
-
-               /*
-                * This is ecc_size_config for ELM mode.  Here we are using
-                * different settings for read and write access and also
-                * depending on BCH strength.
-                */
-               switch (mode) {
-               case NAND_ECC_WRITE:
-                       /* write access only setup eccsize1 config */
-                       val = ((unused_length + bch->nibbles) << 22);
-                       break;
-
-               case NAND_ECC_READ:
-               default:
-                       /*
-                        * by default eccsize0 selected for ecc1resultsize
-                        * eccsize0 config.
-                        */
-                       val  = (bch->nibbles << 12);
-                       /* eccsize1 config */
-                       val |= (unused_length << 22);
-                       break;
+               break;
+       case OMAP_ECC_BCH16_CODE_HW:
+               ecc_algo = 0x1;
+               bch_type = 0x2;
+               if (mode == NAND_ECC_WRITE) {
+                       bch_wrapmode = 0x01;
+                       eccsize0 = 0;  /* extra bits in nibbles per sector */
+                       eccsize1 = 52; /* OOB bits in nibbles per sector */
+               } else {
+                       bch_wrapmode = 0x01;
+                       eccsize0 = 52; /* ECC bits in nibbles per sector */
+                       eccsize1 = 0;  /* non-ECC bits in nibbles per sector */
                }
-       } else {
-               /*
-                * This ecc_size_config setting is for BCH sw library.
-                *
-                * Note: we only support BCH8 currently with BCH sw library!
-                * Should be really easy to adobt to BCH4, however some omap3
-                * have flaws with BCH4.
-                *
-                * 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)
-                */
-               val = (32 << 22) | (0 << 12);
+               break;
+       default:
+               return;
        }
-       /* ecc size configuration */
-       writel(val, &gpmc_cfg->ecc_size_config);
-
-       /*
-        * Configure the ecc engine in gpmc
-        * We assume 512 Byte sector pages for access to NAND.
-        */
-       val  = (1 << 16);               /* enable BCH mode */
-       val |= (bch->type << 12);       /* setup BCH type */
-       val |= (wr_mode << 8);          /* setup wrapping mode */
-       val |= (dev_width << 7);        /* setup device width (16 or 8 bit) */
-       val |= (cs << 1);               /* setup chip select to work on */
-       debug("set ECC_CONFIG=0x%08x\n", val);
-       writel(val, &gpmc_cfg->ecc_config);
-}
-
-/*
- * omap_enable_ecc_bch - This function enables the bch h/w ecc functionality
- * @mtd:       MTD device structure
- * @mode:      Read/Write mode
- */
-__maybe_unused
-static void omap_enable_ecc_bch(struct mtd_info *mtd, int32_t mode)
-{
-       struct nand_chip *chip = mtd->priv;
-
-       omap_hwecc_init_bch(chip, mode);
-       /* enable ecc */
-       writel((readl(&gpmc_cfg->ecc_config) | 0x1), &gpmc_cfg->ecc_config);
-}
-
-/*
- * omap_ecc_disable - Disable H/W ECC calculation
- *
- * @mtd:       MTD device structure
- */
-static void __maybe_unused omap_ecc_disable(struct mtd_info *mtd)
-{
-       writel((readl(&gpmc_cfg->ecc_config) & ~0x1), &gpmc_cfg->ecc_config);
+       /* Clear ecc and enable bits */
+       writel(ECCCLEAR | ECCRESULTREG1, &gpmc_cfg->ecc_control);
+       /* Configure ecc size for BCH */
+       ecc_size_config_val = (eccsize1 << 22) | (eccsize0 << 12);
+       writel(ecc_size_config_val, &gpmc_cfg->ecc_size_config);
+
+       /* Configure device details for BCH engine */
+       ecc_config_val = ((ecc_algo << 16)      | /* HAM1 | BCHx */
+                       (bch_type << 12)        | /* BCH4/BCH8/BCH16 */
+                       (bch_wrapmode << 8)     | /* wrap mode */
+                       (dev_width << 7)        | /* bus width */
+                       (0x0 << 4)              | /* number of sectors */
+                       (cs <<  1)              | /* ECC CS */
+                       (0x1));                   /* enable ECC */
+       writel(ecc_config_val, &gpmc_cfg->ecc_config);
 }
 
 /*
- * BCH support using ELM module
- */
-#ifdef CONFIG_NAND_OMAP_ELM
-/*
- * omap_read_bch8_result - Read BCH result for BCH8 level
- *
- * @mtd:       MTD device structure
- * @big_endian:        When set read register 3 first
- * @ecc_code:  Read syndrome from BCH result registers
+ *  omap_calculate_ecc - Read ECC result
+ *  @mtd:      MTD structure
+ *  @dat:      unused
+ *  @ecc_code: ecc_code buffer
+ *  Using noninverted ECC can be considered ugly since writing a blank
+ *  page ie. padding will clear the ECC bytes. This is no problem as
+ *  long nobody is trying to write data on the seemingly unused page.
+ *  Reading an erased page will produce an ECC mismatch between
+ *  generated and read ECC bytes that has to be dealt with separately.
+ *  E.g. if page is 0xFF (fresh erased), and if HW ECC engine within GPMC
+ *  is used, the result of read will be 0x0 while the ECC offsets of the
+ *  spare area will be 0xFF which will result in an ECC mismatch.
  */
-static void omap_read_bch8_result(struct mtd_info *mtd, uint8_t big_endian,
+static int omap_calculate_ecc(struct mtd_info *mtd, const uint8_t *dat,
                                uint8_t *ecc_code)
 {
-       uint32_t *ptr;
+       struct nand_chip *chip = mtd->priv;
+       struct omap_nand_info *info = chip->priv;
+       uint32_t *ptr, val = 0;
        int8_t i = 0, j;
 
-       if (big_endian) {
+       switch (info->ecc_scheme) {
+       case OMAP_ECC_HAM1_CODE_HW:
+               val = readl(&gpmc_cfg->ecc1_result);
+               ecc_code[0] = val & 0xFF;
+               ecc_code[1] = (val >> 16) & 0xFF;
+               ecc_code[2] = ((val >> 8) & 0x0F) | ((val >> 20) & 0xF0);
+               break;
+#ifdef CONFIG_BCH
+       case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW:
+#endif
+       case OMAP_ECC_BCH8_CODE_HW:
                ptr = &gpmc_cfg->bch_result_0_3[0].bch_result_x[3];
-               ecc_code[i++] = readl(ptr) & 0xFF;
+               val = readl(ptr);
+               ecc_code[i++] = (val >>  0) & 0xFF;
                ptr--;
                for (j = 0; j < 3; j++) {
-                       ecc_code[i++] = (readl(ptr) >> 24) & 0xFF;
-                       ecc_code[i++] = (readl(ptr) >> 16) & 0xFF;
-                       ecc_code[i++] = (readl(ptr) >>  8) & 0xFF;
-                       ecc_code[i++] = readl(ptr) & 0xFF;
+                       val = readl(ptr);
+                       ecc_code[i++] = (val >> 24) & 0xFF;
+                       ecc_code[i++] = (val >> 16) & 0xFF;
+                       ecc_code[i++] = (val >>  8) & 0xFF;
+                       ecc_code[i++] = (val >>  0) & 0xFF;
                        ptr--;
                }
-       } else {
-               ptr = &gpmc_cfg->bch_result_0_3[0].bch_result_x[0];
-               for (j = 0; j < 3; j++) {
-                       ecc_code[i++] = readl(ptr) & 0xFF;
-                       ecc_code[i++] = (readl(ptr) >>  8) & 0xFF;
-                       ecc_code[i++] = (readl(ptr) >> 16) & 0xFF;
-                       ecc_code[i++] = (readl(ptr) >> 24) & 0xFF;
-                       ptr++;
+               break;
+       case OMAP_ECC_BCH16_CODE_HW:
+               val = readl(&gpmc_cfg->bch_result_4_6[0].bch_result_x[2]);
+               ecc_code[i++] = (val >>  8) & 0xFF;
+               ecc_code[i++] = (val >>  0) & 0xFF;
+               val = readl(&gpmc_cfg->bch_result_4_6[0].bch_result_x[1]);
+               ecc_code[i++] = (val >> 24) & 0xFF;
+               ecc_code[i++] = (val >> 16) & 0xFF;
+               ecc_code[i++] = (val >>  8) & 0xFF;
+               ecc_code[i++] = (val >>  0) & 0xFF;
+               val = readl(&gpmc_cfg->bch_result_4_6[0].bch_result_x[0]);
+               ecc_code[i++] = (val >> 24) & 0xFF;
+               ecc_code[i++] = (val >> 16) & 0xFF;
+               ecc_code[i++] = (val >>  8) & 0xFF;
+               ecc_code[i++] = (val >>  0) & 0xFF;
+               for (j = 3; j >= 0; j--) {
+                       val = readl(&gpmc_cfg->bch_result_0_3[0].bch_result_x[j]
+                                                                       );
+                       ecc_code[i++] = (val >> 24) & 0xFF;
+                       ecc_code[i++] = (val >> 16) & 0xFF;
+                       ecc_code[i++] = (val >>  8) & 0xFF;
+                       ecc_code[i++] = (val >>  0) & 0xFF;
                }
-               ecc_code[i++] = readl(ptr) & 0xFF;
-               ecc_code[i++] = 0;      /* 14th byte is always zero */
+               break;
+       default:
+               return -EINVAL;
        }
-}
-
-/*
- * omap_rotate_ecc_bch - Rotate the syndrome bytes
- *
- * @mtd:       MTD device structure
- * @calc_ecc:  ECC read from ECC registers
- * @syndrome:  Rotated syndrome will be retuned in this array
- *
- */
-static void omap_rotate_ecc_bch(struct mtd_info *mtd, uint8_t *calc_ecc,
-               uint8_t *syndrome)
-{
-       struct nand_chip *chip = mtd->priv;
-       struct nand_bch_priv *bch = chip->priv;
-       uint8_t n_bytes = 0;
-       int8_t i, j;
-
-       switch (bch->type) {
-       case ECC_BCH4:
-               n_bytes = 8;
+       /* ECC scheme specific syndrome customizations */
+       switch (info->ecc_scheme) {
+       case OMAP_ECC_HAM1_CODE_HW:
                break;
+#ifdef CONFIG_BCH
+       case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW:
 
-       case ECC_BCH16:
-               n_bytes = 28;
+               for (i = 0; i < chip->ecc.bytes; i++)
+                       *(ecc_code + i) = *(ecc_code + i) ^
+                                               bch8_polynomial[i];
                break;
-
-       case ECC_BCH8:
-       default:
-               n_bytes = 13;
+#endif
+       case OMAP_ECC_BCH8_CODE_HW:
+               ecc_code[chip->ecc.bytes - 1] = 0x00;
+               break;
+       case OMAP_ECC_BCH16_CODE_HW:
                break;
+       default:
+               return -EINVAL;
        }
-
-       for (i = 0, j = (n_bytes-1); i < n_bytes; i++, j--)
-               syndrome[i] =  calc_ecc[j];
-}
-
-/*
- *  omap_calculate_ecc_bch - Read BCH ECC result
- *
- *  @mtd:      MTD structure
- *  @dat:      unused
- *  @ecc_code: ecc_code buffer
- */
-static int omap_calculate_ecc_bch(struct mtd_info *mtd, const uint8_t *dat,
-                               uint8_t *ecc_code)
-{
-       struct nand_chip *chip = mtd->priv;
-       struct nand_bch_priv *bch = chip->priv;
-       uint8_t big_endian = 1;
-       int8_t ret = 0;
-
-       if (bch->type == ECC_BCH8)
-               omap_read_bch8_result(mtd, big_endian, ecc_code);
-       else /* BCH4 and BCH16 currently not supported */
-               ret = -1;
-
-       /*
-        * Stop reading anymore ECC vals and clear old results
-        * enable will be called if more reads are required
-        */
-       omap_ecc_disable(mtd);
-
-       return ret;
+       return 0;
 }
 
+#ifdef CONFIG_NAND_OMAP_ELM
 /*
- * omap_fix_errors_bch - Correct bch error in the data
- *
- * @mtd:       MTD device structure
- * @data:      Data read from flash
- * @error_count:Number of errors in data
- * @error_loc: Locations of errors in the data
- *
- */
-static void omap_fix_errors_bch(struct mtd_info *mtd, uint8_t *data,
-               uint32_t error_count, uint32_t *error_loc)
+ * omap_reverse_list - re-orders list elements in reverse order [internal]
+ * @list:      pointer to start of list
+ * @length:    length of list
+*/
+static void omap_reverse_list(u8 *list, unsigned int length)
 {
-       struct nand_chip *chip = mtd->priv;
-       struct nand_bch_priv *bch = chip->priv;
-       uint8_t count = 0;
-       uint32_t error_byte_pos;
-       uint32_t error_bit_mask;
-       uint32_t last_bit = (bch->nibbles * 4) - 1;
-
-       /* Flip all bits as specified by the error location array. */
-       /* FOR( each found error location flip the bit ) */
-       for (count = 0; count < error_count; count++) {
-               if (error_loc[count] > last_bit) {
-                       /* Remove the ECC spare bits from correction. */
-                       error_loc[count] -= (last_bit + 1);
-                       /* Offset bit in data region */
-                       error_byte_pos = ((512 * 8) -
-                                       (error_loc[count]) - 1) / 8;
-                       /* Error Bit mask */
-                       error_bit_mask = 0x1 << (error_loc[count] % 8);
-                       /* Toggle the error bit to make the correction. */
-                       data[error_byte_pos] ^= error_bit_mask;
-               }
+       unsigned int i, j;
+       unsigned int half_length = length / 2;
+       u8 tmp;
+       for (i = 0, j = length - 1; i < half_length; i++, j--) {
+               tmp = list[i];
+               list[i] = list[j];
+               list[j] = tmp;
        }
 }
 
@@ -531,41 +546,83 @@ static int omap_correct_data_bch(struct mtd_info *mtd, uint8_t *dat,
                                uint8_t *read_ecc, uint8_t *calc_ecc)
 {
        struct nand_chip *chip = mtd->priv;
-       struct nand_bch_priv *bch = chip->priv;
-       uint8_t syndrome[28];
-       uint32_t error_count = 0;
-       uint32_t error_loc[8];
-       uint32_t i, ecc_flag;
+       struct omap_nand_info *info = chip->priv;
+       struct nand_ecc_ctrl *ecc = &chip->ecc;
+       uint32_t error_count = 0, error_max;
+       uint32_t error_loc[ELM_MAX_ERROR_COUNT];
+       enum bch_level bch_type;
+       uint32_t i, ecc_flag = 0;
+       uint8_t count;
+       uint32_t byte_pos, bit_pos;
+       int err = 0;
+
+       /* check calculated ecc */
+       for (i = 0; i < ecc->bytes && !ecc_flag; i++) {
+               if (calc_ecc[i] != 0x00)
+                       ecc_flag = 1;
+       }
+       if (!ecc_flag)
+               return 0;
 
+       /* check for whether its a erased-page */
        ecc_flag = 0;
-       for (i = 0; i < chip->ecc.bytes; i++)
+       for (i = 0; i < ecc->bytes && !ecc_flag; i++) {
                if (read_ecc[i] != 0xff)
                        ecc_flag = 1;
-
+       }
        if (!ecc_flag)
                return 0;
 
-       elm_reset();
-       elm_config((enum bch_level)(bch->type));
-
        /*
         * while reading ECC result we read it in big endian.
         * Hence while loading to ELM we have rotate to get the right endian.
         */
-       omap_rotate_ecc_bch(mtd, calc_ecc, syndrome);
-
-       /* use elm module to check for errors */
-       if (elm_check_error(syndrome, bch->nibbles, &error_count,
-                               error_loc) != 0) {
-               printf("ECC: uncorrectable.\n");
-               return -1;
+       switch (info->ecc_scheme) {
+       case OMAP_ECC_BCH8_CODE_HW:
+               bch_type = BCH_8_BIT;
+               omap_reverse_list(calc_ecc, ecc->bytes - 1);
+               break;
+       case OMAP_ECC_BCH16_CODE_HW:
+               bch_type = BCH_16_BIT;
+               omap_reverse_list(calc_ecc, ecc->bytes);
+               break;
+       default:
+               return -EINVAL;
        }
+       /* use elm module to check for errors */
+       elm_config(bch_type);
+       err = elm_check_error(calc_ecc, bch_type, &error_count, error_loc);
+       if (err)
+               return err;
 
        /* correct bch error */
-       if (error_count > 0)
-               omap_fix_errors_bch(mtd, dat, error_count, error_loc);
-
-       return 0;
+       for (count = 0; count < error_count; count++) {
+               switch (info->ecc_scheme) {
+               case OMAP_ECC_BCH8_CODE_HW:
+                       /* 14th byte in ECC is reserved to match ROM layout */
+                       error_max = SECTOR_BYTES + (ecc->bytes - 1);
+                       break;
+               case OMAP_ECC_BCH16_CODE_HW:
+                       error_max = SECTOR_BYTES + ecc->bytes;
+                       break;
+               default:
+                       return -EINVAL;
+               }
+               byte_pos = error_max - (error_loc[count] / 8) - 1;
+               bit_pos  = error_loc[count] % 8;
+               if (byte_pos < SECTOR_BYTES) {
+                       dat[byte_pos] ^= 1 << bit_pos;
+                       printf("nand: bit-flip corrected @data=%d\n", byte_pos);
+               } else if (byte_pos < error_max) {
+                       read_ecc[byte_pos - SECTOR_BYTES] ^= 1 << bit_pos;
+                       printf("nand: bit-flip corrected @oob=%d\n", byte_pos -
+                                                               SECTOR_BYTES);
+               } else {
+                       err = -EBADMSG;
+                       printf("nand: error: invalid bit-flip location\n");
+               }
+       }
+       return (err) ? err : error_count;
 }
 
 /**
@@ -587,24 +644,23 @@ static int omap_read_page_bch(struct mtd_info *mtd, struct nand_chip *chip,
        uint8_t *ecc_calc = chip->buffers->ecccalc;
        uint8_t *ecc_code = chip->buffers->ecccode;
        uint32_t *eccpos = chip->ecc.layout->eccpos;
-       uint8_t *oob = chip->oob_poi;
+       uint8_t *oob = &chip->oob_poi[eccpos[0]];
        uint32_t data_pos;
        uint32_t oob_pos;
 
        data_pos = 0;
        /* oob area start */
-       oob_pos = (eccsize * eccsteps) + chip->ecc.layout->eccpos[0];
-       oob += chip->ecc.layout->eccpos[0];
+       oob_pos = (eccsize * eccsteps) + eccpos[0];
 
        for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize,
                                oob += eccbytes) {
                chip->ecc.hwctl(mtd, NAND_ECC_READ);
                /* read data */
-               chip->cmdfunc(mtd, NAND_CMD_RNDOUT, data_pos, page);
+               chip->cmdfunc(mtd, NAND_CMD_RNDOUT, data_pos, -1);
                chip->read_buf(mtd, p, eccsize);
 
                /* read respective ecc from oob area */
-               chip->cmdfunc(mtd, NAND_CMD_RNDOUT, oob_pos, page);
+               chip->cmdfunc(mtd, NAND_CMD_RNDOUT, oob_pos, -1);
                chip->read_buf(mtd, oob, eccbytes);
                /* read syndrome */
                chip->ecc.calculate(mtd, p, &ecc_calc[i]);
@@ -636,57 +692,6 @@ static int omap_read_page_bch(struct mtd_info *mtd, struct nand_chip *chip,
  * OMAP3 BCH8 support (with BCH library)
  */
 #ifdef CONFIG_BCH
-/*
- *  omap_calculate_ecc_bch_sw - Read BCH ECC result
- *
- *  @mtd:      MTD device structure
- *  @dat:      The pointer to data on which ecc is computed (unused here)
- *  @ecc:      The ECC output buffer
- */
-static int omap_calculate_ecc_bch_sw(struct mtd_info *mtd, const uint8_t *dat,
-                               uint8_t *ecc)
-{
-       int ret = 0;
-       size_t i;
-       unsigned long nsectors, val1, val2, val3, val4;
-
-       nsectors = ((readl(&gpmc_cfg->ecc_config) >> 4) & 0x7) + 1;
-
-       for (i = 0; i < nsectors; i++) {
-               /* Read hw-computed remainder */
-               val1 = readl(&gpmc_cfg->bch_result_0_3[i].bch_result_x[0]);
-               val2 = readl(&gpmc_cfg->bch_result_0_3[i].bch_result_x[1]);
-               val3 = readl(&gpmc_cfg->bch_result_0_3[i].bch_result_x[2]);
-               val4 = readl(&gpmc_cfg->bch_result_0_3[i].bch_result_x[3]);
-
-               /*
-                * Add constant polynomial to remainder, in order to get an ecc
-                * sequence of 0xFFs for a buffer filled with 0xFFs.
-                */
-               *ecc++ = 0xef ^ (val4 & 0xFF);
-               *ecc++ = 0x51 ^ ((val3 >> 24) & 0xFF);
-               *ecc++ = 0x2e ^ ((val3 >> 16) & 0xFF);
-               *ecc++ = 0x09 ^ ((val3 >> 8) & 0xFF);
-               *ecc++ = 0xed ^ (val3 & 0xFF);
-               *ecc++ = 0x93 ^ ((val2 >> 24) & 0xFF);
-               *ecc++ = 0x9a ^ ((val2 >> 16) & 0xFF);
-               *ecc++ = 0xc2 ^ ((val2 >> 8) & 0xFF);
-               *ecc++ = 0x97 ^ (val2 & 0xFF);
-               *ecc++ = 0x79 ^ ((val1 >> 24) & 0xFF);
-               *ecc++ = 0xe5 ^ ((val1 >> 16) & 0xFF);
-               *ecc++ = 0x24 ^ ((val1 >> 8) & 0xFF);
-               *ecc++ = 0xb5 ^ (val1 & 0xFF);
-       }
-
-       /*
-        * Stop reading anymore ECC vals and clear old results
-        * enable will be called if more reads are required
-        */
-       omap_ecc_disable(mtd);
-
-       return ret;
-}
-
 /**
  * omap_correct_data_bch_sw - Decode received data and correct errors
  * @mtd: MTD device structure
@@ -701,10 +706,10 @@ static int omap_correct_data_bch_sw(struct mtd_info *mtd, u_char *data,
        /* cannot correct more than 8 errors */
        unsigned int errloc[8];
        struct nand_chip *chip = mtd->priv;
-       struct nand_bch_priv *chip_priv = chip->priv;
-       struct bch_control *bch = chip_priv->control;
+       struct omap_nand_info *info = chip->priv;
 
-       count = decode_bch(bch, NULL, 512, read_ecc, calc_ecc, NULL, errloc);
+       count = decode_bch(info->control, NULL, 512, read_ecc, calc_ecc,
+                                                       NULL, errloc);
        if (count > 0) {
                /* correct errors */
                for (i = 0; i < count; i++) {
@@ -713,22 +718,22 @@ static int omap_correct_data_bch_sw(struct mtd_info *mtd, u_char *data,
                                data[errloc[i]/8] ^= 1 << (errloc[i] & 7);
                        printf("corrected bitflip %u\n", errloc[i]);
 #ifdef DEBUG
-                       puts("read_ecc: ");
+                       printf("read_ecc: ");
                        /*
                         * BCH8 have 13 bytes of ECC; BCH4 needs adoption
                         * here!
                         */
                        for (i = 0; i < 13; i++)
                                printf("%02x ", read_ecc[i]);
-                       puts("\n");
-                       puts("calc_ecc: ");
+                       printf("\n");
+                       printf("calc_ecc: ");
                        for (i = 0; i < 13; i++)
                                printf("%02x ", calc_ecc[i]);
-                       puts("\n");
+                       printf("\n");
 #endif
                }
        } else if (count < 0) {
-               puts("ecc unrecoverable error\n");
+               printf("ecc unrecoverable error\n");
        }
        return count;
 }
@@ -740,15 +745,11 @@ static int omap_correct_data_bch_sw(struct mtd_info *mtd, u_char *data,
 static void __maybe_unused omap_free_bch(struct mtd_info *mtd)
 {
        struct nand_chip *chip = mtd->priv;
-       struct nand_bch_priv *chip_priv = chip->priv;
-       struct bch_control *bch = NULL;
-
-       if (chip_priv)
-               bch = chip_priv->control;
+       struct omap_nand_info *info = chip->priv;
 
-       if (bch) {
-               free_bch(bch);
-               chip_priv->control = NULL;
+       if (info->control) {
+               free_bch(info->control);
+               info->control = NULL;
        }
 }
 #endif /* CONFIG_BCH */
@@ -762,7 +763,7 @@ static void __maybe_unused omap_free_bch(struct mtd_info *mtd)
  */
 static int omap_select_ecc_scheme(struct nand_chip *nand,
        enum omap_ecc ecc_scheme, unsigned int pagesize, unsigned int oobsize) {
-       struct nand_bch_priv    *bch            = nand->priv;
+       struct omap_nand_info   *info           = nand->priv;
        struct nand_ecclayout   *ecclayout      = &omap_ecclayout;
        int eccsteps = pagesize / SECTOR_BYTES;
        int i;
@@ -772,12 +773,10 @@ static int omap_select_ecc_scheme(struct nand_chip *nand,
                debug("nand: selected OMAP_ECC_HAM1_CODE_SW\n");
                /* For this ecc-scheme, ecc.bytes, ecc.layout, ... are
                 * initialized in nand_scan_tail(), so just set ecc.mode */
-               bch_priv.control        = NULL;
-               bch_priv.type           = 0;
+               info->control           = NULL;
                nand->ecc.mode          = NAND_ECC_SOFT;
                nand->ecc.layout        = NULL;
                nand->ecc.size          = 0;
-               bch->ecc_scheme         = OMAP_ECC_HAM1_CODE_SW;
                break;
 
        case OMAP_ECC_HAM1_CODE_HW:
@@ -788,8 +787,7 @@ static int omap_select_ecc_scheme(struct nand_chip *nand,
                                (3 * eccsteps) + BADBLOCK_MARKER_LENGTH));
                        return -EINVAL;
                }
-               bch_priv.control        = NULL;
-               bch_priv.type           = 0;
+               info->control           = NULL;
                /* populate ecc specific fields */
                memset(&nand->ecc, 0, sizeof(struct nand_ecc_ctrl));
                nand->ecc.mode          = NAND_ECC_HW;
@@ -810,7 +808,6 @@ static int omap_select_ecc_scheme(struct nand_chip *nand,
                ecclayout->oobfree[0].offset = i + BADBLOCK_MARKER_LENGTH;
                ecclayout->oobfree[0].length = oobsize - ecclayout->eccbytes -
                                                BADBLOCK_MARKER_LENGTH;
-               bch->ecc_scheme         = OMAP_ECC_HAM1_CODE_HW;
                break;
 
        case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW:
@@ -823,21 +820,20 @@ static int omap_select_ecc_scheme(struct nand_chip *nand,
                        return -EINVAL;
                }
                /* check if BCH S/W library can be used for error detection */
-               bch_priv.control = init_bch(13, 8, 0x201b);
-               if (!bch_priv.control) {
+               info->control = init_bch(13, 8, 0x201b);
+               if (!info->control) {
                        printf("nand: error: could not init_bch()\n");
                        return -ENODEV;
                }
-               bch_priv.type = ECC_BCH8;
                /* populate ecc specific fields */
                memset(&nand->ecc, 0, sizeof(struct nand_ecc_ctrl));
                nand->ecc.mode          = NAND_ECC_HW;
                nand->ecc.strength      = 8;
                nand->ecc.size          = SECTOR_BYTES;
                nand->ecc.bytes         = 13;
-               nand->ecc.hwctl         = omap_enable_ecc_bch;
+               nand->ecc.hwctl         = omap_enable_hwecc;
                nand->ecc.correct       = omap_correct_data_bch_sw;
-               nand->ecc.calculate     = omap_calculate_ecc_bch_sw;
+               nand->ecc.calculate     = omap_calculate_ecc;
                /* define ecc-layout */
                ecclayout->eccbytes     = nand->ecc.bytes * eccsteps;
                ecclayout->eccpos[0]    = BADBLOCK_MARKER_LENGTH;
@@ -852,8 +848,6 @@ static int omap_select_ecc_scheme(struct nand_chip *nand,
                ecclayout->oobfree[0].offset = i + BADBLOCK_MARKER_LENGTH;
                ecclayout->oobfree[0].length = oobsize - ecclayout->eccbytes -
                                                BADBLOCK_MARKER_LENGTH;
-               omap_hwecc_init_bch(nand, NAND_ECC_READ);
-               bch->ecc_scheme         = OMAP_ECC_BCH8_CODE_HW_DETECTION_SW;
                break;
 #else
                printf("nand: error: CONFIG_BCH required for ECC\n");
@@ -871,16 +865,16 @@ static int omap_select_ecc_scheme(struct nand_chip *nand,
                }
                /* intialize ELM for ECC error detection */
                elm_init();
-               bch_priv.type           = ECC_BCH8;
+               info->control           = NULL;
                /* populate ecc specific fields */
                memset(&nand->ecc, 0, sizeof(struct nand_ecc_ctrl));
                nand->ecc.mode          = NAND_ECC_HW;
                nand->ecc.strength      = 8;
                nand->ecc.size          = SECTOR_BYTES;
                nand->ecc.bytes         = 14;
-               nand->ecc.hwctl         = omap_enable_ecc_bch;
+               nand->ecc.hwctl         = omap_enable_hwecc;
                nand->ecc.correct       = omap_correct_data_bch;
-               nand->ecc.calculate     = omap_calculate_ecc_bch;
+               nand->ecc.calculate     = omap_calculate_ecc;
                nand->ecc.read_page     = omap_read_page_bch;
                /* define ecc-layout */
                ecclayout->eccbytes     = nand->ecc.bytes * eccsteps;
@@ -889,13 +883,44 @@ static int omap_select_ecc_scheme(struct nand_chip *nand,
                ecclayout->oobfree[0].offset = i + BADBLOCK_MARKER_LENGTH;
                ecclayout->oobfree[0].length = oobsize - ecclayout->eccbytes -
                                                BADBLOCK_MARKER_LENGTH;
-               bch->ecc_scheme         = OMAP_ECC_BCH8_CODE_HW;
                break;
 #else
                printf("nand: error: CONFIG_NAND_OMAP_ELM required for ECC\n");
                return -EINVAL;
 #endif
 
+       case OMAP_ECC_BCH16_CODE_HW:
+#ifdef CONFIG_NAND_OMAP_ELM
+               debug("nand: using OMAP_ECC_BCH16_CODE_HW\n");
+               /* check ecc-scheme requirements before updating ecc info */
+               if ((26 * eccsteps) + BADBLOCK_MARKER_LENGTH > oobsize) {
+                       printf("nand: error: insufficient OOB: require=%d\n", (
+                               (26 * eccsteps) + BADBLOCK_MARKER_LENGTH));
+                       return -EINVAL;
+               }
+               /* intialize ELM for ECC error detection */
+               elm_init();
+               /* populate ecc specific fields */
+               nand->ecc.mode          = NAND_ECC_HW;
+               nand->ecc.size          = SECTOR_BYTES;
+               nand->ecc.bytes         = 26;
+               nand->ecc.strength      = 16;
+               nand->ecc.hwctl         = omap_enable_hwecc;
+               nand->ecc.correct       = omap_correct_data_bch;
+               nand->ecc.calculate     = omap_calculate_ecc;
+               nand->ecc.read_page     = omap_read_page_bch;
+               /* define ecc-layout */
+               ecclayout->eccbytes     = nand->ecc.bytes * eccsteps;
+               for (i = 0; i < ecclayout->eccbytes; i++)
+                       ecclayout->eccpos[i] = i + BADBLOCK_MARKER_LENGTH;
+               ecclayout->oobfree[0].offset = i + BADBLOCK_MARKER_LENGTH;
+               ecclayout->oobfree[0].length = oobsize - nand->ecc.bytes -
+                                               BADBLOCK_MARKER_LENGTH;
+               break;
+#else
+               printf("nand: error: CONFIG_NAND_OMAP_ELM required for ECC\n");
+               return -EINVAL;
+#endif
        default:
                debug("nand: error: ecc scheme not enabled or supported\n");
                return -EINVAL;
@@ -905,6 +930,7 @@ static int omap_select_ecc_scheme(struct nand_chip *nand,
        if (ecc_scheme != OMAP_ECC_HAM1_CODE_SW)
                nand->ecc.layout = ecclayout;
 
+       info->ecc_scheme = ecc_scheme;
        return 0;
 }
 
@@ -933,6 +959,7 @@ int __maybe_unused omap_nand_switch_ecc(uint32_t hardware, uint32_t eccstrength)
        mtd = &nand_info[nand_curr_device];
        nand = mtd->priv;
        nand->options |= NAND_OWN_BUFFERS;
+       nand->options &= ~NAND_SUBPAGE_READ;
        /* Setup the ecc configurations again */
        if (hardware) {
                if (eccstrength == 1) {
@@ -977,7 +1004,7 @@ int __maybe_unused omap_nand_switch_ecc(uint32_t hardware, uint32_t eccstrength)
 int board_nand_init(struct nand_chip *nand)
 {
        int32_t gpmc_config = 0;
-       cs = 0;
+       int cs = cs_next++;
        int err = 0;
        /*
         * xloader/Uboot's gpmc configuration would have configured GPMC for
@@ -1007,16 +1034,23 @@ int board_nand_init(struct nand_chip *nand)
 
        nand->IO_ADDR_R = (void __iomem *)&gpmc_cfg->cs[cs].nand_dat;
        nand->IO_ADDR_W = (void __iomem *)&gpmc_cfg->cs[cs].nand_cmd;
-       nand->priv      = &bch_priv;
+       omap_nand_info[cs].control = NULL;
+       omap_nand_info[cs].cs = cs;
+       nand->priv      = &omap_nand_info[cs];
        nand->cmd_ctrl  = omap_nand_hwcontrol;
        nand->options   |= NAND_NO_PADDING | NAND_CACHEPRG;
-       /* If we are 16 bit dev, our gpmc config tells us that */
-       if ((readl(&gpmc_cfg->cs[cs].config1) & 0x3000) == 0x1000)
-               nand->options |= NAND_BUSWIDTH_16;
-
        nand->chip_delay = 100;
        nand->ecc.layout = &omap_ecclayout;
 
+       /* configure driver and controller based on NAND device bus-width */
+       gpmc_config = readl(&gpmc_cfg->cs[cs].config1);
+#if defined(CONFIG_SYS_NAND_BUSWIDTH_16BIT)
+       nand->options |= NAND_BUSWIDTH_16;
+       writel(gpmc_config | (0x1 << 12), &gpmc_cfg->cs[cs].config1);
+#else
+       nand->options &= ~NAND_BUSWIDTH_16;
+       writel(gpmc_config & ~(0x1 << 12), &gpmc_cfg->cs[cs].config1);
+#endif
        /* select ECC scheme */
 #if defined(CONFIG_NAND_OMAP_ECCSCHEME)
        err = omap_select_ecc_scheme(nand, CONFIG_NAND_OMAP_ECCSCHEME,
@@ -1028,14 +1062,30 @@ int board_nand_init(struct nand_chip *nand)
 #endif
        if (err)
                return err;
+#ifdef CONFIG_SYS_NAND_USE_FLASH_BBT
+       if (nand->ecc.layout) {
+               bbt_main_descr.offs = nand->ecc.layout->oobfree[0].offset;
+               bbt_main_descr.veroffs = bbt_main_descr.offs +
+                       sizeof(bbt_pattern);
+
+               bbt_mirror_descr.offs = nand->ecc.layout->oobfree[0].offset;
+               bbt_mirror_descr.veroffs = bbt_mirror_descr.offs +
+                       sizeof(mirror_pattern);
+       }
+
+       nand->bbt_options |= NAND_BBT_USE_FLASH;
+       nand->bbt_td = &bbt_main_descr;
+       nand->bbt_md = &bbt_mirror_descr;
+#endif
 
 #ifdef CONFIG_SPL_BUILD
        if (nand->options & NAND_BUSWIDTH_16)
                nand->read_buf = nand_read_buf16;
        else
                nand->read_buf = nand_read_buf;
-       nand->dev_ready = omap_spl_dev_ready;
 #endif
 
+       nand->dev_ready = omap_dev_ready;
+
        return 0;
 }