[U-Boot] [PATCH v5 00/32] mtd: Add SPI-NOR core support

Compared to previous patch series this series adds spi-nor core with spi-nor controller drivers are of "mtd uclass"
Why this framework:
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 -------------------------------
SPI-NOR with MTD:
------------------------------ cmd/sf.c ------------------------------ MTD core ------------------------------ spi-nor.c ------------------------------- m25p80.c spi nor drivers ------------------------------- spi-uclass SPI NOR chip ------------------------------- spi drivers ------------------------------- SPI NOR chip -------------------------------
drivers/mtd/spi-nor/spi-nor.c: spi-nor core drivers/mtd/spi-nor/m25p80.c: mtd uclass driver which is an interface layer b/w spi-nor core drivers/spi drivers/mtd/spi-nor/fsl_qspi.c: spi-nor controller driver(mtd uclass)
Changes for v5: - newly designed changes
Testing: $ git clone git://git.denx.de/u-boot-spi.git $ cd u-boot-spi $ git checkout -b spi-nor origin/spi-nor
Jagan Teki (32): mtd: Add m25p80 driver mtd: Add Kconfig entry for MTD_M25P80 mtd: Add SPI-NOR core support doc: device-tree-bindings: jedec,spi-nor mtd: spi-nor: Add Kconfig entry for MTD_SPI_NOR mtd: spi-nor: Add kconfig for MTD_SPI_NOR_USE_4K_SECTORS mtd: spi-nor: Add MTD support mtd: spi-nor: Add spi_nor support in m25p80 mtd: spi-nor: Add dm spi-nor probing mtd: spi-nor: Add spi_flash_probe for mtd-dm-spi-nor cmd: sf: Add mtd_info for mtd-dm-spi-nor code mtd: spi-nor: m25p80: Add spi_nor support for non-dm spi_flash: Use mtd_info operation for non-dm mtd: spi-nor: Move spi_read_then_write to spi layer spi: Rename spi_read_then_write to spi_write_then_read spi: Drop mode_rx spi: Drop SPI_RX_FAST mtd: spi-nor: Rename SPI_FLASH_BAR to SPI_NOR_BAR mtd: spi-nor: Add Kconfig entry for SPI_NOR_BAR mtd: spi-nor: Copy spl files from drivers/mtd/spi mtd: spi-nor: spl: Follow ascending order of include headers mtd: spi-nor: fsl_espi_spl: Use mtd_info mtd: spi-nor: spi_spl_load: Use mtd_info mtd: spi-nor: Add flash vendor Kconfig entries arm: zynq: Kconfig: Select MTD uclass arm: zynq: Kconfig: Drop DM_SPI_FLASH defconfigs: zynq_microzed: Drop CONFIG_SPI_FLASH defconfig: zynq_microzed: Enable CONFIG_MTD_M25P80 defconfig: zynq_microzed: Enable CONFIG_MTD_SPI_NOR spl: Add CONFIG_SPL_SPI_NOR_SUPPORT configs: zynq: Use CONFIG_SPL_SPI_NOR_SUPPORT configs: zynq: Use CONFIG_SPL_MTD_SUPPORT
Makefile | 1 + arch/arm/Kconfig | 2 +- cmd/sf.c | 38 +- configs/zynq_microzed_defconfig | 3 +- doc/device-tree-bindings/mtd/jedec,spi-nor.txt | 78 ++ drivers/Makefile | 1 + drivers/mtd/Kconfig | 2 + drivers/mtd/spi-nor/Kconfig | 99 ++ drivers/mtd/spi-nor/Makefile | 18 + drivers/mtd/spi-nor/fsl_espi_spl.c | 90 ++ drivers/mtd/spi-nor/m25p80.c | 332 +++++++ drivers/mtd/spi-nor/spi-nor-ids.c | 276 ++++++ drivers/mtd/spi-nor/spi-nor-probe.c | 45 + drivers/mtd/spi-nor/spi-nor.c | 1141 ++++++++++++++++++++++++ drivers/mtd/spi-nor/spi_spl_load.c | 89 ++ drivers/spi/ich.c | 6 +- drivers/spi/spi-uclass.c | 35 +- drivers/spi/spi.c | 24 + drivers/spi/ti_qspi.c | 6 +- include/configs/zynq-common.h | 3 +- include/linux/err.h | 5 + include/linux/mtd/spi-nor.h | 251 ++++++ include/spi.h | 33 +- include/spi_flash.h | 79 +- 24 files changed, 2581 insertions(+), 76 deletions(-) create mode 100644 doc/device-tree-bindings/mtd/jedec,spi-nor.txt create mode 100644 drivers/mtd/spi-nor/Kconfig create mode 100644 drivers/mtd/spi-nor/Makefile create mode 100644 drivers/mtd/spi-nor/fsl_espi_spl.c create mode 100644 drivers/mtd/spi-nor/m25p80.c create mode 100644 drivers/mtd/spi-nor/spi-nor-ids.c create mode 100644 drivers/mtd/spi-nor/spi-nor-probe.c create mode 100644 drivers/mtd/spi-nor/spi-nor.c create mode 100644 drivers/mtd/spi-nor/spi_spl_load.c create mode 100644 include/linux/mtd/spi-nor.h

This is MTD SPI-NOR driver for ST M25Pxx (and similar) serial flash chips which is written as MTD_UCLASS.
More features will be adding on further patches.
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 --- Makefile | 1 + drivers/mtd/spi-nor/Makefile | 6 ++++++ drivers/mtd/spi-nor/m25p80.c | 37 +++++++++++++++++++++++++++++++++++++ 3 files changed, 44 insertions(+) create mode 100644 drivers/mtd/spi-nor/Makefile create mode 100644 drivers/mtd/spi-nor/m25p80.c
diff --git a/Makefile b/Makefile index 430dd4f..f299a24 100644 --- a/Makefile +++ b/Makefile @@ -637,6 +637,7 @@ libs-$(CONFIG_CMD_NAND) += drivers/mtd/nand/ libs-y += drivers/mtd/onenand/ libs-$(CONFIG_CMD_UBI) += drivers/mtd/ubi/ libs-y += drivers/mtd/spi/ +libs-y += drivers/mtd/spi-nor/ libs-y += drivers/net/ libs-y += drivers/net/phy/ libs-y += drivers/pci/ diff --git a/drivers/mtd/spi-nor/Makefile b/drivers/mtd/spi-nor/Makefile new file mode 100644 index 0000000..a4c19e3 --- /dev/null +++ b/drivers/mtd/spi-nor/Makefile @@ -0,0 +1,6 @@ +# +# Copyright (C) 2016 Jagan Teki jteki@openedev.com +# +# SPDX-License-Identifier: GPL-2.0+ + +obj-$(CONFIG_MTD_M25P80) += m25p80.o diff --git a/drivers/mtd/spi-nor/m25p80.c b/drivers/mtd/spi-nor/m25p80.c new file mode 100644 index 0000000..833a9c3 --- /dev/null +++ b/drivers/mtd/spi-nor/m25p80.c @@ -0,0 +1,37 @@ +/* + * MTD SPI-NOR driver for ST M25Pxx (and similar) serial flash chips + * + * Copyright (C) 2016 Jagan Teki jteki@openedev.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <spi.h> +#include <linux/mtd/mtd.h> + +static int m25p_probe(struct udevice *dev) +{ + struct spi_slave *spi = dev_get_parent_priv(dev); + struct mtd_info *mtd = dev_get_uclass_priv(dev); + + return 0; +} + +static const struct udevice_id m25p_ids[] = { + /* + * Generic compatibility for SPI NOR that can be identified by the + * JEDEC READ ID opcode (0x9F). Use this, if possible. + */ + { .compatible = "jedec,spi-nor" }, + { } +}; + +U_BOOT_DRIVER(m25p80) = { + .name = "m25p80", + .id = UCLASS_MTD, + .of_match = m25p_ids, + .probe = m25p_probe, +};

Added Kconfig entry for MTD_M25P80
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 --- drivers/mtd/spi-nor/Kconfig | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 drivers/mtd/spi-nor/Kconfig
diff --git a/drivers/mtd/spi-nor/Kconfig b/drivers/mtd/spi-nor/Kconfig new file mode 100644 index 0000000..d32486c --- /dev/null +++ b/drivers/mtd/spi-nor/Kconfig @@ -0,0 +1,15 @@ +config MTD_M25P80 + tristate "Support most SPI Flash chips (AT26DF, M25P, W25X, ...)" + help + This enables access to most modern SPI flash chips, used for + program and data storage. Series supported include Atmel AT26DF, + Spansion S25SL, SST 25VF, ST M25P, and Winbond W25X. Other chips + are supported as well. See the driver source for the current list, + or to add other chips. + + Note that the original DataFlash chips (AT45 series, not AT26DF), + need an entirely different driver. + + Set up your spi devices with the right board-specific platform data, + if you want to specify device partitioning or to use a device which + doesn't support the JEDEC ID instruction.

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 --- 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 | 251 +++++++++ 6 files changed, 1623 insertions(+) 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/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..664c9ae --- /dev/null +++ b/include/linux/mtd/spi-nor.h @@ -0,0 +1,251 @@ +/* + * 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 <linux/bitops.h> +#include <linux/types.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_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 SPI_NOR_NO_ERASE BIT(1) /* No erase command needed */ +#define SST_WRITE BIT(2) /* use SST byte programming */ +#define SPI_NOR_NO_FR BIT(3) /* Can't do fastread */ +#define SECT_4K_PMC BIT(4) /* SNOR_OP_BE_4K_PMC works uniformly */ +#define USE_FSR BIT(5) /* use flag status register */ +#define SNOR_WRITE_QUAD BIT(6) /* 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 */

Since m25p80 follows similar naming convention as Linux, hence added jedec, spi-nor device tree bindings from Linux.
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/device-tree-bindings/mtd/jedec,spi-nor.txt | 78 ++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 doc/device-tree-bindings/mtd/jedec,spi-nor.txt
diff --git a/doc/device-tree-bindings/mtd/jedec,spi-nor.txt b/doc/device-tree-bindings/mtd/jedec,spi-nor.txt new file mode 100644 index 0000000..2c91c03 --- /dev/null +++ b/doc/device-tree-bindings/mtd/jedec,spi-nor.txt @@ -0,0 +1,78 @@ +* SPI NOR flash: ST M25Pxx (and similar) serial flash chips + +Required properties: +- #address-cells, #size-cells : Must be present if the device has sub-nodes + representing partitions. +- compatible : May include a device-specific string consisting of the + manufacturer and name of the chip. A list of supported chip + names follows. + Must also include "jedec,spi-nor" for any SPI NOR flash that can + be identified by the JEDEC READ ID opcode (0x9F). + + Supported chip names: + at25df321a + at25df641 + at26df081a + mr25h256 + mx25l4005a + mx25l1606e + mx25l6405d + mx25l12805d + mx25l25635e + n25q064 + n25q128a11 + n25q128a13 + n25q512a + s25fl256s1 + s25fl512s + s25sl12801 + s25fl008k + s25fl064k + sst25vf040b + m25p40 + m25p80 + m25p16 + m25p32 + m25p64 + m25p128 + w25x80 + w25x32 + w25q32 + w25q32dw + w25q80bl + w25q128 + w25q256 + + The following chip names have been used historically to + designate quirky versions of flash chips that do not support the + JEDEC READ ID opcode (0x9F): + m25p05-nonjedec + m25p10-nonjedec + m25p20-nonjedec + m25p40-nonjedec + m25p80-nonjedec + m25p16-nonjedec + m25p32-nonjedec + m25p64-nonjedec + m25p128-nonjedec + +- reg : Chip-Select number +- spi-max-frequency : Maximum frequency of the SPI bus the chip can operate at + +Optional properties: +- m25p,fast-read : Use the "fast read" opcode to read data from the chip instead + of the usual "read" opcode. This opcode is not supported by + all chips and support for it can not be detected at runtime. + Refer to your chips' datasheet to check if this is supported + by your chip. + +Example: + + flash: m25p80@0 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "spansion,m25p80", "jedec,spi-nor"; + reg = <0>; + spi-max-frequency = <40000000>; + m25p,fast-read; + };

Added kconfig entry for MTD_SPI_NOR
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 --- drivers/mtd/spi-nor/Kconfig | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+)
diff --git a/drivers/mtd/spi-nor/Kconfig b/drivers/mtd/spi-nor/Kconfig index d32486c..f0ea9f9 100644 --- a/drivers/mtd/spi-nor/Kconfig +++ b/drivers/mtd/spi-nor/Kconfig @@ -1,3 +1,19 @@ +menuconfig MTD_SPI_NOR + tristate "SPI-NOR device support" + help + This is the core SPI NOR framework which can be used to interact SPI-NOR + to SPI driver interface layer and the SPI-NOR controller driver. + + Unlike normal/generic spi controllers, they are few controllers which are + exclusively used to connect SPI-NOR devices, called SPI-NOR controllers. + So technically these controllers shouldn't reside at drivers/spi as these + may effect the generic SPI bus functionalities, so this SPI-NOR core acts + as a common core framework between the generic SPI controller drivers vs + SPI-NOR controller drivers for SPI-NOR device access. Note that from SPI-NOR + core to SPI drivers there should be an interface layer. + +if MTD_SPI_NOR + config MTD_M25P80 tristate "Support most SPI Flash chips (AT26DF, M25P, W25X, ...)" help @@ -13,3 +29,5 @@ config MTD_M25P80 Set up your spi devices with the right board-specific platform data, if you want to specify device partitioning or to use a device which doesn't support the JEDEC ID instruction. + +endif # MTD_SPI_NOR

Added kconfig entry for MTD_SPI_NOR_USE_4K_SECTORS.
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 --- drivers/mtd/spi-nor/Kconfig | 14 ++++++++++++++ 1 file changed, 14 insertions(+)
diff --git a/drivers/mtd/spi-nor/Kconfig b/drivers/mtd/spi-nor/Kconfig index f0ea9f9..374cdcb 100644 --- a/drivers/mtd/spi-nor/Kconfig +++ b/drivers/mtd/spi-nor/Kconfig @@ -30,4 +30,18 @@ config MTD_M25P80 if you want to specify device partitioning or to use a device which doesn't support the JEDEC ID instruction.
+config MTD_SPI_NOR_USE_4K_SECTORS + bool "Use small 4096 B erase sectors" + default y + help + Many flash memories support erasing small (4096 B) sectors. Depending + on the usage this feature may provide performance gain in comparison + to erasing whole blocks (32/64 KiB). + Changing a small part of the flash's contents is usually faster with + small sectors. On the other hand erasing should be faster when using + 64 KiB block instead of 16 × 4 KiB sectors. + + Please note that some tools/drivers/filesystems may not work with + 4096 B erase size (e.g. UBIFS requires 15 KiB as a minimum). + endif # MTD_SPI_NOR

This patch adds mtd_info support to spi-nor core instead of using legacy spi_flash{}.
SPI-NOR with MTD:
------------------------------ cmd_sf.c ------------------------------ MTD core ------------------------------ 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 --- drivers/mtd/spi-nor/spi-nor.c | 273 +++++++++++++++++++++++++----------------- 1 file changed, 165 insertions(+), 108 deletions(-)
diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c index f142ae4..3b42b69 100644 --- a/drivers/mtd/spi-nor/spi-nor.c +++ b/drivers/mtd/spi-nor/spi-nor.c @@ -7,6 +7,7 @@ */
#include <common.h> +#include <div64.h> #include <dm.h> #include <errno.h> #include <malloc.h> @@ -14,6 +15,7 @@
#include <linux/math64.h> #include <linux/log2.h> +#include <linux/mtd/mtd.h> #include <linux/mtd/spi-nor.h>
DECLARE_GLOBAL_DATA_PTR; @@ -209,10 +211,11 @@ bar_end:
static int spi_nor_read_bar(struct spi_nor *nor, const struct spi_nor_info *info) { + struct mtd_info *mtd = nor->mtd; u8 curr_bank = 0; int ret;
- if (flash->size <= SNOR_16MB_BOUN) + if (mtd->size <= SNOR_16MB_BOUN) goto bar_end;
switch (JEDEC_MFR(info)) { @@ -240,12 +243,12 @@ bar_end: #ifdef CONFIG_SF_DUAL_FLASH static void spi_nor_dual(struct spi_nor *nor, u32 *addr) { - struct spi_flash *flash = nor->flash; + struct mtd_info *mtd = nor->mtd;
switch (nor->dual) { case SNOR_DUAL_STACKED: - if (*addr >= (flash->size >> 1)) { - *addr -= flash->size >> 1; + if (*addr >= (mtd->size >> 1)) { + *addr -= mtd->size >> 1; nor->flags |= SNOR_F_U_PAGE; } else { nor->flags &= ~SNOR_F_U_PAGE; @@ -263,8 +266,9 @@ static void spi_nor_dual(struct spi_nor *nor, u32 *addr)
#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) + uint64_t *len) { + struct mtd_info *mtd = nor->mtd; u8 mask = SR_BP2 | SR_BP1 | SR_BP0; int shift = ffs(mask) - 1; int pow; @@ -275,18 +279,19 @@ static void stm_get_locked_range(struct spi_nor *nor, u8 sr, loff_t *ofs, *len = 0; } else { pow = ((sr & mask) ^ mask) >> shift; - *len = flash->size >> pow; - *ofs = flash->size - *len; + *len = mtd->size >> pow; + *ofs = mtd->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) +static int stm_is_locked_sr(struct spi_nor *nor, loff_t ofs, uint64_t len, + u8 sr) { loff_t lock_offs; - u32 lock_len; + uint64_t lock_len;
stm_get_locked_range(nor, sr, &lock_offs, &lock_len);
@@ -294,24 +299,6 @@ static int stm_is_locked_sr(struct spi_nor *nor, u32 ofs, u32 len, u8 sr) }
/* - * 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: @@ -334,9 +321,10 @@ static int stm_is_locked(struct spi_nor *nor, u32 ofs, size_t len) * * Returns negative on errors, 0 on success. */ -static int stm_lock(struct spi_nor *nor, u32 ofs, size_t len) +static int stm_lock(struct spi_nor *nor, loff_t ofs, uint64_t len) { - u8 status_old, status_new; + struct mtd_info *mtd = nor->mtd; + int status_old, status_new; u8 mask = SR_BP2 | SR_BP1 | SR_BP0; u8 shift = ffs(mask) - 1, pow, val;
@@ -345,12 +333,12 @@ static int stm_lock(struct spi_nor *nor, u32 ofs, size_t len) return status_old;
/* SPI NOR always locks to the end */ - if (ofs + len != flash->size) { + if (ofs + len != mtd->size) { /* Does combined region extend to end? */ - if (!stm_is_locked_sr(nor, ofs + len, flash->size - ofs - len, + if (!stm_is_locked_sr(nor, ofs + len, mtd->size - ofs - len, status_old)) return -EINVAL; - len = flash->size - ofs; + len = mtd->size - ofs; }
/* @@ -362,11 +350,10 @@ static int stm_lock(struct spi_nor *nor, u32 ofs, size_t len) * * pow = ceil(log2(size / len)) = log2(size) - floor(log2(len)) */ - pow = ilog2(flash->size) - ilog2(len); + pow = ilog2(mtd->size) - ilog2(len); val = mask - (pow << shift); if (val & ~mask) return -EINVAL; - /* Don't "lock" with no region! */ if (!(val & mask)) return -EINVAL; @@ -386,20 +373,22 @@ static int stm_lock(struct spi_nor *nor, u32 ofs, size_t len) * * Returns negative on errors, 0 on success. */ -static int stm_unlock(struct spi_nor *nor, u32 ofs, size_t len) +static int stm_unlock(struct spi_nor *nor, loff_t ofs, uint64_t len) { - uint8_t status_old, status_new; + struct mtd_info *mtd = nor->mtd; + int 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) + 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)) + if (stm_is_locked_sr(nor, status_old, ofs - mtd->erasesize, + mtd->erasesize)) return -EINVAL; + /* * Need largest pow such that: * @@ -409,8 +398,8 @@ static int stm_unlock(struct spi_nor *nor, u32 ofs, size_t len) * * 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) { + pow = ilog2(mtd->size) - order_base_2(mtd->size - (ofs + len)); + if (ofs + len == mtd->size) { val = 0; /* fully unlocked */ } else { val = mask - (pow << shift); @@ -428,8 +417,47 @@ static int stm_unlock(struct spi_nor *nor, u32 ofs, size_t len) write_enable(nor); return write_sr(nor, status_new); } + +/* + * 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, loff_t ofs, uint64_t len) +{ + int status; + + status = read_sr(nor); + if (status < 0) + return status; + + return stm_is_locked_sr(nor, ofs, len, status); +} #endif
+static int spi_nor_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len) +{ + struct spi_nor *nor = mtd->priv; + + return nor->flash_lock(nor, ofs, len); +} + +static int spi_nor_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len) +{ + struct spi_nor *nor = mtd->priv; + + return nor->flash_unlock(nor, ofs, len); +} + +static int spi_nor_is_locked(struct mtd_info *mtd, loff_t ofs, uint64_t len) +{ + struct spi_nor *nor = mtd->priv; + + return nor->flash_is_locked(nor, ofs, len); +} + static const struct spi_nor_info *spi_nor_id(struct spi_nor *nor) { int tmp; @@ -455,30 +483,32 @@ static const struct spi_nor_info *spi_nor_id(struct spi_nor *nor) return ERR_PTR(-ENODEV); }
-static int spi_nor_erase(struct spi_flash *flash, u32 offset, size_t len) +static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr) { - struct spi_nor *nor = flash->nor; - u32 erase_size, erase_addr; + struct spi_nor *nor = mtd->priv; + u32 addr, len, erase_addr; u8 cmd[SNOR_MAX_CMD_SIZE]; + uint32_t rem; 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; - } + div_u64_rem(instr->len, mtd->erasesize, &rem); + if (rem) + return -EINVAL; + + addr = instr->addr; + len = instr->len;
- if (flash->flash_is_locked) { - if (flash->flash_is_locked(flash, offset, len) > 0) { + if (mtd->_is_locked) { + if (mtd->_is_locked(mtd, addr, len) > 0) { printf("offset 0x%x is protected and cannot be erased\n", - offset); + addr); return -EINVAL; } }
- cmd[0] = flash->erase_opcode; + cmd[0] = nor->erase_opcode; while (len) { - erase_addr = offset; + erase_addr = addr;
#ifdef CONFIG_SF_DUAL_FLASH if (nor->dual > SNOR_DUAL_SINGLE) @@ -498,39 +528,47 @@ static int spi_nor_erase(struct spi_flash *flash, u32 offset, size_t len)
ret = nor->write(nor, cmd, sizeof(cmd), NULL, 0); if (ret < 0) - break; + goto erase_err;
ret = spi_nor_wait_till_ready(nor, SNOR_READY_WAIT_ERASE); if (ret < 0) - return ret; + goto erase_err;
- offset += erase_size; - len -= erase_size; + addr += mtd->erasesize; + len -= mtd->erasesize; }
+ write_disable(nor); + + instr->state = MTD_ERASE_DONE; + mtd_erase_callback(instr); + + return ret; + +erase_err: + instr->state = MTD_ERASE_FAILED; return ret; }
-int spi_nor_write(struct spi_flash *flash, u32 offset, - size_t len, const void *buf) +static int spi_nor_write(struct mtd_info *mtd, loff_t offset, size_t len, + size_t *retlen, const u_char *buf) { - struct spi_nor *nor = flash->nor; - unsigned long byte_addr, page_size; - u32 write_addr; + struct spi_nor *nor = mtd->priv; + u32 byte_addr, page_size, 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", + if (mtd->_is_locked) { + if (mtd->_is_locked(mtd, offset, len) > 0) { + printf("offset 0x%llx is protected and cannot be written\n", offset); return -EINVAL; } }
+ page_size = nor->page_size; + cmd[0] = nor->program_opcode; for (actual = 0; actual < len; actual += chunk_len) { write_addr = offset; @@ -568,14 +606,16 @@ int spi_nor_write(struct spi_flash *flash, u32 offset, return ret;
offset += chunk_len; + *retlen += chunk_len; }
return ret; }
-int spi_nor_read(struct spi_flash *flash, u32 offset, size_t len, void *data) +static int spi_nor_read(struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, u_char *buf) { - struct spi_nor *nor = flash->nor; + struct spi_nor *nor = mtd->priv; u32 remain_len, read_len, read_addr; u8 *cmd, cmdsz; int bank_sel = 0; @@ -583,7 +623,7 @@ int spi_nor_read(struct spi_flash *flash, u32 offset, size_t len, void *data)
/* Handle memory-mapped SPI */ if (nor->memory_map) { - ret = nor->read_mmap(nor, data, nor->memory_map + offset, len); + ret = nor->read_mmap(nor, buf, nor->memory_map + from, len); if (ret) { debug("spi-nor: mmap read failed\n"); return ret; @@ -601,7 +641,7 @@ int spi_nor_read(struct spi_flash *flash, u32 offset, size_t len, void *data)
cmd[0] = nor->read_opcode; while (len) { - read_addr = offset; + read_addr = from;
#ifdef CONFIG_SF_DUAL_FLASH if (nor->dual > SNOR_DUAL_SINGLE) @@ -614,7 +654,7 @@ int spi_nor_read(struct spi_flash *flash, u32 offset, size_t len, void *data) bank_sel = nor->bank_curr; #endif remain_len = ((SNOR_16MB_BOUN << nor->shift) * - (bank_sel + 1)) - offset; + (bank_sel + 1)) - from; if (len < remain_len) read_len = len; else @@ -622,13 +662,14 @@ int spi_nor_read(struct spi_flash *flash, u32 offset, size_t len, void *data)
spi_nor_addr(read_addr, cmd);
- ret = nor->read(nor, cmd, cmdsz, data, read_len); + ret = nor->read(nor, cmd, cmdsz, buf, read_len); if (ret < 0) break;
- offset += read_len; + from += read_len; len -= read_len; - data += read_len; + buf += read_len; + *retlen += read_len; }
free(cmd); @@ -636,7 +677,8 @@ int spi_nor_read(struct spi_flash *flash, u32 offset, size_t len, void *data) }
#ifdef CONFIG_SPI_FLASH_SST -static int sst_byte_write(struct spi_nor *nor, u32 offset, const void *buf) +static int sst_byte_write(struct spi_nor *nor, u32 offset, + const void *buf, size_t *retlen) { int ret; u8 cmd[4] = { @@ -657,12 +699,15 @@ static int sst_byte_write(struct spi_nor *nor, u32 offset, const void *buf) if (ret) return ret;
+ *retlen += 1; + 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) +static int sst_write_wp(struct mtd_info *mtd, loff_t offset, size_t len, + size_t *retlen, const u_char *buf) { - struct spi_nor *nor = flash->nor; + struct spi_nor *nor = mtd->priv; size_t actual, cmd_len; int ret; u8 cmd[4]; @@ -670,7 +715,7 @@ int sst_write_wp(struct spi_nor *nor, u32 offset, size_t len, const void *buf) /* If the data is not word aligned, write out leading single byte */ actual = offset % 2; if (actual) { - ret = sst_byte_write(nor, offset, buf); + ret = sst_byte_write(nor, offset, buf, retlen); if (ret) goto done; } @@ -687,7 +732,7 @@ int sst_write_wp(struct spi_nor *nor, u32 offset, size_t len, const void *buf) cmd[3] = offset;
for (; actual < len - 1; actual += 2) { - debug("spi-nor: 0x%p => cmd = { 0x%02x 0x%06x }\n", + debug("spi-nor: 0x%p => cmd = { 0x%02x 0x%06llx }\n", buf + actual, cmd[0], offset);
ret = nor->write(nor, cmd, cmd_len, buf + actual, 2); @@ -702,6 +747,7 @@ int sst_write_wp(struct spi_nor *nor, u32 offset, size_t len, const void *buf)
cmd_len = 1; offset += 2; + *retlen += 2; }
if (!ret) @@ -715,14 +761,15 @@ int sst_write_wp(struct spi_nor *nor, u32 offset, size_t len, const void *buf) return ret; }
-int sst_write_bp(struct spi_nor *nor, u32 offset, size_t len, const void *buf) +static int sst_write_bp(struct mtd_info *mtd, loff_t offset, size_t len, + size_t *retlen, const u_char *buf) { - struct spi_nor *nor = flash->nor; + struct spi_nor *nor = mtd->priv; size_t actual; int ret;
for (actual = 0; actual < len; actual++) { - ret = sst_byte_write(nor, offset, buf + actual); + ret = sst_byte_write(nor, offset, buf + actual, retlen); if (ret) { debug("spi-nor: sst byte program failed\n"); break; @@ -853,6 +900,7 @@ static int set_quad_mode(struct spi_nor *nor, const struct spi_nor_info *info) #if CONFIG_IS_ENABLED(OF_CONTROL) int spi_nor_decode_fdt(const void *blob, struct spi_nor *nor) { + struct mtd_info *mtd = nor->mtd; fdt_addr_t addr; fdt_size_t size; int node; @@ -868,7 +916,7 @@ int spi_nor_decode_fdt(const void *blob, struct spi_nor *nor) return 0; }
- if (flash->size != size) { + if (mtd->size != size) { debug("%s: Memory map must cover entire device\n", __func__); return -1; } @@ -891,6 +939,7 @@ static int spi_nor_check(struct spi_nor *nor)
int spi_nor_scan(struct spi_nor *nor) { + struct mtd_info *mtd = nor->mtd; const struct spi_nor_info *info = NULL; static u8 flash_read_cmd[] = { SNOR_OP_READ, @@ -921,7 +970,13 @@ int spi_nor_scan(struct spi_nor *nor) write_sr(nor, 0); }
- flash->name = info->name; + mtd->name = info->name; + mtd->priv = nor; + mtd->type = MTD_NORFLASH; + mtd->writesize = 1; + mtd->flags = MTD_CAP_NORFLASH; + mtd->_erase = spi_nor_erase; + mtd->_read = spi_nor_read;
if (info->flags & USE_FSR) nor->flags |= SNOR_F_USE_FSR; @@ -929,15 +984,13 @@ int spi_nor_scan(struct spi_nor *nor) 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; + mtd->_write = spi_nor_write; #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; + mtd->_write = sst_write_bp; else - flash->write = sst_write_wp; + mtd->_write = sst_write_wp; } #endif
@@ -951,10 +1004,10 @@ int spi_nor_scan(struct spi_nor *nor) } #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; + if (nor->flash_lock && nor->flash_unlock && nor->flash_is_locked) { + mtd->_lock = spi_nor_lock; + mtd->_unlock = spi_nor_unlock; + mtd->_is_locked = spi_nor_is_locked; }
/* Compute the flash size */ @@ -972,30 +1025,30 @@ int spi_nor_scan(struct spi_nor *nor) 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; + mtd->writebufsize = nor->page_size; + mtd->size = (info->sector_size * info->n_sectors) << nor->shift; #ifdef CONFIG_SF_DUAL_FLASH if (nor->dual & SNOR_DUAL_STACKED) - flash->size <<= 1; + mtd->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; + mtd->erasesize = 4096 << nor->shift; } else if (info->flags & SECT_4K_PMC) { nor->erase_opcode = SNOR_OP_BE_4K_PMC; - nor->erase_size = 4096; + mtd->erasesize = 4096; } else #endif { nor->erase_opcode = SNOR_OP_SE; - nor->erase_size = flash->sector_size; + mtd->erasesize = info->sector_size << nor->shift; }
- /* Now erase size becomes valid sector size */ - flash->sector_size = nor->erase_size; + if (info->flags & SPI_NOR_NO_ERASE) + mtd->flags |= MTD_NO_ERASE;
/* Look for the fastest read cmd */ cmd = fls(info->flash_read & nor->read_mode); @@ -1007,6 +1060,10 @@ int spi_nor_scan(struct spi_nor *nor) nor->read_opcode = SNOR_OP_READ_FAST; }
+ /* Some devices cannot do fast-read */ + if (info->flags & SPI_NOR_NO_FR) + nor->read_opcode = SNOR_OP_READ; + /* 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; @@ -1061,10 +1118,10 @@ int spi_nor_scan(struct spi_nor *nor) #endif
#ifndef CONFIG_SPL_BUILD - printf("spi-nor: detected %s with page size ", flash->name); + printf("spi-nor: detected %s with page size ", mtd->name); print_size(nor->page_size, ", erase size "); - print_size(nor->erase_size, ", total "); - print_size(flash->size, ""); + print_size(mtd->erasesize, ", total "); + print_size(mtd->size, ""); if (nor->memory_map) printf(", mapped at %p", nor->memory_map); puts("\n"); @@ -1072,9 +1129,9 @@ int spi_nor_scan(struct spi_nor *nor)
#ifndef CONFIG_SPI_FLASH_BAR if (((nor->dual == SNOR_DUAL_SINGLE) && - (flash->size > SNOR_16MB_BOUN)) || + (mtd->size > SNOR_16MB_BOUN)) || ((nor->dual > SNOR_DUAL_SINGLE) && - (flash->size > SNOR_16MB_BOUN << 1))) { + (mtd->size > SNOR_16MB_BOUN << 1))) { puts("spi-nor: Warning - Only lower 16MiB accessible,"); puts(" Full access #define CONFIG_SPI_FLASH_BAR\n"); }

m25p80 is flash interface for spi-nor core and drivers/spi so add spi_nor{} functionalities like - allocate spi_nor{} - basic initilization - install hooks - call to spi-nor core, using spi_nor_scan - register with mtd core
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 --- drivers/mtd/spi-nor/m25p80.c | 236 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 236 insertions(+)
diff --git a/drivers/mtd/spi-nor/m25p80.c b/drivers/mtd/spi-nor/m25p80.c index 833a9c3..57e54d0 100644 --- a/drivers/mtd/spi-nor/m25p80.c +++ b/drivers/mtd/spi-nor/m25p80.c @@ -10,14 +10,249 @@ #include <dm.h> #include <errno.h> #include <spi.h> + +#include <dm/device-internal.h> + #include <linux/mtd/mtd.h> +#include <linux/mtd/spi-nor.h> + +struct m25p { + struct spi_slave *spi; + struct spi_nor spi_nor; +}; + +static int spi_read_then_write(struct spi_slave *spi, const u8 *cmd, + size_t cmd_len, const u8 *data_out, + u8 *data_in, size_t data_len) +{ + unsigned long flags = SPI_XFER_BEGIN; + int ret; + + if (data_len == 0) + flags |= SPI_XFER_END; + + ret = spi_xfer(spi, cmd_len * 8, cmd, NULL, flags); + if (ret) { + debug("SF: Failed to send command (%zu bytes): %d\n", + cmd_len, ret); + } else if (data_len != 0) { + ret = spi_xfer(spi, data_len * 8, data_out, data_in, + SPI_XFER_END); + if (ret) + debug("SF: Failed to transfer %zu bytes of data: %d\n", + data_len, ret); + } + + return ret; +} + +static int m25p80_read_reg(struct spi_nor *nor, u8 cmd, u8 *val, int len) +{ + struct m25p *flash = nor->priv; + struct spi_slave *spi = flash->spi; + int ret; + + ret = spi_claim_bus(spi); + if (ret < 0) { + debug("m25p80: unable to claim SPI bus\n"); + return ret; + } + + if (nor->flags & SNOR_F_U_PAGE) + spi->flags |= SPI_XFER_U_PAGE; + + ret = spi_read_then_write(spi, &cmd, 1, NULL, val, len); + if (ret < 0) { + debug("m25p80: error %d reading register %x\n", ret, cmd); + return ret; + } + + spi_release_bus(spi); + + return ret; +} + +static int m25p80_write_reg(struct spi_nor *nor, u8 cmd, u8 *buf, int len) +{ + struct m25p *flash = nor->priv; + struct spi_slave *spi = flash->spi; + int ret; + + ret = spi_claim_bus(spi); + if (ret < 0) { + debug("m25p80: unable to claim SPI bus\n"); + return ret; + } + + if (nor->flags & SNOR_F_U_PAGE) + spi->flags |= SPI_XFER_U_PAGE; + + ret = spi_read_then_write(spi, &cmd, 1, buf, NULL, len); + if (ret < 0) { + debug("m25p80: error %d writing register %x\n", ret, cmd); + return ret; + } + + spi_release_bus(spi); + + return ret; +} + +void __weak flash_copy_mmap(void *data, void *offset, size_t len) +{ + memcpy(data, offset, len); +} + +static int m25p80_read_mmap(struct spi_nor *nor, void *data, + void *offset, size_t len) +{ + struct m25p *flash = nor->priv; + struct spi_slave *spi = flash->spi; + int ret; + + ret = spi_claim_bus(spi); + if (ret) { + debug("m25p80: unable to claim SPI bus\n"); + return ret; + } + + spi_xfer(spi, 0, NULL, NULL, SPI_XFER_MMAP); + flash_copy_mmap(data, offset, len); + spi_xfer(spi, 0, NULL, NULL, SPI_XFER_MMAP_END); + + spi_release_bus(spi); + + return ret; +} + +static int m25p80_read(struct spi_nor *nor, const u8 *cmd, size_t cmd_len, + void *data, size_t data_len) +{ + struct m25p *flash = nor->priv; + struct spi_slave *spi = flash->spi; + int ret; + + ret = spi_claim_bus(spi); + if (ret < 0) { + debug("m25p80: unable to claim SPI bus\n"); + return ret; + } + + if (nor->flags & SNOR_F_U_PAGE) + spi->flags |= SPI_XFER_U_PAGE; + + ret = spi_read_then_write(spi, cmd, cmd_len, NULL, data, data_len); + if (ret < 0) { + debug("m25p80: error %d reading %x\n", ret, *cmd); + return ret; + } + + spi_release_bus(spi); + + return ret; +} + +static int m25p80_write(struct spi_nor *nor, const u8 *cmd, size_t cmd_len, + const void *data, size_t data_len) +{ + struct m25p *flash = nor->priv; + struct spi_slave *spi = flash->spi; + int ret; + + ret = spi_claim_bus(spi); + if (ret < 0) { + debug("m25p80: unable to claim SPI bus\n"); + return ret; + } + + if (nor->flags & SNOR_F_U_PAGE) + spi->flags |= SPI_XFER_U_PAGE; + + ret = spi_read_then_write(spi, cmd, cmd_len, data, NULL, data_len); + if (ret < 0) { + debug("m25p80: error %d writing %x\n", ret, *cmd); + return ret; + } + + spi_release_bus(spi); + + return ret; +}
static int m25p_probe(struct udevice *dev) { struct spi_slave *spi = dev_get_parent_priv(dev); struct mtd_info *mtd = dev_get_uclass_priv(dev); + struct m25p *flash = dev_get_priv(dev); + struct spi_nor *nor; + int ret; + + nor = &flash->spi_nor; + + /* install hooks */ + nor->read_mmap = m25p80_read_mmap; + nor->read = m25p80_read; + nor->write = m25p80_write; + nor->read_reg = m25p80_read_reg; + nor->write_reg = m25p80_write_reg; + + nor->mtd = mtd; + nor->priv = flash; + flash->spi = spi; + + /* claim spi bus */ + ret = spi_claim_bus(spi); + if (ret) { + debug("m25p80: failed to claim SPI bus: %d\n", ret); + return ret; + } + + switch (spi->mode_rx) { + case SPI_RX_SLOW: + nor->read_mode = SNOR_READ; + break; + case SPI_RX_DUAL: + nor->read_mode = SNOR_READ_1_1_2; + break; + case SPI_RX_QUAD: + nor->read_mode = SNOR_READ_1_1_4; + break; + } + + switch (spi->mode) { + case SPI_TX_BYTE: + nor->mode = SNOR_WRITE_1_1_BYTE; + break; + case SPI_TX_QUAD: + nor->mode = SNOR_WRITE_1_1_4; + break; + } + + nor->memory_map = spi->memory_map; + nor->max_write_size = spi->max_write_size; + + /* TODO: unrelated to spi_slave{} */ + if (spi->option & SPI_CONN_DUAL_SHARED) + nor->dual = SNOR_DUAL_STACKED; + else if (spi->option & SPI_CONN_DUAL_SEPARATED) + nor->dual = SNOR_DUAL_PARALLEL; + + ret = spi_nor_scan(nor); + if (ret) + goto err_scan; + + ret = add_mtd_device(mtd); + if (ret) + goto err_mtd;
return 0; + +err_scan: + spi_release_bus(spi); +err_mtd: + spi_free_slave(spi); + device_remove(dev); + return ret; }
static const struct udevice_id m25p_ids[] = { @@ -34,4 +269,5 @@ U_BOOT_DRIVER(m25p80) = { .id = UCLASS_MTD, .of_match = m25p_ids, .probe = m25p_probe, + .priv_auto_alloc_size = sizeof(struct m25p), };

This patch adds driver-model probe from cmd_sf through MTD_DM_SPI_NOR which is depends on MTD and DM_SPI uclass.
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 --- cmd/sf.c | 4 ++-- drivers/mtd/spi-nor/Kconfig | 5 +++++ drivers/mtd/spi-nor/Makefile | 2 ++ drivers/mtd/spi-nor/spi-nor-probe.c | 30 ++++++++++++++++++++++++++++++ include/spi_flash.h | 6 ++++++ 5 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 drivers/mtd/spi-nor/spi-nor-probe.c
diff --git a/cmd/sf.c b/cmd/sf.c index 42862d9..e4d1274 100644 --- a/cmd/sf.c +++ b/cmd/sf.c @@ -85,7 +85,7 @@ static int do_spi_flash_probe(int argc, char * const argv[]) unsigned int speed = CONFIG_SF_DEFAULT_SPEED; unsigned int mode = CONFIG_SF_DEFAULT_MODE; char *endp; -#ifdef CONFIG_DM_SPI_FLASH +#if defined(CONFIG_DM_SPI_FLASH) || defined (CONFIG_MTD_DM_SPI_NOR) struct udevice *new, *bus_dev; int ret; #else @@ -118,7 +118,7 @@ static int do_spi_flash_probe(int argc, char * const argv[]) return -1; }
-#ifdef CONFIG_DM_SPI_FLASH +#if defined(CONFIG_DM_SPI_FLASH) || defined (CONFIG_MTD_DM_SPI_NOR) /* Remove the old device, otherwise probe will just be a nop */ ret = spi_find_bus_and_cs(bus, cs, &bus_dev, &new); if (!ret) { diff --git a/drivers/mtd/spi-nor/Kconfig b/drivers/mtd/spi-nor/Kconfig index 374cdcb..59bb943 100644 --- a/drivers/mtd/spi-nor/Kconfig +++ b/drivers/mtd/spi-nor/Kconfig @@ -1,5 +1,6 @@ menuconfig MTD_SPI_NOR tristate "SPI-NOR device support" + select MTD_DM_SPI_NOR help This is the core SPI NOR framework which can be used to interact SPI-NOR to SPI driver interface layer and the SPI-NOR controller driver. @@ -12,6 +13,10 @@ menuconfig MTD_SPI_NOR SPI-NOR controller drivers for SPI-NOR device access. Note that from SPI-NOR core to SPI drivers there should be an interface layer.
+config MTD_DM_SPI_NOR + tristate + depends on DM_SPI && MTD + if MTD_SPI_NOR
config MTD_M25P80 diff --git a/drivers/mtd/spi-nor/Makefile b/drivers/mtd/spi-nor/Makefile index 9ab6e3d..71e7ae2 100644 --- a/drivers/mtd/spi-nor/Makefile +++ b/drivers/mtd/spi-nor/Makefile @@ -6,6 +6,8 @@ ifdef CONFIG_MTD_SPI_NOR obj-y += spi-nor.o obj-y += spi-nor-ids.o + +obj-$(CONFIG_MTD_DM_SPI_NOR) += spi-nor-probe.o endif
obj-$(CONFIG_MTD_M25P80) += m25p80.o diff --git a/drivers/mtd/spi-nor/spi-nor-probe.c b/drivers/mtd/spi-nor/spi-nor-probe.c new file mode 100644 index 0000000..c808a7d --- /dev/null +++ b/drivers/mtd/spi-nor/spi-nor-probe.c @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2014 Google, Inc + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dm.h> +#include <spi.h> +#include <spi_flash.h> + +int spi_flash_probe_bus_cs(unsigned int busnum, unsigned int cs, + unsigned int max_hz, unsigned int spi_mode, + struct udevice **devp) +{ + struct spi_slave *slave; + struct udevice *bus; + char name[30], *str; + int ret; + + snprintf(name, sizeof(name), "spi-nor@%d:%d", busnum, cs); + str = strdup(name); + ret = spi_get_bus_and_cs(busnum, cs, max_hz, spi_mode, + "m25p80", str, &bus, &slave); + if (ret) + return ret; + + *devp = slave->dev; + return 0; +} diff --git a/include/spi_flash.h b/include/spi_flash.h index d0ce9e7..a458820 100644 --- a/include/spi_flash.h +++ b/include/spi_flash.h @@ -190,6 +190,12 @@ int sandbox_sf_bind_emul(struct sandbox_state *state, int busnum, int cs,
void sandbox_sf_unbind_emul(struct sandbox_state *state, int busnum, int cs);
+#elif CONFIG_MTD_DM_SPI_NOR + +int spi_flash_probe_bus_cs(unsigned int busnum, unsigned int cs, + unsigned int max_hz, unsigned int spi_mode, + struct udevice **devp); + #else struct spi_flash *spi_flash_probe(unsigned int bus, unsigned int cs, unsigned int max_hz, unsigned int spi_mode);

While probing spi-nor in SPL spi_flash_probe is needed, so add the flash probe code in spi-nor-probe.c
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 --- drivers/mtd/spi-nor/spi-nor-probe.c | 15 +++++++++++++++ include/spi_flash.h | 4 ++++ 2 files changed, 19 insertions(+)
diff --git a/drivers/mtd/spi-nor/spi-nor-probe.c b/drivers/mtd/spi-nor/spi-nor-probe.c index c808a7d..9bc61ea 100644 --- a/drivers/mtd/spi-nor/spi-nor-probe.c +++ b/drivers/mtd/spi-nor/spi-nor-probe.c @@ -9,6 +9,21 @@ #include <spi.h> #include <spi_flash.h>
+/* + * TODO(sjg@chromium.org): This is an old-style function. We should remove + * it when all SPI flash drivers use dm + */ +spi_flash_t *spi_flash_probe(unsigned int bus, unsigned int cs, + unsigned int max_hz, unsigned int spi_mode) +{ + struct udevice *dev; + + if (spi_flash_probe_bus_cs(bus, cs, max_hz, spi_mode, &dev)) + return NULL; + + return dev_get_uclass_priv(dev); +} + int spi_flash_probe_bus_cs(unsigned int busnum, unsigned int cs, unsigned int max_hz, unsigned int spi_mode, struct udevice **devp) diff --git a/include/spi_flash.h b/include/spi_flash.h index a458820..1dcce13 100644 --- a/include/spi_flash.h +++ b/include/spi_flash.h @@ -196,6 +196,10 @@ int spi_flash_probe_bus_cs(unsigned int busnum, unsigned int cs, unsigned int max_hz, unsigned int spi_mode, struct udevice **devp);
+/* Compatibility function - this is the old U-Boot API */ +spi_flash_t *spi_flash_probe(unsigned int bus, unsigned int cs, + unsigned int max_hz, unsigned int spi_mode); + #else struct spi_flash *spi_flash_probe(unsigned int bus, unsigned int cs, unsigned int max_hz, unsigned int spi_mode);

This patch adds support for mtd_info operations handling from cmd/sf.c
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 --- cmd/sf.c | 34 +++++++++++++++++----------------- include/spi_flash.h | 48 ++++++++++++++++++++++++++++++++++++------------ 2 files changed, 53 insertions(+), 29 deletions(-)
diff --git a/cmd/sf.c b/cmd/sf.c index e4d1274..5dd7177 100644 --- a/cmd/sf.c +++ b/cmd/sf.c @@ -19,7 +19,7 @@ #include <asm/io.h> #include <dm/device-internal.h>
-static struct spi_flash *flash; +static spi_flash_t *flash;
/* * This function computes the length argument for the erase command. @@ -53,8 +53,8 @@ static int sf_parse_len_arg(char *arg, ulong *len) if (ep == arg || *ep != '\0') return -1;
- if (round_up_len && flash->sector_size > 0) - *len = ROUND(len_arg, flash->sector_size); + if (round_up_len && flash->erasesize > 0) + *len = ROUND(len_arg, flash->erasesize); else *len = len_arg;
@@ -89,7 +89,7 @@ static int do_spi_flash_probe(int argc, char * const argv[]) struct udevice *new, *bus_dev; int ret; #else - struct spi_flash *new; + spi_flash_t *new; #endif
if (argc >= 2) { @@ -166,15 +166,15 @@ static int do_spi_flash_probe(int argc, char * const argv[]) * @param skipped Count of skipped data (incremented by this function) * @return NULL if OK, else a string containing the stage which failed */ -static const char *spi_flash_update_block(struct spi_flash *flash, u32 offset, +static const char *spi_flash_update_block(spi_flash_t *flash, u32 offset, size_t len, const char *buf, char *cmp_buf, size_t *skipped) { char *ptr = (char *)buf;
- debug("offset=%#x, sector_size=%#x, len=%#zx\n", - offset, flash->sector_size, len); + debug("offset=%#x, erasesize=%#x, len=%#zx\n", + offset, flash->erasesize, len); /* Read the entire sector so to allow for rewriting */ - if (spi_flash_read(flash, offset, flash->sector_size, cmp_buf)) + if (spi_flash_read(flash, offset, flash->erasesize, cmp_buf)) return "read"; /* Compare only what is meaningful (len) */ if (memcmp(cmp_buf, buf, len) == 0) { @@ -184,15 +184,15 @@ static const char *spi_flash_update_block(struct spi_flash *flash, u32 offset, return NULL; } /* Erase the entire sector */ - if (spi_flash_erase(flash, offset, flash->sector_size)) + if (spi_flash_erase(flash, offset, flash->erasesize)) return "erase"; /* If it's a partial sector, copy the data into the temp-buffer */ - if (len != flash->sector_size) { + if (len != flash->erasesize) { memcpy(cmp_buf, buf, len); ptr = cmp_buf; } /* Write one complete sector */ - if (spi_flash_write(flash, offset, flash->sector_size, ptr)) + if (spi_flash_write(flash, offset, flash->erasesize, ptr)) return "write";
return NULL; @@ -208,7 +208,7 @@ static const char *spi_flash_update_block(struct spi_flash *flash, u32 offset, * @param buf buffer to write from * @return 0 if ok, 1 on error */ -static int spi_flash_update(struct spi_flash *flash, u32 offset, +static int spi_flash_update(spi_flash_t *flash, u32 offset, size_t len, const char *buf) { const char *err_oper = NULL; @@ -223,12 +223,12 @@ static int spi_flash_update(struct spi_flash *flash, u32 offset,
if (end - buf >= 200) scale = (end - buf) / 100; - cmp_buf = memalign(ARCH_DMA_MINALIGN, flash->sector_size); + cmp_buf = memalign(ARCH_DMA_MINALIGN, flash->erasesize); if (cmp_buf) { ulong last_update = get_timer(0);
for (; buf < end && !err_oper; buf += todo, offset += todo) { - todo = min_t(size_t, end - buf, flash->sector_size); + todo = min_t(size_t, end - buf, flash->erasesize); if (get_timer(last_update) > 100) { printf(" \rUpdating, %zu%% %lu B/s", 100 - (end - buf) / scale, @@ -280,7 +280,7 @@ static int do_spi_flash_read_write(int argc, char * const argv[])
/* Consistency checking */ if (offset + len > flash->size) { - printf("ERROR: attempting %s past flash size (%#x)\n", + printf("ERROR: attempting %s past flash size (%#llx)\n", argv[0], flash->size); return 1; } @@ -336,7 +336,7 @@ static int do_spi_flash_erase(int argc, char * const argv[])
/* Consistency checking */ if (offset + size > flash->size) { - printf("ERROR: attempting %s past flash size (%#x)\n", + printf("ERROR: attempting %s past flash size (%#llx)\n", argv[0], flash->size); return 1; } @@ -436,7 +436,7 @@ static void spi_test_next_stage(struct test_info *test) * @param vbuf Verification buffer * @return 0 if ok, -1 on error */ -static int spi_flash_test(struct spi_flash *flash, uint8_t *buf, ulong len, +static int spi_flash_test(spi_flash_t *flash, uint8_t *buf, ulong len, ulong offset, uint8_t *vbuf) { struct test_info test; diff --git a/include/spi_flash.h b/include/spi_flash.h index 1dcce13..719a6e3 100644 --- a/include/spi_flash.h +++ b/include/spi_flash.h @@ -12,6 +12,7 @@
#include <dm.h> /* Because we dereference struct udevice here */ #include <linux/types.h> +#include <linux/mtd/mtd.h>
#ifndef CONFIG_SF_DEFAULT_SPEED # define CONFIG_SF_DEFAULT_SPEED 1000000 @@ -192,6 +193,41 @@ void sandbox_sf_unbind_emul(struct sandbox_state *state, int busnum, int cs);
#elif CONFIG_MTD_DM_SPI_NOR
+typedef struct mtd_info spi_flash_t; + +static inline int spi_flash_read(spi_flash_t *info, u32 offset, + size_t len, void *buf) +{ + return mtd_read(info, offset, len, &len, (u_char *)buf); +} + +static inline int spi_flash_write(spi_flash_t *info, u32 offset, + size_t len, const void *buf) +{ + return mtd_write(info, offset, len, &len, (u_char *)buf); +} + +static inline int spi_flash_erase(spi_flash_t *info, u32 offset, size_t len) +{ + struct erase_info instr; + + instr.mtd = info; + instr.addr = offset; + instr.len = len; + instr.callback = 0; + + return mtd_erase(info, &instr); +} + +static inline int spi_flash_protect(spi_flash_t *info, u32 ofs, + u32 len, bool prot) +{ + if (prot) + return mtd_lock(info, ofs, len); + else + return mtd_unlock(info, ofs, len); +} + int spi_flash_probe_bus_cs(unsigned int busnum, unsigned int cs, unsigned int max_hz, unsigned int spi_mode, struct udevice **devp); @@ -237,18 +273,6 @@ static inline int spi_flash_erase(struct spi_flash *flash, u32 offset, } #endif
-static inline int spi_flash_protect(struct spi_flash *flash, u32 ofs, u32 len, - bool prot) -{ - if (!flash->flash_lock || !flash->flash_unlock) - return -EOPNOTSUPP; - - if (prot) - return flash->flash_lock(flash, ofs, len); - else - return flash->flash_unlock(flash, ofs, len); -} - void spi_boot(void) __noreturn; void spi_spl_load_image(uint32_t offs, unsigned int size, void *vdst);

Like adding spi_nor support for dm-driven code in m25p80 add the same way for non-dm code as well. - allocate spi_nor{} - basic initilization - install hooks - call to spi-nor core, using spi_nor_scan - register with mtd core
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 --- drivers/mtd/spi-nor/m25p80.c | 108 ++++++++++++++++++++++++++++++++++++++----- 1 file changed, 96 insertions(+), 12 deletions(-)
diff --git a/drivers/mtd/spi-nor/m25p80.c b/drivers/mtd/spi-nor/m25p80.c index 57e54d0..f0340a5 100644 --- a/drivers/mtd/spi-nor/m25p80.c +++ b/drivers/mtd/spi-nor/m25p80.c @@ -19,6 +19,9 @@ struct m25p { struct spi_slave *spi; struct spi_nor spi_nor; +#ifndef CONFIG_MTD_DM_SPI_NOR + struct mtd_info mtd; +#endif };
static int spi_read_then_write(struct spi_slave *spi, const u8 *cmd, @@ -179,16 +182,13 @@ static int m25p80_write(struct spi_nor *nor, const u8 *cmd, size_t cmd_len, return ret; }
-static int m25p_probe(struct udevice *dev) +static int m25p80_spi_nor(struct spi_nor *nor) { - struct spi_slave *spi = dev_get_parent_priv(dev); - struct mtd_info *mtd = dev_get_uclass_priv(dev); - struct m25p *flash = dev_get_priv(dev); - struct spi_nor *nor; + struct mtd_info *mtd = nor->mtd; + struct m25p *flash = nor->priv; + struct spi_slave *spi = flash->spi; int ret;
- nor = &flash->spi_nor; - /* install hooks */ nor->read_mmap = m25p80_read_mmap; nor->read = m25p80_read; @@ -196,10 +196,6 @@ static int m25p_probe(struct udevice *dev) nor->read_reg = m25p80_read_reg; nor->write_reg = m25p80_write_reg;
- nor->mtd = mtd; - nor->priv = flash; - flash->spi = spi; - /* claim spi bus */ ret = spi_claim_bus(spi); if (ret) { @@ -251,10 +247,33 @@ err_scan: spi_release_bus(spi); err_mtd: spi_free_slave(spi); - device_remove(dev); return ret; }
+#ifdef CONFIG_MTD_DM_SPI_NOR +static int m25p_probe(struct udevice *dev) +{ + struct spi_slave *spi = dev_get_parent_priv(dev); + struct mtd_info *mtd = dev_get_uclass_priv(dev); + struct m25p *flash = dev_get_priv(dev); + struct spi_nor *nor; + int ret; + + nor = &flash->spi_nor; + + nor->mtd = mtd; + nor->priv = flash; + flash->spi = spi; + + ret = m25p80_spi_nor(nor); + if (ret) { + device_remove(dev); + return ret; + } + + return 0; +} + static const struct udevice_id m25p_ids[] = { /* * Generic compatibility for SPI NOR that can be identified by the @@ -271,3 +290,68 @@ U_BOOT_DRIVER(m25p80) = { .probe = m25p_probe, .priv_auto_alloc_size = sizeof(struct m25p), }; + +#else + +static struct mtd_info *m25p80_probe_tail(struct spi_slave *bus) +{ + struct m25p *flash; + struct spi_nor *nor; + int ret; + + flash = calloc(1, sizeof(*flash)); + if (!flash) { + debug("mp25p80: failed to allocate m25p\n"); + return NULL; + } + + nor = &flash->spi_nor; + nor->mtd = &flash->mtd; + + nor->priv = flash; + flash->spi = bus; + + ret = m25p80_spi_nor(nor); + if (ret) { + free(flash); + return NULL; + } + + return nor->mtd; +} + +struct mtd_info *spi_flash_probe(unsigned int busnum, unsigned int cs, + unsigned int max_hz, unsigned int spi_mode) +{ + struct spi_slave *bus; + + bus = spi_setup_slave(busnum, cs, max_hz, spi_mode); + if (!bus) + return NULL; + return m25p80_probe_tail(bus); +} + +#ifdef CONFIG_OF_SPI_FLASH +struct mtd_info *spi_flash_probe_fdt(const void *blob, int slave_node, + int spi_node) +{ + struct spi_slave *bus; + + bus = spi_setup_slave_fdt(blob, slave_node, spi_node); + if (!bus) + return NULL; + return m25p80_probe_tail(bus); +} +#endif + +void spi_flash_free(struct mtd_info *info) +{ + struct spi_nor *nor = info->priv; + struct m25p *flash = nor->priv; + + del_mtd_device(info); + spi_free_slave(flash->spi); + free(flash); +} + +#endif /* CONFIG_MTD_DM_SPI_NOR */

Since non-dm code in m25p80 handling spi_nor along with mtd, hence use the mtd_info operation from user commands instead of legacy spi_flash{} ops.
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 --- include/spi_flash.h | 23 ++++------------------- 1 file changed, 4 insertions(+), 19 deletions(-)
diff --git a/include/spi_flash.h b/include/spi_flash.h index 719a6e3..77939bd 100644 --- a/include/spi_flash.h +++ b/include/spi_flash.h @@ -191,7 +191,9 @@ int sandbox_sf_bind_emul(struct sandbox_state *state, int busnum, int cs,
void sandbox_sf_unbind_emul(struct sandbox_state *state, int busnum, int cs);
-#elif CONFIG_MTD_DM_SPI_NOR +#endif + +#ifdef CONFIG_MTD_DM_SPI_NOR
typedef struct mtd_info spi_flash_t;
@@ -254,24 +256,7 @@ struct spi_flash *spi_flash_probe_fdt(const void *blob, int slave_node,
void spi_flash_free(struct spi_flash *flash);
-static inline int spi_flash_read(struct spi_flash *flash, u32 offset, - size_t len, void *buf) -{ - return flash->read(flash, offset, len, buf); -} - -static inline int spi_flash_write(struct spi_flash *flash, u32 offset, - size_t len, const void *buf) -{ - return flash->write(flash, offset, len, buf); -} - -static inline int spi_flash_erase(struct spi_flash *flash, u32 offset, - size_t len) -{ - return flash->erase(flash, offset, len); -} -#endif +#endif /* CONFIG_MTD_DM_SPI_NOR */
void spi_boot(void) __noreturn; void spi_spl_load_image(uint32_t offs, unsigned int size, void *vdst);

Since spi_read_then_write is doing spi operations like setting up commands, tx and rx through spi_xfer, So it is meanfull to have this definition at spi layer and flash layer should use this whenever required.
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 --- drivers/mtd/spi-nor/m25p80.c | 25 ------------------------- drivers/spi/spi-uclass.c | 25 +++++++++++++++++++++++++ drivers/spi/spi.c | 25 +++++++++++++++++++++++++ include/spi.h | 5 +++++ 4 files changed, 55 insertions(+), 25 deletions(-)
diff --git a/drivers/mtd/spi-nor/m25p80.c b/drivers/mtd/spi-nor/m25p80.c index f0340a5..4aefe93 100644 --- a/drivers/mtd/spi-nor/m25p80.c +++ b/drivers/mtd/spi-nor/m25p80.c @@ -24,31 +24,6 @@ struct m25p { #endif };
-static int spi_read_then_write(struct spi_slave *spi, const u8 *cmd, - size_t cmd_len, const u8 *data_out, - u8 *data_in, size_t data_len) -{ - unsigned long flags = SPI_XFER_BEGIN; - int ret; - - if (data_len == 0) - flags |= SPI_XFER_END; - - ret = spi_xfer(spi, cmd_len * 8, cmd, NULL, flags); - if (ret) { - debug("SF: Failed to send command (%zu bytes): %d\n", - cmd_len, ret); - } else if (data_len != 0) { - ret = spi_xfer(spi, data_len * 8, data_out, data_in, - SPI_XFER_END); - if (ret) - debug("SF: Failed to transfer %zu bytes of data: %d\n", - data_len, ret); - } - - return ret; -} - static int m25p80_read_reg(struct spi_nor *nor, u8 cmd, u8 *val, int len) { struct m25p *flash = nor->priv; diff --git a/drivers/spi/spi-uclass.c b/drivers/spi/spi-uclass.c index 677c020..7728eac 100644 --- a/drivers/spi/spi-uclass.c +++ b/drivers/spi/spi-uclass.c @@ -95,6 +95,31 @@ int spi_xfer(struct spi_slave *slave, unsigned int bitlen, return spi_get_ops(bus)->xfer(dev, bitlen, dout, din, flags); }
+int spi_read_then_write(struct spi_slave *spi, const u8 *cmd, + size_t cmd_len, const u8 *data_out, + u8 *data_in, size_t data_len) +{ + unsigned long flags = SPI_XFER_BEGIN; + int ret; + + if (data_len == 0) + flags |= SPI_XFER_END; + + ret = spi_xfer(spi, cmd_len * 8, cmd, NULL, flags); + if (ret) { + debug("spi: failed to send command (%zu bytes): %d\n", + cmd_len, ret); + } else if (data_len != 0) { + ret = spi_xfer(spi, data_len * 8, data_out, data_in, + SPI_XFER_END); + if (ret) + debug("spi: failed to transfer %zu bytes of data: %d\n", + data_len, ret); + } + + return ret; +} + static int spi_post_bind(struct udevice *dev) { /* Scan the bus for devices */ diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 7d81fbd..a050386 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -39,6 +39,31 @@ void *spi_do_alloc_slave(int offset, int size, unsigned int bus, return ptr; }
+int spi_read_then_write(struct spi_slave *spi, const u8 *cmd, + size_t cmd_len, const u8 *data_out, + u8 *data_in, size_t data_len) +{ + unsigned long flags = SPI_XFER_BEGIN; + int ret; + + if (data_len == 0) + flags |= SPI_XFER_END; + + ret = spi_xfer(spi, cmd_len * 8, cmd, NULL, flags); + if (ret) { + debug("spi: failed to send command (%zu bytes): %d\n", + cmd_len, ret); + } else if (data_len != 0) { + ret = spi_xfer(spi, data_len * 8, data_out, data_in, + SPI_XFER_END); + if (ret) + debug("spi: failed to transfer %zu bytes of data: %d\n", + data_len, ret); + } + + return ret; +} + #ifdef CONFIG_OF_SPI struct spi_slave *spi_base_setup_slave_fdt(const void *blob, int busnum, int node) diff --git a/include/spi.h b/include/spi.h index 4b88d39..19589aa 100644 --- a/include/spi.h +++ b/include/spi.h @@ -265,6 +265,11 @@ int spi_set_wordlen(struct spi_slave *slave, unsigned int wordlen); int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout, void *din, unsigned long flags);
+/* spi_write_then_read - SPI synchronous read followed by write */ +int spi_read_then_write(struct spi_slave *spi, const u8 *cmd, + size_t cmd_len, const u8 *data_out, + u8 *data_in, size_t data_len); + /* Copy memory mapped data */ void spi_flash_copy_mmap(void *data, void *offset, size_t len);

Since spi_read_then_write moved into spi layer, the meaning of data transfer is also change from read_then_write to write_then_read, this means first spi will write the opcode through and then read the respective buffer.
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 --- drivers/mtd/spi-nor/m25p80.c | 8 ++++---- drivers/spi/spi-uclass.c | 19 +++++++++---------- drivers/spi/spi.c | 19 +++++++++---------- include/spi.h | 23 +++++++++++++++++++---- 4 files changed, 41 insertions(+), 28 deletions(-)
diff --git a/drivers/mtd/spi-nor/m25p80.c b/drivers/mtd/spi-nor/m25p80.c index 4aefe93..7e2702d 100644 --- a/drivers/mtd/spi-nor/m25p80.c +++ b/drivers/mtd/spi-nor/m25p80.c @@ -39,7 +39,7 @@ static int m25p80_read_reg(struct spi_nor *nor, u8 cmd, u8 *val, int len) if (nor->flags & SNOR_F_U_PAGE) spi->flags |= SPI_XFER_U_PAGE;
- ret = spi_read_then_write(spi, &cmd, 1, NULL, val, len); + ret = spi_write_then_read(spi, &cmd, 1, NULL, val, len); if (ret < 0) { debug("m25p80: error %d reading register %x\n", ret, cmd); return ret; @@ -65,7 +65,7 @@ static int m25p80_write_reg(struct spi_nor *nor, u8 cmd, u8 *buf, int len) if (nor->flags & SNOR_F_U_PAGE) spi->flags |= SPI_XFER_U_PAGE;
- ret = spi_read_then_write(spi, &cmd, 1, buf, NULL, len); + ret = spi_write_then_read(spi, &cmd, 1, buf, NULL, len); if (ret < 0) { debug("m25p80: error %d writing register %x\n", ret, cmd); return ret; @@ -119,7 +119,7 @@ static int m25p80_read(struct spi_nor *nor, const u8 *cmd, size_t cmd_len, if (nor->flags & SNOR_F_U_PAGE) spi->flags |= SPI_XFER_U_PAGE;
- ret = spi_read_then_write(spi, cmd, cmd_len, NULL, data, data_len); + ret = spi_write_then_read(spi, cmd, cmd_len, NULL, data, data_len); if (ret < 0) { debug("m25p80: error %d reading %x\n", ret, *cmd); return ret; @@ -146,7 +146,7 @@ static int m25p80_write(struct spi_nor *nor, const u8 *cmd, size_t cmd_len, if (nor->flags & SNOR_F_U_PAGE) spi->flags |= SPI_XFER_U_PAGE;
- ret = spi_read_then_write(spi, cmd, cmd_len, data, NULL, data_len); + ret = spi_write_then_read(spi, cmd, cmd_len, data, NULL, data_len); if (ret < 0) { debug("m25p80: error %d writing %x\n", ret, *cmd); return ret; diff --git a/drivers/spi/spi-uclass.c b/drivers/spi/spi-uclass.c index 7728eac..0dfdd8b 100644 --- a/drivers/spi/spi-uclass.c +++ b/drivers/spi/spi-uclass.c @@ -95,26 +95,25 @@ int spi_xfer(struct spi_slave *slave, unsigned int bitlen, return spi_get_ops(bus)->xfer(dev, bitlen, dout, din, flags); }
-int spi_read_then_write(struct spi_slave *spi, const u8 *cmd, - size_t cmd_len, const u8 *data_out, - u8 *data_in, size_t data_len) +int spi_write_then_read(struct spi_slave *slave, const u8 *opcode, + size_t n_opcode, const u8 *txbuf, u8 *rxbuf, + size_t n_buf) { unsigned long flags = SPI_XFER_BEGIN; int ret;
- if (data_len == 0) + if (n_buf == 0) flags |= SPI_XFER_END;
- ret = spi_xfer(spi, cmd_len * 8, cmd, NULL, flags); + ret = spi_xfer(slave, n_opcode * 8, opcode, NULL, flags); if (ret) { debug("spi: failed to send command (%zu bytes): %d\n", - cmd_len, ret); - } else if (data_len != 0) { - ret = spi_xfer(spi, data_len * 8, data_out, data_in, - SPI_XFER_END); + n_opcode, ret); + } else if (n_buf != 0) { + ret = spi_xfer(slave, n_buf * 8, txbuf, rxbuf, SPI_XFER_END); if (ret) debug("spi: failed to transfer %zu bytes of data: %d\n", - data_len, ret); + n_buf, ret); }
return ret; diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index a050386..c8051f9 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -39,26 +39,25 @@ void *spi_do_alloc_slave(int offset, int size, unsigned int bus, return ptr; }
-int spi_read_then_write(struct spi_slave *spi, const u8 *cmd, - size_t cmd_len, const u8 *data_out, - u8 *data_in, size_t data_len) +int spi_write_then_read(struct spi_slave *slave, const u8 *opcode, + size_t n_opcode, const u8 *txbuf, u8 *rxbuf, + size_t n_buf) { unsigned long flags = SPI_XFER_BEGIN; int ret;
- if (data_len == 0) + if (n_buf == 0) flags |= SPI_XFER_END;
- ret = spi_xfer(spi, cmd_len * 8, cmd, NULL, flags); + ret = spi_xfer(slave, n_opcode * 8, opcode, NULL, flags); if (ret) { debug("spi: failed to send command (%zu bytes): %d\n", - cmd_len, ret); - } else if (data_len != 0) { - ret = spi_xfer(spi, data_len * 8, data_out, data_in, - SPI_XFER_END); + n_opcode, ret); + } else if (n_buf != 0) { + ret = spi_xfer(slave, n_buf * 8, txbuf, rxbuf, SPI_XFER_END); if (ret) debug("spi: failed to transfer %zu bytes of data: %d\n", - data_len, ret); + n_buf, ret); }
return ret; diff --git a/include/spi.h b/include/spi.h index 19589aa..dd0b11b 100644 --- a/include/spi.h +++ b/include/spi.h @@ -265,10 +265,25 @@ int spi_set_wordlen(struct spi_slave *slave, unsigned int wordlen); int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout, void *din, unsigned long flags);
-/* spi_write_then_read - SPI synchronous read followed by write */ -int spi_read_then_write(struct spi_slave *spi, const u8 *cmd, - size_t cmd_len, const u8 *data_out, - u8 *data_in, size_t data_len); +/** + * spi_write_then_read - SPI synchronous write followed by read + * + * This performs a half duplex transaction in which the first transaction + * is to send the opcode and if the length of buf is non-zero then it start + * the second transaction as tx or rx based on the need from respective slave. + * + * @slave: slave device with which opcode/data will be exchanged + * @opcode: opcode used for specific transfer + * @n_opcode: size of opcode, in bytes + * @txbuf: buffer into which data to be written + * @rxbuf: buffer into which data will be read + * @n_buf: size of buf (whether it's [tx|rx]buf), in bytes + * + * Returns: 0 on success, not 0 on failure + */ +int spi_write_then_read(struct spi_slave *slave, const u8 *opcode, + size_t n_opcode, const u8 *txbuf, u8 *rxbuf, + size_t n_buf);
/* Copy memory mapped data */ void spi_flash_copy_mmap(void *data, void *offset, size_t len);

mp2580 will take care of tx and rx mode's so there is no need to differentiate these into spi layer level hence replaced all mode_rx macros with mode.
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 --- drivers/mtd/spi-nor/m25p80.c | 2 +- drivers/spi/ich.c | 6 ++---- drivers/spi/spi-uclass.c | 11 ++++------- drivers/spi/ti_qspi.c | 6 +++--- include/spi.h | 14 ++++---------- 5 files changed, 14 insertions(+), 25 deletions(-)
diff --git a/drivers/mtd/spi-nor/m25p80.c b/drivers/mtd/spi-nor/m25p80.c index 7e2702d..58e879c 100644 --- a/drivers/mtd/spi-nor/m25p80.c +++ b/drivers/mtd/spi-nor/m25p80.c @@ -178,7 +178,7 @@ static int m25p80_spi_nor(struct spi_nor *nor) return ret; }
- switch (spi->mode_rx) { + switch (spi->mode) { case SPI_RX_SLOW: nor->read_mode = SNOR_READ; break; diff --git a/drivers/spi/ich.c b/drivers/spi/ich.c index e543b8f..5f03508 100644 --- a/drivers/spi/ich.c +++ b/drivers/spi/ich.c @@ -678,10 +678,8 @@ static int ich_spi_child_pre_probe(struct udevice *dev) * ICH 7 SPI controller only supports array read command * and byte program command for SST flash */ - if (plat->ich_version == PCHV_7) { - slave->mode_rx = SPI_RX_SLOW; - slave->mode = SPI_TX_BYTE; - } + if (plat->ich_version == PCHV_7) + slave->mode = SPI_TX_BYTE | SPI_RX_SLOW;
return 0; } diff --git a/drivers/spi/spi-uclass.c b/drivers/spi/spi-uclass.c index 0dfdd8b..ed6c771 100644 --- a/drivers/spi/spi-uclass.c +++ b/drivers/spi/spi-uclass.c @@ -181,7 +181,6 @@ static int spi_child_pre_probe(struct udevice *dev)
slave->max_hz = plat->max_hz; slave->mode = plat->mode; - slave->mode_rx = plat->mode_rx;
return 0; } @@ -393,7 +392,7 @@ void spi_free_slave(struct spi_slave *slave) int spi_slave_ofdata_to_platdata(const void *blob, int node, struct dm_spi_slave_platdata *plat) { - int mode = 0, mode_rx = 0; + int mode = 0; int value;
plat->cs = fdtdec_get_int(blob, node, "reg", -1); @@ -425,24 +424,22 @@ int spi_slave_ofdata_to_platdata(const void *blob, int node, break; }
- plat->mode = mode; - value = fdtdec_get_uint(blob, node, "spi-rx-bus-width", 1); switch (value) { case 1: break; case 2: - mode_rx |= SPI_RX_DUAL; + mode |= SPI_RX_DUAL; break; case 4: - mode_rx |= SPI_RX_QUAD; + mode |= SPI_RX_QUAD; break; default: error("spi-rx-bus-width %d not supported\n", value); break; }
- plat->mode_rx = mode_rx; + plat->mode = mode;
return 0; } diff --git a/drivers/spi/ti_qspi.c b/drivers/spi/ti_qspi.c index b5c974c..d9d65b4 100644 --- a/drivers/spi/ti_qspi.c +++ b/drivers/spi/ti_qspi.c @@ -338,7 +338,7 @@ static void ti_spi_setup_spi_register(struct ti_qspi_priv *priv) QSPI_SETUP0_NUM_D_BYTES_8_BITS | QSPI_SETUP0_READ_QUAD | QSPI_CMD_WRITE | QSPI_NUM_DUMMY_BITS); - slave->mode_rx = SPI_RX_QUAD; + slave->mode |= SPI_RX_QUAD; #else memval |= QSPI_CMD_READ | QSPI_SETUP0_NUM_A_BYTES | QSPI_SETUP0_NUM_D_BYTES_NO_BITS | @@ -422,7 +422,7 @@ static void __ti_qspi_setup_memorymap(struct ti_qspi_priv *priv, bool enable) { u32 memval; - u32 mode = slave->mode_rx & (SPI_RX_QUAD | SPI_RX_DUAL); + u32 mode = slave->mode & (SPI_RX_QUAD | SPI_RX_DUAL);
if (!enable) { writel(0, &priv->base->setup0); @@ -436,7 +436,7 @@ static void __ti_qspi_setup_memorymap(struct ti_qspi_priv *priv, memval |= QSPI_CMD_READ_QUAD; memval |= QSPI_SETUP0_NUM_D_BYTES_8_BITS; memval |= QSPI_SETUP0_READ_QUAD; - slave->mode_rx = SPI_RX_QUAD; + slave->mode |= SPI_RX_QUAD; break; case SPI_RX_DUAL: memval |= QSPI_CMD_READ_DUAL; diff --git a/include/spi.h b/include/spi.h index dd0b11b..61fefa4 100644 --- a/include/spi.h +++ b/include/spi.h @@ -26,12 +26,10 @@ #define SPI_TX_BYTE BIT(8) /* transmit with 1 wire byte */ #define SPI_TX_DUAL BIT(9) /* transmit with 2 wires */ #define SPI_TX_QUAD BIT(10) /* transmit with 4 wires */ - -/* SPI mode_rx flags */ -#define SPI_RX_SLOW BIT(0) /* receive with 1 wire slow */ -#define SPI_RX_FAST BIT(1) /* receive with 1 wire fast */ -#define SPI_RX_DUAL BIT(2) /* receive with 2 wires */ -#define SPI_RX_QUAD BIT(3) /* receive with 4 wires */ +#define SPI_RX_SLOW BIT(11) /* receive with 1 wire slow */ +#define SPI_RX_FAST BIT(12) /* receive with 1 wire fast */ +#define SPI_RX_DUAL BIT(13) /* receive with 2 wires */ +#define SPI_RX_QUAD BIT(14) /* receive with 4 wires */
/* SPI bus connection options - see enum spi_dual_flash */ #define SPI_CONN_DUAL_SHARED (1 << 0) @@ -61,13 +59,11 @@ struct dm_spi_bus { * @cs: Chip select number (0..n-1) * @max_hz: Maximum bus speed that this slave can tolerate * @mode: SPI mode to use for this device (see SPI mode flags) - * @mode_rx: SPI RX mode to use for this slave (see SPI mode_rx flags) */ struct dm_spi_slave_platdata { unsigned int cs; uint max_hz; uint mode; - u8 mode_rx; };
#endif /* CONFIG_DM_SPI */ @@ -94,7 +90,6 @@ struct dm_spi_slave_platdata { * bus (bus->seq) so does not need to be stored * @cs: ID of the chip select connected to the slave. * @mode: SPI mode to use for this slave (see SPI mode flags) - * @mode_rx: SPI RX mode to use for this slave (see SPI mode_rx flags) * @wordlen: Size of SPI word in number of bits * @max_write_size: If non-zero, the maximum number of bytes which can * be written at once, excluding command bytes. @@ -112,7 +107,6 @@ struct spi_slave { unsigned int cs; #endif uint mode; - u8 mode_rx; unsigned int wordlen; unsigned int max_write_size; void *memory_map;

SPI_RX_FAST at spi layer used for spi-nor core to find the fastest read mode, but this handling is taking care at m25p80 hence removed the same at spi layer level.
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 --- include/spi.h | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/include/spi.h b/include/spi.h index 61fefa4..9af2fbb 100644 --- a/include/spi.h +++ b/include/spi.h @@ -27,9 +27,8 @@ #define SPI_TX_DUAL BIT(9) /* transmit with 2 wires */ #define SPI_TX_QUAD BIT(10) /* transmit with 4 wires */ #define SPI_RX_SLOW BIT(11) /* receive with 1 wire slow */ -#define SPI_RX_FAST BIT(12) /* receive with 1 wire fast */ -#define SPI_RX_DUAL BIT(13) /* receive with 2 wires */ -#define SPI_RX_QUAD BIT(14) /* receive with 4 wires */ +#define SPI_RX_DUAL BIT(12) /* receive with 2 wires */ +#define SPI_RX_QUAD BIT(13) /* receive with 4 wires */
/* SPI bus connection options - see enum spi_dual_flash */ #define SPI_CONN_DUAL_SHARED (1 << 0)

Renamed SPI_FLASH_BAR to SPI_NOR_BAR
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 --- drivers/mtd/spi-nor/spi-nor.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c index 3b42b69..32d5cbc 100644 --- a/drivers/mtd/spi-nor/spi-nor.c +++ b/drivers/mtd/spi-nor/spi-nor.c @@ -181,7 +181,7 @@ static int spi_nor_wait_till_ready(struct spi_nor *nor, unsigned long timeout) return -ETIMEDOUT; }
-#ifdef CONFIG_SPI_FLASH_BAR +#ifdef CONFIG_SPI_NOR_BAR static int spi_nor_write_bar(struct spi_nor *nor, u32 offset) { u8 bank_sel; @@ -514,7 +514,7 @@ static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr) if (nor->dual > SNOR_DUAL_SINGLE) spi_nor_dual(nor, &erase_addr); #endif -#ifdef CONFIG_SPI_FLASH_BAR +#ifdef CONFIG_SPI_NOR_BAR ret = spi_nor_write_bar(nor, erase_addr); if (ret < 0) return ret; @@ -577,7 +577,7 @@ static int spi_nor_write(struct mtd_info *mtd, loff_t offset, size_t len, if (nor->dual > SNOR_DUAL_SINGLE) spi_nor_dual(nor, &write_addr); #endif -#ifdef CONFIG_SPI_FLASH_BAR +#ifdef CONFIG_SPI_NOR_BAR ret = spi_nor_write_bar(nor, write_addr); if (ret < 0) return ret; @@ -647,7 +647,7 @@ static int spi_nor_read(struct mtd_info *mtd, loff_t from, size_t len, if (nor->dual > SNOR_DUAL_SINGLE) spi_nor_dual(nor, &read_addr); #endif -#ifdef CONFIG_SPI_FLASH_BAR +#ifdef CONFIG_SPI_NOR_BAR ret = spi_nor_write_bar(nor, read_addr); if (ret < 0) return ret; @@ -1103,7 +1103,7 @@ int spi_nor_scan(struct spi_nor *nor) }
/* Configure the BAR - discover bank cmds and read current bank */ -#ifdef CONFIG_SPI_FLASH_BAR +#ifdef CONFIG_SPI_NOR_BAR ret = spi_nor_read_bar(nor, info); if (ret < 0) return ret; @@ -1127,13 +1127,13 @@ int spi_nor_scan(struct spi_nor *nor) puts("\n"); #endif
-#ifndef CONFIG_SPI_FLASH_BAR +#ifndef CONFIG_SPI_NOR_BAR if (((nor->dual == SNOR_DUAL_SINGLE) && (mtd->size > SNOR_16MB_BOUN)) || ((nor->dual > SNOR_DUAL_SINGLE) && (mtd->size > SNOR_16MB_BOUN << 1))) { puts("spi-nor: Warning - Only lower 16MiB accessible,"); - puts(" Full access #define CONFIG_SPI_FLASH_BAR\n"); + puts(" Full access #define CONFIG_SPI_NOR_BAR\n"); } #endif

Added kconfig entry for SPI_NOR_BAR
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 --- drivers/mtd/spi-nor/Kconfig | 7 +++++++ 1 file changed, 7 insertions(+)
diff --git a/drivers/mtd/spi-nor/Kconfig b/drivers/mtd/spi-nor/Kconfig index 59bb943..43f59b7 100644 --- a/drivers/mtd/spi-nor/Kconfig +++ b/drivers/mtd/spi-nor/Kconfig @@ -49,4 +49,11 @@ config MTD_SPI_NOR_USE_4K_SECTORS Please note that some tools/drivers/filesystems may not work with 4096 B erase size (e.g. UBIFS requires 15 KiB as a minimum).
+config SPI_NOR_BAR + bool "SPI NOR Bank/Extended address register support" + help + Enable the SPI NOR Bank/Extended address register support. + Bank/Extended address registers are used to access the flash + which has size > 16MiB in 3-byte addressing. + endif # MTD_SPI_NOR

Copy spl files from drivers/mtd/spi to spi-nor, more changes will added on future patches.
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 --- drivers/mtd/spi-nor/Makefile | 5 +++ drivers/mtd/spi-nor/fsl_espi_spl.c | 90 ++++++++++++++++++++++++++++++++++++++ drivers/mtd/spi-nor/spi_spl_load.c | 90 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 185 insertions(+) create mode 100644 drivers/mtd/spi-nor/fsl_espi_spl.c create mode 100644 drivers/mtd/spi-nor/spi_spl_load.c
diff --git a/drivers/mtd/spi-nor/Makefile b/drivers/mtd/spi-nor/Makefile index 71e7ae2..4d27811 100644 --- a/drivers/mtd/spi-nor/Makefile +++ b/drivers/mtd/spi-nor/Makefile @@ -3,6 +3,11 @@ # # SPDX-License-Identifier: GPL-2.0+
+ifdef CONFIG_SPL_BUILD +obj-$(CONFIG_SPL_SPI_LOAD) += spi_spl_load.o +obj-$(CONFIG_SPL_SPI_BOOT) += fsl_espi_spl.o +endif + ifdef CONFIG_MTD_SPI_NOR obj-y += spi-nor.o obj-y += spi-nor-ids.o diff --git a/drivers/mtd/spi-nor/fsl_espi_spl.c b/drivers/mtd/spi-nor/fsl_espi_spl.c new file mode 100644 index 0000000..b915469 --- /dev/null +++ b/drivers/mtd/spi-nor/fsl_espi_spl.c @@ -0,0 +1,90 @@ +/* + * Copyright 2013 Freescale Semiconductor, Inc. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <spi_flash.h> +#include <malloc.h> + +#define ESPI_BOOT_IMAGE_SIZE 0x48 +#define ESPI_BOOT_IMAGE_ADDR 0x50 +#define CONFIG_CFG_DATA_SECTOR 0 + +void spi_spl_load_image(uint32_t offs, unsigned int size, void *vdst) +{ + struct spi_flash *flash; + + flash = spi_flash_probe(CONFIG_ENV_SPI_BUS, CONFIG_ENV_SPI_CS, + CONFIG_ENV_SPI_MAX_HZ, CONFIG_ENV_SPI_MODE); + if (flash == NULL) { + puts("\nspi_flash_probe failed"); + hang(); + } + + spi_flash_read(flash, offs, size, vdst); +} + +/* + * The main entry for SPI booting. It's necessary that SDRAM is already + * configured and available since this code loads the main U-Boot image + * from SPI into SDRAM and starts it from there. + */ +void spi_boot(void) +{ + void (*uboot)(void) __noreturn; + u32 offset, code_len, copy_len = 0; +#ifndef CONFIG_FSL_CORENET + unsigned char *buf = NULL; +#endif + struct spi_flash *flash; + + flash = spi_flash_probe(CONFIG_ENV_SPI_BUS, CONFIG_ENV_SPI_CS, + CONFIG_ENV_SPI_MAX_HZ, CONFIG_ENV_SPI_MODE); + if (flash == NULL) { + puts("\nspi_flash_probe failed"); + hang(); + } + +#ifdef CONFIG_FSL_CORENET + offset = CONFIG_SYS_SPI_FLASH_U_BOOT_OFFS; + code_len = CONFIG_SYS_SPI_FLASH_U_BOOT_SIZE; +#else + /* + * Load U-Boot image from SPI flash into RAM + */ + buf = malloc(flash->page_size); + if (buf == NULL) { + puts("\nmalloc failed"); + hang(); + } + memset(buf, 0, flash->page_size); + + spi_flash_read(flash, CONFIG_CFG_DATA_SECTOR, + flash->page_size, (void *)buf); + offset = *(u32 *)(buf + ESPI_BOOT_IMAGE_ADDR); + /* Skip spl code */ + offset += CONFIG_SYS_SPI_FLASH_U_BOOT_OFFS; + /* Get the code size from offset 0x48 */ + code_len = *(u32 *)(buf + ESPI_BOOT_IMAGE_SIZE); + /* Skip spl code */ + code_len = code_len - CONFIG_SPL_MAX_SIZE; +#endif + /* copy code to DDR */ + printf("Loading second stage boot loader "); + while (copy_len <= code_len) { + spi_flash_read(flash, offset + copy_len, 0x2000, + (void *)(CONFIG_SYS_SPI_FLASH_U_BOOT_DST + + copy_len)); + copy_len = copy_len + 0x2000; + putc('.'); + } + + /* + * Jump to U-Boot image + */ + flush_cache(CONFIG_SYS_SPI_FLASH_U_BOOT_DST, code_len); + uboot = (void *)CONFIG_SYS_SPI_FLASH_U_BOOT_START; + (*uboot)(); +} diff --git a/drivers/mtd/spi-nor/spi_spl_load.c b/drivers/mtd/spi-nor/spi_spl_load.c new file mode 100644 index 0000000..ca56fe9 --- /dev/null +++ b/drivers/mtd/spi-nor/spi_spl_load.c @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2011 OMICRON electronics GmbH + * + * based on drivers/mtd/nand/nand_spl_load.c + * + * Copyright (C) 2011 + * Heiko Schocher, DENX Software Engineering, hs@denx.de. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <spi.h> +#include <spi_flash.h> +#include <errno.h> +#include <spl.h> + +#ifdef CONFIG_SPL_OS_BOOT +/* + * Load the kernel, check for a valid header we can parse, and if found load + * the kernel and then device tree. + */ +static int spi_load_image_os(struct spi_flash *flash, + struct image_header *header) +{ + /* Read for a header, parse or error out. */ + spi_flash_read(flash, CONFIG_SYS_SPI_KERNEL_OFFS, 0x40, + (void *)header); + + if (image_get_magic(header) != IH_MAGIC) + return -1; + + spl_parse_image_header(header); + + spi_flash_read(flash, CONFIG_SYS_SPI_KERNEL_OFFS, + spl_image.size, (void *)spl_image.load_addr); + + /* Read device tree. */ + spi_flash_read(flash, CONFIG_SYS_SPI_ARGS_OFFS, + CONFIG_SYS_SPI_ARGS_SIZE, + (void *)CONFIG_SYS_SPL_ARGS_ADDR); + + return 0; +} +#endif + +/* + * The main entry for SPI booting. It's necessary that SDRAM is already + * configured and available since this code loads the main U-Boot image + * from SPI into SDRAM and starts it from there. + */ +int spl_spi_load_image(void) +{ + int err = 0; + struct spi_flash *flash; + struct image_header *header; + + /* + * Load U-Boot image from SPI flash into RAM + */ + + flash = spi_flash_probe(CONFIG_SF_DEFAULT_BUS, + CONFIG_SF_DEFAULT_CS, + CONFIG_SF_DEFAULT_SPEED, + CONFIG_SF_DEFAULT_MODE); + if (!flash) { + puts("SPI probe failed.\n"); + return -ENODEV; + } + + /* use CONFIG_SYS_TEXT_BASE as temporary storage area */ + header = (struct image_header *)(CONFIG_SYS_TEXT_BASE); + +#ifdef CONFIG_SPL_OS_BOOT + if (spl_start_uboot() || spi_load_image_os(flash, header)) +#endif + { + /* Load u-boot, mkimage header is 64 bytes. */ + err = spi_flash_read(flash, CONFIG_SYS_SPI_U_BOOT_OFFS, 0x40, + (void *)header); + if (err) + return err; + + spl_parse_image_header(header); + err = spi_flash_read(flash, CONFIG_SYS_SPI_U_BOOT_OFFS, + spl_image.size, (void *)spl_image.load_addr); + } + + return err; +}

Use ascending order while including headers files.
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 --- drivers/mtd/spi-nor/fsl_espi_spl.c | 2 +- drivers/mtd/spi-nor/spi_spl_load.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/mtd/spi-nor/fsl_espi_spl.c b/drivers/mtd/spi-nor/fsl_espi_spl.c index b915469..7c40245 100644 --- a/drivers/mtd/spi-nor/fsl_espi_spl.c +++ b/drivers/mtd/spi-nor/fsl_espi_spl.c @@ -5,8 +5,8 @@ */
#include <common.h> -#include <spi_flash.h> #include <malloc.h> +#include <spi_flash.h>
#define ESPI_BOOT_IMAGE_SIZE 0x48 #define ESPI_BOOT_IMAGE_ADDR 0x50 diff --git a/drivers/mtd/spi-nor/spi_spl_load.c b/drivers/mtd/spi-nor/spi_spl_load.c index ca56fe9..285b6da 100644 --- a/drivers/mtd/spi-nor/spi_spl_load.c +++ b/drivers/mtd/spi-nor/spi_spl_load.c @@ -10,9 +10,9 @@ */
#include <common.h> +#include <errno.h> #include <spi.h> #include <spi_flash.h> -#include <errno.h> #include <spl.h>
#ifdef CONFIG_SPL_OS_BOOT

Replace spi_flash{} with mtd_info{}
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 --- drivers/mtd/spi-nor/fsl_espi_spl.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/mtd/spi-nor/fsl_espi_spl.c b/drivers/mtd/spi-nor/fsl_espi_spl.c index 7c40245..93b0b2e 100644 --- a/drivers/mtd/spi-nor/fsl_espi_spl.c +++ b/drivers/mtd/spi-nor/fsl_espi_spl.c @@ -14,7 +14,7 @@
void spi_spl_load_image(uint32_t offs, unsigned int size, void *vdst) { - struct spi_flash *flash; + spi_flash_t *flash;
flash = spi_flash_probe(CONFIG_ENV_SPI_BUS, CONFIG_ENV_SPI_CS, CONFIG_ENV_SPI_MAX_HZ, CONFIG_ENV_SPI_MODE); @@ -38,7 +38,7 @@ void spi_boot(void) #ifndef CONFIG_FSL_CORENET unsigned char *buf = NULL; #endif - struct spi_flash *flash; + spi_flash_t *flash;
flash = spi_flash_probe(CONFIG_ENV_SPI_BUS, CONFIG_ENV_SPI_CS, CONFIG_ENV_SPI_MAX_HZ, CONFIG_ENV_SPI_MODE);

Replace spi_flash{} with mtd_info{}
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 --- drivers/mtd/spi-nor/spi_spl_load.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/drivers/mtd/spi-nor/spi_spl_load.c b/drivers/mtd/spi-nor/spi_spl_load.c index 285b6da..9f33826 100644 --- a/drivers/mtd/spi-nor/spi_spl_load.c +++ b/drivers/mtd/spi-nor/spi_spl_load.c @@ -20,8 +20,7 @@ * Load the kernel, check for a valid header we can parse, and if found load * the kernel and then device tree. */ -static int spi_load_image_os(struct spi_flash *flash, - struct image_header *header) +static int spi_load_image_os(spi_flash_t *flash, struct image_header *header) { /* Read for a header, parse or error out. */ spi_flash_read(flash, CONFIG_SYS_SPI_KERNEL_OFFS, 0x40, @@ -52,7 +51,7 @@ static int spi_load_image_os(struct spi_flash *flash, int spl_spi_load_image(void) { int err = 0; - struct spi_flash *flash; + spi_flash_t *flash; struct image_header *header;
/*

Added flash vendor kconfig entries from drivers/mtd/spi
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 --- drivers/mtd/spi-nor/Kconfig | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+)
diff --git a/drivers/mtd/spi-nor/Kconfig b/drivers/mtd/spi-nor/Kconfig index 43f59b7..219306f 100644 --- a/drivers/mtd/spi-nor/Kconfig +++ b/drivers/mtd/spi-nor/Kconfig @@ -56,4 +56,44 @@ config SPI_NOR_BAR Bank/Extended address registers are used to access the flash which has size > 16MiB in 3-byte addressing.
+config SPI_FLASH_ATMEL + bool "Atmel SPI flash support" + help + Add support for various Atmel SPI flash chips (AT45xxx and AT25xxx) + +config SPI_FLASH_EON + bool "EON SPI flash support" + help + Add support for various EON SPI flash chips (EN25xxx) + +config SPI_FLASH_GIGADEVICE + bool "GigaDevice SPI flash support" + help + Add support for various GigaDevice SPI flash chips (GD25xxx) + +config SPI_FLASH_MACRONIX + bool "Macronix SPI flash support" + help + Add support for various Macronix SPI flash chips (MX25Lxxx) + +config SPI_FLASH_SPANSION + bool "Spansion SPI flash support" + help + Add support for various Spansion SPI flash chips (S25FLxxx) + +config SPI_FLASH_STMICRO + bool "STMicro SPI flash support" + help + Add support for various STMicro SPI flash chips (M25Pxxx and N25Qxxx) + +config SPI_FLASH_SST + bool "SST SPI flash support" + help + Add support for various SST SPI flash chips (SST25xxx) + +config SPI_FLASH_WINBOND + bool "Winbond SPI flash support" + help + Add support for various Winbond SPI flash chips (W25xxx) + endif # MTD_SPI_NOR

Since SPI-NOR core relies on MTD uclass.
Cc: Simon Glass sjg@chromium.org Cc: Bin Meng bmeng.cn@gmail.com Cc: Michal Simek michal.simek@xilinx.com Cc: Siva Durga Prasad Paladugu sivadur@xilinx.com Signed-off-by: Jagan Teki jteki@openedev.com --- arch/arm/Kconfig | 1 + 1 file changed, 1 insertion(+)
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index d2dbb1a..2dc2191 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -565,6 +565,7 @@ config ARCH_ZYNQ select DM_MMC select DM_SPI select DM_SERIAL + select MTD select DM_SPI_FLASH select SPL_SEPARATE_BSS if SPL

Drop using legacy DM_SPI_FLASH.
Cc: Simon Glass sjg@chromium.org Cc: Bin Meng bmeng.cn@gmail.com Cc: Michal Simek michal.simek@xilinx.com Cc: Siva Durga Prasad Paladugu sivadur@xilinx.com Signed-off-by: Jagan Teki jteki@openedev.com --- arch/arm/Kconfig | 1 - 1 file changed, 1 deletion(-)
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 2dc2191..b26dddb 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -566,7 +566,6 @@ config ARCH_ZYNQ select DM_SPI select DM_SERIAL select MTD - select DM_SPI_FLASH select SPL_SEPARATE_BSS if SPL
config ARCH_ZYNQMP

Drop using legacy spi_flash core.
Cc: Simon Glass sjg@chromium.org Cc: Bin Meng bmeng.cn@gmail.com Cc: Michal Simek michal.simek@xilinx.com Cc: Siva Durga Prasad Paladugu sivadur@xilinx.com Signed-off-by: Jagan Teki jteki@openedev.com --- configs/zynq_microzed_defconfig | 1 - 1 file changed, 1 deletion(-)
diff --git a/configs/zynq_microzed_defconfig b/configs/zynq_microzed_defconfig index a3a66ec..c181537 100644 --- a/configs/zynq_microzed_defconfig +++ b/configs/zynq_microzed_defconfig @@ -13,7 +13,6 @@ CONFIG_CMD_GPIO=y # CONFIG_CMD_SETEXPR is not set CONFIG_NET_RANDOM_ETHADDR=y CONFIG_SPL_DM_SEQ_ALIAS=y -CONFIG_SPI_FLASH=y CONFIG_SPI_FLASH_SPANSION=y CONFIG_SPI_FLASH_STMICRO=y CONFIG_SPI_FLASH_WINBOND=y

Use m25p80 which is flash interface layer between spi-nor core vs drivers/spi
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 --- configs/zynq_microzed_defconfig | 1 + 1 file changed, 1 insertion(+)
diff --git a/configs/zynq_microzed_defconfig b/configs/zynq_microzed_defconfig index c181537..79f2913 100644 --- a/configs/zynq_microzed_defconfig +++ b/configs/zynq_microzed_defconfig @@ -18,3 +18,4 @@ CONFIG_SPI_FLASH_STMICRO=y CONFIG_SPI_FLASH_WINBOND=y CONFIG_ZYNQ_GEM=y CONFIG_ZYNQ_QSPI=y +CONFIG_MTD_M25P80=y

Enabled SPI-NOR core support
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 --- configs/zynq_microzed_defconfig | 1 + 1 file changed, 1 insertion(+)
diff --git a/configs/zynq_microzed_defconfig b/configs/zynq_microzed_defconfig index 79f2913..db02ba3 100644 --- a/configs/zynq_microzed_defconfig +++ b/configs/zynq_microzed_defconfig @@ -19,3 +19,4 @@ CONFIG_SPI_FLASH_WINBOND=y CONFIG_ZYNQ_GEM=y CONFIG_ZYNQ_QSPI=y CONFIG_MTD_M25P80=y +CONFIG_MTD_SPI_NOR=y

Add SPL support for SPI-NOR flash.
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 --- drivers/Makefile | 1 + 1 file changed, 1 insertion(+)
diff --git a/drivers/Makefile b/drivers/Makefile index e7eab66..1d179b9 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -18,6 +18,7 @@ obj-$(CONFIG_ARMADA_38X) += ddr/marvell/a38x/ obj-$(CONFIG_ARMADA_XP) += ddr/marvell/axp/ obj-$(CONFIG_ALTERA_SDRAM) += ddr/altera/ obj-$(CONFIG_SPL_SERIAL_SUPPORT) += serial/ +obj-$(CONFIG_SPL_SPI_NOR_SUPPORT) += mtd/spi-nor/ obj-$(CONFIG_SPL_SPI_FLASH_SUPPORT) += mtd/spi/ obj-$(CONFIG_SPL_SPI_SUPPORT) += spi/ obj-$(CONFIG_SPL_POWER_SUPPORT) += power/ power/pmic/

Use SPI-NOR SPL support for zynq boards.
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 --- include/configs/zynq-common.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/include/configs/zynq-common.h b/include/configs/zynq-common.h index e8c3ef0..7427d22 100644 --- a/include/configs/zynq-common.h +++ b/include/configs/zynq-common.h @@ -346,7 +346,7 @@ #ifdef CONFIG_ZYNQ_QSPI #define CONFIG_SPL_SPI_SUPPORT #define CONFIG_SPL_SPI_LOAD -#define CONFIG_SPL_SPI_FLASH_SUPPORT +#define CONFIG_SPL_SPI_NOR_SUPPORT #define CONFIG_SYS_SPI_U_BOOT_OFFS 0x100000 #define CONFIG_SYS_SPI_ARGS_OFFS 0x200000 #define CONFIG_SYS_SPI_ARGS_SIZE 0x80000

Since SPI-NOR depends on MTD core enable the same for SPI-NOR SPL.
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 --- include/configs/zynq-common.h | 1 + 1 file changed, 1 insertion(+)
diff --git a/include/configs/zynq-common.h b/include/configs/zynq-common.h index 7427d22..582b2a3 100644 --- a/include/configs/zynq-common.h +++ b/include/configs/zynq-common.h @@ -347,6 +347,7 @@ #define CONFIG_SPL_SPI_SUPPORT #define CONFIG_SPL_SPI_LOAD #define CONFIG_SPL_SPI_NOR_SUPPORT +#define CONFIG_SPL_MTD_SUPPORT #define CONFIG_SYS_SPI_U_BOOT_OFFS 0x100000 #define CONFIG_SYS_SPI_ARGS_OFFS 0x200000 #define CONFIG_SYS_SPI_ARGS_SIZE 0x80000
participants (1)
-
Jagan Teki