mtd: spi-nor: use spi-mem dirmap API
authorSergei Shtylyov <sergei.shtylyov@cogentembedded.com>
Tue, 18 Feb 2020 21:24:10 +0000 (00:24 +0300)
committerTudor Ambarus <tudor.ambarus@microchip.com>
Wed, 19 Feb 2020 07:17:33 +0000 (09:17 +0200)
Make use of the spi-mem direct mapping API to let advanced controllers
optimize read/write operations when they support direct mapping.

Based on the original patch by Boris Brezillon
<boris.brezillon@bootlin.com>.

Signed-off-by: Sergei Shtylyov <sergei.shtylyov@cogentembedded.com>
Reviewed-by: Boris Brezillon <boris.brezillon@collabora.com>
Signed-off-by: Tudor Ambarus <tudor.ambarus@microchip.com>
drivers/mtd/spi-nor/spi-nor.c
include/linux/mtd/spi-nor.h

index 1ce9784d86e86e8db43ea8faa81df266f92c90bf..1224247b26cc494f482539d0642caa7329c79f16 100644 (file)
@@ -306,6 +306,7 @@ static ssize_t spi_nor_spimem_read_data(struct spi_nor *nor, loff_t from,
                           SPI_MEM_OP_DUMMY(nor->read_dummy, 1),
                           SPI_MEM_OP_DATA_IN(len, buf, 1));
        bool usebouncebuf;
+       ssize_t nbytes;
        int error;
 
        /* get transfer protocols. */
@@ -319,14 +320,20 @@ static ssize_t spi_nor_spimem_read_data(struct spi_nor *nor, loff_t from,
 
        usebouncebuf = spi_nor_spimem_bounce(nor, &op);
 
-       error = spi_nor_spimem_exec_op(nor, &op);
-       if (error)
-               return error;
+       if (nor->dirmap.rdesc) {
+               nbytes = spi_mem_dirmap_read(nor->dirmap.rdesc, op.addr.val,
+                                            op.data.nbytes, op.data.buf.in);
+       } else {
+               error = spi_nor_spimem_exec_op(nor, &op);
+               if (error)
+                       return error;
+               nbytes = op.data.nbytes;
+       }
 
-       if (usebouncebuf)
-               memcpy(buf, op.data.buf.in, op.data.nbytes);
+       if (usebouncebuf && nbytes > 0)
+               memcpy(buf, op.data.buf.in, nbytes);
 
-       return op.data.nbytes;
+       return nbytes;
 }
 
 /**
@@ -365,6 +372,7 @@ static ssize_t spi_nor_spimem_write_data(struct spi_nor *nor, loff_t to,
                           SPI_MEM_OP_ADDR(nor->addr_width, to, 1),
                           SPI_MEM_OP_NO_DUMMY,
                           SPI_MEM_OP_DATA_OUT(len, buf, 1));
+       ssize_t nbytes;
        int error;
 
        op.cmd.buswidth = spi_nor_get_protocol_inst_nbits(nor->write_proto);
@@ -377,11 +385,17 @@ static ssize_t spi_nor_spimem_write_data(struct spi_nor *nor, loff_t to,
        if (spi_nor_spimem_bounce(nor, &op))
                memcpy(nor->bouncebuf, buf, op.data.nbytes);
 
-       error = spi_nor_spimem_exec_op(nor, &op);
-       if (error)
-               return error;
+       if (nor->dirmap.wdesc) {
+               nbytes = spi_mem_dirmap_write(nor->dirmap.wdesc, op.addr.val,
+                                             op.data.nbytes, op.data.buf.out);
+       } else {
+               error = spi_nor_spimem_exec_op(nor, &op);
+               if (error)
+                       return error;
+               nbytes = op.data.nbytes;
+       }
 
-       return op.data.nbytes;
+       return nbytes;
 }
 
 /**
@@ -5265,6 +5279,58 @@ int spi_nor_scan(struct spi_nor *nor, const char *name,
 }
 EXPORT_SYMBOL_GPL(spi_nor_scan);
 
+static int spi_nor_create_read_dirmap(struct spi_nor *nor)
+{
+       struct spi_mem_dirmap_info info = {
+               .op_tmpl = SPI_MEM_OP(SPI_MEM_OP_CMD(nor->read_opcode, 1),
+                                     SPI_MEM_OP_ADDR(nor->addr_width, 0, 1),
+                                     SPI_MEM_OP_DUMMY(nor->read_dummy, 1),
+                                     SPI_MEM_OP_DATA_IN(0, NULL, 1)),
+               .offset = 0,
+               .length = nor->mtd.size,
+       };
+       struct spi_mem_op *op = &info.op_tmpl;
+
+       /* get transfer protocols. */
+       op->cmd.buswidth = spi_nor_get_protocol_inst_nbits(nor->read_proto);
+       op->addr.buswidth = spi_nor_get_protocol_addr_nbits(nor->read_proto);
+       op->dummy.buswidth = op->addr.buswidth;
+       op->data.buswidth = spi_nor_get_protocol_data_nbits(nor->read_proto);
+
+       /* convert the dummy cycles to the number of bytes */
+       op->dummy.nbytes = (nor->read_dummy * op->dummy.buswidth) / 8;
+
+       nor->dirmap.rdesc = devm_spi_mem_dirmap_create(nor->dev, nor->spimem,
+                                                      &info);
+       return PTR_ERR_OR_ZERO(nor->dirmap.rdesc);
+}
+
+static int spi_nor_create_write_dirmap(struct spi_nor *nor)
+{
+       struct spi_mem_dirmap_info info = {
+               .op_tmpl = SPI_MEM_OP(SPI_MEM_OP_CMD(nor->program_opcode, 1),
+                                     SPI_MEM_OP_ADDR(nor->addr_width, 0, 1),
+                                     SPI_MEM_OP_NO_DUMMY,
+                                     SPI_MEM_OP_DATA_OUT(0, NULL, 1)),
+               .offset = 0,
+               .length = nor->mtd.size,
+       };
+       struct spi_mem_op *op = &info.op_tmpl;
+
+       /* get transfer protocols. */
+       op->cmd.buswidth = spi_nor_get_protocol_inst_nbits(nor->write_proto);
+       op->addr.buswidth = spi_nor_get_protocol_addr_nbits(nor->write_proto);
+       op->dummy.buswidth = op->addr.buswidth;
+       op->data.buswidth = spi_nor_get_protocol_data_nbits(nor->write_proto);
+
+       if (nor->program_opcode == SPINOR_OP_AAI_WP && nor->sst_write_second)
+               op->addr.nbytes = 0;
+
+       nor->dirmap.wdesc = devm_spi_mem_dirmap_create(nor->dev, nor->spimem,
+                                                      &info);
+       return PTR_ERR_OR_ZERO(nor->dirmap.wdesc);
+}
+
 static int spi_nor_probe(struct spi_mem *spimem)
 {
        struct spi_device *spi = spimem->spi;
@@ -5326,6 +5392,14 @@ static int spi_nor_probe(struct spi_mem *spimem)
                        return -ENOMEM;
        }
 
+       ret = spi_nor_create_read_dirmap(nor);
+       if (ret)
+               return ret;
+
+       ret = spi_nor_create_write_dirmap(nor);
+       if (ret)
+               return ret;
+
        return mtd_device_register(&nor->mtd, data ? data->parts : NULL,
                                   data ? data->nr_parts : 0);
 }
index 61be6ed33097905b4b3f0ec50898e63b88218ca6..de90724f62f1766b83c5d9b1f35f4cc093319c5b 100644 (file)
@@ -580,6 +580,7 @@ struct flash_info;
  *                      The structure includes legacy flash parameters and
  *                      settings that can be overwritten by the spi_nor_fixups
  *                      hooks, or dynamically when parsing the SFDP tables.
+ * @dirmap:            pointers to struct spi_mem_dirmap_desc for reads/writes.
  * @priv:              the private data
  */
 struct spi_nor {
@@ -606,6 +607,11 @@ struct spi_nor {
 
        struct spi_nor_flash_parameter params;
 
+       struct {
+               struct spi_mem_dirmap_desc *rdesc;
+               struct spi_mem_dirmap_desc *wdesc;
+       } dirmap;
+
        void *priv;
 };