mtd: spi-nor: use spi-mem dirmap API

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>
This commit is contained in:
Sergei Shtylyov 2020-02-19 00:24:10 +03:00 committed by Tudor Ambarus
parent b7ad6be2ee
commit df5c21002c
No known key found for this signature in database
GPG key ID: 4B554F47A58D14E9
2 changed files with 90 additions and 10 deletions

View 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);
}

View 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;
};