
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@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);