@@ -1411,9 +1411,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;
@@ -1421,7 +1426,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;
@@ -1500,6 +1505,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;
@@ -1534,6 +1540,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:
*
@@ -1551,13 +1561,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)
@@ -1580,6 +1590,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;
@@ -1614,6 +1625,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:
*
@@ -1633,14 +1647,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)
@@ -128,6 +128,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)
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@samsung.com> --- v5: - remake patch based on latest spi-nor/next tree drivers/mtd/spi-nor/spi-nor.c | 24 +++++++++++++++++++----- include/linux/mtd/spi-nor.h | 1 + 2 files changed, 20 insertions(+), 5 deletions(-)