Add support for flag status register on Micron chips.
Subsystems (2)
PATCH v3 1/1 Add support for flag status register on Micron chips.
I rebased this patch onto the l2-mtd spinor branch. Sorry it took so long, had to patch for our SoC, corporate network issues, etc.
The change to read the flag status register is, afaik, specific to Micron chips. So, imo, the fsr ready check should be in the m25p80.c file. But I put it into the spin-nor.c file, because I need to call the read_sr() function, which is declared static in spi-nor.c.
I *could* duplicate the read_sr code and put the wait_till_fsr_ready in m25p80, but I'm pretty sure that would attract some critique :) Suggestions most welcome.
I also used n25q512ax3 for the id because there are two part numbers for the 512MB part with FSR, n25q512a13 and n25qa512a83. The '83' version has a reset line.
Graham Moore (1):
Add support for flag status register on Micron chips.
drivers/mtd/spi-nor/spi-nor.c | 51 +++++++++++++++++++++++++++++++++++++++++ include/linux/mtd/spi-nor.h | 4 ++++ 2 files changed, 55 insertions(+)
Signature · 1 line
1.7.9.5
PATCH v3 1/1 Add support for flag status register on Micron chips.
Some new Micron flash chips require reading the flag status register to determine when operations have completed.
Furthermore, chips with multi-die stacks of the 65nm 256Mb QSPI also require reading the status register before reading the flag status register.
This patch adds support for the flag status register in the n25q512ax3 and n25q00 Micron QSPI flash chips.
---
V3:
Rebase to l2-mtd spinor branch.
V2:
Remove leading underscore in function names. Remove type cast in dev_err call and use the proper format specifier instead.
--- drivers/mtd/spi-nor/spi-nor.c | 51 +++++++++++++++++++++++++++++++++++++++++ include/linux/mtd/spi-nor.h | 4 ++++ 2 files changed, 55 insertions(+)
drivers/mtd/spi-nor/spi-nor.c +51 -0
@@ -48,6 +48,25 @@ static int read_sr(struct spi_nor *nor) }/* +* Read the flag status register, returning its value in the location +* Return the status register value. +* Returns negative if error occurred. +*/ +static int read_fsr (struct spi_nor *nor) +{ +int ret; +u8 val; + + ret = nor->read_reg (nor,SPINOR_OP_RDFSR , &val,1 ); +if (ret <0 ) { +pr_err ("error %d reading FSR\n" , ret); +return ret; + } + +return val; +} + +/* * Read configuration register, returning its value in the * location. Return the configuration register value. * Returns negative if error occured.
@@ -165,6 +184,32 @@ static int spi_nor_wait_till_ready(struct spi_nor *nor)return -ETIMEDOUT ; } +static int spi_nor_wait_till_fsr_ready (struct spi_nor *nor) +{ +unsigned long deadline; +int sr; +int fsr; + + deadline = jiffies +MAX_READY_WAIT_JIFFIES ; + +do { +cond_resched (); + + sr =read_sr (nor); +if (sr <0 ) +break ; +else if (!(sr &SR_WIP )) { + fsr =read_fsr (nor); +if (fsr <0 ) +break ; +if (fsr &FSR_READY ) +return 0 ; + } + }while (!time_after_eq (jiffies, deadline)); + +return -ETIMEDOUT ; +} +/* * Service routine to read status register until ready, or timeout occurs. * Returns non-zero if error.
@@ -402,6 +447,7 @@ struct flash_info {#define SECT_4K_PMC 0x10/* SPINOR_OP_BE_4K_PMC works uniformly */ #define SPI_NOR_DUAL_READ 0x20/* Flash supports Dual Read */ #define SPI_NOR_QUAD_READ 0x40/* Flash supports Quad Read */ +#define USE_FSR 0x80/* use flag status register */ };#define INFO (_jedec_id, _ext_id, _sector_size, _n_sectors, _flags) \
@@ -488,6 +534,8 @@ const struct spi_device_id spi_nor_ids[] = { { "n25q128a13", INFO(0x20ba18, 0, 64 * 1024, 256, 0) }, { "n25q256a", INFO(0x20ba19, 0, 64 * 1024, 512, SECT_4K) }, { "n25q512a", INFO(0x20bb20, 0, 64 * 1024, 1024, SECT_4K) }, + { "n25q512ax3", INFO(0x20ba20, 0, 64 * 1024, 1024, USE_FSR) }, + { "n25q00", INFO(0x20ba21, 0, 64 * 1024, 2048, USE_FSR) },/* PMC */ { "pm25lv512", INFO(0, 0, 32 * 1024, 2, SECT_4K_PMC) },
@@ -965,6 +1013,9 @@ int spi_nor_scan(struct spi_nor *nor, const struct spi_device_id *id,else mtd->_write = spi_nor_write; +if (info->flags &USE_FSR ) + nor->wait_till_ready = spi_nor_wait_till_fsr_ready; +/* prefer "small sector" erase if possible */ if (info->flags &SECT_4K ) { nor->erase_opcode =SPINOR_OP_BE_4K ;
include/linux/mtd/spi-nor.h +4 -0
@@ -34,6 +34,7 @@#define SPINOR_OP_SE 0xd8/* Sector erase (usually 64KiB) */ #define SPINOR_OP_RDID 0x9f/* Read JEDEC ID */ #define SPINOR_OP_RDCR 0x35/* Read configuration register */ +#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) */
@@ -66,6 +67,9 @@#define SR_QUAD_EN_MX 0x40/* Macronix Quad I/O */ +/* Flag Status Register bits */ +#define FSR_READY 0x80 +/* Configuration Register bits. */ #define CR_QUAD_EN_SPAN 0x2/* Spansion Quad I/O */
Signature · 1 line
1.7.9.5
On Tuesday, April 22, 2014 at 04:03:16 PM, Graham Moore wrote:
[...]
drivers/mtd/spi-nor/spi-nor.c const struct spi_device_id spi_nor_ids[] = { · line 538
Quoted 7 lines
#define
INFO(_jedec_id, _ext_id, _sector_size, _n_sectors, _flags)\drivers/mtd/spi-nor/spi-nor.c +2
-0@@ -488,6 +534,8 @@ const struct spi_device_id spi_nor_ids[] = { { "n25q128a13", INFO(0x20ba18, 0, 64 * 1024, 256, 0) }, { "n25q256a", INFO(0x20ba19, 0, 64 * 1024, 512, SECT_4K) }, { "n25q512a", INFO(0x20bb20, 0, 64 * 1024, 1024, SECT_4K) }, + { "n25q512ax3", INFO(0x20ba20, 0, 64 * 1024, 1024, USE_FSR) }, + { "n25q00", INFO(0x20ba21, 0, 64 * 1024, 2048, USE_FSR) },
Are you sure the n25q512a doesn't use FSR ? Do n25q512a{1,8}3 share the same IDs?
[...]
Best regards,
Marek Vasut
Quoted 2 lines · Marek Vasut
Are you sure the n25q512a doesn't use FSR ? Do n25q512a{1,8}3 share the same IDs?
I looked at the datasheet and the n25q512a *does* have the same FSR usage note, so I suppose I should add USE_FSR to it as well. But how is it working now? Maybe nobody is actually using that chip. Yes, n25q512a{1,8}3 share the same id, 0x20ba20.
Quoted 7 lines · Graham Moore
Quoted 2 lines · Marek Vasut
Are you sure the n25q512a doesn't use FSR ? Do n25q512a{1,8}3 share the same IDs?
I looked at the datasheet and the n25q512a *does* have the same FSR usage note, so I suppose I should add
USE_FSRto it as well. But how is it working now?
Is it at all ?
Quoted 2 lines
Maybe nobody is actually using that chip.
Yes, n25q512a{1,8}3 share the same id,0x20ba20.
Sigh, oh Micron ...
Best regards,
Marek Vasut
Quoted 9 lines · Graham Moore
Quoted 2 lines · Marek Vasut
Are you sure the n25q512a doesn't use FSR ? Do n25q512a{1,8}3 share the same IDs?
I looked at the datasheet and the n25q512a *does* have the same FSR usage note, so I suppose I should add
USE_FSRto it as well. But how is it working now? Maybe nobody is actually using that chip. Yes, n25q512a{1,8}3 share the same id,0x20ba20.
could use the ext_id to distinguish them ? just as we did for Spansion NOR.
i dumped out 5 bytes of my n25q256a: 0x20, 0xba, 0x19, 0x10, 0x00.
thanks
Huang Shijie
Quoted 5 lines · Huang Shijie
Quoted 4 lines · Graham Moore
Quoted 2 lines · Marek Vasut
Are you sure the n25q512a doesn't use FSR ? Do n25q512a{1,8}3 share the same IDs?
Quoted 8 lines
Quoted 4 lines
I looked at the datasheet and the n25q512a *does* have the same FSR usage note, so I suppose I should add
USE_FSRto it as well. But how is it working now? Maybe nobody is actually using that chip. Yes, n25q512a{1,8}3 share the same id,0x20ba20.could use the
ext_idto distinguish them ? just as we did for Spansion NOR.i dumped out 5 bytes of my n25q256a:
0x20,0xba,0x19,0x10,0x00.
The q256 and q512 are rather different chips it seems to me ...
Best regards,
Marek Vasut
Quoted 8 lines · Graham Moore
---
V3:
Rebase to l2-mtd spinor branch.
V2:
Remove leading underscore in function names. Remove type cast indev_errcall and use the proper format specifier instead.
the patch appears to not have dev_err() references, were they removed? see below
Quoted 6 lines
[ ... ]
+ * Read the flag status register, returning its value in the location
+ * Return the status register value.
+ * Returns negative if error occurred.
+ */drivers/mtd/spi-nor/spi-nor.c +1
-0+static int read_fsr (struct spi_nor *nor)
drivers/mtd/spi-nor/spi-nor.c static int read_sr(struct spi_nor *nor) · line 64
Quoted 10 lines
drivers/mtd/spi-nor/spi-nor.c +9
-0+{ +int ret; +u8 val; + + ret = nor->read_reg (nor,SPINOR_OP_RDFSR , &val,1 ); +if (ret <0 ) { +pr_err ("error %d reading FSR\n" , ret); +return ret; + }
this emits a message that an error has occured, but doesn't tell where it occured -- can you dev_err() here to make the message even more helpful?
Quoted 19 lines
drivers/mtd/spi-nor/spi-nor.c +15
-0@@ -165,6 +184,32 @@ static int spi_nor_wait_till_ready(struct spi_nor *nor)return -ETIMEDOUT ; } +static int spi_nor_wait_till_fsr_ready (struct spi_nor *nor) +{ +unsigned long deadline; +int sr; +int fsr; + + deadline = jiffies +MAX_READY_WAIT_JIFFIES ; + + do { + cond_resched(); + + sr = read_sr(nor); + if (sr < 0) + break; + else if (!(sr & SR_WIP)) {
drivers/mtd/spi-nor/spi-nor.c static int spi_nor_wait_till_ready(struct spi_nor *nor) · line 211
Quoted 10 lines
drivers/mtd/spi-nor/spi-nor.c +10
-0+ fsr =read_fsr (nor); +if (fsr <0 ) +break ; +if (fsr &FSR_READY ) +return 0 ; + } + }while (!time_after_eq (jiffies, deadline)); + +return -ETIMEDOUT ; +}
this logic always returns "timed out" when the ready flag is not seen, even in the case of read errors -- can you "preset" the error code with "timed out", and update it with something more appropriate before returning when other errors are seen?
though this is an internal helper, and callers may not tell the situations apart in the first place, so this might be a minor nit
virtually yours
Gerhard Sittig
Signature · 3 lines
DENX Software Engineering GmbH, MD: Wolfgang Denk & Detlev Zundel HRB 165235 Munich, Office: Kirchenstr. 5, D-82194 Groebenzell, Germany Phone: +49-8142-66989-0 Fax: +49-8142-66989-80 Email: [email protected]
Quoted 2 lines · Gerhard Sittig
the patch appears to not have
dev_err()references, were they removed? see below
[...]
Quoted 3 lines
this emits a message that an error has occured, but doesn't tell where it occured -- can you
dev_err()here to make the message even more helpful?
Yeah, the previous dev_err was actually copy-pasted from a similar function that already existed. When I rebased to l2-mtd/spinor, I did the same copy-paste but that branch has pr_err instead of dev_err. I'm a noob, so I didn't want to change things without a good explanation :) On the other hand, I'm in favor of using dev_err instead.
[...]
Quoted 7 lines
this logic always returns "timed out" when the ready flag is not seen, even in the case of read errors -- can you "preset" the error code with "timed out", and update it with something more appropriate before returning when other errors are seen?
though this is an internal helper, and callers may not tell the situations apart in the first place, so this might be a minor nit
Same here as above, copy-pasted, and so the logic is what already existed. I double-checked the calling code, and like you say, the callers do not tell the situations apart. About half the calls assume any error is timeout. The others either pass return value or some other error up, or they ignore it.
Thanks,
Graham
Quoted 103 lines · Graham Moore
Some new Micron flash chips require reading the flag status register to determine when operations have completed.
Furthermore, chips with multi-die stacks of the 65nm 256Mb QSPI also require reading the status register before reading the flag status register.
This patch adds support for the flag status register in the n25q512ax3 and n25q00 Micron QSPI flash chips.
Signed-off-by Graham Moore <[email protected]>---
V3:
Rebase to l2-mtd spinor branch.
V2:
Remove leading underscore in function names. Remove type cast indev_errcall and use the proper format specifier instead.--- drivers/mtd/spi-nor/spi-nor.c | 51 +++++++++++++++++++++++++++++++++++++++++ include/linux/mtd/spi-nor.h | 4 ++++ 2 files changed, 55 insertions(+)drivers/mtd/spi-nor/spi-nor.c +48
-0@@ -48,6 +48,25 @@ static int read_sr(struct spi_nor *nor) }/* +* Read the flag status register, returning its value in the location +* Return the status register value. +* Returns negative if error occurred. +*/ +static int read_fsr (struct spi_nor *nor) +{ +int ret; +u8 val; + + ret = nor->read_reg (nor,SPINOR_OP_RDFSR , &val,1 ); +if (ret <0 ) { +pr_err ("error %d reading FSR\n" , ret); +return ret; + } + +return val; +} + +/* * Read configuration register, returning its value in the * location. Return the configuration register value. * Returns negative if error occured. @@ -165,6 +184,32 @@ static int spi_nor_wait_till_ready(struct spi_nor *nor)return -ETIMEDOUT ; } +static int spi_nor_wait_till_fsr_ready (struct spi_nor *nor) +{ +unsigned long deadline; +int sr; +int fsr; + + deadline = jiffies +MAX_READY_WAIT_JIFFIES ; + +do { +cond_resched (); + + sr =read_sr (nor); +if (sr <0 ) +break ; +else if (!(sr &SR_WIP )) { + fsr =read_fsr (nor); +if (fsr <0 ) +break ; +if (fsr &FSR_READY ) +return 0 ; + } + }while (!time_after_eq (jiffies, deadline)); + +return -ETIMEDOUT ; +} +/* * Service routine to read status register until ready, or timeout occurs. * Returns non-zero if error. @@ -402,6 +447,7 @@ struct flash_info {#define SECT_4K_PMC 0x10/* SPINOR_OP_BE_4K_PMC works uniformly */ #define SPI_NOR_DUAL_READ 0x20/* Flash supports Dual Read */ #define SPI_NOR_QUAD_READ 0x40/* Flash supports Quad Read */ +#define USE_FSR 0x80/* use flag status register */ };#define INFO (_jedec_id, _ext_id, _sector_size, _n_sectors, _flags) \@@ -488,6 +534,8 @@ const struct spi_device_id spi_nor_ids[] = { { "n25q128a13", INFO(0x20ba18, 0, 64 * 1024, 256, 0) }, { "n25q256a", INFO(0x20ba19, 0, 64 * 1024, 512, SECT_4K) }, { "n25q512a", INFO(0x20bb20, 0, 64 * 1024, 1024, SECT_4K) }, + { "n25q512ax3", INFO(0x20ba20, 0, 64 * 1024, 1024, USE_FSR) }, + { "n25q00", INFO(0x20ba21, 0, 64 * 1024, 2048, USE_FSR) },/* PMC */ { "pm25lv512", INFO(0, 0, 32 * 1024, 2, SECT_4K_PMC) },
drivers/mtd/spi-nor/spi-nor.c int spi_nor_scan(struct spi_nor *nor, const struct spi_device_id *id, · line 1018
Quoted 6 lines
drivers/mtd/spi-nor/spi-nor.c +3
-0@@ -965,6 +1013,9 @@ int spi_nor_scan(struct spi_nor *nor, const struct spi_device_id *id,else mtd->_write = spi_nor_write; +if (info->flags &USE_FSR ) + nor->wait_till_ready = spi_nor_wait_till_fsr_ready; +
the drivers may fills this hook itself, so the code should like this:
--------------------------------------------------
if ((info->flags & USE_FSR) && nor->wait_till_ready == spi_nor_wait_till_fsr_ready) nor->wait_till_ready = spi_nor_wait_till_fsr_ready;
--------------------------------------------------
thanks
Huang Shijie
Quoted 94 lines · Huang Shijie
Quoted 93 lines · Graham Moore
Some new Micron flash chips require reading the flag status register to determine when operations have completed.
Furthermore, chips with multi-die stacks of the 65nm 256Mb QSPI also require reading the status register before reading the flag status register.
This patch adds support for the flag status register in the n25q512ax3 and n25q00 Micron QSPI flash chips.
Signed-off-by Graham Moore <[email protected]>---
V3:
Rebase to l2-mtd spinor branch.
V2:
Remove leading underscore in function names. Remove type cast indev_errcall and use the proper format specifier instead.
---drivers/mtd/spi-nor/spi-nor.c | 51 +++++++++++++++++++++++++++++++++++++++++ include/linux/mtd/spi-nor.h | 4 ++++ 2 files changed, 55 insertions(+)(quoted diff) +0
-0
b/drivers/mtd/spi-nor/spi-nor.cindex d6f44d5..24b84d8 100644drivers/mtd/spi-nor/spi-nor.c +19
-0@@ -48,6 +48,25 @@ static int read_sr(struct spi_nor *nor) }/* +* Read the flag status register, returning its value in the location +* Return the status register value. +* Returns negative if error occurred. +*/ +static int read_fsr (struct spi_nor *nor) +{ +int ret; +u8 val; + + ret = nor->read_reg (nor,SPINOR_OP_RDFSR , &val,1 ); +if (ret <0 ) { +pr_err ("error %d reading FSR\n" , ret); +return ret; + } + +return val; +} + +/* * Read configuration register, returning its value in the
* location. Return the configuration register value.
* Returns negative if error occured.drivers/mtd/spi-nor/spi-nor.c +26
-0@@ -165,6 +184,32 @@ static int spi_nor_wait_till_ready(struct spi_nor *nor)return -ETIMEDOUT ; } +static int spi_nor_wait_till_fsr_ready (struct spi_nor *nor) +{ +unsigned long deadline; +int sr; +int fsr; + + deadline = jiffies +MAX_READY_WAIT_JIFFIES ; + +do { +cond_resched (); + + sr =read_sr (nor); +if (sr <0 ) +break ; +else if (!(sr &SR_WIP )) { + fsr =read_fsr (nor); +if (fsr <0 ) +break ; +if (fsr &FSR_READY ) +return 0 ; + } + }while (!time_after_eq (jiffies, deadline)); + +return -ETIMEDOUT ; +} +
Quoted 8 lines
drivers/mtd/spi-nor/spi-nor.c struct flash_info { · line 448
Quoted 8 lines
/*
* Service routine to read status register until ready, or timeout occurs. * Returns non-zero if error.
drivers/mtd/spi-nor/spi-nor.c +0
-0@@ -402,6 +447,7 @@ struct flash_info {#define SECT_4K_PMC 0x10/* SPINOR_OP_BE_4K_PMC works
uniformly */
Quoted 33 lines
Quoted 22 lines
drivers/mtd/spi-nor/spi-nor.c +1
-0#define SPI_NOR_DUAL_READ 0x20/* Flash supports Dual Read */ #define SPI_NOR_QUAD_READ 0x40/* Flash supports Quad Read */ +#define USE_FSR 0x80/* use flag status register */ };
#define
INFO(_jedec_id, _ext_id, _sector_size, _n_sectors, _flags)\drivers/mtd/spi-nor/spi-nor.c +2
-0@@ -488,6 +534,8 @@ const struct spi_device_id spi_nor_ids[] = { { "n25q128a13", INFO(0x20ba18, 0, 64 * 1024, 256, 0) }, { "n25q256a", INFO(0x20ba19, 0, 64 * 1024, 512, SECT_4K) }, { "n25q512a", INFO(0x20bb20, 0, 64 * 1024, 1024, SECT_4K) }, + { "n25q512ax3", INFO(0x20ba20, 0, 64 * 1024, 1024, USE_FSR) }, + { "n25q00", INFO(0x20ba21, 0, 64 * 1024, 2048, USE_FSR) },drivers/mtd/spi-nor/spi-nor.c +0
-0/* PMC */ { "pm25lv512", INFO(0, 0, 32 * 1024, 2, SECT_4K_PMC) },drivers/mtd/spi-nor/spi-nor.c int spi_nor_scan(struct spi_nor *nor, const struct · line 1021
Quoted 9 lines
drivers/mtd/spi-nor/spi-nor.c +3
-0@@ -965,6 +1013,9 @@ int spi_nor_scan(struct spi_nor *nor, const structspi_device_id *id, else mtd->_write = spi_nor_write; +if (info->flags &USE_FSR ) + nor->wait_till_ready = spi_nor_wait_till_fsr_ready; +the drivers may fills this hook itself, so the code should like this:
Quoted 5 lines
-------------------------------------------------- if ((info->flags & USE_FSR) && nor->wait_till_ready == spi_nor_wait_till_fsr_ready) nor->wait_till_ready = spi_nor_wait_till_fsr_ready; --------------------------------------------------
I sense a misdesign of the SPI NOR subsystem here. The subsystem and the driver compete for a function pointer here ? I guess one should have precedence in some way then ... and also, they should be two different pointers, where the subsystem decides which to use.
Quoted 143 lines · Marek Vasut
Quoted 94 lines · Huang Shijie
Quoted 93 lines · Graham Moore
Some new Micron flash chips require reading the flag status register to determine when operations have completed.
Furthermore, chips with multi-die stacks of the 65nm 256Mb QSPI also require reading the status register before reading the flag status register.
This patch adds support for the flag status register in the n25q512ax3 and n25q00 Micron QSPI flash chips.
Signed-off-by Graham Moore <[email protected]>---
V3:
Rebase to l2-mtd spinor branch.
V2:
Remove leading underscore in function names. Remove type cast indev_errcall and use the proper format specifier instead.
---drivers/mtd/spi-nor/spi-nor.c | 51 +++++++++++++++++++++++++++++++++++++++++ include/linux/mtd/spi-nor.h | 4 ++++ 2 files changed, 55 insertions(+)(quoted diff) +0
-0
b/drivers/mtd/spi-nor/spi-nor.cindex d6f44d5..24b84d8 100644drivers/mtd/spi-nor/spi-nor.c +19
-0@@ -48,6 +48,25 @@ static int read_sr(struct spi_nor *nor) }/* +* Read the flag status register, returning its value in the location +* Return the status register value. +* Returns negative if error occurred. +*/ +static int read_fsr (struct spi_nor *nor) +{ +int ret; +u8 val; + + ret = nor->read_reg (nor,SPINOR_OP_RDFSR , &val,1 ); +if (ret <0 ) { +pr_err ("error %d reading FSR\n" , ret); +return ret; + } + +return val; +} + +/* * Read configuration register, returning its value in the
* location. Return the configuration register value.
* Returns negative if error occured.drivers/mtd/spi-nor/spi-nor.c +26
-0@@ -165,6 +184,32 @@ static int spi_nor_wait_till_ready(struct spi_nor *nor)return -ETIMEDOUT ; } +static int spi_nor_wait_till_fsr_ready (struct spi_nor *nor) +{ +unsigned long deadline; +int sr; +int fsr; + + deadline = jiffies +MAX_READY_WAIT_JIFFIES ; + +do { +cond_resched (); + + sr =read_sr (nor); +if (sr <0 ) +break ; +else if (!(sr &SR_WIP )) { + fsr =read_fsr (nor); +if (fsr <0 ) +break ; +if (fsr &FSR_READY ) +return 0 ; + } + }while (!time_after_eq (jiffies, deadline)); + +return -ETIMEDOUT ; +} +Quoted 8 lines
drivers/mtd/spi-nor/spi-nor.c struct flash_info { · line 448
Quoted 8 lines
/*
* Service routine to read status register until ready, or timeout occurs. * Returns non-zero if error.
drivers/mtd/spi-nor/spi-nor.c +0
-0@@ -402,6 +447,7 @@ struct flash_info {#define SECT_4K_PMC 0x10/* SPINOR_OP_BE_4K_PMC works uniformly */
Quoted 33 lines
Quoted 22 lines
drivers/mtd/spi-nor/spi-nor.c +1
-0#define SPI_NOR_DUAL_READ 0x20/* Flash supports Dual Read */ #define SPI_NOR_QUAD_READ 0x40/* Flash supports Quad Read */ +#define USE_FSR 0x80/* use flag status register */ };
#define
INFO(_jedec_id, _ext_id, _sector_size, _n_sectors, _flags)\drivers/mtd/spi-nor/spi-nor.c +2
-0@@ -488,6 +534,8 @@ const struct spi_device_id spi_nor_ids[] = { { "n25q128a13", INFO(0x20ba18, 0, 64 * 1024, 256, 0) }, { "n25q256a", INFO(0x20ba19, 0, 64 * 1024, 512, SECT_4K) }, { "n25q512a", INFO(0x20bb20, 0, 64 * 1024, 1024, SECT_4K) }, + { "n25q512ax3", INFO(0x20ba20, 0, 64 * 1024, 1024, USE_FSR) }, + { "n25q00", INFO(0x20ba21, 0, 64 * 1024, 2048, USE_FSR) },drivers/mtd/spi-nor/spi-nor.c +0
-0/* PMC */ { "pm25lv512", INFO(0, 0, 32 * 1024, 2, SECT_4K_PMC) },drivers/mtd/spi-nor/spi-nor.c int spi_nor_scan(struct spi_nor *nor, const struct · line 1021
Quoted 9 lines
drivers/mtd/spi-nor/spi-nor.c +3
-0@@ -965,6 +1013,9 @@ int spi_nor_scan(struct spi_nor *nor, const structspi_device_id *id, else mtd->_write = spi_nor_write; +if (info->flags &USE_FSR ) + nor->wait_till_ready = spi_nor_wait_till_fsr_ready; +the drivers may fills this hook itself, so the code should like this:
Quoted 5 lines
-------------------------------------------------- if ((info->flags & USE_FSR) && nor->wait_till_ready == spi_nor_wait_till_fsr_ready) nor->wait_till_ready = spi_nor_wait_till_fsr_ready; --------------------------------------------------
Quoted 4 lines
I sense a misdesign of the SPI NOR subsystem here. The subsystem and the driver compete for a function pointer here ? I guess one should have precedence in some way then ... and also, they should be two different pointers, where the subsystem decides which to use.
the subsystem do not decides which one to use, the driver decides which one to use.
If driver has its own @wait_till_ready , it means the driver knows the feature, and has implemented it in its own @wait_till_ready.
If the driver does not fill any wait_till_ready, it means the driver will use the default @wait_till_ready. We can treat the spi_nor_wait_till_fsr_ready as a default hook too.
thanks
Huang Shijie
Quoted 99 lines · Huang Shijie
Quoted 98 lines · Marek Vasut
Quoted 97 lines · Huang Shijie
Quoted 96 lines · Graham Moore
Some new Micron flash chips require reading the flag status register to determine when operations have completed.
Furthermore, chips with multi-die stacks of the 65nm 256Mb QSPI also require reading the status register before reading the flag status register.
This patch adds support for the flag status register in the n25q512ax3 and n25q00 Micron QSPI flash chips.
Signed-off-by Graham Moore <[email protected]>---
V3:
Rebase to l2-mtd spinor branch.
V2:
Remove leading underscore in function names. Remove type cast indev_errcall and use the proper format specifier instead.
---drivers/mtd/spi-nor/spi-nor.c | 51+++++++++++++++++++++++++++++++++++++++++
include/linux/mtd/spi-nor.h| 4 ++++
2 files changed, 55 insertions(+)(quoted diff) +0
-0
b/drivers/mtd/spi-nor/spi-nor.cindex d6f44d5..24b84d8 100644drivers/mtd/spi-nor/spi-nor.c +1
-0@@ -48,6 +48,25 @@ static int read_sr(struct spi_nor *nor) }/* +* Read the flag status register, returning its value in the location + * Return the status register value.
+ * Returns negative if error occurred.
+ */drivers/mtd/spi-nor/spi-nor.c +41
-0+static int read_fsr (struct spi_nor *nor) +{ +int ret; +u8 val; + + ret = nor->read_reg (nor,SPINOR_OP_RDFSR , &val,1 ); +if (ret <0 ) { +pr_err ("error %d reading FSR\n" , ret); +return ret; + } + +return val; +} + +/* * Read configuration register, returning its value in the * location. Return the configuration register value. * Returns negative if error occured. @@ -165,6 +184,32 @@ static int spi_nor_wait_till_ready(struct spi_nor *nor)return -ETIMEDOUT ; } +static int spi_nor_wait_till_fsr_ready (struct spi_nor *nor) +{ +unsigned long deadline; +int sr; +int fsr; + + deadline = jiffies +MAX_READY_WAIT_JIFFIES ; + +do { +cond_resched (); + + sr =read_sr (nor); +if (sr <0 ) +break ; +else if (!(sr &SR_WIP )) { + fsr =read_fsr (nor); +if (fsr <0 ) +break ; +if (fsr &FSR_READY ) +return 0 ; + } + }while (!time_after_eq (jiffies, deadline)); + +return -ETIMEDOUT ; +} +
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
drivers/mtd/spi-nor/spi-nor.c struct flash_info { · line 448
Quoted 8 lines
/*
* Service routine to read status register until ready, or timeout occurs. * Returns non-zero if error.
drivers/mtd/spi-nor/spi-nor.c +0
-0@@ -402,6 +447,7 @@ struct flash_info {#define SECT_4K_PMC 0x10/* SPINOR_OP_BE_4K_PMC
works
Quoted 4 lines
Quoted 4 lines
uniformly */
Quoted 1 line
Quoted 1 line
drivers/mtd/spi-nor/spi-nor.c +0
-0#define SPI_NOR_DUAL_READ 0x20/* Flash supports Dual
Read */
Quoted 1 line
Quoted 1 line
Quoted 1 line
drivers/mtd/spi-nor/spi-nor.c struct flash_info { · line 450
Quoted 1 line
drivers/mtd/spi-nor/spi-nor.c +0
-0#define SPI_NOR_QUAD_READ 0x40/* Flash supports Quad
Read */
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
drivers/mtd/spi-nor/spi-nor.c struct flash_info { · line 451
Quoted 2 lines
drivers/mtd/spi-nor/spi-nor.c +1
-0+#define USE_FSR 0x80/* use flag status
register */
Quoted 11 lines
Quoted 11 lines
Quoted 11 lines
Quoted 11 lines
drivers/mtd/spi-nor/spi-nor.c +0
-0};#define
INFO(_jedec_id, _ext_id, _sector_size, _n_sectors, _flags)\drivers/mtd/spi-nor/spi-nor.c +0
-0@@ -488,6 +534,8 @@ const struct spi_device_id spi_nor_ids[] = { { "n25q128a13", INFO(0x20ba18, 0, 64 * 1024, 256, 0) }, { "n25q256a", INFO(0x20ba19, 0, 64 * 1024, 512, SECT_4K) }, { "n25q512a", INFO(0x20bb20, 0, 64 * 1024, 1024, SECT_4K) },
Quoted 5 lines
Quoted 5 lines
Quoted 5 lines
drivers/mtd/spi-nor/spi-nor.c
Quoted 5 lines
drivers/mtd/spi-nor/spi-nor.c +2
-0+ { "n25q512ax3", INFO(0x20ba20, 0, 64 * 1024, 1024, USE_FSR) }, + { "n25q00", INFO(0x20ba21, 0, 64 * 1024, 2048, USE_FSR) },drivers/mtd/spi-nor/spi-nor.c +0
-0/* PMC */ {"pm25lv512" ,INFO (0 ,0 ,32 *1024 ,2 ,SECT_4K_PMC )
},
Quoted 31 lines
Quoted 19 lines
Quoted 19 lines
drivers/mtd/spi-nor/spi-nor.c int spi_nor_scan(struct spi_nor *nor, const · line 1021
Quoted 11 lines
drivers/mtd/spi-nor/spi-nor.c +3
-0@@ -965,6 +1013,9 @@ int spi_nor_scan(struct spi_nor *nor, conststruct spi_device_id *id, else mtd->_write = spi_nor_write; +if (info->flags &USE_FSR ) + nor->wait_till_ready = spi_nor_wait_till_fsr_ready; +the drivers may fills this hook itself, so the code should like this:
-------------------------------------------------- if ((info->flags & USE_FSR) && nor->wait_till_ready == spi_nor_wait_till_fsr_ready)Quoted 8 lines
Quoted 3 lines
nor->wait_till_ready = spi_nor_wait_till_fsr_ready;--------------------------------------------------I sense a misdesign of the SPI NOR subsystem here. The subsystem and the driver compete for a function pointer here ? I guess one should have precedence in some way then ... and also, they should be two different pointers, where the subsystem decides which to use.
the subsystem do not decides which one to use, the driver decides which one to use.
Quoted 6 lines
If driver has its own @wait_till_ready , it means the driver knows the feature, and has implemented it in its own @wait_till_ready.
If the driver does not fill any
wait_till_ready, it means the driver will use the default @wait_till_ready. We can treat thespi_nor_wait_till_fsr_readyas a default hook too.
I see the driver overwriting a hook previously set by the subsystem. This doesn't seem right to me, but please correct me if I'm wrong ...
Quoted 19 lines · Marek Vasut
Quoted 19 lines
Quoted 7 lines
Quoted 7 lines
the drivers may fills this hook itself, so the code should like this:
-------------------------------------------------- if ((info->flags & USE_FSR) && nor->wait_till_ready == spi_nor_wait_till_fsr_ready)Quoted 8 lines
Quoted 3 lines
nor->wait_till_ready = spi_nor_wait_till_fsr_ready;--------------------------------------------------I sense a misdesign of the SPI NOR subsystem here. The subsystem and the driver compete for a function pointer here ? I guess one should have precedence in some way then ... and also, they should be two different pointers, where the subsystem decides which to use.
the subsystem do not decides which one to use, the driver decides which one to use.
Quoted 8 lines
Quoted 6 lines
If driver has its own @wait_till_ready , it means the driver knows the feature, and has implemented it in its own @wait_till_ready.
If the driver does not fill any
wait_till_ready, it means the driver will use the default @wait_till_ready. We can treat thespi_nor_wait_till_fsr_readyas a default hook too.I see the driver overwriting a hook previously set by the subsystem. This
not sure ;)
The driver set the hooks before the subsystem set these hooks.
If the driver has already set the @wait_till_ready hook before it calls the spi_nor_scan, the subsystem will not set the hook anymore.
Please see the spi_nor_check().
thanks
Huang Shijie
Quoted 29 lines · Huang Shijie
Quoted 19 lines · Marek Vasut
Quoted 19 lines
Quoted 7 lines
Quoted 7 lines
the drivers may fills this hook itself, so the code should like this:
-------------------------------------------------- if ((info->flags & USE_FSR) && nor->wait_till_ready == spi_nor_wait_till_fsr_ready)Quoted 8 lines
Quoted 3 lines
nor->wait_till_ready = spi_nor_wait_till_fsr_ready;--------------------------------------------------I sense a misdesign of the SPI NOR subsystem here. The subsystem and the driver compete for a function pointer here ? I guess one should have precedence in some way then ... and also, they should be two different pointers, where the subsystem decides which to use.
the subsystem do not decides which one to use, the driver decides which one to use.
Quoted 8 lines
Quoted 6 lines
If driver has its own @wait_till_ready , it means the driver knows the feature, and has implemented it in its own @wait_till_ready.
If the driver does not fill any
wait_till_ready, it means the driver will use the default @wait_till_ready. We can treat thespi_nor_wait_till_fsr_readyas a default hook too.I see the driver overwriting a hook previously set by the subsystem. This
Quoted 8 lines
not sure ;)
The driver set the hooks before the subsystem set these hooks.
If the driver has already set the @wait_till_ready hook before it calls the
spi_nor_scan, the subsystem will not set the hook anymore.Please see the
spi_nor_check().
Two things competing over the same pointer looks misdesigned to me. I will need to dig into this one more time ...
Best regards,
Marek Vasut
Quoted 34 lines · Marek Vasut
Quoted 33 lines · Huang Shijie
Quoted 19 lines · Marek Vasut
Quoted 19 lines
Quoted 7 lines
Quoted 7 lines
the drivers may fills this hook itself, so the code should like this:
-------------------------------------------------- if ((info->flags & USE_FSR) && nor->wait_till_ready == spi_nor_wait_till_fsr_ready)Quoted 8 lines
Quoted 3 lines
nor->wait_till_ready = spi_nor_wait_till_fsr_ready;--------------------------------------------------I sense a misdesign of the SPI NOR subsystem here. The subsystem and the driver compete for a function pointer here ? I guess one should have precedence in some way then ... and also, they should be two different pointers, where the subsystem decides which to use.
the subsystem do not decides which one to use, the driver decides which one to use.
Quoted 8 lines
Quoted 6 lines
If driver has its own @wait_till_ready , it means the driver knows the feature, and has implemented it in its own @wait_till_ready.
If the driver does not fill any
wait_till_ready, it means the driver will use the default @wait_till_ready. We can treat thespi_nor_wait_till_fsr_readyas a default hook too.I see the driver overwriting a hook previously set by the subsystem. This
not sure ;)
The driver set the hooks before the subsystem set these hooks.
Quoted 7 lines
Quoted 4 lines
If the driver has already set the @wait_till_ready hook before it calls the
spi_nor_scan, the subsystem will not set the hook anymore.Please see the
spi_nor_check().Two things competing over the same pointer looks misdesigned to me. I will need to dig into this one more time ...
Please refer to the code for NAND chip, the nand_get_flash_type() :
-----------------------------------------------------------
/* Do not replace user supplied command function! */ if (mtd->writesize > 512 && chip->cmdfunc == nand_command) chip->cmdfunc = nand_command_lp;
-----------------------------------------------------------
It uses the same logic:
" Do not replace user supplied command function!"
thanks
Huang Shijie
Quoted 10 lines · Huang Shijie
Quoted 3 lines · Marek Vasut
Two things competing over the same pointer looks misdesigned to me. I will need to dig into this one more time ...
Please refer to the code for NAND chip, the
nand_get_flash_type():-----------------------------------------------------------
/* Do not replace user supplied command function! */ if (mtd->writesize > 512 && chip->cmdfunc == nand_command) chip->cmdfunc = nand_command_lp;
Quoted 7 lines
-----------------------------------------------------------
It uses the same logic:
" Do not replace user supplied command function!"thanks
Huang Shijie
I would like to set the @wait_till_ready in m25p80.c, because the USE_FSR flag is only for Micron chips. But the m25p80 driver doesn't have access to the flags, they are contained in spi-nor.c. It seems to me that the driver_data should be accessible by the driver. So maybe there is room for improvement there.
Thanks,
Graham Moore
Quoted 13 lines · Graham Moore
Quoted 12 lines · Huang Shijie
Quoted 3 lines · Marek Vasut
Two things competing over the same pointer looks misdesigned to me. I will need to dig into this one more time ...
Please refer to the code for NAND chip, the
nand_get_flash_type():-----------------------------------------------------------
/* Do not replace user supplied command function! */ if (mtd->writesize > 512 && chip->cmdfunc == nand_command) chip->cmdfunc = nand_command_lp;-----------------------------------------------------------
Quoted 8 lines
Quoted 5 lines
It uses the same logic:
" Do not replace user supplied command function!"thanks
Huang ShijieI would like to set the @wait_till_ready in
m25p80.c, because theUSE_FSRflag is only for Micron chips. But the m25p80 driver doesn't
m25p80.c is not the right place.
we should put these code in the spi-nor.c
thanks
Huang Shijie
Hi guys,
Sorry to revisit this way late, and sorry for not paying as much attention initially. I'm prepped to merge v4, but some of the conversation matches what I was just thinking.
Quoted 34 lines · Marek Vasut
Quoted 33 lines · Huang Shijie
Quoted 19 lines · Marek Vasut
Quoted 19 lines
Quoted 7 lines
Quoted 7 lines
the drivers may fills this hook itself, so the code should like this:
-------------------------------------------------- if ((info->flags & USE_FSR) && nor->wait_till_ready == spi_nor_wait_till_fsr_ready)Quoted 8 lines
Quoted 3 lines
nor->wait_till_ready = spi_nor_wait_till_fsr_ready;--------------------------------------------------I sense a misdesign of the SPI NOR subsystem here. The subsystem and the driver compete for a function pointer here ? I guess one should have precedence in some way then ... and also, they should be two different pointers, where the subsystem decides which to use.
the subsystem do not decides which one to use, the driver decides which one to use.
Quoted 8 lines
Quoted 6 lines
If driver has its own @wait_till_ready , it means the driver knows the feature, and has implemented it in its own @wait_till_ready.
If the driver does not fill any
wait_till_ready, it means the driver will use the default @wait_till_ready. We can treat thespi_nor_wait_till_fsr_readyas a default hook too.I see the driver overwriting a hook previously set by the subsystem. This
not sure ;)
The driver set the hooks before the subsystem set these hooks.
Quoted 7 lines
Quoted 4 lines
If the driver has already set the @wait_till_ready hook before it calls the
spi_nor_scan, the subsystem will not set the hook anymore.Please see the
spi_nor_check().Two things competing over the same pointer looks misdesigned to me. I will need to dig into this one more time ...
Yes, that is misdesigned. And looking at nand_base for examples is not foolproof; it has quite a bit of legacy and workarounds. It'd be best to get the design right for spi-nor.
The subsystem code should not require a function pointer for FSR vs. non-FSR -- all devices should be able to use the same function. We just need to stash some flash-ID-related data (i.e., a flags field) in the spi_nor struct. On the plus side, we can avoid the code duplication between spi_nor_wait_till_fsr_ready() and spi_nor_wait_till_ready().
I think the wait_till_ready pointer should be reserved for the driver, as a hardware-specific "wait" function.
This still leaves the question of whether the SPI NOR core should assume that any driver's 'wait_till_ready' function (if present) actually implements all necessary waits (FSR vs. non-FSR, for instance). I'd argue that's a maintenance burden, and that the subsystem should still do a sanity check that the status register is correct. After all, that's what the ->{read,write}_reg() functions are useful for. But perhaps there is some performance argument for avoiding the (potentially redundant) register checks?
Anyway, I've tested v4, and I plan to merge it soon. Patches can be sent on top. (I may even cook up my own.)
Regards,
Brian
Quoted 97 lines · Marek Vasut
Quoted 96 lines · Huang Shijie
Quoted 95 lines · Graham Moore
Some new Micron flash chips require reading the flag status register to determine when operations have completed.
Furthermore, chips with multi-die stacks of the 65nm 256Mb QSPI also require reading the status register before reading the flag status register.
This patch adds support for the flag status register in the n25q512ax3 and n25q00 Micron QSPI flash chips.
Signed-off-by Graham Moore <[email protected]>---
V3:
Rebase to l2-mtd spinor branch.
V2:
Remove leading underscore in function names. Remove type cast indev_errcall and use the proper format specifier instead.
---drivers/mtd/spi-nor/spi-nor.c | 51 +++++++++++++++++++++++++++++++++++++++++ include/linux/mtd/spi-nor.h | 4 ++++ 2 files changed, 55 insertions(+)(quoted diff) +0
-0
b/drivers/mtd/spi-nor/spi-nor.cindex d6f44d5..24b84d8 100644drivers/mtd/spi-nor/spi-nor.c +19
-0@@ -48,6 +48,25 @@ static int read_sr(struct spi_nor *nor) }/* +* Read the flag status register, returning its value in the location +* Return the status register value. +* Returns negative if error occurred. +*/ +static int read_fsr (struct spi_nor *nor) +{ +int ret; +u8 val; + + ret = nor->read_reg (nor,SPINOR_OP_RDFSR , &val,1 ); +if (ret <0 ) { +pr_err ("error %d reading FSR\n" , ret); +return ret; + } + +return val; +} + +/* * Read configuration register, returning its value in the
* location. Return the configuration register value.
* Returns negative if error occured.drivers/mtd/spi-nor/spi-nor.c +26
-0@@ -165,6 +184,32 @@ static int spi_nor_wait_till_ready(struct spi_nor *nor)return -ETIMEDOUT ; } +static int spi_nor_wait_till_fsr_ready (struct spi_nor *nor) +{ +unsigned long deadline; +int sr; +int fsr; + + deadline = jiffies +MAX_READY_WAIT_JIFFIES ; + +do { +cond_resched (); + + sr =read_sr (nor); +if (sr <0 ) +break ; +else if (!(sr &SR_WIP )) { + fsr =read_fsr (nor); +if (fsr <0 ) +break ; +if (fsr &FSR_READY ) +return 0 ; + } + }while (!time_after_eq (jiffies, deadline)); + +return -ETIMEDOUT ; +} +/*
Quoted 45 lines
Quoted 6 lines
drivers/mtd/spi-nor/spi-nor.c struct flash_info { · line 448
Quoted 6 lines
* Service routine to read status register until ready, or timeout occurs. * Returns non-zero if error.
drivers/mtd/spi-nor/spi-nor.c +0
-0@@ -402,6 +447,7 @@ struct flash_info {#define SECT_4K_PMC 0x10/* SPINOR_OP_BE_4K_PMC works uniformly */
Quoted 38 lines
Quoted 22 lines
drivers/mtd/spi-nor/spi-nor.c +1
-0#define SPI_NOR_DUAL_READ 0x20/* Flash supports Dual Read */ #define SPI_NOR_QUAD_READ 0x40/* Flash supports Quad Read */ +#define USE_FSR 0x80/* use flag status register */ };
#define
INFO(_jedec_id, _ext_id, _sector_size, _n_sectors, _flags)\drivers/mtd/spi-nor/spi-nor.c +2
-0@@ -488,6 +534,8 @@ const struct spi_device_id spi_nor_ids[] = { { "n25q128a13", INFO(0x20ba18, 0, 64 * 1024, 256, 0) }, { "n25q256a", INFO(0x20ba19, 0, 64 * 1024, 512, SECT_4K) }, { "n25q512a", INFO(0x20bb20, 0, 64 * 1024, 1024, SECT_4K) }, + { "n25q512ax3", INFO(0x20ba20, 0, 64 * 1024, 1024, USE_FSR) }, + { "n25q00", INFO(0x20ba21, 0, 64 * 1024, 2048, USE_FSR) },drivers/mtd/spi-nor/spi-nor.c +0
-0/* PMC */ { "pm25lv512", INFO(0, 0, 32 * 1024, 2, SECT_4K_PMC) },drivers/mtd/spi-nor/spi-nor.c int spi_nor_scan(struct spi_nor *nor, const struct · line 1021
Quoted 9 lines
drivers/mtd/spi-nor/spi-nor.c +3
-0@@ -965,6 +1013,9 @@ int spi_nor_scan(struct spi_nor *nor, const structspi_device_id *id, else mtd->_write = spi_nor_write; +if (info->flags &USE_FSR ) + nor->wait_till_ready = spi_nor_wait_till_fsr_ready; +the drivers may fills this hook itself, so the code should like this:
-------------------------------------------------- if ((info->flags & USE_FSR) && nor->wait_till_ready == spi_nor_wait_till_fsr_ready) nor->wait_till_ready = spi_nor_wait_till_fsr_ready; --------------------------------------------------
sorry, the code should like this:
--------------------------------------------------
if ((info->flags & USE_FSR) &&
nor->wait_till_ready == spi_nor_wait_till_ready)
nor->wait_till_ready = spi_nor_wait_till_fsr_ready;
--------------------------------------------------
thanks
Huang Shijie