mtd: spi-nor: add a stateless method to support memory size above 128Mib
Subsystems (4)
v2 cover mtd: spi-nor: add a stateless method to support memory size above 128Mib
Hi all,
this series of patches is based on next-20161208 and can also be applied to the git://github.com/spi-nor/linux.git tree.
It has been extracted from the SFDP series and is resent as a standalone series.
This series fixes compatibility issue between Linux and many bootloaders when using SPI flash with size greater than 128Mib.
Indeed, before this series, Linux used to make the SPI flash memory enter its 4-byte address mode when its size is greater than 128Mib: The very same Fast Read 1-1-z op code is used, for instance Fast Read 1-1-4 (6Bh) but once in 4-byte address mode, the SPI flash memory now expects a 4-byte address following the op code instead of a 3-byte address. This solution is statefull: it changes the internal state of the memory.
Hence, when the CPU is reset but not the memory, many bootloaders are not aware of this internal state change at the memory side, don't know how to handle this and still send one Fast Read op code followed by a 3-byte address. So the bootloader fails to read data from the SPI flash memory.
ChangeLog:
v1 -> v2:
- collect Acked-by for patch 1.
- replace the dichotomic search by a simple for() loop for better clarity purpose in patch 2.
Cyrille Pitchen (2):
mtd: spi-nor: rename SPINOR_OP_* macros of the 4-byte address op codes
mtd: spi-nor: add a stateless method to support memory size above
128Mib
drivers/mtd/devices/serial_flash_cmds.h | 7 --- drivers/mtd/devices/st_spi_fsm.c | 28 ++++++------ drivers/mtd/spi-nor/spi-nor.c | 78 ++++++++++++++++++++++++--------- drivers/spi/spi-bcm-qspi.c | 6 +-- include/linux/mtd/spi-nor.h | 22 +++++++--- 5 files changed, 90 insertions(+), 51 deletions(-)
Signature · 6 lines
2.7.4 -- To unsubscribe from this list: send the line "unsubscribe linux-spi" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
PATCH v2 1/2 mtd: spi-nor: rename SPINOR_OP_* macros of the 4-byte address op codes
This patch renames the SPINOR_OP_* macros of the 4-byte address instruction set so the new names all share a common pattern: the 4-byte address name is built from the 3-byte address name appending the "_4B" suffix.
The patch also introduces new op codes to support other SPI protocols such as SPI 1-4-4 and SPI 1-2-2.
This is a transitional patch and will help a later patch of spi-nor.c to automate the translation from the 3-byte address op codes into their 4-byte address version.
--- drivers/mtd/devices/serial_flash_cmds.h | 7 ------- drivers/mtd/devices/st_spi_fsm.c | 28 ++++++++++++++-------------- drivers/mtd/spi-nor/spi-nor.c | 8 ++++---- drivers/spi/spi-bcm-qspi.c | 6 +++--- include/linux/mtd/spi-nor.h | 22 ++++++++++++++++------ 5 files changed, 37 insertions(+), 34 deletions(-)
drivers/mtd/devices/serial_flash_cmds.h +0 -7
@@ -18,19 +18,12 @@#define SPINOR_OP_RDVCR 0x85/* JEDEC Standard - Serial Flash Discoverable Parmeters (SFDP) Commands */ -#define SPINOR_OP_READ_1_2_2 0xbb/* DUAL I/O READ */ -#define SPINOR_OP_READ_1_4_4 0xeb/* QUAD I/O READ */ -#define SPINOR_OP_WRITE 0x02/* PAGE PROGRAM */ #define SPINOR_OP_WRITE_1_1_2 0xa2/* DUAL INPUT PROGRAM */ #define SPINOR_OP_WRITE_1_2_2 0xd2/* DUAL INPUT EXT PROGRAM */ #define SPINOR_OP_WRITE_1_1_4 0x32/* QUAD INPUT PROGRAM */ #define SPINOR_OP_WRITE_1_4_4 0x12/* QUAD INPUT EXT PROGRAM */ -/* READ commands with 32-bit addressing */ -#define SPINOR_OP_READ4_1_2_2 0xbc -#define SPINOR_OP_READ4_1_4_4 0xec -/* Configuration flags */ #define FLASH_FLAG_SINGLE 0x000000ff#define FLASH_FLAG_READ_WRITE 0x00000001
drivers/mtd/devices/st_spi_fsm.c +14 -14
@@ -507,13 +507,13 @@ static struct seq_rw_config n25q_read3_configs[] = {* - 'FAST' variants configured for 8 dummy cycles (see note above.) */ static struct seq_rw_config n25q_read4_configs[] = { - {FLASH_FLAG_READ_1_4_4 ,SPINOR_OP_READ4_1_4_4 ,0 ,4 ,4 ,0x00 ,0 ,8 }, - {FLASH_FLAG_READ_1_1_4 ,SPINOR_OP_READ4_1_1_4 ,0 ,1 ,4 ,0x00 ,0 ,8 }, - {FLASH_FLAG_READ_1_2_2 ,SPINOR_OP_READ4_1_2_2 ,0 ,2 ,2 ,0x00 ,0 ,8 }, - {FLASH_FLAG_READ_1_1_2 ,SPINOR_OP_READ4_1_1_2 ,0 ,1 ,2 ,0x00 ,0 ,8 }, - {FLASH_FLAG_READ_FAST ,SPINOR_OP_READ4_FAST ,0 ,1 ,1 ,0x00 ,0 ,8 }, - {FLASH_FLAG_READ_WRITE ,SPINOR_OP_READ4 ,0 ,1 ,1 ,0x00 ,0 ,0 }, - {0x00 ,0 ,0 ,0 ,0 ,0x00 ,0 ,0 }, + {FLASH_FLAG_READ_1_4_4 ,SPINOR_OP_READ_1_4_4_4B ,0 ,4 ,4 ,0x00 ,0 ,8 }, + {FLASH_FLAG_READ_1_1_4 ,SPINOR_OP_READ_1_1_4_4B ,0 ,1 ,4 ,0x00 ,0 ,8 }, + {FLASH_FLAG_READ_1_2_2 ,SPINOR_OP_READ_1_2_2_4B ,0 ,2 ,2 ,0x00 ,0 ,8 }, + {FLASH_FLAG_READ_1_1_2 ,SPINOR_OP_READ_1_1_2_4B ,0 ,1 ,2 ,0x00 ,0 ,8 }, + {FLASH_FLAG_READ_FAST ,SPINOR_OP_READ_FAST_4B ,0 ,1 ,1 ,0x00 ,0 ,8 }, + {FLASH_FLAG_READ_WRITE ,SPINOR_OP_READ_4B ,0 ,1 ,1 ,0x00 ,0 ,0 }, + {0x00 ,0 ,0 ,0 ,0 ,0x00 ,0 ,0 }, };/*
@@ -553,13 +553,13 @@ static int stfsm_mx25_en_32bit_addr_seq(struct stfsm_seq *seq)* entering a state that is incompatible with the SPIBoot Controller. */ static struct seq_rw_config stfsm_s25fl_read4_configs[] = { - {FLASH_FLAG_READ_1_4_4 ,SPINOR_OP_READ4_1_4_4 ,0 ,4 ,4 ,0x00 ,2 ,4 }, - {FLASH_FLAG_READ_1_1_4 ,SPINOR_OP_READ4_1_1_4 ,0 ,1 ,4 ,0x00 ,0 ,8 }, - {FLASH_FLAG_READ_1_2_2 ,SPINOR_OP_READ4_1_2_2 ,0 ,2 ,2 ,0x00 ,4 ,0 }, - {FLASH_FLAG_READ_1_1_2 ,SPINOR_OP_READ4_1_1_2 ,0 ,1 ,2 ,0x00 ,0 ,8 }, - {FLASH_FLAG_READ_FAST ,SPINOR_OP_READ4_FAST ,0 ,1 ,1 ,0x00 ,0 ,8 }, - {FLASH_FLAG_READ_WRITE ,SPINOR_OP_READ4 ,0 ,1 ,1 ,0x00 ,0 ,0 }, - {0x00 ,0 ,0 ,0 ,0 ,0x00 ,0 ,0 }, + {FLASH_FLAG_READ_1_4_4 ,SPINOR_OP_READ_1_4_4_4B ,0 ,4 ,4 ,0x00 ,2 ,4 }, + {FLASH_FLAG_READ_1_1_4 ,SPINOR_OP_READ_1_1_4_4B ,0 ,1 ,4 ,0x00 ,0 ,8 }, + {FLASH_FLAG_READ_1_2_2 ,SPINOR_OP_READ_1_2_2_4B ,0 ,2 ,2 ,0x00 ,4 ,0 }, + {FLASH_FLAG_READ_1_1_2 ,SPINOR_OP_READ_1_1_2_4B ,0 ,1 ,2 ,0x00 ,0 ,8 }, + {FLASH_FLAG_READ_FAST ,SPINOR_OP_READ_FAST_4B ,0 ,1 ,1 ,0x00 ,0 ,8 }, + {FLASH_FLAG_READ_WRITE ,SPINOR_OP_READ_4B ,0 ,1 ,1 ,0x00 ,0 ,0 }, + {0x00 ,0 ,0 ,0 ,0 ,0x00 ,0 ,0 }, };static struct seq_rw_config stfsm_s25fl_write4_configs[] = {
drivers/mtd/spi-nor/spi-nor.c +4 -4
@@ -1490,16 +1490,16 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)/* Dedicated 4-byte command set */ switch (nor->flash_read) {case SPI_NOR_QUAD : - nor->read_opcode =SPINOR_OP_READ4_1_1_4 ; + nor->read_opcode =SPINOR_OP_READ_1_1_4_4B ;break ;case SPI_NOR_DUAL : - nor->read_opcode =SPINOR_OP_READ4_1_1_2 ; + nor->read_opcode =SPINOR_OP_READ_1_1_2_4B ;break ;case SPI_NOR_FAST : - nor->read_opcode =SPINOR_OP_READ4_FAST ; + nor->read_opcode =SPINOR_OP_READ_FAST_4B ;break ;case SPI_NOR_NORMAL : - nor->read_opcode =SPINOR_OP_READ4 ; + nor->read_opcode =SPINOR_OP_READ_4B ;break ; } nor->program_opcode =SPINOR_OP_PP_4B ;
drivers/spi/spi-bcm-qspi.c +3 -3
@@ -371,7 +371,7 @@ static int bcm_qspi_bspi_set_flex_mode(struct bcm_qspi *qspi, int width,/* default mode, does not need flex_cmd */ flex_mode =0 ; else - command =SPINOR_OP_READ4_FAST ; + command =SPINOR_OP_READ_FAST_4B ;break ;case SPI_NBITS_DUAL : bpc =0x00000001 ;
@@ -384,7 +384,7 @@ static int bcm_qspi_bspi_set_flex_mode(struct bcm_qspi *qspi, int width, }else { command =SPINOR_OP_READ_1_1_2 ;if (spans_4byte) - command =SPINOR_OP_READ4_1_1_2 ; + command =SPINOR_OP_READ_1_1_2_4B ; }break ;case SPI_NBITS_QUAD :
@@ -399,7 +399,7 @@ static int bcm_qspi_bspi_set_flex_mode(struct bcm_qspi *qspi, int width, }else { command =SPINOR_OP_READ_1_1_4 ;if (spans_4byte) - command =SPINOR_OP_READ4_1_1_4 ; + command =SPINOR_OP_READ_1_1_4_4B ; }break ;default :
include/linux/mtd/spi-nor.h +16 -6
@@ -43,9 +43,13 @@#define SPINOR_OP_WRSR 0x01/* Write status register 1 byte */ #define SPINOR_OP_READ 0x03/* Read data bytes (low frequency) */ #define SPINOR_OP_READ_FAST 0x0b/* Read data bytes (high frequency) */ -#define SPINOR_OP_READ_1_1_2 0x3b/* Read data bytes (Dual SPI) */ -#define SPINOR_OP_READ_1_1_4 0x6b/* Read data bytes (Quad SPI) */ +#define SPINOR_OP_READ_1_1_2 0x3b/* Read data bytes (Dual Output SPI) */ +#define SPINOR_OP_READ_1_2_2 0xbb/* Read data bytes (Dual I/O SPI) */ +#define SPINOR_OP_READ_1_1_4 0x6b/* Read data bytes (Quad Output SPI) */ +#define SPINOR_OP_READ_1_4_4 0xeb/* Read data bytes (Quad I/O SPI) */ #define SPINOR_OP_PP 0x02/* Page program (up to 256 bytes) */ +#define SPINOR_OP_PP_1_1_4 0x32/* Quad page program */ +#define SPINOR_OP_PP_1_4_4 0x38/* Quad page program */ #define SPINOR_OP_BE_4K 0x20/* Erase 4KiB block */ #define SPINOR_OP_BE_4K_PMC 0xd7/* Erase 4KiB block on PMC chips */ #define SPINOR_OP_BE_32K 0x52/* Erase 32KiB block */
@@ -56,11 +60,17 @@#define SPINOR_OP_RDFSR 0x70/* Read flag status register */ /* 4-byte address opcodes - used on Spansion and some Macronix flashes. */ -#define SPINOR_OP_READ4 0x13/* Read data bytes (low frequency) */ -#define SPINOR_OP_READ4_FAST 0x0c/* Read data bytes (high frequency) */ -#define SPINOR_OP_READ4_1_1_2 0x3c/* Read data bytes (Dual SPI) */ -#define SPINOR_OP_READ4_1_1_4 0x6c/* Read data bytes (Quad SPI) */ +#define SPINOR_OP_READ_4B 0x13/* Read data bytes (low frequency) */ +#define SPINOR_OP_READ_FAST_4B 0x0c/* Read data bytes (high frequency) */ +#define SPINOR_OP_READ_1_1_2_4B 0x3c/* Read data bytes (Dual Output SPI) */ +#define SPINOR_OP_READ_1_2_2_4B 0xbc/* Read data bytes (Dual I/O SPI) */ +#define SPINOR_OP_READ_1_1_4_4B 0x6c/* Read data bytes (Quad Output SPI) */ +#define SPINOR_OP_READ_1_4_4_4B 0xec/* Read data bytes (Quad I/O SPI) */ #define SPINOR_OP_PP_4B 0x12/* Page program (up to 256 bytes) */ +#define SPINOR_OP_PP_1_1_4_4B 0x34/* Quad page program */ +#define SPINOR_OP_PP_1_4_4_4B 0x3e/* Quad page program */ +#define SPINOR_OP_BE_4K_4B 0x21/* Erase 4KiB block */ +#define SPINOR_OP_BE_32K_4B 0x5c/* Erase 32KiB block */ #define SPINOR_OP_SE_4B 0xdc/* Sector erase (usually 64KiB) */ /* Used for SST flashes only. */
Signature · 1 line
2.7.4
PATCH v2 2/2 mtd: spi-nor: add a stateless method to support memory size above 128Mib
In reply to Cyrille PitchenThis patch provides an alternative mean to support memory above 16MiB (128Mib) by replacing 3byte address op codes by their associated 4byte address versions.
Using the dedicated 4byte address op codes doesn't change the internal state of the SPI NOR memory as opposed to using other means such as updating a Base Address Register (BAR) and sending command to enter/leave the 4byte mode.
Hence when a CPU reset occurs, early bootloaders don't need to be aware of BAR value or 4byte mode being enabled: they can still access the first 16MiB of the SPI NOR memory using the regular 3byte address op codes.
--- drivers/mtd/spi-nor/spi-nor.c | 78 +++++++++++++++++++++++++++++++------------ 1 file changed, 57 insertions(+), 21 deletions(-)
drivers/mtd/spi-nor/spi-nor.c +57 -21
@@ -75,6 +75,10 @@ struct flash_info {* bit. Must be used with * SPI_NOR_HAS_LOCK. */ +#define SPI_NOR_4B_OPCODES BIT(10)/* +* Use dedicated 4byte address op codes +* to support memory size above 128Mib. +*/ };#define JEDEC_MFR (info) ((info)->id[0])
@@ -188,6 +192,55 @@ static inline struct spi_nor *mtd_to_spi_nor(struct mtd_info *mtd)return mtd->priv; } + +static u8 spi_nor_3to4_opcode (u8 opcode) +{ +#define ENTRY_3TO4 (_opcode) { _opcode, _opcode##_4B } +static const u8 spi_nor_3to4_table[][2 ] = { +ENTRY_3TO4 (SPINOR_OP_READ ), +ENTRY_3TO4 (SPINOR_OP_READ_FAST ), +ENTRY_3TO4 (SPINOR_OP_READ_1_1_2 ), +ENTRY_3TO4 (SPINOR_OP_READ_1_2_2 ), +ENTRY_3TO4 (SPINOR_OP_READ_1_1_4 ), +ENTRY_3TO4 (SPINOR_OP_READ_1_4_4 ), +ENTRY_3TO4 (SPINOR_OP_PP ), +ENTRY_3TO4 (SPINOR_OP_PP_1_1_4 ), +ENTRY_3TO4 (SPINOR_OP_PP_1_4_4 ), +ENTRY_3TO4 (SPINOR_OP_BE_4K ), +ENTRY_3TO4 (SPINOR_OP_BE_32K ), +ENTRY_3TO4 (SPINOR_OP_SE ), + }; +#undef ENTRY_3TO4 +size_t i; + +for (i =0 ; i <ARRAY_SIZE (spi_nor_3to4_table); ++i) +if (spi_nor_3to4_table[i][0 ] == opcode) +return spi_nor_3to4_table[i][1 ]; + +/* No conversion found, keep input op code. */ +return opcode; +} + +static void spi_nor_set_4byte_opcodes (struct spi_nor *nor, +const struct flash_info *info) +{ +/* Do some manufacturer fixups first */ +switch (JEDEC_MFR (info)) { +case SNOR_MFR_SPANSION : +/* No small sector erase for 4-byte command set */ + nor->erase_opcode =SPINOR_OP_SE ; + nor->mtd.erasesize = info->sector_size; +break ; + +default : +break ; + } + + nor->read_opcode =spi_nor_3to4_opcode (nor->read_opcode); + nor->program_opcode =spi_nor_3to4_opcode (nor->program_opcode); + nor->erase_opcode =spi_nor_3to4_opcode (nor->erase_opcode); +} +/* Enable/disable 4-byte addressing mode. */ static inline int set_4byte (struct spi_nor *nor,const struct flash_info *info,int enable)
@@ -1486,27 +1539,10 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)else if (mtd->size >0x1000000 ) {/* enable 4-byte addressing if the device exceeds 16MiB */ nor->addr_width =4 ; -if (JEDEC_MFR (info) ==SNOR_MFR_SPANSION ) { -/* Dedicated 4-byte command set */ -switch (nor->flash_read) { -case SPI_NOR_QUAD : - nor->read_opcode =SPINOR_OP_READ_1_1_4_4B ; -break ; -case SPI_NOR_DUAL : - nor->read_opcode =SPINOR_OP_READ_1_1_2_4B ; -break ; -case SPI_NOR_FAST : - nor->read_opcode =SPINOR_OP_READ_FAST_4B ; -break ; -case SPI_NOR_NORMAL : - nor->read_opcode =SPINOR_OP_READ_4B ; -break ; - } - nor->program_opcode =SPINOR_OP_PP_4B ; -/* No small sector erase for 4-byte command set */ - nor->erase_opcode =SPINOR_OP_SE_4B ; - mtd->erasesize = info->sector_size; - }else +if (JEDEC_MFR (info) ==SNOR_MFR_SPANSION || + info->flags &SPI_NOR_4B_OPCODES ) +spi_nor_set_4byte_opcodes (nor, info); +else set_4byte (nor, info,1 ); }else { nor->addr_width =3 ;
Signature · 6 lines
2.7.4 ______________________________________________________ Linux MTD discussion mailing list http://lists.infradead.org/mailman/listinfo/linux-mtd/