From: Kuo-Jung Su <dant...@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 <dant...@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 <dant...@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 <dant...@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

_______________________________________________
U-Boot mailing list
U-Boot@lists.denx.de
http://lists.denx.de/mailman/listinfo/u-boot

Reply via email to