Merge tag 'for-linus' of git://git.kernel.org/pub/scm/virt/kvm/kvm
[linux-2.6-microblaze.git] / drivers / mtd / nand / raw / omap2.c
index c75e7a0..b1839ee 100644 (file)
 #define BCH_ECC_SIZE0          0x0     /* ecc_size0 = 0, no oob protection */
 #define BCH_ECC_SIZE1          0x20    /* ecc_size1 = 32 */
 
-#define BADBLOCK_MARKER_LENGTH         2
+#define BBM_LEN                        2
 
 static u_char bch16_vector[] = {0xf5, 0x24, 0x1c, 0xd0, 0x61, 0xb3, 0xf1, 0x55,
                                0x2e, 0x2c, 0x86, 0xa3, 0xed, 0x36, 0x1b, 0x78,
@@ -171,6 +171,10 @@ struct omap_nand_info {
        struct device                   *elm_dev;
        /* NAND ready gpio */
        struct gpio_desc                *ready_gpiod;
+       unsigned int                    neccpg;
+       unsigned int                    nsteps_per_eccpg;
+       unsigned int                    eccpg_size;
+       unsigned int                    eccpg_bytes;
 };
 
 static inline struct omap_nand_info *mtd_to_omap(struct mtd_info *mtd)
@@ -1355,7 +1359,7 @@ static int omap_elm_correct_data(struct nand_chip *chip, u_char *data,
 {
        struct omap_nand_info *info = mtd_to_omap(nand_to_mtd(chip));
        struct nand_ecc_ctrl *ecc = &info->nand.ecc;
-       int eccsteps = info->nand.ecc.steps;
+       int eccsteps = info->nsteps_per_eccpg;
        int i , j, stat = 0;
        int eccflag, actual_eccbytes;
        struct elm_errorvec err_vec[ERROR_VECTOR_MAX];
@@ -1525,24 +1529,37 @@ static int omap_write_page_bch(struct nand_chip *chip, const uint8_t *buf,
                               int oob_required, int page)
 {
        struct mtd_info *mtd = nand_to_mtd(chip);
-       int ret;
+       struct omap_nand_info *info = mtd_to_omap(mtd);
        uint8_t *ecc_calc = chip->ecc.calc_buf;
+       unsigned int eccpg;
+       int ret;
 
-       nand_prog_page_begin_op(chip, page, 0, NULL, 0);
+       ret = nand_prog_page_begin_op(chip, page, 0, NULL, 0);
+       if (ret)
+               return ret;
 
-       /* Enable GPMC ecc engine */
-       chip->ecc.hwctl(chip, NAND_ECC_WRITE);
+       for (eccpg = 0; eccpg < info->neccpg; eccpg++) {
+               /* Enable GPMC ecc engine */
+               chip->ecc.hwctl(chip, NAND_ECC_WRITE);
 
-       /* Write data */
-       chip->legacy.write_buf(chip, buf, mtd->writesize);
+               /* Write data */
+               chip->legacy.write_buf(chip, buf + (eccpg * info->eccpg_size),
+                                      info->eccpg_size);
 
-       /* Update ecc vector from GPMC result registers */
-       omap_calculate_ecc_bch_multi(mtd, buf, &ecc_calc[0]);
+               /* Update ecc vector from GPMC result registers */
+               ret = omap_calculate_ecc_bch_multi(mtd,
+                                                  buf + (eccpg * info->eccpg_size),
+                                                  ecc_calc);
+               if (ret)
+                       return ret;
 
-       ret = mtd_ooblayout_set_eccbytes(mtd, ecc_calc, chip->oob_poi, 0,
-                                        chip->ecc.total);
-       if (ret)
-               return ret;
+               ret = mtd_ooblayout_set_eccbytes(mtd, ecc_calc,
+                                                chip->oob_poi,
+                                                eccpg * info->eccpg_bytes,
+                                                info->eccpg_bytes);
+               if (ret)
+                       return ret;
+       }
 
        /* Write ecc vector to OOB area */
        chip->legacy.write_buf(chip, chip->oob_poi, mtd->oobsize);
@@ -1566,12 +1583,13 @@ static int omap_write_subpage_bch(struct nand_chip *chip, u32 offset,
                                  int oob_required, int page)
 {
        struct mtd_info *mtd = nand_to_mtd(chip);
+       struct omap_nand_info *info = mtd_to_omap(mtd);
        u8 *ecc_calc = chip->ecc.calc_buf;
        int ecc_size      = chip->ecc.size;
        int ecc_bytes     = chip->ecc.bytes;
-       int ecc_steps     = chip->ecc.steps;
        u32 start_step = offset / ecc_size;
        u32 end_step   = (offset + data_len - 1) / ecc_size;
+       unsigned int eccpg;
        int step, ret = 0;
 
        /*
@@ -1580,36 +1598,48 @@ static int omap_write_subpage_bch(struct nand_chip *chip, u32 offset,
         * ECC is calculated for all subpages but we choose
         * only what we want.
         */
-       nand_prog_page_begin_op(chip, page, 0, NULL, 0);
-
-       /* Enable GPMC ECC engine */
-       chip->ecc.hwctl(chip, NAND_ECC_WRITE);
-
-       /* Write data */
-       chip->legacy.write_buf(chip, buf, mtd->writesize);
+       ret = nand_prog_page_begin_op(chip, page, 0, NULL, 0);
+       if (ret)
+               return ret;
 
-       for (step = 0; step < ecc_steps; step++) {
-               /* mask ECC of un-touched subpages by padding 0xFF */
-               if (step < start_step || step > end_step)
-                       memset(ecc_calc, 0xff, ecc_bytes);
-               else
-                       ret = _omap_calculate_ecc_bch(mtd, buf, ecc_calc, step);
+       for (eccpg = 0; eccpg < info->neccpg; eccpg++) {
+               /* Enable GPMC ECC engine */
+               chip->ecc.hwctl(chip, NAND_ECC_WRITE);
+
+               /* Write data */
+               chip->legacy.write_buf(chip, buf + (eccpg * info->eccpg_size),
+                                      info->eccpg_size);
+
+               for (step = 0; step < info->nsteps_per_eccpg; step++) {
+                       unsigned int base_step = eccpg * info->nsteps_per_eccpg;
+                       const u8 *bufoffs = buf + (eccpg * info->eccpg_size);
+
+                       /* Mask ECC of un-touched subpages with 0xFFs */
+                       if ((step + base_step) < start_step ||
+                           (step + base_step) > end_step)
+                               memset(ecc_calc + (step * ecc_bytes), 0xff,
+                                      ecc_bytes);
+                       else
+                               ret = _omap_calculate_ecc_bch(mtd,
+                                                             bufoffs + (step * ecc_size),
+                                                             ecc_calc + (step * ecc_bytes),
+                                                             step);
+
+                       if (ret)
+                               return ret;
+               }
 
+               /*
+                * Copy the calculated ECC for the whole page including the
+                * masked values (0xFF) corresponding to unwritten subpages.
+                */
+               ret = mtd_ooblayout_set_eccbytes(mtd, ecc_calc, chip->oob_poi,
+                                                eccpg * info->eccpg_bytes,
+                                                info->eccpg_bytes);
                if (ret)
                        return ret;
-
-               buf += ecc_size;
-               ecc_calc += ecc_bytes;
        }
 
-       /* copy calculated ECC for whole page to chip->buffer->oob */
-       /* this include masked-value(0xFF) for unwritten subpages */
-       ecc_calc = chip->ecc.calc_buf;
-       ret = mtd_ooblayout_set_eccbytes(mtd, ecc_calc, chip->oob_poi, 0,
-                                        chip->ecc.total);
-       if (ret)
-               return ret;
-
        /* write OOB buffer to NAND device */
        chip->legacy.write_buf(chip, chip->oob_poi, mtd->oobsize);
 
@@ -1634,40 +1664,60 @@ static int omap_read_page_bch(struct nand_chip *chip, uint8_t *buf,
                              int oob_required, int page)
 {
        struct mtd_info *mtd = nand_to_mtd(chip);
+       struct omap_nand_info *info = mtd_to_omap(mtd);
        uint8_t *ecc_calc = chip->ecc.calc_buf;
        uint8_t *ecc_code = chip->ecc.code_buf;
+       unsigned int max_bitflips = 0, eccpg;
        int stat, ret;
-       unsigned int max_bitflips = 0;
-
-       nand_read_page_op(chip, page, 0, NULL, 0);
 
-       /* Enable GPMC ecc engine */
-       chip->ecc.hwctl(chip, NAND_ECC_READ);
+       ret = nand_read_page_op(chip, page, 0, NULL, 0);
+       if (ret)
+               return ret;
 
-       /* Read data */
-       chip->legacy.read_buf(chip, buf, mtd->writesize);
+       for (eccpg = 0; eccpg < info->neccpg; eccpg++) {
+               /* Enable GPMC ecc engine */
+               chip->ecc.hwctl(chip, NAND_ECC_READ);
 
-       /* Read oob bytes */
-       nand_change_read_column_op(chip,
-                                  mtd->writesize + BADBLOCK_MARKER_LENGTH,
-                                  chip->oob_poi + BADBLOCK_MARKER_LENGTH,
-                                  chip->ecc.total, false);
+               /* Read data */
+               ret = nand_change_read_column_op(chip, eccpg * info->eccpg_size,
+                                                buf + (eccpg * info->eccpg_size),
+                                                info->eccpg_size, false);
+               if (ret)
+                       return ret;
 
-       /* Calculate ecc bytes */
-       omap_calculate_ecc_bch_multi(mtd, buf, ecc_calc);
+               /* Read oob bytes */
+               ret = nand_change_read_column_op(chip,
+                                                mtd->writesize + BBM_LEN +
+                                                (eccpg * info->eccpg_bytes),
+                                                chip->oob_poi + BBM_LEN +
+                                                (eccpg * info->eccpg_bytes),
+                                                info->eccpg_bytes, false);
+               if (ret)
+                       return ret;
 
-       ret = mtd_ooblayout_get_eccbytes(mtd, ecc_code, chip->oob_poi, 0,
-                                        chip->ecc.total);
-       if (ret)
-               return ret;
+               /* Calculate ecc bytes */
+               ret = omap_calculate_ecc_bch_multi(mtd,
+                                                  buf + (eccpg * info->eccpg_size),
+                                                  ecc_calc);
+               if (ret)
+                       return ret;
 
-       stat = chip->ecc.correct(chip, buf, ecc_code, ecc_calc);
+               ret = mtd_ooblayout_get_eccbytes(mtd, ecc_code,
+                                                chip->oob_poi,
+                                                eccpg * info->eccpg_bytes,
+                                                info->eccpg_bytes);
+               if (ret)
+                       return ret;
 
-       if (stat < 0) {
-               mtd->ecc_stats.failed++;
-       } else {
-               mtd->ecc_stats.corrected += stat;
-               max_bitflips = max_t(unsigned int, max_bitflips, stat);
+               stat = chip->ecc.correct(chip,
+                                        buf + (eccpg * info->eccpg_size),
+                                        ecc_code, ecc_calc);
+               if (stat < 0) {
+                       mtd->ecc_stats.failed++;
+               } else {
+                       mtd->ecc_stats.corrected += stat;
+                       max_bitflips = max_t(unsigned int, max_bitflips, stat);
+               }
        }
 
        return max_bitflips;
@@ -1820,7 +1870,7 @@ static int omap_ooblayout_ecc(struct mtd_info *mtd, int section,
 {
        struct omap_nand_info *info = mtd_to_omap(mtd);
        struct nand_chip *chip = &info->nand;
-       int off = BADBLOCK_MARKER_LENGTH;
+       int off = BBM_LEN;
 
        if (info->ecc_opt == OMAP_ECC_HAM1_CODE_HW &&
            !(chip->options & NAND_BUSWIDTH_16))
@@ -1840,7 +1890,7 @@ static int omap_ooblayout_free(struct mtd_info *mtd, int section,
 {
        struct omap_nand_info *info = mtd_to_omap(mtd);
        struct nand_chip *chip = &info->nand;
-       int off = BADBLOCK_MARKER_LENGTH;
+       int off = BBM_LEN;
 
        if (info->ecc_opt == OMAP_ECC_HAM1_CODE_HW &&
            !(chip->options & NAND_BUSWIDTH_16))
@@ -1870,7 +1920,7 @@ static int omap_sw_ooblayout_ecc(struct mtd_info *mtd, int section,
        struct nand_device *nand = mtd_to_nanddev(mtd);
        unsigned int nsteps = nanddev_get_ecc_nsteps(nand);
        unsigned int ecc_bytes = nanddev_get_ecc_bytes_per_step(nand);
-       int off = BADBLOCK_MARKER_LENGTH;
+       int off = BBM_LEN;
 
        if (section >= nsteps)
                return -ERANGE;
@@ -1891,7 +1941,7 @@ static int omap_sw_ooblayout_free(struct mtd_info *mtd, int section,
        struct nand_device *nand = mtd_to_nanddev(mtd);
        unsigned int nsteps = nanddev_get_ecc_nsteps(nand);
        unsigned int ecc_bytes = nanddev_get_ecc_bytes_per_step(nand);
-       int off = BADBLOCK_MARKER_LENGTH;
+       int off = BBM_LEN;
 
        if (section)
                return -ERANGE;
@@ -1920,7 +1970,8 @@ static int omap_nand_attach_chip(struct nand_chip *chip)
        struct mtd_info *mtd = nand_to_mtd(chip);
        struct omap_nand_info *info = mtd_to_omap(mtd);
        struct device *dev = &info->pdev->dev;
-       int min_oobbytes = BADBLOCK_MARKER_LENGTH;
+       int min_oobbytes = BBM_LEN;
+       int elm_bch_strength = -1;
        int oobbytes_per_step;
        dma_cap_mask_t mask;
        int err;
@@ -2074,12 +2125,7 @@ static int omap_nand_attach_chip(struct nand_chip *chip)
                chip->ecc.write_subpage = omap_write_subpage_bch;
                mtd_set_ooblayout(mtd, &omap_ooblayout_ops);
                oobbytes_per_step       = chip->ecc.bytes;
-
-               err = elm_config(info->elm_dev, BCH4_ECC,
-                                mtd->writesize / chip->ecc.size,
-                                chip->ecc.size, chip->ecc.bytes);
-               if (err < 0)
-                       return err;
+               elm_bch_strength = BCH4_ECC;
                break;
 
        case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW:
@@ -2116,13 +2162,7 @@ static int omap_nand_attach_chip(struct nand_chip *chip)
                chip->ecc.write_subpage = omap_write_subpage_bch;
                mtd_set_ooblayout(mtd, &omap_ooblayout_ops);
                oobbytes_per_step       = chip->ecc.bytes;
-
-               err = elm_config(info->elm_dev, BCH8_ECC,
-                                mtd->writesize / chip->ecc.size,
-                                chip->ecc.size, chip->ecc.bytes);
-               if (err < 0)
-                       return err;
-
+               elm_bch_strength = BCH8_ECC;
                break;
 
        case OMAP_ECC_BCH16_CODE_HW:
@@ -2138,19 +2178,32 @@ static int omap_nand_attach_chip(struct nand_chip *chip)
                chip->ecc.write_subpage = omap_write_subpage_bch;
                mtd_set_ooblayout(mtd, &omap_ooblayout_ops);
                oobbytes_per_step       = chip->ecc.bytes;
-
-               err = elm_config(info->elm_dev, BCH16_ECC,
-                                mtd->writesize / chip->ecc.size,
-                                chip->ecc.size, chip->ecc.bytes);
-               if (err < 0)
-                       return err;
-
+               elm_bch_strength = BCH16_ECC;
                break;
        default:
                dev_err(dev, "Invalid or unsupported ECC scheme\n");
                return -EINVAL;
        }
 
+       if (elm_bch_strength >= 0) {
+               chip->ecc.steps = mtd->writesize / chip->ecc.size;
+               info->neccpg = chip->ecc.steps / ERROR_VECTOR_MAX;
+               if (info->neccpg) {
+                       info->nsteps_per_eccpg = ERROR_VECTOR_MAX;
+               } else {
+                       info->neccpg = 1;
+                       info->nsteps_per_eccpg = chip->ecc.steps;
+               }
+               info->eccpg_size = info->nsteps_per_eccpg * chip->ecc.size;
+               info->eccpg_bytes = info->nsteps_per_eccpg * chip->ecc.bytes;
+
+               err = elm_config(info->elm_dev, elm_bch_strength,
+                                info->nsteps_per_eccpg, chip->ecc.size,
+                                chip->ecc.bytes);
+               if (err < 0)
+                       return err;
+       }
+
        /* Check if NAND device's OOB is enough to store ECC signatures */
        min_oobbytes += (oobbytes_per_step *
                         (mtd->writesize / chip->ecc.size));