mtd: spi-nor: Set the 4-Byte Address Mode method based on SFDP data
authorTudor Ambarus <tudor.ambarus@linaro.org>
Fri, 31 Mar 2023 07:46:02 +0000 (07:46 +0000)
committerTudor Ambarus <tudor.ambarus@linaro.org>
Tue, 4 Apr 2023 06:43:50 +0000 (09:43 +0300)
JESD216 SFDP defines in BFPT methods to enter and exit the
4-Byte Address Mode. The flash parameters and settings that are
retrieved from SFDP have higher precedence than the static
initialized ones, because they should be more accurate and less
error prone than those initialized statically. Parse and favor the
BFPT-parsed set_4byte_addr_mode methods.

Some regressions may be introduced by this patch, because the
params->set_4byte_addr_mode method that was set either in
spi_nor_init_default_params() or later overwritten in default_init()
hooks, are now be overwritten with a different value based on the
BFPT data. If that's the case, the fix is to introduce a post_bfpt
fixup hook where one should fix the wrong BFPT info.

Link: https://lore.kernel.org/r/20230331074606.3559258-7-tudor.ambarus@linaro.org
Signed-off-by: Tudor Ambarus <tudor.ambarus@linaro.org>
drivers/mtd/spi-nor/core.c
drivers/mtd/spi-nor/macronix.c
drivers/mtd/spi-nor/micron-st.c
drivers/mtd/spi-nor/sfdp.c
drivers/mtd/spi-nor/sfdp.h
drivers/mtd/spi-nor/winbond.c

index c65c025..afb56b2 100644 (file)
@@ -2898,6 +2898,8 @@ static void spi_nor_init_fixup_flags(struct spi_nor *nor)
  */
 static void spi_nor_late_init_params(struct spi_nor *nor)
 {
+       struct spi_nor_flash_parameter *params = nor->params;
+
        if (nor->manufacturer && nor->manufacturer->fixups &&
            nor->manufacturer->fixups->late_init)
                nor->manufacturer->fixups->late_init(nor);
@@ -2905,6 +2907,10 @@ static void spi_nor_late_init_params(struct spi_nor *nor)
        if (nor->info->fixups && nor->info->fixups->late_init)
                nor->info->fixups->late_init(nor);
 
+       /* Default method kept for backward compatibility. */
+       if (!params->set_4byte_addr_mode)
+               params->set_4byte_addr_mode = spi_nor_set_4byte_addr_mode_brwr;
+
        spi_nor_init_flags(nor);
        spi_nor_init_fixup_flags(nor);
 
@@ -2973,7 +2979,6 @@ static void spi_nor_init_default_params(struct spi_nor *nor)
        struct device_node *np = spi_nor_get_flash_node(nor);
 
        params->quad_enable = spi_nor_sr2_bit1_quad_enable;
-       params->set_4byte_addr_mode = spi_nor_set_4byte_addr_mode_brwr;
        params->otp.org = &info->otp_org;
 
        /* Default to 16-bit Write Status (01h) Command */
index 1cd55ba..0488825 100644 (file)
@@ -108,11 +108,17 @@ static const struct flash_info macronix_nor_parts[] = {
 static void macronix_nor_default_init(struct spi_nor *nor)
 {
        nor->params->quad_enable = spi_nor_sr1_bit6_quad_enable;
-       nor->params->set_4byte_addr_mode = spi_nor_set_4byte_addr_mode_en4b_ex4b;
+}
+
+static void macronix_nor_late_init(struct spi_nor *nor)
+{
+       if (!nor->params->set_4byte_addr_mode)
+               nor->params->set_4byte_addr_mode = spi_nor_set_4byte_addr_mode_en4b_ex4b;
 }
 
 static const struct spi_nor_fixups macronix_nor_fixups = {
        .default_init = macronix_nor_default_init,
+       .late_init = macronix_nor_late_init,
 };
 
 const struct spi_nor_manufacturer spi_nor_macronix = {
index a75f0f4..a6f0801 100644 (file)
@@ -425,13 +425,17 @@ static void micron_st_nor_default_init(struct spi_nor *nor)
        nor->flags |= SNOR_F_HAS_LOCK;
        nor->flags &= ~SNOR_F_HAS_16BIT_SR;
        nor->params->quad_enable = NULL;
-       nor->params->set_4byte_addr_mode = spi_nor_set_4byte_addr_mode_wren_en4b_ex4b;
 }
 
 static void micron_st_nor_late_init(struct spi_nor *nor)
 {
+       struct spi_nor_flash_parameter *params = nor->params;
+
        if (nor->info->mfr_flags & USE_FSR)
-               nor->params->ready = micron_st_nor_ready;
+               params->ready = micron_st_nor_ready;
+
+       if (!params->set_4byte_addr_mode)
+               params->set_4byte_addr_mode = spi_nor_set_4byte_addr_mode_wren_en4b_ex4b;
 }
 
 static const struct spi_nor_fixups micron_st_nor_fixups = {
index 298ab5e..69e47c9 100644 (file)
@@ -438,6 +438,7 @@ static int spi_nor_parse_bfpt(struct spi_nor *nor,
        size_t len;
        int i, cmd, err;
        u32 addr, val;
+       u32 dword;
        u16 half;
        u8 erase_mask;
 
@@ -607,6 +608,16 @@ static int spi_nor_parse_bfpt(struct spi_nor *nor,
                break;
        }
 
+       dword = bfpt.dwords[SFDP_DWORD(16)] & BFPT_DWORD16_4B_ADDR_MODE_MASK;
+       if (SFDP_MASK_CHECK(dword, BFPT_DWORD16_4B_ADDR_MODE_BRWR))
+               params->set_4byte_addr_mode = spi_nor_set_4byte_addr_mode_brwr;
+       else if (SFDP_MASK_CHECK(dword, BFPT_DWORD16_4B_ADDR_MODE_WREN_EN4B_EX4B))
+               params->set_4byte_addr_mode = spi_nor_set_4byte_addr_mode_wren_en4b_ex4b;
+       else if (SFDP_MASK_CHECK(dword, BFPT_DWORD16_4B_ADDR_MODE_EN4B_EX4B))
+               params->set_4byte_addr_mode = spi_nor_set_4byte_addr_mode_en4b_ex4b;
+       else
+               dev_dbg(nor->dev, "BFPT: 4-Byte Address Mode method is not recognized or not implemented\n");
+
        /* Soft Reset support. */
        if (bfpt.dwords[SFDP_DWORD(16)] & BFPT_DWORD16_SWRST_EN_RST)
                nor->flags |= SNOR_F_SOFT_RESET;
index 500659b..6eb99e1 100644 (file)
@@ -15,6 +15,7 @@
 
 /* SFDP DWORDS are indexed from 1 but C arrays are indexed from 0. */
 #define SFDP_DWORD(i)          ((i) - 1)
+#define SFDP_MASK_CHECK(dword, mask)           (((dword) & (mask)) == (mask))
 
 /* Basic Flash Parameter Table */
 
@@ -89,6 +90,32 @@ struct sfdp_bfpt {
 #define BFPT_DWORD15_QER_SR2_BIT1_NO_RD                (0x4UL << 20)
 #define BFPT_DWORD15_QER_SR2_BIT1              (0x5UL << 20) /* Spansion */
 
+#define BFPT_DWORD16_EN4B_MASK                 GENMASK(31, 24)
+#define BFPT_DWORD16_EN4B_ALWAYS_4B            BIT(30)
+#define BFPT_DWORD16_EN4B_4B_OPCODES           BIT(29)
+#define BFPT_DWORD16_EN4B_16BIT_NV_CR          BIT(28)
+#define BFPT_DWORD16_EN4B_BRWR                 BIT(27)
+#define BFPT_DWORD16_EN4B_WREAR                        BIT(26)
+#define BFPT_DWORD16_EN4B_WREN_EN4B            BIT(25)
+#define BFPT_DWORD16_EN4B_EN4B                 BIT(24)
+#define BFPT_DWORD16_EX4B_MASK                 GENMASK(18, 14)
+#define BFPT_DWORD16_EX4B_16BIT_NV_CR          BIT(18)
+#define BFPT_DWORD16_EX4B_BRWR                 BIT(17)
+#define BFPT_DWORD16_EX4B_WREAR                        BIT(16)
+#define BFPT_DWORD16_EX4B_WREN_EX4B            BIT(15)
+#define BFPT_DWORD16_EX4B_EX4B                 BIT(14)
+#define BFPT_DWORD16_4B_ADDR_MODE_MASK                 \
+       (BFPT_DWORD16_EN4B_MASK | BFPT_DWORD16_EX4B_MASK)
+#define BFPT_DWORD16_4B_ADDR_MODE_16BIT_NV_CR          \
+       (BFPT_DWORD16_EN4B_16BIT_NV_CR | BFPT_DWORD16_EX4B_16BIT_NV_CR)
+#define BFPT_DWORD16_4B_ADDR_MODE_BRWR                 \
+       (BFPT_DWORD16_EN4B_BRWR | BFPT_DWORD16_EX4B_BRWR)
+#define BFPT_DWORD16_4B_ADDR_MODE_WREAR                        \
+       (BFPT_DWORD16_EN4B_WREAR | BFPT_DWORD16_EX4B_WREAR)
+#define BFPT_DWORD16_4B_ADDR_MODE_WREN_EN4B_EX4B       \
+       (BFPT_DWORD16_EN4B_WREN_EN4B | BFPT_DWORD16_EX4B_WREN_EX4B)
+#define BFPT_DWORD16_4B_ADDR_MODE_EN4B_EX4B            \
+       (BFPT_DWORD16_EN4B_EN4B | BFPT_DWORD16_EX4B_EX4B)
 #define BFPT_DWORD16_SWRST_EN_RST              BIT(12)
 
 #define BFPT_DWORD18_CMD_EXT_MASK              GENMASK(30, 29)
index 9cea241..834d6ba 100644 (file)
@@ -216,19 +216,25 @@ static const struct spi_nor_otp_ops winbond_nor_otp_ops = {
        .is_locked = spi_nor_otp_is_locked_sr2,
 };
 
-static void winbond_nor_default_init(struct spi_nor *nor)
-{
-       nor->params->set_4byte_addr_mode = winbond_nor_set_4byte_addr_mode;
-}
-
 static void winbond_nor_late_init(struct spi_nor *nor)
 {
-       if (nor->params->otp.org->n_regions)
-               nor->params->otp.ops = &winbond_nor_otp_ops;
+       struct spi_nor_flash_parameter *params = nor->params;
+
+       if (params->otp.org->n_regions)
+               params->otp.ops = &winbond_nor_otp_ops;
+
+       /*
+        * Winbond seems to require that the Extended Address Register to be set
+        * to zero when exiting the 4-Byte Address Mode, at least for W25Q256FV.
+        * This requirement is not described in the JESD216 SFDP standard, thus
+        * it is Winbond specific. Since we do not know if other Winbond flashes
+        * have the same requirement, play safe and overwrite the method parsed
+        * from BFPT, if any.
+        */
+       params->set_4byte_addr_mode = winbond_nor_set_4byte_addr_mode;
 }
 
 static const struct spi_nor_fixups winbond_nor_fixups = {
-       .default_init = winbond_nor_default_init,
        .late_init = winbond_nor_late_init,
 };