mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-05-08 12:52:40 -04:00
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:
committed by
Tudor Ambarus
parent
b7ad6be2ee
commit
df5c21002c
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user