[U-Boot] [PATCH 09/11] mtd/spi: add FTSPI020 SPI Flash controller support

Kuo-Jung Su
2013-03-29T07:06:26Z

Faraday FTSPI020 is dedicated SPI bus designed for
SPI Flash chips. It supports Fast-Read-Dual,
Fast-Read-Dual-IO, Fast-Read-Quad and Fast-Read-Quad-IO.

Signed-off-by: Kuo-Jung Su <dantesu@faraday-tech.com>
---
drivers/mtd/spi/Makefile | 4 +
drivers/mtd/spi/ftspi020.c | 589 ++++++++++++++++++++++++++++++++++++++++++++
drivers/mtd/spi/ftspi020.h | 118 +++++++++
drivers/mtd/spi/winbond.c | 17 +-
4 files changed, 727 insertions(+), 1 deletion(-)
create mode 100644 drivers/mtd/spi/ftspi020.c
create mode 100644 drivers/mtd/spi/ftspi020.h

diff --git a/drivers/mtd/spi/Makefile b/drivers/mtd/spi/Makefile
index 90f8392..ce60e1b 100644
--- a/drivers/mtd/spi/Makefile
+++ b/drivers/mtd/spi/Makefile
@@ -29,6 +29,9 @@ ifdef CONFIG_SPL_BUILD
COBJS-$(CONFIG_SPL_SPI_LOAD) += spi_spl_load.o
endif

+ifeq ($(CONFIG_FTSPI020),y)
+COBJS-$(CONFIG_FTSPI020) += ftspi020.o
+else
COBJS-$(CONFIG_SPI_FLASH) += spi_flash.o
COBJS-$(CONFIG_SPI_FLASH_ATMEL) += atmel.o
COBJS-$(CONFIG_SPI_FLASH_EON) += eon.o
@@ -39,6 +42,7 @@ COBJS-$(CONFIG_SPI_FLASH_STMICRO) += stmicro.o
COBJS-$(CONFIG_SPI_FLASH_WINBOND) += winbond.o
COBJS-$(CONFIG_SPI_FRAM_RAMTRON) += ramtron.o
COBJS-$(CONFIG_SPI_M95XXX) += eeprom_m95xxx.o
+endif

COBJS := $(COBJS-y)
SRCS := $(COBJS:.o=.c)
diff --git a/drivers/mtd/spi/ftspi020.c b/drivers/mtd/spi/ftspi020.c
new file mode 100644
index 0000000..5c85203
--- /dev/null
+++ b/drivers/mtd/spi/ftspi020.c
@@ -0,0 +1,589 @@
+/*
+ * Faraday SPI Flash Controller
+ *
+ * (C) Copyright 2010 Faraday Technology
+ * Dante Su <dantesu@faraday-tech.com>
+ *
+ * This file is released under the terms of GPL v2 and any later version.
+ * See the file COPYING in the root directory of the source tree for details.
+ */
+
+#include <common.h>
+#include <malloc.h>
+#include <spi.h>
+#include <spi_flash.h>
+
+#include "ftspi020.h"
+
+#define CFG_USE_FASTRD 1
+#define CFG_USE_FASTRD_QUAD 1 /* Fast Read Quad */
+#define CFG_SHOW_PROGRESS 1 /* print a '.' at the end of each action */
+
+/* Register access macros */
+#define SPI_REG32(reg) *(volatile uint32_t *)(CONFIG_FTSPI020_BASE + (reg))
+
+/* Flash opcodes. */
+#define OPCODE_WREN 0x06 /* Write enable */
+#define OPCODE_RDSR 0x05 /* Read status register */
+#define OPCODE_WRSR 0x01 /* Write status register 1 byte */
+#define OPCODE_NORM_READ 0x03 /* Read data bytes (low frequency) */
+#define OPCODE_NORM_READ4 0x13 /* Read data bytes (low frequency, 4 bytes address) */
+#define OPCODE_FAST_READ 0x0b /* Read data bytes (high frequency) */
+#define OPCODE_FAST_READ4 0x0c /* Read data bytes (high frequency, 4 bytes address) */
+#define OPCODE_FAST_READ_DUAL 0x3b /* Read data bytes (high frequency) */
+#define OPCODE_FAST_READ4_DUAL 0x3c /* Read data bytes (high frequency, 4 bytes address) */
+#define OPCODE_FAST_READ_QUAD 0x6b /* Read data bytes (high frequency) */
+#define OPCODE_FAST_READ4_QUAD 0x6c /* Read data bytes (high frequency, 4 bytes address) */
+#define OPCODE_PP 0x02 /* Page program (up to 256 bytes) */
+#define OPCODE_PP4 0x12 /* Page program (up to 256 bytes, 4 bytes address) */
+#define OPCODE_BE_4K 0x20 /* Erase 4KiB block */
+#define OPCODE_BE_32K 0x52 /* Erase 32KiB block */
+#define OPCODE_CHIP_ERASE 0xc7 /* Erase whole flash chip */
+#define OPCODE_SE 0xd8 /* Sector erase (usually 64KiB) */
+#define OPCODE_SE4 0xdc /* Sector erase (usually 64KiB, 4 bytes address) */
+#define OPCODE_RDID 0x9f /* Read JEDEC ID */
+
+/* Status Register bits. */
+#define SR_WIP BIT(0) /* Write in progress */
+#define SR_WEL BIT(1) /* Write enable latch */
+
+struct spi_flash_param {
+ const char *name;
+ uint32_t id;
+ uint32_t ext_id;
+ uint32_t sz_sector;
+ uint32_t nr_sector;
+
+ uint32_t flags;
+#define SECT_4K BIT(0) /* OPCODE_BE_4K works uniformly */
+#define FASTRD_DUAL BIT(8)
+#if CFG_USE_FASTRD_QUAD
+#define FASTRD_QUAD BIT(9)
+#else
+#define FASTRD_QUAD 0
+#endif
+};
+
+/* spi_flash needs to be first so upper layers can free() it */
+struct spi_flash_info {
+ struct spi_flash flash;
+ const struct spi_flash_param *param;
+
+ unsigned fastrd_dual:1;
+ unsigned fastrd_quad:1;
+};
+
+static inline const struct spi_flash_param *flash_to_param(struct spi_flash *flash)
+{
+ struct spi_flash_info *fl = (struct spi_flash_info *)flash;
+ return fl->param;
+}
+
+static const struct spi_flash_param fl_list[] = {
+
+ /* Atmel -- some are (confusingly) marketed as "DataFlash" */
+ { "at25fs010", 0x1f6601, 0, 32 * 1024, 4 },
+ { "at25fs040", 0x1f6604, 0, 64 * 1024, 8 },
+
+ { "at25df041a", 0x1f4401, 0, 64 * 1024, 8 },
+ { "at25df321a", 0x1f4701, 0, 64 * 1024, 64 },
+ { "at25df641", 0x1f4800, 0, 64 * 1024, 128 },
+
+ { "at26f004", 0x1f0400, 0, 64 * 1024, 8 },
+ { "at26df081a", 0x1f4501, 0, 64 * 1024, 16 },
+ { "at26df161a", 0x1f4601, 0, 64 * 1024, 32 },
+ { "at26df321", 0x1f4700, 0, 64 * 1024, 64 },
+
+ /* EON -- en25xxx */
+ { "en25f32", 0x1c3116, 0, 64 * 1024, 64 },
+ { "en25p32", 0x1c2016, 0, 64 * 1024, 64 },
+ { "en25q32b", 0x1c3016, 0, 64 * 1024, 64 },
+ { "en25p64", 0x1c2017, 0, 64 * 1024, 128 },
+ { "en25qh256", 0x1c7019, 0, 64 * 1024, 512 },
+
+ /* GD -- GD25xxx */
+ { "gd25q16", 0xc84015, 0, 64 * 1024, 32 },
+ { "gd25q32", 0xc84016, 0, 64 * 1024, 64 },
+ { "gd25q64", 0xc84017, 0, 64 * 1024, 128 },
+
+ /* Intel/Numonyx -- xxxs33b */
+ { "160s33b", 0x898911, 0, 64 * 1024, 32 },
+ { "320s33b", 0x898912, 0, 64 * 1024, 64 },
+ { "640s33b", 0x898913, 0, 64 * 1024, 128 },
+
+ /* Macronix */
+ { "mx25l4005a", 0xc22013, 0, 64 * 1024, 8 },
+ { "mx25l8005", 0xc22014, 0, 64 * 1024, 16 },
+ { "mx25l1606e", 0xc22015, 0, 64 * 1024, 32 },
+ { "mx25l3205d", 0xc22016, 0, 64 * 1024, 64 },
+ { "mx25l6405d", 0xc22017, 0, 64 * 1024, 128 },
+ { "mx25l12805d", 0xc22018, 0, 64 * 1024, 256 },
+ { "mx25l12855e", 0xc22618, 0, 64 * 1024, 256 },
+ { "mx25l25635e", 0xc22019, 0, 64 * 1024, 512 },
+ { "mx25l25655e", 0xc22619, 0, 64 * 1024, 512 },
+
+ /* Spansion -- single (large) sector size only, at least
+ * for the chips listed here (without boot sectors).
+ */
+ { "s25sl004a", 0x010212, 0, 64 * 1024, 8 },
+ { "s25sl008a", 0x010213, 0, 64 * 1024, 16 },
+ { "s25sl016a", 0x010214, 0, 64 * 1024, 32 },
+ { "s25sl032a", 0x010215, 0, 64 * 1024, 64 },
+ { "s25sl032p", 0x010215, 0x4d00, 64 * 1024, 64 },
+ { "s25sl064a", 0x010216, 0, 64 * 1024, 128 },
+ { "s25fl128s0", 0x010218, 0x4d00, 256 * 1024, 64, FASTRD_DUAL | FASTRD_QUAD },
+ { "s25fl128s1", 0x010218, 0x4d01, 64 * 1024, 256, FASTRD_DUAL | FASTRD_QUAD },
+ { "s25fl256s0", 0x010219, 0x4d00, 256 * 1024, 128, FASTRD_DUAL | FASTRD_QUAD },
+ { "s25fl256s1", 0x010219, 0x4d01, 64 * 1024, 512, FASTRD_DUAL | FASTRD_QUAD },
+ { "s25fl512s", 0x010220, 0x4d00, 256 * 1024, 256, FASTRD_DUAL | FASTRD_QUAD },
+ { "s70fl01gs", 0x010221, 0x4d00, 256 * 1024, 256 },
+ { "s25sl12800", 0x012018, 0x0300, 256 * 1024, 64 },
+ { "s25sl12801", 0x012018, 0x0301, 64 * 1024, 256 },
+ { "s25fl129p0", 0x012018, 0x4d00, 256 * 1024, 64, FASTRD_DUAL | FASTRD_QUAD },
+ { "s25fl129p1", 0x012018, 0x4d01, 64 * 1024, 256, FASTRD_DUAL | FASTRD_QUAD },
+ { "s25fl016k", 0xef4015, 0, 64 * 1024, 32 },
+ { "s25fl064k", 0xef4017, 0, 64 * 1024, 128 },
+
+ /* SST -- large erase sizes are "overlays", "sectors" are 4K */
+ { "sst25vf040b", 0xbf258d, 0, 64 * 1024, 8 },
+ { "sst25vf080b", 0xbf258e, 0, 64 * 1024, 16 },
+ { "sst25vf016b", 0xbf2541, 0, 64 * 1024, 32 },
+ { "sst25vf032b", 0xbf254a, 0, 64 * 1024, 64 },
+ { "sst25vf064c", 0xbf254b, 0, 64 * 1024, 128, FASTRD_DUAL },
+ { "sst25wf512", 0xbf2501, 0, 64 * 1024, 1 },
+ { "sst25wf010", 0xbf2502, 0, 64 * 1024, 2 },
+ { "sst25wf020", 0xbf2503, 0, 64 * 1024, 4 },
+ { "sst25wf040", 0xbf2504, 0, 64 * 1024, 8 },
+
+ /* Micron -- newer production may have feature updates */
+ { "m25p05", 0x202010, 0, 32 * 1024, 2 },
+ { "m25p10", 0x202011, 0, 32 * 1024, 4 },
+ { "m25p20", 0x202012, 0, 64 * 1024, 4 },
+ { "m25p40", 0x202013, 0, 64 * 1024, 8 },
+ { "m25p80", 0x202014, 0, 64 * 1024, 16 },
+ { "m25p16", 0x202015, 0, 64 * 1024, 32 },
+ { "m25p32", 0x202016, 0, 64 * 1024, 64 },
+ { "m25p64", 0x202017, 0, 64 * 1024, 128 },
+ { "m25p128", 0x202018, 0, 256 * 1024, 64 },
+
+ { "m45pe10", 0x204011, 0, 64 * 1024, 2 },
+ { "m45pe80", 0x204014, 0, 64 * 1024, 16 },
+ { "m45pe16", 0x204015, 0, 64 * 1024, 32 },
+
+ { "m25pe80", 0x208014, 0, 64 * 1024, 16 },
+ { "m25pe16", 0x208015, 0, 64 * 1024, 32 },
+
+ { "m25px32", 0x207116, 0, 64 * 1024, 64 },
+ { "m25px32-s0", 0x207316, 0, 64 * 1024, 64 },
+ { "m25px32-s1", 0x206316, 0, 64 * 1024, 64 },
+ { "m25px64", 0x207117, 0, 64 * 1024, 128 },
+
+ { "n25q032a13e", 0x20ba16, 0, 64 * 1024, 64, },
+ { "n25q064a13e", 0x20ba17, 0, 64 * 1024, 128, },
+ { "n25q128a13e", 0x20ba18, 0, 64 * 1024, 256, },
+ { "n25q256a13e", 0x20ba19, 0, 64 * 1024, 512, },
+ { "n25qax3g", 0x20ba20, 0, 64 * 1024, 1024, },
+ { "n25q00aa13g", 0x20ba21, 0, 64 * 1024, 2048, },
+
+ /* Winbond */
+ { "w25x10", 0xef3011, 0, 64 * 1024, 2, },
+ { "w25x20", 0xef3012, 0, 64 * 1024, 4, },
+ { "w25x40", 0xef3013, 0, 64 * 1024, 8, },
+ { "w25p80", 0xef2014, 0, 64 * 1024, 16, },
+ { "w25x80", 0xef3014, 0, 64 * 1024, 16, },
+ { "w25p16", 0xef2015, 0, 64 * 1024, 32, },
+ { "w25x16", 0xef3015, 0, 64 * 1024, 32, },
+ { "w25x32", 0xef3016, 0, 64 * 1024, 64, },
+ { "w25q32", 0xef4016, 0, 64 * 1024, 64, },
+ { "w25x64", 0xef3017, 0, 64 * 1024, 128, },
+ { "w25q64", 0xef4017, 0, 64 * 1024, 128, },
+ { "w25q128", 0xef4018, 0, 64 * 1024, 256, FASTRD_DUAL | FASTRD_QUAD },
+
+ /* generic */
+ { "unknown", 0, 0, 64 * 1024, 256, },
+};
+
+static int ftspi020_rdid(struct spi_flash *flash, void *buf)
+{
+ uint32_t id32[2];
+ uint8_t *id = buf;
+ int i;
+
+ /* clear isr */
+ SPI_REG32(REG_ISR) = BIT(0);
+
+ /* issue command */
+ SPI_REG32(REG_CMD0) = 0;
+ SPI_REG32(REG_CMD1) = (1 << 24);
+ SPI_REG32(REG_CMD2) = sizeof(id32);
+ SPI_REG32(REG_CMD3) = (OPCODE_RDID << 24) | (flash->spi->cs << 8) | BIT(0);
+
+ for (i = 0; i < sizeof(id32) / sizeof(id32[0]); ++i) {
+ /* wait until rx ready */
+ while (!(SPI_REG32(REG_SR) & BIT(1)))
+ ;
+ id32[i] = SPI_REG32(REG_DR);
+ }
+
+ /* wait until command finish */
+ while (!(SPI_REG32(REG_ISR) & BIT(0)))
+ ;
+ SPI_REG32(REG_ISR) = BIT(0);
+
+ memcpy(id, id32, 5);
+
+ return 0;
+}
+
+static int ftspi020_rdsr(struct spi_flash *flash)
+{
+ int st;
+
+ /* clear isr */
+ SPI_REG32(REG_ISR) = BIT(0);
+
+ /* issue command */
+ SPI_REG32(REG_CMD0) = 0;
+ SPI_REG32(REG_CMD1) = (1 << 24);
+ SPI_REG32(REG_CMD2) = 1;
+ SPI_REG32(REG_CMD3) = (OPCODE_RDSR << 24) | (flash->spi->cs << 8) | BIT(0);
+
+ /* wait until rx ready */
+ while (!(SPI_REG32(REG_SR) & BIT(1)))
+ ;
+ st = SPI_REG32(REG_DR);
+
+ /* wait until command finish */
+ while (!(SPI_REG32(REG_ISR) & BIT(0)))
+ ;
+ SPI_REG32(REG_ISR) = BIT(0);
+
+ return st & 0xff;
+}
+
+/*
+ * Write status register
+ * Returns negative if error occurred.
+ */
+static int ftspi020_wrsr(struct spi_flash *flash, uint32_t val, uint8_t len)
+{
+ /* clear isr */
+ SPI_REG32(REG_ISR) = BIT(0);
+
+ /* issue command */
+ SPI_REG32(REG_CMD0) = 0;
+ SPI_REG32(REG_CMD1) = (1 << 24);
+ SPI_REG32(REG_CMD2) = len;
+ SPI_REG32(REG_CMD3) = (OPCODE_WRSR << 24) | (flash->spi->cs << 8) | BIT(1) | BIT(0);
+
+ /* wait until tx ready */
+ while (!(SPI_REG32(REG_SR) & BIT(0)))
+ ;
+ SPI_REG32(REG_DR) = cpu_to_le32(val);
+
+ /* wait until command finish */
+ while (!(SPI_REG32(REG_ISR) & BIT(0)))
+ ;
+ SPI_REG32(REG_ISR) = BIT(0);
+
+ /* wait until device ready */
+ while (ftspi020_rdsr(flash) & SR_WEL)
+ ;
+
+ return 0;
+}
+
+static int ftspi020_read(struct spi_flash *flash, u32 offset, size_t len, void *buf)
+{
+ struct spi_flash_info *fl = (struct spi_flash_info *)flash;
+ uint32_t i, v;
+
+ if (((uint32_t)buf) & 0x03) {
+ printf("ftspi020: read buffer is not 32-bits aligned!?\n");
+ return -1;
+ }
+
+ /* 1. wait until device ready */
+ while (ftspi020_rdsr(flash) & SR_WIP)
+ ;
+
+ /* 2. issue command (Rd) */
+ SPI_REG32(REG_ISR) = BIT(0);
+ SPI_REG32(REG_CMD0) = offset;
+ SPI_REG32(REG_CMD2) = len;
+
+ if (offset < 0x1000000) {
+#if CFG_USE_FASTRD
+ if (fl->fastrd_quad) {
+ SPI_REG32(REG_CMD1) = (1 << 24) | (8 << 16) | 3;
+ SPI_REG32(REG_CMD3) = (OPCODE_FAST_READ_QUAD << 24) | (flash->spi->cs << 8) | (2 << 5) | BIT(0);
+ } else if (fl->fastrd_dual) {
+ SPI_REG32(REG_CMD1) = (1 << 24) | (8 << 16) | 3;
+ SPI_REG32(REG_CMD3) = (OPCODE_FAST_READ_DUAL << 24) | (flash->spi->cs << 8) | (1 << 5) | BIT(0);
+ } else {
+ SPI_REG32(REG_CMD1) = (1 << 24) | (8 << 16) | 3;
+ SPI_REG32(REG_CMD3) = (OPCODE_FAST_READ << 24) | (flash->spi->cs << 8) | BIT(0);
+ }
+#else
+ SPI_REG32(REG_CMD1) = (1 << 24) | 3;
+ SPI_REG32(REG_CMD3) = (OPCODE_NORM_READ << 24) | (flash->spi->cs << 8) | BIT(0);
+#endif
+ } else {
+#if CFG_USE_FASTRD
+ if (fl->fastrd_quad) {
+ SPI_REG32(REG_CMD1) = (1 << 24) | (8 << 16) | 4;
+ SPI_REG32(REG_CMD3) = (OPCODE_FAST_READ4_QUAD << 24) | (flash->spi->cs << 8) | (2 << 5) | BIT(0);
+ } else if (fl->fastrd_dual) {
+ SPI_REG32(REG_CMD1) = (1 << 24) | (8 << 16) | 4;
+ SPI_REG32(REG_CMD3) = (OPCODE_FAST_READ4_DUAL << 24) | (flash->spi->cs << 8) | (1 << 5) | BIT(0);
+ } else {
+ SPI_REG32(REG_CMD1) = (1 << 24) | (8 << 16) | 4;
+ SPI_REG32(REG_CMD3) = (OPCODE_FAST_READ4 << 24) | (flash->spi->cs << 8) | BIT(0);
+ }
+#else
+ SPI_REG32(REG_CMD1) = (1 << 24) | 4;
+ SPI_REG32(REG_CMD3) = (OPCODE_NORM_READ4 << 24) | (flash->spi->cs << 8) | BIT(0);
+#endif
+ }
+
+ /* 3. data phase */
+ for (i = 0; i < (len & 0xFFFFFFFC); ) {
+ /* wait until rx ready */
+ while (!(SPI_REG32(REG_SR) & BIT(1)))
+ ;
+
+ *((uint32_t *)buf) = SPI_REG32(REG_DR);
+
+ buf = (void *)((uint32_t)buf + 4);
+ i += 4;
+ }
+
+ if (len & 0x03) {
+ /* wait until rx ready */
+ while (!(SPI_REG32(REG_SR) & BIT(1)))
+ ;
+
+ v = SPI_REG32(REG_DR);
+
+ for (i = 0; i < (len & 0x03); ++i)
+ ((uint8_t *)buf)[i] = ((uint8_t *)&v)[i];
+ }
+
+ /* 4. wait until command finish */
+ while (!(SPI_REG32(REG_ISR) & BIT(0)))
+ ;
+ SPI_REG32(REG_ISR) = BIT(0);
+
+ return 0;
+}
+
+static int ftspi020_wren(struct spi_flash *flash)
+{
+ /* issue command (WE) */
+ SPI_REG32(REG_CMD0) = 0;
+ SPI_REG32(REG_CMD1) = (1 << 24);
+ SPI_REG32(REG_CMD2) = 0;
+ SPI_REG32(REG_CMD3) = (OPCODE_WREN << 24) | (flash->spi->cs << 8) | BIT(1) | BIT(0);
+
+ /* wait until command finish */
+ while (!(SPI_REG32(REG_ISR) & BIT(0)))
+ ;
+ SPI_REG32(REG_ISR) = BIT(0);
+
+ return 0;
+}
+
+static int ftspi020_write(struct spi_flash *flash, u32 offset, size_t len, const void *buf)
+{
+ if (offset & 0x03) {
+ printf("ftspi020: offset is not 32-bits aligned!?\n");
+ return -1;
+ }
+
+ if (((uint32_t)buf) & 0x03) {
+ printf("ftspi020: write buffer is not 32-bits aligned!?\n");
+ return -1;
+ }
+
+ /* 256 bytes page write */
+ while (len > 0) {
+ int i;
+ int wl = min(256, len);
+
+#if CFG_SHOW_PROGRESS
+ /* output a '.' on each 64KB boundary */
+ if ((offset & 0x0000ffff) == 0)
+ puts(".");
+#endif
+
+ /* 1. wait until device ready */
+ while (ftspi020_rdsr(flash) & SR_WIP)
+ ;
+
+ /* 2. write enable */
+ while (!(ftspi020_rdsr(flash) & SR_WEL))
+ ftspi020_wren(flash);
+
+ /* issue command (PP) */
+ SPI_REG32(REG_CMD0) = offset;
+ SPI_REG32(REG_CMD2) = wl;
+ if (offset < 0x1000000) {
+ SPI_REG32(REG_CMD1) = (1 << 24) | 3;
+ SPI_REG32(REG_CMD3) = (OPCODE_PP << 24) | (flash->spi->cs << 8) | BIT(1) | BIT(0);
+ } else {
+ SPI_REG32(REG_CMD1) = (1 << 24) | 4;
+ SPI_REG32(REG_CMD3) = (OPCODE_PP4 << 24) | (flash->spi->cs << 8) | BIT(1) | BIT(0);
+ }
+ /* data phase */
+ for (i = 0; i < wl; i += 4) {
+ /* wait until tx ready */
+ while (!(SPI_REG32(REG_SR) & BIT(0)))
+ ;
+ SPI_REG32(REG_DR) = *(uint32_t *)buf;
+ buf = (void *)(((uint32_t)buf) + 4);
+ }
+ offset += wl;
+ len -= wl;
+
+ /* wait until command finish */
+ while (!(SPI_REG32(REG_ISR) & BIT(0)))
+ ;
+ SPI_REG32(REG_ISR) = BIT(0);
+ }
+
+ return 0;
+}
+
+static int ftspi020_erase(struct spi_flash *flash, u32 offset, size_t len)
+{
+ u32 addr = 0;
+ const struct spi_flash_param *param = flash_to_param(flash);
+
+ for (addr = offset & ~(param->sz_sector - 1); addr < offset + len; addr += param->sz_sector) {
+
+#if CFG_SHOW_PROGRESS
+ puts(".");
+#endif
+
+ /* 1. wait until device ready */
+ while (ftspi020_rdsr(flash) & SR_WIP)
+ ;
+
+ /* 2. write enable */
+ while (!(ftspi020_rdsr(flash) & SR_WEL))
+ ftspi020_wren(flash);
+
+ /* 3. sector erase */
+ /* issue command (SE) */
+ SPI_REG32(REG_CMD0) = addr;
+ SPI_REG32(REG_CMD2) = 0;
+ if (addr < 0x1000000) {
+ SPI_REG32(REG_CMD1) = (1 << 24) | 3;
+ SPI_REG32(REG_CMD3) = (OPCODE_SE << 24) | (flash->spi->cs << 8) | BIT(1) | BIT(0);
+ } else {
+ SPI_REG32(REG_CMD1) = (1 << 24) | 4;
+ SPI_REG32(REG_CMD3) = (OPCODE_SE4 << 24) | (flash->spi->cs << 8) | BIT(1) | BIT(0);
+ }
+
+ /* 4. wait until command finish */
+ while (!(SPI_REG32(REG_ISR) & BIT(0)))
+ ;
+ SPI_REG32(REG_ISR) = BIT(0);
+
+ }
+
+ return 0;
+}
+
+struct spi_flash *spi_flash_probe(uint bus, uint cs, uint max_hz, uint spi_mode)
+{
+ struct spi_slave *spi = NULL;
+ struct spi_flash_info *flash = NULL;
+ u32 i, id, ext_id, div = 8;
+ u8 idcode[5];
+
+ if (bus > 0 || cs >= 4)
+ return NULL;
+
+ spi = malloc(sizeof(struct spi_slave));
+ if (spi == NULL)
+ return NULL;
+ spi->bus = bus;
+ spi->cs = cs;
+
+ flash = malloc(sizeof(struct spi_flash_info));
+ if (flash == NULL)
+ return NULL;
+ flash->flash.spi = spi;
+ flash->flash.read = ftspi020_read;
+ flash->flash.write = ftspi020_write;
+ flash->flash.erase = ftspi020_erase;
+
+ /* reset */
+ SPI_REG32(REG_CR) = BIT(8);
+ while (SPI_REG32(REG_CR) & BIT(8))
+ ;
+
+ /* clock speed */
+ if (max_hz > 0) {
+ ulong clk = clk_get_rate("SPI");
+ for (div = 2; div < 8; div += 2) {
+ if (clk / div <= max_hz)
+ break;
+ }
+ }
+
+ /* mode + clock */
+ if (spi_mode == SPI_MODE_3)
+ SPI_REG32(REG_CR) = BIT(4) | ((div >> 1) - 1);
+ else
+ SPI_REG32(REG_CR) = ((div >> 1) - 1);
+
+ /* AC timing: trace delay, cs delay */
+ SPI_REG32(REG_ATR) = 0xFF;
+
+ printf("ftspi020: div=%d\n", div);
+
+ ftspi020_rdid((struct spi_flash *)flash, idcode);
+
+ id = (idcode[0] << 16) | (idcode[1] << 8) | (idcode[2]);
+ ext_id = (idcode[3] << 8) | (idcode[4]);
+
+ printf("ftspi020: id=%06x.%04x\n", id, ext_id);
+
+ for (i = 0; i < ARRAY_SIZE(fl_list) - 1; ++i) {
+ if (id == fl_list[i].id) {
+ if (fl_list[i].ext_id == 0 || fl_list[i].ext_id == ext_id)
+ break;
+ }
+ }
+
+ /*
+ * Atmel, SST and Intel/Numonyx serial flash tend to power
+ * up with the software protection bits set
+ */
+ ftspi020_wren((struct spi_flash *)flash);
+ ftspi020_wrsr((struct spi_flash *)flash, 0, 1);
+
+ flash->param = fl_list + i;
+ flash->flash.name = fl_list[i].name;
+ flash->flash.size = fl_list[i].sz_sector * fl_list[i].nr_sector;
+
+ if (flash->param->flags & FASTRD_QUAD) {
+ printf("ftspi020: enable fast read quad\n");
+ ftspi020_wren((struct spi_flash *)flash);
+ ftspi020_wrsr((struct spi_flash *)flash, 0x0200, 2);
+ flash->fastrd_quad = 1;
+ } else if (flash->param->flags & FASTRD_DUAL) {
+ printf("ftspi020: enable fast read dual\n");
+ flash->fastrd_dual = 1;
+ }
+
+ return (struct spi_flash *)flash;
+}
+
+void spi_flash_free(struct spi_flash *flash)
+{
+ if (flash)
+ free(flash);
+}
diff --git a/drivers/mtd/spi/ftspi020.h b/drivers/mtd/spi/ftspi020.h
new file mode 100644
index 0000000..80d98f2
--- /dev/null
+++ b/drivers/mtd/spi/ftspi020.h
@@ -0,0 +1,118 @@
+/*
+ * Faraday SPI Flash Controller
+ *
+ * (C) Copyright 2010 Faraday Technology
+ * Dante Su <dantesu@faraday-tech.com>
+ *
+ * This file is released under the terms of GPL v2 and any later version.
+ * See the file COPYING in the root directory of the source tree for details.
+ */
+
+#ifndef __FTSPI020_H
+#define __FTSPI020_H
+
+#ifndef BIT
+#define BIT(nr) (1UL << (nr))
+#endif
+
+/*
+ * FTSPI020 Registers
+ */
+#define REG_CMD0 0x00 /* Command Register */
+#define REG_CMD1 0x04
+#define REG_CMD2 0x08
+#define REG_CMD3 0x0c
+#define REG_CR 0x10 /* Control Register */
+#define REG_ATR 0x14 /* AC Timing Register */
+#define REG_SR 0x18 /* Status Register */
+#define REG_ICR 0x20 /* Interrupt Enable Register */
+#define REG_ISR 0x24 /* Interrupt Status Register */
+#define REG_RDST 0x28 /* Read Status Register */
+#define REG_REVR 0x50 /* Revision Register */
+#define REG_FEAR 0x54 /* Feature Register */
+#define REG_DR 0x100/* Data Register */
+
+/*
+ * Control Register offset 0x10
+ */
+#define CR_READY_LOC_MASK ~(0x7 << 16)
+#define CR_READY_LOC(x) (((x) & 0x7) << 16)
+#define CR_ABORT BIT(8)
+#define CR_CLK_MODE_MASK ~BIT(4)
+#define CR_CLK_MODE_0 0
+#define CR_CLK_MODE_3 BIT(4)
+#define CR_CLK_DIVIDER_MASK ~(3 << 0)
+#define CR_CLK_DIVIDER_2 (0 << 0)
+#define CR_CLK_DIVIDER_4 (1 << 0)
+#define CR_CLK_DIVIDER_6 (2 << 0)
+#define CR_CLK_DIVIDER_8 (3 << 0)
+
+/*
+ * Status Register offset 0x18
+ */
+#define SR_RFR BIT(1) /* RX FIFO ready */
+#define SR_TFR BIT(0) /* TX FIFO ready */
+
+/*
+ * Interrupt Control Register
+ */
+#define ICR_RFTH(x) (((x) & 0x3) << 12) /* RX FIFO threshold interrupt */
+#define ICR_TFTH(x) (((x) & 0x3) << 8) /* TX FIFO threshold interrupt */
+#define ICR_DMA BIT(0) /* DMA handshake enable */
+
+/*
+ * Interrupt Status Register
+ */
+#define ISR_CMD_CMPL BIT(0) /* Command complete interrupt */
+
+/*
+ * Feature Register
+ */
+#define FEAR_CLK_MODE(reg) (((reg) >> 25) & 0x1)
+#define FEAR_DTR_MODE(reg) (((reg) >> 24) & 0x1)
+#define FEAR_CMDQ_DEPTH(reg) (((reg) >> 16) & 0xff)
+#define FEAR_RXFIFO_DEPTH(reg) (((reg) >> 8) & 0xff)
+#define FEAR_TXFIFO_DEPTH(reg) (((reg) >> 0) & 0xff)
+
+/*
+ * CMD1 Register offset 4: Command Queue Second Word
+ */
+#define CMD1_CONT_READ_MODE_EN BIT(28)
+
+#define CMD1_OP_CODE_0_BYTE (0 << 24)
+#define CMD1_OP_CODE_1_BYTE (1 << 24)
+#define CMD1_OP_CODE_2_BYTE (2 << 24)
+
+#define CMD1_DUMMY_CYCLE(x) (((x) & 0xff) << 16)
+
+#define CMD1_NO_ADDR (0 << 0)
+#define CMD1_ADDR_1BYTE (1 << 0)
+#define CMD1_ADDR_2BYTE (2 << 0)
+#define CMD1_ADDR_3BYTE (3 << 0)
+#define CMD1_ADDR_4BYTE (4 << 0)
+
+/*
+ * CMD3 Register offset 0xc: Command Queue Fourth Word
+ */
+#define CMD3_INSTR_CODE(x) (((x) & 0xff) << 24)
+#define CMD3_CONT_READ_CODE(x) (((x) & 0xff) << 16)
+#define CMD3_CE(x) (((x) & 0x3) << 8)
+#define CMD3_SERIAL_MODE (0 << 5)
+#define CMD3_DUAL_MODE (1 << 5)
+#define CMD3_QUAD_MODE (2 << 5)
+#define CMD3_DUAL_IO_MODE (3 << 5)
+#define CMD3_QUAD_IO_MODE (4 << 5)
+
+#define CMD3_DTR_MODE_EN BIT(4)
+
+#define CMD3_RDST_SW BIT(3)
+#define CMD3_RDST_HW 0
+
+#define CMD3_RDST BIT(2)
+
+#define CMD3_WRITE BIT(1)
+#define CMD3_READ 0
+
+#define CMD3_CMD_COMPL_INTR BIT(0)
+
+#endif
diff --git a/drivers/mtd/spi/winbond.c b/drivers/mtd/spi/winbond.c
index 05dc644..7c354ce 100644
--- a/drivers/mtd/spi/winbond.c
+++ b/drivers/mtd/spi/winbond.c
@@ -18,6 +18,21 @@ struct winbond_spi_flash_params {

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",
@@ -99,7 +114,7 @@ struct spi_flash *spi_flash_probe_winbond(struct spi_slave *spi, u8 *idcode)
}

flash->page_size = 256;
- flash->sector_size = 4096;
+ flash->sector_size = (idcode[1] == 0x20) ? 65536 : 4096;
flash->size = 4096 * 16 * params->nr_blocks;

return flash;
--
1.7.9.5