chip->ecc.correct() is used for detecting and correcting bit-flips during read
operations. In omap2-nand driver this is done usingt following functions:

- omap_correct_data(): for H/W based HAM1_ECC schemes
        (Un-Touched in current patch)

- omap_elm_correct_data(): for H/W based BCHx_ECC scheme
        Current implementation of this function is not scalable for newer ECC
        schemes because:
        - It depends on a specific byte-position in OOB area (reserved as 0x00)
          to differentiates between programmed-pages and erased-pages.
          This reserved byte-position cannot be accomodated in all ECC schemes.
        - Current code is not scalable for future ECC schemes due to tweaks for
          BCH4_ECC and BCH8_ECC at multiple places.
        - It checks for bit-flips in Erased-pages using check_erased_page().
          This is over-work, as sanity of Erased-page can be verified by just
          comparing them to a pre-defined ECC-syndrome for all_0xFF data.

        This patch optimizes omap_elm_correct_data() in following ways:
        (1) Removes dependency on specific reserved-byte (0x00) in OOB area,
            instead Erased-page is identified by matching calc_ecc with a
            pre-defined ECC syndrome of all(0xFF) data
        (2) merges common code for BCH4_ECC and BCH8_ECC for scalability.
        (3) handles incorrect elm_error_location beyond data+oob buffer.
        (4) removes check_erased_page(): Bit-flips in erased-page are handled
            in same way as for programmed-page

Signed-off-by: Pekon Gupta <[email protected]>
---
 drivers/mtd/nand/omap2.c | 249 ++++++++++++++---------------------------------
 1 file changed, 75 insertions(+), 174 deletions(-)

diff --git a/drivers/mtd/nand/omap2.c b/drivers/mtd/nand/omap2.c
index e9ef743..5321bdcc 100644
--- a/drivers/mtd/nand/omap2.c
+++ b/drivers/mtd/nand/omap2.c
@@ -176,6 +176,7 @@ struct omap_nand_info {
        int                             gpmc_cs;
        unsigned long                   phys_base;
        unsigned long                   mem_size;
+       enum omap_ecc                   ecc_opt;
        struct completion               comp;
        struct dma_chan                 *dma;
        int                             gpmc_irq_fifo;
@@ -1307,219 +1308,118 @@ static int omap3_calculate_ecc_bch(struct mtd_info 
*mtd, const u_char *dat,
 }
 
 /**
- * erased_sector_bitflips - count bit flips
- * @data:      data sector buffer
- * @oob:       oob buffer
- * @info:      omap_nand_info
- *
- * Check the bit flips in erased page falls below correctable level.
- * If falls below, report the page as erased with correctable bit
- * flip, else report as uncorrectable page.
- */
-static int erased_sector_bitflips(u_char *data, u_char *oob,
-               struct omap_nand_info *info)
-{
-       int flip_bits = 0, i;
-
-       for (i = 0; i < info->nand.ecc.size; i++) {
-               flip_bits += hweight8(~data[i]);
-               if (flip_bits > info->nand.ecc.strength)
-                       return 0;
-       }
-
-       for (i = 0; i < info->nand.ecc.bytes - 1; i++) {
-               flip_bits += hweight8(~oob[i]);
-               if (flip_bits > info->nand.ecc.strength)
-                       return 0;
-       }
-
-       /*
-        * Bit flips falls in correctable level.
-        * Fill data area with 0xFF
-        */
-       if (flip_bits) {
-               memset(data, 0xFF, info->nand.ecc.size);
-               memset(oob, 0xFF, info->nand.ecc.bytes);
-       }
-
-       return flip_bits;
-}
-
-/**
  * omap_elm_correct_data - corrects page data area in case error reported
  * @mtd:       MTD device structure
  * @data:      page data
  * @read_ecc:  ecc read from nand flash
- * @calc_ecc:  ecc read from HW ECC registers
- *
- * Calculated ecc vector reported as zero in case of non-error pages.
- * In case of error/erased pages non-zero error vector is reported.
- * In case of non-zero ecc vector, check read_ecc at fixed offset
- * (x = 13/7 in case of BCH8/4 == 0) to find page programmed or not.
- * To handle bit flips in this data, count the number of 0's in
- * read_ecc[x] and check if it greater than 4. If it is less, it is
- * programmed page, else erased page.
- *
- * 1. If page is erased, check with standard ecc vector (ecc vector
- * for erased page to find any bit flip). If check fails, bit flip
- * is present in erased page. Count the bit flips in erased page and
- * if it falls under correctable level, report page with 0xFF and
- * update the correctable bit information.
- * 2. If error is reported on programmed page, update elm error
- * vector and correct the page with ELM error correction routine.
- *
+ * @calc_ecc:  ecc calculated after reading Data and OOB regions from flash
+ * As calc_ecc is calculated over both main & oob, so calc_ecc would be
+ * non-zero only in following cases:
+ * - bit-flips in data or oob region
+ * - erase page, where no ECC is written in OOB area
+ *   However, erased_pages can be differentiated from corrupted pages
+ *   by comparing the calculated ECC with pre-defined syndrome ECC_of_ALL(0xFF)
+ *   Bit-flips in erased-pages would also be caught by comparing, calc_ecc
+ *   with ECC_of_ALL(0xFF)
  */
 static int omap_elm_correct_data(struct mtd_info *mtd, u_char *data,
                                u_char *read_ecc, u_char *calc_ecc)
 {
        struct omap_nand_info *info = container_of(mtd, struct omap_nand_info,
                        mtd);
-       int eccsteps = info->nand.ecc.steps;
-       int i , j, stat = 0;
-       int eccsize, eccflag, ecc_vector_size;
+       enum omap_ecc ecc_opt = info->ecc_opt;
+       struct nand_chip *chip = mtd->priv;
+       int eccsteps = chip->ecc.steps;
+       int eccsize  = chip->ecc.size;
+       int eccbytes = chip->ecc.bytes;
+       int i , j, stat = 0, ret = 0, flag_read_ecc;
        struct elm_errorvec err_vec[ERROR_VECTOR_MAX];
-       u_char *ecc_vec = calc_ecc;
-       u_char *spare_ecc = read_ecc;
-       u_char *erased_ecc_vec;
-       enum bch_ecc type;
+       u_char *ecc;
        bool is_error_reported = false;
+       u32 bit_pos, byte_pos, error_max, pos;
 
        /* Initialize elm error vector to zero */
        memset(err_vec, 0, sizeof(err_vec));
 
-       if (info->nand.ecc.strength == BCH8_MAX_ERROR) {
-               type = BCH8_ECC;
-               erased_ecc_vec = bch8_vector;
-       } else {
-               type = BCH4_ECC;
-               erased_ecc_vec = bch4_vector;
-       }
-
-       ecc_vector_size = info->nand.ecc.bytes;
-
-       /*
-        * Remove extra byte padding for BCH8 RBL
-        * compatibility and erased page handling
-        */
-       eccsize = ecc_vector_size - 1;
-
        for (i = 0; i < eccsteps ; i++) {
-               eccflag = 0;    /* initialize eccflag */
-
-               /*
-                * Check any error reported,
-                * In case of error, non zero ecc reported.
-                */
-
-               for (j = 0; (j < eccsize); j++) {
-                       if (calc_ecc[j] != 0) {
-                               eccflag = 1; /* non zero ecc, error present */
+               flag_read_ecc = 0;
+               ecc = calc_ecc + (i * eccbytes);
+               /* check calc_ecc */
+               for (j = 0; j < eccbytes; j++) {
+                       if (*(ecc + j) != 0x00) {
+                               flag_read_ecc = 1;
                                break;
                        }
                }
-
-               if (eccflag == 1) {
-                       /*
-                        * Set threshold to minimum of 4, half of ecc.strength/2
-                        * to allow max bit flip in byte to 4
-                        */
-                       unsigned int threshold = min_t(unsigned int, 4,
-                                       info->nand.ecc.strength / 2);
-
-                       /*
-                        * Check data area is programmed by counting
-                        * number of 0's at fixed offset in spare area.
-                        * Checking count of 0's against threshold.
-                        * In case programmed page expects at least threshold
-                        * zeros in byte.
-                        * If zeros are less than threshold for programmed page/
-                        * zeros are more than threshold erased page, either
-                        * case page reported as uncorrectable.
-                        */
-                       if (hweight8(~read_ecc[eccsize]) >= threshold) {
-                               /*
-                                * Update elm error vector as
-                                * data area is programmed
-                                */
-                               err_vec[i].error_reported = true;
-                               is_error_reported = true;
-                       } else {
-                               /* Error reported in erased page */
-                               int bitflip_count;
-                               u_char *buf = &data[info->nand.ecc.size * i];
-
-                               if (memcmp(calc_ecc, erased_ecc_vec, eccsize)) {
-                                       bitflip_count = erased_sector_bitflips(
-                                                       buf, read_ecc, info);
-
-                                       if (bitflip_count)
-                                               stat += bitflip_count;
-                                       else
-                                               return -EINVAL;
-                               }
+               /* check if its a erased-page */
+               if (flag_read_ecc) {
+                       switch (ecc_opt) {
+                       case OMAP_ECC_BCH8_CODE_HW:
+                               if (memcmp(ecc, bch8_vector, eccbytes))
+                                       err_vec[i].error_reported = true;
+                               break;
+                       case OMAP_ECC_BCH4_CODE_HW:
+                               if (memcmp(ecc, bch4_vector, eccbytes))
+                                       err_vec[i].error_reported = true;
+                               break;
+                       default:
+                               pr_err("%s: invalid configuration",
+                                                                DRIVER_NAME);
+                               return -EINVAL;
                        }
                }
-
-               /* Update the ecc vector */
-               calc_ecc += ecc_vector_size;
-               read_ecc += ecc_vector_size;
+               /* page definitely has bit-flips */
+               if (err_vec[i].error_reported)
+                       is_error_reported = true;
        }
 
-       /* Check if any error reported */
        if (!is_error_reported)
                return 0;
+       /* detect bit-flips using ELM module */
+       elm_decode_bch_error_page(info->elm_dev, calc_ecc, err_vec);
 
-       /* Decode BCH error using ELM module */
-       elm_decode_bch_error_page(info->elm_dev, ecc_vec, err_vec);
-
+       /* correct bit-flip */
        for (i = 0; i < eccsteps; i++) {
-               if (err_vec[i].error_reported) {
+               if (err_vec[i].error_uncorrectable) {
+                       ret = -EBADMSG;
+               } else if (err_vec[i].error_reported) {
                        for (j = 0; j < err_vec[i].error_count; j++) {
-                               u32 bit_pos, byte_pos, error_max, pos;
-
-                               if (type == BCH8_ECC)
-                                       error_max = BCH8_ECC_MAX;
-                               else
-                                       error_max = BCH4_ECC_MAX;
-
-                               if (info->nand.ecc.strength == BCH8_MAX_ERROR)
-                                       pos = err_vec[i].error_loc[j];
-                               else
+                               switch (ecc_opt) {
+                               case OMAP_ECC_BCH4_CODE_HW:
+                                       error_max = SECTOR_BYTES +
+                                                       (eccbytes - 1);
                                        /* Add 4 to take care 4 bit padding */
                                        pos = err_vec[i].error_loc[j] +
-                                               BCH4_BIT_PAD;
-
-                               /* Calculate bit position of error */
+                                                       BCH4_BIT_PAD;
+                                       break;
+                               case OMAP_ECC_BCH8_CODE_HW:
+                                       error_max = SECTOR_BYTES +
+                                                       (eccbytes - 1);
+                                       pos = err_vec[i].error_loc[j];
+                                       break;
+                               default:
+                                       return -EINVAL;
+                               }
+                               /* Calculate bit & byte bit-flip position */
                                bit_pos = pos % 8;
-
-                               /* Calculate byte position of error */
-                               byte_pos = (error_max - pos - 1) / 8;
-
-                               if (pos < error_max) {
-                                       if (byte_pos < 512)
-                                               data[byte_pos] ^= 1 << bit_pos;
-                                       else
-                                               spare_ecc[byte_pos - 512] ^=
+                               byte_pos = error_max - (pos / 8) - 1;
+                               if (byte_pos < SECTOR_BYTES)
+                                       data[byte_pos] ^= 1 << bit_pos;
+                               else if (byte_pos < error_max)
+                                       read_ecc[byte_pos - SECTOR_BYTES] ^=
                                                        1 << bit_pos;
-                               }
-                               /* else, not interested to correct ecc */
+                               else
+                                       ret = -EBADMSG;
                        }
                }
-
                /* Update number of correctable errors */
                stat += err_vec[i].error_count;
-
                /* Update page data with sector size */
-               data += info->nand.ecc.size;
-               spare_ecc += ecc_vector_size;
+               data     += eccsize;
+               read_ecc += eccbytes;
        }
 
-       for (i = 0; i < eccsteps; i++)
-               /* Return error if uncorrectable error present */
-               if (err_vec[i].error_uncorrectable)
-                       return -EINVAL;
-
-       return stat;
+       return (ret < 0) ? ret : stat;
 }
 
 /**
@@ -1677,13 +1577,14 @@ static int omap_nand_probe(struct platform_device *pdev)
        info->pdev              = pdev;
        info->gpmc_cs           = pdata->cs;
        info->reg               = pdata->reg;
+       info->ecc_opt           = pdata->ecc_opt;
 
        info->nand.options      = NAND_BUSWIDTH_AUTO;
        info->nand.options      |= NAND_SKIP_BBTSCAN;
+       info->nand.ecc.priv     = NULL;
 #ifdef CONFIG_MTD_NAND_OMAP_BCH
        info->of_node           = pdata->of_node;
 #endif
-       info->nand.ecc.priv                     = NULL;
 
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        if (res == NULL) {
-- 
1.8.1

--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to