Main changes:
 * add support for spinand operations working in octal DTR mode
 * randomizer support
 * continuous mode improvements
 * more flashes supported

Signed-off-by: Mikhail Kshevetskiy <[email protected]>
---
 drivers/mtd/nand/spi/Makefile     |   5 +-
 drivers/mtd/nand/spi/core.c       | 504 +++++++++++++++++++++++++-----
 drivers/mtd/nand/spi/dosilicon.c  |  93 ++++++
 drivers/mtd/nand/spi/esmt.c       |  30 +-
 drivers/mtd/nand/spi/fmsh.c       | 175 +++++++++++
 drivers/mtd/nand/spi/foresee.c    |  34 ++
 drivers/mtd/nand/spi/gigadevice.c | 117 ++++++-
 drivers/mtd/nand/spi/heyangtek.c  | 132 ++++++++
 drivers/mtd/nand/spi/macronix.c   |  87 +++++-
 drivers/mtd/nand/spi/micron.c     |   8 +-
 drivers/mtd/nand/spi/toshiba.c    |   3 +-
 drivers/mtd/nand/spi/winbond.c    | 466 +++++++++++++++++++++------
 include/linux/mtd/spinand.h       | 198 ++++++++++--
 13 files changed, 1639 insertions(+), 213 deletions(-)
 create mode 100644 drivers/mtd/nand/spi/dosilicon.c
 create mode 100644 drivers/mtd/nand/spi/heyangtek.c

diff --git a/drivers/mtd/nand/spi/Makefile b/drivers/mtd/nand/spi/Makefile
index a7a0b2cb4b9..106c2d121bc 100644
--- a/drivers/mtd/nand/spi/Makefile
+++ b/drivers/mtd/nand/spi/Makefile
@@ -1,6 +1,7 @@
 # SPDX-License-Identifier: GPL-2.0
 
 spinand-objs := core.o otp.o
-spinand-objs += alliancememory.o ato.o esmt.o fmsh.o foresee.o gigadevice.o 
macronix.o
-spinand-objs += micron.o paragon.o skyhigh.o toshiba.o winbond.o xtx.o
+spinand-objs += alliancememory.o ato.o dosilicon.o esmt.o fmsh.o foresee.o
+spinand-objs += gigadevice.o heyangtek.o macronix.o micron.o paragon.o
+spinand-objs += skyhigh.o toshiba.o winbond.o xtx.o
 obj-$(CONFIG_MTD_SPI_NAND) += spinand.o
diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c
index 5c54e726c4f..4bb5a190bf7 100644
--- a/drivers/mtd/nand/spi/core.c
+++ b/drivers/mtd/nand/spi/core.c
@@ -28,6 +28,7 @@
 #include <ubi_uboot.h>
 #include <dm/device_compat.h>
 #include <dm/devres.h>
+#include <dm/read.h>
 #include <linux/bitops.h>
 #include <linux/bug.h>
 #include <linux/mtd/spinand.h>
@@ -42,10 +43,111 @@ struct spinand_plat {
 /* SPI NAND index visible in MTD names */
 static int spi_nand_idx;
 
+static struct spi_mem_op
+spinand_fill_reset_op(struct spinand_device *spinand)
+{
+       return spinand->op_templates->reset;
+}
+
+static struct spi_mem_op
+spinand_fill_readid_op(struct spinand_device *spinand,
+                      u8 naddr, u8 ndummy, void *buf, unsigned int len)
+{
+       struct spi_mem_op op = spinand->op_templates->readid;
+
+       op.addr.nbytes = naddr;
+       op.dummy.nbytes = ndummy;
+       op.data.buf.in = buf;
+       op.data.nbytes = len;
+
+       return op;
+}
+
+struct spi_mem_op
+spinand_fill_wr_en_op(struct spinand_device *spinand)
+{
+       return spinand->op_templates->wr_en;
+}
+
+static __maybe_unused struct spi_mem_op
+spinand_fill_wr_dis_op(struct spinand_device *spinand)
+{
+       return spinand->op_templates->wr_dis;
+}
+
+struct spi_mem_op
+spinand_fill_set_feature_op(struct spinand_device *spinand, u64 reg, const 
void *valptr)
+{
+       struct spi_mem_op op = spinand->op_templates->set_feature;
+
+       if (op.cmd.dtr && op.cmd.buswidth == 8)
+               reg |= reg << 8;
+
+       op.addr.val = reg;
+       op.data.buf.out = valptr;
+
+       return op;
+}
+
+struct spi_mem_op
+spinand_fill_get_feature_op(struct spinand_device *spinand, u64 reg, void 
*valptr)
+{
+       struct spi_mem_op op = spinand->op_templates->get_feature;
+
+       if (op.cmd.dtr && op.cmd.buswidth == 8)
+               reg |= reg << 8;
+
+       op.addr.val = reg;
+       op.data.buf.in = valptr;
+
+       return op;
+}
+
+static struct spi_mem_op
+spinand_fill_blk_erase_op(struct spinand_device *spinand, u64 addr)
+{
+       struct spi_mem_op op = spinand->op_templates->blk_erase;
+
+       op.addr.val = addr;
+
+       return op;
+}
+
+static struct spi_mem_op
+spinand_fill_page_read_op(struct spinand_device *spinand, u64 addr)
+{
+       struct spi_mem_op op = spinand->op_templates->page_read;
+
+       op.addr.val = addr;
+
+       return op;
+}
+
+static struct spi_mem_op
+spinand_fill_page_read_packed_op(struct spinand_device *spinand, u64 addr)
+{
+       struct spi_mem_op op = spinand->op_templates->page_read;
+
+       op.cmd.opcode |= addr >> 16;
+       op.addr.val = addr & 0xFFFF;
+
+       return op;
+}
+
+struct spi_mem_op
+spinand_fill_prog_exec_op(struct spinand_device *spinand, u64 addr)
+{
+       struct spi_mem_op op = spinand->op_templates->prog_exec;
+
+       op.addr.val = addr;
+
+       return op;
+}
+
 int spinand_read_reg_op(struct spinand_device *spinand, u8 reg, u8 *val)
 {
-       struct spi_mem_op op = SPINAND_GET_FEATURE_1S_1S_1S_OP(reg,
-                                                     spinand->scratchbuf);
+       struct spi_mem_op op = SPINAND_OP(spinand, get_feature,
+                                         reg, spinand->scratchbuf);
        int ret;
 
        ret = spi_mem_exec_op(spinand->slave, &op);
@@ -58,8 +160,8 @@ int spinand_read_reg_op(struct spinand_device *spinand, u8 
reg, u8 *val)
 
 int spinand_write_reg_op(struct spinand_device *spinand, u8 reg, u8 val)
 {
-       struct spi_mem_op op = SPINAND_SET_FEATURE_1S_1S_1S_OP(reg,
-                                                     spinand->scratchbuf);
+       struct spi_mem_op op = SPINAND_OP(spinand, set_feature,
+                                         reg, spinand->scratchbuf);
 
        *spinand->scratchbuf = val;
        return spi_mem_exec_op(spinand->slave, &op);
@@ -199,20 +301,29 @@ static int spinand_init_cfg_cache(struct spinand_device 
*spinand)
        return 0;
 }
 
-static int spinand_init_quad_enable(struct spinand_device *spinand)
+static int spinand_init_quad_enable(struct spinand_device *spinand,
+                                   bool enable)
 {
-       bool enable = false;
+       struct nand_device *nand = spinand_to_nand(spinand);
+       unsigned int target;
+       int ret;
 
-       if (!(spinand->flags & SPINAND_HAS_QE_BIT))
-               return 0;
+       /*
+        * QE is a per-die setting on some devices. Program each target
+        * individually when enabling or disabling quad I/O mode.
+        */
+       for (target = 0; target < nand->memorg.ntargets; target++) {
+               ret = spinand_select_target(spinand, target);
+               if (ret)
+                       return ret;
 
-       if (spinand->op_templates.read_cache->data.buswidth == 4 ||
-           spinand->op_templates.write_cache->data.buswidth == 4 ||
-           spinand->op_templates.update_cache->data.buswidth == 4)
-               enable = true;
+               ret = spinand_upd_cfg(spinand, CFG_QUAD_ENABLE,
+                                     enable ? CFG_QUAD_ENABLE : 0);
+               if (ret)
+                       return ret;
+       }
 
-       return spinand_upd_cfg(spinand, CFG_QUAD_ENABLE,
-                              enable ? CFG_QUAD_ENABLE : 0);
+       return 0;
 }
 
 static int spinand_ecc_enable(struct spinand_device *spinand,
@@ -359,7 +470,7 @@ static void spinand_ondie_ecc_save_status(struct 
nand_device *nand, u8 status)
 
 int spinand_write_enable_op(struct spinand_device *spinand)
 {
-       struct spi_mem_op op = SPINAND_WR_EN_DIS_1S_0_0_OP(true);
+       struct spi_mem_op op = SPINAND_OP(spinand, wr_en);
 
        return spi_mem_exec_op(spinand->slave, &op);
 }
@@ -369,7 +480,10 @@ static int spinand_load_page_op(struct spinand_device 
*spinand,
 {
        struct nand_device *nand = spinand_to_nand(spinand);
        unsigned int row = nanddev_pos_to_row(nand, &req->pos);
-       struct spi_mem_op op = SPINAND_PAGE_READ_1S_1S_0_OP(row);
+       bool packed = spinand->flags & SPINAND_ODTR_PACKED_PAGE_READ;
+       struct spi_mem_op op = packed ?
+               SPINAND_OP(spinand, page_read_packed, row) :
+               SPINAND_OP(spinand, page_read, row);
 
        return spi_mem_exec_op(spinand->slave, &op);
 }
@@ -403,10 +517,14 @@ static int spinand_read_from_cache_op(struct 
spinand_device *spinand,
                }
        }
 
-       if (req->mode == MTD_OPS_RAW)
-               rdesc = spinand->dirmaps[req->pos.plane].rdesc;
+       rdesc = spinand->dirmaps[req->pos.plane].rdesc;
+
+       if (spinand->op_templates->cont_read_cache && req->continuous)
+               rdesc->info.op_tmpl = &rdesc->info.secondary_op_tmpl;
        else
-               rdesc = spinand->dirmaps[req->pos.plane].rdesc_ecc;
+               rdesc->info.op_tmpl = &rdesc->info.primary_op_tmpl;
+
+       rdesc->info.op_tmpl->data.ecc = false;
 
        if (spinand->flags & SPINAND_HAS_READ_PLANE_SELECT_BIT)
                column |= req->pos.plane << fls(nanddev_page_size(nand));
@@ -495,10 +613,8 @@ static int spinand_write_to_cache_op(struct spinand_device 
*spinand,
                               req->ooblen);
        }
 
-       if (req->mode == MTD_OPS_RAW)
-               wdesc = spinand->dirmaps[req->pos.plane].wdesc;
-       else
-               wdesc = spinand->dirmaps[req->pos.plane].wdesc_ecc;
+       wdesc = spinand->dirmaps[req->pos.plane].wdesc;
+       wdesc->info.op_tmpl->data.ecc = false;
 
        if (spinand->flags & SPINAND_HAS_PROG_PLANE_SELECT_BIT)
                column |= req->pos.plane << fls(nanddev_page_size(nand));
@@ -524,7 +640,7 @@ static int spinand_program_op(struct spinand_device 
*spinand,
 {
        struct nand_device *nand = spinand_to_nand(spinand);
        unsigned int row = nanddev_pos_to_row(nand, &req->pos);
-       struct spi_mem_op op = SPINAND_PROG_EXEC_1S_1S_0_OP(row);
+       struct spi_mem_op op = SPINAND_OP(spinand, prog_exec, row);
 
        return spi_mem_exec_op(spinand->slave, &op);
 }
@@ -534,7 +650,7 @@ static int spinand_erase_op(struct spinand_device *spinand,
 {
        struct nand_device *nand = spinand_to_nand(spinand);
        unsigned int row = nanddev_pos_to_row(nand, pos);
-       struct spi_mem_op op = SPINAND_BLK_ERASE_1S_1S_0_OP(row);
+       struct spi_mem_op op = SPINAND_OP(spinand, blk_erase, row);
 
        return spi_mem_exec_op(spinand->slave, &op);
 }
@@ -594,8 +710,8 @@ out:
 static int spinand_read_id_op(struct spinand_device *spinand, u8 naddr,
                              u8 ndummy, u8 *buf)
 {
-       struct spi_mem_op op = SPINAND_READID_1S_1S_1S_OP(
-               naddr, ndummy, spinand->scratchbuf, SPINAND_MAX_ID_LEN);
+       struct spi_mem_op op = SPINAND_OP(spinand, readid,
+                                         naddr, ndummy, spinand->scratchbuf, 
SPINAND_MAX_ID_LEN);
        int ret;
 
        ret = spi_mem_exec_op(spinand->slave, &op);
@@ -607,7 +723,7 @@ static int spinand_read_id_op(struct spinand_device 
*spinand, u8 naddr,
 
 static int spinand_reset_op(struct spinand_device *spinand)
 {
-       struct spi_mem_op op = SPINAND_RESET_1S_0_0_OP;
+       struct spi_mem_op op = SPINAND_OP(spinand, reset);
        int ret;
 
        ret = spi_mem_exec_op(spinand->slave, &op);
@@ -719,7 +835,7 @@ static int spinand_mtd_regular_page_read(struct mtd_info 
*mtd, loff_t from,
        bool disable_ecc = false;
        bool ecc_failed = false;
        unsigned int retry_mode = 0;
-       int ret;
+       int ret = 0;
 
        old_stats = mtd->ecc_stats;
 
@@ -801,6 +917,12 @@ static int spinand_mtd_continuous_page_read(struct 
mtd_info *mtd, loff_t from,
         * Each data read must be a multiple of 4-bytes and full pages should 
be read;
         * otherwise, the data output might get out of sequence from one read 
command
         * to another.
+        *
+        * Continuous reads never cross LUN boundaries. Some devices don't
+        * support crossing planes boundaries. Some devices don't even support
+        * crossing blocks boundaries. The common case being to read through 
UBI,
+        * we will very rarely read two consequent blocks or more, so let's 
only enable
+        * continuous reads when reading within the same erase block.
         */
        nanddev_io_for_each_block(nand, NAND_PAGE_READ, from, ops, &iter) {
                schedule();
@@ -858,6 +980,14 @@ static void spinand_cont_read_init(struct spinand_device 
*spinand)
 {
        /* OOBs cannot be retrieved so external/on-host ECC engine won't work */
        if (spinand->set_cont_read) {
+               /*
+                * Ensure continuous read is disabled on probe.
+                * Some devices retain this state across soft reset,
+                * which leaves the OOB area inaccessible and results
+                * in false positive returns from spinand_isbad().
+                */
+               spinand_cont_read_enable(spinand, false);
+
                spinand->cont_read_possible = true;
        }
 }
@@ -879,19 +1009,6 @@ static bool spinand_use_cont_read(struct mtd_info *mtd, 
loff_t from,
        nanddev_offs_to_pos(nand, from, &start_pos);
        nanddev_offs_to_pos(nand, from + ops->len - 1, &end_pos);
 
-       /*
-        * Continuous reads never cross LUN boundaries. Some devices don't
-        * support crossing planes boundaries. Some devices don't even support
-        * crossing blocks boundaries. The common case being to read through 
UBI,
-        * we will very rarely read two consequent blocks or more, so it is 
safer
-        * and easier (can be improved) to only enable continuous reads when
-        * reading within the same erase block.
-        */
-       if (start_pos.target != end_pos.target ||
-           start_pos.plane != end_pos.plane ||
-           start_pos.eraseblock != end_pos.eraseblock)
-               return false;
-
        return start_pos.page < end_pos.page;
 }
 
@@ -1154,6 +1271,7 @@ static struct spi_mem_dirmap_desc *spinand_create_rdesc(
                 * its spi controller, use regular reading
                 */
                spinand->cont_read_possible = false;
+               memset(&info->secondary_op_tmpl, 0, 
sizeof(info->secondary_op_tmpl));
 
                info->length = nanddev_page_size(nand) +
                               nanddev_per_page_oobsize(nand);
@@ -1169,19 +1287,31 @@ static int spinand_create_dirmap(struct spinand_device 
*spinand,
        struct nand_device *nand = spinand_to_nand(spinand);
        struct spi_mem_dirmap_info info = { 0 };
        struct spi_mem_dirmap_desc *desc;
+       bool enable_ecc = false, secondary_op = false;
+
+       if (spinand->cont_read_possible && 
spinand->op_templates->cont_read_cache)
+               secondary_op = true;
 
        /* The plane number is passed in MSB just above the column address */
        info.offset = plane << fls(nand->memorg.pagesize);
 
+       /* Write descriptor */
        info.length = nanddev_page_size(nand) + nanddev_per_page_oobsize(nand);
-       info.primary_op_tmpl = *spinand->op_templates.update_cache;
+       info.primary_op_tmpl = *spinand->op_templates->update_cache;
+       info.primary_op_tmpl.data.ecc = enable_ecc;
        desc = spi_mem_dirmap_create(spinand->slave, &info);
        if (IS_ERR(desc))
                return PTR_ERR(desc);
 
        spinand->dirmaps[plane].wdesc = desc;
 
-       info.primary_op_tmpl = *spinand->op_templates.read_cache;
+       /* Read descriptor */
+       info.primary_op_tmpl = *spinand->op_templates->read_cache;
+       info.primary_op_tmpl.data.ecc = enable_ecc;
+       if (secondary_op) {
+               info.secondary_op_tmpl = 
*spinand->op_templates->cont_read_cache;
+               info.secondary_op_tmpl.data.ecc = enable_ecc;
+       }
        desc = spinand_create_rdesc(spinand, &info);
        if (IS_ERR(desc)) {
                spi_mem_dirmap_destroy(spinand->dirmaps[plane].wdesc);
@@ -1190,9 +1320,6 @@ static int spinand_create_dirmap(struct spinand_device 
*spinand,
 
        spinand->dirmaps[plane].rdesc = desc;
 
-       spinand->dirmaps[plane].wdesc_ecc = spinand->dirmaps[plane].wdesc;
-       spinand->dirmaps[plane].rdesc_ecc = spinand->dirmaps[plane].rdesc;
-
        return 0;
 }
 
@@ -1217,6 +1344,19 @@ static int spinand_create_dirmaps(struct spinand_device 
*spinand)
        return 0;
 }
 
+static int spinand_randomizer_init(struct spinand_device *spinand)
+{
+       struct udevice *dev = spinand->slave->dev;
+       bool enabled;
+
+       if (!spinand->set_randomizer)
+               return 0;
+
+       enabled = dev_read_bool(dev, "nand-randomizer");
+
+       return spinand->set_randomizer(spinand, enabled);
+}
+
 static const struct nand_ops spinand_ops = {
        .erase = spinand_erase,
        .markbad = spinand_markbad,
@@ -1226,10 +1366,13 @@ static const struct nand_ops spinand_ops = {
 static const struct spinand_manufacturer *spinand_manufacturers[] = {
        &alliancememory_spinand_manufacturer,
        &ato_spinand_manufacturer,
+       &dosilicon_spinand_manufacturer,
+       &esmt_8c_spinand_manufacturer,
        &esmt_c8_spinand_manufacturer,
        &fmsh_spinand_manufacturer,
        &foresee_spinand_manufacturer,
        &gigadevice_spinand_manufacturer,
+       &heyangtek_spinand_manufacturer,
        &macronix_spinand_manufacturer,
        &micron_spinand_manufacturer,
        &paragon_spinand_manufacturer,
@@ -1305,12 +1448,6 @@ static int spinand_manufacturer_init(struct 
spinand_device *spinand)
                        return ret;
        }
 
-       if (spinand->configure_chip) {
-               ret = spinand->configure_chip(spinand);
-               if (ret)
-                       return ret;
-       }
-
        return 0;
 }
 
@@ -1321,8 +1458,104 @@ static void spinand_manufacturer_cleanup(struct 
spinand_device *spinand)
                return spinand->manufacturer->ops->cleanup(spinand);
 }
 
+bool spinand_op_is_odtr(const struct spi_mem_op *op)
+{
+       return op->cmd.dtr && op->cmd.buswidth == 8;
+}
+
+static void spinand_init_ssdr_templates(struct spinand_device *spinand)
+{
+       struct spinand_mem_ops *tmpl = &spinand->ssdr_op_templates;
+
+       tmpl->reset = (struct spi_mem_op)SPINAND_RESET_1S_0_0_OP;
+       tmpl->readid = (struct spi_mem_op)SPINAND_READID_1S_1S_1S_OP(0, 0, 
NULL, 0);
+       tmpl->wr_en = (struct spi_mem_op)SPINAND_WR_EN_1S_0_0_OP;
+       tmpl->wr_dis = (struct spi_mem_op)SPINAND_WR_DIS_1S_0_0_OP;
+       tmpl->set_feature = (struct 
spi_mem_op)SPINAND_SET_FEATURE_1S_1S_1S_OP(0, NULL);
+       tmpl->get_feature = (struct 
spi_mem_op)SPINAND_GET_FEATURE_1S_1S_1S_OP(0, NULL);
+       tmpl->blk_erase = (struct spi_mem_op)SPINAND_BLK_ERASE_1S_1S_0_OP(0);
+       tmpl->page_read = (struct spi_mem_op)SPINAND_PAGE_READ_1S_1S_0_OP(0);
+       tmpl->prog_exec = (struct spi_mem_op)SPINAND_PROG_EXEC_1S_1S_0_OP(0);
+       spinand->op_templates = &spinand->ssdr_op_templates;
+       spinand->bus_iface = SSDR;
+}
+
+static int spinand_support_vendor_ops(struct spinand_device *spinand,
+                                     const struct spinand_info *info,
+                                     enum spinand_bus_interface iface)
+{
+       int i;
+
+       if (!info->vendor_ops)
+               return 0;
+       /*
+        * The vendor ops array is only used in order to verify this chip and 
all its memory
+        * operations are supported. If we see patterns emerging, we could 
ideally name these
+        * operations and define them at the SPI NAND core level instead.
+        * For now, this only serves as a sanity check.
+        */
+       for (i = 0; i < info->vendor_ops->nops; i++) {
+               const struct spi_mem_op *op = &info->vendor_ops->ops[i];
+
+               if ((iface == SSDR && spinand_op_is_odtr(op)) ||
+                   (iface == ODTR && !spinand_op_is_odtr(op)))
+                       continue;
+
+               if (!spi_mem_supports_op(spinand->slave, op))
+                       return -EOPNOTSUPP;
+       }
+
+       return 0;
+}
+
+static int spinand_init_odtr_instruction_set(struct spinand_device *spinand)
+{
+       struct spinand_mem_ops *tmpl = &spinand->odtr_op_templates;
+
+       tmpl->reset = (struct spi_mem_op)SPINAND_RESET_8D_0_0_OP;
+       if (!spi_mem_supports_op(spinand->slave, &tmpl->reset))
+               return -EOPNOTSUPP;
+
+       tmpl->readid = (struct spi_mem_op)SPINAND_READID_8D_8D_8D_OP(0, 0, 
NULL, 0);
+       if (!spi_mem_supports_op(spinand->slave, &tmpl->readid))
+               return -EOPNOTSUPP;
+
+       tmpl->wr_en = (struct spi_mem_op)SPINAND_WR_EN_8D_0_0_OP;
+       if (!spi_mem_supports_op(spinand->slave, &tmpl->wr_en))
+               return -EOPNOTSUPP;
+
+       tmpl->wr_dis = (struct spi_mem_op)SPINAND_WR_DIS_8D_0_0_OP;
+       if (!spi_mem_supports_op(spinand->slave, &tmpl->wr_dis))
+               return -EOPNOTSUPP;
+
+       tmpl->set_feature = (struct 
spi_mem_op)SPINAND_SET_FEATURE_8D_8D_8D_OP(0, NULL);
+       if (!spi_mem_supports_op(spinand->slave, &tmpl->set_feature))
+               return -EOPNOTSUPP;
+
+       tmpl->get_feature = (struct 
spi_mem_op)SPINAND_GET_FEATURE_8D_8D_8D_OP(0, NULL);
+       if (!spi_mem_supports_op(spinand->slave, &tmpl->get_feature))
+               return -EOPNOTSUPP;
+
+       tmpl->blk_erase = (struct spi_mem_op)SPINAND_BLK_ERASE_8D_8D_0_OP(0);
+       if (!spi_mem_supports_op(spinand->slave, &tmpl->blk_erase))
+               return -EOPNOTSUPP;
+
+       if (spinand->flags & SPINAND_ODTR_PACKED_PAGE_READ)
+               tmpl->page_read = (struct 
spi_mem_op)SPINAND_PAGE_READ_PACKED_8D_8D_0_OP(0);
+       else
+               tmpl->page_read = (struct 
spi_mem_op)SPINAND_PAGE_READ_8D_8D_0_OP(0);
+       if (!spi_mem_supports_op(spinand->slave, &tmpl->page_read))
+               return -EOPNOTSUPP;
+
+       tmpl->prog_exec = (struct spi_mem_op)SPINAND_PROG_EXEC_8D_8D_0_OP(0);
+       if (!spi_mem_supports_op(spinand->slave, &tmpl->prog_exec))
+               return -EOPNOTSUPP;
+
+       return 0;
+}
+
 static const struct spi_mem_op *
-spinand_select_op_variant(struct spinand_device *spinand,
+spinand_select_op_variant(struct spinand_device *spinand, enum 
spinand_bus_interface iface,
                          const struct spinand_op_variants *variants)
 {
        struct nand_device *nand = spinand_to_nand(spinand);
@@ -1336,6 +1569,10 @@ spinand_select_op_variant(struct spinand_device *spinand,
                unsigned int nbytes;
                int ret;
 
+               if ((iface == SSDR && spinand_op_is_odtr(&op)) ||
+                   (iface == ODTR && !spinand_op_is_odtr(&op)))
+                       continue;
+
                nbytes = nanddev_per_page_oobsize(nand) +
                         nanddev_page_size(nand);
 
@@ -1426,29 +1663,97 @@ int spinand_match_and_init(struct spinand_device 
*spinand,
                spinand->user_otp = &table[i].user_otp;
                spinand->read_retries = table[i].read_retries;
                spinand->set_read_retry = table[i].set_read_retry;
+               spinand->set_randomizer = table[i].set_randomizer;
 
-               op = spinand_select_op_variant(spinand,
+               /* I/O variants selection with single-spi SDR commands */
+
+               op = spinand_select_op_variant(spinand, SSDR,
                                               info->op_variants.read_cache);
                if (!op)
-                       return -ENOTSUPP;
+                       return -EOPNOTSUPP;
 
-               spinand->op_templates.read_cache = op;
+               spinand->ssdr_op_templates.read_cache = op;
 
-               op = spinand_select_op_variant(spinand,
+               op = spinand_select_op_variant(spinand, SSDR,
                                               info->op_variants.write_cache);
                if (!op)
-                       return -ENOTSUPP;
+                       return -EOPNOTSUPP;
 
-               spinand->op_templates.write_cache = op;
+               spinand->ssdr_op_templates.write_cache = op;
 
-               op = spinand_select_op_variant(spinand,
+               op = spinand_select_op_variant(spinand, SSDR,
                                               info->op_variants.update_cache);
-               spinand->op_templates.update_cache = op;
+               if (!op)
+                       return -EOPNOTSUPP;
+
+               spinand->ssdr_op_templates.update_cache = op;
+
+               ret = spinand_support_vendor_ops(spinand, info, SSDR);
+               if (ret)
+                       return ret;
+
+               if (info->op_variants.cont_read_cache) {
+                       op = spinand_select_op_variant(spinand, SSDR,
+                                                      
info->op_variants.cont_read_cache);
+                       if (op) {
+                               const struct spi_mem_op *read_op;
+
+                               read_op = spinand->ssdr_op_templates.read_cache;
+
+                               /*
+                                * Sometimes the fastest continuous read 
variant may not
+                                * be supported. In this case, prefer to use 
the fastest
+                                * read from cache variant and disable 
continuous reads.
+                                */
+                               if (read_op->cmd.buswidth > op->cmd.buswidth ||
+                                   (read_op->cmd.dtr && !op->cmd.dtr) ||
+                                   read_op->addr.buswidth > op->addr.buswidth 
||
+                                   (read_op->addr.dtr && !op->addr.dtr) ||
+                                   read_op->data.buswidth > op->data.buswidth 
||
+                                   (read_op->data.dtr && !op->data.dtr))
+                                       spinand->cont_read_possible = false;
+                               else
+                                       
spinand->ssdr_op_templates.cont_read_cache = op;
+                       } else {
+                               spinand->cont_read_possible = false;
+                       }
+               }
+
+               /* I/O variants selection with octo-spi DDR commands (optional) 
*/
+
+               ret = spinand_init_odtr_instruction_set(spinand);
+               if (ret)
+                       return 0;
+
+               ret = spinand_support_vendor_ops(spinand, info, ODTR);
+               if (ret)
+                       return 0;
+
+               op = spinand_select_op_variant(spinand, ODTR,
+                                              info->op_variants.read_cache);
+               spinand->odtr_op_templates.read_cache = op;
+
+               op = spinand_select_op_variant(spinand, ODTR,
+                                              info->op_variants.write_cache);
+               spinand->odtr_op_templates.write_cache = op;
+
+               op = spinand_select_op_variant(spinand, ODTR,
+                                              info->op_variants.update_cache);
+               spinand->odtr_op_templates.update_cache = op;
+
+               if (info->op_variants.cont_read_cache) {
+                       op = spinand_select_op_variant(spinand, ODTR,
+                                                      
info->op_variants.cont_read_cache);
+                       if (op)
+                               spinand->odtr_op_templates.cont_read_cache = op;
+                       else
+                               spinand->cont_read_possible = false;
+               }
 
                return 0;
        }
 
-       return -ENOTSUPP;
+       return -EOPNOTSUPP;
 }
 
 static int spinand_detect(struct spinand_device *spinand)
@@ -1484,6 +1789,56 @@ static int spinand_detect(struct spinand_device *spinand)
        return 0;
 }
 
+static int spinand_configure_chip(struct spinand_device *spinand)
+{
+       bool odtr = false, quad_enable = false;
+       int ret;
+
+       if (spinand->odtr_op_templates.read_cache &&
+           spinand->odtr_op_templates.write_cache &&
+           spinand->odtr_op_templates.update_cache)
+               odtr = true;
+
+       if (odtr) {
+               if (!spinand->configure_chip)
+                       goto try_ssdr;
+
+               /* ODTR bus interface configuration happens here */
+               ret = spinand->configure_chip(spinand, ODTR);
+               if (ret) {
+                       spinand->odtr_op_templates.read_cache = NULL;
+                       spinand->odtr_op_templates.write_cache = NULL;
+                       spinand->odtr_op_templates.update_cache = NULL;
+                       goto try_ssdr;
+               }
+
+               spinand->op_templates = &spinand->odtr_op_templates;
+               spinand->bus_iface = ODTR;
+
+               return 0;
+       }
+
+try_ssdr:
+       if (spinand->flags & SPINAND_HAS_QE_BIT) {
+               if (spinand->ssdr_op_templates.read_cache->data.buswidth == 4 ||
+                   spinand->ssdr_op_templates.write_cache->data.buswidth == 4 
||
+                   spinand->ssdr_op_templates.update_cache->data.buswidth == 4)
+                       quad_enable = true;
+
+               ret = spinand_init_quad_enable(spinand, quad_enable);
+               if (ret)
+                       return ret;
+       }
+
+       if (spinand->configure_chip) {
+               ret = spinand->configure_chip(spinand, SSDR);
+               if (ret)
+                       return ret;
+       }
+
+       return 0;
+}
+
 static int spinand_init_flash(struct spinand_device *spinand)
 {
        struct udevice *dev = spinand->slave->dev;
@@ -1494,10 +1849,6 @@ static int spinand_init_flash(struct spinand_device 
*spinand)
        if (ret)
                return ret;
 
-       ret = spinand_init_quad_enable(spinand);
-       if (ret)
-               return ret;
-
        ret = spinand_upd_cfg(spinand, CFG_OTP_ENABLE, 0);
        if (ret)
                return ret;
@@ -1510,19 +1861,25 @@ static int spinand_init_flash(struct spinand_device 
*spinand)
                return ret;
        }
 
+       ret = spinand_configure_chip(spinand);
+       if (ret)
+               goto manuf_cleanup;
+
        /* After power up, all blocks are locked, so unlock them here. */
        for (i = 0; i < nand->memorg.ntargets; i++) {
                ret = spinand_select_target(spinand, i);
                if (ret)
-                       break;
+                       goto manuf_cleanup;
 
                ret = spinand_lock_block(spinand, BL_ALL_UNLOCKED);
                if (ret)
-                       break;
+                       goto manuf_cleanup;
        }
 
-       if (ret)
-               spinand_manufacturer_cleanup(spinand);
+       return 0;
+
+manuf_cleanup:
+       spinand_manufacturer_cleanup(spinand);
 
        return ret;
 }
@@ -1542,6 +1899,8 @@ static int spinand_init(struct spinand_device *spinand)
        if (!spinand->scratchbuf)
                return -ENOMEM;
 
+       spinand_init_ssdr_templates(spinand);
+
        ret = spinand_detect(spinand);
        if (ret)
                goto err_free_bufs;
@@ -1582,6 +1941,9 @@ static int spinand_init(struct spinand_device *spinand)
         * ECC initialization must have happened previously.
         */
        spinand_cont_read_init(spinand);
+       ret = spinand_randomizer_init(spinand);
+       if (ret)
+               goto err_cleanup_nanddev;
 
        mtd->_read_oob = spinand_mtd_read;
        mtd->_write_oob = spinand_mtd_write;
diff --git a/drivers/mtd/nand/spi/dosilicon.c b/drivers/mtd/nand/spi/dosilicon.c
new file mode 100644
index 00000000000..fb6677fc9f3
--- /dev/null
+++ b/drivers/mtd/nand/spi/dosilicon.c
@@ -0,0 +1,93 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Author: Ahmed Naseef <[email protected]>
+ */
+
+#ifndef __UBOOT__
+#include <linux/device.h>
+#include <linux/kernel.h>
+#endif
+#include <linux/mtd/spinand.h>
+
+#define SPINAND_MFR_DOSILICON        0xE5
+
+static SPINAND_OP_VARIANTS(read_cache_variants,
+               SPINAND_PAGE_READ_FROM_CACHE_1S_1S_4S_OP(0, 1, NULL, 0, 0),
+               SPINAND_PAGE_READ_FROM_CACHE_1S_1S_2S_OP(0, 1, NULL, 0, 0),
+               SPINAND_PAGE_READ_FROM_CACHE_FAST_1S_1S_1S_OP(0, 1, NULL, 0, 0),
+               SPINAND_PAGE_READ_FROM_CACHE_1S_1S_1S_OP(0, 1, NULL, 0, 0));
+
+static SPINAND_OP_VARIANTS(write_cache_variants,
+               SPINAND_PROG_LOAD_1S_1S_4S_OP(true, 0, NULL, 0),
+               SPINAND_PROG_LOAD_1S_1S_1S_OP(true, 0, NULL, 0));
+
+static SPINAND_OP_VARIANTS(update_cache_variants,
+               SPINAND_PROG_LOAD_1S_1S_4S_OP(false, 0, NULL, 0),
+               SPINAND_PROG_LOAD_1S_1S_1S_OP(false, 0, NULL, 0));
+
+static int ds35xx_ooblayout_ecc(struct mtd_info *mtd, int section,
+                               struct mtd_oob_region *region)
+{
+       if (section > 3)
+               return -ERANGE;
+
+       region->offset = 8 + (section * 16);
+       region->length = 8;
+
+       return 0;
+}
+
+static int ds35xx_ooblayout_free(struct mtd_info *mtd, int section,
+                                struct mtd_oob_region *region)
+{
+       if (section > 3)
+               return -ERANGE;
+
+       if (section == 0) {
+               /* reserve 2 bytes for the BBM */
+               region->offset = 2;
+               region->length = 6;
+       } else {
+               region->offset = section * 16;
+               region->length = 8;
+       }
+
+       return 0;
+}
+
+static const struct mtd_ooblayout_ops ds35xx_ooblayout = {
+       .ecc = ds35xx_ooblayout_ecc,
+       .rfree = ds35xx_ooblayout_free,
+};
+
+static const struct spinand_info dosilicon_spinand_table[] = {
+       SPINAND_INFO("DS35Q1GA",
+               SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x71),
+               NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1),
+               NAND_ECCREQ(4, 512),
+               SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
+                                        &write_cache_variants,
+                                        &update_cache_variants),
+               SPINAND_HAS_QE_BIT,
+               SPINAND_ECCINFO(&ds35xx_ooblayout, NULL)),
+       SPINAND_INFO("DS35M1GA",
+               SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x21),
+               NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1),
+               NAND_ECCREQ(4, 512),
+               SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
+                                        &write_cache_variants,
+                                        &update_cache_variants),
+               SPINAND_HAS_QE_BIT,
+               SPINAND_ECCINFO(&ds35xx_ooblayout, NULL)),
+};
+
+static const struct spinand_manufacturer_ops dosilicon_spinand_manuf_ops = {
+};
+
+const struct spinand_manufacturer dosilicon_spinand_manufacturer = {
+       .id = SPINAND_MFR_DOSILICON,
+       .name = "Dosilicon",
+       .chips = dosilicon_spinand_table,
+       .nchips = ARRAY_SIZE(dosilicon_spinand_table),
+       .ops = &dosilicon_spinand_manuf_ops,
+};
diff --git a/drivers/mtd/nand/spi/esmt.c b/drivers/mtd/nand/spi/esmt.c
index 6a46f3a3bfc..1bfb13bd764 100644
--- a/drivers/mtd/nand/spi/esmt.c
+++ b/drivers/mtd/nand/spi/esmt.c
@@ -17,6 +17,7 @@
 
 /* ESMT uses GigaDevice 0xc8 JECDEC ID on some SPI NANDs */
 #define SPINAND_MFR_ESMT_C8                    0xc8
+#define SPINAND_MFR_ESMT_8C                    0x8c
 
 #define ESMT_F50L1G41LB_CFG_OTP_PROTECT                BIT(7)
 #define ESMT_F50L1G41LB_CFG_OTP_LOCK           \
@@ -142,8 +143,8 @@ static int f50l1g41lb_user_otp_info(struct spinand_device 
*spinand, size_t len,
 static int f50l1g41lb_otp_lock(struct spinand_device *spinand, loff_t from,
                               size_t len)
 {
-       struct spi_mem_op write_op = SPINAND_WR_EN_DIS_1S_0_0_OP(true);
-       struct spi_mem_op exec_op = SPINAND_PROG_EXEC_1S_1S_0_OP(0);
+       struct spi_mem_op write_op = SPINAND_OP(spinand, wr_en);
+       struct spi_mem_op exec_op = SPINAND_OP(spinand, prog_exec, 0);
        u8 status;
        int ret;
 
@@ -189,6 +190,21 @@ static const struct spinand_fact_otp_ops 
f50l1g41lb_fact_otp_ops = {
        .read = spinand_fact_otp_read,
 };
 
+
+static const struct spinand_info esmt_8c_spinand_table[] = {
+       SPINAND_INFO("F50L1G41LC",
+                    SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x2C),
+                    NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1),
+                    NAND_ECCREQ(1, 512),
+                    SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
+                                             &write_cache_variants,
+                                             &update_cache_variants),
+                    0,
+                    SPINAND_ECCINFO(&f50l1g41lb_ooblayout, NULL),
+                    SPINAND_USER_OTP_INFO(28, 2, &f50l1g41lb_user_otp_ops),
+                    SPINAND_FACT_OTP_INFO(2, 0, &f50l1g41lb_fact_otp_ops)),
+};
+
 static const struct spinand_info esmt_c8_spinand_table[] = {
        SPINAND_INFO("F50L1G41LB",
                     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x01, 0x7f,
@@ -204,7 +220,7 @@ static const struct spinand_info esmt_c8_spinand_table[] = {
                     SPINAND_FACT_OTP_INFO(2, 0, &f50l1g41lb_fact_otp_ops)),
        SPINAND_INFO("F50D1G41LB",
                     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x11, 0x7f,
-                               0x7f),
+                               0x7f, 0x7f),
                     NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1),
                     NAND_ECCREQ(1, 512),
                     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
@@ -229,6 +245,14 @@ static const struct spinand_info esmt_c8_spinand_table[] = 
{
 static const struct spinand_manufacturer_ops esmt_spinand_manuf_ops = {
 };
 
+const struct spinand_manufacturer esmt_8c_spinand_manufacturer = {
+       .id = SPINAND_MFR_ESMT_8C,
+       .name = "ESMT",
+       .chips = esmt_8c_spinand_table,
+       .nchips = ARRAY_SIZE(esmt_8c_spinand_table),
+       .ops = &esmt_spinand_manuf_ops,
+};
+
 const struct spinand_manufacturer esmt_c8_spinand_manufacturer = {
        .id = SPINAND_MFR_ESMT_C8,
        .name = "ESMT",
diff --git a/drivers/mtd/nand/spi/fmsh.c b/drivers/mtd/nand/spi/fmsh.c
index 80837b7dd42..f0d691fa44b 100644
--- a/drivers/mtd/nand/spi/fmsh.c
+++ b/drivers/mtd/nand/spi/fmsh.c
@@ -11,6 +11,23 @@
 #endif
 #include <linux/mtd/spinand.h>
 
+#define FM25G01B_STATUS_ECC_MASK               (7 << 4)
+       #define FM25G01B_STATUS_ECC_NO_BITFLIPS         (0 << 4)
+       #define FM25G01B_STATUS_ECC_1_3_BITFLIPS        (1 << 4)
+       #define FM25G01B_STATUS_ECC_4_BITFLIPS          (2 << 4)
+       #define FM25G01B_STATUS_ECC_5_BITFLIPS          (3 << 4)
+       #define FM25G01B_STATUS_ECC_6_BITFLIPS          (4 << 4)
+       #define FM25G01B_STATUS_ECC_7_BITFLIPS          (5 << 4)
+       #define FM25G01B_STATUS_ECC_8_BITFLIPS          (6 << 4)
+       #define FM25G01B_STATUS_ECC_UNCOR_ERROR         (7 << 4)
+
+#define FM25S01BI3_STATUS_ECC_MASK             (7 << 4)
+       #define FM25S01BI3_STATUS_ECC_NO_BITFLIPS       (0 << 4)
+       #define FM25S01BI3_STATUS_ECC_1_3_BITFLIPS      (1 << 4)
+       #define FM25S01BI3_STATUS_ECC_UNCOR_ERROR       (2 << 4)
+       #define FM25S01BI3_STATUS_ECC_4_6_BITFLIPS      (3 << 4)
+       #define FM25S01BI3_STATUS_ECC_7_8_BITFLIPS      (5 << 4)
+
 #define SPINAND_MFR_FMSH               0xA1
 
 static SPINAND_OP_VARIANTS(read_cache_variants,
@@ -29,6 +46,74 @@ static SPINAND_OP_VARIANTS(update_cache_variants,
                SPINAND_PROG_LOAD_1S_1S_4S_OP(false, 0, NULL, 0),
                SPINAND_PROG_LOAD_1S_1S_1S_OP(false, 0, NULL, 0));
 
+static SPINAND_OP_VARIANTS(fm25g_read_cache_variants,
+               SPINAND_PAGE_READ_FROM_CACHE_1S_4S_4S_OP(0, 1, NULL, 0, 0),
+               SPINAND_PAGE_READ_FROM_CACHE_1S_1S_4S_OP(0, 1, NULL, 0, 0),
+               SPINAND_PAGE_READ_FROM_CACHE_1S_2S_2S_OP(0, 1, NULL, 0, 0),
+               SPINAND_PAGE_READ_FROM_CACHE_1S_1S_2S_OP(0, 1, NULL, 0, 0),
+               SPINAND_PAGE_READ_FROM_CACHE_FAST_1S_1S_1S_OP(0, 1, NULL, 0, 0),
+               SPINAND_PAGE_READ_FROM_CACHE_1S_1S_1S_OP(0, 1, NULL, 0, 0));
+
+static int fm25g01b_ooblayout_ecc(struct mtd_info *mtd, int section,
+                                 struct mtd_oob_region *region)
+{
+       if (section)
+               return -ERANGE;
+
+       region->offset = 64;
+       region->length = 64;
+
+       return 0;
+}
+
+static int fm25g01b_ooblayout_free(struct mtd_info *mtd, int section,
+                                  struct mtd_oob_region *region)
+{
+       if (section)
+               return -ERANGE;
+
+       /* reserve 2 bytes for the BBM */
+       region->offset = 2;
+       region->length = 62;
+
+       return 0;
+}
+
+static int fm25g01b_ecc_get_status(struct spinand_device *spinand,
+                                  u8 status)
+{
+       switch (status & FM25G01B_STATUS_ECC_MASK) {
+       case FM25G01B_STATUS_ECC_NO_BITFLIPS:
+               return 0;
+
+       case FM25G01B_STATUS_ECC_1_3_BITFLIPS:
+               return 3;
+
+       case FM25G01B_STATUS_ECC_4_BITFLIPS:
+               return 4;
+
+       case FM25G01B_STATUS_ECC_5_BITFLIPS:
+               return 5;
+
+       case FM25G01B_STATUS_ECC_6_BITFLIPS:
+               return 6;
+
+       case FM25G01B_STATUS_ECC_7_BITFLIPS:
+               return 7;
+
+       case FM25G01B_STATUS_ECC_8_BITFLIPS:
+               return 8;
+
+       case FM25G01B_STATUS_ECC_UNCOR_ERROR:
+               return -EBADMSG;
+
+       default:
+               break;
+       }
+
+       return -EINVAL;
+}
+
 static int fm25s01a_ooblayout_ecc(struct mtd_info *mtd, int section,
                                  struct mtd_oob_region *region)
 {
@@ -47,12 +132,92 @@ static int fm25s01a_ooblayout_free(struct mtd_info *mtd, 
int section,
        return 0;
 }
 
+static int fm25s01bi3_ecc_get_status(struct spinand_device *spinand,
+                                    u8 status)
+{
+       switch (status & FM25S01BI3_STATUS_ECC_MASK) {
+       case FM25S01BI3_STATUS_ECC_NO_BITFLIPS:
+               return 0;
+
+       case FM25S01BI3_STATUS_ECC_UNCOR_ERROR:
+               return -EBADMSG;
+
+       case FM25S01BI3_STATUS_ECC_1_3_BITFLIPS:
+               return 3;
+
+       case FM25S01BI3_STATUS_ECC_4_6_BITFLIPS:
+               return 6;
+
+       case FM25S01BI3_STATUS_ECC_7_8_BITFLIPS:
+               return 8;
+
+       default:
+               break;
+       }
+
+       return -EINVAL;
+}
+
+static int fm25s01bi3_ooblayout_ecc(struct mtd_info *mtd, int section,
+                                   struct mtd_oob_region *region)
+{
+       if (section)
+               return -ERANGE;
+
+       region->offset = 64;
+       region->length = 64;
+
+       return 0;
+}
+
+static int fm25s01bi3_ooblayout_free(struct mtd_info *mtd, int section,
+                                    struct mtd_oob_region *region)
+{
+       if (section > 3)
+               return -ERANGE;
+
+       region->offset = (16 * section) + 4;
+       region->length = 12;
+
+       return 0;
+}
+
+static const struct mtd_ooblayout_ops fm25g01b_ooblayout = {
+       .ecc = fm25g01b_ooblayout_ecc,
+       .rfree = fm25g01b_ooblayout_free,
+};
+
 static const struct mtd_ooblayout_ops fm25s01a_ooblayout = {
        .ecc = fm25s01a_ooblayout_ecc,
        .rfree = fm25s01a_ooblayout_free,
 };
 
+static const struct mtd_ooblayout_ops fm25s01bi3_ooblayout = {
+       .ecc = fm25s01bi3_ooblayout_ecc,
+       .rfree = fm25s01bi3_ooblayout_free,
+};
+
 static const struct spinand_info fmsh_spinand_table[] = {
+       SPINAND_INFO("FM25G01B",
+                    SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xd1),
+                    NAND_MEMORG(1, 2048, 128, 64, 1024, 21, 1, 1, 1),
+                    NAND_ECCREQ(8, 528),
+                    SPINAND_INFO_OP_VARIANTS(&fm25g_read_cache_variants,
+                                             &write_cache_variants,
+                                             &update_cache_variants),
+                    SPINAND_HAS_QE_BIT,
+                    SPINAND_ECCINFO(&fm25g01b_ooblayout,
+                                    fm25g01b_ecc_get_status)),
+       SPINAND_INFO("FM25G02B",
+                    SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xd2),
+                    NAND_MEMORG(1, 2048, 128, 64, 2048, 41, 1, 1, 1),
+                    NAND_ECCREQ(8, 528),
+                    SPINAND_INFO_OP_VARIANTS(&fm25g_read_cache_variants,
+                                             &write_cache_variants,
+                                             &update_cache_variants),
+                    SPINAND_HAS_QE_BIT,
+                    SPINAND_ECCINFO(&fm25g01b_ooblayout,
+                                    fm25g01b_ecc_get_status)),
        SPINAND_INFO("FM25S01A",
                     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xE4),
                     NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1),
@@ -62,6 +227,16 @@ static const struct spinand_info fmsh_spinand_table[] = {
                                              &update_cache_variants),
                     0,
                     SPINAND_ECCINFO(&fm25s01a_ooblayout, NULL)),
+       SPINAND_INFO("FM25S01BI3",
+                    SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xd4),
+                    NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1),
+                    NAND_ECCREQ(8, 512),
+                    SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
+                                             &write_cache_variants,
+                                             &update_cache_variants),
+                    SPINAND_HAS_QE_BIT,
+                    SPINAND_ECCINFO(&fm25s01bi3_ooblayout,
+                                     fm25s01bi3_ecc_get_status)),
 };
 
 static const struct spinand_manufacturer_ops fmsh_spinand_manuf_ops = {
diff --git a/drivers/mtd/nand/spi/foresee.c b/drivers/mtd/nand/spi/foresee.c
index 370f8494fb5..986f77426e5 100644
--- a/drivers/mtd/nand/spi/foresee.c
+++ b/drivers/mtd/nand/spi/foresee.c
@@ -13,6 +13,11 @@
 
 #define SPINAND_MFR_FORESEE            0xCD
 
+#define F35SQB002G_STATUS_ECC_MASK             (7 << 4)
+#define F35SQB002G_STATUS_ECC_NO_BITFLIPS      (0 << 4)
+#define F35SQB002G_STATUS_ECC_1_3_BITFLIPS     (1 << 4)
+#define F35SQB002G_STATUS_ECC_UNCOR_ERROR      (7 << 4)
+
 static SPINAND_OP_VARIANTS(read_cache_variants,
                SPINAND_PAGE_READ_FROM_CACHE_1S_1S_4S_OP(0, 1, NULL, 0, 0),
                SPINAND_PAGE_READ_FROM_CACHE_1S_1S_2S_OP(0, 1, NULL, 0, 0),
@@ -72,6 +77,25 @@ static int f35sqa002g_ecc_get_status(struct spinand_device 
*spinand, u8 status)
        return -EBADMSG;
 }
 
+static int f35sqb002g_ecc_get_status(struct spinand_device *spinand, u8 status)
+{
+       switch (status & F35SQB002G_STATUS_ECC_MASK) {
+       case F35SQB002G_STATUS_ECC_NO_BITFLIPS:
+               return 0;
+
+       case F35SQB002G_STATUS_ECC_1_3_BITFLIPS:
+               return 3;
+
+       case F35SQB002G_STATUS_ECC_UNCOR_ERROR:
+               return -EBADMSG;
+
+       default: /* (2 << 4) through (6 << 4) are 4-8 corrected errors */
+               return ((status & F35SQB002G_STATUS_ECC_MASK) >> 4) + 2;
+       }
+
+       return -EINVAL;
+}
+
 static const struct spinand_info foresee_spinand_table[] = {
        SPINAND_INFO("F35SQA002G",
                     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x72, 0x72),
@@ -93,6 +117,16 @@ static const struct spinand_info foresee_spinand_table[] = {
                     SPINAND_HAS_QE_BIT,
                     SPINAND_ECCINFO(&f35sqa002g_ooblayout,
                                     f35sqa002g_ecc_get_status)),
+       SPINAND_INFO("F35SQB002G",
+                    SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x52, 0x52),
+                    NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1),
+                    NAND_ECCREQ(8, 512),
+                    SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
+                                             &write_cache_variants,
+                                             &update_cache_variants),
+                    SPINAND_HAS_QE_BIT,
+                    SPINAND_ECCINFO(&f35sqa002g_ooblayout,
+                                    f35sqb002g_ecc_get_status)),
 };
 
 static const struct spinand_manufacturer_ops foresee_spinand_manuf_ops = {
diff --git a/drivers/mtd/nand/spi/gigadevice.c 
b/drivers/mtd/nand/spi/gigadevice.c
index 32fbe11e908..081d54fbbc5 100644
--- a/drivers/mtd/nand/spi/gigadevice.c
+++ b/drivers/mtd/nand/spi/gigadevice.c
@@ -6,9 +6,12 @@
  *   Copyright (c) 2016-2017 Micron Technology, Inc.
  */
 
+#include <linux/bitfield.h>
 #ifndef __UBOOT__
 #include <linux/device.h>
 #include <linux/kernel.h>
+#else
+#include <linux/bug.h>
 #endif
 #include <linux/mtd/spinand.h>
 
@@ -27,6 +30,18 @@
 #define GD5FXGQ4UXFXXG_STATUS_ECC_1_3_BITFLIPS (1 << 4)
 #define GD5FXGQ4UXFXXG_STATUS_ECC_UNCOR_ERROR  (7 << 4)
 
+/* Feature bit definitions */
+#define GD_FEATURE_NR  BIT(3)  /* Normal Read(1=normal,0=continuous) */
+#define GD_FEATURE_CRDC        BIT(2)  /* Continuous Read Dummy */
+
+/* ECC status extraction helpers */
+#define GD_ECCSR_LAST_PAGE(eccsr)      FIELD_GET(GENMASK(3, 0), eccsr)
+#define GD_ECCSR_ACCUMULATED(eccsr)    FIELD_GET(GENMASK(7, 4), eccsr)
+
+struct gigadevice_priv {
+       bool continuous_read;
+};
+
 static SPINAND_OP_VARIANTS(read_cache_variants,
                SPINAND_PAGE_READ_FROM_CACHE_1S_4S_4S_OP(0, 1, NULL, 0, 0),
                SPINAND_PAGE_READ_FROM_CACHE_1S_1S_4S_OP(0, 1, NULL, 0, 0),
@@ -67,6 +82,74 @@ static SPINAND_OP_VARIANTS(update_cache_variants,
                SPINAND_PROG_LOAD_1S_1S_4S_OP(false, 0, NULL, 0),
                SPINAND_PROG_LOAD_1S_1S_1S_OP(false, 0, NULL, 0));
 
+static int gd5fxgm9_get_eccsr(struct spinand_device *spinand, u8 *eccsr)
+{
+       struct gigadevice_priv *priv = spinand->priv;
+       struct spi_mem_op op = SPI_MEM_OP(SPI_MEM_OP_CMD(0x7c, 1),
+                                       SPI_MEM_OP_NO_ADDR,
+                                       SPI_MEM_OP_DUMMY(1, 1),
+                                       SPI_MEM_OP_DATA_IN(1, eccsr, 1));
+       int ret;
+
+       ret = spi_mem_exec_op(spinand->slave, &op);
+       if (ret)
+               return ret;
+
+       if (priv->continuous_read)
+               *eccsr = GD_ECCSR_ACCUMULATED(*eccsr);
+       else
+               *eccsr = GD_ECCSR_LAST_PAGE(*eccsr);
+
+       return 0;
+}
+
+static int gd5fxgm9_ecc_get_status(struct spinand_device *spinand, u8 status)
+{
+       struct nand_device *nand = spinand_to_nand(spinand);
+       u8 eccsr;
+       int ret;
+
+       switch (status & STATUS_ECC_MASK) {
+       case STATUS_ECC_NO_BITFLIPS:
+               return 0;
+
+       case GD5FXGQ4XA_STATUS_ECC_1_7_BITFLIPS:
+               ret = gd5fxgm9_get_eccsr(spinand, spinand->scratchbuf);
+               if (ret)
+                       return nanddev_get_ecc_conf(nand)->strength;
+
+               eccsr = *spinand->scratchbuf;
+               if (WARN_ON(!eccsr || eccsr > 
nanddev_get_ecc_conf(nand)->strength))
+                       return nanddev_get_ecc_conf(nand)->strength;
+
+               return eccsr;
+
+       case GD5FXGQ4XA_STATUS_ECC_8_BITFLIPS:
+               return 8;
+
+       case STATUS_ECC_UNCOR_ERROR:
+               return -EBADMSG;
+
+       default:
+               return -EINVAL;
+       }
+}
+
+static int gd5fxgm9_set_continuous_read(struct spinand_device *spinand, bool 
enable)
+{
+       struct gigadevice_priv *priv = spinand->priv;
+       int ret;
+
+       ret = spinand_upd_cfg(spinand, GD_FEATURE_NR,
+                               enable ? 0 : GD_FEATURE_NR);
+       if (ret)
+               return ret;
+
+       priv->continuous_read = enable;
+
+       return 0;
+}
+
 static int gd5fxgq4xa_ooblayout_ecc(struct mtd_info *mtd, int section,
                                  struct mtd_oob_region *region)
 {
@@ -189,8 +272,8 @@ static int gd5fxgq4uexxg_ecc_get_status(struct 
spinand_device *spinand,
                                        u8 status)
 {
        u8 status2;
-       struct spi_mem_op op = 
SPINAND_GET_FEATURE_1S_1S_1S_OP(GD5FXGQXXEXXG_REG_STATUS2,
-                                                     spinand->scratchbuf);
+       struct spi_mem_op op = SPINAND_OP(spinand, get_feature,
+                                         GD5FXGQXXEXXG_REG_STATUS2, 
spinand->scratchbuf);
        int ret;
 
        switch (status & STATUS_ECC_MASK) {
@@ -232,8 +315,8 @@ static int gd5fxgq5xexxg_ecc_get_status(struct 
spinand_device *spinand,
                                        u8 status)
 {
        u8 status2;
-       struct spi_mem_op op = 
SPINAND_GET_FEATURE_1S_1S_1S_OP(GD5FXGQXXEXXG_REG_STATUS2,
-                                                     spinand->scratchbuf);
+       struct spi_mem_op op = SPINAND_OP(spinand, get_feature,
+                                         GD5FXGQXXEXXG_REG_STATUS2, 
spinand->scratchbuf);
        int ret;
 
        switch (status & STATUS_ECC_MASK) {
@@ -546,7 +629,8 @@ static const struct spinand_info gigadevice_spinand_table[] 
= {
                                              &update_cache_variants),
                     SPINAND_HAS_QE_BIT,
                     SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout,
-                                    gd5fxgq4uexxg_ecc_get_status)),
+                                    gd5fxgm9_ecc_get_status),
+                       SPINAND_CONT_READ(gd5fxgm9_set_continuous_read)),
        SPINAND_INFO("GD5F1GM9RExxG",
                     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x81, 0x01),
                     NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1),
@@ -556,10 +640,31 @@ static const struct spinand_info 
gigadevice_spinand_table[] = {
                                              &update_cache_variants),
                     SPINAND_HAS_QE_BIT,
                     SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout,
-                                    gd5fxgq4uexxg_ecc_get_status)),
+                                    gd5fxgm9_ecc_get_status),
+                       SPINAND_CONT_READ(gd5fxgm9_set_continuous_read)),
 };
 
+static int gd5fxgm9_spinand_init(struct spinand_device *spinand)
+{
+       struct gigadevice_priv *priv;
+
+       priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+       if (!priv)
+               return -ENOMEM;
+
+       spinand->priv =  priv;
+
+       return 0;
+}
+
+static void gd5fxgm9_spinand_cleanup(struct spinand_device *spinand)
+{
+       kfree(spinand->priv);
+}
+
 static const struct spinand_manufacturer_ops gigadevice_spinand_manuf_ops = {
+       .init = gd5fxgm9_spinand_init,
+       .cleanup = gd5fxgm9_spinand_cleanup,
 };
 
 const struct spinand_manufacturer gigadevice_spinand_manufacturer = {
diff --git a/drivers/mtd/nand/spi/heyangtek.c b/drivers/mtd/nand/spi/heyangtek.c
new file mode 100644
index 00000000000..c1eacf5d41f
--- /dev/null
+++ b/drivers/mtd/nand/spi/heyangtek.c
@@ -0,0 +1,132 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Authors:
+ *     Andrey Zolotarev <[email protected]> - the main driver logic
+ *     Aleksei Sviridkin <[email protected]> - adaptation to the mainline Linux 
kernel
+ *
+ * Based on:
+ *     
https://github.com/keenetic/kernel-49/commit/bacade569fb12bc0ad31ba09bca9b890118fbca7
+ */
+
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/mtd/spinand.h>
+
+#define SPINAND_MFR_HEYANGTEK                  0xc9
+
+#define HYF1GQ4_STATUS_ECC_LIMIT_BITFLIPS      (3 << 4)
+
+static SPINAND_OP_VARIANTS(read_cache_variants,
+               SPINAND_PAGE_READ_FROM_CACHE_1S_4S_4S_OP(0, 1, NULL, 0, 0),
+               SPINAND_PAGE_READ_FROM_CACHE_1S_1S_4S_OP(0, 1, NULL, 0, 0),
+               SPINAND_PAGE_READ_FROM_CACHE_1S_2S_2S_OP(0, 1, NULL, 0, 0),
+               SPINAND_PAGE_READ_FROM_CACHE_1S_1S_2S_OP(0, 1, NULL, 0, 0),
+               SPINAND_PAGE_READ_FROM_CACHE_FAST_1S_1S_1S_OP(0, 1, NULL, 0, 0),
+               SPINAND_PAGE_READ_FROM_CACHE_1S_1S_1S_OP(0, 1, NULL, 0, 0));
+
+static SPINAND_OP_VARIANTS(write_cache_variants,
+               SPINAND_PROG_LOAD_1S_1S_4S_OP(true, 0, NULL, 0),
+               SPINAND_PROG_LOAD_1S_1S_1S_OP(true, 0, NULL, 0));
+
+static SPINAND_OP_VARIANTS(update_cache_variants,
+               SPINAND_PROG_LOAD_1S_1S_4S_OP(false, 0, NULL, 0),
+               SPINAND_PROG_LOAD_1S_1S_1S_OP(false, 0, NULL, 0));
+
+/*
+ * HYF1GQ4UDACAE is a GD5F1GQ4-compatible die, so the OOB layout is taken
+ * from gd5fxgq4xa: the on-die ECC parity occupies bytes 8..15 of each
+ * 16-byte section, the bad block marker sits in byte 0 and the remaining
+ * bytes are exposed as free.
+ */
+static int hyf1gq4_ooblayout_ecc(struct mtd_info *mtd, int section,
+                                struct mtd_oob_region *region)
+{
+       if (section > 3)
+               return -ERANGE;
+
+       region->offset = (16 * section) + 8;
+       region->length = 8;
+
+       return 0;
+}
+
+static int hyf1gq4_ooblayout_free(struct mtd_info *mtd, int section,
+                                 struct mtd_oob_region *region)
+{
+       if (section > 3)
+               return -ERANGE;
+
+       if (section) {
+               region->offset = 16 * section;
+               region->length = 8;
+       } else {
+               /* section 0 has one byte reserved for the bad block marker */
+               region->offset = 1;
+               region->length = 7;
+       }
+
+       return 0;
+}
+
+static const struct mtd_ooblayout_ops hyf1gq4_ooblayout = {
+       .ecc = hyf1gq4_ooblayout_ecc,
+       .rfree = hyf1gq4_ooblayout_free,
+};
+
+static int hyf1gq4_ecc_get_status(struct spinand_device *spinand, u8 status)
+{
+       struct nand_device *nand = spinand_to_nand(spinand);
+
+       switch (status & STATUS_ECC_MASK) {
+       case STATUS_ECC_NO_BITFLIPS:
+               return 0;
+
+       case STATUS_ECC_UNCOR_ERROR:
+               return -EBADMSG;
+
+       case STATUS_ECC_HAS_BITFLIPS:
+               /*
+                * The die exposes only a coarse 2-bit ECC status and has no
+                * register for the exact bitflip count. This code means
+                * "corrected, below the refresh threshold", so report half of
+                * the ECC strength as a representative value.
+                */
+               return nanddev_get_ecc_conf(nand)->strength / 2;
+
+       case HYF1GQ4_STATUS_ECC_LIMIT_BITFLIPS:
+               /*
+                * "Corrected, refresh recommended": report the full ECC
+                * strength so the upper layers relocate the data.
+                */
+               return nanddev_get_ecc_conf(nand)->strength;
+
+       default:
+               break;
+       }
+
+       return -EINVAL;
+}
+
+static const struct spinand_info heyangtek_spinand_table[] = {
+       SPINAND_INFO("HYF1GQ4UDACAE",
+                    SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x21),
+                    NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1),
+                    NAND_ECCREQ(4, 512),
+                    SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
+                                             &write_cache_variants,
+                                             &update_cache_variants),
+                    SPINAND_HAS_QE_BIT,
+                    SPINAND_ECCINFO(&hyf1gq4_ooblayout,
+                                    hyf1gq4_ecc_get_status)),
+};
+
+static const struct spinand_manufacturer_ops heyangtek_spinand_manuf_ops = {
+};
+
+const struct spinand_manufacturer heyangtek_spinand_manufacturer = {
+       .id = SPINAND_MFR_HEYANGTEK,
+       .name = "HeYangTek",
+       .chips = heyangtek_spinand_table,
+       .nchips = ARRAY_SIZE(heyangtek_spinand_table),
+       .ops = &heyangtek_spinand_manuf_ops,
+};
diff --git a/drivers/mtd/nand/spi/macronix.c b/drivers/mtd/nand/spi/macronix.c
index f21103bb15a..753bf88cd6f 100644
--- a/drivers/mtd/nand/spi/macronix.c
+++ b/drivers/mtd/nand/spi/macronix.c
@@ -17,6 +17,8 @@
 #define MACRONIX_ECCSR_BF_LAST_PAGE(eccsr) FIELD_GET(GENMASK(3, 0), eccsr)
 #define MACRONIX_ECCSR_BF_ACCUMULATED_PAGES(eccsr) FIELD_GET(GENMASK(7, 4), 
eccsr)
 #define MACRONIX_CFG_CONT_READ         BIT(2)
+#define MACRONIX_CFG_RANDOMIZER_EN     BIT(1)
+#define MACRONIX_FEATURE_ADDR_RANDOMIZER 0x10
 #define MACRONIX_FEATURE_ADDR_READ_RETRY 0x70
 #define MACRONIX_NUM_READ_RETRY_MODES 5
 
@@ -44,6 +46,23 @@ static SPINAND_OP_VARIANTS(update_cache_variants,
                SPINAND_PROG_LOAD_1S_1S_4S_OP(false, 0, NULL, 0),
                SPINAND_PROG_LOAD_1S_1S_1S_OP(false, 0, NULL, 0));
 
+#define SPINAND_MACRONIX_READ_ECCSR_1S_0_1S(buf)                       \
+       SPI_MEM_OP(SPI_MEM_OP_CMD(0x7c, 1),                             \
+                  SPI_MEM_OP_NO_ADDR,                                  \
+                  SPI_MEM_OP_DUMMY(1, 1),                              \
+                  SPI_MEM_OP_DATA_IN(1, buf, 1))
+
+static SPINAND_OP_VARIANTS(macronix_ops,
+               SPINAND_MACRONIX_READ_ECCSR_1S_0_1S(NULL));
+
+static struct spi_mem_op
+spinand_fill_macronix_read_eccsr_op(struct spinand_device *spinand, void 
*valptr)
+{
+       WARN_ON_ONCE(spinand->bus_iface != SSDR);
+
+       return (struct spi_mem_op)SPINAND_MACRONIX_READ_ECCSR_1S_0_1S(valptr);
+}
+
 static int mx35lfxge4ab_ooblayout_ecc(struct mtd_info *mtd, int section,
                                      struct mtd_oob_region *region)
 {
@@ -70,12 +89,10 @@ static const struct mtd_ooblayout_ops 
mx35lfxge4ab_ooblayout = {
 static int macronix_get_eccsr(struct spinand_device *spinand, u8 *eccsr)
 {
        struct macronix_priv *priv = spinand->priv;
-       struct spi_mem_op op = SPI_MEM_OP(SPI_MEM_OP_CMD(0x7c, 1),
-                                         SPI_MEM_OP_NO_ADDR,
-                                         SPI_MEM_OP_DUMMY(1, 1),
-                                         SPI_MEM_OP_DATA_IN(1, eccsr, 1));
+       struct spi_mem_op op = SPINAND_OP(spinand, macronix_read_eccsr, eccsr);
+       int ret;
 
-       int ret = spi_mem_exec_op(spinand->slave, &op);
+       ret = spi_mem_exec_op(spinand->slave, &op);
        if (ret)
                return ret;
 
@@ -151,13 +168,19 @@ static int macronix_set_cont_read(struct spinand_device 
*spinand, bool enable)
 static int macronix_set_read_retry(struct spinand_device *spinand,
                                             unsigned int retry_mode)
 {
-       struct spi_mem_op op = 
SPINAND_SET_FEATURE_1S_1S_1S_OP(MACRONIX_FEATURE_ADDR_READ_RETRY,
-                                                              
spinand->scratchbuf);
+       struct spi_mem_op op = SPINAND_OP(spinand, set_feature,
+                                         MACRONIX_FEATURE_ADDR_READ_RETRY, 
spinand->scratchbuf);
 
        *spinand->scratchbuf = retry_mode;
        return spi_mem_exec_op(spinand->slave, &op);
 }
 
+static int macronix_set_randomizer(struct spinand_device *spinand, bool enable)
+{
+       return spinand_write_reg_op(spinand, MACRONIX_FEATURE_ADDR_RANDOMIZER,
+                                   enable ? MACRONIX_CFG_RANDOMIZER_EN : 0);
+}
+
 static const struct spinand_info macronix_spinand_table[] = {
        SPINAND_INFO("MX35LF1GE4AB",
                     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x12),
@@ -167,6 +190,7 @@ static const struct spinand_info macronix_spinand_table[] = 
{
                                              &write_cache_variants,
                                              &update_cache_variants),
                     SPINAND_HAS_QE_BIT,
+                    SPINAND_INFO_VENDOR_OPS(&macronix_ops),
                     SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
                                     macronix_ecc_get_status)),
        SPINAND_INFO("MX35LF2GE4AB",
@@ -188,6 +212,7 @@ static const struct spinand_info macronix_spinand_table[] = 
{
                                              &write_cache_variants,
                                              &update_cache_variants),
                     SPINAND_HAS_QE_BIT,
+                    SPINAND_INFO_VENDOR_OPS(&macronix_ops),
                     SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
                                     macronix_ecc_get_status),
                     SPINAND_CONT_READ(macronix_set_cont_read),
@@ -201,6 +226,7 @@ static const struct spinand_info macronix_spinand_table[] = 
{
                                              &write_cache_variants,
                                              &update_cache_variants),
                     SPINAND_HAS_QE_BIT,
+                    SPINAND_INFO_VENDOR_OPS(&macronix_ops),
                     SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
                                     macronix_ecc_get_status),
                     SPINAND_CONT_READ(macronix_set_cont_read),
@@ -216,7 +242,8 @@ static const struct spinand_info macronix_spinand_table[] = 
{
                     SPINAND_HAS_QE_BIT,
                     SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, NULL),
                     SPINAND_READ_RETRY(MACRONIX_NUM_READ_RETRY_MODES,
-                                       macronix_set_read_retry)),
+                                       macronix_set_read_retry),
+                    SPINAND_RANDOMIZER(macronix_set_randomizer)),
        SPINAND_INFO("MX35LF2G24AD",
                     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x24, 0x03),
                     NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 2, 1, 1),
@@ -228,7 +255,8 @@ static const struct spinand_info macronix_spinand_table[] = 
{
                     SPINAND_HAS_PROG_PLANE_SELECT_BIT,
                     SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, NULL),
                     SPINAND_READ_RETRY(MACRONIX_NUM_READ_RETRY_MODES,
-                                       macronix_set_read_retry)),
+                                       macronix_set_read_retry),
+                    SPINAND_RANDOMIZER(macronix_set_randomizer)),
        SPINAND_INFO("MX35LF2G24AD-Z4I8",
                     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x64, 0x03),
                     NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1),
@@ -239,7 +267,8 @@ static const struct spinand_info macronix_spinand_table[] = 
{
                     SPINAND_HAS_QE_BIT,
                     SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, NULL),
                     SPINAND_READ_RETRY(MACRONIX_NUM_READ_RETRY_MODES,
-                                       macronix_set_read_retry)),
+                                       macronix_set_read_retry),
+                    SPINAND_RANDOMIZER(macronix_set_randomizer)),
        SPINAND_INFO("MX35LF4G24AD",
                     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x35, 0x03),
                     NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 2, 1, 1),
@@ -251,7 +280,8 @@ static const struct spinand_info macronix_spinand_table[] = 
{
                     SPINAND_HAS_PROG_PLANE_SELECT_BIT,
                     SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, NULL),
                     SPINAND_READ_RETRY(MACRONIX_NUM_READ_RETRY_MODES,
-                                       macronix_set_read_retry)),
+                                       macronix_set_read_retry),
+                    SPINAND_RANDOMIZER(macronix_set_randomizer)),
        SPINAND_INFO("MX35LF4G24AD-Z4I8",
                     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x75, 0x03),
                     NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 1, 1),
@@ -262,7 +292,8 @@ static const struct spinand_info macronix_spinand_table[] = 
{
                     SPINAND_HAS_QE_BIT,
                     SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, NULL),
                     SPINAND_READ_RETRY(MACRONIX_NUM_READ_RETRY_MODES,
-                                       macronix_set_read_retry)),
+                                       macronix_set_read_retry),
+                    SPINAND_RANDOMIZER(macronix_set_randomizer)),
        SPINAND_INFO("MX31LF1GE4BC",
                     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x1e),
                     NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1),
@@ -271,6 +302,7 @@ static const struct spinand_info macronix_spinand_table[] = 
{
                                              &write_cache_variants,
                                              &update_cache_variants),
                     SPINAND_HAS_QE_BIT,
+                    SPINAND_INFO_VENDOR_OPS(&macronix_ops),
                     SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
                                     macronix_ecc_get_status)),
        SPINAND_INFO("MX31UF1GE4BC",
@@ -281,6 +313,7 @@ static const struct spinand_info macronix_spinand_table[] = 
{
                                              &write_cache_variants,
                                              &update_cache_variants),
                     SPINAND_HAS_QE_BIT,
+                    SPINAND_INFO_VENDOR_OPS(&macronix_ops),
                     SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
                                     macronix_ecc_get_status)),
 
@@ -294,6 +327,7 @@ static const struct spinand_info macronix_spinand_table[] = 
{
                     SPINAND_HAS_QE_BIT |
                     SPINAND_HAS_PROG_PLANE_SELECT_BIT |
                     SPINAND_HAS_READ_PLANE_SELECT_BIT,
+                    SPINAND_INFO_VENDOR_OPS(&macronix_ops),
                     SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
                                     macronix_ecc_get_status)),
        SPINAND_INFO("MX35UF4G24AD",
@@ -305,10 +339,12 @@ static const struct spinand_info macronix_spinand_table[] 
= {
                                              &update_cache_variants),
                     SPINAND_HAS_QE_BIT |
                     SPINAND_HAS_PROG_PLANE_SELECT_BIT,
+                    SPINAND_INFO_VENDOR_OPS(&macronix_ops),
                     SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
                                     macronix_ecc_get_status),
                     SPINAND_READ_RETRY(MACRONIX_NUM_READ_RETRY_MODES,
-                                       macronix_set_read_retry)),
+                                       macronix_set_read_retry),
+                    SPINAND_RANDOMIZER(macronix_set_randomizer)),
        SPINAND_INFO("MX35UF4G24AD-Z4I8",
                     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xf5, 0x03),
                     NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 1, 1),
@@ -317,10 +353,12 @@ static const struct spinand_info macronix_spinand_table[] 
= {
                                              &write_cache_variants,
                                              &update_cache_variants),
                     SPINAND_HAS_QE_BIT,
+                    SPINAND_INFO_VENDOR_OPS(&macronix_ops),
                     SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
                                     macronix_ecc_get_status),
                     SPINAND_READ_RETRY(MACRONIX_NUM_READ_RETRY_MODES,
-                                       macronix_set_read_retry)),
+                                       macronix_set_read_retry),
+                    SPINAND_RANDOMIZER(macronix_set_randomizer)),
        SPINAND_INFO("MX35UF4GE4AD",
                     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xb7, 0x03),
                     NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 1, 1),
@@ -329,6 +367,7 @@ static const struct spinand_info macronix_spinand_table[] = 
{
                                              &write_cache_variants,
                                              &update_cache_variants),
                     SPINAND_HAS_QE_BIT,
+                    SPINAND_INFO_VENDOR_OPS(&macronix_ops),
                     SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
                                     macronix_ecc_get_status),
                     SPINAND_CONT_READ(macronix_set_cont_read),
@@ -344,6 +383,7 @@ static const struct spinand_info macronix_spinand_table[] = 
{
                     SPINAND_HAS_QE_BIT |
                     SPINAND_HAS_PROG_PLANE_SELECT_BIT |
                     SPINAND_HAS_READ_PLANE_SELECT_BIT,
+                    SPINAND_INFO_VENDOR_OPS(&macronix_ops),
                     SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
                                     macronix_ecc_get_status)),
        SPINAND_INFO("MX35UF2G24AD",
@@ -355,10 +395,12 @@ static const struct spinand_info macronix_spinand_table[] 
= {
                                              &update_cache_variants),
                     SPINAND_HAS_QE_BIT |
                     SPINAND_HAS_PROG_PLANE_SELECT_BIT,
+                    SPINAND_INFO_VENDOR_OPS(&macronix_ops),
                     SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
                                     macronix_ecc_get_status),
                     SPINAND_READ_RETRY(MACRONIX_NUM_READ_RETRY_MODES,
-                                       macronix_set_read_retry)),
+                                       macronix_set_read_retry),
+                    SPINAND_RANDOMIZER(macronix_set_randomizer)),
        SPINAND_INFO("MX35UF2G24AD-Z4I8",
                     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xe4, 0x03),
                     NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1),
@@ -367,10 +409,12 @@ static const struct spinand_info macronix_spinand_table[] 
= {
                                              &write_cache_variants,
                                              &update_cache_variants),
                     SPINAND_HAS_QE_BIT,
+                    SPINAND_INFO_VENDOR_OPS(&macronix_ops),
                     SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
                                     macronix_ecc_get_status),
                     SPINAND_READ_RETRY(MACRONIX_NUM_READ_RETRY_MODES,
-                                       macronix_set_read_retry)),
+                                       macronix_set_read_retry),
+                    SPINAND_RANDOMIZER(macronix_set_randomizer)),
        SPINAND_INFO("MX35UF2GE4AD",
                     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xa6, 0x03),
                     NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1),
@@ -379,6 +423,7 @@ static const struct spinand_info macronix_spinand_table[] = 
{
                                              &write_cache_variants,
                                              &update_cache_variants),
                     SPINAND_HAS_QE_BIT,
+                    SPINAND_INFO_VENDOR_OPS(&macronix_ops),
                     SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
                                     macronix_ecc_get_status),
                     SPINAND_CONT_READ(macronix_set_cont_read),
@@ -392,6 +437,7 @@ static const struct spinand_info macronix_spinand_table[] = 
{
                                              &write_cache_variants,
                                              &update_cache_variants),
                     SPINAND_HAS_QE_BIT,
+                    SPINAND_INFO_VENDOR_OPS(&macronix_ops),
                     SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
                                     macronix_ecc_get_status),
                     SPINAND_CONT_READ(macronix_set_cont_read)),
@@ -403,6 +449,7 @@ static const struct spinand_info macronix_spinand_table[] = 
{
                                              &write_cache_variants,
                                              &update_cache_variants),
                     SPINAND_HAS_QE_BIT,
+                    SPINAND_INFO_VENDOR_OPS(&macronix_ops),
                     SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
                                     macronix_ecc_get_status)),
        SPINAND_INFO("MX35UF1G24AD",
@@ -413,10 +460,12 @@ static const struct spinand_info macronix_spinand_table[] 
= {
                                              &write_cache_variants,
                                              &update_cache_variants),
                     SPINAND_HAS_QE_BIT,
+                    SPINAND_INFO_VENDOR_OPS(&macronix_ops),
                     SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
                                     macronix_ecc_get_status),
                     SPINAND_READ_RETRY(MACRONIX_NUM_READ_RETRY_MODES,
-                                       macronix_set_read_retry)),
+                                       macronix_set_read_retry),
+                    SPINAND_RANDOMIZER(macronix_set_randomizer)),
        SPINAND_INFO("MX35UF1GE4AD",
                     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x96, 0x03),
                     NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1),
@@ -425,6 +474,7 @@ static const struct spinand_info macronix_spinand_table[] = 
{
                                              &write_cache_variants,
                                              &update_cache_variants),
                     SPINAND_HAS_QE_BIT,
+                    SPINAND_INFO_VENDOR_OPS(&macronix_ops),
                     SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
                                     macronix_ecc_get_status),
                     SPINAND_CONT_READ(macronix_set_cont_read),
@@ -438,6 +488,7 @@ static const struct spinand_info macronix_spinand_table[] = 
{
                                              &write_cache_variants,
                                              &update_cache_variants),
                     SPINAND_HAS_QE_BIT,
+                    SPINAND_INFO_VENDOR_OPS(&macronix_ops),
                     SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
                                     macronix_ecc_get_status),
                     SPINAND_CONT_READ(macronix_set_cont_read)),
@@ -449,6 +500,7 @@ static const struct spinand_info macronix_spinand_table[] = 
{
                                              &write_cache_variants,
                                              &update_cache_variants),
                     SPINAND_HAS_QE_BIT,
+                    SPINAND_INFO_VENDOR_OPS(&macronix_ops),
                     SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
                                     macronix_ecc_get_status)),
        SPINAND_INFO("MX3UF2GE4BC",
@@ -459,6 +511,7 @@ static const struct spinand_info macronix_spinand_table[] = 
{
                                              &write_cache_variants,
                                              &update_cache_variants),
                     SPINAND_HAS_QE_BIT,
+                    SPINAND_INFO_VENDOR_OPS(&macronix_ops),
                     SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
                                     macronix_ecc_get_status)),
 };
diff --git a/drivers/mtd/nand/spi/micron.c b/drivers/mtd/nand/spi/micron.c
index 9af3e99664f..6dead65577f 100644
--- a/drivers/mtd/nand/spi/micron.c
+++ b/drivers/mtd/nand/spi/micron.c
@@ -143,8 +143,8 @@ static const struct mtd_ooblayout_ops micron_4_ooblayout = {
 static int micron_select_target(struct spinand_device *spinand,
                                unsigned int target)
 {
-       struct spi_mem_op op = 
SPINAND_SET_FEATURE_1S_1S_1S_OP(MICRON_DIE_SELECT_REG,
-                                                     spinand->scratchbuf);
+       struct spi_mem_op op = SPINAND_OP(spinand, set_feature,
+                                         MICRON_DIE_SELECT_REG, 
spinand->scratchbuf);
 
        if (target > 1)
                return -EINVAL;
@@ -262,8 +262,8 @@ static int mt29f2g01abagd_user_otp_info(struct 
spinand_device *spinand,
 static int mt29f2g01abagd_otp_lock(struct spinand_device *spinand, loff_t from,
                                   size_t len)
 {
-       struct spi_mem_op write_op = SPINAND_WR_EN_DIS_1S_0_0_OP(true);
-       struct spi_mem_op exec_op = SPINAND_PROG_EXEC_1S_1S_0_OP(0);
+       struct spi_mem_op write_op = SPINAND_OP(spinand, wr_en);
+       struct spi_mem_op exec_op = SPINAND_OP(spinand, prog_exec, 0);
        u8 status;
        int ret;
 
diff --git a/drivers/mtd/nand/spi/toshiba.c b/drivers/mtd/nand/spi/toshiba.c
index 2e7572d72b4..0a4cee673b2 100644
--- a/drivers/mtd/nand/spi/toshiba.c
+++ b/drivers/mtd/nand/spi/toshiba.c
@@ -76,7 +76,8 @@ static int tx58cxgxsxraix_ecc_get_status(struct 
spinand_device *spinand,
 {
        struct nand_device *nand = spinand_to_nand(spinand);
        u8 mbf = 0;
-       struct spi_mem_op op = SPINAND_GET_FEATURE_1S_1S_1S_OP(0x30, 
spinand->scratchbuf);
+       struct spi_mem_op op = SPINAND_OP(spinand, get_feature,
+                                         0x30, spinand->scratchbuf);
 
        switch (status & STATUS_ECC_MASK) {
        case STATUS_ECC_NO_BITFLIPS:
diff --git a/drivers/mtd/nand/spi/winbond.c b/drivers/mtd/nand/spi/winbond.c
index a89aaec516b..7ba1ebdc5cd 100644
--- a/drivers/mtd/nand/spi/winbond.c
+++ b/drivers/mtd/nand/spi/winbond.c
@@ -20,20 +20,121 @@
 
 #define SPINAND_MFR_WINBOND            0xEF
 
+#define WINBOND_CFG_HFREQ              BIT(0)
 #define WINBOND_CFG_BUF_READ           BIT(3)
 
 #define W25N04KV_STATUS_ECC_5_8_BITFLIPS       (3 << 4)
+#define W25W35NXXJW_STATUS_ECC_MULT_UNCOR      (3 << 4)
 
 #define W25N0XJW_SR4                   0xD0
 #define W25N0XJW_SR4_HS                        BIT(2)
 
-#define W35N01JW_VCR_IO_MODE                   0x00
+#define W35N01JW_VCR_IO_MODE_REG       0x00
 #define W35N01JW_VCR_IO_MODE_SINGLE_SDR                0xFF
 #define W35N01JW_VCR_IO_MODE_OCTAL_SDR         0xDF
 #define W35N01JW_VCR_IO_MODE_OCTAL_DDR_DS      0xE7
 #define W35N01JW_VCR_IO_MODE_OCTAL_DDR         0xC7
 #define W35N01JW_VCR_DUMMY_CLOCK_REG   0x01
 
+/*
+ * Winbond chips ignore the address bytes during continuous reads, and
+ * because the dummy cycles are enough they indicate dropping the
+ * address cycles from the continuous read from cache variants. This is
+ * very poorly supported by SPI controller drivers which are "wired" to
+ * always at least provide the column. Keep using address cycles, but
+ * reduce the number of dummy cycles accordingly.
+ */
+#define WINBOND_CONT_READ_FROM_CACHE_FAST_1S_1S_1S_OP(ndummy, buf, len, freq) \
+       SPI_MEM_OP(SPI_MEM_OP_CMD(0x0b, 1),                             \
+                  SPI_MEM_OP_ADDR(1, 0, 1),                            \
+                  SPI_MEM_OP_DUMMY(ndummy - 1, 1),                     \
+                  SPI_MEM_OP_DATA_IN(len, buf, 1),                     \
+                  SPI_MEM_OP_MAX_FREQ(freq))
+
+#define WINBOND_CONT_READ_FROM_CACHE_1S_1D_1D_OP(ndummy, buf, len, freq) \
+       SPI_MEM_OP(SPI_MEM_OP_CMD(0x0d, 1),                             \
+                  SPI_MEM_DTR_OP_ADDR(2, 0, 1),                        \
+                  SPI_MEM_DTR_OP_DUMMY(ndummy, 1),                     \
+                  SPI_MEM_DTR_OP_DATA_IN(len, buf, 1),                 \
+                  SPI_MEM_OP_MAX_FREQ(freq))
+
+#define WINBOND_CONT_READ_FROM_CACHE_1S_1S_2S_OP(ndummy, buf, len, freq) \
+       SPI_MEM_OP(SPI_MEM_OP_CMD(0x3b, 1),                             \
+                  SPI_MEM_OP_ADDR(1, 0, 1),                            \
+                  SPI_MEM_OP_DUMMY(ndummy - 1, 1),                     \
+                  SPI_MEM_OP_DATA_IN(len, buf, 2),                     \
+                  SPI_MEM_OP_MAX_FREQ(freq))
+
+#define WINBOND_CONT_READ_FROM_CACHE_1S_2S_2S_OP(ndummy, buf, len, freq) \
+       SPI_MEM_OP(SPI_MEM_OP_CMD(0xbb, 1),                             \
+                  SPI_MEM_OP_ADDR(1, 0, 2),                            \
+                  SPI_MEM_OP_DUMMY(ndummy - 1, 2),                     \
+                  SPI_MEM_OP_DATA_IN(len, buf, 2),                     \
+                  SPI_MEM_OP_MAX_FREQ(freq))
+
+#define WINBOND_CONT_READ_FROM_CACHE_1S_2D_2D_OP(ndummy, buf, len, freq) \
+       SPI_MEM_OP(SPI_MEM_OP_CMD(0xbd, 1),                             \
+                  SPI_MEM_DTR_OP_ADDR(1, 0, 2),                        \
+                  SPI_MEM_DTR_OP_DUMMY(ndummy - 1, 2),                 \
+                  SPI_MEM_DTR_OP_DATA_IN(len, buf, 2),                 \
+                  SPI_MEM_OP_MAX_FREQ(freq))
+
+#define WINBOND_CONT_READ_FROM_CACHE_1S_1S_4S_OP(ndummy, buf, len, freq) \
+       SPI_MEM_OP(SPI_MEM_OP_CMD(0x6b, 1),                             \
+                  SPI_MEM_OP_ADDR(1, 0, 1),                            \
+                  SPI_MEM_OP_DUMMY(ndummy - 1, 1),                     \
+                  SPI_MEM_OP_DATA_IN(len, buf, 4),                     \
+                  SPI_MEM_OP_MAX_FREQ(freq))
+
+#define WINBOND_CONT_READ_FROM_CACHE_1S_1D_4D_OP(ndummy, buf, len, freq) \
+       SPI_MEM_OP(SPI_MEM_OP_CMD(0x6d, 1),                             \
+                  SPI_MEM_DTR_OP_ADDR(1, 0, 1),                        \
+                  SPI_MEM_DTR_OP_DUMMY(ndummy - 1, 1),                 \
+                  SPI_MEM_DTR_OP_DATA_IN(len, buf, 4),                 \
+                  SPI_MEM_OP_MAX_FREQ(freq))
+
+#define WINBOND_CONT_READ_FROM_CACHE_1S_4S_4S_OP(ndummy, buf, len, freq) \
+       SPI_MEM_OP(SPI_MEM_OP_CMD(0xeb, 1),                             \
+                  SPI_MEM_OP_ADDR(1, 0, 4),                            \
+                  SPI_MEM_OP_DUMMY(ndummy - 1, 4),                     \
+                  SPI_MEM_OP_DATA_IN(len, buf, 4),                     \
+                  SPI_MEM_OP_MAX_FREQ(freq))
+
+#define WINBOND_CONT_READ_FROM_CACHE_1S_4D_4D_OP(ndummy, buf, len, freq) \
+       SPI_MEM_OP(SPI_MEM_OP_CMD(0xed, 1),                             \
+                  SPI_MEM_DTR_OP_ADDR(1, 0, 4),                        \
+                  SPI_MEM_DTR_OP_DUMMY(ndummy - 1, 4),                 \
+                  SPI_MEM_DTR_OP_DATA_IN(len, buf, 4),                 \
+                  SPI_MEM_OP_MAX_FREQ(freq))
+
+#define WINBOND_CONT_READ_FROM_CACHE_1S_1S_8S_OP(ndummy, buf, len, freq) \
+       SPI_MEM_OP(SPI_MEM_OP_CMD(0x8b, 1),                             \
+                  SPI_MEM_OP_ADDR(1, 0, 1),                            \
+                  SPI_MEM_OP_DUMMY(ndummy - 1, 1),                     \
+                  SPI_MEM_OP_DATA_IN(len, buf, 8),                     \
+                  SPI_MEM_OP_MAX_FREQ(freq))
+
+#define WINBOND_CONT_READ_FROM_CACHE_1S_1D_8D_OP(ndummy, buf, len, freq) \
+       SPI_MEM_OP(SPI_MEM_OP_CMD(0x9d, 1),                             \
+                  SPI_MEM_DTR_OP_ADDR(1, 0, 1),                        \
+                  SPI_MEM_DTR_OP_DUMMY(ndummy - 1, 1),                 \
+                  SPI_MEM_DTR_OP_DATA_IN(len, buf, 8),                 \
+                  SPI_MEM_OP_MAX_FREQ(freq))
+
+#define WINBOND_CONT_READ_FROM_CACHE_1S_8S_8S_OP(ndummy, buf, len, freq) \
+       SPI_MEM_OP(SPI_MEM_OP_CMD(0xcb, 1),                             \
+                  SPI_MEM_OP_ADDR(1, 0, 8),                            \
+                  SPI_MEM_OP_DUMMY(ndummy - 1, 8),                     \
+                  SPI_MEM_OP_DATA_IN(len, buf, 8),                     \
+                  SPI_MEM_OP_MAX_FREQ(freq))
+
+#define WINBOND_CONT_READ_FROM_CACHE_8D_8D_8D_OP(ndummy, buf, len, freq) \
+       SPI_MEM_OP(SPI_MEM_DTR_OP_RPT_CMD(0x9d, 8),                     \
+                  SPI_MEM_DTR_OP_ADDR(2, 0, 8),                        \
+                  SPI_MEM_DTR_OP_DUMMY(ndummy - 2, 8),                 \
+                  SPI_MEM_DTR_OP_DATA_IN(len, buf, 8),                 \
+                  SPI_MEM_OP_MAX_FREQ(freq))
+
 /*
  * "X2" in the core is equivalent to "dual output" in the datasheets,
  * "X4" in the core is equivalent to "quad output" in the datasheets.
@@ -41,6 +142,8 @@
  */
 
 static SPINAND_OP_VARIANTS(read_cache_octal_variants,
+               SPINAND_PAGE_READ_FROM_CACHE_8D_8D_8D_OP(0, 24, NULL, 0, 120 * 
HZ_PER_MHZ),
+               SPINAND_PAGE_READ_FROM_CACHE_8D_8D_8D_OP(0, 16, NULL, 0, 86 * 
HZ_PER_MHZ),
                SPINAND_PAGE_READ_FROM_CACHE_1S_1D_8D_OP(0, 3, NULL, 0, 120 * 
HZ_PER_MHZ),
                SPINAND_PAGE_READ_FROM_CACHE_1S_1D_8D_OP(0, 2, NULL, 0, 105 * 
HZ_PER_MHZ),
                SPINAND_PAGE_READ_FROM_CACHE_1S_8S_8S_OP(0, 20, NULL, 0, 0),
@@ -52,12 +155,27 @@ static SPINAND_OP_VARIANTS(read_cache_octal_variants,
                SPINAND_PAGE_READ_FROM_CACHE_FAST_1S_1S_1S_OP(0, 1, NULL, 0, 0),
                SPINAND_PAGE_READ_FROM_CACHE_1S_1S_1S_OP(0, 1, NULL, 0, 0));
 
+static SPINAND_OP_VARIANTS(cont_read_cache_octal_variants,
+               WINBOND_CONT_READ_FROM_CACHE_8D_8D_8D_OP(24, NULL, 0, 120 * 
HZ_PER_MHZ),
+               WINBOND_CONT_READ_FROM_CACHE_8D_8D_8D_OP(16, NULL, 0, 86 * 
HZ_PER_MHZ),
+               WINBOND_CONT_READ_FROM_CACHE_1S_1D_8D_OP(3, NULL, 0, 120 * 
HZ_PER_MHZ),
+               WINBOND_CONT_READ_FROM_CACHE_1S_1D_8D_OP(2, NULL, 0, 105 * 
HZ_PER_MHZ),
+               WINBOND_CONT_READ_FROM_CACHE_1S_8S_8S_OP(20, NULL, 0, 0),
+               WINBOND_CONT_READ_FROM_CACHE_1S_8S_8S_OP(16, NULL, 0, 162 * 
HZ_PER_MHZ),
+               WINBOND_CONT_READ_FROM_CACHE_1S_8S_8S_OP(12, NULL, 0, 124 * 
HZ_PER_MHZ),
+               WINBOND_CONT_READ_FROM_CACHE_1S_8S_8S_OP(8, NULL, 0, 86 * 
HZ_PER_MHZ),
+               WINBOND_CONT_READ_FROM_CACHE_1S_1S_8S_OP(2, NULL, 0, 0),
+               WINBOND_CONT_READ_FROM_CACHE_1S_1S_8S_OP(1, NULL, 0, 133 * 
HZ_PER_MHZ),
+               WINBOND_CONT_READ_FROM_CACHE_FAST_1S_1S_1S_OP(1, NULL, 0, 0));
+
 static SPINAND_OP_VARIANTS(write_cache_octal_variants,
+               SPINAND_PROG_LOAD_8D_8D_8D_OP(true, 0, NULL, 0),
                SPINAND_PROG_LOAD_1S_8S_8S_OP(true, 0, NULL, 0),
                SPINAND_PROG_LOAD_1S_1S_8S_OP(0, NULL, 0),
                SPINAND_PROG_LOAD_1S_1S_1S_OP(true, 0, NULL, 0));
 
 static SPINAND_OP_VARIANTS(update_cache_octal_variants,
+               SPINAND_PROG_LOAD_8D_8D_8D_OP(false, 0, NULL, 0),
                SPINAND_PROG_LOAD_1S_8S_8S_OP(false, 0, NULL, 0),
                SPINAND_PROG_LOAD_1S_1S_1S_OP(false, 0, NULL, 0));
 
@@ -76,6 +194,20 @@ static 
SPINAND_OP_VARIANTS(read_cache_dual_quad_dtr_variants,
                SPINAND_PAGE_READ_FROM_CACHE_FAST_1S_1S_1S_OP(0, 1, NULL, 0, 0),
                SPINAND_PAGE_READ_FROM_CACHE_1S_1S_1S_OP(0, 1, NULL, 0, 54 * 
HZ_PER_MHZ));
 
+static SPINAND_OP_VARIANTS(cont_read_cache_dual_quad_dtr_variants,
+               WINBOND_CONT_READ_FROM_CACHE_1S_4D_4D_OP(11, NULL, 0, 80 * 
HZ_PER_MHZ),
+               WINBOND_CONT_READ_FROM_CACHE_1S_1D_4D_OP(5, NULL, 0, 80 * 
HZ_PER_MHZ),
+               WINBOND_CONT_READ_FROM_CACHE_1S_4S_4S_OP(7, NULL, 0, 0),
+               WINBOND_CONT_READ_FROM_CACHE_1S_4S_4S_OP(6, NULL, 0, 104 * 
HZ_PER_MHZ),
+               WINBOND_CONT_READ_FROM_CACHE_1S_1S_4S_OP(4, NULL, 0, 0),
+               WINBOND_CONT_READ_FROM_CACHE_1S_2D_2D_OP(6, NULL, 0, 80 * 
HZ_PER_MHZ),
+               /* The 1S_1D_2D variant would require 4.5 dummy bytes, this is 
not possible */
+               WINBOND_CONT_READ_FROM_CACHE_1S_2S_2S_OP(5, NULL, 0, 0),
+               WINBOND_CONT_READ_FROM_CACHE_1S_2S_2S_OP(4, NULL, 0, 104 * 
HZ_PER_MHZ),
+               WINBOND_CONT_READ_FROM_CACHE_1S_1S_2S_OP(4, NULL, 0, 0),
+               /* The 1S_1D_1D variant would require 4.5 dummy bytes, this is 
not possible */
+               WINBOND_CONT_READ_FROM_CACHE_FAST_1S_1S_1S_OP(4, NULL, 0, 0));
+
 static SPINAND_OP_VARIANTS(read_cache_variants,
                SPINAND_PAGE_READ_FROM_CACHE_1S_4S_4S_OP(0, 2, NULL, 0, 0),
                SPINAND_PAGE_READ_FROM_CACHE_1S_1S_4S_OP(0, 1, NULL, 0, 0),
@@ -92,6 +224,47 @@ static SPINAND_OP_VARIANTS(update_cache_variants,
                SPINAND_PROG_LOAD_1S_1S_4S_OP(false, 0, NULL, 0),
                SPINAND_PROG_LOAD_1S_1S_1S_OP(false, 0, NULL, 0));
 
+#define SPINAND_WINBOND_WRITE_VCR_1S_1S_1S(reg, buf)                   \
+       SPI_MEM_OP(SPI_MEM_OP_CMD(0x81, 1),                             \
+                  SPI_MEM_OP_ADDR(3, reg, 1),                          \
+                  SPI_MEM_OP_NO_DUMMY,                                 \
+                  SPI_MEM_OP_DATA_OUT(1, buf, 1))
+
+#define SPINAND_WINBOND_WRITE_VCR_8D_8D_8D(reg, buf)                   \
+       SPI_MEM_OP(SPI_MEM_DTR_OP_RPT_CMD(0x81, 8),                     \
+                  SPI_MEM_DTR_OP_ADDR(4, reg << 8, 8),                 \
+                  SPI_MEM_OP_NO_DUMMY,                                 \
+                  SPI_MEM_DTR_OP_DATA_OUT(2, buf, 8))
+
+static SPINAND_OP_VARIANTS(winbond_w35_ops,
+               SPINAND_WINBOND_WRITE_VCR_1S_1S_1S(0, NULL),
+               SPINAND_WINBOND_WRITE_VCR_8D_8D_8D(0, NULL));
+
+static struct spi_mem_op
+spinand_fill_winbond_write_vcr_op(struct spinand_device *spinand, u8 reg, void 
*valptr)
+{
+       return (spinand->bus_iface == SSDR) ?
+               (struct spi_mem_op)SPINAND_WINBOND_WRITE_VCR_1S_1S_1S(reg, 
valptr) :
+               (struct spi_mem_op)SPINAND_WINBOND_WRITE_VCR_8D_8D_8D(reg, 
valptr);
+}
+
+#define SPINAND_WINBOND_SELECT_TARGET_1S_0_1S(buf)                     \
+       SPI_MEM_OP(SPI_MEM_OP_CMD(0xc2, 1),                             \
+                  SPI_MEM_OP_NO_ADDR,                                  \
+                  SPI_MEM_OP_NO_DUMMY,                                 \
+                  SPI_MEM_OP_DATA_OUT(1, buf, 1))
+
+static SPINAND_OP_VARIANTS(winbond_w25_ops,
+               SPINAND_WINBOND_SELECT_TARGET_1S_0_1S(NULL));
+
+static struct spi_mem_op
+spinand_fill_winbond_select_target_op(struct spinand_device *spinand, void 
*valptr)
+{
+       WARN_ON_ONCE(spinand->bus_iface != SSDR);
+
+       return (struct spi_mem_op)SPINAND_WINBOND_SELECT_TARGET_1S_0_1S(valptr);
+}
+
 static int w25m02gv_ooblayout_ecc(struct mtd_info *mtd, int section,
                                  struct mtd_oob_region *region)
 {
@@ -124,12 +297,8 @@ static const struct mtd_ooblayout_ops w25m02gv_ooblayout = 
{
 static int w25m02gv_select_target(struct spinand_device *spinand,
                                  unsigned int target)
 {
-       struct spi_mem_op op = SPI_MEM_OP(SPI_MEM_OP_CMD(0xc2, 1),
-                                         SPI_MEM_OP_NO_ADDR,
-                                         SPI_MEM_OP_NO_DUMMY,
-                                         SPI_MEM_OP_DATA_OUT(1,
-                                                       spinand->scratchbuf,
-                                                       1));
+       struct spi_mem_op op = SPINAND_OP(spinand, winbond_select_target,
+                                         spinand->scratchbuf);
 
        *spinand->scratchbuf = target;
        return spi_mem_exec_op(spinand->slave, &op);
@@ -181,6 +350,36 @@ static const struct mtd_ooblayout_ops w25n02kv_ooblayout = 
{
        .rfree = w25n02kv_ooblayout_free,
 };
 
+static int w25n01jw_ooblayout_ecc(struct mtd_info *mtd, int section,
+                                 struct mtd_oob_region *region)
+{
+       if (section > 3)
+               return -ERANGE;
+
+       region->offset = (16 * section) + 12;
+       region->length = 4;
+
+       return 0;
+}
+
+static int w25n01jw_ooblayout_free(struct mtd_info *mtd, int section,
+                                  struct mtd_oob_region *region)
+{
+       if (section > 3)
+               return -ERANGE;
+
+       region->offset = (16 * section);
+       region->length = 12;
+
+       /* Extract BBM */
+       if (!section) {
+               region->offset += 2;
+               region->length -= 2;
+       }
+
+       return 0;
+}
+
 static int w35n01jw_ooblayout_ecc(struct mtd_info *mtd, int section,
                                  struct mtd_oob_region *region)
 {
@@ -211,6 +410,11 @@ static int w35n01jw_ooblayout_free(struct mtd_info *mtd, 
int section,
        return 0;
 }
 
+static const struct mtd_ooblayout_ops w25n01jw_ooblayout = {
+       .ecc = w25n01jw_ooblayout_ecc,
+       .rfree = w25n01jw_ooblayout_free,
+};
+
 static const struct mtd_ooblayout_ops w35n01jw_ooblayout = {
        .ecc = w35n01jw_ooblayout_ecc,
        .rfree = w35n01jw_ooblayout_free,
@@ -221,7 +425,8 @@ static int w25n02kv_ecc_get_status(struct spinand_device 
*spinand,
 {
        struct nand_device *nand = spinand_to_nand(spinand);
        u8 mbf = 0;
-       struct spi_mem_op op = SPINAND_GET_FEATURE_1S_1S_1S_OP(0x30, 
spinand->scratchbuf);
+       struct spi_mem_op op = SPINAND_OP(spinand, get_feature,
+                                         0x30, spinand->scratchbuf);
 
        switch (status & STATUS_ECC_MASK) {
        case STATUS_ECC_NO_BITFLIPS:
@@ -254,47 +459,90 @@ static int w25n02kv_ecc_get_status(struct spinand_device 
*spinand,
        return -EINVAL;
 }
 
-static int w25n0xjw_hs_cfg(struct spinand_device *spinand)
+static int w25w35nxxjw_ecc_get_status(struct spinand_device *spinand, u8 
status)
 {
-       const struct spi_mem_op *op;
-       bool hs;
-       u8 sr4;
-       int ret;
+       switch (status & STATUS_ECC_MASK) {
+       case STATUS_ECC_NO_BITFLIPS:
+               return 0;
 
-       op = spinand->op_templates.read_cache;
-       if (op->cmd.dtr || op->addr.dtr || op->dummy.dtr || op->data.dtr)
-               hs = false;
-       else if (op->cmd.buswidth == 1 && op->addr.buswidth == 1 &&
-                op->dummy.buswidth == 1 && op->data.buswidth == 1)
-               hs = false;
-       else if (!op->max_freq)
-               hs = true;
-       else
-               hs = false;
+       case STATUS_ECC_HAS_BITFLIPS:
+               return 1;
+
+       case STATUS_ECC_UNCOR_ERROR:
+       case W25W35NXXJW_STATUS_ECC_MULT_UNCOR:
+               return -EBADMSG;
+
+       default:
+               break;
+       }
+
+       return -EINVAL;
+}
+
+static int w25n0xjw_set_sr4_hs(struct spinand_device *spinand, bool enable)
+{
+       int ret;
+       u8 sr4;
 
        ret = spinand_read_reg_op(spinand, W25N0XJW_SR4, &sr4);
        if (ret)
                return ret;
 
-       if (hs)
+       if (enable)
                sr4 |= W25N0XJW_SR4_HS;
        else
                sr4 &= ~W25N0XJW_SR4_HS;
 
-       ret = spinand_write_reg_op(spinand, W25N0XJW_SR4, sr4);
-       if (ret)
-               return ret;
+       return spinand_write_reg_op(spinand, W25N0XJW_SR4, sr4);
+}
 
-       return 0;
+/*
+ * SDR dual and quad I/O operations over 104MHz require the HS bit to
+ * enable a few more dummy cycles.
+ */
+static bool w25n0xjw_op_needs_hs(const struct spi_mem_op *op)
+{
+       if (op->cmd.dtr || op->addr.dtr || op->dummy.dtr || op->data.dtr)
+               return false;
+       else if (op->cmd.buswidth != 1 || op->addr.buswidth == 1)
+               return false;
+       else if (op->max_freq && op->max_freq <= 104 * HZ_PER_MHZ)
+               return false;
+
+       return true;
+}
+
+static int w25n0xjw_hs_cfg(struct spinand_device *spinand,
+                          enum spinand_bus_interface iface)
+{
+       const struct spi_mem_op *op;
+
+       if (iface != SSDR)
+               return -EOPNOTSUPP;
+
+       /*
+        * At this stage, we do not yet know the continuous read template, nor
+        * if there is going to be one. Let's assume the continuous read
+        * template will be selected with the same heuristics as the buffered
+        * read variant, as there cannot be a HS configuration mismatch between
+        * them.
+        */
+       op = spinand->op_templates->read_cache;
+
+       return w25n0xjw_set_sr4_hs(spinand, w25n0xjw_op_needs_hs(op));
+}
+
+static int w25n0xjw_set_cont_read(struct spinand_device *spinand, bool enable)
+{
+       u8 mask = enable ? 0 : WINBOND_CFG_BUF_READ;
+
+       return spinand_upd_cfg(spinand, WINBOND_CFG_BUF_READ, mask);
 }
 
 static int w35n0xjw_write_vcr(struct spinand_device *spinand, u8 reg, u8 val)
 {
-       struct spi_mem_op op =
-               SPI_MEM_OP(SPI_MEM_OP_CMD(0x81, 1),
-                          SPI_MEM_OP_ADDR(3, reg, 1),
-                          SPI_MEM_OP_NO_DUMMY,
-                          SPI_MEM_OP_DATA_OUT(1, spinand->scratchbuf, 1));
+       struct spi_mem_op op = SPINAND_OP(spinand, winbond_write_vcr,
+                                         reg, spinand->scratchbuf);
        int ret;
 
        *spinand->scratchbuf = val;
@@ -317,32 +565,28 @@ static int w35n0xjw_write_vcr(struct spinand_device 
*spinand, u8 reg, u8 val)
        return 0;
 }
 
-static int w35n0xjw_vcr_cfg(struct spinand_device *spinand)
+static int w35n0xjw_vcr_cfg(struct spinand_device *spinand,
+                           enum spinand_bus_interface iface)
 {
-       const struct spi_mem_op *op;
+       const struct spi_mem_op *ref_op;
        unsigned int dummy_cycles;
        bool dtr, single;
        u8 io_mode;
        int ret;
 
-       op = spinand->op_templates.read_cache;
-
-       single = (op->cmd.buswidth == 1 && op->addr.buswidth == 1 && 
op->data.buswidth == 1);
-       dtr = (op->cmd.dtr || op->addr.dtr || op->data.dtr);
-       if (single && !dtr)
-               io_mode = W35N01JW_VCR_IO_MODE_SINGLE_SDR;
-       else if (!single && !dtr)
-               io_mode = W35N01JW_VCR_IO_MODE_OCTAL_SDR;
-       else if (!single && dtr)
-               io_mode = W35N01JW_VCR_IO_MODE_OCTAL_DDR;
-       else
-               return -EINVAL;
-
-       ret = w35n0xjw_write_vcr(spinand, W35N01JW_VCR_IO_MODE, io_mode);
-       if (ret)
-               return ret;
+       switch (iface) {
+       case SSDR:
+               ref_op = spinand->ssdr_op_templates.read_cache;
+               break;
+       case ODTR:
+               ref_op = spinand->odtr_op_templates.read_cache;
+               break;
+       default:
+               return -EOPNOTSUPP;
+       }
 
-       dummy_cycles = ((op->dummy.nbytes * 8) / op->dummy.buswidth) / 
(op->dummy.dtr ? 2 : 1);
+       dummy_cycles = ((ref_op->dummy.nbytes * 8) / ref_op->dummy.buswidth) /
+               (ref_op->dummy.dtr ? 2 : 1);
        switch (dummy_cycles) {
        case 8:
        case 12:
@@ -354,13 +598,43 @@ static int w35n0xjw_vcr_cfg(struct spinand_device 
*spinand)
        default:
                return -EINVAL;
        }
+
        ret = w35n0xjw_write_vcr(spinand, W35N01JW_VCR_DUMMY_CLOCK_REG, 
dummy_cycles);
        if (ret)
                return ret;
 
+       single = (ref_op->cmd.buswidth == 1 &&
+                 ref_op->addr.buswidth == 1 &&
+                 ref_op->data.buswidth == 1);
+       dtr = (ref_op->cmd.dtr && ref_op->addr.dtr && ref_op->data.dtr);
+       if (single && !dtr)
+               io_mode = W35N01JW_VCR_IO_MODE_SINGLE_SDR;
+       else if (!single && !dtr)
+               io_mode = W35N01JW_VCR_IO_MODE_OCTAL_SDR;
+       else if (!single && dtr)
+               io_mode = W35N01JW_VCR_IO_MODE_OCTAL_DDR;
+       else
+               return -EINVAL;
+
+       ret = w35n0xjw_write_vcr(spinand, W35N01JW_VCR_IO_MODE_REG, io_mode);
+       if (ret)
+               return ret;
+
        return 0;
 }
 
+static int w35n0xjw_set_cont_read(struct spinand_device *spinand, bool enable)
+{
+       const struct spi_mem_op *cont_op = 
spinand->op_templates->cont_read_cache;
+       u8 mask = enable ? 0 : WINBOND_CFG_BUF_READ;
+
+       if (cont_op && enable && spinand_op_is_odtr(cont_op) &&
+           cont_op->max_freq >= 90 * HZ_PER_MHZ)
+               mask |= WINBOND_CFG_HFREQ;
+
+       return spinand_upd_cfg(spinand, WINBOND_CFG_BUF_READ | 
WINBOND_CFG_HFREQ, mask);
+}
+
 static const struct spinand_info winbond_spinand_table[] = {
        /* 512M-bit densities */
        SPINAND_INFO("W25N512GW", /* 1.8V */
@@ -395,12 +669,14 @@ static const struct spinand_info winbond_spinand_table[] 
= {
                     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xbc, 0x21),
                     NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1),
                     NAND_ECCREQ(1, 512),
-                    
SPINAND_INFO_OP_VARIANTS(&read_cache_dual_quad_dtr_variants,
-                                             &write_cache_variants,
-                                             &update_cache_variants),
-                    0,
-                    SPINAND_ECCINFO(&w25m02gv_ooblayout, NULL),
-                    SPINAND_CONFIGURE_CHIP(w25n0xjw_hs_cfg)),
+                    
SPINAND_INFO_OP_VARIANTS_WITH_CONT(&read_cache_dual_quad_dtr_variants,
+                                                       &write_cache_variants,
+                                                       &update_cache_variants,
+                                                       
&cont_read_cache_dual_quad_dtr_variants),
+                    SPINAND_HAS_QE_BIT,
+                    SPINAND_ECCINFO(&w25n01jw_ooblayout, 
w25w35nxxjw_ecc_get_status),
+                    SPINAND_CONFIGURE_CHIP(w25n0xjw_hs_cfg),
+                    SPINAND_CONT_READ(w25n0xjw_set_cont_read)),
        SPINAND_INFO("W25N01KV", /* 3.3V */
                     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xae, 0x21),
                     NAND_MEMORG(1, 2048, 96, 64, 1024, 20, 1, 1, 1),
@@ -414,32 +690,15 @@ static const struct spinand_info winbond_spinand_table[] 
= {
                     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xdc, 0x21),
                     NAND_MEMORG(1, 4096, 128, 64, 512, 10, 1, 1, 1),
                     NAND_ECCREQ(1, 512),
-                    SPINAND_INFO_OP_VARIANTS(&read_cache_octal_variants,
-                                             &write_cache_octal_variants,
-                                             &update_cache_octal_variants),
-                    0,
-                    SPINAND_ECCINFO(&w35n01jw_ooblayout, NULL),
-                    SPINAND_CONFIGURE_CHIP(w35n0xjw_vcr_cfg)),
-       SPINAND_INFO("W35N02JW", /* 1.8V */
-                    SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xdf, 0x22),
-                    NAND_MEMORG(1, 4096, 128, 64, 512, 10, 1, 2, 1),
-                    NAND_ECCREQ(1, 512),
-                    SPINAND_INFO_OP_VARIANTS(&read_cache_octal_variants,
-                                             &write_cache_octal_variants,
-                                             &update_cache_octal_variants),
-                    0,
-                    SPINAND_ECCINFO(&w35n01jw_ooblayout, NULL),
-                    SPINAND_CONFIGURE_CHIP(w35n0xjw_vcr_cfg)),
-       SPINAND_INFO("W35N04JW", /* 1.8V */
-                    SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xdf, 0x23),
-                    NAND_MEMORG(1, 4096, 128, 64, 512, 10, 1, 4, 1),
-                    NAND_ECCREQ(1, 512),
-                    SPINAND_INFO_OP_VARIANTS(&read_cache_octal_variants,
-                                             &write_cache_octal_variants,
-                                             &update_cache_octal_variants),
+                    
SPINAND_INFO_OP_VARIANTS_WITH_CONT(&read_cache_octal_variants,
+                                                       
&write_cache_octal_variants,
+                                                       
&update_cache_octal_variants,
+                                                       
&cont_read_cache_octal_variants),
                     0,
-                    SPINAND_ECCINFO(&w35n01jw_ooblayout, NULL),
-                    SPINAND_CONFIGURE_CHIP(w35n0xjw_vcr_cfg)),
+                    SPINAND_INFO_VENDOR_OPS(&winbond_w35_ops),
+                    SPINAND_ECCINFO(&w35n01jw_ooblayout, 
w25w35nxxjw_ecc_get_status),
+                    SPINAND_CONFIGURE_CHIP(w35n0xjw_vcr_cfg),
+                    SPINAND_CONT_READ(w35n0xjw_set_cont_read)),
        /* 2G-bit densities */
        SPINAND_INFO("W25M02GV", /* 2x1G-bit 3.3V */
                     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xab, 0x21),
@@ -449,18 +708,21 @@ static const struct spinand_info winbond_spinand_table[] 
= {
                                              &write_cache_variants,
                                              &update_cache_variants),
                     0,
+                    SPINAND_INFO_VENDOR_OPS(&winbond_w25_ops),
                     SPINAND_ECCINFO(&w25m02gv_ooblayout, NULL),
                     SPINAND_SELECT_TARGET(w25m02gv_select_target)),
        SPINAND_INFO("W25N02JW", /* high-speed 1.8V */
                     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xbf, 0x22),
                     NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 2, 1),
                     NAND_ECCREQ(1, 512),
-                    
SPINAND_INFO_OP_VARIANTS(&read_cache_dual_quad_dtr_variants,
-                                             &write_cache_variants,
-                                             &update_cache_variants),
-                    0,
-                    SPINAND_ECCINFO(&w25m02gv_ooblayout, NULL),
-                    SPINAND_CONFIGURE_CHIP(w25n0xjw_hs_cfg)),
+                    
SPINAND_INFO_OP_VARIANTS_WITH_CONT(&read_cache_dual_quad_dtr_variants,
+                                                       &write_cache_variants,
+                                                       &update_cache_variants,
+                                                       
&cont_read_cache_dual_quad_dtr_variants),
+                    SPINAND_HAS_QE_BIT,
+                    SPINAND_ECCINFO(&w25m02gv_ooblayout, 
w25w35nxxjw_ecc_get_status),
+                    SPINAND_CONFIGURE_CHIP(w25n0xjw_hs_cfg),
+                    SPINAND_CONT_READ(w25n0xjw_set_cont_read)),
        SPINAND_INFO("W25N02KV", /* 3.3V */
                     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xaa, 0x22),
                     NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1),
@@ -479,6 +741,19 @@ static const struct spinand_info winbond_spinand_table[] = 
{
                                              &update_cache_variants),
                     0,
                     SPINAND_ECCINFO(&w25n02kv_ooblayout, 
w25n02kv_ecc_get_status)),
+       SPINAND_INFO("W35N02JW", /* 1.8V */
+                    SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xdf, 0x22),
+                    NAND_MEMORG(1, 4096, 128, 64, 512, 10, 1, 2, 1),
+                    NAND_ECCREQ(1, 512),
+                    
SPINAND_INFO_OP_VARIANTS_WITH_CONT(&read_cache_octal_variants,
+                                                       
&write_cache_octal_variants,
+                                                       
&update_cache_octal_variants,
+                                                       
&cont_read_cache_octal_variants),
+                    SPINAND_ODTR_PACKED_PAGE_READ,
+                    SPINAND_INFO_VENDOR_OPS(&winbond_w35_ops),
+                    SPINAND_ECCINFO(&w35n01jw_ooblayout, 
w25w35nxxjw_ecc_get_status),
+                    SPINAND_CONFIGURE_CHIP(w35n0xjw_vcr_cfg),
+                    SPINAND_CONT_READ(w35n0xjw_set_cont_read)),
        /* 4G-bit densities */
        SPINAND_INFO("W25N04KV", /* 3.3V */
                     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xaa, 0x23),
@@ -498,6 +773,19 @@ static const struct spinand_info winbond_spinand_table[] = 
{
                                              &update_cache_variants),
                     0,
                     SPINAND_ECCINFO(&w25n02kv_ooblayout, 
w25n02kv_ecc_get_status)),
+       SPINAND_INFO("W35N04JW", /* 1.8V */
+                    SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xdf, 0x23),
+                    NAND_MEMORG(1, 4096, 128, 64, 512, 10, 1, 4, 1),
+                    NAND_ECCREQ(1, 512),
+                    
SPINAND_INFO_OP_VARIANTS_WITH_CONT(&read_cache_octal_variants,
+                                                       
&write_cache_octal_variants,
+                                                       
&update_cache_octal_variants,
+                                                       
&cont_read_cache_octal_variants),
+                    SPINAND_ODTR_PACKED_PAGE_READ,
+                    SPINAND_INFO_VENDOR_OPS(&winbond_w35_ops),
+                    SPINAND_ECCINFO(&w35n01jw_ooblayout, 
w25w35nxxjw_ecc_get_status),
+                    SPINAND_CONFIGURE_CHIP(w35n0xjw_vcr_cfg),
+                    SPINAND_CONT_READ(w35n0xjw_set_cont_read)),
 };
 
 static int winbond_spinand_init(struct spinand_device *spinand)
diff --git a/include/linux/mtd/spinand.h b/include/linux/mtd/spinand.h
index e9561c0a395..270958b8101 100644
--- a/include/linux/mtd/spinand.h
+++ b/include/linux/mtd/spinand.h
@@ -32,8 +32,14 @@
                   SPI_MEM_OP_NO_DUMMY,                                 \
                   SPI_MEM_OP_NO_DATA)
 
-#define SPINAND_WR_EN_DIS_1S_0_0_OP(enable)                            \
-       SPI_MEM_OP(SPI_MEM_OP_CMD((enable) ? 0x06 : 0x04, 1),           \
+#define SPINAND_WR_EN_1S_0_0_OP                                        \
+       SPI_MEM_OP(SPI_MEM_OP_CMD(0x06, 1),                             \
+                  SPI_MEM_OP_NO_ADDR,                                  \
+                  SPI_MEM_OP_NO_DUMMY,                                 \
+                  SPI_MEM_OP_NO_DATA)
+
+#define SPINAND_WR_DIS_1S_0_0_OP                                       \
+       SPI_MEM_OP(SPI_MEM_OP_CMD(0x04, 1),                             \
                   SPI_MEM_OP_NO_ADDR,                                  \
                   SPI_MEM_OP_NO_DUMMY,                                 \
                   SPI_MEM_OP_NO_DATA)
@@ -239,10 +245,81 @@
                   SPI_MEM_OP_DATA_OUT(len, buf, 8))
 
 /**
- * Standard SPI NAND flash commands
+ * Octal DDR SPI NAND flash operations
  */
-#define SPINAND_CMD_PROG_LOAD_X4               0x32
-#define SPINAND_CMD_PROG_LOAD_RDM_DATA_X4      0x34
+
+#define SPINAND_RESET_8D_0_0_OP                                                
\
+       SPI_MEM_OP(SPI_MEM_DTR_OP_RPT_CMD(0xff, 8),                     \
+                  SPI_MEM_OP_NO_ADDR,                                  \
+                  SPI_MEM_OP_NO_DUMMY,                                 \
+                  SPI_MEM_OP_NO_DATA)
+
+#define SPINAND_READID_8D_8D_8D_OP(naddr, ndummy, buf, len)            \
+       SPI_MEM_OP(SPI_MEM_DTR_OP_RPT_CMD(0x9f, 8),                     \
+                  SPI_MEM_DTR_OP_ADDR(naddr, 0, 8),                    \
+                  SPI_MEM_DTR_OP_DUMMY(ndummy, 8),                     \
+                  SPI_MEM_DTR_OP_DATA_IN(len, buf, 8))
+
+#define SPINAND_WR_EN_8D_0_0_OP                                                
\
+       SPI_MEM_OP(SPI_MEM_DTR_OP_RPT_CMD(0x06, 8),                     \
+                  SPI_MEM_OP_NO_ADDR,                                  \
+                  SPI_MEM_OP_NO_DUMMY,                                 \
+                  SPI_MEM_OP_NO_DATA)
+
+#define SPINAND_WR_DIS_8D_0_0_OP                                       \
+       SPI_MEM_OP(SPI_MEM_DTR_OP_RPT_CMD(0x04, 8),                     \
+                  SPI_MEM_OP_NO_ADDR,                                  \
+                  SPI_MEM_OP_NO_DUMMY,                                 \
+                  SPI_MEM_OP_NO_DATA)
+
+#define SPINAND_SET_FEATURE_8D_8D_8D_OP(reg, valptr)                   \
+       SPI_MEM_OP(SPI_MEM_DTR_OP_RPT_CMD(0x1f, 8),                     \
+                  SPI_MEM_DTR_OP_RPT_ADDR(reg, 8),                     \
+                  SPI_MEM_OP_NO_DUMMY,                                 \
+                  SPI_MEM_DTR_OP_DATA_OUT(2, valptr, 8))
+
+#define SPINAND_GET_FEATURE_8D_8D_8D_OP(reg, valptr)                   \
+       SPI_MEM_OP(SPI_MEM_DTR_OP_RPT_CMD(0x0f, 8),                     \
+                  SPI_MEM_DTR_OP_RPT_ADDR(reg, 8),                     \
+                  SPI_MEM_DTR_OP_DUMMY(14, 8),                         \
+                  SPI_MEM_DTR_OP_DATA_IN(2, valptr, 8))
+
+#define SPINAND_BLK_ERASE_8D_8D_0_OP(addr)                             \
+       SPI_MEM_OP(SPI_MEM_DTR_OP_RPT_CMD(0xd8, 8),                     \
+                  SPI_MEM_DTR_OP_ADDR(2, addr, 8),                     \
+                  SPI_MEM_OP_NO_DUMMY,                                 \
+                  SPI_MEM_OP_NO_DATA)
+
+#define SPINAND_PAGE_READ_8D_8D_0_OP(addr)                             \
+       SPI_MEM_OP(SPI_MEM_DTR_OP_RPT_CMD(0x13, 8),                     \
+                  SPI_MEM_DTR_OP_ADDR(2, addr, 8),                     \
+                  SPI_MEM_OP_NO_DUMMY,                                 \
+                  SPI_MEM_OP_NO_DATA)
+
+#define SPINAND_PAGE_READ_PACKED_8D_8D_0_OP(addr)                      \
+       SPI_MEM_OP(SPI_MEM_DTR_OP_PACKED_CMD(0x13, addr >> 16, 8),      \
+                  SPI_MEM_DTR_OP_ADDR(2, addr & 0xffff, 8),            \
+                  SPI_MEM_OP_NO_DUMMY,                                 \
+                  SPI_MEM_OP_NO_DATA)
+
+#define SPINAND_PAGE_READ_FROM_CACHE_8D_8D_8D_OP(addr, ndummy, buf, len, freq) 
\
+       SPI_MEM_OP(SPI_MEM_DTR_OP_RPT_CMD(0x9d, 8),                     \
+                  SPI_MEM_DTR_OP_ADDR(2, addr, 8),                     \
+                  SPI_MEM_DTR_OP_DUMMY(ndummy, 8),                     \
+                  SPI_MEM_DTR_OP_DATA_IN(len, buf, 8),                 \
+                  SPI_MEM_OP_MAX_FREQ(freq))
+
+#define SPINAND_PROG_EXEC_8D_8D_0_OP(addr)                             \
+       SPI_MEM_OP(SPI_MEM_DTR_OP_RPT_CMD(0x10, 8),                     \
+                  SPI_MEM_DTR_OP_ADDR(2, addr, 8),                     \
+                  SPI_MEM_OP_NO_DUMMY,                                 \
+                  SPI_MEM_OP_NO_DATA)
+
+#define SPINAND_PROG_LOAD_8D_8D_8D_OP(reset, addr, buf, len)           \
+       SPI_MEM_OP(SPI_MEM_DTR_OP_RPT_CMD((reset ? 0xc2 : 0xc4), 8),    \
+                  SPI_MEM_DTR_OP_ADDR(2, addr, 8),                     \
+                  SPI_MEM_OP_NO_DUMMY,                                 \
+                  SPI_MEM_DTR_OP_DATA_OUT(len, buf, 8))
 
 /* feature register */
 #define REG_BLOCK_LOCK         0xa0
@@ -267,7 +344,7 @@
 struct spinand_op;
 struct spinand_device;
 
-#define SPINAND_MAX_ID_LEN     5
+#define SPINAND_MAX_ID_LEN     6
 /*
  * For erase, write and read operation, we got the following timings :
  * tBERS (erase) 1ms to 4ms
@@ -293,7 +370,7 @@ struct spinand_device;
 
 /**
  * struct spinand_id - SPI NAND id structure
- * @data: buffer containing the id bytes. Currently 4 bytes large, but can
+ * @data: buffer containing the id bytes. Currently 6 bytes large, but can
  *       be extended if required
  * @len: ID length
  */
@@ -360,10 +437,13 @@ struct spinand_manufacturer {
 /* SPI NAND manufacturers */
 extern const struct spinand_manufacturer alliancememory_spinand_manufacturer;
 extern const struct spinand_manufacturer ato_spinand_manufacturer;
+extern const struct spinand_manufacturer dosilicon_spinand_manufacturer;
+extern const struct spinand_manufacturer esmt_8c_spinand_manufacturer;
 extern const struct spinand_manufacturer esmt_c8_spinand_manufacturer;
 extern const struct spinand_manufacturer fmsh_spinand_manufacturer;
 extern const struct spinand_manufacturer foresee_spinand_manufacturer;
 extern const struct spinand_manufacturer gigadevice_spinand_manufacturer;
+extern const struct spinand_manufacturer heyangtek_spinand_manufacturer;
 extern const struct spinand_manufacturer macronix_spinand_manufacturer;
 extern const struct spinand_manufacturer micron_spinand_manufacturer;
 extern const struct spinand_manufacturer paragon_spinand_manufacturer;
@@ -410,11 +490,13 @@ struct spinand_ecc_info {
        const struct mtd_ooblayout_ops *ooblayout;
 };
 
+/* SPI NAND flags */
 #define SPINAND_HAS_QE_BIT                     BIT(0)
 #define SPINAND_HAS_CR_FEAT_BIT                        BIT(1)
 #define SPINAND_HAS_PROG_PLANE_SELECT_BIT      BIT(2)
 #define SPINAND_HAS_READ_PLANE_SELECT_BIT      BIT(3)
 #define SPINAND_NO_RAW_ACCESS                  BIT(4)
+#define SPINAND_ODTR_PACKED_PAGE_READ          BIT(5)
 
 /**
  * struct spinand_otp_layout - structure to describe the SPI NAND OTP area
@@ -477,6 +559,15 @@ struct spinand_user_otp {
        const struct spinand_user_otp_ops *ops;
 };
 
+/**
+ * enum spinand_bus_interface - SPI NAND bus interface types
+ * @SSDR: Bus configuration supporting all 1S-XX-XX operations, including dual 
and quad
+ * @ODTR: Bus configuration supporting only 8D-8D-8D operations
+ */
+enum spinand_bus_interface {
+       SSDR,
+       ODTR,
+};
 
 /**
  * struct spinand_info - Structure used to describe SPI NAND chips
@@ -490,6 +581,8 @@ struct spinand_user_otp {
  * @op_variants.read_cache: variants of the read-cache operation
  * @op_variants.write_cache: variants of the write-cache operation
  * @op_variants.update_cache: variants of the update-cache operation
+ * @op_variants.cont_read_cache: variants of the continuous read-cache 
operation
+ * @vendor_ops: vendor specific operations
  * @select_target: function used to select a target/die. Required only for
  *                multi-die chips
  * @configure_chip: Align the chip configuration with the core settings
@@ -498,6 +591,7 @@ struct spinand_user_otp {
  * @user_otp: SPI NAND user OTP info.
  * @read_retries: the number of read retry modes supported
  * @set_read_retry: enable/disable read retry for data recovery
+ * @set_randomizer: enable/disable randomizer support
  *
  * Each SPI NAND manufacturer driver should have a spinand_info table
  * describing all the chips supported by the driver.
@@ -513,10 +607,13 @@ struct spinand_info {
                const struct spinand_op_variants *read_cache;
                const struct spinand_op_variants *write_cache;
                const struct spinand_op_variants *update_cache;
+               const struct spinand_op_variants *cont_read_cache;
        } op_variants;
+       const struct spinand_op_variants *vendor_ops;
        int (*select_target)(struct spinand_device *spinand,
                             unsigned int target);
-       int (*configure_chip)(struct spinand_device *spinand);
+       int (*configure_chip)(struct spinand_device *spinand,
+                             enum spinand_bus_interface iface);
        int (*set_cont_read)(struct spinand_device *spinand,
                             bool enable);
        struct spinand_fact_otp fact_otp;
@@ -524,6 +621,8 @@ struct spinand_info {
        unsigned int read_retries;
        int (*set_read_retry)(struct spinand_device *spinand,
                             unsigned int read_retry);
+       int (*set_randomizer)(struct spinand_device *spinand,
+                             bool enable);
 };
 
 #define SPINAND_ID(__method, ...)                                      \
@@ -540,6 +639,17 @@ struct spinand_info {
                .update_cache = __update,                               \
        }
 
+#define SPINAND_INFO_OP_VARIANTS_WITH_CONT(__read, __write, __update, 
__cont_read) \
+       {                                                               \
+               .read_cache = __read,                                   \
+               .write_cache = __write,                                 \
+               .update_cache = __update,                               \
+               .cont_read_cache = __cont_read,                         \
+       }
+
+#define SPINAND_INFO_VENDOR_OPS(__ops)                                 \
+       .vendor_ops = __ops
+
 #define SPINAND_ECCINFO(__ooblayout, __get_status)                     \
        .eccinfo = {                                                    \
                .ooblayout = __ooblayout,                               \
@@ -577,6 +687,9 @@ struct spinand_info {
        .read_retries = __read_retries,                                 \
        .set_read_retry = __set_read_retry
 
+#define SPINAND_RANDOMIZER(__set_randomizer)                           \
+       .set_randomizer = __set_randomizer
+
 #define SPINAND_INFO(__model, __id, __memorg, __eccreq, __op_variants, \
                     __flags, ...)                                      \
        {                                                               \
@@ -592,8 +705,38 @@ struct spinand_info {
 struct spinand_dirmap {
        struct spi_mem_dirmap_desc *wdesc;
        struct spi_mem_dirmap_desc *rdesc;
-       struct spi_mem_dirmap_desc *wdesc_ecc;
-       struct spi_mem_dirmap_desc *rdesc_ecc;
+};
+
+/**
+ * struct spinand_mem_ops - SPI NAND memory operations
+ * @reset: reset op template
+ * @readid: read ID op template
+ * @wr_en: write enable op template
+ * @wr_dis: write disable op template
+ * @set_feature: set feature op template
+ * @get_feature: get feature op template
+ * @blk_erase: blk erase op template
+ * @page_read: page read op template
+ * @prog_exec: prog exec op template
+ * @read_cache: read cache op template
+ * @write_cache: write cache op template
+ * @update_cache: update cache op template
+ * @cont_read_cache: continuous read cache op template (optional)
+ */
+struct spinand_mem_ops {
+       struct spi_mem_op reset;
+       struct spi_mem_op readid;
+       struct spi_mem_op wr_en;
+       struct spi_mem_op wr_dis;
+       struct spi_mem_op set_feature;
+       struct spi_mem_op get_feature;
+       struct spi_mem_op blk_erase;
+       struct spi_mem_op page_read;
+       struct spi_mem_op prog_exec;
+       const struct spi_mem_op *read_cache;
+       const struct spi_mem_op *write_cache;
+       const struct spi_mem_op *update_cache;
+       const struct spi_mem_op *cont_read_cache;
 };
 
 /**
@@ -603,10 +746,10 @@ struct spinand_dirmap {
  * @lock: lock used to serialize accesses to the NAND
  * @id: NAND ID as returned by READ_ID
  * @flags: NAND flags
- * @op_templates: various SPI mem op templates
- * @op_templates.read_cache: read cache op template
- * @op_templates.write_cache: write cache op template
- * @op_templates.update_cache: update cache op template
+ * @ssdr_op_templates: Templates for all single SDR SPI mem operations
+ * @odtr_op_templates: Templates for all octal DTR SPI mem operations
+ * @op_templates: Templates for all SPI mem operations
+ * @bus_iface: Current bus interface
  * @select_target: select a specific target/die. Usually called before sending
  *                a command addressing a page or an eraseblock embedded in
  *                this die. Only required if your chip exposes several dies
@@ -632,6 +775,7 @@ struct spinand_dirmap {
  * @user_otp: SPI NAND user OTP info.
  * @read_retries: the number of read retry modes supported
  * @set_read_retry: Enable/disable the read retry feature
+ * @set_randomizer: Enable/disable the randomizer feature
  * @last_wait_status: status of the last wait operation that will be used in 
case
  *                   ->get_status() is not populated by the spinand device.
  */
@@ -646,11 +790,10 @@ struct spinand_device {
        struct spinand_id id;
        u32 flags;
 
-       struct {
-               const struct spi_mem_op *read_cache;
-               const struct spi_mem_op *write_cache;
-               const struct spi_mem_op *update_cache;
-       } op_templates;
+       struct spinand_mem_ops ssdr_op_templates;
+       struct spinand_mem_ops odtr_op_templates;
+       struct spinand_mem_ops *op_templates;
+       enum spinand_bus_interface bus_iface;
 
        struct spinand_dirmap *dirmaps;
 
@@ -669,10 +812,13 @@ struct spinand_device {
 
        u8 last_wait_status;
 
-       int (*configure_chip)(struct spinand_device *spinand);
+       int (*configure_chip)(struct spinand_device *spinand,
+                             enum spinand_bus_interface iface);
        bool cont_read_possible;
        int (*set_cont_read)(struct spinand_device *spinand,
                             bool enable);
+       int (*set_randomizer)(struct spinand_device *spinand,
+                             bool enable);
 
        const struct spinand_fact_otp *fact_otp;
        const struct spinand_user_otp *user_otp;
@@ -682,6 +828,16 @@ struct spinand_device {
                             unsigned int retry_mode);
 };
 
+struct spi_mem_op spinand_fill_wr_en_op(struct spinand_device *spinand);
+struct spi_mem_op spinand_fill_set_feature_op(struct spinand_device *spinand,
+                                             u64 reg, const void *valptr);
+struct spi_mem_op spinand_fill_get_feature_op(struct spinand_device *spinand,
+                                             u64 reg, void *valptr);
+struct spi_mem_op spinand_fill_prog_exec_op(struct spinand_device *spinand, 
u64 addr);
+
+#define SPINAND_OP(spinand, op_name, ...)                      \
+       spinand_fill_ ## op_name ## _op(spinand, ##__VA_ARGS__)
+
 /**
  * mtd_to_spinand() - Get the SPI NAND device attached to an MTD instance
  * @mtd: MTD instance
@@ -755,6 +911,8 @@ static inline void spinand_set_ofnode(struct spinand_device 
*spinand,
 }
 #endif /* __UBOOT__ */
 
+bool spinand_op_is_odtr(const struct spi_mem_op *op);
+
 int spinand_match_and_init(struct spinand_device *spinand,
                           const struct spinand_info *table,
                           unsigned int table_size,
-- 
2.53.0

Reply via email to