]> pilppa.com Git - linux-2.6-omap-h63xx.git/commitdiff
[MTD] [NAND] sh_flctl: add support for Renesas SuperH FLCTL
authorYoshihiro Shimoda <shimoda.yoshihiro@renesas.com>
Tue, 14 Oct 2008 12:23:26 +0000 (21:23 +0900)
committerDavid Woodhouse <David.Woodhouse@intel.com>
Tue, 14 Oct 2008 12:33:05 +0000 (13:33 +0100)
Several Renesas SuperH CPU has FLCTL. The FLCTL support NAND Flash.
This driver support SH7723.

Signed-off-by: Yoshihiro Shimoda <shimoda.yoshihiro@renesas.com>
Acked-by: Paul Mundt <lethal@linux-sh.org>
Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
drivers/mtd/nand/Kconfig
drivers/mtd/nand/Makefile
drivers/mtd/nand/sh_flctl.c [new file with mode: 0644]
include/linux/mtd/sh_flctl.h [new file with mode: 0644]

index 82815dd64bf6252829c5c0a8cf62778dcdd1dc9d..89b4d39386abf763a15cd4516ce011268e33122a 100644 (file)
@@ -407,4 +407,11 @@ config MTD_NAND_MXC
          This enables the driver for the NAND flash controller on the
          MXC processors.
 
+config MTD_NAND_SH_FLCTL
+       tristate "Support for NAND on Renesas SuperH FLCTL"
+       depends on MTD_NAND && SUPERH && CPU_SUBTYPE_SH7723
+       help
+         Several Renesas SuperH CPU has FLCTL. This option enables support
+         for NAND Flash using FLCTL. This driver support SH7723.
+
 endif # MTD_NAND
index e0fee048c1b42f42c64262368b5de298207b6a7f..9bfeca324b32b5f9f3a958b1e85a22b8f9b37b1a 100644 (file)
@@ -33,6 +33,7 @@ obj-$(CONFIG_MTD_NAND_PASEMI)         += pasemi_nand.o
 obj-$(CONFIG_MTD_NAND_ORION)           += orion_nand.o
 obj-$(CONFIG_MTD_NAND_FSL_ELBC)                += fsl_elbc_nand.o
 obj-$(CONFIG_MTD_NAND_FSL_UPM)         += fsl_upm.o
+obj-$(CONFIG_MTD_NAND_SH_FLCTL)                += sh_flctl.o
 obj-$(CONFIG_MTD_NAND_MXC)             += mxc_nand.o
 
 nand-objs := nand_base.o nand_bbt.o
diff --git a/drivers/mtd/nand/sh_flctl.c b/drivers/mtd/nand/sh_flctl.c
new file mode 100644 (file)
index 0000000..600a76f
--- /dev/null
@@ -0,0 +1,301 @@
+/*
+ * SuperH FLCTL nand controller
+ *
+ * Copyright © 2008 Renesas Solutions Corp.
+ * Copyright © 2008 Atom Create Engineering Co., Ltd.
+ *
+ * Based on fsl_elbc_nand.c, Copyright © 2006-2007 Freescale Semiconductor
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/partitions.h>
+#include <linux/mtd/sh_flctl.h>
+
+static struct nand_ecclayout flctl_4secc_oob_16 = {
+       .eccbytes = 10,
+       .eccpos = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9},
+       .oobfree = {
+               {.offset = 12,
+               . length = 4} },
+};
+
+static struct nand_ecclayout flctl_4secc_oob_64 = {
+       .eccbytes = 10,
+       .eccpos = {48, 49, 50, 51, 52, 53, 54, 55, 56, 57},
+       .oobfree = {
+               {.offset = 60,
+               . length = 4} },
+};
+
+static uint8_t scan_ff_pattern[] = { 0xff, 0xff };
+
+static struct nand_bbt_descr flctl_4secc_smallpage = {
+       .options = NAND_BBT_SCAN2NDPAGE,
+       .offs = 11,
+       .len = 1,
+       .pattern = scan_ff_pattern,
+};
+
+static struct nand_bbt_descr flctl_4secc_largepage = {
+       .options = 0,
+       .offs = 58,
+       .len = 2,
+       .pattern = scan_ff_pattern,
+};
+
+static void empty_fifo(struct sh_flctl *flctl)
+{
+       writel(0x000c0000, FLINTDMACR(flctl));  /* FIFO Clear */
+       writel(0x00000000, FLINTDMACR(flctl));  /* Clear Error flags */
+}
+
+static void start_translation(struct sh_flctl *flctl)
+{
+       writeb(TRSTRT, FLTRCR(flctl));
+}
+
+static void wait_completion(struct sh_flctl *flctl)
+{
+       uint32_t timeout = LOOP_TIMEOUT_MAX;
+
+       while (timeout--) {
+               if (readb(FLTRCR(flctl)) & TREND) {
+                       writeb(0x0, FLTRCR(flctl));
+                       return;
+               }
+               udelay(1);
+       }
+
+       printk(KERN_ERR "wait_completion(): Timeout occured \n");
+       writeb(0x0, FLTRCR(flctl));
+}
+
+static void set_addr(struct mtd_info *mtd, int column, int page_addr)
+{
+       struct sh_flctl *flctl = mtd_to_flctl(mtd);
+       uint32_t addr = 0;
+
+       if (column == -1) {
+               addr = page_addr;       /* ERASE1 */
+       } else if (page_addr != -1) {
+               /* SEQIN, READ0, etc.. */
+               if (flctl->page_size) {
+                       addr = column & 0x0FFF;
+                       addr |= (page_addr & 0xff) << 16;
+                       addr |= ((page_addr >> 8) & 0xff) << 24;
+                       /* big than 128MB */
+                       if (flctl->rw_ADRCNT == ADRCNT2_E) {
+                               uint32_t        addr2;
+                               addr2 = (page_addr >> 16) & 0xff;
+                               writel(addr2, FLADR2(flctl));
+                       }
+               } else {
+                       addr = column;
+                       addr |= (page_addr & 0xff) << 8;
+                       addr |= ((page_addr >> 8) & 0xff) << 16;
+                       addr |= ((page_addr >> 16) & 0xff) << 24;
+               }
+       }
+       writel(addr, FLADR(flctl));
+}
+
+static void wait_rfifo_ready(struct sh_flctl *flctl)
+{
+       uint32_t timeout = LOOP_TIMEOUT_MAX;
+
+       while (timeout--) {
+               uint32_t val;
+               /* check FIFO */
+               val = readl(FLDTCNTR(flctl)) >> 16;
+               if (val & 0xFF)
+                       return;
+               udelay(1);
+       }
+       printk(KERN_ERR "wait_rfifo_ready(): Timeout occured \n");
+}
+
+static void wait_wfifo_ready(struct sh_flctl *flctl)
+{
+       uint32_t len, timeout = LOOP_TIMEOUT_MAX;
+
+       while (timeout--) {
+               /* check FIFO */
+               len = (readl(FLDTCNTR(flctl)) >> 16) & 0xFF;
+               if (len >= 4)
+                       return;
+               udelay(1);
+       }
+       printk(KERN_ERR "wait_wfifo_ready(): Timeout occured \n");
+}
+
+static int wait_recfifo_ready(struct sh_flctl *flctl)
+{
+       uint32_t timeout = LOOP_TIMEOUT_MAX;
+       int checked[4];
+       void __iomem *ecc_reg[4];
+       int i;
+       uint32_t data, size;
+
+       memset(checked, 0, sizeof(checked));
+
+       while (timeout--) {
+               size = readl(FLDTCNTR(flctl)) >> 24;
+               if (size & 0xFF)
+                       return 0;       /* success */
+
+               if (readl(FL4ECCCR(flctl)) & _4ECCFA)
+                       return 1;       /* can't correct */
+
+               udelay(1);
+               if (!(readl(FL4ECCCR(flctl)) & _4ECCEND))
+                       continue;
+
+               /* start error correction */
+               ecc_reg[0] = FL4ECCRESULT0(flctl);
+               ecc_reg[1] = FL4ECCRESULT1(flctl);
+               ecc_reg[2] = FL4ECCRESULT2(flctl);
+               ecc_reg[3] = FL4ECCRESULT3(flctl);
+
+               for (i = 0; i < 3; i++) {
+                       data = readl(ecc_reg[i]);
+                       if (data != INIT_FL4ECCRESULT_VAL && !checked[i]) {
+                               uint8_t org;
+                               int index;
+
+                               index = data >> 16;
+                               org = flctl->done_buff[index];
+                               flctl->done_buff[index] = org ^ (data & 0xFF);
+                               checked[i] = 1;
+                       }
+               }
+
+               writel(0, FL4ECCCR(flctl));
+       }
+
+       printk(KERN_ERR "wait_recfifo_ready(): Timeout occured \n");
+       return 1;       /* timeout */
+}
+
+static void wait_wecfifo_ready(struct sh_flctl *flctl)
+{
+       uint32_t timeout = LOOP_TIMEOUT_MAX;
+       uint32_t len;
+
+       while (timeout--) {
+               /* check FLECFIFO */
+               len = (readl(FLDTCNTR(flctl)) >> 24) & 0xFF;
+               if (len >= 4)
+                       return;
+               udelay(1);
+       }
+       printk(KERN_ERR "wait_wecfifo_ready(): Timeout occured \n");
+}
+
+static void read_datareg(struct sh_flctl *flctl, int offset)
+{
+       unsigned long data;
+       unsigned long *buf = (unsigned long *)&flctl->done_buff[offset];
+
+       wait_completion(flctl);
+
+       data = readl(FLDATAR(flctl));
+       *buf = le32_to_cpu(data);
+}
+
+static void read_fiforeg(struct sh_flctl *flctl, int rlen, int offset)
+{
+       int i, len_4align;
+       unsigned long *buf = (unsigned long *)&flctl->done_buff[offset];
+       void *fifo_addr = (void *)FLDTFIFO(flctl);
+
+       len_4align = (rlen + 3) / 4;
+
+       for (i = 0; i < len_4align; i++) {
+               wait_rfifo_ready(flctl);
+               buf[i] = readl(fifo_addr);
+               buf[i] = be32_to_cpu(buf[i]);
+       }
+}
+
+static int read_ecfiforeg(struct sh_flctl *flctl, uint8_t *buff)
+{
+       int i;
+       unsigned long *ecc_buf = (unsigned long *)buff;
+       void *fifo_addr = (void *)FLECFIFO(flctl);
+
+       for (i = 0; i < 4; i++) {
+               if (wait_recfifo_ready(flctl))
+                       return 1;
+               ecc_buf[i] = readl(fifo_addr);
+               ecc_buf[i] = be32_to_cpu(ecc_buf[i]);
+       }
+
+       return 0;
+}
+
+static void write_fiforeg(struct sh_flctl *flctl, int rlen, int offset)
+{
+       int i, len_4align;
+       unsigned long *data = (unsigned long *)&flctl->done_buff[offset];
+       void *fifo_addr = (void *)FLDTFIFO(flctl);
+
+       len_4align = (rlen + 3) / 4;
+       for (i = 0; i < len_4align; i++) {
+               wait_wfifo_ready(flctl);
+               writel(cpu_to_be32(data[i]), fifo_addr);
+       }
+}
+
+static void set_cmd_regs(struct mtd_info *mtd, uint32_t cmd, uint32_t flcmcdr_val)
+{
+       struct sh_flctl *flctl = mtd_to_flctl(mtd);
+       uint32_t flcmncr_val = readl(FLCMNCR(flctl));
+       uint32_t flcmdcr_val, addr_len_bytes = 0;
+
+       /* Set SNAND bit if page size is 2048byte */
+       if (flctl->page_size)
+               flcmncr_val |= SNAND_E;
+       else
+               flcmncr_val &= ~SNAND_E;
+
+       /* default FLCMDCR val */
+       flcmdcr_val = DOCMD1_E | DOADR_E;
+
+       /* Set for FLCMDCR */
+       switch (cmd) {
+       case NAND_CMD_ERASE1:
+               addr_len_bytes = flctl->erase_ADRCNT;
+               flcmdcr_val |= DOCMD2_E;
+               break;
+       case NAND_CMD_READ0:
+       case NAND_CMD_READOOB:
+               addr_len_bytes = flctl->rw_ADRCNT;
+               flcmdcr_val |= CDSRC_E;
+               break;
+       case NAND_CMD_SEQIN:
+               /* This case is that cmd is READ0 or READ1 or READ00 */
+               flcmdcr_val &= ~DOADR_E;        /* ONLY execute 1st cmd */
+               break;
+       case NAND_CMD_PAGEPROG:
+               addr_len_bytes = flctl->rw_ADRCNT;
diff --git a/include/linux/mtd/sh_flctl.h b/include/linux/mtd/sh_flctl.h
new file mode 100644 (file)
index 0000000..e77c1ce
--- /dev/null
@@ -0,0 +1,125 @@
+/*
+ * SuperH FLCTL nand controller
+ *
+ * Copyright © 2008 Renesas Solutions Corp.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef __SH_FLCTL_H__
+#define __SH_FLCTL_H__
+
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/partitions.h>
+
+/* FLCTL registers */
+#define FLCMNCR(f)             (f->reg + 0x0)
+#define FLCMDCR(f)             (f->reg + 0x4)
+#define FLCMCDR(f)             (f->reg + 0x8)
+#define FLADR(f)               (f->reg + 0xC)
+#define FLADR2(f)              (f->reg + 0x3C)
+#define FLDATAR(f)             (f->reg + 0x10)
+#define FLDTCNTR(f)            (f->reg + 0x14)
+#define FLINTDMACR(f)          (f->reg + 0x18)
+#define FLBSYTMR(f)            (f->reg + 0x1C)
+#define FLBSYCNT(f)            (f->reg + 0x20)
+#define FLDTFIFO(f)            (f->reg + 0x24)
+#define FLECFIFO(f)            (f->reg + 0x28)
+#define FLTRCR(f)              (f->reg + 0x2C)
+#define        FL4ECCRESULT0(f)        (f->reg + 0x80)
+#define        FL4ECCRESULT1(f)        (f->reg + 0x84)
+#define        FL4ECCRESULT2(f)        (f->reg + 0x88)
+#define        FL4ECCRESULT3(f)        (f->reg + 0x8C)
+#define        FL4ECCCR(f)             (f->reg + 0x90)
+#define        FL4ECCCNT(f)            (f->reg + 0x94)
+#define        FLERRADR(f)             (f->reg + 0x98)
+
+/* FLCMNCR control bits */
+#define ECCPOS2                (0x1 << 25)
+#define _4ECCCNTEN     (0x1 << 24)
+#define _4ECCEN                (0x1 << 23)
+#define _4ECCCORRECT   (0x1 << 22)
+#define SNAND_E                (0x1 << 18)     /* SNAND (0=512 1=2048)*/
+#define QTSEL_E                (0x1 << 17)
+#define ENDIAN         (0x1 << 16)     /* 1 = little endian */
+#define FCKSEL_E       (0x1 << 15)
+#define ECCPOS_00      (0x00 << 12)
+#define ECCPOS_01      (0x01 << 12)
+#define ECCPOS_02      (0x02 << 12)
+#define ACM_SACCES_MODE        (0x01 << 10)
+#define NANWF_E                (0x1 << 9)
+#define SE_D           (0x1 << 8)      /* Spare area disable */
+#define        CE1_ENABLE      (0x1 << 4)      /* Chip Enable 1 */
+#define        CE0_ENABLE      (0x1 << 3)      /* Chip Enable 0 */
+#define        TYPESEL_SET     (0x1 << 0)
+
+/* FLCMDCR control bits */
+#define ADRCNT2_E      (0x1 << 31)     /* 5byte address enable */
+#define ADRMD_E                (0x1 << 26)     /* Sector address access */
+#define CDSRC_E                (0x1 << 25)     /* Data buffer selection */
+#define DOSR_E         (0x1 << 24)     /* Status read check */
+#define SELRW          (0x1 << 21)     /*  0:read 1:write */
+#define DOADR_E                (0x1 << 20)     /* Address stage execute */
+#define ADRCNT_1       (0x00 << 18)    /* Address data bytes: 1byte */
+#define ADRCNT_2       (0x01 << 18)    /* Address data bytes: 2byte */
+#define ADRCNT_3       (0x02 << 18)    /* Address data bytes: 3byte */
+#define ADRCNT_4       (0x03 << 18)    /* Address data bytes: 4byte */
+#define DOCMD2_E       (0x1 << 17)     /* 2nd cmd stage execute */
+#define DOCMD1_E       (0x1 << 16)     /* 1st cmd stage execute */
+
+/* FLTRCR control bits */
+#define TRSTRT         (0x1 << 0)      /* translation start */
+#define TREND          (0x1 << 1)      /* translation end */
+
+/* FL4ECCCR control bits */
+#define        _4ECCFA         (0x1 << 2)      /* 4 symbols correct fault */
+#define        _4ECCEND        (0x1 << 1)      /* 4 symbols end */
+#define        _4ECCEXST       (0x1 << 0)      /* 4 symbols exist */
+
+#define INIT_FL4ECCRESULT_VAL  0x03FF03FF
+#define LOOP_TIMEOUT_MAX       0x00010000
+
+#define mtd_to_flctl(mtd)      container_of(mtd, struct sh_flctl, mtd)
+
+struct sh_flctl {
+       struct mtd_info         mtd;
+       struct nand_chip        chip;
+       void __iomem            *reg;
+
+       uint8_t done_buff[2048 + 64];   /* max size 2048 + 64 */
+       int     read_bytes;
+       int     index;
+       int     seqin_column;           /* column in SEQIN cmd */
+       int     seqin_page_addr;        /* page_addr in SEQIN cmd */
+       uint32_t seqin_read_cmd;                /* read cmd in SEQIN cmd */
+       int     erase1_page_addr;       /* page_addr in ERASE1 cmd */
+       uint32_t erase_ADRCNT;          /* bits of FLCMDCR in ERASE1 cmd */
+       uint32_t rw_ADRCNT;     /* bits of FLCMDCR in READ WRITE cmd */
+
+       int     hwecc_cant_correct[4];
+
+       unsigned page_size:1;   /* NAND page size (0 = 512, 1 = 2048) */
+       unsigned hwecc:1;       /* Hardware ECC (0 = disabled, 1 = enabled) */
+};
+
+struct sh_flctl_platform_data {
+       struct mtd_partition    *parts;
+       int                     nr_parts;
+       unsigned long           flcmncr_val;
+
+       unsigned has_hwecc:1;
+};
+
+#endif /* __SH_FLCTL_H__ */