[U-Boot] [PATCH v6 03/76] mtd: Add SPI-NOR core support

Jagan Teki
2016-02-14T20:48:02Z

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@chromium.org>
Cc: Bin Meng <bmeng.cn@gmail.com>
Cc: Mugunthan V N <mugunthanvnm@ti.com>
Cc: Michal Simek <michal.simek@xilinx.com>
Cc: Siva Durga Prasad Paladugu <sivadur@xilinx.com>
Signed-off-by: Jagan Teki <jteki@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@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@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..fb32c9f
--- /dev/null
+++ b/include/linux/mtd/spi-nor.h
@@ -0,0 +1,253 @@
+/*
+ * SPI NOR Core header file.
+ *
+ * Copyright (C) 2016 Jagan Teki <jteki@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
+ * @bar_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