From: Kuo-Jung Su <dant...@faraday-tech.com>

Faraday FTSPI020 is dedicated SPI bus designed for SPI Flashes.

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 |  691 ++++++++++++++++++++++++++++++++++++++++++++
 drivers/mtd/spi/ftspi020.h |  109 +++++++
 3 files changed, 804 insertions(+)
 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..3d8a62a
--- /dev/null
+++ b/drivers/mtd/spi/ftspi020.c
@@ -0,0 +1,691 @@
+/*
+ * 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 <asm/io.h>
+#include <malloc.h>
+#include <spi.h>
+#include <spi_flash.h>
+
+#include "ftspi020.h"
+
+#define CFG_SUPP_FASTRD         1
+#define CFG_SUPP_FASTRD_DUAL    1 /* Support fast read dual(2x) */
+#define CFG_SUPP_FASTRD_QUAD    0 /* Support fast read quad(4x) */
+#define CFG_SHOW_PROGRESS       1 /* print a '.' at the end of each action */
+
+/* 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 (low freq.) */
+#define OPCODE_NORM_READ4       0x13 /* Read (low freq., 4 bytes addr) */
+#define OPCODE_FAST_READ        0x0b /* Read (high freq.) */
+#define OPCODE_FAST_READ4       0x0c /* Read (high freq., 4 bytes addr) */
+#define OPCODE_FAST_READ_DUAL   0x3b /* Read (high freq.) */
+#define OPCODE_FAST_READ4_DUAL  0x3c /* Read (high freq., 4 bytes addr) */
+#define OPCODE_FAST_READ_QUAD   0x6b /* Read (high freq.) */
+#define OPCODE_FAST_READ4_QUAD  0x6c /* Read (high freq. 4 bytes addr) */
+#define OPCODE_PP               0x02 /* Page program */
+#define OPCODE_PP4              0x12 /* Page program (4 bytes addr) */
+#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 */
+#define OPCODE_SE4              0xdc /* Sector erase (4 bytes addr) */
+#define OPCODE_RDID             0x9f /* Read JEDEC ID */
+
+/* Status Register bits. */
+#define SR_WIP                  BIT_MASK(0) /* Write in progress */
+#define SR_WEL                  BIT_MASK(1) /* Write enable latch */
+
+struct ftspi020_chip;
+
+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_MASK(0) /* OPCODE_BE_4K works uniformly */
+#if CFG_SUPP_FASTRD_DUAL
+# define FASTRD_DUAL       BIT_MASK(8)
+#else
+# define FASTRD_DUAL       0
+#endif
+#if CFG_SUPP_FASTRD_QUAD
+# define FASTRD_QUAD       BIT_MASK(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;
+       struct ftspi020_chip *chip;
+       const struct spi_flash_param *param;
+
+       unsigned fastrd_dual:1;
+       unsigned fastrd_quad:1;
+};
+
+struct ftspi020_chip {
+       void *iobase;
+       uint32_t cs;
+       struct spi_slave *spi;
+       struct spi_flash_info *sfi;
+};
+
+/* Register access macros */
+#define SPI_READ(r)                    le32_to_cpu(readl(r))
+#define SPI_WRITE(v, r)                writel(cpu_to_le32(v), r)
+#define SPI_SETBITS(m, r)      setbits_le32(r, m)
+#define SPI_CLRBITS(m, r)      clrbits_le32(r, m)
+
+static inline struct ftspi020_chip *
+flash_to_chip(struct spi_flash *flash)
+{
+       struct spi_flash_info *fl = (struct spi_flash_info *)flash;
+       return fl->chip;
+}
+
+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 sf_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 ftspi020_chip *chip, void *buf)
+{
+       struct ftspi020_regs *regs = chip->iobase;
+       uint32_t id32[2];
+       uint8_t *id = buf;
+       int i;
+
+       /* clear isr */
+       SPI_WRITE(ISR_CMD, &regs->isr);
+
+       /* issue command */
+       SPI_WRITE(0, &regs->cmd[0]);
+       SPI_WRITE(CMD1_ILEN(1), &regs->cmd[1]);
+       SPI_WRITE(sizeof(id32), &regs->cmd[2]);
+       SPI_WRITE(CMD3_OPC(OPCODE_RDID) | CMD3_CS(chip->cs) | CMD3_CMDIRQ,
+               &regs->cmd[3]);
+
+       for (i = 0; i < ARRAY_SIZE(id32); ++i) {
+               /* wait until rx ready */
+               while (!(SPI_READ(&regs->sr) & SR_RFR))
+                       ;
+               id32[i] = SPI_READ(&regs->dr);
+       }
+
+       /* wait until command finish */
+       while (!(SPI_READ(&regs->isr) & ISR_CMD))
+               ;
+       /* clear isr */
+       SPI_WRITE(ISR_CMD, &regs->isr);
+
+       memcpy(id, id32, 5);
+
+       return 0;
+}
+
+static int ftspi020_rdsr(struct ftspi020_chip *chip)
+{
+       struct ftspi020_regs *regs = chip->iobase;
+       int st;
+
+       /* clear isr */
+       SPI_WRITE(ISR_CMD, &regs->isr);
+
+       /* issue command */
+       SPI_WRITE(0, &regs->cmd[0]);
+       SPI_WRITE(CMD1_ILEN(1), &regs->cmd[1]);
+       SPI_WRITE(1, &regs->cmd[2]);
+       SPI_WRITE(CMD3_OPC(OPCODE_RDSR) | CMD3_CS(chip->cs)
+               | CMD3_CMDIRQ, &regs->cmd[3]);
+
+       /* wait until rx ready */
+       while (!(SPI_READ(&regs->sr) & SR_RFR))
+               ;
+       st = SPI_READ(&regs->dr);
+
+       /* wait until command finish */
+       while (!(SPI_READ(&regs->isr) & ISR_CMD))
+               ;
+       /* clear isr */
+       SPI_WRITE(ISR_CMD, &regs->isr);
+
+       return st & 0xff;
+}
+
+/*
+ * Write status register
+ * Returns negative if error occurred.
+ */
+static int ftspi020_wrsr(struct ftspi020_chip *chip, uint32_t val, uint8_t len)
+{
+       struct ftspi020_regs *regs = chip->iobase;
+
+       /* clear isr */
+       SPI_WRITE(ISR_CMD, &regs->isr);
+
+       /* issue command */
+       SPI_WRITE(0, &regs->cmd[0]);
+       SPI_WRITE(CMD1_ILEN(1), &regs->cmd[1]);
+       SPI_WRITE(len, &regs->cmd[2]);
+       SPI_WRITE(CMD3_OPC(OPCODE_WRSR) | CMD3_CS(chip->cs)
+               | CMD3_WRITE | CMD3_CMDIRQ, &regs->cmd[3]);
+
+       /* wait until tx ready */
+       while (!(SPI_READ(&regs->sr) & SR_TFR))
+               ;
+       SPI_WRITE(val, &regs->dr);
+
+       /* wait until command finish */
+       while (!(SPI_READ(&regs->isr) & ISR_CMD))
+               ;
+
+       /* wait until device ready */
+       while (ftspi020_rdsr(chip) & SR_WEL)
+               ;
+       /* clear isr */
+       SPI_WRITE(ISR_CMD, &regs->isr);
+
+       return 0;
+}
+
+static int ftspi020_read(struct spi_flash *flash,
+       u32 off, size_t len, void *buf)
+{
+       struct ftspi020_chip *chip = flash_to_chip(flash);
+       struct ftspi020_regs *regs = chip->iobase;
+       struct spi_flash_info *fl = (struct spi_flash_info *)flash;
+       uint32_t i, v;
+
+       /* wait until device ready */
+       while (ftspi020_rdsr(chip) & SR_WIP)
+               ;
+
+       /* issue command (Rd) */
+       SPI_WRITE(ISR_CMD, &regs->isr);
+       SPI_WRITE(off, &regs->cmd[0]);
+       SPI_WRITE(len, &regs->cmd[2]);
+
+       if (off < 0x1000000) {
+#if CFG_SUPP_FASTRD
+               SPI_WRITE(CMD1_ILEN(1) | CMD1_DCYC(8) | CMD1_ALEN(3),
+                       &regs->cmd[1]);
+               if (fl->fastrd_quad) {
+                       SPI_WRITE(CMD3_OPC(OPCODE_FAST_READ_QUAD)
+                               | CMD3_CS(chip->cs) | CMD3_QUAD | CMD3_CMDIRQ,
+                               &regs->cmd[3]);
+               } else if (fl->fastrd_dual) {
+                       SPI_WRITE(CMD3_OPC(OPCODE_FAST_READ_DUAL)
+                               | CMD3_CS(chip->cs) | CMD3_DUAL | CMD3_CMDIRQ,
+                               &regs->cmd[3]);
+               } else {
+                       SPI_WRITE(CMD3_OPC(OPCODE_FAST_READ) | CMD3_CS(chip->cs)
+                               | CMD3_CMDIRQ, &regs->cmd[3]);
+               }
+#else
+               SPI_WRITE(CMD1_ILEN(1) | CMD1_ALEN(3), &regs->cmd[1]);
+               SPI_WRITE(CMD3_OPC(OPCODE_NORM_READ) | CMD3_CS(chip->cs)
+                       | CMD3_CMDIRQ, &regs->cmd[3]);
+#endif
+       } else {
+#if CFG_SUPP_FASTRD
+               SPI_WRITE(CMD1_ILEN(1) | CMD1_DCYC(8) | CMD1_ALEN(4),
+                       &regs->cmd[1]);
+               if (fl->fastrd_quad) {
+                       SPI_WRITE(CMD3_OPC(OPCODE_FAST_READ4_QUAD)
+                               | CMD3_CS(chip->cs) | CMD3_QUAD | CMD3_CMDIRQ,
+                               &regs->cmd[3]);
+               } else if (fl->fastrd_dual) {
+                       SPI_WRITE(CMD3_OPC(OPCODE_FAST_READ4_DUAL)
+                               | CMD3_CS(chip->cs) | CMD3_DUAL | CMD3_CMDIRQ,
+                               &regs->cmd[3]);
+               } else {
+                       SPI_WRITE(CMD3_OPC(OPCODE_FAST_READ4)
+                               | CMD3_CS(chip->cs) | CMD3_CMDIRQ,
+                               &regs->cmd[3]);
+               }
+#else
+               SPI_WRITE(CMD1_ILEN(1) | CMD1_ALEN(4), &regs->cmd[1]);
+               SPI_WRITE(CMD3_OPC(OPCODE_NORM_READ4) | CMD3_CS(chip->cs)
+                       | CMD3_CMDIRQ, &regs->cmd[3]);
+#endif
+       }
+
+       /* data phase */
+       for (i = 0; i < (len & 0xFFFFFFFC); ) {
+               /* wait until rx ready */
+               while (!(SPI_READ(&regs->sr) & SR_RFR))
+                       ;
+
+               *((uint32_t *)buf) = SPI_READ(&regs->dr);
+
+               buf = (void *)((uint32_t)buf + 4);
+               i += 4;
+       }
+
+       if (len & 0x03) {
+               /* wait until rx ready */
+               while (!(SPI_READ(&regs->sr) & SR_RFR))
+                       ;
+
+               v = SPI_READ(&regs->dr);
+
+               for (i = 0; i < (len & 0x03); ++i)
+                       ((uint8_t *)buf)[i] = ((uint8_t *)&v)[i];
+       }
+
+       /* wait until command finish */
+       while (!(SPI_READ(&regs->isr) & ISR_CMD))
+               ;
+       /* clear isr */
+       SPI_WRITE(ISR_CMD, &regs->isr);
+
+       return 0;
+}
+
+static int ftspi020_wren(struct ftspi020_chip *chip)
+{
+       struct ftspi020_regs *regs = chip->iobase;
+
+       /* clear isr */
+       SPI_WRITE(ISR_CMD, &regs->isr);
+
+       /* issue command (WE) */
+       SPI_WRITE(0, &regs->cmd[0]);
+       SPI_WRITE(CMD1_ILEN(1), &regs->cmd[1]);
+       SPI_WRITE(0, &regs->cmd[2]);
+       SPI_WRITE(CMD3_OPC(OPCODE_WREN) | CMD3_CS(chip->cs)
+               | CMD3_WRITE | CMD3_CMDIRQ, &regs->cmd[3]);
+
+       /* wait until command finish */
+       while (!(SPI_READ(&regs->isr) & ISR_CMD))
+               ;
+       /* clear isr */
+       SPI_WRITE(ISR_CMD, &regs->isr);
+
+       return 0;
+}
+
+static int ftspi020_write(struct spi_flash *flash,
+       u32 off, size_t len, const void *buf)
+{
+       int i, wl;
+       struct ftspi020_chip *chip = flash_to_chip(flash);
+       struct ftspi020_regs *regs = chip->iobase;
+
+       /* page write */
+       while (len > 0) {
+               wl = min(flash->page_size, len);
+
+#if CFG_SHOW_PROGRESS
+               /* output a '.' on each 64KB boundary */
+               if (!(off & 0x0000ffff))
+                       puts(".");
+#endif
+               /* wait until device ready */
+               while (ftspi020_rdsr(chip) & SR_WIP)
+                       ;
+
+               /* write enable */
+               while (!(ftspi020_rdsr(chip) & SR_WEL))
+                       ftspi020_wren(chip);
+
+               /* issue command (PP) */
+               SPI_WRITE(off, &regs->cmd[0]);
+               SPI_WRITE(wl, &regs->cmd[2]);
+               if (off < 0x1000000) {
+                       SPI_WRITE(CMD1_ILEN(1) | CMD1_ALEN(3), &regs->cmd[1]);
+                       SPI_WRITE(CMD3_OPC(OPCODE_PP) | CMD3_CS(chip->cs)
+                               | CMD3_WRITE | CMD3_CMDIRQ, &regs->cmd[3]);
+               } else {
+                       SPI_WRITE(CMD1_ILEN(1) | CMD1_ALEN(4), &regs->cmd[1]);
+                       SPI_WRITE(CMD3_OPC(OPCODE_PP4) | CMD3_CS(chip->cs)
+                               | CMD3_WRITE | CMD3_CMDIRQ, &regs->cmd[3]);
+               }
+
+               /* data phase */
+               for (i = 0; i < wl; i += 4) {
+                       /* wait until tx ready */
+                       while (!(SPI_READ(&regs->sr) & SR_TFR))
+                               ;
+                       SPI_WRITE(*(uint32_t *)buf, &regs->dr);
+                       buf = (void *)(((uint32_t)buf) + 4);
+               }
+               off += wl;
+               len -= wl;
+
+               /* wait until command finish */
+               while (!(SPI_READ(&regs->isr) & ISR_CMD))
+                       ;
+               /* clear isr */
+               SPI_WRITE(ISR_CMD, &regs->isr);
+       }
+
+       return 0;
+}
+
+static int ftspi020_erase(struct spi_flash *flash, u32 offset, size_t len)
+{
+       u32 addr = 0;
+       struct ftspi020_chip *chip = flash_to_chip(flash);
+       struct ftspi020_regs *regs = chip->iobase;
+       const struct spi_flash_param *param = flash_to_param(flash);
+
+       for (addr = offset & ~(param->sz_sector - 1);
+               addr < offset + len; addr += param->sz_sector) {
+
+               /* wait until device ready */
+               while (ftspi020_rdsr(chip) & SR_WIP)
+                       ;
+
+               /* write enable */
+               while (!(ftspi020_rdsr(chip) & SR_WEL))
+                       ftspi020_wren(chip);
+
+               /* issue command (SE) */
+               SPI_WRITE(addr, &regs->cmd[0]);
+               SPI_WRITE(0x00, &regs->cmd[2]);
+               if (addr < 0x1000000) {
+                       SPI_WRITE(CMD1_ILEN(1) | CMD1_ALEN(3), &regs->cmd[1]);
+                       SPI_WRITE(CMD3_OPC(OPCODE_SE) | CMD3_CS(chip->cs)
+                               | CMD3_WRITE | CMD3_CMDIRQ, &regs->cmd[3]);
+               } else {
+                       SPI_WRITE(CMD1_ILEN(1) | CMD1_ALEN(4), &regs->cmd[1]);
+                       SPI_WRITE(CMD3_OPC(OPCODE_SE4) | CMD3_CS(chip->cs)
+                               | CMD3_WRITE | CMD3_CMDIRQ, &regs->cmd[3]);
+               }
+
+               /* wait until command finish */
+               while (!(SPI_READ(&regs->isr) & ISR_CMD))
+                       ;
+               /* clear isr */
+               SPI_WRITE(ISR_CMD, &regs->isr);
+
+#if CFG_SHOW_PROGRESS
+               puts(".");
+#endif
+       }
+
+       return 0;
+}
+
+static struct ftspi020_chip chip_list[] = {
+       { .iobase = (void *)CONFIG_FTSPI020_BASE, },
+#ifdef CONFIG_FTSPI020_BASE1
+       { .iobase = (void *)CONFIG_FTSPI020_BASE1, },
+#endif
+#ifdef CONFIG_FTSPI020_BASE2
+       { .iobase = (void *)CONFIG_FTSPI020_BASE2, },
+#endif
+#ifdef CONFIG_FTSPI020_BASE3
+       { .iobase = (void *)CONFIG_FTSPI020_BASE3, },
+#endif
+};
+
+struct spi_flash *
+spi_flash_probe(uint bus, uint cs, uint max_hz, uint spi_mode)
+{
+       struct ftspi020_chip *chip;
+       struct ftspi020_regs *regs;
+       u32 i, id, ext_id, div = 8;
+       u8  idcode[5];
+
+       if (bus > ARRAY_SIZE(chip_list) || cs > 3)
+               return NULL;
+
+       chip = &chip_list[bus];
+       regs = chip->iobase;
+       chip->cs = cs;
+
+       chip->spi = malloc(sizeof(struct spi_slave));
+       if (!chip->spi)
+               return NULL;
+       chip->spi->bus = bus;
+       chip->spi->cs  = cs;
+
+       chip->sfi = malloc(sizeof(struct spi_flash_info));
+       if (!chip->sfi)
+               return NULL;
+       chip->sfi->chip        = chip;
+       chip->sfi->flash.spi   = chip->spi;
+       chip->sfi->flash.read  = ftspi020_read;
+       chip->sfi->flash.write = ftspi020_write;
+       chip->sfi->flash.erase = ftspi020_erase;
+
+       /* reset */
+       SPI_WRITE(CR_ABORT, &regs->cr);
+       while (SPI_READ(&regs->cr) & CR_ABORT)
+               ;
+
+       /* 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 */
+       switch (spi_mode) {
+       case SPI_MODE_0:
+               SPI_WRITE((div >> 1) - 1, &regs->cr);
+               break;
+       case SPI_MODE_3:
+               SPI_WRITE(CR_CLK_MODE_3 | ((div >> 1) - 1), &regs->cr);
+               break;
+       default:
+               printf("ftspi020: MODE%d is not supported.\n", spi_mode);
+               free(chip->spi);
+               free(chip->sfi);
+               return NULL;
+       }
+
+       /* AC timing: worst trace delay and cs delay */
+       SPI_WRITE(0xff, &regs->atr);
+
+       debug("ftspi020: div=%d\n", div);
+
+       ftspi020_rdid(chip, 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(sf_list) - 1; ++i) {
+               if (id == sf_list[i].id && (!sf_list[i].ext_id
+                       || sf_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(chip);
+       ftspi020_wrsr(chip, 0, 1);
+
+       chip->sfi->param = sf_list + i;
+       chip->sfi->flash.name = sf_list[i].name;
+       chip->sfi->flash.size = sf_list[i].sz_sector * sf_list[i].nr_sector;
+       chip->sfi->flash.page_size = 256;
+       chip->sfi->flash.sector_size = sf_list[i].sz_sector;
+       chip->sfi->flash.memory_map = NULL;
+
+       printf("ftspi020: %s (%u MB)\n",
+               chip->sfi->flash.name, chip->sfi->flash.size >> 20);
+
+       if (chip->sfi->param->flags & FASTRD_QUAD) {
+               printf("ftspi020: use fast read quad(4x)\n");
+               ftspi020_wren(chip);
+               ftspi020_wrsr(chip, 0x0200, 2);
+               chip->sfi->fastrd_quad = 1;
+       } else if (chip->sfi->param->flags & FASTRD_DUAL) {
+               printf("ftspi020: use fast read dual(2x)\n");
+               chip->sfi->fastrd_dual = 1;
+       }
+
+       return &chip->sfi->flash;
+}
+
+void spi_flash_free(struct spi_flash *flash)
+{
+       struct ftspi020_chip *chip;
+
+       if (flash) {
+               chip = flash_to_chip(flash);
+               free(chip->spi);
+               free(chip->sfi);
+       }
+}
diff --git a/drivers/mtd/spi/ftspi020.h b/drivers/mtd/spi/ftspi020.h
new file mode 100644
index 0000000..6138e9b
--- /dev/null
+++ b/drivers/mtd/spi/ftspi020.h
@@ -0,0 +1,109 @@
+/*
+ * 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
+
+/*
+ * FTSPI020 Registers
+ */
+struct ftspi020_regs {
+       /* 0x000 ~ 0x0fc */
+       uint32_t        cmd[4]; /* Command Register */
+       uint32_t        cr;             /* Control Register */
+       uint32_t        atr;    /* AC Timing Register */
+       uint32_t        sr;             /* Status Register */
+       uint32_t        rsvd0[1];
+       uint32_t        ier;    /* Interrupt Enable Register */
+       uint32_t        isr;    /* Interrupt Status Register */
+       uint32_t        rdsr;   /* Read Status Register */
+       uint32_t        rsvd1[9];
+       uint32_t        revr;   /* Revision Register */
+       uint32_t        fear;   /* Feature Register */
+       uint32_t        rsvd2[42];
+
+       /* 0x100 ~ 0x1fc */
+       uint32_t        dr;             /* 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_MASK(8)
+#define CR_CLK_MODE_0       0
+#define CR_CLK_MODE_3       BIT_MASK(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_MASK(1) /* RX FIFO Ready */
+#define SR_TFR              BIT_MASK(0) /* TX FIFO Ready */
+
+/*
+ * Interrupt Control Register
+ */
+#define ICR_RFTH(x)         (((x) & 0x3) << 12) /* RX FIFO Threshold */
+#define ICR_TFTH(x)         (((x) & 0x3) << 8)  /* TX FIFO Threshold */
+#define ICR_DMA             BIT_MASK(0) /* DMA HW Handshake Enable */
+
+/*
+ * Interrupt Status Register
+ */
+#define ISR_CMD             BIT_MASK(0) /* Command Complete/Finish  */
+
+/*
+ * 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_CREAD               BIT_MASK(28)
+#define CMD1_ILEN(x)             (((x) & 0x03) << 24)
+#define CMD1_DCYC(x)             (((x) & 0xff) << 16)
+#define CMD1_ALEN(x)             ((x) & 0x07)
+
+/*
+ * CMD3 Register offset 0xc: Command Queue Fourth Word
+ */
+#define CMD3_OPC(x)              (((x) & 0xff) << 24)
+#define CMD3_OPC_CREAD(x)        (((x) & 0xff) << 16)
+#define CMD3_CS(x)               (((x) & 0x3) << 8)
+#define CMD3_SERIAL              (0 << 5)
+#define CMD3_DUAL                (1 << 5)
+#define CMD3_QUAD                (2 << 5)
+#define CMD3_DUAL_IO             (3 << 5)
+#define CMD3_QUAD_IO             (4 << 5)
+
+#define CMD3_DTR                 BIT_MASK(4)
+
+#define CMD3_RDST_SW             BIT_MASK(3)
+#define CMD3_RDST_HW             0
+
+#define CMD3_RDST                BIT_MASK(2)
+
+#define CMD3_WRITE               BIT_MASK(1)
+#define CMD3_READ                0
+
+#define CMD3_CMDIRQ              BIT_MASK(0)
+
+#endif
-- 
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