Skip to content

Commit 2d28476

Browse files
Jungseung-Leeambarus
authored andcommitted
mtd: spi-nor: Add generic formula for SR block protection handling
The current mainline locking was restricted and could only be applied to flashes that have 3 block protection bits and fixed locking ratio. A new method of normalization was reached at the end of the discussion [1]. (1) - if bp slot is insufficient. (2) - if bp slot is sufficient. if (bp_slots_needed > bp_slots) // (1) min_prot_length = sector_size << (bp_slots_needed - bp_slots); else // (2) min_prot_length = sector_size; This patch changes logic to handle block protection based on min_prot_length. It is suitable for the overall flashes with exception of some corner cases (see EON and catalyst) and easy to extend and apply for the case of 2bit or 4bit block protection. [1] http://lists.infradead.org/pipermail/linux-mtd/2020-February/093934.html 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 b0e2d25 commit 2d28476

File tree

1 file changed

+41
-31
lines changed

1 file changed

+41
-31
lines changed

drivers/mtd/spi-nor/core.c

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

1539+
static u64 spi_nor_get_min_prot_length_sr(struct spi_nor *nor)
1540+
{
1541+
unsigned int bp_slots, bp_slots_needed;
1542+
u8 mask = SR_BP2 | SR_BP1 | SR_BP0;
1543+
1544+
/* Reserved one for "protect none" and one for "protect all". */
1545+
bp_slots = (mask >> SR_BP_SHIFT) + 1 - 2;
1546+
bp_slots_needed = ilog2(nor->info->n_sectors);
1547+
1548+
if (bp_slots_needed > bp_slots)
1549+
return nor->info->sector_size <<
1550+
(bp_slots_needed - bp_slots);
1551+
else
1552+
return nor->info->sector_size;
1553+
}
1554+
15391555
static void spi_nor_get_locked_range_sr(struct spi_nor *nor, u8 sr, loff_t *ofs,
15401556
uint64_t *len)
15411557
{
15421558
struct mtd_info *mtd = &nor->mtd;
1559+
u64 min_prot_len;
15431560
u8 mask = SR_BP2 | SR_BP1 | SR_BP0;
15441561
u8 tb_mask = SR_TB_BIT5;
1545-
int pow;
1562+
u8 bp = (sr & mask) >> SR_BP_SHIFT;
15461563

15471564
if (nor->flags & SNOR_F_HAS_SR_TB_BIT6)
15481565
tb_mask = SR_TB_BIT6;
15491566

1550-
if (!(sr & mask)) {
1567+
if (!bp) {
15511568
/* No protection */
15521569
*ofs = 0;
15531570
*len = 0;
1554-
} else {
1555-
pow = ((sr & mask) ^ mask) >> SR_BP_SHIFT;
1556-
*len = mtd->size >> pow;
1557-
if (nor->flags & SNOR_F_HAS_SR_TB && sr & tb_mask)
1558-
*ofs = 0;
1559-
else
1560-
*ofs = mtd->size - *len;
1571+
return;
15611572
}
1573+
1574+
min_prot_len = spi_nor_get_min_prot_length_sr(nor);
1575+
*len = min_prot_len << (bp - 1);
1576+
1577+
if (*len > mtd->size)
1578+
*len = mtd->size;
1579+
1580+
if (nor->flags & SNOR_F_HAS_SR_TB && sr & tb_mask)
1581+
*ofs = 0;
1582+
else
1583+
*ofs = mtd->size - *len;
15621584
}
15631585

15641586
/*
@@ -1631,6 +1653,7 @@ static int spi_nor_is_unlocked_sr(struct spi_nor *nor, loff_t ofs, uint64_t len,
16311653
static int spi_nor_sr_lock(struct spi_nor *nor, loff_t ofs, uint64_t len)
16321654
{
16331655
struct mtd_info *mtd = &nor->mtd;
1656+
u64 min_prot_len;
16341657
int ret, status_old, status_new;
16351658
u8 mask = SR_BP2 | SR_BP1 | SR_BP0;
16361659
u8 tb_mask = SR_TB_BIT5;
@@ -1673,20 +1696,12 @@ static int spi_nor_sr_lock(struct spi_nor *nor, loff_t ofs, uint64_t len)
16731696
if (nor->flags & SNOR_F_HAS_SR_TB_BIT6)
16741697
tb_mask = SR_TB_BIT6;
16751698

1676-
/*
1677-
* Need smallest pow such that:
1678-
*
1679-
* 1 / (2^pow) <= (len / size)
1680-
*
1681-
* so (assuming power-of-2 size) we do:
1682-
*
1683-
* pow = ceil(log2(size / len)) = log2(size) - floor(log2(len))
1684-
*/
16851699
if (lock_len == mtd->size) {
16861700
val = mask;
16871701
} else {
1688-
pow = ilog2(mtd->size) - ilog2(lock_len);
1689-
val = mask - (pow << SR_BP_SHIFT);
1702+
min_prot_len = spi_nor_get_min_prot_length_sr(nor);
1703+
pow = ilog2(lock_len) - ilog2(min_prot_len) + 1;
1704+
val = pow << SR_BP_SHIFT;
16901705

16911706
if (val & ~mask)
16921707
return -EINVAL;
@@ -1723,6 +1738,7 @@ static int spi_nor_sr_lock(struct spi_nor *nor, loff_t ofs, uint64_t len)
17231738
static int spi_nor_sr_unlock(struct spi_nor *nor, loff_t ofs, uint64_t len)
17241739
{
17251740
struct mtd_info *mtd = &nor->mtd;
1741+
u64 min_prot_len;
17261742
int ret, status_old, status_new;
17271743
u8 mask = SR_BP2 | SR_BP1 | SR_BP0;
17281744
u8 tb_mask = SR_TB_BIT5;
@@ -1764,20 +1780,14 @@ static int spi_nor_sr_unlock(struct spi_nor *nor, loff_t ofs, uint64_t len)
17641780

17651781
if (nor->flags & SNOR_F_HAS_SR_TB_BIT6)
17661782
tb_mask = SR_TB_BIT6;
1767-
/*
1768-
* Need largest pow such that:
1769-
*
1770-
* 1 / (2^pow) >= (len / size)
1771-
*
1772-
* so (assuming power-of-2 size) we do:
1773-
*
1774-
* pow = floor(log2(size / len)) = log2(size) - ceil(log2(len))
1775-
*/
1776-
pow = ilog2(mtd->size) - order_base_2(lock_len);
1783+
17771784
if (lock_len == 0) {
17781785
val = 0; /* fully unlocked */
17791786
} else {
1780-
val = mask - (pow << SR_BP_SHIFT);
1787+
min_prot_len = spi_nor_get_min_prot_length_sr(nor);
1788+
pow = ilog2(lock_len) - ilog2(min_prot_len) + 1;
1789+
val = pow << SR_BP_SHIFT;
1790+
17811791
/* Some power-of-two sizes are not supported */
17821792
if (val & ~mask)
17831793
return -EINVAL;

0 commit comments

Comments
 (0)