Commit 191f5c2e authored by Tudor Ambarus's avatar Tudor Ambarus Committed by Miquel Raynal

mtd: spi-nor: use 16-bit WRR command when QE is set on spansion flashes

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 dcb4b22e ("spi-nor: s25fl512s supports region locking")
uncovered a probe error for s25fl512s, when the Quad Enable bit CR[1]
was set to one in the bootloader. When this bit is one, only the Write
Status (01h) command with two data byts may be used, the 01h command with
one data byte is not recognized and hence the error when trying to clear
the block protection bits.

Fix the above by using the Write Status (01h) command with two data bytes
when the Quad Enable bit is one.

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.

Fixes: dcb4b22e ("spi-nor: s25fl512s supports region locking")
Reported-by: default avatarGeert Uytterhoeven <geert@linux-m68k.org>
Signed-off-by: default avatarTudor Ambarus <tudor.ambarus@microchip.com>
Tested-by: default avatarJonas Bonn <jonas@norrbonn.se>
Tested-by: default avatarGeert Uytterhoeven <geert+renesas@glider.be>
Reviewed-by: default avatarVignesh Raghavendra <vigneshr@ti.com>
Tested-by: default avatarVignesh Raghavendra <vigneshr@ti.com>
Signed-off-by: default avatarMiquel Raynal <miquel.raynal@bootlin.com>
parent b2b5921f
...@@ -1636,6 +1636,95 @@ static int sr2_bit7_quad_enable(struct spi_nor *nor) ...@@ -1636,6 +1636,95 @@ static int sr2_bit7_quad_enable(struct spi_nor *nor)
return 0; 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 Enable bit is one, only the
* Write Status (01h) command with two data bytes 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 the Quad Enable bit is zero, use the Write Status (01h) command
* with one data byte.
*/
return spi_nor_clear_sr_bp(nor);
}
/* Used when the "_ext_id" is two bytes at most */ /* Used when the "_ext_id" is two bytes at most */
#define INFO(_jedec_id, _ext_id, _sector_size, _n_sectors, _flags) \ #define INFO(_jedec_id, _ext_id, _sector_size, _n_sectors, _flags) \
.id = { \ .id = { \
...@@ -3660,6 +3749,8 @@ static int spi_nor_init_params(struct spi_nor *nor, ...@@ -3660,6 +3749,8 @@ static int spi_nor_init_params(struct spi_nor *nor,
default: default:
/* Kept only for backward compatibility purpose. */ /* Kept only for backward compatibility purpose. */
params->quad_enable = spansion_quad_enable; params->quad_enable = spansion_quad_enable;
if (nor->clear_sr_bp)
nor->clear_sr_bp = spi_nor_spansion_clear_sr_bp;
break; break;
} }
...@@ -3912,17 +4003,13 @@ static int spi_nor_init(struct spi_nor *nor) ...@@ -3912,17 +4003,13 @@ static int spi_nor_init(struct spi_nor *nor)
{ {
int err; int err;
/* if (nor->clear_sr_bp) {
* Atmel, SST, Intel/Numonyx, and others serial NOR tend to power up err = nor->clear_sr_bp(nor);
* with the software protection bits set if (err) {
*/ dev_err(nor->dev,
if (JEDEC_MFR(nor->info) == SNOR_MFR_ATMEL || "fail to clear block protection bits\n");
JEDEC_MFR(nor->info) == SNOR_MFR_INTEL || return err;
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->quad_enable) { if (nor->quad_enable) {
...@@ -4047,6 +4134,16 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, ...@@ -4047,6 +4134,16 @@ int spi_nor_scan(struct spi_nor *nor, const char *name,
if (info->flags & SPI_S3AN) if (info->flags & SPI_S3AN)
nor->flags |= SNOR_F_READY_XSR_RDY; 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. */ /* Parse the Serial Flash Discoverable Parameters table. */
ret = spi_nor_init_params(nor, &params); ret = spi_nor_init_params(nor, &params);
if (ret) if (ret)
......
...@@ -373,6 +373,8 @@ struct flash_info; ...@@ -373,6 +373,8 @@ struct flash_info;
* @flash_unlock: [FLASH-SPECIFIC] unlock a region of the SPI NOR * @flash_unlock: [FLASH-SPECIFIC] unlock a region of the SPI NOR
* @flash_is_locked: [FLASH-SPECIFIC] check if a region of the SPI NOR is * @flash_is_locked: [FLASH-SPECIFIC] check if a region of the SPI NOR is
* @quad_enable: [FLASH-SPECIFIC] enables SPI NOR quad mode * @quad_enable: [FLASH-SPECIFIC] enables SPI NOR quad mode
* @clear_sr_bp: [FLASH-SPECIFIC] clears the Block Protection Bits from
* the SPI NOR Status Register.
* completely locked * completely locked
* @priv: the private data * @priv: the private data
*/ */
...@@ -410,6 +412,7 @@ struct spi_nor { ...@@ -410,6 +412,7 @@ struct spi_nor {
int (*flash_unlock)(struct spi_nor *nor, loff_t ofs, uint64_t len); 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 (*flash_is_locked)(struct spi_nor *nor, loff_t ofs, uint64_t len);
int (*quad_enable)(struct spi_nor *nor); int (*quad_enable)(struct spi_nor *nor);
int (*clear_sr_bp)(struct spi_nor *nor);
void *priv; void *priv;
}; };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment