]> git.dujemihanovic.xyz Git - u-boot.git/commitdiff
mtd: spi-nor: Clear Winbond SR3 WPS bit on boot
authorMarek Vasut <marex@denx.de>
Mon, 4 Mar 2024 16:16:05 +0000 (17:16 +0100)
committerTom Rini <trini@konsulko.com>
Thu, 10 Oct 2024 14:10:12 +0000 (08:10 -0600)
Some Winbond SPI NORs have special SR3 register which is
used among other things to control whether non-standard
"Individual Block/Sector Write Protection" (WPS bit)
locking scheme is activated. This non-standard locking
scheme is not supported by either U-Boot or Linux SPI
NOR stack so make sure it is disabled, otherwise the
SPI NOR may appear locked for no obvious reason.

This SR3 WPS appears e.g. on W25Q16FW which has the same ID as
W25Q16DW, but the W25Q16DW does not implement the SR3 WPS bit.

Signed-off-by: Marek Vasut <marex@denx.de>
drivers/mtd/spi/spi-nor-core.c
include/linux/mtd/spi-nor.h

index 032e429d3fa2c1f2dc23dfae9a20b79c2469acb0..24c6c31c043e15a5f1a92120544547e9c314e1e7 100644 (file)
@@ -595,6 +595,24 @@ static int read_cr(struct spi_nor *nor)
 }
 #endif
 
+/**
+ * read_sr3() - Read status register 3 unique to newer Winbond flashes
+ * @nor:       pointer to a 'struct spi_nor'
+ */
+static int read_sr3(struct spi_nor *nor)
+{
+       int ret;
+       u8 val;
+
+       ret = nor->read_reg(nor, SPINOR_OP_RDSR3, &val, 1);
+       if (ret < 0) {
+               dev_dbg(nor->dev, "error %d reading SR3\n", ret);
+               return ret;
+       }
+
+       return val;
+}
+
 /*
  * Write status register 1 byte
  * Returns negative if error occurred.
@@ -605,6 +623,17 @@ static int write_sr(struct spi_nor *nor, u8 val)
        return nor->write_reg(nor, SPINOR_OP_WRSR, nor->cmd_buf, 1);
 }
 
+/**
+ * write_sr3() - Write status register 3 unique to newer Winbond flashes
+ * @nor:       pointer to a 'struct spi_nor'
+ * @val:       value to be written into SR3
+ */
+static int write_sr3(struct spi_nor *nor, u8 val)
+{
+       nor->cmd_buf[0] = val;
+       return nor->write_reg(nor, SPINOR_OP_WRSR3, nor->cmd_buf, 1);
+}
+
 /*
  * Set write enable latch with Write Enable command.
  * Returns negative if error occurred.
@@ -4225,6 +4254,24 @@ static int spi_nor_init(struct spi_nor *nor)
                write_enable(nor);
                write_sr(nor, 0);
                spi_nor_wait_till_ready(nor);
+
+               /*
+                * Some Winbond SPI NORs have special SR3 register which is
+                * used among other things to control whether non-standard
+                * "Individual Block/Sector Write Protection" (WPS bit)
+                * locking scheme is activated. This non-standard locking
+                * scheme is not supported by either U-Boot or Linux SPI
+                * NOR stack so make sure it is disabled, otherwise the
+                * SPI NOR may appear locked for no obvious reason.
+                */
+               if (JEDEC_MFR(nor->info) == SNOR_MFR_WINBOND) {
+                       err = read_sr3(nor);
+                       if (err > 0 && err & SR3_WPS) {
+                               write_enable(nor);
+                               write_sr3(nor, err & ~SR3_WPS);
+                               write_disable(nor);
+                       }
+               }
        }
 
        if (nor->quad_enable) {
index d5f4faf0a68d089a666c87a876016bd9bb222d97..047e83e84638fefc650688901513fa5787a8956e 100644 (file)
@@ -48,6 +48,8 @@
 #define SPINOR_OP_WRSR         0x01    /* Write status register 1 byte */
 #define SPINOR_OP_RDSR2                0x3f    /* Read status register 2 */
 #define SPINOR_OP_WRSR2                0x3e    /* Write status register 2 */
+#define SPINOR_OP_RDSR3                0x15    /* Read status register 3 */
+#define SPINOR_OP_WRSR3                0x11    /* Write status register 3 */
 #define SPINOR_OP_READ         0x03    /* Read data bytes (low frequency) */
 #define SPINOR_OP_READ_FAST    0x0b    /* Read data bytes (high frequency) */
 #define SPINOR_OP_READ_1_1_2   0x3b    /* Read data bytes (Dual Output SPI) */
  */
 #define SNOR_FLASH_CNT_MAX     2
 
+/* Status Register 3 bits. */
+#define SR3_WPS                        BIT(2)
+
 /* For Cypress flash. */
 #define SPINOR_OP_RD_ANY_REG                   0x65    /* Read any register */
 #define SPINOR_OP_WR_ANY_REG                   0x71    /* Write any register */