[PATCH v4 02/24] mtd: spinand: Use the spi-mem dirmap API
Frieder Schrempf
frieder.schrempf at kontron.de
Tue Aug 12 11:41:02 CEST 2025
Am 09.08.25 um 03:04 schrieb Mikhail Kshevetskiy:
> Make use of the spi-mem direct mapping API to let advanced controllers
> optimize read/write operations when they support direct mapping.
>
> Based on a linux commit 981d1aa0697ce1393e00933f154d181e965703d0
Please use the shortened SHA-1 as described in [1]. Same for the other
patches in this series. In this case:
$ git log --abbrev=12 --pretty="%h (\"%s\")" -1
981d1aa0697ce1393e00933f154d181e965703d0
981d1aa0697c ("mtd: spinand: Use the spi-mem dirmap API")
[1]
https://docs.kernel.org/process/submitting-patches.html#describe-your-changes
> (mtd: spinand: Use the spi-mem dirmap API)
> created by Boris Brezillon <bbrezillon at kernel.org> with additional
> fixes taken from linux-6.10.
Nitpick: Linux 6.10
>
> Signed-off-by: Mikhail Kshevetskiy <mikhail.kshevetskiy at iopsys.eu>
> ---
> drivers/mtd/nand/spi/core.c | 185 +++++++++++++++++-------------------
> include/linux/mtd/spinand.h | 7 ++
> 2 files changed, 95 insertions(+), 97 deletions(-)
>
> diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c
> index 3a1e7e18736..c1b8b9627f2 100644
> --- a/drivers/mtd/nand/spi/core.c
> +++ b/drivers/mtd/nand/spi/core.c
> @@ -41,21 +41,6 @@ struct spinand_plat {
> /* SPI NAND index visible in MTD names */
> static int spi_nand_idx;
>
> -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,
> @@ -249,27 +234,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;
> @@ -277,28 +256,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->slave, &op);
> - if (ret)
> + ret = spi_mem_dirmap_read(rdesc, column, nbytes, buf);
> + if (ret < 0)
> return ret;
>
> - ret = spi_mem_exec_op(spinand->slave, &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)
> @@ -322,14 +292,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;
> - unsigned int nbytes = 0;
> - void *buf = NULL;
> - u16 column = 0;
> - int ret;
> + struct spi_mem_dirmap_desc *wdesc;
> + unsigned int nbytes, column = 0;
> + void *buf = spinand->databuf;
> + ssize_t ret;
>
> /*
> * Looks like PROGRAM LOAD (AKA write cache) does not necessarily reset
> @@ -338,19 +306,12 @@ static int spinand_write_to_cache_op(struct spinand_device *spinand,
> * the data portion of the page, otherwise we might corrupt the BBM or
> * user data previously programmed in OOB area.
> */
> - memset(spinand->databuf, 0xff,
> - nanddev_page_size(nand) +
> - nanddev_per_page_oobsize(nand));
> + nbytes = nanddev_page_size(nand) + nanddev_per_page_oobsize(nand);
> + memset(spinand->databuf, 0xff, nbytes);
>
> - if (req->datalen) {
> + if (req->datalen)
> memcpy(spinand->databuf + req->dataoffs, req->databuf.out,
> req->datalen);
> - adjreq.dataoffs = 0;
> - adjreq.datalen = nanddev_page_size(nand);
> - adjreq.databuf.out = spinand->databuf;
> - nbytes = adjreq.datalen;
> - buf = spinand->databuf;
> - }
>
> if (req->ooblen) {
> if (req->mode == MTD_OPS_AUTO_OOB)
> @@ -361,52 +322,21 @@ static int spinand_write_to_cache_op(struct spinand_device *spinand,
> else
> memcpy(spinand->oobbuf + req->ooboffs, req->oobbuf.out,
> req->ooblen);
> -
> - adjreq.ooblen = nanddev_per_page_oobsize(nand);
> - adjreq.ooboffs = 0;
> - nbytes += nanddev_per_page_oobsize(nand);
> - if (!buf) {
> - buf = spinand->oobbuf;
> - column = nanddev_page_size(nand);
> - }
> }
>
> - spinand_cache_op_adjust_colum(spinand, &adjreq, &column);
> -
> - op = *spinand->op_templates.write_cache;
> - op.addr.val = column;
> + wdesc = spinand->dirmaps[req->pos.plane].wdesc;
>
> - /*
> - * 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->slave, &op);
> - if (ret)
> - return ret;
> -
> - ret = spi_mem_exec_op(spinand->slave, &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 resets
> - * 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;
> @@ -819,6 +749,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 = spi_mem_dirmap_create(spinand->slave, &info);
> + if (IS_ERR(desc))
> + return PTR_ERR(desc);
> +
> + spinand->dirmaps[plane].wdesc = desc;
> +
> + info.op_tmpl = *spinand->op_templates.read_cache;
> + desc = spi_mem_dirmap_create(spinand->slave, &info);
> + if (IS_ERR(desc)) {
> + spi_mem_dirmap_destroy(spinand->dirmaps[plane].wdesc);
> + 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->slave->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,
> @@ -1134,6 +1117,14 @@ static int spinand_init(struct spinand_device *spinand)
> goto err_free_bufs;
> }
>
> + ret = spinand_create_dirmaps(spinand);
> + if (ret) {
> + dev_err(spinand->slave->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);
> diff --git a/include/linux/mtd/spinand.h b/include/linux/mtd/spinand.h
> index 6fe6fd520a4..163269313f6 100644
> --- a/include/linux/mtd/spinand.h
> +++ b/include/linux/mtd/spinand.h
> @@ -363,6 +363,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
> @@ -406,6 +411,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;
More information about the U-Boot
mailing list