Merge branch '2022-08-04-Kconfig-migrations'
[platform/kernel/u-boot.git] / drivers / mtd / spi / spi-nor-core.c
index 3bf9404..26a356b 100644 (file)
@@ -26,6 +26,7 @@
 
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/spi-nor.h>
+#include <mtd/cfi_flash.h>
 #include <spi-mem.h>
 #include <spi.h>
 
@@ -63,6 +64,10 @@ struct sfdp_parameter_header {
 #define SFDP_SECTOR_MAP_ID     0xff81  /* Sector Map Table */
 #define SFDP_SST_ID            0x01bf  /* Manufacturer specific Table */
 #define SFDP_PROFILE1_ID       0xff05  /* xSPI Profile 1.0 Table */
+#define SFDP_SCCR_MAP_ID       0xff87  /*
+                                        * Status, Control and Configuration
+                                        * Register Map.
+                                        */
 
 #define SFDP_SIGNATURE         0x50444653U
 #define SFDP_JESD216_MAJOR     1
@@ -172,6 +177,9 @@ struct sfdp_header {
 #define PROFILE1_DWORD5_DUMMY_100MHZ           GENMASK(11, 7)
 #define PROFILE1_DUMMY_DEFAULT                 20
 
+/* Status, Control and Configuration Register Map(SCCR) */
+#define SCCR_DWORD22_OCTAL_DTR_EN_VOLATILE      BIT(31)
+
 struct sfdp_bfpt {
        u32     dwords[BFPT_DWORD_MAX];
 };
@@ -315,6 +323,31 @@ static int spi_nor_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len)
        return spi_nor_read_write_reg(nor, &op, buf);
 }
 
+#ifdef CONFIG_SPI_FLASH_SPANSION
+static int spansion_read_any_reg(struct spi_nor *nor, u32 addr, u8 dummy,
+                                u8 *val)
+{
+       struct spi_mem_op op =
+                       SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDAR, 1),
+                                  SPI_MEM_OP_ADDR(nor->addr_width, addr, 1),
+                                  SPI_MEM_OP_DUMMY(dummy / 8, 1),
+                                  SPI_MEM_OP_DATA_IN(1, NULL, 1));
+
+       return spi_nor_read_write_reg(nor, &op, val);
+}
+
+static int spansion_write_any_reg(struct spi_nor *nor, u32 addr, u8 val)
+{
+       struct spi_mem_op op =
+                       SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WRAR, 1),
+                                  SPI_MEM_OP_ADDR(nor->addr_width, addr, 1),
+                                  SPI_MEM_OP_NO_DUMMY,
+                                  SPI_MEM_OP_DATA_OUT(1, NULL, 1));
+
+       return spi_nor_read_write_reg(nor, &op, &val);
+}
+#endif
+
 static ssize_t spi_nor_read_data(struct spi_nor *nor, loff_t from, size_t len,
                                 u_char *buf)
 {
@@ -637,6 +670,9 @@ static int set_4byte(struct spi_nor *nor, const struct flash_info *info,
                }
 
                return status;
+       case SNOR_MFR_CYPRESS:
+               cmd = enable ? SPINOR_OP_EN4B : SPINOR_OP_EX4B_CYPRESS;
+               return nor->write_reg(nor, cmd, NULL, 0);
        default:
                /* Spansion style */
                nor->cmd_buf[0] = enable << 7;
@@ -644,6 +680,35 @@ static int set_4byte(struct spi_nor *nor, const struct flash_info *info,
        }
 }
 
+#ifdef CONFIG_SPI_FLASH_SPANSION
+/*
+ * Read status register 1 by using Read Any Register command to support multi
+ * die package parts.
+ */
+static int spansion_sr_ready(struct spi_nor *nor, u32 addr_base, u8 dummy)
+{
+       u32 reg_addr = addr_base + SPINOR_REG_ADDR_STR1V;
+       u8 sr;
+       int ret;
+
+       ret = spansion_read_any_reg(nor, reg_addr, dummy, &sr);
+       if (ret < 0)
+               return ret;
+
+       if (sr & (SR_E_ERR | SR_P_ERR)) {
+               if (sr & SR_E_ERR)
+                       dev_dbg(nor->dev, "Erase Error occurred\n");
+               else
+                       dev_dbg(nor->dev, "Programming Error occurred\n");
+
+               nor->write_reg(nor, SPINOR_OP_CLSR, NULL, 0);
+               return -EIO;
+       }
+
+       return !(sr & SR_WIP);
+}
+#endif
+
 static int spi_nor_sr_ready(struct spi_nor *nor)
 {
        int sr = read_sr(nor);
@@ -688,7 +753,7 @@ static int spi_nor_fsr_ready(struct spi_nor *nor)
        return fsr & FSR_READY;
 }
 
-static int spi_nor_ready(struct spi_nor *nor)
+static int spi_nor_default_ready(struct spi_nor *nor)
 {
        int sr, fsr;
 
@@ -701,6 +766,14 @@ static int spi_nor_ready(struct spi_nor *nor)
        return sr && fsr;
 }
 
+static int spi_nor_ready(struct spi_nor *nor)
+{
+       if (nor->ready)
+               return nor->ready(nor);
+
+       return spi_nor_default_ready(nor);
+}
+
 /*
  * Service routine to read status register until ready, or timeout occurs.
  * Returns non-zero if error.
@@ -806,7 +879,8 @@ static int read_bar(struct spi_nor *nor, const struct flash_info *info)
 #endif
 
 /*
- * Initiate the erasure of a single sector
+ * Initiate the erasure of a single sector. Returns the number of bytes erased
+ * on success, a negative error code on error.
  */
 static int spi_nor_erase_sector(struct spi_nor *nor, u32 addr)
 {
@@ -815,6 +889,7 @@ static int spi_nor_erase_sector(struct spi_nor *nor, u32 addr)
                           SPI_MEM_OP_ADDR(nor->addr_width, addr, 0),
                           SPI_MEM_OP_NO_DUMMY,
                           SPI_MEM_OP_NO_DATA);
+       int ret;
 
        spi_nor_setup_op(nor, &op, nor->write_proto);
 
@@ -825,7 +900,11 @@ static int spi_nor_erase_sector(struct spi_nor *nor, u32 addr)
         * Default implementation, if driver doesn't have a specialized HW
         * control
         */
-       return spi_mem_exec_op(nor->spi, &op);
+       ret = spi_mem_exec_op(nor->spi, &op);
+       if (ret)
+               return ret;
+
+       return nor->mtd.erasesize;
 }
 
 /*
@@ -835,52 +914,136 @@ static int spi_nor_erase_sector(struct spi_nor *nor, u32 addr)
 static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr)
 {
        struct spi_nor *nor = mtd_to_spi_nor(mtd);
+       bool addr_known = false;
        u32 addr, len, rem;
-       int ret;
+       int ret, err;
 
        dev_dbg(nor->dev, "at 0x%llx, len %lld\n", (long long)instr->addr,
                (long long)instr->len);
 
-       if (!instr->len)
-               return 0;
-
        div_u64_rem(instr->len, mtd->erasesize, &rem);
-       if (rem)
-               return -EINVAL;
+       if (rem) {
+               ret = -EINVAL;
+               goto err;
+       }
 
        addr = instr->addr;
        len = instr->len;
 
+       instr->state = MTD_ERASING;
+       addr_known = true;
+
        while (len) {
                WATCHDOG_RESET();
+               if (!IS_ENABLED(CONFIG_SPL_BUILD) && ctrlc()) {
+                       addr_known = false;
+                       ret = -EINTR;
+                       goto erase_err;
+               }
 #ifdef CONFIG_SPI_FLASH_BAR
                ret = write_bar(nor, addr);
                if (ret < 0)
-                       return ret;
+                       goto erase_err;
 #endif
-               write_enable(nor);
+               ret = write_enable(nor);
+               if (ret < 0)
+                       goto erase_err;
 
                ret = spi_nor_erase_sector(nor, addr);
-               if (ret)
+               if (ret < 0)
                        goto erase_err;
 
-               addr += mtd->erasesize;
-               len -= mtd->erasesize;
+               addr += ret;
+               len -= ret;
 
                ret = spi_nor_wait_till_ready(nor);
                if (ret)
                        goto erase_err;
        }
 
+       addr_known = false;
 erase_err:
 #ifdef CONFIG_SPI_FLASH_BAR
-       ret = clean_bar(nor);
+       err = clean_bar(nor);
+       if (!ret)
+               ret = err;
 #endif
-       write_disable(nor);
+       err = write_disable(nor);
+       if (!ret)
+               ret = err;
+
+err:
+       if (ret) {
+               instr->fail_addr = addr_known ? addr : MTD_FAIL_ADDR_UNKNOWN;
+               instr->state = MTD_ERASE_FAILED;
+       } else {
+               instr->state = MTD_ERASE_DONE;
+       }
 
        return ret;
 }
 
+#ifdef CONFIG_SPI_FLASH_SPANSION
+/**
+ * spansion_erase_non_uniform() - erase non-uniform sectors for Spansion/Cypress
+ *                                chips
+ * @nor:       pointer to a 'struct spi_nor'
+ * @addr:      address of the sector to erase
+ * @opcode_4k: opcode for 4K sector erase
+ * @ovlsz_top: size of overlaid portion at the top address
+ * @ovlsz_btm: size of overlaid portion at the bottom address
+ *
+ * Erase an address range on the nor chip that can contain 4KB sectors overlaid
+ * on top and/or bottom. The appropriate erase opcode and size are chosen by
+ * address to erase and size of overlaid portion.
+ *
+ * Return: number of bytes erased on success, -errno otherwise.
+ */
+static int spansion_erase_non_uniform(struct spi_nor *nor, u32 addr,
+                                     u8 opcode_4k, u32 ovlsz_top,
+                                     u32 ovlsz_btm)
+{
+       struct spi_mem_op op =
+               SPI_MEM_OP(SPI_MEM_OP_CMD(nor->erase_opcode, 0),
+                          SPI_MEM_OP_ADDR(nor->addr_width, addr, 0),
+                          SPI_MEM_OP_NO_DUMMY,
+                          SPI_MEM_OP_NO_DATA);
+       struct mtd_info *mtd = &nor->mtd;
+       u32 erasesize;
+       int ret;
+
+       /* 4KB sectors */
+       if (op.addr.val < ovlsz_btm ||
+           op.addr.val >= mtd->size - ovlsz_top) {
+               op.cmd.opcode = opcode_4k;
+               erasesize = SZ_4K;
+
+       /* Non-overlaid portion in the normal sector at the bottom */
+       } else if (op.addr.val == ovlsz_btm) {
+               op.cmd.opcode = nor->erase_opcode;
+               erasesize = mtd->erasesize - ovlsz_btm;
+
+       /* Non-overlaid portion in the normal sector at the top */
+       } else if (op.addr.val == mtd->size - mtd->erasesize) {
+               op.cmd.opcode = nor->erase_opcode;
+               erasesize = mtd->erasesize - ovlsz_top;
+
+       /* Normal sectors */
+       } else {
+               op.cmd.opcode = nor->erase_opcode;
+               erasesize = mtd->erasesize;
+       }
+
+       spi_nor_setup_op(nor, &op, nor->write_proto);
+
+       ret = spi_mem_exec_op(nor->spi, &op);
+       if (ret)
+               return ret;
+
+       return erasesize;
+}
+#endif
+
 #if defined(CONFIG_SPI_FLASH_STMICRO) || defined(CONFIG_SPI_FLASH_SST)
 /* Write status register and ensure bits in mask match written values */
 static int write_sr_and_check(struct spi_nor *nor, u8 status_new, u8 mask)
@@ -1151,13 +1314,13 @@ static int stm_unlock(struct spi_nor *nor, loff_t ofs, uint64_t len)
 }
 
 /*
- * Check if a region of the flash is (completely) locked. See stm_lock() for
+ * Check if a region of the flash is (completely) unlocked. See stm_lock() for
  * more info.
  *
- * Returns 1 if entire region is locked, 0 if any portion is unlocked, and
+ * Returns 1 if entire region is unlocked, 0 if any portion is locked, and
  * negative on errors.
  */
-static int stm_is_locked(struct spi_nor *nor, loff_t ofs, uint64_t len)
+static int stm_is_unlocked(struct spi_nor *nor, loff_t ofs, uint64_t len)
 {
        int status;
 
@@ -1165,7 +1328,7 @@ static int stm_is_locked(struct spi_nor *nor, loff_t ofs, uint64_t len)
        if (status < 0)
                return status;
 
-       return stm_is_locked_sr(nor, ofs, len, status);
+       return stm_is_unlocked_sr(nor, ofs, len, status);
 }
 #endif /* CONFIG_SPI_FLASH_STMICRO */
 
@@ -1398,16 +1561,16 @@ static int sst26_lock(struct spi_nor *nor, loff_t ofs, uint64_t len)
 }
 
 /*
- * Returns EACCES (positive value) if region is locked, 0 if region is unlocked,
- * and negative on errors.
+ * Returns EACCES (positive value) if region is (partially) locked, 0 if region
+ * is completely unlocked, and negative on errors.
  */
-static int sst26_is_locked(struct spi_nor *nor, loff_t ofs, uint64_t len)
+static int sst26_is_unlocked(struct spi_nor *nor, loff_t ofs, uint64_t len)
 {
        /*
-        * is_locked function is used for check before reading or erasing flash
-        * region, so offset and length might be not 64k allighned, so adjust
-        * them to be 64k allighned as sst26_lock_ctl works only with 64k
-        * allighned regions.
+        * is_unlocked function is used for check before reading or erasing
+        * flash region, so offset and length might be not 64k aligned, so
+        * adjust them to be 64k aligned as sst26_lock_ctl works only with 64k
+        * aligned regions.
         */
        ofs -= ofs & (SZ_64K - 1);
        len = len & (SZ_64K - 1) ? (len & ~(SZ_64K - 1)) + SZ_64K : len;
@@ -1531,9 +1694,6 @@ static int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len,
 
        dev_dbg(nor->dev, "to 0x%08x, len %zd\n", (u32)to, len);
 
-       if (!len)
-               return 0;
-
        for (i = 0; i < len; ) {
                ssize_t written;
                loff_t addr = to + i;
@@ -1619,6 +1779,61 @@ static int macronix_quad_enable(struct spi_nor *nor)
 }
 #endif
 
+#ifdef CONFIG_SPI_FLASH_SPANSION
+/**
+ * spansion_quad_enable_volatile() - enable Quad I/O mode in volatile register.
+ * @nor:       pointer to a 'struct spi_nor'
+ * @addr_base: base address of register (can be >0 in multi-die parts)
+ * @dummy:     number of dummy cycles for register read
+ *
+ * It is recommended to update volatile registers in the field application due
+ * to a risk of the non-volatile registers corruption by power interrupt. This
+ * function sets Quad Enable bit in CFR1 volatile.
+ *
+ * Return: 0 on success, -errno otherwise.
+ */
+static int spansion_quad_enable_volatile(struct spi_nor *nor, u32 addr_base,
+                                        u8 dummy)
+{
+       u32 addr = addr_base + SPINOR_REG_ADDR_CFR1V;
+
+       u8 cr;
+       int ret;
+
+       /* Check current Quad Enable bit value. */
+       ret = spansion_read_any_reg(nor, addr, dummy, &cr);
+       if (ret < 0) {
+               dev_dbg(nor->dev,
+                       "error while reading configuration register\n");
+               return -EINVAL;
+       }
+
+       if (cr & CR_QUAD_EN_SPAN)
+               return 0;
+
+       cr |= CR_QUAD_EN_SPAN;
+
+       write_enable(nor);
+
+       ret = spansion_write_any_reg(nor, addr, cr);
+
+       if (ret < 0) {
+               dev_dbg(nor->dev,
+                       "error while writing configuration register\n");
+               return -EINVAL;
+       }
+
+       /* Read back and check it. */
+       ret = spansion_read_any_reg(nor, addr, dummy, &cr);
+       if (ret || !(cr & CR_QUAD_EN_SPAN)) {
+               dev_dbg(nor->dev, "Spansion Quad bit not set\n");
+               return -EINVAL;
+       }
+
+       return 0;
+}
+#endif
+
 #if defined(CONFIG_SPI_FLASH_SPANSION) || defined(CONFIG_SPI_FLASH_WINBOND)
 /*
  * Write status Register and configuration register with 2 bytes
@@ -2248,6 +2463,44 @@ out:
 }
 
 /**
+ * spi_nor_parse_sccr() - Parse the Status, Control and Configuration Register
+ *                       Map.
+ * @nor:                 pointer to a 'struct spi_nor'
+ * @sccr_header:         pointer to the 'struct sfdp_parameter_header' describing
+ *                       the SCCR Map table length and version.
+ *
+ * Return: 0 on success, -errno otherwise.
+ */
+static int spi_nor_parse_sccr(struct spi_nor *nor,
+                             const struct sfdp_parameter_header *sccr_header)
+{
+       u32 *table, addr;
+       size_t len;
+       int ret, i;
+
+       len = sccr_header->length * sizeof(*table);
+       table = kmalloc(len, GFP_KERNEL);
+       if (!table)
+               return -ENOMEM;
+
+       addr = SFDP_PARAM_HEADER_PTP(sccr_header);
+       ret = spi_nor_read_sfdp(nor, addr, len, table);
+       if (ret)
+               goto out;
+
+       /* Fix endianness of the table DWORDs. */
+       for (i = 0; i < sccr_header->length; i++)
+               table[i] = le32_to_cpu(table[i]);
+
+       if (FIELD_GET(SCCR_DWORD22_OCTAL_DTR_EN_VOLATILE, table[22]))
+               nor->flags |= SNOR_F_IO_MODE_EN_VOLATILE;
+
+out:
+       kfree(table);
+       return ret;
+}
+
+/**
  * spi_nor_parse_sfdp() - parse the Serial Flash Discoverable Parameters.
  * @nor:               pointer to a 'struct spi_nor'
  * @params:            pointer to the 'struct spi_nor_flash_parameter' to be
@@ -2353,6 +2606,10 @@ static int spi_nor_parse_sfdp(struct spi_nor *nor,
                        err = spi_nor_parse_profile1(nor, param_header, params);
                        break;
 
+               case SFDP_SCCR_MAP_ID:
+                       err = spi_nor_parse_sccr(nor, param_header);
+                       break;
+
                default:
                        break;
                }
@@ -2417,18 +2674,28 @@ static int spi_nor_init_params(struct spi_nor *nor,
        params->size = info->sector_size * info->n_sectors;
        params->page_size = info->page_size;
 
+       if (!(info->flags & SPI_NOR_NO_FR)) {
+               /* Default to Fast Read for DT and non-DT platform devices. */
+               params->hwcaps.mask |= SNOR_HWCAPS_READ_FAST;
+
+               /* Mask out Fast Read if not requested at DT instantiation. */
+#if CONFIG_IS_ENABLED(DM_SPI)
+               if (!ofnode_read_bool(dev_ofnode(nor->spi->dev),
+                                     "m25p,fast-read"))
+                       params->hwcaps.mask &= ~SNOR_HWCAPS_READ_FAST;
+#endif
+       }
+
        /* (Fast) Read settings. */
        params->hwcaps.mask |= SNOR_HWCAPS_READ;
        spi_nor_set_read_settings(&params->reads[SNOR_CMD_READ],
                                  0, 0, SPINOR_OP_READ,
                                  SNOR_PROTO_1_1_1);
 
-       if (!(info->flags & SPI_NOR_NO_FR)) {
-               params->hwcaps.mask |= SNOR_HWCAPS_READ_FAST;
+       if (params->hwcaps.mask & SNOR_HWCAPS_READ_FAST)
                spi_nor_set_read_settings(&params->reads[SNOR_CMD_READ_FAST],
                                          0, 8, SPINOR_OP_READ_FAST,
                                          SNOR_PROTO_1_1_1);
-       }
 
        if (info->flags & SPI_NOR_DUAL_READ) {
                params->hwcaps.mask |= SNOR_HWCAPS_READ_1_1_2;
@@ -2671,10 +2938,11 @@ spi_nor_adjust_hwcaps(struct spi_nor *nor,
        unsigned int cap;
 
        /*
-        * Enable all caps by default. We will mask them after checking what's
-        * really supported using spi_mem_supports_op().
+        * Start by assuming the controller supports every capability.
+        * We will mask them after checking what's really supported
+        * using spi_mem_supports_op().
         */
-       *hwcaps = SNOR_HWCAPS_ALL;
+       *hwcaps = SNOR_HWCAPS_ALL & params->hwcaps.mask;
 
        /* X-X-X modes are not supported yet, mask them all. */
        *hwcaps &= ~SNOR_HWCAPS_X_X_X;
@@ -2898,6 +3166,492 @@ static int spi_nor_setup(struct spi_nor *nor, const struct flash_info *info,
        return nor->setup(nor, info, params);
 }
 
+#ifdef CONFIG_SPI_FLASH_SPANSION
+static int s25hx_t_mdp_ready(struct spi_nor *nor)
+{
+       u32 addr;
+       int ret;
+
+       for (addr = 0; addr < nor->mtd.size; addr += SZ_128M) {
+               ret = spansion_sr_ready(nor, addr, 0);
+               if (!ret)
+                       return ret;
+       }
+
+       return 1;
+}
+
+static int s25hx_t_quad_enable(struct spi_nor *nor)
+{
+       u32 addr;
+       int ret;
+
+       for (addr = 0; addr < nor->mtd.size; addr += SZ_128M) {
+               ret = spansion_quad_enable_volatile(nor, addr, 0);
+               if (ret)
+                       return ret;
+       }
+
+       return 0;
+}
+
+static int s25hx_t_erase_non_uniform(struct spi_nor *nor, loff_t addr)
+{
+       /* Support 32 x 4KB sectors at bottom */
+       return spansion_erase_non_uniform(nor, addr, SPINOR_OP_BE_4K_4B, 0,
+                                         SZ_128K);
+}
+
+static int s25hx_t_setup(struct spi_nor *nor, const struct flash_info *info,
+                        const struct spi_nor_flash_parameter *params)
+{
+       int ret;
+       u8 cfr3v;
+
+#ifdef CONFIG_SPI_FLASH_BAR
+       return -ENOTSUPP; /* Bank Address Register is not supported */
+#endif
+       /*
+        * Read CFR3V to check if uniform sector is selected. If not, assign an
+        * erase hook that supports non-uniform erase.
+        */
+       ret = spansion_read_any_reg(nor, SPINOR_REG_ADDR_CFR3V, 0, &cfr3v);
+       if (ret)
+               return ret;
+       if (!(cfr3v & CFR3V_UNHYSA))
+               nor->erase = s25hx_t_erase_non_uniform;
+
+       /*
+        * For the multi-die package parts, the ready() hook is needed to check
+        * all dies' status via read any register.
+        */
+       if (nor->mtd.size > SZ_128M)
+               nor->ready = s25hx_t_mdp_ready;
+
+       return spi_nor_default_setup(nor, info, params);
+}
+
+static void s25hx_t_default_init(struct spi_nor *nor)
+{
+       nor->setup = s25hx_t_setup;
+}
+
+static int s25hx_t_post_bfpt_fixup(struct spi_nor *nor,
+                                  const struct sfdp_parameter_header *header,
+                                  const struct sfdp_bfpt *bfpt,
+                                  struct spi_nor_flash_parameter *params)
+{
+       int ret;
+       u32 addr;
+       u8 cfr3v;
+
+       /* erase size in case it is set to 4K from BFPT */
+       nor->erase_opcode = SPINOR_OP_SE_4B;
+       nor->mtd.erasesize = nor->info->sector_size;
+
+       ret = set_4byte(nor, nor->info, 1);
+       if (ret)
+               return ret;
+       nor->addr_width = 4;
+
+       /*
+        * The page_size is set to 512B from BFPT, but it actually depends on
+        * the configuration register. Look up the CFR3V and determine the
+        * page_size. For multi-die package parts, use 512B only when the all
+        * dies are configured to 512B buffer.
+        */
+       for (addr = 0; addr < params->size; addr += SZ_128M) {
+               ret = spansion_read_any_reg(nor, addr + SPINOR_REG_ADDR_CFR3V,
+                                           0, &cfr3v);
+               if (ret)
+                       return ret;
+
+               if (!(cfr3v & CFR3V_PGMBUF)) {
+                       params->page_size = 256;
+                       return 0;
+               }
+       }
+       params->page_size = 512;
+
+       return 0;
+}
+
+static void s25hx_t_post_sfdp_fixup(struct spi_nor *nor,
+                                   struct spi_nor_flash_parameter *params)
+{
+       /* READ_FAST_4B (0Ch) requires mode cycles*/
+       params->reads[SNOR_CMD_READ_FAST].num_mode_clocks = 8;
+       /* PP_1_1_4 is not supported */
+       params->hwcaps.mask &= ~SNOR_HWCAPS_PP_1_1_4;
+       /* Use volatile register to enable quad */
+       params->quad_enable = s25hx_t_quad_enable;
+}
+
+static struct spi_nor_fixups s25hx_t_fixups = {
+       .default_init = s25hx_t_default_init,
+       .post_bfpt = s25hx_t_post_bfpt_fixup,
+       .post_sfdp = s25hx_t_post_sfdp_fixup,
+};
+
+static int s25fl256l_setup(struct spi_nor *nor, const struct flash_info *info,
+                          const struct spi_nor_flash_parameter *params)
+{
+       return -ENOTSUPP; /* Bank Address Register is not supported */
+}
+
+static void s25fl256l_default_init(struct spi_nor *nor)
+{
+       nor->setup = s25fl256l_setup;
+}
+
+static struct spi_nor_fixups s25fl256l_fixups = {
+       .default_init = s25fl256l_default_init,
+};
+#endif
+
+#ifdef CONFIG_SPI_FLASH_S28HS512T
+/**
+ * spi_nor_cypress_octal_dtr_enable() - Enable octal DTR on Cypress flashes.
+ * @nor:               pointer to a 'struct spi_nor'
+ *
+ * This also sets the memory access latency cycles to 24 to allow the flash to
+ * run at up to 200MHz.
+ *
+ * Return: 0 on success, -errno otherwise.
+ */
+static int spi_nor_cypress_octal_dtr_enable(struct spi_nor *nor)
+{
+       struct spi_mem_op op;
+       u8 buf;
+       u8 addr_width = 3;
+       int ret;
+
+       /* Use 24 dummy cycles for memory array reads. */
+       ret = write_enable(nor);
+       if (ret)
+               return ret;
+
+       buf = SPINOR_REG_CYPRESS_CFR2V_MEMLAT_11_24;
+       op = (struct spi_mem_op)SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WR_ANY_REG, 1),
+                       SPI_MEM_OP_ADDR(addr_width, SPINOR_REG_CYPRESS_CFR2V, 1),
+                       SPI_MEM_OP_NO_DUMMY,
+                       SPI_MEM_OP_DATA_OUT(1, &buf, 1));
+       ret = spi_mem_exec_op(nor->spi, &op);
+       if (ret) {
+               dev_warn(nor->dev,
+                        "failed to set default memory latency value: %d\n",
+                        ret);
+               return ret;
+       }
+       ret = spi_nor_wait_till_ready(nor);
+       if (ret)
+               return ret;
+
+       nor->read_dummy = 24;
+
+       /* Set the octal and DTR enable bits. */
+       ret = write_enable(nor);
+       if (ret)
+               return ret;
+
+       buf = SPINOR_REG_CYPRESS_CFR5V_OCT_DTR_EN;
+       op = (struct spi_mem_op)SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WR_ANY_REG, 1),
+                       SPI_MEM_OP_ADDR(addr_width, SPINOR_REG_CYPRESS_CFR5V, 1),
+                       SPI_MEM_OP_NO_DUMMY,
+                       SPI_MEM_OP_DATA_OUT(1, &buf, 1));
+       ret = spi_mem_exec_op(nor->spi, &op);
+       if (ret) {
+               dev_warn(nor->dev, "Failed to enable octal DTR mode\n");
+               return ret;
+       }
+
+       return 0;
+}
+
+static int s28hs512t_erase_non_uniform(struct spi_nor *nor, loff_t addr)
+{
+       /* Factory default configuration: 32 x 4 KiB sectors at bottom. */
+       return spansion_erase_non_uniform(nor, addr, SPINOR_OP_S28_SE_4K,
+                                         0, SZ_128K);
+}
+
+static int s28hs512t_setup(struct spi_nor *nor, const struct flash_info *info,
+                          const struct spi_nor_flash_parameter *params)
+{
+       struct spi_mem_op op;
+       u8 buf;
+       u8 addr_width = 3;
+       int ret;
+
+       ret = spi_nor_wait_till_ready(nor);
+       if (ret)
+               return ret;
+
+       /*
+        * Check CFR3V to check if non-uniform sector mode is selected. If it
+        * is, set the erase hook to the non-uniform erase procedure.
+        */
+       op = (struct spi_mem_op)
+               SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RD_ANY_REG, 1),
+                          SPI_MEM_OP_ADDR(addr_width,
+                                          SPINOR_REG_CYPRESS_CFR3V, 1),
+                          SPI_MEM_OP_NO_DUMMY,
+                          SPI_MEM_OP_DATA_IN(1, &buf, 1));
+
+       ret = spi_mem_exec_op(nor->spi, &op);
+       if (ret)
+               return ret;
+
+       if (!(buf & SPINOR_REG_CYPRESS_CFR3V_UNISECT))
+               nor->erase = s28hs512t_erase_non_uniform;
+
+       return spi_nor_default_setup(nor, info, params);
+}
+
+static void s28hs512t_default_init(struct spi_nor *nor)
+{
+       nor->octal_dtr_enable = spi_nor_cypress_octal_dtr_enable;
+       nor->setup = s28hs512t_setup;
+}
+
+static void s28hs512t_post_sfdp_fixup(struct spi_nor *nor,
+                                     struct spi_nor_flash_parameter *params)
+{
+       /*
+        * On older versions of the flash the xSPI Profile 1.0 table has the
+        * 8D-8D-8D Fast Read opcode as 0x00. But it actually should be 0xEE.
+        */
+       if (params->reads[SNOR_CMD_READ_8_8_8_DTR].opcode == 0)
+               params->reads[SNOR_CMD_READ_8_8_8_DTR].opcode =
+                       SPINOR_OP_CYPRESS_RD_FAST;
+
+       params->hwcaps.mask |= SNOR_HWCAPS_PP_8_8_8_DTR;
+
+       /* This flash is also missing the 4-byte Page Program opcode bit. */
+       spi_nor_set_pp_settings(&params->page_programs[SNOR_CMD_PP],
+                               SPINOR_OP_PP_4B, SNOR_PROTO_1_1_1);
+       /*
+        * Since xSPI Page Program opcode is backward compatible with
+        * Legacy SPI, use Legacy SPI opcode there as well.
+        */
+       spi_nor_set_pp_settings(&params->page_programs[SNOR_CMD_PP_8_8_8_DTR],
+                               SPINOR_OP_PP_4B, SNOR_PROTO_8_8_8_DTR);
+
+       /*
+        * The xSPI Profile 1.0 table advertises the number of additional
+        * address bytes needed for Read Status Register command as 0 but the
+        * actual value for that is 4.
+        */
+       params->rdsr_addr_nbytes = 4;
+}
+
+static int s28hs512t_post_bfpt_fixup(struct spi_nor *nor,
+                                    const struct sfdp_parameter_header *bfpt_header,
+                                    const struct sfdp_bfpt *bfpt,
+                                    struct spi_nor_flash_parameter *params)
+{
+       struct spi_mem_op op;
+       u8 buf;
+       u8 addr_width = 3;
+       int ret;
+
+       /*
+        * The BFPT table advertises a 512B page size but the page size is
+        * actually configurable (with the default being 256B). Read from
+        * CFR3V[4] and set the correct size.
+        */
+       op = (struct spi_mem_op)
+               SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RD_ANY_REG, 1),
+                          SPI_MEM_OP_ADDR(addr_width, SPINOR_REG_CYPRESS_CFR3V, 1),
+                          SPI_MEM_OP_NO_DUMMY,
+                          SPI_MEM_OP_DATA_IN(1, &buf, 1));
+       ret = spi_mem_exec_op(nor->spi, &op);
+       if (ret)
+               return ret;
+
+       if (buf & SPINOR_REG_CYPRESS_CFR3V_PGSZ)
+               params->page_size = 512;
+       else
+               params->page_size = 256;
+
+       /*
+        * The BFPT advertises that it supports 4k erases, and the datasheet
+        * says the same. But 4k erases did not work when testing. So, use 256k
+        * erases for now.
+        */
+       nor->erase_opcode = SPINOR_OP_SE_4B;
+       nor->mtd.erasesize = 0x40000;
+
+       return 0;
+}
+
+static struct spi_nor_fixups s28hs512t_fixups = {
+       .default_init = s28hs512t_default_init,
+       .post_sfdp = s28hs512t_post_sfdp_fixup,
+       .post_bfpt = s28hs512t_post_bfpt_fixup,
+};
+#endif /* CONFIG_SPI_FLASH_S28HS512T */
+
+#ifdef CONFIG_SPI_FLASH_MT35XU
+static int spi_nor_micron_octal_dtr_enable(struct spi_nor *nor)
+{
+       struct spi_mem_op op;
+       u8 buf;
+       u8 addr_width = 3;
+       int ret;
+
+       /* Set dummy cycles for Fast Read to the default of 20. */
+       ret = write_enable(nor);
+       if (ret)
+               return ret;
+
+       buf = 20;
+       op = (struct spi_mem_op)
+               SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_MT_WR_ANY_REG, 1),
+                          SPI_MEM_OP_ADDR(addr_width, SPINOR_REG_MT_CFR1V, 1),
+                          SPI_MEM_OP_NO_DUMMY,
+                          SPI_MEM_OP_DATA_OUT(1, &buf, 1));
+       ret = spi_mem_exec_op(nor->spi, &op);
+       if (ret)
+               return ret;
+
+       ret = spi_nor_wait_till_ready(nor);
+       if (ret)
+               return ret;
+
+       nor->read_dummy = 20;
+
+       ret = write_enable(nor);
+       if (ret)
+               return ret;
+
+       buf = SPINOR_MT_OCT_DTR;
+       op = (struct spi_mem_op)
+               SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_MT_WR_ANY_REG, 1),
+                          SPI_MEM_OP_ADDR(addr_width, SPINOR_REG_MT_CFR0V, 1),
+                          SPI_MEM_OP_NO_DUMMY,
+                          SPI_MEM_OP_DATA_OUT(1, &buf, 1));
+       ret = spi_mem_exec_op(nor->spi, &op);
+       if (ret) {
+               dev_err(nor->dev, "Failed to enable octal DTR mode\n");
+               return ret;
+       }
+
+       return 0;
+}
+
+static void mt35xu512aba_default_init(struct spi_nor *nor)
+{
+       nor->octal_dtr_enable = spi_nor_micron_octal_dtr_enable;
+}
+
+static void mt35xu512aba_post_sfdp_fixup(struct spi_nor *nor,
+                                        struct spi_nor_flash_parameter *params)
+{
+       /* Set the Fast Read settings. */
+       params->hwcaps.mask |= SNOR_HWCAPS_READ_8_8_8_DTR;
+       spi_nor_set_read_settings(&params->reads[SNOR_CMD_READ_8_8_8_DTR],
+                                 0, 20, SPINOR_OP_MT_DTR_RD,
+                                 SNOR_PROTO_8_8_8_DTR);
+
+       params->hwcaps.mask |= SNOR_HWCAPS_PP_8_8_8_DTR;
+
+       nor->cmd_ext_type = SPI_NOR_EXT_REPEAT;
+       params->rdsr_dummy = 8;
+       params->rdsr_addr_nbytes = 0;
+
+       /*
+        * The BFPT quad enable field is set to a reserved value so the quad
+        * enable function is ignored by spi_nor_parse_bfpt(). Make sure we
+        * disable it.
+        */
+       params->quad_enable = NULL;
+}
+
+static struct spi_nor_fixups mt35xu512aba_fixups = {
+       .default_init = mt35xu512aba_default_init,
+       .post_sfdp = mt35xu512aba_post_sfdp_fixup,
+};
+#endif /* CONFIG_SPI_FLASH_MT35XU */
+
+#if CONFIG_IS_ENABLED(SPI_FLASH_MACRONIX)
+/**
+ * spi_nor_macronix_octal_dtr_enable() - Enable octal DTR on Macronix flashes.
+ * @nor:       pointer to a 'struct spi_nor'
+ *
+ * Set Macronix max dummy cycles 20 to allow the flash to run at fastest frequency.
+ *
+ * Return: 0 on success, -errno otherwise.
+ */
+static int spi_nor_macronix_octal_dtr_enable(struct spi_nor *nor)
+{
+       struct spi_mem_op op;
+       int ret;
+       u8 buf;
+
+       ret = write_enable(nor);
+       if (ret)
+               return ret;
+
+       buf = SPINOR_REG_MXIC_DC_20;
+       op = (struct spi_mem_op)
+               SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WR_CR2, 1),
+                          SPI_MEM_OP_ADDR(4, SPINOR_REG_MXIC_CR2_DC, 1),
+                          SPI_MEM_OP_NO_DUMMY,
+                          SPI_MEM_OP_DATA_OUT(1, &buf, 1));
+
+       ret = spi_mem_exec_op(nor->spi, &op);
+       if (ret)
+               return ret;
+
+       ret = spi_nor_wait_till_ready(nor);
+       if (ret)
+               return ret;
+
+       nor->read_dummy = MXIC_MAX_DC;
+       ret = write_enable(nor);
+       if (ret)
+               return ret;
+
+       buf = SPINOR_REG_MXIC_OPI_DTR_EN;
+       op = (struct spi_mem_op)
+               SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WR_CR2, 1),
+                          SPI_MEM_OP_ADDR(4, SPINOR_REG_MXIC_CR2_MODE, 1),
+                          SPI_MEM_OP_NO_DUMMY,
+                          SPI_MEM_OP_DATA_OUT(1, &buf, 1));
+
+       ret = spi_mem_exec_op(nor->spi, &op);
+       if (ret) {
+               dev_err(nor->dev, "Failed to enable octal DTR mode\n");
+               return ret;
+       }
+       nor->reg_proto = SNOR_PROTO_8_8_8_DTR;
+
+       return 0;
+}
+
+static void macronix_octal_default_init(struct spi_nor *nor)
+{
+       nor->octal_dtr_enable = spi_nor_macronix_octal_dtr_enable;
+}
+
+static void macronix_octal_post_sfdp_fixup(struct spi_nor *nor,
+                                        struct spi_nor_flash_parameter *params)
+{
+       /*
+        * Adding SNOR_HWCAPS_PP_8_8_8_DTR in hwcaps.mask when
+        * SPI_NOR_OCTAL_DTR_READ flag exists.
+        */
+       if (params->hwcaps.mask & SNOR_HWCAPS_READ_8_8_8_DTR)
+               params->hwcaps.mask |= SNOR_HWCAPS_PP_8_8_8_DTR;
+}
+
+static struct spi_nor_fixups macronix_octal_fixups = {
+       .default_init = macronix_octal_default_init,
+       .post_sfdp = macronix_octal_post_sfdp_fixup,
+};
+#endif /* CONFIG_SPI_FLASH_MACRONIX */
+
 /** spi_nor_octal_dtr_enable() - enable Octal DTR I/O if needed
  * @nor:                 pointer to a 'struct spi_nor'
  *
@@ -2914,6 +3668,9 @@ static int spi_nor_octal_dtr_enable(struct spi_nor *nor)
              nor->write_proto == SNOR_PROTO_8_8_8_DTR))
                return 0;
 
+       if (!(nor->flags & SNOR_F_IO_MODE_EN_VOLATILE))
+               return 0;
+
        ret = nor->octal_dtr_enable(nor);
        if (ret)
                return ret;
@@ -2991,7 +3748,12 @@ static int spi_nor_soft_reset(struct spi_nor *nor)
        enum spi_nor_cmd_ext ext;
 
        ext = nor->cmd_ext_type;
-       nor->cmd_ext_type = SPI_NOR_EXT_REPEAT;
+       if (nor->cmd_ext_type == SPI_NOR_EXT_NONE) {
+               nor->cmd_ext_type = SPI_NOR_EXT_REPEAT;
+#if CONFIG_IS_ENABLED(SPI_NOR_BOOT_SOFT_RESET_EXT_INVERT)
+               nor->cmd_ext_type = SPI_NOR_EXT_INVERT;
+#endif /* SPI_NOR_BOOT_SOFT_RESET_EXT_INVERT */
+       }
 
        op = (struct spi_mem_op)SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_SRSTEN, 0),
                        SPI_MEM_OP_NO_DUMMY,
@@ -3041,6 +3803,37 @@ int spi_nor_remove(struct spi_nor *nor)
 
 void spi_nor_set_fixups(struct spi_nor *nor)
 {
+#ifdef CONFIG_SPI_FLASH_SPANSION
+       if (JEDEC_MFR(nor->info) == SNOR_MFR_CYPRESS) {
+               switch (nor->info->id[1]) {
+               case 0x2a: /* S25HL (QSPI, 3.3V) */
+               case 0x2b: /* S25HS (QSPI, 1.8V) */
+                       nor->fixups = &s25hx_t_fixups;
+                       break;
+
+               default:
+                       break;
+               }
+       }
+
+       if (CONFIG_IS_ENABLED(SPI_FLASH_BAR) &&
+           !strcmp(nor->info->name, "s25fl256l"))
+               nor->fixups = &s25fl256l_fixups;
+#endif
+
+#ifdef CONFIG_SPI_FLASH_S28HS512T
+       if (!strcmp(nor->info->name, "s28hs512t"))
+               nor->fixups = &s28hs512t_fixups;
+#endif
+
+#ifdef CONFIG_SPI_FLASH_MT35XU
+       if (!strcmp(nor->info->name, "mt35xu512aba"))
+               nor->fixups = &mt35xu512aba_fixups;
+#endif
+
+#if CONFIG_IS_ENABLED(SPI_FLASH_MACRONIX)
+       nor->fixups = &macronix_octal_fixups;
+#endif /* SPI_FLASH_MACRONIX */
 }
 
 int spi_nor_scan(struct spi_nor *nor)
@@ -3050,6 +3843,11 @@ int spi_nor_scan(struct spi_nor *nor)
        struct mtd_info *mtd = &nor->mtd;
        struct spi_slave *spi = nor->spi;
        int ret;
+       int cfi_mtd_nb = 0;
+
+#ifdef CONFIG_FLASH_CFI_MTD
+       cfi_mtd_nb = CFI_FLASH_BANKS;
+#endif
 
        /* Reset SPI protocol for all commands. */
        nor->reg_proto = SNOR_PROTO_1_1_1;
@@ -3101,8 +3899,12 @@ int spi_nor_scan(struct spi_nor *nor)
        if (ret)
                return ret;
 
-       if (!mtd->name)
-               mtd->name = info->name;
+       if (!mtd->name) {
+               sprintf(nor->mtd_name, "%s%d",
+                       MTD_DEV_TYPE(MTD_DEV_TYPE_NOR),
+                       cfi_mtd_nb + dev_seq(nor->dev));
+               mtd->name = nor->mtd_name;
+       }
        mtd->dev = nor->dev;
        mtd->priv = nor;
        mtd->type = MTD_NORFLASH;
@@ -3121,7 +3923,7 @@ int spi_nor_scan(struct spi_nor *nor)
                        info->flags & SPI_NOR_HAS_LOCK) {
                nor->flash_lock = stm_lock;
                nor->flash_unlock = stm_unlock;
-               nor->flash_is_locked = stm_is_locked;
+               nor->flash_is_unlocked = stm_is_unlocked;
        }
 #endif
 
@@ -3133,7 +3935,7 @@ int spi_nor_scan(struct spi_nor *nor)
        if (info->flags & SPI_NOR_HAS_SST26LOCK) {
                nor->flash_lock = sst26_lock;
                nor->flash_unlock = sst26_unlock;
-               nor->flash_is_locked = sst26_is_locked;
+               nor->flash_is_unlocked = sst26_is_unlocked;
        }
 #endif
 
@@ -3207,7 +4009,7 @@ int spi_nor_scan(struct spi_nor *nor)
 
        nor->rdsr_dummy = params.rdsr_dummy;
        nor->rdsr_addr_nbytes = params.rdsr_addr_nbytes;
-       nor->name = mtd->name;
+       nor->name = info->name;
        nor->size = mtd->size;
        nor->erase_size = mtd->erasesize;
        nor->sector_size = mtd->erasesize;