mtd: at91: extract hw ecc initialization to one function
[firefly-linux-kernel-4.4.55.git] / drivers / mtd / nand / atmel_nand.c
index 2165576a1c67df0e623752970fdfdd141f3ea931..7a41a04beb87286efea71aae4ca142a46aec0641 100644 (file)
@@ -324,9 +324,10 @@ static int atmel_nand_calculate(struct mtd_info *mtd,
  * mtd:        mtd info structure
  * chip:       nand chip info structure
  * buf:        buffer to store read data
+ * oob_required:    caller expects OOB data read to chip->oob_poi
  */
-static int atmel_nand_read_page(struct mtd_info *mtd,
-               struct nand_chip *chip, uint8_t *buf, int page)
+static int atmel_nand_read_page(struct mtd_info *mtd, struct nand_chip *chip,
+                               uint8_t *buf, int oob_required, int page)
 {
        int eccsize = chip->ecc.size;
        int eccbytes = chip->ecc.bytes;
@@ -335,6 +336,7 @@ static int atmel_nand_read_page(struct mtd_info *mtd,
        uint8_t *oob = chip->oob_poi;
        uint8_t *ecc_pos;
        int stat;
+       unsigned int max_bitflips = 0;
 
        /*
         * Errata: ALE is incorrectly wired up to the ECC controller
@@ -371,10 +373,12 @@ static int atmel_nand_read_page(struct mtd_info *mtd,
        /* check if there's an error */
        stat = chip->ecc.correct(mtd, p, oob, NULL);
 
-       if (stat < 0)
+       if (stat < 0) {
                mtd->ecc_stats.failed++;
-       else
+       } else {
                mtd->ecc_stats.corrected += stat;
+               max_bitflips = max_t(unsigned int, max_bitflips, stat);
+       }
 
        /* get back to oob start (end of page) */
        chip->cmdfunc(mtd, NAND_CMD_RNDOUT, mtd->writesize, -1);
@@ -382,7 +386,7 @@ static int atmel_nand_read_page(struct mtd_info *mtd,
        /* read the oob */
        chip->read_buf(mtd, oob, mtd->oobsize);
 
-       return 0;
+       return max_bitflips;
 }
 
 /*
@@ -523,6 +527,66 @@ static int __devinit atmel_of_init_port(struct atmel_nand_host *host,
 }
 #endif
 
+static int __init atmel_hw_nand_init_params(struct platform_device *pdev,
+                                        struct atmel_nand_host *host)
+{
+       struct mtd_info *mtd = &host->mtd;
+       struct nand_chip *nand_chip = &host->nand_chip;
+       struct resource         *regs;
+
+       regs = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+       if (!regs) {
+               dev_err(host->dev,
+                       "Can't get I/O resource regs, use software ECC\n");
+               nand_chip->ecc.mode = NAND_ECC_SOFT;
+               return 0;
+       }
+
+       host->ecc = ioremap(regs->start, resource_size(regs));
+       if (host->ecc == NULL) {
+               dev_err(host->dev, "ioremap failed\n");
+               return -EIO;
+       }
+
+       /* ECC is calculated for the whole page (1 step) */
+       nand_chip->ecc.size = mtd->writesize;
+
+       /* set ECC page size and oob layout */
+       switch (mtd->writesize) {
+       case 512:
+               nand_chip->ecc.layout = &atmel_oobinfo_small;
+               ecc_writel(host->ecc, MR, ATMEL_ECC_PAGESIZE_528);
+               break;
+       case 1024:
+               nand_chip->ecc.layout = &atmel_oobinfo_large;
+               ecc_writel(host->ecc, MR, ATMEL_ECC_PAGESIZE_1056);
+               break;
+       case 2048:
+               nand_chip->ecc.layout = &atmel_oobinfo_large;
+               ecc_writel(host->ecc, MR, ATMEL_ECC_PAGESIZE_2112);
+               break;
+       case 4096:
+               nand_chip->ecc.layout = &atmel_oobinfo_large;
+               ecc_writel(host->ecc, MR, ATMEL_ECC_PAGESIZE_4224);
+               break;
+       default:
+               /* page size not handled by HW ECC */
+               /* switching back to soft ECC */
+               nand_chip->ecc.mode = NAND_ECC_SOFT;
+               return 0;
+       }
+
+       /* set up for HW ECC */
+       nand_chip->ecc.calculate = atmel_nand_calculate;
+       nand_chip->ecc.correct = atmel_nand_correct;
+       nand_chip->ecc.hwctl = atmel_nand_hwctl;
+       nand_chip->ecc.read_page = atmel_nand_read_page;
+       nand_chip->ecc.bytes = 4;
+       nand_chip->ecc.strength = 1;
+
+       return 0;
+}
+
 /*
  * Probe for the NAND device.
  */
@@ -531,7 +595,6 @@ static int __init atmel_nand_probe(struct platform_device *pdev)
        struct atmel_nand_host *host;
        struct mtd_info *mtd;
        struct nand_chip *nand_chip;
-       struct resource *regs;
        struct resource *mem;
        struct mtd_part_parser_data ppdata = {};
        int res;
@@ -583,29 +646,6 @@ static int __init atmel_nand_probe(struct platform_device *pdev)
                nand_chip->dev_ready = atmel_nand_device_ready;
 
        nand_chip->ecc.mode = host->board.ecc_mode;
-
-       regs = platform_get_resource(pdev, IORESOURCE_MEM, 1);
-       if (!regs && nand_chip->ecc.mode == NAND_ECC_HW) {
-               printk(KERN_ERR "atmel_nand: can't get I/O resource "
-                               "regs\nFalling back on software ECC\n");
-               nand_chip->ecc.mode = NAND_ECC_SOFT;
-       }
-
-       if (nand_chip->ecc.mode == NAND_ECC_HW) {
-               host->ecc = ioremap(regs->start, resource_size(regs));
-               if (host->ecc == NULL) {
-                       printk(KERN_ERR "atmel_nand: ioremap failed\n");
-                       res = -EIO;
-                       goto err_ecc_ioremap;
-               }
-               nand_chip->ecc.calculate = atmel_nand_calculate;
-               nand_chip->ecc.correct = atmel_nand_correct;
-               nand_chip->ecc.hwctl = atmel_nand_hwctl;
-               nand_chip->ecc.read_page = atmel_nand_read_page;
-               nand_chip->ecc.bytes = 4;
-               nand_chip->ecc.strength = 1;
-       }
-
        nand_chip->chip_delay = 20;             /* 20us command delay time */
 
        if (host->board.bus_width_16)   /* 16-bit bus width */
@@ -657,40 +697,9 @@ static int __init atmel_nand_probe(struct platform_device *pdev)
        }
 
        if (nand_chip->ecc.mode == NAND_ECC_HW) {
-               /* ECC is calculated for the whole page (1 step) */
-               nand_chip->ecc.size = mtd->writesize;
-
-               /* set ECC page size and oob layout */
-               switch (mtd->writesize) {
-               case 512:
-                       nand_chip->ecc.layout = &atmel_oobinfo_small;
-                       ecc_writel(host->ecc, MR, ATMEL_ECC_PAGESIZE_528);
-                       break;
-               case 1024:
-                       nand_chip->ecc.layout = &atmel_oobinfo_large;
-                       ecc_writel(host->ecc, MR, ATMEL_ECC_PAGESIZE_1056);
-                       break;
-               case 2048:
-                       nand_chip->ecc.layout = &atmel_oobinfo_large;
-                       ecc_writel(host->ecc, MR, ATMEL_ECC_PAGESIZE_2112);
-                       break;
-               case 4096:
-                       nand_chip->ecc.layout = &atmel_oobinfo_large;
-                       ecc_writel(host->ecc, MR, ATMEL_ECC_PAGESIZE_4224);
-                       break;
-               default:
-                       /* page size not handled by HW ECC */
-                       /* switching back to soft ECC */
-                       nand_chip->ecc.mode = NAND_ECC_SOFT;
-                       nand_chip->ecc.calculate = NULL;
-                       nand_chip->ecc.correct = NULL;
-                       nand_chip->ecc.hwctl = NULL;
-                       nand_chip->ecc.read_page = NULL;
-                       nand_chip->ecc.postpad = 0;
-                       nand_chip->ecc.prepad = 0;
-                       nand_chip->ecc.bytes = 0;
-                       break;
-               }
+               res = atmel_hw_nand_init_params(pdev, host);
+               if (res != 0)
+                       goto err_hw_ecc;
        }
 
        /* second phase scan */
@@ -707,15 +716,15 @@ static int __init atmel_nand_probe(struct platform_device *pdev)
                return res;
 
 err_scan_tail:
+       if (host->ecc)
+               iounmap(host->ecc);
+err_hw_ecc:
 err_scan_ident:
 err_no_card:
        atmel_nand_disable(host);
        platform_set_drvdata(pdev, NULL);
        if (host->dma_chan)
                dma_release_channel(host->dma_chan);
-       if (host->ecc)
-               iounmap(host->ecc);
-err_ecc_ioremap:
        iounmap(host->io_base);
 err_nand_ioremap:
        kfree(host);