summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README6
-rw-r--r--common/cmd_sf.c14
-rw-r--r--doc/SPI/README.dual-flash92
-rw-r--r--doc/SPI/README.ftssp010_spi_test41
-rw-r--r--doc/SPI/status.txt11
-rw-r--r--drivers/mtd/spi/Makefile4
-rw-r--r--drivers/mtd/spi/sf.c4
-rw-r--r--drivers/mtd/spi/sf_internal.h34
-rw-r--r--drivers/mtd/spi/sf_ops.c157
-rw-r--r--drivers/mtd/spi/sf_params.c130
-rw-r--r--drivers/mtd/spi/sf_probe.c274
-rw-r--r--drivers/spi/Makefile1
-rw-r--r--drivers/spi/ftssp010_spi.c508
-rw-r--r--drivers/spi/sh_spi.c10
-rw-r--r--include/spi.h26
-rw-r--r--include/spi_flash.h57
16 files changed, 1171 insertions, 198 deletions
diff --git a/README b/README
index a0646c3..aea82be 100644
--- a/README
+++ b/README
@@ -2756,6 +2756,12 @@ CBFS (Coreboot Filesystem) support
Define this option to use the Bank addr/Extended addr
support on SPI flashes which has size > 16Mbytes.
+ CONFIG_SF_DUAL_FLASH Dual flash memories
+
+ Define this option to use dual flash support where two flash
+ memories can be connected with a given cs line.
+ currently Xilinx Zynq qspi support these type of connections.
+
- SystemACE Support:
CONFIG_SYSTEMACE
diff --git a/common/cmd_sf.c b/common/cmd_sf.c
index 3994b06..b4ceb71 100644
--- a/common/cmd_sf.c
+++ b/common/cmd_sf.c
@@ -358,7 +358,8 @@ static void show_time(struct test_info *test, int stage)
int bps; /* Bits per second */
speed = (long long)test->bytes * 1000;
- do_div(speed, test->time_ms[stage] * 1024);
+ if (test->time_ms[stage])
+ do_div(speed, test->time_ms[stage] * 1024);
bps = speed * 8;
printf("%d %s: %d ticks, %d KiB/s %d.%03d Mbps\n", stage,
@@ -446,11 +447,13 @@ static int do_spi_flash_test(int argc, char * const argv[])
{
unsigned long offset;
unsigned long len;
- uint8_t *buf = (uint8_t *)CONFIG_SYS_TEXT_BASE;
+ uint8_t *buf, *from;
char *endp;
uint8_t *vbuf;
int ret;
+ if (argc < 3)
+ return -1;
offset = simple_strtoul(argv[1], &endp, 16);
if (*argv[1] == 0 || *endp != 0)
return -1;
@@ -460,17 +463,18 @@ static int do_spi_flash_test(int argc, char * const argv[])
vbuf = malloc(len);
if (!vbuf) {
- printf("Cannot allocate memory\n");
+ printf("Cannot allocate memory (%lu bytes)\n", len);
return 1;
}
buf = malloc(len);
if (!buf) {
free(vbuf);
- printf("Cannot allocate memory\n");
+ printf("Cannot allocate memory (%lu bytes)\n", len);
return 1;
}
- memcpy(buf, (char *)CONFIG_SYS_TEXT_BASE, len);
+ from = map_sysmem(CONFIG_SYS_TEXT_BASE, 0);
+ memcpy(buf, from, len);
ret = spi_flash_test(flash, buf, len, offset, vbuf);
free(vbuf);
free(buf);
diff --git a/doc/SPI/README.dual-flash b/doc/SPI/README.dual-flash
new file mode 100644
index 0000000..6c88d65
--- /dev/null
+++ b/doc/SPI/README.dual-flash
@@ -0,0 +1,92 @@
+SPI/QSPI Dual flash connection modes:
+=====================================
+
+This describes how SPI/QSPI flash memories are connected to a given
+controller in a single chip select line.
+
+Current spi_flash framework supports, single flash memory connected
+to a given controller with single chip select line, but there are some
+hw logics(ex: xilinx zynq qspi) that describes two/dual memories are
+connected with a single chip select line from a controller.
+
+"dual_flash" from include/spi.h describes these types of connection mode
+
+Possible connections:
+--------------------
+SF_SINGLE_FLASH:
+ - single spi flash memory connected with single chip select line.
+
+ +------------+ CS +---------------+
+ | |----------------------->| |
+ | Controller | I0[3:0] | Flash memory |
+ | SPI/QSPI |<======================>| (SPI/QSPI) |
+ | | CLK | |
+ | |----------------------->| |
+ +------------+ +---------------+
+
+SF_DUAL_STACKED_FLASH:
+ - dual spi/qspi flash memories are connected with a single chipselect
+ line and these two memories are operating stacked fasion with shared buses.
+ - xilinx zynq qspi controller has implemented this feature [1]
+
+ +------------+ CS +---------------+
+ | |---------------------->| |
+ | | I0[3:0] | Upper Flash |
+ | | +=========>| memory |
+ | | | CLK | (SPI/QSPI) |
+ | | | +---->| |
+ | Controller | CS | | +---------------+
+ | SPI/QSPI |------------|----|---->| |
+ | | I0[3:0] | | | Lower Flash |
+ | |<===========+====|====>| memory |
+ | | CLK | | (SPI/QSPI) |
+ | |-----------------+---->| |
+ +------------+ +---------------+
+
+ - two memory flash devices should has same hw part attributes (like size,
+ vendor..etc)
+ - Configurations:
+ on LQSPI_CFG register, Enable TWO_MEM[BIT:30] on LQSPI_CFG
+ Enable U_PAGE[BIT:28] if U_PAGE flag set - upper memory
+ Disable U_PAGE[BIT:28] if U_PAGE flag unset - lower memory
+ - Operation:
+ accessing memories serially like one after another.
+ by default, if U_PAGE is unset lower memory should accessible,
+ once user wants to access upper memory need to set U_PAGE.
+
+SPI_FLASH_CONN_DUALPARALLEL:
+ - dual spi/qspi flash memories are connected with a single chipselect
+ line and these two memories are operating parallel with separate buses.
+ - xilinx zynq qspi controller has implemented this feature [1]
+
+ +-------------+ CS +---------------+
+ | |---------------------->| |
+ | | I0[3:0] | Upper Flash |
+ | |<=====================>| memory |
+ | | CLK | (SPI/QSPI) |
+ | |---------------------->| |
+ | Controller | CS +---------------+
+ | SPI/QSPI |---------------------->| |
+ | | I0[3:0] | Lower Flash |
+ | |<=====================>| memory |
+ | | CLK | (SPI/QSPI) |
+ | |---------------------->| |
+ +-------------+ +---------------+
+
+ - two memory flash devices should has same hw part attributes (like size,
+ vendor..etc)
+ - Configurations:
+ Need to enable SEP_BUS[BIT:29],TWO_MEM[BIT:30] on LQSPI_CFG register.
+ - Operation:
+ Even bits, i.e. bit 0, 2, 4 ., of a data word is located in the lower memory
+ and odd bits, i.e. bit 1, 3, 5, ., of a data word is located in the upper memory.
+
+Note: Technically there is only one CS line from the controller, but
+zynq qspi controller has an internal hw logic to enable additional CS
+when controller is configured for dual memories.
+
+[1] http://www.xilinx.com/support/documentation/user_guides/ug585-Zynq-7000-TRM.pdf
+
+--
+Jagannadha Sutradharudu Teki <jaganna@xilinx.com>
+05-01-2014.
diff --git a/doc/SPI/README.ftssp010_spi_test b/doc/SPI/README.ftssp010_spi_test
new file mode 100644
index 0000000..1d86f36
--- /dev/null
+++ b/doc/SPI/README.ftssp010_spi_test
@@ -0,0 +1,41 @@
+SPI Flash test on Faraday A369 EVB:
+==================================
+
+U-Boot 2014.01-rc2-g3444b6f (Dec 20 2013 - 10:58:40)
+
+CPU: FA626TE 528 MHz
+AHB: 132 MHz
+APB: 66 MHz
+I2C: ready
+DRAM: 256 MiB
+MMU: on
+NAND: 512 MiB
+MMC: ftsdc010: 0
+*** Warning - bad CRC, using default environment
+
+In: serial
+Out: serial
+Err: serial
+Net: FTGMAC100#0
+Hit any key to stop autoboot: 0
+=> sf probe 0:0
+SF: Detected MX25L1605D with page size 256 Bytes, erase size 64 KiB, total 2 MiB
+=> sf read 0x10800000 0 0x400
+SF: 1024 bytes @ 0x0 Read: OK
+=> md 0x10800000
+10800000: ea000013 e59ff014 e59ff014 e59ff014 ................
+10800010: e59ff014 e59ff014 e59ff014 e59ff014 ................
+10800020: 1ff7b0c0 1ff7b120 1ff7b180 1ff7b1e0 .... ...........
+10800030: 1ff7b240 1ff7b2a0 1ff7b300 deadbeef @...............
+10800040: 10800000 0002c1f0 0007409c 00032048 .........@..H ..
+10800050: 1fd6af40 e10f0000 e3c0001f e38000d3 @...............
+10800060: e129f000 eb000001 eb000223 e12fff1e ..).....#...../.
+10800070: e3a00000 ee070f1e ee080f17 ee070f15 ................
+10800080: ee070f9a ee110f10 e3c00c03 e3c00087 ................
+10800090: e3c00a02 e3800002 e3800a01 ee010f10 ................
+108000a0: e1a0c00e eb007a68 e1a0e00c e1a0f00e ....hz..........
+108000b0: e1a00000 e1a00000 e1a00000 e1a00000 ................
+108000c0: e51fd078 e58de000 e14fe000 e58de004 x.........O.....
+108000d0: e3a0d013 e169f00d e1a0e00f e1b0f00e ......i.........
+108000e0: e24dd048 e88d1fff e51f20a0 e892000c H.M...... ......
+108000f0: e28d0048 e28d5034 e1a0100e e885000f H...4P..........
diff --git a/doc/SPI/status.txt b/doc/SPI/status.txt
index 62c3c85..13889f5 100644
--- a/doc/SPI/status.txt
+++ b/doc/SPI/status.txt
@@ -11,6 +11,11 @@ SPI FLASH (drivers/mtd/spi):
- Bank Address Register (Accessing flashes > 16Mbytes in 3-byte addressing)
- Added memory_mapped support for read operations.
- Common probe support for all supported flash vendors except, ramtron.
+- Extended read commands support(dual read, dual IO read)
+- Quad Page Program support.
+- Quad Read support(quad fast read, quad IO read)
+- Dual flash connection topology support(accessing two spi flash memories with single cs)
+- Banking support on dual flash connection topology.
SPI DRIVERS (drivers/spi):
-
@@ -18,14 +23,10 @@ SPI DRIVERS (drivers/spi):
TODO:
- Runtime detection of spi_flash params, SFDP(if possible)
- Add support for multibus build/accessing.
-- Extended read commands support(dual read, dual IO read)
-- Quad Page Program support.
-- Quad Read support(quad fast read, quad IO read)
-- Dual flash connection topology support(accessing two spi flash memories with single cs)
-- Banking support on dual flash connection topology.
- Need proper cleanups on spi_flash and drivers.
--
Jagannadha Sutradharudu Teki <jagannadh.teki@gmail.com>
18-09-2013.
07-10-2013.
+08-01-2014.
diff --git a/drivers/mtd/spi/Makefile b/drivers/mtd/spi/Makefile
index 26483a2..9e18fb4 100644
--- a/drivers/mtd/spi/Makefile
+++ b/drivers/mtd/spi/Makefile
@@ -10,8 +10,8 @@ obj-$(CONFIG_SPL_SPI_LOAD) += spi_spl_load.o
obj-$(CONFIG_SPL_SPI_BOOT) += fsl_espi_spl.o
endif
-obj-$(CONFIG_CMD_SF) += sf.o
-obj-$(CONFIG_SPI_FLASH) += sf_probe.o sf_ops.o
+obj-$(CONFIG_CMD_SF) += sf.o
+obj-$(CONFIG_SPI_FLASH) += sf_params.o sf_probe.o sf_ops.o
obj-$(CONFIG_SPI_FRAM_RAMTRON) += ramtron.o
obj-$(CONFIG_SPI_FLASH_SANDBOX) += sandbox.o
obj-$(CONFIG_SPI_M95XXX) += eeprom_m95xxx.o
diff --git a/drivers/mtd/spi/sf.c b/drivers/mtd/spi/sf.c
index d5e175c..664e860 100644
--- a/drivers/mtd/spi/sf.c
+++ b/drivers/mtd/spi/sf.c
@@ -18,6 +18,10 @@ static int spi_flash_read_write(struct spi_slave *spi,
unsigned long flags = SPI_XFER_BEGIN;
int ret;
+#ifdef CONFIG_SF_DUAL_FLASH
+ if (spi->flags & SPI_XFER_U_PAGE)
+ flags |= SPI_XFER_U_PAGE;
+#endif
if (data_len == 0)
flags |= SPI_XFER_END;
diff --git a/drivers/mtd/spi/sf_internal.h b/drivers/mtd/spi/sf_internal.h
index d291746..6bcd522 100644
--- a/drivers/mtd/spi/sf_internal.h
+++ b/drivers/mtd/spi/sf_internal.h
@@ -10,12 +10,15 @@
#ifndef _SF_INTERNAL_H_
#define _SF_INTERNAL_H_
+#define SPI_FLASH_3B_ADDR_LEN 3
+#define SPI_FLASH_CMD_LEN (1 + SPI_FLASH_3B_ADDR_LEN)
#define SPI_FLASH_16MB_BOUN 0x1000000
-/* SECT flags */
-#define SECT_4K (1 << 1)
-#define SECT_32K (1 << 2)
-#define E_FSR (1 << 3)
+/* CFI Manufacture ID's */
+#define SPI_FLASH_CFI_MFR_SPANSION 0x01
+#define SPI_FLASH_CFI_MFR_STMICRO 0x20
+#define SPI_FLASH_CFI_MFR_MACRONIX 0xc2
+#define SPI_FLASH_CFI_MFR_WINBOND 0xef
/* Erase commands */
#define CMD_ERASE_4K 0x20
@@ -28,6 +31,7 @@
#define CMD_PAGE_PROGRAM 0x02
#define CMD_WRITE_DISABLE 0x04
#define CMD_READ_STATUS 0x05
+#define CMD_QUAD_PAGE_PROGRAM 0x32
#define CMD_READ_STATUS1 0x35
#define CMD_WRITE_ENABLE 0x06
#define CMD_READ_CONFIG 0x35
@@ -36,6 +40,10 @@
/* Read commands */
#define CMD_READ_ARRAY_SLOW 0x03
#define CMD_READ_ARRAY_FAST 0x0b
+#define CMD_READ_DUAL_OUTPUT_FAST 0x3b
+#define CMD_READ_DUAL_IO_FAST 0xbb
+#define CMD_READ_QUAD_OUTPUT_FAST 0x6b
+#define CMD_READ_QUAD_IO_FAST 0xeb
#define CMD_READ_ID 0x9f
/* Bank addr access commands */
@@ -47,8 +55,10 @@
#endif
/* Common status */
-#define STATUS_WIP 0x01
-#define STATUS_PEC 0x80
+#define STATUS_WIP (1 << 0)
+#define STATUS_QEB_WINSPAN (1 << 1)
+#define STATUS_QEB_MXIC (1 << 6)
+#define STATUS_PEC (1 << 7)
/* Flash timeout values */
#define SPI_FLASH_PROG_TIMEOUT (2 * CONFIG_SYS_HZ)
@@ -86,11 +96,17 @@ int spi_flash_cmd_write(struct spi_slave *spi, const u8 *cmd, size_t cmd_len,
/* Flash erase(sectors) operation, support all possible erase commands */
int spi_flash_cmd_erase_ops(struct spi_flash *flash, u32 offset, size_t len);
+/* Read the status register */
+int spi_flash_cmd_read_status(struct spi_flash *flash, u8 *rs);
+
/* Program the status register */
-int spi_flash_cmd_write_status(struct spi_flash *flash, u8 sr);
+int spi_flash_cmd_write_status(struct spi_flash *flash, u8 ws);
+
+/* Read the config register */
+int spi_flash_cmd_read_config(struct spi_flash *flash, u8 *rc);
-/* Set quad enbale bit */
-int spi_flash_set_qeb(struct spi_flash *flash);
+/* Program the config register */
+int spi_flash_cmd_write_config(struct spi_flash *flash, u8 wc);
/* Enable writing on the SPI flash */
static inline int spi_flash_cmd_write_enable(struct spi_flash *flash)
diff --git a/drivers/mtd/spi/sf_ops.c b/drivers/mtd/spi/sf_ops.c
index e316a69..1f1bb36 100644
--- a/drivers/mtd/spi/sf_ops.c
+++ b/drivers/mtd/spi/sf_ops.c
@@ -9,6 +9,7 @@
*/
#include <common.h>
+#include <malloc.h>
#include <spi.h>
#include <spi_flash.h>
#include <watchdog.h>
@@ -23,13 +24,28 @@ static void spi_flash_addr(u32 addr, u8 *cmd)
cmd[3] = addr >> 0;
}
-int spi_flash_cmd_write_status(struct spi_flash *flash, u8 sr)
+int spi_flash_cmd_read_status(struct spi_flash *flash, u8 *rs)
+{
+ int ret;
+ u8 cmd;
+
+ cmd = CMD_READ_STATUS;
+ ret = spi_flash_read_common(flash, &cmd, 1, rs, 1);
+ if (ret < 0) {
+ debug("SF: fail to read status register\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+int spi_flash_cmd_write_status(struct spi_flash *flash, u8 ws)
{
u8 cmd;
int ret;
cmd = CMD_WRITE_STATUS;
- ret = spi_flash_write_common(flash, &cmd, 1, &sr, 1);
+ ret = spi_flash_write_common(flash, &cmd, 1, &ws, 1);
if (ret < 0) {
debug("SF: fail to write status register\n");
return ret;
@@ -38,6 +54,44 @@ int spi_flash_cmd_write_status(struct spi_flash *flash, u8 sr)
return 0;
}
+#if defined(CONFIG_SPI_FLASH_SPANSION) || defined(CONFIG_SPI_FLASH_WINBOND)
+int spi_flash_cmd_read_config(struct spi_flash *flash, u8 *rc)
+{
+ int ret;
+ u8 cmd;
+
+ cmd = CMD_READ_CONFIG;
+ ret = spi_flash_read_common(flash, &cmd, 1, rc, 1);
+ if (ret < 0) {
+ debug("SF: fail to read config register\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+int spi_flash_cmd_write_config(struct spi_flash *flash, u8 wc)
+{
+ u8 data[2];
+ u8 cmd;
+ int ret;
+
+ ret = spi_flash_cmd_read_status(flash, &data[0]);
+ if (ret < 0)
+ return ret;
+
+ cmd = CMD_WRITE_STATUS;
+ data[1] = wc;
+ ret = spi_flash_write_common(flash, &cmd, 1, &data, 2);
+ if (ret) {
+ debug("SF: fail to write config register\n");
+ return ret;
+ }
+
+ return 0;
+}
+#endif
+
#ifdef CONFIG_SPI_FLASH_BAR
static int spi_flash_cmd_bankaddr_write(struct spi_flash *flash, u8 bank_sel)
{
@@ -65,7 +119,7 @@ static int spi_flash_bank(struct spi_flash *flash, u32 offset)
u8 bank_sel;
int ret;
- bank_sel = offset / SPI_FLASH_16MB_BOUN;
+ bank_sel = offset / (SPI_FLASH_16MB_BOUN << flash->shift);
ret = spi_flash_cmd_bankaddr_write(flash, bank_sel);
if (ret) {
@@ -73,7 +127,29 @@ static int spi_flash_bank(struct spi_flash *flash, u32 offset)
return ret;
}
- return 0;
+ return bank_sel;
+}
+#endif
+
+#ifdef CONFIG_SF_DUAL_FLASH
+static void spi_flash_dual_flash(struct spi_flash *flash, u32 *addr)
+{
+ switch (flash->dual_flash) {
+ case SF_DUAL_STACKED_FLASH:
+ if (*addr >= (flash->size >> 1)) {
+ *addr -= flash->size >> 1;
+ flash->spi->flags |= SPI_XFER_U_PAGE;
+ } else {
+ flash->spi->flags &= ~SPI_XFER_U_PAGE;
+ }
+ break;
+ case SF_DUAL_PARALLEL_FLASH:
+ *addr >>= flash->shift;
+ break;
+ default:
+ debug("SF: Unsupported dual_flash=%d\n", flash->dual_flash);
+ break;
+ }
}
#endif
@@ -81,6 +157,7 @@ int spi_flash_cmd_wait_ready(struct spi_flash *flash, unsigned long timeout)
{
struct spi_slave *spi = flash->spi;
unsigned long timebase;
+ unsigned long flags = SPI_XFER_BEGIN;
int ret;
u8 status;
u8 check_status = 0x0;
@@ -92,7 +169,11 @@ int spi_flash_cmd_wait_ready(struct spi_flash *flash, unsigned long timeout)
check_status = poll_bit;
}
- ret = spi_xfer(spi, 8, &cmd, NULL, SPI_XFER_BEGIN);
+#ifdef CONFIG_SF_DUAL_FLASH
+ if (spi->flags & SPI_XFER_U_PAGE)
+ flags |= SPI_XFER_U_PAGE;
+#endif
+ ret = spi_xfer(spi, 8, &cmd, NULL, flags);
if (ret) {
debug("SF: fail to read %s status register\n",
cmd == CMD_READ_STATUS ? "read" : "flag");
@@ -165,8 +246,8 @@ int spi_flash_write_common(struct spi_flash *flash, const u8 *cmd,
int spi_flash_cmd_erase_ops(struct spi_flash *flash, u32 offset, size_t len)
{
- u32 erase_size;
- u8 cmd[4];
+ u32 erase_size, erase_addr;
+ u8 cmd[SPI_FLASH_CMD_LEN];
int ret = -1;
erase_size = flash->erase_size;
@@ -177,15 +258,21 @@ int spi_flash_cmd_erase_ops(struct spi_flash *flash, u32 offset, size_t len)
cmd[0] = flash->erase_cmd;
while (len) {
+ erase_addr = offset;
+
+#ifdef CONFIG_SF_DUAL_FLASH
+ if (flash->dual_flash > SF_SINGLE_FLASH)
+ spi_flash_dual_flash(flash, &erase_addr);
+#endif
#ifdef CONFIG_SPI_FLASH_BAR
- ret = spi_flash_bank(flash, offset);
+ ret = spi_flash_bank(flash, erase_addr);
if (ret < 0)
return ret;
#endif
- spi_flash_addr(offset, cmd);
+ spi_flash_addr(erase_addr, cmd);
debug("SF: erase %2x %2x %2x %2x (%x)\n", cmd[0], cmd[1],
- cmd[2], cmd[3], offset);
+ cmd[2], cmd[3], erase_addr);
ret = spi_flash_write_common(flash, cmd, sizeof(cmd), NULL, 0);
if (ret < 0) {
@@ -204,16 +291,23 @@ int spi_flash_cmd_write_ops(struct spi_flash *flash, u32 offset,
size_t len, const void *buf)
{
unsigned long byte_addr, page_size;
+ u32 write_addr;
size_t chunk_len, actual;
- u8 cmd[4];
+ u8 cmd[SPI_FLASH_CMD_LEN];
int ret = -1;
page_size = flash->page_size;
- cmd[0] = CMD_PAGE_PROGRAM;
+ cmd[0] = flash->write_cmd;
for (actual = 0; actual < len; actual += chunk_len) {
+ write_addr = offset;
+
+#ifdef CONFIG_SF_DUAL_FLASH
+ if (flash->dual_flash > SF_SINGLE_FLASH)
+ spi_flash_dual_flash(flash, &write_addr);
+#endif
#ifdef CONFIG_SPI_FLASH_BAR
- ret = spi_flash_bank(flash, offset);
+ ret = spi_flash_bank(flash, write_addr);
if (ret < 0)
return ret;
#endif
@@ -223,9 +317,9 @@ int spi_flash_cmd_write_ops(struct spi_flash *flash, u32 offset,
if (flash->spi->max_write_size)
chunk_len = min(chunk_len, flash->spi->max_write_size);
- spi_flash_addr(offset, cmd);
+ spi_flash_addr(write_addr, cmd);
- debug("PP: 0x%p => cmd = { 0x%02x 0x%02x%02x%02x } chunk_len = %zu\n",
+ debug("SF: 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);
ret = spi_flash_write_common(flash, cmd, sizeof(cmd),
@@ -267,8 +361,9 @@ int spi_flash_read_common(struct spi_flash *flash, const u8 *cmd,
int spi_flash_cmd_read_ops(struct spi_flash *flash, u32 offset,
size_t len, void *data)
{
- u8 cmd[5], bank_sel = 0;
- u32 remain_len, read_len;
+ u8 *cmd, cmdsz;
+ u32 remain_len, read_len, read_addr;
+ int bank_sel = 0;
int ret = -1;
/* Handle memory-mapped SPI */
@@ -285,29 +380,33 @@ int spi_flash_cmd_read_ops(struct spi_flash *flash, u32 offset,
return 0;
}
- cmd[0] = CMD_READ_ARRAY_FAST;
- cmd[4] = 0x00;
+ cmdsz = SPI_FLASH_CMD_LEN + flash->dummy_byte;
+ cmd = malloc(cmdsz);
+ memset(cmd, 0, cmdsz);
+ cmd[0] = flash->read_cmd;
while (len) {
-#ifdef CONFIG_SPI_FLASH_BAR
- bank_sel = offset / SPI_FLASH_16MB_BOUN;
+ read_addr = offset;
- ret = spi_flash_cmd_bankaddr_write(flash, bank_sel);
- if (ret) {
- debug("SF: fail to set bank%d\n", bank_sel);
+#ifdef CONFIG_SF_DUAL_FLASH
+ if (flash->dual_flash > SF_SINGLE_FLASH)
+ spi_flash_dual_flash(flash, &read_addr);
+#endif
+#ifdef CONFIG_SPI_FLASH_BAR
+ bank_sel = spi_flash_bank(flash, read_addr);
+ if (bank_sel < 0)
return ret;
- }
#endif
- remain_len = (SPI_FLASH_16MB_BOUN * (bank_sel + 1)) - offset;
+ remain_len = ((SPI_FLASH_16MB_BOUN << flash->shift) *
+ (bank_sel + 1)) - offset;
if (len < remain_len)
read_len = len;
else
read_len = remain_len;
- spi_flash_addr(offset, cmd);
+ spi_flash_addr(read_addr, cmd);
- ret = spi_flash_read_common(flash, cmd, sizeof(cmd),
- data, read_len);
+ ret = spi_flash_read_common(flash, cmd, cmdsz, data, read_len);
if (ret < 0) {
debug("SF: read failed\n");
break;
diff --git a/drivers/mtd/spi/sf_params.c b/drivers/mtd/spi/sf_params.c
new file mode 100644
index 0000000..daf8fe7
--- /dev/null
+++ b/drivers/mtd/spi/sf_params.c
@@ -0,0 +1,130 @@
+/*
+ * SPI flash Params table
+ *
+ * Copyright (C) 2013 Jagannadha Sutradharudu Teki, Xilinx Inc.
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <common.h>
+#include <spi_flash.h>
+
+#include "sf_internal.h"
+
+/* SPI/QSPI flash device params structure */
+const struct spi_flash_params spi_flash_params_table[] = {
+#ifdef CONFIG_SPI_FLASH_ATMEL /* ATMEL */
+ {"AT45DB011D", 0x1f2200, 0x0, 64 * 1024, 4, 0, SECT_4K},
+ {"AT45DB021D", 0x1f2300, 0x0, 64 * 1024, 8, 0, SECT_4K},
+ {"AT45DB041D", 0x1f2400, 0x0, 64 * 1024, 8, 0, SECT_4K},
+ {"AT45DB081D", 0x1f2500, 0x0, 64 * 1024, 16, 0, SECT_4K},
+ {"AT45DB161D", 0x1f2600, 0x0, 64 * 1024, 32, 0, SECT_4K},
+ {"AT45DB321D", 0x1f2700, 0x0, 64 * 1024, 64, 0, SECT_4K},
+ {"AT45DB641D", 0x1f2800, 0x0, 64 * 1024, 128, 0, SECT_4K},
+ {"AT25DF321", 0x1f4701, 0x0, 64 * 1024, 64, 0, SECT_4K},
+#endif
+#ifdef CONFIG_SPI_FLASH_EON /* EON */
+ {"EN25Q32B", 0x1c3016, 0x0, 64 * 1024, 64, 0, 0},
+ {"EN25Q64", 0x1c3017, 0x0, 64 * 1024, 128, 0, SECT_4K},
+ {"EN25Q128B", 0x1c3018, 0x0, 64 * 1024, 256, 0, 0},
+ {"EN25S64", 0x1c3817, 0x0, 64 * 1024, 128, 0, 0},
+#endif
+#ifdef CONFIG_SPI_FLASH_GIGADEVICE /* GIGADEVICE */
+ {"GD25Q64B", 0xc84017, 0x0, 64 * 1024, 128, 0, SECT_4K},
+ {"GD25LQ32", 0xc86016, 0x0, 64 * 1024, 64, 0, SECT_4K},
+#endif
+#ifdef CONFIG_SPI_FLASH_MACRONIX /* MACRONIX */
+ {"MX25L2006E", 0xc22012, 0x0, 64 * 1024, 4, 0, 0},
+ {"MX25L4005", 0xc22013, 0x0, 64 * 1024, 8, 0, 0},
+ {"MX25L8005", 0xc22014, 0x0, 64 * 1024, 16, 0, 0},
+ {"MX25L1605D", 0xc22015, 0x0, 64 * 1024, 32, 0, 0},
+ {"MX25L3205D", 0xc22016, 0x0, 64 * 1024, 64, 0, 0},
+ {"MX25L6405D", 0xc22017, 0x0, 64 * 1024, 128, 0, 0},
+ {"MX25L12805", 0xc22018, 0x0, 64 * 1024, 256, RD_FULL, WR_QPP},
+ {"MX25L25635F", 0xc22019, 0x0, 64 * 1024, 512, RD_FULL, WR_QPP},
+ {"MX25L51235F", 0xc2201a, 0x0, 64 * 1024, 1024, RD_FULL, WR_QPP},
+ {"MX25L12855E", 0xc22618, 0x0, 64 * 1024, 256, RD_FULL, WR_QPP},
+#endif
+#ifdef CONFIG_SPI_FLASH_SPANSION /* SPANSION */
+ {"S25FL008A", 0x010213, 0x0, 64 * 1024, 16, 0, 0},
+ {"S25FL016A", 0x010214, 0x0, 64 * 1024, 32, 0, 0},
+ {"S25FL032A", 0x010215, 0x0, 64 * 1024, 64, 0, 0},
+ {"S25FL064A", 0x010216, 0x0, 64 * 1024, 128, 0, 0},
+ {"S25FL128P_256K", 0x012018, 0x0300, 256 * 1024, 64, RD_FULL, WR_QPP},
+ {"S25FL128P_64K", 0x012018, 0x0301, 64 * 1024, 256, RD_FULL, WR_QPP},
+ {"S25FL032P", 0x010215, 0x4d00, 64 * 1024, 64, RD_FULL, WR_QPP},
+ {"S25FL064P", 0x010216, 0x4d00, 64 * 1024, 128, RD_FULL, WR_QPP},
+ {"S25FL128S_64K", 0x012018, 0x4d01, 64 * 1024, 256, RD_FULL, WR_QPP},
+ {"S25FL256S_256K", 0x010219, 0x4d00, 64 * 1024, 512, RD_FULL, WR_QPP},
+ {"S25FL256S_64K", 0x010219, 0x4d01, 64 * 1024, 512, RD_FULL, WR_QPP},
+ {"S25FL512S_256K", 0x010220, 0x4d00, 64 * 1024, 1024, RD_FULL, WR_QPP},
+ {"S25FL512S_64K", 0x010220, 0x4d01, 64 * 1024, 1024, RD_FULL, WR_QPP},
+#endif
+#ifdef CONFIG_SPI_FLASH_STMICRO /* STMICRO */
+ {"M25P10", 0x202011, 0x0, 32 * 1024, 4, 0, 0},
+ {"M25P20", 0x202012, 0x0, 64 * 1024, 4, 0, 0},
+ {"M25P40", 0x202013, 0x0, 64 * 1024, 8, 0, 0},
+ {"M25P80", 0x202014, 0x0, 64 * 1024, 16, 0, 0},
+ {"M25P16", 0x202015, 0x0, 64 * 1024, 32, 0, 0},
+ {"M25P32", 0x202016, 0x0, 64 * 1024, 64, 0, 0},
+ {"M25P64", 0x202017, 0x0, 64 * 1024, 128, 0, 0},
+ {"M25P128", 0x202018, 0x0, 256 * 1024, 64, 0, 0},
+ {"N25Q32", 0x20ba16, 0x0, 64 * 1024, 64, RD_FULL, WR_QPP | SECT_4K},
+ {"N25Q32A", 0x20bb16, 0x0, 64 * 1024, 64, RD_FULL, WR_QPP | SECT_4K},
+ {"N25Q64", 0x20ba17, 0x0, 64 * 1024, 128, RD_FULL, WR_QPP | SECT_4K},
+ {"N25Q64A", 0x20bb17, 0x0, 64 * 1024, 128, RD_FULL, WR_QPP | SECT_4K},
+ {"N25Q128", 0x20ba18, 0x0, 64 * 1024, 256, RD_FULL, WR_QPP},
+ {"N25Q128A", 0x20bb18, 0x0, 64 * 1024, 256, RD_FULL, WR_QPP},
+ {"N25Q256", 0x20ba19, 0x0, 64 * 1024, 512, RD_FULL, WR_QPP | SECT_4K},
+ {"N25Q256A", 0x20bb19, 0x0, 64 * 1024, 512, RD_FULL, WR_QPP | SECT_4K},
+ {"N25Q512", 0x20ba20, 0x0, 64 * 1024, 1024, RD_FULL, WR_QPP | E_FSR | SECT_4K},
+ {"N25Q512A", 0x20bb20, 0x0, 64 * 1024, 1024, RD_FULL, WR_QPP | E_FSR | SECT_4K},
+ {"N25Q1024", 0x20ba21, 0x0, 64 * 1024, 2048, RD_FULL, WR_QPP | E_FSR | SECT_4K},
+ {"N25Q1024A", 0x20bb21, 0x0, 64 * 1024, 2048, RD_FULL, WR_QPP | E_FSR | SECT_4K},
+#endif
+#ifdef CONFIG_SPI_FLASH_SST /* SST */
+ {"SST25VF040B", 0xbf258d, 0x0, 64 * 1024, 8, 0, SECT_4K | SST_WP},
+ {"SST25VF080B", 0xbf258e, 0x0, 64 * 1024, 16, 0, SECT_4K | SST_WP},
+ {"SST25VF016B", 0xbf2541, 0x0, 64 * 1024, 32, 0, SECT_4K | SST_WP},
+ {"SST25VF032B", 0xbf254a, 0x0, 64 * 1024, 64, 0, SECT_4K | SST_WP},
+ {"SST25VF064C", 0xbf254b, 0x0, 64 * 1024, 128, 0, SECT_4K},
+ {"SST25WF512", 0xbf2501, 0x0, 64 * 1024, 1, 0, SECT_4K | SST_WP},
+ {"SST25WF010", 0xbf2502, 0x0, 64 * 1024, 2, 0, SECT_4K | SST_WP},
+ {"SST25WF020", 0xbf2503, 0x0, 64 * 1024, 4, 0, SECT_4K | SST_WP},
+ {"SST25WF040", 0xbf2504, 0x0, 64 * 1024, 8, 0, SECT_4K | SST_WP},
+ {"SST25WF080", 0xbf2505, 0x0, 64 * 1024, 16, 0, SECT_4K | SST_WP},
+#endif
+#ifdef CONFIG_SPI_FLASH_WINBOND /* WINBOND */
+ {"W25P80", 0xef2014, 0x0, 64 * 1024, 16, 0, 0},
+ {"W25P16", 0xef2015, 0x0, 64 * 1024, 32, 0, 0},
+ {"W25P32", 0xef2016, 0x0, 64 * 1024, 64, 0, 0},
+ {"W25X40", 0xef3013, 0x0, 64 * 1024, 8, 0, SECT_4K},
+ {"W25X16", 0xef3015, 0x0, 64 * 1024, 32, 0, SECT_4K},
+ {"W25X32", 0xef3016, 0x0, 64 * 1024, 64, 0, SECT_4K},
+ {"W25X64", 0xef3017, 0x0, 64 * 1024, 128, 0, SECT_4K},
+ {"W25Q80BL", 0xef4014, 0x0, 64 * 1024, 16, RD_FULL, WR_QPP | SECT_4K},
+ {"W25Q16CL", 0xef4015, 0x0, 64 * 1024, 32, RD_FULL, WR_QPP | SECT_4K},
+ {"W25Q32BV", 0xef4016, 0x0, 64 * 1024, 64, RD_FULL, WR_QPP | SECT_4K},
+ {"W25Q64CV", 0xef4017, 0x0, 64 * 1024, 128, RD_FULL, WR_QPP | SECT_4K},
+ {"W25Q128BV", 0xef4018, 0x0, 64 * 1024, 256, RD_FULL, WR_QPP | SECT_4K},
+ {"W25Q256", 0xef4019, 0x0, 64 * 1024, 512, RD_FULL, WR_QPP | SECT_4K},
+ {"W25Q80BW", 0xef5014, 0x0, 64 * 1024, 16, RD_FULL, WR_QPP | SECT_4K},
+ {"W25Q16DW", 0xef6015, 0x0, 64 * 1024, 32, RD_FULL, WR_QPP | SECT_4K},
+ {"W25Q32DW", 0xef6016, 0x0, 64 * 1024, 64, RD_FULL, WR_QPP | SECT_4K},
+ {"W25Q64DW", 0xef6017, 0x0, 64 * 1024, 128, RD_FULL, WR_QPP | SECT_4K},
+ {"W25Q128FW", 0xef6018, 0x0, 64 * 1024, 256, RD_FULL, WR_QPP | SECT_4K},
+#endif
+ /*
+ * Note:
+ * Below paired flash devices has similar spi_flash params.
+ * (S25FL129P_64K, S25FL128S_64K)
+ * (W25Q80BL, W25Q80BV)
+ * (W25Q16CL, W25Q16DV)
+ * (W25Q32BV, W25Q32FV_SPI)
+ * (W25Q64CV, W25Q64FV_SPI)
+ * (W25Q128BV, W25Q128FV_SPI)
+ * (W25Q32DW, W25Q32FV_QPI)
+ * (W25Q64DW, W25Q64FV_QPI)
+ * (W25Q128FW, W25Q128FV_QPI)
+ */
+};
diff --git a/drivers/mtd/spi/sf_probe.c b/drivers/mtd/spi/sf_probe.c
index b863a98..bc3cf6c 100644
--- a/drivers/mtd/spi/sf_probe.c
+++ b/drivers/mtd/spi/sf_probe.c
@@ -19,154 +19,93 @@
DECLARE_GLOBAL_DATA_PTR;
-/**
- * struct spi_flash_params - SPI/QSPI flash device params structure
- *
- * @name: Device name ([MANUFLETTER][DEVTYPE][DENSITY][EXTRAINFO])
- * @jedec: Device jedec ID (0x[1byte_manuf_id][2byte_dev_id])
- * @ext_jedec: Device ext_jedec ID
- * @sector_size: Sector size of this device
- * @nr_sectors: No.of sectors on this device
- * @flags: Importent param, for flash specific behaviour
- */
-struct spi_flash_params {
- const char *name;
- u32 jedec;
- u16 ext_jedec;
- u32 sector_size;
- u32 nr_sectors;
- u16 flags;
+/* Read commands array */
+static u8 spi_read_cmds_array[] = {
+ CMD_READ_ARRAY_SLOW,
+ CMD_READ_DUAL_OUTPUT_FAST,
+ CMD_READ_DUAL_IO_FAST,
+ CMD_READ_QUAD_OUTPUT_FAST,
+ CMD_READ_QUAD_IO_FAST,
};
-static const struct spi_flash_params spi_flash_params_table[] = {
-#ifdef CONFIG_SPI_FLASH_ATMEL /* ATMEL */
- {"AT45DB011D", 0x1f2200, 0x0, 64 * 1024, 4, SECT_4K},
- {"AT45DB021D", 0x1f2300, 0x0, 64 * 1024, 8, SECT_4K},
- {"AT45DB041D", 0x1f2400, 0x0, 64 * 1024, 8, SECT_4K},
- {"AT45DB081D", 0x1f2500, 0x0, 64 * 1024, 16, SECT_4K},
- {"AT45DB161D", 0x1f2600, 0x0, 64 * 1024, 32, SECT_4K},
- {"AT45DB321D", 0x1f2700, 0x0, 64 * 1024, 64, SECT_4K},
- {"AT45DB641D", 0x1f2800, 0x0, 64 * 1024, 128, SECT_4K},
- {"AT25DF321", 0x1f4701, 0x0, 64 * 1024, 64, SECT_4K},
-#endif
-#ifdef CONFIG_SPI_FLASH_EON /* EON */
- {"EN25Q32B", 0x1c3016, 0x0, 64 * 1024, 64, 0},
- {"EN25Q64", 0x1c3017, 0x0, 64 * 1024, 128, SECT_4K},
- {"EN25Q128B", 0x1c3018, 0x0, 64 * 1024, 256, 0},
- {"EN25S64", 0x1c3817, 0x0, 64 * 1024, 128, 0},
-#endif
-#ifdef CONFIG_SPI_FLASH_GIGADEVICE /* GIGADEVICE */
- {"GD25Q64B", 0xc84017, 0x0, 64 * 1024, 128, SECT_4K},
- {"GD25LQ32", 0xc86016, 0x0, 64 * 1024, 64, SECT_4K},
-#endif
-#ifdef CONFIG_SPI_FLASH_MACRONIX /* MACRONIX */
- {"MX25L2006E", 0xc22012, 0x0, 64 * 1024, 4, 0},
- {"MX25L4005", 0xc22013, 0x0, 64 * 1024, 8, 0},
- {"MX25L8005", 0xc22014, 0x0, 64 * 1024, 16, 0},
- {"MX25L1605D", 0xc22015, 0x0, 64 * 1024, 32, 0},
- {"MX25L3205D", 0xc22016, 0x0, 64 * 1024, 64, 0},
- {"MX25L6405D", 0xc22017, 0x0, 64 * 1024, 128, 0},
- {"MX25L12805", 0xc22018, 0x0, 64 * 1024, 256, 0},
- {"MX25L25635F", 0xc22019, 0x0, 64 * 1024, 512, 0},
- {"MX25L51235F", 0xc2201a, 0x0, 64 * 1024, 1024, 0},
- {"MX25L12855E", 0xc22618, 0x0, 64 * 1024, 256, 0},
+#ifdef CONFIG_SPI_FLASH_MACRONIX
+static int spi_flash_set_qeb_mxic(struct spi_flash *flash)
+{
+ u8 qeb_status;
+ int ret;
+
+ ret = spi_flash_cmd_read_status(flash, &qeb_status);
+ if (ret < 0)
+ return ret;
+
+ if (qeb_status & STATUS_QEB_MXIC) {
+ debug("SF: mxic: QEB is already set\n");
+ } else {
+ ret = spi_flash_cmd_write_status(flash, STATUS_QEB_MXIC);
+ if (ret < 0)
+ return ret;
+ }
+
+ return ret;
+}
#endif
-#ifdef CONFIG_SPI_FLASH_SPANSION /* SPANSION */
- {"S25FL008A", 0x010213, 0x0, 64 * 1024, 16, 0},
- {"S25FL016A", 0x010214, 0x0, 64 * 1024, 32, 0},
- {"S25FL032A", 0x010215, 0x0, 64 * 1024, 64, 0},
- {"S25FL064A", 0x010216, 0x0, 64 * 1024, 128, 0},
- {"S25FL128P_256K", 0x012018, 0x0300, 256 * 1024, 64, 0},
- {"S25FL128P_64K", 0x012018, 0x0301, 64 * 1024, 256, 0},
- {"S25FL032P", 0x010215, 0x4d00, 64 * 1024, 64, 0},
- {"S25FL064P", 0x010216, 0x4d00, 64 * 1024, 128, 0},
- {"S25FL128S_64K", 0x012018, 0x4d01, 64 * 1024, 256, 0},
- {"S25FL256S_256K", 0x010219, 0x4d00, 64 * 1024, 512, 0},
- {"S25FL256S_64K", 0x010219, 0x4d01, 64 * 1024, 512, 0},
- {"S25FL512S_256K", 0x010220, 0x4d00, 64 * 1024, 1024, 0},
- {"S25FL512S_64K", 0x010220, 0x4d01, 64 * 1024, 1024, 0},
+
+#if defined(CONFIG_SPI_FLASH_SPANSION) || defined(CONFIG_SPI_FLASH_WINBOND)
+static int spi_flash_set_qeb_winspan(struct spi_flash *flash)
+{
+ u8 qeb_status;
+ int ret;
+
+ ret = spi_flash_cmd_read_config(flash, &qeb_status);
+ if (ret < 0)
+ return ret;
+
+ if (qeb_status & STATUS_QEB_WINSPAN) {
+ debug("SF: winspan: QEB is already set\n");
+ } else {
+ ret = spi_flash_cmd_write_config(flash, STATUS_QEB_WINSPAN);
+ if (ret < 0)
+ return ret;
+ }
+
+ return ret;
+}
#endif
-#ifdef CONFIG_SPI_FLASH_STMICRO /* STMICRO */
- {"M25P10", 0x202011, 0x0, 32 * 1024, 4, 0},
- {"M25P20", 0x202012, 0x0, 64 * 1024, 4, 0},
- {"M25P40", 0x202013, 0x0, 64 * 1024, 8, 0},
- {"M25P80", 0x202014, 0x0, 64 * 1024, 16, 0},
- {"M25P16", 0x202015, 0x0, 64 * 1024, 32, 0},
- {"M25P32", 0x202016, 0x0, 64 * 1024, 64, 0},
- {"M25P64", 0x202017, 0x0, 64 * 1024, 128, 0},
- {"M25P128", 0x202018, 0x0, 256 * 1024, 64, 0},
- {"N25Q32", 0x20ba16, 0x0, 64 * 1024, 64, SECT_4K},
- {"N25Q32A", 0x20bb16, 0x0, 64 * 1024, 64, SECT_4K},
- {"N25Q64", 0x20ba17, 0x0, 64 * 1024, 128, SECT_4K},
- {"N25Q64A", 0x20bb17, 0x0, 64 * 1024, 128, SECT_4K},
- {"N25Q128", 0x20ba18, 0x0, 64 * 1024, 256, SECT_4K},
- {"N25Q128A", 0x20bb18, 0x0, 64 * 1024, 256, SECT_4K},
- {"N25Q256", 0x20ba19, 0x0, 64 * 1024, 512, SECT_4K},
- {"N25Q256A", 0x20bb19, 0x0, 64 * 1024, 512, SECT_4K},
- {"N25Q512", 0x20ba20, 0x0, 64 * 1024, 1024, E_FSR | SECT_4K},
- {"N25Q512A", 0x20bb20, 0x0, 64 * 1024, 1024, E_FSR | SECT_4K},
- {"N25Q1024", 0x20ba21, 0x0, 64 * 1024, 2048, E_FSR | SECT_4K},
- {"N25Q1024A", 0x20bb21, 0x0, 64 * 1024, 2048, E_FSR | SECT_4K},
+
+static int spi_flash_set_qeb(struct spi_flash *flash, u8 idcode0)
+{
+ switch (idcode0) {
+#ifdef CONFIG_SPI_FLASH_MACRONIX
+ case SPI_FLASH_CFI_MFR_MACRONIX:
+ return spi_flash_set_qeb_mxic(flash);
#endif
-#ifdef CONFIG_SPI_FLASH_SST /* SST */
- {"SST25VF040B", 0xbf258d, 0x0, 64 * 1024, 8, SECT_4K | SST_WP},
- {"SST25VF080B", 0xbf258e, 0x0, 64 * 1024, 16, SECT_4K | SST_WP},
- {"SST25VF016B", 0xbf2541, 0x0, 64 * 1024, 32, SECT_4K | SST_WP},
- {"SST25VF032B", 0xbf254a, 0x0, 64 * 1024, 64, SECT_4K | SST_WP},
- {"SST25VF064C", 0xbf254b, 0x0, 64 * 1024, 128, SECT_4K},
- {"SST25WF512", 0xbf2501, 0x0, 64 * 1024, 1, SECT_4K | SST_WP},
- {"SST25WF010", 0xbf2502, 0x0, 64 * 1024, 2, SECT_4K | SST_WP},
- {"SST25WF020", 0xbf2503, 0x0, 64 * 1024, 4, SECT_4K | SST_WP},
- {"SST25WF040", 0xbf2504, 0x0, 64 * 1024, 8, SECT_4K | SST_WP},
- {"SST25WF080", 0xbf2505, 0x0, 64 * 1024, 16, SECT_4K | SST_WP},
+#if defined(CONFIG_SPI_FLASH_SPANSION) || defined(CONFIG_SPI_FLASH_WINBOND)
+ case SPI_FLASH_CFI_MFR_SPANSION:
+ case SPI_FLASH_CFI_MFR_WINBOND:
+ return spi_flash_set_qeb_winspan(flash);
#endif
-#ifdef CONFIG_SPI_FLASH_WINBOND /* WINBOND */
- {"W25P80", 0xef2014, 0x0, 64 * 1024, 16, 0},
- {"W25P16", 0xef2015, 0x0, 64 * 1024, 32, 0},
- {"W25P32", 0xef2016, 0x0, 64 * 1024, 64, 0},
- {"W25X40", 0xef3013, 0x0, 64 * 1024, 8, SECT_4K},
- {"W25X16", 0xef3015, 0x0, 64 * 1024, 32, SECT_4K},
- {"W25X32", 0xef3016, 0x0, 64 * 1024, 64, SECT_4K},
- {"W25X64", 0xef3017, 0x0, 64 * 1024, 128, SECT_4K},
- {"W25Q80BL", 0xef4014, 0x0, 64 * 1024, 16, SECT_4K},
- {"W25Q16CL", 0xef4015, 0x0, 64 * 1024, 32, SECT_4K},
- {"W25Q32BV", 0xef4016, 0x0, 64 * 1024, 64, SECT_4K},
- {"W25Q64CV", 0xef4017, 0x0, 64 * 1024, 128, SECT_4K},
- {"W25Q128BV", 0xef4018, 0x0, 64 * 1024, 256, SECT_4K},
- {"W25Q256", 0xef4019, 0x0, 64 * 1024, 512, SECT_4K},
- {"W25Q80BW", 0xef5014, 0x0, 64 * 1024, 16, SECT_4K},
- {"W25Q16DW", 0xef6015, 0x0, 64 * 1024, 32, SECT_4K},
- {"W25Q32DW", 0xef6016, 0x0, 64 * 1024, 64, SECT_4K},
- {"W25Q64DW", 0xef6017, 0x0, 64 * 1024, 128, SECT_4K},
- {"W25Q128FW", 0xef6018, 0x0, 64 * 1024, 256, SECT_4K},
+#ifdef CONFIG_SPI_FLASH_STMICRO
+ case SPI_FLASH_CFI_MFR_STMICRO:
+ debug("SF: QEB is volatile for %02x flash\n", idcode0);
+ return 0;
#endif
- /*
- * Note:
- * Below paired flash devices has similar spi_flash params.
- * (S25FL129P_64K, S25FL128S_64K)
- * (W25Q80BL, W25Q80BV)
- * (W25Q16CL, W25Q16DV)
- * (W25Q32BV, W25Q32FV_SPI)
- * (W25Q64CV, W25Q64FV_SPI)
- * (W25Q128BV, W25Q128FV_SPI)
- * (W25Q32DW, W25Q32FV_QPI)
- * (W25Q64DW, W25Q64FV_QPI)
- * (W25Q128FW, W25Q128FV_QPI)
- */
-};
+ default:
+ printf("SF: Need set QEB func for %02x flash\n", idcode0);
+ return -1;
+ }
+}
static struct spi_flash *spi_flash_validate_params(struct spi_slave *spi,
u8 *idcode)
{
const struct spi_flash_params *params;
struct spi_flash *flash;
- int i;
+ u8 cmd;
u16 jedec = idcode[1] << 8 | idcode[2];
u16 ext_jedec = idcode[3] << 8 | idcode[4];
- /* Get the flash id (jedec = manuf_id + dev_id, ext_jedec) */
- for (i = 0; i < ARRAY_SIZE(spi_flash_params_table); i++) {
- params = &spi_flash_params_table[i];
+ params = spi_flash_params_table;
+ for (; params->name != NULL; params++) {
if ((params->jedec >> 16) == idcode[0]) {
if ((params->jedec & 0xFFFF) == jedec) {
if (params->ext_jedec == 0)
@@ -177,7 +116,7 @@ static struct spi_flash *spi_flash_validate_params(struct spi_slave *spi,
}
}
- if (i == ARRAY_SIZE(spi_flash_params_table)) {
+ if (!params->name) {
printf("SF: Unsupported flash IDs: ");
printf("manuf %02x, jedec %04x, ext_jedec %04x\n",
idcode[0], jedec, ext_jedec);
@@ -195,6 +134,7 @@ static struct spi_flash *spi_flash_validate_params(struct spi_slave *spi,
flash->spi = spi;
flash->name = params->name;
flash->memory_map = spi->memory_map;
+ flash->dual_flash = flash->spi->option;
/* Assign spi_flash ops */
flash->write = spi_flash_cmd_write_ops;
@@ -206,23 +146,74 @@ static struct spi_flash *spi_flash_validate_params(struct spi_slave *spi,
flash->read = spi_flash_cmd_read_ops;
/* Compute the flash size */
- flash->page_size = (ext_jedec == 0x4d00) ? 512 : 256;
- flash->sector_size = params->sector_size;
- flash->size = flash->sector_size * params->nr_sectors;
+ flash->shift = (flash->dual_flash & SF_DUAL_PARALLEL_FLASH) ? 1 : 0;
+ flash->page_size = ((ext_jedec == 0x4d00) ? 512 : 256) << flash->shift;
+ flash->sector_size = params->sector_size << flash->shift;
+ flash->size = flash->sector_size * params->nr_sectors << flash->shift;
+#ifdef CONFIG_SF_DUAL_FLASH
+ if (flash->dual_flash & SF_DUAL_STACKED_FLASH)
+ flash->size <<= 1;
+#endif
/* Compute erase sector and command */
if (params->flags & SECT_4K) {
flash->erase_cmd = CMD_ERASE_4K;
- flash->erase_size = 4096;
+ flash->erase_size = 4096 << flash->shift;
} else if (params->flags & SECT_32K) {
flash->erase_cmd = CMD_ERASE_32K;
- flash->erase_size = 32768;
+ flash->erase_size = 32768 << flash->shift;
} else {
flash->erase_cmd = CMD_ERASE_64K;
flash->erase_size = flash->sector_size;
}
- /* Poll cmd seclection */
+ /* Look for the fastest read cmd */
+ cmd = fls(params->e_rd_cmd & flash->spi->op_mode_rx);
+ if (cmd) {
+ cmd = spi_read_cmds_array[cmd - 1];
+ flash->read_cmd = cmd;
+ } else {
+ /* Go for default supported read cmd */
+ flash->read_cmd = CMD_READ_ARRAY_FAST;
+ }
+
+ /* Not require to look for fastest only two write cmds yet */
+ if (params->flags & WR_QPP && flash->spi->op_mode_tx & SPI_OPM_TX_QPP)
+ flash->write_cmd = CMD_QUAD_PAGE_PROGRAM;
+ else
+ /* Go for default supported write cmd */
+ flash->write_cmd = CMD_PAGE_PROGRAM;
+
+ /* Set the quad enable bit - only for quad commands */
+ if ((flash->read_cmd == CMD_READ_QUAD_OUTPUT_FAST) ||
+ (flash->read_cmd == CMD_READ_QUAD_IO_FAST) ||
+ (flash->write_cmd == CMD_QUAD_PAGE_PROGRAM)) {
+ if (spi_flash_set_qeb(flash, idcode[0])) {
+ debug("SF: Fail to set QEB for %02x\n", idcode[0]);
+ return NULL;
+ }
+ }
+
+ /* Read dummy_byte: dummy byte is determined based on the
+ * dummy cycles of a particular command.
+ * Fast commands - dummy_byte = dummy_cycles/8
+ * I/O commands- dummy_byte = (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 (flash->read_cmd) {
+ case CMD_READ_QUAD_IO_FAST:
+ flash->dummy_byte = 2;
+ break;
+ case CMD_READ_ARRAY_SLOW:
+ flash->dummy_byte = 0;
+ break;
+ default:
+ flash->dummy_byte = 1;
+ }
+
+ /* Poll cmd selection */
flash->poll_cmd = CMD_READ_STATUS;
#ifdef CONFIG_SPI_FLASH_STMICRO
if (params->flags & E_FSR)
@@ -339,7 +330,10 @@ static struct spi_flash *spi_flash_probe_slave(struct spi_slave *spi)
puts("\n");
#endif
#ifndef CONFIG_SPI_FLASH_BAR
- if (flash->size > SPI_FLASH_16MB_BOUN) {
+ if (((flash->dual_flash == SF_SINGLE_FLASH) &&
+ (flash->size > SPI_FLASH_16MB_BOUN)) ||
+ ((flash->dual_flash > SF_SINGLE_FLASH) &&
+ (flash->size > SPI_FLASH_16MB_BOUN << 1))) {
puts("SF: Warning - Only lower 16MiB accessible,");
puts(" Full access #define CONFIG_SPI_FLASH_BAR\n");
}
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index d5a7143..81b6af6 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -19,6 +19,7 @@ obj-$(CONFIG_CF_SPI) += cf_spi.o
obj-$(CONFIG_CF_QSPI) += cf_qspi.o
obj-$(CONFIG_DAVINCI_SPI) += davinci_spi.o
obj-$(CONFIG_EXYNOS_SPI) += exynos_spi.o
+obj-$(CONFIG_FTSSP010_SPI) += ftssp010_spi.o
obj-$(CONFIG_ICH_SPI) += ich.o
obj-$(CONFIG_KIRKWOOD_SPI) += kirkwood_spi.o
obj-$(CONFIG_MPC52XX_SPI) += mpc52xx_spi.o
diff --git a/drivers/spi/ftssp010_spi.c b/drivers/spi/ftssp010_spi.c
new file mode 100644
index 0000000..aa3b5a0
--- /dev/null
+++ b/drivers/spi/ftssp010_spi.c
@@ -0,0 +1,508 @@
+/*
+ * (C) Copyright 2013
+ * Faraday Technology Corporation. <http://www.faraday-tech.com/tw/>
+ * Kuo-Jung Su <dantesu@gmail.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <common.h>
+#include <linux/compat.h>
+#include <asm/io.h>
+#include <malloc.h>
+#include <spi.h>
+
+#ifndef CONFIG_FTSSP010_BASE_LIST
+#define CONFIG_FTSSP010_BASE_LIST { CONFIG_FTSSP010_BASE }
+#endif
+
+#ifndef CONFIG_FTSSP010_GPIO_BASE
+#define CONFIG_FTSSP010_GPIO_BASE 0
+#endif
+
+#ifndef CONFIG_FTSSP010_GPIO_LIST
+#define CONFIG_FTSSP010_GPIO_LIST { CONFIG_FTSSP010_GPIO_BASE }
+#endif
+
+#ifndef CONFIG_FTSSP010_CLOCK
+#define CONFIG_FTSSP010_CLOCK clk_get_rate("SSP");
+#endif
+
+#ifndef CONFIG_FTSSP010_TIMEOUT
+#define CONFIG_FTSSP010_TIMEOUT 100
+#endif
+
+/* FTSSP010 chip registers */
+struct ftssp010_regs {
+ uint32_t cr[3];/* control register */
+ uint32_t sr; /* status register */
+ uint32_t icr; /* interrupt control register */
+ uint32_t isr; /* interrupt status register */
+ uint32_t dr; /* data register */
+ uint32_t rsvd[17];
+ uint32_t revr; /* revision register */
+ uint32_t fear; /* feature register */
+};
+
+/* Control Register 0 */
+#define CR0_FFMT_MASK (7 << 12)
+#define CR0_FFMT_SSP (0 << 12)
+#define CR0_FFMT_SPI (1 << 12)
+#define CR0_FFMT_MICROWIRE (2 << 12)
+#define CR0_FFMT_I2S (3 << 12)
+#define CR0_FFMT_AC97 (4 << 12)
+#define CR0_FLASH (1 << 11)
+#define CR0_FSDIST(x) (((x) & 0x03) << 8)
+#define CR0_LOOP (1 << 7) /* loopback mode */
+#define CR0_LSB (1 << 6) /* LSB */
+#define CR0_FSPO (1 << 5) /* fs atcive low (I2S only) */
+#define CR0_FSJUSTIFY (1 << 4)
+#define CR0_OPM_SLAVE (0 << 2)
+#define CR0_OPM_MASTER (3 << 2)
+#define CR0_OPM_I2S_MSST (3 << 2) /* master stereo mode */
+#define CR0_OPM_I2S_MSMO (2 << 2) /* master mono mode */
+#define CR0_OPM_I2S_SLST (1 << 2) /* slave stereo mode */
+#define CR0_OPM_I2S_SLMO (0 << 2) /* slave mono mode */
+#define CR0_SCLKPO (1 << 1) /* clock polarity */
+#define CR0_SCLKPH (1 << 0) /* clock phase */
+
+/* Control Register 1 */
+#define CR1_PDL(x) (((x) & 0xff) << 24) /* padding length */
+#define CR1_SDL(x) ((((x) - 1) & 0x1f) << 16) /* data length */
+#define CR1_DIV(x) (((x) - 1) & 0xffff) /* clock divider */
+
+/* Control Register 2 */
+#define CR2_CS(x) (((x) & 3) << 10) /* CS/FS select */
+#define CR2_FS (1 << 9) /* CS/FS signal level */
+#define CR2_TXEN (1 << 8) /* tx enable */
+#define CR2_RXEN (1 << 7) /* rx enable */
+#define CR2_RESET (1 << 6) /* chip reset */
+#define CR2_TXFC (1 << 3) /* tx fifo Clear */
+#define CR2_RXFC (1 << 2) /* rx fifo Clear */
+#define CR2_TXDOE (1 << 1) /* tx data output enable */
+#define CR2_EN (1 << 0) /* chip enable */
+
+/* Status Register */
+#define SR_RFF (1 << 0) /* rx fifo full */
+#define SR_TFNF (1 << 1) /* tx fifo not full */
+#define SR_BUSY (1 << 2) /* chip busy */
+#define SR_RFVE(reg) (((reg) >> 4) & 0x1f) /* rx fifo valid entries */
+#define SR_TFVE(reg) (((reg) >> 12) & 0x1f) /* tx fifo valid entries */
+
+/* Feature Register */
+#define FEAR_BITS(reg) ((((reg) >> 0) & 0xff) + 1) /* data width */
+#define FEAR_RFSZ(reg) ((((reg) >> 8) & 0xff) + 1) /* rx fifo size */
+#define FEAR_TFSZ(reg) ((((reg) >> 16) & 0xff) + 1) /* tx fifo size */
+#define FEAR_AC97 (1 << 24)
+#define FEAR_I2S (1 << 25)
+#define FEAR_SPI_MWR (1 << 26)
+#define FEAR_SSP (1 << 27)
+#define FEAR_SPDIF (1 << 28)
+
+/* FTGPIO010 chip registers */
+struct ftgpio010_regs {
+ uint32_t out; /* 0x00: Data Output */
+ uint32_t in; /* 0x04: Data Input */
+ uint32_t dir; /* 0x08: Direction */
+ uint32_t bypass; /* 0x0c: Bypass */
+ uint32_t set; /* 0x10: Data Set */
+ uint32_t clr; /* 0x14: Data Clear */
+ uint32_t pull_up; /* 0x18: Pull-Up Enabled */
+ uint32_t pull_st; /* 0x1c: Pull State (0=pull-down, 1=pull-up) */
+};
+
+struct ftssp010_gpio {
+ struct ftgpio010_regs *regs;
+ uint32_t pin;
+};
+
+struct ftssp010_spi {
+ struct spi_slave slave;
+ struct ftssp010_gpio gpio;
+ struct ftssp010_regs *regs;
+ uint32_t fifo;
+ uint32_t mode;
+ uint32_t div;
+ uint32_t clk;
+ uint32_t speed;
+ uint32_t revision;
+};
+
+static inline struct ftssp010_spi *to_ftssp010_spi(struct spi_slave *slave)
+{
+ return container_of(slave, struct ftssp010_spi, slave);
+}
+
+static int get_spi_chip(int bus, struct ftssp010_spi *chip)
+{
+ uint32_t fear, base[] = CONFIG_FTSSP010_BASE_LIST;
+
+ if (bus >= ARRAY_SIZE(base) || !base[bus])
+ return -1;
+
+ chip->regs = (struct ftssp010_regs *)base[bus];
+
+ chip->revision = readl(&chip->regs->revr);
+
+ fear = readl(&chip->regs->fear);
+ chip->fifo = min_t(uint32_t, FEAR_TFSZ(fear), FEAR_RFSZ(fear));
+
+ return 0;
+}
+
+static int get_spi_gpio(int bus, struct ftssp010_gpio *chip)
+{
+ uint32_t base[] = CONFIG_FTSSP010_GPIO_LIST;
+
+ if (bus >= ARRAY_SIZE(base) || !base[bus])
+ return -1;
+
+ chip->regs = (struct ftgpio010_regs *)(base[bus] & 0xfff00000);
+ chip->pin = base[bus] & 0x1f;
+
+ /* make it an output pin */
+ setbits_le32(&chip->regs->dir, 1 << chip->pin);
+
+ return 0;
+}
+
+static int ftssp010_wait(struct ftssp010_spi *chip)
+{
+ struct ftssp010_regs *regs = chip->regs;
+ int ret = -1;
+ ulong t;
+
+ /* wait until device idle */
+ for (t = get_timer(0); get_timer(t) < CONFIG_FTSSP010_TIMEOUT; ) {
+ if (readl(&regs->sr) & SR_BUSY)
+ continue;
+ ret = 0;
+ break;
+ }
+
+ if (ret)
+ puts("ftspi010: busy timeout\n");
+
+ return ret;
+}
+
+static int ftssp010_wait_tx(struct ftssp010_spi *chip)
+{
+ struct ftssp010_regs *regs = chip->regs;
+ int ret = -1;
+ ulong t;
+
+ /* wait until tx fifo not full */
+ for (t = get_timer(0); get_timer(t) < CONFIG_FTSSP010_TIMEOUT; ) {
+ if (!(readl(&regs->sr) & SR_TFNF))
+ continue;
+ ret = 0;
+ break;
+ }
+
+ if (ret)
+ puts("ftssp010: tx timeout\n");
+
+ return ret;
+}
+
+static int ftssp010_wait_rx(struct ftssp010_spi *chip)
+{
+ struct ftssp010_regs *regs = chip->regs;
+ int ret = -1;
+ ulong t;
+
+ /* wait until rx fifo not empty */
+ for (t = get_timer(0); get_timer(t) < CONFIG_FTSSP010_TIMEOUT; ) {
+ if (!SR_RFVE(readl(&regs->sr)))
+ continue;
+ ret = 0;
+ break;
+ }
+
+ if (ret)
+ puts("ftssp010: rx timeout\n");
+
+ return ret;
+}
+
+static int ftssp010_spi_work_transfer_v2(struct ftssp010_spi *chip,
+ const void *tx_buf, void *rx_buf, int len, uint flags)
+{
+ struct ftssp010_regs *regs = chip->regs;
+ const uint8_t *txb = tx_buf;
+ uint8_t *rxb = rx_buf;
+
+ while (len > 0) {
+ int i, depth = min(chip->fifo >> 2, len);
+ uint32_t xmsk = 0;
+
+ if (tx_buf) {
+ for (i = 0; i < depth; ++i) {
+ ftssp010_wait_tx(chip);
+ writel(*txb++, &regs->dr);
+ }
+ xmsk |= CR2_TXEN | CR2_TXDOE;
+ if ((readl(&regs->cr[2]) & xmsk) != xmsk)
+ setbits_le32(&regs->cr[2], xmsk);
+ }
+ if (rx_buf) {
+ xmsk |= CR2_RXEN;
+ if ((readl(&regs->cr[2]) & xmsk) != xmsk)
+ setbits_le32(&regs->cr[2], xmsk);
+ for (i = 0; i < depth; ++i) {
+ ftssp010_wait_rx(chip);
+ *rxb++ = (uint8_t)readl(&regs->dr);
+ }
+ }
+
+ len -= depth;
+ }
+
+ return 0;
+}
+
+static int ftssp010_spi_work_transfer_v1(struct ftssp010_spi *chip,
+ const void *tx_buf, void *rx_buf, int len, uint flags)
+{
+ struct ftssp010_regs *regs = chip->regs;
+ const uint8_t *txb = tx_buf;
+ uint8_t *rxb = rx_buf;
+
+ while (len > 0) {
+ int i, depth = min(chip->fifo >> 2, len);
+ uint32_t tmp;
+
+ for (i = 0; i < depth; ++i) {
+ ftssp010_wait_tx(chip);
+ writel(txb ? (*txb++) : 0, &regs->dr);
+ }
+ for (i = 0; i < depth; ++i) {
+ ftssp010_wait_rx(chip);
+ tmp = readl(&regs->dr);
+ if (rxb)
+ *rxb++ = (uint8_t)tmp;
+ }
+
+ len -= depth;
+ }
+
+ return 0;
+}
+
+static void ftssp010_cs_set(struct ftssp010_spi *chip, int high)
+{
+ struct ftssp010_regs *regs = chip->regs;
+ struct ftssp010_gpio *gpio = &chip->gpio;
+ uint32_t mask;
+
+ /* cs pull high/low */
+ if (chip->revision >= 0x11900) {
+ mask = CR2_CS(chip->slave.cs) | (high ? CR2_FS : 0);
+ writel(mask, &regs->cr[2]);
+ } else if (gpio->regs) {
+ mask = 1 << gpio->pin;
+ if (high)
+ writel(mask, &gpio->regs->set);
+ else
+ writel(mask, &gpio->regs->clr);
+ }
+
+ /* extra delay for signal propagation */
+ udelay_masked(1);
+}
+
+/*
+ * Determine if a SPI chipselect is valid.
+ * This function is provided by the board if the low-level SPI driver
+ * needs it to determine if a given chipselect is actually valid.
+ *
+ * Returns: 1 if bus:cs identifies a valid chip on this board, 0
+ * otherwise.
+ */
+int spi_cs_is_valid(unsigned int bus, unsigned int cs)
+{
+ struct ftssp010_spi chip;
+
+ if (get_spi_chip(bus, &chip))
+ return 0;
+
+ if (!cs)
+ return 1;
+ else if ((cs < 4) && (chip.revision >= 0x11900))
+ return 1;
+
+ return 0;
+}
+
+/*
+ * Activate a SPI chipselect.
+ * This function is provided by the board code when using a driver
+ * that can't control its chipselects automatically (e.g.
+ * common/soft_spi.c). When called, it should activate the chip select
+ * to the device identified by "slave".
+ */
+void spi_cs_activate(struct spi_slave *slave)
+{
+ struct ftssp010_spi *chip = to_ftssp010_spi(slave);
+ struct ftssp010_regs *regs = chip->regs;
+
+ /* cs pull */
+ if (chip->mode & SPI_CS_HIGH)
+ ftssp010_cs_set(chip, 1);
+ else
+ ftssp010_cs_set(chip, 0);
+
+ /* chip enable + fifo clear */
+ setbits_le32(&regs->cr[2], CR2_EN | CR2_TXFC | CR2_RXFC);
+}
+
+/*
+ * Deactivate a SPI chipselect.
+ * This function is provided by the board code when using a driver
+ * that can't control its chipselects automatically (e.g.
+ * common/soft_spi.c). When called, it should deactivate the chip
+ * select to the device identified by "slave".
+ */
+void spi_cs_deactivate(struct spi_slave *slave)
+{
+ struct ftssp010_spi *chip = to_ftssp010_spi(slave);
+
+ /* wait until chip idle */
+ ftssp010_wait(chip);
+
+ /* cs pull */
+ if (chip->mode & SPI_CS_HIGH)
+ ftssp010_cs_set(chip, 0);
+ else
+ ftssp010_cs_set(chip, 1);
+}
+
+void spi_init(void)
+{
+ /* nothing to do */
+}
+
+struct spi_slave *spi_setup_slave(uint bus, uint cs, uint max_hz, uint mode)
+{
+ struct ftssp010_spi *chip;
+
+ if (mode & SPI_3WIRE) {
+ puts("ftssp010: can't do 3-wire\n");
+ return NULL;
+ }
+
+ if (mode & SPI_SLAVE) {
+ puts("ftssp010: can't do slave mode\n");
+ return NULL;
+ }
+
+ if (mode & SPI_PREAMBLE) {
+ puts("ftssp010: can't skip preamble bytes\n");
+ return NULL;
+ }
+
+ if (!spi_cs_is_valid(bus, cs)) {
+ puts("ftssp010: invalid (bus, cs)\n");
+ return NULL;
+ }
+
+ chip = spi_alloc_slave(struct ftssp010_spi, bus, cs);
+ if (!chip)
+ return NULL;
+
+ if (get_spi_chip(bus, chip))
+ goto free_out;
+
+ if (chip->revision < 0x11900 && get_spi_gpio(bus, &chip->gpio)) {
+ puts("ftssp010: Before revision 1.19.0, its clock & cs are\n"
+ "controlled by tx engine which is not synced with rx engine,\n"
+ "so the clock & cs might be shutdown before rx engine\n"
+ "finishs its jobs.\n"
+ "If possible, please add a dedicated gpio for it.\n");
+ }
+
+ chip->mode = mode;
+ chip->clk = CONFIG_FTSSP010_CLOCK;
+ chip->div = 2;
+ if (max_hz) {
+ while (chip->div < 0xffff) {
+ if ((chip->clk / (2 * chip->div)) <= max_hz)
+ break;
+ chip->div += 1;
+ }
+ }
+ chip->speed = chip->clk / (2 * chip->div);
+
+ return &chip->slave;
+
+free_out:
+ free(chip);
+ return NULL;
+}
+
+void spi_free_slave(struct spi_slave *slave)
+{
+ free(slave);
+}
+
+int spi_claim_bus(struct spi_slave *slave)
+{
+ struct ftssp010_spi *chip = to_ftssp010_spi(slave);
+ struct ftssp010_regs *regs = chip->regs;
+
+ writel(CR1_SDL(8) | CR1_DIV(chip->div), &regs->cr[1]);
+
+ if (chip->revision >= 0x11900) {
+ writel(CR0_OPM_MASTER | CR0_FFMT_SPI | CR0_FSPO | CR0_FLASH,
+ &regs->cr[0]);
+ writel(CR2_TXFC | CR2_RXFC,
+ &regs->cr[2]);
+ } else {
+ writel(CR0_OPM_MASTER | CR0_FFMT_SPI | CR0_FSPO,
+ &regs->cr[0]);
+ writel(CR2_TXFC | CR2_RXFC | CR2_EN | CR2_TXDOE,
+ &regs->cr[2]);
+ }
+
+ if (chip->mode & SPI_LOOP)
+ setbits_le32(&regs->cr[0], CR0_LOOP);
+
+ if (chip->mode & SPI_CPOL)
+ setbits_le32(&regs->cr[0], CR0_SCLKPO);
+
+ if (chip->mode & SPI_CPHA)
+ setbits_le32(&regs->cr[0], CR0_SCLKPH);
+
+ spi_cs_deactivate(slave);
+
+ return 0;
+}
+
+void spi_release_bus(struct spi_slave *slave)
+{
+ struct ftssp010_spi *chip = to_ftssp010_spi(slave);
+ struct ftssp010_regs *regs = chip->regs;
+
+ writel(0, &regs->cr[2]);
+}
+
+int spi_xfer(struct spi_slave *slave, unsigned int bitlen,
+ const void *dout, void *din, unsigned long flags)
+{
+ struct ftssp010_spi *chip = to_ftssp010_spi(slave);
+ uint32_t len = bitlen >> 3;
+
+ if (flags & SPI_XFER_BEGIN)
+ spi_cs_activate(slave);
+
+ if (chip->revision >= 0x11900)
+ ftssp010_spi_work_transfer_v2(chip, dout, din, len, flags);
+ else
+ ftssp010_spi_work_transfer_v1(chip, dout, din, len, flags);
+
+ if (flags & SPI_XFER_END)
+ spi_cs_deactivate(slave);
+
+ return 0;
+}
diff --git a/drivers/spi/sh_spi.c b/drivers/spi/sh_spi.c
index 744afe3..7ca5e36 100644
--- a/drivers/spi/sh_spi.c
+++ b/drivers/spi/sh_spi.c
@@ -151,7 +151,6 @@ static int sh_spi_send(struct sh_spi *ss, const unsigned char *tx_data,
{
int i, cur_len, ret = 0;
int remain = (int)len;
- unsigned long tmp;
if (len >= SH_SPI_FIFO_SIZE)
sh_spi_set_bit(SH_SPI_SSA, &ss->regs->cr1);
@@ -183,9 +182,7 @@ static int sh_spi_send(struct sh_spi *ss, const unsigned char *tx_data,
}
if (flags & SPI_XFER_END) {
- tmp = sh_spi_read(&ss->regs->cr1);
- tmp = tmp & ~(SH_SPI_SSD | SH_SPI_SSDB);
- sh_spi_write(tmp, &ss->regs->cr1);
+ sh_spi_clear_bit(SH_SPI_SSD | SH_SPI_SSDB, &ss->regs->cr1);
sh_spi_set_bit(SH_SPI_SSA, &ss->regs->cr1);
udelay(100);
write_fifo_empty_wait(ss);
@@ -198,16 +195,13 @@ static int sh_spi_receive(struct sh_spi *ss, unsigned char *rx_data,
unsigned int len, unsigned long flags)
{
int i;
- unsigned long tmp;
if (len > SH_SPI_MAX_BYTE)
sh_spi_write(SH_SPI_MAX_BYTE, &ss->regs->cr3);
else
sh_spi_write(len, &ss->regs->cr3);
- tmp = sh_spi_read(&ss->regs->cr1);
- tmp = tmp & ~(SH_SPI_SSD | SH_SPI_SSDB);
- sh_spi_write(tmp, &ss->regs->cr1);
+ sh_spi_clear_bit(SH_SPI_SSD | SH_SPI_SSDB, &ss->regs->cr1);
sh_spi_set_bit(SH_SPI_SSA, &ss->regs->cr1);
for (i = 0; i < len; i++) {
diff --git a/include/spi.h b/include/spi.h
index aba7922..ffd6647 100644
--- a/include/spi.h
+++ b/include/spi.h
@@ -30,6 +30,24 @@
#define SPI_XFER_MMAP 0x08 /* Memory Mapped start */
#define SPI_XFER_MMAP_END 0x10 /* Memory Mapped End */
#define SPI_XFER_ONCE (SPI_XFER_BEGIN | SPI_XFER_END)
+#define SPI_XFER_U_PAGE (1 << 5)
+
+/* SPI TX operation modes */
+#define SPI_OPM_TX_QPP 1 << 0
+
+/* SPI RX operation modes */
+#define SPI_OPM_RX_AS 1 << 0
+#define SPI_OPM_RX_DOUT 1 << 1
+#define SPI_OPM_RX_DIO 1 << 2
+#define SPI_OPM_RX_QOF 1 << 3
+#define SPI_OPM_RX_QIOF 1 << 4
+#define SPI_OPM_RX_EXTN SPI_OPM_RX_AS | SPI_OPM_RX_DOUT | \
+ SPI_OPM_RX_DIO | SPI_OPM_RX_QOF | \
+ SPI_OPM_RX_QIOF
+
+/* SPI bus connection options */
+#define SPI_CONN_DUAL_SHARED 1 << 0
+#define SPI_CONN_DUAL_SEPARATED 1 << 1
/* Header byte that marks the start of the message */
#define SPI_PREAMBLE_END_BYTE 0xec
@@ -43,17 +61,25 @@
*
* @bus: ID of the bus that the slave is attached to.
* @cs: ID of the chip select connected to the slave.
+ * @op_mode_rx: SPI RX operation mode.
+ * @op_mode_tx: SPI TX operation mode.
* @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.
* @memory_map: Address of read-only SPI flash access.
+ * @option: Varies SPI bus options - separate, shared bus.
+ * @flags: Indication of SPI flags.
*/
struct spi_slave {
unsigned int bus;
unsigned int cs;
+ u8 op_mode_rx;
+ u8 op_mode_tx;
unsigned int wordlen;
unsigned int max_write_size;
void *memory_map;
+ u8 option;
+ u8 flags;
};
/**
diff --git a/include/spi_flash.h b/include/spi_flash.h
index afc3a58..f79f0ea 100644
--- a/include/spi_flash.h
+++ b/include/spi_flash.h
@@ -19,11 +19,60 @@
#include <linux/types.h>
#include <linux/compiler.h>
+/* sf param flags */
+#define SECT_4K 1 << 1
+#define SECT_32K 1 << 2
+#define E_FSR 1 << 3
+#define WR_QPP 1 << 4
+
+/* Enum list - Full read commands */
+enum spi_read_cmds {
+ ARRAY_SLOW = 1 << 0,
+ DUAL_OUTPUT_FAST = 1 << 1,
+ DUAL_IO_FAST = 1 << 2,
+ QUAD_OUTPUT_FAST = 1 << 3,
+ QUAD_IO_FAST = 1 << 4,
+};
+#define RD_EXTN ARRAY_SLOW | DUAL_OUTPUT_FAST | DUAL_IO_FAST
+#define RD_FULL RD_EXTN | QUAD_OUTPUT_FAST | QUAD_IO_FAST
+
+/* Dual SPI flash memories */
+enum spi_dual_flash {
+ SF_SINGLE_FLASH = 0,
+ SF_DUAL_STACKED_FLASH = 1 << 0,
+ SF_DUAL_PARALLEL_FLASH = 1 << 1,
+};
+
+/**
+ * struct spi_flash_params - SPI/QSPI flash device params structure
+ *
+ * @name: Device name ([MANUFLETTER][DEVTYPE][DENSITY][EXTRAINFO])
+ * @jedec: Device jedec ID (0x[1byte_manuf_id][2byte_dev_id])
+ * @ext_jedec: Device ext_jedec ID
+ * @sector_size: Sector size of this device
+ * @nr_sectors: No.of sectors on this device
+ * @e_rd_cmd: Enum list for read commands
+ * @flags: Importent param, for flash specific behaviour
+ */
+struct spi_flash_params {
+ const char *name;
+ u32 jedec;
+ u16 ext_jedec;
+ u32 sector_size;
+ u32 nr_sectors;
+ u8 e_rd_cmd;
+ u16 flags;
+};
+
+extern const struct spi_flash_params spi_flash_params_table[];
+
/**
* struct spi_flash - SPI flash structure
*
* @spi: SPI slave
* @name: Name of SPI flash
+ * @dual_flash: Indicates dual flash memories - dual stacked, parallel
+ * @shift: Flash shift useful in dual parallel
* @size: Total flash size
* @page_size: Write (page) size
* @sector_size: Sector size
@@ -33,6 +82,9 @@
* @bank_curr: Current flash bank
* @poll_cmd: Poll cmd - for flash erase/program
* @erase_cmd: Erase cmd 4K, 32K, 64K
+ * @read_cmd: Read cmd - Array Fast, Extn read and quad read.
+ * @write_cmd: Write cmd - page and quad program.
+ * @dummy_byte: Dummy cycles for read operation.
* @memory_map: Address of read-only SPI flash access
* @read: Flash read ops: Read len bytes at offset into buf
* Supported cmds: Fast Array Read
@@ -45,6 +97,8 @@
struct spi_flash {
struct spi_slave *spi;
const char *name;
+ u8 dual_flash;
+ u8 shift;
u32 size;
u32 page_size;
@@ -57,6 +111,9 @@ struct spi_flash {
#endif
u8 poll_cmd;
u8 erase_cmd;
+ u8 read_cmd;
+ u8 write_cmd;
+ u8 dummy_byte;
void *memory_map;
int (*read)(struct spi_flash *flash, u32 offset, size_t len, void *buf);