Some of the SPI device drivers at drivers/spi not a real
spi controllers, Unlike normal/generic SPI controllers they
operates only with SPI-NOR flash devices. these were technically
termed as SPI-NOR controllers, Ex: drivers/spi/fsl_qspi.c

The problem with these were resides at drivers/spi is entire
SPI layer becomes SPI-NOR flash oriented which is absolutely
a wrong indication where SPI layer getting effected more with
flash operations - So this SPI-NOR core will resolve this issue
by separating all SPI-NOR flash operations from spi layer and
creats a generic layer called SPI-NOR core which can be used to
interact SPI-NOR to SPI driver interface layer and the SPI-NOR
controller driver. The idea is taken from Linux spi-nor framework.

Before SPI-NOR:

        -----------------------
                cmd_sf.c
        -----------------------
                spi_flash.c
        -----------------------
                sf_probe.c
        -----------------------
                spi-uclass
        -----------------------
                spi drivers
        -----------------------
                SPI NOR chip
        -----------------------

After SPI-NOR:

        ------------------------------
                cmd_sf.c
        ------------------------------
                spi-nor.c
        -------------------------------
        m25p80.c        spi nor drivers
        -------------------------------
        spi-uclass      SPI NOR chip
        -------------------------------
        spi drivers
        -------------------------------
        SPI NOR chip
        -------------------------------

Cc: Simon Glass <s...@chromium.org>
Cc: Bin Meng <bmeng...@gmail.com>
Cc: York Sun <york....@nxp.com>
Cc: Mugunthan V N <mugunthan...@ti.com>
Cc: Michal Simek <michal.si...@xilinx.com>
Cc: Siva Durga Prasad Paladugu <siva...@xilinx.com>
Signed-off-by: Jagan Teki <jt...@openedev.com>
---
 doc/mtd/spi-nor.txt               |   81 +++
 drivers/mtd/Kconfig               |    2 +
 drivers/mtd/spi-nor/Makefile      |    5 +
 drivers/mtd/spi-nor/spi-nor-ids.c |  276 ++++++++++
 drivers/mtd/spi-nor/spi-nor.c     | 1084 +++++++++++++++++++++++++++++++++++++
 include/linux/err.h               |    5 +
 include/linux/mtd/spi-nor.h       |  253 +++++++++
 7 files changed, 1706 insertions(+)
 create mode 100644 doc/mtd/spi-nor.txt
 create mode 100644 drivers/mtd/spi-nor/spi-nor-ids.c
 create mode 100644 drivers/mtd/spi-nor/spi-nor.c
 create mode 100644 include/linux/mtd/spi-nor.h

diff --git a/doc/mtd/spi-nor.txt b/doc/mtd/spi-nor.txt
new file mode 100644
index 0000000..8b381c1
--- /dev/null
+++ b/doc/mtd/spi-nor.txt
@@ -0,0 +1,81 @@
+                          SPI NOR framework
+               ============================================
+
+Part I - Why do we need this framework?
+---------------------------------------
+
+SPI bus controllers (drivers/spi/) only deal with streams of bytes; the bus
+controller operates agnostic of the specific device attached. However, some
+controllers (such as Freescale's QuadSPI controller) cannot easily handle
+arbitrary streams of bytes, but rather are designed specifically for SPI NOR.
+
+In particular, Freescale's QuadSPI controller must know the NOR commands to
+find the right LUT sequence. Unfortunately, the SPI subsystem has no notion of
+opcodes, addresses, or data payloads; a SPI controller simply knows to send or
+receive bytes (Tx and Rx). Therefore, we must define a new layering scheme 
under
+which the controller driver is aware of the opcodes, addressing, and other
+details of the SPI NOR protocol.
+
+Part II - How does the framework work?
+--------------------------------------
+
+This framework just adds a new layer between the MTD and the SPI bus driver.
+With this new layer, the SPI NOR controller driver does not depend on the
+m25p80 code anymore.
+
+Before SPI-NOR:
+
+       -----------------------
+               cmd_sf.c
+       -----------------------
+               spi_flash.c
+       -----------------------
+               sf_probe.c
+       -----------------------
+               spi-uclass
+       -----------------------
+               spi drivers
+       -----------------------
+               SPI NOR chip
+       -----------------------
+
+After SPI-NOR:
+
+       ------------------------------
+               cmd_sf.c
+       ------------------------------
+               spi-nor.c
+       -------------------------------
+       m25p80.c        spi nor drivers
+       -------------------------------
+       spi-uclass      SPI NOR chip
+       -------------------------------
+       spi drivers
+       -------------------------------
+       SPI NOR chip
+       -------------------------------
+
+SPI-NOR with MTD:
+
+       ------------------------------
+               cmd_sf.c
+       ------------------------------
+               MTD core
+       ------------------------------
+               spi-nor.c
+       -------------------------------
+       m25p80.c        spi nor drivers
+       -------------------------------
+       spi-uclass      SPI NOR chip
+       -------------------------------
+       spi bus drivers
+       -------------------------------
+       SPI NOR chip
+       -------------------------------
+
+Part III - How can drivers use the framework?
+---------------------------------------------
+
+The main API is spi_nor_scan(). Before you call the hook, a driver should
+initialize the necessary fields for spi_nor{}. Please see
+drivers/mtd/spi-nor/spi-nor.c for detail.
diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig
index c58841e..2c8846b 100644
--- a/drivers/mtd/Kconfig
+++ b/drivers/mtd/Kconfig
@@ -33,3 +33,5 @@ endmenu
 source "drivers/mtd/nand/Kconfig"
 
 source "drivers/mtd/spi/Kconfig"
+
+source "drivers/mtd/spi-nor/Kconfig"
diff --git a/drivers/mtd/spi-nor/Makefile b/drivers/mtd/spi-nor/Makefile
index a4c19e3..9ab6e3d 100644
--- a/drivers/mtd/spi-nor/Makefile
+++ b/drivers/mtd/spi-nor/Makefile
@@ -3,4 +3,9 @@
 #
 # SPDX-License-Identifier:     GPL-2.0+
 
+ifdef CONFIG_MTD_SPI_NOR
+obj-y += spi-nor.o
+obj-y += spi-nor-ids.o
+endif
+
 obj-$(CONFIG_MTD_M25P80)       += m25p80.o
diff --git a/drivers/mtd/spi-nor/spi-nor-ids.c 
b/drivers/mtd/spi-nor/spi-nor-ids.c
new file mode 100644
index 0000000..2599731
--- /dev/null
+++ b/drivers/mtd/spi-nor/spi-nor-ids.c
@@ -0,0 +1,276 @@
+/*
+ * SPI NOR ID's.
+ * Cloned most of the code from the sf_params.c and Linux spi-nor framework.
+ *
+ * Copyright (C) 2016 Jagan Teki <jt...@openedev.com>
+ *
+ * SPDX-License-Identifier:    GPL-2.0+
+ */
+
+#include <linux/mtd/spi-nor.h>
+
+/* Used when the "_ext_id" is two bytes at most */
+#define INFO(_jedec_id, _ext_id, _sector_size, _n_sectors, _flash_read, 
_flags)        \
+               .id = {                                                 \
+                       ((_jedec_id) >> 16) & 0xff,                     \
+                       ((_jedec_id) >> 8) & 0xff,                      \
+                       (_jedec_id) & 0xff,                             \
+                       ((_ext_id) >> 8) & 0xff,                        \
+                       (_ext_id) & 0xff,                               \
+                       },                                              \
+               .id_len = (!(_jedec_id) ? 0 : (3 + ((_ext_id) ? 2 : 0))),       
\
+               .sector_size = (_sector_size),                          \
+               .n_sectors = (_n_sectors),                              \
+               .page_size = 256,                                       \
+               .flash_read = _flash_read,                                      
\
+               .flags = (_flags),
+
+#define INFO6(_jedec_id, _ext_id, _sector_size, _n_sectors, _flash_read, 
_flags)       \
+               .id = {                                                 \
+                       ((_jedec_id) >> 16) & 0xff,                     \
+                       ((_jedec_id) >> 8) & 0xff,                      \
+                       (_jedec_id) & 0xff,                             \
+                       ((_ext_id) >> 16) & 0xff,                       \
+                       ((_ext_id) >> 8) & 0xff,                        \
+                       (_ext_id) & 0xff,                               \
+                       },                                              \
+               .id_len = 6,                                            \
+               .sector_size = (_sector_size),                          \
+               .n_sectors = (_n_sectors),                              \
+               .page_size = 256,                                       \
+               .flash_read = _flash_read,                                      
\
+               .flags = (_flags),
+
+#define CAT25_INFO(_sector_size, _n_sectors, _page_size, _addr_width, 
_flash_read, _flags)     \
+               .sector_size = (_sector_size),                          \
+               .n_sectors = (_n_sectors),                              \
+               .page_size = (_page_size),                              \
+               .addr_width = (_addr_width),                            \
+               .flash_read = _flash_read,                                      
\
+               .flags = (_flags),
+
+/* 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.
+ *
+ * All newly added entries should describe *hardware* and should use SECT_4K
+ * (or SECT_4K_PMC) if hardware supports erasing 4 KiB sectors. For usage
+ * scenarios excluding small sectors there is config option that can be
+ * disabled: CONFIG_MTD_SPI_NOR_USE_4K_SECTORS.
+ * For historical (and compatibility) reasons (before we got above config) some
+ * old entries may be missing 4K flag.
+ */
+const struct spi_nor_info spi_nor_ids[] = {
+#ifdef CONFIG_SPI_FLASH_ATMEL          /* ATMEL */
+       /* Atmel -- some are (confusingly) marketed as "DataFlash" */
+       { "at25fs010",  INFO(0x1f6601, 0, 32 * 1024,   4, SNOR_READ_BASE, 
SECT_4K) },
+       { "at25fs040",  INFO(0x1f6604, 0, 64 * 1024,   8, SNOR_READ_BASE, 
SECT_4K) },
+
+       { "at25df041a", INFO(0x1f4401, 0, 64 * 1024,   8, SNOR_READ_BASE, 
SECT_4K) },
+       { "at25df321a", INFO(0x1f4701, 0, 64 * 1024,  64, SNOR_READ_BASE, 
SECT_4K) },
+       { "at25df641",  INFO(0x1f4800, 0, 64 * 1024, 128, SNOR_READ_BASE, 
SECT_4K) },
+
+       { "at26f004",   INFO(0x1f0400, 0, 64 * 1024,  8, SNOR_READ_BASE, 
SECT_4K) },
+       { "at26df081a", INFO(0x1f4501, 0, 64 * 1024, 16, SNOR_READ_BASE, 
SECT_4K) },
+       { "at26df161a", INFO(0x1f4601, 0, 64 * 1024, 32, SNOR_READ_BASE, 
SECT_4K) },
+       { "at26df321",  INFO(0x1f4700, 0, 64 * 1024, 64, SNOR_READ_BASE, 
SECT_4K) },
+
+       { "at45db011d", INFO(0x1f2200, 0, 64 * 1024,   4, SNOR_READ_BASE, 
SECT_4K) },
+       { "at45db021d", INFO(0x1f2300, 0, 64 * 1024,   8, SNOR_READ_BASE, 
SECT_4K) },
+       { "at45db041d", INFO(0x1f2400, 0, 64 * 1024,   8, SNOR_READ_BASE, 
SECT_4K) },
+       { "at45db081d", INFO(0x1f2500, 0, 64 * 1024,  16, SNOR_READ_BASE, 
SECT_4K) },
+       { "at45db161d", INFO(0x1f2600, 0, 64 * 1024,  32, SNOR_READ_BASE, 
SECT_4K) },
+       { "at45db321d", INFO(0x1f2700, 0, 64 * 1024,  64, SNOR_READ_BASE, 
SECT_4K) },
+       { "at45db641d", INFO(0x1f2800, 0, 64 * 1024, 128, SNOR_READ_BASE, 
SECT_4K) },
+#endif
+#ifdef CONFIG_SPI_FLASH_EON            /* EON */
+       /* EON -- en25xxx */
+       { "en25f32",    INFO(0x1c3116, 0, 64 * 1024,   64, SNOR_READ_BASE, 
SECT_4K) },
+       { "en25p32",    INFO(0x1c2016, 0, 64 * 1024,   64, SNOR_READ_BASE, 0) },
+       { "en25q32b",   INFO(0x1c3016, 0, 64 * 1024,   64, SNOR_READ_BASE, 0) },
+       { "en25p64",    INFO(0x1c2017, 0, 64 * 1024,  128, SNOR_READ_BASE, 0) },
+       { "en25q64",    INFO(0x1c3017, 0, 64 * 1024,  128, SNOR_READ_BASE, 
SECT_4K) },
+       { "en25q128b",  INFO(0x1c3018, 0, 64 * 1024,  256, SNOR_READ_BASE, 0) },
+       { "en25qh128",  INFO(0x1c7018, 0, 64 * 1024,  256, SNOR_READ_BASE, 0) },
+       { "en25qh256",  INFO(0x1c7019, 0, 64 * 1024,  512, SNOR_READ_BASE, 0) },
+       { "en25s64",    INFO(0x1c3817, 0, 64 * 1024,  128, SNOR_READ_BASE, 
SECT_4K) },
+#endif
+       /* ESMT */
+       { "f25l32pa", INFO(0x8c2016, 0, 64 * 1024, 64, SNOR_READ_BASE, SECT_4K) 
},
+
+       /* Everspin */
+       { "mr25h256", CAT25_INFO( 32 * 1024, 1, 256, 2, SNOR_READ_BASE, 
SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) },
+       { "mr25h10",  CAT25_INFO(128 * 1024, 1, 256, 3, SNOR_READ_BASE, 
SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) },
+
+       /* Fujitsu */
+       { "mb85rs1mt", INFO(0x047f27, 0, 128 * 1024, 1, SNOR_READ_BASE, 
SPI_NOR_NO_ERASE) },
+
+#ifdef CONFIG_SPI_FLASH_GIGADEVICE     /* GIGADEVICE */
+       /* GigaDevice */
+       { "gd25q32", INFO(0xc84016, 0, 64 * 1024,  64, SNOR_READ_BASE, SECT_4K) 
},
+       { "gd25q64", INFO(0xc84017, 0, 64 * 1024, 128, SNOR_READ_BASE, SECT_4K) 
},
+       { "gd25q128", INFO(0xc84018, 0, 64 * 1024, 256, SNOR_READ_BASE, 
SECT_4K) },
+       { "gd25lq32", INFO(0xc86016, 0, 64 * 1024,  64, SNOR_READ_BASE, 
SECT_4K) },
+#endif
+       /* Intel/Numonyx -- xxxs33b */
+       { "160s33b",  INFO(0x898911, 0, 64 * 1024,  32, SNOR_READ_BASE, 0) },
+       { "320s33b",  INFO(0x898912, 0, 64 * 1024,  64, SNOR_READ_BASE, 0) },
+       { "640s33b",  INFO(0x898913, 0, 64 * 1024, 128, SNOR_READ_BASE, 0) },
+
+#ifdef CONFIG_SPI_FLASH_ISSI           /* ISSI */
+       /* ISSI */
+       { "is25cd512", INFO(0x7f9d20, 0, 32 * 1024,   2, SNOR_READ_BASE, 
SECT_4K) },
+       { "is25lp032", INFO(0x9d6016, 0, 64 * 1024,  64, SNOR_READ_BASE, 0) },
+       { "is25lp064", INFO(0x9d6017, 0, 64 * 1024, 128, SNOR_READ_BASE, 0) },
+       { "is25lp128", INFO(0x9d6018, 0, 64 * 1024, 256, SNOR_READ_BASE, 0) },
+#endif
+#ifdef CONFIG_SPI_FLASH_MACRONIX       /* MACRONIX */
+       /* Macronix */
+       { "mx25l512e",   INFO(0xc22010, 0, 64 * 1024,   1, SNOR_READ_BASE, 
SECT_4K) },
+       { "mx25l2005a",  INFO(0xc22012, 0, 64 * 1024,   4, SNOR_READ_BASE, 
SECT_4K) },
+       { "mx25l4005a",  INFO(0xc22013, 0, 64 * 1024,   8, SNOR_READ_BASE, 
SECT_4K) },
+       { "mx25l8005",   INFO(0xc22014, 0, 64 * 1024,  16, SNOR_READ_BASE, 0) },
+       { "mx25l1606e",  INFO(0xc22015, 0, 64 * 1024,  32, SNOR_READ_BASE, 
SECT_4K) },
+       { "mx25l3205d",  INFO(0xc22016, 0, 64 * 1024,  64, SNOR_READ_BASE, 0) },
+       { "mx25l3255e",  INFO(0xc29e16, 0, 64 * 1024,  64, SNOR_READ_BASE, 
SECT_4K) },
+       { "mx25l6405d",  INFO(0xc22017, 0, 64 * 1024, 128, SNOR_READ_BASE, 0) },
+       { "mx25u6435f",  INFO(0xc22537, 0, 64 * 1024, 128, SNOR_READ_BASE, 
SECT_4K) },
+       { "mx25l12805d", INFO(0xc22018, 0, 64 * 1024, 256, SNOR_READ_FULL, 
SNOR_WRITE_QUAD) },
+       { "mx25l12855e", INFO(0xc22618, 0, 64 * 1024, 256, SNOR_READ_FULL, 
SNOR_WRITE_QUAD) },
+       { "mx25l25635e", INFO(0xc22019, 0, 64 * 1024, 512, SNOR_READ_FULL, 
SNOR_WRITE_QUAD) },
+       { "mx25l25655e", INFO(0xc22619, 0, 64 * 1024, 512, SNOR_READ_FULL, 
SNOR_WRITE_QUAD) },
+       { "mx66l51235l", INFO(0xc2201a, 0, 64 * 1024, 1024, SNOR_READ_FULL, 
SNOR_WRITE_QUAD) },
+       { "mx66l1g55g",  INFO(0xc2261b, 0, 64 * 1024, 2048, SNOR_READ_FULL, 
SNOR_WRITE_QUAD) },
+#endif
+#ifdef CONFIG_SPI_FLASH_STMICRO                /* STMICRO */
+       /* Micron */
+       { "n25q032",     INFO(0x20ba16, 0, 64 * 1024,   64, SNOR_READ_FULL, 
SNOR_WRITE_QUAD) },
+       { "n25q064",     INFO(0x20ba17, 0, 64 * 1024,  128, SNOR_READ_FULL, 
SNOR_WRITE_QUAD | SECT_4K) },
+       { "n25q064a",    INFO(0x20bb17, 0, 64 * 1024,  128, SNOR_READ_FULL, 
SNOR_WRITE_QUAD | SECT_4K) },
+       { "n25q128a11",  INFO(0x20bb18, 0, 64 * 1024,  256, SNOR_READ_FULL, 
SNOR_WRITE_QUAD) },
+       { "n25q128a13",  INFO(0x20ba18, 0, 64 * 1024,  256, SNOR_READ_FULL, 
SNOR_WRITE_QUAD) },
+       { "n25q256a",    INFO(0x20ba19, 0, 64 * 1024,  512, SNOR_READ_FULL, 
SNOR_WRITE_QUAD | SECT_4K) },
+       { "n25q512a",    INFO(0x20bb20, 0, 64 * 1024, 1024, SNOR_READ_FULL, 
SNOR_WRITE_QUAD | SECT_4K | USE_FSR) },
+       { "n25q512ax3",  INFO(0x20ba20, 0, 64 * 1024, 1024, SNOR_READ_FULL, 
SNOR_WRITE_QUAD | SECT_4K | USE_FSR) },
+       { "n25q00",      INFO(0x20ba21, 0, 64 * 1024, 2048, SNOR_READ_FULL, 
SNOR_WRITE_QUAD | SECT_4K | USE_FSR) },
+#endif
+       /* PMC */
+       { "pm25lv512",   INFO(0,        0, 32 * 1024,    2, SNOR_READ_BASE, 
SECT_4K_PMC) },
+       { "pm25lv010",   INFO(0,        0, 32 * 1024,    4, SNOR_READ_BASE, 
SECT_4K_PMC) },
+       { "pm25lq032",   INFO(0x7f9d46, 0, 64 * 1024,   64, SNOR_READ_BASE, 
SECT_4K) },
+
+#ifdef CONFIG_SPI_FLASH_SPANSION       /* SPANSION */
+       /* Spansion -- single (large) sector size only, at least
+        * for the chips listed here (without boot sectors).
+        */
+       { "s25sl032p",  INFO(0x010215, 0x4d00,  64 * 1024,  64, SNOR_READ_FULL, 
0) },
+       { "s25sl064p",  INFO(0x010216, 0x4d00,  64 * 1024, 128, SNOR_READ_FULL, 
0) },
+       { "s25fl256s0", INFO(0x010219, 0x4d00, 256 * 1024, 128, SNOR_READ_FULL, 
SNOR_WRITE_QUAD) },
+       { "s25fl256s1", INFO(0x010219, 0x4d01,  64 * 1024, 512, SNOR_READ_FULL, 
0) },
+       { "s25fl512s",  INFO(0x010220, 0x4d00, 256 * 1024, 256, SNOR_READ_FULL, 
0) },
+       { "s25fl512s1", INFO(0x010220, 0x4d01,  64 * 1024, 1024, 
SNOR_READ_FULL, SNOR_WRITE_QUAD) },
+       { "s25fl512s2", INFO(0x010220, 0x4f00, 256 * 1024, 256, SNOR_READ_FULL, 
SNOR_WRITE_QUAD) },
+       { "s70fl01gs",  INFO(0x010221, 0x4d00, 256 * 1024, 256, SNOR_READ_FULL, 
SNOR_WRITE_QUAD) },
+       { "s25sl12800", INFO(0x012018, 0x0300, 256 * 1024,  64, SNOR_READ_FULL, 
SNOR_WRITE_QUAD) },
+       { "s25sl12801", INFO(0x012018, 0x0301,  64 * 1024, 256, SNOR_READ_FULL, 
SNOR_WRITE_QUAD) },
+       { "s25fl128s",  INFO6(0x012018, 0x4d0180, 64 * 1024, 256, 
SNOR_READ_FULL, SNOR_WRITE_QUAD) },
+       { "s25fl129p0", INFO(0x012018, 0x4d00, 256 * 1024,  64, SNOR_READ_FULL, 
SNOR_WRITE_QUAD) },
+       { "s25fl129p1", INFO(0x012018, 0x4d01,  64 * 1024, 256, SNOR_READ_FULL, 
SNOR_WRITE_QUAD) },
+       { "s25sl004a",  INFO(0x010212,      0,  64 * 1024,   8, SNOR_READ_BASE, 
0) },
+       { "s25sl008a",  INFO(0x010213,      0,  64 * 1024,  16, SNOR_READ_BASE, 
0) },
+       { "s25sl016a",  INFO(0x010214,      0,  64 * 1024,  32, SNOR_READ_BASE, 
0) },
+       { "s25sl032a",  INFO(0x010215,      0,  64 * 1024,  64, SNOR_READ_BASE, 
0) },
+       { "s25sl064a",  INFO(0x010216,      0,  64 * 1024, 128, SNOR_READ_BASE, 
0) },
+       { "s25fl008k",  INFO(0xef4014,      0,  64 * 1024,  16, SNOR_READ_BASE, 
SECT_4K) },
+       { "s25fl016k",  INFO(0xef4015,      0,  64 * 1024,  32, SNOR_READ_BASE, 
SECT_4K) },
+       { "s25fl064k",  INFO(0xef4017,      0,  64 * 1024, 128, SNOR_READ_BASE, 
SECT_4K) },
+       { "s25fl132k",  INFO(0x014016,      0,  64 * 1024,  64, SNOR_READ_BASE, 
SECT_4K) },
+       { "s25fl164k",  INFO(0x014017,      0,  64 * 1024, 128, SNOR_READ_BASE, 
SECT_4K) },
+       { "s25fl204k",  INFO(0x014013,      0,  64 * 1024,   8, SNOR_READ_BASE, 
SECT_4K) },
+#endif
+#ifdef CONFIG_SPI_FLASH_SST            /* SST */
+       /* SST -- large erase sizes are "overlays", "sectors" are 4K */
+       { "sst25vf040b", INFO(0xbf258d, 0, 64 * 1024,  8, SNOR_READ_BASE, 
SECT_4K | SST_WRITE) },
+       { "sst25vf080b", INFO(0xbf258e, 0, 64 * 1024, 16, SNOR_READ_BASE, 
SECT_4K | SST_WRITE) },
+       { "sst25vf016b", INFO(0xbf2541, 0, 64 * 1024, 32, SNOR_READ_BASE, 
SECT_4K | SST_WRITE) },
+       { "sst25vf032b", INFO(0xbf254a, 0, 64 * 1024, 64, SNOR_READ_BASE, 
SECT_4K | SST_WRITE) },
+       { "sst25vf064c", INFO(0xbf254b, 0, 64 * 1024, 128, SNOR_READ_BASE, 
SECT_4K) },
+       { "sst25wf512",  INFO(0xbf2501, 0, 64 * 1024,  1, SNOR_READ_BASE, 
SECT_4K | SST_WRITE) },
+       { "sst25wf010",  INFO(0xbf2502, 0, 64 * 1024,  2, SNOR_READ_BASE, 
SECT_4K | SST_WRITE) },
+       { "sst25wf020",  INFO(0xbf2503, 0, 64 * 1024,  4, SNOR_READ_BASE, 
SECT_4K | SST_WRITE) },
+       { "sst25wf040",  INFO(0xbf2504, 0, 64 * 1024,  8, SNOR_READ_BASE, 
SECT_4K | SST_WRITE) },
+       { "sst25wf020a", INFO(0x621612, 0, 64 * 1024,  4, SNOR_READ_BASE, 
SECT_4K) },
+       { "sst25wf040b", INFO(0x621613, 0, 64 * 1024,  8, SNOR_READ_BASE, 
SECT_4K) },
+       { "sst25wf040",  INFO(0xbf2504, 0, 64 * 1024,  8, SNOR_READ_BASE, 
SECT_4K | SST_WRITE) },
+       { "sst25wf080",  INFO(0xbf2505, 0, 64 * 1024, 16, SNOR_READ_BASE, 
SECT_4K | SST_WRITE) },
+#endif
+#ifdef CONFIG_SPI_FLASH_STMICRO                /* STMICRO */
+       /* ST Microelectronics -- newer production may have feature updates */
+       { "m25p05",  INFO(0x202010,  0,  32 * 1024,   2, SNOR_READ_BASE, 0) },
+       { "m25p10",  INFO(0x202011,  0,  32 * 1024,   4, SNOR_READ_BASE, 0) },
+       { "m25p20",  INFO(0x202012,  0,  64 * 1024,   4, SNOR_READ_BASE, 0) },
+       { "m25p40",  INFO(0x202013,  0,  64 * 1024,   8, SNOR_READ_BASE, 0) },
+       { "m25p80",  INFO(0x202014,  0,  64 * 1024,  16, SNOR_READ_BASE, 0) },
+       { "m25p16",  INFO(0x202015,  0,  64 * 1024,  32, SNOR_READ_BASE, 0) },
+       { "m25p32",  INFO(0x202016,  0,  64 * 1024,  64, SNOR_READ_BASE, 0) },
+       { "m25p64",  INFO(0x202017,  0,  64 * 1024, 128, SNOR_READ_BASE, 0) },
+       { "m25p128", INFO(0x202018,  0, 256 * 1024,  64, SNOR_READ_BASE, 0) },
+
+       { "m25p05-nonjedec",  INFO(0, 0,  32 * 1024,   2, SNOR_READ_BASE, 0) },
+       { "m25p10-nonjedec",  INFO(0, 0,  32 * 1024,   4, SNOR_READ_BASE, 0) },
+       { "m25p20-nonjedec",  INFO(0, 0,  64 * 1024,   4, SNOR_READ_BASE, 0) },
+       { "m25p40-nonjedec",  INFO(0, 0,  64 * 1024,   8, SNOR_READ_BASE, 0) },
+       { "m25p80-nonjedec",  INFO(0, 0,  64 * 1024,  16, SNOR_READ_BASE, 0) },
+       { "m25p16-nonjedec",  INFO(0, 0,  64 * 1024,  32, SNOR_READ_BASE, 0) },
+       { "m25p32-nonjedec",  INFO(0, 0,  64 * 1024,  64, SNOR_READ_BASE, 0) },
+       { "m25p64-nonjedec",  INFO(0, 0,  64 * 1024, 128, SNOR_READ_BASE, 0) },
+       { "m25p128-nonjedec", INFO(0, 0, 256 * 1024,  64, SNOR_READ_BASE, 0) },
+
+       { "m45pe10", INFO(0x204011,  0, 64 * 1024,    2, SNOR_READ_BASE, 0) },
+       { "m45pe80", INFO(0x204014,  0, 64 * 1024,   16, SNOR_READ_BASE, 0) },
+       { "m45pe16", INFO(0x204015,  0, 64 * 1024,   32, SNOR_READ_BASE, 0) },
+
+       { "m25pe20", INFO(0x208012,  0, 64 * 1024,  4, SNOR_READ_BASE, 0) },
+       { "m25pe80", INFO(0x208014,  0, 64 * 1024, 16, SNOR_READ_BASE, 0) },
+       { "m25pe16", INFO(0x208015,  0, 64 * 1024, 32, SNOR_READ_BASE, SECT_4K) 
},
+
+       { "m25px16",    INFO(0x207115,  0, 64 * 1024, 32, SNOR_READ_BASE, 
SECT_4K) },
+       { "m25px32",    INFO(0x207116,  0, 64 * 1024, 64, SNOR_READ_BASE, 
SECT_4K) },
+       { "m25px32-s0", INFO(0x207316,  0, 64 * 1024, 64, SNOR_READ_BASE, 
SECT_4K) },
+       { "m25px32-s1", INFO(0x206316,  0, 64 * 1024, 64, SNOR_READ_BASE, 
SECT_4K) },
+       { "m25px64",    INFO(0x207117,  0, 64 * 1024, 128, SNOR_READ_BASE, 0) },
+       { "m25px80",    INFO(0x207114,  0, 64 * 1024, 16, SNOR_READ_BASE, 0) },
+#endif
+#ifdef CONFIG_SPI_FLASH_WINBOND                /* WINBOND */
+       /* Winbond -- w25x "blocks" are 64K, "sectors" are 4KiB */
+       { "W25P80", INFO(0xef2014, 0, 64 * 1024, 16, SNOR_READ_BASE, 0) },
+       { "W25P16", INFO(0xef2015, 0, 64 * 1024, 32, SNOR_READ_BASE, 0) },
+       { "W25P32", INFO(0xef2016, 0, 64 * 1024, 64, SNOR_READ_BASE, 0) },
+       { "w25x05", INFO(0xef3010, 0, 64 * 1024,  1, SNOR_READ_BASE, SECT_4K) },
+       { "w25x10", INFO(0xef3011, 0, 64 * 1024,  2, SNOR_READ_BASE, SECT_4K) },
+       { "w25x20", INFO(0xef3012, 0, 64 * 1024,  4, SNOR_READ_BASE, SECT_4K) },
+       { "w25x40", INFO(0xef3013, 0, 64 * 1024,  8, SNOR_READ_BASE, SECT_4K) },
+       { "w25x80", INFO(0xef3014, 0, 64 * 1024,  16, SNOR_READ_BASE, SECT_4K) 
},
+       { "w25x16", INFO(0xef3015, 0, 64 * 1024,  32, SNOR_READ_BASE, SECT_4K) 
},
+       { "w25x32", INFO(0xef3016, 0, 64 * 1024,  64, SNOR_READ_BASE, SECT_4K) 
},
+       { "w25x64", INFO(0xef3017, 0, 64 * 1024, 128, SNOR_READ_BASE, SECT_4K) 
},
+       { "w25q80bl", INFO(0xef4014, 0, 64 * 1024,  16, SNOR_READ_FULL, 
SNOR_WRITE_QUAD | SECT_4K) },
+       {" w25q16cl", INFO(0xef4015, 0, 64 * 1024,  32, SNOR_READ_FULL, 
SNOR_WRITE_QUAD | SECT_4K) },
+       { "w25q32", INFO(0xef4016, 0, 64 * 1024,  64, SNOR_READ_FULL, 
SNOR_WRITE_QUAD | SECT_4K) },
+       { "w25q64", INFO(0xef4017, 0, 64 * 1024, 128, SNOR_READ_FULL, 
SNOR_WRITE_QUAD | SECT_4K) },
+       { "w25q128", INFO(0xef4018, 0, 64 * 1024, 256, SNOR_READ_FULL, 
SNOR_WRITE_QUAD | SECT_4K) },
+       { "w25q256", INFO(0xef4019, 0, 64 * 1024, 512, SNOR_READ_FULL, 
SNOR_WRITE_QUAD | SECT_4K) },
+       { "w25q80", INFO(0xef5014, 0, 64 * 1024,  16, SNOR_READ_FULL, 
SNOR_WRITE_QUAD | SECT_4K) },
+       { "w25q16dw", INFO(0xef6015, 0, 64 * 1024,  32, SNOR_READ_FULL, 
SNOR_WRITE_QUAD | SECT_4K) },
+       { "w25q32dw", INFO(0xef6016, 0, 64 * 1024,  64, SNOR_READ_FULL, 
SNOR_WRITE_QUAD | SECT_4K) },
+       { "w25q64dw", INFO(0xef6017, 0, 64 * 1024, 128, SNOR_READ_FULL, 
SNOR_WRITE_QUAD | SECT_4K) },
+       { "w25q128fw", INFO(0xef6018, 0, 64 * 1024, 256, SNOR_READ_FULL, 
SNOR_WRITE_QUAD | SECT_4K) },
+#endif
+       /* Catalyst / On Semiconductor -- non-JEDEC */
+       { "cat25c11", CAT25_INFO(  16, 8, 16, 1, SNOR_READ_BASE, 
SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) },
+       { "cat25c03", CAT25_INFO(  32, 8, 16, 2, SNOR_READ_BASE, 
SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) },
+       { "cat25c09", CAT25_INFO( 128, 8, 32, 2, SNOR_READ_BASE, 
SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) },
+       { "cat25c17", CAT25_INFO( 256, 8, 32, 2, SNOR_READ_BASE, 
SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) },
+       { "cat25128", CAT25_INFO(2048, 8, 64, 2, SNOR_READ_BASE, 
SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) },
+       { },
+};
diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c
new file mode 100644
index 0000000..f142ae4
--- /dev/null
+++ b/drivers/mtd/spi-nor/spi-nor.c
@@ -0,0 +1,1084 @@
+/*
+ * SPI NOR Core - cloned most of the code from the spi_flash.c
+ *
+ * Copyright (C) 2016 Jagan Teki <jt...@openedev.com>
+ *
+ * SPDX-License-Identifier:    GPL-2.0+
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <errno.h>
+#include <malloc.h>
+#include <mapmem.h>
+
+#include <linux/math64.h>
+#include <linux/log2.h>
+#include <linux/mtd/spi-nor.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+/* Set write enable latch with Write Enable command */
+static inline int write_enable(struct spi_nor *nor)
+{
+       return nor->write_reg(nor, SNOR_OP_WREN, NULL, 0);
+}
+
+/* Re-set write enable latch with Write Disable command */
+static inline int write_disable(struct spi_nor *nor)
+{
+       return nor->write_reg(nor, SNOR_OP_WRDI, NULL, 0);
+}
+
+static void spi_nor_addr(u32 addr, u8 *cmd)
+{
+       /* cmd[0] is actual command */
+       cmd[1] = addr >> 16;
+       cmd[2] = addr >> 8;
+       cmd[3] = addr >> 0;
+}
+
+static int read_sr(struct spi_nor *nor)
+{
+       u8 sr;
+       int ret;
+
+       ret = nor->read_reg(nor, SNOR_OP_RDSR, &sr, 1);
+       if (ret < 0) {
+               debug("spi-nor: fail to read status register\n");
+               return ret;
+       }
+
+       return sr;
+}
+
+static int read_fsr(struct spi_nor *nor)
+{
+       u8 fsr;
+       int ret;
+
+       ret = nor->read_reg(nor, SNOR_OP_RDFSR, &fsr, 1);
+       if (ret < 0) {
+               debug("spi-nor: fail to read flag status register\n");
+               return ret;
+       }
+
+       return fsr;
+}
+
+static int write_sr(struct spi_nor *nor, u8 ws)
+{
+       nor->cmd_buf[0] = ws;
+       return nor->write_reg(nor, SNOR_OP_WRSR, nor->cmd_buf, 1);
+}
+
+#if defined(CONFIG_SPI_FLASH_SPANSION) || defined(CONFIG_SPI_FLASH_WINBOND)
+static int read_cr(struct spi_nor *nor)
+{
+       u8 cr;
+       int ret;
+
+       ret = nor->read_reg(nor, SNOR_OP_RDCR, &cr, 1);
+       if (ret < 0) {
+               debug("spi-nor: fail to read config register\n");
+               return ret;
+       }
+
+       return cr;
+}
+
+/*
+ * Write status Register and configuration register with 2 bytes
+ * - First byte will be written to the status register.
+ * - Second byte will be written to the configuration register.
+ * Return negative if error occured.
+ */
+static int write_sr_cr(struct spi_nor *nor, u16 val)
+{
+       nor->cmd_buf[0] = val & 0xff;
+       nor->cmd_buf[1] = (val >> 8);
+
+       return nor->write_reg(nor, SNOR_OP_WRSR, nor->cmd_buf, 2);
+}
+#endif
+
+#ifdef CONFIG_SPI_FLASH_STMICRO
+static int read_evcr(struct spi_nor *nor)
+{
+       u8 evcr;
+       int ret;
+
+       ret = nor->read_reg(nor, SPINOR_OP_RD_EVCR, &evcr, 1);
+       if (ret < 0) {
+               debug("spi-nor: fail to read EVCR\n");
+               return ret;
+       }
+
+       return evcr;
+}
+
+static int write_evcr(struct spi_nor *nor, u8 evcr)
+{
+       nor->cmd_buf[0] = evcr;
+       return nor->write_reg(nor, SPINOR_OP_WD_EVCR, nor->cmd_buf, 1);
+}
+#endif
+
+static int spi_nor_sr_ready(struct spi_nor *nor)
+{
+       int sr = read_sr(nor);
+       if (sr < 0)
+               return sr;
+       else
+               return !(sr & SR_WIP);
+}
+
+static int spi_nor_fsr_ready(struct spi_nor *nor)
+{
+       int fsr = read_fsr(nor);
+       if (fsr < 0)
+               return fsr;
+       else
+               return fsr & FSR_READY;
+}
+
+static int spi_nor_ready(struct spi_nor *nor)
+{
+       int sr, fsr;
+
+       sr = spi_nor_sr_ready(nor);
+       if (sr < 0)
+               return sr;
+
+       fsr = 1;
+       if (nor->flags & SNOR_F_USE_FSR) {
+               fsr = spi_nor_fsr_ready(nor);
+               if (fsr < 0)
+                       return fsr;
+       }
+
+       return sr && fsr;
+}
+
+static int spi_nor_wait_till_ready(struct spi_nor *nor, unsigned long timeout)
+{
+       int timebase, ret;
+
+       timebase = get_timer(0);
+
+       while (get_timer(timebase) < timeout) {
+               ret = spi_nor_ready(nor);
+               if (ret < 0)
+                       return ret;
+               if (ret)
+                       return 0;
+       }
+
+       printf("spi-nor: Timeout!\n");
+
+       return -ETIMEDOUT;
+}
+
+#ifdef CONFIG_SPI_FLASH_BAR
+static int spi_nor_write_bar(struct spi_nor *nor, u32 offset)
+{
+       u8 bank_sel;
+       int ret;
+
+       bank_sel = offset / (SNOR_16MB_BOUN << nor->shift);
+       if (bank_sel == nor->bank_curr)
+               goto bar_end;
+
+       write_enable(nor);
+
+       nor->cmd_buf[0] = bank_sel;
+       ret = nor->write_reg(nor, nor->bar_program_opcode, nor->cmd_buf, 1);
+       if (ret < 0) {
+               debug("spi-nor: fail to write bank register\n");
+               return ret;
+       }
+
+       ret = spi_nor_wait_till_ready(nor, SNOR_READY_WAIT_PROG);
+       if (ret < 0)
+               return ret;
+
+bar_end:
+       nor->bank_curr = bank_sel;
+       return nor->bank_curr;
+}
+
+static int spi_nor_read_bar(struct spi_nor *nor, const struct spi_nor_info 
*info)
+{
+       u8 curr_bank = 0;
+       int ret;
+
+       if (flash->size <= SNOR_16MB_BOUN)
+               goto bar_end;
+
+       switch (JEDEC_MFR(info)) {
+       case SNOR_MFR_SPANSION:
+               nor->bar_read_opcode = SNOR_OP_BRRD;
+               nor->bar_program_opcode = SNOR_OP_BRWR;
+               break;
+       default:
+               nor->bar_read_opcode = SNOR_OP_RDEAR;
+               nor->bar_program_opcode = SNOR_OP_WREAR;
+       }
+
+       ret = nor->read_reg(nor, nor->bar_read_opcode, &curr_bank, 1);
+       if (ret) {
+               debug("spi-nor: fail to read bank addr register\n");
+               return ret;
+       }
+
+bar_end:
+       nor->bank_curr = curr_bank;
+       return 0;
+}
+#endif
+
+#ifdef CONFIG_SF_DUAL_FLASH
+static void spi_nor_dual(struct spi_nor *nor, u32 *addr)
+{
+       struct spi_flash *flash = nor->flash;
+
+       switch (nor->dual) {
+       case SNOR_DUAL_STACKED:
+               if (*addr >= (flash->size >> 1)) {
+                       *addr -= flash->size >> 1;
+                       nor->flags |= SNOR_F_U_PAGE;
+               } else {
+                       nor->flags &= ~SNOR_F_U_PAGE;
+               }
+               break;
+       case SNOR_DUAL_PARALLEL:
+               *addr >>= nor->shift;
+               break;
+       default:
+               debug("spi-nor: Unsupported dual_flash=%d\n", nor->dual);
+               break;
+       }
+}
+#endif
+
+#if defined(CONFIG_SPI_FLASH_STMICRO) || defined(CONFIG_SPI_FLASH_SST)
+static void stm_get_locked_range(struct spi_nor *nor, u8 sr, loff_t *ofs,
+                                u32 *len)
+{
+       u8 mask = SR_BP2 | SR_BP1 | SR_BP0;
+       int shift = ffs(mask) - 1;
+       int pow;
+
+       if (!(sr & mask)) {
+               /* No protection */
+               *ofs = 0;
+               *len = 0;
+       } else {
+               pow = ((sr & mask) ^ mask) >> shift;
+               *len = flash->size >> pow;
+               *ofs = flash->size - *len;
+       }
+}
+
+/*
+ * Return 1 if the entire region is locked, 0 otherwise
+ */
+static int stm_is_locked_sr(struct spi_nor *nor, u32 ofs, u32 len, u8 sr)
+{
+       loff_t lock_offs;
+       u32 lock_len;
+
+       stm_get_locked_range(nor, sr, &lock_offs, &lock_len);
+
+       return (ofs + len <= lock_offs + lock_len) && (ofs >= lock_offs);
+}
+
+/*
+ * Check if a region of the flash is (completely) locked. See stm_lock() for
+ * more info.
+ *
+ * Returns 1 if entire region is locked, 0 if any portion is unlocked, and
+ * negative on errors.
+ */
+static int stm_is_locked(struct spi_nor *nor, u32 ofs, size_t len)
+{
+       int status;
+
+       status = read_sr(nor);
+       if (status < 0)
+               return status;
+
+       return stm_is_locked_sr(nor, ofs, len, status);
+}
+
+/*
+ * Lock a region of the flash. Compatible with ST Micro and similar flash.
+ * Supports only the block protection bits BP{0,1,2} in the status register
+ * (SR). Does not support these features found in newer SR bitfields:
+ *   - TB: top/bottom protect - only handle TB=0 (top protect)
+ *   - SEC: sector/block protect - only handle SEC=0 (block protect)
+ *   - CMP: complement protect - only support CMP=0 (range is not complemented)
+ *
+ * Sample table portion for 8MB flash (Winbond w25q64fw):
+ *
+ *   SEC  |  TB   |  BP2  |  BP1  |  BP0  |  Prot Length  | Protected Portion
+ *  --------------------------------------------------------------------------
+ *    X   |   X   |   0   |   0   |   0   |  NONE         | NONE
+ *    0   |   0   |   0   |   0   |   1   |  128 KB       | Upper 1/64
+ *    0   |   0   |   0   |   1   |   0   |  256 KB       | Upper 1/32
+ *    0   |   0   |   0   |   1   |   1   |  512 KB       | Upper 1/16
+ *    0   |   0   |   1   |   0   |   0   |  1 MB         | Upper 1/8
+ *    0   |   0   |   1   |   0   |   1   |  2 MB         | Upper 1/4
+ *    0   |   0   |   1   |   1   |   0   |  4 MB         | Upper 1/2
+ *    X   |   X   |   1   |   1   |   1   |  8 MB         | ALL
+ *
+ * Returns negative on errors, 0 on success.
+ */
+static int stm_lock(struct spi_nor *nor, u32 ofs, size_t len)
+{
+       u8 status_old, status_new;
+       u8 mask = SR_BP2 | SR_BP1 | SR_BP0;
+       u8 shift = ffs(mask) - 1, pow, val;
+
+       status_old = read_sr(nor);
+       if (status_old < 0)
+               return status_old;
+
+       /* SPI NOR always locks to the end */
+       if (ofs + len != flash->size) {
+               /* Does combined region extend to end? */
+               if (!stm_is_locked_sr(nor, ofs + len, flash->size - ofs - len,
+                                     status_old))
+                       return -EINVAL;
+               len = flash->size - ofs;
+       }
+
+       /*
+        * Need smallest pow such that:
+        *
+        *   1 / (2^pow) <= (len / size)
+        *
+        * so (assuming power-of-2 size) we do:
+        *
+        *   pow = ceil(log2(size / len)) = log2(size) - floor(log2(len))
+        */
+       pow = ilog2(flash->size) - ilog2(len);
+       val = mask - (pow << shift);
+       if (val & ~mask)
+               return -EINVAL;
+
+       /* Don't "lock" with no region! */
+       if (!(val & mask))
+               return -EINVAL;
+
+       status_new = (status_old & ~mask) | val;
+
+       /* Only modify protection if it will not unlock other areas */
+       if ((status_new & mask) <= (status_old & mask))
+               return -EINVAL;
+
+       write_enable(nor);
+       return write_sr(nor, status_new);
+}
+
+/*
+ * Unlock a region of the flash. See stm_lock() for more info
+ *
+ * Returns negative on errors, 0 on success.
+ */
+static int stm_unlock(struct spi_nor *nor, u32 ofs, size_t len)
+{
+       uint8_t status_old, status_new;
+       u8 mask = SR_BP2 | SR_BP1 | SR_BP0;
+       u8 shift = ffs(mask) - 1, pow, val;
+
+       status_old = read_sr(nor);
+       if (status_old  < 0)
+               return status_old;
+
+       /* Cannot unlock; would unlock larger region than requested */
+       if (stm_is_locked_sr(nor, status_old, ofs - flash->erase_size,
+                            nor->erase_size))
+               return -EINVAL;
+       /*
+        * Need largest pow such that:
+        *
+        *   1 / (2^pow) >= (len / size)
+        *
+        * so (assuming power-of-2 size) we do:
+        *
+        *   pow = floor(log2(size / len)) = log2(size) - ceil(log2(len))
+        */
+       pow = ilog2(flash->size) - order_base_2(flash->size - (ofs + len));
+       if (ofs + len == flash->size) {
+               val = 0; /* fully unlocked */
+       } else {
+               val = mask - (pow << shift);
+               /* Some power-of-two sizes are not supported */
+               if (val & ~mask)
+                       return -EINVAL;
+       }
+
+       status_new = (status_old & ~mask) | val;
+
+       /* Only modify protection if it will not lock other areas */
+       if ((status_new & mask) >= (status_old & mask))
+               return -EINVAL;
+
+       write_enable(nor);
+       return write_sr(nor, status_new);
+}
+#endif
+
+static const struct spi_nor_info *spi_nor_id(struct spi_nor *nor)
+{
+       int                             tmp;
+       u8                              id[SPI_NOR_MAX_ID_LEN];
+       const struct spi_nor_info       *info;
+
+       tmp = nor->read_reg(nor, SNOR_OP_RDID, id, SPI_NOR_MAX_ID_LEN);
+       if (tmp < 0) {
+               printf("spi-nor: error %d reading JEDEC ID\n", tmp);
+               return ERR_PTR(tmp);
+       }
+
+       info = spi_nor_ids;
+       for (; info->name != NULL; info++) {
+               if (info->id_len) {
+                       if (!memcmp(info->id, id, info->id_len))
+                               return info;
+               }
+       }
+
+       printf("spi-nor: unrecognized JEDEC id bytes: %02x, %2x, %2x\n",
+              id[0], id[1], id[2]);
+       return ERR_PTR(-ENODEV);
+}
+
+static int spi_nor_erase(struct spi_flash *flash, u32 offset, size_t len)
+{
+       struct spi_nor *nor = flash->nor;
+       u32 erase_size, erase_addr;
+       u8 cmd[SNOR_MAX_CMD_SIZE];
+       int ret = -1;
+
+       erase_size = nor->erase_size;
+       if (offset % erase_size || len % erase_size) {
+               debug("spi-nor: Erase offset/length not multiple of erase 
size\n");
+               return -1;
+       }
+
+       if (flash->flash_is_locked) {
+               if (flash->flash_is_locked(flash, offset, len) > 0) {
+                       printf("offset 0x%x is protected and cannot be 
erased\n",
+                              offset);
+                       return -EINVAL;
+               }
+       }
+
+       cmd[0] = flash->erase_opcode;
+       while (len) {
+               erase_addr = offset;
+
+#ifdef CONFIG_SF_DUAL_FLASH
+               if (nor->dual > SNOR_DUAL_SINGLE)
+                       spi_nor_dual(nor, &erase_addr);
+#endif
+#ifdef CONFIG_SPI_FLASH_BAR
+               ret = spi_nor_write_bar(nor, erase_addr);
+               if (ret < 0)
+                       return ret;
+#endif
+               spi_nor_addr(erase_addr, cmd);
+
+               debug("spi-nor: erase %2x %2x %2x %2x (%x)\n", cmd[0], cmd[1],
+                     cmd[2], cmd[3], erase_addr);
+
+               write_enable(nor);
+
+               ret = nor->write(nor, cmd, sizeof(cmd), NULL, 0);
+               if (ret < 0)
+                       break;
+
+               ret = spi_nor_wait_till_ready(nor, SNOR_READY_WAIT_ERASE);
+               if (ret < 0)
+                       return ret;
+
+               offset += erase_size;
+               len -= erase_size;
+       }
+
+       return ret;
+}
+
+int spi_nor_write(struct spi_flash *flash, u32 offset,
+                 size_t len, const void *buf)
+{
+       struct spi_nor *nor = flash->nor;
+       unsigned long byte_addr, page_size;
+       u32 write_addr;
+       size_t chunk_len, actual;
+       u8 cmd[SNOR_MAX_CMD_SIZE];
+       int ret = -1;
+
+       page_size = nor->page_size;
+
+       if (flash->flash_is_locked) {
+               if (flash->flash_is_locked(flash, offset, len) > 0) {
+                       printf("offset 0x%x is protected and cannot be 
written\n",
+                              offset);
+                       return -EINVAL;
+               }
+       }
+
+       cmd[0] = nor->program_opcode;
+       for (actual = 0; actual < len; actual += chunk_len) {
+               write_addr = offset;
+
+#ifdef CONFIG_SF_DUAL_FLASH
+               if (nor->dual > SNOR_DUAL_SINGLE)
+                       spi_nor_dual(nor, &write_addr);
+#endif
+#ifdef CONFIG_SPI_FLASH_BAR
+               ret = spi_nor_write_bar(nor, write_addr);
+               if (ret < 0)
+                       return ret;
+#endif
+               byte_addr = offset % page_size;
+               chunk_len = min(len - actual, (size_t)(page_size - byte_addr));
+
+               if (nor->max_write_size)
+                       chunk_len = min(chunk_len,
+                                       (size_t)nor->max_write_size);
+
+               spi_nor_addr(write_addr, cmd);
+
+               debug("spi-nor: 0x%p => cmd = { 0x%02x 0x%02x%02x%02x } 
chunk_len = %zu\n",
+                     buf + actual, cmd[0], cmd[1], cmd[2], cmd[3], chunk_len);
+
+               write_enable(nor);
+
+               ret = nor->write(nor, cmd, sizeof(cmd),
+                                buf + actual, chunk_len);
+               if (ret < 0)
+                       break;
+
+               ret = spi_nor_wait_till_ready(nor, SNOR_READY_WAIT_PROG);
+               if (ret < 0)
+                       return ret;
+
+               offset += chunk_len;
+       }
+
+       return ret;
+}
+
+int spi_nor_read(struct spi_flash *flash, u32 offset, size_t len, void *data)
+{
+       struct spi_nor *nor = flash->nor;
+       u32 remain_len, read_len, read_addr;
+       u8 *cmd, cmdsz;
+       int bank_sel = 0;
+       int ret = -1;
+
+       /* Handle memory-mapped SPI */
+       if (nor->memory_map) {
+               ret = nor->read_mmap(nor, data, nor->memory_map + offset, len);
+               if (ret) {
+                       debug("spi-nor: mmap read failed\n");
+                       return ret;
+               }
+
+               return ret;
+       }
+
+       cmdsz = SNOR_MAX_CMD_SIZE + nor->read_dummy;
+       cmd = calloc(1, cmdsz);
+       if (!cmd) {
+               debug("spi-nor: Failed to allocate cmd\n");
+               return -ENOMEM;
+       }
+
+       cmd[0] = nor->read_opcode;
+       while (len) {
+               read_addr = offset;
+
+#ifdef CONFIG_SF_DUAL_FLASH
+               if (nor->dual > SNOR_DUAL_SINGLE)
+                       spi_nor_dual(nor, &read_addr);
+#endif
+#ifdef CONFIG_SPI_FLASH_BAR
+               ret = spi_nor_write_bar(nor, read_addr);
+               if (ret < 0)
+                       return ret;
+               bank_sel = nor->bank_curr;
+#endif
+               remain_len = ((SNOR_16MB_BOUN << nor->shift) *
+                               (bank_sel + 1)) - offset;
+               if (len < remain_len)
+                       read_len = len;
+               else
+                       read_len = remain_len;
+
+               spi_nor_addr(read_addr, cmd);
+
+               ret = nor->read(nor, cmd, cmdsz, data, read_len);
+               if (ret < 0)
+                       break;
+
+               offset += read_len;
+               len -= read_len;
+               data += read_len;
+       }
+
+       free(cmd);
+       return ret;
+}
+
+#ifdef CONFIG_SPI_FLASH_SST
+static int sst_byte_write(struct spi_nor *nor, u32 offset, const void *buf)
+{
+       int ret;
+       u8 cmd[4] = {
+               SNOR_OP_BP,
+               offset >> 16,
+               offset >> 8,
+               offset,
+       };
+
+       debug("spi-nor: 0x%p => cmd = { 0x%02x 0x%06x }\n",
+             buf, cmd[0], offset);
+
+       ret = write_enable(nor);
+       if (ret)
+               return ret;
+
+       ret = nor->write(nor, cmd, sizeof(cmd), buf, 1);
+       if (ret)
+               return ret;
+
+       return spi_nor_wait_till_ready(nor, SNOR_READY_WAIT_PROG);
+}
+
+int sst_write_wp(struct spi_nor *nor, u32 offset, size_t len, const void *buf)
+{
+       struct spi_nor *nor = flash->nor;
+       size_t actual, cmd_len;
+       int ret;
+       u8 cmd[4];
+
+       /* If the data is not word aligned, write out leading single byte */
+       actual = offset % 2;
+       if (actual) {
+               ret = sst_byte_write(nor, offset, buf);
+               if (ret)
+                       goto done;
+       }
+       offset += actual;
+
+       ret = write_enable(nor);
+       if (ret)
+               goto done;
+
+       cmd_len = 4;
+       cmd[0] = SNOR_OP_AAI_WP;
+       cmd[1] = offset >> 16;
+       cmd[2] = offset >> 8;
+       cmd[3] = offset;
+
+       for (; actual < len - 1; actual += 2) {
+               debug("spi-nor: 0x%p => cmd = { 0x%02x 0x%06x }\n",
+                     buf + actual, cmd[0], offset);
+
+               ret = nor->write(nor, cmd, cmd_len, buf + actual, 2);
+               if (ret) {
+                       debug("spi-nor: sst word program failed\n");
+                       break;
+               }
+
+               ret = spi_nor_wait_till_ready(nor, SNOR_READY_WAIT_PROG);
+               if (ret)
+                       break;
+
+               cmd_len = 1;
+               offset += 2;
+       }
+
+       if (!ret)
+               ret = write_disable(nor);
+
+       /* If there is a single trailing byte, write it out */
+       if (!ret && actual != len)
+               ret = sst_byte_write(nor, offset, buf + actual);
+
+ done:
+       return ret;
+}
+
+int sst_write_bp(struct spi_nor *nor, u32 offset, size_t len, const void *buf)
+{
+       struct spi_nor *nor = flash->nor;
+       size_t actual;
+       int ret;
+
+       for (actual = 0; actual < len; actual++) {
+               ret = sst_byte_write(nor, offset, buf + actual);
+               if (ret) {
+                       debug("spi-nor: sst byte program failed\n");
+                       break;
+               }
+               offset++;
+       }
+
+       if (!ret)
+               ret = write_disable(nor);
+
+       return ret;
+}
+#endif
+
+#ifdef CONFIG_SPI_FLASH_MACRONIX
+static int macronix_quad_enable(struct spi_nor *nor)
+{
+       int ret, val;
+
+       val = read_sr(nor);
+       if (val < 0)
+               return val;
+
+       if (val & SR_QUAD_EN_MX)
+               return 0;
+
+       write_enable(nor);
+
+       ret = write_sr(nor, val | SR_QUAD_EN_MX);
+       if (ret < 0)
+               return ret;
+
+       if (spi_nor_wait_till_ready(nor, SNOR_READY_WAIT_PROG))
+               return 1;
+
+       ret = read_sr(nor);
+       if (!(ret > 0 && (ret & SR_QUAD_EN_MX))) {
+               printf("spi-nor: Macronix Quad bit not set\n");
+               return -EINVAL;
+       }
+
+       return 0;
+}
+#endif
+
+#if defined(CONFIG_SPI_FLASH_SPANSION) || defined(CONFIG_SPI_FLASH_WINBOND)
+static int spansion_quad_enable(struct spi_nor *nor)
+{
+       int ret, val;
+
+       val = read_cr(nor);
+       if (val < 0)
+               return val;
+
+       if (val & CR_QUAD_EN_SPAN)
+               return 0;
+
+       write_enable(nor);
+
+       ret = write_sr_cr(nor, val | CR_QUAD_EN_SPAN);
+       if (ret < 0)
+               return ret;
+
+       if (spi_nor_wait_till_ready(nor, SNOR_READY_WAIT_PROG))
+               return 1;
+
+       /* read back and check it */
+       ret = read_cr(nor);
+       if (!(ret > 0 && (ret & CR_QUAD_EN_SPAN))) {
+               printf("spi-nor: Spansion Quad bit not set\n");
+               return -EINVAL;
+       }
+
+       return 0;
+}
+#endif
+
+#ifdef CONFIG_SPI_FLASH_STMICRO
+static int micron_quad_enable(struct spi_nor *nor)
+{
+       int ret, val;
+
+       val = read_evcr(nor);
+       if (val < 0)
+               return val;
+
+       if (!(val & EVCR_QUAD_EN_MICRON))
+               return 0;
+
+       ret = write_evcr(nor, val & ~EVCR_QUAD_EN_MICRON);
+       if (ret < 0)
+               return ret;
+
+       /* read EVCR and check it */
+       ret = read_evcr(nor);
+       if (!(ret > 0 && !(ret & EVCR_QUAD_EN_MICRON))) {
+               printf("spi-nor: Micron EVCR Quad bit not clear\n");
+               return -EINVAL;
+       }
+
+       return ret;
+}
+#endif
+
+static int set_quad_mode(struct spi_nor *nor, const struct spi_nor_info *info)
+{
+       switch (JEDEC_MFR(info)) {
+#ifdef CONFIG_SPI_FLASH_MACRONIX
+       case SNOR_MFR_MACRONIX:
+               return macronix_quad_enable(nor);
+#endif
+#if defined(CONFIG_SPI_FLASH_SPANSION) || defined(CONFIG_SPI_FLASH_WINBOND)
+       case SNOR_MFR_SPANSION:
+       case SNOR_MFR_WINBOND:
+               return spansion_quad_enable(nor);
+#endif
+#ifdef CONFIG_SPI_FLASH_STMICRO
+       case SNOR_MFR_MICRON:
+               return micron_quad_enable(nor);
+#endif
+       default:
+               printf("spi-nor: Need set QEB func for %02x flash\n",
+                      JEDEC_MFR(info));
+               return -1;
+       }
+}
+
+#if CONFIG_IS_ENABLED(OF_CONTROL)
+int spi_nor_decode_fdt(const void *blob, struct spi_nor *nor)
+{
+       fdt_addr_t addr;
+       fdt_size_t size;
+       int node;
+
+       /* If there is no node, do nothing */
+       node = fdtdec_next_compatible(blob, 0, COMPAT_GENERIC_SPI_FLASH);
+       if (node < 0)
+               return 0;
+
+       addr = fdtdec_get_addr_size(blob, node, "memory-map", &size);
+       if (addr == FDT_ADDR_T_NONE) {
+               debug("%s: Cannot decode address\n", __func__);
+               return 0;
+       }
+
+       if (flash->size != size) {
+               debug("%s: Memory map must cover entire device\n", __func__);
+               return -1;
+       }
+       nor->memory_map = map_sysmem(addr, size);
+
+       return 0;
+}
+#endif /* CONFIG_IS_ENABLED(OF_CONTROL) */
+
+static int spi_nor_check(struct spi_nor *nor)
+{
+       if (!nor->read || !nor->write ||
+           !nor->read_reg || !nor->write_reg) {
+               pr_err("spi-nor: please fill all the necessary fields!\n");
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+int spi_nor_scan(struct spi_nor *nor)
+{
+       const struct spi_nor_info *info = NULL;
+       static u8 flash_read_cmd[] = {
+               SNOR_OP_READ,
+               SNOR_OP_READ_FAST,
+               SNOR_OP_READ_1_1_2,
+               SNOR_OP_READ_1_1_4,
+               SNOR_OP_READ_1_1_2_IO,
+               SNOR_OP_READ_1_1_4_IO };
+       u8 cmd;
+       int ret;
+
+       ret = spi_nor_check(nor);
+       if (ret)
+               return ret;
+
+       info = spi_nor_id(nor);
+       if (IS_ERR_OR_NULL(info))
+               return -ENOENT;
+
+       /*
+        * Atmel, SST, Macronix, and others serial NOR tend to power up
+        * with the software protection bits set
+        */
+       if (JEDEC_MFR(info) == SNOR_MFR_ATMEL ||
+           JEDEC_MFR(info) == SNOR_MFR_MACRONIX ||
+           JEDEC_MFR(info) == SNOR_MFR_SST) {
+               write_enable(nor);
+               write_sr(nor, 0);
+       }
+
+       flash->name = info->name;
+
+       if (info->flags & USE_FSR)
+               nor->flags |= SNOR_F_USE_FSR;
+
+       if (info->flags & SST_WRITE)
+               nor->flags |= SNOR_F_SST_WRITE;
+
+       flash->write = spi_nor_write;
+       flash->erase = spi_nor_erase;
+       flash->read = spi_nor_read;
+#if defined(CONFIG_SPI_FLASH_SST)
+       if (nor->flags & SNOR_F_SST_WRITE) {
+               if (nor->mode & SNOR_WRITE_1_1_BYTE)
+                       flash->write = sst_write_bp;
+               else
+                       flash->write = sst_write_wp;
+       }
+#endif
+
+#if defined(CONFIG_SPI_FLASH_STMICRO) || defined(CONFIG_SPI_FLASH_SST)
+       /* NOR protection support for STmicro/Micron chips and similar */
+       if (JEDEC_MFR(info) == SNOR_MFR_MICRON ||
+           JEDEC_MFR(info) == SNOR_MFR_SST) {
+               nor->flash_lock = stm_lock;
+               nor->flash_unlock = stm_unlock;
+               nor->flash_is_locked = stm_is_locked;
+       }
+#endif
+
+       if (flash->flash_lock && flash->flash_unlock && flash->flash_is_locked) 
{
+               flash->flash_lock = spi_nor_lock;
+               flash->flash_unlock = spi_nor_unlock;
+               flash->flash_is_locked = spi_nor_is_locked;
+       }
+
+       /* Compute the flash size */
+       nor->shift = (nor->dual & SNOR_DUAL_PARALLEL) ? 1 : 0;
+       nor->page_size = info->page_size;
+       /*
+        * The Spansion S25FL032P and S25FL064P have 256b pages, yet use the
+        * 0x4d00 Extended JEDEC code. The rest of the Spansion flashes with
+        * the 0x4d00 Extended JEDEC code have 512b pages. All of the others
+        * have 256b pages.
+        */
+       if (JEDEC_EXT(info) == 0x4d00) {
+               if ((JEDEC_ID(info) != 0x0215) &&
+                   (JEDEC_ID(info) != 0x0216))
+                       nor->page_size = 512;
+       }
+       nor->page_size <<= nor->shift;
+       flash->sector_size = info->sector_size << nor->shift;
+       flash->size = flash->sector_size * info->n_sectors << nor->shift;
+#ifdef CONFIG_SF_DUAL_FLASH
+       if (nor->dual & SNOR_DUAL_STACKED)
+               flash->size <<= 1;
+#endif
+
+#ifdef CONFIG_MTD_SPI_NOR_USE_4K_SECTORS
+       /* prefer "small sector" erase if possible */
+       if (info->flags & SECT_4K) {
+               nor->erase_opcode = SNOR_OP_BE_4K;
+               nor->erase_size = 4096 << nor->shift;
+       } else if (info->flags & SECT_4K_PMC) {
+               nor->erase_opcode = SNOR_OP_BE_4K_PMC;
+               nor->erase_size = 4096;
+       } else
+#endif
+       {
+               nor->erase_opcode = SNOR_OP_SE;
+               nor->erase_size = flash->sector_size;
+       }
+
+       /* Now erase size becomes valid sector size */
+       flash->sector_size = nor->erase_size;
+
+       /* Look for the fastest read cmd */
+       cmd = fls(info->flash_read & nor->read_mode);
+       if (cmd) {
+               cmd = flash_read_cmd[cmd - 1];
+               nor->read_opcode = cmd;
+       } else {
+               /* Go for default supported read cmd */
+               nor->read_opcode = SNOR_OP_READ_FAST;
+       }
+
+       /* Not require to look for fastest only two write cmds yet */
+       if (info->flags & SNOR_WRITE_QUAD && nor->mode & SNOR_WRITE_1_1_4)
+               nor->program_opcode = SNOR_OP_QPP;
+       else
+               /* Go for default supported write cmd */
+               nor->program_opcode = SNOR_OP_PP;
+
+       /* Set the quad enable bit - only for quad commands */
+       if ((nor->read_opcode == SNOR_OP_READ_1_1_4) ||
+           (nor->read_opcode == SNOR_OP_READ_1_1_4_IO) ||
+           (nor->program_opcode == SNOR_OP_QPP)) {
+               ret = set_quad_mode(nor, info);
+               if (ret) {
+                       debug("spi-nor: quad mode not supported for %02x\n",
+                             JEDEC_MFR(info));
+                       return ret;
+               }
+       }
+
+       /* read_dummy: dummy byte is determined based on the
+        * dummy cycles of a particular command.
+        * Fast commands - read_dummy = dummy_cycles/8
+        * I/O commands- read_dummy = (dummy_cycles * no.of lines)/8
+        * For I/O commands except cmd[0] everything goes on no.of lines
+        * based on particular command but incase of fast commands except
+        * data all go on single line irrespective of command.
+        */
+       switch (nor->read_opcode) {
+       case SNOR_OP_READ_1_1_4_IO:
+               nor->read_dummy = 2;
+               break;
+       case SNOR_OP_READ:
+               nor->read_dummy = 0;
+               break;
+       default:
+               nor->read_dummy = 1;
+       }
+
+       /* Configure the BAR - discover bank cmds and read current bank */
+#ifdef CONFIG_SPI_FLASH_BAR
+       ret = spi_nor_read_bar(nor, info);
+       if (ret < 0)
+               return ret;
+#endif
+
+#if CONFIG_IS_ENABLED(OF_CONTROL)
+       ret = spi_nor_decode_fdt(gd->fdt_blob, nor);
+       if (ret) {
+               debug("spi-nor: FDT decode error\n");
+               return -EINVAL;
+       }
+#endif
+
+#ifndef CONFIG_SPL_BUILD
+       printf("spi-nor: detected %s with page size ", flash->name);
+       print_size(nor->page_size, ", erase size ");
+       print_size(nor->erase_size, ", total ");
+       print_size(flash->size, "");
+       if (nor->memory_map)
+               printf(", mapped at %p", nor->memory_map);
+       puts("\n");
+#endif
+
+#ifndef CONFIG_SPI_FLASH_BAR
+       if (((nor->dual == SNOR_DUAL_SINGLE) &&
+            (flash->size > SNOR_16MB_BOUN)) ||
+            ((nor->dual > SNOR_DUAL_SINGLE) &&
+            (flash->size > SNOR_16MB_BOUN << 1))) {
+               puts("spi-nor: Warning - Only lower 16MiB accessible,");
+               puts(" Full access #define CONFIG_SPI_FLASH_BAR\n");
+       }
+#endif
+
+       return ret;
+}
diff --git a/include/linux/err.h b/include/linux/err.h
index 5b3c8bc..1bba498 100644
--- a/include/linux/err.h
+++ b/include/linux/err.h
@@ -36,6 +36,11 @@ static inline long IS_ERR(const void *ptr)
        return IS_ERR_VALUE((unsigned long)ptr);
 }
 
+static inline bool IS_ERR_OR_NULL(const void *ptr)
+{
+       return !ptr || IS_ERR_VALUE((unsigned long)ptr);
+}
+
 /**
  * ERR_CAST - Explicitly cast an error-valued pointer to another pointer type
  * @ptr: The pointer to cast.
diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h
new file mode 100644
index 0000000..4f2e8bc
--- /dev/null
+++ b/include/linux/mtd/spi-nor.h
@@ -0,0 +1,253 @@
+/*
+ * SPI NOR Core header file.
+ *
+ * Copyright (C) 2016 Jagan Teki <jt...@openedev.com>
+ *
+ * SPDX-License-Identifier:    GPL-2.0+
+ */
+
+#ifndef __MTD_SPI_NOR_H
+#define __MTD_SPI_NOR_H
+
+#include <common.h>
+
+/*
+ * Manufacturer IDs
+ *
+ * The first byte returned from the flash after sending opcode SPINOR_OP_RDID.
+ * Sometimes these are the same as CFI IDs, but sometimes they aren't.
+ */
+#define SNOR_MFR_ATMEL         0x1f
+#define SNOR_MFR_MACRONIX      0xc2
+#define SNOR_MFR_MICRON        0x20    /* ST Micro <--> Micron */
+#define SNOR_MFR_SPANSION      0x01
+#define SNOR_MFR_SST           0xbf
+#define SNOR_MFR_WINBOND       0xef
+
+/**
+ * SPI NOR opcodes.
+ *
+ * Note on opcode nomenclature: some opcodes have a format like
+ * SNOR_OP_FUNCTION{4,}_x_y_z. The numbers x, y, and z stand for the number
+ * of I/O lines used for the opcode, address, and data (respectively). The
+ * FUNCTION has an optional suffix of '4', to represent an opcode which
+ * requires a 4-byte (32-bit) address.
+ */
+#define SNOR_OP_WRDI           0x04    /* Write disable */
+#define SNOR_OP_WREN           0x06    /* Write enable */
+#define SNOR_OP_RDSR           0x05    /* Read status register */
+#define SNOR_OP_WRSR           0x01    /* Write status register 1 byte */
+#define SNOR_OP_READ           0x03    /* Read data bytes (low frequency) */
+#define SNOR_OP_READ_FAST      0x0b    /* Read data bytes (high frequency) */
+#define SNOR_OP_READ_1_1_2     0x3b    /* Read data bytes (Dual SPI) */
+#define SNOR_OP_READ_1_1_2_IO  0xbb    /* Read data bytes (Dual IO SPI) */
+#define SNOR_OP_READ_1_1_4     0x6b    /* Read data bytes (Quad SPI) */
+#define SNOR_OP_READ_1_1_4_IO  0xeb    /* Read data bytes (Quad IO SPI) */
+#define SNOR_OP_BRWR           0x17    /* Bank register write */
+#define SNOR_OP_BRRD           0x16    /* Bank register read */
+#define SNOR_OP_WREAR          0xC5    /* Write extended address register */
+#define SNOR_OP_RDEAR          0xC8    /* Read extended address register */
+#define SNOR_OP_PP             0x02    /* Page program (up to 256 bytes) */
+#define SNOR_OP_QPP            0x32    /* Quad Page program */
+#define SNOR_OP_BE_4K          0x20    /* Erase 4KiB block */
+#define SNOR_OP_BE_4K_PMC      0xd7    /* Erase 4KiB block on PMC chips */
+#define SNOR_OP_BE_32K         0x52    /* Erase 32KiB block */
+#define SPINOR_OP_CHIP_ERASE   0xc7    /* Erase whole flash chip */
+#define SNOR_OP_SE             0xd8    /* Sector erase (usually 64KiB) */
+#define SNOR_OP_RDID           0x9f    /* Read JEDEC ID */
+#define SNOR_OP_RDCR           0x35    /* Read configuration register */
+#define SNOR_OP_RDFSR          0x70    /* Read flag status register */
+
+/* Used for SST flashes only. */
+#define SNOR_OP_BP             0x02    /* Byte program */
+#define SNOR_OP_AAI_WP         0xad    /* Auto addr increment word program */
+
+/* Used for Micron flashes only. */
+#define SPINOR_OP_RD_EVCR      0x65    /* Read EVCR register */
+#define SPINOR_OP_WD_EVCR      0x61    /* Write EVCR register */
+
+/* Status Register bits. */
+#define SR_WIP                 BIT(0)  /* Write in progress */
+#define SR_WEL                 BIT(1)  /* Write enable latch */
+
+/* meaning of other SR_* bits may differ between vendors */
+#define SR_BP0                 BIT(2)  /* Block protect 0 */
+#define SR_BP1                 BIT(3)  /* Block protect 1 */
+#define SR_BP2                 BIT(4)  /* Block protect 2 */
+#define SR_SRWD                BIT(7)  /* SR write protect */
+
+#define SR_QUAD_EN_MX          BIT(6)  /* Macronix Quad I/O */
+
+/* Enhanced Volatile Configuration Register bits */
+#define EVCR_QUAD_EN_MICRON    BIT(7)  /* Micron Quad I/O */
+
+/* Flag Status Register bits */
+#define FSR_READY              BIT(7)
+
+/* Configuration Register bits. */
+#define CR_QUAD_EN_SPAN        BIT(1) /* Spansion/Winbond Quad I/O */
+
+/* Flash timeout values */
+#define SNOR_READY_WAIT_PROG   (2 * CONFIG_SYS_HZ)
+#define SNOR_READY_WAIT_ERASE  (5 * CONFIG_SYS_HZ)
+#define SNOR_MAX_CMD_SIZE      4       /* opcode + 3-byte address */
+#define SNOR_16MB_BOUN         0x1000000
+
+enum snor_dual {
+       SNOR_DUAL_SINGLE        = 0,
+       SNOR_DUAL_STACKED       = BIT(0),
+       SNOR_DUAL_PARALLEL      = BIT(1),
+};
+
+enum snor_option_flags {
+       SNOR_F_SST_WRITE        = BIT(0),
+       SNOR_F_USE_FSR          = BIT(1),
+       SNOR_F_U_PAGE           = BIT(1),
+};
+
+enum write_mode {
+       SNOR_WRITE_1_1_BYTE     = BIT(0),
+       SNOR_WRITE_1_1_4        = BIT(1),
+};
+
+enum read_mode {
+       SNOR_READ               = BIT(0),
+       SNOR_READ_FAST          = BIT(1),
+       SNOR_READ_1_1_2         = BIT(2),
+       SNOR_READ_1_1_4         = BIT(3),
+       SNOR_READ_1_1_2_IO      = BIT(4),
+       SNOR_READ_1_1_4_IO      = BIT(5),
+};
+
+#define SNOR_READ_BASE         (SNOR_READ | SNOR_READ_FAST)
+#define SNOR_READ_FULL         (SNOR_READ_BASE | SNOR_READ_1_1_2 | \
+                                SNOR_READ_1_1_4 | SNOR_READ_1_1_2_IO | \
+                                SNOR_READ_1_1_4_IO)
+
+#define JEDEC_MFR(info)        ((info)->id[0])
+#define JEDEC_ID(info)         (((info)->id[1]) << 8 | ((info)->id[2]))
+#define JEDEC_EXT(info)        (((info)->id[3]) << 8 | ((info)->id[4]))
+#define SPI_NOR_MAX_ID_LEN     6
+
+struct spi_nor_info {
+       char            *name;
+
+       /*
+        * This array stores the ID bytes.
+        * The first three bytes are the JEDIC ID.
+        * JEDEC ID zero means "no ID" (mostly older chips).
+        */
+       u8              id[SPI_NOR_MAX_ID_LEN];
+       u8              id_len;
+
+       /* The size listed here is what works with SNOR_OP_SE, which isn't
+        * necessarily called a "sector" by the vendor.
+        */
+       unsigned        sector_size;
+       u16             n_sectors;
+
+       u16             page_size;
+       u16             addr_width;
+
+       /* Enum list for read modes */
+       enum read_mode  flash_read;
+
+       u16             flags;
+#define SECT_4K                BIT(0)  /* SNOR_OP_BE_4K works uniformly */
+#define SECT_32K               BIT(1)  /* SNOR_OP_BE_32K works uniformly */
+#define SPI_NOR_NO_ERASE       BIT(2)  /* No erase command needed */
+#define SST_WRITE              BIT(3)  /* use SST byte programming */
+#define SPI_NOR_NO_FR          BIT(4)  /* Can't do fastread */
+#define SECT_4K_PMC            BIT(5)  /* SNOR_OP_BE_4K_PMC works uniformly */
+#define USE_FSR                BIT(6)  /* use flag status register */
+#define SNOR_WRITE_QUAD        BIT(7)  /* Flash supports Quad Read */
+};
+
+extern const struct spi_nor_info spi_nor_ids[];
+
+/**
+ * struct spi_nor - Structure for defining a the SPI NOR layer
+ *
+ * @mtd:               point to a mtd_info structure
+ * @name:              name of the SPI NOR device
+ * @page_size:         the page size of the SPI NOR
+ * @erase_opcode:      the opcode for erasing a sector
+ * @read_opcode:       the read opcode
+ * @read_dummy:        the dummy bytes needed by the read operation
+ * @program_opcode:    the program opcode
+ * @bar_read_opcode:   the read opcode for bank/extended address registers
+ * @bar_program_opcode: the program opcode for bank/extended address registers
+ * @bank_curr:         indicates current flash bank
+ * @dual:              indicates dual flash memories - dual stacked, parallel
+ * @shift:             flash shift useful in dual parallel
+ * @max_write_size:    If non-zero, the maximum number of bytes which can
+ *                     be written at once, excluding command bytes.
+ * @flags:             flag options for the current SPI-NOR (SNOR_F_*)
+ * @mode:              write mode or any other mode bits.
+ * @read_mode:         read mode.
+ * @cmd_buf:           used by the write_reg
+ * @read_reg:          [DRIVER-SPECIFIC] read out the register
+ * @write_reg:         [DRIVER-SPECIFIC] write data to the register
+ * @read_mmap:         [DRIVER-SPECIFIC] read data from the mmapped SPI NOR
+ * @read:              [DRIVER-SPECIFIC] read data from the SPI NOR
+ * @write:             [DRIVER-SPECIFIC] write data to the SPI NOR
+ * @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
+ * @memory_map:        address of read-only SPI NOR access
+ * @priv:              the private data
+ */
+struct spi_nor {
+       struct mtd_info         *mtd;
+       const char              *name;
+       u32                     page_size;
+       u8                      erase_opcode;
+       u8                      read_opcode;
+       u8                      read_dummy;
+       u8                      program_opcode;
+#ifdef CONFIG_SPI_FLASH_BAR
+       u8                      bar_read_opcode;
+       u8                      bar_program_opcode;
+       u8                      bank_curr;
+#endif
+       u8                      dual;
+       u8                      shift;
+       u32                     max_write_size;
+       u32                     flags;
+       u8                      mode;
+       u8                      read_mode;
+       u8                      cmd_buf[SNOR_MAX_CMD_SIZE];
+
+       int (*read_reg)(struct spi_nor *nor, u8 cmd, u8 *val, int len);
+       int (*write_reg)(struct spi_nor *nor, u8 cmd, u8 *data, int len);
+
+       int (*read_mmap)(struct spi_nor *nor, void *data, void *offset,
+                       size_t len);
+       int (*read)(struct spi_nor *nor, const u8 *opcode, size_t cmd_len,
+                       void *data, size_t data_len);
+       int (*write)(struct spi_nor *nor, const u8 *cmd, size_t cmd_len,
+                       const void *data, size_t data_len);
+
+       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);
+
+       void *memory_map;
+       void *priv;
+};
+
+/**
+ * spi_nor_scan() - scan the SPI NOR
+ * @nor:       the spi_nor structure
+ *
+ * The drivers can use this fuction to scan the SPI NOR.
+ * In the scanning, it will try to get all the necessary information to
+ * fill the mtd_info{} and the spi_nor{}.
+ *
+ * The chip type name can be provided through the @name parameter.
+ *
+ * Return: 0 for success, others for failure.
+ */
+int spi_nor_scan(struct spi_nor *nor);
+
+#endif /* __MTD_SPI_NOR_H */
-- 
1.9.1

_______________________________________________
U-Boot mailing list
U-Boot@lists.denx.de
http://lists.denx.de/mailman/listinfo/u-boot

Reply via email to