[U-Boot] [PATCH 12/18] sf: fix detection of QSPI memories when they boot in Quad or Dual mode

Cyrille Pitchen cyrille.pitchen at atmel.com
Tue Mar 15 19:12:34 CET 2016


The quad (or dual) mode of a SPI flash 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 flash 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 but not the JEDEC Read
ID Multiple I/O (0x9f) command.

Signed-off-by: Cyrille Pitchen <cyrille.pitchen at atmel.com>
---
 drivers/mtd/spi/sf_internal.h |  1 +
 drivers/mtd/spi/spi_flash.c   | 90 +++++++++++++++++++++++++++++++++----------
 include/spi_flash.h           | 45 ++++++++++++++++++++++
 3 files changed, 116 insertions(+), 20 deletions(-)

diff --git a/drivers/mtd/spi/sf_internal.h b/drivers/mtd/spi/sf_internal.h
index 8b8521369e4e..26d359707d5b 100644
--- a/drivers/mtd/spi/sf_internal.h
+++ b/drivers/mtd/spi/sf_internal.h
@@ -99,6 +99,7 @@ enum spi_nor_option_flags {
 #define CMD_READ_STATUS1		0x35
 #define CMD_READ_CONFIG			0x35
 #define CMD_FLAG_STATUS			0x70
+#define CMD_READ_ID_MIO			0xaf
 
 /* Bank addr access commands */
 #ifdef CONFIG_SPI_FLASH_BAR
diff --git a/drivers/mtd/spi/spi_flash.c b/drivers/mtd/spi/spi_flash.c
index 5ba148bd3626..5d641bbb8301 100644
--- a/drivers/mtd/spi/spi_flash.c
+++ b/drivers/mtd/spi/spi_flash.c
@@ -987,10 +987,37 @@ int spi_flash_decode_fdt(const void *blob, struct spi_flash *flash)
 }
 #endif /* CONFIG_IS_ENABLED(OF_CONTROL) */
 
+struct spi_flash_read_id_config {
+	enum spi_flash_protocol	proto;
+	u8			e_rd_cmd;
+	u8			cmd;
+	int			idlen;
+};
+
+static const struct spi_flash_read_id_config configs[] = {
+	/* Regular JEDEC Read ID (MUST be first, always tested) */
+	{SPI_FLASH_PROTO_1_1_1, (ARRAY_SLOW | ARRAY_FAST), CMD_READ_ID, 5},
+#if defined(CONFIG_SPI_FLASH_WINBOND)
+	/* Winbond QPI mode */
+	{SPI_FLASH_PROTO_4_4_4, QUAD_CMD_FAST, CMD_READ_ID, 5},
+#endif
+#if defined(CONFIG_SPI_FLASH_STMICRO) || defined(CONFIG_SPI_FLASH_MACRONIX)
+	/* Micron Quad mode & Macronix QPI mode */
+	{SPI_FLASH_PROTO_4_4_4, QUAD_CMD_FAST, CMD_READ_ID_MIO, 3},
+#endif
+#if defined(CONFIG_SPI_FLASH_STMICRO)
+	/* Micron Dual mode */
+	{SPI_FLASH_PROTO_2_2_2, DUAL_CMD_FAST, CMD_READ_ID_MIO, 3},
+#endif
+	/* Sentinel */
+	{SPI_FLASH_PROTO_1_1_1, 0, 0, 0},
+};
+
 int spi_flash_scan(struct spi_flash *flash, u8 e_rd_cmd)
 {
 	struct spi_slave *spi = flash->spi;
 	const struct spi_flash_params *params;
+	const struct spi_flash_read_id_config *cfg;
 	u16 jedec, ext_jedec;
 	u8 cmd, idcode[5];
 	int ret;
@@ -1014,32 +1041,55 @@ int spi_flash_scan(struct spi_flash *flash, u8 e_rd_cmd)
 	flash->read = spi_flash_cmd_read_ops;
 #endif
 
-	/* Read the ID codes */
-	ret = spi_flash_read_reg(flash, CMD_READ_ID, sizeof(idcode), idcode);
-	if (ret) {
-		printf("SF: Failed to get idcodes\n");
-		return ret;
-	}
+	/*
+	 * Check whether the SPI NOR memory has already been configured (at
+	 * reset of by some bootloader) to use a protocol other than SPI 1-1-1.
+	 */
+	params = NULL;
+	jedec = 0;
+	ext_jedec = 0;
+	for (cfg = configs; cfg->e_rd_cmd; ++cfg) {
+		/* Only try protocols supported by the SPI controller */
+		if (cfg != configs && !(e_rd_cmd & cfg->e_rd_cmd))
+			continue;
+
+		/* Set this protocol for all commands */
+		flash->reg_proto = cfg->proto;
+		flash->read_proto = cfg->proto;
+		flash->write_proto = cfg->proto;
+		flash->erase_proto = cfg->proto;
+
+		/* Read the ID codes */
+		memset(idcode, 0, sizeof(idcode));
+		ret = spi_flash_read_reg(flash, cfg->cmd, cfg->idlen, idcode);
+		if (ret) {
+			printf("SF: Failed to get idcodes\n");
+			return -EINVAL;
+		}
 
 #ifdef DEBUG
-	printf("SF: Got idcodes\n");
-	print_buffer(0, idcode, 1, sizeof(idcode), 0);
+		printf("SF: Got idcodes\n");
+		print_buffer(0, idcode, 1, sizeof(idcode), 0);
 #endif
 
-	jedec = idcode[1] << 8 | idcode[2];
-	ext_jedec = idcode[3] << 8 | idcode[4];
-
-	/* Validate params from spi_flash_params table */
-	params = spi_flash_params_table;
-	for (; params->name != NULL; params++) {
-		if ((params->jedec >> 16) == idcode[0]) {
-			if ((params->jedec & 0xFFFF) == jedec) {
-				if (params->ext_jedec == 0)
-					break;
-				else if (params->ext_jedec == ext_jedec)
-					break;
+		jedec = idcode[1] << 8 | idcode[2];
+		ext_jedec = idcode[3] << 8 | idcode[4];
+
+		/* Validate params from spi_flash_params table */
+		params = spi_flash_params_table;
+		for (; params->name != NULL; params++) {
+			if ((params->jedec >> 16) == idcode[0]) {
+				if ((params->jedec & 0xFFFF) == jedec) {
+					if (params->ext_jedec == 0)
+						break;
+					else if (params->ext_jedec == ext_jedec)
+						break;
+				}
 			}
 		}
+
+		if (params->name)
+			break;
 	}
 
 	if (!params->name) {
diff --git a/include/spi_flash.h b/include/spi_flash.h
index 31d11e55571d..945cc07ee8b2 100644
--- a/include/spi_flash.h
+++ b/include/spi_flash.h
@@ -28,6 +28,42 @@
 
 struct spi_slave;
 
+#define SPI_FLASH_PROTO_CMD_OFF	8
+#define SPI_FLASH_PROTO_CMD_MASK	GENMASK(11, 8)
+#define SPI_FLASH_PROTO_CMD_TO_PROTO(cmd) \
+	(((cmd) << SPI_FLASH_PROTO_CMD_OFF) & SPI_FLASH_PROTO_CMD_MASK)
+#define SPI_FLASH_PROTO_CMD_FROM_PROTO(proto) \
+	((((u32)(proto)) & SPI_FLASH_PROTO_CMD_MASK) >> SPI_FLASH_PROTO_CMD_OFF)
+
+#define SPI_FLASH_PROTO_ADR_OFF	4
+#define SPI_FLASH_PROTO_ADR_MASK	GENMASK(7, 4)
+#define SPI_FLASH_PROTO_ADR_TO_PROTO(adr) \
+	(((adr) << SPI_FLASH_PROTO_ADR_OFF) & SPI_FLASH_PROTO_ADR_MASK)
+#define SPI_FLASH_PROTO_ADR_FROM_PROTO(proto) \
+	((((u32)(proto)) & SPI_FLASH_PROTO_ADR_MASK) >> SPI_FLASH_PROTO_ADR_OFF)
+
+#define SPI_FLASH_PROTO_DAT_OFF	0
+#define SPI_FLASH_PROTO_DAT_MASK	GENMASK(3, 0)
+#define SPI_FLASH_PROTO_DAT_TO_PROTO(dat) \
+	(((dat) << SPI_FLASH_PROTO_DAT_OFF) & SPI_FLASH_PROTO_DAT_MASK)
+#define SPI_FLASH_PROTO_DAT_FROM_PROTO(proto) \
+	((((u32)(proto)) & SPI_FLASH_PROTO_DAT_MASK) >> SPI_FLASH_PROTO_DAT_OFF)
+
+#define SPI_FLASH_PROTO(cmd, adr, dat)	     \
+	(SPI_FLASH_PROTO_CMD_TO_PROTO(cmd) | \
+	 SPI_FLASH_PROTO_ADR_TO_PROTO(adr) | \
+	 SPI_FLASH_PROTO_DAT_TO_PROTO(dat))
+
+enum spi_flash_protocol {
+	SPI_FLASH_PROTO_1_1_1 = SPI_FLASH_PROTO(1, 1, 1), /* SPI */
+	SPI_FLASH_PROTO_1_1_2 = SPI_FLASH_PROTO(1, 1, 2), /* Dual Output */
+	SPI_FLASH_PROTO_1_1_4 = SPI_FLASH_PROTO(1, 1, 4), /* Quad Output */
+	SPI_FLASH_PROTO_1_2_2 = SPI_FLASH_PROTO(1, 2, 2), /* Dual IO */
+	SPI_FLASH_PROTO_1_4_4 = SPI_FLASH_PROTO(1, 4, 4), /* Quad IO */
+	SPI_FLASH_PROTO_2_2_2 = SPI_FLASH_PROTO(2, 2, 2), /* Dual Command */
+	SPI_FLASH_PROTO_4_4_4 = SPI_FLASH_PROTO(4, 4, 4), /* Quad Command */
+};
+
 /**
  * struct spi_flash - SPI flash structure
  *
@@ -48,6 +84,10 @@ struct spi_slave;
  * @read_cmd:		Read cmd - Array Fast, Extn read and quad read.
  * @write_cmd:		Write cmd - page and quad program.
  * @dummy_byte:		Dummy cycles for read operation.
+ * @reg_proto		SPI protocol to be used by &read_reg and &write_reg ops
+ * @read_proto		SPI protocol to be used by &read ops
+ * @write_proto		SPI protocol to be used by &write ops
+ * @erase_proto		SPI protocol to be used by &erase ops
  * @memory_map:		Address of read-only SPI flash access
  * @flash_lock:		lock a region of the SPI Flash
  * @flash_unlock:	unlock a region of the SPI Flash
@@ -87,6 +127,11 @@ struct spi_flash {
 	u8 write_cmd;
 	u8 dummy_byte;
 
+	enum spi_flash_protocol reg_proto;
+	enum spi_flash_protocol read_proto;
+	enum spi_flash_protocol write_proto;
+	enum spi_flash_protocol erase_proto;
+
 	void *memory_map;
 
 	int (*flash_lock)(struct spi_flash *flash, u32 ofs, size_t len);
-- 
1.8.2.2



More information about the U-Boot mailing list