diff options
Diffstat (limited to 'drivers/mtd/nand')
-rw-r--r-- | drivers/mtd/nand/Kconfig | 21 | ||||
-rw-r--r-- | drivers/mtd/nand/Makefile | 2 | ||||
-rw-r--r-- | drivers/mtd/nand/diskonchip.c | 96 | ||||
-rw-r--r-- | drivers/mtd/nand/nand_base.c | 299 | ||||
-rw-r--r-- | drivers/mtd/nand/nand_bbt.c | 114 | ||||
-rw-r--r-- | drivers/mtd/nand/nand_ids.c | 18 | ||||
-rw-r--r-- | drivers/mtd/nand/nandsim.c | 41 | ||||
-rw-r--r-- | drivers/mtd/nand/rtc_from4.c | 140 | ||||
-rw-r--r-- | drivers/mtd/nand/s3c2410.c | 297 | ||||
-rw-r--r--[-rwxr-xr-x] | drivers/mtd/nand/sharpsl.c | 4 | ||||
-rw-r--r-- | drivers/mtd/nand/tx4925ndfmc.c | 416 | ||||
-rw-r--r-- | drivers/mtd/nand/tx4938ndfmc.c | 406 |
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"); |