summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Gleixner <tglx@linutronix.de>2004-10-21 02:35:05 +0100
committerDavid Woodhouse <dwmw2@infradead.org>2004-10-21 02:35:05 +0100
commitce9ef9d9bdd861fbcd221d1722deb5ce13ded23e (patch)
treeb29445bc9cf3f3a6f2ce4bf9e6e0be1f8c6010d4
parent91c730410be06832fa7b0493779e943ffe80a4f5 (diff)
MTD: NAND flash driver updates.
- Use new RS library for ECC - Add support for new NAND flash chips - New board support: - iPAQ H1910 - Renesas AG-AND devel board - Simtec S3C210 - Support for shared controllers on multiple chips. Signed-Off-By: Thomas Gleixner <tglx@linutronix.de> Signed-Off-By: David Woodhouse <dwmw2@infradead.org>
-rw-r--r--drivers/mtd/nand/Kconfig49
-rw-r--r--drivers/mtd/nand/Makefile5
-rw-r--r--drivers/mtd/nand/au1550nd.c420
-rw-r--r--drivers/mtd/nand/autcpu12.c6
-rw-r--r--drivers/mtd/nand/diskonchip.c234
-rw-r--r--drivers/mtd/nand/edb7312.c27
-rw-r--r--drivers/mtd/nand/h1910.c208
-rw-r--r--drivers/mtd/nand/nand_base.c147
-rw-r--r--drivers/mtd/nand/nand_bbt.c5
-rw-r--r--drivers/mtd/nand/ppchameleonevb.c51
-rw-r--r--drivers/mtd/nand/rtc_from4.c561
-rw-r--r--drivers/mtd/nand/s3c2410.c704
-rw-r--r--drivers/mtd/nand/spia.c23
-rw-r--r--drivers/mtd/nand/toto.c24
-rw-r--r--drivers/mtd/nand/tx4925ndfmc.c30
-rw-r--r--drivers/mtd/nand/tx4938ndfmc.c22
-rw-r--r--include/linux/mtd/nand.h20
17 files changed, 2101 insertions, 435 deletions
diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
index ed085292c57d..89ab7da160f1 100644
--- a/drivers/mtd/nand/Kconfig
+++ b/drivers/mtd/nand/Kconfig
@@ -1,5 +1,5 @@
# drivers/mtd/nand/Kconfig
-# $Id: Kconfig,v 1.17 2004/08/10 14:24:07 dwmw2 Exp $
+# $Id: Kconfig,v 1.22 2004/10/05 22:11:46 gleixner Exp $
menu "NAND Flash Device Drivers"
depends on MTD!=n
@@ -36,6 +36,12 @@ config MTD_NAND_EDB7312
This enables the driver for the Cirrus Logic EBD7312 evaluation
board to access the onboard NAND Flash.
+config MTD_NAND_H1900
+ tristate "iPAQ H1900 flash"
+ depends on ARM && MTD_NAND && ARCH_PXA && MTD_PARTITIONS
+ help
+ This enables the driver for the iPAQ h1900 flash.
+
config MTD_NAND_SPIA
tristate "NAND Flash device on SPIA board"
depends on ARM && ARCH_P720T && MTD_NAND
@@ -74,15 +80,54 @@ config MTD_NAND_AU1550
This enables the driver for the NAND flash controller on the
AMD/Alchemy 1550 SOC.
+config MTD_NAND_RTC_FROM4
+ tristate "Renesas Flash ROM 4-slot interface board (FROM_BOARD4)"
+ depends on MTD_NAND && SH_SOLUTION_ENGINE
+ select REED_SOLOMON
+ select REED_SOLOMON_DEC8
+ help
+ This enables the driver for the Renesas Technology AG-AND
+ flash interface board (FROM_BOARD4)
+
config MTD_NAND_PPCHAMELEONEVB
tristate "NAND Flash device on PPChameleonEVB board"
depends on PPCHAMELEONEVB && MTD_NAND
help
- This enables the NAND flash driver on the PPChameleon EVB Board.
+ This enables the NAND flash driver on the PPChameleon EVB Board.
+
+config MTD_NAND_S3C2410
+ tristate "NAND Flash support for S3C2410 SoC"
+ depends on ARCH_S3C2410 && MTD_NAND
+ help
+ This enables the NAND flash controller on the S3C2410.
+
+ No board specfic support is done by this driver, each board
+ must advertise a platform_device for the driver to attach.
+
+config MTD_NAND_S3C2410_DEBUG
+ bool "S3C2410 NAND driver debug"
+ depends on MTD_NAND_S3C2410
+ help
+ Enable debugging of the S3C2410 NAND driver
+
+config MTD_NAND_S3C2410_HWECC
+ bool "S3C2410 NAND Hardware ECC"
+ depends on MTD_NAND_S3C2410
+ help
+ Enable the use of the S3C2410's internal ECC generator when
+ using NAND. Early versions of the chip have had problems with
+ incorrect ECC generation, and if using these, the default of
+ software ECC is preferable.
+
+ If you lay down a device with the hardware ECC, then you will
+ currently not be able to switch to software, as there is no
+ implementation for ECC method used by the S3C2410
config MTD_NAND_DISKONCHIP
tristate "DiskOnChip 2000, Millennium and Millennium Plus (NAND reimplementation) (EXPERIMENTAL)"
depends on MTD_NAND && EXPERIMENTAL
+ select REED_SOLOMON
+ select REED_SOLOMON_DEC16
help
This is a reimplementation of M-Systems DiskOnChip 2000,
Millennium and Millennium Plus as a standard NAND device driver,
diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
index db19399398ad..a0b6c60528da 100644
--- a/drivers/mtd/nand/Makefile
+++ b/drivers/mtd/nand/Makefile
@@ -1,7 +1,7 @@
#
# linux/drivers/nand/Makefile
#
-# $Id: Makefile.common,v 1.9 2004/07/12 16:07:31 dwmw2 Exp $
+# $Id: Makefile.common,v 1.13 2004/09/28 22:04:23 bjd Exp $
obj-$(CONFIG_MTD_NAND) += nand.o nand_ecc.o
obj-$(CONFIG_MTD_NAND_IDS) += nand_ids.o
@@ -14,6 +14,9 @@ obj-$(CONFIG_MTD_NAND_TX4925NDFMC) += tx4925ndfmc.o
obj-$(CONFIG_MTD_NAND_TX4938NDFMC) += tx4938ndfmc.o
obj-$(CONFIG_MTD_NAND_AU1550) += au1550nd.o
obj-$(CONFIG_MTD_NAND_PPCHAMELEONEVB) += ppchameleonevb.o
+obj-$(CONFIG_MTD_NAND_S3C2410) += s3c2410.o
obj-$(CONFIG_MTD_NAND_DISKONCHIP) += diskonchip.o
+obj-$(CONFIG_MTD_NAND_H1900) += h1910.o
+obj-$(CONFIG_MTD_NAND_RTC_FROM4) += rtc_from4.o
nand-objs = nand_base.o nand_bbt.o
diff --git a/drivers/mtd/nand/au1550nd.c b/drivers/mtd/nand/au1550nd.c
index 8e4da650603f..196d1f20a47b 100644
--- a/drivers/mtd/nand/au1550nd.c
+++ b/drivers/mtd/nand/au1550nd.c
@@ -3,7 +3,7 @@
*
* Copyright (C) 2004 Embedded Edge, LLC
*
- * $Id: au1550nd.c,v 1.5 2004/05/17 07:19:35 ppopov Exp $
+ * $Id: au1550nd.c,v 1.9 2004/10/20 05:58:30 ppopov Exp $
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@@ -18,6 +18,17 @@
#include <linux/mtd/nand.h>
#include <linux/mtd/partitions.h>
#include <asm/io.h>
+
+/* fixme: this is ugly */
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 0)
+#include <asm/mach-au1x00/au1000.h>
+#ifdef CONFIG_MIPS_PB1550
+#include <asm/mach-pb1x00/pb1550.h>
+#endif
+#ifdef CONFIG_MIPS_DB1550
+#include <asm/mach-db1x00/db1x00.h>
+#endif
+#else
#include <asm/au1000.h>
#ifdef CONFIG_MIPS_PB1550
#include <asm/pb1550.h>
@@ -25,18 +36,16 @@
#ifdef CONFIG_MIPS_DB1550
#include <asm/db1x00.h>
#endif
-
+#endif
/*
* MTD structure for NAND controller
*/
static struct mtd_info *au1550_mtd = NULL;
-static volatile u32 p_nand;
-static int nand_width = 1; /* default, only x8 supported for now */
+static void __iomem *p_nand;
+static int nand_width = 1; /* default x8*/
-/* Internal buffers. Page buffer and oob buffer for one block*/
-static u_char data_buf[512 + 16];
-static u_char oob_buf[16 * 32];
+#define NAND_CS 1
/*
* Define partitions for flash device
@@ -70,183 +79,262 @@ const static struct mtd_partition partition_info[] = {
#endif
};
-static inline void write_cmd_reg(u8 cmd)
+
+/**
+ * au_read_byte - read one byte from the chip
+ * @mtd: MTD device structure
+ *
+ * read function for 8bit buswith
+ */
+static u_char au_read_byte(struct mtd_info *mtd)
{
- if (nand_width)
- *((volatile u8 *)(p_nand + MEM_STNAND_CMD)) = cmd;
- else
- *((volatile u16 *)(p_nand + MEM_STNAND_CMD)) = cmd;
+ struct nand_chip *this = mtd->priv;
+ u_char ret = readb(this->IO_ADDR_R);
au_sync();
+ return ret;
}
-static inline void write_addr_reg(u8 addr)
+/**
+ * au_write_byte - write one byte to the chip
+ * @mtd: MTD device structure
+ * @byte: pointer to data byte to write
+ *
+ * write function for 8it buswith
+ */
+static void au_write_byte(struct mtd_info *mtd, u_char byte)
{
- if (nand_width)
- *((volatile u8 *)(p_nand + MEM_STNAND_ADDR)) = addr;
- else
- *((volatile u16 *)(p_nand + MEM_STNAND_ADDR)) = addr;
+ struct nand_chip *this = mtd->priv;
+ writeb(byte, this->IO_ADDR_W);
au_sync();
}
-static inline void write_data_reg(u8 data)
+/**
+ * au_read_byte16 - read one byte endianess aware from the chip
+ * @mtd: MTD device structure
+ *
+ * read function for 16bit buswith with
+ * endianess conversion
+ */
+static u_char au_read_byte16(struct mtd_info *mtd)
{
- if (nand_width)
- *((volatile u8 *)(p_nand + MEM_STNAND_DATA)) = data;
- else
- *((volatile u16 *)(p_nand + MEM_STNAND_DATA)) = data;
+ struct nand_chip *this = mtd->priv;
+ u_char ret = (u_char) cpu_to_le16(readw(this->IO_ADDR_R));
au_sync();
+ return ret;
}
-static inline u32 read_data_reg(void)
+/**
+ * au_write_byte16 - write one byte endianess aware to the chip
+ * @mtd: MTD device structure
+ * @byte: pointer to data byte to write
+ *
+ * write function for 16bit buswith with
+ * endianess conversion
+ */
+static void au_write_byte16(struct mtd_info *mtd, u_char byte)
{
- u32 data;
- if (nand_width) {
- data = *((volatile u8 *)(p_nand + MEM_STNAND_DATA));
- au_sync();
- }
- else {
- data = *((volatile u16 *)(p_nand + MEM_STNAND_DATA));
- au_sync();
- }
- return data;
+ struct nand_chip *this = mtd->priv;
+ writew(le16_to_cpu((u16) byte), this->IO_ADDR_W);
+ au_sync();
}
-void au1550_hwcontrol(struct mtd_info *mtd, int cmd)
+/**
+ * au_read_word - read one word from the chip
+ * @mtd: MTD device structure
+ *
+ * read function for 16bit buswith without
+ * endianess conversion
+ */
+static u16 au_read_word(struct mtd_info *mtd)
{
+ struct nand_chip *this = mtd->priv;
+ u16 ret = readw(this->IO_ADDR_R);
+ au_sync();
+ return ret;
}
-int au1550_device_ready(struct mtd_info *mtd)
+/**
+ * au_write_word - write one word to the chip
+ * @mtd: MTD device structure
+ * @word: data word to write
+ *
+ * write function for 16bit buswith without
+ * endianess conversion
+ */
+static void au_write_word(struct mtd_info *mtd, u16 word)
{
- int ready;
- ready = (au_readl(MEM_STSTAT) & 0x1) ? 1 : 0;
- return ready;
+ struct nand_chip *this = mtd->priv;
+ writew(word, this->IO_ADDR_W);
+ au_sync();
}
-static u_char au1550_nand_read_byte(struct mtd_info *mtd)
+/**
+ * au_write_buf - write buffer to chip
+ * @mtd: MTD device structure
+ * @buf: data buffer
+ * @len: number of bytes to write
+ *
+ * write function for 8bit buswith
+ */
+static void au_write_buf(struct mtd_info *mtd, const u_char *buf, int len)
{
- u_char ret;
- ret = read_data_reg();
- return ret;
-}
+ int i;
+ struct nand_chip *this = mtd->priv;
-static void au1550_nand_write_byte(struct mtd_info *mtd, u_char byte)
-{
- write_data_reg((u8)byte);
+ for (i=0; i<len; i++) {
+ writeb(buf[i], this->IO_ADDR_W);
+ au_sync();
+ }
}
-static void
-au1550_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len)
+/**
+ * au_read_buf - read chip data into buffer
+ * @mtd: MTD device structure
+ * @buf: buffer to store date
+ * @len: number of bytes to read
+ *
+ * read function for 8bit buswith
+ */
+static void au_read_buf(struct mtd_info *mtd, u_char *buf, int len)
{
int i;
+ struct nand_chip *this = mtd->priv;
- for (i=0; i<len; i++)
- write_data_reg(buf[i]);
+ for (i=0; i<len; i++) {
+ buf[i] = readb(this->IO_ADDR_R);
+ au_sync();
+ }
}
-static void
-au1550_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len)
+/**
+ * au_verify_buf - Verify chip data against buffer
+ * @mtd: MTD device structure
+ * @buf: buffer containing the data to compare
+ * @len: number of bytes to compare
+ *
+ * verify function for 8bit buswith
+ */
+static int au_verify_buf(struct mtd_info *mtd, const u_char *buf, int len)
{
int i;
+ struct nand_chip *this = mtd->priv;
- for (i=0; i<len; i++)
- buf[i] = (u_char)read_data_reg();
+ for (i=0; i<len; i++) {
+ if (buf[i] != readb(this->IO_ADDR_R))
+ return -EFAULT;
+ au_sync();
+ }
+
+ return 0;
}
-static int
-au1550_nand_verify_buf(struct mtd_info *mtd, const u_char *buf, int len)
+/**
+ * au_write_buf16 - write buffer to chip
+ * @mtd: MTD device structure
+ * @buf: data buffer
+ * @len: number of bytes to write
+ *
+ * write function for 16bit buswith
+ */
+static void au_write_buf16(struct mtd_info *mtd, const u_char *buf, int len)
{
int i;
+ struct nand_chip *this = mtd->priv;
+ u16 *p = (u16 *) buf;
+ len >>= 1;
+
+ for (i=0; i<len; i++) {
+ writew(p[i], this->IO_ADDR_W);
+ au_sync();
+ }
+
+}
- for (i=0; i<len; i++)
- if (buf[i] != (u_char)read_data_reg())
- return -EFAULT;
+/**
+ * au_read_buf16 - read chip data into buffer
+ * @mtd: MTD device structure
+ * @buf: buffer to store date
+ * @len: number of bytes to read
+ *
+ * read function for 16bit buswith
+ */
+static void au_read_buf16(struct mtd_info *mtd, u_char *buf, int len)
+{
+ int i;
+ struct nand_chip *this = mtd->priv;
+ u16 *p = (u16 *) buf;
+ len >>= 1;
- return 0;
+ for (i=0; i<len; i++) {
+ p[i] = readw(this->IO_ADDR_R);
+ au_sync();
+ }
}
-static void au1550_nand_select_chip(struct mtd_info *mtd, int chip)
+/**
+ * au_verify_buf16 - Verify chip data against buffer
+ * @mtd: MTD device structure
+ * @buf: buffer containing the data to compare
+ * @len: number of bytes to compare
+ *
+ * verify function for 16bit buswith
+ */
+static int au_verify_buf16(struct mtd_info *mtd, const u_char *buf, int len)
{
- switch(chip) {
- case -1:
- /* deassert chip enable */
- au_writel(au_readl(MEM_STNDCTL) & ~0x20 , MEM_STNDCTL);
- break;
- case 0:
- /* assert (force assert) chip enable */
- au_writel(au_readl(MEM_STNDCTL) | 0x20 , MEM_STNDCTL);
- break;
+ int i;
+ struct nand_chip *this = mtd->priv;
+ u16 *p = (u16 *) buf;
+ len >>= 1;
- default:
- BUG();
+ for (i=0; i<len; i++) {
+ if (p[i] != readw(this->IO_ADDR_R))
+ return -EFAULT;
+ au_sync();
}
+ return 0;
}
-static void au1550_nand_command (struct mtd_info *mtd, unsigned command,
- int column, int page_addr)
+
+static void au1550_hwcontrol(struct mtd_info *mtd, int cmd)
{
register struct nand_chip *this = mtd->priv;
- /*
- * Write out the command to the device.
- */
- if (command == NAND_CMD_SEQIN) {
- int readcmd;
-
- if (column >= mtd->oobblock) {
- /* OOB area */
- column -= mtd->oobblock;
- readcmd = NAND_CMD_READOOB;
- } else if (column < 256) {
- /* First 256 bytes --> READ0 */
- readcmd = NAND_CMD_READ0;
- } else {
- column -= 256;
- readcmd = NAND_CMD_READ1;
- }
- write_cmd_reg(readcmd);
- }
- write_cmd_reg(command);
-
- if (column != -1 || page_addr != -1) {
-
- /* Serially input address */
- if (column != -1)
- write_addr_reg(column);
- if (page_addr != -1) {
- write_addr_reg((unsigned char) (page_addr & 0xff));
- write_addr_reg(((page_addr >> 8) & 0xff));
- /* One more address cycle for higher density devices */
- if (mtd->size & 0x0c000000)
- write_addr_reg((unsigned char) ((page_addr >> 16) & 0x0f));
- }
- }
-
- switch (command) {
-
- case NAND_CMD_PAGEPROG:
- case NAND_CMD_ERASE1:
- case NAND_CMD_ERASE2:
- case NAND_CMD_SEQIN:
- case NAND_CMD_STATUS:
+ switch(cmd){
+
+ case NAND_CTL_SETCLE: this->IO_ADDR_W = p_nand + MEM_STNAND_CMD; break;
+ case NAND_CTL_CLRCLE: this->IO_ADDR_W = p_nand + MEM_STNAND_DATA; break;
+
+ case NAND_CTL_SETALE: this->IO_ADDR_W = p_nand + MEM_STNAND_ADDR; break;
+ case NAND_CTL_CLRALE:
+ this->IO_ADDR_W = p_nand + MEM_STNAND_DATA;
+ /* FIXME: Nobody knows why this is neccecary,
+ * but it works only that way */
+ udelay(1);
break;
- case NAND_CMD_RESET:
- if (this->dev_ready)
- break;
- udelay(this->chip_delay);
- write_cmd_reg(NAND_CMD_STATUS);
- while ( !(read_data_reg() & 0x40));
- return;
-
- /* This applies to read commands */
- default:
- udelay (this->chip_delay);
+ case NAND_CTL_SETNCE:
+ /* assert (force assert) chip enable */
+ au_writel(au_readl(MEM_STNDCTL) | (1<<(4+NAND_CS)) , MEM_STNDCTL);
+ break;
+
+ case NAND_CTL_CLRNCE:
+ /* deassert chip enable */
+ au_writel(au_readl(MEM_STNDCTL) & ~(1<<(4+NAND_CS)), MEM_STNDCTL);
+ break;
}
+
+ this->IO_ADDR_R = this->IO_ADDR_W;
- /* wait until command is processed */
- while (!this->dev_ready(mtd));
+ /* Drain the writebuffer */
+ au_sync();
}
+int au1550_device_ready(struct mtd_info *mtd)
+{
+ int ret = (au_readl(MEM_STSTAT) & 0x1) ? 1 : 0;
+ au_sync();
+ return ret;
+}
/*
* Main initialization routine
@@ -255,7 +343,7 @@ int __init au1550_init (void)
{
struct nand_chip *this;
u16 boot_swapboot = 0; /* default value */
- u32 mem_time;
+ int retval;
/* Allocate memory for MTD device structure and private data */
au1550_mtd = kmalloc (sizeof(struct mtd_info) +
@@ -275,9 +363,10 @@ int __init au1550_init (void)
/* Link the private data with the MTD structure */
au1550_mtd->priv = this;
+
+ /* MEM_STNDCTL: disable ints, disable nand boot */
/* disable interrupts */
au_writel(au_readl(MEM_STNDCTL) & ~(1<<8), MEM_STNDCTL);
-
/* disable NAND boot */
au_writel(au_readl(MEM_STNDCTL) & ~(1<<0), MEM_STNDCTL);
@@ -295,7 +384,6 @@ int __init au1550_init (void)
case 0xD:
/* x16 NAND Flash */
nand_width = 0;
- printk("Pb1550 NAND: 16-bit NAND not supported by MTD\n");
break;
case 1:
case 9:
@@ -307,62 +395,62 @@ int __init au1550_init (void)
break;
default:
printk("Pb1550 NAND: bad boot:swap\n");
- kfree(au1550_mtd);
- return 1;
+ retval = -EINVAL;
+ goto outmem;
}
+#endif
/* Configure RCE1 - should be done by YAMON */
- au_writel(0x5 | (nand_width << 22), MEM_STCFG1);
- au_writel(NAND_TIMING, MEM_STTIME1);
- mem_time = au_readl(MEM_STTIME1);
+ au_writel(0x5 | (nand_width << 22), 0xB4001010); /* MEM_STCFG1 */
+ au_writel(NAND_TIMING, 0xB4001014); /* MEM_STTIME1 */
au_sync();
- /* setup and enable chip select */
+ /* setup and enable chip select, MEM_STADDR1 */
/* we really need to decode offsets only up till 0x20 */
au_writel((1<<28) | (NAND_PHYS_ADDR>>4) |
(((NAND_PHYS_ADDR + 0x1000)-1) & (0x3fff<<18)>>18),
MEM_STADDR1);
au_sync();
-#endif
-#ifdef CONFIG_MIPS_DB1550
- /* Configure RCE1 - should be done by YAMON */
- au_writel(0x00400005, MEM_STCFG1);
- au_writel(0x00007774, MEM_STTIME1);
- au_writel(0x12000FFF, MEM_STADDR1);
-#endif
-
- p_nand = (volatile struct nand_regs *)ioremap(NAND_PHYS_ADDR, 0x1000);
+ p_nand = (void __iomem *)ioremap(NAND_PHYS_ADDR, 0x1000);
/* Set address of hardware control function */
this->hwcontrol = au1550_hwcontrol;
this->dev_ready = au1550_device_ready;
/* 30 us command delay time */
this->chip_delay = 30;
-
- this->cmdfunc = au1550_nand_command;
- this->select_chip = au1550_nand_select_chip;
- this->write_byte = au1550_nand_write_byte;
- this->read_byte = au1550_nand_read_byte;
- this->write_buf = au1550_nand_write_buf;
- this->read_buf = au1550_nand_read_buf;
- this->verify_buf = au1550_nand_verify_buf;
this->eccmode = NAND_ECC_SOFT;
- /* Set internal data buffer */
- this->data_buf = data_buf;
- this->oob_buf = oob_buf;
+ this->options = NAND_NO_AUTOINCR;
+
+ if (!nand_width)
+ this->options |= NAND_BUSWIDTH_16;
+
+ this->read_byte = (!nand_width) ? au_read_byte16 : au_read_byte;
+ this->write_byte = (!nand_width) ? au_write_byte16 : au_write_byte;
+ this->write_word = au_write_word;
+ this->read_word = au_read_word;
+ this->write_buf = (!nand_width) ? au_write_buf16 : au_write_buf;
+ this->read_buf = (!nand_width) ? au_read_buf16 : au_read_buf;
+ this->verify_buf = (!nand_width) ? au_verify_buf16 : au_verify_buf;
/* Scan to find existence of the device */
if (nand_scan (au1550_mtd, 1)) {
- kfree (au1550_mtd);
- return -ENXIO;
+ retval = -ENXIO;
+ goto outio;
}
/* Register the partitions */
add_mtd_partitions(au1550_mtd, partition_info, NUM_PARTITIONS);
return 0;
+
+ outio:
+ iounmap ((void *)p_nand);
+
+ outmem:
+ kfree (au1550_mtd);
+ return retval;
}
module_init(au1550_init);
@@ -375,16 +463,14 @@ static void __exit au1550_cleanup (void)
{
struct nand_chip *this = (struct nand_chip *) &au1550_mtd[1];
- iounmap ((void *)p_nand);
-
- /* Unregister partitions */
- del_mtd_partitions(au1550_mtd);
-
- /* Unregister the device */
- del_mtd_device (au1550_mtd);
+ /* Release resources, unregister device */
+ nand_release (au1550_mtd);
/* Free the MTD device structure */
kfree (au1550_mtd);
+
+ /* Unmap */
+ iounmap ((void *)p_nand);
}
module_exit(au1550_cleanup);
#endif
diff --git a/drivers/mtd/nand/autcpu12.c b/drivers/mtd/nand/autcpu12.c
index 33779a8dadf4..eb101b47c9ce 100644
--- a/drivers/mtd/nand/autcpu12.c
+++ b/drivers/mtd/nand/autcpu12.c
@@ -6,7 +6,7 @@
* Derived from drivers/mtd/spia.c
* Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com)
*
- * $Id: autcpu12.c,v 1.20 2004/07/20 02:44:26 dwmw2 Exp $
+ * $Id: autcpu12.c,v 1.21 2004/09/16 23:27:14 gleixner Exp $
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@@ -48,7 +48,7 @@ static int autcpu12_io_base = CS89712_VIRT_BASE;
static int autcpu12_fio_pbase = AUTCPU12_PHYS_SMC;
static int autcpu12_fio_ctrl = AUTCPU12_SMC_SELECT_OFFSET;
static int autcpu12_pedr = AUTCPU12_SMC_PORT_OFFSET;
-static int autcpu12_fio_base;
+static void __iomem * autcpu12_fio_base;
#ifdef MODULE
MODULE_PARM(autcpu12_fio_pbase, "i");
@@ -150,7 +150,7 @@ int __init autcpu12_init (void)
}
/* map physical adress */
- autcpu12_fio_base=(unsigned long)ioremap(autcpu12_fio_pbase,SZ_1K);
+ autcpu12_fio_base=(void __iomem *)ioremap(autcpu12_fio_pbase,SZ_1K);
if(!autcpu12_fio_base){
printk("Ioremap autcpu12 SmartMedia Card failed\n");
err = -EIO;
diff --git a/drivers/mtd/nand/diskonchip.c b/drivers/mtd/nand/diskonchip.c
index 0f7dedd6b88e..a94cca472f81 100644
--- a/drivers/mtd/nand/diskonchip.c
+++ b/drivers/mtd/nand/diskonchip.c
@@ -8,16 +8,22 @@
* Author: David Woodhouse <dwmw2@infradead.org>
* Additional Diskonchip 2000 and Millennium support by Dan Brown <dan_brown@ieee.org>
* Diskonchip Millennium Plus support by Kalev Lember <kalev@smartlink.ee>
- *
+ *
+ * Error correction code lifted from the old docecc code
+ * Author: Fabrice Bellard (fabrice.bellard@netgem.com)
+ * Copyright (C) 2000 Netgem S.A.
+ * converted to the generic Reed-Solomon library by Thomas Gleixner <tglx@linutronix.de>
+ *
* Interface to generic NAND code for M-Systems DiskOnChip devices
*
- * $Id: diskonchip.c,v 1.34 2004/08/09 19:41:12 dbrown Exp $
+ * $Id: diskonchip.c,v 1.38 2004/10/05 22:11:46 gleixner Exp $
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/delay.h>
+#include <linux/rslib.h>
#include <asm/io.h>
#include <linux/mtd/mtd.h>
@@ -62,7 +68,7 @@ static unsigned long __initdata doc_locations[] = {
static struct mtd_info *doclist = NULL;
struct doc_priv {
- unsigned long virtadr;
+ void __iomem *virtadr;
unsigned long physadr;
u_char ChipID;
u_char CDSNControl;
@@ -104,8 +110,10 @@ MODULE_PARM(try_dword, "i");
static int no_ecc_failures=0;
MODULE_PARM(no_ecc_failures, "i");
+#ifdef CONFIG_MTD_PARTITIONS
static int no_autopart=0;
MODULE_PARM(no_autopart, "i");
+#endif
#ifdef MTD_NAND_DISKONCHIP_BBTWRITE
static int inftl_bbt_write=1;
@@ -118,6 +126,112 @@ static unsigned long doc_config_location = CONFIG_MTD_DISKONCHIP_PROBE_ADDRESS;
MODULE_PARM(doc_config_location, "l");
MODULE_PARM_DESC(doc_config_location, "Physical memory address at which to probe for DiskOnChip");
+
+/* Sector size for HW ECC */
+#define SECTOR_SIZE 512
+/* The sector bytes are packed into NB_DATA 10 bit words */
+#define NB_DATA (((SECTOR_SIZE + 1) * 8 + 6) / 10)
+/* Number of roots */
+#define NROOTS 4
+/* First consective root */
+#define FCR 510
+/* Number of symbols */
+#define NN 1023
+
+/* the Reed Solomon control structure */
+static struct rs_control *rs_decoder;
+
+/*
+ * The HW decoder in the DoC ASIC's provides us a error syndrome,
+ * which we must convert to a standard syndrom usable by the generic
+ * Reed-Solomon library code.
+ *
+ * Fabrice Bellard figured this out in the old docecc code. I added
+ * some comments, improved a minor bit and converted it to make use
+ * of the generic Reed-Solomon libary. tglx
+ */
+static int doc_ecc_decode (struct rs_control *rs, uint8_t *data, uint8_t *ecc)
+{
+ int i, j, nerr, errpos[8];
+ uint8_t parity;
+ uint16_t ds[4], s[5], tmp, errval[8], syn[4];
+
+ /* Convert the ecc bytes into words */
+ ds[0] = ((ecc[4] & 0xff) >> 0) | ((ecc[5] & 0x03) << 8);
+ ds[1] = ((ecc[5] & 0xfc) >> 2) | ((ecc[2] & 0x0f) << 6);
+ ds[2] = ((ecc[2] & 0xf0) >> 4) | ((ecc[3] & 0x3f) << 4);
+ ds[3] = ((ecc[3] & 0xc0) >> 6) | ((ecc[0] & 0xff) << 2);
+ parity = ecc[1];
+
+ /* Initialize the syndrom buffer */
+ for (i = 0; i < NROOTS; i++)
+ s[i] = ds[0];
+ /*
+ * Evaluate
+ * s[i] = ds[3]x^3 + ds[2]x^2 + ds[1]x^1 + ds[0]
+ * where x = alpha^(FCR + i)
+ */
+ for(j = 1; j < NROOTS; j++) {
+ if(ds[j] == 0)
+ continue;
+ tmp = rs->index_of[ds[j]];
+ for(i = 0; i < NROOTS; i++)
+ s[i] ^= rs->alpha_to[rs_modnn(rs, tmp + (FCR + i) * j)];
+ }
+
+ /* Calc s[i] = s[i] / alpha^(v + i) */
+ for (i = 0; i < NROOTS; i++) {
+ if (syn[i])
+ syn[i] = rs_modnn(rs, rs->index_of[s[i]] + (NN - FCR - i));
+ }
+ /* Call the decoder library */
+ nerr = decode_rs16(rs, NULL, NULL, 1019, syn, 0, errpos, 0, errval);
+
+ /* Incorrectable errors ? */
+ if (nerr < 0)
+ return nerr;
+
+ /*
+ * Correct the errors. The bitpositions are a bit of magic,
+ * but they are given by the design of the de/encoder circuit
+ * in the DoC ASIC's.
+ */
+ for(i = 0;i < nerr; i++) {
+ int index, bitpos, pos = 1015 - errpos[i];
+ uint8_t val;
+ if (pos >= NB_DATA && pos < 1019)
+ continue;
+ if (pos < NB_DATA) {
+ /* extract bit position (MSB first) */
+ pos = 10 * (NB_DATA - 1 - pos) - 6;
+ /* now correct the following 10 bits. At most two bytes
+ can be modified since pos is even */
+ index = (pos >> 3) ^ 1;
+ bitpos = pos & 7;
+ if ((index >= 0 && index < SECTOR_SIZE) ||
+ index == (SECTOR_SIZE + 1)) {
+ val = (uint8_t) (errval[i] >> (2 + bitpos));
+ parity ^= val;
+ if (index < SECTOR_SIZE)
+ data[index] ^= val;
+ }
+ index = ((pos >> 3) + 1) ^ 1;
+ bitpos = (bitpos + 10) & 7;
+ if (bitpos == 0)
+ bitpos = 8;
+ if ((index >= 0 && index < SECTOR_SIZE) ||
+ index == (SECTOR_SIZE + 1)) {
+ val = (uint8_t)(errval[i] << (8 - bitpos));
+ parity ^= val;
+ if (index < SECTOR_SIZE)
+ data[index] ^= val;
+ }
+ }
+ }
+ /* If the parity is wrong, no rescue possible */
+ return parity ? -1 : nerr;
+}
+
static void DoC_Delay(struct doc_priv *doc, unsigned short cycles)
{
volatile char dummy;
@@ -139,7 +253,7 @@ static void DoC_Delay(struct doc_priv *doc, unsigned short cycles)
/* DOC_WaitReady: Wait for RDY line to be asserted by the flash chip */
static int _DoC_WaitReady(struct doc_priv *doc)
{
- unsigned long docptr = doc->virtadr;
+ void __iomem *docptr = doc->virtadr;
unsigned long timeo = jiffies + (HZ * 10);
if(debug) printk("_DoC_WaitReady...\n");
@@ -169,7 +283,7 @@ static int _DoC_WaitReady(struct doc_priv *doc)
static inline int DoC_WaitReady(struct doc_priv *doc)
{
- unsigned long docptr = doc->virtadr;
+ void __iomem *docptr = doc->virtadr;
int ret = 0;
if (DoC_is_MillenniumPlus(doc)) {
@@ -195,7 +309,7 @@ static void doc2000_write_byte(struct mtd_info *mtd, u_char datum)
{
struct nand_chip *this = mtd->priv;
struct doc_priv *doc = (void *)this->priv;
- unsigned long docptr = doc->virtadr;
+ void __iomem *docptr = doc->virtadr;
if(debug)printk("write_byte %02x\n", datum);
WriteDOC(datum, docptr, CDSNSlowIO);
@@ -206,7 +320,7 @@ static u_char doc2000_read_byte(struct mtd_info *mtd)
{
struct nand_chip *this = mtd->priv;
struct doc_priv *doc = (void *)this->priv;
- unsigned long docptr = doc->virtadr;
+ void __iomem *docptr = doc->virtadr;
u_char ret;
ReadDOC(docptr, CDSNSlowIO);
@@ -221,7 +335,7 @@ static void doc2000_writebuf(struct mtd_info *mtd,
{
struct nand_chip *this = mtd->priv;
struct doc_priv *doc = (void *)this->priv;
- unsigned long docptr = doc->virtadr;
+ void __iomem *docptr = doc->virtadr;
int i;
if (debug)printk("writebuf of %d bytes: ", len);
for (i=0; i < len; i++) {
@@ -237,7 +351,7 @@ static void doc2000_readbuf(struct mtd_info *mtd,
{
struct nand_chip *this = mtd->priv;
struct doc_priv *doc = (void *)this->priv;
- unsigned long docptr = doc->virtadr;
+ void __iomem *docptr = doc->virtadr;
int i;
if (debug)printk("readbuf of %d bytes: ", len);
@@ -252,7 +366,7 @@ static void doc2000_readbuf_dword(struct mtd_info *mtd,
{
struct nand_chip *this = mtd->priv;
struct doc_priv *doc = (void *)this->priv;
- unsigned long docptr = doc->virtadr;
+ void __iomem *docptr = doc->virtadr;
int i;
if (debug) printk("readbuf_dword of %d bytes: ", len);
@@ -273,7 +387,7 @@ static int doc2000_verifybuf(struct mtd_info *mtd,
{
struct nand_chip *this = mtd->priv;
struct doc_priv *doc = (void *)this->priv;
- unsigned long docptr = doc->virtadr;
+ void __iomem *docptr = doc->virtadr;
int i;
for (i=0; i < len; i++)
@@ -305,7 +419,7 @@ static uint16_t __init doc200x_ident_chip(struct mtd_info *mtd, int nr)
uint32_t dword;
uint8_t byte[4];
} ident;
- unsigned long docptr = doc->virtadr;
+ void __iomem *docptr = doc->virtadr;
doc200x_hwcontrol(mtd, NAND_CTL_SETCLE);
doc2000_write_byte(mtd, NAND_CMD_READID);
@@ -364,7 +478,7 @@ static void doc2001_write_byte(struct mtd_info *mtd, u_char datum)
{
struct nand_chip *this = mtd->priv;
struct doc_priv *doc = (void *)this->priv;
- unsigned long docptr = doc->virtadr;
+ void __iomem *docptr = doc->virtadr;
WriteDOC(datum, docptr, CDSNSlowIO);
WriteDOC(datum, docptr, Mil_CDSN_IO);
@@ -375,7 +489,7 @@ static u_char doc2001_read_byte(struct mtd_info *mtd)
{
struct nand_chip *this = mtd->priv;
struct doc_priv *doc = (void *)this->priv;
- unsigned long docptr = doc->virtadr;
+ void __iomem *docptr = doc->virtadr;
//ReadDOC(docptr, CDSNSlowIO);
/* 11.4.5 -- delay twice to allow extended length cycle */
@@ -390,7 +504,7 @@ static void doc2001_writebuf(struct mtd_info *mtd,
{
struct nand_chip *this = mtd->priv;
struct doc_priv *doc = (void *)this->priv;
- unsigned long docptr = doc->virtadr;
+ void __iomem *docptr = doc->virtadr;
int i;
for (i=0; i < len; i++)
@@ -404,7 +518,7 @@ static void doc2001_readbuf(struct mtd_info *mtd,
{
struct nand_chip *this = mtd->priv;
struct doc_priv *doc = (void *)this->priv;
- unsigned long docptr = doc->virtadr;
+ void __iomem *docptr = doc->virtadr;
int i;
/* Start read pipeline */
@@ -422,7 +536,7 @@ static int doc2001_verifybuf(struct mtd_info *mtd,
{
struct nand_chip *this = mtd->priv;
struct doc_priv *doc = (void *)this->priv;
- unsigned long docptr = doc->virtadr;
+ void __iomem *docptr = doc->virtadr;
int i;
/* Start read pipeline */
@@ -442,7 +556,7 @@ static u_char doc2001plus_read_byte(struct mtd_info *mtd)
{
struct nand_chip *this = mtd->priv;
struct doc_priv *doc = (void *)this->priv;
- unsigned long docptr = doc->virtadr;
+ void __iomem *docptr = doc->virtadr;
u_char ret;
ReadDOC(docptr, Mplus_ReadPipeInit);
@@ -457,7 +571,7 @@ static void doc2001plus_writebuf(struct mtd_info *mtd,
{
struct nand_chip *this = mtd->priv;
struct doc_priv *doc = (void *)this->priv;
- unsigned long docptr = doc->virtadr;
+ void __iomem *docptr = doc->virtadr;
int i;
if (debug)printk("writebuf of %d bytes: ", len);
@@ -474,7 +588,7 @@ static void doc2001plus_readbuf(struct mtd_info *mtd,
{
struct nand_chip *this = mtd->priv;
struct doc_priv *doc = (void *)this->priv;
- unsigned long docptr = doc->virtadr;
+ void __iomem *docptr = doc->virtadr;
int i;
if (debug)printk("readbuf of %d bytes: ", len);
@@ -504,7 +618,7 @@ static int doc2001plus_verifybuf(struct mtd_info *mtd,
{
struct nand_chip *this = mtd->priv;
struct doc_priv *doc = (void *)this->priv;
- unsigned long docptr = doc->virtadr;
+ void __iomem *docptr = doc->virtadr;
int i;
if (debug)printk("verifybuf of %d bytes: ", len);
@@ -530,7 +644,7 @@ static void doc2001plus_select_chip(struct mtd_info *mtd, int chip)
{
struct nand_chip *this = mtd->priv;
struct doc_priv *doc = (void *)this->priv;
- unsigned long docptr = doc->virtadr;
+ void __iomem *docptr = doc->virtadr;
int floor = 0;
if(debug)printk("select chip (%d)\n", chip);
@@ -556,7 +670,7 @@ static void doc200x_select_chip(struct mtd_info *mtd, int chip)
{
struct nand_chip *this = mtd->priv;
struct doc_priv *doc = (void *)this->priv;
- unsigned long docptr = doc->virtadr;
+ void __iomem *docptr = doc->virtadr;
int floor = 0;
if(debug)printk("select chip (%d)\n", chip);
@@ -583,7 +697,7 @@ static void doc200x_hwcontrol(struct mtd_info *mtd, int cmd)
{
struct nand_chip *this = mtd->priv;
struct doc_priv *doc = (void *)this->priv;
- unsigned long docptr = doc->virtadr;
+ void __iomem *docptr = doc->virtadr;
switch(cmd) {
case NAND_CTL_SETNCE:
@@ -621,7 +735,7 @@ static void doc2001plus_command (struct mtd_info *mtd, unsigned command, int col
{
struct nand_chip *this = mtd->priv;
struct doc_priv *doc = (void *)this->priv;
- unsigned long docptr = doc->virtadr;
+ void __iomem *docptr = doc->virtadr;
/*
* Must terminate write pipeline before sending any commands
@@ -725,7 +839,7 @@ static int doc200x_dev_ready(struct mtd_info *mtd)
{
struct nand_chip *this = mtd->priv;
struct doc_priv *doc = (void *)this->priv;
- unsigned long docptr = doc->virtadr;
+ void __iomem *docptr = doc->virtadr;
if (DoC_is_MillenniumPlus(doc)) {
/* 11.4.2 -- must NOP four times before checking FR/B# */
@@ -763,7 +877,7 @@ static void doc200x_enable_hwecc(struct mtd_info *mtd, int mode)
{
struct nand_chip *this = mtd->priv;
struct doc_priv *doc = (void *)this->priv;
- unsigned long docptr = doc->virtadr;
+ void __iomem *docptr = doc->virtadr;
/* Prime the ECC engine */
switch(mode) {
@@ -782,7 +896,7 @@ static void doc2001plus_enable_hwecc(struct mtd_info *mtd, int mode)
{
struct nand_chip *this = mtd->priv;
struct doc_priv *doc = (void *)this->priv;
- unsigned long docptr = doc->virtadr;
+ void __iomem *docptr = doc->virtadr;
/* Prime the ECC engine */
switch(mode) {
@@ -803,7 +917,7 @@ static int doc200x_calculate_ecc(struct mtd_info *mtd, const u_char *dat,
{
struct nand_chip *this = mtd->priv;
struct doc_priv *doc = (void *)this->priv;
- unsigned long docptr = doc->virtadr;
+ void __iomem *docptr = doc->virtadr;
int i;
int emptymatch = 1;
@@ -861,7 +975,7 @@ static int doc200x_correct_data(struct mtd_info *mtd, u_char *dat, u_char *read_
int i, ret = 0;
struct nand_chip *this = mtd->priv;
struct doc_priv *doc = (void *)this->priv;
- unsigned long docptr = doc->virtadr;
+ void __iomem *docptr = doc->virtadr;
volatile u_char dummy;
int emptymatch = 1;
@@ -914,7 +1028,7 @@ static int doc200x_correct_data(struct mtd_info *mtd, u_char *dat, u_char *read_
erased block, in which case the ECC will not come out right.
We'll suppress the error and tell the caller everything's
OK. Because it is. */
- if (!emptymatch) ret = doc_decode_ecc (dat, calc_ecc);
+ if (!emptymatch) ret = doc_ecc_decode (rs_decoder, dat, calc_ecc);
if (ret > 0)
printk(KERN_ERR "doc200x_correct_data corrected %d errors\n", ret);
}
@@ -1385,13 +1499,13 @@ static inline int __init doc_probe(unsigned long physadr)
struct mtd_info *mtd;
struct nand_chip *nand;
struct doc_priv *doc;
- unsigned long virtadr;
+ void __iomem *virtadr;
unsigned char save_control;
unsigned char tmp, tmpb, tmpc;
int reg, len, numchips;
int ret = 0;
- virtadr = (unsigned long)ioremap(physadr, DOC_IOREMAP_LEN);
+ virtadr = (void __iomem *)ioremap(physadr, DOC_IOREMAP_LEN);
if (!virtadr) {
printk(KERN_ERR "Diskonchip ioremap failed: 0x%x bytes at 0x%lx\n", DOC_IOREMAP_LEN, physadr);
return -EIO;
@@ -1518,7 +1632,7 @@ static inline int __init doc_probe(unsigned long physadr)
sizeof(struct nand_chip) +
sizeof(struct doc_priv) +
(2 * sizeof(struct nand_bbt_descr));
- mtd = kmalloc(len, GFP_KERNEL);
+ mtd = kmalloc(len, GFP_KERNEL);
if (!mtd) {
printk(KERN_ERR "DiskOnChip kmalloc (%d bytes) failed!\n", len);
ret = -ENOMEM;
@@ -1543,7 +1657,7 @@ static inline int __init doc_probe(unsigned long physadr)
nand->enable_hwecc = doc200x_enable_hwecc;
nand->calculate_ecc = doc200x_calculate_ecc;
nand->correct_data = doc200x_correct_data;
- //nand->data_buf
+
nand->autooob = &doc200x_oobinfo;
nand->eccmode = NAND_ECC_HW6_512;
nand->options = NAND_USE_FLASH_BBT | NAND_HWECC_SYNDROME;
@@ -1589,6 +1703,23 @@ fail:
return ret;
}
+static void release_nanddoc(void)
+{
+ struct mtd_info *mtd, *nextmtd;
+ struct nand_chip *nand;
+ struct doc_priv *doc;
+
+ for (mtd = doclist; mtd; mtd = nextmtd) {
+ nand = mtd->priv;
+ doc = (void *)nand->priv;
+
+ nextmtd = doc->nextdoc;
+ nand_release(mtd);
+ iounmap((void *)doc->virtadr);
+ kfree(mtd);
+ }
+}
+
int __init init_nanddoc(void)
{
int i;
@@ -1607,23 +1738,34 @@ int __init init_nanddoc(void)
printk(KERN_INFO "No valid DiskOnChip devices found\n");
return -ENODEV;
}
+
+ /* We could create the decoder on demand, if memory is a concern.
+ * This way we have it handy, if an error happens
+ *
+ * Symbolsize is 10 (bits)
+ * Primitve polynomial is x^10+x^3+1
+ * first consecutive root is 510
+ * primitve element to generate roots = 1
+ * generator polinomial degree = 4
+ */
+ rs_decoder = init_rs(10, 0x409, FCR, 1, NROOTS);
+ if (!rs_decoder) {
+ printk (KERN_ERR "DiskOnChip: Could not create a RS decoder\n");
+ release_nanddoc();
+ return -ENOMEM;
+ }
+
return 0;
}
void __exit cleanup_nanddoc(void)
{
- struct mtd_info *mtd, *nextmtd;
- struct nand_chip *nand;
- struct doc_priv *doc;
-
- for (mtd = doclist; mtd; mtd = nextmtd) {
- nand = mtd->priv;
- doc = (void *)nand->priv;
+ /* Cleanup the nand/DoC resources */
+ release_nanddoc();
- nextmtd = doc->nextdoc;
- nand_release(mtd);
- iounmap((void *)doc->virtadr);
- kfree(mtd);
+ /* Free the reed solomon resources */
+ if (rs_decoder) {
+ free_rs(rs_decoder);
}
}
diff --git a/drivers/mtd/nand/edb7312.c b/drivers/mtd/nand/edb7312.c
index 797794069103..805102e000bd 100644
--- a/drivers/mtd/nand/edb7312.c
+++ b/drivers/mtd/nand/edb7312.c
@@ -6,7 +6,7 @@
* Derived from drivers/mtd/nand/autcpu12.c
* Copyright (c) 2001 Thomas Gleixner (gleixner@autronix.de)
*
- * $Id: edb7312.c,v 1.8 2004/07/12 15:03:26 dwmw2 Exp $
+ * $Id: edb7312.c,v 1.10 2004/10/05 13:50:20 gleixner Exp $
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@@ -53,9 +53,9 @@ static struct mtd_info *ep7312_mtd = NULL;
* Module stuff
*/
-static int ep7312_fio_pbase = EP7312_FIO_PBASE;
-static int ep7312_pxdr = EP7312_PXDR;
-static int ep7312_pxddr = EP7312_PXDDR;
+static unsigned long ep7312_fio_pbase = EP7312_FIO_PBASE;
+static void __iomem * ep7312_pxdr = (void __iomem *) EP7312_PXDR;
+static void __iomem * ep7312_pxddr = (void __iomem *) EP7312_PXDDR;
#ifdef MODULE
MODULE_PARM(ep7312_fio_pbase, "i");
@@ -131,7 +131,7 @@ static int __init ep7312_init (void)
const char *part_type = 0;
int mtd_parts_nb = 0;
struct mtd_partition *mtd_parts = 0;
- int ep7312_fio_base;
+ void __iomem * ep7312_fio_base;
/* Allocate memory for MTD device structure and private data */
ep7312_mtd = kmalloc(sizeof(struct mtd_info) +
@@ -143,7 +143,7 @@ static int __init ep7312_init (void)
}
/* map physical adress */
- ep7312_fio_base = (unsigned long)ioremap(ep7312_fio_pbase, SZ_1K);
+ ep7312_fio_base = (void __iomem *)ioremap(ep7312_fio_pbase, SZ_1K);
if(!ep7312_fio_base) {
printk("ioremap EDB7312 NAND flash failed\n");
kfree(ep7312_mtd);
@@ -181,16 +181,7 @@ static int __init ep7312_init (void)
return -ENXIO;
}
- /* Allocate memory for internal data buffer */
- this->data_buf = kmalloc (sizeof(u_char) * (ep7312_mtd->oobblock + ep7312_mtd->oobsize), GFP_KERNEL);
- if (!this->data_buf) {
- printk("Unable to allocate NAND data buffer for EDB7312.\n");
- iounmap((void *)ep7312_fio_base);
- kfree (ep7312_mtd);
- return -ENOMEM;
- }
-
-#ifdef CONFIG_PARTITIONS
+#ifdef CONFIG_MTD_PARTITIONS
ep7312_mtd->name = "edb7312-nand";
mtd_parts_nb = parse_mtd_partitions(ep7312_mtd, part_probes,
&mtd_parts, 0);
@@ -221,8 +212,8 @@ static void __exit ep7312_cleanup (void)
{
struct nand_chip *this = (struct nand_chip *) &ep7312_mtd[1];
- /* Unregister the device */
- del_mtd_device (ep7312_mtd);
+ /* Release resources, unregister device */
+ nand_release (ap7312_mtd);
/* Free internal data buffer */
kfree (this->data_buf);
diff --git a/drivers/mtd/nand/h1910.c b/drivers/mtd/nand/h1910.c
new file mode 100644
index 000000000000..5d284f68d706
--- /dev/null
+++ b/drivers/mtd/nand/h1910.c
@@ -0,0 +1,208 @@
+/*
+ * drivers/mtd/nand/h1910.c
+ *
+ * Copyright (C) 2003 Joshua Wise (joshua@joshuawise.com)
+ *
+ * Derived from drivers/mtd/nand/edb7312.c
+ * Copyright (C) 2002 Marius Gröger (mag@sysgo.de)
+ * Copyright (c) 2001 Thomas Gleixner (gleixner@autronix.de)
+ *
+ * $Id: h1910.c,v 1.4 2004/10/05 13:50:20 gleixner Exp $
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Overview:
+ * This is a device driver for the NAND flash device found on the
+ * iPAQ h1910 board which utilizes the Samsung K9F2808 part. This is
+ * a 128Mibit (16MiB x 8 bits) NAND flash device.
+ */
+
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/partitions.h>
+#include <asm/io.h>
+#include <asm/arch/hardware.h> /* for CLPS7111_VIRT_BASE */
+#include <asm/sizes.h>
+#include <asm/arch/h1900-gpio.h>
+#include <asm/arch/ipaq.h>
+
+/*
+ * MTD structure for EDB7312 board
+ */
+static struct mtd_info *h1910_nand_mtd = NULL;
+
+/*
+ * Module stuff
+ */
+
+#ifdef CONFIG_MTD_PARTITIONS
+/*
+ * Define static partitions for flash device
+ */
+static struct mtd_partition partition_info[] = {
+ { name: "h1910 NAND Flash",
+ offset: 0,
+ size: 16*1024*1024 }
+};
+#define NUM_PARTITIONS 1
+
+#endif
+
+
+/*
+ * hardware specific access to control-lines
+ */
+static void h1910_hwcontrol(struct mtd_info *mtd, int cmd)
+{
+ struct nand_chip* this = (struct nand_chip *) (mtd->priv);
+
+ switch(cmd) {
+
+ case NAND_CTL_SETCLE:
+ this->IO_ADDR_R |= (1 << 2);
+ this->IO_ADDR_W |= (1 << 2);
+ break;
+ case NAND_CTL_CLRCLE:
+ this->IO_ADDR_R &= ~(1 << 2);
+ this->IO_ADDR_W &= ~(1 << 2);
+ break;
+
+ case NAND_CTL_SETALE:
+ this->IO_ADDR_R |= (1 << 3);
+ this->IO_ADDR_W |= (1 << 3);
+ break;
+ case NAND_CTL_CLRALE:
+ this->IO_ADDR_R &= ~(1 << 3);
+ this->IO_ADDR_W &= ~(1 << 3);
+ break;
+
+ case NAND_CTL_SETNCE:
+ break;
+ case NAND_CTL_CLRNCE:
+ break;
+ }
+}
+
+/*
+ * read device ready pin
+ */
+#if 0
+static int h1910_device_ready(struct mtd_info *mtd)
+{
+ return (GPLR(55) & GPIO_bit(55));
+}
+#endif
+
+/*
+ * Main initialization routine
+ */
+static int __init h1910_init (void)
+{
+ struct nand_chip *this;
+ const char *part_type = 0;
+ int mtd_parts_nb = 0;
+ struct mtd_partition *mtd_parts = 0;
+ void __iomem *nandaddr;
+
+ if (!machine_is_h1900())
+ return -ENODEV;
+
+ nandaddr = (void __iomem *)__ioremap(0x08000000, 0x1000, 0, 1);
+ if (!nandaddr) {
+ printk("Failed to ioremap nand flash.\n");
+ return -ENOMEM;
+ }
+
+ /* Allocate memory for MTD device structure and private data */
+ h1910_nand_mtd = kmalloc(sizeof(struct mtd_info) +
+ sizeof(struct nand_chip),
+ GFP_KERNEL);
+ if (!h1910_nand_mtd) {
+ printk("Unable to allocate h1910 NAND MTD device structure.\n");
+ iounmap ((void *) nandaddr);
+ return -ENOMEM;
+ }
+
+ /* Get pointer to private data */
+ this = (struct nand_chip *) (&h1910_nand_mtd[1]);
+
+ /* Initialize structures */
+ memset((char *) h1910_nand_mtd, 0, sizeof(struct mtd_info));
+ memset((char *) this, 0, sizeof(struct nand_chip));
+
+ /* Link the private data with the MTD structure */
+ h1910_nand_mtd->priv = this;
+
+ /*
+ * Enable VPEN
+ */
+ GPSR(37) = GPIO_bit(37);
+
+ /* insert callbacks */
+ this->IO_ADDR_R = nandaddr;
+ this->IO_ADDR_W = nandaddr;
+ this->hwcontrol = h1910_hwcontrol;
+ this->dev_ready = NULL; /* unknown whether that was correct or not so we will just do it like this */
+ /* 15 us command delay time */
+ this->chip_delay = 50;
+ this->eccmode = NAND_ECC_SOFT;
+ this->options = NAND_NO_AUTOINCR;
+
+ /* Scan to find existence of the device */
+ if (nand_scan (h1910_nand_mtd, 1)) {
+ printk(KERN_NOTICE "No NAND device - returning -ENXIO\n");
+ kfree (h1910_nand_mtd);
+ iounmap ((void *) nandaddr);
+ return -ENXIO;
+ }
+
+#ifdef CONFIG_MTD_CMDLINE_PARTS
+ mtd_parts_nb = parse_cmdline_partitions(h1910_nand_mtd, &mtd_parts,
+ "h1910-nand");
+ if (mtd_parts_nb > 0)
+ part_type = "command line";
+ else
+ mtd_parts_nb = 0;
+#endif
+ if (mtd_parts_nb == 0)
+ {
+ mtd_parts = partition_info;
+ mtd_parts_nb = NUM_PARTITIONS;
+ part_type = "static";
+ }
+
+ /* Register the partitions */
+ printk(KERN_NOTICE "Using %s partition definition\n", part_type);
+ add_mtd_partitions(h1910_nand_mtd, mtd_parts, mtd_parts_nb);
+
+ /* Return happy */
+ return 0;
+}
+module_init(h1910_init);
+
+/*
+ * Clean up routine
+ */
+static void __exit h1910_cleanup (void)
+{
+ struct nand_chip *this = (struct nand_chip *) &h1910_nand_mtd[1];
+
+ /* Release resources, unregister device */
+ nand_release (h1910_nand_mtd);
+
+ /* Release io resource */
+ iounmap ((void *) this->IO_ADDR_W);
+
+ /* Free the MTD device structure */
+ kfree (h1910_nand_mtd);
+}
+module_exit(h1910_cleanup);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Joshua Wise <joshua at joshuawise dot com>");
+MODULE_DESCRIPTION("NAND flash driver for iPAQ h1910");
diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
index ff6adf43f73a..181b95275352 100644
--- a/drivers/mtd/nand/nand_base.c
+++ b/drivers/mtd/nand/nand_base.c
@@ -24,6 +24,10 @@
*
* 05-19-2004 tglx: Basic support for Renesas AG-AND chips
*
+ * 09-24-2004 tglx: add support for hardware controllers (e.g. ECC) shared
+ * among multiple independend devices. Suggestions and initial patch
+ * from Ben Dooks <ben-mtd@fluff.org>
+ *
* Credits:
* David Woodhouse for adding multichip support
*
@@ -37,7 +41,7 @@
* The AG-AND chips have nice features for speed improvement,
* which are not supported yet. Read / program 4 pages in one go.
*
- * $Id: nand_base.c,v 1.115 2004/08/09 13:19:45 dwmw2 Exp $
+ * $Id: nand_base.c,v 1.121 2004/10/06 19:53:11 gleixner Exp $
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@@ -131,25 +135,31 @@ static int nand_verify_pages (struct mtd_info *mtd, struct nand_chip *this, int
#define nand_verify_pages(...) (0)
#endif
-static void nand_get_chip (struct nand_chip *this, struct mtd_info *mtd, int new_state);
+static void nand_get_device (struct nand_chip *this, struct mtd_info *mtd, int new_state);
/**
- * nand_release_chip - [GENERIC] release chip
+ * nand_release_device - [GENERIC] release chip
* @mtd: MTD device structure
*
* Deselect, release chip lock and wake up anyone waiting on the device
*/
-static void nand_release_chip (struct mtd_info *mtd)
+static void nand_release_device (struct mtd_info *mtd)
{
struct nand_chip *this = mtd->priv;
/* De-select the NAND device */
this->select_chip(mtd, -1);
+ /* Do we have a hardware controller ? */
+ if (this->controller) {
+ spin_lock(&this->controller->lock);
+ this->controller->active = NULL;
+ spin_unlock(&this->controller->lock);
+ }
/* Release the chip */
- spin_lock_bh (&this->chip_lock);
+ spin_lock (&this->chip_lock);
this->state = FL_READY;
wake_up (&this->wq);
- spin_unlock_bh (&this->chip_lock);
+ spin_unlock (&this->chip_lock);
}
/**
@@ -388,7 +398,7 @@ static int nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip)
chipnr = (int)(ofs >> this->chip_shift);
/* Grab the lock and see if the device is available */
- nand_get_chip (this, mtd, FL_READING);
+ nand_get_device (this, mtd, FL_READING);
/* Select the NAND device */
this->select_chip(mtd, chipnr);
@@ -410,7 +420,7 @@ static int nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip)
if (getchip) {
/* Deselect and wake up anyone waiting on the device */
- nand_release_chip(mtd);
+ nand_release_device(mtd);
}
return res;
@@ -533,8 +543,8 @@ static void nand_command (struct mtd_info *mtd, unsigned command, int column, in
if (page_addr != -1) {
this->write_byte(mtd, (unsigned char) (page_addr & 0xff));
this->write_byte(mtd, (unsigned char) ((page_addr >> 8) & 0xff));
- /* One more address cycle for higher density devices */
- if (this->chipsize & 0x0c000000)
+ /* One more address cycle for devices > 32MiB */
+ if (this->chipsize > (32 << 20))
this->write_byte(mtd, (unsigned char) ((page_addr >> 16) & 0x0f));
}
/* Latch in address */
@@ -689,15 +699,16 @@ static void nand_command_lp (struct mtd_info *mtd, unsigned command, int column,
}
/**
- * nand_get_chip - [GENERIC] Get chip for selected access
+ * nand_get_device - [GENERIC] Get chip for selected access
* @this: the nand chip descriptor
* @mtd: MTD device structure
* @new_state: the state which is requested
*
* Get the device and lock it for exclusive access
*/
-static void nand_get_chip (struct nand_chip *this, struct mtd_info *mtd, int new_state)
+static void nand_get_device (struct nand_chip *this, struct mtd_info *mtd, int new_state)
{
+ struct nand_chip *active = this;
DECLARE_WAITQUEUE (wait, current);
@@ -705,19 +716,29 @@ static void nand_get_chip (struct nand_chip *this, struct mtd_info *mtd, int new
* Grab the lock and see if the device is available
*/
retry:
- spin_lock_bh (&this->chip_lock);
-
- if (this->state == FL_READY) {
- this->state = new_state;
- spin_unlock_bh (&this->chip_lock);
- return;
+ /* Hardware controller shared among independend devices */
+ if (this->controller) {
+ spin_lock (&this->controller->lock);
+ if (this->controller->active)
+ active = this->controller->active;
+ else
+ this->controller->active = this;
+ spin_unlock (&this->controller->lock);
}
-
+
+ if (active == this) {
+ spin_lock (&this->chip_lock);
+ if (this->state == FL_READY) {
+ this->state = new_state;
+ spin_unlock (&this->chip_lock);
+ return;
+ }
+ }
set_current_state (TASK_UNINTERRUPTIBLE);
- add_wait_queue (&this->wq, &wait);
- spin_unlock_bh (&this->chip_lock);
+ add_wait_queue (&active->wq, &wait);
+ spin_unlock (&active->chip_lock);
schedule ();
- remove_wait_queue (&this->wq, &wait);
+ remove_wait_queue (&active->wq, &wait);
goto retry;
}
@@ -747,7 +768,6 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *this, int state)
* any case on any machine. */
ndelay (100);
- spin_lock_bh (&this->chip_lock);
if ((state == FL_ERASING) && (this->options & NAND_IS_AND))
this->cmdfunc (mtd, NAND_CMD_STATUS_MULTI, -1, -1);
else
@@ -755,24 +775,19 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *this, int state)
while (time_before(jiffies, timeo)) {
/* Check, if we were interrupted */
- if (this->state != state) {
- spin_unlock_bh (&this->chip_lock);
+ if (this->state != state)
return 0;
- }
+
if (this->dev_ready) {
if (this->dev_ready(mtd))
+ break;
+ } else {
+ if (this->read_byte(mtd) & NAND_STATUS_READY)
break;
}
- if (this->read_byte(mtd) & NAND_STATUS_READY)
- break;
-
- spin_unlock_bh (&this->chip_lock);
yield ();
- spin_lock_bh (&this->chip_lock);
}
status = (int) this->read_byte(mtd);
- spin_unlock_bh (&this->chip_lock);
-
return status;
}
@@ -1051,7 +1066,7 @@ static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len,
}
/* Grab the lock and see if the device is available */
- nand_get_chip (this, mtd ,FL_READING);
+ nand_get_device (this, mtd ,FL_READING);
/* use userspace supplied oobinfo, if zero */
if (oobsel == NULL)
@@ -1281,7 +1296,7 @@ static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len,
}
/* Deselect and wake up anyone waiting on the device */
- nand_release_chip(mtd);
+ nand_release_device(mtd);
/*
* Return success, if no ECC failures, else -EBADMSG
@@ -1328,7 +1343,7 @@ static int nand_read_oob (struct mtd_info *mtd, loff_t from, size_t len, size_t
}
/* Grab the lock and see if the device is available */
- nand_get_chip (this, mtd , FL_READING);
+ nand_get_device (this, mtd , FL_READING);
/* Select the NAND device */
this->select_chip(mtd, chipnr);
@@ -1379,7 +1394,7 @@ static int nand_read_oob (struct mtd_info *mtd, loff_t from, size_t len, size_t
}
/* Deselect and wake up anyone waiting on the device */
- nand_release_chip(mtd);
+ nand_release_device(mtd);
/* Return happy */
*retlen = len;
@@ -1413,7 +1428,7 @@ int nand_read_raw (struct mtd_info *mtd, uint8_t *buf, loff_t from, size_t len,
}
/* Grab the lock and see if the device is available */
- nand_get_chip (this, mtd , FL_READING);
+ nand_get_device (this, mtd , FL_READING);
this->select_chip (mtd, chip);
@@ -1442,7 +1457,7 @@ int nand_read_raw (struct mtd_info *mtd, uint8_t *buf, loff_t from, size_t len,
}
/* Deselect and wake up anyone waiting on the device */
- nand_release_chip(mtd);
+ nand_release_device(mtd);
return 0;
}
@@ -1564,7 +1579,7 @@ static int nand_write_ecc (struct mtd_info *mtd, loff_t to, size_t len,
}
/* Grab the lock and see if the device is available */
- nand_get_chip (this, mtd, FL_WRITING);
+ nand_get_device (this, mtd, FL_WRITING);
/* Calculate chipnr */
chipnr = (int)(to >> this->chip_shift);
@@ -1669,7 +1684,7 @@ cmp:
out:
/* Deselect and wake up anyone waiting on the device */
- nand_release_chip(mtd);
+ nand_release_device(mtd);
return ret;
}
@@ -1709,7 +1724,7 @@ static int nand_write_oob (struct mtd_info *mtd, loff_t to, size_t len, size_t *
}
/* Grab the lock and see if the device is available */
- nand_get_chip (this, mtd, FL_WRITING);
+ nand_get_device (this, mtd, FL_WRITING);
/* Select the NAND device */
this->select_chip(mtd, chipnr);
@@ -1771,7 +1786,7 @@ static int nand_write_oob (struct mtd_info *mtd, loff_t to, size_t len, size_t *
ret = 0;
out:
/* Deselect and wake up anyone waiting on the device */
- nand_release_chip(mtd);
+ nand_release_device(mtd);
return ret;
}
@@ -1838,7 +1853,7 @@ static int nand_writev_ecc (struct mtd_info *mtd, const struct kvec *vecs, unsig
}
/* Grab the lock and see if the device is available */
- nand_get_chip (this, mtd, FL_WRITING);
+ nand_get_device (this, mtd, FL_WRITING);
/* Get the current chip-nr */
chipnr = (int) (to >> this->chip_shift);
@@ -1952,7 +1967,7 @@ static int nand_writev_ecc (struct mtd_info *mtd, const struct kvec *vecs, unsig
ret = 0;
out:
/* Deselect and wake up anyone waiting on the device */
- nand_release_chip(mtd);
+ nand_release_device(mtd);
*retlen = written;
return ret;
@@ -2041,7 +2056,7 @@ int nand_erase_nand (struct mtd_info *mtd, struct erase_info *instr, int allowbb
instr->fail_addr = 0xffffffff;
/* Grab the lock and see if the device is available */
- nand_get_chip (this, mtd, FL_ERASING);
+ nand_get_device (this, mtd, FL_ERASING);
/* Shift to get first page */
page = (int) (instr->addr >> this->page_shift);
@@ -2112,7 +2127,7 @@ erase_exit:
mtd_erase_callback(instr);
/* Deselect and wake up anyone waiting on the device */
- nand_release_chip(mtd);
+ nand_release_device(mtd);
/* Return more or less happy */
return ret;
@@ -2127,43 +2142,13 @@ erase_exit:
static void nand_sync (struct mtd_info *mtd)
{
struct nand_chip *this = mtd->priv;
- DECLARE_WAITQUEUE (wait, current);
DEBUG (MTD_DEBUG_LEVEL3, "nand_sync: called\n");
-retry:
- /* Grab the spinlock */
- spin_lock_bh (&this->chip_lock);
-
- /* See what's going on */
- switch (this->state) {
- case FL_READY:
- case FL_SYNCING:
- this->state = FL_SYNCING;
- spin_unlock_bh (&this->chip_lock);
- break;
-
- default:
- /* Not an idle state */
- add_wait_queue (&this->wq, &wait);
- spin_unlock_bh (&this->chip_lock);
- schedule ();
-
- remove_wait_queue (&this->wq, &wait);
- goto retry;
- }
-
- /* Lock the device */
- spin_lock_bh (&this->chip_lock);
-
- /* Set the device to be ready again */
- if (this->state == FL_SYNCING) {
- this->state = FL_READY;
- wake_up (&this->wq);
- }
-
- /* Unlock the device */
- spin_unlock_bh (&this->chip_lock);
+ /* Grab the lock and see if the device is available */
+ nand_get_device (this, mtd, FL_SYNCING);
+ /* Release it and go back */
+ nand_release_device (mtd);
}
diff --git a/drivers/mtd/nand/nand_bbt.c b/drivers/mtd/nand/nand_bbt.c
index 2642e11516e7..77f08d1ee14f 100644
--- a/drivers/mtd/nand/nand_bbt.c
+++ b/drivers/mtd/nand/nand_bbt.c
@@ -6,7 +6,7 @@
*
* Copyright (C) 2004 Thomas Gleixner (tglx@linutronix.de)
*
- * $Id: nand_bbt.c,v 1.24 2004/06/28 08:25:35 gleixner Exp $
+ * $Id: nand_bbt.c,v 1.26 2004/10/05 13:50:20 gleixner Exp $
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@@ -114,6 +114,7 @@ static int check_pattern (uint8_t *buf, int len, int paglen, struct nand_bbt_des
* @num: the number of bbt descriptors to read
* @bits: number of bits per block
* @offs: offset in the memory table
+ * @reserved_block_code: Pattern to identify reserved blocks
*
* Read the bad block table starting from page.
*
@@ -796,7 +797,7 @@ int nand_scan_bbt (struct mtd_info *mtd, struct nand_bbt_descr *bd)
len = mtd->size >> (this->bbt_erase_shift + 2);
/* Allocate memory (2bit per block) */
- this->bbt = (uint8_t *) kmalloc (len, GFP_KERNEL);
+ this->bbt = kmalloc (len, GFP_KERNEL);
if (!this->bbt) {
printk (KERN_ERR "nand_scan_bbt: Out of memory\n");
return -ENOMEM;
diff --git a/drivers/mtd/nand/ppchameleonevb.c b/drivers/mtd/nand/ppchameleonevb.c
index 9c356a0a78ec..d024a5b9a1a8 100644
--- a/drivers/mtd/nand/ppchameleonevb.c
+++ b/drivers/mtd/nand/ppchameleonevb.c
@@ -6,7 +6,7 @@
* Derived from drivers/mtd/nand/edb7312.c
*
*
- * $Id: ppchameleonevb.c,v 1.2 2004/05/05 22:09:54 gleixner Exp $
+ * $Id: ppchameleonevb.c,v 1.4 2004/10/05 13:50:20 gleixner Exp $
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@@ -74,12 +74,6 @@ MODULE_PARM(ppchameleonevb_fio_pbase, "i");
__setup("ppchameleonevb_fio_pbase=",ppchameleonevb_fio_pbase);
#endif
-/* Internal buffers. Page buffer and oob buffer for one block */
-static u_char data_buf[2048 + 64];
-static u_char oob_buf[64 * 64];
-static u_char data_buf_evb[512 + 16];
-static u_char oob_buf_evb[16 * 32];
-
#ifdef CONFIG_MTD_PARTITIONS
/*
* Define static partitions for flash devices
@@ -196,8 +190,8 @@ static int __init ppchameleonevb_init (void)
const char *part_type = 0;
int mtd_parts_nb = 0;
struct mtd_partition *mtd_parts = 0;
- int ppchameleon_fio_base;
- int ppchameleonevb_fio_base;
+ void __iomem *ppchameleon_fio_base;
+ void __iomem *ppchameleonevb_fio_base;
/*********************************
@@ -205,15 +199,14 @@ static int __init ppchameleonevb_init (void)
*********************************/
/* Allocate memory for MTD device structure and private data */
ppchameleon_mtd = kmalloc(sizeof(struct mtd_info) +
- sizeof(struct nand_chip),
- GFP_KERNEL);
+ sizeof(struct nand_chip), GFP_KERNEL);
if (!ppchameleon_mtd) {
printk("Unable to allocate PPChameleon NAND MTD device structure.\n");
return -ENOMEM;
}
/* map physical address */
- ppchameleon_fio_base = (unsigned long)ioremap(ppchameleon_fio_pbase, SZ_4M);
+ ppchameleon_fio_base = (void __iomem *) ioremap(ppchameleon_fio_pbase, SZ_4M);
if(!ppchameleon_fio_base) {
printk("ioremap PPChameleon NAND flash failed\n");
kfree(ppchameleon_mtd);
@@ -264,10 +257,6 @@ static int __init ppchameleonevb_init (void)
/* ECC mode */
this->eccmode = NAND_ECC_SOFT;
- /* Set internal data buffer */
- this->data_buf = data_buf;
- this->oob_buf = oob_buf;
-
/* Scan to find existence of the device (it could not be mounted) */
if (nand_scan (ppchameleon_mtd, 1)) {
iounmap((void *)ppchameleon_fio_base);
@@ -309,15 +298,14 @@ nand_evb_init:
****************************/
/* Allocate memory for MTD device structure and private data */
ppchameleonevb_mtd = kmalloc(sizeof(struct mtd_info) +
- sizeof(struct nand_chip),
- GFP_KERNEL);
+ sizeof(struct nand_chip), GFP_KERNEL);
if (!ppchameleonevb_mtd) {
printk("Unable to allocate PPChameleonEVB NAND MTD device structure.\n");
return -ENOMEM;
}
/* map physical address */
- ppchameleonevb_fio_base = (unsigned long)ioremap(ppchameleonevb_fio_pbase, SZ_4M);
+ ppchameleonevb_fio_base = (void __iomem *)ioremap(ppchameleonevb_fio_pbase, SZ_4M);
if(!ppchameleonevb_fio_base) {
printk("ioremap PPChameleonEVB NAND flash failed\n");
kfree(ppchameleonevb_mtd);
@@ -349,7 +337,8 @@ nand_evb_init:
out_be32((volatile unsigned*)GPIO0_TSRH, in_be32((volatile unsigned*)GPIO0_TSRH) & 0xFFFFFFF0);
out_be32((volatile unsigned*)GPIO0_TSRL, in_be32((volatile unsigned*)GPIO0_TSRL) & 0x3FFFFFFF);
/* enable output driver */
- out_be32((volatile unsigned*)GPIO0_TCR, in_be32((volatile unsigned*)GPIO0_TCR) | NAND_EVB_nCE_GPIO_PIN | NAND_EVB_CLE_GPIO_PIN | NAND_EVB_ALE_GPIO_PIN);
+ out_be32((volatile unsigned*)GPIO0_TCR, in_be32((volatile unsigned*)GPIO0_TCR) | NAND_EVB_nCE_GPIO_PIN |
+ NAND_EVB_CLE_GPIO_PIN | NAND_EVB_ALE_GPIO_PIN);
#ifdef USE_READY_BUSY_PIN
/* three-state select */
out_be32((volatile unsigned*)GPIO0_TSRL, in_be32((volatile unsigned*)GPIO0_TSRL) & 0xFFFFFFFC);
@@ -359,7 +348,6 @@ nand_evb_init:
out_be32((volatile unsigned*)GPIO0_ISR1L, (in_be32((volatile unsigned*)GPIO0_ISR1L) & 0xFFFFFFFC) | 0x00000001);
#endif
-
/* insert callbacks */
this->IO_ADDR_R = ppchameleonevb_fio_base;
this->IO_ADDR_W = ppchameleonevb_fio_base;
@@ -372,10 +360,6 @@ nand_evb_init:
/* ECC mode */
this->eccmode = NAND_ECC_SOFT;
- /* Set internal data buffer */
- this->data_buf = data_buf_evb;
- this->oob_buf = oob_buf_evb;
-
/* Scan to find existence of the device */
if (nand_scan (ppchameleonevb_mtd, 1)) {
iounmap((void *)ppchameleonevb_fio_base);
@@ -412,15 +396,20 @@ module_init(ppchameleonevb_init);
*/
static void __exit ppchameleonevb_cleanup (void)
{
- struct nand_chip *this = (struct nand_chip *) &ppchameleonevb_mtd[1];
-
- /* Unregister the device */
- del_mtd_device (ppchameleonevb_mtd);
+ struct nand_chip *this;
- /* Free internal data buffer */
- kfree (this->data_buf);
+ /* Release resources, unregister device(s) */
+ nand_release (ppchameleon_mtd);
+ nand_release (ppchameleonevb_mtd);
+
+ /* Release iomaps */
+ this = (struct nand_chip *) &ppchameleon_mtd[1];
+ iounmap((void *) this->IO_ADDR_R;
+ this = (struct nand_chip *) &ppchameleonevb_mtd[1];
+ iounmap((void *) this->IO_ADDR_R;
/* Free the MTD device structure */
+ kfree (ppchameleon_mtd);
kfree (ppchameleonevb_mtd);
}
module_exit(ppchameleonevb_cleanup);
diff --git a/drivers/mtd/nand/rtc_from4.c b/drivers/mtd/nand/rtc_from4.c
new file mode 100644
index 000000000000..2d65fa7ce0d1
--- /dev/null
+++ b/drivers/mtd/nand/rtc_from4.c
@@ -0,0 +1,561 @@
+/*
+ * drivers/mtd/nand/rtc_from4.c
+ *
+ * Copyright (C) 2004 Red Hat, Inc.
+ *
+ * Derived from drivers/mtd/nand/spia.c
+ * Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com)
+ *
+ * $Id: rtc_from4.c,v 1.6 2004/10/05 22:11:46 gleixner Exp $
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Overview:
+ * This is a device driver for the AG-AND flash device found on the
+ * Renesas Technology Corp. Flash ROM 4-slot interface board (FROM_BOARD4),
+ * which utilizes the Renesas HN29V1G91T-30 part.
+ * This chip is a 1 GBibit (128MiB x 8 bits) AG-AND flash device.
+ */
+
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/rslib.h>
+#include <linux/module.h>
+#include <linux/mtd/compatmac.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/partitions.h>
+#include <asm/io.h>
+
+/*
+ * MTD structure for Renesas board
+ */
+static struct mtd_info *rtc_from4_mtd = NULL;
+
+#define RTC_FROM4_MAX_CHIPS 2
+
+/* HS77x9 processor register defines */
+#define SH77X9_BCR1 ((volatile unsigned short *)(0xFFFFFF60))
+#define SH77X9_BCR2 ((volatile unsigned short *)(0xFFFFFF62))
+#define SH77X9_WCR1 ((volatile unsigned short *)(0xFFFFFF64))
+#define SH77X9_WCR2 ((volatile unsigned short *)(0xFFFFFF66))
+#define SH77X9_MCR ((volatile unsigned short *)(0xFFFFFF68))
+#define SH77X9_PCR ((volatile unsigned short *)(0xFFFFFF6C))
+#define SH77X9_FRQCR ((volatile unsigned short *)(0xFFFFFF80))
+
+/*
+ * Values specific to the Renesas Technology Corp. FROM_BOARD4 (used with HS77x9 processor)
+ */
+/* Address where flash is mapped */
+#define RTC_FROM4_FIO_BASE 0x14000000
+
+/* CLE and ALE are tied to address lines 5 & 4, respectively */
+#define RTC_FROM4_CLE (1 << 5)
+#define RTC_FROM4_ALE (1 << 4)
+
+/* address lines A24-A22 used for chip selection */
+#define RTC_FROM4_NAND_ADDR_SLOT3 (0x00800000)
+#define RTC_FROM4_NAND_ADDR_SLOT4 (0x00C00000)
+#define RTC_FROM4_NAND_ADDR_FPGA (0x01000000)
+/* mask address lines A24-A22 used for chip selection */
+#define RTC_FROM4_NAND_ADDR_MASK (RTC_FROM4_NAND_ADDR_SLOT3 | RTC_FROM4_NAND_ADDR_SLOT4 | RTC_FROM4_NAND_ADDR_FPGA)
+
+/* FPGA status register for checking device ready (bit zero) */
+#define RTC_FROM4_FPGA_SR (RTC_FROM4_NAND_ADDR_FPGA | 0x00000002)
+#define RTC_FROM4_DEVICE_READY 0x0001
+
+/* FPGA Reed-Solomon ECC Control register */
+
+#define RTC_FROM4_RS_ECC_CTL (RTC_FROM4_NAND_ADDR_FPGA | 0x00000050)
+#define RTC_FROM4_RS_ECC_CTL_CLR (1 << 7)
+#define RTC_FROM4_RS_ECC_CTL_GEN (1 << 6)
+#define RTC_FROM4_RS_ECC_CTL_FD_E (1 << 5)
+
+/* FPGA Reed-Solomon ECC code base */
+#define RTC_FROM4_RS_ECC (RTC_FROM4_NAND_ADDR_FPGA | 0x00000060)
+#define RTC_FROM4_RS_ECCN (RTC_FROM4_NAND_ADDR_FPGA | 0x00000080)
+
+/* FPGA Reed-Solomon ECC check register */
+#define RTC_FROM4_RS_ECC_CHK (RTC_FROM4_NAND_ADDR_FPGA | 0x00000070)
+#define RTC_FROM4_RS_ECC_CHK_ERROR (1 << 7)
+
+/* Undefine for software ECC */
+#define RTC_FROM4_HWECC 1
+
+/*
+ * Module stuff
+ */
+static void __iomem *rtc_from4_fio_base = P2SEGADDR(RTC_FROM4_FIO_BASE);
+
+MODULE_PARM(rtc_from4_fio_base, "i");
+
+const static struct mtd_partition partition_info[] = {
+ {
+ .name = "Renesas flash partition 1",
+ .offset = 0,
+ .size = MTDPART_SIZ_FULL
+ },
+};
+#define NUM_PARTITIONS 1
+
+/*
+ * hardware specific flash bbt decriptors
+ * Note: this is to allow debugging by disabling
+ * NAND_BBT_CREATE and/or NAND_BBT_WRITE
+ *
+ */
+static uint8_t bbt_pattern[] = {'B', 'b', 't', '0' };
+static uint8_t mirror_pattern[] = {'1', 't', 'b', 'B' };
+
+static struct nand_bbt_descr rtc_from4_bbt_main_descr = {
+ .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
+ | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP,
+ .offs = 40,
+ .len = 4,
+ .veroffs = 44,
+ .maxblocks = 4,
+ .pattern = bbt_pattern
+};
+
+static struct nand_bbt_descr rtc_from4_bbt_mirror_descr = {
+ .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
+ | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP,
+ .offs = 40,
+ .len = 4,
+ .veroffs = 44,
+ .maxblocks = 4,
+ .pattern = mirror_pattern
+};
+
+
+
+#ifdef RTC_FROM4_HWECC
+
+/* the Reed Solomon control structure */
+static struct rs_control *rs_decoder;
+
+/*
+ * hardware specific Out Of Band information
+ */
+static struct nand_oobinfo rtc_from4_nand_oobinfo = {
+ .useecc = MTD_NANDECC_AUTOPLACE,
+ .eccbytes = 32,
+ .eccpos = {
+ 0, 1, 2, 3, 4, 5, 6, 7,
+ 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23,
+ 24, 25, 26, 27, 28, 29, 30, 31},
+ .oobfree = { {32, 32} }
+};
+
+/* Aargh. I missed the reversed bit order, when I
+ * was talking to Renesas about the FPGA.
+ *
+ * The table is used for bit reordering and inversion
+ * of the ecc byte which we get from the FPGA
+ */
+static uint8_t revbits[256] = {
+ 0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0,
+ 0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0,
+ 0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8,
+ 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8,
+ 0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4,
+ 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4,
+ 0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec,
+ 0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc,
+ 0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2,
+ 0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2,
+ 0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea,
+ 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa,
+ 0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6,
+ 0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6,
+ 0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee,
+ 0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe,
+ 0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1,
+ 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1,
+ 0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9,
+ 0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9,
+ 0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5,
+ 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5,
+ 0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed,
+ 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd,
+ 0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3,
+ 0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3,
+ 0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb,
+ 0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb,
+ 0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7,
+ 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7,
+ 0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef,
+ 0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff,
+};
+
+#endif
+
+
+
+/*
+ * rtc_from4_hwcontrol - hardware specific access to control-lines
+ * @mtd: MTD device structure
+ * @cmd: hardware control command
+ *
+ * Address lines (A5 and A4) are used to control Command and Address Latch
+ * Enable on this board, so set the read/write address appropriately.
+ *
+ * Chip Enable is also controlled by the Chip Select (CS5) and
+ * Address lines (A24-A22), so no action is required here.
+ *
+ */
+static void rtc_from4_hwcontrol(struct mtd_info *mtd, int cmd)
+{
+ struct nand_chip* this = (struct nand_chip *) (mtd->priv);
+
+ switch(cmd) {
+
+ case NAND_CTL_SETCLE:
+ this->IO_ADDR_W = (void __iomem *)((unsigned long)this->IO_ADDR_W | RTC_FROM4_CLE);
+ break;
+ case NAND_CTL_CLRCLE:
+ this->IO_ADDR_W = (void __iomem *)((unsigned long)this->IO_ADDR_W & ~RTC_FROM4_CLE);
+ break;
+
+ case NAND_CTL_SETALE:
+ this->IO_ADDR_W = (void __iomem *)((unsigned long)this->IO_ADDR_W | RTC_FROM4_ALE);
+ break;
+ case NAND_CTL_CLRALE:
+ this->IO_ADDR_W = (void __iomem *)((unsigned long)this->IO_ADDR_W & ~RTC_FROM4_ALE);
+ break;
+
+ case NAND_CTL_SETNCE:
+ break;
+ case NAND_CTL_CLRNCE:
+ break;
+
+ }
+}
+
+
+/*
+ * rtc_from4_nand_select_chip - hardware specific chip select
+ * @mtd: MTD device structure
+ * @chip: Chip to select (0 == slot 3, 1 == slot 4)
+ *
+ * The chip select is based on address lines A24-A22.
+ * This driver uses flash slots 3 and 4 (A23-A22).
+ *
+ */
+static void rtc_from4_nand_select_chip(struct mtd_info *mtd, int chip)
+{
+ struct nand_chip *this = mtd->priv;
+
+ this->IO_ADDR_R = (void __iomem *)((unsigned long)this->IO_ADDR_R & ~RTC_FROM4_NAND_ADDR_MASK);
+ this->IO_ADDR_W = (void __iomem *)((unsigned long)this->IO_ADDR_W & ~RTC_FROM4_NAND_ADDR_MASK);
+
+ switch(chip) {
+
+ case 0: /* select slot 3 chip */
+ this->IO_ADDR_R = (void __iomem *)((unsigned long)this->IO_ADDR_R | RTC_FROM4_NAND_ADDR_SLOT3);
+ this->IO_ADDR_W = (void __iomem *)((unsigned long)this->IO_ADDR_W | RTC_FROM4_NAND_ADDR_SLOT3);
+ break;
+ case 1: /* select slot 4 chip */
+ this->IO_ADDR_R = (void __iomem *)((unsigned long)this->IO_ADDR_R | RTC_FROM4_NAND_ADDR_SLOT4);
+ this->IO_ADDR_W = (void __iomem *)((unsigned long)this->IO_ADDR_W | RTC_FROM4_NAND_ADDR_SLOT4);
+ break;
+
+ }
+}
+
+
+
+/*
+ * rtc_from4_nand_device_ready - hardware specific ready/busy check
+ * @mtd: MTD device structure
+ *
+ * This board provides the Ready/Busy state in the status register
+ * of the FPGA. Bit zero indicates the RDY(1)/BSY(0) signal.
+ *
+ */
+static int rtc_from4_nand_device_ready(struct mtd_info *mtd)
+{
+ unsigned short status;
+
+ status = *((volatile unsigned short *)(rtc_from4_fio_base + RTC_FROM4_FPGA_SR));
+
+ return (status & RTC_FROM4_DEVICE_READY);
+
+}
+
+#ifdef RTC_FROM4_HWECC
+/*
+ * rtc_from4_enable_hwecc - hardware specific hardware ECC enable function
+ * @mtd: MTD device structure
+ * @mode: I/O mode; read or write
+ *
+ * enable hardware ECC for data read or write
+ *
+ */
+static void rtc_from4_enable_hwecc(struct mtd_info *mtd, int mode)
+{
+ volatile unsigned short * rs_ecc_ctl = (volatile unsigned short *)(rtc_from4_fio_base + RTC_FROM4_RS_ECC_CTL);
+ unsigned short status;
+
+ switch (mode) {
+ case NAND_ECC_READ :
+ status = RTC_FROM4_RS_ECC_CTL_CLR
+ | RTC_FROM4_RS_ECC_CTL_FD_E;
+
+ *rs_ecc_ctl = status;
+ break;
+
+ case NAND_ECC_READSYN :
+ status = 0x00;
+
+ *rs_ecc_ctl = status;
+ break;
+
+ case NAND_ECC_WRITE :
+ status = RTC_FROM4_RS_ECC_CTL_CLR
+ | RTC_FROM4_RS_ECC_CTL_GEN
+ | RTC_FROM4_RS_ECC_CTL_FD_E;
+
+ *rs_ecc_ctl = status;
+ break;
+
+ default:
+ BUG();
+ break;
+ }
+
+}
+
+/*
+ * rtc_from4_calculate_ecc - hardware specific code to read ECC code
+ * @mtd: MTD device structure
+ * @dat: buffer containing the data to generate ECC codes
+ * @ecc_code ECC codes calculated
+ *
+ * The ECC code is calculated by the FPGA. All we have to do is read the values
+ * from the FPGA registers.
+ *
+ * Note: We read from the inverted registers, since data is inverted before
+ * the code is calculated. So all 0xff data (blank page) results in all 0xff rs code
+ *
+ */
+static void rtc_from4_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code)
+{
+ volatile unsigned short * rs_eccn = (volatile unsigned short *)(rtc_from4_fio_base + RTC_FROM4_RS_ECCN);
+ unsigned short value;
+ int i;
+
+ for (i = 0; i < 8; i++) {
+ value = *rs_eccn;
+ ecc_code[i] = (unsigned char)value;
+ rs_eccn++;
+ }
+ ecc_code[7] |= 0x0f; /* set the last four bits (not used) */
+}
+
+/*
+ * rtc_from4_correct_data - hardware specific code to correct data using ECC code
+ * @mtd: MTD device structure
+ * @buf: buffer containing the data to generate ECC codes
+ * @ecc1 ECC codes read
+ * @ecc2 ECC codes calculated
+ *
+ * The FPGA tells us fast, if there's an error or not. If no, we go back happy
+ * else we read the ecc results from the fpga and call the rs library to decode
+ * and hopefully correct the error
+ *
+ * For now I use the code, which we read from the FLASH to use the RS lib,
+ * as the syndrom conversion has a unresolved issue.
+ */
+static int rtc_from4_correct_data(struct mtd_info *mtd, const u_char *buf, u_char *ecc1, u_char *ecc2)
+{
+ int i, j, res;
+ unsigned short status;
+ uint16_t par[6], syn[6], tmp;
+ uint8_t ecc[8];
+ volatile unsigned short *rs_ecc;
+
+ status = *((volatile unsigned short *)(rtc_from4_fio_base + RTC_FROM4_RS_ECC_CHK));
+
+ if (!(status & RTC_FROM4_RS_ECC_CHK_ERROR)) {
+ return 0;
+ }
+
+ /* Read the syndrom pattern from the FPGA and correct the bitorder */
+ rs_ecc = (volatile unsigned short *)(rtc_from4_fio_base + RTC_FROM4_RS_ECC);
+ for (i = 0; i < 8; i++) {
+ ecc[i] = revbits[(*rs_ecc) & 0xFF];
+ rs_ecc++;
+ }
+
+ /* convert into 6 10bit syndrome fields */
+ par[5] = rs_decoder->index_of[(((uint16_t)ecc[0] >> 0) & 0x0ff) |
+ (((uint16_t)ecc[1] << 8) & 0x300)];
+ par[4] = rs_decoder->index_of[(((uint16_t)ecc[1] >> 2) & 0x03f) |
+ (((uint16_t)ecc[2] << 6) & 0x3c0)];
+ par[3] = rs_decoder->index_of[(((uint16_t)ecc[2] >> 4) & 0x00f) |
+ (((uint16_t)ecc[3] << 4) & 0x3f0)];
+ par[2] = rs_decoder->index_of[(((uint16_t)ecc[3] >> 6) & 0x003) |
+ (((uint16_t)ecc[4] << 2) & 0x3fc)];
+ par[1] = rs_decoder->index_of[(((uint16_t)ecc[5] >> 0) & 0x0ff) |
+ (((uint16_t)ecc[6] << 8) & 0x300)];
+ par[0] = (((uint16_t)ecc[6] >> 2) & 0x03f) | (((uint16_t)ecc[7] << 6) & 0x3c0);
+
+ /* Convert to computable syndrome */
+ for (i = 0; i < 6; i++) {
+ syn[i] = par[0];
+ for (j = 1; j < 6; j++)
+ if (par[j] != rs_decoder->nn)
+ syn[i] ^= rs_decoder->alpha_to[rs_modnn(rs_decoder, par[j] + i * j)];
+
+ /* Convert to index form */
+ syn[i] = rs_decoder->index_of[syn[i]];
+ }
+
+ /* Let the library code do its magic.*/
+ res = decode_rs8(rs_decoder, buf, par, 512, syn, 0, NULL, 0xff, NULL);
+ if (res > 0) {
+ DEBUG (MTD_DEBUG_LEVEL0, "rtc_from4_correct_data: "
+ "ECC corrected %d errors on read\n", res);
+ }
+ return res;
+}
+#endif
+
+/*
+ * Main initialization routine
+ */
+int __init rtc_from4_init (void)
+{
+ struct nand_chip *this;
+ unsigned short bcr1, bcr2, wcr2;
+
+ /* Allocate memory for MTD device structure and private data */
+ rtc_from4_mtd = kmalloc(sizeof(struct mtd_info) + sizeof (struct nand_chip),
+ GFP_KERNEL);
+ if (!rtc_from4_mtd) {
+ printk ("Unable to allocate Renesas NAND MTD device structure.\n");
+ return -ENOMEM;
+ }
+
+ /* Get pointer to private data */
+ this = (struct nand_chip *) (&rtc_from4_mtd[1]);
+
+ /* Initialize structures */
+ memset((char *) rtc_from4_mtd, 0, sizeof(struct mtd_info));
+ memset((char *) this, 0, sizeof(struct nand_chip));
+
+ /* Link the private data with the MTD structure */
+ rtc_from4_mtd->priv = this;
+
+ /* set area 5 as PCMCIA mode to clear the spec of tDH(Data hold time;9ns min) */
+ bcr1 = *SH77X9_BCR1 & ~0x0002;
+ bcr1 |= 0x0002;
+ *SH77X9_BCR1 = bcr1;
+
+ /* set */
+ bcr2 = *SH77X9_BCR2 & ~0x0c00;
+ bcr2 |= 0x0800;
+ *SH77X9_BCR2 = bcr2;
+
+ /* set area 5 wait states */
+ wcr2 = *SH77X9_WCR2 & ~0x1c00;
+ wcr2 |= 0x1c00;
+ *SH77X9_WCR2 = wcr2;
+
+ /* Set address of NAND IO lines */
+ this->IO_ADDR_R = rtc_from4_fio_base;
+ this->IO_ADDR_W = rtc_from4_fio_base;
+ /* Set address of hardware control function */
+ this->hwcontrol = rtc_from4_hwcontrol;
+ /* Set address of chip select function */
+ this->select_chip = rtc_from4_nand_select_chip;
+ /* command delay time (in us) */
+ this->chip_delay = 100;
+ /* return the status of the Ready/Busy line */
+ this->dev_ready = rtc_from4_nand_device_ready;
+
+#ifdef RTC_FROM4_HWECC
+ printk(KERN_INFO "rtc_from4_init: using hardware ECC detection.\n");
+
+ this->eccmode = NAND_ECC_HW8_512;
+ this->options |= NAND_HWECC_SYNDROME;
+ /* set the nand_oobinfo to support FPGA H/W error detection */
+ this->autooob = &rtc_from4_nand_oobinfo;
+ this->enable_hwecc = rtc_from4_enable_hwecc;
+ this->calculate_ecc = rtc_from4_calculate_ecc;
+ this->correct_data = rtc_from4_correct_data;
+#else
+ printk(KERN_INFO "rtc_from4_init: using software ECC detection.\n");
+
+ this->eccmode = NAND_ECC_SOFT;
+#endif
+
+ /* set the bad block tables to support debugging */
+ this->bbt_td = &rtc_from4_bbt_main_descr;
+ this->bbt_md = &rtc_from4_bbt_mirror_descr;
+
+ /* Scan to find existence of the device */
+ if (nand_scan(rtc_from4_mtd, RTC_FROM4_MAX_CHIPS)) {
+ kfree(rtc_from4_mtd);
+ return -ENXIO;
+ }
+
+ /* Register the partitions */
+ add_mtd_partitions(rtc_from4_mtd, partition_info, NUM_PARTITIONS);
+
+#ifdef RTC_FROM4_HWECC
+ /* We could create the decoder on demand, if memory is a concern.
+ * This way we have it handy, if an error happens
+ *
+ * Symbolsize is 10 (bits)
+ * Primitve polynomial is x^10+x^3+1
+ * first consecutive root is 0
+ * primitve element to generate roots = 1
+ * generator polinomial degree = 6
+ */
+ rs_decoder = init_rs(10, 0x409, 0, 1, 6);
+ if (!rs_decoder) {
+ printk (KERN_ERR "Could not create a RS decoder\n");
+ nand_release(rtc_from4_mtd);
+ kfree(rtc_from4_mtd);
+ return -ENOMEM;
+ }
+#endif
+ /* Return happy */
+ return 0;
+}
+module_init(rtc_from4_init);
+
+
+/*
+ * Clean up routine
+ */
+#ifdef MODULE
+static void __exit rtc_from4_cleanup (void)
+{
+ /* Release resource, unregister partitions */
+ nand_release(rtc_from4_mtd);
+
+ /* Free the MTD device structure */
+ kfree (rtc_from4_mtd);
+
+#ifdef RTC_FROM4_HWECC
+ /* Free the reed solomon resources */
+ if (rs_decoder) {
+ free_rs(rs_decoder);
+ }
+#endif
+}
+module_exit(rtc_from4_cleanup);
+#endif
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("d.marlin <dmarlin@redhat.com");
+MODULE_DESCRIPTION("Board-specific glue layer for AG-AND flash on Renesas FROM_BOARD4");
+
diff --git a/drivers/mtd/nand/s3c2410.c b/drivers/mtd/nand/s3c2410.c
new file mode 100644
index 000000000000..d11fe47c9e40
--- /dev/null
+++ b/drivers/mtd/nand/s3c2410.c
@@ -0,0 +1,704 @@
+/* linux/drivers/mtd/nand/s3c2410.c
+ *
+ * Copyright (c) 2004 Simtec Electronics
+ * Ben Dooks <ben@simtec.co.uk>
+ *
+ * Samsung S3C2410 NAND driver
+ *
+ * Changelog:
+ * 21-Sep-2004 BJD Initial version
+ * 23-Sep-2004 BJD Mulitple device support
+ * 28-Sep-2004 BJD Fixed ECC placement for Hardware mode
+ * 12-Oct-2004 BJD Fixed errors in use of platform data
+ *
+ * $Id: s3c2410.c,v 1.5 2004/10/12 10:10:15 bjd Exp $
+ *
+ * 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#include <config/mtd/nand/s3c2410/hwecc.h>
+#include <config/mtd/nand/s3c2410/debug.h>
+
+#ifdef CONFIG_MTD_NAND_S3C2410_DEBUG
+#define DEBUG
+#endif
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/ioport.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/nand_ecc.h>
+#include <linux/mtd/partitions.h>
+
+#include <asm/io.h>
+#include <asm/mach-types.h>
+#include <asm/hardware/clock.h>
+
+#include <asm/arch/regs-nand.h>
+#include <asm/arch/nand.h>
+
+#define PFX "s3c2410-nand: "
+
+#ifdef CONFIG_MTD_NAND_S3C2410_HWECC
+static int hardware_ecc = 1;
+#else
+static int hardware_ecc = 0;
+#endif
+
+/* new oob placement block for use with hardware ecc generation
+ */
+
+static struct nand_oobinfo nand_hw_eccoob = {
+ .useecc = MTD_NANDECC_AUTOPLACE,
+ .eccbytes = 3,
+ .eccpos = {0, 1, 2 },
+ .oobfree = { {8, 8} }
+};
+
+/* controller and mtd information */
+
+struct s3c2410_nand_info;
+
+struct s3c2410_nand_mtd {
+ struct mtd_info mtd;
+ struct nand_chip chip;
+ struct s3c2410_nand_set *set;
+ struct s3c2410_nand_info *info;
+ int scan_res;
+};
+
+/* overview of the s3c2410 nand state */
+
+struct s3c2410_nand_info {
+ /* mtd info */
+ struct nand_hw_control controller;
+ struct s3c2410_nand_mtd *mtds;
+ struct s3c2410_platform_nand *platform;
+
+ /* device info */
+ struct device *device;
+ struct resource *area;
+ struct clk *clk;
+ void *regs;
+ int mtd_count;
+};
+
+/* conversion functions */
+
+static struct s3c2410_nand_mtd *s3c2410_nand_mtd_toours(struct mtd_info *mtd)
+{
+ return container_of(mtd, struct s3c2410_nand_mtd, mtd);
+}
+
+static struct s3c2410_nand_info *s3c2410_nand_mtd_toinfo(struct mtd_info *mtd)
+{
+ return s3c2410_nand_mtd_toours(mtd)->info;
+}
+
+static struct s3c2410_nand_info *to_nand_info(struct device *dev)
+{
+ return (struct s3c2410_nand_info *)dev_get_drvdata(dev);
+}
+
+static struct s3c2410_platform_nand *to_nand_plat(struct device *dev)
+{
+ return (struct s3c2410_platform_nand *)dev->platform_data;
+}
+
+/* timing calculations */
+
+#define NS_IN_KHZ 10000000
+
+static int s3c2410_nand_calc_rate(int wanted, unsigned long clk, int max)
+{
+ int result;
+
+ result = (wanted * NS_IN_KHZ) / clk;
+ result++;
+
+ pr_debug("result %d from %ld, %d\n", result, clk, wanted);
+
+ if (result > max) {
+ printk("%d ns is too big for current clock rate %ld\n",
+ wanted, clk);
+ return -1;
+ }
+
+ if (result < 1)
+ result = 1;
+
+ return result;
+}
+
+#define to_ns(ticks,clk) (((clk) * (ticks)) / NS_IN_KHZ)
+
+/* controller setup */
+
+static int s3c2410_nand_inithw(struct s3c2410_nand_info *info,
+ struct device *dev)
+{
+ struct s3c2410_platform_nand *plat = to_nand_plat(dev);
+ unsigned int tacls, twrph0, twrph1;
+ unsigned long clkrate = clk_get_rate(info->clk);
+ unsigned long cfg;
+
+ /* calculate the timing information for the controller */
+
+ if (plat != NULL) {
+ tacls = s3c2410_nand_calc_rate(plat->tacls, clkrate, 8);
+ twrph0 = s3c2410_nand_calc_rate(plat->twrph0, clkrate, 8);
+ twrph1 = s3c2410_nand_calc_rate(plat->twrph0, clkrate, 8);
+ } else {
+ /* default timings */
+ tacls = 8;
+ twrph0 = 8;
+ twrph1 = 8;
+ }
+
+ if (tacls < 0 || twrph0 < 0 || twrph1 < 0) {
+ printk(KERN_ERR PFX "cannot get timings suitable for board\n");
+ return -EINVAL;
+ }
+
+ printk(KERN_INFO PFX "timing: Tacls %ldns, Twrph0 %ldns, Twrph1 %ldns\n",
+ to_ns(tacls, clkrate),
+ to_ns(twrph0, clkrate),
+ to_ns(twrph1, clkrate));
+
+ cfg = S3C2410_NFCONF_EN;
+ cfg |= S3C2410_NFCONF_TACLS(tacls-1);
+ cfg |= S3C2410_NFCONF_TWRPH0(twrph0-1);
+ cfg |= S3C2410_NFCONF_TWRPH1(twrph1-1);
+
+ pr_debug(PFX "NF_CONF is 0x%lx\n", cfg);
+
+ writel(cfg, info->regs + S3C2410_NFCONF);
+ return 0;
+}
+
+/* select chip */
+
+static void s3c2410_nand_select_chip(struct mtd_info *mtd, int chip)
+{
+ struct s3c2410_nand_info *info;
+ struct s3c2410_nand_mtd *nmtd;
+ struct nand_chip *this = mtd->priv;
+ unsigned long cur;
+
+ nmtd = (struct s3c2410_nand_mtd *)this->priv;
+ info = nmtd->info;
+
+ cur = readl(info->regs + S3C2410_NFCONF);
+
+ if (chip == -1) {
+ cur |= S3C2410_NFCONF_nFCE;
+ } else {
+ if (chip > nmtd->set->nr_chips) {
+ printk(KERN_ERR PFX "chip %d out of range\n", chip);
+ return;
+ }
+
+ if (info->platform != NULL) {
+ if (info->platform->select_chip != NULL)
+ (info->platform->select_chip)(nmtd->set, chip);
+ }
+
+ cur &= ~S3C2410_NFCONF_nFCE;
+ }
+
+ writel(cur, info->regs + S3C2410_NFCONF);
+}
+
+/* command and control functions */
+
+static void s3c2410_nand_hwcontrol(struct mtd_info *mtd, int cmd)
+{
+ struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
+ unsigned long cur;
+
+ switch (cmd) {
+ case NAND_CTL_SETNCE:
+ cur = readl(info->regs + S3C2410_NFCONF);
+ cur &= ~S3C2410_NFCONF_nFCE;
+ writel(cur, info->regs + S3C2410_NFCONF);
+ break;
+
+ case NAND_CTL_CLRNCE:
+ cur = readl(info->regs + S3C2410_NFCONF);
+ cur |= S3C2410_NFCONF_nFCE;
+ writel(cur, info->regs + S3C2410_NFCONF);
+ break;
+
+ /* we don't need to implement these */
+ case NAND_CTL_SETCLE:
+ case NAND_CTL_CLRCLE:
+ case NAND_CTL_SETALE:
+ case NAND_CTL_CLRALE:
+ pr_debug(PFX "s3c2410_nand_hwcontrol(%d) unusedn", cmd);
+ break;
+ }
+}
+
+/* s3c2410_nand_command
+ *
+ * This function implements sending commands and the relevant address
+ * information to the chip, via the hardware controller. Since the
+ * S3C2410 generates the correct ALE/CLE signaling automatically, we
+ * do not need to use hwcontrol.
+*/
+
+static void s3c2410_nand_command (struct mtd_info *mtd, unsigned command,
+ int column, int page_addr)
+{
+ register struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
+ register struct nand_chip *this = mtd->priv;
+
+ /*
+ * Write out the command to the device.
+ */
+ if (command == NAND_CMD_SEQIN) {
+ int readcmd;
+
+ if (column >= mtd->oobblock) {
+ /* OOB area */
+ column -= mtd->oobblock;
+ readcmd = NAND_CMD_READOOB;
+ } else if (column < 256) {
+ /* First 256 bytes --> READ0 */
+ readcmd = NAND_CMD_READ0;
+ } else {
+ column -= 256;
+ readcmd = NAND_CMD_READ1;
+ }
+
+ writeb(readcmd, info->regs + S3C2410_NFCMD);
+ }
+ writeb(command, info->regs + S3C2410_NFCMD);
+
+ /* Set ALE and clear CLE to start address cycle */
+
+ if (column != -1 || page_addr != -1) {
+
+ /* Serially input address */
+ if (column != -1) {
+ /* Adjust columns for 16 bit buswidth */
+ if (this->options & NAND_BUSWIDTH_16)
+ column >>= 1;
+ writeb(column, info->regs + S3C2410_NFADDR);
+ }
+ if (page_addr != -1) {
+ writeb((unsigned char) (page_addr), info->regs + S3C2410_NFADDR);
+ writeb((unsigned char) (page_addr >> 8), info->regs + S3C2410_NFADDR);
+ /* One more address cycle for higher density devices */
+ if (this->chipsize & 0x0c000000)
+ writeb((unsigned char) ((page_addr >> 16) & 0x0f),
+ info->regs + S3C2410_NFADDR);
+ }
+ /* Latch in address */
+ }
+
+ /*
+ * program and erase have their own busy handlers
+ * status and sequential in needs no delay
+ */
+ switch (command) {
+
+ case NAND_CMD_PAGEPROG:
+ case NAND_CMD_ERASE1:
+ case NAND_CMD_ERASE2:
+ case NAND_CMD_SEQIN:
+ case NAND_CMD_STATUS:
+ return;
+
+ case NAND_CMD_RESET:
+ if (this->dev_ready)
+ break;
+
+ udelay(this->chip_delay);
+ writeb(NAND_CMD_STATUS, info->regs + S3C2410_NFCMD);
+
+ while ( !(this->read_byte(mtd) & 0x40));
+ return;
+
+ /* This applies to read commands */
+ default:
+ /*
+ * If we don't have access to the busy pin, we apply the given
+ * command delay
+ */
+ if (!this->dev_ready) {
+ udelay (this->chip_delay);
+ return;
+ }
+ }
+
+ /* Apply this short delay always to ensure that we do wait tWB in
+ * any case on any machine. */
+ ndelay (100);
+ /* wait until command is processed */
+ while (!this->dev_ready(mtd));
+}
+
+
+/* s3c2410_nand_devready()
+ *
+ * returns 0 if the nand is busy, 1 if it is ready
+*/
+
+static int s3c2410_nand_devready(struct mtd_info *mtd)
+{
+ struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
+
+ return readb(info->regs + S3C2410_NFSTAT) & S3C2410_NFSTAT_BUSY;
+}
+
+/* ECC handling functions */
+
+static int s3c2410_nand_correct_data(struct mtd_info *mtd, u_char *dat,
+ u_char *read_ecc, u_char *calc_ecc)
+{
+ pr_debug("s3c2410_nand_correct_data(%p,%p,%p,%p)\n",
+ mtd, dat, read_ecc, calc_ecc);
+
+ pr_debug("eccs: read %02x,%02x,%02x vs calc %02x,%02x,%02x\n",
+ read_ecc[0], read_ecc[1], read_ecc[2],
+ calc_ecc[0], calc_ecc[1], calc_ecc[2]);
+
+ if (read_ecc[0] == calc_ecc[0] &&
+ read_ecc[1] == calc_ecc[1] &&
+ read_ecc[2] == calc_ecc[2])
+ return 0;
+
+ /* we curently have no method for correcting the error */
+
+ return -1;
+}
+
+static void s3c2410_nand_enable_hwecc(struct mtd_info *mtd, int mode)
+{
+ struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
+ unsigned long ctrl;
+
+ ctrl = readl(info->regs + S3C2410_NFCONF);
+ ctrl |= S3C2410_NFCONF_INITECC;
+ writel(ctrl, info->regs + S3C2410_NFCONF);
+}
+
+static int s3c2410_nand_calculate_ecc(struct mtd_info *mtd,
+ const u_char *dat, u_char *ecc_code)
+{
+ struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
+
+ ecc_code[0] = readb(info->regs + S3C2410_NFECC + 0);
+ ecc_code[1] = readb(info->regs + S3C2410_NFECC + 1);
+ ecc_code[2] = readb(info->regs + S3C2410_NFECC + 2);
+
+ pr_debug("calculate_ecc: returning ecc %02x,%02x,%02x\n",
+ ecc_code[0], ecc_code[1], ecc_code[2]);
+
+ return 0;
+}
+
+
+/* over-ride the standard functions for a little more speed? */
+
+static void s3c2410_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len)
+{
+ struct nand_chip *this = (struct nand_chip *)mtd->priv;
+ readsb(this->IO_ADDR_R, buf, len);
+}
+
+static void s3c2410_nand_write_buf(struct mtd_info *mtd,
+ const u_char *buf, int len)
+{
+ struct nand_chip *this = (struct nand_chip *)mtd->priv;
+ writesb(this->IO_ADDR_W, buf, len);
+}
+
+/* device management functions */
+
+static int s3c2410_nand_remove(struct device *dev)
+{
+ struct s3c2410_nand_info *info = to_nand_info(dev);
+
+ dev_set_drvdata(dev, NULL);
+
+ if (info == NULL)
+ return 0;
+
+ /* first thing we need to do is release all our mtds
+ * and their partitions, then go through freeing the
+ * resources used
+ */
+
+ if (info->mtds != NULL) {
+ struct s3c2410_nand_mtd *ptr = info->mtds;
+ int mtdno;
+
+ for (mtdno = 0; mtdno < info->mtd_count; mtdno++, ptr++) {
+ pr_debug("releasing mtd %d (%p)\n", mtdno, ptr);
+ nand_release(&ptr->mtd);
+ }
+
+ kfree(info->mtds);
+ }
+
+ /* free the common resources */
+
+ if (info->clk != NULL && !IS_ERR(info->clk)) {
+ clk_disable(info->clk);
+ clk_unuse(info->clk);
+ clk_put(info->clk);
+ }
+
+ if (info->regs != NULL) {
+ iounmap(info->regs);
+ info->regs = NULL;
+ }
+
+ if (info->area != NULL) {
+ release_resource(info->area);
+ kfree(info->area);
+ info->area = NULL;
+ }
+
+ kfree(info);
+
+ return 0;
+}
+
+#ifdef CONFIG_MTD_PARTITIONS
+static int s3c2410_nand_add_partition(struct s3c2410_nand_info *info,
+ struct s3c2410_nand_mtd *mtd,
+ struct s3c2410_nand_set *set)
+{
+ if (set == NULL)
+ return add_mtd_device(&mtd->mtd);
+
+ if (set->nr_partitions > 0 && set->partitions != NULL) {
+ return add_mtd_partitions(&mtd->mtd,
+ set->partitions,
+ set->nr_partitions);
+ }
+
+ return add_mtd_device(&mtd->mtd);
+}
+#else
+static int s3c2410_nand_add_partition(struct s3c2410_nand_info *info,
+ struct s3c2410_nand_mtd *mtd,
+ struct s3c2410_nand_set *set)
+{
+ return add_mtd_device(&mtd->mtd);
+}
+#endif
+
+/* s3c2410_nand_init_chip
+ *
+ * init a single instance of an chip
+*/
+
+static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info,
+ struct s3c2410_nand_mtd *nmtd,
+ struct s3c2410_nand_set *set)
+{
+ struct nand_chip *chip = &nmtd->chip;
+
+ chip->IO_ADDR_R = (char *)info->regs + S3C2410_NFDATA;
+ chip->IO_ADDR_W = (char *)info->regs + S3C2410_NFDATA;
+ chip->hwcontrol = s3c2410_nand_hwcontrol;
+ chip->dev_ready = s3c2410_nand_devready;
+ chip->cmdfunc = s3c2410_nand_command;
+ chip->write_buf = s3c2410_nand_write_buf;
+ chip->read_buf = s3c2410_nand_read_buf;
+ chip->select_chip = s3c2410_nand_select_chip;
+ chip->chip_delay = 50;
+ chip->priv = nmtd;
+ chip->options = 0;
+ chip->controller = &info->controller;
+
+ nmtd->info = info;
+ nmtd->mtd.priv = chip;
+ nmtd->set = set;
+
+ if (hardware_ecc) {
+ chip->correct_data = s3c2410_nand_correct_data;
+ chip->enable_hwecc = s3c2410_nand_enable_hwecc;
+ chip->calculate_ecc = s3c2410_nand_calculate_ecc;
+ chip->eccmode = NAND_ECC_HW3_512;
+ chip->autooob = &nand_hw_eccoob;
+ } else {
+ chip->eccmode = NAND_ECC_SOFT;
+ }
+}
+
+/* s3c2410_nand_probe
+ *
+ * called by device layer when it finds a device matching
+ * one our driver can handled. This code checks to see if
+ * it can allocate all necessary resources then calls the
+ * nand layer to look for devices
+*/
+
+static int s3c2410_nand_probe(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct s3c2410_platform_nand *plat = to_nand_plat(dev);
+ struct s3c2410_nand_info *info;
+ struct s3c2410_nand_mtd *nmtd;
+ struct s3c2410_nand_set *sets;
+ struct resource *res;
+ int err = 0;
+ int size;
+ int nr_sets;
+ int setno;
+
+ pr_debug("s3c2410_nand_probe(%p)\n", dev);
+
+ info = kmalloc(sizeof(*info), GFP_KERNEL);
+ if (info == NULL) {
+ printk(KERN_ERR PFX "no memory for flash info\n");
+ err = -ENOMEM;
+ goto exit_error;
+ }
+
+ memzero(info, sizeof(*info));
+ dev_set_drvdata(dev, info);
+
+ spin_lock_init(&info->controller.lock);
+
+ /* get the clock source and enable it */
+
+ info->clk = clk_get(dev, "nand");
+ if (IS_ERR(info->clk)) {
+ printk(KERN_ERR PFX "failed to get clock");
+ err = -ENOENT;
+ goto exit_error;
+ }
+
+ clk_use(info->clk);
+ clk_enable(info->clk);
+
+ /* allocate and map the resource */
+
+ res = pdev->resource; /* assume that the flash has one resource */
+ size = res->end - res->start + 1;
+
+ info->area = request_mem_region(res->start, size, pdev->name);
+
+ if (info->area == NULL) {
+ printk(KERN_ERR PFX "cannot reserve register region\n");
+ err = -ENOENT;
+ goto exit_error;
+ }
+
+ info->device = dev;
+ info->platform = plat;
+ info->regs = ioremap(res->start, size);
+
+ if (info->regs == NULL) {
+ printk(KERN_ERR PFX "cannot reserve register region\n");
+ err = -EIO;
+ goto exit_error;
+ }
+
+ printk(KERN_INFO PFX "mapped registers at %p\n", info->regs);
+
+ /* initialise the hardware */
+
+ err = s3c2410_nand_inithw(info, dev);
+ if (err != 0)
+ goto exit_error;
+
+ sets = (plat != NULL) ? plat->sets : NULL;
+ nr_sets = (plat != NULL) ? plat->nr_sets : 1;
+
+ info->mtd_count = nr_sets;
+
+ /* allocate our information */
+
+ size = nr_sets * sizeof(*info->mtds);
+ info->mtds = kmalloc(size, GFP_KERNEL);
+ if (info->mtds == NULL) {
+ printk(KERN_ERR PFX "failed to allocate mtd storage\n");
+ err = -ENOMEM;
+ goto exit_error;
+ }
+
+ memzero(info->mtds, size);
+
+ /* initialise all possible chips */
+
+ nmtd = info->mtds;
+
+ for (setno = 0; setno < nr_sets; setno++, nmtd++) {
+ pr_debug("initialising set %d (%p, info %p)\n",
+ setno, nmtd, info);
+
+ s3c2410_nand_init_chip(info, nmtd, sets);
+
+ nmtd->scan_res = nand_scan(&nmtd->mtd,
+ (sets) ? sets->nr_chips : 1);
+
+ if (nmtd->scan_res == 0) {
+ s3c2410_nand_add_partition(info, nmtd, sets);
+ }
+
+ if (sets != NULL)
+ sets++;
+ }
+
+ pr_debug("initialised ok\n");
+ return 0;
+
+ exit_error:
+ s3c2410_nand_remove(dev);
+
+ if (err == 0)
+ err = -EINVAL;
+ return err;
+}
+
+static struct device_driver s3c2410_nand_driver = {
+ .name = "s3c2410-nand",
+ .bus = &platform_bus_type,
+ .probe = s3c2410_nand_probe,
+ .remove = s3c2410_nand_remove,
+};
+
+static int __init s3c2410_nand_init(void)
+{
+ printk("S3C2410 NAND Driver, (c) 2004 Simtec Electronics\n");
+ return driver_register(&s3c2410_nand_driver);
+}
+
+static void __exit s3c2410_nand_exit(void)
+{
+ driver_unregister(&s3c2410_nand_driver);
+}
+
+module_init(s3c2410_nand_init);
+module_exit(s3c2410_nand_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>");
+MODULE_DESCRIPTION("S3C2410 MTD NAND driver");
diff --git a/drivers/mtd/nand/spia.c b/drivers/mtd/nand/spia.c
index acf6fc808b76..b0de249628f2 100644
--- a/drivers/mtd/nand/spia.c
+++ b/drivers/mtd/nand/spia.c
@@ -8,7 +8,7 @@
* to controllines (due to change in nand.c)
* page_cache added
*
- * $Id: spia.c,v 1.21 2003/07/11 15:12:29 dwmw2 Exp $
+ * $Id: spia.c,v 1.23 2004/10/05 13:50:20 gleixner Exp $
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@@ -132,8 +132,8 @@ int __init spia_init (void)
(*(volatile unsigned char *) (spia_io_base + spia_peddr)) = 0x07;
/* Set address of NAND IO lines */
- this->IO_ADDR_R = spia_fio_base;
- this->IO_ADDR_W = spia_fio_base;
+ this->IO_ADDR_R = (void __iomem *) spia_fio_base;
+ this->IO_ADDR_W = (void __iomem *) spia_fio_base;
/* Set address of hardware control function */
this->hwcontrol = spia_hwcontrol;
/* 15 us command delay time */
@@ -145,14 +145,6 @@ int __init spia_init (void)
return -ENXIO;
}
- /* Allocate memory for internal data buffer */
- this->data_buf = kmalloc (sizeof(u_char) * (spia_mtd->oobblock + spia_mtd->oobsize), GFP_KERNEL);
- if (!this->data_buf) {
- printk ("Unable to allocate NAND data buffer for SPIA.\n");
- kfree (spia_mtd);
- return -ENOMEM;
- }
-
/* Register the partitions */
add_mtd_partitions(spia_mtd, partition_info, NUM_PARTITIONS);
@@ -167,13 +159,8 @@ module_init(spia_init);
#ifdef MODULE
static void __exit spia_cleanup (void)
{
- struct nand_chip *this = (struct nand_chip *) &spia_mtd[1];
-
- /* Unregister the device */
- del_mtd_device (spia_mtd);
-
- /* Free internal data buffer */
- kfree (this->data_buf);
+ /* Release resources, unregister device */
+ nand_release (spia_mtd);
/* Free the MTD device structure */
kfree (spia_mtd);
diff --git a/drivers/mtd/nand/toto.c b/drivers/mtd/nand/toto.c
index ecb9f3dc8805..52c808fb5fa9 100644
--- a/drivers/mtd/nand/toto.c
+++ b/drivers/mtd/nand/toto.c
@@ -15,7 +15,7 @@
* This is a device driver for the NAND flash device found on the
* TI fido board. It supports 32MiB and 64MiB cards
*
- * $Id: toto.c,v 1.2 2003/10/21 10:04:58 dwmw2 Exp $
+ * $Id: toto.c,v 1.4 2004/10/05 13:50:20 gleixner Exp $
*/
#include <linux/slab.h>
@@ -37,7 +37,7 @@
*/
static struct mtd_info *toto_mtd = NULL;
-static int toto_io_base = OMAP_FLASH_1_BASE;
+static unsigned long toto_io_base = OMAP_FLASH_1_BASE;
#define CONFIG_NAND_WORKAROUND 1
@@ -155,14 +155,6 @@ int __init toto_init (void)
goto out_mtd;
}
- /* Allocate memory for internal data buffer */
- this->data_buf = kmalloc (sizeof(u_char) * (toto_mtd->oobblock + toto_mtd->oobsize), GFP_KERNEL);
- if (!this->data_buf) {
- printk (KERN_WARNING "Unable to allocate NAND data buffer for toto.\n");
- err = -ENOMEM;
- goto out_mtd;
- }
-
/* Register the partitions */
switch(toto_mtd->size){
case SZ_64M: add_mtd_partitions(toto_mtd, partition_info64M, NUM_PARTITIONS64M); break;
@@ -194,16 +186,8 @@ module_init(toto_init);
*/
static void __exit toto_cleanup (void)
{
- struct nand_chip *this = (struct nand_chip *) &toto_mtd[1];
-
- /* Unregister partitions */
- del_mtd_partitions(toto_mtd);
-
- /* Unregister the device */
- del_mtd_device (toto_mtd);
-
- /* Free internal data buffers */
- kfree (this->data_buf);
+ /* Release resources, unregister device */
+ nand_release (toto_mtd);
/* Free the MTD device structure */
kfree (toto_mtd);
diff --git a/drivers/mtd/nand/tx4925ndfmc.c b/drivers/mtd/nand/tx4925ndfmc.c
index 5f6a2f5ed138..bba688830c9b 100644
--- a/drivers/mtd/nand/tx4925ndfmc.c
+++ b/drivers/mtd/nand/tx4925ndfmc.c
@@ -11,7 +11,7 @@
* Derived from drivers/mtd/autcpu12.c
* Copyright (c) 2001 Thomas Gleixner (gleixner@autronix.de)
*
- * $Id: tx4925ndfmc.c,v 1.3 2004/07/20 02:44:26 dwmw2 Exp $
+ * $Id: tx4925ndfmc.c,v 1.5 2004/10/05 13:50:20 gleixner Exp $
*
* Copyright (C) 2001 Toshiba Corporation
*
@@ -340,8 +340,8 @@ int __init tx4925ndfmc_init (void)
tx4925ndfmc_mtd->priv = this;
/* Set address of NAND IO lines */
- this->IO_ADDR_R = (unsigned long)&(tx4925_ndfmcptr->dtr);
- this->IO_ADDR_W = (unsigned long)&(tx4925_ndfmcptr->dtr);
+ this->IO_ADDR_R = (void __iomem *)&(tx4925_ndfmcptr->dtr);
+ this->IO_ADDR_W = (void __iomem *)&(tx4925_ndfmcptr->dtr);
this->hwcontrol = tx4925ndfmc_hwcontrol;
this->enable_hwecc = tx4925ndfmc_enable_hwecc;
this->calculate_ecc = tx4925ndfmc_readecc;
@@ -363,14 +363,6 @@ int __init tx4925ndfmc_init (void)
goto out_ior;
}
- /* Allocate memory for internal data buffer */
- this->data_buf = kmalloc (sizeof(u_char) * (tx4925ndfmc_mtd->oobblock + tx4925ndfmc_mtd->oobsize), GFP_KERNEL);
- if (!this->data_buf) {
- printk ("Unable to allocate NAND data buffer for RBTX4925.\n");
- err = -ENOMEM;
- goto out_ior;
- }
-
/* Register the partitions */
#ifdef CONFIG_MTD_CMDLINE_PARTS
{
@@ -391,14 +383,12 @@ int __init tx4925ndfmc_init (void)
default: {
printk ("Unsupported SmartMedia device\n");
err = -ENXIO;
- goto out_buf;
+ goto out_ior;
}
}
#endif /* ifdef CONFIG_MTD_CMDLINE_PARTS */
goto out;
-out_buf:
- kfree (this->data_buf);
out_ior:
out:
return err;
@@ -412,16 +402,8 @@ module_init(tx4925ndfmc_init);
#ifdef MODULE
static void __exit tx4925ndfmc_cleanup (void)
{
- struct nand_chip *this = (struct nand_chip *) &tx4925ndfmc_mtd[1];
-
- /* Unregister partitions */
- del_mtd_partitions(tx4925ndfmc_mtd);
-
- /* Unregister the device */
- del_mtd_device (tx4925ndfmc_mtd);
-
- /* Free internal data buffers */
- kfree (this->data_buf);
+ /* Release resources, unregister device */
+ nand_release (tx4925ndfmc_mtd);
/* Free the MTD device structure */
kfree (tx4925ndfmc_mtd);
diff --git a/drivers/mtd/nand/tx4938ndfmc.c b/drivers/mtd/nand/tx4938ndfmc.c
index f2375b24187c..df26e58820b3 100644
--- a/drivers/mtd/nand/tx4938ndfmc.c
+++ b/drivers/mtd/nand/tx4938ndfmc.c
@@ -10,7 +10,7 @@
*
* Based on spia.c by Steven J. Hill
*
- * $Id: tx4938ndfmc.c,v 1.2 2004/03/27 19:55:53 gleixner Exp $
+ * $Id: tx4938ndfmc.c,v 1.4 2004/10/05 13:50:20 gleixner Exp $
*
* Copyright (C) 2000-2001 Toshiba Corporation
*
@@ -365,14 +365,6 @@ int __init tx4938ndfmc_init (void)
return -ENXIO;
}
- /* Allocate memory for internal data buffer */
- this->data_buf = kmalloc (sizeof(u_char) * (tx4938ndfmc_mtd->oobblock + tx4938ndfmc_mtd->oobsize), GFP_KERNEL);
- if (!this->data_buf) {
- printk ("Unable to allocate NAND data buffer for TX4938.\n");
- kfree (tx4938ndfmc_mtd);
- return -ENOMEM;
- }
-
if (protected) {
printk(KERN_INFO "TX4938 NDFMC: write protected.\n");
tx4938ndfmc_mtd->flags &= ~(MTD_WRITEABLE | MTD_ERASEABLE);
@@ -401,19 +393,11 @@ module_init(tx4938ndfmc_init);
*/
static void __exit tx4938ndfmc_cleanup (void)
{
- struct nand_chip *this = (struct nand_chip *) tx4938ndfmc_mtd->priv;
-
- /* Unregister the device */
-#ifdef CONFIG_MTD_CMDLINE_PARTS
- del_mtd_partitions(tx4938ndfmc_mtd);
-#endif
- del_mtd_device (tx4938ndfmc_mtd);
+ /* Release resources, unregister device */
+ nand_release (tx4938ndfmc_mtd);
/* Free the MTD device structure */
kfree (tx4938ndfmc_mtd);
-
- /* Free internal data buffer */
- kfree (this->data_buf);
}
module_exit(tx4938ndfmc_cleanup);
diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h
index 7de9f8dfa8ab..9453cb58c683 100644
--- a/include/linux/mtd/nand.h
+++ b/include/linux/mtd/nand.h
@@ -5,7 +5,7 @@
* Steven J. Hill <sjhill@realitydiluted.com>
* Thomas Gleixner <tglx@linutronix.de>
*
- * $Id: nand.h,v 1.63 2004/07/07 16:29:43 gleixner Exp $
+ * $Id: nand.h,v 1.66 2004/10/02 10:07:08 gleixner Exp $
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@@ -212,6 +212,18 @@ typedef enum {
FL_CACHEDPRG,
} nand_state_t;
+/* Keep gcc happy */
+struct nand_chip;
+
+/**
+ * struct nand_hw_control - Control structure for hardware controller (e.g ECC generator) shared among independend devices
+ * @lock: protection lock
+ * @active: the mtd device which holds the controller currently
+ */
+struct nand_hw_control {
+ spinlock_t lock;
+ struct nand_chip *active;
+};
/**
* struct nand_chip - NAND Private Flash Chip Data
@@ -265,12 +277,13 @@ typedef enum {
* @bbt: [INTERN] bad block table pointer
* @bbt_td: [REPLACEABLE] bad block table descriptor for flash lookup
* @bbt_md: [REPLACEABLE] bad block table mirror descriptor
+ * @controller: [OPTIONAL] a pointer to a hardware controller structure which is shared among multiple independend devices
* @priv: [OPTIONAL] pointer to private chip date
*/
struct nand_chip {
- unsigned long IO_ADDR_R;
- unsigned long IO_ADDR_W;
+ void __iomem *IO_ADDR_R;
+ void __iomem *IO_ADDR_W;
u_char (*read_byte)(struct mtd_info *mtd);
void (*write_byte)(struct mtd_info *mtd, u_char byte);
@@ -317,6 +330,7 @@ struct nand_chip {
uint8_t *bbt;
struct nand_bbt_descr *bbt_td;
struct nand_bbt_descr *bbt_md;
+ struct nand_hw_control *controller;
void *priv;
};