mirror of
https://github.com/AsahiLinux/u-boot
synced 2024-11-25 14:10:43 +00:00
Merge branch 'master' of git://git.denx.de/u-boot-spi
This commit is contained in:
commit
10fcda8e25
16 changed files with 1173 additions and 200 deletions
6
README
6
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
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
92
doc/SPI/README.dual-flash
Normal file
92
doc/SPI/README.dual-flash
Normal file
|
@ -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.
|
41
doc/SPI/README.ftssp010_spi_test
Normal file
41
doc/SPI/README.ftssp010_spi_test
Normal file
|
@ -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..........
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
/* Program the status register */
|
||||
int spi_flash_cmd_write_status(struct spi_flash *flash, u8 sr);
|
||||
/* Read the status register */
|
||||
int spi_flash_cmd_read_status(struct spi_flash *flash, u8 *rs);
|
||||
|
||||
/* Set quad enbale bit */
|
||||
int spi_flash_set_qeb(struct spi_flash *flash);
|
||||
/* Program the status register */
|
||||
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);
|
||||
|
||||
/* 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)
|
||||
|
|
|
@ -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);
|
||||
return ret;
|
||||
}
|
||||
#ifdef CONFIG_SF_DUAL_FLASH
|
||||
if (flash->dual_flash > SF_SINGLE_FLASH)
|
||||
spi_flash_dual_flash(flash, &read_addr);
|
||||
#endif
|
||||
remain_len = (SPI_FLASH_16MB_BOUN * (bank_sel + 1)) - offset;
|
||||
#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 << 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;
|
||||
|
|
130
drivers/mtd/spi/sf_params.c
Normal file
130
drivers/mtd/spi/sf_params.c
Normal file
|
@ -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)
|
||||
*/
|
||||
};
|
|
@ -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},
|
||||
#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_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},
|
||||
|
||||
#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_GIGADEVICE /* GIGADEVICE */
|
||||
{"GD25Q64B", 0xc84017, 0x0, 64 * 1024, 128, SECT_4K},
|
||||
{"GD25LQ32", 0xc86016, 0x0, 64 * 1024, 64, 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_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},
|
||||
#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_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},
|
||||
#ifdef CONFIG_SPI_FLASH_STMICRO
|
||||
case SPI_FLASH_CFI_MFR_STMICRO:
|
||||
debug("SF: QEB is volatile for %02x flash\n", idcode0);
|
||||
return 0;
|
||||
#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},
|
||||
#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},
|
||||
#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},
|
||||
#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");
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
508
drivers/spi/ftssp010_spi.c
Normal file
508
drivers/spi/ftssp010_spi.c
Normal file
|
@ -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(®s->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(®s->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(®s->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++, ®s->dr);
|
||||
}
|
||||
xmsk |= CR2_TXEN | CR2_TXDOE;
|
||||
if ((readl(®s->cr[2]) & xmsk) != xmsk)
|
||||
setbits_le32(®s->cr[2], xmsk);
|
||||
}
|
||||
if (rx_buf) {
|
||||
xmsk |= CR2_RXEN;
|
||||
if ((readl(®s->cr[2]) & xmsk) != xmsk)
|
||||
setbits_le32(®s->cr[2], xmsk);
|
||||
for (i = 0; i < depth; ++i) {
|
||||
ftssp010_wait_rx(chip);
|
||||
*rxb++ = (uint8_t)readl(®s->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, ®s->dr);
|
||||
}
|
||||
for (i = 0; i < depth; ++i) {
|
||||
ftssp010_wait_rx(chip);
|
||||
tmp = readl(®s->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, ®s->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(®s->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), ®s->cr[1]);
|
||||
|
||||
if (chip->revision >= 0x11900) {
|
||||
writel(CR0_OPM_MASTER | CR0_FFMT_SPI | CR0_FSPO | CR0_FLASH,
|
||||
®s->cr[0]);
|
||||
writel(CR2_TXFC | CR2_RXFC,
|
||||
®s->cr[2]);
|
||||
} else {
|
||||
writel(CR0_OPM_MASTER | CR0_FFMT_SPI | CR0_FSPO,
|
||||
®s->cr[0]);
|
||||
writel(CR2_TXFC | CR2_RXFC | CR2_EN | CR2_TXDOE,
|
||||
®s->cr[2]);
|
||||
}
|
||||
|
||||
if (chip->mode & SPI_LOOP)
|
||||
setbits_le32(®s->cr[0], CR0_LOOP);
|
||||
|
||||
if (chip->mode & SPI_CPOL)
|
||||
setbits_le32(®s->cr[0], CR0_SCLKPO);
|
||||
|
||||
if (chip->mode & SPI_CPHA)
|
||||
setbits_le32(®s->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, ®s->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;
|
||||
}
|
|
@ -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++) {
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in a new issue