There are flashes which support locking and are smaller than 32Mbit, for example the gd25q16. The formula in stm_lock() and stm_unlock() will give wrong results, because it assumes that there are no holes in the locking configuration and that the second to last BP configuration is always "protect half the flash". This is only true for flashes larger than or equal to 32MBit. Introduce a function which calculates the number of "unused" BP configuration slots and take them into account when calculating the BP bits. Signed-off-by: Michael Walle <michael@xxxxxxxx> --- Hi Jungseung and all, I've prototyped these functions in userspace. And just compile-tested it with linux. This should also fix the new 4th BP bit calculation if you adapt the masks. Maybe better description is needed and/or a rewrite of the stm_lock()/stm_unlock() functions. But hopefully this patch shows that we don't need two different formulas for 3 and 4 BP bits. drivers/mtd/spi-nor/spi-nor.c | 48 +++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c index ede7ae80ad92..3278708cc329 100644 --- a/drivers/mtd/spi-nor/spi-nor.c +++ b/drivers/mtd/spi-nor/spi-nor.c @@ -1761,6 +1761,49 @@ static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr) return ret; } +/* + * Return the number of "unused" slots for the BP bits. + * + * Sample table portion for 1MB flash (Winbond w25q80bv): + * + * BP2 | BP1 | BP0 | Prot Length | Protected Portion + * ----------------------------------------------------------- + * 0 | 0 | 0 | NONE | NONE + * 0 | 0 | 1 | 64 KB | Upper 1/16 + * 0 | 1 | 0 | 128 KB | Upper 1/8 + * 0 | 1 | 1 | 256 KB | Upper 1/4 + * 1 | 0 | 0 | 512 KB | Upper 1/2 + * 1 | 0 | 1 | 1 MB | ALL + * 1 | 1 | 0 | 1 MB | ALL + * 1 | 1 | 1 | 1 MB | ALL + * + * For the example above the function would return 2, because + * two slots (BP=6 and BP=7) are "unused". + */ +#define STM_LOCK_SECTOR_SIZE (64 * 1024) +static int stm_pow_offset(struct spi_nor *nor) +{ + struct mtd_info *mtd = &nor->mtd; + u8 mask = SR_BP2 | SR_BP1 | SR_BP0; + int shift = ffs(mask) - 1; + int free_slots; + + /* We have 2^3 slots available */ + free_slots = (mask >> shift) + 1; + + /* We need one for "protect none" and one for "protect all" */ + free_slots -= 2; + + /* + * And we need log2(flash_size / sector size) slots for in-between + * configurations. + */ + free_slots -= ilog2(mtd->size / STM_LOCK_SECTOR_SIZE); + + /* make sure we don't return negative values */ + return max(0, free_slots); +} + static void stm_get_locked_range(struct spi_nor *nor, u8 sr, loff_t *ofs, uint64_t *len) { @@ -1779,6 +1822,9 @@ static void stm_get_locked_range(struct spi_nor *nor, u8 sr, loff_t *ofs, *len = 0; } else { pow = ((sr & mask) ^ mask) >> shift; + pow -= stm_pow_offset(nor); + /* make sure we don't get negative */ + pow = max(0, pow); *len = mtd->size >> pow; if (nor->flags & SNOR_F_HAS_SR_TB && sr & tb_mask) *ofs = 0; @@ -1909,6 +1955,7 @@ static int stm_lock(struct spi_nor *nor, loff_t ofs, uint64_t len) * pow = ceil(log2(size / len)) = log2(size) - floor(log2(len)) */ pow = ilog2(mtd->size) - ilog2(lock_len); + pow += stm_pow_offset(nor); val = mask - (pow << shift); if (val & ~mask) return -EINVAL; @@ -1994,6 +2041,7 @@ static int stm_unlock(struct spi_nor *nor, loff_t ofs, uint64_t len) * pow = floor(log2(size / len)) = log2(size) - ceil(log2(len)) */ pow = ilog2(mtd->size) - order_base_2(lock_len); + pow += stm_pow_offset(nor); if (lock_len == 0) { val = 0; /* fully unlocked */ } else { -- 2.20.1 ______________________________________________________ Linux MTD discussion mailing list http://lists.infradead.org/mailman/listinfo/linux-mtd/