Skip to contentSkip to conversation

mtd: spi-nor: parse SFDP tables to setup (Q)SPI memories

PATCH Superseded Cyrille Pitchen 3 participants 25 messages Oct 24, 2016 – Nov 2, 2016

Subsystems (4)

v3 of 7 Superseded Latest is v7 →

lore
Share & tools
lore
#0 lore raw

v3 cover mtd: spi-nor: parse SFDP tables to setup (Q)SPI memories

Hi all,

This series extends support of SPI protocols to new protocols such as SPI x-2-2 and SPI x-4-4. Also spi_nor_scan() tries now to select the right op codes, timing parameters (number of mode and dummy cycles) and erase sector size by parsing the Serial Flash Discoverable Parameter (SFDP) tables, when available, as defined in the JEDEC JESD216 specifications.

When SFDP tables are not available, legacy settings are still used for backward compatibility (SPI and earlier QSPI memories).

Support of SPI memories >128Mbits is also improved by using the 4byte address instruction set, when available. Using those dedicated op codes is stateless as opposed to enter the 4byte address mode, hence a better compatibility with some boot loaders which expect to use 3byte address op codes.

This series was tested on a Atmel sama5d2 xplained board with the atmel-qspi.c driver. Except for SST memories, the SPI bus clock was set to 83MHz. The config MTD_SPI_NOR_USE_4K_SECTORS was selected.

For my tests, I used some shell scripts using mtd_debug and sha1sum to check the data integrity.

e.g:
#!/bin/sh

mtd_debug erase /dev/mtd5 0 0x780000
mtd_debug write /dev/mtd5 0 7674703 sama5d4_doc11238.pdf mtd_debug read /dev/mtd5 0 7674703 sama5d4_tmp.pdf

sha1sum sama5d4_doc11238.pdf sama5d4_tmp.pdf

Depending on the actual memory size, I may have used other partitions (/dev/mtd4) and input file size (2880044 and 320044 bytes).

The series was tested with the following QSPI memories:

Spansion/Cypress:
- s25fl127s OK
- s25fl512s OK
- s25fl164k OK

Micron:
- n25q128a OK
- n25q512 OK
- n25q01g OK

Macronix:
- mx25v1635f OK
- mx25l3235f OK
- mx25l3273f OK
- mx25l6433f OK
- mx25l6473f OK
- mx25l12835f OK
- mx25l12845g OK
- mx25l12873g OK
- mx25l25645g OK
- mx25l25673g OK
- mx25l51245g OK
- mx66l1g45g OK (1)

SST:
- sst26vf016b OK (2)
- sst26vf032b OK (2)
- sst26vf064b OK (2)

(1): requires patch 8
(2): requires patch 9, the SPI bus clock frequency was decreased down to 55.3MHz

Best regards,

Cyrille

ChangeLog:

v2 -> v3
- tested with new samples: Micron n25q512, n25q01g and Macronix mx25v1635f, mx25l3235f, mx25l3273f.
- add "Reviewed-by: Jagan Teki <[email protected]>" on patch 1.
- add "Tested-by: Vignesh R <[email protected]>" 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 (9):
mtd: spi-nor: improve macronix_quad_enable()
mtd: spi-nor: add an alternative method to support memory >16MiB
mtd: spi-nor: add support of SPI protocols like SPI 1-2-2 and SPI

    1-4-4

mtd: spi-nor: remove unused set_quad_mode() function
mtd: m25p80: add support of dual and quad spi protocols to all
commands
mtd: spi-nor: parse Serial Flash Discoverable Parameters (SFDP) tables
mtd: spi-nor: parse SFDP 4-byte Address Instruction Table
mtd: spi-nor: add support to Macronix mx66l1g45g
mtd: spi-nor: add support to SST sst26* QSPI memories

 drivers/mtd/devices/m25p80.c            |  204 ++++--
 drivers/mtd/devices/serial_flash_cmds.h |    7 -
 drivers/mtd/devices/st_spi_fsm.c        |   28 +-
 drivers/mtd/spi-nor/atmel-quadspi.c     |   83 ++-
 drivers/mtd/spi-nor/cadence-quadspi.c   |   18 +-
 drivers/mtd/spi-nor/fsl-quadspi.c       |    8 +-
 drivers/mtd/spi-nor/hisi-sfc.c          |   32 +-
 drivers/mtd/spi-nor/mtk-quadspi.c       |   16 +-
 drivers/mtd/spi-nor/nxp-spifi.c         |   21 +-
 drivers/mtd/spi-nor/spi-nor.c           | 1026 ++++++++++++++++++++++++++++---
 include/linux/mtd/spi-nor.h             |  165 ++++-
 11 files changed, 1372 insertions(+), 236 deletions(-)
Signature · 1 line
2.7.4
#1 lore raw

PATCH v3 1/9 mtd: spi-nor: improve macronix_quad_enable()

The patch checks whether the Quad Enable bit is already set in the Status Register. If so, the function exits immediately with a successful return code. Otherwise, a message is now printed telling we're setting the non-volatile bit.

---
 drivers/mtd/spi-nor/spi-nor.c | 5 +++++
 1 file changed, 5 insertions(+)
drivers/mtd/spi-nor/spi-nor.c +5 -0
index d0fc165d7d66..5c87b2d99507 100644
@@ -1211,6 +1211,11 @@ static int macronix_quad_enable(struct spi_nor *nor)
 	val = read_sr(nor);
 	if (val < 0)
 		return val;
+	if (val & SR_QUAD_EN_MX)
+		return 0;
+
+	/* Update the Quad Enable bit. */
+	dev_info(nor->dev, "setting Macronix Quad Enable (non-volatile) bit\n");
 	write_enable(nor);
 
 	write_sr(nor, val | SR_QUAD_EN_MX);
Signature · 1 line
2.7.4
#2 lore raw

Re: [PATCH v3 1/9] mtd: spi-nor: improve macronix_quad_enable()

Quoted 16 lines · Cyrille Pitchen

The patch checks whether the Quad Enable bit is already set in the Status Register. If so, the function exits immediately with a successful return code. Otherwise, a message is now printed telling we're setting the non-volatile bit.

---
 drivers/mtd/spi-nor/spi-nor.c | 5 +++++
 1 file changed, 5 insertions(+)
drivers/mtd/spi-nor/spi-nor.c +0 -0
index d0fc165d7d66..5c87b2d99507 100644

drivers/mtd/spi-nor/spi-nor.c static int macronix_quad_enable(struct spi_nor *nor) · line 1218

Quoted 8 lines
drivers/mtd/spi-nor/spi-nor.c +5 -0
@@ -1211,6 +1211,11 @@ static int macronix_quad_enable(struct spi_nor *nor)
 	val = read_sr(nor);
 	if (val < 0)
 		return val;
+	if (val & SR_QUAD_EN_MX)
+		return 0;
+
+	/* Update the Quad Enable bit. */
+	dev_info(nor->dev, "setting Macronix Quad Enable (non-volatile) bit\n");

Should probably be dev_dbg(), since this is just churn in the kernel log. The user doesn't care and the developer can just up the debug level or add his own printk .

Quoted 4 lines
drivers/mtd/spi-nor/spi-nor.c +0 -0
 	write_enable(nor);
 
 	write_sr(nor, val | SR_QUAD_EN_MX);
Signature · 2 lines
Best regards,
Marek Vasut
#3 lore raw

Re: [PATCH v3 1/9] mtd: spi-nor: improve macronix_quad_enable()

Hi Marek,

Quoted 21 lines · Marek Vasut
Quoted 20 lines · Cyrille Pitchen

The patch checks whether the Quad Enable bit is already set in the Status Register. If so, the function exits immediately with a successful return code. Otherwise, a message is now printed telling we're setting the non-volatile bit.

---
 drivers/mtd/spi-nor/spi-nor.c | 5 +++++
 1 file changed, 5 insertions(+)
drivers/mtd/spi-nor/spi-nor.c +1 -0
index d0fc165d7d66..5c87b2d99507 100644
@@ -1211,6 +1211,11 @@ static int macronix_quad_enable(struct spi_nor *nor)
 	val = read_sr(nor);
 	if (val < 0)
 		return val;
+	if (val & SR_QUAD_EN_MX)
Quoted 9 lines

drivers/mtd/spi-nor/spi-nor.c static int macronix_quad_enable(struct spi_nor *nor) · line 1218

Quoted 4 lines
drivers/mtd/spi-nor/spi-nor.c +4 -0
+		return 0;
+
+	/* Update the Quad Enable bit. */
+	dev_info(nor->dev, "setting Macronix Quad Enable (non-volatile) bit\n");

Should probably be dev_dbg(), since this is just churn in the kernel log. The user doesn't care and the developer can just up the debug level or add his own printk .

The Quad Enable bit is non-volatile so this trace should be printed only once but I'm perfectly fine with using dev_dbg(). I'll change it in the next version.

Best regards,

Cyrille

Quoted 6 lines
Quoted 4 lines
drivers/mtd/spi-nor/spi-nor.c +0 -0
 	write_enable(nor);
 
 	write_sr(nor, val | SR_QUAD_EN_MX);
#4 lore raw

Re: [PATCH v3 1/9] mtd: spi-nor: improve macronix_quad_enable()

Quoted 34 lines · Cyrille Pitchen

Hi Marek,

Quoted 21 lines · Marek Vasut
Quoted 20 lines · Cyrille Pitchen

The patch checks whether the Quad Enable bit is already set in the Status Register. If so, the function exits immediately with a successful return code. Otherwise, a message is now printed telling we're setting the non-volatile bit.

---
 drivers/mtd/spi-nor/spi-nor.c | 5 +++++
 1 file changed, 5 insertions(+)
drivers/mtd/spi-nor/spi-nor.c +1 -0
index d0fc165d7d66..5c87b2d99507 100644
@@ -1211,6 +1211,11 @@ static int macronix_quad_enable(struct spi_nor *nor)
 	val = read_sr(nor);
 	if (val < 0)
 		return val;
+	if (val & SR_QUAD_EN_MX)
Quoted 9 lines

drivers/mtd/spi-nor/spi-nor.c static int macronix_quad_enable(struct spi_nor *nor) · line 1218

Quoted 4 lines
drivers/mtd/spi-nor/spi-nor.c +4 -0
+		return 0;
+
+	/* Update the Quad Enable bit. */
+	dev_info(nor->dev, "setting Macronix Quad Enable (non-volatile) bit\n");

Should probably be dev_dbg(), since this is just churn in the kernel log. The user doesn't care and the developer can just up the debug level or add his own printk .

Quoted 2 lines

The Quad Enable bit is non-volatile so this trace should be printed only once but I'm perfectly fine with using dev_dbg(). I'll change it in the next version.

I get it, but what's the value of it ? Who will make use of this information being in the kernel log? The kernel is already chatty enough :)

Signature · 2 lines
Best regards,
Marek Vasut
#5 lore raw

PATCH v3 2/9 mtd: spi-nor: add an alternative method to support memory >16MiB

In reply to Cyrille Pitchen

This patch provides an alternative mean to support memory above 16MiB (128Mib) by replacing 3byte address op codes by their associated 4byte address versions.

Using the dedicated 4byte address op codes doesn't change the internal state of the SPI NOR memory as opposed to using other means such as updating a Base Address Register (BAR) and sending command to enter/leave the 4byte mode.

Hence when a CPU reset occurs, early bootloaders don't need to be aware of BAR value or 4byte mode being enabled: they can still access the first 16MiB of the SPI NOR memory using the regular 3byte address op codes.

---
 drivers/mtd/devices/serial_flash_cmds.h |   7 ---
 drivers/mtd/devices/st_spi_fsm.c        |  28 ++++-----
 drivers/mtd/spi-nor/spi-nor.c           | 104 +++++++++++++++++++++++++-------
 include/linux/mtd/spi-nor.h             |  22 +++++--
 4 files changed, 113 insertions(+), 48 deletions(-)
drivers/mtd/devices/serial_flash_cmds.h +0 -7
index f59a125295d0..8b81e15105dd 100644
@@ -18,19 +18,12 @@
 #define SPINOR_OP_RDVCR		0x85
 
 /* JEDEC Standard - Serial Flash Discoverable Parmeters (SFDP) Commands */
-#define SPINOR_OP_READ_1_2_2	0xbb	/* DUAL I/O READ */
-#define SPINOR_OP_READ_1_4_4	0xeb	/* QUAD I/O READ */
-
 #define SPINOR_OP_WRITE		0x02	/* PAGE PROGRAM */
 #define SPINOR_OP_WRITE_1_1_2	0xa2	/* DUAL INPUT PROGRAM */
 #define SPINOR_OP_WRITE_1_2_2	0xd2	/* DUAL INPUT EXT PROGRAM */
 #define SPINOR_OP_WRITE_1_1_4	0x32	/* QUAD INPUT PROGRAM */
 #define SPINOR_OP_WRITE_1_4_4	0x12	/* QUAD INPUT EXT PROGRAM */
 
-/* READ commands with 32-bit addressing */
-#define SPINOR_OP_READ4_1_2_2	0xbc
-#define SPINOR_OP_READ4_1_4_4	0xec
-
 /* Configuration flags */
 #define FLASH_FLAG_SINGLE	0x000000ff
 #define FLASH_FLAG_READ_WRITE	0x00000001
drivers/mtd/devices/st_spi_fsm.c +14 -14
index 5454b4113589..804313a33f2b 100644
@@ -507,13 +507,13 @@ static struct seq_rw_config n25q_read3_configs[] = {
  *	- 'FAST' variants configured for 8 dummy cycles (see note above.)
  */
 static struct seq_rw_config n25q_read4_configs[] = {
-	{FLASH_FLAG_READ_1_4_4, SPINOR_OP_READ4_1_4_4,	0, 4, 4, 0x00, 0, 8},
-	{FLASH_FLAG_READ_1_1_4, SPINOR_OP_READ4_1_1_4,	0, 1, 4, 0x00, 0, 8},
-	{FLASH_FLAG_READ_1_2_2, SPINOR_OP_READ4_1_2_2,	0, 2, 2, 0x00, 0, 8},
-	{FLASH_FLAG_READ_1_1_2, SPINOR_OP_READ4_1_1_2,	0, 1, 2, 0x00, 0, 8},
-	{FLASH_FLAG_READ_FAST,	SPINOR_OP_READ4_FAST,	0, 1, 1, 0x00, 0, 8},
-	{FLASH_FLAG_READ_WRITE, SPINOR_OP_READ4,	0, 1, 1, 0x00, 0, 0},
-	{0x00,			0,			0, 0, 0, 0x00, 0, 0},
+	{FLASH_FLAG_READ_1_4_4, SPINOR_OP_READ_1_4_4_4B, 0, 4, 4, 0x00, 0, 8},
+	{FLASH_FLAG_READ_1_1_4, SPINOR_OP_READ_1_1_4_4B, 0, 1, 4, 0x00, 0, 8},
+	{FLASH_FLAG_READ_1_2_2, SPINOR_OP_READ_1_2_2_4B, 0, 2, 2, 0x00, 0, 8},
+	{FLASH_FLAG_READ_1_1_2, SPINOR_OP_READ_1_1_2_4B, 0, 1, 2, 0x00, 0, 8},
+	{FLASH_FLAG_READ_FAST,	SPINOR_OP_READ_FAST_4B,  0, 1, 1, 0x00, 0, 8},
+	{FLASH_FLAG_READ_WRITE, SPINOR_OP_READ_4B,       0, 1, 1, 0x00, 0, 0},
+	{0x00,			0,                       0, 0, 0, 0x00, 0, 0},
 };
 
 /*
@@ -553,13 +553,13 @@ static int stfsm_mx25_en_32bit_addr_seq(struct stfsm_seq *seq)
  * entering a state that is incompatible with the SPIBoot Controller.
  */
 static struct seq_rw_config stfsm_s25fl_read4_configs[] = {
-	{FLASH_FLAG_READ_1_4_4,  SPINOR_OP_READ4_1_4_4,  0, 4, 4, 0x00, 2, 4},
-	{FLASH_FLAG_READ_1_1_4,  SPINOR_OP_READ4_1_1_4,  0, 1, 4, 0x00, 0, 8},
-	{FLASH_FLAG_READ_1_2_2,  SPINOR_OP_READ4_1_2_2,  0, 2, 2, 0x00, 4, 0},
-	{FLASH_FLAG_READ_1_1_2,  SPINOR_OP_READ4_1_1_2,  0, 1, 2, 0x00, 0, 8},
-	{FLASH_FLAG_READ_FAST,   SPINOR_OP_READ4_FAST,   0, 1, 1, 0x00, 0, 8},
-	{FLASH_FLAG_READ_WRITE,  SPINOR_OP_READ4,        0, 1, 1, 0x00, 0, 0},
-	{0x00,                   0,                      0, 0, 0, 0x00, 0, 0},
+	{FLASH_FLAG_READ_1_4_4,  SPINOR_OP_READ_1_4_4_4B,  0, 4, 4, 0x00, 2, 4},
+	{FLASH_FLAG_READ_1_1_4,  SPINOR_OP_READ_1_1_4_4B,  0, 1, 4, 0x00, 0, 8},
+	{FLASH_FLAG_READ_1_2_2,  SPINOR_OP_READ_1_2_2_4B,  0, 2, 2, 0x00, 4, 0},
+	{FLASH_FLAG_READ_1_1_2,  SPINOR_OP_READ_1_1_2_4B,  0, 1, 2, 0x00, 0, 8},
+	{FLASH_FLAG_READ_FAST,   SPINOR_OP_READ_FAST_4B,   0, 1, 1, 0x00, 0, 8},
+	{FLASH_FLAG_READ_WRITE,  SPINOR_OP_READ_4B,        0, 1, 1, 0x00, 0, 0},
+	{0x00,                   0,                        0, 0, 0, 0x00, 0, 0},
 };
 
 static struct seq_rw_config stfsm_s25fl_write4_configs[] = {
drivers/mtd/spi-nor/spi-nor.c +83 -21

122 lines not on this page · raw message

include/linux/mtd/spi-nor.h +16 -6
index c425c7b4c2a0..8b02fd7864d0 100644
@@ -43,9 +43,13 @@
 #define SPINOR_OP_WRSR		0x01	/* Write status register 1 byte */
 #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 SPI) */
-#define SPINOR_OP_READ_1_1_4	0x6b	/* Read data bytes (Quad SPI) */
+#define SPINOR_OP_READ_1_1_2	0x3b	/* Read data bytes (Dual Output SPI) */
+#define SPINOR_OP_READ_1_2_2	0xbb	/* Read data bytes (Dual I/O SPI) */
+#define SPINOR_OP_READ_1_1_4	0x6b	/* Read data bytes (Quad Output SPI) */
+#define SPINOR_OP_READ_1_4_4	0xeb	/* Read data bytes (Quad I/O SPI) */
 #define SPINOR_OP_PP		0x02	/* Page program (up to 256 bytes) */
+#define SPINOR_OP_PP_1_1_4	0x32	/* Quad page program */
+#define SPINOR_OP_PP_1_4_4	0x38	/* Quad page program */
 #define SPINOR_OP_BE_4K		0x20	/* Erase 4KiB block */
 #define SPINOR_OP_BE_4K_PMC	0xd7	/* Erase 4KiB block on PMC chips */
 #define SPINOR_OP_BE_32K	0x52	/* Erase 32KiB block */
@@ -56,11 +60,17 @@
 #define SPINOR_OP_RDFSR		0x70	/* Read flag status register */
 
 /* 4-byte address opcodes - used on Spansion and some Macronix flashes. */
-#define SPINOR_OP_READ4		0x13	/* Read data bytes (low frequency) */
-#define SPINOR_OP_READ4_FAST	0x0c	/* Read data bytes (high frequency) */
-#define SPINOR_OP_READ4_1_1_2	0x3c	/* Read data bytes (Dual SPI) */
-#define SPINOR_OP_READ4_1_1_4	0x6c	/* Read data bytes (Quad SPI) */
+#define SPINOR_OP_READ_4B	0x13	/* Read data bytes (low frequency) */
+#define SPINOR_OP_READ_FAST_4B	0x0c	/* Read data bytes (high frequency) */
+#define SPINOR_OP_READ_1_1_2_4B	0x3c	/* Read data bytes (Dual Output SPI) */
+#define SPINOR_OP_READ_1_2_2_4B	0xbc	/* Read data bytes (Dual I/O SPI) */
+#define SPINOR_OP_READ_1_1_4_4B	0x6c	/* Read data bytes (Quad Output SPI) */
+#define SPINOR_OP_READ_1_4_4_4B	0xec	/* Read data bytes (Quad I/O SPI) */
 #define SPINOR_OP_PP_4B		0x12	/* Page program (up to 256 bytes) */
+#define SPINOR_OP_PP_1_1_4_4B	0x34	/* Quad page program */
+#define SPINOR_OP_PP_1_4_4_4B	0x3e	/* Quad page program */
+#define SPINOR_OP_BE_4K_4B	0x21	/* Erase 4KiB block */
+#define SPINOR_OP_BE_32K_4B	0x5c	/* Erase 32KiB block */
 #define SPINOR_OP_SE_4B		0xdc	/* Sector erase (usually 64KiB) */
 
 /* Used for SST flashes only. */
Signature · 1 line
2.7.4
#6 lore raw

Re: [PATCH v3 2/9] mtd: spi-nor: add an alternative method to support memory >16MiB

Quoted 33 lines · Cyrille Pitchen

This patch provides an alternative mean to support memory above 16MiB (128Mib) by replacing 3byte address op codes by their associated 4byte address versions.

Using the dedicated 4byte address op codes doesn't change the internal state of the SPI NOR memory as opposed to using other means such as updating a Base Address Register (BAR) and sending command to enter/leave the 4byte mode.

Hence when a CPU reset occurs, early bootloaders don't need to be aware of BAR value or 4byte mode being enabled: they can still access the first 16MiB of the SPI NOR memory using the regular 3byte address op codes.

---
 drivers/mtd/devices/serial_flash_cmds.h |   7 ---
 drivers/mtd/devices/st_spi_fsm.c        |  28 ++++-----
 drivers/mtd/spi-nor/spi-nor.c           | 104 +++++++++++++++++++++++++-------
 include/linux/mtd/spi-nor.h             |  22 +++++--
 4 files changed, 113 insertions(+), 48 deletions(-)
drivers/mtd/devices/serial_flash_cmds.h +0 -3
index f59a125295d0..8b81e15105dd 100644
@@ -18,19 +18,12 @@
 #define SPINOR_OP_RDVCR		0x85
 
 /* JEDEC Standard - Serial Flash Discoverable Parmeters (SFDP) Commands */
-#define SPINOR_OP_READ_1_2_2	0xbb	/* DUAL I/O READ */
-#define SPINOR_OP_READ_1_4_4	0xeb	/* QUAD I/O READ */
-

drivers/mtd/devices/serial_flash_cmds.h line 33

Quoted 10 lines
drivers/mtd/devices/serial_flash_cmds.h +0 -4
 #define SPINOR_OP_WRITE		0x02	/* PAGE PROGRAM */
 #define SPINOR_OP_WRITE_1_1_2	0xa2	/* DUAL INPUT PROGRAM */
 #define SPINOR_OP_WRITE_1_2_2	0xd2	/* DUAL INPUT EXT PROGRAM */
 #define SPINOR_OP_WRITE_1_1_4	0x32	/* QUAD INPUT PROGRAM */
 #define SPINOR_OP_WRITE_1_4_4	0x12	/* QUAD INPUT EXT PROGRAM */
 
-/* READ commands with 32-bit addressing */
-#define SPINOR_OP_READ4_1_2_2	0xbc
-#define SPINOR_OP_READ4_1_4_4	0xec
-

It'd be nice to have this move/rename bit factored out into a separate patch.

Quoted 5 lines
drivers/mtd/devices/serial_flash_cmds.h +0 -0
 /* Configuration flags */
 #define FLASH_FLAG_SINGLE	0x000000ff
 #define FLASH_FLAG_READ_WRITE	0x00000001
drivers/mtd/devices/st_spi_fsm.c +0 -0
index 5454b4113589..804313a33f2b 100644

[...]

drivers/mtd/devices/st_spi_fsm.c

Quoted 9 lines
drivers/mtd/devices/st_spi_fsm.c +9 -0
+static u8 spi_nor_convert_opcode(u8 opcode,
+				 const struct spi_nor_address_entry *entries,
+				 size_t num_entries)
+{
+	int min, max;
+
+	min = 0;
+	max = num_entries - 1;
+	while (min <= max) {

It's really not clear at all what this function does, so please add a comment.

Quoted 9 lines
drivers/mtd/devices/st_spi_fsm.c +9 -0
+		int mid = (min + max) >> 1;
+		const struct spi_nor_address_entry *entry = &entries[mid];
+
+		if (opcode == entry->src_opcode)
+			return entry->dst_opcode;
+
+		if (opcode < entry->src_opcode)
+			max = mid - 1;
+		else

drivers/mtd/devices/st_spi_fsm.c

Quoted 10 lines
drivers/mtd/devices/st_spi_fsm.c +10 -0
+			min = mid + 1;
+	}
+
+	/* No conversion found */
+	return opcode;
+}
+
+static u8 spi_nor_3to4_opcode(u8 opcode)
+{
+	/* MUST be sorted by 3byte opcode */

Because ... why ?

Quoted 1 line

+#define ENTRY_3TO4(_opcode) { _opcode, _opcode##_4B }

Will this break/be unflexible for flashes with some different 4B opcodes ?

Quoted 20 lines
drivers/mtd/devices/st_spi_fsm.c +20 -0
+	static const struct spi_nor_address_entry spi_nor_3to4_table[] = {
+		ENTRY_3TO4(SPINOR_OP_PP),		/* 0x02 */
+		ENTRY_3TO4(SPINOR_OP_READ),		/* 0x03 */
+		ENTRY_3TO4(SPINOR_OP_READ_FAST),	/* 0x0b */
+		ENTRY_3TO4(SPINOR_OP_BE_4K),		/* 0x20 */
+		ENTRY_3TO4(SPINOR_OP_PP_1_1_4),		/* 0x32 */
+		ENTRY_3TO4(SPINOR_OP_PP_1_4_4),		/* 0x38 */
+		ENTRY_3TO4(SPINOR_OP_READ_1_1_2),	/* 0x3b */
+		ENTRY_3TO4(SPINOR_OP_BE_32K),		/* 0x52 */
+		ENTRY_3TO4(SPINOR_OP_READ_1_1_4),	/* 0x6b */
+		ENTRY_3TO4(SPINOR_OP_READ_1_2_2),	/* 0xbb */
+		ENTRY_3TO4(SPINOR_OP_SE),		/* 0xd8 */
+		ENTRY_3TO4(SPINOR_OP_READ_1_4_4),	/* 0xeb */
+	};
+#undef ENTRY_3TO4
+
+	return spi_nor_convert_opcode(opcode, spi_nor_3to4_table,
+				      ARRAY_SIZE(spi_nor_3to4_table));
+}
+

[...]

Signature · 2 lines
Best regards,
Marek Vasut
#7 lore raw

Re: [PATCH v3 2/9] mtd: spi-nor: add an alternative method to support memory >16MiB

Quoted 40 lines · Marek Vasut
Quoted 39 lines · Cyrille Pitchen

This patch provides an alternative mean to support memory above 16MiB (128Mib) by replacing 3byte address op codes by their associated 4byte address versions.

Using the dedicated 4byte address op codes doesn't change the internal state of the SPI NOR memory as opposed to using other means such as updating a Base Address Register (BAR) and sending command to enter/leave the 4byte mode.

Hence when a CPU reset occurs, early bootloaders don't need to be aware of BAR value or 4byte mode being enabled: they can still access the first 16MiB of the SPI NOR memory using the regular 3byte address op codes.

---
 drivers/mtd/devices/serial_flash_cmds.h |   7 ---
 drivers/mtd/devices/st_spi_fsm.c        |  28 ++++-----
 drivers/mtd/spi-nor/spi-nor.c           | 104 +++++++++++++++++++++++++-------
 include/linux/mtd/spi-nor.h             |  22 +++++--
 4 files changed, 113 insertions(+), 48 deletions(-)
drivers/mtd/devices/serial_flash_cmds.h +0 -3
index f59a125295d0..8b81e15105dd 100644
@@ -18,19 +18,12 @@
 #define SPINOR_OP_RDVCR		0x85
 
 /* JEDEC Standard - Serial Flash Discoverable Parmeters (SFDP) Commands */
-#define SPINOR_OP_READ_1_2_2	0xbb	/* DUAL I/O READ */
-#define SPINOR_OP_READ_1_4_4	0xeb	/* QUAD I/O READ */
-
 #define SPINOR_OP_WRITE		0x02	/* PAGE PROGRAM */
 #define SPINOR_OP_WRITE_1_1_2	0xa2	/* DUAL INPUT PROGRAM */
 #define SPINOR_OP_WRITE_1_2_2	0xd2	/* DUAL INPUT EXT PROGRAM */
 #define SPINOR_OP_WRITE_1_1_4	0x32	/* QUAD INPUT PROGRAM */
 #define SPINOR_OP_WRITE_1_4_4	0x12	/* QUAD INPUT EXT PROGRAM */
 
Quoted 8 lines

drivers/mtd/devices/serial_flash_cmds.h line 33

Quoted 4 lines
drivers/mtd/devices/serial_flash_cmds.h +0 -4
-/* READ commands with 32-bit addressing */
-#define SPINOR_OP_READ4_1_2_2	0xbc
-#define SPINOR_OP_READ4_1_4_4	0xec
-

It'd be nice to have this move/rename bit factored out into a separate patch.

OK, I will try to do this. Anyway, this rename seems to break the freshly merged drivers/spi/spi-bcm-qspi.c, which uses the SPINOR_OP_READ4_* macro so I have to do something about this driver. IMHO, the broadcom driver should not select its own op code but use the one provided by read_opcode member of the struct spi_flash_read_message.

Quoted 8 lines
Quoted 5 lines
drivers/mtd/devices/serial_flash_cmds.h +0 -0
 /* Configuration flags */
 #define FLASH_FLAG_SINGLE	0x000000ff
 #define FLASH_FLAG_READ_WRITE	0x00000001
drivers/mtd/devices/st_spi_fsm.c +0 -0
index 5454b4113589..804313a33f2b 100644

[...]

Quoted 13 lines

drivers/mtd/devices/st_spi_fsm.c

Quoted 9 lines
drivers/mtd/devices/st_spi_fsm.c +9 -0
+static u8 spi_nor_convert_opcode(u8 opcode,
+				 const struct spi_nor_address_entry *entries,
+				 size_t num_entries)
+{
+	int min, max;
+
+	min = 0;
+	max = num_entries - 1;
+	while (min <= max) {

It's really not clear at all what this function does, so please add a comment.

This is just a dichotomic search to reduce the number of comparisons: it assumes the entries[] array is sorted by ascending src_opcode.

I will add a comment to clarify this point.

Quoted 13 lines
Quoted 13 lines
drivers/mtd/devices/st_spi_fsm.c +13 -0
+		int mid = (min + max) >> 1;
+		const struct spi_nor_address_entry *entry = &entries[mid];
+
+		if (opcode == entry->src_opcode)
+			return entry->dst_opcode;
+
+		if (opcode < entry->src_opcode)
+			max = mid - 1;
+		else
+			min = mid + 1;
+	}
+
+	/* No conversion found */
Quoted 9 lines

drivers/mtd/devices/st_spi_fsm.c

Quoted 6 lines
drivers/mtd/devices/st_spi_fsm.c +6 -0
+	return opcode;
+}
+
+static u8 spi_nor_3to4_opcode(u8 opcode)
+{
+	/* MUST be sorted by 3byte opcode */

Because ... why ?

As explained above, the dichotomic search implemented in spi_nor_convert_opcode() assumes that the input array is sorted by src_opcode.

Quoted 4 lines
Quoted 1 line

+#define ENTRY_3TO4(_opcode) { _opcode, _opcode##_4B }

Will this break/be unflexible for flashes with some different 4B opcodes ?

Of course I cannot provide any guarantee that it will never happen but currently it seems that all manufacturers use the same op codes. Besides, the 4-byte address instruction set is part the of JESD216B specification, so there is a standard for these op codes and new SPI NOR memories tend to match this specification by providing the SFDP tables.

Indeed this instruction set is maybe one of the few things where all manufacturers seem to agree :)

Quoted 23 lines
Quoted 20 lines
drivers/mtd/devices/st_spi_fsm.c +20 -0
+	static const struct spi_nor_address_entry spi_nor_3to4_table[] = {
+		ENTRY_3TO4(SPINOR_OP_PP),		/* 0x02 */
+		ENTRY_3TO4(SPINOR_OP_READ),		/* 0x03 */
+		ENTRY_3TO4(SPINOR_OP_READ_FAST),	/* 0x0b */
+		ENTRY_3TO4(SPINOR_OP_BE_4K),		/* 0x20 */
+		ENTRY_3TO4(SPINOR_OP_PP_1_1_4),		/* 0x32 */
+		ENTRY_3TO4(SPINOR_OP_PP_1_4_4),		/* 0x38 */
+		ENTRY_3TO4(SPINOR_OP_READ_1_1_2),	/* 0x3b */
+		ENTRY_3TO4(SPINOR_OP_BE_32K),		/* 0x52 */
+		ENTRY_3TO4(SPINOR_OP_READ_1_1_4),	/* 0x6b */
+		ENTRY_3TO4(SPINOR_OP_READ_1_2_2),	/* 0xbb */
+		ENTRY_3TO4(SPINOR_OP_SE),		/* 0xd8 */
+		ENTRY_3TO4(SPINOR_OP_READ_1_4_4),	/* 0xeb */
+	};
+#undef ENTRY_3TO4
+
+	return spi_nor_convert_opcode(opcode, spi_nor_3to4_table,
+				      ARRAY_SIZE(spi_nor_3to4_table));
+}
+

[...]

#8 lore raw

Re: [PATCH v3 2/9] mtd: spi-nor: add an alternative method to support memory >16MiB

Quoted 50 lines · Cyrille Pitchen
Quoted 40 lines · Marek Vasut
Quoted 39 lines · Cyrille Pitchen

This patch provides an alternative mean to support memory above 16MiB (128Mib) by replacing 3byte address op codes by their associated 4byte address versions.

Using the dedicated 4byte address op codes doesn't change the internal state of the SPI NOR memory as opposed to using other means such as updating a Base Address Register (BAR) and sending command to enter/leave the 4byte mode.

Hence when a CPU reset occurs, early bootloaders don't need to be aware of BAR value or 4byte mode being enabled: they can still access the first 16MiB of the SPI NOR memory using the regular 3byte address op codes.

---
 drivers/mtd/devices/serial_flash_cmds.h |   7 ---
 drivers/mtd/devices/st_spi_fsm.c        |  28 ++++-----
 drivers/mtd/spi-nor/spi-nor.c           | 104 +++++++++++++++++++++++++-------
 include/linux/mtd/spi-nor.h             |  22 +++++--
 4 files changed, 113 insertions(+), 48 deletions(-)
drivers/mtd/devices/serial_flash_cmds.h +0 -3
index f59a125295d0..8b81e15105dd 100644
@@ -18,19 +18,12 @@
 #define SPINOR_OP_RDVCR		0x85
 
 /* JEDEC Standard - Serial Flash Discoverable Parmeters (SFDP) Commands */
-#define SPINOR_OP_READ_1_2_2	0xbb	/* DUAL I/O READ */
-#define SPINOR_OP_READ_1_4_4	0xeb	/* QUAD I/O READ */
-
 #define SPINOR_OP_WRITE		0x02	/* PAGE PROGRAM */
 #define SPINOR_OP_WRITE_1_1_2	0xa2	/* DUAL INPUT PROGRAM */
 #define SPINOR_OP_WRITE_1_2_2	0xd2	/* DUAL INPUT EXT PROGRAM */
 #define SPINOR_OP_WRITE_1_1_4	0x32	/* QUAD INPUT PROGRAM */
 #define SPINOR_OP_WRITE_1_4_4	0x12	/* QUAD INPUT EXT PROGRAM */
 
Quoted 8 lines

drivers/mtd/devices/serial_flash_cmds.h line 33

Quoted 4 lines
drivers/mtd/devices/serial_flash_cmds.h +0 -4
-/* READ commands with 32-bit addressing */
-#define SPINOR_OP_READ4_1_2_2	0xbc
-#define SPINOR_OP_READ4_1_4_4	0xec
-

It'd be nice to have this move/rename bit factored out into a separate patch.

Quoted 5 lines

OK, I will try to do this. Anyway, this rename seems to break the freshly merged drivers/spi/spi-bcm-qspi.c, which uses the SPINOR_OP_READ4_* macro so I have to do something about this driver. IMHO, the broadcom driver should not select its own op code but use the one provided by read_opcode member of the struct spi_flash_read_message.

Thanks

Quoted 22 lines
Quoted 8 lines
Quoted 5 lines
drivers/mtd/devices/serial_flash_cmds.h +0 -0
 /* Configuration flags */
 #define FLASH_FLAG_SINGLE	0x000000ff
 #define FLASH_FLAG_READ_WRITE	0x00000001
drivers/mtd/devices/st_spi_fsm.c +0 -0
index 5454b4113589..804313a33f2b 100644

[...]

Quoted 13 lines

drivers/mtd/devices/st_spi_fsm.c

Quoted 9 lines
drivers/mtd/devices/st_spi_fsm.c +9 -0
+static u8 spi_nor_convert_opcode(u8 opcode,
+				 const struct spi_nor_address_entry *entries,
+				 size_t num_entries)
+{
+	int min, max;
+
+	min = 0;
+	max = num_entries - 1;
+	while (min <= max) {

It's really not clear at all what this function does, so please add a comment.

Quoted 4 lines

This is just a dichotomic search to reduce the number of comparisons: it assumes the entries[] array is sorted by ascending src_opcode.

I will add a comment to clarify this point.

Cool, that'd help. Are you invoking this every time an opcode is submitted to the SPI NOR ?

Quoted 23 lines
Quoted 13 lines
Quoted 13 lines
drivers/mtd/devices/st_spi_fsm.c +13 -0
+		int mid = (min + max) >> 1;
+		const struct spi_nor_address_entry *entry = &entries[mid];
+
+		if (opcode == entry->src_opcode)
+			return entry->dst_opcode;
+
+		if (opcode < entry->src_opcode)
+			max = mid - 1;
+		else
+			min = mid + 1;
+	}
+
+	/* No conversion found */
Quoted 9 lines

drivers/mtd/devices/st_spi_fsm.c

Quoted 6 lines
drivers/mtd/devices/st_spi_fsm.c +6 -0
+	return opcode;
+}
+
+static u8 spi_nor_3to4_opcode(u8 opcode)
+{
+	/* MUST be sorted by 3byte opcode */

Because ... why ?

Quoted 2 lines

As explained above, the dichotomic search implemented in spi_nor_convert_opcode() assumes that the input array is sorted by src_opcode.

Maybe you should drop that assumption and do normal traversal of the array ? I feel this is a bit fragile and will break once someone will cluelessly add a new opcode.

... or add a comment with a big WARNING statement.

Quoted 10 lines
Quoted 4 lines
Quoted 1 line

+#define ENTRY_3TO4(_opcode) { _opcode, _opcode##_4B }

Will this break/be unflexible for flashes with some different 4B opcodes ?

Of course I cannot provide any guarantee that it will never happen but currently it seems that all manufacturers use the same op codes. Besides, the 4-byte address instruction set is part the of JESD216B specification, so there is a standard for these op codes and new SPI NOR memories tend to match this specification by providing the SFDP tables.

Great. Still, will the code need an overhaul if some vendor decides to deviate ?

Quoted 2 lines

Indeed this instruction set is maybe one of the few things where all manufacturers seem to agree :)

That's a good start :)

Quoted 24 lines
Quoted 23 lines
Quoted 20 lines
drivers/mtd/devices/st_spi_fsm.c +20 -0
+	static const struct spi_nor_address_entry spi_nor_3to4_table[] = {
+		ENTRY_3TO4(SPINOR_OP_PP),		/* 0x02 */
+		ENTRY_3TO4(SPINOR_OP_READ),		/* 0x03 */
+		ENTRY_3TO4(SPINOR_OP_READ_FAST),	/* 0x0b */
+		ENTRY_3TO4(SPINOR_OP_BE_4K),		/* 0x20 */
+		ENTRY_3TO4(SPINOR_OP_PP_1_1_4),		/* 0x32 */
+		ENTRY_3TO4(SPINOR_OP_PP_1_4_4),		/* 0x38 */
+		ENTRY_3TO4(SPINOR_OP_READ_1_1_2),	/* 0x3b */
+		ENTRY_3TO4(SPINOR_OP_BE_32K),		/* 0x52 */
+		ENTRY_3TO4(SPINOR_OP_READ_1_1_4),	/* 0x6b */
+		ENTRY_3TO4(SPINOR_OP_READ_1_2_2),	/* 0xbb */
+		ENTRY_3TO4(SPINOR_OP_SE),		/* 0xd8 */
+		ENTRY_3TO4(SPINOR_OP_READ_1_4_4),	/* 0xeb */
+	};
+#undef ENTRY_3TO4
+
+	return spi_nor_convert_opcode(opcode, spi_nor_3to4_table,
+				      ARRAY_SIZE(spi_nor_3to4_table));
+}
+

[...]

Signature · 2 lines
Best regards,
Marek Vasut
#9 lore raw

Re: [PATCH v3 2/9] mtd: spi-nor: add an alternative method to support memory >16MiB

In reply to Cyrille Pitchen

Hi Cyrille,

Quoted 207 lines · Cyrille Pitchen

This patch provides an alternative mean to support memory above 16MiB (128Mib) by replacing 3byte address op codes by their associated 4byte address versions.

Using the dedicated 4byte address op codes doesn't change the internal state of the SPI NOR memory as opposed to using other means such as updating a Base Address Register (BAR) and sending command to enter/leave the 4byte mode.

Hence when a CPU reset occurs, early bootloaders don't need to be aware of BAR value or 4byte mode being enabled: they can still access the first 16MiB of the SPI NOR memory using the regular 3byte address op codes.

---
 drivers/mtd/devices/serial_flash_cmds.h |   7 ---
 drivers/mtd/devices/st_spi_fsm.c        |  28 ++++-----
 drivers/mtd/spi-nor/spi-nor.c           | 104 +++++++++++++++++++++++++-------
 include/linux/mtd/spi-nor.h             |  22 +++++--
 4 files changed, 113 insertions(+), 48 deletions(-)
drivers/mtd/devices/serial_flash_cmds.h +0 -7
index f59a125295d0..8b81e15105dd 100644
@@ -18,19 +18,12 @@
 #define SPINOR_OP_RDVCR                0x85
 
 /* JEDEC Standard - Serial Flash Discoverable Parmeters (SFDP) Commands */
-#define SPINOR_OP_READ_1_2_2   0xbb    /* DUAL I/O READ */
-#define SPINOR_OP_READ_1_4_4   0xeb    /* QUAD I/O READ */
-
 #define SPINOR_OP_WRITE                0x02    /* PAGE PROGRAM */
 #define SPINOR_OP_WRITE_1_1_2  0xa2    /* DUAL INPUT PROGRAM */
 #define SPINOR_OP_WRITE_1_2_2  0xd2    /* DUAL INPUT EXT PROGRAM */
 #define SPINOR_OP_WRITE_1_1_4  0x32    /* QUAD INPUT PROGRAM */
 #define SPINOR_OP_WRITE_1_4_4  0x12    /* QUAD INPUT EXT PROGRAM */
 
-/* READ commands with 32-bit addressing */
-#define SPINOR_OP_READ4_1_2_2  0xbc
-#define SPINOR_OP_READ4_1_4_4  0xec
-
 /* Configuration flags */
 #define FLASH_FLAG_SINGLE      0x000000ff
 #define FLASH_FLAG_READ_WRITE  0x00000001
drivers/mtd/devices/st_spi_fsm.c +14 -14
index 5454b4113589..804313a33f2b 100644
@@ -507,13 +507,13 @@ static struct seq_rw_config n25q_read3_configs[] = {
  *     - 'FAST' variants configured for 8 dummy cycles (see note above.)
  */
 static struct seq_rw_config n25q_read4_configs[] = {
-       {FLASH_FLAG_READ_1_4_4, SPINOR_OP_READ4_1_4_4,  0, 4, 4, 0x00, 0, 8},
-       {FLASH_FLAG_READ_1_1_4, SPINOR_OP_READ4_1_1_4,  0, 1, 4, 0x00, 0, 8},
-       {FLASH_FLAG_READ_1_2_2, SPINOR_OP_READ4_1_2_2,  0, 2, 2, 0x00, 0, 8},
-       {FLASH_FLAG_READ_1_1_2, SPINOR_OP_READ4_1_1_2,  0, 1, 2, 0x00, 0, 8},
-       {FLASH_FLAG_READ_FAST,  SPINOR_OP_READ4_FAST,   0, 1, 1, 0x00, 0, 8},
-       {FLASH_FLAG_READ_WRITE, SPINOR_OP_READ4,        0, 1, 1, 0x00, 0, 0},
-       {0x00,                  0,                      0, 0, 0, 0x00, 0, 0},
+       {FLASH_FLAG_READ_1_4_4, SPINOR_OP_READ_1_4_4_4B, 0, 4, 4, 0x00, 0, 8},
+       {FLASH_FLAG_READ_1_1_4, SPINOR_OP_READ_1_1_4_4B, 0, 1, 4, 0x00, 0, 8},
+       {FLASH_FLAG_READ_1_2_2, SPINOR_OP_READ_1_2_2_4B, 0, 2, 2, 0x00, 0, 8},
+       {FLASH_FLAG_READ_1_1_2, SPINOR_OP_READ_1_1_2_4B, 0, 1, 2, 0x00, 0, 8},
+       {FLASH_FLAG_READ_FAST,  SPINOR_OP_READ_FAST_4B,  0, 1, 1, 0x00, 0, 8},
+       {FLASH_FLAG_READ_WRITE, SPINOR_OP_READ_4B,       0, 1, 1, 0x00, 0, 0},
+       {0x00,                  0,                       0, 0, 0, 0x00, 0, 0},
 };
 
 /*
@@ -553,13 +553,13 @@ static int stfsm_mx25_en_32bit_addr_seq(struct stfsm_seq *seq)
  * entering a state that is incompatible with the SPIBoot Controller.
  */
 static struct seq_rw_config stfsm_s25fl_read4_configs[] = {
-       {FLASH_FLAG_READ_1_4_4,  SPINOR_OP_READ4_1_4_4,  0, 4, 4, 0x00, 2, 4},
-       {FLASH_FLAG_READ_1_1_4,  SPINOR_OP_READ4_1_1_4,  0, 1, 4, 0x00, 0, 8},
-       {FLASH_FLAG_READ_1_2_2,  SPINOR_OP_READ4_1_2_2,  0, 2, 2, 0x00, 4, 0},
-       {FLASH_FLAG_READ_1_1_2,  SPINOR_OP_READ4_1_1_2,  0, 1, 2, 0x00, 0, 8},
-       {FLASH_FLAG_READ_FAST,   SPINOR_OP_READ4_FAST,   0, 1, 1, 0x00, 0, 8},
-       {FLASH_FLAG_READ_WRITE,  SPINOR_OP_READ4,        0, 1, 1, 0x00, 0, 0},
-       {0x00,                   0,                      0, 0, 0, 0x00, 0, 0},
+       {FLASH_FLAG_READ_1_4_4,  SPINOR_OP_READ_1_4_4_4B,  0, 4, 4, 0x00, 2, 4},
+       {FLASH_FLAG_READ_1_1_4,  SPINOR_OP_READ_1_1_4_4B,  0, 1, 4, 0x00, 0, 8},
+       {FLASH_FLAG_READ_1_2_2,  SPINOR_OP_READ_1_2_2_4B,  0, 2, 2, 0x00, 4, 0},
+       {FLASH_FLAG_READ_1_1_2,  SPINOR_OP_READ_1_1_2_4B,  0, 1, 2, 0x00, 0, 8},
+       {FLASH_FLAG_READ_FAST,   SPINOR_OP_READ_FAST_4B,   0, 1, 1, 0x00, 0, 8},
+       {FLASH_FLAG_READ_WRITE,  SPINOR_OP_READ_4B,        0, 1, 1, 0x00, 0, 0},
+       {0x00,                   0,                        0, 0, 0, 0x00, 0, 0},
 };
 
 static struct seq_rw_config stfsm_s25fl_write4_configs[] = {
drivers/mtd/spi-nor/spi-nor.c +79 -14
index 5c87b2d99507..423448c1c7a8 100644
@@ -75,6 +75,10 @@ struct flash_info {
                                         * bit. Must be used with
                                         * SPI_NOR_HAS_LOCK.
                                         */
+#define SPI_NOR_4B_OPCODES     BIT(10) /*
+                                        * Use dedicated 4byte address op codes
+                                        * to support memory size above 128Mib.
+                                        */
 };
 
 #define JEDEC_MFR(info)        ((info)->id[0])
@@ -188,6 +192,81 @@ static inline struct spi_nor *mtd_to_spi_nor(struct mtd_info *mtd)
        return mtd->priv;
 }
 
+
+struct spi_nor_address_entry {
+       u8      src_opcode;
+       u8      dst_opcode;
+};
+
+static u8 spi_nor_convert_opcode(u8 opcode,
+                                const struct spi_nor_address_entry *entries,
+                                size_t num_entries)
+{
+       int min, max;
+
+       min = 0;
+       max = num_entries - 1;
+       while (min <= max) {
+               int mid = (min + max) >> 1;
+               const struct spi_nor_address_entry *entry = &entries[mid];
+
+               if (opcode == entry->src_opcode)
+                       return entry->dst_opcode;
+
+               if (opcode < entry->src_opcode)
+                       max = mid - 1;
+               else
+                       min = mid + 1;
+       }
+
+       /* No conversion found */
+       return opcode;
+}
+
+static u8 spi_nor_3to4_opcode(u8 opcode)
+{
+       /* MUST be sorted by 3byte opcode */
+#define ENTRY_3TO4(_opcode)    { _opcode, _opcode##_4B }
+       static const struct spi_nor_address_entry spi_nor_3to4_table[] = {
+               ENTRY_3TO4(SPINOR_OP_PP),               /* 0x02 */
+               ENTRY_3TO4(SPINOR_OP_READ),             /* 0x03 */
+               ENTRY_3TO4(SPINOR_OP_READ_FAST),        /* 0x0b */
+               ENTRY_3TO4(SPINOR_OP_BE_4K),            /* 0x20 */
+               ENTRY_3TO4(SPINOR_OP_PP_1_1_4),         /* 0x32 */
+               ENTRY_3TO4(SPINOR_OP_PP_1_4_4),         /* 0x38 */
+               ENTRY_3TO4(SPINOR_OP_READ_1_1_2),       /* 0x3b */
+               ENTRY_3TO4(SPINOR_OP_BE_32K),           /* 0x52 */
+               ENTRY_3TO4(SPINOR_OP_READ_1_1_4),       /* 0x6b */
+               ENTRY_3TO4(SPINOR_OP_READ_1_2_2),       /* 0xbb */
+               ENTRY_3TO4(SPINOR_OP_SE),               /* 0xd8 */
+               ENTRY_3TO4(SPINOR_OP_READ_1_4_4),       /* 0xeb */
+       };
+#undef ENTRY_3TO4
+
+       return spi_nor_convert_opcode(opcode, spi_nor_3to4_table,
+                                     ARRAY_SIZE(spi_nor_3to4_table));
+}
+
+static void spi_nor_set_4byte_opcodes(struct spi_nor *nor,
+                                     const struct flash_info *info)
+{
+       /* Do some manufacturer fixups first */
+       switch (JEDEC_MFR(info)) {
+       case SNOR_MFR_SPANSION:
+               /* No small sector erase for 4-byte command set */
+               nor->erase_opcode = SPINOR_OP_SE;
+               nor->mtd.erasesize = info->sector_size;
+               break;
+
+       default:
+               break;
+       }
+
+       nor->read_opcode        = spi_nor_3to4_opcode(nor->read_opcode);
+       nor->program_opcode     = spi_nor_3to4_opcode(nor->program_opcode);
+       nor->erase_opcode       = spi_nor_3to4_opcode(nor->erase_opcode);
+}
+
 /* Enable/disable 4-byte addressing mode. */
 static inline int set_4byte(struct spi_nor *nor, const struct flash_info *info,
                            int enable)
@@ -1476,27 +1555,10 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
        else if (mtd->size > 0x1000000) {
                /* enable 4-byte addressing if the device exceeds 16MiB */
                nor->addr_width = 4;
-               if (JEDEC_MFR(info) == SNOR_MFR_SPANSION) {
-                       /* Dedicated 4-byte command set */
-                       switch (nor->flash_read) {
-                       case SPI_NOR_QUAD:
-                               nor->read_opcode = SPINOR_OP_READ4_1_1_4;
-                               break;
-                       case SPI_NOR_DUAL:
-                               nor->read_opcode = SPINOR_OP_READ4_1_1_2;
-                               break;
-                       case SPI_NOR_FAST:
-                               nor->read_opcode = SPINOR_OP_READ4_FAST;
-                               break;
-                       case SPI_NOR_NORMAL:
-                               nor->read_opcode = SPINOR_OP_READ4;

drivers/mtd/spi-nor/spi-nor.c int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode) · line 1560

Quoted 10 lines
drivers/mtd/spi-nor/spi-nor.c +3 -7
-                               break;
-                       }
-                       nor->program_opcode = SPINOR_OP_PP_4B;
-                       /* No small sector erase for 4-byte command set */
-                       nor->erase_opcode = SPINOR_OP_SE_4B;
-                       mtd->erasesize = info->sector_size;
-               } else
+               if (JEDEC_MFR(info) == SNOR_MFR_SPANSION ||
+                   info->flags & SPI_NOR_4B_OPCODES)
+                       spi_nor_set_4byte_opcodes(nor, info);

I am giving my inputs based on the discussion on this thread[1].

Seems like winbond[2], stmicron, spansion look better way or equally same for accessing 4-byte addressing commands. and I find few of the Macronix [3] have support of 4B addressing(see Table 5) but lack/unable to find the 4B opcodes.

And about BAR support, based on my experience in u-boot and research on above chips only require when controller isn't supporting 4-byte addressing even if connected flash chip has > 16MiB, that means there is no exact or equivalent requirement for flash side either.

So, adding the flags on flash table might be the good option instead making code to convert 3B opcode to 4B because anyway the chips require SPI_NOR_4B_OPCODES and also except winbond, stmicron, spansion not too many flash needed > 16MiB support as of now.

[1] https://lkml.org/lkml/2016/10/24/276
[2] https://www.winbond.com/resource-files/w25q256fv_revg1_120214_qpi_website_rev_g.pdf
[3] http://www.zlgmcu.com/mxic/pdf/NOR_Flash_c/MX25L25635E_DS_EN.pdf

thanks!

Signature · 4 lines
Jagan Teki
Free Software Engineer | www.openedev.com
U-Boot, Linux | Upstream Maintainer
Hyderabad, India.
#10 lore raw

Re: [PATCH v3 2/9] mtd: spi-nor: add an alternative method to support memory >16MiB

Hi,

Quoted 224 lines · Jagan Teki

Hi Cyrille,

Quoted 207 lines · Cyrille Pitchen

This patch provides an alternative mean to support memory above 16MiB (128Mib) by replacing 3byte address op codes by their associated 4byte address versions.

Using the dedicated 4byte address op codes doesn't change the internal state of the SPI NOR memory as opposed to using other means such as updating a Base Address Register (BAR) and sending command to enter/leave the 4byte mode.

Hence when a CPU reset occurs, early bootloaders don't need to be aware of BAR value or 4byte mode being enabled: they can still access the first 16MiB of the SPI NOR memory using the regular 3byte address op codes.

---
 drivers/mtd/devices/serial_flash_cmds.h |   7 ---
 drivers/mtd/devices/st_spi_fsm.c        |  28 ++++-----
 drivers/mtd/spi-nor/spi-nor.c           | 104 +++++++++++++++++++++++++-------
 include/linux/mtd/spi-nor.h             |  22 +++++--
 4 files changed, 113 insertions(+), 48 deletions(-)
drivers/mtd/devices/serial_flash_cmds.h +0 -7
index f59a125295d0..8b81e15105dd 100644
@@ -18,19 +18,12 @@
 #define SPINOR_OP_RDVCR                0x85
 
 /* JEDEC Standard - Serial Flash Discoverable Parmeters (SFDP) Commands */
-#define SPINOR_OP_READ_1_2_2   0xbb    /* DUAL I/O READ */
-#define SPINOR_OP_READ_1_4_4   0xeb    /* QUAD I/O READ */
-
 #define SPINOR_OP_WRITE                0x02    /* PAGE PROGRAM */
 #define SPINOR_OP_WRITE_1_1_2  0xa2    /* DUAL INPUT PROGRAM */
 #define SPINOR_OP_WRITE_1_2_2  0xd2    /* DUAL INPUT EXT PROGRAM */
 #define SPINOR_OP_WRITE_1_1_4  0x32    /* QUAD INPUT PROGRAM */
 #define SPINOR_OP_WRITE_1_4_4  0x12    /* QUAD INPUT EXT PROGRAM */
 
-/* READ commands with 32-bit addressing */
-#define SPINOR_OP_READ4_1_2_2  0xbc
-#define SPINOR_OP_READ4_1_4_4  0xec
-
 /* Configuration flags */
 #define FLASH_FLAG_SINGLE      0x000000ff
 #define FLASH_FLAG_READ_WRITE  0x00000001
drivers/mtd/devices/st_spi_fsm.c +14 -14
index 5454b4113589..804313a33f2b 100644
@@ -507,13 +507,13 @@ static struct seq_rw_config n25q_read3_configs[] = {
  *     - 'FAST' variants configured for 8 dummy cycles (see note above.)
  */
 static struct seq_rw_config n25q_read4_configs[] = {
-       {FLASH_FLAG_READ_1_4_4, SPINOR_OP_READ4_1_4_4,  0, 4, 4, 0x00, 0, 8},
-       {FLASH_FLAG_READ_1_1_4, SPINOR_OP_READ4_1_1_4,  0, 1, 4, 0x00, 0, 8},
-       {FLASH_FLAG_READ_1_2_2, SPINOR_OP_READ4_1_2_2,  0, 2, 2, 0x00, 0, 8},
-       {FLASH_FLAG_READ_1_1_2, SPINOR_OP_READ4_1_1_2,  0, 1, 2, 0x00, 0, 8},
-       {FLASH_FLAG_READ_FAST,  SPINOR_OP_READ4_FAST,   0, 1, 1, 0x00, 0, 8},
-       {FLASH_FLAG_READ_WRITE, SPINOR_OP_READ4,        0, 1, 1, 0x00, 0, 0},
-       {0x00,                  0,                      0, 0, 0, 0x00, 0, 0},
+       {FLASH_FLAG_READ_1_4_4, SPINOR_OP_READ_1_4_4_4B, 0, 4, 4, 0x00, 0, 8},
+       {FLASH_FLAG_READ_1_1_4, SPINOR_OP_READ_1_1_4_4B, 0, 1, 4, 0x00, 0, 8},
+       {FLASH_FLAG_READ_1_2_2, SPINOR_OP_READ_1_2_2_4B, 0, 2, 2, 0x00, 0, 8},
+       {FLASH_FLAG_READ_1_1_2, SPINOR_OP_READ_1_1_2_4B, 0, 1, 2, 0x00, 0, 8},
+       {FLASH_FLAG_READ_FAST,  SPINOR_OP_READ_FAST_4B,  0, 1, 1, 0x00, 0, 8},
+       {FLASH_FLAG_READ_WRITE, SPINOR_OP_READ_4B,       0, 1, 1, 0x00, 0, 0},
+       {0x00,                  0,                       0, 0, 0, 0x00, 0, 0},
 };
 
 /*
@@ -553,13 +553,13 @@ static int stfsm_mx25_en_32bit_addr_seq(struct stfsm_seq *seq)
  * entering a state that is incompatible with the SPIBoot Controller.
  */
 static struct seq_rw_config stfsm_s25fl_read4_configs[] = {
-       {FLASH_FLAG_READ_1_4_4,  SPINOR_OP_READ4_1_4_4,  0, 4, 4, 0x00, 2, 4},
-       {FLASH_FLAG_READ_1_1_4,  SPINOR_OP_READ4_1_1_4,  0, 1, 4, 0x00, 0, 8},
-       {FLASH_FLAG_READ_1_2_2,  SPINOR_OP_READ4_1_2_2,  0, 2, 2, 0x00, 4, 0},
-       {FLASH_FLAG_READ_1_1_2,  SPINOR_OP_READ4_1_1_2,  0, 1, 2, 0x00, 0, 8},
-       {FLASH_FLAG_READ_FAST,   SPINOR_OP_READ4_FAST,   0, 1, 1, 0x00, 0, 8},
-       {FLASH_FLAG_READ_WRITE,  SPINOR_OP_READ4,        0, 1, 1, 0x00, 0, 0},
-       {0x00,                   0,                      0, 0, 0, 0x00, 0, 0},
+       {FLASH_FLAG_READ_1_4_4,  SPINOR_OP_READ_1_4_4_4B,  0, 4, 4, 0x00, 2, 4},
+       {FLASH_FLAG_READ_1_1_4,  SPINOR_OP_READ_1_1_4_4B,  0, 1, 4, 0x00, 0, 8},
+       {FLASH_FLAG_READ_1_2_2,  SPINOR_OP_READ_1_2_2_4B,  0, 2, 2, 0x00, 4, 0},
+       {FLASH_FLAG_READ_1_1_2,  SPINOR_OP_READ_1_1_2_4B,  0, 1, 2, 0x00, 0, 8},
+       {FLASH_FLAG_READ_FAST,   SPINOR_OP_READ_FAST_4B,   0, 1, 1, 0x00, 0, 8},
+       {FLASH_FLAG_READ_WRITE,  SPINOR_OP_READ_4B,        0, 1, 1, 0x00, 0, 0},
+       {0x00,                   0,                        0, 0, 0, 0x00, 0, 0},
 };
 
 static struct seq_rw_config stfsm_s25fl_write4_configs[] = {
drivers/mtd/spi-nor/spi-nor.c +79 -14
index 5c87b2d99507..423448c1c7a8 100644
@@ -75,6 +75,10 @@ struct flash_info {
                                         * bit. Must be used with
                                         * SPI_NOR_HAS_LOCK.
                                         */
+#define SPI_NOR_4B_OPCODES     BIT(10) /*
+                                        * Use dedicated 4byte address op codes
+                                        * to support memory size above 128Mib.
+                                        */
 };
 
 #define JEDEC_MFR(info)        ((info)->id[0])
@@ -188,6 +192,81 @@ static inline struct spi_nor *mtd_to_spi_nor(struct mtd_info *mtd)
        return mtd->priv;
 }
 
+
+struct spi_nor_address_entry {
+       u8      src_opcode;
+       u8      dst_opcode;
+};
+
+static u8 spi_nor_convert_opcode(u8 opcode,
+                                const struct spi_nor_address_entry *entries,
+                                size_t num_entries)
+{
+       int min, max;
+
+       min = 0;
+       max = num_entries - 1;
+       while (min <= max) {
+               int mid = (min + max) >> 1;
+               const struct spi_nor_address_entry *entry = &entries[mid];
+
+               if (opcode == entry->src_opcode)
+                       return entry->dst_opcode;
+
+               if (opcode < entry->src_opcode)
+                       max = mid - 1;
+               else
+                       min = mid + 1;
+       }
+
+       /* No conversion found */
+       return opcode;
+}
+
+static u8 spi_nor_3to4_opcode(u8 opcode)
+{
+       /* MUST be sorted by 3byte opcode */
+#define ENTRY_3TO4(_opcode)    { _opcode, _opcode##_4B }
+       static const struct spi_nor_address_entry spi_nor_3to4_table[] = {
+               ENTRY_3TO4(SPINOR_OP_PP),               /* 0x02 */
+               ENTRY_3TO4(SPINOR_OP_READ),             /* 0x03 */
+               ENTRY_3TO4(SPINOR_OP_READ_FAST),        /* 0x0b */
+               ENTRY_3TO4(SPINOR_OP_BE_4K),            /* 0x20 */
+               ENTRY_3TO4(SPINOR_OP_PP_1_1_4),         /* 0x32 */
+               ENTRY_3TO4(SPINOR_OP_PP_1_4_4),         /* 0x38 */
+               ENTRY_3TO4(SPINOR_OP_READ_1_1_2),       /* 0x3b */
+               ENTRY_3TO4(SPINOR_OP_BE_32K),           /* 0x52 */
+               ENTRY_3TO4(SPINOR_OP_READ_1_1_4),       /* 0x6b */
+               ENTRY_3TO4(SPINOR_OP_READ_1_2_2),       /* 0xbb */
+               ENTRY_3TO4(SPINOR_OP_SE),               /* 0xd8 */
+               ENTRY_3TO4(SPINOR_OP_READ_1_4_4),       /* 0xeb */
+       };
+#undef ENTRY_3TO4
+
+       return spi_nor_convert_opcode(opcode, spi_nor_3to4_table,
+                                     ARRAY_SIZE(spi_nor_3to4_table));
+}
+
+static void spi_nor_set_4byte_opcodes(struct spi_nor *nor,
+                                     const struct flash_info *info)
+{
+       /* Do some manufacturer fixups first */
+       switch (JEDEC_MFR(info)) {
+       case SNOR_MFR_SPANSION:
+               /* No small sector erase for 4-byte command set */
+               nor->erase_opcode = SPINOR_OP_SE;
+               nor->mtd.erasesize = info->sector_size;
+               break;
+
+       default:
+               break;
+       }
+
+       nor->read_opcode        = spi_nor_3to4_opcode(nor->read_opcode);
+       nor->program_opcode     = spi_nor_3to4_opcode(nor->program_opcode);
+       nor->erase_opcode       = spi_nor_3to4_opcode(nor->erase_opcode);
+}
+
 /* Enable/disable 4-byte addressing mode. */
 static inline int set_4byte(struct spi_nor *nor, const struct flash_info *info,
                            int enable)
@@ -1476,27 +1555,10 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
        else if (mtd->size > 0x1000000) {
                /* enable 4-byte addressing if the device exceeds 16MiB */
                nor->addr_width = 4;
-               if (JEDEC_MFR(info) == SNOR_MFR_SPANSION) {
-                       /* Dedicated 4-byte command set */
-                       switch (nor->flash_read) {
-                       case SPI_NOR_QUAD:
-                               nor->read_opcode = SPINOR_OP_READ4_1_1_4;
-                               break;
-                       case SPI_NOR_DUAL:
-                               nor->read_opcode = SPINOR_OP_READ4_1_1_2;
-                               break;
-                       case SPI_NOR_FAST:
-                               nor->read_opcode = SPINOR_OP_READ4_FAST;
-                               break;
-                       case SPI_NOR_NORMAL:
-                               nor->read_opcode = SPINOR_OP_READ4;

drivers/mtd/spi-nor/spi-nor.c int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode) · line 1560

Quoted 10 lines
drivers/mtd/spi-nor/spi-nor.c +3 -7
-                               break;
-                       }
-                       nor->program_opcode = SPINOR_OP_PP_4B;
-                       /* No small sector erase for 4-byte command set */
-                       nor->erase_opcode = SPINOR_OP_SE_4B;
-                       mtd->erasesize = info->sector_size;
-               } else
+               if (JEDEC_MFR(info) == SNOR_MFR_SPANSION ||
+                   info->flags & SPI_NOR_4B_OPCODES)
+                       spi_nor_set_4byte_opcodes(nor, info);

I am giving my inputs based on the discussion on this thread[1].

Quoted 5 lines

Seems like winbond[2], stmicron, spansion look better way or equally same for accessing 4-byte addressing commands. and I find few of the Macronix [3] have support of 4B addressing(see Table 5) but lack/unable to find the 4B opcodes.

The Macronix mx25l25635e has no 4-byte address op codes. Instead you can use the EN4B (enter 4-byte mode) B7h op code to make the memory enter its 4-byte mode: hence you still write one of the 3-byte address op code but now this op code is followed by a 4-byte address.
Currently the spi-nor framework uses this EN4B op code for all but Spansion memories. However entering this 4-byte mode is statefull.
For the Macronix 35e won't be able to use the 4-byte address instruction set and we will keep on entering the 4-byte mode.

The idea behind the patch is to use the 4-byte address instruction set when we are absolutely sure this set is supported by a given memory but keep the current behaviour for other memories.

Quoted 10 lines

And about BAR support, based on my experience in u-boot and research on above chips only require when controller isn't supporting 4-byte addressing even if connected flash chip has > 16MiB, that means there is no exact or equivalent requirement for flash side either.

So, adding the flags on flash table might be the good option instead making code to convert 3B opcode to 4B because anyway the chips require SPI_NOR_4B_OPCODES and also except winbond, stmicron, spansion not too many flash needed > 16MiB support as of now.

Sorry but I don't understand what you mean or suggest here!

Quoted 6 lines

[1] https://lkml.org/lkml/2016/10/24/276
[2] https://www.winbond.com/resource-files/w25q256fv_revg1_120214_qpi_website_rev_g.pdf
[3] http://www.zlgmcu.com/mxic/pdf/NOR_Flash_c/MX25L25635E_DS_EN.pdf

thanks!

#11 lore raw

Re: [PATCH v3 2/9] mtd: spi-nor: add an alternative method to support memory >16MiB

Quoted 251 lines · Cyrille Pitchen

Hi,

Quoted 224 lines · Jagan Teki

Hi Cyrille,

Quoted 207 lines · Cyrille Pitchen

This patch provides an alternative mean to support memory above 16MiB (128Mib) by replacing 3byte address op codes by their associated 4byte address versions.

Using the dedicated 4byte address op codes doesn't change the internal state of the SPI NOR memory as opposed to using other means such as updating a Base Address Register (BAR) and sending command to enter/leave the 4byte mode.

Hence when a CPU reset occurs, early bootloaders don't need to be aware of BAR value or 4byte mode being enabled: they can still access the first 16MiB of the SPI NOR memory using the regular 3byte address op codes.

---
 drivers/mtd/devices/serial_flash_cmds.h |   7 ---
 drivers/mtd/devices/st_spi_fsm.c        |  28 ++++-----
 drivers/mtd/spi-nor/spi-nor.c           | 104 +++++++++++++++++++++++++-------
 include/linux/mtd/spi-nor.h             |  22 +++++--
 4 files changed, 113 insertions(+), 48 deletions(-)
drivers/mtd/devices/serial_flash_cmds.h +0 -7
index f59a125295d0..8b81e15105dd 100644
@@ -18,19 +18,12 @@
 #define SPINOR_OP_RDVCR                0x85
 
 /* JEDEC Standard - Serial Flash Discoverable Parmeters (SFDP) Commands */
-#define SPINOR_OP_READ_1_2_2   0xbb    /* DUAL I/O READ */
-#define SPINOR_OP_READ_1_4_4   0xeb    /* QUAD I/O READ */
-
 #define SPINOR_OP_WRITE                0x02    /* PAGE PROGRAM */
 #define SPINOR_OP_WRITE_1_1_2  0xa2    /* DUAL INPUT PROGRAM */
 #define SPINOR_OP_WRITE_1_2_2  0xd2    /* DUAL INPUT EXT PROGRAM */
 #define SPINOR_OP_WRITE_1_1_4  0x32    /* QUAD INPUT PROGRAM */
 #define SPINOR_OP_WRITE_1_4_4  0x12    /* QUAD INPUT EXT PROGRAM */
 
-/* READ commands with 32-bit addressing */
-#define SPINOR_OP_READ4_1_2_2  0xbc
-#define SPINOR_OP_READ4_1_4_4  0xec
-
 /* Configuration flags */
 #define FLASH_FLAG_SINGLE      0x000000ff
 #define FLASH_FLAG_READ_WRITE  0x00000001
drivers/mtd/devices/st_spi_fsm.c +14 -14
index 5454b4113589..804313a33f2b 100644
@@ -507,13 +507,13 @@ static struct seq_rw_config n25q_read3_configs[] = {
  *     - 'FAST' variants configured for 8 dummy cycles (see note above.)
  */
 static struct seq_rw_config n25q_read4_configs[] = {
-       {FLASH_FLAG_READ_1_4_4, SPINOR_OP_READ4_1_4_4,  0, 4, 4, 0x00, 0, 8},
-       {FLASH_FLAG_READ_1_1_4, SPINOR_OP_READ4_1_1_4,  0, 1, 4, 0x00, 0, 8},
-       {FLASH_FLAG_READ_1_2_2, SPINOR_OP_READ4_1_2_2,  0, 2, 2, 0x00, 0, 8},
-       {FLASH_FLAG_READ_1_1_2, SPINOR_OP_READ4_1_1_2,  0, 1, 2, 0x00, 0, 8},
-       {FLASH_FLAG_READ_FAST,  SPINOR_OP_READ4_FAST,   0, 1, 1, 0x00, 0, 8},
-       {FLASH_FLAG_READ_WRITE, SPINOR_OP_READ4,        0, 1, 1, 0x00, 0, 0},
-       {0x00,                  0,                      0, 0, 0, 0x00, 0, 0},
+       {FLASH_FLAG_READ_1_4_4, SPINOR_OP_READ_1_4_4_4B, 0, 4, 4, 0x00, 0, 8},
+       {FLASH_FLAG_READ_1_1_4, SPINOR_OP_READ_1_1_4_4B, 0, 1, 4, 0x00, 0, 8},
+       {FLASH_FLAG_READ_1_2_2, SPINOR_OP_READ_1_2_2_4B, 0, 2, 2, 0x00, 0, 8},
+       {FLASH_FLAG_READ_1_1_2, SPINOR_OP_READ_1_1_2_4B, 0, 1, 2, 0x00, 0, 8},
+       {FLASH_FLAG_READ_FAST,  SPINOR_OP_READ_FAST_4B,  0, 1, 1, 0x00, 0, 8},
+       {FLASH_FLAG_READ_WRITE, SPINOR_OP_READ_4B,       0, 1, 1, 0x00, 0, 0},
+       {0x00,                  0,                       0, 0, 0, 0x00, 0, 0},
 };
 
 /*
@@ -553,13 +553,13 @@ static int stfsm_mx25_en_32bit_addr_seq(struct stfsm_seq *seq)
  * entering a state that is incompatible with the SPIBoot Controller.
  */
 static struct seq_rw_config stfsm_s25fl_read4_configs[] = {
-       {FLASH_FLAG_READ_1_4_4,  SPINOR_OP_READ4_1_4_4,  0, 4, 4, 0x00, 2, 4},
-       {FLASH_FLAG_READ_1_1_4,  SPINOR_OP_READ4_1_1_4,  0, 1, 4, 0x00, 0, 8},
-       {FLASH_FLAG_READ_1_2_2,  SPINOR_OP_READ4_1_2_2,  0, 2, 2, 0x00, 4, 0},
-       {FLASH_FLAG_READ_1_1_2,  SPINOR_OP_READ4_1_1_2,  0, 1, 2, 0x00, 0, 8},
-       {FLASH_FLAG_READ_FAST,   SPINOR_OP_READ4_FAST,   0, 1, 1, 0x00, 0, 8},
-       {FLASH_FLAG_READ_WRITE,  SPINOR_OP_READ4,        0, 1, 1, 0x00, 0, 0},
-       {0x00,                   0,                      0, 0, 0, 0x00, 0, 0},
+       {FLASH_FLAG_READ_1_4_4,  SPINOR_OP_READ_1_4_4_4B,  0, 4, 4, 0x00, 2, 4},
+       {FLASH_FLAG_READ_1_1_4,  SPINOR_OP_READ_1_1_4_4B,  0, 1, 4, 0x00, 0, 8},
+       {FLASH_FLAG_READ_1_2_2,  SPINOR_OP_READ_1_2_2_4B,  0, 2, 2, 0x00, 4, 0},
+       {FLASH_FLAG_READ_1_1_2,  SPINOR_OP_READ_1_1_2_4B,  0, 1, 2, 0x00, 0, 8},
+       {FLASH_FLAG_READ_FAST,   SPINOR_OP_READ_FAST_4B,   0, 1, 1, 0x00, 0, 8},
+       {FLASH_FLAG_READ_WRITE,  SPINOR_OP_READ_4B,        0, 1, 1, 0x00, 0, 0},
+       {0x00,                   0,                        0, 0, 0, 0x00, 0, 0},
 };
 
 static struct seq_rw_config stfsm_s25fl_write4_configs[] = {
drivers/mtd/spi-nor/spi-nor.c +79 -14
index 5c87b2d99507..423448c1c7a8 100644
@@ -75,6 +75,10 @@ struct flash_info {
                                         * bit. Must be used with
                                         * SPI_NOR_HAS_LOCK.
                                         */
+#define SPI_NOR_4B_OPCODES     BIT(10) /*
+                                        * Use dedicated 4byte address op codes
+                                        * to support memory size above 128Mib.
+                                        */
 };
 
 #define JEDEC_MFR(info)        ((info)->id[0])
@@ -188,6 +192,81 @@ static inline struct spi_nor *mtd_to_spi_nor(struct mtd_info *mtd)
        return mtd->priv;
 }
 
+
+struct spi_nor_address_entry {
+       u8      src_opcode;
+       u8      dst_opcode;
+};
+
+static u8 spi_nor_convert_opcode(u8 opcode,
+                                const struct spi_nor_address_entry *entries,
+                                size_t num_entries)
+{
+       int min, max;
+
+       min = 0;
+       max = num_entries - 1;
+       while (min <= max) {
+               int mid = (min + max) >> 1;
+               const struct spi_nor_address_entry *entry = &entries[mid];
+
+               if (opcode == entry->src_opcode)
+                       return entry->dst_opcode;
+
+               if (opcode < entry->src_opcode)
+                       max = mid - 1;
+               else
+                       min = mid + 1;
+       }
+
+       /* No conversion found */
+       return opcode;
+}
+
+static u8 spi_nor_3to4_opcode(u8 opcode)
+{
+       /* MUST be sorted by 3byte opcode */
+#define ENTRY_3TO4(_opcode)    { _opcode, _opcode##_4B }
+       static const struct spi_nor_address_entry spi_nor_3to4_table[] = {
+               ENTRY_3TO4(SPINOR_OP_PP),               /* 0x02 */
+               ENTRY_3TO4(SPINOR_OP_READ),             /* 0x03 */
+               ENTRY_3TO4(SPINOR_OP_READ_FAST),        /* 0x0b */
+               ENTRY_3TO4(SPINOR_OP_BE_4K),            /* 0x20 */
+               ENTRY_3TO4(SPINOR_OP_PP_1_1_4),         /* 0x32 */
+               ENTRY_3TO4(SPINOR_OP_PP_1_4_4),         /* 0x38 */
+               ENTRY_3TO4(SPINOR_OP_READ_1_1_2),       /* 0x3b */
+               ENTRY_3TO4(SPINOR_OP_BE_32K),           /* 0x52 */
+               ENTRY_3TO4(SPINOR_OP_READ_1_1_4),       /* 0x6b */
+               ENTRY_3TO4(SPINOR_OP_READ_1_2_2),       /* 0xbb */
+               ENTRY_3TO4(SPINOR_OP_SE),               /* 0xd8 */
+               ENTRY_3TO4(SPINOR_OP_READ_1_4_4),       /* 0xeb */
+       };
+#undef ENTRY_3TO4
+
+       return spi_nor_convert_opcode(opcode, spi_nor_3to4_table,
+                                     ARRAY_SIZE(spi_nor_3to4_table));
+}
+
+static void spi_nor_set_4byte_opcodes(struct spi_nor *nor,
+                                     const struct flash_info *info)
+{
+       /* Do some manufacturer fixups first */
+       switch (JEDEC_MFR(info)) {
+       case SNOR_MFR_SPANSION:
+               /* No small sector erase for 4-byte command set */
+               nor->erase_opcode = SPINOR_OP_SE;
+               nor->mtd.erasesize = info->sector_size;
+               break;
+
+       default:
+               break;
+       }
+
+       nor->read_opcode        = spi_nor_3to4_opcode(nor->read_opcode);
+       nor->program_opcode     = spi_nor_3to4_opcode(nor->program_opcode);
+       nor->erase_opcode       = spi_nor_3to4_opcode(nor->erase_opcode);
+}
+
 /* Enable/disable 4-byte addressing mode. */
 static inline int set_4byte(struct spi_nor *nor, const struct flash_info *info,
                            int enable)
@@ -1476,27 +1555,10 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
        else if (mtd->size > 0x1000000) {
                /* enable 4-byte addressing if the device exceeds 16MiB */
                nor->addr_width = 4;
-               if (JEDEC_MFR(info) == SNOR_MFR_SPANSION) {
-                       /* Dedicated 4-byte command set */
-                       switch (nor->flash_read) {
-                       case SPI_NOR_QUAD:
-                               nor->read_opcode = SPINOR_OP_READ4_1_1_4;
-                               break;
-                       case SPI_NOR_DUAL:
-                               nor->read_opcode = SPINOR_OP_READ4_1_1_2;
-                               break;
-                       case SPI_NOR_FAST:
-                               nor->read_opcode = SPINOR_OP_READ4_FAST;
-                               break;
-                       case SPI_NOR_NORMAL:
-                               nor->read_opcode = SPINOR_OP_READ4;

drivers/mtd/spi-nor/spi-nor.c int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode) · line 1560

Quoted 10 lines
drivers/mtd/spi-nor/spi-nor.c +3 -7
-                               break;
-                       }
-                       nor->program_opcode = SPINOR_OP_PP_4B;
-                       /* No small sector erase for 4-byte command set */
-                       nor->erase_opcode = SPINOR_OP_SE_4B;
-                       mtd->erasesize = info->sector_size;
-               } else
+               if (JEDEC_MFR(info) == SNOR_MFR_SPANSION ||
+                   info->flags & SPI_NOR_4B_OPCODES)
+                       spi_nor_set_4byte_opcodes(nor, info);

I am giving my inputs based on the discussion on this thread[1].

Quoted 5 lines

Seems like winbond[2], stmicron, spansion look better way or equally same for accessing 4-byte addressing commands. and I find few of the Macronix [3] have support of 4B addressing(see Table 5) but lack/unable to find the 4B opcodes.

The Macronix mx25l25635e has no 4-byte address op codes. Instead you can use the EN4B (enter 4-byte mode) B7h op code to make the memory enter its 4-byte mode: hence you still write one of the 3-byte address op code but now this op code is followed by a 4-byte address.
Currently the spi-nor framework uses this EN4B op code for all but Spansion memories. However entering this 4-byte mode is statefull.
For the Macronix 35e won't be able to use the 4-byte address instruction set and we will keep on entering the 4-byte mode.

The idea behind the patch is to use the 4-byte address instruction set when we are absolutely sure this set is supported by a given memory but keep the current behaviour for other memories.

Quoted 5 lines

And about BAR support, based on my experience in u-boot and research on above chips only require when controller isn't supporting 4-byte addressing even if connected flash chip has > 16MiB, that means there is no exact or equivalent requirement for flash side either.

Quoted 7 lines
Quoted 5 lines

So, adding the flags on flash table might be the good option instead making code to convert 3B opcode to 4B because anyway the chips require SPI_NOR_4B_OPCODES and also except winbond, stmicron, spansion not too many flash needed > 16MiB support as of now.

Sorry but I don't understand what you mean or suggest here!

OK, What I am trying to say here is except the Macronix reaming chips
- Winbond, Stmicron, Spansion has a similar way of opcode handling. So why can't we add the flags for supporting chips instead of 3B_to_4B conversion I'm thinking this conversion of opcodes not much helpful here as we dealing only few vendor chips and also not adequate for all scenarios.

thanks!

Signature · 4 lines
Jagan Teki
Free Software Engineer | www.openedev.com
U-Boot, Linux | Upstream Maintainer
Hyderabad, India.
#12 lore raw

Re: [PATCH v3 2/9] mtd: spi-nor: add an alternative method to support memory >16MiB

Quoted 259 lines · Jagan Teki
Quoted 257 lines · Cyrille Pitchen

Hi,

Quoted 224 lines · Jagan Teki

Hi Cyrille,

Quoted 207 lines · Cyrille Pitchen

This patch provides an alternative mean to support memory above 16MiB (128Mib) by replacing 3byte address op codes by their associated 4byte address versions.

Using the dedicated 4byte address op codes doesn't change the internal state of the SPI NOR memory as opposed to using other means such as updating a Base Address Register (BAR) and sending command to enter/leave the 4byte mode.

Hence when a CPU reset occurs, early bootloaders don't need to be aware of BAR value or 4byte mode being enabled: they can still access the first 16MiB of the SPI NOR memory using the regular 3byte address op codes.

---
 drivers/mtd/devices/serial_flash_cmds.h |   7 ---
 drivers/mtd/devices/st_spi_fsm.c        |  28 ++++-----
 drivers/mtd/spi-nor/spi-nor.c           | 104 +++++++++++++++++++++++++-------
 include/linux/mtd/spi-nor.h             |  22 +++++--
 4 files changed, 113 insertions(+), 48 deletions(-)
drivers/mtd/devices/serial_flash_cmds.h +0 -7
index f59a125295d0..8b81e15105dd 100644
@@ -18,19 +18,12 @@
 #define SPINOR_OP_RDVCR                0x85
 
 /* JEDEC Standard - Serial Flash Discoverable Parmeters (SFDP) Commands */
-#define SPINOR_OP_READ_1_2_2   0xbb    /* DUAL I/O READ */
-#define SPINOR_OP_READ_1_4_4   0xeb    /* QUAD I/O READ */
-
 #define SPINOR_OP_WRITE                0x02    /* PAGE PROGRAM */
 #define SPINOR_OP_WRITE_1_1_2  0xa2    /* DUAL INPUT PROGRAM */
 #define SPINOR_OP_WRITE_1_2_2  0xd2    /* DUAL INPUT EXT PROGRAM */
 #define SPINOR_OP_WRITE_1_1_4  0x32    /* QUAD INPUT PROGRAM */
 #define SPINOR_OP_WRITE_1_4_4  0x12    /* QUAD INPUT EXT PROGRAM */
 
-/* READ commands with 32-bit addressing */
-#define SPINOR_OP_READ4_1_2_2  0xbc
-#define SPINOR_OP_READ4_1_4_4  0xec
-
 /* Configuration flags */
 #define FLASH_FLAG_SINGLE      0x000000ff
 #define FLASH_FLAG_READ_WRITE  0x00000001
drivers/mtd/devices/st_spi_fsm.c +14 -14
index 5454b4113589..804313a33f2b 100644
@@ -507,13 +507,13 @@ static struct seq_rw_config n25q_read3_configs[] = {
  *     - 'FAST' variants configured for 8 dummy cycles (see note above.)
  */
 static struct seq_rw_config n25q_read4_configs[] = {
-       {FLASH_FLAG_READ_1_4_4, SPINOR_OP_READ4_1_4_4,  0, 4, 4, 0x00, 0, 8},
-       {FLASH_FLAG_READ_1_1_4, SPINOR_OP_READ4_1_1_4,  0, 1, 4, 0x00, 0, 8},
-       {FLASH_FLAG_READ_1_2_2, SPINOR_OP_READ4_1_2_2,  0, 2, 2, 0x00, 0, 8},
-       {FLASH_FLAG_READ_1_1_2, SPINOR_OP_READ4_1_1_2,  0, 1, 2, 0x00, 0, 8},
-       {FLASH_FLAG_READ_FAST,  SPINOR_OP_READ4_FAST,   0, 1, 1, 0x00, 0, 8},
-       {FLASH_FLAG_READ_WRITE, SPINOR_OP_READ4,        0, 1, 1, 0x00, 0, 0},
-       {0x00,                  0,                      0, 0, 0, 0x00, 0, 0},
+       {FLASH_FLAG_READ_1_4_4, SPINOR_OP_READ_1_4_4_4B, 0, 4, 4, 0x00, 0, 8},
+       {FLASH_FLAG_READ_1_1_4, SPINOR_OP_READ_1_1_4_4B, 0, 1, 4, 0x00, 0, 8},
+       {FLASH_FLAG_READ_1_2_2, SPINOR_OP_READ_1_2_2_4B, 0, 2, 2, 0x00, 0, 8},
+       {FLASH_FLAG_READ_1_1_2, SPINOR_OP_READ_1_1_2_4B, 0, 1, 2, 0x00, 0, 8},
+       {FLASH_FLAG_READ_FAST,  SPINOR_OP_READ_FAST_4B,  0, 1, 1, 0x00, 0, 8},
+       {FLASH_FLAG_READ_WRITE, SPINOR_OP_READ_4B,       0, 1, 1, 0x00, 0, 0},
+       {0x00,                  0,                       0, 0, 0, 0x00, 0, 0},
 };
 
 /*
@@ -553,13 +553,13 @@ static int stfsm_mx25_en_32bit_addr_seq(struct stfsm_seq *seq)
  * entering a state that is incompatible with the SPIBoot Controller.
  */
 static struct seq_rw_config stfsm_s25fl_read4_configs[] = {
-       {FLASH_FLAG_READ_1_4_4,  SPINOR_OP_READ4_1_4_4,  0, 4, 4, 0x00, 2, 4},
-       {FLASH_FLAG_READ_1_1_4,  SPINOR_OP_READ4_1_1_4,  0, 1, 4, 0x00, 0, 8},
-       {FLASH_FLAG_READ_1_2_2,  SPINOR_OP_READ4_1_2_2,  0, 2, 2, 0x00, 4, 0},
-       {FLASH_FLAG_READ_1_1_2,  SPINOR_OP_READ4_1_1_2,  0, 1, 2, 0x00, 0, 8},
-       {FLASH_FLAG_READ_FAST,   SPINOR_OP_READ4_FAST,   0, 1, 1, 0x00, 0, 8},
-       {FLASH_FLAG_READ_WRITE,  SPINOR_OP_READ4,        0, 1, 1, 0x00, 0, 0},
-       {0x00,                   0,                      0, 0, 0, 0x00, 0, 0},
+       {FLASH_FLAG_READ_1_4_4,  SPINOR_OP_READ_1_4_4_4B,  0, 4, 4, 0x00, 2, 4},
+       {FLASH_FLAG_READ_1_1_4,  SPINOR_OP_READ_1_1_4_4B,  0, 1, 4, 0x00, 0, 8},
+       {FLASH_FLAG_READ_1_2_2,  SPINOR_OP_READ_1_2_2_4B,  0, 2, 2, 0x00, 4, 0},
+       {FLASH_FLAG_READ_1_1_2,  SPINOR_OP_READ_1_1_2_4B,  0, 1, 2, 0x00, 0, 8},
+       {FLASH_FLAG_READ_FAST,   SPINOR_OP_READ_FAST_4B,   0, 1, 1, 0x00, 0, 8},
+       {FLASH_FLAG_READ_WRITE,  SPINOR_OP_READ_4B,        0, 1, 1, 0x00, 0, 0},
+       {0x00,                   0,                        0, 0, 0, 0x00, 0, 0},
 };
 
 static struct seq_rw_config stfsm_s25fl_write4_configs[] = {
drivers/mtd/spi-nor/spi-nor.c +79 -14
index 5c87b2d99507..423448c1c7a8 100644
@@ -75,6 +75,10 @@ struct flash_info {
                                         * bit. Must be used with
                                         * SPI_NOR_HAS_LOCK.
                                         */
+#define SPI_NOR_4B_OPCODES     BIT(10) /*
+                                        * Use dedicated 4byte address op codes
+                                        * to support memory size above 128Mib.
+                                        */
 };
 
 #define JEDEC_MFR(info)        ((info)->id[0])
@@ -188,6 +192,81 @@ static inline struct spi_nor *mtd_to_spi_nor(struct mtd_info *mtd)
        return mtd->priv;
 }
 
+
+struct spi_nor_address_entry {
+       u8      src_opcode;
+       u8      dst_opcode;
+};
+
+static u8 spi_nor_convert_opcode(u8 opcode,
+                                const struct spi_nor_address_entry *entries,
+                                size_t num_entries)
+{
+       int min, max;
+
+       min = 0;
+       max = num_entries - 1;
+       while (min <= max) {
+               int mid = (min + max) >> 1;
+               const struct spi_nor_address_entry *entry = &entries[mid];
+
+               if (opcode == entry->src_opcode)
+                       return entry->dst_opcode;
+
+               if (opcode < entry->src_opcode)
+                       max = mid - 1;
+               else
+                       min = mid + 1;
+       }
+
+       /* No conversion found */
+       return opcode;
+}
+
+static u8 spi_nor_3to4_opcode(u8 opcode)
+{
+       /* MUST be sorted by 3byte opcode */
+#define ENTRY_3TO4(_opcode)    { _opcode, _opcode##_4B }
+       static const struct spi_nor_address_entry spi_nor_3to4_table[] = {
+               ENTRY_3TO4(SPINOR_OP_PP),               /* 0x02 */
+               ENTRY_3TO4(SPINOR_OP_READ),             /* 0x03 */
+               ENTRY_3TO4(SPINOR_OP_READ_FAST),        /* 0x0b */
+               ENTRY_3TO4(SPINOR_OP_BE_4K),            /* 0x20 */
+               ENTRY_3TO4(SPINOR_OP_PP_1_1_4),         /* 0x32 */
+               ENTRY_3TO4(SPINOR_OP_PP_1_4_4),         /* 0x38 */
+               ENTRY_3TO4(SPINOR_OP_READ_1_1_2),       /* 0x3b */
+               ENTRY_3TO4(SPINOR_OP_BE_32K),           /* 0x52 */
+               ENTRY_3TO4(SPINOR_OP_READ_1_1_4),       /* 0x6b */
+               ENTRY_3TO4(SPINOR_OP_READ_1_2_2),       /* 0xbb */
+               ENTRY_3TO4(SPINOR_OP_SE),               /* 0xd8 */
+               ENTRY_3TO4(SPINOR_OP_READ_1_4_4),       /* 0xeb */
+       };
+#undef ENTRY_3TO4
+
+       return spi_nor_convert_opcode(opcode, spi_nor_3to4_table,
+                                     ARRAY_SIZE(spi_nor_3to4_table));
+}
+
+static void spi_nor_set_4byte_opcodes(struct spi_nor *nor,
+                                     const struct flash_info *info)
+{
+       /* Do some manufacturer fixups first */
+       switch (JEDEC_MFR(info)) {
+       case SNOR_MFR_SPANSION:
+               /* No small sector erase for 4-byte command set */
+               nor->erase_opcode = SPINOR_OP_SE;
+               nor->mtd.erasesize = info->sector_size;
+               break;
+
+       default:
+               break;
+       }
+
+       nor->read_opcode        = spi_nor_3to4_opcode(nor->read_opcode);
+       nor->program_opcode     = spi_nor_3to4_opcode(nor->program_opcode);
+       nor->erase_opcode       = spi_nor_3to4_opcode(nor->erase_opcode);
+}
+
 /* Enable/disable 4-byte addressing mode. */
 static inline int set_4byte(struct spi_nor *nor, const struct flash_info *info,
                            int enable)
@@ -1476,27 +1555,10 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
        else if (mtd->size > 0x1000000) {
                /* enable 4-byte addressing if the device exceeds 16MiB */
                nor->addr_width = 4;
-               if (JEDEC_MFR(info) == SNOR_MFR_SPANSION) {
-                       /* Dedicated 4-byte command set */
-                       switch (nor->flash_read) {
-                       case SPI_NOR_QUAD:
-                               nor->read_opcode = SPINOR_OP_READ4_1_1_4;
-                               break;
-                       case SPI_NOR_DUAL:
-                               nor->read_opcode = SPINOR_OP_READ4_1_1_2;
-                               break;
-                       case SPI_NOR_FAST:
-                               nor->read_opcode = SPINOR_OP_READ4_FAST;
-                               break;
-                       case SPI_NOR_NORMAL:
-                               nor->read_opcode = SPINOR_OP_READ4;

drivers/mtd/spi-nor/spi-nor.c int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode) · line 1560

Quoted 10 lines
drivers/mtd/spi-nor/spi-nor.c +3 -7
-                               break;
-                       }
-                       nor->program_opcode = SPINOR_OP_PP_4B;
-                       /* No small sector erase for 4-byte command set */
-                       nor->erase_opcode = SPINOR_OP_SE_4B;
-                       mtd->erasesize = info->sector_size;
-               } else
+               if (JEDEC_MFR(info) == SNOR_MFR_SPANSION ||
+                   info->flags & SPI_NOR_4B_OPCODES)
+                       spi_nor_set_4byte_opcodes(nor, info);

I am giving my inputs based on the discussion on this thread[1].

Quoted 5 lines

Seems like winbond[2], stmicron, spansion look better way or equally same for accessing 4-byte addressing commands. and I find few of the Macronix [3] have support of 4B addressing(see Table 5) but lack/unable to find the 4B opcodes.

The Macronix mx25l25635e has no 4-byte address op codes. Instead you can use the EN4B (enter 4-byte mode) B7h op code to make the memory enter its 4-byte mode: hence you still write one of the 3-byte address op code but now this op code is followed by a 4-byte address.
Currently the spi-nor framework uses this EN4B op code for all but Spansion memories. However entering this 4-byte mode is statefull.
For the Macronix 35e won't be able to use the 4-byte address instruction set and we will keep on entering the 4-byte mode.

The idea behind the patch is to use the 4-byte address instruction set when we are absolutely sure this set is supported by a given memory but keep the current behaviour for other memories.

Quoted 10 lines

And about BAR support, based on my experience in u-boot and research on above chips only require when controller isn't supporting 4-byte addressing even if connected flash chip has > 16MiB, that means there is no exact or equivalent requirement for flash side either.

So, adding the flags on flash table might be the good option instead making code to convert 3B opcode to 4B because anyway the chips require SPI_NOR_4B_OPCODES and also except winbond, stmicron, spansion not too many flash needed > 16MiB support as of now.

Quoted 4 lines
Quoted 1 line

Sorry but I don't understand what you mean or suggest here!

OK, What I am trying to say here is except the Macronix reaming chips
- Winbond, Stmicron, Spansion has a similar way of opcode handling. So

Be careful about Micron memories too! many part numbers share the very same entry in the spi_nor_ids[] array but some of them don't support the Page 4-byte address Program 1-1-4; the op code should be 12h.

For instance, with n25q512* memories about the 3-byte address:
"The code 38h is only valid for line items that enable the additional RESET# pin; otherwise, code 12h is valid."

n25q512a8* parts have the additional RESET# pin:
- 4-byte address Page Program 1-1-1: 12h
- 3-byte address Page Program x-4-4: 38h

n25q512a1* parts don't have the additional RESET# pin:
- 4-byte address Page Program 1-1-1: N/A => entering the 4-byte mode is still needed here.
- 3-byte address Page Program x-4-4: 12h

Quoted 1 line

why can't we add the flags for supporting chips instead of 3B_to_4B

Which flag do you refer to?
The SPI_NOR_4B_OPCODES flag I've proposed in my patch is to enable the op code conversion for a given memory when we know all parts handled by a single spi_nor_ids[] entry support the 4-byte address instruction set.

If you think the conversion is useless, I don't understand what flag you suggest to use instead.

Quoted 4 lines

conversion I'm thinking this conversion of opcodes not much helpful here as we dealing only few vendor chips and also not adequate for all scenarios.

Currently, a conversion is already done for Spansion memories. The conversion is done for (Fast) Read op codes by a switch(nor->flash_read) statement. Also for Page Program operations, currently only the SPI 1-1-1 is supported so the spi-nor framework always use the SPINOR_OP_PP_4B op code.

My patch simply helps translating more op codes since further patches in the series add support to more SPI protocols, hence more op code are needed.

Quoted 2 lines

thanks!

#13 lore raw

PATCH v3 3/9 mtd: spi-nor: add support of SPI protocols like SPI 1-2-2 and SPI 1-4-4

In reply to Cyrille Pitchen

This patch changes the prototype of spi_nor_scan(): its 3rd parameter is replaced by a const struct spi_nor_modes pointer, which tells the spi-nor framework about which SPI protocols are supported by the SPI controller.

Besides, this patch also introduces a new spi_nor_basic_flash_parameter structure telling the spi-nor framework about the SPI protocols supported by the SPI memory and the needed op codes to use these SPI protocols.

Currently the struct spi_nor_basic_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 SPI protocols supported by both the (Q)SPI memory and controller hence selecting the relevant op codes for (Fast) Read, Page Program and Sector Erase operations.

The spi_nor_basic_flash_parameter structure 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 spi_nor_basic_flash_parameter structure, 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.

---
 drivers/mtd/devices/m25p80.c          |  17 ++-
 drivers/mtd/spi-nor/atmel-quadspi.c   |  83 ++++++----
 drivers/mtd/spi-nor/cadence-quadspi.c |  18 ++-
 drivers/mtd/spi-nor/fsl-quadspi.c     |   8 +-
 drivers/mtd/spi-nor/hisi-sfc.c        |  32 +++-
 drivers/mtd/spi-nor/mtk-quadspi.c     |  16 +-
 drivers/mtd/spi-nor/nxp-spifi.c       |  21 +--
 drivers/mtd/spi-nor/spi-nor.c         | 280 +++++++++++++++++++++++++++-------
 include/linux/mtd/spi-nor.h           | 136 +++++++++++++++--
 9 files changed, 482 insertions(+), 129 deletions(-)
drivers/mtd/devices/m25p80.c +10 -7
index 9cf7fcd28034..f0a55c01406b 100644
@@ -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 (SNOR_PROTO_DATA_FROM_PROTO(nor->read_proto)) {
+	case 2:
 		return 2;
-	case SPI_NOR_QUAD:
+	case 4:
 		return 4;
 	default:
 		return 0;
@@ -195,7 +195,10 @@ 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_modes modes = {
+		.rd_modes = SNOR_MODE_SLOW,
+		.wr_modes = SNOR_MODE_1_1_1,
+	};
 	char *flash_name;
 	int ret;
 
@@ -221,9 +224,9 @@ static int m25p_probe(struct spi_device *spi)
 	flash->spi = spi;
 
 	if (spi->mode & SPI_RX_QUAD)
-		mode = SPI_NOR_QUAD;
+		modes.rd_modes |= SNOR_MODE_1_1_4;
 	else if (spi->mode & SPI_RX_DUAL)
-		mode = SPI_NOR_DUAL;
+		modes.rd_modes |= SNOR_MODE_1_1_2;
 
 	if (data && data->name)
 		nor->mtd.name = data->name;
@@ -240,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, &modes);
 	if (ret)
 		return ret;
 
drivers/mtd/spi-nor/atmel-quadspi.c +56 -27

145 lines not on this page · raw message

drivers/mtd/spi-nor/cadence-quadspi.c +12 -6
index d403ba7b8f43..87e49231b4ee 100644
@@ -853,15 +853,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:
@@ -1074,6 +1073,13 @@ static int cqspi_setup_flash(struct cqspi_st *cqspi, struct device_node *np)
 	struct mtd_info *mtd;
 	unsigned int cs;
 	int i, ret;
+	static const struct spi_nor_modes modes = {
+		.rd_modes = (SNOR_MODE_SLOW |
+			     SNOR_MODE_1_1_1 |
+			     SNOR_MODE_1_1_2 |
+			     SNOR_MODE_1_1_4),
+		.wr_modes = SNOR_MODE_1_1_1,
+	};
 
 	/* Get flash device data */
 	for_each_available_child_of_node(dev->of_node, np) {
@@ -1119,7 +1125,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, &modes);
 		if (ret)
 			goto err;
 
drivers/mtd/spi-nor/fsl-quadspi.c +7 -1
index 5c82e4ef1904..01e7356d6623 100644
@@ -980,6 +980,12 @@ static int fsl_qspi_probe(struct platform_device *pdev)
 	struct spi_nor *nor;
 	struct mtd_info *mtd;
 	int ret, i = 0;
+	static const struct spi_nor_modes modes = {
+		.rd_modes = (SNOR_MODE_SLOW |
+			     SNOR_MODE_1_1_1 |
+			     SNOR_MODE_1_1_4),
+		.wr_modes = SNOR_MODE_1_1_1,
+	};
 
 	q = devm_kzalloc(dev, sizeof(*q), GFP_KERNEL);
 	if (!q)
@@ -1081,7 +1087,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, &modes);
 		if (ret)
 			goto mutex_failed;
 
drivers/mtd/spi-nor/hisi-sfc.c +24 -8
index 20378b0d55e9..e4d7625b4397 100644
@@ -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;
@@ -238,6 +243,7 @@ static int hisi_spi_nor_dma_transfer(struct spi_nor *nor, loff_t start_off,
 {
 	struct hifmc_priv *priv = nor->priv;
 	struct hifmc_host *host = priv->host;
+	enum spi_nor_protocol proto;
 	u8 if_type = 0;
 	u32 reg;
 
@@ -253,7 +259,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);
+	proto = (op_type == FMC_OP_READ)
+		? nor->read_proto
+		: nor->write_proto;
+	if_type = get_if_type(proto);
 	reg |= OP_CFG_MEM_IF_TYPE(if_type);
 	if (op_type == FMC_OP_READ)
 		reg |= OP_CFG_DUMMY_NUM(nor->read_dummy >> 3);
@@ -326,6 +335,13 @@ static int hisi_spi_nor_register(struct device_node *np,
 	struct hifmc_priv *priv;
 	struct mtd_info *mtd;
 	int ret;
+	static const struct spi_nor_modes modes = {
+		.rd_modes = (SNOR_MODE_SLOW |
+			     SNOR_MODE_1_1_1 |
+			     SNOR_MODE_1_1_2 |
+			     SNOR_MODE_1_1_4),
+		.wr_modes = SNOR_MODE_1_1_1,
+	};
 
 	nor = devm_kzalloc(dev, sizeof(*nor), GFP_KERNEL);
 	if (!nor)
@@ -362,7 +378,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, &modes);
 	if (ret)
 		return ret;
 
drivers/mtd/spi-nor/mtk-quadspi.c +11 -5
index e661877c23de..4dc2bb8eb488 100644
@@ -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 (SNOR_PROTO_DATA_FROM_PROTO(nor->read_proto)) {
+	case 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 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 4:
 		writeb(nor->read_opcode, mt8173_nor->base +
 		       MTK_NOR_PRGDATA4_REG);
 		writeb(MTK_NOR_QUAD_READ_EN, mt8173_nor->base +
@@ -383,6 +383,12 @@ static int mtk_nor_init(struct mt8173_nor *mt8173_nor,
 {
 	int ret;
 	struct spi_nor *nor;
+	static const struct spi_nor_modes modes = {
+		.rd_modes = (SNOR_MODE_SLOW |
+			     SNOR_MODE_1_1_1 |
+			     SNOR_MODE_1_1_2),
+		.wr_modes = SNOR_MODE_1_1_1,
+	};
 
 	/* initialize controller to accept commands */
 	writel(MTK_NOR_ENABLE_SF_CMD, mt8173_nor->base + MTK_NOR_WRPROT_REG);
@@ -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, &modes);
 	if (ret)
 		return ret;
 
drivers/mtd/spi-nor/nxp-spifi.c +11 -10
index 73a14f40928b..327c5b5fe9da 100644
@@ -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 (SNOR_PROTO_DATA_FROM_PROTO(spifi->nor.read_proto)) {
+	case 1:
 		spifi->mcmd = SPIFI_CMD_FIELDFORM_ALL_SERIAL;
 		break;
-	case SPI_NOR_DUAL:
-	case SPI_NOR_QUAD:
+	case 2:
+	case 4:
 		spifi->mcmd = SPIFI_CMD_FIELDFORM_QUAD_DUAL_DATA;
 		break;
 	default:
@@ -274,7 +273,10 @@ 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_modes modes = {
+		.rd_modes = (SNOR_MODE_SLOW | SNOR_MODE_1_1_1),
+		.wr_modes = SNOR_MODE_1_1_1,
+	};
 	u32 ctrl, property;
 	u16 mode = 0;
 	int ret;
@@ -308,13 +310,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;
+		modes.rd_modes |= SNOR_MODE_1_1_2;
 	} else if (mode & SPI_RX_QUAD) {
 		ctrl &= ~SPIFI_CTRL_DUAL;
-		flash_read = SPI_NOR_QUAD;
+		modes.rd_modes |= SNOR_MODE_1_1_4;
 	} else {
 		ctrl |= SPIFI_CTRL_DUAL;
-		flash_read = SPI_NOR_NORMAL;
 	}
 
 	switch (mode & (SPI_CPHA | SPI_CPOL)) {
@@ -351,7 +352,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, &modes);
 	if (ret) {
 		dev_err(spifi->dev, "device scan failed\n");
 		return ret;
drivers/mtd/spi-nor/spi-nor.c +224 -56

327 lines not on this page · raw message

include/linux/mtd/spi-nor.h +127 -9

167 lines not on this page · raw message

Signature · 1 line
2.7.4
#14 lore raw

PATCH v3 4/9 mtd: spi-nor: remove unused set_quad_mode() function

In reply to Cyrille Pitchen

The set_quad_mode() function is no longer used, so we remove it.

This patch is not squashed into the previous patch on purpose for readiness issue and to ease the review process of the whole series.

---
 drivers/mtd/spi-nor/spi-nor.c | 24 ------------------------
 1 file changed, 24 deletions(-)
drivers/mtd/spi-nor/spi-nor.c +0 -24
index 05000b010e7f..a06fcf400b39 100644
@@ -1331,30 +1331,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 ||
Signature · 1 line
2.7.4
#15 lore raw

PATCH v3 5/9 mtd: m25p80: add support of dual and quad spi protocols to all commands

In reply to 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, all other m25p80_*() hooks only supported SPI 1-1-1.

This patch adds support to all currently existing SPI protocols to cover as many protocols as possible.

---
 drivers/mtd/devices/m25p80.c | 193 +++++++++++++++++++++++++++++++++----------
 1 file changed, 149 insertions(+), 44 deletions(-)
drivers/mtd/devices/m25p80.c +149 -44
index f0a55c01406b..38778eac6c21 100644
@@ -27,22 +27,64 @@
 #include <linux/spi/flash.h>
 #include <linux/mtd/spi-nor.h>
 
-#define	MAX_CMD_SIZE		6
+#define	MAX_CMD_SIZE		16
 struct m25p {
 	struct spi_device	*spi;
 	struct spi_nor		spi_nor;
 	u8			command[MAX_CMD_SIZE];
 };
 
+static inline int m25p80_proto2nbits(enum spi_nor_protocol proto,
+				     unsigned *code_nbits,
+				     unsigned *addr_nbits,
+				     unsigned *data_nbits)
+{
+	if (code_nbits)
+		*code_nbits = SNOR_PROTO_CODE_FROM_PROTO(proto);
+	if (addr_nbits)
+		*addr_nbits = SNOR_PROTO_ADDR_FROM_PROTO(proto);
+	if (data_nbits)
+		*data_nbits = SNOR_PROTO_DATA_FROM_PROTO(proto);
+
+	return 0;
+}
+
 static int m25p80_read_reg(struct spi_nor *nor, u8 code, u8 *val, int len)
 {
 	struct m25p *flash = nor->priv;
 	struct spi_device *spi = flash->spi;
+	unsigned code_nbits, data_nbits;
+	struct spi_transfer xfers[2];
 	int ret;
 
-	ret = spi_write_then_read(spi, &code, 1, val, len);
+	/* Check the total length of command op code and data. */
+	if (len + 1 > MAX_CMD_SIZE)
+		return -EINVAL;
+
+	/* Get transfer protocols (addr_nbits is not relevant here). */
+	ret = m25p80_proto2nbits(nor->reg_proto,
+				 &code_nbits, NULL, &data_nbits);
+	if (ret < 0)
+		return ret;
+
+	/* Set up transfers. */
+	memset(xfers, 0, sizeof(xfers));
+
+	flash->command[0] = code;
+	xfers[0].len = 1;
+	xfers[0].tx_buf = flash->command;
+	xfers[0].tx_nbits = code_nbits;
+
+	xfers[1].len = len;
+	xfers[1].rx_buf = &flash->command[1];
+	xfers[1].rx_nbits = data_nbits;
+
+	/* Process command. */
+	ret = spi_sync_transfer(spi, xfers, 2);
 	if (ret < 0)
 		dev_err(&spi->dev, "error %d reading %x\n", ret, code);
+	else
+		memcpy(val, &flash->command[1], len);
 
 	return ret;
 }
@@ -65,12 +107,42 @@ static int m25p80_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len)
 {
 	struct m25p *flash = nor->priv;
 	struct spi_device *spi = flash->spi;
+	unsigned code_nbits, data_nbits, num_xfers = 1;
+	struct spi_transfer xfers[2];
+	int ret;
+
+	/* Check the total length of command op code and data. */
+	if (buf && (len + 1 > MAX_CMD_SIZE))
+		return -EINVAL;
+
+	/* Get transfer protocols (addr_nbits is not relevant here). */
+	ret = m25p80_proto2nbits(nor->reg_proto,
+				 &code_nbits, NULL, &data_nbits);
+	if (ret < 0)
+		return ret;
+
+	/* Set up transfer(s). */
+	memset(xfers, 0, sizeof(xfers));
 
 	flash->command[0] = opcode;
-	if (buf)
+	xfers[0].len = 1;
+	xfers[0].tx_buf = flash->command;
+	xfers[0].tx_nbits = code_nbits;
+
+	if (buf) {
 		memcpy(&flash->command[1], buf, len);
+		if (data_nbits == code_nbits) {
+			xfers[0].len += len;
+		} else {
+			xfers[1].len = len;
+			xfers[1].tx_buf = &flash->command[1];
+			xfers[1].tx_nbits = data_nbits;
+			num_xfers++;
+		}
+	}
 
-	return spi_write(spi, flash->command, len + 1);
+	/* Process command. */
+	return spi_sync_transfer(spi, xfers, num_xfers);
 }
 
 static ssize_t m25p80_write(struct spi_nor *nor, loff_t to, size_t len,
@@ -78,27 +150,48 @@ 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 code_nbits, addr_nbits, data_nbits, num_xfers = 1;
+	struct spi_transfer xfers[3];
 	struct spi_message m;
 	int cmd_sz = m25p_cmdsz(nor);
 	ssize_t ret;
 
-	spi_message_init(&m);
-
 	if (nor->program_opcode == SPINOR_OP_AAI_WP && nor->sst_write_second)
 		cmd_sz = 1;
 
-	flash->command[0] = nor->program_opcode;
-	m25p_addr2cmd(nor, to, flash->command);
+	/* Get transfer protocols. */
+	ret = m25p80_proto2nbits(nor->write_proto,
+				 &code_nbits, &addr_nbits, &data_nbits);
+	if (ret < 0)
+		return ret;
 
-	t[0].tx_buf = flash->command;
-	t[0].len = cmd_sz;
-	spi_message_add_tail(&t[0], &m);
+	/* Set up transfers. */
+	memset(xfers, 0, sizeof(xfers));
+
+	flash->command[0] = nor->program_opcode;
+	xfers[0].len = 1;
+	xfers[0].tx_buf = flash->command;
+	xfers[0].tx_nbits = code_nbits;
+
+	if (cmd_sz > 1) {
+		m25p_addr2cmd(nor, to, flash->command);
+		if (addr_nbits == code_nbits) {
+			xfers[0].len += nor->addr_width;
+		} else {
+			xfers[1].len = nor->addr_width;
+			xfers[1].tx_buf = &flash->command[1];
+			xfers[1].tx_nbits = addr_nbits;
+			num_xfers++;
+		}
+	}
 
-	t[1].tx_buf = buf;
-	t[1].len = len;
-	spi_message_add_tail(&t[1], &m);
+	xfers[num_xfers].len = len;
+	xfers[num_xfers].tx_buf = buf;
+	xfers[num_xfers].tx_nbits = data_nbits;
+	num_xfers++;
 
+	/* Process command. */
+	spi_message_init_with_transfers(&m, xfers, num_xfers);
 	ret = spi_sync(spi, &m);
 	if (ret)
 		return ret;
@@ -109,18 +202,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 (SNOR_PROTO_DATA_FROM_PROTO(nor->read_proto)) {
-	case 2:
-		return 2;
-	case 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,14 +211,22 @@ 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];
-	struct spi_message m;
+	unsigned code_nbits, addr_nbits, data_nbits, num_xfers = 1;
 	unsigned int dummy = nor->read_dummy;
 	ssize_t ret;
+	struct spi_transfer xfers[3];
+	struct spi_message m;
+
+	/* Get transfer protocols. */
+	ret = m25p80_proto2nbits(nor->read_proto,
+				 &code_nbits, &addr_nbits, &data_nbits);
+	if (ret < 0)
+		return ret;
 
 	/* convert the dummy cycles to the number of bytes */
-	dummy /= 8;
+	dummy = (dummy * addr_nbits) / 8;
 
+	/* Use the SPI flash API if supported. */
 	if (spi_flash_read_supported(spi)) {
 		struct spi_flash_read_message msg;
 
@@ -149,10 +238,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 = code_nbits;
+		msg.addr_nbits = addr_nbits;
+		msg.data_nbits = data_nbits;
 
 		ret = spi_flash_read(spi, &msg);
 		if (ret < 0)
@@ -160,21 +248,38 @@ static ssize_t m25p80_read(struct spi_nor *nor, loff_t from, size_t len,
 		return msg.retlen;
 	}
 
-	spi_message_init(&m);
-	memset(t, 0, (sizeof t));
+	/* Set up transfers. */
+	memset(xfers, 0, sizeof(xfers));
 
 	flash->command[0] = nor->read_opcode;
-	m25p_addr2cmd(nor, from, flash->command);
+	xfers[0].len = 1;
+	xfers[0].tx_buf = flash->command;
+	xfers[0].tx_nbits = code_nbits;
 
-	t[0].tx_buf = flash->command;
-	t[0].len = m25p_cmdsz(nor) + dummy;
-	spi_message_add_tail(&t[0], &m);
+	m25p_addr2cmd(nor, from, flash->command);
+	/*
+	 * Clear all dummy/mode cycle bits to avoid sending some manufacturer
+	 * specific pattern, which might make the memory enter its Continuous
+	 * Read mode by mistake.
+	 */
+	memset(flash->command + 1 + nor->addr_width, 0, dummy);
+
+	if (addr_nbits == code_nbits) {
+		xfers[0].len += nor->addr_width + dummy;
+	} else {
+		xfers[1].len = nor->addr_width + dummy;
+		xfers[1].tx_buf = &flash->command[1];
+		xfers[1].tx_nbits = addr_nbits;
+		num_xfers++;
+	}
 
-	t[1].rx_buf = buf;
-	t[1].rx_nbits = m25p80_rx_nbits(nor);
-	t[1].len = min(len, spi_max_transfer_size(spi));
-	spi_message_add_tail(&t[1], &m);
+	xfers[num_xfers].len = len;
+	xfers[num_xfers].rx_buf = buf;
+	xfers[num_xfers].rx_nbits = data_nbits;
+	num_xfers++;
 
+	/* Process command. */
+	spi_message_init_with_transfers(&m, xfers, num_xfers);
 	ret = spi_sync(spi, &m);
 	if (ret)
 		return ret;
Signature · 1 line
2.7.4
#16 lore raw

Re: [PATCH v3 5/9] mtd: m25p80: add support of dual and quad spi protocols to all commands

Quoted 21 lines · 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, all other m25p80_*() hooks only supported SPI 1-1-1.

This patch adds support to all currently existing SPI protocols to cover as many protocols as possible.

---
 drivers/mtd/devices/m25p80.c | 193 +++++++++++++++++++++++++++++++++----------
 1 file changed, 149 insertions(+), 44 deletions(-)
drivers/mtd/devices/m25p80.c +0 -0
index f0a55c01406b..38778eac6c21 100644
@@ -27,22 +27,64 @@
 #include <linux/spi/flash.h>
 #include <linux/mtd/spi-nor.h>

drivers/mtd/devices/m25p80.c line 37

Quoted 10 lines
drivers/mtd/devices/m25p80.c +2 -1
 
-#define	MAX_CMD_SIZE		6
+#define	MAX_CMD_SIZE		16
 struct m25p {
 	struct spi_device	*spi;
 	struct spi_nor		spi_nor;
 	u8			command[MAX_CMD_SIZE];
 };
 
+static inline int m25p80_proto2nbits(enum spi_nor_protocol proto,

This can be void , since it's always returning zero.

Quoted 14 lines
drivers/mtd/devices/m25p80.c +14 -0
+				     unsigned *code_nbits,
+				     unsigned *addr_nbits,
+				     unsigned *data_nbits)
+{
+	if (code_nbits)
+		*code_nbits = SNOR_PROTO_CODE_FROM_PROTO(proto);
+	if (addr_nbits)
+		*addr_nbits = SNOR_PROTO_ADDR_FROM_PROTO(proto);
+	if (data_nbits)
+		*data_nbits = SNOR_PROTO_DATA_FROM_PROTO(proto);
+
+	return 0;
+}
+

[...]

Signature · 2 lines
Best regards,
Marek Vasut
#17 lore raw

PATCH v3 6/9 mtd: spi-nor: parse Serial Flash Discoverable Parameters (SFDP) tables

In reply to Cyrille Pitchen

This patch adds support the the JESD216B standard and parse the SFDP tables to dynamically initialize the spi_nor_basic_flash_parameter structure.

---
 drivers/mtd/spi-nor/spi-nor.c | 468 +++++++++++++++++++++++++++++++++++++++++-
 include/linux/mtd/spi-nor.h   |   6 +
 2 files changed, 473 insertions(+), 1 deletion(-)
drivers/mtd/spi-nor/spi-nor.c +467 -1
index a06fcf400b39..7fc0138c1d68 100644
@@ -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>
@@ -79,6 +80,7 @@ struct flash_info {
 					 * Use dedicated 4byte address op codes
 					 * to support memory size above 128Mib.
 					 */
+#define SPI_NOR_SKIP_SFDP	BIT(11) /* Skip read of SFDP tables */
 };
 
 #define JEDEC_MFR(info)	((info)->id[0])
@@ -1331,6 +1333,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 ||
@@ -1359,11 +1454,381 @@ static inline void spi_nor_set_erase_settings(struct spi_nor_erase_type *erase,
 	erase->opcode = opcode;
 }
 
+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 */
+
+/* 1st DWORD. */
+#define BFPT_WORD0_FAST_READ_1_1_2	BIT(16)
+#define BFPT_WORD0_ADDRESS_BYTES_MASK	GENMASK(18, 17)
+#define BFPT_WORD0_ADDRESS_BYTES_3_ONLY	(0u << 17)
+#define BFPT_WORD0_ADDRESS_BYTES_3_OR_4	(1u << 17)
+#define BFPT_WORD0_ADDRESS_BYTES_4_ONLY	(2u << 17)
+#define BFPT_WORD0_DTR			BIT(19)
+#define BFPT_WORD0_FAST_READ_1_2_2	BIT(20)
+#define BFPT_WORD0_FAST_READ_1_4_4	BIT(21)
+#define BFPT_WORD0_FAST_READ_1_1_4	BIT(22)
+
+/* 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_WORD14_QER_MASK		GENMASK(22, 20)
+#define BFPT_WORD14_QER_NONE		(0 << 20) /* Micron */
+#define BFPT_WORD14_QER_SR2_BIT1_BUGGY	(1 << 20)
+#define BFPT_WORD14_QER_SR1_BIT6	(2 << 20) /* Macronix */
+#define BFPT_WORD14_QER_SR2_BIT7	(3 << 20)
+#define BFPT_WORD14_QER_SR2_BIT1_NO_RD	(4 << 20)
+#define BFPT_WORD14_QER_SR2_BIT1	(5 << 20) /* Spansion */
+
+/* JESD216B defines a Basic Flash Parameter Table of 16 words. */
+#define SFDP_BFPT_MAX_WORDS	16
+
+struct sfdp_bfpt {
+	u32	word[SFDP_BFPT_MAX_WORDS];
+};
+
+/* Fast Read settings. */
+#define BFPT_FAST_READ_WAIT_STATES(half)	((((u16)(half)) >> 0) & 0x1f)
+#define BFPT_FAST_READ_MODE_CLOCKS(half)	((((u16)(half)) >> 5) & 0x07)
+#define BFPT_FAST_READ_OP_CODE(half)		((((u16)(half)) >> 8) & 0xff)
+
+struct sfdp_read {
+	/* The protocol index of SPI x-y-z. */
+	enum spi_nor_protocol_index	pindex;
+
+	/*
+	 * The bit <wbit> in DWORD<windex> tells us whether the
+	 * Fast Read x-y-z command is supported.
+	 */
+	int				windex;
+	int				wbit;
+
+	/*
+	 * The half-word at offset <hshift> in DWORD<hindex> encodes the
+	 * op code, the number of mode clocks and the number of wait states
+	 * to be used by Fast Read x-y-z commands.
+	 */
+	int				hindex;
+	int				hshift;
+};
+
+static const struct sfdp_read sfdp_reads[] = {
+	/* Supported: DWORD0 bit 16,  Settings: DWORD3 bit[15:0] */
+	{SNOR_PINDEX_1_1_2, 0, 16, 3,  0},
+
+	/* Supported: DWORD0 bit 20,  Settings: DWORD3 bit[31:16] */
+	{SNOR_PINDEX_1_2_2, 0, 20, 3, 16},
+
+	/* Supported: DWORD4 bit  0,  Settings: DWORD5 bit[31:16] */
+	{SNOR_PINDEX_2_2_2, 4,  0, 5, 16},
+
+	/* Supported: DWORD0 bit 22,  Settings: DWORD2 bit[31:16] */
+	{SNOR_PINDEX_1_1_4, 0, 22, 2, 16},
+
+	/* Supported: DWORD0 bit 21,  Settings: DWORD2 bit[15:0] */
+	{SNOR_PINDEX_1_4_4, 0, 21, 2, 0},
+
+	/* Supported: DWORD4 bit  4,  Settings: DWORD6 bit[31:16] */
+	{SNOR_PINDEX_4_4_4, 4, 4, 6, 16},
+};
+
+
+/* Sector Erase settings. */
+#define BFPT_ERASE_SIZE(half)		((((u16)(half)) >> 0) & 0xff)
+#define BFPT_ERASE_OP_CODE(half)	((((u16)(half)) >> 8) & 0xff)
+
+struct sfdp_erase {
+	/*
+	 * The half-word at offset <hshift> in DWORD<hindex> encodes the
+	 * op code and erase sector size to be used by Sector Erase commands.
+	 */
+	int		hindex;
+	int		hshift;
+};
+
+static const struct sfdp_erase sfdp_erases[SNOR_MAX_ERASE_TYPES] = {
+	/* Erase Type 1 in DWORD7 bits[15:0] */
+	{7, 0},
+
+	/* Erase Type 2 in DWORD7 bits[31:16] */
+	{7, 16},
+
+	/* Erase Type 3 in DWORD8 bits[15:0] */
+	{8, 0},
+
+	/* Erase Type 4: in DWORD8 bits[31:16] */
+	{8, 16},
+};
+
+
+static int spi_nor_parse_bfpt(struct spi_nor *nor,
+			      const struct sfdp_parameter_header *bfpt_header,
+			      struct spi_nor_basic_flash_parameter *params)
+{
+	struct sfdp_bfpt bfpt;
+	size_t len;
+	int i, err;
+	u32 addr;
+	u16 half;
+
+	/* JESD216 Basic Flash Parameter Table length is at least 9 words. */
+	if (bfpt_header->length < 9)
+		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;
+
+	for (i = 0; i < SFDP_BFPT_MAX_WORDS; ++i)
+		bfpt.word[i] = le32_to_cpu(bfpt.word[i]);
+
+	memset(params, 0, sizeof(*params));
+
+	/* Fast Read settings. */
+	params->rd_modes = (SNOR_MODE_SLOW | SNOR_MODE_1_1_1);
+	spi_nor_set_read_settings(&params->reads[SNOR_PINDEX_SLOW],
+				  0, 0, SPINOR_OP_READ);
+	spi_nor_set_read_settings(&params->reads[SNOR_PINDEX_1_1_1],
+				  0, 8, SPINOR_OP_READ_FAST);
+
+	for (i = 0; i < ARRAY_SIZE(sfdp_reads); ++i) {
+		const struct sfdp_read *rd_info = &sfdp_reads[i];
+		struct spi_nor_read *read = &params->reads[rd_info->pindex];
+
+		if (!(bfpt.word[rd_info->windex] & BIT(rd_info->wbit)))
+			continue;
+
+		params->rd_modes |= BIT(rd_info->pindex);
+		half = bfpt.word[rd_info->hindex] >> rd_info->hshift;
+		read->num_mode_clocks = BFPT_FAST_READ_MODE_CLOCKS(half);
+		read->num_wait_states = BFPT_FAST_READ_WAIT_STATES(half);
+		read->opcode = BFPT_FAST_READ_OP_CODE(half);
+	}
+
+	/* Page Program settings. */
+	params->wr_modes = SNOR_MODE_1_1_1;
+	params->page_programs[SNOR_PINDEX_1_1_1] = SPINOR_OP_PP;
+
+	/* Sector Erase settings. */
+	for (i = 0; i < SNOR_MAX_ERASE_TYPES; ++i) {
+		const struct sfdp_erase *er_info = &sfdp_erases[i];
+		struct spi_nor_erase_type *erase = &params->erase_types[i];
+
+		half = bfpt.word[er_info->hindex] >> er_info->hshift;
+		erase->size = BFPT_ERASE_SIZE(half);
+		erase->opcode = BFPT_ERASE_OP_CODE(half);
+	}
+
+	/* Stop here if not JESD216 rev A or later. */
+	if (bfpt_header->length < 15)
+		return 0;
+
+	/* Enable Quad I/O. */
+	switch (bfpt.word[14] & BFPT_WORD14_QER_MASK) {
+	default:
+	case BFPT_WORD14_QER_NONE:
+		break;
+
+	case BFPT_WORD14_QER_SR2_BIT1_BUGGY:
+	case BFPT_WORD14_QER_SR2_BIT1_NO_RD:
+		params->enable_quad_io = spansion_quad_enable;
+		break;
+
+	case BFPT_WORD14_QER_SR1_BIT6:
+		params->enable_quad_io = macronix_quad_enable;
+		break;
+
+	case BFPT_WORD14_QER_SR2_BIT7:
+		params->enable_quad_io = sr2_bit7_quad_enable;
+		break;
+
+	case BFPT_WORD14_QER_SR2_BIT1:
+		params->enable_quad_io = spansion_new_quad_enable;
+		break;
+	}
+
+	return 0;
+}
+
+static int spi_nor_parse_sfdp(struct spi_nor *nor,
+			      struct spi_nor_basic_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) ? err : bfpt_header->minor;
+}
+
 static int spi_nor_init_params(struct spi_nor *nor,
 			       const struct flash_info *info,
 			       struct spi_nor_basic_flash_parameter *params)
 {
-	// TODO: parse SFDP table
+	int jesd216_minor = -EINVAL;
+
+	/* First trying to parse SFDP tables. */
+	if (!(info->flags & SPI_NOR_SKIP_SFDP)) {
+		jesd216_minor = spi_nor_parse_sfdp(nor, params);
+
+		if (jesd216_minor >= SFDP_JESD216A_MINOR)
+			return 0;
+
+		if (jesd216_minor >= SFDP_JESD216_MINOR)
+			goto set_enable_quad_io;
+	}
 
 	/* If SFDP tables are not available, use legacy settings. */
 	memset(params, 0, sizeof(*params));
@@ -1402,6 +1867,7 @@ static int spi_nor_init_params(struct spi_nor *nor,
 		spi_nor_set_erase_settings(&params->erase_types[1],
 					   SNOR_ERASE_4K, SPINOR_OP_BE_4K_PMC);
 
+set_enable_quad_io:
 	/* Select the procedure to set the Quad Enable bit. */
 	if (params->rd_modes & (SNOR_MODE_1_1_4 |
 				SNOR_MODE_1_4_4 |
include/linux/mtd/spi-nor.h +6 -0
index 88ac446b1230..8fd9619dabff 100644
@@ -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 */
 
@@ -110,6 +113,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 */
 enum spi_nor_protocol_class {
Signature · 1 line
2.7.4
#18 lore raw

PATCH v3 7/9 mtd: spi-nor: parse SFDP 4-byte Address Instruction Table

In reply to 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).

---
 drivers/mtd/spi-nor/spi-nor.c | 140 +++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 139 insertions(+), 1 deletion(-)
drivers/mtd/spi-nor/spi-nor.c +139 -1
index 7fc0138c1d68..db4874d4af79 100644
@@ -1494,6 +1494,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
@@ -1741,6 +1742,124 @@ static int spi_nor_parse_bfpt(struct spi_nor *nor,
 	return 0;
 }
 
+struct sfdp_4bait {
+	enum spi_nor_protocol_index	pindex;
+	int				wbit;
+};
+
+static int spi_nor_parse_4bait(struct spi_nor *nor,
+			       const struct sfdp_parameter_header *param_header,
+			       struct spi_nor_basic_flash_parameter *params)
+{
+	static const struct sfdp_4bait reads[] = {
+		{SNOR_PINDEX_SLOW,  0},	/* 0x13 */
+		{SNOR_PINDEX_1_1_1, 1}, /* 0x0c */
+		{SNOR_PINDEX_1_1_2, 2}, /* 0x3c */
+		{SNOR_PINDEX_1_2_2, 3},	/* 0xbc */
+		{SNOR_PINDEX_1_1_4, 4},	/* 0x6c */
+		{SNOR_PINDEX_1_4_4, 5},	/* 0xec */
+	};
+	static const struct sfdp_4bait programs[] = {
+		{SNOR_PINDEX_1_1_1, 6}, /* 0x12 */
+		{SNOR_PINDEX_1_1_4, 7}, /* 0x34 */
+		{SNOR_PINDEX_1_4_4, 8}, /* 0x3e */
+	};
+	static const struct sfdp_4bait erases[SNOR_MAX_ERASE_TYPES] = {
+		{SNOR_PINDEX_1_1_1,  9},
+		{SNOR_PINDEX_1_1_1, 10},
+		{SNOR_PINDEX_1_1_1, 11},
+		{SNOR_PINDEX_1_1_1, 12},
+	};
+	u32 word[2], addr, rd_modes, wr_modes, erase_modes;
+	int i, err;
+
+	if (param_header->major != SFDP_JESD216_MAJOR ||
+	    param_header->length < 2)
+		return -EINVAL;
+
+	/* Read the 4-byte Address Instruction Table. */
+	addr = SFDP_PARAM_HEADER_PTP(param_header);
+	err = spi_nor_read_sfdp(nor, addr, sizeof(word), word);
+	if (err)
+		return err;
+
+	for (i = 0; i < 2; ++i)
+		word[i] = le32_to_cpu(word[i]);
+
+	/*
+	 * Compute the subset of (Fast) Read commands for which the 4-byte
+	 * version is supported.
+	 */
+	rd_modes = 0;
+	for (i = 0; i < ARRAY_SIZE(reads); ++i) {
+		const struct sfdp_4bait *read = &reads[i];
+
+		if ((params->rd_modes & BIT(read->pindex)) &&
+		    (word[0] & BIT(read->wbit)))
+			rd_modes |= BIT(read->pindex);
+	}
+
+	/*
+	 * Compute the subset of Page Program commands for which the 4-byte
+	 * version is supported.
+	 */
+	wr_modes = 0;
+	for (i = 0; i < ARRAY_SIZE(programs); ++i) {
+		const struct sfdp_4bait *program = &programs[i];
+
+		if ((params->wr_modes & BIT(program->pindex)) &&
+		    (word[0] & BIT(program->wbit)))
+			wr_modes |= BIT(program->pindex);
+	}
+
+	/*
+	 * Compute the subet of Sector Erase commands for which the 4-byte
+	 * version is supported.
+	 */
+	erase_modes = 0;
+	for (i = 0; i < SNOR_MAX_ERASE_TYPES; ++i) {
+		const struct sfdp_4bait *erase = &erases[i];
+
+		if ((params->erase_types[i].size > 0) &&
+		    (word[0] & BIT(erase->wbit)))
+			erase_modes |= 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 (!rd_modes || !wr_modes || !erase_modes)
+		return 0;
+
+	/*
+	 * Discard all operations from the 4-byte instruction set which are
+	 * not supported by this memory.
+	 */
+	params->rd_modes = rd_modes;
+	params->wr_modes = wr_modes;
+	for (i = 0; i < SNOR_MAX_ERASE_TYPES; ++i)
+		if (!(erase_modes & BIT(i)))
+			params->erase_types[i].size = 0;
+
+	/* Use the 4-byte address instruction set. */
+	params->reads[SNOR_PINDEX_SLOW].opcode  = SPINOR_OP_READ_4B;
+	params->reads[SNOR_PINDEX_1_1_1].opcode = SPINOR_OP_READ_FAST_4B;
+	params->reads[SNOR_PINDEX_1_1_2].opcode = SPINOR_OP_READ_1_1_2_4B;
+	params->reads[SNOR_PINDEX_1_2_2].opcode = SPINOR_OP_READ_1_2_2_4B;
+	params->reads[SNOR_PINDEX_1_1_4].opcode = SPINOR_OP_READ_1_1_4_4B;
+	params->reads[SNOR_PINDEX_1_4_4].opcode = SPINOR_OP_READ_1_4_4_4B;
+	params->page_programs[SNOR_PINDEX_1_1_1] = SPINOR_OP_PP_4B;
+	params->page_programs[SNOR_PINDEX_1_1_4] = SPINOR_OP_PP_1_1_4_4B;
+	params->page_programs[SNOR_PINDEX_1_4_4] = SPINOR_OP_PP_1_4_4_4B;
+	for (i = 0; i < SNOR_MAX_ERASE_TYPES; ++i)
+		params->erase_types[i].opcode = (word[1] >> (i * 8)) & 0xff;
+
+	nor->addr_width = 4;
+	return 0;
+}
+
 static int spi_nor_parse_sfdp(struct spi_nor *nor,
 			      struct spi_nor_basic_flash_parameter *params)
 {
@@ -1808,6 +1927,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) ? err : bfpt_header->minor;
@@ -2162,7 +2298,9 @@ 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 */
Signature · 1 line
2.7.4
#19 lore raw

PATCH v3 8/9 mtd: spi-nor: add support to Macronix mx66l1g45g

In reply to Cyrille Pitchen

This patch adds an entry in the spi_nor_ids[] table to add support to the Macronix mx66l1g45g.

---
 drivers/mtd/spi-nor/spi-nor.c | 1 +
 1 file changed, 1 insertion(+)
drivers/mtd/spi-nor/spi-nor.c +1 -0
index db4874d4af79..eb21d3d4e4e6 100644
@@ -937,6 +937,7 @@ static const struct flash_info spi_nor_ids[] = {
 	{ "mx25l25635e", INFO(0xc22019, 0, 64 * 1024, 512, 0) },
 	{ "mx25l25655e", INFO(0xc22619, 0, 64 * 1024, 512, 0) },
 	{ "mx66l51235l", INFO(0xc2201a, 0, 64 * 1024, 1024, SPI_NOR_QUAD_READ) },
+	{ "mx66l1g45g",  INFO(0xc2201b, 0, 64 * 1024, 2048, SPI_NOR_QUAD_READ) },
 	{ "mx66l1g55g",  INFO(0xc2261b, 0, 64 * 1024, 2048, SPI_NOR_QUAD_READ) },
 
 	/* Micron */
Signature · 1 line
2.7.4
#20 lore raw

Re: [PATCH v3 8/9] mtd: spi-nor: add support to Macronix mx66l1g45g

Quoted 14 lines · Cyrille Pitchen

This patch adds an entry in the spi_nor_ids[] table to add support to the Macronix mx66l1g45g.

---
 drivers/mtd/spi-nor/spi-nor.c | 1 +
 1 file changed, 1 insertion(+)
drivers/mtd/spi-nor/spi-nor.c +0 -0
index db4874d4af79..eb21d3d4e4e6 100644

drivers/mtd/spi-nor/spi-nor.c static const struct flash_info spi_nor_ids[] = { · line 943

Quoted 7 lines
drivers/mtd/spi-nor/spi-nor.c +1 -0
@@ -937,6 +937,7 @@ static const struct flash_info spi_nor_ids[] = {
 	{ "mx25l25635e", INFO(0xc22019, 0, 64 * 1024, 512, 0) },
 	{ "mx25l25655e", INFO(0xc22619, 0, 64 * 1024, 512, 0) },
 	{ "mx66l51235l", INFO(0xc2201a, 0, 64 * 1024, 1024, SPI_NOR_QUAD_READ) },
+	{ "mx66l1g45g",  INFO(0xc2201b, 0, 64 * 1024, 2048, SPI_NOR_QUAD_READ) },
 	{ "mx66l1g55g",  INFO(0xc2261b, 0, 64 * 1024, 2048, SPI_NOR_QUAD_READ) },
 
 	/* Micron */

This should be submitted separately as it could be applied right away.

Signature · 2 lines
Best regards,
Marek Vasut
#21 lore raw

Re: [PATCH v3 8/9] mtd: spi-nor: add support to Macronix mx66l1g45g

Hi Marek,

Quoted 20 lines · Marek Vasut
Quoted 19 lines · Cyrille Pitchen

This patch adds an entry in the spi_nor_ids[] table to add support to the Macronix mx66l1g45g.

---
 drivers/mtd/spi-nor/spi-nor.c | 1 +
 1 file changed, 1 insertion(+)
drivers/mtd/spi-nor/spi-nor.c +1 -0
index db4874d4af79..eb21d3d4e4e6 100644
@@ -937,6 +937,7 @@ static const struct flash_info spi_nor_ids[] = {
 	{ "mx25l25635e", INFO(0xc22019, 0, 64 * 1024, 512, 0) },
 	{ "mx25l25655e", INFO(0xc22619, 0, 64 * 1024, 512, 0) },
 	{ "mx66l51235l", INFO(0xc2201a, 0, 64 * 1024, 1024, SPI_NOR_QUAD_READ) },
+	{ "mx66l1g45g",  INFO(0xc2201b, 0, 64 * 1024, 2048, SPI_NOR_QUAD_READ) },
 	{ "mx66l1g55g",  INFO(0xc2261b, 0, 64 * 1024, 2048, SPI_NOR_QUAD_READ) },
 
Quoted 4 lines

drivers/mtd/spi-nor/spi-nor.c static const struct flash_info spi_nor_ids[] = { · line 943

Quoted 2 lines
drivers/mtd/spi-nor/spi-nor.c +0 -0
 	/* Micron */

This should be submitted separately as it could be applied right away.

I totally agree with you. Indeed I have only provided patch 8 and 9 because I refer to these memories in the cover letter and to "prove" that I did really test every memory of the list. If everyone trust me about the test I do, I will remove patch 8 and 9 in the next version.

By the way, I need to add a remark about the Spansion S25FL127S. The sample I have is compliant with neither the JESD216B specification nor the Cypress datasheet. More precisely, the Cypress datasheet claims that the S25FL127S is compliant with the JESD216B (minor 6) so its Basic Flash Parameter Table (BFPT) should contain 16 DWORDS.
However my sample claims to be JESD216B compliant (I read minor 6) and that the BFPT has 16 DWORDS but the 7 last DWORDS of this table are all 0xFFFFFFFF. Only the first 9 DWORDS are properly filled, the DWORDS described in JESD216 (minor 0).
Also my sample pretend to provide the optional 4-byte address instruction set (4BAIS) table but the data I read are once again all 0xFFFFFFFF.

It's surprising that this 128Mbit memory supports the 4-byte address instruction set but the 4-byte Fast Read actually seems to be supported.

Consequently, Fast Read operations work but Sector Erase cannot due to the 0xFF op code read from the 4BAIS table. I think I just have a broken sample so if anyone can test with another sample and confirm that the SFDP series also work with Spansion S25FL127S it would be nice! :)

Best regards,

Cyrille

#22 lore raw

Re: [PATCH v3 8/9] mtd: spi-nor: add support to Macronix mx66l1g45g

Quoted 1 line · Cyrille Pitchen

Hi Marek,

Hi,

Quoted 26 lines · Marek Vasut
Quoted 20 lines · Cyrille Pitchen
Quoted 19 lines

This patch adds an entry in the spi_nor_ids[] table to add support to the Macronix mx66l1g45g.

---
 drivers/mtd/spi-nor/spi-nor.c | 1 +
 1 file changed, 1 insertion(+)
drivers/mtd/spi-nor/spi-nor.c +1 -0
index db4874d4af79..eb21d3d4e4e6 100644
@@ -937,6 +937,7 @@ static const struct flash_info spi_nor_ids[] = {
 	{ "mx25l25635e", INFO(0xc22019, 0, 64 * 1024, 512, 0) },
 	{ "mx25l25655e", INFO(0xc22619, 0, 64 * 1024, 512, 0) },
 	{ "mx66l51235l", INFO(0xc2201a, 0, 64 * 1024, 1024, SPI_NOR_QUAD_READ) },
+	{ "mx66l1g45g",  INFO(0xc2201b, 0, 64 * 1024, 2048, SPI_NOR_QUAD_READ) },
 	{ "mx66l1g55g",  INFO(0xc2261b, 0, 64 * 1024, 2048, SPI_NOR_QUAD_READ) },
 
Quoted 4 lines

drivers/mtd/spi-nor/spi-nor.c static const struct flash_info spi_nor_ids[] = { · line 943

Quoted 2 lines
drivers/mtd/spi-nor/spi-nor.c +0 -0
 	/* Micron */

This should be submitted separately as it could be applied right away.

Quoted 4 lines

I totally agree with you. Indeed I have only provided patch 8 and 9 because I refer to these memories in the cover letter and to "prove" that I did really test every memory of the list. If everyone trust me about the test I do, I will remove patch 8 and 9 in the next version.

I think 9 needs to stay or be separated somehow differently from the series, since it also adds some code to support the flash. Otherwise, go for submitting them separately.

Quoted 12 lines

By the way, I need to add a remark about the Spansion S25FL127S. The sample I have is compliant with neither the JESD216B specification nor the Cypress datasheet. More precisely, the Cypress datasheet claims that the S25FL127S is compliant with the JESD216B (minor 6) so its Basic Flash Parameter Table (BFPT) should contain 16 DWORDS.
However my sample claims to be JESD216B compliant (I read minor 6) and that the BFPT has 16 DWORDS but the 7 last DWORDS of this table are all 0xFFFFFFFF. Only the first 9 DWORDS are properly filled, the DWORDS described in JESD216 (minor 0).
Also my sample pretend to provide the optional 4-byte address instruction set (4BAIS) table but the data I read are once again all 0xFFFFFFFF.

Quoted 2 lines

It's surprising that this 128Mbit memory supports the 4-byte address instruction set but the 4-byte Fast Read actually seems to be supported.

They probably use the same HDL design for the controller block in many memories, they just burn in different ID/SFDP/geometry tables for each chip they roll out from the factory based on how well the memory array works.

Quoted 4 lines

Consequently, Fast Read operations work but Sector Erase cannot due to the 0xFF op code read from the 4BAIS table. I think I just have a broken sample so if anyone can test with another sample and confirm that the SFDP series also work with Spansion S25FL127S it would be nice! :)

I don't have one, sorry.

Signature · 2 lines
Best regards,
Marek Vasut
#23 lore raw

PATCH v3 9/9 mtd: spi-nor: add support to SST sst26* QSPI memories

In reply to Cyrille Pitchen

This patch adds support to the SST sst26* QSPI memories. It also adds a new SST_BLOCK_PROTECT flag so spi_nor_scan() sends a SST Global Block Protection Unlock (98h) command once for all otherwise later Sector Erase and Page Program commands would fail.

It was tested with sst26vf016b, sst26vf032b, sst26vf032ba and sst26vf064b on a sama5d2 xplained board at 55.3MHz and 3.3V.

sst26wf040b and sst26wf080b were not tested since they are 1.8V memories hence don't fit the sama5d2 xplained board requirements.

---
 drivers/mtd/spi-nor/spi-nor.c | 20 ++++++++++++++++++++
 include/linux/mtd/spi-nor.h   |  1 +
 2 files changed, 21 insertions(+)
drivers/mtd/spi-nor/spi-nor.c +20 -0
index eb21d3d4e4e6..300ee38f4e9a 100644
@@ -81,6 +81,7 @@ struct flash_info {
 					 * to support memory size above 128Mib.
 					 */
 #define SPI_NOR_SKIP_SFDP	BIT(11) /* Skip read of SFDP tables */
+#define SST_BLOCK_PROTECT	BIT(12)	/* use SST Unlock Block-Protection */
 };
 
 #define JEDEC_MFR(info)	((info)->id[0])
@@ -999,6 +1000,11 @@ static const struct flash_info spi_nor_ids[] = {
 	{ "sst25wf040b", INFO(0x621613, 0, 64 * 1024,  8, SECT_4K) },
 	{ "sst25wf040",  INFO(0xbf2504, 0, 64 * 1024,  8, SECT_4K | SST_WRITE) },
 	{ "sst25wf080",  INFO(0xbf2505, 0, 64 * 1024, 16, SECT_4K | SST_WRITE) },
+	{ "sst26vf016b", INFO(0xbf2641, 0, 64 * 1024, 32, SECT_4K | SST_BLOCK_PROTECT) },
+	{ "sst26vf032b", INFO(0xbf2642, 0, 64 * 1024, 64, SECT_4K | SST_BLOCK_PROTECT) },
+	{ "sst26vf064b", INFO(0xbf2643, 0, 64 * 1024, 128, SECT_4K | SST_BLOCK_PROTECT) },
+	{ "sst26wf040b", INFO(0xbf2654, 0, 64 * 1024,  8, SECT_4K | SST_BLOCK_PROTECT) },
+	{ "sst26wf080b", INFO(0xbf2658, 0, 64 * 1024, 16, SECT_4K | SST_BLOCK_PROTECT) },
 
 	/* ST Microelectronics -- newer production may have feature updates */
 	{ "m25p05",  INFO(0x202010,  0,  32 * 1024,   2, 0) },
@@ -1135,6 +1141,14 @@ static int spi_nor_read(struct mtd_info *mtd, loff_t from, size_t len,
 	return ret;
 }
 
+static int sst_unlock_block_protection(struct spi_nor *nor)
+{
+	int ret;
+
+	ret = write_enable(nor);
+	return ret ? ret : nor->write_reg(nor, SPINOR_OP_ULBPR, NULL, 0);
+}
+
 static int sst_write(struct mtd_info *mtd, loff_t to, size_t len,
 		size_t *retlen, const u_char *buf)
 {
@@ -2321,6 +2335,12 @@ int spi_nor_scan(struct spi_nor *nor, const char *name,
 		return -EINVAL;
 	}
 
+	if (info->flags & SST_BLOCK_PROTECT) {
+		ret = sst_unlock_block_protection(nor);
+		if (ret)
+			return ret;
+	}
+
 	dev_info(dev, "%s (%lld Kbytes)\n", info->name,
 			(long long)mtd->size >> 10);
 
include/linux/mtd/spi-nor.h +1 -0
index 8fd9619dabff..c8f7e231a25c 100644
@@ -80,6 +80,7 @@
 #define SPINOR_OP_BP		0x02	/* Byte program */
 #define SPINOR_OP_WRDI		0x04	/* Write disable */
 #define SPINOR_OP_AAI_WP	0xad	/* Auto address increment word program */
+#define SPINOR_OP_ULBPR		0x98	/* Global Block Protection Unlock */
 
 /* Used for Macronix and Winbond flashes. */
 #define SPINOR_OP_EN4B		0xb7	/* Enter 4-byte mode */
Signature · 1 line
2.7.4
#24 lore raw

Re: [PATCH v3 9/9] mtd: spi-nor: add support to SST sst26* QSPI memories

Quoted 30 lines · Cyrille Pitchen

This patch adds support to the SST sst26* QSPI memories. It also adds a new SST_BLOCK_PROTECT flag so spi_nor_scan() sends a SST Global Block Protection Unlock (98h) command once for all otherwise later Sector Erase and Page Program commands would fail.

It was tested with sst26vf016b, sst26vf032b, sst26vf032ba and sst26vf064b on a sama5d2 xplained board at 55.3MHz and 3.3V.

sst26wf040b and sst26wf080b were not tested since they are 1.8V memories hence don't fit the sama5d2 xplained board requirements.

---
 drivers/mtd/spi-nor/spi-nor.c | 20 ++++++++++++++++++++
 include/linux/mtd/spi-nor.h   |  1 +
 2 files changed, 21 insertions(+)
drivers/mtd/spi-nor/spi-nor.c +1 -0
index eb21d3d4e4e6..300ee38f4e9a 100644
@@ -81,6 +81,7 @@ struct flash_info {
 					 * to support memory size above 128Mib.
 					 */
 #define SPI_NOR_SKIP_SFDP	BIT(11) /* Skip read of SFDP tables */
+#define SST_BLOCK_PROTECT	BIT(12)	/* use SST Unlock Block-Protection */
 };
 
 #define JEDEC_MFR(info)	((info)->id[0])

drivers/mtd/spi-nor/spi-nor.c static const struct flash_info spi_nor_ids[] = { · line 1007

Quoted 8 lines
drivers/mtd/spi-nor/spi-nor.c +5 -0
@@ -999,6 +1000,11 @@ static const struct flash_info spi_nor_ids[] = {
 	{ "sst25wf040b", INFO(0x621613, 0, 64 * 1024,  8, SECT_4K) },
 	{ "sst25wf040",  INFO(0xbf2504, 0, 64 * 1024,  8, SECT_4K | SST_WRITE) },
 	{ "sst25wf080",  INFO(0xbf2505, 0, 64 * 1024, 16, SECT_4K | SST_WRITE) },
+	{ "sst26vf016b", INFO(0xbf2641, 0, 64 * 1024, 32, SECT_4K | SST_BLOCK_PROTECT) },
+	{ "sst26vf032b", INFO(0xbf2642, 0, 64 * 1024, 64, SECT_4K | SST_BLOCK_PROTECT) },
+	{ "sst26vf064b", INFO(0xbf2643, 0, 64 * 1024, 128, SECT_4K | SST_BLOCK_PROTECT) },
+	{ "sst26wf040b", INFO(0xbf2654, 0, 64 * 1024,  8, SECT_4K | SST_BLOCK_PROTECT) },
+	{ "sst26wf080b", INFO(0xbf2658, 0, 64 * 1024, 16, SECT_4K | SST_BLOCK_PROTECT) },

You should split adding the chip and adding the sst block protection into two separate patches.

drivers/mtd/spi-nor/spi-nor.c static int spi_nor_read(struct mtd_info *mtd, loff_t from, size_t len, · line 1149

Quoted 12 lines
drivers/mtd/spi-nor/spi-nor.c +6 -0
 	/* ST Microelectronics -- newer production may have feature updates */
 	{ "m25p05",  INFO(0x202010,  0,  32 * 1024,   2, 0) },
@@ -1135,6 +1141,14 @@ static int spi_nor_read(struct mtd_info *mtd, loff_t from, size_t len,
 	return ret;
 }
 
+static int sst_unlock_block_protection(struct spi_nor *nor)
+{
+	int ret;
+
+	ret = write_enable(nor);
+	return ret ? ret : nor->write_reg(nor, SPINOR_OP_ULBPR, NULL, 0);

I'm not a big fan of the ternary here, what about:

ret = write_enable()
if (!ret)
ret = nor->write_reg();
return ret;

Quoted 31 lines
drivers/mtd/spi-nor/spi-nor.c +8 -0
+}
+
 static int sst_write(struct mtd_info *mtd, loff_t to, size_t len,
 		size_t *retlen, const u_char *buf)
 {
@@ -2321,6 +2335,12 @@ int spi_nor_scan(struct spi_nor *nor, const char *name,
 		return -EINVAL;
 	}
 
+	if (info->flags & SST_BLOCK_PROTECT) {
+		ret = sst_unlock_block_protection(nor);
+		if (ret)
+			return ret;
+	}
+
 	dev_info(dev, "%s (%lld Kbytes)\n", info->name,
 			(long long)mtd->size >> 10);
 
include/linux/mtd/spi-nor.h +1 -0
index 8fd9619dabff..c8f7e231a25c 100644
@@ -80,6 +80,7 @@
 #define SPINOR_OP_BP		0x02	/* Byte program */
 #define SPINOR_OP_WRDI		0x04	/* Write disable */
 #define SPINOR_OP_AAI_WP	0xad	/* Auto address increment word program */
+#define SPINOR_OP_ULBPR		0x98	/* Global Block Protection Unlock */
 
 /* Used for Macronix and Winbond flashes. */
 #define SPINOR_OP_EN4B		0xb7	/* Enter 4-byte mode */
Signature · 2 lines
Best regards,
Marek Vasut