[U-Boot] [PATCH 00/18] sf: fix support of QSPI memories and controllers

Hi all,
This series of patches fixes and extend the support of QSPI memories in the SPI flash framework. The updates are split into many parts to make it easier to understand and review but they should be considered as a whole.
This was tested on a Atmel sama5d2 xplained board with a Micron n25q128a memory.
Best regards,
Cyrille
Cyrille Pitchen (18): Revert "sf: Fix quad bit set for micron devices" sf: call spi_claim_bus() and spi_release_bus() only once per read, write or erase sf: replace spi_flash_read_common() calls by spi_flash_cmd_read() sf: remove spi_flash_write_common() sf: export spi_flash_wait_ready() function sf: share erase generic algorithm sf: share write generic algorithm sf: share read generic algorithm sf: add hooks to handle register read and write operations sf: move support of SST flash into generic spi_flash_write_alg() sf: fix selection of supported READ commands for QSPI memories sf: fix detection of QSPI memories when they boot in Quad or Dual mode sf: add helper function to set the number of dummy bytes sf: add 4byte address opcodes sf: prepare next fixes to support of QSPI memories by manufacturer sf: fix support of Micron memories ARM: at91: clock: add function to get QSPI clocks sf: add driver for Atmel QSPI controller
arch/arm/mach-at91/include/mach/clk.h | 5 + drivers/mtd/spi/Kconfig | 18 +- drivers/mtd/spi/Makefile | 2 + drivers/mtd/spi/atmel_qspi_flash.c | 432 ++++++++++++++++++++ drivers/mtd/spi/sf-uclass.c | 11 + drivers/mtd/spi/sf_internal.h | 115 ++++-- drivers/mtd/spi/sf_micron.c | 222 ++++++++++ drivers/mtd/spi/sf_params.c | 32 +- drivers/mtd/spi/sf_probe.c | 46 ++- drivers/mtd/spi/spi_flash.c | 744 +++++++++++++++++++++------------- drivers/spi/Kconfig | 9 + drivers/spi/Makefile | 1 + drivers/spi/atmel_qspi.c | 150 +++++++ drivers/spi/atmel_qspi.h | 145 +++++++ include/spi_flash.h | 105 +++++ 15 files changed, 1701 insertions(+), 336 deletions(-) create mode 100644 drivers/mtd/spi/atmel_qspi_flash.c create mode 100644 drivers/mtd/spi/sf_micron.c create mode 100644 drivers/spi/atmel_qspi.c create mode 100644 drivers/spi/atmel_qspi.h

This reverts commit c56ae7519f141523ba1248b22b5b5169b21772fe.
Once the 'Quad Enable' bit is cleared in their Enhanced Volatile Configuration Register (EVCR), Micron memories expect ALL commands to use the SPI 4-4-4 protocol. Commands using SPI 1-y-z protocols are no longer accepted.
Within the reverted commit, the write_evcr() function is implemented using the spi_flash_write_common(), which is a shortcut for the [ spi_flash_cmd_write_enable(), spi_flash_cmd_write(), spi_flash_cmd_wait_ready() ] sequence.
Since the internal state of the Micron memory has been changed when the spi_flash_cmd_write() function completes, the later call of the spi_flash_cmd_wait_ready() function fails.
Indeed the SPI controller driver is not aware of the SPI protocol switch.
Further patches will fix the support of Micron QSPI memories.
Signed-off-by: Cyrille Pitchen cyrille.pitchen@atmel.com --- drivers/mtd/spi/sf_internal.h | 3 --- drivers/mtd/spi/spi_flash.c | 62 ++----------------------------------------- 2 files changed, 2 insertions(+), 63 deletions(-)
diff --git a/drivers/mtd/spi/sf_internal.h b/drivers/mtd/spi/sf_internal.h index 007a5a085caf..7418c76b272c 100644 --- a/drivers/mtd/spi/sf_internal.h +++ b/drivers/mtd/spi/sf_internal.h @@ -77,7 +77,6 @@ enum spi_nor_option_flags { #define CMD_WRITE_DISABLE 0x04 #define CMD_WRITE_ENABLE 0x06 #define CMD_QUAD_PAGE_PROGRAM 0x32 -#define CMD_WRITE_EVCR 0x61
/* Read commands */ #define CMD_READ_ARRAY_SLOW 0x03 @@ -91,7 +90,6 @@ enum spi_nor_option_flags { #define CMD_READ_STATUS1 0x35 #define CMD_READ_CONFIG 0x35 #define CMD_FLAG_STATUS 0x70 -#define CMD_READ_EVCR 0x65
/* Bank addr access commands */ #ifdef CONFIG_SPI_FLASH_BAR @@ -106,7 +104,6 @@ enum spi_nor_option_flags { #define STATUS_QEB_WINSPAN BIT(1) #define STATUS_QEB_MXIC BIT(6) #define STATUS_PEC BIT(7) -#define STATUS_QEB_MICRON BIT(7) #define SR_BP0 BIT(2) /* Block protect 0 */ #define SR_BP1 BIT(3) /* Block protect 1 */ #define SR_BP2 BIT(4) /* Block protect 2 */ diff --git a/drivers/mtd/spi/spi_flash.c b/drivers/mtd/spi/spi_flash.c index 44d9e9ba0105..e52728da9a09 100644 --- a/drivers/mtd/spi/spi_flash.c +++ b/drivers/mtd/spi/spi_flash.c @@ -112,37 +112,6 @@ static int write_cr(struct spi_flash *flash, u8 wc) } #endif
-#ifdef CONFIG_SPI_FLASH_STMICRO -static int read_evcr(struct spi_flash *flash, u8 *evcr) -{ - int ret; - const u8 cmd = CMD_READ_EVCR; - - ret = spi_flash_read_common(flash, &cmd, 1, evcr, 1); - if (ret < 0) { - debug("SF: error reading EVCR\n"); - return ret; - } - - return 0; -} - -static int write_evcr(struct spi_flash *flash, u8 evcr) -{ - u8 cmd; - int ret; - - cmd = CMD_WRITE_EVCR; - ret = spi_flash_write_common(flash, &cmd, 1, &evcr, 1); - if (ret < 0) { - debug("SF: error while writing EVCR register\n"); - return ret; - } - - return 0; -} -#endif - #ifdef CONFIG_SPI_FLASH_BAR static int spi_flash_write_bar(struct spi_flash *flash, u32 offset) { @@ -895,34 +864,6 @@ static int spansion_quad_enable(struct spi_flash *flash) } #endif
-#ifdef CONFIG_SPI_FLASH_STMICRO -static int micron_quad_enable(struct spi_flash *flash) -{ - u8 qeb_status; - int ret; - - ret = read_evcr(flash, &qeb_status); - if (ret < 0) - return ret; - - if (!(qeb_status & STATUS_QEB_MICRON)) - return 0; - - ret = write_evcr(flash, qeb_status & ~STATUS_QEB_MICRON); - if (ret < 0) - return ret; - - /* read EVCR and check it */ - ret = read_evcr(flash, &qeb_status); - if (!(ret >= 0 && !(qeb_status & STATUS_QEB_MICRON))) { - printf("SF: Micron EVCR Quad bit not clear\n"); - return -EINVAL; - } - - return ret; -} -#endif - static int set_quad_mode(struct spi_flash *flash, u8 idcode0) { switch (idcode0) { @@ -937,7 +878,8 @@ static int set_quad_mode(struct spi_flash *flash, u8 idcode0) #endif #ifdef CONFIG_SPI_FLASH_STMICRO case SPI_FLASH_CFI_MFR_STMICRO: - return micron_quad_enable(flash); + debug("SF: QEB is volatile for %02x flash\n", idcode0); + return 0; #endif default: printf("SF: Need set QEB func for %02x flash\n", idcode0);

There is no need to claim/release the SPI bus for each command sent to the memory when performing read, write or erase operations. Now we claim the SPI bus once for all at the very beginning of these operations then we release it just before exiting.
Signed-off-by: Cyrille Pitchen cyrille.pitchen@atmel.com --- drivers/mtd/spi/sf_internal.h | 5 +--- drivers/mtd/spi/spi_flash.c | 68 +++++++++++++++++++++++++------------------ 2 files changed, 40 insertions(+), 33 deletions(-)
diff --git a/drivers/mtd/spi/sf_internal.h b/drivers/mtd/spi/sf_internal.h index 7418c76b272c..dd8152721fe1 100644 --- a/drivers/mtd/spi/sf_internal.h +++ b/drivers/mtd/spi/sf_internal.h @@ -192,11 +192,9 @@ static inline int spi_flash_cmd_write_disable(struct spi_flash *flash)
/* * Used for spi_flash write operation - * - SPI claim * - spi_flash_cmd_write_enable * - spi_flash_cmd_write * - spi_flash_cmd_wait_ready - * - SPI release */ int spi_flash_write_common(struct spi_flash *flash, const u8 *cmd, size_t cmd_len, const void *buf, size_t buf_len); @@ -210,8 +208,7 @@ int spi_flash_cmd_write_ops(struct spi_flash *flash, u32 offset, size_t len, const void *buf);
/* - * Same as spi_flash_cmd_read() except it also claims/releases the SPI - * bus. Used as common part of the ->read() operation. + * Same as spi_flash_cmd_read(). Used as common part of the ->read() operation. */ int spi_flash_read_common(struct spi_flash *flash, const u8 *cmd, size_t cmd_len, void *data, size_t data_len); diff --git a/drivers/mtd/spi/spi_flash.c b/drivers/mtd/spi/spi_flash.c index e52728da9a09..740ecd44473a 100644 --- a/drivers/mtd/spi/spi_flash.c +++ b/drivers/mtd/spi/spi_flash.c @@ -261,12 +261,6 @@ int spi_flash_write_common(struct spi_flash *flash, const u8 *cmd, if (buf == NULL) timeout = SPI_FLASH_PAGE_ERASE_TIMEOUT;
- ret = spi_claim_bus(spi); - if (ret) { - debug("SF: unable to claim SPI bus\n"); - return ret; - } - ret = spi_flash_cmd_write_enable(flash); if (ret < 0) { debug("SF: enabling write failed\n"); @@ -287,13 +281,12 @@ int spi_flash_write_common(struct spi_flash *flash, const u8 *cmd, return ret; }
- spi_release_bus(spi); - return ret; }
int spi_flash_cmd_erase_ops(struct spi_flash *flash, u32 offset, size_t len) { + struct spi_slave *spi = flash->spi; u32 erase_size, erase_addr; u8 cmd[SPI_FLASH_CMD_LEN]; int ret = -1; @@ -304,11 +297,18 @@ int spi_flash_cmd_erase_ops(struct spi_flash *flash, u32 offset, size_t len) return -1; }
+ ret = spi_claim_bus(spi); + if (ret) { + debug("SF: unable to claim SPI bus\n"); + return ret; + } + if (flash->flash_is_locked) { if (flash->flash_is_locked(flash, offset, len) > 0) { printf("offset 0x%x is protected and cannot be erased\n", offset); - return -EINVAL; + ret = -EINVAL; + goto release; } }
@@ -323,7 +323,7 @@ int spi_flash_cmd_erase_ops(struct spi_flash *flash, u32 offset, size_t len) #ifdef CONFIG_SPI_FLASH_BAR ret = spi_flash_write_bar(flash, erase_addr); if (ret < 0) - return ret; + break; #endif spi_flash_addr(erase_addr, cmd);
@@ -340,6 +340,9 @@ int spi_flash_cmd_erase_ops(struct spi_flash *flash, u32 offset, size_t len) len -= erase_size; }
+release: + spi_release_bus(spi); + return ret; }
@@ -355,11 +358,18 @@ int spi_flash_cmd_write_ops(struct spi_flash *flash, u32 offset,
page_size = flash->page_size;
+ ret = spi_claim_bus(spi); + if (ret) { + debug("SF: unable to claim SPI bus\n"); + return ret; + } + if (flash->flash_is_locked) { if (flash->flash_is_locked(flash, offset, len) > 0) { printf("offset 0x%x is protected and cannot be written\n", offset); - return -EINVAL; + ret = -EINVAL; + goto release; } }
@@ -374,7 +384,7 @@ int spi_flash_cmd_write_ops(struct spi_flash *flash, u32 offset, #ifdef CONFIG_SPI_FLASH_BAR ret = spi_flash_write_bar(flash, write_addr); if (ret < 0) - return ret; + break; #endif byte_addr = offset % page_size; chunk_len = min(len - actual, (size_t)(page_size - byte_addr)); @@ -398,6 +408,9 @@ int spi_flash_cmd_write_ops(struct spi_flash *flash, u32 offset, offset += chunk_len; }
+release: + spi_release_bus(spi); + return ret; }
@@ -407,20 +420,12 @@ int spi_flash_read_common(struct spi_flash *flash, const u8 *cmd, struct spi_slave *spi = flash->spi; int ret;
- ret = spi_claim_bus(spi); - if (ret) { - debug("SF: unable to claim SPI bus\n"); - return ret; - } - ret = spi_flash_cmd_read(spi, cmd, cmd_len, data, data_len); if (ret < 0) { debug("SF: read cmd failed\n"); return ret; }
- spi_release_bus(spi); - return ret; }
@@ -446,25 +451,26 @@ int spi_flash_cmd_read_ops(struct spi_flash *flash, u32 offset, int bank_sel = 0; int ret = -1;
+ ret = spi_claim_bus(spi); + if (ret) { + debug("SF: unable to claim SPI bus\n"); + return ret; + } + /* Handle memory-mapped SPI */ if (flash->memory_map) { - ret = spi_claim_bus(spi); - if (ret) { - debug("SF: unable to claim SPI bus\n"); - return ret; - } spi_xfer(spi, 0, NULL, NULL, SPI_XFER_MMAP); spi_flash_copy_mmap(data, flash->memory_map + offset, len); spi_xfer(spi, 0, NULL, NULL, SPI_XFER_MMAP_END); - spi_release_bus(spi); - return 0; + goto release; }
cmdsz = SPI_FLASH_CMD_LEN + flash->dummy_byte; cmd = calloc(1, cmdsz); if (!cmd) { debug("SF: Failed to allocate cmd\n"); - return -ENOMEM; + ret = -ENOMEM; + goto release; }
cmd[0] = flash->read_cmd; @@ -478,7 +484,7 @@ int spi_flash_cmd_read_ops(struct spi_flash *flash, u32 offset, #ifdef CONFIG_SPI_FLASH_BAR ret = spi_flash_write_bar(flash, read_addr); if (ret < 0) - return ret; + break; bank_sel = flash->bank_curr; #endif remain_len = ((SPI_FLASH_16MB_BOUN << flash->shift) * @@ -502,6 +508,10 @@ int spi_flash_cmd_read_ops(struct spi_flash *flash, u32 offset, }
free(cmd); + +release: + spi_release_bus(spi); + return ret; }

Since spi_claim_bus() and spi_release_bus() are now called outside spi_flash_read_common(), this latest function is just a useless alias for spi_flash_cmd_read(). So we remove it.
Signed-off-by: Cyrille Pitchen cyrille.pitchen@atmel.com --- drivers/mtd/spi/sf_internal.h | 6 ------ drivers/mtd/spi/spi_flash.c | 30 +++++++++--------------------- 2 files changed, 9 insertions(+), 27 deletions(-)
diff --git a/drivers/mtd/spi/sf_internal.h b/drivers/mtd/spi/sf_internal.h index dd8152721fe1..96c9255cb03d 100644 --- a/drivers/mtd/spi/sf_internal.h +++ b/drivers/mtd/spi/sf_internal.h @@ -207,12 +207,6 @@ int spi_flash_write_common(struct spi_flash *flash, const u8 *cmd, int spi_flash_cmd_write_ops(struct spi_flash *flash, u32 offset, size_t len, const void *buf);
-/* - * Same as spi_flash_cmd_read(). Used as common part of the ->read() operation. - */ -int spi_flash_read_common(struct spi_flash *flash, const u8 *cmd, - size_t cmd_len, void *data, size_t data_len); - /* Flash read operation, support all possible read commands */ int spi_flash_cmd_read_ops(struct spi_flash *flash, u32 offset, size_t len, void *data); diff --git a/drivers/mtd/spi/spi_flash.c b/drivers/mtd/spi/spi_flash.c index 740ecd44473a..b1ebc65f37d7 100644 --- a/drivers/mtd/spi/spi_flash.c +++ b/drivers/mtd/spi/spi_flash.c @@ -32,11 +32,12 @@ static void spi_flash_addr(u32 addr, u8 *cmd)
static int read_sr(struct spi_flash *flash, u8 *rs) { + struct spi_slave *spi = flash->spi; int ret; u8 cmd;
cmd = CMD_READ_STATUS; - ret = spi_flash_read_common(flash, &cmd, 1, rs, 1); + ret = spi_flash_cmd_read(spi, &cmd, 1, rs, 1); if (ret < 0) { debug("SF: fail to read status register\n"); return ret; @@ -47,10 +48,11 @@ static int read_sr(struct spi_flash *flash, u8 *rs)
static int read_fsr(struct spi_flash *flash, u8 *fsr) { + struct spi_slave *spi = flash->spi; int ret; const u8 cmd = CMD_FLAG_STATUS;
- ret = spi_flash_read_common(flash, &cmd, 1, fsr, 1); + ret = spi_flash_cmd_read(spi, &cmd, 1, fsr, 1); if (ret < 0) { debug("SF: fail to read flag status register\n"); return ret; @@ -77,11 +79,12 @@ static int write_sr(struct spi_flash *flash, u8 ws) #if defined(CONFIG_SPI_FLASH_SPANSION) || defined(CONFIG_SPI_FLASH_WINBOND) static int read_cr(struct spi_flash *flash, u8 *rc) { + struct spi_slave *spi = flash->spi; int ret; u8 cmd;
cmd = CMD_READ_CONFIG; - ret = spi_flash_read_common(flash, &cmd, 1, rc, 1); + ret = spi_flash_cmd_read(spi, &cmd, 1, rc, 1); if (ret < 0) { debug("SF: fail to read config register\n"); return ret; @@ -136,6 +139,7 @@ bar_end:
static int spi_flash_read_bar(struct spi_flash *flash, u8 idcode0) { + struct spi_slave *spi = flash->spi; u8 curr_bank = 0; int ret;
@@ -152,8 +156,7 @@ static int spi_flash_read_bar(struct spi_flash *flash, u8 idcode0) flash->bank_write_cmd = CMD_EXTNADDR_WREAR; }
- ret = spi_flash_read_common(flash, &flash->bank_read_cmd, 1, - &curr_bank, 1); + ret = spi_flash_cmd_read(spi, &flash->bank_read_cmd, 1, &curr_bank, 1); if (ret) { debug("SF: fail to read bank addr register\n"); return ret; @@ -414,21 +417,6 @@ release: return ret; }
-int spi_flash_read_common(struct spi_flash *flash, const u8 *cmd, - size_t cmd_len, void *data, size_t data_len) -{ - struct spi_slave *spi = flash->spi; - int ret; - - ret = spi_flash_cmd_read(spi, cmd, cmd_len, data, data_len); - if (ret < 0) { - debug("SF: read cmd failed\n"); - return ret; - } - - return ret; -} - /* * TODO: remove the weak after all the other spi_flash_copy_mmap * implementations removed from drivers @@ -496,7 +484,7 @@ int spi_flash_cmd_read_ops(struct spi_flash *flash, u32 offset,
spi_flash_addr(read_addr, cmd);
- ret = spi_flash_read_common(flash, cmd, cmdsz, data, read_len); + ret = spi_flash_cmd_read(spi, cmd, cmdsz, data, read_len); if (ret < 0) { debug("SF: read failed\n"); break;

This patch prepares the split of memory versus internal registers SPI commands.
Signed-off-by: Cyrille Pitchen cyrille.pitchen@atmel.com --- drivers/mtd/spi/sf_internal.h | 8 +++--- drivers/mtd/spi/spi_flash.c | 62 ++++++++++++++++++++++++++----------------- 2 files changed, 42 insertions(+), 28 deletions(-)
diff --git a/drivers/mtd/spi/sf_internal.h b/drivers/mtd/spi/sf_internal.h index 96c9255cb03d..25fc5a112d92 100644 --- a/drivers/mtd/spi/sf_internal.h +++ b/drivers/mtd/spi/sf_internal.h @@ -191,13 +191,13 @@ static inline int spi_flash_cmd_write_disable(struct spi_flash *flash) }
/* - * Used for spi_flash write operation + * Used for spi_flash register update operation * - spi_flash_cmd_write_enable - * - spi_flash_cmd_write + * - write new register value * - spi_flash_cmd_wait_ready */ -int spi_flash_write_common(struct spi_flash *flash, const u8 *cmd, - size_t cmd_len, const void *buf, size_t buf_len); +int spi_flash_update_reg(struct spi_flash *flash, u8 opcode, size_t len, + const void *buf);
/* * Flash write operation, support all possible write commands. diff --git a/drivers/mtd/spi/spi_flash.c b/drivers/mtd/spi/spi_flash.c index b1ebc65f37d7..e43e74fd95cd 100644 --- a/drivers/mtd/spi/spi_flash.c +++ b/drivers/mtd/spi/spi_flash.c @@ -63,11 +63,9 @@ static int read_fsr(struct spi_flash *flash, u8 *fsr)
static int write_sr(struct spi_flash *flash, u8 ws) { - u8 cmd; int ret;
- cmd = CMD_WRITE_STATUS; - ret = spi_flash_write_common(flash, &cmd, 1, &ws, 1); + ret = spi_flash_update_reg(flash, CMD_WRITE_STATUS, 1, &ws); if (ret < 0) { debug("SF: fail to write status register\n"); return ret; @@ -96,16 +94,14 @@ static int read_cr(struct spi_flash *flash, u8 *rc) static int write_cr(struct spi_flash *flash, u8 wc) { u8 data[2]; - u8 cmd; int ret;
ret = read_sr(flash, &data[0]); if (ret < 0) return ret;
- cmd = CMD_WRITE_STATUS; data[1] = wc; - ret = spi_flash_write_common(flash, &cmd, 1, &data, 2); + ret = spi_flash_update_reg(flash, CMD_WRITE_STATUS, 2, data); if (ret) { debug("SF: fail to write config register\n"); return ret; @@ -118,15 +114,14 @@ static int write_cr(struct spi_flash *flash, u8 wc) #ifdef CONFIG_SPI_FLASH_BAR static int spi_flash_write_bar(struct spi_flash *flash, u32 offset) { - u8 cmd, bank_sel; + u8 bank_sel; int ret;
bank_sel = offset / (SPI_FLASH_16MB_BOUN << flash->shift); if (bank_sel == flash->bank_curr) goto bar_end;
- cmd = flash->bank_write_cmd; - ret = spi_flash_write_common(flash, &cmd, 1, &bank_sel, 1); + ret = spi_flash_update_reg(flash, flash->bank_write_cmd, 1, &bank_sel); if (ret < 0) { debug("SF: fail to write bank register\n"); return ret; @@ -254,37 +249,31 @@ static int spi_flash_cmd_wait_ready(struct spi_flash *flash, return -ETIMEDOUT; }
-int spi_flash_write_common(struct spi_flash *flash, const u8 *cmd, - size_t cmd_len, const void *buf, size_t buf_len) +int spi_flash_update_reg(struct spi_flash *flash, u8 opcode, + size_t len, const void *buf) { struct spi_slave *spi = flash->spi; - unsigned long timeout = SPI_FLASH_PROG_TIMEOUT; int ret;
- if (buf == NULL) - timeout = SPI_FLASH_PAGE_ERASE_TIMEOUT; - ret = spi_flash_cmd_write_enable(flash); if (ret < 0) { debug("SF: enabling write failed\n"); return ret; }
- ret = spi_flash_cmd_write(spi, cmd, cmd_len, buf, buf_len); + ret = spi_flash_cmd_write(spi, &opcode, 1, buf, len); if (ret < 0) { debug("SF: write cmd failed\n"); return ret; }
- ret = spi_flash_cmd_wait_ready(flash, timeout); + ret = spi_flash_cmd_wait_ready(flash, SPI_FLASH_PROG_TIMEOUT); if (ret < 0) { - debug("SF: write %s timed out\n", - timeout == SPI_FLASH_PROG_TIMEOUT ? - "program" : "page erase"); + debug("SF: write register timed out\n"); return ret; }
- return ret; + return 0; }
int spi_flash_cmd_erase_ops(struct spi_flash *flash, u32 offset, size_t len) @@ -328,17 +317,30 @@ int spi_flash_cmd_erase_ops(struct spi_flash *flash, u32 offset, size_t len) if (ret < 0) break; #endif + ret = spi_flash_cmd_write_enable(flash); + if (ret < 0) { + debug("SF: enabling write failed\n"); + break; + } + spi_flash_addr(erase_addr, cmd);
debug("SF: erase %2x %2x %2x %2x (%x)\n", cmd[0], cmd[1], cmd[2], cmd[3], erase_addr);
- ret = spi_flash_write_common(flash, cmd, sizeof(cmd), NULL, 0); + ret = spi_flash_cmd_write(spi, cmd, sizeof(cmd), NULL, 0); if (ret < 0) { debug("SF: erase failed\n"); break; }
+ ret = spi_flash_cmd_wait_ready(flash, + SPI_FLASH_PAGE_ERASE_TIMEOUT); + if (ret < 0) { + debug("SF: write page erase timed out\n"); + break; + } + offset += erase_size; len -= erase_size; } @@ -396,18 +398,30 @@ int spi_flash_cmd_write_ops(struct spi_flash *flash, u32 offset, chunk_len = min(chunk_len, (size_t)spi->max_write_size);
+ ret = spi_flash_cmd_write_enable(flash); + if (ret < 0) { + debug("SF: enabling write failed\n"); + break; + } + spi_flash_addr(write_addr, cmd);
debug("SF: 0x%p => cmd = { 0x%02x 0x%02x%02x%02x } chunk_len = %zu\n", buf + actual, cmd[0], cmd[1], cmd[2], cmd[3], chunk_len);
- ret = spi_flash_write_common(flash, cmd, sizeof(cmd), - buf + actual, chunk_len); + ret = spi_flash_cmd_write(spi, cmd, sizeof(cmd), + buf + actual, chunk_len); if (ret < 0) { debug("SF: write failed\n"); break; }
+ ret = spi_flash_cmd_wait_ready(flash, SPI_FLASH_PROG_TIMEOUT); + if (ret < 0) { + debug("SF: write program timed out\n"); + break; + } + offset += chunk_len; }

This patch exports a new function so future drivers can use it.
Signed-off-by: Cyrille Pitchen cyrille.pitchen@atmel.com --- drivers/mtd/spi/sf_internal.h | 3 +++ drivers/mtd/spi/spi_flash.c | 7 ++++++- 2 files changed, 9 insertions(+), 1 deletion(-)
diff --git a/drivers/mtd/spi/sf_internal.h b/drivers/mtd/spi/sf_internal.h index 25fc5a112d92..e2a4449dbf40 100644 --- a/drivers/mtd/spi/sf_internal.h +++ b/drivers/mtd/spi/sf_internal.h @@ -190,6 +190,9 @@ static inline int spi_flash_cmd_write_disable(struct spi_flash *flash) return spi_flash_cmd(flash->spi, CMD_WRITE_DISABLE, NULL, 0); }
+/* Wait for Busy/Write in Progress flag to be cleared */ +int spi_flash_wait_ready(struct spi_flash *flash); + /* * Used for spi_flash register update operation * - spi_flash_cmd_write_enable diff --git a/drivers/mtd/spi/spi_flash.c b/drivers/mtd/spi/spi_flash.c index e43e74fd95cd..6f30ff6f8d7c 100644 --- a/drivers/mtd/spi/spi_flash.c +++ b/drivers/mtd/spi/spi_flash.c @@ -249,6 +249,11 @@ static int spi_flash_cmd_wait_ready(struct spi_flash *flash, return -ETIMEDOUT; }
+int spi_flash_wait_ready(struct spi_flash *flash) +{ + return spi_flash_cmd_wait_ready(flash, SPI_FLASH_PROG_TIMEOUT); +} + int spi_flash_update_reg(struct spi_flash *flash, u8 opcode, size_t len, const void *buf) { @@ -267,7 +272,7 @@ int spi_flash_update_reg(struct spi_flash *flash, u8 opcode, return ret; }
- ret = spi_flash_cmd_wait_ready(flash, SPI_FLASH_PROG_TIMEOUT); + ret = spi_flash_wait_ready(flash); if (ret < 0) { debug("SF: write register timed out\n"); return ret;

This patch splits the generic algorithm to erase pages/sectors from the actual implementation which is specific to each UCLASS_SPI_FLASH driver.
For now, the sf_probe.c driver is the only instance of this driver class using this generic algorithm but other driver instances are to come. This patch will ease their implementation.
Please note that the sf_dataflash.c driver has its own implementation of the .erase hook (of the struct dm_spi_flash_ops) which is not compatible with this generic algorithm. This is why we can't simply change the prototype of .erase hook and create a spi_flash_erase() wrapper.
Signed-off-by: Cyrille Pitchen cyrille.pitchen@atmel.com --- drivers/mtd/spi/sf_internal.h | 4 ++++ drivers/mtd/spi/spi_flash.c | 28 +++++++++++++++++++--------- 2 files changed, 23 insertions(+), 9 deletions(-)
diff --git a/drivers/mtd/spi/sf_internal.h b/drivers/mtd/spi/sf_internal.h index e2a4449dbf40..3c6bac3464f8 100644 --- a/drivers/mtd/spi/sf_internal.h +++ b/drivers/mtd/spi/sf_internal.h @@ -169,6 +169,10 @@ int spi_flash_cmd_write(struct spi_slave *spi, const u8 *cmd, size_t cmd_len, /* Flash erase(sectors) operation, support all possible erase commands */ int spi_flash_cmd_erase_ops(struct spi_flash *flash, u32 offset, size_t len);
+typedef int (*spi_flash_erase_fn)(struct spi_flash *, u32); +int spi_flash_erase_alg(struct spi_flash *flash, u32 offset, size_t len, + spi_flash_erase_fn erase_fn); + /* Lock stmicro spi flash region */ int stm_lock(struct spi_flash *flash, u32 ofs, size_t len);
diff --git a/drivers/mtd/spi/spi_flash.c b/drivers/mtd/spi/spi_flash.c index 6f30ff6f8d7c..20b8c31c09e0 100644 --- a/drivers/mtd/spi/spi_flash.c +++ b/drivers/mtd/spi/spi_flash.c @@ -281,11 +281,11 @@ int spi_flash_update_reg(struct spi_flash *flash, u8 opcode, return 0; }
-int spi_flash_cmd_erase_ops(struct spi_flash *flash, u32 offset, size_t len) +int spi_flash_erase_alg(struct spi_flash *flash, u32 offset, size_t len, + spi_flash_erase_fn erase_fn) { struct spi_slave *spi = flash->spi; u32 erase_size, erase_addr; - u8 cmd[SPI_FLASH_CMD_LEN]; int ret = -1;
erase_size = flash->erase_size; @@ -309,7 +309,6 @@ int spi_flash_cmd_erase_ops(struct spi_flash *flash, u32 offset, size_t len) } }
- cmd[0] = flash->erase_cmd; while (len) { erase_addr = offset;
@@ -328,12 +327,8 @@ int spi_flash_cmd_erase_ops(struct spi_flash *flash, u32 offset, size_t len) break; }
- spi_flash_addr(erase_addr, cmd); - - debug("SF: erase %2x %2x %2x %2x (%x)\n", cmd[0], cmd[1], - cmd[2], cmd[3], erase_addr); - - ret = spi_flash_cmd_write(spi, cmd, sizeof(cmd), NULL, 0); + debug("SF: erase %2x %06x\n", flash->erase_cmd, erase_addr); + ret = erase_fn(flash, erase_addr); if (ret < 0) { debug("SF: erase failed\n"); break; @@ -356,6 +351,21 @@ release: return ret; }
+static int spi_flash_erase_impl(struct spi_flash *flash, u32 offset) +{ + struct spi_slave *spi = flash->spi; + u8 cmd[SPI_FLASH_CMD_LEN]; + + cmd[0] = flash->erase_cmd; + spi_flash_addr(offset, cmd); + return spi_flash_cmd_write(spi, cmd, sizeof(cmd), NULL, 0); +} + +int spi_flash_cmd_erase_ops(struct spi_flash *flash, u32 offset, size_t len) +{ + return spi_flash_erase_alg(flash, offset, len, spi_flash_erase_impl); +} + int spi_flash_cmd_write_ops(struct spi_flash *flash, u32 offset, size_t len, const void *buf) {

This patch splits the generic algorithm to write data from the actual implementation which is specific to each UCLASS_SPI_FLASH driver.
For now, the sf_probe.c driver is the only instance of this driver class using this generic algorithm but other driver instances are to come. This patch will ease their implementation.
Please note that the sf_dataflash.c driver has its own implementation of the .write hook (of the struct dm_spi_flash_ops) which is not compatible with this generic algorithm. This is why we can't simply change the prototype of .write hook and create a spi_flash_write() wrapper.
Signed-off-by: Cyrille Pitchen cyrille.pitchen@atmel.com --- drivers/mtd/spi/sf_internal.h | 5 +++++ drivers/mtd/spi/spi_flash.c | 34 ++++++++++++++++++++++++---------- 2 files changed, 29 insertions(+), 10 deletions(-)
diff --git a/drivers/mtd/spi/sf_internal.h b/drivers/mtd/spi/sf_internal.h index 3c6bac3464f8..592c23cd4ce1 100644 --- a/drivers/mtd/spi/sf_internal.h +++ b/drivers/mtd/spi/sf_internal.h @@ -214,6 +214,11 @@ int spi_flash_update_reg(struct spi_flash *flash, u8 opcode, size_t len, int spi_flash_cmd_write_ops(struct spi_flash *flash, u32 offset, size_t len, const void *buf);
+typedef int (*spi_flash_write_fn)(struct spi_flash *, u32, size_t, + const void *); +int spi_flash_write_alg(struct spi_flash *flash, u32 offset, size_t len, + const void *buf, spi_flash_write_fn write_fn); + /* Flash read operation, support all possible read commands */ int spi_flash_cmd_read_ops(struct spi_flash *flash, u32 offset, size_t len, void *data); diff --git a/drivers/mtd/spi/spi_flash.c b/drivers/mtd/spi/spi_flash.c index 20b8c31c09e0..3c8224c2b084 100644 --- a/drivers/mtd/spi/spi_flash.c +++ b/drivers/mtd/spi/spi_flash.c @@ -366,14 +366,13 @@ int spi_flash_cmd_erase_ops(struct spi_flash *flash, u32 offset, size_t len) return spi_flash_erase_alg(flash, offset, len, spi_flash_erase_impl); }
-int spi_flash_cmd_write_ops(struct spi_flash *flash, u32 offset, - size_t len, const void *buf) +int spi_flash_write_alg(struct spi_flash *flash, u32 offset, size_t len, + const void *buf, spi_flash_write_fn write_fn) { struct spi_slave *spi = flash->spi; unsigned long byte_addr, page_size; u32 write_addr; size_t chunk_len, actual; - u8 cmd[SPI_FLASH_CMD_LEN]; int ret = -1;
page_size = flash->page_size; @@ -393,7 +392,6 @@ int spi_flash_cmd_write_ops(struct spi_flash *flash, u32 offset, } }
- cmd[0] = flash->write_cmd; for (actual = 0; actual < len; actual += chunk_len) { write_addr = offset;
@@ -419,13 +417,10 @@ int spi_flash_cmd_write_ops(struct spi_flash *flash, u32 offset, break; }
- spi_flash_addr(write_addr, cmd); - - debug("SF: 0x%p => cmd = { 0x%02x 0x%02x%02x%02x } chunk_len = %zu\n", - buf + actual, cmd[0], cmd[1], cmd[2], cmd[3], chunk_len); + debug("SF: 0x%p => cmd = { 0x%02x 0x%06x } chunk_len = %zu\n", + buf + actual, flash->write_cmd, write_addr, chunk_len);
- ret = spi_flash_cmd_write(spi, cmd, sizeof(cmd), - buf + actual, chunk_len); + ret = write_fn(flash, write_addr, chunk_len, buf + actual); if (ret < 0) { debug("SF: write failed\n"); break; @@ -446,6 +441,25 @@ release: return ret; }
+static int spi_flash_write_impl(struct spi_flash *flash, u32 offset, + size_t len, const void *buf) +{ + struct spi_slave *spi = flash->spi; + u8 cmd[SPI_FLASH_CMD_LEN]; + size_t cmdsz = sizeof(cmd); + + cmd[0] = flash->write_cmd; + spi_flash_addr(offset, cmd); + return spi_flash_cmd_write(spi, cmd, cmdsz, buf, len); +} + +int spi_flash_cmd_write_ops(struct spi_flash *flash, u32 offset, + size_t len, const void *buf) +{ + return spi_flash_write_alg(flash, offset, len, buf, + spi_flash_write_impl); +} + /* * TODO: remove the weak after all the other spi_flash_copy_mmap * implementations removed from drivers

This patch splits the generic algorithm to read data from the actual implementation which is specific to each UCLASS_SPI_FLASH driver.
For now, the sf_probe.c driver is the only instance of this driver class using this generic algorithm but other driver instances are to come. This patch will ease their implementation.
Please note that the sf_dataflash.c driver has its own implementation of the .read hook (of the struct dm_spi_flash_ops) which is not compatible with this generic algorithm. This is why we can't simply change the prototype of .read hook and create a spi_flash_read() wrapper.
Signed-off-by: Cyrille Pitchen cyrille.pitchen@atmel.com --- drivers/mtd/spi/sf_internal.h | 4 ++++ drivers/mtd/spi/spi_flash.c | 48 ++++++++++++++++++++++++++++--------------- 2 files changed, 35 insertions(+), 17 deletions(-)
diff --git a/drivers/mtd/spi/sf_internal.h b/drivers/mtd/spi/sf_internal.h index 592c23cd4ce1..569863a31684 100644 --- a/drivers/mtd/spi/sf_internal.h +++ b/drivers/mtd/spi/sf_internal.h @@ -223,6 +223,10 @@ int spi_flash_write_alg(struct spi_flash *flash, u32 offset, size_t len, int spi_flash_cmd_read_ops(struct spi_flash *flash, u32 offset, size_t len, void *data);
+typedef int (*spi_flash_read_fn)(struct spi_flash *, u32, size_t, void *); +int spi_flash_read_alg(struct spi_flash *flash, u32 offset, size_t len, + void *data, spi_flash_read_fn read_fn); + #ifdef CONFIG_SPI_FLASH_MTD int spi_flash_mtd_register(struct spi_flash *flash); void spi_flash_mtd_unregister(void); diff --git a/drivers/mtd/spi/spi_flash.c b/drivers/mtd/spi/spi_flash.c index 3c8224c2b084..4be99ea0b424 100644 --- a/drivers/mtd/spi/spi_flash.c +++ b/drivers/mtd/spi/spi_flash.c @@ -473,11 +473,10 @@ void __weak spi_flash_copy_mmap(void *data, void *offset, size_t len) memcpy(data, offset, len); }
-int spi_flash_cmd_read_ops(struct spi_flash *flash, u32 offset, - size_t len, void *data) +int spi_flash_read_alg(struct spi_flash *flash, u32 offset, size_t len, + void *data, spi_flash_read_fn read_fn) { struct spi_slave *spi = flash->spi; - u8 *cmd, cmdsz; u32 remain_len, read_len, read_addr; int bank_sel = 0; int ret = -1; @@ -496,15 +495,6 @@ int spi_flash_cmd_read_ops(struct spi_flash *flash, u32 offset, goto release; }
- cmdsz = SPI_FLASH_CMD_LEN + flash->dummy_byte; - cmd = calloc(1, cmdsz); - if (!cmd) { - debug("SF: Failed to allocate cmd\n"); - ret = -ENOMEM; - goto release; - } - - cmd[0] = flash->read_cmd; while (len) { read_addr = offset;
@@ -525,9 +515,7 @@ int spi_flash_cmd_read_ops(struct spi_flash *flash, u32 offset, else read_len = remain_len;
- spi_flash_addr(read_addr, cmd); - - ret = spi_flash_cmd_read(spi, cmd, cmdsz, data, read_len); + ret = read_fn(flash, read_addr, read_len, data); if (ret < 0) { debug("SF: read failed\n"); break; @@ -538,14 +526,40 @@ int spi_flash_cmd_read_ops(struct spi_flash *flash, u32 offset, data += read_len; }
- free(cmd); - release: spi_release_bus(spi);
return ret; }
+static int spi_flash_read_impl(struct spi_flash *flash, u32 offset, + size_t len, void *buf) +{ + struct spi_slave *spi = flash->spi; + u8 *cmd; + size_t cmdsz; + int ret; + + cmdsz = SPI_FLASH_CMD_LEN + flash->dummy_byte; + cmd = calloc(1, cmdsz); + if (!cmd) { + debug("SF: Failed to allocate cmd\n"); + return -ENOMEM; + } + + cmd[0] = flash->read_cmd; + spi_flash_addr(offset, cmd); + ret = spi_flash_cmd_read(spi, cmd, cmdsz, buf, len); + free(cmd); + return ret; +} + +int spi_flash_cmd_read_ops(struct spi_flash *flash, u32 offset, + size_t len, void *buf) +{ + return spi_flash_read_alg(flash, offset, len, buf, spi_flash_read_impl); +} + #ifdef CONFIG_SPI_FLASH_SST static int sst_byte_write(struct spi_flash *flash, u32 offset, const void *buf) {

This patch finalizes the split of internal register and memory data SPI commands. Indeed some (Q)SPI controllers such as the Atmel QSPI controller need to make the difference between register read/write SPI commands and data read/write/erase SPI commands.
It follows an interface close to the one used by the spi-nor.c driver in Linux: - read_reg: read out the register - write_reg: write data to the register - read: read data from the SPI flash - write: write data into the SPI flash - erase: erase sectors of the SPI flash
Signed-off-by: Cyrille Pitchen cyrille.pitchen@atmel.com --- drivers/mtd/spi/sf-uclass.c | 11 +++++++ drivers/mtd/spi/sf_internal.h | 13 ++++++-- drivers/mtd/spi/sf_probe.c | 18 +++++++++++ drivers/mtd/spi/spi_flash.c | 70 ++++++++++++++++++++++++------------------- include/spi_flash.h | 58 +++++++++++++++++++++++++++++++++++ 5 files changed, 137 insertions(+), 33 deletions(-)
diff --git a/drivers/mtd/spi/sf-uclass.c b/drivers/mtd/spi/sf-uclass.c index 19de964e6121..ebae9b1edb15 100644 --- a/drivers/mtd/spi/sf-uclass.c +++ b/drivers/mtd/spi/sf-uclass.c @@ -13,6 +13,17 @@
DECLARE_GLOBAL_DATA_PTR;
+int spi_flash_read_reg_dm(struct udevice *dev, u8 opcode, size_t len, void *buf) +{ + return sf_get_ops(dev)->read_reg(dev, opcode, len, buf); +} + +int spi_flash_write_reg_dm(struct udevice *dev, u8 opcode, size_t len, + const void *buf) +{ + return sf_get_ops(dev)->write_reg(dev, opcode, len, buf); +} + int spi_flash_read_dm(struct udevice *dev, u32 offset, size_t len, void *buf) { return sf_get_ops(dev)->read(dev, offset, len, buf); diff --git a/drivers/mtd/spi/sf_internal.h b/drivers/mtd/spi/sf_internal.h index 569863a31684..15a5fa9c2afd 100644 --- a/drivers/mtd/spi/sf_internal.h +++ b/drivers/mtd/spi/sf_internal.h @@ -12,6 +12,7 @@
#include <linux/types.h> #include <linux/compiler.h> +#include <spi_flash.h>
/* Dual SPI flash memories - see SPI_COMM_DUAL_... */ enum spi_dual_flash { @@ -185,13 +186,13 @@ int stm_is_locked(struct spi_flash *flash, u32 ofs, size_t len); /* Enable writing on the SPI flash */ static inline int spi_flash_cmd_write_enable(struct spi_flash *flash) { - return spi_flash_cmd(flash->spi, CMD_WRITE_ENABLE, NULL, 0); + return spi_flash_write_reg(flash, CMD_WRITE_ENABLE, 0, NULL); }
/* Disable writing on the SPI flash */ static inline int spi_flash_cmd_write_disable(struct spi_flash *flash) { - return spi_flash_cmd(flash->spi, CMD_WRITE_DISABLE, NULL, 0); + return spi_flash_write_reg(flash, CMD_WRITE_DISABLE, 0, NULL); }
/* Wait for Busy/Write in Progress flag to be cleared */ @@ -206,6 +207,10 @@ int spi_flash_wait_ready(struct spi_flash *flash); int spi_flash_update_reg(struct spi_flash *flash, u8 opcode, size_t len, const void *buf);
+/* Flash write register operation */ +int spi_flash_cmd_write_reg_ops(struct spi_flash *flash, u8 opcode, size_t len, + const void *buf); + /* * Flash write operation, support all possible write commands. * Write the requested data out breaking it up into multiple write @@ -219,6 +224,10 @@ typedef int (*spi_flash_write_fn)(struct spi_flash *, u32, size_t, int spi_flash_write_alg(struct spi_flash *flash, u32 offset, size_t len, const void *buf, spi_flash_write_fn write_fn);
+/* Flash read register operation */ +int spi_flash_cmd_read_reg_ops(struct spi_flash *flash, u8 opcode, size_t len, + void *buf); + /* Flash read operation, support all possible read commands */ int spi_flash_cmd_read_ops(struct spi_flash *flash, u32 offset, size_t len, void *data); diff --git a/drivers/mtd/spi/sf_probe.c b/drivers/mtd/spi/sf_probe.c index 7b296378d2be..fbd7d4740b51 100644 --- a/drivers/mtd/spi/sf_probe.c +++ b/drivers/mtd/spi/sf_probe.c @@ -111,6 +111,22 @@ void spi_flash_free(struct spi_flash *flash)
#else /* defined CONFIG_DM_SPI_FLASH */
+static int spi_flash_std_read_reg(struct udevice *dev, u8 opcode, size_t len, + void *buf) +{ + struct spi_flash *flash = dev_get_uclass_priv(dev); + + return spi_flash_cmd_read_reg_ops(flash, opcode, len, buf); +} + +static int spi_flash_std_write_reg(struct udevice *dev, u8 opcode, size_t len, + const void *buf) +{ + struct spi_flash *flash = dev_get_uclass_priv(dev); + + return spi_flash_cmd_write_reg_ops(flash, opcode, len, buf); +} + static int spi_flash_std_read(struct udevice *dev, u32 offset, size_t len, void *buf) { @@ -157,6 +173,8 @@ static int spi_flash_std_probe(struct udevice *dev) }
static const struct dm_spi_flash_ops spi_flash_std_ops = { + .read_reg = spi_flash_std_read_reg, + .write_reg = spi_flash_std_write_reg, .read = spi_flash_std_read, .write = spi_flash_std_write, .erase = spi_flash_std_erase, diff --git a/drivers/mtd/spi/spi_flash.c b/drivers/mtd/spi/spi_flash.c index 4be99ea0b424..4f269eacc591 100644 --- a/drivers/mtd/spi/spi_flash.c +++ b/drivers/mtd/spi/spi_flash.c @@ -32,12 +32,9 @@ static void spi_flash_addr(u32 addr, u8 *cmd)
static int read_sr(struct spi_flash *flash, u8 *rs) { - struct spi_slave *spi = flash->spi; int ret; - u8 cmd;
- cmd = CMD_READ_STATUS; - ret = spi_flash_cmd_read(spi, &cmd, 1, rs, 1); + ret = spi_flash_read_reg(flash, CMD_READ_STATUS, 1, rs); if (ret < 0) { debug("SF: fail to read status register\n"); return ret; @@ -48,11 +45,9 @@ static int read_sr(struct spi_flash *flash, u8 *rs)
static int read_fsr(struct spi_flash *flash, u8 *fsr) { - struct spi_slave *spi = flash->spi; int ret; - const u8 cmd = CMD_FLAG_STATUS;
- ret = spi_flash_cmd_read(spi, &cmd, 1, fsr, 1); + ret = spi_flash_read_reg(flash, CMD_FLAG_STATUS, 1, fsr); if (ret < 0) { debug("SF: fail to read flag status register\n"); return ret; @@ -77,12 +72,9 @@ static int write_sr(struct spi_flash *flash, u8 ws) #if defined(CONFIG_SPI_FLASH_SPANSION) || defined(CONFIG_SPI_FLASH_WINBOND) static int read_cr(struct spi_flash *flash, u8 *rc) { - struct spi_slave *spi = flash->spi; int ret; - u8 cmd;
- cmd = CMD_READ_CONFIG; - ret = spi_flash_cmd_read(spi, &cmd, 1, rc, 1); + ret = spi_flash_read_reg(flash, CMD_READ_CONFIG, 1, rc); if (ret < 0) { debug("SF: fail to read config register\n"); return ret; @@ -134,7 +126,6 @@ bar_end:
static int spi_flash_read_bar(struct spi_flash *flash, u8 idcode0) { - struct spi_slave *spi = flash->spi; u8 curr_bank = 0; int ret;
@@ -151,7 +142,7 @@ static int spi_flash_read_bar(struct spi_flash *flash, u8 idcode0) flash->bank_write_cmd = CMD_EXTNADDR_WREAR; }
- ret = spi_flash_cmd_read(spi, &flash->bank_read_cmd, 1, &curr_bank, 1); + ret = spi_flash_read_reg(flash, flash->bank_read_cmd, &curr_bank, 1); if (ret) { debug("SF: fail to read bank addr register\n"); return ret; @@ -257,7 +248,6 @@ int spi_flash_wait_ready(struct spi_flash *flash) int spi_flash_update_reg(struct spi_flash *flash, u8 opcode, size_t len, const void *buf) { - struct spi_slave *spi = flash->spi; int ret;
ret = spi_flash_cmd_write_enable(flash); @@ -266,7 +256,7 @@ int spi_flash_update_reg(struct spi_flash *flash, u8 opcode, return ret; }
- ret = spi_flash_cmd_write(spi, &opcode, 1, buf, len); + ret = spi_flash_write_reg(flash, opcode, len, buf); if (ret < 0) { debug("SF: write cmd failed\n"); return ret; @@ -281,6 +271,14 @@ int spi_flash_update_reg(struct spi_flash *flash, u8 opcode, return 0; }
+int spi_flash_cmd_write_reg_ops(struct spi_flash *flash, u8 opcode, size_t len, + const void *buf) +{ + struct spi_slave *spi = flash->spi; + + return spi_flash_cmd_write(spi, &opcode, 1, buf, len); +} + int spi_flash_erase_alg(struct spi_flash *flash, u32 offset, size_t len, spi_flash_erase_fn erase_fn) { @@ -460,6 +458,14 @@ int spi_flash_cmd_write_ops(struct spi_flash *flash, u32 offset, spi_flash_write_impl); }
+int spi_flash_cmd_read_reg_ops(struct spi_flash *flash, u8 opcode, + size_t len, void *buf) +{ + struct spi_slave *spi = flash->spi; + + return spi_flash_cmd_read(spi, &opcode, 1, buf, len); +} + /* * TODO: remove the weak after all the other spi_flash_copy_mmap * implementations removed from drivers @@ -982,8 +988,25 @@ int spi_flash_scan(struct spi_flash *flash) CMD_READ_DUAL_IO_FAST, CMD_READ_QUAD_IO_FAST };
+ /* Assign spi_flash ops */ +#ifndef CONFIG_DM_SPI_FLASH + flash->read_reg = spi_flash_cmd_read_reg_ops; + flash->write_reg = spi_flash_cmd_write_reg_ops; + flash->write = spi_flash_cmd_write_ops; +#if defined(CONFIG_SPI_FLASH_SST) + if (flash->flags & SNOR_F_SST_WR) { + if (spi->mode & SPI_TX_BYTE) + flash->write = sst_write_bp; + else + flash->write = sst_write_wp; + } +#endif + flash->erase = spi_flash_cmd_erase_ops; + flash->read = spi_flash_cmd_read_ops; +#endif + /* Read the ID codes */ - ret = spi_flash_cmd(spi, CMD_READ_ID, idcode, sizeof(idcode)); + ret = spi_flash_read_reg(flash, CMD_READ_ID, sizeof(idcode), idcode); if (ret) { printf("SF: Failed to get idcodes\n"); return ret; @@ -1032,21 +1055,6 @@ int spi_flash_scan(struct spi_flash *flash) if (params->flags & SST_WR) flash->flags |= SNOR_F_SST_WR;
- /* Assign spi_flash ops */ -#ifndef CONFIG_DM_SPI_FLASH - flash->write = spi_flash_cmd_write_ops; -#if defined(CONFIG_SPI_FLASH_SST) - if (flash->flags & SNOR_F_SST_WR) { - if (spi->mode & SPI_TX_BYTE) - flash->write = sst_write_bp; - else - flash->write = sst_write_wp; - } -#endif - flash->erase = spi_flash_cmd_erase_ops; - flash->read = spi_flash_cmd_read_ops; -#endif - /* lock hooks are flash specific - assign them based on idcode0 */ switch (idcode[0]) { #if defined(CONFIG_SPI_FLASH_STMICRO) || defined(CONFIG_SPI_FLASH_SST) diff --git a/include/spi_flash.h b/include/spi_flash.h index d0ce9e721ad0..31d11e55571d 100644 --- a/include/spi_flash.h +++ b/include/spi_flash.h @@ -52,6 +52,9 @@ struct spi_slave; * @flash_lock: lock a region of the SPI Flash * @flash_unlock: unlock a region of the SPI Flash * @flash_is_locked: check if a region of the SPI Flash is completely locked + * @read_reg: Flash read_reg ops: Send cmd to read len bytes into buf + * @write_reg: Flash write_reg ops: Send cmd to write len bytes from + * buf * @read: Flash read ops: Read len bytes at offset into buf * Supported cmds: Fast Array Read * @write: Flash write ops: Write len bytes from buf into offset @@ -101,6 +104,10 @@ struct spi_flash { * if required, perhaps with a way of scanning through the list to * find the driver that matches the device. */ + int (*read_reg)(struct spi_flash *flash, u8 opcode, size_t len, + void *buf); + int (*write_reg)(struct spi_flash *flash, u8 opcode, size_t len, + const void *buf); int (*read)(struct spi_flash *flash, u32 offset, size_t len, void *buf); int (*write)(struct spi_flash *flash, u32 offset, size_t len, const void *buf); @@ -109,6 +116,9 @@ struct spi_flash { };
struct dm_spi_flash_ops { + int (*read_reg)(struct udevice *dev, u8 opcode, size_t len, void *buf); + int (*write_reg)(struct udevice *dev, u8 opcode, size_t len, + const void *buf); int (*read)(struct udevice *dev, u32 offset, size_t len, void *buf); int (*write)(struct udevice *dev, u32 offset, size_t len, const void *buf); @@ -120,6 +130,30 @@ struct dm_spi_flash_ops {
#ifdef CONFIG_DM_SPI_FLASH /** + * spi_flash_read_reg_dm() - Send register command and read data result + * + * @dev: SPI flash device + * @opcode: Register command op code + * @len: Number of bytes to read as register command output + * @buf: Buffer to fill with the register command output + * @return 0 if OK, -ve on error + */ +int spi_flash_read_reg_dm(struct udevice *dev, u8 opcode, size_t len, + void *buf); + +/** + * spi_flash_write_reg_dm() - Send register command with data + * + * @dev: SPI flash device + * @opcode: Register command op code + * @len: Number of bytes to write as register command input + * @buf: Buffer filled with the register command input + * @return 0 if OK, -ve on error + */ +int spi_flash_write_reg_dm(struct udevice *dev, u8 opcode, size_t len, + const void *buf); + +/** * spi_flash_read_dm() - Read data from SPI flash * * @dev: SPI flash device @@ -165,6 +199,18 @@ struct spi_flash *spi_flash_probe(unsigned int bus, unsigned int cs, /* Compatibility function - this is the old U-Boot API */ void spi_flash_free(struct spi_flash *flash);
+static inline int spi_flash_read_reg(struct spi_flash *flash, u8 opcode, + size_t len, void *buf) +{ + return spi_flash_read_reg_dm(flash->dev, opcode, len, buf); +} + +static inline int spi_flash_write_reg(struct spi_flash *flash, u8 opcode, + size_t len, const void *buf) +{ + return spi_flash_write_reg_dm(flash->dev, opcode, len, buf); +} + static inline int spi_flash_read(struct spi_flash *flash, u32 offset, size_t len, void *buf) { @@ -208,6 +254,18 @@ struct spi_flash *spi_flash_probe_fdt(const void *blob, int slave_node,
void spi_flash_free(struct spi_flash *flash);
+static inline int spi_flash_read_reg(struct spi_flash *flash, u8 opcode, + size_t len, void *buf) +{ + return flash->read_reg(flash, opcode, len, buf); +} + +static inline int spi_flash_write_reg(struct spi_flash *flash, u8 opcode, + size_t len, const void *buf) +{ + return flash->write_reg(flash, opcode, len, buf); +} + static inline int spi_flash_read(struct spi_flash *flash, u32 offset, size_t len, void *buf) {

This patch makes the support of SST flashes available to all UCLASS_SPI_FLASH drivers, not only the sf_probe.c one.
Signed-off-by: Cyrille Pitchen cyrille.pitchen@atmel.com --- drivers/mtd/spi/sf_internal.h | 8 ++--- drivers/mtd/spi/sf_probe.c | 9 ----- drivers/mtd/spi/spi_flash.c | 76 +++++++++++++++++++++++-------------------- 3 files changed, 43 insertions(+), 50 deletions(-)
diff --git a/drivers/mtd/spi/sf_internal.h b/drivers/mtd/spi/sf_internal.h index 15a5fa9c2afd..c5966fb37ac8 100644 --- a/drivers/mtd/spi/sf_internal.h +++ b/drivers/mtd/spi/sf_internal.h @@ -51,7 +51,8 @@ enum {
enum spi_nor_option_flags { SNOR_F_SST_WR = BIT(0), - SNOR_F_USE_FSR = BIT(1), + SNOR_F_SST_WR_2ND = BIT(1), + SNOR_F_USE_FSR = BIT(2), };
#define SPI_FLASH_3B_ADDR_LEN 3 @@ -118,11 +119,6 @@ enum spi_nor_option_flags { #ifdef CONFIG_SPI_FLASH_SST # define CMD_SST_BP 0x02 /* Byte Program */ # define CMD_SST_AAI_WP 0xAD /* Auto Address Incr Word Program */ - -int sst_write_wp(struct spi_flash *flash, u32 offset, size_t len, - const void *buf); -int sst_write_bp(struct spi_flash *flash, u32 offset, size_t len, - const void *buf); #endif
/** diff --git a/drivers/mtd/spi/sf_probe.c b/drivers/mtd/spi/sf_probe.c index fbd7d4740b51..837535fdb7a5 100644 --- a/drivers/mtd/spi/sf_probe.c +++ b/drivers/mtd/spi/sf_probe.c @@ -140,15 +140,6 @@ static int spi_flash_std_write(struct udevice *dev, u32 offset, size_t len, { struct spi_flash *flash = dev_get_uclass_priv(dev);
-#if defined(CONFIG_SPI_FLASH_SST) - if (flash->flags & SNOR_F_SST_WR) { - if (flash->spi->mode & SPI_TX_BYTE) - return sst_write_bp(flash, offset, len, buf); - else - return sst_write_wp(flash, offset, len, buf); - } -#endif - return spi_flash_cmd_write_ops(flash, offset, len, buf); }
diff --git a/drivers/mtd/spi/spi_flash.c b/drivers/mtd/spi/spi_flash.c index 4f269eacc591..7f5341a87c07 100644 --- a/drivers/mtd/spi/spi_flash.c +++ b/drivers/mtd/spi/spi_flash.c @@ -364,6 +364,14 @@ int spi_flash_cmd_erase_ops(struct spi_flash *flash, u32 offset, size_t len) return spi_flash_erase_alg(flash, offset, len, spi_flash_erase_impl); }
+#if defined(CONFIG_SPI_FLASH_SST) +static int sst_write_wp(struct spi_flash *flash, u32 offset, size_t len, + const void *buf, spi_flash_write_fn write_fn); + +static int sst_write_bp(struct spi_flash *flash, u32 offset, size_t len, + const void *buf, spi_flash_write_fn write_fn); +#endif + int spi_flash_write_alg(struct spi_flash *flash, u32 offset, size_t len, const void *buf, spi_flash_write_fn write_fn) { @@ -373,6 +381,15 @@ int spi_flash_write_alg(struct spi_flash *flash, u32 offset, size_t len, size_t chunk_len, actual; int ret = -1;
+#if defined(CONFIG_SPI_FLASH_SST) + if (flash->flags & SNOR_F_SST_WR) { + if (spi->mode & SPI_TX_BYTE) + return sst_write_bp(flash, offset, len, buf, write_fn); + else + return sst_write_wp(flash, offset, len, buf, write_fn); + } +#endif + page_size = flash->page_size;
ret = spi_claim_bus(spi); @@ -448,6 +465,10 @@ static int spi_flash_write_impl(struct spi_flash *flash, u32 offset,
cmd[0] = flash->write_cmd; spi_flash_addr(offset, cmd); +#ifdef CONFIG_SPI_FLASH_SST + if (flash->flags & SNOR_F_SST_WR_2ND) + cmdsz = 1; +#endif return spi_flash_cmd_write(spi, cmd, cmdsz, buf, len); }
@@ -567,38 +588,33 @@ int spi_flash_cmd_read_ops(struct spi_flash *flash, u32 offset, }
#ifdef CONFIG_SPI_FLASH_SST -static int sst_byte_write(struct spi_flash *flash, u32 offset, const void *buf) +static int sst_byte_write(struct spi_flash *flash, u32 offset, const void *buf, + spi_flash_write_fn write_fn) { struct spi_slave *spi = flash->spi; int ret; - u8 cmd[4] = { - CMD_SST_BP, - offset >> 16, - offset >> 8, - offset, - };
+ flash->write_cmd = CMD_SST_BP; debug("BP[%02x]: 0x%p => cmd = { 0x%02x 0x%06x }\n", - spi_w8r8(spi, CMD_READ_STATUS), buf, cmd[0], offset); + spi_w8r8(spi, CMD_READ_STATUS), buf, flash->write_cmd, offset);
ret = spi_flash_cmd_write_enable(flash); if (ret) return ret;
- ret = spi_flash_cmd_write(spi, cmd, sizeof(cmd), buf, 1); + ret = write_fn(flash, offset, 1, buf); if (ret) return ret;
return spi_flash_cmd_wait_ready(flash, SPI_FLASH_PROG_TIMEOUT); }
-int sst_write_wp(struct spi_flash *flash, u32 offset, size_t len, - const void *buf) +static int sst_write_wp(struct spi_flash *flash, u32 offset, size_t len, + const void *buf, spi_flash_write_fn write_fn) { struct spi_slave *spi = flash->spi; - size_t actual, cmd_len; + size_t actual; int ret; - u8 cmd[4];
ret = spi_claim_bus(spi); if (ret) { @@ -606,10 +622,12 @@ int sst_write_wp(struct spi_flash *flash, u32 offset, size_t len, return ret; }
+ flash->flags &= ~SNOR_F_SST_WR_2ND; + /* If the data is not word aligned, write out leading single byte */ actual = offset % 2; if (actual) { - ret = sst_byte_write(flash, offset, buf); + ret = sst_byte_write(flash, offset, buf, write_fn); if (ret) goto done; } @@ -619,19 +637,14 @@ int sst_write_wp(struct spi_flash *flash, u32 offset, size_t len, if (ret) goto done;
- cmd_len = 4; - cmd[0] = CMD_SST_AAI_WP; - cmd[1] = offset >> 16; - cmd[2] = offset >> 8; - cmd[3] = offset; + flash->write_cmd = CMD_SST_AAI_WP;
for (; actual < len - 1; actual += 2) { debug("WP[%02x]: 0x%p => cmd = { 0x%02x 0x%06x }\n", spi_w8r8(spi, CMD_READ_STATUS), buf + actual, - cmd[0], offset); + flash->write_cmd, offset);
- ret = spi_flash_cmd_write(spi, cmd, cmd_len, - buf + actual, 2); + ret = write_fn(flash, offset, 2, buf + actual); if (ret) { debug("SF: sst word program failed\n"); break; @@ -641,16 +654,17 @@ int sst_write_wp(struct spi_flash *flash, u32 offset, size_t len, if (ret) break;
- cmd_len = 1; + flash->flags |= SNOR_F_SST_WR_2ND; offset += 2; } + flash->flags &= ~SNOR_F_SST_WR_2ND;
if (!ret) ret = spi_flash_cmd_write_disable(flash);
/* If there is a single trailing byte, write it out */ if (!ret && actual != len) - ret = sst_byte_write(flash, offset, buf + actual); + ret = sst_byte_write(flash, offset, buf + actual, write_fn);
done: debug("SF: sst: program %s %zu bytes @ 0x%zx\n", @@ -660,8 +674,8 @@ int sst_write_wp(struct spi_flash *flash, u32 offset, size_t len, return ret; }
-int sst_write_bp(struct spi_flash *flash, u32 offset, size_t len, - const void *buf) +static int sst_write_bp(struct spi_flash *flash, u32 offset, size_t len, + const void *buf, spi_flash_write_fn write_fn) { struct spi_slave *spi = flash->spi; size_t actual; @@ -674,7 +688,7 @@ int sst_write_bp(struct spi_flash *flash, u32 offset, size_t len, }
for (actual = 0; actual < len; actual++) { - ret = sst_byte_write(flash, offset, buf + actual); + ret = sst_byte_write(flash, offset, buf + actual, write_fn); if (ret) { debug("SF: sst byte program failed\n"); break; @@ -993,14 +1007,6 @@ int spi_flash_scan(struct spi_flash *flash) flash->read_reg = spi_flash_cmd_read_reg_ops; flash->write_reg = spi_flash_cmd_write_reg_ops; flash->write = spi_flash_cmd_write_ops; -#if defined(CONFIG_SPI_FLASH_SST) - if (flash->flags & SNOR_F_SST_WR) { - if (spi->mode & SPI_TX_BYTE) - flash->write = sst_write_bp; - else - flash->write = sst_write_wp; - } -#endif flash->erase = spi_flash_cmd_erase_ops; flash->read = spi_flash_cmd_read_ops; #endif

It looks odd to compute a logical AND between the e_rd_cmd field of struct spi_flash_params and the mode_rx field of struct spi_slave.
Indeed, these two fields don't use the same range of values. mode_rx is limited to SPI_RX_{SLOW, FAST, DUAL, QUAD}. Even completed with the SPI_TX_{DUAL, QUAD} flags from the mode field, this is not enough to find out whether the SPI controller driver supports Fast Read 4-4-4 (Micron, Macronix, Winbond) commands. With only the SPI_RX_QUAD and the SPI_TX_QUAD, how to make the difference between Fast Read 1-4-4 and Fast Read 4-4-4 ?
On the other hand, the e_rd_cmd field already provides a more accurate knowledge of the Fast Read commands supported by the SPI flash memory.
Then this patch provides a safe convertion from mode_rx + mode values into e_rd_cmd values to be used by the sf_probe.c driver: this driver never try using the Fast Read 4-4-4 command. Other drivers will provide an exhaustive list of supported Fast Read commands.
Hence a more accurate op code is chosen according to both the SPI flash memory and the SPI controller capabilities.
Please note that we cannot simply extend the SPI_TX_* and SPI_RX_* flags of the struct spi_slave since they are set according DT properties shared with Linux: spi-tx-bus-width and spi-rx-bus-width.
These two DT properties don't allow us to make the difference between the SPI 1-4-4 and SPI 4-4-4 protocols.
Signed-off-by: Cyrille Pitchen cyrille.pitchen@atmel.com --- drivers/mtd/spi/sf_internal.h | 14 +++++++++++--- drivers/mtd/spi/sf_params.c | 32 ++++++++++++++++---------------- drivers/mtd/spi/sf_probe.c | 19 ++++++++++++++++++- drivers/mtd/spi/spi_flash.c | 9 ++++++--- 4 files changed, 51 insertions(+), 23 deletions(-)
diff --git a/drivers/mtd/spi/sf_internal.h b/drivers/mtd/spi/sf_internal.h index c5966fb37ac8..8b8521369e4e 100644 --- a/drivers/mtd/spi/sf_internal.h +++ b/drivers/mtd/spi/sf_internal.h @@ -29,12 +29,19 @@ enum spi_read_cmds { QUAD_OUTPUT_FAST = BIT(3), DUAL_IO_FAST = BIT(4), QUAD_IO_FAST = BIT(5), + DUAL_CMD_FAST = BIT(6), + QUAD_CMD_FAST = BIT(7), };
/* Normal - Extended - Full command set */ #define RD_NORM (ARRAY_SLOW | ARRAY_FAST) -#define RD_EXTN (RD_NORM | DUAL_OUTPUT_FAST | DUAL_IO_FAST) -#define RD_FULL (RD_EXTN | QUAD_OUTPUT_FAST | QUAD_IO_FAST) +#define RD_EXTN (RD_NORM | DUAL_OUTPUT_FAST) +#define RD_DUAL (RD_NORM | DUAL_OUTPUT_FAST | DUAL_IO_FAST) +#define RD_DCMD (RD_DUAL | DUAL_CMD_FAST) +#define RD_QUAD (RD_NORM | QUAD_OUTPUT_FAST | QUAD_IO_FAST) +#define RD_QCMD (RD_QUAD | QUAD_CMD_FAST) +#define RD_FULL (RD_DUAL | RD_QUAD) +#define RD_FCMD (RD_DCMD | RD_QCMD)
/* sf param flags */ enum { @@ -240,6 +247,7 @@ void spi_flash_mtd_unregister(void); /** * spi_flash_scan - scan the SPI FLASH * @flash: the spi flash structure + * @e_rd_cmd: Enum list for read commands supported by the SPI controller * * The drivers can use this fuction to scan the SPI FLASH. * In the scanning, it will try to get all the necessary information to @@ -247,6 +255,6 @@ void spi_flash_mtd_unregister(void); * * Return: 0 for success, others for failure. */ -int spi_flash_scan(struct spi_flash *flash); +int spi_flash_scan(struct spi_flash *flash, u8 e_rd_cmd);
#endif /* _SF_INTERNAL_H_ */ diff --git a/drivers/mtd/spi/sf_params.c b/drivers/mtd/spi/sf_params.c index 4f37e33eb068..d3103dbc8742 100644 --- a/drivers/mtd/spi/sf_params.c +++ b/drivers/mtd/spi/sf_params.c @@ -47,10 +47,10 @@ const struct spi_flash_params spi_flash_params_table[] = { {"MX25L1605D", 0xc22015, 0x0, 64 * 1024, 32, RD_NORM, 0}, {"MX25L3205D", 0xc22016, 0x0, 64 * 1024, 64, RD_NORM, 0}, {"MX25L6405D", 0xc22017, 0x0, 64 * 1024, 128, RD_NORM, 0}, - {"MX25L12805", 0xc22018, 0x0, 64 * 1024, 256, RD_FULL, WR_QPP}, - {"MX25L25635F", 0xc22019, 0x0, 64 * 1024, 512, RD_FULL, WR_QPP}, - {"MX25L51235F", 0xc2201a, 0x0, 64 * 1024, 1024, RD_FULL, WR_QPP}, - {"MX25L12855E", 0xc22618, 0x0, 64 * 1024, 256, RD_FULL, WR_QPP}, + {"MX25L12805", 0xc22018, 0x0, 64 * 1024, 256, RD_QCMD, WR_QPP}, + {"MX25L25635F", 0xc22019, 0x0, 64 * 1024, 512, RD_QCMD, WR_QPP}, + {"MX25L51235F", 0xc2201a, 0x0, 64 * 1024, 1024, RD_QCMD, WR_QPP}, + {"MX25L12855E", 0xc22618, 0x0, 64 * 1024, 256, RD_QCMD, WR_QPP}, #endif #ifdef CONFIG_SPI_FLASH_SPANSION /* SPANSION */ {"S25FL008A", 0x010213, 0x0, 64 * 1024, 16, RD_NORM, 0}, @@ -83,18 +83,18 @@ const struct spi_flash_params spi_flash_params_table[] = { {"M25P64", 0x202017, 0x0, 64 * 1024, 128, RD_NORM, 0}, {"M25P128", 0x202018, 0x0, 256 * 1024, 64, RD_NORM, 0}, {"M25PX64", 0x207117, 0x0, 64 * 1024, 128, RD_NORM, SECT_4K}, - {"N25Q32", 0x20ba16, 0x0, 64 * 1024, 64, RD_FULL, WR_QPP | SECT_4K}, - {"N25Q32A", 0x20bb16, 0x0, 64 * 1024, 64, RD_FULL, WR_QPP | SECT_4K}, - {"N25Q64", 0x20ba17, 0x0, 64 * 1024, 128, RD_FULL, WR_QPP | SECT_4K}, - {"N25Q64A", 0x20bb17, 0x0, 64 * 1024, 128, RD_FULL, WR_QPP | SECT_4K}, - {"N25Q128", 0x20ba18, 0x0, 64 * 1024, 256, RD_FULL, WR_QPP}, - {"N25Q128A", 0x20bb18, 0x0, 64 * 1024, 256, RD_FULL, WR_QPP}, - {"N25Q256", 0x20ba19, 0x0, 64 * 1024, 512, RD_FULL, WR_QPP | SECT_4K}, - {"N25Q256A", 0x20bb19, 0x0, 64 * 1024, 512, RD_FULL, WR_QPP | SECT_4K}, - {"N25Q512", 0x20ba20, 0x0, 64 * 1024, 1024, RD_FULL, WR_QPP | E_FSR | SECT_4K}, - {"N25Q512A", 0x20bb20, 0x0, 64 * 1024, 1024, RD_FULL, WR_QPP | E_FSR | SECT_4K}, - {"N25Q1024", 0x20ba21, 0x0, 64 * 1024, 2048, RD_FULL, WR_QPP | E_FSR | SECT_4K}, - {"N25Q1024A", 0x20bb21, 0x0, 64 * 1024, 2048, RD_FULL, WR_QPP | E_FSR | SECT_4K}, + {"N25Q32", 0x20ba16, 0x0, 64 * 1024, 64, RD_FCMD, WR_QPP | SECT_4K}, + {"N25Q32A", 0x20bb16, 0x0, 64 * 1024, 64, RD_FCMD, WR_QPP | SECT_4K}, + {"N25Q64", 0x20ba17, 0x0, 64 * 1024, 128, RD_FCMD, WR_QPP | SECT_4K}, + {"N25Q64A", 0x20bb17, 0x0, 64 * 1024, 128, RD_FCMD, WR_QPP | SECT_4K}, + {"N25Q128", 0x20ba18, 0x0, 64 * 1024, 256, RD_FCMD, WR_QPP}, + {"N25Q128A", 0x20bb18, 0x0, 64 * 1024, 256, RD_FCMD, WR_QPP}, + {"N25Q256", 0x20ba19, 0x0, 64 * 1024, 512, RD_FCMD, WR_QPP | SECT_4K}, + {"N25Q256A", 0x20bb19, 0x0, 64 * 1024, 512, RD_FCMD, WR_QPP | SECT_4K}, + {"N25Q512", 0x20ba20, 0x0, 64 * 1024, 1024, RD_FCMD, WR_QPP | E_FSR | SECT_4K}, + {"N25Q512A", 0x20bb20, 0x0, 64 * 1024, 1024, RD_FCMD, WR_QPP | E_FSR | SECT_4K}, + {"N25Q1024", 0x20ba21, 0x0, 64 * 1024, 2048, RD_FCMD, WR_QPP | E_FSR | SECT_4K}, + {"N25Q1024A", 0x20bb21, 0x0, 64 * 1024, 2048, RD_FCMD, WR_QPP | E_FSR | SECT_4K}, #endif #ifdef CONFIG_SPI_FLASH_SST /* SST */ {"SST25VF040B", 0xbf258d, 0x0, 64 * 1024, 8, RD_NORM, SECT_4K | SST_WR}, diff --git a/drivers/mtd/spi/sf_probe.c b/drivers/mtd/spi/sf_probe.c index 837535fdb7a5..8a0345bc1994 100644 --- a/drivers/mtd/spi/sf_probe.c +++ b/drivers/mtd/spi/sf_probe.c @@ -26,6 +26,7 @@ static int spi_flash_probe_slave(struct spi_flash *flash) { struct spi_slave *spi = flash->spi; + u8 mode_rx, e_rd_cmd; int ret;
/* Setup spi_slave */ @@ -34,6 +35,22 @@ static int spi_flash_probe_slave(struct spi_flash *flash) return -ENODEV; }
+ /* Convert SPI mode_rx and mode to SPI flash read commands */ + mode_rx = spi->mode_rx; + if (mode_rx & SPI_RX_QUAD) { + e_rd_cmd = RD_NORM | QUAD_OUTPUT_FAST; + if (spi->mode & SPI_TX_QUAD) + e_rd_cmd |= QUAD_IO_FAST; + } else if (mode_rx & SPI_RX_DUAL) { + e_rd_cmd = RD_NORM | DUAL_OUTPUT_FAST; + if (spi->mode & SPI_TX_DUAL) + e_rd_cmd |= DUAL_IO_FAST; + } else if ((mode_rx & (SPI_RX_SLOW | SPI_RX_FAST)) == SPI_RX_SLOW) { + e_rd_cmd = ARRAY_SLOW; + } else { + e_rd_cmd = RD_NORM; + } + /* Claim spi bus */ ret = spi_claim_bus(spi); if (ret) { @@ -41,7 +58,7 @@ static int spi_flash_probe_slave(struct spi_flash *flash) return ret; }
- ret = spi_flash_scan(flash); + ret = spi_flash_scan(flash, e_rd_cmd); if (ret) goto err_read_id;
diff --git a/drivers/mtd/spi/spi_flash.c b/drivers/mtd/spi/spi_flash.c index 7f5341a87c07..5ba148bd3626 100644 --- a/drivers/mtd/spi/spi_flash.c +++ b/drivers/mtd/spi/spi_flash.c @@ -987,7 +987,7 @@ int spi_flash_decode_fdt(const void *blob, struct spi_flash *flash) } #endif /* CONFIG_IS_ENABLED(OF_CONTROL) */
-int spi_flash_scan(struct spi_flash *flash) +int spi_flash_scan(struct spi_flash *flash, u8 e_rd_cmd) { struct spi_slave *spi = flash->spi; const struct spi_flash_params *params; @@ -1000,7 +1000,10 @@ int spi_flash_scan(struct spi_flash *flash) CMD_READ_DUAL_OUTPUT_FAST, CMD_READ_QUAD_OUTPUT_FAST, CMD_READ_DUAL_IO_FAST, - CMD_READ_QUAD_IO_FAST }; + CMD_READ_QUAD_IO_FAST, + CMD_READ_DUAL_IO_FAST, /* same op code as for DUAL_IO_FAST */ + CMD_READ_QUAD_IO_FAST, /* same op code as for QUAD_IO_FAST */ + };
/* Assign spi_flash ops */ #ifndef CONFIG_DM_SPI_FLASH @@ -1115,7 +1118,7 @@ int spi_flash_scan(struct spi_flash *flash) flash->sector_size = flash->erase_size;
/* Look for the fastest read cmd */ - cmd = fls(params->e_rd_cmd & spi->mode_rx); + cmd = fls(params->e_rd_cmd & e_rd_cmd); if (cmd) { cmd = spi_read_cmds_array[cmd - 1]; flash->read_cmd = cmd;

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

This patch adds a helper function to ease the conversion between a number of dummy clock cycles and number of bytes.
Actually this number of bytes depends on both the number of dummy clock cycles and SPI protocol used by (Fast) Read commands.
This new function is exported so it can be used by other drivers.
Signed-off-by: Cyrille Pitchen cyrille.pitchen@atmel.com --- drivers/mtd/spi/sf_internal.h | 6 ++++++ drivers/mtd/spi/spi_flash.c | 11 +++++++++++ 2 files changed, 17 insertions(+)
diff --git a/drivers/mtd/spi/sf_internal.h b/drivers/mtd/spi/sf_internal.h index 26d359707d5b..86a0276f8518 100644 --- a/drivers/mtd/spi/sf_internal.h +++ b/drivers/mtd/spi/sf_internal.h @@ -240,6 +240,12 @@ typedef int (*spi_flash_read_fn)(struct spi_flash *, u32, size_t, void *); int spi_flash_read_alg(struct spi_flash *flash, u32 offset, size_t len, void *data, spi_flash_read_fn read_fn);
+/* + * Helper function to compute the number of dummy bytes from both the number + * of dummy cycles and the SPI protocol used for (Fast) Read operations. + */ +int spi_flash_set_dummy_byte(struct spi_flash *flash, u8 num_dummy_cycles); + #ifdef CONFIG_SPI_FLASH_MTD int spi_flash_mtd_register(struct spi_flash *flash); void spi_flash_mtd_unregister(void); diff --git a/drivers/mtd/spi/spi_flash.c b/drivers/mtd/spi/spi_flash.c index 5d641bbb8301..01a073d81eb9 100644 --- a/drivers/mtd/spi/spi_flash.c +++ b/drivers/mtd/spi/spi_flash.c @@ -882,6 +882,17 @@ int stm_unlock(struct spi_flash *flash, u32 ofs, size_t len) } #endif
+int spi_flash_set_dummy_byte(struct spi_flash *flash, u8 num_dummy_cycles) +{ + u8 dummy_bits = num_dummy_cycles; + + dummy_bits *= SPI_FLASH_PROTO_ADR_FROM_PROTO(flash->read_proto); + if (dummy_bits & 0x7) + return -EINVAL; + + flash->dummy_byte = dummy_bits >> 3; + return 0; +}
#ifdef CONFIG_SPI_FLASH_MACRONIX static int macronix_quad_enable(struct spi_flash *flash)

This patch provides an alternative to support memory >16MiB (>128Mib). Indeed using the Base Address Register changes the internal state of the SPI flash memory. However some early boot loaders expect to access the first memory bank of the SPI flash. Then when another bank has been selected, those boot loader will fail to read the right data.
Using 4byte address opcodes doesn't change the internal state of the SPI flash memory but sill allows to access data above 16MiB.
Signed-off-by: Cyrille Pitchen cyrille.pitchen@atmel.com --- drivers/mtd/spi/Kconfig | 18 ++++++++- drivers/mtd/spi/sf_internal.h | 17 +++++++- drivers/mtd/spi/spi_flash.c | 93 +++++++++++++++++++++++++++++++++++++------ include/spi_flash.h | 2 + 4 files changed, 114 insertions(+), 16 deletions(-)
diff --git a/drivers/mtd/spi/Kconfig b/drivers/mtd/spi/Kconfig index 3f7433cbc214..999c3176adee 100644 --- a/drivers/mtd/spi/Kconfig +++ b/drivers/mtd/spi/Kconfig @@ -34,14 +34,30 @@ config SPI_FLASH
If unsure, say N
+config SPI_FLASH_ABOVE_16MB + bool "Support of >16MB memories" + depends on SPI_FLASH + default n + +choice + prompt "Select >16MB method" + depends on SPI_FLASH_ABOVE_16MB + config SPI_FLASH_BAR bool "SPI flash Bank/Extended address register support" - depends on SPI_FLASH help Enable the SPI flash Bank/Extended address register support. Bank/Extended address registers are used to access the flash which has size > 16MiB in 3-byte addressing.
+config SPI_FLASH_4B_OPCODES + bool "4-byte address op codes support" + help + Replace regular 3-byte address op codes by thier 4-byte address + version when the size of the memory if greater than 16MB. + +endchoice + if SPI_FLASH
config SPI_FLASH_ATMEL diff --git a/drivers/mtd/spi/sf_internal.h b/drivers/mtd/spi/sf_internal.h index 86a0276f8518..a98c011218ce 100644 --- a/drivers/mtd/spi/sf_internal.h +++ b/drivers/mtd/spi/sf_internal.h @@ -62,8 +62,8 @@ enum spi_nor_option_flags { SNOR_F_USE_FSR = BIT(2), };
-#define SPI_FLASH_3B_ADDR_LEN 3 -#define SPI_FLASH_CMD_LEN (1 + SPI_FLASH_3B_ADDR_LEN) +#define SPI_FLASH_4B_ADDR_LEN 4 +#define SPI_FLASH_CMD_LEN (1 + SPI_FLASH_4B_ADDR_LEN) #define SPI_FLASH_16MB_BOUN 0x1000000
/* CFI Manufacture ID's */ @@ -79,6 +79,9 @@ enum spi_nor_option_flags { #define CMD_ERASE_32K 0x52 #define CMD_ERASE_CHIP 0xc7 #define CMD_ERASE_64K 0xd8 +#define CMD_ERASE_4K_4B 0x21 +#define CMD_ERASE_32K_4B 0x5c +#define CMD_ERASE_64K_4B 0xdc
/* Write commands */ #define CMD_WRITE_STATUS 0x01 @@ -86,6 +89,10 @@ enum spi_nor_option_flags { #define CMD_WRITE_DISABLE 0x04 #define CMD_WRITE_ENABLE 0x06 #define CMD_QUAD_PAGE_PROGRAM 0x32 +#define CMD_QUAD_PAGE_PROGRAM_MXIC 0x38 +#define CMD_PAGE_PROGRAM_4B 0x12 +#define CMD_QUAD_PAGE_PROGRAM_4B 0x34 +#define CMD_QUAD_PAGE_PROGRAM_MXIC_4B 0x3e
/* Read commands */ #define CMD_READ_ARRAY_SLOW 0x03 @@ -94,6 +101,12 @@ enum spi_nor_option_flags { #define CMD_READ_DUAL_IO_FAST 0xbb #define CMD_READ_QUAD_OUTPUT_FAST 0x6b #define CMD_READ_QUAD_IO_FAST 0xeb +#define CMD_READ_ARRAY_SLOW_4B 0x13 +#define CMD_READ_ARRAY_FAST_4B 0x0c +#define CMD_READ_DUAL_OUTPUT_FAST_4B 0x3c +#define CMD_READ_DUAL_IO_FAST_4B 0xbc +#define CMD_READ_QUAD_OUTPUT_FAST_4B 0x6c +#define CMD_READ_QUAD_IO_FAST_4B 0xec #define CMD_READ_ID 0x9f #define CMD_READ_STATUS 0x05 #define CMD_READ_STATUS1 0x35 diff --git a/drivers/mtd/spi/spi_flash.c b/drivers/mtd/spi/spi_flash.c index 01a073d81eb9..b4a44e25f190 100644 --- a/drivers/mtd/spi/spi_flash.c +++ b/drivers/mtd/spi/spi_flash.c @@ -22,12 +22,15 @@
DECLARE_GLOBAL_DATA_PTR;
-static void spi_flash_addr(u32 addr, u8 *cmd) +static void spi_flash_addr(const struct spi_flash *flash, u32 addr, u8 *cmd) { + u8 shift = flash->addr_width * 8; + /* cmd[0] is actual command */ - cmd[1] = addr >> 16; - cmd[2] = addr >> 8; - cmd[3] = addr >> 0; + cmd[1] = addr >> (shift - 8); + cmd[2] = addr >> (shift - 16); + cmd[3] = addr >> (shift - 24); + cmd[4] = addr >> (shift - 32); }
static int read_sr(struct spi_flash *flash, u8 *rs) @@ -154,6 +157,62 @@ bar_end: } #endif
+#ifdef CONFIG_SPI_FLASH_4B_OPCODES +struct address_entry { + u8 src_opcode; + u8 dst_opcode; +}; + +static u8 spi_flash_convert_opcode(u8 opcode, + const struct address_entry *entries, + size_t num_entries) +{ + int b, e; + + b = 0; + e = num_entries - 1; + while (b <= e) { + int m = (b + e) >> 1; + const struct address_entry *entry = &entries[m]; + + if (opcode == entry->src_opcode) + return entry->dst_opcode; + + if (opcode < entry->src_opcode) + e = m - 1; + else + b = m + 1; + } + + /* No convertion found */ + return opcode; +} + +static u8 spi_flash_3to4_opcode(u8 opcode) +{ + /* MUST be sorted by 3byte opcode */ +#define ENTRY_3TO4(_opcode) { _opcode, _opcode##_4B } + static const struct address_entry spi_flash_3to4_table[] = { + ENTRY_3TO4(CMD_READ_ARRAY_SLOW), /* 0x03 */ + ENTRY_3TO4(CMD_READ_ARRAY_FAST), /* 0x0b */ + ENTRY_3TO4(CMD_ERASE_4K), /* 0x20 */ + ENTRY_3TO4(CMD_QUAD_PAGE_PROGRAM), /* 0x32 */ + ENTRY_3TO4(CMD_QUAD_PAGE_PROGRAM_MXIC), /* 0x38 */ + ENTRY_3TO4(CMD_READ_DUAL_OUTPUT_FAST), /* 0x3b */ + ENTRY_3TO4(CMD_ERASE_32K), /* 0x52 */ + ENTRY_3TO4(CMD_READ_QUAD_OUTPUT_FAST), /* 0x6b */ + ENTRY_3TO4(CMD_READ_DUAL_IO_FAST), /* 0xbb */ + ENTRY_3TO4(CMD_ERASE_64K), /* 0xd8 */ + ENTRY_3TO4(CMD_READ_QUAD_IO_FAST), /* 0xeb */ + }; +#undef ENTRY_3TO4 + + return spi_flash_convert_opcode(opcode, + spi_flash_3to4_table, + ARRAY_SIZE(spi_flash_3to4_table)); +} +#endif /* CONFIG_SPI_FLASH_4B_OPCODES */ + #ifdef CONFIG_SF_DUAL_FLASH static void spi_flash_dual(struct spi_flash *flash, u32 *addr) { @@ -353,10 +412,11 @@ static int spi_flash_erase_impl(struct spi_flash *flash, u32 offset) { struct spi_slave *spi = flash->spi; u8 cmd[SPI_FLASH_CMD_LEN]; + size_t cmdsz = 1 + flash->addr_width;
cmd[0] = flash->erase_cmd; - spi_flash_addr(offset, cmd); - return spi_flash_cmd_write(spi, cmd, sizeof(cmd), NULL, 0); + spi_flash_addr(flash, offset, cmd); + return spi_flash_cmd_write(spi, cmd, cmdsz, NULL, 0); }
int spi_flash_cmd_erase_ops(struct spi_flash *flash, u32 offset, size_t len) @@ -461,10 +521,10 @@ static int spi_flash_write_impl(struct spi_flash *flash, u32 offset, { struct spi_slave *spi = flash->spi; u8 cmd[SPI_FLASH_CMD_LEN]; - size_t cmdsz = sizeof(cmd); + size_t cmdsz = 1 + flash->addr_width;
cmd[0] = flash->write_cmd; - spi_flash_addr(offset, cmd); + spi_flash_addr(flash, offset, cmd); #ifdef CONFIG_SPI_FLASH_SST if (flash->flags & SNOR_F_SST_WR_2ND) cmdsz = 1; @@ -567,7 +627,7 @@ static int spi_flash_read_impl(struct spi_flash *flash, u32 offset, size_t cmdsz; int ret;
- cmdsz = SPI_FLASH_CMD_LEN + flash->dummy_byte; + cmdsz = 1 + flash->addr_width + flash->dummy_byte; cmd = calloc(1, cmdsz); if (!cmd) { debug("SF: Failed to allocate cmd\n"); @@ -575,7 +635,7 @@ static int spi_flash_read_impl(struct spi_flash *flash, u32 offset, }
cmd[0] = flash->read_cmd; - spi_flash_addr(offset, cmd); + spi_flash_addr(flash, offset, cmd); ret = spi_flash_cmd_read(spi, cmd, cmdsz, buf, len); free(cmd); return ret; @@ -1255,15 +1315,22 @@ int spi_flash_scan(struct spi_flash *flash, u8 e_rd_cmd) puts("\n"); #endif
-#ifndef CONFIG_SPI_FLASH_BAR + /* Set number of bytes for address and convert opcodes if needed */ + flash->addr_width = 3; if (((flash->dual_flash == SF_SINGLE_FLASH) && (flash->size > SPI_FLASH_16MB_BOUN)) || ((flash->dual_flash > SF_SINGLE_FLASH) && (flash->size > SPI_FLASH_16MB_BOUN << 1))) { +#if defined(CONFIG_SPI_FLASH_4B_OPCODES) + flash->addr_width = 4; + flash->read_cmd = spi_flash_3to4_opcode(flash->read_cmd); + flash->write_cmd = spi_flash_3to4_opcode(flash->write_cmd); + flash->erase_cmd = spi_flash_3to4_opcode(flash->erase_cmd); +#elif !defined(CONFIG_SPI_FLASH_BAR) puts("SF: Warning - Only lower 16MiB accessible,"); - puts(" Full access #define CONFIG_SPI_FLASH_BAR\n"); - } + puts(" Full access #define CONFIG_SPI_FLASH_ABOVE_16MB\n"); #endif + }
return ret; } diff --git a/include/spi_flash.h b/include/spi_flash.h index 945cc07ee8b2..5db06e5133f3 100644 --- a/include/spi_flash.h +++ b/include/spi_flash.h @@ -88,6 +88,7 @@ enum spi_flash_protocol { * @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 + * @addr_width Number of address bytes (typically 3 or 4) * @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 @@ -117,6 +118,7 @@ struct spi_flash { u32 page_size; u32 sector_size; u32 erase_size; + u32 addr_width; #ifdef CONFIG_SPI_FLASH_BAR u8 bank_read_cmd; u8 bank_write_cmd;

This patch provides an alternative to support memory >16MiB (>128Mib). Indeed using the Base Address Register changes the internal state of the SPI flash memory. However some early boot loaders expect to access the first memory bank of the SPI flash. Then when another bank has been selected, those boot loader will fail to read the right data.
Using 4byte address opcodes doesn't change the internal state of the SPI flash memory but sill allows to access data above 16MiB.
Signed-off-by: Cyrille Pitchen cyrille.pitchen@atmel.com ---
ChangeLog:
v1 -> v2: - add missing entry for PAGE PROGRAM op code
drivers/mtd/spi/Kconfig | 18 ++++++++- drivers/mtd/spi/sf_internal.h | 17 +++++++- drivers/mtd/spi/spi_flash.c | 94 +++++++++++++++++++++++++++++++++++++------ include/spi_flash.h | 2 + 4 files changed, 115 insertions(+), 16 deletions(-)
diff --git a/drivers/mtd/spi/Kconfig b/drivers/mtd/spi/Kconfig index 3f7433cbc214..999c3176adee 100644 --- a/drivers/mtd/spi/Kconfig +++ b/drivers/mtd/spi/Kconfig @@ -34,14 +34,30 @@ config SPI_FLASH
If unsure, say N
+config SPI_FLASH_ABOVE_16MB + bool "Support of >16MB memories" + depends on SPI_FLASH + default n + +choice + prompt "Select >16MB method" + depends on SPI_FLASH_ABOVE_16MB + config SPI_FLASH_BAR bool "SPI flash Bank/Extended address register support" - depends on SPI_FLASH help Enable the SPI flash Bank/Extended address register support. Bank/Extended address registers are used to access the flash which has size > 16MiB in 3-byte addressing.
+config SPI_FLASH_4B_OPCODES + bool "4-byte address op codes support" + help + Replace regular 3-byte address op codes by thier 4-byte address + version when the size of the memory if greater than 16MB. + +endchoice + if SPI_FLASH
config SPI_FLASH_ATMEL diff --git a/drivers/mtd/spi/sf_internal.h b/drivers/mtd/spi/sf_internal.h index 86a0276f8518..a98c011218ce 100644 --- a/drivers/mtd/spi/sf_internal.h +++ b/drivers/mtd/spi/sf_internal.h @@ -62,8 +62,8 @@ enum spi_nor_option_flags { SNOR_F_USE_FSR = BIT(2), };
-#define SPI_FLASH_3B_ADDR_LEN 3 -#define SPI_FLASH_CMD_LEN (1 + SPI_FLASH_3B_ADDR_LEN) +#define SPI_FLASH_4B_ADDR_LEN 4 +#define SPI_FLASH_CMD_LEN (1 + SPI_FLASH_4B_ADDR_LEN) #define SPI_FLASH_16MB_BOUN 0x1000000
/* CFI Manufacture ID's */ @@ -79,6 +79,9 @@ enum spi_nor_option_flags { #define CMD_ERASE_32K 0x52 #define CMD_ERASE_CHIP 0xc7 #define CMD_ERASE_64K 0xd8 +#define CMD_ERASE_4K_4B 0x21 +#define CMD_ERASE_32K_4B 0x5c +#define CMD_ERASE_64K_4B 0xdc
/* Write commands */ #define CMD_WRITE_STATUS 0x01 @@ -86,6 +89,10 @@ enum spi_nor_option_flags { #define CMD_WRITE_DISABLE 0x04 #define CMD_WRITE_ENABLE 0x06 #define CMD_QUAD_PAGE_PROGRAM 0x32 +#define CMD_QUAD_PAGE_PROGRAM_MXIC 0x38 +#define CMD_PAGE_PROGRAM_4B 0x12 +#define CMD_QUAD_PAGE_PROGRAM_4B 0x34 +#define CMD_QUAD_PAGE_PROGRAM_MXIC_4B 0x3e
/* Read commands */ #define CMD_READ_ARRAY_SLOW 0x03 @@ -94,6 +101,12 @@ enum spi_nor_option_flags { #define CMD_READ_DUAL_IO_FAST 0xbb #define CMD_READ_QUAD_OUTPUT_FAST 0x6b #define CMD_READ_QUAD_IO_FAST 0xeb +#define CMD_READ_ARRAY_SLOW_4B 0x13 +#define CMD_READ_ARRAY_FAST_4B 0x0c +#define CMD_READ_DUAL_OUTPUT_FAST_4B 0x3c +#define CMD_READ_DUAL_IO_FAST_4B 0xbc +#define CMD_READ_QUAD_OUTPUT_FAST_4B 0x6c +#define CMD_READ_QUAD_IO_FAST_4B 0xec #define CMD_READ_ID 0x9f #define CMD_READ_STATUS 0x05 #define CMD_READ_STATUS1 0x35 diff --git a/drivers/mtd/spi/spi_flash.c b/drivers/mtd/spi/spi_flash.c index 01a073d81eb9..8c327961302c 100644 --- a/drivers/mtd/spi/spi_flash.c +++ b/drivers/mtd/spi/spi_flash.c @@ -22,12 +22,15 @@
DECLARE_GLOBAL_DATA_PTR;
-static void spi_flash_addr(u32 addr, u8 *cmd) +static void spi_flash_addr(const struct spi_flash *flash, u32 addr, u8 *cmd) { + u8 shift = flash->addr_width * 8; + /* cmd[0] is actual command */ - cmd[1] = addr >> 16; - cmd[2] = addr >> 8; - cmd[3] = addr >> 0; + cmd[1] = addr >> (shift - 8); + cmd[2] = addr >> (shift - 16); + cmd[3] = addr >> (shift - 24); + cmd[4] = addr >> (shift - 32); }
static int read_sr(struct spi_flash *flash, u8 *rs) @@ -154,6 +157,63 @@ bar_end: } #endif
+#ifdef CONFIG_SPI_FLASH_4B_OPCODES +struct address_entry { + u8 src_opcode; + u8 dst_opcode; +}; + +static u8 spi_flash_convert_opcode(u8 opcode, + const struct address_entry *entries, + size_t num_entries) +{ + int b, e; + + b = 0; + e = num_entries - 1; + while (b <= e) { + int m = (b + e) >> 1; + const struct address_entry *entry = &entries[m]; + + if (opcode == entry->src_opcode) + return entry->dst_opcode; + + if (opcode < entry->src_opcode) + e = m - 1; + else + b = m + 1; + } + + /* No convertion found */ + return opcode; +} + +static u8 spi_flash_3to4_opcode(u8 opcode) +{ + /* MUST be sorted by 3byte opcode */ +#define ENTRY_3TO4(_opcode) { _opcode, _opcode##_4B } + static const struct address_entry spi_flash_3to4_table[] = { + ENTRY_3TO4(CMD_PAGE_PROGRAM), /* 0x02 */ + ENTRY_3TO4(CMD_READ_ARRAY_SLOW), /* 0x03 */ + ENTRY_3TO4(CMD_READ_ARRAY_FAST), /* 0x0b */ + ENTRY_3TO4(CMD_ERASE_4K), /* 0x20 */ + ENTRY_3TO4(CMD_QUAD_PAGE_PROGRAM), /* 0x32 */ + ENTRY_3TO4(CMD_QUAD_PAGE_PROGRAM_MXIC), /* 0x38 */ + ENTRY_3TO4(CMD_READ_DUAL_OUTPUT_FAST), /* 0x3b */ + ENTRY_3TO4(CMD_ERASE_32K), /* 0x52 */ + ENTRY_3TO4(CMD_READ_QUAD_OUTPUT_FAST), /* 0x6b */ + ENTRY_3TO4(CMD_READ_DUAL_IO_FAST), /* 0xbb */ + ENTRY_3TO4(CMD_ERASE_64K), /* 0xd8 */ + ENTRY_3TO4(CMD_READ_QUAD_IO_FAST), /* 0xeb */ + }; +#undef ENTRY_3TO4 + + return spi_flash_convert_opcode(opcode, + spi_flash_3to4_table, + ARRAY_SIZE(spi_flash_3to4_table)); +} +#endif /* CONFIG_SPI_FLASH_4B_OPCODES */ + #ifdef CONFIG_SF_DUAL_FLASH static void spi_flash_dual(struct spi_flash *flash, u32 *addr) { @@ -353,10 +413,11 @@ static int spi_flash_erase_impl(struct spi_flash *flash, u32 offset) { struct spi_slave *spi = flash->spi; u8 cmd[SPI_FLASH_CMD_LEN]; + size_t cmdsz = 1 + flash->addr_width;
cmd[0] = flash->erase_cmd; - spi_flash_addr(offset, cmd); - return spi_flash_cmd_write(spi, cmd, sizeof(cmd), NULL, 0); + spi_flash_addr(flash, offset, cmd); + return spi_flash_cmd_write(spi, cmd, cmdsz, NULL, 0); }
int spi_flash_cmd_erase_ops(struct spi_flash *flash, u32 offset, size_t len) @@ -461,10 +522,10 @@ static int spi_flash_write_impl(struct spi_flash *flash, u32 offset, { struct spi_slave *spi = flash->spi; u8 cmd[SPI_FLASH_CMD_LEN]; - size_t cmdsz = sizeof(cmd); + size_t cmdsz = 1 + flash->addr_width;
cmd[0] = flash->write_cmd; - spi_flash_addr(offset, cmd); + spi_flash_addr(flash, offset, cmd); #ifdef CONFIG_SPI_FLASH_SST if (flash->flags & SNOR_F_SST_WR_2ND) cmdsz = 1; @@ -567,7 +628,7 @@ static int spi_flash_read_impl(struct spi_flash *flash, u32 offset, size_t cmdsz; int ret;
- cmdsz = SPI_FLASH_CMD_LEN + flash->dummy_byte; + cmdsz = 1 + flash->addr_width + flash->dummy_byte; cmd = calloc(1, cmdsz); if (!cmd) { debug("SF: Failed to allocate cmd\n"); @@ -575,7 +636,7 @@ static int spi_flash_read_impl(struct spi_flash *flash, u32 offset, }
cmd[0] = flash->read_cmd; - spi_flash_addr(offset, cmd); + spi_flash_addr(flash, offset, cmd); ret = spi_flash_cmd_read(spi, cmd, cmdsz, buf, len); free(cmd); return ret; @@ -1255,15 +1316,22 @@ int spi_flash_scan(struct spi_flash *flash, u8 e_rd_cmd) puts("\n"); #endif
-#ifndef CONFIG_SPI_FLASH_BAR + /* Set number of bytes for address and convert opcodes if needed */ + flash->addr_width = 3; if (((flash->dual_flash == SF_SINGLE_FLASH) && (flash->size > SPI_FLASH_16MB_BOUN)) || ((flash->dual_flash > SF_SINGLE_FLASH) && (flash->size > SPI_FLASH_16MB_BOUN << 1))) { +#if defined(CONFIG_SPI_FLASH_4B_OPCODES) + flash->addr_width = 4; + flash->read_cmd = spi_flash_3to4_opcode(flash->read_cmd); + flash->write_cmd = spi_flash_3to4_opcode(flash->write_cmd); + flash->erase_cmd = spi_flash_3to4_opcode(flash->erase_cmd); +#elif !defined(CONFIG_SPI_FLASH_BAR) puts("SF: Warning - Only lower 16MiB accessible,"); - puts(" Full access #define CONFIG_SPI_FLASH_BAR\n"); - } + puts(" Full access #define CONFIG_SPI_FLASH_ABOVE_16MB\n"); #endif + }
return ret; } diff --git a/include/spi_flash.h b/include/spi_flash.h index 945cc07ee8b2..5db06e5133f3 100644 --- a/include/spi_flash.h +++ b/include/spi_flash.h @@ -88,6 +88,7 @@ enum spi_flash_protocol { * @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 + * @addr_width Number of address bytes (typically 3 or 4) * @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 @@ -117,6 +118,7 @@ struct spi_flash { u32 page_size; u32 sector_size; u32 erase_size; + u32 addr_width; #ifdef CONFIG_SPI_FLASH_BAR u8 bank_read_cmd; u8 bank_write_cmd;

This is just a transitional patch to add manufacturer dedicated support for QSPI memories.
Indeed, using Quad SPI protocols is slighty more complicated than simply enabling the Quad I/O or QPI mode. We should also take care about the number of dummy cycles. For instance, some Spansion QSPI memories use a factory default value of 6 dummy cycles.
Besides we have to care about the mode cycles: special kind of dummy cycles which make the SPI flash memory enter/leaver its Continuous Read/ Enhanced Performance mode. This mode is actually used by XIP operations but not relevant in the MTD layer. However we should prevent the SPI flash memory from entering Continuous Read mode by mistake. Depending on the manufacturer, it can be done by using the right Fast Read op code (Winbond) or by updating a dedicated bit in one configuration register (Micron).
Also the QSPI manufacturers don't support the exact same SPI protocols for Fast Read operations. We need to provide a mean to tune SPI protocols and the op codes by manufacturer.
For all these reasons, future patches are to come to add QSPI support by manufacturer. Till then, the legacy code is still used as a fallback implementation.
Signed-off-by: Cyrille Pitchen cyrille.pitchen@atmel.com --- drivers/mtd/spi/spi_flash.c | 155 ++++++++++++++++++++++++++++---------------- 1 file changed, 99 insertions(+), 56 deletions(-)
diff --git a/drivers/mtd/spi/spi_flash.c b/drivers/mtd/spi/spi_flash.c index b4a44e25f190..50250491d228 100644 --- a/drivers/mtd/spi/spi_flash.c +++ b/drivers/mtd/spi/spi_flash.c @@ -1033,6 +1033,98 @@ static int set_quad_mode(struct spi_flash *flash, u8 idcode0) } }
+/* Transitional function to be removed */ +static int spi_flash_setup_deprecated(struct spi_flash *flash, + const struct spi_flash_params *params, + int best_match) +{ + struct spi_slave *spi = flash->spi; + u8 idcode0 = params->jedec >> 16; + u8 cmd; + int ret; + static u8 spi_read_cmds_array[] = { + CMD_READ_ARRAY_SLOW, + CMD_READ_ARRAY_FAST, + CMD_READ_DUAL_OUTPUT_FAST, + CMD_READ_QUAD_OUTPUT_FAST, + CMD_READ_DUAL_IO_FAST, + CMD_READ_QUAD_IO_FAST, + CMD_READ_DUAL_IO_FAST, /* same op code as for DUAL_IO_FAST */ + CMD_READ_QUAD_IO_FAST, /* same op code as for QUAD_IO_FAST */ + }; + + /* Look for the fastest read cmd */ + if (best_match) { + cmd = spi_read_cmds_array[best_match - 1]; + flash->read_cmd = cmd; + } else { + /* Go for default supported read cmd */ + flash->read_cmd = CMD_READ_ARRAY_FAST; + } + + /* Not require to look for fastest only two write cmds yet */ + if (params->flags & WR_QPP && spi->mode & SPI_TX_QUAD) + flash->write_cmd = CMD_QUAD_PAGE_PROGRAM; + else + /* Go for default supported write cmd */ + flash->write_cmd = CMD_PAGE_PROGRAM; + + /* Set the quad enable bit - only for quad commands */ + if ((flash->read_cmd == CMD_READ_QUAD_OUTPUT_FAST) || + (flash->read_cmd == CMD_READ_QUAD_IO_FAST) || + (flash->write_cmd == CMD_QUAD_PAGE_PROGRAM)) { + ret = set_quad_mode(flash, idcode0); + if (ret) { + debug("SF: Fail to set QEB for %02x\n", idcode0); + return -EINVAL; + } + } + + /* Read dummy_byte: dummy byte is determined based on the + * dummy cycles of a particular command. + * Fast commands - dummy_byte = dummy_cycles/8 + * I/O commands- dummy_byte = (dummy_cycles * no.of lines)/8 + * For I/O commands except cmd[0] everything goes on no.of lines + * based on particular command but incase of fast commands except + * data all go on single line irrespective of command. + */ + switch (flash->read_cmd) { + case CMD_READ_QUAD_IO_FAST: + flash->dummy_byte = 2; + break; + case CMD_READ_ARRAY_SLOW: + flash->dummy_byte = 0; + break; + default: + flash->dummy_byte = 1; + } + + return 0; +} + +static int spi_flash_setup(struct spi_flash *flash, + const struct spi_flash_params *params, + u8 e_rd_cmd) +{ + int match = fls(params->e_rd_cmd & e_rd_cmd); + + /* + * The (Fast) Read and Page Program opcodes to be used depends on the + * manufacturer. + */ + switch (params->jedec >> 16) { + /* + * TODO: Manufacturer dedicated setup will be added HERE by + * further patches. + */ + + default: + return spi_flash_setup_deprecated(flash, params, match); + } + + return 0; +} + #if CONFIG_IS_ENABLED(OF_CONTROL) int spi_flash_decode_fdt(const void *blob, struct spi_flash *flash) { @@ -1090,18 +1182,8 @@ int spi_flash_scan(struct spi_flash *flash, u8 e_rd_cmd) const struct spi_flash_params *params; const struct spi_flash_read_id_config *cfg; u16 jedec, ext_jedec; - u8 cmd, idcode[5]; + u8 idcode[5]; int ret; - static u8 spi_read_cmds_array[] = { - CMD_READ_ARRAY_SLOW, - CMD_READ_ARRAY_FAST, - CMD_READ_DUAL_OUTPUT_FAST, - CMD_READ_QUAD_OUTPUT_FAST, - CMD_READ_DUAL_IO_FAST, - CMD_READ_QUAD_IO_FAST, - CMD_READ_DUAL_IO_FAST, /* same op code as for DUAL_IO_FAST */ - CMD_READ_QUAD_IO_FAST, /* same op code as for QUAD_IO_FAST */ - };
/* Assign spi_flash ops */ #ifndef CONFIG_DM_SPI_FLASH @@ -1238,52 +1320,13 @@ int spi_flash_scan(struct spi_flash *flash, u8 e_rd_cmd) /* Now erase size becomes valid sector size */ flash->sector_size = flash->erase_size;
- /* Look for the fastest read cmd */ - cmd = fls(params->e_rd_cmd & e_rd_cmd); - if (cmd) { - cmd = spi_read_cmds_array[cmd - 1]; - flash->read_cmd = cmd; - } else { - /* Go for default supported read cmd */ - flash->read_cmd = CMD_READ_ARRAY_FAST; - } - - /* Not require to look for fastest only two write cmds yet */ - if (params->flags & WR_QPP && spi->mode & SPI_TX_QUAD) - flash->write_cmd = CMD_QUAD_PAGE_PROGRAM; - else - /* Go for default supported write cmd */ - flash->write_cmd = CMD_PAGE_PROGRAM; - - /* Set the quad enable bit - only for quad commands */ - if ((flash->read_cmd == CMD_READ_QUAD_OUTPUT_FAST) || - (flash->read_cmd == CMD_READ_QUAD_IO_FAST) || - (flash->write_cmd == CMD_QUAD_PAGE_PROGRAM)) { - ret = set_quad_mode(flash, idcode[0]); - if (ret) { - debug("SF: Fail to set QEB for %02x\n", idcode[0]); - return -EINVAL; - } - } - - /* Read dummy_byte: dummy byte is determined based on the - * dummy cycles of a particular command. - * Fast commands - dummy_byte = dummy_cycles/8 - * I/O commands- dummy_byte = (dummy_cycles * no.of lines)/8 - * For I/O commands except cmd[0] everything goes on no.of lines - * based on particular command but incase of fast commands except - * data all go on single line irrespective of command. + /* + * Setup (Fast) Read & Page Program opcode and protocols. + * Also set the right number of dummy/mode cycles. */ - switch (flash->read_cmd) { - case CMD_READ_QUAD_IO_FAST: - flash->dummy_byte = 2; - break; - case CMD_READ_ARRAY_SLOW: - flash->dummy_byte = 0; - break; - default: - flash->dummy_byte = 1; - } + ret = spi_flash_setup(flash, params, e_rd_cmd); + if (ret < 0) + return ret;
#ifdef CONFIG_SPI_FLASH_STMICRO if (params->flags & E_FSR)

This patch provides support of Micron QSPI memories.
Signed-off-by: Cyrille Pitchen cyrille.pitchen@atmel.com --- drivers/mtd/spi/Makefile | 1 + drivers/mtd/spi/sf_internal.h | 24 +++++ drivers/mtd/spi/sf_micron.c | 222 ++++++++++++++++++++++++++++++++++++++++++ drivers/mtd/spi/spi_flash.c | 13 +-- 4 files changed, 251 insertions(+), 9 deletions(-) create mode 100644 drivers/mtd/spi/sf_micron.c
diff --git a/drivers/mtd/spi/Makefile b/drivers/mtd/spi/Makefile index c665836f9560..90266d619ddf 100644 --- a/drivers/mtd/spi/Makefile +++ b/drivers/mtd/spi/Makefile @@ -13,6 +13,7 @@ obj-$(CONFIG_SPL_SPI_BOOT) += fsl_espi_spl.o endif
obj-$(CONFIG_SPI_FLASH) += sf_probe.o spi_flash.o sf_params.o sf.o +obj-$(CONFIG_SPI_FLASH_STMICRO) += sf_micron.o obj-$(CONFIG_SPI_FLASH_DATAFLASH) += sf_dataflash.o obj-$(CONFIG_SPI_FLASH_MTD) += sf_mtd.o obj-$(CONFIG_SPI_FLASH_SANDBOX) += sandbox.o diff --git a/drivers/mtd/spi/sf_internal.h b/drivers/mtd/spi/sf_internal.h index a98c011218ce..afee8b69ca6b 100644 --- a/drivers/mtd/spi/sf_internal.h +++ b/drivers/mtd/spi/sf_internal.h @@ -142,6 +142,24 @@ enum spi_nor_option_flags { # define CMD_SST_AAI_WP 0xAD /* Auto Address Incr Word Program */ #endif
+/* Micron specific */ +#ifdef CONFIG_SPI_FLASH_STMICRO + +/* Volatile Configuration Register (VCR) */ +#define CMD_MICRON_WR_VCR 0x81 +#define CMD_MICRON_RD_VCR 0x85 +#define MICRON_VCR_XIP BIT(3) +#define MICRON_VCR_DUMMIES 0xf0 +#define MICRON_VCR_DUMMIES_(x) (((x) << 4) & MICRON_VCR_DUMMIES) + +/* Enhanced Volatile Configuraiton Register (EVCR) */ +#define CMD_MICRON_WR_EVCR 0x61 +#define CMD_MICRON_RD_EVCR 0x65 +#define MICRON_EVCR_QUAD_DIS BIT(7) +#define MICRON_EVCR_DUAL_DIS BIT(6) +#endif + + /** * struct spi_flash_params - SPI/QSPI flash device params structure * @@ -264,6 +282,12 @@ int spi_flash_mtd_register(struct spi_flash *flash); void spi_flash_mtd_unregister(void); #endif
+#ifdef CONFIG_SPI_FLASH_STMICRO +int spi_flash_setup_micron(struct spi_flash *flash, + const struct spi_flash_params *params, + int best_match); +#endif + /** * spi_flash_scan - scan the SPI FLASH * @flash: the spi flash structure diff --git a/drivers/mtd/spi/sf_micron.c b/drivers/mtd/spi/sf_micron.c new file mode 100644 index 000000000000..b399b6ff52fe --- /dev/null +++ b/drivers/mtd/spi/sf_micron.c @@ -0,0 +1,222 @@ +/* + * Micron SPI NOR flash support + * + * Copyright (C) 2016 Cyrille Pitchen cyrille.pitchen@atmel.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <errno.h> +#include <spi.h> +#include <spi_flash.h> + +#include "sf_internal.h" + +static int spi_flash_micron_set_dummy_cycles(struct spi_flash *flash, + u8 num_dummy_cycles) +{ + u8 vcr, mask, value; + int ret; + + /* Set the number of dummy cycles and disable XIP */ + mask = (MICRON_VCR_DUMMIES | MICRON_VCR_XIP); + value = MICRON_VCR_DUMMIES_(num_dummy_cycles) | MICRON_VCR_XIP; + + /* Check current VCR value */ + ret = spi_flash_read_reg(flash, CMD_MICRON_RD_VCR, 1, &vcr); + if (ret) + goto end; + + if ((vcr & mask) == value) + goto end; + + /* VCR update is needed */ + vcr = (vcr & ~mask) | value; + ret = spi_flash_update_reg(flash, CMD_MICRON_WR_VCR, 1, &vcr); + if (ret) + goto end; + + /* Verify update */ + ret = spi_flash_read_reg(flash, CMD_MICRON_RD_VCR, 1, &vcr); + if (ret) + goto end; + + if ((vcr & mask) != value) + ret = -EIO; + +end: + if (ret) + printf("SF: failed to set the dummy cycles on Micron memory!\n"); + + return ret; +} + +static int spi_flash_micron_set_protocol(struct spi_flash *flash, + enum spi_flash_protocol proto, + u8 mask, u8 value) +{ + u8 evcr; + int ret; + + /* Check current EVCR value */ + ret = spi_flash_read_reg(flash, CMD_MICRON_RD_EVCR, 1, &evcr); + if (ret) + goto end; + + if ((evcr & mask) == value) + goto end; + + /* EVCR update is needed */ + ret = spi_flash_cmd_write_enable(flash); + if (ret) + goto end; + + evcr = (evcr & ~mask) | value; + ret = spi_flash_write_reg(flash, CMD_MICRON_WR_EVCR, 1, &evcr); + if (ret) + goto end; + + /* + * Don't forget to update the protocol HERE otherwise next commands + * will fail. + */ + flash->reg_proto = proto; + + /* Wait for ready status */ + ret = spi_flash_wait_ready(flash); + if (ret) + goto end; + + /* Verify update */ + ret = spi_flash_read_reg(flash, CMD_MICRON_RD_EVCR, 1, &evcr); + if (ret) + goto end; + + if ((evcr & mask) != value) + ret = -EIO; + +end: + if (ret) { + printf("SF: failed to set I/O mode on Micron memory!\n"); + return ret; + } + + flash->read_proto = proto; + flash->write_proto = proto; + flash->erase_proto = proto; + return 0; +} + +static inline int spi_flash_micron_set_ext_spi_mode(struct spi_flash *flash) +{ + enum spi_flash_protocol proto = SPI_FLASH_PROTO_1_1_1; + u8 mask = (MICRON_EVCR_QUAD_DIS | MICRON_EVCR_DUAL_DIS); + u8 value = mask; + + return spi_flash_micron_set_protocol(flash, proto, mask, value); +} + +static inline int spi_flash_micron_set_dual_mode(struct spi_flash *flash) +{ + enum spi_flash_protocol proto = SPI_FLASH_PROTO_2_2_2; + u8 mask = (MICRON_EVCR_QUAD_DIS | MICRON_EVCR_DUAL_DIS); + u8 value = MICRON_EVCR_QUAD_DIS; + + return spi_flash_micron_set_protocol(flash, proto, mask, value); +} + +static inline int spi_flash_micron_set_quad_mode(struct spi_flash *flash) +{ + enum spi_flash_protocol proto = SPI_FLASH_PROTO_4_4_4; + u8 mask = MICRON_EVCR_QUAD_DIS; + u8 value = 0; + + return spi_flash_micron_set_protocol(flash, proto, mask, value); +} + +int spi_flash_setup_micron(struct spi_flash *flash, + const struct spi_flash_params *params, + int best_match) +{ + u16 read_cmd = best_match ? (1 << (best_match - 1)) : ARRAY_FAST; + u8 dummy_cycles = (read_cmd == ARRAY_SLOW) ? 0 : 8; + int ret = 0; + + /* Configure (Fast) Read operations */ + switch (read_cmd) { + case QUAD_CMD_FAST: + if (flash->reg_proto != SPI_FLASH_PROTO_4_4_4) + ret = spi_flash_micron_set_quad_mode(flash); + flash->read_cmd = CMD_READ_QUAD_IO_FAST; + break; + + case QUAD_IO_FAST: + if (flash->reg_proto != SPI_FLASH_PROTO_1_1_1) + ret = spi_flash_micron_set_ext_spi_mode(flash); + flash->read_proto = SPI_FLASH_PROTO_1_4_4; + flash->read_cmd = CMD_READ_QUAD_IO_FAST; + break; + + case QUAD_OUTPUT_FAST: + if (flash->reg_proto != SPI_FLASH_PROTO_1_1_1) + ret = spi_flash_micron_set_ext_spi_mode(flash); + flash->read_proto = SPI_FLASH_PROTO_1_1_4; + flash->read_cmd = CMD_READ_QUAD_OUTPUT_FAST; + break; + + case DUAL_CMD_FAST: + if (flash->reg_proto != SPI_FLASH_PROTO_2_2_2) + ret = spi_flash_micron_set_dual_mode(flash); + flash->read_cmd = CMD_READ_DUAL_IO_FAST; + break; + + case DUAL_IO_FAST: + if (flash->reg_proto != SPI_FLASH_PROTO_1_1_1) + ret = spi_flash_micron_set_ext_spi_mode(flash); + flash->read_proto = SPI_FLASH_PROTO_1_2_2; + flash->read_cmd = CMD_READ_DUAL_IO_FAST; + break; + + case DUAL_OUTPUT_FAST: + if (flash->reg_proto != SPI_FLASH_PROTO_1_1_1) + ret = spi_flash_micron_set_ext_spi_mode(flash); + flash->read_proto = SPI_FLASH_PROTO_1_1_2; + flash->read_cmd = CMD_READ_DUAL_OUTPUT_FAST; + break; + + case ARRAY_FAST: + flash->read_proto = SPI_FLASH_PROTO_1_1_1; + flash->read_cmd = CMD_READ_ARRAY_FAST; + break; + + case ARRAY_SLOW: + flash->read_proto = SPI_FLASH_PROTO_1_1_1; + flash->read_cmd = CMD_READ_ARRAY_SLOW; + break; + + default: + return -EINVAL; + } + + if (ret) + return ret; + + /* Configure Page Program operations */ + if (flash->write_proto == SPI_FLASH_PROTO_4_4_4 || + flash->write_proto == SPI_FLASH_PROTO_2_2_2) { + flash->write_cmd = CMD_PAGE_PROGRAM; + } else if ((flash->spi->mode & SPI_TX_QUAD) && + (params->flags & WR_QPP)) { + flash->write_cmd = CMD_QUAD_PAGE_PROGRAM; + flash->write_proto = SPI_FLASH_PROTO_1_1_4; + } else { + flash->write_cmd = CMD_PAGE_PROGRAM; + flash->write_proto = SPI_FLASH_PROTO_1_1_1; + } + + /* Set number of dummy cycles for Fast Read operations */ + if (params->e_rd_cmd & (QUAD_CMD_FAST | DUAL_CMD_FAST)) + ret = spi_flash_micron_set_dummy_cycles(flash, dummy_cycles); + return ret ? : spi_flash_set_dummy_byte(flash, dummy_cycles); +} diff --git a/drivers/mtd/spi/spi_flash.c b/drivers/mtd/spi/spi_flash.c index 50250491d228..922afc811a0b 100644 --- a/drivers/mtd/spi/spi_flash.c +++ b/drivers/mtd/spi/spi_flash.c @@ -1022,11 +1022,6 @@ static int set_quad_mode(struct spi_flash *flash, u8 idcode0) case SPI_FLASH_CFI_MFR_WINBOND: return spansion_quad_enable(flash); #endif -#ifdef CONFIG_SPI_FLASH_STMICRO - case SPI_FLASH_CFI_MFR_STMICRO: - debug("SF: QEB is volatile for %02x flash\n", idcode0); - return 0; -#endif default: printf("SF: Need set QEB func for %02x flash\n", idcode0); return -1; @@ -1113,10 +1108,10 @@ static int spi_flash_setup(struct spi_flash *flash, * manufacturer. */ switch (params->jedec >> 16) { - /* - * TODO: Manufacturer dedicated setup will be added HERE by - * further patches. - */ +#ifdef CONFIG_SPI_FLASH_STMICRO + case SPI_FLASH_CFI_MFR_STMICRO: + return spi_flash_setup_micron(flash, params, match); +#endif
default: return spi_flash_setup_deprecated(flash, params, match);

Add the new get_qpsi_clk_rate() function which will be needed by the future Atmel QSPI controller driver.
Signed-off-by: Cyrille Pitchen cyrille.pitchen@atmel.com --- arch/arm/mach-at91/include/mach/clk.h | 5 +++++ 1 file changed, 5 insertions(+)
diff --git a/arch/arm/mach-at91/include/mach/clk.h b/arch/arm/mach-at91/include/mach/clk.h index 8577c74b47b7..aba7656e72f7 100644 --- a/arch/arm/mach-at91/include/mach/clk.h +++ b/arch/arm/mach-at91/include/mach/clk.h @@ -99,6 +99,11 @@ static inline unsigned long get_spi_clk_rate(unsigned int dev_id) return get_mck_clk_rate(); }
+static inline unsigned long get_qspi_clk_rate(unsigned int dev_id) +{ + return get_mck_clk_rate(); +} + static inline unsigned long get_twi_clk_rate(unsigned int dev_id) { if (get_h32mxdiv())

This patch adds support of the Atmel QSPI controller.
Signed-off-by: Cyrille Pitchen cyrille.pitchen@atmel.com --- drivers/mtd/spi/Makefile | 1 + drivers/mtd/spi/atmel_qspi_flash.c | 432 +++++++++++++++++++++++++++++++++++++ drivers/spi/Kconfig | 9 + drivers/spi/Makefile | 1 + drivers/spi/atmel_qspi.c | 150 +++++++++++++ drivers/spi/atmel_qspi.h | 145 +++++++++++++ 6 files changed, 738 insertions(+) create mode 100644 drivers/mtd/spi/atmel_qspi_flash.c create mode 100644 drivers/spi/atmel_qspi.c create mode 100644 drivers/spi/atmel_qspi.h
diff --git a/drivers/mtd/spi/Makefile b/drivers/mtd/spi/Makefile index 90266d619ddf..207e1d556f3f 100644 --- a/drivers/mtd/spi/Makefile +++ b/drivers/mtd/spi/Makefile @@ -17,3 +17,4 @@ obj-$(CONFIG_SPI_FLASH_STMICRO) += sf_micron.o obj-$(CONFIG_SPI_FLASH_DATAFLASH) += sf_dataflash.o obj-$(CONFIG_SPI_FLASH_MTD) += sf_mtd.o obj-$(CONFIG_SPI_FLASH_SANDBOX) += sandbox.o +obj-$(CONFIG_ATMEL_QSPI) += atmel_qspi_flash.o diff --git a/drivers/mtd/spi/atmel_qspi_flash.c b/drivers/mtd/spi/atmel_qspi_flash.c new file mode 100644 index 000000000000..97f85ae2e30c --- /dev/null +++ b/drivers/mtd/spi/atmel_qspi_flash.c @@ -0,0 +1,432 @@ +#include <common.h> +#include <errno.h> +#include <spi.h> +#include <spi_flash.h> + +#include "sf_internal.h" +#include "../../spi/atmel_qspi.h" + +struct atmel_qspi_command { + union { + struct { + unsigned int instruction:1; + unsigned int address:3; + unsigned int mode:1; + unsigned int dummy:1; + unsigned int data:1; + unsigned int reserved:25; + } bits; + unsigned int word; + } enable; + unsigned char instruction; + unsigned char mode; + unsigned char num_mode_cycles; + unsigned char num_dummy_cycles; + unsigned int address; + + size_t buf_len; + const void *tx_buf; + void *rx_buf; + + enum spi_flash_protocol protocol; + u32 ifr_tfrtype; +}; + + +static void atmel_qspi_memcpy_fromio(void *dst, unsigned long src, size_t len) +{ + u8 *d = (u8 *)dst; + + while (len--) { + *d++ = readb(src); + src++; + } +} + +static void atmel_qspi_memcpy_toio(unsigned long dst, const void *src, + size_t len) +{ + const u8 *s = (const u8 *)src; + + while (len--) { + writeb(*s, dst); + dst++; + s++; + } +} + +static int atmel_qpsi_set_ifr_width(enum spi_flash_protocol proto, u32 *ifr) +{ + u32 ifr_width; + + switch (proto) { + case SPI_FLASH_PROTO_1_1_1: + ifr_width = QSPI_IFR_WIDTH_SINGLE_BIT_SPI; + break; + + case SPI_FLASH_PROTO_1_1_2: + ifr_width = QSPI_IFR_WIDTH_DUAL_OUTPUT; + break; + + case SPI_FLASH_PROTO_1_2_2: + ifr_width = QSPI_IFR_WIDTH_DUAL_IO; + break; + + case SPI_FLASH_PROTO_2_2_2: + ifr_width = QSPI_IFR_WIDTH_DUAL_CMD; + break; + + case SPI_FLASH_PROTO_1_1_4: + ifr_width = QSPI_IFR_WIDTH_QUAD_OUTPUT; + break; + + case SPI_FLASH_PROTO_1_4_4: + ifr_width = QSPI_IFR_WIDTH_QUAD_IO; + break; + + case SPI_FLASH_PROTO_4_4_4: + ifr_width = QSPI_IFR_WIDTH_QUAD_CMD; + break; + + default: + return -EINVAL; + } + + *ifr = (*ifr & ~QSPI_IFR_WIDTH) | ifr_width; + return 0; +} + +static int atmel_qspi_send_command(struct atmel_qspi_priv *aq, + const struct atmel_qspi_command *cmd) +{ + unsigned int iar, icr, ifr; + unsigned int offset; + unsigned int imr, sr; + unsigned long memaddr; + int err; + + iar = 0; + icr = 0; + ifr = cmd->ifr_tfrtype; + + err = atmel_qpsi_set_ifr_width(cmd->protocol, &ifr); + if (err) + return err; + + /* Compute instruction parameters */ + if (cmd->enable.bits.instruction) { + icr |= QSPI_ICR_INST_(cmd->instruction); + ifr |= QSPI_IFR_INSTEN; + } + + /* Compute address parameters. */ + switch (cmd->enable.bits.address) { + case 4: + ifr |= QSPI_IFR_ADDRL_32_BIT; + //break; /* fall through the 24bit (3 byte) address case */ + case 3: + iar = (cmd->enable.bits.data) ? 0 : cmd->address; + ifr |= QSPI_IFR_ADDREN; + offset = cmd->address; + break; + case 0: + offset = 0; + break; + default: + return -EINVAL; + } + + /* Compute option parameters. */ + if (cmd->enable.bits.mode && cmd->num_mode_cycles) { + unsigned int mode_cycle_bits, mode_bits; + + icr |= QSPI_ICR_OPT_(cmd->mode); + ifr |= QSPI_IFR_OPTEN; + + switch (ifr & QSPI_IFR_WIDTH) { + case QSPI_IFR_WIDTH_SINGLE_BIT_SPI: + case QSPI_IFR_WIDTH_DUAL_OUTPUT: + case QSPI_IFR_WIDTH_QUAD_OUTPUT: + mode_cycle_bits = 1; + break; + case QSPI_IFR_WIDTH_DUAL_IO: + case QSPI_IFR_WIDTH_DUAL_CMD: + mode_cycle_bits = 2; + break; + case QSPI_IFR_WIDTH_QUAD_IO: + case QSPI_IFR_WIDTH_QUAD_CMD: + mode_cycle_bits = 4; + break; + default: + return -EINVAL; + } + + mode_bits = cmd->num_mode_cycles * mode_cycle_bits; + switch (mode_bits) { + case 1: + ifr |= QSPI_IFR_OPTL_1BIT; + break; + + case 2: + ifr |= QSPI_IFR_OPTL_2BIT; + break; + + case 4: + ifr |= QSPI_IFR_OPTL_4BIT; + break; + + case 8: + ifr |= QSPI_IFR_OPTL_8BIT; + break; + + default: + return -EINVAL; + } + } + + /* Set the number of dummy cycles. */ + if (cmd->enable.bits.dummy) + ifr |= QSPI_IFR_NBDUM_(cmd->num_dummy_cycles); + + /* Set data enable. */ + if (cmd->enable.bits.data) { + ifr |= QSPI_IFR_DATAEN; + + /* Special case for Continuous Read Mode. */ + if (!cmd->tx_buf && !cmd->rx_buf) + ifr |= QSPI_IFR_CRM; + } + + /* Clear pending interrupts. */ + (void)qspi_readl(aq, QSPI_SR); + + /* Set QSPI Instruction Frame registers. */ + qspi_writel(aq, QSPI_IAR, iar); + qspi_writel(aq, QSPI_ICR, icr); + qspi_writel(aq, QSPI_IFR, ifr); + + /* Skip to the final steps if there is no data. */ + if (!cmd->enable.bits.data) + goto no_data; + + /* Dummy read of QSPI_IFR to synchronize APB and AHB accesses. */ + (void)qspi_readl(aq, QSPI_IFR); + + /* Stop here for Continuous Read. */ + memaddr = (unsigned long)(aq->membase + offset); + if (cmd->tx_buf) + /* Write data. */ + atmel_qspi_memcpy_toio(memaddr, cmd->tx_buf, cmd->buf_len); + else if (cmd->rx_buf) + /* Read data. */ + atmel_qspi_memcpy_fromio(cmd->rx_buf, memaddr, cmd->buf_len); + else + /* Stop here for continuous read */ + return 0; + + /* Release the chip-select. */ + qspi_writel(aq, QSPI_CR, QSPI_CR_LASTXFER); + +no_data: + /* Poll INSTruction End and Chip Select Rise flags. */ + imr = (QSPI_SR_INSTRE | QSPI_SR_CSR); + sr = 0; + while (sr != (QSPI_SR_INSTRE | QSPI_SR_CSR)) + sr |= qspi_readl(aq, QSPI_SR) & imr; + + return 0; +} + +static int atmel_qspi_read_reg(struct udevice *dev, u8 opcode, + size_t len, void *buf) +{ + struct spi_flash *flash = dev_get_uclass_priv(dev); + struct atmel_qspi_priv *aq = dev_get_priv(dev->parent); + struct atmel_qspi_command cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.enable.bits.instruction = 1; + cmd.enable.bits.data = 1; + cmd.instruction = opcode; + cmd.rx_buf = buf; + cmd.buf_len = len; + cmd.protocol = flash->reg_proto; + cmd.ifr_tfrtype = QSPI_IFR_TFRTYPE_READ; + return atmel_qspi_send_command(aq, &cmd); +} + +static int atmel_qspi_write_reg(struct udevice *dev, u8 opcode, + size_t len, const void *buf) +{ + struct spi_flash *flash = dev_get_uclass_priv(dev); + struct atmel_qspi_priv *aq = dev_get_priv(dev->parent); + struct atmel_qspi_command cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.enable.bits.instruction = 1; + cmd.enable.bits.data = (buf && len); + cmd.instruction = opcode; + cmd.tx_buf = buf; + cmd.buf_len = len; + cmd.protocol = flash->reg_proto; + cmd.ifr_tfrtype = QSPI_IFR_TFRTYPE_WRITE; + return atmel_qspi_send_command(aq, &cmd); +} + +static int atmel_qspi_read_impl(struct spi_flash *flash, u32 offset, + size_t len, void *buf) +{ + struct atmel_qspi_priv *aq = dev_get_priv(flash->dev->parent); + struct atmel_qspi_command cmd; + u8 lshift; + + switch (SPI_FLASH_PROTO_ADR_FROM_PROTO(flash->read_proto)) { + case 1: + lshift = 3; + break; + + case 2: + lshift = 2; + break; + + case 4: + lshift = 1; + break; + + default: + return -EINVAL; + } + + memset(&cmd, 0, sizeof(cmd)); + cmd.enable.bits.instruction = 1; + cmd.enable.bits.address = flash->addr_width; + cmd.enable.bits.mode = 0; + cmd.enable.bits.dummy = (flash->dummy_byte > 0); + cmd.enable.bits.data = 1; + cmd.instruction = flash->read_cmd; + cmd.address = offset; + cmd.num_dummy_cycles = flash->dummy_byte << lshift; + cmd.rx_buf = buf; + cmd.buf_len = len; + cmd.protocol = flash->read_proto; + cmd.ifr_tfrtype = QSPI_IFR_TFRTYPE_READ_MEMORY; + return atmel_qspi_send_command(aq, &cmd); +} + +static int atmel_qspi_read(struct udevice *dev, u32 offset, + size_t len, void *buf) +{ + struct spi_flash *flash = dev_get_uclass_priv(dev); + + return spi_flash_read_alg(flash, offset, len, buf, + atmel_qspi_read_impl); +} + +static int atmel_qspi_write_impl(struct spi_flash *flash, u32 offset, + size_t len, const void *buf) +{ + struct atmel_qspi_priv *aq = dev_get_priv(flash->dev->parent); + struct atmel_qspi_command cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.enable.bits.instruction = 1; + cmd.enable.bits.address = flash->addr_width; + cmd.enable.bits.data = 1; + cmd.instruction = flash->write_cmd; + cmd.address = offset; + cmd.tx_buf = buf; + cmd.buf_len = len; + cmd.protocol = flash->write_proto; + cmd.ifr_tfrtype = QSPI_IFR_TFRTYPE_WRITE_MEMORY; + return atmel_qspi_send_command(aq, &cmd); +} + +static int atmel_qspi_write(struct udevice *dev, u32 offset, + size_t len, const void *buf) +{ + struct spi_flash *flash = dev_get_uclass_priv(dev); + + return spi_flash_write_alg(flash, offset, len, buf, + atmel_qspi_write_impl); +} + +static int atmel_qspi_erase_impl(struct spi_flash *flash, u32 offset) +{ + struct atmel_qspi_priv *aq = dev_get_priv(flash->dev->parent); + struct atmel_qspi_command cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.enable.bits.instruction = 1; + cmd.enable.bits.address = flash->addr_width; + cmd.instruction = flash->erase_cmd; + cmd.address = offset; + cmd.protocol = flash->erase_proto; + cmd.ifr_tfrtype = QSPI_IFR_TFRTYPE_WRITE; + return atmel_qspi_send_command(aq, &cmd); +} + +static int atmel_qspi_erase(struct udevice *dev, u32 offset, size_t len) +{ + struct spi_flash *flash = dev_get_uclass_priv(dev); + + return spi_flash_erase_alg(flash, offset, len, + atmel_qspi_erase_impl); +} + +static const struct dm_spi_flash_ops atmel_qspi_flash_ops = { + .read_reg = atmel_qspi_read_reg, + .write_reg = atmel_qspi_write_reg, + .read = atmel_qspi_read, + .write = atmel_qspi_write, + .erase = atmel_qspi_erase, +}; + +static int atmel_qspi_flash_probe(struct udevice *dev) +{ + struct spi_flash *flash = dev_get_uclass_priv(dev); + struct spi_slave *slave = dev_get_parent_priv(dev); + int ret; + + flash->dev = dev; + flash->spi = slave; + + /* Claim spi bus */ + ret = spi_claim_bus(slave); + if (ret) { + debug("SF: Failed to claim SPI bus: %d\n", ret); + return ret; + } + + /* The Quad SPI controller supports all Dual & Quad I/O protocols */ + slave->mode |= (SPI_TX_QUAD | SPI_TX_DUAL); + ret = spi_flash_scan(flash, RD_FCMD); + if (ret) { + ret = -EINVAL; + goto release; + } + +#ifdef CONFIG_SPI_FLASH_MTD + ret = spi_flash_mtd_register(flash); +#endif + +release: + spi_release_bus(slave); + return ret; +} + +#if CONFIG_IS_ENABLED(OF_CONTROL) +static const struct udevice_id atmel_qspi_flash_ids[] = { + { .compatible = "atmel,sama5d2-qspi-flash" }, + { } +}; +#endif + +U_BOOT_DRIVER(atmel_qspi_flash) = { + .name = "atmel_qspi_flash", + .id = UCLASS_SPI_FLASH, +#if CONFIG_IS_ENABLED(OF_CONTROL) + .of_match = atmel_qspi_flash_ids, +#endif + .probe = atmel_qspi_flash_probe, + .ops = &atmel_qspi_flash_ops, +}; diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index f0258f84afeb..d478c5d953c6 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -23,6 +23,15 @@ config ALTERA_SPI IP core. Please find details on the "Embedded Peripherals IP User Guide" of Altera.
+config ATMEL_QSPI + bool "Atmel QSPI driver" + depends on ARCH_AT91 + select SPI_FLASH + select DM_SPI_FLASH + help + Enable the Ateml Quad-SPI (QSPI) driver. This driver can only be + used to access SPI NOR flashes. + config CADENCE_QSPI bool "Cadence QSPI driver" help diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 3eca7456d6a6..eb3bd95f5ee7 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -18,6 +18,7 @@ endif obj-$(CONFIG_ALTERA_SPI) += altera_spi.o obj-$(CONFIG_ARMADA100_SPI) += armada100_spi.o obj-$(CONFIG_ATMEL_DATAFLASH_SPI) += atmel_dataflash_spi.o +obj-$(CONFIG_ATMEL_QSPI) += atmel_qspi.o obj-$(CONFIG_ATMEL_SPI) += atmel_spi.o obj-$(CONFIG_BFIN_SPI) += bfin_spi.o obj-$(CONFIG_BFIN_SPI6XX) += bfin_spi6xx.o diff --git a/drivers/spi/atmel_qspi.c b/drivers/spi/atmel_qspi.c new file mode 100644 index 000000000000..b8e280553d99 --- /dev/null +++ b/drivers/spi/atmel_qspi.c @@ -0,0 +1,150 @@ +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <spi.h> +#include <asm/io.h> +#include <mach/clk.h> + +#include "atmel_qspi.h" + +DECLARE_GLOBAL_DATA_PTR; + + +static int atmel_qspi_xfer(struct udevice *dev, unsigned int bitlen, + const void *dout, void *din, unsigned long flags) +{ + /* This controller can only be used with SPI NOR flashes. */ + return -EINVAL; +} + +static int atmel_qspi_set_speed(struct udevice *bus, uint hz) +{ + struct atmel_qspi_priv *aq = dev_get_priv(bus); + unsigned long src_rate; + u32 scr, scbr, mask, new_value; + + /* Compute the QSPI baudrate */ + src_rate = get_qspi_clk_rate(aq->dev_id); + scbr = DIV_ROUND_UP(src_rate, hz); + if (scbr > 0) + scbr--; + + new_value = QSPI_SCR_SCBR_(scbr); + mask = QSPI_SCR_SCBR; + + scr = qspi_readl(aq, QSPI_SCR); + if ((scr & mask) == new_value) + return 0; + + scr = (scr & ~mask) | new_value; + qspi_writel(aq, QSPI_SCR, scr); + + return 0; +} + +static int atmel_qspi_set_mode(struct udevice *bus, uint mode) +{ + struct atmel_qspi_priv *aq = dev_get_priv(bus); + u32 scr, mask, new_value; + + new_value = (QSPI_SCR_CPOL_((mode & SPI_CPOL) != 0) | + QSPI_SCR_CPHA_((mode & SPI_CPHA) != 0)); + mask = (QSPI_SCR_CPOL | QSPI_SCR_CPHA); + + scr = qspi_readl(aq, QSPI_SCR); + if ((scr & mask) == new_value) + return 0; + + scr = (scr & ~mask) | new_value; + qspi_writel(aq, QSPI_SCR, scr); + + return 0; +} + +static const struct dm_spi_ops atmel_qspi_ops = { + .xfer = atmel_qspi_xfer, + .set_speed = atmel_qspi_set_speed, + .set_mode = atmel_qspi_set_mode, +}; + +static int atmel_qspi_probe(struct udevice *dev) +{ + const struct atmel_qspi_platdata *plat = dev_get_platdata(dev); + struct atmel_qspi_priv *aq = dev_get_priv(dev); + u32 mr; + + aq->regbase = plat->regbase; + aq->membase = plat->membase; + aq->dev_id = plat->dev_id; + + /* Reset the QSPI controler */ + qspi_writel(aq, QSPI_CR, QSPI_CR_SWRST); + + /* Set the QSPI controller in Serial Memory Mode */ + mr = (QSPI_MR_NBBITS_8_BIT | + QSPI_MR_SMM_MEMORY | + QSPI_MR_CSMODE_LASTXFER); + qspi_writel(aq, QSPI_MR, mr); + + /* Enable the QSPI controller */ + qspi_writel(aq, QSPI_CR, QSPI_CR_QSPIEN); + + return 0; +} + +static int atmel_qspi_remove(struct udevice *dev) +{ + struct atmel_qspi_priv *aq = dev_get_priv(dev); + + /* Disable the QSPI controller */ + qspi_writel(aq, QSPI_CR, QSPI_CR_QSPIDIS); + + return 0; +} + +#if CONFIG_IS_ENABLED(OF_CONTROL) +static int atmel_qspi_ofdata_to_platdata(struct udevice *dev) +{ + struct atmel_qspi_platdata *plat = dev_get_platdata(dev); + const void *blob = gd->fdt_blob; + int node = dev->of_offset; + u32 data[4]; + int ret, seq; + + ret = fdtdec_get_int_array(blob, node, "reg", data, ARRAY_SIZE(data)); + if (ret) { + printf("Error: Can't get base addresses (ret=%d)!\n", ret); + return -ENODEV; + } + plat->regbase = (void *)data[0]; + plat->membase = (void *)data[2]; + + ret = fdtdec_get_alias_seq(blob, "spi", node, &seq); + if (ret) { + printf("Error: Can't get device ID (ret=%d)!\n", ret); + return -ENODEV; + } + plat->dev_id = (unsigned int)seq; + + return 0; +} + +static const struct udevice_id atmel_qspi_ids[] = { + { .compatible = "atmel,sama5d2-qspi" }, + { } +}; +#endif + +U_BOOT_DRIVER(atmel_qspi) = { + .name = "atmel_qspi", + .id = UCLASS_SPI, +#if CONFIG_IS_ENABLED(OF_CONTROL) + .of_match = atmel_qspi_ids, + .ofdata_to_platdata = atmel_qspi_ofdata_to_platdata, + .platdata_auto_alloc_size = sizeof(struct atmel_qspi_platdata), +#endif + .priv_auto_alloc_size = sizeof(struct atmel_qspi_priv), + .ops = &atmel_qspi_ops, + .probe = atmel_qspi_probe, + .remove = atmel_qspi_remove, +}; diff --git a/drivers/spi/atmel_qspi.h b/drivers/spi/atmel_qspi.h new file mode 100644 index 000000000000..2b221dd44335 --- /dev/null +++ b/drivers/spi/atmel_qspi.h @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2016 + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __ATMEL_QSPI_H__ +#define __ATMEL_QSPI_H__ + +/* + * Register Definitions + */ +#define QSPI_CR 0x00 /* Control Register */ +#define QSPI_MR 0x04 /* Mode Register */ +#define QSPI_RDR 0x08 /* Receive Data Register */ +#define QSPI_TDR 0x0c /* Transmit Data Register */ +#define QSPI_SR 0x10 /* Status Register */ +#define QSPI_IER 0x14 /* Interrupt Enable Register */ +#define QSPI_IDR 0x18 /* Interrupt Disable Register */ +#define QSPI_IMR 0x1c /* Interrupt Mask Register */ +#define QSPI_SCR 0x20 /* Serial Clock Register */ +#define QSPI_IAR 0x30 /* Instruction Address Register */ +#define QSPI_ICR 0x34 /* Instruction Code Register */ +#define QSPI_IFR 0x38 /* Instruction Frame Register */ +/* 0x3c Reserved */ +#define QSPI_SMR 0x40 /* Scrambling Mode Register */ +#define QSPI_SKR 0x44 /* Scrambling Key Register */ +/* 0x48 ~ 0xe0 */ +#define QSPI_WPMR 0xe4 /* Write Protection Mode Register */ +#define QSPI_WPSR 0xe8 /* Write Protection Status Register */ +/* 0xec ~ 0xf8 Reserved */ +/* 0xfc Reserved */ + +/* + * Register Field Definitions + */ +/* QSPI_CR */ +#define QSPI_CR_QSPIEN (0x1 << 0) /* QSPI Enable */ +#define QSPI_CR_QSPIDIS (0x1 << 1) /* QSPI Disable */ +#define QSPI_CR_SWRST (0x1 << 7) /* QSPI Software Reset */ +#define QSPI_CR_LASTXFER (0x1 << 24) /* Last Transfer */ + +/* QSPI_MR */ +#define QSPI_MR_SMM (0x1 << 0) /* Serial Memort Mode */ +#define QSPI_MR_SMM_SPI (0x0 << 0) +#define QSPI_MR_SMM_MEMORY (0x1 << 0) +#define QSPI_MR_LLB (0x1 << 1) /* Local Localback Enable */ +#define QSPI_MR_LLB_DISABLED (0x0 << 1) +#define QSPI_MR_LLB_ENABLED (0x1 << 1) +#define QSPI_MR_WDRBT (0x1 << 2) /* Wait Data Read Before Transfer */ +#define QSPI_MR_WDRBT_DISABLED (0x0 << 2) +#define QSPI_MR_WDRBT_ENABLED (0x1 << 2) +#define QSPI_MR_SMRM (0x1 << 3) /* Serial Memory Register Mode */ +#define QSPI_MR_SMRM_AHB (0x0 << 3) +#define QSPI_MR_SMRM_APB (0x1 << 3) +#define QSPI_MR_CSMODE (0x3 << 4) /* Chip Select Mode */ +#define QSPI_MR_CSMODE_NOT_RELOADED (0x0 << 4) +#define QSPI_MR_CSMODE_LASTXFER (0x1 << 4) +#define QSPI_MR_CSMODE_SYSTEMATICALLY (0x2 << 4) +#define QSPI_MR_NBBITS (0xf << 8) /* Number of Bits Per Transfer */ +#define QSPI_MR_NBBITS_8_BIT (0x0 << 8) +#define QSPI_MR_NBBITS_16_BIT (0x8 << 8) +#define QSPI_MR_DLYBCT (0xff << 16) /* Delay Between Consecutive Transfers */ +#define QSPI_MR_DLYCS (0xff << 24) /* Minimum Inactive QCS Delay */ + +/* QSPI_SR */ +#define QSPI_SR_RDRF (0x1 << 0) /* Receive Data Register Full */ +#define QSPI_SR_TDRE (0x1 << 1) /* Transmit Data Register Empty */ +#define QSPI_SR_TXEMPTY (0x1 << 2) /* Transmission Registers Empty */ +#define QSPI_SR_OVRES (0x1 << 3) /* Overrun Error Status */ +#define QSPI_SR_CSR (0x1 << 8) /* Chip Select Rise */ +#define QSPI_SR_CSS (0x1 << 9) /* Chip Select Status */ +#define QSPI_SR_INSTRE (0x1 << 10) /* Instruction End Status */ +#define QSPI_SR_QSPIENS (0x1 << 24) /* QSPI Enable Status */ + +/* QSPI_SCR */ +#define QSPI_SCR_CPOL (0x1 << 0) /* Clock Polarity */ +#define QSPI_SCR_CPOL_(x) ((x) << 0) +#define QSPI_SCR_CPHA (0x1 << 1) /* Clock Phase */ +#define QSPI_SCR_CPHA_(x) ((x) << 1) +#define QSPI_SCR_SCBR (0xff << 8) /* Serial Clock Baud Rate */ +#define QSPI_SCR_SCBR_(x) ((x) << 8) +#define QSPI_SCR_DLYBS_(x) ((x) << 16) /* Delay Before QSCK */ + +/* QSPI_ICR */ +#define QSPI_ICR_INST_(x) ((x) << 0) /* Instruction Code */ +#define QSPI_ICR_OPT_(x) ((x) << 16) /* Option Code */ + +/* QSPI_IFR */ +#define QSPI_IFR_WIDTH (0x7 << 0) /* Width of Instruction Code, Address, Option Code and Data */ +#define QSPI_IFR_WIDTH_SINGLE_BIT_SPI (0x0 << 0) +#define QSPI_IFR_WIDTH_DUAL_OUTPUT (0x1 << 0) +#define QSPI_IFR_WIDTH_QUAD_OUTPUT (0x2 << 0) +#define QSPI_IFR_WIDTH_DUAL_IO (0x3 << 0) +#define QSPI_IFR_WIDTH_QUAD_IO (0x4 << 0) +#define QSPI_IFR_WIDTH_DUAL_CMD (0x5 << 0) +#define QSPI_IFR_WIDTH_QUAD_CMD (0x6 << 0) +#define QSPI_IFR_WIDTH_(x) (((x) << 0) & QSPI_IFR_WIDTH) +#define QSPI_IFR_INSTEN (0x1 << 4) /* Instruction Enable*/ +#define QSPI_IFR_ADDREN (0x1 << 5) /* Address Enable*/ +#define QSPI_IFR_OPTEN (0x1 << 6) /* Option Enable*/ +#define QSPI_IFR_DATAEN (0x1 << 7) /* Data Enable*/ +#define QSPI_IFR_OPTL (0x3 << 8) /* Option Code Length */ +#define QSPI_IFR_OPTL_1BIT (0x0 << 8) +#define QSPI_IFR_OPTL_2BIT (0x1 << 8) +#define QSPI_IFR_OPTL_4BIT (0x2 << 8) +#define QSPI_IFR_OPTL_8BIT (0x3 << 8) +#define QSPI_IFR_ADDRL (0x1 << 10) /* Address Length */ +#define QSPI_IFR_ADDRL_24_BIT (0x0 << 10) +#define QSPI_IFR_ADDRL_32_BIT (0x1 << 10) +#define QSPI_IFR_TFRTYPE (0x3 << 12) /* Data Transfer Type */ +#define QSPI_IFR_TFRTYPE_READ (0x0 << 12) +#define QSPI_IFR_TFRTYPE_READ_MEMORY (0x1 << 12) +#define QSPI_IFR_TFRTYPE_WRITE (0x2 << 12) +#define QSPI_IFR_TFRTYPE_WRITE_MEMORY (0x3 << 12) +#define QSPI_IFR_TFRTYPE_(x) (((x) << 12) & QSPI_IFR_TFRTYPE) +#define QSPI_IFR_CRM (0x1 << 14) /* Continuous Read Mode */ +#define QSPI_IFR_NBDUM_(x) ((x) << 16) /* Number Of Dummy Cycles */ + + +struct atmel_qspi_platdata { + unsigned int dev_id; + void *regbase; + void *membase; +}; + +struct atmel_qspi_priv { + unsigned int dev_id; + void *regbase; + void *membase; +}; + +#include <asm/io.h> + +static inline u32 qspi_readl(struct atmel_qspi_priv *aq, u32 reg) +{ + return readl(aq->regbase + reg); +} + +static inline void qspi_writel(struct atmel_qspi_priv *aq, u32 reg, u32 value) +{ + writel(value, aq->regbase + reg); +} + +#endif /* __ATMEL_QSPI_H__ */

On Tuesday 15 March 2016 11:42 PM, Cyrille Pitchen wrote:
Hi all,
This series of patches fixes and extend the support of QSPI memories in the SPI flash framework. The updates are split into many parts to make it easier to understand and review but they should be considered as a whole.
This was tested on a Atmel sama5d2 xplained board with a Micron n25q128a memory.
Best regards,
Cyrille
Cyrille Pitchen (18): Revert "sf: Fix quad bit set for micron devices" sf: call spi_claim_bus() and spi_release_bus() only once per read, write or erase sf: replace spi_flash_read_common() calls by spi_flash_cmd_read() sf: remove spi_flash_write_common() sf: export spi_flash_wait_ready() function sf: share erase generic algorithm sf: share write generic algorithm sf: share read generic algorithm sf: add hooks to handle register read and write operations sf: move support of SST flash into generic spi_flash_write_alg() sf: fix selection of supported READ commands for QSPI memories sf: fix detection of QSPI memories when they boot in Quad or Dual mode sf: add helper function to set the number of dummy bytes sf: add 4byte address opcodes sf: prepare next fixes to support of QSPI memories by manufacturer sf: fix support of Micron memories ARM: at91: clock: add function to get QSPI clocks sf: add driver for Atmel QSPI controller
Appreciate for the work, we're working on spi-nor framework[1] planning to push in couple of weeks. Will let you know once it merged so that you can add your changes on top of that.
[1] http://git.denx.de/?p=u-boot/u-boot-spi.git;a=shortlog;h=refs/heads/spi-nor-...

On 03/15/2016 07:21 PM, Jagan Teki wrote:
On Tuesday 15 March 2016 11:42 PM, Cyrille Pitchen wrote:
Hi all,
This series of patches fixes and extend the support of QSPI memories in the SPI flash framework. The updates are split into many parts to make it easier to understand and review but they should be considered as a whole.
This was tested on a Atmel sama5d2 xplained board with a Micron n25q128a memory.
Best regards,
Cyrille
Cyrille Pitchen (18): Revert "sf: Fix quad bit set for micron devices" sf: call spi_claim_bus() and spi_release_bus() only once per read, write or erase sf: replace spi_flash_read_common() calls by spi_flash_cmd_read() sf: remove spi_flash_write_common() sf: export spi_flash_wait_ready() function sf: share erase generic algorithm sf: share write generic algorithm sf: share read generic algorithm sf: add hooks to handle register read and write operations sf: move support of SST flash into generic spi_flash_write_alg() sf: fix selection of supported READ commands for QSPI memories sf: fix detection of QSPI memories when they boot in Quad or Dual mode sf: add helper function to set the number of dummy bytes sf: add 4byte address opcodes sf: prepare next fixes to support of QSPI memories by manufacturer sf: fix support of Micron memories ARM: at91: clock: add function to get QSPI clocks sf: add driver for Atmel QSPI controller
Appreciate for the work, we're working on spi-nor framework[1] planning to push in couple of weeks. Will let you know once it merged so that you can add your changes on top of that.
[1] http://git.denx.de/?p=u-boot/u-boot-spi.git;a=shortlog;h=refs/heads/spi-nor-...
The patchset seems to be in a sensible shape to me, so maybe we should do it the other way around -- get this in and have you rebase your V6(?) patchset on top of it -- since clearly your patchset will take some more time to ripe.

Le 15/03/2016 19:21, Jagan Teki a écrit :
On Tuesday 15 March 2016 11:42 PM, Cyrille Pitchen wrote:
Hi all,
This series of patches fixes and extend the support of QSPI memories in the SPI flash framework. The updates are split into many parts to make it easier to understand and review but they should be considered as a whole.
This was tested on a Atmel sama5d2 xplained board with a Micron n25q128a memory.
Best regards,
Cyrille
Cyrille Pitchen (18): Revert "sf: Fix quad bit set for micron devices" sf: call spi_claim_bus() and spi_release_bus() only once per read, write or erase sf: replace spi_flash_read_common() calls by spi_flash_cmd_read() sf: remove spi_flash_write_common() sf: export spi_flash_wait_ready() function sf: share erase generic algorithm sf: share write generic algorithm sf: share read generic algorithm sf: add hooks to handle register read and write operations sf: move support of SST flash into generic spi_flash_write_alg() sf: fix selection of supported READ commands for QSPI memories sf: fix detection of QSPI memories when they boot in Quad or Dual mode sf: add helper function to set the number of dummy bytes sf: add 4byte address opcodes sf: prepare next fixes to support of QSPI memories by manufacturer sf: fix support of Micron memories ARM: at91: clock: add function to get QSPI clocks sf: add driver for Atmel QSPI controller
Appreciate for the work, we're working on spi-nor framework[1] planning to push in couple of weeks. Will let you know once it merged so that you can add your changes on top of that.
[1] http://git.denx.de/?p=u-boot/u-boot-spi.git;a=shortlog;h=refs/heads/spi-nor-...
Hi Jagan,
I've started to have a look on your branch. I hope it's not to late for few comments:
Globally I see the new code attend to match the spi-nor framework from Linux. OK that's fine but please note the current spi-nor framework in Linux has incomplete and sometime not working support of QSPI memories.
First, after a discussion with Brian and Bean on linux-mtd [1], Bean's commit to add support to Micron QSPI memories was reverted since it didn't work alone. In his reply, Brian agreed the code was not tested and should not have been merged.
This highlights a more general issue: currently, there is no mean for the spi-nor framework to notify the SPI controller driver about a SPI protocol change at the QSPI memory side. This applies to Micron memories when they enter their Quad I/O mode. If so, ALL commands, even JEDEC Read ID, Read Status Register, ..., MUST use the SPI 4-4-4 protocol. Commands sent using SPI 1-x-y protocols are no longer decoded properly. This also applies to Macronix and Winbond memories if they enter their QPI mode, which is the equivalent of Micron Quad I/O mode. This is why I've suggested to add 4 new fields in the struct spi_nor: - .reg_proto: the SPI protocol to be used by .read_reg() and .write_reg() hooks. - .read_proto: the SPI protocol to be used by the .read() hooks, maybe by the .read_mmap() also. - .write_proto: the SPI protocol to be used by the .write() hooks - .erase_proto: the SPI protocol to be used by the .erase() hooks.
(Q)SPI controller drivers cannot guess the protocol to be used from the command op code. Indeed, taking the Micron case as un example, the very same 0xeb op code may be used with the SPI 1-4-4 protocol (Micron Extended SPI mode) or with the SPI 4-4-4 protocol (Micron Quad I/O mode).
Also just some words about the naming of SPI x-y-z protocols: - x refers to the number of I/O lines used to send the op code byte - y is the number of I/O lines used to send the address, mode/dummy cycles (if these cycles exist for the command op code) - z is the number of I/O lines used to send/receive data (if needed)
So the SNOR_OP_READ_1_1_2_IO macro for the Fast Read Dual I/O command (as opposed to the macro SNOR_OP_READ_1_1_2 macro for the Fast Read Dual Output command) doesn't make sense: it should be named SNOR_OP_READ_1_2_2.
Then about the value used for the dummy cycles, it's not always true that we don't care about initializing them. Depending on the configuration of the memory, some special dummy cycles, sometime called mode cycles, are used to during Fast Read operations to make the memory enter/leaver its Continuous Read mode. Once is Continuous Read mode, the op code byte is no longer sent, it is implicit and the command actually starts from the address cycles. This mode is mostly used for XIP applications hence is not relevant for mtd usage. However we should take care not to enter this Continuous Mode by mistake. Depending on the memory manufacturer, the Continuous Mode can be disabled by updating the relevant bit in some configuration register (e.g. setting the XIP bit in Micron Volatile Configuration Register) or by choosing the right op code (e.g. for Winbond memories in QPI mode, both the 0x0b and 0xeb op codes can be used for Fast Read 4-4-4 operation but only the 0xeb op code cares about the dummy cycle value to enter/leave the Continuous Read mode). Some Spansion memories use 6 dummy cycles for Fast Read 1-4-4 command as factory default, not 8.
Besides when sending a regular JEDEC Read ID (0x9f) command to probe the (Q)SPI memory, the current spi-nor framework assumes the Quad I/O or QPI mode is not already enabled. This not always true, some early bootloarders, such as the sama5d2 ROM Code, enables the Micron Quad I/O mode when configured to boot from the QSPI memory. If so, the QSPI memory no longer replies to the 0x9f command in SPI 1-1-1 protocol but instead to the 0xaf command in SPI 4-4-4 protocol.
Finally, about the proper way to describe the SPI controller capabilities, the SPI_TX_{DUAL, QUAD} and SPI_RX_{DUAL, QUAD} mode flags are set in the SPI framework based on the "spi-rx-bus-width" and "spi-tx-bus-width" DT properties already used in Linux. This is not enough to make the difference between the SPI 1-4-4 and SPI 4-4-4 protocols. Maybe some SPI controllers support the first protocol but not the latest. It could be good to add another argument to spi_nor_scan() providing an exhaustive list of SPI protocols supported by the SPI controller. Then to match this list with the list of SPI protocols supported by the SPI memory and select the proper protocol, this new argument should use the same range of values as the .flash_read field in the struct spi_nor_info used to describe the SPI memories.
For backward compatibility, the m25p80 driver could then convert the SPI modes into spi-nor read modes. Please have a look at patch 11 of my series; the chunk related to spi_flash_probe_slave() in sf_probe.c:
/* Convert SPI mode_rx and mode to SPI flash read commands */ + mode_rx = spi->mode_rx; + if (mode_rx & SPI_RX_QUAD) { + e_rd_cmd = RD_NORM | QUAD_OUTPUT_FAST; + if (spi->mode & SPI_TX_QUAD) + e_rd_cmd |= QUAD_IO_FAST; + } else if (mode_rx & SPI_RX_DUAL) { + e_rd_cmd = RD_NORM | DUAL_OUTPUT_FAST; + if (spi->mode & SPI_TX_DUAL) + e_rd_cmd |= DUAL_IO_FAST; + } else if ((mode_rx & (SPI_RX_SLOW | SPI_RX_FAST)) == SPI_RX_SLOW) { + e_rd_cmd = ARRAY_SLOW; + } else { + e_rd_cmd = RD_NORM; + } + [...] - ret = spi_flash_scan(flash); + ret = spi_flash_scan(flash, e_rd_cmd);
I've spent a lot of time working on the QSPI memory topic so I can tell you that there are many other traps to avoid! If I can help you on this topic during your rework of the SPI NOR support, please let me know.
Best regards,
Cyrille
[1] http://lists.infradead.org/pipermail/linux-mtd/2016-February/065371.html

On Wednesday 16 March 2016 07:00 PM, Cyrille Pitchen wrote:
Le 15/03/2016 19:21, Jagan Teki a écrit :
On Tuesday 15 March 2016 11:42 PM, Cyrille Pitchen wrote:
Hi all,
This series of patches fixes and extend the support of QSPI memories in the SPI flash framework. The updates are split into many parts to make it easier to understand and review but they should be considered as a whole.
This was tested on a Atmel sama5d2 xplained board with a Micron n25q128a memory.
Best regards,
Cyrille
Cyrille Pitchen (18): Revert "sf: Fix quad bit set for micron devices" sf: call spi_claim_bus() and spi_release_bus() only once per read, write or erase sf: replace spi_flash_read_common() calls by spi_flash_cmd_read() sf: remove spi_flash_write_common() sf: export spi_flash_wait_ready() function sf: share erase generic algorithm sf: share write generic algorithm sf: share read generic algorithm sf: add hooks to handle register read and write operations sf: move support of SST flash into generic spi_flash_write_alg() sf: fix selection of supported READ commands for QSPI memories sf: fix detection of QSPI memories when they boot in Quad or Dual mode sf: add helper function to set the number of dummy bytes sf: add 4byte address opcodes sf: prepare next fixes to support of QSPI memories by manufacturer sf: fix support of Micron memories ARM: at91: clock: add function to get QSPI clocks sf: add driver for Atmel QSPI controller
Appreciate for the work, we're working on spi-nor framework[1] planning to push in couple of weeks. Will let you know once it merged so that you can add your changes on top of that.
[1] http://git.denx.de/?p=u-boot/u-boot-spi.git;a=shortlog;h=refs/heads/spi-nor-...
Hi Jagan,
I've started to have a look on your branch. I hope it's not to late for few comments:
Globally I see the new code attend to match the spi-nor framework from Linux. OK that's fine but please note the current spi-nor framework in Linux has incomplete and sometime not working support of QSPI memories.
First, after a discussion with Brian and Bean on linux-mtd [1], Bean's commit to add support to Micron QSPI memories was reverted since it didn't work alone. In his reply, Brian agreed the code was not tested and should not have been merged.
This highlights a more general issue: currently, there is no mean for the spi-nor framework to notify the SPI controller driver about a SPI protocol change at the QSPI memory side. This applies to Micron memories when they enter their Quad I/O mode. If so, ALL commands, even JEDEC Read ID, Read Status Register, ..., MUST use the SPI 4-4-4 protocol. Commands sent using SPI 1-x-y protocols are no longer decoded properly. This also applies to Macronix and Winbond memories if they enter their QPI mode, which is the equivalent of Micron Quad I/O mode. This is why I've suggested to add 4 new fields in the struct spi_nor:
- .reg_proto: the SPI protocol to be used by .read_reg() and .write_reg() hooks.
- .read_proto: the SPI protocol to be used by the .read() hooks, maybe by the .read_mmap() also.
- .write_proto: the SPI protocol to be used by the .write() hooks
- .erase_proto: the SPI protocol to be used by the .erase() hooks.
(Q)SPI controller drivers cannot guess the protocol to be used from the command op code. Indeed, taking the Micron case as un example, the very same 0xeb op code may be used with the SPI 1-4-4 protocol (Micron Extended SPI mode) or with the SPI 4-4-4 protocol (Micron Quad I/O mode).
Also just some words about the naming of SPI x-y-z protocols:
- x refers to the number of I/O lines used to send the op code byte
- y is the number of I/O lines used to send the address, mode/dummy cycles (if these cycles exist for the command op code)
- z is the number of I/O lines used to send/receive data (if needed)
So the SNOR_OP_READ_1_1_2_IO macro for the Fast Read Dual I/O command (as opposed to the macro SNOR_OP_READ_1_1_2 macro for the Fast Read Dual Output command) doesn't make sense: it should be named SNOR_OP_READ_1_2_2.
Then about the value used for the dummy cycles, it's not always true that we don't care about initializing them. Depending on the configuration of the memory, some special dummy cycles, sometime called mode cycles, are used to during Fast Read operations to make the memory enter/leaver its Continuous Read mode. Once is Continuous Read mode, the op code byte is no longer sent, it is implicit and the command actually starts from the address cycles. This mode is mostly used for XIP applications hence is not relevant for mtd usage. However we should take care not to enter this Continuous Mode by mistake. Depending on the memory manufacturer, the Continuous Mode can be disabled by updating the relevant bit in some configuration register (e.g. setting the XIP bit in Micron Volatile Configuration Register) or by choosing the right op code (e.g. for Winbond memories in QPI mode, both the 0x0b and 0xeb op codes can be used for Fast Read 4-4-4 operation but only the 0xeb op code cares about the dummy cycle value to enter/leave the Continuous Read mode). Some Spansion memories use 6 dummy cycles for Fast Read 1-4-4 command as factory default, not 8.
Besides when sending a regular JEDEC Read ID (0x9f) command to probe the (Q)SPI memory, the current spi-nor framework assumes the Quad I/O or QPI mode is not already enabled. This not always true, some early bootloarders, such as the sama5d2 ROM Code, enables the Micron Quad I/O mode when configured to boot from the QSPI memory. If so, the QSPI memory no longer replies to the 0x9f command in SPI 1-1-1 protocol but instead to the 0xaf command in SPI 4-4-4 protocol.
Finally, about the proper way to describe the SPI controller capabilities, the SPI_TX_{DUAL, QUAD} and SPI_RX_{DUAL, QUAD} mode flags are set in the SPI framework based on the "spi-rx-bus-width" and "spi-tx-bus-width" DT properties already used in Linux. This is not enough to make the difference between the SPI 1-4-4 and SPI 4-4-4 protocols. Maybe some SPI controllers support the first protocol but not the latest. It could be good to add another argument to spi_nor_scan() providing an exhaustive list of SPI protocols supported by the SPI controller. Then to match this list with the list of SPI protocols supported by the SPI memory and select the proper protocol, this new argument should use the same range of values as the .flash_read field in the struct spi_nor_info used to describe the SPI memories.
For backward compatibility, the m25p80 driver could then convert the SPI modes into spi-nor read modes. Please have a look at patch 11 of my series; the chunk related to spi_flash_probe_slave() in sf_probe.c:
/* Convert SPI mode_rx and mode to SPI flash read commands */
- mode_rx = spi->mode_rx;
- if (mode_rx & SPI_RX_QUAD) {
e_rd_cmd = RD_NORM | QUAD_OUTPUT_FAST;
if (spi->mode & SPI_TX_QUAD)
e_rd_cmd |= QUAD_IO_FAST;
- } else if (mode_rx & SPI_RX_DUAL) {
e_rd_cmd = RD_NORM | DUAL_OUTPUT_FAST;
if (spi->mode & SPI_TX_DUAL)
e_rd_cmd |= DUAL_IO_FAST;
- } else if ((mode_rx & (SPI_RX_SLOW | SPI_RX_FAST)) == SPI_RX_SLOW) {
e_rd_cmd = ARRAY_SLOW;
- } else {
e_rd_cmd = RD_NORM;
- }
[...]
- ret = spi_flash_scan(flash);
- ret = spi_flash_scan(flash, e_rd_cmd);
I've spent a lot of time working on the QSPI memory topic so I can tell you that there are many other traps to avoid! If I can help you on this topic during your rework of the SPI NOR support, please let me know.
I understand your points, thanks for that and anyway this spi-nor work is a starting point for both syncing with Linux as well with new feature or for better tunning. And more over I started this work in 2014 end and it's been reviewing over and over and we finally landed up with MTD driver model.
So, please wait for sometime until this gets merged we definitely work together for better tunning, thanks!

Hello Jagan,
On Wed, 16 Mar 2016 19:44:26 +0530, Jagan Teki jteki@openedev.com wrote:
On Wednesday 16 March 2016 07:00 PM, Cyrille Pitchen wrote:
Le 15/03/2016 19:21, Jagan Teki a écrit :
On Tuesday 15 March 2016 11:42 PM, Cyrille Pitchen wrote:
Hi all,
This series of patches fixes and extend the support of QSPI memories in the SPI flash framework. The updates are split into many parts to make it easier to understand and review but they should be considered as a whole.
This was tested on a Atmel sama5d2 xplained board with a Micron n25q128a memory.
Best regards,
Cyrille
Cyrille Pitchen (18): Revert "sf: Fix quad bit set for micron devices" sf: call spi_claim_bus() and spi_release_bus() only once per read, write or erase sf: replace spi_flash_read_common() calls by spi_flash_cmd_read() sf: remove spi_flash_write_common() sf: export spi_flash_wait_ready() function sf: share erase generic algorithm sf: share write generic algorithm sf: share read generic algorithm sf: add hooks to handle register read and write operations sf: move support of SST flash into generic spi_flash_write_alg() sf: fix selection of supported READ commands for QSPI memories sf: fix detection of QSPI memories when they boot in Quad or Dual mode sf: add helper function to set the number of dummy bytes sf: add 4byte address opcodes sf: prepare next fixes to support of QSPI memories by manufacturer sf: fix support of Micron memories ARM: at91: clock: add function to get QSPI clocks sf: add driver for Atmel QSPI controller
Appreciate for the work, we're working on spi-nor framework[1] planning to push in couple of weeks. Will let you know once it merged so that you can add your changes on top of that.
[1] http://git.denx.de/?p=u-boot/u-boot-spi.git;a=shortlog;h=refs/heads/spi-nor-...
Hi Jagan,
I've started to have a look on your branch. I hope it's not to late for few comments:
Globally I see the new code attend to match the spi-nor framework from Linux. OK that's fine but please note the current spi-nor framework in Linux has incomplete and sometime not working support of QSPI memories.
First, after a discussion with Brian and Bean on linux-mtd [1], Bean's commit to add support to Micron QSPI memories was reverted since it didn't work alone. In his reply, Brian agreed the code was not tested and should not have been merged.
This highlights a more general issue: currently, there is no mean for the spi-nor framework to notify the SPI controller driver about a SPI protocol change at the QSPI memory side. This applies to Micron memories when they enter their Quad I/O mode. If so, ALL commands, even JEDEC Read ID, Read Status Register, ..., MUST use the SPI 4-4-4 protocol. Commands sent using SPI 1-x-y protocols are no longer decoded properly. This also applies to Macronix and Winbond memories if they enter their QPI mode, which is the equivalent of Micron Quad I/O mode. This is why I've suggested to add 4 new fields in the struct spi_nor:
- .reg_proto: the SPI protocol to be used by .read_reg() and .write_reg() hooks.
- .read_proto: the SPI protocol to be used by the .read() hooks, maybe by the .read_mmap() also.
- .write_proto: the SPI protocol to be used by the .write() hooks
- .erase_proto: the SPI protocol to be used by the .erase() hooks.
(Q)SPI controller drivers cannot guess the protocol to be used from the command op code. Indeed, taking the Micron case as un example, the very same 0xeb op code may be used with the SPI 1-4-4 protocol (Micron Extended SPI mode) or with the SPI 4-4-4 protocol (Micron Quad I/O mode).
Also just some words about the naming of SPI x-y-z protocols:
- x refers to the number of I/O lines used to send the op code byte
- y is the number of I/O lines used to send the address, mode/dummy cycles (if these cycles exist for the command op code)
- z is the number of I/O lines used to send/receive data (if needed)
So the SNOR_OP_READ_1_1_2_IO macro for the Fast Read Dual I/O command (as opposed to the macro SNOR_OP_READ_1_1_2 macro for the Fast Read Dual Output command) doesn't make sense: it should be named SNOR_OP_READ_1_2_2.
Then about the value used for the dummy cycles, it's not always true that we don't care about initializing them. Depending on the configuration of the memory, some special dummy cycles, sometime called mode cycles, are used to during Fast Read operations to make the memory enter/leaver its Continuous Read mode. Once is Continuous Read mode, the op code byte is no longer sent, it is implicit and the command actually starts from the address cycles. This mode is mostly used for XIP applications hence is not relevant for mtd usage. However we should take care not to enter this Continuous Mode by mistake. Depending on the memory manufacturer, the Continuous Mode can be disabled by updating the relevant bit in some configuration register (e.g. setting the XIP bit in Micron Volatile Configuration Register) or by choosing the right op code (e.g. for Winbond memories in QPI mode, both the 0x0b and 0xeb op codes can be used for Fast Read 4-4-4 operation but only the 0xeb op code cares about the dummy cycle value to enter/leave the Continuous Read mode). Some Spansion memories use 6 dummy cycles for Fast Read 1-4-4 command as factory default, not 8.
Besides when sending a regular JEDEC Read ID (0x9f) command to probe the (Q)SPI memory, the current spi-nor framework assumes the Quad I/O or QPI mode is not already enabled. This not always true, some early bootloarders, such as the sama5d2 ROM Code, enables the Micron Quad I/O mode when configured to boot from the QSPI memory. If so, the QSPI memory no longer replies to the 0x9f command in SPI 1-1-1 protocol but instead to the 0xaf command in SPI 4-4-4 protocol.
Finally, about the proper way to describe the SPI controller capabilities, the SPI_TX_{DUAL, QUAD} and SPI_RX_{DUAL, QUAD} mode flags are set in the SPI framework based on the "spi-rx-bus-width" and "spi-tx-bus-width" DT properties already used in Linux. This is not enough to make the difference between the SPI 1-4-4 and SPI 4-4-4 protocols. Maybe some SPI controllers support the first protocol but not the latest. It could be good to add another argument to spi_nor_scan() providing an exhaustive list of SPI protocols supported by the SPI controller. Then to match this list with the list of SPI protocols supported by the SPI memory and select the proper protocol, this new argument should use the same range of values as the .flash_read field in the struct spi_nor_info used to describe the SPI memories.
For backward compatibility, the m25p80 driver could then convert the SPI modes into spi-nor read modes. Please have a look at patch 11 of my series; the chunk related to spi_flash_probe_slave() in sf_probe.c:
/* Convert SPI mode_rx and mode to SPI flash read commands */
- mode_rx = spi->mode_rx;
- if (mode_rx & SPI_RX_QUAD) {
e_rd_cmd = RD_NORM | QUAD_OUTPUT_FAST;
if (spi->mode & SPI_TX_QUAD)
e_rd_cmd |= QUAD_IO_FAST;
- } else if (mode_rx & SPI_RX_DUAL) {
e_rd_cmd = RD_NORM | DUAL_OUTPUT_FAST;
if (spi->mode & SPI_TX_DUAL)
e_rd_cmd |= DUAL_IO_FAST;
- } else if ((mode_rx & (SPI_RX_SLOW | SPI_RX_FAST)) == SPI_RX_SLOW) {
e_rd_cmd = ARRAY_SLOW;
- } else {
e_rd_cmd = RD_NORM;
- }
[...]
- ret = spi_flash_scan(flash);
- ret = spi_flash_scan(flash, e_rd_cmd);
I've spent a lot of time working on the QSPI memory topic so I can tell you that there are many other traps to avoid! If I can help you on this topic during your rework of the SPI NOR support, please let me know.
I understand your points, thanks for that and anyway this spi-nor work is a starting point for both syncing with Linux as well with new feature or for better tunning. And more over I started this work in 2014 end and it's been reviewing over and over and we finally landed up with MTD driver model.
So, please wait for sometime until this gets merged we definitely work together for better tunning, thanks!
If I understand Cyrille's post correctly, it is not about better tuning, it is about fixing existing issues, right? I mean, Cyrille is talking about situations where the code will be not simply slow, but plain wrong, correct?
If so, and since it appears Cyrills's patch series are bug fixes which the framework would not properly work if it did not integrate them anyway, I would agree with Marek that it makes more sense applying Cyrille's patches first.
-- Jagan
Amicalement,

Hi Albert,
On Wednesday 16 March 2016 09:53 PM, Albert ARIBAUD wrote:
Hello Jagan,
On Wed, 16 Mar 2016 19:44:26 +0530, Jagan Teki jteki@openedev.com wrote:
On Wednesday 16 March 2016 07:00 PM, Cyrille Pitchen wrote:
Le 15/03/2016 19:21, Jagan Teki a écrit :
On Tuesday 15 March 2016 11:42 PM, Cyrille Pitchen wrote:
Hi all,
This series of patches fixes and extend the support of QSPI memories in the SPI flash framework. The updates are split into many parts to make it easier to understand and review but they should be considered as a whole.
This was tested on a Atmel sama5d2 xplained board with a Micron n25q128a memory.
Best regards,
Cyrille
Cyrille Pitchen (18): Revert "sf: Fix quad bit set for micron devices" sf: call spi_claim_bus() and spi_release_bus() only once per read, write or erase sf: replace spi_flash_read_common() calls by spi_flash_cmd_read() sf: remove spi_flash_write_common() sf: export spi_flash_wait_ready() function sf: share erase generic algorithm sf: share write generic algorithm sf: share read generic algorithm sf: add hooks to handle register read and write operations sf: move support of SST flash into generic spi_flash_write_alg() sf: fix selection of supported READ commands for QSPI memories sf: fix detection of QSPI memories when they boot in Quad or Dual mode sf: add helper function to set the number of dummy bytes sf: add 4byte address opcodes sf: prepare next fixes to support of QSPI memories by manufacturer sf: fix support of Micron memories ARM: at91: clock: add function to get QSPI clocks sf: add driver for Atmel QSPI controller
Appreciate for the work, we're working on spi-nor framework[1] planning to push in couple of weeks. Will let you know once it merged so that you can add your changes on top of that.
[1] http://git.denx.de/?p=u-boot/u-boot-spi.git;a=shortlog;h=refs/heads/spi-nor-...
Hi Jagan,
I've started to have a look on your branch. I hope it's not to late for few comments:
Globally I see the new code attend to match the spi-nor framework from Linux. OK that's fine but please note the current spi-nor framework in Linux has incomplete and sometime not working support of QSPI memories.
First, after a discussion with Brian and Bean on linux-mtd [1], Bean's commit to add support to Micron QSPI memories was reverted since it didn't work alone. In his reply, Brian agreed the code was not tested and should not have been merged.
This highlights a more general issue: currently, there is no mean for the spi-nor framework to notify the SPI controller driver about a SPI protocol change at the QSPI memory side. This applies to Micron memories when they enter their Quad I/O mode. If so, ALL commands, even JEDEC Read ID, Read Status Register, ..., MUST use the SPI 4-4-4 protocol. Commands sent using SPI 1-x-y protocols are no longer decoded properly. This also applies to Macronix and Winbond memories if they enter their QPI mode, which is the equivalent of Micron Quad I/O mode. This is why I've suggested to add 4 new fields in the struct spi_nor:
- .reg_proto: the SPI protocol to be used by .read_reg() and .write_reg() hooks.
- .read_proto: the SPI protocol to be used by the .read() hooks, maybe by the .read_mmap() also.
- .write_proto: the SPI protocol to be used by the .write() hooks
- .erase_proto: the SPI protocol to be used by the .erase() hooks.
(Q)SPI controller drivers cannot guess the protocol to be used from the command op code. Indeed, taking the Micron case as un example, the very same 0xeb op code may be used with the SPI 1-4-4 protocol (Micron Extended SPI mode) or with the SPI 4-4-4 protocol (Micron Quad I/O mode).
Also just some words about the naming of SPI x-y-z protocols:
- x refers to the number of I/O lines used to send the op code byte
- y is the number of I/O lines used to send the address, mode/dummy cycles (if these cycles exist for the command op code)
- z is the number of I/O lines used to send/receive data (if needed)
So the SNOR_OP_READ_1_1_2_IO macro for the Fast Read Dual I/O command (as opposed to the macro SNOR_OP_READ_1_1_2 macro for the Fast Read Dual Output command) doesn't make sense: it should be named SNOR_OP_READ_1_2_2.
Then about the value used for the dummy cycles, it's not always true that we don't care about initializing them. Depending on the configuration of the memory, some special dummy cycles, sometime called mode cycles, are used to during Fast Read operations to make the memory enter/leaver its Continuous Read mode. Once is Continuous Read mode, the op code byte is no longer sent, it is implicit and the command actually starts from the address cycles. This mode is mostly used for XIP applications hence is not relevant for mtd usage. However we should take care not to enter this Continuous Mode by mistake. Depending on the memory manufacturer, the Continuous Mode can be disabled by updating the relevant bit in some configuration register (e.g. setting the XIP bit in Micron Volatile Configuration Register) or by choosing the right op code (e.g. for Winbond memories in QPI mode, both the 0x0b and 0xeb op codes can be used for Fast Read 4-4-4 operation but only the 0xeb op code cares about the dummy cycle value to enter/leave the Continuous Read mode). Some Spansion memories use 6 dummy cycles for Fast Read 1-4-4 command as factory default, not 8.
Besides when sending a regular JEDEC Read ID (0x9f) command to probe the (Q)SPI memory, the current spi-nor framework assumes the Quad I/O or QPI mode is not already enabled. This not always true, some early bootloarders, such as the sama5d2 ROM Code, enables the Micron Quad I/O mode when configured to boot from the QSPI memory. If so, the QSPI memory no longer replies to the 0x9f command in SPI 1-1-1 protocol but instead to the 0xaf command in SPI 4-4-4 protocol.
Finally, about the proper way to describe the SPI controller capabilities, the SPI_TX_{DUAL, QUAD} and SPI_RX_{DUAL, QUAD} mode flags are set in the SPI framework based on the "spi-rx-bus-width" and "spi-tx-bus-width" DT properties already used in Linux. This is not enough to make the difference between the SPI 1-4-4 and SPI 4-4-4 protocols. Maybe some SPI controllers support the first protocol but not the latest. It could be good to add another argument to spi_nor_scan() providing an exhaustive list of SPI protocols supported by the SPI controller. Then to match this list with the list of SPI protocols supported by the SPI memory and select the proper protocol, this new argument should use the same range of values as the .flash_read field in the struct spi_nor_info used to describe the SPI memories.
For backward compatibility, the m25p80 driver could then convert the SPI modes into spi-nor read modes. Please have a look at patch 11 of my series; the chunk related to spi_flash_probe_slave() in sf_probe.c:
/* Convert SPI mode_rx and mode to SPI flash read commands */
- mode_rx = spi->mode_rx;
- if (mode_rx & SPI_RX_QUAD) {
e_rd_cmd = RD_NORM | QUAD_OUTPUT_FAST;
if (spi->mode & SPI_TX_QUAD)
e_rd_cmd |= QUAD_IO_FAST;
- } else if (mode_rx & SPI_RX_DUAL) {
e_rd_cmd = RD_NORM | DUAL_OUTPUT_FAST;
if (spi->mode & SPI_TX_DUAL)
e_rd_cmd |= DUAL_IO_FAST;
- } else if ((mode_rx & (SPI_RX_SLOW | SPI_RX_FAST)) == SPI_RX_SLOW) {
e_rd_cmd = ARRAY_SLOW;
- } else {
e_rd_cmd = RD_NORM;
- }
[...]
- ret = spi_flash_scan(flash);
- ret = spi_flash_scan(flash, e_rd_cmd);
I've spent a lot of time working on the QSPI memory topic so I can tell you that there are many other traps to avoid! If I can help you on this topic during your rework of the SPI NOR support, please let me know.
I understand your points, thanks for that and anyway this spi-nor work is a starting point for both syncing with Linux as well with new feature or for better tunning. And more over I started this work in 2014 end and it's been reviewing over and over and we finally landed up with MTD driver model.
So, please wait for sometime until this gets merged we definitely work together for better tunning, thanks!
If I understand Cyrille's post correctly, it is not about better tuning, it is about fixing existing issues, right? I mean, Cyrille is talking about situations where the code will be not simply slow, but plain wrong, correct?
If so, and since it appears Cyrills's patch series are bug fixes which the framework would not properly work if it did not integrate them anyway, I would agree with Marek that it makes more sense applying Cyrille's patches first.
OK, if these are bug fixes then what is the issue on working on top of spi-nor? and more over this series is on ML just now and need a proper review as well and will take some time too. I have planned spi-nor series for this release better work on this series - thanks!

Hello Jagan,
On Wed, 16 Mar 2016 22:04:23 +0530, Jagan Teki jteki@openedev.com wrote:
Hi Albert,
On Wednesday 16 March 2016 09:53 PM, Albert ARIBAUD wrote:
Hello Jagan,
On Wed, 16 Mar 2016 19:44:26 +0530, Jagan Teki jteki@openedev.com wrote:
On Wednesday 16 March 2016 07:00 PM, Cyrille Pitchen wrote:
Le 15/03/2016 19:21, Jagan Teki a écrit :
On Tuesday 15 March 2016 11:42 PM, Cyrille Pitchen wrote:
Hi all,
This series of patches fixes and extend the support of QSPI memories in the SPI flash framework. The updates are split into many parts to make it easier to understand and review but they should be considered as a whole.
This was tested on a Atmel sama5d2 xplained board with a Micron n25q128a memory.
Best regards,
Cyrille
Cyrille Pitchen (18): Revert "sf: Fix quad bit set for micron devices" sf: call spi_claim_bus() and spi_release_bus() only once per read, write or erase sf: replace spi_flash_read_common() calls by spi_flash_cmd_read() sf: remove spi_flash_write_common() sf: export spi_flash_wait_ready() function sf: share erase generic algorithm sf: share write generic algorithm sf: share read generic algorithm sf: add hooks to handle register read and write operations sf: move support of SST flash into generic spi_flash_write_alg() sf: fix selection of supported READ commands for QSPI memories sf: fix detection of QSPI memories when they boot in Quad or Dual mode sf: add helper function to set the number of dummy bytes sf: add 4byte address opcodes sf: prepare next fixes to support of QSPI memories by manufacturer sf: fix support of Micron memories ARM: at91: clock: add function to get QSPI clocks sf: add driver for Atmel QSPI controller
Appreciate for the work, we're working on spi-nor framework[1] planning to push in couple of weeks. Will let you know once it merged so that you can add your changes on top of that.
[1] http://git.denx.de/?p=u-boot/u-boot-spi.git;a=shortlog;h=refs/heads/spi-nor-...
Hi Jagan,
I've started to have a look on your branch. I hope it's not to late for few comments:
Globally I see the new code attend to match the spi-nor framework from Linux. OK that's fine but please note the current spi-nor framework in Linux has incomplete and sometime not working support of QSPI memories.
First, after a discussion with Brian and Bean on linux-mtd [1], Bean's commit to add support to Micron QSPI memories was reverted since it didn't work alone. In his reply, Brian agreed the code was not tested and should not have been merged.
This highlights a more general issue: currently, there is no mean for the spi-nor framework to notify the SPI controller driver about a SPI protocol change at the QSPI memory side. This applies to Micron memories when they enter their Quad I/O mode. If so, ALL commands, even JEDEC Read ID, Read Status Register, ..., MUST use the SPI 4-4-4 protocol. Commands sent using SPI 1-x-y protocols are no longer decoded properly. This also applies to Macronix and Winbond memories if they enter their QPI mode, which is the equivalent of Micron Quad I/O mode. This is why I've suggested to add 4 new fields in the struct spi_nor:
- .reg_proto: the SPI protocol to be used by .read_reg() and .write_reg() hooks.
- .read_proto: the SPI protocol to be used by the .read() hooks, maybe by the .read_mmap() also.
- .write_proto: the SPI protocol to be used by the .write() hooks
- .erase_proto: the SPI protocol to be used by the .erase() hooks.
(Q)SPI controller drivers cannot guess the protocol to be used from the command op code. Indeed, taking the Micron case as un example, the very same 0xeb op code may be used with the SPI 1-4-4 protocol (Micron Extended SPI mode) or with the SPI 4-4-4 protocol (Micron Quad I/O mode).
Also just some words about the naming of SPI x-y-z protocols:
- x refers to the number of I/O lines used to send the op code byte
- y is the number of I/O lines used to send the address, mode/dummy cycles (if these cycles exist for the command op code)
- z is the number of I/O lines used to send/receive data (if needed)
So the SNOR_OP_READ_1_1_2_IO macro for the Fast Read Dual I/O command (as opposed to the macro SNOR_OP_READ_1_1_2 macro for the Fast Read Dual Output command) doesn't make sense: it should be named SNOR_OP_READ_1_2_2.
Then about the value used for the dummy cycles, it's not always true that we don't care about initializing them. Depending on the configuration of the memory, some special dummy cycles, sometime called mode cycles, are used to during Fast Read operations to make the memory enter/leaver its Continuous Read mode. Once is Continuous Read mode, the op code byte is no longer sent, it is implicit and the command actually starts from the address cycles. This mode is mostly used for XIP applications hence is not relevant for mtd usage. However we should take care not to enter this Continuous Mode by mistake. Depending on the memory manufacturer, the Continuous Mode can be disabled by updating the relevant bit in some configuration register (e.g. setting the XIP bit in Micron Volatile Configuration Register) or by choosing the right op code (e.g. for Winbond memories in QPI mode, both the 0x0b and 0xeb op codes can be used for Fast Read 4-4-4 operation but only the 0xeb op code cares about the dummy cycle value to enter/leave the Continuous Read mode). Some Spansion memories use 6 dummy cycles for Fast Read 1-4-4 command as factory default, not 8.
Besides when sending a regular JEDEC Read ID (0x9f) command to probe the (Q)SPI memory, the current spi-nor framework assumes the Quad I/O or QPI mode is not already enabled. This not always true, some early bootloarders, such as the sama5d2 ROM Code, enables the Micron Quad I/O mode when configured to boot from the QSPI memory. If so, the QSPI memory no longer replies to the 0x9f command in SPI 1-1-1 protocol but instead to the 0xaf command in SPI 4-4-4 protocol.
Finally, about the proper way to describe the SPI controller capabilities, the SPI_TX_{DUAL, QUAD} and SPI_RX_{DUAL, QUAD} mode flags are set in the SPI framework based on the "spi-rx-bus-width" and "spi-tx-bus-width" DT properties already used in Linux. This is not enough to make the difference between the SPI 1-4-4 and SPI 4-4-4 protocols. Maybe some SPI controllers support the first protocol but not the latest. It could be good to add another argument to spi_nor_scan() providing an exhaustive list of SPI protocols supported by the SPI controller. Then to match this list with the list of SPI protocols supported by the SPI memory and select the proper protocol, this new argument should use the same range of values as the .flash_read field in the struct spi_nor_info used to describe the SPI memories.
For backward compatibility, the m25p80 driver could then convert the SPI modes into spi-nor read modes. Please have a look at patch 11 of my series; the chunk related to spi_flash_probe_slave() in sf_probe.c:
/* Convert SPI mode_rx and mode to SPI flash read commands */
- mode_rx = spi->mode_rx;
- if (mode_rx & SPI_RX_QUAD) {
e_rd_cmd = RD_NORM | QUAD_OUTPUT_FAST;
if (spi->mode & SPI_TX_QUAD)
e_rd_cmd |= QUAD_IO_FAST;
- } else if (mode_rx & SPI_RX_DUAL) {
e_rd_cmd = RD_NORM | DUAL_OUTPUT_FAST;
if (spi->mode & SPI_TX_DUAL)
e_rd_cmd |= DUAL_IO_FAST;
- } else if ((mode_rx & (SPI_RX_SLOW | SPI_RX_FAST)) == SPI_RX_SLOW) {
e_rd_cmd = ARRAY_SLOW;
- } else {
e_rd_cmd = RD_NORM;
- }
[...]
- ret = spi_flash_scan(flash);
- ret = spi_flash_scan(flash, e_rd_cmd);
I've spent a lot of time working on the QSPI memory topic so I can tell you that there are many other traps to avoid! If I can help you on this topic during your rework of the SPI NOR support, please let me know.
I understand your points, thanks for that and anyway this spi-nor work is a starting point for both syncing with Linux as well with new feature or for better tunning. And more over I started this work in 2014 end and it's been reviewing over and over and we finally landed up with MTD driver model.
So, please wait for sometime until this gets merged we definitely work together for better tunning, thanks!
If I understand Cyrille's post correctly, it is not about better tuning, it is about fixing existing issues, right? I mean, Cyrille is talking about situations where the code will be not simply slow, but plain wrong, correct?
If so, and since it appears Cyrills's patch series are bug fixes which the framework would not properly work if it did not integrate them anyway, I would agree with Marek that it makes more sense applying Cyrille's patches first.
OK, if these are bug fixes then what is the issue on working on top of spi-nor?
I believe you said spi-nor is not going to be submitted again until a couple of weeks, and then there will be a review period; that may not fit 2016.05. Cyrille's fixes could go out of the review phase before spi-nor does, and possibly even be ready for 2016.05.
and more over this series is on ML just now and need a proper review as well and will take some time too.
Yes, but non-structural bugfixes are easier and quicker to test than structural changes (which btw won't be entirely testable until the bug fixes are applied IIUC).
I have planned spi-nor series for this release better work on this series - thanks!
But if spi-nor is released in 2016.05 without Cyrille's patches, there will be known bugs unfixed, right?
-- Jagan
Amicalement,

Le 16/03/2016 15:14, Jagan Teki a écrit :
On Wednesday 16 March 2016 07:00 PM, Cyrille Pitchen wrote:
Le 15/03/2016 19:21, Jagan Teki a écrit :
On Tuesday 15 March 2016 11:42 PM, Cyrille Pitchen wrote:
Hi all,
This series of patches fixes and extend the support of QSPI memories in the SPI flash framework. The updates are split into many parts to make it easier to understand and review but they should be considered as a whole.
This was tested on a Atmel sama5d2 xplained board with a Micron n25q128a memory.
Best regards,
Cyrille
Cyrille Pitchen (18): Revert "sf: Fix quad bit set for micron devices" sf: call spi_claim_bus() and spi_release_bus() only once per read, write or erase sf: replace spi_flash_read_common() calls by spi_flash_cmd_read() sf: remove spi_flash_write_common() sf: export spi_flash_wait_ready() function sf: share erase generic algorithm sf: share write generic algorithm sf: share read generic algorithm sf: add hooks to handle register read and write operations sf: move support of SST flash into generic spi_flash_write_alg() sf: fix selection of supported READ commands for QSPI memories sf: fix detection of QSPI memories when they boot in Quad or Dual mode sf: add helper function to set the number of dummy bytes sf: add 4byte address opcodes sf: prepare next fixes to support of QSPI memories by manufacturer sf: fix support of Micron memories ARM: at91: clock: add function to get QSPI clocks sf: add driver for Atmel QSPI controller
Appreciate for the work, we're working on spi-nor framework[1] planning to push in couple of weeks. Will let you know once it merged so that you can add your changes on top of that.
[1] http://git.denx.de/?p=u-boot/u-boot-spi.git;a=shortlog;h=refs/heads/spi-nor-...
Hi Jagan,
I've started to have a look on your branch. I hope it's not to late for few comments:
Globally I see the new code attend to match the spi-nor framework from Linux. OK that's fine but please note the current spi-nor framework in Linux has incomplete and sometime not working support of QSPI memories.
First, after a discussion with Brian and Bean on linux-mtd [1], Bean's commit to add support to Micron QSPI memories was reverted since it didn't work alone. In his reply, Brian agreed the code was not tested and should not have been merged.
This highlights a more general issue: currently, there is no mean for the spi-nor framework to notify the SPI controller driver about a SPI protocol change at the QSPI memory side. This applies to Micron memories when they enter their Quad I/O mode. If so, ALL commands, even JEDEC Read ID, Read Status Register, ..., MUST use the SPI 4-4-4 protocol. Commands sent using SPI 1-x-y protocols are no longer decoded properly. This also applies to Macronix and Winbond memories if they enter their QPI mode, which is the equivalent of Micron Quad I/O mode. This is why I've suggested to add 4 new fields in the struct spi_nor:
- .reg_proto: the SPI protocol to be used by .read_reg() and .write_reg() hooks.
- .read_proto: the SPI protocol to be used by the .read() hooks, maybe by the .read_mmap() also.
- .write_proto: the SPI protocol to be used by the .write() hooks
- .erase_proto: the SPI protocol to be used by the .erase() hooks.
(Q)SPI controller drivers cannot guess the protocol to be used from the command op code. Indeed, taking the Micron case as un example, the very same 0xeb op code may be used with the SPI 1-4-4 protocol (Micron Extended SPI mode) or with the SPI 4-4-4 protocol (Micron Quad I/O mode).
Also just some words about the naming of SPI x-y-z protocols:
- x refers to the number of I/O lines used to send the op code byte
- y is the number of I/O lines used to send the address, mode/dummy cycles (if these cycles exist for the command op code)
- z is the number of I/O lines used to send/receive data (if needed)
So the SNOR_OP_READ_1_1_2_IO macro for the Fast Read Dual I/O command (as opposed to the macro SNOR_OP_READ_1_1_2 macro for the Fast Read Dual Output command) doesn't make sense: it should be named SNOR_OP_READ_1_2_2.
Then about the value used for the dummy cycles, it's not always true that we don't care about initializing them. Depending on the configuration of the memory, some special dummy cycles, sometime called mode cycles, are used to during Fast Read operations to make the memory enter/leaver its Continuous Read mode. Once is Continuous Read mode, the op code byte is no longer sent, it is implicit and the command actually starts from the address cycles. This mode is mostly used for XIP applications hence is not relevant for mtd usage. However we should take care not to enter this Continuous Mode by mistake. Depending on the memory manufacturer, the Continuous Mode can be disabled by updating the relevant bit in some configuration register (e.g. setting the XIP bit in Micron Volatile Configuration Register) or by choosing the right op code (e.g. for Winbond memories in QPI mode, both the 0x0b and 0xeb op codes can be used for Fast Read 4-4-4 operation but only the 0xeb op code cares about the dummy cycle value to enter/leave the Continuous Read mode). Some Spansion memories use 6 dummy cycles for Fast Read 1-4-4 command as factory default, not 8.
Besides when sending a regular JEDEC Read ID (0x9f) command to probe the (Q)SPI memory, the current spi-nor framework assumes the Quad I/O or QPI mode is not already enabled. This not always true, some early bootloarders, such as the sama5d2 ROM Code, enables the Micron Quad I/O mode when configured to boot from the QSPI memory. If so, the QSPI memory no longer replies to the 0x9f command in SPI 1-1-1 protocol but instead to the 0xaf command in SPI 4-4-4 protocol.
Finally, about the proper way to describe the SPI controller capabilities, the SPI_TX_{DUAL, QUAD} and SPI_RX_{DUAL, QUAD} mode flags are set in the SPI framework based on the "spi-rx-bus-width" and "spi-tx-bus-width" DT properties already used in Linux. This is not enough to make the difference between the SPI 1-4-4 and SPI 4-4-4 protocols. Maybe some SPI controllers support the first protocol but not the latest. It could be good to add another argument to spi_nor_scan() providing an exhaustive list of SPI protocols supported by the SPI controller. Then to match this list with the list of SPI protocols supported by the SPI memory and select the proper protocol, this new argument should use the same range of values as the .flash_read field in the struct spi_nor_info used to describe the SPI memories.
For backward compatibility, the m25p80 driver could then convert the SPI modes into spi-nor read modes. Please have a look at patch 11 of my series; the chunk related to spi_flash_probe_slave() in sf_probe.c:
/* Convert SPI mode_rx and mode to SPI flash read commands */
- mode_rx = spi->mode_rx;
- if (mode_rx & SPI_RX_QUAD) {
e_rd_cmd = RD_NORM | QUAD_OUTPUT_FAST;
if (spi->mode & SPI_TX_QUAD)
e_rd_cmd |= QUAD_IO_FAST;
- } else if (mode_rx & SPI_RX_DUAL) {
e_rd_cmd = RD_NORM | DUAL_OUTPUT_FAST;
if (spi->mode & SPI_TX_DUAL)
e_rd_cmd |= DUAL_IO_FAST;
- } else if ((mode_rx & (SPI_RX_SLOW | SPI_RX_FAST)) == SPI_RX_SLOW) {
e_rd_cmd = ARRAY_SLOW;
- } else {
e_rd_cmd = RD_NORM;
- }
[...]
- ret = spi_flash_scan(flash);
- ret = spi_flash_scan(flash, e_rd_cmd);
I've spent a lot of time working on the QSPI memory topic so I can tell you that there are many other traps to avoid! If I can help you on this topic during your rework of the SPI NOR support, please let me know.
I understand your points, thanks for that and anyway this spi-nor work is a starting point for both syncing with Linux as well with new feature or for better tunning. And more over I started this work in 2014 end and it's been reviewing over and over and we finally landed up with MTD driver model.
So, please wait for sometime until this gets merged we definitely work together for better tunning, thanks!
OK, no problem!
I will just send the additional patches I used to test with the sama5d2 xplained board. They are not related with the spi_flash/spi-nor framework itself but they are part of a WIP update of the sama5d2_xplained board support. If some people need those patches to unblock their own developments using QSPI memories on sama5d2 SoCs, all the material will be available :)
Best regards,
Cyrille

--- board/atmel/sama5d2_xplained/Kconfig | 14 ++++++++++++++ board/atmel/sama5d2_xplained/sama5d2_xplained.c | 14 +++++++++----- drivers/mmc/Kconfig | 6 ++++++ include/configs/at91-sama5_common.h | 10 ++++++++++ include/configs/sama5d2_xplained.h | 15 ++------------- 5 files changed, 41 insertions(+), 18 deletions(-)
diff --git a/board/atmel/sama5d2_xplained/Kconfig b/board/atmel/sama5d2_xplained/Kconfig index 55712e97454b..d4106b90f599 100644 --- a/board/atmel/sama5d2_xplained/Kconfig +++ b/board/atmel/sama5d2_xplained/Kconfig @@ -12,4 +12,18 @@ config SYS_SOC config SYS_CONFIG_NAME default "sama5d2_xplained"
+config ATMEL_SPI0 + bool "SPI0" + default y + +config ATMEL_SDHCI0 + bool "SDHCI0" + select ATMEL_SDHCI + default y + +config ATMEL_SDHCI1 + bool "SDHCI1" + select ATMEL_SDHCI + default y + endif diff --git a/board/atmel/sama5d2_xplained/sama5d2_xplained.c b/board/atmel/sama5d2_xplained/sama5d2_xplained.c index 10edf28a9bd6..4dd83e5e991f 100644 --- a/board/atmel/sama5d2_xplained/sama5d2_xplained.c +++ b/board/atmel/sama5d2_xplained/sama5d2_xplained.c @@ -25,6 +25,7 @@
DECLARE_GLOBAL_DATA_PTR;
+#ifndef CONFIG_DM_SPI int spi_cs_is_valid(unsigned int bus, unsigned int cs) { return bus == 0 && cs == 0; @@ -39,7 +40,9 @@ void spi_cs_deactivate(struct spi_slave *slave) { atmel_pio4_set_pio_output(AT91_PIO_PORTA, 17, 1); } +#endif
+#ifdef CONFIG_ATMEL_SPI0 static void board_spi0_hw_init(void) { atmel_pio4_set_a_periph(AT91_PIO_PORTA, 14, 0); @@ -50,6 +53,7 @@ static void board_spi0_hw_init(void)
at91_periph_clk_enable(ATMEL_ID_SPI0); } +#endif
static void board_usb_hw_init(void) { @@ -157,6 +161,7 @@ static void board_gmac_hw_init(void) at91_periph_clk_enable(ATMEL_ID_GMAC); }
+#ifdef CONFIG_ATMEL_SDHCI0 static void board_sdhci0_hw_init(void) { atmel_pio4_set_a_periph(AT91_PIO_PORTA, 0, 0); /* SDMMC0_CK */ @@ -176,7 +181,9 @@ static void board_sdhci0_hw_init(void) at91_enable_periph_generated_clk(ATMEL_ID_SDMMC0, GCK_CSS_PLLA_CLK, 1); } +#endif
+#ifdef CONFIG_ATMEL_SDHCI1 static void board_sdhci1_hw_init(void) { atmel_pio4_set_e_periph(AT91_PIO_PORTA, 18, 0); /* SDMMC1_DAT0 */ @@ -192,6 +199,7 @@ static void board_sdhci1_hw_init(void) at91_enable_periph_generated_clk(ATMEL_ID_SDMMC1, GCK_CSS_PLLA_CLK, 1); } +#endif
int board_mmc_init(bd_t *bis) { @@ -230,17 +238,15 @@ int board_init(void) /* address of boot parameters */ gd->bd->bi_boot_params = CONFIG_SYS_SDRAM_BASE + 0x100;
-#ifdef CONFIG_ATMEL_SPI +#ifdef CONFIG_ATMEL_SPI0 board_spi0_hw_init(); #endif -#ifdef CONFIG_ATMEL_SDHCI #ifdef CONFIG_ATMEL_SDHCI0 board_sdhci0_hw_init(); #endif #ifdef CONFIG_ATMEL_SDHCI1 board_sdhci1_hw_init(); #endif -#endif #ifdef CONFIG_MACB board_gmac_hw_init(); #endif @@ -289,14 +295,12 @@ void spl_board_init(void) #ifdef CONFIG_SYS_USE_SERIALFLASH board_spi0_hw_init(); #endif -#ifdef CONFIG_ATMEL_SDHCI #ifdef CONFIG_ATMEL_SDHCI0 board_sdhci0_hw_init(); #endif #ifdef CONFIG_ATMEL_SDHCI1 board_sdhci1_hw_init(); #endif -#endif }
static void ddrc_conf(struct atmel_mpddrc_config *ddrc) diff --git a/drivers/mmc/Kconfig b/drivers/mmc/Kconfig index dc8532fe9373..b3d085119ac8 100644 --- a/drivers/mmc/Kconfig +++ b/drivers/mmc/Kconfig @@ -49,4 +49,10 @@ config MMC_UNIPHIER help This selects support for the SD/MMC Host Controller on UniPhier SoCs.
+config ATMEL_SDHCI + bool "Atmel SD Host Controller Interface" + depends on ARCH_AT91 + help + This enables support for the Atmel SD/eMMC controller + endmenu diff --git a/include/configs/at91-sama5_common.h b/include/configs/at91-sama5_common.h index 6525b5c3701e..0297fd781dc6 100644 --- a/include/configs/at91-sama5_common.h +++ b/include/configs/at91-sama5_common.h @@ -104,6 +104,16 @@ "sf read 0x21000000 0x60000 0xc000; " \ "sf read 0x22000000 0x6c000 0x394000; " \ "bootz 0x22000000 - 0x21000000" +#elif CONFIG_SYS_USE_QSPIFLASH +/* u-boot env in QSPI flash, by default is bus 0 and cs 0 */ +#define CONFIG_ENV_IS_IN_SPI_FLASH +#define CONFIG_ENV_OFFSET 0x10000 +#define CONFIG_ENV_SIZE 0x10000 +#define CONFIG_ENV_SECT_SIZE 0x10000 +#define CONFIG_BOOTCOMMAND "sf probe 0; " \ + "sf read 0x21000000 0x60000 0x10000; " \ + "sf read 0x22000000 0x70000 0x500000; " \ + "bootz 0x22000000 - 0x21000000" #endif
#endif diff --git a/include/configs/sama5d2_xplained.h b/include/configs/sama5d2_xplained.h index 272257ea0edd..3391a0ea5b45 100644 --- a/include/configs/sama5d2_xplained.h +++ b/include/configs/sama5d2_xplained.h @@ -16,6 +16,8 @@ #include "at91-sama5_common.h"
/* serial console */ +#undef CONFIG_BAUDRATE +#define CONFIG_BAUDRATE 57600 #define CONFIG_ATMEL_USART #define CONFIG_USART_BASE ATMEL_BASE_UART1 #define CONFIG_USART_ID ATMEL_ID_UART1 @@ -37,16 +39,6 @@ #undef CONFIG_AT91_GPIO #define CONFIG_ATMEL_PIO4
-/* SerialFlash */ -#ifdef CONFIG_CMD_SF -#define CONFIG_ATMEL_SPI -#define CONFIG_ATMEL_SPI0 -#define CONFIG_SPI_FLASH_ATMEL -#define CONFIG_SF_DEFAULT_BUS 0 -#define CONFIG_SF_DEFAULT_CS 0 -#define CONFIG_SF_DEFAULT_SPEED 30000000 -#endif - /* NAND flash */ #undef CONFIG_CMD_NAND
@@ -57,9 +49,6 @@ #define CONFIG_MMC #define CONFIG_GENERIC_MMC #define CONFIG_SDHCI -#define CONFIG_ATMEL_SDHCI -#define CONFIG_ATMEL_SDHCI0 -#define CONFIG_ATMEL_SDHCI1 #define CONFIG_SUPPORT_EMMC_BOOT #endif

Signed-off-by: Cyrille Pitchen cyrille.pitchen@atmel.com --- board/atmel/sama5d2_xplained/Kconfig | 6 ++++++ board/atmel/sama5d2_xplained/sama5d2_xplained.c | 18 ++++++++++++++++++ 2 files changed, 24 insertions(+)
diff --git a/board/atmel/sama5d2_xplained/Kconfig b/board/atmel/sama5d2_xplained/Kconfig index d4106b90f599..1659a2043b04 100644 --- a/board/atmel/sama5d2_xplained/Kconfig +++ b/board/atmel/sama5d2_xplained/Kconfig @@ -26,4 +26,10 @@ config ATMEL_SDHCI1 select ATMEL_SDHCI default y
+config ATMEL_QSPI0 + bool "QSPI0" + select DM_SPI + select ATMEL_QSPI + default n + endif diff --git a/board/atmel/sama5d2_xplained/sama5d2_xplained.c b/board/atmel/sama5d2_xplained/sama5d2_xplained.c index 4dd83e5e991f..6d4c2da99bc3 100644 --- a/board/atmel/sama5d2_xplained/sama5d2_xplained.c +++ b/board/atmel/sama5d2_xplained/sama5d2_xplained.c @@ -55,6 +55,21 @@ static void board_spi0_hw_init(void) } #endif
+#ifdef CONFIG_ATMEL_QSPI0 +static void board_qspi0_hw_init(void) +{ + /* QSPI0 - ioset 3 */ + atmel_pio4_set_f_periph(AT91_PIO_PORTA, 22, 0); /* SCK */ + atmel_pio4_set_f_periph(AT91_PIO_PORTA, 23, 0); /* CS */ + atmel_pio4_set_f_periph(AT91_PIO_PORTA, 24, 1); /* IO0 */ + atmel_pio4_set_f_periph(AT91_PIO_PORTA, 25, 1); /* IO1 */ + atmel_pio4_set_f_periph(AT91_PIO_PORTA, 26, 1); /* IO2 */ + atmel_pio4_set_f_periph(AT91_PIO_PORTA, 27, 1); /* IO3 */ + + at91_periph_clk_enable(ATMEL_ID_QSPI0); +} +#endif + static void board_usb_hw_init(void) { atmel_pio4_set_pio_output(AT91_PIO_PORTB, 10, 1); @@ -241,6 +256,9 @@ int board_init(void) #ifdef CONFIG_ATMEL_SPI0 board_spi0_hw_init(); #endif +#ifdef CONFIG_ATMEL_QSPI0 + board_qspi0_hw_init(); +#endif #ifdef CONFIG_ATMEL_SDHCI0 board_sdhci0_hw_init(); #endif

Signed-off-by: Cyrille Pitchen cyrille.pitchen@atmel.com --- arch/arm/dts/Makefile | 3 +++ arch/arm/dts/at91-sama5d2_xplained.dts | 33 +++++++++++++++++++++++++++++++++ arch/arm/dts/sama5d2.dtsi | 19 +++++++++++++++++++ 3 files changed, 55 insertions(+) create mode 100644 arch/arm/dts/at91-sama5d2_xplained.dts create mode 100644 arch/arm/dts/sama5d2.dtsi
diff --git a/arch/arm/dts/Makefile b/arch/arm/dts/Makefile index 578038be21f9..97521f16aac4 100644 --- a/arch/arm/dts/Makefile +++ b/arch/arm/dts/Makefile @@ -207,6 +207,9 @@ dtb-$(CONFIG_SOC_KEYSTONE) += k2hk-evm.dtb \ k2e-evm.dtb \ k2g-evm.dtb
+dtb-$(CONFIG_ARCH_AT91) += \ + at91-sama5d2_xplained.dtb + targets += $(dtb-y)
# Add any required device tree compiler flags here diff --git a/arch/arm/dts/at91-sama5d2_xplained.dts b/arch/arm/dts/at91-sama5d2_xplained.dts new file mode 100644 index 000000000000..2394f6bd4249 --- /dev/null +++ b/arch/arm/dts/at91-sama5d2_xplained.dts @@ -0,0 +1,33 @@ +/dts-v1/; +#include "sama5d2.dtsi" + +/ { + model = "Atmel SAMA5D2 Xplained"; + compatible = "atmel,sama5d2-xplained", "atmel,sama5d2", "atmel,sama5"; + + chosen { + stdout-path = "serial0"; + }; +}; + +&qspi0 { + status = "okay"; + + flash@0 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "atmel,sama5d2-qspi-flash"; + reg = <0>; + spi-max-frequency = <83000000>; + + partition@00000000 { + label = "boot"; + reg = <0x00000000 0x00c00000>; + }; + + partition@00c00000 { + label = "rootfs"; + reg = <0x00c00000 0x00000000>; + }; + }; +}; diff --git a/arch/arm/dts/sama5d2.dtsi b/arch/arm/dts/sama5d2.dtsi new file mode 100644 index 000000000000..dccc66cccb88 --- /dev/null +++ b/arch/arm/dts/sama5d2.dtsi @@ -0,0 +1,19 @@ +#include "skeleton.dtsi" + +/ { + model = "Atmel SAMA5D2 family SoC"; + compatible = "atmel,sama5d2"; + + aliases { + spi0 = &qspi0; + }; + + qspi0: spi@f0020000 { + compatible = "atmel,sama5d2-qspi"; + reg = <0xf0020000 0x100>, <0xd0000000 0x08000000>; + reg-names = "qspi_base", "qspi_mmap"; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; +}; \ No newline at end of file

This patch fixes the "sf probe" command. The very first SPI flash probe passes, for instance when u-boot tries to read its environment settings from a (Q)SPI memory but next "sf probe" commands fail because the flash memory node is unbound from the SPI controller children nodes.
Signed-off-by: Cyrille Pitchen cyrille.pitchen@atmel.com --- cmd/sf.c | 1 - 1 file changed, 1 deletion(-)
diff --git a/cmd/sf.c b/cmd/sf.c index 42862d9d921a..5a666bbe9491 100644 --- a/cmd/sf.c +++ b/cmd/sf.c @@ -123,7 +123,6 @@ static int do_spi_flash_probe(int argc, char * const argv[]) ret = spi_find_bus_and_cs(bus, cs, &bus_dev, &new); if (!ret) { device_remove(new); - device_unbind(new); } flash = NULL; ret = spi_flash_probe_bus_cs(bus, cs, speed, mode, &new);

Hi All,
please excuse the late reply to this thread. But I'm very interested in QSPI, as one of my customers uses Micron QSPI NOR and really wants to take full advantage of the device (quad access) to speed up the overall boot process. This is on SoCFPGA btw.
Please allow me a few comments below.
On 16.03.2016 15:14, Jagan Teki wrote:
On Wednesday 16 March 2016 07:00 PM, Cyrille Pitchen wrote:
Le 15/03/2016 19:21, Jagan Teki a écrit :
On Tuesday 15 March 2016 11:42 PM, Cyrille Pitchen wrote:
Hi all,
This series of patches fixes and extend the support of QSPI memories in the SPI flash framework. The updates are split into many parts to make it easier to understand and review but they should be considered as a whole.
This was tested on a Atmel sama5d2 xplained board with a Micron n25q128a memory.
Best regards,
Cyrille
Cyrille Pitchen (18): Revert "sf: Fix quad bit set for micron devices" sf: call spi_claim_bus() and spi_release_bus() only once per read, write or erase sf: replace spi_flash_read_common() calls by spi_flash_cmd_read() sf: remove spi_flash_write_common() sf: export spi_flash_wait_ready() function sf: share erase generic algorithm sf: share write generic algorithm sf: share read generic algorithm sf: add hooks to handle register read and write operations sf: move support of SST flash into generic spi_flash_write_alg() sf: fix selection of supported READ commands for QSPI memories sf: fix detection of QSPI memories when they boot in Quad or Dual mode sf: add helper function to set the number of dummy bytes sf: add 4byte address opcodes sf: prepare next fixes to support of QSPI memories by manufacturer sf: fix support of Micron memories ARM: at91: clock: add function to get QSPI clocks sf: add driver for Atmel QSPI controller
Appreciate for the work, we're working on spi-nor framework[1] planning to push in couple of weeks. Will let you know once it merged so that you can add your changes on top of that.
[1] http://git.denx.de/?p=u-boot/u-boot-spi.git;a=shortlog;h=refs/heads/spi-nor-...
Hi Jagan,
I've started to have a look on your branch. I hope it's not to late for few comments:
Globally I see the new code attend to match the spi-nor framework from Linux. OK that's fine but please note the current spi-nor framework in Linux has incomplete and sometime not working support of QSPI memories.
First, after a discussion with Brian and Bean on linux-mtd [1], Bean's commit to add support to Micron QSPI memories was reverted since it didn't work alone. In his reply, Brian agreed the code was not tested and should not have been merged.
This highlights a more general issue: currently, there is no mean for the spi-nor framework to notify the SPI controller driver about a SPI protocol change at the QSPI memory side. This applies to Micron memories when they enter their Quad I/O mode. If so, ALL commands, even JEDEC Read ID, Read Status Register, ..., MUST use the SPI 4-4-4 protocol. Commands sent using SPI 1-x-y protocols are no longer decoded properly. This also applies to Macronix and Winbond memories if they enter their QPI mode, which is the equivalent of Micron Quad I/O mode. This is why I've suggested to add 4 new fields in the struct spi_nor:
- .reg_proto: the SPI protocol to be used by .read_reg() and .write_reg() hooks.
- .read_proto: the SPI protocol to be used by the .read() hooks, maybe
by the .read_mmap() also.
- .write_proto: the SPI protocol to be used by the .write() hooks
- .erase_proto: the SPI protocol to be used by the .erase() hooks.
(Q)SPI controller drivers cannot guess the protocol to be used from the command op code. Indeed, taking the Micron case as un example, the very same 0xeb op code may be used with the SPI 1-4-4 protocol (Micron Extended SPI mode) or with the SPI 4-4-4 protocol (Micron Quad I/O mode).
Also just some words about the naming of SPI x-y-z protocols:
- x refers to the number of I/O lines used to send the op code byte
- y is the number of I/O lines used to send the address, mode/dummy
cycles (if these cycles exist for the command op code)
- z is the number of I/O lines used to send/receive data (if needed)
So the SNOR_OP_READ_1_1_2_IO macro for the Fast Read Dual I/O command (as opposed to the macro SNOR_OP_READ_1_1_2 macro for the Fast Read Dual Output command) doesn't make sense: it should be named SNOR_OP_READ_1_2_2.
Then about the value used for the dummy cycles, it's not always true that we don't care about initializing them. Depending on the configuration of the memory, some special dummy cycles, sometime called mode cycles, are used to during Fast Read operations to make the memory enter/leaver its Continuous Read mode. Once is Continuous Read mode, the op code byte is no longer sent, it is implicit and the command actually starts from the address cycles. This mode is mostly used for XIP applications hence is not relevant for mtd usage. However we should take care not to enter this Continuous Mode by mistake. Depending on the memory manufacturer, the Continuous Mode can be disabled by updating the relevant bit in some configuration register (e.g. setting the XIP bit in Micron Volatile Configuration Register) or by choosing the right op code (e.g. for Winbond memories in QPI mode, both the 0x0b and 0xeb op codes can be used for Fast Read 4-4-4 operation but only the 0xeb op code cares about the dummy cycle value to enter/leave the Continuous Read mode). Some Spansion memories use 6 dummy cycles for Fast Read 1-4-4 command as factory default, not 8.
Besides when sending a regular JEDEC Read ID (0x9f) command to probe the (Q)SPI memory, the current spi-nor framework assumes the Quad I/O or QPI mode is not already enabled. This not always true, some early bootloarders, such as the sama5d2 ROM Code, enables the Micron Quad I/O mode when configured to boot from the QSPI memory. If so, the QSPI memory no longer replies to the 0x9f command in SPI 1-1-1 protocol but instead to the 0xaf command in SPI 4-4-4 protocol.
Finally, about the proper way to describe the SPI controller capabilities, the SPI_TX_{DUAL, QUAD} and SPI_RX_{DUAL, QUAD} mode flags are set in the SPI framework based on the "spi-rx-bus-width" and "spi-tx-bus-width" DT properties already used in Linux. This is not enough to make the difference between the SPI 1-4-4 and SPI 4-4-4 protocols. Maybe some SPI controllers support the first protocol but not the latest. It could be good to add another argument to spi_nor_scan() providing an exhaustive list of SPI protocols supported by the SPI controller. Then to match this list with the list of SPI protocols supported by the SPI memory and select the proper protocol, this new argument should use the same range of values as the .flash_read field in the struct spi_nor_info used to describe the SPI memories.
For backward compatibility, the m25p80 driver could then convert the SPI modes into spi-nor read modes. Please have a look at patch 11 of my series; the chunk related to spi_flash_probe_slave() in sf_probe.c:
/* Convert SPI mode_rx and mode to SPI flash read commands */
- mode_rx = spi->mode_rx;
- if (mode_rx & SPI_RX_QUAD) {
e_rd_cmd = RD_NORM | QUAD_OUTPUT_FAST;
if (spi->mode & SPI_TX_QUAD)
e_rd_cmd |= QUAD_IO_FAST;
- } else if (mode_rx & SPI_RX_DUAL) {
e_rd_cmd = RD_NORM | DUAL_OUTPUT_FAST;
if (spi->mode & SPI_TX_DUAL)
e_rd_cmd |= DUAL_IO_FAST;
- } else if ((mode_rx & (SPI_RX_SLOW | SPI_RX_FAST)) == SPI_RX_SLOW) {
e_rd_cmd = ARRAY_SLOW;
- } else {
e_rd_cmd = RD_NORM;
- }
[...]
- ret = spi_flash_scan(flash);
- ret = spi_flash_scan(flash, e_rd_cmd);
I've spent a lot of time working on the QSPI memory topic so I can tell you that there are many other traps to avoid! If I can help you on this topic during your rework of the SPI NOR support, please let me know.
Thanks Cyrille, for this very detailed and good explanation. This is really appreciated. And I really hope that Jagan will get back to your generous offer, to assist with any such QSPI NOR related problems. I'll definitely remember this and might bug you at some time... ;)
I understand your points, thanks for that and anyway this spi-nor work is a starting point for both syncing with Linux as well with new feature or for better tunning. And more over I started this work in 2014 end and it's been reviewing over and over and we finally landed up with MTD driver model.
So, please wait for sometime until this gets merged we definitely work together for better tunning, thanks!
Jagan, what is the current plan here? I have to admit that I didn't check closely on your SPI-NOR work lately, sorry. So I can't really comment with a profound deep knowledge. But from what I read in Cyrille's patchset and explanation above, his patchset fixes some issues and brings us also more in line with the (upcoming) Linux framework. So I would really love to see this integrated into mainline quickly. Allowing a broader test basis on multiple platforms. Will it be a big task for Cyrille to rebase his patchset on top of your patches? Can you perhaps assist Cyrille with this? Or would it perhaps make sense to postpone your patchset and integrate Cyrille's first instead?
Cyrille, if Jagans rework goes in first, do you already have a plan on when to rebase your patchset on top of this? Or do you see some "show-stoppers" here?
Comments welcome.
Thanks, Stefan

Hi Stefan,
Le 18/03/2016 14:48, Stefan Roese a écrit :
Hi All,
please excuse the late reply to this thread. But I'm very interested in QSPI, as one of my customers uses Micron QSPI NOR and really wants to take full advantage of the device (quad access) to speed up the overall boot process. This is on SoCFPGA btw.
Please allow me a few comments below.
On 16.03.2016 15:14, Jagan Teki wrote:
On Wednesday 16 March 2016 07:00 PM, Cyrille Pitchen wrote:
Le 15/03/2016 19:21, Jagan Teki a écrit :
On Tuesday 15 March 2016 11:42 PM, Cyrille Pitchen wrote:
Hi all,
This series of patches fixes and extend the support of QSPI memories in the SPI flash framework. The updates are split into many parts to make it easier to understand and review but they should be considered as a whole.
This was tested on a Atmel sama5d2 xplained board with a Micron n25q128a memory.
Best regards,
Cyrille
Cyrille Pitchen (18): Revert "sf: Fix quad bit set for micron devices" sf: call spi_claim_bus() and spi_release_bus() only once per read, write or erase sf: replace spi_flash_read_common() calls by spi_flash_cmd_read() sf: remove spi_flash_write_common() sf: export spi_flash_wait_ready() function sf: share erase generic algorithm sf: share write generic algorithm sf: share read generic algorithm sf: add hooks to handle register read and write operations sf: move support of SST flash into generic spi_flash_write_alg() sf: fix selection of supported READ commands for QSPI memories sf: fix detection of QSPI memories when they boot in Quad or Dual mode sf: add helper function to set the number of dummy bytes sf: add 4byte address opcodes sf: prepare next fixes to support of QSPI memories by manufacturer sf: fix support of Micron memories ARM: at91: clock: add function to get QSPI clocks sf: add driver for Atmel QSPI controller
Appreciate for the work, we're working on spi-nor framework[1] planning to push in couple of weeks. Will let you know once it merged so that you can add your changes on top of that.
[1] http://git.denx.de/?p=u-boot/u-boot-spi.git;a=shortlog;h=refs/heads/spi-nor-...
Hi Jagan,
I've started to have a look on your branch. I hope it's not to late for few comments:
Globally I see the new code attend to match the spi-nor framework from Linux. OK that's fine but please note the current spi-nor framework in Linux has incomplete and sometime not working support of QSPI memories.
First, after a discussion with Brian and Bean on linux-mtd [1], Bean's commit to add support to Micron QSPI memories was reverted since it didn't work alone. In his reply, Brian agreed the code was not tested and should not have been merged.
This highlights a more general issue: currently, there is no mean for the spi-nor framework to notify the SPI controller driver about a SPI protocol change at the QSPI memory side. This applies to Micron memories when they enter their Quad I/O mode. If so, ALL commands, even JEDEC Read ID, Read Status Register, ..., MUST use the SPI 4-4-4 protocol. Commands sent using SPI 1-x-y protocols are no longer decoded properly. This also applies to Macronix and Winbond memories if they enter their QPI mode, which is the equivalent of Micron Quad I/O mode. This is why I've suggested to add 4 new fields in the struct spi_nor:
- .reg_proto: the SPI protocol to be used by .read_reg() and .write_reg() hooks.
- .read_proto: the SPI protocol to be used by the .read() hooks, maybe
by the .read_mmap() also.
- .write_proto: the SPI protocol to be used by the .write() hooks
- .erase_proto: the SPI protocol to be used by the .erase() hooks.
(Q)SPI controller drivers cannot guess the protocol to be used from the command op code. Indeed, taking the Micron case as un example, the very same 0xeb op code may be used with the SPI 1-4-4 protocol (Micron Extended SPI mode) or with the SPI 4-4-4 protocol (Micron Quad I/O mode).
Also just some words about the naming of SPI x-y-z protocols:
- x refers to the number of I/O lines used to send the op code byte
- y is the number of I/O lines used to send the address, mode/dummy
cycles (if these cycles exist for the command op code)
- z is the number of I/O lines used to send/receive data (if needed)
So the SNOR_OP_READ_1_1_2_IO macro for the Fast Read Dual I/O command (as opposed to the macro SNOR_OP_READ_1_1_2 macro for the Fast Read Dual Output command) doesn't make sense: it should be named SNOR_OP_READ_1_2_2.
Then about the value used for the dummy cycles, it's not always true that we don't care about initializing them. Depending on the configuration of the memory, some special dummy cycles, sometime called mode cycles, are used to during Fast Read operations to make the memory enter/leaver its Continuous Read mode. Once is Continuous Read mode, the op code byte is no longer sent, it is implicit and the command actually starts from the address cycles. This mode is mostly used for XIP applications hence is not relevant for mtd usage. However we should take care not to enter this Continuous Mode by mistake. Depending on the memory manufacturer, the Continuous Mode can be disabled by updating the relevant bit in some configuration register (e.g. setting the XIP bit in Micron Volatile Configuration Register) or by choosing the right op code (e.g. for Winbond memories in QPI mode, both the 0x0b and 0xeb op codes can be used for Fast Read 4-4-4 operation but only the 0xeb op code cares about the dummy cycle value to enter/leave the Continuous Read mode). Some Spansion memories use 6 dummy cycles for Fast Read 1-4-4 command as factory default, not 8.
Besides when sending a regular JEDEC Read ID (0x9f) command to probe the (Q)SPI memory, the current spi-nor framework assumes the Quad I/O or QPI mode is not already enabled. This not always true, some early bootloarders, such as the sama5d2 ROM Code, enables the Micron Quad I/O mode when configured to boot from the QSPI memory. If so, the QSPI memory no longer replies to the 0x9f command in SPI 1-1-1 protocol but instead to the 0xaf command in SPI 4-4-4 protocol.
Finally, about the proper way to describe the SPI controller capabilities, the SPI_TX_{DUAL, QUAD} and SPI_RX_{DUAL, QUAD} mode flags are set in the SPI framework based on the "spi-rx-bus-width" and "spi-tx-bus-width" DT properties already used in Linux. This is not enough to make the difference between the SPI 1-4-4 and SPI 4-4-4 protocols. Maybe some SPI controllers support the first protocol but not the latest. It could be good to add another argument to spi_nor_scan() providing an exhaustive list of SPI protocols supported by the SPI controller. Then to match this list with the list of SPI protocols supported by the SPI memory and select the proper protocol, this new argument should use the same range of values as the .flash_read field in the struct spi_nor_info used to describe the SPI memories.
For backward compatibility, the m25p80 driver could then convert the SPI modes into spi-nor read modes. Please have a look at patch 11 of my series; the chunk related to spi_flash_probe_slave() in sf_probe.c:
/* Convert SPI mode_rx and mode to SPI flash read commands */
- mode_rx = spi->mode_rx;
- if (mode_rx & SPI_RX_QUAD) {
e_rd_cmd = RD_NORM | QUAD_OUTPUT_FAST;
if (spi->mode & SPI_TX_QUAD)
e_rd_cmd |= QUAD_IO_FAST;
- } else if (mode_rx & SPI_RX_DUAL) {
e_rd_cmd = RD_NORM | DUAL_OUTPUT_FAST;
if (spi->mode & SPI_TX_DUAL)
e_rd_cmd |= DUAL_IO_FAST;
- } else if ((mode_rx & (SPI_RX_SLOW | SPI_RX_FAST)) == SPI_RX_SLOW) {
e_rd_cmd = ARRAY_SLOW;
- } else {
e_rd_cmd = RD_NORM;
- }
[...]
- ret = spi_flash_scan(flash);
- ret = spi_flash_scan(flash, e_rd_cmd);
I've spent a lot of time working on the QSPI memory topic so I can tell you that there are many other traps to avoid! If I can help you on this topic during your rework of the SPI NOR support, please let me know.
Thanks Cyrille, for this very detailed and good explanation. This is really appreciated. And I really hope that Jagan will get back to your generous offer, to assist with any such QSPI NOR related problems. I'll definitely remember this and might bug you at some time... ;)
I understand your points, thanks for that and anyway this spi-nor work is a starting point for both syncing with Linux as well with new feature or for better tunning. And more over I started this work in 2014 end and it's been reviewing over and over and we finally landed up with MTD driver model.
So, please wait for sometime until this gets merged we definitely work together for better tunning, thanks!
Jagan, what is the current plan here? I have to admit that I didn't check closely on your SPI-NOR work lately, sorry. So I can't really comment with a profound deep knowledge. But from what I read in Cyrille's patchset and explanation above, his patchset fixes some issues and brings us also more in line with the (upcoming) Linux framework. So I would really love to see this integrated into mainline quickly. Allowing a broader test basis on multiple platforms. Will it be a big task for Cyrille to rebase his patchset on top of your patches? Can you perhaps assist Cyrille with this? Or would it perhaps make sense to postpone your patchset and integrate Cyrille's first instead?
Cyrille, if Jagans rework goes in first, do you already have a plan on when to rebase your patchset on top of this? Or do you see some "show-stoppers" here?
Well if I need to rebase my patchset, for sure I will wait for Jagan's rework to be completed and accepted first. I don't think it would be efficient to rebase the QSPI series on the fly before the rework is totally stabilized.
One of the commit message from u-boot-spi.git can sum up the global rework: "sf: Drop entire spi-flash framework" [1]
Indeed all files from drivers/mtd/spi are deleted, the next commits replace them by new files in the spi-nor folder. These new files are almost some "copy/paste" of drivers/mtd/spi-nor files from Linux.
OK, why not but it also implies that I can't simply rebase my patchset. Since the all files are different it means I have to write a new series of patches from scratch. As I said, I won't start such a work before the new framework is totally accepted and integrated in next releases of u-boot.
I don't say using the Linux code as a reference is a bad idea, indeed there are good things in the spi-nor framework to be taken. For instance, in my own series I added .read_reg() and .write_reg() hooks the same way as done in the Linux source. I just warn developers that currently the Linux code misses some stuff to support QSPI memories efficiently. As an example, spi_nor_scan() fails to read the JEDEC ID if the QSPI memory is already in QPI mode (Micron, Macronix, Winbond, ...). This issue didn't exist with regular SPI NOR flash memories as they are almost "stateless" as compared to QSPI memories. SPI memories always use a single protocol, SPI 1-1-1 with MISO and MOSI I/O lines, for all commands. On the other hand, QSPI memories are much complex: you have to deal with many different protocols and find a way to guess the current internal state of the QSPI memory before sending commands to it.
So, this is why I've send all the material I have in the case other developers are blocked and need support for QSPI memories in u-boot.
Best regards,
Cyrille
[1] http://git.denx.de/?p=u-boot/u-boot-spi.git;a=commit;h=7ae4605cd2b3484be9e61...
Comments welcome.
Thanks, Stefan

Hi Cyrille,
On 18 March 2016 at 20:48, Cyrille Pitchen cyrille.pitchen@atmel.com wrote:
Hi Stefan,
Le 18/03/2016 14:48, Stefan Roese a écrit :
Hi All,
please excuse the late reply to this thread. But I'm very interested in QSPI, as one of my customers uses Micron QSPI NOR and really wants to take full advantage of the device (quad access) to speed up the overall boot process. This is on SoCFPGA btw.
Please allow me a few comments below.
On 16.03.2016 15:14, Jagan Teki wrote:
On Wednesday 16 March 2016 07:00 PM, Cyrille Pitchen wrote:
Le 15/03/2016 19:21, Jagan Teki a écrit :
On Tuesday 15 March 2016 11:42 PM, Cyrille Pitchen wrote:
Hi all,
This series of patches fixes and extend the support of QSPI memories in the SPI flash framework. The updates are split into many parts to make it easier to understand and review but they should be considered as a whole.
This was tested on a Atmel sama5d2 xplained board with a Micron n25q128a memory.
Best regards,
Cyrille
Cyrille Pitchen (18): Revert "sf: Fix quad bit set for micron devices" sf: call spi_claim_bus() and spi_release_bus() only once per read, write or erase sf: replace spi_flash_read_common() calls by spi_flash_cmd_read() sf: remove spi_flash_write_common() sf: export spi_flash_wait_ready() function sf: share erase generic algorithm sf: share write generic algorithm sf: share read generic algorithm sf: add hooks to handle register read and write operations sf: move support of SST flash into generic spi_flash_write_alg() sf: fix selection of supported READ commands for QSPI memories sf: fix detection of QSPI memories when they boot in Quad or Dual mode sf: add helper function to set the number of dummy bytes sf: add 4byte address opcodes sf: prepare next fixes to support of QSPI memories by manufacturer sf: fix support of Micron memories ARM: at91: clock: add function to get QSPI clocks sf: add driver for Atmel QSPI controller
Appreciate for the work, we're working on spi-nor framework[1] planning to push in couple of weeks. Will let you know once it merged so that you can add your changes on top of that.
[1] http://git.denx.de/?p=u-boot/u-boot-spi.git;a=shortlog;h=refs/heads/spi-nor-...
Hi Jagan,
I've started to have a look on your branch. I hope it's not to late for few comments:
Globally I see the new code attend to match the spi-nor framework from Linux. OK that's fine but please note the current spi-nor framework in Linux has incomplete and sometime not working support of QSPI memories.
First, after a discussion with Brian and Bean on linux-mtd [1], Bean's commit to add support to Micron QSPI memories was reverted since it didn't work alone. In his reply, Brian agreed the code was not tested and should not have been merged.
This highlights a more general issue: currently, there is no mean for the spi-nor framework to notify the SPI controller driver about a SPI protocol change at the QSPI memory side. This applies to Micron memories when they enter their Quad I/O mode. If so, ALL commands, even JEDEC Read ID, Read Status Register, ..., MUST use the SPI 4-4-4 protocol. Commands sent using SPI 1-x-y protocols are no longer decoded properly. This also applies to Macronix and Winbond memories if they enter their QPI mode, which is the equivalent of Micron Quad I/O mode. This is why I've suggested to add 4 new fields in the struct spi_nor:
- .reg_proto: the SPI protocol to be used by .read_reg() and .write_reg() hooks.
- .read_proto: the SPI protocol to be used by the .read() hooks, maybe
by the .read_mmap() also.
- .write_proto: the SPI protocol to be used by the .write() hooks
- .erase_proto: the SPI protocol to be used by the .erase() hooks.
(Q)SPI controller drivers cannot guess the protocol to be used from the command op code. Indeed, taking the Micron case as un example, the very same 0xeb op code may be used with the SPI 1-4-4 protocol (Micron Extended SPI mode) or with the SPI 4-4-4 protocol (Micron Quad I/O mode).
Also just some words about the naming of SPI x-y-z protocols:
- x refers to the number of I/O lines used to send the op code byte
- y is the number of I/O lines used to send the address, mode/dummy
cycles (if these cycles exist for the command op code)
- z is the number of I/O lines used to send/receive data (if needed)
So the SNOR_OP_READ_1_1_2_IO macro for the Fast Read Dual I/O command (as opposed to the macro SNOR_OP_READ_1_1_2 macro for the Fast Read Dual Output command) doesn't make sense: it should be named SNOR_OP_READ_1_2_2.
Then about the value used for the dummy cycles, it's not always true that we don't care about initializing them. Depending on the configuration of the memory, some special dummy cycles, sometime called mode cycles, are used to during Fast Read operations to make the memory enter/leaver its Continuous Read mode. Once is Continuous Read mode, the op code byte is no longer sent, it is implicit and the command actually starts from the address cycles. This mode is mostly used for XIP applications hence is not relevant for mtd usage. However we should take care not to enter this Continuous Mode by mistake. Depending on the memory manufacturer, the Continuous Mode can be disabled by updating the relevant bit in some configuration register (e.g. setting the XIP bit in Micron Volatile Configuration Register) or by choosing the right op code (e.g. for Winbond memories in QPI mode, both the 0x0b and 0xeb op codes can be used for Fast Read 4-4-4 operation but only the 0xeb op code cares about the dummy cycle value to enter/leave the Continuous Read mode). Some Spansion memories use 6 dummy cycles for Fast Read 1-4-4 command as factory default, not 8.
Besides when sending a regular JEDEC Read ID (0x9f) command to probe the (Q)SPI memory, the current spi-nor framework assumes the Quad I/O or QPI mode is not already enabled. This not always true, some early bootloarders, such as the sama5d2 ROM Code, enables the Micron Quad I/O mode when configured to boot from the QSPI memory. If so, the QSPI memory no longer replies to the 0x9f command in SPI 1-1-1 protocol but instead to the 0xaf command in SPI 4-4-4 protocol.
Finally, about the proper way to describe the SPI controller capabilities, the SPI_TX_{DUAL, QUAD} and SPI_RX_{DUAL, QUAD} mode flags are set in the SPI framework based on the "spi-rx-bus-width" and "spi-tx-bus-width" DT properties already used in Linux. This is not enough to make the difference between the SPI 1-4-4 and SPI 4-4-4 protocols. Maybe some SPI controllers support the first protocol but not the latest. It could be good to add another argument to spi_nor_scan() providing an exhaustive list of SPI protocols supported by the SPI controller. Then to match this list with the list of SPI protocols supported by the SPI memory and select the proper protocol, this new argument should use the same range of values as the .flash_read field in the struct spi_nor_info used to describe the SPI memories.
For backward compatibility, the m25p80 driver could then convert the SPI modes into spi-nor read modes. Please have a look at patch 11 of my series; the chunk related to spi_flash_probe_slave() in sf_probe.c:
/* Convert SPI mode_rx and mode to SPI flash read commands */
- mode_rx = spi->mode_rx;
- if (mode_rx & SPI_RX_QUAD) {
e_rd_cmd = RD_NORM | QUAD_OUTPUT_FAST;
if (spi->mode & SPI_TX_QUAD)
e_rd_cmd |= QUAD_IO_FAST;
- } else if (mode_rx & SPI_RX_DUAL) {
e_rd_cmd = RD_NORM | DUAL_OUTPUT_FAST;
if (spi->mode & SPI_TX_DUAL)
e_rd_cmd |= DUAL_IO_FAST;
- } else if ((mode_rx & (SPI_RX_SLOW | SPI_RX_FAST)) == SPI_RX_SLOW) {
e_rd_cmd = ARRAY_SLOW;
- } else {
e_rd_cmd = RD_NORM;
- }
[...]
- ret = spi_flash_scan(flash);
- ret = spi_flash_scan(flash, e_rd_cmd);
I've spent a lot of time working on the QSPI memory topic so I can tell you that there are many other traps to avoid! If I can help you on this topic during your rework of the SPI NOR support, please let me know.
Thanks Cyrille, for this very detailed and good explanation. This is really appreciated. And I really hope that Jagan will get back to your generous offer, to assist with any such QSPI NOR related problems. I'll definitely remember this and might bug you at some time... ;)
I understand your points, thanks for that and anyway this spi-nor work is a starting point for both syncing with Linux as well with new feature or for better tunning. And more over I started this work in 2014 end and it's been reviewing over and over and we finally landed up with MTD driver model.
So, please wait for sometime until this gets merged we definitely work together for better tunning, thanks!
Jagan, what is the current plan here? I have to admit that I didn't check closely on your SPI-NOR work lately, sorry. So I can't really comment with a profound deep knowledge. But from what I read in Cyrille's patchset and explanation above, his patchset fixes some issues and brings us also more in line with the (upcoming) Linux framework. So I would really love to see this integrated into mainline quickly. Allowing a broader test basis on multiple platforms. Will it be a big task for Cyrille to rebase his patchset on top of your patches? Can you perhaps assist Cyrille with this? Or would it perhaps make sense to postpone your patchset and integrate Cyrille's first instead?
Cyrille, if Jagans rework goes in first, do you already have a plan on when to rebase your patchset on top of this? Or do you see some "show-stoppers" here?
Well if I need to rebase my patchset, for sure I will wait for Jagan's rework to be completed and accepted first. I don't think it would be efficient to rebase the QSPI series on the fly before the rework is totally stabilized.
One of the commit message from u-boot-spi.git can sum up the global rework: "sf: Drop entire spi-flash framework" [1]
Indeed all files from drivers/mtd/spi are deleted, the next commits replace them by new files in the spi-nor folder. These new files are almost some "copy/paste" of drivers/mtd/spi-nor files from Linux.
Yeah.. the idea of using Linux spi-nor is like u-boot also facing similar issue where Linux facing like spi drivers became more flash centric, and it's not totally same design as Linux. We took the idea from Linux and have MTD driver model on top of it. The whole idea to add this is to follow the u-boot driver model principle and if the stack look some how similar to Linux then the development is as faster as, when compared to maintain separate thread of development - this is very legacy story I have been saying on this on ML for year back.
And did you find any issues with this spi-nor series?
OK, why not but it also implies that I can't simply rebase my patchset. Since the all files are different it means I have to write a new series of patches from scratch. As I said, I won't start such a work before the new framework is totally accepted and integrated in next releases of u-boot.
I don't say using the Linux code as a reference is a bad idea, indeed there are good things in the spi-nor framework to be taken. For instance, in my own series I added .read_reg() and .write_reg() hooks the same way as done in the Linux source. I just warn developers that currently the Linux code misses some stuff to support QSPI memories efficiently. As an example, spi_nor_scan() fails to read the JEDEC ID if the QSPI memory is already in QPI mode (Micron, Macronix, Winbond, ...). This issue didn't exist with regular SPI NOR flash memories as they are almost "stateless" as compared to QSPI memories. SPI memories always use a single protocol, SPI 1-1-1 with MISO and MOSI I/O lines, for all commands. On the other hand, QSPI memories are much complex: you have to deal with many different protocols and find a way to guess the current internal state of the QSPI memory before sending commands to it.
OK, I understand that you sent separate patches, are these patches fix this issue?
https://patchwork.ozlabs.org/patch/598527/ https://patchwork.ozlabs.org/patch/598528/ https://patchwork.ozlabs.org/patch/598530/ https://patchwork.ozlabs.org/patch/598532/
If so, then I will start reviewing these and will push these changes first and will rebase spi-nor on top of this.
thanks!

Hi Jagan,
Le 21/03/2016 10:07, Jagan Teki a écrit :
Hi Cyrille,
On 18 March 2016 at 20:48, Cyrille Pitchen cyrille.pitchen@atmel.com wrote:
Hi Stefan,
Le 18/03/2016 14:48, Stefan Roese a écrit :
Hi All,
please excuse the late reply to this thread. But I'm very interested in QSPI, as one of my customers uses Micron QSPI NOR and really wants to take full advantage of the device (quad access) to speed up the overall boot process. This is on SoCFPGA btw.
Please allow me a few comments below.
On 16.03.2016 15:14, Jagan Teki wrote:
On Wednesday 16 March 2016 07:00 PM, Cyrille Pitchen wrote:
Le 15/03/2016 19:21, Jagan Teki a écrit :
On Tuesday 15 March 2016 11:42 PM, Cyrille Pitchen wrote: > Hi all, > > This series of patches fixes and extend the support of QSPI memories > in the SPI flash framework. The updates are split into many parts to > make it easier to understand and review but they should be considered > as a whole. > > This was tested on a Atmel sama5d2 xplained board with a Micron > n25q128a > memory. > > Best regards, > > Cyrille > > Cyrille Pitchen (18): > Revert "sf: Fix quad bit set for micron devices" > sf: call spi_claim_bus() and spi_release_bus() only once per read, > write or erase > sf: replace spi_flash_read_common() calls by spi_flash_cmd_read() > sf: remove spi_flash_write_common() > sf: export spi_flash_wait_ready() function > sf: share erase generic algorithm > sf: share write generic algorithm > sf: share read generic algorithm > sf: add hooks to handle register read and write operations > sf: move support of SST flash into generic spi_flash_write_alg() > sf: fix selection of supported READ commands for QSPI memories > sf: fix detection of QSPI memories when they boot in Quad or > Dual mode > sf: add helper function to set the number of dummy bytes > sf: add 4byte address opcodes > sf: prepare next fixes to support of QSPI memories by manufacturer > sf: fix support of Micron memories > ARM: at91: clock: add function to get QSPI clocks > sf: add driver for Atmel QSPI controller
Appreciate for the work, we're working on spi-nor framework[1] planning to push in couple of weeks. Will let you know once it merged so that you can add your changes on top of that.
[1] http://git.denx.de/?p=u-boot/u-boot-spi.git;a=shortlog;h=refs/heads/spi-nor-...
Hi Jagan,
I've started to have a look on your branch. I hope it's not to late for few comments:
Globally I see the new code attend to match the spi-nor framework from Linux. OK that's fine but please note the current spi-nor framework in Linux has incomplete and sometime not working support of QSPI memories.
First, after a discussion with Brian and Bean on linux-mtd [1], Bean's commit to add support to Micron QSPI memories was reverted since it didn't work alone. In his reply, Brian agreed the code was not tested and should not have been merged.
This highlights a more general issue: currently, there is no mean for the spi-nor framework to notify the SPI controller driver about a SPI protocol change at the QSPI memory side. This applies to Micron memories when they enter their Quad I/O mode. If so, ALL commands, even JEDEC Read ID, Read Status Register, ..., MUST use the SPI 4-4-4 protocol. Commands sent using SPI 1-x-y protocols are no longer decoded properly. This also applies to Macronix and Winbond memories if they enter their QPI mode, which is the equivalent of Micron Quad I/O mode. This is why I've suggested to add 4 new fields in the struct spi_nor:
- .reg_proto: the SPI protocol to be used by .read_reg() and .write_reg() hooks.
- .read_proto: the SPI protocol to be used by the .read() hooks, maybe
by the .read_mmap() also.
- .write_proto: the SPI protocol to be used by the .write() hooks
- .erase_proto: the SPI protocol to be used by the .erase() hooks.
(Q)SPI controller drivers cannot guess the protocol to be used from the command op code. Indeed, taking the Micron case as un example, the very same 0xeb op code may be used with the SPI 1-4-4 protocol (Micron Extended SPI mode) or with the SPI 4-4-4 protocol (Micron Quad I/O mode).
Also just some words about the naming of SPI x-y-z protocols:
- x refers to the number of I/O lines used to send the op code byte
- y is the number of I/O lines used to send the address, mode/dummy
cycles (if these cycles exist for the command op code)
- z is the number of I/O lines used to send/receive data (if needed)
So the SNOR_OP_READ_1_1_2_IO macro for the Fast Read Dual I/O command (as opposed to the macro SNOR_OP_READ_1_1_2 macro for the Fast Read Dual Output command) doesn't make sense: it should be named SNOR_OP_READ_1_2_2.
Then about the value used for the dummy cycles, it's not always true that we don't care about initializing them. Depending on the configuration of the memory, some special dummy cycles, sometime called mode cycles, are used to during Fast Read operations to make the memory enter/leaver its Continuous Read mode. Once is Continuous Read mode, the op code byte is no longer sent, it is implicit and the command actually starts from the address cycles. This mode is mostly used for XIP applications hence is not relevant for mtd usage. However we should take care not to enter this Continuous Mode by mistake. Depending on the memory manufacturer, the Continuous Mode can be disabled by updating the relevant bit in some configuration register (e.g. setting the XIP bit in Micron Volatile Configuration Register) or by choosing the right op code (e.g. for Winbond memories in QPI mode, both the 0x0b and 0xeb op codes can be used for Fast Read 4-4-4 operation but only the 0xeb op code cares about the dummy cycle value to enter/leave the Continuous Read mode). Some Spansion memories use 6 dummy cycles for Fast Read 1-4-4 command as factory default, not 8.
Besides when sending a regular JEDEC Read ID (0x9f) command to probe the (Q)SPI memory, the current spi-nor framework assumes the Quad I/O or QPI mode is not already enabled. This not always true, some early bootloarders, such as the sama5d2 ROM Code, enables the Micron Quad I/O mode when configured to boot from the QSPI memory. If so, the QSPI memory no longer replies to the 0x9f command in SPI 1-1-1 protocol but instead to the 0xaf command in SPI 4-4-4 protocol.
Finally, about the proper way to describe the SPI controller capabilities, the SPI_TX_{DUAL, QUAD} and SPI_RX_{DUAL, QUAD} mode flags are set in the SPI framework based on the "spi-rx-bus-width" and "spi-tx-bus-width" DT properties already used in Linux. This is not enough to make the difference between the SPI 1-4-4 and SPI 4-4-4 protocols. Maybe some SPI controllers support the first protocol but not the latest. It could be good to add another argument to spi_nor_scan() providing an exhaustive list of SPI protocols supported by the SPI controller. Then to match this list with the list of SPI protocols supported by the SPI memory and select the proper protocol, this new argument should use the same range of values as the .flash_read field in the struct spi_nor_info used to describe the SPI memories.
For backward compatibility, the m25p80 driver could then convert the SPI modes into spi-nor read modes. Please have a look at patch 11 of my series; the chunk related to spi_flash_probe_slave() in sf_probe.c:
/* Convert SPI mode_rx and mode to SPI flash read commands */
- mode_rx = spi->mode_rx;
- if (mode_rx & SPI_RX_QUAD) {
e_rd_cmd = RD_NORM | QUAD_OUTPUT_FAST;
if (spi->mode & SPI_TX_QUAD)
e_rd_cmd |= QUAD_IO_FAST;
- } else if (mode_rx & SPI_RX_DUAL) {
e_rd_cmd = RD_NORM | DUAL_OUTPUT_FAST;
if (spi->mode & SPI_TX_DUAL)
e_rd_cmd |= DUAL_IO_FAST;
- } else if ((mode_rx & (SPI_RX_SLOW | SPI_RX_FAST)) == SPI_RX_SLOW) {
e_rd_cmd = ARRAY_SLOW;
- } else {
e_rd_cmd = RD_NORM;
- }
[...]
- ret = spi_flash_scan(flash);
- ret = spi_flash_scan(flash, e_rd_cmd);
I've spent a lot of time working on the QSPI memory topic so I can tell you that there are many other traps to avoid! If I can help you on this topic during your rework of the SPI NOR support, please let me know.
Thanks Cyrille, for this very detailed and good explanation. This is really appreciated. And I really hope that Jagan will get back to your generous offer, to assist with any such QSPI NOR related problems. I'll definitely remember this and might bug you at some time... ;)
I understand your points, thanks for that and anyway this spi-nor work is a starting point for both syncing with Linux as well with new feature or for better tunning. And more over I started this work in 2014 end and it's been reviewing over and over and we finally landed up with MTD driver model.
So, please wait for sometime until this gets merged we definitely work together for better tunning, thanks!
Jagan, what is the current plan here? I have to admit that I didn't check closely on your SPI-NOR work lately, sorry. So I can't really comment with a profound deep knowledge. But from what I read in Cyrille's patchset and explanation above, his patchset fixes some issues and brings us also more in line with the (upcoming) Linux framework. So I would really love to see this integrated into mainline quickly. Allowing a broader test basis on multiple platforms. Will it be a big task for Cyrille to rebase his patchset on top of your patches? Can you perhaps assist Cyrille with this? Or would it perhaps make sense to postpone your patchset and integrate Cyrille's first instead?
Cyrille, if Jagans rework goes in first, do you already have a plan on when to rebase your patchset on top of this? Or do you see some "show-stoppers" here?
Well if I need to rebase my patchset, for sure I will wait for Jagan's rework to be completed and accepted first. I don't think it would be efficient to rebase the QSPI series on the fly before the rework is totally stabilized.
One of the commit message from u-boot-spi.git can sum up the global rework: "sf: Drop entire spi-flash framework" [1]
Indeed all files from drivers/mtd/spi are deleted, the next commits replace them by new files in the spi-nor folder. These new files are almost some "copy/paste" of drivers/mtd/spi-nor files from Linux.
Yeah.. the idea of using Linux spi-nor is like u-boot also facing similar issue where Linux facing like spi drivers became more flash centric, and it's not totally same design as Linux. We took the idea from Linux and have MTD driver model on top of it. The whole idea to add this is to follow the u-boot driver model principle and if the stack look some how similar to Linux then the development is as faster as, when compared to maintain separate thread of development - this is very legacy story I have been saying on this on ML for year back.
And did you find any issues with this spi-nor series?
Well, the spi-nor series is based on Linux code, which already lacks support of QSPI memories.
Moreover, the Linux version of m25p80.c uses the tx_nbits and rx_bits fields of the struct spi_transfer to tell the SPI controller driver about the number of I/O lines to be used to send or receive the command bytes. However there is no equivalent mechanism with the u-boot version which relies on calls of spi_write_then_read(). The u-boot version has no mean to support SPI protocols other than SPI 1-1-1 and the SPI controller drivers should not try to guess the SPI protocol from the op code.
Also be aware that the design chosen by the Linux m25p80 driver, that is to say the use of spi_sync_transfer() or spi_sync(), is not suited to all hardware QSPI controllers. For instance the Atmel QSPI controller expects separated inputs for the command op code, the address, the dummy cycles and the data.
spi_write_then_read() calls spi_xfer() twice: the first time to send the op code, address and dummy cycles then the second time to send the data. How can a SPI controller driver make the difference between the two calls if it should decode the input buffer to extract the op code, the address then the dummy cycles on the first call but send raw data for the second call?
In addition, maybe it could be better to call spi_claim_bus() / spi_release_bus() once for all at the beginning / end of spi_nor_read(), spi_nor_erase() and spi_nor_write() instead of calling them for each register read/write operation. I would avoid so many calls.
About the support the 16MiB memories, I see that commit 795c078c4563 ("mtd: spi-nor: Add 4-byte addressing") copies the Linux version except for the special case of Spansion memories which uses the dedicated 4byte address op codes for Fast Read, Page Program and Sector Erase. Please note that entering the 4byte address mode, like set_4byte() does, changes the internal state of the SPI memory which has side effects: if a CPU reset occurs and if early bootloaders don't support this address mode, sending only 3 address bytes inside Fast Read commands, they fail reading from the SPI memory. That's why I've sent a patch to implement an alternative (thus optional) method to support >16MiB memories by converting the 3byte address op codes to their associated 4byte version: 0x02 -> 0x12 Page Program 0x03 -> 0x13 Read 0x0b -> 0x0c Fast Read 0x6b -> 0x6c Fast Read Quad Output 1-1-4 0xd8 -> 0xdc Sector Erase ...
OK, why not but it also implies that I can't simply rebase my patchset. Since the all files are different it means I have to write a new series of patches from scratch. As I said, I won't start such a work before the new framework is totally accepted and integrated in next releases of u-boot.
I don't say using the Linux code as a reference is a bad idea, indeed there are good things in the spi-nor framework to be taken. For instance, in my own series I added .read_reg() and .write_reg() hooks the same way as done in the Linux source. I just warn developers that currently the Linux code misses some stuff to support QSPI memories efficiently. As an example, spi_nor_scan() fails to read the JEDEC ID if the QSPI memory is already in QPI mode (Micron, Macronix, Winbond, ...). This issue didn't exist with regular SPI NOR flash memories as they are almost "stateless" as compared to QSPI memories. SPI memories always use a single protocol, SPI 1-1-1 with MISO and MOSI I/O lines, for all commands. On the other hand, QSPI memories are much complex: you have to deal with many different protocols and find a way to guess the current internal state of the QSPI memory before sending commands to it.
OK, I understand that you sent separate patches, are these patches fix this issue?
https://patchwork.ozlabs.org/patch/598527/ https://patchwork.ozlabs.org/patch/598528/ https://patchwork.ozlabs.org/patch/598530/
Those 3 patches only deal with support of the sama5d2 xplained board. They prepare the migration to the DM model and DT support since the Atmel QSPI controller driver uses the DM model. One of them also configures the pin muxing as needed to use the QSPI controller.
They are Work In Progress and not related with the mtd sub-system. I only provide them to unblock some customers who are waiting for support of QSPI memories in u-boot on sama5d2 SoCs.
I guess this last one is not related with the mtd sub-system itself. It fixes an issue I found using the "sf probe" command. However it might need some review because I don't understand why the "device_unbind(new)" call was there at first so maybe I miss something. I guess there was a good reason to add this call and I want to avoid introducing regression.
If so, then I will start reviewing these and will push these changes first and will rebase spi-nor on top of this.
thanks!
Best regards,
Cyrille
participants (6)
-
Albert ARIBAUD
-
Cyrille Pitchen
-
Jagan Teki
-
Jagan Teki
-
Marek Vasut
-
Stefan Roese