Merge tag 'spi-nor/for-5.4' of git://git.kernel.org/pub/scm/linux/kernel/git/mtd...
authorRichard Weinberger <richard@nod.at>
Sun, 15 Sep 2019 21:00:55 +0000 (23:00 +0200)
committerRichard Weinberger <richard@nod.at>
Sun, 15 Sep 2019 21:00:55 +0000 (23:00 +0200)
MTD core changes:
- add debugfs nodes for querying the flash name and id

SPI NOR core changes:
- always use bounce buffer for register read/writes
- move m25p80 code in spi-nor.c
- rework hwcaps selection for the spi-mem case
- rework the core in order to move the manufacturer specific code
  out of it:
        - regroup flash parameters in 'struct spi_nor_flash_parameter'
        - add default_init() and post_sfdp() hooks to tweak the flash
          parameters
        - introduce the ->set_4byte(), ->convert_addr() and ->setup()
          methods, to deal with manufacturer specific code
        - rework the SPI NOR lock/unlock logic
- fix an error code in spi_nor_read_raw()
- fix a memory leak bug
- enable the debugfs for the partname and partid
- add support for few flashes

SPI NOR controller drivers changes:
- intel-spi:
        - Whitelist 4B read commands
        - Add support for Intel Tiger Lake SPI serial flash
- aspeed-smc: Add of_node_put()
- hisi-sfc: Add of_node_put()
- cadence-quadspi: Fix QSPI RCU Schedule Stall

13 files changed:
drivers/mtd/devices/Kconfig
drivers/mtd/devices/Makefile
drivers/mtd/devices/m25p80.c [deleted file]
drivers/mtd/mtdcore.c
drivers/mtd/spi-nor/Kconfig
drivers/mtd/spi-nor/aspeed-smc.c
drivers/mtd/spi-nor/cadence-quadspi.c
drivers/mtd/spi-nor/hisi-sfc.c
drivers/mtd/spi-nor/intel-spi-pci.c
drivers/mtd/spi-nor/intel-spi.c
drivers/mtd/spi-nor/spi-nor.c
include/linux/mtd/mtd.h
include/linux/mtd/spi-nor.h

index 49abbc5..f96287c 100644 (file)
@@ -79,24 +79,6 @@ config MTD_DATAFLASH_OTP
          other key product data.  The second half is programmed with a
          unique-to-each-chip bit pattern at the factory.
 
-config MTD_M25P80
-       tristate "Support most SPI Flash chips (AT26DF, M25P, W25X, ...)"
-       depends on SPI_MASTER && MTD_SPI_NOR
-       select SPI_MEM
-       help
-         This enables access to most modern SPI flash chips, used for
-         program and data storage.   Series supported include Atmel AT26DF,
-         Spansion S25SL, SST 25VF, ST M25P, and Winbond W25X.  Other chips
-         are supported as well.  See the driver source for the current list,
-         or to add other chips.
-
-         Note that the original DataFlash chips (AT45 series, not AT26DF),
-         need an entirely different driver.
-
-         Set up your spi devices with the right board-specific platform data,
-         if you want to specify device partitioning or to use a device which
-         doesn't support the JEDEC ID instruction.
-
 config MTD_MCHP23K256
        tristate "Microchip 23K256 SRAM"
        depends on SPI_MASTER
index 94895ea..991c8d1 100644 (file)
@@ -12,7 +12,6 @@ obj-$(CONFIG_MTD_MTDRAM)      += mtdram.o
 obj-$(CONFIG_MTD_LART)         += lart.o
 obj-$(CONFIG_MTD_BLOCK2MTD)    += block2mtd.o
 obj-$(CONFIG_MTD_DATAFLASH)    += mtd_dataflash.o
-obj-$(CONFIG_MTD_M25P80)       += m25p80.o
 obj-$(CONFIG_MTD_MCHP23K256)   += mchp23k256.o
 obj-$(CONFIG_MTD_SPEAR_SMI)    += spear_smi.o
 obj-$(CONFIG_MTD_SST25L)       += sst25l.o
diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c
deleted file mode 100644 (file)
index c508886..0000000
+++ /dev/null
@@ -1,347 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * MTD SPI driver for ST M25Pxx (and similar) serial flash chips
- *
- * Author: Mike Lavender, mike@steroidmicros.com
- *
- * Copyright (c) 2005, Intec Automation Inc.
- *
- * Some parts are based on lart.c by Abraham Van Der Merwe
- *
- * Cleaned up and generalized based on mtd_dataflash.c
- */
-
-#include <linux/err.h>
-#include <linux/errno.h>
-#include <linux/module.h>
-#include <linux/device.h>
-
-#include <linux/mtd/mtd.h>
-#include <linux/mtd/partitions.h>
-
-#include <linux/spi/spi.h>
-#include <linux/spi/spi-mem.h>
-#include <linux/spi/flash.h>
-#include <linux/mtd/spi-nor.h>
-
-struct m25p {
-       struct spi_mem          *spimem;
-       struct spi_nor          spi_nor;
-};
-
-static int m25p80_read_reg(struct spi_nor *nor, u8 code, u8 *val, int len)
-{
-       struct m25p *flash = nor->priv;
-       struct spi_mem_op op = SPI_MEM_OP(SPI_MEM_OP_CMD(code, 1),
-                                         SPI_MEM_OP_NO_ADDR,
-                                         SPI_MEM_OP_NO_DUMMY,
-                                         SPI_MEM_OP_DATA_IN(len, NULL, 1));
-       void *scratchbuf;
-       int ret;
-
-       scratchbuf = kmalloc(len, GFP_KERNEL);
-       if (!scratchbuf)
-               return -ENOMEM;
-
-       op.data.buf.in = scratchbuf;
-       ret = spi_mem_exec_op(flash->spimem, &op);
-       if (ret < 0)
-               dev_err(&flash->spimem->spi->dev, "error %d reading %x\n", ret,
-                       code);
-       else
-               memcpy(val, scratchbuf, len);
-
-       kfree(scratchbuf);
-
-       return ret;
-}
-
-static int m25p80_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len)
-{
-       struct m25p *flash = nor->priv;
-       struct spi_mem_op op = SPI_MEM_OP(SPI_MEM_OP_CMD(opcode, 1),
-                                         SPI_MEM_OP_NO_ADDR,
-                                         SPI_MEM_OP_NO_DUMMY,
-                                         SPI_MEM_OP_DATA_OUT(len, NULL, 1));
-       void *scratchbuf;
-       int ret;
-
-       scratchbuf = kmemdup(buf, len, GFP_KERNEL);
-       if (!scratchbuf)
-               return -ENOMEM;
-
-       op.data.buf.out = scratchbuf;
-       ret = spi_mem_exec_op(flash->spimem, &op);
-       kfree(scratchbuf);
-
-       return ret;
-}
-
-static ssize_t m25p80_write(struct spi_nor *nor, loff_t to, size_t len,
-                           const u_char *buf)
-{
-       struct m25p *flash = nor->priv;
-       struct spi_mem_op op =
-                       SPI_MEM_OP(SPI_MEM_OP_CMD(nor->program_opcode, 1),
-                                  SPI_MEM_OP_ADDR(nor->addr_width, to, 1),
-                                  SPI_MEM_OP_NO_DUMMY,
-                                  SPI_MEM_OP_DATA_OUT(len, buf, 1));
-       int ret;
-
-       /* get transfer protocols. */
-       op.cmd.buswidth = spi_nor_get_protocol_inst_nbits(nor->write_proto);
-       op.addr.buswidth = spi_nor_get_protocol_addr_nbits(nor->write_proto);
-       op.data.buswidth = spi_nor_get_protocol_data_nbits(nor->write_proto);
-
-       if (nor->program_opcode == SPINOR_OP_AAI_WP && nor->sst_write_second)
-               op.addr.nbytes = 0;
-
-       ret = spi_mem_adjust_op_size(flash->spimem, &op);
-       if (ret)
-               return ret;
-       op.data.nbytes = len < op.data.nbytes ? len : op.data.nbytes;
-
-       ret = spi_mem_exec_op(flash->spimem, &op);
-       if (ret)
-               return ret;
-
-       return op.data.nbytes;
-}
-
-/*
- * Read an address range from the nor chip.  The address range
- * may be any size provided it is within the physical boundaries.
- */
-static ssize_t m25p80_read(struct spi_nor *nor, loff_t from, size_t len,
-                          u_char *buf)
-{
-       struct m25p *flash = nor->priv;
-       struct spi_mem_op op =
-                       SPI_MEM_OP(SPI_MEM_OP_CMD(nor->read_opcode, 1),
-                                  SPI_MEM_OP_ADDR(nor->addr_width, from, 1),
-                                  SPI_MEM_OP_DUMMY(nor->read_dummy, 1),
-                                  SPI_MEM_OP_DATA_IN(len, buf, 1));
-       size_t remaining = len;
-       int ret;
-
-       /* get transfer protocols. */
-       op.cmd.buswidth = spi_nor_get_protocol_inst_nbits(nor->read_proto);
-       op.addr.buswidth = spi_nor_get_protocol_addr_nbits(nor->read_proto);
-       op.dummy.buswidth = op.addr.buswidth;
-       op.data.buswidth = spi_nor_get_protocol_data_nbits(nor->read_proto);
-
-       /* convert the dummy cycles to the number of bytes */
-       op.dummy.nbytes = (nor->read_dummy * op.dummy.buswidth) / 8;
-
-       while (remaining) {
-               op.data.nbytes = remaining < UINT_MAX ? remaining : UINT_MAX;
-               ret = spi_mem_adjust_op_size(flash->spimem, &op);
-               if (ret)
-                       return ret;
-
-               ret = spi_mem_exec_op(flash->spimem, &op);
-               if (ret)
-                       return ret;
-
-               op.addr.val += op.data.nbytes;
-               remaining -= op.data.nbytes;
-               op.data.buf.in += op.data.nbytes;
-       }
-
-       return len;
-}
-
-/*
- * board specific setup should have ensured the SPI clock used here
- * matches what the READ command supports, at least until this driver
- * understands FAST_READ (for clocks over 25 MHz).
- */
-static int m25p_probe(struct spi_mem *spimem)
-{
-       struct spi_device *spi = spimem->spi;
-       struct flash_platform_data      *data;
-       struct m25p *flash;
-       struct spi_nor *nor;
-       struct spi_nor_hwcaps hwcaps = {
-               .mask = SNOR_HWCAPS_READ |
-                       SNOR_HWCAPS_READ_FAST |
-                       SNOR_HWCAPS_PP,
-       };
-       char *flash_name;
-       int ret;
-
-       data = dev_get_platdata(&spimem->spi->dev);
-
-       flash = devm_kzalloc(&spimem->spi->dev, sizeof(*flash), GFP_KERNEL);
-       if (!flash)
-               return -ENOMEM;
-
-       nor = &flash->spi_nor;
-
-       /* install the hooks */
-       nor->read = m25p80_read;
-       nor->write = m25p80_write;
-       nor->write_reg = m25p80_write_reg;
-       nor->read_reg = m25p80_read_reg;
-
-       nor->dev = &spimem->spi->dev;
-       spi_nor_set_flash_node(nor, spi->dev.of_node);
-       nor->priv = flash;
-
-       spi_mem_set_drvdata(spimem, flash);
-       flash->spimem = spimem;
-
-       if (spi->mode & SPI_RX_OCTAL) {
-               hwcaps.mask |= SNOR_HWCAPS_READ_1_1_8;
-
-               if (spi->mode & SPI_TX_OCTAL)
-                       hwcaps.mask |= (SNOR_HWCAPS_READ_1_8_8 |
-                                       SNOR_HWCAPS_PP_1_1_8 |
-                                       SNOR_HWCAPS_PP_1_8_8);
-       } else if (spi->mode & SPI_RX_QUAD) {
-               hwcaps.mask |= SNOR_HWCAPS_READ_1_1_4;
-
-               if (spi->mode & SPI_TX_QUAD)
-                       hwcaps.mask |= (SNOR_HWCAPS_READ_1_4_4 |
-                                       SNOR_HWCAPS_PP_1_1_4 |
-                                       SNOR_HWCAPS_PP_1_4_4);
-       } else if (spi->mode & SPI_RX_DUAL) {
-               hwcaps.mask |= SNOR_HWCAPS_READ_1_1_2;
-
-               if (spi->mode & SPI_TX_DUAL)
-                       hwcaps.mask |= SNOR_HWCAPS_READ_1_2_2;
-       }
-
-       if (data && data->name)
-               nor->mtd.name = data->name;
-
-       if (!nor->mtd.name)
-               nor->mtd.name = spi_mem_get_name(spimem);
-
-       /* For some (historical?) reason many platforms provide two different
-        * names in flash_platform_data: "name" and "type". Quite often name is
-        * set to "m25p80" and then "type" provides a real chip name.
-        * If that's the case, respect "type" and ignore a "name".
-        */
-       if (data && data->type)
-               flash_name = data->type;
-       else if (!strcmp(spi->modalias, "spi-nor"))
-               flash_name = NULL; /* auto-detect */
-       else
-               flash_name = spi->modalias;
-
-       ret = spi_nor_scan(nor, flash_name, &hwcaps);
-       if (ret)
-               return ret;
-
-       return mtd_device_register(&nor->mtd, data ? data->parts : NULL,
-                                  data ? data->nr_parts : 0);
-}
-
-
-static int m25p_remove(struct spi_mem *spimem)
-{
-       struct m25p     *flash = spi_mem_get_drvdata(spimem);
-
-       spi_nor_restore(&flash->spi_nor);
-
-       /* Clean up MTD stuff. */
-       return mtd_device_unregister(&flash->spi_nor.mtd);
-}
-
-static void m25p_shutdown(struct spi_mem *spimem)
-{
-       struct m25p *flash = spi_mem_get_drvdata(spimem);
-
-       spi_nor_restore(&flash->spi_nor);
-}
-/*
- * Do NOT add to this array without reading the following:
- *
- * Historically, many flash devices are bound to this driver by their name. But
- * since most of these flash are compatible to some extent, and their
- * differences can often be differentiated by the JEDEC read-ID command, we
- * encourage new users to add support to the spi-nor library, and simply bind
- * against a generic string here (e.g., "jedec,spi-nor").
- *
- * Many flash names are kept here in this list (as well as in spi-nor.c) to
- * keep them available as module aliases for existing platforms.
- */
-static const struct spi_device_id m25p_ids[] = {
-       /*
-        * Allow non-DT platform devices to bind to the "spi-nor" modalias, and
-        * hack around the fact that the SPI core does not provide uevent
-        * matching for .of_match_table
-        */
-       {"spi-nor"},
-
-       /*
-        * Entries not used in DTs that should be safe to drop after replacing
-        * them with "spi-nor" in platform data.
-        */
-       {"s25sl064a"},  {"w25x16"},     {"m25p10"},     {"m25px64"},
-
-       /*
-        * Entries that were used in DTs without "jedec,spi-nor" fallback and
-        * should be kept for backward compatibility.
-        */
-       {"at25df321a"}, {"at25df641"},  {"at26df081a"},
-       {"mx25l4005a"}, {"mx25l1606e"}, {"mx25l6405d"}, {"mx25l12805d"},
-       {"mx25l25635e"},{"mx66l51235l"},
-       {"n25q064"},    {"n25q128a11"}, {"n25q128a13"}, {"n25q512a"},
-       {"s25fl256s1"}, {"s25fl512s"},  {"s25sl12801"}, {"s25fl008k"},
-       {"s25fl064k"},
-       {"sst25vf040b"},{"sst25vf016b"},{"sst25vf032b"},{"sst25wf040"},
-       {"m25p40"},     {"m25p80"},     {"m25p16"},     {"m25p32"},
-       {"m25p64"},     {"m25p128"},
-       {"w25x80"},     {"w25x32"},     {"w25q32"},     {"w25q32dw"},
-       {"w25q80bl"},   {"w25q128"},    {"w25q256"},
-
-       /* Flashes that can't be detected using JEDEC */
-       {"m25p05-nonjedec"},    {"m25p10-nonjedec"},    {"m25p20-nonjedec"},
-       {"m25p40-nonjedec"},    {"m25p80-nonjedec"},    {"m25p16-nonjedec"},
-       {"m25p32-nonjedec"},    {"m25p64-nonjedec"},    {"m25p128-nonjedec"},
-
-       /* Everspin MRAMs (non-JEDEC) */
-       { "mr25h128" }, /* 128 Kib, 40 MHz */
-       { "mr25h256" }, /* 256 Kib, 40 MHz */
-       { "mr25h10" },  /*   1 Mib, 40 MHz */
-       { "mr25h40" },  /*   4 Mib, 40 MHz */
-
-       { },
-};
-MODULE_DEVICE_TABLE(spi, m25p_ids);
-
-static const struct of_device_id m25p_of_table[] = {
-       /*
-        * Generic compatibility for SPI NOR that can be identified by the
-        * JEDEC READ ID opcode (0x9F). Use this, if possible.
-        */
-       { .compatible = "jedec,spi-nor" },
-       {}
-};
-MODULE_DEVICE_TABLE(of, m25p_of_table);
-
-static struct spi_mem_driver m25p80_driver = {
-       .spidrv = {
-               .driver = {
-                       .name   = "m25p80",
-                       .of_match_table = m25p_of_table,
-               },
-               .id_table       = m25p_ids,
-       },
-       .probe  = m25p_probe,
-       .remove = m25p_remove,
-       .shutdown       = m25p_shutdown,
-
-       /* REVISIT: many of these chips have deep power-down modes, which
-        * should clearly be entered on suspend() to minimize power use.
-        * And also when they're otherwise idle...
-        */
-};
-
-module_spi_mem_driver(m25p80_driver);
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mike Lavender");
-MODULE_DESCRIPTION("MTD SPI driver for ST M25Pxx flash chips");
index 408615f..6cc7ecb 100644 (file)
@@ -335,6 +335,82 @@ static const struct device_type mtd_devtype = {
        .release        = mtd_release,
 };
 
+static int mtd_partid_show(struct seq_file *s, void *p)
+{
+       struct mtd_info *mtd = s->private;
+
+       seq_printf(s, "%s\n", mtd->dbg.partid);
+
+       return 0;
+}
+
+static int mtd_partid_debugfs_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, mtd_partid_show, inode->i_private);
+}
+
+static const struct file_operations mtd_partid_debug_fops = {
+       .open           = mtd_partid_debugfs_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+static int mtd_partname_show(struct seq_file *s, void *p)
+{
+       struct mtd_info *mtd = s->private;
+
+       seq_printf(s, "%s\n", mtd->dbg.partname);
+
+       return 0;
+}
+
+static int mtd_partname_debugfs_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, mtd_partname_show, inode->i_private);
+}
+
+static const struct file_operations mtd_partname_debug_fops = {
+       .open           = mtd_partname_debugfs_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+static struct dentry *dfs_dir_mtd;
+
+static void mtd_debugfs_populate(struct mtd_info *mtd)
+{
+       struct device *dev = &mtd->dev;
+       struct dentry *root, *dent;
+
+       if (IS_ERR_OR_NULL(dfs_dir_mtd))
+               return;
+
+       root = debugfs_create_dir(dev_name(dev), dfs_dir_mtd);
+       if (IS_ERR_OR_NULL(root)) {
+               dev_dbg(dev, "won't show data in debugfs\n");
+               return;
+       }
+
+       mtd->dbg.dfs_dir = root;
+
+       if (mtd->dbg.partid) {
+               dent = debugfs_create_file("partid", 0400, root, mtd,
+                                          &mtd_partid_debug_fops);
+               if (IS_ERR_OR_NULL(dent))
+                       dev_err(dev, "can't create debugfs entry for partid\n");
+       }
+
+       if (mtd->dbg.partname) {
+               dent = debugfs_create_file("partname", 0400, root, mtd,
+                                          &mtd_partname_debug_fops);
+               if (IS_ERR_OR_NULL(dent))
+                       dev_err(dev,
+                               "can't create debugfs entry for partname\n");
+       }
+}
+
 #ifndef CONFIG_MMU
 unsigned mtd_mmap_capabilities(struct mtd_info *mtd)
 {
@@ -512,8 +588,6 @@ static int mtd_nvmem_add(struct mtd_info *mtd)
        return 0;
 }
 
-static struct dentry *dfs_dir_mtd;
-
 /**
  *     add_mtd_device - register an MTD device
  *     @mtd: pointer to new MTD device info structure
@@ -607,13 +681,7 @@ int add_mtd_device(struct mtd_info *mtd)
        if (error)
                goto fail_nvmem_add;
 
-       if (!IS_ERR_OR_NULL(dfs_dir_mtd)) {
-               mtd->dbg.dfs_dir = debugfs_create_dir(dev_name(&mtd->dev), dfs_dir_mtd);
-               if (IS_ERR_OR_NULL(mtd->dbg.dfs_dir)) {
-                       pr_debug("mtd device %s won't show data in debugfs\n",
-                                dev_name(&mtd->dev));
-               }
-       }
+       mtd_debugfs_populate(mtd);
 
        device_create(&mtd_class, mtd->dev.parent, MTD_DEVT(i) + 1, NULL,
                      "mtd%dro", i);
index 6de8327..f237fcd 100644 (file)
@@ -2,6 +2,8 @@
 menuconfig MTD_SPI_NOR
        tristate "SPI-NOR device support"
        depends on MTD
+       depends on MTD && SPI_MASTER
+       select SPI_MEM
        help
          This is the framework for the SPI NOR which can be used by the SPI
          device drivers and the SPI-NOR device driver.
index 19b8757..009c1da 100644 (file)
@@ -836,8 +836,10 @@ static int aspeed_smc_setup_flash(struct aspeed_smc_controller *controller,
                controller->chips[cs] = chip;
        }
 
-       if (ret)
+       if (ret) {
+               of_node_put(child);
                aspeed_smc_unregister(controller);
+       }
 
        return ret;
 }
index 67f15a1..7bef639 100644 (file)
@@ -13,6 +13,7 @@
 #include <linux/errno.h>
 #include <linux/interrupt.h>
 #include <linux/io.h>
+#include <linux/iopoll.h>
 #include <linux/jiffies.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
@@ -241,23 +242,13 @@ struct cqspi_driver_platdata {
 
 #define CQSPI_IRQ_STATUS_MASK          0x1FFFF
 
-static int cqspi_wait_for_bit(void __iomem *reg, const u32 mask, bool clear)
+static int cqspi_wait_for_bit(void __iomem *reg, const u32 mask, bool clr)
 {
-       unsigned long end = jiffies + msecs_to_jiffies(CQSPI_TIMEOUT_MS);
        u32 val;
 
-       while (1) {
-               val = readl(reg);
-               if (clear)
-                       val = ~val;
-               val &= mask;
-
-               if (val == mask)
-                       return 0;
-
-               if (time_after(jiffies, end))
-                       return -ETIMEDOUT;
-       }
+       return readl_relaxed_poll_timeout(reg, val,
+                                         (((clr ? ~val : val) & mask) == mask),
+                                         10, CQSPI_TIMEOUT_MS * 1000);
 }
 
 static bool cqspi_is_idle(struct cqspi_st *cqspi)
index dea43ea..6dac9dd 100644 (file)
@@ -401,6 +401,7 @@ static int hisi_spi_nor_register_all(struct hifmc_host *host)
 
                if (host->num_chip == HIFMC_MAX_CHIP_NUM) {
                        dev_warn(dev, "Flash device number exceeds the maximum chipselect number\n");
+                       of_node_put(np);
                        break;
                }
        }
index b83c4ab..3cda8e7 100644 (file)
@@ -65,6 +65,7 @@ static const struct pci_device_id intel_spi_pci_ids[] = {
        { PCI_VDEVICE(INTEL, 0x19e0), (unsigned long)&bxt_info },
        { PCI_VDEVICE(INTEL, 0x34a4), (unsigned long)&bxt_info },
        { PCI_VDEVICE(INTEL, 0x4b24), (unsigned long)&bxt_info },
+       { PCI_VDEVICE(INTEL, 0xa0a4), (unsigned long)&bxt_info },
        { PCI_VDEVICE(INTEL, 0xa1a4), (unsigned long)&bxt_info },
        { PCI_VDEVICE(INTEL, 0xa224), (unsigned long)&bxt_info },
        { },
index 1ccf23f..43e55a2 100644 (file)
@@ -621,6 +621,8 @@ static ssize_t intel_spi_read(struct spi_nor *nor, loff_t from, size_t len,
        switch (nor->read_opcode) {
        case SPINOR_OP_READ:
        case SPINOR_OP_READ_FAST:
+       case SPINOR_OP_READ_4B:
+       case SPINOR_OP_READ_FAST_4B:
                break;
        default:
                return -EINVAL;
index 654bdc4..1d8621d 100644 (file)
@@ -19,6 +19,7 @@
 
 #include <linux/mtd/mtd.h>
 #include <linux/of_platform.h>
+#include <linux/sched/task_stack.h>
 #include <linux/spi/flash.h>
 #include <linux/mtd/spi-nor.h>
 
 #define SPI_NOR_MAX_ID_LEN     6
 #define SPI_NOR_MAX_ADDR_WIDTH 4
 
-struct spi_nor_read_command {
-       u8                      num_mode_clocks;
-       u8                      num_wait_states;
-       u8                      opcode;
-       enum spi_nor_protocol   proto;
-};
-
-struct spi_nor_pp_command {
-       u8                      opcode;
-       enum spi_nor_protocol   proto;
-};
-
-enum spi_nor_read_command_index {
-       SNOR_CMD_READ,
-       SNOR_CMD_READ_FAST,
-       SNOR_CMD_READ_1_1_1_DTR,
-
-       /* Dual SPI */
-       SNOR_CMD_READ_1_1_2,
-       SNOR_CMD_READ_1_2_2,
-       SNOR_CMD_READ_2_2_2,
-       SNOR_CMD_READ_1_2_2_DTR,
-
-       /* Quad SPI */
-       SNOR_CMD_READ_1_1_4,
-       SNOR_CMD_READ_1_4_4,
-       SNOR_CMD_READ_4_4_4,
-       SNOR_CMD_READ_1_4_4_DTR,
-
-       /* Octal SPI */
-       SNOR_CMD_READ_1_1_8,
-       SNOR_CMD_READ_1_8_8,
-       SNOR_CMD_READ_8_8_8,
-       SNOR_CMD_READ_1_8_8_DTR,
-
-       SNOR_CMD_READ_MAX
-};
-
-enum spi_nor_pp_command_index {
-       SNOR_CMD_PP,
-
-       /* Quad SPI */
-       SNOR_CMD_PP_1_1_4,
-       SNOR_CMD_PP_1_4_4,
-       SNOR_CMD_PP_4_4_4,
-
-       /* Octal SPI */
-       SNOR_CMD_PP_1_1_8,
-       SNOR_CMD_PP_1_8_8,
-       SNOR_CMD_PP_8_8_8,
-
-       SNOR_CMD_PP_MAX
-};
-
-struct spi_nor_flash_parameter {
-       u64                             size;
-       u32                             page_size;
-
-       struct spi_nor_hwcaps           hwcaps;
-       struct spi_nor_read_command     reads[SNOR_CMD_READ_MAX];
-       struct spi_nor_pp_command       page_programs[SNOR_CMD_PP_MAX];
-
-       int (*quad_enable)(struct spi_nor *nor);
-};
-
 struct sfdp_parameter_header {
        u8              id_lsb;
        u8              minor;
@@ -218,16 +154,26 @@ struct sfdp_bfpt {
 
 /**
  * struct spi_nor_fixups - SPI NOR fixup hooks
+ * @default_init: called after default flash parameters init. Used to tweak
+ *                flash parameters when information provided by the flash_info
+ *                table is incomplete or wrong.
  * @post_bfpt: called after the BFPT table has been parsed
+ * @post_sfdp: called after SFDP has been parsed (is also called for SPI NORs
+ *             that do not support RDSFDP). Typically used to tweak various
+ *             parameters that could not be extracted by other means (i.e.
+ *             when information provided by the SFDP/flash_info tables are
+ *             incomplete or wrong).
  *
  * Those hooks can be used to tweak the SPI NOR configuration when the SFDP
  * table is broken or not available.
  */
 struct spi_nor_fixups {
+       void (*default_init)(struct spi_nor *nor);
        int (*post_bfpt)(struct spi_nor *nor,
                         const struct sfdp_parameter_header *bfpt_header,
                         const struct sfdp_bfpt *bfpt,
                         struct spi_nor_flash_parameter *params);
+       void (*post_sfdp)(struct spi_nor *nor);
 };
 
 struct flash_info {
@@ -265,6 +211,14 @@ struct flash_info {
                                         * bit. Must be used with
                                         * SPI_NOR_HAS_LOCK.
                                         */
+#define SPI_NOR_XSR_RDY                BIT(10) /*
+                                        * S3AN flashes have specific opcode to
+                                        * read the status register.
+                                        * Flags SPI_NOR_XSR_RDY and SPI_S3AN
+                                        * use the same bit as one implies the
+                                        * other, but we will get rid of
+                                        * SPI_S3AN soon.
+                                        */
 #define        SPI_S3AN                BIT(10) /*
                                         * Xilinx Spartan 3AN In-System Flash
                                         * (MFR cannot be used for probing
@@ -282,12 +236,158 @@ struct flash_info {
 
        /* Part specific fixup hooks. */
        const struct spi_nor_fixups *fixups;
-
-       int     (*quad_enable)(struct spi_nor *nor);
 };
 
 #define JEDEC_MFR(info)        ((info)->id[0])
 
+/**
+ * spi_nor_spimem_xfer_data() - helper function to read/write data to
+ *                              flash's memory region
+ * @nor:        pointer to 'struct spi_nor'
+ * @op:         pointer to 'struct spi_mem_op' template for transfer
+ *
+ * Return: number of bytes transferred on success, -errno otherwise
+ */
+static ssize_t spi_nor_spimem_xfer_data(struct spi_nor *nor,
+                                       struct spi_mem_op *op)
+{
+       bool usebouncebuf = false;
+       void *rdbuf = NULL;
+       const void *buf;
+       int ret;
+
+       if (op->data.dir == SPI_MEM_DATA_IN)
+               buf = op->data.buf.in;
+       else
+               buf = op->data.buf.out;
+
+       if (object_is_on_stack(buf) || !virt_addr_valid(buf))
+               usebouncebuf = true;
+
+       if (usebouncebuf) {
+               if (op->data.nbytes > nor->bouncebuf_size)
+                       op->data.nbytes = nor->bouncebuf_size;
+
+               if (op->data.dir == SPI_MEM_DATA_IN) {
+                       rdbuf = op->data.buf.in;
+                       op->data.buf.in = nor->bouncebuf;
+               } else {
+                       op->data.buf.out = nor->bouncebuf;
+                       memcpy(nor->bouncebuf, buf,
+                              op->data.nbytes);
+               }
+       }
+
+       ret = spi_mem_adjust_op_size(nor->spimem, op);
+       if (ret)
+               return ret;
+
+       ret = spi_mem_exec_op(nor->spimem, op);
+       if (ret)
+               return ret;
+
+       if (usebouncebuf && op->data.dir == SPI_MEM_DATA_IN)
+               memcpy(rdbuf, nor->bouncebuf, op->data.nbytes);
+
+       return op->data.nbytes;
+}
+
+/**
+ * spi_nor_spimem_read_data() - read data from flash's memory region via
+ *                              spi-mem
+ * @nor:        pointer to 'struct spi_nor'
+ * @from:       offset to read from
+ * @len:        number of bytes to read
+ * @buf:        pointer to dst buffer
+ *
+ * Return: number of bytes read successfully, -errno otherwise
+ */
+static ssize_t spi_nor_spimem_read_data(struct spi_nor *nor, loff_t from,
+                                       size_t len, u8 *buf)
+{
+       struct spi_mem_op op =
+               SPI_MEM_OP(SPI_MEM_OP_CMD(nor->read_opcode, 1),
+                          SPI_MEM_OP_ADDR(nor->addr_width, from, 1),
+                          SPI_MEM_OP_DUMMY(nor->read_dummy, 1),
+                          SPI_MEM_OP_DATA_IN(len, buf, 1));
+
+       /* get transfer protocols. */
+       op.cmd.buswidth = spi_nor_get_protocol_inst_nbits(nor->read_proto);
+       op.addr.buswidth = spi_nor_get_protocol_addr_nbits(nor->read_proto);
+       op.dummy.buswidth = op.addr.buswidth;
+       op.data.buswidth = spi_nor_get_protocol_data_nbits(nor->read_proto);
+
+       /* convert the dummy cycles to the number of bytes */
+       op.dummy.nbytes = (nor->read_dummy * op.dummy.buswidth) / 8;
+
+       return spi_nor_spimem_xfer_data(nor, &op);
+}
+
+/**
+ * spi_nor_read_data() - read data from flash memory
+ * @nor:        pointer to 'struct spi_nor'
+ * @from:       offset to read from
+ * @len:        number of bytes to read
+ * @buf:        pointer to dst buffer
+ *
+ * Return: number of bytes read successfully, -errno otherwise
+ */
+static ssize_t spi_nor_read_data(struct spi_nor *nor, loff_t from, size_t len,
+                                u8 *buf)
+{
+       if (nor->spimem)
+               return spi_nor_spimem_read_data(nor, from, len, buf);
+
+       return nor->read(nor, from, len, buf);
+}
+
+/**
+ * spi_nor_spimem_write_data() - write data to flash memory via
+ *                               spi-mem
+ * @nor:        pointer to 'struct spi_nor'
+ * @to:         offset to write to
+ * @len:        number of bytes to write
+ * @buf:        pointer to src buffer
+ *
+ * Return: number of bytes written successfully, -errno otherwise
+ */
+static ssize_t spi_nor_spimem_write_data(struct spi_nor *nor, loff_t to,
+                                        size_t len, const u8 *buf)
+{
+       struct spi_mem_op op =
+               SPI_MEM_OP(SPI_MEM_OP_CMD(nor->program_opcode, 1),
+                          SPI_MEM_OP_ADDR(nor->addr_width, to, 1),
+                          SPI_MEM_OP_NO_DUMMY,
+                          SPI_MEM_OP_DATA_OUT(len, buf, 1));
+
+       op.cmd.buswidth = spi_nor_get_protocol_inst_nbits(nor->write_proto);
+       op.addr.buswidth = spi_nor_get_protocol_addr_nbits(nor->write_proto);
+       op.data.buswidth = spi_nor_get_protocol_data_nbits(nor->write_proto);
+
+       if (nor->program_opcode == SPINOR_OP_AAI_WP && nor->sst_write_second)
+               op.addr.nbytes = 0;
+
+       return spi_nor_spimem_xfer_data(nor, &op);
+}
+
+/**
+ * spi_nor_write_data() - write data to flash memory
+ * @nor:        pointer to 'struct spi_nor'
+ * @to:         offset to write to
+ * @len:        number of bytes to write
+ * @buf:        pointer to src buffer
+ *
+ * Return: number of bytes written successfully, -errno otherwise
+ */
+static ssize_t spi_nor_write_data(struct spi_nor *nor, loff_t to, size_t len,
+                                 const u8 *buf)
+{
+       if (nor->spimem)
+               return spi_nor_spimem_write_data(nor, to, len, buf);
+
+       return nor->write(nor, to, len, buf);
+}
+
 /*
  * Read the status register, returning its value in the location
  * Return the status register value.
@@ -296,15 +396,25 @@ struct flash_info {
 static int read_sr(struct spi_nor *nor)
 {
        int ret;
-       u8 val;
 
-       ret = nor->read_reg(nor, SPINOR_OP_RDSR, &val, 1);
+       if (nor->spimem) {
+               struct spi_mem_op op =
+                       SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDSR, 1),
+                                  SPI_MEM_OP_NO_ADDR,
+                                  SPI_MEM_OP_NO_DUMMY,
+                                  SPI_MEM_OP_DATA_IN(1, nor->bouncebuf, 1));
+
+               ret = spi_mem_exec_op(nor->spimem, &op);
+       } else {
+               ret = nor->read_reg(nor, SPINOR_OP_RDSR, nor->bouncebuf, 1);
+       }
+
        if (ret < 0) {
                pr_err("error %d reading SR\n", (int) ret);
                return ret;
        }
 
-       return val;
+       return nor->bouncebuf[0];
 }
 
 /*
@@ -315,15 +425,25 @@ static int read_sr(struct spi_nor *nor)
 static int read_fsr(struct spi_nor *nor)
 {
        int ret;
-       u8 val;
 
-       ret = nor->read_reg(nor, SPINOR_OP_RDFSR, &val, 1);
+       if (nor->spimem) {
+               struct spi_mem_op op =
+                       SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDFSR, 1),
+                                  SPI_MEM_OP_NO_ADDR,
+                                  SPI_MEM_OP_NO_DUMMY,
+                                  SPI_MEM_OP_DATA_IN(1, nor->bouncebuf, 1));
+
+               ret = spi_mem_exec_op(nor->spimem, &op);
+       } else {
+               ret = nor->read_reg(nor, SPINOR_OP_RDFSR, nor->bouncebuf, 1);
+       }
+
        if (ret < 0) {
                pr_err("error %d reading FSR\n", ret);
                return ret;
        }
 
-       return val;
+       return nor->bouncebuf[0];
 }
 
 /*
@@ -334,15 +454,25 @@ static int read_fsr(struct spi_nor *nor)
 static int read_cr(struct spi_nor *nor)
 {
        int ret;
-       u8 val;
 
-       ret = nor->read_reg(nor, SPINOR_OP_RDCR, &val, 1);
+       if (nor->spimem) {
+               struct spi_mem_op op =
+                       SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDCR, 1),
+                                  SPI_MEM_OP_NO_ADDR,
+                                  SPI_MEM_OP_NO_DUMMY,
+                                  SPI_MEM_OP_DATA_IN(1, nor->bouncebuf, 1));
+
+               ret = spi_mem_exec_op(nor->spimem, &op);
+       } else {
+               ret = nor->read_reg(nor, SPINOR_OP_RDCR, nor->bouncebuf, 1);
+       }
+
        if (ret < 0) {
                dev_err(nor->dev, "error %d reading CR\n", ret);
                return ret;
        }
 
-       return val;
+       return nor->bouncebuf[0];
 }
 
 /*
@@ -351,8 +481,18 @@ static int read_cr(struct spi_nor *nor)
  */
 static int write_sr(struct spi_nor *nor, u8 val)
 {
-       nor->cmd_buf[0] = val;
-       return nor->write_reg(nor, SPINOR_OP_WRSR, nor->cmd_buf, 1);
+       nor->bouncebuf[0] = val;
+       if (nor->spimem) {
+               struct spi_mem_op op =
+                       SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WRSR, 1),
+                                  SPI_MEM_OP_NO_ADDR,
+                                  SPI_MEM_OP_NO_DUMMY,
+                                  SPI_MEM_OP_DATA_IN(1, nor->bouncebuf, 1));
+
+               return spi_mem_exec_op(nor->spimem, &op);
+       }
+
+       return nor->write_reg(nor, SPINOR_OP_WRSR, nor->bouncebuf, 1);
 }
 
 /*
@@ -361,6 +501,16 @@ static int write_sr(struct spi_nor *nor, u8 val)
  */
 static int write_enable(struct spi_nor *nor)
 {
+       if (nor->spimem) {
+               struct spi_mem_op op =
+                       SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WREN, 1),
+                                  SPI_MEM_OP_NO_ADDR,
+                                  SPI_MEM_OP_NO_DUMMY,
+                                  SPI_MEM_OP_NO_DATA);
+
+               return spi_mem_exec_op(nor->spimem, &op);
+       }
+
        return nor->write_reg(nor, SPINOR_OP_WREN, NULL, 0);
 }
 
@@ -369,6 +519,16 @@ static int write_enable(struct spi_nor *nor)
  */
 static int write_disable(struct spi_nor *nor)
 {
+       if (nor->spimem) {
+               struct spi_mem_op op =
+                       SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WRDI, 1),
+                                  SPI_MEM_OP_NO_ADDR,
+                                  SPI_MEM_OP_NO_DUMMY,
+                                  SPI_MEM_OP_NO_DATA);
+
+               return spi_mem_exec_op(nor->spimem, &op);
+       }
+
        return nor->write_reg(nor, SPINOR_OP_WRDI, NULL, 0);
 }
 
@@ -439,24 +599,12 @@ static u8 spi_nor_convert_3to4_erase(u8 opcode)
 
 static void spi_nor_set_4byte_opcodes(struct spi_nor *nor)
 {
-       /* Do some manufacturer fixups first */
-       switch (JEDEC_MFR(nor->info)) {
-       case SNOR_MFR_SPANSION:
-               /* No small sector erase for 4-byte command set */
-               nor->erase_opcode = SPINOR_OP_SE;
-               nor->mtd.erasesize = nor->info->sector_size;
-               break;
-
-       default:
-               break;
-       }
-
        nor->read_opcode = spi_nor_convert_3to4_read(nor->read_opcode);
        nor->program_opcode = spi_nor_convert_3to4_program(nor->program_opcode);
        nor->erase_opcode = spi_nor_convert_3to4_erase(nor->erase_opcode);
 
        if (!spi_nor_has_uniform_erase(nor)) {
-               struct spi_nor_erase_map *map = &nor->erase_map;
+               struct spi_nor_erase_map *map = &nor->params.erase_map;
                struct spi_nor_erase_type *erase;
                int i;
 
@@ -468,63 +616,131 @@ static void spi_nor_set_4byte_opcodes(struct spi_nor *nor)
        }
 }
 
-/* Enable/disable 4-byte addressing mode. */
-static int set_4byte(struct spi_nor *nor, bool enable)
+static int macronix_set_4byte(struct spi_nor *nor, bool enable)
 {
-       int status;
-       bool need_wren = false;
-       u8 cmd;
+       if (nor->spimem) {
+               struct spi_mem_op op =
+                       SPI_MEM_OP(SPI_MEM_OP_CMD(enable ?
+                                                 SPINOR_OP_EN4B :
+                                                 SPINOR_OP_EX4B,
+                                                 1),
+                                 SPI_MEM_OP_NO_ADDR,
+                                 SPI_MEM_OP_NO_DUMMY,
+                                 SPI_MEM_OP_NO_DATA);
+
+               return spi_mem_exec_op(nor->spimem, &op);
+       }
 
-       switch (JEDEC_MFR(nor->info)) {
-       case SNOR_MFR_ST:
-       case SNOR_MFR_MICRON:
-               /* Some Micron need WREN command; all will accept it */
-               need_wren = true;
-               /* fall through */
-       case SNOR_MFR_MACRONIX:
-       case SNOR_MFR_WINBOND:
-               if (need_wren)
-                       write_enable(nor);
+       return nor->write_reg(nor, enable ? SPINOR_OP_EN4B : SPINOR_OP_EX4B,
+                             NULL, 0);
+}
 
-               cmd = enable ? SPINOR_OP_EN4B : SPINOR_OP_EX4B;
-               status = nor->write_reg(nor, cmd, NULL, 0);
-               if (need_wren)
-                       write_disable(nor);
+static int st_micron_set_4byte(struct spi_nor *nor, bool enable)
+{
+       int ret;
 
-               if (!status && !enable &&
-                   JEDEC_MFR(nor->info) == SNOR_MFR_WINBOND) {
-                       /*
-                        * On Winbond W25Q256FV, leaving 4byte mode causes
-                        * the Extended Address Register to be set to 1, so all
-                        * 3-byte-address reads come from the second 16M.
-                        * We must clear the register to enable normal behavior.
-                        */
-                       write_enable(nor);
-                       nor->cmd_buf[0] = 0;
-                       nor->write_reg(nor, SPINOR_OP_WREAR, nor->cmd_buf, 1);
-                       write_disable(nor);
-               }
+       write_enable(nor);
+       ret = macronix_set_4byte(nor, enable);
+       write_disable(nor);
 
-               return status;
-       default:
-               /* Spansion style */
-               nor->cmd_buf[0] = enable << 7;
-               return nor->write_reg(nor, SPINOR_OP_BRWR, nor->cmd_buf, 1);
+       return ret;
+}
+
+static int spansion_set_4byte(struct spi_nor *nor, bool enable)
+{
+       nor->bouncebuf[0] = enable << 7;
+
+       if (nor->spimem) {
+               struct spi_mem_op op =
+                       SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_BRWR, 1),
+                                  SPI_MEM_OP_NO_ADDR,
+                                  SPI_MEM_OP_NO_DUMMY,
+                                  SPI_MEM_OP_DATA_OUT(1, nor->bouncebuf, 1));
+
+               return spi_mem_exec_op(nor->spimem, &op);
+       }
+
+       return nor->write_reg(nor, SPINOR_OP_BRWR, nor->bouncebuf, 1);
+}
+
+static int spi_nor_write_ear(struct spi_nor *nor, u8 ear)
+{
+       nor->bouncebuf[0] = ear;
+
+       if (nor->spimem) {
+               struct spi_mem_op op =
+                       SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WREAR, 1),
+                                  SPI_MEM_OP_NO_ADDR,
+                                  SPI_MEM_OP_NO_DUMMY,
+                                  SPI_MEM_OP_DATA_OUT(1, nor->bouncebuf, 1));
+
+               return spi_mem_exec_op(nor->spimem, &op);
+       }
+
+       return nor->write_reg(nor, SPINOR_OP_WREAR, nor->bouncebuf, 1);
+}
+
+static int winbond_set_4byte(struct spi_nor *nor, bool enable)
+{
+       int ret;
+
+       ret = macronix_set_4byte(nor, enable);
+       if (ret || enable)
+               return ret;
+
+       /*
+        * On Winbond W25Q256FV, leaving 4byte mode causes the Extended Address
+        * Register to be set to 1, so all 3-byte-address reads come from the
+        * second 16M. We must clear the register to enable normal behavior.
+        */
+       write_enable(nor);
+       ret = spi_nor_write_ear(nor, 0);
+       write_disable(nor);
+
+       return ret;
+}
+
+static int spi_nor_xread_sr(struct spi_nor *nor, u8 *sr)
+{
+       if (nor->spimem) {
+               struct spi_mem_op op =
+                       SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_XRDSR, 1),
+                                  SPI_MEM_OP_NO_ADDR,
+                                  SPI_MEM_OP_NO_DUMMY,
+                                  SPI_MEM_OP_DATA_IN(1, sr, 1));
+
+               return spi_mem_exec_op(nor->spimem, &op);
        }
+
+       return nor->read_reg(nor, SPINOR_OP_XRDSR, sr, 1);
 }
 
 static int s3an_sr_ready(struct spi_nor *nor)
 {
        int ret;
-       u8 val;
 
-       ret = nor->read_reg(nor, SPINOR_OP_XRDSR, &val, 1);
+       ret = spi_nor_xread_sr(nor, nor->bouncebuf);
        if (ret < 0) {
                dev_err(nor->dev, "error %d reading XRDSR\n", (int) ret);
                return ret;
        }
 
-       return !!(val & XSR_RDY);
+       return !!(nor->bouncebuf[0] & XSR_RDY);
+}
+
+static int spi_nor_clear_sr(struct spi_nor *nor)
+{
+       if (nor->spimem) {
+               struct spi_mem_op op =
+                       SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_CLSR, 1),
+                                  SPI_MEM_OP_NO_ADDR,
+                                  SPI_MEM_OP_NO_DUMMY,
+                                  SPI_MEM_OP_NO_DATA);
+
+               return spi_mem_exec_op(nor->spimem, &op);
+       }
+
+       return nor->write_reg(nor, SPINOR_OP_CLSR, NULL, 0);
 }
 
 static int spi_nor_sr_ready(struct spi_nor *nor)
@@ -539,13 +755,28 @@ static int spi_nor_sr_ready(struct spi_nor *nor)
                else
                        dev_err(nor->dev, "Programming Error occurred\n");
 
-               nor->write_reg(nor, SPINOR_OP_CLSR, NULL, 0);
+               spi_nor_clear_sr(nor);
                return -EIO;
        }
 
        return !(sr & SR_WIP);
 }
 
+static int spi_nor_clear_fsr(struct spi_nor *nor)
+{
+       if (nor->spimem) {
+               struct spi_mem_op op =
+                       SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_CLFSR, 1),
+                                  SPI_MEM_OP_NO_ADDR,
+                                  SPI_MEM_OP_NO_DUMMY,
+                                  SPI_MEM_OP_NO_DATA);
+
+               return spi_mem_exec_op(nor->spimem, &op);
+       }
+
+       return nor->write_reg(nor, SPINOR_OP_CLFSR, NULL, 0);
+}
+
 static int spi_nor_fsr_ready(struct spi_nor *nor)
 {
        int fsr = read_fsr(nor);
@@ -562,7 +793,7 @@ static int spi_nor_fsr_ready(struct spi_nor *nor)
                        dev_err(nor->dev,
                        "Attempted to modify a protected sector.\n");
 
-               nor->write_reg(nor, SPINOR_OP_CLFSR, NULL, 0);
+               spi_nor_clear_fsr(nor);
                return -EIO;
        }
 
@@ -630,6 +861,16 @@ static int erase_chip(struct spi_nor *nor)
 {
        dev_dbg(nor->dev, " %lldKiB\n", (long long)(nor->mtd.size >> 10));
 
+       if (nor->spimem) {
+               struct spi_mem_op op =
+                       SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_CHIP_ERASE, 1),
+                                  SPI_MEM_OP_NO_ADDR,
+                                  SPI_MEM_OP_NO_DUMMY,
+                                  SPI_MEM_OP_NO_DATA);
+
+               return spi_mem_exec_op(nor->spimem, &op);
+       }
+
        return nor->write_reg(nor, SPINOR_OP_CHIP_ERASE, NULL, 0);
 }
 
@@ -666,10 +907,9 @@ static void spi_nor_unlock_and_unprep(struct spi_nor *nor, enum spi_nor_ops ops)
  * Addr can safely be unsigned int, the biggest S3AN device is smaller than
  * 4 MiB.
  */
-static loff_t spi_nor_s3an_addr_convert(struct spi_nor *nor, unsigned int addr)
+static u32 s3an_convert_addr(struct spi_nor *nor, u32 addr)
 {
-       unsigned int offset;
-       unsigned int page;
+       u32 offset, page;
 
        offset = addr % nor->page_size;
        page = addr / nor->page_size;
@@ -678,30 +918,47 @@ static loff_t spi_nor_s3an_addr_convert(struct spi_nor *nor, unsigned int addr)
        return page | offset;
 }
 
+static u32 spi_nor_convert_addr(struct spi_nor *nor, loff_t addr)
+{
+       if (!nor->params.convert_addr)
+               return addr;
+
+       return nor->params.convert_addr(nor, addr);
+}
+
 /*
  * Initiate the erasure of a single sector
  */
 static int spi_nor_erase_sector(struct spi_nor *nor, u32 addr)
 {
-       u8 buf[SPI_NOR_MAX_ADDR_WIDTH];
        int i;
 
-       if (nor->flags & SNOR_F_S3AN_ADDR_DEFAULT)
-               addr = spi_nor_s3an_addr_convert(nor, addr);
+       addr = spi_nor_convert_addr(nor, addr);
 
        if (nor->erase)
                return nor->erase(nor, addr);
 
+       if (nor->spimem) {
+               struct spi_mem_op op =
+                       SPI_MEM_OP(SPI_MEM_OP_CMD(nor->erase_opcode, 1),
+                                  SPI_MEM_OP_ADDR(nor->addr_width, addr, 1),
+                                  SPI_MEM_OP_NO_DUMMY,
+                                  SPI_MEM_OP_NO_DATA);
+
+               return spi_mem_exec_op(nor->spimem, &op);
+       }
+
        /*
         * Default implementation, if driver doesn't have a specialized HW
         * control
         */
        for (i = nor->addr_width - 1; i >= 0; i--) {
-               buf[i] = addr & 0xff;
+               nor->bouncebuf[i] = addr & 0xff;
                addr >>= 8;
        }
 
-       return nor->write_reg(nor, nor->erase_opcode, buf, nor->addr_width);
+       return nor->write_reg(nor, nor->erase_opcode, nor->bouncebuf,
+                             nor->addr_width);
 }
 
 /**
@@ -876,7 +1133,7 @@ static int spi_nor_init_erase_cmd_list(struct spi_nor *nor,
                                       struct list_head *erase_list,
                                       u64 addr, u32 len)
 {
-       const struct spi_nor_erase_map *map = &nor->erase_map;
+       const struct spi_nor_erase_map *map = &nor->params.erase_map;
        const struct spi_nor_erase_type *erase, *prev_erase = NULL;
        struct spi_nor_erase_region *region;
        struct spi_nor_erase_command *cmd = NULL;
@@ -1349,6 +1606,12 @@ static int stm_is_locked(struct spi_nor *nor, loff_t ofs, uint64_t len)
        return stm_is_locked_sr(nor, ofs, len, status);
 }
 
+static const struct spi_nor_locking_ops stm_locking_ops = {
+       .lock = stm_lock,
+       .unlock = stm_unlock,
+       .is_locked = stm_is_locked,
+};
+
 static int spi_nor_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
 {
        struct spi_nor *nor = mtd_to_spi_nor(mtd);
@@ -1358,7 +1621,7 @@ static int spi_nor_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
        if (ret)
                return ret;
 
-       ret = nor->flash_lock(nor, ofs, len);
+       ret = nor->params.locking_ops->lock(nor, ofs, len);
 
        spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_UNLOCK);
        return ret;
@@ -1373,7 +1636,7 @@ static int spi_nor_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
        if (ret)
                return ret;
 
-       ret = nor->flash_unlock(nor, ofs, len);
+       ret = nor->params.locking_ops->unlock(nor, ofs, len);
 
        spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_LOCK);
        return ret;
@@ -1388,7 +1651,7 @@ static int spi_nor_is_locked(struct mtd_info *mtd, loff_t ofs, uint64_t len)
        if (ret)
                return ret;
 
-       ret = nor->flash_is_locked(nor, ofs, len);
+       ret = nor->params.locking_ops->is_locked(nor, ofs, len);
 
        spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_LOCK);
        return ret;
@@ -1406,7 +1669,18 @@ static int write_sr_cr(struct spi_nor *nor, u8 *sr_cr)
 
        write_enable(nor);
 
-       ret = nor->write_reg(nor, SPINOR_OP_WRSR, sr_cr, 2);
+       if (nor->spimem) {
+               struct spi_mem_op op =
+                       SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WRSR, 1),
+                                  SPI_MEM_OP_NO_ADDR,
+                                  SPI_MEM_OP_NO_DUMMY,
+                                  SPI_MEM_OP_DATA_OUT(2, sr_cr, 1));
+
+               ret = spi_mem_exec_op(nor->spimem, &op);
+       } else {
+               ret = nor->write_reg(nor, SPINOR_OP_WRSR, sr_cr, 2);
+       }
+
        if (ret < 0) {
                dev_err(nor->dev,
                        "error while writing configuration register\n");
@@ -1485,9 +1759,11 @@ static int macronix_quad_enable(struct spi_nor *nor)
  */
 static int spansion_quad_enable(struct spi_nor *nor)
 {
-       u8 sr_cr[2] = {0, CR_QUAD_EN_SPAN};
+       u8 *sr_cr = nor->bouncebuf;
        int ret;
 
+       sr_cr[0] = 0;
+       sr_cr[1] = CR_QUAD_EN_SPAN;
        ret = write_sr_cr(nor, sr_cr);
        if (ret)
                return ret;
@@ -1517,7 +1793,7 @@ static int spansion_quad_enable(struct spi_nor *nor)
  */
 static int spansion_no_read_cr_quad_enable(struct spi_nor *nor)
 {
-       u8 sr_cr[2];
+       u8 *sr_cr = nor->bouncebuf;
        int ret;
 
        /* Keep the current value of the Status Register. */
@@ -1548,7 +1824,7 @@ static int spansion_no_read_cr_quad_enable(struct spi_nor *nor)
 static int spansion_read_cr_quad_enable(struct spi_nor *nor)
 {
        struct device *dev = nor->dev;
-       u8 sr_cr[2];
+       u8 *sr_cr = nor->bouncebuf;
        int ret;
 
        /* Check current Quad Enable bit value. */
@@ -1585,6 +1861,36 @@ static int spansion_read_cr_quad_enable(struct spi_nor *nor)
        return 0;
 }
 
+static int spi_nor_write_sr2(struct spi_nor *nor, u8 *sr2)
+{
+       if (nor->spimem) {
+               struct spi_mem_op op =
+                       SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WRSR2, 1),
+                                  SPI_MEM_OP_NO_ADDR,
+                                  SPI_MEM_OP_NO_DUMMY,
+                                  SPI_MEM_OP_DATA_OUT(1, sr2, 1));
+
+               return spi_mem_exec_op(nor->spimem, &op);
+       }
+
+       return nor->write_reg(nor, SPINOR_OP_WRSR2, sr2, 1);
+}
+
+static int spi_nor_read_sr2(struct spi_nor *nor, u8 *sr2)
+{
+       if (nor->spimem) {
+               struct spi_mem_op op =
+                       SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDSR2, 1),
+                                  SPI_MEM_OP_NO_ADDR,
+                                  SPI_MEM_OP_NO_DUMMY,
+                                  SPI_MEM_OP_DATA_IN(1, sr2, 1));
+
+               return spi_mem_exec_op(nor->spimem, &op);
+       }
+
+       return nor->read_reg(nor, SPINOR_OP_RDSR2, sr2, 1);
+}
+
 /**
  * sr2_bit7_quad_enable() - set QE bit in Status Register 2.
  * @nor:       pointer to a 'struct spi_nor'
@@ -1599,22 +1905,22 @@ static int spansion_read_cr_quad_enable(struct spi_nor *nor)
  */
 static int sr2_bit7_quad_enable(struct spi_nor *nor)
 {
-       u8 sr2;
+       u8 *sr2 = nor->bouncebuf;
        int ret;
 
        /* Check current Quad Enable bit value. */
-       ret = nor->read_reg(nor, SPINOR_OP_RDSR2, &sr2, 1);
+       ret = spi_nor_read_sr2(nor, sr2);
        if (ret)
                return ret;
-       if (sr2 & SR2_QUAD_EN_BIT7)
+       if (*sr2 & SR2_QUAD_EN_BIT7)
                return 0;
 
        /* Update the Quad Enable bit. */
-       sr2 |= SR2_QUAD_EN_BIT7;
+       *sr2 |= SR2_QUAD_EN_BIT7;
 
        write_enable(nor);
 
-       ret = nor->write_reg(nor, SPINOR_OP_WRSR2, &sr2, 1);
+       ret = spi_nor_write_sr2(nor, sr2);
        if (ret < 0) {
                dev_err(nor->dev, "error while writing status register 2\n");
                return -EINVAL;
@@ -1627,8 +1933,8 @@ static int sr2_bit7_quad_enable(struct spi_nor *nor)
        }
 
        /* Read back and check it. */
-       ret = nor->read_reg(nor, SPINOR_OP_RDSR2, &sr2, 1);
-       if (!(ret > 0 && (sr2 & SR2_QUAD_EN_BIT7))) {
+       ret = spi_nor_read_sr2(nor, sr2);
+       if (!(ret > 0 && (*sr2 & SR2_QUAD_EN_BIT7))) {
                dev_err(nor->dev, "SR2 Quad bit not set\n");
                return -EINVAL;
        }
@@ -1687,7 +1993,7 @@ static int spi_nor_spansion_clear_sr_bp(struct spi_nor *nor)
 {
        int ret;
        u8 mask = SR_BP2 | SR_BP1 | SR_BP0;
-       u8 sr_cr[2] = {0};
+       u8 *sr_cr =  nor->bouncebuf;
 
        /* Check current Quad Enable bit value. */
        ret = read_cr(nor);
@@ -1822,6 +2128,21 @@ static struct spi_nor_fixups mx25l25635_fixups = {
        .post_bfpt = mx25l25635_post_bfpt_fixups,
 };
 
+static void gd25q256_default_init(struct spi_nor *nor)
+{
+       /*
+        * Some manufacturer like GigaDevice may use different
+        * bit to set QE on different memories, so the MFR can't
+        * indicate the quad_enable method for this case, we need
+        * to set it in the default_init fixup hook.
+        */
+       nor->params.quad_enable = macronix_quad_enable;
+}
+
+static struct spi_nor_fixups gd25q256_fixups = {
+       .default_init = gd25q256_default_init,
+};
+
 /* NOTE: double check command sets and memory organization when you add
  * more nor chips.  This current list focusses on newer chips, which
  * have been converging on command sets which including JEDEC ID.
@@ -1914,7 +2235,7 @@ static const struct flash_info spi_nor_ids[] = {
                "gd25q256", INFO(0xc84019, 0, 64 * 1024, 512,
                        SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ |
                        SPI_NOR_4B_OPCODES | SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
-                       .quad_enable = macronix_quad_enable,
+                       .fixups = &gd25q256_fixups,
        },
 
        /* Intel/Numonyx -- xxxs33b */
@@ -1988,13 +2309,16 @@ static const struct flash_info spi_nor_ids[] = {
        { "n25q128a13",  INFO(0x20ba18, 0, 64 * 1024,  256, SECT_4K | SPI_NOR_QUAD_READ) },
        { "n25q256a",    INFO(0x20ba19, 0, 64 * 1024,  512, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
        { "n25q256ax1",  INFO(0x20bb19, 0, 64 * 1024,  512, SECT_4K | SPI_NOR_QUAD_READ) },
-       { "n25q512a",    INFO(0x20bb20, 0, 64 * 1024, 1024, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ) },
        { "n25q512ax3",  INFO(0x20ba20, 0, 64 * 1024, 1024, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ) },
        { "n25q00",      INFO(0x20ba21, 0, 64 * 1024, 2048, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ | NO_CHIP_ERASE) },
        { "n25q00a",     INFO(0x20bb21, 0, 64 * 1024, 2048, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ | NO_CHIP_ERASE) },
        { "mt25ql02g",   INFO(0x20ba22, 0, 64 * 1024, 4096,
                              SECT_4K | USE_FSR | SPI_NOR_QUAD_READ |
                              NO_CHIP_ERASE) },
+       { "mt25qu512a (n25q512a)", INFO(0x20bb20, 0, 64 * 1024, 1024,
+                                       SECT_4K | USE_FSR | SPI_NOR_DUAL_READ |
+                                       SPI_NOR_QUAD_READ |
+                                       SPI_NOR_4B_OPCODES) },
        { "mt25qu02g",   INFO(0x20bb22, 0, 64 * 1024, 4096, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ | NO_CHIP_ERASE) },
 
        /* Micron */
@@ -2003,6 +2327,9 @@ static const struct flash_info spi_nor_ids[] = {
                        SECT_4K | USE_FSR | SPI_NOR_OCTAL_READ |
                        SPI_NOR_4B_OPCODES)
        },
+       { "mt35xu02g",  INFO(0x2c5b1c, 0, 128 * 1024, 2048,
+                            SECT_4K | USE_FSR | SPI_NOR_OCTAL_READ |
+                            SPI_NOR_4B_OPCODES) },
 
        /* PMC */
        { "pm25lv512",   INFO(0,        0, 32 * 1024,    2, SECT_4K_PMC) },
@@ -2022,7 +2349,7 @@ static const struct flash_info spi_nor_ids[] = {
        { "s25fl256s1", INFO(0x010219, 0x4d01,  64 * 1024, 512, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | USE_CLSR) },
        { "s25fl512s",  INFO6(0x010220, 0x4d0080, 256 * 1024, 256,
                        SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ |
-                       SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | USE_CLSR) },
+                       SPI_NOR_HAS_LOCK | USE_CLSR) },
        { "s25fs512s",  INFO6(0x010220, 0x4d0081, 256 * 1024, 256, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | USE_CLSR) },
        { "s70fl01gs",  INFO(0x010221, 0x4d00, 256 * 1024, 256, 0) },
        { "s25sl12800", INFO(0x012018, 0x0300, 256 * 1024,  64, 0) },
@@ -2060,6 +2387,8 @@ static const struct flash_info spi_nor_ids[] = {
        { "sst25wf040b", INFO(0x621613, 0, 64 * 1024,  8, SECT_4K) },
        { "sst25wf040",  INFO(0xbf2504, 0, 64 * 1024,  8, SECT_4K | SST_WRITE) },
        { "sst25wf080",  INFO(0xbf2505, 0, 64 * 1024, 16, SECT_4K | SST_WRITE) },
+       { "sst26wf016b", INFO(0xbf2651, 0, 64 * 1024, 32, SECT_4K |
+                             SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
        { "sst26vf064b", INFO(0xbf2643, 0, 64 * 1024, 128, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
 
        /* ST Microelectronics -- newer production may have feature updates */
@@ -2151,6 +2480,8 @@ static const struct flash_info spi_nor_ids[] = {
        { "w25q80bl", INFO(0xef4014, 0, 64 * 1024,  16, SECT_4K) },
        { "w25q128", INFO(0xef4018, 0, 64 * 1024, 256, SECT_4K) },
        { "w25q256", INFO(0xef4019, 0, 64 * 1024, 512, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
+       { "w25q256jvm", INFO(0xef7019, 0, 64 * 1024, 512,
+                            SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
        { "w25m512jv", INFO(0xef7119, 0, 64 * 1024, 1024,
                        SECT_4K | SPI_NOR_QUAD_READ | SPI_NOR_DUAL_READ) },
 
@@ -2177,10 +2508,21 @@ static const struct flash_info spi_nor_ids[] = {
 static const struct flash_info *spi_nor_read_id(struct spi_nor *nor)
 {
        int                     tmp;
-       u8                      id[SPI_NOR_MAX_ID_LEN];
+       u8                      *id = nor->bouncebuf;
        const struct flash_info *info;
 
-       tmp = nor->read_reg(nor, SPINOR_OP_RDID, id, SPI_NOR_MAX_ID_LEN);
+       if (nor->spimem) {
+               struct spi_mem_op op =
+                       SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDID, 1),
+                                  SPI_MEM_OP_NO_ADDR,
+                                  SPI_MEM_OP_NO_DUMMY,
+                                  SPI_MEM_OP_DATA_IN(SPI_NOR_MAX_ID_LEN, id, 1));
+
+               tmp = spi_mem_exec_op(nor->spimem, &op);
+       } else {
+               tmp = nor->read_reg(nor, SPINOR_OP_RDID, id,
+                                   SPI_NOR_MAX_ID_LEN);
+       }
        if (tmp < 0) {
                dev_err(nor->dev, "error %d reading JEDEC ID\n", tmp);
                return ERR_PTR(tmp);
@@ -2213,10 +2555,9 @@ static int spi_nor_read(struct mtd_info *mtd, loff_t from, size_t len,
        while (len) {
                loff_t addr = from;
 
-               if (nor->flags & SNOR_F_S3AN_ADDR_DEFAULT)
-                       addr = spi_nor_s3an_addr_convert(nor, addr);
+               addr = spi_nor_convert_addr(nor, addr);
 
-               ret = nor->read(nor, addr, len, buf);
+               ret = spi_nor_read_data(nor, addr, len, buf);
                if (ret == 0) {
                        /* We shouldn't see 0-length reads */
                        ret = -EIO;
@@ -2261,7 +2602,7 @@ static int sst_write(struct mtd_info *mtd, loff_t to, size_t len,
                nor->program_opcode = SPINOR_OP_BP;
 
                /* write one byte. */
-               ret = nor->write(nor, to, 1, buf);
+               ret = spi_nor_write_data(nor, to, 1, buf);
                if (ret < 0)
                        goto sst_write_err;
                WARN(ret != 1, "While writing 1 byte written %i bytes\n",
@@ -2277,7 +2618,7 @@ static int sst_write(struct mtd_info *mtd, loff_t to, size_t len,
                nor->program_opcode = SPINOR_OP_AAI_WP;
 
                /* write two bytes. */
-               ret = nor->write(nor, to, 2, buf + actual);
+               ret = spi_nor_write_data(nor, to, 2, buf + actual);
                if (ret < 0)
                        goto sst_write_err;
                WARN(ret != 2, "While writing 2 bytes written %i bytes\n",
@@ -2300,7 +2641,7 @@ static int sst_write(struct mtd_info *mtd, loff_t to, size_t len,
                write_enable(nor);
 
                nor->program_opcode = SPINOR_OP_BP;
-               ret = nor->write(nor, to, 1, buf + actual);
+               ret = spi_nor_write_data(nor, to, 1, buf + actual);
                if (ret < 0)
                        goto sst_write_err;
                WARN(ret != 1, "While writing 1 byte written %i bytes\n",
@@ -2358,11 +2699,10 @@ static int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len,
                page_remain = min_t(size_t,
                                    nor->page_size - page_offset, len - i);
 
-               if (nor->flags & SNOR_F_S3AN_ADDR_DEFAULT)
-                       addr = spi_nor_s3an_addr_convert(nor, addr);
+               addr = spi_nor_convert_addr(nor, addr);
 
                write_enable(nor);
-               ret = nor->write(nor, addr, page_remain, buf + i);
+               ret = spi_nor_write_data(nor, addr, page_remain, buf + i);
                if (ret < 0)
                        goto write_err;
                written = ret;
@@ -2381,8 +2721,10 @@ write_err:
 
 static int spi_nor_check(struct spi_nor *nor)
 {
-       if (!nor->dev || !nor->read || !nor->write ||
-               !nor->read_reg || !nor->write_reg) {
+       if (!nor->dev ||
+           (!nor->spimem &&
+           (!nor->read || !nor->write || !nor->read_reg ||
+             !nor->write_reg))) {
                pr_err("spi-nor: please fill all the necessary fields!\n");
                return -EINVAL;
        }
@@ -2390,12 +2732,12 @@ static int spi_nor_check(struct spi_nor *nor)
        return 0;
 }
 
-static int s3an_nor_scan(struct spi_nor *nor)
+static int s3an_nor_setup(struct spi_nor *nor,
+                         const struct spi_nor_hwcaps *hwcaps)
 {
        int ret;
-       u8 val;
 
-       ret = nor->read_reg(nor, SPINOR_OP_XRDSR, &val, 1);
+       ret = spi_nor_xread_sr(nor, nor->bouncebuf);
        if (ret < 0) {
                dev_err(nor->dev, "error %d reading XRDSR\n", (int) ret);
                return ret;
@@ -2417,7 +2759,7 @@ static int s3an_nor_scan(struct spi_nor *nor)
         * The current addressing mode can be read from the XRDSR register
         * and should not be changed, because is a destructive operation.
         */
-       if (val & XSR_PAGESIZE) {
+       if (nor->bouncebuf[0] & XSR_PAGESIZE) {
                /* Flash in Power of 2 mode */
                nor->page_size = (nor->page_size == 264) ? 256 : 512;
                nor->mtd.writebufsize = nor->page_size;
@@ -2425,7 +2767,8 @@ static int s3an_nor_scan(struct spi_nor *nor)
                nor->mtd.erasesize = 8 * nor->page_size;
        } else {
                /* Flash in Default addressing mode */
-               nor->flags |= SNOR_F_S3AN_ADDR_DEFAULT;
+               nor->params.convert_addr = s3an_convert_addr;
+               nor->mtd.erasesize = nor->info->sector_size;
        }
 
        return 0;
@@ -2525,11 +2868,11 @@ static int spi_nor_read_raw(struct spi_nor *nor, u32 addr, size_t len, u8 *buf)
        int ret;
 
        while (len) {
-               ret = nor->read(nor, addr, len, buf);
-               if (!ret || ret > len)
-                       return -EIO;
+               ret = spi_nor_read_data(nor, addr, len, buf);
                if (ret < 0)
                        return ret;
+               if (!ret || ret > len)
+                       return -EIO;
 
                buf += ret;
                addr += ret;
@@ -2574,6 +2917,126 @@ static int spi_nor_read_sfdp(struct spi_nor *nor, u32 addr,
        return ret;
 }
 
+/**
+ * spi_nor_spimem_check_op - check if the operation is supported
+ *                           by controller
+ *@nor:        pointer to a 'struct spi_nor'
+ *@op:         pointer to op template to be checked
+ *
+ * Returns 0 if operation is supported, -ENOTSUPP otherwise.
+ */
+static int spi_nor_spimem_check_op(struct spi_nor *nor,
+                                  struct spi_mem_op *op)
+{
+       /*
+        * First test with 4 address bytes. The opcode itself might
+        * be a 3B addressing opcode but we don't care, because
+        * SPI controller implementation should not check the opcode,
+        * but just the sequence.
+        */
+       op->addr.nbytes = 4;
+       if (!spi_mem_supports_op(nor->spimem, op)) {
+               if (nor->mtd.size > SZ_16M)
+                       return -ENOTSUPP;
+
+               /* If flash size <= 16MB, 3 address bytes are sufficient */
+               op->addr.nbytes = 3;
+               if (!spi_mem_supports_op(nor->spimem, op))
+                       return -ENOTSUPP;
+       }
+
+       return 0;
+}
+
+/**
+ * spi_nor_spimem_check_readop - check if the read op is supported
+ *                               by controller
+ *@nor:         pointer to a 'struct spi_nor'
+ *@read:        pointer to op template to be checked
+ *
+ * Returns 0 if operation is supported, -ENOTSUPP otherwise.
+ */
+static int spi_nor_spimem_check_readop(struct spi_nor *nor,
+                                      const struct spi_nor_read_command *read)
+{
+       struct spi_mem_op op = SPI_MEM_OP(SPI_MEM_OP_CMD(read->opcode, 1),
+                                         SPI_MEM_OP_ADDR(3, 0, 1),
+                                         SPI_MEM_OP_DUMMY(0, 1),
+                                         SPI_MEM_OP_DATA_IN(0, NULL, 1));
+
+       op.cmd.buswidth = spi_nor_get_protocol_inst_nbits(read->proto);
+       op.addr.buswidth = spi_nor_get_protocol_addr_nbits(read->proto);
+       op.data.buswidth = spi_nor_get_protocol_data_nbits(read->proto);
+       op.dummy.buswidth = op.addr.buswidth;
+       op.dummy.nbytes = (read->num_mode_clocks + read->num_wait_states) *
+                         op.dummy.buswidth / 8;
+
+       return spi_nor_spimem_check_op(nor, &op);
+}
+
+/**
+ * spi_nor_spimem_check_pp - check if the page program op is supported
+ *                           by controller
+ *@nor:         pointer to a 'struct spi_nor'
+ *@pp:          pointer to op template to be checked
+ *
+ * Returns 0 if operation is supported, -ENOTSUPP otherwise.
+ */
+static int spi_nor_spimem_check_pp(struct spi_nor *nor,
+                                  const struct spi_nor_pp_command *pp)
+{
+       struct spi_mem_op op = SPI_MEM_OP(SPI_MEM_OP_CMD(pp->opcode, 1),
+                                         SPI_MEM_OP_ADDR(3, 0, 1),
+                                         SPI_MEM_OP_NO_DUMMY,
+                                         SPI_MEM_OP_DATA_OUT(0, NULL, 1));
+
+       op.cmd.buswidth = spi_nor_get_protocol_inst_nbits(pp->proto);
+       op.addr.buswidth = spi_nor_get_protocol_addr_nbits(pp->proto);
+       op.data.buswidth = spi_nor_get_protocol_data_nbits(pp->proto);
+
+       return spi_nor_spimem_check_op(nor, &op);
+}
+
+/**
+ * spi_nor_spimem_adjust_hwcaps - Find optimal Read/Write protocol
+ *                                based on SPI controller capabilities
+ * @nor:        pointer to a 'struct spi_nor'
+ * @hwcaps:     pointer to resulting capabilities after adjusting
+ *              according to controller and flash's capability
+ */
+static void
+spi_nor_spimem_adjust_hwcaps(struct spi_nor *nor, u32 *hwcaps)
+{
+       struct spi_nor_flash_parameter *params =  &nor->params;
+       unsigned int cap;
+
+       /* DTR modes are not supported yet, mask them all. */
+       *hwcaps &= ~SNOR_HWCAPS_DTR;
+
+       /* X-X-X modes are not supported yet, mask them all. */
+       *hwcaps &= ~SNOR_HWCAPS_X_X_X;
+
+       for (cap = 0; cap < sizeof(*hwcaps) * BITS_PER_BYTE; cap++) {
+               int rdidx, ppidx;
+
+               if (!(*hwcaps & BIT(cap)))
+                       continue;
+
+               rdidx = spi_nor_hwcaps_read2cmd(BIT(cap));
+               if (rdidx >= 0 &&
+                   spi_nor_spimem_check_readop(nor, &params->reads[rdidx]))
+                       *hwcaps &= ~BIT(cap);
+
+               ppidx = spi_nor_hwcaps_pp2cmd(BIT(cap));
+               if (ppidx < 0)
+                       continue;
+
+               if (spi_nor_spimem_check_pp(nor,
+                                           &params->page_programs[ppidx]))
+                       *hwcaps &= ~BIT(cap);
+       }
+}
+
 /**
  * spi_nor_read_sfdp_dma_unsafe() - read Serial Flash Discoverable Parameters.
  * @nor:       pointer to a 'struct spi_nor'
@@ -2892,7 +3355,7 @@ static int spi_nor_parse_bfpt(struct spi_nor *nor,
                              const struct sfdp_parameter_header *bfpt_header,
                              struct spi_nor_flash_parameter *params)
 {
-       struct spi_nor_erase_map *map = &nor->erase_map;
+       struct spi_nor_erase_map *map = &params->erase_map;
        struct spi_nor_erase_type *erase_type = map->erase_type;
        struct sfdp_bfpt bfpt;
        size_t len;
@@ -2973,7 +3436,7 @@ static int spi_nor_parse_bfpt(struct spi_nor *nor,
         * Erase Types defined in the bfpt table.
         */
        erase_mask = 0;
-       memset(&nor->erase_map, 0, sizeof(nor->erase_map));
+       memset(&params->erase_map, 0, sizeof(params->erase_map));
        for (i = 0; i < ARRAY_SIZE(sfdp_bfpt_erases); i++) {
                const struct sfdp_bfpt_erase *er = &sfdp_bfpt_erases[i];
                u32 erasesize;
@@ -3248,14 +3711,18 @@ spi_nor_region_check_overlay(struct spi_nor_erase_region *region,
 /**
  * spi_nor_init_non_uniform_erase_map() - initialize the non-uniform erase map
  * @nor:       pointer to a 'struct spi_nor'
+ * @params:     pointer to a duplicate 'struct spi_nor_flash_parameter' that is
+ *              used for storing SFDP parsed data
  * @smpt:      pointer to the sector map parameter table
  *
  * Return: 0 on success, -errno otherwise.
  */
-static int spi_nor_init_non_uniform_erase_map(struct spi_nor *nor,
-                                             const u32 *smpt)
+static int
+spi_nor_init_non_uniform_erase_map(struct spi_nor *nor,
+                                  struct spi_nor_flash_parameter *params,
+                                  const u32 *smpt)
 {
-       struct spi_nor_erase_map *map = &nor->erase_map;
+       struct spi_nor_erase_map *map = &params->erase_map;
        struct spi_nor_erase_type *erase = map->erase_type;
        struct spi_nor_erase_region *region;
        u64 offset;
@@ -3334,6 +3801,8 @@ static int spi_nor_init_non_uniform_erase_map(struct spi_nor *nor,
  * spi_nor_parse_smpt() - parse Sector Map Parameter Table
  * @nor:               pointer to a 'struct spi_nor'
  * @smpt_header:       sector map parameter table header
+ * @params:            pointer to a duplicate 'struct spi_nor_flash_parameter'
+ *                      that is used for storing SFDP parsed data
  *
  * This table is optional, but when available, we parse it to identify the
  * location and size of sectors within the main data array of the flash memory
@@ -3342,7 +3811,8 @@ static int spi_nor_init_non_uniform_erase_map(struct spi_nor *nor,
  * Return: 0 on success, -errno otherwise.
  */
 static int spi_nor_parse_smpt(struct spi_nor *nor,
-                             const struct sfdp_parameter_header *smpt_header)
+                             const struct sfdp_parameter_header *smpt_header,
+                             struct spi_nor_flash_parameter *params)
 {
        const u32 *sector_map;
        u32 *smpt;
@@ -3371,11 +3841,11 @@ static int spi_nor_parse_smpt(struct spi_nor *nor,
                goto out;
        }
 
-       ret = spi_nor_init_non_uniform_erase_map(nor, sector_map);
+       ret = spi_nor_init_non_uniform_erase_map(nor, params, sector_map);
        if (ret)
                goto out;
 
-       spi_nor_regions_sort_erase_types(&nor->erase_map);
+       spi_nor_regions_sort_erase_types(&params->erase_map);
        /* fall through */
 out:
        kfree(smpt);
@@ -3431,7 +3901,7 @@ static int spi_nor_parse_4bait(struct spi_nor *nor,
                { 0u /* not used */,            BIT(12) },
        };
        struct spi_nor_pp_command *params_pp = params->page_programs;
-       struct spi_nor_erase_map *map = &nor->erase_map;
+       struct spi_nor_erase_map *map = &params->erase_map;
        struct spi_nor_erase_type *erase_type = map->erase_type;
        u32 *dwords;
        size_t len;
@@ -3453,7 +3923,7 @@ static int spi_nor_parse_4bait(struct spi_nor *nor,
        addr = SFDP_PARAM_HEADER_PTP(param_header);
        ret = spi_nor_read_sfdp(nor, addr, len, dwords);
        if (ret)
-               return ret;
+               goto out;
 
        /* Fix endianness of the 4BAIT DWORDs. */
        for (i = 0; i < SFDP_4BAIT_DWORD_MAX; i++)
@@ -3661,7 +4131,7 @@ static int spi_nor_parse_sfdp(struct spi_nor *nor,
 
                switch (SFDP_PARAM_HEADER_ID(param_header)) {
                case SFDP_SECTOR_MAP_ID:
-                       err = spi_nor_parse_smpt(nor, param_header);
+                       err = spi_nor_parse_smpt(nor, param_header, params);
                        break;
 
                case SFDP_4BAIT_ID:
@@ -3690,137 +4160,11 @@ exit:
        return err;
 }
 
-static int spi_nor_init_params(struct spi_nor *nor,
-                              struct spi_nor_flash_parameter *params)
+static int spi_nor_select_read(struct spi_nor *nor,
+                              u32 shared_hwcaps)
 {
-       struct spi_nor_erase_map *map = &nor->erase_map;
-       const struct flash_info *info = nor->info;
-       u8 i, erase_mask;
-
-       /* Set legacy flash parameters as default. */
-       memset(params, 0, sizeof(*params));
-
-       /* Set SPI NOR sizes. */
-       params->size = (u64)info->sector_size * info->n_sectors;
-       params->page_size = info->page_size;
-
-       /* (Fast) Read settings. */
-       params->hwcaps.mask |= SNOR_HWCAPS_READ;
-       spi_nor_set_read_settings(&params->reads[SNOR_CMD_READ],
-                                 0, 0, SPINOR_OP_READ,
-                                 SNOR_PROTO_1_1_1);
-
-       if (!(info->flags & SPI_NOR_NO_FR)) {
-               params->hwcaps.mask |= SNOR_HWCAPS_READ_FAST;
-               spi_nor_set_read_settings(&params->reads[SNOR_CMD_READ_FAST],
-                                         0, 8, SPINOR_OP_READ_FAST,
-                                         SNOR_PROTO_1_1_1);
-       }
-
-       if (info->flags & SPI_NOR_DUAL_READ) {
-               params->hwcaps.mask |= SNOR_HWCAPS_READ_1_1_2;
-               spi_nor_set_read_settings(&params->reads[SNOR_CMD_READ_1_1_2],
-                                         0, 8, SPINOR_OP_READ_1_1_2,
-                                         SNOR_PROTO_1_1_2);
-       }
-
-       if (info->flags & SPI_NOR_QUAD_READ) {
-               params->hwcaps.mask |= SNOR_HWCAPS_READ_1_1_4;
-               spi_nor_set_read_settings(&params->reads[SNOR_CMD_READ_1_1_4],
-                                         0, 8, SPINOR_OP_READ_1_1_4,
-                                         SNOR_PROTO_1_1_4);
-       }
-
-       if (info->flags & SPI_NOR_OCTAL_READ) {
-               params->hwcaps.mask |= SNOR_HWCAPS_READ_1_1_8;
-               spi_nor_set_read_settings(&params->reads[SNOR_CMD_READ_1_1_8],
-                                         0, 8, SPINOR_OP_READ_1_1_8,
-                                         SNOR_PROTO_1_1_8);
-       }
-
-       /* Page Program settings. */
-       params->hwcaps.mask |= SNOR_HWCAPS_PP;
-       spi_nor_set_pp_settings(&params->page_programs[SNOR_CMD_PP],
-                               SPINOR_OP_PP, SNOR_PROTO_1_1_1);
-
-       /*
-        * Sector Erase settings. Sort Erase Types in ascending order, with the
-        * smallest erase size starting at BIT(0).
-        */
-       erase_mask = 0;
-       i = 0;
-       if (info->flags & SECT_4K_PMC) {
-               erase_mask |= BIT(i);
-               spi_nor_set_erase_type(&map->erase_type[i], 4096u,
-                                      SPINOR_OP_BE_4K_PMC);
-               i++;
-       } else if (info->flags & SECT_4K) {
-               erase_mask |= BIT(i);
-               spi_nor_set_erase_type(&map->erase_type[i], 4096u,
-                                      SPINOR_OP_BE_4K);
-               i++;
-       }
-       erase_mask |= BIT(i);
-       spi_nor_set_erase_type(&map->erase_type[i], info->sector_size,
-                              SPINOR_OP_SE);
-       spi_nor_init_uniform_erase_map(map, erase_mask, params->size);
-
-       /* Select the procedure to set the Quad Enable bit. */
-       if (params->hwcaps.mask & (SNOR_HWCAPS_READ_QUAD |
-                                  SNOR_HWCAPS_PP_QUAD)) {
-               switch (JEDEC_MFR(info)) {
-               case SNOR_MFR_MACRONIX:
-                       params->quad_enable = macronix_quad_enable;
-                       break;
-
-               case SNOR_MFR_ST:
-               case SNOR_MFR_MICRON:
-                       break;
-
-               default:
-                       /* Kept only for backward compatibility purpose. */
-                       params->quad_enable = spansion_quad_enable;
-                       break;
-               }
-
-               /*
-                * Some manufacturer like GigaDevice may use different
-                * bit to set QE on different memories, so the MFR can't
-                * indicate the quad_enable method for this case, we need
-                * set it in flash info list.
-                */
-               if (info->quad_enable)
-                       params->quad_enable = info->quad_enable;
-       }
-
-       if ((info->flags & (SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ)) &&
-           !(info->flags & SPI_NOR_SKIP_SFDP)) {
-               struct spi_nor_flash_parameter sfdp_params;
-               struct spi_nor_erase_map prev_map;
-
-               memcpy(&sfdp_params, params, sizeof(sfdp_params));
-               memcpy(&prev_map, &nor->erase_map, sizeof(prev_map));
-
-               if (spi_nor_parse_sfdp(nor, &sfdp_params)) {
-                       nor->addr_width = 0;
-                       nor->flags &= ~SNOR_F_4B_OPCODES;
-                       /* restore previous erase map */
-                       memcpy(&nor->erase_map, &prev_map,
-                              sizeof(nor->erase_map));
-               } else {
-                       memcpy(params, &sfdp_params, sizeof(*params));
-               }
-       }
-
-       return 0;
-}
-
-static int spi_nor_select_read(struct spi_nor *nor,
-                              const struct spi_nor_flash_parameter *params,
-                              u32 shared_hwcaps)
-{
-       int cmd, best_match = fls(shared_hwcaps & SNOR_HWCAPS_READ_MASK) - 1;
-       const struct spi_nor_read_command *read;
+       int cmd, best_match = fls(shared_hwcaps & SNOR_HWCAPS_READ_MASK) - 1;
+       const struct spi_nor_read_command *read;
 
        if (best_match < 0)
                return -EINVAL;
@@ -3829,7 +4173,7 @@ static int spi_nor_select_read(struct spi_nor *nor,
        if (cmd < 0)
                return -EINVAL;
 
-       read = &params->reads[cmd];
+       read = &nor->params.reads[cmd];
        nor->read_opcode = read->opcode;
        nor->read_proto = read->proto;
 
@@ -3848,7 +4192,6 @@ static int spi_nor_select_read(struct spi_nor *nor,
 }
 
 static int spi_nor_select_pp(struct spi_nor *nor,
-                            const struct spi_nor_flash_parameter *params,
                             u32 shared_hwcaps)
 {
        int cmd, best_match = fls(shared_hwcaps & SNOR_HWCAPS_PP_MASK) - 1;
@@ -3861,7 +4204,7 @@ static int spi_nor_select_pp(struct spi_nor *nor,
        if (cmd < 0)
                return -EINVAL;
 
-       pp = &params->page_programs[cmd];
+       pp = &nor->params.page_programs[cmd];
        nor->program_opcode = pp->opcode;
        nor->write_proto = pp->proto;
        return 0;
@@ -3920,11 +4263,12 @@ spi_nor_select_uniform_erase(struct spi_nor_erase_map *map,
        return erase;
 }
 
-static int spi_nor_select_erase(struct spi_nor *nor, u32 wanted_size)
+static int spi_nor_select_erase(struct spi_nor *nor)
 {
-       struct spi_nor_erase_map *map = &nor->erase_map;
+       struct spi_nor_erase_map *map = &nor->params.erase_map;
        const struct spi_nor_erase_type *erase = NULL;
        struct mtd_info *mtd = &nor->mtd;
+       u32 wanted_size = nor->info->sector_size;
        int i;
 
        /*
@@ -3967,12 +4311,11 @@ static int spi_nor_select_erase(struct spi_nor *nor, u32 wanted_size)
        return 0;
 }
 
-static int spi_nor_setup(struct spi_nor *nor,
-                        const struct spi_nor_flash_parameter *params,
-                        const struct spi_nor_hwcaps *hwcaps)
+static int spi_nor_default_setup(struct spi_nor *nor,
+                                const struct spi_nor_hwcaps *hwcaps)
 {
+       struct spi_nor_flash_parameter *params = &nor->params;
        u32 ignored_mask, shared_mask;
-       bool enable_quad_io;
        int err;
 
        /*
@@ -3981,20 +4324,29 @@ static int spi_nor_setup(struct spi_nor *nor,
         */
        shared_mask = hwcaps->mask & params->hwcaps.mask;
 
-       /* SPI n-n-n protocols are not supported yet. */
-       ignored_mask = (SNOR_HWCAPS_READ_2_2_2 |
-                       SNOR_HWCAPS_READ_4_4_4 |
-                       SNOR_HWCAPS_READ_8_8_8 |
-                       SNOR_HWCAPS_PP_4_4_4 |
-                       SNOR_HWCAPS_PP_8_8_8);
-       if (shared_mask & ignored_mask) {
-               dev_dbg(nor->dev,
-                       "SPI n-n-n protocols are not supported yet.\n");
-               shared_mask &= ~ignored_mask;
+       if (nor->spimem) {
+               /*
+                * When called from spi_nor_probe(), all caps are set and we
+                * need to discard some of them based on what the SPI
+                * controller actually supports (using spi_mem_supports_op()).
+                */
+               spi_nor_spimem_adjust_hwcaps(nor, &shared_mask);
+       } else {
+               /*
+                * SPI n-n-n protocols are not supported when the SPI
+                * controller directly implements the spi_nor interface.
+                * Yet another reason to switch to spi-mem.
+                */
+               ignored_mask = SNOR_HWCAPS_X_X_X;
+               if (shared_mask & ignored_mask) {
+                       dev_dbg(nor->dev,
+                               "SPI n-n-n protocols are not supported.\n");
+                       shared_mask &= ~ignored_mask;
+               }
        }
 
        /* Select the (Fast) Read command. */
-       err = spi_nor_select_read(nor, params, shared_mask);
+       err = spi_nor_select_read(nor, shared_mask);
        if (err) {
                dev_err(nor->dev,
                        "can't select read settings supported by both the SPI controller and memory.\n");
@@ -4002,7 +4354,7 @@ static int spi_nor_setup(struct spi_nor *nor,
        }
 
        /* Select the Page Program command. */
-       err = spi_nor_select_pp(nor, params, shared_mask);
+       err = spi_nor_select_pp(nor, shared_mask);
        if (err) {
                dev_err(nor->dev,
                        "can't select write settings supported by both the SPI controller and memory.\n");
@@ -4010,30 +4362,325 @@ static int spi_nor_setup(struct spi_nor *nor,
        }
 
        /* Select the Sector Erase command. */
-       err = spi_nor_select_erase(nor, nor->info->sector_size);
+       err = spi_nor_select_erase(nor);
        if (err) {
                dev_err(nor->dev,
                        "can't select erase settings supported by both the SPI controller and memory.\n");
                return err;
        }
 
-       /* Enable Quad I/O if needed. */
-       enable_quad_io = (spi_nor_get_protocol_width(nor->read_proto) == 4 ||
-                         spi_nor_get_protocol_width(nor->write_proto) == 4);
-       if (enable_quad_io && params->quad_enable)
-               nor->quad_enable = params->quad_enable;
-       else
-               nor->quad_enable = NULL;
-
        return 0;
 }
 
+static int spi_nor_setup(struct spi_nor *nor,
+                        const struct spi_nor_hwcaps *hwcaps)
+{
+       if (!nor->params.setup)
+               return 0;
+
+       return nor->params.setup(nor, hwcaps);
+}
+
+static void macronix_set_default_init(struct spi_nor *nor)
+{
+       nor->params.quad_enable = macronix_quad_enable;
+       nor->params.set_4byte = macronix_set_4byte;
+}
+
+static void st_micron_set_default_init(struct spi_nor *nor)
+{
+       nor->flags |= SNOR_F_HAS_LOCK;
+       nor->params.quad_enable = NULL;
+       nor->params.set_4byte = st_micron_set_4byte;
+}
+
+static void winbond_set_default_init(struct spi_nor *nor)
+{
+       nor->params.set_4byte = winbond_set_4byte;
+}
+
+/**
+ * spi_nor_manufacturer_init_params() - Initialize the flash's parameters and
+ * settings based on MFR register and ->default_init() hook.
+ * @nor:       pointer to a 'struct spi-nor'.
+ */
+static void spi_nor_manufacturer_init_params(struct spi_nor *nor)
+{
+       /* Init flash parameters based on MFR */
+       switch (JEDEC_MFR(nor->info)) {
+       case SNOR_MFR_MACRONIX:
+               macronix_set_default_init(nor);
+               break;
+
+       case SNOR_MFR_ST:
+       case SNOR_MFR_MICRON:
+               st_micron_set_default_init(nor);
+               break;
+
+       case SNOR_MFR_WINBOND:
+               winbond_set_default_init(nor);
+               break;
+
+       default:
+               break;
+       }
+
+       if (nor->info->fixups && nor->info->fixups->default_init)
+               nor->info->fixups->default_init(nor);
+}
+
+/**
+ * spi_nor_sfdp_init_params() - Initialize the flash's parameters and settings
+ * based on JESD216 SFDP standard.
+ * @nor:       pointer to a 'struct spi-nor'.
+ *
+ * The method has a roll-back mechanism: in case the SFDP parsing fails, the
+ * legacy flash parameters and settings will be restored.
+ */
+static void spi_nor_sfdp_init_params(struct spi_nor *nor)
+{
+       struct spi_nor_flash_parameter sfdp_params;
+
+       memcpy(&sfdp_params, &nor->params, sizeof(sfdp_params));
+
+       if (spi_nor_parse_sfdp(nor, &sfdp_params)) {
+               nor->addr_width = 0;
+               nor->flags &= ~SNOR_F_4B_OPCODES;
+       } else {
+               memcpy(&nor->params, &sfdp_params, sizeof(nor->params));
+       }
+}
+
+/**
+ * spi_nor_info_init_params() - Initialize the flash's parameters and settings
+ * based on nor->info data.
+ * @nor:       pointer to a 'struct spi-nor'.
+ */
+static void spi_nor_info_init_params(struct spi_nor *nor)
+{
+       struct spi_nor_flash_parameter *params = &nor->params;
+       struct spi_nor_erase_map *map = &params->erase_map;
+       const struct flash_info *info = nor->info;
+       struct device_node *np = spi_nor_get_flash_node(nor);
+       u8 i, erase_mask;
+
+       /* Initialize legacy flash parameters and settings. */
+       params->quad_enable = spansion_quad_enable;
+       params->set_4byte = spansion_set_4byte;
+       params->setup = spi_nor_default_setup;
+
+       /* Set SPI NOR sizes. */
+       params->size = (u64)info->sector_size * info->n_sectors;
+       params->page_size = info->page_size;
+
+       if (!(info->flags & SPI_NOR_NO_FR)) {
+               /* Default to Fast Read for DT and non-DT platform devices. */
+               params->hwcaps.mask |= SNOR_HWCAPS_READ_FAST;
+
+               /* Mask out Fast Read if not requested at DT instantiation. */
+               if (np && !of_property_read_bool(np, "m25p,fast-read"))
+                       params->hwcaps.mask &= ~SNOR_HWCAPS_READ_FAST;
+       }
+
+       /* (Fast) Read settings. */
+       params->hwcaps.mask |= SNOR_HWCAPS_READ;
+       spi_nor_set_read_settings(&params->reads[SNOR_CMD_READ],
+                                 0, 0, SPINOR_OP_READ,
+                                 SNOR_PROTO_1_1_1);
+
+       if (params->hwcaps.mask & SNOR_HWCAPS_READ_FAST)
+               spi_nor_set_read_settings(&params->reads[SNOR_CMD_READ_FAST],
+                                         0, 8, SPINOR_OP_READ_FAST,
+                                         SNOR_PROTO_1_1_1);
+
+       if (info->flags & SPI_NOR_DUAL_READ) {
+               params->hwcaps.mask |= SNOR_HWCAPS_READ_1_1_2;
+               spi_nor_set_read_settings(&params->reads[SNOR_CMD_READ_1_1_2],
+                                         0, 8, SPINOR_OP_READ_1_1_2,
+                                         SNOR_PROTO_1_1_2);
+       }
+
+       if (info->flags & SPI_NOR_QUAD_READ) {
+               params->hwcaps.mask |= SNOR_HWCAPS_READ_1_1_4;
+               spi_nor_set_read_settings(&params->reads[SNOR_CMD_READ_1_1_4],
+                                         0, 8, SPINOR_OP_READ_1_1_4,
+                                         SNOR_PROTO_1_1_4);
+       }
+
+       if (info->flags & SPI_NOR_OCTAL_READ) {
+               params->hwcaps.mask |= SNOR_HWCAPS_READ_1_1_8;
+               spi_nor_set_read_settings(&params->reads[SNOR_CMD_READ_1_1_8],
+                                         0, 8, SPINOR_OP_READ_1_1_8,
+                                         SNOR_PROTO_1_1_8);
+       }
+
+       /* Page Program settings. */
+       params->hwcaps.mask |= SNOR_HWCAPS_PP;
+       spi_nor_set_pp_settings(&params->page_programs[SNOR_CMD_PP],
+                               SPINOR_OP_PP, SNOR_PROTO_1_1_1);
+
+       /*
+        * Sector Erase settings. Sort Erase Types in ascending order, with the
+        * smallest erase size starting at BIT(0).
+        */
+       erase_mask = 0;
+       i = 0;
+       if (info->flags & SECT_4K_PMC) {
+               erase_mask |= BIT(i);
+               spi_nor_set_erase_type(&map->erase_type[i], 4096u,
+                                      SPINOR_OP_BE_4K_PMC);
+               i++;
+       } else if (info->flags & SECT_4K) {
+               erase_mask |= BIT(i);
+               spi_nor_set_erase_type(&map->erase_type[i], 4096u,
+                                      SPINOR_OP_BE_4K);
+               i++;
+       }
+       erase_mask |= BIT(i);
+       spi_nor_set_erase_type(&map->erase_type[i], info->sector_size,
+                              SPINOR_OP_SE);
+       spi_nor_init_uniform_erase_map(map, erase_mask, params->size);
+}
+
+static void spansion_post_sfdp_fixups(struct spi_nor *nor)
+{
+       struct mtd_info *mtd = &nor->mtd;
+
+       if (mtd->size <= SZ_16M)
+               return;
+
+       nor->flags |= SNOR_F_4B_OPCODES;
+       /* No small sector erase for 4-byte command set */
+       nor->erase_opcode = SPINOR_OP_SE;
+       nor->mtd.erasesize = nor->info->sector_size;
+}
+
+static void s3an_post_sfdp_fixups(struct spi_nor *nor)
+{
+       nor->params.setup = s3an_nor_setup;
+}
+
+/**
+ * spi_nor_post_sfdp_fixups() - Updates the flash's parameters and settings
+ * after SFDP has been parsed (is also called for SPI NORs that do not
+ * support RDSFDP).
+ * @nor:       pointer to a 'struct spi_nor'
+ *
+ * Typically used to tweak various parameters that could not be extracted by
+ * other means (i.e. when information provided by the SFDP/flash_info tables
+ * are incomplete or wrong).
+ */
+static void spi_nor_post_sfdp_fixups(struct spi_nor *nor)
+{
+       switch (JEDEC_MFR(nor->info)) {
+       case SNOR_MFR_SPANSION:
+               spansion_post_sfdp_fixups(nor);
+               break;
+
+       default:
+               break;
+       }
+
+       if (nor->info->flags & SPI_S3AN)
+               s3an_post_sfdp_fixups(nor);
+
+       if (nor->info->fixups && nor->info->fixups->post_sfdp)
+               nor->info->fixups->post_sfdp(nor);
+}
+
+/**
+ * spi_nor_late_init_params() - Late initialization of default flash parameters.
+ * @nor:       pointer to a 'struct spi_nor'
+ *
+ * Used to set default flash parameters and settings when the ->default_init()
+ * hook or the SFDP parser let voids.
+ */
+static void spi_nor_late_init_params(struct spi_nor *nor)
+{
+       /*
+        * NOR protection support. When locking_ops are not provided, we pick
+        * the default ones.
+        */
+       if (nor->flags & SNOR_F_HAS_LOCK && !nor->params.locking_ops)
+               nor->params.locking_ops = &stm_locking_ops;
+}
+
+/**
+ * spi_nor_init_params() - Initialize the flash's parameters and settings.
+ * @nor:       pointer to a 'struct spi-nor'.
+ *
+ * The flash parameters and settings are initialized based on a sequence of
+ * calls that are ordered by priority:
+ *
+ * 1/ Default flash parameters initialization. The initializations are done
+ *    based on nor->info data:
+ *             spi_nor_info_init_params()
+ *
+ * which can be overwritten by:
+ * 2/ Manufacturer flash parameters initialization. The initializations are
+ *    done based on MFR register, or when the decisions can not be done solely
+ *    based on MFR, by using specific flash_info tweeks, ->default_init():
+ *             spi_nor_manufacturer_init_params()
+ *
+ * which can be overwritten by:
+ * 3/ SFDP flash parameters initialization. JESD216 SFDP is a standard and
+ *    should be more accurate that the above.
+ *             spi_nor_sfdp_init_params()
+ *
+ *    Please note that there is a ->post_bfpt() fixup hook that can overwrite
+ *    the flash parameters and settings immediately after parsing the Basic
+ *    Flash Parameter Table.
+ *
+ * which can be overwritten by:
+ * 4/ Post SFDP flash parameters initialization. Used to tweak various
+ *    parameters that could not be extracted by other means (i.e. when
+ *    information provided by the SFDP/flash_info tables are incomplete or
+ *    wrong).
+ *             spi_nor_post_sfdp_fixups()
+ *
+ * 5/ Late default flash parameters initialization, used when the
+ * ->default_init() hook or the SFDP parser do not set specific params.
+ *             spi_nor_late_init_params()
+ */
+static void spi_nor_init_params(struct spi_nor *nor)
+{
+       spi_nor_info_init_params(nor);
+
+       spi_nor_manufacturer_init_params(nor);
+
+       if ((nor->info->flags & (SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ)) &&
+           !(nor->info->flags & SPI_NOR_SKIP_SFDP))
+               spi_nor_sfdp_init_params(nor);
+
+       spi_nor_post_sfdp_fixups(nor);
+
+       spi_nor_late_init_params(nor);
+}
+
+/**
+ * spi_nor_quad_enable() - enable Quad I/O if needed.
+ * @nor:                pointer to a 'struct spi_nor'
+ *
+ * Return: 0 on success, -errno otherwise.
+ */
+static int spi_nor_quad_enable(struct spi_nor *nor)
+{
+       if (!nor->params.quad_enable)
+               return 0;
+
+       if (!(spi_nor_get_protocol_width(nor->read_proto) == 4 ||
+             spi_nor_get_protocol_width(nor->write_proto) == 4))
+               return 0;
+
+       return nor->params.quad_enable(nor);
+}
+
 static int spi_nor_init(struct spi_nor *nor)
 {
        int err;
 
        if (nor->clear_sr_bp) {
-               if (nor->quad_enable == spansion_quad_enable)
+               if (nor->params.quad_enable == spansion_quad_enable)
                        nor->clear_sr_bp = spi_nor_spansion_clear_sr_bp;
 
                err = nor->clear_sr_bp(nor);
@@ -4044,12 +4691,10 @@ static int spi_nor_init(struct spi_nor *nor)
                }
        }
 
-       if (nor->quad_enable) {
-               err = nor->quad_enable(nor);
-               if (err) {
-                       dev_err(nor->dev, "quad mode not supported\n");
-                       return err;
-               }
+       err = spi_nor_quad_enable(nor);
+       if (err) {
+               dev_err(nor->dev, "quad mode not supported\n");
+               return err;
        }
 
        if (nor->addr_width == 4 && !(nor->flags & SNOR_F_4B_OPCODES)) {
@@ -4062,7 +4707,7 @@ static int spi_nor_init(struct spi_nor *nor)
                 */
                WARN_ONCE(nor->flags & SNOR_F_BROKEN_RESET,
                          "enabling reset hack; may not recover from unexpected reboots\n");
-               set_4byte(nor, true);
+               nor->params.set_4byte(nor, true);
        }
 
        return 0;
@@ -4086,7 +4731,7 @@ void spi_nor_restore(struct spi_nor *nor)
        /* restore the addressing mode */
        if (nor->addr_width == 4 && !(nor->flags & SNOR_F_4B_OPCODES) &&
            nor->flags & SNOR_F_BROKEN_RESET)
-               set_4byte(nor, false);
+               nor->params.set_4byte(nor, false);
 }
 EXPORT_SYMBOL_GPL(spi_nor_restore);
 
@@ -4102,25 +4747,47 @@ static const struct flash_info *spi_nor_match_id(const char *name)
        return NULL;
 }
 
-int spi_nor_scan(struct spi_nor *nor, const char *name,
-                const struct spi_nor_hwcaps *hwcaps)
+static int spi_nor_set_addr_width(struct spi_nor *nor)
+{
+       if (nor->addr_width) {
+               /* already configured from SFDP */
+       } else if (nor->info->addr_width) {
+               nor->addr_width = nor->info->addr_width;
+       } else if (nor->mtd.size > 0x1000000) {
+               /* enable 4-byte addressing if the device exceeds 16MiB */
+               nor->addr_width = 4;
+       } else {
+               nor->addr_width = 3;
+       }
+
+       if (nor->addr_width > SPI_NOR_MAX_ADDR_WIDTH) {
+               dev_err(nor->dev, "address width is too large: %u\n",
+                       nor->addr_width);
+               return -EINVAL;
+       }
+
+       /* Set 4byte opcodes when possible. */
+       if (nor->addr_width == 4 && nor->flags & SNOR_F_4B_OPCODES &&
+           !(nor->flags & SNOR_F_HAS_4BAIT))
+               spi_nor_set_4byte_opcodes(nor);
+
+       return 0;
+}
+
+static void spi_nor_debugfs_init(struct spi_nor *nor,
+                                const struct flash_info *info)
 {
-       struct spi_nor_flash_parameter params;
-       const struct flash_info *info = NULL;
-       struct device *dev = nor->dev;
        struct mtd_info *mtd = &nor->mtd;
-       struct device_node *np = spi_nor_get_flash_node(nor);
-       int ret;
-       int i;
 
-       ret = spi_nor_check(nor);
-       if (ret)
-               return ret;
+       mtd->dbg.partname = info->name;
+       mtd->dbg.partid = devm_kasprintf(nor->dev, GFP_KERNEL, "spi-nor:%*phN",
+                                        info->id_len, info->id);
+}
 
-       /* Reset SPI protocol for all commands. */
-       nor->reg_proto = SNOR_PROTO_1_1_1;
-       nor->read_proto = SNOR_PROTO_1_1_1;
-       nor->write_proto = SNOR_PROTO_1_1_1;
+static const struct flash_info *spi_nor_get_flash_info(struct spi_nor *nor,
+                                                      const char *name)
+{
+       const struct flash_info *info = NULL;
 
        if (name)
                info = spi_nor_match_id(name);
@@ -4128,7 +4795,7 @@ int spi_nor_scan(struct spi_nor *nor, const char *name,
        if (!info)
                info = spi_nor_read_id(nor);
        if (IS_ERR_OR_NULL(info))
-               return -ENOENT;
+               return ERR_PTR(-ENOENT);
 
        /*
         * If caller has specified name of flash model that can normally be
@@ -4139,7 +4806,7 @@ int spi_nor_scan(struct spi_nor *nor, const char *name,
 
                jinfo = spi_nor_read_id(nor);
                if (IS_ERR(jinfo)) {
-                       return PTR_ERR(jinfo);
+                       return jinfo;
                } else if (jinfo != info) {
                        /*
                         * JEDEC knows better, so overwrite platform ID. We
@@ -4148,14 +4815,57 @@ int spi_nor_scan(struct spi_nor *nor, const char *name,
                         * marked read-only, and we don't want to lose that
                         * information, even if it's not 100% accurate.
                         */
-                       dev_warn(dev, "found %s, expected %s\n",
+                       dev_warn(nor->dev, "found %s, expected %s\n",
                                 jinfo->name, info->name);
                        info = jinfo;
                }
        }
 
+       return info;
+}
+
+int spi_nor_scan(struct spi_nor *nor, const char *name,
+                const struct spi_nor_hwcaps *hwcaps)
+{
+       const struct flash_info *info;
+       struct device *dev = nor->dev;
+       struct mtd_info *mtd = &nor->mtd;
+       struct device_node *np = spi_nor_get_flash_node(nor);
+       struct spi_nor_flash_parameter *params = &nor->params;
+       int ret;
+       int i;
+
+       ret = spi_nor_check(nor);
+       if (ret)
+               return ret;
+
+       /* Reset SPI protocol for all commands. */
+       nor->reg_proto = SNOR_PROTO_1_1_1;
+       nor->read_proto = SNOR_PROTO_1_1_1;
+       nor->write_proto = SNOR_PROTO_1_1_1;
+
+       /*
+        * We need the bounce buffer early to read/write registers when going
+        * through the spi-mem layer (buffers have to be DMA-able).
+        * For spi-mem drivers, we'll reallocate a new buffer if
+        * nor->page_size turns out to be greater than PAGE_SIZE (which
+        * shouldn't happen before long since NOR pages are usually less
+        * than 1KB) after spi_nor_scan() returns.
+        */
+       nor->bouncebuf_size = PAGE_SIZE;
+       nor->bouncebuf = devm_kmalloc(dev, nor->bouncebuf_size,
+                                     GFP_KERNEL);
+       if (!nor->bouncebuf)
+               return -ENOMEM;
+
+       info = spi_nor_get_flash_info(nor, name);
+       if (IS_ERR(info))
+               return PTR_ERR(info);
+
        nor->info = info;
 
+       spi_nor_debugfs_init(nor, info);
+
        mutex_init(&nor->lock);
 
        /*
@@ -4163,9 +4873,12 @@ int spi_nor_scan(struct spi_nor *nor, const char *name,
         * spi_nor_wait_till_ready(). Xilinx S3AN share MFR
         * with Atmel spi-nor
         */
-       if (info->flags & SPI_S3AN)
+       if (info->flags & SPI_NOR_XSR_RDY)
                nor->flags |=  SNOR_F_READY_XSR_RDY;
 
+       if (info->flags & SPI_NOR_HAS_LOCK)
+               nor->flags |= SNOR_F_HAS_LOCK;
+
        /*
         * Atmel, SST, Intel/Numonyx, and others serial NOR tend to power up
         * with the software protection bits set.
@@ -4176,10 +4889,8 @@ int spi_nor_scan(struct spi_nor *nor, const char *name,
            nor->info->flags & SPI_NOR_HAS_LOCK)
                nor->clear_sr_bp = spi_nor_clear_sr_bp;
 
-       /* Parse the Serial Flash Discoverable Parameters table. */
-       ret = spi_nor_init_params(nor, &params);
-       if (ret)
-               return ret;
+       /* Init flash parameters based on flash_info struct and SFDP */
+       spi_nor_init_params(nor);
 
        if (!mtd->name)
                mtd->name = dev_name(dev);
@@ -4187,21 +4898,12 @@ int spi_nor_scan(struct spi_nor *nor, const char *name,
        mtd->type = MTD_NORFLASH;
        mtd->writesize = 1;
        mtd->flags = MTD_CAP_NORFLASH;
-       mtd->size = params.size;
+       mtd->size = params->size;
        mtd->_erase = spi_nor_erase;
        mtd->_read = spi_nor_read;
        mtd->_resume = spi_nor_resume;
 
-       /* NOR protection support for STmicro/Micron chips and similar */
-       if (JEDEC_MFR(info) == SNOR_MFR_ST ||
-           JEDEC_MFR(info) == SNOR_MFR_MICRON ||
-           info->flags & SPI_NOR_HAS_LOCK) {
-               nor->flash_lock = stm_lock;
-               nor->flash_unlock = stm_unlock;
-               nor->flash_is_locked = stm_is_locked;
-       }
-
-       if (nor->flash_lock && nor->flash_unlock && nor->flash_is_locked) {
+       if (nor->params.locking_ops) {
                mtd->_lock = spi_nor_lock;
                mtd->_unlock = spi_nor_unlock;
                mtd->_is_locked = spi_nor_is_locked;
@@ -4226,68 +4928,28 @@ int spi_nor_scan(struct spi_nor *nor, const char *name,
                mtd->flags |= MTD_NO_ERASE;
 
        mtd->dev.parent = dev;
-       nor->page_size = params.page_size;
+       nor->page_size = params->page_size;
        mtd->writebufsize = nor->page_size;
 
-       if (np) {
-               /* If we were instantiated by DT, use it */
-               if (of_property_read_bool(np, "m25p,fast-read"))
-                       params.hwcaps.mask |= SNOR_HWCAPS_READ_FAST;
-               else
-                       params.hwcaps.mask &= ~SNOR_HWCAPS_READ_FAST;
-       } else {
-               /* If we weren't instantiated by DT, default to fast-read */
-               params.hwcaps.mask |= SNOR_HWCAPS_READ_FAST;
-       }
-
        if (of_property_read_bool(np, "broken-flash-reset"))
                nor->flags |= SNOR_F_BROKEN_RESET;
 
-       /* Some devices cannot do fast-read, no matter what DT tells us */
-       if (info->flags & SPI_NOR_NO_FR)
-               params.hwcaps.mask &= ~SNOR_HWCAPS_READ_FAST;
-
        /*
         * Configure the SPI memory:
         * - select op codes for (Fast) Read, Page Program and Sector Erase.
         * - set the number of dummy cycles (mode cycles + wait states).
         * - set the SPI protocols for register and memory accesses.
-        * - set the Quad Enable bit if needed (required by SPI x-y-4 protos).
         */
-       ret = spi_nor_setup(nor, &params, hwcaps);
+       ret = spi_nor_setup(nor, hwcaps);
        if (ret)
                return ret;
 
-       if (nor->addr_width) {
-               /* already configured from SFDP */
-       } else if (info->addr_width) {
-               nor->addr_width = info->addr_width;
-       } else if (mtd->size > 0x1000000) {
-               /* enable 4-byte addressing if the device exceeds 16MiB */
-               nor->addr_width = 4;
-       } else {
-               nor->addr_width = 3;
-       }
-
-       if (info->flags & SPI_NOR_4B_OPCODES ||
-           (JEDEC_MFR(info) == SNOR_MFR_SPANSION && mtd->size > SZ_16M))
+       if (info->flags & SPI_NOR_4B_OPCODES)
                nor->flags |= SNOR_F_4B_OPCODES;
 
-       if (nor->addr_width == 4 && nor->flags & SNOR_F_4B_OPCODES &&
-           !(nor->flags & SNOR_F_HAS_4BAIT))
-               spi_nor_set_4byte_opcodes(nor);
-
-       if (nor->addr_width > SPI_NOR_MAX_ADDR_WIDTH) {
-               dev_err(dev, "address width is too large: %u\n",
-                       nor->addr_width);
-               return -EINVAL;
-       }
-
-       if (info->flags & SPI_S3AN) {
-               ret = s3an_nor_scan(nor);
-               if (ret)
-                       return ret;
-       }
+       ret = spi_nor_set_addr_width(nor);
+       if (ret)
+               return ret;
 
        /* Send all the required SPI flash commands to initialize device */
        ret = spi_nor_init(nor);
@@ -4317,6 +4979,174 @@ int spi_nor_scan(struct spi_nor *nor, const char *name,
 }
 EXPORT_SYMBOL_GPL(spi_nor_scan);
 
+static int spi_nor_probe(struct spi_mem *spimem)
+{
+       struct spi_device *spi = spimem->spi;
+       struct flash_platform_data *data = dev_get_platdata(&spi->dev);
+       struct spi_nor *nor;
+       /*
+        * Enable all caps by default. The core will mask them after
+        * checking what's really supported using spi_mem_supports_op().
+        */
+       const struct spi_nor_hwcaps hwcaps = { .mask = SNOR_HWCAPS_ALL };
+       char *flash_name;
+       int ret;
+
+       nor = devm_kzalloc(&spi->dev, sizeof(*nor), GFP_KERNEL);
+       if (!nor)
+               return -ENOMEM;
+
+       nor->spimem = spimem;
+       nor->dev = &spi->dev;
+       spi_nor_set_flash_node(nor, spi->dev.of_node);
+
+       spi_mem_set_drvdata(spimem, nor);
+
+       if (data && data->name)
+               nor->mtd.name = data->name;
+
+       if (!nor->mtd.name)
+               nor->mtd.name = spi_mem_get_name(spimem);
+
+       /*
+        * For some (historical?) reason many platforms provide two different
+        * names in flash_platform_data: "name" and "type". Quite often name is
+        * set to "m25p80" and then "type" provides a real chip name.
+        * If that's the case, respect "type" and ignore a "name".
+        */
+       if (data && data->type)
+               flash_name = data->type;
+       else if (!strcmp(spi->modalias, "spi-nor"))
+               flash_name = NULL; /* auto-detect */
+       else
+               flash_name = spi->modalias;
+
+       ret = spi_nor_scan(nor, flash_name, &hwcaps);
+       if (ret)
+               return ret;
+
+       /*
+        * None of the existing parts have > 512B pages, but let's play safe
+        * and add this logic so that if anyone ever adds support for such
+        * a NOR we don't end up with buffer overflows.
+        */
+       if (nor->page_size > PAGE_SIZE) {
+               nor->bouncebuf_size = nor->page_size;
+               devm_kfree(nor->dev, nor->bouncebuf);
+               nor->bouncebuf = devm_kmalloc(nor->dev,
+                                             nor->bouncebuf_size,
+                                             GFP_KERNEL);
+               if (!nor->bouncebuf)
+                       return -ENOMEM;
+       }
+
+       return mtd_device_register(&nor->mtd, data ? data->parts : NULL,
+                                  data ? data->nr_parts : 0);
+}
+
+static int spi_nor_remove(struct spi_mem *spimem)
+{
+       struct spi_nor *nor = spi_mem_get_drvdata(spimem);
+
+       spi_nor_restore(nor);
+
+       /* Clean up MTD stuff. */
+       return mtd_device_unregister(&nor->mtd);
+}
+
+static void spi_nor_shutdown(struct spi_mem *spimem)
+{
+       struct spi_nor *nor = spi_mem_get_drvdata(spimem);
+
+       spi_nor_restore(nor);
+}
+
+/*
+ * Do NOT add to this array without reading the following:
+ *
+ * Historically, many flash devices are bound to this driver by their name. But
+ * since most of these flash are compatible to some extent, and their
+ * differences can often be differentiated by the JEDEC read-ID command, we
+ * encourage new users to add support to the spi-nor library, and simply bind
+ * against a generic string here (e.g., "jedec,spi-nor").
+ *
+ * Many flash names are kept here in this list (as well as in spi-nor.c) to
+ * keep them available as module aliases for existing platforms.
+ */
+static const struct spi_device_id spi_nor_dev_ids[] = {
+       /*
+        * Allow non-DT platform devices to bind to the "spi-nor" modalias, and
+        * hack around the fact that the SPI core does not provide uevent
+        * matching for .of_match_table
+        */
+       {"spi-nor"},
+
+       /*
+        * Entries not used in DTs that should be safe to drop after replacing
+        * them with "spi-nor" in platform data.
+        */
+       {"s25sl064a"},  {"w25x16"},     {"m25p10"},     {"m25px64"},
+
+       /*
+        * Entries that were used in DTs without "jedec,spi-nor" fallback and
+        * should be kept for backward compatibility.
+        */
+       {"at25df321a"}, {"at25df641"},  {"at26df081a"},
+       {"mx25l4005a"}, {"mx25l1606e"}, {"mx25l6405d"}, {"mx25l12805d"},
+       {"mx25l25635e"},{"mx66l51235l"},
+       {"n25q064"},    {"n25q128a11"}, {"n25q128a13"}, {"n25q512a"},
+       {"s25fl256s1"}, {"s25fl512s"},  {"s25sl12801"}, {"s25fl008k"},
+       {"s25fl064k"},
+       {"sst25vf040b"},{"sst25vf016b"},{"sst25vf032b"},{"sst25wf040"},
+       {"m25p40"},     {"m25p80"},     {"m25p16"},     {"m25p32"},
+       {"m25p64"},     {"m25p128"},
+       {"w25x80"},     {"w25x32"},     {"w25q32"},     {"w25q32dw"},
+       {"w25q80bl"},   {"w25q128"},    {"w25q256"},
+
+       /* Flashes that can't be detected using JEDEC */
+       {"m25p05-nonjedec"},    {"m25p10-nonjedec"},    {"m25p20-nonjedec"},
+       {"m25p40-nonjedec"},    {"m25p80-nonjedec"},    {"m25p16-nonjedec"},
+       {"m25p32-nonjedec"},    {"m25p64-nonjedec"},    {"m25p128-nonjedec"},
+
+       /* Everspin MRAMs (non-JEDEC) */
+       { "mr25h128" }, /* 128 Kib, 40 MHz */
+       { "mr25h256" }, /* 256 Kib, 40 MHz */
+       { "mr25h10" },  /*   1 Mib, 40 MHz */
+       { "mr25h40" },  /*   4 Mib, 40 MHz */
+
+       { },
+};
+MODULE_DEVICE_TABLE(spi, spi_nor_dev_ids);
+
+static const struct of_device_id spi_nor_of_table[] = {
+       /*
+        * Generic compatibility for SPI NOR that can be identified by the
+        * JEDEC READ ID opcode (0x9F). Use this, if possible.
+        */
+       { .compatible = "jedec,spi-nor" },
+       { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, spi_nor_of_table);
+
+/*
+ * REVISIT: many of these chips have deep power-down modes, which
+ * should clearly be entered on suspend() to minimize power use.
+ * And also when they're otherwise idle...
+ */
+static struct spi_mem_driver spi_nor_driver = {
+       .spidrv = {
+               .driver = {
+                       .name = "spi-nor",
+                       .of_match_table = spi_nor_of_table,
+               },
+               .id_table = spi_nor_dev_ids,
+       },
+       .probe = spi_nor_probe,
+       .remove = spi_nor_remove,
+       .shutdown = spi_nor_shutdown,
+};
+module_spi_mem_driver(spi_nor_driver);
+
 MODULE_LICENSE("GPL v2");
 MODULE_AUTHOR("Huang Shijie <shijie8@gmail.com>");
 MODULE_AUTHOR("Mike Lavender");
index 4ca8c1c..249e8d9 100644 (file)
@@ -189,6 +189,9 @@ struct module;      /* only needed for owner field in mtd_info */
  */
 struct mtd_debug_info {
        struct dentry *dfs_dir;
+
+       const char *partname;
+       const char *partid;
 };
 
 struct mtd_info {
index 9f57cdf..fc0b4b1 100644 (file)
@@ -9,6 +9,7 @@
 #include <linux/bitops.h>
 #include <linux/mtd/cfi.h>
 #include <linux/mtd/mtd.h>
+#include <linux/spi/spi-mem.h>
 
 /*
  * Manufacturer IDs
@@ -224,7 +225,6 @@ static inline u8 spi_nor_get_protocol_width(enum spi_nor_protocol proto)
        return spi_nor_get_protocol_data_nbits(proto);
 }
 
-#define SPI_NOR_MAX_CMD_SIZE   8
 enum spi_nor_ops {
        SPI_NOR_OPS_READ = 0,
        SPI_NOR_OPS_WRITE,
@@ -237,12 +237,12 @@ enum spi_nor_option_flags {
        SNOR_F_USE_FSR          = BIT(0),
        SNOR_F_HAS_SR_TB        = BIT(1),
        SNOR_F_NO_OP_CHIP_ERASE = BIT(2),
-       SNOR_F_S3AN_ADDR_DEFAULT = BIT(3),
-       SNOR_F_READY_XSR_RDY    = BIT(4),
-       SNOR_F_USE_CLSR         = BIT(5),
-       SNOR_F_BROKEN_RESET     = BIT(6),
-       SNOR_F_4B_OPCODES       = BIT(7),
-       SNOR_F_HAS_4BAIT        = BIT(8),
+       SNOR_F_READY_XSR_RDY    = BIT(3),
+       SNOR_F_USE_CLSR         = BIT(4),
+       SNOR_F_BROKEN_RESET     = BIT(5),
+       SNOR_F_4B_OPCODES       = BIT(6),
+       SNOR_F_HAS_4BAIT        = BIT(7),
+       SNOR_F_HAS_LOCK         = BIT(8),
 };
 
 /**
@@ -333,6 +333,195 @@ struct spi_nor_erase_map {
        u8                              uniform_erase_type;
 };
 
+/**
+ * struct spi_nor_hwcaps - Structure for describing the hardware capabilies
+ * supported by the SPI controller (bus master).
+ * @mask:              the bitmask listing all the supported hw capabilies
+ */
+struct spi_nor_hwcaps {
+       u32     mask;
+};
+
+/*
+ *(Fast) Read capabilities.
+ * MUST be ordered by priority: the higher bit position, the higher priority.
+ * As a matter of performances, it is relevant to use Octal SPI protocols first,
+ * then Quad SPI protocols before Dual SPI protocols, Fast Read and lastly
+ * (Slow) Read.
+ */
+#define SNOR_HWCAPS_READ_MASK          GENMASK(14, 0)
+#define SNOR_HWCAPS_READ               BIT(0)
+#define SNOR_HWCAPS_READ_FAST          BIT(1)
+#define SNOR_HWCAPS_READ_1_1_1_DTR     BIT(2)
+
+#define SNOR_HWCAPS_READ_DUAL          GENMASK(6, 3)
+#define SNOR_HWCAPS_READ_1_1_2         BIT(3)
+#define SNOR_HWCAPS_READ_1_2_2         BIT(4)
+#define SNOR_HWCAPS_READ_2_2_2         BIT(5)
+#define SNOR_HWCAPS_READ_1_2_2_DTR     BIT(6)
+
+#define SNOR_HWCAPS_READ_QUAD          GENMASK(10, 7)
+#define SNOR_HWCAPS_READ_1_1_4         BIT(7)
+#define SNOR_HWCAPS_READ_1_4_4         BIT(8)
+#define SNOR_HWCAPS_READ_4_4_4         BIT(9)
+#define SNOR_HWCAPS_READ_1_4_4_DTR     BIT(10)
+
+#define SNOR_HWCAPS_READ_OCTAL         GENMASK(14, 11)
+#define SNOR_HWCAPS_READ_1_1_8         BIT(11)
+#define SNOR_HWCAPS_READ_1_8_8         BIT(12)
+#define SNOR_HWCAPS_READ_8_8_8         BIT(13)
+#define SNOR_HWCAPS_READ_1_8_8_DTR     BIT(14)
+
+/*
+ * Page Program capabilities.
+ * MUST be ordered by priority: the higher bit position, the higher priority.
+ * Like (Fast) Read capabilities, Octal/Quad SPI protocols are preferred to the
+ * legacy SPI 1-1-1 protocol.
+ * Note that Dual Page Programs are not supported because there is no existing
+ * JEDEC/SFDP standard to define them. Also at this moment no SPI flash memory
+ * implements such commands.
+ */
+#define SNOR_HWCAPS_PP_MASK    GENMASK(22, 16)
+#define SNOR_HWCAPS_PP         BIT(16)
+
+#define SNOR_HWCAPS_PP_QUAD    GENMASK(19, 17)
+#define SNOR_HWCAPS_PP_1_1_4   BIT(17)
+#define SNOR_HWCAPS_PP_1_4_4   BIT(18)
+#define SNOR_HWCAPS_PP_4_4_4   BIT(19)
+
+#define SNOR_HWCAPS_PP_OCTAL   GENMASK(22, 20)
+#define SNOR_HWCAPS_PP_1_1_8   BIT(20)
+#define SNOR_HWCAPS_PP_1_8_8   BIT(21)
+#define SNOR_HWCAPS_PP_8_8_8   BIT(22)
+
+#define SNOR_HWCAPS_X_X_X      (SNOR_HWCAPS_READ_2_2_2 |       \
+                                SNOR_HWCAPS_READ_4_4_4 |       \
+                                SNOR_HWCAPS_READ_8_8_8 |       \
+                                SNOR_HWCAPS_PP_4_4_4 |         \
+                                SNOR_HWCAPS_PP_8_8_8)
+
+#define SNOR_HWCAPS_DTR                (SNOR_HWCAPS_READ_1_1_1_DTR |   \
+                                SNOR_HWCAPS_READ_1_2_2_DTR |   \
+                                SNOR_HWCAPS_READ_1_4_4_DTR |   \
+                                SNOR_HWCAPS_READ_1_8_8_DTR)
+
+#define SNOR_HWCAPS_ALL                (SNOR_HWCAPS_READ_MASK |        \
+                                SNOR_HWCAPS_PP_MASK)
+
+struct spi_nor_read_command {
+       u8                      num_mode_clocks;
+       u8                      num_wait_states;
+       u8                      opcode;
+       enum spi_nor_protocol   proto;
+};
+
+struct spi_nor_pp_command {
+       u8                      opcode;
+       enum spi_nor_protocol   proto;
+};
+
+enum spi_nor_read_command_index {
+       SNOR_CMD_READ,
+       SNOR_CMD_READ_FAST,
+       SNOR_CMD_READ_1_1_1_DTR,
+
+       /* Dual SPI */
+       SNOR_CMD_READ_1_1_2,
+       SNOR_CMD_READ_1_2_2,
+       SNOR_CMD_READ_2_2_2,
+       SNOR_CMD_READ_1_2_2_DTR,
+
+       /* Quad SPI */
+       SNOR_CMD_READ_1_1_4,
+       SNOR_CMD_READ_1_4_4,
+       SNOR_CMD_READ_4_4_4,
+       SNOR_CMD_READ_1_4_4_DTR,
+
+       /* Octal SPI */
+       SNOR_CMD_READ_1_1_8,
+       SNOR_CMD_READ_1_8_8,
+       SNOR_CMD_READ_8_8_8,
+       SNOR_CMD_READ_1_8_8_DTR,
+
+       SNOR_CMD_READ_MAX
+};
+
+enum spi_nor_pp_command_index {
+       SNOR_CMD_PP,
+
+       /* Quad SPI */
+       SNOR_CMD_PP_1_1_4,
+       SNOR_CMD_PP_1_4_4,
+       SNOR_CMD_PP_4_4_4,
+
+       /* Octal SPI */
+       SNOR_CMD_PP_1_1_8,
+       SNOR_CMD_PP_1_8_8,
+       SNOR_CMD_PP_8_8_8,
+
+       SNOR_CMD_PP_MAX
+};
+
+/* Forward declaration that will be used in 'struct spi_nor_flash_parameter' */
+struct spi_nor;
+
+/**
+ * struct spi_nor_locking_ops - SPI NOR locking methods
+ * @lock:      lock a region of the SPI NOR.
+ * @unlock:    unlock a region of the SPI NOR.
+ * @is_locked: check if a region of the SPI NOR is completely locked
+ */
+struct spi_nor_locking_ops {
+       int (*lock)(struct spi_nor *nor, loff_t ofs, uint64_t len);
+       int (*unlock)(struct spi_nor *nor, loff_t ofs, uint64_t len);
+       int (*is_locked)(struct spi_nor *nor, loff_t ofs, uint64_t len);
+};
+
+/**
+ * struct spi_nor_flash_parameter - SPI NOR flash parameters and settings.
+ * Includes legacy flash parameters and settings that can be overwritten
+ * by the spi_nor_fixups hooks, or dynamically when parsing the JESD216
+ * Serial Flash Discoverable Parameters (SFDP) tables.
+ *
+ * @size:              the flash memory density in bytes.
+ * @page_size:         the page size of the SPI NOR flash memory.
+ * @hwcaps:            describes the read and page program hardware
+ *                     capabilities.
+ * @reads:             read capabilities ordered by priority: the higher index
+ *                      in the array, the higher priority.
+ * @page_programs:     page program capabilities ordered by priority: the
+ *                      higher index in the array, the higher priority.
+ * @erase_map:         the erase map parsed from the SFDP Sector Map Parameter
+ *                      Table.
+ * @quad_enable:       enables SPI NOR quad mode.
+ * @set_4byte:         puts the SPI NOR in 4 byte addressing mode.
+ * @convert_addr:      converts an absolute address into something the flash
+ *                      will understand. Particularly useful when pagesize is
+ *                      not a power-of-2.
+ * @setup:              configures the SPI NOR memory. Useful for SPI NOR
+ *                      flashes that have peculiarities to the SPI NOR standard
+ *                      e.g. different opcodes, specific address calculation,
+ *                      page size, etc.
+ * @locking_ops:       SPI NOR locking methods.
+ */
+struct spi_nor_flash_parameter {
+       u64                             size;
+       u32                             page_size;
+
+       struct spi_nor_hwcaps           hwcaps;
+       struct spi_nor_read_command     reads[SNOR_CMD_READ_MAX];
+       struct spi_nor_pp_command       page_programs[SNOR_CMD_PP_MAX];
+
+       struct spi_nor_erase_map        erase_map;
+
+       int (*quad_enable)(struct spi_nor *nor);
+       int (*set_4byte)(struct spi_nor *nor, bool enable);
+       u32 (*convert_addr)(struct spi_nor *nor, u32 addr);
+       int (*setup)(struct spi_nor *nor, const struct spi_nor_hwcaps *hwcaps);
+
+       const struct spi_nor_locking_ops *locking_ops;
+};
+
 /**
  * struct flash_info - Forward declaration of a structure used internally by
  *                    spi_nor_scan()
@@ -344,6 +533,10 @@ struct flash_info;
  * @mtd:               point to a mtd_info structure
  * @lock:              the lock for the read/write/erase/lock/unlock operations
  * @dev:               point to a spi device, or a spi nor controller device.
+ * @spimem:            point to the spi mem device
+ * @bouncebuf:         bounce buffer used when the buffer passed by the MTD
+ *                      layer is not DMA-able
+ * @bouncebuf_size:    size of the bounce buffer
  * @info:              spi-nor part JDEC MFR id and other info
  * @page_size:         the page size of the SPI NOR
  * @addr_width:                number of address bytes
@@ -356,8 +549,6 @@ struct flash_info;
  * @read_proto:                the SPI protocol for read operations
  * @write_proto:       the SPI protocol for write operations
  * @reg_proto          the SPI protocol for read_reg/write_reg/erase operations
- * @cmd_buf:           used by the write_reg
- * @erase_map:         the erase map of the SPI NOR
  * @prepare:           [OPTIONAL] do some preparations for the
  *                     read/write/erase/lock/unlock operations
  * @unprepare:         [OPTIONAL] do some post work after the
@@ -369,19 +560,21 @@ struct flash_info;
  * @erase:             [DRIVER-SPECIFIC] erase a sector of the SPI NOR
  *                     at the offset @offs; if not provided by the driver,
  *                     spi-nor will send the erase opcode via write_reg()
- * @flash_lock:                [FLASH-SPECIFIC] lock a region of the SPI NOR
- * @flash_unlock:      [FLASH-SPECIFIC] unlock a region of the SPI NOR
- * @flash_is_locked:   [FLASH-SPECIFIC] check if a region of the SPI NOR is
- * @quad_enable:       [FLASH-SPECIFIC] enables SPI NOR quad mode
  * @clear_sr_bp:       [FLASH-SPECIFIC] clears the Block Protection Bits from
  *                     the SPI NOR Status Register.
- *                     completely locked
+ * @params:            [FLASH-SPECIFIC] SPI-NOR flash parameters and settings.
+ *                      The structure includes legacy flash parameters and
+ *                      settings that can be overwritten by the spi_nor_fixups
+ *                      hooks, or dynamically when parsing the SFDP tables.
  * @priv:              the private data
  */
 struct spi_nor {
        struct mtd_info         mtd;
        struct mutex            lock;
        struct device           *dev;
+       struct spi_mem          *spimem;
+       u8                      *bouncebuf;
+       size_t                  bouncebuf_size;
        const struct flash_info *info;
        u32                     page_size;
        u8                      addr_width;
@@ -394,8 +587,6 @@ struct spi_nor {
        enum spi_nor_protocol   reg_proto;
        bool                    sst_write_second;
        u32                     flags;
-       u8                      cmd_buf[SPI_NOR_MAX_CMD_SIZE];
-       struct spi_nor_erase_map        erase_map;
 
        int (*prepare)(struct spi_nor *nor, enum spi_nor_ops ops);
        void (*unprepare)(struct spi_nor *nor, enum spi_nor_ops ops);
@@ -408,11 +599,8 @@ struct spi_nor {
                        size_t len, const u_char *write_buf);
        int (*erase)(struct spi_nor *nor, loff_t offs);
 
-       int (*flash_lock)(struct spi_nor *nor, loff_t ofs, uint64_t len);
-       int (*flash_unlock)(struct spi_nor *nor, loff_t ofs, uint64_t len);
-       int (*flash_is_locked)(struct spi_nor *nor, loff_t ofs, uint64_t len);
-       int (*quad_enable)(struct spi_nor *nor);
        int (*clear_sr_bp)(struct spi_nor *nor);
+       struct spi_nor_flash_parameter params;
 
        void *priv;
 };
@@ -443,7 +631,7 @@ spi_nor_region_mark_overlay(struct spi_nor_erase_region *region)
 
 static bool __maybe_unused spi_nor_has_uniform_erase(const struct spi_nor *nor)
 {
-       return !!nor->erase_map.uniform_erase_type;
+       return !!nor->params.erase_map.uniform_erase_type;
 }
 
 static inline void spi_nor_set_flash_node(struct spi_nor *nor,
@@ -457,67 +645,6 @@ static inline struct device_node *spi_nor_get_flash_node(struct spi_nor *nor)
        return mtd_get_of_node(&nor->mtd);
 }
 
-/**
- * struct spi_nor_hwcaps - Structure for describing the hardware capabilies
- * supported by the SPI controller (bus master).
- * @mask:              the bitmask listing all the supported hw capabilies
- */
-struct spi_nor_hwcaps {
-       u32     mask;
-};
-
-/*
- *(Fast) Read capabilities.
- * MUST be ordered by priority: the higher bit position, the higher priority.
- * As a matter of performances, it is relevant to use Octal SPI protocols first,
- * then Quad SPI protocols before Dual SPI protocols, Fast Read and lastly
- * (Slow) Read.
- */
-#define SNOR_HWCAPS_READ_MASK          GENMASK(14, 0)
-#define SNOR_HWCAPS_READ               BIT(0)
-#define SNOR_HWCAPS_READ_FAST          BIT(1)
-#define SNOR_HWCAPS_READ_1_1_1_DTR     BIT(2)
-
-#define SNOR_HWCAPS_READ_DUAL          GENMASK(6, 3)
-#define SNOR_HWCAPS_READ_1_1_2         BIT(3)
-#define SNOR_HWCAPS_READ_1_2_2         BIT(4)
-#define SNOR_HWCAPS_READ_2_2_2         BIT(5)
-#define SNOR_HWCAPS_READ_1_2_2_DTR     BIT(6)
-
-#define SNOR_HWCAPS_READ_QUAD          GENMASK(10, 7)
-#define SNOR_HWCAPS_READ_1_1_4         BIT(7)
-#define SNOR_HWCAPS_READ_1_4_4         BIT(8)
-#define SNOR_HWCAPS_READ_4_4_4         BIT(9)
-#define SNOR_HWCAPS_READ_1_4_4_DTR     BIT(10)
-
-#define SNOR_HWCAPS_READ_OCTAL         GENMASK(14, 11)
-#define SNOR_HWCAPS_READ_1_1_8         BIT(11)
-#define SNOR_HWCAPS_READ_1_8_8         BIT(12)
-#define SNOR_HWCAPS_READ_8_8_8         BIT(13)
-#define SNOR_HWCAPS_READ_1_8_8_DTR     BIT(14)
-
-/*
- * Page Program capabilities.
- * MUST be ordered by priority: the higher bit position, the higher priority.
- * Like (Fast) Read capabilities, Octal/Quad SPI protocols are preferred to the
- * legacy SPI 1-1-1 protocol.
- * Note that Dual Page Programs are not supported because there is no existing
- * JEDEC/SFDP standard to define them. Also at this moment no SPI flash memory
- * implements such commands.
- */
-#define SNOR_HWCAPS_PP_MASK    GENMASK(22, 16)
-#define SNOR_HWCAPS_PP         BIT(16)
-
-#define SNOR_HWCAPS_PP_QUAD    GENMASK(19, 17)
-#define SNOR_HWCAPS_PP_1_1_4   BIT(17)
-#define SNOR_HWCAPS_PP_1_4_4   BIT(18)
-#define SNOR_HWCAPS_PP_4_4_4   BIT(19)
-
-#define SNOR_HWCAPS_PP_OCTAL   GENMASK(22, 20)
-#define SNOR_HWCAPS_PP_1_1_8   BIT(20)
-#define SNOR_HWCAPS_PP_1_8_8   BIT(21)
-#define SNOR_HWCAPS_PP_8_8_8   BIT(22)
-
 /**
  * spi_nor_scan() - scan the SPI NOR
  * @nor:       the spi_nor structure