
after writing new block protection bits, read back the block protection bit register and test whether the desired settings were accepted. fail with error message otherwise.
this adjusts the behavior of "sf protect (un)lock" for SST26* chips to be similar to the (un)locking behavior of Micron-compatible chips, where the statur register is read back and checked after writing it with the new block protection bit setting.
comparing the desired and current values fails if hardware write protection is enabled (SRWD (Micron) or WPEN (SST26*) bit set), and the WP# pin is not asserted, as the respective registers are write-protected in that case.
Signed-off-by: Bernhard Kirchen bernhard.kirchen@mbconnectline.com ---
drivers/mtd/spi/spi-nor-core.c | 55 +++++++++++++++++++++++++++++----- 1 file changed, 48 insertions(+), 7 deletions(-)
diff --git a/drivers/mtd/spi/spi-nor-core.c b/drivers/mtd/spi/spi-nor-core.c index 10c01cf20e..b3873aaf6e 100644 --- a/drivers/mtd/spi/spi-nor-core.c +++ b/drivers/mtd/spi/spi-nor-core.c @@ -998,6 +998,47 @@ static bool sst26_process_bpr(u32 bpr_size, u8 *cmd, u32 bit, enum lock_ctl ctl) return false; }
+static int sst26_read_bpr(struct spi_nor *nor, u8 *buff, u32 *len) +{ + struct mtd_info *mtd = &nor->mtd; + u32 bpr_size; + int ret; + + bpr_size = 2 + (mtd->size / SZ_64K / 8); + + if (*len < bpr_size) { + dev_err(nor->dev, "block-protection register buffer too small\n"); + return -EINVAL; + } + + ret = nor->read_reg(nor, SPINOR_OP_READ_BPR, buff, bpr_size); + if (ret < 0) { + dev_err(nor->dev, "failed to read block-protection register\n"); + return ret; + } + + *len = bpr_size; + return 0; +} + +static int sst26_check_bpr(struct spi_nor *nor, u8 *expected_buf, u32 expected_len) +{ + u8 actual_buf[SST26_MAX_BPR_REG_LEN] = {}; + u32 actual_len = SST26_MAX_BPR_REG_LEN; + int ret; + + ret = sst26_read_bpr(nor, actual_buf, &actual_len); + if (ret < 0) + return ret; + + if (expected_len != actual_len || memcmp(expected_buf, actual_buf, actual_len)) { + dev_err(nor->dev, "device did not accept new block protection bits\n"); + return -EACCES; + } + + return 0; +} + static int sst26_hardware_write_protection(struct spi_nor *nor, bool lock) { int ret; @@ -1034,7 +1075,7 @@ static int sst26_hardware_write_protection(struct spi_nor *nor, bool lock) static int sst26_lock_ctl(struct spi_nor *nor, loff_t ofs, uint64_t len, enum lock_ctl ctl) { struct mtd_info *mtd = &nor->mtd; - u32 i, bpr_ptr, rptr_64k, lptr_64k, bpr_size; + u32 i, bpr_ptr, rptr_64k, lptr_64k, bpr_size = SST26_MAX_BPR_REG_LEN; bool lower_64k = false, upper_64k = false; u8 bpr_buff[SST26_MAX_BPR_REG_LEN] = {}; int ret; @@ -1057,13 +1098,9 @@ static int sst26_lock_ctl(struct spi_nor *nor, loff_t ofs, uint64_t len, enum lo mtd->size != SZ_8M) return -EINVAL;
- bpr_size = 2 + (mtd->size / SZ_64K / 8); - - ret = nor->read_reg(nor, SPINOR_OP_READ_BPR, bpr_buff, bpr_size); - if (ret < 0) { - dev_err(nor->dev, "fail to read block-protection register\n"); + ret = sst26_read_bpr(nor, bpr_buff, &bpr_size); + if (ret < 0) return ret; - }
rptr_64k = min_t(u32, ofs + len, mtd->size - SST26_BOUND_REG_SIZE); lptr_64k = max_t(u32, ofs, SST26_BOUND_REG_SIZE); @@ -1134,6 +1171,10 @@ static int sst26_lock_ctl(struct spi_nor *nor, loff_t ofs, uint64_t len, enum lo return ret; }
+ ret = sst26_check_bpr(nor, bpr_buff, bpr_size); + if (ret < 0) + return ret; + // enable hardware write protection iff any protection bit is set for (i = 0; i < bpr_size; ++i) if (bpr_buff[i] != 0)