Skip to content

Commit 191f5c2

Browse files
ambarusmiquelraynal
authored andcommitted
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 dcb4b22 ("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: dcb4b22 ("spi-nor: s25fl512s supports region locking") Reported-by: Geert Uytterhoeven <geert@linux-m68k.org> Signed-off-by: Tudor Ambarus <tudor.ambarus@microchip.com> Tested-by: Jonas Bonn <jonas@norrbonn.se> Tested-by: Geert Uytterhoeven <geert+renesas@glider.be> Reviewed-by: Vignesh Raghavendra <vigneshr@ti.com> Tested-by: Vignesh Raghavendra <vigneshr@ti.com> Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
1 parent b2b5921 commit 191f5c2

File tree

2 files changed

+111
-11
lines changed

2 files changed

+111
-11
lines changed

drivers/mtd/spi-nor/spi-nor.c

Lines changed: 108 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1636,6 +1636,95 @@ static int sr2_bit7_quad_enable(struct spi_nor *nor)
16361636
return 0;
16371637
}
16381638

1639+
/**
1640+
* spi_nor_clear_sr_bp() - clear the Status Register Block Protection bits.
1641+
* @nor: pointer to a 'struct spi_nor'
1642+
*
1643+
* Read-modify-write function that clears the Block Protection bits from the
1644+
* Status Register without affecting other bits.
1645+
*
1646+
* Return: 0 on success, -errno otherwise.
1647+
*/
1648+
static int spi_nor_clear_sr_bp(struct spi_nor *nor)
1649+
{
1650+
int ret;
1651+
u8 mask = SR_BP2 | SR_BP1 | SR_BP0;
1652+
1653+
ret = read_sr(nor);
1654+
if (ret < 0) {
1655+
dev_err(nor->dev, "error while reading status register\n");
1656+
return ret;
1657+
}
1658+
1659+
write_enable(nor);
1660+
1661+
ret = write_sr(nor, ret & ~mask);
1662+
if (ret) {
1663+
dev_err(nor->dev, "write to status register failed\n");
1664+
return ret;
1665+
}
1666+
1667+
ret = spi_nor_wait_till_ready(nor);
1668+
if (ret)
1669+
dev_err(nor->dev, "timeout while writing status register\n");
1670+
return ret;
1671+
}
1672+
1673+
/**
1674+
* spi_nor_spansion_clear_sr_bp() - clear the Status Register Block Protection
1675+
* bits on spansion flashes.
1676+
* @nor: pointer to a 'struct spi_nor'
1677+
*
1678+
* Read-modify-write function that clears the Block Protection bits from the
1679+
* Status Register without affecting other bits. The function is tightly
1680+
* coupled with the spansion_quad_enable() function. Both assume that the Write
1681+
* Register with 16 bits, together with the Read Configuration Register (35h)
1682+
* instructions are supported.
1683+
*
1684+
* Return: 0 on success, -errno otherwise.
1685+
*/
1686+
static int spi_nor_spansion_clear_sr_bp(struct spi_nor *nor)
1687+
{
1688+
int ret;
1689+
u8 mask = SR_BP2 | SR_BP1 | SR_BP0;
1690+
u8 sr_cr[2] = {0};
1691+
1692+
/* Check current Quad Enable bit value. */
1693+
ret = read_cr(nor);
1694+
if (ret < 0) {
1695+
dev_err(nor->dev,
1696+
"error while reading configuration register\n");
1697+
return ret;
1698+
}
1699+
1700+
/*
1701+
* When the configuration register Quad Enable bit is one, only the
1702+
* Write Status (01h) command with two data bytes may be used.
1703+
*/
1704+
if (ret & CR_QUAD_EN_SPAN) {
1705+
sr_cr[1] = ret;
1706+
1707+
ret = read_sr(nor);
1708+
if (ret < 0) {
1709+
dev_err(nor->dev,
1710+
"error while reading status register\n");
1711+
return ret;
1712+
}
1713+
sr_cr[0] = ret & ~mask;
1714+
1715+
ret = write_sr_cr(nor, sr_cr);
1716+
if (ret)
1717+
dev_err(nor->dev, "16-bit write register failed\n");
1718+
return ret;
1719+
}
1720+
1721+
/*
1722+
* If the Quad Enable bit is zero, use the Write Status (01h) command
1723+
* with one data byte.
1724+
*/
1725+
return spi_nor_clear_sr_bp(nor);
1726+
}
1727+
16391728
/* Used when the "_ext_id" is two bytes at most */
16401729
#define INFO(_jedec_id, _ext_id, _sector_size, _n_sectors, _flags) \
16411730
.id = { \
@@ -3660,6 +3749,8 @@ static int spi_nor_init_params(struct spi_nor *nor,
36603749
default:
36613750
/* Kept only for backward compatibility purpose. */
36623751
params->quad_enable = spansion_quad_enable;
3752+
if (nor->clear_sr_bp)
3753+
nor->clear_sr_bp = spi_nor_spansion_clear_sr_bp;
36633754
break;
36643755
}
36653756

@@ -3912,17 +4003,13 @@ static int spi_nor_init(struct spi_nor *nor)
39124003
{
39134004
int err;
39144005

3915-
/*
3916-
* Atmel, SST, Intel/Numonyx, and others serial NOR tend to power up
3917-
* with the software protection bits set
3918-
*/
3919-
if (JEDEC_MFR(nor->info) == SNOR_MFR_ATMEL ||
3920-
JEDEC_MFR(nor->info) == SNOR_MFR_INTEL ||
3921-
JEDEC_MFR(nor->info) == SNOR_MFR_SST ||
3922-
nor->info->flags & SPI_NOR_HAS_LOCK) {
3923-
write_enable(nor);
3924-
write_sr(nor, 0);
3925-
spi_nor_wait_till_ready(nor);
4006+
if (nor->clear_sr_bp) {
4007+
err = nor->clear_sr_bp(nor);
4008+
if (err) {
4009+
dev_err(nor->dev,
4010+
"fail to clear block protection bits\n");
4011+
return err;
4012+
}
39264013
}
39274014

39284015
if (nor->quad_enable) {
@@ -4047,6 +4134,16 @@ int spi_nor_scan(struct spi_nor *nor, const char *name,
40474134
if (info->flags & SPI_S3AN)
40484135
nor->flags |= SNOR_F_READY_XSR_RDY;
40494136

4137+
/*
4138+
* Atmel, SST, Intel/Numonyx, and others serial NOR tend to power up
4139+
* with the software protection bits set.
4140+
*/
4141+
if (JEDEC_MFR(nor->info) == SNOR_MFR_ATMEL ||
4142+
JEDEC_MFR(nor->info) == SNOR_MFR_INTEL ||
4143+
JEDEC_MFR(nor->info) == SNOR_MFR_SST ||
4144+
nor->info->flags & SPI_NOR_HAS_LOCK)
4145+
nor->clear_sr_bp = spi_nor_clear_sr_bp;
4146+
40504147
/* Parse the Serial Flash Discoverable Parameters table. */
40514148
ret = spi_nor_init_params(nor, &params);
40524149
if (ret)

include/linux/mtd/spi-nor.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -373,6 +373,8 @@ struct flash_info;
373373
* @flash_unlock: [FLASH-SPECIFIC] unlock a region of the SPI NOR
374374
* @flash_is_locked: [FLASH-SPECIFIC] check if a region of the SPI NOR is
375375
* @quad_enable: [FLASH-SPECIFIC] enables SPI NOR quad mode
376+
* @clear_sr_bp: [FLASH-SPECIFIC] clears the Block Protection Bits from
377+
* the SPI NOR Status Register.
376378
* completely locked
377379
* @priv: the private data
378380
*/
@@ -410,6 +412,7 @@ struct spi_nor {
410412
int (*flash_unlock)(struct spi_nor *nor, loff_t ofs, uint64_t len);
411413
int (*flash_is_locked)(struct spi_nor *nor, loff_t ofs, uint64_t len);
412414
int (*quad_enable)(struct spi_nor *nor);
415+
int (*clear_sr_bp)(struct spi_nor *nor);
413416

414417
void *priv;
415418
};

0 commit comments

Comments
 (0)