2020-05-19 14:29:05

by Pratyush Yadav

[permalink] [raw]
Subject: [PATCH v5 00/19] mtd: spi-nor: add xSPI Octal DTR support

Hi,

This series adds support for octal DTR flashes in the spi-nor framework,
and then adds hooks for the Cypress Semper and Mircom Xcella flashes to
allow running them in octal DTR mode. This series assumes that the flash
is handed to the kernel in Legacy SPI mode.

Tested on TI J721e EVM with 1-bit ECC on the Cypress flash.

Changes in v5:
- Do not enable stateful X-X-X modes if the reset line is broken.

- Instead of setting SNOR_READ_HWCAPS_8_8_8_DTR from Profile 1.0 table
parsing, do it in spi_nor_info_init_params() instead based on the
SPI_NOR_OCTAL_DTR_READ flag instead.

- Set SNOR_HWCAPS_PP_8_8_8_DTR in s28hs post_sfdp hook since this
capability is no longer set in Profile 1.0 parsing.

- Instead of just checking for spi_nor_get_protocol_width() in
spi_nor_octal_dtr_enable(), make sure the protocol is
SNOR_PROTO_8_8_8_DTR since get_protocol_width() only cares about data
width.

- Drop flag SPI_NOR_SOFT_RESET. Instead, discover soft reset capability
via BFPT.

- Do not make an invalid Quad Enable BFPT field a fatal error. Silently
ignore it by assuming no quad enable bit is present.

- Set dummy cycles for Cypress Semper flash to 24 instead of 20. This
allows for 200MHz operation in 8D mode compared to the 166MHz with 20.

- Rename spi_nor_cypress_octal_enable() to
spi_nor_cypress_octal_dtr_enable().

- Update spi-mtk-nor.c to reject DTR ops since it doesn't call
spi_mem_default_supports_op().

Changes in v4:
- Refactor the series to use the new spi-nor framework with the
manufacturer-specific bits separated from the core.

- Add support for Micron MT35XU512ABA.

- Use cmd.nbytes as the criteria of whether the data phase exists or not
instead of cmd.buf.in || cmd.buf.out in spi_nor_spimem_setup_op().

- Update Read FSR to use the same dummy cycles and address width as Read
SR.

- Fix BFPT parsing stopping too early for JESD216 rev B flashes.

- Use 2 byte reads for Read SR and FSR commands in DTR mode.

Changes in v3:
- Drop the DT properties "spi-rx-dtr" and "spi-tx-dtr". Instead, if
later a need is felt to disable DTR in case someone has a board with
Octal DTR capable flash but does not support DTR transactions for some
reason, a property like "spi-no-dtr" can be added.

- Remove mode bits SPI_RX_DTR and SPI_TX_DTR.

- Remove the Cadence Quadspi controller patch to un-block this series. I
will submit it as a separate patch.

- Rebase on latest 'master' and fix merge conflicts.

- Update read and write dirmap templates to use DTR.

- Rename 'is_dtr' to 'dtr'.

- Make 'dtr' a bitfield.

- Reject DTR ops in spi_mem_default_supports_op().

- Update atmel-quadspi to reject DTR ops. All other controller drivers
call spi_mem_default_supports_op() so they will automatically reject
DTR ops.

- Add support for both enabling and disabling DTR modes.

- Perform a Software Reset on flashes that support it when shutting
down.

- Disable Octal DTR mode on suspend, and re-enable it on resume.

- Drop enum 'spi_mem_cmd_ext' and make command opcode u16 instead.
Update spi-nor to use the 2-byte command instead of the command
extension. Since we still need a "extension type", mode that enum to
spi-nor and name it 'spi_nor_cmd_ext'.

- Default variable address width to 3 to fix SMPT parsing.

- Drop non-volatile change to uniform sector mode and rely on parsing
SMPT.

Changes in v2:
- Add DT properties "spi-rx-dtr" and "spi-tx-dtr" to allow expressing
DTR capabilities.

- Set the mode bits SPI_RX_DTR and SPI_TX_DTR when we discover the DT
properties "spi-rx-dtr" and spi-tx-dtr".

- spi_nor_cypress_octal_enable() was updating nor->params.read[] with
the intention of setting the correct number of dummy cycles. But this
function is called _after_ selecting the read so setting
nor->params.read[] will have no effect. So, update nor->read_dummy
directly.

- Fix spi_nor_spimem_check_readop() and spi_nor_spimem_check_pp()
passing nor->read_proto and nor->write_proto to
spi_nor_spimem_setup_op() instead of read->proto and pp->proto
respectively.

- Move the call to cqspi_setup_opcode_ext() inside cqspi_enable_dtr().
This avoids repeating the 'if (f_pdata->is_dtr)
cqspi_setup_opcode_ext()...` snippet multiple times.

- Call the default 'supports_op()' from cqspi_supports_mem_op(). This
makes sure the buswidth requirements are also enforced along with the
DTR requirements.

- Drop the 'is_dtr' argument from spi_check_dtr_req(). We only call it
when a phase is DTR so it is redundant.

Pratyush Yadav (19):
spi: spi-mem: allow specifying whether an op is DTR or not
spi: atmel-quadspi: reject DTR ops
spi: spi-mtk-nor: reject DTR ops
spi: spi-mem: allow specifying a command's extension
mtd: spi-nor: add support for DTR protocol
mtd: spi-nor: sfdp: default to addr_width of 3 for configurable widths
mtd: spi-nor: sfdp: prepare BFPT parsing for JESD216 rev D
mtd: spi-nor: sfdp: get command opcode extension type from BFPT
mtd: spi-nor: sfdp: parse xSPI Profile 1.0 table
mtd: spi-nor: core: use dummy cycle and address width info from SFDP
mtd: spi-nor: core: do 2 byte reads for SR and FSR in DTR mode
mtd: spi-nor: core: enable octal DTR mode when possible
mtd: spi-nor: sfdp: do not make invalid quad enable fatal
mtd: spi-nor: sfdp: detect Soft Reset sequence support from BFPT
mtd: spi-nor: core: perform a Soft Reset on shutdown
mtd: spi-nor: core: disable Octal DTR mode on suspend.
mtd: spi-nor: core: expose spi_nor_default_setup() in core.h
mtd: spi-nor: spansion: add support for Cypress Semper flash
mtd: spi-nor: micron-st: allow using MT35XU512ABA in Octal DTR mode

drivers/mtd/spi-nor/core.c | 446 +++++++++++++++++++++++++++-----
drivers/mtd/spi-nor/core.h | 22 ++
drivers/mtd/spi-nor/micron-st.c | 112 +++++++-
drivers/mtd/spi-nor/sfdp.c | 117 ++++++++-
drivers/mtd/spi-nor/sfdp.h | 13 +-
drivers/mtd/spi-nor/spansion.c | 167 ++++++++++++
drivers/spi/atmel-quadspi.c | 4 +
drivers/spi/spi-mem.c | 3 +
drivers/spi/spi-mtk-nor.c | 4 +
include/linux/mtd/spi-nor.h | 53 +++-
include/linux/spi/spi-mem.h | 13 +-
11 files changed, 862 insertions(+), 92 deletions(-)

--
2.26.2


2020-05-19 14:29:10

by Pratyush Yadav

[permalink] [raw]
Subject: [PATCH v5 01/19] spi: spi-mem: allow specifying whether an op is DTR or not

Each phase is given a separate 'dtr' field so mixed protocols like
4S-4D-4D can be supported.

Signed-off-by: Pratyush Yadav <[email protected]>
---
drivers/spi/spi-mem.c | 3 +++
include/linux/spi/spi-mem.h | 8 ++++++++
2 files changed, 11 insertions(+)

diff --git a/drivers/spi/spi-mem.c b/drivers/spi/spi-mem.c
index 9a86cc27fcc0..93e255287ab9 100644
--- a/drivers/spi/spi-mem.c
+++ b/drivers/spi/spi-mem.c
@@ -156,6 +156,9 @@ bool spi_mem_default_supports_op(struct spi_mem *mem,
op->data.dir == SPI_MEM_DATA_OUT))
return false;

+ if (op->cmd.dtr || op->addr.dtr || op->dummy.dtr || op->data.dtr)
+ return false;
+
return true;
}
EXPORT_SYMBOL_GPL(spi_mem_default_supports_op);
diff --git a/include/linux/spi/spi-mem.h b/include/linux/spi/spi-mem.h
index af9ff2f0f1b2..e3dcb956bf61 100644
--- a/include/linux/spi/spi-mem.h
+++ b/include/linux/spi/spi-mem.h
@@ -71,9 +71,11 @@ enum spi_mem_data_dir {
* struct spi_mem_op - describes a SPI memory operation
* @cmd.buswidth: number of IO lines used to transmit the command
* @cmd.opcode: operation opcode
+ * @cmd.dtr: whether the command opcode should be sent in DTR mode or not
* @addr.nbytes: number of address bytes to send. Can be zero if the operation
* does not need to send an address
* @addr.buswidth: number of IO lines used to transmit the address cycles
+ * @addr.dtr: whether the address should be sent in DTR mode or not
* @addr.val: address value. This value is always sent MSB first on the bus.
* Note that only @addr.nbytes are taken into account in this
* address value, so users should make sure the value fits in the
@@ -81,7 +83,9 @@ enum spi_mem_data_dir {
* @dummy.nbytes: number of dummy bytes to send after an opcode or address. Can
* be zero if the operation does not require dummy bytes
* @dummy.buswidth: number of IO lanes used to transmit the dummy bytes
+ * @dummy.dtr: whether the dummy bytes should be sent in DTR mode or not
* @data.buswidth: number of IO lanes used to send/receive the data
+ * @data.dtr: whether the data should be sent in DTR mode or not
* @data.dir: direction of the transfer
* @data.nbytes: number of data bytes to send/receive. Can be zero if the
* operation does not involve transferring data
@@ -91,22 +95,26 @@ enum spi_mem_data_dir {
struct spi_mem_op {
struct {
u8 buswidth;
+ u8 dtr : 1;
u8 opcode;
} cmd;

struct {
u8 nbytes;
u8 buswidth;
+ u8 dtr : 1;
u64 val;
} addr;

struct {
u8 nbytes;
u8 buswidth;
+ u8 dtr : 1;
} dummy;

struct {
u8 buswidth;
+ u8 dtr : 1;
enum spi_mem_data_dir dir;
unsigned int nbytes;
union {
--
2.26.2

2020-05-19 14:29:13

by Pratyush Yadav

[permalink] [raw]
Subject: [PATCH v5 04/19] spi: spi-mem: allow specifying a command's extension

In xSPI mode, flashes expect 2-byte opcodes. The second byte is called
the "command extension". There can be 3 types of extensions in xSPI:
repeat, invert, and hex. When the extension type is "repeat", the same
opcode is sent twice. When it is "invert", the second byte is the
inverse of the opcode. When it is "hex" an additional opcode byte based
is sent with the command whose value can be anything.

So, make opcode a 16-bit value and add a 'nbytes', similar to how
multiple address widths are handled.

Signed-off-by: Pratyush Yadav <[email protected]>
---
include/linux/spi/spi-mem.h | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/include/linux/spi/spi-mem.h b/include/linux/spi/spi-mem.h
index e3dcb956bf61..731bb64c6ba6 100644
--- a/include/linux/spi/spi-mem.h
+++ b/include/linux/spi/spi-mem.h
@@ -69,6 +69,8 @@ enum spi_mem_data_dir {

/**
* struct spi_mem_op - describes a SPI memory operation
+ * @cmd.nbytes: number of opcode bytes (only 1 or 2 are valid). The opcode is
+ * sent MSB-first.
* @cmd.buswidth: number of IO lines used to transmit the command
* @cmd.opcode: operation opcode
* @cmd.dtr: whether the command opcode should be sent in DTR mode or not
@@ -94,9 +96,10 @@ enum spi_mem_data_dir {
*/
struct spi_mem_op {
struct {
+ u8 nbytes;
u8 buswidth;
u8 dtr : 1;
- u8 opcode;
+ u16 opcode;
} cmd;

struct {
--
2.26.2

2020-05-19 14:29:42

by Pratyush Yadav

[permalink] [raw]
Subject: [PATCH v5 03/19] spi: spi-mtk-nor: reject DTR ops

Double Transfer Rate (DTR) ops are added in spi-mem. But this controller
doesn't support DTR transactions. Since we don't use the default
supports_op(), which rejects all DTR ops, do that explicitly in our
supports_op().

Signed-off-by: Pratyush Yadav <[email protected]>
---
drivers/spi/spi-mtk-nor.c | 4 ++++
1 file changed, 4 insertions(+)

diff --git a/drivers/spi/spi-mtk-nor.c b/drivers/spi/spi-mtk-nor.c
index 7bc302b50396..7015dccedf00 100644
--- a/drivers/spi/spi-mtk-nor.c
+++ b/drivers/spi/spi-mtk-nor.c
@@ -211,6 +211,10 @@ static bool mtk_nor_supports_op(struct spi_mem *mem,
if (op->cmd.buswidth != 1)
return false;

+ /* DTR ops not supported. */
+ if (op->cmd.dtr || op->addr.dtr || op->dummy.dtr || op->data.dtr)
+ return false;
+
if ((op->addr.nbytes == 3) || (op->addr.nbytes == 4)) {
if ((op->data.dir == SPI_MEM_DATA_IN) && mtk_nor_match_read(op))
return true;
--
2.26.2

2020-05-19 14:29:43

by Pratyush Yadav

[permalink] [raw]
Subject: [PATCH v5 02/19] spi: atmel-quadspi: reject DTR ops

Double Transfer Rate (DTR) ops are added in spi-mem. But this controller
doesn't support DTR transactions. Since we don't use the default
supports_op(), which rejects all DTR ops, do that explicitly in our
supports_op().

Signed-off-by: Pratyush Yadav <[email protected]>
---
drivers/spi/atmel-quadspi.c | 4 ++++
1 file changed, 4 insertions(+)

diff --git a/drivers/spi/atmel-quadspi.c b/drivers/spi/atmel-quadspi.c
index cb44d1e169aa..4a29fa7ebdac 100644
--- a/drivers/spi/atmel-quadspi.c
+++ b/drivers/spi/atmel-quadspi.c
@@ -285,6 +285,10 @@ static bool atmel_qspi_supports_op(struct spi_mem *mem,
op->dummy.nbytes == 0)
return false;

+ /* DTR ops not supported. */
+ if (op->cmd.dtr || op->addr.dtr || op->dummy.dtr || op->data.dtr)
+ return false;
+
return true;
}

--
2.26.2

2020-05-19 14:29:45

by Pratyush Yadav

[permalink] [raw]
Subject: [PATCH v5 05/19] mtd: spi-nor: add support for DTR protocol

Double Transfer Rate (DTR) is SPI protocol in which data is transferred
on each clock edge as opposed to on each clock cycle. Make
framework-level changes to allow supporting flashes in DTR mode.

Right now, mixed DTR modes are not supported. So, for example a mode
like 4S-4D-4D will not work. All phases need to be either DTR or STR.

Signed-off-by: Pratyush Yadav <[email protected]>
---
drivers/mtd/spi-nor/core.c | 305 ++++++++++++++++++++++++++++--------
drivers/mtd/spi-nor/core.h | 6 +
drivers/mtd/spi-nor/sfdp.c | 9 +-
include/linux/mtd/spi-nor.h | 51 ++++--
4 files changed, 295 insertions(+), 76 deletions(-)

diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c
index 1ab4386a099a..388e695e763f 100644
--- a/drivers/mtd/spi-nor/core.c
+++ b/drivers/mtd/spi-nor/core.c
@@ -40,6 +40,76 @@

#define SPI_NOR_MAX_ADDR_WIDTH 4

+/**
+ * spi_nor_get_cmd_ext() - Get the command opcode extension based on the
+ * extension type.
+ * @nor: pointer to a 'struct spi_nor'
+ * @op: pointer to the 'struct spi_mem_op' whose properties
+ * need to be initialized.
+ *
+ * Right now, only "repeat" and "invert" are supported.
+ *
+ * Return: The opcode extension.
+ */
+static u8 spi_nor_get_cmd_ext(const struct spi_nor *nor,
+ const struct spi_mem_op *op)
+{
+ switch (nor->cmd_ext_type) {
+ case SPI_NOR_EXT_INVERT:
+ return ~op->cmd.opcode;
+
+ case SPI_NOR_EXT_REPEAT:
+ return op->cmd.opcode;
+
+ default:
+ dev_err(nor->dev, "Unknown command extension type\n");
+ return 0;
+ }
+}
+
+/**
+ * spi_nor_spimem_setup_op() - Set up common properties of a spi-mem op.
+ * @nor: pointer to a 'struct spi_nor'
+ * @op: pointer to the 'struct spi_mem_op' whose properties
+ * need to be initialized.
+ * @proto: the protocol from which the properties need to be set.
+ */
+void spi_nor_spimem_setup_op(const struct spi_nor *nor,
+ struct spi_mem_op *op,
+ const enum spi_nor_protocol proto)
+{
+ u8 ext;
+
+ op->cmd.buswidth = spi_nor_get_protocol_inst_nbits(proto);
+
+ if (op->addr.nbytes)
+ op->addr.buswidth = spi_nor_get_protocol_addr_nbits(proto);
+
+ if (op->dummy.nbytes)
+ op->dummy.buswidth = spi_nor_get_protocol_addr_nbits(proto);
+
+ if (op->data.nbytes)
+ op->data.buswidth = spi_nor_get_protocol_data_nbits(proto);
+
+ if (spi_nor_protocol_is_dtr(proto)) {
+ /*
+ * spi-mem supports mixed DTR modes, but right now we can only
+ * have all phases either DTR or STR. IOW, spi-mem can have
+ * something like 4S-4D-4D, but spi-nor can't. So, set all 4
+ * phases to either DTR or STR.
+ */
+ op->cmd.dtr = op->addr.dtr = op->dummy.dtr
+ = op->data.dtr = true;
+
+ /* 2 bytes per clock cycle in DTR mode. */
+ op->dummy.nbytes *= 2;
+
+ ext = spi_nor_get_cmd_ext(nor, op);
+ op->cmd.opcode = (op->cmd.opcode << 8) | ext;
+ op->cmd.nbytes = 2;
+ }
+}
+
/**
* spi_nor_spimem_bounce() - check if a bounce buffer is needed for the data
* transfer
@@ -104,14 +174,12 @@ static ssize_t spi_nor_spimem_read_data(struct spi_nor *nor, loff_t from,
ssize_t nbytes;
int error;

- /* get transfer protocols. */
- op.cmd.buswidth = spi_nor_get_protocol_inst_nbits(nor->read_proto);
- op.addr.buswidth = spi_nor_get_protocol_addr_nbits(nor->read_proto);
- op.dummy.buswidth = op.addr.buswidth;
- op.data.buswidth = spi_nor_get_protocol_data_nbits(nor->read_proto);
+ spi_nor_spimem_setup_op(nor, &op, nor->read_proto);

/* convert the dummy cycles to the number of bytes */
op.dummy.nbytes = (nor->read_dummy * op.dummy.buswidth) / 8;
+ if (spi_nor_protocol_is_dtr(nor->read_proto))
+ op.dummy.nbytes *= 2;

usebouncebuf = spi_nor_spimem_bounce(nor, &op);

@@ -169,13 +237,11 @@ static ssize_t spi_nor_spimem_write_data(struct spi_nor *nor, loff_t to,
ssize_t nbytes;
int error;

- op.cmd.buswidth = spi_nor_get_protocol_inst_nbits(nor->write_proto);
- op.addr.buswidth = spi_nor_get_protocol_addr_nbits(nor->write_proto);
- op.data.buswidth = spi_nor_get_protocol_data_nbits(nor->write_proto);
-
if (nor->program_opcode == SPINOR_OP_AAI_WP && nor->sst_write_second)
op.addr.nbytes = 0;

+ spi_nor_spimem_setup_op(nor, &op, nor->write_proto);
+
if (spi_nor_spimem_bounce(nor, &op))
memcpy(nor->bouncebuf, buf, op.data.nbytes);

@@ -227,10 +293,16 @@ int spi_nor_write_enable(struct spi_nor *nor)
SPI_MEM_OP_NO_DUMMY,
SPI_MEM_OP_NO_DATA);

+ spi_nor_spimem_setup_op(nor, &op, nor->reg_proto);
+
ret = spi_mem_exec_op(nor->spimem, &op);
} else {
- ret = nor->controller_ops->write_reg(nor, SPINOR_OP_WREN,
- NULL, 0);
+ if (spi_nor_protocol_is_dtr(nor->reg_proto))
+ ret = -ENOTSUPP;
+ else
+ ret = nor->controller_ops->write_reg(nor,
+ SPINOR_OP_WREN,
+ NULL, 0);
}

if (ret)
@@ -256,10 +328,16 @@ int spi_nor_write_disable(struct spi_nor *nor)
SPI_MEM_OP_NO_DUMMY,
SPI_MEM_OP_NO_DATA);

+ spi_nor_spimem_setup_op(nor, &op, nor->reg_proto);
+
ret = spi_mem_exec_op(nor->spimem, &op);
} else {
- ret = nor->controller_ops->write_reg(nor, SPINOR_OP_WRDI,
- NULL, 0);
+ if (spi_nor_protocol_is_dtr(nor->reg_proto))
+ ret = -ENOTSUPP;
+ else
+ ret = nor->controller_ops->write_reg(nor,
+ SPINOR_OP_WRDI,
+ NULL, 0);
}

if (ret)
@@ -318,10 +396,15 @@ static int spi_nor_read_fsr(struct spi_nor *nor, u8 *fsr)
SPI_MEM_OP_NO_DUMMY,
SPI_MEM_OP_DATA_IN(1, fsr, 1));

+ spi_nor_spimem_setup_op(nor, &op, nor->reg_proto);
+
ret = spi_mem_exec_op(nor->spimem, &op);
} else {
- ret = nor->controller_ops->read_reg(nor, SPINOR_OP_RDFSR,
- fsr, 1);
+ if (spi_nor_protocol_is_dtr(nor->reg_proto))
+ ret = -ENOTSUPP;
+ else
+ ret = nor->controller_ops->read_reg(nor, SPINOR_OP_RDFSR,
+ fsr, 1);
}

if (ret)
@@ -350,9 +433,15 @@ static int spi_nor_read_cr(struct spi_nor *nor, u8 *cr)
SPI_MEM_OP_NO_DUMMY,
SPI_MEM_OP_DATA_IN(1, cr, 1));

+ spi_nor_spimem_setup_op(nor, &op, nor->reg_proto);
+
ret = spi_mem_exec_op(nor->spimem, &op);
} else {
- ret = nor->controller_ops->read_reg(nor, SPINOR_OP_RDCR, cr, 1);
+ if (spi_nor_protocol_is_dtr(nor->reg_proto))
+ ret = -ENOTSUPP;
+ else
+ ret = nor->controller_ops->read_reg(nor, SPINOR_OP_RDCR,
+ cr, 1);
}

if (ret)
@@ -383,12 +472,17 @@ int spi_nor_set_4byte_addr_mode(struct spi_nor *nor, bool enable)
SPI_MEM_OP_NO_DUMMY,
SPI_MEM_OP_NO_DATA);

+ spi_nor_spimem_setup_op(nor, &op, nor->reg_proto);
+
ret = spi_mem_exec_op(nor->spimem, &op);
} else {
- ret = nor->controller_ops->write_reg(nor,
- enable ? SPINOR_OP_EN4B :
- SPINOR_OP_EX4B,
- NULL, 0);
+ if (spi_nor_protocol_is_dtr(nor->reg_proto))
+ ret = -ENOTSUPP;
+ else
+ ret = nor->controller_ops->write_reg(nor,
+ enable ? SPINOR_OP_EN4B :
+ SPINOR_OP_EX4B,
+ NULL, 0);
}

if (ret)
@@ -419,10 +513,15 @@ static int spansion_set_4byte_addr_mode(struct spi_nor *nor, bool enable)
SPI_MEM_OP_NO_DUMMY,
SPI_MEM_OP_DATA_OUT(1, nor->bouncebuf, 1));

+ spi_nor_spimem_setup_op(nor, &op, nor->reg_proto);
+
ret = spi_mem_exec_op(nor->spimem, &op);
} else {
- ret = nor->controller_ops->write_reg(nor, SPINOR_OP_BRWR,
- nor->bouncebuf, 1);
+ if (spi_nor_protocol_is_dtr(nor->reg_proto))
+ ret = -ENOTSUPP;
+ else
+ ret = nor->controller_ops->write_reg(nor, SPINOR_OP_BRWR,
+ nor->bouncebuf, 1);
}

if (ret)
@@ -451,10 +550,16 @@ int spi_nor_write_ear(struct spi_nor *nor, u8 ear)
SPI_MEM_OP_NO_DUMMY,
SPI_MEM_OP_DATA_OUT(1, nor->bouncebuf, 1));

+ spi_nor_spimem_setup_op(nor, &op, nor->reg_proto);
+
ret = spi_mem_exec_op(nor->spimem, &op);
} else {
- ret = nor->controller_ops->write_reg(nor, SPINOR_OP_WREAR,
- nor->bouncebuf, 1);
+ if (spi_nor_protocol_is_dtr(nor->reg_proto))
+ ret = -ENOTSUPP;
+ else
+ ret = nor->controller_ops->write_reg(nor,
+ SPINOR_OP_WREAR,
+ nor->bouncebuf, 1);
}

if (ret)
@@ -482,10 +587,16 @@ int spi_nor_xread_sr(struct spi_nor *nor, u8 *sr)
SPI_MEM_OP_NO_DUMMY,
SPI_MEM_OP_DATA_IN(1, sr, 1));

+ spi_nor_spimem_setup_op(nor, &op, nor->reg_proto);
+
ret = spi_mem_exec_op(nor->spimem, &op);
} else {
- ret = nor->controller_ops->read_reg(nor, SPINOR_OP_XRDSR,
- sr, 1);
+ if (spi_nor_protocol_is_dtr(nor->reg_proto))
+ ret = -ENOTSUPP;
+ else
+ ret = nor->controller_ops->read_reg(nor,
+ SPINOR_OP_XRDSR,
+ sr, 1);
}

if (ret)
@@ -527,10 +638,16 @@ static void spi_nor_clear_sr(struct spi_nor *nor)
SPI_MEM_OP_NO_DUMMY,
SPI_MEM_OP_NO_DATA);

+ spi_nor_spimem_setup_op(nor, &op, nor->reg_proto);
+
ret = spi_mem_exec_op(nor->spimem, &op);
} else {
- ret = nor->controller_ops->write_reg(nor, SPINOR_OP_CLSR,
- NULL, 0);
+ if (spi_nor_protocol_is_dtr(nor->reg_proto))
+ ret = -ENOTSUPP;
+ else
+ ret = nor->controller_ops->write_reg(nor,
+ SPINOR_OP_CLSR,
+ NULL, 0);
}

if (ret)
@@ -591,10 +708,16 @@ static void spi_nor_clear_fsr(struct spi_nor *nor)
SPI_MEM_OP_NO_DUMMY,
SPI_MEM_OP_NO_DATA);

+ spi_nor_spimem_setup_op(nor, &op, nor->reg_proto);
+
ret = spi_mem_exec_op(nor->spimem, &op);
} else {
- ret = nor->controller_ops->write_reg(nor, SPINOR_OP_CLFSR,
- NULL, 0);
+ if (spi_nor_protocol_is_dtr(nor->reg_proto))
+ ret = -ENOTSUPP;
+ else
+ ret = nor->controller_ops->write_reg(nor,
+ SPINOR_OP_CLFSR,
+ NULL, 0);
}

if (ret)
@@ -735,10 +858,16 @@ static int spi_nor_write_sr(struct spi_nor *nor, const u8 *sr, size_t len)
SPI_MEM_OP_NO_DUMMY,
SPI_MEM_OP_DATA_OUT(len, sr, 1));

+ spi_nor_spimem_setup_op(nor, &op, nor->reg_proto);
+
ret = spi_mem_exec_op(nor->spimem, &op);
} else {
- ret = nor->controller_ops->write_reg(nor, SPINOR_OP_WRSR,
- sr, len);
+ if (spi_nor_protocol_is_dtr(nor->reg_proto))
+ ret = -ENOTSUPP;
+ else
+ ret = nor->controller_ops->write_reg(nor,
+ SPINOR_OP_WRSR,
+ sr, len);
}

if (ret) {
@@ -937,10 +1066,16 @@ static int spi_nor_write_sr2(struct spi_nor *nor, const u8 *sr2)
SPI_MEM_OP_NO_DUMMY,
SPI_MEM_OP_DATA_OUT(1, sr2, 1));

+ spi_nor_spimem_setup_op(nor, &op, nor->reg_proto);
+
ret = spi_mem_exec_op(nor->spimem, &op);
} else {
- ret = nor->controller_ops->write_reg(nor, SPINOR_OP_WRSR2,
- sr2, 1);
+ if (spi_nor_protocol_is_dtr(nor->reg_proto))
+ ret = -ENOTSUPP;
+ else
+ ret = nor->controller_ops->write_reg(nor,
+ SPINOR_OP_WRSR2,
+ sr2, 1);
}

if (ret) {
@@ -971,10 +1106,16 @@ static int spi_nor_read_sr2(struct spi_nor *nor, u8 *sr2)
SPI_MEM_OP_NO_DUMMY,
SPI_MEM_OP_DATA_IN(1, sr2, 1));

+ spi_nor_spimem_setup_op(nor, &op, nor->reg_proto);
+
ret = spi_mem_exec_op(nor->spimem, &op);
} else {
- ret = nor->controller_ops->read_reg(nor, SPINOR_OP_RDSR2,
- sr2, 1);
+ if (spi_nor_protocol_is_dtr(nor->reg_proto))
+ ret = -ENOTSUPP;
+ else
+ ret = nor->controller_ops->read_reg(nor,
+ SPINOR_OP_RDSR2,
+ sr2, 1);
}

if (ret)
@@ -1002,10 +1143,16 @@ static int spi_nor_erase_chip(struct spi_nor *nor)
SPI_MEM_OP_NO_DUMMY,
SPI_MEM_OP_NO_DATA);

+ spi_nor_spimem_setup_op(nor, &op, nor->write_proto);
+
ret = spi_mem_exec_op(nor->spimem, &op);
} else {
- ret = nor->controller_ops->write_reg(nor, SPINOR_OP_CHIP_ERASE,
- NULL, 0);
+ if (spi_nor_protocol_is_dtr(nor->write_proto))
+ ret = -ENOTSUPP;
+ else
+ ret = nor->controller_ops->write_reg(nor,
+ SPINOR_OP_CHIP_ERASE,
+ NULL, 0);
}

if (ret)
@@ -1144,7 +1291,11 @@ static int spi_nor_erase_sector(struct spi_nor *nor, u32 addr)
SPI_MEM_OP_NO_DUMMY,
SPI_MEM_OP_NO_DATA);

+ spi_nor_spimem_setup_op(nor, &op, nor->write_proto);
+
return spi_mem_exec_op(nor->spimem, &op);
+ } else if (spi_nor_protocol_is_dtr(nor->write_proto)) {
+ return -ENOTSUPP;
} else if (nor->controller_ops->erase) {
return nor->controller_ops->erase(nor, addr);
}
@@ -2253,6 +2404,7 @@ int spi_nor_hwcaps_read2cmd(u32 hwcaps)
{ SNOR_HWCAPS_READ_1_8_8, SNOR_CMD_READ_1_8_8 },
{ SNOR_HWCAPS_READ_8_8_8, SNOR_CMD_READ_8_8_8 },
{ SNOR_HWCAPS_READ_1_8_8_DTR, SNOR_CMD_READ_1_8_8_DTR },
+ { SNOR_HWCAPS_READ_8_8_8_DTR, SNOR_CMD_READ_8_8_8_DTR },
};

return spi_nor_hwcaps2cmd(hwcaps, hwcaps_read2cmd,
@@ -2269,6 +2421,7 @@ static int spi_nor_hwcaps_pp2cmd(u32 hwcaps)
{ SNOR_HWCAPS_PP_1_1_8, SNOR_CMD_PP_1_1_8 },
{ SNOR_HWCAPS_PP_1_8_8, SNOR_CMD_PP_1_8_8 },
{ SNOR_HWCAPS_PP_8_8_8, SNOR_CMD_PP_8_8_8 },
+ { SNOR_HWCAPS_PP_8_8_8_DTR, SNOR_CMD_PP_8_8_8_DTR },
};

return spi_nor_hwcaps2cmd(hwcaps, hwcaps_pp2cmd,
@@ -2322,13 +2475,11 @@ static int spi_nor_spimem_check_readop(struct spi_nor *nor,
SPI_MEM_OP_DUMMY(0, 1),
SPI_MEM_OP_DATA_IN(0, NULL, 1));

- op.cmd.buswidth = spi_nor_get_protocol_inst_nbits(read->proto);
- op.addr.buswidth = spi_nor_get_protocol_addr_nbits(read->proto);
- op.data.buswidth = spi_nor_get_protocol_data_nbits(read->proto);
- op.dummy.buswidth = op.addr.buswidth;
op.dummy.nbytes = (read->num_mode_clocks + read->num_wait_states) *
op.dummy.buswidth / 8;

+ spi_nor_spimem_setup_op(nor, &op, read->proto);
+
return spi_nor_spimem_check_op(nor, &op);
}

@@ -2348,9 +2499,7 @@ static int spi_nor_spimem_check_pp(struct spi_nor *nor,
SPI_MEM_OP_NO_DUMMY,
SPI_MEM_OP_DATA_OUT(0, NULL, 1));

- op.cmd.buswidth = spi_nor_get_protocol_inst_nbits(pp->proto);
- op.addr.buswidth = spi_nor_get_protocol_addr_nbits(pp->proto);
- op.data.buswidth = spi_nor_get_protocol_data_nbits(pp->proto);
+ spi_nor_spimem_setup_op(nor, &op, pp->proto);

return spi_nor_spimem_check_op(nor, &op);
}
@@ -2368,12 +2517,16 @@ spi_nor_spimem_adjust_hwcaps(struct spi_nor *nor, u32 *hwcaps)
struct spi_nor_flash_parameter *params = nor->params;
unsigned int cap;

- /* DTR modes are not supported yet, mask them all. */
- *hwcaps &= ~SNOR_HWCAPS_DTR;
-
/* X-X-X modes are not supported yet, mask them all. */
*hwcaps &= ~SNOR_HWCAPS_X_X_X;

+ /*
+ * If the reset line is broken, we do not want to enter a stateful
+ * mode.
+ */
+ if (nor->flags & SNOR_F_BROKEN_RESET)
+ *hwcaps &= ~(SNOR_HWCAPS_X_X_X | SNOR_HWCAPS_X_X_X_DTR);
+
for (cap = 0; cap < sizeof(*hwcaps) * BITS_PER_BYTE; cap++) {
int rdidx, ppidx;

@@ -2628,7 +2781,7 @@ static int spi_nor_default_setup(struct spi_nor *nor,
* controller directly implements the spi_nor interface.
* Yet another reason to switch to spi-mem.
*/
- ignored_mask = SNOR_HWCAPS_X_X_X;
+ ignored_mask = SNOR_HWCAPS_X_X_X | SNOR_HWCAPS_X_X_X_DTR;
if (shared_mask & ignored_mask) {
dev_dbg(nor->dev,
"SPI n-n-n protocols are not supported.\n");
@@ -2774,11 +2927,25 @@ static void spi_nor_info_init_params(struct spi_nor *nor)
SNOR_PROTO_1_1_8);
}

+ if (info->flags & SPI_NOR_OCTAL_DTR_READ) {
+ params->hwcaps.mask |= SNOR_HWCAPS_READ_8_8_8_DTR;
+ spi_nor_set_read_settings(&params->reads[SNOR_CMD_READ_8_8_8_DTR],
+ 0, 20, SPINOR_OP_READ_FAST,
+ SNOR_PROTO_8_8_8_DTR);
+ }
+
/* Page Program settings. */
params->hwcaps.mask |= SNOR_HWCAPS_PP;
spi_nor_set_pp_settings(&params->page_programs[SNOR_CMD_PP],
SPINOR_OP_PP, SNOR_PROTO_1_1_1);

+ /*
+ * Since xSPI Page Program opcode is backward compatible with
+ * Legacy SPI, use Legacy SPI opcode there as well.
+ */
+ spi_nor_set_pp_settings(&params->page_programs[SNOR_CMD_PP_8_8_8_DTR],
+ SPINOR_OP_PP, SNOR_PROTO_8_8_8_DTR);
+
/*
* Sector Erase settings. Sort Erase Types in ascending order, with the
* smallest erase size starting at BIT(0).
@@ -2886,7 +3053,8 @@ static int spi_nor_init_params(struct spi_nor *nor)

spi_nor_manufacturer_init_params(nor);

- if ((nor->info->flags & (SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ)) &&
+ if ((nor->info->flags & (SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ |
+ SPI_NOR_OCTAL_READ | SPI_NOR_OCTAL_DTR_READ)) &&
!(nor->info->flags & SPI_NOR_SKIP_SFDP))
spi_nor_sfdp_init_params(nor);

@@ -2948,7 +3116,9 @@ static int spi_nor_init(struct spi_nor *nor)
return err;
}

- if (nor->addr_width == 4 && !(nor->flags & SNOR_F_4B_OPCODES)) {
+ if (nor->addr_width == 4 &&
+ !(nor->info->flags & SPI_NOR_OCTAL_DTR_READ) &&
+ !(nor->flags & SNOR_F_4B_OPCODES)) {
/*
* If the RESET# pin isn't hooked up properly, or the system
* otherwise doesn't perform a reset command in the boot
@@ -3007,6 +3177,9 @@ static int spi_nor_set_addr_width(struct spi_nor *nor)
{
if (nor->addr_width) {
/* already configured from SFDP */
+ } else if (spi_nor_protocol_is_dtr(nor->read_proto)) {
+ /* Always use 4-byte addresses in DTR mode. */
+ nor->addr_width = 4;
} else if (nor->info->addr_width) {
nor->addr_width = nor->info->addr_width;
} else if (nor->mtd.size > 0x1000000) {
@@ -3244,14 +3417,19 @@ static int spi_nor_create_read_dirmap(struct spi_nor *nor)
};
struct spi_mem_op *op = &info.op_tmpl;

- /* get transfer protocols. */
- op->cmd.buswidth = spi_nor_get_protocol_inst_nbits(nor->read_proto);
- op->addr.buswidth = spi_nor_get_protocol_addr_nbits(nor->read_proto);
- op->dummy.buswidth = op->addr.buswidth;
- op->data.buswidth = spi_nor_get_protocol_data_nbits(nor->read_proto);
+ spi_nor_spimem_setup_op(nor, op, nor->read_proto);

/* convert the dummy cycles to the number of bytes */
op->dummy.nbytes = (nor->read_dummy * op->dummy.buswidth) / 8;
+ if (spi_nor_protocol_is_dtr(nor->read_proto))
+ op->dummy.nbytes *= 2;
+
+ /*
+ * Since spi_nor_spimem_setup_op() only sets buswidth when the number
+ * of data bytes is non-zero, the data buswidth won't be set here. So,
+ * do it explicitly.
+ */
+ op->data.buswidth = spi_nor_get_protocol_data_nbits(nor->read_proto);

nor->dirmap.rdesc = devm_spi_mem_dirmap_create(nor->dev, nor->spimem,
&info);
@@ -3270,15 +3448,18 @@ static int spi_nor_create_write_dirmap(struct spi_nor *nor)
};
struct spi_mem_op *op = &info.op_tmpl;

- /* get transfer protocols. */
- op->cmd.buswidth = spi_nor_get_protocol_inst_nbits(nor->write_proto);
- op->addr.buswidth = spi_nor_get_protocol_addr_nbits(nor->write_proto);
- op->dummy.buswidth = op->addr.buswidth;
- op->data.buswidth = spi_nor_get_protocol_data_nbits(nor->write_proto);
-
if (nor->program_opcode == SPINOR_OP_AAI_WP && nor->sst_write_second)
op->addr.nbytes = 0;

+ spi_nor_spimem_setup_op(nor, op, nor->write_proto);
+
+ /*
+ * Since spi_nor_spimem_setup_op() only sets buswidth when the number
+ * of data bytes is non-zero, the data buswidth won't be set here. So,
+ * do it explicitly.
+ */
+ op->data.buswidth = spi_nor_get_protocol_data_nbits(nor->write_proto);
+
nor->dirmap.wdesc = devm_spi_mem_dirmap_create(nor->dev, nor->spimem,
&info);
return PTR_ERR_OR_ZERO(nor->dirmap.wdesc);
diff --git a/drivers/mtd/spi-nor/core.h b/drivers/mtd/spi-nor/core.h
index 6f2f6b27173f..de1e3917889f 100644
--- a/drivers/mtd/spi-nor/core.h
+++ b/drivers/mtd/spi-nor/core.h
@@ -62,6 +62,7 @@ enum spi_nor_read_command_index {
SNOR_CMD_READ_1_8_8,
SNOR_CMD_READ_8_8_8,
SNOR_CMD_READ_1_8_8_DTR,
+ SNOR_CMD_READ_8_8_8_DTR,

SNOR_CMD_READ_MAX
};
@@ -78,6 +79,7 @@ enum spi_nor_pp_command_index {
SNOR_CMD_PP_1_1_8,
SNOR_CMD_PP_1_8_8,
SNOR_CMD_PP_8_8_8,
+ SNOR_CMD_PP_8_8_8_DTR,

SNOR_CMD_PP_MAX
};
@@ -311,6 +313,7 @@ struct flash_info {
* BP3 is bit 6 of status register.
* Must be used with SPI_NOR_4BIT_BP.
*/
+#define SPI_NOR_OCTAL_DTR_READ BIT(19) /* Flash supports octal DTR Read. */

/* Part specific fixup hooks. */
const struct spi_nor_fixups *fixups;
@@ -399,6 +402,9 @@ extern const struct spi_nor_manufacturer spi_nor_winbond;
extern const struct spi_nor_manufacturer spi_nor_xilinx;
extern const struct spi_nor_manufacturer spi_nor_xmc;

+void spi_nor_spimem_setup_op(const struct spi_nor *nor,
+ struct spi_mem_op *op,
+ const enum spi_nor_protocol proto);
int spi_nor_write_enable(struct spi_nor *nor);
int spi_nor_write_disable(struct spi_nor *nor);
int spi_nor_set_4byte_addr_mode(struct spi_nor *nor, bool enable);
diff --git a/drivers/mtd/spi-nor/sfdp.c b/drivers/mtd/spi-nor/sfdp.c
index f6038d3a3684..f917631c8110 100644
--- a/drivers/mtd/spi-nor/sfdp.c
+++ b/drivers/mtd/spi-nor/sfdp.c
@@ -1044,9 +1044,16 @@ static int spi_nor_parse_4bait(struct spi_nor *nor,
}

/* 4BAIT is the only SFDP table that indicates page program support. */
- if (pp_hwcaps & SNOR_HWCAPS_PP)
+ if (pp_hwcaps & SNOR_HWCAPS_PP) {
spi_nor_set_pp_settings(&params_pp[SNOR_CMD_PP],
SPINOR_OP_PP_4B, SNOR_PROTO_1_1_1);
+ /*
+ * Since xSPI Page Program opcode is backward compatible with
+ * Legacy SPI, use Legacy SPI opcode there as well.
+ */
+ spi_nor_set_pp_settings(&params_pp[SNOR_CMD_PP_8_8_8_DTR],
+ SPINOR_OP_PP_4B, SNOR_PROTO_8_8_8_DTR);
+ }
if (pp_hwcaps & SNOR_HWCAPS_PP_1_1_4)
spi_nor_set_pp_settings(&params_pp[SNOR_CMD_PP_1_1_4],
SPINOR_OP_PP_1_1_4_4B,
diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h
index bebff2729c18..d251a5d02be2 100644
--- a/include/linux/mtd/spi-nor.h
+++ b/include/linux/mtd/spi-nor.h
@@ -182,6 +182,7 @@ enum spi_nor_protocol {
SNOR_PROTO_1_2_2_DTR = SNOR_PROTO_DTR(1, 2, 2),
SNOR_PROTO_1_4_4_DTR = SNOR_PROTO_DTR(1, 4, 4),
SNOR_PROTO_1_8_8_DTR = SNOR_PROTO_DTR(1, 8, 8),
+ SNOR_PROTO_8_8_8_DTR = SNOR_PROTO_DTR(8, 8, 8),
};

static inline bool spi_nor_protocol_is_dtr(enum spi_nor_protocol proto)
@@ -228,7 +229,7 @@ struct spi_nor_hwcaps {
* then Quad SPI protocols before Dual SPI protocols, Fast Read and lastly
* (Slow) Read.
*/
-#define SNOR_HWCAPS_READ_MASK GENMASK(14, 0)
+#define SNOR_HWCAPS_READ_MASK GENMASK(15, 0)
#define SNOR_HWCAPS_READ BIT(0)
#define SNOR_HWCAPS_READ_FAST BIT(1)
#define SNOR_HWCAPS_READ_1_1_1_DTR BIT(2)
@@ -245,11 +246,12 @@ struct spi_nor_hwcaps {
#define SNOR_HWCAPS_READ_4_4_4 BIT(9)
#define SNOR_HWCAPS_READ_1_4_4_DTR BIT(10)

-#define SNOR_HWCAPS_READ_OCTAL GENMASK(14, 11)
+#define SNOR_HWCAPS_READ_OCTAL GENMASK(15, 11)
#define SNOR_HWCAPS_READ_1_1_8 BIT(11)
#define SNOR_HWCAPS_READ_1_8_8 BIT(12)
#define SNOR_HWCAPS_READ_8_8_8 BIT(13)
#define SNOR_HWCAPS_READ_1_8_8_DTR BIT(14)
+#define SNOR_HWCAPS_READ_8_8_8_DTR BIT(15)

/*
* Page Program capabilities.
@@ -260,18 +262,19 @@ struct spi_nor_hwcaps {
* JEDEC/SFDP standard to define them. Also at this moment no SPI flash memory
* implements such commands.
*/
-#define SNOR_HWCAPS_PP_MASK GENMASK(22, 16)
-#define SNOR_HWCAPS_PP BIT(16)
+#define SNOR_HWCAPS_PP_MASK GENMASK(23, 16)
+#define SNOR_HWCAPS_PP BIT(16)

-#define SNOR_HWCAPS_PP_QUAD GENMASK(19, 17)
-#define SNOR_HWCAPS_PP_1_1_4 BIT(17)
-#define SNOR_HWCAPS_PP_1_4_4 BIT(18)
-#define SNOR_HWCAPS_PP_4_4_4 BIT(19)
+#define SNOR_HWCAPS_PP_QUAD GENMASK(19, 17)
+#define SNOR_HWCAPS_PP_1_1_4 BIT(17)
+#define SNOR_HWCAPS_PP_1_4_4 BIT(18)
+#define SNOR_HWCAPS_PP_4_4_4 BIT(19)

-#define SNOR_HWCAPS_PP_OCTAL GENMASK(22, 20)
-#define SNOR_HWCAPS_PP_1_1_8 BIT(20)
-#define SNOR_HWCAPS_PP_1_8_8 BIT(21)
-#define SNOR_HWCAPS_PP_8_8_8 BIT(22)
+#define SNOR_HWCAPS_PP_OCTAL GENMASK(23, 20)
+#define SNOR_HWCAPS_PP_1_1_8 BIT(20)
+#define SNOR_HWCAPS_PP_1_8_8 BIT(21)
+#define SNOR_HWCAPS_PP_8_8_8 BIT(22)
+#define SNOR_HWCAPS_PP_8_8_8_DTR BIT(23)

#define SNOR_HWCAPS_X_X_X (SNOR_HWCAPS_READ_2_2_2 | \
SNOR_HWCAPS_READ_4_4_4 | \
@@ -279,10 +282,14 @@ struct spi_nor_hwcaps {
SNOR_HWCAPS_PP_4_4_4 | \
SNOR_HWCAPS_PP_8_8_8)

+#define SNOR_HWCAPS_X_X_X_DTR (SNOR_HWCAPS_READ_8_8_8_DTR | \
+ SNOR_HWCAPS_PP_8_8_8_DTR)
+
#define SNOR_HWCAPS_DTR (SNOR_HWCAPS_READ_1_1_1_DTR | \
SNOR_HWCAPS_READ_1_2_2_DTR | \
SNOR_HWCAPS_READ_1_4_4_DTR | \
- SNOR_HWCAPS_READ_1_8_8_DTR)
+ SNOR_HWCAPS_READ_1_8_8_DTR | \
+ SNOR_HWCAPS_READ_8_8_8_DTR)

#define SNOR_HWCAPS_ALL (SNOR_HWCAPS_READ_MASK | \
SNOR_HWCAPS_PP_MASK)
@@ -318,6 +325,22 @@ struct spi_nor_controller_ops {
int (*erase)(struct spi_nor *nor, loff_t offs);
};

+/**
+ * enum spi_nor_cmd_ext - describes the command opcode extension in DTR mode
+ * @SPI_NOR_EXT_NONE: no extension. This is the default, and is used in Legacy
+ * SPI mode
+ * @SPI_NOR_EXT_REPEAT: the extension is same as the opcode
+ * @SPI_NOR_EXT_INVERT: the extension is the bitwise inverse of the opcode
+ * @SPI_NOR_EXT_HEX: the extension is any hex value. The command and opcode
+ * combine to form a 16-bit opcode.
+ */
+enum spi_nor_cmd_ext {
+ SPI_NOR_EXT_NONE = 0,
+ SPI_NOR_EXT_REPEAT,
+ SPI_NOR_EXT_INVERT,
+ SPI_NOR_EXT_HEX,
+};
+
/*
* Forward declarations that are used internally by the core and manufacturer
* drivers.
@@ -345,6 +368,7 @@ struct spi_nor_flash_parameter;
* @program_opcode: the program opcode
* @sst_write_second: used by the SST write operation
* @flags: flag options for the current SPI-NOR (SNOR_F_*)
+ * @cmd_ext_type: the command opcode extension type for DTR mode.
* @read_proto: the SPI protocol for read operations
* @write_proto: the SPI protocol for write operations
* @reg_proto: the SPI protocol for read_reg/write_reg/erase operations
@@ -376,6 +400,7 @@ struct spi_nor {
enum spi_nor_protocol reg_proto;
bool sst_write_second;
u32 flags;
+ enum spi_nor_cmd_ext cmd_ext_type;

const struct spi_nor_controller_ops *controller_ops;

--
2.26.2

2020-05-19 14:29:47

by Pratyush Yadav

[permalink] [raw]
Subject: [PATCH v5 07/19] mtd: spi-nor: sfdp: prepare BFPT parsing for JESD216 rev D

JESD216 rev D makes BFPT 20 DWORDs. Update the BFPT size define to
reflect that.

The check for rev A or later compared the BFPT header length with the
maximum BFPT length, BFPT_DWORD_MAX. Since BFPT_DWORD_MAX was 16, and so
was the BFPT length for both rev A and B, this check worked fine. But
now, since BFPT_DWORD_MAX is 20, it means this check will also stop BFPT
parsing for rev A or B, since their length is 16.

So, instead check for BFPT_DWORD_MAX_JESD216 to stop BFPT parsing for
the first JESD216 version, and check for BFPT_DWORD_MAX_JESD216B for the
next two versions.

Signed-off-by: Pratyush Yadav <[email protected]>
---
drivers/mtd/spi-nor/sfdp.c | 7 ++++++-
drivers/mtd/spi-nor/sfdp.h | 5 +++--
2 files changed, 9 insertions(+), 3 deletions(-)

diff --git a/drivers/mtd/spi-nor/sfdp.c b/drivers/mtd/spi-nor/sfdp.c
index 5cecc4ba2141..96960f2f3d7a 100644
--- a/drivers/mtd/spi-nor/sfdp.c
+++ b/drivers/mtd/spi-nor/sfdp.c
@@ -549,7 +549,7 @@ static int spi_nor_parse_bfpt(struct spi_nor *nor,
SNOR_ERASE_TYPE_MASK;

/* Stop here if not JESD216 rev A or later. */
- if (bfpt_header->length < BFPT_DWORD_MAX)
+ if (bfpt_header->length == BFPT_DWORD_MAX_JESD216)
return spi_nor_post_bfpt_fixups(nor, bfpt_header, &bfpt,
params);

@@ -605,6 +605,11 @@ static int spi_nor_parse_bfpt(struct spi_nor *nor,
return -EINVAL;
}

+ /* Stop here if JESD216 rev B. */
+ if (bfpt_header->length == BFPT_DWORD_MAX_JESD216B)
+ return spi_nor_post_bfpt_fixups(nor, bfpt_header, &bfpt,
+ params);
+
return spi_nor_post_bfpt_fixups(nor, bfpt_header, &bfpt, params);
}

diff --git a/drivers/mtd/spi-nor/sfdp.h b/drivers/mtd/spi-nor/sfdp.h
index e0a8ded04890..f8198af43a63 100644
--- a/drivers/mtd/spi-nor/sfdp.h
+++ b/drivers/mtd/spi-nor/sfdp.h
@@ -10,11 +10,11 @@
/* Basic Flash Parameter Table */

/*
- * JESD216 rev B defines a Basic Flash Parameter Table of 16 DWORDs.
+ * JESD216 rev D defines a Basic Flash Parameter Table of 20 DWORDs.
* They are indexed from 1 but C arrays are indexed from 0.
*/
#define BFPT_DWORD(i) ((i) - 1)
-#define BFPT_DWORD_MAX 16
+#define BFPT_DWORD_MAX 20

struct sfdp_bfpt {
u32 dwords[BFPT_DWORD_MAX];
@@ -22,6 +22,7 @@ struct sfdp_bfpt {

/* The first version of JESD216 defined only 9 DWORDs. */
#define BFPT_DWORD_MAX_JESD216 9
+#define BFPT_DWORD_MAX_JESD216B 16

/* 1st DWORD. */
#define BFPT_DWORD1_FAST_READ_1_1_2 BIT(16)
--
2.26.2

2020-05-19 14:29:50

by Pratyush Yadav

[permalink] [raw]
Subject: [PATCH v5 06/19] mtd: spi-nor: sfdp: default to addr_width of 3 for configurable widths

JESD216D.01 says that when the address width can be 3 or 4, it defaults
to 3 and enters 4-byte mode when given the appropriate command. So, when
we see a configurable width, default to 3 and let flash that default to
4 change it in a post-bfpt fixup.

This fixes SMPT parsing for flashes with configurable address width. If
the SMPT descriptor advertises variable address width, we use
nor->addr_width as the address width. But since it was not set to any
value from the SFDP table, the read command uses an address width of 0,
resulting in an incorrect read being issued.

Signed-off-by: Pratyush Yadav <[email protected]>
---
drivers/mtd/spi-nor/sfdp.c | 1 +
1 file changed, 1 insertion(+)

diff --git a/drivers/mtd/spi-nor/sfdp.c b/drivers/mtd/spi-nor/sfdp.c
index f917631c8110..5cecc4ba2141 100644
--- a/drivers/mtd/spi-nor/sfdp.c
+++ b/drivers/mtd/spi-nor/sfdp.c
@@ -460,6 +460,7 @@ static int spi_nor_parse_bfpt(struct spi_nor *nor,
/* Number of address bytes. */
switch (bfpt.dwords[BFPT_DWORD(1)] & BFPT_DWORD1_ADDRESS_BYTES_MASK) {
case BFPT_DWORD1_ADDRESS_BYTES_3_ONLY:
+ case BFPT_DWORD1_ADDRESS_BYTES_3_OR_4:
nor->addr_width = 3;
break;

--
2.26.2

2020-05-19 14:29:59

by Pratyush Yadav

[permalink] [raw]
Subject: [PATCH v5 09/19] mtd: spi-nor: sfdp: parse xSPI Profile 1.0 table

This table is indication that the flash is xSPI compliant and hence
supports octal DTR mode. Extract information like the fast read opcode,
the number of dummy cycles needed for a Read Status Register command,
and the number of address bytes needed for a Read Status Register
command.

The default dummy cycles for a fast octal DTR read are set to 20. Since
there is no simple way of determining the dummy cycles needed for the
fast read command, flashes that use a different value should update it
in their flash-specific hooks.

Since we want to set read settings, expose spi_nor_set_read_settings()
in core.h.

Signed-off-by: Pratyush Yadav <[email protected]>
---
drivers/mtd/spi-nor/core.c | 2 +-
drivers/mtd/spi-nor/core.h | 10 ++++++
drivers/mtd/spi-nor/sfdp.c | 73 ++++++++++++++++++++++++++++++++++++++
3 files changed, 84 insertions(+), 1 deletion(-)

diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c
index 388e695e763f..642e3c07acf9 100644
--- a/drivers/mtd/spi-nor/core.c
+++ b/drivers/mtd/spi-nor/core.c
@@ -2355,7 +2355,7 @@ static int spi_nor_check(struct spi_nor *nor)
return 0;
}

-static void
+void
spi_nor_set_read_settings(struct spi_nor_read_command *read,
u8 num_mode_clocks,
u8 num_wait_states,
diff --git a/drivers/mtd/spi-nor/core.h b/drivers/mtd/spi-nor/core.h
index de1e3917889f..7e6df8322da0 100644
--- a/drivers/mtd/spi-nor/core.h
+++ b/drivers/mtd/spi-nor/core.h
@@ -192,6 +192,9 @@ struct spi_nor_locking_ops {
*
* @size: the flash memory density in bytes.
* @page_size: the page size of the SPI NOR flash memory.
+ * @rdsr_dummy: dummy cycles needed for Read Status Register command.
+ * @rdsr_addr_nbytes: dummy address bytes needed for Read Status Register
+ * command.
* @hwcaps: describes the read and page program hardware
* capabilities.
* @reads: read capabilities ordered by priority: the higher index
@@ -214,6 +217,8 @@ struct spi_nor_locking_ops {
struct spi_nor_flash_parameter {
u64 size;
u32 page_size;
+ u8 rdsr_dummy;
+ u8 rdsr_addr_nbytes;

struct spi_nor_hwcaps hwcaps;
struct spi_nor_read_command reads[SNOR_CMD_READ_MAX];
@@ -424,6 +429,11 @@ ssize_t spi_nor_write_data(struct spi_nor *nor, loff_t to, size_t len,

int spi_nor_hwcaps_read2cmd(u32 hwcaps);
u8 spi_nor_convert_3to4_read(u8 opcode);
+void spi_nor_set_read_settings(struct spi_nor_read_command *read,
+ u8 num_mode_clocks,
+ u8 num_wait_states,
+ u8 opcode,
+ enum spi_nor_protocol proto);
void spi_nor_set_pp_settings(struct spi_nor_pp_command *pp, u8 opcode,
enum spi_nor_protocol proto);

diff --git a/drivers/mtd/spi-nor/sfdp.c b/drivers/mtd/spi-nor/sfdp.c
index ab086aa4746f..4e5e0eabe2d9 100644
--- a/drivers/mtd/spi-nor/sfdp.c
+++ b/drivers/mtd/spi-nor/sfdp.c
@@ -4,6 +4,7 @@
* Copyright (C) 2014, Freescale Semiconductor, Inc.
*/

+#include <linux/bitfield.h>
#include <linux/slab.h>
#include <linux/sort.h>
#include <linux/mtd/spi-nor.h>
@@ -19,12 +20,14 @@
#define SFDP_BFPT_ID 0xff00 /* Basic Flash Parameter Table */
#define SFDP_SECTOR_MAP_ID 0xff81 /* Sector Map Table */
#define SFDP_4BAIT_ID 0xff84 /* 4-byte Address Instruction Table */
+#define SFDP_PROFILE1_ID 0xff05 /* xSPI Profile 1.0 table. */

#define SFDP_SIGNATURE 0x50444653U
#define SFDP_JESD216_MAJOR 1
#define SFDP_JESD216_MINOR 0
#define SFDP_JESD216A_MINOR 5
#define SFDP_JESD216B_MINOR 6
+#define SFDP_JESD216D_MINOR 8

struct sfdp_header {
u32 signature; /* Ox50444653U <=> "SFDP" */
@@ -70,6 +73,11 @@ struct sfdp_bfpt_erase {
u32 shift;
};

+/* xSPI Profile 1.0 table (from JESD216D.01). */
+#define PROFILE1_DWORD1_RD_FAST_CMD GENMASK(15, 8)
+#define PROFILE1_DWORD1_RDSR_DUMMY BIT(28)
+#define PROFILE1_DWORD1_RDSR_ADDR_BYTES BIT(29)
+
#define SMPT_CMD_ADDRESS_LEN_MASK GENMASK(23, 22)
#define SMPT_CMD_ADDRESS_LEN_0 (0x0UL << 22)
#define SMPT_CMD_ADDRESS_LEN_3 (0x1UL << 22)
@@ -1110,6 +1118,67 @@ static int spi_nor_parse_4bait(struct spi_nor *nor,
return ret;
}

+/**
+ * spi_nor_parse_profile1() - parse the xSPI Profile 1.0 table
+ * @nor: pointer to a 'struct spi_nor'
+ * @param_header: pointer to the 'struct sfdp_parameter_header' describing
+ * the 4-Byte Address Instruction Table length and version.
+ * @params: pointer to the 'struct spi_nor_flash_parameter' to be.
+ *
+ * Return: 0 on success, -errno otherwise.
+ */
+static int spi_nor_parse_profile1(struct spi_nor *nor,
+ const struct sfdp_parameter_header *profile1_header,
+ struct spi_nor_flash_parameter *params)
+{
+ u32 *table, opcode, addr;
+ size_t len;
+ int ret, i;
+
+ len = profile1_header->length * sizeof(*table);
+ table = kmalloc(len, GFP_KERNEL);
+ if (!table)
+ return -ENOMEM;
+
+ addr = SFDP_PARAM_HEADER_PTP(profile1_header);
+ ret = spi_nor_read_sfdp(nor, addr, len, table);
+ if (ret)
+ goto out;
+
+ /* Fix endianness of the table DWORDs. */
+ for (i = 0; i < profile1_header->length; i++)
+ table[i] = le32_to_cpu(table[i]);
+
+ /* Get 8D-8D-8D fast read opcode and dummy cycles. */
+ opcode = FIELD_GET(PROFILE1_DWORD1_RD_FAST_CMD, table[0]);
+
+ /*
+ * Update the fast read settings. We set the default dummy cycles to 20
+ * here. Flashes can change this value if they need to when enabling
+ * octal mode.
+ */
+ spi_nor_set_read_settings(&params->reads[SNOR_CMD_READ_8_8_8_DTR],
+ 0, 20, opcode,
+ SNOR_PROTO_8_8_8_DTR);
+
+ /*
+ * Set the Read Status Register dummy cycles and dummy address bytes.
+ */
+ if (table[0] & PROFILE1_DWORD1_RDSR_DUMMY)
+ params->rdsr_dummy = 8;
+ else
+ params->rdsr_dummy = 4;
+
+ if (table[0] & PROFILE1_DWORD1_RDSR_ADDR_BYTES)
+ params->rdsr_addr_nbytes = 4;
+ else
+ params->rdsr_addr_nbytes = 0;
+
+out:
+ kfree(table);
+ return ret;
+}
+
/**
* spi_nor_parse_sfdp() - parse the Serial Flash Discoverable Parameters.
* @nor: pointer to a 'struct spi_nor'
@@ -1211,6 +1280,10 @@ int spi_nor_parse_sfdp(struct spi_nor *nor,
err = spi_nor_parse_4bait(nor, param_header, params);
break;

+ case SFDP_PROFILE1_ID:
+ err = spi_nor_parse_profile1(nor, param_header, params);
+ break;
+
default:
break;
}
--
2.26.2

2020-05-19 14:30:11

by Pratyush Yadav

[permalink] [raw]
Subject: [PATCH v5 10/19] mtd: spi-nor: core: use dummy cycle and address width info from SFDP

The xSPI Profile 1.0 table specifies how many dummy cycles and address
bytes are needed for the Read Status Register command in octal DTR mode.
Use that information to send the correct Read SR command.

Signed-off-by: Pratyush Yadav <[email protected]>
---
drivers/mtd/spi-nor/core.c | 25 +++++++++++++++++++++++--
1 file changed, 23 insertions(+), 2 deletions(-)

diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c
index 642e3c07acf9..2ad248140b6c 100644
--- a/drivers/mtd/spi-nor/core.c
+++ b/drivers/mtd/spi-nor/core.c
@@ -357,6 +357,8 @@ int spi_nor_write_disable(struct spi_nor *nor)
static int spi_nor_read_sr(struct spi_nor *nor, u8 *sr)
{
int ret;
+ u8 addr_bytes = nor->params->rdsr_addr_nbytes;
+ u8 dummy = nor->params->rdsr_dummy;

if (nor->spimem) {
struct spi_mem_op op =
@@ -365,10 +367,21 @@ static int spi_nor_read_sr(struct spi_nor *nor, u8 *sr)
SPI_MEM_OP_NO_DUMMY,
SPI_MEM_OP_DATA_IN(1, sr, 1));

+ if (spi_nor_protocol_is_dtr(nor->reg_proto)) {
+ op.addr.nbytes = addr_bytes;
+ op.addr.val = 0;
+ op.dummy.nbytes = dummy;
+ }
+
+ spi_nor_spimem_setup_op(nor, &op, nor->reg_proto);
+
ret = spi_mem_exec_op(nor->spimem, &op);
} else {
- ret = nor->controller_ops->read_reg(nor, SPINOR_OP_RDSR,
- sr, 1);
+ if (spi_nor_protocol_is_dtr(nor->reg_proto))
+ ret = -ENOTSUPP;
+ else
+ ret = nor->controller_ops->read_reg(nor, SPINOR_OP_RDSR,
+ sr, 1);
}

if (ret)
@@ -388,6 +401,8 @@ static int spi_nor_read_sr(struct spi_nor *nor, u8 *sr)
static int spi_nor_read_fsr(struct spi_nor *nor, u8 *fsr)
{
int ret;
+ u8 addr_bytes = nor->params->rdsr_addr_nbytes;
+ u8 dummy = nor->params->rdsr_dummy;

if (nor->spimem) {
struct spi_mem_op op =
@@ -396,6 +411,12 @@ static int spi_nor_read_fsr(struct spi_nor *nor, u8 *fsr)
SPI_MEM_OP_NO_DUMMY,
SPI_MEM_OP_DATA_IN(1, fsr, 1));

+ if (spi_nor_protocol_is_dtr(nor->reg_proto)) {
+ op.addr.nbytes = addr_bytes;
+ op.addr.val = 0;
+ op.dummy.nbytes = dummy;
+ }
+
spi_nor_spimem_setup_op(nor, &op, nor->reg_proto);

ret = spi_mem_exec_op(nor->spimem, &op);
--
2.26.2

2020-05-19 14:30:21

by Pratyush Yadav

[permalink] [raw]
Subject: [PATCH v5 11/19] mtd: spi-nor: core: do 2 byte reads for SR and FSR in DTR mode

Some controllers, like the cadence qspi controller, have trouble reading
only 1 byte in DTR mode. So, do 2 byte reads for SR and FSR commands in
DTR mode, and then discard the second byte.

Signed-off-by: Pratyush Yadav <[email protected]>
---
drivers/mtd/spi-nor/core.c | 15 +++++++++++++--
1 file changed, 13 insertions(+), 2 deletions(-)

diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c
index 2ad248140b6c..5cb7e391cd29 100644
--- a/drivers/mtd/spi-nor/core.c
+++ b/drivers/mtd/spi-nor/core.c
@@ -350,7 +350,7 @@ int spi_nor_write_disable(struct spi_nor *nor)
* spi_nor_read_sr() - Read the Status Register.
* @nor: pointer to 'struct spi_nor'.
* @sr: pointer to a DMA-able buffer where the value of the
- * Status Register will be written.
+ * Status Register will be written. Should be at least 2 bytes.
*
* Return: 0 on success, -errno otherwise.
*/
@@ -371,6 +371,11 @@ static int spi_nor_read_sr(struct spi_nor *nor, u8 *sr)
op.addr.nbytes = addr_bytes;
op.addr.val = 0;
op.dummy.nbytes = dummy;
+ /*
+ * We don't want to read only one byte in DTR mode. So,
+ * read 2 and then discard the second byte.
+ */
+ op.data.nbytes = 2;
}

spi_nor_spimem_setup_op(nor, &op, nor->reg_proto);
@@ -394,7 +399,8 @@ static int spi_nor_read_sr(struct spi_nor *nor, u8 *sr)
* spi_nor_read_fsr() - Read the Flag Status Register.
* @nor: pointer to 'struct spi_nor'
* @fsr: pointer to a DMA-able buffer where the value of the
- * Flag Status Register will be written.
+ * Flag Status Register will be written. Should be at least 2
+ * bytes.
*
* Return: 0 on success, -errno otherwise.
*/
@@ -415,6 +421,11 @@ static int spi_nor_read_fsr(struct spi_nor *nor, u8 *fsr)
op.addr.nbytes = addr_bytes;
op.addr.val = 0;
op.dummy.nbytes = dummy;
+ /*
+ * We don't want to read only one byte in DTR mode. So,
+ * read 2 and then discard the second byte.
+ */
+ op.data.nbytes = 2;
}

spi_nor_spimem_setup_op(nor, &op, nor->reg_proto);
--
2.26.2

2020-05-19 14:30:45

by Pratyush Yadav

[permalink] [raw]
Subject: [PATCH v5 12/19] mtd: spi-nor: core: enable octal DTR mode when possible

Allow flashes to specify a hook to enable octal DTR mode. Use this hook
whenever possible to get optimal transfer speeds.

Signed-off-by: Pratyush Yadav <[email protected]>
---
drivers/mtd/spi-nor/core.c | 35 +++++++++++++++++++++++++++++++++++
drivers/mtd/spi-nor/core.h | 2 ++
2 files changed, 37 insertions(+)

diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c
index 5cb7e391cd29..a94376344be5 100644
--- a/drivers/mtd/spi-nor/core.c
+++ b/drivers/mtd/spi-nor/core.c
@@ -3097,6 +3097,35 @@ static int spi_nor_init_params(struct spi_nor *nor)
return 0;
}

+/** spi_nor_octal_dtr_enable() - enable Octal DTR I/O if needed
+ * @nor: pointer to a 'struct spi_nor'
+ * @enable: whether to enable or disable Octal DTR
+ *
+ * Return: 0 on success, -errno otherwise.
+ */
+static int spi_nor_octal_dtr_enable(struct spi_nor *nor, bool enable)
+{
+ int ret;
+
+ if (!nor->params->octal_dtr_enable)
+ return 0;
+
+ if (!(nor->read_proto == SNOR_PROTO_8_8_8_DTR &&
+ nor->write_proto == SNOR_PROTO_8_8_8_DTR))
+ return 0;
+
+ ret = nor->params->octal_dtr_enable(nor, enable);
+ if (ret)
+ return ret;
+
+ if (enable)
+ nor->reg_proto = SNOR_PROTO_8_8_8_DTR;
+ else
+ nor->reg_proto = SNOR_PROTO_1_1_1;
+
+ return 0;
+}
+
/**
* spi_nor_quad_enable() - enable Quad I/O if needed.
* @nor: pointer to a 'struct spi_nor'
@@ -3136,6 +3165,12 @@ static int spi_nor_init(struct spi_nor *nor)
{
int err;

+ err = spi_nor_octal_dtr_enable(nor, true);
+ if (err) {
+ dev_dbg(nor->dev, "octal mode not supported\n");
+ return err;
+ }
+
err = spi_nor_quad_enable(nor);
if (err) {
dev_dbg(nor->dev, "quad mode not supported\n");
diff --git a/drivers/mtd/spi-nor/core.h b/drivers/mtd/spi-nor/core.h
index 7e6df8322da0..6338d32a0d77 100644
--- a/drivers/mtd/spi-nor/core.h
+++ b/drivers/mtd/spi-nor/core.h
@@ -203,6 +203,7 @@ struct spi_nor_locking_ops {
* higher index in the array, the higher priority.
* @erase_map: the erase map parsed from the SFDP Sector Map Parameter
* Table.
+ * @octal_dtr_enable: enables SPI NOR octal DTR mode.
* @quad_enable: enables SPI NOR quad mode.
* @set_4byte_addr_mode: puts the SPI NOR in 4 byte addressing mode.
* @convert_addr: converts an absolute address into something the flash
@@ -226,6 +227,7 @@ struct spi_nor_flash_parameter {

struct spi_nor_erase_map erase_map;

+ int (*octal_dtr_enable)(struct spi_nor *nor, bool enable);
int (*quad_enable)(struct spi_nor *nor);
int (*set_4byte_addr_mode)(struct spi_nor *nor, bool enable);
u32 (*convert_addr)(struct spi_nor *nor, u32 addr);
--
2.26.2

2020-05-19 14:31:08

by Pratyush Yadav

[permalink] [raw]
Subject: [PATCH v5 14/19] mtd: spi-nor: sfdp: detect Soft Reset sequence support from BFPT

A Soft Reset sequence will return the flash to Power-on-Reset (POR)
state. It consists of two commands: Soft Reset Enable and Soft Reset.
Find out if the sequence is supported from BFPT DWORD 16.

Signed-off-by: Pratyush Yadav <[email protected]>
---
drivers/mtd/spi-nor/core.h | 1 +
drivers/mtd/spi-nor/sfdp.c | 4 ++++
drivers/mtd/spi-nor/sfdp.h | 2 ++
3 files changed, 7 insertions(+)

diff --git a/drivers/mtd/spi-nor/core.h b/drivers/mtd/spi-nor/core.h
index 6338d32a0d77..79ce952c0539 100644
--- a/drivers/mtd/spi-nor/core.h
+++ b/drivers/mtd/spi-nor/core.h
@@ -26,6 +26,7 @@ enum spi_nor_option_flags {
SNOR_F_HAS_SR_TB_BIT6 = BIT(11),
SNOR_F_HAS_4BIT_BP = BIT(12),
SNOR_F_HAS_SR_BP3_BIT6 = BIT(13),
+ SNOR_F_SOFT_RESET = BIT(14),
};

struct spi_nor_read_command {
diff --git a/drivers/mtd/spi-nor/sfdp.c b/drivers/mtd/spi-nor/sfdp.c
index be9c6c3d6590..490ece7b3d33 100644
--- a/drivers/mtd/spi-nor/sfdp.c
+++ b/drivers/mtd/spi-nor/sfdp.c
@@ -611,6 +611,10 @@ static int spi_nor_parse_bfpt(struct spi_nor *nor,
break;
}

+ /* Soft Reset support. */
+ if (bfpt.dwords[BFPT_DWORD(16)] & BFPT_DWORD16_SOFT_RST)
+ nor->flags |= SNOR_F_SOFT_RESET;
+
/* Stop here if JESD216 rev B. */
if (bfpt_header->length == BFPT_DWORD_MAX_JESD216B)
return spi_nor_post_bfpt_fixups(nor, bfpt_header, &bfpt,
diff --git a/drivers/mtd/spi-nor/sfdp.h b/drivers/mtd/spi-nor/sfdp.h
index e15e30796d62..d1d43ee09a0a 100644
--- a/drivers/mtd/spi-nor/sfdp.h
+++ b/drivers/mtd/spi-nor/sfdp.h
@@ -84,6 +84,8 @@ struct sfdp_bfpt {
#define BFPT_DWORD15_QER_SR2_BIT1_NO_RD (0x4UL << 20)
#define BFPT_DWORD15_QER_SR2_BIT1 (0x5UL << 20) /* Spansion */

+#define BFPT_DWORD16_SOFT_RST BIT(12)
+
#define BFPT_DWORD18_CMD_EXT_MASK GENMASK(30, 29)
#define BFPT_DWORD18_CMD_EXT_REP (0x0UL << 29) /* Repeat */
#define BFPT_DWORD18_CMD_EXT_INV (0x1UL << 29) /* Invert */
--
2.26.2

2020-05-19 14:31:26

by Pratyush Yadav

[permalink] [raw]
Subject: [PATCH v5 17/19] mtd: spi-nor: core: expose spi_nor_default_setup() in core.h

Flashes might want to add a custom setup hook to configure the flash in
the proper mode for operation. But after that, they would still want to
run the default setup hook because it selects the read, program, and
erase operations. Since there is little point in repeating all that
code, expose the spi_nor_default_setup() in core.h to
manufacturer-specific files.

Signed-off-by: Pratyush Yadav <[email protected]>
---
drivers/mtd/spi-nor/core.c | 4 ++--
drivers/mtd/spi-nor/core.h | 3 +++
2 files changed, 5 insertions(+), 2 deletions(-)

diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c
index 63ab588299f4..30d9149fd17b 100644
--- a/drivers/mtd/spi-nor/core.c
+++ b/drivers/mtd/spi-nor/core.c
@@ -2790,8 +2790,8 @@ static int spi_nor_select_erase(struct spi_nor *nor)
return 0;
}

-static int spi_nor_default_setup(struct spi_nor *nor,
- const struct spi_nor_hwcaps *hwcaps)
+int spi_nor_default_setup(struct spi_nor *nor,
+ const struct spi_nor_hwcaps *hwcaps)
{
struct spi_nor_flash_parameter *params = nor->params;
u32 ignored_mask, shared_mask;
diff --git a/drivers/mtd/spi-nor/core.h b/drivers/mtd/spi-nor/core.h
index 79ce952c0539..d37a9b1d111f 100644
--- a/drivers/mtd/spi-nor/core.h
+++ b/drivers/mtd/spi-nor/core.h
@@ -452,6 +452,9 @@ int spi_nor_post_bfpt_fixups(struct spi_nor *nor,
const struct sfdp_bfpt *bfpt,
struct spi_nor_flash_parameter *params);

+int spi_nor_default_setup(struct spi_nor *nor,
+ const struct spi_nor_hwcaps *hwcaps);
+
static struct spi_nor __maybe_unused *mtd_to_spi_nor(struct mtd_info *mtd)
{
return mtd->priv;
--
2.26.2

2020-05-19 14:31:30

by Pratyush Yadav

[permalink] [raw]
Subject: [PATCH v5 18/19] mtd: spi-nor: spansion: add support for Cypress Semper flash

The Cypress Semper flash is an xSPI compliant octal DTR flash. Add
support for using it in octal DTR mode.

The flash by default boots in a hybrid sector mode. But the sector map
table on the part I had was programmed incorrectly and the SMPT values
on the flash don't match the public datasheet. Specifically, in some
places erase type 3 was used instead of 4. In addition, the region sizes
were incorrect in some places. So, for testing I set CFR3N[3] to enable
uniform sector sizes. Since the uniform sector mode bit is a
non-volatile bit, this series does not change it to avoid making any
permanent changes to the flash configuration. The correct data to
implement a fixup is not available right now and will be done in a
follow-up patch if needed.

Signed-off-by: Pratyush Yadav <[email protected]>
---
drivers/mtd/spi-nor/spansion.c | 167 +++++++++++++++++++++++++++++++++
1 file changed, 167 insertions(+)

diff --git a/drivers/mtd/spi-nor/spansion.c b/drivers/mtd/spi-nor/spansion.c
index 88183eba8ac1..e5dc36b70e4e 100644
--- a/drivers/mtd/spi-nor/spansion.c
+++ b/drivers/mtd/spi-nor/spansion.c
@@ -8,6 +8,169 @@

#include "core.h"

+/* For Cypress flash. */
+#define SPINOR_OP_RD_ANY_REG 0x65 /* Read any register */
+#define SPINOR_OP_WR_ANY_REG 0x71 /* Write any register */
+#define SPINOR_REG_CYPRESS_CFR2V 0x00800003
+#define SPINOR_REG_CYPRESS_CFR2V_MEMLAT_11_24 0xb
+#define SPINOR_REG_CYPRESS_CFR3V 0x00800004
+#define SPINOR_REG_CYPRESS_CFR3V_PGSZ BIT(4) /* Page size. */
+#define SPINOR_REG_CYPRESS_CFR5V 0x00800006
+#define SPINOR_REG_CYPRESS_CFR5V_OCT_DTR_EN 0x3
+#define SPINOR_REG_CYPRESS_CFR5V_OCT_DTR_DS 0
+#define SPINOR_OP_CYPRESS_RD_FAST 0xee
+
+/**
+ * spi_nor_cypress_octal_dtr_enable() - Enable octal DTR on Cypress flashes.
+ * @nor: pointer to a 'struct spi_nor'
+ *
+ * This also sets the memory access latency cycles to 24 to allow the flash to
+ * run at up to 200MHz.
+ *
+ * Return: 0 on success, -errno otherwise.
+ */
+static int spi_nor_cypress_octal_dtr_enable(struct spi_nor *nor, bool enable)
+{
+ struct spi_mem_op op;
+ u8 *buf = nor->bouncebuf;
+ u8 addr_width;
+ int ret;
+
+ if (enable)
+ addr_width = 3;
+ else
+ addr_width = 4;
+
+ if (enable) {
+ /* Use 24 dummy cycles for memory array reads. */
+ ret = spi_nor_write_enable(nor);
+ if (ret)
+ return ret;
+
+ *buf = SPINOR_REG_CYPRESS_CFR2V_MEMLAT_11_24;
+ op = (struct spi_mem_op)
+ SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WR_ANY_REG, 1),
+ SPI_MEM_OP_ADDR(addr_width,
+ SPINOR_REG_CYPRESS_CFR2V,
+ 1),
+ SPI_MEM_OP_NO_DUMMY,
+ SPI_MEM_OP_DATA_OUT(1, buf, 1));
+ ret = spi_mem_exec_op(nor->spimem, &op);
+ if (ret) {
+ dev_warn(nor->dev,
+ "failed to set default memory latency value: %d\n",
+ ret);
+ return ret;
+ }
+ ret = spi_nor_wait_till_ready(nor);
+ if (ret)
+ return ret;
+
+ nor->read_dummy = 24;
+ }
+
+ /* Set/unset the octal and DTR enable bits. */
+ ret = spi_nor_write_enable(nor);
+ if (ret)
+ return ret;
+
+ if (enable)
+ *buf = SPINOR_REG_CYPRESS_CFR5V_OCT_DTR_EN;
+ else
+ *buf = SPINOR_REG_CYPRESS_CFR5V_OCT_DTR_DS;
+ op = (struct spi_mem_op)
+ SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WR_ANY_REG, 1),
+ SPI_MEM_OP_ADDR(addr_width,
+ SPINOR_REG_CYPRESS_CFR5V,
+ 1),
+ SPI_MEM_OP_NO_DUMMY,
+ SPI_MEM_OP_DATA_OUT(1, buf, 1));
+
+ if (!enable)
+ spi_nor_spimem_setup_op(nor, &op, SNOR_PROTO_8_8_8_DTR);
+
+ ret = spi_mem_exec_op(nor->spimem, &op);
+ if (ret) {
+ dev_warn(nor->dev, "Failed to enable octal DTR mode\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static void s28hs512t_default_init(struct spi_nor *nor)
+{
+ nor->params->octal_dtr_enable = spi_nor_cypress_octal_dtr_enable;
+}
+
+static void s28hs512t_post_sfdp_fixup(struct spi_nor *nor)
+{
+ /*
+ * On older versions of the flash the xSPI Profile 1.0 table has the
+ * 8D-8D-8D Fast Read opcode as 0x00. But it actually should be 0xEE.
+ */
+ if (nor->params->reads[SNOR_CMD_READ_8_8_8_DTR].opcode == 0)
+ nor->params->reads[SNOR_CMD_READ_8_8_8_DTR].opcode =
+ SPINOR_OP_CYPRESS_RD_FAST;
+
+ nor->params->hwcaps.mask |= SNOR_HWCAPS_PP_8_8_8_DTR;
+
+ /* This flash is also missing the 4-byte Page Program opcode bit. */
+ spi_nor_set_pp_settings(&nor->params->page_programs[SNOR_CMD_PP],
+ SPINOR_OP_PP_4B, SNOR_PROTO_1_1_1);
+ /*
+ * Since xSPI Page Program opcode is backward compatible with
+ * Legacy SPI, use Legacy SPI opcode there as well.
+ */
+ spi_nor_set_pp_settings(&nor->params->page_programs[SNOR_CMD_PP_8_8_8_DTR],
+ SPINOR_OP_PP_4B, SNOR_PROTO_8_8_8_DTR);
+
+ /*
+ * The xSPI Profile 1.0 table advertises the number of additional
+ * address bytes needed for Read Status Register command as 0 but the
+ * actual value for that is 4.
+ */
+ nor->params->rdsr_addr_nbytes = 4;
+}
+
+static int s28hs512t_post_bfpt_fixup(struct spi_nor *nor,
+ const struct sfdp_parameter_header *bfpt_header,
+ const struct sfdp_bfpt *bfpt,
+ struct spi_nor_flash_parameter *params)
+{
+ struct spi_mem_op op;
+ u8 *buf = nor->bouncebuf;
+ u8 addr_width = 3;
+ int ret;
+
+ /*
+ * The BFPT table advertises a 512B page size but the page size is
+ * actually configurable (with the default being 256B). Read from
+ * CFR3V[4] and set the correct size.
+ */
+ op = (struct spi_mem_op)
+ SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RD_ANY_REG, 1),
+ SPI_MEM_OP_ADDR(addr_width, SPINOR_REG_CYPRESS_CFR3V, 1),
+ SPI_MEM_OP_NO_DUMMY,
+ SPI_MEM_OP_DATA_IN(1, buf, 1));
+ ret = spi_mem_exec_op(nor->spimem, &op);
+ if (ret)
+ return ret;
+
+ if (*buf & SPINOR_REG_CYPRESS_CFR3V_PGSZ)
+ params->page_size = 512;
+ else
+ params->page_size = 256;
+
+ return 0;
+}
+
+static struct spi_nor_fixups s28hs512t_fixups = {
+ .default_init = s28hs512t_default_init,
+ .post_sfdp = s28hs512t_post_sfdp_fixup,
+ .post_bfpt = s28hs512t_post_bfpt_fixup,
+};
+
static const struct flash_info spansion_parts[] = {
/* Spansion/Cypress -- single (large) sector size only, at least
* for the chips listed here (without boot sectors).
@@ -72,6 +235,10 @@ static const struct flash_info spansion_parts[] = {
{ "s25fl256l", INFO(0x016019, 0, 64 * 1024, 512,
SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ |
SPI_NOR_4B_OPCODES) },
+ { "s28hs512t", INFO(0x345b1a, 0, 256 * 1024, 256,
+ SECT_4K | SPI_NOR_OCTAL_DTR_READ)
+ .fixups = &s28hs512t_fixups,
+ },
};

static void spansion_post_sfdp_fixups(struct spi_nor *nor)
--
2.26.2

2020-05-19 14:32:23

by Pratyush Yadav

[permalink] [raw]
Subject: [PATCH v5 08/19] mtd: spi-nor: sfdp: get command opcode extension type from BFPT

Some devices in DTR mode expect an extra command byte called the
extension. The extension can either be same as the opcode, bitwise
inverse of the opcode, or another additional byte forming a 16-byte
opcode. Get the extension type from the BFPT. For now, only flashes with
"repeat" and "inverse" extensions are supported.

Signed-off-by: Pratyush Yadav <[email protected]>
---
drivers/mtd/spi-nor/sfdp.c | 17 +++++++++++++++++
drivers/mtd/spi-nor/sfdp.h | 6 ++++++
2 files changed, 23 insertions(+)

diff --git a/drivers/mtd/spi-nor/sfdp.c b/drivers/mtd/spi-nor/sfdp.c
index 96960f2f3d7a..ab086aa4746f 100644
--- a/drivers/mtd/spi-nor/sfdp.c
+++ b/drivers/mtd/spi-nor/sfdp.c
@@ -609,6 +609,23 @@ static int spi_nor_parse_bfpt(struct spi_nor *nor,
if (bfpt_header->length == BFPT_DWORD_MAX_JESD216B)
return spi_nor_post_bfpt_fixups(nor, bfpt_header, &bfpt,
params);
+ /* 8D-8D-8D command extension. */
+ switch (bfpt.dwords[BFPT_DWORD(18)] & BFPT_DWORD18_CMD_EXT_MASK) {
+ case BFPT_DWORD18_CMD_EXT_REP:
+ nor->cmd_ext_type = SPI_NOR_EXT_REPEAT;
+ break;
+
+ case BFPT_DWORD18_CMD_EXT_INV:
+ nor->cmd_ext_type = SPI_NOR_EXT_INVERT;
+ break;
+
+ case BFPT_DWORD18_CMD_EXT_RES:
+ return -EINVAL;
+
+ case BFPT_DWORD18_CMD_EXT_16B:
+ dev_err(nor->dev, "16-bit opcodes not supported\n");
+ return -ENOTSUPP;
+ }

return spi_nor_post_bfpt_fixups(nor, bfpt_header, &bfpt, params);
}
diff --git a/drivers/mtd/spi-nor/sfdp.h b/drivers/mtd/spi-nor/sfdp.h
index f8198af43a63..e15e30796d62 100644
--- a/drivers/mtd/spi-nor/sfdp.h
+++ b/drivers/mtd/spi-nor/sfdp.h
@@ -84,6 +84,12 @@ struct sfdp_bfpt {
#define BFPT_DWORD15_QER_SR2_BIT1_NO_RD (0x4UL << 20)
#define BFPT_DWORD15_QER_SR2_BIT1 (0x5UL << 20) /* Spansion */

+#define BFPT_DWORD18_CMD_EXT_MASK GENMASK(30, 29)
+#define BFPT_DWORD18_CMD_EXT_REP (0x0UL << 29) /* Repeat */
+#define BFPT_DWORD18_CMD_EXT_INV (0x1UL << 29) /* Invert */
+#define BFPT_DWORD18_CMD_EXT_RES (0x2UL << 29) /* Reserved */
+#define BFPT_DWORD18_CMD_EXT_16B (0x3UL << 29) /* 16-bit opcode */
+
struct sfdp_parameter_header {
u8 id_lsb;
u8 minor;
--
2.26.2

2020-05-19 14:32:45

by Pratyush Yadav

[permalink] [raw]
Subject: [PATCH v5 13/19] mtd: spi-nor: sfdp: do not make invalid quad enable fatal

The Micron MT35XU512ABA flash does not support the quad enable bit. But
instead of programming the Quad Enable Require field to 000b ("Device
does not have a QE bit"), it is programmed to 111b ("Reserved").

While this is technically incorrect, it is not reason enough to abort
BFPT parsing. Instead, continue BFPT parsing assuming there is no quad
enable bit present.

Signed-off-by: Pratyush Yadav <[email protected]>
---
drivers/mtd/spi-nor/sfdp.c | 8 +++-----
1 file changed, 3 insertions(+), 5 deletions(-)

diff --git a/drivers/mtd/spi-nor/sfdp.c b/drivers/mtd/spi-nor/sfdp.c
index 4e5e0eabe2d9..be9c6c3d6590 100644
--- a/drivers/mtd/spi-nor/sfdp.c
+++ b/drivers/mtd/spi-nor/sfdp.c
@@ -569,10 +569,6 @@ static int spi_nor_parse_bfpt(struct spi_nor *nor,

/* Quad Enable Requirements. */
switch (bfpt.dwords[BFPT_DWORD(15)] & BFPT_DWORD15_QER_MASK) {
- case BFPT_DWORD15_QER_NONE:
- params->quad_enable = NULL;
- break;
-
case BFPT_DWORD15_QER_SR2_BIT1_BUGGY:
/*
* Writing only one byte to the Status Register has the
@@ -609,8 +605,10 @@ static int spi_nor_parse_bfpt(struct spi_nor *nor,
params->quad_enable = spi_nor_sr2_bit1_quad_enable;
break;

+ case BFPT_DWORD15_QER_NONE:
default:
- return -EINVAL;
+ params->quad_enable = NULL;
+ break;
}

/* Stop here if JESD216 rev B. */
--
2.26.2

2020-05-19 14:33:25

by Pratyush Yadav

[permalink] [raw]
Subject: [PATCH v5 15/19] mtd: spi-nor: core: perform a Soft Reset on shutdown

Perform a Soft Reset on shutdown on flashes that support it so that the
flash can be reset to its initial state and any configurations made by
spi-nor (given that they're only done in volatile registers) will be
reset. This will hand back the flash in pristine state for any further
operations on it.

Signed-off-by: Pratyush Yadav <[email protected]>
---
drivers/mtd/spi-nor/core.c | 42 +++++++++++++++++++++++++++++++++++++
include/linux/mtd/spi-nor.h | 2 ++
2 files changed, 44 insertions(+)

diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c
index a94376344be5..68559386f6f8 100644
--- a/drivers/mtd/spi-nor/core.c
+++ b/drivers/mtd/spi-nor/core.c
@@ -40,6 +40,9 @@

#define SPI_NOR_MAX_ADDR_WIDTH 4

+#define SPI_NOR_SRST_SLEEP_MIN 200
+#define SPI_NOR_SRST_SLEEP_MAX 400
+
/**
* spi_nor_get_cmd_ext() - Get the command opcode extension based on the
* extension type.
@@ -3201,6 +3204,41 @@ static int spi_nor_init(struct spi_nor *nor)
return 0;
}

+static void spi_nor_soft_reset(struct spi_nor *nor)
+{
+ struct spi_mem_op op;
+ int ret;
+
+ op = (struct spi_mem_op)SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_SRSTEN, 8),
+ SPI_MEM_OP_NO_DUMMY,
+ SPI_MEM_OP_NO_ADDR,
+ SPI_MEM_OP_NO_DATA);
+ spi_nor_spimem_setup_op(nor, &op, SNOR_PROTO_8_8_8_DTR);
+ ret = spi_mem_exec_op(nor->spimem, &op);
+ if (ret) {
+ dev_warn(nor->dev, "Software reset failed: %d\n", ret);
+ return;
+ }
+
+ op = (struct spi_mem_op)SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_SRST, 8),
+ SPI_MEM_OP_NO_DUMMY,
+ SPI_MEM_OP_NO_ADDR,
+ SPI_MEM_OP_NO_DATA);
+ spi_nor_spimem_setup_op(nor, &op, SNOR_PROTO_8_8_8_DTR);
+ ret = spi_mem_exec_op(nor->spimem, &op);
+ if (ret) {
+ dev_warn(nor->dev, "Software reset failed: %d\n", ret);
+ return;
+ }
+
+ /*
+ * Software Reset is not instant, and the delay varies from flash to
+ * flash. Looking at a few flashes, most range somewhere below 100
+ * microseconds. So, sleep for a range of 200-400 us.
+ */
+ usleep_range(SPI_NOR_SRST_SLEEP_MIN, SPI_NOR_SRST_SLEEP_MAX);
+}
+
/* mtd resume handler */
static void spi_nor_resume(struct mtd_info *mtd)
{
@@ -3220,6 +3258,10 @@ void spi_nor_restore(struct spi_nor *nor)
if (nor->addr_width == 4 && !(nor->flags & SNOR_F_4B_OPCODES) &&
nor->flags & SNOR_F_BROKEN_RESET)
nor->params->set_4byte_addr_mode(nor, false);
+
+ if (nor->info->flags & SPI_NOR_OCTAL_DTR_READ &&
+ nor->flags & SNOR_F_SOFT_RESET)
+ spi_nor_soft_reset(nor);
}
EXPORT_SYMBOL_GPL(spi_nor_restore);

diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h
index d251a5d02be2..06884a188315 100644
--- a/include/linux/mtd/spi-nor.h
+++ b/include/linux/mtd/spi-nor.h
@@ -51,6 +51,8 @@
#define SPINOR_OP_CLFSR 0x50 /* Clear flag status register */
#define SPINOR_OP_RDEAR 0xc8 /* Read Extended Address Register */
#define SPINOR_OP_WREAR 0xc5 /* Write Extended Address Register */
+#define SPINOR_OP_SRSTEN 0x66 /* Software Reset Enable */
+#define SPINOR_OP_SRST 0x99 /* Software Reset */

/* 4-byte address opcodes - used on Spansion and some Macronix flashes. */
#define SPINOR_OP_READ_4B 0x13 /* Read data bytes (low frequency) */
--
2.26.2

2020-05-19 14:33:25

by Pratyush Yadav

[permalink] [raw]
Subject: [PATCH v5 16/19] mtd: spi-nor: core: disable Octal DTR mode on suspend.

On resume, the init procedure will be run that will re-enable it.

Signed-off-by: Pratyush Yadav <[email protected]>
---
drivers/mtd/spi-nor/core.c | 18 ++++++++++++++++++
1 file changed, 18 insertions(+)

diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c
index 68559386f6f8..63ab588299f4 100644
--- a/drivers/mtd/spi-nor/core.c
+++ b/drivers/mtd/spi-nor/core.c
@@ -3239,6 +3239,23 @@ static void spi_nor_soft_reset(struct spi_nor *nor)
usleep_range(SPI_NOR_SRST_SLEEP_MIN, SPI_NOR_SRST_SLEEP_MAX);
}

+/* mtd suspend handler */
+static int spi_nor_suspend(struct mtd_info *mtd)
+{
+ struct spi_nor *nor = mtd_to_spi_nor(mtd);
+ struct device *dev = nor->dev;
+ int ret;
+
+ /* Disable octal DTR mode if we enabled it. */
+ ret = spi_nor_octal_dtr_enable(nor, false);
+ if (ret) {
+ dev_err(dev, "suspend() failed\n");
+ return ret;
+ }
+
+ return 0;
+}
+
/* mtd resume handler */
static void spi_nor_resume(struct mtd_info *mtd)
{
@@ -3432,6 +3449,7 @@ int spi_nor_scan(struct spi_nor *nor, const char *name,
mtd->size = nor->params->size;
mtd->_erase = spi_nor_erase;
mtd->_read = spi_nor_read;
+ mtd->_suspend = spi_nor_suspend;
mtd->_resume = spi_nor_resume;

if (nor->params->locking_ops) {
--
2.26.2

2020-05-19 14:33:41

by Pratyush Yadav

[permalink] [raw]
Subject: [PATCH v5 19/19] mtd: spi-nor: micron-st: allow using MT35XU512ABA in Octal DTR mode

Since this flash doesn't have a Profile 1.0 table, the Octal DTR
capabilities are enabled in the post SFDP fixup, along with the 8D-8D-8D
fast read settings.

Enable Octal DTR mode with 20 dummy cycles to allow running at the
maximum supported frequency of 200Mhz.

The flash supports the soft reset sequence. So, add the flag in the
flash's info.

Signed-off-by: Pratyush Yadav <[email protected]>
---
drivers/mtd/spi-nor/micron-st.c | 112 +++++++++++++++++++++++++++++++-
1 file changed, 111 insertions(+), 1 deletion(-)

diff --git a/drivers/mtd/spi-nor/micron-st.c b/drivers/mtd/spi-nor/micron-st.c
index 3dca5b9af3b6..3414c44a5c96 100644
--- a/drivers/mtd/spi-nor/micron-st.c
+++ b/drivers/mtd/spi-nor/micron-st.c
@@ -8,10 +8,120 @@

#include "core.h"

+#define SPINOR_OP_MT_DTR_RD 0xfd /* Fast Read opcode in DTR mode */
+#define SPINOR_OP_MT_RD_ANY_REG 0x85 /* Read volatile register */
+#define SPINOR_OP_MT_WR_ANY_REG 0x81 /* Write volatile register */
+#define SPINOR_REG_MT_CFR0V 0x00 /* For setting octal DTR mode */
+#define SPINOR_REG_MT_CFR1V 0x01 /* For setting dummy cycles */
+#define SPINOR_MT_DTR_NO_DQS 0xc7 /* Enable Octal DTR without DQS. */
+#define SPINOR_MT_EXSPI 0xff /* Enable Extended SPI (default) */
+
+static int spi_nor_micron_octal_dtr_enable(struct spi_nor *nor, bool enable)
+{
+ struct spi_mem_op op;
+ u8 *buf = nor->bouncebuf;
+ u8 addr_width;
+ int ret;
+
+ if (enable)
+ addr_width = 3;
+ else
+ addr_width = 4;
+
+ ret = spi_nor_write_enable(nor);
+ if (ret)
+ return ret;
+
+ if (enable)
+ *buf = SPINOR_MT_DTR_NO_DQS;
+ else
+ *buf = SPINOR_MT_EXSPI;
+ op = (struct spi_mem_op)
+ SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_MT_WR_ANY_REG, 1),
+ SPI_MEM_OP_ADDR(addr_width, SPINOR_REG_MT_CFR0V, 1),
+ SPI_MEM_OP_NO_DUMMY,
+ SPI_MEM_OP_DATA_OUT(1, buf, 1));
+
+ if (!enable)
+ spi_nor_spimem_setup_op(nor, &op, SNOR_PROTO_8_8_8_DTR);
+
+ ret = spi_mem_exec_op(nor->spimem, &op);
+ if (ret) {
+ dev_err(nor->dev, "Failed to enable octal DTR mode\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int mt35xu512aba_setup(struct spi_nor *nor,
+ const struct spi_nor_hwcaps *hwcaps)
+{
+ struct spi_mem_op op;
+ u8 *buf = nor->bouncebuf;
+ u8 addr_width = 3;
+ int ret;
+
+ if (!nor->spimem) {
+ dev_err(nor->dev,
+ "operation not supported for non-spimem drivers\n");
+ return -ENOTSUPP;
+ }
+
+ /* Set dummy cycles for Fast Read to the default of 20. */
+ ret = spi_nor_write_enable(nor);
+ if (ret)
+ return ret;
+
+ *buf = 20;
+ op = (struct spi_mem_op)
+ SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_MT_WR_ANY_REG, 1),
+ SPI_MEM_OP_ADDR(addr_width, SPINOR_REG_MT_CFR1V, 1),
+ SPI_MEM_OP_NO_DUMMY,
+ SPI_MEM_OP_DATA_OUT(1, buf, 1));
+ ret = spi_mem_exec_op(nor->spimem, &op);
+ if (ret)
+ return ret;
+
+ ret = spi_nor_wait_till_ready(nor);
+ if (ret)
+ return ret;
+
+
+ return spi_nor_default_setup(nor, hwcaps);
+}
+
+static void mt35xu512aba_default_init(struct spi_nor *nor)
+{
+ nor->params->octal_dtr_enable = spi_nor_micron_octal_dtr_enable;
+ nor->params->setup = mt35xu512aba_setup;
+}
+
+static void mt35xu512aba_post_sfdp_fixup(struct spi_nor *nor)
+{
+ /* Set the Fast Read settings. */
+ nor->params->hwcaps.mask |= SNOR_HWCAPS_READ_8_8_8_DTR;
+ spi_nor_set_read_settings(&nor->params->reads[SNOR_CMD_READ_8_8_8_DTR],
+ 0, 20, SPINOR_OP_MT_DTR_RD,
+ SNOR_PROTO_8_8_8_DTR);
+
+ nor->params->hwcaps.mask |= SNOR_HWCAPS_PP_8_8_8_DTR;
+
+ nor->cmd_ext_type = SPI_NOR_EXT_REPEAT;
+ nor->params->rdsr_dummy = 8;
+ nor->params->rdsr_addr_nbytes = 0;
+}
+
+static struct spi_nor_fixups mt35xu512aba_fixups = {
+ .default_init = mt35xu512aba_default_init,
+ .post_sfdp = mt35xu512aba_post_sfdp_fixup,
+};
+
static const struct flash_info micron_parts[] = {
{ "mt35xu512aba", INFO(0x2c5b1a, 0, 128 * 1024, 512,
SECT_4K | USE_FSR | SPI_NOR_OCTAL_READ |
- SPI_NOR_4B_OPCODES) },
+ SPI_NOR_4B_OPCODES | SPI_NOR_OCTAL_DTR_READ)
+ .fixups = &mt35xu512aba_fixups},
{ "mt35xu02g", INFO(0x2c5b1c, 0, 128 * 1024, 2048,
SECT_4K | USE_FSR | SPI_NOR_OCTAL_READ |
SPI_NOR_4B_OPCODES) },
--
2.26.2

2020-05-20 08:01:39

by Mason Yang

[permalink] [raw]
Subject: Re: [PATCH v5 09/19] mtd: spi-nor: sfdp: parse xSPI Profile 1.0 table


Hi Pratyush,

> +/**
> + * spi_nor_parse_profile1() - parse the xSPI Profile 1.0 table
> + * @nor: pointer to a 'struct spi_nor'
> + * @param_header: pointer to the 'struct sfdp_parameter_header'
describing
> + * the 4-Byte Address Instruction Table length and version.
> + * @params: pointer to the 'struct spi_nor_flash_parameter' to be.
> + *
> + * Return: 0 on success, -errno otherwise.
> + */
> +static int spi_nor_parse_profile1(struct spi_nor *nor,
> + const struct sfdp_parameter_header *profile1_header,
> + struct spi_nor_flash_parameter *params)
> +{
> + u32 *table, opcode, addr;
> + size_t len;
> + int ret, i;
> +
> + len = profile1_header->length * sizeof(*table);
> + table = kmalloc(len, GFP_KERNEL);
> + if (!table)
> + return -ENOMEM;
> +
> + addr = SFDP_PARAM_HEADER_PTP(profile1_header);
> + ret = spi_nor_read_sfdp(nor, addr, len, table);
> + if (ret)
> + goto out;
> +
> + /* Fix endianness of the table DWORDs. */
> + for (i = 0; i < profile1_header->length; i++)
> + table[i] = le32_to_cpu(table[i]);
> +
> + /* Get 8D-8D-8D fast read opcode and dummy cycles. */
> + opcode = FIELD_GET(PROFILE1_DWORD1_RD_FAST_CMD, table[0]);
> +
> + /*
> + * Update the fast read settings. We set the default dummy cycles to
20
> + * here. Flashes can change this value if they need to when enabling
> + * octal mode.
> + */
> + spi_nor_set_read_settings(&params->reads[SNOR_CMD_READ_8_8_8_DTR],
> + 0, 20, opcode,
> + SNOR_PROTO_8_8_8_DTR);
> +


I thought we have a agreement that only do parse here, no other read
parameters setting.

Driver should get dummy cycles used for various frequencies
from 4th and 5th DWORD of xSPI table.[1]

[1]
https://patchwork.ozlabs.org/project/linux-mtd/patch/[email protected]/


In addition, 20 dummy cycles is for 200MHz but not for 100MHz, 133MHz and
166MHz
in case of read performance concern.

Given a correct dummy cycles for a specific device. [2]

[2]
https://patchwork.ozlabs.org/project/linux-mtd/patch/[email protected]/



> + /*
> + * Set the Read Status Register dummy cycles and dummy address
bytes.
> + */
> + if (table[0] & PROFILE1_DWORD1_RDSR_DUMMY)
> + params->rdsr_dummy = 8;
> + else
> + params->rdsr_dummy = 4;
> +
> + if (table[0] & PROFILE1_DWORD1_RDSR_ADDR_BYTES)
> + params->rdsr_addr_nbytes = 4;
> + else
> + params->rdsr_addr_nbytes = 0;
> +
> +out:
> + kfree(table);
> + return ret;
> +}
> +

thanks & best regards,
Mason


CONFIDENTIALITY NOTE:

This e-mail and any attachments may contain confidential information
and/or personal data, which is protected by applicable laws. Please be
reminded that duplication, disclosure, distribution, or use of this e-mail
(and/or its attachments) or any part thereof is prohibited. If you receive
this e-mail in error, please notify us immediately and delete this mail as
well as its attachment(s) from your system. In addition, please be
informed that collection, processing, and/or use of personal data is
prohibited unless expressly permitted by personal data protection laws.
Thank you for your attention and cooperation.

Macronix International Co., Ltd.

=====================================================================



============================================================================

CONFIDENTIALITY NOTE:

This e-mail and any attachments may contain confidential information and/or personal data, which is protected by applicable laws. Please be reminded that duplication, disclosure, distribution, or use of this e-mail (and/or its attachments) or any part thereof is prohibited. If you receive this e-mail in error, please notify us immediately and delete this mail as well as its attachment(s) from your system. In addition, please be informed that collection, processing, and/or use of personal data is prohibited unless expressly permitted by personal data protection laws. Thank you for your attention and cooperation.

Macronix International Co., Ltd.

=====================================================================

2020-05-20 08:59:47

by Pratyush Yadav

[permalink] [raw]
Subject: Re: [PATCH v5 09/19] mtd: spi-nor: sfdp: parse xSPI Profile 1.0 table

Hi Mason,

On 20/05/20 03:59PM, [email protected] wrote:
>
> Hi Pratyush,
>
> > +/**
> > + * spi_nor_parse_profile1() - parse the xSPI Profile 1.0 table
> > + * @nor: pointer to a 'struct spi_nor'
> > + * @param_header: pointer to the 'struct sfdp_parameter_header'
> describing
> > + * the 4-Byte Address Instruction Table length and version.
> > + * @params: pointer to the 'struct spi_nor_flash_parameter' to be.
> > + *
> > + * Return: 0 on success, -errno otherwise.
> > + */
> > +static int spi_nor_parse_profile1(struct spi_nor *nor,
> > + const struct sfdp_parameter_header *profile1_header,
> > + struct spi_nor_flash_parameter *params)
> > +{
> > + u32 *table, opcode, addr;
> > + size_t len;
> > + int ret, i;
> > +
> > + len = profile1_header->length * sizeof(*table);
> > + table = kmalloc(len, GFP_KERNEL);
> > + if (!table)
> > + return -ENOMEM;
> > +
> > + addr = SFDP_PARAM_HEADER_PTP(profile1_header);
> > + ret = spi_nor_read_sfdp(nor, addr, len, table);
> > + if (ret)
> > + goto out;
> > +
> > + /* Fix endianness of the table DWORDs. */
> > + for (i = 0; i < profile1_header->length; i++)
> > + table[i] = le32_to_cpu(table[i]);
> > +
> > + /* Get 8D-8D-8D fast read opcode and dummy cycles. */
> > + opcode = FIELD_GET(PROFILE1_DWORD1_RD_FAST_CMD, table[0]);
> > +
> > + /*
> > + * Update the fast read settings. We set the default dummy cycles to
> 20
> > + * here. Flashes can change this value if they need to when enabling
> > + * octal mode.
> > + */
> > + spi_nor_set_read_settings(&params->reads[SNOR_CMD_READ_8_8_8_DTR],
> > + 0, 20, opcode,
> > + SNOR_PROTO_8_8_8_DTR);
> > +
>
>
> I thought we have a agreement that only do parse here, no other read
> parameters setting.

Yes, and I considered it. But it didn't make much sense to me to
introduce an extra member in struct spi_nor just to make this call in
some other function later.

Why exactly do you think doing this here is bad? The way I see it, we
avoid carrying around an extra member in spi_nor and this also allows
flashes to change the read settings easily in a post-sfdp hook. The
4bait parsing function does something similar.

What are the benefits of doing it otherwise?

Note that I did remove HWCAPS selection from here, which did seem like a
sane idea.

> Driver should get dummy cycles used for various frequencies
> from 4th and 5th DWORD of xSPI table.[1]
>
> [1]
> https://patchwork.ozlabs.org/project/linux-mtd/patch/[email protected]/
>
>
> In addition, 20 dummy cycles is for 200MHz but not for 100MHz, 133MHz and
> 166MHz
> in case of read performance concern.
>
> Given a correct dummy cycles for a specific device. [2]
>
> [2]
> https://patchwork.ozlabs.org/project/linux-mtd/patch/[email protected]/

The problem is that we don't know what speed the controller is driving
the flash at, and whether it is using Data Strobe. BFPT tells us the
maximum speed of the flash based on if Data Strobe is being used. The
controller can also drive it slower than the maximum. And it can drive
it with or without DS.

So, we have to be conservative and just use the dummy cycles for the
maximum speed so we can at least make sure the flash works, albeit at
slightly less efficiency. I hard-coded it to 20 but I suppose we can
find it out from the Profile 1.0 table and use that (though we'd have to
round it to an even value to avoid tripping up controllers). Will fix in
next version (or, Tudor if you're fine with fixup! patches, I can send
that too because I suspect it will be a small change).

>
> > + /*
> > + * Set the Read Status Register dummy cycles and dummy address
> bytes.
> > + */
> > + if (table[0] & PROFILE1_DWORD1_RDSR_DUMMY)
> > + params->rdsr_dummy = 8;
> > + else
> > + params->rdsr_dummy = 4;
> > +
> > + if (table[0] & PROFILE1_DWORD1_RDSR_ADDR_BYTES)
> > + params->rdsr_addr_nbytes = 4;
> > + else
> > + params->rdsr_addr_nbytes = 0;
> > +
> > +out:
> > + kfree(table);
> > + return ret;
> > +}
> > +
>

--
Regards,
Pratyush Yadav
Texas Instruments India

2020-05-20 09:42:30

by Mason Yang

[permalink] [raw]
Subject: Re: [PATCH v5 09/19] mtd: spi-nor: sfdp: parse xSPI Profile 1.0 table


Hi Pratyush,

> > > +/**
> > > + * spi_nor_parse_profile1() - parse the xSPI Profile 1.0 table
> > > + * @nor: pointer to a 'struct spi_nor'
> > > + * @param_header: pointer to the 'struct sfdp_parameter_header'
> > describing
> > > + * the 4-Byte Address Instruction Table length and version.
> > > + * @params: pointer to the 'struct spi_nor_flash_parameter' to
be.
> > > + *
> > > + * Return: 0 on success, -errno otherwise.
> > > + */
> > > +static int spi_nor_parse_profile1(struct spi_nor *nor,
> > > + const struct sfdp_parameter_header *profile1_header,
> > > + struct spi_nor_flash_parameter *params)
> > > +{
> > > + u32 *table, opcode, addr;
> > > + size_t len;
> > > + int ret, i;
> > > +
> > > + len = profile1_header->length * sizeof(*table);
> > > + table = kmalloc(len, GFP_KERNEL);
> > > + if (!table)
> > > + return -ENOMEM;
> > > +
> > > + addr = SFDP_PARAM_HEADER_PTP(profile1_header);
> > > + ret = spi_nor_read_sfdp(nor, addr, len, table);
> > > + if (ret)
> > > + goto out;
> > > +
> > > + /* Fix endianness of the table DWORDs. */
> > > + for (i = 0; i < profile1_header->length; i++)
> > > + table[i] = le32_to_cpu(table[i]);
> > > +
> > > + /* Get 8D-8D-8D fast read opcode and dummy cycles. */
> > > + opcode = FIELD_GET(PROFILE1_DWORD1_RD_FAST_CMD, table[0]);
> > > +
> > > + /*
> > > + * Update the fast read settings. We set the default dummy
cycles to
> > 20
> > > + * here. Flashes can change this value if they need to when
enabling
> > > + * octal mode.
> > > + */
> > > + spi_nor_set_read_settings(&params->reads[SNOR_CMD_READ_8_8_8_DTR],
> > > + 0, 20, opcode,
> > > + SNOR_PROTO_8_8_8_DTR);
> > > +
> >
> >
> > I thought we have a agreement that only do parse here, no other read
> > parameters setting.
>
> Yes, and I considered it. But it didn't make much sense to me to
> introduce an extra member in struct spi_nor just to make this call in
> some other function later.
>
> Why exactly do you think doing this here is bad? The way I see it, we
> avoid carrying around an extra member in spi_nor and this also allows
> flashes to change the read settings easily in a post-sfdp hook. The
> 4bait parsing function does something similar.

I think it's not a question for good or bad.

4bait parsing function parse the 4-Byte Address Instruction Table
and set up read/pp parameters there for sure.

Here we give the function name spi_nor_parse_profile1() but also
do others setting that has nothing to do with it,
it seems not good for SW module design.
oh, it's my humble opinion.


>
> What are the benefits of doing it otherwise?

For other Octal Flash like mx25*

>
> Note that I did remove HWCAPS selection from here, which did seem like a

> sane idea.
>
> > Driver should get dummy cycles used for various frequencies
> > from 4th and 5th DWORD of xSPI table.[1]
> >
> > [1]
> >
https://patchwork.ozlabs.org/project/linux-mtd/patch/1587451187-6889-3-git-

> [email protected]/
> >
> >
> > In addition, 20 dummy cycles is for 200MHz but not for 100MHz, 133MHz
and
> > 166MHz
> > in case of read performance concern.
> >
> > Given a correct dummy cycles for a specific device. [2]
> >
> > [2]
> >
https://patchwork.ozlabs.org/project/linux-mtd/patch/1587451187-6889-5-git-

> [email protected]/
>
> The problem is that we don't know what speed the controller is driving
> the flash at, and whether it is using Data Strobe. BFPT tells us the
> maximum speed of the flash based on if Data Strobe is being used. The
> controller can also drive it slower than the maximum. And it can drive
> it with or without DS.

This is for flash, not every Octal flash could work in 200MHz,
The Max operation speeds for other Octal Flash is 100, 133 , or 166MHz.

If a specific Octal Flash could work in 166MHz(Max), and driver setup the
correct 16 dummy cycles for it rather than 20 dummy cycles.
it's for performance concern.

>
> So, we have to be conservative and just use the dummy cycles for the
> maximum speed so we can at least make sure the flash works, albeit at
> slightly less efficiency. I hard-coded it to 20 but I suppose we can
> find it out from the Profile 1.0 table and use that (though we'd have to

> round it to an even value to avoid tripping up controllers). Will fix in

> next version (or, Tudor if you're fine with fixup! patches, I can send
> that too because I suspect it will be a small change).
>
> >

thanks & best regards,
Mason


CONFIDENTIALITY NOTE:

This e-mail and any attachments may contain confidential information
and/or personal data, which is protected by applicable laws. Please be
reminded that duplication, disclosure, distribution, or use of this e-mail
(and/or its attachments) or any part thereof is prohibited. If you receive
this e-mail in error, please notify us immediately and delete this mail as
well as its attachment(s) from your system. In addition, please be
informed that collection, processing, and/or use of personal data is
prohibited unless expressly permitted by personal data protection laws.
Thank you for your attention and cooperation.

Macronix International Co., Ltd.

=====================================================================



============================================================================

CONFIDENTIALITY NOTE:

This e-mail and any attachments may contain confidential information and/or personal data, which is protected by applicable laws. Please be reminded that duplication, disclosure, distribution, or use of this e-mail (and/or its attachments) or any part thereof is prohibited. If you receive this e-mail in error, please notify us immediately and delete this mail as well as its attachment(s) from your system. In addition, please be informed that collection, processing, and/or use of personal data is prohibited unless expressly permitted by personal data protection laws. Thank you for your attention and cooperation.

Macronix International Co., Ltd.

=====================================================================

2020-05-20 10:42:30

by Pratyush Yadav

[permalink] [raw]
Subject: Re: [PATCH v5 09/19] mtd: spi-nor: sfdp: parse xSPI Profile 1.0 table

On 20/05/20 05:40PM, [email protected] wrote:
>
> Hi Pratyush,
>
> > > > +/**
> > > > + * spi_nor_parse_profile1() - parse the xSPI Profile 1.0 table
> > > > + * @nor: pointer to a 'struct spi_nor'
> > > > + * @param_header: pointer to the 'struct sfdp_parameter_header'
> > > describing
> > > > + * the 4-Byte Address Instruction Table length and version.
> > > > + * @params: pointer to the 'struct spi_nor_flash_parameter' to
> be.
> > > > + *
> > > > + * Return: 0 on success, -errno otherwise.
> > > > + */
> > > > +static int spi_nor_parse_profile1(struct spi_nor *nor,
> > > > + const struct sfdp_parameter_header *profile1_header,
> > > > + struct spi_nor_flash_parameter *params)
> > > > +{
> > > > + u32 *table, opcode, addr;
> > > > + size_t len;
> > > > + int ret, i;
> > > > +
> > > > + len = profile1_header->length * sizeof(*table);
> > > > + table = kmalloc(len, GFP_KERNEL);
> > > > + if (!table)
> > > > + return -ENOMEM;
> > > > +
> > > > + addr = SFDP_PARAM_HEADER_PTP(profile1_header);
> > > > + ret = spi_nor_read_sfdp(nor, addr, len, table);
> > > > + if (ret)
> > > > + goto out;
> > > > +
> > > > + /* Fix endianness of the table DWORDs. */
> > > > + for (i = 0; i < profile1_header->length; i++)
> > > > + table[i] = le32_to_cpu(table[i]);
> > > > +
> > > > + /* Get 8D-8D-8D fast read opcode and dummy cycles. */
> > > > + opcode = FIELD_GET(PROFILE1_DWORD1_RD_FAST_CMD, table[0]);
> > > > +
> > > > + /*
> > > > + * Update the fast read settings. We set the default dummy
> cycles to
> > > 20
> > > > + * here. Flashes can change this value if they need to when
> enabling
> > > > + * octal mode.
> > > > + */
> > > > + spi_nor_set_read_settings(&params->reads[SNOR_CMD_READ_8_8_8_DTR],
> > > > + 0, 20, opcode,
> > > > + SNOR_PROTO_8_8_8_DTR);
> > > > +
> > >
> > >
> > > I thought we have a agreement that only do parse here, no other read
> > > parameters setting.
> >
> > Yes, and I considered it. But it didn't make much sense to me to
> > introduce an extra member in struct spi_nor just to make this call in
> > some other function later.
> >
> > Why exactly do you think doing this here is bad? The way I see it, we
> > avoid carrying around an extra member in spi_nor and this also allows
> > flashes to change the read settings easily in a post-sfdp hook. The
> > 4bait parsing function does something similar.
>
> I think it's not a question for good or bad.
>
> 4bait parsing function parse the 4-Byte Address Instruction Table
> and set up read/pp parameters there for sure.
>
> Here we give the function name spi_nor_parse_profile1() but also

But the function that parses 4bait table is also called
spi_nor_parse_4bait().

> do others setting that has nothing to do with it,

Why has setting read opcode and dummy cycles got nothing to do with it?
The purpose of the Profile 1.0 table is to tell us the Read Fast command
and dummy cycles, among other things. I think it _does_ have something
to do with it.

Just like the 4bait table tells us the 4-byte opcodes and we set them up
in our data structures, the profile 1.0 table tells us the 8D read
opcode and dummy cycles, and we set them up in our data structures.

> it seems not good for SW module design.
> oh, it's my humble opinion.
>
> >
> > What are the benefits of doing it otherwise?
>
> For other Octal Flash like mx25*

I mean from a design perspective. How does it make the code better, or
the job of people who need to read/change it easier?

> >
> > Note that I did remove HWCAPS selection from here, which did seem like a
>
> > sane idea.
> >
> > > Driver should get dummy cycles used for various frequencies
> > > from 4th and 5th DWORD of xSPI table.[1]
> > >
> > > [1]
> > >
> https://patchwork.ozlabs.org/project/linux-mtd/patch/1587451187-6889-3-git-
>
> > [email protected]/
> > >
> > >
> > > In addition, 20 dummy cycles is for 200MHz but not for 100MHz, 133MHz
> and
> > > 166MHz
> > > in case of read performance concern.
> > >
> > > Given a correct dummy cycles for a specific device. [2]
> > >
> > > [2]
> > >
> https://patchwork.ozlabs.org/project/linux-mtd/patch/1587451187-6889-5-git-
>
> > [email protected]/
> >
> > The problem is that we don't know what speed the controller is driving
> > the flash at, and whether it is using Data Strobe. BFPT tells us the
> > maximum speed of the flash based on if Data Strobe is being used. The
> > controller can also drive it slower than the maximum. And it can drive
> > it with or without DS.
>
> This is for flash, not every Octal flash could work in 200MHz,
> The Max operation speeds for other Octal Flash is 100, 133 , or 166MHz.
>
> If a specific Octal Flash could work in 166MHz(Max), and driver setup the
> correct 16 dummy cycles for it rather than 20 dummy cycles.
> it's for performance concern.

Agreed. Like I mentioned in the next paragraph, will fix.

> >
> > So, we have to be conservative and just use the dummy cycles for the
> > maximum speed so we can at least make sure the flash works, albeit at
> > slightly less efficiency. I hard-coded it to 20 but I suppose we can
> > find it out from the Profile 1.0 table and use that (though we'd have to
>
> > round it to an even value to avoid tripping up controllers). Will fix in
>
> > next version (or, Tudor if you're fine with fixup! patches, I can send
> > that too because I suspect it will be a small change).
> >

--
Regards,
Pratyush Yadav
Texas Instruments India

2020-05-21 08:11:47

by Mason Yang

[permalink] [raw]
Subject: Re: [PATCH v5 09/19] mtd: spi-nor: sfdp: parse xSPI Profile 1.0 table


Hi Pratyush,

> >
> > > > > +/**
> > > > > + * spi_nor_parse_profile1() - parse the xSPI Profile 1.0 table
> > > > > + * @nor: pointer to a 'struct spi_nor'
> > > > > + * @param_header: pointer to the 'struct
sfdp_parameter_header'
> > > > describing
> > > > > + * the 4-Byte Address Instruction Table length and
version.
> > > > > + * @params: pointer to the 'struct
spi_nor_flash_parameter' to
> > be.
> > > > > + *
> > > > > + * Return: 0 on success, -errno otherwise.
> > > > > + */
> > > > > +static int spi_nor_parse_profile1(struct spi_nor *nor,
> > > > > + const struct sfdp_parameter_header
*profile1_header,
> > > > > + struct spi_nor_flash_parameter *params)
> > > > > +{
> > > > > + u32 *table, opcode, addr;
> > > > > + size_t len;
> > > > > + int ret, i;
> > > > > +
> > > > > + len = profile1_header->length * sizeof(*table);
> > > > > + table = kmalloc(len, GFP_KERNEL);
> > > > > + if (!table)
> > > > > + return -ENOMEM;
> > > > > +
> > > > > + addr = SFDP_PARAM_HEADER_PTP(profile1_header);
> > > > > + ret = spi_nor_read_sfdp(nor, addr, len, table);
> > > > > + if (ret)
> > > > > + goto out;
> > > > > +
> > > > > + /* Fix endianness of the table DWORDs. */
> > > > > + for (i = 0; i < profile1_header->length; i++)
> > > > > + table[i] = le32_to_cpu(table[i]);
> > > > > +
> > > > > + /* Get 8D-8D-8D fast read opcode and dummy cycles. */
> > > > > + opcode = FIELD_GET(PROFILE1_DWORD1_RD_FAST_CMD, table[0]);
> > > > > +
> > > > > + /*
> > > > > + * Update the fast read settings. We set the default dummy
> > cycles to
> > > > 20
> > > > > + * here. Flashes can change this value if they need to when
> > enabling
> > > > > + * octal mode.
> > > > > + */
> > > > > +
spi_nor_set_read_settings(&params->reads[SNOR_CMD_READ_8_8_8_DTR],
> > > > > + 0, 20, opcode,
> > > > > + SNOR_PROTO_8_8_8_DTR);
> > > > > +
> > > >
> > > >
> > > > I thought we have a agreement that only do parse here, no other
read
> > > > parameters setting.
> > >
> > > Yes, and I considered it. But it didn't make much sense to me to
> > > introduce an extra member in struct spi_nor just to make this call
in
> > > some other function later.
> > >
> > > Why exactly do you think doing this here is bad? The way I see it,
we
> > > avoid carrying around an extra member in spi_nor and this also
allows
> > > flashes to change the read settings easily in a post-sfdp hook. The
> > > 4bait parsing function does something similar.
> >
> > I think it's not a question for good or bad.
> >
> > 4bait parsing function parse the 4-Byte Address Instruction Table
> > and set up read/pp parameters there for sure.
> >
> > Here we give the function name spi_nor_parse_profile1() but also
>
> But the function that parses 4bait table is also called
> spi_nor_parse_4bait().
>
> > do others setting that has nothing to do with it,
>
> Why has setting read opcode and dummy cycles got nothing to do with it?
> The purpose of the Profile 1.0 table is to tell us the Read Fast command

> and dummy cycles, among other things. I think it _does_ have something
> to do with it.

As you know I mean this function just do parse parameter of profile 1
table
and keep these value data for later usage.

A device supports xSPI profile table could work in either 8S-8S-8S or
8D-8D-8D mode.
It seems to setup these parameters somewhere out here is betters.

>
> Just like the 4bait table tells us the 4-byte opcodes and we set them up

> in our data structures, the profile 1.0 table tells us the 8D read
> opcode and dummy cycles, and we set them up in our data structures.
>
> > it seems not good for SW module design.
> > oh, it's my humble opinion.
> >
> > >
> > > What are the benefits of doing it otherwise?
> >
> > For other Octal Flash like mx25*
>
> I mean from a design perspective. How does it make the code better, or
> the job of people who need to read/change it easier?

yes, agreed.
I also need to patch for 8S-8S-8S mode, not only 8D-8D-8D mode.
That's why we have some discussions.

thanks & best regards,
Mason

CONFIDENTIALITY NOTE:

This e-mail and any attachments may contain confidential information
and/or personal data, which is protected by applicable laws. Please be
reminded that duplication, disclosure, distribution, or use of this e-mail
(and/or its attachments) or any part thereof is prohibited. If you receive
this e-mail in error, please notify us immediately and delete this mail as
well as its attachment(s) from your system. In addition, please be
informed that collection, processing, and/or use of personal data is
prohibited unless expressly permitted by personal data protection laws.
Thank you for your attention and cooperation.

Macronix International Co., Ltd.

=====================================================================



============================================================================

CONFIDENTIALITY NOTE:

This e-mail and any attachments may contain confidential information and/or personal data, which is protected by applicable laws. Please be reminded that duplication, disclosure, distribution, or use of this e-mail (and/or its attachments) or any part thereof is prohibited. If you receive this e-mail in error, please notify us immediately and delete this mail as well as its attachment(s) from your system. In addition, please be informed that collection, processing, and/or use of personal data is prohibited unless expressly permitted by personal data protection laws. Thank you for your attention and cooperation.

Macronix International Co., Ltd.

=====================================================================

2020-05-21 08:30:14

by Mason Yang

[permalink] [raw]
Subject: Re: [PATCH v5 01/19] spi: spi-mem: allow specifying whether an op is DTR or not


Hi Pratyush,

Given cmd.nbytes a initial value & check it !

>
> [PATCH v5 01/19] spi: spi-mem: allow specifying whether an op is DTR or
not
>
> Each phase is given a separate 'dtr' field so mixed protocols like
> 4S-4D-4D can be supported.
>
> Signed-off-by: Pratyush Yadav <[email protected]>
> ---
> drivers/spi/spi-mem.c | 3 +++
> include/linux/spi/spi-mem.h | 8 ++++++++
> 2 files changed, 11 insertions(+)
>
> diff --git a/drivers/spi/spi-mem.c b/drivers/spi/spi-mem.c
> index 9a86cc27fcc0..93e255287ab9 100644
> --- a/drivers/spi/spi-mem.c
> +++ b/drivers/spi/spi-mem.c
> @@ -156,6 +156,9 @@ bool spi_mem_default_supports_op(struct spi_mem
*mem,
> op->data.dir == SPI_MEM_DATA_OUT))
> return false;
>
> + if (op->cmd.dtr || op->addr.dtr || op->dummy.dtr || op->data.dtr)
> + return false;
> +

+ if (op->cmd.nbytes != 1)
+ return false;

> return true;
> }
> EXPORT_SYMBOL_GPL(spi_mem_default_supports_op);


static int spi_mem_check_op(const struct spi_mem_op *op)
{
- if (!op->cmd.buswidth)
+ if (!op->cmd.buswidth || op->cmd.nbytes < 1 ||
op->cmd.nbytes > 2)
return -EINVAL;


> diff --git a/include/linux/spi/spi-mem.h b/include/linux/spi/spi-mem.h
> index af9ff2f0f1b2..e3dcb956bf61 100644
> --- a/include/linux/spi/spi-mem.h
> +++ b/include/linux/spi/spi-mem.h

#define SPI_MEM_OP_CMD(__opcode, __buswidth) \
{ \
.buswidth = __buswidth, \
.opcode = __opcode, \
+ .nbytes = 1, \
}



> @@ -71,9 +71,11 @@ enum spi_mem_data_dir {
> * struct spi_mem_op - describes a SPI memory operation
> * @cmd.buswidth: number of IO lines used to transmit the command
> * @cmd.opcode: operation opcode
> + * @cmd.dtr: whether the command opcode should be sent in DTR mode or
not
> * @addr.nbytes: number of address bytes to send. Can be zero if the
operation
> * does not need to send an address
> * @addr.buswidth: number of IO lines used to transmit the address
cycles
> + * @addr.dtr: whether the address should be sent in DTR mode or not
> * @addr.val: address value. This value is always sent MSB first on the
bus.
> * Note that only @addr.nbytes are taken into account in this
> * address value, so users should make sure the value fits in
the
> @@ -81,7 +83,9 @@ enum spi_mem_data_dir {
> * @dummy.nbytes: number of dummy bytes to send after an opcode or
address. Can
> * be zero if the operation does not require dummy bytes
> * @dummy.buswidth: number of IO lanes used to transmit the dummy bytes
> + * @dummy.dtr: whether the dummy bytes should be sent in DTR mode or
not
> * @data.buswidth: number of IO lanes used to send/receive the data
> + * @data.dtr: whether the data should be sent in DTR mode or not
> * @data.dir: direction of the transfer
> * @data.nbytes: number of data bytes to send/receive. Can be zero if
the
> * operation does not involve transferring data
> @@ -91,22 +95,26 @@ enum spi_mem_data_dir {
> struct spi_mem_op {
> struct {
> u8 buswidth;
> + u8 dtr : 1;
> u8 opcode;
> } cmd;
>
> struct {
> u8 nbytes;
> u8 buswidth;
> + u8 dtr : 1;
> u64 val;
> } addr;
>
> struct {
> u8 nbytes;
> u8 buswidth;
> + u8 dtr : 1;
> } dummy;
>
> struct {
> u8 buswidth;
> + u8 dtr : 1;
> enum spi_mem_data_dir dir;
> unsigned int nbytes;
> union {
> --
> 2.26.2
>

thanks & best regards,
Mason

CONFIDENTIALITY NOTE:

This e-mail and any attachments may contain confidential information
and/or personal data, which is protected by applicable laws. Please be
reminded that duplication, disclosure, distribution, or use of this e-mail
(and/or its attachments) or any part thereof is prohibited. If you receive
this e-mail in error, please notify us immediately and delete this mail as
well as its attachment(s) from your system. In addition, please be
informed that collection, processing, and/or use of personal data is
prohibited unless expressly permitted by personal data protection laws.
Thank you for your attention and cooperation.

Macronix International Co., Ltd.

=====================================================================



============================================================================

CONFIDENTIALITY NOTE:

This e-mail and any attachments may contain confidential information and/or personal data, which is protected by applicable laws. Please be reminded that duplication, disclosure, distribution, or use of this e-mail (and/or its attachments) or any part thereof is prohibited. If you receive this e-mail in error, please notify us immediately and delete this mail as well as its attachment(s) from your system. In addition, please be informed that collection, processing, and/or use of personal data is prohibited unless expressly permitted by personal data protection laws. Thank you for your attention and cooperation.

Macronix International Co., Ltd.

=====================================================================

2020-05-21 09:17:51

by Pratyush Yadav

[permalink] [raw]
Subject: Re: [PATCH v5 09/19] mtd: spi-nor: sfdp: parse xSPI Profile 1.0 table

On 21/05/20 04:09PM, [email protected] wrote:
>
> Hi Pratyush,
>
> > > > > > + /* Get 8D-8D-8D fast read opcode and dummy cycles. */
> > > > > > + opcode = FIELD_GET(PROFILE1_DWORD1_RD_FAST_CMD, table[0]);
> > > > > > +
> > > > > > + /*
> > > > > > + * Update the fast read settings. We set the default dummy
> > > cycles to
> > > > > 20
> > > > > > + * here. Flashes can change this value if they need to when
> > > enabling
> > > > > > + * octal mode.
> > > > > > + */
> > > > > > +
> spi_nor_set_read_settings(&params->reads[SNOR_CMD_READ_8_8_8_DTR],
> > > > > > + 0, 20, opcode,
> > > > > > + SNOR_PROTO_8_8_8_DTR);
> > > > > > +
> > > > >
> > > > >
> > > > > I thought we have a agreement that only do parse here, no other
> read
> > > > > parameters setting.
> > > >
> > > > Yes, and I considered it. But it didn't make much sense to me to
> > > > introduce an extra member in struct spi_nor just to make this call
> in
> > > > some other function later.
> > > >
> > > > Why exactly do you think doing this here is bad? The way I see it,
> we
> > > > avoid carrying around an extra member in spi_nor and this also
> allows
> > > > flashes to change the read settings easily in a post-sfdp hook. The
> > > > 4bait parsing function does something similar.
> > >
> > > I think it's not a question for good or bad.
> > >
> > > 4bait parsing function parse the 4-Byte Address Instruction Table
> > > and set up read/pp parameters there for sure.
> > >
> > > Here we give the function name spi_nor_parse_profile1() but also
> >
> > But the function that parses 4bait table is also called
> > spi_nor_parse_4bait().
> >
> > > do others setting that has nothing to do with it,
> >
> > Why has setting read opcode and dummy cycles got nothing to do with it?
> > The purpose of the Profile 1.0 table is to tell us the Read Fast
> > command and dummy cycles, among other things. I think it _does_ have
> > something to do with it.
>
> As you know I mean this function just do parse parameter of profile 1
> table
> and keep these value data for later usage.
>
> A device supports xSPI profile table could work in either 8S-8S-8S or
> 8D-8D-8D mode.
> It seems to setup these parameters somewhere out here is betters.

As far as I know, the Profile 1.0 table only describes 8D-8D-8D mode. I
see no mention of 8S-8S-8S in JESD251 or JESD216D.01. No field in the
table describes anything related to 8S. In fact, searching for "8S" in
the JESD251 spec yields 0 results.

Anyway, you should set up 8S parameters in SNOR_CMD_READ_8_8_8, not
SNOR_CMD_READ_8_8_8_DTR. 8D configuration is independent of 8S
configuration.

PS: If you have any more comments, please send them now. The merge
window is getting close, and I'd like to see this make it in.

--
Regards,
Pratyush Yadav
Texas Instruments India

2020-05-21 09:27:01

by Mason Yang

[permalink] [raw]
Subject: Re: [PATCH v5 05/19] mtd: spi-nor: add support for DTR protocol


Hi Pratyush,


> @@ -311,6 +313,7 @@ struct flash_info {
> * BP3 is bit 6 of status register.
> * Must be used with SPI_NOR_4BIT_BP.
> */
> +#define SPI_NOR_OCTAL_DTR_READ BIT(19) /* Flash supports octal DTR
Read. */

#define SPI_NOR_OCTAL_DTR_RDWR BIT(19) /* Support Octal DTR Read & Write
*/

more precisely and clearly ?

thanks,
Mason

CONFIDENTIALITY NOTE:

This e-mail and any attachments may contain confidential information
and/or personal data, which is protected by applicable laws. Please be
reminded that duplication, disclosure, distribution, or use of this e-mail
(and/or its attachments) or any part thereof is prohibited. If you receive
this e-mail in error, please notify us immediately and delete this mail as
well as its attachment(s) from your system. In addition, please be
informed that collection, processing, and/or use of personal data is
prohibited unless expressly permitted by personal data protection laws.
Thank you for your attention and cooperation.

Macronix International Co., Ltd.

=====================================================================



============================================================================

CONFIDENTIALITY NOTE:

This e-mail and any attachments may contain confidential information and/or personal data, which is protected by applicable laws. Please be reminded that duplication, disclosure, distribution, or use of this e-mail (and/or its attachments) or any part thereof is prohibited. If you receive this e-mail in error, please notify us immediately and delete this mail as well as its attachment(s) from your system. In addition, please be informed that collection, processing, and/or use of personal data is prohibited unless expressly permitted by personal data protection laws. Thank you for your attention and cooperation.

Macronix International Co., Ltd.

=====================================================================

2020-05-21 12:55:11

by Pratyush Yadav

[permalink] [raw]
Subject: Re: [PATCH v5 05/19] mtd: spi-nor: add support for DTR protocol

Hi,

On 21/05/20 05:24PM, [email protected] wrote:
>
> Hi Pratyush,
>
>
> > @@ -311,6 +313,7 @@ struct flash_info {
> > * BP3 is bit 6 of status register.
> > * Must be used with SPI_NOR_4BIT_BP.
> > */
> > +#define SPI_NOR_OCTAL_DTR_READ BIT(19) /* Flash supports octal DTR
> Read. */
>
> #define SPI_NOR_OCTAL_DTR_RDWR BIT(19) /* Support Octal DTR Read & Write
> */

This flag only enables SNOR_HWCAPS_READ_8_8_8_DTR. It does not affect
SNOR_HWCAPS_PP_8_8_8_DTR. So it shouldn't be called RDWR.

> more precisely and clearly ?
>
> thanks,
> Mason
>

--
Regards,
Pratyush Yadav
Texas Instruments India

2020-05-22 06:33:16

by Mason Yang

[permalink] [raw]
Subject: Re: [PATCH v5 05/19] mtd: spi-nor: add support for DTR protocol


Hi Pratyush,


> +/**
> + * spi_nor_spimem_setup_op() - Set up common properties of a spi-mem
op.
> + * @nor: pointer to a 'struct spi_nor'
> + * @op: pointer to the 'struct spi_mem_op' whose properties
> + * need to be initialized.
> + * @proto: the protocol from which the properties need to be set.
> + */
> +void spi_nor_spimem_setup_op(const struct spi_nor *nor,
> + struct spi_mem_op *op,
> + const enum spi_nor_protocol proto)
> +{
> + u8 ext;
> +
> + op->cmd.buswidth = spi_nor_get_protocol_inst_nbits(proto);
> +
> + if (op->addr.nbytes)
> + op->addr.buswidth = spi_nor_get_protocol_addr_nbits(proto);
> +
> + if (op->dummy.nbytes)
> + op->dummy.buswidth = spi_nor_get_protocol_addr_nbits(proto);
> +
> + if (op->data.nbytes)
> + op->data.buswidth = spi_nor_get_protocol_data_nbits(proto);
> +
> + if (spi_nor_protocol_is_dtr(proto)) {

As mentioned before that I am also patching mx25* which supports 8S-8S-8S
and
8D-8D-8D mode.

please patch to spi_nor_protocol_is_8_8_8(proto) for 8S-8S-8S mode
support.

> + /*
> + * spi-mem supports mixed DTR modes, but right now we can only
> + * have all phases either DTR or STR. IOW, spi-mem can have
> + * something like 4S-4D-4D, but spi-nor can't. So, set all 4
> + * phases to either DTR or STR.
> + */

if (spi_nor_protocol_is_8D_8D_8D(proto) {

> + op->cmd.dtr = op->addr.dtr = op->dummy.dtr
> + = op->data.dtr = true;
> +
> + /* 2 bytes per clock cycle in DTR mode. */
> + op->dummy.nbytes *= 2;

}

> +
> + ext = spi_nor_get_cmd_ext(nor, op);
> + op->cmd.opcode = (op->cmd.opcode << 8) | ext;
> + op->cmd.nbytes = 2;
> + }
> +}
> +

thanks & best regards,
Mason


CONFIDENTIALITY NOTE:

This e-mail and any attachments may contain confidential information
and/or personal data, which is protected by applicable laws. Please be
reminded that duplication, disclosure, distribution, or use of this e-mail
(and/or its attachments) or any part thereof is prohibited. If you receive
this e-mail in error, please notify us immediately and delete this mail as
well as its attachment(s) from your system. In addition, please be
informed that collection, processing, and/or use of personal data is
prohibited unless expressly permitted by personal data protection laws.
Thank you for your attention and cooperation.

Macronix International Co., Ltd.

=====================================================================



============================================================================

CONFIDENTIALITY NOTE:

This e-mail and any attachments may contain confidential information and/or personal data, which is protected by applicable laws. Please be reminded that duplication, disclosure, distribution, or use of this e-mail (and/or its attachments) or any part thereof is prohibited. If you receive this e-mail in error, please notify us immediately and delete this mail as well as its attachment(s) from your system. In addition, please be informed that collection, processing, and/or use of personal data is prohibited unless expressly permitted by personal data protection laws. Thank you for your attention and cooperation.

Macronix International Co., Ltd.

=====================================================================

2020-05-22 08:42:21

by Pratyush Yadav

[permalink] [raw]
Subject: Re: [PATCH v5 05/19] mtd: spi-nor: add support for DTR protocol

On 22/05/20 02:30PM, [email protected] wrote:
>
> Hi Pratyush,
>
>
> > +/**
> > + * spi_nor_spimem_setup_op() - Set up common properties of a spi-mem
> op.
> > + * @nor: pointer to a 'struct spi_nor'
> > + * @op: pointer to the 'struct spi_mem_op' whose properties
> > + * need to be initialized.
> > + * @proto: the protocol from which the properties need to be set.
> > + */
> > +void spi_nor_spimem_setup_op(const struct spi_nor *nor,
> > + struct spi_mem_op *op,
> > + const enum spi_nor_protocol proto)
> > +{
> > + u8 ext;
> > +
> > + op->cmd.buswidth = spi_nor_get_protocol_inst_nbits(proto);
> > +
> > + if (op->addr.nbytes)
> > + op->addr.buswidth = spi_nor_get_protocol_addr_nbits(proto);
> > +
> > + if (op->dummy.nbytes)
> > + op->dummy.buswidth = spi_nor_get_protocol_addr_nbits(proto);
> > +
> > + if (op->data.nbytes)
> > + op->data.buswidth = spi_nor_get_protocol_data_nbits(proto);
> > +
> > + if (spi_nor_protocol_is_dtr(proto)) {
>
> As mentioned before that I am also patching mx25* which supports 8S-8S-8S
> and
> 8D-8D-8D mode.
>
> please patch to spi_nor_protocol_is_8_8_8(proto) for 8S-8S-8S mode
> support.

Like I said before, we should try to avoid creeping up the scope of this
series. This series aims to add 8D support. Once this lands, I don't see
why you can't 8S support on top. Unless we make a fundamental change
that makes it impossible to add 8S support, it should stay as-is.

All that said, I fail to see why 8S would have any problems with this
function. We just fill in the buswidths from the protocol, and adjust
the op if it is DTR. So in case of 8S mode, this function as it is will
fill in the buswidths to 8 for all phases. And it won't hit the if block
here so this code is of no concern to 8S mode.

> > + /*
> > + * spi-mem supports mixed DTR modes, but right now we can only
> > + * have all phases either DTR or STR. IOW, spi-mem can have
> > + * something like 4S-4D-4D, but spi-nor can't. So, set all 4
> > + * phases to either DTR or STR.
> > + */
>
> if (spi_nor_protocol_is_8D_8D_8D(proto) {

No. The adjustments below apply to _all_ DTR ops, not just 8D-8D-8D
ones. So in case someone wants to use 4D-4D-4D mode, they won't have to
touch this code at all.

> > + op->cmd.dtr = op->addr.dtr = op->dummy.dtr
> > + = op->data.dtr = true;
> > +
> > + /* 2 bytes per clock cycle in DTR mode. */
> > + op->dummy.nbytes *= 2;
>
> }
>
> > +
> > + ext = spi_nor_get_cmd_ext(nor, op);
> > + op->cmd.opcode = (op->cmd.opcode << 8) | ext;
> > + op->cmd.nbytes = 2;
> > + }
> > +}
> > +

--
Regards,
Pratyush Yadav
Texas Instruments India

2020-05-22 10:50:08

by Pratyush Yadav

[permalink] [raw]
Subject: Re: [PATCH v5 01/19] spi: spi-mem: allow specifying whether an op is DTR or not

[Seems like I forgot to send this and it stayed in my Drafts folder.
Anyway, fixed in v7]

Hi Mason,

On 21/05/20 04:27PM, [email protected] wrote:
>
> Hi Pratyush,
>
> Given cmd.nbytes a initial value & check it !
>
> >
> > [PATCH v5 01/19] spi: spi-mem: allow specifying whether an op is DTR or
> not
> >
> > Each phase is given a separate 'dtr' field so mixed protocols like
> > 4S-4D-4D can be supported.
> >
> > Signed-off-by: Pratyush Yadav <[email protected]>
> > ---
> > drivers/spi/spi-mem.c | 3 +++
> > include/linux/spi/spi-mem.h | 8 ++++++++
> > 2 files changed, 11 insertions(+)
> >
> > diff --git a/drivers/spi/spi-mem.c b/drivers/spi/spi-mem.c
> > index 9a86cc27fcc0..93e255287ab9 100644
> > --- a/drivers/spi/spi-mem.c
> > +++ b/drivers/spi/spi-mem.c
> > @@ -156,6 +156,9 @@ bool spi_mem_default_supports_op(struct spi_mem
> *mem,
> > op->data.dir == SPI_MEM_DATA_OUT))
> > return false;
> >
> > + if (op->cmd.dtr || op->addr.dtr || op->dummy.dtr || op->data.dtr)
> > + return false;
> > +
>
> + if (op->cmd.nbytes != 1)
> + return false;

Good catch. Will fix.

> > return true;
> > }
> > EXPORT_SYMBOL_GPL(spi_mem_default_supports_op);
>
>
> static int spi_mem_check_op(const struct spi_mem_op *op)
> {
> - if (!op->cmd.buswidth)
> + if (!op->cmd.buswidth || op->cmd.nbytes < 1 ||
> op->cmd.nbytes > 2)
> return -EINVAL;

Will fix.

>
> > diff --git a/include/linux/spi/spi-mem.h b/include/linux/spi/spi-mem.h
> > index af9ff2f0f1b2..e3dcb956bf61 100644
> > --- a/include/linux/spi/spi-mem.h
> > +++ b/include/linux/spi/spi-mem.h
>
> #define SPI_MEM_OP_CMD(__opcode, __buswidth) \
> { \
> .buswidth = __buswidth, \
> .opcode = __opcode, \
> + .nbytes = 1, \
> }

Will fix. Thanks.

--
Regards,
Pratyush Yadav
Texas Instruments India