[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