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 {
u16 page_size;
u16 addr_width;
u16 flags;
u32 flags;
#define SECT_4K BIT(0) /* SPINOR_OP_BE_4K works uniformly */
#define SPI_NOR_NO_ERASE BIT(1) /* No erase command needed */
#define SST_WRITE BIT(2) /* use SST byte programming */
......@@ -233,6 +233,11 @@ struct flash_info {
#define SPI_NOR_SKIP_SFDP BIT(13) /* Skip parsing of SFDP tables */
#define USE_CLSR BIT(14) /* use CLSR command */
#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. */
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,
{
struct mtd_info *mtd = &nor->mtd;
u8 mask = SR_BP2 | SR_BP1 | SR_BP0;
u8 tb_mask = SR_TB_BIT5;
int shift = ffs(mask) - 1;
int pow;
if (nor->flags & SNOR_F_HAS_SR_TB_BIT6)
tb_mask = SR_TB_BIT6;
if (!(sr & mask)) {
/* No protection */
*ofs = 0;
......@@ -1771,7 +1780,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 & tb_mask)
*ofs = 0;
else
*ofs = mtd->size - *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;
int ret, status_old, status_new;
u8 mask = SR_BP2 | SR_BP1 | SR_BP0;
u8 tb_mask = 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;
......@@ -1886,6 +1896,9 @@ static int stm_lock(struct spi_nor *nor, loff_t ofs, uint64_t len)
else
lock_len = ofs + len;
if (nor->flags & SNOR_F_HAS_SR_TB_BIT6)
tb_mask = SR_TB_BIT6;
/*
* Need smallest pow such that:
*
......@@ -1903,13 +1916,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 & ~tb_mask) | val;
/* Disallow further writes if WP pin is asserted */
status_new |= SR_SRWD;
if (!use_top)
status_new |= SR_TB_BIT5;
status_new |= tb_mask;
/* Don't bother if they're the same */
if (status_new == status_old)
......@@ -1932,6 +1945,7 @@ static int stm_unlock(struct spi_nor *nor, loff_t ofs, uint64_t len)
struct mtd_info *mtd = &nor->mtd;
int ret, status_old, status_new;
u8 mask = SR_BP2 | SR_BP1 | SR_BP0;
u8 tb_mask = 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;
......@@ -1968,6 +1982,8 @@ static int stm_unlock(struct spi_nor *nor, loff_t ofs, uint64_t len)
else
lock_len = ofs;
if (nor->flags & SNOR_F_HAS_SR_TB_BIT6)
tb_mask = SR_TB_BIT6;
/*
* Need largest pow such that:
*
......@@ -1987,14 +2003,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 & ~tb_mask) | 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 |= tb_mask;
/* Don't bother if they're the same */
if (status_new == status_old)
......@@ -5144,8 +5160,12 @@ int spi_nor_scan(struct spi_nor *nor, const char *name,
if (info->flags & 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;
if (info->flags & SPI_NOR_TB_SR_BIT6)
nor->flags |= SNOR_F_HAS_SR_TB_BIT6;
}
if (info->flags & NO_CHIP_ERASE)
nor->flags |= SNOR_F_NO_OP_CHIP_ERASE;
if (info->flags & USE_CLSR)
......
......@@ -245,6 +245,7 @@ enum spi_nor_option_flags {
SNOR_F_HAS_LOCK = BIT(8),
SNOR_F_HAS_16BIT_SR = BIT(9),
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