Signed-off-by: Mikhail Kshevetskiy <mikhail.kshevets...@iopsys.eu>
---
 drivers/mtd/nand/spi/core.c | 259 +++++++++++++++++++++---------------
 1 file changed, 151 insertions(+), 108 deletions(-)

diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c
index 8664e882753..1b2eefc9041 100644
--- a/drivers/mtd/nand/spi/core.c
+++ b/drivers/mtd/nand/spi/core.c
@@ -156,20 +156,12 @@ int spinand_select_target(struct spinand_device *spinand, 
unsigned int target)
        return 0;
 }
 
-static int spinand_init_cfg_cache(struct spinand_device *spinand)
+static int spinand_read_cfg(struct spinand_device *spinand)
 {
        struct nand_device *nand = spinand_to_nand(spinand);
-       struct udevice *dev = spinand->slave->dev;
        unsigned int target;
        int ret;
 
-       spinand->cfg_cache = devm_kzalloc(dev,
-                                         sizeof(*spinand->cfg_cache) *
-                                         nand->memorg.ntargets,
-                                         GFP_KERNEL);
-       if (!spinand->cfg_cache)
-               return -ENOMEM;
-
        for (target = 0; target < nand->memorg.ntargets; target++) {
                ret = spinand_select_target(spinand, target);
                if (ret)
@@ -188,6 +180,21 @@ static int spinand_init_cfg_cache(struct spinand_device 
*spinand)
        return 0;
 }
 
+static int spinand_init_cfg_cache(struct spinand_device *spinand)
+{
+       struct nand_device *nand = spinand_to_nand(spinand);
+       struct udevice *dev = spinand->slave->dev;
+
+       spinand->cfg_cache = devm_kcalloc(dev,
+                                         nand->memorg.ntargets,
+                                         sizeof(*spinand->cfg_cache),
+                                         GFP_KERNEL);
+       if (!spinand->cfg_cache)
+               return -ENOMEM;
+
+       return 0;
+}
+
 static int spinand_init_quad_enable(struct spinand_device *spinand)
 {
        bool enable = false;
@@ -211,6 +218,59 @@ static int spinand_ecc_enable(struct spinand_device 
*spinand,
                               enable ? CFG_ECC_ENABLE : 0);
 }
 
+static int spinand_check_ecc_status(struct spinand_device *spinand, u8 status)
+{
+       struct nand_device *nand = spinand_to_nand(spinand);
+
+       if (spinand->eccinfo.get_status)
+               return spinand->eccinfo.get_status(spinand, status);
+
+       switch (status & STATUS_ECC_MASK) {
+       case STATUS_ECC_NO_BITFLIPS:
+               return 0;
+
+       case STATUS_ECC_HAS_BITFLIPS:
+               /*
+                * We have no way to know exactly how many bitflips have been
+                * fixed, so let's return the maximum possible value so that
+                * wear-leveling layers move the data immediately.
+                */
+               return nand->eccreq.strength;
+
+       case STATUS_ECC_UNCOR_ERROR:
+               return -EBADMSG;
+
+       default:
+               break;
+       }
+
+       return -EINVAL;
+}
+
+static int spinand_noecc_ooblayout_ecc(struct mtd_info *mtd, int section,
+                                      struct mtd_oob_region *region)
+{
+       return -ERANGE;
+}
+
+static int spinand_noecc_ooblayout_free(struct mtd_info *mtd, int section,
+                                       struct mtd_oob_region *region)
+{
+       if (section)
+               return -ERANGE;
+
+       /* Reserve 2 bytes for the BBM. */
+       region->offset = 2;
+       region->length = 62;
+
+       return 0;
+}
+
+static const struct mtd_ooblayout_ops spinand_noecc_ooblayout = {
+       .ecc = spinand_noecc_ooblayout_ecc,
+       .rfree = spinand_noecc_ooblayout_free,
+};
+
 static int spinand_write_enable_op(struct spinand_device *spinand)
 {
        struct spi_mem_op op = SPINAND_WR_EN_DIS_OP(true);
@@ -232,7 +292,7 @@ static int spinand_read_from_cache_op(struct spinand_device 
*spinand,
                                      const struct nand_page_io_req *req)
 {
        struct nand_device *nand = spinand_to_nand(spinand);
-       struct mtd_info *mtd = nanddev_to_mtd(nand);
+       struct mtd_info *mtd = spinand_to_mtd(spinand);
        struct spi_mem_dirmap_desc *rdesc;
        unsigned int nbytes = 0;
        void *buf = NULL;
@@ -290,7 +350,7 @@ static int spinand_write_to_cache_op(struct spinand_device 
*spinand,
                                     const struct nand_page_io_req *req)
 {
        struct nand_device *nand = spinand_to_nand(spinand);
-       struct mtd_info *mtd = nanddev_to_mtd(nand);
+       struct mtd_info *mtd = spinand_to_mtd(spinand);
        struct spi_mem_dirmap_desc *wdesc;
        unsigned int nbytes, column = 0;
        void *buf = spinand->databuf;
@@ -352,7 +412,7 @@ static int spinand_program_op(struct spinand_device 
*spinand,
 static int spinand_erase_op(struct spinand_device *spinand,
                            const struct nand_pos *pos)
 {
-       struct nand_device *nand = &spinand->base;
+       struct nand_device *nand = spinand_to_nand(spinand);
        unsigned int row = nanddev_pos_to_row(nand, pos);
        struct spi_mem_op op = SPINAND_BLK_ERASE_OP(row);
 
@@ -402,9 +462,8 @@ out:
 static int spinand_read_id_op(struct spinand_device *spinand, u8 naddr,
                              u8 ndummy, u8 *buf)
 {
-       struct spi_mem_op op = SPINAND_READID_OP(naddr, ndummy,
-                                                spinand->scratchbuf,
-                                                SPINAND_MAX_ID_LEN);
+       struct spi_mem_op op = SPINAND_READID_OP(
+               naddr, ndummy, spinand->scratchbuf, SPINAND_MAX_ID_LEN);
        int ret;
 
        ret = spi_mem_exec_op(spinand->slave, &op);
@@ -434,35 +493,6 @@ static int spinand_lock_block(struct spinand_device 
*spinand, u8 lock)
        return spinand_write_reg_op(spinand, REG_BLOCK_LOCK, lock);
 }
 
-static int spinand_check_ecc_status(struct spinand_device *spinand, u8 status)
-{
-       struct nand_device *nand = spinand_to_nand(spinand);
-
-       if (spinand->eccinfo.get_status)
-               return spinand->eccinfo.get_status(spinand, status);
-
-       switch (status & STATUS_ECC_MASK) {
-       case STATUS_ECC_NO_BITFLIPS:
-               return 0;
-
-       case STATUS_ECC_HAS_BITFLIPS:
-               /*
-                * We have no way to know exactly how many bitflips have been
-                * fixed, so let's return the maximum possible value so that
-                * wear-leveling layers move the data immediately.
-                */
-               return nand->eccreq.strength;
-
-       case STATUS_ECC_UNCOR_ERROR:
-               return -EBADMSG;
-
-       default:
-               break;
-       }
-
-       return -EINVAL;
-}
-
 static int spinand_read_page(struct spinand_device *spinand,
                             const struct nand_page_io_req *req,
                             bool ecc_enabled)
@@ -524,30 +554,36 @@ static int spinand_mtd_read(struct mtd_info *mtd, loff_t 
from,
 {
        struct spinand_device *spinand = mtd_to_spinand(mtd);
        struct nand_device *nand = mtd_to_nanddev(mtd);
+       struct mtd_ecc_stats old_stats;
        unsigned int max_bitflips = 0;
        struct nand_io_iter iter;
-       bool enable_ecc = false;
+       bool disable_ecc = false;
        bool ecc_failed = false;
        int ret = 0;
 
-       if (ops->mode != MTD_OPS_RAW && spinand->eccinfo.ooblayout)
-               enable_ecc = true;
+       if (ops->mode == MTD_OPS_RAW || !spinand->eccinfo.ooblayout)
+               disable_ecc = true;
 
 #ifndef __UBOOT__
        mutex_lock(&spinand->lock);
 #endif
 
+       old_stats = mtd->ecc_stats;
+
        nanddev_io_for_each_page(nand, NAND_PAGE_READ, from, ops, &iter) {
                schedule();
+               if (disable_ecc)
+                       iter.req.mode = MTD_OPS_RAW;
+
                ret = spinand_select_target(spinand, iter.req.pos.target);
                if (ret)
                        break;
 
-               ret = spinand_ecc_enable(spinand, enable_ecc);
+               ret = spinand_ecc_enable(spinand, !disable_ecc);
                if (ret)
                        break;
 
-               ret = spinand_read_page(spinand, &iter.req, enable_ecc);
+               ret = spinand_read_page(spinand, &iter.req, !disable_ecc);
                if (ret < 0 && ret != -EBADMSG)
                        break;
 
@@ -564,6 +600,13 @@ static int spinand_mtd_read(struct mtd_info *mtd, loff_t 
from,
                ops->oobretlen += iter.req.ooblen;
        }
 
+       if (ops->stats) {
+               ops->stats->uncorrectable_errors +=
+                       mtd->ecc_stats.failed - old_stats.failed;
+               ops->stats->corrected_bitflips +=
+                       mtd->ecc_stats.corrected - old_stats.corrected;
+       }
+
 #ifndef __UBOOT__
        mutex_unlock(&spinand->lock);
 #endif
@@ -579,11 +622,11 @@ static int spinand_mtd_write(struct mtd_info *mtd, loff_t 
to,
        struct spinand_device *spinand = mtd_to_spinand(mtd);
        struct nand_device *nand = mtd_to_nanddev(mtd);
        struct nand_io_iter iter;
-       bool enable_ecc = false;
+       bool disable_ecc = false;
        int ret = 0;
 
-       if (ops->mode != MTD_OPS_RAW && mtd->ooblayout)
-               enable_ecc = true;
+       if (ops->mode == MTD_OPS_RAW || !mtd->ooblayout)
+               disable_ecc = true;
 
 #ifndef __UBOOT__
        mutex_lock(&spinand->lock);
@@ -591,11 +634,14 @@ static int spinand_mtd_write(struct mtd_info *mtd, loff_t 
to,
 
        nanddev_io_for_each_page(nand, NAND_PAGE_WRITE, to, ops, &iter) {
                schedule();
+               if (disable_ecc)
+                       iter.req.mode = MTD_OPS_RAW;
+
                ret = spinand_select_target(spinand, iter.req.pos.target);
                if (ret)
                        break;
 
-               ret = spinand_ecc_enable(spinand, enable_ecc);
+               ret = spinand_ecc_enable(spinand, !disable_ecc);
                if (ret)
                        break;
 
@@ -1039,35 +1085,55 @@ static int spinand_detect(struct spinand_device 
*spinand)
        return 0;
 }
 
-static int spinand_noecc_ooblayout_ecc(struct mtd_info *mtd, int section,
-                                      struct mtd_oob_region *region)
+static int spinand_init_flash(struct spinand_device *spinand)
 {
-       return -ERANGE;
-}
+       struct udevice *dev = spinand->slave->dev;
+       struct nand_device *nand = spinand_to_nand(spinand);
+       int ret, i;
 
-static int spinand_noecc_ooblayout_free(struct mtd_info *mtd, int section,
-                                       struct mtd_oob_region *region)
-{
-       if (section)
-               return -ERANGE;
+       ret = spinand_read_cfg(spinand);
+       if (ret)
+               return ret;
 
-       /* Reserve 2 bytes for the BBM. */
-       region->offset = 2;
-       region->length = 62;
+       ret = spinand_init_quad_enable(spinand);
+       if (ret)
+               return ret;
 
-       return 0;
-}
+       ret = spinand_upd_cfg(spinand, CFG_OTP_ENABLE, 0);
+       if (ret)
+               return ret;
 
-static const struct mtd_ooblayout_ops spinand_noecc_ooblayout = {
-       .ecc = spinand_noecc_ooblayout_ecc,
-       .rfree = spinand_noecc_ooblayout_free,
-};
+       ret = spinand_manufacturer_init(spinand);
+       if (ret) {
+               dev_err(dev,
+                       "Failed to initialize the SPI NAND chip (err = %d)\n",
+                       ret);
+               return ret;
+       }
+
+       /* After power up, all blocks are locked, so unlock them here. */
+       for (i = 0; i < nand->memorg.ntargets; i++) {
+               ret = spinand_select_target(spinand, i);
+               if (ret)
+                       break;
+
+               ret = spinand_lock_block(spinand, BL_ALL_UNLOCKED);
+               if (ret)
+                       break;
+       }
+
+       if (ret)
+               spinand_manufacturer_cleanup(spinand);
+
+       return ret;
+}
 
 static int spinand_init(struct spinand_device *spinand)
 {
+       struct udevice *dev = spinand->slave->dev;
        struct mtd_info *mtd = spinand_to_mtd(spinand);
        struct nand_device *nand = mtd_to_nanddev(mtd);
-       int ret, i;
+       int ret;
 
        /*
         * We need a scratch buffer because the spi_mem interface requires that
@@ -1100,49 +1166,14 @@ static int spinand_init(struct spinand_device *spinand)
        if (ret)
                goto err_free_bufs;
 
-       ret = spinand_init_quad_enable(spinand);
+       ret = spinand_init_flash(spinand);
        if (ret)
                goto err_free_bufs;
 
-       ret = spinand_upd_cfg(spinand, CFG_OTP_ENABLE, 0);
-       if (ret)
-               goto err_free_bufs;
-
-       ret = spinand_manufacturer_init(spinand);
-       if (ret) {
-               dev_err(spinand->slave->dev,
-                       "Failed to initialize the SPI NAND chip (err = %d)\n",
-                       ret);
-               goto err_free_bufs;
-       }
-
-       ret = spinand_create_dirmaps(spinand);
-       if (ret) {
-               dev_err(spinand->slave->dev,
-                       "Failed to create direct mappings for read/write 
operations (err = %d)\n",
-                       ret);
-               goto err_manuf_cleanup;
-       }
-
-       /* After power up, all blocks are locked, so unlock them here. */
-       for (i = 0; i < nand->memorg.ntargets; i++) {
-               ret = spinand_select_target(spinand, i);
-               if (ret)
-                       goto err_manuf_cleanup;
-
-               ret = spinand_lock_block(spinand, BL_ALL_UNLOCKED);
-               if (ret)
-                       goto err_manuf_cleanup;
-       }
-
        ret = nanddev_init(nand, &spinand_ops, THIS_MODULE);
        if (ret)
                goto err_manuf_cleanup;
 
-       /*
-        * Right now, we don't support ECC, so let the whole oob
-        * area is available for user.
-        */
        mtd->_read_oob = spinand_mtd_read;
        mtd->_write_oob = spinand_mtd_write;
        mtd->_block_isbad = spinand_mtd_block_isbad;
@@ -1161,8 +1192,18 @@ static int spinand_init(struct spinand_device *spinand)
 
        mtd->oobavail = ret;
 
+       ret = spinand_create_dirmaps(spinand);
+       if (ret) {
+               dev_err(dev,
+                       "Failed to create direct mappings for read/write 
operations (err = %d)\n",
+                       ret);
+               goto err_cleanup_ecc_engine;
+       }
+
        return 0;
 
+err_cleanup_ecc_engine:
+
 err_cleanup_nanddev:
        nanddev_cleanup(nand);
 
@@ -1262,12 +1303,14 @@ static const struct spi_device_id spinand_ids[] = {
        { .name = "spi-nand" },
        { /* sentinel */ },
 };
+MODULE_DEVICE_TABLE(spi, spinand_ids);
 
 #ifdef CONFIG_OF
 static const struct of_device_id spinand_of_ids[] = {
        { .compatible = "spi-nand" },
        { /* sentinel */ },
 };
+MODULE_DEVICE_TABLE(of, spinand_of_ids);
 #endif
 
 static struct spi_mem_driver spinand_drv = {
-- 
2.43.0

Reply via email to