summaryrefslogtreecommitdiff
path: root/drivers/mtd/nand
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mtd/nand')
-rw-r--r--drivers/mtd/nand/Kconfig21
-rw-r--r--drivers/mtd/nand/Makefile2
-rw-r--r--drivers/mtd/nand/diskonchip.c96
-rw-r--r--drivers/mtd/nand/nand_base.c299
-rw-r--r--drivers/mtd/nand/nand_bbt.c114
-rw-r--r--drivers/mtd/nand/nand_ids.c18
-rw-r--r--drivers/mtd/nand/nandsim.c41
-rw-r--r--drivers/mtd/nand/rtc_from4.c140
-rw-r--r--drivers/mtd/nand/s3c2410.c297
-rw-r--r--[-rwxr-xr-x]drivers/mtd/nand/sharpsl.c4
-rw-r--r--drivers/mtd/nand/tx4925ndfmc.c416
-rw-r--r--drivers/mtd/nand/tx4938ndfmc.c406
12 files changed, 679 insertions, 1175 deletions
diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
index f7801eb730ce..36d34e5e5a5a 100644
--- a/drivers/mtd/nand/Kconfig
+++ b/drivers/mtd/nand/Kconfig
@@ -1,5 +1,5 @@
# drivers/mtd/nand/Kconfig
-# $Id: Kconfig,v 1.26 2005/01/05 12:42:24 dwmw2 Exp $
+# $Id: Kconfig,v 1.31 2005/06/20 12:03:21 bjd Exp $
menu "NAND Flash Device Drivers"
depends on MTD!=n
@@ -58,20 +58,6 @@ config MTD_NAND_TOTO
config MTD_NAND_IDS
tristate
-config MTD_NAND_TX4925NDFMC
- tristate "SmartMedia Card on Toshiba RBTX4925 reference board"
- depends on TOSHIBA_RBTX4925 && MTD_NAND && TOSHIBA_RBTX4925_MPLEX_NAND
- help
- This enables the driver for the NAND flash device found on the
- Toshiba RBTX4925 reference board, which is a SmartMediaCard.
-
-config MTD_NAND_TX4938NDFMC
- tristate "NAND Flash device on Toshiba RBTX4938 reference board"
- depends on TOSHIBA_RBTX4938 && MTD_NAND && TOSHIBA_RBTX4938_MPLEX_NAND
- help
- This enables the driver for the NAND flash device found on the
- Toshiba RBTX4938 reference board.
-
config MTD_NAND_AU1550
tristate "Au1550 NAND support"
depends on SOC_AU1550 && MTD_NAND
@@ -95,10 +81,11 @@ config MTD_NAND_PPCHAMELEONEVB
This enables the NAND flash driver on the PPChameleon EVB Board.
config MTD_NAND_S3C2410
- tristate "NAND Flash support for S3C2410 SoC"
+ tristate "NAND Flash support for S3C2410/S3C2440 SoC"
depends on ARCH_S3C2410 && MTD_NAND
help
- This enables the NAND flash controller on the S3C2410.
+ This enables the NAND flash controller on the S3C2410 and S3C2440
+ SoCs
No board specfic support is done by this driver, each board
must advertise a platform_device for the driver to attach.
diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
index d9dc8cc2da8c..41742026a52e 100644
--- a/drivers/mtd/nand/Makefile
+++ b/drivers/mtd/nand/Makefile
@@ -10,8 +10,6 @@ obj-$(CONFIG_MTD_NAND_SPIA) += spia.o
obj-$(CONFIG_MTD_NAND_TOTO) += toto.o
obj-$(CONFIG_MTD_NAND_AUTCPU12) += autcpu12.o
obj-$(CONFIG_MTD_NAND_EDB7312) += edb7312.o
-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
diff --git a/drivers/mtd/nand/diskonchip.c b/drivers/mtd/nand/diskonchip.c
index 02135c3ac29a..fdb5d4ad3d52 100644
--- a/drivers/mtd/nand/diskonchip.c
+++ b/drivers/mtd/nand/diskonchip.c
@@ -16,7 +16,7 @@
*
* Interface to generic NAND code for M-Systems DiskOnChip devices
*
- * $Id: diskonchip.c,v 1.45 2005/01/05 18:05:14 dwmw2 Exp $
+ * $Id: diskonchip.c,v 1.54 2005/04/07 14:22:55 dbrown Exp $
*/
#include <linux/kernel.h>
@@ -35,13 +35,13 @@
#include <linux/mtd/inftl.h>
/* Where to look for the devices? */
-#ifndef CONFIG_MTD_DISKONCHIP_PROBE_ADDRESS
-#define CONFIG_MTD_DISKONCHIP_PROBE_ADDRESS 0
+#ifndef CONFIG_MTD_NAND_DISKONCHIP_PROBE_ADDRESS
+#define CONFIG_MTD_NAND_DISKONCHIP_PROBE_ADDRESS 0
#endif
static unsigned long __initdata doc_locations[] = {
#if defined (__alpha__) || defined(__i386__) || defined(__x86_64__)
-#ifdef CONFIG_MTD_DISKONCHIP_PROBE_HIGH
+#ifdef CONFIG_MTD_NAND_DISKONCHIP_PROBE_HIGH
0xfffc8000, 0xfffca000, 0xfffcc000, 0xfffce000,
0xfffd0000, 0xfffd2000, 0xfffd4000, 0xfffd6000,
0xfffd8000, 0xfffda000, 0xfffdc000, 0xfffde000,
@@ -81,11 +81,6 @@ struct doc_priv {
struct mtd_info *nextdoc;
};
-/* Max number of eraseblocks to scan (from start of device) for the (I)NFTL
- MediaHeader. The spec says to just keep going, I think, but that's just
- silly. */
-#define MAX_MEDIAHEADER_SCAN 8
-
/* This is the syndrome computed by the HW ecc generator upon reading an empty
page, one with all 0xff for data and stored ecc code. */
static u_char empty_read_syndrome[6] = { 0x26, 0xff, 0x6d, 0x47, 0x73, 0x7a };
@@ -111,10 +106,11 @@ module_param(try_dword, int, 0);
static int no_ecc_failures=0;
module_param(no_ecc_failures, int, 0);
-#ifdef CONFIG_MTD_PARTITIONS
static int no_autopart=0;
module_param(no_autopart, int, 0);
-#endif
+
+static int show_firmware_partition=0;
+module_param(show_firmware_partition, int, 0);
#ifdef MTD_NAND_DISKONCHIP_BBTWRITE
static int inftl_bbt_write=1;
@@ -123,7 +119,7 @@ static int inftl_bbt_write=0;
#endif
module_param(inftl_bbt_write, int, 0);
-static unsigned long doc_config_location = CONFIG_MTD_DISKONCHIP_PROBE_ADDRESS;
+static unsigned long doc_config_location = CONFIG_MTD_NAND_DISKONCHIP_PROBE_ADDRESS;
module_param(doc_config_location, ulong, 0);
MODULE_PARM_DESC(doc_config_location, "Physical memory address at which to probe for DiskOnChip");
@@ -410,7 +406,12 @@ static uint16_t __init doc200x_ident_chip(struct mtd_info *mtd, int nr)
doc200x_hwcontrol(mtd, NAND_CTL_SETALE);
this->write_byte(mtd, 0);
doc200x_hwcontrol(mtd, NAND_CTL_CLRALE);
-
+
+ /* We cant' use dev_ready here, but at least we wait for the
+ * command to complete
+ */
+ udelay(50);
+
ret = this->read_byte(mtd) << 8;
ret |= this->read_byte(mtd);
@@ -429,6 +430,8 @@ static uint16_t __init doc200x_ident_chip(struct mtd_info *mtd, int nr)
doc2000_write_byte(mtd, 0);
doc200x_hwcontrol(mtd, NAND_CTL_CLRALE);
+ udelay(50);
+
ident.dword = readl(docptr + DoC_2k_CDSN_IO);
if (((ident.byte[0] << 8) | ident.byte[1]) == ret) {
printk(KERN_INFO "DiskOnChip 2000 responds to DWORD access\n");
@@ -1046,11 +1049,21 @@ static int doc200x_correct_data(struct mtd_info *mtd, u_char *dat, u_char *read_
//u_char mydatabuf[528];
+/* The strange out-of-order .oobfree list below is a (possibly unneeded)
+ * attempt to retain compatibility. It used to read:
+ * .oobfree = { {8, 8} }
+ * Since that leaves two bytes unusable, it was changed. But the following
+ * scheme might affect existing jffs2 installs by moving the cleanmarker:
+ * .oobfree = { {6, 10} }
+ * jffs2 seems to handle the above gracefully, but the current scheme seems
+ * safer. The only problem with it is that any code that parses oobfree must
+ * be able to handle out-of-order segments.
+ */
static struct nand_oobinfo doc200x_oobinfo = {
.useecc = MTD_NANDECC_AUTOPLACE,
.eccbytes = 6,
.eccpos = {0, 1, 2, 3, 4, 5},
- .oobfree = { {8, 8} }
+ .oobfree = { {8, 8}, {6, 2} }
};
/* Find the (I)NFTL Media Header, and optionally also the mirror media header.
@@ -1064,12 +1077,11 @@ static int __init find_media_headers(struct mtd_info *mtd, u_char *buf,
{
struct nand_chip *this = mtd->priv;
struct doc_priv *doc = this->priv;
- unsigned offs, end = (MAX_MEDIAHEADER_SCAN << this->phys_erase_shift);
+ unsigned offs;
int ret;
size_t retlen;
- end = min(end, mtd->size); // paranoia
- for (offs = 0; offs < end; offs += mtd->erasesize) {
+ for (offs = 0; offs < mtd->size; offs += mtd->erasesize) {
ret = mtd->read(mtd, offs, mtd->oobblock, &retlen, buf);
if (retlen != mtd->oobblock) continue;
if (ret) {
@@ -1111,6 +1123,7 @@ static inline int __init nftl_partscan(struct mtd_info *mtd,
u_char *buf;
struct NFTLMediaHeader *mh;
const unsigned psize = 1 << this->page_shift;
+ int numparts = 0;
unsigned blocks, maxblocks;
int offs, numheaders;
@@ -1122,8 +1135,10 @@ static inline int __init nftl_partscan(struct mtd_info *mtd,
if (!(numheaders=find_media_headers(mtd, buf, "ANAND", 1))) goto out;
mh = (struct NFTLMediaHeader *) buf;
-//#ifdef CONFIG_MTD_DEBUG_VERBOSE
-// if (CONFIG_MTD_DEBUG_VERBOSE >= 2)
+ mh->NumEraseUnits = le16_to_cpu(mh->NumEraseUnits);
+ mh->FirstPhysicalEUN = le16_to_cpu(mh->FirstPhysicalEUN);
+ mh->FormattedSize = le32_to_cpu(mh->FormattedSize);
+
printk(KERN_INFO " DataOrgID = %s\n"
" NumEraseUnits = %d\n"
" FirstPhysicalEUN = %d\n"
@@ -1132,7 +1147,6 @@ static inline int __init nftl_partscan(struct mtd_info *mtd,
mh->DataOrgID, mh->NumEraseUnits,
mh->FirstPhysicalEUN, mh->FormattedSize,
mh->UnitSizeFactor);
-//#endif
blocks = mtd->size >> this->phys_erase_shift;
maxblocks = min(32768U, mtd->erasesize - psize);
@@ -1175,23 +1189,28 @@ static inline int __init nftl_partscan(struct mtd_info *mtd,
offs <<= this->page_shift;
offs += mtd->erasesize;
- //parts[0].name = " DiskOnChip Boot / Media Header partition";
- //parts[0].offset = 0;
- //parts[0].size = offs;
+ if (show_firmware_partition == 1) {
+ parts[0].name = " DiskOnChip Firmware / Media Header partition";
+ parts[0].offset = 0;
+ parts[0].size = offs;
+ numparts = 1;
+ }
- parts[0].name = " DiskOnChip BDTL partition";
- parts[0].offset = offs;
- parts[0].size = (mh->NumEraseUnits - numheaders) << this->bbt_erase_shift;
+ parts[numparts].name = " DiskOnChip BDTL partition";
+ parts[numparts].offset = offs;
+ parts[numparts].size = (mh->NumEraseUnits - numheaders) << this->bbt_erase_shift;
+
+ offs += parts[numparts].size;
+ numparts++;
- offs += parts[0].size;
if (offs < mtd->size) {
- parts[1].name = " DiskOnChip Remainder partition";
- parts[1].offset = offs;
- parts[1].size = mtd->size - offs;
- ret = 2;
- goto out;
+ parts[numparts].name = " DiskOnChip Remainder partition";
+ parts[numparts].offset = offs;
+ parts[numparts].size = mtd->size - offs;
+ numparts++;
}
- ret = 1;
+
+ ret = numparts;
out:
kfree(buf);
return ret;
@@ -1233,8 +1252,6 @@ static inline int __init inftl_partscan(struct mtd_info *mtd,
mh->FormatFlags = le32_to_cpu(mh->FormatFlags);
mh->PercentUsed = le32_to_cpu(mh->PercentUsed);
-//#ifdef CONFIG_MTD_DEBUG_VERBOSE
-// if (CONFIG_MTD_DEBUG_VERBOSE >= 2)
printk(KERN_INFO " bootRecordID = %s\n"
" NoOfBootImageBlocks = %d\n"
" NoOfBinaryPartitions = %d\n"
@@ -1252,7 +1269,6 @@ static inline int __init inftl_partscan(struct mtd_info *mtd,
((unsigned char *) &mh->OsakVersion)[2] & 0xf,
((unsigned char *) &mh->OsakVersion)[3] & 0xf,
mh->PercentUsed);
-//#endif
vshift = this->phys_erase_shift + mh->BlockMultiplierBits;
@@ -1278,8 +1294,6 @@ static inline int __init inftl_partscan(struct mtd_info *mtd,
ip->spareUnits = le32_to_cpu(ip->spareUnits);
ip->Reserved0 = le32_to_cpu(ip->Reserved0);
-//#ifdef CONFIG_MTD_DEBUG_VERBOSE
-// if (CONFIG_MTD_DEBUG_VERBOSE >= 2)
printk(KERN_INFO " PARTITION[%d] ->\n"
" virtualUnits = %d\n"
" firstUnit = %d\n"
@@ -1289,16 +1303,14 @@ static inline int __init inftl_partscan(struct mtd_info *mtd,
i, ip->virtualUnits, ip->firstUnit,
ip->lastUnit, ip->flags,
ip->spareUnits);
-//#endif
-/*
- if ((i == 0) && (ip->firstUnit > 0)) {
+ if ((show_firmware_partition == 1) &&
+ (i == 0) && (ip->firstUnit > 0)) {
parts[0].name = " DiskOnChip IPL / Media Header partition";
parts[0].offset = 0;
parts[0].size = mtd->erasesize * ip->firstUnit;
numparts = 1;
}
-*/
if (ip->flags & INFTL_BINARY)
parts[numparts].name = " DiskOnChip BDK partition";
diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
index 44d5b128911f..1bd71a598c79 100644
--- a/drivers/mtd/nand/nand_base.c
+++ b/drivers/mtd/nand/nand_base.c
@@ -28,6 +28,24 @@
* among multiple independend devices. Suggestions and initial patch
* from Ben Dooks <ben-mtd@fluff.org>
*
+ * 12-05-2004 dmarlin: add workaround for Renesas AG-AND chips "disturb" issue.
+ * Basically, any block not rewritten may lose data when surrounding blocks
+ * are rewritten many times. JFFS2 ensures this doesn't happen for blocks
+ * it uses, but the Bad Block Table(s) may not be rewritten. To ensure they
+ * do not lose data, force them to be rewritten when some of the surrounding
+ * blocks are erased. Rather than tracking a specific nearby block (which
+ * could itself go bad), use a page address 'mask' to select several blocks
+ * in the same area, and rewrite the BBT when any of them are erased.
+ *
+ * 01-03-2005 dmarlin: added support for the device recovery command sequence for Renesas
+ * AG-AND chips. If there was a sudden loss of power during an erase operation,
+ * a "device recovery" operation must be performed when power is restored
+ * to ensure correct operation.
+ *
+ * 01-20-2005 dmarlin: added support for optional hardware specific callback routine to
+ * perform extra error status checks on erase and write failures. This required
+ * adding a wrapper function for nand_read_ecc.
+ *
* Credits:
* David Woodhouse for adding multichip support
*
@@ -41,7 +59,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.126 2004/12/13 11:22:25 lavinen Exp $
+ * $Id: nand_base.c,v 1.146 2005/06/17 15:02:06 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
@@ -149,17 +167,21 @@ static void nand_release_device (struct mtd_info *mtd)
/* De-select the NAND device */
this->select_chip(mtd, -1);
- /* Do we have a hardware controller ? */
+
if (this->controller) {
+ /* Release the controller and the chip */
spin_lock(&this->controller->lock);
this->controller->active = NULL;
+ this->state = FL_READY;
+ wake_up(&this->controller->wq);
spin_unlock(&this->controller->lock);
+ } else {
+ /* Release the chip */
+ spin_lock(&this->chip_lock);
+ this->state = FL_READY;
+ wake_up(&this->wq);
+ spin_unlock(&this->chip_lock);
}
- /* Release the chip */
- spin_lock (&this->chip_lock);
- this->state = FL_READY;
- wake_up (&this->wq);
- spin_unlock (&this->chip_lock);
}
/**
@@ -443,7 +465,8 @@ static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs)
/* Get block number */
block = ((int) ofs) >> this->bbt_erase_shift;
- this->bbt[block >> 2] |= 0x01 << ((block & 0x03) << 1);
+ if (this->bbt)
+ this->bbt[block >> 2] |= 0x01 << ((block & 0x03) << 1);
/* Do we have a flash based bad block table ? */
if (this->options & NAND_USE_FLASH_BBT)
@@ -466,7 +489,7 @@ static int nand_check_wp (struct mtd_info *mtd)
struct nand_chip *this = mtd->priv;
/* Check the WP bit */
this->cmdfunc (mtd, NAND_CMD_STATUS, -1, -1);
- return (this->read_byte(mtd) & 0x80) ? 0 : 1;
+ return (this->read_byte(mtd) & NAND_STATUS_WP) ? 0 : 1;
}
/**
@@ -490,6 +513,22 @@ static int nand_block_checkbad (struct mtd_info *mtd, loff_t ofs, int getchip, i
return nand_isbad_bbt (mtd, ofs, allowbbt);
}
+/*
+ * Wait for the ready pin, after a command
+ * The timeout is catched later.
+ */
+static void nand_wait_ready(struct mtd_info *mtd)
+{
+ struct nand_chip *this = mtd->priv;
+ unsigned long timeo = jiffies + 2;
+
+ /* wait until command is processed or timeout occures */
+ do {
+ if (this->dev_ready(mtd))
+ return;
+ } while (time_before(jiffies, timeo));
+}
+
/**
* nand_command - [DEFAULT] Send command to NAND device
* @mtd: MTD device structure
@@ -571,7 +610,7 @@ static void nand_command (struct mtd_info *mtd, unsigned command, int column, in
this->hwcontrol(mtd, NAND_CTL_SETCLE);
this->write_byte(mtd, NAND_CMD_STATUS);
this->hwcontrol(mtd, NAND_CTL_CLRCLE);
- while ( !(this->read_byte(mtd) & 0x40));
+ while ( !(this->read_byte(mtd) & NAND_STATUS_READY));
return;
/* This applies to read commands */
@@ -585,12 +624,11 @@ static void nand_command (struct mtd_info *mtd, unsigned command, int column, in
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));
+
+ nand_wait_ready(mtd);
}
/**
@@ -619,7 +657,7 @@ static void nand_command_lp (struct mtd_info *mtd, unsigned command, int column,
/* Begin command latch cycle */
this->hwcontrol(mtd, NAND_CTL_SETCLE);
/* Write out the command to the device. */
- this->write_byte(mtd, command);
+ this->write_byte(mtd, (command & 0xff));
/* End command latch cycle */
this->hwcontrol(mtd, NAND_CTL_CLRCLE);
@@ -647,8 +685,8 @@ static void nand_command_lp (struct mtd_info *mtd, unsigned command, int column,
/*
* program and erase have their own busy handlers
- * status and sequential in needs no delay
- */
+ * status, sequential in, and deplete1 need no delay
+ */
switch (command) {
case NAND_CMD_CACHEDPROG:
@@ -657,8 +695,19 @@ static void nand_command_lp (struct mtd_info *mtd, unsigned command, int column,
case NAND_CMD_ERASE2:
case NAND_CMD_SEQIN:
case NAND_CMD_STATUS:
+ case NAND_CMD_DEPLETE1:
return;
+ /*
+ * read error status commands require only a short delay
+ */
+ case NAND_CMD_STATUS_ERROR:
+ case NAND_CMD_STATUS_ERROR0:
+ case NAND_CMD_STATUS_ERROR1:
+ case NAND_CMD_STATUS_ERROR2:
+ case NAND_CMD_STATUS_ERROR3:
+ udelay(this->chip_delay);
+ return;
case NAND_CMD_RESET:
if (this->dev_ready)
@@ -667,7 +716,7 @@ static void nand_command_lp (struct mtd_info *mtd, unsigned command, int column,
this->hwcontrol(mtd, NAND_CTL_SETCLE);
this->write_byte(mtd, NAND_CMD_STATUS);
this->hwcontrol(mtd, NAND_CTL_CLRCLE);
- while ( !(this->read_byte(mtd) & 0x40));
+ while ( !(this->read_byte(mtd) & NAND_STATUS_READY));
return;
case NAND_CMD_READ0:
@@ -690,12 +739,12 @@ static void nand_command_lp (struct mtd_info *mtd, unsigned command, int column,
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));
+
+ nand_wait_ready(mtd);
}
/**
@@ -708,37 +757,34 @@ static void nand_command_lp (struct mtd_info *mtd, unsigned command, int column,
*/
static void nand_get_device (struct nand_chip *this, struct mtd_info *mtd, int new_state)
{
- struct nand_chip *active = this;
-
+ struct nand_chip *active;
+ spinlock_t *lock;
+ wait_queue_head_t *wq;
DECLARE_WAITQUEUE (wait, current);
- /*
- * Grab the lock and see if the device is available
- */
+ lock = (this->controller) ? &this->controller->lock : &this->chip_lock;
+ wq = (this->controller) ? &this->controller->wq : &this->wq;
retry:
+ active = this;
+ spin_lock(lock);
+
/* 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 (&active->wq, &wait);
- spin_unlock (&active->chip_lock);
- schedule ();
- remove_wait_queue (&active->wq, &wait);
+ if (active == this && this->state == FL_READY) {
+ this->state = new_state;
+ spin_unlock(lock);
+ return;
+ }
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ add_wait_queue(wq, &wait);
+ spin_unlock(lock);
+ schedule();
+ remove_wait_queue(wq, &wait);
goto retry;
}
@@ -785,7 +831,7 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *this, int state)
if (this->read_byte(mtd) & NAND_STATUS_READY)
break;
}
- yield ();
+ cond_resched();
}
status = (int) this->read_byte(mtd);
return status;
@@ -871,8 +917,14 @@ static int nand_write_page (struct mtd_info *mtd, struct nand_chip *this, int pa
if (!cached) {
/* call wait ready function */
status = this->waitfunc (mtd, this, FL_WRITING);
+
+ /* See if operation failed and additional status checks are available */
+ if ((status & NAND_STATUS_FAIL) && (this->errstat)) {
+ status = this->errstat(mtd, this, FL_WRITING, status, page);
+ }
+
/* See if device thinks it succeeded */
- if (status & 0x01) {
+ if (status & NAND_STATUS_FAIL) {
DEBUG (MTD_DEBUG_LEVEL0, "%s: " "Failed write, page 0x%08x, ", __FUNCTION__, page);
return -EIO;
}
@@ -975,7 +1027,7 @@ static int nand_verify_pages (struct mtd_info *mtd, struct nand_chip *this, int
if (!this->dev_ready)
udelay (this->chip_delay);
else
- while (!this->dev_ready(mtd));
+ nand_wait_ready(mtd);
/* All done, return happy */
if (!numpages)
@@ -997,23 +1049,24 @@ out:
#endif
/**
- * nand_read - [MTD Interface] MTD compability function for nand_read_ecc
+ * nand_read - [MTD Interface] MTD compability function for nand_do_read_ecc
* @mtd: MTD device structure
* @from: offset to read from
* @len: number of bytes to read
* @retlen: pointer to variable to store the number of read bytes
* @buf: the databuffer to put data
*
- * This function simply calls nand_read_ecc with oob buffer and oobsel = NULL
-*/
+ * This function simply calls nand_do_read_ecc with oob buffer and oobsel = NULL
+ * and flags = 0xff
+ */
static int nand_read (struct mtd_info *mtd, loff_t from, size_t len, size_t * retlen, u_char * buf)
{
- return nand_read_ecc (mtd, from, len, retlen, buf, NULL, NULL);
-}
+ return nand_do_read_ecc (mtd, from, len, retlen, buf, NULL, &mtd->oobinfo, 0xff);
+}
/**
- * nand_read_ecc - [MTD Interface] Read data with ECC
+ * nand_read_ecc - [MTD Interface] MTD compability function for nand_do_read_ecc
* @mtd: MTD device structure
* @from: offset to read from
* @len: number of bytes to read
@@ -1022,11 +1075,39 @@ static int nand_read (struct mtd_info *mtd, loff_t from, size_t len, size_t * re
* @oob_buf: filesystem supplied oob data buffer
* @oobsel: oob selection structure
*
- * NAND read with ECC
+ * This function simply calls nand_do_read_ecc with flags = 0xff
*/
static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len,
size_t * retlen, u_char * buf, u_char * oob_buf, struct nand_oobinfo *oobsel)
{
+ /* use userspace supplied oobinfo, if zero */
+ if (oobsel == NULL)
+ oobsel = &mtd->oobinfo;
+ return nand_do_read_ecc(mtd, from, len, retlen, buf, oob_buf, oobsel, 0xff);
+}
+
+
+/**
+ * nand_do_read_ecc - [MTD Interface] Read data with ECC
+ * @mtd: MTD device structure
+ * @from: offset to read from
+ * @len: number of bytes to read
+ * @retlen: pointer to variable to store the number of read bytes
+ * @buf: the databuffer to put data
+ * @oob_buf: filesystem supplied oob data buffer (can be NULL)
+ * @oobsel: oob selection structure
+ * @flags: flag to indicate if nand_get_device/nand_release_device should be preformed
+ * and how many corrected error bits are acceptable:
+ * bits 0..7 - number of tolerable errors
+ * bit 8 - 0 == do not get/release chip, 1 == get/release chip
+ *
+ * NAND read with ECC
+ */
+int nand_do_read_ecc (struct mtd_info *mtd, loff_t from, size_t len,
+ size_t * retlen, u_char * buf, u_char * oob_buf,
+ struct nand_oobinfo *oobsel, int flags)
+{
+
int i, j, col, realpage, page, end, ecc, chipnr, sndcmd = 1;
int read = 0, oob = 0, ecc_status = 0, ecc_failed = 0;
struct nand_chip *this = mtd->priv;
@@ -1051,12 +1132,9 @@ 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_device (this, mtd ,FL_READING);
+ if (flags & NAND_GET_DEVICE)
+ nand_get_device (this, mtd, FL_READING);
- /* use userspace supplied oobinfo, if zero */
- if (oobsel == NULL)
- oobsel = &mtd->oobinfo;
-
/* Autoplace of oob data ? Use the default placement scheme */
if (oobsel->useecc == MTD_NANDECC_AUTOPLACE)
oobsel = this->autooob;
@@ -1118,7 +1196,8 @@ static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len,
}
/* get oob area, if we have no oob buffer from fs-driver */
- if (!oob_buf || oobsel->useecc == MTD_NANDECC_AUTOPLACE)
+ if (!oob_buf || oobsel->useecc == MTD_NANDECC_AUTOPLACE ||
+ oobsel->useecc == MTD_NANDECC_AUTOPL_USR)
oob_data = &this->data_buf[end];
eccsteps = this->eccsteps;
@@ -1155,7 +1234,8 @@ static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len,
/* We calc error correction directly, it checks the hw
* generator for an error, reads back the syndrome and
* does the error correction on the fly */
- if (this->correct_data(mtd, &data_poi[datidx], &oob_data[i], &ecc_code[i]) == -1) {
+ ecc_status = this->correct_data(mtd, &data_poi[datidx], &oob_data[i], &ecc_code[i]);
+ if ((ecc_status == -1) || (ecc_status > (flags && 0xff))) {
DEBUG (MTD_DEBUG_LEVEL0, "nand_read_ecc: "
"Failed ECC read, page 0x%08x on chip %d\n", page, chipnr);
ecc_failed++;
@@ -1194,7 +1274,7 @@ static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len,
p[i] = ecc_status;
}
- if (ecc_status == -1) {
+ if ((ecc_status == -1) || (ecc_status > (flags && 0xff))) {
DEBUG (MTD_DEBUG_LEVEL0, "nand_read_ecc: " "Failed ECC read, page 0x%08x\n", page);
ecc_failed++;
}
@@ -1206,14 +1286,14 @@ static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len,
/* without autoplace. Legacy mode used by YAFFS1 */
switch(oobsel->useecc) {
case MTD_NANDECC_AUTOPLACE:
+ case MTD_NANDECC_AUTOPL_USR:
/* Walk through the autoplace chunks */
- for (i = 0, j = 0; j < mtd->oobavail; i++) {
+ for (i = 0; oobsel->oobfree[i][1]; i++) {
int from = oobsel->oobfree[i][0];
int num = oobsel->oobfree[i][1];
memcpy(&oob_buf[oob], &oob_data[from], num);
- j+= num;
+ oob += num;
}
- oob += mtd->oobavail;
break;
case MTD_NANDECC_PLACE:
/* YAFFS1 legacy mode */
@@ -1239,7 +1319,7 @@ static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len,
if (!this->dev_ready)
udelay (this->chip_delay);
else
- while (!this->dev_ready(mtd));
+ nand_wait_ready(mtd);
if (read == len)
break;
@@ -1264,7 +1344,8 @@ 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_device(mtd);
+ if (flags & NAND_GET_DEVICE)
+ nand_release_device(mtd);
/*
* Return success, if no ECC failures, else -EBADMSG
@@ -1337,7 +1418,7 @@ static int nand_read_oob (struct mtd_info *mtd, loff_t from, size_t len, size_t
if (!this->dev_ready)
udelay (this->chip_delay);
else
- while (!this->dev_ready(mtd));
+ nand_wait_ready(mtd);
/* Read more ? */
if (i < len) {
@@ -1417,7 +1498,7 @@ int nand_read_raw (struct mtd_info *mtd, uint8_t *buf, loff_t from, size_t len,
if (!this->dev_ready)
udelay (this->chip_delay);
else
- while (!this->dev_ready(mtd));
+ nand_wait_ready(mtd);
/* Check, if the chip supports auto page increment */
if (!NAND_CANAUTOINCR(this) || !(page & blockcheck))
@@ -1567,6 +1648,8 @@ static int nand_write_ecc (struct mtd_info *mtd, loff_t to, size_t len,
oobsel = this->autooob;
autoplace = 1;
}
+ if (oobsel->useecc == MTD_NANDECC_AUTOPL_USR)
+ autoplace = 1;
/* Setup variables and oob buffer */
totalpages = len >> this->page_shift;
@@ -1733,7 +1816,7 @@ static int nand_write_oob (struct mtd_info *mtd, loff_t to, size_t len, size_t *
status = this->waitfunc (mtd, this, FL_WRITING);
/* See if device thinks it succeeded */
- if (status & 0x01) {
+ if (status & NAND_STATUS_FAIL) {
DEBUG (MTD_DEBUG_LEVEL0, "nand_write_oob: " "Failed write, page 0x%08x\n", page);
ret = -EIO;
goto out;
@@ -1841,6 +1924,8 @@ static int nand_writev_ecc (struct mtd_info *mtd, const struct kvec *vecs, unsig
oobsel = this->autooob;
autoplace = 1;
}
+ if (oobsel->useecc == MTD_NANDECC_AUTOPL_USR)
+ autoplace = 1;
/* Setup start page */
page = (int) (to >> this->page_shift);
@@ -1987,6 +2072,7 @@ static int nand_erase (struct mtd_info *mtd, struct erase_info *instr)
return nand_erase_nand (mtd, instr, 0);
}
+#define BBT_PAGE_MASK 0xffffff3f
/**
* nand_erase_intern - [NAND Interface] erase block(s)
* @mtd: MTD device structure
@@ -1999,6 +2085,10 @@ int nand_erase_nand (struct mtd_info *mtd, struct erase_info *instr, int allowbb
{
int page, len, status, pages_per_block, ret, chipnr;
struct nand_chip *this = mtd->priv;
+ int rewrite_bbt[NAND_MAX_CHIPS]={0}; /* flags to indicate the page, if bbt needs to be rewritten. */
+ unsigned int bbt_masked_page; /* bbt mask to compare to page being erased. */
+ /* It is used to see if the current page is in the same */
+ /* 256 block group and the same bank as the bbt. */
DEBUG (MTD_DEBUG_LEVEL3,
"nand_erase: start = 0x%08x, len = %i\n", (unsigned int) instr->addr, (unsigned int) instr->len);
@@ -2044,6 +2134,13 @@ int nand_erase_nand (struct mtd_info *mtd, struct erase_info *instr, int allowbb
goto erase_exit;
}
+ /* if BBT requires refresh, set the BBT page mask to see if the BBT should be rewritten */
+ if (this->options & BBT_AUTO_REFRESH) {
+ bbt_masked_page = this->bbt_td->pages[chipnr] & BBT_PAGE_MASK;
+ } else {
+ bbt_masked_page = 0xffffffff; /* should not match anything */
+ }
+
/* Loop through the pages */
len = instr->len;
@@ -2066,13 +2163,26 @@ int nand_erase_nand (struct mtd_info *mtd, struct erase_info *instr, int allowbb
status = this->waitfunc (mtd, this, FL_ERASING);
+ /* See if operation failed and additional status checks are available */
+ if ((status & NAND_STATUS_FAIL) && (this->errstat)) {
+ status = this->errstat(mtd, this, FL_ERASING, status, page);
+ }
+
/* See if block erase succeeded */
- if (status & 0x01) {
+ if (status & NAND_STATUS_FAIL) {
DEBUG (MTD_DEBUG_LEVEL0, "nand_erase: " "Failed erase, page 0x%08x\n", page);
instr->state = MTD_ERASE_FAILED;
instr->fail_addr = (page << this->page_shift);
goto erase_exit;
}
+
+ /* if BBT requires refresh, set the BBT rewrite flag to the page being erased */
+ if (this->options & BBT_AUTO_REFRESH) {
+ if (((page & BBT_PAGE_MASK) == bbt_masked_page) &&
+ (page != this->bbt_td->pages[chipnr])) {
+ rewrite_bbt[chipnr] = (page << this->page_shift);
+ }
+ }
/* Increment page address and decrement length */
len -= (1 << this->phys_erase_shift);
@@ -2083,6 +2193,13 @@ int nand_erase_nand (struct mtd_info *mtd, struct erase_info *instr, int allowbb
chipnr++;
this->select_chip(mtd, -1);
this->select_chip(mtd, chipnr);
+
+ /* if BBT requires refresh and BBT-PERCHIP,
+ * set the BBT page mask to see if this BBT should be rewritten */
+ if ((this->options & BBT_AUTO_REFRESH) && (this->bbt_td->options & NAND_BBT_PERCHIP)) {
+ bbt_masked_page = this->bbt_td->pages[chipnr] & BBT_PAGE_MASK;
+ }
+
}
}
instr->state = MTD_ERASE_DONE;
@@ -2097,6 +2214,18 @@ erase_exit:
/* Deselect and wake up anyone waiting on the device */
nand_release_device(mtd);
+ /* if BBT requires refresh and erase was successful, rewrite any selected bad block tables */
+ if ((this->options & BBT_AUTO_REFRESH) && (!ret)) {
+ for (chipnr = 0; chipnr < this->numchips; chipnr++) {
+ if (rewrite_bbt[chipnr]) {
+ /* update the BBT for chip */
+ DEBUG (MTD_DEBUG_LEVEL0, "nand_erase_nand: nand_update_bbt (%d:0x%0x 0x%0x)\n",
+ chipnr, rewrite_bbt[chipnr], this->bbt_td->pages[chipnr]);
+ nand_update_bbt (mtd, rewrite_bbt[chipnr]);
+ }
+ }
+ }
+
/* Return more or less happy */
return ret;
}
@@ -2168,7 +2297,7 @@ static int nand_block_markbad (struct mtd_info *mtd, loff_t ofs)
*/
int nand_scan (struct mtd_info *mtd, int maxchips)
{
- int i, j, nand_maf_id, nand_dev_id, busw;
+ int i, nand_maf_id, nand_dev_id, busw, maf_id;
struct nand_chip *this = mtd->priv;
/* Get buswidth to select the correct functions*/
@@ -2256,12 +2385,18 @@ int nand_scan (struct mtd_info *mtd, int maxchips)
busw = nand_flash_ids[i].options & NAND_BUSWIDTH_16;
}
+ /* Try to identify manufacturer */
+ for (maf_id = 0; nand_manuf_ids[maf_id].id != 0x0; maf_id++) {
+ if (nand_manuf_ids[maf_id].id == nand_maf_id)
+ break;
+ }
+
/* Check, if buswidth is correct. Hardware drivers should set
* this correct ! */
if (busw != (this->options & NAND_BUSWIDTH_16)) {
printk (KERN_INFO "NAND device: Manufacturer ID:"
" 0x%02x, Chip ID: 0x%02x (%s %s)\n", nand_maf_id, nand_dev_id,
- nand_manuf_ids[i].name , mtd->name);
+ nand_manuf_ids[maf_id].name , mtd->name);
printk (KERN_WARNING
"NAND bus width %d instead %d bit\n",
(this->options & NAND_BUSWIDTH_16) ? 16 : 8,
@@ -2300,14 +2435,9 @@ int nand_scan (struct mtd_info *mtd, int maxchips)
if (mtd->oobblock > 512 && this->cmdfunc == nand_command)
this->cmdfunc = nand_command_lp;
- /* Try to identify manufacturer */
- for (j = 0; nand_manuf_ids[j].id != 0x0; j++) {
- if (nand_manuf_ids[j].id == nand_maf_id)
- break;
- }
printk (KERN_INFO "NAND device: Manufacturer ID:"
" 0x%02x, Chip ID: 0x%02x (%s %s)\n", nand_maf_id, nand_dev_id,
- nand_manuf_ids[j].name , nand_flash_ids[i].name);
+ nand_manuf_ids[maf_id].name , nand_flash_ids[i].name);
break;
}
@@ -2388,12 +2518,9 @@ int nand_scan (struct mtd_info *mtd, int maxchips)
/* The number of bytes available for the filesystem to place fs dependend
* oob data */
- if (this->options & NAND_BUSWIDTH_16) {
- mtd->oobavail = mtd->oobsize - (this->autooob->eccbytes + 2);
- if (this->autooob->eccbytes & 0x01)
- mtd->oobavail--;
- } else
- mtd->oobavail = mtd->oobsize - (this->autooob->eccbytes + 1);
+ mtd->oobavail = 0;
+ for (i = 0; this->autooob->oobfree[i][1]; i++)
+ mtd->oobavail += this->autooob->oobfree[i][1];
/*
* check ECC mode, default to software
@@ -2524,6 +2651,10 @@ int nand_scan (struct mtd_info *mtd, int maxchips)
memcpy(&mtd->oobinfo, this->autooob, sizeof(mtd->oobinfo));
mtd->owner = THIS_MODULE;
+
+ /* Check, if we should skip the bad block table scan */
+ if (this->options & NAND_SKIP_BBTSCAN)
+ return 0;
/* Build bad block table */
return this->scan_bbt (mtd);
@@ -2555,8 +2686,8 @@ void nand_release (struct mtd_info *mtd)
kfree (this->data_buf);
}
-EXPORT_SYMBOL (nand_scan);
-EXPORT_SYMBOL (nand_release);
+EXPORT_SYMBOL_GPL (nand_scan);
+EXPORT_SYMBOL_GPL (nand_release);
MODULE_LICENSE ("GPL");
MODULE_AUTHOR ("Steven J. Hill <sjhill@realitydiluted.com>, Thomas Gleixner <tglx@linutronix.de>");
diff --git a/drivers/mtd/nand/nand_bbt.c b/drivers/mtd/nand/nand_bbt.c
index 9a1949751c1f..5ac2d2962220 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.28 2004/11/13 10:19:09 gleixner Exp $
+ * $Id: nand_bbt.c,v 1.33 2005/06/14 15:47:56 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
@@ -77,7 +77,7 @@
*/
static int check_pattern (uint8_t *buf, int len, int paglen, struct nand_bbt_descr *td)
{
- int i, end;
+ int i, end = 0;
uint8_t *p = buf;
end = paglen + td->offs;
@@ -95,9 +95,9 @@ static int check_pattern (uint8_t *buf, int len, int paglen, struct nand_bbt_des
return -1;
}
- p += td->len;
- end += td->len;
if (td->options & NAND_BBT_SCANEMPTY) {
+ p += td->len;
+ end += td->len;
for (i = end; i < len; i++) {
if (*p++ != 0xff)
return -1;
@@ -106,6 +106,32 @@ static int check_pattern (uint8_t *buf, int len, int paglen, struct nand_bbt_des
return 0;
}
+/**
+ * check_short_pattern - [GENERIC] check if a pattern is in the buffer
+ * @buf: the buffer to search
+ * @len: the length of buffer to search
+ * @paglen: the pagelength
+ * @td: search pattern descriptor
+ *
+ * Check for a pattern at the given place. Used to search bad block
+ * tables and good / bad block identifiers. Same as check_pattern, but
+ * no optional empty check and the pattern is expected to start
+ * at offset 0.
+ *
+*/
+static int check_short_pattern (uint8_t *buf, int len, int paglen, struct nand_bbt_descr *td)
+{
+ int i;
+ uint8_t *p = buf;
+
+ /* Compare the pattern */
+ for (i = 0; i < td->len; i++) {
+ if (p[i] != td->pattern[i])
+ return -1;
+ }
+ return 0;
+}
+
/**
* read_bbt - [GENERIC] Read the bad block table starting from page
* @mtd: MTD device structure
@@ -252,7 +278,7 @@ static int read_abs_bbts (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_de
* Create a bad block table by scanning the device
* for the given good/bad block identify pattern
*/
-static void create_bbt (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *bd, int chip)
+static int create_bbt (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *bd, int chip)
{
struct nand_chip *this = mtd->priv;
int i, j, numblocks, len, scanlen;
@@ -270,9 +296,17 @@ static void create_bbt (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_desc
else
len = 1;
}
- scanlen = mtd->oobblock + mtd->oobsize;
- readlen = len * mtd->oobblock;
- ooblen = len * mtd->oobsize;
+
+ if (!(bd->options & NAND_BBT_SCANEMPTY)) {
+ /* We need only read few bytes from the OOB area */
+ scanlen = ooblen = 0;
+ readlen = bd->len;
+ } else {
+ /* Full page content should be read */
+ scanlen = mtd->oobblock + mtd->oobsize;
+ readlen = len * mtd->oobblock;
+ ooblen = len * mtd->oobsize;
+ }
if (chip == -1) {
/* Note that numblocks is 2 * (real numblocks) here, see i+=2 below as it
@@ -284,7 +318,7 @@ static void create_bbt (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_desc
if (chip >= this->numchips) {
printk (KERN_WARNING "create_bbt(): chipnr (%d) > available chips (%d)\n",
chip + 1, this->numchips);
- return;
+ return -EINVAL;
}
numblocks = this->chipsize >> (this->bbt_erase_shift - 1);
startblock = chip * numblocks;
@@ -293,18 +327,41 @@ static void create_bbt (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_desc
}
for (i = startblock; i < numblocks;) {
- nand_read_raw (mtd, buf, from, readlen, ooblen);
+ int ret;
+
+ if (bd->options & NAND_BBT_SCANEMPTY)
+ if ((ret = nand_read_raw (mtd, buf, from, readlen, ooblen)))
+ return ret;
+
for (j = 0; j < len; j++) {
- if (check_pattern (&buf[j * scanlen], scanlen, mtd->oobblock, bd)) {
- this->bbt[i >> 3] |= 0x03 << (i & 0x6);
- printk (KERN_WARNING "Bad eraseblock %d at 0x%08x\n",
- i >> 1, (unsigned int) from);
- break;
+ if (!(bd->options & NAND_BBT_SCANEMPTY)) {
+ size_t retlen;
+
+ /* No need to read pages fully, just read required OOB bytes */
+ ret = mtd->read_oob(mtd, from + j * mtd->oobblock + bd->offs,
+ readlen, &retlen, &buf[0]);
+ if (ret)
+ return ret;
+
+ if (check_short_pattern (&buf[j * scanlen], scanlen, mtd->oobblock, bd)) {
+ this->bbt[i >> 3] |= 0x03 << (i & 0x6);
+ printk (KERN_WARNING "Bad eraseblock %d at 0x%08x\n",
+ i >> 1, (unsigned int) from);
+ break;
+ }
+ } else {
+ if (check_pattern (&buf[j * scanlen], scanlen, mtd->oobblock, bd)) {
+ this->bbt[i >> 3] |= 0x03 << (i & 0x6);
+ printk (KERN_WARNING "Bad eraseblock %d at 0x%08x\n",
+ i >> 1, (unsigned int) from);
+ break;
+ }
}
}
i += 2;
from += (1 << this->bbt_erase_shift);
}
+ return 0;
}
/**
@@ -589,14 +646,12 @@ write:
* The function creates a memory based bbt by scanning the device
* for manufacturer / software marked good / bad blocks
*/
-static int nand_memory_bbt (struct mtd_info *mtd, struct nand_bbt_descr *bd)
+static inline int nand_memory_bbt (struct mtd_info *mtd, struct nand_bbt_descr *bd)
{
struct nand_chip *this = mtd->priv;
- /* Ensure that we only scan for the pattern and nothing else */
- bd->options = 0;
- create_bbt (mtd, this->data_buf, bd, -1);
- return 0;
+ bd->options &= ~NAND_BBT_SCANEMPTY;
+ return create_bbt (mtd, this->data_buf, bd, -1);
}
/**
@@ -808,8 +863,14 @@ int nand_scan_bbt (struct mtd_info *mtd, struct nand_bbt_descr *bd)
/* If no primary table decriptor is given, scan the device
* to build a memory based bad block table
*/
- if (!td)
- return nand_memory_bbt(mtd, bd);
+ if (!td) {
+ if ((res = nand_memory_bbt(mtd, bd))) {
+ printk (KERN_ERR "nand_bbt: Can't scan flash and build the RAM-based BBT\n");
+ kfree (this->bbt);
+ this->bbt = NULL;
+ }
+ return res;
+ }
/* Allocate a temporary buffer for one eraseblock incl. oob */
len = (1 << this->bbt_erase_shift);
@@ -904,14 +965,11 @@ out:
}
/* Define some generic bad / good block scan pattern which are used
- * while scanning a device for factory marked good / bad blocks
- *
- * The memory based patterns just
- */
+ * while scanning a device for factory marked good / bad blocks. */
static uint8_t scan_ff_pattern[] = { 0xff, 0xff };
static struct nand_bbt_descr smallpage_memorybased = {
- .options = 0,
+ .options = NAND_BBT_SCAN2NDPAGE,
.offs = 5,
.len = 1,
.pattern = scan_ff_pattern
@@ -1042,7 +1100,7 @@ int nand_isbad_bbt (struct mtd_info *mtd, loff_t offs, int allowbbt)
res = (this->bbt[block >> 3] >> (block & 0x06)) & 0x03;
DEBUG (MTD_DEBUG_LEVEL2, "nand_isbad_bbt(): bbt info for offs 0x%08x: (block %d) 0x%02x\n",
- (unsigned int)offs, res, block >> 1);
+ (unsigned int)offs, block >> 1, res);
switch ((int)res) {
case 0x00: return 0;
diff --git a/drivers/mtd/nand/nand_ids.c b/drivers/mtd/nand/nand_ids.c
index 2d8c4321275b..efe246961b69 100644
--- a/drivers/mtd/nand/nand_ids.c
+++ b/drivers/mtd/nand/nand_ids.c
@@ -2,8 +2,8 @@
* drivers/mtd/nandids.c
*
* Copyright (C) 2002 Thomas Gleixner (tglx@linutronix.de)
- *
- * $Id: nand_ids.c,v 1.10 2004/05/26 13:40:12 gleixner Exp $
+ *
+ * $Id: nand_ids.c,v 1.14 2005/06/23 09:38:50 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
@@ -56,17 +56,24 @@ struct nand_flash_dev nand_flash_ids[] = {
{"NAND 64MiB 3,3V 16-bit", 0x56, 512, 64, 0x4000, NAND_BUSWIDTH_16},
{"NAND 128MiB 1,8V 8-bit", 0x78, 512, 128, 0x4000, 0},
+ {"NAND 128MiB 1,8V 8-bit", 0x39, 512, 128, 0x4000, 0},
{"NAND 128MiB 3,3V 8-bit", 0x79, 512, 128, 0x4000, 0},
{"NAND 128MiB 1,8V 16-bit", 0x72, 512, 128, 0x4000, NAND_BUSWIDTH_16},
+ {"NAND 128MiB 1,8V 16-bit", 0x49, 512, 128, 0x4000, NAND_BUSWIDTH_16},
{"NAND 128MiB 3,3V 16-bit", 0x74, 512, 128, 0x4000, NAND_BUSWIDTH_16},
+ {"NAND 128MiB 3,3V 16-bit", 0x59, 512, 128, 0x4000, NAND_BUSWIDTH_16},
{"NAND 256MiB 3,3V 8-bit", 0x71, 512, 256, 0x4000, 0},
- {"NAND 512MiB 3,3V 8-bit", 0xDC, 512, 512, 0x4000, 0},
-
/* These are the new chips with large page size. The pagesize
* and the erasesize is determined from the extended id bytes
*/
+ /*512 Megabit */
+ {"NAND 64MiB 1,8V 8-bit", 0xA2, 0, 64, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR},
+ {"NAND 64MiB 3,3V 8-bit", 0xF2, 0, 64, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR},
+ {"NAND 64MiB 1,8V 16-bit", 0xB2, 0, 64, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR},
+ {"NAND 64MiB 3,3V 16-bit", 0xC2, 0, 64, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR},
+
/* 1 Gigabit */
{"NAND 128MiB 1,8V 8-bit", 0xA1, 0, 128, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR},
{"NAND 128MiB 3,3V 8-bit", 0xF1, 0, 128, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR},
@@ -103,7 +110,7 @@ struct nand_flash_dev nand_flash_ids[] = {
* Anyway JFFS2 would increase the eraseblock size so we chose a combined one which can be erased in one go
* There are more speed improvements for reads and writes possible, but not implemented now
*/
- {"AND 128MiB 3,3V 8-bit", 0x01, 2048, 128, 0x4000, NAND_IS_AND | NAND_NO_AUTOINCR | NAND_4PAGE_ARRAY},
+ {"AND 128MiB 3,3V 8-bit", 0x01, 2048, 128, 0x4000, NAND_IS_AND | NAND_NO_AUTOINCR | NAND_4PAGE_ARRAY | BBT_AUTO_REFRESH},
{NULL,}
};
@@ -118,6 +125,7 @@ struct nand_manufacturers nand_manuf_ids[] = {
{NAND_MFR_NATIONAL, "National"},
{NAND_MFR_RENESAS, "Renesas"},
{NAND_MFR_STMICRO, "ST Micro"},
+ {NAND_MFR_HYNIX, "Hynix"},
{0x0, "Unknown"}
};
diff --git a/drivers/mtd/nand/nandsim.c b/drivers/mtd/nand/nandsim.c
index 13feefd7d8ca..754b6ed7ce14 100644
--- a/drivers/mtd/nand/nandsim.c
+++ b/drivers/mtd/nand/nandsim.c
@@ -22,7 +22,7 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA
*
- * $Id: nandsim.c,v 1.7 2004/12/06 11:53:06 dedekind Exp $
+ * $Id: nandsim.c,v 1.8 2005/03/19 15:33:56 dedekind Exp $
*/
#include <linux/config.h>
@@ -1484,33 +1484,6 @@ ns_nand_verify_buf(struct mtd_info *mtd, const u_char *buf, int len)
}
/*
- * Having only NAND chip IDs we call nand_scan which detects NAND flash
- * parameters and then calls scan_bbt in order to scan/find/build the
- * NAND flash bad block table. But since at that moment the NAND flash
- * image isn't allocated in the simulator, errors arise. To avoid this
- * we redefine the scan_bbt callback and initialize the nandsim structure
- * before the flash media scanning.
- */
-int ns_scan_bbt(struct mtd_info *mtd)
-{
- struct nand_chip *chip = (struct nand_chip *)mtd->priv;
- struct nandsim *ns = (struct nandsim *)(chip->priv);
- int retval;
-
- if (!NS_IS_INITIALIZED(ns))
- if ((retval = init_nandsim(mtd)) != 0) {
- NS_ERR("scan_bbt: can't initialize the nandsim structure\n");
- return retval;
- }
- if ((retval = nand_default_bbt(mtd)) != 0) {
- free_nandsim(ns);
- return retval;
- }
-
- return 0;
-}
-
-/*
* Module initialization function
*/
int __init ns_init_module(void)
@@ -1544,7 +1517,6 @@ int __init ns_init_module(void)
chip->hwcontrol = ns_hwcontrol;
chip->read_byte = ns_nand_read_byte;
chip->dev_ready = ns_device_ready;
- chip->scan_bbt = ns_scan_bbt;
chip->write_byte = ns_nand_write_byte;
chip->write_buf = ns_nand_write_buf;
chip->read_buf = ns_nand_read_buf;
@@ -1552,6 +1524,7 @@ int __init ns_init_module(void)
chip->write_word = ns_nand_write_word;
chip->read_word = ns_nand_read_word;
chip->eccmode = NAND_ECC_SOFT;
+ chip->options |= NAND_SKIP_BBTSCAN;
/*
* Perform minimum nandsim structure initialization to handle
@@ -1580,6 +1553,16 @@ int __init ns_init_module(void)
goto error;
}
+ if ((retval = init_nandsim(nsmtd)) != 0) {
+ NS_ERR("scan_bbt: can't initialize the nandsim structure\n");
+ goto error;
+ }
+
+ if ((retval = nand_default_bbt(nsmtd)) != 0) {
+ free_nandsim(nand);
+ goto error;
+ }
+
/* Register NAND as one big partition */
add_mtd_partitions(nsmtd, &nand->part, 1);
diff --git a/drivers/mtd/nand/rtc_from4.c b/drivers/mtd/nand/rtc_from4.c
index 02305a2adca7..031051cbde76 100644
--- a/drivers/mtd/nand/rtc_from4.c
+++ b/drivers/mtd/nand/rtc_from4.c
@@ -6,7 +6,7 @@
* Derived from drivers/mtd/nand/spia.c
* Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com)
*
- * $Id: rtc_from4.c,v 1.7 2004/11/04 12:53:10 gleixner Exp $
+ * $Id: rtc_from4.c,v 1.9 2005/01/24 20:40:11 dmarlin 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
@@ -83,13 +83,18 @@ static struct mtd_info *rtc_from4_mtd = NULL;
#define RTC_FROM4_RS_ECC_CHK (RTC_FROM4_NAND_ADDR_FPGA | 0x00000070)
#define RTC_FROM4_RS_ECC_CHK_ERROR (1 << 7)
+#define ERR_STAT_ECC_AVAILABLE 0x20
+
/* Undefine for software ECC */
#define RTC_FROM4_HWECC 1
+/* Define as 1 for no virtual erase blocks (in JFFS2) */
+#define RTC_FROM4_NO_VIRTBLOCKS 0
+
/*
* Module stuff
*/
-static void __iomem *rtc_from4_fio_base = P2SEGADDR(RTC_FROM4_FIO_BASE);
+static void __iomem *rtc_from4_fio_base = (void *)P2SEGADDR(RTC_FROM4_FIO_BASE);
const static struct mtd_partition partition_info[] = {
{
@@ -267,7 +272,6 @@ static void rtc_from4_nand_select_chip(struct mtd_info *mtd, int chip)
}
-
/*
* rtc_from4_nand_device_ready - hardware specific ready/busy check
* @mtd: MTD device structure
@@ -286,6 +290,40 @@ static int rtc_from4_nand_device_ready(struct mtd_info *mtd)
}
+
+/*
+ * deplete - code to perform device recovery in case there was a power loss
+ * @mtd: MTD device structure
+ * @chip: Chip to select (0 == slot 3, 1 == slot 4)
+ *
+ * If there was a sudden loss of power during an erase operation, a
+ * "device recovery" operation must be performed when power is restored
+ * to ensure correct operation. This routine performs the required steps
+ * for the requested chip.
+ *
+ * See page 86 of the data sheet for details.
+ *
+ */
+static void deplete(struct mtd_info *mtd, int chip)
+{
+ struct nand_chip *this = mtd->priv;
+
+ /* wait until device is ready */
+ while (!this->dev_ready(mtd));
+
+ this->select_chip(mtd, chip);
+
+ /* Send the commands for device recovery, phase 1 */
+ this->cmdfunc (mtd, NAND_CMD_DEPLETE1, 0x0000, 0x0000);
+ this->cmdfunc (mtd, NAND_CMD_DEPLETE2, -1, -1);
+
+ /* Send the commands for device recovery, phase 2 */
+ this->cmdfunc (mtd, NAND_CMD_DEPLETE1, 0x0000, 0x0004);
+ this->cmdfunc (mtd, NAND_CMD_DEPLETE2, -1, -1);
+
+}
+
+
#ifdef RTC_FROM4_HWECC
/*
* rtc_from4_enable_hwecc - hardware specific hardware ECC enable function
@@ -329,6 +367,7 @@ static void rtc_from4_enable_hwecc(struct mtd_info *mtd, int mode)
}
+
/*
* rtc_from4_calculate_ecc - hardware specific code to read ECC code
* @mtd: MTD device structure
@@ -356,6 +395,7 @@ static void rtc_from4_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_c
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
@@ -365,16 +405,14 @@ static void rtc_from4_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_c
*
* 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
+ * 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;
+ uint16_t par[6], syn[6];
uint8_t ecc[8];
volatile unsigned short *rs_ecc;
@@ -416,15 +454,86 @@ static int rtc_from4_correct_data(struct mtd_info *mtd, const u_char *buf, u_cha
}
/* Let the library code do its magic.*/
- res = decode_rs8(rs_decoder, buf, par, 512, syn, 0, NULL, 0xff, NULL);
+ res = decode_rs8(rs_decoder, (uint8_t *)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;
}
+
+
+/**
+ * rtc_from4_errstat - perform additional error status checks
+ * @mtd: MTD device structure
+ * @this: NAND chip structure
+ * @state: state or the operation
+ * @status: status code returned from read status
+ * @page: startpage inside the chip, must be called with (page & this->pagemask)
+ *
+ * Perform additional error status checks on erase and write failures
+ * to determine if errors are correctable. For this device, correctable
+ * 1-bit errors on erase and write are considered acceptable.
+ *
+ * note: see pages 34..37 of data sheet for details.
+ *
+ */
+static int rtc_from4_errstat(struct mtd_info *mtd, struct nand_chip *this, int state, int status, int page)
+{
+ int er_stat=0;
+ int rtn, retlen;
+ size_t len;
+ uint8_t *buf;
+ int i;
+
+ this->cmdfunc (mtd, NAND_CMD_STATUS_CLEAR, -1, -1);
+
+ if (state == FL_ERASING) {
+ for (i=0; i<4; i++) {
+ if (status & 1<<(i+1)) {
+ this->cmdfunc (mtd, (NAND_CMD_STATUS_ERROR + i + 1), -1, -1);
+ rtn = this->read_byte(mtd);
+ this->cmdfunc (mtd, NAND_CMD_STATUS_RESET, -1, -1);
+ if (!(rtn & ERR_STAT_ECC_AVAILABLE)) {
+ er_stat |= 1<<(i+1); /* err_ecc_not_avail */
+ }
+ }
+ }
+ } else if (state == FL_WRITING) {
+ /* single bank write logic */
+ this->cmdfunc (mtd, NAND_CMD_STATUS_ERROR, -1, -1);
+ rtn = this->read_byte(mtd);
+ this->cmdfunc (mtd, NAND_CMD_STATUS_RESET, -1, -1);
+ if (!(rtn & ERR_STAT_ECC_AVAILABLE)) {
+ er_stat |= 1<<1; /* err_ecc_not_avail */
+ } else {
+ len = mtd->oobblock;
+ buf = kmalloc (len, GFP_KERNEL);
+ if (!buf) {
+ printk (KERN_ERR "rtc_from4_errstat: Out of memory!\n");
+ er_stat = 1; /* if we can't check, assume failed */
+ } else {
+ /* recovery read */
+ /* page read */
+ rtn = nand_do_read_ecc (mtd, page, len, &retlen, buf, NULL, this->autooob, 1);
+ if (rtn) { /* if read failed or > 1-bit error corrected */
+ er_stat |= 1<<1; /* ECC read failed */
+ }
+ kfree(buf);
+ }
+ }
+ }
+
+ rtn = status;
+ if (er_stat == 0) { /* if ECC is available */
+ rtn = (status & ~NAND_STATUS_FAIL); /* clear the error bit */
+ }
+
+ return rtn;
+}
#endif
+
/*
* Main initialization routine
*/
@@ -432,6 +541,7 @@ int __init rtc_from4_init (void)
{
struct nand_chip *this;
unsigned short bcr1, bcr2, wcr2;
+ int i;
/* Allocate memory for MTD device structure and private data */
rtc_from4_mtd = kmalloc(sizeof(struct mtd_info) + sizeof (struct nand_chip),
@@ -483,6 +593,8 @@ int __init rtc_from4_init (void)
this->eccmode = NAND_ECC_HW8_512;
this->options |= NAND_HWECC_SYNDROME;
+ /* return the status of extra status and ECC checks */
+ this->errstat = rtc_from4_errstat;
/* set the nand_oobinfo to support FPGA H/W error detection */
this->autooob = &rtc_from4_nand_oobinfo;
this->enable_hwecc = rtc_from4_enable_hwecc;
@@ -504,6 +616,18 @@ int __init rtc_from4_init (void)
return -ENXIO;
}
+ /* Perform 'device recovery' for each chip in case there was a power loss. */
+ for (i=0; i < this->numchips; i++) {
+ deplete(rtc_from4_mtd, i);
+ }
+
+#if RTC_FROM4_NO_VIRTBLOCKS
+ /* use a smaller erase block to minimize wasted space when a block is bad */
+ /* note: this uses eight times as much RAM as using the default and makes */
+ /* mounts take four times as long. */
+ rtc_from4_mtd->flags |= MTD_NO_VIRTBLOCKS;
+#endif
+
/* Register the partitions */
add_mtd_partitions(rtc_from4_mtd, partition_info, NUM_PARTITIONS);
diff --git a/drivers/mtd/nand/s3c2410.c b/drivers/mtd/nand/s3c2410.c
index d05e9b97947d..891e3a1b9110 100644
--- a/drivers/mtd/nand/s3c2410.c
+++ b/drivers/mtd/nand/s3c2410.c
@@ -1,17 +1,24 @@
/* linux/drivers/mtd/nand/s3c2410.c
*
- * Copyright (c) 2004 Simtec Electronics
- * Ben Dooks <ben@simtec.co.uk>
+ * Copyright (c) 2004,2005 Simtec Electronics
+ * http://www.simtec.co.uk/products/SWLINUX/
+ * Ben Dooks <ben@simtec.co.uk>
*
- * Samsung S3C2410 NAND driver
+ * Samsung S3C2410/S3C240 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
+ * 18-Feb-2005 BJD Fix sparse errors
+ * 14-Mar-2005 BJD Applied tglx's code reduction patch
+ * 02-May-2005 BJD Fixed s3c2440 support
+ * 02-May-2005 BJD Reduced hwcontrol decode
+ * 20-Jun-2005 BJD Updated s3c2440 support, fixed timing bug
+ * 08-Jul-2005 BJD Fix OOPS when no platform data supplied
*
- * $Id: s3c2410.c,v 1.7 2005/01/05 18:05:14 dwmw2 Exp $
+ * $Id: s3c2410.c,v 1.14 2005/07/06 20:05:06 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
@@ -69,10 +76,10 @@ static int hardware_ecc = 0;
*/
static struct nand_oobinfo nand_hw_eccoob = {
- .useecc = MTD_NANDECC_AUTOPLACE,
- .eccbytes = 3,
- .eccpos = {0, 1, 2 },
- .oobfree = { {8, 8} }
+ .useecc = MTD_NANDECC_AUTOPLACE,
+ .eccbytes = 3,
+ .eccpos = {0, 1, 2 },
+ .oobfree = { {8, 8} }
};
/* controller and mtd information */
@@ -99,8 +106,10 @@ struct s3c2410_nand_info {
struct device *device;
struct resource *area;
struct clk *clk;
- void *regs;
+ void __iomem *regs;
int mtd_count;
+
+ unsigned char is_s3c2440;
};
/* conversion functions */
@@ -165,12 +174,12 @@ static int s3c2410_nand_inithw(struct s3c2410_nand_info *info,
/* calculate the timing information for the controller */
if (plat != NULL) {
- tacls = s3c2410_nand_calc_rate(plat->tacls, clkrate, 8);
+ tacls = s3c2410_nand_calc_rate(plat->tacls, clkrate, 4);
twrph0 = s3c2410_nand_calc_rate(plat->twrph0, clkrate, 8);
twrph1 = s3c2410_nand_calc_rate(plat->twrph1, clkrate, 8);
} else {
/* default timings */
- tacls = 8;
+ tacls = 4;
twrph0 = 8;
twrph1 = 8;
}
@@ -185,10 +194,16 @@ static int s3c2410_nand_inithw(struct s3c2410_nand_info *info,
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);
+ if (!info->is_s3c2440) {
+ cfg = S3C2410_NFCONF_EN;
+ cfg |= S3C2410_NFCONF_TACLS(tacls-1);
+ cfg |= S3C2410_NFCONF_TWRPH0(twrph0-1);
+ cfg |= S3C2410_NFCONF_TWRPH1(twrph1-1);
+ } else {
+ cfg = S3C2440_NFCONF_TACLS(tacls-1);
+ cfg |= S3C2440_NFCONF_TWRPH0(twrph0-1);
+ cfg |= S3C2440_NFCONF_TWRPH1(twrph1-1);
+ }
pr_debug(PFX "NF_CONF is 0x%lx\n", cfg);
@@ -203,17 +218,22 @@ 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;
+ void __iomem *reg;
unsigned long cur;
+ unsigned long bit;
nmtd = this->priv;
info = nmtd->info;
- cur = readl(info->regs + S3C2410_NFCONF);
+ bit = (info->is_s3c2440) ? S3C2440_NFCONT_nFCE : S3C2410_NFCONF_nFCE;
+ reg = info->regs+((info->is_s3c2440) ? S3C2440_NFCONT:S3C2410_NFCONF);
+
+ cur = readl(reg);
if (chip == -1) {
- cur |= S3C2410_NFCONF_nFCE;
+ cur |= bit;
} else {
- if (chip > nmtd->set->nr_chips) {
+ if (nmtd->set != NULL && chip > nmtd->set->nr_chips) {
printk(KERN_ERR PFX "chip %d out of range\n", chip);
return;
}
@@ -223,143 +243,76 @@ static void s3c2410_nand_select_chip(struct mtd_info *mtd, int chip)
(info->platform->select_chip)(nmtd->set, chip);
}
- cur &= ~S3C2410_NFCONF_nFCE;
+ cur &= ~bit;
}
- writel(cur, info->regs + S3C2410_NFCONF);
+ writel(cur, reg);
}
-/* command and control functions */
+/* command and control functions
+ *
+ * Note, these all use tglx's method of changing the IO_ADDR_W field
+ * to make the code simpler, and use the nand layer's code to issue the
+ * command and address sequences via the proper IO ports.
+ *
+*/
static void s3c2410_nand_hwcontrol(struct mtd_info *mtd, int cmd)
{
struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
- unsigned long cur;
+ struct nand_chip *chip = mtd->priv;
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);
+ printk(KERN_ERR "%s: called for NCE\n", __FUNCTION__);
break;
- /* we don't need to implement these */
case NAND_CTL_SETCLE:
- case NAND_CTL_CLRCLE:
+ chip->IO_ADDR_W = info->regs + S3C2410_NFCMD;
+ break;
+
case NAND_CTL_SETALE:
- case NAND_CTL_CLRALE:
- pr_debug(PFX "s3c2410_nand_hwcontrol(%d) unusedn", cmd);
+ chip->IO_ADDR_W = info->regs + S3C2410_NFADDR;
+ break;
+
+ /* NAND_CTL_CLRCLE: */
+ /* NAND_CTL_CLRALE: */
+ default:
+ chip->IO_ADDR_W = info->regs + S3C2410_NFDATA;
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.
-*/
+/* command and control functions */
-static void s3c2410_nand_command (struct mtd_info *mtd, unsigned command,
- int column, int page_addr)
+static void s3c2440_nand_hwcontrol(struct mtd_info *mtd, int cmd)
{
- register struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
- register struct nand_chip *this = mtd->priv;
+ struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
+ struct nand_chip *chip = 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);
+ switch (cmd) {
+ case NAND_CTL_SETNCE:
+ case NAND_CTL_CLRNCE:
+ printk(KERN_ERR "%s: called for NCE\n", __FUNCTION__);
+ break;
- /* Set ALE and clear CLE to start address cycle */
+ case NAND_CTL_SETCLE:
+ chip->IO_ADDR_W = info->regs + S3C2440_NFCMD;
+ break;
- if (column != -1 || page_addr != -1) {
+ case NAND_CTL_SETALE:
+ chip->IO_ADDR_W = info->regs + S3C2440_NFADDR;
+ break;
- /* 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 */
+ /* NAND_CTL_CLRCLE: */
+ /* NAND_CTL_CLRALE: */
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;
- }
+ chip->IO_ADDR_W = info->regs + S3C2440_NFDATA;
+ break;
}
-
- /* 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
@@ -369,9 +322,12 @@ static int s3c2410_nand_devready(struct mtd_info *mtd)
{
struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
+ if (info->is_s3c2440)
+ return readb(info->regs + S3C2440_NFSTAT) & S3C2440_NFSTAT_READY;
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,
@@ -394,6 +350,12 @@ static int s3c2410_nand_correct_data(struct mtd_info *mtd, u_char *dat,
return -1;
}
+/* ECC functions
+ *
+ * These allow the s3c2410 and s3c2440 to use the controller's ECC
+ * generator block to ECC the data as it passes through]
+*/
+
static void s3c2410_nand_enable_hwecc(struct mtd_info *mtd, int mode)
{
struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
@@ -404,6 +366,15 @@ static void s3c2410_nand_enable_hwecc(struct mtd_info *mtd, int mode)
writel(ctrl, info->regs + S3C2410_NFCONF);
}
+static void s3c2440_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 + S3C2440_NFCONT);
+ writel(ctrl | S3C2440_NFCONT_INITECC, info->regs + S3C2440_NFCONT);
+}
+
static int s3c2410_nand_calculate_ecc(struct mtd_info *mtd,
const u_char *dat, u_char *ecc_code)
{
@@ -420,7 +391,26 @@ static int s3c2410_nand_calculate_ecc(struct mtd_info *mtd,
}
-/* over-ride the standard functions for a little more speed? */
+static int s3c2440_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);
+ unsigned long ecc = readl(info->regs + S3C2440_NFMECC0);
+
+ ecc_code[0] = ecc;
+ ecc_code[1] = ecc >> 8;
+ ecc_code[2] = ecc >> 16;
+
+ 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. We can
+ * use read/write block to move the data buffers to/from the controller
+*/
static void s3c2410_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len)
{
@@ -523,11 +513,10 @@ static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info,
{
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->IO_ADDR_R = info->regs + S3C2410_NFDATA;
+ chip->IO_ADDR_W = 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;
@@ -536,6 +525,12 @@ static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info,
chip->options = 0;
chip->controller = &info->controller;
+ if (info->is_s3c2440) {
+ chip->IO_ADDR_R = info->regs + S3C2440_NFDATA;
+ chip->IO_ADDR_W = info->regs + S3C2440_NFDATA;
+ chip->hwcontrol = s3c2440_nand_hwcontrol;
+ }
+
nmtd->info = info;
nmtd->mtd.priv = chip;
nmtd->set = set;
@@ -546,6 +541,11 @@ static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info,
chip->calculate_ecc = s3c2410_nand_calculate_ecc;
chip->eccmode = NAND_ECC_HW3_512;
chip->autooob = &nand_hw_eccoob;
+
+ if (info->is_s3c2440) {
+ chip->enable_hwecc = s3c2440_nand_enable_hwecc;
+ chip->calculate_ecc = s3c2440_nand_calculate_ecc;
+ }
} else {
chip->eccmode = NAND_ECC_SOFT;
}
@@ -559,7 +559,7 @@ static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info,
* nand layer to look for devices
*/
-static int s3c2410_nand_probe(struct device *dev)
+static int s3c24xx_nand_probe(struct device *dev, int is_s3c2440)
{
struct platform_device *pdev = to_platform_device(dev);
struct s3c2410_platform_nand *plat = to_nand_plat(dev);
@@ -585,6 +585,7 @@ static int s3c2410_nand_probe(struct device *dev)
dev_set_drvdata(dev, info);
spin_lock_init(&info->controller.lock);
+ init_waitqueue_head(&info->controller.wq);
/* get the clock source and enable it */
@@ -600,7 +601,8 @@ static int s3c2410_nand_probe(struct device *dev)
/* allocate and map the resource */
- res = pdev->resource; /* assume that the flash has one resource */
+ /* currently we assume we have the one resource */
+ res = pdev->resource;
size = res->end - res->start + 1;
info->area = request_mem_region(res->start, size, pdev->name);
@@ -611,9 +613,10 @@ static int s3c2410_nand_probe(struct device *dev)
goto exit_error;
}
- info->device = dev;
- info->platform = plat;
- info->regs = ioremap(res->start, size);
+ info->device = dev;
+ info->platform = plat;
+ info->regs = ioremap(res->start, size);
+ info->is_s3c2440 = is_s3c2440;
if (info->regs == NULL) {
printk(KERN_ERR PFX "cannot reserve register region\n");
@@ -678,6 +681,18 @@ static int s3c2410_nand_probe(struct device *dev)
return err;
}
+/* driver device registration */
+
+static int s3c2410_nand_probe(struct device *dev)
+{
+ return s3c24xx_nand_probe(dev, 0);
+}
+
+static int s3c2440_nand_probe(struct device *dev)
+{
+ return s3c24xx_nand_probe(dev, 1);
+}
+
static struct device_driver s3c2410_nand_driver = {
.name = "s3c2410-nand",
.bus = &platform_bus_type,
@@ -685,14 +700,24 @@ static struct device_driver s3c2410_nand_driver = {
.remove = s3c2410_nand_remove,
};
+static struct device_driver s3c2440_nand_driver = {
+ .name = "s3c2440-nand",
+ .bus = &platform_bus_type,
+ .probe = s3c2440_nand_probe,
+ .remove = s3c2410_nand_remove,
+};
+
static int __init s3c2410_nand_init(void)
{
- printk("S3C2410 NAND Driver, (c) 2004 Simtec Electronics\n");
+ printk("S3C24XX NAND Driver, (c) 2004 Simtec Electronics\n");
+
+ driver_register(&s3c2440_nand_driver);
return driver_register(&s3c2410_nand_driver);
}
static void __exit s3c2410_nand_exit(void)
{
+ driver_unregister(&s3c2440_nand_driver);
driver_unregister(&s3c2410_nand_driver);
}
@@ -701,4 +726,4 @@ module_exit(s3c2410_nand_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>");
-MODULE_DESCRIPTION("S3C2410 MTD NAND driver");
+MODULE_DESCRIPTION("S3C24XX MTD NAND driver");
diff --git a/drivers/mtd/nand/sharpsl.c b/drivers/mtd/nand/sharpsl.c
index 29572793334c..9853b87bb756 100755..100644
--- a/drivers/mtd/nand/sharpsl.c
+++ b/drivers/mtd/nand/sharpsl.c
@@ -3,7 +3,7 @@
*
* Copyright (C) 2004 Richard Purdie
*
- * $Id: sharpsl.c,v 1.3 2005/01/03 14:53:50 rpurdie Exp $
+ * $Id: sharpsl.c,v 1.4 2005/01/23 11:09:19 rpurdie Exp $
*
* Based on Sharp's NAND driver sharp_sl.c
*
@@ -216,7 +216,7 @@ sharpsl_nand_init(void)
nr_partitions = DEFAULT_NUM_PARTITIONS;
sharpsl_partition_info = sharpsl_nand_default_partition_info;
if (machine_is_poodle()) {
- sharpsl_partition_info[1].size=22 * 1024 * 1024;
+ sharpsl_partition_info[1].size=30 * 1024 * 1024;
} else if (machine_is_corgi() || machine_is_shepherd()) {
sharpsl_partition_info[1].size=25 * 1024 * 1024;
} else if (machine_is_husky()) {
diff --git a/drivers/mtd/nand/tx4925ndfmc.c b/drivers/mtd/nand/tx4925ndfmc.c
deleted file mode 100644
index bba688830c9b..000000000000
--- a/drivers/mtd/nand/tx4925ndfmc.c
+++ /dev/null
@@ -1,416 +0,0 @@
-/*
- * drivers/mtd/tx4925ndfmc.c
- *
- * Overview:
- * This is a device driver for the NAND flash device found on the
- * Toshiba RBTX4925 reference board, which is a SmartMediaCard. It supports
- * 16MiB, 32MiB and 64MiB cards.
- *
- * Author: MontaVista Software, Inc. source@mvista.com
- *
- * Derived from drivers/mtd/autcpu12.c
- * Copyright (c) 2001 Thomas Gleixner (gleixner@autronix.de)
- *
- * $Id: tx4925ndfmc.c,v 1.5 2004/10/05 13:50:20 gleixner Exp $
- *
- * Copyright (C) 2001 Toshiba Corporation
- *
- * 2003 (c) MontaVista Software, Inc. This file is licensed under
- * the terms of the GNU General Public License version 2. This program
- * is licensed "as is" without any warranty of any kind, whether express
- * or implied.
- *
- */
-
-#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 <linux/delay.h>
-#include <asm/io.h>
-#include <asm/tx4925/tx4925_nand.h>
-
-extern struct nand_oobinfo jffs2_oobinfo;
-
-/*
- * MTD structure for RBTX4925 board
- */
-static struct mtd_info *tx4925ndfmc_mtd = NULL;
-
-/*
- * Define partitions for flash devices
- */
-
-static struct mtd_partition partition_info16k[] = {
- { .name = "RBTX4925 flash partition 1",
- .offset = 0,
- .size = 8 * 0x00100000 },
- { .name = "RBTX4925 flash partition 2",
- .offset = 8 * 0x00100000,
- .size = 8 * 0x00100000 },
-};
-
-static struct mtd_partition partition_info32k[] = {
- { .name = "RBTX4925 flash partition 1",
- .offset = 0,
- .size = 8 * 0x00100000 },
- { .name = "RBTX4925 flash partition 2",
- .offset = 8 * 0x00100000,
- .size = 24 * 0x00100000 },
-};
-
-static struct mtd_partition partition_info64k[] = {
- { .name = "User FS",
- .offset = 0,
- .size = 16 * 0x00100000 },
- { .name = "RBTX4925 flash partition 2",
- .offset = 16 * 0x00100000,
- .size = 48 * 0x00100000},
-};
-
-static struct mtd_partition partition_info128k[] = {
- { .name = "Skip bad section",
- .offset = 0,
- .size = 16 * 0x00100000 },
- { .name = "User FS",
- .offset = 16 * 0x00100000,
- .size = 112 * 0x00100000 },
-};
-#define NUM_PARTITIONS16K 2
-#define NUM_PARTITIONS32K 2
-#define NUM_PARTITIONS64K 2
-#define NUM_PARTITIONS128K 2
-
-/*
- * hardware specific access to control-lines
-*/
-static void tx4925ndfmc_hwcontrol(struct mtd_info *mtd, int cmd)
-{
-
- switch(cmd){
-
- case NAND_CTL_SETCLE:
- tx4925_ndfmcptr->mcr |= TX4925_NDFMCR_CLE;
- break;
- case NAND_CTL_CLRCLE:
- tx4925_ndfmcptr->mcr &= ~TX4925_NDFMCR_CLE;
- break;
- case NAND_CTL_SETALE:
- tx4925_ndfmcptr->mcr |= TX4925_NDFMCR_ALE;
- break;
- case NAND_CTL_CLRALE:
- tx4925_ndfmcptr->mcr &= ~TX4925_NDFMCR_ALE;
- break;
- case NAND_CTL_SETNCE:
- tx4925_ndfmcptr->mcr |= TX4925_NDFMCR_CE;
- break;
- case NAND_CTL_CLRNCE:
- tx4925_ndfmcptr->mcr &= ~TX4925_NDFMCR_CE;
- break;
- case NAND_CTL_SETWP:
- tx4925_ndfmcptr->mcr |= TX4925_NDFMCR_WE;
- break;
- case NAND_CTL_CLRWP:
- tx4925_ndfmcptr->mcr &= ~TX4925_NDFMCR_WE;
- break;
- }
-}
-
-/*
-* read device ready pin
-*/
-static int tx4925ndfmc_device_ready(struct mtd_info *mtd)
-{
- int ready;
- ready = (tx4925_ndfmcptr->sr & TX4925_NDSFR_BUSY) ? 0 : 1;
- return ready;
-}
-void tx4925ndfmc_enable_hwecc(struct mtd_info *mtd, int mode)
-{
- /* reset first */
- tx4925_ndfmcptr->mcr |= TX4925_NDFMCR_ECC_CNTL_MASK;
- tx4925_ndfmcptr->mcr &= ~TX4925_NDFMCR_ECC_CNTL_MASK;
- tx4925_ndfmcptr->mcr |= TX4925_NDFMCR_ECC_CNTL_ENAB;
-}
-static void tx4925ndfmc_disable_ecc(void)
-{
- tx4925_ndfmcptr->mcr &= ~TX4925_NDFMCR_ECC_CNTL_MASK;
-}
-static void tx4925ndfmc_enable_read_ecc(void)
-{
- tx4925_ndfmcptr->mcr &= ~TX4925_NDFMCR_ECC_CNTL_MASK;
- tx4925_ndfmcptr->mcr |= TX4925_NDFMCR_ECC_CNTL_READ;
-}
-void tx4925ndfmc_readecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code){
- int i;
- u_char *ecc = ecc_code;
- tx4925ndfmc_enable_read_ecc();
- for (i = 0;i < 6;i++,ecc++)
- *ecc = tx4925_read_nfmc(&(tx4925_ndfmcptr->dtr));
- tx4925ndfmc_disable_ecc();
-}
-void tx4925ndfmc_device_setup(void)
-{
-
- *(unsigned char *)0xbb005000 &= ~0x08;
-
- /* reset NDFMC */
- tx4925_ndfmcptr->rstr |= TX4925_NDFRSTR_RST;
- while (tx4925_ndfmcptr->rstr & TX4925_NDFRSTR_RST);
-
- /* setup BusSeparete, Hold Time, Strobe Pulse Width */
- tx4925_ndfmcptr->mcr = TX4925_BSPRT ? TX4925_NDFMCR_BSPRT : 0;
- tx4925_ndfmcptr->spr = TX4925_HOLD << 4 | TX4925_SPW;
-}
-static u_char tx4925ndfmc_nand_read_byte(struct mtd_info *mtd)
-{
- struct nand_chip *this = mtd->priv;
- return tx4925_read_nfmc(this->IO_ADDR_R);
-}
-
-static void tx4925ndfmc_nand_write_byte(struct mtd_info *mtd, u_char byte)
-{
- struct nand_chip *this = mtd->priv;
- tx4925_write_nfmc(byte, this->IO_ADDR_W);
-}
-
-static void tx4925ndfmc_nand_write_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++)
- tx4925_write_nfmc(buf[i], this->IO_ADDR_W);
-}
-
-static void tx4925ndfmc_nand_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++)
- buf[i] = tx4925_read_nfmc(this->IO_ADDR_R);
-}
-
-static int tx4925ndfmc_nand_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++)
- if (buf[i] != tx4925_read_nfmc(this->IO_ADDR_R))
- return -EFAULT;
-
- return 0;
-}
-
-/*
- * Send command to NAND device
- */
-static void tx4925ndfmc_nand_command (struct mtd_info *mtd, unsigned command, int column, int page_addr)
-{
- register struct nand_chip *this = mtd->priv;
-
- /* Begin command latch cycle */
- this->hwcontrol(mtd, NAND_CTL_SETCLE);
- /*
- * 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;
- }
- this->write_byte(mtd, readcmd);
- }
- this->write_byte(mtd, command);
-
- /* Set ALE and clear CLE to start address cycle */
- this->hwcontrol(mtd, NAND_CTL_CLRCLE);
-
- if (column != -1 || page_addr != -1) {
- this->hwcontrol(mtd, NAND_CTL_SETALE);
-
- /* Serially input address */
- if (column != -1)
- this->write_byte(mtd, column);
- 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 (mtd->size & 0x0c000000)
- this->write_byte(mtd, (unsigned char) ((page_addr >> 16) & 0x0f));
- }
- /* Latch in address */
- this->hwcontrol(mtd, NAND_CTL_CLRALE);
- }
-
- /*
- * program and erase have their own busy handlers
- * status and sequential in needs no delay
- */
- switch (command) {
-
- case NAND_CMD_PAGEPROG:
- /* Turn off WE */
- this->hwcontrol (mtd, NAND_CTL_CLRWP);
- return;
-
- case NAND_CMD_SEQIN:
- /* Turn on WE */
- this->hwcontrol (mtd, NAND_CTL_SETWP);
- return;
-
- case NAND_CMD_ERASE1:
- case NAND_CMD_ERASE2:
- case NAND_CMD_STATUS:
- return;
-
- case NAND_CMD_RESET:
- if (this->dev_ready)
- break;
- this->hwcontrol(mtd, NAND_CTL_SETCLE);
- this->write_byte(mtd, NAND_CMD_STATUS);
- this->hwcontrol(mtd, NAND_CTL_CLRCLE);
- 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;
- }
- }
-
- /* wait until command is processed */
- while (!this->dev_ready(mtd));
-}
-
-#ifdef CONFIG_MTD_CMDLINE_PARTS
-extern int parse_cmdline_partitions(struct mtd_info *master, struct mtd_partitio
-n **pparts, char *);
-#endif
-
-/*
- * Main initialization routine
- */
-extern int nand_correct_data(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, u_char *calc_ecc);
-int __init tx4925ndfmc_init (void)
-{
- struct nand_chip *this;
- int err = 0;
-
- /* Allocate memory for MTD device structure and private data */
- tx4925ndfmc_mtd = kmalloc (sizeof(struct mtd_info) + sizeof (struct nand_chip),
- GFP_KERNEL);
- if (!tx4925ndfmc_mtd) {
- printk ("Unable to allocate RBTX4925 NAND MTD device structure.\n");
- err = -ENOMEM;
- goto out;
- }
-
- tx4925ndfmc_device_setup();
-
- /* io is indirect via a register so don't need to ioremap address */
-
- /* Get pointer to private data */
- this = (struct nand_chip *) (&tx4925ndfmc_mtd[1]);
-
- /* Initialize structures */
- memset((char *) tx4925ndfmc_mtd, 0, sizeof(struct mtd_info));
- memset((char *) this, 0, sizeof(struct nand_chip));
-
- /* Link the private data with the MTD structure */
- tx4925ndfmc_mtd->priv = this;
-
- /* Set address of NAND IO lines */
- 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;
- this->correct_data = nand_correct_data;
- this->eccmode = NAND_ECC_HW6_512;
- this->dev_ready = tx4925ndfmc_device_ready;
- /* 20 us command delay time */
- this->chip_delay = 20;
- this->read_byte = tx4925ndfmc_nand_read_byte;
- this->write_byte = tx4925ndfmc_nand_write_byte;
- this->cmdfunc = tx4925ndfmc_nand_command;
- this->write_buf = tx4925ndfmc_nand_write_buf;
- this->read_buf = tx4925ndfmc_nand_read_buf;
- this->verify_buf = tx4925ndfmc_nand_verify_buf;
-
- /* Scan to find existance of the device */
- if (nand_scan (tx4925ndfmc_mtd, 1)) {
- err = -ENXIO;
- goto out_ior;
- }
-
- /* Register the partitions */
-#ifdef CONFIG_MTD_CMDLINE_PARTS
- {
- int mtd_parts_nb = 0;
- struct mtd_partition *mtd_parts = 0;
- mtd_parts_nb = parse_cmdline_partitions(tx4925ndfmc_mtd, &mtd_parts, "tx4925ndfmc");
- if (mtd_parts_nb > 0)
- add_mtd_partitions(tx4925ndfmc_mtd, mtd_parts, mtd_parts_nb);
- else
- add_mtd_device(tx4925ndfmc_mtd);
- }
-#else /* ifdef CONFIG_MTD_CMDLINE_PARTS */
- switch(tx4925ndfmc_mtd->size){
- case 0x01000000: add_mtd_partitions(tx4925ndfmc_mtd, partition_info16k, NUM_PARTITIONS16K); break;
- case 0x02000000: add_mtd_partitions(tx4925ndfmc_mtd, partition_info32k, NUM_PARTITIONS32K); break;
- case 0x04000000: add_mtd_partitions(tx4925ndfmc_mtd, partition_info64k, NUM_PARTITIONS64K); break;
- case 0x08000000: add_mtd_partitions(tx4925ndfmc_mtd, partition_info128k, NUM_PARTITIONS128K); break;
- default: {
- printk ("Unsupported SmartMedia device\n");
- err = -ENXIO;
- goto out_ior;
- }
- }
-#endif /* ifdef CONFIG_MTD_CMDLINE_PARTS */
- goto out;
-
-out_ior:
-out:
- return err;
-}
-
-module_init(tx4925ndfmc_init);
-
-/*
- * Clean up routine
- */
-#ifdef MODULE
-static void __exit tx4925ndfmc_cleanup (void)
-{
- /* Release resources, unregister device */
- nand_release (tx4925ndfmc_mtd);
-
- /* Free the MTD device structure */
- kfree (tx4925ndfmc_mtd);
-}
-module_exit(tx4925ndfmc_cleanup);
-#endif
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Alice Hennessy <ahennessy@mvista.com>");
-MODULE_DESCRIPTION("Glue layer for SmartMediaCard on Toshiba RBTX4925");
diff --git a/drivers/mtd/nand/tx4938ndfmc.c b/drivers/mtd/nand/tx4938ndfmc.c
deleted file mode 100644
index df26e58820b3..000000000000
--- a/drivers/mtd/nand/tx4938ndfmc.c
+++ /dev/null
@@ -1,406 +0,0 @@
-/*
- * drivers/mtd/nand/tx4938ndfmc.c
- *
- * Overview:
- * This is a device driver for the NAND flash device connected to
- * TX4938 internal NAND Memory Controller.
- * TX4938 NDFMC is almost same as TX4925 NDFMC, but register size are 64 bit.
- *
- * Author: source@mvista.com
- *
- * Based on spia.c by Steven J. Hill
- *
- * $Id: tx4938ndfmc.c,v 1.4 2004/10/05 13:50:20 gleixner Exp $
- *
- * Copyright (C) 2000-2001 Toshiba Corporation
- *
- * 2003 (c) MontaVista Software, Inc. This file is licensed under the
- * terms of the GNU General Public License version 2. This program is
- * licensed "as is" without any warranty of any kind, whether express
- * or implied.
- */
-#include <linux/config.h>
-#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/nand_ecc.h>
-#include <linux/mtd/partitions.h>
-#include <asm/io.h>
-#include <asm/bootinfo.h>
-#include <linux/delay.h>
-#include <asm/tx4938/rbtx4938.h>
-
-extern struct nand_oobinfo jffs2_oobinfo;
-
-/*
- * MTD structure for TX4938 NDFMC
- */
-static struct mtd_info *tx4938ndfmc_mtd;
-
-/*
- * Define partitions for flash device
- */
-#define flush_wb() (void)tx4938_ndfmcptr->mcr;
-
-#define NUM_PARTITIONS 3
-#define NUMBER_OF_CIS_BLOCKS 24
-#define SIZE_OF_BLOCK 0x00004000
-#define NUMBER_OF_BLOCK_PER_ZONE 1024
-#define SIZE_OF_ZONE (NUMBER_OF_BLOCK_PER_ZONE * SIZE_OF_BLOCK)
-#ifndef CONFIG_MTD_CMDLINE_PARTS
-/*
- * You can use the following sample of MTD partitions
- * on the NAND Flash Memory 32MB or more.
- *
- * The following figure shows the image of the sample partition on
- * the 32MB NAND Flash Memory.
- *
- * Block No.
- * 0 +-----------------------------+ ------
- * | CIS | ^
- * 24 +-----------------------------+ |
- * | kernel image | | Zone 0
- * | | |
- * +-----------------------------+ |
- * 1023 | unused area | v
- * +-----------------------------+ ------
- * 1024 | JFFS2 | ^
- * | | |
- * | | | Zone 1
- * | | |
- * | | |
- * | | v
- * 2047 +-----------------------------+ ------
- *
- */
-static struct mtd_partition partition_info[NUM_PARTITIONS] = {
- {
- .name = "RBTX4938 CIS Area",
- .offset = 0,
- .size = (NUMBER_OF_CIS_BLOCKS * SIZE_OF_BLOCK),
- .mask_flags = MTD_WRITEABLE /* This partition is NOT writable */
- },
- {
- .name = "RBTX4938 kernel image",
- .offset = MTDPART_OFS_APPEND,
- .size = 8 * 0x00100000, /* 8MB (Depends on size of kernel image) */
- .mask_flags = MTD_WRITEABLE /* This partition is NOT writable */
- },
- {
- .name = "Root FS (JFFS2)",
- .offset = (0 + SIZE_OF_ZONE), /* start address of next zone */
- .size = MTDPART_SIZ_FULL
- },
-};
-#endif
-
-static void tx4938ndfmc_hwcontrol(struct mtd_info *mtd, int cmd)
-{
- switch (cmd) {
- case NAND_CTL_SETCLE:
- tx4938_ndfmcptr->mcr |= TX4938_NDFMCR_CLE;
- break;
- case NAND_CTL_CLRCLE:
- tx4938_ndfmcptr->mcr &= ~TX4938_NDFMCR_CLE;
- break;
- case NAND_CTL_SETALE:
- tx4938_ndfmcptr->mcr |= TX4938_NDFMCR_ALE;
- break;
- case NAND_CTL_CLRALE:
- tx4938_ndfmcptr->mcr &= ~TX4938_NDFMCR_ALE;
- break;
- /* TX4938_NDFMCR_CE bit is 0:high 1:low */
- case NAND_CTL_SETNCE:
- tx4938_ndfmcptr->mcr |= TX4938_NDFMCR_CE;
- break;
- case NAND_CTL_CLRNCE:
- tx4938_ndfmcptr->mcr &= ~TX4938_NDFMCR_CE;
- break;
- case NAND_CTL_SETWP:
- tx4938_ndfmcptr->mcr |= TX4938_NDFMCR_WE;
- break;
- case NAND_CTL_CLRWP:
- tx4938_ndfmcptr->mcr &= ~TX4938_NDFMCR_WE;
- break;
- }
-}
-static int tx4938ndfmc_dev_ready(struct mtd_info *mtd)
-{
- flush_wb();
- return !(tx4938_ndfmcptr->sr & TX4938_NDFSR_BUSY);
-}
-static void tx4938ndfmc_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code)
-{
- u32 mcr = tx4938_ndfmcptr->mcr;
- mcr &= ~TX4938_NDFMCR_ECC_ALL;
- tx4938_ndfmcptr->mcr = mcr | TX4938_NDFMCR_ECC_OFF;
- tx4938_ndfmcptr->mcr = mcr | TX4938_NDFMCR_ECC_READ;
- ecc_code[1] = tx4938_ndfmcptr->dtr;
- ecc_code[0] = tx4938_ndfmcptr->dtr;
- ecc_code[2] = tx4938_ndfmcptr->dtr;
- tx4938_ndfmcptr->mcr = mcr | TX4938_NDFMCR_ECC_OFF;
-}
-static void tx4938ndfmc_enable_hwecc(struct mtd_info *mtd, int mode)
-{
- u32 mcr = tx4938_ndfmcptr->mcr;
- mcr &= ~TX4938_NDFMCR_ECC_ALL;
- tx4938_ndfmcptr->mcr = mcr | TX4938_NDFMCR_ECC_RESET;
- tx4938_ndfmcptr->mcr = mcr | TX4938_NDFMCR_ECC_OFF;
- tx4938_ndfmcptr->mcr = mcr | TX4938_NDFMCR_ECC_ON;
-}
-
-static u_char tx4938ndfmc_nand_read_byte(struct mtd_info *mtd)
-{
- struct nand_chip *this = mtd->priv;
- return tx4938_read_nfmc(this->IO_ADDR_R);
-}
-
-static void tx4938ndfmc_nand_write_byte(struct mtd_info *mtd, u_char byte)
-{
- struct nand_chip *this = mtd->priv;
- tx4938_write_nfmc(byte, this->IO_ADDR_W);
-}
-
-static void tx4938ndfmc_nand_write_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++)
- tx4938_write_nfmc(buf[i], this->IO_ADDR_W);
-}
-
-static void tx4938ndfmc_nand_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++)
- buf[i] = tx4938_read_nfmc(this->IO_ADDR_R);
-}
-
-static int tx4938ndfmc_nand_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++)
- if (buf[i] != tx4938_read_nfmc(this->IO_ADDR_R))
- return -EFAULT;
-
- return 0;
-}
-
-/*
- * Send command to NAND device
- */
-static void tx4938ndfmc_nand_command (struct mtd_info *mtd, unsigned command, int column, int page_addr)
-{
- register struct nand_chip *this = mtd->priv;
-
- /* Begin command latch cycle */
- this->hwcontrol(mtd, NAND_CTL_SETCLE);
- /*
- * 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;
- }
- this->write_byte(mtd, readcmd);
- }
- this->write_byte(mtd, command);
-
- /* Set ALE and clear CLE to start address cycle */
- this->hwcontrol(mtd, NAND_CTL_CLRCLE);
-
- if (column != -1 || page_addr != -1) {
- this->hwcontrol(mtd, NAND_CTL_SETALE);
-
- /* Serially input address */
- if (column != -1)
- this->write_byte(mtd, column);
- 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 (mtd->size & 0x0c000000)
- this->write_byte(mtd, (unsigned char) ((page_addr >> 16) & 0x0f));
- }
- /* Latch in address */
- this->hwcontrol(mtd, NAND_CTL_CLRALE);
- }
-
- /*
- * program and erase have their own busy handlers
- * status and sequential in needs no delay
- */
- switch (command) {
-
- case NAND_CMD_PAGEPROG:
- /* Turn off WE */
- this->hwcontrol (mtd, NAND_CTL_CLRWP);
- return;
-
- case NAND_CMD_SEQIN:
- /* Turn on WE */
- this->hwcontrol (mtd, NAND_CTL_SETWP);
- return;
-
- case NAND_CMD_ERASE1:
- case NAND_CMD_ERASE2:
- case NAND_CMD_STATUS:
- return;
-
- case NAND_CMD_RESET:
- if (this->dev_ready)
- break;
- this->hwcontrol(mtd, NAND_CTL_SETCLE);
- this->write_byte(mtd, NAND_CMD_STATUS);
- this->hwcontrol(mtd, NAND_CTL_CLRCLE);
- 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;
- }
- }
-
- /* wait until command is processed */
- while (!this->dev_ready(mtd));
-}
-
-#ifdef CONFIG_MTD_CMDLINE_PARTS
-extern int parse_cmdline_partitions(struct mtd_info *master, struct mtd_partition **pparts, char *);
-#endif
-/*
- * Main initialization routine
- */
-int __init tx4938ndfmc_init (void)
-{
- struct nand_chip *this;
- int bsprt = 0, hold = 0xf, spw = 0xf;
- int protected = 0;
-
- if ((*rbtx4938_piosel_ptr & 0x0c) != 0x08) {
- printk("TX4938 NDFMC: disabled by IOC PIOSEL\n");
- return -ENODEV;
- }
- bsprt = 1;
- hold = 2;
- spw = 9 - 1; /* 8 GBUSCLK = 80ns (@ GBUSCLK 100MHz) */
-
- if ((tx4938_ccfgptr->pcfg &
- (TX4938_PCFG_ATA_SEL|TX4938_PCFG_ISA_SEL|TX4938_PCFG_NDF_SEL))
- != TX4938_PCFG_NDF_SEL) {
- printk("TX4938 NDFMC: disabled by PCFG.\n");
- return -ENODEV;
- }
-
- /* reset NDFMC */
- tx4938_ndfmcptr->rstr |= TX4938_NDFRSTR_RST;
- while (tx4938_ndfmcptr->rstr & TX4938_NDFRSTR_RST)
- ;
- /* setup BusSeparete, Hold Time, Strobe Pulse Width */
- tx4938_ndfmcptr->mcr = bsprt ? TX4938_NDFMCR_BSPRT : 0;
- tx4938_ndfmcptr->spr = hold << 4 | spw;
-
- /* Allocate memory for MTD device structure and private data */
- tx4938ndfmc_mtd = kmalloc (sizeof(struct mtd_info) + sizeof (struct nand_chip),
- GFP_KERNEL);
- if (!tx4938ndfmc_mtd) {
- printk ("Unable to allocate TX4938 NDFMC MTD device structure.\n");
- return -ENOMEM;
- }
-
- /* Get pointer to private data */
- this = (struct nand_chip *) (&tx4938ndfmc_mtd[1]);
-
- /* Initialize structures */
- memset((char *) tx4938ndfmc_mtd, 0, sizeof(struct mtd_info));
- memset((char *) this, 0, sizeof(struct nand_chip));
-
- /* Link the private data with the MTD structure */
- tx4938ndfmc_mtd->priv = this;
-
- /* Set address of NAND IO lines */
- this->IO_ADDR_R = (unsigned long)&tx4938_ndfmcptr->dtr;
- this->IO_ADDR_W = (unsigned long)&tx4938_ndfmcptr->dtr;
- this->hwcontrol = tx4938ndfmc_hwcontrol;
- this->dev_ready = tx4938ndfmc_dev_ready;
- this->calculate_ecc = tx4938ndfmc_calculate_ecc;
- this->correct_data = nand_correct_data;
- this->enable_hwecc = tx4938ndfmc_enable_hwecc;
- this->eccmode = NAND_ECC_HW3_256;
- this->chip_delay = 100;
- this->read_byte = tx4938ndfmc_nand_read_byte;
- this->write_byte = tx4938ndfmc_nand_write_byte;
- this->cmdfunc = tx4938ndfmc_nand_command;
- this->write_buf = tx4938ndfmc_nand_write_buf;
- this->read_buf = tx4938ndfmc_nand_read_buf;
- this->verify_buf = tx4938ndfmc_nand_verify_buf;
-
- /* Scan to find existance of the device */
- if (nand_scan (tx4938ndfmc_mtd, 1)) {
- kfree (tx4938ndfmc_mtd);
- return -ENXIO;
- }
-
- if (protected) {
- printk(KERN_INFO "TX4938 NDFMC: write protected.\n");
- tx4938ndfmc_mtd->flags &= ~(MTD_WRITEABLE | MTD_ERASEABLE);
- }
-
-#ifdef CONFIG_MTD_CMDLINE_PARTS
- {
- int mtd_parts_nb = 0;
- struct mtd_partition *mtd_parts = 0;
- mtd_parts_nb = parse_cmdline_partitions(tx4938ndfmc_mtd, &mtd_parts, "tx4938ndfmc");
- if (mtd_parts_nb > 0)
- add_mtd_partitions(tx4938ndfmc_mtd, mtd_parts, mtd_parts_nb);
- else
- add_mtd_device(tx4938ndfmc_mtd);
- }
-#else
- add_mtd_partitions(tx4938ndfmc_mtd, partition_info, NUM_PARTITIONS );
-#endif
-
- return 0;
-}
-module_init(tx4938ndfmc_init);
-
-/*
- * Clean up routine
- */
-static void __exit tx4938ndfmc_cleanup (void)
-{
- /* Release resources, unregister device */
- nand_release (tx4938ndfmc_mtd);
-
- /* Free the MTD device structure */
- kfree (tx4938ndfmc_mtd);
-}
-module_exit(tx4938ndfmc_cleanup);
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Alice Hennessy <ahennessy@mvista.com>");
-MODULE_DESCRIPTION("Board-specific glue layer for NAND flash on TX4938 NDFMC");