* [PATCH v2 01/27] spi: spi-mem: Make the DTR command operation macro more suitable
2026-01-09 17:17 [PATCH v2 00/27] mtd: spinand: Octal DTR support Miquel Raynal
@ 2026-01-09 17:17 ` Miquel Raynal
2026-01-09 17:18 ` [PATCH v2 02/27] spi: spi-mem: Create a repeated address operation Miquel Raynal
` (27 subsequent siblings)
28 siblings, 0 replies; 32+ messages in thread
From: Miquel Raynal @ 2026-01-09 17:17 UTC (permalink / raw)
To: Mark Brown, Richard Weinberger, Vignesh Raghavendra
Cc: Tudor Ambarus, Pratyush Yadav, Thomas Petazzoni, Steam Lin,
Santhosh Kumar K, linux-spi, linux-kernel, linux-mtd,
Miquel Raynal
In order to introduce DTR support in SPI NAND, a number of macros had to
be created in the spi-mem layer. One of them remained unused at this
point, SPI_MEM_DTR_OP_CMD. Being in the process of introducing octal DTR
support now, experience shows that as-is the macro is not useful. In
order to be really useful in octal DTR mode, the command opcode (one
byte) must always be transmitted on the 8 data lines on both the rising
and falling edge of the clock. Align the macro with the real needs by
duplicating the opcode in the buffer and doubling its size.
Reviewed-by: Tudor Ambarus <tudor.ambarus@linaro.org>
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
---
include/linux/spi/spi-mem.h | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/include/linux/spi/spi-mem.h b/include/linux/spi/spi-mem.h
index 82390712794c..81c9c7e793b6 100644
--- a/include/linux/spi/spi-mem.h
+++ b/include/linux/spi/spi-mem.h
@@ -20,10 +20,10 @@
.opcode = __opcode, \
}
-#define SPI_MEM_DTR_OP_CMD(__opcode, __buswidth) \
+#define SPI_MEM_DTR_OP_RPT_CMD(__opcode, __buswidth) \
{ \
- .nbytes = 1, \
- .opcode = __opcode, \
+ .nbytes = 2, \
+ .opcode = __opcode | __opcode << 8, \
.buswidth = __buswidth, \
.dtr = true, \
}
--
2.51.1
______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/
^ permalink raw reply related [flat|nested] 32+ messages in thread* [PATCH v2 02/27] spi: spi-mem: Create a repeated address operation
2026-01-09 17:17 [PATCH v2 00/27] mtd: spinand: Octal DTR support Miquel Raynal
2026-01-09 17:17 ` [PATCH v2 01/27] spi: spi-mem: Make the DTR command operation macro more suitable Miquel Raynal
@ 2026-01-09 17:18 ` Miquel Raynal
2026-01-09 17:18 ` [PATCH v2 03/27] spi: spi-mem: Limit octal DTR constraints to octal DTR situations Miquel Raynal
` (26 subsequent siblings)
28 siblings, 0 replies; 32+ messages in thread
From: Miquel Raynal @ 2026-01-09 17:18 UTC (permalink / raw)
To: Mark Brown, Richard Weinberger, Vignesh Raghavendra
Cc: Tudor Ambarus, Pratyush Yadav, Thomas Petazzoni, Steam Lin,
Santhosh Kumar K, linux-spi, linux-kernel, linux-mtd,
Miquel Raynal
In octal DTR mode addresses may either be long enough to cover at least
two bytes (in which case the existing macro works), or otherwise for
single byte addresses, the byte must also be duplicated and sent twice:
on each front of the clock.
Create a macro for this common case.
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
---
include/linux/spi/spi-mem.h | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/include/linux/spi/spi-mem.h b/include/linux/spi/spi-mem.h
index 81c9c7e793b6..e4db0924898c 100644
--- a/include/linux/spi/spi-mem.h
+++ b/include/linux/spi/spi-mem.h
@@ -43,6 +43,14 @@
.dtr = true, \
}
+#define SPI_MEM_DTR_OP_RPT_ADDR(__val, __buswidth) \
+ { \
+ .nbytes = 2, \
+ .val = __val | __val << 8, \
+ .buswidth = __buswidth, \
+ .dtr = true, \
+ }
+
#define SPI_MEM_OP_NO_ADDR { }
#define SPI_MEM_OP_DUMMY(__nbytes, __buswidth) \
--
2.51.1
______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/
^ permalink raw reply related [flat|nested] 32+ messages in thread* [PATCH v2 03/27] spi: spi-mem: Limit octal DTR constraints to octal DTR situations
2026-01-09 17:17 [PATCH v2 00/27] mtd: spinand: Octal DTR support Miquel Raynal
2026-01-09 17:17 ` [PATCH v2 01/27] spi: spi-mem: Make the DTR command operation macro more suitable Miquel Raynal
2026-01-09 17:18 ` [PATCH v2 02/27] spi: spi-mem: Create a repeated address operation Miquel Raynal
@ 2026-01-09 17:18 ` Miquel Raynal
2026-01-09 17:18 ` [PATCH v2 04/27] mtd: spinand: Fix kernel doc Miquel Raynal
` (25 subsequent siblings)
28 siblings, 0 replies; 32+ messages in thread
From: Miquel Raynal @ 2026-01-09 17:18 UTC (permalink / raw)
To: Mark Brown, Richard Weinberger, Vignesh Raghavendra
Cc: Tudor Ambarus, Pratyush Yadav, Thomas Petazzoni, Steam Lin,
Santhosh Kumar K, linux-spi, linux-kernel, linux-mtd,
Miquel Raynal
In this helper, any operation with a single DTR cycle (like 1S-1S-8D) is
considered requiring a duplicated command opcode. This is wrong as this
constraint only applies to octal DTR operations (8D-8D-8D).
Narrow the application of this constraint to the concerned bus
interface.
Note: none of the possible XD-XD-XD pattern, with X being one of {1, 2,
4} would benefit from this check either as there is only in octal DTR
mode that a single clock edge would be enough to transmit the full
opcode.
Make sure the constraint of expecting two bytes for the command is
applied to the relevant bus interface.
Reviewed-by: Tudor Ambarus <tudor.ambarus@linaro.org>
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
---
drivers/spi/spi-mem.c | 15 +++++++++++++--
1 file changed, 13 insertions(+), 2 deletions(-)
diff --git a/drivers/spi/spi-mem.c b/drivers/spi/spi-mem.c
index c8b2add2640e..6c7921469b90 100644
--- a/drivers/spi/spi-mem.c
+++ b/drivers/spi/spi-mem.c
@@ -178,8 +178,19 @@ bool spi_mem_default_supports_op(struct spi_mem *mem,
if (op->data.swap16 && !spi_mem_controller_is_capable(ctlr, swap16))
return false;
- if (op->cmd.nbytes != 2)
- return false;
+ /* Extra 8D-8D-8D limitations */
+ if (op->cmd.dtr && op->cmd.buswidth == 8) {
+ if (op->cmd.nbytes != 2)
+ return false;
+
+ if ((op->addr.nbytes % 2) ||
+ (op->dummy.nbytes % 2) ||
+ (op->data.nbytes % 2)) {
+ dev_err(&ctlr->dev,
+ "Even byte numbers not allowed in octal DTR operations\n");
+ return false;
+ }
+ }
} else {
if (op->cmd.nbytes != 1)
return false;
--
2.51.1
______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/
^ permalink raw reply related [flat|nested] 32+ messages in thread* [PATCH v2 04/27] mtd: spinand: Fix kernel doc
2026-01-09 17:17 [PATCH v2 00/27] mtd: spinand: Octal DTR support Miquel Raynal
` (2 preceding siblings ...)
2026-01-09 17:18 ` [PATCH v2 03/27] spi: spi-mem: Limit octal DTR constraints to octal DTR situations Miquel Raynal
@ 2026-01-09 17:18 ` Miquel Raynal
2026-01-09 17:18 ` [PATCH v2 05/27] mtd: spinand: Add missing check Miquel Raynal
` (24 subsequent siblings)
28 siblings, 0 replies; 32+ messages in thread
From: Miquel Raynal @ 2026-01-09 17:18 UTC (permalink / raw)
To: Mark Brown, Richard Weinberger, Vignesh Raghavendra
Cc: Tudor Ambarus, Pratyush Yadav, Thomas Petazzoni, Steam Lin,
Santhosh Kumar K, linux-spi, linux-kernel, linux-mtd,
Miquel Raynal
The @data buffer is 5 bytes, not 4, it has been extended for the need of
devices with an extra ID bytes.
Fixes: 34a956739d29 ("mtd: spinand: Add support for 5-byte IDs")
Reviewed-by: Tudor Ambarus <tudor.ambarus@linaro.org>
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
---
include/linux/mtd/spinand.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/include/linux/mtd/spinand.h b/include/linux/mtd/spinand.h
index ce76f5c632e1..049d55c38d52 100644
--- a/include/linux/mtd/spinand.h
+++ b/include/linux/mtd/spinand.h
@@ -287,7 +287,7 @@ struct spinand_device;
/**
* struct spinand_id - SPI NAND id structure
- * @data: buffer containing the id bytes. Currently 4 bytes large, but can
+ * @data: buffer containing the id bytes. Currently 5 bytes large, but can
* be extended if required
* @len: ID length
*/
--
2.51.1
______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/
^ permalink raw reply related [flat|nested] 32+ messages in thread* [PATCH v2 05/27] mtd: spinand: Add missing check
2026-01-09 17:17 [PATCH v2 00/27] mtd: spinand: Octal DTR support Miquel Raynal
` (3 preceding siblings ...)
2026-01-09 17:18 ` [PATCH v2 04/27] mtd: spinand: Fix kernel doc Miquel Raynal
@ 2026-01-09 17:18 ` Miquel Raynal
2026-01-09 17:18 ` [PATCH v2 06/27] mtd: spinand: Remove stale definitions Miquel Raynal
` (23 subsequent siblings)
28 siblings, 0 replies; 32+ messages in thread
From: Miquel Raynal @ 2026-01-09 17:18 UTC (permalink / raw)
To: Mark Brown, Richard Weinberger, Vignesh Raghavendra
Cc: Tudor Ambarus, Pratyush Yadav, Thomas Petazzoni, Steam Lin,
Santhosh Kumar K, linux-spi, linux-kernel, linux-mtd,
Miquel Raynal
The update cache variant is mandatory, both read and write versions are
being checked, but not this one. All chip drivers seem to implement this
variant, so there should be no breakage.
Reviewed-by: Tudor Ambarus <tudor.ambarus@linaro.org>
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
---
The core has been like that since the begining, I do not think this
patch should be backported, hence no Fixes tag. This barely
qualifies as a fix anyway.
---
drivers/mtd/nand/spi/core.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c
index 62447801abb1..8acea0372f21 100644
--- a/drivers/mtd/nand/spi/core.c
+++ b/drivers/mtd/nand/spi/core.c
@@ -1432,6 +1432,9 @@ int spinand_match_and_init(struct spinand_device *spinand,
op = spinand_select_op_variant(spinand,
info->op_variants.update_cache);
+ if (!op)
+ return -ENOTSUPP;
+
spinand->op_templates.update_cache = op;
return 0;
--
2.51.1
______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/
^ permalink raw reply related [flat|nested] 32+ messages in thread* [PATCH v2 06/27] mtd: spinand: Remove stale definitions
2026-01-09 17:17 [PATCH v2 00/27] mtd: spinand: Octal DTR support Miquel Raynal
` (4 preceding siblings ...)
2026-01-09 17:18 ` [PATCH v2 05/27] mtd: spinand: Add missing check Miquel Raynal
@ 2026-01-09 17:18 ` Miquel Raynal
2026-01-09 17:18 ` [PATCH v2 07/27] mtd: spinand: Use standard return values Miquel Raynal
` (22 subsequent siblings)
28 siblings, 0 replies; 32+ messages in thread
From: Miquel Raynal @ 2026-01-09 17:18 UTC (permalink / raw)
To: Mark Brown, Richard Weinberger, Vignesh Raghavendra
Cc: Tudor Ambarus, Pratyush Yadav, Thomas Petazzoni, Steam Lin,
Santhosh Kumar K, linux-spi, linux-kernel, linux-mtd,
Miquel Raynal
SPI NAND command values are directly included in the macros defining the
ops. These are stale definitions, they are unused so drop them.
Reviewed-by: Tudor Ambarus <tudor.ambarus@linaro.org>
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
---
include/linux/mtd/spinand.h | 6 ------
1 file changed, 6 deletions(-)
diff --git a/include/linux/mtd/spinand.h b/include/linux/mtd/spinand.h
index 049d55c38d52..ad2773f1f963 100644
--- a/include/linux/mtd/spinand.h
+++ b/include/linux/mtd/spinand.h
@@ -232,12 +232,6 @@
SPI_MEM_OP_NO_DUMMY, \
SPI_MEM_OP_DATA_OUT(len, buf, 8))
-/**
- * Standard SPI NAND flash commands
- */
-#define SPINAND_CMD_PROG_LOAD_X4 0x32
-#define SPINAND_CMD_PROG_LOAD_RDM_DATA_X4 0x34
-
/* feature register */
#define REG_BLOCK_LOCK 0xa0
#define BL_ALL_UNLOCKED 0x00
--
2.51.1
______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/
^ permalink raw reply related [flat|nested] 32+ messages in thread* [PATCH v2 07/27] mtd: spinand: Use standard return values
2026-01-09 17:17 [PATCH v2 00/27] mtd: spinand: Octal DTR support Miquel Raynal
` (5 preceding siblings ...)
2026-01-09 17:18 ` [PATCH v2 06/27] mtd: spinand: Remove stale definitions Miquel Raynal
@ 2026-01-09 17:18 ` Miquel Raynal
2026-01-09 17:18 ` [PATCH v2 08/27] mtd: spinand: Decouple write enable and write disable operations Miquel Raynal
` (21 subsequent siblings)
28 siblings, 0 replies; 32+ messages in thread
From: Miquel Raynal @ 2026-01-09 17:18 UTC (permalink / raw)
To: Mark Brown, Richard Weinberger, Vignesh Raghavendra
Cc: Tudor Ambarus, Pratyush Yadav, Thomas Petazzoni, Steam Lin,
Santhosh Kumar K, linux-spi, linux-kernel, linux-mtd,
Miquel Raynal
Replace -ENOTSUPP with -EOPNOTSUPP which is as relevant in this case but
is standard.
Reviewed-by: Tudor Ambarus <tudor.ambarus@linaro.org>
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
---
drivers/mtd/nand/spi/core.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c
index 8acea0372f21..3abc56e4d23f 100644
--- a/drivers/mtd/nand/spi/core.c
+++ b/drivers/mtd/nand/spi/core.c
@@ -1419,28 +1419,28 @@ int spinand_match_and_init(struct spinand_device *spinand,
op = spinand_select_op_variant(spinand,
info->op_variants.read_cache);
if (!op)
- return -ENOTSUPP;
+ return -EOPNOTSUPP;
spinand->op_templates.read_cache = op;
op = spinand_select_op_variant(spinand,
info->op_variants.write_cache);
if (!op)
- return -ENOTSUPP;
+ return -EOPNOTSUPP;
spinand->op_templates.write_cache = op;
op = spinand_select_op_variant(spinand,
info->op_variants.update_cache);
if (!op)
- return -ENOTSUPP;
+ return -EOPNOTSUPP;
spinand->op_templates.update_cache = op;
return 0;
}
- return -ENOTSUPP;
+ return -EOPNOTSUPP;
}
static int spinand_detect(struct spinand_device *spinand)
--
2.51.1
______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/
^ permalink raw reply related [flat|nested] 32+ messages in thread* [PATCH v2 08/27] mtd: spinand: Decouple write enable and write disable operations
2026-01-09 17:17 [PATCH v2 00/27] mtd: spinand: Octal DTR support Miquel Raynal
` (6 preceding siblings ...)
2026-01-09 17:18 ` [PATCH v2 07/27] mtd: spinand: Use standard return values Miquel Raynal
@ 2026-01-09 17:18 ` Miquel Raynal
2026-01-09 17:18 ` [PATCH v2 09/27] mtd: spinand: Create an array of operation templates Miquel Raynal
` (20 subsequent siblings)
28 siblings, 0 replies; 32+ messages in thread
From: Miquel Raynal @ 2026-01-09 17:18 UTC (permalink / raw)
To: Mark Brown, Richard Weinberger, Vignesh Raghavendra
Cc: Tudor Ambarus, Pratyush Yadav, Thomas Petazzoni, Steam Lin,
Santhosh Kumar K, linux-spi, linux-kernel, linux-mtd,
Miquel Raynal
In order to introduce templates for all operations and not only for page
helpers (in order to introduce octal DDR support), decouple the WR_EN
and WR_DIS operations into two separate macros.
Adapt the callers accordingly.
There is no functional change.
Reviewed-by: Tudor Ambarus <tudor.ambarus@linaro.org>
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
---
drivers/mtd/nand/spi/core.c | 2 +-
drivers/mtd/nand/spi/esmt.c | 2 +-
drivers/mtd/nand/spi/micron.c | 2 +-
include/linux/mtd/spinand.h | 10 ++++++++--
4 files changed, 11 insertions(+), 5 deletions(-)
diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c
index 3abc56e4d23f..b6613f2651bd 100644
--- a/drivers/mtd/nand/spi/core.c
+++ b/drivers/mtd/nand/spi/core.c
@@ -362,7 +362,7 @@ static void spinand_ondie_ecc_save_status(struct nand_device *nand, u8 status)
int spinand_write_enable_op(struct spinand_device *spinand)
{
- struct spi_mem_op op = SPINAND_WR_EN_DIS_1S_0_0_OP(true);
+ struct spi_mem_op op = SPINAND_WR_EN_1S_0_0_OP;
return spi_mem_exec_op(spinand->spimem, &op);
}
diff --git a/drivers/mtd/nand/spi/esmt.c b/drivers/mtd/nand/spi/esmt.c
index e60e4ac1fd6f..adadc01e8f2f 100644
--- a/drivers/mtd/nand/spi/esmt.c
+++ b/drivers/mtd/nand/spi/esmt.c
@@ -138,7 +138,7 @@ static int f50l1g41lb_user_otp_info(struct spinand_device *spinand, size_t len,
static int f50l1g41lb_otp_lock(struct spinand_device *spinand, loff_t from,
size_t len)
{
- struct spi_mem_op write_op = SPINAND_WR_EN_DIS_1S_0_0_OP(true);
+ struct spi_mem_op write_op = SPINAND_WR_EN_1S_0_0_OP;
struct spi_mem_op exec_op = SPINAND_PROG_EXEC_1S_1S_0_OP(0);
u8 status;
int ret;
diff --git a/drivers/mtd/nand/spi/micron.c b/drivers/mtd/nand/spi/micron.c
index a49d7cb6a96d..b8130e04e8e7 100644
--- a/drivers/mtd/nand/spi/micron.c
+++ b/drivers/mtd/nand/spi/micron.c
@@ -251,7 +251,7 @@ static int mt29f2g01abagd_user_otp_info(struct spinand_device *spinand,
static int mt29f2g01abagd_otp_lock(struct spinand_device *spinand, loff_t from,
size_t len)
{
- struct spi_mem_op write_op = SPINAND_WR_EN_DIS_1S_0_0_OP(true);
+ struct spi_mem_op write_op = SPINAND_WR_EN_1S_0_0_OP;
struct spi_mem_op exec_op = SPINAND_PROG_EXEC_1S_1S_0_OP(0);
u8 status;
int ret;
diff --git a/include/linux/mtd/spinand.h b/include/linux/mtd/spinand.h
index ad2773f1f963..f7621b47b28f 100644
--- a/include/linux/mtd/spinand.h
+++ b/include/linux/mtd/spinand.h
@@ -26,8 +26,14 @@
SPI_MEM_OP_NO_DUMMY, \
SPI_MEM_OP_NO_DATA)
-#define SPINAND_WR_EN_DIS_1S_0_0_OP(enable) \
- SPI_MEM_OP(SPI_MEM_OP_CMD((enable) ? 0x06 : 0x04, 1), \
+#define SPINAND_WR_EN_1S_0_0_OP \
+ SPI_MEM_OP(SPI_MEM_OP_CMD(0x06, 1), \
+ SPI_MEM_OP_NO_ADDR, \
+ SPI_MEM_OP_NO_DUMMY, \
+ SPI_MEM_OP_NO_DATA)
+
+#define SPINAND_WR_DIS_1S_0_0_OP \
+ SPI_MEM_OP(SPI_MEM_OP_CMD(0x04, 1), \
SPI_MEM_OP_NO_ADDR, \
SPI_MEM_OP_NO_DUMMY, \
SPI_MEM_OP_NO_DATA)
--
2.51.1
______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/
^ permalink raw reply related [flat|nested] 32+ messages in thread* [PATCH v2 09/27] mtd: spinand: Create an array of operation templates
2026-01-09 17:17 [PATCH v2 00/27] mtd: spinand: Octal DTR support Miquel Raynal
` (7 preceding siblings ...)
2026-01-09 17:18 ` [PATCH v2 08/27] mtd: spinand: Decouple write enable and write disable operations Miquel Raynal
@ 2026-01-09 17:18 ` Miquel Raynal
2026-01-09 17:18 ` [PATCH v2 10/27] mtd: spinand: Make use of the operation templates through SPINAND_OP() Miquel Raynal
` (19 subsequent siblings)
28 siblings, 0 replies; 32+ messages in thread
From: Miquel Raynal @ 2026-01-09 17:18 UTC (permalink / raw)
To: Mark Brown, Richard Weinberger, Vignesh Raghavendra
Cc: Tudor Ambarus, Pratyush Yadav, Thomas Petazzoni, Steam Lin,
Santhosh Kumar K, linux-spi, linux-kernel, linux-mtd,
Miquel Raynal
Currently, the SPI NAND core implementation directly calls macros to get
the various operations in shape. These macros are specific to the bus
interface, currently only supporting the single SDR interface (any
command following the 1S-XX-XX pattern).
Introducing support for other bus interfaces (such as octal DTR) would
mean that every user of these macros should become aware of the current
bus interface and act accordingly, picking up and adapting to the
current configuration. This would add quite a bit of boilerplate, be
repetitive as well as error prone in case we miss one occurrence.
Instead, let's create a table with all SPI NAND memory operations that
are currently supported. We initialize them with the same single SDR _OP
macros as before. This opens the possibility for users of the individual
macros to make use of these templates instead. This way, when we will add
another bus interface, we can just switch to another set of templates
and all users will magically fill in their spi_mem_op structures with
the correct ops.
The existing read, write and update cache variants are also moved in
this template array, which is barely noticeable by callers as we also
add a structure member pointing to it.
Reviewed-by: Tudor Ambarus <tudor.ambarus@linaro.org>
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
---
drivers/mtd/nand/spi/core.c | 38 +++++++++++++++++++++++++++----------
drivers/mtd/nand/spi/winbond.c | 4 ++--
include/linux/mtd/spinand.h | 43 +++++++++++++++++++++++++++++++++---------
3 files changed, 64 insertions(+), 21 deletions(-)
diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c
index b6613f2651bd..3e1b6c6c6a22 100644
--- a/drivers/mtd/nand/spi/core.c
+++ b/drivers/mtd/nand/spi/core.c
@@ -184,9 +184,9 @@ static int spinand_init_quad_enable(struct spinand_device *spinand)
if (!(spinand->flags & SPINAND_HAS_QE_BIT))
return 0;
- if (spinand->op_templates.read_cache->data.buswidth == 4 ||
- spinand->op_templates.write_cache->data.buswidth == 4 ||
- spinand->op_templates.update_cache->data.buswidth == 4)
+ if (spinand->op_templates->read_cache->data.buswidth == 4 ||
+ spinand->op_templates->write_cache->data.buswidth == 4 ||
+ spinand->op_templates->update_cache->data.buswidth == 4)
enable = true;
return spinand_upd_cfg(spinand, CFG_QUAD_ENABLE,
@@ -1154,7 +1154,7 @@ static int spinand_create_dirmap(struct spinand_device *spinand,
info.offset = plane << fls(nand->memorg.pagesize);
info.length = nanddev_page_size(nand) + nanddev_per_page_oobsize(nand);
- info.op_tmpl = *spinand->op_templates.update_cache;
+ 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))
@@ -1162,7 +1162,7 @@ static int spinand_create_dirmap(struct spinand_device *spinand,
spinand->dirmaps[plane].wdesc = desc;
- info.op_tmpl = *spinand->op_templates.read_cache;
+ info.op_tmpl = *spinand->op_templates->read_cache;
desc = spinand_create_rdesc(spinand, &info);
if (IS_ERR(desc))
return PTR_ERR(desc);
@@ -1177,7 +1177,7 @@ static int spinand_create_dirmap(struct spinand_device *spinand,
}
info.length = nanddev_page_size(nand) + nanddev_per_page_oobsize(nand);
- info.op_tmpl = *spinand->op_templates.update_cache;
+ 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);
@@ -1186,7 +1186,7 @@ static int spinand_create_dirmap(struct spinand_device *spinand,
spinand->dirmaps[plane].wdesc_ecc = desc;
- info.op_tmpl = *spinand->op_templates.read_cache;
+ info.op_tmpl = *spinand->op_templates->read_cache;
info.op_tmpl.data.ecc = true;
desc = spinand_create_rdesc(spinand, &info);
if (IS_ERR(desc))
@@ -1323,6 +1323,22 @@ static void spinand_manufacturer_cleanup(struct spinand_device *spinand)
return spinand->manufacturer->ops->cleanup(spinand);
}
+static void spinand_init_ssdr_templates(struct spinand_device *spinand)
+{
+ struct spinand_mem_ops *tmpl = &spinand->ssdr_op_templates;
+
+ tmpl->reset = (struct spi_mem_op)SPINAND_RESET_1S_0_0_OP;
+ tmpl->readid = (struct spi_mem_op)SPINAND_READID_1S_1S_1S_OP(0, 0, NULL, 0);
+ tmpl->wr_en = (struct spi_mem_op)SPINAND_WR_EN_1S_0_0_OP;
+ tmpl->wr_dis = (struct spi_mem_op)SPINAND_WR_DIS_1S_0_0_OP;
+ tmpl->set_feature = (struct spi_mem_op)SPINAND_SET_FEATURE_1S_1S_1S_OP(0, NULL);
+ tmpl->get_feature = (struct spi_mem_op)SPINAND_GET_FEATURE_1S_1S_1S_OP(0, NULL);
+ tmpl->blk_erase = (struct spi_mem_op)SPINAND_BLK_ERASE_1S_1S_0_OP(0);
+ tmpl->page_read = (struct spi_mem_op)SPINAND_PAGE_READ_1S_1S_0_OP(0);
+ tmpl->prog_exec = (struct spi_mem_op)SPINAND_PROG_EXEC_1S_1S_0_OP(0);
+ spinand->op_templates = &spinand->ssdr_op_templates;
+}
+
static const struct spi_mem_op *
spinand_select_op_variant(struct spinand_device *spinand,
const struct spinand_op_variants *variants)
@@ -1421,21 +1437,21 @@ int spinand_match_and_init(struct spinand_device *spinand,
if (!op)
return -EOPNOTSUPP;
- spinand->op_templates.read_cache = op;
+ spinand->ssdr_op_templates.read_cache = op;
op = spinand_select_op_variant(spinand,
info->op_variants.write_cache);
if (!op)
return -EOPNOTSUPP;
- spinand->op_templates.write_cache = op;
+ spinand->ssdr_op_templates.write_cache = op;
op = spinand_select_op_variant(spinand,
info->op_variants.update_cache);
if (!op)
return -EOPNOTSUPP;
- spinand->op_templates.update_cache = op;
+ spinand->ssdr_op_templates.update_cache = op;
return 0;
}
@@ -1550,6 +1566,8 @@ static int spinand_init(struct spinand_device *spinand)
if (!spinand->scratchbuf)
return -ENOMEM;
+ spinand_init_ssdr_templates(spinand);
+
ret = spinand_detect(spinand);
if (ret)
goto err_free_bufs;
diff --git a/drivers/mtd/nand/spi/winbond.c b/drivers/mtd/nand/spi/winbond.c
index 4870b2d5edb2..d5799c2df065 100644
--- a/drivers/mtd/nand/spi/winbond.c
+++ b/drivers/mtd/nand/spi/winbond.c
@@ -291,7 +291,7 @@ static int w25n0xjw_hs_cfg(struct spinand_device *spinand)
u8 sr4;
int ret;
- op = spinand->op_templates.read_cache;
+ op = spinand->op_templates->read_cache;
if (op->cmd.dtr || op->addr.dtr || op->dummy.dtr || op->data.dtr)
hs = false;
else if (op->cmd.buswidth == 1 && op->addr.buswidth == 1 &&
@@ -355,7 +355,7 @@ static int w35n0xjw_vcr_cfg(struct spinand_device *spinand)
u8 io_mode;
int ret;
- op = spinand->op_templates.read_cache;
+ op = spinand->op_templates->read_cache;
single = (op->cmd.buswidth == 1 && op->addr.buswidth == 1 && op->data.buswidth == 1);
dtr = (op->cmd.dtr || op->addr.dtr || op->data.dtr);
diff --git a/include/linux/mtd/spinand.h b/include/linux/mtd/spinand.h
index f7621b47b28f..2691b5818f77 100644
--- a/include/linux/mtd/spinand.h
+++ b/include/linux/mtd/spinand.h
@@ -599,6 +599,36 @@ struct spinand_dirmap {
struct spi_mem_dirmap_desc *rdesc_ecc;
};
+/**
+ * struct spinand_mem_ops - SPI NAND memory operations
+ * @reset: reset op template
+ * @readid: read ID op template
+ * @wr_en: write enable op template
+ * @wr_dis: write disable op template
+ * @set_feature: set feature op template
+ * @get_feature: get feature op template
+ * @blk_erase: blk erase op template
+ * @page_read: page read op template
+ * @prog_exec: prog exec op template
+ * @read_cache: read cache op template
+ * @write_cache: write cache op template
+ * @update_cache: update cache op template
+ */
+struct spinand_mem_ops {
+ struct spi_mem_op reset;
+ struct spi_mem_op readid;
+ struct spi_mem_op wr_en;
+ struct spi_mem_op wr_dis;
+ struct spi_mem_op set_feature;
+ struct spi_mem_op get_feature;
+ struct spi_mem_op blk_erase;
+ struct spi_mem_op page_read;
+ struct spi_mem_op prog_exec;
+ const struct spi_mem_op *read_cache;
+ const struct spi_mem_op *write_cache;
+ const struct spi_mem_op *update_cache;
+};
+
/**
* struct spinand_device - SPI NAND device instance
* @base: NAND device instance
@@ -606,10 +636,8 @@ struct spinand_dirmap {
* @lock: lock used to serialize accesses to the NAND
* @id: NAND ID as returned by READ_ID
* @flags: NAND flags
- * @op_templates: various SPI mem op templates
- * @op_templates.read_cache: read cache op template
- * @op_templates.write_cache: write cache op template
- * @op_templates.update_cache: update cache op template
+ * @ssdr_op_templates: Templates for all single SDR SPI mem operations
+ * @op_templates: Templates for all SPI mem operations
* @select_target: select a specific target/die. Usually called before sending
* a command addressing a page or an eraseblock embedded in
* this die. Only required if your chip exposes several dies
@@ -643,11 +671,8 @@ struct spinand_device {
struct spinand_id id;
u32 flags;
- struct {
- const struct spi_mem_op *read_cache;
- const struct spi_mem_op *write_cache;
- const struct spi_mem_op *update_cache;
- } op_templates;
+ struct spinand_mem_ops ssdr_op_templates;
+ struct spinand_mem_ops *op_templates;
struct spinand_dirmap *dirmaps;
--
2.51.1
______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/
^ permalink raw reply related [flat|nested] 32+ messages in thread* [PATCH v2 10/27] mtd: spinand: Make use of the operation templates through SPINAND_OP()
2026-01-09 17:17 [PATCH v2 00/27] mtd: spinand: Octal DTR support Miquel Raynal
` (8 preceding siblings ...)
2026-01-09 17:18 ` [PATCH v2 09/27] mtd: spinand: Create an array of operation templates Miquel Raynal
@ 2026-01-09 17:18 ` Miquel Raynal
2026-01-09 17:18 ` [PATCH v2 11/27] mtd: spinand: macronix: Convert vendor specific operation to SPINAND_OP() Miquel Raynal
` (18 subsequent siblings)
28 siblings, 0 replies; 32+ messages in thread
From: Miquel Raynal @ 2026-01-09 17:18 UTC (permalink / raw)
To: Mark Brown, Richard Weinberger, Vignesh Raghavendra
Cc: Tudor Ambarus, Pratyush Yadav, Thomas Petazzoni, Steam Lin,
Santhosh Kumar K, linux-spi, linux-kernel, linux-mtd,
Miquel Raynal
Create a SPINAND_OP() macro to which we give the name of the operation
we want. This macro retrieves the correct operation template based on
the current bus interface (currently only single SDR, will soon be
extended to octal DTR) and fills it with the usual parameters.
This macro makes the transition from calling directly the low-level
macros into using the (bus interface dependent) templates very smooth.
Use it in all places that can be trivially converted. At this stage
there is no functional change expected, until octal DTR support gets
added.
Reviewed-by: Tudor Ambarus <tudor.ambarus@linaro.org>
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
---
drivers/mtd/nand/spi/core.c | 110 +++++++++++++++++++++++++++++++++-----
drivers/mtd/nand/spi/esmt.c | 4 +-
drivers/mtd/nand/spi/gigadevice.c | 8 +--
drivers/mtd/nand/spi/macronix.c | 4 +-
drivers/mtd/nand/spi/micron.c | 8 +--
drivers/mtd/nand/spi/toshiba.c | 3 +-
drivers/mtd/nand/spi/winbond.c | 3 +-
include/linux/mtd/spinand.h | 8 +++
8 files changed, 121 insertions(+), 27 deletions(-)
diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c
index 3e1b6c6c6a22..609a955788fa 100644
--- a/drivers/mtd/nand/spi/core.c
+++ b/drivers/mtd/nand/spi/core.c
@@ -20,10 +20,94 @@
#include <linux/spi/spi.h>
#include <linux/spi/spi-mem.h>
+static struct spi_mem_op
+spinand_fill_reset_op(struct spinand_device *spinand)
+{
+ return spinand->op_templates->reset;
+}
+
+static struct spi_mem_op
+spinand_fill_readid_op(struct spinand_device *spinand,
+ u8 naddr, u8 ndummy, void *buf, unsigned int len)
+{
+ struct spi_mem_op op = spinand->op_templates->readid;
+
+ op.addr.nbytes = naddr;
+ op.dummy.nbytes = ndummy;
+ op.data.buf.in = buf;
+ op.data.nbytes = len;
+
+ return op;
+}
+
+struct spi_mem_op
+spinand_fill_wr_en_op(struct spinand_device *spinand)
+{
+ return spinand->op_templates->wr_en;
+}
+
+static __maybe_unused struct spi_mem_op
+spinand_fill_wr_dis_op(struct spinand_device *spinand)
+{
+ return spinand->op_templates->wr_dis;
+}
+
+struct spi_mem_op
+spinand_fill_set_feature_op(struct spinand_device *spinand, u64 reg, const void *valptr)
+{
+ struct spi_mem_op op = spinand->op_templates->set_feature;
+
+ op.addr.val = reg;
+ op.data.buf.out = valptr;
+
+ return op;
+}
+
+struct spi_mem_op
+spinand_fill_get_feature_op(struct spinand_device *spinand, u64 reg, void *valptr)
+{
+ struct spi_mem_op op = spinand->op_templates->get_feature;
+
+ op.addr.val = reg;
+ op.data.buf.in = valptr;
+
+ return op;
+}
+
+static struct spi_mem_op
+spinand_fill_blk_erase_op(struct spinand_device *spinand, u64 addr)
+{
+ struct spi_mem_op op = spinand->op_templates->blk_erase;
+
+ op.addr.val = addr;
+
+ return op;
+}
+
+static struct spi_mem_op
+spinand_fill_page_read_op(struct spinand_device *spinand, u64 addr)
+{
+ struct spi_mem_op op = spinand->op_templates->page_read;
+
+ op.addr.val = addr;
+
+ return op;
+}
+
+struct spi_mem_op
+spinand_fill_prog_exec_op(struct spinand_device *spinand, u64 addr)
+{
+ struct spi_mem_op op = spinand->op_templates->prog_exec;
+
+ op.addr.val = addr;
+
+ return op;
+}
+
int spinand_read_reg_op(struct spinand_device *spinand, u8 reg, u8 *val)
{
- struct spi_mem_op op = SPINAND_GET_FEATURE_1S_1S_1S_OP(reg,
- spinand->scratchbuf);
+ struct spi_mem_op op = SPINAND_OP(spinand, get_feature,
+ reg, spinand->scratchbuf);
int ret;
ret = spi_mem_exec_op(spinand->spimem, &op);
@@ -36,8 +120,8 @@ int spinand_read_reg_op(struct spinand_device *spinand, u8 reg, u8 *val)
int spinand_write_reg_op(struct spinand_device *spinand, u8 reg, u8 val)
{
- struct spi_mem_op op = SPINAND_SET_FEATURE_1S_1S_1S_OP(reg,
- spinand->scratchbuf);
+ struct spi_mem_op op = SPINAND_OP(spinand, set_feature,
+ reg, spinand->scratchbuf);
*spinand->scratchbuf = val;
return spi_mem_exec_op(spinand->spimem, &op);
@@ -362,7 +446,7 @@ static void spinand_ondie_ecc_save_status(struct nand_device *nand, u8 status)
int spinand_write_enable_op(struct spinand_device *spinand)
{
- struct spi_mem_op op = SPINAND_WR_EN_1S_0_0_OP;
+ struct spi_mem_op op = SPINAND_OP(spinand, wr_en);
return spi_mem_exec_op(spinand->spimem, &op);
}
@@ -372,7 +456,7 @@ static int spinand_load_page_op(struct spinand_device *spinand,
{
struct nand_device *nand = spinand_to_nand(spinand);
unsigned int row = nanddev_pos_to_row(nand, &req->pos);
- struct spi_mem_op op = SPINAND_PAGE_READ_1S_1S_0_OP(row);
+ struct spi_mem_op op = SPINAND_OP(spinand, page_read, row);
return spi_mem_exec_op(spinand->spimem, &op);
}
@@ -527,7 +611,7 @@ static int spinand_program_op(struct spinand_device *spinand,
{
struct nand_device *nand = spinand_to_nand(spinand);
unsigned int row = nanddev_pos_to_row(nand, &req->pos);
- struct spi_mem_op op = SPINAND_PROG_EXEC_1S_1S_0_OP(row);
+ struct spi_mem_op op = SPINAND_OP(spinand, prog_exec, row);
return spi_mem_exec_op(spinand->spimem, &op);
}
@@ -537,7 +621,7 @@ static int spinand_erase_op(struct spinand_device *spinand,
{
struct nand_device *nand = spinand_to_nand(spinand);
unsigned int row = nanddev_pos_to_row(nand, pos);
- struct spi_mem_op op = SPINAND_BLK_ERASE_1S_1S_0_OP(row);
+ struct spi_mem_op op = SPINAND_OP(spinand, blk_erase, row);
return spi_mem_exec_op(spinand->spimem, &op);
}
@@ -557,8 +641,8 @@ static int spinand_erase_op(struct spinand_device *spinand,
int spinand_wait(struct spinand_device *spinand, unsigned long initial_delay_us,
unsigned long poll_delay_us, u8 *s)
{
- struct spi_mem_op op = SPINAND_GET_FEATURE_1S_1S_1S_OP(REG_STATUS,
- spinand->scratchbuf);
+ struct spi_mem_op op = SPINAND_OP(spinand, get_feature,
+ REG_STATUS, spinand->scratchbuf);
u8 status;
int ret;
@@ -591,8 +675,8 @@ int spinand_wait(struct spinand_device *spinand, unsigned long initial_delay_us,
static int spinand_read_id_op(struct spinand_device *spinand, u8 naddr,
u8 ndummy, u8 *buf)
{
- struct spi_mem_op op = SPINAND_READID_1S_1S_1S_OP(
- naddr, ndummy, spinand->scratchbuf, SPINAND_MAX_ID_LEN);
+ struct spi_mem_op op = SPINAND_OP(spinand, readid,
+ naddr, ndummy, spinand->scratchbuf, SPINAND_MAX_ID_LEN);
int ret;
ret = spi_mem_exec_op(spinand->spimem, &op);
@@ -604,7 +688,7 @@ static int spinand_read_id_op(struct spinand_device *spinand, u8 naddr,
static int spinand_reset_op(struct spinand_device *spinand)
{
- struct spi_mem_op op = SPINAND_RESET_1S_0_0_OP;
+ struct spi_mem_op op = SPINAND_OP(spinand, reset);
int ret;
ret = spi_mem_exec_op(spinand->spimem, &op);
diff --git a/drivers/mtd/nand/spi/esmt.c b/drivers/mtd/nand/spi/esmt.c
index adadc01e8f2f..3020aa89a495 100644
--- a/drivers/mtd/nand/spi/esmt.c
+++ b/drivers/mtd/nand/spi/esmt.c
@@ -138,8 +138,8 @@ static int f50l1g41lb_user_otp_info(struct spinand_device *spinand, size_t len,
static int f50l1g41lb_otp_lock(struct spinand_device *spinand, loff_t from,
size_t len)
{
- struct spi_mem_op write_op = SPINAND_WR_EN_1S_0_0_OP;
- struct spi_mem_op exec_op = SPINAND_PROG_EXEC_1S_1S_0_OP(0);
+ struct spi_mem_op write_op = SPINAND_OP(spinand, wr_en);
+ struct spi_mem_op exec_op = SPINAND_OP(spinand, prog_exec, 0);
u8 status;
int ret;
diff --git a/drivers/mtd/nand/spi/gigadevice.c b/drivers/mtd/nand/spi/gigadevice.c
index 72ad36c9a126..e4380208edd0 100644
--- a/drivers/mtd/nand/spi/gigadevice.c
+++ b/drivers/mtd/nand/spi/gigadevice.c
@@ -266,8 +266,8 @@ static int gd5fxgq4uexxg_ecc_get_status(struct spinand_device *spinand,
u8 status)
{
u8 status2;
- struct spi_mem_op op = SPINAND_GET_FEATURE_1S_1S_1S_OP(GD5FXGQXXEXXG_REG_STATUS2,
- spinand->scratchbuf);
+ struct spi_mem_op op = SPINAND_OP(spinand, get_feature,
+ GD5FXGQXXEXXG_REG_STATUS2, spinand->scratchbuf);
int ret;
switch (status & STATUS_ECC_MASK) {
@@ -309,8 +309,8 @@ static int gd5fxgq5xexxg_ecc_get_status(struct spinand_device *spinand,
u8 status)
{
u8 status2;
- struct spi_mem_op op = SPINAND_GET_FEATURE_1S_1S_1S_OP(GD5FXGQXXEXXG_REG_STATUS2,
- spinand->scratchbuf);
+ struct spi_mem_op op = SPINAND_OP(spinand, get_feature,
+ GD5FXGQXXEXXG_REG_STATUS2, spinand->scratchbuf);
int ret;
switch (status & STATUS_ECC_MASK) {
diff --git a/drivers/mtd/nand/spi/macronix.c b/drivers/mtd/nand/spi/macronix.c
index edf63b9996cf..143cc120bdec 100644
--- a/drivers/mtd/nand/spi/macronix.c
+++ b/drivers/mtd/nand/spi/macronix.c
@@ -148,8 +148,8 @@ static int macronix_set_cont_read(struct spinand_device *spinand, bool enable)
static int macronix_set_read_retry(struct spinand_device *spinand,
unsigned int retry_mode)
{
- struct spi_mem_op op = SPINAND_SET_FEATURE_1S_1S_1S_OP(MACRONIX_FEATURE_ADDR_READ_RETRY,
- spinand->scratchbuf);
+ struct spi_mem_op op = SPINAND_OP(spinand, set_feature,
+ MACRONIX_FEATURE_ADDR_READ_RETRY, spinand->scratchbuf);
*spinand->scratchbuf = retry_mode;
return spi_mem_exec_op(spinand->spimem, &op);
diff --git a/drivers/mtd/nand/spi/micron.c b/drivers/mtd/nand/spi/micron.c
index b8130e04e8e7..36f6cbbd7462 100644
--- a/drivers/mtd/nand/spi/micron.c
+++ b/drivers/mtd/nand/spi/micron.c
@@ -137,8 +137,8 @@ static const struct mtd_ooblayout_ops micron_4_ooblayout = {
static int micron_select_target(struct spinand_device *spinand,
unsigned int target)
{
- struct spi_mem_op op = SPINAND_SET_FEATURE_1S_1S_1S_OP(MICRON_DIE_SELECT_REG,
- spinand->scratchbuf);
+ struct spi_mem_op op = SPINAND_OP(spinand, set_feature,
+ MICRON_DIE_SELECT_REG, spinand->scratchbuf);
if (target > 1)
return -EINVAL;
@@ -251,8 +251,8 @@ static int mt29f2g01abagd_user_otp_info(struct spinand_device *spinand,
static int mt29f2g01abagd_otp_lock(struct spinand_device *spinand, loff_t from,
size_t len)
{
- struct spi_mem_op write_op = SPINAND_WR_EN_1S_0_0_OP;
- struct spi_mem_op exec_op = SPINAND_PROG_EXEC_1S_1S_0_OP(0);
+ struct spi_mem_op write_op = SPINAND_OP(spinand, wr_en);
+ struct spi_mem_op exec_op = SPINAND_OP(spinand, prog_exec, 0);
u8 status;
int ret;
diff --git a/drivers/mtd/nand/spi/toshiba.c b/drivers/mtd/nand/spi/toshiba.c
index 6530257ac0be..ef649162ee68 100644
--- a/drivers/mtd/nand/spi/toshiba.c
+++ b/drivers/mtd/nand/spi/toshiba.c
@@ -73,7 +73,8 @@ static int tx58cxgxsxraix_ecc_get_status(struct spinand_device *spinand,
{
struct nand_device *nand = spinand_to_nand(spinand);
u8 mbf = 0;
- struct spi_mem_op op = SPINAND_GET_FEATURE_1S_1S_1S_OP(0x30, spinand->scratchbuf);
+ struct spi_mem_op op = SPINAND_OP(spinand, get_feature,
+ 0x30, spinand->scratchbuf);
switch (status & STATUS_ECC_MASK) {
case STATUS_ECC_NO_BITFLIPS:
diff --git a/drivers/mtd/nand/spi/winbond.c b/drivers/mtd/nand/spi/winbond.c
index d5799c2df065..bfec5d037f25 100644
--- a/drivers/mtd/nand/spi/winbond.c
+++ b/drivers/mtd/nand/spi/winbond.c
@@ -251,7 +251,8 @@ static int w25n02kv_ecc_get_status(struct spinand_device *spinand,
{
struct nand_device *nand = spinand_to_nand(spinand);
u8 mbf = 0;
- struct spi_mem_op op = SPINAND_GET_FEATURE_1S_1S_1S_OP(0x30, spinand->scratchbuf);
+ struct spi_mem_op op = SPINAND_OP(spinand, get_feature,
+ 0x30, spinand->scratchbuf);
switch (status & STATUS_ECC_MASK) {
case STATUS_ECC_NO_BITFLIPS:
diff --git a/include/linux/mtd/spinand.h b/include/linux/mtd/spinand.h
index 2691b5818f77..f579245c90a6 100644
--- a/include/linux/mtd/spinand.h
+++ b/include/linux/mtd/spinand.h
@@ -702,6 +702,14 @@ struct spinand_device {
unsigned int retry_mode);
};
+struct spi_mem_op spinand_fill_wr_en_op(struct spinand_device *spinand);
+struct spi_mem_op spinand_fill_set_feature_op(struct spinand_device *spinand, u64 reg, const void *valptr);
+struct spi_mem_op spinand_fill_get_feature_op(struct spinand_device *spinand, u64 reg, void *valptr);
+struct spi_mem_op spinand_fill_prog_exec_op(struct spinand_device *spinand, u64 addr);
+
+#define SPINAND_OP(spinand, op_name, ...) \
+ spinand_fill_ ## op_name ## _op(spinand, ##__VA_ARGS__)
+
/**
* mtd_to_spinand() - Get the SPI NAND device attached to an MTD instance
* @mtd: MTD instance
--
2.51.1
______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/
^ permalink raw reply related [flat|nested] 32+ messages in thread* [PATCH v2 11/27] mtd: spinand: macronix: Convert vendor specific operation to SPINAND_OP()
2026-01-09 17:17 [PATCH v2 00/27] mtd: spinand: Octal DTR support Miquel Raynal
` (9 preceding siblings ...)
2026-01-09 17:18 ` [PATCH v2 10/27] mtd: spinand: Make use of the operation templates through SPINAND_OP() Miquel Raynal
@ 2026-01-09 17:18 ` Miquel Raynal
2026-01-09 17:18 ` [PATCH v2 12/27] mtd: spinand: winbond: Convert W25N " Miquel Raynal
` (17 subsequent siblings)
28 siblings, 0 replies; 32+ messages in thread
From: Miquel Raynal @ 2026-01-09 17:18 UTC (permalink / raw)
To: Mark Brown, Richard Weinberger, Vignesh Raghavendra
Cc: Tudor Ambarus, Pratyush Yadav, Thomas Petazzoni, Steam Lin,
Santhosh Kumar K, linux-spi, linux-kernel, linux-mtd,
Miquel Raynal
Macronix chips require a vendor specific operation to read the ECC
status register. Instead of defining this op only in the function that
needs it, hiding it from the core, make it a proper define like all
other spi-mem operations, and implement the necessary
spinand_fill_*_op() helper to make the SPINAND_OP() macro work. This way
we can use it from any function without any extra handling outside of
this helper when we will convert the core to support octal DDR busses.
Reviewed-by: Tudor Ambarus <tudor.ambarus@linaro.org>
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
---
drivers/mtd/nand/spi/macronix.c | 20 +++++++++++++++-----
1 file changed, 15 insertions(+), 5 deletions(-)
diff --git a/drivers/mtd/nand/spi/macronix.c b/drivers/mtd/nand/spi/macronix.c
index 143cc120bdec..a847ea8f49a8 100644
--- a/drivers/mtd/nand/spi/macronix.c
+++ b/drivers/mtd/nand/spi/macronix.c
@@ -41,6 +41,18 @@ static SPINAND_OP_VARIANTS(update_cache_variants,
SPINAND_PROG_LOAD_1S_1S_4S_OP(false, 0, NULL, 0),
SPINAND_PROG_LOAD_1S_1S_1S_OP(false, 0, NULL, 0));
+#define SPINAND_MACRONIX_READ_ECCSR_1S_0_1S(buf) \
+ SPI_MEM_OP(SPI_MEM_OP_CMD(0x7c, 1), \
+ SPI_MEM_OP_NO_ADDR, \
+ SPI_MEM_OP_DUMMY(1, 1), \
+ SPI_MEM_OP_DATA_IN(1, buf, 1))
+
+static struct spi_mem_op
+spinand_fill_macronix_read_eccsr_op(struct spinand_device *spinand, void *valptr)
+{
+ return (struct spi_mem_op)SPINAND_MACRONIX_READ_ECCSR_1S_0_1S(valptr);
+}
+
static int mx35lfxge4ab_ooblayout_ecc(struct mtd_info *mtd, int section,
struct mtd_oob_region *region)
{
@@ -67,12 +79,10 @@ static const struct mtd_ooblayout_ops mx35lfxge4ab_ooblayout = {
static int macronix_get_eccsr(struct spinand_device *spinand, u8 *eccsr)
{
struct macronix_priv *priv = spinand->priv;
- struct spi_mem_op op = SPI_MEM_OP(SPI_MEM_OP_CMD(0x7c, 1),
- SPI_MEM_OP_NO_ADDR,
- SPI_MEM_OP_DUMMY(1, 1),
- SPI_MEM_OP_DATA_IN(1, eccsr, 1));
+ struct spi_mem_op op = SPINAND_OP(spinand, macronix_read_eccsr, eccsr);
+ int ret;
- int ret = spi_mem_exec_op(spinand->spimem, &op);
+ ret = spi_mem_exec_op(spinand->spimem, &op);
if (ret)
return ret;
--
2.51.1
______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/
^ permalink raw reply related [flat|nested] 32+ messages in thread* [PATCH v2 12/27] mtd: spinand: winbond: Convert W25N specific operation to SPINAND_OP()
2026-01-09 17:17 [PATCH v2 00/27] mtd: spinand: Octal DTR support Miquel Raynal
` (10 preceding siblings ...)
2026-01-09 17:18 ` [PATCH v2 11/27] mtd: spinand: macronix: Convert vendor specific operation to SPINAND_OP() Miquel Raynal
@ 2026-01-09 17:18 ` Miquel Raynal
2026-01-09 17:18 ` [PATCH v2 13/27] mtd: spinand: winbond: Convert W35N " Miquel Raynal
` (16 subsequent siblings)
28 siblings, 0 replies; 32+ messages in thread
From: Miquel Raynal @ 2026-01-09 17:18 UTC (permalink / raw)
To: Mark Brown, Richard Weinberger, Vignesh Raghavendra
Cc: Tudor Ambarus, Pratyush Yadav, Thomas Petazzoni, Steam Lin,
Santhosh Kumar K, linux-spi, linux-kernel, linux-mtd,
Miquel Raynal
Winbond W25N* chips require a vendor specific operation to select the
target. Instead of defining this op only in the function that
needs it, hiding it from the core, make it a proper define like all
other spi-mem operations, and implement the necessary
spinand_fill_*_op() helper to make the SPINAND_OP() macro work. This way
we can use it from any function without any extra handling outside of
this helper when we will convert the core to support octal DDR busses.
Reviewed-by: Tudor Ambarus <tudor.ambarus@linaro.org>
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
---
drivers/mtd/nand/spi/winbond.c | 20 ++++++++++++++------
1 file changed, 14 insertions(+), 6 deletions(-)
diff --git a/drivers/mtd/nand/spi/winbond.c b/drivers/mtd/nand/spi/winbond.c
index bfec5d037f25..dde59f8f63f5 100644
--- a/drivers/mtd/nand/spi/winbond.c
+++ b/drivers/mtd/nand/spi/winbond.c
@@ -87,6 +87,18 @@ static SPINAND_OP_VARIANTS(update_cache_variants,
SPINAND_PROG_LOAD_1S_1S_4S_OP(false, 0, NULL, 0),
SPINAND_PROG_LOAD_1S_1S_1S_OP(false, 0, NULL, 0));
+#define SPINAND_WINBOND_SELECT_TARGET_1S_0_1S(buf) \
+ SPI_MEM_OP(SPI_MEM_OP_CMD(0xc2, 1), \
+ SPI_MEM_OP_NO_ADDR, \
+ SPI_MEM_OP_NO_DUMMY, \
+ SPI_MEM_OP_DATA_OUT(1, buf, 1))
+
+static struct spi_mem_op
+spinand_fill_winbond_select_target_op(struct spinand_device *spinand, void *valptr)
+{
+ return (struct spi_mem_op)SPINAND_WINBOND_SELECT_TARGET_1S_0_1S(valptr);
+}
+
static int w25m02gv_ooblayout_ecc(struct mtd_info *mtd, int section,
struct mtd_oob_region *region)
{
@@ -119,12 +131,8 @@ static const struct mtd_ooblayout_ops w25m02gv_ooblayout = {
static int w25m02gv_select_target(struct spinand_device *spinand,
unsigned int target)
{
- struct spi_mem_op op = SPI_MEM_OP(SPI_MEM_OP_CMD(0xc2, 1),
- SPI_MEM_OP_NO_ADDR,
- SPI_MEM_OP_NO_DUMMY,
- SPI_MEM_OP_DATA_OUT(1,
- spinand->scratchbuf,
- 1));
+ struct spi_mem_op op = SPINAND_OP(spinand, winbond_select_target,
+ spinand->scratchbuf);
*spinand->scratchbuf = target;
return spi_mem_exec_op(spinand->spimem, &op);
--
2.51.1
______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/
^ permalink raw reply related [flat|nested] 32+ messages in thread* [PATCH v2 13/27] mtd: spinand: winbond: Convert W35N specific operation to SPINAND_OP()
2026-01-09 17:17 [PATCH v2 00/27] mtd: spinand: Octal DTR support Miquel Raynal
` (11 preceding siblings ...)
2026-01-09 17:18 ` [PATCH v2 12/27] mtd: spinand: winbond: Convert W25N " Miquel Raynal
@ 2026-01-09 17:18 ` Miquel Raynal
2026-01-09 17:18 ` [PATCH v2 14/27] mtd: spinand: List vendor specific operations and make sure they are supported Miquel Raynal
` (15 subsequent siblings)
28 siblings, 0 replies; 32+ messages in thread
From: Miquel Raynal @ 2026-01-09 17:18 UTC (permalink / raw)
To: Mark Brown, Richard Weinberger, Vignesh Raghavendra
Cc: Tudor Ambarus, Pratyush Yadav, Thomas Petazzoni, Steam Lin,
Santhosh Kumar K, linux-spi, linux-kernel, linux-mtd,
Miquel Raynal
Winbond W35N* chips require a vendor specific operation to write their
VCR register (a configuration register, typically used for tuning the
number of dummy cycles and switching to a different bus
interface). Instead of defining this op only in the function that needs
it, hiding it from the core, make it a proper define like all other
spi-mem operations, and implement the necessary spinand_fill_*_op()
helper to make the SPINAND_OP() macro work. This way we can use it from
any function without any extra handling outside of this helper when we
will convert the core to support octal DDR busses.
Reviewed-by: Tudor Ambarus <tudor.ambarus@linaro.org>
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
---
drivers/mtd/nand/spi/winbond.c | 19 ++++++++++++++-----
1 file changed, 14 insertions(+), 5 deletions(-)
diff --git a/drivers/mtd/nand/spi/winbond.c b/drivers/mtd/nand/spi/winbond.c
index dde59f8f63f5..3003ad7e83ee 100644
--- a/drivers/mtd/nand/spi/winbond.c
+++ b/drivers/mtd/nand/spi/winbond.c
@@ -87,6 +87,18 @@ static SPINAND_OP_VARIANTS(update_cache_variants,
SPINAND_PROG_LOAD_1S_1S_4S_OP(false, 0, NULL, 0),
SPINAND_PROG_LOAD_1S_1S_1S_OP(false, 0, NULL, 0));
+#define SPINAND_WINBOND_WRITE_VCR_1S_1S_1S(reg, buf) \
+ SPI_MEM_OP(SPI_MEM_OP_CMD(0x81, 1), \
+ SPI_MEM_OP_ADDR(3, reg, 1), \
+ SPI_MEM_OP_NO_DUMMY, \
+ SPI_MEM_OP_DATA_OUT(1, buf, 1))
+
+static struct spi_mem_op
+spinand_fill_winbond_write_vcr_op(struct spinand_device *spinand, u8 reg, void *valptr)
+{
+ return (struct spi_mem_op)SPINAND_WINBOND_WRITE_VCR_1S_1S_1S(reg, valptr);
+}
+
#define SPINAND_WINBOND_SELECT_TARGET_1S_0_1S(buf) \
SPI_MEM_OP(SPI_MEM_OP_CMD(0xc2, 1), \
SPI_MEM_OP_NO_ADDR, \
@@ -329,11 +341,8 @@ static int w25n0xjw_hs_cfg(struct spinand_device *spinand)
static int w35n0xjw_write_vcr(struct spinand_device *spinand, u8 reg, u8 val)
{
- struct spi_mem_op op =
- SPI_MEM_OP(SPI_MEM_OP_CMD(0x81, 1),
- SPI_MEM_OP_ADDR(3, reg, 1),
- SPI_MEM_OP_NO_DUMMY,
- SPI_MEM_OP_DATA_OUT(1, spinand->scratchbuf, 1));
+ struct spi_mem_op op = SPINAND_OP(spinand, winbond_write_vcr,
+ reg, spinand->scratchbuf);
int ret;
*spinand->scratchbuf = val;
--
2.51.1
______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/
^ permalink raw reply related [flat|nested] 32+ messages in thread* [PATCH v2 14/27] mtd: spinand: List vendor specific operations and make sure they are supported
2026-01-09 17:17 [PATCH v2 00/27] mtd: spinand: Octal DTR support Miquel Raynal
` (12 preceding siblings ...)
2026-01-09 17:18 ` [PATCH v2 13/27] mtd: spinand: winbond: Convert W35N " Miquel Raynal
@ 2026-01-09 17:18 ` Miquel Raynal
2026-01-09 17:18 ` [PATCH v2 15/27] mtd: spinand: macronix: Register vendor specific operation Miquel Raynal
` (14 subsequent siblings)
28 siblings, 0 replies; 32+ messages in thread
From: Miquel Raynal @ 2026-01-09 17:18 UTC (permalink / raw)
To: Mark Brown, Richard Weinberger, Vignesh Raghavendra
Cc: Tudor Ambarus, Pratyush Yadav, Thomas Petazzoni, Steam Lin,
Santhosh Kumar K, linux-spi, linux-kernel, linux-mtd,
Miquel Raynal
It is probably safe to expect that all SPI controller drivers will ever
support all the most basic SPI NAND operations, such as write enable,
register reads, page program, block erases, etc. However, what about
vendor specific operations? So far nobody complained about it, but as we
are about to introduce octal DTR support, and as none of the SPI NAND
instruction set is defined in any standard, we must remain careful about
these extra operations.
One way to make sure we do not blindly get ourselves in strange
situations with vendor commands failing silently is to make the check
once for all, while probing the chip. However at this stage we have no
such list, so let's add the necessary infrastructure to allow:
- registering vendor operations,
- checking they are actually supported when appropriate.
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
---
drivers/mtd/nand/spi/core.c | 26 ++++++++++++++++++++++++++
include/linux/mtd/spinand.h | 5 +++++
2 files changed, 31 insertions(+)
diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c
index 609a955788fa..88d87a96ddb0 100644
--- a/drivers/mtd/nand/spi/core.c
+++ b/drivers/mtd/nand/spi/core.c
@@ -1423,6 +1423,27 @@ static void spinand_init_ssdr_templates(struct spinand_device *spinand)
spinand->op_templates = &spinand->ssdr_op_templates;
}
+static int spinand_support_vendor_ops(struct spinand_device *spinand,
+ const struct spinand_info *info)
+{
+ int i;
+
+ /*
+ * The vendor ops array is only used in order to verify this chip and all its memory
+ * operations are supported. If we see patterns emerging, we could ideally name these
+ * operations and define them at the SPI NAND core level instead.
+ * For now, this only serves as a sanity check.
+ */
+ for (i = 0; i < info->vendor_ops->nops; i++) {
+ const struct spi_mem_op *op = &info->vendor_ops->ops[i];
+
+ if (!spi_mem_supports_op(spinand->spimem, op))
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
static const struct spi_mem_op *
spinand_select_op_variant(struct spinand_device *spinand,
const struct spinand_op_variants *variants)
@@ -1492,6 +1513,7 @@ int spinand_match_and_init(struct spinand_device *spinand,
u8 *id = spinand->id.data;
struct nand_device *nand = spinand_to_nand(spinand);
unsigned int i;
+ int ret;
for (i = 0; i < table_size; i++) {
const struct spinand_info *info = &table[i];
@@ -1537,6 +1559,10 @@ int spinand_match_and_init(struct spinand_device *spinand,
spinand->ssdr_op_templates.update_cache = op;
+ ret = spinand_support_vendor_ops(spinand, info);
+ if (ret)
+ return ret;
+
return 0;
}
diff --git a/include/linux/mtd/spinand.h b/include/linux/mtd/spinand.h
index f579245c90a6..88871287c739 100644
--- a/include/linux/mtd/spinand.h
+++ b/include/linux/mtd/spinand.h
@@ -493,6 +493,7 @@ struct spinand_user_otp {
* @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
+ * @vendor_ops: vendor specific operations
* @select_target: function used to select a target/die. Required only for
* multi-die chips
* @configure_chip: Align the chip configuration with the core settings
@@ -517,6 +518,7 @@ struct spinand_info {
const struct spinand_op_variants *write_cache;
const struct spinand_op_variants *update_cache;
} op_variants;
+ const struct spinand_op_variants *vendor_ops;
int (*select_target)(struct spinand_device *spinand,
unsigned int target);
int (*configure_chip)(struct spinand_device *spinand);
@@ -543,6 +545,9 @@ struct spinand_info {
.update_cache = __update, \
}
+#define SPINAND_INFO_VENDOR_OPS(__ops) \
+ .vendor_ops = __ops
+
#define SPINAND_ECCINFO(__ooblayout, __get_status) \
.eccinfo = { \
.ooblayout = __ooblayout, \
--
2.51.1
______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/
^ permalink raw reply related [flat|nested] 32+ messages in thread* [PATCH v2 15/27] mtd: spinand: macronix: Register vendor specific operation
2026-01-09 17:17 [PATCH v2 00/27] mtd: spinand: Octal DTR support Miquel Raynal
` (13 preceding siblings ...)
2026-01-09 17:18 ` [PATCH v2 14/27] mtd: spinand: List vendor specific operations and make sure they are supported Miquel Raynal
@ 2026-01-09 17:18 ` Miquel Raynal
2026-01-09 17:18 ` [PATCH v2 16/27] mtd: spinand: winbond: Register W25N " Miquel Raynal
` (13 subsequent siblings)
28 siblings, 0 replies; 32+ messages in thread
From: Miquel Raynal @ 2026-01-09 17:18 UTC (permalink / raw)
To: Mark Brown, Richard Weinberger, Vignesh Raghavendra
Cc: Tudor Ambarus, Pratyush Yadav, Thomas Petazzoni, Steam Lin,
Santhosh Kumar K, linux-spi, linux-kernel, linux-mtd,
Miquel Raynal
Provide the Macronix specific "read ECC status register" operation so
that the core can verify if it is supported by the controller before
using it.
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
---
drivers/mtd/nand/spi/macronix.c | 23 +++++++++++++++++++++++
1 file changed, 23 insertions(+)
diff --git a/drivers/mtd/nand/spi/macronix.c b/drivers/mtd/nand/spi/macronix.c
index a847ea8f49a8..6b7cbcc6e287 100644
--- a/drivers/mtd/nand/spi/macronix.c
+++ b/drivers/mtd/nand/spi/macronix.c
@@ -47,6 +47,9 @@ static SPINAND_OP_VARIANTS(update_cache_variants,
SPI_MEM_OP_DUMMY(1, 1), \
SPI_MEM_OP_DATA_IN(1, buf, 1))
+static SPINAND_OP_VARIANTS(macronix_ops,
+ SPINAND_MACRONIX_READ_ECCSR_1S_0_1S(NULL));
+
static struct spi_mem_op
spinand_fill_macronix_read_eccsr_op(struct spinand_device *spinand, void *valptr)
{
@@ -174,6 +177,7 @@ static const struct spinand_info macronix_spinand_table[] = {
&write_cache_variants,
&update_cache_variants),
SPINAND_HAS_QE_BIT,
+ SPINAND_INFO_VENDOR_OPS(¯onix_ops),
SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
macronix_ecc_get_status)),
SPINAND_INFO("MX35LF2GE4AB",
@@ -195,6 +199,7 @@ static const struct spinand_info macronix_spinand_table[] = {
&write_cache_variants,
&update_cache_variants),
SPINAND_HAS_QE_BIT,
+ SPINAND_INFO_VENDOR_OPS(¯onix_ops),
SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
macronix_ecc_get_status),
SPINAND_CONT_READ(macronix_set_cont_read),
@@ -208,6 +213,7 @@ static const struct spinand_info macronix_spinand_table[] = {
&write_cache_variants,
&update_cache_variants),
SPINAND_HAS_QE_BIT,
+ SPINAND_INFO_VENDOR_OPS(¯onix_ops),
SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
macronix_ecc_get_status),
SPINAND_CONT_READ(macronix_set_cont_read),
@@ -278,6 +284,7 @@ static const struct spinand_info macronix_spinand_table[] = {
&write_cache_variants,
&update_cache_variants),
SPINAND_HAS_QE_BIT,
+ SPINAND_INFO_VENDOR_OPS(¯onix_ops),
SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
macronix_ecc_get_status)),
SPINAND_INFO("MX31UF1GE4BC",
@@ -288,6 +295,7 @@ static const struct spinand_info macronix_spinand_table[] = {
&write_cache_variants,
&update_cache_variants),
SPINAND_HAS_QE_BIT,
+ SPINAND_INFO_VENDOR_OPS(¯onix_ops),
SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
macronix_ecc_get_status)),
@@ -301,6 +309,7 @@ static const struct spinand_info macronix_spinand_table[] = {
SPINAND_HAS_QE_BIT |
SPINAND_HAS_PROG_PLANE_SELECT_BIT |
SPINAND_HAS_READ_PLANE_SELECT_BIT,
+ SPINAND_INFO_VENDOR_OPS(¯onix_ops),
SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
macronix_ecc_get_status)),
SPINAND_INFO("MX35UF4G24AD",
@@ -312,6 +321,7 @@ static const struct spinand_info macronix_spinand_table[] = {
&update_cache_variants),
SPINAND_HAS_QE_BIT |
SPINAND_HAS_PROG_PLANE_SELECT_BIT,
+ SPINAND_INFO_VENDOR_OPS(¯onix_ops),
SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
macronix_ecc_get_status),
SPINAND_READ_RETRY(MACRONIX_NUM_READ_RETRY_MODES,
@@ -324,6 +334,7 @@ static const struct spinand_info macronix_spinand_table[] = {
&write_cache_variants,
&update_cache_variants),
SPINAND_HAS_QE_BIT,
+ SPINAND_INFO_VENDOR_OPS(¯onix_ops),
SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
macronix_ecc_get_status),
SPINAND_READ_RETRY(MACRONIX_NUM_READ_RETRY_MODES,
@@ -336,6 +347,7 @@ static const struct spinand_info macronix_spinand_table[] = {
&write_cache_variants,
&update_cache_variants),
SPINAND_HAS_QE_BIT,
+ SPINAND_INFO_VENDOR_OPS(¯onix_ops),
SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
macronix_ecc_get_status),
SPINAND_CONT_READ(macronix_set_cont_read),
@@ -351,6 +363,7 @@ static const struct spinand_info macronix_spinand_table[] = {
SPINAND_HAS_QE_BIT |
SPINAND_HAS_PROG_PLANE_SELECT_BIT |
SPINAND_HAS_READ_PLANE_SELECT_BIT,
+ SPINAND_INFO_VENDOR_OPS(¯onix_ops),
SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
macronix_ecc_get_status)),
SPINAND_INFO("MX35UF2G24AD",
@@ -362,6 +375,7 @@ static const struct spinand_info macronix_spinand_table[] = {
&update_cache_variants),
SPINAND_HAS_QE_BIT |
SPINAND_HAS_PROG_PLANE_SELECT_BIT,
+ SPINAND_INFO_VENDOR_OPS(¯onix_ops),
SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
macronix_ecc_get_status),
SPINAND_READ_RETRY(MACRONIX_NUM_READ_RETRY_MODES,
@@ -374,6 +388,7 @@ static const struct spinand_info macronix_spinand_table[] = {
&write_cache_variants,
&update_cache_variants),
SPINAND_HAS_QE_BIT,
+ SPINAND_INFO_VENDOR_OPS(¯onix_ops),
SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
macronix_ecc_get_status),
SPINAND_READ_RETRY(MACRONIX_NUM_READ_RETRY_MODES,
@@ -386,6 +401,7 @@ static const struct spinand_info macronix_spinand_table[] = {
&write_cache_variants,
&update_cache_variants),
SPINAND_HAS_QE_BIT,
+ SPINAND_INFO_VENDOR_OPS(¯onix_ops),
SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
macronix_ecc_get_status),
SPINAND_CONT_READ(macronix_set_cont_read),
@@ -399,6 +415,7 @@ static const struct spinand_info macronix_spinand_table[] = {
&write_cache_variants,
&update_cache_variants),
SPINAND_HAS_QE_BIT,
+ SPINAND_INFO_VENDOR_OPS(¯onix_ops),
SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
macronix_ecc_get_status),
SPINAND_CONT_READ(macronix_set_cont_read)),
@@ -410,6 +427,7 @@ static const struct spinand_info macronix_spinand_table[] = {
&write_cache_variants,
&update_cache_variants),
SPINAND_HAS_QE_BIT,
+ SPINAND_INFO_VENDOR_OPS(¯onix_ops),
SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
macronix_ecc_get_status)),
SPINAND_INFO("MX35UF1G24AD",
@@ -420,6 +438,7 @@ static const struct spinand_info macronix_spinand_table[] = {
&write_cache_variants,
&update_cache_variants),
SPINAND_HAS_QE_BIT,
+ SPINAND_INFO_VENDOR_OPS(¯onix_ops),
SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
macronix_ecc_get_status),
SPINAND_READ_RETRY(MACRONIX_NUM_READ_RETRY_MODES,
@@ -432,6 +451,7 @@ static const struct spinand_info macronix_spinand_table[] = {
&write_cache_variants,
&update_cache_variants),
SPINAND_HAS_QE_BIT,
+ SPINAND_INFO_VENDOR_OPS(¯onix_ops),
SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
macronix_ecc_get_status),
SPINAND_CONT_READ(macronix_set_cont_read),
@@ -445,6 +465,7 @@ static const struct spinand_info macronix_spinand_table[] = {
&write_cache_variants,
&update_cache_variants),
SPINAND_HAS_QE_BIT,
+ SPINAND_INFO_VENDOR_OPS(¯onix_ops),
SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
macronix_ecc_get_status),
SPINAND_CONT_READ(macronix_set_cont_read)),
@@ -456,6 +477,7 @@ static const struct spinand_info macronix_spinand_table[] = {
&write_cache_variants,
&update_cache_variants),
SPINAND_HAS_QE_BIT,
+ SPINAND_INFO_VENDOR_OPS(¯onix_ops),
SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
macronix_ecc_get_status)),
SPINAND_INFO("MX3UF2GE4BC",
@@ -466,6 +488,7 @@ static const struct spinand_info macronix_spinand_table[] = {
&write_cache_variants,
&update_cache_variants),
SPINAND_HAS_QE_BIT,
+ SPINAND_INFO_VENDOR_OPS(¯onix_ops),
SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
macronix_ecc_get_status)),
};
--
2.51.1
______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/
^ permalink raw reply related [flat|nested] 32+ messages in thread* [PATCH v2 16/27] mtd: spinand: winbond: Register W25N vendor specific operation
2026-01-09 17:17 [PATCH v2 00/27] mtd: spinand: Octal DTR support Miquel Raynal
` (14 preceding siblings ...)
2026-01-09 17:18 ` [PATCH v2 15/27] mtd: spinand: macronix: Register vendor specific operation Miquel Raynal
@ 2026-01-09 17:18 ` Miquel Raynal
2026-01-09 17:18 ` [PATCH v2 17/27] mtd: spinand: winbond: Register W35N " Miquel Raynal
` (12 subsequent siblings)
28 siblings, 0 replies; 32+ messages in thread
From: Miquel Raynal @ 2026-01-09 17:18 UTC (permalink / raw)
To: Mark Brown, Richard Weinberger, Vignesh Raghavendra
Cc: Tudor Ambarus, Pratyush Yadav, Thomas Petazzoni, Steam Lin,
Santhosh Kumar K, linux-spi, linux-kernel, linux-mtd,
Miquel Raynal
Provide the Winbond W25N specific "select target" operation to let the
core verify it is supported by the controller before using it.
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
---
drivers/mtd/nand/spi/winbond.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/drivers/mtd/nand/spi/winbond.c b/drivers/mtd/nand/spi/winbond.c
index 3003ad7e83ee..36053f35ee5e 100644
--- a/drivers/mtd/nand/spi/winbond.c
+++ b/drivers/mtd/nand/spi/winbond.c
@@ -105,6 +105,9 @@ spinand_fill_winbond_write_vcr_op(struct spinand_device *spinand, u8 reg, void *
SPI_MEM_OP_NO_DUMMY, \
SPI_MEM_OP_DATA_OUT(1, buf, 1))
+static SPINAND_OP_VARIANTS(winbond_w25_ops,
+ SPINAND_WINBOND_SELECT_TARGET_1S_0_1S(NULL));
+
static struct spi_mem_op
spinand_fill_winbond_select_target_op(struct spinand_device *spinand, void *valptr)
{
@@ -497,6 +500,7 @@ static const struct spinand_info winbond_spinand_table[] = {
&write_cache_variants,
&update_cache_variants),
0,
+ SPINAND_INFO_VENDOR_OPS(&winbond_w25_ops),
SPINAND_ECCINFO(&w25m02gv_ooblayout, NULL),
SPINAND_SELECT_TARGET(w25m02gv_select_target)),
SPINAND_INFO("W25N02JW", /* high-speed 1.8V */
--
2.51.1
______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/
^ permalink raw reply related [flat|nested] 32+ messages in thread* [PATCH v2 17/27] mtd: spinand: winbond: Register W35N vendor specific operation
2026-01-09 17:17 [PATCH v2 00/27] mtd: spinand: Octal DTR support Miquel Raynal
` (15 preceding siblings ...)
2026-01-09 17:18 ` [PATCH v2 16/27] mtd: spinand: winbond: Register W25N " Miquel Raynal
@ 2026-01-09 17:18 ` Miquel Raynal
2026-01-09 17:18 ` [PATCH v2 18/27] mtd: spinand: winbond: Fix style Miquel Raynal
` (11 subsequent siblings)
28 siblings, 0 replies; 32+ messages in thread
From: Miquel Raynal @ 2026-01-09 17:18 UTC (permalink / raw)
To: Mark Brown, Richard Weinberger, Vignesh Raghavendra
Cc: Tudor Ambarus, Pratyush Yadav, Thomas Petazzoni, Steam Lin,
Santhosh Kumar K, linux-spi, linux-kernel, linux-mtd,
Miquel Raynal
Provide the Winbond W35N specific "write VCR register" operation to let
the core verify it is supported by the controller before using it.
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
---
drivers/mtd/nand/spi/winbond.c | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/drivers/mtd/nand/spi/winbond.c b/drivers/mtd/nand/spi/winbond.c
index 36053f35ee5e..1c13dba08369 100644
--- a/drivers/mtd/nand/spi/winbond.c
+++ b/drivers/mtd/nand/spi/winbond.c
@@ -93,6 +93,9 @@ static SPINAND_OP_VARIANTS(update_cache_variants,
SPI_MEM_OP_NO_DUMMY, \
SPI_MEM_OP_DATA_OUT(1, buf, 1))
+static SPINAND_OP_VARIANTS(winbond_w35_ops,
+ SPINAND_WINBOND_WRITE_VCR_1S_1S_1S(0, NULL));
+
static struct spi_mem_op
spinand_fill_winbond_write_vcr_op(struct spinand_device *spinand, u8 reg, void *valptr)
{
@@ -469,6 +472,7 @@ static const struct spinand_info winbond_spinand_table[] = {
&write_cache_octal_variants,
&update_cache_octal_variants),
0,
+ SPINAND_INFO_VENDOR_OPS(&winbond_w35_ops),
SPINAND_ECCINFO(&w35n01jw_ooblayout, NULL),
SPINAND_CONFIGURE_CHIP(w35n0xjw_vcr_cfg)),
SPINAND_INFO("W35N02JW", /* 1.8V */
@@ -479,6 +483,7 @@ static const struct spinand_info winbond_spinand_table[] = {
&write_cache_octal_variants,
&update_cache_octal_variants),
0,
+ SPINAND_INFO_VENDOR_OPS(&winbond_w35_ops),
SPINAND_ECCINFO(&w35n01jw_ooblayout, NULL),
SPINAND_CONFIGURE_CHIP(w35n0xjw_vcr_cfg)),
SPINAND_INFO("W35N04JW", /* 1.8V */
@@ -489,6 +494,7 @@ static const struct spinand_info winbond_spinand_table[] = {
&write_cache_octal_variants,
&update_cache_octal_variants),
0,
+ SPINAND_INFO_VENDOR_OPS(&winbond_w35_ops),
SPINAND_ECCINFO(&w35n01jw_ooblayout, NULL),
SPINAND_CONFIGURE_CHIP(w35n0xjw_vcr_cfg)),
/* 2G-bit densities */
--
2.51.1
______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/
^ permalink raw reply related [flat|nested] 32+ messages in thread* [PATCH v2 18/27] mtd: spinand: winbond: Fix style
2026-01-09 17:17 [PATCH v2 00/27] mtd: spinand: Octal DTR support Miquel Raynal
` (16 preceding siblings ...)
2026-01-09 17:18 ` [PATCH v2 17/27] mtd: spinand: winbond: Register W35N " Miquel Raynal
@ 2026-01-09 17:18 ` Miquel Raynal
2026-01-09 17:18 ` [PATCH v2 19/27] mtd: spinand: winbond: Rename IO_MODE register macro Miquel Raynal
` (10 subsequent siblings)
28 siblings, 0 replies; 32+ messages in thread
From: Miquel Raynal @ 2026-01-09 17:18 UTC (permalink / raw)
To: Mark Brown, Richard Weinberger, Vignesh Raghavendra
Cc: Tudor Ambarus, Pratyush Yadav, Thomas Petazzoni, Steam Lin,
Santhosh Kumar K, linux-spi, linux-kernel, linux-mtd,
Miquel Raynal
Add a missing new line in the middle of the driver.
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
---
drivers/mtd/nand/spi/winbond.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/drivers/mtd/nand/spi/winbond.c b/drivers/mtd/nand/spi/winbond.c
index 1c13dba08369..7eade2251f7b 100644
--- a/drivers/mtd/nand/spi/winbond.c
+++ b/drivers/mtd/nand/spi/winbond.c
@@ -408,6 +408,7 @@ static int w35n0xjw_vcr_cfg(struct spinand_device *spinand)
default:
return -EINVAL;
}
+
ret = w35n0xjw_write_vcr(spinand, W35N01JW_VCR_DUMMY_CLOCK_REG, dummy_cycles);
if (ret)
return ret;
--
2.51.1
______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/
^ permalink raw reply related [flat|nested] 32+ messages in thread* [PATCH v2 19/27] mtd: spinand: winbond: Rename IO_MODE register macro
2026-01-09 17:17 [PATCH v2 00/27] mtd: spinand: Octal DTR support Miquel Raynal
` (17 preceding siblings ...)
2026-01-09 17:18 ` [PATCH v2 18/27] mtd: spinand: winbond: Fix style Miquel Raynal
@ 2026-01-09 17:18 ` Miquel Raynal
2026-01-09 17:18 ` [PATCH v2 20/27] mtd: spinand: winbond: Configure the IO mode after the dummy cycles Miquel Raynal
` (9 subsequent siblings)
28 siblings, 0 replies; 32+ messages in thread
From: Miquel Raynal @ 2026-01-09 17:18 UTC (permalink / raw)
To: Mark Brown, Richard Weinberger, Vignesh Raghavendra
Cc: Tudor Ambarus, Pratyush Yadav, Thomas Petazzoni, Steam Lin,
Santhosh Kumar K, linux-spi, linux-kernel, linux-mtd,
Miquel Raynal
Suffix the macro name with *_REG to align with the rest of the driver.
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
---
drivers/mtd/nand/spi/winbond.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/mtd/nand/spi/winbond.c b/drivers/mtd/nand/spi/winbond.c
index 7eade2251f7b..b16963637683 100644
--- a/drivers/mtd/nand/spi/winbond.c
+++ b/drivers/mtd/nand/spi/winbond.c
@@ -22,7 +22,7 @@
#define W25N0XJW_SR4 0xD0
#define W25N0XJW_SR4_HS BIT(2)
-#define W35N01JW_VCR_IO_MODE 0x00
+#define W35N01JW_VCR_IO_MODE_REG 0x00
#define W35N01JW_VCR_IO_MODE_SINGLE_SDR 0xFF
#define W35N01JW_VCR_IO_MODE_OCTAL_SDR 0xDF
#define W35N01JW_VCR_IO_MODE_OCTAL_DDR_DS 0xE7
@@ -392,7 +392,7 @@ static int w35n0xjw_vcr_cfg(struct spinand_device *spinand)
else
return -EINVAL;
- ret = w35n0xjw_write_vcr(spinand, W35N01JW_VCR_IO_MODE, io_mode);
+ ret = w35n0xjw_write_vcr(spinand, W35N01JW_VCR_IO_MODE_REG, io_mode);
if (ret)
return ret;
--
2.51.1
______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/
^ permalink raw reply related [flat|nested] 32+ messages in thread* [PATCH v2 20/27] mtd: spinand: winbond: Configure the IO mode after the dummy cycles
2026-01-09 17:17 [PATCH v2 00/27] mtd: spinand: Octal DTR support Miquel Raynal
` (18 preceding siblings ...)
2026-01-09 17:18 ` [PATCH v2 19/27] mtd: spinand: winbond: Rename IO_MODE register macro Miquel Raynal
@ 2026-01-09 17:18 ` Miquel Raynal
2026-01-09 17:18 ` [PATCH v2 21/27] mtd: spinand: Gather all the bus interface steps in one single function Miquel Raynal
` (8 subsequent siblings)
28 siblings, 0 replies; 32+ messages in thread
From: Miquel Raynal @ 2026-01-09 17:18 UTC (permalink / raw)
To: Mark Brown, Richard Weinberger, Vignesh Raghavendra
Cc: Tudor Ambarus, Pratyush Yadav, Thomas Petazzoni, Steam Lin,
Santhosh Kumar K, linux-spi, linux-kernel, linux-mtd,
Miquel Raynal
When we will change the bus interface, the action that actually performs
the transition is the IO mode register write. This means after the IO
mode register write, we should use the new bus interface. But the
->configure_chip() hook itself is not responsible of making this change
official, it is the caller that must act according to the return value.
Reorganize this helper to first configure the dummy cycles before
possibly switching to another bus interface.
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
---
drivers/mtd/nand/spi/winbond.c | 30 +++++++++++++++---------------
1 file changed, 15 insertions(+), 15 deletions(-)
diff --git a/drivers/mtd/nand/spi/winbond.c b/drivers/mtd/nand/spi/winbond.c
index b16963637683..1d79a8ae7920 100644
--- a/drivers/mtd/nand/spi/winbond.c
+++ b/drivers/mtd/nand/spi/winbond.c
@@ -381,21 +381,6 @@ static int w35n0xjw_vcr_cfg(struct spinand_device *spinand)
op = spinand->op_templates->read_cache;
- single = (op->cmd.buswidth == 1 && op->addr.buswidth == 1 && op->data.buswidth == 1);
- dtr = (op->cmd.dtr || op->addr.dtr || op->data.dtr);
- if (single && !dtr)
- io_mode = W35N01JW_VCR_IO_MODE_SINGLE_SDR;
- else if (!single && !dtr)
- io_mode = W35N01JW_VCR_IO_MODE_OCTAL_SDR;
- else if (!single && dtr)
- io_mode = W35N01JW_VCR_IO_MODE_OCTAL_DDR;
- else
- return -EINVAL;
-
- ret = w35n0xjw_write_vcr(spinand, W35N01JW_VCR_IO_MODE_REG, io_mode);
- if (ret)
- return ret;
-
dummy_cycles = ((op->dummy.nbytes * 8) / op->dummy.buswidth) / (op->dummy.dtr ? 2 : 1);
switch (dummy_cycles) {
case 8:
@@ -413,6 +398,21 @@ static int w35n0xjw_vcr_cfg(struct spinand_device *spinand)
if (ret)
return ret;
+ single = (op->cmd.buswidth == 1 && op->addr.buswidth == 1 && op->data.buswidth == 1);
+ dtr = (op->cmd.dtr && op->addr.dtr && op->data.dtr);
+ if (single && !dtr)
+ io_mode = W35N01JW_VCR_IO_MODE_SINGLE_SDR;
+ else if (!single && !dtr)
+ io_mode = W35N01JW_VCR_IO_MODE_OCTAL_SDR;
+ else if (!single && dtr)
+ io_mode = W35N01JW_VCR_IO_MODE_OCTAL_DDR;
+ else
+ return -EINVAL;
+
+ ret = w35n0xjw_write_vcr(spinand, W35N01JW_VCR_IO_MODE_REG, io_mode);
+ if (ret)
+ return ret;
+
return 0;
}
--
2.51.1
______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/
^ permalink raw reply related [flat|nested] 32+ messages in thread* [PATCH v2 21/27] mtd: spinand: Gather all the bus interface steps in one single function
2026-01-09 17:17 [PATCH v2 00/27] mtd: spinand: Octal DTR support Miquel Raynal
` (19 preceding siblings ...)
2026-01-09 17:18 ` [PATCH v2 20/27] mtd: spinand: winbond: Configure the IO mode after the dummy cycles Miquel Raynal
@ 2026-01-09 17:18 ` Miquel Raynal
2026-01-09 17:18 ` [PATCH v2 22/27] mtd: spinand: Add support for setting a bus interface Miquel Raynal
` (7 subsequent siblings)
28 siblings, 0 replies; 32+ messages in thread
From: Miquel Raynal @ 2026-01-09 17:18 UTC (permalink / raw)
To: Mark Brown, Richard Weinberger, Vignesh Raghavendra
Cc: Tudor Ambarus, Pratyush Yadav, Thomas Petazzoni, Steam Lin,
Santhosh Kumar K, linux-spi, linux-kernel, linux-mtd,
Miquel Raynal
Writing the quad enable bit in one helper and doing the chip
configuration in another does not make much sense from a bus interface
setup point of view.
Instead, let's create a broader helper which is going to be in charge of
all the bus configuration steps at once. This will specifically allow to
transition to octal DDR mode, and even fallback to quad (if suppoorted)
or single mode otherwise.
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
---
drivers/mtd/nand/spi/core.c | 62 +++++++++++++++++++++++++++------------------
1 file changed, 37 insertions(+), 25 deletions(-)
diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c
index 88d87a96ddb0..54a32cea3755 100644
--- a/drivers/mtd/nand/spi/core.c
+++ b/drivers/mtd/nand/spi/core.c
@@ -261,18 +261,9 @@ static int spinand_init_cfg_cache(struct spinand_device *spinand)
return 0;
}
-static int spinand_init_quad_enable(struct spinand_device *spinand)
+static int spinand_init_quad_enable(struct spinand_device *spinand,
+ bool enable)
{
- bool enable = false;
-
- if (!(spinand->flags & SPINAND_HAS_QE_BIT))
- return 0;
-
- if (spinand->op_templates->read_cache->data.buswidth == 4 ||
- spinand->op_templates->write_cache->data.buswidth == 4 ||
- spinand->op_templates->update_cache->data.buswidth == 4)
- enable = true;
-
return spinand_upd_cfg(spinand, CFG_QUAD_ENABLE,
enable ? CFG_QUAD_ENABLE : 0);
}
@@ -1391,12 +1382,6 @@ static int spinand_manufacturer_init(struct spinand_device *spinand)
return ret;
}
- if (spinand->configure_chip) {
- ret = spinand->configure_chip(spinand);
- if (ret)
- return ret;
- }
-
return 0;
}
@@ -1602,6 +1587,31 @@ static int spinand_detect(struct spinand_device *spinand)
return 0;
}
+static int spinand_configure_chip(struct spinand_device *spinand)
+{
+ bool quad_enable = false;
+ int ret;
+
+ if (spinand->flags & SPINAND_HAS_QE_BIT) {
+ if (spinand->ssdr_op_templates.read_cache->data.buswidth == 4 ||
+ spinand->ssdr_op_templates.write_cache->data.buswidth == 4 ||
+ spinand->ssdr_op_templates.update_cache->data.buswidth == 4)
+ quad_enable = true;
+ }
+
+ ret = spinand_init_quad_enable(spinand, quad_enable);
+ if (ret)
+ return ret;
+
+ if (spinand->configure_chip) {
+ ret = spinand->configure_chip(spinand);
+ if (ret)
+ return ret;
+ }
+
+ return ret;
+}
+
static int spinand_init_flash(struct spinand_device *spinand)
{
struct device *dev = &spinand->spimem->spi->dev;
@@ -1612,10 +1622,6 @@ static int spinand_init_flash(struct spinand_device *spinand)
if (ret)
return ret;
- ret = spinand_init_quad_enable(spinand);
- if (ret)
- return ret;
-
ret = spinand_upd_cfg(spinand, CFG_OTP_ENABLE, 0);
if (ret)
return ret;
@@ -1628,19 +1634,25 @@ static int spinand_init_flash(struct spinand_device *spinand)
return ret;
}
+ ret = spinand_configure_chip(spinand);
+ if (ret)
+ goto 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);
if (ret)
- break;
+ goto manuf_cleanup;
ret = spinand_lock_block(spinand, BL_ALL_UNLOCKED);
if (ret)
- break;
+ goto manuf_cleanup;
}
- if (ret)
- spinand_manufacturer_cleanup(spinand);
+ return 0;
+
+manuf_cleanup:
+ spinand_manufacturer_cleanup(spinand);
return ret;
}
--
2.51.1
______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/
^ permalink raw reply related [flat|nested] 32+ messages in thread* [PATCH v2 22/27] mtd: spinand: Add support for setting a bus interface
2026-01-09 17:17 [PATCH v2 00/27] mtd: spinand: Octal DTR support Miquel Raynal
` (20 preceding siblings ...)
2026-01-09 17:18 ` [PATCH v2 21/27] mtd: spinand: Gather all the bus interface steps in one single function Miquel Raynal
@ 2026-01-09 17:18 ` Miquel Raynal
2026-01-09 17:18 ` [PATCH v2 23/27] mtd: spinand: Propagate the bus interface across core helpers Miquel Raynal
` (6 subsequent siblings)
28 siblings, 0 replies; 32+ messages in thread
From: Miquel Raynal @ 2026-01-09 17:18 UTC (permalink / raw)
To: Mark Brown, Richard Weinberger, Vignesh Raghavendra
Cc: Tudor Ambarus, Pratyush Yadav, Thomas Petazzoni, Steam Lin,
Santhosh Kumar K, linux-spi, linux-kernel, linux-mtd,
Miquel Raynal
Create a bus interface enumeration, currently only containing the
one we support: SSDR, for single SDR, so any operation whose command is
sent over a single data line in SDR mode, ie. any operation matching
1S-XX-XX.
The main spinand_device structure gets a new parameter to store this
enumeration, for now unused. Of course it is set to SSDR during the SSDR
templates initialization to further clarify the state we are in at the
moment.
This member is subject to be used to know in which bus configuration we
and be updated by the core when we switch to faster mode(s).
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
---
drivers/mtd/nand/spi/core.c | 1 +
include/linux/mtd/spinand.h | 10 ++++++++++
2 files changed, 11 insertions(+)
diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c
index 54a32cea3755..a5166b49020c 100644
--- a/drivers/mtd/nand/spi/core.c
+++ b/drivers/mtd/nand/spi/core.c
@@ -1406,6 +1406,7 @@ static void spinand_init_ssdr_templates(struct spinand_device *spinand)
tmpl->page_read = (struct spi_mem_op)SPINAND_PAGE_READ_1S_1S_0_OP(0);
tmpl->prog_exec = (struct spi_mem_op)SPINAND_PROG_EXEC_1S_1S_0_OP(0);
spinand->op_templates = &spinand->ssdr_op_templates;
+ spinand->bus_iface = SSDR;
}
static int spinand_support_vendor_ops(struct spinand_device *spinand,
diff --git a/include/linux/mtd/spinand.h b/include/linux/mtd/spinand.h
index 88871287c739..027923841bba 100644
--- a/include/linux/mtd/spinand.h
+++ b/include/linux/mtd/spinand.h
@@ -481,6 +481,14 @@ struct spinand_user_otp {
const struct spinand_user_otp_ops *ops;
};
+/**
+ * enum spinand_bus_interface - SPI NAND bus interface types
+ * @SSDR: Bus configuration supporting all 1S-XX-XX operations, including dual and quad
+ */
+enum spinand_bus_interface {
+ SSDR,
+};
+
/**
* struct spinand_info - Structure used to describe SPI NAND chips
* @model: model name
@@ -643,6 +651,7 @@ struct spinand_mem_ops {
* @flags: NAND flags
* @ssdr_op_templates: Templates for all single SDR SPI mem operations
* @op_templates: Templates for all SPI mem operations
+ * @bus_iface: Current bus interface
* @select_target: select a specific target/die. Usually called before sending
* a command addressing a page or an eraseblock embedded in
* this die. Only required if your chip exposes several dies
@@ -678,6 +687,7 @@ struct spinand_device {
struct spinand_mem_ops ssdr_op_templates;
struct spinand_mem_ops *op_templates;
+ enum spinand_bus_interface bus_iface;
struct spinand_dirmap *dirmaps;
--
2.51.1
______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/
^ permalink raw reply related [flat|nested] 32+ messages in thread* [PATCH v2 23/27] mtd: spinand: Propagate the bus interface across core helpers
2026-01-09 17:17 [PATCH v2 00/27] mtd: spinand: Octal DTR support Miquel Raynal
` (21 preceding siblings ...)
2026-01-09 17:18 ` [PATCH v2 22/27] mtd: spinand: Add support for setting a bus interface Miquel Raynal
@ 2026-01-09 17:18 ` Miquel Raynal
2026-01-09 17:18 ` [PATCH v2 24/27] mtd: spinand: Give the bus interface to the configuration helper Miquel Raynal
` (5 subsequent siblings)
28 siblings, 0 replies; 32+ messages in thread
From: Miquel Raynal @ 2026-01-09 17:18 UTC (permalink / raw)
To: Mark Brown, Richard Weinberger, Vignesh Raghavendra
Cc: Tudor Ambarus, Pratyush Yadav, Thomas Petazzoni, Steam Lin,
Santhosh Kumar K, linux-spi, linux-kernel, linux-mtd,
Miquel Raynal
For now all drivers provide SSDR variants only. When we add support for
ODTR modes, there will be a need to differentiate the type of variant we
target as well as the need to check if we support one or the other type
of operations.
Pass this parameter to lower level helpers, which for now is unused, in
order to simplify the patch introducing ODTR support.
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
---
drivers/mtd/nand/spi/core.c | 13 +++++++------
1 file changed, 7 insertions(+), 6 deletions(-)
diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c
index a5166b49020c..019594182c60 100644
--- a/drivers/mtd/nand/spi/core.c
+++ b/drivers/mtd/nand/spi/core.c
@@ -1410,7 +1410,8 @@ static void spinand_init_ssdr_templates(struct spinand_device *spinand)
}
static int spinand_support_vendor_ops(struct spinand_device *spinand,
- const struct spinand_info *info)
+ const struct spinand_info *info,
+ enum spinand_bus_interface iface)
{
int i;
@@ -1431,7 +1432,7 @@ static int spinand_support_vendor_ops(struct spinand_device *spinand,
}
static const struct spi_mem_op *
-spinand_select_op_variant(struct spinand_device *spinand,
+spinand_select_op_variant(struct spinand_device *spinand, enum spinand_bus_interface iface,
const struct spinand_op_variants *variants)
{
struct nand_device *nand = spinand_to_nand(spinand);
@@ -1524,28 +1525,28 @@ int spinand_match_and_init(struct spinand_device *spinand,
spinand->read_retries = table[i].read_retries;
spinand->set_read_retry = table[i].set_read_retry;
- op = spinand_select_op_variant(spinand,
+ op = spinand_select_op_variant(spinand, SSDR,
info->op_variants.read_cache);
if (!op)
return -EOPNOTSUPP;
spinand->ssdr_op_templates.read_cache = op;
- op = spinand_select_op_variant(spinand,
+ op = spinand_select_op_variant(spinand, SSDR,
info->op_variants.write_cache);
if (!op)
return -EOPNOTSUPP;
spinand->ssdr_op_templates.write_cache = op;
- op = spinand_select_op_variant(spinand,
+ op = spinand_select_op_variant(spinand, SSDR,
info->op_variants.update_cache);
if (!op)
return -EOPNOTSUPP;
spinand->ssdr_op_templates.update_cache = op;
- ret = spinand_support_vendor_ops(spinand, info);
+ ret = spinand_support_vendor_ops(spinand, info, SSDR);
if (ret)
return ret;
--
2.51.1
______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/
^ permalink raw reply related [flat|nested] 32+ messages in thread* [PATCH v2 24/27] mtd: spinand: Give the bus interface to the configuration helper
2026-01-09 17:17 [PATCH v2 00/27] mtd: spinand: Octal DTR support Miquel Raynal
` (22 preceding siblings ...)
2026-01-09 17:18 ` [PATCH v2 23/27] mtd: spinand: Propagate the bus interface across core helpers Miquel Raynal
@ 2026-01-09 17:18 ` Miquel Raynal
2026-01-09 17:18 ` [PATCH v2 25/27] mtd: spinand: Warn if using SSDR-only vendor commands in a non SSDR mode Miquel Raynal
` (4 subsequent siblings)
28 siblings, 0 replies; 32+ messages in thread
From: Miquel Raynal @ 2026-01-09 17:18 UTC (permalink / raw)
To: Mark Brown, Richard Weinberger, Vignesh Raghavendra
Cc: Tudor Ambarus, Pratyush Yadav, Thomas Petazzoni, Steam Lin,
Santhosh Kumar K, linux-spi, linux-kernel, linux-mtd,
Miquel Raynal
The chip configuration hook is the one responsible to actually switch
the switch between bus interfaces. It is natural to give it the bus
interface we expect with a new parameter. For now the only value we can
give is SSDR, but this is subject to change in the future, so add a bit
of extra logic in the implementations of this callback to make sure
both the core and the chip driver are aligned on the request.
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
---
drivers/mtd/nand/spi/core.c | 2 +-
drivers/mtd/nand/spi/winbond.c | 28 +++++++++++++++++++++-------
include/linux/mtd/spinand.h | 6 ++++--
3 files changed, 26 insertions(+), 10 deletions(-)
diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c
index 019594182c60..1ac1d0181a91 100644
--- a/drivers/mtd/nand/spi/core.c
+++ b/drivers/mtd/nand/spi/core.c
@@ -1606,7 +1606,7 @@ static int spinand_configure_chip(struct spinand_device *spinand)
return ret;
if (spinand->configure_chip) {
- ret = spinand->configure_chip(spinand);
+ ret = spinand->configure_chip(spinand, SSDR);
if (ret)
return ret;
}
diff --git a/drivers/mtd/nand/spi/winbond.c b/drivers/mtd/nand/spi/winbond.c
index 1d79a8ae7920..419f4303a0dc 100644
--- a/drivers/mtd/nand/spi/winbond.c
+++ b/drivers/mtd/nand/spi/winbond.c
@@ -311,13 +311,17 @@ static int w25n02kv_ecc_get_status(struct spinand_device *spinand,
return -EINVAL;
}
-static int w25n0xjw_hs_cfg(struct spinand_device *spinand)
+static int w25n0xjw_hs_cfg(struct spinand_device *spinand,
+ enum spinand_bus_interface iface)
{
const struct spi_mem_op *op;
bool hs;
u8 sr4;
int ret;
+ if (iface != SSDR)
+ return -EOPNOTSUPP;
+
op = spinand->op_templates->read_cache;
if (op->cmd.dtr || op->addr.dtr || op->dummy.dtr || op->data.dtr)
hs = false;
@@ -371,17 +375,25 @@ static int w35n0xjw_write_vcr(struct spinand_device *spinand, u8 reg, u8 val)
return 0;
}
-static int w35n0xjw_vcr_cfg(struct spinand_device *spinand)
+static int w35n0xjw_vcr_cfg(struct spinand_device *spinand,
+ enum spinand_bus_interface iface)
{
- const struct spi_mem_op *op;
+ const struct spi_mem_op *ref_op;
unsigned int dummy_cycles;
bool dtr, single;
u8 io_mode;
int ret;
- op = spinand->op_templates->read_cache;
+ switch (iface) {
+ case SSDR:
+ ref_op = spinand->ssdr_op_templates.read_cache;
+ break;
+ default:
+ return -EOPNOTSUPP;
+ };
- dummy_cycles = ((op->dummy.nbytes * 8) / op->dummy.buswidth) / (op->dummy.dtr ? 2 : 1);
+ dummy_cycles = ((ref_op->dummy.nbytes * 8) / ref_op->dummy.buswidth) /
+ (ref_op->dummy.dtr ? 2 : 1);
switch (dummy_cycles) {
case 8:
case 12:
@@ -398,8 +410,10 @@ static int w35n0xjw_vcr_cfg(struct spinand_device *spinand)
if (ret)
return ret;
- single = (op->cmd.buswidth == 1 && op->addr.buswidth == 1 && op->data.buswidth == 1);
- dtr = (op->cmd.dtr && op->addr.dtr && op->data.dtr);
+ single = (ref_op->cmd.buswidth == 1 &&
+ ref_op->addr.buswidth == 1 &&
+ ref_op->data.buswidth == 1);
+ dtr = (ref_op->cmd.dtr && ref_op->addr.dtr && ref_op->data.dtr);
if (single && !dtr)
io_mode = W35N01JW_VCR_IO_MODE_SINGLE_SDR;
else if (!single && !dtr)
diff --git a/include/linux/mtd/spinand.h b/include/linux/mtd/spinand.h
index 027923841bba..b7eb3eceb138 100644
--- a/include/linux/mtd/spinand.h
+++ b/include/linux/mtd/spinand.h
@@ -529,7 +529,8 @@ struct spinand_info {
const struct spinand_op_variants *vendor_ops;
int (*select_target)(struct spinand_device *spinand,
unsigned int target);
- int (*configure_chip)(struct spinand_device *spinand);
+ int (*configure_chip)(struct spinand_device *spinand,
+ enum spinand_bus_interface iface);
int (*set_cont_read)(struct spinand_device *spinand,
bool enable);
struct spinand_fact_otp fact_otp;
@@ -704,7 +705,8 @@ struct spinand_device {
const struct spinand_manufacturer *manufacturer;
void *priv;
- int (*configure_chip)(struct spinand_device *spinand);
+ int (*configure_chip)(struct spinand_device *spinand,
+ enum spinand_bus_interface iface);
bool cont_read_possible;
int (*set_cont_read)(struct spinand_device *spinand,
bool enable);
--
2.51.1
______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/
^ permalink raw reply related [flat|nested] 32+ messages in thread* [PATCH v2 25/27] mtd: spinand: Warn if using SSDR-only vendor commands in a non SSDR mode
2026-01-09 17:17 [PATCH v2 00/27] mtd: spinand: Octal DTR support Miquel Raynal
` (23 preceding siblings ...)
2026-01-09 17:18 ` [PATCH v2 24/27] mtd: spinand: Give the bus interface to the configuration helper Miquel Raynal
@ 2026-01-09 17:18 ` Miquel Raynal
2026-01-09 17:18 ` [PATCH v2 26/27] mtd: spinand: Add octal DTR support Miquel Raynal
` (3 subsequent siblings)
28 siblings, 0 replies; 32+ messages in thread
From: Miquel Raynal @ 2026-01-09 17:18 UTC (permalink / raw)
To: Mark Brown, Richard Weinberger, Vignesh Raghavendra
Cc: Tudor Ambarus, Pratyush Yadav, Thomas Petazzoni, Steam Lin,
Santhosh Kumar K, linux-spi, linux-kernel, linux-mtd,
Miquel Raynal
Both Macronix and Winbond have chip specific operations which are SSDR
only. Trying to use them in an ODTR setup will fail and doing this is a
pure software bug. Warn explicitly in this case.
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
---
drivers/mtd/nand/spi/macronix.c | 2 ++
drivers/mtd/nand/spi/winbond.c | 2 ++
2 files changed, 4 insertions(+)
diff --git a/drivers/mtd/nand/spi/macronix.c b/drivers/mtd/nand/spi/macronix.c
index 6b7cbcc6e287..84be5e0402b5 100644
--- a/drivers/mtd/nand/spi/macronix.c
+++ b/drivers/mtd/nand/spi/macronix.c
@@ -53,6 +53,8 @@ static SPINAND_OP_VARIANTS(macronix_ops,
static struct spi_mem_op
spinand_fill_macronix_read_eccsr_op(struct spinand_device *spinand, void *valptr)
{
+ WARN_ON_ONCE(spinand->bus_iface != SSDR);
+
return (struct spi_mem_op)SPINAND_MACRONIX_READ_ECCSR_1S_0_1S(valptr);
}
diff --git a/drivers/mtd/nand/spi/winbond.c b/drivers/mtd/nand/spi/winbond.c
index 419f4303a0dc..90e4ece00cf5 100644
--- a/drivers/mtd/nand/spi/winbond.c
+++ b/drivers/mtd/nand/spi/winbond.c
@@ -114,6 +114,8 @@ static SPINAND_OP_VARIANTS(winbond_w25_ops,
static struct spi_mem_op
spinand_fill_winbond_select_target_op(struct spinand_device *spinand, void *valptr)
{
+ WARN_ON_ONCE(spinand->bus_iface != SSDR);
+
return (struct spi_mem_op)SPINAND_WINBOND_SELECT_TARGET_1S_0_1S(valptr);
}
--
2.51.1
______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/
^ permalink raw reply related [flat|nested] 32+ messages in thread* [PATCH v2 26/27] mtd: spinand: Add octal DTR support
2026-01-09 17:17 [PATCH v2 00/27] mtd: spinand: Octal DTR support Miquel Raynal
` (24 preceding siblings ...)
2026-01-09 17:18 ` [PATCH v2 25/27] mtd: spinand: Warn if using SSDR-only vendor commands in a non SSDR mode Miquel Raynal
@ 2026-01-09 17:18 ` Miquel Raynal
2026-01-09 17:18 ` [PATCH v2 27/27] mtd: spinand: winbond: W35N " Miquel Raynal
` (2 subsequent siblings)
28 siblings, 0 replies; 32+ messages in thread
From: Miquel Raynal @ 2026-01-09 17:18 UTC (permalink / raw)
To: Mark Brown, Richard Weinberger, Vignesh Raghavendra
Cc: Tudor Ambarus, Pratyush Yadav, Thomas Petazzoni, Steam Lin,
Santhosh Kumar K, linux-spi, linux-kernel, linux-mtd,
Miquel Raynal
Create a new bus interface named ODTR for "octal DTR", which matches the
following pattern: 8D-8D-8D.
Add octal DTR support for all the existing core operations. Add a second
set of templates for this bus interface.
Give the possibility for drivers to register their read, write and
update cache variants as well as their vendor specific operations.
Check the SPI controller driver supports all the octal DTR commands that
we might need before switching to the ODTR bus interface.
Make the switch by calling ->configure_chip() with the ODTR
parameter. Fallback in case this step fails.
If someone ever attempts to suspend a chip in octal DTR mode, there are
changes that it will loose its configuration at resume. Prevent any
problem by explicitly switching back to SSDR while suspending. Note:
there is a limitation in the current approach, page I/Os are not
available as the dirmaps will be created for the ODTR bus interface if
that option is supported and not switched back to SSDR during
suspend. Switching them is possible but would be costly and would not
bring anything as right after resuming we will switch again to ODTR. In
case this capability is used for debug, developpers should mind to
destroy and recreate suitable direct mappings.
Finally, as a side effect, we increase the buffer for reading IDs to
6. No device at this point returns 6 bytes, but we support 5 bytes IDs,
which means in octal DTR mode we have no other choice than reading an
even number of bytes, hence 6.
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
---
drivers/mtd/nand/spi/core.c | 140 +++++++++++++++++++++++++++++++++++++++++++-
include/linux/mtd/spinand.h | 79 ++++++++++++++++++++++++-
2 files changed, 216 insertions(+), 3 deletions(-)
diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c
index 1ac1d0181a91..e1336859c600 100644
--- a/drivers/mtd/nand/spi/core.c
+++ b/drivers/mtd/nand/spi/core.c
@@ -57,6 +57,9 @@ spinand_fill_set_feature_op(struct spinand_device *spinand, u64 reg, const void
{
struct spi_mem_op op = spinand->op_templates->set_feature;
+ if (op.cmd.dtr && op.cmd.buswidth == 8)
+ reg |= reg << 8;
+
op.addr.val = reg;
op.data.buf.out = valptr;
@@ -68,6 +71,9 @@ spinand_fill_get_feature_op(struct spinand_device *spinand, u64 reg, void *valpt
{
struct spi_mem_op op = spinand->op_templates->get_feature;
+ if (op.cmd.dtr && op.cmd.buswidth == 8)
+ reg |= reg << 8;
+
op.addr.val = reg;
op.data.buf.in = valptr;
@@ -1392,6 +1398,11 @@ 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)
+{
+ return op->cmd.dtr && op->cmd.buswidth == 8;
+}
+
static void spinand_init_ssdr_templates(struct spinand_device *spinand)
{
struct spinand_mem_ops *tmpl = &spinand->ssdr_op_templates;
@@ -1424,6 +1435,10 @@ static int spinand_support_vendor_ops(struct spinand_device *spinand,
for (i = 0; i < info->vendor_ops->nops; i++) {
const struct spi_mem_op *op = &info->vendor_ops->ops[i];
+ if ((iface == SSDR && spinand_op_is_odtr(op)) ||
+ (iface == ODTR && !spinand_op_is_odtr(op)))
+ continue;
+
if (!spi_mem_supports_op(spinand->spimem, op))
return -EOPNOTSUPP;
}
@@ -1431,6 +1446,49 @@ static int spinand_support_vendor_ops(struct spinand_device *spinand,
return 0;
}
+static int spinand_init_odtr_instruction_set(struct spinand_device *spinand)
+{
+ struct spinand_mem_ops *tmpl = &spinand->odtr_op_templates;
+
+ tmpl->reset = (struct spi_mem_op)SPINAND_RESET_8D_0_0_OP;
+ if (!spi_mem_supports_op(spinand->spimem, &tmpl->reset))
+ return -EOPNOTSUPP;
+
+ tmpl->readid = (struct spi_mem_op)SPINAND_READID_8D_8D_8D_OP(0, 0, NULL, 0);
+ if (!spi_mem_supports_op(spinand->spimem, &tmpl->readid))
+ return -EOPNOTSUPP;
+
+ tmpl->wr_en = (struct spi_mem_op)SPINAND_WR_EN_8D_0_0_OP;
+ if (!spi_mem_supports_op(spinand->spimem, &tmpl->wr_en))
+ return -EOPNOTSUPP;
+
+ tmpl->wr_dis = (struct spi_mem_op)SPINAND_WR_DIS_8D_0_0_OP;
+ if (!spi_mem_supports_op(spinand->spimem, &tmpl->wr_dis))
+ return -EOPNOTSUPP;
+
+ tmpl->set_feature = (struct spi_mem_op)SPINAND_SET_FEATURE_8D_8D_8D_OP(0, NULL);
+ if (!spi_mem_supports_op(spinand->spimem, &tmpl->set_feature))
+ return -EOPNOTSUPP;
+
+ tmpl->get_feature = (struct spi_mem_op)SPINAND_GET_FEATURE_8D_8D_8D_OP(0, NULL);
+ if (!spi_mem_supports_op(spinand->spimem, &tmpl->get_feature))
+ return -EOPNOTSUPP;
+
+ tmpl->blk_erase = (struct spi_mem_op)SPINAND_BLK_ERASE_8D_8D_0_OP(0);
+ if (!spi_mem_supports_op(spinand->spimem, &tmpl->blk_erase))
+ return -EOPNOTSUPP;
+
+ tmpl->page_read = (struct spi_mem_op)SPINAND_PAGE_READ_8D_8D_0_OP(0);
+ if (!spi_mem_supports_op(spinand->spimem, &tmpl->page_read))
+ return -EOPNOTSUPP;
+
+ tmpl->prog_exec = (struct spi_mem_op)SPINAND_PROG_EXEC_8D_8D_0_OP(0);
+ if (!spi_mem_supports_op(spinand->spimem, &tmpl->prog_exec))
+ return -EOPNOTSUPP;
+
+ return 0;
+}
+
static const struct spi_mem_op *
spinand_select_op_variant(struct spinand_device *spinand, enum spinand_bus_interface iface,
const struct spinand_op_variants *variants)
@@ -1446,6 +1504,10 @@ spinand_select_op_variant(struct spinand_device *spinand, enum spinand_bus_inter
unsigned int nbytes;
int ret;
+ if ((iface == SSDR && spinand_op_is_odtr(&op)) ||
+ (iface == ODTR && !spinand_op_is_odtr(&op)))
+ continue;
+
nbytes = nanddev_per_page_oobsize(nand) +
nanddev_page_size(nand);
@@ -1525,6 +1587,8 @@ int spinand_match_and_init(struct spinand_device *spinand,
spinand->read_retries = table[i].read_retries;
spinand->set_read_retry = table[i].set_read_retry;
+ /* I/O variants selection with single-spi SDR commands */
+
op = spinand_select_op_variant(spinand, SSDR,
info->op_variants.read_cache);
if (!op)
@@ -1550,6 +1614,28 @@ int spinand_match_and_init(struct spinand_device *spinand,
if (ret)
return ret;
+ /* I/O variants selection with octo-spi DDR commands (optional) */
+
+ ret = spinand_init_odtr_instruction_set(spinand);
+ if (ret)
+ return 0;
+
+ ret = spinand_support_vendor_ops(spinand, info, ODTR);
+ if (ret)
+ return 0;
+
+ op = spinand_select_op_variant(spinand, ODTR,
+ info->op_variants.read_cache);
+ spinand->odtr_op_templates.read_cache = op;
+
+ op = spinand_select_op_variant(spinand, ODTR,
+ info->op_variants.write_cache);
+ spinand->odtr_op_templates.write_cache = op;
+
+ op = spinand_select_op_variant(spinand, ODTR,
+ info->op_variants.update_cache);
+ spinand->odtr_op_templates.update_cache = op;
+
return 0;
}
@@ -1591,9 +1677,34 @@ static int spinand_detect(struct spinand_device *spinand)
static int spinand_configure_chip(struct spinand_device *spinand)
{
- bool quad_enable = false;
+ bool odtr = false, quad_enable = false;
int ret;
+ if (spinand->odtr_op_templates.read_cache &&
+ spinand->odtr_op_templates.write_cache &&
+ spinand->odtr_op_templates.update_cache)
+ odtr = true;
+
+ if (odtr) {
+ if (!spinand->configure_chip)
+ goto try_ssdr;
+
+ /* ODTR bus interface configuration happens here */
+ ret = spinand->configure_chip(spinand, ODTR);
+ if (ret) {
+ spinand->odtr_op_templates.read_cache = NULL;
+ spinand->odtr_op_templates.write_cache = NULL;
+ spinand->odtr_op_templates.update_cache = NULL;
+ goto try_ssdr;
+ }
+
+ spinand->op_templates = &spinand->odtr_op_templates;
+ spinand->bus_iface = ODTR;
+
+ return 0;
+ }
+
+try_ssdr:
if (spinand->flags & SPINAND_HAS_QE_BIT) {
if (spinand->ssdr_op_templates.read_cache->data.buswidth == 4 ||
spinand->ssdr_op_templates.write_cache->data.buswidth == 4 ||
@@ -1675,6 +1786,32 @@ static void spinand_mtd_resume(struct mtd_info *mtd)
spinand_ecc_enable(spinand, false);
}
+static int spinand_mtd_suspend(struct mtd_info *mtd)
+{
+ struct spinand_device *spinand = mtd_to_spinand(mtd);
+ int ret;
+
+ /*
+ * Return to SSDR interface in the suspend path to make sure the
+ * reset operation is correctly processed upon resume.
+ *
+ * Note: Once back in SSDR mode, every operation but the page helpers
+ * (dirmap based I/O accessors) will work. Page accesses would require
+ * destroying and recreating the dirmaps twice to work, which would be
+ * impacting for no reason, as this is just a transitional state.
+ */
+ if (spinand->bus_iface == ODTR) {
+ ret = spinand->configure_chip(spinand, SSDR);
+ if (ret)
+ return ret;
+
+ spinand->op_templates = &spinand->ssdr_op_templates;
+ spinand->bus_iface = SSDR;
+ }
+
+ return 0;
+}
+
static int spinand_init(struct spinand_device *spinand)
{
struct device *dev = &spinand->spimem->spi->dev;
@@ -1744,6 +1881,7 @@ static int spinand_init(struct spinand_device *spinand)
mtd->_block_isreserved = spinand_mtd_block_isreserved;
mtd->_erase = spinand_mtd_erase;
mtd->_max_bad_blocks = nanddev_mtd_max_bad_blocks;
+ mtd->_suspend = spinand_mtd_suspend;
mtd->_resume = spinand_mtd_resume;
if (spinand_user_otp_size(spinand) || spinand_fact_otp_size(spinand)) {
diff --git a/include/linux/mtd/spinand.h b/include/linux/mtd/spinand.h
index b7eb3eceb138..fc5111fc778f 100644
--- a/include/linux/mtd/spinand.h
+++ b/include/linux/mtd/spinand.h
@@ -238,6 +238,77 @@
SPI_MEM_OP_NO_DUMMY, \
SPI_MEM_OP_DATA_OUT(len, buf, 8))
+/**
+ * Octal DDR SPI NAND flash operations
+ */
+
+#define SPINAND_RESET_8D_0_0_OP \
+ SPI_MEM_OP(SPI_MEM_DTR_OP_RPT_CMD(0xff, 8), \
+ SPI_MEM_OP_NO_ADDR, \
+ SPI_MEM_OP_NO_DUMMY, \
+ SPI_MEM_OP_NO_DATA)
+
+#define SPINAND_READID_8D_8D_8D_OP(naddr, ndummy, buf, len) \
+ SPI_MEM_OP(SPI_MEM_DTR_OP_RPT_CMD(0x9f, 8), \
+ SPI_MEM_DTR_OP_ADDR(naddr, 0, 8), \
+ SPI_MEM_DTR_OP_DUMMY(ndummy, 8), \
+ SPI_MEM_DTR_OP_DATA_IN(len, buf, 8))
+
+#define SPINAND_WR_EN_8D_0_0_OP \
+ SPI_MEM_OP(SPI_MEM_DTR_OP_RPT_CMD(0x06, 8), \
+ SPI_MEM_OP_NO_ADDR, \
+ SPI_MEM_OP_NO_DUMMY, \
+ SPI_MEM_OP_NO_DATA)
+
+#define SPINAND_WR_DIS_8D_0_0_OP \
+ SPI_MEM_OP(SPI_MEM_DTR_OP_RPT_CMD(0x04, 8), \
+ SPI_MEM_OP_NO_ADDR, \
+ SPI_MEM_OP_NO_DUMMY, \
+ SPI_MEM_OP_NO_DATA)
+
+#define SPINAND_SET_FEATURE_8D_8D_8D_OP(reg, valptr) \
+ SPI_MEM_OP(SPI_MEM_DTR_OP_RPT_CMD(0x1f, 8), \
+ SPI_MEM_DTR_OP_RPT_ADDR(reg, 8), \
+ SPI_MEM_OP_NO_DUMMY, \
+ SPI_MEM_DTR_OP_DATA_OUT(2, valptr, 8))
+
+#define SPINAND_GET_FEATURE_8D_8D_8D_OP(reg, valptr) \
+ SPI_MEM_OP(SPI_MEM_DTR_OP_RPT_CMD(0x0f, 8), \
+ SPI_MEM_DTR_OP_RPT_ADDR(reg, 8), \
+ SPI_MEM_DTR_OP_DUMMY(14, 8), \
+ SPI_MEM_DTR_OP_DATA_IN(2, valptr, 8))
+
+#define SPINAND_BLK_ERASE_8D_8D_0_OP(addr) \
+ SPI_MEM_OP(SPI_MEM_DTR_OP_RPT_CMD(0xd8, 8), \
+ SPI_MEM_DTR_OP_ADDR(2, addr, 8), \
+ SPI_MEM_OP_NO_DUMMY, \
+ SPI_MEM_OP_NO_DATA)
+
+#define SPINAND_PAGE_READ_8D_8D_0_OP(addr) \
+ SPI_MEM_OP(SPI_MEM_DTR_OP_RPT_CMD(0x13, 8), \
+ SPI_MEM_DTR_OP_ADDR(2, addr, 8), \
+ SPI_MEM_OP_NO_DUMMY, \
+ SPI_MEM_OP_NO_DATA)
+
+#define SPINAND_PAGE_READ_FROM_CACHE_8D_8D_8D_OP(addr, ndummy, buf, len, freq) \
+ SPI_MEM_OP(SPI_MEM_DTR_OP_RPT_CMD(0x9d, 8), \
+ SPI_MEM_DTR_OP_ADDR(2, addr, 8), \
+ SPI_MEM_DTR_OP_DUMMY(ndummy, 8), \
+ SPI_MEM_DTR_OP_DATA_IN(len, buf, 8), \
+ SPI_MEM_OP_MAX_FREQ(freq))
+
+#define SPINAND_PROG_EXEC_8D_8D_0_OP(addr) \
+ SPI_MEM_OP(SPI_MEM_DTR_OP_RPT_CMD(0x10, 8), \
+ SPI_MEM_DTR_OP_ADDR(2, addr, 8), \
+ SPI_MEM_OP_NO_DUMMY, \
+ SPI_MEM_OP_NO_DATA)
+
+#define SPINAND_PROG_LOAD_8D_8D_8D_OP(reset, addr, buf, len) \
+ SPI_MEM_OP(SPI_MEM_DTR_OP_RPT_CMD((reset ? 0xc2 : 0xc4), 8), \
+ SPI_MEM_DTR_OP_ADDR(2, addr, 8), \
+ SPI_MEM_OP_NO_DUMMY, \
+ SPI_MEM_DTR_OP_DATA_OUT(len, buf, 8))
+
/* feature register */
#define REG_BLOCK_LOCK 0xa0
#define BL_ALL_UNLOCKED 0x00
@@ -261,7 +332,7 @@
struct spinand_op;
struct spinand_device;
-#define SPINAND_MAX_ID_LEN 5
+#define SPINAND_MAX_ID_LEN 6
/*
* For erase, write and read operation, we got the following timings :
* tBERS (erase) 1ms to 4ms
@@ -287,7 +358,7 @@ struct spinand_device;
/**
* struct spinand_id - SPI NAND id structure
- * @data: buffer containing the id bytes. Currently 5 bytes large, but can
+ * @data: buffer containing the id bytes. Currently 6 bytes large, but can
* be extended if required
* @len: ID length
*/
@@ -484,9 +555,11 @@ struct spinand_user_otp {
/**
* enum spinand_bus_interface - SPI NAND bus interface types
* @SSDR: Bus configuration supporting all 1S-XX-XX operations, including dual and quad
+ * @ODTR: Bus configuration supporting only 8D-8D-8D operations
*/
enum spinand_bus_interface {
SSDR,
+ ODTR,
};
/**
@@ -651,6 +724,7 @@ struct spinand_mem_ops {
* @id: NAND ID as returned by READ_ID
* @flags: NAND flags
* @ssdr_op_templates: Templates for all single SDR SPI mem operations
+ * @odtr_op_templates: Templates for all octal DTR SPI mem operations
* @op_templates: Templates for all SPI mem operations
* @bus_iface: Current bus interface
* @select_target: select a specific target/die. Usually called before sending
@@ -687,6 +761,7 @@ struct spinand_device {
u32 flags;
struct spinand_mem_ops ssdr_op_templates;
+ struct spinand_mem_ops odtr_op_templates;
struct spinand_mem_ops *op_templates;
enum spinand_bus_interface bus_iface;
--
2.51.1
______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/
^ permalink raw reply related [flat|nested] 32+ messages in thread* [PATCH v2 27/27] mtd: spinand: winbond: W35N octal DTR support
2026-01-09 17:17 [PATCH v2 00/27] mtd: spinand: Octal DTR support Miquel Raynal
` (25 preceding siblings ...)
2026-01-09 17:18 ` [PATCH v2 26/27] mtd: spinand: Add octal DTR support Miquel Raynal
@ 2026-01-09 17:18 ` Miquel Raynal
2026-01-13 17:29 ` (subset) [PATCH v2 00/27] mtd: spinand: Octal " Mark Brown
2026-01-14 11:25 ` Mark Brown
28 siblings, 0 replies; 32+ messages in thread
From: Miquel Raynal @ 2026-01-09 17:18 UTC (permalink / raw)
To: Mark Brown, Richard Weinberger, Vignesh Raghavendra
Cc: Tudor Ambarus, Pratyush Yadav, Thomas Petazzoni, Steam Lin,
Santhosh Kumar K, linux-spi, linux-kernel, linux-mtd,
Miquel Raynal
Extend the support for the W35N chip family by supporting the ODTR bus
interface. The chip is capable to run in this mode, which brings a
significant performance improvement.
1S-8S-8S:
# flash_speed /dev/mtd0 -c1 -d
eraseblock write speed is 7529 KiB/s
eraseblock read speed is 15058 KiB/s
8D-8D-8D:
# flash_speed /dev/mtd0 -c1 -d
eraseblock write speed is 9481 KiB/s
eraseblock read speed is 23272 KiB/s
This is +55% read speed and +26% write speed with the same hardware.
Tests have been conducted with a TI AM62A7 using the Cadence quad SPI
controller.
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
---
drivers/mtd/nand/spi/winbond.c | 20 ++++++++++++++++++--
1 file changed, 18 insertions(+), 2 deletions(-)
diff --git a/drivers/mtd/nand/spi/winbond.c b/drivers/mtd/nand/spi/winbond.c
index 90e4ece00cf5..8430ae307be0 100644
--- a/drivers/mtd/nand/spi/winbond.c
+++ b/drivers/mtd/nand/spi/winbond.c
@@ -36,6 +36,8 @@
*/
static SPINAND_OP_VARIANTS(read_cache_octal_variants,
+ SPINAND_PAGE_READ_FROM_CACHE_8D_8D_8D_OP(0, 24, NULL, 0, 120 * HZ_PER_MHZ),
+ SPINAND_PAGE_READ_FROM_CACHE_8D_8D_8D_OP(0, 16, NULL, 0, 86 * HZ_PER_MHZ),
SPINAND_PAGE_READ_FROM_CACHE_1S_1D_8D_OP(0, 3, NULL, 0, 120 * HZ_PER_MHZ),
SPINAND_PAGE_READ_FROM_CACHE_1S_1D_8D_OP(0, 2, NULL, 0, 105 * HZ_PER_MHZ),
SPINAND_PAGE_READ_FROM_CACHE_1S_8S_8S_OP(0, 20, NULL, 0, 0),
@@ -48,11 +50,13 @@ static SPINAND_OP_VARIANTS(read_cache_octal_variants,
SPINAND_PAGE_READ_FROM_CACHE_1S_1S_1S_OP(0, 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),
SPINAND_PROG_LOAD_1S_1S_8S_OP(0, NULL, 0),
SPINAND_PROG_LOAD_1S_1S_1S_OP(true, 0, NULL, 0));
static SPINAND_OP_VARIANTS(update_cache_octal_variants,
+ SPINAND_PROG_LOAD_8D_8D_8D_OP(false, 0, NULL, 0),
SPINAND_PROG_LOAD_1S_8S_8S_OP(false, 0, NULL, 0),
SPINAND_PROG_LOAD_1S_1S_1S_OP(false, 0, NULL, 0));
@@ -93,13 +97,22 @@ static SPINAND_OP_VARIANTS(update_cache_variants,
SPI_MEM_OP_NO_DUMMY, \
SPI_MEM_OP_DATA_OUT(1, buf, 1))
+#define SPINAND_WINBOND_WRITE_VCR_8D_8D_8D(reg, buf) \
+ SPI_MEM_OP(SPI_MEM_DTR_OP_RPT_CMD(0x81, 8), \
+ SPI_MEM_DTR_OP_ADDR(4, reg, 8), \
+ SPI_MEM_OP_NO_DUMMY, \
+ SPI_MEM_DTR_OP_DATA_OUT(2, buf, 8))
+
static SPINAND_OP_VARIANTS(winbond_w35_ops,
- SPINAND_WINBOND_WRITE_VCR_1S_1S_1S(0, NULL));
+ SPINAND_WINBOND_WRITE_VCR_1S_1S_1S(0, NULL),
+ SPINAND_WINBOND_WRITE_VCR_8D_8D_8D(0, NULL));
static struct spi_mem_op
spinand_fill_winbond_write_vcr_op(struct spinand_device *spinand, u8 reg, void *valptr)
{
- return (struct spi_mem_op)SPINAND_WINBOND_WRITE_VCR_1S_1S_1S(reg, valptr);
+ return (spinand->bus_iface == SSDR) ?
+ (struct spi_mem_op)SPINAND_WINBOND_WRITE_VCR_1S_1S_1S(reg, valptr) :
+ (struct spi_mem_op)SPINAND_WINBOND_WRITE_VCR_8D_8D_8D(reg, valptr);
}
#define SPINAND_WINBOND_SELECT_TARGET_1S_0_1S(buf) \
@@ -390,6 +403,9 @@ static int w35n0xjw_vcr_cfg(struct spinand_device *spinand,
case SSDR:
ref_op = spinand->ssdr_op_templates.read_cache;
break;
+ case ODTR:
+ ref_op = spinand->odtr_op_templates.read_cache;
+ break;
default:
return -EOPNOTSUPP;
};
--
2.51.1
______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/
^ permalink raw reply related [flat|nested] 32+ messages in thread* Re: (subset) [PATCH v2 00/27] mtd: spinand: Octal DTR support
2026-01-09 17:17 [PATCH v2 00/27] mtd: spinand: Octal DTR support Miquel Raynal
` (26 preceding siblings ...)
2026-01-09 17:18 ` [PATCH v2 27/27] mtd: spinand: winbond: W35N " Miquel Raynal
@ 2026-01-13 17:29 ` Mark Brown
2026-01-14 11:25 ` Mark Brown
28 siblings, 0 replies; 32+ messages in thread
From: Mark Brown @ 2026-01-13 17:29 UTC (permalink / raw)
To: Richard Weinberger, Vignesh Raghavendra, Miquel Raynal
Cc: Tudor Ambarus, Pratyush Yadav, Thomas Petazzoni, Steam Lin,
Santhosh Kumar K, linux-spi, linux-kernel, linux-mtd
On Fri, 09 Jan 2026 18:17:58 +0100, Miquel Raynal wrote:
> This series adds support for 8D-8D-8D in SPI NAND, which can already be
> leveraged without any SPI changes as controllers already have this
> support for some SPI NOR devices.
>
> The series is a bit long because many preparation patches were needed
> in order to have a clean 8D-8D-8D introduction (one of the last
> patches), but I believe the split is worth it.
>
> [...]
Applied to
https://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi.git for-next
Thanks!
[01/27] spi: spi-mem: Make the DTR command operation macro more suitable
commit: 0196932f539e306e122b6edf24c9f5e30d1f73ee
[02/27] spi: spi-mem: Create a repeated address operation
commit: af4b2dc4810380a469dcd7508923b70892c2996a
[03/27] spi: spi-mem: Limit octal DTR constraints to octal DTR situations
commit: 8618271887ca10ac5108fe7e1d82ba8f1b152cf9
All being well this means that it will be integrated into the linux-next
tree (usually sometime in the next 24 hours) and sent to Linus during
the next merge window (or sooner if it is a bug fix), however if
problems are discovered then the patch may be dropped or reverted.
You may get further e-mails resulting from automated or manual testing
and review of the tree, please engage with people reporting problems and
send followup patches addressing any issues that are reported if needed.
If any updates are required or you are submitting further changes they
should be sent as incremental updates against current git, existing
patches will not be replaced.
Please add any relevant lists and maintainers to the CCs when replying
to this mail.
Thanks,
Mark
______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/
^ permalink raw reply [flat|nested] 32+ messages in thread* Re: [PATCH v2 00/27] mtd: spinand: Octal DTR support
2026-01-09 17:17 [PATCH v2 00/27] mtd: spinand: Octal DTR support Miquel Raynal
` (27 preceding siblings ...)
2026-01-13 17:29 ` (subset) [PATCH v2 00/27] mtd: spinand: Octal " Mark Brown
@ 2026-01-14 11:25 ` Mark Brown
2026-01-14 16:17 ` Miquel Raynal
2026-01-29 19:23 ` Miquel Raynal
28 siblings, 2 replies; 32+ messages in thread
From: Mark Brown @ 2026-01-14 11:25 UTC (permalink / raw)
To: Miquel Raynal
Cc: Richard Weinberger, Vignesh Raghavendra, Tudor Ambarus,
Pratyush Yadav, Thomas Petazzoni, Steam Lin, Santhosh Kumar K,
linux-spi, linux-kernel, linux-mtd
[-- Attachment #1.1: Type: text/plain, Size: 1620 bytes --]
On Fri, Jan 09, 2026 at 06:17:58PM +0100, Miquel Raynal wrote:
> This series adds support for 8D-8D-8D in SPI NAND, which can already be
> leveraged without any SPI changes as controllers already have this
> support for some SPI NOR devices.
The following changes since commit 0f61b1860cc3f52aef9036d7235ed1f017632193:
Linux 6.19-rc5 (2026-01-11 17:03:14 -1000)
are available in the Git repository at:
https://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi.git tags/spi-octal-dtr
for you to fetch changes up to 8618271887ca10ac5108fe7e1d82ba8f1b152cf9:
spi: spi-mem: Limit octal DTR constraints to octal DTR situations (2026-01-12 12:40:30 +0000)
----------------------------------------------------------------
spi: Octal DTR support
This series adds support for 8D-8D-8D in SPI NAND, which can already be
leveraged without any SPI changes as controllers already have this
support for some SPI NOR devices.
Among the few spi-mem patches, they are needed for building the SPI NAND
changes (especially the ODTR introduction at the end) and therefore an
immutable tag will be needed for merging in the MTD tree (unless all the
series goes through MTD directly ofc).
----------------------------------------------------------------
Miquel Raynal (3):
spi: spi-mem: Make the DTR command operation macro more suitable
spi: spi-mem: Create a repeated address operation
spi: spi-mem: Limit octal DTR constraints to octal DTR situations
drivers/spi/spi-mem.c | 15 +++++++++++++--
include/linux/spi/spi-mem.h | 14 +++++++++++---
2 files changed, 24 insertions(+), 5 deletions(-)
[-- 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] 32+ messages in thread* Re: [PATCH v2 00/27] mtd: spinand: Octal DTR support
2026-01-14 11:25 ` Mark Brown
@ 2026-01-14 16:17 ` Miquel Raynal
2026-01-29 19:23 ` Miquel Raynal
1 sibling, 0 replies; 32+ messages in thread
From: Miquel Raynal @ 2026-01-14 16:17 UTC (permalink / raw)
To: Mark Brown
Cc: Richard Weinberger, Vignesh Raghavendra, Tudor Ambarus,
Pratyush Yadav, Thomas Petazzoni, Steam Lin, Santhosh Kumar K,
linux-spi, linux-kernel, linux-mtd
Hi Mark,
>> This series adds support for 8D-8D-8D in SPI NAND, which can already be
>> leveraged without any SPI changes as controllers already have this
>> support for some SPI NOR devices.
>
> The following changes since commit 0f61b1860cc3f52aef9036d7235ed1f017632193:
>
> Linux 6.19-rc5 (2026-01-11 17:03:14 -1000)
>
> are available in the Git repository at:
>
> https://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi.git tags/spi-octal-dtr
>
> for you to fetch changes up to 8618271887ca10ac5108fe7e1d82ba8f1b152cf9:
>
> spi: spi-mem: Limit octal DTR constraints to octal DTR situations (2026-01-12 12:40:30 +0000)
Thanks you very much! I will wait a bit more and eventually merge it in
mtd/next before applying the rest of the series.
Thanks,
Miquèl
______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH v2 00/27] mtd: spinand: Octal DTR support
2026-01-14 11:25 ` Mark Brown
2026-01-14 16:17 ` Miquel Raynal
@ 2026-01-29 19:23 ` Miquel Raynal
1 sibling, 0 replies; 32+ messages in thread
From: Miquel Raynal @ 2026-01-29 19:23 UTC (permalink / raw)
To: Mark Brown
Cc: Richard Weinberger, Vignesh Raghavendra, Tudor Ambarus,
Pratyush Yadav, Thomas Petazzoni, Steam Lin, Santhosh Kumar K,
linux-spi, linux-kernel, linux-mtd
Hello all,
> The following changes since commit 0f61b1860cc3f52aef9036d7235ed1f017632193:
>
> Linux 6.19-rc5 (2026-01-11 17:03:14 -1000)
>
> are available in the Git repository at:
>
> https://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi.git tags/spi-octal-dtr
>
> for you to fetch changes up to 8618271887ca10ac5108fe7e1d82ba8f1b152cf9:
>
> spi: spi-mem: Limit octal DTR constraints to octal DTR situations
> (2026-01-12 12:40:30 +0000)
Pulled in nand/next!
I then applied the rest of the series on top of it.
Thanks,
Miquèl
______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/
^ permalink raw reply [flat|nested] 32+ messages in thread