Hi,

On 10-Jun-19 11:54 AM, tudor.amba...@microchip.com wrote:
> From: Tudor Ambarus <tudor.amba...@microchip.com>
> 
> SPI memory devices from different manufacturers have widely
> different configurations for Status, Control and Configuration
> registers. JEDEC 216C defines a new map for these common register
> bits and their functions, and describes how the individual bits may
> be accessed for a specific device. For the JEDEC 216B compliant
> flashes, we can partially deduce Status and Configuration registers
> functions by inspecting the 16th DWORD of BFPT. Older flashes that
> don't declare the SFDP tables (SPANSION FL512SAIFG1 311QQ063 A ©11
> SPANSION) let the software decide how to interact with these registers.
> 
> The commit dcb4b22eeaf4 ("spi-nor: s25fl512s supports region locking")
> uncovered a probe error for s25fl512s, when the QUAD bit CR[1] was set
> in the bootloader. When this bit is set, only the Write Register
> WRR command format with 16 data bits may be used, WRR with 8 bits
> is not recognized and hence the error when trying to clear the block
> protection bits.
> 

I see above text in s25fl512s datasheet[1] as well. So the patch is
indeed needed:

Reviewed-by: Vignesh Raghavendra <vigne...@ti.com>

Also, I was able to reproduce above scenario with TI's K2G EVM with
s25fl512s flash and can confirm that this patch fixes the issue:

Tested-by: Vignesh Raghavendra <vigne...@ti.com>

Regards
Vignesh

[1] https://www.cypress.com/file/177971/download 9.3.7 Write Registers
> Fix the above by using 16-bits WRR command when Quad bit is set.
> 
> Backward compatibility should be fine. The newly introduced
> spi_nor_spansion_clear_sr_bp() is tightly coupled with the
> spansion_quad_enable() function. Both assume that the Write Register
> with 16 bits, together with the Read Configuration Register (35h)
> instructions are supported.
> 
> Reported-by: Geert Uytterhoeven <ge...@linux-m68k.org>
> Signed-off-by: Tudor Ambarus <tudor.amba...@microchip.com>
> ---
> Geert, Jonas,
> 
> This patch is compile-tested only. I don't have the flash, I need your
> help for testing this.
> 
> Thanks,
> ta
> 
>  drivers/mtd/spi-nor/spi-nor.c | 116 
> ++++++++++++++++++++++++++++++++++++++----
>  include/linux/mtd/spi-nor.h   |   1 +
>  2 files changed, 106 insertions(+), 11 deletions(-)
> 
> diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c
> index c0a8837c0575..af9ac7f09cc2 100644
> --- a/drivers/mtd/spi-nor/spi-nor.c
> +++ b/drivers/mtd/spi-nor/spi-nor.c
> @@ -1636,6 +1636,92 @@ static int sr2_bit7_quad_enable(struct spi_nor *nor)
>       return 0;
>  }
>  
> +/**
> + * spi_nor_clear_sr_bp() - clear the Status Register Block Protection bits.
> + * @nor:        pointer to a 'struct spi_nor'
> + *
> + * Read-modify-write function that clears the Block Protection bits from the
> + * Status Register without affecting other bits.
> + *
> + * Return: 0 on success, -errno otherwise.
> + */
> +static int spi_nor_clear_sr_bp(struct spi_nor *nor)
> +{
> +     int ret;
> +     u8 mask = SR_BP2 | SR_BP1 | SR_BP0;
> +
> +     ret = read_sr(nor);
> +     if (ret < 0) {
> +             dev_err(nor->dev, "error while reading status register\n");
> +             return ret;
> +     }
> +
> +     write_enable(nor);
> +
> +     ret = write_sr(nor, ret & ~mask);
> +     if (ret) {
> +             dev_err(nor->dev, "write to status register failed\n");
> +             return ret;
> +     }
> +
> +     ret = spi_nor_wait_till_ready(nor);
> +     if (ret)
> +             dev_err(nor->dev, "timeout while writing status register\n");
> +     return ret;
> +}
> +
> +/**
> + * spi_nor_spansion_clear_sr_bp() - clear the Status Register Block 
> Protection
> + * bits on spansion flashes.
> + * @nor:        pointer to a 'struct spi_nor'
> + *
> + * Read-modify-write function that clears the Block Protection bits from the
> + * Status Register without affecting other bits. The function is tightly
> + * coupled with the spansion_quad_enable() function. Both assume that the 
> Write
> + * Register with 16 bits, together with the Read Configuration Register (35h)
> + * instructions are supported
> + *
> + * Return: 0 on success, -errno otherwise.
> + */
> +static int spi_nor_spansion_clear_sr_bp(struct spi_nor *nor)
> +{
> +     int ret;
> +     u8 mask = SR_BP2 | SR_BP1 | SR_BP0;
> +     u8 sr_cr[2] = {0};
> +
> +     /* Check current Quad Enable bit value. */
> +     ret = read_cr(nor);
> +     if (ret < 0) {
> +             dev_err(nor->dev,
> +                     "error while reading configuration register\n");
> +             return ret;
> +     }
> +
> +     /*
> +      * When the configuration register QUAD bit CR[1] is 1, only
> +      * the WRR command format with 16 data bits may be used.
> +      */
> +     if (ret & CR_QUAD_EN_SPAN) {
> +             sr_cr[1] = ret;
> +
> +             ret = read_sr(nor);
> +             if (ret < 0) {
> +                     dev_err(nor->dev,
> +                             "error while reading status register\n");
> +                     return ret;
> +             }
> +             sr_cr[0] = ret & ~mask;
> +
> +             ret = write_sr_cr(nor, sr_cr);
> +             if (ret)
> +                     dev_err(nor->dev, "16-bit write register failed\n");
> +             return ret;
> +     }
> +
> +     /* If quad bit is not set, use 8-bit WRR command. */
> +     return spi_nor_clear_sr_bp(nor);
> +}
> +
>  /* Used when the "_ext_id" is two bytes at most */
>  #define INFO(_jedec_id, _ext_id, _sector_size, _n_sectors, _flags)   \
>               .id = {                                                 \
> @@ -3663,6 +3749,8 @@ static int spi_nor_init_params(struct spi_nor *nor,
>               default:
>                       /* Kept only for backward compatibility purpose. */
>                       params->quad_enable = spansion_quad_enable;
> +                     if (nor->clear_sr_bp)
> +                             nor->clear_sr_bp = spi_nor_spansion_clear_sr_bp;
>                       break;
>               }
>  
> @@ -3915,17 +4003,13 @@ static int spi_nor_init(struct spi_nor *nor)
>  {
>       int err;
>  
> -     /*
> -      * Atmel, SST, Intel/Numonyx, and others serial NOR tend to power up
> -      * with the software protection bits set
> -      */
> -     if (JEDEC_MFR(nor->info) == SNOR_MFR_ATMEL ||
> -         JEDEC_MFR(nor->info) == SNOR_MFR_INTEL ||
> -         JEDEC_MFR(nor->info) == SNOR_MFR_SST ||
> -         nor->info->flags & SPI_NOR_HAS_LOCK) {
> -             write_enable(nor);
> -             write_sr(nor, 0);
> -             spi_nor_wait_till_ready(nor);
> +     if (nor->clear_sr_bp) {
> +             err = nor->clear_sr_bp(nor);
> +             if (err) {
> +                     dev_err(nor->dev,
> +                             "fail to clear block protection bits\n");
> +                     return err;
> +             }
>       }
>  
>       if (nor->quad_enable) {
> @@ -4050,6 +4134,16 @@ int spi_nor_scan(struct spi_nor *nor, const char *name,
>       if (info->flags & SPI_S3AN)
>               nor->flags |=  SNOR_F_READY_XSR_RDY;
>  
> +     /*
> +      * Atmel, SST, Intel/Numonyx, and others serial NOR tend to power up
> +      * with the software protection bits set.
> +      */
> +     if (JEDEC_MFR(nor->info) == SNOR_MFR_ATMEL ||
> +         JEDEC_MFR(nor->info) == SNOR_MFR_INTEL ||
> +         JEDEC_MFR(nor->info) == SNOR_MFR_SST ||
> +         nor->info->flags & SPI_NOR_HAS_LOCK)
> +             nor->clear_sr_bp = spi_nor_clear_sr_bp;
> +
>       /* Parse the Serial Flash Discoverable Parameters table. */
>       ret = spi_nor_init_params(nor, &params);
>       if (ret)
> diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h
> index b3d360b0ee3d..566bd5010bc8 100644
> --- a/include/linux/mtd/spi-nor.h
> +++ b/include/linux/mtd/spi-nor.h
> @@ -410,6 +410,7 @@ struct spi_nor {
>       int (*flash_unlock)(struct spi_nor *nor, loff_t ofs, uint64_t len);
>       int (*flash_is_locked)(struct spi_nor *nor, loff_t ofs, uint64_t len);
>       int (*quad_enable)(struct spi_nor *nor);
> +     int (*clear_sr_bp)(struct spi_nor *nor);
>  
>       void *priv;
>  };
> 

Reply via email to