[U-Boot] [PATCH v2 08/12] mtd: spi: add FTSPI020 SPI Flash controller support

Kuo-Jung Su
2013-04-18T09:25:35Z

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 <dantesu@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 <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 <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 <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
+
+/*
+ * 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