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