* [PATCH RFC 0/8] mtd: spinand: Winbond continuous read support
@ 2025-12-05 19:38 Miquel Raynal
2025-12-05 19:38 ` [PATCH RFC 1/8] mtd: spinand: Drop a too strong limitation Miquel Raynal
` (7 more replies)
0 siblings, 8 replies; 12+ messages in thread
From: Miquel Raynal @ 2025-12-05 19:38 UTC (permalink / raw)
To: Mark Brown, Richard Weinberger, Vignesh Raghavendra,
Michael Walle
Cc: Tudor Ambarus, Pratyush Yadav, Thomas Petazzoni, Steam Lin,
Santhosh Kumar K, linux-spi, linux-kernel, linux-mtd,
Miquel Raynal
Hello,
SPI NAND continuous read support has already been added a few releases
ago, but only Macronix chips were benefiting from this support. Winbond
chips also have a continuous read feature, which is slightly more
complex to use in the scope of the Linux kernel, because they these
chips expect a different read from cache operation once in continuous
mode.
In order to be more flexible, this series changes the logic behind
dirmaps. Direct mappings used to be very static, not flexible. I am
proposing to change this and turn them in to slightly more dynamic
interfaces, where for instance we can:
- Enable/disable the correction (was previously handled by creating yet
another pair of direct mappings per target).
- Select one or another variant for the cache operations.
I propose to name the variants available in a direct mapping "primary"
and "secondary", and let the upper layer (SPI NOR or SPI NAND) point to
the one that needs to be used for the operation. Controller drivers
should not really care about this change, expect the fact that they
should not keep a static representation of the template on their
side. Because of that, I am creating a capability boolean to flag
drivers that support this capability (the flag is ignored in the
nodirmap case).
This series is sent as an RFC because it changes the way direct mappings
work, even though in practice I expect no regressions™. I am interested
to get an "early" feedback on it. I have also not yet experimentally
tested all combinations yet (last patch is only compile tested), and
because this will take a lot of time, I would like to validate the
approach first.
The series must be applied on top of the SPI NAND Octal DTR series, as I
wanted to include the octal DTR variants in my testing/benchmarks.
Link: https://lore.kernel.org/r/20251031-winbond-v6-17-rc1-oddr-v1-0-be42de23ebf1@bootlin.com
Here is a benchmark with the faster Winbond chip I have, W35N02JW on a
TI AM62a7 LP SK featuring the Cadence QSPI controller, clocked at
25MHz. Speed gain for a 10-page read is about +32% in octal SDR mode,
+47% for a 10-page read in octal DTR mode and up to +83% for a entire
block read!
1S-8S-8S, no continuous read:
64 page read speed is 15058 KiB/s
1S-8S-8S, with continuous reads:
1 page read speed is 15058 KiB/s
2 page read speed is 15058 KiB/s
3 page read speed is 16800 KiB/s
4 page read speed is 17066 KiB/s
5 page read speed is 18461 KiB/s
6 page read speed is 18461 KiB/s
7 page read speed is 19384 KiB/s
8 page read speed is 19692 KiB/s
9 page read speed is 19384 KiB/s
10 page read speed is 20000 KiB/s
11 page read speed is 20000 KiB/s
12 page read speed is 20000 KiB/s
13 page read speed is 20800 KiB/s
14 page read speed is 20363 KiB/s
15 page read speed is 20000 KiB/s
16 page read speed is 19692 KiB/s
32 page read speed is 19692 KiB/s
64 page read speed is 19692 KiB/s
8D-8D-8D, no continuous read:
64 page read speed is 23272 KiB/s
8D-8D-8D, with continuous read:
1 page read speed is 23272 KiB/s
2 page read speed is 23272 KiB/s
3 page read speed is 28000 KiB/s
4 page read speed is 32000 KiB/s
5 page read speed is 34285 KiB/s
6 page read speed is 34285 KiB/s
7 page read speed is 36000 KiB/s
8 page read speed is 36571 KiB/s
9 page read speed is 36000 KiB/s
10 page read speed is 34285 KiB/s
11 page read speed is 36666 KiB/s
12 page read speed is 40000 KiB/s
13 page read speed is 41600 KiB/s
14 page read speed is 37333 KiB/s
15 page read speed is 40000 KiB/s
16 page read speed is 36571 KiB/s
32 page read speed is 42666 KiB/s
64 page read speed is 42666 KiB/s
Thanks!
Miquèl
---
Miquel Raynal (8):
mtd: spinand: Drop a too strong limitation
mtd: spinand: Expose spinand_op_is_odtr()
mtd: spinand: Drop ECC dirmaps
spi: spi-mem: Transform the read operation template
spi: spi-mem: Create a secondary read operation
mtd: spinand: Use secondary ops for continuous reads
mtd: spinand: winbond: Add support for continuous reads on W35NxxJW
mtd: spinand: winbond: Add support for continuous reads on W25NxxJW
drivers/mtd/nand/spi/core.c | 125 ++++++++++++++---------
drivers/mtd/nand/spi/winbond.c | 227 ++++++++++++++++++++++++++++++++++++-----
drivers/mtd/spi-nor/core.c | 20 ++--
drivers/spi/spi-mem.c | 32 ++++--
include/linux/mtd/spinand.h | 16 ++-
include/linux/spi/spi-mem.h | 8 +-
6 files changed, 336 insertions(+), 92 deletions(-)
---
base-commit: dbb1c07d7654e3609ca892fcebafde7f33d67c38
change-id: 20251204-winbond-v6-18-rc1-cont-read-664791ddb263
Best regards,
--
Miquel Raynal <miquel.raynal@bootlin.com>
______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH RFC 1/8] mtd: spinand: Drop a too strong limitation
2025-12-05 19:38 [PATCH RFC 0/8] mtd: spinand: Winbond continuous read support Miquel Raynal
@ 2025-12-05 19:38 ` Miquel Raynal
2025-12-05 19:38 ` [PATCH RFC 2/8] mtd: spinand: Expose spinand_op_is_odtr() Miquel Raynal
` (6 subsequent siblings)
7 siblings, 0 replies; 12+ messages in thread
From: Miquel Raynal @ 2025-12-05 19:38 UTC (permalink / raw)
To: Mark Brown, Richard Weinberger, Vignesh Raghavendra,
Michael Walle
Cc: Tudor Ambarus, Pratyush Yadav, Thomas Petazzoni, Steam Lin,
Santhosh Kumar K, linux-spi, linux-kernel, linux-mtd,
Miquel Raynal
Since continuous reads may sometimes not be able to go past an erase
block boundary, it has been decided not to attempt longer reads and if
the user request is bigger, it will be split across eraseblocks.
As these request will anyway be handled correctly, there is no reason to
filter out cases where we would go over a target or a die, so drop this
limitation which had a side effect: any request to read more than the
content of an eraseblock would simply not benefit from the continuous
read feature.
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
---
drivers/mtd/nand/spi/core.c | 19 ++++++-------------
1 file changed, 6 insertions(+), 13 deletions(-)
diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c
index 49ee03a7252b..f19150740979 100644
--- a/drivers/mtd/nand/spi/core.c
+++ b/drivers/mtd/nand/spi/core.c
@@ -788,6 +788,12 @@ static int spinand_mtd_continuous_page_read(struct mtd_info *mtd, loff_t from,
* Each data read must be a multiple of 4-bytes and full pages should be read;
* otherwise, the data output might get out of sequence from one read command
* to another.
+ *
+ * Continuous reads never cross LUN boundaries. Some devices don't
+ * support crossing planes boundaries. Some devices don't even support
+ * crossing blocks boundaries. The common case being to read through UBI,
+ * we will very rarely read two consequent blocks or more, so let's only enable
+ * continuous reads when reading within the same erase block.
*/
nanddev_io_for_each_block(nand, NAND_PAGE_READ, from, ops, &iter) {
ret = spinand_select_target(spinand, iter.req.pos.target);
@@ -870,19 +876,6 @@ static bool spinand_use_cont_read(struct mtd_info *mtd, loff_t from,
nanddev_offs_to_pos(nand, from, &start_pos);
nanddev_offs_to_pos(nand, from + ops->len - 1, &end_pos);
- /*
- * Continuous reads never cross LUN boundaries. Some devices don't
- * support crossing planes boundaries. Some devices don't even support
- * crossing blocks boundaries. The common case being to read through UBI,
- * we will very rarely read two consequent blocks or more, so it is safer
- * and easier (can be improved) to only enable continuous reads when
- * reading within the same erase block.
- */
- if (start_pos.target != end_pos.target ||
- start_pos.plane != end_pos.plane ||
- start_pos.eraseblock != end_pos.eraseblock)
- return false;
-
return start_pos.page < end_pos.page;
}
--
2.51.1
______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH RFC 2/8] mtd: spinand: Expose spinand_op_is_odtr()
2025-12-05 19:38 [PATCH RFC 0/8] mtd: spinand: Winbond continuous read support Miquel Raynal
2025-12-05 19:38 ` [PATCH RFC 1/8] mtd: spinand: Drop a too strong limitation Miquel Raynal
@ 2025-12-05 19:38 ` Miquel Raynal
2025-12-05 19:38 ` [PATCH RFC 3/8] mtd: spinand: Drop ECC dirmaps Miquel Raynal
` (5 subsequent siblings)
7 siblings, 0 replies; 12+ messages in thread
From: Miquel Raynal @ 2025-12-05 19:38 UTC (permalink / raw)
To: Mark Brown, Richard Weinberger, Vignesh Raghavendra,
Michael Walle
Cc: Tudor Ambarus, Pratyush Yadav, Thomas Petazzoni, Steam Lin,
Santhosh Kumar K, linux-spi, linux-kernel, linux-mtd,
Miquel Raynal
This helper is going to be needed in a vendor driver, so expose it.
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
---
drivers/mtd/nand/spi/core.c | 2 +-
include/linux/mtd/spinand.h | 2 ++
2 files changed, 3 insertions(+), 1 deletion(-)
diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c
index f19150740979..3765fcc95c4a 100644
--- a/drivers/mtd/nand/spi/core.c
+++ b/drivers/mtd/nand/spi/core.c
@@ -1300,7 +1300,7 @@ static void spinand_manufacturer_cleanup(struct spinand_device *spinand)
return spinand->manufacturer->ops->cleanup(spinand);
}
-static bool spinand_op_is_odtr(const struct spi_mem_op *op)
+bool spinand_op_is_odtr(const struct spi_mem_op *op)
{
return op->cmd.dtr && op->cmd.buswidth == 8;
}
diff --git a/include/linux/mtd/spinand.h b/include/linux/mtd/spinand.h
index eb8ae164b3aa..a90e873cf693 100644
--- a/include/linux/mtd/spinand.h
+++ b/include/linux/mtd/spinand.h
@@ -944,6 +944,8 @@ static inline void spinand_set_of_node(struct spinand_device *spinand,
nanddev_set_of_node(&spinand->base, np);
}
+bool spinand_op_is_odtr(const struct spi_mem_op *op);
+
int spinand_match_and_init(struct spinand_device *spinand,
const struct spinand_info *table,
unsigned int table_size,
--
2.51.1
______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH RFC 3/8] mtd: spinand: Drop ECC dirmaps
2025-12-05 19:38 [PATCH RFC 0/8] mtd: spinand: Winbond continuous read support Miquel Raynal
2025-12-05 19:38 ` [PATCH RFC 1/8] mtd: spinand: Drop a too strong limitation Miquel Raynal
2025-12-05 19:38 ` [PATCH RFC 2/8] mtd: spinand: Expose spinand_op_is_odtr() Miquel Raynal
@ 2025-12-05 19:38 ` Miquel Raynal
2025-12-05 19:38 ` [PATCH RFC 4/8] spi: spi-mem: Transform the read operation template Miquel Raynal
` (4 subsequent siblings)
7 siblings, 0 replies; 12+ messages in thread
From: Miquel Raynal @ 2025-12-05 19:38 UTC (permalink / raw)
To: Mark Brown, Richard Weinberger, Vignesh Raghavendra,
Michael Walle
Cc: Tudor Ambarus, Pratyush Yadav, Thomas Petazzoni, Steam Lin,
Santhosh Kumar K, linux-spi, linux-kernel, linux-mtd,
Miquel Raynal
Direct mappings are very static concepts, which allow us to reuse a
template to perform reads or writes in a very efficient manner after a
single initialization. With the introduction of pipelined ECC engines
for SPI controllers, the need to differentiate between an operation with
and without correction has arised. The chosen solution at that time has
been to create new direct mappings for these operations, jumping from 2
to 4 dirmaps per target. Enabling ECC was done by choosing the correct
dirmap.
Today, we need to further parametrize dirmaps. With the goal to enable
continuous reads on a wider range of devices, we will need more
flexibility regarding the read from cache operation template to pick at
run time, for instance to use shorter "continuous read from cache"
variants.
We could create other direct mappings, but it would increase the matrix
by a power of two, bringing the theoretical number of dirmaps to
8 (read/write, ecc, shorter read variants) per target. This grow is not
sustainable, so let's change how dirmaps work - a little bit.
Operations already carry an ECC parameter, use it to indicate whether
error correction is required or not. In practice this change happens
only at the core level, SPI controller drivers do not care about the
direct mapping structure in this case, they just pick whatever is in the
template as a base. As a result, we allow the core to dynamically change
the content of the templates.
He who can do more can do less, so during the checking steps, make sure
to enable the ECC requirement just for the time of the checks.
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
---
drivers/mtd/nand/spi/core.c | 52 +++++++++++++++++----------------------------
include/linux/mtd/spinand.h | 2 --
2 files changed, 20 insertions(+), 34 deletions(-)
diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c
index 3765fcc95c4a..086c9f293373 100644
--- a/drivers/mtd/nand/spi/core.c
+++ b/drivers/mtd/nand/spi/core.c
@@ -397,10 +397,13 @@ static int spinand_read_from_cache_op(struct spinand_device *spinand,
}
}
- if (req->mode == MTD_OPS_RAW)
- rdesc = spinand->dirmaps[req->pos.plane].rdesc;
+ rdesc = spinand->dirmaps[req->pos.plane].rdesc;
+
+ if (nand->ecc.engine->integration == NAND_ECC_ENGINE_INTEGRATION_PIPELINED &&
+ req->mode != MTD_OPS_RAW)
+ rdesc->info.op_tmpl.data.ecc = true;
else
- rdesc = spinand->dirmaps[req->pos.plane].rdesc_ecc;
+ rdesc->info.op_tmpl.data.ecc = false;
if (spinand->flags & SPINAND_HAS_READ_PLANE_SELECT_BIT)
column |= req->pos.plane << fls(nanddev_page_size(nand));
@@ -489,10 +492,13 @@ static int spinand_write_to_cache_op(struct spinand_device *spinand,
req->ooblen);
}
- if (req->mode == MTD_OPS_RAW)
- wdesc = spinand->dirmaps[req->pos.plane].wdesc;
+ wdesc = spinand->dirmaps[req->pos.plane].wdesc;
+
+ if (nand->ecc.engine->integration == NAND_ECC_ENGINE_INTEGRATION_PIPELINED &&
+ req->mode != MTD_OPS_RAW)
+ wdesc->info.op_tmpl.data.ecc = true;
else
- wdesc = spinand->dirmaps[req->pos.plane].wdesc_ecc;
+ wdesc->info.op_tmpl.data.ecc = false;
if (spinand->flags & SPINAND_HAS_PROG_PLANE_SELECT_BIT)
column |= req->pos.plane << fls(nanddev_page_size(nand));
@@ -1133,12 +1139,17 @@ static int spinand_create_dirmap(struct spinand_device *spinand,
struct nand_device *nand = spinand_to_nand(spinand);
struct spi_mem_dirmap_info info = { 0 };
struct spi_mem_dirmap_desc *desc;
+ bool enable_ecc = false;
+
+ if (nand->ecc.engine->integration == NAND_ECC_ENGINE_INTEGRATION_PIPELINED)
+ enable_ecc = true;
/* The plane number is passed in MSB just above the column address */
info.offset = plane << fls(nand->memorg.pagesize);
+ /* Write descriptor */
info.length = nanddev_page_size(nand) + nanddev_per_page_oobsize(nand);
- info.op_tmpl = *spinand->op_templates->update_cache;
+ info.op_tmpl.data.ecc = enable_ecc;
desc = devm_spi_mem_dirmap_create(&spinand->spimem->spi->dev,
spinand->spimem, &info);
if (IS_ERR(desc))
@@ -1146,38 +1157,15 @@ static int spinand_create_dirmap(struct spinand_device *spinand,
spinand->dirmaps[plane].wdesc = desc;
+ /* Read descriptor */
info.op_tmpl = *spinand->op_templates->read_cache;
+ info.op_tmpl.data.ecc = enable_ecc;
desc = spinand_create_rdesc(spinand, &info);
if (IS_ERR(desc))
return PTR_ERR(desc);
spinand->dirmaps[plane].rdesc = desc;
- if (nand->ecc.engine->integration != NAND_ECC_ENGINE_INTEGRATION_PIPELINED) {
- spinand->dirmaps[plane].wdesc_ecc = spinand->dirmaps[plane].wdesc;
- spinand->dirmaps[plane].rdesc_ecc = spinand->dirmaps[plane].rdesc;
-
- return 0;
- }
-
- info.length = nanddev_page_size(nand) + nanddev_per_page_oobsize(nand);
- info.op_tmpl = *spinand->op_templates->update_cache;
- info.op_tmpl.data.ecc = true;
- 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_ecc = desc;
-
- info.op_tmpl = *spinand->op_templates->read_cache;
- info.op_tmpl.data.ecc = true;
- desc = spinand_create_rdesc(spinand, &info);
- if (IS_ERR(desc))
- return PTR_ERR(desc);
-
- spinand->dirmaps[plane].rdesc_ecc = desc;
-
return 0;
}
diff --git a/include/linux/mtd/spinand.h b/include/linux/mtd/spinand.h
index a90e873cf693..5ca1181048f7 100644
--- a/include/linux/mtd/spinand.h
+++ b/include/linux/mtd/spinand.h
@@ -681,8 +681,6 @@ struct spinand_info {
struct spinand_dirmap {
struct spi_mem_dirmap_desc *wdesc;
struct spi_mem_dirmap_desc *rdesc;
- struct spi_mem_dirmap_desc *wdesc_ecc;
- struct spi_mem_dirmap_desc *rdesc_ecc;
};
/**
--
2.51.1
______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH RFC 4/8] spi: spi-mem: Transform the read operation template
2025-12-05 19:38 [PATCH RFC 0/8] mtd: spinand: Winbond continuous read support Miquel Raynal
` (2 preceding siblings ...)
2025-12-05 19:38 ` [PATCH RFC 3/8] mtd: spinand: Drop ECC dirmaps Miquel Raynal
@ 2025-12-05 19:38 ` Miquel Raynal
2025-12-11 2:03 ` Mark Brown
2025-12-05 19:38 ` [PATCH RFC 5/8] spi: spi-mem: Create a secondary read operation Miquel Raynal
` (3 subsequent siblings)
7 siblings, 1 reply; 12+ messages in thread
From: Miquel Raynal @ 2025-12-05 19:38 UTC (permalink / raw)
To: Mark Brown, Richard Weinberger, Vignesh Raghavendra,
Michael Walle
Cc: Tudor Ambarus, Pratyush Yadav, Thomas Petazzoni, Steam Lin,
Santhosh Kumar K, linux-spi, linux-kernel, linux-mtd,
Miquel Raynal
As of now, we only use a single operation template when creating SPI
memory direct mappings. With the idea to extend this possibility to 2,
rename the template to reflect that we are currently setting the
"primary" operation, and create a pointer in the same structure to point
to it.
From a user point of view, the op_tmpl name remains but becomes a
pointer, leading to minor changes in both the SPI NAND and SPI NOR
cores.
There is no functional change.
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
---
drivers/mtd/nand/spi/core.c | 15 ++++++++-------
drivers/mtd/spi-nor/core.c | 20 ++++++++++----------
drivers/spi/spi-mem.c | 15 ++++++++-------
include/linux/spi/spi-mem.h | 3 ++-
4 files changed, 28 insertions(+), 25 deletions(-)
diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c
index 086c9f293373..209146f21326 100644
--- a/drivers/mtd/nand/spi/core.c
+++ b/drivers/mtd/nand/spi/core.c
@@ -401,9 +401,9 @@ static int spinand_read_from_cache_op(struct spinand_device *spinand,
if (nand->ecc.engine->integration == NAND_ECC_ENGINE_INTEGRATION_PIPELINED &&
req->mode != MTD_OPS_RAW)
- rdesc->info.op_tmpl.data.ecc = true;
+ rdesc->info.op_tmpl->data.ecc = true;
else
- rdesc->info.op_tmpl.data.ecc = false;
+ rdesc->info.op_tmpl->data.ecc = false;
if (spinand->flags & SPINAND_HAS_READ_PLANE_SELECT_BIT)
column |= req->pos.plane << fls(nanddev_page_size(nand));
@@ -496,9 +496,9 @@ static int spinand_write_to_cache_op(struct spinand_device *spinand,
if (nand->ecc.engine->integration == NAND_ECC_ENGINE_INTEGRATION_PIPELINED &&
req->mode != MTD_OPS_RAW)
- wdesc->info.op_tmpl.data.ecc = true;
+ wdesc->info.op_tmpl->data.ecc = true;
else
- wdesc->info.op_tmpl.data.ecc = false;
+ wdesc->info.op_tmpl->data.ecc = false;
if (spinand->flags & SPINAND_HAS_PROG_PLANE_SELECT_BIT)
column |= req->pos.plane << fls(nanddev_page_size(nand));
@@ -1149,7 +1149,8 @@ static int spinand_create_dirmap(struct spinand_device *spinand,
/* Write descriptor */
info.length = nanddev_page_size(nand) + nanddev_per_page_oobsize(nand);
- info.op_tmpl.data.ecc = enable_ecc;
+ info.primary_op_tmpl = *spinand->op_templates->update_cache;
+ info.primary_op_tmpl.data.ecc = enable_ecc;
desc = devm_spi_mem_dirmap_create(&spinand->spimem->spi->dev,
spinand->spimem, &info);
if (IS_ERR(desc))
@@ -1158,8 +1159,8 @@ static int spinand_create_dirmap(struct spinand_device *spinand,
spinand->dirmaps[plane].wdesc = desc;
/* Read descriptor */
- info.op_tmpl = *spinand->op_templates->read_cache;
- info.op_tmpl.data.ecc = enable_ecc;
+ info.primary_op_tmpl = *spinand->op_templates->read_cache;
+ info.primary_op_tmpl.data.ecc = enable_ecc;
desc = spinand_create_rdesc(spinand, &info);
if (IS_ERR(desc))
return PTR_ERR(desc);
diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c
index 20ea80450f22..4de0a2b66e56 100644
--- a/drivers/mtd/spi-nor/core.c
+++ b/drivers/mtd/spi-nor/core.c
@@ -3631,14 +3631,14 @@ 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, 0),
- SPI_MEM_OP_ADDR(nor->addr_nbytes, 0, 0),
- SPI_MEM_OP_DUMMY(nor->read_dummy, 0),
- SPI_MEM_OP_DATA_IN(0, NULL, 0)),
+ .primary_op_tmpl = SPI_MEM_OP(SPI_MEM_OP_CMD(nor->read_opcode, 0),
+ SPI_MEM_OP_ADDR(nor->addr_nbytes, 0, 0),
+ SPI_MEM_OP_DUMMY(nor->read_dummy, 0),
+ SPI_MEM_OP_DATA_IN(0, NULL, 0)),
.offset = 0,
.length = nor->params->size,
};
- struct spi_mem_op *op = &info.op_tmpl;
+ struct spi_mem_op *op = info.op_tmpl;
spi_nor_spimem_setup_op(nor, op, nor->read_proto);
@@ -3662,14 +3662,14 @@ static int spi_nor_create_read_dirmap(struct spi_nor *nor)
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, 0),
- SPI_MEM_OP_ADDR(nor->addr_nbytes, 0, 0),
- SPI_MEM_OP_NO_DUMMY,
- SPI_MEM_OP_DATA_OUT(0, NULL, 0)),
+ .primary_op_tmpl = SPI_MEM_OP(SPI_MEM_OP_CMD(nor->program_opcode, 0),
+ SPI_MEM_OP_ADDR(nor->addr_nbytes, 0, 0),
+ SPI_MEM_OP_NO_DUMMY,
+ SPI_MEM_OP_DATA_OUT(0, NULL, 0)),
.offset = 0,
.length = nor->params->size,
};
- struct spi_mem_op *op = &info.op_tmpl;
+ struct spi_mem_op *op = info.op_tmpl;
if (nor->program_opcode == SPINOR_OP_AAI_WP && nor->sst_write_second)
op->addr.nbytes = 0;
diff --git a/drivers/spi/spi-mem.c b/drivers/spi/spi-mem.c
index 71e3eaf59df9..50f16943dc73 100644
--- a/drivers/spi/spi-mem.c
+++ b/drivers/spi/spi-mem.c
@@ -642,7 +642,7 @@ EXPORT_SYMBOL_GPL(spi_mem_calc_op_duration);
static ssize_t spi_mem_no_dirmap_read(struct spi_mem_dirmap_desc *desc,
u64 offs, size_t len, void *buf)
{
- struct spi_mem_op op = desc->info.op_tmpl;
+ struct spi_mem_op op = *desc->info.op_tmpl;
int ret;
op.addr.val = desc->info.offset + offs;
@@ -662,7 +662,7 @@ static ssize_t spi_mem_no_dirmap_read(struct spi_mem_dirmap_desc *desc,
static ssize_t spi_mem_no_dirmap_write(struct spi_mem_dirmap_desc *desc,
u64 offs, size_t len, const void *buf)
{
- struct spi_mem_op op = desc->info.op_tmpl;
+ struct spi_mem_op op = *desc->info.op_tmpl;
int ret;
op.addr.val = desc->info.offset + offs;
@@ -701,11 +701,11 @@ spi_mem_dirmap_create(struct spi_mem *mem,
int ret = -ENOTSUPP;
/* Make sure the number of address cycles is between 1 and 8 bytes. */
- if (!info->op_tmpl.addr.nbytes || info->op_tmpl.addr.nbytes > 8)
+ if (!info->primary_op_tmpl.addr.nbytes || info->primary_op_tmpl.addr.nbytes > 8)
return ERR_PTR(-EINVAL);
/* data.dir should either be SPI_MEM_DATA_IN or SPI_MEM_DATA_OUT. */
- if (info->op_tmpl.data.dir == SPI_MEM_NO_DATA)
+ if (info->primary_op_tmpl.data.dir == SPI_MEM_NO_DATA)
return ERR_PTR(-EINVAL);
desc = kzalloc(sizeof(*desc), GFP_KERNEL);
@@ -714,12 +714,13 @@ spi_mem_dirmap_create(struct spi_mem *mem,
desc->mem = mem;
desc->info = *info;
+ desc->info.op_tmpl = &desc->info.primary_op_tmpl;
if (ctlr->mem_ops && ctlr->mem_ops->dirmap_create)
ret = ctlr->mem_ops->dirmap_create(desc);
if (ret) {
desc->nodirmap = true;
- if (!spi_mem_supports_op(desc->mem, &desc->info.op_tmpl))
+ if (!spi_mem_supports_op(desc->mem, &desc->info.primary_op_tmpl))
ret = -EOPNOTSUPP;
else
ret = 0;
@@ -843,7 +844,7 @@ ssize_t spi_mem_dirmap_read(struct spi_mem_dirmap_desc *desc,
struct spi_controller *ctlr = desc->mem->spi->controller;
ssize_t ret;
- if (desc->info.op_tmpl.data.dir != SPI_MEM_DATA_IN)
+ if (desc->info.op_tmpl->data.dir != SPI_MEM_DATA_IN)
return -EINVAL;
if (!len)
@@ -889,7 +890,7 @@ ssize_t spi_mem_dirmap_write(struct spi_mem_dirmap_desc *desc,
struct spi_controller *ctlr = desc->mem->spi->controller;
ssize_t ret;
- if (desc->info.op_tmpl.data.dir != SPI_MEM_DATA_OUT)
+ if (desc->info.op_tmpl->data.dir != SPI_MEM_DATA_OUT)
return -EINVAL;
if (!len)
diff --git a/include/linux/spi/spi-mem.h b/include/linux/spi/spi-mem.h
index e4db0924898c..3092caefa0b6 100644
--- a/include/linux/spi/spi-mem.h
+++ b/include/linux/spi/spi-mem.h
@@ -217,7 +217,8 @@ struct spi_mem_op {
* direction is directly encoded in the ->op_tmpl.data.dir field.
*/
struct spi_mem_dirmap_info {
- struct spi_mem_op op_tmpl;
+ struct spi_mem_op *op_tmpl;
+ struct spi_mem_op primary_op_tmpl;
u64 offset;
u64 length;
};
--
2.51.1
______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH RFC 5/8] spi: spi-mem: Create a secondary read operation
2025-12-05 19:38 [PATCH RFC 0/8] mtd: spinand: Winbond continuous read support Miquel Raynal
` (3 preceding siblings ...)
2025-12-05 19:38 ` [PATCH RFC 4/8] spi: spi-mem: Transform the read operation template Miquel Raynal
@ 2025-12-05 19:38 ` Miquel Raynal
2025-12-11 2:06 ` Mark Brown
2025-12-05 19:38 ` [PATCH RFC 6/8] mtd: spinand: Use secondary ops for continuous reads Miquel Raynal
` (2 subsequent siblings)
7 siblings, 1 reply; 12+ messages in thread
From: Miquel Raynal @ 2025-12-05 19:38 UTC (permalink / raw)
To: Mark Brown, Richard Weinberger, Vignesh Raghavendra,
Michael Walle
Cc: Tudor Ambarus, Pratyush Yadav, Thomas Petazzoni, Steam Lin,
Santhosh Kumar K, linux-spi, linux-kernel, linux-mtd,
Miquel Raynal
In some situations, direct mappings may need to use different
operation templates.
For instance, when enabling continuous reads, Winbond SPI NANDs no
longer expect address cycles because they would be ignoring them
otherwise. Hence, right after the command opcode, they start counting
dummy cycles, followed by the data cycles as usual.
This breaks the assumptions of "reads from cache" always being done
identically once the best variant has been picked up, across the
lifetime of the system.
In order to support this feature, we must give direct mapping more than
a single operation template to use, in order to switch to using
secondary operations upon request by the upper layer.
Create the concept of optional secondary operation template, which may
or may not be fulfilled by the SPI NAND and SPI NOR cores. If the
underlying SPI controller does not leverage any kind of direct mapping
acceleration, the feature has no impact and can be freely
used. Otherwise, the controller driver needs to opt-in for using this
feature, if supported.
The condition checked to know whether a secondary operation has been
provided or not is to look for a non zero opcode to limit the creation
of extra variables. In practice, the opcode 0x00 exist, but is not
related to any cache related operation.
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
---
The choice of defining two variables named primary and secondary instead
of using an array of templates is on purpose, to simplify the reading. I
find less obvious the use of an array here but this is personal taste.
---
drivers/spi/spi-mem.c | 17 +++++++++++++++++
include/linux/spi/spi-mem.h | 5 +++++
2 files changed, 22 insertions(+)
diff --git a/drivers/spi/spi-mem.c b/drivers/spi/spi-mem.c
index 50f16943dc73..b6debc796cf8 100644
--- a/drivers/spi/spi-mem.c
+++ b/drivers/spi/spi-mem.c
@@ -708,6 +708,23 @@ spi_mem_dirmap_create(struct spi_mem *mem,
if (info->primary_op_tmpl.data.dir == SPI_MEM_NO_DATA)
return ERR_PTR(-EINVAL);
+ /* Apply similar constraints to the secondary template */
+ if (info->secondary_op_tmpl.cmd.opcode) {
+ if (!info->secondary_op_tmpl.addr.nbytes ||
+ info->secondary_op_tmpl.addr.nbytes > 8)
+ return ERR_PTR(-EINVAL);
+
+ if (info->secondary_op_tmpl.data.dir == SPI_MEM_NO_DATA)
+ return ERR_PTR(-EINVAL);
+
+ if (!spi_mem_supports_op(mem, &info->secondary_op_tmpl))
+ return ERR_PTR(-EOPNOTSUPP);
+
+ if (ctlr->mem_ops && ctlr->mem_ops->dirmap_create &&
+ !spi_mem_controller_is_capable(ctlr, secondary_op_tmpl))
+ return ERR_PTR(-EOPNOTSUPP);
+ }
+
desc = kzalloc(sizeof(*desc), GFP_KERNEL);
if (!desc)
return ERR_PTR(-ENOMEM);
diff --git a/include/linux/spi/spi-mem.h b/include/linux/spi/spi-mem.h
index 3092caefa0b6..099de23e7084 100644
--- a/include/linux/spi/spi-mem.h
+++ b/include/linux/spi/spi-mem.h
@@ -207,6 +207,8 @@ struct spi_mem_op {
* struct spi_mem_dirmap_info - Direct mapping information
* @op_tmpl: operation template that should be used by the direct mapping when
* the memory device is accessed
+ * @secondary_op_tmpl: secondary template, may be used as an alternative to the
+ * primary template (decided by the upper layer)
* @offset: absolute offset this direct mapping is pointing to
* @length: length in byte of this direct mapping
*
@@ -219,6 +221,7 @@ struct spi_mem_op {
struct spi_mem_dirmap_info {
struct spi_mem_op *op_tmpl;
struct spi_mem_op primary_op_tmpl;
+ struct spi_mem_op secondary_op_tmpl;
u64 offset;
u64 length;
};
@@ -362,12 +365,14 @@ struct spi_controller_mem_ops {
* @swap16: Supports swapping bytes on a 16 bit boundary when configured in
* Octal DTR
* @per_op_freq: Supports per operation frequency switching
+ * @secondary_op_tmpl: Supports leveraging a secondary memory operation template
*/
struct spi_controller_mem_caps {
bool dtr;
bool ecc;
bool swap16;
bool per_op_freq;
+ bool secondary_op_tmpl;
};
#define spi_mem_controller_is_capable(ctlr, cap) \
--
2.51.1
______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH RFC 6/8] mtd: spinand: Use secondary ops for continuous reads
2025-12-05 19:38 [PATCH RFC 0/8] mtd: spinand: Winbond continuous read support Miquel Raynal
` (4 preceding siblings ...)
2025-12-05 19:38 ` [PATCH RFC 5/8] spi: spi-mem: Create a secondary read operation Miquel Raynal
@ 2025-12-05 19:38 ` Miquel Raynal
2025-12-05 19:38 ` [PATCH RFC 7/8] mtd: spinand: winbond: Add support for continuous reads on W35NxxJW Miquel Raynal
2025-12-05 19:38 ` [PATCH RFC 8/8] mtd: spinand: winbond: Add support for continuous reads on W25NxxJW Miquel Raynal
7 siblings, 0 replies; 12+ messages in thread
From: Miquel Raynal @ 2025-12-05 19:38 UTC (permalink / raw)
To: Mark Brown, Richard Weinberger, Vignesh Raghavendra,
Michael Walle
Cc: Tudor Ambarus, Pratyush Yadav, Thomas Petazzoni, Steam Lin,
Santhosh Kumar K, linux-spi, linux-kernel, linux-mtd,
Miquel Raynal
In case a chip supports continuous reads, but uses a slightly different
cache operation for these, it may provide a secondary operation template
which will be used only during continuous cache read operations.
From a vendor driver point of view, enabling this feature implies
providing a new set of templates for these continuous read
operations. The core will automatically pick the fastest variant,
depending on the hardware capabilities.
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
---
drivers/mtd/nand/spi/core.c | 51 ++++++++++++++++++++++++++++++++++++++++++++-
include/linux/mtd/spinand.h | 12 +++++++++++
2 files changed, 62 insertions(+), 1 deletion(-)
diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c
index 209146f21326..37a0d0373942 100644
--- a/drivers/mtd/nand/spi/core.c
+++ b/drivers/mtd/nand/spi/core.c
@@ -399,6 +399,11 @@ static int spinand_read_from_cache_op(struct spinand_device *spinand,
rdesc = spinand->dirmaps[req->pos.plane].rdesc;
+ if (spinand->op_templates->cont_read_cache && req->continuous)
+ rdesc->info.op_tmpl = &rdesc->info.secondary_op_tmpl;
+ else
+ rdesc->info.op_tmpl = &rdesc->info.primary_op_tmpl;
+
if (nand->ecc.engine->integration == NAND_ECC_ENGINE_INTEGRATION_PIPELINED &&
req->mode != MTD_OPS_RAW)
rdesc->info.op_tmpl->data.ecc = true;
@@ -1123,6 +1128,7 @@ static struct spi_mem_dirmap_desc *spinand_create_rdesc(
* its spi controller, use regular reading
*/
spinand->cont_read_possible = false;
+ memset(&info->secondary_op_tmpl, 0, sizeof(info->secondary_op_tmpl));
info->length = nanddev_page_size(nand) +
nanddev_per_page_oobsize(nand);
@@ -1139,11 +1145,24 @@ static int spinand_create_dirmap(struct spinand_device *spinand,
struct nand_device *nand = spinand_to_nand(spinand);
struct spi_mem_dirmap_info info = { 0 };
struct spi_mem_dirmap_desc *desc;
- bool enable_ecc = false;
+ bool enable_ecc = false, secondary_op = false;
if (nand->ecc.engine->integration == NAND_ECC_ENGINE_INTEGRATION_PIPELINED)
enable_ecc = true;
+ if (spinand->cont_read_possible && spinand->op_templates->cont_read_cache)
+ secondary_op = true;
+
+ /*
+ * Continuous read implies that only the main data is retrieved, backed
+ * by an on-die ECC engine. It is not possible to use a pipelind ECC
+ * engine with continuous read.
+ */
+ if (enable_ecc && secondary_op) {
+ secondary_op = false;
+ spinand->cont_read_possible = false;
+ }
+
/* The plane number is passed in MSB just above the column address */
info.offset = plane << fls(nand->memorg.pagesize);
@@ -1161,6 +1180,10 @@ static int spinand_create_dirmap(struct spinand_device *spinand,
/* Read descriptor */
info.primary_op_tmpl = *spinand->op_templates->read_cache;
info.primary_op_tmpl.data.ecc = enable_ecc;
+ if (secondary_op) {
+ info.secondary_op_tmpl = *spinand->op_templates->cont_read_cache;
+ info.secondary_op_tmpl.data.ecc = enable_ecc;
+ }
desc = spinand_create_rdesc(spinand, &info);
if (IS_ERR(desc))
return PTR_ERR(desc);
@@ -1505,6 +1528,27 @@ int spinand_match_and_init(struct spinand_device *spinand,
if (ret)
return ret;
+ op = spinand_select_op_variant(spinand, SSDR,
+ info->op_variants.cont_read_cache);
+ if (op) {
+ const struct spi_mem_op *read_op = spinand->ssdr_op_templates.read_cache;
+
+ /*
+ * Sometimes the fastest continuous read variant may not
+ * be supported. In this case, prefer to use the fastest
+ * read from cache variant and disable continuous reads.
+ */
+ if (read_op->cmd.buswidth != op->cmd.buswidth ||
+ read_op->cmd.dtr != op->cmd.dtr ||
+ read_op->addr.buswidth != op->addr.buswidth ||
+ read_op->addr.dtr != op->addr.dtr ||
+ read_op->cmd.buswidth != op->cmd.buswidth ||
+ read_op->cmd.dtr != op->cmd.dtr)
+ spinand->cont_read_possible = false;
+ else
+ spinand->ssdr_op_templates.cont_read_cache = op;
+ }
+
/* I/O variants selection with octo-spi DDR commands (optional) */
ret = spinand_init_odtr_instruction_set(spinand);
@@ -1527,6 +1571,11 @@ int spinand_match_and_init(struct spinand_device *spinand,
info->op_variants.update_cache);
spinand->odtr_op_templates.update_cache = op;
+ op = spinand_select_op_variant(spinand, ODTR,
+ info->op_variants.cont_read_cache);
+ if (op)
+ spinand->odtr_op_templates.cont_read_cache = op;
+
return 0;
}
diff --git a/include/linux/mtd/spinand.h b/include/linux/mtd/spinand.h
index 5ca1181048f7..5ec7d756df8b 100644
--- a/include/linux/mtd/spinand.h
+++ b/include/linux/mtd/spinand.h
@@ -573,6 +573,7 @@ enum spinand_bus_interface {
* @op_variants.read_cache: variants of the read-cache operation
* @op_variants.write_cache: variants of the write-cache operation
* @op_variants.update_cache: variants of the update-cache operation
+ * @op_variants.cont_read_cache: variants of the continuous read-cache operation
* @vendor_ops: vendor specific operations
* @select_target: function used to select a target/die. Required only for
* multi-die chips
@@ -597,6 +598,7 @@ struct spinand_info {
const struct spinand_op_variants *read_cache;
const struct spinand_op_variants *write_cache;
const struct spinand_op_variants *update_cache;
+ const struct spinand_op_variants *cont_read_cache;
} op_variants;
const struct spinand_op_variants *vendor_ops;
int (*select_target)(struct spinand_device *spinand,
@@ -626,6 +628,14 @@ struct spinand_info {
.update_cache = __update, \
}
+#define SPINAND_INFO_OP_VARIANTS_WITH_CONT(__read, __write, __update, __cont_read) \
+ { \
+ .read_cache = __read, \
+ .write_cache = __write, \
+ .update_cache = __update, \
+ .cont_read_cache = __cont_read, \
+ }
+
#define SPINAND_INFO_VENDOR_OPS(__ops) \
.vendor_ops = __ops
@@ -697,6 +707,7 @@ struct spinand_dirmap {
* @read_cache: read cache op template
* @write_cache: write cache op template
* @update_cache: update cache op template
+ * @cont_read_cache: continuous read cache op template (optional)
*/
struct spinand_mem_ops {
struct spi_mem_op reset;
@@ -711,6 +722,7 @@ struct spinand_mem_ops {
const struct spi_mem_op *read_cache;
const struct spi_mem_op *write_cache;
const struct spi_mem_op *update_cache;
+ const struct spi_mem_op *cont_read_cache;
};
/**
--
2.51.1
______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH RFC 7/8] mtd: spinand: winbond: Add support for continuous reads on W35NxxJW
2025-12-05 19:38 [PATCH RFC 0/8] mtd: spinand: Winbond continuous read support Miquel Raynal
` (5 preceding siblings ...)
2025-12-05 19:38 ` [PATCH RFC 6/8] mtd: spinand: Use secondary ops for continuous reads Miquel Raynal
@ 2025-12-05 19:38 ` Miquel Raynal
2025-12-05 19:38 ` [PATCH RFC 8/8] mtd: spinand: winbond: Add support for continuous reads on W25NxxJW Miquel Raynal
7 siblings, 0 replies; 12+ messages in thread
From: Miquel Raynal @ 2025-12-05 19:38 UTC (permalink / raw)
To: Mark Brown, Richard Weinberger, Vignesh Raghavendra,
Michael Walle
Cc: Tudor Ambarus, Pratyush Yadav, Thomas Petazzoni, Steam Lin,
Santhosh Kumar K, linux-spi, linux-kernel, linux-mtd,
Miquel Raynal
W35N{01,02,04}JW support being read continuously under certain
circumstances. A bit must be set in their configuration register, and
a specific read from cache operation, a bit shorter than usual because
it no longer requires the address cycles, must be used for the occasion.
Setting the "enable" bit is already supported by the core, aside from
the subtlety of making sure the HFREQ bit is also set in octal DTR mode
above 90MHz. However, handling two different read from cache templates
involves creating a list of read from cache variants adapted the
continuous reads, ie. without address cycles.
Unfortunately, these operations, despite being very close to their
original read from cache cousins, are often unsupported by smart SPI
controller drivers because reading from cache historically allowed
changing the offset at which the host would start by providing a 2-byte
column address. In order to prevent issues with this, it has been
decided to implement these variants with a single "ignored" address byte
(respectively two in the octal DTR case), further reducing the amount of
dummy cycles needed before the first bit of data.
Enabling continuous reads has a side effect: the ECC status register now
may also return the value b11, which means that more than 1
uncorrectable error happened during the read. This non standard
behaviour requires to re-implement, almost identically the "get ECC"
helper from the core, with just an extra case for this value (it is
prefixed "w25w35nxxjw" because all these chips have the same behaviour).
Speed gain is substantial, see below. The flash_speed -C benchmark has
been run on a TI AM62A7 LP SK with CPU power management disabled,
mounted with a W35N01JW chip.
1S-8S-8S:
1 page read speed is 15058 KiB/s
2 page read speed is 15058 KiB/s
3 page read speed is 16800 KiB/s
4 page read speed is 17066 KiB/s
5 page read speed is 18461 KiB/s
6 page read speed is 18461 KiB/s
7 page read speed is 19384 KiB/s
8 page read speed is 19692 KiB/s
9 page read speed is 19384 KiB/s
10 page read speed is 20000 KiB/s
11 page read speed is 20000 KiB/s
12 page read speed is 20000 KiB/s
13 page read speed is 20800 KiB/s
14 page read speed is 20363 KiB/s
15 page read speed is 20000 KiB/s
16 page read speed is 19692 KiB/s
32 page read speed is 19692 KiB/s
64 page read speed is 19692 KiB/s
8D-8D-8D:
1 page read speed is 23272 KiB/s
2 page read speed is 23272 KiB/s
3 page read speed is 28000 KiB/s
4 page read speed is 32000 KiB/s
5 page read speed is 34285 KiB/s
6 page read speed is 34285 KiB/s
7 page read speed is 36000 KiB/s
8 page read speed is 36571 KiB/s
9 page read speed is 36000 KiB/s
10 page read speed is 34285 KiB/s
11 page read speed is 36666 KiB/s
12 page read speed is 40000 KiB/s
13 page read speed is 41600 KiB/s
14 page read speed is 37333 KiB/s
15 page read speed is 40000 KiB/s
16 page read speed is 36571 KiB/s
32 page read speed is 42666 KiB/s
64 page read speed is 42666 KiB/s
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
---
Not all configurations have been tested/validated yet.
---
drivers/mtd/nand/spi/winbond.c | 126 ++++++++++++++++++++++++++++++++++++-----
1 file changed, 111 insertions(+), 15 deletions(-)
diff --git a/drivers/mtd/nand/spi/winbond.c b/drivers/mtd/nand/spi/winbond.c
index d0c50eb93827..716ec0031dcc 100644
--- a/drivers/mtd/nand/spi/winbond.c
+++ b/drivers/mtd/nand/spi/winbond.c
@@ -15,9 +15,11 @@
#define SPINAND_MFR_WINBOND 0xEF
+#define WINBOND_CFG_HFREQ BIT(0)
#define WINBOND_CFG_BUF_READ BIT(3)
#define W25N04KV_STATUS_ECC_5_8_BITFLIPS (3 << 4)
+#define W25W35NXXJW_STATUS_ECC_MULT_UNCOR (3 << 4)
#define W25N0XJW_SR4 0xD0
#define W25N0XJW_SR4_HS BIT(2)
@@ -29,6 +31,49 @@
#define W35N01JW_VCR_IO_MODE_OCTAL_DDR 0xC7
#define W35N01JW_VCR_DUMMY_CLOCK_REG 0x01
+/*
+ * Winbond chips ignore the address bytes during continuous reads, and
+ * because the dummy cycles are enough they indicate dropping the
+ * address cycles from the continuous read from cache variants. This is
+ * very poorly supported by SPI controller drivers which are "wired" to
+ * always at least provide the column. Keep using address cycles, but
+ * reduce the number of dummy cycles accordingly.
+ */
+#define WINBOND_CONT_READ_FROM_CACHE_FAST_1S_1S_1S_OP(ndummy, buf, len, freq) \
+ SPI_MEM_OP(SPI_MEM_OP_CMD(0x0b, 1), \
+ SPI_MEM_OP_ADDR(1, 0, 1), \
+ SPI_MEM_OP_DUMMY(ndummy - 1, 1), \
+ SPI_MEM_OP_DATA_IN(len, buf, 1), \
+ SPI_MEM_OP_MAX_FREQ(freq))
+
+#define WINBOND_CONT_READ_FROM_CACHE_1S_1S_8S_OP(ndummy, buf, len, freq) \
+ SPI_MEM_OP(SPI_MEM_OP_CMD(0x8b, 1), \
+ SPI_MEM_OP_ADDR(1, 0, 1), \
+ SPI_MEM_OP_DUMMY(ndummy - 1, 1), \
+ SPI_MEM_OP_DATA_IN(len, buf, 8), \
+ SPI_MEM_OP_MAX_FREQ(freq))
+
+#define WINBOND_CONT_READ_FROM_CACHE_1S_1D_8D_OP(ndummy, buf, len, freq) \
+ SPI_MEM_OP(SPI_MEM_OP_CMD(0x9d, 1), \
+ SPI_MEM_DTR_OP_ADDR(1, 0, 1), \
+ SPI_MEM_DTR_OP_DUMMY(ndummy - 1, 1), \
+ SPI_MEM_DTR_OP_DATA_IN(len, buf, 8), \
+ SPI_MEM_OP_MAX_FREQ(freq))
+
+#define WINBOND_CONT_READ_FROM_CACHE_1S_8S_8S_OP(ndummy, buf, len, freq) \
+ SPI_MEM_OP(SPI_MEM_OP_CMD(0xcb, 1), \
+ SPI_MEM_OP_ADDR(1, 0, 8), \
+ SPI_MEM_OP_DUMMY(ndummy - 1, 8), \
+ SPI_MEM_OP_DATA_IN(len, buf, 8), \
+ SPI_MEM_OP_MAX_FREQ(freq))
+
+#define WINBOND_CONT_READ_FROM_CACHE_8D_8D_8D_OP(ndummy, buf, len, freq) \
+ SPI_MEM_OP(SPI_MEM_DTR_OP_RPT_CMD(0x9d, 8), \
+ SPI_MEM_DTR_OP_ADDR(2, 0, 8), \
+ SPI_MEM_DTR_OP_DUMMY(ndummy - 2, 8), \
+ SPI_MEM_DTR_OP_DATA_IN(len, buf, 8), \
+ SPI_MEM_OP_MAX_FREQ(freq))
+
/*
* "X2" in the core is equivalent to "dual output" in the datasheets,
* "X4" in the core is equivalent to "quad output" in the datasheets.
@@ -49,6 +94,19 @@ static SPINAND_OP_VARIANTS(read_cache_octal_variants,
SPINAND_PAGE_READ_FROM_CACHE_FAST_1S_1S_1S_OP(0, 1, NULL, 0, 0),
SPINAND_PAGE_READ_FROM_CACHE_1S_1S_1S_OP(0, 1, NULL, 0, 0));
+static SPINAND_OP_VARIANTS(cont_read_cache_octal_variants,
+ WINBOND_CONT_READ_FROM_CACHE_8D_8D_8D_OP(24, NULL, 0, 120 * HZ_PER_MHZ),
+ WINBOND_CONT_READ_FROM_CACHE_8D_8D_8D_OP(16, NULL, 0, 86 * HZ_PER_MHZ),
+ WINBOND_CONT_READ_FROM_CACHE_1S_1D_8D_OP(3, NULL, 0, 120 * HZ_PER_MHZ),
+ WINBOND_CONT_READ_FROM_CACHE_1S_1D_8D_OP(2, NULL, 0, 105 * HZ_PER_MHZ),
+ WINBOND_CONT_READ_FROM_CACHE_1S_8S_8S_OP(20, NULL, 0, 0),
+ WINBOND_CONT_READ_FROM_CACHE_1S_8S_8S_OP(16, NULL, 0, 162 * HZ_PER_MHZ),
+ WINBOND_CONT_READ_FROM_CACHE_1S_8S_8S_OP(12, NULL, 0, 124 * HZ_PER_MHZ),
+ WINBOND_CONT_READ_FROM_CACHE_1S_8S_8S_OP(8, NULL, 0, 86 * HZ_PER_MHZ),
+ WINBOND_CONT_READ_FROM_CACHE_1S_1S_8S_OP(2, NULL, 0, 0),
+ WINBOND_CONT_READ_FROM_CACHE_1S_1S_8S_OP(1, NULL, 0, 133 * HZ_PER_MHZ),
+ WINBOND_CONT_READ_FROM_CACHE_FAST_1S_1S_1S_OP(1, NULL, 0, 0));
+
static SPINAND_OP_VARIANTS(write_cache_octal_variants,
SPINAND_PROG_LOAD_8D_8D_8D_OP(true, 0, NULL, 0),
SPINAND_PROG_LOAD_1S_8S_8S_OP(true, 0, NULL, 0),
@@ -326,6 +384,26 @@ static int w25n02kv_ecc_get_status(struct spinand_device *spinand,
return -EINVAL;
}
+static int w25w35nxxjw_ecc_get_status(struct spinand_device *spinand, u8 status)
+{
+ switch (status & STATUS_ECC_MASK) {
+ case STATUS_ECC_NO_BITFLIPS:
+ return 0;
+
+ case STATUS_ECC_HAS_BITFLIPS:
+ return 1;
+
+ case STATUS_ECC_UNCOR_ERROR:
+ case W25W35NXXJW_STATUS_ECC_MULT_UNCOR:
+ return -EBADMSG;
+
+ default:
+ break;
+ }
+
+ return -EINVAL;
+}
+
static int w25n0xjw_hs_cfg(struct spinand_device *spinand,
enum spinand_bus_interface iface)
{
@@ -448,6 +526,18 @@ static int w35n0xjw_vcr_cfg(struct spinand_device *spinand,
return 0;
}
+static int w35n0xjw_set_cont_read(struct spinand_device *spinand, bool enable)
+{
+ const struct spi_mem_op *cont_op = spinand->op_templates->cont_read_cache;
+ u8 mask = enable ? 0 : WINBOND_CFG_BUF_READ;
+
+ if (cont_op && enable && spinand_op_is_odtr(cont_op) &&
+ cont_op->max_freq >= 90 * HZ_PER_MHZ)
+ mask |= WINBOND_CFG_HFREQ;
+
+ return spinand_upd_cfg(spinand, WINBOND_CFG_BUF_READ | WINBOND_CFG_HFREQ, mask);
+}
+
static const struct spinand_info winbond_spinand_table[] = {
/* 512M-bit densities */
SPINAND_INFO("W25N512GW", /* 1.8V */
@@ -501,35 +591,41 @@ static const struct spinand_info winbond_spinand_table[] = {
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xdc, 0x21),
NAND_MEMORG(1, 4096, 128, 64, 512, 10, 1, 1, 1),
NAND_ECCREQ(1, 512),
- SPINAND_INFO_OP_VARIANTS(&read_cache_octal_variants,
- &write_cache_octal_variants,
- &update_cache_octal_variants),
+ SPINAND_INFO_OP_VARIANTS_WITH_CONT(&read_cache_octal_variants,
+ &write_cache_octal_variants,
+ &update_cache_octal_variants,
+ &cont_read_cache_octal_variants),
0,
SPINAND_INFO_VENDOR_OPS(&winbond_w35_ops),
- SPINAND_ECCINFO(&w35n01jw_ooblayout, NULL),
- SPINAND_CONFIGURE_CHIP(w35n0xjw_vcr_cfg)),
+ SPINAND_ECCINFO(&w35n01jw_ooblayout, w25w35nxxjw_ecc_get_status),
+ SPINAND_CONFIGURE_CHIP(w35n0xjw_vcr_cfg),
+ SPINAND_CONT_READ(w35n0xjw_set_cont_read)),
SPINAND_INFO("W35N02JW", /* 1.8V */
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xdf, 0x22),
NAND_MEMORG(1, 4096, 128, 64, 512, 10, 1, 2, 1),
NAND_ECCREQ(1, 512),
- SPINAND_INFO_OP_VARIANTS(&read_cache_octal_variants,
- &write_cache_octal_variants,
- &update_cache_octal_variants),
+ SPINAND_INFO_OP_VARIANTS_WITH_CONT(&read_cache_octal_variants,
+ &write_cache_octal_variants,
+ &update_cache_octal_variants,
+ &cont_read_cache_octal_variants),
0,
SPINAND_INFO_VENDOR_OPS(&winbond_w35_ops),
- SPINAND_ECCINFO(&w35n01jw_ooblayout, NULL),
- SPINAND_CONFIGURE_CHIP(w35n0xjw_vcr_cfg)),
+ SPINAND_ECCINFO(&w35n01jw_ooblayout, w25w35nxxjw_ecc_get_status),
+ SPINAND_CONFIGURE_CHIP(w35n0xjw_vcr_cfg),
+ SPINAND_CONT_READ(w35n0xjw_set_cont_read)),
SPINAND_INFO("W35N04JW", /* 1.8V */
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xdf, 0x23),
NAND_MEMORG(1, 4096, 128, 64, 512, 10, 1, 4, 1),
NAND_ECCREQ(1, 512),
- SPINAND_INFO_OP_VARIANTS(&read_cache_octal_variants,
- &write_cache_octal_variants,
- &update_cache_octal_variants),
+ SPINAND_INFO_OP_VARIANTS_WITH_CONT(&read_cache_octal_variants,
+ &write_cache_octal_variants,
+ &update_cache_octal_variants,
+ &cont_read_cache_octal_variants),
0,
SPINAND_INFO_VENDOR_OPS(&winbond_w35_ops),
- SPINAND_ECCINFO(&w35n01jw_ooblayout, NULL),
- SPINAND_CONFIGURE_CHIP(w35n0xjw_vcr_cfg)),
+ SPINAND_ECCINFO(&w35n01jw_ooblayout, w25w35nxxjw_ecc_get_status),
+ SPINAND_CONFIGURE_CHIP(w35n0xjw_vcr_cfg),
+ SPINAND_CONT_READ(w35n0xjw_set_cont_read)),
/* 2G-bit densities */
SPINAND_INFO("W25M02GV", /* 2x1G-bit 3.3V */
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xab, 0x21),
--
2.51.1
______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH RFC 8/8] mtd: spinand: winbond: Add support for continuous reads on W25NxxJW
2025-12-05 19:38 [PATCH RFC 0/8] mtd: spinand: Winbond continuous read support Miquel Raynal
` (6 preceding siblings ...)
2025-12-05 19:38 ` [PATCH RFC 7/8] mtd: spinand: winbond: Add support for continuous reads on W35NxxJW Miquel Raynal
@ 2025-12-05 19:38 ` Miquel Raynal
7 siblings, 0 replies; 12+ messages in thread
From: Miquel Raynal @ 2025-12-05 19:38 UTC (permalink / raw)
To: Mark Brown, Richard Weinberger, Vignesh Raghavendra,
Michael Walle
Cc: Tudor Ambarus, Pratyush Yadav, Thomas Petazzoni, Steam Lin,
Santhosh Kumar K, linux-spi, linux-kernel, linux-mtd,
Miquel Raynal
As for the W35NxxJW family, add support for W25N{01,02}JW continuous
read support. Similar operations require to be done, such as setting a
specific bit in a configuration register, and providing a set of read
variants without the address cycles.
As read from cache variants are badly supported by SPI memory
controllers, we create a new set of read from cache templates with a
fake address cycle and just enough dummy cycles. There are two
unsupported configurations (which would require 4.5 dummy bytes), so we
just do not provide them.
The same extra value in the ECC is possible as with the W35NxxJW family,
so we reference the same helper to retrieve the ECC status.
TODO: Further validate the feature and measure its impact.
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
---
This patch has been compile tested only.
---
drivers/mtd/nand/spi/winbond.c | 101 +++++++++++++++++++++++++++++++++++++----
1 file changed, 91 insertions(+), 10 deletions(-)
diff --git a/drivers/mtd/nand/spi/winbond.c b/drivers/mtd/nand/spi/winbond.c
index 716ec0031dcc..55f6229666bc 100644
--- a/drivers/mtd/nand/spi/winbond.c
+++ b/drivers/mtd/nand/spi/winbond.c
@@ -46,6 +46,62 @@
SPI_MEM_OP_DATA_IN(len, buf, 1), \
SPI_MEM_OP_MAX_FREQ(freq))
+#define WINBOND_CONT_READ_FROM_CACHE_1S_1D_1D_OP(ndummy, buf, len, freq) \
+ SPI_MEM_OP(SPI_MEM_OP_CMD(0x0d, 1), \
+ SPI_MEM_DTR_OP_ADDR(2, 0, 1), \
+ SPI_MEM_DTR_OP_DUMMY(ndummy, 1), \
+ SPI_MEM_DTR_OP_DATA_IN(len, buf, 1), \
+ SPI_MEM_OP_MAX_FREQ(freq))
+
+#define WINBOND_CONT_READ_FROM_CACHE_1S_1S_2S_OP(ndummy, buf, len, freq) \
+ SPI_MEM_OP(SPI_MEM_OP_CMD(0x3b, 1), \
+ SPI_MEM_OP_ADDR(1, 0, 1), \
+ SPI_MEM_OP_DUMMY(ndummy - 1, 1), \
+ SPI_MEM_OP_DATA_IN(len, buf, 2), \
+ SPI_MEM_OP_MAX_FREQ(freq))
+
+#define WINBOND_CONT_READ_FROM_CACHE_1S_2S_2S_OP(ndummy, buf, len, freq) \
+ SPI_MEM_OP(SPI_MEM_OP_CMD(0xbb, 1), \
+ SPI_MEM_OP_ADDR(1, 0, 2), \
+ SPI_MEM_OP_DUMMY(ndummy - 1, 2), \
+ SPI_MEM_OP_DATA_IN(len, buf, 2), \
+ SPI_MEM_OP_MAX_FREQ(freq))
+
+#define WINBOND_CONT_READ_FROM_CACHE_1S_2D_2D_OP(ndummy, buf, len, freq) \
+ SPI_MEM_OP(SPI_MEM_OP_CMD(0xbd, 1), \
+ SPI_MEM_DTR_OP_ADDR(1, 0, 2), \
+ SPI_MEM_DTR_OP_DUMMY(ndummy - 1, 2), \
+ SPI_MEM_DTR_OP_DATA_IN(len, buf, 2), \
+ SPI_MEM_OP_MAX_FREQ(freq))
+
+#define WINBOND_CONT_READ_FROM_CACHE_1S_1S_4S_OP(ndummy, buf, len, freq) \
+ SPI_MEM_OP(SPI_MEM_OP_CMD(0x6b, 1), \
+ SPI_MEM_OP_ADDR(1, 0, 1), \
+ SPI_MEM_OP_DUMMY(ndummy - 1, 1), \
+ SPI_MEM_OP_DATA_IN(len, buf, 4), \
+ SPI_MEM_OP_MAX_FREQ(freq))
+
+#define WINBOND_CONT_READ_FROM_CACHE_1S_1D_4D_OP(ndummy, buf, len, freq) \
+ SPI_MEM_OP(SPI_MEM_OP_CMD(0x6d, 1), \
+ SPI_MEM_DTR_OP_ADDR(1, 0, 1), \
+ SPI_MEM_DTR_OP_DUMMY(ndummy - 1, 1), \
+ SPI_MEM_DTR_OP_DATA_IN(len, buf, 4), \
+ SPI_MEM_OP_MAX_FREQ(freq))
+
+#define WINBOND_CONT_READ_FROM_CACHE_1S_4S_4S_OP(ndummy, buf, len, freq) \
+ SPI_MEM_OP(SPI_MEM_OP_CMD(0xeb, 1), \
+ SPI_MEM_OP_ADDR(1, 0, 4), \
+ SPI_MEM_OP_DUMMY(ndummy - 1, 4), \
+ SPI_MEM_OP_DATA_IN(len, buf, 4), \
+ SPI_MEM_OP_MAX_FREQ(freq))
+
+#define WINBOND_CONT_READ_FROM_CACHE_1S_4D_4D_OP(ndummy, buf, len, freq) \
+ SPI_MEM_OP(SPI_MEM_OP_CMD(0xed, 1), \
+ SPI_MEM_DTR_OP_ADDR(1, 0, 4), \
+ SPI_MEM_DTR_OP_DUMMY(ndummy - 1, 4), \
+ SPI_MEM_DTR_OP_DATA_IN(len, buf, 4), \
+ SPI_MEM_OP_MAX_FREQ(freq))
+
#define WINBOND_CONT_READ_FROM_CACHE_1S_1S_8S_OP(ndummy, buf, len, freq) \
SPI_MEM_OP(SPI_MEM_OP_CMD(0x8b, 1), \
SPI_MEM_OP_ADDR(1, 0, 1), \
@@ -133,6 +189,20 @@ static SPINAND_OP_VARIANTS(read_cache_dual_quad_dtr_variants,
SPINAND_PAGE_READ_FROM_CACHE_FAST_1S_1S_1S_OP(0, 1, NULL, 0, 0),
SPINAND_PAGE_READ_FROM_CACHE_1S_1S_1S_OP(0, 1, NULL, 0, 54 * HZ_PER_MHZ));
+static SPINAND_OP_VARIANTS(cont_read_cache_dual_quad_dtr_variants,
+ WINBOND_CONT_READ_FROM_CACHE_1S_4D_4D_OP(11, NULL, 0, 80 * HZ_PER_MHZ),
+ WINBOND_CONT_READ_FROM_CACHE_1S_1D_4D_OP(5, NULL, 0, 80 * HZ_PER_MHZ),
+ WINBOND_CONT_READ_FROM_CACHE_1S_4S_4S_OP(8, NULL, 0, 0),
+ WINBOND_CONT_READ_FROM_CACHE_1S_4S_4S_OP(6, NULL, 0, 104 * HZ_PER_MHZ),
+ WINBOND_CONT_READ_FROM_CACHE_1S_1S_4S_OP(4, NULL, 0, 0),
+ WINBOND_CONT_READ_FROM_CACHE_1S_2D_2D_OP(6, NULL, 0, 80 * HZ_PER_MHZ),
+ /* The 1S_1D_2D variant would require 4.5 dummy bytes, this is not possible */
+ WINBOND_CONT_READ_FROM_CACHE_1S_2S_2S_OP(5, NULL, 0, 0),
+ WINBOND_CONT_READ_FROM_CACHE_1S_2S_2S_OP(4, NULL, 0, 104 * HZ_PER_MHZ),
+ WINBOND_CONT_READ_FROM_CACHE_1S_1S_2S_OP(4, NULL, 0, 0),
+ /* The 1S_1D_1D variant would require 4.5 dummy bytes, this is not possible */
+ WINBOND_CONT_READ_FROM_CACHE_FAST_1S_1S_1S_OP(4, NULL, 0, 0));
+
static SPINAND_OP_VARIANTS(read_cache_variants,
SPINAND_PAGE_READ_FROM_CACHE_1S_4S_4S_OP(0, 2, NULL, 0, 0),
SPINAND_PAGE_READ_FROM_CACHE_1S_1S_4S_OP(0, 1, NULL, 0, 0),
@@ -442,6 +512,13 @@ static int w25n0xjw_hs_cfg(struct spinand_device *spinand,
return 0;
}
+static int w25n0xjw_set_cont_read(struct spinand_device *spinand, bool enable)
+{
+ u8 mask = enable ? 0 : WINBOND_CFG_BUF_READ;
+
+ return spinand_upd_cfg(spinand, WINBOND_CFG_BUF_READ | WINBOND_CFG_HFREQ, mask);
+}
+
static int w35n0xjw_write_vcr(struct spinand_device *spinand, u8 reg, u8 val)
{
struct spi_mem_op op = SPINAND_OP(spinand, winbond_write_vcr,
@@ -572,12 +649,14 @@ static const struct spinand_info winbond_spinand_table[] = {
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xbc, 0x21),
NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1),
NAND_ECCREQ(1, 512),
- SPINAND_INFO_OP_VARIANTS(&read_cache_dual_quad_dtr_variants,
- &write_cache_variants,
- &update_cache_variants),
+ SPINAND_INFO_OP_VARIANTS_WITH_CONT(&read_cache_dual_quad_dtr_variants,
+ &write_cache_variants,
+ &update_cache_variants,
+ &cont_read_cache_dual_quad_dtr_variants),
0,
- SPINAND_ECCINFO(&w25n01jw_ooblayout, NULL),
- SPINAND_CONFIGURE_CHIP(w25n0xjw_hs_cfg)),
+ SPINAND_ECCINFO(&w25n01jw_ooblayout, w25w35nxxjw_ecc_get_status),
+ SPINAND_CONFIGURE_CHIP(w25n0xjw_hs_cfg),
+ SPINAND_CONT_READ(w25n0xjw_set_cont_read)),
SPINAND_INFO("W25N01KV", /* 3.3V */
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xae, 0x21),
NAND_MEMORG(1, 2048, 96, 64, 1024, 20, 1, 1, 1),
@@ -642,12 +721,14 @@ static const struct spinand_info winbond_spinand_table[] = {
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xbf, 0x22),
NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 2, 1),
NAND_ECCREQ(1, 512),
- SPINAND_INFO_OP_VARIANTS(&read_cache_dual_quad_dtr_variants,
- &write_cache_variants,
- &update_cache_variants),
+ SPINAND_INFO_OP_VARIANTS_WITH_CONT(&read_cache_dual_quad_dtr_variants,
+ &write_cache_variants,
+ &update_cache_variants,
+ &cont_read_cache_dual_quad_dtr_variants),
0,
- SPINAND_ECCINFO(&w25m02gv_ooblayout, NULL),
- SPINAND_CONFIGURE_CHIP(w25n0xjw_hs_cfg)),
+ SPINAND_ECCINFO(&w25m02gv_ooblayout, w25w35nxxjw_ecc_get_status),
+ SPINAND_CONFIGURE_CHIP(w25n0xjw_hs_cfg),
+ SPINAND_CONT_READ(w25n0xjw_set_cont_read)),
SPINAND_INFO("W25N02KV", /* 3.3V */
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xaa, 0x22),
NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1),
--
2.51.1
______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/
^ permalink raw reply related [flat|nested] 12+ messages in thread
* Re: [PATCH RFC 4/8] spi: spi-mem: Transform the read operation template
2025-12-05 19:38 ` [PATCH RFC 4/8] spi: spi-mem: Transform the read operation template Miquel Raynal
@ 2025-12-11 2:03 ` Mark Brown
0 siblings, 0 replies; 12+ messages in thread
From: Mark Brown @ 2025-12-11 2:03 UTC (permalink / raw)
To: Miquel Raynal
Cc: Richard Weinberger, Vignesh Raghavendra, Michael Walle,
Tudor Ambarus, Pratyush Yadav, Thomas Petazzoni, Steam Lin,
Santhosh Kumar K, linux-spi, linux-kernel, linux-mtd
[-- Attachment #1.1: Type: text/plain, Size: 600 bytes --]
On Fri, Dec 05, 2025 at 08:38:55PM +0100, Miquel Raynal wrote:
> As of now, we only use a single operation template when creating SPI
> memory direct mappings. With the idea to extend this possibility to 2,
> rename the template to reflect that we are currently setting the
> "primary" operation, and create a pointer in the same structure to point
> to it.
>
> From a user point of view, the op_tmpl name remains but becomes a
> pointer, leading to minor changes in both the SPI NAND and SPI NOR
> cores.
>
> There is no functional change.
Acked-by: Mark Brown <broonie@kernel.org>
[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
[-- Attachment #2: Type: text/plain, Size: 144 bytes --]
______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH RFC 5/8] spi: spi-mem: Create a secondary read operation
2025-12-05 19:38 ` [PATCH RFC 5/8] spi: spi-mem: Create a secondary read operation Miquel Raynal
@ 2025-12-11 2:06 ` Mark Brown
2025-12-11 14:19 ` Miquel Raynal
0 siblings, 1 reply; 12+ messages in thread
From: Mark Brown @ 2025-12-11 2:06 UTC (permalink / raw)
To: Miquel Raynal
Cc: Richard Weinberger, Vignesh Raghavendra, Michael Walle,
Tudor Ambarus, Pratyush Yadav, Thomas Petazzoni, Steam Lin,
Santhosh Kumar K, linux-spi, linux-kernel, linux-mtd
[-- Attachment #1.1: Type: text/plain, Size: 558 bytes --]
On Fri, Dec 05, 2025 at 08:38:56PM +0100, Miquel Raynal wrote:
> The choice of defining two variables named primary and secondary instead
> of using an array of templates is on purpose, to simplify the reading. I
> find less obvious the use of an array here but this is personal taste.
This makes sense to me:
Acked-by: Mark Brown <broonie@kernel.org>
Feel free to carry both these patches along with the rest of the
series, if/when you do end up applying it a signed tag would probably be
a good idea in case there's some collision with other SPI work.
[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
[-- Attachment #2: Type: text/plain, Size: 144 bytes --]
______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH RFC 5/8] spi: spi-mem: Create a secondary read operation
2025-12-11 2:06 ` Mark Brown
@ 2025-12-11 14:19 ` Miquel Raynal
0 siblings, 0 replies; 12+ messages in thread
From: Miquel Raynal @ 2025-12-11 14:19 UTC (permalink / raw)
To: Mark Brown
Cc: Richard Weinberger, Vignesh Raghavendra, Michael Walle,
Tudor Ambarus, Pratyush Yadav, Thomas Petazzoni, Steam Lin,
Santhosh Kumar K, linux-spi, linux-kernel, linux-mtd
On 11/12/2025 at 11:06:11 +09, Mark Brown <broonie@kernel.org> wrote:
> On Fri, Dec 05, 2025 at 08:38:56PM +0100, Miquel Raynal wrote:
>
>> The choice of defining two variables named primary and secondary instead
>> of using an array of templates is on purpose, to simplify the reading. I
>> find less obvious the use of an array here but this is personal taste.
>
> This makes sense to me:
>
> Acked-by: Mark Brown <broonie@kernel.org>
>
> Feel free to carry both these patches along with the rest of the
> series, if/when you do end up applying it a signed tag would probably be
> a good idea in case there's some collision with other SPI work.
Thanks a lot for the feedback.
Yes, when the series is considered ready, I'll take care of that tag.
Thanks,
Miquèl
______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/
^ permalink raw reply [flat|nested] 12+ messages in thread
end of thread, other threads:[~2025-12-11 14:19 UTC | newest]
Thread overview: 12+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-12-05 19:38 [PATCH RFC 0/8] mtd: spinand: Winbond continuous read support Miquel Raynal
2025-12-05 19:38 ` [PATCH RFC 1/8] mtd: spinand: Drop a too strong limitation Miquel Raynal
2025-12-05 19:38 ` [PATCH RFC 2/8] mtd: spinand: Expose spinand_op_is_odtr() Miquel Raynal
2025-12-05 19:38 ` [PATCH RFC 3/8] mtd: spinand: Drop ECC dirmaps Miquel Raynal
2025-12-05 19:38 ` [PATCH RFC 4/8] spi: spi-mem: Transform the read operation template Miquel Raynal
2025-12-11 2:03 ` Mark Brown
2025-12-05 19:38 ` [PATCH RFC 5/8] spi: spi-mem: Create a secondary read operation Miquel Raynal
2025-12-11 2:06 ` Mark Brown
2025-12-11 14:19 ` Miquel Raynal
2025-12-05 19:38 ` [PATCH RFC 6/8] mtd: spinand: Use secondary ops for continuous reads Miquel Raynal
2025-12-05 19:38 ` [PATCH RFC 7/8] mtd: spinand: winbond: Add support for continuous reads on W35NxxJW Miquel Raynal
2025-12-05 19:38 ` [PATCH RFC 8/8] mtd: spinand: winbond: Add support for continuous reads on W25NxxJW Miquel Raynal
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).