For winbond and gigadevice flashes, wrong bit for top/bottom selection are being used. Fix it to use appropriate value. Signed-off-by: Jungseung Lee <js07.lee@xxxxxxxxxxx> --- drivers/mtd/spi-nor/spi-nor.c | 24 +++++++++++++++++++----- include/linux/mtd/spi-nor.h | 1 + 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c index 612a6f0a87c3..f522201d22a5 100644 --- a/drivers/mtd/spi-nor/spi-nor.c +++ b/drivers/mtd/spi-nor/spi-nor.c @@ -1089,9 +1089,14 @@ static void stm_get_locked_range(struct spi_nor *nor, u8 sr, loff_t *ofs, { struct mtd_info *mtd = &nor->mtd; u8 mask = SR_BP2 | SR_BP1 | SR_BP0; + u8 mask_tb = SR_TB_BIT5; int shift = ffs(mask) - 1; int pow; + if (JEDEC_MFR(nor->info) == SNOR_MFR_WINBOND || + JEDEC_MFR(nor->info) == SNOR_MFR_GIGADEVICE) + mask_tb = SR_TB_BIT6; + if (!(sr & mask)) { /* No protection */ *ofs = 0; @@ -1099,7 +1104,7 @@ static void stm_get_locked_range(struct spi_nor *nor, u8 sr, loff_t *ofs, } else { pow = ((sr & mask) ^ mask) >> shift; *len = mtd->size >> pow; - if (nor->flags & SNOR_F_HAS_SR_TB && sr & SR_TB_BIT5) + if (nor->flags & SNOR_F_HAS_SR_TB && sr & mask_tb) *ofs = 0; else *ofs = mtd->size - *len; @@ -1178,6 +1183,7 @@ static int stm_lock(struct spi_nor *nor, loff_t ofs, uint64_t len) struct mtd_info *mtd = &nor->mtd; int status_old, status_new; u8 mask = SR_BP2 | SR_BP1 | SR_BP0; + u8 mask_tb = SR_TB_BIT5; u8 shift = ffs(mask) - 1, pow, val; loff_t lock_len; bool can_be_top = true, can_be_bottom = nor->flags & SNOR_F_HAS_SR_TB; @@ -1212,6 +1218,10 @@ static int stm_lock(struct spi_nor *nor, loff_t ofs, uint64_t len) else lock_len = ofs + len; + if (JEDEC_MFR(nor->info) == SNOR_MFR_WINBOND || + JEDEC_MFR(nor->info) == SNOR_MFR_GIGADEVICE) + mask_tb = SR_TB_BIT6; + /* * Need smallest pow such that: * @@ -1229,13 +1239,13 @@ static int stm_lock(struct spi_nor *nor, loff_t ofs, uint64_t len) if (!(val & mask)) return -EINVAL; - status_new = (status_old & ~mask & ~SR_TB_BIT5) | val; + status_new = (status_old & ~mask & ~mask_tb) | val; /* Disallow further writes if WP pin is asserted */ status_new |= SR_SRWD; if (!use_top) - status_new |= SR_TB_BIT5; + status_new |= mask_tb; /* Don't bother if they're the same */ if (status_new == status_old) @@ -1258,6 +1268,7 @@ static int stm_unlock(struct spi_nor *nor, loff_t ofs, uint64_t len) struct mtd_info *mtd = &nor->mtd; int status_old, status_new; u8 mask = SR_BP2 | SR_BP1 | SR_BP0; + u8 mask_tb = SR_TB_BIT5; u8 shift = ffs(mask) - 1, pow, val; loff_t lock_len; bool can_be_top = true, can_be_bottom = nor->flags & SNOR_F_HAS_SR_TB; @@ -1292,6 +1303,9 @@ static int stm_unlock(struct spi_nor *nor, loff_t ofs, uint64_t len) else lock_len = ofs; + if (JEDEC_MFR(nor->info) == SNOR_MFR_WINBOND || + JEDEC_MFR(nor->info) == SNOR_MFR_GIGADEVICE) + mask_tb = SR_TB_BIT6; /* * Need largest pow such that: * @@ -1311,14 +1325,14 @@ static int stm_unlock(struct spi_nor *nor, loff_t ofs, uint64_t len) return -EINVAL; } - status_new = (status_old & ~mask & ~SR_TB_BIT5) | val; + status_new = (status_old & ~mask & ~mask_tb) | val; /* Don't protect status register if we're fully unlocked */ if (lock_len == 0) status_new &= ~SR_SRWD; if (!use_top) - status_new |= SR_TB_BIT5; + status_new |= mask_tb; /* Don't bother if they're the same */ if (status_new == status_old) diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h index 06de3e6e8d4e..67cdbed6611a 100644 --- a/include/linux/mtd/spi-nor.h +++ b/include/linux/mtd/spi-nor.h @@ -127,6 +127,7 @@ #define SR_BP1 BIT(3) /* Block protect 1 */ #define SR_BP2 BIT(4) /* Block protect 2 */ #define SR_TB_BIT5 BIT(5) /* Top/Bottom protect */ +#define SR_TB_BIT6 BIT(6) /* Top/Bottom protect (on Winbond/GigaDevice) */ #define SR_SRWD BIT(7) /* SR write protect */ /* Spansion/Cypress specific status bits */ #define SR_E_ERR BIT(5) -- 2.17.1 ______________________________________________________ Linux MTD discussion mailing list http://lists.infradead.org/mailman/listinfo/linux-mtd/