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

login
register
mail settings
Submitter Kuo-Jung Su
Date March 29, 2013, 7:06 a.m.
Message ID <1364540788-13943-10-git-send-email-dantesu@gmail.com>
Download mbox | patch
Permalink /patch/232381/
State Superseded
Delegated to: Albert ARIBAUD
Headers show

Comments

Kuo-Jung Su - March 29, 2013, 7:06 a.m.
From: Kuo-Jung Su <dantesu@faraday-tech.com>

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

Patch

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;