[U-Boot] [PATCH v7 03/87] mtd: Add SPI-NOR core support

Jagan Teki jteki at openedev.com
Tue Mar 22 08:37:15 CET 2016


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 <sjg at chromium.org>
Cc: Bin Meng <bmeng.cn at gmail.com>
Cc: York Sun <york.sun at nxp.com>
Cc: Mugunthan V N <mugunthanvnm at ti.com>
Cc: Michal Simek <michal.simek at xilinx.com>
Cc: Siva Durga Prasad Paladugu <sivadur at xilinx.com>
Signed-off-by: Jagan Teki <jteki at 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 <jteki at 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 <jteki at 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 <jteki at 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



More information about the U-Boot mailing list