Merge tag 'mtd/for-4.20' of git://git.infradead.org/linux-mtd
[linux-2.6-block.git] / drivers / mtd / nand / raw / marvell_nand.c
index dde64609415ff0c320bde53b62a8cc7ff1384e13..650f2b490a054c16c8c20f882f0c72134fadd7b1 100644 (file)
@@ -284,8 +284,11 @@ static const struct marvell_hw_ecc_layout marvell_nfc_layouts[] = {
        MARVELL_LAYOUT(  512,   512,  1,  1,  1,  512,  8,  8,  0,  0,  0),
        MARVELL_LAYOUT( 2048,   512,  1,  1,  1, 2048, 40, 24,  0,  0,  0),
        MARVELL_LAYOUT( 2048,   512,  4,  1,  1, 2048, 32, 30,  0,  0,  0),
+       MARVELL_LAYOUT( 2048,   512,  8,  2,  1, 1024,  0, 30,1024,32, 30),
        MARVELL_LAYOUT( 4096,   512,  4,  2,  2, 2048, 32, 30,  0,  0,  0),
        MARVELL_LAYOUT( 4096,   512,  8,  5,  4, 1024,  0, 30,  0, 64, 30),
+       MARVELL_LAYOUT( 8192,   512,  4,  4,  4, 2048,  0, 30,  0,  0,  0),
+       MARVELL_LAYOUT( 8192,   512,  8,  9,  8, 1024,  0, 30,  0, 160, 30),
 };
 
 /**
@@ -701,9 +704,8 @@ static int marvell_nfc_wait_op(struct nand_chip *chip, unsigned int timeout_ms)
        return 0;
 }
 
-static void marvell_nfc_select_chip(struct mtd_info *mtd, int die_nr)
+static void marvell_nfc_select_chip(struct nand_chip *chip, int die_nr)
 {
-       struct nand_chip *chip = mtd_to_nand(mtd);
        struct marvell_nand_chip *marvell_nand = to_marvell_nand(chip);
        struct marvell_nfc *nfc = to_marvell_nfc(chip->controller);
        u32 ndcr_generic;
@@ -753,7 +755,7 @@ static irqreturn_t marvell_nfc_isr(int irq, void *dev_id)
 
        marvell_nfc_disable_int(nfc, st & NDCR_ALL_INT);
 
-       if (!(st & (NDSR_RDDREQ | NDSR_WRDREQ | NDSR_WRCMDREQ)))
+       if (st & (NDSR_RDY(0) | NDSR_RDY(1)))
                complete(&nfc->complete);
 
        return IRQ_HANDLED;
@@ -1026,18 +1028,15 @@ static int marvell_nfc_hw_ecc_hmg_do_read_page(struct nand_chip *chip,
        return ret;
 }
 
-static int marvell_nfc_hw_ecc_hmg_read_page_raw(struct mtd_info *mtd,
-                                               struct nand_chip *chip, u8 *buf,
+static int marvell_nfc_hw_ecc_hmg_read_page_raw(struct nand_chip *chip, u8 *buf,
                                                int oob_required, int page)
 {
        return marvell_nfc_hw_ecc_hmg_do_read_page(chip, buf, chip->oob_poi,
                                                   true, page);
 }
 
-static int marvell_nfc_hw_ecc_hmg_read_page(struct mtd_info *mtd,
-                                           struct nand_chip *chip,
-                                           u8 *buf, int oob_required,
-                                           int page)
+static int marvell_nfc_hw_ecc_hmg_read_page(struct nand_chip *chip, u8 *buf,
+                                           int oob_required, int page)
 {
        const struct marvell_hw_ecc_layout *lt = to_marvell_nand(chip)->layout;
        unsigned int full_sz = lt->data_bytes + lt->spare_bytes + lt->ecc_bytes;
@@ -1075,8 +1074,7 @@ static int marvell_nfc_hw_ecc_hmg_read_page(struct mtd_info *mtd,
  * it appears before the ECC bytes when reading), the ->read_oob_raw() function
  * also stands for ->read_oob().
  */
-static int marvell_nfc_hw_ecc_hmg_read_oob_raw(struct mtd_info *mtd,
-                                              struct nand_chip *chip, int page)
+static int marvell_nfc_hw_ecc_hmg_read_oob_raw(struct nand_chip *chip, int page)
 {
        /* Invalidate page cache */
        chip->pagebuf = -1;
@@ -1140,8 +1138,7 @@ static int marvell_nfc_hw_ecc_hmg_do_write_page(struct nand_chip *chip,
        return ret;
 }
 
-static int marvell_nfc_hw_ecc_hmg_write_page_raw(struct mtd_info *mtd,
-                                                struct nand_chip *chip,
+static int marvell_nfc_hw_ecc_hmg_write_page_raw(struct nand_chip *chip,
                                                 const u8 *buf,
                                                 int oob_required, int page)
 {
@@ -1149,8 +1146,7 @@ static int marvell_nfc_hw_ecc_hmg_write_page_raw(struct mtd_info *mtd,
                                                    true, page);
 }
 
-static int marvell_nfc_hw_ecc_hmg_write_page(struct mtd_info *mtd,
-                                            struct nand_chip *chip,
+static int marvell_nfc_hw_ecc_hmg_write_page(struct nand_chip *chip,
                                             const u8 *buf,
                                             int oob_required, int page)
 {
@@ -1169,10 +1165,11 @@ static int marvell_nfc_hw_ecc_hmg_write_page(struct mtd_info *mtd,
  * it appears before the ECC bytes when reading), the ->write_oob_raw() function
  * also stands for ->write_oob().
  */
-static int marvell_nfc_hw_ecc_hmg_write_oob_raw(struct mtd_info *mtd,
-                                               struct nand_chip *chip,
+static int marvell_nfc_hw_ecc_hmg_write_oob_raw(struct nand_chip *chip,
                                                int page)
 {
+       struct mtd_info *mtd = nand_to_mtd(chip);
+
        /* Invalidate page cache */
        chip->pagebuf = -1;
 
@@ -1183,10 +1180,10 @@ static int marvell_nfc_hw_ecc_hmg_write_oob_raw(struct mtd_info *mtd,
 }
 
 /* BCH read helpers */
-static int marvell_nfc_hw_ecc_bch_read_page_raw(struct mtd_info *mtd,
-                                               struct nand_chip *chip, u8 *buf,
+static int marvell_nfc_hw_ecc_bch_read_page_raw(struct nand_chip *chip, u8 *buf,
                                                int oob_required, int page)
 {
+       struct mtd_info *mtd = nand_to_mtd(chip);
        const struct marvell_hw_ecc_layout *lt = to_marvell_nand(chip)->layout;
        u8 *oob = chip->oob_poi;
        int chunk_size = lt->data_bytes + lt->spare_bytes + lt->ecc_bytes;
@@ -1295,17 +1292,17 @@ static void marvell_nfc_hw_ecc_bch_read_chunk(struct nand_chip *chip, int chunk,
        }
 }
 
-static int marvell_nfc_hw_ecc_bch_read_page(struct mtd_info *mtd,
-                                           struct nand_chip *chip,
+static int marvell_nfc_hw_ecc_bch_read_page(struct nand_chip *chip,
                                            u8 *buf, int oob_required,
                                            int page)
 {
+       struct mtd_info *mtd = nand_to_mtd(chip);
        const struct marvell_hw_ecc_layout *lt = to_marvell_nand(chip)->layout;
-       int data_len = lt->data_bytes, spare_len = lt->spare_bytes, ecc_len;
-       u8 *data = buf, *spare = chip->oob_poi, *ecc;
+       int data_len = lt->data_bytes, spare_len = lt->spare_bytes;
+       u8 *data = buf, *spare = chip->oob_poi;
        int max_bitflips = 0;
        u32 failure_mask = 0;
-       int chunk, ecc_offset_in_page, ret;
+       int chunk, ret;
 
        /*
         * With BCH, OOB is not fully used (and thus not read entirely), not
@@ -1346,73 +1343,98 @@ static int marvell_nfc_hw_ecc_bch_read_page(struct mtd_info *mtd,
         * the controller in normal mode and must be re-read in raw mode. To
         * avoid dropping the performances, we prefer not to include them. The
         * user should re-read the page in raw mode if ECC bytes are required.
+        */
+
+       /*
+        * In case there is any subpage read error reported by ->correct(), we
+        * usually re-read only ECC bytes in raw mode and check if the whole
+        * page is empty. In this case, it is normal that the ECC check failed
+        * and we just ignore the error.
         *
-        * However, for any subpage read error reported by ->correct(), the ECC
-        * bytes must be read in raw mode and the full subpage must be checked
-        * to see if it is entirely empty of if there was an actual error.
+        * However, it has been empirically observed that for some layouts (e.g
+        * 2k page, 8b strength per 512B chunk), the controller tries to correct
+        * bits and may create itself bitflips in the erased area. To overcome
+        * this strange behavior, the whole page is re-read in raw mode, not
+        * only the ECC bytes.
         */
        for (chunk = 0; chunk < lt->nchunks; chunk++) {
+               int data_off_in_page, spare_off_in_page, ecc_off_in_page;
+               int data_off, spare_off, ecc_off;
+               int data_len, spare_len, ecc_len;
+
                /* No failure reported for this chunk, move to the next one */
                if (!(failure_mask & BIT(chunk)))
                        continue;
 
-               /* Derive ECC bytes positions (in page/buffer) and length */
-               ecc = chip->oob_poi +
-                       (lt->full_chunk_cnt * lt->spare_bytes) +
-                       lt->last_spare_bytes +
-                       (chunk * ALIGN(lt->ecc_bytes, 32));
-               ecc_offset_in_page =
-                       (chunk * (lt->data_bytes + lt->spare_bytes +
-                                 lt->ecc_bytes)) +
-                       (chunk < lt->full_chunk_cnt ?
-                        lt->data_bytes + lt->spare_bytes :
-                        lt->last_data_bytes + lt->last_spare_bytes);
-               ecc_len = chunk < lt->full_chunk_cnt ?
-                       lt->ecc_bytes : lt->last_ecc_bytes;
-
-               /* Do the actual raw read of the ECC bytes */
-               nand_change_read_column_op(chip, ecc_offset_in_page,
-                                          ecc, ecc_len, false);
-
-               /* Derive data/spare bytes positions (in buffer) and length */
-               data = buf + (chunk * lt->data_bytes);
-               data_len = chunk < lt->full_chunk_cnt ?
-                       lt->data_bytes : lt->last_data_bytes;
-               spare = chip->oob_poi + (chunk * (lt->spare_bytes +
-                                                 lt->ecc_bytes));
-               spare_len = chunk < lt->full_chunk_cnt ?
-                       lt->spare_bytes : lt->last_spare_bytes;
+               data_off_in_page = chunk * (lt->data_bytes + lt->spare_bytes +
+                                           lt->ecc_bytes);
+               spare_off_in_page = data_off_in_page +
+                       (chunk < lt->full_chunk_cnt ? lt->data_bytes :
+                                                     lt->last_data_bytes);
+               ecc_off_in_page = spare_off_in_page +
+                       (chunk < lt->full_chunk_cnt ? lt->spare_bytes :
+                                                     lt->last_spare_bytes);
+
+               data_off = chunk * lt->data_bytes;
+               spare_off = chunk * lt->spare_bytes;
+               ecc_off = (lt->full_chunk_cnt * lt->spare_bytes) +
+                         lt->last_spare_bytes +
+                         (chunk * (lt->ecc_bytes + 2));
+
+               data_len = chunk < lt->full_chunk_cnt ? lt->data_bytes :
+                                                       lt->last_data_bytes;
+               spare_len = chunk < lt->full_chunk_cnt ? lt->spare_bytes :
+                                                        lt->last_spare_bytes;
+               ecc_len = chunk < lt->full_chunk_cnt ? lt->ecc_bytes :
+                                                      lt->last_ecc_bytes;
+
+               /*
+                * Only re-read the ECC bytes, unless we are using the 2k/8b
+                * layout which is buggy in the sense that the ECC engine will
+                * try to correct data bytes anyway, creating bitflips. In this
+                * case, re-read the entire page.
+                */
+               if (lt->writesize == 2048 && lt->strength == 8) {
+                       nand_change_read_column_op(chip, data_off_in_page,
+                                                  buf + data_off, data_len,
+                                                  false);
+                       nand_change_read_column_op(chip, spare_off_in_page,
+                                                  chip->oob_poi + spare_off, spare_len,
+                                                  false);
+               }
+
+               nand_change_read_column_op(chip, ecc_off_in_page,
+                                          chip->oob_poi + ecc_off, ecc_len,
+                                          false);
 
                /* Check the entire chunk (data + spare + ecc) for emptyness */
-               marvell_nfc_check_empty_chunk(chip, data, data_len, spare,
-                                             spare_len, ecc, ecc_len,
+               marvell_nfc_check_empty_chunk(chip, buf + data_off, data_len,
+                                             chip->oob_poi + spare_off, spare_len,
+                                             chip->oob_poi + ecc_off, ecc_len,
                                              &max_bitflips);
        }
 
        return max_bitflips;
 }
 
-static int marvell_nfc_hw_ecc_bch_read_oob_raw(struct mtd_info *mtd,
-                                              struct nand_chip *chip, int page)
+static int marvell_nfc_hw_ecc_bch_read_oob_raw(struct nand_chip *chip, int page)
 {
        /* Invalidate page cache */
        chip->pagebuf = -1;
 
-       return chip->ecc.read_page_raw(mtd, chip, chip->data_buf, true, page);
+       return chip->ecc.read_page_raw(chip, chip->data_buf, true, page);
 }
 
-static int marvell_nfc_hw_ecc_bch_read_oob(struct mtd_info *mtd,
-                                          struct nand_chip *chip, int page)
+static int marvell_nfc_hw_ecc_bch_read_oob(struct nand_chip *chip, int page)
 {
        /* Invalidate page cache */
        chip->pagebuf = -1;
 
-       return chip->ecc.read_page(mtd, chip, chip->data_buf, true, page);
+       return chip->ecc.read_page(chip, chip->data_buf, true, page);
 }
 
 /* BCH write helpers */
-static int marvell_nfc_hw_ecc_bch_write_page_raw(struct mtd_info *mtd,
-                                                struct nand_chip *chip,
+static int marvell_nfc_hw_ecc_bch_write_page_raw(struct nand_chip *chip,
                                                 const u8 *buf,
                                                 int oob_required, int page)
 {
@@ -1525,11 +1547,11 @@ marvell_nfc_hw_ecc_bch_write_chunk(struct nand_chip *chip, int chunk,
        return 0;
 }
 
-static int marvell_nfc_hw_ecc_bch_write_page(struct mtd_info *mtd,
-                                            struct nand_chip *chip,
+static int marvell_nfc_hw_ecc_bch_write_page(struct nand_chip *chip,
                                             const u8 *buf,
                                             int oob_required, int page)
 {
+       struct mtd_info *mtd = nand_to_mtd(chip);
        const struct marvell_hw_ecc_layout *lt = to_marvell_nand(chip)->layout;
        const u8 *data = buf;
        const u8 *spare = chip->oob_poi;
@@ -1574,27 +1596,29 @@ static int marvell_nfc_hw_ecc_bch_write_page(struct mtd_info *mtd,
        return 0;
 }
 
-static int marvell_nfc_hw_ecc_bch_write_oob_raw(struct mtd_info *mtd,
-                                               struct nand_chip *chip,
+static int marvell_nfc_hw_ecc_bch_write_oob_raw(struct nand_chip *chip,
                                                int page)
 {
+       struct mtd_info *mtd = nand_to_mtd(chip);
+
        /* Invalidate page cache */
        chip->pagebuf = -1;
 
        memset(chip->data_buf, 0xFF, mtd->writesize);
 
-       return chip->ecc.write_page_raw(mtd, chip, chip->data_buf, true, page);
+       return chip->ecc.write_page_raw(chip, chip->data_buf, true, page);
 }
 
-static int marvell_nfc_hw_ecc_bch_write_oob(struct mtd_info *mtd,
-                                           struct nand_chip *chip, int page)
+static int marvell_nfc_hw_ecc_bch_write_oob(struct nand_chip *chip, int page)
 {
+       struct mtd_info *mtd = nand_to_mtd(chip);
+
        /* Invalidate page cache */
        chip->pagebuf = -1;
 
        memset(chip->data_buf, 0xFF, mtd->writesize);
 
-       return chip->ecc.write_page(mtd, chip, chip->data_buf, true, page);
+       return chip->ecc.write_page(chip, chip->data_buf, true, page);
 }
 
 /* NAND framework ->exec_op() hooks and related helpers */
@@ -1614,7 +1638,7 @@ static void marvell_nfc_parse_instructions(struct nand_chip *chip,
        for (op_id = 0; op_id < subop->ninstrs; op_id++) {
                unsigned int offset, naddrs;
                const u8 *addrs;
-               int len = nand_subop_get_data_len(subop, op_id);
+               int len;
 
                instr = &subop->instrs[op_id];
 
@@ -1660,6 +1684,7 @@ static void marvell_nfc_parse_instructions(struct nand_chip *chip,
                                nfc_op->ndcb[0] |=
                                        NDCB0_CMD_XTYPE(XTYPE_MONOLITHIC_RW) |
                                        NDCB0_LEN_OVRD;
+                               len = nand_subop_get_data_len(subop, op_id);
                                nfc_op->ndcb[3] |= round_up(len, FIFO_DEPTH);
                        }
                        nfc_op->data_delay_ns = instr->delay_ns;
@@ -1673,6 +1698,7 @@ static void marvell_nfc_parse_instructions(struct nand_chip *chip,
                                nfc_op->ndcb[0] |=
                                        NDCB0_CMD_XTYPE(XTYPE_MONOLITHIC_RW) |
                                        NDCB0_LEN_OVRD;
+                               len = nand_subop_get_data_len(subop, op_id);
                                nfc_op->ndcb[3] |= round_up(len, FIFO_DEPTH);
                        }
                        nfc_op->data_delay_ns = instr->delay_ns;
@@ -2162,6 +2188,16 @@ static int marvell_nand_hw_ecc_ctrl_init(struct mtd_info *mtd,
                return -ENOTSUPP;
        }
 
+       /* Special care for the layout 2k/8-bit/512B  */
+       if (l->writesize == 2048 && l->strength == 8) {
+               if (mtd->oobsize < 128) {
+                       dev_err(nfc->dev, "Requested layout needs at least 128 OOB bytes\n");
+                       return -ENOTSUPP;
+               } else {
+                       chip->bbt_options |= NAND_BBT_NO_OOB_BBM;
+               }
+       }
+
        mtd_set_ooblayout(mtd, &marvell_nand_ooblayout_ops);
        ecc->steps = l->nchunks;
        ecc->size = l->data_bytes;
@@ -2257,11 +2293,10 @@ static struct nand_bbt_descr bbt_mirror_descr = {
        .pattern = bbt_mirror_pattern
 };
 
-static int marvell_nfc_setup_data_interface(struct mtd_info *mtd, int chipnr,
+static int marvell_nfc_setup_data_interface(struct nand_chip *chip, int chipnr,
                                            const struct nand_data_interface
                                            *conf)
 {
-       struct nand_chip *chip = mtd_to_nand(mtd);
        struct marvell_nand_chip *marvell_nand = to_marvell_nand(chip);
        struct marvell_nfc *nfc = to_marvell_nfc(chip->controller);
        unsigned int period_ns = 1000000000 / clk_get_rate(nfc->core_clk) * 2;
@@ -2618,7 +2653,7 @@ static int marvell_nand_chip_init(struct device *dev, struct marvell_nfc *nfc,
                ret = mtd_device_register(mtd, NULL, 0);
        if (ret) {
                dev_err(dev, "failed to register mtd device: %d\n", ret);
-               nand_release(mtd);
+               nand_release(chip);
                return ret;
        }
 
@@ -2673,7 +2708,7 @@ static void marvell_nand_chips_cleanup(struct marvell_nfc *nfc)
        struct marvell_nand_chip *entry, *temp;
 
        list_for_each_entry_safe(entry, temp, &nfc->chips, node) {
-               nand_release(nand_to_mtd(&entry->chip));
+               nand_release(&entry->chip);
                list_del(&entry->node);
        }
 }