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.pitc...@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

_______________________________________________
U-Boot mailing list
U-Boot@lists.denx.de
http://lists.denx.de/mailman/listinfo/u-boot

Reply via email to