Merge branch 'master' of git://git.denx.de/u-boot-spi

This commit is contained in:
Tom Rini 2013-10-08 09:03:15 -04:00
commit 968294bd7b
33 changed files with 1695 additions and 2317 deletions

View File

@ -170,7 +170,7 @@ static const struct dpll_params per_dpll_params_768mhz_es2[NUM_SYS_CLKS] = {
static const struct dpll_params per_dpll_params_768mhz_dra7xx[NUM_SYS_CLKS] = {
{32, 0, 4, 1, 3, 4, 10, 2, -1, -1, -1, -1}, /* 12 MHz */
{96, 4, 4, 1, 3, 4, 10, 2, -1, -1, -1, -1}, /* 20 MHz */
{96, 4, 4, 1, 3, 4, 4, 2, -1, -1, -1, -1}, /* 20 MHz */
{160, 6, 4, 1, 3, 4, 10, 2, -1, -1, -1, -1}, /* 16.8 MHz */
{20, 0, 4, 1, 3, 4, 10, 2, -1, -1, -1, -1}, /* 19.2 MHz */
{192, 12, 4, 1, 3, 4, 10, 2, -1, -1, -1, -1}, /* 26 MHz */
@ -426,6 +426,10 @@ void enable_basic_clocks(void)
#ifdef CONFIG_DRIVER_TI_CPSW
(*prcm)->cm_gmac_gmac_clkctrl,
#endif
#ifdef CONFIG_TI_QSPI
(*prcm)->cm_l4per_qspi_clkctrl,
#endif
0
};
@ -454,6 +458,10 @@ void enable_basic_clocks(void)
clk_modules_explicit_en_essential,
1);
#ifdef CONFIG_TI_QSPI
setbits_le32((*prcm)->cm_l4per_qspi_clkctrl, (1<<24));
#endif
/* Enable SCRM OPT clocks for PER and CORE dpll */
setbits_le32((*prcm)->cm_wkupaon_scrm_clkctrl,
OPTFCLKEN_SCRM_PER_MASK);

View File

@ -921,6 +921,7 @@ struct prcm_regs const dra7xx_prcm = {
.cm_l4per_gpio8_clkctrl = 0x4a009818,
.cm_l4per_mmcsd3_clkctrl = 0x4a009820,
.cm_l4per_mmcsd4_clkctrl = 0x4a009828,
.cm_l4per_qspi_clkctrl = 0x4a009838,
.cm_l4per_uart1_clkctrl = 0x4a009840,
.cm_l4per_uart2_clkctrl = 0x4a009848,
.cm_l4per_uart3_clkctrl = 0x4a009850,

View File

@ -22,7 +22,7 @@ struct exynos_spi {
unsigned int rx_data; /* 0x1c */
unsigned int pkt_cnt; /* 0x20 */
unsigned char reserved2[4];
unsigned char reserved3[4];
unsigned int swap_cfg; /* 0x28 */
unsigned int fb_clk; /* 0x2c */
unsigned char padding[0xffd0];
};
@ -62,5 +62,14 @@ struct exynos_spi {
/* Packet Count */
#define SPI_PACKET_CNT_EN (1 << 16)
/* Swap config */
#define SPI_TX_SWAP_EN (1 << 0)
#define SPI_TX_BYTE_SWAP (1 << 2)
#define SPI_TX_HWORD_SWAP (1 << 3)
#define SPI_TX_BYTE_SWAP (1 << 2)
#define SPI_RX_SWAP_EN (1 << 4)
#define SPI_RX_BYTE_SWAP (1 << 6)
#define SPI_RX_HWORD_SWAP (1 << 7)
#endif /* __ASSEMBLY__ */
#endif

View File

@ -61,6 +61,9 @@
/* GPMC */
#define OMAP54XX_GPMC_BASE 0x50000000
/* QSPI */
#define QSPI_BASE 0x4B300000
/*
* Hardware Register Details
*/

View File

@ -15,6 +15,7 @@
#define BOOT_DEVICE_MMC1 5
#define BOOT_DEVICE_MMC2 6
#define BOOT_DEVICE_MMC2_2 7
#define BOOT_DEVICE_SPI 10
#define MMC_BOOT_DEVICES_START BOOT_DEVICE_MMC1
#define MMC_BOOT_DEVICES_END BOOT_DEVICE_MMC2_2

View File

@ -266,6 +266,7 @@ struct prcm_regs {
u32 cm_l4per_mmcsd4_clkctrl;
u32 cm_l4per_msprohg_clkctrl;
u32 cm_l4per_slimbus2_clkctrl;
u32 cm_l4per_qspi_clkctrl;
u32 cm_l4per_uart1_clkctrl;
u32 cm_l4per_uart2_clkctrl;
u32 cm_l4per_uart3_clkctrl;

View File

@ -51,5 +51,15 @@ const struct pad_conf_entry core_padconf_array_essential[] = {
{RGMII0_RXD2, (IEN | M0) },
{RGMII0_RXD1, (IEN | M0) },
{RGMII0_RXD0, (IEN | M0) },
{GPMC_A13, (IEN | PDIS | M1)}, /* QSPI1_RTCLK */
{GPMC_A14, (IEN | PDIS | M1)}, /* QSPI1_D[3] */
{GPMC_A15, (IEN | PDIS | M1)}, /* QSPI1_D[2] */
{GPMC_A16, (IEN | PDIS | M1)}, /* QSPI1_D[1] */
{GPMC_A17, (IEN | PDIS | M1)}, /* QSPI1_D[0] */
{GPMC_A18, (M1)}, /* QSPI1_SCLK */
{GPMC_A3, (IEN | PDIS | M1)}, /* QSPI1_CS2 */
{GPMC_A4, (IEN | PDIS | M1)}, /* QSPI1_CS3 */
{GPMC_CS2, (IEN | PTU | PDIS | M1)}, /* QSPI1_CS0 */
{GPMC_CS3, (IEN | PTU | PDIS | M1)}, /* QSPI1_CS1*/
};
#endif /* _MUX_DATA_DRA7XX_H_ */

View File

@ -0,0 +1,48 @@
-------------------------------------------------
Simple steps used to test the QSPI at U-Boot
-------------------------------------------------
For #1, build the patched U-Boot and load MLO/u-boot.img
----------------------------------
Boot from another medium like MMC
----------------------------------
U-Boot# mmc dev 0
mmc0 is current device
U-Boot# fatload mmc 0 0x82000000 MLO
reading MLO
55872 bytes read in 8 ms (6.7 MiB/s)
U-Boot# fatload mmc 0 0x83000000 u-boot.img
reading u-boot.img
248600 bytes read in 19 ms (12.5 MiB/s)
--------------------------------------------------
Commands to erase/write u-boot/mlo to flash device
--------------------------------------------------
U-Boot# sf probe 0
SF: Detected S25FL256S_64K with page size 256 Bytes, erase size 64 KiB, total 32 MiB, mapped at 5c000000
SF: Warning - Only lower 16MiB accessible, Full access #define CONFIG_SPI_FLASH_BAR
U-Boot# sf erase 0 0x10000
SF: 65536 bytes @ 0x0 Erased: OK
U-Boot# sf erase 0x20000 0x10000
SF: 65536 bytes @ 0x20000 Erased: OK
U-Boot# sf erase 0x30000 0x10000
SF: 65536 bytes @ 0x30000 Erased: OK
U-Boot# sf erase 0x40000 0x10000
SF: 65536 bytes @ 0x40000 Erased: OK
U-Boot# sf erase 0x50000 0x10000
SF: 65536 bytes @ 0x50000 Erased: OK
U-Boot# sf erase 0x60000 0x10000
SF: 65536 bytes @ 0x60000 Erased: OK
U-Boot# sf write 82000000 0 0x10000
SF: 65536 bytes @ 0x0 Written: OK
U-Boot# sf write 83000000 0x20000 0x60000
SF: 393216 bytes @ 0x20000 Written: OK
For #2, set sysboot to QSPI-1 boot mode(SYSBOOT[5:0] = 100110) and power
on. ROM should find the GP header at offset 0 and load/execute SPL. SPL
then detects that ROM was in QSPI-1 mode (boot code 10) and attempts to
find a U-Boot image header at offset 0x20000 (set in the config file)
and proceeds to load that image using the U-Boot image payload offset/size
from the header. It will then start U-Boot.

View File

@ -0,0 +1,47 @@
QSPI U-boot support
------------------
Host processor is connected to serial flash device via qpsi
interface. QSPI is a kind of spi module that allows single,
dual and quad read access to external spi devices. The module
has a memory mapped interface which provide direct interface
for accessing data form external spi devices.
The one QSPI in the device is primarily intended for fast booting
from Quad SPI flash devices.
Usecase
-------
MLO/u-boot.img will be flashed from SD/MMC to the flash device
using serial flash erase and write commands. Then, switch settings
will be changed to qspi boot. Then, the ROM code will read MLO
from the predefined location in the flash, where it was flashed and
execute it after storing it in SDRAM. Then, the MLO will read
u-boot.img from flash and execute it from SDRAM.
SPI mode
-------
SPI mode uses mtd spi framework for transfer and reception of data.
Can be used in:
1. Normal mode: use single pin for transfers
2. Dual Mode: use two pins for transfers.
3. Quad mode: use four pin for transfer
Memory mapped read mode
-----------------------
In this, SPI controller is configured using configuration port and then
controler is switched to memory mapped port for data read.
Driver
------
drivers/qspi/ti_qspi.c
- Newly created file which is responsible for configuring the
qspi controller and also for providing the low level api which
is responsible for transferring the datas from host controller
to flash device and vice versa.
Testing
-------
A seperated file named README.dra_qspi_test has been created which gives all the
details about the commands required to test qspi at u-boot level.

31
doc/SPI/status.txt Normal file
View File

@ -0,0 +1,31 @@
Status on SPI subsystem:
=======================
SPI COMMAND (common/cmd_sf, cmd_spi):
-
SPI FLASH (drivers/mtd/spi):
- sf_probe.c: SPI flash probing code.
- sf_ops.c: SPI flash operations code.
- sf.c: SPI flash interface, which interacts controller driver.
- 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.
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.

View File

@ -14,16 +14,11 @@ COBJS-$(CONFIG_SPL_SPI_LOAD) += spi_spl_load.o
COBJS-$(CONFIG_SPL_SPI_BOOT) += fsl_espi_spl.o
endif
COBJS-$(CONFIG_SPI_FLASH) += spi_flash.o
COBJS-$(CONFIG_SPI_FLASH_ATMEL) += atmel.o
COBJS-$(CONFIG_SPI_FLASH_EON) += eon.o
COBJS-$(CONFIG_SPI_FLASH_GIGADEVICE) += gigadevice.o
COBJS-$(CONFIG_SPI_FLASH_MACRONIX) += macronix.o
COBJS-$(CONFIG_SPI_FLASH_SPANSION) += spansion.o
COBJS-$(CONFIG_SPI_FLASH_SST) += sst.o
COBJS-$(CONFIG_SPI_FLASH_STMICRO) += stmicro.o
COBJS-$(CONFIG_SPI_FLASH_WINBOND) += winbond.o
COBJS-$(CONFIG_SPI_FRAM_RAMTRON) += ramtron.o
ifdef CONFIG_CMD_SF
COBJS-y += sf.o
endif
COBJS-$(CONFIG_SPI_FLASH) += sf_probe.o sf_ops.o
COBJS-$(CONFIG_SPI_FRAM_RAMTRON) += ramtron.o
COBJS-$(CONFIG_SPI_M95XXX) += eeprom_m95xxx.o
COBJS := $(COBJS-y)

View File

@ -1,544 +0,0 @@
/*
* Atmel SPI DataFlash support
*
* Copyright (C) 2008 Atmel Corporation
* Licensed under the GPL-2 or later.
*/
#include <common.h>
#include <malloc.h>
#include <spi_flash.h>
#include "spi_flash_internal.h"
/* AT45-specific commands */
#define CMD_AT45_READ_STATUS 0xd7
#define CMD_AT45_ERASE_PAGE 0x81
#define CMD_AT45_LOAD_PROG_BUF1 0x82
#define CMD_AT45_LOAD_BUF1 0x84
#define CMD_AT45_LOAD_PROG_BUF2 0x85
#define CMD_AT45_LOAD_BUF2 0x87
#define CMD_AT45_PROG_BUF1 0x88
#define CMD_AT45_PROG_BUF2 0x89
/* AT45 status register bits */
#define AT45_STATUS_P2_PAGE_SIZE (1 << 0)
#define AT45_STATUS_READY (1 << 7)
/* DataFlash family IDs, as obtained from the second idcode byte */
#define DF_FAMILY_AT26F 0
#define DF_FAMILY_AT45 1
#define DF_FAMILY_AT26DF 2 /* AT25DF and AT26DF */
struct atmel_spi_flash_params {
u8 idcode1;
/* Log2 of page size in power-of-two mode */
u8 l2_page_size;
u8 pages_per_block;
u8 blocks_per_sector;
u8 nr_sectors;
const char *name;
};
/* spi_flash needs to be first so upper layers can free() it */
struct atmel_spi_flash {
struct spi_flash flash;
const struct atmel_spi_flash_params *params;
};
static inline struct atmel_spi_flash *
to_atmel_spi_flash(struct spi_flash *flash)
{
return container_of(flash, struct atmel_spi_flash, flash);
}
static const struct atmel_spi_flash_params atmel_spi_flash_table[] = {
{
.idcode1 = 0x22,
.l2_page_size = 8,
.pages_per_block = 8,
.blocks_per_sector = 16,
.nr_sectors = 4,
.name = "AT45DB011D",
},
{
.idcode1 = 0x23,
.l2_page_size = 8,
.pages_per_block = 8,
.blocks_per_sector = 16,
.nr_sectors = 8,
.name = "AT45DB021D",
},
{
.idcode1 = 0x24,
.l2_page_size = 8,
.pages_per_block = 8,
.blocks_per_sector = 32,
.nr_sectors = 8,
.name = "AT45DB041D",
},
{
.idcode1 = 0x25,
.l2_page_size = 8,
.pages_per_block = 8,
.blocks_per_sector = 32,
.nr_sectors = 16,
.name = "AT45DB081D",
},
{
.idcode1 = 0x26,
.l2_page_size = 9,
.pages_per_block = 8,
.blocks_per_sector = 32,
.nr_sectors = 16,
.name = "AT45DB161D",
},
{
.idcode1 = 0x27,
.l2_page_size = 9,
.pages_per_block = 8,
.blocks_per_sector = 64,
.nr_sectors = 64,
.name = "AT45DB321D",
},
{
.idcode1 = 0x28,
.l2_page_size = 10,
.pages_per_block = 8,
.blocks_per_sector = 32,
.nr_sectors = 32,
.name = "AT45DB642D",
},
{
.idcode1 = 0x47,
.l2_page_size = 8,
.pages_per_block = 16,
.blocks_per_sector = 16,
.nr_sectors = 64,
.name = "AT25DF321",
},
};
static int at45_wait_ready(struct spi_flash *flash, unsigned long timeout)
{
struct spi_slave *spi = flash->spi;
unsigned long timebase;
int ret;
u8 cmd = CMD_AT45_READ_STATUS;
u8 status;
timebase = get_timer(0);
ret = spi_xfer(spi, 8, &cmd, NULL, SPI_XFER_BEGIN);
if (ret)
return -1;
do {
ret = spi_xfer(spi, 8, NULL, &status, 0);
if (ret)
return -1;
if (status & AT45_STATUS_READY)
break;
} while (get_timer(timebase) < timeout);
/* Deactivate CS */
spi_xfer(spi, 0, NULL, NULL, SPI_XFER_END);
if (status & AT45_STATUS_READY)
return 0;
/* Timed out */
return -1;
}
/*
* Assemble the address part of a command for AT45 devices in
* non-power-of-two page size mode.
*/
static void at45_build_address(struct atmel_spi_flash *asf, u8 *cmd, u32 offset)
{
unsigned long page_addr;
unsigned long byte_addr;
unsigned long page_size;
unsigned int page_shift;
/*
* The "extra" space per page is the power-of-two page size
* divided by 32.
*/
page_shift = asf->params->l2_page_size;
page_size = (1 << page_shift) + (1 << (page_shift - 5));
page_shift++;
page_addr = offset / page_size;
byte_addr = offset % page_size;
cmd[0] = page_addr >> (16 - page_shift);
cmd[1] = page_addr << (page_shift - 8) | (byte_addr >> 8);
cmd[2] = byte_addr;
}
static int dataflash_read_fast_at45(struct spi_flash *flash,
u32 offset, size_t len, void *buf)
{
struct atmel_spi_flash *asf = to_atmel_spi_flash(flash);
u8 cmd[5];
cmd[0] = CMD_READ_ARRAY_FAST;
at45_build_address(asf, cmd + 1, offset);
cmd[4] = 0x00;
return spi_flash_read_common(flash, cmd, sizeof(cmd), buf, len);
}
/*
* TODO: the two write funcs (_p2/_at45) should get unified ...
*/
static int dataflash_write_p2(struct spi_flash *flash,
u32 offset, size_t len, const void *buf)
{
struct atmel_spi_flash *asf = to_atmel_spi_flash(flash);
unsigned long page_size;
u32 addr = offset;
size_t chunk_len;
size_t actual;
int ret;
u8 cmd[4];
/*
* TODO: This function currently uses only page buffer #1. We can
* speed this up by using both buffers and loading one buffer while
* the other is being programmed into main memory.
*/
page_size = (1 << asf->params->l2_page_size);
ret = spi_claim_bus(flash->spi);
if (ret) {
debug("SF: Unable to claim SPI bus\n");
return ret;
}
for (actual = 0; actual < len; actual += chunk_len) {
chunk_len = min(len - actual, page_size - (addr % page_size));
/* Use the same address bits for both commands */
cmd[0] = CMD_AT45_LOAD_BUF1;
cmd[1] = addr >> 16;
cmd[2] = addr >> 8;
cmd[3] = addr;
ret = spi_flash_cmd_write(flash->spi, cmd, 4,
buf + actual, chunk_len);
if (ret < 0) {
debug("SF: Loading AT45 buffer failed\n");
goto out;
}
cmd[0] = CMD_AT45_PROG_BUF1;
ret = spi_flash_cmd_write(flash->spi, cmd, 4, NULL, 0);
if (ret < 0) {
debug("SF: AT45 page programming failed\n");
goto out;
}
ret = at45_wait_ready(flash, SPI_FLASH_PROG_TIMEOUT);
if (ret < 0) {
debug("SF: AT45 page programming timed out\n");
goto out;
}
addr += chunk_len;
}
debug("SF: AT45: Successfully programmed %zu bytes @ 0x%x\n",
len, offset);
ret = 0;
out:
spi_release_bus(flash->spi);
return ret;
}
static int dataflash_write_at45(struct spi_flash *flash,
u32 offset, size_t len, const void *buf)
{
struct atmel_spi_flash *asf = to_atmel_spi_flash(flash);
unsigned long page_addr;
unsigned long byte_addr;
unsigned long page_size;
unsigned int page_shift;
size_t chunk_len;
size_t actual;
int ret;
u8 cmd[4];
/*
* TODO: This function currently uses only page buffer #1. We can
* speed this up by using both buffers and loading one buffer while
* the other is being programmed into main memory.
*/
page_shift = asf->params->l2_page_size;
page_size = (1 << page_shift) + (1 << (page_shift - 5));
page_shift++;
page_addr = offset / page_size;
byte_addr = offset % page_size;
ret = spi_claim_bus(flash->spi);
if (ret) {
debug("SF: Unable to claim SPI bus\n");
return ret;
}
for (actual = 0; actual < len; actual += chunk_len) {
chunk_len = min(len - actual, page_size - byte_addr);
/* Use the same address bits for both commands */
cmd[0] = CMD_AT45_LOAD_BUF1;
cmd[1] = page_addr >> (16 - page_shift);
cmd[2] = page_addr << (page_shift - 8) | (byte_addr >> 8);
cmd[3] = byte_addr;
ret = spi_flash_cmd_write(flash->spi, cmd, 4,
buf + actual, chunk_len);
if (ret < 0) {
debug("SF: Loading AT45 buffer failed\n");
goto out;
}
cmd[0] = CMD_AT45_PROG_BUF1;
ret = spi_flash_cmd_write(flash->spi, cmd, 4, NULL, 0);
if (ret < 0) {
debug("SF: AT45 page programming failed\n");
goto out;
}
ret = at45_wait_ready(flash, SPI_FLASH_PROG_TIMEOUT);
if (ret < 0) {
debug("SF: AT45 page programming timed out\n");
goto out;
}
page_addr++;
byte_addr = 0;
}
debug("SF: AT45: Successfully programmed %zu bytes @ 0x%x\n",
len, offset);
ret = 0;
out:
spi_release_bus(flash->spi);
return ret;
}
/*
* TODO: the two erase funcs (_p2/_at45) should get unified ...
*/
static int dataflash_erase_p2(struct spi_flash *flash, u32 offset, size_t len)
{
struct atmel_spi_flash *asf = to_atmel_spi_flash(flash);
unsigned long page_size;
size_t actual;
int ret;
u8 cmd[4];
/*
* TODO: This function currently uses page erase only. We can
* probably speed things up by using block and/or sector erase
* when possible.
*/
page_size = (1 << asf->params->l2_page_size);
if (offset % page_size || len % page_size) {
debug("SF: Erase offset/length not multiple of page size\n");
return -1;
}
cmd[0] = CMD_AT45_ERASE_PAGE;
cmd[3] = 0x00;
ret = spi_claim_bus(flash->spi);
if (ret) {
debug("SF: Unable to claim SPI bus\n");
return ret;
}
for (actual = 0; actual < len; actual += page_size) {
cmd[1] = offset >> 16;
cmd[2] = offset >> 8;
ret = spi_flash_cmd_write(flash->spi, cmd, 4, NULL, 0);
if (ret < 0) {
debug("SF: AT45 page erase failed\n");
goto out;
}
ret = at45_wait_ready(flash, SPI_FLASH_PAGE_ERASE_TIMEOUT);
if (ret < 0) {
debug("SF: AT45 page erase timed out\n");
goto out;
}
offset += page_size;
}
debug("SF: AT45: Successfully erased %zu bytes @ 0x%x\n",
len, offset);
ret = 0;
out:
spi_release_bus(flash->spi);
return ret;
}
static int dataflash_erase_at45(struct spi_flash *flash, u32 offset, size_t len)
{
struct atmel_spi_flash *asf = to_atmel_spi_flash(flash);
unsigned long page_addr;
unsigned long page_size;
unsigned int page_shift;
size_t actual;
int ret;
u8 cmd[4];
/*
* TODO: This function currently uses page erase only. We can
* probably speed things up by using block and/or sector erase
* when possible.
*/
page_shift = asf->params->l2_page_size;
page_size = (1 << page_shift) + (1 << (page_shift - 5));
page_shift++;
page_addr = offset / page_size;
if (offset % page_size || len % page_size) {
debug("SF: Erase offset/length not multiple of page size\n");
return -1;
}
cmd[0] = CMD_AT45_ERASE_PAGE;
cmd[3] = 0x00;
ret = spi_claim_bus(flash->spi);
if (ret) {
debug("SF: Unable to claim SPI bus\n");
return ret;
}
for (actual = 0; actual < len; actual += page_size) {
cmd[1] = page_addr >> (16 - page_shift);
cmd[2] = page_addr << (page_shift - 8);
ret = spi_flash_cmd_write(flash->spi, cmd, 4, NULL, 0);
if (ret < 0) {
debug("SF: AT45 page erase failed\n");
goto out;
}
ret = at45_wait_ready(flash, SPI_FLASH_PAGE_ERASE_TIMEOUT);
if (ret < 0) {
debug("SF: AT45 page erase timed out\n");
goto out;
}
page_addr++;
}
debug("SF: AT45: Successfully erased %zu bytes @ 0x%x\n",
len, offset);
ret = 0;
out:
spi_release_bus(flash->spi);
return ret;
}
struct spi_flash *spi_flash_probe_atmel(struct spi_slave *spi, u8 *idcode)
{
const struct atmel_spi_flash_params *params;
unsigned page_size;
unsigned int family;
struct atmel_spi_flash *asf;
unsigned int i;
int ret;
u8 status;
for (i = 0; i < ARRAY_SIZE(atmel_spi_flash_table); i++) {
params = &atmel_spi_flash_table[i];
if (params->idcode1 == idcode[1])
break;
}
if (i == ARRAY_SIZE(atmel_spi_flash_table)) {
debug("SF: Unsupported DataFlash ID %02x\n",
idcode[1]);
return NULL;
}
asf = spi_flash_alloc(struct atmel_spi_flash, spi, params->name);
if (!asf) {
debug("SF: Failed to allocate memory\n");
return NULL;
}
asf->params = params;
/* Assuming power-of-two page size initially. */
page_size = 1 << params->l2_page_size;
family = idcode[1] >> 5;
switch (family) {
case DF_FAMILY_AT45:
/*
* AT45 chips have configurable page size. The status
* register indicates which configuration is active.
*/
ret = spi_flash_cmd(spi, CMD_AT45_READ_STATUS, &status, 1);
if (ret)
goto err;
debug("SF: AT45 status register: %02x\n", status);
if (!(status & AT45_STATUS_P2_PAGE_SIZE)) {
asf->flash.read = dataflash_read_fast_at45;
asf->flash.write = dataflash_write_at45;
asf->flash.erase = dataflash_erase_at45;
page_size += 1 << (params->l2_page_size - 5);
} else {
asf->flash.write = dataflash_write_p2;
asf->flash.erase = dataflash_erase_p2;
}
asf->flash.page_size = page_size;
asf->flash.sector_size = page_size;
break;
case DF_FAMILY_AT26F:
case DF_FAMILY_AT26DF:
asf->flash.page_size = page_size;
asf->flash.sector_size = 4096;
/* clear SPRL# bit for locked flash */
spi_flash_cmd_write_status(&asf->flash, 0);
break;
default:
debug("SF: Unsupported DataFlash family %u\n", family);
goto err;
}
asf->flash.size = page_size * params->pages_per_block
* params->blocks_per_sector
* params->nr_sectors;
return &asf->flash;
err:
free(asf);
return NULL;
}

View File

@ -1,60 +0,0 @@
/*
* (C) Copyright 2010, ucRobotics Inc.
* Author: Chong Huang <chuang@ucrobotics.com>
* Licensed under the GPL-2 or later.
*/
#include <common.h>
#include <malloc.h>
#include <spi_flash.h>
#include "spi_flash_internal.h"
struct eon_spi_flash_params {
u8 idcode1;
u16 nr_sectors;
const char *name;
};
static const struct eon_spi_flash_params eon_spi_flash_table[] = {
{
.idcode1 = 0x16,
.nr_sectors = 1024,
.name = "EN25Q32B",
},
{
.idcode1 = 0x18,
.nr_sectors = 4096,
.name = "EN25Q128",
},
};
struct spi_flash *spi_flash_probe_eon(struct spi_slave *spi, u8 *idcode)
{
const struct eon_spi_flash_params *params;
struct spi_flash *flash;
unsigned int i;
for (i = 0; i < ARRAY_SIZE(eon_spi_flash_table); ++i) {
params = &eon_spi_flash_table[i];
if (params->idcode1 == idcode[2])
break;
}
if (i == ARRAY_SIZE(eon_spi_flash_table)) {
debug("SF: Unsupported EON ID %02x\n", idcode[1]);
return NULL;
}
flash = spi_flash_alloc_base(spi, params->name);
if (!flash) {
debug("SF: Failed to allocate memory\n");
return NULL;
}
flash->page_size = 256;
flash->sector_size = 256 * 16 * 16;
flash->size = 256 * 16 * params->nr_sectors;
return flash;
}

View File

@ -1,65 +0,0 @@
/*
* Gigadevice SPI flash driver
* Copyright 2013, Samsung Electronics Co., Ltd.
* Author: Banajit Goswami <banajit.g@samsung.com>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <malloc.h>
#include <spi_flash.h>
#include "spi_flash_internal.h"
struct gigadevice_spi_flash_params {
uint16_t id;
uint16_t nr_blocks;
const char *name;
};
static const struct gigadevice_spi_flash_params gigadevice_spi_flash_table[] = {
{
.id = 0x6016,
.nr_blocks = 64,
.name = "GD25LQ",
},
{
.id = 0x4017,
.nr_blocks = 128,
.name = "GD25Q64B",
},
};
struct spi_flash *spi_flash_probe_gigadevice(struct spi_slave *spi, u8 *idcode)
{
const struct gigadevice_spi_flash_params *params;
struct spi_flash *flash;
unsigned int i;
for (i = 0; i < ARRAY_SIZE(gigadevice_spi_flash_table); i++) {
params = &gigadevice_spi_flash_table[i];
if (params->id == ((idcode[1] << 8) | idcode[2]))
break;
}
if (i == ARRAY_SIZE(gigadevice_spi_flash_table)) {
debug("SF: Unsupported Gigadevice ID %02x%02x\n",
idcode[1], idcode[2]);
return NULL;
}
flash = spi_flash_alloc_base(spi, params->name);
if (!flash) {
debug("SF: Failed to allocate memory\n");
return NULL;
}
/* page_size */
flash->page_size = 256;
/* sector_size = page_size * pages_per_sector */
flash->sector_size = flash->page_size * 16;
/* size = sector_size * sector_per_block * number of blocks */
flash->size = flash->sector_size * 16 * params->nr_blocks;
return flash;
}

View File

@ -1,98 +0,0 @@
/*
* Copyright 2009(C) Marvell International Ltd. and its affiliates
* Prafulla Wadaskar <prafulla@marvell.com>
*
* Based on drivers/mtd/spi/stmicro.c
*
* Copyright 2008, Network Appliance Inc.
* Jason McMullan <mcmullan@netapp.com>
*
* Copyright (C) 2004-2007 Freescale Semiconductor, Inc.
* TsiChung Liew (Tsi-Chung.Liew@freescale.com)
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <malloc.h>
#include <spi_flash.h>
#include "spi_flash_internal.h"
struct macronix_spi_flash_params {
u16 idcode;
u16 nr_blocks;
const char *name;
};
static const struct macronix_spi_flash_params macronix_spi_flash_table[] = {
{
.idcode = 0x2013,
.nr_blocks = 8,
.name = "MX25L4005",
},
{
.idcode = 0x2014,
.nr_blocks = 16,
.name = "MX25L8005",
},
{
.idcode = 0x2015,
.nr_blocks = 32,
.name = "MX25L1605D",
},
{
.idcode = 0x2016,
.nr_blocks = 64,
.name = "MX25L3205D",
},
{
.idcode = 0x2017,
.nr_blocks = 128,
.name = "MX25L6405D",
},
{
.idcode = 0x2018,
.nr_blocks = 256,
.name = "MX25L12805D",
},
{
.idcode = 0x2618,
.nr_blocks = 256,
.name = "MX25L12855E",
},
};
struct spi_flash *spi_flash_probe_macronix(struct spi_slave *spi, u8 *idcode)
{
const struct macronix_spi_flash_params *params;
struct spi_flash *flash;
unsigned int i;
u16 id = idcode[2] | idcode[1] << 8;
for (i = 0; i < ARRAY_SIZE(macronix_spi_flash_table); i++) {
params = &macronix_spi_flash_table[i];
if (params->idcode == id)
break;
}
if (i == ARRAY_SIZE(macronix_spi_flash_table)) {
debug("SF: Unsupported Macronix ID %04x\n", id);
return NULL;
}
flash = spi_flash_alloc_base(spi, params->name);
if (!flash) {
debug("SF: Failed to allocate memory\n");
return NULL;
}
flash->page_size = 256;
flash->sector_size = 256 * 16 * 16;
flash->size = flash->sector_size * params->nr_blocks;
/* Clear BP# bits for read-only flash */
spi_flash_cmd_write_status(flash, 0);
return flash;
}

View File

@ -36,7 +36,7 @@
#include <common.h>
#include <malloc.h>
#include <spi_flash.h>
#include "spi_flash_internal.h"
#include "sf_internal.h"
/*
* Properties of supported FRAMs
@ -214,7 +214,8 @@ static int ramtron_erase(struct spi_flash *flash, u32 offset, size_t len)
* nore: we are called here with idcode pointing to the first non-0x7f byte
* already!
*/
struct spi_flash *spi_fram_probe_ramtron(struct spi_slave *spi, u8 *idcode)
static struct spi_flash *spi_fram_probe_ramtron(struct spi_slave *spi,
u8 *idcode)
{
const struct ramtron_spi_fram_params *params;
struct ramtron_spi_fram *sn;
@ -270,7 +271,7 @@ struct spi_flash *spi_fram_probe_ramtron(struct spi_slave *spi, u8 *idcode)
return NULL;
found:
sn = spi_flash_alloc(struct ramtron_spi_fram, spi, params->name);
sn = malloc(sizeof(*sn));
if (!sn) {
debug("SF: Failed to allocate memory\n");
return NULL;
@ -285,3 +286,118 @@ found:
return &sn->flash;
}
/*
* The following table holds all device probe functions
* (All flashes are removed and implemented a common probe at
* spi_flash_probe.c)
*
* shift: number of continuation bytes before the ID
* idcode: the expected IDCODE or 0xff for non JEDEC devices
* probe: the function to call
*
* Non JEDEC devices should be ordered in the table such that
* the probe functions with best detection algorithms come first.
*
* Several matching entries are permitted, they will be tried
* in sequence until a probe function returns non NULL.
*
* IDCODE_CONT_LEN may be redefined if a device needs to declare a
* larger "shift" value. IDCODE_PART_LEN generally shouldn't be
* changed. This is the max number of bytes probe functions may
* examine when looking up part-specific identification info.
*
* Probe functions will be given the idcode buffer starting at their
* manu id byte (the "idcode" in the table below). In other words,
* all of the continuation bytes will be skipped (the "shift" below).
*/
#define IDCODE_CONT_LEN 0
#define IDCODE_PART_LEN 5
static const struct {
const u8 shift;
const u8 idcode;
struct spi_flash *(*probe) (struct spi_slave *spi, u8 *idcode);
} flashes[] = {
/* Keep it sorted by define name */
#ifdef CONFIG_SPI_FRAM_RAMTRON
{ 6, 0xc2, spi_fram_probe_ramtron, },
# undef IDCODE_CONT_LEN
# define IDCODE_CONT_LEN 6
#endif
#ifdef CONFIG_SPI_FRAM_RAMTRON_NON_JEDEC
{ 0, 0xff, spi_fram_probe_ramtron, },
#endif
};
#define IDCODE_LEN (IDCODE_CONT_LEN + IDCODE_PART_LEN)
struct spi_flash *spi_flash_probe(unsigned int bus, unsigned int cs,
unsigned int max_hz, unsigned int spi_mode)
{
struct spi_slave *spi;
struct spi_flash *flash = NULL;
int ret, i, shift;
u8 idcode[IDCODE_LEN], *idp;
spi = spi_setup_slave(bus, cs, max_hz, spi_mode);
if (!spi) {
printf("SF: Failed to set up slave\n");
return NULL;
}
ret = spi_claim_bus(spi);
if (ret) {
debug("SF: Failed to claim SPI bus: %d\n", ret);
goto err_claim_bus;
}
/* Read the ID codes */
ret = spi_flash_cmd(spi, CMD_READ_ID, idcode, sizeof(idcode));
if (ret)
goto err_read_id;
#ifdef DEBUG
printf("SF: Got idcodes\n");
print_buffer(0, idcode, 1, sizeof(idcode), 0);
#endif
/* count the number of continuation bytes */
for (shift = 0, idp = idcode;
shift < IDCODE_CONT_LEN && *idp == 0x7f;
++shift, ++idp)
continue;
/* search the table for matches in shift and id */
for (i = 0; i < ARRAY_SIZE(flashes); ++i)
if (flashes[i].shift == shift && flashes[i].idcode == *idp) {
/* we have a match, call probe */
flash = flashes[i].probe(spi, idp);
if (flash)
break;
}
if (!flash) {
printf("SF: Unsupported manufacturer %02x\n", *idp);
goto err_manufacturer_probe;
}
printf("SF: Detected %s with total size ", flash->name);
print_size(flash->size, "");
puts("\n");
spi_release_bus(spi);
return flash;
err_manufacturer_probe:
err_read_id:
spi_release_bus(spi);
err_claim_bus:
spi_free_slave(spi);
return NULL;
}
void spi_flash_free(struct spi_flash *flash)
{
spi_free_slave(flash->spi);
free(flash);
}

54
drivers/mtd/spi/sf.c Normal file
View File

@ -0,0 +1,54 @@
/*
* SPI flash interface
*
* Copyright (C) 2008 Atmel Corporation
* Copyright (C) 2010 Reinhard Meyer, EMK Elektronik
*
* Licensed under the GPL-2 or later.
*/
#include <common.h>
#include <spi.h>
static int spi_flash_read_write(struct spi_slave *spi,
const u8 *cmd, size_t cmd_len,
const u8 *data_out, u8 *data_in,
size_t data_len)
{
unsigned long flags = SPI_XFER_BEGIN;
int ret;
if (data_len == 0)
flags |= SPI_XFER_END;
ret = spi_xfer(spi, cmd_len * 8, cmd, NULL, flags);
if (ret) {
debug("SF: Failed to send command (%zu bytes): %d\n",
cmd_len, ret);
} else if (data_len != 0) {
ret = spi_xfer(spi, data_len * 8, data_out, data_in,
SPI_XFER_END);
if (ret)
debug("SF: Failed to transfer %zu bytes of data: %d\n",
data_len, ret);
}
return ret;
}
int spi_flash_cmd_read(struct spi_slave *spi, const u8 *cmd,
size_t cmd_len, void *data, size_t data_len)
{
return spi_flash_read_write(spi, cmd, cmd_len, NULL, data, data_len);
}
int spi_flash_cmd(struct spi_slave *spi, u8 cmd, void *response, size_t len)
{
return spi_flash_cmd_read(spi, &cmd, 1, response, len);
}
int spi_flash_cmd_write(struct spi_slave *spi, const u8 *cmd, size_t cmd_len,
const void *data, size_t data_len)
{
return spi_flash_read_write(spi, cmd, cmd_len, data, NULL, data_len);
}

View File

@ -2,42 +2,43 @@
* SPI flash internal definitions
*
* Copyright (C) 2008 Atmel Corporation
* Copyright (C) 2013 Jagannadha Sutradharudu Teki, Xilinx Inc.
*
* Licensed under the GPL-2 or later.
*/
/* Common parameters -- kind of high, but they should only occur when there
* is a problem (and well your system already is broken), so err on the side
* of caution in case we're dealing with slower SPI buses and/or processors.
*/
#define SPI_FLASH_PROG_TIMEOUT (2 * CONFIG_SYS_HZ)
#define SPI_FLASH_PAGE_ERASE_TIMEOUT (5 * CONFIG_SYS_HZ)
#define SPI_FLASH_SECTOR_ERASE_TIMEOUT (10 * CONFIG_SYS_HZ)
#ifndef _SPI_FLASH_INTERNAL_H_
#define _SPI_FLASH_INTERNAL_H_
/* Common commands */
#define CMD_READ_ID 0x9f
#define SPI_FLASH_16MB_BOUN 0x1000000
#define CMD_READ_ARRAY_SLOW 0x03
#define CMD_READ_ARRAY_FAST 0x0b
/* SECT flags */
#define SECT_4K (1 << 1)
#define SECT_32K (1 << 2)
#define E_FSR (1 << 3)
/* Erase commands */
#define CMD_ERASE_4K 0x20
#define CMD_ERASE_32K 0x52
#define CMD_ERASE_CHIP 0xc7
#define CMD_ERASE_64K 0xd8
/* Write commands */
#define CMD_WRITE_STATUS 0x01
#define CMD_PAGE_PROGRAM 0x02
#define CMD_WRITE_DISABLE 0x04
#define CMD_READ_STATUS 0x05
#define CMD_FLAG_STATUS 0x70
#define CMD_WRITE_ENABLE 0x06
#define CMD_ERASE_4K 0x20
#define CMD_ERASE_32K 0x52
#define CMD_ERASE_64K 0xd8
#define CMD_ERASE_CHIP 0xc7
#define CMD_READ_CONFIG 0x35
#define CMD_FLAG_STATUS 0x70
#define SPI_FLASH_16MB_BOUN 0x1000000
/* Read commands */
#define CMD_READ_ARRAY_SLOW 0x03
#define CMD_READ_ARRAY_FAST 0x0b
#define CMD_READ_ID 0x9f
/* Manufacture ID's */
#define SPI_FLASH_SPANSION_IDCODE0 0x01
#define SPI_FLASH_STMICRO_IDCODE0 0x20
#define SPI_FLASH_WINBOND_IDCODE0 0xef
#ifdef CONFIG_SPI_FLASH_BAR
/* Bank addr access commands */
#ifdef CONFIG_SPI_FLASH_BAR
# define CMD_BANKADDR_BRWR 0x17
# define CMD_BANKADDR_BRRD 0x16
# define CMD_EXTNADDR_WREAR 0xC5
@ -48,6 +49,21 @@
#define STATUS_WIP 0x01
#define STATUS_PEC 0x80
/* Flash timeout values */
#define SPI_FLASH_PROG_TIMEOUT (2 * CONFIG_SYS_HZ)
#define SPI_FLASH_PAGE_ERASE_TIMEOUT (5 * CONFIG_SYS_HZ)
#define SPI_FLASH_SECTOR_ERASE_TIMEOUT (10 * CONFIG_SYS_HZ)
/* SST specific */
#ifdef CONFIG_SPI_FLASH_SST
# define SST_WP 0x01 /* Supports AAI word program */
# define CMD_SST_BP 0x02 /* Byte Program */
# define CMD_SST_AAI_WP 0xAD /* Auto Address Incr Word Program */
int sst_write_wp(struct spi_flash *flash, u32 offset, size_t len,
const void *buf);
#endif
/* Send a single-byte command to the device and read the response */
int spi_flash_cmd(struct spi_slave *spi, u8 cmd, void *response, size_t len);
@ -58,9 +74,6 @@ int spi_flash_cmd(struct spi_slave *spi, u8 cmd, void *response, size_t len);
int spi_flash_cmd_read(struct spi_slave *spi, const u8 *cmd,
size_t cmd_len, void *data, size_t data_len);
int spi_flash_cmd_read_fast(struct spi_flash *flash, u32 offset,
size_t len, void *data);
/*
* Send a multi-byte command to the device followed by (optional)
* data. Used for programming the flash array, etc.
@ -68,46 +81,34 @@ int spi_flash_cmd_read_fast(struct spi_flash *flash, u32 offset,
int spi_flash_cmd_write(struct spi_slave *spi, const u8 *cmd, size_t cmd_len,
const void *data, size_t data_len);
/*
* Write the requested data out breaking it up into multiple write
* commands as needed per the write size.
*/
int spi_flash_cmd_write_multi(struct spi_flash *flash, u32 offset,
size_t len, const void *buf);
/*
* Enable writing on the SPI flash.
*/
/* 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);
/* Set quad enbale bit */
int spi_flash_set_qeb(struct spi_flash *flash);
/* Enable writing on the SPI flash */
static inline int spi_flash_cmd_write_enable(struct spi_flash *flash)
{
return spi_flash_cmd(flash->spi, CMD_WRITE_ENABLE, NULL, 0);
}
/*
* Disable writing on the SPI flash.
*/
/* Disable writing on the SPI flash */
static inline int spi_flash_cmd_write_disable(struct spi_flash *flash)
{
return spi_flash_cmd(flash->spi, CMD_WRITE_DISABLE, NULL, 0);
}
/* Program the status register. */
int spi_flash_cmd_write_status(struct spi_flash *flash, u8 sr);
#ifdef CONFIG_SPI_FLASH_BAR
/* Program the bank address register */
int spi_flash_cmd_bankaddr_write(struct spi_flash *flash, u8 bank_sel);
/* Configure the BAR - discover the bank cmds */
int spi_flash_bank_config(struct spi_flash *flash, u8 idcode0);
#endif
/*
* Same as spi_flash_cmd_read() except it also claims/releases the SPI
* bus. Used as common part of the ->read() operation.
* Send the read status command to the device and wait for the wip
* (write-in-progress) bit to clear itself.
*/
int spi_flash_read_common(struct spi_flash *flash, const u8 *cmd,
size_t cmd_len, void *data, size_t data_len);
int spi_flash_cmd_wait_ready(struct spi_flash *flash, unsigned long timeout);
/*
* Used for spi_flash write operation
* - SPI claim
@ -120,21 +121,22 @@ int spi_flash_write_common(struct spi_flash *flash, const u8 *cmd,
size_t cmd_len, const void *buf, size_t buf_len);
/*
* Send the read status command to the device and wait for the wip
* (write-in-progress) bit to clear itself.
* Flash write operation, support all possible write commands.
* Write the requested data out breaking it up into multiple write
* commands as needed per the write size.
*/
int spi_flash_cmd_wait_ready(struct spi_flash *flash, unsigned long timeout);
int spi_flash_cmd_write_ops(struct spi_flash *flash, u32 offset,
size_t len, const void *buf);
/* Erase sectors. */
int spi_flash_cmd_erase(struct spi_flash *flash, u32 offset, size_t len);
/*
* Same as spi_flash_cmd_read() except it also claims/releases the SPI
* bus. Used as common part of the ->read() operation.
*/
int spi_flash_read_common(struct spi_flash *flash, const u8 *cmd,
size_t cmd_len, void *data, size_t data_len);
/* Manufacturer-specific probe functions */
struct spi_flash *spi_flash_probe_spansion(struct spi_slave *spi, u8 *idcode);
struct spi_flash *spi_flash_probe_atmel(struct spi_slave *spi, u8 *idcode);
struct spi_flash *spi_flash_probe_eon(struct spi_slave *spi, u8 *idcode);
struct spi_flash *spi_flash_probe_macronix(struct spi_slave *spi, u8 *idcode);
struct spi_flash *spi_flash_probe_sst(struct spi_slave *spi, u8 *idcode);
struct spi_flash *spi_flash_probe_stmicro(struct spi_slave *spi, u8 *idcode);
struct spi_flash *spi_flash_probe_winbond(struct spi_slave *spi, u8 *idcode);
struct spi_flash *spi_fram_probe_ramtron(struct spi_slave *spi, u8 *idcode);
struct spi_flash *spi_flash_probe_gigadevice(struct spi_slave *spi, u8 *idcode);
/* Flash read operation, support all possible read commands */
int spi_flash_cmd_read_ops(struct spi_flash *flash, u32 offset,
size_t len, void *data);
#endif /* _SPI_FLASH_INTERNAL_H_ */

405
drivers/mtd/spi/sf_ops.c Normal file
View File

@ -0,0 +1,405 @@
/*
* SPI flash operations
*
* Copyright (C) 2008 Atmel Corporation
* Copyright (C) 2010 Reinhard Meyer, EMK Elektronik
* Copyright (C) 2013 Jagannadha Sutradharudu Teki, Xilinx Inc.
*
* Licensed under the GPL-2 or later.
*/
#include <common.h>
#include <spi.h>
#include <spi_flash.h>
#include <watchdog.h>
#include "sf_internal.h"
static void spi_flash_addr(u32 addr, u8 *cmd)
{
/* cmd[0] is actual command */
cmd[1] = addr >> 16;
cmd[2] = addr >> 8;
cmd[3] = addr >> 0;
}
int spi_flash_cmd_write_status(struct spi_flash *flash, u8 sr)
{
u8 cmd;
int ret;
cmd = CMD_WRITE_STATUS;
ret = spi_flash_write_common(flash, &cmd, 1, &sr, 1);
if (ret < 0) {
debug("SF: fail to write status register\n");
return ret;
}
return 0;
}
#ifdef CONFIG_SPI_FLASH_BAR
static int spi_flash_cmd_bankaddr_write(struct spi_flash *flash, u8 bank_sel)
{
u8 cmd;
int ret;
if (flash->bank_curr == bank_sel) {
debug("SF: not require to enable bank%d\n", bank_sel);
return 0;
}
cmd = flash->bank_write_cmd;
ret = spi_flash_write_common(flash, &cmd, 1, &bank_sel, 1);
if (ret < 0) {
debug("SF: fail to write bank register\n");
return ret;
}
flash->bank_curr = bank_sel;
return 0;
}
#endif
int spi_flash_cmd_wait_ready(struct spi_flash *flash, unsigned long timeout)
{
struct spi_slave *spi = flash->spi;
unsigned long timebase;
int ret;
u8 status;
u8 check_status = 0x0;
u8 poll_bit = STATUS_WIP;
u8 cmd = flash->poll_cmd;
if (cmd == CMD_FLAG_STATUS) {
poll_bit = STATUS_PEC;
check_status = poll_bit;
}
ret = spi_xfer(spi, 8, &cmd, NULL, SPI_XFER_BEGIN);
if (ret) {
debug("SF: fail to read %s status register\n",
cmd == CMD_READ_STATUS ? "read" : "flag");
return ret;
}
timebase = get_timer(0);
do {
WATCHDOG_RESET();
ret = spi_xfer(spi, 8, NULL, &status, 0);
if (ret)
return -1;
if ((status & poll_bit) == check_status)
break;
} while (get_timer(timebase) < timeout);
spi_xfer(spi, 0, NULL, NULL, SPI_XFER_END);
if ((status & poll_bit) == check_status)
return 0;
/* Timed out */
debug("SF: time out!\n");
return -1;
}
int spi_flash_write_common(struct spi_flash *flash, const u8 *cmd,
size_t cmd_len, const void *buf, size_t buf_len)
{
struct spi_slave *spi = flash->spi;
unsigned long timeout = SPI_FLASH_PROG_TIMEOUT;
int ret;
if (buf == NULL)
timeout = SPI_FLASH_PAGE_ERASE_TIMEOUT;
ret = spi_claim_bus(flash->spi);
if (ret) {
debug("SF: unable to claim SPI bus\n");
return ret;
}
ret = spi_flash_cmd_write_enable(flash);
if (ret < 0) {
debug("SF: enabling write failed\n");
return ret;
}
ret = spi_flash_cmd_write(spi, cmd, cmd_len, buf, buf_len);
if (ret < 0) {
debug("SF: write cmd failed\n");
return ret;
}
ret = spi_flash_cmd_wait_ready(flash, timeout);
if (ret < 0) {
debug("SF: write %s timed out\n",
timeout == SPI_FLASH_PROG_TIMEOUT ?
"program" : "page erase");
return ret;
}
spi_release_bus(spi);
return ret;
}
int spi_flash_cmd_erase_ops(struct spi_flash *flash, u32 offset, size_t len)
{
u32 erase_size;
u8 cmd[4];
int ret = -1;
erase_size = flash->erase_size;
if (offset % erase_size || len % erase_size) {
debug("SF: Erase offset/length not multiple of erase size\n");
return -1;
}
cmd[0] = flash->erase_cmd;
while (len) {
#ifdef CONFIG_SPI_FLASH_BAR
u8 bank_sel;
bank_sel = offset / SPI_FLASH_16MB_BOUN;
ret = spi_flash_cmd_bankaddr_write(flash, bank_sel);
if (ret) {
debug("SF: fail to set bank%d\n", bank_sel);
return ret;
}
#endif
spi_flash_addr(offset, cmd);
debug("SF: erase %2x %2x %2x %2x (%x)\n", cmd[0], cmd[1],
cmd[2], cmd[3], offset);
ret = spi_flash_write_common(flash, cmd, sizeof(cmd), NULL, 0);
if (ret < 0) {
debug("SF: erase failed\n");
break;
}
offset += erase_size;
len -= erase_size;
}
return ret;
}
int spi_flash_cmd_write_ops(struct spi_flash *flash, u32 offset,
size_t len, const void *buf)
{
unsigned long byte_addr, page_size;
size_t chunk_len, actual;
u8 cmd[4];
int ret = -1;
page_size = flash->page_size;
cmd[0] = CMD_PAGE_PROGRAM;
for (actual = 0; actual < len; actual += chunk_len) {
#ifdef CONFIG_SPI_FLASH_BAR
u8 bank_sel;
bank_sel = offset / SPI_FLASH_16MB_BOUN;
ret = spi_flash_cmd_bankaddr_write(flash, bank_sel);
if (ret) {
debug("SF: fail to set bank%d\n", bank_sel);
return ret;
}
#endif
byte_addr = offset % page_size;
chunk_len = min(len - actual, page_size - byte_addr);
if (flash->spi->max_write_size)
chunk_len = min(chunk_len, flash->spi->max_write_size);
spi_flash_addr(offset, cmd);
debug("PP: 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),
buf + actual, chunk_len);
if (ret < 0) {
debug("SF: write failed\n");
break;
}
offset += chunk_len;
}
return ret;
}
int spi_flash_read_common(struct spi_flash *flash, const u8 *cmd,
size_t cmd_len, void *data, size_t data_len)
{
struct spi_slave *spi = flash->spi;
int ret;
ret = spi_claim_bus(flash->spi);
if (ret) {
debug("SF: unable to claim SPI bus\n");
return ret;
}
ret = spi_flash_cmd_read(spi, cmd, cmd_len, data, data_len);
if (ret < 0) {
debug("SF: read cmd failed\n");
return ret;
}
spi_release_bus(spi);
return ret;
}
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;
int ret = -1;
/* Handle memory-mapped SPI */
if (flash->memory_map) {
spi_xfer(flash->spi, 0, NULL, NULL, SPI_XFER_MMAP);
memcpy(data, flash->memory_map + offset, len);
spi_xfer(flash->spi, 0, NULL, NULL, SPI_XFER_MMAP_END);
return 0;
}
cmd[0] = CMD_READ_ARRAY_FAST;
cmd[4] = 0x00;
while (len) {
#ifdef CONFIG_SPI_FLASH_BAR
bank_sel = offset / SPI_FLASH_16MB_BOUN;
ret = spi_flash_cmd_bankaddr_write(flash, bank_sel);
if (ret) {
debug("SF: fail to set bank%d\n", bank_sel);
return ret;
}
#endif
remain_len = (SPI_FLASH_16MB_BOUN * (bank_sel + 1) - offset);
if (len < remain_len)
read_len = len;
else
read_len = remain_len;
spi_flash_addr(offset, cmd);
ret = spi_flash_read_common(flash, cmd, sizeof(cmd),
data, read_len);
if (ret < 0) {
debug("SF: read failed\n");
break;
}
offset += read_len;
len -= read_len;
data += read_len;
}
return ret;
}
#ifdef CONFIG_SPI_FLASH_SST
static int sst_byte_write(struct spi_flash *flash, u32 offset, const void *buf)
{
int ret;
u8 cmd[4] = {
CMD_SST_BP,
offset >> 16,
offset >> 8,
offset,
};
debug("BP[%02x]: 0x%p => cmd = { 0x%02x 0x%06x }\n",
spi_w8r8(flash->spi, CMD_READ_STATUS), buf, cmd[0], offset);
ret = spi_flash_cmd_write_enable(flash);
if (ret)
return ret;
ret = spi_flash_cmd_write(flash->spi, cmd, sizeof(cmd), buf, 1);
if (ret)
return ret;
return spi_flash_cmd_wait_ready(flash, SPI_FLASH_PROG_TIMEOUT);
}
int sst_write_wp(struct spi_flash *flash, u32 offset, size_t len,
const void *buf)
{
size_t actual, cmd_len;
int ret;
u8 cmd[4];
ret = spi_claim_bus(flash->spi);
if (ret) {
debug("SF: Unable to claim SPI bus\n");
return ret;
}
/* If the data is not word aligned, write out leading single byte */
actual = offset % 2;
if (actual) {
ret = sst_byte_write(flash, offset, buf);
if (ret)
goto done;
}
offset += actual;
ret = spi_flash_cmd_write_enable(flash);
if (ret)
goto done;
cmd_len = 4;
cmd[0] = CMD_SST_AAI_WP;
cmd[1] = offset >> 16;
cmd[2] = offset >> 8;
cmd[3] = offset;
for (; actual < len - 1; actual += 2) {
debug("WP[%02x]: 0x%p => cmd = { 0x%02x 0x%06x }\n",
spi_w8r8(flash->spi, CMD_READ_STATUS), buf + actual,
cmd[0], offset);
ret = spi_flash_cmd_write(flash->spi, cmd, cmd_len,
buf + actual, 2);
if (ret) {
debug("SF: sst word program failed\n");
break;
}
ret = spi_flash_cmd_wait_ready(flash, SPI_FLASH_PROG_TIMEOUT);
if (ret)
break;
cmd_len = 1;
offset += 2;
}
if (!ret)
ret = spi_flash_cmd_write_disable(flash);
/* If there is a single trailing byte, write it out */
if (!ret && actual != len)
ret = sst_byte_write(flash, offset, buf + actual);
done:
debug("SF: sst: program %s %zu bytes @ 0x%zx\n",
ret ? "failure" : "success", len, offset - actual);
spi_release_bus(flash->spi);
return ret;
}
#endif

363
drivers/mtd/spi/sf_probe.c Normal file
View File

@ -0,0 +1,363 @@
/*
* SPI flash probing
*
* Copyright (C) 2008 Atmel Corporation
* Copyright (C) 2010 Reinhard Meyer, EMK Elektronik
* Copyright (C) 2013 Jagannadha Sutradharudu Teki, Xilinx Inc.
*
* Licensed under the GPL-2 or later.
*/
#include <common.h>
#include <fdtdec.h>
#include <malloc.h>
#include <spi.h>
#include <spi_flash.h>
#include "sf_internal.h"
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;
};
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},
#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 */
{"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},
#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},
#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 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)
*/
};
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;
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];
if ((params->jedec >> 16) == idcode[0]) {
if ((params->jedec & 0xFFFF) == jedec) {
if (params->ext_jedec == 0)
break;
else if (params->ext_jedec == ext_jedec)
break;
}
}
}
if (i == ARRAY_SIZE(spi_flash_params_table)) {
printf("SF: Unsupported flash IDs: ");
printf("manuf %02x, jedec %04x, ext_jedec %04x\n",
idcode[0], jedec, ext_jedec);
return NULL;
}
flash = malloc(sizeof(*flash));
if (!flash) {
debug("SF: Failed to allocate spi_flash\n");
return NULL;
}
memset(flash, '\0', sizeof(*flash));
flash->spi = spi;
flash->name = params->name;
flash->memory_map = spi->memory_map;
/* Assign spi_flash ops */
flash->write = spi_flash_cmd_write_ops;
#ifdef CONFIG_SPI_FLASH_SST
if (params->flags & SST_WP)
flash->write = sst_write_wp;
#endif
flash->erase = spi_flash_cmd_erase_ops;
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;
/* Compute erase sector and command */
if (params->flags & SECT_4K) {
flash->erase_cmd = CMD_ERASE_4K;
flash->erase_size = 4096;
} else if (params->flags & SECT_32K) {
flash->erase_cmd = CMD_ERASE_32K;
flash->erase_size = 32768;
} else {
flash->erase_cmd = CMD_ERASE_64K;
flash->erase_size = flash->sector_size;
}
/* Poll cmd seclection */
flash->poll_cmd = CMD_READ_STATUS;
#ifdef CONFIG_SPI_FLASH_STMICRO
if (params->flags & E_FSR)
flash->poll_cmd = CMD_FLAG_STATUS;
#endif
/* Configure the BAR - discover bank cmds and read current bank */
#ifdef CONFIG_SPI_FLASH_BAR
u8 curr_bank = 0;
if (flash->size > SPI_FLASH_16MB_BOUN) {
flash->bank_read_cmd = (idcode[0] == 0x01) ?
CMD_BANKADDR_BRRD : CMD_EXTNADDR_RDEAR;
flash->bank_write_cmd = (idcode[0] == 0x01) ?
CMD_BANKADDR_BRWR : CMD_EXTNADDR_WREAR;
if (spi_flash_read_common(flash, &flash->bank_read_cmd, 1,
&curr_bank, 1)) {
debug("SF: fail to read bank addr register\n");
return NULL;
}
flash->bank_curr = curr_bank;
} else {
flash->bank_curr = curr_bank;
}
#endif
/* Flash powers up read-only, so clear BP# bits */
#if defined(CONFIG_SPI_FLASH_ATMEL) || \
defined(CONFIG_SPI_FLASH_MACRONIX) || \
defined(CONFIG_SPI_FLASH_SST)
spi_flash_cmd_write_status(flash, 0);
#endif
return flash;
}
#ifdef CONFIG_OF_CONTROL
int spi_flash_decode_fdt(const void *blob, struct spi_flash *flash)
{
fdt_addr_t addr;
fdt_size_t size;
int node;
/* If there is no node, do nothing */
node = fdtdec_next_compatible(blob, 0, COMPAT_GENERIC_SPI_FLASH);
if (node < 0)
return 0;
addr = fdtdec_get_addr_size(blob, node, "memory-map", &size);
if (addr == FDT_ADDR_T_NONE) {
debug("%s: Cannot decode address\n", __func__);
return 0;
}
if (flash->size != size) {
debug("%s: Memory map must cover entire device\n", __func__);
return -1;
}
flash->memory_map = (void *)addr;
return 0;
}
#endif /* CONFIG_OF_CONTROL */
struct spi_flash *spi_flash_probe(unsigned int bus, unsigned int cs,
unsigned int max_hz, unsigned int spi_mode)
{
struct spi_slave *spi;
struct spi_flash *flash = NULL;
u8 idcode[5];
int ret;
/* Setup spi_slave */
spi = spi_setup_slave(bus, cs, max_hz, spi_mode);
if (!spi) {
printf("SF: Failed to set up slave\n");
return NULL;
}
/* Claim spi bus */
ret = spi_claim_bus(spi);
if (ret) {
debug("SF: Failed to claim SPI bus: %d\n", ret);
goto err_claim_bus;
}
/* Read the ID codes */
ret = spi_flash_cmd(spi, CMD_READ_ID, idcode, sizeof(idcode));
if (ret) {
printf("SF: Failed to get idcodes\n");
goto err_read_id;
}
#ifdef DEBUG
printf("SF: Got idcodes\n");
print_buffer(0, idcode, 1, sizeof(idcode), 0);
#endif
/* Validate params from spi_flash_params table */
flash = spi_flash_validate_params(spi, idcode);
if (!flash)
goto err_read_id;
#ifdef CONFIG_OF_CONTROL
if (spi_flash_decode_fdt(gd->fdt_blob, flash)) {
debug("SF: FDT decode error\n");
goto err_read_id;
}
#endif
#ifndef CONFIG_SPL_BUILD
printf("SF: Detected %s with page size ", flash->name);
print_size(flash->page_size, ", erase size ");
print_size(flash->erase_size, ", total ");
print_size(flash->size, "");
if (flash->memory_map)
printf(", mapped at %p", flash->memory_map);
puts("\n");
#endif
#ifndef CONFIG_SPI_FLASH_BAR
if (flash->size > SPI_FLASH_16MB_BOUN) {
puts("SF: Warning - Only lower 16MiB accessible,");
puts(" Full access #define CONFIG_SPI_FLASH_BAR\n");
}
#endif
/* Release spi bus */
spi_release_bus(spi);
return flash;
err_read_id:
spi_release_bus(spi);
err_claim_bus:
spi_free_slave(spi);
return NULL;
}
void spi_flash_free(struct spi_flash *flash)
{
spi_free_slave(flash->spi);
free(flash);
}

View File

@ -1,141 +0,0 @@
/*
* Copyright (C) 2009 Freescale Semiconductor, Inc.
*
* Author: Mingkai Hu (Mingkai.hu@freescale.com)
* Based on stmicro.c by Wolfgang Denk (wd@denx.de),
* TsiChung Liew (Tsi-Chung.Liew@freescale.com),
* and Jason McMullan (mcmullan@netapp.com)
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <malloc.h>
#include <spi_flash.h>
#include "spi_flash_internal.h"
struct spansion_spi_flash_params {
u16 idcode1;
u16 idcode2;
u16 pages_per_sector;
u16 nr_sectors;
const char *name;
};
static const struct spansion_spi_flash_params spansion_spi_flash_table[] = {
{
.idcode1 = 0x0213,
.idcode2 = 0,
.pages_per_sector = 256,
.nr_sectors = 16,
.name = "S25FL008A",
},
{
.idcode1 = 0x0214,
.idcode2 = 0,
.pages_per_sector = 256,
.nr_sectors = 32,
.name = "S25FL016A",
},
{
.idcode1 = 0x0215,
.idcode2 = 0,
.pages_per_sector = 256,
.nr_sectors = 64,
.name = "S25FL032A",
},
{
.idcode1 = 0x0216,
.idcode2 = 0,
.pages_per_sector = 256,
.nr_sectors = 128,
.name = "S25FL064A",
},
{
.idcode1 = 0x2018,
.idcode2 = 0x0301,
.pages_per_sector = 256,
.nr_sectors = 256,
.name = "S25FL128P_64K",
},
{
.idcode1 = 0x2018,
.idcode2 = 0x0300,
.pages_per_sector = 1024,
.nr_sectors = 64,
.name = "S25FL128P_256K",
},
{
.idcode1 = 0x0215,
.idcode2 = 0x4d00,
.pages_per_sector = 256,
.nr_sectors = 64,
.name = "S25FL032P",
},
{
.idcode1 = 0x0216,
.idcode2 = 0x4d00,
.pages_per_sector = 256,
.nr_sectors = 128,
.name = "S25FL064P",
},
{
.idcode1 = 0x2018,
.idcode2 = 0x4d01,
.pages_per_sector = 256,
.nr_sectors = 256,
.name = "S25FL129P_64K/S25FL128S_64K",
},
{
.idcode1 = 0x0219,
.idcode2 = 0x4d01,
.pages_per_sector = 256,
.nr_sectors = 512,
.name = "S25FL256S_64K",
},
{
.idcode1 = 0x0220,
.idcode2 = 0x4d01,
.pages_per_sector = 256,
.nr_sectors = 1024,
.name = "S25FL512S_64K",
},
};
struct spi_flash *spi_flash_probe_spansion(struct spi_slave *spi, u8 *idcode)
{
const struct spansion_spi_flash_params *params;
struct spi_flash *flash;
unsigned int i;
unsigned short jedec, ext_jedec;
jedec = idcode[1] << 8 | idcode[2];
ext_jedec = idcode[3] << 8 | idcode[4];
for (i = 0; i < ARRAY_SIZE(spansion_spi_flash_table); i++) {
params = &spansion_spi_flash_table[i];
if (params->idcode1 == jedec) {
if (params->idcode2 == ext_jedec)
break;
}
}
if (i == ARRAY_SIZE(spansion_spi_flash_table)) {
debug("SF: Unsupported SPANSION ID %04x %04x\n",
jedec, ext_jedec);
return NULL;
}
flash = spi_flash_alloc_base(spi, params->name);
if (!flash) {
debug("SF: Failed to allocate memory\n");
return NULL;
}
flash->page_size = 256;
flash->sector_size = 256 * params->pages_per_sector;
flash->size = flash->sector_size * params->nr_sectors;
return flash;
}

View File

@ -1,615 +0,0 @@
/*
* SPI flash interface
*
* Copyright (C) 2008 Atmel Corporation
* Copyright (C) 2010 Reinhard Meyer, EMK Elektronik
*
* Licensed under the GPL-2 or later.
*/
#include <common.h>
#include <fdtdec.h>
#include <malloc.h>
#include <spi.h>
#include <spi_flash.h>
#include <watchdog.h>
#include "spi_flash_internal.h"
DECLARE_GLOBAL_DATA_PTR;
static void spi_flash_addr(u32 addr, u8 *cmd)
{
/* cmd[0] is actual command */
cmd[1] = addr >> 16;
cmd[2] = addr >> 8;
cmd[3] = addr >> 0;
}
static int spi_flash_read_write(struct spi_slave *spi,
const u8 *cmd, size_t cmd_len,
const u8 *data_out, u8 *data_in,
size_t data_len)
{
unsigned long flags = SPI_XFER_BEGIN;
int ret;
if (data_len == 0)
flags |= SPI_XFER_END;
ret = spi_xfer(spi, cmd_len * 8, cmd, NULL, flags);
if (ret) {
debug("SF: Failed to send command (%zu bytes): %d\n",
cmd_len, ret);
} else if (data_len != 0) {
ret = spi_xfer(spi, data_len * 8, data_out, data_in,
SPI_XFER_END);
if (ret)
debug("SF: Failed to transfer %zu bytes of data: %d\n",
data_len, ret);
}
return ret;
}
int spi_flash_cmd(struct spi_slave *spi, u8 cmd, void *response, size_t len)
{
return spi_flash_cmd_read(spi, &cmd, 1, response, len);
}
int spi_flash_cmd_read(struct spi_slave *spi, const u8 *cmd,
size_t cmd_len, void *data, size_t data_len)
{
return spi_flash_read_write(spi, cmd, cmd_len, NULL, data, data_len);
}
int spi_flash_cmd_write(struct spi_slave *spi, const u8 *cmd, size_t cmd_len,
const void *data, size_t data_len)
{
return spi_flash_read_write(spi, cmd, cmd_len, data, NULL, data_len);
}
int spi_flash_cmd_wait_ready(struct spi_flash *flash, unsigned long timeout)
{
struct spi_slave *spi = flash->spi;
unsigned long timebase;
int ret;
u8 status;
u8 check_status = 0x0;
u8 poll_bit = STATUS_WIP;
u8 cmd = flash->poll_cmd;
if (cmd == CMD_FLAG_STATUS) {
poll_bit = STATUS_PEC;
check_status = poll_bit;
}
ret = spi_xfer(spi, 8, &cmd, NULL, SPI_XFER_BEGIN);
if (ret) {
debug("SF: fail to read %s status register\n",
cmd == CMD_READ_STATUS ? "read" : "flag");
return ret;
}
timebase = get_timer(0);
do {
WATCHDOG_RESET();
ret = spi_xfer(spi, 8, NULL, &status, 0);
if (ret)
return -1;
if ((status & poll_bit) == check_status)
break;
} while (get_timer(timebase) < timeout);
spi_xfer(spi, 0, NULL, NULL, SPI_XFER_END);
if ((status & poll_bit) == check_status)
return 0;
/* Timed out */
debug("SF: time out!\n");
return -1;
}
int spi_flash_write_common(struct spi_flash *flash, const u8 *cmd,
size_t cmd_len, const void *buf, size_t buf_len)
{
struct spi_slave *spi = flash->spi;
unsigned long timeout = SPI_FLASH_PROG_TIMEOUT;
int ret;
if (buf == NULL)
timeout = SPI_FLASH_PAGE_ERASE_TIMEOUT;
ret = spi_claim_bus(flash->spi);
if (ret) {
debug("SF: unable to claim SPI bus\n");
return ret;
}
ret = spi_flash_cmd_write_enable(flash);
if (ret < 0) {
debug("SF: enabling write failed\n");
return ret;
}
ret = spi_flash_cmd_write(spi, cmd, cmd_len, buf, buf_len);
if (ret < 0) {
debug("SF: write cmd failed\n");
return ret;
}
ret = spi_flash_cmd_wait_ready(flash, timeout);
if (ret < 0) {
debug("SF: write %s timed out\n",
timeout == SPI_FLASH_PROG_TIMEOUT ?
"program" : "page erase");
return ret;
}
spi_release_bus(spi);
return ret;
}
int spi_flash_cmd_erase(struct spi_flash *flash, u32 offset, size_t len)
{
u32 erase_size;
u8 cmd[4];
int ret = -1;
erase_size = flash->sector_size;
if (offset % erase_size || len % erase_size) {
debug("SF: Erase offset/length not multiple of erase size\n");
return -1;
}
if (erase_size == 4096)
cmd[0] = CMD_ERASE_4K;
else
cmd[0] = CMD_ERASE_64K;
while (len) {
#ifdef CONFIG_SPI_FLASH_BAR
u8 bank_sel;
bank_sel = offset / SPI_FLASH_16MB_BOUN;
ret = spi_flash_cmd_bankaddr_write(flash, bank_sel);
if (ret) {
debug("SF: fail to set bank%d\n", bank_sel);
return ret;
}
#endif
spi_flash_addr(offset, cmd);
debug("SF: erase %2x %2x %2x %2x (%x)\n", cmd[0], cmd[1],
cmd[2], cmd[3], offset);
ret = spi_flash_write_common(flash, cmd, sizeof(cmd), NULL, 0);
if (ret < 0) {
debug("SF: erase failed\n");
break;
}
offset += erase_size;
len -= erase_size;
}
return ret;
}
int spi_flash_cmd_write_multi(struct spi_flash *flash, u32 offset,
size_t len, const void *buf)
{
unsigned long byte_addr, page_size;
size_t chunk_len, actual;
u8 cmd[4];
int ret = -1;
page_size = flash->page_size;
cmd[0] = CMD_PAGE_PROGRAM;
for (actual = 0; actual < len; actual += chunk_len) {
#ifdef CONFIG_SPI_FLASH_BAR
u8 bank_sel;
bank_sel = offset / SPI_FLASH_16MB_BOUN;
ret = spi_flash_cmd_bankaddr_write(flash, bank_sel);
if (ret) {
debug("SF: fail to set bank%d\n", bank_sel);
return ret;
}
#endif
byte_addr = offset % page_size;
chunk_len = min(len - actual, page_size - byte_addr);
if (flash->spi->max_write_size)
chunk_len = min(chunk_len, flash->spi->max_write_size);
spi_flash_addr(offset, cmd);
debug("PP: 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),
buf + actual, chunk_len);
if (ret < 0) {
debug("SF: write failed\n");
break;
}
offset += chunk_len;
}
return ret;
}
int spi_flash_read_common(struct spi_flash *flash, const u8 *cmd,
size_t cmd_len, void *data, size_t data_len)
{
struct spi_slave *spi = flash->spi;
int ret;
ret = spi_claim_bus(flash->spi);
if (ret) {
debug("SF: unable to claim SPI bus\n");
return ret;
}
ret = spi_flash_cmd_read(spi, cmd, cmd_len, data, data_len);
if (ret < 0) {
debug("SF: read cmd failed\n");
return ret;
}
spi_release_bus(spi);
return ret;
}
int spi_flash_cmd_read_fast(struct spi_flash *flash, u32 offset,
size_t len, void *data)
{
u8 cmd[5], bank_sel = 0;
u32 remain_len, read_len;
int ret = -1;
/* Handle memory-mapped SPI */
if (flash->memory_map) {
memcpy(data, flash->memory_map + offset, len);
return 0;
}
cmd[0] = CMD_READ_ARRAY_FAST;
cmd[4] = 0x00;
while (len) {
#ifdef CONFIG_SPI_FLASH_BAR
bank_sel = offset / SPI_FLASH_16MB_BOUN;
ret = spi_flash_cmd_bankaddr_write(flash, bank_sel);
if (ret) {
debug("SF: fail to set bank%d\n", bank_sel);
return ret;
}
#endif
remain_len = (SPI_FLASH_16MB_BOUN * (bank_sel + 1) - offset);
if (len < remain_len)
read_len = len;
else
read_len = remain_len;
spi_flash_addr(offset, cmd);
ret = spi_flash_read_common(flash, cmd, sizeof(cmd),
data, read_len);
if (ret < 0) {
debug("SF: read failed\n");
break;
}
offset += read_len;
len -= read_len;
data += read_len;
}
return ret;
}
int spi_flash_cmd_write_status(struct spi_flash *flash, u8 sr)
{
u8 cmd;
int ret;
cmd = CMD_WRITE_STATUS;
ret = spi_flash_write_common(flash, &cmd, 1, &sr, 1);
if (ret < 0) {
debug("SF: fail to write status register\n");
return ret;
}
return 0;
}
#ifdef CONFIG_SPI_FLASH_BAR
int spi_flash_cmd_bankaddr_write(struct spi_flash *flash, u8 bank_sel)
{
u8 cmd;
int ret;
if (flash->bank_curr == bank_sel) {
debug("SF: not require to enable bank%d\n", bank_sel);
return 0;
}
cmd = flash->bank_write_cmd;
ret = spi_flash_write_common(flash, &cmd, 1, &bank_sel, 1);
if (ret < 0) {
debug("SF: fail to write bank register\n");
return ret;
}
flash->bank_curr = bank_sel;
return 0;
}
int spi_flash_bank_config(struct spi_flash *flash, u8 idcode0)
{
u8 cmd;
u8 curr_bank = 0;
/* discover bank cmds */
switch (idcode0) {
case SPI_FLASH_SPANSION_IDCODE0:
flash->bank_read_cmd = CMD_BANKADDR_BRRD;
flash->bank_write_cmd = CMD_BANKADDR_BRWR;
break;
case SPI_FLASH_STMICRO_IDCODE0:
case SPI_FLASH_WINBOND_IDCODE0:
flash->bank_read_cmd = CMD_EXTNADDR_RDEAR;
flash->bank_write_cmd = CMD_EXTNADDR_WREAR;
break;
default:
printf("SF: Unsupported bank commands %02x\n", idcode0);
return -1;
}
/* read the bank reg - on which bank the flash is in currently */
cmd = flash->bank_read_cmd;
if (flash->size > SPI_FLASH_16MB_BOUN) {
if (spi_flash_read_common(flash, &cmd, 1, &curr_bank, 1)) {
debug("SF: fail to read bank addr register\n");
return -1;
}
flash->bank_curr = curr_bank;
} else {
flash->bank_curr = curr_bank;
}
return 0;
}
#endif
#ifdef CONFIG_OF_CONTROL
int spi_flash_decode_fdt(const void *blob, struct spi_flash *flash)
{
fdt_addr_t addr;
fdt_size_t size;
int node;
/* If there is no node, do nothing */
node = fdtdec_next_compatible(blob, 0, COMPAT_GENERIC_SPI_FLASH);
if (node < 0)
return 0;
addr = fdtdec_get_addr_size(blob, node, "memory-map", &size);
if (addr == FDT_ADDR_T_NONE) {
debug("%s: Cannot decode address\n", __func__);
return 0;
}
if (flash->size != size) {
debug("%s: Memory map must cover entire device\n", __func__);
return -1;
}
flash->memory_map = (void *)addr;
return 0;
}
#endif /* CONFIG_OF_CONTROL */
/*
* The following table holds all device probe functions
*
* shift: number of continuation bytes before the ID
* idcode: the expected IDCODE or 0xff for non JEDEC devices
* probe: the function to call
*
* Non JEDEC devices should be ordered in the table such that
* the probe functions with best detection algorithms come first.
*
* Several matching entries are permitted, they will be tried
* in sequence until a probe function returns non NULL.
*
* IDCODE_CONT_LEN may be redefined if a device needs to declare a
* larger "shift" value. IDCODE_PART_LEN generally shouldn't be
* changed. This is the max number of bytes probe functions may
* examine when looking up part-specific identification info.
*
* Probe functions will be given the idcode buffer starting at their
* manu id byte (the "idcode" in the table below). In other words,
* all of the continuation bytes will be skipped (the "shift" below).
*/
#define IDCODE_CONT_LEN 0
#define IDCODE_PART_LEN 5
static const struct {
const u8 shift;
const u8 idcode;
struct spi_flash *(*probe) (struct spi_slave *spi, u8 *idcode);
} flashes[] = {
/* Keep it sorted by define name */
#ifdef CONFIG_SPI_FLASH_ATMEL
{ 0, 0x1f, spi_flash_probe_atmel, },
#endif
#ifdef CONFIG_SPI_FLASH_EON
{ 0, 0x1c, spi_flash_probe_eon, },
#endif
#ifdef CONFIG_SPI_FLASH_GIGADEVICE
{ 0, 0xc8, spi_flash_probe_gigadevice, },
#endif
#ifdef CONFIG_SPI_FLASH_MACRONIX
{ 0, 0xc2, spi_flash_probe_macronix, },
#endif
#ifdef CONFIG_SPI_FLASH_SPANSION
{ 0, 0x01, spi_flash_probe_spansion, },
#endif
#ifdef CONFIG_SPI_FLASH_SST
{ 0, 0xbf, spi_flash_probe_sst, },
#endif
#ifdef CONFIG_SPI_FLASH_STMICRO
{ 0, 0x20, spi_flash_probe_stmicro, },
#endif
#ifdef CONFIG_SPI_FLASH_WINBOND
{ 0, 0xef, spi_flash_probe_winbond, },
#endif
#ifdef CONFIG_SPI_FRAM_RAMTRON
{ 6, 0xc2, spi_fram_probe_ramtron, },
# undef IDCODE_CONT_LEN
# define IDCODE_CONT_LEN 6
#endif
/* Keep it sorted by best detection */
#ifdef CONFIG_SPI_FLASH_STMICRO
{ 0, 0xff, spi_flash_probe_stmicro, },
#endif
#ifdef CONFIG_SPI_FRAM_RAMTRON_NON_JEDEC
{ 0, 0xff, spi_fram_probe_ramtron, },
#endif
};
#define IDCODE_LEN (IDCODE_CONT_LEN + IDCODE_PART_LEN)
struct spi_flash *spi_flash_probe(unsigned int bus, unsigned int cs,
unsigned int max_hz, unsigned int spi_mode)
{
struct spi_slave *spi;
struct spi_flash *flash = NULL;
int ret, i, shift;
u8 idcode[IDCODE_LEN], *idp;
spi = spi_setup_slave(bus, cs, max_hz, spi_mode);
if (!spi) {
printf("SF: Failed to set up slave\n");
return NULL;
}
ret = spi_claim_bus(spi);
if (ret) {
debug("SF: Failed to claim SPI bus: %d\n", ret);
goto err_claim_bus;
}
/* Read the ID codes */
ret = spi_flash_cmd(spi, CMD_READ_ID, idcode, sizeof(idcode));
if (ret)
goto err_read_id;
#ifdef DEBUG
printf("SF: Got idcodes\n");
print_buffer(0, idcode, 1, sizeof(idcode), 0);
#endif
/* count the number of continuation bytes */
for (shift = 0, idp = idcode;
shift < IDCODE_CONT_LEN && *idp == 0x7f;
++shift, ++idp)
continue;
/* search the table for matches in shift and id */
for (i = 0; i < ARRAY_SIZE(flashes); ++i)
if (flashes[i].shift == shift && flashes[i].idcode == *idp) {
/* we have a match, call probe */
flash = flashes[i].probe(spi, idp);
if (flash)
break;
}
if (!flash) {
printf("SF: Unsupported manufacturer %02x\n", *idp);
goto err_manufacturer_probe;
}
#ifdef CONFIG_SPI_FLASH_BAR
/* Configure the BAR - disover bank cmds and read current bank */
ret = spi_flash_bank_config(flash, *idp);
if (ret < 0)
goto err_manufacturer_probe;
#endif
#ifdef CONFIG_OF_CONTROL
if (spi_flash_decode_fdt(gd->fdt_blob, flash)) {
debug("SF: FDT decode error\n");
goto err_manufacturer_probe;
}
#endif
#ifndef CONFIG_SPL_BUILD
printf("SF: Detected %s with page size ", flash->name);
print_size(flash->sector_size, ", total ");
print_size(flash->size, "");
if (flash->memory_map)
printf(", mapped at %p", flash->memory_map);
puts("\n");
#endif
#ifndef CONFIG_SPI_FLASH_BAR
if (flash->size > SPI_FLASH_16MB_BOUN) {
puts("SF: Warning - Only lower 16MiB accessible,");
puts(" Full access #define CONFIG_SPI_FLASH_BAR\n");
}
#endif
spi_release_bus(spi);
return flash;
err_manufacturer_probe:
err_read_id:
spi_release_bus(spi);
err_claim_bus:
spi_free_slave(spi);
return NULL;
}
void *spi_flash_do_alloc(int offset, int size, struct spi_slave *spi,
const char *name)
{
struct spi_flash *flash;
void *ptr;
ptr = malloc(size);
if (!ptr) {
debug("SF: Failed to allocate memory\n");
return NULL;
}
memset(ptr, '\0', size);
flash = (struct spi_flash *)(ptr + offset);
/* Set up some basic fields - caller will sort out sizes */
flash->spi = spi;
flash->name = name;
flash->poll_cmd = CMD_READ_STATUS;
flash->read = spi_flash_cmd_read_fast;
flash->write = spi_flash_cmd_write_multi;
flash->erase = spi_flash_cmd_erase;
return flash;
}
void spi_flash_free(struct spi_flash *flash)
{
spi_free_slave(flash->spi);
free(flash);
}

View File

@ -1,238 +0,0 @@
/*
* Driver for SST serial flashes
*
* (C) Copyright 2000-2002
* Wolfgang Denk, DENX Software Engineering, wd@denx.de.
* Copyright 2008, Network Appliance Inc.
* Jason McMullan <mcmullan@netapp.com>
* Copyright (C) 2004-2007 Freescale Semiconductor, Inc.
* TsiChung Liew (Tsi-Chung.Liew@freescale.com)
* Copyright (c) 2008-2009 Analog Devices Inc.
*
* Licensed under the GPL-2 or later.
*/
#include <common.h>
#include <malloc.h>
#include <spi_flash.h>
#include "spi_flash_internal.h"
#define CMD_SST_BP 0x02 /* Byte Program */
#define CMD_SST_AAI_WP 0xAD /* Auto Address Incr Word Program */
#define SST_SR_WIP (1 << 0) /* Write-in-Progress */
#define SST_SR_WEL (1 << 1) /* Write enable */
#define SST_SR_BP0 (1 << 2) /* Block Protection 0 */
#define SST_SR_BP1 (1 << 3) /* Block Protection 1 */
#define SST_SR_BP2 (1 << 4) /* Block Protection 2 */
#define SST_SR_AAI (1 << 6) /* Addressing mode */
#define SST_SR_BPL (1 << 7) /* BP bits lock */
#define SST_FEAT_WP (1 << 0) /* Supports AAI word program */
#define SST_FEAT_MBP (1 << 1) /* Supports multibyte program */
struct sst_spi_flash_params {
u8 idcode1;
u8 flags;
u16 nr_sectors;
const char *name;
};
struct sst_spi_flash {
struct spi_flash flash;
const struct sst_spi_flash_params *params;
};
static const struct sst_spi_flash_params sst_spi_flash_table[] = {
{
.idcode1 = 0x8d,
.flags = SST_FEAT_WP,
.nr_sectors = 128,
.name = "SST25VF040B",
},
{
.idcode1 = 0x8e,
.flags = SST_FEAT_WP,
.nr_sectors = 256,
.name = "SST25VF080B",
},
{
.idcode1 = 0x41,
.flags = SST_FEAT_WP,
.nr_sectors = 512,
.name = "SST25VF016B",
},
{
.idcode1 = 0x4a,
.flags = SST_FEAT_WP,
.nr_sectors = 1024,
.name = "SST25VF032B",
},
{
.idcode1 = 0x4b,
.flags = SST_FEAT_MBP,
.nr_sectors = 2048,
.name = "SST25VF064C",
},
{
.idcode1 = 0x01,
.flags = SST_FEAT_WP,
.nr_sectors = 16,
.name = "SST25WF512",
},
{
.idcode1 = 0x02,
.flags = SST_FEAT_WP,
.nr_sectors = 32,
.name = "SST25WF010",
},
{
.idcode1 = 0x03,
.flags = SST_FEAT_WP,
.nr_sectors = 64,
.name = "SST25WF020",
},
{
.idcode1 = 0x04,
.flags = SST_FEAT_WP,
.nr_sectors = 128,
.name = "SST25WF040",
},
{
.idcode1 = 0x05,
.flags = SST_FEAT_WP,
.nr_sectors = 256,
.name = "SST25WF080",
},
};
static int
sst_byte_write(struct spi_flash *flash, u32 offset, const void *buf)
{
int ret;
u8 cmd[4] = {
CMD_SST_BP,
offset >> 16,
offset >> 8,
offset,
};
debug("BP[%02x]: 0x%p => cmd = { 0x%02x 0x%06x }\n",
spi_w8r8(flash->spi, CMD_READ_STATUS), buf, cmd[0], offset);
ret = spi_flash_cmd_write_enable(flash);
if (ret)
return ret;
ret = spi_flash_cmd_write(flash->spi, cmd, sizeof(cmd), buf, 1);
if (ret)
return ret;
return spi_flash_cmd_wait_ready(flash, SPI_FLASH_PROG_TIMEOUT);
}
static int
sst_write_wp(struct spi_flash *flash, u32 offset, size_t len, const void *buf)
{
size_t actual, cmd_len;
int ret;
u8 cmd[4];
ret = spi_claim_bus(flash->spi);
if (ret) {
debug("SF: Unable to claim SPI bus\n");
return ret;
}
/* If the data is not word aligned, write out leading single byte */
actual = offset % 2;
if (actual) {
ret = sst_byte_write(flash, offset, buf);
if (ret)
goto done;
}
offset += actual;
ret = spi_flash_cmd_write_enable(flash);
if (ret)
goto done;
cmd_len = 4;
cmd[0] = CMD_SST_AAI_WP;
cmd[1] = offset >> 16;
cmd[2] = offset >> 8;
cmd[3] = offset;
for (; actual < len - 1; actual += 2) {
debug("WP[%02x]: 0x%p => cmd = { 0x%02x 0x%06x }\n",
spi_w8r8(flash->spi, CMD_READ_STATUS), buf + actual,
cmd[0], offset);
ret = spi_flash_cmd_write(flash->spi, cmd, cmd_len,
buf + actual, 2);
if (ret) {
debug("SF: sst word program failed\n");
break;
}
ret = spi_flash_cmd_wait_ready(flash, SPI_FLASH_PROG_TIMEOUT);
if (ret)
break;
cmd_len = 1;
offset += 2;
}
if (!ret)
ret = spi_flash_cmd_write_disable(flash);
/* If there is a single trailing byte, write it out */
if (!ret && actual != len)
ret = sst_byte_write(flash, offset, buf + actual);
done:
debug("SF: sst: program %s %zu bytes @ 0x%zx\n",
ret ? "failure" : "success", len, offset - actual);
spi_release_bus(flash->spi);
return ret;
}
struct spi_flash *
spi_flash_probe_sst(struct spi_slave *spi, u8 *idcode)
{
const struct sst_spi_flash_params *params;
struct sst_spi_flash *stm;
size_t i;
for (i = 0; i < ARRAY_SIZE(sst_spi_flash_table); ++i) {
params = &sst_spi_flash_table[i];
if (params->idcode1 == idcode[2])
break;
}
if (i == ARRAY_SIZE(sst_spi_flash_table)) {
debug("SF: Unsupported SST ID %02x\n", idcode[1]);
return NULL;
}
stm = spi_flash_alloc(struct sst_spi_flash, spi, params->name);
if (!stm) {
debug("SF: Failed to allocate memory\n");
return NULL;
}
stm->params = params;
if (stm->params->flags & SST_FEAT_WP)
stm->flash.write = sst_write_wp;
stm->flash.page_size = 256;
stm->flash.sector_size = 4096;
stm->flash.size = stm->flash.sector_size * params->nr_sectors;
/* Flash powers up read-only, so clear BP# bits */
spi_flash_cmd_write_status(&stm->flash, 0);
return &stm->flash;
}

View File

@ -1,202 +0,0 @@
/*
* (C) Copyright 2000-2002
* Wolfgang Denk, DENX Software Engineering, wd@denx.de.
*
* Copyright 2008, Network Appliance Inc.
* Jason McMullan <mcmullan@netapp.com>
*
* Copyright (C) 2004-2007 Freescale Semiconductor, Inc.
* TsiChung Liew (Tsi-Chung.Liew@freescale.com)
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <malloc.h>
#include <spi_flash.h>
#include "spi_flash_internal.h"
/* M25Pxx-specific commands */
#define CMD_M25PXX_RES 0xab /* Release from DP, and Read Signature */
struct stmicro_spi_flash_params {
u16 id;
u16 pages_per_sector;
u16 nr_sectors;
const char *name;
};
static const struct stmicro_spi_flash_params stmicro_spi_flash_table[] = {
{
.id = 0x2011,
.pages_per_sector = 128,
.nr_sectors = 4,
.name = "M25P10",
},
{
.id = 0x2015,
.pages_per_sector = 256,
.nr_sectors = 32,
.name = "M25P16",
},
{
.id = 0x2012,
.pages_per_sector = 256,
.nr_sectors = 4,
.name = "M25P20",
},
{
.id = 0x2016,
.pages_per_sector = 256,
.nr_sectors = 64,
.name = "M25P32",
},
{
.id = 0x2013,
.pages_per_sector = 256,
.nr_sectors = 8,
.name = "M25P40",
},
{
.id = 0x2017,
.pages_per_sector = 256,
.nr_sectors = 128,
.name = "M25P64",
},
{
.id = 0x2014,
.pages_per_sector = 256,
.nr_sectors = 16,
.name = "M25P80",
},
{
.id = 0x2018,
.pages_per_sector = 1024,
.nr_sectors = 64,
.name = "M25P128",
},
{
.id = 0xba16,
.pages_per_sector = 256,
.nr_sectors = 64,
.name = "N25Q32",
},
{
.id = 0xbb16,
.pages_per_sector = 256,
.nr_sectors = 64,
.name = "N25Q32A",
},
{
.id = 0xba17,
.pages_per_sector = 256,
.nr_sectors = 128,
.name = "N25Q064",
},
{
.id = 0xbb17,
.pages_per_sector = 256,
.nr_sectors = 128,
.name = "N25Q64A",
},
{
.id = 0xba18,
.pages_per_sector = 256,
.nr_sectors = 256,
.name = "N25Q128",
},
{
.id = 0xbb18,
.pages_per_sector = 256,
.nr_sectors = 256,
.name = "N25Q128A",
},
{
.id = 0xba19,
.pages_per_sector = 256,
.nr_sectors = 512,
.name = "N25Q256",
},
{
.id = 0xbb19,
.pages_per_sector = 256,
.nr_sectors = 512,
.name = "N25Q256A",
},
{
.id = 0xba20,
.pages_per_sector = 256,
.nr_sectors = 1024,
.name = "N25Q512",
},
{
.id = 0xbb20,
.pages_per_sector = 256,
.nr_sectors = 1024,
.name = "N25Q512A",
},
{
.id = 0xba21,
.pages_per_sector = 256,
.nr_sectors = 2048,
.name = "N25Q1024",
},
{
.id = 0xbb21,
.pages_per_sector = 256,
.nr_sectors = 2048,
.name = "N25Q1024A",
},
};
struct spi_flash *spi_flash_probe_stmicro(struct spi_slave *spi, u8 *idcode)
{
const struct stmicro_spi_flash_params *params;
struct spi_flash *flash;
unsigned int i;
u16 id;
if (idcode[0] == 0xff) {
i = spi_flash_cmd(spi, CMD_M25PXX_RES,
idcode, 4);
if (i)
return NULL;
if ((idcode[3] & 0xf0) == 0x10) {
idcode[0] = 0x20;
idcode[1] = 0x20;
idcode[2] = idcode[3] + 1;
} else {
return NULL;
}
}
id = ((idcode[1] << 8) | idcode[2]);
for (i = 0; i < ARRAY_SIZE(stmicro_spi_flash_table); i++) {
params = &stmicro_spi_flash_table[i];
if (params->id == id)
break;
}
if (i == ARRAY_SIZE(stmicro_spi_flash_table)) {
debug("SF: Unsupported STMicro ID %04x\n", id);
return NULL;
}
flash = spi_flash_alloc_base(spi, params->name);
if (!flash) {
debug("SF: Failed to allocate memory\n");
return NULL;
}
flash->page_size = 256;
flash->sector_size = 256 * params->pages_per_sector;
flash->size = flash->sector_size * params->nr_sectors;
/* for >= 512MiB flashes, use flag status instead of read_status */
if (flash->size >= 0x4000000)
flash->poll_cmd = CMD_FLAG_STATUS;
return flash;
}

View File

@ -1,141 +0,0 @@
/*
* Copyright 2008, Network Appliance Inc.
* Author: Jason McMullan <mcmullan <at> netapp.com>
* Licensed under the GPL-2 or later.
*/
#include <common.h>
#include <malloc.h>
#include <spi_flash.h>
#include "spi_flash_internal.h"
struct winbond_spi_flash_params {
uint16_t id;
uint16_t nr_blocks;
const char *name;
};
static const struct winbond_spi_flash_params winbond_spi_flash_table[] = {
{
.id = 0x2014,
.nr_blocks = 16,
.name = "W25P80",
},
{
.id = 0x2015,
.nr_blocks = 32,
.name = "W25P16",
},
{
.id = 0x2016,
.nr_blocks = 64,
.name = "W25P32",
},
{
.id = 0x3013,
.nr_blocks = 8,
.name = "W25X40",
},
{
.id = 0x3015,
.nr_blocks = 32,
.name = "W25X16",
},
{
.id = 0x3016,
.nr_blocks = 64,
.name = "W25X32",
},
{
.id = 0x3017,
.nr_blocks = 128,
.name = "W25X64",
},
{
.id = 0x4014,
.nr_blocks = 16,
.name = "W25Q80BL/W25Q80BV",
},
{
.id = 0x4015,
.nr_blocks = 32,
.name = "W25Q16CL/W25Q16DV",
},
{
.id = 0x4016,
.nr_blocks = 64,
.name = "W25Q32BV/W25Q32FV_SPI",
},
{
.id = 0x4017,
.nr_blocks = 128,
.name = "W25Q64CV/W25Q64FV_SPI",
},
{
.id = 0x4018,
.nr_blocks = 256,
.name = "W25Q128BV/W25Q128FV_SPI",
},
{
.id = 0x4019,
.nr_blocks = 512,
.name = "W25Q256",
},
{
.id = 0x5014,
.nr_blocks = 16,
.name = "W25Q80BW",
},
{
.id = 0x6015,
.nr_blocks = 32,
.name = "W25Q16DW",
},
{
.id = 0x6016,
.nr_blocks = 64,
.name = "W25Q32DW/W25Q32FV_QPI",
},
{
.id = 0x6017,
.nr_blocks = 128,
.name = "W25Q64DW/W25Q64FV_QPI",
},
{
.id = 0x6018,
.nr_blocks = 256,
.name = "W25Q128FW/W25Q128FV_QPI",
},
};
struct spi_flash *spi_flash_probe_winbond(struct spi_slave *spi, u8 *idcode)
{
const struct winbond_spi_flash_params *params;
struct spi_flash *flash;
unsigned int i;
for (i = 0; i < ARRAY_SIZE(winbond_spi_flash_table); i++) {
params = &winbond_spi_flash_table[i];
if (params->id == ((idcode[1] << 8) | idcode[2]))
break;
}
if (i == ARRAY_SIZE(winbond_spi_flash_table)) {
debug("SF: Unsupported Winbond ID %02x%02x\n",
idcode[1], idcode[2]);
return NULL;
}
flash = spi_flash_alloc_base(spi, params->name);
if (!flash) {
debug("SF: Failed to allocate memory\n");
return NULL;
}
flash->page_size = 256;
flash->sector_size = (idcode[1] == 0x20) ? 65536 : 4096;
flash->size = 4096 * 16 * params->nr_blocks;
return flash;
}

View File

@ -38,6 +38,7 @@ COBJS-$(CONFIG_FDT_SPI) += fdt_spi.o
COBJS-$(CONFIG_TEGRA20_SFLASH) += tegra20_sflash.o
COBJS-$(CONFIG_TEGRA20_SLINK) += tegra20_slink.o
COBJS-$(CONFIG_TEGRA114_SPI) += tegra114_spi.o
COBJS-$(CONFIG_TI_QSPI) += ti_qspi.o
COBJS-$(CONFIG_XILINX_SPI) += xilinx_spi.o
COBJS-$(CONFIG_ZYNQ_SPI) += zynq_spi.o

View File

@ -26,6 +26,7 @@ struct spi_bus {
struct exynos_spi *regs;
int inited; /* 1 if this bus is ready for use */
int node;
uint deactivate_delay_us; /* Delay to wait after deactivate */
};
/* A list of spi buses that we know about */
@ -40,6 +41,8 @@ struct exynos_spi_slave {
enum periph_id periph_id; /* Peripheral ID for this device */
unsigned int fifo_size;
int skip_preamble;
struct spi_bus *bus; /* Pointer to our SPI bus info */
ulong last_transaction_us; /* Time of last transaction end */
};
static struct spi_bus *spi_get_bus(unsigned dev_index)
@ -85,6 +88,7 @@ struct spi_slave *spi_setup_slave(unsigned int busnum, unsigned int cs,
}
bus = &spi_bus[busnum];
spi_slave->bus = bus;
spi_slave->regs = bus->regs;
spi_slave->mode = mode;
spi_slave->periph_id = bus->periph_id;
@ -95,6 +99,7 @@ struct spi_slave *spi_setup_slave(unsigned int busnum, unsigned int cs,
spi_slave->fifo_size = 256;
spi_slave->skip_preamble = 0;
spi_slave->last_transaction_us = timer_get_us();
spi_slave->freq = bus->frequency;
if (max_hz)
@ -199,12 +204,29 @@ static void spi_get_fifo_levels(struct exynos_spi *regs,
*
* @param regs SPI peripheral registers
* @param count Number of bytes to transfer
* @param step Number of bytes to transfer in each packet (1 or 4)
*/
static void spi_request_bytes(struct exynos_spi *regs, int count)
static void spi_request_bytes(struct exynos_spi *regs, int count, int step)
{
/* For word address we need to swap bytes */
if (step == 4) {
setbits_le32(&regs->mode_cfg,
SPI_MODE_CH_WIDTH_WORD | SPI_MODE_BUS_WIDTH_WORD);
count /= 4;
setbits_le32(&regs->swap_cfg, SPI_TX_SWAP_EN | SPI_RX_SWAP_EN |
SPI_TX_BYTE_SWAP | SPI_RX_BYTE_SWAP |
SPI_TX_HWORD_SWAP | SPI_RX_HWORD_SWAP);
} else {
/* Select byte access and clear the swap configuration */
clrbits_le32(&regs->mode_cfg,
SPI_MODE_CH_WIDTH_WORD | SPI_MODE_BUS_WIDTH_WORD);
writel(0, &regs->swap_cfg);
}
assert(count && count < (1 << 16));
setbits_le32(&regs->ch_cfg, SPI_CH_RST);
clrbits_le32(&regs->ch_cfg, SPI_CH_RST);
writel(count | SPI_PACKET_CNT_EN, &regs->pkt_cnt);
}
@ -219,17 +241,27 @@ static int spi_rx_tx(struct exynos_spi_slave *spi_slave, int todo,
int toread;
unsigned start = get_timer(0);
int stopping;
int step;
out_bytes = in_bytes = todo;
stopping = spi_slave->skip_preamble && (flags & SPI_XFER_END) &&
!(spi_slave->mode & SPI_SLAVE);
/*
* Try to transfer words if we can. This helps read performance at
* SPI clock speeds above about 20MHz.
*/
step = 1;
if (!((todo | (uintptr_t)rxp | (uintptr_t)txp) & 3) &&
!spi_slave->skip_preamble)
step = 4;
/*
* If there's something to send, do a software reset and set a
* transaction size.
*/
spi_request_bytes(regs, todo);
spi_request_bytes(regs, todo, step);
/*
* Bytes are transmitted/received in pairs. Wait to receive all the
@ -242,24 +274,42 @@ static int spi_rx_tx(struct exynos_spi_slave *spi_slave, int todo,
/* Keep the fifos full/empty. */
spi_get_fifo_levels(regs, &rx_lvl, &tx_lvl);
if (tx_lvl < spi_slave->fifo_size && out_bytes) {
temp = txp ? *txp++ : 0xff;
/*
* Don't completely fill the txfifo, since we don't want our
* rxfifo to overflow, and it may already contain data.
*/
while (tx_lvl < spi_slave->fifo_size/2 && out_bytes) {
if (!txp)
temp = -1;
else if (step == 4)
temp = *(uint32_t *)txp;
else
temp = *txp;
writel(temp, &regs->tx_data);
out_bytes--;
out_bytes -= step;
if (txp)
txp += step;
tx_lvl += step;
}
if (rx_lvl > 0) {
temp = readl(&regs->rx_data);
if (spi_slave->skip_preamble) {
if (temp == SPI_PREAMBLE_END_BYTE) {
spi_slave->skip_preamble = 0;
stopping = 0;
if (rx_lvl >= step) {
while (rx_lvl >= step) {
temp = readl(&regs->rx_data);
if (spi_slave->skip_preamble) {
if (temp == SPI_PREAMBLE_END_BYTE) {
spi_slave->skip_preamble = 0;
stopping = 0;
}
} else {
if (rxp || stopping) {
*rxp = temp;
rxp += step;
}
in_bytes -= step;
}
} else {
if (rxp || stopping)
*rxp++ = temp;
in_bytes--;
toread -= step;
rx_lvl -= step;
}
toread--;
} else if (!toread) {
/*
* We have run out of input data, but haven't read
@ -271,7 +321,7 @@ static int spi_rx_tx(struct exynos_spi_slave *spi_slave, int todo,
out_bytes = in_bytes;
toread = in_bytes;
txp = NULL;
spi_request_bytes(regs, toread);
spi_request_bytes(regs, toread, step);
}
if (spi_slave->skip_preamble && get_timer(start) > 100) {
printf("SPI timeout: in_bytes=%d, out_bytes=%d, ",
@ -315,10 +365,14 @@ int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout,
if ((flags & SPI_XFER_BEGIN))
spi_cs_activate(slave);
/* Exynos SPI limits each transfer to 65535 bytes */
/*
* Exynos SPI limits each transfer to 65535 transfers. To keep
* things simple, allow a maximum of 65532 bytes. We could allow
* more in word mode, but the performance difference is small.
*/
bytelen = bitlen / 8;
for (upto = 0; !ret && upto < bytelen; upto += todo) {
todo = min(bytelen - upto, (1 << 16) - 1);
todo = min(bytelen - upto, (1 << 16) - 4);
ret = spi_rx_tx(spi_slave, todo, &din, &dout, flags);
if (ret)
break;
@ -359,9 +413,22 @@ void spi_cs_activate(struct spi_slave *slave)
{
struct exynos_spi_slave *spi_slave = to_exynos_spi(slave);
/* If it's too soon to do another transaction, wait */
if (spi_slave->bus->deactivate_delay_us &&
spi_slave->last_transaction_us) {
ulong delay_us; /* The delay completed so far */
delay_us = timer_get_us() - spi_slave->last_transaction_us;
if (delay_us < spi_slave->bus->deactivate_delay_us)
udelay(spi_slave->bus->deactivate_delay_us - delay_us);
}
clrbits_le32(&spi_slave->regs->cs_reg, SPI_SLAVE_SIG_INACT);
debug("Activate CS, bus %d\n", spi_slave->slave.bus);
spi_slave->skip_preamble = spi_slave->mode & SPI_PREAMBLE;
/* Remember time of this transaction so we can honour the bus delay */
if (spi_slave->bus->deactivate_delay_us)
spi_slave->last_transaction_us = timer_get_us();
}
/**
@ -411,6 +478,8 @@ static int spi_get_config(const void *blob, int node, struct spi_bus *bus)
/* Use 500KHz as a suitable default */
bus->frequency = fdtdec_get_int(blob, node, "spi-max-frequency",
500000);
bus->deactivate_delay_us = fdtdec_get_int(blob, node,
"spi-deactivate-delay", 0);
return 0;
}

311
drivers/spi/ti_qspi.c Normal file
View File

@ -0,0 +1,311 @@
/*
* TI QSPI driver
*
* Copyright (C) 2013, Texas Instruments, Incorporated
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <asm/io.h>
#include <asm/arch/omap.h>
#include <malloc.h>
#include <spi.h>
/* ti qpsi register bit masks */
#define QSPI_TIMEOUT 2000000
#define QSPI_FCLK 192000000
/* clock control */
#define QSPI_CLK_EN (1 << 31)
#define QSPI_CLK_DIV_MAX 0xffff
/* command */
#define QSPI_EN_CS(n) (n << 28)
#define QSPI_WLEN(n) ((n-1) << 19)
#define QSPI_3_PIN (1 << 18)
#define QSPI_RD_SNGL (1 << 16)
#define QSPI_WR_SNGL (2 << 16)
#define QSPI_INVAL (4 << 16)
#define QSPI_RD_QUAD (7 << 16)
/* device control */
#define QSPI_DD(m, n) (m << (3 + n*8))
#define QSPI_CKPHA(n) (1 << (2 + n*8))
#define QSPI_CSPOL(n) (1 << (1 + n*8))
#define QSPI_CKPOL(n) (1 << (n*8))
/* status */
#define QSPI_WC (1 << 1)
#define QSPI_BUSY (1 << 0)
#define QSPI_WC_BUSY (QSPI_WC | QSPI_BUSY)
#define QSPI_XFER_DONE QSPI_WC
#define MM_SWITCH 0x01
#define MEM_CS 0x100
#define MEM_CS_UNSELECT 0xfffff0ff
#define MMAP_START_ADDR 0x5c000000
#define CORE_CTRL_IO 0x4a002558
#define QSPI_CMD_READ (0x3 << 0)
#define QSPI_CMD_READ_QUAD (0x6b << 0)
#define QSPI_CMD_READ_FAST (0x0b << 0)
#define QSPI_SETUP0_NUM_A_BYTES (0x2 << 8)
#define QSPI_SETUP0_NUM_D_BYTES_NO_BITS (0x0 << 10)
#define QSPI_SETUP0_NUM_D_BYTES_8_BITS (0x1 << 10)
#define QSPI_SETUP0_READ_NORMAL (0x0 << 12)
#define QSPI_SETUP0_READ_QUAD (0x3 << 12)
#define QSPI_CMD_WRITE (0x2 << 16)
#define QSPI_NUM_DUMMY_BITS (0x0 << 24)
/* ti qspi register set */
struct ti_qspi_regs {
u32 pid;
u32 pad0[3];
u32 sysconfig;
u32 pad1[3];
u32 int_stat_raw;
u32 int_stat_en;
u32 int_en_set;
u32 int_en_ctlr;
u32 intc_eoi;
u32 pad2[3];
u32 clk_ctrl;
u32 dc;
u32 cmd;
u32 status;
u32 data;
u32 setup0;
u32 setup1;
u32 setup2;
u32 setup3;
u32 memswitch;
u32 data1;
u32 data2;
u32 data3;
};
/* ti qspi slave */
struct ti_qspi_slave {
struct spi_slave slave;
struct ti_qspi_regs *base;
unsigned int mode;
u32 cmd;
u32 dc;
};
static inline struct ti_qspi_slave *to_ti_qspi_slave(struct spi_slave *slave)
{
return container_of(slave, struct ti_qspi_slave, slave);
}
static void ti_spi_setup_spi_register(struct ti_qspi_slave *qslave)
{
struct spi_slave *slave = &qslave->slave;
u32 memval = 0;
slave->memory_map = (void *)MMAP_START_ADDR;
memval |= QSPI_CMD_READ | QSPI_SETUP0_NUM_A_BYTES |
QSPI_SETUP0_NUM_D_BYTES_NO_BITS |
QSPI_SETUP0_READ_NORMAL | QSPI_CMD_WRITE |
QSPI_NUM_DUMMY_BITS;
writel(memval, &qslave->base->setup0);
}
static void ti_spi_set_speed(struct spi_slave *slave, uint hz)
{
struct ti_qspi_slave *qslave = to_ti_qspi_slave(slave);
uint clk_div;
debug("ti_spi_set_speed: hz: %d, clock divider %d\n", hz, clk_div);
if (!hz)
clk_div = 0;
else
clk_div = (QSPI_FCLK / hz) - 1;
/* disable SCLK */
writel(readl(&qslave->base->clk_ctrl) & ~QSPI_CLK_EN,
&qslave->base->clk_ctrl);
/* assign clk_div values */
if (clk_div < 0)
clk_div = 0;
else if (clk_div > QSPI_CLK_DIV_MAX)
clk_div = QSPI_CLK_DIV_MAX;
/* enable SCLK */
writel(QSPI_CLK_EN | clk_div, &qslave->base->clk_ctrl);
}
int spi_cs_is_valid(unsigned int bus, unsigned int cs)
{
return 1;
}
void spi_cs_activate(struct spi_slave *slave)
{
/* CS handled in xfer */
return;
}
void spi_cs_deactivate(struct spi_slave *slave)
{
struct ti_qspi_slave *qslave = to_ti_qspi_slave(slave);
debug("spi_cs_deactivate: 0x%08x\n", (u32)slave);
writel(qslave->cmd | QSPI_INVAL, &qslave->base->cmd);
}
void spi_init(void)
{
/* nothing to do */
}
struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
unsigned int max_hz, unsigned int mode)
{
struct ti_qspi_slave *qslave;
qslave = spi_alloc_slave(struct ti_qspi_slave, bus, cs);
if (!qslave) {
printf("SPI_error: Fail to allocate ti_qspi_slave\n");
return NULL;
}
qslave->base = (struct ti_qspi_regs *)QSPI_BASE;
qslave->mode = mode;
ti_spi_set_speed(&qslave->slave, max_hz);
#ifdef CONFIG_TI_SPI_MMAP
ti_spi_setup_spi_register(qslave);
#endif
return &qslave->slave;
}
void spi_free_slave(struct spi_slave *slave)
{
struct ti_qspi_slave *qslave = to_ti_qspi_slave(slave);
free(qslave);
}
int spi_claim_bus(struct spi_slave *slave)
{
struct ti_qspi_slave *qslave = to_ti_qspi_slave(slave);
debug("spi_claim_bus: bus:%i cs:%i\n", slave->bus, slave->cs);
qslave->dc = 0;
if (qslave->mode & SPI_CPHA)
qslave->dc |= QSPI_CKPHA(slave->cs);
if (qslave->mode & SPI_CPOL)
qslave->dc |= QSPI_CKPOL(slave->cs);
if (qslave->mode & SPI_CS_HIGH)
qslave->dc |= QSPI_CSPOL(slave->cs);
writel(qslave->dc, &qslave->base->dc);
writel(0, &qslave->base->cmd);
writel(0, &qslave->base->data);
return 0;
}
void spi_release_bus(struct spi_slave *slave)
{
struct ti_qspi_slave *qslave = to_ti_qspi_slave(slave);
debug("spi_release_bus: bus:%i cs:%i\n", slave->bus, slave->cs);
writel(0, &qslave->base->dc);
writel(0, &qslave->base->cmd);
writel(0, &qslave->base->data);
}
int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout,
void *din, unsigned long flags)
{
struct ti_qspi_slave *qslave = to_ti_qspi_slave(slave);
uint words = bitlen >> 3; /* fixed 8-bit word length */
const uchar *txp = dout;
uchar *rxp = din;
uint status;
int timeout, val;
debug("spi_xfer: bus:%i cs:%i bitlen:%i words:%i flags:%lx\n",
slave->bus, slave->cs, bitlen, words, flags);
/* Setup mmap flags */
if (flags & SPI_XFER_MMAP) {
writel(MM_SWITCH, &qslave->base->memswitch);
val = readl(CORE_CTRL_IO);
val |= MEM_CS;
writel(val, CORE_CTRL_IO);
return 0;
} else if (flags & SPI_XFER_MMAP_END) {
writel(~MM_SWITCH, &qslave->base->memswitch);
val = readl(CORE_CTRL_IO);
val &= MEM_CS_UNSELECT;
writel(val, CORE_CTRL_IO);
return 0;
}
if (bitlen == 0)
return -1;
if (bitlen % 8) {
debug("spi_xfer: Non byte aligned SPI transfer\n");
return -1;
}
/* Setup command reg */
qslave->cmd = 0;
qslave->cmd |= QSPI_WLEN(8);
qslave->cmd |= QSPI_EN_CS(slave->cs);
if (flags & SPI_3WIRE)
qslave->cmd |= QSPI_3_PIN;
qslave->cmd |= 0xfff;
while (words--) {
if (txp) {
debug("tx cmd %08x dc %08x data %02x\n",
qslave->cmd | QSPI_WR_SNGL, qslave->dc, *txp);
writel(*txp++, &qslave->base->data);
writel(qslave->cmd | QSPI_WR_SNGL,
&qslave->base->cmd);
status = readl(&qslave->base->status);
timeout = QSPI_TIMEOUT;
while ((status & QSPI_WC_BUSY) != QSPI_XFER_DONE) {
if (--timeout < 0) {
printf("spi_xfer: TX timeout!\n");
return -1;
}
status = readl(&qslave->base->status);
}
debug("tx done, status %08x\n", status);
}
if (rxp) {
qslave->cmd |= QSPI_RD_SNGL;
debug("rx cmd %08x dc %08x\n",
qslave->cmd, qslave->dc);
writel(qslave->cmd, &qslave->base->cmd);
status = readl(&qslave->base->status);
timeout = QSPI_TIMEOUT;
while ((status & QSPI_WC_BUSY) != QSPI_XFER_DONE) {
if (--timeout < 0) {
printf("spi_xfer: RX timeout!\n");
return -1;
}
status = readl(&qslave->base->status);
}
*rxp++ = readl(&qslave->base->data);
debug("rx done, status %08x, read %02x\n",
status, *(rxp-1));
}
}
/* Terminate frame */
if (flags & SPI_XFER_END)
spi_cs_deactivate(slave);
return 0;
}

View File

@ -596,6 +596,12 @@ void ddr_enable_ecc(unsigned int dram_size);
#endif
#endif
/*
* Return the current value of a monotonically increasing microsecond timer.
* Granularity may be larger than 1us if hardware does not support this.
*/
ulong timer_get_us(void);
/* $(CPU)/cpu.c */
static inline int cpumask_next(int cpu, unsigned int mask)
{

View File

@ -48,4 +48,23 @@
#define CONFIG_PHYLIB
#define CONFIG_PHY_ADDR 2
/* SPI */
#undef CONFIG_OMAP3_SPI
#define CONFIG_TI_QSPI
#define CONFIG_SPI_FLASH
#define CONFIG_SPI_FLASH_SPANSION
#define CONFIG_CMD_SF
#define CONFIG_CMD_SPI
#define CONFIG_TI_SPI_MMAP
#define CONFIG_SF_DEFAULT_SPEED 48000000
#define CONFIG_DEFAULT_SPI_MODE SPI_MODE_3
/* SPI SPL */
#define CONFIG_SPL_SPI_SUPPORT
#define CONFIG_SPL_SPI_LOAD
#define CONFIG_SPL_SPI_FLASH_SUPPORT
#define CONFIG_SPL_SPI_BUS 0
#define CONFIG_SPL_SPI_CS 0
#define CONFIG_SYS_SPI_U_BOOT_OFFS 0x20000
#endif /* __CONFIG_DRA7XX_EVM_H */

View File

@ -120,7 +120,6 @@
#define CONFIG_ATMEL_SPI0 /* SPI used for FRAM is SPI0 */
#define FRAM_SPI_BUS 0
#define FRAM_CS_NUM 0
#define CONFIG_SPI_FLASH /* RAMTRON FRAM on SPI bus */
#define CONFIG_SPI_FRAM_RAMTRON
#define CONFIG_SF_DEFAULT_SPEED 1000000 /* be conservative here... */
#define CONFIG_SF_DEFAULT_MODE SPI_MODE_0

View File

@ -25,29 +25,33 @@
#define SPI_PREAMBLE 0x80 /* Skip preamble bytes */
/* SPI transfer flags */
#define SPI_XFER_BEGIN 0x01 /* Assert CS before transfer */
#define SPI_XFER_END 0x02 /* Deassert CS after transfer */
#define SPI_XFER_BEGIN 0x01 /* Assert CS before transfer */
#define SPI_XFER_END 0x02 /* Deassert CS after transfer */
#define SPI_XFER_MMAP 0x08 /* Memory Mapped start */
#define SPI_XFER_MMAP_END 0x10 /* Memory Mapped End */
/* Header byte that marks the start of the message */
#define SPI_PREAMBLE_END_BYTE 0xec
/*-----------------------------------------------------------------------
* Representation of a SPI slave, i.e. what we're communicating with.
/**
* struct spi_slave - Representation of a SPI slave
*
* Drivers are expected to extend this with controller-specific data.
*
* bus: ID of the bus that the slave is attached to.
* cs: ID of the chip select connected to the slave.
* max_write_size: If non-zero, the maximum number of bytes which can
* be written at once, excluding command bytes.
* @bus: ID of the bus that the slave is attached to.
* @cs: ID of the chip select connected to the slave.
* @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.
*/
struct spi_slave {
unsigned int bus;
unsigned int cs;
unsigned int bus;
unsigned int cs;
unsigned int max_write_size;
void *memory_map;
};
/*-----------------------------------------------------------------------
/**
* Initialization, must be called once on start up.
*
* TODO: I don't think we really need this.
@ -60,10 +64,10 @@ void spi_init(void);
* Allocate and zero all fields in the spi slave, and set the bus/chip
* select. Use the helper macro spi_alloc_slave() to call this.
*
* @offset: Offset of struct spi_slave within slave structure
* @size: Size of slave structure
* @bus: Bus ID of the slave chip.
* @cs: Chip select ID of the slave chip on the specified bus.
* @offset: Offset of struct spi_slave within slave structure.
* @size: Size of slave structure.
* @bus: Bus ID of the slave chip.
* @cs: Chip select ID of the slave chip on the specified bus.
*/
void *spi_do_alloc_slave(int offset, int size, unsigned int bus,
unsigned int cs);
@ -74,10 +78,10 @@ void *spi_do_alloc_slave(int offset, int size, unsigned int bus,
* Allocate and zero all fields in the spi slave, and set the bus/chip
* select.
*
* @_struct: Name of structure to allocate (e.g. struct tegra_spi). This
* structure must contain a member 'struct spi_slave *slave'.
* @bus: Bus ID of the slave chip.
* @cs: Chip select ID of the slave chip on the specified bus.
* @_struct: Name of structure to allocate (e.g. struct tegra_spi).
* This structure must contain a member 'struct spi_slave *slave'.
* @bus: Bus ID of the slave chip.
* @cs: Chip select ID of the slave chip on the specified bus.
*/
#define spi_alloc_slave(_struct, bus, cs) \
spi_do_alloc_slave(offsetof(_struct, slave), \
@ -89,13 +93,13 @@ void *spi_do_alloc_slave(int offset, int size, unsigned int bus,
* Allocate and zero all fields in the spi slave, and set the bus/chip
* select.
*
* @bus: Bus ID of the slave chip.
* @cs: Chip select ID of the slave chip on the specified bus.
* @bus: Bus ID of the slave chip.
* @cs: Chip select ID of the slave chip on the specified bus.
*/
#define spi_alloc_slave_base(bus, cs) \
spi_do_alloc_slave(0, sizeof(struct spi_slave), bus, cs)
/*-----------------------------------------------------------------------
/**
* Set up communications parameters for a SPI slave.
*
* This must be called once for each slave. Note that this function
@ -103,10 +107,10 @@ void *spi_do_alloc_slave(int offset, int size, unsigned int bus,
* contents of spi_slave so that the hardware can be easily
* initialized later.
*
* bus: Bus ID of the slave chip.
* cs: Chip select ID of the slave chip on the specified bus.
* max_hz: Maximum SCK rate in Hz.
* mode: Clock polarity, clock phase and other parameters.
* @bus: Bus ID of the slave chip.
* @cs: Chip select ID of the slave chip on the specified bus.
* @max_hz: Maximum SCK rate in Hz.
* @mode: Clock polarity, clock phase and other parameters.
*
* Returns: A spi_slave reference that can be used in subsequent SPI
* calls, or NULL if one or more of the parameters are not supported.
@ -114,14 +118,14 @@ void *spi_do_alloc_slave(int offset, int size, unsigned int bus,
struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
unsigned int max_hz, unsigned int mode);
/*-----------------------------------------------------------------------
/**
* Free any memory associated with a SPI slave.
*
* slave: The SPI slave
* @slave: The SPI slave
*/
void spi_free_slave(struct spi_slave *slave);
/*-----------------------------------------------------------------------
/**
* Claim the bus and prepare it for communication with a given slave.
*
* This must be called before doing any transfers with a SPI slave. It
@ -130,25 +134,25 @@ void spi_free_slave(struct spi_slave *slave);
* allowed to claim the same bus for several slaves without releasing
* the bus in between.
*
* slave: The SPI slave
* @slave: The SPI slave
*
* Returns: 0 if the bus was claimed successfully, or a negative value
* if it wasn't.
*/
int spi_claim_bus(struct spi_slave *slave);
/*-----------------------------------------------------------------------
/**
* Release the SPI bus
*
* This must be called once for every call to spi_claim_bus() after
* all transfers have finished. It may disable any SPI hardware as
* appropriate.
*
* slave: The SPI slave
* @slave: The SPI slave
*/
void spi_release_bus(struct spi_slave *slave);
/*-----------------------------------------------------------------------
/**
* SPI transfer
*
* This writes "bitlen" bits out the SPI MOSI port and simultaneously clocks
@ -161,19 +165,19 @@ void spi_release_bus(struct spi_slave *slave);
* temporary variables, this is OK).
*
* spi_xfer() interface:
* slave: The SPI slave which will be sending/receiving the data.
* bitlen: How many bits to write and read.
* dout: Pointer to a string of bits to send out. The bits are
* @slave: The SPI slave which will be sending/receiving the data.
* @bitlen: How many bits to write and read.
* @dout: Pointer to a string of bits to send out. The bits are
* held in a byte array and are sent MSB first.
* din: Pointer to a string of bits that will be filled in.
* flags: A bitwise combination of SPI_XFER_* flags.
* @din: Pointer to a string of bits that will be filled in.
* @flags: A bitwise combination of SPI_XFER_* flags.
*
* Returns: 0 on success, not 0 on failure
* Returns: 0 on success, not 0 on failure
*/
int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout,
void *din, unsigned long flags);
/*-----------------------------------------------------------------------
/**
* 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.
@ -183,7 +187,7 @@ int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout,
*/
int spi_cs_is_valid(unsigned int bus, unsigned int cs);
/*-----------------------------------------------------------------------
/**
* 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.
@ -192,7 +196,7 @@ int spi_cs_is_valid(unsigned int bus, unsigned int cs);
*/
void spi_cs_activate(struct spi_slave *slave);
/*-----------------------------------------------------------------------
/**
* 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.
@ -201,18 +205,18 @@ void spi_cs_activate(struct spi_slave *slave);
*/
void spi_cs_deactivate(struct spi_slave *slave);
/*-----------------------------------------------------------------------
/**
* Set transfer speed.
* This sets a new speed to be applied for next spi_xfer().
* slave: The SPI slave
* hz: The transfer speed
* @slave: The SPI slave
* @hz: The transfer speed
*/
void spi_set_speed(struct spi_slave *slave, uint hz);
/*-----------------------------------------------------------------------
/**
* Write 8 bits, then read 8 bits.
* slave: The SPI slave we're communicating with
* byte: Byte to be written
* @slave: The SPI slave we're communicating with
* @byte: Byte to be written
*
* Returns: The value that was read, or a negative value on error.
*

View File

@ -1,7 +1,8 @@
/*
* Interface to SPI flash
* Common SPI flash Interface
*
* Copyright (C) 2008 Atmel Corporation
* Copyright (C) 2013 Jagannadha Sutradharudu Teki, Xilinx Inc.
*
* See file CREDITS for list of people who contributed to this
* project.
@ -10,6 +11,7 @@
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*/
#ifndef _SPI_FLASH_H_
#define _SPI_FLASH_H_
@ -17,75 +19,52 @@
#include <linux/types.h>
#include <linux/compiler.h>
/**
* struct spi_flash - SPI flash structure
*
* @spi: SPI slave
* @name: Name of SPI flash
* @size: Total flash size
* @page_size: Write (page) size
* @sector_size: Sector size
* @erase_size: Erase size
* @bank_read_cmd: Bank read cmd
* @bank_write_cmd: Bank write cmd
* @bank_curr: Current flash bank
* @poll_cmd: Poll cmd - for flash erase/program
* @erase_cmd: Erase cmd 4K, 32K, 64K
* @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
* @write: Flash write ops: Write len bytes from buf into offeset
* Supported cmds: Page Program
* @erase: Flash erase ops: Erase len bytes from offset
* Supported cmds: Sector erase 4K, 32K, 64K
* return 0 - Sucess, 1 - Failure
*/
struct spi_flash {
struct spi_slave *spi;
const char *name;
const char *name;
/* Total flash size */
u32 size;
/* Write (page) size */
u32 page_size;
/* Erase (sector) size */
u32 sector_size;
u32 size;
u32 page_size;
u32 sector_size;
u32 erase_size;
#ifdef CONFIG_SPI_FLASH_BAR
/* Bank read cmd */
u8 bank_read_cmd;
/* Bank write cmd */
u8 bank_write_cmd;
/* Current flash bank */
u8 bank_curr;
u8 bank_read_cmd;
u8 bank_write_cmd;
u8 bank_curr;
#endif
/* Poll cmd - for flash erase/program */
u8 poll_cmd;
u8 poll_cmd;
u8 erase_cmd;
void *memory_map; /* Address of read-only SPI flash access */
int (*read)(struct spi_flash *flash, u32 offset,
size_t len, void *buf);
int (*write)(struct spi_flash *flash, u32 offset,
size_t len, const void *buf);
int (*erase)(struct spi_flash *flash, u32 offset,
size_t len);
void *memory_map;
int (*read)(struct spi_flash *flash, u32 offset, size_t len, void *buf);
int (*write)(struct spi_flash *flash, u32 offset, size_t len,
const void *buf);
int (*erase)(struct spi_flash *flash, u32 offset, size_t len);
};
/**
* spi_flash_do_alloc - Allocate a new spi flash structure
*
* The structure is allocated and cleared with default values for
* read, write and erase, which the caller can modify. The caller must set
* up size, page_size and sector_size.
*
* Use the helper macro spi_flash_alloc() to call this.
*
* @offset: Offset of struct spi_slave within slave structure
* @size: Size of slave structure
* @spi: SPI slave
* @name: Name of SPI flash device
*/
void *spi_flash_do_alloc(int offset, int size, struct spi_slave *spi,
const char *name);
/**
* spi_flash_alloc - Allocate a new SPI flash structure
*
* @_struct: Name of structure to allocate (e.g. struct ramtron_spi_fram). This
* structure must contain a member 'struct spi_flash *flash'.
* @spi: SPI slave
* @name: Name of SPI flash device
*/
#define spi_flash_alloc(_struct, spi, name) \
spi_flash_do_alloc(offsetof(_struct, flash), sizeof(_struct), \
spi, name)
/**
* spi_flash_alloc_base - Allocate a new SPI flash structure with no private data
*
* @spi: SPI slave
* @name: Name of SPI flash device
*/
#define spi_flash_alloc_base(spi, name) \
spi_flash_do_alloc(0, sizeof(struct spi_flash), spi, name)
struct spi_flash *spi_flash_probe(unsigned int bus, unsigned int cs,
unsigned int max_hz, unsigned int spi_mode);
void spi_flash_free(struct spi_flash *flash);