]> git.karo-electronics.de Git - mv-sheeva.git/blobdiff - drivers/mtd/nand/fsl_elbc_nand.c
mtd: fsl_elbc_nand: set Nand flash page address to FBAR and FPAR correctly
[mv-sheeva.git] / drivers / mtd / nand / fsl_elbc_nand.c
index bff4791d73c3bc27c5ce129e357ba1ce729f2751..d29479abe50441b009d83c0e5ce15a8f1e0bb668 100644 (file)
@@ -75,7 +75,6 @@ struct fsl_elbc_fcm_ctrl {
        unsigned int use_mdr;    /* Non zero if the MDR is to be set      */
        unsigned int oob;        /* Non zero if operating on OOB data     */
        unsigned int counter;    /* counter for the initializations       */
-       char *oob_poi;           /* Place to write ECC after read back    */
 };
 
 /* These map to the positions used by the FCM hardware ECC generator */
@@ -167,15 +166,22 @@ static void set_addr(struct mtd_info *mtd, int column, int page_addr, int oob)
 
        elbc_fcm_ctrl->page = page_addr;
 
-       out_be32(&lbc->fbar,
-                page_addr >> (chip->phys_erase_shift - chip->page_shift));
-
        if (priv->page_size) {
+               /*
+                * large page size chip : FPAR[PI] save the lowest 6 bits,
+                *                        FBAR[BLK] save the other bits.
+                */
+               out_be32(&lbc->fbar, page_addr >> 6);
                out_be32(&lbc->fpar,
                         ((page_addr << FPAR_LP_PI_SHIFT) & FPAR_LP_PI) |
                         (oob ? FPAR_LP_MS : 0) | column);
                buf_num = (page_addr & 1) << 2;
        } else {
+               /*
+                * small page size chip : FPAR[PI] save the lowest 5 bits,
+                *                        FBAR[BLK] save the other bits.
+                */
+               out_be32(&lbc->fbar, page_addr >> 5);
                out_be32(&lbc->fpar,
                         ((page_addr << FPAR_SP_PI_SHIFT) & FPAR_SP_PI) |
                         (oob ? FPAR_SP_MS : 0) | column);
@@ -244,6 +250,25 @@ static int fsl_elbc_run_command(struct mtd_info *mtd)
                return -EIO;
        }
 
+       if (chip->ecc.mode != NAND_ECC_HW)
+               return 0;
+
+       if (elbc_fcm_ctrl->read_bytes == mtd->writesize + mtd->oobsize) {
+               uint32_t lteccr = in_be32(&lbc->lteccr);
+               /*
+                * if command was a full page read and the ELBC
+                * has the LTECCR register, then bits 12-15 (ppc order) of
+                * LTECCR indicates which 512 byte sub-pages had fixed errors.
+                * bits 28-31 are uncorrectable errors, marked elsewhere.
+                * for small page nand only 1 bit is used.
+                * if the ELBC doesn't have the lteccr register it reads 0
+                */
+               if (lteccr & 0x000F000F)
+                       out_be32(&lbc->lteccr, 0x000F000F); /* clear lteccr */
+               if (lteccr & 0x000F0000)
+                       mtd->ecc_stats.corrected++;
+       }
+
        return 0;
 }
 
@@ -389,9 +414,17 @@ static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command,
                         page_addr, column);
 
                elbc_fcm_ctrl->column = column;
-               elbc_fcm_ctrl->oob = 0;
                elbc_fcm_ctrl->use_mdr = 1;
 
+               if (column >= mtd->writesize) {
+                       /* OOB area */
+                       column -= mtd->writesize;
+                       elbc_fcm_ctrl->oob = 1;
+               } else {
+                       WARN_ON(column != 0);
+                       elbc_fcm_ctrl->oob = 0;
+               }
+
                fcr = (NAND_CMD_STATUS   << FCR_CMD1_SHIFT) |
                      (NAND_CMD_SEQIN    << FCR_CMD2_SHIFT) |
                      (NAND_CMD_PAGEPROG << FCR_CMD3_SHIFT);
@@ -416,16 +449,12 @@ static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command,
                                 (FIR_OP_CW1 << FIR_OP6_SHIFT) |
                                 (FIR_OP_RS  << FIR_OP7_SHIFT));
 
-                       if (column >= mtd->writesize) {
+                       if (elbc_fcm_ctrl->oob)
                                /* OOB area --> READOOB */
-                               column -= mtd->writesize;
                                fcr |= NAND_CMD_READOOB << FCR_CMD0_SHIFT;
-                               elbc_fcm_ctrl->oob = 1;
-                       } else {
-                               WARN_ON(column != 0);
+                       else
                                /* First 256 bytes --> READ0 */
                                fcr |= NAND_CMD_READ0 << FCR_CMD0_SHIFT;
-                       }
                }
 
                out_be32(&lbc->fcr, fcr);
@@ -435,7 +464,6 @@ static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command,
 
        /* PAGEPROG reuses all of the setup from SEQIN and adds the length */
        case NAND_CMD_PAGEPROG: {
-               int full_page;
                dev_vdbg(priv->dev,
                         "fsl_elbc_cmdfunc: NAND_CMD_PAGEPROG "
                         "writing %d bytes.\n", elbc_fcm_ctrl->index);
@@ -445,34 +473,13 @@ static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command,
                 * write so the HW generates the ECC.
                 */
                if (elbc_fcm_ctrl->oob || elbc_fcm_ctrl->column != 0 ||
-                   elbc_fcm_ctrl->index != mtd->writesize + mtd->oobsize) {
-                       out_be32(&lbc->fbcr, elbc_fcm_ctrl->index);
-                       full_page = 0;
-               } else {
+                   elbc_fcm_ctrl->index != mtd->writesize + mtd->oobsize)
+                       out_be32(&lbc->fbcr,
+                               elbc_fcm_ctrl->index - elbc_fcm_ctrl->column);
+               else
                        out_be32(&lbc->fbcr, 0);
-                       full_page = 1;
-               }
 
                fsl_elbc_run_command(mtd);
-
-               /* Read back the page in order to fill in the ECC for the
-                * caller.  Is this really needed?
-                */
-               if (full_page && elbc_fcm_ctrl->oob_poi) {
-                       out_be32(&lbc->fbcr, 3);
-                       set_addr(mtd, 6, page_addr, 1);
-
-                       elbc_fcm_ctrl->read_bytes = mtd->writesize + 9;
-
-                       fsl_elbc_do_read(chip, 1);
-                       fsl_elbc_run_command(mtd);
-
-                       memcpy_fromio(elbc_fcm_ctrl->oob_poi + 6,
-                               &elbc_fcm_ctrl->addr[elbc_fcm_ctrl->index], 3);
-                       elbc_fcm_ctrl->index += 3;
-               }
-
-               elbc_fcm_ctrl->oob_poi = NULL;
                return;
        }
 
@@ -752,13 +759,8 @@ static void fsl_elbc_write_page(struct mtd_info *mtd,
                                 struct nand_chip *chip,
                                 const uint8_t *buf)
 {
-       struct fsl_elbc_mtd *priv = chip->priv;
-       struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = priv->ctrl->nand;
-
        fsl_elbc_write_buf(mtd, buf, mtd->writesize);
        fsl_elbc_write_buf(mtd, chip->oob_poi, mtd->oobsize);
-
-       elbc_fcm_ctrl->oob_poi = chip->oob_poi;
 }
 
 static int fsl_elbc_chip_init(struct fsl_elbc_mtd *priv)
@@ -792,7 +794,7 @@ static int fsl_elbc_chip_init(struct fsl_elbc_mtd *priv)
 
        /* set up nand options */
        chip->options = NAND_NO_READRDY | NAND_NO_AUTOINCR;
-       chip->bbt_options = NAND_USE_FLASH_BBT;
+       chip->bbt_options = NAND_BBT_USE_FLASH;
 
        chip->controller = &elbc_fcm_ctrl->controller;
        chip->priv = priv;
@@ -829,7 +831,6 @@ static int fsl_elbc_chip_remove(struct fsl_elbc_mtd *priv)
 
        elbc_fcm_ctrl->chips[priv->bank] = NULL;
        kfree(priv);
-       kfree(elbc_fcm_ctrl);
        return 0;
 }
 
@@ -842,13 +843,14 @@ static int __devinit fsl_elbc_nand_probe(struct platform_device *pdev)
        struct resource res;
        struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl;
        static const char *part_probe_types[]
-               = { "cmdlinepart", "RedBoot", NULL };
-       struct mtd_partition *parts;
+               = { "cmdlinepart", "RedBoot", "ofpart", NULL };
        int ret;
        int bank;
        struct device *dev;
        struct device_node *node = pdev->dev.of_node;
+       struct mtd_part_parser_data ppdata;
 
+       ppdata.of_node = pdev->dev.of_node;
        if (!fsl_lbc_ctrl_dev || !fsl_lbc_ctrl_dev->regs)
                return -ENODEV;
        lbc = fsl_lbc_ctrl_dev->regs;
@@ -934,17 +936,8 @@ static int __devinit fsl_elbc_nand_probe(struct platform_device *pdev)
 
        /* First look for RedBoot table or partitions on the command
         * line, these take precedence over device tree information */
-       ret = parse_mtd_partitions(&priv->mtd, part_probe_types, &parts, 0);
-       if (ret < 0)
-               goto err;
-
-       if (ret == 0) {
-               ret = of_mtd_parse_partitions(priv->dev, node, &parts);
-               if (ret < 0)
-                       goto err;
-       }
-
-       mtd_device_register(&priv->mtd, parts, ret);
+       mtd_device_parse_register(&priv->mtd, part_probe_types, &ppdata,
+                                 NULL, 0);
 
        printk(KERN_INFO "eLBC NAND device at 0x%llx, bank %d\n",
               (unsigned long long)res.start, priv->bank);
@@ -990,18 +983,7 @@ static struct platform_driver fsl_elbc_nand_driver = {
        .remove = fsl_elbc_nand_remove,
 };
 
-static int __init fsl_elbc_nand_init(void)
-{
-       return platform_driver_register(&fsl_elbc_nand_driver);
-}
-
-static void __exit fsl_elbc_nand_exit(void)
-{
-       platform_driver_unregister(&fsl_elbc_nand_driver);
-}
-
-module_init(fsl_elbc_nand_init);
-module_exit(fsl_elbc_nand_exit);
+module_platform_driver(fsl_elbc_nand_driver);
 
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Freescale");