From patchwork Fri Apr 26 08:02:36 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: 239719 X-Patchwork-Delegate: scottwood@freescale.com 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 73CFE2C0112 for ; Fri, 26 Apr 2013 18:03:13 +1000 (EST) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 5BEEA4A1CA; Fri, 26 Apr 2013 10:02:57 +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 gds30JsfLtMs; Fri, 26 Apr 2013 10:02:57 +0200 (CEST) Received: from theia.denx.de (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 014BE4A123; Fri, 26 Apr 2013 10:02:23 +0200 (CEST) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id D1B0A4A0ED for ; Fri, 26 Apr 2013 10:02:14 +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 lqtbWOT2lbAg for ; Fri, 26 Apr 2013 10:02:11 +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-pd0-f178.google.com (mail-pd0-f178.google.com [209.85.192.178]) by theia.denx.de (Postfix) with ESMTPS id 98CD44A0BF for ; Fri, 26 Apr 2013 10:02:01 +0200 (CEST) Received: by mail-pd0-f178.google.com with SMTP id w11so309454pde.9 for ; Fri, 26 Apr 2013 01:02:00 -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=wchNEz9G/agBZoNsftOo53KJWL7F4+EvafJLluYcukE=; b=nVhb0zuPi5GCue+1wDORVAbWk6ndwMWH25QNJaWeNht0Vr20fOcJgullCzH6v+BGoO fRCK021UwXbeprm2Pjf3O5z8QV2F1FC/umZbFmP5cLAiLCHMmW1XKrp5Pa74Jh3VgD7g nOcvi4nwn03jjasjGhNBI/LPCctKC+AUHEkOLHy+qM5tPkOhIqHxA/bSU3Fvkq50DHuj CpJK0EzobhBY0HnBfFfllEMapj4obK03+OYI9ar9AWUtTinlw9Kvr5M8tfyxSli+pBDx pke0wU6Hg4Zx4pXEwr5IGDrWP561Nkz2ucKzzLNd1PKlN1obL2sBgQj1SG+lraI1InRH NYcw== X-Received: by 10.66.88.38 with SMTP id bd6mr21215791pab.184.1366963320210; Fri, 26 Apr 2013 01:02:00 -0700 (PDT) Received: from localhost.localdomain ([220.132.37.35]) by mx.google.com with ESMTPSA id lo7sm11690140pab.19.2013.04.26.01.01.58 for (version=TLSv1 cipher=DES-CBC3-SHA bits=168/168); Fri, 26 Apr 2013 01:01:59 -0700 (PDT) From: Kuo-Jung Su To: u-boot@lists.denx.de Date: Fri, 26 Apr 2013 16:02:36 +0800 Message-Id: <1366963360-2987-8-git-send-email-dantesu@gmail.com> X-Mailer: git-send-email 1.7.9.5 In-Reply-To: <1366963360-2987-1-git-send-email-dantesu@gmail.com> References: <1366963360-2987-1-git-send-email-dantesu@gmail.com> In-Reply-To: <1366277139-29728-2-git-send-email-dantesu@gmail.com> References: <1366277139-29728-2-git-send-email-dantesu@gmail.com> Cc: Scott Wood , Kuo-Jung Su Subject: [U-Boot] [PATCH v3 07/11] mtd: nand: add Faraday FTNANDC021 NAND 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 FTNANDC021 is a integrated NAND flash controller. It use a build-in command table to abstract the underlying NAND flash control logic. For example: Issuing a command 0x10 to FTNANDC021 would result in a page write + a read status operation. Signed-off-by: Kuo-Jung Su CC: Scott Wood --- README | 7 + drivers/mtd/nand/Makefile | 1 + drivers/mtd/nand/ftnandc021.c | 724 +++++++++++++++++++++++++++++++++++++++++ drivers/mtd/nand/ftnandc021.h | 137 ++++++++ include/faraday/nand.h | 34 ++ 5 files changed, 903 insertions(+) create mode 100644 drivers/mtd/nand/ftnandc021.c create mode 100644 drivers/mtd/nand/ftnandc021.h create mode 100644 include/faraday/nand.h diff --git a/README b/README index 862bb3e..adc198f 100644 --- a/README +++ b/README @@ -3872,6 +3872,13 @@ Low Level (hardware related) configuration options: - drivers/mtd/nand/ndfc.c - drivers/mtd/nand/mxc_nand.c +- CONFIG_SYS_NAND_TIMING + Defined to tell the NAND controller that the NAND chip is using + a customized timing parameters. + Not all NAND drivers use this symbol. + Example of drivers that use it: + - drivers/mtd/nand/ftnandc021.c + - CONFIG_SYS_NDFC_EBC0_CFG Sets the EBC0_CFG register for the NDFC. If not defined a default value will be used. diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index 35769c5..f6f89f0 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -63,6 +63,7 @@ COBJS-$(CONFIG_NAND_FSL_ELBC) += fsl_elbc_nand.o COBJS-$(CONFIG_NAND_FSL_IFC) += fsl_ifc_nand.o COBJS-$(CONFIG_NAND_FSL_UPM) += fsl_upm.o COBJS-$(CONFIG_NAND_FSMC) += fsmc_nand.o +COBJS-$(CONFIG_NAND_FTNANDC021) += ftnandc021.o COBJS-$(CONFIG_NAND_JZ4740) += jz4740_nand.o COBJS-$(CONFIG_NAND_KB9202) += kb9202_nand.o COBJS-$(CONFIG_NAND_KIRKWOOD) += kirkwood_nand.o diff --git a/drivers/mtd/nand/ftnandc021.c b/drivers/mtd/nand/ftnandc021.c new file mode 100644 index 0000000..39c181f --- /dev/null +++ b/drivers/mtd/nand/ftnandc021.c @@ -0,0 +1,724 @@ +/* + * Faraday NAND 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 +#include + +#include "ftnandc021.h" + +#define CFG_HWECC /* Enable hardware ECC */ + +struct ftnandc021_chip { + void __iomem *regs; + int col; /* current column address */ + int row; /* current row address/page index */ + int cmd; /* current NAND command code */ + int cmd_hc; /* current FTNANDC021 command code */ + + struct { + int idx; + int len; + u8 dat[NAND_MAX_OOBSIZE]; + } buf; +}; + +static struct nand_ecclayout ftnandc021_ecclayout[] = { + { /* page size = 512 (oob size = 16) */ + .eccbytes = 6, + .eccpos = { 0, 1, 2, 3, 6, 7 }, + .oobfree = { +#ifdef CFG_HWECC + { 9, 3 }, +#else + { 8, 4 }, +#endif + } + }, + { /* page size = 2048 (oob size = 64) */ + .eccbytes = 24, + .eccpos = { + 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, + 56, 57, 58, 59, 60, 61, 62, 63 + }, + .oobfree = { +#ifdef CFG_HWECC + { 9, 3 }, +#else + { 8, 4 }, +#endif + }, + }, + { /* page size = 4096 (oob size = 128) */ + .eccbytes = 48, + .eccpos = { + 80, 81, 82, 83, 84, 85, 86, 87, + 88, 89, 90, 91, 92, 93, 94, 95, + 96, 97, 98, 99, 100, 101, 102, 103, + 104, 105, 106, 107, 108, 109, 110, 111, + 112, 113, 114, 115, 116, 117, 118, 119, + 120, 121, 122, 123, 124, 125, 126, 127 + }, + .oobfree = { +#ifdef CFG_HWECC + { 9, 7 }, +#else + { 8, 8 }, +#endif + }, + }, +}; + +static inline int ftnandc021_ckst(struct ftnandc021_chip *priv) +{ + struct ftnandc021_regs __iomem *regs = priv->regs; + uint32_t st = readl(®s->idr[1]); + + if (st & NAND_STATUS_FAIL) + return -EIO; + + if (!(st & NAND_STATUS_READY)) + return -EBUSY; + + if (!(st & NAND_STATUS_WP)) + return -EIO; + + return 0; +} + +static inline int ftnandc021_wait(struct ftnandc021_chip *priv) +{ + struct ftnandc021_regs __iomem *regs = priv->regs; + char rc = 'c'; + ulong ts; + + for (ts = get_timer(0); get_timer(ts) < 200; ) { + if (readl(®s->sr) & SR_ECC) { + rc = 'e'; + break; + } + if (!(readl(®s->acr) & ACR_START)) { + rc = 0; + break; + } + } + + switch (rc) { + case 'e': + printf("ftnandc021: ecc timeout, cmd_hc=%d\n", + priv->cmd_hc); + break; + case 'c': + printf("ftnandc021: cmd timeout, cmd_hc=%d\n", + priv->cmd_hc); + break; + default: + break; + } + + return rc ? -ETIMEDOUT : 0; +} + +#ifdef CFG_HWECC + +static int ftnandc021_reset(struct nand_chip *chip); +static int ftnandc021_command(struct ftnandc021_chip *priv, uint32_t cmd); +static void ftnandc021_write_oob(struct mtd_info *mtd, + const uint8_t *buf, int len); + +static void ftnandc021_ecc_hwctl(struct mtd_info *mtd, int mode) +{ + /* nothing needs to be done */ +} + +static int ftnandc021_ecc_calculate(struct mtd_info *mtd, const uint8_t *dat, + uint8_t *ecc_code) +{ + return 0; +} + +static int ftnandc021_ecc_correct(struct mtd_info *mtd, uint8_t *dat, + uint8_t *read_ecc, uint8_t *calc_ecc) +{ + struct nand_chip *chip = mtd->priv; + struct faraday_nand_chip *info = chip->priv; + struct ftnandc021_chip *priv = info->priv; + struct ftnandc021_regs __iomem *regs = priv->regs; + uint32_t st = readl(®s->ecc_sr); + int ret = 0; + + if (st & ECC_SR_CERR) { + printf("ftnandc021: ecc corection error\n"); + ret = -EIO; + } else if (st & ECC_SR_ERR) { + printf("ftnandc021: ecc error\n"); + ret = -EIO; + } + + return ret; +} + +static int ftnandc021_read_page_raw(struct mtd_info *mtd, + struct nand_chip *chip, uint8_t *buf, int page) +{ + printf("ftnandc021: read_page_raw is not supported\n"); + return -EIO; +} + +static void ftnandc021_write_page_raw(struct mtd_info *mtd, + struct nand_chip *chip, const uint8_t *buf) +{ + printf("ftnandc021: write_page_raw is not supported\n"); +} + +#endif /* #ifdef CFG_HWECC */ + +static int ftnandc021_command(struct ftnandc021_chip *priv, uint32_t cmd) +{ + struct ftnandc021_regs __iomem *regs = priv->regs; + int ret = 0; + + priv->cmd_hc = cmd; + + writel(ACR_START | ACR_CMD(cmd), ®s->acr); + + /* + * pgread : (We have queued data at the IO port) + * pgwrite : (We have queued data at the IO port) + * bkerase : nand_wait + nand_ckst + * oobwr : nand_wait + nand_ckst + * otherwise : nand_wait + */ + switch (cmd) { + case FTNANDC021_CMD_RDPG: + case FTNANDC021_CMD_WRPG: + break; + case FTNANDC021_CMD_ERBLK: + case FTNANDC021_CMD_WROOB: + ret = ftnandc021_wait(priv) || ftnandc021_ckst(priv); + break; + default: + ret = ftnandc021_wait(priv); + } + + return ret; +} + +static int ftnandc021_reset(struct nand_chip *chip) +{ + struct faraday_nand_chip *info = chip->priv; + struct ftnandc021_chip *priv = info->priv; + struct ftnandc021_regs __iomem *regs = priv->regs; + uint32_t ts, bk, pg, ac, mask; +#ifdef CONFIG_SYS_NAND_TIMING + uint32_t timing[] = CONFIG_SYS_NAND_TIMING; + + writel(timing[0], ®s->atr[0]); + writel(timing[1], ®s->atr[1]); +#endif + + writel(0, ®s->ier); + writel(0, ®s->pir); + writel(0xff, ®s->bbiwr); + writel(0xffffffff, ®s->lsnwr); + writel(0xffffffff, ®s->crcwr); + + if (chip->options & NAND_BUSWIDTH_16) + writel(FCR_SWCRC | FCR_IGNCRC | FCR_16BIT, ®s->fcr); + else + writel(FCR_SWCRC | FCR_IGNCRC, ®s->fcr); + + /* chip reset */ + mask = SRR_CHIP_RESET; +#ifdef CFG_HWECC + mask |= SRR_ECC_EN; +#endif + writel(mask, ®s->srr); + for (ts = get_timer(0); get_timer(ts) < 500; ) { + if (readl(®s->srr) & SRR_CHIP_RESET) + continue; + break; + } + if (readl(®s->srr) & SRR_CHIP_RESET) { + printf("ftnandc021: reset failed\n"); + return -ENXIO; + } + + /* sanity check on page size */ + if (info->pgsz != 512 && info->pgsz != 2048 && info->pgsz != 4096) { + printf("ftnandc021: invalid page size=%d\n", info->pgsz); + return -EINVAL; + } + + bk = ffs(info->bksz / info->pgsz) - 5; + pg = (info->pgsz < 2048) ? 0 : (ffs(info->pgsz) - 11); + ac = info->alen - 3; + + writel(MCR_ME(0) | MCR_32GB | (bk << 16) | (pg << 8) | (ac << 10), + ®s->mcr); + + /* IO mode = PIO */ + writel(0, ®s->bcr); + + /* ECC mode */ + chip->ecc.layout = ftnandc021_ecclayout + pg; +#ifdef CFG_HWECC + chip->ecc.bytes = chip->ecc.layout->eccbytes; + chip->ecc.size = info->pgsz; + chip->ecc.steps = 1; + chip->ecc.hwctl = ftnandc021_ecc_hwctl; + chip->ecc.calculate = ftnandc021_ecc_calculate; + chip->ecc.correct = ftnandc021_ecc_correct; + chip->ecc.read_page_raw = ftnandc021_read_page_raw; + chip->ecc.write_page_raw = ftnandc021_write_page_raw; + chip->ecc.mode = NAND_ECC_HW; +#else + chip->ecc.mode = NAND_ECC_NONE; +#endif + + /* reset the attached flash */ + if (ftnandc021_command(priv, FTNANDC021_CMD_RESET)) + return -ENXIO; + + return 0; +} + +/* + * Check hardware register for wait status. Returns 1 if device is ready, + * 0 if it is still busy. + */ +static int ftnandc021_dev_ready(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd->priv; + struct faraday_nand_chip *info = chip->priv; + struct ftnandc021_chip *priv = info->priv; + struct ftnandc021_regs __iomem *regs = priv->regs; + int ret = 1; + + if (ftnandc021_wait(priv)) + ret = 0; + else if (!(readl(®s->sr) & SR_READY)) + ret = 0; + + return ret; +} + +static int ftnandc021_pio_wait(struct ftnandc021_chip *priv) +{ + struct ftnandc021_regs __iomem *regs = priv->regs; + int ret = -ETIMEDOUT; + uint32_t ts; + + for (ts = get_timer(0); get_timer(ts) < 200; ) { + if (!(readl(®s->ior) & IOR_READY)) + continue; + ret = 0; + break; + } + + if (ret) + printf("ftnandc021: pio timeout\n"); + + return ret; +} + +static void ftnandc021_read_oob(struct mtd_info *mtd, uint8_t *buf, int len) +{ + struct nand_chip *chip = mtd->priv; + struct faraday_nand_chip *info = chip->priv; + struct ftnandc021_chip *priv = info->priv; + struct ftnandc021_regs __iomem *regs = priv->regs; + uint32_t tmp; + + memset(buf, 0xff, len); + + /* bad block */ + buf[chip->badblockpos] = readl(®s->bbird) & 0xff; + + /* data */ + tmp = readl(®s->crcrd); + buf[8] = (tmp >> 0) & 0xff; + buf[9] = (tmp >> 8) & 0xff; + if (mtd->writesize >= 4096) { + buf[12] = (tmp >> 16) & 0xff; + buf[13] = (tmp >> 24) & 0xff; + } + + tmp = readl(®s->lsnrd); + buf[10] = (tmp >> 0) & 0xff; + buf[11] = (tmp >> 8) & 0xff; + if (mtd->writesize >= 4096) { + buf[14] = (tmp >> 16) & 0xff; + buf[15] = (tmp >> 24) & 0xff; + } +} + +static void ftnandc021_write_oob(struct mtd_info *mtd, + const uint8_t *buf, int len) +{ + struct nand_chip *chip = mtd->priv; + struct faraday_nand_chip *info = chip->priv; + struct ftnandc021_chip *priv = info->priv; + struct ftnandc021_regs __iomem *regs = priv->regs; + uint32_t tmp; + + /* bad block */ + tmp = buf[chip->badblockpos]; + writel(tmp, ®s->bbiwr); + + /* data */ +#ifdef CFG_HWECC + /* mark it as 'not blank' */ + tmp = 'W'; +#else + tmp = buf[8]; +#endif + tmp |= buf[9] << 8; + if (mtd->writesize >= 4096) + tmp |= (buf[12] << 16) | (buf[13] << 24); + writel(tmp, ®s->crcwr); + + tmp = buf[10] | (buf[11] << 8); + if (mtd->writesize >= 4096) + tmp |= (buf[14] << 16) | (buf[15] << 24); + writel(tmp, ®s->lsnwr); +} + +static uint8_t ftnandc021_read_byte(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd->priv; + struct faraday_nand_chip *info = chip->priv; + struct ftnandc021_chip *priv = info->priv; + struct ftnandc021_regs __iomem *regs = priv->regs; + uint8_t ret = 0xff; + + switch (priv->cmd_hc) { + case FTNANDC021_CMD_RDID: + case FTNANDC021_CMD_RDOOB: + if (priv->buf.idx >= priv->buf.len) + break; + ret = priv->buf.dat[priv->buf.idx]; + priv->buf.idx += 1; + break; + case FTNANDC021_CMD_RDST: + ret = (uint8_t)(readl(®s->idr[1]) & 0xff); + break; + default: + printf("ftnandc021: unknown cmd=0x%x in read_byte\n", + priv->cmd_hc); + break; + } + + return ret; +} + +static uint16_t ftnandc021_read_word(struct mtd_info *mtd) +{ + uint16_t ret = 0xffff; + uint8_t *buf = (uint8_t *)&ret; + + /* LSB format */ + buf[0] = ftnandc021_read_byte(mtd); + buf[1] = ftnandc021_read_byte(mtd); + + return ret; +} + +static int ftnandc021_copy_oob(struct mtd_info *mtd, int off, + uint8_t *buf, int len) +{ + struct nand_chip *chip = mtd->priv; + struct faraday_nand_chip *info = chip->priv; + struct ftnandc021_chip *priv = info->priv; + int pos; + + pos = min(off, mtd->oobsize); + len = min(mtd->oobsize - pos, len); + memcpy(buf, priv->buf.dat + pos, len); + + return len; +} + +/** + * Read data from NAND controller into buffer + * @mtd: MTD device structure + * @buf: buffer to store date + * @len: number of bytes to read + */ +static void ftnandc021_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) +{ + struct nand_chip *chip = mtd->priv; + struct faraday_nand_chip *info = chip->priv; + struct ftnandc021_chip *priv = info->priv; + struct ftnandc021_regs __iomem *regs = priv->regs; + int off; + + if (priv->col >= mtd->writesize) { + len = ftnandc021_copy_oob(mtd, + priv->col - mtd->writesize, buf, len); + priv->col += len; + if (priv->cmd == NAND_CMD_READOOB) + priv->buf.idx += len; + return; + } + + if (priv->cmd == NAND_CMD_READOOB) + BUG(); /* should never happen */ + +#ifdef CFG_HWECC + /* skip if it's a blank page */ + if (priv->buf.dat[8] != 'W') { + memset(buf, 0xff, len); + return; + } +#endif + + off = 0; + while (off < len && priv->col < mtd->writesize) { + ftnandc021_pio_wait(priv); + *(uint32_t *)(buf + off) = readl(®s->dr); + priv->col += 4; + off += 4; + } + + if (!ftnandc021_wait(priv) + && off < len && priv->col >= mtd->writesize) { + len = ftnandc021_copy_oob(mtd, + priv->col - mtd->writesize, buf + off, len - off); + off += len; + priv->col += len; + } +} + +/** + * Write buffer to NAND controller + * @mtd: MTD device structure + * @buf: data buffer + * @len: number of bytes to write + */ +static void ftnandc021_write_buf( + struct mtd_info *mtd, const uint8_t *buf, int len) +{ + struct nand_chip *chip = mtd->priv; + struct faraday_nand_chip *info = chip->priv; + struct ftnandc021_chip *priv = info->priv; + struct ftnandc021_regs __iomem *regs = priv->regs; + int off; + + /* + * FTNANDC021 HW design issues: + * + * 1. OOB data must be set before issuing write command, + * so it's too late to do it right here + * 2. Only after command issued, the data register + * could accept data. + */ + if (priv->col >= mtd->writesize) + return; + + for (off = 0; off < len && priv->col < mtd->writesize; ) { + ftnandc021_pio_wait(priv); + writel(*(uint32_t *)(buf + off), ®s->dr); + priv->col += 4; + off += 4; + } + + ftnandc021_wait(priv); +} + +/** + * Verify chip data against buffer + * @mtd: MTD device structure + * @buf: buffer containing the data to compare + * @len: number of bytes to compare + */ +static int ftnandc021_verify_buf(struct mtd_info *mtd, + const uint8_t *buf, int len) +{ + int ret = 0; + uint8_t *tmp; + + len = min_t(int, len, mtd->writesize); + tmp = malloc(mtd->writesize); + + if (!tmp) { + printf("ftnandc021: out of memory\n"); + return -ENOMEM; + } else { + ftnandc021_read_buf(mtd, tmp, len); + if (memcmp(tmp, buf, len)) + ret = -EINVAL; + } + + free(tmp); + return ret; +} + +static void ftnandc021_cmdfunc(struct mtd_info *mtd, + unsigned cmd, int col, int row) +{ + struct nand_chip *chip = mtd->priv; + struct faraday_nand_chip *info = chip->priv; + struct ftnandc021_chip *priv = info->priv; + struct ftnandc021_regs __iomem *regs = priv->regs; + + priv->cmd = cmd; + priv->col = col; + priv->row = row; + + switch (cmd) { + case NAND_CMD_READID: /* 0x90 */ + if (ftnandc021_command(priv, FTNANDC021_CMD_RDID)) { + printf("ftnandc021: RDID failed.\n"); + break; + } + priv->buf.idx = 0; + priv->buf.len = 8; + put_unaligned_le32(readl(®s->idr[0]), + priv->buf.dat); + put_unaligned_le32(readl(®s->idr[1]), + priv->buf.dat + 4); + break; + + case NAND_CMD_READOOB: /* 0x50 */ + writel(row, ®s->pir); + writel(1, ®s->pcr); + if (ftnandc021_command(priv, FTNANDC021_CMD_RDOOB)) { + printf("ftnandc021: RDOOB failed.\n"); + break; + } + if (mtd->oobsize > NAND_MAX_OOBSIZE) + BUG(); /* should never happen */ + ftnandc021_read_oob(mtd, priv->buf.dat, mtd->oobsize); + priv->buf.idx = 0; + priv->buf.len = mtd->oobsize; + priv->col = mtd->writesize; + break; + + case NAND_CMD_READ0: /* 0x00 */ + writel(row, ®s->pir); + writel(1, ®s->pcr); +#ifdef CFG_HWECC + if (ftnandc021_command(priv, FTNANDC021_CMD_RDOOB)) { + printf("ftnandc021: RDOOB failed.\n"); + break; + } + ftnandc021_read_oob(mtd, priv->buf.dat, mtd->oobsize); + /* cancel if it's a blank page */ + if (priv->buf.dat[8] != 'W') { + debug("ftnandc021: skip page %d\n", row); + break; + } +#endif + if (ftnandc021_command(priv, FTNANDC021_CMD_RDPG)) + printf("ftnandc021: RDPG failed.\n"); + break; + + case NAND_CMD_ERASE1: /* 0x60 */ + writel(row, ®s->pir); + writel(1, ®s->pcr); + break; + + case NAND_CMD_ERASE2: /* 0xD0 */ + if (ftnandc021_command(priv, FTNANDC021_CMD_ERBLK)) + printf("ftnandc021: ERBLK failed\n"); + break; + + case NAND_CMD_STATUS: /* 0x70 */ + if (ftnandc021_command(priv, FTNANDC021_CMD_RDST)) + printf("ftnandc021: RDST failed\n"); + break; + + case NAND_CMD_SEQIN: /* 0x80 (Write Stage 1.) */ + writel(row, ®s->pir); + writel(1, ®s->pcr); + /* OOB data must be set before issuing command */ + ftnandc021_write_oob(mtd, chip->oob_poi, mtd->oobsize); + if (priv->col >= mtd->writesize) { + if (ftnandc021_command(priv, FTNANDC021_CMD_WROOB)) + printf("ftnandc021: WROOB failed\n"); + } else { + if (ftnandc021_command(priv, FTNANDC021_CMD_WRPG)) + printf("ftnandc021: WRPG failed\n"); + } + break; + + case NAND_CMD_PAGEPROG: /* 0x10 (Write Stage 2.) */ + /* nothing needs to be done */ + break; + + case NAND_CMD_RESET: /* 0xFF */ + if (ftnandc021_command(priv, FTNANDC021_CMD_RESET)) + printf("ftnandc021: RESET failed.\n"); + break; + + default: + printf("ftnandc021: unknown cmd=0x%x\n", cmd); + } +} + +/** + * hardware specific access to control-lines + * @mtd: MTD device structure + * @cmd: command to device + * @ctrl: + * NAND_NCE: bit 0 -> don't care + * NAND_CLE: bit 1 -> Command Latch + * NAND_ALE: bit 2 -> Address Latch + * + * NOTE: boards may use different bits for these!! + */ +static void ftnandc021_hwcontrol(struct mtd_info *mtd, + int cmd, unsigned int ctrl) +{ + /* nothing needs to be done */ +} + +int ftnandc021_init(struct nand_chip *chip) +{ + struct faraday_nand_chip *info; + struct ftnandc021_chip *priv; + + info = chip->priv; + if (!info) + return -EINVAL; + + priv = calloc(1, sizeof(struct ftnandc021_chip)); + if (!priv) + return -ENOMEM; + info->priv = priv; + priv->regs = info->regs; + + debug("ftnandc021: pg=%dK, bk=%dK, alen=%d\n", + info->pgsz >> 10, info->bksz >> 10, info->alen); + + /* hardware reset */ + if (ftnandc021_reset(chip)) + return -EINVAL; + + /* hwcontrol always must be implemented */ + chip->cmd_ctrl = ftnandc021_hwcontrol; + chip->cmdfunc = ftnandc021_cmdfunc; + chip->dev_ready = ftnandc021_dev_ready; + chip->chip_delay = 0; + + chip->read_byte = ftnandc021_read_byte; + chip->read_word = ftnandc021_read_word; + chip->read_buf = ftnandc021_read_buf; + chip->write_buf = ftnandc021_write_buf; + chip->verify_buf = ftnandc021_verify_buf; + + return 0; +} diff --git a/drivers/mtd/nand/ftnandc021.h b/drivers/mtd/nand/ftnandc021.h new file mode 100644 index 0000000..eb75d73 --- /dev/null +++ b/drivers/mtd/nand/ftnandc021.h @@ -0,0 +1,137 @@ +/* + * Faraday NAND 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 _FTNANDC021_H +#define _FTNANDC021_H + +/* NANDC control registers */ +struct ftnandc021_regs { + /* 0x000 ~ 0x0fc */ + uint32_t ecc_pr[4];/* ECC Parity Register */ + uint32_t ecc_sr; /* ECC Status Register */ + uint32_t rsvd0[59]; + + /* 0x100 ~ 0x1fc */ + uint32_t sr; /* Status Register */ + uint32_t acr; /* Access Control Register */ + uint32_t fcr; /* Flow Control Register */ + uint32_t pir; /* Page Index Register */ + uint32_t mcr; /* Memory Configuration Register */ + uint32_t atr[2]; /* AC Timing Register */ + uint32_t rsvd1[1]; + uint32_t idr[2]; /* Device ID Register */ + uint32_t ier; /* Interrupt Enable Register */ + uint32_t iscr; /* Interrupt Status Clear Register */ + uint32_t rsvd2[4]; + uint32_t bbiwr; /* Bad Block Info Write */ + uint32_t lsn; /* LSN Initialize */ + uint32_t crcwr; /* LSN CRC Write */ + uint32_t lsnwr; /* LSN Write */ + uint32_t bbird; /* Bad Block Info Read */ + uint32_t lsnrd; /* LSN Read */ + uint32_t crcrd; /* CRC Read */ + uint32_t rsvd3[41]; + + /* 0x200 ~ 0x2fc */ + uint32_t rsvd4[1]; + uint32_t icr; /* BMC Interrupt Control Register */ + uint32_t ior; /* BMC PIO Status Register */ + uint32_t bcr; /* BMC Burst Control Register */ + uint32_t rsvd5[60]; + + /* 0x300 ~ 0x3fc */ + uint32_t dr; /* MLC Data Register */ + uint32_t isr; /* MLC Interrupt Status Register */ + uint32_t pcr; /* Page Count Register */ + uint32_t srr; /* MLC Software Reset Register */ + uint32_t rsvd7[58]; + uint32_t revr; /* Revision Register */ + uint32_t cfgr; /* Configuration Register */ +}; + +/* bit mask */ +#define ECC_SR_CERR BIT_MASK(3) /* correction error */ +#define ECC_SR_ERR BIT_MASK(2) /* ecc error */ +#define ECC_SR_DEC BIT_MASK(1) /* ecc decode finished */ +#define ECC_SR_ENC BIT_MASK(0) /* ecc encode finished */ + +#define SR_BLANK BIT_MASK(7) /* blanking check failed */ +#define SR_ECC BIT_MASK(6) /* ecc timeout */ +#define SR_STS BIT_MASK(4) /* status error */ +#define SR_CRC BIT_MASK(3) /* crc error */ +#define SR_CMD BIT_MASK(2) /* command finished */ +#define SR_READY BIT_MASK(1) /* chip ready/busy */ +#define SR_ENA BIT_MASK(0) /* chip enabled */ + +#define ACR_CMD(x) (((x) & 0x1f) << 8) /* command code */ +#define ACR_START BIT_MASK(7) /* command start */ + +#define FCR_SWCRC BIT_MASK(8) /* CRC controlled by Software */ +#define FCR_IGNCRC BIT_MASK(7) /* Bypass/Ignore CRC checking */ +#define FCR_16BIT BIT_MASK(4) /* 16 bit data bus */ +#define FCR_WPROT BIT_MASK(3) /* write protected */ +#define FCR_NOSC BIT_MASK(2) /* bypass status check error */ +#define FCR_MICRON BIT_MASK(1) /* Micron 2-plane command */ +#define FCR_NOBC BIT_MASK(0) /* skip blanking check error */ + +#define IER_ENA BIT_MASK(7) /* interrupt enabled */ +#define IER_ECC BIT_MASK(3) /* ecc error timeout */ +#define IER_STS BIT_MASK(2) /* status error */ +#define IER_CRC BIT_MASK(1) /* crc error */ +#define IER_CMD BIT_MASK(0) /* command finished */ + +#define IOR_READY BIT_MASK(0) /* PIO ready */ + +#define SRR_ECC_EN BIT_MASK(8) /* ECC enabled */ +#define SRR_NANDC_RESET BIT_MASK(2) /* NANDC reset */ +#define SRR_BMC_RESET BIT_MASK(1) /* BMC reset */ +#define SRR_ECC_RESET BIT_MASK(0) /* ECC reset */ +#define SRR_CHIP_RESET (SRR_NANDC_RESET | SRR_BMC_RESET | SRR_ECC_RESET) + +#define MCR_BS16P (0 << 16) /* page count per block */ +#define MCR_BS32P (1 << 16) +#define MCR_BS64P (2 << 16) +#define MCR_BS128P (3 << 16) +#define MCR_1PLANE (0 << 14) /* memory architecture */ +#define MCR_2PLANE (1 << 14) +#define MCR_SERIAL (0 << 12) /* interleaving: off, 2 flash, 4 flash */ +#define MCR_IL2 (1 << 12) +#define MCR_IL4 (2 << 12) +#define MCR_ALEN3 (0 << 10) /* address length */ +#define MCR_ALEN4 (1 << 10) +#define MCR_ALEN5 (2 << 10) +#define MCR_PS512 (0 << 8) /* size per page (bytes) */ +#define MCR_PS2048 (1 << 8) +#define MCR_PS4096 (2 << 8) +#define MCR_16MB (0 << 4) /* flash size */ +#define MCR_32MB (1 << 4) +#define MCR_64MB (2 << 4) +#define MCR_128MB (3 << 4) +#define MCR_256MB (4 << 4) +#define MCR_512MB (5 << 4) +#define MCR_1GB (6 << 4) +#define MCR_2GB (7 << 4) +#define MCR_4GB (8 << 4) +#define MCR_8GB (9 << 4) +#define MCR_16GB (10 << 4) +#define MCR_32GB (11 << 4) +#define MCR_ME(n) (1 << (n)) /* module enable, 0 <= n <= 3 */ + +/* FTNANDC021 integrated command set */ +#define FTNANDC021_CMD_RDID 0x01 /* read id */ +#define FTNANDC021_CMD_RESET 0x02 /* reset flash */ +#define FTNANDC021_CMD_RDST 0x04 /* read status */ +#define FTNANDC021_CMD_RDPG 0x05 /* read page (data + oob) */ +#define FTNANDC021_CMD_RDOOB 0x06 /* read oob */ +#define FTNANDC021_CMD_WRPG 0x10 /* write page (data + oob) */ +#define FTNANDC021_CMD_ERBLK 0x11 /* erase block */ +#define FTNANDC021_CMD_WROOB 0x13 /* write oob */ + +#endif diff --git a/include/faraday/nand.h b/include/faraday/nand.h new file mode 100644 index 0000000..144ec13 --- /dev/null +++ b/include/faraday/nand.h @@ -0,0 +1,34 @@ +/* + * Faraday NAND 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 __FARADAY_NAND_H +#define __FARADAY_NAND_H + +#include + +/** + * struct faraday_nand_chip - chip level device structure + * @regs: base address of hardware registers + * @priv: hardware controller specific settings + * @alen: address length/cycle + * @pgsz: page size (byte) + * @bksz: block size (byte) + */ +struct faraday_nand_chip { + void __iomem *regs; + void *priv; + uint32_t alen; + uint32_t pgsz; + uint32_t bksz; +}; + +int ftnandc021_init(struct nand_chip *chip); + +#endif /* _FARADAY_NAND_H */