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.kirc...@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)
-- 
2.29.2

Reply via email to