Merge tag '4.9/mtd-pairing-scheme' of github.com:linux-nand/linux
[cascardo/linux.git] / drivers / mtd / nand / brcmnand / brcmnand.c
index b76ad7c..9d2424b 100644 (file)
@@ -340,6 +340,36 @@ static const u16 brcmnand_regs_v71[] = {
        [BRCMNAND_FC_BASE]              = 0x400,
 };
 
+/* BRCMNAND v7.2 */
+static const u16 brcmnand_regs_v72[] = {
+       [BRCMNAND_CMD_START]            =  0x04,
+       [BRCMNAND_CMD_EXT_ADDRESS]      =  0x08,
+       [BRCMNAND_CMD_ADDRESS]          =  0x0c,
+       [BRCMNAND_INTFC_STATUS]         =  0x14,
+       [BRCMNAND_CS_SELECT]            =  0x18,
+       [BRCMNAND_CS_XOR]               =  0x1c,
+       [BRCMNAND_LL_OP]                =  0x20,
+       [BRCMNAND_CS0_BASE]             =  0x50,
+       [BRCMNAND_CS1_BASE]             =     0,
+       [BRCMNAND_CORR_THRESHOLD]       =  0xdc,
+       [BRCMNAND_CORR_THRESHOLD_EXT]   =  0xe0,
+       [BRCMNAND_UNCORR_COUNT]         =  0xfc,
+       [BRCMNAND_CORR_COUNT]           = 0x100,
+       [BRCMNAND_CORR_EXT_ADDR]        = 0x10c,
+       [BRCMNAND_CORR_ADDR]            = 0x110,
+       [BRCMNAND_UNCORR_EXT_ADDR]      = 0x114,
+       [BRCMNAND_UNCORR_ADDR]          = 0x118,
+       [BRCMNAND_SEMAPHORE]            = 0x150,
+       [BRCMNAND_ID]                   = 0x194,
+       [BRCMNAND_ID_EXT]               = 0x198,
+       [BRCMNAND_LL_RDATA]             = 0x19c,
+       [BRCMNAND_OOB_READ_BASE]        = 0x200,
+       [BRCMNAND_OOB_READ_10_BASE]     =     0,
+       [BRCMNAND_OOB_WRITE_BASE]       = 0x400,
+       [BRCMNAND_OOB_WRITE_10_BASE]    =     0,
+       [BRCMNAND_FC_BASE]              = 0x600,
+};
+
 enum brcmnand_cs_reg {
        BRCMNAND_CS_CFG_EXT = 0,
        BRCMNAND_CS_CFG,
@@ -435,7 +465,9 @@ static int brcmnand_revision_init(struct brcmnand_controller *ctrl)
        }
 
        /* Register offsets */
-       if (ctrl->nand_version >= 0x0701)
+       if (ctrl->nand_version >= 0x0702)
+               ctrl->reg_offsets = brcmnand_regs_v72;
+       else if (ctrl->nand_version >= 0x0701)
                ctrl->reg_offsets = brcmnand_regs_v71;
        else if (ctrl->nand_version >= 0x0600)
                ctrl->reg_offsets = brcmnand_regs_v60;
@@ -480,7 +512,9 @@ static int brcmnand_revision_init(struct brcmnand_controller *ctrl)
        }
 
        /* Maximum spare area sector size (per 512B) */
-       if (ctrl->nand_version >= 0x0600)
+       if (ctrl->nand_version >= 0x0702)
+               ctrl->max_oob = 128;
+       else if (ctrl->nand_version >= 0x0600)
                ctrl->max_oob = 64;
        else if (ctrl->nand_version >= 0x0500)
                ctrl->max_oob = 32;
@@ -583,14 +617,20 @@ static void brcmnand_wr_corr_thresh(struct brcmnand_host *host, u8 val)
        enum brcmnand_reg reg = BRCMNAND_CORR_THRESHOLD;
        int cs = host->cs;
 
-       if (ctrl->nand_version >= 0x0600)
+       if (ctrl->nand_version >= 0x0702)
+               bits = 7;
+       else if (ctrl->nand_version >= 0x0600)
                bits = 6;
        else if (ctrl->nand_version >= 0x0500)
                bits = 5;
        else
                bits = 4;
 
-       if (ctrl->nand_version >= 0x0600) {
+       if (ctrl->nand_version >= 0x0702) {
+               if (cs >= 4)
+                       reg = BRCMNAND_CORR_THRESHOLD_EXT;
+               shift = (cs % 4) * bits;
+       } else if (ctrl->nand_version >= 0x0600) {
                if (cs >= 5)
                        reg = BRCMNAND_CORR_THRESHOLD_EXT;
                shift = (cs % 5) * bits;
@@ -631,19 +671,28 @@ enum {
 
 static inline u32 brcmnand_spare_area_mask(struct brcmnand_controller *ctrl)
 {
-       if (ctrl->nand_version >= 0x0600)
+       if (ctrl->nand_version >= 0x0702)
+               return GENMASK(7, 0);
+       else if (ctrl->nand_version >= 0x0600)
                return GENMASK(6, 0);
        else
                return GENMASK(5, 0);
 }
 
 #define NAND_ACC_CONTROL_ECC_SHIFT     16
+#define NAND_ACC_CONTROL_ECC_EXT_SHIFT 13
 
 static inline u32 brcmnand_ecc_level_mask(struct brcmnand_controller *ctrl)
 {
        u32 mask = (ctrl->nand_version >= 0x0600) ? 0x1f : 0x0f;
 
-       return mask << NAND_ACC_CONTROL_ECC_SHIFT;
+       mask <<= NAND_ACC_CONTROL_ECC_SHIFT;
+
+       /* v7.2 includes additional ECC levels */
+       if (ctrl->nand_version >= 0x0702)
+               mask |= 0x7 << NAND_ACC_CONTROL_ECC_EXT_SHIFT;
+
+       return mask;
 }
 
 static void brcmnand_set_ecc_enabled(struct brcmnand_host *host, int en)
@@ -667,7 +716,9 @@ static void brcmnand_set_ecc_enabled(struct brcmnand_host *host, int en)
 
 static inline int brcmnand_sector_1k_shift(struct brcmnand_controller *ctrl)
 {
-       if (ctrl->nand_version >= 0x0600)
+       if (ctrl->nand_version >= 0x0702)
+               return 9;
+       else if (ctrl->nand_version >= 0x0600)
                return 7;
        else if (ctrl->nand_version >= 0x0500)
                return 6;
@@ -773,10 +824,16 @@ enum brcmnand_llop_type {
  * Internal support functions
  ***********************************************************************/
 
-static inline bool is_hamming_ecc(struct brcmnand_cfg *cfg)
+static inline bool is_hamming_ecc(struct brcmnand_controller *ctrl,
+                                 struct brcmnand_cfg *cfg)
 {
-       return cfg->sector_size_1k == 0 && cfg->spare_area_size == 16 &&
-               cfg->ecc_level == 15;
+       if (ctrl->nand_version <= 0x0701)
+               return cfg->sector_size_1k == 0 && cfg->spare_area_size == 16 &&
+                       cfg->ecc_level == 15;
+       else
+               return cfg->sector_size_1k == 0 && ((cfg->spare_area_size == 16 &&
+                       cfg->ecc_level == 15) ||
+                       (cfg->spare_area_size == 28 && cfg->ecc_level == 16));
 }
 
 /*
@@ -931,7 +988,7 @@ static int brcmstb_choose_ecc_layout(struct brcmnand_host *host)
        if (p->sector_size_1k)
                ecc_level <<= 1;
 
-       if (is_hamming_ecc(p)) {
+       if (is_hamming_ecc(host->ctrl, p)) {
                ecc->bytes = 3 * sectors;
                mtd_set_ooblayout(mtd, &brcmnand_hamming_ooblayout_ops);
                return 0;
@@ -1108,7 +1165,7 @@ static void brcmnand_send_cmd(struct brcmnand_host *host, int cmd)
        ctrl->cmd_pending = cmd;
 
        intfc = brcmnand_read_reg(ctrl, BRCMNAND_INTFC_STATUS);
-       BUG_ON(!(intfc & INTFC_CTLR_READY));
+       WARN_ON(!(intfc & INTFC_CTLR_READY));
 
        mb(); /* flush previous writes */
        brcmnand_write_reg(ctrl, BRCMNAND_CMD_START,
@@ -1279,7 +1336,7 @@ static void brcmnand_cmdfunc(struct mtd_info *mtd, unsigned command,
                u32 *flash_cache = (u32 *)ctrl->flash_cache;
                int i;
 
-               brcmnand_soc_data_bus_prepare(ctrl->soc);
+               brcmnand_soc_data_bus_prepare(ctrl->soc, true);
 
                /*
                 * Must cache the FLASH_CACHE now, since changes in
@@ -1292,7 +1349,7 @@ static void brcmnand_cmdfunc(struct mtd_info *mtd, unsigned command,
                         */
                        flash_cache[i] = be32_to_cpu(brcmnand_read_fc(ctrl, i));
 
-               brcmnand_soc_data_bus_unprepare(ctrl->soc);
+               brcmnand_soc_data_bus_unprepare(ctrl->soc, true);
 
                /* Cleanup from HW quirk: restore SECTOR_SIZE_1K */
                if (host->hwcfg.sector_size_1k)
@@ -1508,12 +1565,12 @@ static int brcmnand_read_by_pio(struct mtd_info *mtd, struct nand_chip *chip,
                brcmnand_waitfunc(mtd, chip);
 
                if (likely(buf)) {
-                       brcmnand_soc_data_bus_prepare(ctrl->soc);
+                       brcmnand_soc_data_bus_prepare(ctrl->soc, false);
 
                        for (j = 0; j < FC_WORDS; j++, buf++)
                                *buf = brcmnand_read_fc(ctrl, j);
 
-                       brcmnand_soc_data_bus_unprepare(ctrl->soc);
+                       brcmnand_soc_data_bus_unprepare(ctrl->soc, false);
                }
 
                if (oob)
@@ -1545,6 +1602,56 @@ static int brcmnand_read_by_pio(struct mtd_info *mtd, struct nand_chip *chip,
        return ret;
 }
 
+/*
+ * Check a page to see if it is erased (w/ bitflips) after an uncorrectable ECC
+ * error
+ *
+ * Because the HW ECC signals an ECC error if an erase paged has even a single
+ * bitflip, we must check each ECC error to see if it is actually an erased
+ * page with bitflips, not a truly corrupted page.
+ *
+ * On a real error, return a negative error code (-EBADMSG for ECC error), and
+ * buf will contain raw data.
+ * Otherwise, buf gets filled with 0xffs and return the maximum number of
+ * bitflips-per-ECC-sector to the caller.
+ *
+ */
+static int brcmstb_nand_verify_erased_page(struct mtd_info *mtd,
+                 struct nand_chip *chip, void *buf, u64 addr)
+{
+       int i, sas;
+       void *oob = chip->oob_poi;
+       int bitflips = 0;
+       int page = addr >> chip->page_shift;
+       int ret;
+
+       if (!buf) {
+               buf = chip->buffers->databuf;
+               /* Invalidate page cache */
+               chip->pagebuf = -1;
+       }
+
+       sas = mtd->oobsize / chip->ecc.steps;
+
+       /* read without ecc for verification */
+       chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page);
+       ret = chip->ecc.read_page_raw(mtd, chip, buf, true, page);
+       if (ret)
+               return ret;
+
+       for (i = 0; i < chip->ecc.steps; i++, oob += sas) {
+               ret = nand_check_erased_ecc_chunk(buf, chip->ecc.size,
+                                                 oob, sas, NULL, 0,
+                                                 chip->ecc.strength);
+               if (ret < 0)
+                       return ret;
+
+               bitflips = max(bitflips, ret);
+       }
+
+       return bitflips;
+}
+
 static int brcmnand_read(struct mtd_info *mtd, struct nand_chip *chip,
                         u64 addr, unsigned int trans, u32 *buf, u8 *oob)
 {
@@ -1552,9 +1659,11 @@ static int brcmnand_read(struct mtd_info *mtd, struct nand_chip *chip,
        struct brcmnand_controller *ctrl = host->ctrl;
        u64 err_addr = 0;
        int err;
+       bool retry = true;
 
        dev_dbg(ctrl->dev, "read %llx -> %p\n", (unsigned long long)addr, buf);
 
+try_dmaread:
        brcmnand_write_reg(ctrl, BRCMNAND_UNCORR_COUNT, 0);
 
        if (has_flash_dma(ctrl) && !oob && flash_dma_buf_ok(buf)) {
@@ -1575,6 +1684,34 @@ static int brcmnand_read(struct mtd_info *mtd, struct nand_chip *chip,
        }
 
        if (mtd_is_eccerr(err)) {
+               /*
+                * On controller version and 7.0, 7.1 , DMA read after a
+                * prior PIO read that reported uncorrectable error,
+                * the DMA engine captures this error following DMA read
+                * cleared only on subsequent DMA read, so just retry once
+                * to clear a possible false error reported for current DMA
+                * read
+                */
+               if ((ctrl->nand_version == 0x0700) ||
+                   (ctrl->nand_version == 0x0701)) {
+                       if (retry) {
+                               retry = false;
+                               goto try_dmaread;
+                       }
+               }
+
+               /*
+                * Controller version 7.2 has hw encoder to detect erased page
+                * bitflips, apply sw verification for older controllers only
+                */
+               if (ctrl->nand_version < 0x0702) {
+                       err = brcmstb_nand_verify_erased_page(mtd, chip, buf,
+                                                             addr);
+                       /* erased page bitflips corrected */
+                       if (err > 0)
+                               return err;
+               }
+
                dev_dbg(ctrl->dev, "uncorrectable error at 0x%llx\n",
                        (unsigned long long)err_addr);
                mtd->ecc_stats.failed++;
@@ -1678,12 +1815,12 @@ static int brcmnand_write(struct mtd_info *mtd, struct nand_chip *chip,
                (void)brcmnand_read_reg(ctrl, BRCMNAND_CMD_ADDRESS);
 
                if (buf) {
-                       brcmnand_soc_data_bus_prepare(ctrl->soc);
+                       brcmnand_soc_data_bus_prepare(ctrl->soc, false);
 
                        for (j = 0; j < FC_WORDS; j++, buf++)
                                brcmnand_write_fc(ctrl, j, *buf);
 
-                       brcmnand_soc_data_bus_unprepare(ctrl->soc);
+                       brcmnand_soc_data_bus_unprepare(ctrl->soc, false);
                } else if (oob) {
                        for (j = 0; j < FC_WORDS; j++)
                                brcmnand_write_fc(ctrl, j, 0xffffffff);
@@ -1857,7 +1994,8 @@ static int brcmnand_set_cfg(struct brcmnand_host *host,
        return 0;
 }
 
-static void brcmnand_print_cfg(char *buf, struct brcmnand_cfg *cfg)
+static void brcmnand_print_cfg(struct brcmnand_host *host,
+                              char *buf, struct brcmnand_cfg *cfg)
 {
        buf += sprintf(buf,
                "%lluMiB total, %uKiB blocks, %u%s pages, %uB OOB, %u-bit",
@@ -1868,7 +2006,7 @@ static void brcmnand_print_cfg(char *buf, struct brcmnand_cfg *cfg)
                cfg->spare_area_size, cfg->device_width);
 
        /* Account for Hamming ECC and for BCH 512B vs 1KiB sectors */
-       if (is_hamming_ecc(cfg))
+       if (is_hamming_ecc(host->ctrl, cfg))
                sprintf(buf, ", Hamming ECC");
        else if (cfg->sector_size_1k)
                sprintf(buf, ", BCH-%u (1KiB sector)", cfg->ecc_level << 1);
@@ -1987,7 +2125,7 @@ static int brcmnand_setup_dev(struct brcmnand_host *host)
 
        brcmnand_set_ecc_enabled(host, 1);
 
-       brcmnand_print_cfg(msg, cfg);
+       brcmnand_print_cfg(host, msg, cfg);
        dev_info(ctrl->dev, "detected %s\n", msg);
 
        /* Configure ACC_CONTROL */
@@ -1995,6 +2133,10 @@ static int brcmnand_setup_dev(struct brcmnand_host *host)
        tmp = nand_readreg(ctrl, offs);
        tmp &= ~ACC_CONTROL_PARTIAL_PAGE;
        tmp &= ~ACC_CONTROL_RD_ERASED;
+
+       /* We need to turn on Read from erased paged protected by ECC */
+       if (ctrl->nand_version >= 0x0702)
+               tmp |= ACC_CONTROL_RD_ERASED;
        tmp &= ~ACC_CONTROL_FAST_PGM_RDIN;
        if (ctrl->features & BRCMNAND_HAS_PREFETCH) {
                /*
@@ -2195,6 +2337,7 @@ static const struct of_device_id brcmnand_of_match[] = {
        { .compatible = "brcm,brcmnand-v6.2" },
        { .compatible = "brcm,brcmnand-v7.0" },
        { .compatible = "brcm,brcmnand-v7.1" },
+       { .compatible = "brcm,brcmnand-v7.2" },
        {},
 };
 MODULE_DEVICE_TABLE(of, brcmnand_of_match);
@@ -2227,8 +2370,7 @@ int brcmnand_probe(struct platform_device *pdev, struct brcmnand_soc *soc)
 
        init_completion(&ctrl->done);
        init_completion(&ctrl->dma_done);
-       spin_lock_init(&ctrl->controller.lock);
-       init_waitqueue_head(&ctrl->controller.wq);
+       nand_hw_control_init(&ctrl->controller);
        INIT_LIST_HEAD(&ctrl->host_list);
 
        /* NAND register range */