linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v5 0/6] mtd: spi-nor: parse SFDP tables to setup (Q)SPI memories
@ 2017-03-22 23:33 Cyrille Pitchen
  2017-03-22 23:33 ` [PATCH v5 1/6] mtd: spi-nor: introduce more SPI protocols and the Dual Transfer Mode Cyrille Pitchen
                   ` (6 more replies)
  0 siblings, 7 replies; 27+ messages in thread
From: Cyrille Pitchen @ 2017-03-22 23:33 UTC (permalink / raw)
  To: marek.vasut, linux-mtd, jartur, kdasu.kdev, mar.krzeminski
  Cc: computersforpeace, dwmw2, boris.brezillon, richard, linux-kernel,
	nicolas.ferre, Cyrille Pitchen

Hi all,

based on git-hub/spi-nor and likely applicable on linux-next within the
next few days.


This new series of patchs aims to upgrade to spi-nor framework. We need to
take into account latest SPI memories which cannot be handled correctly by
the current implementation.

For instance, SPI NOR memories like Spansion S25FS512S or Macronix
MX25U4035 support only the Fast Read 1-4-4 (EBh) command but not the
Fast Read 1-1-4 (6Bh) command. However the current spi-nor framework
supports only Fast Read 1-1-4 (6Bh).

Also the Spansion S25FS512S memory (and others) may use a non-uniform
sector erase map, whereas the spi-nor framework assumes that a single
sector erase size and opcode can be used anywhere inside the data array.
This assumption is no longer valid.

Then parsing SFDP tables is an attempt to solve many of those issues by
discovering dynamically most of the parameters and settings of the SPI NOR
memory:
- the flash size
- the page size for Page Program commands
- the supported Fast Read commands with the associated opcodes and number
  of mode/wait-state (dummy) cycles.
- the supported Sector/Block Erase commands with the associated opcodes
  and sizes.
- the erase sector map (for non-uniform memory).


Besides, most QSPI controllers from the different vendors are capable to
support the SPI 1-2-2 and 1-4-4 protocols but the spi-nor framework was
not ready to use them. This series also fixes this issue and computes the
best match between the hardware capabilies of both the SPI memory and the
SPI controler (master) to select the right opcodes and dummy cycles for
Fast Read and Page Program operations.

The new 'struct spi_nor_hwcaps' uses a bitmask to describe all the
supported hardware capabilies and makes the difference between Fast Read
and Page Program operations and also between the different SPI protocols
(SPI 1-1-2 vs SPI 1-2-2 or SPI 1-1-4 vs SPI 1-4-4).


IMHO, the first 3 patches of this series are ready to be merged into the
github/spi-nor tree. Marek, do you agree with that?

Artur, patch 1 introduces some basic support to Octal SPI to help you with
your series. About the op code values, I would like to wait for some JEDEC
specification to provide the reference values or at least the datasheets
of different vendors to guess the actual standard. For now, IMHO, only one
manufacturer is not enough.

Kamal, if you don't mind I've put your Signed-off-by in patch 3 since the
original patch is from you so I wanted to give you the credits :)
I've just fixed some issue: in the original version, set4_byte() was
called from spi_nor_init() even if (nor->addr_width == 3).
I hope it will help you with the suspend/resume series.


The 3 last patches are RFC and are provided so it's more easy for
reviewers to understand the direction I would like to follow to upgrade
the spi-nor framework.

Marcin, I haven't finished yet another 7th patch to parse the sector erase
map table from SFDP so for now patch 4 is almost useless. Besides I only
tested it with uniform memories to avoid regressions. I may need your help
to test it with non-uniform memories, if you don't mind :)


Best regards,

Cyrille


ChangeLog:

v4 -> v5
- rework the whole series.
- introduce support for Octo SPI protocols.
- introduce support for Double Transfer Rate (DTR) protocols.
- introduce support for memories with non-uniform sector erase sizes.

v3 -> v4
- replace dev_info() by dev_dbg() in patch 1.
- split former patch 2 into 2 patches:
  + new patch 2 deals with the rename of SPINOR_OP_READ4_* macros
  + new patch 3 deals with the alternative methode to support memory
> 16MiB
- add comment in patch 3 to describe the dichotomic search performed by
  spi_nor_convert_opcode().
- change return type from int to void for m25p80_proto2nbits() in patch 6.
- remove former patches 8 & 9 from the v2 series: the support of the
  Macronix mx66l1g45g memory will be sent in a separated patch.

v2 -> v3
- tested with new samples: Micron n25q512, n25q01g and Macronix
  mx25v1635f, mx25l3235f, mx25l3273f.
- add "Reviewed-by: Jagan Teki <jagan@openedev.com>" on patch 1.
- add "Tested-by: Vignesh R <vigneshr@ti.com>" on patch 2.
- fix some checkpatch warnings.
- add call of spi_nor_wait_till_ready() in spansion_new_quad_enable()
  and sr2_bit7_quad_enable(), as suggested by Joel Esponde on patch 6.
- test JESD216 rev A (minor 5) instead of rev B (minor 6) with the return
  code of spi_nor_parse_sfdp() from spi_nor_init_params() on patch 6.
  The seven additional DWORDs of the Basic Flash Parameter Table were
  introduced in rev A, not rev B, so the 15th DWORD was already available
  in rev A. The 15th DWORD provides us with the Quad Enable Requirements
  (QER) bits.
  Basic Flash Parameter Table size:
  + JESD216 :  9 DWORDS
  + JESD216A: 16 DWORDS
  + JESD216B: 16 DWORDS

v1 -> v2
- fix patch 3 to resolve compiler errors on hisi-sfc.c and cadence-quadspi.c
  drivers


Cyrille Pitchen (6):
  mtd: spi-nor: introduce more SPI protocols and the Dual Transfer Mode
  mtd: m25p80: add support of SPI 1-2-2 and 1-4-4 protocols
  mtd: spi-nor: add spi_nor_init() function
  mtd: spi-nor: add support to non-uniform SPI NOR flash memories
  mtd: spi-nor: parse Serial Flash Discoverable Parameters (SFDP) tables
  mtd: spi-nor: parse SFDP 4-byte Address Instruction Table

 drivers/mtd/devices/m25p80.c          |  130 ++-
 drivers/mtd/spi-nor/aspeed-smc.c      |   23 +-
 drivers/mtd/spi-nor/atmel-quadspi.c   |   80 +-
 drivers/mtd/spi-nor/cadence-quadspi.c |   18 +-
 drivers/mtd/spi-nor/fsl-quadspi.c     |    8 +-
 drivers/mtd/spi-nor/hisi-sfc.c        |   31 +-
 drivers/mtd/spi-nor/intel-spi.c       |    7 +-
 drivers/mtd/spi-nor/mtk-quadspi.c     |   16 +-
 drivers/mtd/spi-nor/nxp-spifi.c       |   22 +-
 drivers/mtd/spi-nor/spi-nor.c         | 1722 +++++++++++++++++++++++++++++----
 include/linux/mtd/spi-nor.h           |  228 ++++-
 11 files changed, 1983 insertions(+), 302 deletions(-)

-- 
2.9.3

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

* [PATCH v5 1/6] mtd: spi-nor: introduce more SPI protocols and the Dual Transfer Mode
  2017-03-22 23:33 [PATCH v5 0/6] mtd: spi-nor: parse SFDP tables to setup (Q)SPI memories Cyrille Pitchen
@ 2017-03-22 23:33 ` Cyrille Pitchen
  2017-03-23 15:13   ` Cédric Le Goater
                     ` (2 more replies)
  2017-03-22 23:33 ` [PATCH v5 2/6] mtd: m25p80: add support of SPI 1-2-2 and 1-4-4 protocols Cyrille Pitchen
                   ` (5 subsequent siblings)
  6 siblings, 3 replies; 27+ messages in thread
From: Cyrille Pitchen @ 2017-03-22 23:33 UTC (permalink / raw)
  To: marek.vasut, linux-mtd, jartur, kdasu.kdev, mar.krzeminski
  Cc: computersforpeace, dwmw2, boris.brezillon, richard, linux-kernel,
	nicolas.ferre, Cyrille Pitchen

This patch changes the prototype of spi_nor_scan(): its 3rd parameter
is replaced by a 'struct spi_nor_hwcaps' pointer, which tells the spi-nor
framework about the actual hardware capabilities supported by the SPI
controller and its driver.

Besides, this patch also introduces a new 'struct spi_nor_flash_parameter'
telling the spi-nor framework about the hardware capabilities supported by
the SPI flash memory and the associated settings required to use those
hardware caps.

Currently the 'struct spi_nor_flash_parameter' is filled with legacy
values but a later patch will allow to fill it dynamically by reading the
JESD216 Serial Flash Discoverable Parameter (SFDP) tables from the SPI
memory.

With both structures, the spi-nor framework can now compute the best
match between hardware caps supported by both the (Q)SPI memory and
controller hence selecting the relevant SPI protocols and op codes for
(Fast) Read, Page Program and Sector Erase operations.

The 'struct spi_nor_flash_parameter' also provides the spi-nor framework
with the number of dummy cycles to be used with each Fast Read commands
and the erase block size associated to the erase block op codes.

Finally the 'struct spi_nor_flash_parameter', through the optional
.enable_quad_io() hook, tells the spi-nor framework how to set the Quad
Enable (QE) bit of the QSPI memory to enable its Quad SPI features.

Signed-off-by: Cyrille Pitchen <cyrille.pitchen@atmel.com>
---
 drivers/mtd/devices/m25p80.c          |  16 +-
 drivers/mtd/spi-nor/aspeed-smc.c      |  23 +-
 drivers/mtd/spi-nor/atmel-quadspi.c   |  80 +++---
 drivers/mtd/spi-nor/cadence-quadspi.c |  18 +-
 drivers/mtd/spi-nor/fsl-quadspi.c     |   8 +-
 drivers/mtd/spi-nor/hisi-sfc.c        |  31 ++-
 drivers/mtd/spi-nor/intel-spi.c       |   7 +-
 drivers/mtd/spi-nor/mtk-quadspi.c     |  16 +-
 drivers/mtd/spi-nor/nxp-spifi.c       |  22 +-
 drivers/mtd/spi-nor/spi-nor.c         | 441 +++++++++++++++++++++++++++-------
 include/linux/mtd/spi-nor.h           | 158 +++++++++++-
 11 files changed, 643 insertions(+), 177 deletions(-)

diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c
index c4df3b1bded0..68986a26c8fe 100644
--- a/drivers/mtd/devices/m25p80.c
+++ b/drivers/mtd/devices/m25p80.c
@@ -111,10 +111,10 @@ static ssize_t m25p80_write(struct spi_nor *nor, loff_t to, size_t len,
 
 static inline unsigned int m25p80_rx_nbits(struct spi_nor *nor)
 {
-	switch (nor->flash_read) {
-	case SPI_NOR_DUAL:
+	switch (nor->read_proto) {
+	case SNOR_PROTO_1_1_2:
 		return 2;
-	case SPI_NOR_QUAD:
+	case SNOR_PROTO_1_1_4:
 		return 4;
 	default:
 		return 0;
@@ -196,7 +196,9 @@ static int m25p_probe(struct spi_device *spi)
 	struct flash_platform_data	*data;
 	struct m25p *flash;
 	struct spi_nor *nor;
-	enum read_mode mode = SPI_NOR_NORMAL;
+	struct spi_nor_hwcaps hwcaps = {
+		.mask = (SNOR_HWCAPS_READ | SNOR_HWCAPS_PP),
+	};
 	char *flash_name;
 	int ret;
 
@@ -222,9 +224,9 @@ static int m25p_probe(struct spi_device *spi)
 	flash->spi = spi;
 
 	if (spi->mode & SPI_RX_QUAD)
-		mode = SPI_NOR_QUAD;
+		hwcaps.mask |= SNOR_HWCAPS_READ_1_1_4;
 	else if (spi->mode & SPI_RX_DUAL)
-		mode = SPI_NOR_DUAL;
+		hwcaps.mask |= SNOR_HWCAPS_READ_1_1_2;
 
 	if (data && data->name)
 		nor->mtd.name = data->name;
@@ -241,7 +243,7 @@ static int m25p_probe(struct spi_device *spi)
 	else
 		flash_name = spi->modalias;
 
-	ret = spi_nor_scan(nor, flash_name, mode);
+	ret = spi_nor_scan(nor, flash_name, &hwcaps);
 	if (ret)
 		return ret;
 
diff --git a/drivers/mtd/spi-nor/aspeed-smc.c b/drivers/mtd/spi-nor/aspeed-smc.c
index 56051d30f000..723026d9cf0c 100644
--- a/drivers/mtd/spi-nor/aspeed-smc.c
+++ b/drivers/mtd/spi-nor/aspeed-smc.c
@@ -585,14 +585,12 @@ static int aspeed_smc_chip_setup_finish(struct aspeed_smc_chip *chip)
 	 * TODO: Adjust clocks if fast read is supported and interpret
 	 * SPI-NOR flags to adjust controller settings.
 	 */
-	switch (chip->nor.flash_read) {
-	case SPI_NOR_NORMAL:
-		cmd = CONTROL_COMMAND_MODE_NORMAL;
-		break;
-	case SPI_NOR_FAST:
-		cmd = CONTROL_COMMAND_MODE_FREAD;
-		break;
-	default:
+	if (chip->nor.read_proto == SNOR_PROTO_1_1_1) {
+		if (chip->nor.read_dummy == 0)
+			cmd = CONTROL_COMMAND_MODE_NORMAL;
+		else
+			cmd = CONTROL_COMMAND_MODE_FREAD;
+	} else {
 		dev_err(chip->nor.dev, "unsupported SPI read mode\n");
 		return -EINVAL;
 	}
@@ -608,6 +606,11 @@ static int aspeed_smc_chip_setup_finish(struct aspeed_smc_chip *chip)
 static int aspeed_smc_setup_flash(struct aspeed_smc_controller *controller,
 				  struct device_node *np, struct resource *r)
 {
+	struct spi_nor_hwcaps hwcaps = {
+		.mask = (SNOR_HWCAPS_READ |
+			 SNOR_HWCAPS_READ_FAST |
+			 SNOR_HWCAPS_PP),
+	};
 	const struct aspeed_smc_info *info = controller->info;
 	struct device *dev = controller->dev;
 	struct device_node *child;
@@ -671,11 +674,11 @@ static int aspeed_smc_setup_flash(struct aspeed_smc_controller *controller,
 			break;
 
 		/*
-		 * TODO: Add support for SPI_NOR_QUAD and SPI_NOR_DUAL
+		 * TODO: Add support for Dual and Quad SPI protocols
 		 * attach when board support is present as determined
 		 * by of property.
 		 */
-		ret = spi_nor_scan(nor, NULL, SPI_NOR_NORMAL);
+		ret = spi_nor_scan(nor, NULL, &hwcaps);
 		if (ret)
 			break;
 
diff --git a/drivers/mtd/spi-nor/atmel-quadspi.c b/drivers/mtd/spi-nor/atmel-quadspi.c
index 47937d9beec6..9f579f7c1733 100644
--- a/drivers/mtd/spi-nor/atmel-quadspi.c
+++ b/drivers/mtd/spi-nor/atmel-quadspi.c
@@ -275,14 +275,48 @@ static void atmel_qspi_debug_command(struct atmel_qspi *aq,
 
 static int atmel_qspi_run_command(struct atmel_qspi *aq,
 				  const struct atmel_qspi_command *cmd,
-				  u32 ifr_tfrtyp, u32 ifr_width)
+				  u32 ifr_tfrtyp, enum spi_nor_protocol proto)
 {
 	u32 iar, icr, ifr, sr;
 	int err = 0;
 
 	iar = 0;
 	icr = 0;
-	ifr = ifr_tfrtyp | ifr_width;
+	ifr = ifr_tfrtyp;
+
+	/* Set the SPI protocol */
+	switch (proto) {
+	case SNOR_PROTO_1_1_1:
+		ifr |= QSPI_IFR_WIDTH_SINGLE_BIT_SPI;
+		break;
+
+	case SNOR_PROTO_1_1_2:
+		ifr |= QSPI_IFR_WIDTH_DUAL_OUTPUT;
+		break;
+
+	case SNOR_PROTO_1_1_4:
+		ifr |= QSPI_IFR_WIDTH_QUAD_OUTPUT;
+		break;
+
+	case SNOR_PROTO_1_2_2:
+		ifr |= QSPI_IFR_WIDTH_DUAL_IO;
+		break;
+
+	case SNOR_PROTO_1_4_4:
+		ifr |= QSPI_IFR_WIDTH_QUAD_IO;
+		break;
+
+	case SNOR_PROTO_2_2_2:
+		ifr |= QSPI_IFR_WIDTH_DUAL_CMD;
+		break;
+
+	case SNOR_PROTO_4_4_4:
+		ifr |= QSPI_IFR_WIDTH_QUAD_CMD;
+		break;
+
+	default:
+		return -EINVAL;
+	}
 
 	/* Compute instruction parameters */
 	if (cmd->enable.bits.instruction) {
@@ -434,7 +468,7 @@ static int atmel_qspi_read_reg(struct spi_nor *nor, u8 opcode,
 	cmd.rx_buf = buf;
 	cmd.buf_len = len;
 	return atmel_qspi_run_command(aq, &cmd, QSPI_IFR_TFRTYP_TRSFR_READ,
-				      QSPI_IFR_WIDTH_SINGLE_BIT_SPI);
+				      nor->reg_proto);
 }
 
 static int atmel_qspi_write_reg(struct spi_nor *nor, u8 opcode,
@@ -450,7 +484,7 @@ static int atmel_qspi_write_reg(struct spi_nor *nor, u8 opcode,
 	cmd.tx_buf = buf;
 	cmd.buf_len = len;
 	return atmel_qspi_run_command(aq, &cmd, QSPI_IFR_TFRTYP_TRSFR_WRITE,
-				      QSPI_IFR_WIDTH_SINGLE_BIT_SPI);
+				      nor->reg_proto);
 }
 
 static ssize_t atmel_qspi_write(struct spi_nor *nor, loff_t to, size_t len,
@@ -469,7 +503,7 @@ static ssize_t atmel_qspi_write(struct spi_nor *nor, loff_t to, size_t len,
 	cmd.tx_buf = write_buf;
 	cmd.buf_len = len;
 	ret = atmel_qspi_run_command(aq, &cmd, QSPI_IFR_TFRTYP_TRSFR_WRITE_MEM,
-				     QSPI_IFR_WIDTH_SINGLE_BIT_SPI);
+				     nor->write_proto);
 	return (ret < 0) ? ret : len;
 }
 
@@ -484,7 +518,7 @@ static int atmel_qspi_erase(struct spi_nor *nor, loff_t offs)
 	cmd.instruction = nor->erase_opcode;
 	cmd.address = (u32)offs;
 	return atmel_qspi_run_command(aq, &cmd, QSPI_IFR_TFRTYP_TRSFR_WRITE,
-				      QSPI_IFR_WIDTH_SINGLE_BIT_SPI);
+				      nor->reg_proto);
 }
 
 static ssize_t atmel_qspi_read(struct spi_nor *nor, loff_t from, size_t len,
@@ -493,27 +527,8 @@ static ssize_t atmel_qspi_read(struct spi_nor *nor, loff_t from, size_t len,
 	struct atmel_qspi *aq = nor->priv;
 	struct atmel_qspi_command cmd;
 	u8 num_mode_cycles, num_dummy_cycles;
-	u32 ifr_width;
 	ssize_t ret;
 
-	switch (nor->flash_read) {
-	case SPI_NOR_NORMAL:
-	case SPI_NOR_FAST:
-		ifr_width = QSPI_IFR_WIDTH_SINGLE_BIT_SPI;
-		break;
-
-	case SPI_NOR_DUAL:
-		ifr_width = QSPI_IFR_WIDTH_DUAL_OUTPUT;
-		break;
-
-	case SPI_NOR_QUAD:
-		ifr_width = QSPI_IFR_WIDTH_QUAD_OUTPUT;
-		break;
-
-	default:
-		return -EINVAL;
-	}
-
 	if (nor->read_dummy >= 2) {
 		num_mode_cycles = 2;
 		num_dummy_cycles = nor->read_dummy - 2;
@@ -536,7 +551,7 @@ static ssize_t atmel_qspi_read(struct spi_nor *nor, loff_t from, size_t len,
 	cmd.rx_buf = read_buf;
 	cmd.buf_len = len;
 	ret = atmel_qspi_run_command(aq, &cmd, QSPI_IFR_TFRTYP_TRSFR_READ_MEM,
-				     ifr_width);
+				     nor->read_proto);
 	return (ret < 0) ? ret : len;
 }
 
@@ -590,6 +605,17 @@ static irqreturn_t atmel_qspi_interrupt(int irq, void *dev_id)
 
 static int atmel_qspi_probe(struct platform_device *pdev)
 {
+	struct spi_nor_hwcaps hwcaps = {
+		.mask = (SNOR_HWCAPS_READ |
+			 SNOR_HWCAPS_READ_FAST |
+			 SNOR_HWCAPS_READ_1_1_2 |
+			 SNOR_HWCAPS_READ_1_2_2 |
+			 SNOR_HWCAPS_READ_1_1_4 |
+			 SNOR_HWCAPS_READ_1_4_4 |
+			 SNOR_HWCAPS_PP |
+			 SNOR_HWCAPS_PP_1_1_4 |
+			 SNOR_HWCAPS_PP_1_4_4),
+	};
 	struct device_node *child, *np = pdev->dev.of_node;
 	struct atmel_qspi *aq;
 	struct resource *res;
@@ -679,7 +705,7 @@ static int atmel_qspi_probe(struct platform_device *pdev)
 	if (err)
 		goto disable_clk;
 
-	err = spi_nor_scan(nor, NULL, SPI_NOR_QUAD);
+	err = spi_nor_scan(nor, NULL, &hwcaps);
 	if (err)
 		goto disable_clk;
 
diff --git a/drivers/mtd/spi-nor/cadence-quadspi.c b/drivers/mtd/spi-nor/cadence-quadspi.c
index 9f8102de1b16..3f91a3e97892 100644
--- a/drivers/mtd/spi-nor/cadence-quadspi.c
+++ b/drivers/mtd/spi-nor/cadence-quadspi.c
@@ -855,15 +855,14 @@ static int cqspi_set_protocol(struct spi_nor *nor, const int read)
 	f_pdata->data_width = CQSPI_INST_TYPE_SINGLE;
 
 	if (read) {
-		switch (nor->flash_read) {
-		case SPI_NOR_NORMAL:
-		case SPI_NOR_FAST:
+		switch (nor->read_proto) {
+		case SNOR_PROTO_1_1_1:
 			f_pdata->data_width = CQSPI_INST_TYPE_SINGLE;
 			break;
-		case SPI_NOR_DUAL:
+		case SNOR_PROTO_1_1_2:
 			f_pdata->data_width = CQSPI_INST_TYPE_DUAL;
 			break;
-		case SPI_NOR_QUAD:
+		case SNOR_PROTO_1_1_4:
 			f_pdata->data_width = CQSPI_INST_TYPE_QUAD;
 			break;
 		default:
@@ -1069,6 +1068,13 @@ static void cqspi_controller_init(struct cqspi_st *cqspi)
 
 static int cqspi_setup_flash(struct cqspi_st *cqspi, struct device_node *np)
 {
+	struct spi_nor_hwcaps hwcaps = {
+		.mask = (SNOR_HWCAPS_READ |
+			 SNOR_HWCAPS_READ_FAST |
+			 SNOR_HWCAPS_READ_1_1_2 |
+			 SNOR_HWCAPS_READ_1_1_4 |
+			 SNOR_HWCAPS_PP),
+	};
 	struct platform_device *pdev = cqspi->pdev;
 	struct device *dev = &pdev->dev;
 	struct cqspi_flash_pdata *f_pdata;
@@ -1123,7 +1129,7 @@ static int cqspi_setup_flash(struct cqspi_st *cqspi, struct device_node *np)
 			goto err;
 		}
 
-		ret = spi_nor_scan(nor, NULL, SPI_NOR_QUAD);
+		ret = spi_nor_scan(nor, NULL, &hwcaps);
 		if (ret)
 			goto err;
 
diff --git a/drivers/mtd/spi-nor/fsl-quadspi.c b/drivers/mtd/spi-nor/fsl-quadspi.c
index 1476135e0d50..ec9c8e960fd2 100644
--- a/drivers/mtd/spi-nor/fsl-quadspi.c
+++ b/drivers/mtd/spi-nor/fsl-quadspi.c
@@ -957,6 +957,12 @@ static void fsl_qspi_unprep(struct spi_nor *nor, enum spi_nor_ops ops)
 
 static int fsl_qspi_probe(struct platform_device *pdev)
 {
+	struct spi_nor_hwcaps hwcaps = {
+		.mask = (SNOR_HWCAPS_READ |
+			 SNOR_HWCAPS_READ_FAST |
+			 SNOR_HWCAPS_READ_1_1_4 |
+			 SNOR_HWCAPS_PP),
+	};
 	struct device_node *np = pdev->dev.of_node;
 	struct device *dev = &pdev->dev;
 	struct fsl_qspi *q;
@@ -1065,7 +1071,7 @@ static int fsl_qspi_probe(struct platform_device *pdev)
 		/* set the chip address for READID */
 		fsl_qspi_set_base_addr(q, nor);
 
-		ret = spi_nor_scan(nor, NULL, SPI_NOR_QUAD);
+		ret = spi_nor_scan(nor, NULL, &hwcaps);
 		if (ret)
 			goto mutex_failed;
 
diff --git a/drivers/mtd/spi-nor/hisi-sfc.c b/drivers/mtd/spi-nor/hisi-sfc.c
index a286350627a6..80e2d173abdd 100644
--- a/drivers/mtd/spi-nor/hisi-sfc.c
+++ b/drivers/mtd/spi-nor/hisi-sfc.c
@@ -120,19 +120,24 @@ static inline int wait_op_finish(struct hifmc_host *host)
 		(reg & FMC_INT_OP_DONE), 0, FMC_WAIT_TIMEOUT);
 }
 
-static int get_if_type(enum read_mode flash_read)
+static int get_if_type(enum spi_nor_protocol proto)
 {
 	enum hifmc_iftype if_type;
 
-	switch (flash_read) {
-	case SPI_NOR_DUAL:
+	switch (proto) {
+	case SNOR_PROTO_1_1_2:
 		if_type = IF_TYPE_DUAL;
 		break;
-	case SPI_NOR_QUAD:
+	case SNOR_PROTO_1_2_2:
+		if_type = IF_TYPE_DIO;
+		break;
+	case SNOR_PROTO_1_1_4:
 		if_type = IF_TYPE_QUAD;
 		break;
-	case SPI_NOR_NORMAL:
-	case SPI_NOR_FAST:
+	case SNOR_PROTO_1_4_4:
+		if_type = IF_TYPE_QIO;
+		break;
+	case SNOR_PROTO_1_1_1:
 	default:
 		if_type = IF_TYPE_STD;
 		break;
@@ -253,7 +258,10 @@ static int hisi_spi_nor_dma_transfer(struct spi_nor *nor, loff_t start_off,
 	writel(FMC_DMA_LEN_SET(len), host->regbase + FMC_DMA_LEN);
 
 	reg = OP_CFG_FM_CS(priv->chipselect);
-	if_type = get_if_type(nor->flash_read);
+	if (op_type == FMC_OP_READ)
+		if_type = get_if_type(nor->read_proto);
+	else
+		if_type = get_if_type(nor->write_proto);
 	reg |= OP_CFG_MEM_IF_TYPE(if_type);
 	if (op_type == FMC_OP_READ)
 		reg |= OP_CFG_DUMMY_NUM(nor->read_dummy >> 3);
@@ -321,6 +329,13 @@ static ssize_t hisi_spi_nor_write(struct spi_nor *nor, loff_t to,
 static int hisi_spi_nor_register(struct device_node *np,
 				struct hifmc_host *host)
 {
+	struct spi_nor_hwcaps hwcaps = {
+		.mask = (SNOR_HWCAPS_READ |
+			 SNOR_HWCAPS_READ_FAST |
+			 SNOR_HWCAPS_READ_1_1_2 |
+			 SNOR_HWCAPS_READ_1_1_4 |
+			 SNOR_HWCAPS_PP),
+	};
 	struct device *dev = host->dev;
 	struct spi_nor *nor;
 	struct hifmc_priv *priv;
@@ -362,7 +377,7 @@ static int hisi_spi_nor_register(struct device_node *np,
 	nor->read = hisi_spi_nor_read;
 	nor->write = hisi_spi_nor_write;
 	nor->erase = NULL;
-	ret = spi_nor_scan(nor, NULL, SPI_NOR_QUAD);
+	ret = spi_nor_scan(nor, NULL, &hwcaps);
 	if (ret)
 		return ret;
 
diff --git a/drivers/mtd/spi-nor/intel-spi.c b/drivers/mtd/spi-nor/intel-spi.c
index 986a3d020a3a..515aa1f7f4f1 100644
--- a/drivers/mtd/spi-nor/intel-spi.c
+++ b/drivers/mtd/spi-nor/intel-spi.c
@@ -715,6 +715,11 @@ static void intel_spi_fill_partition(struct intel_spi *ispi,
 struct intel_spi *intel_spi_probe(struct device *dev,
 	struct resource *mem, const struct intel_spi_boardinfo *info)
 {
+	struct spi_nor_hwcaps hwcaps = {
+		.mask = (SNOR_HWCAPS_READ |
+			 SNOR_HWCAPS_READ_FAST |
+			 SNOR_HWCAPS_PP),
+	};
 	struct mtd_partition part;
 	struct intel_spi *ispi;
 	int ret;
@@ -746,7 +751,7 @@ struct intel_spi *intel_spi_probe(struct device *dev,
 	ispi->nor.write = intel_spi_write;
 	ispi->nor.erase = intel_spi_erase;
 
-	ret = spi_nor_scan(&ispi->nor, NULL, SPI_NOR_NORMAL);
+	ret = spi_nor_scan(&ispi->nor, NULL, &hwcaps);
 	if (ret) {
 		dev_info(dev, "failed to locate the chip\n");
 		return ERR_PTR(ret);
diff --git a/drivers/mtd/spi-nor/mtk-quadspi.c b/drivers/mtd/spi-nor/mtk-quadspi.c
index e661877c23de..615e258866f7 100644
--- a/drivers/mtd/spi-nor/mtk-quadspi.c
+++ b/drivers/mtd/spi-nor/mtk-quadspi.c
@@ -121,20 +121,20 @@ static void mt8173_nor_set_read_mode(struct mt8173_nor *mt8173_nor)
 {
 	struct spi_nor *nor = &mt8173_nor->nor;
 
-	switch (nor->flash_read) {
-	case SPI_NOR_FAST:
+	switch (nor->read_proto) {
+	case SNOR_PROTO_1_1_1:
 		writeb(nor->read_opcode, mt8173_nor->base +
 		       MTK_NOR_PRGDATA3_REG);
 		writeb(MTK_NOR_FAST_READ, mt8173_nor->base +
 		       MTK_NOR_CFG1_REG);
 		break;
-	case SPI_NOR_DUAL:
+	case SNOR_PROTO_1_1_2:
 		writeb(nor->read_opcode, mt8173_nor->base +
 		       MTK_NOR_PRGDATA3_REG);
 		writeb(MTK_NOR_DUAL_READ_EN, mt8173_nor->base +
 		       MTK_NOR_DUAL_REG);
 		break;
-	case SPI_NOR_QUAD:
+	case SNOR_PROTO_1_1_4:
 		writeb(nor->read_opcode, mt8173_nor->base +
 		       MTK_NOR_PRGDATA4_REG);
 		writeb(MTK_NOR_QUAD_READ_EN, mt8173_nor->base +
@@ -381,6 +381,12 @@ static int mt8173_nor_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf,
 static int mtk_nor_init(struct mt8173_nor *mt8173_nor,
 			struct device_node *flash_node)
 {
+	struct spi_nor_hwcaps hwcaps = {
+		.mask = (SNOR_HWCAPS_READ |
+			 SNOR_HWCAPS_READ_FAST |
+			 SNOR_HWCAPS_READ_1_1_2 |
+			 SNOR_HWCAPS_PP),
+	};
 	int ret;
 	struct spi_nor *nor;
 
@@ -399,7 +405,7 @@ static int mtk_nor_init(struct mt8173_nor *mt8173_nor,
 	nor->write_reg = mt8173_nor_write_reg;
 	nor->mtd.name = "mtk_nor";
 	/* initialized with NULL */
-	ret = spi_nor_scan(nor, NULL, SPI_NOR_DUAL);
+	ret = spi_nor_scan(nor, NULL, &hwcaps);
 	if (ret)
 		return ret;
 
diff --git a/drivers/mtd/spi-nor/nxp-spifi.c b/drivers/mtd/spi-nor/nxp-spifi.c
index 73a14f40928b..c5992e099542 100644
--- a/drivers/mtd/spi-nor/nxp-spifi.c
+++ b/drivers/mtd/spi-nor/nxp-spifi.c
@@ -240,13 +240,12 @@ static int nxp_spifi_erase(struct spi_nor *nor, loff_t offs)
 
 static int nxp_spifi_setup_memory_cmd(struct nxp_spifi *spifi)
 {
-	switch (spifi->nor.flash_read) {
-	case SPI_NOR_NORMAL:
-	case SPI_NOR_FAST:
+	switch (spifi->nor.read_proto) {
+	case SNOR_PROTO_1_1_1:
 		spifi->mcmd = SPIFI_CMD_FIELDFORM_ALL_SERIAL;
 		break;
-	case SPI_NOR_DUAL:
-	case SPI_NOR_QUAD:
+	case SNOR_PROTO_1_1_2:
+	case SNOR_PROTO_1_1_4:
 		spifi->mcmd = SPIFI_CMD_FIELDFORM_QUAD_DUAL_DATA;
 		break;
 	default:
@@ -274,7 +273,11 @@ static void nxp_spifi_dummy_id_read(struct spi_nor *nor)
 static int nxp_spifi_setup_flash(struct nxp_spifi *spifi,
 				 struct device_node *np)
 {
-	enum read_mode flash_read;
+	struct spi_nor_hwcaps hwcaps = {
+		.mask = (SNOR_HWCAPS_READ |
+			 SNOR_HWCAPS_READ_FAST |
+			 SNOR_HWCAPS_PP),
+	};
 	u32 ctrl, property;
 	u16 mode = 0;
 	int ret;
@@ -308,13 +311,12 @@ static int nxp_spifi_setup_flash(struct nxp_spifi *spifi,
 
 	if (mode & SPI_RX_DUAL) {
 		ctrl |= SPIFI_CTRL_DUAL;
-		flash_read = SPI_NOR_DUAL;
+		hwcaps.mask |= SNOR_HWCAPS_READ_1_1_2;
 	} else if (mode & SPI_RX_QUAD) {
 		ctrl &= ~SPIFI_CTRL_DUAL;
-		flash_read = SPI_NOR_QUAD;
+		hwcaps.mask |= SNOR_HWCAPS_READ_1_1_4;
 	} else {
 		ctrl |= SPIFI_CTRL_DUAL;
-		flash_read = SPI_NOR_NORMAL;
 	}
 
 	switch (mode & (SPI_CPHA | SPI_CPOL)) {
@@ -351,7 +353,7 @@ static int nxp_spifi_setup_flash(struct nxp_spifi *spifi,
 	 */
 	nxp_spifi_dummy_id_read(&spifi->nor);
 
-	ret = spi_nor_scan(&spifi->nor, NULL, flash_read);
+	ret = spi_nor_scan(&spifi->nor, NULL, &hwcaps);
 	if (ret) {
 		dev_err(spifi->dev, "device scan failed\n");
 		return ret;
diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c
index d3cb44b28490..cc443c6cbae8 100644
--- a/drivers/mtd/spi-nor/spi-nor.c
+++ b/drivers/mtd/spi-nor/spi-nor.c
@@ -150,24 +150,6 @@ static int read_cr(struct spi_nor *nor)
 }
 
 /*
- * Dummy Cycle calculation for different type of read.
- * It can be used to support more commands with
- * different dummy cycle requirements.
- */
-static inline int spi_nor_read_dummy_cycles(struct spi_nor *nor)
-{
-	switch (nor->flash_read) {
-	case SPI_NOR_FAST:
-	case SPI_NOR_DUAL:
-	case SPI_NOR_QUAD:
-		return 8;
-	case SPI_NOR_NORMAL:
-		return 0;
-	}
-	return 0;
-}
-
-/*
  * Write status register 1 byte
  * Returns negative if error occurred.
  */
@@ -221,6 +203,10 @@ static inline u8 spi_nor_convert_3to4_read(u8 opcode)
 		{ SPINOR_OP_READ_1_2_2,	SPINOR_OP_READ_1_2_2_4B },
 		{ SPINOR_OP_READ_1_1_4,	SPINOR_OP_READ_1_1_4_4B },
 		{ SPINOR_OP_READ_1_4_4,	SPINOR_OP_READ_1_4_4_4B },
+
+		{ SPINOR_OP_READ_1_1_1_DTR,	SPINOR_OP_READ_1_1_1_DTR_4B },
+		{ SPINOR_OP_READ_1_2_2_DTR,	SPINOR_OP_READ_1_2_2_DTR_4B },
+		{ SPINOR_OP_READ_1_4_4_DTR,	SPINOR_OP_READ_1_4_4_DTR_4B },
 	};
 
 	return spi_nor_convert_opcode(opcode, spi_nor_3to4_read,
@@ -1459,30 +1445,6 @@ static int spansion_quad_enable(struct spi_nor *nor)
 	return 0;
 }
 
-static int set_quad_mode(struct spi_nor *nor, const struct flash_info *info)
-{
-	int status;
-
-	switch (JEDEC_MFR(info)) {
-	case SNOR_MFR_MACRONIX:
-		status = macronix_quad_enable(nor);
-		if (status) {
-			dev_err(nor->dev, "Macronix quad-read not enabled\n");
-			return -EINVAL;
-		}
-		return status;
-	case SNOR_MFR_MICRON:
-		return 0;
-	default:
-		status = spansion_quad_enable(nor);
-		if (status) {
-			dev_err(nor->dev, "Spansion quad-read not enabled\n");
-			return -EINVAL;
-		}
-		return status;
-	}
-}
-
 static int spi_nor_check(struct spi_nor *nor)
 {
 	if (!nor->dev || !nor->read || !nor->write ||
@@ -1535,8 +1497,322 @@ static int s3an_nor_scan(const struct flash_info *info, struct spi_nor *nor)
 	return 0;
 }
 
-int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
+
+struct spi_nor_read_command {
+	u8			num_mode_clocks;
+	u8			num_wait_states;
+	u8			opcode;
+	enum spi_nor_protocol	proto;
+};
+
+struct spi_nor_pp_command {
+	u8			opcode;
+	enum spi_nor_protocol	proto;
+};
+
+enum spi_nor_read_command_index {
+	SNOR_CMD_READ,
+	SNOR_CMD_READ_FAST,
+	SNOR_CMD_READ_1_1_1_DTR,
+
+	/* Dual SPI */
+	SNOR_CMD_READ_1_1_2,
+	SNOR_CMD_READ_1_2_2,
+	SNOR_CMD_READ_2_2_2,
+	SNOR_CMD_READ_1_2_2_DTR,
+
+	/* Quad SPI */
+	SNOR_CMD_READ_1_1_4,
+	SNOR_CMD_READ_1_4_4,
+	SNOR_CMD_READ_4_4_4,
+	SNOR_CMD_READ_1_4_4_DTR,
+
+	/* Octo SPI */
+	SNOR_CMD_READ_1_1_8,
+	SNOR_CMD_READ_1_8_8,
+	SNOR_CMD_READ_8_8_8,
+	SNOR_CMD_READ_1_8_8_DTR,
+
+	SNOR_CMD_READ_MAX
+};
+
+enum spi_nor_pp_command_index {
+	SNOR_CMD_PP,
+
+	/* Quad SPI */
+	SNOR_CMD_PP_1_1_4,
+	SNOR_CMD_PP_1_4_4,
+	SNOR_CMD_PP_4_4_4,
+
+	/* Octo SPI */
+	SNOR_CMD_PP_1_1_8,
+	SNOR_CMD_PP_1_8_8,
+	SNOR_CMD_PP_8_8_8,
+
+	SNOR_CMD_PP_MAX
+};
+
+struct spi_nor_flash_parameter {
+	u64				size;
+	u32				page_size;
+
+	struct spi_nor_hwcaps		hwcaps;
+	struct spi_nor_read_command	reads[SNOR_CMD_READ_MAX];
+	struct spi_nor_pp_command	page_programs[SNOR_CMD_PP_MAX];
+
+	int (*quad_enable)(struct spi_nor *nor);
+};
+
+
+static inline 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)
+{
+	read->num_mode_clocks = num_mode_clocks;
+	read->num_wait_states = num_wait_states;
+	read->opcode = opcode;
+	read->proto = proto;
+}
+
+static inline void
+spi_nor_set_pp_settings(struct spi_nor_pp_command *pp,
+			u8 opcode,
+			enum spi_nor_protocol proto)
+{
+	pp->opcode = opcode;
+	pp->proto = proto;
+}
+
+static int spi_nor_init_params(struct spi_nor *nor,
+			       const struct flash_info *info,
+			       struct spi_nor_flash_parameter *params)
+{
+	/* Set legacy flash parameters as default. */
+	memset(params, 0, sizeof(*params));
+
+	/* Set SPI NOR sizes. */
+	params->size = info->sector_size * info->n_sectors;
+	params->page_size = info->page_size;
+
+	/* (Fast) Read settings. */
+	params->hwcaps.mask |= SNOR_HWCAPS_READ;
+	spi_nor_set_read_settings(&params->reads[SNOR_CMD_READ],
+				  0, 0, SPINOR_OP_READ,
+				  SNOR_PROTO_1_1_1);
+	if (!(info->flags & SPI_NOR_NO_FR)) {
+		params->hwcaps.mask |= SNOR_HWCAPS_READ_FAST;
+		spi_nor_set_read_settings(&params->reads[SNOR_CMD_READ_FAST],
+					  0, 8, SPINOR_OP_READ_FAST,
+					  SNOR_PROTO_1_1_1);
+	}
+	if (info->flags & SPI_NOR_DUAL_READ) {
+		params->hwcaps.mask |= SNOR_HWCAPS_READ_1_1_2;
+		spi_nor_set_read_settings(&params->reads[SNOR_CMD_READ_1_1_2],
+					  0, 8, SPINOR_OP_READ_1_1_2,
+					  SNOR_PROTO_1_1_2);
+	}
+	if (info->flags & SPI_NOR_QUAD_READ) {
+		params->hwcaps.mask |= SNOR_HWCAPS_READ_1_1_4;
+		spi_nor_set_read_settings(&params->reads[SNOR_CMD_READ_1_1_4],
+					  0, 8, SPINOR_OP_READ_1_1_4,
+					  SNOR_PROTO_1_1_4);
+	}
+
+	/* 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);
+
+	/* Select the procedure to set the Quad Enable bit. */
+	if (params->hwcaps.mask & (SNOR_HWCAPS_READ_QUAD |
+				   SNOR_HWCAPS_PP_QUAD)) {
+		switch (JEDEC_MFR(info)) {
+		case SNOR_MFR_MACRONIX:
+			params->quad_enable = macronix_quad_enable;
+			break;
+
+		case SNOR_MFR_MICRON:
+			break;
+
+		default:
+			params->quad_enable = spansion_quad_enable;
+			break;
+		}
+	}
+
+	return 0;
+}
+
+static int spi_nor_hwcaps2cmd(u32 hwcaps)
 {
+	switch (hwcaps) {
+	case SNOR_HWCAPS_READ:			return SNOR_CMD_READ;
+	case SNOR_HWCAPS_READ_FAST:		return SNOR_CMD_READ_FAST;
+	case SNOR_HWCAPS_READ_1_1_1_DTR:	return SNOR_CMD_READ_1_1_1_DTR;
+	case SNOR_HWCAPS_READ_1_1_2:		return SNOR_CMD_READ_1_1_2;
+	case SNOR_HWCAPS_READ_1_2_2:		return SNOR_CMD_READ_1_2_2;
+	case SNOR_HWCAPS_READ_2_2_2:		return SNOR_CMD_READ_2_2_2;
+	case SNOR_HWCAPS_READ_1_2_2_DTR:	return SNOR_CMD_READ_1_2_2_DTR;
+	case SNOR_HWCAPS_READ_1_1_4:		return SNOR_CMD_READ_1_1_4;
+	case SNOR_HWCAPS_READ_1_4_4:		return SNOR_CMD_READ_1_4_4;
+	case SNOR_HWCAPS_READ_4_4_4:		return SNOR_CMD_READ_4_4_4;
+	case SNOR_HWCAPS_READ_1_4_4_DTR:	return SNOR_CMD_READ_1_4_4_DTR;
+	case SNOR_HWCAPS_READ_1_1_8:		return SNOR_CMD_READ_1_1_8;
+	case SNOR_HWCAPS_READ_1_8_8:		return SNOR_CMD_READ_1_8_8;
+	case SNOR_HWCAPS_READ_8_8_8:		return SNOR_CMD_READ_8_8_8;
+	case SNOR_HWCAPS_READ_1_8_8_DTR:	return SNOR_CMD_READ_1_8_8_DTR;
+
+	case SNOR_HWCAPS_PP:			return SNOR_CMD_PP;
+	case SNOR_HWCAPS_PP_1_1_4:		return SNOR_CMD_PP_1_1_4;
+	case SNOR_HWCAPS_PP_1_4_4:		return SNOR_CMD_PP_1_4_4;
+	case SNOR_HWCAPS_PP_4_4_4:		return SNOR_CMD_PP_4_4_4;
+	case SNOR_HWCAPS_PP_1_1_8:		return SNOR_CMD_PP_1_1_8;
+	case SNOR_HWCAPS_PP_1_8_8:		return SNOR_CMD_PP_1_8_8;
+	case SNOR_HWCAPS_PP_8_8_8:		return SNOR_CMD_PP_8_8_8;
+	}
+
+	return -EINVAL;
+}
+
+static int spi_nor_select_read(struct spi_nor *nor,
+			       const struct spi_nor_flash_parameter *params,
+			       u32 shared_hwcaps)
+{
+	int cmd, best_match = fls(shared_hwcaps & SNOR_HWCAPS_READ_MASK) - 1;
+	const struct spi_nor_read_command *read;
+
+	if (best_match < 0)
+		return -EINVAL;
+
+	cmd = spi_nor_hwcaps2cmd(BIT(best_match));
+	if (cmd < 0)
+		return -EINVAL;
+
+	read = &params->reads[cmd];
+	nor->read_opcode = read->opcode;
+	nor->read_proto = read->proto;
+
+	/*
+	 * In the spi-nor framework, we don't need to make the difference
+	 * between mode clock cycles and wait state clock cycles.
+	 * Indeed, the value of the mode clock cycles is used by a QSPI
+	 * flash memory to know whether it should enter or leave its 0-4-4
+	 * (Continuous Read / XIP) mode.
+	 * eXecution In Place is out of the scope of the mtd sub-system.
+	 * Hence we choose to merge both mode and wait state clock cycles
+	 * into the so called dummy clock cycles.
+	 */
+	nor->read_dummy = read->num_mode_clocks + read->num_wait_states;
+	return 0;
+}
+
+static int spi_nor_select_pp(struct spi_nor *nor,
+			     const struct spi_nor_flash_parameter *params,
+			     u32 shared_hwcaps)
+{
+	int cmd, best_match = fls(shared_hwcaps & SNOR_HWCAPS_PP_MASK) - 1;
+	const struct spi_nor_pp_command *pp;
+
+	if (best_match < 0)
+		return -EINVAL;
+
+	cmd = spi_nor_hwcaps2cmd(BIT(best_match));
+	if (cmd < 0)
+		return -EINVAL;
+
+	pp = &params->page_programs[cmd];
+	nor->program_opcode = pp->opcode;
+	nor->write_proto = pp->proto;
+	return 0;
+}
+
+static int spi_nor_select_erase(struct spi_nor *nor,
+				const struct flash_info *info)
+{
+	struct mtd_info *mtd = &nor->mtd;
+
+#ifdef CONFIG_MTD_SPI_NOR_USE_4K_SECTORS
+	/* prefer "small sector" erase if possible */
+	if (info->flags & SECT_4K) {
+		nor->erase_opcode = SPINOR_OP_BE_4K;
+		mtd->erasesize = 4096;
+	} else if (info->flags & SECT_4K_PMC) {
+		nor->erase_opcode = SPINOR_OP_BE_4K_PMC;
+		mtd->erasesize = 4096;
+	} else
+#endif
+	{
+		nor->erase_opcode = SPINOR_OP_SE;
+		mtd->erasesize = info->sector_size;
+	}
+	return 0;
+}
+
+static int spi_nor_setup(struct spi_nor *nor, const struct flash_info *info,
+			 const struct spi_nor_flash_parameter *params,
+			 const struct spi_nor_hwcaps *hwcaps)
+{
+	u32 ignored_mask, shared_mask;
+	bool enable_quad_io;
+	int err;
+
+	/*
+	 * Keep only the hardware capabilities supported by both the SPI
+	 * controller and the SPI flash memory.
+	 */
+	shared_mask = hwcaps->mask & params->hwcaps.mask;
+
+	/* SPI protocol classes N-N-N are not supported yet. */
+	ignored_mask = (SNOR_HWCAPS_READ_2_2_2 |
+			SNOR_HWCAPS_READ_4_4_4 |
+			SNOR_HWCAPS_READ_8_8_8 |
+			SNOR_HWCAPS_PP_4_4_4 |
+			SNOR_HWCAPS_PP_8_8_8);
+	if (shared_mask & ignored_mask) {
+		dev_dbg(nor->dev,
+			"SPI protocol classes N-N-N are not supported yet.\n");
+		shared_mask &= ~ignored_mask;
+	}
+
+	/* Select the (Fast) Read command. */
+	err = spi_nor_select_read(nor, params, shared_mask);
+	if (err) {
+		dev_err(nor->dev, "invalid (fast) read\n");
+		return err;
+	}
+
+	/* Select the Page Program command. */
+	err = spi_nor_select_pp(nor, params, shared_mask);
+	if (err) {
+		dev_err(nor->dev, "invalid page program\n");
+		return err;
+	}
+
+	/* Select the Sector Erase command. */
+	err = spi_nor_select_erase(nor, info);
+	if (err) {
+		dev_err(nor->dev, "invalid sector/block erase\n");
+		return err;
+	}
+
+	/* Enable Quad I/O if needed. */
+	enable_quad_io = (spi_nor_get_protocol_width(nor->read_proto) == 4 ||
+			  spi_nor_get_protocol_width(nor->write_proto) == 4);
+	if (enable_quad_io && params->quad_enable)
+		nor->flash_quad_enable = params->quad_enable;
+	else
+		nor->flash_quad_enable = NULL;
+
+	return 0;
+}
+
+int spi_nor_scan(struct spi_nor *nor, const char *name,
+		 const struct spi_nor_hwcaps *hwcaps)
+{
+	struct spi_nor_flash_parameter params;
 	const struct flash_info *info = NULL;
 	struct device *dev = nor->dev;
 	struct mtd_info *mtd = &nor->mtd;
@@ -1548,6 +1824,11 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
 	if (ret)
 		return ret;
 
+	/* Reset SPI protocol for all commands */
+	nor->reg_proto = SNOR_PROTO_1_1_1;
+	nor->read_proto = SNOR_PROTO_1_1_1;
+	nor->write_proto = SNOR_PROTO_1_1_1;
+
 	if (name)
 		info = spi_nor_match_id(name);
 	/* Try to auto-detect if chip name wasn't specified or not found */
@@ -1580,6 +1861,11 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
 		}
 	}
 
+	/* Parse the Serial Flash Discoverable Parameters table */
+	ret = spi_nor_init_params(nor, info, &params);
+	if (ret)
+		return ret;
+
 	mutex_init(&nor->lock);
 
 	/*
@@ -1610,7 +1896,7 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
 	mtd->type = MTD_NORFLASH;
 	mtd->writesize = 1;
 	mtd->flags = MTD_CAP_NORFLASH;
-	mtd->size = info->sector_size * info->n_sectors;
+	mtd->size = params.size;
 	mtd->_erase = spi_nor_erase;
 	mtd->_read = spi_nor_read;
 
@@ -1641,76 +1927,47 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
 	if (info->flags & NO_CHIP_ERASE)
 		nor->flags |= SNOR_F_NO_OP_CHIP_ERASE;
 
-#ifdef CONFIG_MTD_SPI_NOR_USE_4K_SECTORS
-	/* prefer "small sector" erase if possible */
-	if (info->flags & SECT_4K) {
-		nor->erase_opcode = SPINOR_OP_BE_4K;
-		mtd->erasesize = 4096;
-	} else if (info->flags & SECT_4K_PMC) {
-		nor->erase_opcode = SPINOR_OP_BE_4K_PMC;
-		mtd->erasesize = 4096;
-	} else
-#endif
-	{
-		nor->erase_opcode = SPINOR_OP_SE;
-		mtd->erasesize = info->sector_size;
-	}
-
 	if (info->flags & SPI_NOR_NO_ERASE)
 		mtd->flags |= MTD_NO_ERASE;
 
 	mtd->dev.parent = dev;
-	nor->page_size = info->page_size;
+	nor->page_size = params.page_size;
 	mtd->writebufsize = nor->page_size;
 
 	if (np) {
 		/* If we were instantiated by DT, use it */
 		if (of_property_read_bool(np, "m25p,fast-read"))
-			nor->flash_read = SPI_NOR_FAST;
+			params.hwcaps.mask |= SNOR_HWCAPS_READ_FAST;
 		else
-			nor->flash_read = SPI_NOR_NORMAL;
+			params.hwcaps.mask &= ~SNOR_HWCAPS_READ_FAST;
 	} else {
 		/* If we weren't instantiated by DT, default to fast-read */
-		nor->flash_read = SPI_NOR_FAST;
+		params.hwcaps.mask |= SNOR_HWCAPS_READ_FAST;
 	}
 
 	/* Some devices cannot do fast-read, no matter what DT tells us */
 	if (info->flags & SPI_NOR_NO_FR)
-		nor->flash_read = SPI_NOR_NORMAL;
+		params.hwcaps.mask &= ~SNOR_HWCAPS_READ_FAST;
+
+	/*
+	 * Configure the SPI memory:
+	 * - select op codes for (Fast) Read, Page Program and Sector Erase.
+	 * - set the number of dummy cycles (mode cycles + wait states).
+	 * - set the SPI protocols for register and memory accesses.
+	 * - set the Quad Enable bit if needed (required by SPI x-y-4 protos).
+	 */
+	ret = spi_nor_setup(nor, info, &params, hwcaps);
+	if (ret)
+		return ret;
 
-	/* Quad/Dual-read mode takes precedence over fast/normal */
-	if (mode == SPI_NOR_QUAD && info->flags & SPI_NOR_QUAD_READ) {
-		ret = set_quad_mode(nor, info);
+	if (nor->flash_quad_enable) {
+		ret = nor->flash_quad_enable(nor);
 		if (ret) {
 			dev_err(dev, "quad mode not supported\n");
 			return ret;
 		}
-		nor->flash_read = SPI_NOR_QUAD;
-	} else if (mode == SPI_NOR_DUAL && info->flags & SPI_NOR_DUAL_READ) {
-		nor->flash_read = SPI_NOR_DUAL;
 	}
 
-	/* Default commands */
-	switch (nor->flash_read) {
-	case SPI_NOR_QUAD:
-		nor->read_opcode = SPINOR_OP_READ_1_1_4;
-		break;
-	case SPI_NOR_DUAL:
-		nor->read_opcode = SPINOR_OP_READ_1_1_2;
-		break;
-	case SPI_NOR_FAST:
-		nor->read_opcode = SPINOR_OP_READ_FAST;
-		break;
-	case SPI_NOR_NORMAL:
-		nor->read_opcode = SPINOR_OP_READ;
-		break;
-	default:
-		dev_err(dev, "No Read opcode defined\n");
-		return -EINVAL;
-	}
-
-	nor->program_opcode = SPINOR_OP_PP;
-
 	if (info->addr_width)
 		nor->addr_width = info->addr_width;
 	else if (mtd->size > 0x1000000) {
@@ -1731,8 +1988,6 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
 		return -EINVAL;
 	}
 
-	nor->read_dummy = spi_nor_read_dummy_cycles(nor);
-
 	if (info->flags & SPI_S3AN) {
 		ret = s3an_nor_scan(info, nor);
 		if (ret)
diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h
index f2a718030476..732ee6cd5330 100644
--- a/include/linux/mtd/spi-nor.h
+++ b/include/linux/mtd/spi-nor.h
@@ -73,6 +73,15 @@
 #define SPINOR_OP_BE_32K_4B	0x5c	/* Erase 32KiB block */
 #define SPINOR_OP_SE_4B		0xdc	/* Sector erase (usually 64KiB) */
 
+/* Double Transfer Rate opcodes - defined in JEDEC JESD216B. */
+#define SPINOR_OP_READ_1_1_1_DTR	0x0d
+#define SPINOR_OP_READ_1_2_2_DTR	0xbd
+#define SPINOR_OP_READ_1_4_4_DTR	0xed
+
+#define SPINOR_OP_READ_1_1_1_DTR_4B	0x0e
+#define SPINOR_OP_READ_1_2_2_DTR_4B	0xbe
+#define SPINOR_OP_READ_1_4_4_DTR_4B	0xee
+
 /* Used for SST flashes only. */
 #define SPINOR_OP_BP		0x02	/* Byte program */
 #define SPINOR_OP_WRDI		0x04	/* Write disable */
@@ -119,13 +128,75 @@
 /* Configuration Register bits. */
 #define CR_QUAD_EN_SPAN		BIT(1)	/* Spansion Quad I/O */
 
-enum read_mode {
-	SPI_NOR_NORMAL = 0,
-	SPI_NOR_FAST,
-	SPI_NOR_DUAL,
-	SPI_NOR_QUAD,
+
+/* Supported SPI protocols */
+#define SNOR_PROTO_WIDTH_MASK	GENMASK(7, 0)
+
+#define SNOR_PROTO_CLASS_MASK	GENMASK(9, 8)
+#define SNOR_PROTO_CLASS_1_1_N	(0x0u << 8)
+#define SNOR_PROTO_CLASS_1_N_N	(0x1u << 8)
+#define SNOR_PROTO_CLASS_N_N_N	(0x2u << 8)
+
+#define SNOR_PROTO_IS_DTR	BIT(10)	/* Double Transfer Rate */
+
+#define SNOR_PROTO_STR(_pclass, _pwidth) \
+	((_pclass) | (_pwidth))
+#define SNOR_PROTO_DTR(_pclass, _pwidth) \
+	(SNOR_PROTO_IS_DTR | (_pclass) | (_pwidth))
+
+enum spi_nor_protocol {
+	SNOR_PROTO_1_1_1 = SNOR_PROTO_STR(SNOR_PROTO_CLASS_1_1_N, 1),
+	SNOR_PROTO_1_1_2 = SNOR_PROTO_STR(SNOR_PROTO_CLASS_1_1_N, 2),
+	SNOR_PROTO_1_1_4 = SNOR_PROTO_STR(SNOR_PROTO_CLASS_1_1_N, 4),
+	SNOR_PROTO_1_1_8 = SNOR_PROTO_STR(SNOR_PROTO_CLASS_1_1_N, 8),
+	SNOR_PROTO_1_2_2 = SNOR_PROTO_STR(SNOR_PROTO_CLASS_1_N_N, 2),
+	SNOR_PROTO_1_4_4 = SNOR_PROTO_STR(SNOR_PROTO_CLASS_1_N_N, 4),
+	SNOR_PROTO_1_8_8 = SNOR_PROTO_STR(SNOR_PROTO_CLASS_1_N_N, 8),
+	SNOR_PROTO_2_2_2 = SNOR_PROTO_STR(SNOR_PROTO_CLASS_N_N_N, 2),
+	SNOR_PROTO_4_4_4 = SNOR_PROTO_STR(SNOR_PROTO_CLASS_N_N_N, 4),
+	SNOR_PROTO_8_8_8 = SNOR_PROTO_STR(SNOR_PROTO_CLASS_N_N_N, 8),
+
+	SNOR_PROTO_1_1_1_DTR = SNOR_PROTO_DTR(SNOR_PROTO_CLASS_1_1_N, 1),
+	SNOR_PROTO_1_2_2_DTR = SNOR_PROTO_DTR(SNOR_PROTO_CLASS_1_N_N, 2),
+	SNOR_PROTO_1_4_4_DTR = SNOR_PROTO_DTR(SNOR_PROTO_CLASS_1_N_N, 4),
+	SNOR_PROTO_1_8_8_DTR = SNOR_PROTO_DTR(SNOR_PROTO_CLASS_1_N_N, 8),
 };
 
+static inline bool spi_nor_protocol_is_dtr(enum spi_nor_protocol proto)
+{
+	return (proto & SNOR_PROTO_IS_DTR) == SNOR_PROTO_IS_DTR;
+}
+
+static inline u32 spi_nor_get_protocol_class(enum spi_nor_protocol proto)
+{
+	return proto & SNOR_PROTO_CLASS_MASK;
+}
+
+static inline u8 spi_nor_get_protocol_width(enum spi_nor_protocol proto)
+{
+	return proto & SNOR_PROTO_WIDTH_MASK;
+}
+
+static inline u8 spi_nor_get_protocol_inst_width(enum spi_nor_protocol proto)
+{
+	return (spi_nor_get_protocol_class(proto) == SNOR_PROTO_CLASS_N_N_N) ?
+		spi_nor_get_protocol_width(proto) :
+		1u;
+}
+
+static inline u8 spi_nor_get_protocol_addr_width(enum spi_nor_protocol proto)
+{
+	return (spi_nor_get_protocol_class(proto) != SNOR_PROTO_CLASS_1_1_N) ?
+		spi_nor_get_protocol_width(proto) :
+		1u;
+}
+
+static inline u8 spi_nor_get_protocol_data_width(enum spi_nor_protocol proto)
+{
+	return spi_nor_get_protocol_width(proto);
+}
+
+
 #define SPI_NOR_MAX_CMD_SIZE	8
 enum spi_nor_ops {
 	SPI_NOR_OPS_READ = 0,
@@ -154,9 +225,11 @@ enum spi_nor_option_flags {
  * @read_opcode:	the read opcode
  * @read_dummy:		the dummy needed by the read operation
  * @program_opcode:	the program opcode
- * @flash_read:		the mode of the read
  * @sst_write_second:	used by the SST write operation
  * @flags:		flag options for the current SPI-NOR (SNOR_F_*)
+ * @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
  * @cmd_buf:		used by the write_reg
  * @prepare:		[OPTIONAL] do some preparations for the
  *			read/write/erase/lock/unlock operations
@@ -173,6 +246,7 @@ enum spi_nor_option_flags {
  * @flash_unlock:	[FLASH-SPECIFIC] unlock a region of the SPI NOR
  * @flash_is_locked:	[FLASH-SPECIFIC] check if a region of the SPI NOR is
  *			completely locked
+ * @flash_quad_enable:	[FLASH-SPECIFIC] set the Quad Enable bit of the SPI NOR
  * @priv:		the private data
  */
 struct spi_nor {
@@ -185,7 +259,9 @@ struct spi_nor {
 	u8			read_opcode;
 	u8			read_dummy;
 	u8			program_opcode;
-	enum read_mode		flash_read;
+	enum spi_nor_protocol	read_proto;
+	enum spi_nor_protocol	write_proto;
+	enum spi_nor_protocol	reg_proto;
 	bool			sst_write_second;
 	u32			flags;
 	u8			cmd_buf[SPI_NOR_MAX_CMD_SIZE];
@@ -204,6 +280,7 @@ struct spi_nor {
 	int (*flash_lock)(struct spi_nor *nor, loff_t ofs, uint64_t len);
 	int (*flash_unlock)(struct spi_nor *nor, loff_t ofs, uint64_t len);
 	int (*flash_is_locked)(struct spi_nor *nor, loff_t ofs, uint64_t len);
+	int (*flash_quad_enable)(struct spi_nor *nor);
 
 	void *priv;
 };
@@ -219,11 +296,73 @@ static inline struct device_node *spi_nor_get_flash_node(struct spi_nor *nor)
 	return mtd_get_of_node(&nor->mtd);
 }
 
+
+/**
+ * struct spi_nor_hwcaps - Structure for describing the hardware capabilies
+ * supported by the SPI controller (bus master).
+ * @mask:		the bitmask listing all the supported hw capabilies
+ */
+struct spi_nor_hwcaps {
+	u32	mask;
+};
+
+/*
+ *(Fast) Read capabilities.
+ * MUST be ordered by priority: the higher bit position, the higher priority.
+ * As a matter of performances, it is relevant to use Octo SPI protocols first,
+ * then Quad SPI protocols before Dual SPI protocols, Fast Read and lastly
+ * (Slow) Read.
+ */
+#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)
+
+#define SNOR_HWCAPS_READ_DUAL		GENMASK(7, 4)
+#define SNOR_HWCAPS_READ_1_1_2		BIT(4)
+#define SNOR_HWCAPS_READ_1_2_2		BIT(5)
+#define SNOR_HWCAPS_READ_2_2_2		BIT(6)
+#define SNOR_HWCAPS_READ_1_2_2_DTR	BIT(7)
+
+#define SNOR_HWCAPS_READ_QUAD		GENMASK(11, 8)
+#define SNOR_HWCAPS_READ_1_1_4		BIT(8)
+#define SNOR_HWCAPS_READ_1_4_4		BIT(9)
+#define SNOR_HWCAPS_READ_4_4_4		BIT(10)
+#define SNOR_HWCAPS_READ_1_4_4_DTR	BIT(11)
+
+#define SNOR_HWCPAS_READ_OCTO		GENMASK(15, 12)
+#define SNOR_HWCAPS_READ_1_1_8		BIT(12)
+#define SNOR_HWCAPS_READ_1_8_8		BIT(13)
+#define SNOR_HWCAPS_READ_8_8_8		BIT(14)
+#define SNOR_HWCAPS_READ_1_8_8_DTR	BIT(15)
+
+/*
+ * Page Program capabilities.
+ * MUST be ordered by priority: the higher bit position, the higher priority.
+ * Like (Fast) Read capabilities, Octo/Quad SPI protocols are preferred to the
+ * legacy SPI 1-1-1 protocol.
+ * Note that Dual Page Programs are not supported because there is no existing
+ * 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_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_OCTO	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)
+
 /**
  * spi_nor_scan() - scan the SPI NOR
  * @nor:	the spi_nor structure
  * @name:	the chip type name
- * @mode:	the read mode supported by the driver
+ * @hwcaps:	the hardware capabilities supported by the controller driver
  *
  * The drivers can use this fuction to scan the SPI NOR.
  * In the scanning, it will try to get all the necessary information to
@@ -233,6 +372,7 @@ static inline struct device_node *spi_nor_get_flash_node(struct spi_nor *nor)
  *
  * Return: 0 for success, others for failure.
  */
-int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode);
+int spi_nor_scan(struct spi_nor *nor, const char *name,
+		 const struct spi_nor_hwcaps *hwcaps);
 
 #endif
-- 
2.9.3

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

* [PATCH v5 2/6] mtd: m25p80: add support of SPI 1-2-2 and 1-4-4 protocols
  2017-03-22 23:33 [PATCH v5 0/6] mtd: spi-nor: parse SFDP tables to setup (Q)SPI memories Cyrille Pitchen
  2017-03-22 23:33 ` [PATCH v5 1/6] mtd: spi-nor: introduce more SPI protocols and the Dual Transfer Mode Cyrille Pitchen
@ 2017-03-22 23:33 ` Cyrille Pitchen
  2017-04-02 17:05   ` Cyrille Pitchen
  2017-04-06 23:37   ` Marek Vasut
  2017-03-22 23:33 ` [PATCH v5 3/6] mtd: spi-nor: add spi_nor_init() function Cyrille Pitchen
                   ` (4 subsequent siblings)
  6 siblings, 2 replies; 27+ messages in thread
From: Cyrille Pitchen @ 2017-03-22 23:33 UTC (permalink / raw)
  To: marek.vasut, linux-mtd, jartur, kdasu.kdev, mar.krzeminski
  Cc: computersforpeace, dwmw2, boris.brezillon, richard, linux-kernel,
	nicolas.ferre, Cyrille Pitchen

Before this patch, m25p80_read() supported few SPI protocols:
- regular SPI 1-1-1
- SPI Dual Output 1-1-2
- SPI Quad Output 1-1-4
On the other hand, m25p80_write() only supported SPI 1-1-1.

This patch updates both m25p80_read() and m25p80_write() functions to let
them support SPI 1-2-2 and SPI 1-4-4 protocols for Fast Read and Page
Program SPI commands.

It adopts a conservative approach to avoid regressions. Hence the new
implementations try to be as close as possible to the old implementations,
so the main differences are:
- the tx_nbits values now being set properly for the spi_transfer
  structures carrying the (op code + address/dummy) bytes
- and the spi_transfer structure being split into 2 spi_transfer
  structures when the numbers of I/O lines are different for op code and
  for address/dummy byte transfers on the SPI bus.

Besides, the current spi-nor framework supports neither the SPI 2-2-2 nor
the SPI 4-4-4 protocols. So, for now, we don't need to update the
m25p80_{read|write}_reg() functions as SPI 1-1-1 is the only one possible
protocol.

Signed-off-by: Cyrille Pitchen <cyrille.pitchen@atmel.com>
---
 drivers/mtd/devices/m25p80.c | 120 ++++++++++++++++++++++++++++++++-----------
 1 file changed, 90 insertions(+), 30 deletions(-)

diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c
index 68986a26c8fe..64d562efc25d 100644
--- a/drivers/mtd/devices/m25p80.c
+++ b/drivers/mtd/devices/m25p80.c
@@ -34,6 +34,19 @@ struct m25p {
 	u8			command[MAX_CMD_SIZE];
 };
 
+static inline void m25p80_proto2nbits(enum spi_nor_protocol proto,
+				      unsigned int *inst_nbits,
+				      unsigned int *addr_nbits,
+				      unsigned int *data_nbits)
+{
+	if (inst_nbits)
+		*inst_nbits = spi_nor_get_protocol_inst_width(proto);
+	if (addr_nbits)
+		*addr_nbits = spi_nor_get_protocol_addr_width(proto);
+	if (data_nbits)
+		*data_nbits = spi_nor_get_protocol_data_width(proto);
+}
+
 static int m25p80_read_reg(struct spi_nor *nor, u8 code, u8 *val, int len)
 {
 	struct m25p *flash = nor->priv;
@@ -78,11 +91,16 @@ static ssize_t m25p80_write(struct spi_nor *nor, loff_t to, size_t len,
 {
 	struct m25p *flash = nor->priv;
 	struct spi_device *spi = flash->spi;
-	struct spi_transfer t[2] = {};
+	unsigned int inst_nbits, addr_nbits, data_nbits, data_idx;
+	struct spi_transfer t[3] = {};
 	struct spi_message m;
 	int cmd_sz = m25p_cmdsz(nor);
 	ssize_t ret;
 
+	/* get transfer protocols. */
+	m25p80_proto2nbits(nor->write_proto, &inst_nbits,
+			   &addr_nbits, &data_nbits);
+
 	spi_message_init(&m);
 
 	if (nor->program_opcode == SPINOR_OP_AAI_WP && nor->sst_write_second)
@@ -92,12 +110,27 @@ static ssize_t m25p80_write(struct spi_nor *nor, loff_t to, size_t len,
 	m25p_addr2cmd(nor, to, flash->command);
 
 	t[0].tx_buf = flash->command;
+	t[0].tx_nbits = inst_nbits;
 	t[0].len = cmd_sz;
 	spi_message_add_tail(&t[0], &m);
 
-	t[1].tx_buf = buf;
-	t[1].len = len;
-	spi_message_add_tail(&t[1], &m);
+	/* split the op code and address bytes into two transfers if needed. */
+	data_idx = 1;
+	if (addr_nbits != inst_nbits) {
+		t[0].len = 1;
+
+		t[1].tx_buf = &flash->command[1];
+		t[1].tx_nbits = addr_nbits;
+		t[1].len = cmd_sz - 1;
+		spi_message_add_tail(&t[1], &m);
+
+		data_idx = 2;
+	}
+
+	t[data_idx].tx_buf = buf;
+	t[data_idx].tx_nbits = data_nbits;
+	t[data_idx].len = len;
+	spi_message_add_tail(&t[data_idx], &m);
 
 	ret = spi_sync(spi, &m);
 	if (ret)
@@ -109,18 +142,6 @@ static ssize_t m25p80_write(struct spi_nor *nor, loff_t to, size_t len,
 	return ret;
 }
 
-static inline unsigned int m25p80_rx_nbits(struct spi_nor *nor)
-{
-	switch (nor->read_proto) {
-	case SNOR_PROTO_1_1_2:
-		return 2;
-	case SNOR_PROTO_1_1_4:
-		return 4;
-	default:
-		return 0;
-	}
-}
-
 /*
  * Read an address range from the nor chip.  The address range
  * may be any size provided it is within the physical boundaries.
@@ -130,13 +151,19 @@ static ssize_t m25p80_read(struct spi_nor *nor, loff_t from, size_t len,
 {
 	struct m25p *flash = nor->priv;
 	struct spi_device *spi = flash->spi;
-	struct spi_transfer t[2];
+	unsigned int inst_nbits, addr_nbits, data_nbits, data_idx;
+	struct spi_transfer t[3];
 	struct spi_message m;
 	unsigned int dummy = nor->read_dummy;
 	ssize_t ret;
+	int cmd_sz;
+
+	/* get transfer protocols. */
+	m25p80_proto2nbits(nor->read_proto, &inst_nbits,
+			   &addr_nbits, &data_nbits);
 
 	/* convert the dummy cycles to the number of bytes */
-	dummy /= 8;
+	dummy = (dummy * addr_nbits) / 8;
 
 	if (spi_flash_read_supported(spi)) {
 		struct spi_flash_read_message msg;
@@ -149,10 +176,9 @@ static ssize_t m25p80_read(struct spi_nor *nor, loff_t from, size_t len,
 		msg.read_opcode = nor->read_opcode;
 		msg.addr_width = nor->addr_width;
 		msg.dummy_bytes = dummy;
-		/* TODO: Support other combinations */
-		msg.opcode_nbits = SPI_NBITS_SINGLE;
-		msg.addr_nbits = SPI_NBITS_SINGLE;
-		msg.data_nbits = m25p80_rx_nbits(nor);
+		msg.opcode_nbits = inst_nbits;
+		msg.addr_nbits = addr_nbits;
+		msg.data_nbits = data_nbits;
 
 		ret = spi_flash_read(spi, &msg);
 		if (ret < 0)
@@ -167,20 +193,45 @@ static ssize_t m25p80_read(struct spi_nor *nor, loff_t from, size_t len,
 	m25p_addr2cmd(nor, from, flash->command);
 
 	t[0].tx_buf = flash->command;
+	t[0].tx_nbits = inst_nbits;
 	t[0].len = m25p_cmdsz(nor) + dummy;
 	spi_message_add_tail(&t[0], &m);
 
-	t[1].rx_buf = buf;
-	t[1].rx_nbits = m25p80_rx_nbits(nor);
-	t[1].len = min3(len, spi_max_transfer_size(spi),
-			spi_max_message_size(spi) - t[0].len);
-	spi_message_add_tail(&t[1], &m);
+	/*
+	 * Set all dummy/mode cycle bits to avoid sending some manufacturer
+	 * specific pattern, which might make the memory enter its Continuous
+	 * Read mode by mistake.
+	 * Based on the different mode cycle bit patterns listed and described
+	 * in the JESD216B speficication, the 0xff value works for all memories
+	 * and all manufacturers.
+	 */
+	cmd_sz = t[0].len;
+	memset(flash->command + cmd_sz - dummy, 0xff, dummy);
+
+	/* split the op code and address bytes into two transfers if needed. */
+	data_idx = 1;
+	if (addr_nbits != inst_nbits) {
+		t[0].len = 1;
+
+		t[1].tx_buf = &flash->command[1];
+		t[1].tx_nbits = addr_nbits;
+		t[1].len = cmd_sz - 1;
+		spi_message_add_tail(&t[1], &m);
+
+		data_idx = 2;
+	}
+
+	t[data_idx].rx_buf = buf;
+	t[data_idx].rx_nbits = data_nbits;
+	t[data_idx].len = min3(len, spi_max_transfer_size(spi),
+			       spi_max_message_size(spi) - cmd_sz);
+	spi_message_add_tail(&t[data_idx], &m);
 
 	ret = spi_sync(spi, &m);
 	if (ret)
 		return ret;
 
-	ret = m.actual_length - m25p_cmdsz(nor) - dummy;
+	ret = m.actual_length - cmd_sz;
 	if (ret < 0)
 		return -EIO;
 	return ret;
@@ -223,11 +274,20 @@ static int m25p_probe(struct spi_device *spi)
 	spi_set_drvdata(spi, flash);
 	flash->spi = spi;
 
-	if (spi->mode & SPI_RX_QUAD)
+	if (spi->mode & SPI_RX_QUAD) {
 		hwcaps.mask |= SNOR_HWCAPS_READ_1_1_4;
-	else if (spi->mode & SPI_RX_DUAL)
+
+		if (spi->mode & SPI_TX_QUAD)
+			hwcaps.mask |= (SNOR_HWCAPS_READ_1_4_4 |
+					SNOR_HWCAPS_PP_1_1_4 |
+					SNOR_HWCAPS_PP_1_4_4);
+	} else if (spi->mode & SPI_RX_DUAL) {
 		hwcaps.mask |= SNOR_HWCAPS_READ_1_1_2;
 
+		if (spi->mode & SPI_TX_DUAL)
+			hwcaps.mask |= SNOR_HWCAPS_READ_1_2_2;
+	}
+
 	if (data && data->name)
 		nor->mtd.name = data->name;
 
-- 
2.9.3

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

* [PATCH v5 3/6] mtd: spi-nor: add spi_nor_init() function
  2017-03-22 23:33 [PATCH v5 0/6] mtd: spi-nor: parse SFDP tables to setup (Q)SPI memories Cyrille Pitchen
  2017-03-22 23:33 ` [PATCH v5 1/6] mtd: spi-nor: introduce more SPI protocols and the Dual Transfer Mode Cyrille Pitchen
  2017-03-22 23:33 ` [PATCH v5 2/6] mtd: m25p80: add support of SPI 1-2-2 and 1-4-4 protocols Cyrille Pitchen
@ 2017-03-22 23:33 ` Cyrille Pitchen
  2017-04-02 17:06   ` Cyrille Pitchen
  2017-03-22 23:33 ` [RFC PATCH v5 4/6] mtd: spi-nor: add support to non-uniform SPI NOR flash memories Cyrille Pitchen
                   ` (3 subsequent siblings)
  6 siblings, 1 reply; 27+ messages in thread
From: Cyrille Pitchen @ 2017-03-22 23:33 UTC (permalink / raw)
  To: marek.vasut, linux-mtd, jartur, kdasu.kdev, mar.krzeminski
  Cc: computersforpeace, dwmw2, boris.brezillon, richard, linux-kernel,
	nicolas.ferre, Cyrille Pitchen

This patch extracts some chunks from spi_nor_scan() and moves them into
a new spi_nor_init() function.

Indeed, spi_nor_init() regroups all the required SPI flash commands to be
sent to the SPI flash memory before performing any runtime operations
(Fast Read, Page Program, Sector Erase, ...). Hence spi_nor_init():
1) removes the flash protection if applicable for certain vendors.
2) sets the Quad Enable bit, if needed, before using Quad SPI protocols.
3) makes the memory enter its (stateful) 4-byte address mode, if needed,
   for SPI flash memory > 128Mbits not supporting the 4-byte address
   instruction set.

spi_nor_scan() now ends by calling spi_nor_init() once the probe phase has
completed. Further patches could also use spi_nor_init() to implement the
mtd->_resume() handler for the spi-nor framework.

Signed-off-by: Kamal Dasu <kdasu.kdev@gmail.com>
Signed-off-by: Cyrille Pitchen <cyrille.pitchen@atmel.com>
---
 drivers/mtd/spi-nor/spi-nor.c | 71 ++++++++++++++++++++++++++++---------------
 include/linux/mtd/spi-nor.h   |  8 +++++
 2 files changed, 55 insertions(+), 24 deletions(-)

diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c
index cc443c6cbae8..f648374d6824 100644
--- a/drivers/mtd/spi-nor/spi-nor.c
+++ b/drivers/mtd/spi-nor/spi-nor.c
@@ -255,6 +255,8 @@ static void spi_nor_set_4byte_opcodes(struct spi_nor *nor,
 	nor->read_opcode = spi_nor_convert_3to4_read(nor->read_opcode);
 	nor->program_opcode = spi_nor_convert_3to4_program(nor->program_opcode);
 	nor->erase_opcode = spi_nor_convert_3to4_erase(nor->erase_opcode);
+
+	nor->flags |= SNOR_F_4B_OPCODES;
 }
 
 /* Enable/disable 4-byte addressing mode. */
@@ -1809,6 +1811,45 @@ static int spi_nor_setup(struct spi_nor *nor, const struct flash_info *info,
 	return 0;
 }
 
+static int spi_nor_init(struct spi_nor *nor)
+{
+	const struct flash_info *info = nor->info;
+	struct device *dev = nor->dev;
+	int ret;
+
+	/*
+	 * Atmel, SST, Intel/Numonyx, and others serial NOR tend to power up
+	 * with the software protection bits set
+	 */
+
+	if (JEDEC_MFR(info) == SNOR_MFR_ATMEL ||
+	    JEDEC_MFR(info) == SNOR_MFR_INTEL ||
+	    JEDEC_MFR(info) == SNOR_MFR_SST ||
+	    info->flags & SPI_NOR_HAS_LOCK) {
+		write_enable(nor);
+		write_sr(nor, 0);
+		spi_nor_wait_till_ready(nor);
+	}
+
+	/* Set the Quad Enable bit, if needed. */
+	if (nor->flash_quad_enable) {
+		ret = nor->flash_quad_enable(nor);
+		if (ret) {
+			dev_err(dev, "quad mode not supported\n");
+			return ret;
+		}
+	}
+
+	/*
+	 * For SPI flash memories above 128Mib, enter the 4-byte address mode
+	 * only if the 4-byte address instruction set is not supported.
+	 */
+	if (nor->addr_width == 4 && !(nor->flags & SNOR_F_4B_OPCODES))
+		set_4byte(nor, info, 1);
+
+	return 0;
+}
+
 int spi_nor_scan(struct spi_nor *nor, const char *name,
 		 const struct spi_nor_hwcaps *hwcaps)
 {
@@ -1876,20 +1917,6 @@ int spi_nor_scan(struct spi_nor *nor, const char *name,
 	if (info->flags & SPI_S3AN)
 		nor->flags |=  SNOR_F_READY_XSR_RDY;
 
-	/*
-	 * Atmel, SST, Intel/Numonyx, and others serial NOR tend to power up
-	 * with the software protection bits set
-	 */
-
-	if (JEDEC_MFR(info) == SNOR_MFR_ATMEL ||
-	    JEDEC_MFR(info) == SNOR_MFR_INTEL ||
-	    JEDEC_MFR(info) == SNOR_MFR_SST ||
-	    info->flags & SPI_NOR_HAS_LOCK) {
-		write_enable(nor);
-		write_sr(nor, 0);
-		spi_nor_wait_till_ready(nor);
-	}
-
 	if (!mtd->name)
 		mtd->name = dev_name(dev);
 	mtd->priv = nor;
@@ -1960,14 +1987,6 @@ int spi_nor_scan(struct spi_nor *nor, const char *name,
 	if (ret)
 		return ret;
 
-	if (nor->flash_quad_enable) {
-		ret = nor->flash_quad_enable(nor);
-		if (ret) {
-			dev_err(dev, "quad mode not supported\n");
-			return ret;
-		}
-	}
-
 	if (info->addr_width)
 		nor->addr_width = info->addr_width;
 	else if (mtd->size > 0x1000000) {
@@ -1976,8 +1995,6 @@ int spi_nor_scan(struct spi_nor *nor, const char *name,
 		if (JEDEC_MFR(info) == SNOR_MFR_SPANSION ||
 		    info->flags & SPI_NOR_4B_OPCODES)
 			spi_nor_set_4byte_opcodes(nor, info);
-		else
-			set_4byte(nor, info, 1);
 	} else {
 		nor->addr_width = 3;
 	}
@@ -1994,6 +2011,12 @@ int spi_nor_scan(struct spi_nor *nor, const char *name,
 			return ret;
 	}
 
+	/* Send all the required SPI flash commands to initialize the memory. */
+	nor->info = info;
+	ret = spi_nor_init(nor);
+	if (ret)
+		return ret;
+
 	dev_info(dev, "%s (%lld Kbytes)\n", info->name,
 			(long long)mtd->size >> 10);
 
diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h
index 732ee6cd5330..d270788f5ab6 100644
--- a/include/linux/mtd/spi-nor.h
+++ b/include/linux/mtd/spi-nor.h
@@ -212,9 +212,16 @@ enum spi_nor_option_flags {
 	SNOR_F_NO_OP_CHIP_ERASE	= BIT(2),
 	SNOR_F_S3AN_ADDR_DEFAULT = BIT(3),
 	SNOR_F_READY_XSR_RDY	= BIT(4),
+	SNOR_F_4B_OPCODES	= BIT(5),
 };
 
 /**
+ * struct flash_info -	Forward declaration of a structure used internally by
+ *			spi_nor_scan() and spi_nor_init().
+ */
+struct flash_info;
+
+/**
  * struct spi_nor - Structure for defining a the SPI NOR layer
  * @mtd:		point to a mtd_info structure
  * @lock:		the lock for the read/write/erase/lock/unlock operations
@@ -253,6 +260,7 @@ struct spi_nor {
 	struct mtd_info		mtd;
 	struct mutex		lock;
 	struct device		*dev;
+	const struct flash_info	*info;
 	u32			page_size;
 	u8			addr_width;
 	u8			erase_opcode;
-- 
2.9.3

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

* [RFC PATCH v5 4/6] mtd: spi-nor: add support to non-uniform SPI NOR flash memories
  2017-03-22 23:33 [PATCH v5 0/6] mtd: spi-nor: parse SFDP tables to setup (Q)SPI memories Cyrille Pitchen
                   ` (2 preceding siblings ...)
  2017-03-22 23:33 ` [PATCH v5 3/6] mtd: spi-nor: add spi_nor_init() function Cyrille Pitchen
@ 2017-03-22 23:33 ` Cyrille Pitchen
  2017-04-15 15:24   ` Marek Vasut
  2017-03-22 23:33 ` [RFC PATCH v5 5/6] mtd: spi-nor: parse Serial Flash Discoverable Parameters (SFDP) tables Cyrille Pitchen
                   ` (2 subsequent siblings)
  6 siblings, 1 reply; 27+ messages in thread
From: Cyrille Pitchen @ 2017-03-22 23:33 UTC (permalink / raw)
  To: marek.vasut, linux-mtd, jartur, kdasu.kdev, mar.krzeminski
  Cc: computersforpeace, dwmw2, boris.brezillon, richard, linux-kernel,
	nicolas.ferre, Cyrille Pitchen

This patch is a first step in introducing  the support of SPI memories
with non-uniform erase sizes like Spansion s25fs512s.

It introduces the memory erase map which splits the memory array into one
or many erase regions. Each erase region supports up to 4 erase commands,
as defined by the JEDEC JESD216B (SFDP) specification.
In turn, an erase command is defined by an op code and a sector size.

To be backward compatible, the erase map of uniform SPI NOR flash memories
is initialized so it contains only one erase region and this erase region
supports only one erase command. Hence a single size is used to erase any
sector/block of the memory.

Besides, since the algorithm used to erase sectors on non-uniform SPI NOR
flash memories is quite expensive, when possible, the erase map is tuned
to come back to the uniform case.

This is a transitional patch: non-uniform erase maps will be used later
when initialized based on the SFDP data.

Signed-off-by: Cyrille Pitchen <cyrille.pitchen@atmel.com>
---
 drivers/mtd/spi-nor/spi-nor.c | 368 ++++++++++++++++++++++++++++++++++++++++--
 include/linux/mtd/spi-nor.h   |  56 +++++++
 2 files changed, 410 insertions(+), 14 deletions(-)

diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c
index f648374d6824..2e54792d506d 100644
--- a/drivers/mtd/spi-nor/spi-nor.c
+++ b/drivers/mtd/spi-nor/spi-nor.c
@@ -256,6 +256,18 @@ static void spi_nor_set_4byte_opcodes(struct spi_nor *nor,
 	nor->program_opcode = spi_nor_convert_3to4_program(nor->program_opcode);
 	nor->erase_opcode = spi_nor_convert_3to4_erase(nor->erase_opcode);
 
+	if (!spi_nor_has_uniform_erase(nor)) {
+		struct spi_nor_erase_map *map = &nor->erase_map;
+		struct spi_nor_erase_command *cmd;
+		int i;
+
+		for (i = 0; i < SNOR_CMD_ERASE_MAX; i++) {
+			cmd = &map->commands[i];
+
+			cmd->opcode = spi_nor_convert_3to4_erase(cmd->opcode);
+		}
+	}
+
 	nor->flags |= SNOR_F_4B_OPCODES;
 }
 
@@ -456,6 +468,132 @@ static int spi_nor_erase_sector(struct spi_nor *nor, u32 addr)
 	return nor->write_reg(nor, nor->erase_opcode, buf, nor->addr_width);
 }
 
+static inline u64
+spi_nor_div_by_erase_size(const struct spi_nor_erase_command *cmd,
+			  u64 dividend, u32 *remainder)
+{
+	if (likely(cmd->size_shift)) {
+		*remainder = (u32)dividend & cmd->size_mask;
+		return dividend >> cmd->size_shift;
+	}
+
+	return div_u64_rem(dividend, cmd->size, remainder);
+}
+
+static bool
+spi_nor_test_erase_region(struct spi_nor *nor, u64 addr, u32 len,
+			  const struct spi_nor_erase_region *region,
+			  const struct spi_nor_erase_command **cmd)
+{
+	const struct spi_nor_erase_map *map = &nor->erase_map;
+	const struct spi_nor_erase_command *best_cmd = NULL;
+	const struct spi_nor_erase_command *tested_cmd;
+	u64 region_start, region_end, cmd_mask;
+	u32 rem;
+	int i;
+
+	region_start = region->offset & ~SNOR_CMD_ERASE_MASK;
+	region_end = region_start + region->size;
+
+	cmd_mask = region->offset & SNOR_CMD_ERASE_MASK;
+	for (i = 0; i < SNOR_CMD_ERASE_MAX; i++) {
+		/* Does the erase region support the tested erase command? */
+		if (!(cmd_mask & BIT(i)))
+			continue;
+
+		tested_cmd = &map->commands[i];
+
+		/* Don't erase more than what the user has asked for. */
+		if (tested_cmd->size > len)
+			continue;
+
+		/* 'addr' must be aligned to the erase size. */
+		spi_nor_div_by_erase_size(tested_cmd, addr, &rem);
+		if (rem)
+			continue;
+
+		/*
+		 * 'addr' must be inside the region.
+		 * Erase regions may overlap, so compute the actual start offset
+		 * of this erase region based on the size of the tested erase
+		 * command.
+		 */
+		spi_nor_div_by_erase_size(tested_cmd, region_start, &rem);
+		if (addr < (region_start - rem) || region_end <= addr)
+			continue;
+
+		/*
+		 * The tested erase size is valid but we still need to check
+		 * whether it is better than the current best erase command.
+		 */
+		if (!best_cmd || best_cmd->size < tested_cmd->size)
+			best_cmd = tested_cmd;
+	}
+
+	*cmd = best_cmd;
+	return (best_cmd != NULL);
+}
+
+static bool
+spi_nor_find_erase_region(struct spi_nor *nor, u64 addr, u32 len,
+			  const struct spi_nor_erase_region **region,
+			  const struct spi_nor_erase_command **cmd)
+{
+	const struct spi_nor_erase_map *map = &nor->erase_map;
+	const struct spi_nor_erase_region *best_region = NULL;
+	const struct spi_nor_erase_command *best_cmd = NULL;
+	int i;
+
+	for (i = 0; i < map->num_regions; i++) {
+		const struct spi_nor_erase_command *tested_cmd = NULL;
+
+		if (!spi_nor_test_erase_region(nor, addr, len, &map->regions[i],
+					       &tested_cmd))
+			continue;
+
+		if (!best_cmd || best_cmd->size < tested_cmd->size) {
+			best_region = &map->regions[i];
+			best_cmd = tested_cmd;
+		}
+	}
+
+	*region = best_region;
+	*cmd = best_cmd;
+	return (best_cmd != NULL);
+}
+
+static int spi_nor_erase_multi_sectors(struct spi_nor *nor, u32 addr, u32 len)
+{
+	const struct spi_nor_erase_region *region;
+	const struct spi_nor_erase_command *cmd;
+	u64 region_start, region_end;
+	int ret;
+
+	while (len) {
+		if (!spi_nor_find_erase_region(nor, addr, len, &region, &cmd))
+			return -EINVAL;
+
+		nor->erase_opcode = cmd->opcode;
+
+		region_start = region->offset & ~SNOR_CMD_ERASE_MASK;
+		region_end = region_start + region->size;
+		while (len && (u64)addr < region_end) {
+			ret = spi_nor_erase_sector(nor, addr);
+			if (ret)
+				return ret;
+
+			addr += cmd->size;
+			len -= cmd->size;
+
+			ret = spi_nor_wait_till_ready(nor);
+			if (ret)
+				return ret;
+		}
+	}
+
+	return 0;
+}
+
 /*
  * Erase an address range on the nor chip.  The address range may extend
  * one or more erase sectors.  Return an error is there is a problem erasing.
@@ -470,9 +608,11 @@ static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr)
 	dev_dbg(nor->dev, "at 0x%llx, len %lld\n", (long long)instr->addr,
 			(long long)instr->len);
 
-	div_u64_rem(instr->len, mtd->erasesize, &rem);
-	if (rem)
-		return -EINVAL;
+	if (likely(spi_nor_has_uniform_erase(nor))) {
+		div_u64_rem(instr->len, mtd->erasesize, &rem);
+		if (rem)
+			return -EINVAL;
+	}
 
 	addr = instr->addr;
 	len = instr->len;
@@ -511,7 +651,7 @@ static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr)
 	 */
 
 	/* "sector"-at-a-time erase */
-	} else {
+	} else if (likely(spi_nor_has_uniform_erase(nor))) {
 		while (len) {
 			write_enable(nor);
 
@@ -526,6 +666,12 @@ static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr)
 			if (ret)
 				goto erase_err;
 		}
+
+	/* erase multiple sectors */
+	} else {
+		ret = spi_nor_erase_multi_sectors(nor, addr, len);
+		if (ret)
+			goto erase_err;
 	}
 
 	write_disable(nor);
@@ -1588,10 +1734,38 @@ spi_nor_set_pp_settings(struct spi_nor_pp_command *pp,
 	pp->proto = proto;
 }
 
+static inline void
+spi_nor_set_erase_command(struct spi_nor_erase_command *cmd,
+			  u32 size, u8 opcode)
+{
+	cmd->size = size;
+	cmd->opcode = opcode;
+
+	if (is_power_of_2(cmd->size))
+		cmd->size_shift = ffs(cmd->size) - 1;
+	else
+		cmd->size_shift = 0;
+
+	cmd->size_mask = (1 << cmd->size_shift) - 1;
+}
+
+static inline void
+spi_nor_init_uniform_erase_map(struct spi_nor_erase_map *map,
+			       u32 cmd_mask, u64 flash_size)
+{
+	map->num_regions = 1;
+	map->regions = &map->uniform_region;
+	map->uniform_region.offset = SNOR_CMD_ERASE_OFFSET(cmd_mask, 0);
+	map->uniform_region.size = flash_size;
+}
+
 static int spi_nor_init_params(struct spi_nor *nor,
 			       const struct flash_info *info,
 			       struct spi_nor_flash_parameter *params)
 {
+	struct spi_nor_erase_map *map = &nor->erase_map;
+	u32 erase_mask = 0;
+
 	/* Set legacy flash parameters as default. */
 	memset(params, 0, sizeof(*params));
 
@@ -1628,6 +1802,21 @@ static int spi_nor_init_params(struct spi_nor *nor,
 	spi_nor_set_pp_settings(&params->page_programs[SNOR_CMD_PP],
 				SPINOR_OP_PP, SNOR_PROTO_1_1_1);
 
+	/* Sector Erase settings. */
+	erase_mask |= BIT(0);
+	spi_nor_set_erase_command(&map->commands[0],
+				  info->sector_size, SPINOR_OP_SE);
+	if (info->flags & SECT_4K_PMC) {
+		erase_mask |= BIT(1);
+		spi_nor_set_erase_command(&map->commands[1],
+					  4096u, SPINOR_OP_BE_4K_PMC);
+	} else if (info->flags & SECT_4K) {
+		erase_mask |= BIT(1);
+		spi_nor_set_erase_command(&map->commands[1],
+					  4096u, SPINOR_OP_BE_4K);
+	}
+	spi_nor_init_uniform_erase_map(map, erase_mask, params->size);
+
 	/* Select the procedure to set the Quad Enable bit. */
 	if (params->hwcaps.mask & (SNOR_HWCAPS_READ_QUAD |
 				   SNOR_HWCAPS_PP_QUAD)) {
@@ -1731,25 +1920,176 @@ static int spi_nor_select_pp(struct spi_nor *nor,
 	return 0;
 }
 
+static bool spi_nor_find_uniform_erase(const struct spi_nor *nor,
+				       u32 preferred_size,
+				       const struct spi_nor_erase_command **cmd)
+{
+	const struct spi_nor_erase_map *map = &nor->erase_map;
+	const struct spi_nor_erase_command *tested_cmd;
+	const struct spi_nor_erase_region *region;
+	const struct mtd_info *mtd = &nor->mtd;
+	u64 pos, region_start, region_end;
+	int cidx, ridx;
+	u32 rem;
+
+	/* Try all erase commands to find the best one (greatest erase size). */
+	*cmd = NULL;
+	for (cidx = 0; cidx < SNOR_CMD_ERASE_MAX; cidx++) {
+		tested_cmd = &map->commands[cidx];
+
+		/* The SPI flash size must be a multiple of the erase size. */
+		spi_nor_div_by_erase_size(tested_cmd, mtd->size, &rem);
+		if (rem)
+			continue;
+
+		/*
+		 * Walk through regions to check whether the whole memory can be
+		 * erased using only the tested erase command.
+		 */
+		pos = 0;
+		while (pos < mtd->size) {
+			for (ridx = 0; ridx < map->num_regions; ridx++) {
+				region = &map->regions[ridx];
+
+				/* The region must support the erase command. */
+				if (!(region->offset & BIT(cidx)))
+					continue;
+
+				/*
+				 * Compute the actual start and end offsets of
+				 * region based on the erase size.
+				 */
+				region_start = region->offset;
+				region_start &= ~SNOR_CMD_ERASE_MASK;
+				region_end = region_start + region->size;
+
+				spi_nor_div_by_erase_size(tested_cmd,
+							  region_start,
+							  &rem);
+				region_start -= rem;
+
+				spi_nor_div_by_erase_size(tested_cmd,
+							  region_end,
+							  &rem);
+				if (rem)
+					region_end += tested_cmd->size - rem;
+
+				/*
+				 * The current position must be the start offset
+				 * of the region.
+				 */
+				if (region_start == pos) {
+					/* Keep walking through regions. */
+					pos = region_end;
+					break;
+				}
+			}
+
+			/* No region found. */
+			if (ridx == map->num_regions)
+				break;
+		}
+
+		/*
+		 * If we can't erase the whole memory using only the current
+		 * erase command, stop here and try the next supported erase
+		 * command.
+		 */
+		if (pos != mtd->size)
+			continue;
+
+		/*
+		 * If the current erase size is the preferred one, stop here:
+		 * we have found the right uniform Sector Erase command.
+		 */
+		if (tested_cmd->size == preferred_size) {
+			*cmd = tested_cmd;
+			break;
+		}
+
+		/*
+		 * Otherwise, the current erase size is still a valid canditate:
+		 * we select the highest possible erase size.
+		 */
+		if (!(*cmd) || tested_cmd->size > (*cmd)->size)
+			*cmd = tested_cmd;
+	}
+
+	return (*cmd != NULL);
+}
+
 static int spi_nor_select_erase(struct spi_nor *nor,
 				const struct flash_info *info)
 {
+	struct spi_nor_erase_map *map = &nor->erase_map;
+	const struct spi_nor_erase_command *cmd = NULL;
+	u32 preferred_size = info->sector_size;
 	struct mtd_info *mtd = &nor->mtd;
+	bool is_uniform = false;
+	int i;
 
+	/*
+	 * The previous implementation handling Sector Erase commands assumed
+	 * that the SPI flash memory has an uniform layout then used only one
+	 * of the supported erase sizes for all Sector Erase commands.
+	 * So to be backward compatible, the new implementation also tries to
+	 * manage the SPI flash memory as uniform with a single erase sector
+	 * size.
+	 */
 #ifdef CONFIG_MTD_SPI_NOR_USE_4K_SECTORS
 	/* prefer "small sector" erase if possible */
-	if (info->flags & SECT_4K) {
-		nor->erase_opcode = SPINOR_OP_BE_4K;
-		mtd->erasesize = 4096;
-	} else if (info->flags & SECT_4K_PMC) {
-		nor->erase_opcode = SPINOR_OP_BE_4K_PMC;
-		mtd->erasesize = 4096;
-	} else
+	if (info->flags & (SECT_4K | SECT_4K_PMC))
+		preferred_size = 4096u;
 #endif
-	{
-		nor->erase_opcode = SPINOR_OP_SE;
-		mtd->erasesize = info->sector_size;
+
+	if (spi_nor_has_uniform_erase(nor)) {
+		/* The SPI flash memory is a real uniform one. */
+		cmd = NULL;
+		for (i = 0; i < SNOR_CMD_ERASE_MAX; i++) {
+			if (!(map->uniform_region.offset & BIT(i)))
+				continue;
+
+			if (map->commands[i].size == preferred_size) {
+				cmd = &map->commands[i];
+				break;
+			}
+			if (!cmd || map->commands[i].size > cmd->size)
+				cmd = &map->commands[i];
+		}
+
+		if (!cmd || !cmd->size)
+			return -EINVAL;
+
+		/* Disable all other Sector Erase commands. */
+		map->uniform_region.offset &= ~SNOR_CMD_ERASE_MASK;
+		map->uniform_region.offset |= BIT(cmd - map->commands);
+		is_uniform = true;
+	} else if (spi_nor_find_uniform_erase(nor, preferred_size, &cmd)) {
+		/* The SPI flash memory can be managed as an uniform one. */
+		spi_nor_init_uniform_erase_map(map, BIT(cmd - map->commands),
+					       mtd->size);
+		is_uniform = true;
+	}
+
+	if (is_uniform) {
+		/* Set the Sector Erase opcode and the associated size. */
+		nor->erase_opcode = cmd->opcode;
+		mtd->erasesize = cmd->size;
+		return 0;
 	}
+
+	/*
+	 * For non-uniform SPI flash memory, set mtd->erasesize to the
+	 * maximum erase sector size. No need to set nor->erase_opcode.
+	 */
+	cmd = NULL;
+	for (i = 0; i < SNOR_CMD_ERASE_MAX; i++)
+		if (!cmd || map->commands[i].size > cmd->size)
+			cmd = &map->commands[i];
+	if (!cmd || !cmd->size)
+		return -EINVAL;
+
+	mtd->erasesize = cmd->size;
 	return 0;
 }
 
diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h
index d270788f5ab6..c12cafe99bee 100644
--- a/include/linux/mtd/spi-nor.h
+++ b/include/linux/mtd/spi-nor.h
@@ -216,6 +216,55 @@ enum spi_nor_option_flags {
 };
 
 /**
+ * struct spi_nor_erase_command - Structure to describe a SPI NOR erase command
+ * @size:		the size of the sector/block erased by the command.
+ * @size_shift:		the size shift: if @size is a power of 2 then the shift
+ *			is stored in @size_shift, otherwise @size_shift is zero.
+ * @size_mask:		the size mask based on @size_shift.
+ * @opcode:		the SPI command op code to erase the sector/block.
+ */
+struct spi_nor_erase_command {
+	u32	size;
+	u32	size_shift;
+	u32	size_mask;
+	u8	opcode;
+};
+
+/**
+ * struct spi_nor_erase_region - Structure to describe a SPI NOR erase region
+ * @offset:		the offset in the data array of erase region start.
+ *			LSB bits are used as a bitmask encoding the erase
+ *			commands supported inside this erase region.
+ * @size:		the size of the region in bytes.
+ */
+struct spi_nor_erase_region {
+	u64		offset;
+	u64		size;
+};
+
+#define SNOR_CMD_ERASE_MAX	4
+#define SNOR_CMD_ERASE_MASK	GENMASK_ULL(SNOR_CMD_ERASE_MAX - 1, 0)
+#define SNOR_CMD_ERASE_OFFSET(_cmd_mask, _offset)	\
+	((((u64)(_offset)) & ~SNOR_CMD_ERASE_MASK) |	\
+	 (((u64)(_cmd_mask)) & SNOR_CMD_ERASE_MASK))
+
+/**
+ * struct spi_nor_erase_map - Structure to describe the SPI NOR erase map
+ * @commands:		an array of erase commands shared by all the regions.
+ * @uniform_region:	a pre-allocated erase region for SPI NOR with a uniform
+ *			sector size (legacy implementation).
+ * @regions:		point to an array describing the boundaries of the erase
+ *			regions.
+ * @num_regions:	the number of elements in the @regions array.
+ */
+struct spi_nor_erase_map {
+	struct spi_nor_erase_command	commands[SNOR_CMD_ERASE_MAX];
+	struct spi_nor_erase_region	uniform_region;
+	struct spi_nor_erase_region	*regions;
+	u32				num_regions;
+};
+
+/**
  * struct flash_info -	Forward declaration of a structure used internally by
  *			spi_nor_scan() and spi_nor_init().
  */
@@ -238,6 +287,7 @@ struct flash_info;
  * @write_proto:	the SPI protocol for write operations
  * @reg_proto		the SPI protocol for read_reg/write_reg/erase operations
  * @cmd_buf:		used by the write_reg
+ * @erase_map:		the erase map of the SPI NOR
  * @prepare:		[OPTIONAL] do some preparations for the
  *			read/write/erase/lock/unlock operations
  * @unprepare:		[OPTIONAL] do some post work after the
@@ -273,6 +323,7 @@ struct spi_nor {
 	bool			sst_write_second;
 	u32			flags;
 	u8			cmd_buf[SPI_NOR_MAX_CMD_SIZE];
+	struct spi_nor_erase_map	erase_map;
 
 	int (*prepare)(struct spi_nor *nor, enum spi_nor_ops ops);
 	void (*unprepare)(struct spi_nor *nor, enum spi_nor_ops ops);
@@ -293,6 +344,11 @@ struct spi_nor {
 	void *priv;
 };
 
+static inline bool spi_nor_has_uniform_erase(const struct spi_nor *nor)
+{
+	return (nor->erase_map.regions == &nor->erase_map.uniform_region);
+}
+
 static inline void spi_nor_set_flash_node(struct spi_nor *nor,
 					  struct device_node *np)
 {
-- 
2.9.3

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

* [RFC PATCH v5 5/6] mtd: spi-nor: parse Serial Flash Discoverable Parameters (SFDP) tables
  2017-03-22 23:33 [PATCH v5 0/6] mtd: spi-nor: parse SFDP tables to setup (Q)SPI memories Cyrille Pitchen
                   ` (3 preceding siblings ...)
  2017-03-22 23:33 ` [RFC PATCH v5 4/6] mtd: spi-nor: add support to non-uniform SPI NOR flash memories Cyrille Pitchen
@ 2017-03-22 23:33 ` Cyrille Pitchen
  2017-04-15 15:34   ` Marek Vasut
  2017-03-22 23:39 ` [RFC PATCH v5 6/6] mtd: spi-nor: parse SFDP 4-byte Address Instruction Table Cyrille Pitchen
  2017-03-29 16:45 ` [PATCH v5 0/6] mtd: spi-nor: parse SFDP tables to setup (Q)SPI memories Cyrille Pitchen
  6 siblings, 1 reply; 27+ messages in thread
From: Cyrille Pitchen @ 2017-03-22 23:33 UTC (permalink / raw)
  To: marek.vasut, linux-mtd, jartur, kdasu.kdev, mar.krzeminski
  Cc: computersforpeace, dwmw2, boris.brezillon, richard, linux-kernel,
	nicolas.ferre, Cyrille Pitchen

This patch adds support to the JESD216B standard and parses the SFDP
tables to dynamically initialize the 'struct spi_nor_flash_parameter'.

Signed-off-by: Cyrille Pitchen <cyrille.pitchen@atmel.com>
---
 drivers/mtd/spi-nor/spi-nor.c | 558 ++++++++++++++++++++++++++++++++++++++++++
 include/linux/mtd/spi-nor.h   |   6 +
 2 files changed, 564 insertions(+)

diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c
index 2e54792d506d..ce8722055a9c 100644
--- a/drivers/mtd/spi-nor/spi-nor.c
+++ b/drivers/mtd/spi-nor/spi-nor.c
@@ -17,6 +17,7 @@
 #include <linux/mutex.h>
 #include <linux/math64.h>
 #include <linux/sizes.h>
+#include <linux/slab.h>
 
 #include <linux/mtd/mtd.h>
 #include <linux/of_platform.h>
@@ -86,6 +87,7 @@ struct flash_info {
 					 * to support memory size above 128Mib.
 					 */
 #define NO_CHIP_ERASE		BIT(12) /* Chip does not support chip erase */
+#define SPI_NOR_SKIP_SFDP	BIT(13)	/* Skip parsing of SFDP tables */
 };
 
 #define JEDEC_MFR(info)	((info)->id[0])
@@ -1593,6 +1595,99 @@ static int spansion_quad_enable(struct spi_nor *nor)
 	return 0;
 }
 
+static int spansion_new_quad_enable(struct spi_nor *nor)
+{
+	u8 sr_cr[2];
+	int ret;
+
+	/* Check current Quad Enable bit value. */
+	ret = read_cr(nor);
+	if (ret < 0) {
+		dev_err(nor->dev,
+			"error while reading configuration register\n");
+		return -EINVAL;
+	}
+	sr_cr[1] = ret;
+	if (sr_cr[1] & CR_QUAD_EN_SPAN)
+		return 0;
+
+	dev_info(nor->dev, "setting Spansion Quad Enable (non-volatile) bit\n");
+
+	/* Keep the current value of the Status Register. */
+	ret = read_sr(nor);
+	if (ret < 0) {
+		dev_err(nor->dev,
+			"error while reading status register\n");
+		return -EINVAL;
+	}
+	sr_cr[0] = ret;
+	sr_cr[1] |= CR_QUAD_EN_SPAN;
+
+	write_enable(nor);
+
+	ret = nor->write_reg(nor, SPINOR_OP_WRSR, sr_cr, 2);
+	if (ret < 0) {
+		dev_err(nor->dev,
+			"error while writing configuration register\n");
+		return -EINVAL;
+	}
+
+	ret = spi_nor_wait_till_ready(nor);
+	if (ret < 0) {
+		dev_err(nor->dev, "error while waiting for WRSR completion\n");
+		return ret;
+	}
+
+	/* read back and check it */
+	ret = read_cr(nor);
+	if (!(ret > 0 && (ret & CR_QUAD_EN_SPAN))) {
+		dev_err(nor->dev, "Spansion Quad bit not set\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int sr2_bit7_quad_enable(struct spi_nor *nor)
+{
+	u8 sr2;
+	int ret;
+
+	/* Check current Quad Enable bit value. */
+	ret = nor->read_reg(nor, SPINOR_OP_RDSR2, &sr2, 1);
+	if (ret)
+		return ret;
+	if (sr2 & SR2_QUAD_EN_BIT7)
+		return 0;
+
+	/* Update the Quad Enable bit. */
+	sr2 |= SR2_QUAD_EN_BIT7;
+
+	write_enable(nor);
+
+	ret = nor->write_reg(nor, SPINOR_OP_WRSR2, &sr2, 1);
+	if (ret < 0) {
+		dev_err(nor->dev,
+			"error while writing status register 2\n");
+		return -EINVAL;
+	}
+
+	ret = spi_nor_wait_till_ready(nor);
+	if (ret < 0) {
+		dev_err(nor->dev, "error while waiting for WRSR2 completion\n");
+		return ret;
+	}
+
+	/* Read back and check it. */
+	ret = nor->read_reg(nor, SPINOR_OP_RDSR2, &sr2, 1);
+	if (ret || !(sr2 & SR2_QUAD_EN_BIT7)) {
+		dev_err(nor->dev, "SR2 Quad bit not set\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
 static int spi_nor_check(struct spi_nor *nor)
 {
 	if (!nor->dev || !nor->read || !nor->write ||
@@ -1759,6 +1854,465 @@ spi_nor_init_uniform_erase_map(struct spi_nor_erase_map *map,
 	map->uniform_region.size = flash_size;
 }
 
+
+/*
+ * SFDP parsing.
+ */
+
+static int spi_nor_read_sfdp(struct spi_nor *nor, u32 addr,
+			     size_t len, void *buf)
+{
+	u8 addr_width, read_opcode, read_dummy;
+	int ret;
+
+	read_opcode = nor->read_opcode;
+	addr_width = nor->addr_width;
+	read_dummy = nor->read_dummy;
+
+	nor->read_opcode = SPINOR_OP_RDSFDP;
+	nor->addr_width = 3;
+	nor->read_dummy = 8;
+
+	ret = nor->read(nor, addr, len, (u8 *)buf);
+
+	nor->read_opcode = read_opcode;
+	nor->addr_width = addr_width;
+	nor->read_dummy = read_dummy;
+
+	return (ret < 0) ? ret : 0;
+}
+
+struct sfdp_parameter_header {
+	u8		id_lsb;
+	u8		minor;
+	u8		major;
+	u8		length; /* in double words */
+	u8		parameter_table_pointer[3]; /* byte address */
+	u8		id_msb;
+};
+
+#define SFDP_PARAM_HEADER_ID(p)	((u16)(((p)->id_msb << 8) | (p)->id_lsb))
+#define SFDP_PARAM_HEADER_PTP(p) \
+	((u32)(((p)->parameter_table_pointer[2] << 16) | \
+	       ((p)->parameter_table_pointer[1] <<  8) | \
+	       ((p)->parameter_table_pointer[0] <<  0)))
+
+
+#define SFDP_BFPT_ID		0xff00u	/* Basic Flash Parameter 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
+
+struct sfdp_header {
+	u32		signature; /* Ox50444653 <=> "SFDP" */
+	u8		minor;
+	u8		major;
+	u8		nph; /* 0-base number of parameter headers */
+	u8		unused;
+
+	/* Basic Flash Parameter Table. */
+	struct sfdp_parameter_header	bfpt_header;
+};
+
+/* Basic Flash Parameter Table */
+
+/*
+ * JESD216B defines a Basic Flash Parameter Table of 16 DWORDs.
+ * They are indexed from 1 but C arrays are indexed from 0.
+ */
+enum sfdp_bfpt_dword {
+	BFPT_DWORD1 = 0,
+	BFPT_DWORD2,
+	BFPT_DWORD3,
+	BFPT_DWORD4,
+	BFPT_DWORD5,
+	BFPT_DWORD6,
+	BFPT_DWORD7,
+	BFPT_DWORD8,
+	BFPT_DWORD9,
+	BFPT_DWORD10,
+	BFPT_DWORD11,
+	BFPT_DWORD12,
+	BFPT_DWORD13,
+	BFPT_DWORD14,
+	BFPT_DWORD15,
+	BFPT_DWORD16,
+
+	BFPT_DWORD_MAX
+};
+
+/* The first revision of JESB216 defined only 9 DWORDs. */
+#define BFPT_DWORD_MAX_JESD216			9
+
+/* 1st DWORD. */
+#define BFPT_DWORD1_FAST_READ_1_1_2		BIT(16)
+#define BFPT_DWORD1_ADDRESS_BYTES_MASK		GENMASK(18, 17)
+#define BFPT_DWORD1_ADDRESS_BYTES_3_ONLY	(0u << 17)
+#define BFPT_DWORD1_ADDRESS_BYTES_3_OR_4	(1u << 17)
+#define BFPT_DWORD1_ADDRESS_BYTES_4_ONLY	(2u << 17)
+#define BFPT_DWORD1_DTR				BIT(19)
+#define BFPT_DWORD1_FAST_READ_1_2_2		BIT(20)
+#define BFPT_DWORD1_FAST_READ_1_4_4		BIT(21)
+#define BFPT_DWORD1_FAST_READ_1_1_4		BIT(22)
+
+/* 5th DWORD. */
+#define BFPT_DWORD5_FAST_READ_2_2_2		BIT(0)
+#define BFPT_DWORD5_FAST_READ_4_4_4		BIT(4)
+
+/* 11th DWORD. */
+#define BFPT_DWORD11_PAGE_SIZE_SHIFT		4
+#define BFPT_DWORD11_PAGE_SIZE_MASK		GENMASK(7, 4)
+
+/* 15th DWORD. */
+
+/*
+ * (from JESD216B)
+ * Quad Enable Requirements (QER):
+ * - 000b: Device does not have a QE bit. Device detects 1-1-4 and 1-4-4
+ *         reads based on instruction. DQ3/HOLD# functions are hold during
+ *         instruction pahse.
+ * - 001b: QE is bit 1 of status register 2. It is set via Write Status with
+ *         two data bytes where bit 1 of the second byte is one.
+ *         [...]
+ *         Writing only one byte to the status register has the side-effect of
+ *         clearing status register 2, including the QE bit. The 100b code is
+ *         used if writing one byte to the status register does not modify
+ *         status register 2.
+ * - 010b: QE is bit 6 of status register 1. It is set via Write Status with
+ *         one data byte where bit 6 is one.
+ *         [...]
+ * - 011b: QE is bit 7 of status register 2. It is set via Write status
+ *         register 2 instruction 3Eh with one data byte where bit 7 is one.
+ *         [...]
+ *         The status register 2 is read using instruction 3Fh.
+ * - 100b: QE is bit 1 of status register 2. It is set via Write Status with
+ *         two data bytes where bit 1 of the second byte is one.
+ *         [...]
+ *         In contrast to the 001b code, writing one byte to the status
+ *         register does not modify status register 2.
+ * - 101b: QE is bit 1 of status register 2. Status register 1 is read using
+ *         Read Status instruction 05h. Status register2 is read using
+ *         instruction 35h. QE is set via Writ Status instruction 01h with
+ *         two data bytes where bit 1 of the second byte is one.
+ *         [...]
+ */
+#define BFPT_DWORD15_QER_MASK			GENMASK(22, 20)
+#define BFPT_DWORD15_QER_NONE			(0u << 20) /* Micron */
+#define BFPT_DWORD15_QER_SR2_BIT1_BUGGY		(1u << 20)
+#define BFPT_DWORD15_QER_SR1_BIT6		(2u << 20) /* Macronix */
+#define BFPT_DWORD15_QER_SR2_BIT7		(3u << 20)
+#define BFPT_DWORD15_QER_SR2_BIT1_NO_RD		(4u << 20)
+#define BFPT_DWORD15_QER_SR2_BIT1		(5u << 20) /* Spansion */
+
+
+struct sfdp_bfpt {
+	u32	dwords[BFPT_DWORD_MAX];
+};
+
+/* Fast Read settings. */
+
+static inline void
+spi_nor_set_read_settings_from_bfpt(struct spi_nor_read_command *read,
+				    u16 half,
+				    enum spi_nor_protocol proto)
+{
+	read->num_mode_clocks = (half >> 5) & 0x07u;
+	read->num_wait_states = (half >> 0) & 0x1Fu;
+	read->opcode = (half >> 8) & 0xFFu;
+	read->proto = proto;
+}
+
+struct sfdp_bfpt_read {
+	/* The Fast Read x-y-z hardware capability in params->hwcaps.mask. */
+	u32				hwcaps;
+
+	/*
+	 * The <supported_bit> bit in <supported_dword> BFPT DWORD tells us
+	 * whether the Fast Read x-y-z command is supported.
+	 */
+	enum sfdp_bfpt_dword		supported_dword;
+	u32				supported_bit;
+
+	/*
+	 * The half-word at offset <setting_shift> in <setting_dword> BFPT DWORD
+	 * encodes the op code, the number of mode clocks and the number of wait
+	 * states to be used by Fast Read x-y-z command.
+	 */
+	enum sfdp_bfpt_dword		settings_dword;
+	int				settings_shift;
+
+	/* The SPI protocol for this Fast Read x-y-z command. */
+	enum spi_nor_protocol		proto;
+};
+
+static const struct sfdp_bfpt_read sfdp_bfpt_reads[] = {
+	/* Fast Read 1-1-2 */
+	{
+		SNOR_HWCAPS_READ_1_1_2,
+		BFPT_DWORD1, BIT(16),	/* Supported bit */
+		BFPT_DWORD4, 0,		/* Settings */
+		SNOR_PROTO_1_1_2,
+	},
+
+	/* Fast Read 1-2-2 */
+	{
+		SNOR_HWCAPS_READ_1_2_2,
+		BFPT_DWORD1, BIT(20),	/* Supported bit */
+		BFPT_DWORD4, 16,	/* Settings */
+		SNOR_PROTO_1_2_2,
+	},
+
+	/* Fast Read 2-2-2 */
+	{
+		SNOR_HWCAPS_READ_2_2_2,
+		BFPT_DWORD5,  BIT(0),	/* Supported bit */
+		BFPT_DWORD6, 16,	/* Settings */
+		SNOR_PROTO_2_2_2,
+	},
+
+	/* Fast Read 1-1-4 */
+	{
+		SNOR_HWCAPS_READ_1_1_4,
+		BFPT_DWORD1, BIT(22),	/* Supported bit */
+		BFPT_DWORD3, 16,	/* Settings */
+		SNOR_PROTO_1_1_4,
+	},
+
+	/* Fast Read 1-4-4 */
+	{
+		SNOR_HWCAPS_READ_1_4_4,
+		BFPT_DWORD1, BIT(21),	/* Supported bit */
+		BFPT_DWORD3, 0,		/* Settings */
+		SNOR_PROTO_1_4_4,
+	},
+
+	/* Fast Read 4-4-4 */
+	{
+		SNOR_HWCAPS_READ_4_4_4,
+		BFPT_DWORD5, BIT(4),	/* Supported bit */
+		BFPT_DWORD7, 16,	/* Settings */
+		SNOR_PROTO_4_4_4,
+	},
+};
+
+
+/* Sector Erase settings. */
+
+static inline void
+spi_nor_set_erase_command_from_bfpt(struct spi_nor_erase_command *cmd,
+				    u16 half)
+{
+	u32 size = (half >> 0) & 0xff;
+	u8 opcode = (half >> 8) & 0xff;
+
+	/* size == 0 means this Erase Type is not supported. */
+	if (size)
+		size = (1u << size);
+	spi_nor_set_erase_command(cmd, size, opcode);
+}
+
+struct sfdp_bfpt_erase {
+	/*
+	 * The half-word at offset <shift> in DWORD <dword> encodes the
+	 * op code and erase sector size to be used by Sector Erase commands.
+	 */
+	enum sfdp_bfpt_dword	dword;
+	int			shift;
+};
+
+static const struct sfdp_bfpt_erase sfdp_bfpt_erases[SNOR_CMD_ERASE_MAX] = {
+	/* Erase Type 1 in DWORD8 bits[15:0] */
+	{BFPT_DWORD8, 0},
+
+	/* Erase Type 2 in DWORD8 bits[31:16] */
+	{BFPT_DWORD8, 16},
+
+	/* Erase Type 3 in DWORD9 bits[15:0] */
+	{BFPT_DWORD9, 0},
+
+	/* Erase Type 4: in DWORD9 bits[31:16] */
+	{BFPT_DWORD9, 16},
+};
+
+
+static int spi_nor_hwcaps2cmd(u32 hwcaps);
+
+static int spi_nor_parse_bfpt(struct spi_nor *nor,
+			      const struct sfdp_parameter_header *bfpt_header,
+			      struct spi_nor_flash_parameter *params)
+{
+	struct spi_nor_erase_map *map = &nor->erase_map;
+	struct sfdp_bfpt bfpt;
+	size_t len;
+	int i, cmd, err;
+	u32 addr, erase_mask;
+	u16 half;
+
+	/* JESD216 Basic Flash Parameter Table length is at least 9 DWORDs. */
+	if (bfpt_header->length < BFPT_DWORD_MAX_JESD216)
+		return -EINVAL;
+
+	/* Read the Basic Flash Parameter Table. */
+	len = min_t(size_t, sizeof(bfpt),
+		    bfpt_header->length * sizeof(uint32_t));
+	addr = SFDP_PARAM_HEADER_PTP(bfpt_header);
+	memset(&bfpt, 0, sizeof(bfpt));
+	err = spi_nor_read_sfdp(nor,  addr, len, &bfpt);
+	if (err)
+		return err;
+
+	/* Fix endianness of the BFPT DWORDs. */
+	for (i = 0; i < BFPT_DWORD_MAX; i++)
+		bfpt.dwords[i] = le32_to_cpu(bfpt.dwords[i]);
+
+	/* Flash Memory Density (in bits). */
+	params->size = bfpt.dwords[BFPT_DWORD2];
+	if (params->size & BIT(31)) {
+		params->size &= ~BIT(31);
+		params->size = 1ULL << params->size;
+	}
+	params->size >>= 3; /* Convert to bytes. */
+
+	/* Fast Read settings. */
+	for (i = 0; i < ARRAY_SIZE(sfdp_bfpt_reads); i++) {
+		const struct sfdp_bfpt_read *rd = &sfdp_bfpt_reads[i];
+		struct spi_nor_read_command *read;
+
+		if (!(bfpt.dwords[rd->supported_dword] & rd->supported_bit))
+			continue;
+
+		params->hwcaps.mask |= rd->hwcaps;
+		cmd = spi_nor_hwcaps2cmd(rd->hwcaps);
+		read = &params->reads[cmd];
+		half = bfpt.dwords[rd->settings_dword] >> rd->settings_shift;
+		spi_nor_set_read_settings_from_bfpt(read, half, rd->proto);
+	}
+
+	/* Sector Erase settings. */
+	erase_mask = 0;
+	for (i = 0; i < SNOR_CMD_ERASE_MAX; i++) {
+		const struct sfdp_bfpt_erase *er = &sfdp_bfpt_erases[i];
+
+		half = bfpt.dwords[er->dword] >> er->shift;
+		spi_nor_set_erase_command_from_bfpt(&map->commands[i], half);
+
+		if (map->commands[i].size)
+			erase_mask |= BIT(i);
+	}
+	spi_nor_init_uniform_erase_map(map, erase_mask, params->size);
+
+	/* Stop here if not JESD216 rev A or later. */
+	if (bfpt_header->length < BFPT_DWORD_MAX)
+		return 0;
+
+	/* Page size: this field specifies 'N' so the page size = 2^N bytes. */
+	params->page_size = bfpt.dwords[BFPT_DWORD11];
+	params->page_size &= BFPT_DWORD11_PAGE_SIZE_MASK;
+	params->page_size >>= BFPT_DWORD11_PAGE_SIZE_SHIFT;
+	params->page_size = (1u << params->page_size);
+
+	/* Enable Quad I/O. */
+	switch (bfpt.dwords[BFPT_DWORD15] & BFPT_DWORD15_QER_MASK) {
+	default:
+	case BFPT_DWORD15_QER_NONE:
+		break;
+
+	case BFPT_DWORD15_QER_SR2_BIT1_BUGGY:
+	case BFPT_DWORD15_QER_SR2_BIT1_NO_RD:
+		params->quad_enable = spansion_quad_enable;
+		break;
+
+	case BFPT_DWORD15_QER_SR1_BIT6:
+		params->quad_enable = macronix_quad_enable;
+		break;
+
+	case BFPT_DWORD15_QER_SR2_BIT7:
+		params->quad_enable = sr2_bit7_quad_enable;
+		break;
+
+	case BFPT_DWORD15_QER_SR2_BIT1:
+		params->quad_enable = spansion_new_quad_enable;
+		break;
+	}
+
+	return 0;
+}
+
+static int spi_nor_parse_sfdp(struct spi_nor *nor,
+			      struct spi_nor_flash_parameter *params)
+{
+	const struct sfdp_parameter_header *param_header, *bfpt_header;
+	struct sfdp_parameter_header *param_headers = NULL;
+	struct sfdp_header header;
+	size_t psize;
+	int i, err;
+
+	/* Get the SFDP header. */
+	err = spi_nor_read_sfdp(nor, 0, sizeof(header), &header);
+	if (err)
+		return err;
+
+	/* Check the SFDP header version. */
+	if (le32_to_cpu(header.signature) != SFDP_SIGNATURE ||
+	    header.major != SFDP_JESD216_MAJOR ||
+	    header.minor < SFDP_JESD216_MINOR)
+		return -EINVAL;
+
+	/*
+	 * Verify that the first and only mandatory parameter header is a
+	 * Basic Flash Parameter Table header as specified in JESD216.
+	 */
+	bfpt_header = &header.bfpt_header;
+	if (SFDP_PARAM_HEADER_ID(bfpt_header) != SFDP_BFPT_ID ||
+	    bfpt_header->major != SFDP_JESD216_MAJOR)
+		return -EINVAL;
+
+	/* Allocate memory for parameter headers. */
+	if (header.nph) {
+		psize = header.nph * sizeof(*param_headers);
+
+		param_headers = kmalloc(psize, GFP_KERNEL);
+		if (!param_headers) {
+			dev_err(nor->dev,
+				"failed to allocate memory for SFDP parameter headers\n");
+			return -ENOMEM;
+		}
+
+		err = spi_nor_read_sfdp(nor, sizeof(header),
+					psize, param_headers);
+		if (err) {
+			dev_err(nor->dev,
+				"failed to read SFDP parameter headers\n");
+			goto exit;
+		}
+	}
+
+	/*
+	 * Check other parameter headers to get the latest revision of
+	 * the basic flash parameter table.
+	 */
+	for (i = 0; i < header.nph; i++) {
+		param_header = &param_headers[i];
+
+		if (SFDP_PARAM_HEADER_ID(param_header) == SFDP_BFPT_ID &&
+		    param_header->major == SFDP_JESD216_MAJOR &&
+		    (param_header->minor > bfpt_header->minor ||
+		     (param_header->minor == bfpt_header->minor &&
+		      param_header->length > bfpt_header->length)))
+			bfpt_header = param_header;
+	}
+	err = spi_nor_parse_bfpt(nor, bfpt_header, params);
+	if (err)
+		goto exit;
+
+exit:
+	kfree(param_headers);
+	return err;
+}
+
 static int spi_nor_init_params(struct spi_nor *nor,
 			       const struct flash_info *info,
 			       struct spi_nor_flash_parameter *params)
@@ -1834,6 +2388,10 @@ static int spi_nor_init_params(struct spi_nor *nor,
 		}
 	}
 
+	/* Override the parameters with data read from SFDP tables. */
+	if (!(info->flags & SPI_NOR_SKIP_SFDP))
+		spi_nor_parse_sfdp(nor, params);
+
 	return 0;
 }
 
diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h
index c12cafe99bee..a0d21a973d60 100644
--- a/include/linux/mtd/spi-nor.h
+++ b/include/linux/mtd/spi-nor.h
@@ -41,6 +41,8 @@
 #define SPINOR_OP_WREN		0x06	/* Write enable */
 #define SPINOR_OP_RDSR		0x05	/* Read status register */
 #define SPINOR_OP_WRSR		0x01	/* Write status register 1 byte */
+#define SPINOR_OP_RDSR2		0x3f	/* Read status register 2 */
+#define SPINOR_OP_WRSR2		0x3e	/* Write status register 2 */
 #define SPINOR_OP_READ		0x03	/* Read data bytes (low frequency) */
 #define SPINOR_OP_READ_FAST	0x0b	/* Read data bytes (high frequency) */
 #define SPINOR_OP_READ_1_1_2	0x3b	/* Read data bytes (Dual Output SPI) */
@@ -56,6 +58,7 @@
 #define SPINOR_OP_CHIP_ERASE	0xc7	/* Erase whole flash chip */
 #define SPINOR_OP_SE		0xd8	/* Sector erase (usually 64KiB) */
 #define SPINOR_OP_RDID		0x9f	/* Read JEDEC ID */
+#define SPINOR_OP_RDSFDP	0x5a	/* Read SFDP */
 #define SPINOR_OP_RDCR		0x35	/* Read configuration register */
 #define SPINOR_OP_RDFSR		0x70	/* Read flag status register */
 
@@ -128,6 +131,9 @@
 /* Configuration Register bits. */
 #define CR_QUAD_EN_SPAN		BIT(1)	/* Spansion Quad I/O */
 
+/* Status Register 2 bits. */
+#define SR2_QUAD_EN_BIT7	BIT(7)
+
 
 /* Supported SPI protocols */
 #define SNOR_PROTO_WIDTH_MASK	GENMASK(7, 0)
-- 
2.9.3

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

* [RFC PATCH v5 6/6] mtd: spi-nor: parse SFDP 4-byte Address Instruction Table
  2017-03-22 23:33 [PATCH v5 0/6] mtd: spi-nor: parse SFDP tables to setup (Q)SPI memories Cyrille Pitchen
                   ` (4 preceding siblings ...)
  2017-03-22 23:33 ` [RFC PATCH v5 5/6] mtd: spi-nor: parse Serial Flash Discoverable Parameters (SFDP) tables Cyrille Pitchen
@ 2017-03-22 23:39 ` Cyrille Pitchen
  2017-04-15 15:36   ` Marek Vasut
  2017-03-29 16:45 ` [PATCH v5 0/6] mtd: spi-nor: parse SFDP tables to setup (Q)SPI memories Cyrille Pitchen
  6 siblings, 1 reply; 27+ messages in thread
From: Cyrille Pitchen @ 2017-03-22 23:39 UTC (permalink / raw)
  To: marek.vasut, linux-mtd, jartur, kdasu.kdev, mar.krzeminski
  Cc: computersforpeace, dwmw2, boris.brezillon, richard, linux-kernel,
	nicolas.ferre, Cyrille Pitchen

This patch adds supports for SFDP (JESD216B) 4-byte Address Instruction
Table. This table is optional but when available, we parse it to get the
4-byte address op codes supported by the memory.
Using these op codes is stateless as opposed to entering the 4-byte
address mode or setting the Base Address Register (BAR).

Signed-off-by: Cyrille Pitchen <cyrille.pitchen@atmel.com>
---
 drivers/mtd/spi-nor/spi-nor.c | 166 +++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 165 insertions(+), 1 deletion(-)

diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c
index ce8722055a9c..ea044efc4e6d 100644
--- a/drivers/mtd/spi-nor/spi-nor.c
+++ b/drivers/mtd/spi-nor/spi-nor.c
@@ -1899,6 +1899,7 @@ struct sfdp_parameter_header {
 
 
 #define SFDP_BFPT_ID		0xff00u	/* Basic Flash Parameter Table */
+#define SFDP_4BAIT_ID		0xff84u	/* 4-byte Address Instruction Table */
 
 #define SFDP_SIGNATURE		0x50444653u
 #define SFDP_JESD216_MAJOR	1
@@ -2241,6 +2242,149 @@ static int spi_nor_parse_bfpt(struct spi_nor *nor,
 	return 0;
 }
 
+struct sfdp_4bait {
+	/* The hardware capability. */
+	u32		hwcaps;
+
+	/*
+	 * The <supported_bit> bit in DWORD1 of the 4BAIT tells us whether
+	 * the associated 4-byte address op code is supported.
+	 */
+	u32		supported_bit;
+};
+
+static int spi_nor_parse_4bait(struct spi_nor *nor,
+			       const struct sfdp_parameter_header *param_header,
+			       struct spi_nor_flash_parameter *params)
+{
+	static const struct sfdp_4bait reads[] = {
+		{ SNOR_HWCAPS_READ,		BIT(0) },
+		{ SNOR_HWCAPS_READ_FAST,	BIT(1) },
+		{ SNOR_HWCAPS_READ_1_1_2,	BIT(2) },
+		{ SNOR_HWCAPS_READ_1_2_2,	BIT(3) },
+		{ SNOR_HWCAPS_READ_1_1_4,	BIT(4) },
+		{ SNOR_HWCAPS_READ_1_4_4,	BIT(5) },
+		{ SNOR_HWCAPS_READ_1_1_1_DTR,	BIT(13) },
+		{ SNOR_HWCAPS_READ_1_2_2_DTR,	BIT(14) },
+		{ SNOR_HWCAPS_READ_1_4_4_DTR,	BIT(15) },
+	};
+	static const struct sfdp_4bait programs[] = {
+		{ SNOR_HWCAPS_PP,		BIT(6) },
+		{ SNOR_HWCAPS_PP_1_1_4,		BIT(7) },
+		{ SNOR_HWCAPS_PP_1_4_4,		BIT(8) },
+	};
+	static const struct sfdp_4bait erases[SNOR_CMD_ERASE_MAX] = {
+		{ 0u /* not used */,		BIT(9) },
+		{ 0u /* not used */,		BIT(10) },
+		{ 0u /* not used */,		BIT(11) },
+		{ 0u /* not used */,		BIT(12) },
+	};
+	u32 dwords[2], addr, discard_hwcaps, read_hwcaps, pp_hwcaps, erase_mask;
+	struct spi_nor_erase_map *map = &nor->erase_map;
+	int i, err;
+
+	if (param_header->major != SFDP_JESD216_MAJOR ||
+	    param_header->length < ARRAY_SIZE(dwords))
+		return -EINVAL;
+
+	/* Read the 4-byte Address Instruction Table. */
+	addr = SFDP_PARAM_HEADER_PTP(param_header);
+	err = spi_nor_read_sfdp(nor, addr, sizeof(dwords), dwords);
+	if (err)
+		return err;
+
+	/* Fix endianness of the 4BAIT DWORDs. */
+	for (i = 0; i < ARRAY_SIZE(dwords); i++)
+		dwords[i] = le32_to_cpu(dwords[i]);
+
+	/*
+	 * Compute the subset of (Fast) Read commands for which the 4-byte
+	 * version is supported.
+	 */
+	discard_hwcaps = 0;
+	read_hwcaps = 0;
+	for (i = 0; i < ARRAY_SIZE(reads); i++) {
+		const struct sfdp_4bait *read = &reads[i];
+
+		discard_hwcaps |= read->hwcaps;
+		if ((params->hwcaps.mask & read->hwcaps) &&
+		    (dwords[0] & read->supported_bit))
+			read_hwcaps |= read->hwcaps;
+	}
+
+	/*
+	 * Compute the subset of Page Program commands for which the 4-byte
+	 * version is supported.
+	 */
+	pp_hwcaps = 0;
+	for (i = 0; i < ARRAY_SIZE(programs); i++) {
+		const struct sfdp_4bait *program = &programs[i];
+
+		discard_hwcaps |= program->hwcaps;
+		if ((params->hwcaps.mask & program->hwcaps) &&
+		    (dwords[0] & program->supported_bit))
+			pp_hwcaps |= program->hwcaps;
+	}
+
+	/*
+	 * Compute the subet of Sector Erase commands for which the 4-byte
+	 * version is supported.
+	 */
+	erase_mask = 0;
+	for (i = 0; i < SNOR_CMD_ERASE_MAX; i++) {
+		const struct sfdp_4bait *erase = &erases[i];
+
+		if ((map->commands[i].size > 0) &&
+		    (dwords[0] & erase->supported_bit))
+			erase_mask |= BIT(i);
+	}
+
+	/*
+	 * We need at least one 4-byte op code per read, program and erase
+	 * operation; the .read(), .write() and .erase() hooks share the
+	 * nor->addr_width value.
+	 */
+	if (!read_hwcaps || !pp_hwcaps || !erase_mask)
+		return 0;
+
+	/*
+	 * Discard all operations from the 4-byte instruction set which are
+	 * not supported by this memory.
+	 */
+	params->hwcaps.mask &= ~discard_hwcaps;
+	params->hwcaps.mask |= (read_hwcaps | pp_hwcaps);
+
+	/* Use the 4-byte address instruction set. */
+	for (i = 0; i < SNOR_CMD_READ_MAX; i++) {
+		struct spi_nor_read_command *read_cmd = &params->reads[i];
+
+		read_cmd->opcode = spi_nor_convert_3to4_read(read_cmd->opcode);
+	}
+	for (i = 0; i < SNOR_CMD_PP_MAX; i++) {
+		struct spi_nor_pp_command *pp_cmd = &params->page_programs[i];
+
+		pp_cmd->opcode = spi_nor_convert_3to4_program(pp_cmd->opcode);
+	}
+	for (i = 0; i < SNOR_CMD_ERASE_MAX; i++) {
+		struct spi_nor_erase_command *erase_cmd = &map->commands[i];
+
+		if (erase_mask & BIT(i))
+			erase_cmd->opcode = (dwords[1] >> (i * 8)) & 0xFF;
+		else
+			spi_nor_set_erase_command(erase_cmd, 0u, 0xFF);
+	}
+
+	/*
+	 * We set nor->addr_width here to skip spi_nor_set_4byte_opcodes()
+	 * later because this latest function implements a legacy quirk for
+	 * the erase size of Spansion memory. However this quirk is no longer
+	 * needed with new SFDP compliant memories.
+	 */
+	nor->addr_width = 4;
+	nor->flags |= SNOR_F_4B_OPCODES;
+	return 0;
+}
+
 static int spi_nor_parse_sfdp(struct spi_nor *nor,
 			      struct spi_nor_flash_parameter *params)
 {
@@ -2308,6 +2452,23 @@ static int spi_nor_parse_sfdp(struct spi_nor *nor,
 	if (err)
 		goto exit;
 
+	/* Parse other parameter headers. */
+	for (i = 0; i < header.nph; i++) {
+		param_header = &param_headers[i];
+
+		switch (SFDP_PARAM_HEADER_ID(param_header)) {
+		case SFDP_4BAIT_ID:
+			err = spi_nor_parse_4bait(nor, param_header, params);
+			break;
+
+		default:
+			break;
+		}
+
+		if (err)
+			goto exit;
+	}
+
 exit:
 	kfree(param_headers);
 	return err;
@@ -2885,7 +3046,10 @@ int spi_nor_scan(struct spi_nor *nor, const char *name,
 	if (ret)
 		return ret;
 
-	if (info->addr_width)
+	if (nor->addr_width)
+		/* already configured by spi_nor_setup() */
+		;
+	else if (info->addr_width)
 		nor->addr_width = info->addr_width;
 	else if (mtd->size > 0x1000000) {
 		/* enable 4-byte addressing if the device exceeds 16MiB */
-- 
2.9.3

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

* Re: [PATCH v5 1/6] mtd: spi-nor: introduce more SPI protocols and the Dual Transfer Mode
  2017-03-22 23:33 ` [PATCH v5 1/6] mtd: spi-nor: introduce more SPI protocols and the Dual Transfer Mode Cyrille Pitchen
@ 2017-03-23 15:13   ` Cédric Le Goater
  2017-03-23 19:10     ` Cyrille Pitchen
  2017-04-02 17:05   ` Cyrille Pitchen
  2017-04-06 23:30   ` Marek Vasut
  2 siblings, 1 reply; 27+ messages in thread
From: Cédric Le Goater @ 2017-03-23 15:13 UTC (permalink / raw)
  To: Cyrille Pitchen, marek.vasut, linux-mtd, jartur, kdasu.kdev,
	mar.krzeminski
  Cc: boris.brezillon, richard, nicolas.ferre, linux-kernel,
	computersforpeace, dwmw2

On 03/23/2017 12:33 AM, Cyrille Pitchen wrote:
> This patch changes the prototype of spi_nor_scan(): its 3rd parameter
> is replaced by a 'struct spi_nor_hwcaps' pointer, which tells the spi-nor
> framework about the actual hardware capabilities supported by the SPI
> controller and its driver.
> 
> Besides, this patch also introduces a new 'struct spi_nor_flash_parameter'
> telling the spi-nor framework about the hardware capabilities supported by
> the SPI flash memory and the associated settings required to use those
> hardware caps.
>
> Currently the 'struct spi_nor_flash_parameter' is filled with legacy
> values but a later patch will allow to fill it dynamically by reading the
> JESD216 Serial Flash Discoverable Parameter (SFDP) tables from the SPI
> memory.
> 
> With both structures, the spi-nor framework can now compute the best
> match between hardware caps supported by both the (Q)SPI memory and
> controller hence selecting the relevant SPI protocols and op codes for
> (Fast) Read, Page Program and Sector Erase operations.
> 
> The 'struct spi_nor_flash_parameter' also provides the spi-nor framework
> with the number of dummy cycles to be used with each Fast Read commands
> and the erase block size associated to the erase block op codes.
> 
> Finally the 'struct spi_nor_flash_parameter', through the optional
> .enable_quad_io() hook, tells the spi-nor framework how to set the Quad
> Enable (QE) bit of the QSPI memory to enable its Quad SPI features.

The Aspeed controller only supports Dual I/O and a helper similar 
to the one for Quad I/O would be needed to setup the chip for 
multiple I/O: 

	int (*dual_enable)(struct spi_nor *nor);

Is the approach correct ? or maybe rename the current 'quad_enable()'
to 'multiple_enable()' and add an extra parameter.

I have a bunch of patches queued for Dual I/O data support but I 
think they will conflict with your patches. I will wait for this 
one to be merged. Then, I can look at Dual I/O address + data 
support.

Thanks,

C.     


> Signed-off-by: Cyrille Pitchen <cyrille.pitchen@atmel.com>
> ---
>  drivers/mtd/devices/m25p80.c          |  16 +-
>  drivers/mtd/spi-nor/aspeed-smc.c      |  23 +-
>  drivers/mtd/spi-nor/atmel-quadspi.c   |  80 +++---
>  drivers/mtd/spi-nor/cadence-quadspi.c |  18 +-
>  drivers/mtd/spi-nor/fsl-quadspi.c     |   8 +-
>  drivers/mtd/spi-nor/hisi-sfc.c        |  31 ++-
>  drivers/mtd/spi-nor/intel-spi.c       |   7 +-
>  drivers/mtd/spi-nor/mtk-quadspi.c     |  16 +-
>  drivers/mtd/spi-nor/nxp-spifi.c       |  22 +-
>  drivers/mtd/spi-nor/spi-nor.c         | 441 +++++++++++++++++++++++++++-------
>  include/linux/mtd/spi-nor.h           | 158 +++++++++++-
>  11 files changed, 643 insertions(+), 177 deletions(-)
> 
> diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c
> index c4df3b1bded0..68986a26c8fe 100644
> --- a/drivers/mtd/devices/m25p80.c
> +++ b/drivers/mtd/devices/m25p80.c
> @@ -111,10 +111,10 @@ static ssize_t m25p80_write(struct spi_nor *nor, loff_t to, size_t len,
>  
>  static inline unsigned int m25p80_rx_nbits(struct spi_nor *nor)
>  {
> -	switch (nor->flash_read) {
> -	case SPI_NOR_DUAL:
> +	switch (nor->read_proto) {
> +	case SNOR_PROTO_1_1_2:
>  		return 2;
> -	case SPI_NOR_QUAD:
> +	case SNOR_PROTO_1_1_4:
>  		return 4;
>  	default:
>  		return 0;
> @@ -196,7 +196,9 @@ static int m25p_probe(struct spi_device *spi)
>  	struct flash_platform_data	*data;
>  	struct m25p *flash;
>  	struct spi_nor *nor;
> -	enum read_mode mode = SPI_NOR_NORMAL;
> +	struct spi_nor_hwcaps hwcaps = {
> +		.mask = (SNOR_HWCAPS_READ | SNOR_HWCAPS_PP),
> +	};
>  	char *flash_name;
>  	int ret;
>  
> @@ -222,9 +224,9 @@ static int m25p_probe(struct spi_device *spi)
>  	flash->spi = spi;
>  
>  	if (spi->mode & SPI_RX_QUAD)
> -		mode = SPI_NOR_QUAD;
> +		hwcaps.mask |= SNOR_HWCAPS_READ_1_1_4;
>  	else if (spi->mode & SPI_RX_DUAL)
> -		mode = SPI_NOR_DUAL;
> +		hwcaps.mask |= SNOR_HWCAPS_READ_1_1_2;
>  
>  	if (data && data->name)
>  		nor->mtd.name = data->name;
> @@ -241,7 +243,7 @@ static int m25p_probe(struct spi_device *spi)
>  	else
>  		flash_name = spi->modalias;
>  
> -	ret = spi_nor_scan(nor, flash_name, mode);
> +	ret = spi_nor_scan(nor, flash_name, &hwcaps);
>  	if (ret)
>  		return ret;
>  
> diff --git a/drivers/mtd/spi-nor/aspeed-smc.c b/drivers/mtd/spi-nor/aspeed-smc.c
> index 56051d30f000..723026d9cf0c 100644
> --- a/drivers/mtd/spi-nor/aspeed-smc.c
> +++ b/drivers/mtd/spi-nor/aspeed-smc.c
> @@ -585,14 +585,12 @@ static int aspeed_smc_chip_setup_finish(struct aspeed_smc_chip *chip)
>  	 * TODO: Adjust clocks if fast read is supported and interpret
>  	 * SPI-NOR flags to adjust controller settings.
>  	 */
> -	switch (chip->nor.flash_read) {
> -	case SPI_NOR_NORMAL:
> -		cmd = CONTROL_COMMAND_MODE_NORMAL;
> -		break;
> -	case SPI_NOR_FAST:
> -		cmd = CONTROL_COMMAND_MODE_FREAD;
> -		break;
> -	default:
> +	if (chip->nor.read_proto == SNOR_PROTO_1_1_1) {
> +		if (chip->nor.read_dummy == 0)
> +			cmd = CONTROL_COMMAND_MODE_NORMAL;
> +		else
> +			cmd = CONTROL_COMMAND_MODE_FREAD;
> +	} else {
>  		dev_err(chip->nor.dev, "unsupported SPI read mode\n");
>  		return -EINVAL;
>  	}
> @@ -608,6 +606,11 @@ static int aspeed_smc_chip_setup_finish(struct aspeed_smc_chip *chip)
>  static int aspeed_smc_setup_flash(struct aspeed_smc_controller *controller,
>  				  struct device_node *np, struct resource *r)
>  {
> +	struct spi_nor_hwcaps hwcaps = {
> +		.mask = (SNOR_HWCAPS_READ |
> +			 SNOR_HWCAPS_READ_FAST |
> +			 SNOR_HWCAPS_PP),
> +	};
>  	const struct aspeed_smc_info *info = controller->info;
>  	struct device *dev = controller->dev;
>  	struct device_node *child;
> @@ -671,11 +674,11 @@ static int aspeed_smc_setup_flash(struct aspeed_smc_controller *controller,
>  			break;
>  
>  		/*
> -		 * TODO: Add support for SPI_NOR_QUAD and SPI_NOR_DUAL
> +		 * TODO: Add support for Dual and Quad SPI protocols
>
>  		 * attach when board support is present as determined
>  		 * by of property.
>  		 */
> -		ret = spi_nor_scan(nor, NULL, SPI_NOR_NORMAL);
> +		ret = spi_nor_scan(nor, NULL, &hwcaps);
>  		if (ret)
>  			break;
>  
> diff --git a/drivers/mtd/spi-nor/atmel-quadspi.c b/drivers/mtd/spi-nor/atmel-quadspi.c
> index 47937d9beec6..9f579f7c1733 100644
> --- a/drivers/mtd/spi-nor/atmel-quadspi.c
> +++ b/drivers/mtd/spi-nor/atmel-quadspi.c
> @@ -275,14 +275,48 @@ static void atmel_qspi_debug_command(struct atmel_qspi *aq,
>  
>  static int atmel_qspi_run_command(struct atmel_qspi *aq,
>  				  const struct atmel_qspi_command *cmd,
> -				  u32 ifr_tfrtyp, u32 ifr_width)
> +				  u32 ifr_tfrtyp, enum spi_nor_protocol proto)
>  {
>  	u32 iar, icr, ifr, sr;
>  	int err = 0;
>  
>  	iar = 0;
>  	icr = 0;
> -	ifr = ifr_tfrtyp | ifr_width;
> +	ifr = ifr_tfrtyp;
> +
> +	/* Set the SPI protocol */
> +	switch (proto) {
> +	case SNOR_PROTO_1_1_1:
> +		ifr |= QSPI_IFR_WIDTH_SINGLE_BIT_SPI;
> +		break;
> +
> +	case SNOR_PROTO_1_1_2:
> +		ifr |= QSPI_IFR_WIDTH_DUAL_OUTPUT;
> +		break;
> +
> +	case SNOR_PROTO_1_1_4:
> +		ifr |= QSPI_IFR_WIDTH_QUAD_OUTPUT;
> +		break;
> +
> +	case SNOR_PROTO_1_2_2:
> +		ifr |= QSPI_IFR_WIDTH_DUAL_IO;
> +		break;
> +
> +	case SNOR_PROTO_1_4_4:
> +		ifr |= QSPI_IFR_WIDTH_QUAD_IO;
> +		break;
> +
> +	case SNOR_PROTO_2_2_2:
> +		ifr |= QSPI_IFR_WIDTH_DUAL_CMD;
> +		break;
> +
> +	case SNOR_PROTO_4_4_4:
> +		ifr |= QSPI_IFR_WIDTH_QUAD_CMD;
> +		break;
> +
> +	default:
> +		return -EINVAL;
> +	}
>  
>  	/* Compute instruction parameters */
>  	if (cmd->enable.bits.instruction) {
> @@ -434,7 +468,7 @@ static int atmel_qspi_read_reg(struct spi_nor *nor, u8 opcode,
>  	cmd.rx_buf = buf;
>  	cmd.buf_len = len;
>  	return atmel_qspi_run_command(aq, &cmd, QSPI_IFR_TFRTYP_TRSFR_READ,
> -				      QSPI_IFR_WIDTH_SINGLE_BIT_SPI);
> +				      nor->reg_proto);
>  }
>  
>  static int atmel_qspi_write_reg(struct spi_nor *nor, u8 opcode,
> @@ -450,7 +484,7 @@ static int atmel_qspi_write_reg(struct spi_nor *nor, u8 opcode,
>  	cmd.tx_buf = buf;
>  	cmd.buf_len = len;
>  	return atmel_qspi_run_command(aq, &cmd, QSPI_IFR_TFRTYP_TRSFR_WRITE,
> -				      QSPI_IFR_WIDTH_SINGLE_BIT_SPI);
> +				      nor->reg_proto);
>  }
>  
>  static ssize_t atmel_qspi_write(struct spi_nor *nor, loff_t to, size_t len,
> @@ -469,7 +503,7 @@ static ssize_t atmel_qspi_write(struct spi_nor *nor, loff_t to, size_t len,
>  	cmd.tx_buf = write_buf;
>  	cmd.buf_len = len;
>  	ret = atmel_qspi_run_command(aq, &cmd, QSPI_IFR_TFRTYP_TRSFR_WRITE_MEM,
> -				     QSPI_IFR_WIDTH_SINGLE_BIT_SPI);
> +				     nor->write_proto);
>  	return (ret < 0) ? ret : len;
>  }
>  
> @@ -484,7 +518,7 @@ static int atmel_qspi_erase(struct spi_nor *nor, loff_t offs)
>  	cmd.instruction = nor->erase_opcode;
>  	cmd.address = (u32)offs;
>  	return atmel_qspi_run_command(aq, &cmd, QSPI_IFR_TFRTYP_TRSFR_WRITE,
> -				      QSPI_IFR_WIDTH_SINGLE_BIT_SPI);
> +				      nor->reg_proto);
>  }
>  
>  static ssize_t atmel_qspi_read(struct spi_nor *nor, loff_t from, size_t len,
> @@ -493,27 +527,8 @@ static ssize_t atmel_qspi_read(struct spi_nor *nor, loff_t from, size_t len,
>  	struct atmel_qspi *aq = nor->priv;
>  	struct atmel_qspi_command cmd;
>  	u8 num_mode_cycles, num_dummy_cycles;
> -	u32 ifr_width;
>  	ssize_t ret;
>  
> -	switch (nor->flash_read) {
> -	case SPI_NOR_NORMAL:
> -	case SPI_NOR_FAST:
> -		ifr_width = QSPI_IFR_WIDTH_SINGLE_BIT_SPI;
> -		break;
> -
> -	case SPI_NOR_DUAL:
> -		ifr_width = QSPI_IFR_WIDTH_DUAL_OUTPUT;
> -		break;
> -
> -	case SPI_NOR_QUAD:
> -		ifr_width = QSPI_IFR_WIDTH_QUAD_OUTPUT;
> -		break;
> -
> -	default:
> -		return -EINVAL;
> -	}
> -
>  	if (nor->read_dummy >= 2) {
>  		num_mode_cycles = 2;
>  		num_dummy_cycles = nor->read_dummy - 2;
> @@ -536,7 +551,7 @@ static ssize_t atmel_qspi_read(struct spi_nor *nor, loff_t from, size_t len,
>  	cmd.rx_buf = read_buf;
>  	cmd.buf_len = len;
>  	ret = atmel_qspi_run_command(aq, &cmd, QSPI_IFR_TFRTYP_TRSFR_READ_MEM,
> -				     ifr_width);
> +				     nor->read_proto);
>  	return (ret < 0) ? ret : len;
>  }
>  
> @@ -590,6 +605,17 @@ static irqreturn_t atmel_qspi_interrupt(int irq, void *dev_id)
>  
>  static int atmel_qspi_probe(struct platform_device *pdev)
>  {
> +	struct spi_nor_hwcaps hwcaps = {
> +		.mask = (SNOR_HWCAPS_READ |
> +			 SNOR_HWCAPS_READ_FAST |
> +			 SNOR_HWCAPS_READ_1_1_2 |
> +			 SNOR_HWCAPS_READ_1_2_2 |
> +			 SNOR_HWCAPS_READ_1_1_4 |
> +			 SNOR_HWCAPS_READ_1_4_4 |
> +			 SNOR_HWCAPS_PP |
> +			 SNOR_HWCAPS_PP_1_1_4 |
> +			 SNOR_HWCAPS_PP_1_4_4),
> +	};
>  	struct device_node *child, *np = pdev->dev.of_node;
>  	struct atmel_qspi *aq;
>  	struct resource *res;
> @@ -679,7 +705,7 @@ static int atmel_qspi_probe(struct platform_device *pdev)
>  	if (err)
>  		goto disable_clk;
>  
> -	err = spi_nor_scan(nor, NULL, SPI_NOR_QUAD);
> +	err = spi_nor_scan(nor, NULL, &hwcaps);
>  	if (err)
>  		goto disable_clk;
>  
> diff --git a/drivers/mtd/spi-nor/cadence-quadspi.c b/drivers/mtd/spi-nor/cadence-quadspi.c
> index 9f8102de1b16..3f91a3e97892 100644
> --- a/drivers/mtd/spi-nor/cadence-quadspi.c
> +++ b/drivers/mtd/spi-nor/cadence-quadspi.c
> @@ -855,15 +855,14 @@ static int cqspi_set_protocol(struct spi_nor *nor, const int read)
>  	f_pdata->data_width = CQSPI_INST_TYPE_SINGLE;
>  
>  	if (read) {
> -		switch (nor->flash_read) {
> -		case SPI_NOR_NORMAL:
> -		case SPI_NOR_FAST:
> +		switch (nor->read_proto) {
> +		case SNOR_PROTO_1_1_1:
>  			f_pdata->data_width = CQSPI_INST_TYPE_SINGLE;
>  			break;
> -		case SPI_NOR_DUAL:
> +		case SNOR_PROTO_1_1_2:
>  			f_pdata->data_width = CQSPI_INST_TYPE_DUAL;
>  			break;
> -		case SPI_NOR_QUAD:
> +		case SNOR_PROTO_1_1_4:
>  			f_pdata->data_width = CQSPI_INST_TYPE_QUAD;
>  			break;
>  		default:
> @@ -1069,6 +1068,13 @@ static void cqspi_controller_init(struct cqspi_st *cqspi)
>  
>  static int cqspi_setup_flash(struct cqspi_st *cqspi, struct device_node *np)
>  {
> +	struct spi_nor_hwcaps hwcaps = {
> +		.mask = (SNOR_HWCAPS_READ |
> +			 SNOR_HWCAPS_READ_FAST |
> +			 SNOR_HWCAPS_READ_1_1_2 |
> +			 SNOR_HWCAPS_READ_1_1_4 |
> +			 SNOR_HWCAPS_PP),
> +	};
>  	struct platform_device *pdev = cqspi->pdev;
>  	struct device *dev = &pdev->dev;
>  	struct cqspi_flash_pdata *f_pdata;
> @@ -1123,7 +1129,7 @@ static int cqspi_setup_flash(struct cqspi_st *cqspi, struct device_node *np)
>  			goto err;
>  		}
>  
> -		ret = spi_nor_scan(nor, NULL, SPI_NOR_QUAD);
> +		ret = spi_nor_scan(nor, NULL, &hwcaps);
>  		if (ret)
>  			goto err;
>  
> diff --git a/drivers/mtd/spi-nor/fsl-quadspi.c b/drivers/mtd/spi-nor/fsl-quadspi.c
> index 1476135e0d50..ec9c8e960fd2 100644
> --- a/drivers/mtd/spi-nor/fsl-quadspi.c
> +++ b/drivers/mtd/spi-nor/fsl-quadspi.c
> @@ -957,6 +957,12 @@ static void fsl_qspi_unprep(struct spi_nor *nor, enum spi_nor_ops ops)
>  
>  static int fsl_qspi_probe(struct platform_device *pdev)
>  {
> +	struct spi_nor_hwcaps hwcaps = {
> +		.mask = (SNOR_HWCAPS_READ |
> +			 SNOR_HWCAPS_READ_FAST |
> +			 SNOR_HWCAPS_READ_1_1_4 |
> +			 SNOR_HWCAPS_PP),
> +	};
>  	struct device_node *np = pdev->dev.of_node;
>  	struct device *dev = &pdev->dev;
>  	struct fsl_qspi *q;
> @@ -1065,7 +1071,7 @@ static int fsl_qspi_probe(struct platform_device *pdev)
>  		/* set the chip address for READID */
>  		fsl_qspi_set_base_addr(q, nor);
>  
> -		ret = spi_nor_scan(nor, NULL, SPI_NOR_QUAD);
> +		ret = spi_nor_scan(nor, NULL, &hwcaps);
>  		if (ret)
>  			goto mutex_failed;
>  
> diff --git a/drivers/mtd/spi-nor/hisi-sfc.c b/drivers/mtd/spi-nor/hisi-sfc.c
> index a286350627a6..80e2d173abdd 100644
> --- a/drivers/mtd/spi-nor/hisi-sfc.c
> +++ b/drivers/mtd/spi-nor/hisi-sfc.c
> @@ -120,19 +120,24 @@ static inline int wait_op_finish(struct hifmc_host *host)
>  		(reg & FMC_INT_OP_DONE), 0, FMC_WAIT_TIMEOUT);
>  }
>  
> -static int get_if_type(enum read_mode flash_read)
> +static int get_if_type(enum spi_nor_protocol proto)
>  {
>  	enum hifmc_iftype if_type;
>  
> -	switch (flash_read) {
> -	case SPI_NOR_DUAL:
> +	switch (proto) {
> +	case SNOR_PROTO_1_1_2:
>  		if_type = IF_TYPE_DUAL;
>  		break;
> -	case SPI_NOR_QUAD:
> +	case SNOR_PROTO_1_2_2:
> +		if_type = IF_TYPE_DIO;
> +		break;
> +	case SNOR_PROTO_1_1_4:
>  		if_type = IF_TYPE_QUAD;
>  		break;
> -	case SPI_NOR_NORMAL:
> -	case SPI_NOR_FAST:
> +	case SNOR_PROTO_1_4_4:
> +		if_type = IF_TYPE_QIO;
> +		break;
> +	case SNOR_PROTO_1_1_1:
>  	default:
>  		if_type = IF_TYPE_STD;
>  		break;
> @@ -253,7 +258,10 @@ static int hisi_spi_nor_dma_transfer(struct spi_nor *nor, loff_t start_off,
>  	writel(FMC_DMA_LEN_SET(len), host->regbase + FMC_DMA_LEN);
>  
>  	reg = OP_CFG_FM_CS(priv->chipselect);
> -	if_type = get_if_type(nor->flash_read);
> +	if (op_type == FMC_OP_READ)
> +		if_type = get_if_type(nor->read_proto);
> +	else
> +		if_type = get_if_type(nor->write_proto);
>  	reg |= OP_CFG_MEM_IF_TYPE(if_type);
>  	if (op_type == FMC_OP_READ)
>  		reg |= OP_CFG_DUMMY_NUM(nor->read_dummy >> 3);
> @@ -321,6 +329,13 @@ static ssize_t hisi_spi_nor_write(struct spi_nor *nor, loff_t to,
>  static int hisi_spi_nor_register(struct device_node *np,
>  				struct hifmc_host *host)
>  {
> +	struct spi_nor_hwcaps hwcaps = {
> +		.mask = (SNOR_HWCAPS_READ |
> +			 SNOR_HWCAPS_READ_FAST |
> +			 SNOR_HWCAPS_READ_1_1_2 |
> +			 SNOR_HWCAPS_READ_1_1_4 |
> +			 SNOR_HWCAPS_PP),
> +	};
>  	struct device *dev = host->dev;
>  	struct spi_nor *nor;
>  	struct hifmc_priv *priv;
> @@ -362,7 +377,7 @@ static int hisi_spi_nor_register(struct device_node *np,
>  	nor->read = hisi_spi_nor_read;
>  	nor->write = hisi_spi_nor_write;
>  	nor->erase = NULL;
> -	ret = spi_nor_scan(nor, NULL, SPI_NOR_QUAD);
> +	ret = spi_nor_scan(nor, NULL, &hwcaps);
>  	if (ret)
>  		return ret;
>  
> diff --git a/drivers/mtd/spi-nor/intel-spi.c b/drivers/mtd/spi-nor/intel-spi.c
> index 986a3d020a3a..515aa1f7f4f1 100644
> --- a/drivers/mtd/spi-nor/intel-spi.c
> +++ b/drivers/mtd/spi-nor/intel-spi.c
> @@ -715,6 +715,11 @@ static void intel_spi_fill_partition(struct intel_spi *ispi,
>  struct intel_spi *intel_spi_probe(struct device *dev,
>  	struct resource *mem, const struct intel_spi_boardinfo *info)
>  {
> +	struct spi_nor_hwcaps hwcaps = {
> +		.mask = (SNOR_HWCAPS_READ |
> +			 SNOR_HWCAPS_READ_FAST |
> +			 SNOR_HWCAPS_PP),
> +	};
>  	struct mtd_partition part;
>  	struct intel_spi *ispi;
>  	int ret;
> @@ -746,7 +751,7 @@ struct intel_spi *intel_spi_probe(struct device *dev,
>  	ispi->nor.write = intel_spi_write;
>  	ispi->nor.erase = intel_spi_erase;
>  
> -	ret = spi_nor_scan(&ispi->nor, NULL, SPI_NOR_NORMAL);
> +	ret = spi_nor_scan(&ispi->nor, NULL, &hwcaps);
>  	if (ret) {
>  		dev_info(dev, "failed to locate the chip\n");
>  		return ERR_PTR(ret);
> diff --git a/drivers/mtd/spi-nor/mtk-quadspi.c b/drivers/mtd/spi-nor/mtk-quadspi.c
> index e661877c23de..615e258866f7 100644
> --- a/drivers/mtd/spi-nor/mtk-quadspi.c
> +++ b/drivers/mtd/spi-nor/mtk-quadspi.c
> @@ -121,20 +121,20 @@ static void mt8173_nor_set_read_mode(struct mt8173_nor *mt8173_nor)
>  {
>  	struct spi_nor *nor = &mt8173_nor->nor;
>  
> -	switch (nor->flash_read) {
> -	case SPI_NOR_FAST:
> +	switch (nor->read_proto) {
> +	case SNOR_PROTO_1_1_1:
>  		writeb(nor->read_opcode, mt8173_nor->base +
>  		       MTK_NOR_PRGDATA3_REG);
>  		writeb(MTK_NOR_FAST_READ, mt8173_nor->base +
>  		       MTK_NOR_CFG1_REG);
>  		break;
> -	case SPI_NOR_DUAL:
> +	case SNOR_PROTO_1_1_2:
>  		writeb(nor->read_opcode, mt8173_nor->base +
>  		       MTK_NOR_PRGDATA3_REG);
>  		writeb(MTK_NOR_DUAL_READ_EN, mt8173_nor->base +
>  		       MTK_NOR_DUAL_REG);
>  		break;
> -	case SPI_NOR_QUAD:
> +	case SNOR_PROTO_1_1_4:
>  		writeb(nor->read_opcode, mt8173_nor->base +
>  		       MTK_NOR_PRGDATA4_REG);
>  		writeb(MTK_NOR_QUAD_READ_EN, mt8173_nor->base +
> @@ -381,6 +381,12 @@ static int mt8173_nor_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf,
>  static int mtk_nor_init(struct mt8173_nor *mt8173_nor,
>  			struct device_node *flash_node)
>  {
> +	struct spi_nor_hwcaps hwcaps = {
> +		.mask = (SNOR_HWCAPS_READ |
> +			 SNOR_HWCAPS_READ_FAST |
> +			 SNOR_HWCAPS_READ_1_1_2 |
> +			 SNOR_HWCAPS_PP),
> +	};
>  	int ret;
>  	struct spi_nor *nor;
>  
> @@ -399,7 +405,7 @@ static int mtk_nor_init(struct mt8173_nor *mt8173_nor,
>  	nor->write_reg = mt8173_nor_write_reg;
>  	nor->mtd.name = "mtk_nor";
>  	/* initialized with NULL */
> -	ret = spi_nor_scan(nor, NULL, SPI_NOR_DUAL);
> +	ret = spi_nor_scan(nor, NULL, &hwcaps);
>  	if (ret)
>  		return ret;
>  
> diff --git a/drivers/mtd/spi-nor/nxp-spifi.c b/drivers/mtd/spi-nor/nxp-spifi.c
> index 73a14f40928b..c5992e099542 100644
> --- a/drivers/mtd/spi-nor/nxp-spifi.c
> +++ b/drivers/mtd/spi-nor/nxp-spifi.c
> @@ -240,13 +240,12 @@ static int nxp_spifi_erase(struct spi_nor *nor, loff_t offs)
>  
>  static int nxp_spifi_setup_memory_cmd(struct nxp_spifi *spifi)
>  {
> -	switch (spifi->nor.flash_read) {
> -	case SPI_NOR_NORMAL:
> -	case SPI_NOR_FAST:
> +	switch (spifi->nor.read_proto) {
> +	case SNOR_PROTO_1_1_1:
>  		spifi->mcmd = SPIFI_CMD_FIELDFORM_ALL_SERIAL;
>  		break;
> -	case SPI_NOR_DUAL:
> -	case SPI_NOR_QUAD:
> +	case SNOR_PROTO_1_1_2:
> +	case SNOR_PROTO_1_1_4:
>  		spifi->mcmd = SPIFI_CMD_FIELDFORM_QUAD_DUAL_DATA;
>  		break;
>  	default:
> @@ -274,7 +273,11 @@ static void nxp_spifi_dummy_id_read(struct spi_nor *nor)
>  static int nxp_spifi_setup_flash(struct nxp_spifi *spifi,
>  				 struct device_node *np)
>  {
> -	enum read_mode flash_read;
> +	struct spi_nor_hwcaps hwcaps = {
> +		.mask = (SNOR_HWCAPS_READ |
> +			 SNOR_HWCAPS_READ_FAST |
> +			 SNOR_HWCAPS_PP),
> +	};
>  	u32 ctrl, property;
>  	u16 mode = 0;
>  	int ret;
> @@ -308,13 +311,12 @@ static int nxp_spifi_setup_flash(struct nxp_spifi *spifi,
>  
>  	if (mode & SPI_RX_DUAL) {
>  		ctrl |= SPIFI_CTRL_DUAL;
> -		flash_read = SPI_NOR_DUAL;
> +		hwcaps.mask |= SNOR_HWCAPS_READ_1_1_2;
>  	} else if (mode & SPI_RX_QUAD) {
>  		ctrl &= ~SPIFI_CTRL_DUAL;
> -		flash_read = SPI_NOR_QUAD;
> +		hwcaps.mask |= SNOR_HWCAPS_READ_1_1_4;
>  	} else {
>  		ctrl |= SPIFI_CTRL_DUAL;
> -		flash_read = SPI_NOR_NORMAL;
>  	}
>  
>  	switch (mode & (SPI_CPHA | SPI_CPOL)) {
> @@ -351,7 +353,7 @@ static int nxp_spifi_setup_flash(struct nxp_spifi *spifi,
>  	 */
>  	nxp_spifi_dummy_id_read(&spifi->nor);
>  
> -	ret = spi_nor_scan(&spifi->nor, NULL, flash_read);
> +	ret = spi_nor_scan(&spifi->nor, NULL, &hwcaps);
>  	if (ret) {
>  		dev_err(spifi->dev, "device scan failed\n");
>  		return ret;
> diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c
> index d3cb44b28490..cc443c6cbae8 100644
> --- a/drivers/mtd/spi-nor/spi-nor.c
> +++ b/drivers/mtd/spi-nor/spi-nor.c
> @@ -150,24 +150,6 @@ static int read_cr(struct spi_nor *nor)
>  }
>  
>  /*
> - * Dummy Cycle calculation for different type of read.
> - * It can be used to support more commands with
> - * different dummy cycle requirements.
> - */
> -static inline int spi_nor_read_dummy_cycles(struct spi_nor *nor)
> -{
> -	switch (nor->flash_read) {
> -	case SPI_NOR_FAST:
> -	case SPI_NOR_DUAL:
> -	case SPI_NOR_QUAD:
> -		return 8;
> -	case SPI_NOR_NORMAL:
> -		return 0;
> -	}
> -	return 0;
> -}
> -
> -/*
>   * Write status register 1 byte
>   * Returns negative if error occurred.
>   */
> @@ -221,6 +203,10 @@ static inline u8 spi_nor_convert_3to4_read(u8 opcode)
>  		{ SPINOR_OP_READ_1_2_2,	SPINOR_OP_READ_1_2_2_4B },
>  		{ SPINOR_OP_READ_1_1_4,	SPINOR_OP_READ_1_1_4_4B },
>  		{ SPINOR_OP_READ_1_4_4,	SPINOR_OP_READ_1_4_4_4B },
> +
> +		{ SPINOR_OP_READ_1_1_1_DTR,	SPINOR_OP_READ_1_1_1_DTR_4B },
> +		{ SPINOR_OP_READ_1_2_2_DTR,	SPINOR_OP_READ_1_2_2_DTR_4B },
> +		{ SPINOR_OP_READ_1_4_4_DTR,	SPINOR_OP_READ_1_4_4_DTR_4B },
>  	};
>  
>  	return spi_nor_convert_opcode(opcode, spi_nor_3to4_read,
> @@ -1459,30 +1445,6 @@ static int spansion_quad_enable(struct spi_nor *nor)
>  	return 0;
>  }
>  
> -static int set_quad_mode(struct spi_nor *nor, const struct flash_info *info)
> -{
> -	int status;
> -
> -	switch (JEDEC_MFR(info)) {
> -	case SNOR_MFR_MACRONIX:
> -		status = macronix_quad_enable(nor);
> -		if (status) {
> -			dev_err(nor->dev, "Macronix quad-read not enabled\n");
> -			return -EINVAL;
> -		}
> -		return status;
> -	case SNOR_MFR_MICRON:
> -		return 0;
> -	default:
> -		status = spansion_quad_enable(nor);
> -		if (status) {
> -			dev_err(nor->dev, "Spansion quad-read not enabled\n");
> -			return -EINVAL;
> -		}
> -		return status;
> -	}
> -}
> -
>  static int spi_nor_check(struct spi_nor *nor)
>  {
>  	if (!nor->dev || !nor->read || !nor->write ||
> @@ -1535,8 +1497,322 @@ static int s3an_nor_scan(const struct flash_info *info, struct spi_nor *nor)
>  	return 0;
>  }
>  
> -int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
> +
> +struct spi_nor_read_command {
> +	u8			num_mode_clocks;
> +	u8			num_wait_states;
> +	u8			opcode;
> +	enum spi_nor_protocol	proto;
> +};
> +
> +struct spi_nor_pp_command {
> +	u8			opcode;
> +	enum spi_nor_protocol	proto;
> +};
> +
> +enum spi_nor_read_command_index {
> +	SNOR_CMD_READ,
> +	SNOR_CMD_READ_FAST,
> +	SNOR_CMD_READ_1_1_1_DTR,
> +
> +	/* Dual SPI */
> +	SNOR_CMD_READ_1_1_2,
> +	SNOR_CMD_READ_1_2_2,
> +	SNOR_CMD_READ_2_2_2,
> +	SNOR_CMD_READ_1_2_2_DTR,
> +
> +	/* Quad SPI */
> +	SNOR_CMD_READ_1_1_4,
> +	SNOR_CMD_READ_1_4_4,
> +	SNOR_CMD_READ_4_4_4,
> +	SNOR_CMD_READ_1_4_4_DTR,
> +
> +	/* Octo SPI */
> +	SNOR_CMD_READ_1_1_8,
> +	SNOR_CMD_READ_1_8_8,
> +	SNOR_CMD_READ_8_8_8,
> +	SNOR_CMD_READ_1_8_8_DTR,
> +
> +	SNOR_CMD_READ_MAX
> +};
> +
> +enum spi_nor_pp_command_index {
> +	SNOR_CMD_PP,
> +
> +	/* Quad SPI */
> +	SNOR_CMD_PP_1_1_4,
> +	SNOR_CMD_PP_1_4_4,
> +	SNOR_CMD_PP_4_4_4,
> +
> +	/* Octo SPI */
> +	SNOR_CMD_PP_1_1_8,
> +	SNOR_CMD_PP_1_8_8,
> +	SNOR_CMD_PP_8_8_8,
> +
> +	SNOR_CMD_PP_MAX
> +};
> +
> +struct spi_nor_flash_parameter {
> +	u64				size;
> +	u32				page_size;
> +
> +	struct spi_nor_hwcaps		hwcaps;
> +	struct spi_nor_read_command	reads[SNOR_CMD_READ_MAX];
> +	struct spi_nor_pp_command	page_programs[SNOR_CMD_PP_MAX];
> +
> +	int (*quad_enable)(struct spi_nor *nor);
> +};
> +
> +
> +static inline 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)
> +{
> +	read->num_mode_clocks = num_mode_clocks;
> +	read->num_wait_states = num_wait_states;
> +	read->opcode = opcode;
> +	read->proto = proto;
> +}
> +
> +static inline void
> +spi_nor_set_pp_settings(struct spi_nor_pp_command *pp,
> +			u8 opcode,
> +			enum spi_nor_protocol proto)
> +{
> +	pp->opcode = opcode;
> +	pp->proto = proto;
> +}
> +
> +static int spi_nor_init_params(struct spi_nor *nor,
> +			       const struct flash_info *info,
> +			       struct spi_nor_flash_parameter *params)
> +{
> +	/* Set legacy flash parameters as default. */
> +	memset(params, 0, sizeof(*params));
> +
> +	/* Set SPI NOR sizes. */
> +	params->size = info->sector_size * info->n_sectors;
> +	params->page_size = info->page_size;
> +
> +	/* (Fast) Read settings. */
> +	params->hwcaps.mask |= SNOR_HWCAPS_READ;
> +	spi_nor_set_read_settings(&params->reads[SNOR_CMD_READ],
> +				  0, 0, SPINOR_OP_READ,
> +				  SNOR_PROTO_1_1_1);
> +	if (!(info->flags & SPI_NOR_NO_FR)) {
> +		params->hwcaps.mask |= SNOR_HWCAPS_READ_FAST;
> +		spi_nor_set_read_settings(&params->reads[SNOR_CMD_READ_FAST],
> +					  0, 8, SPINOR_OP_READ_FAST,
> +					  SNOR_PROTO_1_1_1);
> +	}
> +	if (info->flags & SPI_NOR_DUAL_READ) {
> +		params->hwcaps.mask |= SNOR_HWCAPS_READ_1_1_2;
> +		spi_nor_set_read_settings(&params->reads[SNOR_CMD_READ_1_1_2],
> +					  0, 8, SPINOR_OP_READ_1_1_2,
> +					  SNOR_PROTO_1_1_2);
> +	}
> +	if (info->flags & SPI_NOR_QUAD_READ) {
> +		params->hwcaps.mask |= SNOR_HWCAPS_READ_1_1_4;
> +		spi_nor_set_read_settings(&params->reads[SNOR_CMD_READ_1_1_4],
> +					  0, 8, SPINOR_OP_READ_1_1_4,
> +					  SNOR_PROTO_1_1_4);
> +	}
> +
> +	/* 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);
> +
> +	/* Select the procedure to set the Quad Enable bit. */
> +	if (params->hwcaps.mask & (SNOR_HWCAPS_READ_QUAD |
> +				   SNOR_HWCAPS_PP_QUAD)) {
> +		switch (JEDEC_MFR(info)) {
> +		case SNOR_MFR_MACRONIX:
> +			params->quad_enable = macronix_quad_enable;
> +			break;
> +
> +		case SNOR_MFR_MICRON:
> +			break;
> +
> +		default:
> +			params->quad_enable = spansion_quad_enable;
> +			break;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +static int spi_nor_hwcaps2cmd(u32 hwcaps)
>  {
> +	switch (hwcaps) {
> +	case SNOR_HWCAPS_READ:			return SNOR_CMD_READ;
> +	case SNOR_HWCAPS_READ_FAST:		return SNOR_CMD_READ_FAST;
> +	case SNOR_HWCAPS_READ_1_1_1_DTR:	return SNOR_CMD_READ_1_1_1_DTR;
> +	case SNOR_HWCAPS_READ_1_1_2:		return SNOR_CMD_READ_1_1_2;
> +	case SNOR_HWCAPS_READ_1_2_2:		return SNOR_CMD_READ_1_2_2;
> +	case SNOR_HWCAPS_READ_2_2_2:		return SNOR_CMD_READ_2_2_2;
> +	case SNOR_HWCAPS_READ_1_2_2_DTR:	return SNOR_CMD_READ_1_2_2_DTR;
> +	case SNOR_HWCAPS_READ_1_1_4:		return SNOR_CMD_READ_1_1_4;
> +	case SNOR_HWCAPS_READ_1_4_4:		return SNOR_CMD_READ_1_4_4;
> +	case SNOR_HWCAPS_READ_4_4_4:		return SNOR_CMD_READ_4_4_4;
> +	case SNOR_HWCAPS_READ_1_4_4_DTR:	return SNOR_CMD_READ_1_4_4_DTR;
> +	case SNOR_HWCAPS_READ_1_1_8:		return SNOR_CMD_READ_1_1_8;
> +	case SNOR_HWCAPS_READ_1_8_8:		return SNOR_CMD_READ_1_8_8;
> +	case SNOR_HWCAPS_READ_8_8_8:		return SNOR_CMD_READ_8_8_8;
> +	case SNOR_HWCAPS_READ_1_8_8_DTR:	return SNOR_CMD_READ_1_8_8_DTR;
> +
> +	case SNOR_HWCAPS_PP:			return SNOR_CMD_PP;
> +	case SNOR_HWCAPS_PP_1_1_4:		return SNOR_CMD_PP_1_1_4;
> +	case SNOR_HWCAPS_PP_1_4_4:		return SNOR_CMD_PP_1_4_4;
> +	case SNOR_HWCAPS_PP_4_4_4:		return SNOR_CMD_PP_4_4_4;
> +	case SNOR_HWCAPS_PP_1_1_8:		return SNOR_CMD_PP_1_1_8;
> +	case SNOR_HWCAPS_PP_1_8_8:		return SNOR_CMD_PP_1_8_8;
> +	case SNOR_HWCAPS_PP_8_8_8:		return SNOR_CMD_PP_8_8_8;
> +	}
> +
> +	return -EINVAL;
> +}
> +
> +static int spi_nor_select_read(struct spi_nor *nor,
> +			       const struct spi_nor_flash_parameter *params,
> +			       u32 shared_hwcaps)
> +{
> +	int cmd, best_match = fls(shared_hwcaps & SNOR_HWCAPS_READ_MASK) - 1;
> +	const struct spi_nor_read_command *read;
> +
> +	if (best_match < 0)
> +		return -EINVAL;
> +
> +	cmd = spi_nor_hwcaps2cmd(BIT(best_match));
> +	if (cmd < 0)
> +		return -EINVAL;
> +
> +	read = &params->reads[cmd];
> +	nor->read_opcode = read->opcode;
> +	nor->read_proto = read->proto;
> +
> +	/*
> +	 * In the spi-nor framework, we don't need to make the difference
> +	 * between mode clock cycles and wait state clock cycles.
> +	 * Indeed, the value of the mode clock cycles is used by a QSPI
> +	 * flash memory to know whether it should enter or leave its 0-4-4
> +	 * (Continuous Read / XIP) mode.
> +	 * eXecution In Place is out of the scope of the mtd sub-system.
> +	 * Hence we choose to merge both mode and wait state clock cycles
> +	 * into the so called dummy clock cycles.
> +	 */
> +	nor->read_dummy = read->num_mode_clocks + read->num_wait_states;
> +	return 0;
> +}
> +
> +static int spi_nor_select_pp(struct spi_nor *nor,
> +			     const struct spi_nor_flash_parameter *params,
> +			     u32 shared_hwcaps)
> +{
> +	int cmd, best_match = fls(shared_hwcaps & SNOR_HWCAPS_PP_MASK) - 1;
> +	const struct spi_nor_pp_command *pp;
> +
> +	if (best_match < 0)
> +		return -EINVAL;
> +
> +	cmd = spi_nor_hwcaps2cmd(BIT(best_match));
> +	if (cmd < 0)
> +		return -EINVAL;
> +
> +	pp = &params->page_programs[cmd];
> +	nor->program_opcode = pp->opcode;
> +	nor->write_proto = pp->proto;
> +	return 0;
> +}
> +
> +static int spi_nor_select_erase(struct spi_nor *nor,
> +				const struct flash_info *info)
> +{
> +	struct mtd_info *mtd = &nor->mtd;
> +
> +#ifdef CONFIG_MTD_SPI_NOR_USE_4K_SECTORS
> +	/* prefer "small sector" erase if possible */
> +	if (info->flags & SECT_4K) {
> +		nor->erase_opcode = SPINOR_OP_BE_4K;
> +		mtd->erasesize = 4096;
> +	} else if (info->flags & SECT_4K_PMC) {
> +		nor->erase_opcode = SPINOR_OP_BE_4K_PMC;
> +		mtd->erasesize = 4096;
> +	} else
> +#endif
> +	{
> +		nor->erase_opcode = SPINOR_OP_SE;
> +		mtd->erasesize = info->sector_size;
> +	}
> +	return 0;
> +}
> +
> +static int spi_nor_setup(struct spi_nor *nor, const struct flash_info *info,
> +			 const struct spi_nor_flash_parameter *params,
> +			 const struct spi_nor_hwcaps *hwcaps)
> +{
> +	u32 ignored_mask, shared_mask;
> +	bool enable_quad_io;
> +	int err;
> +
> +	/*
> +	 * Keep only the hardware capabilities supported by both the SPI
> +	 * controller and the SPI flash memory.
> +	 */
> +	shared_mask = hwcaps->mask & params->hwcaps.mask;
> +
> +	/* SPI protocol classes N-N-N are not supported yet. */
> +	ignored_mask = (SNOR_HWCAPS_READ_2_2_2 |
> +			SNOR_HWCAPS_READ_4_4_4 |
> +			SNOR_HWCAPS_READ_8_8_8 |
> +			SNOR_HWCAPS_PP_4_4_4 |
> +			SNOR_HWCAPS_PP_8_8_8);
> +	if (shared_mask & ignored_mask) {
> +		dev_dbg(nor->dev,
> +			"SPI protocol classes N-N-N are not supported yet.\n");
> +		shared_mask &= ~ignored_mask;
> +	}
> +
> +	/* Select the (Fast) Read command. */
> +	err = spi_nor_select_read(nor, params, shared_mask);
> +	if (err) {
> +		dev_err(nor->dev, "invalid (fast) read\n");
> +		return err;
> +	}
> +
> +	/* Select the Page Program command. */
> +	err = spi_nor_select_pp(nor, params, shared_mask);
> +	if (err) {
> +		dev_err(nor->dev, "invalid page program\n");
> +		return err;
> +	}
> +
> +	/* Select the Sector Erase command. */
> +	err = spi_nor_select_erase(nor, info);
> +	if (err) {
> +		dev_err(nor->dev, "invalid sector/block erase\n");
> +		return err;
> +	}
> +
> +	/* Enable Quad I/O if needed. */
> +	enable_quad_io = (spi_nor_get_protocol_width(nor->read_proto) == 4 ||
> +			  spi_nor_get_protocol_width(nor->write_proto) == 4);
> +	if (enable_quad_io && params->quad_enable)
> +		nor->flash_quad_enable = params->quad_enable;
> +	else
> +		nor->flash_quad_enable = NULL;
> +
> +	return 0;
> +}
> +
> +int spi_nor_scan(struct spi_nor *nor, const char *name,
> +		 const struct spi_nor_hwcaps *hwcaps)
> +{
> +	struct spi_nor_flash_parameter params;
>  	const struct flash_info *info = NULL;
>  	struct device *dev = nor->dev;
>  	struct mtd_info *mtd = &nor->mtd;
> @@ -1548,6 +1824,11 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
>  	if (ret)
>  		return ret;
>  
> +	/* Reset SPI protocol for all commands */
> +	nor->reg_proto = SNOR_PROTO_1_1_1;
> +	nor->read_proto = SNOR_PROTO_1_1_1;
> +	nor->write_proto = SNOR_PROTO_1_1_1;
> +
>  	if (name)
>  		info = spi_nor_match_id(name);
>  	/* Try to auto-detect if chip name wasn't specified or not found */
> @@ -1580,6 +1861,11 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
>  		}
>  	}
>  
> +	/* Parse the Serial Flash Discoverable Parameters table */
> +	ret = spi_nor_init_params(nor, info, &params);
> +	if (ret)
> +		return ret;
> +
>  	mutex_init(&nor->lock);
>  
>  	/*
> @@ -1610,7 +1896,7 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
>  	mtd->type = MTD_NORFLASH;
>  	mtd->writesize = 1;
>  	mtd->flags = MTD_CAP_NORFLASH;
> -	mtd->size = info->sector_size * info->n_sectors;
> +	mtd->size = params.size;
>  	mtd->_erase = spi_nor_erase;
>  	mtd->_read = spi_nor_read;
>  
> @@ -1641,76 +1927,47 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
>  	if (info->flags & NO_CHIP_ERASE)
>  		nor->flags |= SNOR_F_NO_OP_CHIP_ERASE;
>  
> -#ifdef CONFIG_MTD_SPI_NOR_USE_4K_SECTORS
> -	/* prefer "small sector" erase if possible */
> -	if (info->flags & SECT_4K) {
> -		nor->erase_opcode = SPINOR_OP_BE_4K;
> -		mtd->erasesize = 4096;
> -	} else if (info->flags & SECT_4K_PMC) {
> -		nor->erase_opcode = SPINOR_OP_BE_4K_PMC;
> -		mtd->erasesize = 4096;
> -	} else
> -#endif
> -	{
> -		nor->erase_opcode = SPINOR_OP_SE;
> -		mtd->erasesize = info->sector_size;
> -	}
> -
>  	if (info->flags & SPI_NOR_NO_ERASE)
>  		mtd->flags |= MTD_NO_ERASE;
>  
>  	mtd->dev.parent = dev;
> -	nor->page_size = info->page_size;
> +	nor->page_size = params.page_size;
>  	mtd->writebufsize = nor->page_size;
>  
>  	if (np) {
>  		/* If we were instantiated by DT, use it */
>  		if (of_property_read_bool(np, "m25p,fast-read"))
> -			nor->flash_read = SPI_NOR_FAST;
> +			params.hwcaps.mask |= SNOR_HWCAPS_READ_FAST;
>  		else
> -			nor->flash_read = SPI_NOR_NORMAL;
> +			params.hwcaps.mask &= ~SNOR_HWCAPS_READ_FAST;
>  	} else {
>  		/* If we weren't instantiated by DT, default to fast-read */
> -		nor->flash_read = SPI_NOR_FAST;
> +		params.hwcaps.mask |= SNOR_HWCAPS_READ_FAST;
>  	}
>  
>  	/* Some devices cannot do fast-read, no matter what DT tells us */
>  	if (info->flags & SPI_NOR_NO_FR)
> -		nor->flash_read = SPI_NOR_NORMAL;
> +		params.hwcaps.mask &= ~SNOR_HWCAPS_READ_FAST;
> +
> +	/*
> +	 * Configure the SPI memory:
> +	 * - select op codes for (Fast) Read, Page Program and Sector Erase.
> +	 * - set the number of dummy cycles (mode cycles + wait states).
> +	 * - set the SPI protocols for register and memory accesses.
> +	 * - set the Quad Enable bit if needed (required by SPI x-y-4 protos).
> +	 */
> +	ret = spi_nor_setup(nor, info, &params, hwcaps);
> +	if (ret)
> +		return ret;
>  
> -	/* Quad/Dual-read mode takes precedence over fast/normal */
> -	if (mode == SPI_NOR_QUAD && info->flags & SPI_NOR_QUAD_READ) {
> -		ret = set_quad_mode(nor, info);
> +	if (nor->flash_quad_enable) {
> +		ret = nor->flash_quad_enable(nor);
>  		if (ret) {
>  			dev_err(dev, "quad mode not supported\n");
>  			return ret;
>  		}
> -		nor->flash_read = SPI_NOR_QUAD;
> -	} else if (mode == SPI_NOR_DUAL && info->flags & SPI_NOR_DUAL_READ) {
> -		nor->flash_read = SPI_NOR_DUAL;
>  	}
>  
> -	/* Default commands */
> -	switch (nor->flash_read) {
> -	case SPI_NOR_QUAD:
> -		nor->read_opcode = SPINOR_OP_READ_1_1_4;
> -		break;
> -	case SPI_NOR_DUAL:
> -		nor->read_opcode = SPINOR_OP_READ_1_1_2;
> -		break;
> -	case SPI_NOR_FAST:
> -		nor->read_opcode = SPINOR_OP_READ_FAST;
> -		break;
> -	case SPI_NOR_NORMAL:
> -		nor->read_opcode = SPINOR_OP_READ;
> -		break;
> -	default:
> -		dev_err(dev, "No Read opcode defined\n");
> -		return -EINVAL;
> -	}
> -
> -	nor->program_opcode = SPINOR_OP_PP;
> -
>  	if (info->addr_width)
>  		nor->addr_width = info->addr_width;
>  	else if (mtd->size > 0x1000000) {
> @@ -1731,8 +1988,6 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
>  		return -EINVAL;
>  	}
>  
> -	nor->read_dummy = spi_nor_read_dummy_cycles(nor);
> -
>  	if (info->flags & SPI_S3AN) {
>  		ret = s3an_nor_scan(info, nor);
>  		if (ret)
> diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h
> index f2a718030476..732ee6cd5330 100644
> --- a/include/linux/mtd/spi-nor.h
> +++ b/include/linux/mtd/spi-nor.h
> @@ -73,6 +73,15 @@
>  #define SPINOR_OP_BE_32K_4B	0x5c	/* Erase 32KiB block */
>  #define SPINOR_OP_SE_4B		0xdc	/* Sector erase (usually 64KiB) */
>  
> +/* Double Transfer Rate opcodes - defined in JEDEC JESD216B. */
> +#define SPINOR_OP_READ_1_1_1_DTR	0x0d
> +#define SPINOR_OP_READ_1_2_2_DTR	0xbd
> +#define SPINOR_OP_READ_1_4_4_DTR	0xed
> +
> +#define SPINOR_OP_READ_1_1_1_DTR_4B	0x0e
> +#define SPINOR_OP_READ_1_2_2_DTR_4B	0xbe
> +#define SPINOR_OP_READ_1_4_4_DTR_4B	0xee
> +
>  /* Used for SST flashes only. */
>  #define SPINOR_OP_BP		0x02	/* Byte program */
>  #define SPINOR_OP_WRDI		0x04	/* Write disable */
> @@ -119,13 +128,75 @@
>  /* Configuration Register bits. */
>  #define CR_QUAD_EN_SPAN		BIT(1)	/* Spansion Quad I/O */
>  
> -enum read_mode {
> -	SPI_NOR_NORMAL = 0,
> -	SPI_NOR_FAST,
> -	SPI_NOR_DUAL,
> -	SPI_NOR_QUAD,
> +
> +/* Supported SPI protocols */
> +#define SNOR_PROTO_WIDTH_MASK	GENMASK(7, 0)
> +
> +#define SNOR_PROTO_CLASS_MASK	GENMASK(9, 8)
> +#define SNOR_PROTO_CLASS_1_1_N	(0x0u << 8)
> +#define SNOR_PROTO_CLASS_1_N_N	(0x1u << 8)
> +#define SNOR_PROTO_CLASS_N_N_N	(0x2u << 8)
> +
> +#define SNOR_PROTO_IS_DTR	BIT(10)	/* Double Transfer Rate */
> +
> +#define SNOR_PROTO_STR(_pclass, _pwidth) \
> +	((_pclass) | (_pwidth))
> +#define SNOR_PROTO_DTR(_pclass, _pwidth) \
> +	(SNOR_PROTO_IS_DTR | (_pclass) | (_pwidth))
> +
> +enum spi_nor_protocol {
> +	SNOR_PROTO_1_1_1 = SNOR_PROTO_STR(SNOR_PROTO_CLASS_1_1_N, 1),
> +	SNOR_PROTO_1_1_2 = SNOR_PROTO_STR(SNOR_PROTO_CLASS_1_1_N, 2),
> +	SNOR_PROTO_1_1_4 = SNOR_PROTO_STR(SNOR_PROTO_CLASS_1_1_N, 4),
> +	SNOR_PROTO_1_1_8 = SNOR_PROTO_STR(SNOR_PROTO_CLASS_1_1_N, 8),
> +	SNOR_PROTO_1_2_2 = SNOR_PROTO_STR(SNOR_PROTO_CLASS_1_N_N, 2),
> +	SNOR_PROTO_1_4_4 = SNOR_PROTO_STR(SNOR_PROTO_CLASS_1_N_N, 4),
> +	SNOR_PROTO_1_8_8 = SNOR_PROTO_STR(SNOR_PROTO_CLASS_1_N_N, 8),
> +	SNOR_PROTO_2_2_2 = SNOR_PROTO_STR(SNOR_PROTO_CLASS_N_N_N, 2),
> +	SNOR_PROTO_4_4_4 = SNOR_PROTO_STR(SNOR_PROTO_CLASS_N_N_N, 4),
> +	SNOR_PROTO_8_8_8 = SNOR_PROTO_STR(SNOR_PROTO_CLASS_N_N_N, 8),
> +
> +	SNOR_PROTO_1_1_1_DTR = SNOR_PROTO_DTR(SNOR_PROTO_CLASS_1_1_N, 1),
> +	SNOR_PROTO_1_2_2_DTR = SNOR_PROTO_DTR(SNOR_PROTO_CLASS_1_N_N, 2),
> +	SNOR_PROTO_1_4_4_DTR = SNOR_PROTO_DTR(SNOR_PROTO_CLASS_1_N_N, 4),
> +	SNOR_PROTO_1_8_8_DTR = SNOR_PROTO_DTR(SNOR_PROTO_CLASS_1_N_N, 8),
>  };
>  
> +static inline bool spi_nor_protocol_is_dtr(enum spi_nor_protocol proto)
> +{
> +	return (proto & SNOR_PROTO_IS_DTR) == SNOR_PROTO_IS_DTR;
> +}
> +
> +static inline u32 spi_nor_get_protocol_class(enum spi_nor_protocol proto)
> +{
> +	return proto & SNOR_PROTO_CLASS_MASK;
> +}
> +
> +static inline u8 spi_nor_get_protocol_width(enum spi_nor_protocol proto)
> +{
> +	return proto & SNOR_PROTO_WIDTH_MASK;
> +}
> +
> +static inline u8 spi_nor_get_protocol_inst_width(enum spi_nor_protocol proto)
> +{
> +	return (spi_nor_get_protocol_class(proto) == SNOR_PROTO_CLASS_N_N_N) ?
> +		spi_nor_get_protocol_width(proto) :
> +		1u;
> +}
> +
> +static inline u8 spi_nor_get_protocol_addr_width(enum spi_nor_protocol proto)
> +{
> +	return (spi_nor_get_protocol_class(proto) != SNOR_PROTO_CLASS_1_1_N) ?
> +		spi_nor_get_protocol_width(proto) :
> +		1u;
> +}
> +
> +static inline u8 spi_nor_get_protocol_data_width(enum spi_nor_protocol proto)
> +{
> +	return spi_nor_get_protocol_width(proto);
> +}
> +
> +
>  #define SPI_NOR_MAX_CMD_SIZE	8
>  enum spi_nor_ops {
>  	SPI_NOR_OPS_READ = 0,
> @@ -154,9 +225,11 @@ enum spi_nor_option_flags {
>   * @read_opcode:	the read opcode
>   * @read_dummy:		the dummy needed by the read operation
>   * @program_opcode:	the program opcode
> - * @flash_read:		the mode of the read
>   * @sst_write_second:	used by the SST write operation
>   * @flags:		flag options for the current SPI-NOR (SNOR_F_*)
> + * @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
>   * @cmd_buf:		used by the write_reg
>   * @prepare:		[OPTIONAL] do some preparations for the
>   *			read/write/erase/lock/unlock operations
> @@ -173,6 +246,7 @@ enum spi_nor_option_flags {
>   * @flash_unlock:	[FLASH-SPECIFIC] unlock a region of the SPI NOR
>   * @flash_is_locked:	[FLASH-SPECIFIC] check if a region of the SPI NOR is
>   *			completely locked
> + * @flash_quad_enable:	[FLASH-SPECIFIC] set the Quad Enable bit of the SPI NOR
>   * @priv:		the private data
>   */
>  struct spi_nor {
> @@ -185,7 +259,9 @@ struct spi_nor {
>  	u8			read_opcode;
>  	u8			read_dummy;
>  	u8			program_opcode;
> -	enum read_mode		flash_read;
> +	enum spi_nor_protocol	read_proto;
> +	enum spi_nor_protocol	write_proto;
> +	enum spi_nor_protocol	reg_proto;
>  	bool			sst_write_second;
>  	u32			flags;
>  	u8			cmd_buf[SPI_NOR_MAX_CMD_SIZE];
> @@ -204,6 +280,7 @@ struct spi_nor {
>  	int (*flash_lock)(struct spi_nor *nor, loff_t ofs, uint64_t len);
>  	int (*flash_unlock)(struct spi_nor *nor, loff_t ofs, uint64_t len);
>  	int (*flash_is_locked)(struct spi_nor *nor, loff_t ofs, uint64_t len);
> +	int (*flash_quad_enable)(struct spi_nor *nor);
>  
>  	void *priv;
>  };
> @@ -219,11 +296,73 @@ static inline struct device_node *spi_nor_get_flash_node(struct spi_nor *nor)
>  	return mtd_get_of_node(&nor->mtd);
>  }
>  
> +
> +/**
> + * struct spi_nor_hwcaps - Structure for describing the hardware capabilies
> + * supported by the SPI controller (bus master).
> + * @mask:		the bitmask listing all the supported hw capabilies
> + */
> +struct spi_nor_hwcaps {
> +	u32	mask;
> +};
> +
> +/*
> + *(Fast) Read capabilities.
> + * MUST be ordered by priority: the higher bit position, the higher priority.
> + * As a matter of performances, it is relevant to use Octo SPI protocols first,
> + * then Quad SPI protocols before Dual SPI protocols, Fast Read and lastly
> + * (Slow) Read.
> + */
> +#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)
> +
> +#define SNOR_HWCAPS_READ_DUAL		GENMASK(7, 4)
> +#define SNOR_HWCAPS_READ_1_1_2		BIT(4)
> +#define SNOR_HWCAPS_READ_1_2_2		BIT(5)
> +#define SNOR_HWCAPS_READ_2_2_2		BIT(6)
> +#define SNOR_HWCAPS_READ_1_2_2_DTR	BIT(7)
> +
> +#define SNOR_HWCAPS_READ_QUAD		GENMASK(11, 8)
> +#define SNOR_HWCAPS_READ_1_1_4		BIT(8)
> +#define SNOR_HWCAPS_READ_1_4_4		BIT(9)
> +#define SNOR_HWCAPS_READ_4_4_4		BIT(10)
> +#define SNOR_HWCAPS_READ_1_4_4_DTR	BIT(11)
> +
> +#define SNOR_HWCPAS_READ_OCTO		GENMASK(15, 12)
> +#define SNOR_HWCAPS_READ_1_1_8		BIT(12)
> +#define SNOR_HWCAPS_READ_1_8_8		BIT(13)
> +#define SNOR_HWCAPS_READ_8_8_8		BIT(14)
> +#define SNOR_HWCAPS_READ_1_8_8_DTR	BIT(15)
> +
> +/*
> + * Page Program capabilities.
> + * MUST be ordered by priority: the higher bit position, the higher priority.
> + * Like (Fast) Read capabilities, Octo/Quad SPI protocols are preferred to the
> + * legacy SPI 1-1-1 protocol.
> + * Note that Dual Page Programs are not supported because there is no existing
> + * 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_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_OCTO	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)
> +
>  /**
>   * spi_nor_scan() - scan the SPI NOR
>   * @nor:	the spi_nor structure
>   * @name:	the chip type name
> - * @mode:	the read mode supported by the driver
> + * @hwcaps:	the hardware capabilities supported by the controller driver
>   *
>   * The drivers can use this fuction to scan the SPI NOR.
>   * In the scanning, it will try to get all the necessary information to
> @@ -233,6 +372,7 @@ static inline struct device_node *spi_nor_get_flash_node(struct spi_nor *nor)
>   *
>   * Return: 0 for success, others for failure.
>   */
> -int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode);
> +int spi_nor_scan(struct spi_nor *nor, const char *name,
> +		 const struct spi_nor_hwcaps *hwcaps);
>  
>  #endif
> 

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

* Re: [PATCH v5 1/6] mtd: spi-nor: introduce more SPI protocols and the Dual Transfer Mode
  2017-03-23 15:13   ` Cédric Le Goater
@ 2017-03-23 19:10     ` Cyrille Pitchen
  2017-03-24 10:03       ` Cédric Le Goater
  0 siblings, 1 reply; 27+ messages in thread
From: Cyrille Pitchen @ 2017-03-23 19:10 UTC (permalink / raw)
  To: Cédric Le Goater, Cyrille Pitchen, marek.vasut, linux-mtd,
	jartur, kdasu.kdev, mar.krzeminski
  Cc: boris.brezillon, richard, nicolas.ferre, linux-kernel,
	computersforpeace, dwmw2

Hi Cédic,

Le 23/03/2017 à 16:13, Cédric Le Goater a écrit :
> On 03/23/2017 12:33 AM, Cyrille Pitchen wrote:
>> This patch changes the prototype of spi_nor_scan(): its 3rd parameter
>> is replaced by a 'struct spi_nor_hwcaps' pointer, which tells the spi-nor
>> framework about the actual hardware capabilities supported by the SPI
>> controller and its driver.
>>
>> Besides, this patch also introduces a new 'struct spi_nor_flash_parameter'
>> telling the spi-nor framework about the hardware capabilities supported by
>> the SPI flash memory and the associated settings required to use those
>> hardware caps.
>>
>> Currently the 'struct spi_nor_flash_parameter' is filled with legacy
>> values but a later patch will allow to fill it dynamically by reading the
>> JESD216 Serial Flash Discoverable Parameter (SFDP) tables from the SPI
>> memory.
>>
>> With both structures, the spi-nor framework can now compute the best
>> match between hardware caps supported by both the (Q)SPI memory and
>> controller hence selecting the relevant SPI protocols and op codes for
>> (Fast) Read, Page Program and Sector Erase operations.
>>
>> The 'struct spi_nor_flash_parameter' also provides the spi-nor framework
>> with the number of dummy cycles to be used with each Fast Read commands
>> and the erase block size associated to the erase block op codes.
>>
>> Finally the 'struct spi_nor_flash_parameter', through the optional
>> .enable_quad_io() hook, tells the spi-nor framework how to set the Quad

.enable_quad_io() was renamed into .quad_enable()

>> Enable (QE) bit of the QSPI memory to enable its Quad SPI features.
> 
> The Aspeed controller only supports Dual I/O and a helper similar 
> to the one for Quad I/O would be needed to setup the chip for 
> multiple I/O: 
> 
> 	int (*dual_enable)(struct spi_nor *nor);
> 
> Is the approach correct ? or maybe rename the current 'quad_enable()'
> to 'multiple_enable()' and add an extra parameter.
>

No, nor->flash_quad_enable() [ = params->quad_enable() ] is a flash
specific handler as described in spi-nor.h and like other flash specific
handlers such as flash_lock(), flash_unlock() or flash_is_locked(), is
only used internally in spi-nor.c: spi-nor controller drivers like
aspeed-smc.c should not use those handlers at all.

the choice of the nor->flash_quad_enable() handler depends only on the
memory manufacturer (and the memory part):
- (none): Micron
- macronix_quad_enable: Macronix
- spansion_quad_enable: Spansion, Winbond, ...
- spansion_new_quad_enable: Spansion (latest memories), ...
- sr2_bit7_quad_enable: ??? (defined in the JESD216B specification)

The purpose of those functions is to implement the vendor specific
procedure to set the so called "Quad Enable" (QE) bit in some Status
Register of the SPI NOR memory.

Indeed, most QSPI memories are pin to pin compatible with legacy SPI
memories. 2 pins of those memories were dedicated to the Write Protect
(WP) and Reset/Hold (RST) functions. Hence before using any Quad SPI
commands, almost all QSPI memories (Micron being the only exception I
know) require us to set their QE bit so the WP and RST pins are
reassigned to functions IO2 and IO3, the 3rd and 4th IO lines needed by
SPI 1-1-4 and SPI 1-4-4 protocols. Then the Write Protect and Hold/Reset
functions are disabled.

>From a software point of view, there is nothing to do before using the
SPI 1-1-2 or SPI 1-2-2 protocols. As a matter of fact, with those
protocols, MISO and MOSI pins are simply reassigned to functions IO0 and
IO1.


Back to the SPI flash controller, if the hardware needs to be configured
in some way to use any Dual or Quad SPI protocols, it has to be done
before calling spi_nor_scan() or more likely directly inside the
controller driver specific implementation of nor->read(), nor->write().

With nor->read(), the driver has to check the value of nor->read_proto
to know the actual number of I/O lines used during Instruction (x),
Address/Dummy (y) and Data (z) clock cycles: nor->read_proto provides
the driver with the SPI x-y-z protocol to be used.

Also nor->write() has to check the value of nor->write_proto.

[ nor->erase() has to check the value of nor->reg_proto: I removed
nor->erase_proto since it always had the same value as nor->reg_proto. ]

Both nor->write() and nor->read are controller driver specific: they
must be implemented by the controller driver and set before calling
spi_nor_scan().

Then nor->read_proto and nor->write_proto are chosen from spi_nor_scan()
based on the actual hardware capabilities shared by both the SPI memory
and controller. The SPI controller driver tells the spi-nor framework
which SPI protocols it supports or wants to use by setting the new
'struct spi_nor_hwcaps' argument of spi_nor_scan() accordingly.


You can have a look at the atmel-quadspi.c driver to have an example of
what to do to support the SPI 1-2-2 or SPI 1-4-4 protocols at the
controller side.

I've updated the Atmel Quad SPI driver since I'm the maintainer of this
driver so I know the exact hardware capabilities of this controller but
I don't have such a knowledge for Quad SPI controllers of other vendors.

Please note that patch is conservative: if a controller driver like the
Aspeed one currently doesn't support the Quad or Dual SPI protocols, the
patch doesn't enable them in hwcaps.mask.

Maintainers of the controller drivers will have to, if they want, extend
their driver to add the support of new SPI protocols, otherwise those
drivers will keep on working exactly as they used to do before this
series. For instance, a controller driver which used SPI_NOR_QUAD before
still uses the SPI 1-1-4 protocol.

Best regards,

Cyrille


> I have a bunch of patches queued for Dual I/O data support but I 
> think they will conflict with your patches. I will wait for this 
> one to be merged. Then, I can look at Dual I/O address + data 
> support.
> 
> Thanks,
> 
> C.     
> 
> 
>> Signed-off-by: Cyrille Pitchen <cyrille.pitchen@atmel.com>
>> ---
>>  drivers/mtd/devices/m25p80.c          |  16 +-
>>  drivers/mtd/spi-nor/aspeed-smc.c      |  23 +-
>>  drivers/mtd/spi-nor/atmel-quadspi.c   |  80 +++---
>>  drivers/mtd/spi-nor/cadence-quadspi.c |  18 +-
>>  drivers/mtd/spi-nor/fsl-quadspi.c     |   8 +-
>>  drivers/mtd/spi-nor/hisi-sfc.c        |  31 ++-
>>  drivers/mtd/spi-nor/intel-spi.c       |   7 +-
>>  drivers/mtd/spi-nor/mtk-quadspi.c     |  16 +-
>>  drivers/mtd/spi-nor/nxp-spifi.c       |  22 +-
>>  drivers/mtd/spi-nor/spi-nor.c         | 441 +++++++++++++++++++++++++++-------
>>  include/linux/mtd/spi-nor.h           | 158 +++++++++++-
>>  11 files changed, 643 insertions(+), 177 deletions(-)
>>
>> diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c
>> index c4df3b1bded0..68986a26c8fe 100644
>> --- a/drivers/mtd/devices/m25p80.c
>> +++ b/drivers/mtd/devices/m25p80.c
>> @@ -111,10 +111,10 @@ static ssize_t m25p80_write(struct spi_nor *nor, loff_t to, size_t len,
>>  
>>  static inline unsigned int m25p80_rx_nbits(struct spi_nor *nor)
>>  {
>> -	switch (nor->flash_read) {
>> -	case SPI_NOR_DUAL:
>> +	switch (nor->read_proto) {
>> +	case SNOR_PROTO_1_1_2:
>>  		return 2;
>> -	case SPI_NOR_QUAD:
>> +	case SNOR_PROTO_1_1_4:
>>  		return 4;
>>  	default:
>>  		return 0;
>> @@ -196,7 +196,9 @@ static int m25p_probe(struct spi_device *spi)
>>  	struct flash_platform_data	*data;
>>  	struct m25p *flash;
>>  	struct spi_nor *nor;
>> -	enum read_mode mode = SPI_NOR_NORMAL;
>> +	struct spi_nor_hwcaps hwcaps = {
>> +		.mask = (SNOR_HWCAPS_READ | SNOR_HWCAPS_PP),
>> +	};
>>  	char *flash_name;
>>  	int ret;
>>  
>> @@ -222,9 +224,9 @@ static int m25p_probe(struct spi_device *spi)
>>  	flash->spi = spi;
>>  
>>  	if (spi->mode & SPI_RX_QUAD)
>> -		mode = SPI_NOR_QUAD;
>> +		hwcaps.mask |= SNOR_HWCAPS_READ_1_1_4;
>>  	else if (spi->mode & SPI_RX_DUAL)
>> -		mode = SPI_NOR_DUAL;
>> +		hwcaps.mask |= SNOR_HWCAPS_READ_1_1_2;
>>  
>>  	if (data && data->name)
>>  		nor->mtd.name = data->name;
>> @@ -241,7 +243,7 @@ static int m25p_probe(struct spi_device *spi)
>>  	else
>>  		flash_name = spi->modalias;
>>  
>> -	ret = spi_nor_scan(nor, flash_name, mode);
>> +	ret = spi_nor_scan(nor, flash_name, &hwcaps);
>>  	if (ret)
>>  		return ret;
>>  
>> diff --git a/drivers/mtd/spi-nor/aspeed-smc.c b/drivers/mtd/spi-nor/aspeed-smc.c
>> index 56051d30f000..723026d9cf0c 100644
>> --- a/drivers/mtd/spi-nor/aspeed-smc.c
>> +++ b/drivers/mtd/spi-nor/aspeed-smc.c
>> @@ -585,14 +585,12 @@ static int aspeed_smc_chip_setup_finish(struct aspeed_smc_chip *chip)
>>  	 * TODO: Adjust clocks if fast read is supported and interpret
>>  	 * SPI-NOR flags to adjust controller settings.
>>  	 */
>> -	switch (chip->nor.flash_read) {
>> -	case SPI_NOR_NORMAL:
>> -		cmd = CONTROL_COMMAND_MODE_NORMAL;
>> -		break;
>> -	case SPI_NOR_FAST:
>> -		cmd = CONTROL_COMMAND_MODE_FREAD;
>> -		break;
>> -	default:
>> +	if (chip->nor.read_proto == SNOR_PROTO_1_1_1) {
>> +		if (chip->nor.read_dummy == 0)
>> +			cmd = CONTROL_COMMAND_MODE_NORMAL;
>> +		else
>> +			cmd = CONTROL_COMMAND_MODE_FREAD;
>> +	} else {
>>  		dev_err(chip->nor.dev, "unsupported SPI read mode\n");
>>  		return -EINVAL;
>>  	}
>> @@ -608,6 +606,11 @@ static int aspeed_smc_chip_setup_finish(struct aspeed_smc_chip *chip)
>>  static int aspeed_smc_setup_flash(struct aspeed_smc_controller *controller,
>>  				  struct device_node *np, struct resource *r)
>>  {
>> +	struct spi_nor_hwcaps hwcaps = {
>> +		.mask = (SNOR_HWCAPS_READ |
>> +			 SNOR_HWCAPS_READ_FAST |
>> +			 SNOR_HWCAPS_PP),
>> +	};
>>  	const struct aspeed_smc_info *info = controller->info;
>>  	struct device *dev = controller->dev;
>>  	struct device_node *child;
>> @@ -671,11 +674,11 @@ static int aspeed_smc_setup_flash(struct aspeed_smc_controller *controller,
>>  			break;
>>  
>>  		/*
>> -		 * TODO: Add support for SPI_NOR_QUAD and SPI_NOR_DUAL
>> +		 * TODO: Add support for Dual and Quad SPI protocols
>>
>>  		 * attach when board support is present as determined
>>  		 * by of property.
>>  		 */
>> -		ret = spi_nor_scan(nor, NULL, SPI_NOR_NORMAL);
>> +		ret = spi_nor_scan(nor, NULL, &hwcaps);
>>  		if (ret)
>>  			break;
>>  
>> diff --git a/drivers/mtd/spi-nor/atmel-quadspi.c b/drivers/mtd/spi-nor/atmel-quadspi.c
>> index 47937d9beec6..9f579f7c1733 100644
>> --- a/drivers/mtd/spi-nor/atmel-quadspi.c
>> +++ b/drivers/mtd/spi-nor/atmel-quadspi.c
>> @@ -275,14 +275,48 @@ static void atmel_qspi_debug_command(struct atmel_qspi *aq,
>>  
>>  static int atmel_qspi_run_command(struct atmel_qspi *aq,
>>  				  const struct atmel_qspi_command *cmd,
>> -				  u32 ifr_tfrtyp, u32 ifr_width)
>> +				  u32 ifr_tfrtyp, enum spi_nor_protocol proto)
>>  {
>>  	u32 iar, icr, ifr, sr;
>>  	int err = 0;
>>  
>>  	iar = 0;
>>  	icr = 0;
>> -	ifr = ifr_tfrtyp | ifr_width;
>> +	ifr = ifr_tfrtyp;
>> +
>> +	/* Set the SPI protocol */
>> +	switch (proto) {
>> +	case SNOR_PROTO_1_1_1:
>> +		ifr |= QSPI_IFR_WIDTH_SINGLE_BIT_SPI;
>> +		break;
>> +
>> +	case SNOR_PROTO_1_1_2:
>> +		ifr |= QSPI_IFR_WIDTH_DUAL_OUTPUT;
>> +		break;
>> +
>> +	case SNOR_PROTO_1_1_4:
>> +		ifr |= QSPI_IFR_WIDTH_QUAD_OUTPUT;
>> +		break;
>> +
>> +	case SNOR_PROTO_1_2_2:
>> +		ifr |= QSPI_IFR_WIDTH_DUAL_IO;
>> +		break;
>> +
>> +	case SNOR_PROTO_1_4_4:
>> +		ifr |= QSPI_IFR_WIDTH_QUAD_IO;
>> +		break;
>> +
>> +	case SNOR_PROTO_2_2_2:
>> +		ifr |= QSPI_IFR_WIDTH_DUAL_CMD;
>> +		break;
>> +
>> +	case SNOR_PROTO_4_4_4:
>> +		ifr |= QSPI_IFR_WIDTH_QUAD_CMD;
>> +		break;
>> +
>> +	default:
>> +		return -EINVAL;
>> +	}
>>  
>>  	/* Compute instruction parameters */
>>  	if (cmd->enable.bits.instruction) {
>> @@ -434,7 +468,7 @@ static int atmel_qspi_read_reg(struct spi_nor *nor, u8 opcode,
>>  	cmd.rx_buf = buf;
>>  	cmd.buf_len = len;
>>  	return atmel_qspi_run_command(aq, &cmd, QSPI_IFR_TFRTYP_TRSFR_READ,
>> -				      QSPI_IFR_WIDTH_SINGLE_BIT_SPI);
>> +				      nor->reg_proto);
>>  }
>>  
>>  static int atmel_qspi_write_reg(struct spi_nor *nor, u8 opcode,
>> @@ -450,7 +484,7 @@ static int atmel_qspi_write_reg(struct spi_nor *nor, u8 opcode,
>>  	cmd.tx_buf = buf;
>>  	cmd.buf_len = len;
>>  	return atmel_qspi_run_command(aq, &cmd, QSPI_IFR_TFRTYP_TRSFR_WRITE,
>> -				      QSPI_IFR_WIDTH_SINGLE_BIT_SPI);
>> +				      nor->reg_proto);
>>  }
>>  
>>  static ssize_t atmel_qspi_write(struct spi_nor *nor, loff_t to, size_t len,
>> @@ -469,7 +503,7 @@ static ssize_t atmel_qspi_write(struct spi_nor *nor, loff_t to, size_t len,
>>  	cmd.tx_buf = write_buf;
>>  	cmd.buf_len = len;
>>  	ret = atmel_qspi_run_command(aq, &cmd, QSPI_IFR_TFRTYP_TRSFR_WRITE_MEM,
>> -				     QSPI_IFR_WIDTH_SINGLE_BIT_SPI);
>> +				     nor->write_proto);
>>  	return (ret < 0) ? ret : len;
>>  }
>>  
>> @@ -484,7 +518,7 @@ static int atmel_qspi_erase(struct spi_nor *nor, loff_t offs)
>>  	cmd.instruction = nor->erase_opcode;
>>  	cmd.address = (u32)offs;
>>  	return atmel_qspi_run_command(aq, &cmd, QSPI_IFR_TFRTYP_TRSFR_WRITE,
>> -				      QSPI_IFR_WIDTH_SINGLE_BIT_SPI);
>> +				      nor->reg_proto);
>>  }
>>  
>>  static ssize_t atmel_qspi_read(struct spi_nor *nor, loff_t from, size_t len,
>> @@ -493,27 +527,8 @@ static ssize_t atmel_qspi_read(struct spi_nor *nor, loff_t from, size_t len,
>>  	struct atmel_qspi *aq = nor->priv;
>>  	struct atmel_qspi_command cmd;
>>  	u8 num_mode_cycles, num_dummy_cycles;
>> -	u32 ifr_width;
>>  	ssize_t ret;
>>  
>> -	switch (nor->flash_read) {
>> -	case SPI_NOR_NORMAL:
>> -	case SPI_NOR_FAST:
>> -		ifr_width = QSPI_IFR_WIDTH_SINGLE_BIT_SPI;
>> -		break;
>> -
>> -	case SPI_NOR_DUAL:
>> -		ifr_width = QSPI_IFR_WIDTH_DUAL_OUTPUT;
>> -		break;
>> -
>> -	case SPI_NOR_QUAD:
>> -		ifr_width = QSPI_IFR_WIDTH_QUAD_OUTPUT;
>> -		break;
>> -
>> -	default:
>> -		return -EINVAL;
>> -	}
>> -
>>  	if (nor->read_dummy >= 2) {
>>  		num_mode_cycles = 2;
>>  		num_dummy_cycles = nor->read_dummy - 2;
>> @@ -536,7 +551,7 @@ static ssize_t atmel_qspi_read(struct spi_nor *nor, loff_t from, size_t len,
>>  	cmd.rx_buf = read_buf;
>>  	cmd.buf_len = len;
>>  	ret = atmel_qspi_run_command(aq, &cmd, QSPI_IFR_TFRTYP_TRSFR_READ_MEM,
>> -				     ifr_width);
>> +				     nor->read_proto);
>>  	return (ret < 0) ? ret : len;
>>  }
>>  
>> @@ -590,6 +605,17 @@ static irqreturn_t atmel_qspi_interrupt(int irq, void *dev_id)
>>  
>>  static int atmel_qspi_probe(struct platform_device *pdev)
>>  {
>> +	struct spi_nor_hwcaps hwcaps = {
>> +		.mask = (SNOR_HWCAPS_READ |
>> +			 SNOR_HWCAPS_READ_FAST |
>> +			 SNOR_HWCAPS_READ_1_1_2 |
>> +			 SNOR_HWCAPS_READ_1_2_2 |
>> +			 SNOR_HWCAPS_READ_1_1_4 |
>> +			 SNOR_HWCAPS_READ_1_4_4 |
>> +			 SNOR_HWCAPS_PP |
>> +			 SNOR_HWCAPS_PP_1_1_4 |
>> +			 SNOR_HWCAPS_PP_1_4_4),
>> +	};
>>  	struct device_node *child, *np = pdev->dev.of_node;
>>  	struct atmel_qspi *aq;
>>  	struct resource *res;
>> @@ -679,7 +705,7 @@ static int atmel_qspi_probe(struct platform_device *pdev)
>>  	if (err)
>>  		goto disable_clk;
>>  
>> -	err = spi_nor_scan(nor, NULL, SPI_NOR_QUAD);
>> +	err = spi_nor_scan(nor, NULL, &hwcaps);
>>  	if (err)
>>  		goto disable_clk;
>>  
>> diff --git a/drivers/mtd/spi-nor/cadence-quadspi.c b/drivers/mtd/spi-nor/cadence-quadspi.c
>> index 9f8102de1b16..3f91a3e97892 100644
>> --- a/drivers/mtd/spi-nor/cadence-quadspi.c
>> +++ b/drivers/mtd/spi-nor/cadence-quadspi.c
>> @@ -855,15 +855,14 @@ static int cqspi_set_protocol(struct spi_nor *nor, const int read)
>>  	f_pdata->data_width = CQSPI_INST_TYPE_SINGLE;
>>  
>>  	if (read) {
>> -		switch (nor->flash_read) {
>> -		case SPI_NOR_NORMAL:
>> -		case SPI_NOR_FAST:
>> +		switch (nor->read_proto) {
>> +		case SNOR_PROTO_1_1_1:
>>  			f_pdata->data_width = CQSPI_INST_TYPE_SINGLE;
>>  			break;
>> -		case SPI_NOR_DUAL:
>> +		case SNOR_PROTO_1_1_2:
>>  			f_pdata->data_width = CQSPI_INST_TYPE_DUAL;
>>  			break;
>> -		case SPI_NOR_QUAD:
>> +		case SNOR_PROTO_1_1_4:
>>  			f_pdata->data_width = CQSPI_INST_TYPE_QUAD;
>>  			break;
>>  		default:
>> @@ -1069,6 +1068,13 @@ static void cqspi_controller_init(struct cqspi_st *cqspi)
>>  
>>  static int cqspi_setup_flash(struct cqspi_st *cqspi, struct device_node *np)
>>  {
>> +	struct spi_nor_hwcaps hwcaps = {
>> +		.mask = (SNOR_HWCAPS_READ |
>> +			 SNOR_HWCAPS_READ_FAST |
>> +			 SNOR_HWCAPS_READ_1_1_2 |
>> +			 SNOR_HWCAPS_READ_1_1_4 |
>> +			 SNOR_HWCAPS_PP),
>> +	};
>>  	struct platform_device *pdev = cqspi->pdev;
>>  	struct device *dev = &pdev->dev;
>>  	struct cqspi_flash_pdata *f_pdata;
>> @@ -1123,7 +1129,7 @@ static int cqspi_setup_flash(struct cqspi_st *cqspi, struct device_node *np)
>>  			goto err;
>>  		}
>>  
>> -		ret = spi_nor_scan(nor, NULL, SPI_NOR_QUAD);
>> +		ret = spi_nor_scan(nor, NULL, &hwcaps);
>>  		if (ret)
>>  			goto err;
>>  
>> diff --git a/drivers/mtd/spi-nor/fsl-quadspi.c b/drivers/mtd/spi-nor/fsl-quadspi.c
>> index 1476135e0d50..ec9c8e960fd2 100644
>> --- a/drivers/mtd/spi-nor/fsl-quadspi.c
>> +++ b/drivers/mtd/spi-nor/fsl-quadspi.c
>> @@ -957,6 +957,12 @@ static void fsl_qspi_unprep(struct spi_nor *nor, enum spi_nor_ops ops)
>>  
>>  static int fsl_qspi_probe(struct platform_device *pdev)
>>  {
>> +	struct spi_nor_hwcaps hwcaps = {
>> +		.mask = (SNOR_HWCAPS_READ |
>> +			 SNOR_HWCAPS_READ_FAST |
>> +			 SNOR_HWCAPS_READ_1_1_4 |
>> +			 SNOR_HWCAPS_PP),
>> +	};
>>  	struct device_node *np = pdev->dev.of_node;
>>  	struct device *dev = &pdev->dev;
>>  	struct fsl_qspi *q;
>> @@ -1065,7 +1071,7 @@ static int fsl_qspi_probe(struct platform_device *pdev)
>>  		/* set the chip address for READID */
>>  		fsl_qspi_set_base_addr(q, nor);
>>  
>> -		ret = spi_nor_scan(nor, NULL, SPI_NOR_QUAD);
>> +		ret = spi_nor_scan(nor, NULL, &hwcaps);
>>  		if (ret)
>>  			goto mutex_failed;
>>  
>> diff --git a/drivers/mtd/spi-nor/hisi-sfc.c b/drivers/mtd/spi-nor/hisi-sfc.c
>> index a286350627a6..80e2d173abdd 100644
>> --- a/drivers/mtd/spi-nor/hisi-sfc.c
>> +++ b/drivers/mtd/spi-nor/hisi-sfc.c
>> @@ -120,19 +120,24 @@ static inline int wait_op_finish(struct hifmc_host *host)
>>  		(reg & FMC_INT_OP_DONE), 0, FMC_WAIT_TIMEOUT);
>>  }
>>  
>> -static int get_if_type(enum read_mode flash_read)
>> +static int get_if_type(enum spi_nor_protocol proto)
>>  {
>>  	enum hifmc_iftype if_type;
>>  
>> -	switch (flash_read) {
>> -	case SPI_NOR_DUAL:
>> +	switch (proto) {
>> +	case SNOR_PROTO_1_1_2:
>>  		if_type = IF_TYPE_DUAL;
>>  		break;
>> -	case SPI_NOR_QUAD:
>> +	case SNOR_PROTO_1_2_2:
>> +		if_type = IF_TYPE_DIO;
>> +		break;
>> +	case SNOR_PROTO_1_1_4:
>>  		if_type = IF_TYPE_QUAD;
>>  		break;
>> -	case SPI_NOR_NORMAL:
>> -	case SPI_NOR_FAST:
>> +	case SNOR_PROTO_1_4_4:
>> +		if_type = IF_TYPE_QIO;
>> +		break;
>> +	case SNOR_PROTO_1_1_1:
>>  	default:
>>  		if_type = IF_TYPE_STD;
>>  		break;
>> @@ -253,7 +258,10 @@ static int hisi_spi_nor_dma_transfer(struct spi_nor *nor, loff_t start_off,
>>  	writel(FMC_DMA_LEN_SET(len), host->regbase + FMC_DMA_LEN);
>>  
>>  	reg = OP_CFG_FM_CS(priv->chipselect);
>> -	if_type = get_if_type(nor->flash_read);
>> +	if (op_type == FMC_OP_READ)
>> +		if_type = get_if_type(nor->read_proto);
>> +	else
>> +		if_type = get_if_type(nor->write_proto);
>>  	reg |= OP_CFG_MEM_IF_TYPE(if_type);
>>  	if (op_type == FMC_OP_READ)
>>  		reg |= OP_CFG_DUMMY_NUM(nor->read_dummy >> 3);
>> @@ -321,6 +329,13 @@ static ssize_t hisi_spi_nor_write(struct spi_nor *nor, loff_t to,
>>  static int hisi_spi_nor_register(struct device_node *np,
>>  				struct hifmc_host *host)
>>  {
>> +	struct spi_nor_hwcaps hwcaps = {
>> +		.mask = (SNOR_HWCAPS_READ |
>> +			 SNOR_HWCAPS_READ_FAST |
>> +			 SNOR_HWCAPS_READ_1_1_2 |
>> +			 SNOR_HWCAPS_READ_1_1_4 |
>> +			 SNOR_HWCAPS_PP),
>> +	};
>>  	struct device *dev = host->dev;
>>  	struct spi_nor *nor;
>>  	struct hifmc_priv *priv;
>> @@ -362,7 +377,7 @@ static int hisi_spi_nor_register(struct device_node *np,
>>  	nor->read = hisi_spi_nor_read;
>>  	nor->write = hisi_spi_nor_write;
>>  	nor->erase = NULL;
>> -	ret = spi_nor_scan(nor, NULL, SPI_NOR_QUAD);
>> +	ret = spi_nor_scan(nor, NULL, &hwcaps);
>>  	if (ret)
>>  		return ret;
>>  
>> diff --git a/drivers/mtd/spi-nor/intel-spi.c b/drivers/mtd/spi-nor/intel-spi.c
>> index 986a3d020a3a..515aa1f7f4f1 100644
>> --- a/drivers/mtd/spi-nor/intel-spi.c
>> +++ b/drivers/mtd/spi-nor/intel-spi.c
>> @@ -715,6 +715,11 @@ static void intel_spi_fill_partition(struct intel_spi *ispi,
>>  struct intel_spi *intel_spi_probe(struct device *dev,
>>  	struct resource *mem, const struct intel_spi_boardinfo *info)
>>  {
>> +	struct spi_nor_hwcaps hwcaps = {
>> +		.mask = (SNOR_HWCAPS_READ |
>> +			 SNOR_HWCAPS_READ_FAST |
>> +			 SNOR_HWCAPS_PP),
>> +	};
>>  	struct mtd_partition part;
>>  	struct intel_spi *ispi;
>>  	int ret;
>> @@ -746,7 +751,7 @@ struct intel_spi *intel_spi_probe(struct device *dev,
>>  	ispi->nor.write = intel_spi_write;
>>  	ispi->nor.erase = intel_spi_erase;
>>  
>> -	ret = spi_nor_scan(&ispi->nor, NULL, SPI_NOR_NORMAL);
>> +	ret = spi_nor_scan(&ispi->nor, NULL, &hwcaps);
>>  	if (ret) {
>>  		dev_info(dev, "failed to locate the chip\n");
>>  		return ERR_PTR(ret);
>> diff --git a/drivers/mtd/spi-nor/mtk-quadspi.c b/drivers/mtd/spi-nor/mtk-quadspi.c
>> index e661877c23de..615e258866f7 100644
>> --- a/drivers/mtd/spi-nor/mtk-quadspi.c
>> +++ b/drivers/mtd/spi-nor/mtk-quadspi.c
>> @@ -121,20 +121,20 @@ static void mt8173_nor_set_read_mode(struct mt8173_nor *mt8173_nor)
>>  {
>>  	struct spi_nor *nor = &mt8173_nor->nor;
>>  
>> -	switch (nor->flash_read) {
>> -	case SPI_NOR_FAST:
>> +	switch (nor->read_proto) {
>> +	case SNOR_PROTO_1_1_1:
>>  		writeb(nor->read_opcode, mt8173_nor->base +
>>  		       MTK_NOR_PRGDATA3_REG);
>>  		writeb(MTK_NOR_FAST_READ, mt8173_nor->base +
>>  		       MTK_NOR_CFG1_REG);
>>  		break;
>> -	case SPI_NOR_DUAL:
>> +	case SNOR_PROTO_1_1_2:
>>  		writeb(nor->read_opcode, mt8173_nor->base +
>>  		       MTK_NOR_PRGDATA3_REG);
>>  		writeb(MTK_NOR_DUAL_READ_EN, mt8173_nor->base +
>>  		       MTK_NOR_DUAL_REG);
>>  		break;
>> -	case SPI_NOR_QUAD:
>> +	case SNOR_PROTO_1_1_4:
>>  		writeb(nor->read_opcode, mt8173_nor->base +
>>  		       MTK_NOR_PRGDATA4_REG);
>>  		writeb(MTK_NOR_QUAD_READ_EN, mt8173_nor->base +
>> @@ -381,6 +381,12 @@ static int mt8173_nor_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf,
>>  static int mtk_nor_init(struct mt8173_nor *mt8173_nor,
>>  			struct device_node *flash_node)
>>  {
>> +	struct spi_nor_hwcaps hwcaps = {
>> +		.mask = (SNOR_HWCAPS_READ |
>> +			 SNOR_HWCAPS_READ_FAST |
>> +			 SNOR_HWCAPS_READ_1_1_2 |
>> +			 SNOR_HWCAPS_PP),
>> +	};
>>  	int ret;
>>  	struct spi_nor *nor;
>>  
>> @@ -399,7 +405,7 @@ static int mtk_nor_init(struct mt8173_nor *mt8173_nor,
>>  	nor->write_reg = mt8173_nor_write_reg;
>>  	nor->mtd.name = "mtk_nor";
>>  	/* initialized with NULL */
>> -	ret = spi_nor_scan(nor, NULL, SPI_NOR_DUAL);
>> +	ret = spi_nor_scan(nor, NULL, &hwcaps);
>>  	if (ret)
>>  		return ret;
>>  
>> diff --git a/drivers/mtd/spi-nor/nxp-spifi.c b/drivers/mtd/spi-nor/nxp-spifi.c
>> index 73a14f40928b..c5992e099542 100644
>> --- a/drivers/mtd/spi-nor/nxp-spifi.c
>> +++ b/drivers/mtd/spi-nor/nxp-spifi.c
>> @@ -240,13 +240,12 @@ static int nxp_spifi_erase(struct spi_nor *nor, loff_t offs)
>>  
>>  static int nxp_spifi_setup_memory_cmd(struct nxp_spifi *spifi)
>>  {
>> -	switch (spifi->nor.flash_read) {
>> -	case SPI_NOR_NORMAL:
>> -	case SPI_NOR_FAST:
>> +	switch (spifi->nor.read_proto) {
>> +	case SNOR_PROTO_1_1_1:
>>  		spifi->mcmd = SPIFI_CMD_FIELDFORM_ALL_SERIAL;
>>  		break;
>> -	case SPI_NOR_DUAL:
>> -	case SPI_NOR_QUAD:
>> +	case SNOR_PROTO_1_1_2:
>> +	case SNOR_PROTO_1_1_4:
>>  		spifi->mcmd = SPIFI_CMD_FIELDFORM_QUAD_DUAL_DATA;
>>  		break;
>>  	default:
>> @@ -274,7 +273,11 @@ static void nxp_spifi_dummy_id_read(struct spi_nor *nor)
>>  static int nxp_spifi_setup_flash(struct nxp_spifi *spifi,
>>  				 struct device_node *np)
>>  {
>> -	enum read_mode flash_read;
>> +	struct spi_nor_hwcaps hwcaps = {
>> +		.mask = (SNOR_HWCAPS_READ |
>> +			 SNOR_HWCAPS_READ_FAST |
>> +			 SNOR_HWCAPS_PP),
>> +	};
>>  	u32 ctrl, property;
>>  	u16 mode = 0;
>>  	int ret;
>> @@ -308,13 +311,12 @@ static int nxp_spifi_setup_flash(struct nxp_spifi *spifi,
>>  
>>  	if (mode & SPI_RX_DUAL) {
>>  		ctrl |= SPIFI_CTRL_DUAL;
>> -		flash_read = SPI_NOR_DUAL;
>> +		hwcaps.mask |= SNOR_HWCAPS_READ_1_1_2;
>>  	} else if (mode & SPI_RX_QUAD) {
>>  		ctrl &= ~SPIFI_CTRL_DUAL;
>> -		flash_read = SPI_NOR_QUAD;
>> +		hwcaps.mask |= SNOR_HWCAPS_READ_1_1_4;
>>  	} else {
>>  		ctrl |= SPIFI_CTRL_DUAL;
>> -		flash_read = SPI_NOR_NORMAL;
>>  	}
>>  
>>  	switch (mode & (SPI_CPHA | SPI_CPOL)) {
>> @@ -351,7 +353,7 @@ static int nxp_spifi_setup_flash(struct nxp_spifi *spifi,
>>  	 */
>>  	nxp_spifi_dummy_id_read(&spifi->nor);
>>  
>> -	ret = spi_nor_scan(&spifi->nor, NULL, flash_read);
>> +	ret = spi_nor_scan(&spifi->nor, NULL, &hwcaps);
>>  	if (ret) {
>>  		dev_err(spifi->dev, "device scan failed\n");
>>  		return ret;
>> diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c
>> index d3cb44b28490..cc443c6cbae8 100644
>> --- a/drivers/mtd/spi-nor/spi-nor.c
>> +++ b/drivers/mtd/spi-nor/spi-nor.c
>> @@ -150,24 +150,6 @@ static int read_cr(struct spi_nor *nor)
>>  }
>>  
>>  /*
>> - * Dummy Cycle calculation for different type of read.
>> - * It can be used to support more commands with
>> - * different dummy cycle requirements.
>> - */
>> -static inline int spi_nor_read_dummy_cycles(struct spi_nor *nor)
>> -{
>> -	switch (nor->flash_read) {
>> -	case SPI_NOR_FAST:
>> -	case SPI_NOR_DUAL:
>> -	case SPI_NOR_QUAD:
>> -		return 8;
>> -	case SPI_NOR_NORMAL:
>> -		return 0;
>> -	}
>> -	return 0;
>> -}
>> -
>> -/*
>>   * Write status register 1 byte
>>   * Returns negative if error occurred.
>>   */
>> @@ -221,6 +203,10 @@ static inline u8 spi_nor_convert_3to4_read(u8 opcode)
>>  		{ SPINOR_OP_READ_1_2_2,	SPINOR_OP_READ_1_2_2_4B },
>>  		{ SPINOR_OP_READ_1_1_4,	SPINOR_OP_READ_1_1_4_4B },
>>  		{ SPINOR_OP_READ_1_4_4,	SPINOR_OP_READ_1_4_4_4B },
>> +
>> +		{ SPINOR_OP_READ_1_1_1_DTR,	SPINOR_OP_READ_1_1_1_DTR_4B },
>> +		{ SPINOR_OP_READ_1_2_2_DTR,	SPINOR_OP_READ_1_2_2_DTR_4B },
>> +		{ SPINOR_OP_READ_1_4_4_DTR,	SPINOR_OP_READ_1_4_4_DTR_4B },
>>  	};
>>  
>>  	return spi_nor_convert_opcode(opcode, spi_nor_3to4_read,
>> @@ -1459,30 +1445,6 @@ static int spansion_quad_enable(struct spi_nor *nor)
>>  	return 0;
>>  }
>>  
>> -static int set_quad_mode(struct spi_nor *nor, const struct flash_info *info)
>> -{
>> -	int status;
>> -
>> -	switch (JEDEC_MFR(info)) {
>> -	case SNOR_MFR_MACRONIX:
>> -		status = macronix_quad_enable(nor);
>> -		if (status) {
>> -			dev_err(nor->dev, "Macronix quad-read not enabled\n");
>> -			return -EINVAL;
>> -		}
>> -		return status;
>> -	case SNOR_MFR_MICRON:
>> -		return 0;
>> -	default:
>> -		status = spansion_quad_enable(nor);
>> -		if (status) {
>> -			dev_err(nor->dev, "Spansion quad-read not enabled\n");
>> -			return -EINVAL;
>> -		}
>> -		return status;
>> -	}
>> -}
>> -
>>  static int spi_nor_check(struct spi_nor *nor)
>>  {
>>  	if (!nor->dev || !nor->read || !nor->write ||
>> @@ -1535,8 +1497,322 @@ static int s3an_nor_scan(const struct flash_info *info, struct spi_nor *nor)
>>  	return 0;
>>  }
>>  
>> -int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
>> +
>> +struct spi_nor_read_command {
>> +	u8			num_mode_clocks;
>> +	u8			num_wait_states;
>> +	u8			opcode;
>> +	enum spi_nor_protocol	proto;
>> +};
>> +
>> +struct spi_nor_pp_command {
>> +	u8			opcode;
>> +	enum spi_nor_protocol	proto;
>> +};
>> +
>> +enum spi_nor_read_command_index {
>> +	SNOR_CMD_READ,
>> +	SNOR_CMD_READ_FAST,
>> +	SNOR_CMD_READ_1_1_1_DTR,
>> +
>> +	/* Dual SPI */
>> +	SNOR_CMD_READ_1_1_2,
>> +	SNOR_CMD_READ_1_2_2,
>> +	SNOR_CMD_READ_2_2_2,
>> +	SNOR_CMD_READ_1_2_2_DTR,
>> +
>> +	/* Quad SPI */
>> +	SNOR_CMD_READ_1_1_4,
>> +	SNOR_CMD_READ_1_4_4,
>> +	SNOR_CMD_READ_4_4_4,
>> +	SNOR_CMD_READ_1_4_4_DTR,
>> +
>> +	/* Octo SPI */
>> +	SNOR_CMD_READ_1_1_8,
>> +	SNOR_CMD_READ_1_8_8,
>> +	SNOR_CMD_READ_8_8_8,
>> +	SNOR_CMD_READ_1_8_8_DTR,
>> +
>> +	SNOR_CMD_READ_MAX
>> +};
>> +
>> +enum spi_nor_pp_command_index {
>> +	SNOR_CMD_PP,
>> +
>> +	/* Quad SPI */
>> +	SNOR_CMD_PP_1_1_4,
>> +	SNOR_CMD_PP_1_4_4,
>> +	SNOR_CMD_PP_4_4_4,
>> +
>> +	/* Octo SPI */
>> +	SNOR_CMD_PP_1_1_8,
>> +	SNOR_CMD_PP_1_8_8,
>> +	SNOR_CMD_PP_8_8_8,
>> +
>> +	SNOR_CMD_PP_MAX
>> +};
>> +
>> +struct spi_nor_flash_parameter {
>> +	u64				size;
>> +	u32				page_size;
>> +
>> +	struct spi_nor_hwcaps		hwcaps;
>> +	struct spi_nor_read_command	reads[SNOR_CMD_READ_MAX];
>> +	struct spi_nor_pp_command	page_programs[SNOR_CMD_PP_MAX];
>> +
>> +	int (*quad_enable)(struct spi_nor *nor);
>> +};
>> +
>> +
>> +static inline 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)
>> +{
>> +	read->num_mode_clocks = num_mode_clocks;
>> +	read->num_wait_states = num_wait_states;
>> +	read->opcode = opcode;
>> +	read->proto = proto;
>> +}
>> +
>> +static inline void
>> +spi_nor_set_pp_settings(struct spi_nor_pp_command *pp,
>> +			u8 opcode,
>> +			enum spi_nor_protocol proto)
>> +{
>> +	pp->opcode = opcode;
>> +	pp->proto = proto;
>> +}
>> +
>> +static int spi_nor_init_params(struct spi_nor *nor,
>> +			       const struct flash_info *info,
>> +			       struct spi_nor_flash_parameter *params)
>> +{
>> +	/* Set legacy flash parameters as default. */
>> +	memset(params, 0, sizeof(*params));
>> +
>> +	/* Set SPI NOR sizes. */
>> +	params->size = info->sector_size * info->n_sectors;
>> +	params->page_size = info->page_size;
>> +
>> +	/* (Fast) Read settings. */
>> +	params->hwcaps.mask |= SNOR_HWCAPS_READ;
>> +	spi_nor_set_read_settings(&params->reads[SNOR_CMD_READ],
>> +				  0, 0, SPINOR_OP_READ,
>> +				  SNOR_PROTO_1_1_1);
>> +	if (!(info->flags & SPI_NOR_NO_FR)) {
>> +		params->hwcaps.mask |= SNOR_HWCAPS_READ_FAST;
>> +		spi_nor_set_read_settings(&params->reads[SNOR_CMD_READ_FAST],
>> +					  0, 8, SPINOR_OP_READ_FAST,
>> +					  SNOR_PROTO_1_1_1);
>> +	}
>> +	if (info->flags & SPI_NOR_DUAL_READ) {
>> +		params->hwcaps.mask |= SNOR_HWCAPS_READ_1_1_2;
>> +		spi_nor_set_read_settings(&params->reads[SNOR_CMD_READ_1_1_2],
>> +					  0, 8, SPINOR_OP_READ_1_1_2,
>> +					  SNOR_PROTO_1_1_2);
>> +	}
>> +	if (info->flags & SPI_NOR_QUAD_READ) {
>> +		params->hwcaps.mask |= SNOR_HWCAPS_READ_1_1_4;
>> +		spi_nor_set_read_settings(&params->reads[SNOR_CMD_READ_1_1_4],
>> +					  0, 8, SPINOR_OP_READ_1_1_4,
>> +					  SNOR_PROTO_1_1_4);
>> +	}
>> +
>> +	/* 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);
>> +
>> +	/* Select the procedure to set the Quad Enable bit. */
>> +	if (params->hwcaps.mask & (SNOR_HWCAPS_READ_QUAD |
>> +				   SNOR_HWCAPS_PP_QUAD)) {
>> +		switch (JEDEC_MFR(info)) {
>> +		case SNOR_MFR_MACRONIX:
>> +			params->quad_enable = macronix_quad_enable;
>> +			break;
>> +
>> +		case SNOR_MFR_MICRON:
>> +			break;
>> +
>> +		default:
>> +			params->quad_enable = spansion_quad_enable;
>> +			break;
>> +		}
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +static int spi_nor_hwcaps2cmd(u32 hwcaps)
>>  {
>> +	switch (hwcaps) {
>> +	case SNOR_HWCAPS_READ:			return SNOR_CMD_READ;
>> +	case SNOR_HWCAPS_READ_FAST:		return SNOR_CMD_READ_FAST;
>> +	case SNOR_HWCAPS_READ_1_1_1_DTR:	return SNOR_CMD_READ_1_1_1_DTR;
>> +	case SNOR_HWCAPS_READ_1_1_2:		return SNOR_CMD_READ_1_1_2;
>> +	case SNOR_HWCAPS_READ_1_2_2:		return SNOR_CMD_READ_1_2_2;
>> +	case SNOR_HWCAPS_READ_2_2_2:		return SNOR_CMD_READ_2_2_2;
>> +	case SNOR_HWCAPS_READ_1_2_2_DTR:	return SNOR_CMD_READ_1_2_2_DTR;
>> +	case SNOR_HWCAPS_READ_1_1_4:		return SNOR_CMD_READ_1_1_4;
>> +	case SNOR_HWCAPS_READ_1_4_4:		return SNOR_CMD_READ_1_4_4;
>> +	case SNOR_HWCAPS_READ_4_4_4:		return SNOR_CMD_READ_4_4_4;
>> +	case SNOR_HWCAPS_READ_1_4_4_DTR:	return SNOR_CMD_READ_1_4_4_DTR;
>> +	case SNOR_HWCAPS_READ_1_1_8:		return SNOR_CMD_READ_1_1_8;
>> +	case SNOR_HWCAPS_READ_1_8_8:		return SNOR_CMD_READ_1_8_8;
>> +	case SNOR_HWCAPS_READ_8_8_8:		return SNOR_CMD_READ_8_8_8;
>> +	case SNOR_HWCAPS_READ_1_8_8_DTR:	return SNOR_CMD_READ_1_8_8_DTR;
>> +
>> +	case SNOR_HWCAPS_PP:			return SNOR_CMD_PP;
>> +	case SNOR_HWCAPS_PP_1_1_4:		return SNOR_CMD_PP_1_1_4;
>> +	case SNOR_HWCAPS_PP_1_4_4:		return SNOR_CMD_PP_1_4_4;
>> +	case SNOR_HWCAPS_PP_4_4_4:		return SNOR_CMD_PP_4_4_4;
>> +	case SNOR_HWCAPS_PP_1_1_8:		return SNOR_CMD_PP_1_1_8;
>> +	case SNOR_HWCAPS_PP_1_8_8:		return SNOR_CMD_PP_1_8_8;
>> +	case SNOR_HWCAPS_PP_8_8_8:		return SNOR_CMD_PP_8_8_8;
>> +	}
>> +
>> +	return -EINVAL;
>> +}
>> +
>> +static int spi_nor_select_read(struct spi_nor *nor,
>> +			       const struct spi_nor_flash_parameter *params,
>> +			       u32 shared_hwcaps)
>> +{
>> +	int cmd, best_match = fls(shared_hwcaps & SNOR_HWCAPS_READ_MASK) - 1;
>> +	const struct spi_nor_read_command *read;
>> +
>> +	if (best_match < 0)
>> +		return -EINVAL;
>> +
>> +	cmd = spi_nor_hwcaps2cmd(BIT(best_match));
>> +	if (cmd < 0)
>> +		return -EINVAL;
>> +
>> +	read = &params->reads[cmd];
>> +	nor->read_opcode = read->opcode;
>> +	nor->read_proto = read->proto;
>> +
>> +	/*
>> +	 * In the spi-nor framework, we don't need to make the difference
>> +	 * between mode clock cycles and wait state clock cycles.
>> +	 * Indeed, the value of the mode clock cycles is used by a QSPI
>> +	 * flash memory to know whether it should enter or leave its 0-4-4
>> +	 * (Continuous Read / XIP) mode.
>> +	 * eXecution In Place is out of the scope of the mtd sub-system.
>> +	 * Hence we choose to merge both mode and wait state clock cycles
>> +	 * into the so called dummy clock cycles.
>> +	 */
>> +	nor->read_dummy = read->num_mode_clocks + read->num_wait_states;
>> +	return 0;
>> +}
>> +
>> +static int spi_nor_select_pp(struct spi_nor *nor,
>> +			     const struct spi_nor_flash_parameter *params,
>> +			     u32 shared_hwcaps)
>> +{
>> +	int cmd, best_match = fls(shared_hwcaps & SNOR_HWCAPS_PP_MASK) - 1;
>> +	const struct spi_nor_pp_command *pp;
>> +
>> +	if (best_match < 0)
>> +		return -EINVAL;
>> +
>> +	cmd = spi_nor_hwcaps2cmd(BIT(best_match));
>> +	if (cmd < 0)
>> +		return -EINVAL;
>> +
>> +	pp = &params->page_programs[cmd];
>> +	nor->program_opcode = pp->opcode;
>> +	nor->write_proto = pp->proto;
>> +	return 0;
>> +}
>> +
>> +static int spi_nor_select_erase(struct spi_nor *nor,
>> +				const struct flash_info *info)
>> +{
>> +	struct mtd_info *mtd = &nor->mtd;
>> +
>> +#ifdef CONFIG_MTD_SPI_NOR_USE_4K_SECTORS
>> +	/* prefer "small sector" erase if possible */
>> +	if (info->flags & SECT_4K) {
>> +		nor->erase_opcode = SPINOR_OP_BE_4K;
>> +		mtd->erasesize = 4096;
>> +	} else if (info->flags & SECT_4K_PMC) {
>> +		nor->erase_opcode = SPINOR_OP_BE_4K_PMC;
>> +		mtd->erasesize = 4096;
>> +	} else
>> +#endif
>> +	{
>> +		nor->erase_opcode = SPINOR_OP_SE;
>> +		mtd->erasesize = info->sector_size;
>> +	}
>> +	return 0;
>> +}
>> +
>> +static int spi_nor_setup(struct spi_nor *nor, const struct flash_info *info,
>> +			 const struct spi_nor_flash_parameter *params,
>> +			 const struct spi_nor_hwcaps *hwcaps)
>> +{
>> +	u32 ignored_mask, shared_mask;
>> +	bool enable_quad_io;
>> +	int err;
>> +
>> +	/*
>> +	 * Keep only the hardware capabilities supported by both the SPI
>> +	 * controller and the SPI flash memory.
>> +	 */
>> +	shared_mask = hwcaps->mask & params->hwcaps.mask;
>> +
>> +	/* SPI protocol classes N-N-N are not supported yet. */
>> +	ignored_mask = (SNOR_HWCAPS_READ_2_2_2 |
>> +			SNOR_HWCAPS_READ_4_4_4 |
>> +			SNOR_HWCAPS_READ_8_8_8 |
>> +			SNOR_HWCAPS_PP_4_4_4 |
>> +			SNOR_HWCAPS_PP_8_8_8);
>> +	if (shared_mask & ignored_mask) {
>> +		dev_dbg(nor->dev,
>> +			"SPI protocol classes N-N-N are not supported yet.\n");
>> +		shared_mask &= ~ignored_mask;
>> +	}
>> +
>> +	/* Select the (Fast) Read command. */
>> +	err = spi_nor_select_read(nor, params, shared_mask);
>> +	if (err) {
>> +		dev_err(nor->dev, "invalid (fast) read\n");
>> +		return err;
>> +	}
>> +
>> +	/* Select the Page Program command. */
>> +	err = spi_nor_select_pp(nor, params, shared_mask);
>> +	if (err) {
>> +		dev_err(nor->dev, "invalid page program\n");
>> +		return err;
>> +	}
>> +
>> +	/* Select the Sector Erase command. */
>> +	err = spi_nor_select_erase(nor, info);
>> +	if (err) {
>> +		dev_err(nor->dev, "invalid sector/block erase\n");
>> +		return err;
>> +	}
>> +
>> +	/* Enable Quad I/O if needed. */
>> +	enable_quad_io = (spi_nor_get_protocol_width(nor->read_proto) == 4 ||
>> +			  spi_nor_get_protocol_width(nor->write_proto) == 4);
>> +	if (enable_quad_io && params->quad_enable)
>> +		nor->flash_quad_enable = params->quad_enable;
>> +	else
>> +		nor->flash_quad_enable = NULL;
>> +
>> +	return 0;
>> +}
>> +
>> +int spi_nor_scan(struct spi_nor *nor, const char *name,
>> +		 const struct spi_nor_hwcaps *hwcaps)
>> +{
>> +	struct spi_nor_flash_parameter params;
>>  	const struct flash_info *info = NULL;
>>  	struct device *dev = nor->dev;
>>  	struct mtd_info *mtd = &nor->mtd;
>> @@ -1548,6 +1824,11 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
>>  	if (ret)
>>  		return ret;
>>  
>> +	/* Reset SPI protocol for all commands */
>> +	nor->reg_proto = SNOR_PROTO_1_1_1;
>> +	nor->read_proto = SNOR_PROTO_1_1_1;
>> +	nor->write_proto = SNOR_PROTO_1_1_1;
>> +
>>  	if (name)
>>  		info = spi_nor_match_id(name);
>>  	/* Try to auto-detect if chip name wasn't specified or not found */
>> @@ -1580,6 +1861,11 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
>>  		}
>>  	}
>>  
>> +	/* Parse the Serial Flash Discoverable Parameters table */
>> +	ret = spi_nor_init_params(nor, info, &params);
>> +	if (ret)
>> +		return ret;
>> +
>>  	mutex_init(&nor->lock);
>>  
>>  	/*
>> @@ -1610,7 +1896,7 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
>>  	mtd->type = MTD_NORFLASH;
>>  	mtd->writesize = 1;
>>  	mtd->flags = MTD_CAP_NORFLASH;
>> -	mtd->size = info->sector_size * info->n_sectors;
>> +	mtd->size = params.size;
>>  	mtd->_erase = spi_nor_erase;
>>  	mtd->_read = spi_nor_read;
>>  
>> @@ -1641,76 +1927,47 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
>>  	if (info->flags & NO_CHIP_ERASE)
>>  		nor->flags |= SNOR_F_NO_OP_CHIP_ERASE;
>>  
>> -#ifdef CONFIG_MTD_SPI_NOR_USE_4K_SECTORS
>> -	/* prefer "small sector" erase if possible */
>> -	if (info->flags & SECT_4K) {
>> -		nor->erase_opcode = SPINOR_OP_BE_4K;
>> -		mtd->erasesize = 4096;
>> -	} else if (info->flags & SECT_4K_PMC) {
>> -		nor->erase_opcode = SPINOR_OP_BE_4K_PMC;
>> -		mtd->erasesize = 4096;
>> -	} else
>> -#endif
>> -	{
>> -		nor->erase_opcode = SPINOR_OP_SE;
>> -		mtd->erasesize = info->sector_size;
>> -	}
>> -
>>  	if (info->flags & SPI_NOR_NO_ERASE)
>>  		mtd->flags |= MTD_NO_ERASE;
>>  
>>  	mtd->dev.parent = dev;
>> -	nor->page_size = info->page_size;
>> +	nor->page_size = params.page_size;
>>  	mtd->writebufsize = nor->page_size;
>>  
>>  	if (np) {
>>  		/* If we were instantiated by DT, use it */
>>  		if (of_property_read_bool(np, "m25p,fast-read"))
>> -			nor->flash_read = SPI_NOR_FAST;
>> +			params.hwcaps.mask |= SNOR_HWCAPS_READ_FAST;
>>  		else
>> -			nor->flash_read = SPI_NOR_NORMAL;
>> +			params.hwcaps.mask &= ~SNOR_HWCAPS_READ_FAST;
>>  	} else {
>>  		/* If we weren't instantiated by DT, default to fast-read */
>> -		nor->flash_read = SPI_NOR_FAST;
>> +		params.hwcaps.mask |= SNOR_HWCAPS_READ_FAST;
>>  	}
>>  
>>  	/* Some devices cannot do fast-read, no matter what DT tells us */
>>  	if (info->flags & SPI_NOR_NO_FR)
>> -		nor->flash_read = SPI_NOR_NORMAL;
>> +		params.hwcaps.mask &= ~SNOR_HWCAPS_READ_FAST;
>> +
>> +	/*
>> +	 * Configure the SPI memory:
>> +	 * - select op codes for (Fast) Read, Page Program and Sector Erase.
>> +	 * - set the number of dummy cycles (mode cycles + wait states).
>> +	 * - set the SPI protocols for register and memory accesses.
>> +	 * - set the Quad Enable bit if needed (required by SPI x-y-4 protos).
>> +	 */
>> +	ret = spi_nor_setup(nor, info, &params, hwcaps);
>> +	if (ret)
>> +		return ret;
>>  
>> -	/* Quad/Dual-read mode takes precedence over fast/normal */
>> -	if (mode == SPI_NOR_QUAD && info->flags & SPI_NOR_QUAD_READ) {
>> -		ret = set_quad_mode(nor, info);
>> +	if (nor->flash_quad_enable) {
>> +		ret = nor->flash_quad_enable(nor);
>>  		if (ret) {
>>  			dev_err(dev, "quad mode not supported\n");
>>  			return ret;
>>  		}
>> -		nor->flash_read = SPI_NOR_QUAD;
>> -	} else if (mode == SPI_NOR_DUAL && info->flags & SPI_NOR_DUAL_READ) {
>> -		nor->flash_read = SPI_NOR_DUAL;
>>  	}
>>  
>> -	/* Default commands */
>> -	switch (nor->flash_read) {
>> -	case SPI_NOR_QUAD:
>> -		nor->read_opcode = SPINOR_OP_READ_1_1_4;
>> -		break;
>> -	case SPI_NOR_DUAL:
>> -		nor->read_opcode = SPINOR_OP_READ_1_1_2;
>> -		break;
>> -	case SPI_NOR_FAST:
>> -		nor->read_opcode = SPINOR_OP_READ_FAST;
>> -		break;
>> -	case SPI_NOR_NORMAL:
>> -		nor->read_opcode = SPINOR_OP_READ;
>> -		break;
>> -	default:
>> -		dev_err(dev, "No Read opcode defined\n");
>> -		return -EINVAL;
>> -	}
>> -
>> -	nor->program_opcode = SPINOR_OP_PP;
>> -
>>  	if (info->addr_width)
>>  		nor->addr_width = info->addr_width;
>>  	else if (mtd->size > 0x1000000) {
>> @@ -1731,8 +1988,6 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
>>  		return -EINVAL;
>>  	}
>>  
>> -	nor->read_dummy = spi_nor_read_dummy_cycles(nor);
>> -
>>  	if (info->flags & SPI_S3AN) {
>>  		ret = s3an_nor_scan(info, nor);
>>  		if (ret)
>> diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h
>> index f2a718030476..732ee6cd5330 100644
>> --- a/include/linux/mtd/spi-nor.h
>> +++ b/include/linux/mtd/spi-nor.h
>> @@ -73,6 +73,15 @@
>>  #define SPINOR_OP_BE_32K_4B	0x5c	/* Erase 32KiB block */
>>  #define SPINOR_OP_SE_4B		0xdc	/* Sector erase (usually 64KiB) */
>>  
>> +/* Double Transfer Rate opcodes - defined in JEDEC JESD216B. */
>> +#define SPINOR_OP_READ_1_1_1_DTR	0x0d
>> +#define SPINOR_OP_READ_1_2_2_DTR	0xbd
>> +#define SPINOR_OP_READ_1_4_4_DTR	0xed
>> +
>> +#define SPINOR_OP_READ_1_1_1_DTR_4B	0x0e
>> +#define SPINOR_OP_READ_1_2_2_DTR_4B	0xbe
>> +#define SPINOR_OP_READ_1_4_4_DTR_4B	0xee
>> +
>>  /* Used for SST flashes only. */
>>  #define SPINOR_OP_BP		0x02	/* Byte program */
>>  #define SPINOR_OP_WRDI		0x04	/* Write disable */
>> @@ -119,13 +128,75 @@
>>  /* Configuration Register bits. */
>>  #define CR_QUAD_EN_SPAN		BIT(1)	/* Spansion Quad I/O */
>>  
>> -enum read_mode {
>> -	SPI_NOR_NORMAL = 0,
>> -	SPI_NOR_FAST,
>> -	SPI_NOR_DUAL,
>> -	SPI_NOR_QUAD,
>> +
>> +/* Supported SPI protocols */
>> +#define SNOR_PROTO_WIDTH_MASK	GENMASK(7, 0)
>> +
>> +#define SNOR_PROTO_CLASS_MASK	GENMASK(9, 8)
>> +#define SNOR_PROTO_CLASS_1_1_N	(0x0u << 8)
>> +#define SNOR_PROTO_CLASS_1_N_N	(0x1u << 8)
>> +#define SNOR_PROTO_CLASS_N_N_N	(0x2u << 8)
>> +
>> +#define SNOR_PROTO_IS_DTR	BIT(10)	/* Double Transfer Rate */
>> +
>> +#define SNOR_PROTO_STR(_pclass, _pwidth) \
>> +	((_pclass) | (_pwidth))
>> +#define SNOR_PROTO_DTR(_pclass, _pwidth) \
>> +	(SNOR_PROTO_IS_DTR | (_pclass) | (_pwidth))
>> +
>> +enum spi_nor_protocol {
>> +	SNOR_PROTO_1_1_1 = SNOR_PROTO_STR(SNOR_PROTO_CLASS_1_1_N, 1),
>> +	SNOR_PROTO_1_1_2 = SNOR_PROTO_STR(SNOR_PROTO_CLASS_1_1_N, 2),
>> +	SNOR_PROTO_1_1_4 = SNOR_PROTO_STR(SNOR_PROTO_CLASS_1_1_N, 4),
>> +	SNOR_PROTO_1_1_8 = SNOR_PROTO_STR(SNOR_PROTO_CLASS_1_1_N, 8),
>> +	SNOR_PROTO_1_2_2 = SNOR_PROTO_STR(SNOR_PROTO_CLASS_1_N_N, 2),
>> +	SNOR_PROTO_1_4_4 = SNOR_PROTO_STR(SNOR_PROTO_CLASS_1_N_N, 4),
>> +	SNOR_PROTO_1_8_8 = SNOR_PROTO_STR(SNOR_PROTO_CLASS_1_N_N, 8),
>> +	SNOR_PROTO_2_2_2 = SNOR_PROTO_STR(SNOR_PROTO_CLASS_N_N_N, 2),
>> +	SNOR_PROTO_4_4_4 = SNOR_PROTO_STR(SNOR_PROTO_CLASS_N_N_N, 4),
>> +	SNOR_PROTO_8_8_8 = SNOR_PROTO_STR(SNOR_PROTO_CLASS_N_N_N, 8),
>> +
>> +	SNOR_PROTO_1_1_1_DTR = SNOR_PROTO_DTR(SNOR_PROTO_CLASS_1_1_N, 1),
>> +	SNOR_PROTO_1_2_2_DTR = SNOR_PROTO_DTR(SNOR_PROTO_CLASS_1_N_N, 2),
>> +	SNOR_PROTO_1_4_4_DTR = SNOR_PROTO_DTR(SNOR_PROTO_CLASS_1_N_N, 4),
>> +	SNOR_PROTO_1_8_8_DTR = SNOR_PROTO_DTR(SNOR_PROTO_CLASS_1_N_N, 8),
>>  };
>>  
>> +static inline bool spi_nor_protocol_is_dtr(enum spi_nor_protocol proto)
>> +{
>> +	return (proto & SNOR_PROTO_IS_DTR) == SNOR_PROTO_IS_DTR;
>> +}
>> +
>> +static inline u32 spi_nor_get_protocol_class(enum spi_nor_protocol proto)
>> +{
>> +	return proto & SNOR_PROTO_CLASS_MASK;
>> +}
>> +
>> +static inline u8 spi_nor_get_protocol_width(enum spi_nor_protocol proto)
>> +{
>> +	return proto & SNOR_PROTO_WIDTH_MASK;
>> +}
>> +
>> +static inline u8 spi_nor_get_protocol_inst_width(enum spi_nor_protocol proto)
>> +{
>> +	return (spi_nor_get_protocol_class(proto) == SNOR_PROTO_CLASS_N_N_N) ?
>> +		spi_nor_get_protocol_width(proto) :
>> +		1u;
>> +}
>> +
>> +static inline u8 spi_nor_get_protocol_addr_width(enum spi_nor_protocol proto)
>> +{
>> +	return (spi_nor_get_protocol_class(proto) != SNOR_PROTO_CLASS_1_1_N) ?
>> +		spi_nor_get_protocol_width(proto) :
>> +		1u;
>> +}
>> +
>> +static inline u8 spi_nor_get_protocol_data_width(enum spi_nor_protocol proto)
>> +{
>> +	return spi_nor_get_protocol_width(proto);
>> +}
>> +
>> +
>>  #define SPI_NOR_MAX_CMD_SIZE	8
>>  enum spi_nor_ops {
>>  	SPI_NOR_OPS_READ = 0,
>> @@ -154,9 +225,11 @@ enum spi_nor_option_flags {
>>   * @read_opcode:	the read opcode
>>   * @read_dummy:		the dummy needed by the read operation
>>   * @program_opcode:	the program opcode
>> - * @flash_read:		the mode of the read
>>   * @sst_write_second:	used by the SST write operation
>>   * @flags:		flag options for the current SPI-NOR (SNOR_F_*)
>> + * @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
>>   * @cmd_buf:		used by the write_reg
>>   * @prepare:		[OPTIONAL] do some preparations for the
>>   *			read/write/erase/lock/unlock operations
>> @@ -173,6 +246,7 @@ enum spi_nor_option_flags {
>>   * @flash_unlock:	[FLASH-SPECIFIC] unlock a region of the SPI NOR
>>   * @flash_is_locked:	[FLASH-SPECIFIC] check if a region of the SPI NOR is
>>   *			completely locked
>> + * @flash_quad_enable:	[FLASH-SPECIFIC] set the Quad Enable bit of the SPI NOR
>>   * @priv:		the private data
>>   */
>>  struct spi_nor {
>> @@ -185,7 +259,9 @@ struct spi_nor {
>>  	u8			read_opcode;
>>  	u8			read_dummy;
>>  	u8			program_opcode;
>> -	enum read_mode		flash_read;
>> +	enum spi_nor_protocol	read_proto;
>> +	enum spi_nor_protocol	write_proto;
>> +	enum spi_nor_protocol	reg_proto;
>>  	bool			sst_write_second;
>>  	u32			flags;
>>  	u8			cmd_buf[SPI_NOR_MAX_CMD_SIZE];
>> @@ -204,6 +280,7 @@ struct spi_nor {
>>  	int (*flash_lock)(struct spi_nor *nor, loff_t ofs, uint64_t len);
>>  	int (*flash_unlock)(struct spi_nor *nor, loff_t ofs, uint64_t len);
>>  	int (*flash_is_locked)(struct spi_nor *nor, loff_t ofs, uint64_t len);
>> +	int (*flash_quad_enable)(struct spi_nor *nor);
>>  
>>  	void *priv;
>>  };
>> @@ -219,11 +296,73 @@ static inline struct device_node *spi_nor_get_flash_node(struct spi_nor *nor)
>>  	return mtd_get_of_node(&nor->mtd);
>>  }
>>  
>> +
>> +/**
>> + * struct spi_nor_hwcaps - Structure for describing the hardware capabilies
>> + * supported by the SPI controller (bus master).
>> + * @mask:		the bitmask listing all the supported hw capabilies
>> + */
>> +struct spi_nor_hwcaps {
>> +	u32	mask;
>> +};
>> +
>> +/*
>> + *(Fast) Read capabilities.
>> + * MUST be ordered by priority: the higher bit position, the higher priority.
>> + * As a matter of performances, it is relevant to use Octo SPI protocols first,
>> + * then Quad SPI protocols before Dual SPI protocols, Fast Read and lastly
>> + * (Slow) Read.
>> + */
>> +#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)
>> +
>> +#define SNOR_HWCAPS_READ_DUAL		GENMASK(7, 4)
>> +#define SNOR_HWCAPS_READ_1_1_2		BIT(4)
>> +#define SNOR_HWCAPS_READ_1_2_2		BIT(5)
>> +#define SNOR_HWCAPS_READ_2_2_2		BIT(6)
>> +#define SNOR_HWCAPS_READ_1_2_2_DTR	BIT(7)
>> +
>> +#define SNOR_HWCAPS_READ_QUAD		GENMASK(11, 8)
>> +#define SNOR_HWCAPS_READ_1_1_4		BIT(8)
>> +#define SNOR_HWCAPS_READ_1_4_4		BIT(9)
>> +#define SNOR_HWCAPS_READ_4_4_4		BIT(10)
>> +#define SNOR_HWCAPS_READ_1_4_4_DTR	BIT(11)
>> +
>> +#define SNOR_HWCPAS_READ_OCTO		GENMASK(15, 12)
>> +#define SNOR_HWCAPS_READ_1_1_8		BIT(12)
>> +#define SNOR_HWCAPS_READ_1_8_8		BIT(13)
>> +#define SNOR_HWCAPS_READ_8_8_8		BIT(14)
>> +#define SNOR_HWCAPS_READ_1_8_8_DTR	BIT(15)
>> +
>> +/*
>> + * Page Program capabilities.
>> + * MUST be ordered by priority: the higher bit position, the higher priority.
>> + * Like (Fast) Read capabilities, Octo/Quad SPI protocols are preferred to the
>> + * legacy SPI 1-1-1 protocol.
>> + * Note that Dual Page Programs are not supported because there is no existing
>> + * 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_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_OCTO	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)
>> +
>>  /**
>>   * spi_nor_scan() - scan the SPI NOR
>>   * @nor:	the spi_nor structure
>>   * @name:	the chip type name
>> - * @mode:	the read mode supported by the driver
>> + * @hwcaps:	the hardware capabilities supported by the controller driver
>>   *
>>   * The drivers can use this fuction to scan the SPI NOR.
>>   * In the scanning, it will try to get all the necessary information to
>> @@ -233,6 +372,7 @@ static inline struct device_node *spi_nor_get_flash_node(struct spi_nor *nor)
>>   *
>>   * Return: 0 for success, others for failure.
>>   */
>> -int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode);
>> +int spi_nor_scan(struct spi_nor *nor, const char *name,
>> +		 const struct spi_nor_hwcaps *hwcaps);
>>  
>>  #endif
>>
> 
> 
> ______________________________________________________
> Linux MTD discussion mailing list
> http://lists.infradead.org/mailman/listinfo/linux-mtd/
> 

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

* Re: [PATCH v5 1/6] mtd: spi-nor: introduce more SPI protocols and the Dual Transfer Mode
  2017-03-23 19:10     ` Cyrille Pitchen
@ 2017-03-24 10:03       ` Cédric Le Goater
  2017-03-24 11:39         ` Cyrille Pitchen
  0 siblings, 1 reply; 27+ messages in thread
From: Cédric Le Goater @ 2017-03-24 10:03 UTC (permalink / raw)
  To: Cyrille Pitchen, Cyrille Pitchen, marek.vasut, linux-mtd, jartur,
	kdasu.kdev, mar.krzeminski
  Cc: boris.brezillon, richard, nicolas.ferre, linux-kernel,
	computersforpeace, dwmw2

On 03/23/2017 08:10 PM, Cyrille Pitchen wrote:
> Hi Cédic,
> 
> Le 23/03/2017 à 16:13, Cédric Le Goater a écrit :
>> On 03/23/2017 12:33 AM, Cyrille Pitchen wrote:
>>> This patch changes the prototype of spi_nor_scan(): its 3rd parameter
>>> is replaced by a 'struct spi_nor_hwcaps' pointer, which tells the spi-nor
>>> framework about the actual hardware capabilities supported by the SPI
>>> controller and its driver.
>>>
>>> Besides, this patch also introduces a new 'struct spi_nor_flash_parameter'
>>> telling the spi-nor framework about the hardware capabilities supported by
>>> the SPI flash memory and the associated settings required to use those
>>> hardware caps.
>>>
>>> Currently the 'struct spi_nor_flash_parameter' is filled with legacy
>>> values but a later patch will allow to fill it dynamically by reading the
>>> JESD216 Serial Flash Discoverable Parameter (SFDP) tables from the SPI
>>> memory.
>>>
>>> With both structures, the spi-nor framework can now compute the best
>>> match between hardware caps supported by both the (Q)SPI memory and
>>> controller hence selecting the relevant SPI protocols and op codes for
>>> (Fast) Read, Page Program and Sector Erase operations.
>>>
>>> The 'struct spi_nor_flash_parameter' also provides the spi-nor framework
>>> with the number of dummy cycles to be used with each Fast Read commands
>>> and the erase block size associated to the erase block op codes.
>>>
>>> Finally the 'struct spi_nor_flash_parameter', through the optional
>>> .enable_quad_io() hook, tells the spi-nor framework how to set the Quad
> 
> .enable_quad_io() was renamed into .quad_enable()
> 
>>> Enable (QE) bit of the QSPI memory to enable its Quad SPI features.
>>
>> The Aspeed controller only supports Dual I/O and a helper similar 
>> to the one for Quad I/O would be needed to setup the chip for 
>> multiple I/O: 
>>
>> 	int (*dual_enable)(struct spi_nor *nor);
>>
>> Is the approach correct ? or maybe rename the current 'quad_enable()'
>> to 'multiple_enable()' and add an extra parameter.
>>
> 
> No, nor->flash_quad_enable() [ = params->quad_enable() ] is a flash
> specific handler as described in spi-nor.h and like other flash specific
> handlers such as flash_lock(), flash_unlock() or flash_is_locked(), is
> only used internally in spi-nor.c: spi-nor controller drivers like
> aspeed-smc.c should not use those handlers at all.

I was talking about adding a spi-nor handler to set up the chip for 
dual I/O because today we only have one for quad I/O, and some
controllers (like the Aspeed) do not support Quad. So the only 
fast I/O we can do today for these controllers is dual I/O data 
(SPI_NOR_DUAL)

> the choice of the nor->flash_quad_enable() handler depends only on the
> memory manufacturer (and the memory part):
> - (none): Micron
> - macronix_quad_enable: Macronix
> - spansion_quad_enable: Spansion, Winbond, ...
> - spansion_new_quad_enable: Spansion (latest memories), ...
> - sr2_bit7_quad_enable: ??? (defined in the JESD216B specification)

yes. Some Micron have a VCONF register to set multiple I/O also.
 
> The purpose of those functions is to implement the vendor specific
> procedure to set the so called "Quad Enable" (QE) bit in some Status
> Register of the SPI NOR memory.
> 
> Indeed, most QSPI memories are pin to pin compatible with legacy SPI
> memories. 2 pins of those memories were dedicated to the Write Protect
> (WP) and Reset/Hold (RST) functions. Hence before using any Quad SPI
> commands, almost all QSPI memories (Micron being the only exception I
> know) require us to set their QE bit so the WP and RST pins are
> reassigned to functions IO2 and IO3, the 3rd and 4th IO lines needed by
> SPI 1-1-4 and SPI 1-4-4 protocols. Then the Write Protect and Hold/Reset
> functions are disabled.
> 
> From a software point of view, there is nothing to do before using the
> SPI 1-1-2 or SPI 1-2-2 protocols. As a matter of fact, with those
> protocols, MISO and MOSI pins are simply reassigned to functions IO0 and
> IO1.
> 
> 
> Back to the SPI flash controller, if the hardware needs to be configured
> in some way to use any Dual or Quad SPI protocols, it has to be done
> before calling spi_nor_scan() or more likely directly inside the
> controller driver specific implementation of nor->read(), nor->write().

yes.

> With nor->read(), the driver has to check the value of nor->read_proto
> to know the actual number of I/O lines used during Instruction (x),
> Address/Dummy (y) and Data (z) clock cycles: nor->read_proto provides
> the driver with the SPI x-y-z protocol to be used.
> 
> Also nor->write() has to check the value of nor->write_proto.
> 
> [ nor->erase() has to check the value of nor->reg_proto: I removed
> nor->erase_proto since it always had the same value as nor->reg_proto. ]
> 
> Both nor->write() and nor->read are controller driver specific: they
> must be implemented by the controller driver and set before calling
> spi_nor_scan().
> 
> Then nor->read_proto and nor->write_proto are chosen from spi_nor_scan()
> based on the actual hardware capabilities shared by both the SPI memory
> and controller. The SPI controller driver tells the spi-nor framework
> which SPI protocols it supports or wants to use by setting the new
> 'struct spi_nor_hwcaps' argument of spi_nor_scan() accordingly.
> 
> 
> You can have a look at the atmel-quadspi.c driver to have an example of
> what to do to support the SPI 1-2-2 or SPI 1-4-4 protocols at the
> controller side.
> 
> I've updated the Atmel Quad SPI driver since I'm the maintainer of this
> driver so I know the exact hardware capabilities of this controller but
> I don't have such a knowledge for Quad SPI controllers of other vendors.
> 
> Please note that patch is conservative: if a controller driver like the
> Aspeed one currently doesn't support the Quad or Dual SPI protocols, the
> patch doesn't enable them in hwcaps.mask.
> 
> Maintainers of the controller drivers will have to, if they want, extend
> their driver to add the support of new SPI protocols, otherwise those
> drivers will keep on working exactly as they used to do before this
> series. For instance, a controller driver which used SPI_NOR_QUAD before
> still uses the SPI 1-1-4 protocol.

Yes. I have some patches adding DUAL support to Aspeed but I will rebase 
on your patchset when it is merged because they currently conflict.

Thanks for the detailed explanations !

C. 


> Best regards,
> 
> Cyrille
> 
> 
>> I have a bunch of patches queued for Dual I/O data support but I 
>> think they will conflict with your patches. I will wait for this 
>> one to be merged. Then, I can look at Dual I/O address + data 
>> support.
>>
>> Thanks,
>>
>> C.     
>>
>>
>>> Signed-off-by: Cyrille Pitchen <cyrille.pitchen@atmel.com>
>>> ---
>>>  drivers/mtd/devices/m25p80.c          |  16 +-
>>>  drivers/mtd/spi-nor/aspeed-smc.c      |  23 +-
>>>  drivers/mtd/spi-nor/atmel-quadspi.c   |  80 +++---
>>>  drivers/mtd/spi-nor/cadence-quadspi.c |  18 +-
>>>  drivers/mtd/spi-nor/fsl-quadspi.c     |   8 +-
>>>  drivers/mtd/spi-nor/hisi-sfc.c        |  31 ++-
>>>  drivers/mtd/spi-nor/intel-spi.c       |   7 +-
>>>  drivers/mtd/spi-nor/mtk-quadspi.c     |  16 +-
>>>  drivers/mtd/spi-nor/nxp-spifi.c       |  22 +-
>>>  drivers/mtd/spi-nor/spi-nor.c         | 441 +++++++++++++++++++++++++++-------
>>>  include/linux/mtd/spi-nor.h           | 158 +++++++++++-
>>>  11 files changed, 643 insertions(+), 177 deletions(-)
>>>
>>> diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c
>>> index c4df3b1bded0..68986a26c8fe 100644
>>> --- a/drivers/mtd/devices/m25p80.c
>>> +++ b/drivers/mtd/devices/m25p80.c
>>> @@ -111,10 +111,10 @@ static ssize_t m25p80_write(struct spi_nor *nor, loff_t to, size_t len,
>>>  
>>>  static inline unsigned int m25p80_rx_nbits(struct spi_nor *nor)
>>>  {
>>> -	switch (nor->flash_read) {
>>> -	case SPI_NOR_DUAL:
>>> +	switch (nor->read_proto) {
>>> +	case SNOR_PROTO_1_1_2:
>>>  		return 2;
>>> -	case SPI_NOR_QUAD:
>>> +	case SNOR_PROTO_1_1_4:
>>>  		return 4;
>>>  	default:
>>>  		return 0;
>>> @@ -196,7 +196,9 @@ static int m25p_probe(struct spi_device *spi)
>>>  	struct flash_platform_data	*data;
>>>  	struct m25p *flash;
>>>  	struct spi_nor *nor;
>>> -	enum read_mode mode = SPI_NOR_NORMAL;
>>> +	struct spi_nor_hwcaps hwcaps = {
>>> +		.mask = (SNOR_HWCAPS_READ | SNOR_HWCAPS_PP),
>>> +	};
>>>  	char *flash_name;
>>>  	int ret;
>>>  
>>> @@ -222,9 +224,9 @@ static int m25p_probe(struct spi_device *spi)
>>>  	flash->spi = spi;
>>>  
>>>  	if (spi->mode & SPI_RX_QUAD)
>>> -		mode = SPI_NOR_QUAD;
>>> +		hwcaps.mask |= SNOR_HWCAPS_READ_1_1_4;
>>>  	else if (spi->mode & SPI_RX_DUAL)
>>> -		mode = SPI_NOR_DUAL;
>>> +		hwcaps.mask |= SNOR_HWCAPS_READ_1_1_2;
>>>  
>>>  	if (data && data->name)
>>>  		nor->mtd.name = data->name;
>>> @@ -241,7 +243,7 @@ static int m25p_probe(struct spi_device *spi)
>>>  	else
>>>  		flash_name = spi->modalias;
>>>  
>>> -	ret = spi_nor_scan(nor, flash_name, mode);
>>> +	ret = spi_nor_scan(nor, flash_name, &hwcaps);
>>>  	if (ret)
>>>  		return ret;
>>>  
>>> diff --git a/drivers/mtd/spi-nor/aspeed-smc.c b/drivers/mtd/spi-nor/aspeed-smc.c
>>> index 56051d30f000..723026d9cf0c 100644
>>> --- a/drivers/mtd/spi-nor/aspeed-smc.c
>>> +++ b/drivers/mtd/spi-nor/aspeed-smc.c
>>> @@ -585,14 +585,12 @@ static int aspeed_smc_chip_setup_finish(struct aspeed_smc_chip *chip)
>>>  	 * TODO: Adjust clocks if fast read is supported and interpret
>>>  	 * SPI-NOR flags to adjust controller settings.
>>>  	 */
>>> -	switch (chip->nor.flash_read) {
>>> -	case SPI_NOR_NORMAL:
>>> -		cmd = CONTROL_COMMAND_MODE_NORMAL;
>>> -		break;
>>> -	case SPI_NOR_FAST:
>>> -		cmd = CONTROL_COMMAND_MODE_FREAD;
>>> -		break;
>>> -	default:
>>> +	if (chip->nor.read_proto == SNOR_PROTO_1_1_1) {
>>> +		if (chip->nor.read_dummy == 0)
>>> +			cmd = CONTROL_COMMAND_MODE_NORMAL;
>>> +		else
>>> +			cmd = CONTROL_COMMAND_MODE_FREAD;
>>> +	} else {
>>>  		dev_err(chip->nor.dev, "unsupported SPI read mode\n");
>>>  		return -EINVAL;
>>>  	}
>>> @@ -608,6 +606,11 @@ static int aspeed_smc_chip_setup_finish(struct aspeed_smc_chip *chip)
>>>  static int aspeed_smc_setup_flash(struct aspeed_smc_controller *controller,
>>>  				  struct device_node *np, struct resource *r)
>>>  {
>>> +	struct spi_nor_hwcaps hwcaps = {
>>> +		.mask = (SNOR_HWCAPS_READ |
>>> +			 SNOR_HWCAPS_READ_FAST |
>>> +			 SNOR_HWCAPS_PP),
>>> +	};
>>>  	const struct aspeed_smc_info *info = controller->info;
>>>  	struct device *dev = controller->dev;
>>>  	struct device_node *child;
>>> @@ -671,11 +674,11 @@ static int aspeed_smc_setup_flash(struct aspeed_smc_controller *controller,
>>>  			break;
>>>  
>>>  		/*
>>> -		 * TODO: Add support for SPI_NOR_QUAD and SPI_NOR_DUAL
>>> +		 * TODO: Add support for Dual and Quad SPI protocols
>>>
>>>  		 * attach when board support is present as determined
>>>  		 * by of property.
>>>  		 */
>>> -		ret = spi_nor_scan(nor, NULL, SPI_NOR_NORMAL);
>>> +		ret = spi_nor_scan(nor, NULL, &hwcaps);
>>>  		if (ret)
>>>  			break;
>>>  
>>> diff --git a/drivers/mtd/spi-nor/atmel-quadspi.c b/drivers/mtd/spi-nor/atmel-quadspi.c
>>> index 47937d9beec6..9f579f7c1733 100644
>>> --- a/drivers/mtd/spi-nor/atmel-quadspi.c
>>> +++ b/drivers/mtd/spi-nor/atmel-quadspi.c
>>> @@ -275,14 +275,48 @@ static void atmel_qspi_debug_command(struct atmel_qspi *aq,
>>>  
>>>  static int atmel_qspi_run_command(struct atmel_qspi *aq,
>>>  				  const struct atmel_qspi_command *cmd,
>>> -				  u32 ifr_tfrtyp, u32 ifr_width)
>>> +				  u32 ifr_tfrtyp, enum spi_nor_protocol proto)
>>>  {
>>>  	u32 iar, icr, ifr, sr;
>>>  	int err = 0;
>>>  
>>>  	iar = 0;
>>>  	icr = 0;
>>> -	ifr = ifr_tfrtyp | ifr_width;
>>> +	ifr = ifr_tfrtyp;
>>> +
>>> +	/* Set the SPI protocol */
>>> +	switch (proto) {
>>> +	case SNOR_PROTO_1_1_1:
>>> +		ifr |= QSPI_IFR_WIDTH_SINGLE_BIT_SPI;
>>> +		break;
>>> +
>>> +	case SNOR_PROTO_1_1_2:
>>> +		ifr |= QSPI_IFR_WIDTH_DUAL_OUTPUT;
>>> +		break;
>>> +
>>> +	case SNOR_PROTO_1_1_4:
>>> +		ifr |= QSPI_IFR_WIDTH_QUAD_OUTPUT;
>>> +		break;
>>> +
>>> +	case SNOR_PROTO_1_2_2:
>>> +		ifr |= QSPI_IFR_WIDTH_DUAL_IO;
>>> +		break;
>>> +
>>> +	case SNOR_PROTO_1_4_4:
>>> +		ifr |= QSPI_IFR_WIDTH_QUAD_IO;
>>> +		break;
>>> +
>>> +	case SNOR_PROTO_2_2_2:
>>> +		ifr |= QSPI_IFR_WIDTH_DUAL_CMD;
>>> +		break;
>>> +
>>> +	case SNOR_PROTO_4_4_4:
>>> +		ifr |= QSPI_IFR_WIDTH_QUAD_CMD;
>>> +		break;
>>> +
>>> +	default:
>>> +		return -EINVAL;
>>> +	}
>>>  
>>>  	/* Compute instruction parameters */
>>>  	if (cmd->enable.bits.instruction) {
>>> @@ -434,7 +468,7 @@ static int atmel_qspi_read_reg(struct spi_nor *nor, u8 opcode,
>>>  	cmd.rx_buf = buf;
>>>  	cmd.buf_len = len;
>>>  	return atmel_qspi_run_command(aq, &cmd, QSPI_IFR_TFRTYP_TRSFR_READ,
>>> -				      QSPI_IFR_WIDTH_SINGLE_BIT_SPI);
>>> +				      nor->reg_proto);
>>>  }
>>>  
>>>  static int atmel_qspi_write_reg(struct spi_nor *nor, u8 opcode,
>>> @@ -450,7 +484,7 @@ static int atmel_qspi_write_reg(struct spi_nor *nor, u8 opcode,
>>>  	cmd.tx_buf = buf;
>>>  	cmd.buf_len = len;
>>>  	return atmel_qspi_run_command(aq, &cmd, QSPI_IFR_TFRTYP_TRSFR_WRITE,
>>> -				      QSPI_IFR_WIDTH_SINGLE_BIT_SPI);
>>> +				      nor->reg_proto);
>>>  }
>>>  
>>>  static ssize_t atmel_qspi_write(struct spi_nor *nor, loff_t to, size_t len,
>>> @@ -469,7 +503,7 @@ static ssize_t atmel_qspi_write(struct spi_nor *nor, loff_t to, size_t len,
>>>  	cmd.tx_buf = write_buf;
>>>  	cmd.buf_len = len;
>>>  	ret = atmel_qspi_run_command(aq, &cmd, QSPI_IFR_TFRTYP_TRSFR_WRITE_MEM,
>>> -				     QSPI_IFR_WIDTH_SINGLE_BIT_SPI);
>>> +				     nor->write_proto);
>>>  	return (ret < 0) ? ret : len;
>>>  }
>>>  
>>> @@ -484,7 +518,7 @@ static int atmel_qspi_erase(struct spi_nor *nor, loff_t offs)
>>>  	cmd.instruction = nor->erase_opcode;
>>>  	cmd.address = (u32)offs;
>>>  	return atmel_qspi_run_command(aq, &cmd, QSPI_IFR_TFRTYP_TRSFR_WRITE,
>>> -				      QSPI_IFR_WIDTH_SINGLE_BIT_SPI);
>>> +				      nor->reg_proto);
>>>  }
>>>  
>>>  static ssize_t atmel_qspi_read(struct spi_nor *nor, loff_t from, size_t len,
>>> @@ -493,27 +527,8 @@ static ssize_t atmel_qspi_read(struct spi_nor *nor, loff_t from, size_t len,
>>>  	struct atmel_qspi *aq = nor->priv;
>>>  	struct atmel_qspi_command cmd;
>>>  	u8 num_mode_cycles, num_dummy_cycles;
>>> -	u32 ifr_width;
>>>  	ssize_t ret;
>>>  
>>> -	switch (nor->flash_read) {
>>> -	case SPI_NOR_NORMAL:
>>> -	case SPI_NOR_FAST:
>>> -		ifr_width = QSPI_IFR_WIDTH_SINGLE_BIT_SPI;
>>> -		break;
>>> -
>>> -	case SPI_NOR_DUAL:
>>> -		ifr_width = QSPI_IFR_WIDTH_DUAL_OUTPUT;
>>> -		break;
>>> -
>>> -	case SPI_NOR_QUAD:
>>> -		ifr_width = QSPI_IFR_WIDTH_QUAD_OUTPUT;
>>> -		break;
>>> -
>>> -	default:
>>> -		return -EINVAL;
>>> -	}
>>> -
>>>  	if (nor->read_dummy >= 2) {
>>>  		num_mode_cycles = 2;
>>>  		num_dummy_cycles = nor->read_dummy - 2;
>>> @@ -536,7 +551,7 @@ static ssize_t atmel_qspi_read(struct spi_nor *nor, loff_t from, size_t len,
>>>  	cmd.rx_buf = read_buf;
>>>  	cmd.buf_len = len;
>>>  	ret = atmel_qspi_run_command(aq, &cmd, QSPI_IFR_TFRTYP_TRSFR_READ_MEM,
>>> -				     ifr_width);
>>> +				     nor->read_proto);
>>>  	return (ret < 0) ? ret : len;
>>>  }
>>>  
>>> @@ -590,6 +605,17 @@ static irqreturn_t atmel_qspi_interrupt(int irq, void *dev_id)
>>>  
>>>  static int atmel_qspi_probe(struct platform_device *pdev)
>>>  {
>>> +	struct spi_nor_hwcaps hwcaps = {
>>> +		.mask = (SNOR_HWCAPS_READ |
>>> +			 SNOR_HWCAPS_READ_FAST |
>>> +			 SNOR_HWCAPS_READ_1_1_2 |
>>> +			 SNOR_HWCAPS_READ_1_2_2 |
>>> +			 SNOR_HWCAPS_READ_1_1_4 |
>>> +			 SNOR_HWCAPS_READ_1_4_4 |
>>> +			 SNOR_HWCAPS_PP |
>>> +			 SNOR_HWCAPS_PP_1_1_4 |
>>> +			 SNOR_HWCAPS_PP_1_4_4),
>>> +	};
>>>  	struct device_node *child, *np = pdev->dev.of_node;
>>>  	struct atmel_qspi *aq;
>>>  	struct resource *res;
>>> @@ -679,7 +705,7 @@ static int atmel_qspi_probe(struct platform_device *pdev)
>>>  	if (err)
>>>  		goto disable_clk;
>>>  
>>> -	err = spi_nor_scan(nor, NULL, SPI_NOR_QUAD);
>>> +	err = spi_nor_scan(nor, NULL, &hwcaps);
>>>  	if (err)
>>>  		goto disable_clk;
>>>  
>>> diff --git a/drivers/mtd/spi-nor/cadence-quadspi.c b/drivers/mtd/spi-nor/cadence-quadspi.c
>>> index 9f8102de1b16..3f91a3e97892 100644
>>> --- a/drivers/mtd/spi-nor/cadence-quadspi.c
>>> +++ b/drivers/mtd/spi-nor/cadence-quadspi.c
>>> @@ -855,15 +855,14 @@ static int cqspi_set_protocol(struct spi_nor *nor, const int read)
>>>  	f_pdata->data_width = CQSPI_INST_TYPE_SINGLE;
>>>  
>>>  	if (read) {
>>> -		switch (nor->flash_read) {
>>> -		case SPI_NOR_NORMAL:
>>> -		case SPI_NOR_FAST:
>>> +		switch (nor->read_proto) {
>>> +		case SNOR_PROTO_1_1_1:
>>>  			f_pdata->data_width = CQSPI_INST_TYPE_SINGLE;
>>>  			break;
>>> -		case SPI_NOR_DUAL:
>>> +		case SNOR_PROTO_1_1_2:
>>>  			f_pdata->data_width = CQSPI_INST_TYPE_DUAL;
>>>  			break;
>>> -		case SPI_NOR_QUAD:
>>> +		case SNOR_PROTO_1_1_4:
>>>  			f_pdata->data_width = CQSPI_INST_TYPE_QUAD;
>>>  			break;
>>>  		default:
>>> @@ -1069,6 +1068,13 @@ static void cqspi_controller_init(struct cqspi_st *cqspi)
>>>  
>>>  static int cqspi_setup_flash(struct cqspi_st *cqspi, struct device_node *np)
>>>  {
>>> +	struct spi_nor_hwcaps hwcaps = {
>>> +		.mask = (SNOR_HWCAPS_READ |
>>> +			 SNOR_HWCAPS_READ_FAST |
>>> +			 SNOR_HWCAPS_READ_1_1_2 |
>>> +			 SNOR_HWCAPS_READ_1_1_4 |
>>> +			 SNOR_HWCAPS_PP),
>>> +	};
>>>  	struct platform_device *pdev = cqspi->pdev;
>>>  	struct device *dev = &pdev->dev;
>>>  	struct cqspi_flash_pdata *f_pdata;
>>> @@ -1123,7 +1129,7 @@ static int cqspi_setup_flash(struct cqspi_st *cqspi, struct device_node *np)
>>>  			goto err;
>>>  		}
>>>  
>>> -		ret = spi_nor_scan(nor, NULL, SPI_NOR_QUAD);
>>> +		ret = spi_nor_scan(nor, NULL, &hwcaps);
>>>  		if (ret)
>>>  			goto err;
>>>  
>>> diff --git a/drivers/mtd/spi-nor/fsl-quadspi.c b/drivers/mtd/spi-nor/fsl-quadspi.c
>>> index 1476135e0d50..ec9c8e960fd2 100644
>>> --- a/drivers/mtd/spi-nor/fsl-quadspi.c
>>> +++ b/drivers/mtd/spi-nor/fsl-quadspi.c
>>> @@ -957,6 +957,12 @@ static void fsl_qspi_unprep(struct spi_nor *nor, enum spi_nor_ops ops)
>>>  
>>>  static int fsl_qspi_probe(struct platform_device *pdev)
>>>  {
>>> +	struct spi_nor_hwcaps hwcaps = {
>>> +		.mask = (SNOR_HWCAPS_READ |
>>> +			 SNOR_HWCAPS_READ_FAST |
>>> +			 SNOR_HWCAPS_READ_1_1_4 |
>>> +			 SNOR_HWCAPS_PP),
>>> +	};
>>>  	struct device_node *np = pdev->dev.of_node;
>>>  	struct device *dev = &pdev->dev;
>>>  	struct fsl_qspi *q;
>>> @@ -1065,7 +1071,7 @@ static int fsl_qspi_probe(struct platform_device *pdev)
>>>  		/* set the chip address for READID */
>>>  		fsl_qspi_set_base_addr(q, nor);
>>>  
>>> -		ret = spi_nor_scan(nor, NULL, SPI_NOR_QUAD);
>>> +		ret = spi_nor_scan(nor, NULL, &hwcaps);
>>>  		if (ret)
>>>  			goto mutex_failed;
>>>  
>>> diff --git a/drivers/mtd/spi-nor/hisi-sfc.c b/drivers/mtd/spi-nor/hisi-sfc.c
>>> index a286350627a6..80e2d173abdd 100644
>>> --- a/drivers/mtd/spi-nor/hisi-sfc.c
>>> +++ b/drivers/mtd/spi-nor/hisi-sfc.c
>>> @@ -120,19 +120,24 @@ static inline int wait_op_finish(struct hifmc_host *host)
>>>  		(reg & FMC_INT_OP_DONE), 0, FMC_WAIT_TIMEOUT);
>>>  }
>>>  
>>> -static int get_if_type(enum read_mode flash_read)
>>> +static int get_if_type(enum spi_nor_protocol proto)
>>>  {
>>>  	enum hifmc_iftype if_type;
>>>  
>>> -	switch (flash_read) {
>>> -	case SPI_NOR_DUAL:
>>> +	switch (proto) {
>>> +	case SNOR_PROTO_1_1_2:
>>>  		if_type = IF_TYPE_DUAL;
>>>  		break;
>>> -	case SPI_NOR_QUAD:
>>> +	case SNOR_PROTO_1_2_2:
>>> +		if_type = IF_TYPE_DIO;
>>> +		break;
>>> +	case SNOR_PROTO_1_1_4:
>>>  		if_type = IF_TYPE_QUAD;
>>>  		break;
>>> -	case SPI_NOR_NORMAL:
>>> -	case SPI_NOR_FAST:
>>> +	case SNOR_PROTO_1_4_4:
>>> +		if_type = IF_TYPE_QIO;
>>> +		break;
>>> +	case SNOR_PROTO_1_1_1:
>>>  	default:
>>>  		if_type = IF_TYPE_STD;
>>>  		break;
>>> @@ -253,7 +258,10 @@ static int hisi_spi_nor_dma_transfer(struct spi_nor *nor, loff_t start_off,
>>>  	writel(FMC_DMA_LEN_SET(len), host->regbase + FMC_DMA_LEN);
>>>  
>>>  	reg = OP_CFG_FM_CS(priv->chipselect);
>>> -	if_type = get_if_type(nor->flash_read);
>>> +	if (op_type == FMC_OP_READ)
>>> +		if_type = get_if_type(nor->read_proto);
>>> +	else
>>> +		if_type = get_if_type(nor->write_proto);
>>>  	reg |= OP_CFG_MEM_IF_TYPE(if_type);
>>>  	if (op_type == FMC_OP_READ)
>>>  		reg |= OP_CFG_DUMMY_NUM(nor->read_dummy >> 3);
>>> @@ -321,6 +329,13 @@ static ssize_t hisi_spi_nor_write(struct spi_nor *nor, loff_t to,
>>>  static int hisi_spi_nor_register(struct device_node *np,
>>>  				struct hifmc_host *host)
>>>  {
>>> +	struct spi_nor_hwcaps hwcaps = {
>>> +		.mask = (SNOR_HWCAPS_READ |
>>> +			 SNOR_HWCAPS_READ_FAST |
>>> +			 SNOR_HWCAPS_READ_1_1_2 |
>>> +			 SNOR_HWCAPS_READ_1_1_4 |
>>> +			 SNOR_HWCAPS_PP),
>>> +	};
>>>  	struct device *dev = host->dev;
>>>  	struct spi_nor *nor;
>>>  	struct hifmc_priv *priv;
>>> @@ -362,7 +377,7 @@ static int hisi_spi_nor_register(struct device_node *np,
>>>  	nor->read = hisi_spi_nor_read;
>>>  	nor->write = hisi_spi_nor_write;
>>>  	nor->erase = NULL;
>>> -	ret = spi_nor_scan(nor, NULL, SPI_NOR_QUAD);
>>> +	ret = spi_nor_scan(nor, NULL, &hwcaps);
>>>  	if (ret)
>>>  		return ret;
>>>  
>>> diff --git a/drivers/mtd/spi-nor/intel-spi.c b/drivers/mtd/spi-nor/intel-spi.c
>>> index 986a3d020a3a..515aa1f7f4f1 100644
>>> --- a/drivers/mtd/spi-nor/intel-spi.c
>>> +++ b/drivers/mtd/spi-nor/intel-spi.c
>>> @@ -715,6 +715,11 @@ static void intel_spi_fill_partition(struct intel_spi *ispi,
>>>  struct intel_spi *intel_spi_probe(struct device *dev,
>>>  	struct resource *mem, const struct intel_spi_boardinfo *info)
>>>  {
>>> +	struct spi_nor_hwcaps hwcaps = {
>>> +		.mask = (SNOR_HWCAPS_READ |
>>> +			 SNOR_HWCAPS_READ_FAST |
>>> +			 SNOR_HWCAPS_PP),
>>> +	};
>>>  	struct mtd_partition part;
>>>  	struct intel_spi *ispi;
>>>  	int ret;
>>> @@ -746,7 +751,7 @@ struct intel_spi *intel_spi_probe(struct device *dev,
>>>  	ispi->nor.write = intel_spi_write;
>>>  	ispi->nor.erase = intel_spi_erase;
>>>  
>>> -	ret = spi_nor_scan(&ispi->nor, NULL, SPI_NOR_NORMAL);
>>> +	ret = spi_nor_scan(&ispi->nor, NULL, &hwcaps);
>>>  	if (ret) {
>>>  		dev_info(dev, "failed to locate the chip\n");
>>>  		return ERR_PTR(ret);
>>> diff --git a/drivers/mtd/spi-nor/mtk-quadspi.c b/drivers/mtd/spi-nor/mtk-quadspi.c
>>> index e661877c23de..615e258866f7 100644
>>> --- a/drivers/mtd/spi-nor/mtk-quadspi.c
>>> +++ b/drivers/mtd/spi-nor/mtk-quadspi.c
>>> @@ -121,20 +121,20 @@ static void mt8173_nor_set_read_mode(struct mt8173_nor *mt8173_nor)
>>>  {
>>>  	struct spi_nor *nor = &mt8173_nor->nor;
>>>  
>>> -	switch (nor->flash_read) {
>>> -	case SPI_NOR_FAST:
>>> +	switch (nor->read_proto) {
>>> +	case SNOR_PROTO_1_1_1:
>>>  		writeb(nor->read_opcode, mt8173_nor->base +
>>>  		       MTK_NOR_PRGDATA3_REG);
>>>  		writeb(MTK_NOR_FAST_READ, mt8173_nor->base +
>>>  		       MTK_NOR_CFG1_REG);
>>>  		break;
>>> -	case SPI_NOR_DUAL:
>>> +	case SNOR_PROTO_1_1_2:
>>>  		writeb(nor->read_opcode, mt8173_nor->base +
>>>  		       MTK_NOR_PRGDATA3_REG);
>>>  		writeb(MTK_NOR_DUAL_READ_EN, mt8173_nor->base +
>>>  		       MTK_NOR_DUAL_REG);
>>>  		break;
>>> -	case SPI_NOR_QUAD:
>>> +	case SNOR_PROTO_1_1_4:
>>>  		writeb(nor->read_opcode, mt8173_nor->base +
>>>  		       MTK_NOR_PRGDATA4_REG);
>>>  		writeb(MTK_NOR_QUAD_READ_EN, mt8173_nor->base +
>>> @@ -381,6 +381,12 @@ static int mt8173_nor_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf,
>>>  static int mtk_nor_init(struct mt8173_nor *mt8173_nor,
>>>  			struct device_node *flash_node)
>>>  {
>>> +	struct spi_nor_hwcaps hwcaps = {
>>> +		.mask = (SNOR_HWCAPS_READ |
>>> +			 SNOR_HWCAPS_READ_FAST |
>>> +			 SNOR_HWCAPS_READ_1_1_2 |
>>> +			 SNOR_HWCAPS_PP),
>>> +	};
>>>  	int ret;
>>>  	struct spi_nor *nor;
>>>  
>>> @@ -399,7 +405,7 @@ static int mtk_nor_init(struct mt8173_nor *mt8173_nor,
>>>  	nor->write_reg = mt8173_nor_write_reg;
>>>  	nor->mtd.name = "mtk_nor";
>>>  	/* initialized with NULL */
>>> -	ret = spi_nor_scan(nor, NULL, SPI_NOR_DUAL);
>>> +	ret = spi_nor_scan(nor, NULL, &hwcaps);
>>>  	if (ret)
>>>  		return ret;
>>>  
>>> diff --git a/drivers/mtd/spi-nor/nxp-spifi.c b/drivers/mtd/spi-nor/nxp-spifi.c
>>> index 73a14f40928b..c5992e099542 100644
>>> --- a/drivers/mtd/spi-nor/nxp-spifi.c
>>> +++ b/drivers/mtd/spi-nor/nxp-spifi.c
>>> @@ -240,13 +240,12 @@ static int nxp_spifi_erase(struct spi_nor *nor, loff_t offs)
>>>  
>>>  static int nxp_spifi_setup_memory_cmd(struct nxp_spifi *spifi)
>>>  {
>>> -	switch (spifi->nor.flash_read) {
>>> -	case SPI_NOR_NORMAL:
>>> -	case SPI_NOR_FAST:
>>> +	switch (spifi->nor.read_proto) {
>>> +	case SNOR_PROTO_1_1_1:
>>>  		spifi->mcmd = SPIFI_CMD_FIELDFORM_ALL_SERIAL;
>>>  		break;
>>> -	case SPI_NOR_DUAL:
>>> -	case SPI_NOR_QUAD:
>>> +	case SNOR_PROTO_1_1_2:
>>> +	case SNOR_PROTO_1_1_4:
>>>  		spifi->mcmd = SPIFI_CMD_FIELDFORM_QUAD_DUAL_DATA;
>>>  		break;
>>>  	default:
>>> @@ -274,7 +273,11 @@ static void nxp_spifi_dummy_id_read(struct spi_nor *nor)
>>>  static int nxp_spifi_setup_flash(struct nxp_spifi *spifi,
>>>  				 struct device_node *np)
>>>  {
>>> -	enum read_mode flash_read;
>>> +	struct spi_nor_hwcaps hwcaps = {
>>> +		.mask = (SNOR_HWCAPS_READ |
>>> +			 SNOR_HWCAPS_READ_FAST |
>>> +			 SNOR_HWCAPS_PP),
>>> +	};
>>>  	u32 ctrl, property;
>>>  	u16 mode = 0;
>>>  	int ret;
>>> @@ -308,13 +311,12 @@ static int nxp_spifi_setup_flash(struct nxp_spifi *spifi,
>>>  
>>>  	if (mode & SPI_RX_DUAL) {
>>>  		ctrl |= SPIFI_CTRL_DUAL;
>>> -		flash_read = SPI_NOR_DUAL;
>>> +		hwcaps.mask |= SNOR_HWCAPS_READ_1_1_2;
>>>  	} else if (mode & SPI_RX_QUAD) {
>>>  		ctrl &= ~SPIFI_CTRL_DUAL;
>>> -		flash_read = SPI_NOR_QUAD;
>>> +		hwcaps.mask |= SNOR_HWCAPS_READ_1_1_4;
>>>  	} else {
>>>  		ctrl |= SPIFI_CTRL_DUAL;
>>> -		flash_read = SPI_NOR_NORMAL;
>>>  	}
>>>  
>>>  	switch (mode & (SPI_CPHA | SPI_CPOL)) {
>>> @@ -351,7 +353,7 @@ static int nxp_spifi_setup_flash(struct nxp_spifi *spifi,
>>>  	 */
>>>  	nxp_spifi_dummy_id_read(&spifi->nor);
>>>  
>>> -	ret = spi_nor_scan(&spifi->nor, NULL, flash_read);
>>> +	ret = spi_nor_scan(&spifi->nor, NULL, &hwcaps);
>>>  	if (ret) {
>>>  		dev_err(spifi->dev, "device scan failed\n");
>>>  		return ret;
>>> diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c
>>> index d3cb44b28490..cc443c6cbae8 100644
>>> --- a/drivers/mtd/spi-nor/spi-nor.c
>>> +++ b/drivers/mtd/spi-nor/spi-nor.c
>>> @@ -150,24 +150,6 @@ static int read_cr(struct spi_nor *nor)
>>>  }
>>>  
>>>  /*
>>> - * Dummy Cycle calculation for different type of read.
>>> - * It can be used to support more commands with
>>> - * different dummy cycle requirements.
>>> - */
>>> -static inline int spi_nor_read_dummy_cycles(struct spi_nor *nor)
>>> -{
>>> -	switch (nor->flash_read) {
>>> -	case SPI_NOR_FAST:
>>> -	case SPI_NOR_DUAL:
>>> -	case SPI_NOR_QUAD:
>>> -		return 8;
>>> -	case SPI_NOR_NORMAL:
>>> -		return 0;
>>> -	}
>>> -	return 0;
>>> -}
>>> -
>>> -/*
>>>   * Write status register 1 byte
>>>   * Returns negative if error occurred.
>>>   */
>>> @@ -221,6 +203,10 @@ static inline u8 spi_nor_convert_3to4_read(u8 opcode)
>>>  		{ SPINOR_OP_READ_1_2_2,	SPINOR_OP_READ_1_2_2_4B },
>>>  		{ SPINOR_OP_READ_1_1_4,	SPINOR_OP_READ_1_1_4_4B },
>>>  		{ SPINOR_OP_READ_1_4_4,	SPINOR_OP_READ_1_4_4_4B },
>>> +
>>> +		{ SPINOR_OP_READ_1_1_1_DTR,	SPINOR_OP_READ_1_1_1_DTR_4B },
>>> +		{ SPINOR_OP_READ_1_2_2_DTR,	SPINOR_OP_READ_1_2_2_DTR_4B },
>>> +		{ SPINOR_OP_READ_1_4_4_DTR,	SPINOR_OP_READ_1_4_4_DTR_4B },
>>>  	};
>>>  
>>>  	return spi_nor_convert_opcode(opcode, spi_nor_3to4_read,
>>> @@ -1459,30 +1445,6 @@ static int spansion_quad_enable(struct spi_nor *nor)
>>>  	return 0;
>>>  }
>>>  
>>> -static int set_quad_mode(struct spi_nor *nor, const struct flash_info *info)
>>> -{
>>> -	int status;
>>> -
>>> -	switch (JEDEC_MFR(info)) {
>>> -	case SNOR_MFR_MACRONIX:
>>> -		status = macronix_quad_enable(nor);
>>> -		if (status) {
>>> -			dev_err(nor->dev, "Macronix quad-read not enabled\n");
>>> -			return -EINVAL;
>>> -		}
>>> -		return status;
>>> -	case SNOR_MFR_MICRON:
>>> -		return 0;
>>> -	default:
>>> -		status = spansion_quad_enable(nor);
>>> -		if (status) {
>>> -			dev_err(nor->dev, "Spansion quad-read not enabled\n");
>>> -			return -EINVAL;
>>> -		}
>>> -		return status;
>>> -	}
>>> -}
>>> -
>>>  static int spi_nor_check(struct spi_nor *nor)
>>>  {
>>>  	if (!nor->dev || !nor->read || !nor->write ||
>>> @@ -1535,8 +1497,322 @@ static int s3an_nor_scan(const struct flash_info *info, struct spi_nor *nor)
>>>  	return 0;
>>>  }
>>>  
>>> -int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
>>> +
>>> +struct spi_nor_read_command {
>>> +	u8			num_mode_clocks;
>>> +	u8			num_wait_states;
>>> +	u8			opcode;
>>> +	enum spi_nor_protocol	proto;
>>> +};
>>> +
>>> +struct spi_nor_pp_command {
>>> +	u8			opcode;
>>> +	enum spi_nor_protocol	proto;
>>> +};
>>> +
>>> +enum spi_nor_read_command_index {
>>> +	SNOR_CMD_READ,
>>> +	SNOR_CMD_READ_FAST,
>>> +	SNOR_CMD_READ_1_1_1_DTR,
>>> +
>>> +	/* Dual SPI */
>>> +	SNOR_CMD_READ_1_1_2,
>>> +	SNOR_CMD_READ_1_2_2,
>>> +	SNOR_CMD_READ_2_2_2,
>>> +	SNOR_CMD_READ_1_2_2_DTR,
>>> +
>>> +	/* Quad SPI */
>>> +	SNOR_CMD_READ_1_1_4,
>>> +	SNOR_CMD_READ_1_4_4,
>>> +	SNOR_CMD_READ_4_4_4,
>>> +	SNOR_CMD_READ_1_4_4_DTR,
>>> +
>>> +	/* Octo SPI */
>>> +	SNOR_CMD_READ_1_1_8,
>>> +	SNOR_CMD_READ_1_8_8,
>>> +	SNOR_CMD_READ_8_8_8,
>>> +	SNOR_CMD_READ_1_8_8_DTR,
>>> +
>>> +	SNOR_CMD_READ_MAX
>>> +};
>>> +
>>> +enum spi_nor_pp_command_index {
>>> +	SNOR_CMD_PP,
>>> +
>>> +	/* Quad SPI */
>>> +	SNOR_CMD_PP_1_1_4,
>>> +	SNOR_CMD_PP_1_4_4,
>>> +	SNOR_CMD_PP_4_4_4,
>>> +
>>> +	/* Octo SPI */
>>> +	SNOR_CMD_PP_1_1_8,
>>> +	SNOR_CMD_PP_1_8_8,
>>> +	SNOR_CMD_PP_8_8_8,
>>> +
>>> +	SNOR_CMD_PP_MAX
>>> +};
>>> +
>>> +struct spi_nor_flash_parameter {
>>> +	u64				size;
>>> +	u32				page_size;
>>> +
>>> +	struct spi_nor_hwcaps		hwcaps;
>>> +	struct spi_nor_read_command	reads[SNOR_CMD_READ_MAX];
>>> +	struct spi_nor_pp_command	page_programs[SNOR_CMD_PP_MAX];
>>> +
>>> +	int (*quad_enable)(struct spi_nor *nor);
>>> +};
>>> +
>>> +
>>> +static inline 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)
>>> +{
>>> +	read->num_mode_clocks = num_mode_clocks;
>>> +	read->num_wait_states = num_wait_states;
>>> +	read->opcode = opcode;
>>> +	read->proto = proto;
>>> +}
>>> +
>>> +static inline void
>>> +spi_nor_set_pp_settings(struct spi_nor_pp_command *pp,
>>> +			u8 opcode,
>>> +			enum spi_nor_protocol proto)
>>> +{
>>> +	pp->opcode = opcode;
>>> +	pp->proto = proto;
>>> +}
>>> +
>>> +static int spi_nor_init_params(struct spi_nor *nor,
>>> +			       const struct flash_info *info,
>>> +			       struct spi_nor_flash_parameter *params)
>>> +{
>>> +	/* Set legacy flash parameters as default. */
>>> +	memset(params, 0, sizeof(*params));
>>> +
>>> +	/* Set SPI NOR sizes. */
>>> +	params->size = info->sector_size * info->n_sectors;
>>> +	params->page_size = info->page_size;
>>> +
>>> +	/* (Fast) Read settings. */
>>> +	params->hwcaps.mask |= SNOR_HWCAPS_READ;
>>> +	spi_nor_set_read_settings(&params->reads[SNOR_CMD_READ],
>>> +				  0, 0, SPINOR_OP_READ,
>>> +				  SNOR_PROTO_1_1_1);
>>> +	if (!(info->flags & SPI_NOR_NO_FR)) {
>>> +		params->hwcaps.mask |= SNOR_HWCAPS_READ_FAST;
>>> +		spi_nor_set_read_settings(&params->reads[SNOR_CMD_READ_FAST],
>>> +					  0, 8, SPINOR_OP_READ_FAST,
>>> +					  SNOR_PROTO_1_1_1);
>>> +	}
>>> +	if (info->flags & SPI_NOR_DUAL_READ) {
>>> +		params->hwcaps.mask |= SNOR_HWCAPS_READ_1_1_2;
>>> +		spi_nor_set_read_settings(&params->reads[SNOR_CMD_READ_1_1_2],
>>> +					  0, 8, SPINOR_OP_READ_1_1_2,
>>> +					  SNOR_PROTO_1_1_2);
>>> +	}
>>> +	if (info->flags & SPI_NOR_QUAD_READ) {
>>> +		params->hwcaps.mask |= SNOR_HWCAPS_READ_1_1_4;
>>> +		spi_nor_set_read_settings(&params->reads[SNOR_CMD_READ_1_1_4],
>>> +					  0, 8, SPINOR_OP_READ_1_1_4,
>>> +					  SNOR_PROTO_1_1_4);
>>> +	}
>>> +
>>> +	/* 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);
>>> +
>>> +	/* Select the procedure to set the Quad Enable bit. */
>>> +	if (params->hwcaps.mask & (SNOR_HWCAPS_READ_QUAD |
>>> +				   SNOR_HWCAPS_PP_QUAD)) {
>>> +		switch (JEDEC_MFR(info)) {
>>> +		case SNOR_MFR_MACRONIX:
>>> +			params->quad_enable = macronix_quad_enable;
>>> +			break;
>>> +
>>> +		case SNOR_MFR_MICRON:
>>> +			break;
>>> +
>>> +		default:
>>> +			params->quad_enable = spansion_quad_enable;
>>> +			break;
>>> +		}
>>> +	}
>>> +
>>> +	return 0;
>>> +}
>>> +
>>> +static int spi_nor_hwcaps2cmd(u32 hwcaps)
>>>  {
>>> +	switch (hwcaps) {
>>> +	case SNOR_HWCAPS_READ:			return SNOR_CMD_READ;
>>> +	case SNOR_HWCAPS_READ_FAST:		return SNOR_CMD_READ_FAST;
>>> +	case SNOR_HWCAPS_READ_1_1_1_DTR:	return SNOR_CMD_READ_1_1_1_DTR;
>>> +	case SNOR_HWCAPS_READ_1_1_2:		return SNOR_CMD_READ_1_1_2;
>>> +	case SNOR_HWCAPS_READ_1_2_2:		return SNOR_CMD_READ_1_2_2;
>>> +	case SNOR_HWCAPS_READ_2_2_2:		return SNOR_CMD_READ_2_2_2;
>>> +	case SNOR_HWCAPS_READ_1_2_2_DTR:	return SNOR_CMD_READ_1_2_2_DTR;
>>> +	case SNOR_HWCAPS_READ_1_1_4:		return SNOR_CMD_READ_1_1_4;
>>> +	case SNOR_HWCAPS_READ_1_4_4:		return SNOR_CMD_READ_1_4_4;
>>> +	case SNOR_HWCAPS_READ_4_4_4:		return SNOR_CMD_READ_4_4_4;
>>> +	case SNOR_HWCAPS_READ_1_4_4_DTR:	return SNOR_CMD_READ_1_4_4_DTR;
>>> +	case SNOR_HWCAPS_READ_1_1_8:		return SNOR_CMD_READ_1_1_8;
>>> +	case SNOR_HWCAPS_READ_1_8_8:		return SNOR_CMD_READ_1_8_8;
>>> +	case SNOR_HWCAPS_READ_8_8_8:		return SNOR_CMD_READ_8_8_8;
>>> +	case SNOR_HWCAPS_READ_1_8_8_DTR:	return SNOR_CMD_READ_1_8_8_DTR;
>>> +
>>> +	case SNOR_HWCAPS_PP:			return SNOR_CMD_PP;
>>> +	case SNOR_HWCAPS_PP_1_1_4:		return SNOR_CMD_PP_1_1_4;
>>> +	case SNOR_HWCAPS_PP_1_4_4:		return SNOR_CMD_PP_1_4_4;
>>> +	case SNOR_HWCAPS_PP_4_4_4:		return SNOR_CMD_PP_4_4_4;
>>> +	case SNOR_HWCAPS_PP_1_1_8:		return SNOR_CMD_PP_1_1_8;
>>> +	case SNOR_HWCAPS_PP_1_8_8:		return SNOR_CMD_PP_1_8_8;
>>> +	case SNOR_HWCAPS_PP_8_8_8:		return SNOR_CMD_PP_8_8_8;
>>> +	}
>>> +
>>> +	return -EINVAL;
>>> +}
>>> +
>>> +static int spi_nor_select_read(struct spi_nor *nor,
>>> +			       const struct spi_nor_flash_parameter *params,
>>> +			       u32 shared_hwcaps)
>>> +{
>>> +	int cmd, best_match = fls(shared_hwcaps & SNOR_HWCAPS_READ_MASK) - 1;
>>> +	const struct spi_nor_read_command *read;
>>> +
>>> +	if (best_match < 0)
>>> +		return -EINVAL;
>>> +
>>> +	cmd = spi_nor_hwcaps2cmd(BIT(best_match));
>>> +	if (cmd < 0)
>>> +		return -EINVAL;
>>> +
>>> +	read = &params->reads[cmd];
>>> +	nor->read_opcode = read->opcode;
>>> +	nor->read_proto = read->proto;
>>> +
>>> +	/*
>>> +	 * In the spi-nor framework, we don't need to make the difference
>>> +	 * between mode clock cycles and wait state clock cycles.
>>> +	 * Indeed, the value of the mode clock cycles is used by a QSPI
>>> +	 * flash memory to know whether it should enter or leave its 0-4-4
>>> +	 * (Continuous Read / XIP) mode.
>>> +	 * eXecution In Place is out of the scope of the mtd sub-system.
>>> +	 * Hence we choose to merge both mode and wait state clock cycles
>>> +	 * into the so called dummy clock cycles.
>>> +	 */
>>> +	nor->read_dummy = read->num_mode_clocks + read->num_wait_states;
>>> +	return 0;
>>> +}
>>> +
>>> +static int spi_nor_select_pp(struct spi_nor *nor,
>>> +			     const struct spi_nor_flash_parameter *params,
>>> +			     u32 shared_hwcaps)
>>> +{
>>> +	int cmd, best_match = fls(shared_hwcaps & SNOR_HWCAPS_PP_MASK) - 1;
>>> +	const struct spi_nor_pp_command *pp;
>>> +
>>> +	if (best_match < 0)
>>> +		return -EINVAL;
>>> +
>>> +	cmd = spi_nor_hwcaps2cmd(BIT(best_match));
>>> +	if (cmd < 0)
>>> +		return -EINVAL;
>>> +
>>> +	pp = &params->page_programs[cmd];
>>> +	nor->program_opcode = pp->opcode;
>>> +	nor->write_proto = pp->proto;
>>> +	return 0;
>>> +}
>>> +
>>> +static int spi_nor_select_erase(struct spi_nor *nor,
>>> +				const struct flash_info *info)
>>> +{
>>> +	struct mtd_info *mtd = &nor->mtd;
>>> +
>>> +#ifdef CONFIG_MTD_SPI_NOR_USE_4K_SECTORS
>>> +	/* prefer "small sector" erase if possible */
>>> +	if (info->flags & SECT_4K) {
>>> +		nor->erase_opcode = SPINOR_OP_BE_4K;
>>> +		mtd->erasesize = 4096;
>>> +	} else if (info->flags & SECT_4K_PMC) {
>>> +		nor->erase_opcode = SPINOR_OP_BE_4K_PMC;
>>> +		mtd->erasesize = 4096;
>>> +	} else
>>> +#endif
>>> +	{
>>> +		nor->erase_opcode = SPINOR_OP_SE;
>>> +		mtd->erasesize = info->sector_size;
>>> +	}
>>> +	return 0;
>>> +}
>>> +
>>> +static int spi_nor_setup(struct spi_nor *nor, const struct flash_info *info,
>>> +			 const struct spi_nor_flash_parameter *params,
>>> +			 const struct spi_nor_hwcaps *hwcaps)
>>> +{
>>> +	u32 ignored_mask, shared_mask;
>>> +	bool enable_quad_io;
>>> +	int err;
>>> +
>>> +	/*
>>> +	 * Keep only the hardware capabilities supported by both the SPI
>>> +	 * controller and the SPI flash memory.
>>> +	 */
>>> +	shared_mask = hwcaps->mask & params->hwcaps.mask;
>>> +
>>> +	/* SPI protocol classes N-N-N are not supported yet. */
>>> +	ignored_mask = (SNOR_HWCAPS_READ_2_2_2 |
>>> +			SNOR_HWCAPS_READ_4_4_4 |
>>> +			SNOR_HWCAPS_READ_8_8_8 |
>>> +			SNOR_HWCAPS_PP_4_4_4 |
>>> +			SNOR_HWCAPS_PP_8_8_8);
>>> +	if (shared_mask & ignored_mask) {
>>> +		dev_dbg(nor->dev,
>>> +			"SPI protocol classes N-N-N are not supported yet.\n");
>>> +		shared_mask &= ~ignored_mask;
>>> +	}
>>> +
>>> +	/* Select the (Fast) Read command. */
>>> +	err = spi_nor_select_read(nor, params, shared_mask);
>>> +	if (err) {
>>> +		dev_err(nor->dev, "invalid (fast) read\n");
>>> +		return err;
>>> +	}
>>> +
>>> +	/* Select the Page Program command. */
>>> +	err = spi_nor_select_pp(nor, params, shared_mask);
>>> +	if (err) {
>>> +		dev_err(nor->dev, "invalid page program\n");
>>> +		return err;
>>> +	}
>>> +
>>> +	/* Select the Sector Erase command. */
>>> +	err = spi_nor_select_erase(nor, info);
>>> +	if (err) {
>>> +		dev_err(nor->dev, "invalid sector/block erase\n");
>>> +		return err;
>>> +	}
>>> +
>>> +	/* Enable Quad I/O if needed. */
>>> +	enable_quad_io = (spi_nor_get_protocol_width(nor->read_proto) == 4 ||
>>> +			  spi_nor_get_protocol_width(nor->write_proto) == 4);
>>> +	if (enable_quad_io && params->quad_enable)
>>> +		nor->flash_quad_enable = params->quad_enable;
>>> +	else
>>> +		nor->flash_quad_enable = NULL;
>>> +
>>> +	return 0;
>>> +}
>>> +
>>> +int spi_nor_scan(struct spi_nor *nor, const char *name,
>>> +		 const struct spi_nor_hwcaps *hwcaps)
>>> +{
>>> +	struct spi_nor_flash_parameter params;
>>>  	const struct flash_info *info = NULL;
>>>  	struct device *dev = nor->dev;
>>>  	struct mtd_info *mtd = &nor->mtd;
>>> @@ -1548,6 +1824,11 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
>>>  	if (ret)
>>>  		return ret;
>>>  
>>> +	/* Reset SPI protocol for all commands */
>>> +	nor->reg_proto = SNOR_PROTO_1_1_1;
>>> +	nor->read_proto = SNOR_PROTO_1_1_1;
>>> +	nor->write_proto = SNOR_PROTO_1_1_1;
>>> +
>>>  	if (name)
>>>  		info = spi_nor_match_id(name);
>>>  	/* Try to auto-detect if chip name wasn't specified or not found */
>>> @@ -1580,6 +1861,11 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
>>>  		}
>>>  	}
>>>  
>>> +	/* Parse the Serial Flash Discoverable Parameters table */
>>> +	ret = spi_nor_init_params(nor, info, &params);
>>> +	if (ret)
>>> +		return ret;
>>> +
>>>  	mutex_init(&nor->lock);
>>>  
>>>  	/*
>>> @@ -1610,7 +1896,7 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
>>>  	mtd->type = MTD_NORFLASH;
>>>  	mtd->writesize = 1;
>>>  	mtd->flags = MTD_CAP_NORFLASH;
>>> -	mtd->size = info->sector_size * info->n_sectors;
>>> +	mtd->size = params.size;
>>>  	mtd->_erase = spi_nor_erase;
>>>  	mtd->_read = spi_nor_read;
>>>  
>>> @@ -1641,76 +1927,47 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
>>>  	if (info->flags & NO_CHIP_ERASE)
>>>  		nor->flags |= SNOR_F_NO_OP_CHIP_ERASE;
>>>  
>>> -#ifdef CONFIG_MTD_SPI_NOR_USE_4K_SECTORS
>>> -	/* prefer "small sector" erase if possible */
>>> -	if (info->flags & SECT_4K) {
>>> -		nor->erase_opcode = SPINOR_OP_BE_4K;
>>> -		mtd->erasesize = 4096;
>>> -	} else if (info->flags & SECT_4K_PMC) {
>>> -		nor->erase_opcode = SPINOR_OP_BE_4K_PMC;
>>> -		mtd->erasesize = 4096;
>>> -	} else
>>> -#endif
>>> -	{
>>> -		nor->erase_opcode = SPINOR_OP_SE;
>>> -		mtd->erasesize = info->sector_size;
>>> -	}
>>> -
>>>  	if (info->flags & SPI_NOR_NO_ERASE)
>>>  		mtd->flags |= MTD_NO_ERASE;
>>>  
>>>  	mtd->dev.parent = dev;
>>> -	nor->page_size = info->page_size;
>>> +	nor->page_size = params.page_size;
>>>  	mtd->writebufsize = nor->page_size;
>>>  
>>>  	if (np) {
>>>  		/* If we were instantiated by DT, use it */
>>>  		if (of_property_read_bool(np, "m25p,fast-read"))
>>> -			nor->flash_read = SPI_NOR_FAST;
>>> +			params.hwcaps.mask |= SNOR_HWCAPS_READ_FAST;
>>>  		else
>>> -			nor->flash_read = SPI_NOR_NORMAL;
>>> +			params.hwcaps.mask &= ~SNOR_HWCAPS_READ_FAST;
>>>  	} else {
>>>  		/* If we weren't instantiated by DT, default to fast-read */
>>> -		nor->flash_read = SPI_NOR_FAST;
>>> +		params.hwcaps.mask |= SNOR_HWCAPS_READ_FAST;
>>>  	}
>>>  
>>>  	/* Some devices cannot do fast-read, no matter what DT tells us */
>>>  	if (info->flags & SPI_NOR_NO_FR)
>>> -		nor->flash_read = SPI_NOR_NORMAL;
>>> +		params.hwcaps.mask &= ~SNOR_HWCAPS_READ_FAST;
>>> +
>>> +	/*
>>> +	 * Configure the SPI memory:
>>> +	 * - select op codes for (Fast) Read, Page Program and Sector Erase.
>>> +	 * - set the number of dummy cycles (mode cycles + wait states).
>>> +	 * - set the SPI protocols for register and memory accesses.
>>> +	 * - set the Quad Enable bit if needed (required by SPI x-y-4 protos).
>>> +	 */
>>> +	ret = spi_nor_setup(nor, info, &params, hwcaps);
>>> +	if (ret)
>>> +		return ret;
>>>  
>>> -	/* Quad/Dual-read mode takes precedence over fast/normal */
>>> -	if (mode == SPI_NOR_QUAD && info->flags & SPI_NOR_QUAD_READ) {
>>> -		ret = set_quad_mode(nor, info);
>>> +	if (nor->flash_quad_enable) {
>>> +		ret = nor->flash_quad_enable(nor);
>>>  		if (ret) {
>>>  			dev_err(dev, "quad mode not supported\n");
>>>  			return ret;
>>>  		}
>>> -		nor->flash_read = SPI_NOR_QUAD;
>>> -	} else if (mode == SPI_NOR_DUAL && info->flags & SPI_NOR_DUAL_READ) {
>>> -		nor->flash_read = SPI_NOR_DUAL;
>>>  	}
>>>  
>>> -	/* Default commands */
>>> -	switch (nor->flash_read) {
>>> -	case SPI_NOR_QUAD:
>>> -		nor->read_opcode = SPINOR_OP_READ_1_1_4;
>>> -		break;
>>> -	case SPI_NOR_DUAL:
>>> -		nor->read_opcode = SPINOR_OP_READ_1_1_2;
>>> -		break;
>>> -	case SPI_NOR_FAST:
>>> -		nor->read_opcode = SPINOR_OP_READ_FAST;
>>> -		break;
>>> -	case SPI_NOR_NORMAL:
>>> -		nor->read_opcode = SPINOR_OP_READ;
>>> -		break;
>>> -	default:
>>> -		dev_err(dev, "No Read opcode defined\n");
>>> -		return -EINVAL;
>>> -	}
>>> -
>>> -	nor->program_opcode = SPINOR_OP_PP;
>>> -
>>>  	if (info->addr_width)
>>>  		nor->addr_width = info->addr_width;
>>>  	else if (mtd->size > 0x1000000) {
>>> @@ -1731,8 +1988,6 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
>>>  		return -EINVAL;
>>>  	}
>>>  
>>> -	nor->read_dummy = spi_nor_read_dummy_cycles(nor);
>>> -
>>>  	if (info->flags & SPI_S3AN) {
>>>  		ret = s3an_nor_scan(info, nor);
>>>  		if (ret)
>>> diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h
>>> index f2a718030476..732ee6cd5330 100644
>>> --- a/include/linux/mtd/spi-nor.h
>>> +++ b/include/linux/mtd/spi-nor.h
>>> @@ -73,6 +73,15 @@
>>>  #define SPINOR_OP_BE_32K_4B	0x5c	/* Erase 32KiB block */
>>>  #define SPINOR_OP_SE_4B		0xdc	/* Sector erase (usually 64KiB) */
>>>  
>>> +/* Double Transfer Rate opcodes - defined in JEDEC JESD216B. */
>>> +#define SPINOR_OP_READ_1_1_1_DTR	0x0d
>>> +#define SPINOR_OP_READ_1_2_2_DTR	0xbd
>>> +#define SPINOR_OP_READ_1_4_4_DTR	0xed
>>> +
>>> +#define SPINOR_OP_READ_1_1_1_DTR_4B	0x0e
>>> +#define SPINOR_OP_READ_1_2_2_DTR_4B	0xbe
>>> +#define SPINOR_OP_READ_1_4_4_DTR_4B	0xee
>>> +
>>>  /* Used for SST flashes only. */
>>>  #define SPINOR_OP_BP		0x02	/* Byte program */
>>>  #define SPINOR_OP_WRDI		0x04	/* Write disable */
>>> @@ -119,13 +128,75 @@
>>>  /* Configuration Register bits. */
>>>  #define CR_QUAD_EN_SPAN		BIT(1)	/* Spansion Quad I/O */
>>>  
>>> -enum read_mode {
>>> -	SPI_NOR_NORMAL = 0,
>>> -	SPI_NOR_FAST,
>>> -	SPI_NOR_DUAL,
>>> -	SPI_NOR_QUAD,
>>> +
>>> +/* Supported SPI protocols */
>>> +#define SNOR_PROTO_WIDTH_MASK	GENMASK(7, 0)
>>> +
>>> +#define SNOR_PROTO_CLASS_MASK	GENMASK(9, 8)
>>> +#define SNOR_PROTO_CLASS_1_1_N	(0x0u << 8)
>>> +#define SNOR_PROTO_CLASS_1_N_N	(0x1u << 8)
>>> +#define SNOR_PROTO_CLASS_N_N_N	(0x2u << 8)
>>> +
>>> +#define SNOR_PROTO_IS_DTR	BIT(10)	/* Double Transfer Rate */
>>> +
>>> +#define SNOR_PROTO_STR(_pclass, _pwidth) \
>>> +	((_pclass) | (_pwidth))
>>> +#define SNOR_PROTO_DTR(_pclass, _pwidth) \
>>> +	(SNOR_PROTO_IS_DTR | (_pclass) | (_pwidth))
>>> +
>>> +enum spi_nor_protocol {
>>> +	SNOR_PROTO_1_1_1 = SNOR_PROTO_STR(SNOR_PROTO_CLASS_1_1_N, 1),
>>> +	SNOR_PROTO_1_1_2 = SNOR_PROTO_STR(SNOR_PROTO_CLASS_1_1_N, 2),
>>> +	SNOR_PROTO_1_1_4 = SNOR_PROTO_STR(SNOR_PROTO_CLASS_1_1_N, 4),
>>> +	SNOR_PROTO_1_1_8 = SNOR_PROTO_STR(SNOR_PROTO_CLASS_1_1_N, 8),
>>> +	SNOR_PROTO_1_2_2 = SNOR_PROTO_STR(SNOR_PROTO_CLASS_1_N_N, 2),
>>> +	SNOR_PROTO_1_4_4 = SNOR_PROTO_STR(SNOR_PROTO_CLASS_1_N_N, 4),
>>> +	SNOR_PROTO_1_8_8 = SNOR_PROTO_STR(SNOR_PROTO_CLASS_1_N_N, 8),
>>> +	SNOR_PROTO_2_2_2 = SNOR_PROTO_STR(SNOR_PROTO_CLASS_N_N_N, 2),
>>> +	SNOR_PROTO_4_4_4 = SNOR_PROTO_STR(SNOR_PROTO_CLASS_N_N_N, 4),
>>> +	SNOR_PROTO_8_8_8 = SNOR_PROTO_STR(SNOR_PROTO_CLASS_N_N_N, 8),
>>> +
>>> +	SNOR_PROTO_1_1_1_DTR = SNOR_PROTO_DTR(SNOR_PROTO_CLASS_1_1_N, 1),
>>> +	SNOR_PROTO_1_2_2_DTR = SNOR_PROTO_DTR(SNOR_PROTO_CLASS_1_N_N, 2),
>>> +	SNOR_PROTO_1_4_4_DTR = SNOR_PROTO_DTR(SNOR_PROTO_CLASS_1_N_N, 4),
>>> +	SNOR_PROTO_1_8_8_DTR = SNOR_PROTO_DTR(SNOR_PROTO_CLASS_1_N_N, 8),
>>>  };
>>>  
>>> +static inline bool spi_nor_protocol_is_dtr(enum spi_nor_protocol proto)
>>> +{
>>> +	return (proto & SNOR_PROTO_IS_DTR) == SNOR_PROTO_IS_DTR;
>>> +}
>>> +
>>> +static inline u32 spi_nor_get_protocol_class(enum spi_nor_protocol proto)
>>> +{
>>> +	return proto & SNOR_PROTO_CLASS_MASK;
>>> +}
>>> +
>>> +static inline u8 spi_nor_get_protocol_width(enum spi_nor_protocol proto)
>>> +{
>>> +	return proto & SNOR_PROTO_WIDTH_MASK;
>>> +}
>>> +
>>> +static inline u8 spi_nor_get_protocol_inst_width(enum spi_nor_protocol proto)
>>> +{
>>> +	return (spi_nor_get_protocol_class(proto) == SNOR_PROTO_CLASS_N_N_N) ?
>>> +		spi_nor_get_protocol_width(proto) :
>>> +		1u;
>>> +}
>>> +
>>> +static inline u8 spi_nor_get_protocol_addr_width(enum spi_nor_protocol proto)
>>> +{
>>> +	return (spi_nor_get_protocol_class(proto) != SNOR_PROTO_CLASS_1_1_N) ?
>>> +		spi_nor_get_protocol_width(proto) :
>>> +		1u;
>>> +}
>>> +
>>> +static inline u8 spi_nor_get_protocol_data_width(enum spi_nor_protocol proto)
>>> +{
>>> +	return spi_nor_get_protocol_width(proto);
>>> +}
>>> +
>>> +
>>>  #define SPI_NOR_MAX_CMD_SIZE	8
>>>  enum spi_nor_ops {
>>>  	SPI_NOR_OPS_READ = 0,
>>> @@ -154,9 +225,11 @@ enum spi_nor_option_flags {
>>>   * @read_opcode:	the read opcode
>>>   * @read_dummy:		the dummy needed by the read operation
>>>   * @program_opcode:	the program opcode
>>> - * @flash_read:		the mode of the read
>>>   * @sst_write_second:	used by the SST write operation
>>>   * @flags:		flag options for the current SPI-NOR (SNOR_F_*)
>>> + * @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
>>>   * @cmd_buf:		used by the write_reg
>>>   * @prepare:		[OPTIONAL] do some preparations for the
>>>   *			read/write/erase/lock/unlock operations
>>> @@ -173,6 +246,7 @@ enum spi_nor_option_flags {
>>>   * @flash_unlock:	[FLASH-SPECIFIC] unlock a region of the SPI NOR
>>>   * @flash_is_locked:	[FLASH-SPECIFIC] check if a region of the SPI NOR is
>>>   *			completely locked
>>> + * @flash_quad_enable:	[FLASH-SPECIFIC] set the Quad Enable bit of the SPI NOR
>>>   * @priv:		the private data
>>>   */
>>>  struct spi_nor {
>>> @@ -185,7 +259,9 @@ struct spi_nor {
>>>  	u8			read_opcode;
>>>  	u8			read_dummy;
>>>  	u8			program_opcode;
>>> -	enum read_mode		flash_read;
>>> +	enum spi_nor_protocol	read_proto;
>>> +	enum spi_nor_protocol	write_proto;
>>> +	enum spi_nor_protocol	reg_proto;
>>>  	bool			sst_write_second;
>>>  	u32			flags;
>>>  	u8			cmd_buf[SPI_NOR_MAX_CMD_SIZE];
>>> @@ -204,6 +280,7 @@ struct spi_nor {
>>>  	int (*flash_lock)(struct spi_nor *nor, loff_t ofs, uint64_t len);
>>>  	int (*flash_unlock)(struct spi_nor *nor, loff_t ofs, uint64_t len);
>>>  	int (*flash_is_locked)(struct spi_nor *nor, loff_t ofs, uint64_t len);
>>> +	int (*flash_quad_enable)(struct spi_nor *nor);
>>>  
>>>  	void *priv;
>>>  };
>>> @@ -219,11 +296,73 @@ static inline struct device_node *spi_nor_get_flash_node(struct spi_nor *nor)
>>>  	return mtd_get_of_node(&nor->mtd);
>>>  }
>>>  
>>> +
>>> +/**
>>> + * struct spi_nor_hwcaps - Structure for describing the hardware capabilies
>>> + * supported by the SPI controller (bus master).
>>> + * @mask:		the bitmask listing all the supported hw capabilies
>>> + */
>>> +struct spi_nor_hwcaps {
>>> +	u32	mask;
>>> +};
>>> +
>>> +/*
>>> + *(Fast) Read capabilities.
>>> + * MUST be ordered by priority: the higher bit position, the higher priority.
>>> + * As a matter of performances, it is relevant to use Octo SPI protocols first,
>>> + * then Quad SPI protocols before Dual SPI protocols, Fast Read and lastly
>>> + * (Slow) Read.
>>> + */
>>> +#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)
>>> +
>>> +#define SNOR_HWCAPS_READ_DUAL		GENMASK(7, 4)
>>> +#define SNOR_HWCAPS_READ_1_1_2		BIT(4)
>>> +#define SNOR_HWCAPS_READ_1_2_2		BIT(5)
>>> +#define SNOR_HWCAPS_READ_2_2_2		BIT(6)
>>> +#define SNOR_HWCAPS_READ_1_2_2_DTR	BIT(7)
>>> +
>>> +#define SNOR_HWCAPS_READ_QUAD		GENMASK(11, 8)
>>> +#define SNOR_HWCAPS_READ_1_1_4		BIT(8)
>>> +#define SNOR_HWCAPS_READ_1_4_4		BIT(9)
>>> +#define SNOR_HWCAPS_READ_4_4_4		BIT(10)
>>> +#define SNOR_HWCAPS_READ_1_4_4_DTR	BIT(11)
>>> +
>>> +#define SNOR_HWCPAS_READ_OCTO		GENMASK(15, 12)
>>> +#define SNOR_HWCAPS_READ_1_1_8		BIT(12)
>>> +#define SNOR_HWCAPS_READ_1_8_8		BIT(13)
>>> +#define SNOR_HWCAPS_READ_8_8_8		BIT(14)
>>> +#define SNOR_HWCAPS_READ_1_8_8_DTR	BIT(15)
>>> +
>>> +/*
>>> + * Page Program capabilities.
>>> + * MUST be ordered by priority: the higher bit position, the higher priority.
>>> + * Like (Fast) Read capabilities, Octo/Quad SPI protocols are preferred to the
>>> + * legacy SPI 1-1-1 protocol.
>>> + * Note that Dual Page Programs are not supported because there is no existing
>>> + * 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_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_OCTO	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)
>>> +
>>>  /**
>>>   * spi_nor_scan() - scan the SPI NOR
>>>   * @nor:	the spi_nor structure
>>>   * @name:	the chip type name
>>> - * @mode:	the read mode supported by the driver
>>> + * @hwcaps:	the hardware capabilities supported by the controller driver
>>>   *
>>>   * The drivers can use this fuction to scan the SPI NOR.
>>>   * In the scanning, it will try to get all the necessary information to
>>> @@ -233,6 +372,7 @@ static inline struct device_node *spi_nor_get_flash_node(struct spi_nor *nor)
>>>   *
>>>   * Return: 0 for success, others for failure.
>>>   */
>>> -int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode);
>>> +int spi_nor_scan(struct spi_nor *nor, const char *name,
>>> +		 const struct spi_nor_hwcaps *hwcaps);
>>>  
>>>  #endif
>>>
>>
>>
>> ______________________________________________________
>> Linux MTD discussion mailing list
>> http://lists.infradead.org/mailman/listinfo/linux-mtd/
>>
> 

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

* Re: [PATCH v5 1/6] mtd: spi-nor: introduce more SPI protocols and the Dual Transfer Mode
  2017-03-24 10:03       ` Cédric Le Goater
@ 2017-03-24 11:39         ` Cyrille Pitchen
  0 siblings, 0 replies; 27+ messages in thread
From: Cyrille Pitchen @ 2017-03-24 11:39 UTC (permalink / raw)
  To: Cédric Le Goater, Cyrille Pitchen, marek.vasut, linux-mtd,
	jartur, kdasu.kdev, mar.krzeminski
  Cc: boris.brezillon, richard, nicolas.ferre, linux-kernel,
	computersforpeace, dwmw2

Le 24/03/2017 à 11:03, Cédric Le Goater a écrit :
> On 03/23/2017 08:10 PM, Cyrille Pitchen wrote:
>> Hi Cédic,
>>
>> Le 23/03/2017 à 16:13, Cédric Le Goater a écrit :
>>> On 03/23/2017 12:33 AM, Cyrille Pitchen wrote:
>>>> This patch changes the prototype of spi_nor_scan(): its 3rd parameter
>>>> is replaced by a 'struct spi_nor_hwcaps' pointer, which tells the spi-nor
>>>> framework about the actual hardware capabilities supported by the SPI
>>>> controller and its driver.
>>>>
>>>> Besides, this patch also introduces a new 'struct spi_nor_flash_parameter'
>>>> telling the spi-nor framework about the hardware capabilities supported by
>>>> the SPI flash memory and the associated settings required to use those
>>>> hardware caps.
>>>>
>>>> Currently the 'struct spi_nor_flash_parameter' is filled with legacy
>>>> values but a later patch will allow to fill it dynamically by reading the
>>>> JESD216 Serial Flash Discoverable Parameter (SFDP) tables from the SPI
>>>> memory.
>>>>
>>>> With both structures, the spi-nor framework can now compute the best
>>>> match between hardware caps supported by both the (Q)SPI memory and
>>>> controller hence selecting the relevant SPI protocols and op codes for
>>>> (Fast) Read, Page Program and Sector Erase operations.
>>>>
>>>> The 'struct spi_nor_flash_parameter' also provides the spi-nor framework
>>>> with the number of dummy cycles to be used with each Fast Read commands
>>>> and the erase block size associated to the erase block op codes.
>>>>
>>>> Finally the 'struct spi_nor_flash_parameter', through the optional
>>>> .enable_quad_io() hook, tells the spi-nor framework how to set the Quad
>>
>> .enable_quad_io() was renamed into .quad_enable()
>>
>>>> Enable (QE) bit of the QSPI memory to enable its Quad SPI features.
>>>
>>> The Aspeed controller only supports Dual I/O and a helper similar 
>>> to the one for Quad I/O would be needed to setup the chip for 
>>> multiple I/O: 
>>>
>>> 	int (*dual_enable)(struct spi_nor *nor);
>>>
>>> Is the approach correct ? or maybe rename the current 'quad_enable()'
>>> to 'multiple_enable()' and add an extra parameter.
>>>
>>
>> No, nor->flash_quad_enable() [ = params->quad_enable() ] is a flash
>> specific handler as described in spi-nor.h and like other flash specific
>> handlers such as flash_lock(), flash_unlock() or flash_is_locked(), is
>> only used internally in spi-nor.c: spi-nor controller drivers like
>> aspeed-smc.c should not use those handlers at all.
> 
> I was talking about adding a spi-nor handler to set up the chip for 
> dual I/O because today we only have one for quad I/O, and some
> controllers (like the Aspeed) do not support Quad. So the only 
> fast I/O we can do today for these controllers is dual I/O data 
> (SPI_NOR_DUAL)
>

OK but as I've explained below, there is nothing to do at the memory
side before using the SPI 1-1-2 or SPI 1-2-2 protocols: there is no need
to reassign pins IO0 and IO1 to other functions ;)

Hence, the JESD216B specification describes all the vendor specific
procedures to be done before using SPI 1-1-4 or SPI 1-4-4 but no
procedure is provided for the Dual SPI case simply because there is
nothing to do!

There are also dedicated procedures for SPI 2-2-2 and 4-4-4 protocols
but those protocols are not supported yet because they come with many
associated issues but no real benefit.
They are state-full and the spi-nor framework would have to guess in
which state the SPI NOR memory is when running spi_nor_scan(). Currently
spi_nor_scan() relies on the READ JEDEC ID (9Fh) command to probe the
memory but for instance Micron memories don't support this command once
in 2-2-2 or 4-4-4 modes; only the READ JEDEC ID Multiple I/O (AFh) is
supported. However this command is not supported at all by many other
vendors. Winbond memories expect the 9Fh command even in 4-4-4 mode but
I've found out that sending 9Fh with the SPI 4-4-4 protocol to many
Micron QSPI memories has a bad side effect: it seems that they return in
their reset state. Also, we don't know the right command to send before
we actually know the memory vendor...

So it's a real mess to support the 2-2-2 and 4-4-4 modes. Besides, the
bandwidth improvement would be so small that it is not worth.

So except for the 2-2-2 mode, there is no SPI command to send to the SPI
flash memory before using Dual SPI protocol.

So everything should already be ready if you want to add support to the
SPI 1-1-2 or SPI 1-2-2 protocols in the Aspeed driver.
If you need support or have more questions, I can help you of course :)

>> the choice of the nor->flash_quad_enable() handler depends only on the
>> memory manufacturer (and the memory part):
>> - (none): Micron
>> - macronix_quad_enable: Macronix
>> - spansion_quad_enable: Spansion, Winbond, ...
>> - spansion_new_quad_enable: Spansion (latest memories), ...
>> - sr2_bit7_quad_enable: ??? (defined in the JESD216B specification)
> 
> yes. Some Micron have a VCONF register to set multiple I/O also.
>  
>> The purpose of those functions is to implement the vendor specific
>> procedure to set the so called "Quad Enable" (QE) bit in some Status
>> Register of the SPI NOR memory.
>>
>> Indeed, most QSPI memories are pin to pin compatible with legacy SPI
>> memories. 2 pins of those memories were dedicated to the Write Protect
>> (WP) and Reset/Hold (RST) functions. Hence before using any Quad SPI
>> commands, almost all QSPI memories (Micron being the only exception I
>> know) require us to set their QE bit so the WP and RST pins are
>> reassigned to functions IO2 and IO3, the 3rd and 4th IO lines needed by
>> SPI 1-1-4 and SPI 1-4-4 protocols. Then the Write Protect and Hold/Reset
>> functions are disabled.
>>
>> From a software point of view, there is nothing to do before using the
>> SPI 1-1-2 or SPI 1-2-2 protocols. As a matter of fact, with those
>> protocols, MISO and MOSI pins are simply reassigned to functions IO0 and
>> IO1.
>>
>>
>> Back to the SPI flash controller, if the hardware needs to be configured
>> in some way to use any Dual or Quad SPI protocols, it has to be done
>> before calling spi_nor_scan() or more likely directly inside the
>> controller driver specific implementation of nor->read(), nor->write().
> 
> yes.
> 
>> With nor->read(), the driver has to check the value of nor->read_proto
>> to know the actual number of I/O lines used during Instruction (x),
>> Address/Dummy (y) and Data (z) clock cycles: nor->read_proto provides
>> the driver with the SPI x-y-z protocol to be used.
>>
>> Also nor->write() has to check the value of nor->write_proto.
>>
>> [ nor->erase() has to check the value of nor->reg_proto: I removed
>> nor->erase_proto since it always had the same value as nor->reg_proto. ]
>>
>> Both nor->write() and nor->read are controller driver specific: they
>> must be implemented by the controller driver and set before calling
>> spi_nor_scan().
>>
>> Then nor->read_proto and nor->write_proto are chosen from spi_nor_scan()
>> based on the actual hardware capabilities shared by both the SPI memory
>> and controller. The SPI controller driver tells the spi-nor framework
>> which SPI protocols it supports or wants to use by setting the new
>> 'struct spi_nor_hwcaps' argument of spi_nor_scan() accordingly.
>>
>>
>> You can have a look at the atmel-quadspi.c driver to have an example of
>> what to do to support the SPI 1-2-2 or SPI 1-4-4 protocols at the
>> controller side.
>>
>> I've updated the Atmel Quad SPI driver since I'm the maintainer of this
>> driver so I know the exact hardware capabilities of this controller but
>> I don't have such a knowledge for Quad SPI controllers of other vendors.
>>
>> Please note that patch is conservative: if a controller driver like the
>> Aspeed one currently doesn't support the Quad or Dual SPI protocols, the
>> patch doesn't enable them in hwcaps.mask.
>>
>> Maintainers of the controller drivers will have to, if they want, extend
>> their driver to add the support of new SPI protocols, otherwise those
>> drivers will keep on working exactly as they used to do before this
>> series. For instance, a controller driver which used SPI_NOR_QUAD before
>> still uses the SPI 1-1-4 protocol.
> 
> Yes. I have some patches adding DUAL support to Aspeed but I will rebase 
> on your patchset when it is merged because they currently conflict.
> 
> Thanks for the detailed explanations !
> 
> C. 
> 
> 
>> Best regards,
>>
>> Cyrille
>>
>>
>>> I have a bunch of patches queued for Dual I/O data support but I 
>>> think they will conflict with your patches. I will wait for this 
>>> one to be merged. Then, I can look at Dual I/O address + data 
>>> support.
>>>
>>> Thanks,
>>>
>>> C.     
>>>
>>>
>>>> Signed-off-by: Cyrille Pitchen <cyrille.pitchen@atmel.com>
>>>> ---
>>>>  drivers/mtd/devices/m25p80.c          |  16 +-
>>>>  drivers/mtd/spi-nor/aspeed-smc.c      |  23 +-
>>>>  drivers/mtd/spi-nor/atmel-quadspi.c   |  80 +++---
>>>>  drivers/mtd/spi-nor/cadence-quadspi.c |  18 +-
>>>>  drivers/mtd/spi-nor/fsl-quadspi.c     |   8 +-
>>>>  drivers/mtd/spi-nor/hisi-sfc.c        |  31 ++-
>>>>  drivers/mtd/spi-nor/intel-spi.c       |   7 +-
>>>>  drivers/mtd/spi-nor/mtk-quadspi.c     |  16 +-
>>>>  drivers/mtd/spi-nor/nxp-spifi.c       |  22 +-
>>>>  drivers/mtd/spi-nor/spi-nor.c         | 441 +++++++++++++++++++++++++++-------
>>>>  include/linux/mtd/spi-nor.h           | 158 +++++++++++-
>>>>  11 files changed, 643 insertions(+), 177 deletions(-)
>>>>
>>>> diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c
>>>> index c4df3b1bded0..68986a26c8fe 100644
>>>> --- a/drivers/mtd/devices/m25p80.c
>>>> +++ b/drivers/mtd/devices/m25p80.c
>>>> @@ -111,10 +111,10 @@ static ssize_t m25p80_write(struct spi_nor *nor, loff_t to, size_t len,
>>>>  
>>>>  static inline unsigned int m25p80_rx_nbits(struct spi_nor *nor)
>>>>  {
>>>> -	switch (nor->flash_read) {
>>>> -	case SPI_NOR_DUAL:
>>>> +	switch (nor->read_proto) {
>>>> +	case SNOR_PROTO_1_1_2:
>>>>  		return 2;
>>>> -	case SPI_NOR_QUAD:
>>>> +	case SNOR_PROTO_1_1_4:
>>>>  		return 4;
>>>>  	default:
>>>>  		return 0;
>>>> @@ -196,7 +196,9 @@ static int m25p_probe(struct spi_device *spi)
>>>>  	struct flash_platform_data	*data;
>>>>  	struct m25p *flash;
>>>>  	struct spi_nor *nor;
>>>> -	enum read_mode mode = SPI_NOR_NORMAL;
>>>> +	struct spi_nor_hwcaps hwcaps = {
>>>> +		.mask = (SNOR_HWCAPS_READ | SNOR_HWCAPS_PP),
>>>> +	};
>>>>  	char *flash_name;
>>>>  	int ret;
>>>>  
>>>> @@ -222,9 +224,9 @@ static int m25p_probe(struct spi_device *spi)
>>>>  	flash->spi = spi;
>>>>  
>>>>  	if (spi->mode & SPI_RX_QUAD)
>>>> -		mode = SPI_NOR_QUAD;
>>>> +		hwcaps.mask |= SNOR_HWCAPS_READ_1_1_4;
>>>>  	else if (spi->mode & SPI_RX_DUAL)
>>>> -		mode = SPI_NOR_DUAL;
>>>> +		hwcaps.mask |= SNOR_HWCAPS_READ_1_1_2;
>>>>  
>>>>  	if (data && data->name)
>>>>  		nor->mtd.name = data->name;
>>>> @@ -241,7 +243,7 @@ static int m25p_probe(struct spi_device *spi)
>>>>  	else
>>>>  		flash_name = spi->modalias;
>>>>  
>>>> -	ret = spi_nor_scan(nor, flash_name, mode);
>>>> +	ret = spi_nor_scan(nor, flash_name, &hwcaps);
>>>>  	if (ret)
>>>>  		return ret;
>>>>  
>>>> diff --git a/drivers/mtd/spi-nor/aspeed-smc.c b/drivers/mtd/spi-nor/aspeed-smc.c
>>>> index 56051d30f000..723026d9cf0c 100644
>>>> --- a/drivers/mtd/spi-nor/aspeed-smc.c
>>>> +++ b/drivers/mtd/spi-nor/aspeed-smc.c
>>>> @@ -585,14 +585,12 @@ static int aspeed_smc_chip_setup_finish(struct aspeed_smc_chip *chip)
>>>>  	 * TODO: Adjust clocks if fast read is supported and interpret
>>>>  	 * SPI-NOR flags to adjust controller settings.
>>>>  	 */
>>>> -	switch (chip->nor.flash_read) {
>>>> -	case SPI_NOR_NORMAL:
>>>> -		cmd = CONTROL_COMMAND_MODE_NORMAL;
>>>> -		break;
>>>> -	case SPI_NOR_FAST:
>>>> -		cmd = CONTROL_COMMAND_MODE_FREAD;
>>>> -		break;
>>>> -	default:
>>>> +	if (chip->nor.read_proto == SNOR_PROTO_1_1_1) {
>>>> +		if (chip->nor.read_dummy == 0)
>>>> +			cmd = CONTROL_COMMAND_MODE_NORMAL;
>>>> +		else
>>>> +			cmd = CONTROL_COMMAND_MODE_FREAD;
>>>> +	} else {
>>>>  		dev_err(chip->nor.dev, "unsupported SPI read mode\n");
>>>>  		return -EINVAL;
>>>>  	}
>>>> @@ -608,6 +606,11 @@ static int aspeed_smc_chip_setup_finish(struct aspeed_smc_chip *chip)
>>>>  static int aspeed_smc_setup_flash(struct aspeed_smc_controller *controller,
>>>>  				  struct device_node *np, struct resource *r)
>>>>  {
>>>> +	struct spi_nor_hwcaps hwcaps = {
>>>> +		.mask = (SNOR_HWCAPS_READ |
>>>> +			 SNOR_HWCAPS_READ_FAST |
>>>> +			 SNOR_HWCAPS_PP),
>>>> +	};
>>>>  	const struct aspeed_smc_info *info = controller->info;
>>>>  	struct device *dev = controller->dev;
>>>>  	struct device_node *child;
>>>> @@ -671,11 +674,11 @@ static int aspeed_smc_setup_flash(struct aspeed_smc_controller *controller,
>>>>  			break;
>>>>  
>>>>  		/*
>>>> -		 * TODO: Add support for SPI_NOR_QUAD and SPI_NOR_DUAL
>>>> +		 * TODO: Add support for Dual and Quad SPI protocols
>>>>
>>>>  		 * attach when board support is present as determined
>>>>  		 * by of property.
>>>>  		 */
>>>> -		ret = spi_nor_scan(nor, NULL, SPI_NOR_NORMAL);
>>>> +		ret = spi_nor_scan(nor, NULL, &hwcaps);
>>>>  		if (ret)
>>>>  			break;
>>>>  
>>>> diff --git a/drivers/mtd/spi-nor/atmel-quadspi.c b/drivers/mtd/spi-nor/atmel-quadspi.c
>>>> index 47937d9beec6..9f579f7c1733 100644
>>>> --- a/drivers/mtd/spi-nor/atmel-quadspi.c
>>>> +++ b/drivers/mtd/spi-nor/atmel-quadspi.c
>>>> @@ -275,14 +275,48 @@ static void atmel_qspi_debug_command(struct atmel_qspi *aq,
>>>>  
>>>>  static int atmel_qspi_run_command(struct atmel_qspi *aq,
>>>>  				  const struct atmel_qspi_command *cmd,
>>>> -				  u32 ifr_tfrtyp, u32 ifr_width)
>>>> +				  u32 ifr_tfrtyp, enum spi_nor_protocol proto)
>>>>  {
>>>>  	u32 iar, icr, ifr, sr;
>>>>  	int err = 0;
>>>>  
>>>>  	iar = 0;
>>>>  	icr = 0;
>>>> -	ifr = ifr_tfrtyp | ifr_width;
>>>> +	ifr = ifr_tfrtyp;
>>>> +
>>>> +	/* Set the SPI protocol */
>>>> +	switch (proto) {
>>>> +	case SNOR_PROTO_1_1_1:
>>>> +		ifr |= QSPI_IFR_WIDTH_SINGLE_BIT_SPI;
>>>> +		break;
>>>> +
>>>> +	case SNOR_PROTO_1_1_2:
>>>> +		ifr |= QSPI_IFR_WIDTH_DUAL_OUTPUT;
>>>> +		break;
>>>> +
>>>> +	case SNOR_PROTO_1_1_4:
>>>> +		ifr |= QSPI_IFR_WIDTH_QUAD_OUTPUT;
>>>> +		break;
>>>> +
>>>> +	case SNOR_PROTO_1_2_2:
>>>> +		ifr |= QSPI_IFR_WIDTH_DUAL_IO;
>>>> +		break;
>>>> +
>>>> +	case SNOR_PROTO_1_4_4:
>>>> +		ifr |= QSPI_IFR_WIDTH_QUAD_IO;
>>>> +		break;
>>>> +
>>>> +	case SNOR_PROTO_2_2_2:
>>>> +		ifr |= QSPI_IFR_WIDTH_DUAL_CMD;
>>>> +		break;
>>>> +
>>>> +	case SNOR_PROTO_4_4_4:
>>>> +		ifr |= QSPI_IFR_WIDTH_QUAD_CMD;
>>>> +		break;
>>>> +
>>>> +	default:
>>>> +		return -EINVAL;
>>>> +	}
>>>>  
>>>>  	/* Compute instruction parameters */
>>>>  	if (cmd->enable.bits.instruction) {
>>>> @@ -434,7 +468,7 @@ static int atmel_qspi_read_reg(struct spi_nor *nor, u8 opcode,
>>>>  	cmd.rx_buf = buf;
>>>>  	cmd.buf_len = len;
>>>>  	return atmel_qspi_run_command(aq, &cmd, QSPI_IFR_TFRTYP_TRSFR_READ,
>>>> -				      QSPI_IFR_WIDTH_SINGLE_BIT_SPI);
>>>> +				      nor->reg_proto);
>>>>  }
>>>>  
>>>>  static int atmel_qspi_write_reg(struct spi_nor *nor, u8 opcode,
>>>> @@ -450,7 +484,7 @@ static int atmel_qspi_write_reg(struct spi_nor *nor, u8 opcode,
>>>>  	cmd.tx_buf = buf;
>>>>  	cmd.buf_len = len;
>>>>  	return atmel_qspi_run_command(aq, &cmd, QSPI_IFR_TFRTYP_TRSFR_WRITE,
>>>> -				      QSPI_IFR_WIDTH_SINGLE_BIT_SPI);
>>>> +				      nor->reg_proto);
>>>>  }
>>>>  
>>>>  static ssize_t atmel_qspi_write(struct spi_nor *nor, loff_t to, size_t len,
>>>> @@ -469,7 +503,7 @@ static ssize_t atmel_qspi_write(struct spi_nor *nor, loff_t to, size_t len,
>>>>  	cmd.tx_buf = write_buf;
>>>>  	cmd.buf_len = len;
>>>>  	ret = atmel_qspi_run_command(aq, &cmd, QSPI_IFR_TFRTYP_TRSFR_WRITE_MEM,
>>>> -				     QSPI_IFR_WIDTH_SINGLE_BIT_SPI);
>>>> +				     nor->write_proto);
>>>>  	return (ret < 0) ? ret : len;
>>>>  }
>>>>  
>>>> @@ -484,7 +518,7 @@ static int atmel_qspi_erase(struct spi_nor *nor, loff_t offs)
>>>>  	cmd.instruction = nor->erase_opcode;
>>>>  	cmd.address = (u32)offs;
>>>>  	return atmel_qspi_run_command(aq, &cmd, QSPI_IFR_TFRTYP_TRSFR_WRITE,
>>>> -				      QSPI_IFR_WIDTH_SINGLE_BIT_SPI);
>>>> +				      nor->reg_proto);
>>>>  }
>>>>  
>>>>  static ssize_t atmel_qspi_read(struct spi_nor *nor, loff_t from, size_t len,
>>>> @@ -493,27 +527,8 @@ static ssize_t atmel_qspi_read(struct spi_nor *nor, loff_t from, size_t len,
>>>>  	struct atmel_qspi *aq = nor->priv;
>>>>  	struct atmel_qspi_command cmd;
>>>>  	u8 num_mode_cycles, num_dummy_cycles;
>>>> -	u32 ifr_width;
>>>>  	ssize_t ret;
>>>>  
>>>> -	switch (nor->flash_read) {
>>>> -	case SPI_NOR_NORMAL:
>>>> -	case SPI_NOR_FAST:
>>>> -		ifr_width = QSPI_IFR_WIDTH_SINGLE_BIT_SPI;
>>>> -		break;
>>>> -
>>>> -	case SPI_NOR_DUAL:
>>>> -		ifr_width = QSPI_IFR_WIDTH_DUAL_OUTPUT;
>>>> -		break;
>>>> -
>>>> -	case SPI_NOR_QUAD:
>>>> -		ifr_width = QSPI_IFR_WIDTH_QUAD_OUTPUT;
>>>> -		break;
>>>> -
>>>> -	default:
>>>> -		return -EINVAL;
>>>> -	}
>>>> -
>>>>  	if (nor->read_dummy >= 2) {
>>>>  		num_mode_cycles = 2;
>>>>  		num_dummy_cycles = nor->read_dummy - 2;
>>>> @@ -536,7 +551,7 @@ static ssize_t atmel_qspi_read(struct spi_nor *nor, loff_t from, size_t len,
>>>>  	cmd.rx_buf = read_buf;
>>>>  	cmd.buf_len = len;
>>>>  	ret = atmel_qspi_run_command(aq, &cmd, QSPI_IFR_TFRTYP_TRSFR_READ_MEM,
>>>> -				     ifr_width);
>>>> +				     nor->read_proto);
>>>>  	return (ret < 0) ? ret : len;
>>>>  }
>>>>  
>>>> @@ -590,6 +605,17 @@ static irqreturn_t atmel_qspi_interrupt(int irq, void *dev_id)
>>>>  
>>>>  static int atmel_qspi_probe(struct platform_device *pdev)
>>>>  {
>>>> +	struct spi_nor_hwcaps hwcaps = {
>>>> +		.mask = (SNOR_HWCAPS_READ |
>>>> +			 SNOR_HWCAPS_READ_FAST |
>>>> +			 SNOR_HWCAPS_READ_1_1_2 |
>>>> +			 SNOR_HWCAPS_READ_1_2_2 |
>>>> +			 SNOR_HWCAPS_READ_1_1_4 |
>>>> +			 SNOR_HWCAPS_READ_1_4_4 |
>>>> +			 SNOR_HWCAPS_PP |
>>>> +			 SNOR_HWCAPS_PP_1_1_4 |
>>>> +			 SNOR_HWCAPS_PP_1_4_4),
>>>> +	};
>>>>  	struct device_node *child, *np = pdev->dev.of_node;
>>>>  	struct atmel_qspi *aq;
>>>>  	struct resource *res;
>>>> @@ -679,7 +705,7 @@ static int atmel_qspi_probe(struct platform_device *pdev)
>>>>  	if (err)
>>>>  		goto disable_clk;
>>>>  
>>>> -	err = spi_nor_scan(nor, NULL, SPI_NOR_QUAD);
>>>> +	err = spi_nor_scan(nor, NULL, &hwcaps);
>>>>  	if (err)
>>>>  		goto disable_clk;
>>>>  
>>>> diff --git a/drivers/mtd/spi-nor/cadence-quadspi.c b/drivers/mtd/spi-nor/cadence-quadspi.c
>>>> index 9f8102de1b16..3f91a3e97892 100644
>>>> --- a/drivers/mtd/spi-nor/cadence-quadspi.c
>>>> +++ b/drivers/mtd/spi-nor/cadence-quadspi.c
>>>> @@ -855,15 +855,14 @@ static int cqspi_set_protocol(struct spi_nor *nor, const int read)
>>>>  	f_pdata->data_width = CQSPI_INST_TYPE_SINGLE;
>>>>  
>>>>  	if (read) {
>>>> -		switch (nor->flash_read) {
>>>> -		case SPI_NOR_NORMAL:
>>>> -		case SPI_NOR_FAST:
>>>> +		switch (nor->read_proto) {
>>>> +		case SNOR_PROTO_1_1_1:
>>>>  			f_pdata->data_width = CQSPI_INST_TYPE_SINGLE;
>>>>  			break;
>>>> -		case SPI_NOR_DUAL:
>>>> +		case SNOR_PROTO_1_1_2:
>>>>  			f_pdata->data_width = CQSPI_INST_TYPE_DUAL;
>>>>  			break;
>>>> -		case SPI_NOR_QUAD:
>>>> +		case SNOR_PROTO_1_1_4:
>>>>  			f_pdata->data_width = CQSPI_INST_TYPE_QUAD;
>>>>  			break;
>>>>  		default:
>>>> @@ -1069,6 +1068,13 @@ static void cqspi_controller_init(struct cqspi_st *cqspi)
>>>>  
>>>>  static int cqspi_setup_flash(struct cqspi_st *cqspi, struct device_node *np)
>>>>  {
>>>> +	struct spi_nor_hwcaps hwcaps = {
>>>> +		.mask = (SNOR_HWCAPS_READ |
>>>> +			 SNOR_HWCAPS_READ_FAST |
>>>> +			 SNOR_HWCAPS_READ_1_1_2 |
>>>> +			 SNOR_HWCAPS_READ_1_1_4 |
>>>> +			 SNOR_HWCAPS_PP),
>>>> +	};
>>>>  	struct platform_device *pdev = cqspi->pdev;
>>>>  	struct device *dev = &pdev->dev;
>>>>  	struct cqspi_flash_pdata *f_pdata;
>>>> @@ -1123,7 +1129,7 @@ static int cqspi_setup_flash(struct cqspi_st *cqspi, struct device_node *np)
>>>>  			goto err;
>>>>  		}
>>>>  
>>>> -		ret = spi_nor_scan(nor, NULL, SPI_NOR_QUAD);
>>>> +		ret = spi_nor_scan(nor, NULL, &hwcaps);
>>>>  		if (ret)
>>>>  			goto err;
>>>>  
>>>> diff --git a/drivers/mtd/spi-nor/fsl-quadspi.c b/drivers/mtd/spi-nor/fsl-quadspi.c
>>>> index 1476135e0d50..ec9c8e960fd2 100644
>>>> --- a/drivers/mtd/spi-nor/fsl-quadspi.c
>>>> +++ b/drivers/mtd/spi-nor/fsl-quadspi.c
>>>> @@ -957,6 +957,12 @@ static void fsl_qspi_unprep(struct spi_nor *nor, enum spi_nor_ops ops)
>>>>  
>>>>  static int fsl_qspi_probe(struct platform_device *pdev)
>>>>  {
>>>> +	struct spi_nor_hwcaps hwcaps = {
>>>> +		.mask = (SNOR_HWCAPS_READ |
>>>> +			 SNOR_HWCAPS_READ_FAST |
>>>> +			 SNOR_HWCAPS_READ_1_1_4 |
>>>> +			 SNOR_HWCAPS_PP),
>>>> +	};
>>>>  	struct device_node *np = pdev->dev.of_node;
>>>>  	struct device *dev = &pdev->dev;
>>>>  	struct fsl_qspi *q;
>>>> @@ -1065,7 +1071,7 @@ static int fsl_qspi_probe(struct platform_device *pdev)
>>>>  		/* set the chip address for READID */
>>>>  		fsl_qspi_set_base_addr(q, nor);
>>>>  
>>>> -		ret = spi_nor_scan(nor, NULL, SPI_NOR_QUAD);
>>>> +		ret = spi_nor_scan(nor, NULL, &hwcaps);
>>>>  		if (ret)
>>>>  			goto mutex_failed;
>>>>  
>>>> diff --git a/drivers/mtd/spi-nor/hisi-sfc.c b/drivers/mtd/spi-nor/hisi-sfc.c
>>>> index a286350627a6..80e2d173abdd 100644
>>>> --- a/drivers/mtd/spi-nor/hisi-sfc.c
>>>> +++ b/drivers/mtd/spi-nor/hisi-sfc.c
>>>> @@ -120,19 +120,24 @@ static inline int wait_op_finish(struct hifmc_host *host)
>>>>  		(reg & FMC_INT_OP_DONE), 0, FMC_WAIT_TIMEOUT);
>>>>  }
>>>>  
>>>> -static int get_if_type(enum read_mode flash_read)
>>>> +static int get_if_type(enum spi_nor_protocol proto)
>>>>  {
>>>>  	enum hifmc_iftype if_type;
>>>>  
>>>> -	switch (flash_read) {
>>>> -	case SPI_NOR_DUAL:
>>>> +	switch (proto) {
>>>> +	case SNOR_PROTO_1_1_2:
>>>>  		if_type = IF_TYPE_DUAL;
>>>>  		break;
>>>> -	case SPI_NOR_QUAD:
>>>> +	case SNOR_PROTO_1_2_2:
>>>> +		if_type = IF_TYPE_DIO;
>>>> +		break;
>>>> +	case SNOR_PROTO_1_1_4:
>>>>  		if_type = IF_TYPE_QUAD;
>>>>  		break;
>>>> -	case SPI_NOR_NORMAL:
>>>> -	case SPI_NOR_FAST:
>>>> +	case SNOR_PROTO_1_4_4:
>>>> +		if_type = IF_TYPE_QIO;
>>>> +		break;
>>>> +	case SNOR_PROTO_1_1_1:
>>>>  	default:
>>>>  		if_type = IF_TYPE_STD;
>>>>  		break;
>>>> @@ -253,7 +258,10 @@ static int hisi_spi_nor_dma_transfer(struct spi_nor *nor, loff_t start_off,
>>>>  	writel(FMC_DMA_LEN_SET(len), host->regbase + FMC_DMA_LEN);
>>>>  
>>>>  	reg = OP_CFG_FM_CS(priv->chipselect);
>>>> -	if_type = get_if_type(nor->flash_read);
>>>> +	if (op_type == FMC_OP_READ)
>>>> +		if_type = get_if_type(nor->read_proto);
>>>> +	else
>>>> +		if_type = get_if_type(nor->write_proto);
>>>>  	reg |= OP_CFG_MEM_IF_TYPE(if_type);
>>>>  	if (op_type == FMC_OP_READ)
>>>>  		reg |= OP_CFG_DUMMY_NUM(nor->read_dummy >> 3);
>>>> @@ -321,6 +329,13 @@ static ssize_t hisi_spi_nor_write(struct spi_nor *nor, loff_t to,
>>>>  static int hisi_spi_nor_register(struct device_node *np,
>>>>  				struct hifmc_host *host)
>>>>  {
>>>> +	struct spi_nor_hwcaps hwcaps = {
>>>> +		.mask = (SNOR_HWCAPS_READ |
>>>> +			 SNOR_HWCAPS_READ_FAST |
>>>> +			 SNOR_HWCAPS_READ_1_1_2 |
>>>> +			 SNOR_HWCAPS_READ_1_1_4 |
>>>> +			 SNOR_HWCAPS_PP),
>>>> +	};
>>>>  	struct device *dev = host->dev;
>>>>  	struct spi_nor *nor;
>>>>  	struct hifmc_priv *priv;
>>>> @@ -362,7 +377,7 @@ static int hisi_spi_nor_register(struct device_node *np,
>>>>  	nor->read = hisi_spi_nor_read;
>>>>  	nor->write = hisi_spi_nor_write;
>>>>  	nor->erase = NULL;
>>>> -	ret = spi_nor_scan(nor, NULL, SPI_NOR_QUAD);
>>>> +	ret = spi_nor_scan(nor, NULL, &hwcaps);
>>>>  	if (ret)
>>>>  		return ret;
>>>>  
>>>> diff --git a/drivers/mtd/spi-nor/intel-spi.c b/drivers/mtd/spi-nor/intel-spi.c
>>>> index 986a3d020a3a..515aa1f7f4f1 100644
>>>> --- a/drivers/mtd/spi-nor/intel-spi.c
>>>> +++ b/drivers/mtd/spi-nor/intel-spi.c
>>>> @@ -715,6 +715,11 @@ static void intel_spi_fill_partition(struct intel_spi *ispi,
>>>>  struct intel_spi *intel_spi_probe(struct device *dev,
>>>>  	struct resource *mem, const struct intel_spi_boardinfo *info)
>>>>  {
>>>> +	struct spi_nor_hwcaps hwcaps = {
>>>> +		.mask = (SNOR_HWCAPS_READ |
>>>> +			 SNOR_HWCAPS_READ_FAST |
>>>> +			 SNOR_HWCAPS_PP),
>>>> +	};
>>>>  	struct mtd_partition part;
>>>>  	struct intel_spi *ispi;
>>>>  	int ret;
>>>> @@ -746,7 +751,7 @@ struct intel_spi *intel_spi_probe(struct device *dev,
>>>>  	ispi->nor.write = intel_spi_write;
>>>>  	ispi->nor.erase = intel_spi_erase;
>>>>  
>>>> -	ret = spi_nor_scan(&ispi->nor, NULL, SPI_NOR_NORMAL);
>>>> +	ret = spi_nor_scan(&ispi->nor, NULL, &hwcaps);
>>>>  	if (ret) {
>>>>  		dev_info(dev, "failed to locate the chip\n");
>>>>  		return ERR_PTR(ret);
>>>> diff --git a/drivers/mtd/spi-nor/mtk-quadspi.c b/drivers/mtd/spi-nor/mtk-quadspi.c
>>>> index e661877c23de..615e258866f7 100644
>>>> --- a/drivers/mtd/spi-nor/mtk-quadspi.c
>>>> +++ b/drivers/mtd/spi-nor/mtk-quadspi.c
>>>> @@ -121,20 +121,20 @@ static void mt8173_nor_set_read_mode(struct mt8173_nor *mt8173_nor)
>>>>  {
>>>>  	struct spi_nor *nor = &mt8173_nor->nor;
>>>>  
>>>> -	switch (nor->flash_read) {
>>>> -	case SPI_NOR_FAST:
>>>> +	switch (nor->read_proto) {
>>>> +	case SNOR_PROTO_1_1_1:
>>>>  		writeb(nor->read_opcode, mt8173_nor->base +
>>>>  		       MTK_NOR_PRGDATA3_REG);
>>>>  		writeb(MTK_NOR_FAST_READ, mt8173_nor->base +
>>>>  		       MTK_NOR_CFG1_REG);
>>>>  		break;
>>>> -	case SPI_NOR_DUAL:
>>>> +	case SNOR_PROTO_1_1_2:
>>>>  		writeb(nor->read_opcode, mt8173_nor->base +
>>>>  		       MTK_NOR_PRGDATA3_REG);
>>>>  		writeb(MTK_NOR_DUAL_READ_EN, mt8173_nor->base +
>>>>  		       MTK_NOR_DUAL_REG);
>>>>  		break;
>>>> -	case SPI_NOR_QUAD:
>>>> +	case SNOR_PROTO_1_1_4:
>>>>  		writeb(nor->read_opcode, mt8173_nor->base +
>>>>  		       MTK_NOR_PRGDATA4_REG);
>>>>  		writeb(MTK_NOR_QUAD_READ_EN, mt8173_nor->base +
>>>> @@ -381,6 +381,12 @@ static int mt8173_nor_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf,
>>>>  static int mtk_nor_init(struct mt8173_nor *mt8173_nor,
>>>>  			struct device_node *flash_node)
>>>>  {
>>>> +	struct spi_nor_hwcaps hwcaps = {
>>>> +		.mask = (SNOR_HWCAPS_READ |
>>>> +			 SNOR_HWCAPS_READ_FAST |
>>>> +			 SNOR_HWCAPS_READ_1_1_2 |
>>>> +			 SNOR_HWCAPS_PP),
>>>> +	};
>>>>  	int ret;
>>>>  	struct spi_nor *nor;
>>>>  
>>>> @@ -399,7 +405,7 @@ static int mtk_nor_init(struct mt8173_nor *mt8173_nor,
>>>>  	nor->write_reg = mt8173_nor_write_reg;
>>>>  	nor->mtd.name = "mtk_nor";
>>>>  	/* initialized with NULL */
>>>> -	ret = spi_nor_scan(nor, NULL, SPI_NOR_DUAL);
>>>> +	ret = spi_nor_scan(nor, NULL, &hwcaps);
>>>>  	if (ret)
>>>>  		return ret;
>>>>  
>>>> diff --git a/drivers/mtd/spi-nor/nxp-spifi.c b/drivers/mtd/spi-nor/nxp-spifi.c
>>>> index 73a14f40928b..c5992e099542 100644
>>>> --- a/drivers/mtd/spi-nor/nxp-spifi.c
>>>> +++ b/drivers/mtd/spi-nor/nxp-spifi.c
>>>> @@ -240,13 +240,12 @@ static int nxp_spifi_erase(struct spi_nor *nor, loff_t offs)
>>>>  
>>>>  static int nxp_spifi_setup_memory_cmd(struct nxp_spifi *spifi)
>>>>  {
>>>> -	switch (spifi->nor.flash_read) {
>>>> -	case SPI_NOR_NORMAL:
>>>> -	case SPI_NOR_FAST:
>>>> +	switch (spifi->nor.read_proto) {
>>>> +	case SNOR_PROTO_1_1_1:
>>>>  		spifi->mcmd = SPIFI_CMD_FIELDFORM_ALL_SERIAL;
>>>>  		break;
>>>> -	case SPI_NOR_DUAL:
>>>> -	case SPI_NOR_QUAD:
>>>> +	case SNOR_PROTO_1_1_2:
>>>> +	case SNOR_PROTO_1_1_4:
>>>>  		spifi->mcmd = SPIFI_CMD_FIELDFORM_QUAD_DUAL_DATA;
>>>>  		break;
>>>>  	default:
>>>> @@ -274,7 +273,11 @@ static void nxp_spifi_dummy_id_read(struct spi_nor *nor)
>>>>  static int nxp_spifi_setup_flash(struct nxp_spifi *spifi,
>>>>  				 struct device_node *np)
>>>>  {
>>>> -	enum read_mode flash_read;
>>>> +	struct spi_nor_hwcaps hwcaps = {
>>>> +		.mask = (SNOR_HWCAPS_READ |
>>>> +			 SNOR_HWCAPS_READ_FAST |
>>>> +			 SNOR_HWCAPS_PP),
>>>> +	};
>>>>  	u32 ctrl, property;
>>>>  	u16 mode = 0;
>>>>  	int ret;
>>>> @@ -308,13 +311,12 @@ static int nxp_spifi_setup_flash(struct nxp_spifi *spifi,
>>>>  
>>>>  	if (mode & SPI_RX_DUAL) {
>>>>  		ctrl |= SPIFI_CTRL_DUAL;
>>>> -		flash_read = SPI_NOR_DUAL;
>>>> +		hwcaps.mask |= SNOR_HWCAPS_READ_1_1_2;
>>>>  	} else if (mode & SPI_RX_QUAD) {
>>>>  		ctrl &= ~SPIFI_CTRL_DUAL;
>>>> -		flash_read = SPI_NOR_QUAD;
>>>> +		hwcaps.mask |= SNOR_HWCAPS_READ_1_1_4;
>>>>  	} else {
>>>>  		ctrl |= SPIFI_CTRL_DUAL;
>>>> -		flash_read = SPI_NOR_NORMAL;
>>>>  	}
>>>>  
>>>>  	switch (mode & (SPI_CPHA | SPI_CPOL)) {
>>>> @@ -351,7 +353,7 @@ static int nxp_spifi_setup_flash(struct nxp_spifi *spifi,
>>>>  	 */
>>>>  	nxp_spifi_dummy_id_read(&spifi->nor);
>>>>  
>>>> -	ret = spi_nor_scan(&spifi->nor, NULL, flash_read);
>>>> +	ret = spi_nor_scan(&spifi->nor, NULL, &hwcaps);
>>>>  	if (ret) {
>>>>  		dev_err(spifi->dev, "device scan failed\n");
>>>>  		return ret;
>>>> diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c
>>>> index d3cb44b28490..cc443c6cbae8 100644
>>>> --- a/drivers/mtd/spi-nor/spi-nor.c
>>>> +++ b/drivers/mtd/spi-nor/spi-nor.c
>>>> @@ -150,24 +150,6 @@ static int read_cr(struct spi_nor *nor)
>>>>  }
>>>>  
>>>>  /*
>>>> - * Dummy Cycle calculation for different type of read.
>>>> - * It can be used to support more commands with
>>>> - * different dummy cycle requirements.
>>>> - */
>>>> -static inline int spi_nor_read_dummy_cycles(struct spi_nor *nor)
>>>> -{
>>>> -	switch (nor->flash_read) {
>>>> -	case SPI_NOR_FAST:
>>>> -	case SPI_NOR_DUAL:
>>>> -	case SPI_NOR_QUAD:
>>>> -		return 8;
>>>> -	case SPI_NOR_NORMAL:
>>>> -		return 0;
>>>> -	}
>>>> -	return 0;
>>>> -}
>>>> -
>>>> -/*
>>>>   * Write status register 1 byte
>>>>   * Returns negative if error occurred.
>>>>   */
>>>> @@ -221,6 +203,10 @@ static inline u8 spi_nor_convert_3to4_read(u8 opcode)
>>>>  		{ SPINOR_OP_READ_1_2_2,	SPINOR_OP_READ_1_2_2_4B },
>>>>  		{ SPINOR_OP_READ_1_1_4,	SPINOR_OP_READ_1_1_4_4B },
>>>>  		{ SPINOR_OP_READ_1_4_4,	SPINOR_OP_READ_1_4_4_4B },
>>>> +
>>>> +		{ SPINOR_OP_READ_1_1_1_DTR,	SPINOR_OP_READ_1_1_1_DTR_4B },
>>>> +		{ SPINOR_OP_READ_1_2_2_DTR,	SPINOR_OP_READ_1_2_2_DTR_4B },
>>>> +		{ SPINOR_OP_READ_1_4_4_DTR,	SPINOR_OP_READ_1_4_4_DTR_4B },
>>>>  	};
>>>>  
>>>>  	return spi_nor_convert_opcode(opcode, spi_nor_3to4_read,
>>>> @@ -1459,30 +1445,6 @@ static int spansion_quad_enable(struct spi_nor *nor)
>>>>  	return 0;
>>>>  }
>>>>  
>>>> -static int set_quad_mode(struct spi_nor *nor, const struct flash_info *info)
>>>> -{
>>>> -	int status;
>>>> -
>>>> -	switch (JEDEC_MFR(info)) {
>>>> -	case SNOR_MFR_MACRONIX:
>>>> -		status = macronix_quad_enable(nor);
>>>> -		if (status) {
>>>> -			dev_err(nor->dev, "Macronix quad-read not enabled\n");
>>>> -			return -EINVAL;
>>>> -		}
>>>> -		return status;
>>>> -	case SNOR_MFR_MICRON:
>>>> -		return 0;
>>>> -	default:
>>>> -		status = spansion_quad_enable(nor);
>>>> -		if (status) {
>>>> -			dev_err(nor->dev, "Spansion quad-read not enabled\n");
>>>> -			return -EINVAL;
>>>> -		}
>>>> -		return status;
>>>> -	}
>>>> -}
>>>> -
>>>>  static int spi_nor_check(struct spi_nor *nor)
>>>>  {
>>>>  	if (!nor->dev || !nor->read || !nor->write ||
>>>> @@ -1535,8 +1497,322 @@ static int s3an_nor_scan(const struct flash_info *info, struct spi_nor *nor)
>>>>  	return 0;
>>>>  }
>>>>  
>>>> -int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
>>>> +
>>>> +struct spi_nor_read_command {
>>>> +	u8			num_mode_clocks;
>>>> +	u8			num_wait_states;
>>>> +	u8			opcode;
>>>> +	enum spi_nor_protocol	proto;
>>>> +};
>>>> +
>>>> +struct spi_nor_pp_command {
>>>> +	u8			opcode;
>>>> +	enum spi_nor_protocol	proto;
>>>> +};
>>>> +
>>>> +enum spi_nor_read_command_index {
>>>> +	SNOR_CMD_READ,
>>>> +	SNOR_CMD_READ_FAST,
>>>> +	SNOR_CMD_READ_1_1_1_DTR,
>>>> +
>>>> +	/* Dual SPI */
>>>> +	SNOR_CMD_READ_1_1_2,
>>>> +	SNOR_CMD_READ_1_2_2,
>>>> +	SNOR_CMD_READ_2_2_2,
>>>> +	SNOR_CMD_READ_1_2_2_DTR,
>>>> +
>>>> +	/* Quad SPI */
>>>> +	SNOR_CMD_READ_1_1_4,
>>>> +	SNOR_CMD_READ_1_4_4,
>>>> +	SNOR_CMD_READ_4_4_4,
>>>> +	SNOR_CMD_READ_1_4_4_DTR,
>>>> +
>>>> +	/* Octo SPI */
>>>> +	SNOR_CMD_READ_1_1_8,
>>>> +	SNOR_CMD_READ_1_8_8,
>>>> +	SNOR_CMD_READ_8_8_8,
>>>> +	SNOR_CMD_READ_1_8_8_DTR,
>>>> +
>>>> +	SNOR_CMD_READ_MAX
>>>> +};
>>>> +
>>>> +enum spi_nor_pp_command_index {
>>>> +	SNOR_CMD_PP,
>>>> +
>>>> +	/* Quad SPI */
>>>> +	SNOR_CMD_PP_1_1_4,
>>>> +	SNOR_CMD_PP_1_4_4,
>>>> +	SNOR_CMD_PP_4_4_4,
>>>> +
>>>> +	/* Octo SPI */
>>>> +	SNOR_CMD_PP_1_1_8,
>>>> +	SNOR_CMD_PP_1_8_8,
>>>> +	SNOR_CMD_PP_8_8_8,
>>>> +
>>>> +	SNOR_CMD_PP_MAX
>>>> +};
>>>> +
>>>> +struct spi_nor_flash_parameter {
>>>> +	u64				size;
>>>> +	u32				page_size;
>>>> +
>>>> +	struct spi_nor_hwcaps		hwcaps;
>>>> +	struct spi_nor_read_command	reads[SNOR_CMD_READ_MAX];
>>>> +	struct spi_nor_pp_command	page_programs[SNOR_CMD_PP_MAX];
>>>> +
>>>> +	int (*quad_enable)(struct spi_nor *nor);
>>>> +};
>>>> +
>>>> +
>>>> +static inline 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)
>>>> +{
>>>> +	read->num_mode_clocks = num_mode_clocks;
>>>> +	read->num_wait_states = num_wait_states;
>>>> +	read->opcode = opcode;
>>>> +	read->proto = proto;
>>>> +}
>>>> +
>>>> +static inline void
>>>> +spi_nor_set_pp_settings(struct spi_nor_pp_command *pp,
>>>> +			u8 opcode,
>>>> +			enum spi_nor_protocol proto)
>>>> +{
>>>> +	pp->opcode = opcode;
>>>> +	pp->proto = proto;
>>>> +}
>>>> +
>>>> +static int spi_nor_init_params(struct spi_nor *nor,
>>>> +			       const struct flash_info *info,
>>>> +			       struct spi_nor_flash_parameter *params)
>>>> +{
>>>> +	/* Set legacy flash parameters as default. */
>>>> +	memset(params, 0, sizeof(*params));
>>>> +
>>>> +	/* Set SPI NOR sizes. */
>>>> +	params->size = info->sector_size * info->n_sectors;
>>>> +	params->page_size = info->page_size;
>>>> +
>>>> +	/* (Fast) Read settings. */
>>>> +	params->hwcaps.mask |= SNOR_HWCAPS_READ;
>>>> +	spi_nor_set_read_settings(&params->reads[SNOR_CMD_READ],
>>>> +				  0, 0, SPINOR_OP_READ,
>>>> +				  SNOR_PROTO_1_1_1);
>>>> +	if (!(info->flags & SPI_NOR_NO_FR)) {
>>>> +		params->hwcaps.mask |= SNOR_HWCAPS_READ_FAST;
>>>> +		spi_nor_set_read_settings(&params->reads[SNOR_CMD_READ_FAST],
>>>> +					  0, 8, SPINOR_OP_READ_FAST,
>>>> +					  SNOR_PROTO_1_1_1);
>>>> +	}
>>>> +	if (info->flags & SPI_NOR_DUAL_READ) {
>>>> +		params->hwcaps.mask |= SNOR_HWCAPS_READ_1_1_2;
>>>> +		spi_nor_set_read_settings(&params->reads[SNOR_CMD_READ_1_1_2],
>>>> +					  0, 8, SPINOR_OP_READ_1_1_2,
>>>> +					  SNOR_PROTO_1_1_2);
>>>> +	}
>>>> +	if (info->flags & SPI_NOR_QUAD_READ) {
>>>> +		params->hwcaps.mask |= SNOR_HWCAPS_READ_1_1_4;
>>>> +		spi_nor_set_read_settings(&params->reads[SNOR_CMD_READ_1_1_4],
>>>> +					  0, 8, SPINOR_OP_READ_1_1_4,
>>>> +					  SNOR_PROTO_1_1_4);
>>>> +	}
>>>> +
>>>> +	/* 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);
>>>> +
>>>> +	/* Select the procedure to set the Quad Enable bit. */
>>>> +	if (params->hwcaps.mask & (SNOR_HWCAPS_READ_QUAD |
>>>> +				   SNOR_HWCAPS_PP_QUAD)) {
>>>> +		switch (JEDEC_MFR(info)) {
>>>> +		case SNOR_MFR_MACRONIX:
>>>> +			params->quad_enable = macronix_quad_enable;
>>>> +			break;
>>>> +
>>>> +		case SNOR_MFR_MICRON:
>>>> +			break;
>>>> +
>>>> +		default:
>>>> +			params->quad_enable = spansion_quad_enable;
>>>> +			break;
>>>> +		}
>>>> +	}
>>>> +
>>>> +	return 0;
>>>> +}
>>>> +
>>>> +static int spi_nor_hwcaps2cmd(u32 hwcaps)
>>>>  {
>>>> +	switch (hwcaps) {
>>>> +	case SNOR_HWCAPS_READ:			return SNOR_CMD_READ;
>>>> +	case SNOR_HWCAPS_READ_FAST:		return SNOR_CMD_READ_FAST;
>>>> +	case SNOR_HWCAPS_READ_1_1_1_DTR:	return SNOR_CMD_READ_1_1_1_DTR;
>>>> +	case SNOR_HWCAPS_READ_1_1_2:		return SNOR_CMD_READ_1_1_2;
>>>> +	case SNOR_HWCAPS_READ_1_2_2:		return SNOR_CMD_READ_1_2_2;
>>>> +	case SNOR_HWCAPS_READ_2_2_2:		return SNOR_CMD_READ_2_2_2;
>>>> +	case SNOR_HWCAPS_READ_1_2_2_DTR:	return SNOR_CMD_READ_1_2_2_DTR;
>>>> +	case SNOR_HWCAPS_READ_1_1_4:		return SNOR_CMD_READ_1_1_4;
>>>> +	case SNOR_HWCAPS_READ_1_4_4:		return SNOR_CMD_READ_1_4_4;
>>>> +	case SNOR_HWCAPS_READ_4_4_4:		return SNOR_CMD_READ_4_4_4;
>>>> +	case SNOR_HWCAPS_READ_1_4_4_DTR:	return SNOR_CMD_READ_1_4_4_DTR;
>>>> +	case SNOR_HWCAPS_READ_1_1_8:		return SNOR_CMD_READ_1_1_8;
>>>> +	case SNOR_HWCAPS_READ_1_8_8:		return SNOR_CMD_READ_1_8_8;
>>>> +	case SNOR_HWCAPS_READ_8_8_8:		return SNOR_CMD_READ_8_8_8;
>>>> +	case SNOR_HWCAPS_READ_1_8_8_DTR:	return SNOR_CMD_READ_1_8_8_DTR;
>>>> +
>>>> +	case SNOR_HWCAPS_PP:			return SNOR_CMD_PP;
>>>> +	case SNOR_HWCAPS_PP_1_1_4:		return SNOR_CMD_PP_1_1_4;
>>>> +	case SNOR_HWCAPS_PP_1_4_4:		return SNOR_CMD_PP_1_4_4;
>>>> +	case SNOR_HWCAPS_PP_4_4_4:		return SNOR_CMD_PP_4_4_4;
>>>> +	case SNOR_HWCAPS_PP_1_1_8:		return SNOR_CMD_PP_1_1_8;
>>>> +	case SNOR_HWCAPS_PP_1_8_8:		return SNOR_CMD_PP_1_8_8;
>>>> +	case SNOR_HWCAPS_PP_8_8_8:		return SNOR_CMD_PP_8_8_8;
>>>> +	}
>>>> +
>>>> +	return -EINVAL;
>>>> +}
>>>> +
>>>> +static int spi_nor_select_read(struct spi_nor *nor,
>>>> +			       const struct spi_nor_flash_parameter *params,
>>>> +			       u32 shared_hwcaps)
>>>> +{
>>>> +	int cmd, best_match = fls(shared_hwcaps & SNOR_HWCAPS_READ_MASK) - 1;
>>>> +	const struct spi_nor_read_command *read;
>>>> +
>>>> +	if (best_match < 0)
>>>> +		return -EINVAL;
>>>> +
>>>> +	cmd = spi_nor_hwcaps2cmd(BIT(best_match));
>>>> +	if (cmd < 0)
>>>> +		return -EINVAL;
>>>> +
>>>> +	read = &params->reads[cmd];
>>>> +	nor->read_opcode = read->opcode;
>>>> +	nor->read_proto = read->proto;
>>>> +
>>>> +	/*
>>>> +	 * In the spi-nor framework, we don't need to make the difference
>>>> +	 * between mode clock cycles and wait state clock cycles.
>>>> +	 * Indeed, the value of the mode clock cycles is used by a QSPI
>>>> +	 * flash memory to know whether it should enter or leave its 0-4-4
>>>> +	 * (Continuous Read / XIP) mode.
>>>> +	 * eXecution In Place is out of the scope of the mtd sub-system.
>>>> +	 * Hence we choose to merge both mode and wait state clock cycles
>>>> +	 * into the so called dummy clock cycles.
>>>> +	 */
>>>> +	nor->read_dummy = read->num_mode_clocks + read->num_wait_states;
>>>> +	return 0;
>>>> +}
>>>> +
>>>> +static int spi_nor_select_pp(struct spi_nor *nor,
>>>> +			     const struct spi_nor_flash_parameter *params,
>>>> +			     u32 shared_hwcaps)
>>>> +{
>>>> +	int cmd, best_match = fls(shared_hwcaps & SNOR_HWCAPS_PP_MASK) - 1;
>>>> +	const struct spi_nor_pp_command *pp;
>>>> +
>>>> +	if (best_match < 0)
>>>> +		return -EINVAL;
>>>> +
>>>> +	cmd = spi_nor_hwcaps2cmd(BIT(best_match));
>>>> +	if (cmd < 0)
>>>> +		return -EINVAL;
>>>> +
>>>> +	pp = &params->page_programs[cmd];
>>>> +	nor->program_opcode = pp->opcode;
>>>> +	nor->write_proto = pp->proto;
>>>> +	return 0;
>>>> +}
>>>> +
>>>> +static int spi_nor_select_erase(struct spi_nor *nor,
>>>> +				const struct flash_info *info)
>>>> +{
>>>> +	struct mtd_info *mtd = &nor->mtd;
>>>> +
>>>> +#ifdef CONFIG_MTD_SPI_NOR_USE_4K_SECTORS
>>>> +	/* prefer "small sector" erase if possible */
>>>> +	if (info->flags & SECT_4K) {
>>>> +		nor->erase_opcode = SPINOR_OP_BE_4K;
>>>> +		mtd->erasesize = 4096;
>>>> +	} else if (info->flags & SECT_4K_PMC) {
>>>> +		nor->erase_opcode = SPINOR_OP_BE_4K_PMC;
>>>> +		mtd->erasesize = 4096;
>>>> +	} else
>>>> +#endif
>>>> +	{
>>>> +		nor->erase_opcode = SPINOR_OP_SE;
>>>> +		mtd->erasesize = info->sector_size;
>>>> +	}
>>>> +	return 0;
>>>> +}
>>>> +
>>>> +static int spi_nor_setup(struct spi_nor *nor, const struct flash_info *info,
>>>> +			 const struct spi_nor_flash_parameter *params,
>>>> +			 const struct spi_nor_hwcaps *hwcaps)
>>>> +{
>>>> +	u32 ignored_mask, shared_mask;
>>>> +	bool enable_quad_io;
>>>> +	int err;
>>>> +
>>>> +	/*
>>>> +	 * Keep only the hardware capabilities supported by both the SPI
>>>> +	 * controller and the SPI flash memory.
>>>> +	 */
>>>> +	shared_mask = hwcaps->mask & params->hwcaps.mask;
>>>> +
>>>> +	/* SPI protocol classes N-N-N are not supported yet. */
>>>> +	ignored_mask = (SNOR_HWCAPS_READ_2_2_2 |
>>>> +			SNOR_HWCAPS_READ_4_4_4 |
>>>> +			SNOR_HWCAPS_READ_8_8_8 |
>>>> +			SNOR_HWCAPS_PP_4_4_4 |
>>>> +			SNOR_HWCAPS_PP_8_8_8);
>>>> +	if (shared_mask & ignored_mask) {
>>>> +		dev_dbg(nor->dev,
>>>> +			"SPI protocol classes N-N-N are not supported yet.\n");
>>>> +		shared_mask &= ~ignored_mask;
>>>> +	}
>>>> +
>>>> +	/* Select the (Fast) Read command. */
>>>> +	err = spi_nor_select_read(nor, params, shared_mask);
>>>> +	if (err) {
>>>> +		dev_err(nor->dev, "invalid (fast) read\n");
>>>> +		return err;
>>>> +	}
>>>> +
>>>> +	/* Select the Page Program command. */
>>>> +	err = spi_nor_select_pp(nor, params, shared_mask);
>>>> +	if (err) {
>>>> +		dev_err(nor->dev, "invalid page program\n");
>>>> +		return err;
>>>> +	}
>>>> +
>>>> +	/* Select the Sector Erase command. */
>>>> +	err = spi_nor_select_erase(nor, info);
>>>> +	if (err) {
>>>> +		dev_err(nor->dev, "invalid sector/block erase\n");
>>>> +		return err;
>>>> +	}
>>>> +
>>>> +	/* Enable Quad I/O if needed. */
>>>> +	enable_quad_io = (spi_nor_get_protocol_width(nor->read_proto) == 4 ||
>>>> +			  spi_nor_get_protocol_width(nor->write_proto) == 4);
>>>> +	if (enable_quad_io && params->quad_enable)
>>>> +		nor->flash_quad_enable = params->quad_enable;
>>>> +	else
>>>> +		nor->flash_quad_enable = NULL;
>>>> +
>>>> +	return 0;
>>>> +}
>>>> +
>>>> +int spi_nor_scan(struct spi_nor *nor, const char *name,
>>>> +		 const struct spi_nor_hwcaps *hwcaps)
>>>> +{
>>>> +	struct spi_nor_flash_parameter params;
>>>>  	const struct flash_info *info = NULL;
>>>>  	struct device *dev = nor->dev;
>>>>  	struct mtd_info *mtd = &nor->mtd;
>>>> @@ -1548,6 +1824,11 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
>>>>  	if (ret)
>>>>  		return ret;
>>>>  
>>>> +	/* Reset SPI protocol for all commands */
>>>> +	nor->reg_proto = SNOR_PROTO_1_1_1;
>>>> +	nor->read_proto = SNOR_PROTO_1_1_1;
>>>> +	nor->write_proto = SNOR_PROTO_1_1_1;
>>>> +
>>>>  	if (name)
>>>>  		info = spi_nor_match_id(name);
>>>>  	/* Try to auto-detect if chip name wasn't specified or not found */
>>>> @@ -1580,6 +1861,11 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
>>>>  		}
>>>>  	}
>>>>  
>>>> +	/* Parse the Serial Flash Discoverable Parameters table */
>>>> +	ret = spi_nor_init_params(nor, info, &params);
>>>> +	if (ret)
>>>> +		return ret;
>>>> +
>>>>  	mutex_init(&nor->lock);
>>>>  
>>>>  	/*
>>>> @@ -1610,7 +1896,7 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
>>>>  	mtd->type = MTD_NORFLASH;
>>>>  	mtd->writesize = 1;
>>>>  	mtd->flags = MTD_CAP_NORFLASH;
>>>> -	mtd->size = info->sector_size * info->n_sectors;
>>>> +	mtd->size = params.size;
>>>>  	mtd->_erase = spi_nor_erase;
>>>>  	mtd->_read = spi_nor_read;
>>>>  
>>>> @@ -1641,76 +1927,47 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
>>>>  	if (info->flags & NO_CHIP_ERASE)
>>>>  		nor->flags |= SNOR_F_NO_OP_CHIP_ERASE;
>>>>  
>>>> -#ifdef CONFIG_MTD_SPI_NOR_USE_4K_SECTORS
>>>> -	/* prefer "small sector" erase if possible */
>>>> -	if (info->flags & SECT_4K) {
>>>> -		nor->erase_opcode = SPINOR_OP_BE_4K;
>>>> -		mtd->erasesize = 4096;
>>>> -	} else if (info->flags & SECT_4K_PMC) {
>>>> -		nor->erase_opcode = SPINOR_OP_BE_4K_PMC;
>>>> -		mtd->erasesize = 4096;
>>>> -	} else
>>>> -#endif
>>>> -	{
>>>> -		nor->erase_opcode = SPINOR_OP_SE;
>>>> -		mtd->erasesize = info->sector_size;
>>>> -	}
>>>> -
>>>>  	if (info->flags & SPI_NOR_NO_ERASE)
>>>>  		mtd->flags |= MTD_NO_ERASE;
>>>>  
>>>>  	mtd->dev.parent = dev;
>>>> -	nor->page_size = info->page_size;
>>>> +	nor->page_size = params.page_size;
>>>>  	mtd->writebufsize = nor->page_size;
>>>>  
>>>>  	if (np) {
>>>>  		/* If we were instantiated by DT, use it */
>>>>  		if (of_property_read_bool(np, "m25p,fast-read"))
>>>> -			nor->flash_read = SPI_NOR_FAST;
>>>> +			params.hwcaps.mask |= SNOR_HWCAPS_READ_FAST;
>>>>  		else
>>>> -			nor->flash_read = SPI_NOR_NORMAL;
>>>> +			params.hwcaps.mask &= ~SNOR_HWCAPS_READ_FAST;
>>>>  	} else {
>>>>  		/* If we weren't instantiated by DT, default to fast-read */
>>>> -		nor->flash_read = SPI_NOR_FAST;
>>>> +		params.hwcaps.mask |= SNOR_HWCAPS_READ_FAST;
>>>>  	}
>>>>  
>>>>  	/* Some devices cannot do fast-read, no matter what DT tells us */
>>>>  	if (info->flags & SPI_NOR_NO_FR)
>>>> -		nor->flash_read = SPI_NOR_NORMAL;
>>>> +		params.hwcaps.mask &= ~SNOR_HWCAPS_READ_FAST;
>>>> +
>>>> +	/*
>>>> +	 * Configure the SPI memory:
>>>> +	 * - select op codes for (Fast) Read, Page Program and Sector Erase.
>>>> +	 * - set the number of dummy cycles (mode cycles + wait states).
>>>> +	 * - set the SPI protocols for register and memory accesses.
>>>> +	 * - set the Quad Enable bit if needed (required by SPI x-y-4 protos).
>>>> +	 */
>>>> +	ret = spi_nor_setup(nor, info, &params, hwcaps);
>>>> +	if (ret)
>>>> +		return ret;
>>>>  
>>>> -	/* Quad/Dual-read mode takes precedence over fast/normal */
>>>> -	if (mode == SPI_NOR_QUAD && info->flags & SPI_NOR_QUAD_READ) {
>>>> -		ret = set_quad_mode(nor, info);
>>>> +	if (nor->flash_quad_enable) {
>>>> +		ret = nor->flash_quad_enable(nor);
>>>>  		if (ret) {
>>>>  			dev_err(dev, "quad mode not supported\n");
>>>>  			return ret;
>>>>  		}
>>>> -		nor->flash_read = SPI_NOR_QUAD;
>>>> -	} else if (mode == SPI_NOR_DUAL && info->flags & SPI_NOR_DUAL_READ) {
>>>> -		nor->flash_read = SPI_NOR_DUAL;
>>>>  	}
>>>>  
>>>> -	/* Default commands */
>>>> -	switch (nor->flash_read) {
>>>> -	case SPI_NOR_QUAD:
>>>> -		nor->read_opcode = SPINOR_OP_READ_1_1_4;
>>>> -		break;
>>>> -	case SPI_NOR_DUAL:
>>>> -		nor->read_opcode = SPINOR_OP_READ_1_1_2;
>>>> -		break;
>>>> -	case SPI_NOR_FAST:
>>>> -		nor->read_opcode = SPINOR_OP_READ_FAST;
>>>> -		break;
>>>> -	case SPI_NOR_NORMAL:
>>>> -		nor->read_opcode = SPINOR_OP_READ;
>>>> -		break;
>>>> -	default:
>>>> -		dev_err(dev, "No Read opcode defined\n");
>>>> -		return -EINVAL;
>>>> -	}
>>>> -
>>>> -	nor->program_opcode = SPINOR_OP_PP;
>>>> -
>>>>  	if (info->addr_width)
>>>>  		nor->addr_width = info->addr_width;
>>>>  	else if (mtd->size > 0x1000000) {
>>>> @@ -1731,8 +1988,6 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
>>>>  		return -EINVAL;
>>>>  	}
>>>>  
>>>> -	nor->read_dummy = spi_nor_read_dummy_cycles(nor);
>>>> -
>>>>  	if (info->flags & SPI_S3AN) {
>>>>  		ret = s3an_nor_scan(info, nor);
>>>>  		if (ret)
>>>> diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h
>>>> index f2a718030476..732ee6cd5330 100644
>>>> --- a/include/linux/mtd/spi-nor.h
>>>> +++ b/include/linux/mtd/spi-nor.h
>>>> @@ -73,6 +73,15 @@
>>>>  #define SPINOR_OP_BE_32K_4B	0x5c	/* Erase 32KiB block */
>>>>  #define SPINOR_OP_SE_4B		0xdc	/* Sector erase (usually 64KiB) */
>>>>  
>>>> +/* Double Transfer Rate opcodes - defined in JEDEC JESD216B. */
>>>> +#define SPINOR_OP_READ_1_1_1_DTR	0x0d
>>>> +#define SPINOR_OP_READ_1_2_2_DTR	0xbd
>>>> +#define SPINOR_OP_READ_1_4_4_DTR	0xed
>>>> +
>>>> +#define SPINOR_OP_READ_1_1_1_DTR_4B	0x0e
>>>> +#define SPINOR_OP_READ_1_2_2_DTR_4B	0xbe
>>>> +#define SPINOR_OP_READ_1_4_4_DTR_4B	0xee
>>>> +
>>>>  /* Used for SST flashes only. */
>>>>  #define SPINOR_OP_BP		0x02	/* Byte program */
>>>>  #define SPINOR_OP_WRDI		0x04	/* Write disable */
>>>> @@ -119,13 +128,75 @@
>>>>  /* Configuration Register bits. */
>>>>  #define CR_QUAD_EN_SPAN		BIT(1)	/* Spansion Quad I/O */
>>>>  
>>>> -enum read_mode {
>>>> -	SPI_NOR_NORMAL = 0,
>>>> -	SPI_NOR_FAST,
>>>> -	SPI_NOR_DUAL,
>>>> -	SPI_NOR_QUAD,
>>>> +
>>>> +/* Supported SPI protocols */
>>>> +#define SNOR_PROTO_WIDTH_MASK	GENMASK(7, 0)
>>>> +
>>>> +#define SNOR_PROTO_CLASS_MASK	GENMASK(9, 8)
>>>> +#define SNOR_PROTO_CLASS_1_1_N	(0x0u << 8)
>>>> +#define SNOR_PROTO_CLASS_1_N_N	(0x1u << 8)
>>>> +#define SNOR_PROTO_CLASS_N_N_N	(0x2u << 8)
>>>> +
>>>> +#define SNOR_PROTO_IS_DTR	BIT(10)	/* Double Transfer Rate */
>>>> +
>>>> +#define SNOR_PROTO_STR(_pclass, _pwidth) \
>>>> +	((_pclass) | (_pwidth))
>>>> +#define SNOR_PROTO_DTR(_pclass, _pwidth) \
>>>> +	(SNOR_PROTO_IS_DTR | (_pclass) | (_pwidth))
>>>> +
>>>> +enum spi_nor_protocol {
>>>> +	SNOR_PROTO_1_1_1 = SNOR_PROTO_STR(SNOR_PROTO_CLASS_1_1_N, 1),
>>>> +	SNOR_PROTO_1_1_2 = SNOR_PROTO_STR(SNOR_PROTO_CLASS_1_1_N, 2),
>>>> +	SNOR_PROTO_1_1_4 = SNOR_PROTO_STR(SNOR_PROTO_CLASS_1_1_N, 4),
>>>> +	SNOR_PROTO_1_1_8 = SNOR_PROTO_STR(SNOR_PROTO_CLASS_1_1_N, 8),
>>>> +	SNOR_PROTO_1_2_2 = SNOR_PROTO_STR(SNOR_PROTO_CLASS_1_N_N, 2),
>>>> +	SNOR_PROTO_1_4_4 = SNOR_PROTO_STR(SNOR_PROTO_CLASS_1_N_N, 4),
>>>> +	SNOR_PROTO_1_8_8 = SNOR_PROTO_STR(SNOR_PROTO_CLASS_1_N_N, 8),
>>>> +	SNOR_PROTO_2_2_2 = SNOR_PROTO_STR(SNOR_PROTO_CLASS_N_N_N, 2),
>>>> +	SNOR_PROTO_4_4_4 = SNOR_PROTO_STR(SNOR_PROTO_CLASS_N_N_N, 4),
>>>> +	SNOR_PROTO_8_8_8 = SNOR_PROTO_STR(SNOR_PROTO_CLASS_N_N_N, 8),
>>>> +
>>>> +	SNOR_PROTO_1_1_1_DTR = SNOR_PROTO_DTR(SNOR_PROTO_CLASS_1_1_N, 1),
>>>> +	SNOR_PROTO_1_2_2_DTR = SNOR_PROTO_DTR(SNOR_PROTO_CLASS_1_N_N, 2),
>>>> +	SNOR_PROTO_1_4_4_DTR = SNOR_PROTO_DTR(SNOR_PROTO_CLASS_1_N_N, 4),
>>>> +	SNOR_PROTO_1_8_8_DTR = SNOR_PROTO_DTR(SNOR_PROTO_CLASS_1_N_N, 8),
>>>>  };
>>>>  
>>>> +static inline bool spi_nor_protocol_is_dtr(enum spi_nor_protocol proto)
>>>> +{
>>>> +	return (proto & SNOR_PROTO_IS_DTR) == SNOR_PROTO_IS_DTR;
>>>> +}
>>>> +
>>>> +static inline u32 spi_nor_get_protocol_class(enum spi_nor_protocol proto)
>>>> +{
>>>> +	return proto & SNOR_PROTO_CLASS_MASK;
>>>> +}
>>>> +
>>>> +static inline u8 spi_nor_get_protocol_width(enum spi_nor_protocol proto)
>>>> +{
>>>> +	return proto & SNOR_PROTO_WIDTH_MASK;
>>>> +}
>>>> +
>>>> +static inline u8 spi_nor_get_protocol_inst_width(enum spi_nor_protocol proto)
>>>> +{
>>>> +	return (spi_nor_get_protocol_class(proto) == SNOR_PROTO_CLASS_N_N_N) ?
>>>> +		spi_nor_get_protocol_width(proto) :
>>>> +		1u;
>>>> +}
>>>> +
>>>> +static inline u8 spi_nor_get_protocol_addr_width(enum spi_nor_protocol proto)
>>>> +{
>>>> +	return (spi_nor_get_protocol_class(proto) != SNOR_PROTO_CLASS_1_1_N) ?
>>>> +		spi_nor_get_protocol_width(proto) :
>>>> +		1u;
>>>> +}
>>>> +
>>>> +static inline u8 spi_nor_get_protocol_data_width(enum spi_nor_protocol proto)
>>>> +{
>>>> +	return spi_nor_get_protocol_width(proto);
>>>> +}
>>>> +
>>>> +
>>>>  #define SPI_NOR_MAX_CMD_SIZE	8
>>>>  enum spi_nor_ops {
>>>>  	SPI_NOR_OPS_READ = 0,
>>>> @@ -154,9 +225,11 @@ enum spi_nor_option_flags {
>>>>   * @read_opcode:	the read opcode
>>>>   * @read_dummy:		the dummy needed by the read operation
>>>>   * @program_opcode:	the program opcode
>>>> - * @flash_read:		the mode of the read
>>>>   * @sst_write_second:	used by the SST write operation
>>>>   * @flags:		flag options for the current SPI-NOR (SNOR_F_*)
>>>> + * @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
>>>>   * @cmd_buf:		used by the write_reg
>>>>   * @prepare:		[OPTIONAL] do some preparations for the
>>>>   *			read/write/erase/lock/unlock operations
>>>> @@ -173,6 +246,7 @@ enum spi_nor_option_flags {
>>>>   * @flash_unlock:	[FLASH-SPECIFIC] unlock a region of the SPI NOR
>>>>   * @flash_is_locked:	[FLASH-SPECIFIC] check if a region of the SPI NOR is
>>>>   *			completely locked
>>>> + * @flash_quad_enable:	[FLASH-SPECIFIC] set the Quad Enable bit of the SPI NOR
>>>>   * @priv:		the private data
>>>>   */
>>>>  struct spi_nor {
>>>> @@ -185,7 +259,9 @@ struct spi_nor {
>>>>  	u8			read_opcode;
>>>>  	u8			read_dummy;
>>>>  	u8			program_opcode;
>>>> -	enum read_mode		flash_read;
>>>> +	enum spi_nor_protocol	read_proto;
>>>> +	enum spi_nor_protocol	write_proto;
>>>> +	enum spi_nor_protocol	reg_proto;
>>>>  	bool			sst_write_second;
>>>>  	u32			flags;
>>>>  	u8			cmd_buf[SPI_NOR_MAX_CMD_SIZE];
>>>> @@ -204,6 +280,7 @@ struct spi_nor {
>>>>  	int (*flash_lock)(struct spi_nor *nor, loff_t ofs, uint64_t len);
>>>>  	int (*flash_unlock)(struct spi_nor *nor, loff_t ofs, uint64_t len);
>>>>  	int (*flash_is_locked)(struct spi_nor *nor, loff_t ofs, uint64_t len);
>>>> +	int (*flash_quad_enable)(struct spi_nor *nor);
>>>>  
>>>>  	void *priv;
>>>>  };
>>>> @@ -219,11 +296,73 @@ static inline struct device_node *spi_nor_get_flash_node(struct spi_nor *nor)
>>>>  	return mtd_get_of_node(&nor->mtd);
>>>>  }
>>>>  
>>>> +
>>>> +/**
>>>> + * struct spi_nor_hwcaps - Structure for describing the hardware capabilies
>>>> + * supported by the SPI controller (bus master).
>>>> + * @mask:		the bitmask listing all the supported hw capabilies
>>>> + */
>>>> +struct spi_nor_hwcaps {
>>>> +	u32	mask;
>>>> +};
>>>> +
>>>> +/*
>>>> + *(Fast) Read capabilities.
>>>> + * MUST be ordered by priority: the higher bit position, the higher priority.
>>>> + * As a matter of performances, it is relevant to use Octo SPI protocols first,
>>>> + * then Quad SPI protocols before Dual SPI protocols, Fast Read and lastly
>>>> + * (Slow) Read.
>>>> + */
>>>> +#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)
>>>> +
>>>> +#define SNOR_HWCAPS_READ_DUAL		GENMASK(7, 4)
>>>> +#define SNOR_HWCAPS_READ_1_1_2		BIT(4)
>>>> +#define SNOR_HWCAPS_READ_1_2_2		BIT(5)
>>>> +#define SNOR_HWCAPS_READ_2_2_2		BIT(6)
>>>> +#define SNOR_HWCAPS_READ_1_2_2_DTR	BIT(7)
>>>> +
>>>> +#define SNOR_HWCAPS_READ_QUAD		GENMASK(11, 8)
>>>> +#define SNOR_HWCAPS_READ_1_1_4		BIT(8)
>>>> +#define SNOR_HWCAPS_READ_1_4_4		BIT(9)
>>>> +#define SNOR_HWCAPS_READ_4_4_4		BIT(10)
>>>> +#define SNOR_HWCAPS_READ_1_4_4_DTR	BIT(11)
>>>> +
>>>> +#define SNOR_HWCPAS_READ_OCTO		GENMASK(15, 12)
>>>> +#define SNOR_HWCAPS_READ_1_1_8		BIT(12)
>>>> +#define SNOR_HWCAPS_READ_1_8_8		BIT(13)
>>>> +#define SNOR_HWCAPS_READ_8_8_8		BIT(14)
>>>> +#define SNOR_HWCAPS_READ_1_8_8_DTR	BIT(15)
>>>> +
>>>> +/*
>>>> + * Page Program capabilities.
>>>> + * MUST be ordered by priority: the higher bit position, the higher priority.
>>>> + * Like (Fast) Read capabilities, Octo/Quad SPI protocols are preferred to the
>>>> + * legacy SPI 1-1-1 protocol.
>>>> + * Note that Dual Page Programs are not supported because there is no existing
>>>> + * 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_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_OCTO	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)
>>>> +
>>>>  /**
>>>>   * spi_nor_scan() - scan the SPI NOR
>>>>   * @nor:	the spi_nor structure
>>>>   * @name:	the chip type name
>>>> - * @mode:	the read mode supported by the driver
>>>> + * @hwcaps:	the hardware capabilities supported by the controller driver
>>>>   *
>>>>   * The drivers can use this fuction to scan the SPI NOR.
>>>>   * In the scanning, it will try to get all the necessary information to
>>>> @@ -233,6 +372,7 @@ static inline struct device_node *spi_nor_get_flash_node(struct spi_nor *nor)
>>>>   *
>>>>   * Return: 0 for success, others for failure.
>>>>   */
>>>> -int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode);
>>>> +int spi_nor_scan(struct spi_nor *nor, const char *name,
>>>> +		 const struct spi_nor_hwcaps *hwcaps);
>>>>  
>>>>  #endif
>>>>
>>>
>>>
>>> ______________________________________________________
>>> Linux MTD discussion mailing list
>>> http://lists.infradead.org/mailman/listinfo/linux-mtd/
>>>
>>
> 
> 

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

* Re: [PATCH v5 0/6] mtd: spi-nor: parse SFDP tables to setup (Q)SPI memories
  2017-03-22 23:33 [PATCH v5 0/6] mtd: spi-nor: parse SFDP tables to setup (Q)SPI memories Cyrille Pitchen
                   ` (5 preceding siblings ...)
  2017-03-22 23:39 ` [RFC PATCH v5 6/6] mtd: spi-nor: parse SFDP 4-byte Address Instruction Table Cyrille Pitchen
@ 2017-03-29 16:45 ` Cyrille Pitchen
  2017-04-02 18:32   ` Marek Vasut
  6 siblings, 1 reply; 27+ messages in thread
From: Cyrille Pitchen @ 2017-03-29 16:45 UTC (permalink / raw)
  To: Cyrille Pitchen, marek.vasut, linux-mtd, jartur, kdasu.kdev,
	mar.krzeminski
  Cc: boris.brezillon, richard, nicolas.ferre, linux-kernel,
	computersforpeace, dwmw2

Hi all,

Le 23/03/2017 à 00:33, Cyrille Pitchen a écrit :
> Hi all,
> 
> based on git-hub/spi-nor and likely applicable on linux-next within the
> next few days.
> 
> 
> This new series of patchs aims to upgrade to spi-nor framework. We need to
> take into account latest SPI memories which cannot be handled correctly by
> the current implementation.
> 
> For instance, SPI NOR memories like Spansion S25FS512S or Macronix
> MX25U4035 support only the Fast Read 1-4-4 (EBh) command but not the
> Fast Read 1-1-4 (6Bh) command. However the current spi-nor framework
> supports only Fast Read 1-1-4 (6Bh).
> 
> Also the Spansion S25FS512S memory (and others) may use a non-uniform
> sector erase map, whereas the spi-nor framework assumes that a single
> sector erase size and opcode can be used anywhere inside the data array.
> This assumption is no longer valid.
> 
> Then parsing SFDP tables is an attempt to solve many of those issues by
> discovering dynamically most of the parameters and settings of the SPI NOR
> memory:
> - the flash size
> - the page size for Page Program commands
> - the supported Fast Read commands with the associated opcodes and number
>   of mode/wait-state (dummy) cycles.
> - the supported Sector/Block Erase commands with the associated opcodes
>   and sizes.
> - the erase sector map (for non-uniform memory).
> 
> 
> Besides, most QSPI controllers from the different vendors are capable to
> support the SPI 1-2-2 and 1-4-4 protocols but the spi-nor framework was
> not ready to use them. This series also fixes this issue and computes the
> best match between the hardware capabilies of both the SPI memory and the
> SPI controler (master) to select the right opcodes and dummy cycles for
> Fast Read and Page Program operations.
> 
> The new 'struct spi_nor_hwcaps' uses a bitmask to describe all the
> supported hardware capabilies and makes the difference between Fast Read
> and Page Program operations and also between the different SPI protocols
> (SPI 1-1-2 vs SPI 1-2-2 or SPI 1-1-4 vs SPI 1-4-4).
> 
> 
> IMHO, the first 3 patches of this series are ready to be merged into the
> github/spi-nor tree. Marek, do you agree with that?

Marek, if you have no comment on them, I will merge patches 1, 2, 3 in
the spi-nor tree within the next few days. Hence, people like Cédric
waiting for the series to merged could base their work on these patches.

Best regards,

Cyrille

> 
> Artur, patch 1 introduces some basic support to Octal SPI to help you with
> your series. About the op code values, I would like to wait for some JEDEC
> specification to provide the reference values or at least the datasheets
> of different vendors to guess the actual standard. For now, IMHO, only one
> manufacturer is not enough.
> 
> Kamal, if you don't mind I've put your Signed-off-by in patch 3 since the
> original patch is from you so I wanted to give you the credits :)
> I've just fixed some issue: in the original version, set4_byte() was
> called from spi_nor_init() even if (nor->addr_width == 3).
> I hope it will help you with the suspend/resume series.
> 
> 
> The 3 last patches are RFC and are provided so it's more easy for
> reviewers to understand the direction I would like to follow to upgrade
> the spi-nor framework.
> 
> Marcin, I haven't finished yet another 7th patch to parse the sector erase
> map table from SFDP so for now patch 4 is almost useless. Besides I only
> tested it with uniform memories to avoid regressions. I may need your help
> to test it with non-uniform memories, if you don't mind :)
> 
> 
> Best regards,
> 
> Cyrille
> 
> 
> ChangeLog:
> 
> v4 -> v5
> - rework the whole series.
> - introduce support for Octo SPI protocols.
> - introduce support for Double Transfer Rate (DTR) protocols.
> - introduce support for memories with non-uniform sector erase sizes.
> 
> v3 -> v4
> - replace dev_info() by dev_dbg() in patch 1.
> - split former patch 2 into 2 patches:
>   + new patch 2 deals with the rename of SPINOR_OP_READ4_* macros
>   + new patch 3 deals with the alternative methode to support memory
>> 16MiB
> - add comment in patch 3 to describe the dichotomic search performed by
>   spi_nor_convert_opcode().
> - change return type from int to void for m25p80_proto2nbits() in patch 6.
> - remove former patches 8 & 9 from the v2 series: the support of the
>   Macronix mx66l1g45g memory will be sent in a separated patch.
> 
> v2 -> v3
> - tested with new samples: Micron n25q512, n25q01g and Macronix
>   mx25v1635f, mx25l3235f, mx25l3273f.
> - add "Reviewed-by: Jagan Teki <jagan@openedev.com>" on patch 1.
> - add "Tested-by: Vignesh R <vigneshr@ti.com>" on patch 2.
> - fix some checkpatch warnings.
> - add call of spi_nor_wait_till_ready() in spansion_new_quad_enable()
>   and sr2_bit7_quad_enable(), as suggested by Joel Esponde on patch 6.
> - test JESD216 rev A (minor 5) instead of rev B (minor 6) with the return
>   code of spi_nor_parse_sfdp() from spi_nor_init_params() on patch 6.
>   The seven additional DWORDs of the Basic Flash Parameter Table were
>   introduced in rev A, not rev B, so the 15th DWORD was already available
>   in rev A. The 15th DWORD provides us with the Quad Enable Requirements
>   (QER) bits.
>   Basic Flash Parameter Table size:
>   + JESD216 :  9 DWORDS
>   + JESD216A: 16 DWORDS
>   + JESD216B: 16 DWORDS
> 
> v1 -> v2
> - fix patch 3 to resolve compiler errors on hisi-sfc.c and cadence-quadspi.c
>   drivers
> 
> 
> Cyrille Pitchen (6):
>   mtd: spi-nor: introduce more SPI protocols and the Dual Transfer Mode
>   mtd: m25p80: add support of SPI 1-2-2 and 1-4-4 protocols
>   mtd: spi-nor: add spi_nor_init() function
>   mtd: spi-nor: add support to non-uniform SPI NOR flash memories
>   mtd: spi-nor: parse Serial Flash Discoverable Parameters (SFDP) tables
>   mtd: spi-nor: parse SFDP 4-byte Address Instruction Table
> 
>  drivers/mtd/devices/m25p80.c          |  130 ++-
>  drivers/mtd/spi-nor/aspeed-smc.c      |   23 +-
>  drivers/mtd/spi-nor/atmel-quadspi.c   |   80 +-
>  drivers/mtd/spi-nor/cadence-quadspi.c |   18 +-
>  drivers/mtd/spi-nor/fsl-quadspi.c     |    8 +-
>  drivers/mtd/spi-nor/hisi-sfc.c        |   31 +-
>  drivers/mtd/spi-nor/intel-spi.c       |    7 +-
>  drivers/mtd/spi-nor/mtk-quadspi.c     |   16 +-
>  drivers/mtd/spi-nor/nxp-spifi.c       |   22 +-
>  drivers/mtd/spi-nor/spi-nor.c         | 1722 +++++++++++++++++++++++++++++----
>  include/linux/mtd/spi-nor.h           |  228 ++++-
>  11 files changed, 1983 insertions(+), 302 deletions(-)
> 

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

* Re: [PATCH v5 1/6] mtd: spi-nor: introduce more SPI protocols and the Dual Transfer Mode
  2017-03-22 23:33 ` [PATCH v5 1/6] mtd: spi-nor: introduce more SPI protocols and the Dual Transfer Mode Cyrille Pitchen
  2017-03-23 15:13   ` Cédric Le Goater
@ 2017-04-02 17:05   ` Cyrille Pitchen
  2017-04-06 23:30   ` Marek Vasut
  2 siblings, 0 replies; 27+ messages in thread
From: Cyrille Pitchen @ 2017-04-02 17:05 UTC (permalink / raw)
  To: Cyrille Pitchen, marek.vasut, linux-mtd, jartur, kdasu.kdev,
	mar.krzeminski
  Cc: boris.brezillon, richard, nicolas.ferre, linux-kernel,
	computersforpeace, dwmw2

Le 23/03/2017 à 00:33, Cyrille Pitchen a écrit :
> This patch changes the prototype of spi_nor_scan(): its 3rd parameter
> is replaced by a 'struct spi_nor_hwcaps' pointer, which tells the spi-nor
> framework about the actual hardware capabilities supported by the SPI
> controller and its driver.
> 
> Besides, this patch also introduces a new 'struct spi_nor_flash_parameter'
> telling the spi-nor framework about the hardware capabilities supported by
> the SPI flash memory and the associated settings required to use those
> hardware caps.
> 
> Currently the 'struct spi_nor_flash_parameter' is filled with legacy
> values but a later patch will allow to fill it dynamically by reading the
> JESD216 Serial Flash Discoverable Parameter (SFDP) tables from the SPI
> memory.
> 
> With both structures, the spi-nor framework can now compute the best
> match between hardware caps supported by both the (Q)SPI memory and
> controller hence selecting the relevant SPI protocols and op codes for
> (Fast) Read, Page Program and Sector Erase operations.
> 
> The 'struct spi_nor_flash_parameter' also provides the spi-nor framework
> with the number of dummy cycles to be used with each Fast Read commands
> and the erase block size associated to the erase block op codes.
> 
> Finally the 'struct spi_nor_flash_parameter', through the optional
> .enable_quad_io() hook, tells the spi-nor framework how to set the Quad
> Enable (QE) bit of the QSPI memory to enable its Quad SPI features.
> 
> Signed-off-by: Cyrille Pitchen <cyrille.pitchen@atmel.com>

Applied to github/spi-nor.

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

* Re: [PATCH v5 2/6] mtd: m25p80: add support of SPI 1-2-2 and 1-4-4 protocols
  2017-03-22 23:33 ` [PATCH v5 2/6] mtd: m25p80: add support of SPI 1-2-2 and 1-4-4 protocols Cyrille Pitchen
@ 2017-04-02 17:05   ` Cyrille Pitchen
  2017-04-06 23:37   ` Marek Vasut
  1 sibling, 0 replies; 27+ messages in thread
From: Cyrille Pitchen @ 2017-04-02 17:05 UTC (permalink / raw)
  To: Cyrille Pitchen, marek.vasut, linux-mtd, jartur, kdasu.kdev,
	mar.krzeminski
  Cc: boris.brezillon, richard, nicolas.ferre, linux-kernel,
	computersforpeace, dwmw2

Le 23/03/2017 à 00:33, Cyrille Pitchen a écrit :
> Before this patch, m25p80_read() supported few SPI protocols:
> - regular SPI 1-1-1
> - SPI Dual Output 1-1-2
> - SPI Quad Output 1-1-4
> On the other hand, m25p80_write() only supported SPI 1-1-1.
> 
> This patch updates both m25p80_read() and m25p80_write() functions to let
> them support SPI 1-2-2 and SPI 1-4-4 protocols for Fast Read and Page
> Program SPI commands.
> 
> It adopts a conservative approach to avoid regressions. Hence the new
> implementations try to be as close as possible to the old implementations,
> so the main differences are:
> - the tx_nbits values now being set properly for the spi_transfer
>   structures carrying the (op code + address/dummy) bytes
> - and the spi_transfer structure being split into 2 spi_transfer
>   structures when the numbers of I/O lines are different for op code and
>   for address/dummy byte transfers on the SPI bus.
> 
> Besides, the current spi-nor framework supports neither the SPI 2-2-2 nor
> the SPI 4-4-4 protocols. So, for now, we don't need to update the
> m25p80_{read|write}_reg() functions as SPI 1-1-1 is the only one possible
> protocol.
> 
> Signed-off-by: Cyrille Pitchen <cyrille.pitchen@atmel.com>

Applied to github/spi-nor

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

* Re: [PATCH v5 3/6] mtd: spi-nor: add spi_nor_init() function
  2017-03-22 23:33 ` [PATCH v5 3/6] mtd: spi-nor: add spi_nor_init() function Cyrille Pitchen
@ 2017-04-02 17:06   ` Cyrille Pitchen
  0 siblings, 0 replies; 27+ messages in thread
From: Cyrille Pitchen @ 2017-04-02 17:06 UTC (permalink / raw)
  To: Cyrille Pitchen, marek.vasut, linux-mtd, jartur, kdasu.kdev,
	mar.krzeminski
  Cc: boris.brezillon, richard, nicolas.ferre, linux-kernel,
	computersforpeace, dwmw2

Le 23/03/2017 à 00:33, Cyrille Pitchen a écrit :
> This patch extracts some chunks from spi_nor_scan() and moves them into
> a new spi_nor_init() function.
> 
> Indeed, spi_nor_init() regroups all the required SPI flash commands to be
> sent to the SPI flash memory before performing any runtime operations
> (Fast Read, Page Program, Sector Erase, ...). Hence spi_nor_init():
> 1) removes the flash protection if applicable for certain vendors.
> 2) sets the Quad Enable bit, if needed, before using Quad SPI protocols.
> 3) makes the memory enter its (stateful) 4-byte address mode, if needed,
>    for SPI flash memory > 128Mbits not supporting the 4-byte address
>    instruction set.
> 
> spi_nor_scan() now ends by calling spi_nor_init() once the probe phase has
> completed. Further patches could also use spi_nor_init() to implement the
> mtd->_resume() handler for the spi-nor framework.
> 
> Signed-off-by: Kamal Dasu <kdasu.kdev@gmail.com>
> Signed-off-by: Cyrille Pitchen <cyrille.pitchen@atmel.com>

Applied to github/spi-nor

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

* Re: [PATCH v5 0/6] mtd: spi-nor: parse SFDP tables to setup (Q)SPI memories
  2017-03-29 16:45 ` [PATCH v5 0/6] mtd: spi-nor: parse SFDP tables to setup (Q)SPI memories Cyrille Pitchen
@ 2017-04-02 18:32   ` Marek Vasut
  0 siblings, 0 replies; 27+ messages in thread
From: Marek Vasut @ 2017-04-02 18:32 UTC (permalink / raw)
  To: Cyrille Pitchen, Cyrille Pitchen, linux-mtd, jartur, kdasu.kdev,
	mar.krzeminski
  Cc: boris.brezillon, richard, nicolas.ferre, linux-kernel,
	computersforpeace, dwmw2

On 03/29/2017 06:45 PM, Cyrille Pitchen wrote:
> Hi all,
> 
> Le 23/03/2017 à 00:33, Cyrille Pitchen a écrit :
>> Hi all,
>>
>> based on git-hub/spi-nor and likely applicable on linux-next within the
>> next few days.
>>
>>
>> This new series of patchs aims to upgrade to spi-nor framework. We need to
>> take into account latest SPI memories which cannot be handled correctly by
>> the current implementation.
>>
>> For instance, SPI NOR memories like Spansion S25FS512S or Macronix
>> MX25U4035 support only the Fast Read 1-4-4 (EBh) command but not the
>> Fast Read 1-1-4 (6Bh) command. However the current spi-nor framework
>> supports only Fast Read 1-1-4 (6Bh).
>>
>> Also the Spansion S25FS512S memory (and others) may use a non-uniform
>> sector erase map, whereas the spi-nor framework assumes that a single
>> sector erase size and opcode can be used anywhere inside the data array.
>> This assumption is no longer valid.
>>
>> Then parsing SFDP tables is an attempt to solve many of those issues by
>> discovering dynamically most of the parameters and settings of the SPI NOR
>> memory:
>> - the flash size
>> - the page size for Page Program commands
>> - the supported Fast Read commands with the associated opcodes and number
>>   of mode/wait-state (dummy) cycles.
>> - the supported Sector/Block Erase commands with the associated opcodes
>>   and sizes.
>> - the erase sector map (for non-uniform memory).
>>
>>
>> Besides, most QSPI controllers from the different vendors are capable to
>> support the SPI 1-2-2 and 1-4-4 protocols but the spi-nor framework was
>> not ready to use them. This series also fixes this issue and computes the
>> best match between the hardware capabilies of both the SPI memory and the
>> SPI controler (master) to select the right opcodes and dummy cycles for
>> Fast Read and Page Program operations.
>>
>> The new 'struct spi_nor_hwcaps' uses a bitmask to describe all the
>> supported hardware capabilies and makes the difference between Fast Read
>> and Page Program operations and also between the different SPI protocols
>> (SPI 1-1-2 vs SPI 1-2-2 or SPI 1-1-4 vs SPI 1-4-4).
>>
>>
>> IMHO, the first 3 patches of this series are ready to be merged into the
>> github/spi-nor tree. Marek, do you agree with that?
> 
> Marek, if you have no comment on them, I will merge patches 1, 2, 3 in
> the spi-nor tree within the next few days. Hence, people like Cédric
> waiting for the series to merged could base their work on these patches.

Hm well, you could've at least poked me about this, I was so snowed
under that I didn't have any chance to take a look :(

-- 
Best regards,
Marek Vasut

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

* Re: [PATCH v5 1/6] mtd: spi-nor: introduce more SPI protocols and the Dual Transfer Mode
  2017-03-22 23:33 ` [PATCH v5 1/6] mtd: spi-nor: introduce more SPI protocols and the Dual Transfer Mode Cyrille Pitchen
  2017-03-23 15:13   ` Cédric Le Goater
  2017-04-02 17:05   ` Cyrille Pitchen
@ 2017-04-06 23:30   ` Marek Vasut
  2017-04-09 21:16     ` Cyrille Pitchen
  2 siblings, 1 reply; 27+ messages in thread
From: Marek Vasut @ 2017-04-06 23:30 UTC (permalink / raw)
  To: Cyrille Pitchen, linux-mtd, jartur, kdasu.kdev, mar.krzeminski
  Cc: computersforpeace, dwmw2, boris.brezillon, richard, linux-kernel,
	nicolas.ferre

On 03/23/2017 12:33 AM, Cyrille Pitchen wrote:
> This patch changes the prototype of spi_nor_scan(): its 3rd parameter
> is replaced by a 'struct spi_nor_hwcaps' pointer, which tells the spi-nor
> framework about the actual hardware capabilities supported by the SPI
> controller and its driver.
> 
> Besides, this patch also introduces a new 'struct spi_nor_flash_parameter'
> telling the spi-nor framework about the hardware capabilities supported by
> the SPI flash memory and the associated settings required to use those
> hardware caps.
> 
> Currently the 'struct spi_nor_flash_parameter' is filled with legacy
> values but a later patch will allow to fill it dynamically by reading the
> JESD216 Serial Flash Discoverable Parameter (SFDP) tables from the SPI
> memory.
> 
> With both structures, the spi-nor framework can now compute the best
> match between hardware caps supported by both the (Q)SPI memory and
> controller hence selecting the relevant SPI protocols and op codes for
> (Fast) Read, Page Program and Sector Erase operations.
> 
> The 'struct spi_nor_flash_parameter' also provides the spi-nor framework
> with the number of dummy cycles to be used with each Fast Read commands
> and the erase block size associated to the erase block op codes.
> 
> Finally the 'struct spi_nor_flash_parameter', through the optional
> .enable_quad_io() hook, tells the spi-nor framework how to set the Quad
> Enable (QE) bit of the QSPI memory to enable its Quad SPI features.
> 
> Signed-off-by: Cyrille Pitchen <cyrille.pitchen@atmel.com>
> ---
>  drivers/mtd/devices/m25p80.c          |  16 +-
>  drivers/mtd/spi-nor/aspeed-smc.c      |  23 +-
>  drivers/mtd/spi-nor/atmel-quadspi.c   |  80 +++---
>  drivers/mtd/spi-nor/cadence-quadspi.c |  18 +-
>  drivers/mtd/spi-nor/fsl-quadspi.c     |   8 +-
>  drivers/mtd/spi-nor/hisi-sfc.c        |  31 ++-
>  drivers/mtd/spi-nor/intel-spi.c       |   7 +-
>  drivers/mtd/spi-nor/mtk-quadspi.c     |  16 +-
>  drivers/mtd/spi-nor/nxp-spifi.c       |  22 +-
>  drivers/mtd/spi-nor/spi-nor.c         | 441 +++++++++++++++++++++++++++-------
>  include/linux/mtd/spi-nor.h           | 158 +++++++++++-
>  11 files changed, 643 insertions(+), 177 deletions(-)
> 
> diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c
> index c4df3b1bded0..68986a26c8fe 100644
> --- a/drivers/mtd/devices/m25p80.c
> +++ b/drivers/mtd/devices/m25p80.c
> @@ -111,10 +111,10 @@ static ssize_t m25p80_write(struct spi_nor *nor, loff_t to, size_t len,
>  
>  static inline unsigned int m25p80_rx_nbits(struct spi_nor *nor)
>  {
> -	switch (nor->flash_read) {
> -	case SPI_NOR_DUAL:
> +	switch (nor->read_proto) {
> +	case SNOR_PROTO_1_1_2:
>  		return 2;
> -	case SPI_NOR_QUAD:
> +	case SNOR_PROTO_1_1_4:
>  		return 4;
>  	default:
>  		return 0;
> @@ -196,7 +196,9 @@ static int m25p_probe(struct spi_device *spi)
>  	struct flash_platform_data	*data;
>  	struct m25p *flash;
>  	struct spi_nor *nor;
> -	enum read_mode mode = SPI_NOR_NORMAL;
> +	struct spi_nor_hwcaps hwcaps = {
> +		.mask = (SNOR_HWCAPS_READ | SNOR_HWCAPS_PP),

Drop the unneeded parenthesis.

> +	};
>  	char *flash_name;
>  	int ret;
>  
> @@ -222,9 +224,9 @@ static int m25p_probe(struct spi_device *spi)
>  	flash->spi = spi;
>  
>  	if (spi->mode & SPI_RX_QUAD)
> -		mode = SPI_NOR_QUAD;
> +		hwcaps.mask |= SNOR_HWCAPS_READ_1_1_4;
>  	else if (spi->mode & SPI_RX_DUAL)
> -		mode = SPI_NOR_DUAL;
> +		hwcaps.mask |= SNOR_HWCAPS_READ_1_1_2;
>  
>  	if (data && data->name)
>  		nor->mtd.name = data->name;
> @@ -241,7 +243,7 @@ static int m25p_probe(struct spi_device *spi)
>  	else
>  		flash_name = spi->modalias;
>  
> -	ret = spi_nor_scan(nor, flash_name, mode);
> +	ret = spi_nor_scan(nor, flash_name, &hwcaps);
>  	if (ret)
>  		return ret;
>  
> diff --git a/drivers/mtd/spi-nor/aspeed-smc.c b/drivers/mtd/spi-nor/aspeed-smc.c
> index 56051d30f000..723026d9cf0c 100644
> --- a/drivers/mtd/spi-nor/aspeed-smc.c
> +++ b/drivers/mtd/spi-nor/aspeed-smc.c
> @@ -585,14 +585,12 @@ static int aspeed_smc_chip_setup_finish(struct aspeed_smc_chip *chip)
>  	 * TODO: Adjust clocks if fast read is supported and interpret
>  	 * SPI-NOR flags to adjust controller settings.
>  	 */
> -	switch (chip->nor.flash_read) {
> -	case SPI_NOR_NORMAL:
> -		cmd = CONTROL_COMMAND_MODE_NORMAL;
> -		break;
> -	case SPI_NOR_FAST:
> -		cmd = CONTROL_COMMAND_MODE_FREAD;
> -		break;
> -	default:
> +	if (chip->nor.read_proto == SNOR_PROTO_1_1_1) {
> +		if (chip->nor.read_dummy == 0)
> +			cmd = CONTROL_COMMAND_MODE_NORMAL;
> +		else
> +			cmd = CONTROL_COMMAND_MODE_FREAD;
> +	} else {
>  		dev_err(chip->nor.dev, "unsupported SPI read mode\n");
>  		return -EINVAL;
>  	}
> @@ -608,6 +606,11 @@ static int aspeed_smc_chip_setup_finish(struct aspeed_smc_chip *chip)
>  static int aspeed_smc_setup_flash(struct aspeed_smc_controller *controller,
>  				  struct device_node *np, struct resource *r)
>  {
> +	struct spi_nor_hwcaps hwcaps = {
> +		.mask = (SNOR_HWCAPS_READ |
> +			 SNOR_HWCAPS_READ_FAST |
> +			 SNOR_HWCAPS_PP),

Drop the extra parenthesis ... shouldn't the structure be const ?

> +	};
>  	const struct aspeed_smc_info *info = controller->info;
>  	struct device *dev = controller->dev;
>  	struct device_node *child;
> @@ -671,11 +674,11 @@ static int aspeed_smc_setup_flash(struct aspeed_smc_controller *controller,
>  			break;
>  
>  		/*
> -		 * TODO: Add support for SPI_NOR_QUAD and SPI_NOR_DUAL
> +		 * TODO: Add support for Dual and Quad SPI protocols
>  		 * attach when board support is present as determined
>  		 * by of property.
>  		 */
> -		ret = spi_nor_scan(nor, NULL, SPI_NOR_NORMAL);
> +		ret = spi_nor_scan(nor, NULL, &hwcaps);
>  		if (ret)
>  			break;


[...]

> +struct spi_nor_flash_parameter {
> +	u64				size;
> +	u32				page_size;
> +
> +	struct spi_nor_hwcaps		hwcaps;
> +	struct spi_nor_read_command	reads[SNOR_CMD_READ_MAX];
> +	struct spi_nor_pp_command	page_programs[SNOR_CMD_PP_MAX];
> +
> +	int (*quad_enable)(struct spi_nor *nor);

This callback should be added by a separate patch, there's WAY too much
crap in this patch.

> +};
> +
> +
> +static inline 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)
> +{
> +	read->num_mode_clocks = num_mode_clocks;
> +	read->num_wait_states = num_wait_states;
> +	read->opcode = opcode;
> +	read->proto = proto;
> +}
> +
> +static inline void

Drop the inline , the compiler will decide. Fix globally.

> +spi_nor_set_pp_settings(struct spi_nor_pp_command *pp,
> +			u8 opcode,
> +			enum spi_nor_protocol proto)
> +{
> +	pp->opcode = opcode;
> +	pp->proto = proto;
> +}
> +
> +static int spi_nor_init_params(struct spi_nor *nor,
> +			       const struct flash_info *info,
> +			       struct spi_nor_flash_parameter *params)
> +{
> +	/* Set legacy flash parameters as default. */
> +	memset(params, 0, sizeof(*params));
> +
> +	/* Set SPI NOR sizes. */
> +	params->size = info->sector_size * info->n_sectors;
> +	params->page_size = info->page_size;
> +
> +	/* (Fast) Read settings. */
> +	params->hwcaps.mask |= SNOR_HWCAPS_READ;
> +	spi_nor_set_read_settings(&params->reads[SNOR_CMD_READ],
> +				  0, 0, SPINOR_OP_READ,
> +				  SNOR_PROTO_1_1_1);

Newline

> +	if (!(info->flags & SPI_NOR_NO_FR)) {
> +		params->hwcaps.mask |= SNOR_HWCAPS_READ_FAST;
> +		spi_nor_set_read_settings(&params->reads[SNOR_CMD_READ_FAST],
> +					  0, 8, SPINOR_OP_READ_FAST,
> +					  SNOR_PROTO_1_1_1);
> +	}

Newline

> +	if (info->flags & SPI_NOR_DUAL_READ) {
> +		params->hwcaps.mask |= SNOR_HWCAPS_READ_1_1_2;
> +		spi_nor_set_read_settings(&params->reads[SNOR_CMD_READ_1_1_2],
> +					  0, 8, SPINOR_OP_READ_1_1_2,
> +					  SNOR_PROTO_1_1_2);
> +	}

Newline ... this is really hard to read as it is.

> +	if (info->flags & SPI_NOR_QUAD_READ) {
> +		params->hwcaps.mask |= SNOR_HWCAPS_READ_1_1_4;
> +		spi_nor_set_read_settings(&params->reads[SNOR_CMD_READ_1_1_4],
> +					  0, 8, SPINOR_OP_READ_1_1_4,
> +					  SNOR_PROTO_1_1_4);
> +	}
> +
> +	/* 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);
> +
> +	/* Select the procedure to set the Quad Enable bit. */
> +	if (params->hwcaps.mask & (SNOR_HWCAPS_READ_QUAD |
> +				   SNOR_HWCAPS_PP_QUAD)) {
> +		switch (JEDEC_MFR(info)) {
> +		case SNOR_MFR_MACRONIX:
> +			params->quad_enable = macronix_quad_enable;
> +			break;
> +
> +		case SNOR_MFR_MICRON:
> +			break;
> +
> +		default:

Are you sure this is a good idea ?

> +			params->quad_enable = spansion_quad_enable;
> +			break;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +static int spi_nor_hwcaps2cmd(u32 hwcaps)

const u32 hwcaps ...

>  {
> +	switch (hwcaps) {
> +	case SNOR_HWCAPS_READ:			return SNOR_CMD_READ;

You can do this as a table lookup or array indexing and it would be
checkpatch clean.

> +	case SNOR_HWCAPS_READ_FAST:		return SNOR_CMD_READ_FAST;
> +	case SNOR_HWCAPS_READ_1_1_1_DTR:	return SNOR_CMD_READ_1_1_1_DTR;
> +	case SNOR_HWCAPS_READ_1_1_2:		return SNOR_CMD_READ_1_1_2;
> +	case SNOR_HWCAPS_READ_1_2_2:		return SNOR_CMD_READ_1_2_2;
> +	case SNOR_HWCAPS_READ_2_2_2:		return SNOR_CMD_READ_2_2_2;
> +	case SNOR_HWCAPS_READ_1_2_2_DTR:	return SNOR_CMD_READ_1_2_2_DTR;
> +	case SNOR_HWCAPS_READ_1_1_4:		return SNOR_CMD_READ_1_1_4;
> +	case SNOR_HWCAPS_READ_1_4_4:		return SNOR_CMD_READ_1_4_4;
> +	case SNOR_HWCAPS_READ_4_4_4:		return SNOR_CMD_READ_4_4_4;
> +	case SNOR_HWCAPS_READ_1_4_4_DTR:	return SNOR_CMD_READ_1_4_4_DTR;
> +	case SNOR_HWCAPS_READ_1_1_8:		return SNOR_CMD_READ_1_1_8;
> +	case SNOR_HWCAPS_READ_1_8_8:		return SNOR_CMD_READ_1_8_8;
> +	case SNOR_HWCAPS_READ_8_8_8:		return SNOR_CMD_READ_8_8_8;
> +	case SNOR_HWCAPS_READ_1_8_8_DTR:	return SNOR_CMD_READ_1_8_8_DTR;
> +
> +	case SNOR_HWCAPS_PP:			return SNOR_CMD_PP;
> +	case SNOR_HWCAPS_PP_1_1_4:		return SNOR_CMD_PP_1_1_4;
> +	case SNOR_HWCAPS_PP_1_4_4:		return SNOR_CMD_PP_1_4_4;
> +	case SNOR_HWCAPS_PP_4_4_4:		return SNOR_CMD_PP_4_4_4;
> +	case SNOR_HWCAPS_PP_1_1_8:		return SNOR_CMD_PP_1_1_8;
> +	case SNOR_HWCAPS_PP_1_8_8:		return SNOR_CMD_PP_1_8_8;
> +	case SNOR_HWCAPS_PP_8_8_8:		return SNOR_CMD_PP_8_8_8;
> +	}
> +
> +	return -EINVAL;
> +}
> +
> +static int spi_nor_select_read(struct spi_nor *nor,
> +			       const struct spi_nor_flash_parameter *params,
> +			       u32 shared_hwcaps)
> +{
> +	int cmd, best_match = fls(shared_hwcaps & SNOR_HWCAPS_READ_MASK) - 1;
> +	const struct spi_nor_read_command *read;
> +
> +	if (best_match < 0)
> +		return -EINVAL;
> +
> +	cmd = spi_nor_hwcaps2cmd(BIT(best_match));

How does this work? Do we assume that hwcaps2cmd is always given a value
with 1-bit set ? That's quite wasteful IMO.

> +	if (cmd < 0)
> +		return -EINVAL;
> +
> +	read = &params->reads[cmd];
> +	nor->read_opcode = read->opcode;
> +	nor->read_proto = read->proto;
> +
> +	/*
> +	 * In the spi-nor framework, we don't need to make the difference
> +	 * between mode clock cycles and wait state clock cycles.
> +	 * Indeed, the value of the mode clock cycles is used by a QSPI
> +	 * flash memory to know whether it should enter or leave its 0-4-4
> +	 * (Continuous Read / XIP) mode.

0-4-4 ?

> +	 * eXecution In Place is out of the scope of the mtd sub-system.
> +	 * Hence we choose to merge both mode and wait state clock cycles
> +	 * into the so called dummy clock cycles.
> +	 */
> +	nor->read_dummy = read->num_mode_clocks + read->num_wait_states;
> +	return 0;
> +}
> +
> +static int spi_nor_select_pp(struct spi_nor *nor,
> +			     const struct spi_nor_flash_parameter *params,
> +			     u32 shared_hwcaps)
> +{
> +	int cmd, best_match = fls(shared_hwcaps & SNOR_HWCAPS_PP_MASK) - 1;
> +	const struct spi_nor_pp_command *pp;
> +
> +	if (best_match < 0)
> +		return -EINVAL;
> +
> +	cmd = spi_nor_hwcaps2cmd(BIT(best_match));
> +	if (cmd < 0)
> +		return -EINVAL;
> +
> +	pp = &params->page_programs[cmd];
> +	nor->program_opcode = pp->opcode;
> +	nor->write_proto = pp->proto;
> +	return 0;
> +}
> +
> +static int spi_nor_select_erase(struct spi_nor *nor,
> +				const struct flash_info *info)
> +{
> +	struct mtd_info *mtd = &nor->mtd;
> +
> +#ifdef CONFIG_MTD_SPI_NOR_USE_4K_SECTORS
> +	/* prefer "small sector" erase if possible */
> +	if (info->flags & SECT_4K) {
> +		nor->erase_opcode = SPINOR_OP_BE_4K;
> +		mtd->erasesize = 4096;
> +	} else if (info->flags & SECT_4K_PMC) {
> +		nor->erase_opcode = SPINOR_OP_BE_4K_PMC;
> +		mtd->erasesize = 4096;
> +	} else
> +#endif
> +	{
> +		nor->erase_opcode = SPINOR_OP_SE;
> +		mtd->erasesize = info->sector_size;
> +	}
> +	return 0;
> +}
> +
> +static int spi_nor_setup(struct spi_nor *nor, const struct flash_info *info,
> +			 const struct spi_nor_flash_parameter *params,
> +			 const struct spi_nor_hwcaps *hwcaps)
> +{
> +	u32 ignored_mask, shared_mask;
> +	bool enable_quad_io;
> +	int err;
> +
> +	/*
> +	 * Keep only the hardware capabilities supported by both the SPI
> +	 * controller and the SPI flash memory.
> +	 */
> +	shared_mask = hwcaps->mask & params->hwcaps.mask;
> +
> +	/* SPI protocol classes N-N-N are not supported yet. */
> +	ignored_mask = (SNOR_HWCAPS_READ_2_2_2 |
> +			SNOR_HWCAPS_READ_4_4_4 |
> +			SNOR_HWCAPS_READ_8_8_8 |
> +			SNOR_HWCAPS_PP_4_4_4 |
> +			SNOR_HWCAPS_PP_8_8_8);
> +	if (shared_mask & ignored_mask) {
> +		dev_dbg(nor->dev,
> +			"SPI protocol classes N-N-N are not supported yet.\n");
> +		shared_mask &= ~ignored_mask;
> +	}
> +
> +	/* Select the (Fast) Read command. */
> +	err = spi_nor_select_read(nor, params, shared_mask);
> +	if (err) {
> +		dev_err(nor->dev, "invalid (fast) read\n");

What does this information tell me, as an end user ? If I see this error
message, what sort of conclusion can I derive from it ? I have
no idea ...

> +		return err;
> +	}
> +
> +	/* Select the Page Program command. */
> +	err = spi_nor_select_pp(nor, params, shared_mask);
> +	if (err) {
> +		dev_err(nor->dev, "invalid page program\n");
> +		return err;
> +	}
> +
> +	/* Select the Sector Erase command. */
> +	err = spi_nor_select_erase(nor, info);
> +	if (err) {
> +		dev_err(nor->dev, "invalid sector/block erase\n");
> +		return err;
> +	}
> +
> +	/* Enable Quad I/O if needed. */
> +	enable_quad_io = (spi_nor_get_protocol_width(nor->read_proto) == 4 ||
> +			  spi_nor_get_protocol_width(nor->write_proto) == 4);

What if read_proto != write_proto ? Also, this is awful code ... fix it.

> +	if (enable_quad_io && params->quad_enable)
> +		nor->flash_quad_enable = params->quad_enable;
> +	else
> +		nor->flash_quad_enable = NULL;
> +
> +	return 0;
> +}
> +
> +int spi_nor_scan(struct spi_nor *nor, const char *name,
> +		 const struct spi_nor_hwcaps *hwcaps)
> +{
> +	struct spi_nor_flash_parameter params;
>  	const struct flash_info *info = NULL;
>  	struct device *dev = nor->dev;
>  	struct mtd_info *mtd = &nor->mtd;
> @@ -1548,6 +1824,11 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
>  	if (ret)
>  		return ret;
>  
> +	/* Reset SPI protocol for all commands */
> +	nor->reg_proto = SNOR_PROTO_1_1_1;
> +	nor->read_proto = SNOR_PROTO_1_1_1;
> +	nor->write_proto = SNOR_PROTO_1_1_1;
> +
>  	if (name)
>  		info = spi_nor_match_id(name);
>  	/* Try to auto-detect if chip name wasn't specified or not found */
[...]

-- 
Best regards,
Marek Vasut

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

* Re: [PATCH v5 2/6] mtd: m25p80: add support of SPI 1-2-2 and 1-4-4 protocols
  2017-03-22 23:33 ` [PATCH v5 2/6] mtd: m25p80: add support of SPI 1-2-2 and 1-4-4 protocols Cyrille Pitchen
  2017-04-02 17:05   ` Cyrille Pitchen
@ 2017-04-06 23:37   ` Marek Vasut
  2017-04-09 19:37     ` Cyrille Pitchen
  1 sibling, 1 reply; 27+ messages in thread
From: Marek Vasut @ 2017-04-06 23:37 UTC (permalink / raw)
  To: Cyrille Pitchen, linux-mtd, jartur, kdasu.kdev, mar.krzeminski
  Cc: computersforpeace, dwmw2, boris.brezillon, richard, linux-kernel,
	nicolas.ferre

On 03/23/2017 12:33 AM, Cyrille Pitchen wrote:
> Before this patch, m25p80_read() supported few SPI protocols:
> - regular SPI 1-1-1
> - SPI Dual Output 1-1-2
> - SPI Quad Output 1-1-4
> On the other hand, m25p80_write() only supported SPI 1-1-1.
> 
> This patch updates both m25p80_read() and m25p80_write() functions to let
> them support SPI 1-2-2 and SPI 1-4-4 protocols for Fast Read and Page
> Program SPI commands.
> 
> It adopts a conservative approach to avoid regressions. Hence the new
                                             ^ FYI, regression != bug

> implementations try to be as close as possible to the old implementations,
> so the main differences are:
> - the tx_nbits values now being set properly for the spi_transfer
>   structures carrying the (op code + address/dummy) bytes
> - and the spi_transfer structure being split into 2 spi_transfer
>   structures when the numbers of I/O lines are different for op code and
>   for address/dummy byte transfers on the SPI bus.
> 
> Besides, the current spi-nor framework supports neither the SPI 2-2-2 nor
> the SPI 4-4-4 protocols. So, for now, we don't need to update the
> m25p80_{read|write}_reg() functions as SPI 1-1-1 is the only one possible
> protocol.
> 
> Signed-off-by: Cyrille Pitchen <cyrille.pitchen@atmel.com>
> ---
>  drivers/mtd/devices/m25p80.c | 120 ++++++++++++++++++++++++++++++++-----------
>  1 file changed, 90 insertions(+), 30 deletions(-)
> 
> diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c
> index 68986a26c8fe..64d562efc25d 100644
> --- a/drivers/mtd/devices/m25p80.c
> +++ b/drivers/mtd/devices/m25p80.c
> @@ -34,6 +34,19 @@ struct m25p {
>  	u8			command[MAX_CMD_SIZE];
>  };
>  
> +static inline void m25p80_proto2nbits(enum spi_nor_protocol proto,
> +				      unsigned int *inst_nbits,
> +				      unsigned int *addr_nbits,
> +				      unsigned int *data_nbits)
> +{

Why don't we just have some generic macros to extract the number of bits
from $proto ?

> +	if (inst_nbits)
> +		*inst_nbits = spi_nor_get_protocol_inst_width(proto);
> +	if (addr_nbits)
> +		*addr_nbits = spi_nor_get_protocol_addr_width(proto);
> +	if (data_nbits)
> +		*data_nbits = spi_nor_get_protocol_data_width(proto);
> +}
> +
>  static int m25p80_read_reg(struct spi_nor *nor, u8 code, u8 *val, int len)
>  {
>  	struct m25p *flash = nor->priv;
> @@ -78,11 +91,16 @@ static ssize_t m25p80_write(struct spi_nor *nor, loff_t to, size_t len,
>  {
>  	struct m25p *flash = nor->priv;
>  	struct spi_device *spi = flash->spi;
> -	struct spi_transfer t[2] = {};
> +	unsigned int inst_nbits, addr_nbits, data_nbits, data_idx;
> +	struct spi_transfer t[3] = {};
>  	struct spi_message m;
>  	int cmd_sz = m25p_cmdsz(nor);
>  	ssize_t ret;
>  
> +	/* get transfer protocols. */
> +	m25p80_proto2nbits(nor->write_proto, &inst_nbits,
> +			   &addr_nbits, &data_nbits);
> +
>  	spi_message_init(&m);
>  
>  	if (nor->program_opcode == SPINOR_OP_AAI_WP && nor->sst_write_second)
> @@ -92,12 +110,27 @@ static ssize_t m25p80_write(struct spi_nor *nor, loff_t to, size_t len,
>  	m25p_addr2cmd(nor, to, flash->command);
>  
>  	t[0].tx_buf = flash->command;
> +	t[0].tx_nbits = inst_nbits;
>  	t[0].len = cmd_sz;
>  	spi_message_add_tail(&t[0], &m);
>  
> -	t[1].tx_buf = buf;
> -	t[1].len = len;
> -	spi_message_add_tail(&t[1], &m);
> +	/* split the op code and address bytes into two transfers if needed. */
> +	data_idx = 1;
> +	if (addr_nbits != inst_nbits) {
> +		t[0].len = 1;
> +
> +		t[1].tx_buf = &flash->command[1];
> +		t[1].tx_nbits = addr_nbits;
> +		t[1].len = cmd_sz - 1;
> +		spi_message_add_tail(&t[1], &m);
> +
> +		data_idx = 2;
> +	}
> +
> +	t[data_idx].tx_buf = buf;
> +	t[data_idx].tx_nbits = data_nbits;
> +	t[data_idx].len = len;
> +	spi_message_add_tail(&t[data_idx], &m);
>  
>  	ret = spi_sync(spi, &m);
>  	if (ret)
> @@ -109,18 +142,6 @@ static ssize_t m25p80_write(struct spi_nor *nor, loff_t to, size_t len,
>  	return ret;
>  }
>  
> -static inline unsigned int m25p80_rx_nbits(struct spi_nor *nor)
> -{
> -	switch (nor->read_proto) {
> -	case SNOR_PROTO_1_1_2:
> -		return 2;
> -	case SNOR_PROTO_1_1_4:
> -		return 4;
> -	default:
> -		return 0;
> -	}
> -}
> -
>  /*
>   * Read an address range from the nor chip.  The address range
>   * may be any size provided it is within the physical boundaries.
> @@ -130,13 +151,19 @@ static ssize_t m25p80_read(struct spi_nor *nor, loff_t from, size_t len,
>  {
>  	struct m25p *flash = nor->priv;
>  	struct spi_device *spi = flash->spi;
> -	struct spi_transfer t[2];
> +	unsigned int inst_nbits, addr_nbits, data_nbits, data_idx;
> +	struct spi_transfer t[3];
>  	struct spi_message m;
>  	unsigned int dummy = nor->read_dummy;
>  	ssize_t ret;
> +	int cmd_sz;
> +
> +	/* get transfer protocols. */
> +	m25p80_proto2nbits(nor->read_proto, &inst_nbits,
> +			   &addr_nbits, &data_nbits);
>  
>  	/* convert the dummy cycles to the number of bytes */
> -	dummy /= 8;
> +	dummy = (dummy * addr_nbits) / 8;
>  
>  	if (spi_flash_read_supported(spi)) {
>  		struct spi_flash_read_message msg;
> @@ -149,10 +176,9 @@ static ssize_t m25p80_read(struct spi_nor *nor, loff_t from, size_t len,
>  		msg.read_opcode = nor->read_opcode;
>  		msg.addr_width = nor->addr_width;
>  		msg.dummy_bytes = dummy;
> -		/* TODO: Support other combinations */
> -		msg.opcode_nbits = SPI_NBITS_SINGLE;
> -		msg.addr_nbits = SPI_NBITS_SINGLE;
> -		msg.data_nbits = m25p80_rx_nbits(nor);
> +		msg.opcode_nbits = inst_nbits;
> +		msg.addr_nbits = addr_nbits;
> +		msg.data_nbits = data_nbits;
>  
>  		ret = spi_flash_read(spi, &msg);
>  		if (ret < 0)
> @@ -167,20 +193,45 @@ static ssize_t m25p80_read(struct spi_nor *nor, loff_t from, size_t len,
>  	m25p_addr2cmd(nor, from, flash->command);
>  
>  	t[0].tx_buf = flash->command;
> +	t[0].tx_nbits = inst_nbits;
>  	t[0].len = m25p_cmdsz(nor) + dummy;
>  	spi_message_add_tail(&t[0], &m);
>  
> -	t[1].rx_buf = buf;
> -	t[1].rx_nbits = m25p80_rx_nbits(nor);
> -	t[1].len = min3(len, spi_max_transfer_size(spi),
> -			spi_max_message_size(spi) - t[0].len);
> -	spi_message_add_tail(&t[1], &m);
> +	/*
> +	 * Set all dummy/mode cycle bits to avoid sending some manufacturer
> +	 * specific pattern, which might make the memory enter its Continuous
> +	 * Read mode by mistake.
> +	 * Based on the different mode cycle bit patterns listed and described
> +	 * in the JESD216B speficication, the 0xff value works for all memories
                           ^
                           specification, typo

> +	 * and all manufacturers.
> +	 */
> +	cmd_sz = t[0].len;
> +	memset(flash->command + cmd_sz - dummy, 0xff, dummy);
> +
> +	/* split the op code and address bytes into two transfers if needed. */
> +	data_idx = 1;
> +	if (addr_nbits != inst_nbits) {
> +		t[0].len = 1;
> +
> +		t[1].tx_buf = &flash->command[1];
> +		t[1].tx_nbits = addr_nbits;
> +		t[1].len = cmd_sz - 1;
> +		spi_message_add_tail(&t[1], &m);
> +
> +		data_idx = 2;
> +	}
> +
> +	t[data_idx].rx_buf = buf;
> +	t[data_idx].rx_nbits = data_nbits;
> +	t[data_idx].len = min3(len, spi_max_transfer_size(spi),
> +			       spi_max_message_size(spi) - cmd_sz);
> +	spi_message_add_tail(&t[data_idx], &m);
>  
>  	ret = spi_sync(spi, &m);
>  	if (ret)
>  		return ret;
>  
> -	ret = m.actual_length - m25p_cmdsz(nor) - dummy;
> +	ret = m.actual_length - cmd_sz;
>  	if (ret < 0)
>  		return -EIO;
>  	return ret;
> @@ -223,11 +274,20 @@ static int m25p_probe(struct spi_device *spi)
>  	spi_set_drvdata(spi, flash);
>  	flash->spi = spi;
>  
> -	if (spi->mode & SPI_RX_QUAD)
> +	if (spi->mode & SPI_RX_QUAD) {
>  		hwcaps.mask |= SNOR_HWCAPS_READ_1_1_4;
> -	else if (spi->mode & SPI_RX_DUAL)
> +
> +		if (spi->mode & SPI_TX_QUAD)
> +			hwcaps.mask |= (SNOR_HWCAPS_READ_1_4_4 |
> +					SNOR_HWCAPS_PP_1_1_4 |
> +					SNOR_HWCAPS_PP_1_4_4);
> +	} else if (spi->mode & SPI_RX_DUAL) {
>  		hwcaps.mask |= SNOR_HWCAPS_READ_1_1_2;
>  
> +		if (spi->mode & SPI_TX_DUAL)
> +			hwcaps.mask |= SNOR_HWCAPS_READ_1_2_2;
> +	}
> +
>  	if (data && data->name)
>  		nor->mtd.name = data->name;
>  
> 


-- 
Best regards,
Marek Vasut

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

* Re: [PATCH v5 2/6] mtd: m25p80: add support of SPI 1-2-2 and 1-4-4 protocols
  2017-04-06 23:37   ` Marek Vasut
@ 2017-04-09 19:37     ` Cyrille Pitchen
  2017-04-09 20:46       ` Marek Vasut
  0 siblings, 1 reply; 27+ messages in thread
From: Cyrille Pitchen @ 2017-04-09 19:37 UTC (permalink / raw)
  To: Marek Vasut, Cyrille Pitchen, linux-mtd, jartur, kdasu.kdev,
	mar.krzeminski
  Cc: boris.brezillon, richard, nicolas.ferre, linux-kernel,
	computersforpeace, dwmw2

Hi Marek,

Le 07/04/2017 à 01:37, Marek Vasut a écrit :
> On 03/23/2017 12:33 AM, Cyrille Pitchen wrote:
>> Before this patch, m25p80_read() supported few SPI protocols:
>> - regular SPI 1-1-1
>> - SPI Dual Output 1-1-2
>> - SPI Quad Output 1-1-4
>> On the other hand, m25p80_write() only supported SPI 1-1-1.
>>
>> This patch updates both m25p80_read() and m25p80_write() functions to let
>> them support SPI 1-2-2 and SPI 1-4-4 protocols for Fast Read and Page
>> Program SPI commands.
>>
>> It adopts a conservative approach to avoid regressions. Hence the new
>                                              ^ FYI, regression != bug
> 
>> implementations try to be as close as possible to the old implementations,
>> so the main differences are:
>> - the tx_nbits values now being set properly for the spi_transfer
>>   structures carrying the (op code + address/dummy) bytes
>> - and the spi_transfer structure being split into 2 spi_transfer
>>   structures when the numbers of I/O lines are different for op code and
>>   for address/dummy byte transfers on the SPI bus.
>>
>> Besides, the current spi-nor framework supports neither the SPI 2-2-2 nor
>> the SPI 4-4-4 protocols. So, for now, we don't need to update the
>> m25p80_{read|write}_reg() functions as SPI 1-1-1 is the only one possible
>> protocol.
>>
>> Signed-off-by: Cyrille Pitchen <cyrille.pitchen@atmel.com>
>> ---
>>  drivers/mtd/devices/m25p80.c | 120 ++++++++++++++++++++++++++++++++-----------
>>  1 file changed, 90 insertions(+), 30 deletions(-)
>>
>> diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c
>> index 68986a26c8fe..64d562efc25d 100644
>> --- a/drivers/mtd/devices/m25p80.c
>> +++ b/drivers/mtd/devices/m25p80.c
>> @@ -34,6 +34,19 @@ struct m25p {
>>  	u8			command[MAX_CMD_SIZE];
>>  };
>>  
>> +static inline void m25p80_proto2nbits(enum spi_nor_protocol proto,
>> +				      unsigned int *inst_nbits,
>> +				      unsigned int *addr_nbits,
>> +				      unsigned int *data_nbits)
>> +{
> 
> Why don't we just have some generic macros to extract the number of bits
> from $proto ?
>

from Documentation/process/coding-style.rst:
"Generally, inline functions are preferable to macros resembling functions."

inline functions provide better type checking of their arguments and/or
returned value than macros.

Type checking is also the reason I have chosen to create the 'enum
spi_nor_protocol' rather than using constant macros.

>> +	if (inst_nbits)
>> +		*inst_nbits = spi_nor_get_protocol_inst_width(proto);
>> +	if (addr_nbits)
>> +		*addr_nbits = spi_nor_get_protocol_addr_width(proto);
>> +	if (data_nbits)
>> +		*data_nbits = spi_nor_get_protocol_data_width(proto);
>> +}
>> +
>>  static int m25p80_read_reg(struct spi_nor *nor, u8 code, u8 *val, int len)
>>  {
>>  	struct m25p *flash = nor->priv;
>> @@ -78,11 +91,16 @@ static ssize_t m25p80_write(struct spi_nor *nor, loff_t to, size_t len,
>>  {
>>  	struct m25p *flash = nor->priv;
>>  	struct spi_device *spi = flash->spi;
>> -	struct spi_transfer t[2] = {};
>> +	unsigned int inst_nbits, addr_nbits, data_nbits, data_idx;
>> +	struct spi_transfer t[3] = {};
>>  	struct spi_message m;
>>  	int cmd_sz = m25p_cmdsz(nor);
>>  	ssize_t ret;
>>  
>> +	/* get transfer protocols. */
>> +	m25p80_proto2nbits(nor->write_proto, &inst_nbits,
>> +			   &addr_nbits, &data_nbits);
>> +
>>  	spi_message_init(&m);
>>  
>>  	if (nor->program_opcode == SPINOR_OP_AAI_WP && nor->sst_write_second)
>> @@ -92,12 +110,27 @@ static ssize_t m25p80_write(struct spi_nor *nor, loff_t to, size_t len,
>>  	m25p_addr2cmd(nor, to, flash->command);
>>  
>>  	t[0].tx_buf = flash->command;
>> +	t[0].tx_nbits = inst_nbits;
>>  	t[0].len = cmd_sz;
>>  	spi_message_add_tail(&t[0], &m);
>>  
>> -	t[1].tx_buf = buf;
>> -	t[1].len = len;
>> -	spi_message_add_tail(&t[1], &m);
>> +	/* split the op code and address bytes into two transfers if needed. */
>> +	data_idx = 1;
>> +	if (addr_nbits != inst_nbits) {
>> +		t[0].len = 1;
>> +
>> +		t[1].tx_buf = &flash->command[1];
>> +		t[1].tx_nbits = addr_nbits;
>> +		t[1].len = cmd_sz - 1;
>> +		spi_message_add_tail(&t[1], &m);
>> +
>> +		data_idx = 2;
>> +	}
>> +
>> +	t[data_idx].tx_buf = buf;
>> +	t[data_idx].tx_nbits = data_nbits;
>> +	t[data_idx].len = len;
>> +	spi_message_add_tail(&t[data_idx], &m);
>>  
>>  	ret = spi_sync(spi, &m);
>>  	if (ret)
>> @@ -109,18 +142,6 @@ static ssize_t m25p80_write(struct spi_nor *nor, loff_t to, size_t len,
>>  	return ret;
>>  }
>>  
>> -static inline unsigned int m25p80_rx_nbits(struct spi_nor *nor)
>> -{
>> -	switch (nor->read_proto) {
>> -	case SNOR_PROTO_1_1_2:
>> -		return 2;
>> -	case SNOR_PROTO_1_1_4:
>> -		return 4;
>> -	default:
>> -		return 0;
>> -	}
>> -}
>> -
>>  /*
>>   * Read an address range from the nor chip.  The address range
>>   * may be any size provided it is within the physical boundaries.
>> @@ -130,13 +151,19 @@ static ssize_t m25p80_read(struct spi_nor *nor, loff_t from, size_t len,
>>  {
>>  	struct m25p *flash = nor->priv;
>>  	struct spi_device *spi = flash->spi;
>> -	struct spi_transfer t[2];
>> +	unsigned int inst_nbits, addr_nbits, data_nbits, data_idx;
>> +	struct spi_transfer t[3];
>>  	struct spi_message m;
>>  	unsigned int dummy = nor->read_dummy;
>>  	ssize_t ret;
>> +	int cmd_sz;
>> +
>> +	/* get transfer protocols. */
>> +	m25p80_proto2nbits(nor->read_proto, &inst_nbits,
>> +			   &addr_nbits, &data_nbits);
>>  
>>  	/* convert the dummy cycles to the number of bytes */
>> -	dummy /= 8;
>> +	dummy = (dummy * addr_nbits) / 8;
>>  
>>  	if (spi_flash_read_supported(spi)) {
>>  		struct spi_flash_read_message msg;
>> @@ -149,10 +176,9 @@ static ssize_t m25p80_read(struct spi_nor *nor, loff_t from, size_t len,
>>  		msg.read_opcode = nor->read_opcode;
>>  		msg.addr_width = nor->addr_width;
>>  		msg.dummy_bytes = dummy;
>> -		/* TODO: Support other combinations */
>> -		msg.opcode_nbits = SPI_NBITS_SINGLE;
>> -		msg.addr_nbits = SPI_NBITS_SINGLE;
>> -		msg.data_nbits = m25p80_rx_nbits(nor);
>> +		msg.opcode_nbits = inst_nbits;
>> +		msg.addr_nbits = addr_nbits;
>> +		msg.data_nbits = data_nbits;
>>  
>>  		ret = spi_flash_read(spi, &msg);
>>  		if (ret < 0)
>> @@ -167,20 +193,45 @@ static ssize_t m25p80_read(struct spi_nor *nor, loff_t from, size_t len,
>>  	m25p_addr2cmd(nor, from, flash->command);
>>  
>>  	t[0].tx_buf = flash->command;
>> +	t[0].tx_nbits = inst_nbits;
>>  	t[0].len = m25p_cmdsz(nor) + dummy;
>>  	spi_message_add_tail(&t[0], &m);
>>  
>> -	t[1].rx_buf = buf;
>> -	t[1].rx_nbits = m25p80_rx_nbits(nor);
>> -	t[1].len = min3(len, spi_max_transfer_size(spi),
>> -			spi_max_message_size(spi) - t[0].len);
>> -	spi_message_add_tail(&t[1], &m);
>> +	/*
>> +	 * Set all dummy/mode cycle bits to avoid sending some manufacturer
>> +	 * specific pattern, which might make the memory enter its Continuous
>> +	 * Read mode by mistake.
>> +	 * Based on the different mode cycle bit patterns listed and described
>> +	 * in the JESD216B speficication, the 0xff value works for all memories
>                            ^
>                            specification, typo
> 

good catch :)

Best regards,

Cyrille

>> +	 * and all manufacturers.
>> +	 */
>> +	cmd_sz = t[0].len;
>> +	memset(flash->command + cmd_sz - dummy, 0xff, dummy);
>> +
>> +	/* split the op code and address bytes into two transfers if needed. */
>> +	data_idx = 1;
>> +	if (addr_nbits != inst_nbits) {
>> +		t[0].len = 1;
>> +
>> +		t[1].tx_buf = &flash->command[1];
>> +		t[1].tx_nbits = addr_nbits;
>> +		t[1].len = cmd_sz - 1;
>> +		spi_message_add_tail(&t[1], &m);
>> +
>> +		data_idx = 2;
>> +	}
>> +
>> +	t[data_idx].rx_buf = buf;
>> +	t[data_idx].rx_nbits = data_nbits;
>> +	t[data_idx].len = min3(len, spi_max_transfer_size(spi),
>> +			       spi_max_message_size(spi) - cmd_sz);
>> +	spi_message_add_tail(&t[data_idx], &m);
>>  
>>  	ret = spi_sync(spi, &m);
>>  	if (ret)
>>  		return ret;
>>  
>> -	ret = m.actual_length - m25p_cmdsz(nor) - dummy;
>> +	ret = m.actual_length - cmd_sz;
>>  	if (ret < 0)
>>  		return -EIO;
>>  	return ret;
>> @@ -223,11 +274,20 @@ static int m25p_probe(struct spi_device *spi)
>>  	spi_set_drvdata(spi, flash);
>>  	flash->spi = spi;
>>  
>> -	if (spi->mode & SPI_RX_QUAD)
>> +	if (spi->mode & SPI_RX_QUAD) {
>>  		hwcaps.mask |= SNOR_HWCAPS_READ_1_1_4;
>> -	else if (spi->mode & SPI_RX_DUAL)
>> +
>> +		if (spi->mode & SPI_TX_QUAD)
>> +			hwcaps.mask |= (SNOR_HWCAPS_READ_1_4_4 |
>> +					SNOR_HWCAPS_PP_1_1_4 |
>> +					SNOR_HWCAPS_PP_1_4_4);
>> +	} else if (spi->mode & SPI_RX_DUAL) {
>>  		hwcaps.mask |= SNOR_HWCAPS_READ_1_1_2;
>>  
>> +		if (spi->mode & SPI_TX_DUAL)
>> +			hwcaps.mask |= SNOR_HWCAPS_READ_1_2_2;
>> +	}
>> +
>>  	if (data && data->name)
>>  		nor->mtd.name = data->name;
>>  
>>
> 
> 

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

* Re: [PATCH v5 2/6] mtd: m25p80: add support of SPI 1-2-2 and 1-4-4 protocols
  2017-04-09 19:37     ` Cyrille Pitchen
@ 2017-04-09 20:46       ` Marek Vasut
  2017-04-09 21:30         ` Cyrille Pitchen
  0 siblings, 1 reply; 27+ messages in thread
From: Marek Vasut @ 2017-04-09 20:46 UTC (permalink / raw)
  To: Cyrille Pitchen, Cyrille Pitchen, linux-mtd, jartur, kdasu.kdev,
	mar.krzeminski
  Cc: boris.brezillon, richard, nicolas.ferre, linux-kernel,
	computersforpeace, dwmw2

On 04/09/2017 09:37 PM, Cyrille Pitchen wrote:
> Hi Marek,
> 
> Le 07/04/2017 à 01:37, Marek Vasut a écrit :
>> On 03/23/2017 12:33 AM, Cyrille Pitchen wrote:
>>> Before this patch, m25p80_read() supported few SPI protocols:
>>> - regular SPI 1-1-1
>>> - SPI Dual Output 1-1-2
>>> - SPI Quad Output 1-1-4
>>> On the other hand, m25p80_write() only supported SPI 1-1-1.
>>>
>>> This patch updates both m25p80_read() and m25p80_write() functions to let
>>> them support SPI 1-2-2 and SPI 1-4-4 protocols for Fast Read and Page
>>> Program SPI commands.
>>>
>>> It adopts a conservative approach to avoid regressions. Hence the new
>>                                              ^ FYI, regression != bug
>>
>>> implementations try to be as close as possible to the old implementations,
>>> so the main differences are:
>>> - the tx_nbits values now being set properly for the spi_transfer
>>>   structures carrying the (op code + address/dummy) bytes
>>> - and the spi_transfer structure being split into 2 spi_transfer
>>>   structures when the numbers of I/O lines are different for op code and
>>>   for address/dummy byte transfers on the SPI bus.
>>>
>>> Besides, the current spi-nor framework supports neither the SPI 2-2-2 nor
>>> the SPI 4-4-4 protocols. So, for now, we don't need to update the
>>> m25p80_{read|write}_reg() functions as SPI 1-1-1 is the only one possible
>>> protocol.
>>>
>>> Signed-off-by: Cyrille Pitchen <cyrille.pitchen@atmel.com>
>>> ---
>>>  drivers/mtd/devices/m25p80.c | 120 ++++++++++++++++++++++++++++++++-----------
>>>  1 file changed, 90 insertions(+), 30 deletions(-)
>>>
>>> diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c
>>> index 68986a26c8fe..64d562efc25d 100644
>>> --- a/drivers/mtd/devices/m25p80.c
>>> +++ b/drivers/mtd/devices/m25p80.c
>>> @@ -34,6 +34,19 @@ struct m25p {
>>>  	u8			command[MAX_CMD_SIZE];
>>>  };
>>>  
>>> +static inline void m25p80_proto2nbits(enum spi_nor_protocol proto,
>>> +				      unsigned int *inst_nbits,
>>> +				      unsigned int *addr_nbits,
>>> +				      unsigned int *data_nbits)
>>> +{
>>
>> Why don't we just have some generic macros to extract the number of bits
>> from $proto ?
>>
> 
> from Documentation/process/coding-style.rst:
> "Generally, inline functions are preferable to macros resembling functions."
> 
> inline functions provide better type checking of their arguments and/or
> returned value than macros.
> 
> Type checking is also the reason I have chosen to create the 'enum
> spi_nor_protocol' rather than using constant macros.

That part I get (no, not really [1], inline is compiler _hint_ and for
static function, the compiler is smart enough to figure out it should
inline it, so drop it. Also cf. __always_inline).

What I don't quite get is why don't we just encode the proto as ie.

#define PROTO_1_1_4 0x00010204 /* (== BIT(16) | BIT(8) | BIT(2)) */

in which case this whole function would turn into constant-time

return (proto >> (n * 8)) & 0xff;

where n is 0 for data, 1 for address , 2 for command .

[1] https://lwn.net/Articles/166172/

>>> +	if (inst_nbits)
>>> +		*inst_nbits = spi_nor_get_protocol_inst_width(proto);
>>> +	if (addr_nbits)
>>> +		*addr_nbits = spi_nor_get_protocol_addr_width(proto);
>>> +	if (data_nbits)
>>> +		*data_nbits = spi_nor_get_protocol_data_width(proto);
>>> +}
>>> +
[...]

-- 
Best regards,
Marek Vasut

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

* Re: [PATCH v5 1/6] mtd: spi-nor: introduce more SPI protocols and the Dual Transfer Mode
  2017-04-06 23:30   ` Marek Vasut
@ 2017-04-09 21:16     ` Cyrille Pitchen
  2017-04-09 21:40       ` Marek Vasut
  0 siblings, 1 reply; 27+ messages in thread
From: Cyrille Pitchen @ 2017-04-09 21:16 UTC (permalink / raw)
  To: Marek Vasut, Cyrille Pitchen, linux-mtd, jartur, kdasu.kdev,
	mar.krzeminski
  Cc: boris.brezillon, richard, nicolas.ferre, linux-kernel,
	computersforpeace, dwmw2

Hi Marek,

thanks for the review.

my comments below:

Le 07/04/2017 à 01:30, Marek Vasut a écrit :
> On 03/23/2017 12:33 AM, Cyrille Pitchen wrote:
>> This patch changes the prototype of spi_nor_scan(): its 3rd parameter
>> is replaced by a 'struct spi_nor_hwcaps' pointer, which tells the spi-nor
>> framework about the actual hardware capabilities supported by the SPI
>> controller and its driver.
>>
>> Besides, this patch also introduces a new 'struct spi_nor_flash_parameter'
>> telling the spi-nor framework about the hardware capabilities supported by
>> the SPI flash memory and the associated settings required to use those
>> hardware caps.
>>
>> Currently the 'struct spi_nor_flash_parameter' is filled with legacy
>> values but a later patch will allow to fill it dynamically by reading the
>> JESD216 Serial Flash Discoverable Parameter (SFDP) tables from the SPI
>> memory.
>>
>> With both structures, the spi-nor framework can now compute the best
>> match between hardware caps supported by both the (Q)SPI memory and
>> controller hence selecting the relevant SPI protocols and op codes for
>> (Fast) Read, Page Program and Sector Erase operations.
>>
>> The 'struct spi_nor_flash_parameter' also provides the spi-nor framework
>> with the number of dummy cycles to be used with each Fast Read commands
>> and the erase block size associated to the erase block op codes.
>>
>> Finally the 'struct spi_nor_flash_parameter', through the optional
>> .enable_quad_io() hook, tells the spi-nor framework how to set the Quad
>> Enable (QE) bit of the QSPI memory to enable its Quad SPI features.
>>
>> Signed-off-by: Cyrille Pitchen <cyrille.pitchen@atmel.com>
>> ---
>>  drivers/mtd/devices/m25p80.c          |  16 +-
>>  drivers/mtd/spi-nor/aspeed-smc.c      |  23 +-
>>  drivers/mtd/spi-nor/atmel-quadspi.c   |  80 +++---
>>  drivers/mtd/spi-nor/cadence-quadspi.c |  18 +-
>>  drivers/mtd/spi-nor/fsl-quadspi.c     |   8 +-
>>  drivers/mtd/spi-nor/hisi-sfc.c        |  31 ++-
>>  drivers/mtd/spi-nor/intel-spi.c       |   7 +-
>>  drivers/mtd/spi-nor/mtk-quadspi.c     |  16 +-
>>  drivers/mtd/spi-nor/nxp-spifi.c       |  22 +-
>>  drivers/mtd/spi-nor/spi-nor.c         | 441 +++++++++++++++++++++++++++-------
>>  include/linux/mtd/spi-nor.h           | 158 +++++++++++-
>>  11 files changed, 643 insertions(+), 177 deletions(-)
>>
>> diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c
>> index c4df3b1bded0..68986a26c8fe 100644
>> --- a/drivers/mtd/devices/m25p80.c
>> +++ b/drivers/mtd/devices/m25p80.c
>> @@ -111,10 +111,10 @@ static ssize_t m25p80_write(struct spi_nor *nor, loff_t to, size_t len,
>>  
>>  static inline unsigned int m25p80_rx_nbits(struct spi_nor *nor)
>>  {
>> -	switch (nor->flash_read) {
>> -	case SPI_NOR_DUAL:
>> +	switch (nor->read_proto) {
>> +	case SNOR_PROTO_1_1_2:
>>  		return 2;
>> -	case SPI_NOR_QUAD:
>> +	case SNOR_PROTO_1_1_4:
>>  		return 4;
>>  	default:
>>  		return 0;
>> @@ -196,7 +196,9 @@ static int m25p_probe(struct spi_device *spi)
>>  	struct flash_platform_data	*data;
>>  	struct m25p *flash;
>>  	struct spi_nor *nor;
>> -	enum read_mode mode = SPI_NOR_NORMAL;
>> +	struct spi_nor_hwcaps hwcaps = {
>> +		.mask = (SNOR_HWCAPS_READ | SNOR_HWCAPS_PP),
> 
> Drop the unneeded parenthesis.
> 
>> +	};
>>  	char *flash_name;
>>  	int ret;
>>  
>> @@ -222,9 +224,9 @@ static int m25p_probe(struct spi_device *spi)
>>  	flash->spi = spi;
>>  
>>  	if (spi->mode & SPI_RX_QUAD)
>> -		mode = SPI_NOR_QUAD;
>> +		hwcaps.mask |= SNOR_HWCAPS_READ_1_1_4;
>>  	else if (spi->mode & SPI_RX_DUAL)
>> -		mode = SPI_NOR_DUAL;
>> +		hwcaps.mask |= SNOR_HWCAPS_READ_1_1_2;
>>  
>>  	if (data && data->name)
>>  		nor->mtd.name = data->name;
>> @@ -241,7 +243,7 @@ static int m25p_probe(struct spi_device *spi)
>>  	else
>>  		flash_name = spi->modalias;
>>  
>> -	ret = spi_nor_scan(nor, flash_name, mode);
>> +	ret = spi_nor_scan(nor, flash_name, &hwcaps);
>>  	if (ret)
>>  		return ret;
>>  
>> diff --git a/drivers/mtd/spi-nor/aspeed-smc.c b/drivers/mtd/spi-nor/aspeed-smc.c
>> index 56051d30f000..723026d9cf0c 100644
>> --- a/drivers/mtd/spi-nor/aspeed-smc.c
>> +++ b/drivers/mtd/spi-nor/aspeed-smc.c
>> @@ -585,14 +585,12 @@ static int aspeed_smc_chip_setup_finish(struct aspeed_smc_chip *chip)
>>  	 * TODO: Adjust clocks if fast read is supported and interpret
>>  	 * SPI-NOR flags to adjust controller settings.
>>  	 */
>> -	switch (chip->nor.flash_read) {
>> -	case SPI_NOR_NORMAL:
>> -		cmd = CONTROL_COMMAND_MODE_NORMAL;
>> -		break;
>> -	case SPI_NOR_FAST:
>> -		cmd = CONTROL_COMMAND_MODE_FREAD;
>> -		break;
>> -	default:
>> +	if (chip->nor.read_proto == SNOR_PROTO_1_1_1) {
>> +		if (chip->nor.read_dummy == 0)
>> +			cmd = CONTROL_COMMAND_MODE_NORMAL;
>> +		else
>> +			cmd = CONTROL_COMMAND_MODE_FREAD;
>> +	} else {
>>  		dev_err(chip->nor.dev, "unsupported SPI read mode\n");
>>  		return -EINVAL;
>>  	}
>> @@ -608,6 +606,11 @@ static int aspeed_smc_chip_setup_finish(struct aspeed_smc_chip *chip)
>>  static int aspeed_smc_setup_flash(struct aspeed_smc_controller *controller,
>>  				  struct device_node *np, struct resource *r)
>>  {
>> +	struct spi_nor_hwcaps hwcaps = {
>> +		.mask = (SNOR_HWCAPS_READ |
>> +			 SNOR_HWCAPS_READ_FAST |
>> +			 SNOR_HWCAPS_PP),
> 
> Drop the extra parenthesis ... shouldn't the structure be const ?
> 
>> +	};
>>  	const struct aspeed_smc_info *info = controller->info;
>>  	struct device *dev = controller->dev;
>>  	struct device_node *child;
>> @@ -671,11 +674,11 @@ static int aspeed_smc_setup_flash(struct aspeed_smc_controller *controller,
>>  			break;
>>  
>>  		/*
>> -		 * TODO: Add support for SPI_NOR_QUAD and SPI_NOR_DUAL
>> +		 * TODO: Add support for Dual and Quad SPI protocols
>>  		 * attach when board support is present as determined
>>  		 * by of property.
>>  		 */
>> -		ret = spi_nor_scan(nor, NULL, SPI_NOR_NORMAL);
>> +		ret = spi_nor_scan(nor, NULL, &hwcaps);
>>  		if (ret)
>>  			break;
> 
> 
> [...]
> 
>> +struct spi_nor_flash_parameter {
>> +	u64				size;
>> +	u32				page_size;
>> +
>> +	struct spi_nor_hwcaps		hwcaps;
>> +	struct spi_nor_read_command	reads[SNOR_CMD_READ_MAX];
>> +	struct spi_nor_pp_command	page_programs[SNOR_CMD_PP_MAX];
>> +
>> +	int (*quad_enable)(struct spi_nor *nor);
> 
> This callback should be added by a separate patch, there's WAY too much
> crap in this patch.
>

The manufacturer/flash specific quad_enable() handler sets the Quad
Enable (QE) bit in some internal status register of the SPI flash.
The QE bit may not exist for some manufacturers (actually only Micron
AFAIK) but when it does, we must set this bit before sending any flash
command using any Quad SPI protocol.

So my point is that the use of the quad_enable() handler is tightly
bound up with the possibility to use more (Quad) SPI protocols, which is
the purpose of this patch.

>From the JESD216B (SFDP) specification, the Quad Enable Requirement
(QER) is part of the Basic Flash Parameter Table (BFPT). QER describes
the procedure to set the QE bit. So this notion is translated into the
quad_enable() handler being a member of the 'struct
spi_nor_flash_parameter'.

The 'struct spi_nor_flash_parameter' is initialized in
spi_nor_init_params(). This includes the (Fast) Read and Page Programs
settings, which are also provided by the BFPT, as well as the choice of
the right quad_enable() handler.
Then spi_nor_setup() selects the right settings and calls the
quad_enable() handler, if needed: that is to say if any Quad SPI
protocol was selected for Fast Read or Page Program operation.

Then if you don't mind, I'd rather keep the quad_enable() handler within
this patch. IMHO, it makes more sense.


>> +};
>> +
>> +
>> +static inline 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)
>> +{
>> +	read->num_mode_clocks = num_mode_clocks;
>> +	read->num_wait_states = num_wait_states;
>> +	read->opcode = opcode;
>> +	read->proto = proto;
>> +}
>> +
>> +static inline void
> 
> Drop the inline , the compiler will decide. Fix globally.
> 
>> +spi_nor_set_pp_settings(struct spi_nor_pp_command *pp,
>> +			u8 opcode,
>> +			enum spi_nor_protocol proto)
>> +{
>> +	pp->opcode = opcode;
>> +	pp->proto = proto;
>> +}
>> +
>> +static int spi_nor_init_params(struct spi_nor *nor,
>> +			       const struct flash_info *info,
>> +			       struct spi_nor_flash_parameter *params)
>> +{
>> +	/* Set legacy flash parameters as default. */
>> +	memset(params, 0, sizeof(*params));
>> +
>> +	/* Set SPI NOR sizes. */
>> +	params->size = info->sector_size * info->n_sectors;
>> +	params->page_size = info->page_size;
>> +
>> +	/* (Fast) Read settings. */
>> +	params->hwcaps.mask |= SNOR_HWCAPS_READ;
>> +	spi_nor_set_read_settings(&params->reads[SNOR_CMD_READ],
>> +				  0, 0, SPINOR_OP_READ,
>> +				  SNOR_PROTO_1_1_1);
> 
> Newline
> 
>> +	if (!(info->flags & SPI_NOR_NO_FR)) {
>> +		params->hwcaps.mask |= SNOR_HWCAPS_READ_FAST;
>> +		spi_nor_set_read_settings(&params->reads[SNOR_CMD_READ_FAST],
>> +					  0, 8, SPINOR_OP_READ_FAST,
>> +					  SNOR_PROTO_1_1_1);
>> +	}
> 
> Newline
> 
>> +	if (info->flags & SPI_NOR_DUAL_READ) {
>> +		params->hwcaps.mask |= SNOR_HWCAPS_READ_1_1_2;
>> +		spi_nor_set_read_settings(&params->reads[SNOR_CMD_READ_1_1_2],
>> +					  0, 8, SPINOR_OP_READ_1_1_2,
>> +					  SNOR_PROTO_1_1_2);
>> +	}
> 
> Newline ... this is really hard to read as it is.
> 
>> +	if (info->flags & SPI_NOR_QUAD_READ) {
>> +		params->hwcaps.mask |= SNOR_HWCAPS_READ_1_1_4;
>> +		spi_nor_set_read_settings(&params->reads[SNOR_CMD_READ_1_1_4],
>> +					  0, 8, SPINOR_OP_READ_1_1_4,
>> +					  SNOR_PROTO_1_1_4);
>> +	}
>> +
>> +	/* 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);
>> +
>> +	/* Select the procedure to set the Quad Enable bit. */
>> +	if (params->hwcaps.mask & (SNOR_HWCAPS_READ_QUAD |
>> +				   SNOR_HWCAPS_PP_QUAD)) {
>> +		switch (JEDEC_MFR(info)) {
>> +		case SNOR_MFR_MACRONIX:
>> +			params->quad_enable = macronix_quad_enable;
>> +			break;
>> +
>> +		case SNOR_MFR_MICRON:
>> +			break;
>> +
>> +		default:
> 
> Are you sure this is a good idea ?

This is exactly what was done before this patch. Please have a look at
the former set_quad_mode() function.

Is it a good idea to choose spansion_quad_enable() also for default
case? I agree with you, it is not.

However this patch should be seen as a base for further patches fixing
step by step the many pending known issues. Currently, I'm focusing on a
smooth transition in changing the 3rd argument of spi_nor_scan().
Everything is expected to work just as before.

About the quad_enable() handler to be chosen for Spansion and other
manufacturer SPI flash memories:
Even when regarding Spansion memories only, the procedure to set the QE
bit has evolved based on whether or not the memory part supports the op
code to read the Control Register 1 / Status Register 2.

If supported, a new procedure is described in the JESD216B
specification. The new procedure is more efficient and reliable than the
old one, ie spansion_quad_enable().

This issue is fixed by patch 5 of this series.


> 
>> +			params->quad_enable = spansion_quad_enable;
>> +			break;
>> +		}
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +static int spi_nor_hwcaps2cmd(u32 hwcaps)
> 
> const u32 hwcaps ...
> 
>>  {
>> +	switch (hwcaps) {
>> +	case SNOR_HWCAPS_READ:			return SNOR_CMD_READ;
> 
> You can do this as a table lookup or array indexing and it would be
> checkpatch clean.
>

This patch has already passed the checkpatch test.

>> +	case SNOR_HWCAPS_READ_FAST:		return SNOR_CMD_READ_FAST;
>> +	case SNOR_HWCAPS_READ_1_1_1_DTR:	return SNOR_CMD_READ_1_1_1_DTR;
>> +	case SNOR_HWCAPS_READ_1_1_2:		return SNOR_CMD_READ_1_1_2;
>> +	case SNOR_HWCAPS_READ_1_2_2:		return SNOR_CMD_READ_1_2_2;
>> +	case SNOR_HWCAPS_READ_2_2_2:		return SNOR_CMD_READ_2_2_2;
>> +	case SNOR_HWCAPS_READ_1_2_2_DTR:	return SNOR_CMD_READ_1_2_2_DTR;
>> +	case SNOR_HWCAPS_READ_1_1_4:		return SNOR_CMD_READ_1_1_4;
>> +	case SNOR_HWCAPS_READ_1_4_4:		return SNOR_CMD_READ_1_4_4;
>> +	case SNOR_HWCAPS_READ_4_4_4:		return SNOR_CMD_READ_4_4_4;
>> +	case SNOR_HWCAPS_READ_1_4_4_DTR:	return SNOR_CMD_READ_1_4_4_DTR;
>> +	case SNOR_HWCAPS_READ_1_1_8:		return SNOR_CMD_READ_1_1_8;
>> +	case SNOR_HWCAPS_READ_1_8_8:		return SNOR_CMD_READ_1_8_8;
>> +	case SNOR_HWCAPS_READ_8_8_8:		return SNOR_CMD_READ_8_8_8;
>> +	case SNOR_HWCAPS_READ_1_8_8_DTR:	return SNOR_CMD_READ_1_8_8_DTR;
>> +
>> +	case SNOR_HWCAPS_PP:			return SNOR_CMD_PP;
>> +	case SNOR_HWCAPS_PP_1_1_4:		return SNOR_CMD_PP_1_1_4;
>> +	case SNOR_HWCAPS_PP_1_4_4:		return SNOR_CMD_PP_1_4_4;
>> +	case SNOR_HWCAPS_PP_4_4_4:		return SNOR_CMD_PP_4_4_4;
>> +	case SNOR_HWCAPS_PP_1_1_8:		return SNOR_CMD_PP_1_1_8;
>> +	case SNOR_HWCAPS_PP_1_8_8:		return SNOR_CMD_PP_1_8_8;
>> +	case SNOR_HWCAPS_PP_8_8_8:		return SNOR_CMD_PP_8_8_8;
>> +	}
>> +
>> +	return -EINVAL;
>> +}
>> +
>> +static int spi_nor_select_read(struct spi_nor *nor,
>> +			       const struct spi_nor_flash_parameter *params,
>> +			       u32 shared_hwcaps)
>> +{
>> +	int cmd, best_match = fls(shared_hwcaps & SNOR_HWCAPS_READ_MASK) - 1;
>> +	const struct spi_nor_read_command *read;
>> +
>> +	if (best_match < 0)
>> +		return -EINVAL;
>> +
>> +	cmd = spi_nor_hwcaps2cmd(BIT(best_match));
> 
> How does this work? Do we assume that hwcaps2cmd is always given a value
> with 1-bit set ? That's quite wasteful IMO.
>

SNOR_HWCAPS_READ* and SNOR_HWCAPS_PP* are all defined with the BIT()
macro since they are used to set the bitmask describing the hardware
capabilities supported by the SPI flash memory and controller.
Each of them can support many SPI commands hence the use of a bitmask.

Then we compute the best match from the hardware capabilities shared by
both the memory and controller. It means we always select a single bit
from the shared_hwcaps bitmask. So to answer your question, indeed,
hwcaps2cmd() is always given a value with a single bit set.


>> +	if (cmd < 0)
>> +		return -EINVAL;
>> +
>> +	read = &params->reads[cmd];
>> +	nor->read_opcode = read->opcode;
>> +	nor->read_proto = read->proto;
>> +
>> +	/*
>> +	 * In the spi-nor framework, we don't need to make the difference
>> +	 * between mode clock cycles and wait state clock cycles.
>> +	 * Indeed, the value of the mode clock cycles is used by a QSPI
>> +	 * flash memory to know whether it should enter or leave its 0-4-4
>> +	 * (Continuous Read / XIP) mode.
> 
> 0-4-4 ?

Some manufacturer datasheets name this mode the "Continuous Read" mode
or the "eXecution In Place" mode but the JESD216B specification calls it
the 0-4-4 mode, just to have a consistent naming with the 4-4-4 mode.

Once in 0-4-4 mode, the SPI flash memory expects later Fast Read
commands to start directly from the address clocks cycles, skipping the
instruction clock cycles, hence the leading 0.

The value of the mode cycles, between the address and wait state cycles,
in the Fast Read x-4-4 command tells the SPI flash memory whether is the
next SPI flash command will be a Fast Read 0-4-4 command or any other
command with instruction clock cycles.

It is common to split SPI flash commands into 3 parts:
instruction | (address;mode;wait states) | data

So 0-4-4 means:
- no instruction clock cycles
- 4 I/O lines being used during address;mode;wait states clock cycles
- 4 I/O lines being used during data clock cycles


> 
>> +	 * eXecution In Place is out of the scope of the mtd sub-system.
>> +	 * Hence we choose to merge both mode and wait state clock cycles
>> +	 * into the so called dummy clock cycles.
>> +	 */
>> +	nor->read_dummy = read->num_mode_clocks + read->num_wait_states;
>> +	return 0;
>> +}
>> +
>> +static int spi_nor_select_pp(struct spi_nor *nor,
>> +			     const struct spi_nor_flash_parameter *params,
>> +			     u32 shared_hwcaps)
>> +{
>> +	int cmd, best_match = fls(shared_hwcaps & SNOR_HWCAPS_PP_MASK) - 1;
>> +	const struct spi_nor_pp_command *pp;
>> +
>> +	if (best_match < 0)
>> +		return -EINVAL;
>> +
>> +	cmd = spi_nor_hwcaps2cmd(BIT(best_match));
>> +	if (cmd < 0)
>> +		return -EINVAL;
>> +
>> +	pp = &params->page_programs[cmd];
>> +	nor->program_opcode = pp->opcode;
>> +	nor->write_proto = pp->proto;
>> +	return 0;
>> +}
>> +
>> +static int spi_nor_select_erase(struct spi_nor *nor,
>> +				const struct flash_info *info)
>> +{
>> +	struct mtd_info *mtd = &nor->mtd;
>> +
>> +#ifdef CONFIG_MTD_SPI_NOR_USE_4K_SECTORS
>> +	/* prefer "small sector" erase if possible */
>> +	if (info->flags & SECT_4K) {
>> +		nor->erase_opcode = SPINOR_OP_BE_4K;
>> +		mtd->erasesize = 4096;
>> +	} else if (info->flags & SECT_4K_PMC) {
>> +		nor->erase_opcode = SPINOR_OP_BE_4K_PMC;
>> +		mtd->erasesize = 4096;
>> +	} else
>> +#endif
>> +	{
>> +		nor->erase_opcode = SPINOR_OP_SE;
>> +		mtd->erasesize = info->sector_size;
>> +	}
>> +	return 0;
>> +}
>> +
>> +static int spi_nor_setup(struct spi_nor *nor, const struct flash_info *info,
>> +			 const struct spi_nor_flash_parameter *params,
>> +			 const struct spi_nor_hwcaps *hwcaps)
>> +{
>> +	u32 ignored_mask, shared_mask;
>> +	bool enable_quad_io;
>> +	int err;
>> +
>> +	/*
>> +	 * Keep only the hardware capabilities supported by both the SPI
>> +	 * controller and the SPI flash memory.
>> +	 */
>> +	shared_mask = hwcaps->mask & params->hwcaps.mask;
>> +
>> +	/* SPI protocol classes N-N-N are not supported yet. */
>> +	ignored_mask = (SNOR_HWCAPS_READ_2_2_2 |
>> +			SNOR_HWCAPS_READ_4_4_4 |
>> +			SNOR_HWCAPS_READ_8_8_8 |
>> +			SNOR_HWCAPS_PP_4_4_4 |
>> +			SNOR_HWCAPS_PP_8_8_8);
>> +	if (shared_mask & ignored_mask) {
>> +		dev_dbg(nor->dev,
>> +			"SPI protocol classes N-N-N are not supported yet.\n");
>> +		shared_mask &= ~ignored_mask;
>> +	}
>> +
>> +	/* Select the (Fast) Read command. */
>> +	err = spi_nor_select_read(nor, params, shared_mask);
>> +	if (err) {
>> +		dev_err(nor->dev, "invalid (fast) read\n");
> 
> What does this information tell me, as an end user ? If I see this error
> message, what sort of conclusion can I derive from it ? I have
> no idea ...
>

I agree, this could be improved.


>> +		return err;
>> +	}
>> +
>> +	/* Select the Page Program command. */
>> +	err = spi_nor_select_pp(nor, params, shared_mask);
>> +	if (err) {
>> +		dev_err(nor->dev, "invalid page program\n");
>> +		return err;
>> +	}
>> +
>> +	/* Select the Sector Erase command. */
>> +	err = spi_nor_select_erase(nor, info);
>> +	if (err) {
>> +		dev_err(nor->dev, "invalid sector/block erase\n");
>> +		return err;
>> +	}
>> +
>> +	/* Enable Quad I/O if needed. */
>> +	enable_quad_io = (spi_nor_get_protocol_width(nor->read_proto) == 4 ||
>> +			  spi_nor_get_protocol_width(nor->write_proto) == 4);
> 
> What if read_proto != write_proto ? Also, this is awful code ... fix it.
>

As explained earlier about the QE bit and the Quad Enable Requirement,
this is indeed exactly what we want: we must set the QE bit, hence call
nor->flash_quad_enable() whenever any Quad SPI protocol is used for any
SPI flash command.

Currently Fast Read and Page Program are the only commands selected by
the spi-nor protocols, which can use one Quad SPI protocol.

So this code works whether of not (read_proto == write_proto).

>> +	if (enable_quad_io && params->quad_enable)
>> +		nor->flash_quad_enable = params->quad_enable;
>> +	else
>> +		nor->flash_quad_enable = NULL;
>> +
>> +	return 0;
>> +}
>> +
>> +int spi_nor_scan(struct spi_nor *nor, const char *name,
>> +		 const struct spi_nor_hwcaps *hwcaps)
>> +{
>> +	struct spi_nor_flash_parameter params;
>>  	const struct flash_info *info = NULL;
>>  	struct device *dev = nor->dev;
>>  	struct mtd_info *mtd = &nor->mtd;
>> @@ -1548,6 +1824,11 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
>>  	if (ret)
>>  		return ret;
>>  
>> +	/* Reset SPI protocol for all commands */
>> +	nor->reg_proto = SNOR_PROTO_1_1_1;
>> +	nor->read_proto = SNOR_PROTO_1_1_1;
>> +	nor->write_proto = SNOR_PROTO_1_1_1;
>> +
>>  	if (name)
>>  		info = spi_nor_match_id(name);
>>  	/* Try to auto-detect if chip name wasn't specified or not found */
> [...]
> 

Best regards,

Cyrille

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

* Re: [PATCH v5 2/6] mtd: m25p80: add support of SPI 1-2-2 and 1-4-4 protocols
  2017-04-09 20:46       ` Marek Vasut
@ 2017-04-09 21:30         ` Cyrille Pitchen
  2017-04-09 21:46           ` Marek Vasut
  0 siblings, 1 reply; 27+ messages in thread
From: Cyrille Pitchen @ 2017-04-09 21:30 UTC (permalink / raw)
  To: Marek Vasut, Cyrille Pitchen, linux-mtd, jartur, kdasu.kdev,
	mar.krzeminski
  Cc: boris.brezillon, richard, nicolas.ferre, linux-kernel,
	computersforpeace, dwmw2

Le 09/04/2017 à 22:46, Marek Vasut a écrit :
> On 04/09/2017 09:37 PM, Cyrille Pitchen wrote:
>> Hi Marek,
>>
>> Le 07/04/2017 à 01:37, Marek Vasut a écrit :
>>> On 03/23/2017 12:33 AM, Cyrille Pitchen wrote:
>>>> Before this patch, m25p80_read() supported few SPI protocols:
>>>> - regular SPI 1-1-1
>>>> - SPI Dual Output 1-1-2
>>>> - SPI Quad Output 1-1-4
>>>> On the other hand, m25p80_write() only supported SPI 1-1-1.
>>>>
>>>> This patch updates both m25p80_read() and m25p80_write() functions to let
>>>> them support SPI 1-2-2 and SPI 1-4-4 protocols for Fast Read and Page
>>>> Program SPI commands.
>>>>
>>>> It adopts a conservative approach to avoid regressions. Hence the new
>>>                                              ^ FYI, regression != bug
>>>
>>>> implementations try to be as close as possible to the old implementations,
>>>> so the main differences are:
>>>> - the tx_nbits values now being set properly for the spi_transfer
>>>>   structures carrying the (op code + address/dummy) bytes
>>>> - and the spi_transfer structure being split into 2 spi_transfer
>>>>   structures when the numbers of I/O lines are different for op code and
>>>>   for address/dummy byte transfers on the SPI bus.
>>>>
>>>> Besides, the current spi-nor framework supports neither the SPI 2-2-2 nor
>>>> the SPI 4-4-4 protocols. So, for now, we don't need to update the
>>>> m25p80_{read|write}_reg() functions as SPI 1-1-1 is the only one possible
>>>> protocol.
>>>>
>>>> Signed-off-by: Cyrille Pitchen <cyrille.pitchen@atmel.com>
>>>> ---
>>>>  drivers/mtd/devices/m25p80.c | 120 ++++++++++++++++++++++++++++++++-----------
>>>>  1 file changed, 90 insertions(+), 30 deletions(-)
>>>>
>>>> diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c
>>>> index 68986a26c8fe..64d562efc25d 100644
>>>> --- a/drivers/mtd/devices/m25p80.c
>>>> +++ b/drivers/mtd/devices/m25p80.c
>>>> @@ -34,6 +34,19 @@ struct m25p {
>>>>  	u8			command[MAX_CMD_SIZE];
>>>>  };
>>>>  
>>>> +static inline void m25p80_proto2nbits(enum spi_nor_protocol proto,
>>>> +				      unsigned int *inst_nbits,
>>>> +				      unsigned int *addr_nbits,
>>>> +				      unsigned int *data_nbits)
>>>> +{
>>>
>>> Why don't we just have some generic macros to extract the number of bits
>>> from $proto ?
>>>
>>
>> from Documentation/process/coding-style.rst:
>> "Generally, inline functions are preferable to macros resembling functions."
>>
>> inline functions provide better type checking of their arguments and/or
>> returned value than macros.
>>
>> Type checking is also the reason I have chosen to create the 'enum
>> spi_nor_protocol' rather than using constant macros.
> 
> That part I get (no, not really [1], inline is compiler _hint_ and for
> static function, the compiler is smart enough to figure out it should
> inline it, so drop it. Also cf. __always_inline).
> 
> What I don't quite get is why don't we just encode the proto as ie.
> 
> #define PROTO_1_1_4 0x00010204 /* (== BIT(16) | BIT(8) | BIT(2)) */
>

This is what I did in former versions of the patch: the scheme you
propose requires more bits to encode the number of I/O lines for
instruction, address and data: there would be less bits available for
future extensions.

Also using the notion of protocol class (1-1-N, 1-N-N, N-N-N) in the
encoding scheme prevents from setting impossible combinations like
4-1-4, 1-2-4, ...


> in which case this whole function would turn into constant-time
> 
> return (proto >> (n * 8)) & 0xff;
> 
> where n is 0 for data, 1 for address , 2 for command .
> 
> [1] https://lwn.net/Articles/166172/
> 
>>>> +	if (inst_nbits)
>>>> +		*inst_nbits = spi_nor_get_protocol_inst_width(proto);
>>>> +	if (addr_nbits)
>>>> +		*addr_nbits = spi_nor_get_protocol_addr_width(proto);
>>>> +	if (data_nbits)
>>>> +		*data_nbits = spi_nor_get_protocol_data_width(proto);
>>>> +}
>>>> +
> [...]
> 

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

* Re: [PATCH v5 1/6] mtd: spi-nor: introduce more SPI protocols and the Dual Transfer Mode
  2017-04-09 21:16     ` Cyrille Pitchen
@ 2017-04-09 21:40       ` Marek Vasut
  0 siblings, 0 replies; 27+ messages in thread
From: Marek Vasut @ 2017-04-09 21:40 UTC (permalink / raw)
  To: Cyrille Pitchen, Cyrille Pitchen, linux-mtd, jartur, kdasu.kdev,
	mar.krzeminski
  Cc: boris.brezillon, richard, nicolas.ferre, linux-kernel,
	computersforpeace, dwmw2

On 04/09/2017 11:16 PM, Cyrille Pitchen wrote:
> Hi Marek,

Hi,

> thanks for the review.

[...]

>>> +struct spi_nor_flash_parameter {
>>> +	u64				size;
>>> +	u32				page_size;
>>> +
>>> +	struct spi_nor_hwcaps		hwcaps;
>>> +	struct spi_nor_read_command	reads[SNOR_CMD_READ_MAX];
>>> +	struct spi_nor_pp_command	page_programs[SNOR_CMD_PP_MAX];
>>> +
>>> +	int (*quad_enable)(struct spi_nor *nor);
>>
>> This callback should be added by a separate patch, there's WAY too much
>> crap in this patch.
>>
> 
> The manufacturer/flash specific quad_enable() handler sets the Quad
> Enable (QE) bit in some internal status register of the SPI flash.
> The QE bit may not exist for some manufacturers (actually only Micron
> AFAIK) but when it does, we must set this bit before sending any flash
> command using any Quad SPI protocol.

How does that matter for Dual Transfer Mode ? It doesn't , so it can be
split away from this patch .

> So my point is that the use of the quad_enable() handler is tightly
> bound up with the possibility to use more (Quad) SPI protocols, which is
> the purpose of this patch.

Which this patch does NOT enable ...

> From the JESD216B (SFDP) specification, the Quad Enable Requirement
> (QER) is part of the Basic Flash Parameter Table (BFPT). QER describes
> the procedure to set the QE bit. So this notion is translated into the
> quad_enable() handler being a member of the 'struct
> spi_nor_flash_parameter'.
> 
> The 'struct spi_nor_flash_parameter' is initialized in
> spi_nor_init_params(). This includes the (Fast) Read and Page Programs
> settings, which are also provided by the BFPT, as well as the choice of
> the right quad_enable() handler.
> Then spi_nor_setup() selects the right settings and calls the
> quad_enable() handler, if needed: that is to say if any Quad SPI
> protocol was selected for Fast Read or Page Program operation.
> 
> Then if you don't mind, I'd rather keep the quad_enable() handler within
> this patch. IMHO, it makes more sense.

[...]

>>> +	if (info->flags & SPI_NOR_QUAD_READ) {
>>> +		params->hwcaps.mask |= SNOR_HWCAPS_READ_1_1_4;
>>> +		spi_nor_set_read_settings(&params->reads[SNOR_CMD_READ_1_1_4],
>>> +					  0, 8, SPINOR_OP_READ_1_1_4,
>>> +					  SNOR_PROTO_1_1_4);
>>> +	}
>>> +
>>> +	/* 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);
>>> +
>>> +	/* Select the procedure to set the Quad Enable bit. */
>>> +	if (params->hwcaps.mask & (SNOR_HWCAPS_READ_QUAD |
>>> +				   SNOR_HWCAPS_PP_QUAD)) {
>>> +		switch (JEDEC_MFR(info)) {
>>> +		case SNOR_MFR_MACRONIX:
>>> +			params->quad_enable = macronix_quad_enable;
>>> +			break;
>>> +
>>> +		case SNOR_MFR_MICRON:
>>> +			break;
>>> +
>>> +		default:
>>
>> Are you sure this is a good idea ?
> 
> This is exactly what was done before this patch. Please have a look at
> the former set_quad_mode() function.
> 
> Is it a good idea to choose spansion_quad_enable() also for default
> case? I agree with you, it is not.

Then why don't we fix that first ?

> However this patch should be seen as a base for further patches fixing
> step by step the many pending known issues. Currently, I'm focusing on a
> smooth transition in changing the 3rd argument of spi_nor_scan().
> Everything is expected to work just as before.

OK

> About the quad_enable() handler to be chosen for Spansion and other
> manufacturer SPI flash memories:
> Even when regarding Spansion memories only, the procedure to set the QE
> bit has evolved based on whether or not the memory part supports the op
> code to read the Control Register 1 / Status Register 2.
> 
> If supported, a new procedure is described in the JESD216B
> specification. The new procedure is more efficient and reliable than the
> old one, ie spansion_quad_enable().
> 
> This issue is fixed by patch 5 of this series.
> 
> 
>>
>>> +			params->quad_enable = spansion_quad_enable;
>>> +			break;
>>> +		}
>>> +	}
>>> +
>>> +	return 0;
>>> +}
>>> +
>>> +static int spi_nor_hwcaps2cmd(u32 hwcaps)
>>
>> const u32 hwcaps ...
>>
>>>  {
>>> +	switch (hwcaps) {
>>> +	case SNOR_HWCAPS_READ:			return SNOR_CMD_READ;
>>
>> You can do this as a table lookup or array indexing and it would be
>> checkpatch clean.
>>
> 
> This patch has already passed the checkpatch test.

This is weird, I wouldn't have expected this to be acceptable syntax.

>>> +	case SNOR_HWCAPS_READ_FAST:		return SNOR_CMD_READ_FAST;
>>> +	case SNOR_HWCAPS_READ_1_1_1_DTR:	return SNOR_CMD_READ_1_1_1_DTR;
>>> +	case SNOR_HWCAPS_READ_1_1_2:		return SNOR_CMD_READ_1_1_2;
>>> +	case SNOR_HWCAPS_READ_1_2_2:		return SNOR_CMD_READ_1_2_2;
>>> +	case SNOR_HWCAPS_READ_2_2_2:		return SNOR_CMD_READ_2_2_2;
>>> +	case SNOR_HWCAPS_READ_1_2_2_DTR:	return SNOR_CMD_READ_1_2_2_DTR;
>>> +	case SNOR_HWCAPS_READ_1_1_4:		return SNOR_CMD_READ_1_1_4;
>>> +	case SNOR_HWCAPS_READ_1_4_4:		return SNOR_CMD_READ_1_4_4;
>>> +	case SNOR_HWCAPS_READ_4_4_4:		return SNOR_CMD_READ_4_4_4;
>>> +	case SNOR_HWCAPS_READ_1_4_4_DTR:	return SNOR_CMD_READ_1_4_4_DTR;
>>> +	case SNOR_HWCAPS_READ_1_1_8:		return SNOR_CMD_READ_1_1_8;
>>> +	case SNOR_HWCAPS_READ_1_8_8:		return SNOR_CMD_READ_1_8_8;
>>> +	case SNOR_HWCAPS_READ_8_8_8:		return SNOR_CMD_READ_8_8_8;
>>> +	case SNOR_HWCAPS_READ_1_8_8_DTR:	return SNOR_CMD_READ_1_8_8_DTR;
>>> +
>>> +	case SNOR_HWCAPS_PP:			return SNOR_CMD_PP;
>>> +	case SNOR_HWCAPS_PP_1_1_4:		return SNOR_CMD_PP_1_1_4;
>>> +	case SNOR_HWCAPS_PP_1_4_4:		return SNOR_CMD_PP_1_4_4;
>>> +	case SNOR_HWCAPS_PP_4_4_4:		return SNOR_CMD_PP_4_4_4;
>>> +	case SNOR_HWCAPS_PP_1_1_8:		return SNOR_CMD_PP_1_1_8;
>>> +	case SNOR_HWCAPS_PP_1_8_8:		return SNOR_CMD_PP_1_8_8;
>>> +	case SNOR_HWCAPS_PP_8_8_8:		return SNOR_CMD_PP_8_8_8;
>>> +	}
>>> +
>>> +	return -EINVAL;
>>> +}
>>> +
>>> +static int spi_nor_select_read(struct spi_nor *nor,
>>> +			       const struct spi_nor_flash_parameter *params,
>>> +			       u32 shared_hwcaps)
>>> +{
>>> +	int cmd, best_match = fls(shared_hwcaps & SNOR_HWCAPS_READ_MASK) - 1;
>>> +	const struct spi_nor_read_command *read;
>>> +
>>> +	if (best_match < 0)
>>> +		return -EINVAL;
>>> +
>>> +	cmd = spi_nor_hwcaps2cmd(BIT(best_match));
>>
>> How does this work? Do we assume that hwcaps2cmd is always given a value
>> with 1-bit set ? That's quite wasteful IMO.
>>
> 
> SNOR_HWCAPS_READ* and SNOR_HWCAPS_PP* are all defined with the BIT()
> macro since they are used to set the bitmask describing the hardware
> capabilities supported by the SPI flash memory and controller.
> Each of them can support many SPI commands hence the use of a bitmask.
> 
> Then we compute the best match from the hardware capabilities shared by
> both the memory and controller. It means we always select a single bit
> from the shared_hwcaps bitmask. So to answer your question, indeed,
> hwcaps2cmd() is always given a value with a single bit set.

Something tells me you might run out of bits very soon here ...

>>> +	if (cmd < 0)
>>> +		return -EINVAL;

return cmd ; ... propagate the errors .

>>> +	read = &params->reads[cmd];
>>> +	nor->read_opcode = read->opcode;
>>> +	nor->read_proto = read->proto;
>>> +
>>> +	/*
>>> +	 * In the spi-nor framework, we don't need to make the difference
>>> +	 * between mode clock cycles and wait state clock cycles.
>>> +	 * Indeed, the value of the mode clock cycles is used by a QSPI
>>> +	 * flash memory to know whether it should enter or leave its 0-4-4
>>> +	 * (Continuous Read / XIP) mode.
>>
>> 0-4-4 ?
> 
> Some manufacturer datasheets name this mode the "Continuous Read" mode
> or the "eXecution In Place" mode but the JESD216B specification calls it
> the 0-4-4 mode, just to have a consistent naming with the 4-4-4 mode.

OK

> Once in 0-4-4 mode, the SPI flash memory expects later Fast Read
> commands to start directly from the address clocks cycles, skipping the
> instruction clock cycles, hence the leading 0.
> 
> The value of the mode cycles, between the address and wait state cycles,
> in the Fast Read x-4-4 command tells the SPI flash memory whether is the
> next SPI flash command will be a Fast Read 0-4-4 command or any other
> command with instruction clock cycles.
> 
> It is common to split SPI flash commands into 3 parts:
> instruction | (address;mode;wait states) | data
> 
> So 0-4-4 means:
> - no instruction clock cycles
> - 4 I/O lines being used during address;mode;wait states clock cycles
> - 4 I/O lines being used during data clock cycles
> 
> 
>>
>>> +	 * eXecution In Place is out of the scope of the mtd sub-system.
>>> +	 * Hence we choose to merge both mode and wait state clock cycles
>>> +	 * into the so called dummy clock cycles.
>>> +	 */
>>> +	nor->read_dummy = read->num_mode_clocks + read->num_wait_states;
>>> +	return 0;
>>> +}
>>> +
>>> +static int spi_nor_select_pp(struct spi_nor *nor,
>>> +			     const struct spi_nor_flash_parameter *params,
>>> +			     u32 shared_hwcaps)
>>> +{
>>> +	int cmd, best_match = fls(shared_hwcaps & SNOR_HWCAPS_PP_MASK) - 1;
>>> +	const struct spi_nor_pp_command *pp;
>>> +
>>> +	if (best_match < 0)
>>> +		return -EINVAL;
>>> +
>>> +	cmd = spi_nor_hwcaps2cmd(BIT(best_match));
>>> +	if (cmd < 0)
>>> +		return -EINVAL;
>>> +
>>> +	pp = &params->page_programs[cmd];
>>> +	nor->program_opcode = pp->opcode;
>>> +	nor->write_proto = pp->proto;
>>> +	return 0;
>>> +}
>>> +
>>> +static int spi_nor_select_erase(struct spi_nor *nor,
>>> +				const struct flash_info *info)
>>> +{
>>> +	struct mtd_info *mtd = &nor->mtd;
>>> +
>>> +#ifdef CONFIG_MTD_SPI_NOR_USE_4K_SECTORS
>>> +	/* prefer "small sector" erase if possible */
>>> +	if (info->flags & SECT_4K) {
>>> +		nor->erase_opcode = SPINOR_OP_BE_4K;
>>> +		mtd->erasesize = 4096;
>>> +	} else if (info->flags & SECT_4K_PMC) {
>>> +		nor->erase_opcode = SPINOR_OP_BE_4K_PMC;
>>> +		mtd->erasesize = 4096;
>>> +	} else
>>> +#endif
>>> +	{
>>> +		nor->erase_opcode = SPINOR_OP_SE;
>>> +		mtd->erasesize = info->sector_size;
>>> +	}
>>> +	return 0;
>>> +}
>>> +
>>> +static int spi_nor_setup(struct spi_nor *nor, const struct flash_info *info,
>>> +			 const struct spi_nor_flash_parameter *params,
>>> +			 const struct spi_nor_hwcaps *hwcaps)
>>> +{
>>> +	u32 ignored_mask, shared_mask;
>>> +	bool enable_quad_io;
>>> +	int err;
>>> +
>>> +	/*
>>> +	 * Keep only the hardware capabilities supported by both the SPI
>>> +	 * controller and the SPI flash memory.
>>> +	 */
>>> +	shared_mask = hwcaps->mask & params->hwcaps.mask;
>>> +
>>> +	/* SPI protocol classes N-N-N are not supported yet. */
>>> +	ignored_mask = (SNOR_HWCAPS_READ_2_2_2 |
>>> +			SNOR_HWCAPS_READ_4_4_4 |
>>> +			SNOR_HWCAPS_READ_8_8_8 |
>>> +			SNOR_HWCAPS_PP_4_4_4 |
>>> +			SNOR_HWCAPS_PP_8_8_8);
>>> +	if (shared_mask & ignored_mask) {
>>> +		dev_dbg(nor->dev,
>>> +			"SPI protocol classes N-N-N are not supported yet.\n");
>>> +		shared_mask &= ~ignored_mask;
>>> +	}
>>> +
>>> +	/* Select the (Fast) Read command. */
>>> +	err = spi_nor_select_read(nor, params, shared_mask);
>>> +	if (err) {
>>> +		dev_err(nor->dev, "invalid (fast) read\n");
>>
>> What does this information tell me, as an end user ? If I see this error
>> message, what sort of conclusion can I derive from it ? I have
>> no idea ...
>>
> 
> I agree, this could be improved.

Please do.

>>> +		return err;
>>> +	}
>>> +
>>> +	/* Select the Page Program command. */
>>> +	err = spi_nor_select_pp(nor, params, shared_mask);
>>> +	if (err) {
>>> +		dev_err(nor->dev, "invalid page program\n");
>>> +		return err;
>>> +	}
>>> +
>>> +	/* Select the Sector Erase command. */
>>> +	err = spi_nor_select_erase(nor, info);
>>> +	if (err) {
>>> +		dev_err(nor->dev, "invalid sector/block erase\n");
>>> +		return err;
>>> +	}
>>> +
>>> +	/* Enable Quad I/O if needed. */
>>> +	enable_quad_io = (spi_nor_get_protocol_width(nor->read_proto) == 4 ||
>>> +			  spi_nor_get_protocol_width(nor->write_proto) == 4);
>>
>> What if read_proto != write_proto ? Also, this is awful code ... fix it.
>>
> 
> As explained earlier about the QE bit and the Quad Enable Requirement,
> this is indeed exactly what we want: we must set the QE bit, hence call
> nor->flash_quad_enable() whenever any Quad SPI protocol is used for any
> SPI flash command.
> 
> Currently Fast Read and Page Program are the only commands selected by
> the spi-nor protocols, which can use one Quad SPI protocol.
> 
> So this code works whether of not (read_proto == write_proto).

OK, then this should also be a separate patch .

>>> +	if (enable_quad_io && params->quad_enable)
>>> +		nor->flash_quad_enable = params->quad_enable;
>>> +	else
>>> +		nor->flash_quad_enable = NULL;
>>> +
>>> +	return 0;
>>> +}
>>> +
>>> +int spi_nor_scan(struct spi_nor *nor, const char *name,
>>> +		 const struct spi_nor_hwcaps *hwcaps)
>>> +{
>>> +	struct spi_nor_flash_parameter params;
>>>  	const struct flash_info *info = NULL;
>>>  	struct device *dev = nor->dev;
>>>  	struct mtd_info *mtd = &nor->mtd;
>>> @@ -1548,6 +1824,11 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
>>>  	if (ret)
>>>  		return ret;
>>>  
>>> +	/* Reset SPI protocol for all commands */
>>> +	nor->reg_proto = SNOR_PROTO_1_1_1;
>>> +	nor->read_proto = SNOR_PROTO_1_1_1;
>>> +	nor->write_proto = SNOR_PROTO_1_1_1;
>>> +
>>>  	if (name)
>>>  		info = spi_nor_match_id(name);
>>>  	/* Try to auto-detect if chip name wasn't specified or not found */
>> [...]
>>
> 
> Best regards,
> 
> Cyrille
> 


-- 
Best regards,
Marek Vasut

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

* Re: [PATCH v5 2/6] mtd: m25p80: add support of SPI 1-2-2 and 1-4-4 protocols
  2017-04-09 21:30         ` Cyrille Pitchen
@ 2017-04-09 21:46           ` Marek Vasut
  0 siblings, 0 replies; 27+ messages in thread
From: Marek Vasut @ 2017-04-09 21:46 UTC (permalink / raw)
  To: Cyrille Pitchen, Cyrille Pitchen, linux-mtd, jartur, kdasu.kdev,
	mar.krzeminski
  Cc: boris.brezillon, richard, nicolas.ferre, linux-kernel,
	computersforpeace, dwmw2

On 04/09/2017 11:30 PM, Cyrille Pitchen wrote:
> Le 09/04/2017 à 22:46, Marek Vasut a écrit :
>> On 04/09/2017 09:37 PM, Cyrille Pitchen wrote:
>>> Hi Marek,
>>>
>>> Le 07/04/2017 à 01:37, Marek Vasut a écrit :
>>>> On 03/23/2017 12:33 AM, Cyrille Pitchen wrote:
>>>>> Before this patch, m25p80_read() supported few SPI protocols:
>>>>> - regular SPI 1-1-1
>>>>> - SPI Dual Output 1-1-2
>>>>> - SPI Quad Output 1-1-4
>>>>> On the other hand, m25p80_write() only supported SPI 1-1-1.
>>>>>
>>>>> This patch updates both m25p80_read() and m25p80_write() functions to let
>>>>> them support SPI 1-2-2 and SPI 1-4-4 protocols for Fast Read and Page
>>>>> Program SPI commands.
>>>>>
>>>>> It adopts a conservative approach to avoid regressions. Hence the new
>>>>                                              ^ FYI, regression != bug
>>>>
>>>>> implementations try to be as close as possible to the old implementations,
>>>>> so the main differences are:
>>>>> - the tx_nbits values now being set properly for the spi_transfer
>>>>>   structures carrying the (op code + address/dummy) bytes
>>>>> - and the spi_transfer structure being split into 2 spi_transfer
>>>>>   structures when the numbers of I/O lines are different for op code and
>>>>>   for address/dummy byte transfers on the SPI bus.
>>>>>
>>>>> Besides, the current spi-nor framework supports neither the SPI 2-2-2 nor
>>>>> the SPI 4-4-4 protocols. So, for now, we don't need to update the
>>>>> m25p80_{read|write}_reg() functions as SPI 1-1-1 is the only one possible
>>>>> protocol.
>>>>>
>>>>> Signed-off-by: Cyrille Pitchen <cyrille.pitchen@atmel.com>
>>>>> ---
>>>>>  drivers/mtd/devices/m25p80.c | 120 ++++++++++++++++++++++++++++++++-----------
>>>>>  1 file changed, 90 insertions(+), 30 deletions(-)
>>>>>
>>>>> diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c
>>>>> index 68986a26c8fe..64d562efc25d 100644
>>>>> --- a/drivers/mtd/devices/m25p80.c
>>>>> +++ b/drivers/mtd/devices/m25p80.c
>>>>> @@ -34,6 +34,19 @@ struct m25p {
>>>>>  	u8			command[MAX_CMD_SIZE];
>>>>>  };
>>>>>  
>>>>> +static inline void m25p80_proto2nbits(enum spi_nor_protocol proto,
>>>>> +				      unsigned int *inst_nbits,
>>>>> +				      unsigned int *addr_nbits,
>>>>> +				      unsigned int *data_nbits)
>>>>> +{
>>>>
>>>> Why don't we just have some generic macros to extract the number of bits
>>>> from $proto ?
>>>>
>>>
>>> from Documentation/process/coding-style.rst:
>>> "Generally, inline functions are preferable to macros resembling functions."
>>>
>>> inline functions provide better type checking of their arguments and/or
>>> returned value than macros.
>>>
>>> Type checking is also the reason I have chosen to create the 'enum
>>> spi_nor_protocol' rather than using constant macros.
>>
>> That part I get (no, not really [1], inline is compiler _hint_ and for
>> static function, the compiler is smart enough to figure out it should
>> inline it, so drop it. Also cf. __always_inline).
>>
>> What I don't quite get is why don't we just encode the proto as ie.
>>
>> #define PROTO_1_1_4 0x00010204 /* (== BIT(16) | BIT(8) | BIT(2)) */
>>
> 
> This is what I did in former versions of the patch: the scheme you
> propose requires more bits to encode the number of I/O lines for
> instruction, address and data: there would be less bits available for
> future extensions.

Are we ever gonna reach 128bit SPI ? I don't think so. Yes, it requires
more bits, but it also makes it easier to extract information from it
without some elaborate loops.

> Also using the notion of protocol class (1-1-N, 1-N-N, N-N-N) in the
> encoding scheme prevents from setting impossible combinations like
> 4-1-4, 1-2-4, ...

I'd suspect that the review process would catch this.

>> in which case this whole function would turn into constant-time
>>
>> return (proto >> (n * 8)) & 0xff;
>>
>> where n is 0 for data, 1 for address , 2 for command .
>>
>> [1] https://lwn.net/Articles/166172/
>>
>>>>> +	if (inst_nbits)
>>>>> +		*inst_nbits = spi_nor_get_protocol_inst_width(proto);
>>>>> +	if (addr_nbits)
>>>>> +		*addr_nbits = spi_nor_get_protocol_addr_width(proto);
>>>>> +	if (data_nbits)
>>>>> +		*data_nbits = spi_nor_get_protocol_data_width(proto);
>>>>> +}
>>>>> +
>> [...]
>>
> 


-- 
Best regards,
Marek Vasut

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

* Re: [RFC PATCH v5 4/6] mtd: spi-nor: add support to non-uniform SPI NOR flash memories
  2017-03-22 23:33 ` [RFC PATCH v5 4/6] mtd: spi-nor: add support to non-uniform SPI NOR flash memories Cyrille Pitchen
@ 2017-04-15 15:24   ` Marek Vasut
  0 siblings, 0 replies; 27+ messages in thread
From: Marek Vasut @ 2017-04-15 15:24 UTC (permalink / raw)
  To: Cyrille Pitchen, linux-mtd, jartur, kdasu.kdev, mar.krzeminski
  Cc: computersforpeace, dwmw2, boris.brezillon, richard, linux-kernel,
	nicolas.ferre

On 03/23/2017 12:33 AM, Cyrille Pitchen wrote:

Hrmmmm, sigh, took me almost month to review this one, sorry :(

> This patch is a first step in introducing  the support of SPI memories
> with non-uniform erase sizes like Spansion s25fs512s.
> 
> It introduces the memory erase map which splits the memory array into one
> or many erase regions. Each erase region supports up to 4 erase commands,
> as defined by the JEDEC JESD216B (SFDP) specification.
> In turn, an erase command is defined by an op code and a sector size.
> 
> To be backward compatible, the erase map of uniform SPI NOR flash memories
> is initialized so it contains only one erase region and this erase region
> supports only one erase command. Hence a single size is used to erase any
> sector/block of the memory.
> 
> Besides, since the algorithm used to erase sectors on non-uniform SPI NOR
> flash memories is quite expensive, when possible, the erase map is tuned
> to come back to the uniform case.
> 
> This is a transitional patch: non-uniform erase maps will be used later
> when initialized based on the SFDP data.
> 
> Signed-off-by: Cyrille Pitchen <cyrille.pitchen@atmel.com>

[...]

Before I dive into the code, I have two questions:

1) On ie. 128 MiB part, how many struct spi_nor_erase_region {}
   instances would be allocated in total (consider you support
   4k, 64k and 32M erase opcodes) ? Three ?

2) Would it make sense to represent the erase regions as a tree instead?
   For example

      [ region with 32MiB die erase opcode , start=0 , count=4 ]
            |
            V
[ region with 64k erase opcode ][ region with 64k erase opcode ]
[       start=0, count=1       ][      start=0, count=511      ]
            |
            V
[ region with 4k erase opcode ]
[      start=0, count=16      ]

I think it'd make the lookup for the best-fitting opcode combination
faster if the user decides to erase some arbitrarily-aligned block of
the flash.

What do you think ?

Note this tree-based approach does not handle the cases where erase
regions would overlap, although I doubt that could be a problem .

> diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h
> index d270788f5ab6..c12cafe99bee 100644
> --- a/include/linux/mtd/spi-nor.h
> +++ b/include/linux/mtd/spi-nor.h
> @@ -216,6 +216,55 @@ enum spi_nor_option_flags {
>  };
>  
>  /**
> + * struct spi_nor_erase_command - Structure to describe a SPI NOR erase command
> + * @size:		the size of the sector/block erased by the command.
> + * @size_shift:		the size shift: if @size is a power of 2 then the shift
> + *			is stored in @size_shift, otherwise @size_shift is zero.
> + * @size_mask:		the size mask based on @size_shift.
> + * @opcode:		the SPI command op code to erase the sector/block.
> + */
> +struct spi_nor_erase_command {
> +	u32	size;
> +	u32	size_shift;
> +	u32	size_mask;
> +	u8	opcode;
> +};
> +
> +/**
> + * struct spi_nor_erase_region - Structure to describe a SPI NOR erase region
> + * @offset:		the offset in the data array of erase region start.
> + *			LSB bits are used as a bitmask encoding the erase
> + *			commands supported inside this erase region.
> + * @size:		the size of the region in bytes.
> + */
> +struct spi_nor_erase_region {
> +	u64		offset;
> +	u64		size;
> +};
> +
> +#define SNOR_CMD_ERASE_MAX	4
> +#define SNOR_CMD_ERASE_MASK	GENMASK_ULL(SNOR_CMD_ERASE_MAX - 1, 0)
> +#define SNOR_CMD_ERASE_OFFSET(_cmd_mask, _offset)	\
> +	((((u64)(_offset)) & ~SNOR_CMD_ERASE_MASK) |	\
> +	 (((u64)(_cmd_mask)) & SNOR_CMD_ERASE_MASK))
> +
> +/**
> + * struct spi_nor_erase_map - Structure to describe the SPI NOR erase map
> + * @commands:		an array of erase commands shared by all the regions.
> + * @uniform_region:	a pre-allocated erase region for SPI NOR with a uniform
> + *			sector size (legacy implementation).
> + * @regions:		point to an array describing the boundaries of the erase
> + *			regions.
> + * @num_regions:	the number of elements in the @regions array.
> + */
> +struct spi_nor_erase_map {
> +	struct spi_nor_erase_command	commands[SNOR_CMD_ERASE_MAX];
> +	struct spi_nor_erase_region	uniform_region;
> +	struct spi_nor_erase_region	*regions;
> +	u32				num_regions;
> +};
> +
> +/**
>   * struct flash_info -	Forward declaration of a structure used internally by
>   *			spi_nor_scan() and spi_nor_init().
>   */
> @@ -238,6 +287,7 @@ struct flash_info;
>   * @write_proto:	the SPI protocol for write operations
>   * @reg_proto		the SPI protocol for read_reg/write_reg/erase operations
>   * @cmd_buf:		used by the write_reg
> + * @erase_map:		the erase map of the SPI NOR
>   * @prepare:		[OPTIONAL] do some preparations for the
>   *			read/write/erase/lock/unlock operations
>   * @unprepare:		[OPTIONAL] do some post work after the
> @@ -273,6 +323,7 @@ struct spi_nor {
>  	bool			sst_write_second;
>  	u32			flags;
>  	u8			cmd_buf[SPI_NOR_MAX_CMD_SIZE];
> +	struct spi_nor_erase_map	erase_map;
>  
>  	int (*prepare)(struct spi_nor *nor, enum spi_nor_ops ops);
>  	void (*unprepare)(struct spi_nor *nor, enum spi_nor_ops ops);
> @@ -293,6 +344,11 @@ struct spi_nor {
>  	void *priv;
>  };
>  
> +static inline bool spi_nor_has_uniform_erase(const struct spi_nor *nor)
> +{
> +	return (nor->erase_map.regions == &nor->erase_map.uniform_region);
> +}
> +
>  static inline void spi_nor_set_flash_node(struct spi_nor *nor,
>  					  struct device_node *np)
>  {
> 


-- 
Best regards,
Marek Vasut

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

* Re: [RFC PATCH v5 5/6] mtd: spi-nor: parse Serial Flash Discoverable Parameters (SFDP) tables
  2017-03-22 23:33 ` [RFC PATCH v5 5/6] mtd: spi-nor: parse Serial Flash Discoverable Parameters (SFDP) tables Cyrille Pitchen
@ 2017-04-15 15:34   ` Marek Vasut
  0 siblings, 0 replies; 27+ messages in thread
From: Marek Vasut @ 2017-04-15 15:34 UTC (permalink / raw)
  To: Cyrille Pitchen, linux-mtd, jartur, kdasu.kdev, mar.krzeminski
  Cc: computersforpeace, dwmw2, boris.brezillon, richard, linux-kernel,
	nicolas.ferre

On 03/23/2017 12:33 AM, Cyrille Pitchen wrote:
> This patch adds support to the JESD216B standard and parses the SFDP
> tables to dynamically initialize the 'struct spi_nor_flash_parameter'.
> 
> Signed-off-by: Cyrille Pitchen <cyrille.pitchen@atmel.com>

Hi, mostly nits below.

> ---
>  drivers/mtd/spi-nor/spi-nor.c | 558 ++++++++++++++++++++++++++++++++++++++++++
>  include/linux/mtd/spi-nor.h   |   6 +
>  2 files changed, 564 insertions(+)
> 
> diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c
> index 2e54792d506d..ce8722055a9c 100644
> --- a/drivers/mtd/spi-nor/spi-nor.c
> +++ b/drivers/mtd/spi-nor/spi-nor.c
> @@ -17,6 +17,7 @@
>  #include <linux/mutex.h>
>  #include <linux/math64.h>
>  #include <linux/sizes.h>
> +#include <linux/slab.h>
>  
>  #include <linux/mtd/mtd.h>
>  #include <linux/of_platform.h>
> @@ -86,6 +87,7 @@ struct flash_info {
>  					 * to support memory size above 128Mib.
>  					 */
>  #define NO_CHIP_ERASE		BIT(12) /* Chip does not support chip erase */
> +#define SPI_NOR_SKIP_SFDP	BIT(13)	/* Skip parsing of SFDP tables */
>  };
>  
>  #define JEDEC_MFR(info)	((info)->id[0])
> @@ -1593,6 +1595,99 @@ static int spansion_quad_enable(struct spi_nor *nor)
>  	return 0;
>  }
>  
> +static int spansion_new_quad_enable(struct spi_nor *nor)
> +{
> +	u8 sr_cr[2];
> +	int ret;
> +
> +	/* Check current Quad Enable bit value. */
> +	ret = read_cr(nor);
> +	if (ret < 0) {
> +		dev_err(nor->dev,
> +			"error while reading configuration register\n");
> +		return -EINVAL;
> +	}
> +	sr_cr[1] = ret;
> +	if (sr_cr[1] & CR_QUAD_EN_SPAN)
> +		return 0;
> +
> +	dev_info(nor->dev, "setting Spansion Quad Enable (non-volatile) bit\n");
> +
> +	/* Keep the current value of the Status Register. */
> +	ret = read_sr(nor);
> +	if (ret < 0) {
> +		dev_err(nor->dev,
> +			"error while reading status register\n");
> +		return -EINVAL;
> +	}
> +	sr_cr[0] = ret;
> +	sr_cr[1] |= CR_QUAD_EN_SPAN;
> +
> +	write_enable(nor);
> +
> +	ret = nor->write_reg(nor, SPINOR_OP_WRSR, sr_cr, 2);
> +	if (ret < 0) {
> +		dev_err(nor->dev,
> +			"error while writing configuration register\n");
> +		return -EINVAL;
> +	}
> +
> +	ret = spi_nor_wait_till_ready(nor);
> +	if (ret < 0) {
> +		dev_err(nor->dev, "error while waiting for WRSR completion\n");
> +		return ret;
> +	}
> +
> +	/* read back and check it */
> +	ret = read_cr(nor);
> +	if (!(ret > 0 && (ret & CR_QUAD_EN_SPAN))) {

Nit, you might want to align this with sr2_bit7_quad_enable() below, that is

if (ret || !(ret & CR_QUAD_ENABLE_SPAN))
 ...

> +		dev_err(nor->dev, "Spansion Quad bit not set\n");
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +static int sr2_bit7_quad_enable(struct spi_nor *nor)
> +{
> +	u8 sr2;
> +	int ret;
> +
> +	/* Check current Quad Enable bit value. */
> +	ret = nor->read_reg(nor, SPINOR_OP_RDSR2, &sr2, 1);
> +	if (ret)
> +		return ret;
> +	if (sr2 & SR2_QUAD_EN_BIT7)
> +		return 0;
> +
> +	/* Update the Quad Enable bit. */
> +	sr2 |= SR2_QUAD_EN_BIT7;
> +
> +	write_enable(nor);
> +
> +	ret = nor->write_reg(nor, SPINOR_OP_WRSR2, &sr2, 1);
> +	if (ret < 0) {
> +		dev_err(nor->dev,
> +			"error while writing status register 2\n");
> +		return -EINVAL;
> +	}
> +
> +	ret = spi_nor_wait_till_ready(nor);
> +	if (ret < 0) {
> +		dev_err(nor->dev, "error while waiting for WRSR2 completion\n");
> +		return ret;
> +	}
> +
> +	/* Read back and check it. */
> +	ret = nor->read_reg(nor, SPINOR_OP_RDSR2, &sr2, 1);
> +	if (ret || !(sr2 & SR2_QUAD_EN_BIT7)) {
> +		dev_err(nor->dev, "SR2 Quad bit not set\n");
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
>  static int spi_nor_check(struct spi_nor *nor)
>  {
>  	if (!nor->dev || !nor->read || !nor->write ||
> @@ -1759,6 +1854,465 @@ spi_nor_init_uniform_erase_map(struct spi_nor_erase_map *map,
>  	map->uniform_region.size = flash_size;
>  }
>  
> +
> +/*
> + * SFDP parsing.
> + */
> +
> +static int spi_nor_read_sfdp(struct spi_nor *nor, u32 addr,
> +			     size_t len, void *buf)
> +{
> +	u8 addr_width, read_opcode, read_dummy;
> +	int ret;
> +
> +	read_opcode = nor->read_opcode;
> +	addr_width = nor->addr_width;
> +	read_dummy = nor->read_dummy;
> +
> +	nor->read_opcode = SPINOR_OP_RDSFDP;
> +	nor->addr_width = 3;
> +	nor->read_dummy = 8;
> +
> +	ret = nor->read(nor, addr, len, (u8 *)buf);
> +
> +	nor->read_opcode = read_opcode;
> +	nor->addr_width = addr_width;
> +	nor->read_dummy = read_dummy;
> +
> +	return (ret < 0) ? ret : 0;
> +}
> +
> +struct sfdp_parameter_header {
> +	u8		id_lsb;
> +	u8		minor;
> +	u8		major;
> +	u8		length; /* in double words */
> +	u8		parameter_table_pointer[3]; /* byte address */
> +	u8		id_msb;
> +};
> +
> +#define SFDP_PARAM_HEADER_ID(p)	((u16)(((p)->id_msb << 8) | (p)->id_lsb))
> +#define SFDP_PARAM_HEADER_PTP(p) \
> +	((u32)(((p)->parameter_table_pointer[2] << 16) | \

Is the u32 cast needed ?  And the u16 one above ?

> +	       ((p)->parameter_table_pointer[1] <<  8) | \
> +	       ((p)->parameter_table_pointer[0] <<  0)))
> +
> +
> +#define SFDP_BFPT_ID		0xff00u	/* Basic Flash Parameter 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
> +
> +struct sfdp_header {
> +	u32		signature; /* Ox50444653 <=> "SFDP" */
> +	u8		minor;
> +	u8		major;
> +	u8		nph; /* 0-base number of parameter headers */
> +	u8		unused;
> +
> +	/* Basic Flash Parameter Table. */
> +	struct sfdp_parameter_header	bfpt_header;
> +};
> +
> +/* Basic Flash Parameter Table */
> +
> +/*
> + * JESD216B defines a Basic Flash Parameter Table of 16 DWORDs.
> + * They are indexed from 1 but C arrays are indexed from 0.
> + */
> +enum sfdp_bfpt_dword {

Is this really useful at all ?

> +	BFPT_DWORD1 = 0,
> +	BFPT_DWORD2,
> +	BFPT_DWORD3,
> +	BFPT_DWORD4,
> +	BFPT_DWORD5,
> +	BFPT_DWORD6,
> +	BFPT_DWORD7,
> +	BFPT_DWORD8,
> +	BFPT_DWORD9,
> +	BFPT_DWORD10,
> +	BFPT_DWORD11,
> +	BFPT_DWORD12,
> +	BFPT_DWORD13,
> +	BFPT_DWORD14,
> +	BFPT_DWORD15,
> +	BFPT_DWORD16,
> +
> +	BFPT_DWORD_MAX
> +};
> +
> +/* The first revision of JESB216 defined only 9 DWORDs. */
> +#define BFPT_DWORD_MAX_JESD216			9
> +
> +/* 1st DWORD. */
> +#define BFPT_DWORD1_FAST_READ_1_1_2		BIT(16)
> +#define BFPT_DWORD1_ADDRESS_BYTES_MASK		GENMASK(18, 17)
> +#define BFPT_DWORD1_ADDRESS_BYTES_3_ONLY	(0u << 17)
> +#define BFPT_DWORD1_ADDRESS_BYTES_3_OR_4	(1u << 17)
> +#define BFPT_DWORD1_ADDRESS_BYTES_4_ONLY	(2u << 17)
> +#define BFPT_DWORD1_DTR				BIT(19)
> +#define BFPT_DWORD1_FAST_READ_1_2_2		BIT(20)
> +#define BFPT_DWORD1_FAST_READ_1_4_4		BIT(21)
> +#define BFPT_DWORD1_FAST_READ_1_1_4		BIT(22)
> +
> +/* 5th DWORD. */
> +#define BFPT_DWORD5_FAST_READ_2_2_2		BIT(0)
> +#define BFPT_DWORD5_FAST_READ_4_4_4		BIT(4)
> +
> +/* 11th DWORD. */
> +#define BFPT_DWORD11_PAGE_SIZE_SHIFT		4
> +#define BFPT_DWORD11_PAGE_SIZE_MASK		GENMASK(7, 4)
> +
> +/* 15th DWORD. */
> +
> +/*
> + * (from JESD216B)
> + * Quad Enable Requirements (QER):
> + * - 000b: Device does not have a QE bit. Device detects 1-1-4 and 1-4-4
> + *         reads based on instruction. DQ3/HOLD# functions are hold during
> + *         instruction pahse.
> + * - 001b: QE is bit 1 of status register 2. It is set via Write Status with
> + *         two data bytes where bit 1 of the second byte is one.
> + *         [...]
> + *         Writing only one byte to the status register has the side-effect of
> + *         clearing status register 2, including the QE bit. The 100b code is
> + *         used if writing one byte to the status register does not modify
> + *         status register 2.
> + * - 010b: QE is bit 6 of status register 1. It is set via Write Status with
> + *         one data byte where bit 6 is one.
> + *         [...]
> + * - 011b: QE is bit 7 of status register 2. It is set via Write status
> + *         register 2 instruction 3Eh with one data byte where bit 7 is one.
> + *         [...]
> + *         The status register 2 is read using instruction 3Fh.
> + * - 100b: QE is bit 1 of status register 2. It is set via Write Status with
> + *         two data bytes where bit 1 of the second byte is one.
> + *         [...]
> + *         In contrast to the 001b code, writing one byte to the status
> + *         register does not modify status register 2.
> + * - 101b: QE is bit 1 of status register 2. Status register 1 is read using
> + *         Read Status instruction 05h. Status register2 is read using
> + *         instruction 35h. QE is set via Writ Status instruction 01h with
> + *         two data bytes where bit 1 of the second byte is one.
> + *         [...]
> + */
> +#define BFPT_DWORD15_QER_MASK			GENMASK(22, 20)
> +#define BFPT_DWORD15_QER_NONE			(0u << 20) /* Micron */
> +#define BFPT_DWORD15_QER_SR2_BIT1_BUGGY		(1u << 20)
> +#define BFPT_DWORD15_QER_SR1_BIT6		(2u << 20) /* Macronix */
> +#define BFPT_DWORD15_QER_SR2_BIT7		(3u << 20)
> +#define BFPT_DWORD15_QER_SR2_BIT1_NO_RD		(4u << 20)
> +#define BFPT_DWORD15_QER_SR2_BIT1		(5u << 20) /* Spansion */
> +
> +
> +struct sfdp_bfpt {
> +	u32	dwords[BFPT_DWORD_MAX];
> +};
> +
> +/* Fast Read settings. */
> +
> +static inline void
> +spi_nor_set_read_settings_from_bfpt(struct spi_nor_read_command *read,
> +				    u16 half,
> +				    enum spi_nor_protocol proto)
> +{
> +	read->num_mode_clocks = (half >> 5) & 0x07u;

Is this u in 0x07u really needed here ?

> +	read->num_wait_states = (half >> 0) & 0x1Fu;
> +	read->opcode = (half >> 8) & 0xFFu;
> +	read->proto = proto;
> +}
> +

[...]

> +static int spi_nor_parse_sfdp(struct spi_nor *nor,
> +			      struct spi_nor_flash_parameter *params)
> +{
> +	const struct sfdp_parameter_header *param_header, *bfpt_header;
> +	struct sfdp_parameter_header *param_headers = NULL;
> +	struct sfdp_header header;
> +	size_t psize;
> +	int i, err;
> +
> +	/* Get the SFDP header. */
> +	err = spi_nor_read_sfdp(nor, 0, sizeof(header), &header);
> +	if (err)
> +		return err;
> +
> +	/* Check the SFDP header version. */
> +	if (le32_to_cpu(header.signature) != SFDP_SIGNATURE ||
> +	    header.major != SFDP_JESD216_MAJOR ||
> +	    header.minor < SFDP_JESD216_MINOR)
> +		return -EINVAL;
> +
> +	/*
> +	 * Verify that the first and only mandatory parameter header is a
> +	 * Basic Flash Parameter Table header as specified in JESD216.
> +	 */
> +	bfpt_header = &header.bfpt_header;
> +	if (SFDP_PARAM_HEADER_ID(bfpt_header) != SFDP_BFPT_ID ||
> +	    bfpt_header->major != SFDP_JESD216_MAJOR)
> +		return -EINVAL;
> +
> +	/* Allocate memory for parameter headers. */
> +	if (header.nph) {
> +		psize = header.nph * sizeof(*param_headers);
> +
> +		param_headers = kmalloc(psize, GFP_KERNEL);
> +		if (!param_headers) {
> +			dev_err(nor->dev,
> +				"failed to allocate memory for SFDP parameter headers\n");

If malloc in kernel fails, you will most likely not be able to print
anything because that printing will also need to allocate memory
somewhere down the line. Nuke these prints , they are useless.

> +			return -ENOMEM;
> +		}
> +
> +		err = spi_nor_read_sfdp(nor, sizeof(header),
> +					psize, param_headers);
> +		if (err) {
> +			dev_err(nor->dev,
> +				"failed to read SFDP parameter headers\n");
> +			goto exit;
> +		}
> +	}
> +
> +	/*
> +	 * Check other parameter headers to get the latest revision of
> +	 * the basic flash parameter table.
> +	 */
> +	for (i = 0; i < header.nph; i++) {
> +		param_header = &param_headers[i];
> +
> +		if (SFDP_PARAM_HEADER_ID(param_header) == SFDP_BFPT_ID &&
> +		    param_header->major == SFDP_JESD216_MAJOR &&
> +		    (param_header->minor > bfpt_header->minor ||
> +		     (param_header->minor == bfpt_header->minor &&
> +		      param_header->length > bfpt_header->length)))
> +			bfpt_header = param_header;
> +	}
> +	err = spi_nor_parse_bfpt(nor, bfpt_header, params);
> +	if (err)
> +		goto exit;
> +
> +exit:
> +	kfree(param_headers);
> +	return err;
> +}
> +
>  static int spi_nor_init_params(struct spi_nor *nor,
>  			       const struct flash_info *info,
>  			       struct spi_nor_flash_parameter *params)
> @@ -1834,6 +2388,10 @@ static int spi_nor_init_params(struct spi_nor *nor,
>  		}
>  	}
>  
> +	/* Override the parameters with data read from SFDP tables. */
> +	if (!(info->flags & SPI_NOR_SKIP_SFDP))
> +		spi_nor_parse_sfdp(nor, params);

This returns int, handle the retval.

> +
>  	return 0;
>  }
>  
> diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h
> index c12cafe99bee..a0d21a973d60 100644
> --- a/include/linux/mtd/spi-nor.h
> +++ b/include/linux/mtd/spi-nor.h
> @@ -41,6 +41,8 @@
>  #define SPINOR_OP_WREN		0x06	/* Write enable */
>  #define SPINOR_OP_RDSR		0x05	/* Read status register */
>  #define SPINOR_OP_WRSR		0x01	/* Write status register 1 byte */
> +#define SPINOR_OP_RDSR2		0x3f	/* Read status register 2 */
> +#define SPINOR_OP_WRSR2		0x3e	/* Write status register 2 */
>  #define SPINOR_OP_READ		0x03	/* Read data bytes (low frequency) */
>  #define SPINOR_OP_READ_FAST	0x0b	/* Read data bytes (high frequency) */
>  #define SPINOR_OP_READ_1_1_2	0x3b	/* Read data bytes (Dual Output SPI) */
> @@ -56,6 +58,7 @@
>  #define SPINOR_OP_CHIP_ERASE	0xc7	/* Erase whole flash chip */
>  #define SPINOR_OP_SE		0xd8	/* Sector erase (usually 64KiB) */
>  #define SPINOR_OP_RDID		0x9f	/* Read JEDEC ID */
> +#define SPINOR_OP_RDSFDP	0x5a	/* Read SFDP */
>  #define SPINOR_OP_RDCR		0x35	/* Read configuration register */
>  #define SPINOR_OP_RDFSR		0x70	/* Read flag status register */
>  
> @@ -128,6 +131,9 @@
>  /* Configuration Register bits. */
>  #define CR_QUAD_EN_SPAN		BIT(1)	/* Spansion Quad I/O */
>  
> +/* Status Register 2 bits. */
> +#define SR2_QUAD_EN_BIT7	BIT(7)
> +
>  
>  /* Supported SPI protocols */
>  #define SNOR_PROTO_WIDTH_MASK	GENMASK(7, 0)
> 


-- 
Best regards,
Marek Vasut

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

* Re: [RFC PATCH v5 6/6] mtd: spi-nor: parse SFDP 4-byte Address Instruction Table
  2017-03-22 23:39 ` [RFC PATCH v5 6/6] mtd: spi-nor: parse SFDP 4-byte Address Instruction Table Cyrille Pitchen
@ 2017-04-15 15:36   ` Marek Vasut
  0 siblings, 0 replies; 27+ messages in thread
From: Marek Vasut @ 2017-04-15 15:36 UTC (permalink / raw)
  To: Cyrille Pitchen, linux-mtd, jartur, kdasu.kdev, mar.krzeminski
  Cc: computersforpeace, dwmw2, boris.brezillon, richard, linux-kernel,
	nicolas.ferre

On 03/23/2017 12:33 AM, Cyrille Pitchen wrote:
> This patch adds supports for SFDP (JESD216B) 4-byte Address Instruction
> Table. This table is optional but when available, we parse it to get the
> 4-byte address op codes supported by the memory.
> Using these op codes is stateless as opposed to entering the 4-byte
> address mode or setting the Base Address Register (BAR).
> 
> Signed-off-by: Cyrille Pitchen <cyrille.pitchen@atmel.com>
> ---
>  drivers/mtd/spi-nor/spi-nor.c | 166 +++++++++++++++++++++++++++++++++++++++++-
>  1 file changed, 165 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c
> index ce8722055a9c..ea044efc4e6d 100644
> --- a/drivers/mtd/spi-nor/spi-nor.c
> +++ b/drivers/mtd/spi-nor/spi-nor.c
> @@ -1899,6 +1899,7 @@ struct sfdp_parameter_header {
>  
>  
>  #define SFDP_BFPT_ID		0xff00u	/* Basic Flash Parameter Table */
> +#define SFDP_4BAIT_ID		0xff84u	/* 4-byte Address Instruction Table */
>  
>  #define SFDP_SIGNATURE		0x50444653u
>  #define SFDP_JESD216_MAJOR	1
> @@ -2241,6 +2242,149 @@ static int spi_nor_parse_bfpt(struct spi_nor *nor,
>  	return 0;
>  }
>  
> +struct sfdp_4bait {
> +	/* The hardware capability. */
> +	u32		hwcaps;
> +
> +	/*
> +	 * The <supported_bit> bit in DWORD1 of the 4BAIT tells us whether
> +	 * the associated 4-byte address op code is supported.
> +	 */
> +	u32		supported_bit;
> +};
> +
> +static int spi_nor_parse_4bait(struct spi_nor *nor,
> +			       const struct sfdp_parameter_header *param_header,
> +			       struct spi_nor_flash_parameter *params)
> +{
> +	static const struct sfdp_4bait reads[] = {
> +		{ SNOR_HWCAPS_READ,		BIT(0) },
> +		{ SNOR_HWCAPS_READ_FAST,	BIT(1) },
> +		{ SNOR_HWCAPS_READ_1_1_2,	BIT(2) },
> +		{ SNOR_HWCAPS_READ_1_2_2,	BIT(3) },
> +		{ SNOR_HWCAPS_READ_1_1_4,	BIT(4) },
> +		{ SNOR_HWCAPS_READ_1_4_4,	BIT(5) },
> +		{ SNOR_HWCAPS_READ_1_1_1_DTR,	BIT(13) },
> +		{ SNOR_HWCAPS_READ_1_2_2_DTR,	BIT(14) },
> +		{ SNOR_HWCAPS_READ_1_4_4_DTR,	BIT(15) },
> +	};
> +	static const struct sfdp_4bait programs[] = {
> +		{ SNOR_HWCAPS_PP,		BIT(6) },
> +		{ SNOR_HWCAPS_PP_1_1_4,		BIT(7) },
> +		{ SNOR_HWCAPS_PP_1_4_4,		BIT(8) },
> +	};
> +	static const struct sfdp_4bait erases[SNOR_CMD_ERASE_MAX] = {
> +		{ 0u /* not used */,		BIT(9) },
> +		{ 0u /* not used */,		BIT(10) },
> +		{ 0u /* not used */,		BIT(11) },
> +		{ 0u /* not used */,		BIT(12) },
> +	};
> +	u32 dwords[2], addr, discard_hwcaps, read_hwcaps, pp_hwcaps, erase_mask;
> +	struct spi_nor_erase_map *map = &nor->erase_map;
> +	int i, err;
> +
> +	if (param_header->major != SFDP_JESD216_MAJOR ||
> +	    param_header->length < ARRAY_SIZE(dwords))
> +		return -EINVAL;
> +
> +	/* Read the 4-byte Address Instruction Table. */
> +	addr = SFDP_PARAM_HEADER_PTP(param_header);
> +	err = spi_nor_read_sfdp(nor, addr, sizeof(dwords), dwords);
> +	if (err)
> +		return err;
> +
> +	/* Fix endianness of the 4BAIT DWORDs. */
> +	for (i = 0; i < ARRAY_SIZE(dwords); i++)
> +		dwords[i] = le32_to_cpu(dwords[i]);
> +
> +	/*
> +	 * Compute the subset of (Fast) Read commands for which the 4-byte
> +	 * version is supported.
> +	 */
> +	discard_hwcaps = 0;
> +	read_hwcaps = 0;
> +	for (i = 0; i < ARRAY_SIZE(reads); i++) {
> +		const struct sfdp_4bait *read = &reads[i];
> +
> +		discard_hwcaps |= read->hwcaps;
> +		if ((params->hwcaps.mask & read->hwcaps) &&
> +		    (dwords[0] & read->supported_bit))
> +			read_hwcaps |= read->hwcaps;
> +	}

Looks like there is a bit of repeated stuff here, maybe this can be
pulled out ?

> +	/*
> +	 * Compute the subset of Page Program commands for which the 4-byte
> +	 * version is supported.
> +	 */
> +	pp_hwcaps = 0;
> +	for (i = 0; i < ARRAY_SIZE(programs); i++) {
> +		const struct sfdp_4bait *program = &programs[i];
> +
> +		discard_hwcaps |= program->hwcaps;
> +		if ((params->hwcaps.mask & program->hwcaps) &&
> +		    (dwords[0] & program->supported_bit))
> +			pp_hwcaps |= program->hwcaps;
> +	}
> +
> +	/*
> +	 * Compute the subet of Sector Erase commands for which the 4-byte
> +	 * version is supported.
> +	 */
> +	erase_mask = 0;
> +	for (i = 0; i < SNOR_CMD_ERASE_MAX; i++) {
> +		const struct sfdp_4bait *erase = &erases[i];
> +
> +		if ((map->commands[i].size > 0) &&
> +		    (dwords[0] & erase->supported_bit))
> +			erase_mask |= BIT(i);
> +	}
> +
> +	/*
> +	 * We need at least one 4-byte op code per read, program and erase
> +	 * operation; the .read(), .write() and .erase() hooks share the
> +	 * nor->addr_width value.
> +	 */
> +	if (!read_hwcaps || !pp_hwcaps || !erase_mask)
> +		return 0;
> +
> +	/*
> +	 * Discard all operations from the 4-byte instruction set which are
> +	 * not supported by this memory.
> +	 */
> +	params->hwcaps.mask &= ~discard_hwcaps;
> +	params->hwcaps.mask |= (read_hwcaps | pp_hwcaps);
> +
> +	/* Use the 4-byte address instruction set. */
> +	for (i = 0; i < SNOR_CMD_READ_MAX; i++) {
> +		struct spi_nor_read_command *read_cmd = &params->reads[i];
> +
> +		read_cmd->opcode = spi_nor_convert_3to4_read(read_cmd->opcode);
> +	}
> +	for (i = 0; i < SNOR_CMD_PP_MAX; i++) {
> +		struct spi_nor_pp_command *pp_cmd = &params->page_programs[i];
> +
> +		pp_cmd->opcode = spi_nor_convert_3to4_program(pp_cmd->opcode);
> +	}
> +	for (i = 0; i < SNOR_CMD_ERASE_MAX; i++) {
> +		struct spi_nor_erase_command *erase_cmd = &map->commands[i];
> +
> +		if (erase_mask & BIT(i))
> +			erase_cmd->opcode = (dwords[1] >> (i * 8)) & 0xFF;
> +		else
> +			spi_nor_set_erase_command(erase_cmd, 0u, 0xFF);
> +	}
> +
> +	/*
> +	 * We set nor->addr_width here to skip spi_nor_set_4byte_opcodes()
> +	 * later because this latest function implements a legacy quirk for
> +	 * the erase size of Spansion memory. However this quirk is no longer
> +	 * needed with new SFDP compliant memories.
> +	 */
> +	nor->addr_width = 4;
> +	nor->flags |= SNOR_F_4B_OPCODES;
> +	return 0;
> +}
> +
>  static int spi_nor_parse_sfdp(struct spi_nor *nor,
>  			      struct spi_nor_flash_parameter *params)
>  {
> @@ -2308,6 +2452,23 @@ static int spi_nor_parse_sfdp(struct spi_nor *nor,
>  	if (err)
>  		goto exit;
>  
> +	/* Parse other parameter headers. */
> +	for (i = 0; i < header.nph; i++) {
> +		param_header = &param_headers[i];
> +
> +		switch (SFDP_PARAM_HEADER_ID(param_header)) {
> +		case SFDP_4BAIT_ID:
> +			err = spi_nor_parse_4bait(nor, param_header, params);
> +			break;
> +
> +		default:
> +			break;
> +		}
> +
> +		if (err)
> +			goto exit;
> +	}
> +
>  exit:
>  	kfree(param_headers);
>  	return err;
> @@ -2885,7 +3046,10 @@ int spi_nor_scan(struct spi_nor *nor, const char *name,
>  	if (ret)
>  		return ret;
>  
> -	if (info->addr_width)
> +	if (nor->addr_width)
> +		/* already configured by spi_nor_setup() */
> +		;

I think you can drop this part.

> +	else if (info->addr_width)
>  		nor->addr_width = info->addr_width;
>  	else if (mtd->size > 0x1000000) {
>  		/* enable 4-byte addressing if the device exceeds 16MiB */
> 


-- 
Best regards,
Marek Vasut

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

end of thread, other threads:[~2017-04-15 15:36 UTC | newest]

Thread overview: 27+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-03-22 23:33 [PATCH v5 0/6] mtd: spi-nor: parse SFDP tables to setup (Q)SPI memories Cyrille Pitchen
2017-03-22 23:33 ` [PATCH v5 1/6] mtd: spi-nor: introduce more SPI protocols and the Dual Transfer Mode Cyrille Pitchen
2017-03-23 15:13   ` Cédric Le Goater
2017-03-23 19:10     ` Cyrille Pitchen
2017-03-24 10:03       ` Cédric Le Goater
2017-03-24 11:39         ` Cyrille Pitchen
2017-04-02 17:05   ` Cyrille Pitchen
2017-04-06 23:30   ` Marek Vasut
2017-04-09 21:16     ` Cyrille Pitchen
2017-04-09 21:40       ` Marek Vasut
2017-03-22 23:33 ` [PATCH v5 2/6] mtd: m25p80: add support of SPI 1-2-2 and 1-4-4 protocols Cyrille Pitchen
2017-04-02 17:05   ` Cyrille Pitchen
2017-04-06 23:37   ` Marek Vasut
2017-04-09 19:37     ` Cyrille Pitchen
2017-04-09 20:46       ` Marek Vasut
2017-04-09 21:30         ` Cyrille Pitchen
2017-04-09 21:46           ` Marek Vasut
2017-03-22 23:33 ` [PATCH v5 3/6] mtd: spi-nor: add spi_nor_init() function Cyrille Pitchen
2017-04-02 17:06   ` Cyrille Pitchen
2017-03-22 23:33 ` [RFC PATCH v5 4/6] mtd: spi-nor: add support to non-uniform SPI NOR flash memories Cyrille Pitchen
2017-04-15 15:24   ` Marek Vasut
2017-03-22 23:33 ` [RFC PATCH v5 5/6] mtd: spi-nor: parse Serial Flash Discoverable Parameters (SFDP) tables Cyrille Pitchen
2017-04-15 15:34   ` Marek Vasut
2017-03-22 23:39 ` [RFC PATCH v5 6/6] mtd: spi-nor: parse SFDP 4-byte Address Instruction Table Cyrille Pitchen
2017-04-15 15:36   ` Marek Vasut
2017-03-29 16:45 ` [PATCH v5 0/6] mtd: spi-nor: parse SFDP tables to setup (Q)SPI memories Cyrille Pitchen
2017-04-02 18:32   ` Marek Vasut

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