From patchwork Thu Apr 18 09:25:35 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Kuo-Jung Su X-Patchwork-Id: 237582 X-Patchwork-Delegate: albert.aribaud@free.fr Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from theia.denx.de (theia.denx.de [85.214.87.163]) by ozlabs.org (Postfix) with ESMTP id D44862C0168 for ; Thu, 18 Apr 2013 19:29:05 +1000 (EST) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id E33434A366; Thu, 18 Apr 2013 11:27:31 +0200 (CEST) X-Virus-Scanned: Debian amavisd-new at theia.denx.de Received: from theia.denx.de ([127.0.0.1]) by localhost (theia.denx.de [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id dtRQbUg6pA1Y; Thu, 18 Apr 2013 11:27:31 +0200 (CEST) Received: from theia.denx.de (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id D5C564A371; Thu, 18 Apr 2013 11:26:44 +0200 (CEST) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 9140B4A319 for ; Thu, 18 Apr 2013 11:26:09 +0200 (CEST) X-Virus-Scanned: Debian amavisd-new at theia.denx.de Received: from theia.denx.de ([127.0.0.1]) by localhost (theia.denx.de [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id zsHuO5zbQYfh for ; Thu, 18 Apr 2013 11:25:54 +0200 (CEST) X-policyd-weight: NOT_IN_SBL_XBL_SPAMHAUS=-1.5 NOT_IN_SPAMCOP=-1.5 NOT_IN_BL_NJABL=-1.5 (only DNSBL check requested) Received: from mail-da0-f49.google.com (mail-da0-f49.google.com [209.85.210.49]) by theia.denx.de (Postfix) with ESMTPS id 4C4924A34D for ; Thu, 18 Apr 2013 11:25:29 +0200 (CEST) Received: by mail-da0-f49.google.com with SMTP id t11so1249398daj.22 for ; Thu, 18 Apr 2013 02:25:27 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=x-received:from:to:cc:subject:date:message-id:x-mailer:in-reply-to :references:in-reply-to:references; bh=PP7xIUrTf6xnN2/bZOfH4Xz2XxlVfvfn0GjhLe2tv7I=; b=SjVWYAE3DKWCJUtPOMaC8NZWbpNs6eZIWTOHMbDUE7nhKh9CTjDrH60HYsJkk1Humd VQiFUEJaX+BiV/vlKoz1vja3/Q6kBeEkFfbOQPu3Tg5sRz6CElyrNW10BidRSMQzTITT AiShlmIsnrECMYIX8L7Q57U4Ayj6DEH3bZg431DEZinMds+6dlNkbvxDyqo4sLgrREjt ESARCycViSZVWlRs5gh93V1G/PWFdn+AsuceCvv9y8iKbMrx0XUCjv822WflSCBrdd32 17Lo9iOTFZL/t+TK69dNTq0hVafo8Gy/HEdSF6qkGAgg57QaUahTIlE7Pt79XJVNssH8 K0oQ== X-Received: by 10.66.144.69 with SMTP id sk5mr12834913pab.69.1366277127418; Thu, 18 Apr 2013 02:25:27 -0700 (PDT) Received: from localhost.localdomain ([220.132.37.35]) by mx.google.com with ESMTPS id xl10sm10280496pac.15.2013.04.18.02.25.25 (version=TLSv1 cipher=DES-CBC3-SHA bits=168/168); Thu, 18 Apr 2013 02:25:26 -0700 (PDT) From: Kuo-Jung Su To: u-boot@lists.denx.de Date: Thu, 18 Apr 2013 17:25:35 +0800 Message-Id: <1366277139-29728-9-git-send-email-dantesu@gmail.com> X-Mailer: git-send-email 1.7.9.5 In-Reply-To: <1366277139-29728-1-git-send-email-dantesu@gmail.com> References: <1366277139-29728-1-git-send-email-dantesu@gmail.com> In-Reply-To: <1364540788-13943-2-git-send-email-dantesu@gmail.com> References: <1364540788-13943-2-git-send-email-dantesu@gmail.com> Cc: Kuo-Jung Su Subject: [U-Boot] [PATCH v2 08/12] mtd: spi: add FTSPI020 SPI Flash controller support X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.11 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: u-boot-bounces@lists.denx.de Errors-To: u-boot-bounces@lists.denx.de From: Kuo-Jung Su 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 --- 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 + * + * 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 +#include +#include +#include +#include + +#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, ®s->isr); + + /* issue command */ + SPI_WRITE(0, ®s->cmd[0]); + SPI_WRITE(CMD1_ILEN(1), ®s->cmd[1]); + SPI_WRITE(sizeof(id32), ®s->cmd[2]); + SPI_WRITE(CMD3_OPC(OPCODE_RDID) | CMD3_CS(chip->cs) | CMD3_CMDIRQ, + ®s->cmd[3]); + + for (i = 0; i < ARRAY_SIZE(id32); ++i) { + /* wait until rx ready */ + while (!(SPI_READ(®s->sr) & SR_RFR)) + ; + id32[i] = SPI_READ(®s->dr); + } + + /* wait until command finish */ + while (!(SPI_READ(®s->isr) & ISR_CMD)) + ; + /* clear isr */ + SPI_WRITE(ISR_CMD, ®s->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, ®s->isr); + + /* issue command */ + SPI_WRITE(0, ®s->cmd[0]); + SPI_WRITE(CMD1_ILEN(1), ®s->cmd[1]); + SPI_WRITE(1, ®s->cmd[2]); + SPI_WRITE(CMD3_OPC(OPCODE_RDSR) | CMD3_CS(chip->cs) + | CMD3_CMDIRQ, ®s->cmd[3]); + + /* wait until rx ready */ + while (!(SPI_READ(®s->sr) & SR_RFR)) + ; + st = SPI_READ(®s->dr); + + /* wait until command finish */ + while (!(SPI_READ(®s->isr) & ISR_CMD)) + ; + /* clear isr */ + SPI_WRITE(ISR_CMD, ®s->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, ®s->isr); + + /* issue command */ + SPI_WRITE(0, ®s->cmd[0]); + SPI_WRITE(CMD1_ILEN(1), ®s->cmd[1]); + SPI_WRITE(len, ®s->cmd[2]); + SPI_WRITE(CMD3_OPC(OPCODE_WRSR) | CMD3_CS(chip->cs) + | CMD3_WRITE | CMD3_CMDIRQ, ®s->cmd[3]); + + /* wait until tx ready */ + while (!(SPI_READ(®s->sr) & SR_TFR)) + ; + SPI_WRITE(val, ®s->dr); + + /* wait until command finish */ + while (!(SPI_READ(®s->isr) & ISR_CMD)) + ; + + /* wait until device ready */ + while (ftspi020_rdsr(chip) & SR_WEL) + ; + /* clear isr */ + SPI_WRITE(ISR_CMD, ®s->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, ®s->isr); + SPI_WRITE(off, ®s->cmd[0]); + SPI_WRITE(len, ®s->cmd[2]); + + if (off < 0x1000000) { +#if CFG_SUPP_FASTRD + SPI_WRITE(CMD1_ILEN(1) | CMD1_DCYC(8) | CMD1_ALEN(3), + ®s->cmd[1]); + if (fl->fastrd_quad) { + SPI_WRITE(CMD3_OPC(OPCODE_FAST_READ_QUAD) + | CMD3_CS(chip->cs) | CMD3_QUAD | CMD3_CMDIRQ, + ®s->cmd[3]); + } else if (fl->fastrd_dual) { + SPI_WRITE(CMD3_OPC(OPCODE_FAST_READ_DUAL) + | CMD3_CS(chip->cs) | CMD3_DUAL | CMD3_CMDIRQ, + ®s->cmd[3]); + } else { + SPI_WRITE(CMD3_OPC(OPCODE_FAST_READ) | CMD3_CS(chip->cs) + | CMD3_CMDIRQ, ®s->cmd[3]); + } +#else + SPI_WRITE(CMD1_ILEN(1) | CMD1_ALEN(3), ®s->cmd[1]); + SPI_WRITE(CMD3_OPC(OPCODE_NORM_READ) | CMD3_CS(chip->cs) + | CMD3_CMDIRQ, ®s->cmd[3]); +#endif + } else { +#if CFG_SUPP_FASTRD + SPI_WRITE(CMD1_ILEN(1) | CMD1_DCYC(8) | CMD1_ALEN(4), + ®s->cmd[1]); + if (fl->fastrd_quad) { + SPI_WRITE(CMD3_OPC(OPCODE_FAST_READ4_QUAD) + | CMD3_CS(chip->cs) | CMD3_QUAD | CMD3_CMDIRQ, + ®s->cmd[3]); + } else if (fl->fastrd_dual) { + SPI_WRITE(CMD3_OPC(OPCODE_FAST_READ4_DUAL) + | CMD3_CS(chip->cs) | CMD3_DUAL | CMD3_CMDIRQ, + ®s->cmd[3]); + } else { + SPI_WRITE(CMD3_OPC(OPCODE_FAST_READ4) + | CMD3_CS(chip->cs) | CMD3_CMDIRQ, + ®s->cmd[3]); + } +#else + SPI_WRITE(CMD1_ILEN(1) | CMD1_ALEN(4), ®s->cmd[1]); + SPI_WRITE(CMD3_OPC(OPCODE_NORM_READ4) | CMD3_CS(chip->cs) + | CMD3_CMDIRQ, ®s->cmd[3]); +#endif + } + + /* data phase */ + for (i = 0; i < (len & 0xFFFFFFFC); ) { + /* wait until rx ready */ + while (!(SPI_READ(®s->sr) & SR_RFR)) + ; + + *((uint32_t *)buf) = SPI_READ(®s->dr); + + buf = (void *)((uint32_t)buf + 4); + i += 4; + } + + if (len & 0x03) { + /* wait until rx ready */ + while (!(SPI_READ(®s->sr) & SR_RFR)) + ; + + v = SPI_READ(®s->dr); + + for (i = 0; i < (len & 0x03); ++i) + ((uint8_t *)buf)[i] = ((uint8_t *)&v)[i]; + } + + /* wait until command finish */ + while (!(SPI_READ(®s->isr) & ISR_CMD)) + ; + /* clear isr */ + SPI_WRITE(ISR_CMD, ®s->isr); + + return 0; +} + +static int ftspi020_wren(struct ftspi020_chip *chip) +{ + struct ftspi020_regs *regs = chip->iobase; + + /* clear isr */ + SPI_WRITE(ISR_CMD, ®s->isr); + + /* issue command (WE) */ + SPI_WRITE(0, ®s->cmd[0]); + SPI_WRITE(CMD1_ILEN(1), ®s->cmd[1]); + SPI_WRITE(0, ®s->cmd[2]); + SPI_WRITE(CMD3_OPC(OPCODE_WREN) | CMD3_CS(chip->cs) + | CMD3_WRITE | CMD3_CMDIRQ, ®s->cmd[3]); + + /* wait until command finish */ + while (!(SPI_READ(®s->isr) & ISR_CMD)) + ; + /* clear isr */ + SPI_WRITE(ISR_CMD, ®s->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, ®s->cmd[0]); + SPI_WRITE(wl, ®s->cmd[2]); + if (off < 0x1000000) { + SPI_WRITE(CMD1_ILEN(1) | CMD1_ALEN(3), ®s->cmd[1]); + SPI_WRITE(CMD3_OPC(OPCODE_PP) | CMD3_CS(chip->cs) + | CMD3_WRITE | CMD3_CMDIRQ, ®s->cmd[3]); + } else { + SPI_WRITE(CMD1_ILEN(1) | CMD1_ALEN(4), ®s->cmd[1]); + SPI_WRITE(CMD3_OPC(OPCODE_PP4) | CMD3_CS(chip->cs) + | CMD3_WRITE | CMD3_CMDIRQ, ®s->cmd[3]); + } + + /* data phase */ + for (i = 0; i < wl; i += 4) { + /* wait until tx ready */ + while (!(SPI_READ(®s->sr) & SR_TFR)) + ; + SPI_WRITE(*(uint32_t *)buf, ®s->dr); + buf = (void *)(((uint32_t)buf) + 4); + } + off += wl; + len -= wl; + + /* wait until command finish */ + while (!(SPI_READ(®s->isr) & ISR_CMD)) + ; + /* clear isr */ + SPI_WRITE(ISR_CMD, ®s->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, ®s->cmd[0]); + SPI_WRITE(0x00, ®s->cmd[2]); + if (addr < 0x1000000) { + SPI_WRITE(CMD1_ILEN(1) | CMD1_ALEN(3), ®s->cmd[1]); + SPI_WRITE(CMD3_OPC(OPCODE_SE) | CMD3_CS(chip->cs) + | CMD3_WRITE | CMD3_CMDIRQ, ®s->cmd[3]); + } else { + SPI_WRITE(CMD1_ILEN(1) | CMD1_ALEN(4), ®s->cmd[1]); + SPI_WRITE(CMD3_OPC(OPCODE_SE4) | CMD3_CS(chip->cs) + | CMD3_WRITE | CMD3_CMDIRQ, ®s->cmd[3]); + } + + /* wait until command finish */ + while (!(SPI_READ(®s->isr) & ISR_CMD)) + ; + /* clear isr */ + SPI_WRITE(ISR_CMD, ®s->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, ®s->cr); + while (SPI_READ(®s->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, ®s->cr); + break; + case SPI_MODE_3: + SPI_WRITE(CR_CLK_MODE_3 | ((div >> 1) - 1), ®s->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, ®s->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 + * + * 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