2020-05-28 08:00:36

by Mason Yang

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

Hello,

JESD216C has defined specification for Octal 8S-8S-8S and 8D-8D-8D.
Based on JEDEC216C Basic Flash Parameter Table (BFPT) driver extract
DWORD-18: command and command extension type.
DWORD-20: Maximum operation speed of device in Octal mode.

xSPI profile 1.0 table:
DWORD-1: Read Fast command, the number of dummy cycles and address nbytes
for Read Status Register command.
DWORD-2: Read/Write volatile Register command for CFG Reg2.
DWORD-4 and DWORD-5: dummy cycles used for various frequencies based on
maximum speed of device from BFPT 20th DWORD.

Ccommand sequences to change to octal DTR mode:
The length of each command sequence is 8 per byte for single SPI mode and
patching driver to parse and execute these sequences for octal DTR mode.

By Vignesh's comments to patch these drivers based on Pratyush's patches
set [1].

This series adds support for Macronix mx25uw51245g works in octal DTR mode.

Tested on Macronix's Zynq PicoZed board with Macronix's SPI controller
(spi-mxic.c) driver patched on mx25uw51245g Octal flash.


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


Summary of change log
v3:
Add support command sequences to change octal DTR mode and based on
part of Pratyush's patches set.

v2:
Parse BFPT & xSPI table for Octal 8D-8D-8D mode parameters and enable Octal
mode in spi_nor_late_init_params().
Using Macros in spi_nor_spimem_read_data, spi_nor_spimem_write_data and
so on by Vignesh comments.

v1:
Without parsing BFPT & xSPI profile 1.0 table and enter Octal 8D-8D-8D
mode directly in spi_nor_fixups hooks.


thnaks for your time and review.
best regards,
Mason

--
Mason Yang (7):
mtd: spi-nor: sfdp: get octal mode maximum speed from BFPT
mtd: spi-nor: sfdp: parse xSPI Profile 1.0 table
mtd: spi-nor: sfdp: parse command sequences to change octal DTR mode
mtd: spi-nor: core: add configuration register 2 read & write support
spi: mxic: patch for octal DTR mode support
mtd: spi-nor: core: execute command sequences to change octal DTR mode
mtd: spi-nor: macronix: Add Octal 8D-8D-8D supports for Macronix
mx25uw51245g

Pratyush Yadav (7):
spi: spi-mem: allow specifying whether an op is DTR or not
spi: spi-mem: allow specifying a command's extension
mtd: spi-nor: add support for DTR protocol
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: core: use dummy cycle and address width info from SFDP
mtd: spi-nor: core: enable octal DTR mode when possible

drivers/mtd/spi-nor/core.c | 514 +++++++++++++++++++++++++++++++++++------
drivers/mtd/spi-nor/core.h | 49 ++++
drivers/mtd/spi-nor/macronix.c | 55 +++++
drivers/mtd/spi-nor/sfdp.c | 278 +++++++++++++++++++++-
drivers/mtd/spi-nor/sfdp.h | 15 +-
drivers/spi/spi-mem.c | 16 +-
drivers/spi/spi-mtk-nor.c | 4 +-
drivers/spi/spi-mxic.c | 101 +++++---
drivers/spi/spi-zynq-qspi.c | 11 +-
include/linux/mtd/spi-nor.h | 51 ++--
include/linux/spi/spi-mem.h | 14 +-
11 files changed, 980 insertions(+), 128 deletions(-)

--
1.9.1


2020-05-28 08:00:57

by Mason Yang

[permalink] [raw]
Subject: [PATCH v3 02/14] spi: spi-mem: allow specifying a command's extension

From: Pratyush Yadav <[email protected]>

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.

Some places use sizeof(op->cmd.opcode). Replace them with op->cmd.nbytes

The spi-mxic and spi-zynq-qspi drivers directly use op->cmd.opcode as a
buffer. Now that opcode is a 2-byte field, this can result in different
behaviour depending on if the machine is little endian or big endian.
Extract the opcode in a local 1-byte variable and use that as the buffer
instead. Both these drivers would reject multi-byte opcodes in their
supports_op() hook anyway, so we only need to worry about single-byte
opcodes for now.

The above two changes are put in this commit to keep the series
bisectable.

Signed-off-by: Pratyush Yadav <[email protected]>
---
drivers/spi/spi-mem.c | 13 +++++++------
drivers/spi/spi-mtk-nor.c | 4 ++--
drivers/spi/spi-zynq-qspi.c | 11 ++++++-----
include/linux/spi/spi-mem.h | 6 +++++-
4 files changed, 20 insertions(+), 14 deletions(-)

diff --git a/drivers/spi/spi-mem.c b/drivers/spi/spi-mem.c
index d8905e0..a0366ff 100644
--- a/drivers/spi/spi-mem.c
+++ b/drivers/spi/spi-mem.c
@@ -157,6 +157,9 @@ bool spi_mem_default_supports_op(struct spi_mem *mem,
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);
@@ -171,7 +174,7 @@ static bool spi_mem_buswidth_is_valid(u8 buswidth)

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

if ((op->addr.nbytes && !op->addr.buswidth) ||
@@ -307,8 +310,7 @@ int spi_mem_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)
return ret;
}

- tmpbufsize = sizeof(op->cmd.opcode) + op->addr.nbytes +
- op->dummy.nbytes;
+ tmpbufsize = op->cmd.nbytes + op->addr.nbytes + op->dummy.nbytes;

/*
* Allocate a buffer to transmit the CMD, ADDR cycles with kmalloc() so
@@ -323,7 +325,7 @@ int spi_mem_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)

tmpbuf[0] = op->cmd.opcode;
xfers[xferpos].tx_buf = tmpbuf;
- xfers[xferpos].len = sizeof(op->cmd.opcode);
+ xfers[xferpos].len = op->cmd.nbytes;
xfers[xferpos].tx_nbits = op->cmd.buswidth;
spi_message_add_tail(&xfers[xferpos], &msg);
xferpos++;
@@ -425,8 +427,7 @@ int spi_mem_adjust_op_size(struct spi_mem *mem, struct spi_mem_op *op)
return ctlr->mem_ops->adjust_op_size(mem, op);

if (!ctlr->mem_ops || !ctlr->mem_ops->exec_op) {
- len = sizeof(op->cmd.opcode) + op->addr.nbytes +
- op->dummy.nbytes;
+ len = op->cmd.nbytes + op->addr.nbytes + op->dummy.nbytes;

if (len > spi_max_transfer_size(mem->spi))
return -EINVAL;
diff --git a/drivers/spi/spi-mtk-nor.c b/drivers/spi/spi-mtk-nor.c
index c15a991..d1f4cfc 100644
--- a/drivers/spi/spi-mtk-nor.c
+++ b/drivers/spi/spi-mtk-nor.c
@@ -195,7 +195,7 @@ static int mtk_nor_adjust_op_size(struct spi_mem *mem, struct spi_mem_op *op)
}
}

- len = MTK_NOR_PRG_MAX_SIZE - sizeof(op->cmd.opcode) - op->addr.nbytes -
+ len = MTK_NOR_PRG_MAX_SIZE - op->cmd.nbytes - op->addr.nbytes -
op->dummy.nbytes;
if (op->data.nbytes > len)
op->data.nbytes = len;
@@ -219,7 +219,7 @@ static bool mtk_nor_supports_op(struct spi_mem *mem,
(op->dummy.buswidth == 0) &&
(op->data.buswidth == 1);
}
- len = sizeof(op->cmd.opcode) + op->addr.nbytes + op->dummy.nbytes;
+ len = op->cmd.nbytes + op->addr.nbytes + op->dummy.nbytes;
if ((len > MTK_NOR_PRG_MAX_SIZE) ||
((op->data.nbytes) && (len == MTK_NOR_PRG_MAX_SIZE)))
return false;
diff --git a/drivers/spi/spi-zynq-qspi.c b/drivers/spi/spi-zynq-qspi.c
index 1764115..bbf3d90 100644
--- a/drivers/spi/spi-zynq-qspi.c
+++ b/drivers/spi/spi-zynq-qspi.c
@@ -527,20 +527,21 @@ static int zynq_qspi_exec_mem_op(struct spi_mem *mem,
struct zynq_qspi *xqspi = spi_controller_get_devdata(mem->spi->master);
int err = 0, i;
u8 *tmpbuf;
+ u8 opcode = op->cmd.opcode;

dev_dbg(xqspi->dev, "cmd:%#x mode:%d.%d.%d.%d\n",
- op->cmd.opcode, op->cmd.buswidth, op->addr.buswidth,
+ opcode, op->cmd.buswidth, op->addr.buswidth,
op->dummy.buswidth, op->data.buswidth);

zynq_qspi_chipselect(mem->spi, true);
zynq_qspi_config_op(xqspi, mem->spi);

- if (op->cmd.opcode) {
+ if (op->cmd.nbytes) {
reinit_completion(&xqspi->data_completion);
- xqspi->txbuf = (u8 *)&op->cmd.opcode;
+ xqspi->txbuf = &opcode;
xqspi->rxbuf = NULL;
- xqspi->tx_bytes = sizeof(op->cmd.opcode);
- xqspi->rx_bytes = sizeof(op->cmd.opcode);
+ xqspi->tx_bytes = op->cmd.nbytes;
+ xqspi->rx_bytes = op->cmd.nbytes;
zynq_qspi_write_op(xqspi, ZYNQ_QSPI_FIFO_DEPTH, true);
zynq_qspi_write(xqspi, ZYNQ_QSPI_IEN_OFFSET,
ZYNQ_QSPI_IXR_RXTX_MASK);
diff --git a/include/linux/spi/spi-mem.h b/include/linux/spi/spi-mem.h
index e3dcb95..159463c 100644
--- a/include/linux/spi/spi-mem.h
+++ b/include/linux/spi/spi-mem.h
@@ -17,6 +17,7 @@
{ \
.buswidth = __buswidth, \
.opcode = __opcode, \
+ .nbytes = 1, \
}

#define SPI_MEM_OP_ADDR(__nbytes, __val, __buswidth) \
@@ -69,6 +70,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 +97,10 @@ enum spi_mem_data_dir {
*/
struct spi_mem_op {
struct {
+ u8 nbytes;
u8 buswidth;
u8 dtr : 1;
- u8 opcode;
+ u16 opcode;
} cmd;

struct {
--
1.9.1

2020-05-28 08:01:09

by Mason Yang

[permalink] [raw]
Subject: [PATCH v3 03/14] mtd: spi-nor: add support for DTR protocol

From: Pratyush Yadav <[email protected]>

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 1ab4386..388e695 100644
--- a/drivers/mtd/spi-nor/core.c
+++ b/drivers/mtd/spi-nor/core.c
@@ -41,6 +41,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
* @nor: pointer to 'struct spi_nor'
@@ -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 @@ static int spi_nor_spimem_check_pp(struct spi_nor *nor,
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,12 +2927,26 @@ 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 6f2f6b2..de1e391 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 @@ struct spi_nor_manufacturer {
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 f6038d3..f917631 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 bebff27..d251a5d 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_controller_ops {
* @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;

--
1.9.1

2020-05-28 08:01:10

by Mason Yang

[permalink] [raw]
Subject: [PATCH v3 04/14] mtd: spi-nor: sfdp: prepare BFPT parsing for JESD216 rev D

From: Pratyush Yadav <[email protected]>

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 f917631..c289395 100644
--- a/drivers/mtd/spi-nor/sfdp.c
+++ b/drivers/mtd/spi-nor/sfdp.c
@@ -548,7 +548,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);

@@ -604,6 +604,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 e0a8ded..f8198af 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)
--
1.9.1

2020-05-28 08:01:16

by Mason Yang

[permalink] [raw]
Subject: [PATCH v3 06/14] mtd: spi-nor: sfdp: get octal mode maximum speed from BFPT

Get maximum operation speed of device in octal mode from
BFPT 20th DWORD.

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

diff --git a/drivers/mtd/spi-nor/core.h b/drivers/mtd/spi-nor/core.h
index de1e391..f644ed1 100644
--- a/drivers/mtd/spi-nor/core.h
+++ b/drivers/mtd/spi-nor/core.h
@@ -192,6 +192,7 @@ struct spi_nor_locking_ops {
*
* @size: the flash memory density in bytes.
* @page_size: the page size of the SPI NOR flash memory.
+ * @octal_max_speed: maximum operation speed of device in octal mode.
* @hwcaps: describes the read and page program hardware
* capabilities.
* @reads: read capabilities ordered by priority: the higher index
@@ -214,6 +215,7 @@ struct spi_nor_locking_ops {
struct spi_nor_flash_parameter {
u64 size;
u32 page_size;
+ u16 octal_max_speed;

struct spi_nor_hwcaps hwcaps;
struct spi_nor_read_command reads[SNOR_CMD_READ_MAX];
diff --git a/drivers/mtd/spi-nor/sfdp.c b/drivers/mtd/spi-nor/sfdp.c
index 3685a3a..d7ef47a 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>
@@ -26,6 +27,12 @@
#define SFDP_JESD216A_MINOR 5
#define SFDP_JESD216B_MINOR 6

+/* Basic Flash Parameter Table 20th DWORD, Max operation speed of device */
+struct octal_max_speed {
+ u8 idx; /* Bits value */
+ u16 hz; /* MHz */
+};
+
struct sfdp_header {
u32 signature; /* Ox50444653U <=> "SFDP" */
u8 minor;
@@ -440,6 +447,22 @@ static int spi_nor_parse_bfpt(struct spi_nor *nor,
u32 addr;
u16 half;
u8 erase_mask;
+ static const struct octal_max_speed max_hz[] = {
+ /* Bits value, MHz */
+ { 0x0c, 400 },
+ { 0x0b, 333 },
+ { 0x0a, 266 },
+ { 0x09, 250 },
+ { 0x08, 200 },
+ { 0x07, 166 },
+ { 0x06, 133 },
+ { 0x05, 100 },
+ { 0x04, 80 },
+ { 0x03, 66 },
+ { 0x02, 50 },
+ { 0x01, 33 },
+ };
+ u8 idx;

/* JESD216 Basic Flash Parameter Table length is at least 9 DWORDs. */
if (bfpt_header->length < BFPT_DWORD_MAX_JESD216)
@@ -626,6 +649,19 @@ static int spi_nor_parse_bfpt(struct spi_nor *nor,
return -ENOTSUPP;
}

+ /* Octal mode max speed */
+ idx = max(FIELD_GET(BFPT_DWORD20_OCTAL_DTR_MAX_SPEED,
+ bfpt.dwords[BFPT_DWORD(20)]),
+ FIELD_GET(BFPT_DWORD20_OCTAL_STR_MAX_SPEED,
+ bfpt.dwords[BFPT_DWORD(20)]));
+
+ for (i = 0; i < ARRAY_SIZE(max_hz); i++) {
+ if (max_hz[i].idx == idx) {
+ params->octal_max_speed = max_hz[i].hz;
+ break;
+ }
+ }
+
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 e15e307..d8ee681 100644
--- a/drivers/mtd/spi-nor/sfdp.h
+++ b/drivers/mtd/spi-nor/sfdp.h
@@ -90,6 +90,10 @@ struct sfdp_bfpt {
#define BFPT_DWORD18_CMD_EXT_RES (0x2UL << 29) /* Reserved */
#define BFPT_DWORD18_CMD_EXT_16B (0x3UL << 29) /* 16-bit opcode */

+#define BFPT_DWORD20_OCTAL_MAX_SPEED_MASK GENMASK(31, 16)
+#define BFPT_DWORD20_OCTAL_DTR_MAX_SPEED GENMASK(31, 28)
+#define BFPT_DWORD20_OCTAL_STR_MAX_SPEED GENMASK(19, 16)
+
struct sfdp_parameter_header {
u8 id_lsb;
u8 minor;
--
1.9.1

2020-05-28 08:01:28

by Mason Yang

[permalink] [raw]
Subject: [PATCH v3 05/14] mtd: spi-nor: sfdp: get command opcode extension type from BFPT

From: Pratyush Yadav <[email protected]>

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 c289395..3685a3a 100644
--- a/drivers/mtd/spi-nor/sfdp.c
+++ b/drivers/mtd/spi-nor/sfdp.c
@@ -608,6 +608,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 f8198af..e15e307 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;
--
1.9.1

2020-05-28 08:01:56

by Mason Yang

[permalink] [raw]
Subject: [PATCH v3 08/14] mtd: spi-nor: sfdp: parse command sequences to change octal DTR mode

A set of simple command sequences is provided which can be executed
directly by the host controller to enable octal DTR mode.

Each command sequence is 8 per byte for single SPI mode.

Signed-off-by: Mason Yang <[email protected]>
---
drivers/mtd/spi-nor/core.h | 18 ++++++++
drivers/mtd/spi-nor/sfdp.c | 103 +++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 121 insertions(+)

diff --git a/drivers/mtd/spi-nor/core.h b/drivers/mtd/spi-nor/core.h
index 837f5e9..101726c 100644
--- a/drivers/mtd/spi-nor/core.h
+++ b/drivers/mtd/spi-nor/core.h
@@ -185,6 +185,21 @@ struct spi_nor_locking_ops {
};

/**
+ * struct cmd_seq_octal_dtr - command sequences to change to octal DTR mode
+ * Each command sequence is 8 per byte for single SPI mode.
+ * @len: commmand length of each command sequence.
+ * @opcode: command code.
+ * @addr: address offset to device.
+ * @data: data write to device.
+ */
+struct cmd_seq_octal_dtr {
+ u8 len;
+ u8 opcode;
+ u32 addr;
+ u8 data;
+};
+
+/**
* struct spi_nor_flash_parameter - SPI NOR flash parameters and settings.
* Includes legacy flash parameters and settings that can be overwritten
* by the spi_nor_fixups hooks, or dynamically when parsing the JESD216
@@ -207,6 +222,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.
+ * @cmd_seq: command sequence to change to 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
@@ -234,6 +250,8 @@ struct spi_nor_flash_parameter {

struct spi_nor_erase_map erase_map;

+ struct cmd_seq_octal_dtr cmd_seq[4];
+
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);
diff --git a/drivers/mtd/spi-nor/sfdp.c b/drivers/mtd/spi-nor/sfdp.c
index 550422b..fb5f9f3 100644
--- a/drivers/mtd/spi-nor/sfdp.c
+++ b/drivers/mtd/spi-nor/sfdp.c
@@ -21,6 +21,7 @@
#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_CMD_TO_8DTR_ID 0xff0a /* Command Sequence to Octal DDR */

#define SFDP_SIGNATURE 0x50444653U
#define SFDP_JESD216_MAJOR 1
@@ -55,6 +56,19 @@ struct octal_max_speed {
u16 hz; /* MHz */
};

+#define CMD_TO_8DTR_LEN GENMASK(31, 24)
+#define CMD_TO_8DTR_OPCODE GENMASK(23, 16)
+#define CMD_TO_8DTR_1_ADDR GENMASK(15, 8)
+#define CMD_TO_8DTR_1_ADDR_DATA GENMASK(7, 0)
+#define CMD_TO_8DTR_4_ADDR_MSB GENMASK(15, 0)
+#define CMD_TO_8DTR_4_ADDR_LSB GENMASK(31, 16)
+#define CMD_TO_8DTR_4_ADDR_DATA GENMASK(15, 8)
+#define CMD_TO_8DTR_SIZE_MAX 8
+
+struct sfdp_cmd_to_8dtr {
+ u32 dwords[CMD_TO_8DTR_SIZE_MAX];
+};
+
struct sfdp_header {
u32 signature; /* Ox50444653U <=> "SFDP" */
u8 minor;
@@ -1248,6 +1262,90 @@ static int spi_nor_parse_profile1(struct spi_nor *nor,
}

/**
+ * spi_nor_parse_cmd_to_8dtr() - parse the command sequence to octal DTR
+ * @nor: pointer to a 'struct spi_nor'
+ * @param_header: command sequence to octal DTR parameter table header.
+ * @params: pointer to the 'struct spi_nor_flash_parameter' to be.
+ *
+ * Return: 0 on success, -errno otherwise.
+ */
+static int spi_nor_parse_cmd_to_8dtr(struct spi_nor *nor,
+ const struct sfdp_parameter_header *header,
+ struct spi_nor_flash_parameter *params)
+{
+ struct sfdp_cmd_to_8dtr cmd_seq;
+ u32 i, j, addr;
+ size_t len;
+ int ret;
+
+ if (header->major != SFDP_JESD216_MAJOR ||
+ header->length < CMD_TO_8DTR_SIZE_MAX)
+ return -EINVAL;
+
+ len = min_t(size_t, sizeof(cmd_seq),
+ header->length * sizeof(u32));
+
+ memset(&cmd_seq, 0, sizeof(cmd_seq));
+
+ addr = SFDP_PARAM_HEADER_PTP(header);
+ ret = spi_nor_read_sfdp(nor, addr, len, &cmd_seq);
+ if (ret)
+ goto out;
+
+ /* Fix endianness of the Command Sequences to octal DTR. */
+ le32_to_cpu_array(cmd_seq.dwords, CMD_TO_8DTR_SIZE_MAX);
+
+ memset(params->cmd_seq, 0, sizeof(params->cmd_seq[4]));
+
+ for (i = 0, j = 0; i < 4 && j < 8; i++, j += 2) {
+ params->cmd_seq[i].len = FIELD_GET(CMD_TO_8DTR_LEN,
+ cmd_seq.dwords[j]);
+ if (!params->cmd_seq[i].len)
+ break;
+
+ switch (params->cmd_seq[i].len) {
+ case 1:
+ params->cmd_seq[i].opcode =
+ FIELD_GET(CMD_TO_8DTR_OPCODE,
+ cmd_seq.dwords[j]);
+ break;
+
+ case 3:
+ params->cmd_seq[i].opcode =
+ FIELD_GET(CMD_TO_8DTR_OPCODE,
+ cmd_seq.dwords[j]);
+ params->cmd_seq[i].addr =
+ FIELD_GET(CMD_TO_8DTR_1_ADDR,
+ cmd_seq.dwords[j]);
+ params->cmd_seq[i].data =
+ FIELD_GET(CMD_TO_8DTR_1_ADDR_DATA,
+ cmd_seq.dwords[j]);
+ break;
+
+ case 6:
+ params->cmd_seq[i].opcode =
+ FIELD_GET(CMD_TO_8DTR_OPCODE,
+ cmd_seq.dwords[j]);
+ params->cmd_seq[i].addr =
+ FIELD_GET(CMD_TO_8DTR_4_ADDR_MSB,
+ cmd_seq.dwords[j]) << 16 |
+ FIELD_GET(CMD_TO_8DTR_4_ADDR_LSB,
+ cmd_seq.dwords[j + 1]);
+ params->cmd_seq[i].data =
+ FIELD_GET(CMD_TO_8DTR_4_ADDR_DATA,
+ cmd_seq.dwords[j + 1]);
+ break;
+
+ default:
+ break;
+ }
+ }
+
+out:
+ return ret;
+}
+
+/**
* spi_nor_parse_sfdp() - parse the Serial Flash Discoverable Parameters.
* @nor: pointer to a 'struct spi_nor'
* @params: pointer to the 'struct spi_nor_flash_parameter' to be
@@ -1352,6 +1450,11 @@ int spi_nor_parse_sfdp(struct spi_nor *nor,
err = spi_nor_parse_profile1(nor, param_header, params);
break;

+ case SFDP_CMD_TO_8DTR_ID:
+ err = spi_nor_parse_cmd_to_8dtr(nor,
+ param_header, params);
+ break;
+
default:
break;
}
--
1.9.1

2020-05-28 08:01:57

by Mason Yang

[permalink] [raw]
Subject: [PATCH v3 09/14] mtd: spi-nor: core: add configuration register 2 read & write support

Configuration register 2 is to set the device operation condition like
STR or DTR mode at address offset 0 and DQS mode at address offset 0x200.

Each device has various address offset for it's specific operatoin
setting.

Signed-off-by: Mason Yang <[email protected]>
---
drivers/mtd/spi-nor/core.c | 78 ++++++++++++++++++++++++++++++++++++++++++++++
drivers/mtd/spi-nor/core.h | 2 ++
2 files changed, 80 insertions(+)

diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c
index 642e3c0..117d117 100644
--- a/drivers/mtd/spi-nor/core.c
+++ b/drivers/mtd/spi-nor/core.c
@@ -347,6 +347,84 @@ int spi_nor_write_disable(struct spi_nor *nor)
}

/**
+ * spi_nor_read_cr2() - Read the Configuration Register 2.
+ * @nor: pointer to 'struct spi_nor'.
+ * @addr: offset address to read.
+ * @cr2: pointer to a DMA-able buffer where the value of the
+ * Configuration Register 2 will be written.
+ *
+ * Return: 0 on success, -errno otherwise.
+ */
+int spi_nor_read_cr2(struct spi_nor *nor, u32 addr, u8 *cr2)
+{
+ int ret;
+ u8 cmd = nor->params->rd_reg_cmd;
+
+ if (nor->spimem) {
+ struct spi_mem_op op =
+ SPI_MEM_OP(SPI_MEM_OP_CMD(cmd, 1),
+ SPI_MEM_OP_ADDR(4, addr, 1),
+ SPI_MEM_OP_DUMMY(4, 1),
+ SPI_MEM_OP_DATA_IN(1, cr2, 1));
+
+ spi_nor_spimem_setup_op(nor, &op, nor->reg_proto);
+
+ ret = spi_mem_exec_op(nor->spimem, &op);
+ } else {
+ if (spi_nor_protocol_is_dtr(nor->reg_proto))
+ ret = -ENOTSUPP;
+ else
+ ret = nor->controller_ops->read_reg(nor, cmd, cr2, 1);
+ }
+
+ if (ret)
+ dev_dbg(nor->dev, "error %d reading CR2\n", ret);
+
+ return ret;
+}
+
+/**
+ * spi_nor_write_cr2() - Write the Configuration Register 2.
+ * @nor: pointer to 'struct spi_nor'.
+ * @addr: offset address to write.
+ * @cr2: pointer to a DMA-able buffer where the value of the
+ * Configuratin Register 2 will be read.
+ *
+ * Return: 0 on success, -errno otherwise.
+ */
+int spi_nor_write_cr2(struct spi_nor *nor, u32 addr, u8 *cr2)
+{
+ int ret;
+ u8 cmd = nor->params->wr_reg_cmd;
+
+ ret = spi_nor_write_enable(nor);
+ if (ret)
+ return ret;
+
+ if (nor->spimem) {
+ struct spi_mem_op op =
+ SPI_MEM_OP(SPI_MEM_OP_CMD(cmd, 1),
+ SPI_MEM_OP_ADDR(4, addr, 1),
+ SPI_MEM_OP_NO_DUMMY,
+ SPI_MEM_OP_DATA_OUT(1, cr2, 1));
+
+ spi_nor_spimem_setup_op(nor, &op, nor->reg_proto);
+
+ ret = spi_mem_exec_op(nor->spimem, &op);
+ } else {
+ if (spi_nor_protocol_is_dtr(nor->reg_proto))
+ ret = -ENOTSUPP;
+ else
+ ret = nor->controller_ops->write_reg(nor, cmd, cr2, 1);
+ }
+
+ if (ret)
+ dev_dbg(nor->dev, "error %d write CFG Reg 2\n", ret);
+
+ return ret;
+}
+
+/**
* 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
diff --git a/drivers/mtd/spi-nor/core.h b/drivers/mtd/spi-nor/core.h
index 101726c..91bc69a 100644
--- a/drivers/mtd/spi-nor/core.h
+++ b/drivers/mtd/spi-nor/core.h
@@ -438,6 +438,8 @@ void spi_nor_spimem_setup_op(const struct spi_nor *nor,
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_read_cr2(struct spi_nor *nor, u32 addr, u8 *cr2);
+int spi_nor_write_cr2(struct spi_nor *nor, u32 addr, u8 *cr2);
int spi_nor_set_4byte_addr_mode(struct spi_nor *nor, bool enable);
int spi_nor_write_ear(struct spi_nor *nor, u8 ear);
int spi_nor_wait_till_ready(struct spi_nor *nor);
--
1.9.1

2020-05-28 08:02:08

by Mason Yang

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

From: Pratyush Yadav <[email protected]>

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 | 23 +++++++++++++++++++++--
1 file changed, 21 insertions(+), 2 deletions(-)

diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c
index 117d117..cbfdf544 100644
--- a/drivers/mtd/spi-nor/core.c
+++ b/drivers/mtd/spi-nor/core.c
@@ -435,6 +435,8 @@ int spi_nor_write_cr2(struct spi_nor *nor, u32 addr, u8 *cr2)
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 =
@@ -443,10 +445,19 @@ 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;
+ }
+
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)
@@ -466,6 +477,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 =
@@ -474,6 +487,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);
--
1.9.1

2020-05-28 08:02:20

by Mason Yang

[permalink] [raw]
Subject: [PATCH v3 13/14] mtd: spi-nor: core: execute command sequences to change octal DTR mode

Execute command sequences to change octal DTR mode.

Signed-off-by: Mason Yang <[email protected]>
---
drivers/mtd/spi-nor/core.c | 71 ++++++++++++++++++++++++++++++++++++++++++++++
drivers/mtd/spi-nor/core.h | 3 ++
2 files changed, 74 insertions(+)

diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c
index a353830..caac58cd 100644
--- a/drivers/mtd/spi-nor/core.c
+++ b/drivers/mtd/spi-nor/core.c
@@ -425,6 +425,77 @@ int spi_nor_write_cr2(struct spi_nor *nor, u32 addr, u8 *cr2)
}

/**
+ * spi_nor_cmd_seq_octal_dtr() - command sequences to change to octal DTR mode
+ * @nor: pointer to 'struct spi_nor'.
+ * @enable: enable Octal DTR.
+ *
+ * Return: 0 on success, -errno otherwise.
+ */
+int spi_nor_cmd_seq_octal_dtr(struct spi_nor *nor, bool enable)
+{
+ struct spi_nor_flash_parameter *p = nor->params;
+ struct cmd_seq_octal_dtr *cs = p->cmd_seq;
+ int i, ret;
+ struct spi_mem_op op;
+
+ if (!nor->spimem || !p->cmd_seq[0].len)
+ return -ENOTSUPP;
+
+ if (!enable)
+ return 0;
+
+ for (i = 0; i < CMD_SEQ_OCTAL_DTR_DWORD_LEN; i++) {
+ switch (p->cmd_seq[i].len) {
+ case 1:
+ op = (struct spi_mem_op)
+ SPI_MEM_OP(SPI_MEM_OP_CMD(cs[i].opcode, 1),
+ SPI_MEM_OP_NO_ADDR,
+ SPI_MEM_OP_NO_DUMMY,
+ SPI_MEM_OP_NO_DATA);
+
+ ret = spi_mem_exec_op(nor->spimem, &op);
+ if (ret)
+ return ret;
+ break;
+
+ case 3:
+ op = (struct spi_mem_op)
+ SPI_MEM_OP(SPI_MEM_OP_CMD(cs[i].opcode, 1),
+ SPI_MEM_OP_ADDR(1, cs[i].addr, 1),
+ SPI_MEM_OP_NO_DUMMY,
+ SPI_MEM_OP_DATA_OUT(1,
+ &cs[i].data, 1));
+
+ ret = spi_mem_exec_op(nor->spimem, &op);
+ if (ret)
+ return ret;
+ break;
+
+ case 6:
+ op = (struct spi_mem_op)
+ SPI_MEM_OP(SPI_MEM_OP_CMD(cs[i].opcode, 1),
+ SPI_MEM_OP_ADDR(4, cs[i].addr, 1),
+ SPI_MEM_OP_NO_DUMMY,
+ SPI_MEM_OP_DATA_OUT(1,
+ &cs[i].data, 1));
+
+ ret = spi_mem_exec_op(nor->spimem, &op);
+ if (ret)
+ return ret;
+ break;
+
+ default:
+ dev_err(nor->dev,
+ "Error %d sequences to Octal DTR\n",
+ p->cmd_seq[i].len);
+ break;
+ }
+ }
+
+ return ret;
+}
+
+/**
* 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
diff --git a/drivers/mtd/spi-nor/core.h b/drivers/mtd/spi-nor/core.h
index 9b0e0ba..b47bc44d 100644
--- a/drivers/mtd/spi-nor/core.h
+++ b/drivers/mtd/spi-nor/core.h
@@ -199,6 +199,8 @@ struct cmd_seq_octal_dtr {
u8 data;
};

+#define CMD_SEQ_OCTAL_DTR_DWORD_LEN 4
+
/**
* struct spi_nor_flash_parameter - SPI NOR flash parameters and settings.
* Includes legacy flash parameters and settings that can be overwritten
@@ -442,6 +444,7 @@ void spi_nor_spimem_setup_op(const struct spi_nor *nor,
int spi_nor_write_disable(struct spi_nor *nor);
int spi_nor_read_cr2(struct spi_nor *nor, u32 addr, u8 *cr2);
int spi_nor_write_cr2(struct spi_nor *nor, u32 addr, u8 *cr2);
+int spi_nor_cmd_seq_octal_dtr(struct spi_nor *nor, bool enable);
int spi_nor_set_4byte_addr_mode(struct spi_nor *nor, bool enable);
int spi_nor_write_ear(struct spi_nor *nor, u8 ear);
int spi_nor_wait_till_ready(struct spi_nor *nor);
--
1.9.1

2020-05-28 08:02:32

by Mason Yang

[permalink] [raw]
Subject: [PATCH v3 12/14] spi: mxic: patch for octal DTR mode support

Driver patch for octal 8D-8D-8D mode support.

Signed-off-by: Mason Yang <[email protected]>
---
drivers/spi/spi-mxic.c | 101 +++++++++++++++++++++++++++++++++----------------
1 file changed, 69 insertions(+), 32 deletions(-)

diff --git a/drivers/spi/spi-mxic.c b/drivers/spi/spi-mxic.c
index 69491f3..c83c8c2 100644
--- a/drivers/spi/spi-mxic.c
+++ b/drivers/spi/spi-mxic.c
@@ -280,10 +280,58 @@ static void mxic_spi_hw_init(struct mxic_spi *mxic)
mxic->regs + HC_CFG);
}

+static u32 mxic_spi_mem_prep_op_cfg(const struct spi_mem_op *op)
+{
+ u32 cfg = OP_CMD_BYTES(op->cmd.nbytes) |
+ OP_CMD_BUSW(fls(op->cmd.buswidth) - 1) |
+ (op->cmd.dtr ? OP_CMD_DDR : 0);
+
+ if (op->addr.nbytes)
+ cfg |= OP_ADDR_BYTES(op->addr.nbytes) |
+ OP_ADDR_BUSW(fls(op->addr.buswidth) - 1) |
+ (op->addr.dtr ? OP_ADDR_DDR : 0);
+
+ if (op->dummy.nbytes)
+ cfg |= OP_DUMMY_CYC(op->dummy.nbytes);
+
+ if (op->data.nbytes) {
+ cfg |= OP_DATA_BUSW(fls(op->data.buswidth) - 1) |
+ (op->data.dtr ? OP_DATA_DDR : 0);
+ if (op->data.dir == SPI_MEM_DATA_IN) {
+ cfg |= OP_READ;
+ if (op->data.dtr == OP_DATA_DDR)
+ cfg |= OP_DQS_EN;
+ }
+ }
+
+ return cfg;
+}
+
+static void mxic_spi_set_hc_cfg(struct spi_device *spi, u32 flags)
+{
+ struct mxic_spi *mxic = spi_master_get_devdata(spi->master);
+ int nio = 1;
+
+ if (spi->mode & (SPI_RX_OCTAL | SPI_TX_OCTAL))
+ nio = 8;
+ else if (spi->mode & (SPI_TX_QUAD | SPI_RX_QUAD))
+ nio = 4;
+ else if (spi->mode & (SPI_TX_DUAL | SPI_RX_DUAL))
+ nio = 2;
+
+ writel(flags | HC_CFG_NIO(nio) |
+ HC_CFG_TYPE(spi->chip_select, HC_CFG_TYPE_SPI_NOR) |
+ HC_CFG_SLV_ACT(spi->chip_select) | HC_CFG_IDLE_SIO_LVL(1),
+ mxic->regs + HC_CFG);
+}
+
static int mxic_spi_data_xfer(struct mxic_spi *mxic, const void *txbuf,
void *rxbuf, unsigned int len)
{
unsigned int pos = 0;
+ bool dtr_enabled;
+
+ dtr_enabled = (readl(mxic->regs + SS_CTRL(0)) & OP_DATA_DDR);

while (pos < len) {
unsigned int nbytes = len - pos;
@@ -302,6 +350,9 @@ static int mxic_spi_data_xfer(struct mxic_spi *mxic, const void *txbuf,
if (ret)
return ret;

+ if (dtr_enabled && len & 0x1)
+ nbytes++;
+
writel(data, mxic->regs + TXD(nbytes % 4));

if (rxbuf) {
@@ -319,6 +370,8 @@ static int mxic_spi_data_xfer(struct mxic_spi *mxic, const void *txbuf,

data = readl(mxic->regs + RXD);
data >>= (8 * (4 - nbytes));
+ if (dtr_enabled && len & 0x1)
+ nbytes++;
memcpy(rxbuf + pos, &data, nbytes);
WARN_ON(readl(mxic->regs + INT_STS) & INT_RX_NOT_EMPTY);
} else {
@@ -335,8 +388,8 @@ static int mxic_spi_data_xfer(struct mxic_spi *mxic, const void *txbuf,
static bool mxic_spi_mem_supports_op(struct spi_mem *mem,
const struct spi_mem_op *op)
{
- if (op->data.buswidth > 4 || op->addr.buswidth > 4 ||
- op->dummy.buswidth > 4 || op->cmd.buswidth > 4)
+ if (op->data.buswidth > 8 || op->addr.buswidth > 8 ||
+ op->dummy.buswidth > 8 || op->cmd.buswidth > 8)
return false;

if (op->data.nbytes && op->dummy.nbytes &&
@@ -346,6 +399,9 @@ static bool mxic_spi_mem_supports_op(struct spi_mem *mem,
if (op->addr.nbytes > 7)
return false;

+ if (op->cmd.buswidth == 8 && op->cmd.nbytes == 2)
+ return true;
+
return spi_mem_default_supports_op(mem, op);
}

@@ -353,47 +409,27 @@ static int mxic_spi_mem_exec_op(struct spi_mem *mem,
const struct spi_mem_op *op)
{
struct mxic_spi *mxic = spi_master_get_devdata(mem->spi->master);
- int nio = 1, i, ret;
- u32 ss_ctrl;
- u8 addr[8];
+ int i, ret;
+ u8 addr[8], cmd[2];

ret = mxic_spi_set_freq(mxic, mem->spi->max_speed_hz);
if (ret)
return ret;

- if (mem->spi->mode & (SPI_TX_QUAD | SPI_RX_QUAD))
- nio = 4;
- else if (mem->spi->mode & (SPI_TX_DUAL | SPI_RX_DUAL))
- nio = 2;
+ mxic_spi_set_hc_cfg(mem->spi, HC_CFG_MAN_CS_EN);

- writel(HC_CFG_NIO(nio) |
- HC_CFG_TYPE(mem->spi->chip_select, HC_CFG_TYPE_SPI_NOR) |
- HC_CFG_SLV_ACT(mem->spi->chip_select) | HC_CFG_IDLE_SIO_LVL(1) |
- HC_CFG_MAN_CS_EN,
- mxic->regs + HC_CFG);
writel(HC_EN_BIT, mxic->regs + HC_EN);

- ss_ctrl = OP_CMD_BYTES(1) | OP_CMD_BUSW(fls(op->cmd.buswidth) - 1);
-
- if (op->addr.nbytes)
- ss_ctrl |= OP_ADDR_BYTES(op->addr.nbytes) |
- OP_ADDR_BUSW(fls(op->addr.buswidth) - 1);
-
- if (op->dummy.nbytes)
- ss_ctrl |= OP_DUMMY_CYC(op->dummy.nbytes);
-
- if (op->data.nbytes) {
- ss_ctrl |= OP_DATA_BUSW(fls(op->data.buswidth) - 1);
- if (op->data.dir == SPI_MEM_DATA_IN)
- ss_ctrl |= OP_READ;
- }
-
- writel(ss_ctrl, mxic->regs + SS_CTRL(mem->spi->chip_select));
+ writel(mxic_spi_mem_prep_op_cfg(op),
+ mxic->regs + SS_CTRL(mem->spi->chip_select));

writel(readl(mxic->regs + HC_CFG) | HC_CFG_MAN_CS_ASSERT,
mxic->regs + HC_CFG);

- ret = mxic_spi_data_xfer(mxic, &op->cmd.opcode, NULL, 1);
+ for (i = 0; i < op->cmd.nbytes; i++)
+ cmd[i] = op->cmd.opcode >> (8 * (op->cmd.nbytes - i - 1));
+
+ ret = mxic_spi_data_xfer(mxic, cmd, NULL, op->cmd.nbytes);
if (ret)
goto out;

@@ -566,7 +602,8 @@ static int mxic_spi_probe(struct platform_device *pdev)
master->bits_per_word_mask = SPI_BPW_MASK(8);
master->mode_bits = SPI_CPOL | SPI_CPHA |
SPI_RX_DUAL | SPI_TX_DUAL |
- SPI_RX_QUAD | SPI_TX_QUAD;
+ SPI_RX_QUAD | SPI_TX_QUAD |
+ SPI_RX_OCTAL | SPI_TX_OCTAL;

mxic_spi_hw_init(mxic);

--
1.9.1

2020-05-28 08:02:39

by Mason Yang

[permalink] [raw]
Subject: [PATCH v3 14/14] mtd: spi-nor: macronix: Add Octal 8D-8D-8D supports for Macronix mx25uw51245g

Macronix mx25uw51245g is a SPI NOR that supports 1-1-1/8-8-8 mode.

Correct the dummy cycles to device for various frequencies
after xSPI profile 1.0 table parsed.

Enable mx25uw51245g to Octal DTR mode by executing the command sequences
to change to octal DTR mode.

Signed-off-by: Mason Yang <[email protected]>
---
drivers/mtd/spi-nor/macronix.c | 55 ++++++++++++++++++++++++++++++++++++++++++
1 file changed, 55 insertions(+)

diff --git a/drivers/mtd/spi-nor/macronix.c b/drivers/mtd/spi-nor/macronix.c
index 96735d8..6c9a24c 100644
--- a/drivers/mtd/spi-nor/macronix.c
+++ b/drivers/mtd/spi-nor/macronix.c
@@ -8,6 +8,57 @@

#include "core.h"

+#define MXIC_CR2_DUMMY_SET_ADDR 0x300
+
+/* Fixup the dummy cycles to device and setup octa_dtr_enable() */
+static void mx25uw51245g_post_sfdp_fixups(struct spi_nor *nor)
+{
+ struct spi_nor_flash_parameter *params = nor->params;
+ int ret;
+ u8 rdc, wdc;
+
+ ret = spi_nor_read_cr2(nor, MXIC_CR2_DUMMY_SET_ADDR, &rdc);
+ if (ret)
+ return;
+
+ /* Refer to dummy cycle and frequency table(MHz) */
+ switch (params->dummy_cycles) {
+ case 10: /* 10 dummy cycles for 104 MHz */
+ wdc = 5;
+ break;
+ case 12: /* 12 dummy cycles for 133 MHz */
+ wdc = 4;
+ break;
+ case 16: /* 16 dummy cycles for 166 MHz */
+ wdc = 2;
+ break;
+ case 18: /* 18 dummy cycles for 173 MHz */
+ wdc = 1;
+ break;
+ case 20: /* 20 dummy cycles for 200 MHz */
+ default:
+ wdc = 0;
+ }
+
+ if (rdc != wdc)
+ spi_nor_write_cr2(nor, MXIC_CR2_DUMMY_SET_ADDR, &wdc);
+
+ if (params->cmd_seq[0].len) {
+ params->octal_dtr_enable = spi_nor_cmd_seq_octal_dtr;
+ params->hwcaps.mask |= SNOR_HWCAPS_READ_8_8_8_DTR;
+ params->hwcaps.mask |= SNOR_HWCAPS_PP_8_8_8_DTR;
+
+ } else {
+ params->octal_dtr_enable = NULL;
+ params->hwcaps.mask &= ~SNOR_HWCAPS_READ_8_8_8_DTR;
+ params->hwcaps.mask &= ~SNOR_HWCAPS_PP_8_8_8_DTR;
+ }
+}
+
+static struct spi_nor_fixups mx25uw51245g_fixups = {
+ .post_sfdp = mx25uw51245g_post_sfdp_fixups,
+};
+
static int
mx25l25635_post_bfpt_fixups(struct spi_nor *nor,
const struct sfdp_parameter_header *bfpt_header,
@@ -84,6 +135,10 @@
SPI_NOR_QUAD_READ) },
{ "mx66l1g55g", INFO(0xc2261b, 0, 64 * 1024, 2048,
SPI_NOR_QUAD_READ) },
+ { "mx25uw51245g", INFO(0xc2813a, 0, 64 * 1024, 1024,
+ SECT_4K | SPI_NOR_4B_OPCODES |
+ SPI_NOR_OCTAL_DTR_READ)
+ .fixups = &mx25uw51245g_fixups },
};

static void macronix_default_init(struct spi_nor *nor)
--
1.9.1

2020-05-28 08:02:42

by Mason Yang

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

From: Pratyush Yadav <[email protected]>

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 adaa0c4..d8905e0 100644
--- a/drivers/spi/spi-mem.c
+++ b/drivers/spi/spi-mem.c
@@ -154,6 +154,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 af9ff2f..e3dcb95 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 {
--
1.9.1

2020-05-28 08:03:18

by Mason Yang

[permalink] [raw]
Subject: [PATCH v3 07/14] mtd: spi-nor: sfdp: parse xSPI Profile 1.0 table

JESD251, xSPI profile 1.0 table supports octal DTR mode.
Extract information like the fast read opcode, dummy cycles for various
frequencies, the number of dummy cycles needed for a Read Status
Register command, the number of address bytes needed for a Read
Status Register command, read volatile register command and write
volatile register command.

According to BFPT 20th DWORD of octal maximum speed, driver get it's
specific dummy cycles from profile 1.0 table and then could update
it to device by their fixup hooks.

Since driver get octal DTR read opcode and then set read settings,
expose spi_nor_set_read_settings() in core.h.

Signed-off-by: Mason Yang <[email protected]>
---
drivers/mtd/spi-nor/core.c | 2 +-
drivers/mtd/spi-nor/core.h | 16 +++++++
drivers/mtd/spi-nor/sfdp.c | 106 +++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 123 insertions(+), 1 deletion(-)

diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c
index 388e695..642e3c0 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 f644ed1..837f5e9 100644
--- a/drivers/mtd/spi-nor/core.h
+++ b/drivers/mtd/spi-nor/core.h
@@ -193,6 +193,12 @@ struct spi_nor_locking_ops {
* @size: the flash memory density in bytes.
* @page_size: the page size of the SPI NOR flash memory.
* @octal_max_speed: maximum operation speed of device in octal mode.
+ * @rdsr_dummy: dummy cycles needed for Read Status Register command.
+ * @rdsr_addr_nbytes: dummy address bytes needed for Read Status Register
+ * command.
+ * @rd_reg_cmd: read volatile register command for xSPI device.
+ * @wr_reg_cmd: write volatile register command for xSPI device.
+ * @dummy_cycles: dummy cycles used for various frequencies.
* @hwcaps: describes the read and page program hardware
* capabilities.
* @reads: read capabilities ordered by priority: the higher index
@@ -216,6 +222,11 @@ struct spi_nor_flash_parameter {
u64 size;
u32 page_size;
u16 octal_max_speed;
+ u8 rdsr_dummy;
+ u8 rdsr_addr_nbytes;
+ u8 rd_reg_cmd;
+ u8 wr_reg_cmd;
+ u8 dummy_cycles;

struct spi_nor_hwcaps hwcaps;
struct spi_nor_read_command reads[SNOR_CMD_READ_MAX];
@@ -426,6 +437,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 d7ef47a..550422b 100644
--- a/drivers/mtd/spi-nor/sfdp.c
+++ b/drivers/mtd/spi-nor/sfdp.c
@@ -20,6 +20,7 @@
#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
@@ -27,6 +28,27 @@
#define SFDP_JESD216A_MINOR 5
#define SFDP_JESD216B_MINOR 6

+/* xSPI Profile 1.0 table (from JESD216D.01). */
+#define XSPI_PF1_DWORD1_RD_CMD GENMASK(15, 8)
+#define XSPI_PF1_DWORD1_RDSR_ADDR_BYTES BIT(29)
+#define XSPI_PF1_DWORD1_RDSR_DUMMY_CYCLES BIT(28)
+#define XSPI_PF1_DWORD2_RD_REG_CMD GENMASK(31, 24)
+#define XSPI_PF1_DWORD2_WR_REG_CMD GENMASK(15, 8)
+#define XSPI_PF1_DUMMY_CYCLES_DEFAULT 20
+#define XSPI_DWORD(x) ((x) - 1)
+#define XSPI_DWORD_MAX 5
+
+struct sfdp_xspi {
+ u32 dwords[XSPI_DWORD_MAX];
+};
+
+struct xspi_dummy_cycles {
+ u16 speed_hz; /* Speed MHz */
+ u8 dwords; /* Dwords index */
+ u32 mask; /* Mask */
+ u8 shift; /* Bit shift */
+};
+
/* Basic Flash Parameter Table 20th DWORD, Max operation speed of device */
struct octal_max_speed {
u8 idx; /* Bits value */
@@ -1146,6 +1168,86 @@ static int spi_nor_parse_4bait(struct spi_nor *nor,
}

/**
+ * spi_nor_parse_profile1() - parse the xSPI Profile 1.0 table
+ * @nor: pointer to a 'struct spi_nor'
+ * @param_header: xspi profile 1 parameter table header
+ * @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 *header,
+ struct spi_nor_flash_parameter *params)
+{
+ struct sfdp_xspi pfile1;
+ u8 opcode;
+ u32 i, addr;
+ size_t len;
+ int ret;
+ static const struct xspi_dummy_cycles dummy[] = {
+ /* {MHz, Dwords index, Mask, Bit shift} */
+ { 200, 4, GENMASK(11, 7), 7},
+ { 166, 5, GENMASK(31, 27), 27},
+ { 133, 5, GENMASK(21, 17), 17},
+ { 100, 5, GENMASK(11, 7), 7},
+ };
+
+ if (header->major != SFDP_JESD216_MAJOR ||
+ header->length < XSPI_DWORD_MAX)
+ return -EINVAL;
+
+ len = min_t(size_t, sizeof(pfile1),
+ header->length * sizeof(u32));
+
+ memset(&pfile1, 0, sizeof(pfile1));
+
+ addr = SFDP_PARAM_HEADER_PTP(header);
+ ret = spi_nor_read_sfdp(nor, addr, len, &pfile1);
+ if (ret)
+ goto out;
+
+ /* Fix endianness of the xSPI 1.0 DWORDs. */
+ le32_to_cpu_array(pfile1.dwords, XSPI_DWORD_MAX);
+
+ /* Get 8D-8D-8D fast read opcode and dummy cycles. */
+ opcode = FIELD_GET(XSPI_PF1_DWORD1_RD_CMD,
+ pfile1.dwords[XSPI_DWORD(1)]);
+
+ if (pfile1.dwords[XSPI_DWORD(1)] & XSPI_PF1_DWORD1_RDSR_ADDR_BYTES)
+ params->rdsr_addr_nbytes = 4;
+ else
+ params->rdsr_addr_nbytes = 0;
+
+ if (pfile1.dwords[XSPI_DWORD(1)] & XSPI_PF1_DWORD1_RDSR_DUMMY_CYCLES)
+ params->rdsr_dummy = 8;
+ else
+ params->rdsr_dummy = 4;
+
+ params->rd_reg_cmd = FIELD_GET(XSPI_PF1_DWORD2_RD_REG_CMD,
+ pfile1.dwords[XSPI_DWORD(2)]);
+ params->wr_reg_cmd = FIELD_GET(XSPI_PF1_DWORD2_WR_REG_CMD,
+ pfile1.dwords[XSPI_DWORD(2)]);
+
+ /* according to BFPT 20th DOWRD to get devices dummy cycles */
+ for (i = 0; i < ARRAY_SIZE(dummy); i++) {
+ if (params->octal_max_speed == dummy[i].speed_hz) {
+ params->dummy_cycles = (dummy[i].mask &
+ pfile1.dwords[XSPI_DWORD(dummy[i].dwords)]) >>
+ dummy[i].shift;
+ break;
+ }
+ }
+ if (i == ARRAY_SIZE(dummy))
+ params->dummy_cycles = XSPI_PF1_DUMMY_CYCLES_DEFAULT;
+
+ spi_nor_set_read_settings(&params->reads[SNOR_CMD_READ_8_8_8_DTR],
+ 0, params->dummy_cycles,
+ opcode, SNOR_PROTO_8_8_8_DTR);
+out:
+ return ret;
+}
+
+/**
* spi_nor_parse_sfdp() - parse the Serial Flash Discoverable Parameters.
* @nor: pointer to a 'struct spi_nor'
* @params: pointer to the 'struct spi_nor_flash_parameter' to be
@@ -1246,6 +1348,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;
}
--
1.9.1

2020-05-28 08:04:23

by Mason Yang

[permalink] [raw]
Subject: [PATCH v3 11/14] mtd: spi-nor: core: enable octal DTR mode when possible

From: Pratyush Yadav <[email protected]>

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 cbfdf544..a353830 100644
--- a/drivers/mtd/spi-nor/core.c
+++ b/drivers/mtd/spi-nor/core.c
@@ -3162,6 +3162,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'
@@ -3201,6 +3230,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 91bc69a..9b0e0ba 100644
--- a/drivers/mtd/spi-nor/core.h
+++ b/drivers/mtd/spi-nor/core.h
@@ -223,6 +223,7 @@ struct cmd_seq_octal_dtr {
* @erase_map: the erase map parsed from the SFDP Sector Map Parameter
* Table.
* @cmd_seq: command sequence to change to octal DTR mode.
+ * @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
@@ -252,6 +253,7 @@ struct spi_nor_flash_parameter {

struct cmd_seq_octal_dtr cmd_seq[4];

+ 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);
--
1.9.1

2020-05-28 08:30:39

by Boris Brezillon

[permalink] [raw]
Subject: Re: [PATCH v3 00/14] mtd: spi-nor: add xSPI Octal DTR support

On Thu, 28 May 2020 15:58:02 +0800
Mason Yang <[email protected]> wrote:

> Hello,
>
> JESD216C has defined specification for Octal 8S-8S-8S and 8D-8D-8D.
> Based on JEDEC216C Basic Flash Parameter Table (BFPT) driver extract
> DWORD-18: command and command extension type.
> DWORD-20: Maximum operation speed of device in Octal mode.
>
> xSPI profile 1.0 table:
> DWORD-1: Read Fast command, the number of dummy cycles and address nbytes
> for Read Status Register command.
> DWORD-2: Read/Write volatile Register command for CFG Reg2.
> DWORD-4 and DWORD-5: dummy cycles used for various frequencies based on
> maximum speed of device from BFPT 20th DWORD.
>
> Ccommand sequences to change to octal DTR mode:
> The length of each command sequence is 8 per byte for single SPI mode and
> patching driver to parse and execute these sequences for octal DTR mode.
>
> By Vignesh's comments to patch these drivers based on Pratyush's patches
> set [1].
>
> This series adds support for Macronix mx25uw51245g works in octal DTR mode.
>
> Tested on Macronix's Zynq PicoZed board with Macronix's SPI controller
> (spi-mxic.c) driver patched on mx25uw51245g Octal flash.
>
>
> [1] https://patchwork.ozlabs.org/project/linux-mtd/cover/[email protected]/
>
>
> Summary of change log
> v3:
> Add support command sequences to change octal DTR mode and based on
> part of Pratyush's patches set.
>
> v2:
> Parse BFPT & xSPI table for Octal 8D-8D-8D mode parameters and enable Octal
> mode in spi_nor_late_init_params().
> Using Macros in spi_nor_spimem_read_data, spi_nor_spimem_write_data and
> so on by Vignesh comments.
>
> v1:
> Without parsing BFPT & xSPI profile 1.0 table and enter Octal 8D-8D-8D
> mode directly in spi_nor_fixups hooks.
>
>
> thnaks for your time and review.
> best regards,
> Mason
>
> --
> Mason Yang (7):
> mtd: spi-nor: sfdp: get octal mode maximum speed from BFPT
> mtd: spi-nor: sfdp: parse xSPI Profile 1.0 table
> mtd: spi-nor: sfdp: parse command sequences to change octal DTR mode
> mtd: spi-nor: core: add configuration register 2 read & write support
> spi: mxic: patch for octal DTR mode support
> mtd: spi-nor: core: execute command sequences to change octal DTR mode
> mtd: spi-nor: macronix: Add Octal 8D-8D-8D supports for Macronix
> mx25uw51245g
>
> Pratyush Yadav (7):
> spi: spi-mem: allow specifying whether an op is DTR or not
> spi: spi-mem: allow specifying a command's extension
> mtd: spi-nor: add support for DTR protocol
> 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: core: use dummy cycle and address width info from SFDP
> mtd: spi-nor: core: enable octal DTR mode when possible

Why are you doing that?! This series is being actively worked on by
Pratyush, and all you gain by sending it on your own is more
confusion. If you have patches on top of a series that's not been
merged yet, mention the dependency in the cover letter, but don't
resend patches that have already been sent and are being reviewed.

I think it's time you spend a bit of time learning about the submission
process, because that's not the first mistake you do, and I'm pretty
sure I already mentioned that in my previous reviews.

2020-05-28 21:49:04

by kernel test robot

[permalink] [raw]
Subject: Re: [PATCH v3 12/14] spi: mxic: patch for octal DTR mode support

Hi Mason,

Thank you for the patch! Perhaps something to improve:

[auto build test WARNING on next-20200526]
[also build test WARNING on v5.7-rc7]
[cannot apply to spi/for-next xlnx/master linus/master linux/master v5.7-rc7 v5.7-rc6 v5.7-rc5]
[if your patch is applied to the wrong git tree, please drop us a note to help
improve the system. BTW, we also suggest to use '--base' option to specify the
base tree in git format-patch, please see https://stackoverflow.com/a/37406982]

url: https://github.com/0day-ci/linux/commits/Mason-Yang/mtd-spi-nor-add-xSPI-Octal-DTR-support/20200528-162925
base: b0523c7b1c9d0edcd6c0fe6d2cb558a9ad5c60a8
config: x86_64-allyesconfig (attached as .config)
compiler: clang version 11.0.0 (https://github.com/llvm/llvm-project 2d068e534f1671459e1b135852c1b3c10502e929)
reproduce (this is a W=1 build):
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# install x86_64 cross compiling tool for clang build
# apt-get install binutils-x86-64-linux-gnu
# save the attached .config to linux build tree
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross ARCH=x86_64

If you fix the issue, kindly add following tag as appropriate
Reported-by: kbuild test robot <[email protected]>

All warnings (new ones prefixed by >>, old ones prefixed by <<):

>> drivers/spi/spi-mxic.c:302:21: warning: result of comparison of constant 256 with expression of type 'const u8' (aka 'const unsigned char') is always false [-Wtautological-constant-out-of-range-compare]
if (op->data.dtr == OP_DATA_DDR)
~~~~~~~~~~~~ ^ ~~~~~~~~~~~
1 warning generated.

vim +302 drivers/spi/spi-mxic.c

282
283 static u32 mxic_spi_mem_prep_op_cfg(const struct spi_mem_op *op)
284 {
285 u32 cfg = OP_CMD_BYTES(op->cmd.nbytes) |
286 OP_CMD_BUSW(fls(op->cmd.buswidth) - 1) |
287 (op->cmd.dtr ? OP_CMD_DDR : 0);
288
289 if (op->addr.nbytes)
290 cfg |= OP_ADDR_BYTES(op->addr.nbytes) |
291 OP_ADDR_BUSW(fls(op->addr.buswidth) - 1) |
292 (op->addr.dtr ? OP_ADDR_DDR : 0);
293
294 if (op->dummy.nbytes)
295 cfg |= OP_DUMMY_CYC(op->dummy.nbytes);
296
297 if (op->data.nbytes) {
298 cfg |= OP_DATA_BUSW(fls(op->data.buswidth) - 1) |
299 (op->data.dtr ? OP_DATA_DDR : 0);
300 if (op->data.dir == SPI_MEM_DATA_IN) {
301 cfg |= OP_READ;
> 302 if (op->data.dtr == OP_DATA_DDR)
303 cfg |= OP_DQS_EN;
304 }
305 }
306
307 return cfg;
308 }
309

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/[email protected]


Attachments:
(No filename) (2.72 kB)
.config.gz (72.83 kB)
Download all attachments

2020-05-29 05:29:24

by Mason Yang

[permalink] [raw]
Subject: Re: [PATCH v3 00/14] mtd: spi-nor: add xSPI Octal DTR support


Hi Boris,

> >
> >
> > Summary of change log
> > v3:
> > Add support command sequences to change octal DTR mode and based on
> > part of Pratyush's patches set.
> >
> > v2:
> > Parse BFPT & xSPI table for Octal 8D-8D-8D mode parameters and enable
Octal
> > mode in spi_nor_late_init_params().
> > Using Macros in spi_nor_spimem_read_data, spi_nor_spimem_write_data
and
> > so on by Vignesh comments.
> >
> > v1:
> > Without parsing BFPT & xSPI profile 1.0 table and enter Octal 8D-8D-8D
> > mode directly in spi_nor_fixups hooks.
> >
> >
> > thnaks for your time and review.
> > best regards,
> > Mason
> >
> > --
> > Mason Yang (7):
> > mtd: spi-nor: sfdp: get octal mode maximum speed from BFPT
> > mtd: spi-nor: sfdp: parse xSPI Profile 1.0 table
> > mtd: spi-nor: sfdp: parse command sequences to change octal DTR mode
> > mtd: spi-nor: core: add configuration register 2 read & write
support
> > spi: mxic: patch for octal DTR mode support
> > mtd: spi-nor: core: execute command sequences to change octal DTR
mode
> > mtd: spi-nor: macronix: Add Octal 8D-8D-8D supports for Macronix
> > mx25uw51245g
> >
> > Pratyush Yadav (7):
> > spi: spi-mem: allow specifying whether an op is DTR or not
> > spi: spi-mem: allow specifying a command's extension
> > mtd: spi-nor: add support for DTR protocol
> > 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: core: use dummy cycle and address width info from SFDP
> > mtd: spi-nor: core: enable octal DTR mode when possible
>
> Why are you doing that?! This series is being actively worked on by
> Pratyush, and all you gain by sending it on your own is more
> confusion. If you have patches on top of a series that's not been
> merged yet, mention the dependency in the cover letter, but don't
> resend patches that have already been sent and are being reviewed.

okay, much thank for your comments.
Will re-submit and mention the dependency in my cover letter.

Hi Pratyush,
Sorry if these patches make you uncomfortable.

>
> I think it's time you spend a bit of time learning about the submission
> process, because that's not the first mistake you do, and I'm pretty
> sure I already mentioned that in my previous reviews.

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.

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