Hi, On 10-Jun-19 11:54 AM, Tudor.Ambarus@xxxxxxxxxxxxx wrote: > From: Tudor Ambarus <tudor.ambarus@xxxxxxxxxxxxx> > > 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 <vigneshr@xxxxxx> 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 <vigneshr@xxxxxx> 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 <geert@xxxxxxxxxxxxxx> > Signed-off-by: Tudor Ambarus <tudor.ambarus@xxxxxxxxxxxxx> > --- > 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, ¶ms); > 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; > }; > ______________________________________________________ Linux MTD discussion mailing list http://lists.infradead.org/mailman/listinfo/linux-mtd/