mtd: spinand: Use the 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.

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>
This commit is contained in:
Boris Brezillon 2019-01-24 15:56:43 +01:00 committed by Miquel Raynal
parent 9e98c678c2
commit 981d1aa069
2 changed files with 91 additions and 84 deletions

View File

@ -19,21 +19,6 @@
#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)
ret = spi_mem_dirmap_write(wdesc, 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;
/*
* 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);

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