Skip to content

Commit 05635c1

Browse files
Jungseung-Leeambarus
authored andcommitted
mtd: spi-nor: Add SR 4bit block protection support
Currently we are supporting block protection only for flash chips with 3 block protection bits (BP0-2) in the SR register. Enable block protection support for flashes with 4 block protection bits (BP0-3). Add a flash_info flag for flashes that describe 4 block protection bits. Add another flash_info flag for flashes in which BP3 bit is not adjacent to the BP0-2 bits. Tested with a n25q512ax3 (BP0-3) and w25q128 (BP0-2). Signed-off-by: Jungseung Lee <[email protected]> Reviewed-by: Michael Walle <[email protected]> Tested-by: Michael Walle <[email protected]> Signed-off-by: Tudor Ambarus <[email protected]>
1 parent 2d28476 commit 05635c1

File tree

3 files changed

+60
-18
lines changed

3 files changed

+60
-18
lines changed

drivers/mtd/spi-nor/core.c

Lines changed: 48 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1536,13 +1536,34 @@ static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr)
15361536
return ret;
15371537
}
15381538

1539+
static u8 spi_nor_get_sr_bp_mask(struct spi_nor *nor)
1540+
{
1541+
u8 mask = SR_BP2 | SR_BP1 | SR_BP0;
1542+
1543+
if (nor->flags & SNOR_F_HAS_SR_BP3_BIT6)
1544+
return mask | SR_BP3_BIT6;
1545+
1546+
if (nor->flags & SNOR_F_HAS_4BIT_BP)
1547+
return mask | SR_BP3;
1548+
1549+
return mask;
1550+
}
1551+
1552+
static u8 spi_nor_get_sr_tb_mask(struct spi_nor *nor)
1553+
{
1554+
if (nor->flags & SNOR_F_HAS_SR_TB_BIT6)
1555+
return SR_TB_BIT6;
1556+
else
1557+
return SR_TB_BIT5;
1558+
}
1559+
15391560
static u64 spi_nor_get_min_prot_length_sr(struct spi_nor *nor)
15401561
{
15411562
unsigned int bp_slots, bp_slots_needed;
1542-
u8 mask = SR_BP2 | SR_BP1 | SR_BP0;
1563+
u8 mask = spi_nor_get_sr_bp_mask(nor);
15431564

15441565
/* Reserved one for "protect none" and one for "protect all". */
1545-
bp_slots = (mask >> SR_BP_SHIFT) + 1 - 2;
1566+
bp_slots = (1 << hweight8(mask)) - 2;
15461567
bp_slots_needed = ilog2(nor->info->n_sectors);
15471568

15481569
if (bp_slots_needed > bp_slots)
@@ -1557,12 +1578,14 @@ static void spi_nor_get_locked_range_sr(struct spi_nor *nor, u8 sr, loff_t *ofs,
15571578
{
15581579
struct mtd_info *mtd = &nor->mtd;
15591580
u64 min_prot_len;
1560-
u8 mask = SR_BP2 | SR_BP1 | SR_BP0;
1561-
u8 tb_mask = SR_TB_BIT5;
1562-
u8 bp = (sr & mask) >> SR_BP_SHIFT;
1581+
u8 mask = spi_nor_get_sr_bp_mask(nor);
1582+
u8 tb_mask = spi_nor_get_sr_tb_mask(nor);
1583+
u8 bp, val = sr & mask;
15631584

1564-
if (nor->flags & SNOR_F_HAS_SR_TB_BIT6)
1565-
tb_mask = SR_TB_BIT6;
1585+
if (nor->flags & SNOR_F_HAS_SR_BP3_BIT6 && val & SR_BP3_BIT6)
1586+
val = (val & ~SR_BP3_BIT6) | SR_BP3;
1587+
1588+
bp = val >> SR_BP_SHIFT;
15661589

15671590
if (!bp) {
15681591
/* No protection */
@@ -1620,7 +1643,8 @@ static int spi_nor_is_unlocked_sr(struct spi_nor *nor, loff_t ofs, uint64_t len,
16201643

16211644
/*
16221645
* Lock a region of the flash. Compatible with ST Micro and similar flash.
1623-
* Supports the block protection bits BP{0,1,2} in the status register
1646+
* Supports the block protection bits BP{0,1,2}/BP{0,1,2,3} in the status
1647+
* register
16241648
* (SR). Does not support these features found in newer SR bitfields:
16251649
* - SEC: sector/block protect - only handle SEC=0 (block protect)
16261650
* - CMP: complement protect - only support CMP=0 (range is not complemented)
@@ -1655,8 +1679,8 @@ static int spi_nor_sr_lock(struct spi_nor *nor, loff_t ofs, uint64_t len)
16551679
struct mtd_info *mtd = &nor->mtd;
16561680
u64 min_prot_len;
16571681
int ret, status_old, status_new;
1658-
u8 mask = SR_BP2 | SR_BP1 | SR_BP0;
1659-
u8 tb_mask = SR_TB_BIT5;
1682+
u8 mask = spi_nor_get_sr_bp_mask(nor);
1683+
u8 tb_mask = spi_nor_get_sr_tb_mask(nor);
16601684
u8 pow, val;
16611685
loff_t lock_len;
16621686
bool can_be_top = true, can_be_bottom = nor->flags & SNOR_F_HAS_SR_TB;
@@ -1693,16 +1717,16 @@ static int spi_nor_sr_lock(struct spi_nor *nor, loff_t ofs, uint64_t len)
16931717
else
16941718
lock_len = ofs + len;
16951719

1696-
if (nor->flags & SNOR_F_HAS_SR_TB_BIT6)
1697-
tb_mask = SR_TB_BIT6;
1698-
16991720
if (lock_len == mtd->size) {
17001721
val = mask;
17011722
} else {
17021723
min_prot_len = spi_nor_get_min_prot_length_sr(nor);
17031724
pow = ilog2(lock_len) - ilog2(min_prot_len) + 1;
17041725
val = pow << SR_BP_SHIFT;
17051726

1727+
if (nor->flags & SNOR_F_HAS_SR_BP3_BIT6 && val & SR_BP3)
1728+
val = (val & ~SR_BP3) | SR_BP3_BIT6;
1729+
17061730
if (val & ~mask)
17071731
return -EINVAL;
17081732

@@ -1740,8 +1764,8 @@ static int spi_nor_sr_unlock(struct spi_nor *nor, loff_t ofs, uint64_t len)
17401764
struct mtd_info *mtd = &nor->mtd;
17411765
u64 min_prot_len;
17421766
int ret, status_old, status_new;
1743-
u8 mask = SR_BP2 | SR_BP1 | SR_BP0;
1744-
u8 tb_mask = SR_TB_BIT5;
1767+
u8 mask = spi_nor_get_sr_bp_mask(nor);
1768+
u8 tb_mask = spi_nor_get_sr_tb_mask(nor);
17451769
u8 pow, val;
17461770
loff_t lock_len;
17471771
bool can_be_top = true, can_be_bottom = nor->flags & SNOR_F_HAS_SR_TB;
@@ -1778,16 +1802,16 @@ static int spi_nor_sr_unlock(struct spi_nor *nor, loff_t ofs, uint64_t len)
17781802
else
17791803
lock_len = ofs;
17801804

1781-
if (nor->flags & SNOR_F_HAS_SR_TB_BIT6)
1782-
tb_mask = SR_TB_BIT6;
1783-
17841805
if (lock_len == 0) {
17851806
val = 0; /* fully unlocked */
17861807
} else {
17871808
min_prot_len = spi_nor_get_min_prot_length_sr(nor);
17881809
pow = ilog2(lock_len) - ilog2(min_prot_len) + 1;
17891810
val = pow << SR_BP_SHIFT;
17901811

1812+
if (nor->flags & SNOR_F_HAS_SR_BP3_BIT6 && val & SR_BP3)
1813+
val = (val & ~SR_BP3) | SR_BP3_BIT6;
1814+
17911815
/* Some power-of-two sizes are not supported */
17921816
if (val & ~mask)
17931817
return -EINVAL;
@@ -3147,6 +3171,12 @@ int spi_nor_scan(struct spi_nor *nor, const char *name,
31473171
if (info->flags & USE_CLSR)
31483172
nor->flags |= SNOR_F_USE_CLSR;
31493173

3174+
if (info->flags & SPI_NOR_4BIT_BP) {
3175+
nor->flags |= SNOR_F_HAS_4BIT_BP;
3176+
if (info->flags & SPI_NOR_BP3_SR_BIT6)
3177+
nor->flags |= SNOR_F_HAS_SR_BP3_BIT6;
3178+
}
3179+
31503180
if (info->flags & SPI_NOR_NO_ERASE)
31513181
mtd->flags |= MTD_NO_ERASE;
31523182

drivers/mtd/spi-nor/core.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ enum spi_nor_option_flags {
2424
SNOR_F_HAS_16BIT_SR = BIT(9),
2525
SNOR_F_NO_READ_CR = BIT(10),
2626
SNOR_F_HAS_SR_TB_BIT6 = BIT(11),
27+
SNOR_F_HAS_4BIT_BP = BIT(12),
28+
SNOR_F_HAS_SR_BP3_BIT6 = BIT(13),
2729
};
2830

2931
struct spi_nor_read_command {
@@ -301,6 +303,14 @@ struct flash_info {
301303
* status register. Must be used with
302304
* SPI_NOR_HAS_TB.
303305
*/
306+
#define SPI_NOR_4BIT_BP BIT(17) /*
307+
* Flash SR has 4 bit fields (BP0-3)
308+
* for block protection.
309+
*/
310+
#define SPI_NOR_BP3_SR_BIT6 BIT(18) /*
311+
* BP3 is bit 6 of status register.
312+
* Must be used with SPI_NOR_4BIT_BP.
313+
*/
304314

305315
/* Part specific fixup hooks. */
306316
const struct spi_nor_fixups *fixups;

include/linux/mtd/spi-nor.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,9 @@
111111
#define SR_BP0 BIT(2) /* Block protect 0 */
112112
#define SR_BP1 BIT(3) /* Block protect 1 */
113113
#define SR_BP2 BIT(4) /* Block protect 2 */
114+
#define SR_BP3 BIT(5) /* Block protect 3 */
114115
#define SR_TB_BIT5 BIT(5) /* Top/Bottom protect */
116+
#define SR_BP3_BIT6 BIT(6) /* Block protect 3 */
115117
#define SR_TB_BIT6 BIT(6) /* Top/Bottom protect */
116118
#define SR_SRWD BIT(7) /* SR write protect */
117119
/* Spansion/Cypress specific status bits */

0 commit comments

Comments
 (0)