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