Skip to contentSkip to conversation

mtd: spi-nor: add driver for Atmel QSPI controller

PATCH Superseded Cyrille Pitchen 2 participants 20 messages Jan 8, 2016 – Jan 29, 2016

Subsystems (4)

v2 of 3 Superseded Latest is v3 →

lore
Share & tools
lore
#0 lore raw

v2 cover mtd: spi-nor: add driver for Atmel QSPI controller

Hi all,

This series of patches adds support to the Atmel QSPI controller available on sama5d2 SoCs. It was tested on a sama5d2 xplained ultra board with a Micron n25q128a13 QSPI memory and a at25df321a SPI memory.

In order to use the Micron memory in its Quad SPI mode, the spi-nor framework needed to be patched to fix the support of Quad/Dual SPI protocols with some memory manufacturers such as Spansion, Micron and Macronix. There are many comments in the source code to explain the implementation choices based on the datasheets from memory manufacturers.

This series was based and tested on linux-next

1 - Atmel QSPI + Micron n25q128a13 (atmel-quadspi.c driver)

SPI 1-1-1: This mode was tested replacing SPI_NOR_QUAD by SPI_NOR_FAST as
argument to spi_nor_scan() called from atmel_qspi_probe().

SPI 1-1-4: Bootloaders (at91bootstrap/uboot) don't enable the Quad SPI
mode of the Micron memory. When probed from Linux, the memory uses its Extended SPI mode and replies to the regular Read ID (0x9f) command.

SPI 4-4-4: The romcode enabled the Quad SPI mode the of Micron memory
before loading the at91bootstrap. When probed from Linux, the memory uses its Quad SPI mode and no longer replies to the regular Read ID (0x9f) command but instead to the Read ID Multiple I/O (0xaf) command. The memory expects ALL commands to use the SPI 4-4-4 protocol.

2 - Atmel QSPI + Spansion s25fl512 (atmel-quadspi.c driver)

SPI 1-1-4: default settings. Early developpements on a FPGA board.

3 - Atmel SPI + at25df321a (m25p80.c driver)

SPI 1-1-1: tested with the m25p80 driver for non regression purpose.

Winbond and Macronix memories were NOT tested. The patches were written only based on the manufacturers' datasheets.

Best regards,

Cyrille

ChangeLog:

v1 -> v2:
- I've totally reworked the whole series. Especially I have split the former patch 2 to ease the code review. The Single/Dual/Quad SPI mode support fixes are now done by manufacturer.
- fix support and probing of Winbond memories.
- m25p80: compute the number of dummy bytes from both the number of dummy clock cycles and the number of I/O lines used to send them.
- fix Kconfig dependencies for the Atmel QSPI controller driver (it should fix the kbuild test robot warnings).
- add more comments.

Cyrille Pitchen (14):
mtd: spi-nor: remove micron_quad_enable()
mtd: spi-nor: properly detect the memory when it boots in Quad or Dual
mode
mtd: spi-nor: select op codes and SPI NOR protocols by manufacturer
mtd: spi-nor: fix support of Macronix memories
mtd: spi-nor: fix support of Winbond memories
mtd: spi-nor: fix support of Micron memories
mtd: spi-nor: fix support of Spansion memories
mtd: spi-nor: configure the number of dummy clock cycles by
manufacturer
mtd: spi-nor: configure the number of dummy clock cycles on Micron
memories
mtd: spi-nor: configure the number of dummy clock cycles on Macronix
memories
mtd: spi-nor: configure the number of dummy clock cycles on Spansion
memories
mtd: m25p80: add support of dual and quad spi protocols to all
commands
Documentation: atmel-quadspi: add binding file for Atmel QSPI driver
mtd: atmel-quadspi: add driver for Atmel QSPI controller

 .../devicetree/bindings/mtd/atmel-quadspi.txt      |  32 +
 drivers/mtd/devices/m25p80.c                       | 192 ++++-
 drivers/mtd/spi-nor/Kconfig                        |   9 +
 drivers/mtd/spi-nor/Makefile                       |   1 +
 drivers/mtd/spi-nor/atmel-quadspi.c                | 877 +++++++++++++++++++
 drivers/mtd/spi-nor/spi-nor.c                      | 928 +++++++++++++++++++--
 include/linux/mtd/spi-nor.h                        |  71 +-
 7 files changed, 1985 insertions(+), 125 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/mtd/atmel-quadspi.txt
 create mode 100644 drivers/mtd/spi-nor/atmel-quadspi.c
Signature · 1 line
1.8.2.2
#1 lore raw

PATCH v2 1/14 mtd: spi-nor: remove micron_quad_enable()

This patch remove the micron_quad_enable() function which force the Quad SPI mode. However, once this mode is enabled, the Micron memory expect ALL commands to use the SPI 4-4-4 protocol. Hence a failure does occur when calling spi_nor_wait_till_ready() right after the update of the Enhanced Volatile Configuration Register (EVCR) in the micron_quad_enable() as the SPI controller driver is not aware about the protocol change.

Since there is almost no performance increase using Fast Read 4-4-4 commands instead of Fast Read 1-1-4 commands, we rather keep on using the Extended SPI mode than enabling the Quad SPI mode.

Let's take the example of the pretty standard use of 8 dummy cycles during Fast Read operations on 64KB erase sectors:

Fast Read 1-1-4 requires 8 cycles for the command, then 24 cycles for the 3byte address followed by 8 dummy clock cycles and finally 65536*2 cycles for the read data; so 131112 clock cycles.

On the other hand the Fast Read 4-4-4 would require 2 cycles for the command, then 6 cycles for the 3byte address followed by 8 dummy clock cycles and finally 65536*2 cycles for the read data. So 131088 clock cycles. The theorical bandwidth increase is 0.0%.

Now using Fast Read operations on 512byte pages:
Fast Read 1-1-4 needs 8+24+8+(512*2) = 1064 clock cycles whereas Fast Read 4-4-4 would requires 2+6+8+(512*2) = 1040 clock cycles. Hence the theorical bandwidth increase is 2.3%.
Consecutive reads for non sequential pages is not a relevant use case so The Quad SPI mode is not worth it.

mtd_speedtest seems to confirm these figures.

Fixes 548cd3ab54da ("mtd: spi-nor: Add quad I/O support for Micron SPI NOR")
---
 drivers/mtd/spi-nor/spi-nor.c | 46 +------------------------------------------
 1 file changed, 1 insertion(+), 45 deletions(-)
drivers/mtd/spi-nor/spi-nor.c +1 -45
index ed0c19c558b5..3028c06547c1 100644
@@ -1100,45 +1100,6 @@ static int spansion_quad_enable(struct spi_nor *nor)
 	return 0;
 }
 
-static int micron_quad_enable(struct spi_nor *nor)
-{
-	int ret;
-	u8 val;
-
-	ret = nor->read_reg(nor, SPINOR_OP_RD_EVCR, &val, 1);
-	if (ret < 0) {
-		dev_err(nor->dev, "error %d reading EVCR\n", ret);
-		return ret;
-	}
-
-	write_enable(nor);
-
-	/* set EVCR, enable quad I/O */
-	nor->cmd_buf[0] = val & ~EVCR_QUAD_EN_MICRON;
-	ret = nor->write_reg(nor, SPINOR_OP_WD_EVCR, nor->cmd_buf, 1);
-	if (ret < 0) {
-		dev_err(nor->dev, "error while writing EVCR register\n");
-		return ret;
-	}
-
-	ret = spi_nor_wait_till_ready(nor);
-	if (ret)
-		return ret;
-
-	/* read EVCR and check it */
-	ret = nor->read_reg(nor, SPINOR_OP_RD_EVCR, &val, 1);
-	if (ret < 0) {
-		dev_err(nor->dev, "error %d reading EVCR\n", ret);
-		return ret;
-	}
-	if (val & EVCR_QUAD_EN_MICRON) {
-		dev_err(nor->dev, "Micron EVCR Quad bit not clear\n");
-		return -EINVAL;
-	}
-
-	return 0;
-}
-
 static int set_quad_mode(struct spi_nor *nor, const struct flash_info *info)
 {
 	int status;
@@ -1152,12 +1113,7 @@ static int set_quad_mode(struct spi_nor *nor, const struct flash_info *info)
 		}
 		return status;
 	case SNOR_MFR_MICRON:
-		status = micron_quad_enable(nor);
-		if (status) {
-			dev_err(nor->dev, "Micron quad-read not enabled\n");
-			return -EINVAL;
-		}
-		return status;
+		return 0;
 	default:
 		status = spansion_quad_enable(nor);
 		if (status) {
Signature · 1 line
1.8.2.2
#2 lore raw

PATCH v2 2/14 mtd: spi-nor: properly detect the memory when it boots in Quad or Dual mode

In reply to Cyrille Pitchen

The quad (or dual) mode of a spi-nor memory may be enabled at boot time by non-volatile bits in some setting register. Also such a mode may have already been enabled at early stage by some boot loader.

Hence, we should not guess the spi-nor memory is always configured for the regular SPI 1-1-1 protocol.

Micron and Macronix memories, once their Quad (or dual for Micron) mode enabled, no longer process the regular JEDEC Read ID (0x9f) command but instead reply to a new command: JEDEC Read ID Multiple I/O (0xaf). Besides, in Quad mode both memory manufacturers expect ALL commands to use the SPI 4-4-4 protocol. For Micron memories, enabling their Dual mode implies to use the SPI 2-2-2 protocol for ALL commands.

Winbond memories, once their Quad mode enabled, expect ALL commands to use the SPI 4-4-4 protocol. Unlike Micron and Macronix memories, they still reply to the regular JEDEC Read ID (0x9f) command.

---
 drivers/mtd/spi-nor/spi-nor.c | 83 ++++++++++++++++++++++++++++++++++++++++---
 include/linux/mtd/spi-nor.h   | 50 ++++++++++++++++++++++++--
 2 files changed, 127 insertions(+), 6 deletions(-)
drivers/mtd/spi-nor/spi-nor.c +79 -4
index 3028c06547c1..8967319ea7da 100644
@@ -73,6 +73,12 @@ struct flash_info {
 
 #define JEDEC_MFR(info)	((info)->id[0])
 
+struct read_id_config {
+	enum read_mode		mode;
+	enum spi_nor_protocol	proto;
+	u8			opcode;
+};
+
 static const struct flash_info *spi_nor_match_id(const char *name);
 
 /*
@@ -879,11 +885,22 @@ static const struct flash_info spi_nor_ids[] = {
 	{ },
 };
 
-static const struct flash_info *spi_nor_read_id(struct spi_nor *nor)
+static const struct flash_info *spi_nor_read_id(struct spi_nor *nor,
+						enum read_mode mode)
 {
-	int			tmp;
+	int			i, tmp;
 	u8			id[SPI_NOR_MAX_ID_LEN];
 	const struct flash_info	*info;
+	static const struct read_id_config configs[] = {
+		/* Winbond QPI mode */
+		{SPI_NOR_QUAD, SNOR_PROTO_4_4_4, SPINOR_OP_RDID},
+
+		/* Micron Quad mode & Macronix QPI mode */
+		{SPI_NOR_QUAD, SNOR_PROTO_4_4_4, SPINOR_OP_MIO_RDID},
+
+		/* Micron Dual mode */
+		{SPI_NOR_DUAL, SNOR_PROTO_2_2_2, SPINOR_OP_MIO_RDID}
+	};
 
 	tmp = nor->read_reg(nor, SPINOR_OP_RDID, id, SPI_NOR_MAX_ID_LEN);
 	if (tmp < 0) {
@@ -891,6 +908,58 @@ static const struct flash_info *spi_nor_read_id(struct spi_nor *nor)
 		return ERR_PTR(tmp);
 	}
 
+	/*
+	 * Check whether the SPI NOR memory has already been configured (at
+	 * reset or by some bootloader) to use a protocol other than SPI 1-1-1.
+	 */
+	for (i = 0; i < ARRAY_SIZE(configs); ++i) {
+		int len = SPI_NOR_MAX_ID_LEN;
+		bool is_multi = false;
+
+		/*
+		 * Check the latest read Manufacturer ID + Device ID (3 bytes):
+		 * if they are different from both 0x000000 and 0xffffff, we
+		 * assume that we succeeded in reading a valid JEDEC ID so we
+		 * don't need to try other SPI protocols.
+		 * Indeed when either the protocol or the op code are not valid,
+		 * the SPI NOR memory should not reply to the command. Hence the
+		 * SPI I/O lines remain in their default state: 1 when connected
+		 * to pull-up resistors or 0 with pull-down.
+		 */
+		if (!((id[0] == 0xff && id[1] == 0xff && id[2] == 0xff) ||
+		      (id[0] == 0x00 && id[1] == 0x00 && id[2] == 0x00)))
+			break;
+
+		/* Only try protocols supported by the user. */
+		if (configs[i].mode != mode)
+			continue;
+
+		/* Set this protocol for all commands. */
+		nor->reg_proto = configs[i].proto;
+		nor->read_proto = configs[i].proto;
+		nor->write_proto = configs[i].proto;
+		nor->erase_proto = configs[i].proto;
+
+		/*
+		 * Multiple I/O Read ID only returns the Manufacturer ID
+		 * (1 byte) and the Device ID (2 bytes). So we reset the
+		 * remaining bytes.
+		 */
+		if (configs[i].opcode == SPINOR_OP_MIO_RDID) {
+			is_multi = true;
+			len = 3;
+			memset(id + len, 0, sizeof(id) - len);
+		}
+
+		tmp = nor->read_reg(nor, configs[i].opcode, id, len);
+		if (tmp < 0) {
+			dev_dbg(nor->dev,
+				"error %d reading JEDEC ID%s\n",
+				tmp, (is_multi ? " Multi I/O" : ""));
+			return ERR_PTR(tmp);
+		}
+	}
+
 	for (tmp = 0; tmp < ARRAY_SIZE(spi_nor_ids) - 1; tmp++) {
 		info = &spi_nor_ids[tmp];
 		if (info->id_len) {
@@ -1148,11 +1217,17 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
 	if (ret)
 		return ret;
 
+	/* Reset SPI protocol for all commands */
+	nor->erase_proto = SNOR_PROTO_1_1_1;
+	nor->read_proto = SNOR_PROTO_1_1_1;
+	nor->write_proto = SNOR_PROTO_1_1_1;
+	nor->reg_proto = SNOR_PROTO_1_1_1;
+
 	if (name)
 		info = spi_nor_match_id(name);
 	/* Try to auto-detect if chip name wasn't specified or not found */
 	if (!info)
-		info = spi_nor_read_id(nor);
+		info = spi_nor_read_id(nor, mode);
 	if (IS_ERR_OR_NULL(info))
 		return -ENOENT;
 
@@ -1163,7 +1238,7 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
 	if (name && info->id_len) {
 		const struct flash_info *jinfo;
 
-		jinfo = spi_nor_read_id(nor);
+		jinfo = spi_nor_read_id(nor, mode);
 		if (IS_ERR(jinfo)) {
 			return PTR_ERR(jinfo);
 		} else if (jinfo != info) {
include/linux/mtd/spi-nor.h +48 -2

74 lines not on this page · raw message

Signature · 1 line
1.8.2.2
#3 lore raw

Re: [PATCH linux-next v2 02/14] mtd: spi-nor: properly detect the memory when it boots in Quad or Dual mode

Hi Cyrille,

Sorry for this pretty useless review, but I think you know more about SPI-NOR than I do. So, consider my comments as nitpicks, which shouldn't prevent your series from being applied.

Quoted 81 lines · On Fri, 8 Jan 2016 17:02:14 +0100 Cyrille Pitchen <[email protected]>…

The quad (or dual) mode of a spi-nor memory may be enabled at boot time by non-volatile bits in some setting register. Also such a mode may have already been enabled at early stage by some boot loader.

Hence, we should not guess the spi-nor memory is always configured for the regular SPI 1-1-1 protocol.

Micron and Macronix memories, once their Quad (or dual for Micron) mode enabled, no longer process the regular JEDEC Read ID (0x9f) command but instead reply to a new command: JEDEC Read ID Multiple I/O (0xaf). Besides, in Quad mode both memory manufacturers expect ALL commands to use the SPI 4-4-4 protocol. For Micron memories, enabling their Dual mode implies to use the SPI 2-2-2 protocol for ALL commands.

Winbond memories, once their Quad mode enabled, expect ALL commands to use the SPI 4-4-4 protocol. Unlike Micron and Macronix memories, they still reply to the regular JEDEC Read ID (0x9f) command.

---
 drivers/mtd/spi-nor/spi-nor.c | 83 ++++++++++++++++++++++++++++++++++++++++---
 include/linux/mtd/spi-nor.h   | 50 ++++++++++++++++++++++++--
 2 files changed, 127 insertions(+), 6 deletions(-)
drivers/mtd/spi-nor/spi-nor.c +30 -2
index 3028c06547c1..8967319ea7da 100644
@@ -73,6 +73,12 @@ struct flash_info {
 
 #define JEDEC_MFR(info)	((info)->id[0])
 
+struct read_id_config {
+	enum read_mode		mode;
+	enum spi_nor_protocol	proto;
+	u8			opcode;
+};
+
 static const struct flash_info *spi_nor_match_id(const char *name);
 
 /*
@@ -879,11 +885,22 @@ static const struct flash_info spi_nor_ids[] = {
 	{ },
 };
 
-static const struct flash_info *spi_nor_read_id(struct spi_nor *nor)
+static const struct flash_info *spi_nor_read_id(struct spi_nor *nor,
+						enum read_mode mode)
 {
-	int			tmp;
+	int			i, tmp;
 	u8			id[SPI_NOR_MAX_ID_LEN];
 	const struct flash_info	*info;
+	static const struct read_id_config configs[] = {
+		/* Winbond QPI mode */
+		{SPI_NOR_QUAD, SNOR_PROTO_4_4_4, SPINOR_OP_RDID},
+
+		/* Micron Quad mode & Macronix QPI mode */
+		{SPI_NOR_QUAD, SNOR_PROTO_4_4_4, SPINOR_OP_MIO_RDID},
+
+		/* Micron Dual mode */
+		{SPI_NOR_DUAL, SNOR_PROTO_2_2_2, SPINOR_OP_MIO_RDID}
+	};
 
 	tmp = nor->read_reg(nor, SPINOR_OP_RDID, id, SPI_NOR_MAX_ID_LEN);
 	if (tmp < 0) {
@@ -891,6 +908,58 @@ static const struct flash_info *spi_nor_read_id(struct spi_nor *nor)
 		return ERR_PTR(tmp);
 	}
 
+	/*
+	 * Check whether the SPI NOR memory has already been configured (at
+	 * reset or by some bootloader) to use a protocol other than SPI 1-1-1.
+	 */
+	for (i = 0; i < ARRAY_SIZE(configs); ++i) {
+		int len = SPI_NOR_MAX_ID_LEN;
+		bool is_multi = false;
+
+		/*
+		 * Check the latest read Manufacturer ID + Device ID (3 bytes):
+		 * if they are different from both 0x000000 and 0xffffff, we

drivers/mtd/spi-nor/spi-nor.c static const struct flash_info *spi_nor_read_id(struct spi_nor *nor) · line 931

Quoted 10 lines
drivers/mtd/spi-nor/spi-nor.c +10 -0
+		 * assume that we succeeded in reading a valid JEDEC ID so we
+		 * don't need to try other SPI protocols.
+		 * Indeed when either the protocol or the op code are not valid,
+		 * the SPI NOR memory should not reply to the command. Hence the
+		 * SPI I/O lines remain in their default state: 1 when connected
+		 * to pull-up resistors or 0 with pull-down.
+		 */
+		if (!((id[0] == 0xff && id[1] == 0xff && id[2] == 0xff) ||
+		      (id[0] == 0x00 && id[1] == 0x00 && id[2] == 0x00)))
+			break;

Hopefully, you'll have all the pins configured with pull-up or pull-down resistors, but what if one of them is configured differently?

Shouldn't we try to find a valid chip definition by iterating over the spi_nor_ids[] table after each mode change?

Quoted 79 lines
drivers/mtd/spi-nor/spi-nor.c +39 -2
+
+		/* Only try protocols supported by the user. */
+		if (configs[i].mode != mode)
+			continue;
+
+		/* Set this protocol for all commands. */
+		nor->reg_proto = configs[i].proto;
+		nor->read_proto = configs[i].proto;
+		nor->write_proto = configs[i].proto;
+		nor->erase_proto = configs[i].proto;
+
+		/*
+		 * Multiple I/O Read ID only returns the Manufacturer ID
+		 * (1 byte) and the Device ID (2 bytes). So we reset the
+		 * remaining bytes.
+		 */
+		if (configs[i].opcode == SPINOR_OP_MIO_RDID) {
+			is_multi = true;
+			len = 3;
+			memset(id + len, 0, sizeof(id) - len);
+		}
+
+		tmp = nor->read_reg(nor, configs[i].opcode, id, len);
+		if (tmp < 0) {
+			dev_dbg(nor->dev,
+				"error %d reading JEDEC ID%s\n",
+				tmp, (is_multi ? " Multi I/O" : ""));
+			return ERR_PTR(tmp);
+		}
+	}
+
 	for (tmp = 0; tmp < ARRAY_SIZE(spi_nor_ids) - 1; tmp++) {
 		info = &spi_nor_ids[tmp];
 		if (info->id_len) {
@@ -1148,11 +1217,17 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
 	if (ret)
 		return ret;
 
+	/* Reset SPI protocol for all commands */
+	nor->erase_proto = SNOR_PROTO_1_1_1;
+	nor->read_proto = SNOR_PROTO_1_1_1;
+	nor->write_proto = SNOR_PROTO_1_1_1;
+	nor->reg_proto = SNOR_PROTO_1_1_1;
+
 	if (name)
 		info = spi_nor_match_id(name);
 	/* Try to auto-detect if chip name wasn't specified or not found */
 	if (!info)
-		info = spi_nor_read_id(nor);
+		info = spi_nor_read_id(nor, mode);
 	if (IS_ERR_OR_NULL(info))
 		return -ENOENT;
 
@@ -1163,7 +1238,7 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
 	if (name && info->id_len) {
 		const struct flash_info *jinfo;
 
-		jinfo = spi_nor_read_id(nor);
+		jinfo = spi_nor_read_id(nor, mode);
 		if (IS_ERR(jinfo)) {
 			return PTR_ERR(jinfo);
 		} else if (jinfo != info) {
include/linux/mtd/spi-nor.h +3 -2
index 62356d50815b..53932c87bcf2 100644
@@ -75,8 +75,9 @@
 #define SPINOR_OP_BRWR		0x17	/* Bank register write */
 
 /* Used for Micron flashes only. */
-#define SPINOR_OP_RD_EVCR      0x65    /* Read EVCR register */
-#define SPINOR_OP_WD_EVCR      0x61    /* Write EVCR register */
+#define SPINOR_OP_MIO_RDID	0xaf	/* Multiple I/O Read JEDEC ID */
+#define SPINOR_OP_RD_EVCR	0x65    /* Read EVCR register */
+#define SPINOR_OP_WD_EVCR	0x61    /* Write EVCR register */
 
 /* Status Register bits. */
 #define SR_WIP			BIT(0)	/* Write in progress */

include/linux/mtd/spi-nor.h enum read_mode { · line 109

Quoted 4 lines
include/linux/mtd/spi-nor.h +1 -0
@@ -105,6 +106,43 @@ enum read_mode {
 	SPI_NOR_QUAD,
 };
 
+

Extra blank line here ^

Quoted 6 lines
include/linux/mtd/spi-nor.h +6 -0
+#define SNOR_PROTO_CMD_OFF	8
+#define SNOR_PROTO_CMD_MASK	GENMASK(11, 8)
+#define SNOR_PROTO_CMD_TO_PROTO(cmd) \
+	(((cmd) << SNOR_PROTO_CMD_OFF) & SNOR_PROTO_CMD_MASK)
+#define SNOR_PROTO_CMD_FROM_PROTO(proto) \
+	((((u32)(proto)) & SNOR_PROTO_CMD_MASK) >> SNOR_PROTO_CMD_OFF)

include/linux/mtd/spi-nor.h enum read_mode { · line 125

Quoted 10 lines
include/linux/mtd/spi-nor.h +10 -0
+
+#define SNOR_PROTO_ADDR_OFF	4
+#define SNOR_PROTO_ADDR_MASK	GENMASK(7, 4)
+#define SNOR_PROTO_ADDR_TO_PROTO(addr) \
+	(((addr) << SNOR_PROTO_ADDR_OFF) & SNOR_PROTO_ADDR_MASK)
+#define SNOR_PROTO_ADDR_FROM_PROTO(proto) \
+	((((u32)(proto)) & SNOR_PROTO_ADDR_MASK) >> SNOR_PROTO_ADDR_OFF)
+
+#define SNOR_PROTO_DATA_OFF	0
+#define SNOR_PROTO_DATA_MASK	GENMASK(3, 0)

Why not directly reserving 8 bits per cycle type. Your end result is stored on a 32 bit integer anyway, and since you'll always have only 3 cycle types (ADDR, CMD and DATA). This way, you'll be ready for SPI-8-8-8 :-).

Quoted 9 lines
include/linux/mtd/spi-nor.h +9 -0
+#define SNOR_PROTO_DATA_TO_PROTO(data) \
+	(((data) << SNOR_PROTO_DATA_OFF) & SNOR_PROTO_DATA_MASK)
+#define SNOR_PROTO_DATA_FROM_PROTO(proto) \
+	((((u32)(proto)) & SNOR_PROTO_DATA_MASK) >> SNOR_PROTO_DATA_OFF)
+
+#define SNOR_PROTO(cmd, addr, data)	  \
+	(SNOR_PROTO_CMD_TO_PROTO(cmd) |   \
+	 SNOR_PROTO_ADDR_TO_PROTO(addr) | \
+	 SNOR_PROTO_DATA_TO_PROTO(data))

include/linux/mtd/spi-nor.h enum read_mode { · line 144

Quoted 10 lines
include/linux/mtd/spi-nor.h +10 -0
+
+enum spi_nor_protocol {
+	SNOR_PROTO_1_1_1 = SNOR_PROTO(1, 1, 1),	/* SPI */
+	SNOR_PROTO_1_1_2 = SNOR_PROTO(1, 1, 2),	/* Dual Output */
+	SNOR_PROTO_1_1_4 = SNOR_PROTO(1, 1, 4),	/* Quad Output */
+	SNOR_PROTO_1_2_2 = SNOR_PROTO(1, 2, 2),	/* Dual IO */
+	SNOR_PROTO_1_4_4 = SNOR_PROTO(1, 4, 4),	/* Quad IO */
+	SNOR_PROTO_2_2_2 = SNOR_PROTO(2, 2, 2),	/* Dual Command */
+	SNOR_PROTO_4_4_4 = SNOR_PROTO(4, 4, 4),	/* Quad Command */
+};

Do you really need this enum definition? I mean, directly using SNOR_PROTO(X, Y, Z) is just as easy as using the SNOR_PROTO_X_Y_Z enum definitions, and you won't have to define new ones if spi-nor vendors decide to support new combinations.

Quoted 16 lines
include/linux/mtd/spi-nor.h +5 -0
+
 #define SPI_NOR_MAX_CMD_SIZE	8
 enum spi_nor_ops {
 	SPI_NOR_OPS_READ = 0,
@@ -132,6 +170,10 @@ enum spi_nor_option_flags {
  * @flash_read:		the mode of the read
  * @sst_write_second:	used by the SST write operation
  * @flags:		flag options for the current SPI-NOR (SNOR_F_*)
+ * @erase_proto:	the SPI protocol used by erase operations
+ * @read_proto:		the SPI protocol used by read operations
+ * @write_proto:	the SPI protocol used by write operations
+ * @reg_proto		the SPI protocol used by read_reg/write_reg operations
  * @cmd_buf:		used by the write_reg
  * @prepare:		[OPTIONAL] do some preparations for the
  *			read/write/erase/lock/unlock operations

include/linux/mtd/spi-nor.h struct spi_nor { · line 208

Quoted 7 lines
include/linux/mtd/spi-nor.h +4 -0
@@ -160,6 +202,10 @@ struct spi_nor {
 	u8			read_opcode;
 	u8			read_dummy;
 	u8			program_opcode;
+	enum spi_nor_protocol	erase_proto;
+	enum spi_nor_protocol	read_proto;
+	enum spi_nor_protocol	write_proto;
+	enum spi_nor_protocol	reg_proto;

Should be u32 fields if you decide to drop the enum definition.

Quoted 3 lines
include/linux/mtd/spi-nor.h +0 -0
 	enum read_mode		flash_read;
 	bool			sst_write_second;
 	u32			flags;
Signature · 3 lines
Boris Brezillon, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com
#4 lore raw

Re: [PATCH linux-next v2 02/14] mtd: spi-nor: properly detect the memory when it boots in Quad or Dual mode

Hi Boris,

Le 11/01/2016 11:08, Boris Brezillon a ?crit :

Quoted 104 lines

Hi Cyrille,

Sorry for this pretty useless review, but I think you know more about SPI-NOR than I do. So, consider my comments as nitpicks, which shouldn't prevent your series from being applied.

Quoted 81 lines · On Fri, 8 Jan 2016 17:02:14 +0100 Cyrille Pitchen <[email protected]>…

The quad (or dual) mode of a spi-nor memory may be enabled at boot time by non-volatile bits in some setting register. Also such a mode may have already been enabled at early stage by some boot loader.

Hence, we should not guess the spi-nor memory is always configured for the regular SPI 1-1-1 protocol.

Micron and Macronix memories, once their Quad (or dual for Micron) mode enabled, no longer process the regular JEDEC Read ID (0x9f) command but instead reply to a new command: JEDEC Read ID Multiple I/O (0xaf). Besides, in Quad mode both memory manufacturers expect ALL commands to use the SPI 4-4-4 protocol. For Micron memories, enabling their Dual mode implies to use the SPI 2-2-2 protocol for ALL commands.

Winbond memories, once their Quad mode enabled, expect ALL commands to use the SPI 4-4-4 protocol. Unlike Micron and Macronix memories, they still reply to the regular JEDEC Read ID (0x9f) command.

---
 drivers/mtd/spi-nor/spi-nor.c | 83 ++++++++++++++++++++++++++++++++++++++++---
 include/linux/mtd/spi-nor.h   | 50 ++++++++++++++++++++++++--
 2 files changed, 127 insertions(+), 6 deletions(-)
drivers/mtd/spi-nor/spi-nor.c +30 -2
index 3028c06547c1..8967319ea7da 100644
@@ -73,6 +73,12 @@ struct flash_info {
 
 #define JEDEC_MFR(info)	((info)->id[0])
 
+struct read_id_config {
+	enum read_mode		mode;
+	enum spi_nor_protocol	proto;
+	u8			opcode;
+};
+
 static const struct flash_info *spi_nor_match_id(const char *name);
 
 /*
@@ -879,11 +885,22 @@ static const struct flash_info spi_nor_ids[] = {
 	{ },
 };
 
-static const struct flash_info *spi_nor_read_id(struct spi_nor *nor)
+static const struct flash_info *spi_nor_read_id(struct spi_nor *nor,
+						enum read_mode mode)
 {
-	int			tmp;
+	int			i, tmp;
 	u8			id[SPI_NOR_MAX_ID_LEN];
 	const struct flash_info	*info;
+	static const struct read_id_config configs[] = {
+		/* Winbond QPI mode */
+		{SPI_NOR_QUAD, SNOR_PROTO_4_4_4, SPINOR_OP_RDID},
+
+		/* Micron Quad mode & Macronix QPI mode */
+		{SPI_NOR_QUAD, SNOR_PROTO_4_4_4, SPINOR_OP_MIO_RDID},
+
+		/* Micron Dual mode */
+		{SPI_NOR_DUAL, SNOR_PROTO_2_2_2, SPINOR_OP_MIO_RDID}
+	};
 
 	tmp = nor->read_reg(nor, SPINOR_OP_RDID, id, SPI_NOR_MAX_ID_LEN);
 	if (tmp < 0) {
@@ -891,6 +908,58 @@ static const struct flash_info *spi_nor_read_id(struct spi_nor *nor)
 		return ERR_PTR(tmp);
 	}
 
+	/*
+	 * Check whether the SPI NOR memory has already been configured (at
+	 * reset or by some bootloader) to use a protocol other than SPI 1-1-1.
+	 */
+	for (i = 0; i < ARRAY_SIZE(configs); ++i) {
+		int len = SPI_NOR_MAX_ID_LEN;
+		bool is_multi = false;
+
+		/*
+		 * Check the latest read Manufacturer ID + Device ID (3 bytes):
+		 * if they are different from both 0x000000 and 0xffffff, we

drivers/mtd/spi-nor/spi-nor.c static const struct flash_info *spi_nor_read_id(struct spi_nor *nor) · line 931

Quoted 10 lines
drivers/mtd/spi-nor/spi-nor.c +10 -0
+		 * assume that we succeeded in reading a valid JEDEC ID so we
+		 * don't need to try other SPI protocols.
+		 * Indeed when either the protocol or the op code are not valid,
+		 * the SPI NOR memory should not reply to the command. Hence the
+		 * SPI I/O lines remain in their default state: 1 when connected
+		 * to pull-up resistors or 0 with pull-down.
+		 */
+		if (!((id[0] == 0xff && id[1] == 0xff && id[2] == 0xff) ||
+		      (id[0] == 0x00 && id[1] == 0x00 && id[2] == 0x00)))
+			break;

Hopefully, you'll have all the pins configured with pull-up or pull-down resistors, but what if one of them is configured differently?

Quoted 3 lines

Shouldn't we try to find a valid chip definition by iterating over the spi_nor_ids[] table after each mode change?

Honestly I don't know and I didn't test what would happen if no resistors were used. So maybe you're right, it would be safer to look in the ID table up after every read attempt.

Quoted 99 lines
Quoted 79 lines
drivers/mtd/spi-nor/spi-nor.c +39 -2
+
+		/* Only try protocols supported by the user. */
+		if (configs[i].mode != mode)
+			continue;
+
+		/* Set this protocol for all commands. */
+		nor->reg_proto = configs[i].proto;
+		nor->read_proto = configs[i].proto;
+		nor->write_proto = configs[i].proto;
+		nor->erase_proto = configs[i].proto;
+
+		/*
+		 * Multiple I/O Read ID only returns the Manufacturer ID
+		 * (1 byte) and the Device ID (2 bytes). So we reset the
+		 * remaining bytes.
+		 */
+		if (configs[i].opcode == SPINOR_OP_MIO_RDID) {
+			is_multi = true;
+			len = 3;
+			memset(id + len, 0, sizeof(id) - len);
+		}
+
+		tmp = nor->read_reg(nor, configs[i].opcode, id, len);
+		if (tmp < 0) {
+			dev_dbg(nor->dev,
+				"error %d reading JEDEC ID%s\n",
+				tmp, (is_multi ? " Multi I/O" : ""));
+			return ERR_PTR(tmp);
+		}
+	}
+
 	for (tmp = 0; tmp < ARRAY_SIZE(spi_nor_ids) - 1; tmp++) {
 		info = &spi_nor_ids[tmp];
 		if (info->id_len) {
@@ -1148,11 +1217,17 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
 	if (ret)
 		return ret;
 
+	/* Reset SPI protocol for all commands */
+	nor->erase_proto = SNOR_PROTO_1_1_1;
+	nor->read_proto = SNOR_PROTO_1_1_1;
+	nor->write_proto = SNOR_PROTO_1_1_1;
+	nor->reg_proto = SNOR_PROTO_1_1_1;
+
 	if (name)
 		info = spi_nor_match_id(name);
 	/* Try to auto-detect if chip name wasn't specified or not found */
 	if (!info)
-		info = spi_nor_read_id(nor);
+		info = spi_nor_read_id(nor, mode);
 	if (IS_ERR_OR_NULL(info))
 		return -ENOENT;
 
@@ -1163,7 +1238,7 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
 	if (name && info->id_len) {
 		const struct flash_info *jinfo;
 
-		jinfo = spi_nor_read_id(nor);
+		jinfo = spi_nor_read_id(nor, mode);
 		if (IS_ERR(jinfo)) {
 			return PTR_ERR(jinfo);
 		} else if (jinfo != info) {
include/linux/mtd/spi-nor.h +3 -2
index 62356d50815b..53932c87bcf2 100644
@@ -75,8 +75,9 @@
 #define SPINOR_OP_BRWR		0x17	/* Bank register write */
 
 /* Used for Micron flashes only. */
-#define SPINOR_OP_RD_EVCR      0x65    /* Read EVCR register */
-#define SPINOR_OP_WD_EVCR      0x61    /* Write EVCR register */
+#define SPINOR_OP_MIO_RDID	0xaf	/* Multiple I/O Read JEDEC ID */
+#define SPINOR_OP_RD_EVCR	0x65    /* Read EVCR register */
+#define SPINOR_OP_WD_EVCR	0x61    /* Write EVCR register */
 
 /* Status Register bits. */
 #define SR_WIP			BIT(0)	/* Write in progress */

include/linux/mtd/spi-nor.h enum read_mode { · line 109

Quoted 4 lines
include/linux/mtd/spi-nor.h +1 -0
@@ -105,6 +106,43 @@ enum read_mode {
 	SPI_NOR_QUAD,
 };
 
+

Extra blank line here ^

Quoted 13 lines
include/linux/mtd/spi-nor.h +13 -0
+#define SNOR_PROTO_CMD_OFF	8
+#define SNOR_PROTO_CMD_MASK	GENMASK(11, 8)
+#define SNOR_PROTO_CMD_TO_PROTO(cmd) \
+	(((cmd) << SNOR_PROTO_CMD_OFF) & SNOR_PROTO_CMD_MASK)
+#define SNOR_PROTO_CMD_FROM_PROTO(proto) \
+	((((u32)(proto)) & SNOR_PROTO_CMD_MASK) >> SNOR_PROTO_CMD_OFF)
+
+#define SNOR_PROTO_ADDR_OFF	4
+#define SNOR_PROTO_ADDR_MASK	GENMASK(7, 4)
+#define SNOR_PROTO_ADDR_TO_PROTO(addr) \
+	(((addr) << SNOR_PROTO_ADDR_OFF) & SNOR_PROTO_ADDR_MASK)
+#define SNOR_PROTO_ADDR_FROM_PROTO(proto) \
+	((((u32)(proto)) & SNOR_PROTO_ADDR_MASK) >> SNOR_PROTO_ADDR_OFF)
Quoted 9 lines

include/linux/mtd/spi-nor.h enum read_mode { · line 125

Quoted 3 lines
include/linux/mtd/spi-nor.h +3 -0
+
+#define SNOR_PROTO_DATA_OFF	0
+#define SNOR_PROTO_DATA_MASK	GENMASK(3, 0)

Why not directly reserving 8 bits per cycle type. Your end result is stored on a 32 bit integer anyway, and since you'll always have only 3 cycle types (ADDR, CMD and DATA). This way, you'll be ready for SPI-8-8-8 :-).

I've reserved 4 bits per cycle type so the possible values range into [0 - 15]. I thought it would be enough to anticipate future evolution like SPI 8-8-8. Also it leaves more bits available for future usage.
Finally, I've chosen 4 bits and this order so when debugging and/or printing the value in hex format, you can easily decode the corresponding protocol:

SPI 1-1-4 -> 0x00000114
SPI 1-2-2 -> 0x00000122
SPI 4-4-4 -> 0x00000444

Anyway I don't mind using 8 bits instead if it is preferred.

Quoted 16 lines
Quoted 16 lines
include/linux/mtd/spi-nor.h +16 -0
+#define SNOR_PROTO_DATA_TO_PROTO(data) \
+	(((data) << SNOR_PROTO_DATA_OFF) & SNOR_PROTO_DATA_MASK)
+#define SNOR_PROTO_DATA_FROM_PROTO(proto) \
+	((((u32)(proto)) & SNOR_PROTO_DATA_MASK) >> SNOR_PROTO_DATA_OFF)
+
+#define SNOR_PROTO(cmd, addr, data)	  \
+	(SNOR_PROTO_CMD_TO_PROTO(cmd) |   \
+	 SNOR_PROTO_ADDR_TO_PROTO(addr) | \
+	 SNOR_PROTO_DATA_TO_PROTO(data))
+
+enum spi_nor_protocol {
+	SNOR_PROTO_1_1_1 = SNOR_PROTO(1, 1, 1),	/* SPI */
+	SNOR_PROTO_1_1_2 = SNOR_PROTO(1, 1, 2),	/* Dual Output */
+	SNOR_PROTO_1_1_4 = SNOR_PROTO(1, 1, 4),	/* Quad Output */
+	SNOR_PROTO_1_2_2 = SNOR_PROTO(1, 2, 2),	/* Dual IO */
+	SNOR_PROTO_1_4_4 = SNOR_PROTO(1, 4, 4),	/* Quad IO */
Quoted 9 lines

include/linux/mtd/spi-nor.h enum read_mode { · line 144

Quoted 3 lines
include/linux/mtd/spi-nor.h +3 -0
+	SNOR_PROTO_2_2_2 = SNOR_PROTO(2, 2, 2),	/* Dual Command */
+	SNOR_PROTO_4_4_4 = SNOR_PROTO(4, 4, 4),	/* Quad Command */
+};

Do you really need this enum definition? I mean, directly using SNOR_PROTO(X, Y, Z) is just as easy as using the SNOR_PROTO_X_Y_Z enum definitions, and you won't have to define new ones if spi-nor vendors decide to support new combinations.

This enum was created only for stronger type checking; Except for that, there is no strong reason to keep the enum. So I don't mind removing it if other people agree with you.

Quoted 17 lines
Quoted 17 lines
include/linux/mtd/spi-nor.h +5 -0
+
 #define SPI_NOR_MAX_CMD_SIZE	8
 enum spi_nor_ops {
 	SPI_NOR_OPS_READ = 0,
@@ -132,6 +170,10 @@ enum spi_nor_option_flags {
  * @flash_read:		the mode of the read
  * @sst_write_second:	used by the SST write operation
  * @flags:		flag options for the current SPI-NOR (SNOR_F_*)
+ * @erase_proto:	the SPI protocol used by erase operations
+ * @read_proto:		the SPI protocol used by read operations
+ * @write_proto:	the SPI protocol used by write operations
+ * @reg_proto		the SPI protocol used by read_reg/write_reg operations
  * @cmd_buf:		used by the write_reg
  * @prepare:		[OPTIONAL] do some preparations for the
  *			read/write/erase/lock/unlock operations
@@ -160,6 +202,10 @@ struct spi_nor {
 	u8			read_opcode;
Quoted 15 lines

include/linux/mtd/spi-nor.h struct spi_nor { · line 208

Quoted 6 lines
include/linux/mtd/spi-nor.h +4 -0
 	u8			read_dummy;
 	u8			program_opcode;
+	enum spi_nor_protocol	erase_proto;
+	enum spi_nor_protocol	read_proto;
+	enum spi_nor_protocol	write_proto;
+	enum spi_nor_protocol	reg_proto;

Should be u32 fields if you decide to drop the enum definition.

Quoted 3 lines
include/linux/mtd/spi-nor.h +0 -0
 	enum read_mode		flash_read;
 	bool			sst_write_second;
 	u32			flags;

Thanks for your review :)

Best regards,

Cyrille

#5 lore raw

PATCH v2 3/14 mtd: spi-nor: select op codes and SPI NOR protocols by manufacturer

In reply to Cyrille Pitchen

This is a transitional patch to prepare the split by Manufacturer of the support of Single/Dual/Quad SPI modes.

Indeed every QSPI NOR manufacturer (Spansion, Micron, Macronix, Winbond) supports Dual or Quad SPI modes on its way. Especially the Fast Read op code and the SPI NOR protocols to use are not quite the same depending on the manufacturer.

For instance Quad commands can use either the SPI 1-1-4, 1-4-4 or 4-4-4 protocol.

This is why this patch will be completed by fixes for each manufacturer.

---
 drivers/mtd/spi-nor/spi-nor.c | 110 ++++++++++++++++++++++++++++++++----------
 include/linux/mtd/spi-nor.h   |  12 +++--
 2 files changed, 92 insertions(+), 30 deletions(-)
drivers/mtd/spi-nor/spi-nor.c +84 -26
index 8967319ea7da..8793cebbe5a9 100644
@@ -1180,17 +1180,61 @@ static int set_quad_mode(struct spi_nor *nor, const struct flash_info *info)
 			dev_err(nor->dev, "Macronix quad-read not enabled\n");
 			return -EINVAL;
 		}
-		return status;
+		/* Check whether Macronix QPI mode is enabled. */
+		if (nor->read_proto != SNOR_PROTO_4_4_4)
+			nor->read_proto = SNOR_PROTO_1_1_4;
+		break;
+
 	case SNOR_MFR_MICRON:
-		return 0;
-	default:
+		/* Check whether Micron Quad mode is enabled. */
+		if (nor->read_proto != SNOR_PROTO_4_4_4)
+			nor->read_proto = SNOR_PROTO_1_1_4;
+		break;
+
+	case SNOR_MFR_SPANSION:
 		status = spansion_quad_enable(nor);
 		if (status) {
 			dev_err(nor->dev, "Spansion quad-read not enabled\n");
 			return -EINVAL;
 		}
-		return status;
+		nor->read_proto = SNOR_PROTO_1_1_4;
+		break;
+
+	default:
+		return -EINVAL;
 	}
+
+	nor->read_opcode = SPINOR_OP_READ_1_1_4;
+	return 0;
+}
+
+static int set_dual_mode(struct spi_nor *nor, const struct flash_info *info)
+{
+	switch (JEDEC_MFR(info)) {
+	case SNOR_MFR_MICRON:
+		/* Check whether Micron Dual mode is enabled. */
+		if (nor->read_proto != SNOR_PROTO_2_2_2)
+			nor->read_proto = SNOR_PROTO_1_1_2;
+		break;
+
+	default:
+		nor->read_proto = SNOR_PROTO_1_1_2;
+		break;
+	}
+
+	nor->read_opcode = SPINOR_OP_READ_1_1_2;
+	return 0;
+}
+
+static int set_single_mode(struct spi_nor *nor, const struct flash_info *info)
+{
+	switch (JEDEC_MFR(info)) {
+	default:
+		nor->read_proto = SNOR_PROTO_1_1_1;
+		break;
+	}
+
+	return 0;
 }
 
 static int spi_nor_check(struct spi_nor *nor)
@@ -1338,7 +1382,30 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
 	if (info->flags & SPI_NOR_NO_FR)
 		nor->flash_read = SPI_NOR_NORMAL;
 
-	/* Quad/Dual-read mode takes precedence over fast/normal */
+	/* Default commands */
+	if (nor->flash_read == SPI_NOR_NORMAL)
+		nor->read_opcode = SPINOR_OP_READ;
+	else
+		nor->read_opcode = SPINOR_OP_READ_FAST;
+
+	nor->program_opcode = SPINOR_OP_PP;
+
+	/*
+	 * Quad/Dual-read mode takes precedence over fast/normal.
+	 *
+	 * Manufacturer specific modes are discovered when reading the JEDEC ID
+	 * and are reported by the nor->read_proto value:
+	 *  - SNOR_PROTO_4_4_4 is either:
+	 *    + Micron Quad mode enabled
+	 *    + Macronix/Winbond QPI mode enabled
+	 *  - SNOR_PROTO_2_2_2 is either:
+	 *    + Micron Dual mode enabled
+	 *
+	 * The opcodes and the protocols are updated depending on the
+	 * manufacturer.
+	 * The read opcode and protocol should be updated by the relevant
+	 * function when entering Quad or Dual mode.
+	 */
 	if (mode == SPI_NOR_QUAD && info->flags & SPI_NOR_QUAD_READ) {
 		ret = set_quad_mode(nor, info);
 		if (ret) {
@@ -1347,30 +1414,21 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
 		}
 		nor->flash_read = SPI_NOR_QUAD;
 	} else if (mode == SPI_NOR_DUAL && info->flags & SPI_NOR_DUAL_READ) {
+		ret = set_dual_mode(nor, info);
+		if (ret) {
+			dev_err(dev, "dual mode not supported\n");
+			return ret;
+		}
 		nor->flash_read = SPI_NOR_DUAL;
+	} else if (info->flags & (SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ)) {
+		/* We may need to leave a Quad or Dual mode */
+		ret = set_single_mode(nor, info);
+		if (ret) {
+			dev_err(dev, "failed to switch back to single mode\n");
+			return ret;
+		}
 	}
 
-	/* Default commands */
-	switch (nor->flash_read) {
-	case SPI_NOR_QUAD:
-		nor->read_opcode = SPINOR_OP_READ_1_1_4;
-		break;
-	case SPI_NOR_DUAL:
-		nor->read_opcode = SPINOR_OP_READ_1_1_2;
-		break;
-	case SPI_NOR_FAST:
-		nor->read_opcode = SPINOR_OP_READ_FAST;
-		break;
-	case SPI_NOR_NORMAL:
-		nor->read_opcode = SPINOR_OP_READ;
-		break;
-	default:
-		dev_err(dev, "No Read opcode defined\n");
-		return -EINVAL;
-	}
-
-	nor->program_opcode = SPINOR_OP_PP;
-
 	if (info->addr_width)
 		nor->addr_width = info->addr_width;
 	else if (mtd->size > 0x1000000) {
include/linux/mtd/spi-nor.h +8 -4
index 53932c87bcf2..89e3228ec1d0 100644
@@ -42,8 +42,10 @@
 #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_BE_4K		0x20	/* Erase 4KiB block */
 #define SPINOR_OP_BE_4K_PMC	0xd7	/* Erase 4KiB block on PMC chips */
@@ -57,8 +59,10 @@
 /* 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_READ4_1_1_2	0x3c	/* Read data bytes (Dual Output SPI) */
+#define SPINOR_OP_READ4_1_2_2	0xbc	/* Read data bytes (Dual I/O SPI) */
+#define SPINOR_OP_READ4_1_1_4	0x6c	/* Read data bytes (Quad Output SPI) */
+#define SPINOR_OP_READ4_1_4_4	0xec	/* Read data bytes (Quad I/O SPI) */
 #define SPINOR_OP_PP_4B		0x12	/* Page program (up to 256 bytes) */
 #define SPINOR_OP_SE_4B		0xdc	/* Sector erase (usually 64KiB) */
 
Signature · 1 line
1.8.2.2
#6 lore raw

Re: [PATCH linux-next v2 03/14] mtd: spi-nor: select op codes and SPI NOR protocols by manufacturer

Hi,

Quoted 32 lines · On Fri, 8 Jan 2016 17:02:15 +0100 Cyrille Pitchen <[email protected]>…

This is a transitional patch to prepare the split by Manufacturer of the support of Single/Dual/Quad SPI modes.

Indeed every QSPI NOR manufacturer (Spansion, Micron, Macronix, Winbond) supports Dual or Quad SPI modes on its way. Especially the Fast Read op code and the SPI NOR protocols to use are not quite the same depending on the manufacturer.

For instance Quad commands can use either the SPI 1-1-4, 1-4-4 or 4-4-4 protocol.

This is why this patch will be completed by fixes for each manufacturer.

---
 drivers/mtd/spi-nor/spi-nor.c | 110 ++++++++++++++++++++++++++++++++----------
 include/linux/mtd/spi-nor.h   |  12 +++--
 2 files changed, 92 insertions(+), 30 deletions(-)
drivers/mtd/spi-nor/spi-nor.c +4 -1
index 8967319ea7da..8793cebbe5a9 100644
@@ -1180,17 +1180,61 @@ static int set_quad_mode(struct spi_nor *nor, const struct flash_info *info)
 			dev_err(nor->dev, "Macronix quad-read not enabled\n");
 			return -EINVAL;
 		}
-		return status;
+		/* Check whether Macronix QPI mode is enabled. */
+		if (nor->read_proto != SNOR_PROTO_4_4_4)
+			nor->read_proto = SNOR_PROTO_1_1_4;
+		break;

drivers/mtd/spi-nor/spi-nor.c static int set_quad_mode(struct spi_nor *nor, const struct flash_info *info) · line 1194

Quoted 10 lines
drivers/mtd/spi-nor/spi-nor.c +7 -2
+
 	case SNOR_MFR_MICRON:
-		return 0;
-	default:
+		/* Check whether Micron Quad mode is enabled. */
+		if (nor->read_proto != SNOR_PROTO_4_4_4)
+			nor->read_proto = SNOR_PROTO_1_1_4;
+		break;
+
+	case SNOR_MFR_SPANSION:

The following comment is not only related to your changes, but after looking at the spi_nor_scan() function and a few other functions in the core, I see a lot of vendor specific initialization.

Would it make sense to somehow set a default configuration in spi_nor_scan() and then overload this default config using a per-manufacturer ->init() method. Something like that.

struct spi_nor_manufacturer {
	int (*init)(struct spi_nor *nor, const struct flash_info *fi);
}
static const struct spi_nor_manufacturer manufacturers[] = {
	[<manuf-id>] = {
		.init = <manuf-init-callback>
	},
};

Of course you could add other methods if you think this differentiation is needed after the initialization step.

Quoted 32 lines
drivers/mtd/spi-nor/spi-nor.c +25 -1
 		status = spansion_quad_enable(nor);
 		if (status) {
 			dev_err(nor->dev, "Spansion quad-read not enabled\n");
 			return -EINVAL;
 		}
-		return status;
+		nor->read_proto = SNOR_PROTO_1_1_4;
+		break;
+
+	default:
+		return -EINVAL;
 	}
+
+	nor->read_opcode = SPINOR_OP_READ_1_1_4;
+	return 0;
+}
+
+static int set_dual_mode(struct spi_nor *nor, const struct flash_info *info)
+{
+	switch (JEDEC_MFR(info)) {
+	case SNOR_MFR_MICRON:
+		/* Check whether Micron Dual mode is enabled. */
+		if (nor->read_proto != SNOR_PROTO_2_2_2)
+			nor->read_proto = SNOR_PROTO_1_1_2;
+		break;
+
+	default:
+		nor->read_proto = SNOR_PROTO_1_1_2;
+		break;
+	}
+
+	nor->read_opcode = SPINOR_OP_READ_1_1_2;

drivers/mtd/spi-nor/spi-nor.c static int set_quad_mode(struct spi_nor *nor, const struct flash_info *info) · line 1235

Quoted 10 lines
drivers/mtd/spi-nor/spi-nor.c +10 -0
+	return 0;
+}
+
+static int set_single_mode(struct spi_nor *nor, const struct flash_info *info)
+{
+	switch (JEDEC_MFR(info)) {
+	default:
+		nor->read_proto = SNOR_PROTO_1_1_1;
+		break;
+	}

You can just put

	nor->read_proto = SNOR_PROTO_1_1_1;

until you have a manufacturer specific case.

Quoted 110 lines
drivers/mtd/spi-nor/spi-nor.c +38 -22
+
+	return 0;
 }
 
 static int spi_nor_check(struct spi_nor *nor)
@@ -1338,7 +1382,30 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
 	if (info->flags & SPI_NOR_NO_FR)
 		nor->flash_read = SPI_NOR_NORMAL;
 
-	/* Quad/Dual-read mode takes precedence over fast/normal */
+	/* Default commands */
+	if (nor->flash_read == SPI_NOR_NORMAL)
+		nor->read_opcode = SPINOR_OP_READ;
+	else
+		nor->read_opcode = SPINOR_OP_READ_FAST;
+
+	nor->program_opcode = SPINOR_OP_PP;
+
+	/*
+	 * Quad/Dual-read mode takes precedence over fast/normal.
+	 *
+	 * Manufacturer specific modes are discovered when reading the JEDEC ID
+	 * and are reported by the nor->read_proto value:
+	 *  - SNOR_PROTO_4_4_4 is either:
+	 *    + Micron Quad mode enabled
+	 *    + Macronix/Winbond QPI mode enabled
+	 *  - SNOR_PROTO_2_2_2 is either:
+	 *    + Micron Dual mode enabled
+	 *
+	 * The opcodes and the protocols are updated depending on the
+	 * manufacturer.
+	 * The read opcode and protocol should be updated by the relevant
+	 * function when entering Quad or Dual mode.
+	 */
 	if (mode == SPI_NOR_QUAD && info->flags & SPI_NOR_QUAD_READ) {
 		ret = set_quad_mode(nor, info);
 		if (ret) {
@@ -1347,30 +1414,21 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
 		}
 		nor->flash_read = SPI_NOR_QUAD;
 	} else if (mode == SPI_NOR_DUAL && info->flags & SPI_NOR_DUAL_READ) {
+		ret = set_dual_mode(nor, info);
+		if (ret) {
+			dev_err(dev, "dual mode not supported\n");
+			return ret;
+		}
 		nor->flash_read = SPI_NOR_DUAL;
+	} else if (info->flags & (SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ)) {
+		/* We may need to leave a Quad or Dual mode */
+		ret = set_single_mode(nor, info);
+		if (ret) {
+			dev_err(dev, "failed to switch back to single mode\n");
+			return ret;
+		}
 	}
 
-	/* Default commands */
-	switch (nor->flash_read) {
-	case SPI_NOR_QUAD:
-		nor->read_opcode = SPINOR_OP_READ_1_1_4;
-		break;
-	case SPI_NOR_DUAL:
-		nor->read_opcode = SPINOR_OP_READ_1_1_2;
-		break;
-	case SPI_NOR_FAST:
-		nor->read_opcode = SPINOR_OP_READ_FAST;
-		break;
-	case SPI_NOR_NORMAL:
-		nor->read_opcode = SPINOR_OP_READ;
-		break;
-	default:
-		dev_err(dev, "No Read opcode defined\n");
-		return -EINVAL;
-	}
-
-	nor->program_opcode = SPINOR_OP_PP;
-
 	if (info->addr_width)
 		nor->addr_width = info->addr_width;
 	else if (mtd->size > 0x1000000) {
include/linux/mtd/spi-nor.h +8 -4
index 53932c87bcf2..89e3228ec1d0 100644
@@ -42,8 +42,10 @@
 #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_BE_4K		0x20	/* Erase 4KiB block */
 #define SPINOR_OP_BE_4K_PMC	0xd7	/* Erase 4KiB block on PMC chips */
@@ -57,8 +59,10 @@
 /* 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_READ4_1_1_2	0x3c	/* Read data bytes (Dual Output SPI) */
+#define SPINOR_OP_READ4_1_2_2	0xbc	/* Read data bytes (Dual I/O SPI) */
+#define SPINOR_OP_READ4_1_1_4	0x6c	/* Read data bytes (Quad Output SPI) */
+#define SPINOR_OP_READ4_1_4_4	0xec	/* Read data bytes (Quad I/O SPI) */
 #define SPINOR_OP_PP_4B		0x12	/* Page program (up to 256 bytes) */
 #define SPINOR_OP_SE_4B		0xdc	/* Sector erase (usually 64KiB) */
 
Signature · 3 lines
Boris Brezillon, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com
#7 lore raw

Re: [PATCH linux-next v2 03/14] mtd: spi-nor: select op codes and SPI NOR protocols by manufacturer

Hi Boris,

Le 11/01/2016 11:24, Boris Brezillon a ?crit :

Quoted 60 lines

Hi,

Quoted 32 lines · On Fri, 8 Jan 2016 17:02:15 +0100 Cyrille Pitchen <[email protected]>…

This is a transitional patch to prepare the split by Manufacturer of the support of Single/Dual/Quad SPI modes.

Indeed every QSPI NOR manufacturer (Spansion, Micron, Macronix, Winbond) supports Dual or Quad SPI modes on its way. Especially the Fast Read op code and the SPI NOR protocols to use are not quite the same depending on the manufacturer.

For instance Quad commands can use either the SPI 1-1-4, 1-4-4 or 4-4-4 protocol.

This is why this patch will be completed by fixes for each manufacturer.

---
 drivers/mtd/spi-nor/spi-nor.c | 110 ++++++++++++++++++++++++++++++++----------
 include/linux/mtd/spi-nor.h   |  12 +++--
 2 files changed, 92 insertions(+), 30 deletions(-)
drivers/mtd/spi-nor/spi-nor.c +4 -1
index 8967319ea7da..8793cebbe5a9 100644
@@ -1180,17 +1180,61 @@ static int set_quad_mode(struct spi_nor *nor, const struct flash_info *info)
 			dev_err(nor->dev, "Macronix quad-read not enabled\n");
 			return -EINVAL;
 		}
-		return status;
+		/* Check whether Macronix QPI mode is enabled. */
+		if (nor->read_proto != SNOR_PROTO_4_4_4)
+			nor->read_proto = SNOR_PROTO_1_1_4;
+		break;

drivers/mtd/spi-nor/spi-nor.c static int set_quad_mode(struct spi_nor *nor, const struct flash_info *info) · line 1194

Quoted 10 lines
drivers/mtd/spi-nor/spi-nor.c +7 -2
+
 	case SNOR_MFR_MICRON:
-		return 0;
-	default:
+		/* Check whether Micron Quad mode is enabled. */
+		if (nor->read_proto != SNOR_PROTO_4_4_4)
+			nor->read_proto = SNOR_PROTO_1_1_4;
+		break;
+
+	case SNOR_MFR_SPANSION:

The following comment is not only related to your changes, but after looking at the spi_nor_scan() function and a few other functions in the core, I see a lot of vendor specific initialization.

Would it make sense to somehow set a default configuration in spi_nor_scan() and then overload this default config using a per-manufacturer ->init() method. Something like that.

struct spi_nor_manufacturer {
	int (*init)(struct spi_nor *nor, const struct flash_info *fi);
}
Quoted 9 lines
static const struct spi_nor_manufacturer manufacturers[] = {
	[<manuf-id>] = {
		.init = <manuf-init-callback>
	},
};

Of course you could add other methods if you think this differentiation is needed after the initialization step.

It might be a good idea. I don't mind changing if needed. Brian, could you please comment on this point?

Quoted 45 lines
Quoted 32 lines
drivers/mtd/spi-nor/spi-nor.c +25 -1
 		status = spansion_quad_enable(nor);
 		if (status) {
 			dev_err(nor->dev, "Spansion quad-read not enabled\n");
 			return -EINVAL;
 		}
-		return status;
+		nor->read_proto = SNOR_PROTO_1_1_4;
+		break;
+
+	default:
+		return -EINVAL;
 	}
+
+	nor->read_opcode = SPINOR_OP_READ_1_1_4;
+	return 0;
+}
+
+static int set_dual_mode(struct spi_nor *nor, const struct flash_info *info)
+{
+	switch (JEDEC_MFR(info)) {
+	case SNOR_MFR_MICRON:
+		/* Check whether Micron Dual mode is enabled. */
+		if (nor->read_proto != SNOR_PROTO_2_2_2)
+			nor->read_proto = SNOR_PROTO_1_1_2;
+		break;
+
+	default:
+		nor->read_proto = SNOR_PROTO_1_1_2;
+		break;
+	}
+
+	nor->read_opcode = SPINOR_OP_READ_1_1_2;

drivers/mtd/spi-nor/spi-nor.c static int set_quad_mode(struct spi_nor *nor, const struct flash_info *info) · line 1235

Quoted 10 lines
drivers/mtd/spi-nor/spi-nor.c +10 -0
+	return 0;
+}
+
+static int set_single_mode(struct spi_nor *nor, const struct flash_info *info)
+{
+	switch (JEDEC_MFR(info)) {
+	default:
+		nor->read_proto = SNOR_PROTO_1_1_1;
+		break;
+	}

You can just put

Quoted 4 lines
	nor->read_proto = SNOR_PROTO_1_1_1;

until you have a manufacturer specific case.

I did it on purpose: it is a transitional patch which was written only to ease the split of the QSPI support by manufacturer. So the diff producing the next patch of this series is a little bit more easy to review: it is just a new case in a switch statement instead of replacing a 'if' statement by a 'switch' one.
Also it removes some (not all) constraints on the order the manufacturer specific patches should be applied, which makes it easier to remove some of them from the series if needed: the rebase job will be simplified. Ideally, all manufacturer patches should be totally independent.

[...]

Best regards,

Cyrille

#8 lore raw

PATCH v2 4/14 mtd: spi-nor: fix support of Macronix memories

In reply to Cyrille Pitchen

This patch fixes the support of Macronix memories. Especially we avoid updating the Status Register when not needed as the Quad Enable (QE) bit is a non-volatile bit.

Also we add comments to explain why we use some Fast Read op codes rather than others.

---
 drivers/mtd/spi-nor/spi-nor.c | 81 ++++++++++++++++++++++++++++++++++++++-----
 1 file changed, 72 insertions(+), 9 deletions(-)
drivers/mtd/spi-nor/spi-nor.c +72 -9
index 8793cebbe5a9..042ac49d6188 100644
@@ -1115,6 +1115,11 @@ static int macronix_quad_enable(struct spi_nor *nor)
 	val = read_sr(nor);
 	if (val < 0)
 		return val;
+
+	if (likely(val & SR_QUAD_EN_MX))
+		return 0;
+	dev_warn(nor->dev, "Macronix Quad mode disabled, enable it\n");
+
 	write_enable(nor);
 
 	write_sr(nor, val | SR_QUAD_EN_MX);
@@ -1169,21 +1174,73 @@ static int spansion_quad_enable(struct spi_nor *nor)
 	return 0;
 }
 
+static int macronix_set_quad_mode(struct spi_nor *nor)
+{
+	int status;
+
+	/* Check whether the QPI mode is enabled. */
+	if (nor->read_proto == SNOR_PROTO_4_4_4) {
+		/*
+		 * Since the QPI mode is enabled, the Quad Enabled (QE)
+		 * non-volatile bit is already set.
+		 * However in QPI mode, only the Fast Read 1-4-4 (0xeb)
+		 * op code is supported.
+		 * WARNING: we should take care about the performance
+		 * enhance toggling bits P0-P7 written during the
+		 * dummy/mode cycles to avoid entering the continuous
+		 * read (performance enhance) mode by mistake!
+		 */
+		nor->read_opcode = SPINOR_OP_READ_1_4_4;
+		return 0;
+	}
+
+	/*
+	 * The QPI mode is disabled but we still need to set the QE bit:
+	 * this disables the reset and write protect features and
+	 * frees the associated pins so they can be used as the 3rd
+	 * and 4th I/O lines required by Quad SPI commands.
+	 * Also we'd rather use the Fast Read 1-1-4 (0x6b) op code than
+	 * the Fast Read 1-4-4 (0xeb) op code so we don't care about
+	 * entering the continuous read mode by mistake if some
+	 * performance enhance toggling bits P0-P7 were written during
+	 * dummy/mode cycles.
+	 */
+	status = macronix_quad_enable(nor);
+	if (status) {
+		dev_err(nor->dev, "Macronix quad-read not enabled\n");
+		return status;
+	}
+	nor->read_proto = SNOR_PROTO_1_1_4;
+	nor->read_opcode = SPINOR_OP_READ_1_1_4;
+	return 0;
+}
+
+/*
+ * For both Macronix Dual and Single modes, we don't care about the value of
+ * the Quad Enabled (QE) bit since the memory still replies to Dual or Single
+ * SPI commands.
+ */
+
+static int macronix_set_dual_mode(struct spi_nor *nor)
+{
+	nor->read_proto = SNOR_PROTO_1_1_2;
+	nor->read_opcode = SPINOR_OP_READ_1_1_2;
+	return 0;
+}
+
+static int macronix_set_single_mode(struct spi_nor *nor)
+{
+	nor->read_proto = SNOR_PROTO_1_1_1;
+	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;
-		}
-		/* Check whether Macronix QPI mode is enabled. */
-		if (nor->read_proto != SNOR_PROTO_4_4_4)
-			nor->read_proto = SNOR_PROTO_1_1_4;
-		break;
+		return macronix_set_quad_mode(nor);
 
 	case SNOR_MFR_MICRON:
 		/* Check whether Micron Quad mode is enabled. */
@@ -1211,6 +1268,9 @@ static int set_quad_mode(struct spi_nor *nor, const struct flash_info *info)
 static int set_dual_mode(struct spi_nor *nor, const struct flash_info *info)
 {
 	switch (JEDEC_MFR(info)) {
+	case SNOR_MFR_MACRONIX:
+		return macronix_set_dual_mode(nor);
+
 	case SNOR_MFR_MICRON:
 		/* Check whether Micron Dual mode is enabled. */
 		if (nor->read_proto != SNOR_PROTO_2_2_2)
@@ -1229,6 +1289,9 @@ static int set_dual_mode(struct spi_nor *nor, const struct flash_info *info)
 static int set_single_mode(struct spi_nor *nor, const struct flash_info *info)
 {
 	switch (JEDEC_MFR(info)) {
+	case SNOR_MFR_MACRONIX:
+		return macronix_set_single_mode(nor);
+
 	default:
 		nor->read_proto = SNOR_PROTO_1_1_1;
 		break;
Signature · 1 line
1.8.2.2
#9 lore raw

PATCH v2 5/14 mtd: spi-nor: fix support of Winbond memories

In reply to Cyrille Pitchen

This patch fixes the support of Winbond memories. Indeed, before performing any Quad SPI command, the Quad Enable (QE) non-volatile bit MUST be set in the Status Register 2.

According to the w25q16fw datasheet from Winbond:
"When QE=1, the /WP pin becomes IO2 and /HOLD pin becomes IO3."

Quad SPI instructions requires the bidirectional IO2 and IO3 pins.

---
 drivers/mtd/spi-nor/spi-nor.c | 100 ++++++++++++++++++++++++++++++++++++++++++
 include/linux/mtd/spi-nor.h   |   6 +++
 2 files changed, 106 insertions(+)
drivers/mtd/spi-nor/spi-nor.c +100 -0
index 042ac49d6188..f8a4887ca6eb 100644
@@ -1174,6 +1174,40 @@ static int spansion_quad_enable(struct spi_nor *nor)
 	return 0;
 }
 
+static int winbond_quad_enable(struct spi_nor *nor)
+{
+	int ret;
+	u8 sr2;
+
+	ret = nor->read_reg(nor, SPINOR_OP_RDSR2_WINB, &sr2, 1);
+	if (ret < 0)
+		return ret;
+
+	if (likely(sr2 & SR2_QUAD_EN_WINB))
+		return 0;
+	dev_warn(nor->dev, "Winbond Quad mode disabled, enable it\n");
+
+	write_enable(nor);
+
+	sr2 |= SR2_QUAD_EN_WINB;
+	ret = nor->write_reg(nor, SPINOR_OP_WRSR2_WINB, &sr2, 1);
+	if (ret < 0)
+		return ret;
+
+	if (spi_nor_wait_till_ready(nor))
+		return -EIO;
+
+	ret = nor->read_reg(nor, SPINOR_OP_RDSR2_WINB, &sr2, 1);
+	if (ret < 0)
+		return ret;
+	if (!(sr2 & SR2_QUAD_EN_WINB)) {
+		dev_err(nor->dev, "Winbond Quad bit not set\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
 static int macronix_set_quad_mode(struct spi_nor *nor)
 {
 	int status;
@@ -1234,6 +1268,63 @@ static int macronix_set_single_mode(struct spi_nor *nor)
 	return 0;
 }
 
+static int winbond_set_quad_mode(struct spi_nor *nor)
+{
+	int status;
+
+	/* Check whether the QPI mode is enabled. */
+	if (nor->read_proto == SNOR_PROTO_4_4_4) {
+		/* Since the QPI mode is enabled, the Quad Enabled (QE)
+		 * non-volatile bit is already set.
+		 * If the Fast Read 1-4-4 (0xeb) were used, we should
+		 * take care about the value M7-M0 written during
+		 * dummy/mode cycles to avoid entering the continuous
+		 * read mode by  mistake.
+		 * Also the Fast Read 1-1-4 (0x6b) op code is not
+		 * supported in QPI mode.
+		 * Hence the Fast Read 1-1-1 (0x0b) op code is chosen.
+		 */
+		nor->read_opcode = SPINOR_OP_READ_FAST;
+		return 0;
+	}
+
+	/*
+	 * The QPI mode is disabled but we still need to set the QE bit:
+	 * when QE=1, the /WP pin becomes IO2 and /HOLD pin becomes IO3.
+	 * If the Fast Read 1-4-4 (0xeb) were used, we should take care
+	 * about the value M7-M0 written during dummy/mode cycles to
+	 * avoid entering the continuous read mode by mistake.
+	 * Hence the Fast Read 1-1-4 (0x6b) op code is preferred.
+	 */
+	status = winbond_quad_enable(nor);
+	if (status) {
+		dev_err(nor->dev, "Winbond quad-read nor enabled\n");
+		return status;
+	}
+	nor->read_proto = SNOR_PROTO_1_1_4;
+	nor->read_opcode = SPINOR_OP_READ_1_1_4;
+	return 0;
+}
+
+/*
+ * For both Winbond Dual and Single modes, we don't care about the value of
+ * the Quad Enabled (QE) bit since the memory still replies to Dual or Single
+ * SPI commands.
+ */
+
+static int winbond_set_dual_mode(struct spi_nor *nor)
+{
+	nor->read_proto = SNOR_PROTO_1_1_2;
+	nor->read_opcode = SPINOR_OP_READ_1_1_2;
+	return 0;
+}
+
+static int winbond_set_single_mode(struct spi_nor *nor)
+{
+	nor->read_proto = SNOR_PROTO_1_1_1;
+	return 0;
+}
+
 static int set_quad_mode(struct spi_nor *nor, const struct flash_info *info)
 {
 	int status;
@@ -1242,6 +1333,9 @@ static int set_quad_mode(struct spi_nor *nor, const struct flash_info *info)
 	case SNOR_MFR_MACRONIX:
 		return macronix_set_quad_mode(nor);
 
+	case SNOR_MFR_WINBOND:
+		return winbond_set_quad_mode(nor);
+
 	case SNOR_MFR_MICRON:
 		/* Check whether Micron Quad mode is enabled. */
 		if (nor->read_proto != SNOR_PROTO_4_4_4)
@@ -1271,6 +1365,9 @@ static int set_dual_mode(struct spi_nor *nor, const struct flash_info *info)
 	case SNOR_MFR_MACRONIX:
 		return macronix_set_dual_mode(nor);
 
+	case SNOR_MFR_WINBOND:
+		return winbond_set_dual_mode(nor);
+
 	case SNOR_MFR_MICRON:
 		/* Check whether Micron Dual mode is enabled. */
 		if (nor->read_proto != SNOR_PROTO_2_2_2)
@@ -1292,6 +1389,9 @@ static int set_single_mode(struct spi_nor *nor, const struct flash_info *info)
 	case SNOR_MFR_MACRONIX:
 		return macronix_set_single_mode(nor);
 
+	case SNOR_MFR_WINBOND:
+		return winbond_set_single_mode(nor);
+
 	default:
 		nor->read_proto = SNOR_PROTO_1_1_1;
 		break;
include/linux/mtd/spi-nor.h +6 -0
index 89e3228ec1d0..46343f5a8162 100644
@@ -75,6 +75,12 @@
 #define SPINOR_OP_EN4B		0xb7	/* Enter 4-byte mode */
 #define SPINOR_OP_EX4B		0xe9	/* Exit 4-byte mode */
 
+/* Used for Winbond flashes only. */
+#define SPINOR_OP_RDSR2_WINB	0x35	/* Read status register 2 */
+#define SPINOR_OP_WRSR2_WINB	0x31	/* Write status register 2 */
+
+#define SR2_QUAD_EN_WINB	BIT(1)	/* Quad Enable bit */
+
 /* Used for Spansion flashes only. */
 #define SPINOR_OP_BRWR		0x17	/* Bank register write */
 
Signature · 1 line
1.8.2.2
#10 lore raw

PATCH v2 6/14 mtd: spi-nor: fix support of Micron memories

In reply to Cyrille Pitchen

This patch adds missing mode transitions. Indeed depending on both the current memory mode and the new protocol wanted by the user, we may need to perform a switch back to the Extended SPI mode.

However when the current mode is the Quad mode and the user has asked for a Quad SPI protocol, we'd rather stay in Quad mode and use the Fast Read 4-4-4 command than switch to the Extended SPI mode and use the Fast Read 1-1-4 command.

Also we'd rather stay in Dual mode than swith to the Extended SPI mode whenever the user has asked for Dual SPI protocol.

---
 drivers/mtd/spi-nor/spi-nor.c | 154 +++++++++++++++++++++++++++++++++++++++---
 include/linux/mtd/spi-nor.h   |   1 +
 2 files changed, 147 insertions(+), 8 deletions(-)
drivers/mtd/spi-nor/spi-nor.c +146 -8
index f8a4887ca6eb..db76af852198 100644
@@ -1325,6 +1325,147 @@ static int winbond_set_single_mode(struct spi_nor *nor)
 	return 0;
 }
 
+static int micron_set_protocol(struct spi_nor *nor, u8 mask, u8 val,
+			       enum spi_nor_protocol proto)
+{
+	u8 evcr;
+	int ret;
+
+	/* Read the Enhanced Volatile Configuration Register (EVCR). */
+	ret = nor->read_reg(nor, SPINOR_OP_RD_EVCR, &evcr, 1);
+	if (ret < 0) {
+		dev_err(nor->dev, "error while reading EVCR register\n");
+		return ret;
+	}
+
+	/* Check whether we need to update the protocol bits. */
+	if ((evcr & mask) == val)
+		return 0;
+
+	/* Set EVCR protocol bits. */
+	write_enable(nor);
+	evcr = (evcr & ~mask) | val;
+	ret = nor->write_reg(nor, SPINOR_OP_WD_EVCR, &evcr, 1);
+	if (ret < 0) {
+		dev_err(nor->dev, "error while writing EVCR register\n");
+		return ret;
+	}
+
+	/* Switch reg protocol now before accessing any other registers. */
+	nor->reg_proto = proto;
+
+	ret = spi_nor_wait_till_ready(nor);
+	if (ret)
+		return ret;
+
+	/* Read EVCR and check it. */
+	ret = nor->read_reg(nor, SPINOR_OP_RD_EVCR, &evcr, 1);
+	if (ret < 0 || (evcr & mask) != val) {
+		dev_err(nor->dev, "Micron EVCR protocol bits not updated\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int micron_set_extended_spi_protocol(struct spi_nor *nor)
+{
+	int ret;
+
+	/* Set Quad/Dual bits to 11 to select the Extended SPI mode */
+	ret = micron_set_protocol(nor,
+				  EVCR_QUAD_EN_MICRON | EVCR_DUAL_EN_MICRON,
+				  EVCR_QUAD_EN_MICRON | EVCR_DUAL_EN_MICRON,
+				  SNOR_PROTO_1_1_1);
+	if (ret) {
+		dev_err(nor->dev, "Failed to set Micron Extended SPI mode\n");
+		return ret;
+	}
+
+	nor->write_proto = SNOR_PROTO_1_1_1;
+	nor->erase_proto = SNOR_PROTO_1_1_1;
+	return 0;
+}
+
+static int micron_set_quad_mode(struct spi_nor *nor)
+{
+	/* Check whether the Dual SPI mode is enabled. */
+	if (unlikely(nor->read_proto == SNOR_PROTO_2_2_2)) {
+		int ret;
+
+		/*
+		 * Exit Micron Dual mode and switch to the Extended SPI mode:
+		 * we can change the mode safely as we write into a volatile
+		 * register.
+		 * Also the Quad mode is not worth it for MTD usages: it
+		 * should only be relevant for eXecution In Place (XIP) usages,
+		 * which are out of the scope of the spi-nor framework.
+		 */
+		ret = micron_set_extended_spi_protocol(nor);
+		if (ret)
+			return ret;
+	}
+
+	/*
+	 * Whatever the Quad mode is enabled or not, the
+	 * Fast Read Quad Output 1-1-4 (0x6b) op code is supported.
+	 */
+	if (nor->read_proto != SNOR_PROTO_4_4_4)
+		nor->read_proto = SNOR_PROTO_1_1_4;
+	nor->read_opcode = SPINOR_OP_READ_1_1_4;
+	return 0;
+}
+
+static int micron_set_dual_mode(struct spi_nor *nor)
+{
+	/* Check whether Quad mode is enabled. */
+	if (unlikely(nor->read_proto == SNOR_PROTO_4_4_4)) {
+		int ret;
+
+		/*
+		 * Exit Micron Quad mode and switch to the Extended SPI mode:
+		 * we can change the mode safely as we write into a volatile
+		 * register.
+		 * Also the Dual mode is not worth it for MTD usages: it
+		 * should only be relevant for eXecution In Place (XIP) usages,
+		 * which are out of the scope of the spi-nor framework.
+		 */
+		ret = micron_set_extended_spi_protocol(nor);
+		if (ret)
+			return ret;
+	}
+
+	/*
+	 * Whatever the Dual mode is enabled or not, the
+	 * Fast Read Dual Output 1-1-2 (0x3b) op code is supported.
+	 */
+	if (nor->read_proto != SNOR_PROTO_2_2_2)
+		nor->read_proto = SNOR_PROTO_1_1_2;
+	nor->read_opcode = SPINOR_OP_READ_1_1_2;
+	return 0;
+}
+
+static int micron_set_single_mode(struct spi_nor *nor)
+{
+	/* Check whether either the Dual or Quad mode is enabled. */
+	if (unlikely(nor->read_proto != SNOR_PROTO_1_1_1)) {
+		int ret;
+
+		/*
+		 * Exit Micron Dual or Quad mode and switch to the Extended SPI
+		 * mode: we can change the mode safely as we write into a
+		 * volatile register.
+		 */
+		ret = micron_set_extended_spi_protocol(nor);
+		if (ret)
+			return ret;
+
+		nor->read_proto = SNOR_PROTO_1_1_1;
+	}
+
+	return 0;
+}
+
 static int set_quad_mode(struct spi_nor *nor, const struct flash_info *info)
 {
 	int status;
@@ -1337,10 +1478,7 @@ static int set_quad_mode(struct spi_nor *nor, const struct flash_info *info)
 		return winbond_set_quad_mode(nor);
 
 	case SNOR_MFR_MICRON:
-		/* Check whether Micron Quad mode is enabled. */
-		if (nor->read_proto != SNOR_PROTO_4_4_4)
-			nor->read_proto = SNOR_PROTO_1_1_4;
-		break;
+		return micron_set_quad_mode(nor);
 
 	case SNOR_MFR_SPANSION:
 		status = spansion_quad_enable(nor);
@@ -1369,10 +1507,7 @@ static int set_dual_mode(struct spi_nor *nor, const struct flash_info *info)
 		return winbond_set_dual_mode(nor);
 
 	case SNOR_MFR_MICRON:
-		/* Check whether Micron Dual mode is enabled. */
-		if (nor->read_proto != SNOR_PROTO_2_2_2)
-			nor->read_proto = SNOR_PROTO_1_1_2;
-		break;
+		return micron_set_dual_mode(nor);
 
 	default:
 		nor->read_proto = SNOR_PROTO_1_1_2;
@@ -1392,6 +1527,9 @@ static int set_single_mode(struct spi_nor *nor, const struct flash_info *info)
 	case SNOR_MFR_WINBOND:
 		return winbond_set_single_mode(nor);
 
+	case SNOR_MFR_MICRON:
+		return micron_set_single_mode(nor);
+
 	default:
 		nor->read_proto = SNOR_PROTO_1_1_1;
 		break;
include/linux/mtd/spi-nor.h +1 -0
index 46343f5a8162..d0a6f343a063 100644
@@ -102,6 +102,7 @@
 
 /* Enhanced Volatile Configuration Register bits */
 #define EVCR_QUAD_EN_MICRON	BIT(7)	/* Micron Quad I/O */
+#define EVCR_DUAL_EN_MICRON	BIT(6)	/* Micron Dual I/O */
 
 /* Flag Status Register bits */
 #define FSR_READY		BIT(7)
Signature · 1 line
1.8.2.2
#11 lore raw

PATCH v2 7/14 mtd: spi-nor: fix support of Spansion memories

In reply to Cyrille Pitchen

This patch is only a transitional one. It concludes the series of patches to select op codes and protocols by manufacturer.

---
 drivers/mtd/spi-nor/spi-nor.c | 53 ++++++++++++++++++++++++++++++-------------
 1 file changed, 37 insertions(+), 16 deletions(-)
drivers/mtd/spi-nor/spi-nor.c +37 -16
index db76af852198..067425c7a0ff 100644
@@ -1466,10 +1466,35 @@ static int micron_set_single_mode(struct spi_nor *nor)
 	return 0;
 }
 
-static int set_quad_mode(struct spi_nor *nor, const struct flash_info *info)
+static int spansion_set_quad_mode(struct spi_nor *nor)
 {
 	int status;
 
+	status = spansion_quad_enable(nor);
+	if (status) {
+		dev_err(nor->dev, "Spansion quad-read not enabled\n");
+		return -EINVAL;
+	}
+	nor->read_proto = SNOR_PROTO_1_1_4;
+	nor->read_opcode = SPINOR_OP_READ_1_1_4;
+	return 0;
+}
+
+static int spansion_set_dual_mode(struct spi_nor *nor)
+{
+	nor->read_proto = SNOR_PROTO_1_1_2;
+	nor->read_opcode = SPINOR_OP_READ_1_1_2;
+	return 0;
+}
+
+static int spansion_set_single_mode(struct spi_nor *nor)
+{
+	nor->read_proto = SNOR_PROTO_1_1_1;
+	return 0;
+}
+
+static int set_quad_mode(struct spi_nor *nor, const struct flash_info *info)
+{
 	switch (JEDEC_MFR(info)) {
 	case SNOR_MFR_MACRONIX:
 		return macronix_set_quad_mode(nor);
@@ -1481,20 +1506,13 @@ static int set_quad_mode(struct spi_nor *nor, const struct flash_info *info)
 		return micron_set_quad_mode(nor);
 
 	case SNOR_MFR_SPANSION:
-		status = spansion_quad_enable(nor);
-		if (status) {
-			dev_err(nor->dev, "Spansion quad-read not enabled\n");
-			return -EINVAL;
-		}
-		nor->read_proto = SNOR_PROTO_1_1_4;
-		break;
+		return spansion_set_quad_mode(nor);
 
 	default:
-		return -EINVAL;
+		break;
 	}
 
-	nor->read_opcode = SPINOR_OP_READ_1_1_4;
-	return 0;
+	return -EINVAL;
 }
 
 static int set_dual_mode(struct spi_nor *nor, const struct flash_info *info)
@@ -1509,13 +1527,14 @@ static int set_dual_mode(struct spi_nor *nor, const struct flash_info *info)
 	case SNOR_MFR_MICRON:
 		return micron_set_dual_mode(nor);
 
+	case SNOR_MFR_SPANSION:
+		return spansion_set_dual_mode(nor);
+
 	default:
-		nor->read_proto = SNOR_PROTO_1_1_2;
 		break;
 	}
 
-	nor->read_opcode = SPINOR_OP_READ_1_1_2;
-	return 0;
+	return -EINVAL;
 }
 
 static int set_single_mode(struct spi_nor *nor, const struct flash_info *info)
@@ -1530,12 +1549,14 @@ static int set_single_mode(struct spi_nor *nor, const struct flash_info *info)
 	case SNOR_MFR_MICRON:
 		return micron_set_single_mode(nor);
 
+	case SNOR_MFR_SPANSION:
+		return spansion_set_single_mode(nor);
+
 	default:
-		nor->read_proto = SNOR_PROTO_1_1_1;
 		break;
 	}
 
-	return 0;
+	return -EINVAL;
 }
 
 static int spi_nor_check(struct spi_nor *nor)
Signature · 1 line
1.8.2.2
#12 lore raw

PATCH v2 8/14 mtd: spi-nor: configure the number of dummy clock cycles by manufacturer

In reply to Cyrille Pitchen

This is a transitional patch which let us set the number of dummy clock cycles by manufacturer.

More patches will follow by manufacturer to actually configure the relevant number of dummy clock cycles following the dedicated procedure.

For instance, some manufacturers like Spansion configure the number of dummy clock cycles to be used by Fast Read command through some non-volatile register. In such a case, we should avoid updating its value but instead read it then set the nor->read_dummy accordingly.

On the other hand, some manufacturers like Micron use some volatile register. In this case, we'd rather update this register to use a number of dummy clock cycles, which is a multiple of 8.
Indeed some drivers, like m25p80, only support writing bytes, hence multiples of 8 bits.

---
 drivers/mtd/spi-nor/spi-nor.c | 99 ++++++++++++++++++++++++++++++++-----------
 1 file changed, 74 insertions(+), 25 deletions(-)
drivers/mtd/spi-nor/spi-nor.c +74 -25
index 067425c7a0ff..353a0f6ac3fe 100644
@@ -139,24 +139,6 @@ static int read_cr(struct spi_nor *nor)
 }
 
 /*
- * Dummy Cycle calculation for different type of read.
- * It can be used to support more commands with
- * different dummy cycle requirements.
- */
-static inline int spi_nor_read_dummy_cycles(struct spi_nor *nor)
-{
-	switch (nor->flash_read) {
-	case SPI_NOR_FAST:
-	case SPI_NOR_DUAL:
-	case SPI_NOR_QUAD:
-		return 8;
-	case SPI_NOR_NORMAL:
-		return 0;
-	}
-	return 0;
-}
-
-/*
  * Write status register 1 byte
  * Returns negative if error occurred.
  */
@@ -1225,6 +1207,7 @@ static int macronix_set_quad_mode(struct spi_nor *nor)
 		 * read (performance enhance) mode by mistake!
 		 */
 		nor->read_opcode = SPINOR_OP_READ_1_4_4;
+		nor->read_dummy = 8;
 		return 0;
 	}
 
@@ -1246,6 +1229,7 @@ static int macronix_set_quad_mode(struct spi_nor *nor)
 	}
 	nor->read_proto = SNOR_PROTO_1_1_4;
 	nor->read_opcode = SPINOR_OP_READ_1_1_4;
+	nor->read_dummy = 8;
 	return 0;
 }
 
@@ -1259,12 +1243,27 @@ static int macronix_set_dual_mode(struct spi_nor *nor)
 {
 	nor->read_proto = SNOR_PROTO_1_1_2;
 	nor->read_opcode = SPINOR_OP_READ_1_1_2;
+	nor->read_dummy = 8;
 	return 0;
 }
 
 static int macronix_set_single_mode(struct spi_nor *nor)
 {
+	u8 read_dummy;
+
+	switch (nor->read_opcode) {
+	case SPINOR_OP_READ:
+	case SPINOR_OP_READ4:
+		read_dummy = 0;
+		break;
+
+	default:
+		read_dummy = 8;
+		break;
+	}
+
 	nor->read_proto = SNOR_PROTO_1_1_1;
+	nor->read_dummy = read_dummy;
 	return 0;
 }
 
@@ -1285,6 +1284,7 @@ static int winbond_set_quad_mode(struct spi_nor *nor)
 		 * Hence the Fast Read 1-1-1 (0x0b) op code is chosen.
 		 */
 		nor->read_opcode = SPINOR_OP_READ_FAST;
+		nor->read_dummy = 8;
 		return 0;
 	}
 
@@ -1303,6 +1303,7 @@ static int winbond_set_quad_mode(struct spi_nor *nor)
 	}
 	nor->read_proto = SNOR_PROTO_1_1_4;
 	nor->read_opcode = SPINOR_OP_READ_1_1_4;
+	nor->read_dummy = 8;
 	return 0;
 }
 
@@ -1316,12 +1317,27 @@ static int winbond_set_dual_mode(struct spi_nor *nor)
 {
 	nor->read_proto = SNOR_PROTO_1_1_2;
 	nor->read_opcode = SPINOR_OP_READ_1_1_2;
+	nor->read_dummy = 8;
 	return 0;
 }
 
 static int winbond_set_single_mode(struct spi_nor *nor)
 {
+	u8 read_dummy;
+
+	switch (nor->read_opcode) {
+	case SPINOR_OP_READ:
+	case SPINOR_OP_READ4:
+		read_dummy = 0;
+		break;
+
+	default:
+		read_dummy = 8;
+		break;
+	}
+
 	nor->read_proto = SNOR_PROTO_1_1_1;
+	nor->read_dummy = read_dummy;
 	return 0;
 }
 
@@ -1413,6 +1429,7 @@ static int micron_set_quad_mode(struct spi_nor *nor)
 	if (nor->read_proto != SNOR_PROTO_4_4_4)
 		nor->read_proto = SNOR_PROTO_1_1_4;
 	nor->read_opcode = SPINOR_OP_READ_1_1_4;
+	nor->read_dummy = 8;
 	return 0;
 }
 
@@ -1442,11 +1459,14 @@ static int micron_set_dual_mode(struct spi_nor *nor)
 	if (nor->read_proto != SNOR_PROTO_2_2_2)
 		nor->read_proto = SNOR_PROTO_1_1_2;
 	nor->read_opcode = SPINOR_OP_READ_1_1_2;
+	nor->read_dummy = 8;
 	return 0;
 }
 
 static int micron_set_single_mode(struct spi_nor *nor)
 {
+	u8 read_dummy;
+
 	/* Check whether either the Dual or Quad mode is enabled. */
 	if (unlikely(nor->read_proto != SNOR_PROTO_1_1_1)) {
 		int ret;
@@ -1463,6 +1483,18 @@ static int micron_set_single_mode(struct spi_nor *nor)
 		nor->read_proto = SNOR_PROTO_1_1_1;
 	}
 
+	/* Force the number of dummy cycles to 8 for Fast Read, 0 for Read. */
+	switch (nor->read_opcode) {
+	case SPINOR_OP_READ:
+	case SPINOR_OP_READ4:
+		read_dummy = 0;
+		break;
+
+	default:
+		read_dummy = 8;
+		break;
+	}
+	nor->read_dummy = read_dummy;
 	return 0;
 }
 
@@ -1477,6 +1509,7 @@ static int spansion_set_quad_mode(struct spi_nor *nor)
 	}
 	nor->read_proto = SNOR_PROTO_1_1_4;
 	nor->read_opcode = SPINOR_OP_READ_1_1_4;
+	nor->read_dummy = 8;
 	return 0;
 }
 
@@ -1484,12 +1517,27 @@ static int spansion_set_dual_mode(struct spi_nor *nor)
 {
 	nor->read_proto = SNOR_PROTO_1_1_2;
 	nor->read_opcode = SPINOR_OP_READ_1_1_2;
+	nor->read_dummy = 8;
 	return 0;
 }
 
 static int spansion_set_single_mode(struct spi_nor *nor)
 {
+	u8 read_dummy;
+
+	switch (nor->read_opcode) {
+	case SPINOR_OP_READ:
+	case SPINOR_OP_READ4:
+		read_dummy = 0;
+		break;
+
+	default:
+		read_dummy = 8;
+		break;
+	}
+
 	nor->read_proto = SNOR_PROTO_1_1_1;
+	nor->read_dummy = read_dummy;
 	return 0;
 }
 
@@ -1704,11 +1752,14 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
 	if (info->flags & SPI_NOR_NO_FR)
 		nor->flash_read = SPI_NOR_NORMAL;
 
-	/* Default commands */
-	if (nor->flash_read == SPI_NOR_NORMAL)
+	/* Default commands and number of dummy cycles */
+	if (nor->flash_read == SPI_NOR_NORMAL) {
 		nor->read_opcode = SPINOR_OP_READ;
-	else
+		nor->read_dummy = 0;
+	} else {
 		nor->read_opcode = SPINOR_OP_READ_FAST;
+		nor->read_dummy = 8;
+	}
 
 	nor->program_opcode = SPINOR_OP_PP;
 
@@ -1723,8 +1774,8 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
 	 *  - SNOR_PROTO_2_2_2 is either:
 	 *    + Micron Dual mode enabled
 	 *
-	 * The opcodes and the protocols are updated depending on the
-	 * manufacturer.
+	 * The opcodes, the protocols and the number of dummy cycles are updated
+	 * depending on the manufacturer.
 	 * The read opcode and protocol should be updated by the relevant
 	 * function when entering Quad or Dual mode.
 	 */
@@ -1788,8 +1839,6 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
 		return -EINVAL;
 	}
 
-	nor->read_dummy = spi_nor_read_dummy_cycles(nor);
-
 	dev_info(dev, "%s (%lld Kbytes)\n", info->name,
 			(long long)mtd->size >> 10);
 
Signature · 1 line
1.8.2.2
#13 lore raw

PATCH v2 9/14 mtd: spi-nor: configure the number of dummy clock cycles on Micron memories

In reply to Cyrille Pitchen

The spi-nor framework currently expects all Fast Read operations to use 8 dummy clock cycles. Especially some drivers like m25p80 can only support multiple of 8 dummy clock cycles.

On Micron memories, the number of dummy clock cycles to be used by Fast Read commands can be safely set to 8 by updating the Volatile Configuration Register (VCR).

Also the XIP bit is set at the same time when updating the VCR so the Continuous Read mode is disabled: this prevents us from entering it by mistake.

---
 drivers/mtd/spi-nor/spi-nor.c | 72 ++++++++++++++++++++++++++++++++++++++-----
 include/linux/mtd/spi-nor.h   |  2 ++
 2 files changed, 67 insertions(+), 7 deletions(-)
drivers/mtd/spi-nor/spi-nor.c +65 -7
index 353a0f6ac3fe..3f79619aea52 100644
@@ -1341,6 +1341,53 @@ static int winbond_set_single_mode(struct spi_nor *nor)
 	return 0;
 }
 
+static int micron_set_dummy_cycles(struct spi_nor *nor, u8 read_dummy)
+{
+	u8 vcr, val, mask;
+	int ret;
+
+	/* Set bit3 (XIP) to disable the Continuous Read mode */
+	mask = GENMASK(7, 4) | BIT(3);
+	val = ((read_dummy << 4) | BIT(3)) & mask;
+
+	/* Read the Volatile Configuration Register (VCR). */
+	ret = nor->read_reg(nor, SPINOR_OP_RD_VCR, &vcr, 1);
+	if (ret < 0) {
+		dev_err(nor->dev, "error while reading VCR register\n");
+		return ret;
+	}
+
+	/* Check whether we need to update the number of dummy cycles. */
+	if ((vcr & mask) == val) {
+		nor->read_dummy = read_dummy;
+		return 0;
+	}
+
+	/* Update the number of dummy into the VCR. */
+	write_enable(nor);
+	vcr = (vcr & ~mask) | val;
+	ret = nor->write_reg(nor, SPINOR_OP_WR_VCR, &vcr, 1);
+	if (ret < 0) {
+		dev_err(nor->dev, "error while writing VCR register\n");
+		return ret;
+	}
+
+	ret = spi_nor_wait_till_ready(nor);
+	if (ret)
+		return ret;
+
+	/* Read VCR and check it. */
+	ret = nor->read_reg(nor, SPINOR_OP_RD_VCR, &vcr, 1);
+	if (ret < 0 || (vcr & mask) != val) {
+		dev_err(nor->dev, "Micron VCR dummy cycles not updated\n");
+		return -EINVAL;
+	}
+
+	/* Save the number of dummy cycles to use with Fast Read commands */
+	nor->read_dummy = read_dummy;
+	return 0;
+}
+
 static int micron_set_protocol(struct spi_nor *nor, u8 mask, u8 val,
 			       enum spi_nor_protocol proto)
 {
@@ -1425,12 +1472,15 @@ static int micron_set_quad_mode(struct spi_nor *nor)
 	/*
 	 * Whatever the Quad mode is enabled or not, the
 	 * Fast Read Quad Output 1-1-4 (0x6b) op code is supported.
+	 * Force the number of dummy cycles to 8 and disable the Continuous Read
+	 * mode to prevent some drivers from using it by mistake (m25p80).
+	 * We can change these settings safely as we write into a volatile
+	 * register.
 	 */
 	if (nor->read_proto != SNOR_PROTO_4_4_4)
 		nor->read_proto = SNOR_PROTO_1_1_4;
 	nor->read_opcode = SPINOR_OP_READ_1_1_4;
-	nor->read_dummy = 8;
-	return 0;
+	return micron_set_dummy_cycles(nor, 8);
 }
 
 static int micron_set_dual_mode(struct spi_nor *nor)
@@ -1455,12 +1505,15 @@ static int micron_set_dual_mode(struct spi_nor *nor)
 	/*
 	 * Whatever the Dual mode is enabled or not, the
 	 * Fast Read Dual Output 1-1-2 (0x3b) op code is supported.
+	 * Force the number of dummy cycles to 8 and disable the Continuous Read
+	 * mode to prevent some drivers from using it by mistake (m25p80).
+	 * We can change these settings safely as we write into a volatile
+	 * register.
 	 */
 	if (nor->read_proto != SNOR_PROTO_2_2_2)
 		nor->read_proto = SNOR_PROTO_1_1_2;
 	nor->read_opcode = SPINOR_OP_READ_1_1_2;
-	nor->read_dummy = 8;
-	return 0;
+	return micron_set_dummy_cycles(nor, 8);
 }
 
 static int micron_set_single_mode(struct spi_nor *nor)
@@ -1483,7 +1536,13 @@ static int micron_set_single_mode(struct spi_nor *nor)
 		nor->read_proto = SNOR_PROTO_1_1_1;
 	}
 
-	/* Force the number of dummy cycles to 8 for Fast Read, 0 for Read. */
+	/*
+	 * Force the number of dummy cycles to 8 for Fast Read, 0 for Read
+	 * and disable the Continuous Read mode to prevent some drivers from
+	 * using it by mistake (m25p80).
+	 * We can change these settings safely as we write into a volatile
+	 * register.
+	 */
 	switch (nor->read_opcode) {
 	case SPINOR_OP_READ:
 	case SPINOR_OP_READ4:
@@ -1494,8 +1553,7 @@ static int micron_set_single_mode(struct spi_nor *nor)
 		read_dummy = 8;
 		break;
 	}
-	nor->read_dummy = read_dummy;
-	return 0;
+	return micron_set_dummy_cycles(nor, read_dummy);
 }
 
 static int spansion_set_quad_mode(struct spi_nor *nor)
include/linux/mtd/spi-nor.h +2 -0
index d0a6f343a063..2dc0f8b429ca 100644
@@ -86,6 +86,8 @@
 
 /* Used for Micron flashes only. */
 #define SPINOR_OP_MIO_RDID	0xaf	/* Multiple I/O Read JEDEC ID */
+#define SPINOR_OP_RD_VCR	0x85	/* Read VCR register */
+#define SPINOR_OP_WR_VCR	0x81	/* Write VCR register */
 #define SPINOR_OP_RD_EVCR	0x65    /* Read EVCR register */
 #define SPINOR_OP_WD_EVCR	0x61    /* Write EVCR register */
 
Signature · 1 line
1.8.2.2
#14 lore raw

PATCH v2 10/14 mtd: spi-nor: configure the number of dummy clock cycles on Macronix memories

In reply to Cyrille Pitchen

The spi-nor framework currently expects all Fast Read operations to use 8 dummy clock cycles. Especially some drivers like m25p80 can only support multiple of 8 dummy clock cycles.

On Macronix memories, the number of dummy clock cycles to be used by Fast Read commands can be safely set to 8 by updating the DC0 and DC1 volatile bits inside the Configuration Register.

According to the mx66l1g45g datasheet from Macronix, using 8 dummy clock cycles should be enough to set the SPI bus clock frequency up to:
- 133 MHz for Fast Read 1-1-1, 1-1-2, 1-1-4 and 1-2-2 commands in Single Transfer Rate (STR)
- 104 MHz for Fast Read 1-4-4 (or 4-4-4 in QPI mode) commands (STR)

---
 drivers/mtd/spi-nor/spi-nor.c | 155 +++++++++++++++++++++++++++++++++++++++---
 1 file changed, 147 insertions(+), 8 deletions(-)
drivers/mtd/spi-nor/spi-nor.c +147 -8
index 3f79619aea52..68abae5c72e9 100644
@@ -1190,6 +1190,136 @@ static int winbond_quad_enable(struct spi_nor *nor)
 	return 0;
 }
 
+static int macronix_dummy2code(u8 read_opcode, u8 read_dummy, u8 *dc)
+{
+	switch (read_opcode) {
+	case SPINOR_OP_READ:
+	case SPINOR_OP_READ4:
+		*dc = 0;
+		break;
+
+	case SPINOR_OP_READ_FAST:
+	case SPINOR_OP_READ_1_1_2:
+	case SPINOR_OP_READ_1_1_4:
+	case SPINOR_OP_READ4_FAST:
+	case SPINOR_OP_READ4_1_1_2:
+	case SPINOR_OP_READ4_1_1_4:
+		switch (read_dummy) {
+		case 6:
+			*dc = 1;
+			break;
+		case 8:
+			*dc = 0;
+			break;
+		case 10:
+			*dc = 3;
+			break;
+		default:
+			return -EINVAL;
+		}
+		break;
+
+	case SPINOR_OP_READ_1_2_2:
+	case SPINOR_OP_READ4_1_2_2:
+		switch (read_dummy) {
+		case 4:
+			*dc = 0;
+			break;
+		case 6:
+			*dc = 1;
+			break;
+		case 8:
+			*dc = 2;
+			break;
+		case 10:
+			*dc = 3;
+		default:
+			return -EINVAL;
+		}
+		break;
+
+	case SPINOR_OP_READ_1_4_4:
+	case SPINOR_OP_READ4_1_4_4:
+		switch (read_dummy) {
+		case 4:
+			*dc = 1;
+			break;
+		case 6:
+			*dc = 0;
+			break;
+		case 8:
+			*dc = 2;
+			break;
+		case 10:
+			*dc = 3;
+		default:
+			return -EINVAL;
+		}
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int macronix_set_dummy_cycles(struct spi_nor *nor, u8 read_dummy)
+{
+	int ret, sr, cr, mask, val;
+	u16 sr_cr;
+	u8 dc;
+
+	/* Convert the number of dummy cycles into Macronix DC volatile bits */
+	ret = macronix_dummy2code(nor->read_opcode, read_dummy, &dc);
+	if (ret)
+		return ret;
+
+	mask = GENMASK(7, 6);
+	val = (dc << 6) & mask;
+
+	cr = read_cr(nor);
+	if (cr < 0) {
+		dev_err(nor->dev, "error while reading the config register\n");
+		return cr;
+	}
+
+	if ((cr & mask) == val) {
+		nor->read_dummy = read_dummy;
+		return 0;
+	}
+
+	sr = read_sr(nor);
+	if (sr < 0) {
+		dev_err(nor->dev, "error while reading the status register\n");
+		return sr;
+	}
+
+	cr = (cr & ~mask) | val;
+	sr_cr = (sr & 0xff) | ((cr & 0xff) << 8);
+	write_enable(nor);
+	ret = write_sr_cr(nor, sr_cr);
+	if (ret) {
+		dev_err(nor->dev,
+			"error while writing the SR and CR registers\n");
+		return ret;
+	}
+
+	ret = spi_nor_wait_till_ready(nor);
+	if (ret)
+		return ret;
+
+	cr = read_cr(nor);
+	if (cr < 0 || (cr & mask) != val) {
+		dev_err(nor->dev, "Macronix Dummy Cycle bits not updated\n");
+		return -EINVAL;
+	}
+
+	/* Save the number of dummy cycles to use with Fast Read commands */
+	nor->read_dummy = read_dummy;
+	return 0;
+}
+
 static int macronix_set_quad_mode(struct spi_nor *nor)
 {
 	int status;
@@ -1207,8 +1337,7 @@ static int macronix_set_quad_mode(struct spi_nor *nor)
 		 * read (performance enhance) mode by mistake!
 		 */
 		nor->read_opcode = SPINOR_OP_READ_1_4_4;
-		nor->read_dummy = 8;
-		return 0;
+		return macronix_set_dummy_cycles(nor, 8);
 	}
 
 	/*
@@ -1221,6 +1350,9 @@ static int macronix_set_quad_mode(struct spi_nor *nor)
 	 * entering the continuous read mode by mistake if some
 	 * performance enhance toggling bits P0-P7 were written during
 	 * dummy/mode cycles.
+	 *
+	 * Use the Fast Read Quad Output 1-1-4 (0x6b) command with 8 dummy
+	 * cycles (up to 133MHz for STR and 66MHz for DTR).
 	 */
 	status = macronix_quad_enable(nor);
 	if (status) {
@@ -1229,8 +1361,7 @@ static int macronix_set_quad_mode(struct spi_nor *nor)
 	}
 	nor->read_proto = SNOR_PROTO_1_1_4;
 	nor->read_opcode = SPINOR_OP_READ_1_1_4;
-	nor->read_dummy = 8;
-	return 0;
+	return macronix_set_dummy_cycles(nor, 8);
 }
 
 /*
@@ -1241,16 +1372,25 @@ static int macronix_set_quad_mode(struct spi_nor *nor)
 
 static int macronix_set_dual_mode(struct spi_nor *nor)
 {
+	/*
+	 * Use the Fast Read Dual Output 1-1-2 (0x3b) command with 8 dummy
+	 * cycles (up to 133MHz for STR and 66MHz for DTR).
+	 */
 	nor->read_proto = SNOR_PROTO_1_1_2;
 	nor->read_opcode = SPINOR_OP_READ_1_1_2;
-	nor->read_dummy = 8;
-	return 0;
+	return macronix_set_dummy_cycles(nor, 8);
 }
 
 static int macronix_set_single_mode(struct spi_nor *nor)
 {
 	u8 read_dummy;
 
+	/*
+	 * Configure 8 dummy cycles for Fast Read 1-1-1 (0x0b) command (up to
+	 * 133MHz for STR and 66MHz for DTR). The Read 1-1-1 (0x03) command
+	 * expects no dummy cycle.
+	 * read_opcode should not be overridden here!
+	 */
 	switch (nor->read_opcode) {
 	case SPINOR_OP_READ:
 	case SPINOR_OP_READ4:
@@ -1263,8 +1403,7 @@ static int macronix_set_single_mode(struct spi_nor *nor)
 	}
 
 	nor->read_proto = SNOR_PROTO_1_1_1;
-	nor->read_dummy = read_dummy;
-	return 0;
+	return macronix_set_dummy_cycles(nor, read_dummy);
 }
 
 static int winbond_set_quad_mode(struct spi_nor *nor)
Signature · 1 line
1.8.2.2
#15 lore raw

Re: [PATCH linux-next v2 10/14] mtd: spi-nor: configure the number of dummy clock cycles on Macronix memories

Hi all,

I've found a small issue within this patch when I test with a Macronix mx25l51245g (JEDEC ID C2201A so reported as "mx66l51235l" by spi-nor.c). It deals with a wrong op code used when reading the Configuration Register. See below for more details.

So I will send a new patch to fix this issue.

Otherwise, I tested on a sama5d2 xplained board and it works :)

Le 08/01/2016 17:02, Cyrille Pitchen a ?crit :

Quoted 16 lines

The spi-nor framework currently expects all Fast Read operations to use 8 dummy clock cycles. Especially some drivers like m25p80 can only support multiple of 8 dummy clock cycles.

On Macronix memories, the number of dummy clock cycles to be used by Fast Read commands can be safely set to 8 by updating the DC0 and DC1 volatile bits inside the Configuration Register.

According to the mx66l1g45g datasheet from Macronix, using 8 dummy clock cycles should be enough to set the SPI bus clock frequency up to:
- 133 MHz for Fast Read 1-1-1, 1-1-2, 1-1-4 and 1-2-2 commands in Single Transfer Rate (STR)
- 104 MHz for Fast Read 1-4-4 (or 4-4-4 in QPI mode) commands (STR)

---

[...]

Quoted 5 lines
drivers/mtd/spi-nor/spi-nor.c +5 -0
+static int macronix_set_dummy_cycles(struct spi_nor *nor, u8 read_dummy)
+{
+	int ret, sr, cr, mask, val;
+	u16 sr_cr;
+	u8 dc;

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

Quoted 10 lines
drivers/mtd/spi-nor/spi-nor.c +10 -0
+
+	/* Convert the number of dummy cycles into Macronix DC volatile bits */
+	ret = macronix_dummy2code(nor->read_opcode, read_dummy, &dc);
+	if (ret)
+		return ret;
+
+	mask = GENMASK(7, 6);
+	val = (dc << 6) & mask;
+
+	cr = read_cr(nor);

Macronix memories use the 0x15 op code (not 0x35) to read the Configuration Register. The 0x35 op code is used to enter the QPI mode. So read_cr() cannot be used here.

Quoted 21 lines
drivers/mtd/spi-nor/spi-nor.c +21 -0
+	if (cr < 0) {
+		dev_err(nor->dev, "error while reading the config register\n");
+		return cr;
+	}
+
+	if ((cr & mask) == val) {
+		nor->read_dummy = read_dummy;
+		return 0;
+	}
+
+	sr = read_sr(nor);
+	if (sr < 0) {
+		dev_err(nor->dev, "error while reading the status register\n");
+		return sr;
+	}
+
+	cr = (cr & ~mask) | val;
+	sr_cr = (sr & 0xff) | ((cr & 0xff) << 8);
+	write_enable(nor);
+	ret = write_sr_cr(nor, sr_cr);
+	if (ret) {

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

Quoted 10 lines
drivers/mtd/spi-nor/spi-nor.c +10 -0
+		dev_err(nor->dev,
+			"error while writing the SR and CR registers\n");
+		return ret;
+	}
+
+	ret = spi_nor_wait_till_ready(nor);
+	if (ret)
+		return ret;
+
+	cr = read_cr(nor);

Here again, we must use the 0x15 op code instead of 0x35.

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

Quoted 10 lines
drivers/mtd/spi-nor/spi-nor.c +10 -0
+	if (cr < 0 || (cr & mask) != val) {
+		dev_err(nor->dev, "Macronix Dummy Cycle bits not updated\n");
+		return -EINVAL;
+	}
+
+	/* Save the number of dummy cycles to use with Fast Read commands */
+	nor->read_dummy = read_dummy;
+	return 0;
+}
+

Best regards,

Cyrille

#16 lore raw

PATCH v2 11/14 mtd: spi-nor: configure the number of dummy clock cycles on Spansion memories

In reply to Cyrille Pitchen

On Spansion memories, the number of dummy clock cycles to be used during Fast Read commands is configured through the 2bit latency code (LC). These bits are non-volatile inside the Configuration Register.

To avoid breaking the configuration expected at reset by some bootloaders, we'd rather read the latency code and set the nor->read_dummy value accordingly than update those non-volatile bits.

Since the Quad Enable non-volatile bit can be read at the same time from the Control Register, we now check its value to avoid some calls of the spansion_quad_enable() function when they are not needed.

---
 drivers/mtd/spi-nor/spi-nor.c | 159 ++++++++++++++++++++++++++++++++++++------
 1 file changed, 137 insertions(+), 22 deletions(-)
drivers/mtd/spi-nor/spi-nor.c +137 -22
index 68abae5c72e9..9ee271b2e5cb 100644
@@ -1695,47 +1695,162 @@ static int micron_set_single_mode(struct spi_nor *nor)
 	return micron_set_dummy_cycles(nor, read_dummy);
 }
 
-static int spansion_set_quad_mode(struct spi_nor *nor)
+static inline int spansion_get_config(struct spi_nor *nor,
+				      bool *quad_enabled,
+				      u8 *latency_code)
 {
-	int status;
+	int cr;
 
-	status = spansion_quad_enable(nor);
-	if (status) {
-		dev_err(nor->dev, "Spansion quad-read not enabled\n");
+	cr = read_cr(nor);
+	if (cr < 0) {
+		dev_err(nor->dev,
+			"error while reading the configuration register\n");
+		return cr;
+	}
+
+	if (quad_enabled)
+		*quad_enabled = !!(cr & CR_QUAD_EN_SPAN);
+
+	if (latency_code)
+		*latency_code = (u8)((cr & GENMASK(7, 6)) >> 6);
+
+	return 0;
+}
+
+static int spansion_set_dummy_cycles(struct spi_nor *nor, u8 latency_code)
+{
+	/* SDR dummy cycles */
+	switch (nor->read_opcode) {
+	case SPINOR_OP_READ:
+	case SPINOR_OP_READ4:
+		nor->read_dummy = 0;
+		break;
+
+	case SPINOR_OP_READ_FAST:
+	case SPINOR_OP_READ_1_1_2:
+	case SPINOR_OP_READ_1_1_4:
+	case SPINOR_OP_READ4_FAST:
+	case SPINOR_OP_READ4_1_1_2:
+	case SPINOR_OP_READ4_1_1_4:
+		nor->read_dummy = (latency_code == 3) ? 0 : 8;
+		break;
+
+	case SPINOR_OP_READ_1_2_2:
+	case SPINOR_OP_READ4_1_2_2:
+		switch (latency_code) {
+		default:
+		case 0:
+		case 3:
+			nor->read_dummy = 4;
+			break;
+		case 1:
+			nor->read_dummy = 5;
+			break;
+		case 2:
+			nor->read_dummy = 6;
+			break;
+		}
+		break;
+
+
+	case SPINOR_OP_READ_1_4_4:
+	case SPINOR_OP_READ4_1_4_4:
+		switch (latency_code) {
+		default:
+		case 0:
+		case 1:
+			nor->read_dummy = 4;
+			break;
+		case 2:
+			nor->read_dummy = 5;
+			break;
+		case 3:
+			nor->read_dummy = 1;
+			break;
+		}
+
+	default:
 		return -EINVAL;
 	}
+
+	return 0;
+}
+
+static int spansion_set_quad_mode(struct spi_nor *nor)
+{
+	bool quad_enabled;
+	u8 latency_code;
+	int ret;
+
+	/*
+	 * The QUAD bit of Configuration Register must be set (CR Bit1=1) for
+	 * using any Quad SPI command.
+	 */
+	ret = spansion_get_config(nor, &quad_enabled, &latency_code);
+	if (ret)
+		return ret;
+
+	/* The Quad mode should be enabled ... */
+	if (!quad_enabled) {
+		/* ... if not try to enable it. */
+		dev_warn(nor->dev, "Spansion Quad mode disabled, enable it\n");
+		ret = spansion_quad_enable(nor);
+		if (ret)
+			return ret;
+	}
+
+	/*
+	 * Don't use the Fast Read Quad I/O (0xeb / 0xec) commands as their
+	 * number of dummy cycles can not be set to a multiple of 8: some SPI
+	 * controllers, especially those relying on the m25p80 driver, expect
+	 * the number of dummy cycles to be a multiple of 8.
+	 * Also when using a Fast Read Quad I/O command, the memory checks the
+	 * value of the first mode/dummy cycles to decice whether it enters or
+	 * leaves the Countinuous Read mode. We should never enter the
+	 * Countinuous Read mode as the spi-nor framework doesn't support it.
+	 * For all these reason, we'd rather use the Fast Read Quad Output
+	 * 1-1-4 (0x6b / 0x6c) commands instead.
+	 */
 	nor->read_proto = SNOR_PROTO_1_1_4;
 	nor->read_opcode = SPINOR_OP_READ_1_1_4;
-	nor->read_dummy = 8;
-	return 0;
+	return spansion_set_dummy_cycles(nor, latency_code);
 }
 
 static int spansion_set_dual_mode(struct spi_nor *nor)
 {
+	u8 latency_code;
+	int ret;
+
+	/* We don't care about the quad mode status */
+	ret = spansion_get_config(nor, NULL, &latency_code);
+	if (ret)
+		return ret;
+
+	/*
+	 * Don't use the Fast Read Dual I/O (0xbb / 0xbc) commands as their
+	 * number of dummy cycles can not bet set to a multiple of 8: some SPI
+	 * controllers, especially those relying on the m25p80 driver, expect
+	 * the number of dummy cycles to be a multiple of 8.
+	 * For this reason, w'd rather use the Fast Read Dual Output 1-1-2
+	 * (0x3b / 0x3c) commands instead.
+	 */
 	nor->read_proto = SNOR_PROTO_1_1_2;
 	nor->read_opcode = SPINOR_OP_READ_1_1_2;
-	nor->read_dummy = 8;
-	return 0;
+	return spansion_set_dummy_cycles(nor, latency_code);
 }
 
 static int spansion_set_single_mode(struct spi_nor *nor)
 {
-	u8 read_dummy;
-
-	switch (nor->read_opcode) {
-	case SPINOR_OP_READ:
-	case SPINOR_OP_READ4:
-		read_dummy = 0;
-		break;
+	u8 latency_code;
+	int ret;
 
-	default:
-		read_dummy = 8;
-		break;
-	}
+	/* We don't care about the quad mode status */
+	ret = spansion_get_config(nor, NULL, &latency_code);
+	if (ret)
+		return ret;
 
 	nor->read_proto = SNOR_PROTO_1_1_1;
-	nor->read_dummy = read_dummy;
-	return 0;
+	return spansion_set_dummy_cycles(nor, latency_code);
 }
 
 static int set_quad_mode(struct spi_nor *nor, const struct flash_info *info)
Signature · 1 line
1.8.2.2
#17 lore raw

PATCH v2 12/14 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.

However once their Quad mode enabled, Micron and Macronix spi-nor memories expect all commands to use the SPI 4-4-4 protocol.

Also, once their Dual mode enabled, Micron spi-nor memories expect all commands to use the SPI-2-2-2 protocol.

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

---
 drivers/mtd/devices/m25p80.c | 192 ++++++++++++++++++++++++++++++++++---------
 1 file changed, 151 insertions(+), 41 deletions(-)
drivers/mtd/devices/m25p80.c +151 -41
index c9c3b7fa3051..a57c7d0e3f39 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_CMD_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 void m25p80_write(struct spi_nor *nor, loff_t to, size_t len,
@@ -78,43 +150,54 @@ static void 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);
-
-	spi_message_init(&m);
+	int ret, cmd_sz = m25p_cmdsz(nor);
 
 	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) {
+		*retlen = 0;
+		return;
+	}
 
-	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);
 	spi_sync(spi, &m);
 
 	*retlen += m.actual_length - cmd_sz;
 }
 
-static inline unsigned int m25p80_rx_nbits(struct spi_nor *nor)
-{
-	switch (nor->flash_read) {
-	case SPI_NOR_DUAL:
-		return 2;
-	case SPI_NOR_QUAD:
-		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.
@@ -124,28 +207,55 @@ static int 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;
+	struct spi_transfer xfers[3];
+	struct spi_message m;
+	int ret;
+
+	/* Get transfer protocols. */
+	ret = m25p80_proto2nbits(nor->read_proto,
+				 &code_nbits, &addr_nbits, &data_nbits);
+	if (ret < 0) {
+		*retlen = 0;
+		return ret;
+	}
 
 	/* convert the dummy cycles to the number of bytes */
-	dummy /= 8;
+	dummy = (dummy * addr_nbits) / 8;
 
-	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 = len;
-	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);
 	spi_sync(spi, &m);
 
 	*retlen = m.actual_length - m25p_cmdsz(nor) - dummy;
Signature · 1 line
1.8.2.2
#18 lore raw

PATCH v2 13/14 Documentation: atmel-quadspi: add binding file for Atmel QSPI driver

In reply to Cyrille Pitchen

This patch documents the DT bindings for the driver of the Atmel QSPI controller embedded inside sama5d2x SoCs.

---
 .../devicetree/bindings/mtd/atmel-quadspi.txt      | 32 ++++++++++++++++++++++
 1 file changed, 32 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/mtd/atmel-quadspi.txt
Documentation/devicetree/bindings/mtd/atmel-quadspi.txt new file +32 -0
new file mode 100644
index 000000000000..489807005eda
@@ -0,0 +1,32 @@
+* Atmel Quad Serial Peripheral Interface (QSPI)
+
+Required properties:
+- compatible:     Should be "atmel,sama5d2-qspi".
+- reg:            Should contain the locations and lengths of the base registers
+                  and the mapped memory.
+- reg-names:      Should contain the resource reg names:
+                  - qspi_base: configuration register address space
+                  - qspi_mmap: memory mapped address space
+- interrupts:     Should contain the interrupt for the device.
+- clocks:         The phandle of the clock needed by the QSPI controller.
+- #address-cells: Should be <1>.
+- #size-cells:    Should be <0>.
+
+Example:
+
+spi at f0020000 {
+	compatible = "atmel,sama5d2-qspi";
+	reg = <0xf0020000 0x100>, <0xd0000000 0x8000000>;
+	reg-names = "qspi_base", "qspi_mmap";
+	interrupts = <52 IRQ_TYPE_LEVEL_HIGH 7>;
+	clocks = <&spi0_clk>;
+	#address-cells = <1>;
+	#size-cells = <0>;
+	pinctrl-names = "default";
+	pinctrl-0 = <&pinctrl_spi0_default>;
+	status = "okay";
+
+	m25p80 at 0 {
+		...
+	};
+};
Signature · 1 line
1.8.2.2
#19 lore raw

PATCH v2 14/14 mtd: atmel-quadspi: add driver for Atmel QSPI controller

In reply to Cyrille Pitchen

This driver add support to the new Atmel QSPI controller embedded into sama5d2x SoCs. It expects a NOR memory to be connected to the QSPI controller.

---
 drivers/mtd/spi-nor/Kconfig         |   9 +
 drivers/mtd/spi-nor/Makefile        |   1 +
 drivers/mtd/spi-nor/atmel-quadspi.c | 877 ++++++++++++++++++++++++++++++++++++
 3 files changed, 887 insertions(+)
 create mode 100644 drivers/mtd/spi-nor/atmel-quadspi.c
drivers/mtd/spi-nor/Kconfig +9 -0
index 0dc927540b3d..db768d3c5bf9 100644
@@ -37,6 +37,15 @@ config SPI_FSL_QUADSPI
 	  This controller does not support generic SPI. It only supports
 	  SPI NOR.
 
+config SPI_ATMEL_QUADSPI
+	tristate "Atmel Quad SPI Controller"
+	depends on ARCH_AT91 || (ARM && COMPILE_TEST)
+	depends on OF && HAS_DMA && HAS_IOMEM
+	help
+	  This enables support for the Quad SPI controller in master mode.
+	  This driver does not support generic SPI. The implementation only
+	  supports SPI NOR.
+
 config SPI_NXP_SPIFI
 	tristate "NXP SPI Flash Interface (SPIFI)"
 	depends on OF && (ARCH_LPC18XX || COMPILE_TEST)
drivers/mtd/spi-nor/Makefile +1 -0
index 0bf3a7f81675..1525913698ad 100644
@@ -2,3 +2,4 @@ obj-$(CONFIG_MTD_SPI_NOR)	+= spi-nor.o
 obj-$(CONFIG_SPI_FSL_QUADSPI)	+= fsl-quadspi.o
 obj-$(CONFIG_MTD_MT81xx_NOR)    += mtk-quadspi.o
 obj-$(CONFIG_SPI_NXP_SPIFI)	+= nxp-spifi.o
+obj-$(CONFIG_SPI_ATMEL_QUADSPI)	+= atmel-quadspi.o
drivers/mtd/spi-nor/atmel-quadspi.c new file +877 -0

877 lines not on this page · raw message

Signature · 1 line
1.8.2.2