Commit adf1092f authored by Jungseung Lee's avatar Jungseung Lee Committed by Tudor Ambarus

mtd: spi-nor: Support TB selection using SR bit 6

There are some flashes to use bit 6 of status register for Top/Bottom (TB).
Use top/bottom bit variable instead of fixed value and support this case.

Set the Top/Bottom (TB) mask based on SPI_NOR_TB_SR_BIT6 flash_info flag.
We can't use a bigger granularity, for example to set TB_BIT6 per
manufacturer using a SNOR_F flag. The manufacturers don't have a common
rule in regards to the TB bit:

Winbond : Use the 6th bit from 32MB capacity
W25Q20EW, W25Q50BW, W25Q128V - TB(5)
W25Q256JV, W25M512JV - TB(6)

GigaDevice : Use the 6th bit from 32MB capacity
GD25Q16C, GD25Q32C, GD25LQ32D, GD25Q64C, GD25Q128 - TB(5)
GD25Q256 - TB(6)

Micron/STM : Keep to use 5th bit
M25PX64, N25Q128A, N25Q512A, MT25QL512ABB, MT25QL02GCBB - TB(5)

Spansion : Use the 6th bit from 16MB capacity
S25FL116K, S25FL132K, S25FL165K - TB(5)
S25FL128L, S25FL256L - TB(6)

We can't make a correlation between TB and BP3 either, i.e. assume that if
BP3 is defined then TB will be at BIT(6). Micron breaks this rule.
Signed-off-by: default avatarJungseung Lee <js07.lee@samsung.com>
[tudor.ambarus@microchip.com: describe the reason for setting a
new flash_info flag.]
Signed-off-by: default avatarTudor Ambarus <tudor.ambarus@microchip.com>
parent 52487e21
...@@ -196,7 +196,7 @@ struct flash_info { ...@@ -196,7 +196,7 @@ struct flash_info {
u16 page_size; u16 page_size;
u16 addr_width; u16 addr_width;
u16 flags; u32 flags;
#define SECT_4K BIT(0) /* SPINOR_OP_BE_4K works uniformly */ #define SECT_4K BIT(0) /* SPINOR_OP_BE_4K works uniformly */
#define SPI_NOR_NO_ERASE BIT(1) /* No erase command needed */ #define SPI_NOR_NO_ERASE BIT(1) /* No erase command needed */
#define SST_WRITE BIT(2) /* use SST byte programming */ #define SST_WRITE BIT(2) /* use SST byte programming */
...@@ -233,6 +233,11 @@ struct flash_info { ...@@ -233,6 +233,11 @@ struct flash_info {
#define SPI_NOR_SKIP_SFDP BIT(13) /* Skip parsing of SFDP tables */ #define SPI_NOR_SKIP_SFDP BIT(13) /* Skip parsing of SFDP tables */
#define USE_CLSR BIT(14) /* use CLSR command */ #define USE_CLSR BIT(14) /* use CLSR command */
#define SPI_NOR_OCTAL_READ BIT(15) /* Flash supports Octal Read */ #define SPI_NOR_OCTAL_READ BIT(15) /* Flash supports Octal Read */
#define SPI_NOR_TB_SR_BIT6 BIT(16) /*
* Top/Bottom (TB) is bit 6 of
* status register. Must be used with
* SPI_NOR_HAS_TB.
*/
/* Part specific fixup hooks. */ /* Part specific fixup hooks. */
const struct spi_nor_fixups *fixups; const struct spi_nor_fixups *fixups;
...@@ -1761,9 +1766,13 @@ static void stm_get_locked_range(struct spi_nor *nor, u8 sr, loff_t *ofs, ...@@ -1761,9 +1766,13 @@ static void stm_get_locked_range(struct spi_nor *nor, u8 sr, loff_t *ofs,
{ {
struct mtd_info *mtd = &nor->mtd; struct mtd_info *mtd = &nor->mtd;
u8 mask = SR_BP2 | SR_BP1 | SR_BP0; u8 mask = SR_BP2 | SR_BP1 | SR_BP0;
u8 tb_mask = SR_TB_BIT5;
int shift = ffs(mask) - 1; int shift = ffs(mask) - 1;
int pow; int pow;
if (nor->flags & SNOR_F_HAS_SR_TB_BIT6)
tb_mask = SR_TB_BIT6;
if (!(sr & mask)) { if (!(sr & mask)) {
/* No protection */ /* No protection */
*ofs = 0; *ofs = 0;
...@@ -1771,7 +1780,7 @@ static void stm_get_locked_range(struct spi_nor *nor, u8 sr, loff_t *ofs, ...@@ -1771,7 +1780,7 @@ static void stm_get_locked_range(struct spi_nor *nor, u8 sr, loff_t *ofs,
} else { } else {
pow = ((sr & mask) ^ mask) >> shift; pow = ((sr & mask) ^ mask) >> shift;
*len = mtd->size >> pow; *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 & tb_mask)
*ofs = 0; *ofs = 0;
else else
*ofs = mtd->size - *len; *ofs = mtd->size - *len;
...@@ -1850,6 +1859,7 @@ static int stm_lock(struct spi_nor *nor, loff_t ofs, uint64_t len) ...@@ -1850,6 +1859,7 @@ static int stm_lock(struct spi_nor *nor, loff_t ofs, uint64_t len)
struct mtd_info *mtd = &nor->mtd; struct mtd_info *mtd = &nor->mtd;
int ret, status_old, status_new; int ret, status_old, status_new;
u8 mask = SR_BP2 | SR_BP1 | SR_BP0; u8 mask = SR_BP2 | SR_BP1 | SR_BP0;
u8 tb_mask = SR_TB_BIT5;
u8 shift = ffs(mask) - 1, pow, val; u8 shift = ffs(mask) - 1, pow, val;
loff_t lock_len; loff_t lock_len;
bool can_be_top = true, can_be_bottom = nor->flags & SNOR_F_HAS_SR_TB; bool can_be_top = true, can_be_bottom = nor->flags & SNOR_F_HAS_SR_TB;
...@@ -1886,6 +1896,9 @@ static int stm_lock(struct spi_nor *nor, loff_t ofs, uint64_t len) ...@@ -1886,6 +1896,9 @@ static int stm_lock(struct spi_nor *nor, loff_t ofs, uint64_t len)
else else
lock_len = ofs + len; lock_len = ofs + len;
if (nor->flags & SNOR_F_HAS_SR_TB_BIT6)
tb_mask = SR_TB_BIT6;
/* /*
* Need smallest pow such that: * Need smallest pow such that:
* *
...@@ -1903,13 +1916,13 @@ static int stm_lock(struct spi_nor *nor, loff_t ofs, uint64_t len) ...@@ -1903,13 +1916,13 @@ static int stm_lock(struct spi_nor *nor, loff_t ofs, uint64_t len)
if (!(val & mask)) if (!(val & mask))
return -EINVAL; return -EINVAL;
status_new = (status_old & ~mask & ~SR_TB_BIT5) | val; status_new = (status_old & ~mask & ~tb_mask) | val;
/* Disallow further writes if WP pin is asserted */ /* Disallow further writes if WP pin is asserted */
status_new |= SR_SRWD; status_new |= SR_SRWD;
if (!use_top) if (!use_top)
status_new |= SR_TB_BIT5; status_new |= tb_mask;
/* Don't bother if they're the same */ /* Don't bother if they're the same */
if (status_new == status_old) if (status_new == status_old)
...@@ -1932,6 +1945,7 @@ static int stm_unlock(struct spi_nor *nor, loff_t ofs, uint64_t len) ...@@ -1932,6 +1945,7 @@ static int stm_unlock(struct spi_nor *nor, loff_t ofs, uint64_t len)
struct mtd_info *mtd = &nor->mtd; struct mtd_info *mtd = &nor->mtd;
int ret, status_old, status_new; int ret, status_old, status_new;
u8 mask = SR_BP2 | SR_BP1 | SR_BP0; u8 mask = SR_BP2 | SR_BP1 | SR_BP0;
u8 tb_mask = SR_TB_BIT5;
u8 shift = ffs(mask) - 1, pow, val; u8 shift = ffs(mask) - 1, pow, val;
loff_t lock_len; loff_t lock_len;
bool can_be_top = true, can_be_bottom = nor->flags & SNOR_F_HAS_SR_TB; bool can_be_top = true, can_be_bottom = nor->flags & SNOR_F_HAS_SR_TB;
...@@ -1968,6 +1982,8 @@ static int stm_unlock(struct spi_nor *nor, loff_t ofs, uint64_t len) ...@@ -1968,6 +1982,8 @@ static int stm_unlock(struct spi_nor *nor, loff_t ofs, uint64_t len)
else else
lock_len = ofs; lock_len = ofs;
if (nor->flags & SNOR_F_HAS_SR_TB_BIT6)
tb_mask = SR_TB_BIT6;
/* /*
* Need largest pow such that: * Need largest pow such that:
* *
...@@ -1987,14 +2003,14 @@ static int stm_unlock(struct spi_nor *nor, loff_t ofs, uint64_t len) ...@@ -1987,14 +2003,14 @@ static int stm_unlock(struct spi_nor *nor, loff_t ofs, uint64_t len)
return -EINVAL; return -EINVAL;
} }
status_new = (status_old & ~mask & ~SR_TB_BIT5) | val; status_new = (status_old & ~mask & ~tb_mask) | val;
/* Don't protect status register if we're fully unlocked */ /* Don't protect status register if we're fully unlocked */
if (lock_len == 0) if (lock_len == 0)
status_new &= ~SR_SRWD; status_new &= ~SR_SRWD;
if (!use_top) if (!use_top)
status_new |= SR_TB_BIT5; status_new |= tb_mask;
/* Don't bother if they're the same */ /* Don't bother if they're the same */
if (status_new == status_old) if (status_new == status_old)
...@@ -5144,8 +5160,12 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, ...@@ -5144,8 +5160,12 @@ int spi_nor_scan(struct spi_nor *nor, const char *name,
if (info->flags & USE_FSR) if (info->flags & USE_FSR)
nor->flags |= SNOR_F_USE_FSR; nor->flags |= SNOR_F_USE_FSR;
if (info->flags & SPI_NOR_HAS_TB) if (info->flags & SPI_NOR_HAS_TB) {
nor->flags |= SNOR_F_HAS_SR_TB; nor->flags |= SNOR_F_HAS_SR_TB;
if (info->flags & SPI_NOR_TB_SR_BIT6)
nor->flags |= SNOR_F_HAS_SR_TB_BIT6;
}
if (info->flags & NO_CHIP_ERASE) if (info->flags & NO_CHIP_ERASE)
nor->flags |= SNOR_F_NO_OP_CHIP_ERASE; nor->flags |= SNOR_F_NO_OP_CHIP_ERASE;
if (info->flags & USE_CLSR) if (info->flags & USE_CLSR)
......
...@@ -245,6 +245,7 @@ enum spi_nor_option_flags { ...@@ -245,6 +245,7 @@ enum spi_nor_option_flags {
SNOR_F_HAS_LOCK = BIT(8), SNOR_F_HAS_LOCK = BIT(8),
SNOR_F_HAS_16BIT_SR = BIT(9), SNOR_F_HAS_16BIT_SR = BIT(9),
SNOR_F_NO_READ_CR = BIT(10), SNOR_F_NO_READ_CR = BIT(10),
SNOR_F_HAS_SR_TB_BIT6 = BIT(11),
}; };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment