nand, gpmc: fix reading after switching ecc
[platform/kernel/u-boot.git] / drivers / mtd / nand / omap_gpmc.c
index e6b289d..389c4de 100644 (file)
@@ -283,53 +283,55 @@ static void omap_hwecc_init_bch(struct nand_chip *chip, int32_t mode)
        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;
-       }
-
-       /*
-        * 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;
+               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;
+               }
 
-       case NAND_ECC_READ:
-       default:
                /*
-                * by default eccsize0 selected for ecc1resultsize
-                * eccsize0 config.
+                * 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.
                 */
-               val  = (bch->nibbles << 12);
-               /* eccsize1 config */
-               val |= (unused_length << 22);
-               break;
-       }
+               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;
+               }
        } 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);
+               /*
+                * 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);
        }
        /* ecc size configuration */
        writel(val, &gpmc_cfg->ecc_size_config);
@@ -761,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 nand_ecclayout   *ecclayout      = nand->ecc.layout;
+       struct nand_ecclayout   *ecclayout      = &omap_ecclayout;
        int eccsteps = pagesize / SECTOR_BYTES;
        int i;
 
@@ -774,7 +776,7 @@ static int omap_select_ecc_scheme(struct nand_chip *nand,
                bch_priv.type           = 0;
                nand->ecc.mode          = NAND_ECC_SOFT;
                nand->ecc.layout        = NULL;
-               nand->ecc.size          = pagesize;
+               nand->ecc.size          = 0;
                bch->ecc_scheme         = OMAP_ECC_HAM1_CODE_SW;
                break;
 
@@ -789,6 +791,7 @@ static int omap_select_ecc_scheme(struct nand_chip *nand,
                bch_priv.control        = NULL;
                bch_priv.type           = 0;
                /* populate ecc specific fields */
+               memset(&nand->ecc, 0, sizeof(struct nand_ecc_ctrl));
                nand->ecc.mode          = NAND_ECC_HW;
                nand->ecc.strength      = 1;
                nand->ecc.size          = SECTOR_BYTES;
@@ -798,8 +801,12 @@ static int omap_select_ecc_scheme(struct nand_chip *nand,
                nand->ecc.calculate     = omap_calculate_ecc;
                /* define ecc-layout */
                ecclayout->eccbytes     = nand->ecc.bytes * eccsteps;
-               for (i = 0; i < ecclayout->eccbytes; i++)
-                       ecclayout->eccpos[i] = i + BADBLOCK_MARKER_LENGTH;
+               for (i = 0; i < ecclayout->eccbytes; i++) {
+                       if (nand->options & NAND_BUSWIDTH_16)
+                               ecclayout->eccpos[i] = i + 2;
+                       else
+                               ecclayout->eccpos[i] = i + 1;
+               }
                ecclayout->oobfree[0].offset = i + BADBLOCK_MARKER_LENGTH;
                ecclayout->oobfree[0].length = oobsize - ecclayout->eccbytes -
                                                BADBLOCK_MARKER_LENGTH;
@@ -823,6 +830,7 @@ static int omap_select_ecc_scheme(struct nand_chip *nand,
                }
                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;
@@ -865,6 +873,7 @@ static int omap_select_ecc_scheme(struct nand_chip *nand,
                elm_init();
                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;
@@ -891,6 +900,11 @@ static int omap_select_ecc_scheme(struct nand_chip *nand,
                debug("nand: error: ecc scheme not enabled or supported\n");
                return -EINVAL;
        }
+
+       /* nand_scan_tail() sets ham1 sw ecc; hw ecc layout is set by driver */
+       if (ecc_scheme != OMAP_ECC_HAM1_CODE_SW)
+               nand->ecc.layout = ecclayout;
+
        return 0;
 }
 
@@ -919,6 +933,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) {
@@ -1004,18 +1019,13 @@ int board_nand_init(struct nand_chip *nand)
        nand->ecc.layout = &omap_ecclayout;
 
        /* select ECC scheme */
-#if defined(CONFIG_NAND_OMAP_ELM)
-       err = omap_select_ecc_scheme(nand, OMAP_ECC_BCH8_CODE_HW,
+#if defined(CONFIG_NAND_OMAP_ECCSCHEME)
+       err = omap_select_ecc_scheme(nand, CONFIG_NAND_OMAP_ECCSCHEME,
                        CONFIG_SYS_NAND_PAGE_SIZE, CONFIG_SYS_NAND_OOBSIZE);
-#elif defined(CONFIG_NAND_OMAP_BCH8)
-       err = omap_select_ecc_scheme(nand, OMAP_ECC_BCH8_CODE_HW_DETECTION_SW,
-                       CONFIG_SYS_NAND_PAGE_SIZE, CONFIG_SYS_NAND_OOBSIZE);
-#elif !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_NAND_SOFTECC)
+#else
+       /* pagesize and oobsize are not required to configure sw ecc-scheme */
        err = omap_select_ecc_scheme(nand, OMAP_ECC_HAM1_CODE_SW,
                        0, 0);
-#else
-       err = omap_select_ecc_scheme(nand, OMAP_ECC_HAM1_CODE_HW,
-                       CONFIG_SYS_NAND_PAGE_SIZE, CONFIG_SYS_NAND_OOBSIZE);
 #endif
        if (err)
                return err;