linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v2 00/14] mtd: spinand: Add Octal DTR SPI (8D-8D-8D) mode support
@ 2021-10-11 20:46 Apurva Nandan
  2021-10-11 20:46 ` [PATCH v2 01/14] spi: spi-mem: Add DTR templates for cmd, address, dummy and data phase Apurva Nandan
                   ` (13 more replies)
  0 siblings, 14 replies; 20+ messages in thread
From: Apurva Nandan @ 2021-10-11 20:46 UTC (permalink / raw)
  To: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
	Mark Brown, Apurva Nandan, Patrice Chotard, Christophe Kerello,
	Boris Brezillon, linux-mtd, linux-kernel, linux-spi
  Cc: p.yadav

Hi,
This series proposes patches for adding the following functionality
in SPI NAND core:

- Octal DTR SPI (8D-8D-8D) mode support

- Winbond W35N01JW SPI NAND chip support

- Power-on-Reset instruction support

This series has been tested on TI J721e EVM with the Winbond W35N01JW
flash with following test utilities:

- nandtest
  Test log: https://textbin.net/raw/fhypoz63f9

- mtd_stresstest
  Test log: https://textbin.net/raw/0xqjmqntj7

- UBIFS LTP stress test (NAND_XL_STRESS_DD_RW_UBIFS).
  Test log: https://textbin.net/raw/pyokws7wku

Datasheet: https://www.winbond.com/export/sites/winbond/datasheet/W35N01JW_Datasheet_Brief.pdf

---
Changes in v2:

- Removed *_ALL_ARGS() macros from spi-mem.h, and redefined DTR macros.

- Renamed spinand_setup_op() to spinand_patch_op(). Reduced one
  conditional check from this function. Had to keep tweaking in hot-path
  to avoid complicated implementation "hacks".

- Changes in commit messages and added comments.

- Dropped "Reject 8D-8D-8D op_templates if octal_dtr_enale() is
  missing in manufacturer_op" patch.

- Reduced PoR reset delay.

- Splitted "mtd: spinand: Add support for Winbond W35N01JW SPI NAND
  flash" into 3 independent patches.

Apurva Nandan (14):
  spi: spi-mem: Add DTR templates for cmd, address, dummy and data phase
  mtd: spinand: Add enum spinand_proto to indicate current SPI IO mode
  mtd: spinand: Patch spi_mem_op for the SPI IO protocol using reg_proto
  mtd: spinand: Fix odd byte addr and data phase in read and write reg
    op for Octal DTR mode
  mtd: spinand: Add adjust_op() in manufacturer_ops to modify the ops
    for manufacturer specific changes
  mtd: spinand: Add macros for Octal DTR page read and write operations
  mtd: spinand: Allow enabling Octal DTR mode in the core
  mtd: spinand: winbond: Add support for write volatile configuration
    register op
  mtd: spinand: winbond: Add octal_dtr_enable() for manufacturer_ops
  mtd: spinand: Add support for Power-on-Reset (PoR) instruction
  mtd: spinand: Perform Power-on-Reset on the flash in mtd_suspend()
  mtd: spinand: Add adjust_op() in Winbond manufacturer_ops
  mtd: spinand: winbond: Rename cache op_variants struct variable
  mtd: spinand: winbond: Add support for Winbond W35N01JW SPI NAND flash

 drivers/mtd/nand/spi/core.c    | 187 +++++++++++++++++++++++++++++-
 drivers/mtd/nand/spi/winbond.c | 200 +++++++++++++++++++++++++++++++--
 include/linux/mtd/spinand.h    |  67 +++++++++++
 include/linux/spi/spi-mem.h    |  41 +++++++
 4 files changed, 484 insertions(+), 11 deletions(-)

-- 
2.25.1


^ permalink raw reply	[flat|nested] 20+ messages in thread

* [PATCH v2 01/14] spi: spi-mem: Add DTR templates for cmd, address, dummy and data phase
  2021-10-11 20:46 [PATCH v2 00/14] mtd: spinand: Add Octal DTR SPI (8D-8D-8D) mode support Apurva Nandan
@ 2021-10-11 20:46 ` Apurva Nandan
  2021-10-11 20:46 ` [PATCH v2 02/14] mtd: spinand: Add enum spinand_proto to indicate current SPI IO mode Apurva Nandan
                   ` (12 subsequent siblings)
  13 siblings, 0 replies; 20+ messages in thread
From: Apurva Nandan @ 2021-10-11 20:46 UTC (permalink / raw)
  To: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
	Mark Brown, Apurva Nandan, Patrice Chotard, Christophe Kerello,
	Boris Brezillon, linux-mtd, linux-kernel, linux-spi
  Cc: p.yadav

Setting dtr field of spi_mem_op is useful when creating templates
for DTR ops in spinand.h. Also, 2 bytes cmd phases are required when
operating in Octal DTR SPI mode.

Create new templates for dtr mode cmd, address, dummy and data phase
in spi_mem_op, which set the dtr field to 1 and also allow passing
the nbytes for the cmd phase.

Signed-off-by: Apurva Nandan <a-nandan@ti.com>
---
 include/linux/spi/spi-mem.h | 41 +++++++++++++++++++++++++++++++++++++
 1 file changed, 41 insertions(+)

diff --git a/include/linux/spi/spi-mem.h b/include/linux/spi/spi-mem.h
index 85e2ff7b840d..4a99e26aa0b6 100644
--- a/include/linux/spi/spi-mem.h
+++ b/include/linux/spi/spi-mem.h
@@ -20,6 +20,14 @@
 		.nbytes = 1,					\
 	}
 
+#define SPI_MEM_OP_CMD_DTR(__nbytes, __opcode, __buswidth)	\
+	{							\
+		.buswidth = __buswidth,				\
+		.opcode = __opcode,				\
+		.nbytes = __nbytes,				\
+		.dtr = 1,					\
+	}
+
 #define SPI_MEM_OP_ADDR(__nbytes, __val, __buswidth)		\
 	{							\
 		.nbytes = __nbytes,				\
@@ -27,6 +35,14 @@
 		.buswidth = __buswidth,				\
 	}
 
+#define SPI_MEM_OP_ADDR_DTR(__nbytes, __val, __buswidth)	\
+	{							\
+		.nbytes = __nbytes,				\
+		.val = __val,					\
+		.buswidth = __buswidth,				\
+		.dtr = 1,					\
+	}
+
 #define SPI_MEM_OP_NO_ADDR	{ }
 
 #define SPI_MEM_OP_DUMMY(__nbytes, __buswidth)			\
@@ -35,6 +51,13 @@
 		.buswidth = __buswidth,				\
 	}
 
+#define SPI_MEM_OP_DUMMY_DTR(__nbytes, __buswidth)		\
+	{							\
+		.nbytes = __nbytes,				\
+		.buswidth = __buswidth,				\
+		.dtr = 1,					\
+	}
+
 #define SPI_MEM_OP_NO_DUMMY	{ }
 
 #define SPI_MEM_OP_DATA_IN(__nbytes, __buf, __buswidth)		\
@@ -45,6 +68,15 @@
 		.buswidth = __buswidth,				\
 	}
 
+#define SPI_MEM_OP_DATA_IN_DTR(__nbytes, __buf, __buswidth)	\
+	{							\
+		.dir = SPI_MEM_DATA_IN,				\
+		.nbytes = __nbytes,				\
+		.buf.in = __buf,				\
+		.buswidth = __buswidth,				\
+		.dtr = 1,					\
+	}
+
 #define SPI_MEM_OP_DATA_OUT(__nbytes, __buf, __buswidth)	\
 	{							\
 		.dir = SPI_MEM_DATA_OUT,			\
@@ -53,6 +85,15 @@
 		.buswidth = __buswidth,				\
 	}
 
+#define SPI_MEM_OP_DATA_OUT_DTR(__nbytes, __buf, __buswidth)	\
+	{							\
+		.dir = SPI_MEM_DATA_OUT,			\
+		.nbytes = __nbytes,				\
+		.buf.out = __buf,				\
+		.buswidth = __buswidth,				\
+		.dtr = 1,					\
+	}
+
 #define SPI_MEM_OP_NO_DATA	{ }
 
 /**
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH v2 02/14] mtd: spinand: Add enum spinand_proto to indicate current SPI IO mode
  2021-10-11 20:46 [PATCH v2 00/14] mtd: spinand: Add Octal DTR SPI (8D-8D-8D) mode support Apurva Nandan
  2021-10-11 20:46 ` [PATCH v2 01/14] spi: spi-mem: Add DTR templates for cmd, address, dummy and data phase Apurva Nandan
@ 2021-10-11 20:46 ` Apurva Nandan
  2021-10-12  6:39   ` Boris Brezillon
  2021-10-11 20:46 ` [PATCH v2 03/14] mtd: spinand: Patch spi_mem_op for the SPI IO protocol using reg_proto Apurva Nandan
                   ` (11 subsequent siblings)
  13 siblings, 1 reply; 20+ messages in thread
From: Apurva Nandan @ 2021-10-11 20:46 UTC (permalink / raw)
  To: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
	Mark Brown, Apurva Nandan, Patrice Chotard, Christophe Kerello,
	Boris Brezillon, linux-mtd, linux-kernel, linux-spi
  Cc: p.yadav

Unlike Dual and Quad SPI modes flashes, Octal DTR SPI NAND flashes
require all instructions to be made in 8D-8D-8D protocol when the
flash is in Octal DTR mode. Hence, storing the current SPI IO mode
becomes necessary for correctly generating non-array access operations.

Store the current SPI IO mode in the spinand struct using a reg_proto
enum. This would act as a flag, denoting that the core should use
the given SPI protocol for non-page access operations.

Also provide basic macros for extracting buswidth and dtr mode
information from the spinand_proto enum.

Signed-off-by: Apurva Nandan <a-nandan@ti.com>
---
 drivers/mtd/nand/spi/core.c |  2 ++
 include/linux/mtd/spinand.h | 30 ++++++++++++++++++++++++++++++
 2 files changed, 32 insertions(+)

diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c
index 2c8685f1f2fa..d82a3e6d9bb5 100644
--- a/drivers/mtd/nand/spi/core.c
+++ b/drivers/mtd/nand/spi/core.c
@@ -1155,6 +1155,7 @@ static void spinand_mtd_resume(struct mtd_info *mtd)
 	struct spinand_device *spinand = mtd_to_spinand(mtd);
 	int ret;
 
+	spinand->reg_proto = SPINAND_SINGLE_STR;
 	ret = spinand_reset_op(spinand);
 	if (ret)
 		return;
@@ -1181,6 +1182,7 @@ static int spinand_init(struct spinand_device *spinand)
 	if (!spinand->scratchbuf)
 		return -ENOMEM;
 
+	spinand->reg_proto = SPINAND_SINGLE_STR;
 	ret = spinand_detect(spinand);
 	if (ret)
 		goto err_free_bufs;
diff --git a/include/linux/mtd/spinand.h b/include/linux/mtd/spinand.h
index 6988956b8492..f6093cd98d7b 100644
--- a/include/linux/mtd/spinand.h
+++ b/include/linux/mtd/spinand.h
@@ -140,6 +140,31 @@
 		   SPI_MEM_OP_NO_DUMMY,					\
 		   SPI_MEM_OP_DATA_OUT(len, buf, 4))
 
+#define SPINAND_PROTO_BUSWIDTH_MASK	GENMASK(6, 0)
+#define SPINAND_PROTO_DTR_BIT		BIT(7)
+
+#define SPINAND_PROTO_STR(__buswidth)	\
+	((u8)(((__buswidth) - 1) & SPINAND_PROTO_BUSWIDTH_MASK))
+#define SPINAND_PROTO_DTR(__buswidth)	\
+	(SPINAND_PROTO_DTR_BIT | SPINAND_PROTO_STR(__buswidth))
+
+#define SPINAND_PROTO_BUSWIDTH(__proto)	\
+	((u8)(((__proto) & SPINAND_PROTO_BUSWIDTH_MASK) + 1))
+#define SPINAND_PROTO_IS_DTR(__proto)	(!!((__proto) & SPINAND_PROTO_DTR_BIT))
+
+/**
+ * enum spinand_proto - List allowable SPI protocol variants for read reg,
+ *			write reg, blk erase, write enable/disable, page read
+ *			and program exec operations.
+ */
+enum spinand_proto {
+	SPINAND_SINGLE_STR = SPINAND_PROTO_STR(1),
+	SPINAND_DUAL_STR = SPINAND_PROTO_STR(2),
+	SPINAND_QUAD_STR = SPINAND_PROTO_STR(4),
+	SPINAND_OCTAL_STR = SPINAND_PROTO_STR(8),
+	SPINAND_OCTAL_DTR = SPINAND_PROTO_DTR(8),
+};
+
 /**
  * Standard SPI NAND flash commands
  */
@@ -407,6 +432,9 @@ struct spinand_dirmap {
  *		   this die. Only required if your chip exposes several dies
  * @cur_target: currently selected target/die
  * @eccinfo: on-die ECC information
+ * @reg_proto: select a variant of SPI IO protocol (single, quad, octal or
+ *	       octal DTR) for read_reg/write_reg/erase operations. Update on
+ *	       successful transition into a different SPI IO protocol.
  * @cfg_cache: config register cache. One entry per die
  * @databuf: bounce buffer for data
  * @oobbuf: bounce buffer for OOB data
@@ -438,6 +466,8 @@ struct spinand_device {
 
 	struct spinand_ecc_info eccinfo;
 
+	enum spinand_proto reg_proto;
+
 	u8 *cfg_cache;
 	u8 *databuf;
 	u8 *oobbuf;
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH v2 03/14] mtd: spinand: Patch spi_mem_op for the SPI IO protocol using reg_proto
  2021-10-11 20:46 [PATCH v2 00/14] mtd: spinand: Add Octal DTR SPI (8D-8D-8D) mode support Apurva Nandan
  2021-10-11 20:46 ` [PATCH v2 01/14] spi: spi-mem: Add DTR templates for cmd, address, dummy and data phase Apurva Nandan
  2021-10-11 20:46 ` [PATCH v2 02/14] mtd: spinand: Add enum spinand_proto to indicate current SPI IO mode Apurva Nandan
@ 2021-10-11 20:46 ` Apurva Nandan
  2021-10-12  6:40   ` Boris Brezillon
  2021-10-11 20:46 ` [PATCH v2 04/14] mtd: spinand: Fix odd byte addr and data phase in read and write reg op for Octal DTR mode Apurva Nandan
                   ` (10 subsequent siblings)
  13 siblings, 1 reply; 20+ messages in thread
From: Apurva Nandan @ 2021-10-11 20:46 UTC (permalink / raw)
  To: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
	Mark Brown, Apurva Nandan, Patrice Chotard, Christophe Kerello,
	Boris Brezillon, linux-mtd, linux-kernel, linux-spi
  Cc: p.yadav

Currently, the op macros in spinand.h don't give the option to setup
any non-array access instructions for Dual/Quad/Octal DTR SPI bus.
Having a function that patches the op based on reg_proto would be
better than trying to write all the setup logic in op macros.

Create a spimem_patch_op() that would patch cmd, addr, dummy and data
phase of the spi_mem op, for the given spinand->reg_proto. And hence,
call the spimem_patch_op() before executing any spi_mem op.

Note: In this commit, spimem_patch_op() isn't called in the
read_reg_op(), write_reg_op() and wait() functions, as they need
modifications in address value and data nbytes when in Octal DTR mode.
This will be fixed in a later commit.

Signed-off-by: Apurva Nandan <a-nandan@ti.com>
---
 drivers/mtd/nand/spi/core.c | 49 +++++++++++++++++++++++++++++++++++++
 1 file changed, 49 insertions(+)

diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c
index d82a3e6d9bb5..11746d858f87 100644
--- a/drivers/mtd/nand/spi/core.c
+++ b/drivers/mtd/nand/spi/core.c
@@ -20,6 +20,49 @@
 #include <linux/spi/spi.h>
 #include <linux/spi/spi-mem.h>
 
+/**
+ * spinand_patch_op() - Helper function to patch the spi_mem op based on the
+ *			spinand->reg_proto
+ * @spinand: the spinand device
+ * @op: the spi_mem op to patch
+ *
+ * Set up buswidth and dtr fields for cmd, addr, dummy and data phase. Also
+ * adjust cmd opcode and dummy nbytes. This function doesn't make any changes
+ * to addr val or data buf.
+ */
+static void spinand_patch_op(const struct spinand_device *spinand,
+			     struct spi_mem_op *op)
+{
+	u8 op_buswidth = SPINAND_PROTO_BUSWIDTH(spinand->reg_proto);
+	u8 op_is_dtr = SPINAND_PROTO_IS_DTR(spinand->reg_proto);
+
+	if (spinand->reg_proto == SPINAND_SINGLE_STR)
+		return;
+
+	op->cmd.buswidth = op_buswidth;
+	op->cmd.dtr = op_is_dtr;
+	if (spinand->reg_proto == SPINAND_OCTAL_DTR) {
+		op->cmd.opcode = (op->cmd.opcode << 8) | op->cmd.opcode;
+		op->cmd.nbytes = 2;
+	}
+
+	if (op->addr.nbytes) {
+		op->addr.buswidth = op_buswidth;
+		op->addr.dtr = op_is_dtr;
+	}
+
+	if (op->dummy.nbytes) {
+		op->dummy.buswidth = op_buswidth;
+		op->dummy.dtr = op_is_dtr;
+		op->dummy.nbytes <<= op_is_dtr;
+	}
+
+	if (op->data.nbytes) {
+		op->data.buswidth = op_buswidth;
+		op->data.dtr = op_is_dtr;
+	}
+}
+
 static int spinand_read_reg_op(struct spinand_device *spinand, u8 reg, u8 *val)
 {
 	struct spi_mem_op op = SPINAND_GET_FEATURE_OP(reg,
@@ -343,6 +386,7 @@ static int spinand_write_enable_op(struct spinand_device *spinand)
 {
 	struct spi_mem_op op = SPINAND_WR_EN_DIS_OP(true);
 
+	spinand_patch_op(spinand, &op);
 	return spi_mem_exec_op(spinand->spimem, &op);
 }
 
@@ -353,6 +397,7 @@ static int spinand_load_page_op(struct spinand_device *spinand,
 	unsigned int row = nanddev_pos_to_row(nand, &req->pos);
 	struct spi_mem_op op = SPINAND_PAGE_READ_OP(row);
 
+	spinand_patch_op(spinand, &op);
 	return spi_mem_exec_op(spinand->spimem, &op);
 }
 
@@ -477,6 +522,7 @@ static int spinand_program_op(struct spinand_device *spinand,
 	unsigned int row = nanddev_pos_to_row(nand, &req->pos);
 	struct spi_mem_op op = SPINAND_PROG_EXEC_OP(row);
 
+	spinand_patch_op(spinand, &op);
 	return spi_mem_exec_op(spinand->spimem, &op);
 }
 
@@ -487,6 +533,7 @@ static int spinand_erase_op(struct spinand_device *spinand,
 	unsigned int row = nanddev_pos_to_row(nand, pos);
 	struct spi_mem_op op = SPINAND_BLK_ERASE_OP(row);
 
+	spinand_patch_op(spinand, &op);
 	return spi_mem_exec_op(spinand->spimem, &op);
 }
 
@@ -533,6 +580,7 @@ static int spinand_read_id_op(struct spinand_device *spinand, u8 naddr,
 		naddr, ndummy, spinand->scratchbuf, SPINAND_MAX_ID_LEN);
 	int ret;
 
+	spinand_patch_op(spinand, &op);
 	ret = spi_mem_exec_op(spinand->spimem, &op);
 	if (!ret)
 		memcpy(buf, spinand->scratchbuf, SPINAND_MAX_ID_LEN);
@@ -545,6 +593,7 @@ static int spinand_reset_op(struct spinand_device *spinand)
 	struct spi_mem_op op = SPINAND_RESET_OP;
 	int ret;
 
+	spinand_patch_op(spinand, &op);
 	ret = spi_mem_exec_op(spinand->spimem, &op);
 	if (ret)
 		return ret;
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH v2 04/14] mtd: spinand: Fix odd byte addr and data phase in read and write reg op for Octal DTR mode
  2021-10-11 20:46 [PATCH v2 00/14] mtd: spinand: Add Octal DTR SPI (8D-8D-8D) mode support Apurva Nandan
                   ` (2 preceding siblings ...)
  2021-10-11 20:46 ` [PATCH v2 03/14] mtd: spinand: Patch spi_mem_op for the SPI IO protocol using reg_proto Apurva Nandan
@ 2021-10-11 20:46 ` Apurva Nandan
  2021-10-11 20:46 ` [PATCH v2 05/14] mtd: spinand: Add adjust_op() in manufacturer_ops to modify the ops for manufacturer specific changes Apurva Nandan
                   ` (9 subsequent siblings)
  13 siblings, 0 replies; 20+ messages in thread
From: Apurva Nandan @ 2021-10-11 20:46 UTC (permalink / raw)
  To: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
	Mark Brown, Apurva Nandan, Patrice Chotard, Christophe Kerello,
	Boris Brezillon, linux-mtd, linux-kernel, linux-spi
  Cc: p.yadav

In Octal DTR SPI mode, 2 bytes of data gets transmitted over one clock
cycle, and half-cycle instruction phases aren't supported yet. So,
every DTR spi_mem_op needs to have even nbytes in all phases for
non-erratic behaviour from the SPI controller.

The odd length cmd and dummy phases get handled by spimem_patch_op()
but the odd length address and data phases need to be handled according
to the use case. For example in Octal DTR mode, read register operation
has one byte long address and data phase. So it needs to extend it
by adding a suitable extra byte in addr and reading 2 bytes of data,
discarding the second byte.

Handle address and data phases for Octal DTR mode in read and write
register operations by adding a suitable extra byte in the address and
data phase.

Create spimem_patch_reg_op() helper function to ease setting up
read/write register operations in other functions, e.g. wait().

Signed-off-by: Apurva Nandan <a-nandan@ti.com>
---
 drivers/mtd/nand/spi/core.c | 21 ++++++++++++++++++++-
 1 file changed, 20 insertions(+), 1 deletion(-)

diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c
index 11746d858f87..4da794ae728d 100644
--- a/drivers/mtd/nand/spi/core.c
+++ b/drivers/mtd/nand/spi/core.c
@@ -63,12 +63,29 @@ static void spinand_patch_op(const struct spinand_device *spinand,
 	}
 }
 
+static void spinand_patch_reg_op(const struct spinand_device *spinand,
+				 struct spi_mem_op *op)
+{
+	if (spinand->reg_proto == SPINAND_OCTAL_DTR) {
+		/*
+		 * Assigning same first and second byte will result in constant
+		 * bits on the SPI bus between positive and negative clock edges
+		 */
+		op->addr.val = (op->addr.val << 8) | op->addr.val;
+		op->addr.nbytes = 2;
+		op->data.nbytes = 2;
+	}
+
+	spinand_patch_op(spinand, op);
+}
+
 static int spinand_read_reg_op(struct spinand_device *spinand, u8 reg, u8 *val)
 {
 	struct spi_mem_op op = SPINAND_GET_FEATURE_OP(reg,
 						      spinand->scratchbuf);
 	int ret;
 
+	spinand_patch_reg_op(spinand, &op);
 	ret = spi_mem_exec_op(spinand->spimem, &op);
 	if (ret)
 		return ret;
@@ -82,7 +99,8 @@ static int spinand_write_reg_op(struct spinand_device *spinand, u8 reg, u8 val)
 	struct spi_mem_op op = SPINAND_SET_FEATURE_OP(reg,
 						      spinand->scratchbuf);
 
-	*spinand->scratchbuf = val;
+	spinand_patch_reg_op(spinand, &op);
+	memset(spinand->scratchbuf, val, op.data.nbytes);
 	return spi_mem_exec_op(spinand->spimem, &op);
 }
 
@@ -547,6 +565,7 @@ static int spinand_wait(struct spinand_device *spinand,
 	u8 status;
 	int ret;
 
+	spinand_patch_reg_op(spinand, &op);
 	ret = spi_mem_poll_status(spinand->spimem, &op, STATUS_BUSY, 0,
 				  initial_delay_us,
 				  poll_delay_us,
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH v2 05/14] mtd: spinand: Add adjust_op() in manufacturer_ops to modify the ops for manufacturer specific changes
  2021-10-11 20:46 [PATCH v2 00/14] mtd: spinand: Add Octal DTR SPI (8D-8D-8D) mode support Apurva Nandan
                   ` (3 preceding siblings ...)
  2021-10-11 20:46 ` [PATCH v2 04/14] mtd: spinand: Fix odd byte addr and data phase in read and write reg op for Octal DTR mode Apurva Nandan
@ 2021-10-11 20:46 ` Apurva Nandan
  2021-10-12  6:54   ` Boris Brezillon
  2021-10-11 20:46 ` [PATCH v2 06/14] mtd: spinand: Add macros for Octal DTR page read and write operations Apurva Nandan
                   ` (8 subsequent siblings)
  13 siblings, 1 reply; 20+ messages in thread
From: Apurva Nandan @ 2021-10-11 20:46 UTC (permalink / raw)
  To: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
	Mark Brown, Apurva Nandan, Patrice Chotard, Christophe Kerello,
	Boris Brezillon, linux-mtd, linux-kernel, linux-spi
  Cc: p.yadav

Manufacturers might use a variation of standard SPI NAND flash
instructions, e.g. Winbond W35N01JW changes the dummy cycle length for
read register commands when in Octal DTR mode.

Add new function in manufacturer_ops: adjust_op(), which can be called
to correct the spi_mem op for any alteration in the instruction made by
the manufacturers. And hence, this function can also be used for
incorporating variations of SPI instructions in Octal DTR mode.

Datasheet: https://www.winbond.com/export/sites/winbond/datasheet/W35N01JW_Datasheet_Brief.pdf

Signed-off-by: Apurva Nandan <a-nandan@ti.com>
---
 drivers/mtd/nand/spi/core.c | 3 +++
 include/linux/mtd/spinand.h | 4 ++++
 2 files changed, 7 insertions(+)

diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c
index 4da794ae728d..8e6cf7941a0f 100644
--- a/drivers/mtd/nand/spi/core.c
+++ b/drivers/mtd/nand/spi/core.c
@@ -61,6 +61,9 @@ static void spinand_patch_op(const struct spinand_device *spinand,
 		op->data.buswidth = op_buswidth;
 		op->data.dtr = op_is_dtr;
 	}
+
+	if (spinand->manufacturer->ops->adjust_op)
+		spinand->manufacturer->ops->adjust_op(op, spinand->reg_proto);
 }
 
 static void spinand_patch_reg_op(const struct spinand_device *spinand,
diff --git a/include/linux/mtd/spinand.h b/include/linux/mtd/spinand.h
index f6093cd98d7b..ebb19b2cec84 100644
--- a/include/linux/mtd/spinand.h
+++ b/include/linux/mtd/spinand.h
@@ -257,6 +257,8 @@ struct spinand_devid {
 /**
  * struct manufacurer_ops - SPI NAND manufacturer specific operations
  * @init: initialize a SPI NAND device
+ * @adjust_op: modify the ops for any variation in their cmd, address, dummy or
+ *	       data phase by the manufacturer
  * @cleanup: cleanup a SPI NAND device
  *
  * Each SPI NAND manufacturer driver should implement this interface so that
@@ -264,6 +266,8 @@ struct spinand_devid {
  */
 struct spinand_manufacturer_ops {
 	int (*init)(struct spinand_device *spinand);
+	void (*adjust_op)(struct spi_mem_op *op,
+			  const enum spinand_proto reg_proto);
 	void (*cleanup)(struct spinand_device *spinand);
 };
 
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH v2 06/14] mtd: spinand: Add macros for Octal DTR page read and write operations
  2021-10-11 20:46 [PATCH v2 00/14] mtd: spinand: Add Octal DTR SPI (8D-8D-8D) mode support Apurva Nandan
                   ` (4 preceding siblings ...)
  2021-10-11 20:46 ` [PATCH v2 05/14] mtd: spinand: Add adjust_op() in manufacturer_ops to modify the ops for manufacturer specific changes Apurva Nandan
@ 2021-10-11 20:46 ` Apurva Nandan
  2021-10-11 20:46 ` [PATCH v2 07/14] mtd: spinand: Allow enabling Octal DTR mode in the core Apurva Nandan
                   ` (7 subsequent siblings)
  13 siblings, 0 replies; 20+ messages in thread
From: Apurva Nandan @ 2021-10-11 20:46 UTC (permalink / raw)
  To: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
	Mark Brown, Apurva Nandan, Patrice Chotard, Christophe Kerello,
	Boris Brezillon, linux-mtd, linux-kernel, linux-spi
  Cc: p.yadav

Define new PAGE_READ_FROM_CACHE and PROG_LOAD op templates for Octal
DTR SPI mode. These templates will be used in op_variants and
op_templates for defining Octal DTR read from cache and write to
cache operations.

Datasheet: https://www.winbond.com/export/sites/winbond/datasheet/W35N01JW_Datasheet_Brief.pdf

Signed-off-by: Apurva Nandan <a-nandan@ti.com>
---
 include/linux/mtd/spinand.h | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/include/linux/mtd/spinand.h b/include/linux/mtd/spinand.h
index ebb19b2cec84..35816b8cfe81 100644
--- a/include/linux/mtd/spinand.h
+++ b/include/linux/mtd/spinand.h
@@ -122,6 +122,12 @@
 		   SPI_MEM_OP_DUMMY(ndummy, 4),				\
 		   SPI_MEM_OP_DATA_IN(len, buf, 4))
 
+#define SPINAND_PAGE_READ_FROM_CACHE_OCTALIO_DTR_OP(addr, ndummy, buf, len) \
+	SPI_MEM_OP(SPI_MEM_OP_CMD_DTR(2, 0x9d9d, 8),			\
+		   SPI_MEM_OP_ADDR_DTR(2, addr, 8),			\
+		   SPI_MEM_OP_DUMMY_DTR(ndummy, 8),			\
+		   SPI_MEM_OP_DATA_IN_DTR(len, buf, 8))
+
 #define SPINAND_PROG_EXEC_OP(addr)					\
 	SPI_MEM_OP(SPI_MEM_OP_CMD(0x10, 1),				\
 		   SPI_MEM_OP_ADDR(3, addr, 1),				\
@@ -140,6 +146,12 @@
 		   SPI_MEM_OP_NO_DUMMY,					\
 		   SPI_MEM_OP_DATA_OUT(len, buf, 4))
 
+#define SPINAND_PROG_LOAD_OCTALIO_DTR(reset, addr, buf, len)		\
+	SPI_MEM_OP(SPI_MEM_OP_CMD_DTR(2, reset ? 0x0202 : 0x8484, 8),	\
+		   SPI_MEM_OP_ADDR_DTR(2, addr, 8),			\
+		   SPI_MEM_OP_NO_DUMMY,					\
+		   SPI_MEM_OP_DATA_OUT_DTR(len, buf, 8))
+
 #define SPINAND_PROTO_BUSWIDTH_MASK	GENMASK(6, 0)
 #define SPINAND_PROTO_DTR_BIT		BIT(7)
 
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH v2 07/14] mtd: spinand: Allow enabling Octal DTR mode in the core
  2021-10-11 20:46 [PATCH v2 00/14] mtd: spinand: Add Octal DTR SPI (8D-8D-8D) mode support Apurva Nandan
                   ` (5 preceding siblings ...)
  2021-10-11 20:46 ` [PATCH v2 06/14] mtd: spinand: Add macros for Octal DTR page read and write operations Apurva Nandan
@ 2021-10-11 20:46 ` Apurva Nandan
  2021-10-12  7:14   ` Boris Brezillon
  2021-10-11 20:46 ` [PATCH v2 08/14] mtd: spinand: winbond: Add support for write volatile configuration register op Apurva Nandan
                   ` (6 subsequent siblings)
  13 siblings, 1 reply; 20+ messages in thread
From: Apurva Nandan @ 2021-10-11 20:46 UTC (permalink / raw)
  To: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
	Mark Brown, Apurva Nandan, Patrice Chotard, Christophe Kerello,
	Boris Brezillon, linux-mtd, linux-kernel, linux-spi
  Cc: p.yadav

Enable Octal DTR SPI mode, i.e. 8D-8D-8D mode, if the SPI NAND flash
device supports it. Mixed OSPI (1S-1S-8S & 1S-8S-8S), mixed DTR modes
(1S-1D-8D), etc. aren't supported yet.

The method to switch to Octal DTR SPI mode may vary across
manufacturers. For example, for Winbond, it is enabled by writing
values to the volatile configuration register. So, let the
manufacturer's code have their own implementation for switching to
Octal DTR SPI mode.

Check for the SPI NAND device's support for Octal DTR mode using
spinand flags, and if the op_templates allows 8D-8D-8D, call
octal_dtr_enable() manufacturer op. If the SPI controller doesn't
supports these modes, the selected op_templates will prevent switching
to the Octal DTR mode. And finally update the spinand reg_proto
on success.

Signed-off-by: Apurva Nandan <a-nandan@ti.com>
---
 drivers/mtd/nand/spi/core.c | 46 +++++++++++++++++++++++++++++++++++++
 include/linux/mtd/spinand.h |  3 +++
 2 files changed, 49 insertions(+)

diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c
index 8e6cf7941a0f..1210946f8447 100644
--- a/drivers/mtd/nand/spi/core.c
+++ b/drivers/mtd/nand/spi/core.c
@@ -257,6 +257,48 @@ static int spinand_init_quad_enable(struct spinand_device *spinand)
 			       enable ? CFG_QUAD_ENABLE : 0);
 }
 
+static bool spinand_op_is_octal_dtr(const struct spi_mem_op *op)
+{
+	return  op->cmd.buswidth == 8 && op->cmd.dtr &&
+		op->addr.buswidth == 8 && op->addr.dtr &&
+		op->data.buswidth == 8 && op->data.dtr;
+}
+
+static int spinand_init_octal_dtr_enable(struct spinand_device *spinand)
+{
+	struct device *dev = &spinand->spimem->spi->dev;
+	int ret;
+
+	if (!(spinand->flags & SPINAND_HAS_OCTAL_DTR_BIT))
+		return 0;
+
+	if (!(spinand_op_is_octal_dtr(spinand->op_templates.read_cache) &&
+	      spinand_op_is_octal_dtr(spinand->op_templates.write_cache) &&
+	      spinand_op_is_octal_dtr(spinand->op_templates.update_cache)))
+		return 0;
+
+	if (!spinand->manufacturer->ops->octal_dtr_enable) {
+		dev_dbg(dev,
+			"Missing ->octal_dtr_enable(), unable to switch mode\n");
+		return -EINVAL;
+	}
+
+	ret = spinand->manufacturer->ops->octal_dtr_enable(spinand);
+	if (ret) {
+		dev_err(dev,
+			"Failed to enable Octal DTR SPI mode (err = %d)\n",
+			ret);
+		return ret;
+	}
+
+	spinand->reg_proto = SPINAND_OCTAL_DTR;
+
+	dev_dbg(dev,
+		"%s SPI NAND switched to Octal DTR SPI (8D-8D-8D) mode\n",
+		spinand->manufacturer->name);
+	return 0;
+}
+
 static int spinand_ecc_enable(struct spinand_device *spinand,
 			      bool enable)
 {
@@ -1192,6 +1234,10 @@ static int spinand_init_flash(struct spinand_device *spinand)
 	if (ret)
 		return ret;
 
+	ret = spinand_init_octal_dtr_enable(spinand);
+	if (ret)
+		return ret;
+
 	ret = spinand_upd_cfg(spinand, CFG_OTP_ENABLE, 0);
 	if (ret)
 		return ret;
diff --git a/include/linux/mtd/spinand.h b/include/linux/mtd/spinand.h
index 35816b8cfe81..daa2ac5c3110 100644
--- a/include/linux/mtd/spinand.h
+++ b/include/linux/mtd/spinand.h
@@ -271,6 +271,7 @@ struct spinand_devid {
  * @init: initialize a SPI NAND device
  * @adjust_op: modify the ops for any variation in their cmd, address, dummy or
  *	       data phase by the manufacturer
+ * @octal_dtr_enable: switch the SPI NAND flash into Octal DTR SPI mode
  * @cleanup: cleanup a SPI NAND device
  *
  * Each SPI NAND manufacturer driver should implement this interface so that
@@ -280,6 +281,7 @@ struct spinand_manufacturer_ops {
 	int (*init)(struct spinand_device *spinand);
 	void (*adjust_op)(struct spi_mem_op *op,
 			  const enum spinand_proto reg_proto);
+	int (*octal_dtr_enable)(struct spinand_device *spinand);
 	void (*cleanup)(struct spinand_device *spinand);
 };
 
@@ -348,6 +350,7 @@ struct spinand_ecc_info {
 
 #define SPINAND_HAS_QE_BIT		BIT(0)
 #define SPINAND_HAS_CR_FEAT_BIT		BIT(1)
+#define SPINAND_HAS_OCTAL_DTR_BIT	BIT(2)
 
 /**
  * struct spinand_ondie_ecc_conf - private SPI-NAND on-die ECC engine structure
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH v2 08/14] mtd: spinand: winbond: Add support for write volatile configuration register op
  2021-10-11 20:46 [PATCH v2 00/14] mtd: spinand: Add Octal DTR SPI (8D-8D-8D) mode support Apurva Nandan
                   ` (6 preceding siblings ...)
  2021-10-11 20:46 ` [PATCH v2 07/14] mtd: spinand: Allow enabling Octal DTR mode in the core Apurva Nandan
@ 2021-10-11 20:46 ` Apurva Nandan
  2021-10-11 20:46 ` [PATCH v2 09/14] mtd: spinand: winbond: Add octal_dtr_enable() for manufacturer_ops Apurva Nandan
                   ` (5 subsequent siblings)
  13 siblings, 0 replies; 20+ messages in thread
From: Apurva Nandan @ 2021-10-11 20:46 UTC (permalink / raw)
  To: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
	Mark Brown, Apurva Nandan, Patrice Chotard, Christophe Kerello,
	Boris Brezillon, linux-mtd, linux-kernel, linux-spi
  Cc: p.yadav

Volatile configuration register are a different set of configuration
registers, i.e. they differ from the status registers. A different
SPI instruction is required to write to these registers. Any changes
to the Volatile Configuration Register get transferred directly to
the Internal Configuration Register and instantly reflect on the
device operation.

In Winbond W35N01JW, these volatile configuration register must be
configured in order to switch to Octal DTR SPI mode.

Add support for writing to volatile configuration registers using a
new WRITE_VCR_OP template.

Datasheet: https://www.winbond.com/export/sites/winbond/datasheet/W35N01JW_Datasheet_Brief.pdf

Signed-off-by: Apurva Nandan <a-nandan@ti.com>
---
 drivers/mtd/nand/spi/core.c    |  2 +-
 drivers/mtd/nand/spi/winbond.c | 42 ++++++++++++++++++++++++++++++++++
 include/linux/mtd/spinand.h    |  1 +
 3 files changed, 44 insertions(+), 1 deletion(-)

diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c
index 1210946f8447..2bea21bd9747 100644
--- a/drivers/mtd/nand/spi/core.c
+++ b/drivers/mtd/nand/spi/core.c
@@ -445,7 +445,7 @@ static void spinand_ondie_ecc_save_status(struct nand_device *nand, u8 status)
 		engine_conf->status = status;
 }
 
-static int spinand_write_enable_op(struct spinand_device *spinand)
+int spinand_write_enable_op(struct spinand_device *spinand)
 {
 	struct spi_mem_op op = SPINAND_WR_EN_DIS_OP(true);
 
diff --git a/drivers/mtd/nand/spi/winbond.c b/drivers/mtd/nand/spi/winbond.c
index 76684428354e..89d8ee801f56 100644
--- a/drivers/mtd/nand/spi/winbond.c
+++ b/drivers/mtd/nand/spi/winbond.c
@@ -7,6 +7,7 @@
  *	Boris Brezillon <boris.brezillon@bootlin.com>
  */
 
+#include <linux/delay.h>
 #include <linux/device.h>
 #include <linux/kernel.h>
 #include <linux/mtd/spinand.h>
@@ -114,6 +115,47 @@ static int winbond_spinand_init(struct spinand_device *spinand)
 	return 0;
 }
 
+/**
+ * winbond_write_vcr_op() - write values onto the volatile configuration
+ *			    registers (VCR)
+ * @spinand: the spinand device
+ * @reg: the address of the particular reg in the VCR to be written on
+ * @val: the value to be written on the reg in the VCR
+ *
+ * Volatile configuration registers are a separate set of configuration
+ * registers, i.e. they differ from the status registers SR-1/2/3. A different
+ * SPI instruction is required to write to these registers. Any changes
+ * to the Volatile Configuration Register get transferred directly to
+ * the Internal Configuration Register and instantly reflect on the
+ * device operation.
+ */
+static int winbond_write_vcr_op(struct spinand_device *spinand, u8 reg, u8 val)
+{
+	int ret;
+	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));
+
+	*spinand->scratchbuf = val;
+
+	ret = spinand_write_enable_op(spinand);
+	if (ret)
+		return ret;
+
+	ret = spi_mem_exec_op(spinand->spimem, &op);
+	if (ret)
+		return ret;
+
+	/*
+	 * Write VCR operation doesn't set the busy bit in SR, so can't perform
+	 * a status poll. Minimum time of 50ns is needed to complete the write.
+	 * So, give thrice the minimum required delay.
+	 */
+	ndelay(150);
+	return 0;
+}
+
 static const struct spinand_manufacturer_ops winbond_spinand_manuf_ops = {
 	.init = winbond_spinand_init,
 };
diff --git a/include/linux/mtd/spinand.h b/include/linux/mtd/spinand.h
index daa2ac5c3110..21a4e5adcd59 100644
--- a/include/linux/mtd/spinand.h
+++ b/include/linux/mtd/spinand.h
@@ -560,5 +560,6 @@ int spinand_match_and_init(struct spinand_device *spinand,
 
 int spinand_upd_cfg(struct spinand_device *spinand, u8 mask, u8 val);
 int spinand_select_target(struct spinand_device *spinand, unsigned int target);
+int spinand_write_enable_op(struct spinand_device *spinand);
 
 #endif /* __LINUX_MTD_SPINAND_H */
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH v2 09/14] mtd: spinand: winbond: Add octal_dtr_enable() for manufacturer_ops
  2021-10-11 20:46 [PATCH v2 00/14] mtd: spinand: Add Octal DTR SPI (8D-8D-8D) mode support Apurva Nandan
                   ` (7 preceding siblings ...)
  2021-10-11 20:46 ` [PATCH v2 08/14] mtd: spinand: winbond: Add support for write volatile configuration register op Apurva Nandan
@ 2021-10-11 20:46 ` Apurva Nandan
  2021-10-11 20:46 ` [PATCH v2 10/14] mtd: spinand: Add support for Power-on-Reset (PoR) instruction Apurva Nandan
                   ` (4 subsequent siblings)
  13 siblings, 0 replies; 20+ messages in thread
From: Apurva Nandan @ 2021-10-11 20:46 UTC (permalink / raw)
  To: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
	Mark Brown, Apurva Nandan, Patrice Chotard, Christophe Kerello,
	Boris Brezillon, linux-mtd, linux-kernel, linux-spi
  Cc: p.yadav

Add implementation of octal_dtr_enable() manufacturer_ops for Winbond.
To switch to Ocatl DTR mode, setting programmable dummy cycles and
SPI IO mode using the volatile configuration register is required. To
function at max 120MHz SPI clock in Octal DTR mode, 12 programmable
dummy clock cycle setting is required. (Default number of dummy cycle
are 8 clocks)

Set the programmable dummy cycle to 12 clocks, and SPI IO mode to
Octal DTR with Data Strobe in the VCR. Also, perform a READ ID
operation in Octal DTR SPI mode to ensure the switch was successful.

Datasheet: https://www.winbond.com/export/sites/winbond/datasheet/W35N01JW_Datasheet_Brief.pdf

Signed-off-by: Apurva Nandan <a-nandan@ti.com>
---
 drivers/mtd/nand/spi/winbond.c | 42 ++++++++++++++++++++++++++++++++++
 1 file changed, 42 insertions(+)

diff --git a/drivers/mtd/nand/spi/winbond.c b/drivers/mtd/nand/spi/winbond.c
index 89d8ee801f56..e2cb82d68f96 100644
--- a/drivers/mtd/nand/spi/winbond.c
+++ b/drivers/mtd/nand/spi/winbond.c
@@ -16,6 +16,14 @@
 
 #define WINBOND_CFG_BUF_READ		BIT(3)
 
+/* Octal DTR SPI mode (8D-8D-8D) with Data Strobe output*/
+#define WINBOND_IO_MODE_VCR_OCTAL_DTR	0xE7
+#define WINBOND_IO_MODE_VCR_ADDR	0x00
+
+/* Use 12 dummy clk cycles for using Octal DTR SPI at max 120MHZ */
+#define WINBOND_DUMMY_CLK_COUNT		12
+#define WINBOND_DUMMY_CLK_VCR_ADDR	0x01
+
 static SPINAND_OP_VARIANTS(read_cache_variants,
 		SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 2, NULL, 0),
 		SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0),
@@ -156,8 +164,42 @@ static int winbond_write_vcr_op(struct spinand_device *spinand, u8 reg, u8 val)
 	return 0;
 }
 
+static int winbond_spinand_octal_dtr_enable(struct spinand_device *spinand)
+{
+	int ret;
+	struct spi_mem_op op;
+
+	ret = winbond_write_vcr_op(spinand, WINBOND_DUMMY_CLK_VCR_ADDR,
+				   WINBOND_DUMMY_CLK_COUNT);
+	if (ret)
+		return ret;
+
+	ret = winbond_write_vcr_op(spinand, WINBOND_IO_MODE_VCR_ADDR,
+				   WINBOND_IO_MODE_VCR_OCTAL_DTR);
+	if (ret)
+		return ret;
+
+	/* Read flash ID to make sure the switch was successful. */
+	op = (struct spi_mem_op)
+		SPI_MEM_OP(SPI_MEM_OP_CMD_DTR(2, 0x9f9f, 8),
+			   SPI_MEM_OP_NO_ADDR,
+			   SPI_MEM_OP_DUMMY_DTR(16, 8),
+			   SPI_MEM_OP_DATA_IN_DTR(SPINAND_MAX_ID_LEN,
+						  spinand->scratchbuf, 8));
+
+	ret = spi_mem_exec_op(spinand->spimem, &op);
+	if (ret)
+		return ret;
+
+	if (memcmp(spinand->scratchbuf, spinand->id.data, SPINAND_MAX_ID_LEN))
+		return -EINVAL;
+
+	return 0;
+}
+
 static const struct spinand_manufacturer_ops winbond_spinand_manuf_ops = {
 	.init = winbond_spinand_init,
+	.octal_dtr_enable = winbond_spinand_octal_dtr_enable,
 };
 
 const struct spinand_manufacturer winbond_spinand_manufacturer = {
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH v2 10/14] mtd: spinand: Add support for Power-on-Reset (PoR) instruction
  2021-10-11 20:46 [PATCH v2 00/14] mtd: spinand: Add Octal DTR SPI (8D-8D-8D) mode support Apurva Nandan
                   ` (8 preceding siblings ...)
  2021-10-11 20:46 ` [PATCH v2 09/14] mtd: spinand: winbond: Add octal_dtr_enable() for manufacturer_ops Apurva Nandan
@ 2021-10-11 20:46 ` Apurva Nandan
  2021-10-11 20:46 ` [PATCH v2 11/14] mtd: spinand: Perform Power-on-Reset on the flash in mtd_suspend() Apurva Nandan
                   ` (3 subsequent siblings)
  13 siblings, 0 replies; 20+ messages in thread
From: Apurva Nandan @ 2021-10-11 20:46 UTC (permalink / raw)
  To: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
	Mark Brown, Apurva Nandan, Patrice Chotard, Christophe Kerello,
	Boris Brezillon, linux-mtd, linux-kernel, linux-spi
  Cc: p.yadav

Manufacturers like Gigadevice and Winbond are adding Power-on-Reset
functionality in their SPI NAND flash chips. PoR instruction consists
of a 66h command followed by 99h command, and is different from the FFh
reset. The reset command FFh just clears the status only registers,
while the PoR command erases all the configurations written to the
flash and is equivalent to a power-down -> power-up cycle.

Add support for the Power-on-Reset command for any flash that provides
this feature.

Datasheet: https://www.winbond.com/export/sites/winbond/datasheet/W35N01JW_Datasheet_Brief.pdf

Signed-off-by: Apurva Nandan <a-nandan@ti.com>
---
 drivers/mtd/nand/spi/core.c | 43 +++++++++++++++++++++++++++++++++++++
 include/linux/mtd/spinand.h | 17 +++++++++++++++
 2 files changed, 60 insertions(+)

diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c
index 2bea21bd9747..9b570570ee81 100644
--- a/drivers/mtd/nand/spi/core.c
+++ b/drivers/mtd/nand/spi/core.c
@@ -9,6 +9,7 @@
 
 #define pr_fmt(fmt)	"spi-nand: " fmt
 
+#include <linux/delay.h>
 #include <linux/device.h>
 #include <linux/jiffies.h>
 #include <linux/kernel.h>
@@ -668,6 +669,48 @@ static int spinand_reset_op(struct spinand_device *spinand)
 			    NULL);
 }
 
+static int spinand_power_on_rst_op(struct spinand_device *spinand)
+{
+	struct spi_mem_op op;
+	int ret;
+
+	if (!(spinand->flags & SPINAND_HAS_POR_CMD_BIT))
+		return -EOPNOTSUPP;
+
+	/*
+	 * If flash is in a busy state, wait for it to finish the operation.
+	 * As the operation is unknown, use reset poll delays here.
+	 */
+	ret = spinand_wait(spinand,
+			   SPINAND_RESET_INITIAL_DELAY_US,
+			   SPINAND_RESET_POLL_DELAY_US,
+			   NULL);
+	if (ret)
+		return ret;
+
+	op = (struct spi_mem_op)SPINAND_EN_POWER_ON_RST_OP;
+
+	spinand_patch_op(spinand, &op);
+	ret = spi_mem_exec_op(spinand->spimem, &op);
+	if (ret)
+		return ret;
+
+	op = (struct spi_mem_op)SPINAND_POWER_ON_RST_OP;
+
+	spinand_patch_op(spinand, &op);
+	ret = spi_mem_exec_op(spinand->spimem, &op);
+	if (ret)
+		return ret;
+
+	/* PoR can take max 500 us to complete, so sleep for 600 to 700 us*/
+	usleep_range(SPINAND_POR_MIN_DELAY_US, SPINAND_POR_MAX_DELAY_US);
+
+	dev_dbg(&spinand->spimem->spi->dev,
+		"%s SPI NAND reset to Power-On-Reset state.\n",
+		spinand->manufacturer->name);
+	return 0;
+}
+
 static int spinand_lock_block(struct spinand_device *spinand, u8 lock)
 {
 	return spinand_write_reg_op(spinand, REG_BLOCK_LOCK, lock);
diff --git a/include/linux/mtd/spinand.h b/include/linux/mtd/spinand.h
index 21a4e5adcd59..baaf8e94f301 100644
--- a/include/linux/mtd/spinand.h
+++ b/include/linux/mtd/spinand.h
@@ -26,6 +26,18 @@
 		   SPI_MEM_OP_NO_DUMMY,					\
 		   SPI_MEM_OP_NO_DATA)
 
+#define SPINAND_EN_POWER_ON_RST_OP					\
+	SPI_MEM_OP(SPI_MEM_OP_CMD(0x66, 1),				\
+		   SPI_MEM_OP_NO_ADDR,					\
+		   SPI_MEM_OP_NO_DUMMY,					\
+		   SPI_MEM_OP_NO_DATA)
+
+#define SPINAND_POWER_ON_RST_OP						\
+	SPI_MEM_OP(SPI_MEM_OP_CMD(0x99, 1),				\
+		   SPI_MEM_OP_NO_ADDR,					\
+		   SPI_MEM_OP_NO_DUMMY,					\
+		   SPI_MEM_OP_NO_DATA)
+
 #define SPINAND_WR_EN_DIS_OP(enable)					\
 	SPI_MEM_OP(SPI_MEM_OP_CMD((enable) ? 0x06 : 0x04, 1),		\
 		   SPI_MEM_OP_NO_ADDR,					\
@@ -218,6 +230,8 @@ struct spinand_device;
  * reading/programming/erasing when the RESET occurs. Since we always
  * issue a RESET when the device is IDLE, 5us is selected for both initial
  * and poll delay.
+ * Power on Reset can take upto 500 us to complete, so sleep for 600 us
+ * to 700 us safely.
  */
 #define SPINAND_READ_INITIAL_DELAY_US	6
 #define SPINAND_READ_POLL_DELAY_US	5
@@ -227,6 +241,8 @@ struct spinand_device;
 #define SPINAND_WRITE_POLL_DELAY_US	15
 #define SPINAND_ERASE_INITIAL_DELAY_US	250
 #define SPINAND_ERASE_POLL_DELAY_US	50
+#define SPINAND_POR_MIN_DELAY_US	600
+#define SPINAND_POR_MAX_DELAY_US	700
 
 #define SPINAND_WAITRDY_TIMEOUT_MS	400
 
@@ -351,6 +367,7 @@ struct spinand_ecc_info {
 #define SPINAND_HAS_QE_BIT		BIT(0)
 #define SPINAND_HAS_CR_FEAT_BIT		BIT(1)
 #define SPINAND_HAS_OCTAL_DTR_BIT	BIT(2)
+#define SPINAND_HAS_POR_CMD_BIT		BIT(3)
 
 /**
  * struct spinand_ondie_ecc_conf - private SPI-NAND on-die ECC engine structure
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH v2 11/14] mtd: spinand: Perform Power-on-Reset on the flash in mtd_suspend()
  2021-10-11 20:46 [PATCH v2 00/14] mtd: spinand: Add Octal DTR SPI (8D-8D-8D) mode support Apurva Nandan
                   ` (9 preceding siblings ...)
  2021-10-11 20:46 ` [PATCH v2 10/14] mtd: spinand: Add support for Power-on-Reset (PoR) instruction Apurva Nandan
@ 2021-10-11 20:46 ` Apurva Nandan
  2021-10-12  7:25   ` Boris Brezillon
  2021-10-11 20:46 ` [PATCH v2 12/14] mtd: spinand: Add adjust_op() in Winbond manufacturer_ops Apurva Nandan
                   ` (2 subsequent siblings)
  13 siblings, 1 reply; 20+ messages in thread
From: Apurva Nandan @ 2021-10-11 20:46 UTC (permalink / raw)
  To: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
	Mark Brown, Apurva Nandan, Patrice Chotard, Christophe Kerello,
	Boris Brezillon, linux-mtd, linux-kernel, linux-spi
  Cc: p.yadav

A soft reset using FFh command doesn't erase the flash's configuration
and doesn't reset the SPI IO mode also. This can result in the flash
being in a different SPI IO mode, e.g. Octal DTR, when resuming from
sleep. This could put the flash in an unrecognized SPI IO mode, making
it unusable.

Perform a Power-on-Reset (PoR), if available in the flash, when
performing mtd_suspend(). This would set the flash to clean
state for reinitialization during resume and would also ensure that it
is in standard SPI IO mode (1S-1S-1S) before the resume begins.

Signed-off-by: Apurva Nandan <a-nandan@ti.com>
---
 drivers/mtd/nand/spi/core.c | 21 +++++++++++++++++++++
 1 file changed, 21 insertions(+)

diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c
index 9b570570ee81..60408531979a 100644
--- a/drivers/mtd/nand/spi/core.c
+++ b/drivers/mtd/nand/spi/core.c
@@ -1316,6 +1316,11 @@ static void spinand_mtd_resume(struct mtd_info *mtd)
 	int ret;
 
 	spinand->reg_proto = SPINAND_SINGLE_STR;
+	/*
+	 * PoR Reset (if available by the manufacturer) is performed at the suspend
+	 * time. Hence, those flashes remain in power-on-state at this point, in a
+	 * standard SPI IO mode. So, now the core unanimously performs a soft reset.
+	 */
 	ret = spinand_reset_op(spinand);
 	if (ret)
 		return;
@@ -1327,6 +1332,21 @@ 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;
+
+	if (!(spinand->flags & SPINAND_HAS_POR_CMD_BIT))
+		return 0;
+
+	ret = spinand_power_on_rst_op(spinand);
+	if (ret)
+		dev_err(&spinand->spimem->spi->dev, "suspend() failed\n");
+
+	return ret;
+}
+
 static int spinand_init(struct spinand_device *spinand)
 {
 	struct device *dev = &spinand->spimem->spi->dev;
@@ -1399,6 +1419,7 @@ static int spinand_init(struct spinand_device *spinand)
 	mtd->_erase = spinand_mtd_erase;
 	mtd->_max_bad_blocks = nanddev_mtd_max_bad_blocks;
 	mtd->_resume = spinand_mtd_resume;
+	mtd->_suspend = spinand_mtd_suspend;
 
 	if (nand->ecc.engine) {
 		ret = mtd_ooblayout_count_freebytes(mtd);
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH v2 12/14] mtd: spinand: Add adjust_op() in Winbond manufacturer_ops
  2021-10-11 20:46 [PATCH v2 00/14] mtd: spinand: Add Octal DTR SPI (8D-8D-8D) mode support Apurva Nandan
                   ` (10 preceding siblings ...)
  2021-10-11 20:46 ` [PATCH v2 11/14] mtd: spinand: Perform Power-on-Reset on the flash in mtd_suspend() Apurva Nandan
@ 2021-10-11 20:46 ` Apurva Nandan
  2021-10-11 20:46 ` [PATCH v2 13/14] mtd: spinand: winbond: Rename cache op_variants struct variable Apurva Nandan
  2021-10-11 20:46 ` [PATCH v2 14/14] mtd: spinand: winbond: Add support for Winbond W35N01JW SPI NAND flash Apurva Nandan
  13 siblings, 0 replies; 20+ messages in thread
From: Apurva Nandan @ 2021-10-11 20:46 UTC (permalink / raw)
  To: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
	Mark Brown, Apurva Nandan, Patrice Chotard, Christophe Kerello,
	Boris Brezillon, linux-mtd, linux-kernel, linux-spi
  Cc: p.yadav

Add implementation of adjust_op() manufacturer_ops for Winbond. This
handles the variations introduced in read register, read vcr, blk
erase, page read and program exec ops when operating in Octal DTR mode.
Read register operation requires 7 dummy cycles and read VCR operation
requires 8 dummy cycles in Octal DTR mode instead of default 0 dummy
cycle. Block erase, page read and program exec operations require
2 byte address in Octal DTR mode instead of default 3 bytes.

Datasheet: https://www.winbond.com/export/sites/winbond/datasheet/W35N01JW_Datasheet_Brief.pdf

Signed-off-by: Apurva Nandan <a-nandan@ti.com>
---
 drivers/mtd/nand/spi/winbond.c | 45 ++++++++++++++++++++++++++++++++++
 1 file changed, 45 insertions(+)

diff --git a/drivers/mtd/nand/spi/winbond.c b/drivers/mtd/nand/spi/winbond.c
index e2cb82d68f96..d962221d4082 100644
--- a/drivers/mtd/nand/spi/winbond.c
+++ b/drivers/mtd/nand/spi/winbond.c
@@ -16,6 +16,13 @@
 
 #define WINBOND_CFG_BUF_READ		BIT(3)
 
+#define WINBOND_BLK_ERASE_OPCODE	0xD8
+#define WINBOND_PAGE_READ_OPCODE	0x13
+#define WINBOND_PROG_EXEC_OPCODE	0x10
+#define WINBOND_READ_REG_OPCODE_1	0x05
+#define WINBOND_READ_REG_OPCODE_2	0x0F
+#define WINBOND_READ_VCR_OPCODE		0x85
+
 /* Octal DTR SPI mode (8D-8D-8D) with Data Strobe output*/
 #define WINBOND_IO_MODE_VCR_OCTAL_DTR	0xE7
 #define WINBOND_IO_MODE_VCR_ADDR	0x00
@@ -197,9 +204,47 @@ static int winbond_spinand_octal_dtr_enable(struct spinand_device *spinand)
 	return 0;
 }
 
+static void winbond_spinand_adjust_op(struct spi_mem_op *op,
+				      const enum spinand_proto reg_proto)
+{
+	/*
+	 * To support both 1 byte opcode and 2 byte opcodes, extract the MSB
+	 * byte from the opcode as the LSB byte in 2 byte opcode is treated as
+	 * don't care.
+	 */
+	u8 opcode = op->cmd.opcode >> (8 * (op->cmd.nbytes - 1));
+
+	if (reg_proto == SPINAND_OCTAL_DTR) {
+		switch (opcode) {
+		case WINBOND_READ_REG_OPCODE_1:
+		case WINBOND_READ_REG_OPCODE_2:
+			op->dummy.nbytes = 14;
+			op->dummy.buswidth = 8;
+			op->dummy.dtr = true;
+			return;
+
+		case WINBOND_READ_VCR_OPCODE:
+			op->dummy.nbytes = 16;
+			op->dummy.buswidth = 8;
+			op->dummy.dtr = true;
+			return;
+
+		case WINBOND_BLK_ERASE_OPCODE:
+		case WINBOND_PAGE_READ_OPCODE:
+		case WINBOND_PROG_EXEC_OPCODE:
+			op->addr.nbytes = 2;
+			return;
+
+		default:
+			return;
+		}
+	}
+}
+
 static const struct spinand_manufacturer_ops winbond_spinand_manuf_ops = {
 	.init = winbond_spinand_init,
 	.octal_dtr_enable = winbond_spinand_octal_dtr_enable,
+	.adjust_op = winbond_spinand_adjust_op,
 };
 
 const struct spinand_manufacturer winbond_spinand_manufacturer = {
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH v2 13/14] mtd: spinand: winbond: Rename cache op_variants struct variable
  2021-10-11 20:46 [PATCH v2 00/14] mtd: spinand: Add Octal DTR SPI (8D-8D-8D) mode support Apurva Nandan
                   ` (11 preceding siblings ...)
  2021-10-11 20:46 ` [PATCH v2 12/14] mtd: spinand: Add adjust_op() in Winbond manufacturer_ops Apurva Nandan
@ 2021-10-11 20:46 ` Apurva Nandan
  2021-10-11 20:46 ` [PATCH v2 14/14] mtd: spinand: winbond: Add support for Winbond W35N01JW SPI NAND flash Apurva Nandan
  13 siblings, 0 replies; 20+ messages in thread
From: Apurva Nandan @ 2021-10-11 20:46 UTC (permalink / raw)
  To: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
	Mark Brown, Apurva Nandan, Patrice Chotard, Christophe Kerello,
	Boris Brezillon, linux-mtd, linux-kernel, linux-spi
  Cc: p.yadav

Till now, supported Winbond SPI NAND flashes had same supported
op_variants. W35N01JW introduces Octal DTR SPI IO mode, so now
different op_variants struct variables are required for different
Winbond flashes. Hence, rename and append the flash name in the
op_variants struct variable.

Datasheet: https://www.winbond.com/export/sites/winbond/datasheet/W35N01JW_Datasheet_Brief.pdf

Signed-off-by: Apurva Nandan <a-nandan@ti.com>
---
 drivers/mtd/nand/spi/winbond.c | 18 +++++++++---------
 1 file changed, 9 insertions(+), 9 deletions(-)

diff --git a/drivers/mtd/nand/spi/winbond.c b/drivers/mtd/nand/spi/winbond.c
index d962221d4082..1857836f19d0 100644
--- a/drivers/mtd/nand/spi/winbond.c
+++ b/drivers/mtd/nand/spi/winbond.c
@@ -31,7 +31,7 @@
 #define WINBOND_DUMMY_CLK_COUNT		12
 #define WINBOND_DUMMY_CLK_VCR_ADDR	0x01
 
-static SPINAND_OP_VARIANTS(read_cache_variants,
+static SPINAND_OP_VARIANTS(read_cache_variants_w25xxgv,
 		SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 2, NULL, 0),
 		SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0),
 		SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 1, NULL, 0),
@@ -39,11 +39,11 @@ static SPINAND_OP_VARIANTS(read_cache_variants,
 		SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0),
 		SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0));
 
-static SPINAND_OP_VARIANTS(write_cache_variants,
+static SPINAND_OP_VARIANTS(write_cache_variants_w25xxgv,
 		SPINAND_PROG_LOAD_X4(true, 0, NULL, 0),
 		SPINAND_PROG_LOAD(true, 0, NULL, 0));
 
-static SPINAND_OP_VARIANTS(update_cache_variants,
+static SPINAND_OP_VARIANTS(update_cache_variants_w25xxgv,
 		SPINAND_PROG_LOAD_X4(false, 0, NULL, 0),
 		SPINAND_PROG_LOAD(false, 0, NULL, 0));
 
@@ -95,9 +95,9 @@ static const struct spinand_info winbond_spinand_table[] = {
 		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xab),
 		     NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 2),
 		     NAND_ECCREQ(1, 512),
-		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
-					      &write_cache_variants,
-					      &update_cache_variants),
+		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants_w25xxgv,
+					      &write_cache_variants_w25xxgv,
+					      &update_cache_variants_w25xxgv),
 		     0,
 		     SPINAND_ECCINFO(&w25m02gv_ooblayout, NULL),
 		     SPINAND_SELECT_TARGET(w25m02gv_select_target)),
@@ -105,9 +105,9 @@ static const struct spinand_info winbond_spinand_table[] = {
 		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xaa),
 		     NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1),
 		     NAND_ECCREQ(1, 512),
-		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
-					      &write_cache_variants,
-					      &update_cache_variants),
+		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants_w25xxgv,
+					      &write_cache_variants_w25xxgv,
+					      &update_cache_variants_w25xxgv),
 		     0,
 		     SPINAND_ECCINFO(&w25m02gv_ooblayout, NULL)),
 };
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH v2 14/14] mtd: spinand: winbond: Add support for Winbond W35N01JW SPI NAND flash
  2021-10-11 20:46 [PATCH v2 00/14] mtd: spinand: Add Octal DTR SPI (8D-8D-8D) mode support Apurva Nandan
                   ` (12 preceding siblings ...)
  2021-10-11 20:46 ` [PATCH v2 13/14] mtd: spinand: winbond: Rename cache op_variants struct variable Apurva Nandan
@ 2021-10-11 20:46 ` Apurva Nandan
  13 siblings, 0 replies; 20+ messages in thread
From: Apurva Nandan @ 2021-10-11 20:46 UTC (permalink / raw)
  To: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
	Mark Brown, Apurva Nandan, Patrice Chotard, Christophe Kerello,
	Boris Brezillon, linux-mtd, linux-kernel, linux-spi
  Cc: p.yadav

Winbond W35N01JW is a SPI NAND flash supporting Octal DTR SPI protocol.
Add op_variants for W35N01JW, which include the Octal DTR read/write
page ops as well. Add W35N01JW's OOB layout functions for the
mtd_ooblayout_ops. Finally, add an entry for W35N01JW in spinand_info
table.

Datasheet: https://www.winbond.com/export/sites/winbond/datasheet/W35N01JW_Datasheet_Brief.pdf

Signed-off-by: Apurva Nandan <a-nandan@ti.com>
---
 drivers/mtd/nand/spi/winbond.c | 53 ++++++++++++++++++++++++++++++++++
 1 file changed, 53 insertions(+)

diff --git a/drivers/mtd/nand/spi/winbond.c b/drivers/mtd/nand/spi/winbond.c
index 1857836f19d0..8f687b6a6697 100644
--- a/drivers/mtd/nand/spi/winbond.c
+++ b/drivers/mtd/nand/spi/winbond.c
@@ -47,6 +47,19 @@ static SPINAND_OP_VARIANTS(update_cache_variants_w25xxgv,
 		SPINAND_PROG_LOAD_X4(false, 0, NULL, 0),
 		SPINAND_PROG_LOAD(false, 0, NULL, 0));
 
+static SPINAND_OP_VARIANTS(read_cache_variants_w35n01jw,
+		SPINAND_PAGE_READ_FROM_CACHE_OCTALIO_DTR_OP(0, 24, NULL, 0),
+		SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0),
+		SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0));
+
+static SPINAND_OP_VARIANTS(write_cache_variants_w35n01jw,
+		SPINAND_PROG_LOAD_OCTALIO_DTR(true, 0, NULL, 0),
+		SPINAND_PROG_LOAD(true, 0, NULL, 0));
+
+static SPINAND_OP_VARIANTS(update_cache_variants_w35n01jw,
+		SPINAND_PROG_LOAD_OCTALIO_DTR(false, 0, NULL, 0),
+		SPINAND_PROG_LOAD(false, 0, NULL, 0));
+
 static int w25m02gv_ooblayout_ecc(struct mtd_info *mtd, int section,
 				  struct mtd_oob_region *region)
 {
@@ -71,11 +84,40 @@ static int w25m02gv_ooblayout_free(struct mtd_info *mtd, int section,
 	return 0;
 }
 
+static int w35n01jw_ooblayout_ecc(struct mtd_info *mtd, int section,
+				  struct mtd_oob_region *region)
+{
+	if (section > 7)
+		return -ERANGE;
+
+	region->offset = (16 * section) + 12;
+	region->length = 4;
+
+	return 0;
+}
+
+static int w35n01jw_ooblayout_free(struct mtd_info *mtd, int section,
+				   struct mtd_oob_region *region)
+{
+	if (section > 7)
+		return -ERANGE;
+
+	region->offset = (16 * section) + 2;
+	region->length = 10;
+
+	return 0;
+}
+
 static const struct mtd_ooblayout_ops w25m02gv_ooblayout = {
 	.ecc = w25m02gv_ooblayout_ecc,
 	.free = w25m02gv_ooblayout_free,
 };
 
+static const struct mtd_ooblayout_ops w35n01jw_ooblayout = {
+	.ecc = w35n01jw_ooblayout_ecc,
+	.free = w35n01jw_ooblayout_free,
+};
+
 static int w25m02gv_select_target(struct spinand_device *spinand,
 				  unsigned int target)
 {
@@ -110,6 +152,17 @@ static const struct spinand_info winbond_spinand_table[] = {
 					      &update_cache_variants_w25xxgv),
 		     0,
 		     SPINAND_ECCINFO(&w25m02gv_ooblayout, NULL)),
+	SPINAND_INFO("W35N01JW",
+		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xdc),
+		     NAND_MEMORG(1, 4096, 128, 64, 512, 20, 1, 1, 1),
+		     NAND_ECCREQ(1, 512),
+		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants_w35n01jw,
+					      &write_cache_variants_w35n01jw,
+					      &update_cache_variants_w35n01jw),
+		     SPINAND_HAS_OCTAL_DTR_BIT | SPINAND_HAS_POR_CMD_BIT |
+		     SPINAND_HAS_CR_FEAT_BIT,
+		     SPINAND_ECCINFO(&w35n01jw_ooblayout, NULL)),
+
 };
 
 static int winbond_spinand_init(struct spinand_device *spinand)
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* Re: [PATCH v2 02/14] mtd: spinand: Add enum spinand_proto to indicate current SPI IO mode
  2021-10-11 20:46 ` [PATCH v2 02/14] mtd: spinand: Add enum spinand_proto to indicate current SPI IO mode Apurva Nandan
@ 2021-10-12  6:39   ` Boris Brezillon
  0 siblings, 0 replies; 20+ messages in thread
From: Boris Brezillon @ 2021-10-12  6:39 UTC (permalink / raw)
  To: Apurva Nandan
  Cc: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
	Mark Brown, Patrice Chotard, Christophe Kerello, linux-mtd,
	linux-kernel, linux-spi, p.yadav

On Tue, 12 Oct 2021 02:16:07 +0530
Apurva Nandan <a-nandan@ti.com> wrote:

> Unlike Dual and Quad SPI modes flashes, Octal DTR SPI NAND flashes
> require all instructions to be made in 8D-8D-8D protocol when the
> flash is in Octal DTR mode. Hence, storing the current SPI IO mode
> becomes necessary for correctly generating non-array access operations.
> 
> Store the current SPI IO mode in the spinand struct using a reg_proto
> enum. This would act as a flag, denoting that the core should use
> the given SPI protocol for non-page access operations.
> 
> Also provide basic macros for extracting buswidth and dtr mode
> information from the spinand_proto enum.
> 
> Signed-off-by: Apurva Nandan <a-nandan@ti.com>
> ---
>  drivers/mtd/nand/spi/core.c |  2 ++
>  include/linux/mtd/spinand.h | 30 ++++++++++++++++++++++++++++++
>  2 files changed, 32 insertions(+)
> 
> diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c
> index 2c8685f1f2fa..d82a3e6d9bb5 100644
> --- a/drivers/mtd/nand/spi/core.c
> +++ b/drivers/mtd/nand/spi/core.c
> @@ -1155,6 +1155,7 @@ static void spinand_mtd_resume(struct mtd_info *mtd)
>  	struct spinand_device *spinand = mtd_to_spinand(mtd);
>  	int ret;
>  
> +	spinand->reg_proto = SPINAND_SINGLE_STR;
>  	ret = spinand_reset_op(spinand);
>  	if (ret)
>  		return;
> @@ -1181,6 +1182,7 @@ static int spinand_init(struct spinand_device *spinand)
>  	if (!spinand->scratchbuf)
>  		return -ENOMEM;
>  
> +	spinand->reg_proto = SPINAND_SINGLE_STR;
>  	ret = spinand_detect(spinand);
>  	if (ret)
>  		goto err_free_bufs;
> diff --git a/include/linux/mtd/spinand.h b/include/linux/mtd/spinand.h
> index 6988956b8492..f6093cd98d7b 100644
> --- a/include/linux/mtd/spinand.h
> +++ b/include/linux/mtd/spinand.h
> @@ -140,6 +140,31 @@
>  		   SPI_MEM_OP_NO_DUMMY,					\
>  		   SPI_MEM_OP_DATA_OUT(len, buf, 4))
>  
> +#define SPINAND_PROTO_BUSWIDTH_MASK	GENMASK(6, 0)
> +#define SPINAND_PROTO_DTR_BIT		BIT(7)
> +
> +#define SPINAND_PROTO_STR(__buswidth)	\
> +	((u8)(((__buswidth) - 1) & SPINAND_PROTO_BUSWIDTH_MASK))
> +#define SPINAND_PROTO_DTR(__buswidth)	\
> +	(SPINAND_PROTO_DTR_BIT | SPINAND_PROTO_STR(__buswidth))
> +
> +#define SPINAND_PROTO_BUSWIDTH(__proto)	\
> +	((u8)(((__proto) & SPINAND_PROTO_BUSWIDTH_MASK) + 1))
> +#define SPINAND_PROTO_IS_DTR(__proto)	(!!((__proto) & SPINAND_PROTO_DTR_BIT))
> +
> +/**
> + * enum spinand_proto - List allowable SPI protocol variants for read reg,
> + *			write reg, blk erase, write enable/disable, page read
> + *			and program exec operations.
> + */
> +enum spinand_proto {

s/spinand_proto/spinand_protocol/

> +	SPINAND_SINGLE_STR = SPINAND_PROTO_STR(1),
> +	SPINAND_DUAL_STR = SPINAND_PROTO_STR(2),
> +	SPINAND_QUAD_STR = SPINAND_PROTO_STR(4),
> +	SPINAND_OCTAL_STR = SPINAND_PROTO_STR(8),
> +	SPINAND_OCTAL_DTR = SPINAND_PROTO_DTR(8),

Why not have a contiguous enum listing all the modes? Are you extracting
the buswidth from these values?

> +};
> +
>  /**
>   * Standard SPI NAND flash commands
>   */
> @@ -407,6 +432,9 @@ struct spinand_dirmap {
>   *		   this die. Only required if your chip exposes several dies
>   * @cur_target: currently selected target/die
>   * @eccinfo: on-die ECC information
> + * @reg_proto: select a variant of SPI IO protocol (single, quad, octal or
> + *	       octal DTR) for read_reg/write_reg/erase operations. Update on
> + *	       successful transition into a different SPI IO protocol.
>   * @cfg_cache: config register cache. One entry per die
>   * @databuf: bounce buffer for data
>   * @oobbuf: bounce buffer for OOB data
> @@ -438,6 +466,8 @@ struct spinand_device {
>  
>  	struct spinand_ecc_info eccinfo;
>  
> +	enum spinand_proto reg_proto;
> +

I guess this mode will apply to all sort of commands, not just reg
accesses, so why not name it protocol or mode?

>  	u8 *cfg_cache;
>  	u8 *databuf;
>  	u8 *oobbuf;


^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH v2 03/14] mtd: spinand: Patch spi_mem_op for the SPI IO protocol using reg_proto
  2021-10-11 20:46 ` [PATCH v2 03/14] mtd: spinand: Patch spi_mem_op for the SPI IO protocol using reg_proto Apurva Nandan
@ 2021-10-12  6:40   ` Boris Brezillon
  0 siblings, 0 replies; 20+ messages in thread
From: Boris Brezillon @ 2021-10-12  6:40 UTC (permalink / raw)
  To: Apurva Nandan
  Cc: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
	Mark Brown, Patrice Chotard, Christophe Kerello, linux-mtd,
	linux-kernel, linux-spi, p.yadav

On Tue, 12 Oct 2021 02:16:08 +0530
Apurva Nandan <a-nandan@ti.com> wrote:

> Currently, the op macros in spinand.h don't give the option to setup
> any non-array access instructions for Dual/Quad/Octal DTR SPI bus.
> Having a function that patches the op based on reg_proto would be
> better than trying to write all the setup logic in op macros.
> 
> Create a spimem_patch_op() that would patch cmd, addr, dummy and data
> phase of the spi_mem op, for the given spinand->reg_proto. And hence,
> call the spimem_patch_op() before executing any spi_mem op.

I'm honestly not sure this rule will stand over time, and patching
operations at submission time has a cost (ok, it's negligible compared
to the IO duration, but still), so I'd rather see this done statically
with all relevant macros taking an extra dtr argument.

> 
> Note: In this commit, spimem_patch_op() isn't called in the
> read_reg_op(), write_reg_op() and wait() functions, as they need
> modifications in address value and data nbytes when in Octal DTR mode.
> This will be fixed in a later commit.

See, that's what I'm talking about.

> 
> Signed-off-by: Apurva Nandan <a-nandan@ti.com>
> ---
>  drivers/mtd/nand/spi/core.c | 49 +++++++++++++++++++++++++++++++++++++
>  1 file changed, 49 insertions(+)
> 
> diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c
> index d82a3e6d9bb5..11746d858f87 100644
> --- a/drivers/mtd/nand/spi/core.c
> +++ b/drivers/mtd/nand/spi/core.c
> @@ -20,6 +20,49 @@
>  #include <linux/spi/spi.h>
>  #include <linux/spi/spi-mem.h>
>  
> +/**
> + * spinand_patch_op() - Helper function to patch the spi_mem op based on the
> + *			spinand->reg_proto
> + * @spinand: the spinand device
> + * @op: the spi_mem op to patch
> + *
> + * Set up buswidth and dtr fields for cmd, addr, dummy and data phase. Also
> + * adjust cmd opcode and dummy nbytes. This function doesn't make any changes
> + * to addr val or data buf.
> + */
> +static void spinand_patch_op(const struct spinand_device *spinand,
> +			     struct spi_mem_op *op)
> +{
> +	u8 op_buswidth = SPINAND_PROTO_BUSWIDTH(spinand->reg_proto);
> +	u8 op_is_dtr = SPINAND_PROTO_IS_DTR(spinand->reg_proto);
> +
> +	if (spinand->reg_proto == SPINAND_SINGLE_STR)
> +		return;
> +
> +	op->cmd.buswidth = op_buswidth;
> +	op->cmd.dtr = op_is_dtr;
> +	if (spinand->reg_proto == SPINAND_OCTAL_DTR) {
> +		op->cmd.opcode = (op->cmd.opcode << 8) | op->cmd.opcode;
> +		op->cmd.nbytes = 2;
> +	}
> +
> +	if (op->addr.nbytes) {
> +		op->addr.buswidth = op_buswidth;
> +		op->addr.dtr = op_is_dtr;
> +	}
> +
> +	if (op->dummy.nbytes) {
> +		op->dummy.buswidth = op_buswidth;
> +		op->dummy.dtr = op_is_dtr;
> +		op->dummy.nbytes <<= op_is_dtr;
> +	}
> +
> +	if (op->data.nbytes) {
> +		op->data.buswidth = op_buswidth;
> +		op->data.dtr = op_is_dtr;
> +	}
> +}
> +
>  static int spinand_read_reg_op(struct spinand_device *spinand, u8 reg, u8 *val)
>  {
>  	struct spi_mem_op op = SPINAND_GET_FEATURE_OP(reg,
> @@ -343,6 +386,7 @@ static int spinand_write_enable_op(struct spinand_device *spinand)
>  {
>  	struct spi_mem_op op = SPINAND_WR_EN_DIS_OP(true);
>  
> +	spinand_patch_op(spinand, &op);
>  	return spi_mem_exec_op(spinand->spimem, &op);
>  }
>  
> @@ -353,6 +397,7 @@ static int spinand_load_page_op(struct spinand_device *spinand,
>  	unsigned int row = nanddev_pos_to_row(nand, &req->pos);
>  	struct spi_mem_op op = SPINAND_PAGE_READ_OP(row);
>  
> +	spinand_patch_op(spinand, &op);
>  	return spi_mem_exec_op(spinand->spimem, &op);
>  }
>  
> @@ -477,6 +522,7 @@ static int spinand_program_op(struct spinand_device *spinand,
>  	unsigned int row = nanddev_pos_to_row(nand, &req->pos);
>  	struct spi_mem_op op = SPINAND_PROG_EXEC_OP(row);
>  
> +	spinand_patch_op(spinand, &op);
>  	return spi_mem_exec_op(spinand->spimem, &op);
>  }
>  
> @@ -487,6 +533,7 @@ static int spinand_erase_op(struct spinand_device *spinand,
>  	unsigned int row = nanddev_pos_to_row(nand, pos);
>  	struct spi_mem_op op = SPINAND_BLK_ERASE_OP(row);
>  
> +	spinand_patch_op(spinand, &op);
>  	return spi_mem_exec_op(spinand->spimem, &op);
>  }
>  
> @@ -533,6 +580,7 @@ static int spinand_read_id_op(struct spinand_device *spinand, u8 naddr,
>  		naddr, ndummy, spinand->scratchbuf, SPINAND_MAX_ID_LEN);
>  	int ret;
>  
> +	spinand_patch_op(spinand, &op);
>  	ret = spi_mem_exec_op(spinand->spimem, &op);
>  	if (!ret)
>  		memcpy(buf, spinand->scratchbuf, SPINAND_MAX_ID_LEN);
> @@ -545,6 +593,7 @@ static int spinand_reset_op(struct spinand_device *spinand)
>  	struct spi_mem_op op = SPINAND_RESET_OP;
>  	int ret;
>  
> +	spinand_patch_op(spinand, &op);
>  	ret = spi_mem_exec_op(spinand->spimem, &op);
>  	if (ret)
>  		return ret;


^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH v2 05/14] mtd: spinand: Add adjust_op() in manufacturer_ops to modify the ops for manufacturer specific changes
  2021-10-11 20:46 ` [PATCH v2 05/14] mtd: spinand: Add adjust_op() in manufacturer_ops to modify the ops for manufacturer specific changes Apurva Nandan
@ 2021-10-12  6:54   ` Boris Brezillon
  0 siblings, 0 replies; 20+ messages in thread
From: Boris Brezillon @ 2021-10-12  6:54 UTC (permalink / raw)
  To: Apurva Nandan
  Cc: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
	Mark Brown, Patrice Chotard, Christophe Kerello, linux-mtd,
	linux-kernel, linux-spi, p.yadav

On Tue, 12 Oct 2021 02:16:10 +0530
Apurva Nandan <a-nandan@ti.com> wrote:

> Manufacturers might use a variation of standard SPI NAND flash
> instructions, e.g. Winbond W35N01JW changes the dummy cycle length for
> read register commands when in Octal DTR mode.
> 
> Add new function in manufacturer_ops: adjust_op(), which can be called
> to correct the spi_mem op for any alteration in the instruction made by
> the manufacturers. And hence, this function can also be used for
> incorporating variations of SPI instructions in Octal DTR mode.

Okay, so now we have reg accesses differing between vendors in DTR.
Looks like we should have an OP-table for non-IO commands too (similar
to the op_variants mechanism we have for IO commands), at least when
operating in DTR. The reg access path would just end up doing:

	struct spi_mem_op op =
		spinand->templates[spinand->protocol][cmd];

This way you get rid of all this live-patching/vendor-specific
adjustment (which is likely to get more and more complex since flash
vendors have a tendency of diverging from the common behavior), and
everything gets initialized at probe time.

> 
> Datasheet: https://www.winbond.com/export/sites/winbond/datasheet/W35N01JW_Datasheet_Brief.pdf
> 
> Signed-off-by: Apurva Nandan <a-nandan@ti.com>
> ---
>  drivers/mtd/nand/spi/core.c | 3 +++
>  include/linux/mtd/spinand.h | 4 ++++
>  2 files changed, 7 insertions(+)
> 
> diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c
> index 4da794ae728d..8e6cf7941a0f 100644
> --- a/drivers/mtd/nand/spi/core.c
> +++ b/drivers/mtd/nand/spi/core.c
> @@ -61,6 +61,9 @@ static void spinand_patch_op(const struct spinand_device *spinand,
>  		op->data.buswidth = op_buswidth;
>  		op->data.dtr = op_is_dtr;
>  	}
> +
> +	if (spinand->manufacturer->ops->adjust_op)
> +		spinand->manufacturer->ops->adjust_op(op, spinand->reg_proto);
>  }
>  
>  static void spinand_patch_reg_op(const struct spinand_device *spinand,
> diff --git a/include/linux/mtd/spinand.h b/include/linux/mtd/spinand.h
> index f6093cd98d7b..ebb19b2cec84 100644
> --- a/include/linux/mtd/spinand.h
> +++ b/include/linux/mtd/spinand.h
> @@ -257,6 +257,8 @@ struct spinand_devid {
>  /**
>   * struct manufacurer_ops - SPI NAND manufacturer specific operations
>   * @init: initialize a SPI NAND device
> + * @adjust_op: modify the ops for any variation in their cmd, address, dummy or
> + *	       data phase by the manufacturer
>   * @cleanup: cleanup a SPI NAND device
>   *
>   * Each SPI NAND manufacturer driver should implement this interface so that
> @@ -264,6 +266,8 @@ struct spinand_devid {
>   */
>  struct spinand_manufacturer_ops {
>  	int (*init)(struct spinand_device *spinand);
> +	void (*adjust_op)(struct spi_mem_op *op,
> +			  const enum spinand_proto reg_proto);
>  	void (*cleanup)(struct spinand_device *spinand);
>  };
>  


^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH v2 07/14] mtd: spinand: Allow enabling Octal DTR mode in the core
  2021-10-11 20:46 ` [PATCH v2 07/14] mtd: spinand: Allow enabling Octal DTR mode in the core Apurva Nandan
@ 2021-10-12  7:14   ` Boris Brezillon
  0 siblings, 0 replies; 20+ messages in thread
From: Boris Brezillon @ 2021-10-12  7:14 UTC (permalink / raw)
  To: Apurva Nandan
  Cc: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
	Mark Brown, Patrice Chotard, Christophe Kerello, linux-mtd,
	linux-kernel, linux-spi, p.yadav

On Tue, 12 Oct 2021 02:16:12 +0530
Apurva Nandan <a-nandan@ti.com> wrote:

> Enable Octal DTR SPI mode, i.e. 8D-8D-8D mode, if the SPI NAND flash
> device supports it. Mixed OSPI (1S-1S-8S & 1S-8S-8S), mixed DTR modes
> (1S-1D-8D), etc. aren't supported yet.
> 
> The method to switch to Octal DTR SPI mode may vary across
> manufacturers. For example, for Winbond, it is enabled by writing
> values to the volatile configuration register. So, let the
> manufacturer's code have their own implementation for switching to
> Octal DTR SPI mode.
> 
> Check for the SPI NAND device's support for Octal DTR mode using
> spinand flags, and if the op_templates allows 8D-8D-8D, call
> octal_dtr_enable() manufacturer op. If the SPI controller doesn't
> supports these modes, the selected op_templates will prevent switching
> to the Octal DTR mode. And finally update the spinand reg_proto
> on success.
> 
> Signed-off-by: Apurva Nandan <a-nandan@ti.com>
> ---
>  drivers/mtd/nand/spi/core.c | 46 +++++++++++++++++++++++++++++++++++++
>  include/linux/mtd/spinand.h |  3 +++
>  2 files changed, 49 insertions(+)
> 
> diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c
> index 8e6cf7941a0f..1210946f8447 100644
> --- a/drivers/mtd/nand/spi/core.c
> +++ b/drivers/mtd/nand/spi/core.c
> @@ -257,6 +257,48 @@ static int spinand_init_quad_enable(struct spinand_device *spinand)
>  			       enable ? CFG_QUAD_ENABLE : 0);
>  }
>  
> +static bool spinand_op_is_octal_dtr(const struct spi_mem_op *op)
> +{
> +	return  op->cmd.buswidth == 8 && op->cmd.dtr &&
> +		op->addr.buswidth == 8 && op->addr.dtr &&
> +		op->data.buswidth == 8 && op->data.dtr;
> +}
> +
> +static int spinand_init_octal_dtr_enable(struct spinand_device *spinand)

I see no spinand_octal_dtr_disable(), and I feel like we'll want to get
back to single-STR mode just before rebooting/kexec-ing, or if/when
we need to execute other maintenance operations. Actually, I think we
should have something more generic to enter a new mode (see below
for a detailed explanation).

> +{
> +	struct device *dev = &spinand->spimem->spi->dev;
> +	int ret;
> +
> +	if (!(spinand->flags & SPINAND_HAS_OCTAL_DTR_BIT))
> +		return 0;
> +
> +	if (!(spinand_op_is_octal_dtr(spinand->op_templates.read_cache) &&
> +	      spinand_op_is_octal_dtr(spinand->op_templates.write_cache) &&
> +	      spinand_op_is_octal_dtr(spinand->op_templates.update_cache)))
> +		return 0;

Ok, so you check the controller-side octal-DTR capability when selecting
the read/write/update_cache variants, but what if other commands (reg
access, erase, ...) can't be issued in this mode? I really think all
the commands you might use should be tested at probe time, not just
these 3 operations.

> +
> +	if (!spinand->manufacturer->ops->octal_dtr_enable) {
> +		dev_dbg(dev,
> +			"Missing ->octal_dtr_enable(), unable to switch mode\n");
> +		return -EINVAL;
> +	}
> +
> +	ret = spinand->manufacturer->ops->octal_dtr_enable(spinand);
> +	if (ret) {
> +		dev_err(dev,
> +			"Failed to enable Octal DTR SPI mode (err = %d)\n",
> +			ret);
> +		return ret;
> +	}
> +
> +	spinand->reg_proto = SPINAND_OCTAL_DTR;
> +
> +	dev_dbg(dev,
> +		"%s SPI NAND switched to Octal DTR SPI (8D-8D-8D) mode\n",
> +		spinand->manufacturer->name);
> +	return 0;
> +}
> +
>  static int spinand_ecc_enable(struct spinand_device *spinand,
>  			      bool enable)
>  {
> @@ -1192,6 +1234,10 @@ static int spinand_init_flash(struct spinand_device *spinand)
>  	if (ret)
>  		return ret;
>  
> +	ret = spinand_init_octal_dtr_enable(spinand);
> +	if (ret)
> +		return ret;
> +

Why not delaying the 'enter fastest available mode' at the end of this
initialization? I fear some flashes won't support some of the
maintenance commands in 8-8-8-DTR, so it's probably safer to enter it
once you've initialized everything else.

>  	ret = spinand_upd_cfg(spinand, CFG_OTP_ENABLE, 0);
>  	if (ret)
>  		return ret;
> diff --git a/include/linux/mtd/spinand.h b/include/linux/mtd/spinand.h
> index 35816b8cfe81..daa2ac5c3110 100644
> --- a/include/linux/mtd/spinand.h
> +++ b/include/linux/mtd/spinand.h
> @@ -271,6 +271,7 @@ struct spinand_devid {
>   * @init: initialize a SPI NAND device
>   * @adjust_op: modify the ops for any variation in their cmd, address, dummy or
>   *	       data phase by the manufacturer
> + * @octal_dtr_enable: switch the SPI NAND flash into Octal DTR SPI mode
>   * @cleanup: cleanup a SPI NAND device
>   *
>   * Each SPI NAND manufacturer driver should implement this interface so that
> @@ -280,6 +281,7 @@ struct spinand_manufacturer_ops {
>  	int (*init)(struct spinand_device *spinand);
>  	void (*adjust_op)(struct spi_mem_op *op,
>  			  const enum spinand_proto reg_proto);
> +	int (*octal_dtr_enable)(struct spinand_device *spinand);

I'd probably opt for a more generic name and pass the spinand_protocol
you want to enter in:

	int (*change_mode)(struct spinand_device *spinand,
			   enum spinand_protocol proto);

This way we can get back to 1-1-1-STR if we have to, and we can also
easily extend the logic to support 4-4-4-STR and 8-8-8-STR, which,
IIRC, are a thing (at least they exist on SPI NORs).

>  	void (*cleanup)(struct spinand_device *spinand);
>  };
>  
> @@ -348,6 +350,7 @@ struct spinand_ecc_info {
>  
>  #define SPINAND_HAS_QE_BIT		BIT(0)
>  #define SPINAND_HAS_CR_FEAT_BIT		BIT(1)
> +#define SPINAND_HAS_OCTAL_DTR_BIT	BIT(2)
>  
>  /**
>   * struct spinand_ondie_ecc_conf - private SPI-NAND on-die ECC engine structure


^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH v2 11/14] mtd: spinand: Perform Power-on-Reset on the flash in mtd_suspend()
  2021-10-11 20:46 ` [PATCH v2 11/14] mtd: spinand: Perform Power-on-Reset on the flash in mtd_suspend() Apurva Nandan
@ 2021-10-12  7:25   ` Boris Brezillon
  0 siblings, 0 replies; 20+ messages in thread
From: Boris Brezillon @ 2021-10-12  7:25 UTC (permalink / raw)
  To: Apurva Nandan
  Cc: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
	Mark Brown, Patrice Chotard, Christophe Kerello, linux-mtd,
	linux-kernel, linux-spi, p.yadav

On Tue, 12 Oct 2021 02:16:16 +0530
Apurva Nandan <a-nandan@ti.com> wrote:

> A soft reset using FFh command doesn't erase the flash's configuration
> and doesn't reset the SPI IO mode also. This can result in the flash
> being in a different SPI IO mode, e.g. Octal DTR, when resuming from
> sleep. This could put the flash in an unrecognized SPI IO mode, making
> it unusable.
> 
> Perform a Power-on-Reset (PoR), if available in the flash, when
> performing mtd_suspend(). This would set the flash to clean
> state for reinitialization during resume and would also ensure that it
> is in standard SPI IO mode (1S-1S-1S) before the resume begins.
> 
> Signed-off-by: Apurva Nandan <a-nandan@ti.com>
> ---
>  drivers/mtd/nand/spi/core.c | 21 +++++++++++++++++++++
>  1 file changed, 21 insertions(+)
> 
> diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c
> index 9b570570ee81..60408531979a 100644
> --- a/drivers/mtd/nand/spi/core.c
> +++ b/drivers/mtd/nand/spi/core.c
> @@ -1316,6 +1316,11 @@ static void spinand_mtd_resume(struct mtd_info *mtd)
>  	int ret;
>  
>  	spinand->reg_proto = SPINAND_SINGLE_STR;
> +	/*
> +	 * PoR Reset (if available by the manufacturer) is performed at the suspend
> +	 * time. Hence, those flashes remain in power-on-state at this point, in a
> +	 * standard SPI IO mode. So, now the core unanimously performs a soft reset.
> +	 */
>  	ret = spinand_reset_op(spinand);
>  	if (ret)
>  		return;
> @@ -1327,6 +1332,21 @@ 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;
> +
> +	if (!(spinand->flags & SPINAND_HAS_POR_CMD_BIT))
> +		return 0;
> +
> +	ret = spinand_power_on_rst_op(spinand);
> +	if (ret)
> +		dev_err(&spinand->spimem->spi->dev, "suspend() failed\n");
> +
> +	return ret;
> +}

I suspect you need to implement the spi_mem_driver.shutdown() method
and do a PoR in that case too. If the device doesn't support the PoR
command, we should at least switch back to the 1-1-1-STR mode manually.

> +
>  static int spinand_init(struct spinand_device *spinand)
>  {
>  	struct device *dev = &spinand->spimem->spi->dev;
> @@ -1399,6 +1419,7 @@ static int spinand_init(struct spinand_device *spinand)
>  	mtd->_erase = spinand_mtd_erase;
>  	mtd->_max_bad_blocks = nanddev_mtd_max_bad_blocks;
>  	mtd->_resume = spinand_mtd_resume;
> +	mtd->_suspend = spinand_mtd_suspend;
>  
>  	if (nand->ecc.engine) {
>  		ret = mtd_ooblayout_count_freebytes(mtd);


^ permalink raw reply	[flat|nested] 20+ messages in thread

end of thread, other threads:[~2021-10-12  7:25 UTC | newest]

Thread overview: 20+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-10-11 20:46 [PATCH v2 00/14] mtd: spinand: Add Octal DTR SPI (8D-8D-8D) mode support Apurva Nandan
2021-10-11 20:46 ` [PATCH v2 01/14] spi: spi-mem: Add DTR templates for cmd, address, dummy and data phase Apurva Nandan
2021-10-11 20:46 ` [PATCH v2 02/14] mtd: spinand: Add enum spinand_proto to indicate current SPI IO mode Apurva Nandan
2021-10-12  6:39   ` Boris Brezillon
2021-10-11 20:46 ` [PATCH v2 03/14] mtd: spinand: Patch spi_mem_op for the SPI IO protocol using reg_proto Apurva Nandan
2021-10-12  6:40   ` Boris Brezillon
2021-10-11 20:46 ` [PATCH v2 04/14] mtd: spinand: Fix odd byte addr and data phase in read and write reg op for Octal DTR mode Apurva Nandan
2021-10-11 20:46 ` [PATCH v2 05/14] mtd: spinand: Add adjust_op() in manufacturer_ops to modify the ops for manufacturer specific changes Apurva Nandan
2021-10-12  6:54   ` Boris Brezillon
2021-10-11 20:46 ` [PATCH v2 06/14] mtd: spinand: Add macros for Octal DTR page read and write operations Apurva Nandan
2021-10-11 20:46 ` [PATCH v2 07/14] mtd: spinand: Allow enabling Octal DTR mode in the core Apurva Nandan
2021-10-12  7:14   ` Boris Brezillon
2021-10-11 20:46 ` [PATCH v2 08/14] mtd: spinand: winbond: Add support for write volatile configuration register op Apurva Nandan
2021-10-11 20:46 ` [PATCH v2 09/14] mtd: spinand: winbond: Add octal_dtr_enable() for manufacturer_ops Apurva Nandan
2021-10-11 20:46 ` [PATCH v2 10/14] mtd: spinand: Add support for Power-on-Reset (PoR) instruction Apurva Nandan
2021-10-11 20:46 ` [PATCH v2 11/14] mtd: spinand: Perform Power-on-Reset on the flash in mtd_suspend() Apurva Nandan
2021-10-12  7:25   ` Boris Brezillon
2021-10-11 20:46 ` [PATCH v2 12/14] mtd: spinand: Add adjust_op() in Winbond manufacturer_ops Apurva Nandan
2021-10-11 20:46 ` [PATCH v2 13/14] mtd: spinand: winbond: Rename cache op_variants struct variable Apurva Nandan
2021-10-11 20:46 ` [PATCH v2 14/14] mtd: spinand: winbond: Add support for Winbond W35N01JW SPI NAND flash Apurva Nandan

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).