]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
mtd: spi-nor: Support TB selection using SR bit 6
authorJungseung Lee <js07.lee@samsung.com>
Mon, 2 Dec 2019 06:35:06 +0000 (15:35 +0900)
committerTudor Ambarus <tudor.ambarus@microchip.com>
Mon, 23 Dec 2019 16:12:09 +0000 (18:12 +0200)
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: Jungseung Lee <js07.lee@samsung.com>
[tudor.ambarus@microchip.com: describe the reason for setting a
new flash_info flag.]
Signed-off-by: Tudor Ambarus <tudor.ambarus@microchip.com>
drivers/mtd/spi-nor/spi-nor.c
include/linux/mtd/spi-nor.h

index 887a9ad7519cb42d404919088fd01ec08954ef24..92b38157b38cd0718bbac7526a22b3730a97dd55 100644 (file)
@@ -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)
index 5124c306f60bb4021b9a24652a5c3da3812ac8d5..7e32adce72f77e2678bded88e224c9b6a4c28343 100644 (file)
@@ -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),
 
 };