diff mbox series

[v4,1/3] mtd: spi-nor: Add the SNOR_F_4B_OPCODES flag

Message ID 20181129144143.26079-2-boris.brezillon@bootlin.com
State Superseded
Headers show
Series mtd: spi-nor: mx25l25635f: Use 4B opcodes | expand

Commit Message

Boris Brezillon Nov. 29, 2018, 2:41 p.m. UTC
Some flash_info entries have the SPI_NOR_4B_OPCODES flag set to let the
core know that the flash supports 4B opcode. While this solution works
fine for id-based caps detection, it doesn't work that well when relying
on SFDP-based caps detection. Let's add an SNOR_F_4B_OPCODES flag so
that the SFDP parsing code can set it when appropriate.

Reported-by: Alexandre Belloni <alexandre.belloni@bootlin.com>
Signed-off-by: Boris Brezillon <boris.brezillon@bootlin.com>
Tested-by: Alexandre Belloni <alexandre.belloni@bootlin.com>
Reviewed-by: Tudor Ambarus <tudor.ambarus@microchip.com>
---
Changes in v4:
- Set SNOR_F_4B_OPCODES flag outside of the if (mtd->size > 0x1000000)
  block
- Do not set SNOR_F_4B_OPCODES when BFPT_DWORD1_ADDRESS_BYTES_4_ONLY,
  because 4byte only does not imply 4B opcodes are supported

Changes in v3:
- Clear SNOR_F_4B_OPCODES flag when SFDP fails
- Add Alexandre R-b

Changes in v2:
- Fix the commit message
- Fix the ->addr_width check
- Add a comma at the end of the SNOR_F_4B_OPCODES definition
---
 drivers/mtd/spi-nor/spi-nor.c | 22 ++++++++++++----------
 include/linux/mtd/spi-nor.h   |  1 +
 2 files changed, 13 insertions(+), 10 deletions(-)

Comments

Tudor Ambarus Dec. 5, 2018, 3:08 p.m. UTC | #1
Hi, Boris,

On 11/29/2018 04:41 PM, Boris Brezillon wrote:
> Some flash_info entries have the SPI_NOR_4B_OPCODES flag set to let the
> core know that the flash supports 4B opcode. While this solution works
> fine for id-based caps detection, it doesn't work that well when relying
> on SFDP-based caps detection. Let's add an SNOR_F_4B_OPCODES flag so
> that the SFDP parsing code can set it when appropriate.
> 
> Reported-by: Alexandre Belloni <alexandre.belloni@bootlin.com>
> Signed-off-by: Boris Brezillon <boris.brezillon@bootlin.com>
> Tested-by: Alexandre Belloni <alexandre.belloni@bootlin.com>
> Reviewed-by: Tudor Ambarus <tudor.ambarus@microchip.com>
> ---
> Changes in v4:
> - Set SNOR_F_4B_OPCODES flag outside of the if (mtd->size > 0x1000000)
>   block
> - Do not set SNOR_F_4B_OPCODES when BFPT_DWORD1_ADDRESS_BYTES_4_ONLY,
>   because 4byte only does not imply 4B opcodes are supported

and you got rid of the superfluous check "(JEDEC_MFR(nor->info) !=
SNOR_MFR_SPANSION)", which is good, thanks.

And I guess you should have dropped my rb tag, because I haven't reviewed v4
yet. No worries, just saying ...

> 
> Changes in v3:
> - Clear SNOR_F_4B_OPCODES flag when SFDP fails
> - Add Alexandre R-b
> 
> Changes in v2:
> - Fix the commit message
> - Fix the ->addr_width check
> - Add a comma at the end of the SNOR_F_4B_OPCODES definition
> ---
>  drivers/mtd/spi-nor/spi-nor.c | 22 ++++++++++++----------
>  include/linux/mtd/spi-nor.h   |  1 +
>  2 files changed, 13 insertions(+), 10 deletions(-)
> 
> diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c
> index c1d9c2e50bee..98b8d9a778aa 100644
> --- a/drivers/mtd/spi-nor/spi-nor.c
> +++ b/drivers/mtd/spi-nor/spi-nor.c
> @@ -3311,6 +3311,7 @@ static int spi_nor_init_params(struct spi_nor *nor,
>  
>  		if (spi_nor_parse_sfdp(nor, &sfdp_params)) {
>  			nor->addr_width = 0;
> +			nor->flags &= ~SNOR_F_4B_OPCODES;
>  			/* restore previous erase map */
>  			memcpy(&nor->erase_map, &prev_map,
>  			       sizeof(nor->erase_map));
> @@ -3561,9 +3562,7 @@ static int spi_nor_init(struct spi_nor *nor)
>  		}
>  	}
>  
> -	if ((nor->addr_width == 4) &&
> -	    (JEDEC_MFR(nor->info) != SNOR_MFR_SPANSION) &&
> -	    !(nor->info->flags & SPI_NOR_4B_OPCODES)) {
> +	if (nor->addr_width == 4 && !(nor->flags & SNOR_F_4B_OPCODES)) {
>  		/*
>  		 * If the RESET# pin isn't hooked up properly, or the system
>  		 * otherwise doesn't perform a reset command in the boot
> @@ -3595,10 +3594,8 @@ static void spi_nor_resume(struct mtd_info *mtd)
>  void spi_nor_restore(struct spi_nor *nor)
>  {
>  	/* restore the addressing mode */
> -	if ((nor->addr_width == 4) &&
> -	    (JEDEC_MFR(nor->info) != SNOR_MFR_SPANSION) &&
> -	    !(nor->info->flags & SPI_NOR_4B_OPCODES) &&
> -	    (nor->flags & SNOR_F_BROKEN_RESET))
> +	if (nor->addr_width == 4 && !(nor->flags & SNOR_F_4B_OPCODES) &&
> +	    nor->flags & SNOR_F_BROKEN_RESET)
>  		set_4byte(nor, false);
>  }
>  EXPORT_SYMBOL_GPL(spi_nor_restore);
> @@ -3750,6 +3747,10 @@ int spi_nor_scan(struct spi_nor *nor, const char *name,
>  	if (info->flags & SPI_NOR_NO_FR)
>  		params.hwcaps.mask &= ~SNOR_HWCAPS_READ_FAST;
>  
> +	if (info->flags & SPI_NOR_4B_OPCODES ||
> +	    (JEDEC_MFR(info) == SNOR_MFR_SPANSION && mtd->size > SZ_16M))
> +		nor->flags |= SNOR_F_4B_OPCODES;
> +

you are potentially overwriting the SNOR_F_4B_OPCODES that may be set in SFDP. I
suggest to set SNOR_F_4B_OPCODES flag inside of the "if (mtd->size > 0x1000000)"
block.

>  	/*
>  	 * Configure the SPI memory:
>  	 * - select op codes for (Fast) Read, Page Program and Sector Erase.
> @@ -3768,13 +3769,14 @@ int spi_nor_scan(struct spi_nor *nor, const char *name,
>  	} 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 ||
> -		    info->flags & SPI_NOR_4B_OPCODES)
> -			spi_nor_set_4byte_opcodes(nor);
>  	} else {
>  		nor->addr_width = 3;
>  	}
>  
> +	if (nor->addr_width == 4 &&
> +	    nor->flags & SNOR_F_4B_OPCODES)

the conditions fit in a single line

Cheers,
ta
Boris Brezillon Dec. 5, 2018, 3:19 p.m. UTC | #2
On Wed, 5 Dec 2018 15:08:49 +0000
<Tudor.Ambarus@microchip.com> wrote:

> Hi, Boris,
> 
> On 11/29/2018 04:41 PM, Boris Brezillon wrote:
> > Some flash_info entries have the SPI_NOR_4B_OPCODES flag set to let the
> > core know that the flash supports 4B opcode. While this solution works
> > fine for id-based caps detection, it doesn't work that well when relying
> > on SFDP-based caps detection. Let's add an SNOR_F_4B_OPCODES flag so
> > that the SFDP parsing code can set it when appropriate.
> > 
> > Reported-by: Alexandre Belloni <alexandre.belloni@bootlin.com>
> > Signed-off-by: Boris Brezillon <boris.brezillon@bootlin.com>
> > Tested-by: Alexandre Belloni <alexandre.belloni@bootlin.com>
> > Reviewed-by: Tudor Ambarus <tudor.ambarus@microchip.com>
> > ---
> > Changes in v4:
> > - Set SNOR_F_4B_OPCODES flag outside of the if (mtd->size > 0x1000000)
> >   block
> > - Do not set SNOR_F_4B_OPCODES when BFPT_DWORD1_ADDRESS_BYTES_4_ONLY,
> >   because 4byte only does not imply 4B opcodes are supported  
> 
> and you got rid of the superfluous check "(JEDEC_MFR(nor->info) !=
> SNOR_MFR_SPANSION)", which is good, thanks.
> 
> And I guess you should have dropped my rb tag, because I haven't reviewed v4
> yet. No worries, just saying ...

Yes, my bad.

> 
> > 
> > Changes in v3:
> > - Clear SNOR_F_4B_OPCODES flag when SFDP fails
> > - Add Alexandre R-b
> > 
> > Changes in v2:
> > - Fix the commit message
> > - Fix the ->addr_width check
> > - Add a comma at the end of the SNOR_F_4B_OPCODES definition
> > ---
> >  drivers/mtd/spi-nor/spi-nor.c | 22 ++++++++++++----------
> >  include/linux/mtd/spi-nor.h   |  1 +
> >  2 files changed, 13 insertions(+), 10 deletions(-)
> > 
> > diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c
> > index c1d9c2e50bee..98b8d9a778aa 100644
> > --- a/drivers/mtd/spi-nor/spi-nor.c
> > +++ b/drivers/mtd/spi-nor/spi-nor.c
> > @@ -3311,6 +3311,7 @@ static int spi_nor_init_params(struct spi_nor *nor,
> >  
> >  		if (spi_nor_parse_sfdp(nor, &sfdp_params)) {
> >  			nor->addr_width = 0;
> > +			nor->flags &= ~SNOR_F_4B_OPCODES;
> >  			/* restore previous erase map */
> >  			memcpy(&nor->erase_map, &prev_map,
> >  			       sizeof(nor->erase_map));
> > @@ -3561,9 +3562,7 @@ static int spi_nor_init(struct spi_nor *nor)
> >  		}
> >  	}
> >  
> > -	if ((nor->addr_width == 4) &&
> > -	    (JEDEC_MFR(nor->info) != SNOR_MFR_SPANSION) &&
> > -	    !(nor->info->flags & SPI_NOR_4B_OPCODES)) {
> > +	if (nor->addr_width == 4 && !(nor->flags & SNOR_F_4B_OPCODES)) {
> >  		/*
> >  		 * If the RESET# pin isn't hooked up properly, or the system
> >  		 * otherwise doesn't perform a reset command in the boot
> > @@ -3595,10 +3594,8 @@ static void spi_nor_resume(struct mtd_info *mtd)
> >  void spi_nor_restore(struct spi_nor *nor)
> >  {
> >  	/* restore the addressing mode */
> > -	if ((nor->addr_width == 4) &&
> > -	    (JEDEC_MFR(nor->info) != SNOR_MFR_SPANSION) &&
> > -	    !(nor->info->flags & SPI_NOR_4B_OPCODES) &&
> > -	    (nor->flags & SNOR_F_BROKEN_RESET))
> > +	if (nor->addr_width == 4 && !(nor->flags & SNOR_F_4B_OPCODES) &&
> > +	    nor->flags & SNOR_F_BROKEN_RESET)
> >  		set_4byte(nor, false);
> >  }
> >  EXPORT_SYMBOL_GPL(spi_nor_restore);
> > @@ -3750,6 +3747,10 @@ int spi_nor_scan(struct spi_nor *nor, const char *name,
> >  	if (info->flags & SPI_NOR_NO_FR)
> >  		params.hwcaps.mask &= ~SNOR_HWCAPS_READ_FAST;
> >  
> > +	if (info->flags & SPI_NOR_4B_OPCODES ||
> > +	    (JEDEC_MFR(info) == SNOR_MFR_SPANSION && mtd->size > SZ_16M))
> > +		nor->flags |= SNOR_F_4B_OPCODES;
> > +  
> 
> you are potentially overwriting the SNOR_F_4B_OPCODES that may be set in SFDP. I
> suggest to set SNOR_F_4B_OPCODES flag inside of the "if (mtd->size > 0x1000000)"
> block.

Shouldn't we override this value anyway? I mean, I thought flash_info
flags had precedence on the SFDP ones. Also, just because the flash is
smaller than 16MB, doesn't mean it does not support 4B opcodes. We
probably won't use the 4B opcodes in that case, but still.

> 
> >  	/*
> >  	 * Configure the SPI memory:
> >  	 * - select op codes for (Fast) Read, Page Program and Sector Erase.
> > @@ -3768,13 +3769,14 @@ int spi_nor_scan(struct spi_nor *nor, const char *name,
> >  	} 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 ||
> > -		    info->flags & SPI_NOR_4B_OPCODES)
> > -			spi_nor_set_4byte_opcodes(nor);
> >  	} else {
> >  		nor->addr_width = 3;
> >  	}
> >  
> > +	if (nor->addr_width == 4 &&
> > +	    nor->flags & SNOR_F_4B_OPCODES)  
> 
> the conditions fit in a single line

Will fix that.
Tudor Ambarus Dec. 5, 2018, 3:48 p.m. UTC | #3
On 12/05/2018 05:19 PM, Boris Brezillon wrote:
>>> @@ -3750,6 +3747,10 @@ int spi_nor_scan(struct spi_nor *nor, const char *name,
>>>  	if (info->flags & SPI_NOR_NO_FR)
>>>  		params.hwcaps.mask &= ~SNOR_HWCAPS_READ_FAST;
>>>  
>>> +	if (info->flags & SPI_NOR_4B_OPCODES ||
>>> +	    (JEDEC_MFR(info) == SNOR_MFR_SPANSION && mtd->size > SZ_16M))
>>> +		nor->flags |= SNOR_F_4B_OPCODES;
>>> +  
>> you are potentially overwriting the SNOR_F_4B_OPCODES that may be set in SFDP. I
>> suggest to set SNOR_F_4B_OPCODES flag inside of the "if (mtd->size > 0x1000000)"
>> block.
> Shouldn't we override this value anyway? I mean, I thought flash_info
> flags had precedence on the SFDP ones. Also, just because the flash is

I tend to say that we shouldn't. We have some "JEDEC knows better" attitude in
the code: we are overwriting platform ID if we find a different ID in sfdp, we
choose addr_width from SFDP even if set in info->addr_width, and we are
overwriting all the settings based on flash_info when sfdp parsing succeeds in
spi_nor_init_params().

> smaller than 16MB, doesn't mean it does not support 4B opcodes. We
> probably won't use the 4B opcodes in that case, but still.
> 

I agree that manufacturers have a sense of humor and this might be possible. But
there's no need to use 4B opcodes in this case, so a post_sfdp fixup will help
here too.

Cheers,
ta
Boris Brezillon Dec. 5, 2018, 4 p.m. UTC | #4
On Wed, 5 Dec 2018 15:48:46 +0000
<Tudor.Ambarus@microchip.com> wrote:

> On 12/05/2018 05:19 PM, Boris Brezillon wrote:
> >>> @@ -3750,6 +3747,10 @@ int spi_nor_scan(struct spi_nor *nor, const char *name,
> >>>  	if (info->flags & SPI_NOR_NO_FR)
> >>>  		params.hwcaps.mask &= ~SNOR_HWCAPS_READ_FAST;
> >>>  
> >>> +	if (info->flags & SPI_NOR_4B_OPCODES ||
> >>> +	    (JEDEC_MFR(info) == SNOR_MFR_SPANSION && mtd->size > SZ_16M))
> >>> +		nor->flags |= SNOR_F_4B_OPCODES;
> >>> +    
> >> you are potentially overwriting the SNOR_F_4B_OPCODES that may be set in SFDP. I
> >> suggest to set SNOR_F_4B_OPCODES flag inside of the "if (mtd->size > 0x1000000)"
> >> block.  
> > Shouldn't we override this value anyway? I mean, I thought flash_info
> > flags had precedence on the SFDP ones. Also, just because the flash is  
> 
> I tend to say that we shouldn't. We have some "JEDEC knows better" attitude in
> the code: we are overwriting platform ID if we find a different ID in sfdp, we
> choose addr_width from SFDP even if set in info->addr_width, and we are
> overwriting all the settings based on flash_info when sfdp parsing succeeds in
> spi_nor_init_params().

Given all the "broken SFDP" problems we had, I'm not sure this was the
right decision, but that's another topic.

For this specific one, I'd really prefer to keep this code as is. Note
that the "JEDEC_MFR(info) == SNOR_MFR_SPANSION && mtd->size > SZ_16M"
is later moved to a post SFDP fixup hook in my rework, which means we'll
anyway override the decision taken by the SFDP parsing.

> 
> > smaller than 16MB, doesn't mean it does not support 4B opcodes. We
> > probably won't use the 4B opcodes in that case, but still.
> >   
> 
> I agree that manufacturers have a sense of humor and this might be possible. But
> there's no need to use 4B opcodes in this case, so a post_sfdp fixup will help
> here too.

Except there's nothing to fix in this case, we just won't use 4B
opcodes if we don't need to, that's all.
Tudor Ambarus Dec. 5, 2018, 4:26 p.m. UTC | #5
On 12/05/2018 06:00 PM, Boris Brezillon wrote:
> On Wed, 5 Dec 2018 15:48:46 +0000
> <Tudor.Ambarus@microchip.com> wrote:
> 
>> On 12/05/2018 05:19 PM, Boris Brezillon wrote:
>>>>> @@ -3750,6 +3747,10 @@ int spi_nor_scan(struct spi_nor *nor, const char *name,
>>>>>  	if (info->flags & SPI_NOR_NO_FR)
>>>>>  		params.hwcaps.mask &= ~SNOR_HWCAPS_READ_FAST;
>>>>>  
>>>>> +	if (info->flags & SPI_NOR_4B_OPCODES ||
>>>>> +	    (JEDEC_MFR(info) == SNOR_MFR_SPANSION && mtd->size > SZ_16M))
>>>>> +		nor->flags |= SNOR_F_4B_OPCODES;
>>>>> +    
>>>> you are potentially overwriting the SNOR_F_4B_OPCODES that may be set in SFDP. I
>>>> suggest to set SNOR_F_4B_OPCODES flag inside of the "if (mtd->size > 0x1000000)"
>>>> block.  
>>> Shouldn't we override this value anyway? I mean, I thought flash_info
>>> flags had precedence on the SFDP ones. Also, just because the flash is  
>>
>> I tend to say that we shouldn't. We have some "JEDEC knows better" attitude in
>> the code: we are overwriting platform ID if we find a different ID in sfdp, we
>> choose addr_width from SFDP even if set in info->addr_width, and we are
>> overwriting all the settings based on flash_info when sfdp parsing succeeds in
>> spi_nor_init_params().
> 
> Given all the "broken SFDP" problems we had, I'm not sure this was the
> right decision, but that's another topic.
> 
> For this specific one, I'd really prefer to keep this code as is. Note
> that the "JEDEC_MFR(info) == SNOR_MFR_SPANSION && mtd->size > SZ_16M"
> is later moved to a post SFDP fixup hook in my rework, which means we'll
> anyway override the decision taken by the SFDP parsing.
> 
>>
>>> smaller than 16MB, doesn't mean it does not support 4B opcodes. We
>>> probably won't use the 4B opcodes in that case, but still.
>>>   
>>
>> I agree that manufacturers have a sense of humor and this might be possible. But
>> there's no need to use 4B opcodes in this case, so a post_sfdp fixup will help
>> here too.
> 
> Except there's nothing to fix in this case, we just won't use 4B
> opcodes if we don't need to, that's all.

you'll have an extra byte of address that has a tiny impact on performance on
small requests. I see it as a fix, we should do what's best to do. anyway ...
Boris Brezillon Dec. 5, 2018, 4:32 p.m. UTC | #6
On Wed, 5 Dec 2018 16:26:06 +0000
<Tudor.Ambarus@microchip.com> wrote:

> On 12/05/2018 06:00 PM, Boris Brezillon wrote:
> > On Wed, 5 Dec 2018 15:48:46 +0000
> > <Tudor.Ambarus@microchip.com> wrote:
> >   
> >> On 12/05/2018 05:19 PM, Boris Brezillon wrote:  
> >>>>> @@ -3750,6 +3747,10 @@ int spi_nor_scan(struct spi_nor *nor, const char *name,
> >>>>>  	if (info->flags & SPI_NOR_NO_FR)
> >>>>>  		params.hwcaps.mask &= ~SNOR_HWCAPS_READ_FAST;
> >>>>>  
> >>>>> +	if (info->flags & SPI_NOR_4B_OPCODES ||
> >>>>> +	    (JEDEC_MFR(info) == SNOR_MFR_SPANSION && mtd->size > SZ_16M))
> >>>>> +		nor->flags |= SNOR_F_4B_OPCODES;
> >>>>> +      
> >>>> you are potentially overwriting the SNOR_F_4B_OPCODES that may be set in SFDP. I
> >>>> suggest to set SNOR_F_4B_OPCODES flag inside of the "if (mtd->size > 0x1000000)"
> >>>> block.    
> >>> Shouldn't we override this value anyway? I mean, I thought flash_info
> >>> flags had precedence on the SFDP ones. Also, just because the flash is    
> >>
> >> I tend to say that we shouldn't. We have some "JEDEC knows better" attitude in
> >> the code: we are overwriting platform ID if we find a different ID in sfdp, we
> >> choose addr_width from SFDP even if set in info->addr_width, and we are
> >> overwriting all the settings based on flash_info when sfdp parsing succeeds in
> >> spi_nor_init_params().  
> > 
> > Given all the "broken SFDP" problems we had, I'm not sure this was the
> > right decision, but that's another topic.
> > 
> > For this specific one, I'd really prefer to keep this code as is. Note
> > that the "JEDEC_MFR(info) == SNOR_MFR_SPANSION && mtd->size > SZ_16M"
> > is later moved to a post SFDP fixup hook in my rework, which means we'll
> > anyway override the decision taken by the SFDP parsing.
> >   
> >>  
> >>> smaller than 16MB, doesn't mean it does not support 4B opcodes. We
> >>> probably won't use the 4B opcodes in that case, but still.
> >>>     
> >>
> >> I agree that manufacturers have a sense of humor and this might be possible. But
> >> there's no need to use 4B opcodes in this case, so a post_sfdp fixup will help
> >> here too.  
> > 
> > Except there's nothing to fix in this case, we just won't use 4B
> > opcodes if we don't need to, that's all.  
> 
> you'll have an extra byte of address that has a tiny impact on performance on
> small requests. I see it as a fix, we should do what's best to do. anyway ...

No, see the checks that are done in this patch: to use 4B_CODES the
flag should be set and the NOR should be larger than 16MB. The flag
does not mean "use 4B opcodes", it meas "4B opcodes are supported".
Boris Brezillon Dec. 5, 2018, 4:54 p.m. UTC | #7
On Wed, 5 Dec 2018 17:32:54 +0100
Boris Brezillon <boris.brezillon@bootlin.com> wrote:

> > >>> smaller than 16MB, doesn't mean it does not support 4B opcodes. We
> > >>> probably won't use the 4B opcodes in that case, but still.
> > >>>       
> > >>
> > >> I agree that manufacturers have a sense of humor and this might be possible. But
> > >> there's no need to use 4B opcodes in this case, so a post_sfdp fixup will help
> > >> here too.    
> > > 
> > > Except there's nothing to fix in this case, we just won't use 4B
> > > opcodes if we don't need to, that's all.    
> > 
> > you'll have an extra byte of address that has a tiny impact on performance on
> > small requests. I see it as a fix, we should do what's best to do. anyway ...  
> 
> No, see the checks that are done in this patch: to use 4B_CODES the
> flag should be set and the NOR should be larger than 16MB. The flag
> does not mean "use 4B opcodes", it meas "4B opMcodes are supported".

Maybe I should rename the flag SNOR_F_SUPPORTS_4B_OPCODES to make it
clear.
diff mbox series

Patch

diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c
index c1d9c2e50bee..98b8d9a778aa 100644
--- a/drivers/mtd/spi-nor/spi-nor.c
+++ b/drivers/mtd/spi-nor/spi-nor.c
@@ -3311,6 +3311,7 @@  static int spi_nor_init_params(struct spi_nor *nor,
 
 		if (spi_nor_parse_sfdp(nor, &sfdp_params)) {
 			nor->addr_width = 0;
+			nor->flags &= ~SNOR_F_4B_OPCODES;
 			/* restore previous erase map */
 			memcpy(&nor->erase_map, &prev_map,
 			       sizeof(nor->erase_map));
@@ -3561,9 +3562,7 @@  static int spi_nor_init(struct spi_nor *nor)
 		}
 	}
 
-	if ((nor->addr_width == 4) &&
-	    (JEDEC_MFR(nor->info) != SNOR_MFR_SPANSION) &&
-	    !(nor->info->flags & SPI_NOR_4B_OPCODES)) {
+	if (nor->addr_width == 4 && !(nor->flags & SNOR_F_4B_OPCODES)) {
 		/*
 		 * If the RESET# pin isn't hooked up properly, or the system
 		 * otherwise doesn't perform a reset command in the boot
@@ -3595,10 +3594,8 @@  static void spi_nor_resume(struct mtd_info *mtd)
 void spi_nor_restore(struct spi_nor *nor)
 {
 	/* restore the addressing mode */
-	if ((nor->addr_width == 4) &&
-	    (JEDEC_MFR(nor->info) != SNOR_MFR_SPANSION) &&
-	    !(nor->info->flags & SPI_NOR_4B_OPCODES) &&
-	    (nor->flags & SNOR_F_BROKEN_RESET))
+	if (nor->addr_width == 4 && !(nor->flags & SNOR_F_4B_OPCODES) &&
+	    nor->flags & SNOR_F_BROKEN_RESET)
 		set_4byte(nor, false);
 }
 EXPORT_SYMBOL_GPL(spi_nor_restore);
@@ -3750,6 +3747,10 @@  int spi_nor_scan(struct spi_nor *nor, const char *name,
 	if (info->flags & SPI_NOR_NO_FR)
 		params.hwcaps.mask &= ~SNOR_HWCAPS_READ_FAST;
 
+	if (info->flags & SPI_NOR_4B_OPCODES ||
+	    (JEDEC_MFR(info) == SNOR_MFR_SPANSION && mtd->size > SZ_16M))
+		nor->flags |= SNOR_F_4B_OPCODES;
+
 	/*
 	 * Configure the SPI memory:
 	 * - select op codes for (Fast) Read, Page Program and Sector Erase.
@@ -3768,13 +3769,14 @@  int spi_nor_scan(struct spi_nor *nor, const char *name,
 	} 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 ||
-		    info->flags & SPI_NOR_4B_OPCODES)
-			spi_nor_set_4byte_opcodes(nor);
 	} else {
 		nor->addr_width = 3;
 	}
 
+	if (nor->addr_width == 4 &&
+	    nor->flags & SNOR_F_4B_OPCODES)
+		spi_nor_set_4byte_opcodes(nor);
+
 	if (nor->addr_width > SPI_NOR_MAX_ADDR_WIDTH) {
 		dev_err(dev, "address width is too large: %u\n",
 			nor->addr_width);
diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h
index 8b1acf68b7ac..981d628305a2 100644
--- a/include/linux/mtd/spi-nor.h
+++ b/include/linux/mtd/spi-nor.h
@@ -237,6 +237,7 @@  enum spi_nor_option_flags {
 	SNOR_F_READY_XSR_RDY	= BIT(4),
 	SNOR_F_USE_CLSR		= BIT(5),
 	SNOR_F_BROKEN_RESET	= BIT(6),
+	SNOR_F_4B_OPCODES	= BIT(7),
 };
 
 /**