+++ /dev/null
-// 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");
 
 
 #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 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.
 {
        int ret;
 
-       ret = nor->read_reg(nor, SPINOR_OP_RDSR, nor->bouncebuf, 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;
 {
        int ret;
 
-       ret = nor->read_reg(nor, SPINOR_OP_RDFSR, nor->bouncebuf, 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;
 {
        int ret;
 
-       ret = nor->read_reg(nor, SPINOR_OP_RDCR, nor->bouncebuf, 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;
 static int write_sr(struct spi_nor *nor, u8 val)
 {
        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);
 }
 
  */
 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);
 }
 
  */
 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);
 }
 
        }
 }
 
+static int macronix_set_4byte(struct spi_nor *nor, bool enable)
+{
+       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);
+       }
+
+       return nor->write_reg(nor, enable ? SPINOR_OP_EN4B : SPINOR_OP_EX4B,
+                             NULL, 0);
+}
+
+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);
+}
+
 /* Enable/disable 4-byte addressing mode. */
 static int set_4byte(struct spi_nor *nor, bool enable)
 {
        int status;
        bool need_wren = false;
-       u8 cmd;
 
        switch (JEDEC_MFR(nor->info)) {
        case SNOR_MFR_ST:
                if (need_wren)
                        write_enable(nor);
 
-               cmd = enable ? SPINOR_OP_EN4B : SPINOR_OP_EX4B;
-               status = nor->write_reg(nor, cmd, NULL, 0);
+               status = macronix_set_4byte(nor, enable);
                if (need_wren)
                        write_disable(nor);
 
                         * We must clear the register to enable normal behavior.
                         */
                        write_enable(nor);
-                       nor->bouncebuf[0] = 0;
-                       nor->write_reg(nor, SPINOR_OP_WREAR,
-                                      nor->bouncebuf, 1);
+                       spi_nor_write_ear(nor, 0);
                        write_disable(nor);
                }
 
                return status;
        default:
                /* Spansion style */
-               nor->bouncebuf[0] = enable << 7;
-               return nor->write_reg(nor, SPINOR_OP_BRWR, nor->bouncebuf, 1);
+               return spansion_set_4byte(nor, enable);
        }
 }
 
+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;
 
-       ret = nor->read_reg(nor, SPINOR_OP_XRDSR, nor->bouncebuf, 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 !!(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)
 {
        int sr = read_sr(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);
                        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;
        }
 
 {
        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);
 }
 
        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
 
        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");
        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'
        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)
 
        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;
        }
 
        /* Read back and check it. */
-       ret = nor->read_reg(nor, SPINOR_OP_RDSR2, sr2, 1);
+       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;
        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);
                if (nor->flags & SNOR_F_S3AN_ADDR_DEFAULT)
                        addr = spi_nor_s3an_addr_convert(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;
                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",
                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",
                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",
                        addr = spi_nor_s3an_addr_convert(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;
 
 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;
        }
 {
        int ret;
 
-       ret = nor->read_reg(nor, SPINOR_OP_XRDSR, nor->bouncebuf, 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;
        int ret;
 
        while (len) {
-               ret = nor->read(nor, addr, len, buf);
+               ret = spi_nor_read_data(nor, addr, len, buf);
                if (!ret || ret > len)
                        return -EIO;
                if (ret < 0)
        /*
         * 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,
 }
 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;
+       struct spi_nor_hwcaps hwcaps = {
+               .mask = SNOR_HWCAPS_READ |
+                       SNOR_HWCAPS_READ_FAST |
+                       SNOR_HWCAPS_PP,
+       };
+       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 (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;
+
+       /*
+        * 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");