]> asedeno.scripts.mit.edu Git - linux.git/commitdiff
mtd: spinand: Use the spi-mem dirmap API
authorBoris Brezillon <bbrezillon@kernel.org>
Thu, 24 Jan 2019 14:56:43 +0000 (15:56 +0100)
committerMiquel Raynal <miquel.raynal@bootlin.com>
Thu, 21 Mar 2019 15:44:51 +0000 (16:44 +0100)
Make use of the spi-mem direct mapping API to let advanced controllers
optimize read/write operations when they support direct mapping.

Signed-off-by: Boris Brezillon <bbrezillon@kernel.org>
Cc: Stefan Roese <sr@denx.de>
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
Tested-by: Stefan Roese <sr@denx.de>
drivers/mtd/nand/spi/core.c
include/linux/mtd/spinand.h

index fa87ae28cdfecd21fb6fb435fac35047a3b37325..fa54fe446b2d3eaf1dce9241c0af05bf7f4c16cb 100644 (file)
 #include <linux/spi/spi.h>
 #include <linux/spi/spi-mem.h>
 
-static void spinand_cache_op_adjust_colum(struct spinand_device *spinand,
-                                         const struct nand_page_io_req *req,
-                                         u16 *column)
-{
-       struct nand_device *nand = spinand_to_nand(spinand);
-       unsigned int shift;
-
-       if (nand->memorg.planes_per_lun < 2)
-               return;
-
-       /* The plane number is passed in MSB just above the column address */
-       shift = fls(nand->memorg.pagesize);
-       *column |= req->pos.plane << shift;
-}
-
 static int spinand_read_reg_op(struct spinand_device *spinand, u8 reg, u8 *val)
 {
        struct spi_mem_op op = SPINAND_GET_FEATURE_OP(reg,
@@ -227,27 +212,21 @@ static int spinand_load_page_op(struct spinand_device *spinand,
 static int spinand_read_from_cache_op(struct spinand_device *spinand,
                                      const struct nand_page_io_req *req)
 {
-       struct spi_mem_op op = *spinand->op_templates.read_cache;
        struct nand_device *nand = spinand_to_nand(spinand);
        struct mtd_info *mtd = nanddev_to_mtd(nand);
-       struct nand_page_io_req adjreq = *req;
+       struct spi_mem_dirmap_desc *rdesc;
        unsigned int nbytes = 0;
        void *buf = NULL;
        u16 column = 0;
-       int ret;
+       ssize_t ret;
 
        if (req->datalen) {
-               adjreq.datalen = nanddev_page_size(nand);
-               adjreq.dataoffs = 0;
-               adjreq.databuf.in = spinand->databuf;
                buf = spinand->databuf;
-               nbytes = adjreq.datalen;
+               nbytes = nanddev_page_size(nand);
+               column = 0;
        }
 
        if (req->ooblen) {
-               adjreq.ooblen = nanddev_per_page_oobsize(nand);
-               adjreq.ooboffs = 0;
-               adjreq.oobbuf.in = spinand->oobbuf;
                nbytes += nanddev_per_page_oobsize(nand);
                if (!buf) {
                        buf = spinand->oobbuf;
@@ -255,28 +234,19 @@ static int spinand_read_from_cache_op(struct spinand_device *spinand,
                }
        }
 
-       spinand_cache_op_adjust_colum(spinand, &adjreq, &column);
-       op.addr.val = column;
+       rdesc = spinand->dirmaps[req->pos.plane].rdesc;
 
-       /*
-        * Some controllers are limited in term of max RX data size. In this
-        * case, just repeat the READ_CACHE operation after updating the
-        * column.
-        */
        while (nbytes) {
-               op.data.buf.in = buf;
-               op.data.nbytes = nbytes;
-               ret = spi_mem_adjust_op_size(spinand->spimem, &op);
-               if (ret)
+               ret = spi_mem_dirmap_read(rdesc, column, nbytes, buf);
+               if (ret < 0)
                        return ret;
 
-               ret = spi_mem_exec_op(spinand->spimem, &op);
-               if (ret)
-                       return ret;
+               if (!ret || ret > nbytes)
+                       return -EIO;
 
-               buf += op.data.nbytes;
-               nbytes -= op.data.nbytes;
-               op.addr.val += op.data.nbytes;
+               nbytes -= ret;
+               column += ret;
+               buf += ret;
        }
 
        if (req->datalen)
@@ -300,14 +270,12 @@ static int spinand_read_from_cache_op(struct spinand_device *spinand,
 static int spinand_write_to_cache_op(struct spinand_device *spinand,
                                     const struct nand_page_io_req *req)
 {
-       struct spi_mem_op op = *spinand->op_templates.write_cache;
        struct nand_device *nand = spinand_to_nand(spinand);
        struct mtd_info *mtd = nanddev_to_mtd(nand);
-       struct nand_page_io_req adjreq = *req;
+       struct spi_mem_dirmap_desc *wdesc;
+       unsigned int nbytes, column = 0;
        void *buf = spinand->databuf;
-       unsigned int nbytes;
-       u16 column = 0;
-       int ret;
+       ssize_t ret;
 
        /*
         * Looks like PROGRAM LOAD (AKA write cache) does not necessarily reset
@@ -318,12 +286,6 @@ static int spinand_write_to_cache_op(struct spinand_device *spinand,
         */
        nbytes = nanddev_page_size(nand) + nanddev_per_page_oobsize(nand);
        memset(spinand->databuf, 0xff, nbytes);
-       adjreq.dataoffs = 0;
-       adjreq.datalen = nanddev_page_size(nand);
-       adjreq.databuf.out = spinand->databuf;
-       adjreq.ooblen = nanddev_per_page_oobsize(nand);
-       adjreq.ooboffs = 0;
-       adjreq.oobbuf.out = spinand->oobbuf;
 
        if (req->datalen)
                memcpy(spinand->databuf + req->dataoffs, req->databuf.out,
@@ -340,42 +302,19 @@ static int spinand_write_to_cache_op(struct spinand_device *spinand,
                               req->ooblen);
        }
 
-       spinand_cache_op_adjust_colum(spinand, &adjreq, &column);
+       wdesc = spinand->dirmaps[req->pos.plane].wdesc;
 
-       op = *spinand->op_templates.write_cache;
-       op.addr.val = column;
-
-       /*
-        * Some controllers are limited in term of max TX data size. In this
-        * case, split the operation into one LOAD CACHE and one or more
-        * LOAD RANDOM CACHE.
-        */
        while (nbytes) {
-               op.data.buf.out = buf;
-               op.data.nbytes = nbytes;
-
-               ret = spi_mem_adjust_op_size(spinand->spimem, &op);
-               if (ret)
-                       return ret;
-
-               ret = spi_mem_exec_op(spinand->spimem, &op);
-               if (ret)
+               ret = spi_mem_dirmap_write(wdesc, column, nbytes, buf);
+               if (ret < 0)
                        return ret;
 
-               buf += op.data.nbytes;
-               nbytes -= op.data.nbytes;
-               op.addr.val += op.data.nbytes;
+               if (!ret || ret > nbytes)
+                       return -EIO;
 
-               /*
-                * We need to use the RANDOM LOAD CACHE operation if there's
-                * more than one iteration, because the LOAD operation might
-                * reset the cache to 0xff.
-                */
-               if (nbytes) {
-                       column = op.addr.val;
-                       op = *spinand->op_templates.update_cache;
-                       op.addr.val = column;
-               }
+               nbytes -= ret;
+               column += ret;
+               buf += ret;
        }
 
        return 0;
@@ -755,6 +694,59 @@ static int spinand_mtd_block_isreserved(struct mtd_info *mtd, loff_t offs)
        return ret;
 }
 
+static int spinand_create_dirmap(struct spinand_device *spinand,
+                                unsigned int plane)
+{
+       struct nand_device *nand = spinand_to_nand(spinand);
+       struct spi_mem_dirmap_info info = {
+               .length = nanddev_page_size(nand) +
+                         nanddev_per_page_oobsize(nand),
+       };
+       struct spi_mem_dirmap_desc *desc;
+
+       /* The plane number is passed in MSB just above the column address */
+       info.offset = plane << fls(nand->memorg.pagesize);
+
+       info.op_tmpl = *spinand->op_templates.update_cache;
+       desc = devm_spi_mem_dirmap_create(&spinand->spimem->spi->dev,
+                                         spinand->spimem, &info);
+       if (IS_ERR(desc))
+               return PTR_ERR(desc);
+
+       spinand->dirmaps[plane].wdesc = desc;
+
+       info.op_tmpl = *spinand->op_templates.read_cache;
+       desc = devm_spi_mem_dirmap_create(&spinand->spimem->spi->dev,
+                                         spinand->spimem, &info);
+       if (IS_ERR(desc))
+               return PTR_ERR(desc);
+
+       spinand->dirmaps[plane].rdesc = desc;
+
+       return 0;
+}
+
+static int spinand_create_dirmaps(struct spinand_device *spinand)
+{
+       struct nand_device *nand = spinand_to_nand(spinand);
+       int i, ret;
+
+       spinand->dirmaps = devm_kzalloc(&spinand->spimem->spi->dev,
+                                       sizeof(*spinand->dirmaps) *
+                                       nand->memorg.planes_per_lun,
+                                       GFP_KERNEL);
+       if (!spinand->dirmaps)
+               return -ENOMEM;
+
+       for (i = 0; i < nand->memorg.planes_per_lun; i++) {
+               ret = spinand_create_dirmap(spinand, i);
+               if (ret)
+                       return ret;
+       }
+
+       return 0;
+}
+
 static const struct nand_ops spinand_ops = {
        .erase = spinand_erase,
        .markbad = spinand_markbad,
@@ -1012,6 +1004,14 @@ static int spinand_init(struct spinand_device *spinand)
                goto err_free_bufs;
        }
 
+       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_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);
index b92e2aa955b6f8f59fec6dfc802e15658b126b5c..507f7e289bd1596c746062a705455a9c4c5722e0 100644 (file)
@@ -302,6 +302,11 @@ struct spinand_info {
                __VA_ARGS__                                             \
        }
 
+struct spinand_dirmap {
+       struct spi_mem_dirmap_desc *wdesc;
+       struct spi_mem_dirmap_desc *rdesc;
+};
+
 /**
  * struct spinand_device - SPI NAND device instance
  * @base: NAND device instance
@@ -341,6 +346,8 @@ struct spinand_device {
                const struct spi_mem_op *update_cache;
        } op_templates;
 
+       struct spinand_dirmap *dirmaps;
+
        int (*select_target)(struct spinand_device *spinand,
                             unsigned int target);
        unsigned int cur_target;