diff options
Diffstat (limited to 'drivers/mtd/nand/nand_base.c')
| -rw-r--r-- | drivers/mtd/nand/nand_base.c | 3445 | 
1 files changed, 1776 insertions, 1669 deletions
| diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 6416d1529..a29ff1146 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -10,39 +10,21 @@   *	http://www.linux-mtd.infradead.org/tech/nand.html   *   *  Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com) - *		  2002 Thomas Gleixner (tglx@linutronix.de) + *		  2002-2006 Thomas Gleixner (tglx@linutronix.de)   * - *  02-08-2004  tglx: support for strange chips, which cannot auto increment - *		pages on read / read_oob - * - *  03-17-2004  tglx: Check ready before auto increment check. Simon Bayes - *		pointed this out, as he marked an auto increment capable chip - *		as NOAUTOINCR in the board driver. - *		Make reads over block boundaries work too - * - *  04-14-2004	tglx: first working version for 2k page size chips - * - *  05-19-2004  tglx: Basic support for Renesas AG-AND chips - * - *  09-24-2004  tglx: add support for hardware controllers (e.g. ECC) shared - *		among multiple independend devices. Suggestions and initial patch - *		from Ben Dooks <ben-mtd@fluff.org> - * - * Credits: + *  Credits:   *	David Woodhouse for adding multichip support   *   *	Aleph One Ltd. and Toby Churchill Ltd. for supporting the   *	rework for 2K page size chips   * - * TODO: + *  TODO:   *	Enable cached programming for 2k page size chips   *	Check, if mtd->ecctype should be set to MTD_ECC_HW   *	if we have HW ecc support.   *	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 $ - *   * This program is free software; you can redistribute it and/or modify   * it under the terms of the GNU General Public License version 2 as   * published by the Free Software Foundation. @@ -51,8 +33,10 @@  /* XXX U-BOOT XXX */  #if 0 +#include <linux/module.h>  #include <linux/delay.h>  #include <linux/errno.h> +#include <linux/err.h>  #include <linux/sched.h>  #include <linux/slab.h>  #include <linux/types.h> @@ -62,6 +46,7 @@  #include <linux/mtd/compatmac.h>  #include <linux/interrupt.h>  #include <linux/bitops.h> +#include <linux/leds.h>  #include <asm/io.h>  #ifdef CONFIG_MTD_PARTITIONS @@ -72,10 +57,13 @@  #include <common.h> +#define ENOTSUPP	524	/* Operation is not supported */ +  #if defined(CONFIG_CMD_NAND) && !defined(CFG_NAND_LEGACY)  #include <malloc.h>  #include <watchdog.h> +#include <linux/err.h>  #include <linux/mtd/compat.h>  #include <linux/mtd/mtd.h>  #include <linux/mtd/nand.h> @@ -89,83 +77,67 @@  #endif  /* Define default oob placement schemes for large and small page devices */ -static struct nand_oobinfo nand_oob_8 = { -	.useecc = MTD_NANDECC_AUTOPLACE, +static struct nand_ecclayout nand_oob_8 = {  	.eccbytes = 3,  	.eccpos = {0, 1, 2}, -	.oobfree = { {3, 2}, {6, 2} } +	.oobfree = { +		{.offset = 3, +		 .length = 2}, +		{.offset = 6, +		 .length = 2}}  }; -static struct nand_oobinfo nand_oob_16 = { -	.useecc = MTD_NANDECC_AUTOPLACE, +static struct nand_ecclayout nand_oob_16 = {  	.eccbytes = 6,  	.eccpos = {0, 1, 2, 3, 6, 7}, -	.oobfree = { {8, 8} } +	.oobfree = { +		{.offset = 8, +		 . length = 8}}  }; -static struct nand_oobinfo nand_oob_64 = { -	.useecc = MTD_NANDECC_AUTOPLACE, +static struct nand_ecclayout nand_oob_64 = {  	.eccbytes = 24,  	.eccpos = { -		40, 41, 42, 43, 44, 45, 46, 47, -		48, 49, 50, 51, 52, 53, 54, 55, -		56, 57, 58, 59, 60, 61, 62, 63}, -	.oobfree = { {2, 38} } +		   40, 41, 42, 43, 44, 45, 46, 47, +		   48, 49, 50, 51, 52, 53, 54, 55, +		   56, 57, 58, 59, 60, 61, 62, 63}, +	.oobfree = { +		{.offset = 2, +		 .length = 38}}  }; -static struct nand_oobinfo nand_oob_128 = { -	.useecc = MTD_NANDECC_AUTOPLACE, +static struct nand_ecclayout nand_oob_128 = {  	.eccbytes = 48,  	.eccpos = { -		80,  81,  82,  83,  84,  85,  86,  87, -		88,  89,  90,  91,  92,  93,  94,  95, -		96,  97,  98,  99, 100, 101, 102, 103, -		104, 105, 106, 107, 108, 109, 110, 111, -		112, 113, 114, 115, 116, 117, 118, 119, -		120, 121, 122, 123, 124, 125, 126, 127}, -	.oobfree = { {2, 78} } +		    80,  81,  82,  83,  84,  85,  86,  87, +		    88,  89,  90,  91,  92,  93,  94,  95, +		    96,  97,  98,  99, 100, 101, 102, 103, +		   104, 105, 106, 107, 108, 109, 110, 111, +		   112, 113, 114, 115, 116, 117, 118, 119, +		   120, 121, 122, 123, 124, 125, 126, 127}, +	.oobfree = { +		{.offset = 2, +		 .length = 78}}  }; -/* This is used for padding purposes in nand_write_oob */ -static u_char *ffchars; + +static int nand_get_device(struct nand_chip *chip, struct mtd_info *mtd, +			   int new_state); + +static int nand_do_write_oob(struct mtd_info *mtd, loff_t to, +			     struct mtd_oob_ops *ops); + +static int nand_wait(struct mtd_info *mtd, struct nand_chip *this);  /* - * NAND low-level MTD interface functions + * For devices which display every fart in the system on a seperate LED. Is + * compiled away when LED support is disabled.   */ -static void nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len); -static void nand_read_buf(struct mtd_info *mtd, u_char *buf, int len); -static int nand_verify_buf(struct mtd_info *mtd, const u_char *buf, int len); - -static int nand_read (struct mtd_info *mtd, loff_t from, size_t len, size_t * retlen, u_char * buf); -static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len, -			  size_t * retlen, u_char * buf, u_char * eccbuf, struct nand_oobinfo *oobsel); -static int nand_read_oob (struct mtd_info *mtd, loff_t from, size_t len, size_t * retlen, u_char * buf); -static int nand_write (struct mtd_info *mtd, loff_t to, size_t len, size_t * retlen, const u_char * buf); -static int nand_write_ecc (struct mtd_info *mtd, loff_t to, size_t len, -			   size_t * retlen, const u_char * buf, u_char * eccbuf, struct nand_oobinfo *oobsel); -static int nand_write_oob (struct mtd_info *mtd, loff_t to, size_t len, size_t * retlen, const u_char *buf);  /* XXX U-BOOT XXX */  #if 0 -static int nand_writev (struct mtd_info *mtd, const struct kvec *vecs, -			unsigned long count, loff_t to, size_t * retlen); -static int nand_writev_ecc (struct mtd_info *mtd, const struct kvec *vecs, -			unsigned long count, loff_t to, size_t * retlen, u_char *eccbuf, struct nand_oobinfo *oobsel); -#endif -static int nand_erase (struct mtd_info *mtd, struct erase_info *instr); -static void nand_sync (struct mtd_info *mtd); - -/* Some internal functions */ -static int nand_write_page (struct mtd_info *mtd, struct nand_chip *this, int page, u_char *oob_buf, -		struct nand_oobinfo *oobsel, int mode); -#ifdef CONFIG_MTD_NAND_VERIFY_WRITE -static int nand_verify_pages (struct mtd_info *mtd, struct nand_chip *this, int page, int numpages, -	u_char *oob_buf, struct nand_oobinfo *oobsel, int chipnr, int oobmode); -#else -#define nand_verify_pages(...) (0) +DEFINE_LED_TRIGGER(nand_led_trigger);  #endif -static void nand_get_device (struct nand_chip *this, struct mtd_info *mtd, int new_state); -  /**   * nand_release_device - [GENERIC] release chip   * @mtd:	MTD device structure @@ -174,33 +146,25 @@ static void nand_get_device (struct nand_chip *this, struct mtd_info *mtd, int n   */  /* XXX U-BOOT XXX */  #if 0 -static void nand_release_device (struct mtd_info *mtd) +static void nand_release_device(struct mtd_info *mtd)  { -	struct nand_chip *this = mtd->priv; +	struct nand_chip *chip = mtd->priv;  	/* De-select the NAND device */ -	this->select_chip(mtd, -1); -	/* Do we have a hardware controller ? */ -	if (this->controller) { -		spin_lock(&this->controller->lock); -		this->controller->active = NULL; -		spin_unlock(&this->controller->lock); -	} -	/* Release the chip */ -	spin_lock (&this->chip_lock); -	this->state = FL_READY; -	wake_up (&this->wq); -	spin_unlock (&this->chip_lock); +	chip->select_chip(mtd, -1); + +	/* Release the controller and the chip */ +	spin_lock(&chip->controller->lock); +	chip->controller->active = NULL; +	chip->state = FL_READY; +	wake_up(&chip->controller->wq); +	spin_unlock(&chip->controller->lock);  }  #else  static void nand_release_device (struct mtd_info *mtd)  {  	struct nand_chip *this = mtd->priv;  	this->select_chip(mtd, -1);	/* De-select the NAND device */ -	if (ffchars) { -		kfree(ffchars); -		ffchars = NULL; -	}  }  #endif @@ -210,23 +174,10 @@ static void nand_release_device (struct mtd_info *mtd)   *   * Default read function for 8bit buswith   */ -static u_char nand_read_byte(struct mtd_info *mtd) -{ -	struct nand_chip *this = mtd->priv; -	return readb(this->IO_ADDR_R); -} - -/** - * nand_write_byte - [DEFAULT] write one byte to the chip - * @mtd:	MTD device structure - * @byte:	pointer to data byte to write - * - * Default write function for 8it buswith - */ -static void nand_write_byte(struct mtd_info *mtd, u_char byte) +static uint8_t nand_read_byte(struct mtd_info *mtd)  { -	struct nand_chip *this = mtd->priv; -	writeb(byte, this->IO_ADDR_W); +	struct nand_chip *chip = mtd->priv; +	return readb(chip->IO_ADDR_R);  }  /** @@ -236,24 +187,10 @@ static void nand_write_byte(struct mtd_info *mtd, u_char byte)   * Default read function for 16bit buswith with   * endianess conversion   */ -static u_char nand_read_byte16(struct mtd_info *mtd) -{ -	struct nand_chip *this = mtd->priv; -	return (u_char) cpu_to_le16(readw(this->IO_ADDR_R)); -} - -/** - * nand_write_byte16 - [DEFAULT] write one byte endianess aware to the chip - * @mtd:	MTD device structure - * @byte:	pointer to data byte to write - * - * Default write function for 16bit buswith with - * endianess conversion - */ -static void nand_write_byte16(struct mtd_info *mtd, u_char byte) +static uint8_t nand_read_byte16(struct mtd_info *mtd)  { -	struct nand_chip *this = mtd->priv; -	writew(le16_to_cpu((u16) byte), this->IO_ADDR_W); +	struct nand_chip *chip = mtd->priv; +	return (uint8_t) cpu_to_le16(readw(chip->IO_ADDR_R));  }  /** @@ -265,40 +202,26 @@ static void nand_write_byte16(struct mtd_info *mtd, u_char byte)   */  static u16 nand_read_word(struct mtd_info *mtd)  { -	struct nand_chip *this = mtd->priv; -	return readw(this->IO_ADDR_R); -} - -/** - * nand_write_word - [DEFAULT] write one word to the chip - * @mtd:	MTD device structure - * @word:	data word to write - * - * Default write function for 16bit buswith without - * endianess conversion - */ -static void nand_write_word(struct mtd_info *mtd, u16 word) -{ -	struct nand_chip *this = mtd->priv; -	writew(word, this->IO_ADDR_W); +	struct nand_chip *chip = mtd->priv; +	return readw(chip->IO_ADDR_R);  }  /**   * nand_select_chip - [DEFAULT] control CE line   * @mtd:	MTD device structure - * @chip:	chipnumber to select, -1 for deselect + * @chipnr:	chipnumber to select, -1 for deselect   *   * Default select function for 1 chip devices.   */ -static void nand_select_chip(struct mtd_info *mtd, int chip) +static void nand_select_chip(struct mtd_info *mtd, int chipnr)  { -	struct nand_chip *this = mtd->priv; -	switch(chip) { +	struct nand_chip *chip = mtd->priv; + +	switch (chipnr) {  	case -1: -		this->hwcontrol(mtd, NAND_CTL_CLRNCE); +		chip->cmd_ctrl(mtd, NAND_CMD_NONE, 0 | NAND_CTRL_CHANGE);  		break;  	case 0: -		this->hwcontrol(mtd, NAND_CTL_SETNCE);  		break;  	default: @@ -314,13 +237,13 @@ static void nand_select_chip(struct mtd_info *mtd, int chip)   *   * Default write function for 8bit buswith   */ -static void nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len) +static void nand_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)  {  	int i; -	struct nand_chip *this = mtd->priv; +	struct nand_chip *chip = mtd->priv; -	for (i=0; i<len; i++) -		writeb(buf[i], this->IO_ADDR_W); +	for (i = 0; i < len; i++) +		writeb(buf[i], chip->IO_ADDR_W);  }  /** @@ -331,13 +254,13 @@ static void nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len)   *   * Default read function for 8bit buswith   */ -static void nand_read_buf(struct mtd_info *mtd, u_char *buf, int len) +static void nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)  {  	int i; -	struct nand_chip *this = mtd->priv; +	struct nand_chip *chip = mtd->priv; -	for (i=0; i<len; i++) -		buf[i] = readb(this->IO_ADDR_R); +	for (i = 0; i < len; i++) +		buf[i] = readb(chip->IO_ADDR_R);  }  /** @@ -348,15 +271,14 @@ static void nand_read_buf(struct mtd_info *mtd, u_char *buf, int len)   *   * Default verify function for 8bit buswith   */ -static int nand_verify_buf(struct mtd_info *mtd, const u_char *buf, int len) +static int nand_verify_buf(struct mtd_info *mtd, const uint8_t *buf, int len)  {  	int i; -	struct nand_chip *this = mtd->priv; +	struct nand_chip *chip = mtd->priv; -	for (i=0; i<len; i++) -		if (buf[i] != readb(this->IO_ADDR_R)) +	for (i = 0; i < len; i++) +		if (buf[i] != readb(chip->IO_ADDR_R))  			return -EFAULT; -  	return 0;  } @@ -368,15 +290,15 @@ static int nand_verify_buf(struct mtd_info *mtd, const u_char *buf, int len)   *   * Default write function for 16bit buswith   */ -static void nand_write_buf16(struct mtd_info *mtd, const u_char *buf, int len) +static void nand_write_buf16(struct mtd_info *mtd, const uint8_t *buf, int len)  {  	int i; -	struct nand_chip *this = mtd->priv; +	struct nand_chip *chip = mtd->priv;  	u16 *p = (u16 *) buf;  	len >>= 1; -	for (i=0; i<len; i++) -		writew(p[i], this->IO_ADDR_W); +	for (i = 0; i < len; i++) +		writew(p[i], chip->IO_ADDR_W);  } @@ -388,15 +310,15 @@ static void nand_write_buf16(struct mtd_info *mtd, const u_char *buf, int len)   *   * Default read function for 16bit buswith   */ -static void nand_read_buf16(struct mtd_info *mtd, u_char *buf, int len) +static void nand_read_buf16(struct mtd_info *mtd, uint8_t *buf, int len)  {  	int i; -	struct nand_chip *this = mtd->priv; +	struct nand_chip *chip = mtd->priv;  	u16 *p = (u16 *) buf;  	len >>= 1; -	for (i=0; i<len; i++) -		p[i] = readw(this->IO_ADDR_R); +	for (i = 0; i < len; i++) +		p[i] = readw(chip->IO_ADDR_R);  }  /** @@ -407,15 +329,15 @@ static void nand_read_buf16(struct mtd_info *mtd, u_char *buf, int len)   *   * Default verify function for 16bit buswith   */ -static int nand_verify_buf16(struct mtd_info *mtd, const u_char *buf, int len) +static int nand_verify_buf16(struct mtd_info *mtd, const uint8_t *buf, int len)  {  	int i; -	struct nand_chip *this = mtd->priv; +	struct nand_chip *chip = mtd->priv;  	u16 *p = (u16 *) buf;  	len >>= 1; -	for (i=0; i<len; i++) -		if (p[i] != readw(this->IO_ADDR_R)) +	for (i = 0; i < len; i++) +		if (p[i] != readw(chip->IO_ADDR_R))  			return -EFAULT;  	return 0; @@ -432,38 +354,36 @@ static int nand_verify_buf16(struct mtd_info *mtd, const u_char *buf, int len)  static int nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip)  {  	int page, chipnr, res = 0; -	struct nand_chip *this = mtd->priv; +	struct nand_chip *chip = mtd->priv;  	u16 bad; -	page = (int)(ofs >> this->page_shift) & this->pagemask; +	page = (int)(ofs >> chip->page_shift) & chip->pagemask;  	if (getchip) { -		chipnr = (int)(ofs >> this->chip_shift); +		chipnr = (int)(ofs >> chip->chip_shift); -		/* Grab the lock and see if the device is available */ -		nand_get_device (this, mtd, FL_READING); +		nand_get_device(chip, mtd, FL_READING);  		/* Select the NAND device */ -		this->select_chip(mtd, chipnr); +		chip->select_chip(mtd, chipnr);  	} -	if (this->options & NAND_BUSWIDTH_16) { -		this->cmdfunc (mtd, NAND_CMD_READOOB, this->badblockpos & 0xFE, page); -		bad = cpu_to_le16(this->read_word(mtd)); -		if (this->badblockpos & 0x1) -			bad >>= 1; +	if (chip->options & NAND_BUSWIDTH_16) { +		chip->cmdfunc(mtd, NAND_CMD_READOOB, chip->badblockpos & 0xFE, +			      page); +		bad = cpu_to_le16(chip->read_word(mtd)); +		if (chip->badblockpos & 0x1) +			bad >>= 8;  		if ((bad & 0xFF) != 0xff)  			res = 1;  	} else { -		this->cmdfunc (mtd, NAND_CMD_READOOB, this->badblockpos, page); -		if (this->read_byte(mtd) != 0xff) +		chip->cmdfunc(mtd, NAND_CMD_READOOB, chip->badblockpos, page); +		if (chip->read_byte(mtd) != 0xff)  			res = 1;  	} -	if (getchip) { -		/* Deselect and wake up anyone waiting on the device */ +	if (getchip)  		nand_release_device(mtd); -	}  	return res;  } @@ -478,22 +398,33 @@ static int nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip)  */  static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs)  { -	struct nand_chip *this = mtd->priv; -	u_char buf[2] = {0, 0}; -	size_t	retlen; -	int block; +	struct nand_chip *chip = mtd->priv; +	uint8_t buf[2] = { 0, 0 }; +	int block, ret;  	/* Get block number */ -	block = ((int) ofs) >> this->bbt_erase_shift; -	this->bbt[block >> 2] |= 0x01 << ((block & 0x03) << 1); +	block = (int)(ofs >> chip->bbt_erase_shift); +	if (chip->bbt) +		chip->bbt[block >> 2] |= 0x01 << ((block & 0x03) << 1);  	/* Do we have a flash based bad block table ? */ -	if (this->options & NAND_USE_FLASH_BBT) -		return nand_update_bbt (mtd, ofs); +	if (chip->options & NAND_USE_FLASH_BBT) +		ret = nand_update_bbt(mtd, ofs); +	else { +		/* We write two bytes, so we dont have to mess with 16 bit +		 * access +		 */ +		ofs += mtd->oobsize; +		chip->ops.len = chip->ops.ooblen = 2; +		chip->ops.datbuf = NULL; +		chip->ops.oobbuf = buf; +		chip->ops.ooboffs = chip->badblockpos & ~0x01; -	/* We write two bytes, so we dont have to mess with 16 bit access */ -	ofs += mtd->oobsize + (this->badblockpos & ~0x01); -	return nand_write_oob (mtd, ofs , 2, &retlen, buf); +		ret = nand_do_write_oob(mtd, ofs, &chip->ops); +	} +	if (!ret) +		mtd->ecc_stats.badblocks++; +	return ret;  }  /** @@ -503,12 +434,12 @@ static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs)   *   * The function expects, that the device is already selected   */ -static int nand_check_wp (struct mtd_info *mtd) +static int nand_check_wp(struct mtd_info *mtd)  { -	struct nand_chip *this = mtd->priv; +	struct nand_chip *chip = mtd->priv;  	/* Check the WP bit */ -	this->cmdfunc (mtd, NAND_CMD_STATUS, -1, -1); -	return (this->read_byte(mtd) & 0x80) ? 0 : 1; +	chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1); +	return (chip->read_byte(mtd) & NAND_STATUS_WP) ? 0 : 1;  }  /** @@ -521,17 +452,61 @@ static int nand_check_wp (struct mtd_info *mtd)   * Check, if the block is bad. Either by reading the bad block table or   * calling of the scan function.   */ -static int nand_block_checkbad (struct mtd_info *mtd, loff_t ofs, int getchip, int allowbbt) +static int nand_block_checkbad(struct mtd_info *mtd, loff_t ofs, int getchip, +			       int allowbbt)  { -	struct nand_chip *this = mtd->priv; +	struct nand_chip *chip = mtd->priv; -	if (!this->bbt) -		return this->block_bad(mtd, ofs, getchip); +	if (!(chip->options & NAND_BBT_SCANNED)) { +		chip->scan_bbt(mtd); +		chip->options |= NAND_BBT_SCANNED; +	} + +	if (!chip->bbt) +		return chip->block_bad(mtd, ofs, getchip);  	/* Return info from the table */ -	return nand_isbad_bbt (mtd, ofs, allowbbt); +	return nand_isbad_bbt(mtd, ofs, allowbbt);  } +/* + * Wait for the ready pin, after a command + * The timeout is catched later. + */ +/* XXX U-BOOT XXX */ +#if 0 +void nand_wait_ready(struct mtd_info *mtd) +{ +	struct nand_chip *chip = mtd->priv; +	unsigned long timeo = jiffies + 2; + +	led_trigger_event(nand_led_trigger, LED_FULL); +	/* wait until command is processed or timeout occures */ +	do { +		if (chip->dev_ready(mtd)) +			break; +		touch_softlockup_watchdog(); +	} while (time_before(jiffies, timeo)); +	led_trigger_event(nand_led_trigger, LED_OFF); +} +EXPORT_SYMBOL_GPL(nand_wait_ready); +#else +void nand_wait_ready(struct mtd_info *mtd) +{ +	struct nand_chip *chip = mtd->priv; +	u32 timeo = (CFG_HZ * 20) / 1000; + +	reset_timer(); + +	/* wait until command is processed or timeout occures */ +	while (get_timer(0) < timeo) { +		if (chip->dev_ready) +			if (chip->dev_ready(mtd)) +				break; +	} +} +#endif +  /**   * nand_command - [DEFAULT] Send command to NAND device   * @mtd:	MTD device structure @@ -542,21 +517,21 @@ static int nand_block_checkbad (struct mtd_info *mtd, loff_t ofs, int getchip, i   * Send command to NAND device. This function is used for small page   * devices (256/512 Bytes per page)   */ -static void nand_command (struct mtd_info *mtd, unsigned command, int column, int page_addr) +static void nand_command(struct mtd_info *mtd, unsigned int command, +			 int column, int page_addr)  { -	register struct nand_chip *this = mtd->priv; +	register struct nand_chip *chip = mtd->priv; +	int ctrl = NAND_CTRL_CLE | NAND_CTRL_CHANGE; -	/* 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) { +		if (column >= mtd->writesize) {  			/* OOB area */ -			column -= mtd->oobblock; +			column -= mtd->writesize;  			readcmd = NAND_CMD_READOOB;  		} else if (column < 256) {  			/* First 256 bytes --> READ0 */ @@ -565,38 +540,37 @@ static void nand_command (struct mtd_info *mtd, unsigned command, int column, in  			column -= 256;  			readcmd = NAND_CMD_READ1;  		} -		this->write_byte(mtd, readcmd); +		chip->cmd_ctrl(mtd, readcmd, ctrl); +		ctrl &= ~NAND_CTRL_CHANGE;  	} -	this->write_byte(mtd, command); - -	/* Set ALE and clear CLE to start address cycle */ -	this->hwcontrol(mtd, NAND_CTL_CLRCLE); +	chip->cmd_ctrl(mtd, command, ctrl); -	if (column != -1 || page_addr != -1) { -		this->hwcontrol(mtd, NAND_CTL_SETALE); - -		/* Serially input address */ -		if (column != -1) { -			/* Adjust columns for 16 bit buswidth */ -			if (this->options & NAND_BUSWIDTH_16) -				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 devices > 32MiB */ -			if (this->chipsize > (32 << 20)) -				this->write_byte(mtd, (unsigned char) ((page_addr >> 16) & 0x0f)); -		} -		/* Latch in address */ -		this->hwcontrol(mtd, NAND_CTL_CLRALE); +	/* +	 * Address cycle, when necessary +	 */ +	ctrl = NAND_CTRL_ALE | NAND_CTRL_CHANGE; +	/* Serially input address */ +	if (column != -1) { +		/* Adjust columns for 16 bit buswidth */ +		if (chip->options & NAND_BUSWIDTH_16) +			column >>= 1; +		chip->cmd_ctrl(mtd, column, ctrl); +		ctrl &= ~NAND_CTRL_CHANGE; +	} +	if (page_addr != -1) { +		chip->cmd_ctrl(mtd, page_addr, ctrl); +		ctrl &= ~NAND_CTRL_CHANGE; +		chip->cmd_ctrl(mtd, page_addr >> 8, ctrl); +		/* One more address cycle for devices > 32MiB */ +		if (chip->chipsize > (32 << 20)) +			chip->cmd_ctrl(mtd, page_addr >> 16, ctrl);  	} +	chip->cmd_ctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE);  	/*  	 * program and erase have their own busy handlers  	 * status and sequential in needs no delay -	*/ +	 */  	switch (command) {  	case NAND_CMD_PAGEPROG: @@ -607,32 +581,32 @@ static void nand_command (struct mtd_info *mtd, unsigned command, int column, in  		return;  	case NAND_CMD_RESET: -		if (this->dev_ready) +		if (chip->dev_ready)  			break; -		udelay(this->chip_delay); -		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)); +		udelay(chip->chip_delay); +		chip->cmd_ctrl(mtd, NAND_CMD_STATUS, +			       NAND_CTRL_CLE | NAND_CTRL_CHANGE); +		chip->cmd_ctrl(mtd, +			       NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); +		while (!(chip->read_byte(mtd) & NAND_STATUS_READY)) ;  		return; -	/* This applies to read commands */ +		/* 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); +		 */ +		if (!chip->dev_ready) { +			udelay(chip->chip_delay);  			return;  		}  	} -  	/* Apply this short delay always to ensure that we do wait tWB in  	 * any case on any machine. */ -	ndelay (100); -	/* wait until command is processed */ -	while (!this->dev_ready(mtd)); +	ndelay(100); + +	nand_wait_ready(mtd);  }  /** @@ -642,55 +616,53 @@ static void nand_command (struct mtd_info *mtd, unsigned command, int column, in   * @column:	the column address for this command, -1 if none   * @page_addr:	the page address for this command, -1 if none   * - * Send command to NAND device. This is the version for the new large page devices - * We dont have the seperate regions as we have in the small page devices. - * We must emulate NAND_CMD_READOOB to keep the code compatible. - * + * Send command to NAND device. This is the version for the new large page + * devices We dont have the separate regions as we have in the small page + * devices.  We must emulate NAND_CMD_READOOB to keep the code compatible.   */ -static void nand_command_lp (struct mtd_info *mtd, unsigned command, int column, int page_addr) +static void nand_command_lp(struct mtd_info *mtd, unsigned int command, +			    int column, int page_addr)  { -	register struct nand_chip *this = mtd->priv; +	register struct nand_chip *chip = mtd->priv;  	/* Emulate NAND_CMD_READOOB */  	if (command == NAND_CMD_READOOB) { -		column += mtd->oobblock; +		column += mtd->writesize;  		command = NAND_CMD_READ0;  	} - -	/* Begin command latch cycle */ -	this->hwcontrol(mtd, NAND_CTL_SETCLE); -	/* Write out the command to the device. */ -	this->write_byte(mtd, command); -	/* End command latch cycle */ -	this->hwcontrol(mtd, NAND_CTL_CLRCLE); +	/* Command latch cycle */ +	chip->cmd_ctrl(mtd, command & 0xff, +		       NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE);  	if (column != -1 || page_addr != -1) { -		this->hwcontrol(mtd, NAND_CTL_SETALE); +		int ctrl = NAND_CTRL_CHANGE | NAND_NCE | NAND_ALE;  		/* Serially input address */  		if (column != -1) {  			/* Adjust columns for 16 bit buswidth */ -			if (this->options & NAND_BUSWIDTH_16) +			if (chip->options & NAND_BUSWIDTH_16)  				column >>= 1; -			this->write_byte(mtd, column & 0xff); -			this->write_byte(mtd, column >> 8); +			chip->cmd_ctrl(mtd, column, ctrl); +			ctrl &= ~NAND_CTRL_CHANGE; +			chip->cmd_ctrl(mtd, column >> 8, ctrl);  		}  		if (page_addr != -1) { -			this->write_byte(mtd, (unsigned char) (page_addr & 0xff)); -			this->write_byte(mtd, (unsigned char) ((page_addr >> 8) & 0xff)); +			chip->cmd_ctrl(mtd, page_addr, ctrl); +			chip->cmd_ctrl(mtd, page_addr >> 8, +				       NAND_NCE | NAND_ALE);  			/* One more address cycle for devices > 128MiB */ -			if (this->chipsize > (128 << 20)) -				this->write_byte(mtd, (unsigned char) ((page_addr >> 16) & 0xff)); +			if (chip->chipsize > (128 << 20)) +				chip->cmd_ctrl(mtd, page_addr >> 16, +					       NAND_NCE | NAND_ALE);  		} -		/* Latch in address */ -		this->hwcontrol(mtd, NAND_CTL_CLRALE);  	} +	chip->cmd_ctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE);  	/*  	 * 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: @@ -698,51 +670,69 @@ static void nand_command_lp (struct mtd_info *mtd, unsigned command, int column,  	case NAND_CMD_ERASE1:  	case NAND_CMD_ERASE2:  	case NAND_CMD_SEQIN: +	case NAND_CMD_RNDIN:  	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(chip->chip_delay); +		return;  	case NAND_CMD_RESET: -		if (this->dev_ready) +		if (chip->dev_ready)  			break; -		udelay(this->chip_delay); -		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)); +		udelay(chip->chip_delay); +		chip->cmd_ctrl(mtd, NAND_CMD_STATUS, +			       NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE); +		chip->cmd_ctrl(mtd, NAND_CMD_NONE, +			       NAND_NCE | NAND_CTRL_CHANGE); +		while (!(chip->read_byte(mtd) & NAND_STATUS_READY)) ; +		return; + +	case NAND_CMD_RNDOUT: +		/* No ready / busy check necessary */ +		chip->cmd_ctrl(mtd, NAND_CMD_RNDOUTSTART, +			       NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE); +		chip->cmd_ctrl(mtd, NAND_CMD_NONE, +			       NAND_NCE | NAND_CTRL_CHANGE);  		return;  	case NAND_CMD_READ0: -		/* Begin command latch cycle */ -		this->hwcontrol(mtd, NAND_CTL_SETCLE); -		/* Write out the start read command */ -		this->write_byte(mtd, NAND_CMD_READSTART); -		/* End command latch cycle */ -		this->hwcontrol(mtd, NAND_CTL_CLRCLE); -		/* Fall through into ready check */ +		chip->cmd_ctrl(mtd, NAND_CMD_READSTART, +			       NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE); +		chip->cmd_ctrl(mtd, NAND_CMD_NONE, +			       NAND_NCE | NAND_CTRL_CHANGE); -	/* This applies to read commands */ +		/* 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); +		 */ +		if (!chip->dev_ready) { +			udelay(chip->chip_delay);  			return;  		}  	}  	/* Apply this short delay always to ensure that we do wait tWB in  	 * any case on any machine. */ -	ndelay (100); -	/* wait until command is processed */ -	while (!this->dev_ready(mtd)); +	ndelay(100); + +	nand_wait_ready(mtd);  }  /**   * nand_get_device - [GENERIC] Get chip for selected access - * @this:	the nand chip descriptor + * @chip:	the nand chip descriptor   * @mtd:	MTD device structure   * @new_state:	the state which is requested   * @@ -750,100 +740,97 @@ static void nand_command_lp (struct mtd_info *mtd, unsigned command, int column,   */  /* XXX U-BOOT XXX */  #if 0 -static void nand_get_device (struct nand_chip *this, struct mtd_info *mtd, int new_state) +static int +nand_get_device(struct nand_chip *chip, struct mtd_info *mtd, int new_state)  { -	struct nand_chip *active = this; - -	DECLARE_WAITQUEUE (wait, current); +	spinlock_t *lock = &chip->controller->lock; +	wait_queue_head_t *wq = &chip->controller->wq; +	DECLARE_WAITQUEUE(wait, current); + retry: +	spin_lock(lock); -	/* -	 * Grab the lock and see if the device is available -	*/ -retry:  	/* 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); -	} +	/* Hardware controller shared among independend devices */ +	if (!chip->controller->active) +		chip->controller->active = chip; -	if (active == this) { -		spin_lock (&this->chip_lock); -		if (this->state == FL_READY) { -			this->state = new_state; -			spin_unlock (&this->chip_lock); -			return; -		} +	if (chip->controller->active == chip && chip->state == FL_READY) { +		chip->state = new_state; +		spin_unlock(lock); +		return 0; +	} +	if (new_state == FL_PM_SUSPENDED) { +		spin_unlock(lock); +		return (chip->state == FL_PM_SUSPENDED) ? 0 : -EAGAIN;  	} -	set_current_state (TASK_UNINTERRUPTIBLE); -	add_wait_queue (&active->wq, &wait); -	spin_unlock (&active->chip_lock); -	schedule (); -	remove_wait_queue (&active->wq, &wait); +	set_current_state(TASK_UNINTERRUPTIBLE); +	add_wait_queue(wq, &wait); +	spin_unlock(lock); +	schedule(); +	remove_wait_queue(wq, &wait);  	goto retry;  }  #else -static void nand_get_device (struct nand_chip *this, struct mtd_info *mtd, int new_state) {} +static int nand_get_device (struct nand_chip *this, struct mtd_info *mtd, int new_state) +{ +	this->state = new_state; +	return 0; +}  #endif  /**   * nand_wait - [DEFAULT]  wait until the command is done   * @mtd:	MTD device structure - * @this:	NAND chip structure - * @state:	state to select the max. timeout value + * @chip:	NAND chip structure   *   * Wait for command done. This applies to erase and program only   * Erase can take up to 400ms and program up to 20ms according to   * general NAND and SmartMedia specs - * -*/ + */  /* XXX U-BOOT XXX */  #if 0 -static int nand_wait(struct mtd_info *mtd, struct nand_chip *this, int state) +static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip)  { -	unsigned long	timeo = jiffies; -	int	status; + +	unsigned long timeo = jiffies; +	int status, state = chip->state;  	if (state == FL_ERASING) -		 timeo += (HZ * 400) / 1000; +		timeo += (HZ * 400) / 1000;  	else -		 timeo += (HZ * 20) / 1000; +		timeo += (HZ * 20) / 1000; + +	led_trigger_event(nand_led_trigger, LED_FULL);  	/* Apply this short delay always to ensure that we do wait tWB in  	 * any case on any machine. */ -	ndelay (100); +	ndelay(100); -	if ((state == FL_ERASING) && (this->options & NAND_IS_AND)) -		this->cmdfunc (mtd, NAND_CMD_STATUS_MULTI, -1, -1); +	if ((state == FL_ERASING) && (chip->options & NAND_IS_AND)) +		chip->cmdfunc(mtd, NAND_CMD_STATUS_MULTI, -1, -1);  	else -		this->cmdfunc (mtd, NAND_CMD_STATUS, -1, -1); +		chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1);  	while (time_before(jiffies, timeo)) { -		/* Check, if we were interrupted */ -		if (this->state != state) -			return 0; - -		if (this->dev_ready) { -			if (this->dev_ready(mtd)) +		if (chip->dev_ready) { +			if (chip->dev_ready(mtd))  				break;  		} else { -			if (this->read_byte(mtd) & NAND_STATUS_READY) +			if (chip->read_byte(mtd) & NAND_STATUS_READY)  				break;  		} -		yield (); +		cond_resched();  	} -	status = (int) this->read_byte(mtd); -	return status; +	led_trigger_event(nand_led_trigger, LED_OFF); -	return 0; +	status = (int)chip->read_byte(mtd); +	return status;  }  #else -static int nand_wait(struct mtd_info *mtd, struct nand_chip *this, int state) +static int nand_wait(struct mtd_info *mtd, struct nand_chip *this)  {  	unsigned long	timeo; +	int state = this->state;  	if (state == FL_ERASING)  		timeo = (CFG_HZ * 400) / 1000; @@ -881,478 +868,305 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *this, int state)  #endif  /** - * nand_write_page - [GENERIC] write one page - * @mtd:	MTD device structure - * @this:	NAND chip structure - * @page:	startpage inside the chip, must be called with (page & this->pagemask) - * @oob_buf:	out of band data buffer - * @oobsel:	out of band selecttion structre - * @cached:	1 = enable cached programming if supported by chip - * - * Nand_page_program function is used for write and writev ! - * This function will always program a full page of data - * If you call it with a non page aligned buffer, you're lost :) - * - * Cached programming is not supported yet. + * nand_read_page_raw - [Intern] read raw page data without ecc + * @mtd:	mtd info structure + * @chip:	nand chip info structure + * @buf:	buffer to store read data   */ -static int nand_write_page (struct mtd_info *mtd, struct nand_chip *this, int page, -	u_char *oob_buf,  struct nand_oobinfo *oobsel, int cached) +static int nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, +			      uint8_t *buf)  { -	int	i, status; -	u_char	ecc_code[NAND_MAX_OOBSIZE]; -	int	eccmode = oobsel->useecc ? this->eccmode : NAND_ECC_NONE; -	uint	*oob_config = oobsel->eccpos; -	int	datidx = 0, eccidx = 0, eccsteps = this->eccsteps; -	int	eccbytes = 0; +	chip->read_buf(mtd, buf, mtd->writesize); +	chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); +	return 0; +} -	/* FIXME: Enable cached programming */ -	cached = 0; +/** + * nand_read_page_swecc - [REPLACABLE] software ecc based page read function + * @mtd:	mtd info structure + * @chip:	nand chip info structure + * @buf:	buffer to store read data + */ +static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, +				uint8_t *buf) +{ +	int i, eccsize = chip->ecc.size; +	int eccbytes = chip->ecc.bytes; +	int eccsteps = chip->ecc.steps; +	uint8_t *p = buf; +	uint8_t *ecc_calc = chip->buffers->ecccalc; +	uint8_t *ecc_code = chip->buffers->ecccode; +	uint32_t *eccpos = chip->ecc.layout->eccpos; -	/* Send command to begin auto page programming */ -	this->cmdfunc (mtd, NAND_CMD_SEQIN, 0x00, page); +	chip->ecc.read_page_raw(mtd, chip, buf); -	/* Write out complete page of data, take care of eccmode */ -	switch (eccmode) { -	/* No ecc, write all */ -	case NAND_ECC_NONE: -		printk (KERN_WARNING "Writing data without ECC to NAND-FLASH is not recommended\n"); -		this->write_buf(mtd, this->data_poi, mtd->oobblock); -		break; +	for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) +		chip->ecc.calculate(mtd, p, &ecc_calc[i]); -	/* Software ecc 3/256, write all */ -	case NAND_ECC_SOFT: -		for (; eccsteps; eccsteps--) { -			this->calculate_ecc(mtd, &this->data_poi[datidx], ecc_code); -			for (i = 0; i < 3; i++, eccidx++) -				oob_buf[oob_config[eccidx]] = ecc_code[i]; -			datidx += this->eccsize; -		} -		this->write_buf(mtd, this->data_poi, mtd->oobblock); -		break; -	default: -		eccbytes = this->eccbytes; -		for (; eccsteps; eccsteps--) { -			/* enable hardware ecc logic for write */ -			this->enable_hwecc(mtd, NAND_ECC_WRITE); -			this->write_buf(mtd, &this->data_poi[datidx], this->eccsize); -			this->calculate_ecc(mtd, &this->data_poi[datidx], ecc_code); -			for (i = 0; i < eccbytes; i++, eccidx++) -				oob_buf[oob_config[eccidx]] = ecc_code[i]; -			/* If the hardware ecc provides syndromes then -			 * the ecc code must be written immediately after -			 * the data bytes (words) */ -			if (this->options & NAND_HWECC_SYNDROME) -				this->write_buf(mtd, ecc_code, eccbytes); -			datidx += this->eccsize; -		} -		break; -	} +	for (i = 0; i < chip->ecc.total; i++) +		ecc_code[i] = chip->oob_poi[eccpos[i]]; -	/* Write out OOB data */ -	if (this->options & NAND_HWECC_SYNDROME) -		this->write_buf(mtd, &oob_buf[oobsel->eccbytes], mtd->oobsize - oobsel->eccbytes); -	else -		this->write_buf(mtd, oob_buf, mtd->oobsize); +	eccsteps = chip->ecc.steps; +	p = buf; -	/* Send command to actually program the data */ -	this->cmdfunc (mtd, cached ? NAND_CMD_CACHEDPROG : NAND_CMD_PAGEPROG, -1, -1); +	for (i = 0 ; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { +		int stat; -	if (!cached) { -		/* call wait ready function */ -		status = this->waitfunc (mtd, this, FL_WRITING); -		/* See if device thinks it succeeded */ -		if (status & 0x01) { -			MTDDEBUG (MTD_DEBUG_LEVEL0, -			          "%s: Failed write, page 0x%08x, ", -			          __FUNCTION__, page); -			return -EIO; -		} -	} else { -		/* FIXME: Implement cached programming ! */ -		/* wait until cache is ready*/ -		/* status = this->waitfunc (mtd, this, FL_CACHEDRPG); */ +		stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]); +		if (stat == -1) +			mtd->ecc_stats.failed++; +		else +			mtd->ecc_stats.corrected += stat;  	}  	return 0;  } -#ifdef CONFIG_MTD_NAND_VERIFY_WRITE  /** - * nand_verify_pages - [GENERIC] verify the chip contents after a write - * @mtd:	MTD device structure - * @this:	NAND chip structure - * @page:	startpage inside the chip, must be called with (page & this->pagemask) - * @numpages:	number of pages to verify - * @oob_buf:	out of band data buffer - * @oobsel:	out of band selecttion structre - * @chipnr:	number of the current chip - * @oobmode:	1 = full buffer verify, 0 = ecc only + * nand_read_page_hwecc - [REPLACABLE] hardware ecc based page read function + * @mtd:	mtd info structure + * @chip:	nand chip info structure + * @buf:	buffer to store read data   * - * The NAND device assumes that it is always writing to a cleanly erased page. - * Hence, it performs its internal write verification only on bits that - * transitioned from 1 to 0. The device does NOT verify the whole page on a - * byte by byte basis. It is possible that the page was not completely erased - * or the page is becoming unusable due to wear. The read with ECC would catch - * the error later when the ECC page check fails, but we would rather catch - * it early in the page write stage. Better to write no data than invalid data. + * Not for syndrome calculating ecc controllers which need a special oob layout   */ -static int nand_verify_pages (struct mtd_info *mtd, struct nand_chip *this, int page, int numpages, -	u_char *oob_buf, struct nand_oobinfo *oobsel, int chipnr, int oobmode) +static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, +				uint8_t *buf)  { -	int	i, j, datidx = 0, oobofs = 0, res = -EIO; -	int	eccsteps = this->eccsteps; -	int	hweccbytes; -	u_char	oobdata[64]; - -	hweccbytes = (this->options & NAND_HWECC_SYNDROME) ? (oobsel->eccbytes / eccsteps) : 0; +	int i, eccsize = chip->ecc.size; +	int eccbytes = chip->ecc.bytes; +	int eccsteps = chip->ecc.steps; +	uint8_t *p = buf; +	uint8_t *ecc_calc = chip->buffers->ecccalc; +	uint8_t *ecc_code = chip->buffers->ecccode; +	uint32_t *eccpos = chip->ecc.layout->eccpos; -	/* Send command to read back the first page */ -	this->cmdfunc (mtd, NAND_CMD_READ0, 0, page); +	for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { +		chip->ecc.hwctl(mtd, NAND_ECC_READ); +		chip->read_buf(mtd, p, eccsize); +		chip->ecc.calculate(mtd, p, &ecc_calc[i]); +	} +	chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); -	for(;;) { -		for (j = 0; j < eccsteps; j++) { -			/* Loop through and verify the data */ -			if (this->verify_buf(mtd, &this->data_poi[datidx], mtd->eccsize)) { -				MTDDEBUG (MTD_DEBUG_LEVEL0, "%s: " -				          "Failed write verify, page 0x%08x ", -				          __FUNCTION__, page); -				goto out; -			} -			datidx += mtd->eccsize; -			/* Have we a hw generator layout ? */ -			if (!hweccbytes) -				continue; -			if (this->verify_buf(mtd, &this->oob_buf[oobofs], hweccbytes)) { -				MTDDEBUG (MTD_DEBUG_LEVEL0, "%s: " -				          "Failed write verify, page 0x%08x ", -				          __FUNCTION__, page); -				goto out; -			} -			oobofs += hweccbytes; -		} +	for (i = 0; i < chip->ecc.total; i++) +		ecc_code[i] = chip->oob_poi[eccpos[i]]; -		/* check, if we must compare all data or if we just have to -		 * compare the ecc bytes -		 */ -		if (oobmode) { -			if (this->verify_buf(mtd, &oob_buf[oobofs], mtd->oobsize - hweccbytes * eccsteps)) { -				MTDDEBUG (MTD_DEBUG_LEVEL0, "%s: " -				          "Failed write verify, page 0x%08x ", -				          __FUNCTION__, page); -				goto out; -			} -		} else { -			/* Read always, else autoincrement fails */ -			this->read_buf(mtd, oobdata, mtd->oobsize - hweccbytes * eccsteps); +	eccsteps = chip->ecc.steps; +	p = buf; -			if (oobsel->useecc != MTD_NANDECC_OFF && !hweccbytes) { -				int ecccnt = oobsel->eccbytes; +	for (i = 0 ; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { +		int stat; -				for (i = 0; i < ecccnt; i++) { -					int idx = oobsel->eccpos[i]; -					if (oobdata[idx] != oob_buf[oobofs + idx] ) { -						MTDDEBUG (MTD_DEBUG_LEVEL0, -						"%s: Failed ECC write " -						"verify, page 0x%08x, " -						"%6i bytes were succesful\n", -						__FUNCTION__, page, i); -						goto out; -					} -				} -			} -		} -		oobofs += mtd->oobsize - hweccbytes * eccsteps; -		page++; -		numpages--; - -		/* Apply delay or wait for ready/busy pin -		 * Do this before the AUTOINCR check, so no problems -		 * arise if a chip which does auto increment -		 * is marked as NOAUTOINCR by the board driver. -		 * Do this also before returning, so the chip is -		 * ready for the next command. -		*/ -		if (!this->dev_ready) -			udelay (this->chip_delay); +		stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]); +		if (stat == -1) +			mtd->ecc_stats.failed++;  		else -			while (!this->dev_ready(mtd)); - -		/* All done, return happy */ -		if (!numpages) -			return 0; - - -		/* Check, if the chip supports auto page increment */ -		if (!NAND_CANAUTOINCR(this)) -			this->cmdfunc (mtd, NAND_CMD_READ0, 0x00, page); +			mtd->ecc_stats.corrected += stat;  	} -	/* -	 * Terminate the read command. We come here in case of an error -	 * So we must issue a reset command. -	 */ -out: -	this->cmdfunc (mtd, NAND_CMD_RESET, -1, -1); -	return res; -} -#endif - -/** - * nand_read - [MTD Interface] MTD compability function for nand_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 -*/ -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 0;  } -  /** - * nand_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 - * @oobsel:	oob selection structure + * nand_read_page_syndrome - [REPLACABLE] hardware ecc syndrom based page read + * @mtd:	mtd info structure + * @chip:	nand chip info structure + * @buf:	buffer to store read data   * - * NAND read with ECC + * The hw generator calculates the error syndrome automatically. Therefor + * we need a special oob layout and handling.   */ -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) +static int nand_read_page_syndrome(struct mtd_info *mtd, struct nand_chip *chip, +				   uint8_t *buf)  { -	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; -	u_char *data_poi, *oob_data = oob_buf; -	u_char ecc_calc[NAND_MAX_OOBSIZE]; -	u_char ecc_code[NAND_MAX_OOBSIZE]; -	int eccmode, eccsteps; -	unsigned *oob_config; -	int	datidx; -	int	blockcheck = (1 << (this->phys_erase_shift - this->page_shift)) - 1; -	int	eccbytes; -	int	compareecc = 1; -	int	oobreadlen; +	int i, eccsize = chip->ecc.size; +	int eccbytes = chip->ecc.bytes; +	int eccsteps = chip->ecc.steps; +	uint8_t *p = buf; +	uint8_t *oob = chip->oob_poi; +	for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { +		int stat; -	MTDDEBUG (MTD_DEBUG_LEVEL3, "nand_read_ecc: from = 0x%08x, len = %i\n", -	          (unsigned int) from, (int) len); +		chip->ecc.hwctl(mtd, NAND_ECC_READ); +		chip->read_buf(mtd, p, eccsize); -	/* Do not allow reads past end of device */ -	if ((from + len) > mtd->size) { -		MTDDEBUG (MTD_DEBUG_LEVEL0, -		          "nand_read_ecc: Attempt read beyond end of device\n"); -		*retlen = 0; -		return -EINVAL; -	} - -	/* Grab the lock and see if the device is available */ -	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; +		if (chip->ecc.prepad) { +			chip->read_buf(mtd, oob, chip->ecc.prepad); +			oob += chip->ecc.prepad; +		} -	eccmode = oobsel->useecc ? this->eccmode : NAND_ECC_NONE; -	oob_config = oobsel->eccpos; +		chip->ecc.hwctl(mtd, NAND_ECC_READSYN); +		chip->read_buf(mtd, oob, eccbytes); +		stat = chip->ecc.correct(mtd, p, oob, NULL); -	/* Select the NAND device */ -	chipnr = (int)(from >> this->chip_shift); -	this->select_chip(mtd, chipnr); +		if (stat == -1) +			mtd->ecc_stats.failed++; +		else +			mtd->ecc_stats.corrected += stat; -	/* First we calculate the starting page */ -	realpage = (int) (from >> this->page_shift); -	page = realpage & this->pagemask; +		oob += eccbytes; -	/* Get raw starting column */ -	col = from & (mtd->oobblock - 1); +		if (chip->ecc.postpad) { +			chip->read_buf(mtd, oob, chip->ecc.postpad); +			oob += chip->ecc.postpad; +		} +	} -	end = mtd->oobblock; -	ecc = this->eccsize; -	eccbytes = this->eccbytes; +	/* Calculate remaining oob bytes */ +	i = mtd->oobsize - (oob - chip->oob_poi); +	if (i) +		chip->read_buf(mtd, oob, i); -	if ((eccmode == NAND_ECC_NONE) || (this->options & NAND_HWECC_SYNDROME)) -		compareecc = 0; +	return 0; +} -	oobreadlen = mtd->oobsize; -	if (this->options & NAND_HWECC_SYNDROME) -		oobreadlen -= oobsel->eccbytes; +/** + * nand_transfer_oob - [Internal] Transfer oob to client buffer + * @chip:	nand chip structure + * @oob:	oob destination address + * @ops:	oob ops structure + * @len:	size of oob to transfer + */ +static uint8_t *nand_transfer_oob(struct nand_chip *chip, uint8_t *oob, +				  struct mtd_oob_ops *ops, size_t len) +{ +	switch(ops->mode) { -	/* Loop until all data read */ -	while (read < len) { +	case MTD_OOB_PLACE: +	case MTD_OOB_RAW: +		memcpy(oob, chip->oob_poi + ops->ooboffs, len); +		return oob + len; -		int aligned = (!col && (len - read) >= end); -		/* -		 * If the read is not page aligned, we have to read into data buffer -		 * due to ecc, else we read into return buffer direct -		 */ -		if (aligned) -			data_poi = &buf[read]; -		else -			data_poi = this->data_buf; +	case MTD_OOB_AUTO: { +		struct nand_oobfree *free = chip->ecc.layout->oobfree; +		uint32_t boffs = 0, roffs = ops->ooboffs; +		size_t bytes = 0; -		/* Check, if we have this page in the buffer -		 * -		 * FIXME: Make it work when we must provide oob data too, -		 * check the usage of data_buf oob field -		 */ -		if (realpage == this->pagebuf && !oob_buf) { -			/* aligned read ? */ -			if (aligned) -				memcpy (data_poi, this->data_buf, end); -			goto readdata; +		for(; free->length && len; free++, len -= bytes) { +			/* Read request not from offset 0 ? */ +			if (unlikely(roffs)) { +				if (roffs >= free->length) { +					roffs -= free->length; +					continue; +				} +				boffs = free->offset + roffs; +				bytes = min_t(size_t, len, +					      (free->length - roffs)); +				roffs = 0; +			} else { +				bytes = min_t(size_t, len, free->length); +				boffs = free->offset; +			} +			memcpy(oob, chip->oob_poi + boffs, bytes); +			oob += bytes;  		} +		return oob; +	} +	default: +		BUG(); +	} +	return NULL; +} -		/* Check, if we must send the read command */ -		if (sndcmd) { -			this->cmdfunc (mtd, NAND_CMD_READ0, 0x00, page); -			sndcmd = 0; -		} +/** + * nand_do_read_ops - [Internal] Read data with ECC + * + * @mtd:	MTD device structure + * @from:	offset to read from + * @ops:	oob ops structure + * + * Internal function. Called with chip held. + */ +static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, +			    struct mtd_oob_ops *ops) +{ +	int chipnr, page, realpage, col, bytes, aligned; +	struct nand_chip *chip = mtd->priv; +	struct mtd_ecc_stats stats; +	int blkcheck = (1 << (chip->phys_erase_shift - chip->page_shift)) - 1; +	int sndcmd = 1; +	int ret = 0; +	uint32_t readlen = ops->len; +	uint32_t oobreadlen = ops->ooblen; +	uint8_t *bufpoi, *oob, *buf; -		/* get oob area, if we have no oob buffer from fs-driver */ -		if (!oob_buf || oobsel->useecc == MTD_NANDECC_AUTOPLACE || -			oobsel->useecc == MTD_NANDECC_AUTOPL_USR) -			oob_data = &this->data_buf[end]; +	stats = mtd->ecc_stats; -		eccsteps = this->eccsteps; +	chipnr = (int)(from >> chip->chip_shift); +	chip->select_chip(mtd, chipnr); -		switch (eccmode) { -		case NAND_ECC_NONE: {	/* No ECC, Read in a page */ -/* XXX U-BOOT XXX */ -#if 0 -			static unsigned long lastwhinge = 0; -			if ((lastwhinge / HZ) != (jiffies / HZ)) { -				printk (KERN_WARNING "Reading data from NAND FLASH without ECC is not recommended\n"); -				lastwhinge = jiffies; -			} -#else -			puts("Reading data from NAND FLASH without ECC is not recommended\n"); -#endif -			this->read_buf(mtd, data_poi, end); -			break; -		} +	realpage = (int)(from >> chip->page_shift); +	page = realpage & chip->pagemask; -		case NAND_ECC_SOFT:	/* Software ECC 3/256: Read in a page + oob data */ -			this->read_buf(mtd, data_poi, end); -			for (i = 0, datidx = 0; eccsteps; eccsteps--, i+=3, datidx += ecc) -				this->calculate_ecc(mtd, &data_poi[datidx], &ecc_calc[i]); -			break; +	col = (int)(from & (mtd->writesize - 1)); -		default: -			for (i = 0, datidx = 0; eccsteps; eccsteps--, i+=eccbytes, datidx += ecc) { -				this->enable_hwecc(mtd, NAND_ECC_READ); -				this->read_buf(mtd, &data_poi[datidx], ecc); +	buf = ops->datbuf; +	oob = ops->oobbuf; -				/* HW ecc with syndrome calculation must read the -				 * syndrome from flash immidiately after the data */ -				if (!compareecc) { -					/* Some hw ecc generators need to know when the -					 * syndrome is read from flash */ -					this->enable_hwecc(mtd, NAND_ECC_READSYN); -					this->read_buf(mtd, &oob_data[i], eccbytes); -					/* 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) { -						MTDDEBUG (MTD_DEBUG_LEVEL0, "nand_read_ecc: " -							"Failed ECC read, page 0x%08x on chip %d\n", page, chipnr); -						ecc_failed++; -					} -				} else { -					this->calculate_ecc(mtd, &data_poi[datidx], &ecc_calc[i]); -				} -			} -			break; -		} - -		/* read oobdata */ -		this->read_buf(mtd, &oob_data[mtd->oobsize - oobreadlen], oobreadlen); - -		/* Skip ECC check, if not requested (ECC_NONE or HW_ECC with syndromes) */ -		if (!compareecc) -			goto readoob; +	while(1) { +		bytes = min(mtd->writesize - col, readlen); +		aligned = (bytes == mtd->writesize); -		/* Pick the ECC bytes out of the oob data */ -		for (j = 0; j < oobsel->eccbytes; j++) -			ecc_code[j] = oob_data[oob_config[j]]; +		/* Is the current page in the buffer ? */ +		if (realpage != chip->pagebuf || oob) { +			bufpoi = aligned ? buf : chip->buffers->databuf; -		/* correct data, if neccecary */ -		for (i = 0, j = 0, datidx = 0; i < this->eccsteps; i++, datidx += ecc) { -			ecc_status = this->correct_data(mtd, &data_poi[datidx], &ecc_code[j], &ecc_calc[j]); +			if (likely(sndcmd)) { +				chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page); +				sndcmd = 0; +			} -			/* Get next chunk of ecc bytes */ -			j += eccbytes; +			/* Now read the page into the buffer */ +			if (unlikely(ops->mode == MTD_OOB_RAW)) +				ret = chip->ecc.read_page_raw(mtd, chip, bufpoi); +			else +				ret = chip->ecc.read_page(mtd, chip, bufpoi); +			if (ret < 0) +				break; -			/* Check, if we have a fs supplied oob-buffer, -			 * This is the legacy mode. Used by YAFFS1 -			 * Should go away some day -			 */ -			if (oob_buf && oobsel->useecc == MTD_NANDECC_PLACE) { -				int *p = (int *)(&oob_data[mtd->oobsize]); -				p[i] = ecc_status; +			/* Transfer not aligned data */ +			if (!aligned) { +				chip->pagebuf = realpage; +				memcpy(buf, chip->buffers->databuf + col, bytes);  			} -			if (ecc_status == -1) { -				MTDDEBUG (MTD_DEBUG_LEVEL0, "nand_read_ecc: " -				          "Failed ECC read, page 0x%08x\n", -				          page); -				ecc_failed++; +			buf += bytes; + +			if (unlikely(oob)) { +				/* Raw mode does data:oob:data:oob */ +				if (ops->mode != MTD_OOB_RAW) { +					int toread = min(oobreadlen, +						chip->ecc.layout->oobavail); +					if (toread) { +						oob = nand_transfer_oob(chip, +							oob, ops, toread); +						oobreadlen -= toread; +					} +				} else +					buf = nand_transfer_oob(chip, +						buf, ops, mtd->oobsize);  			} -		} -	readoob: -		/* check, if we have a fs supplied oob-buffer */ -		if (oob_buf) { -			/* 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++) { -					int from = oobsel->oobfree[i][0]; -					int num = oobsel->oobfree[i][1]; -					memcpy(&oob_buf[oob+j], &oob_data[from], num); -					j+= num; -				} -				oob += mtd->oobavail; -				break; -			case MTD_NANDECC_PLACE: -				/* YAFFS1 legacy mode */ -				oob_data += this->eccsteps * sizeof (int); -			default: -				oob_data += mtd->oobsize; +			if (!(chip->options & NAND_NO_READRDY)) { +				/* +				 * Apply delay or wait for ready/busy pin. Do +				 * this before the AUTOINCR check, so no +				 * problems arise if a chip which does auto +				 * increment is marked as NOAUTOINCR by the +				 * board driver. +				 */ +				if (!chip->dev_ready) +					udelay(chip->chip_delay); +				else +					nand_wait_ready(mtd);  			} +		} else { +			memcpy(buf, chip->buffers->databuf + col, bytes); +			buf += bytes;  		} -	readdata: -		/* Partial page read, transfer data into fs buffer */ -		if (!aligned) { -			for (j = col; j < end && read < len; j++) -				buf[read++] = data_poi[j]; -			this->pagebuf = realpage; -		} else -			read += mtd->oobblock; -		/* Apply delay or wait for ready/busy pin -		 * Do this before the AUTOINCR check, so no problems -		 * arise if a chip which does auto increment -		 * is marked as NOAUTOINCR by the board driver. -		*/ -		if (!this->dev_ready) -			udelay (this->chip_delay); -		else -			while (!this->dev_ready(mtd)); +		readlen -= bytes; -		if (read == len) +		if (!readlen)  			break;  		/* For subsequent reads align to page boundary. */ @@ -1360,732 +1174,829 @@ static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len,  		/* Increment page address */  		realpage++; -		page = realpage & this->pagemask; +		page = realpage & chip->pagemask;  		/* Check, if we cross a chip boundary */  		if (!page) {  			chipnr++; -			this->select_chip(mtd, -1); -			this->select_chip(mtd, chipnr); +			chip->select_chip(mtd, -1); +			chip->select_chip(mtd, chipnr);  		} +  		/* Check, if the chip supports auto page increment  		 * or if we have hit a block boundary. -		*/ -		if (!NAND_CANAUTOINCR(this) || !(page & blockcheck)) +		 */ +		if (!NAND_CANAUTOINCR(chip) || !(page & blkcheck))  			sndcmd = 1;  	} -	/* Deselect and wake up anyone waiting on the device */ -	nand_release_device(mtd); +	ops->retlen = ops->len - (size_t) readlen; +	if (oob) +		ops->oobretlen = ops->ooblen - oobreadlen; -	/* -	 * Return success, if no ECC failures, else -EBADMSG -	 * fs driver will take care of that, because -	 * retlen == desired len and result == -EBADMSG -	 */ -	*retlen = read; -	return ecc_failed ? -EBADMSG : 0; +	if (ret) +		return ret; + +	if (mtd->ecc_stats.failed - stats.failed) +		return -EBADMSG; + +	return  mtd->ecc_stats.corrected - stats.corrected ? -EUCLEAN : 0;  }  /** - * nand_read_oob - [MTD Interface] NAND read out-of-band + * 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   * - * NAND read out-of-band data from the spare area + * Get hold of the chip and call nand_do_read   */ -static int nand_read_oob (struct mtd_info *mtd, loff_t from, size_t len, size_t * retlen, u_char * buf) +static int nand_read(struct mtd_info *mtd, loff_t from, size_t len, +		     size_t *retlen, uint8_t *buf)  { -	int i, col, page, chipnr; -	struct nand_chip *this = mtd->priv; -	int	blockcheck = (1 << (this->phys_erase_shift - this->page_shift)) - 1; +	struct nand_chip *chip = mtd->priv; +	int ret; -	MTDDEBUG (MTD_DEBUG_LEVEL3, "nand_read_oob: from = 0x%08x, len = %i\n", -	          (unsigned int) from, (int) len); +	/* Do not allow reads past end of device */ +	if ((from + len) > mtd->size) +		return -EINVAL; +	if (!len) +		return 0; -	/* Shift to get page */ -	page = (int)(from >> this->page_shift); -	chipnr = (int)(from >> this->chip_shift); +	nand_get_device(chip, mtd, FL_READING); -	/* Mask to get column */ -	col = from & (mtd->oobsize - 1); +	chip->ops.len = len; +	chip->ops.datbuf = buf; +	chip->ops.oobbuf = NULL; -	/* Initialize return length value */ -	*retlen = 0; +	ret = nand_do_read_ops(mtd, from, &chip->ops); -	/* Do not allow reads past end of device */ -	if ((from + len) > mtd->size) { -		MTDDEBUG (MTD_DEBUG_LEVEL0, -		          "nand_read_oob: Attempt read beyond end of device\n"); -		*retlen = 0; -		return -EINVAL; +	*retlen = chip->ops.retlen; + +	nand_release_device(mtd); + +	return ret; +} + +/** + * nand_read_oob_std - [REPLACABLE] the most common OOB data read function + * @mtd:	mtd info structure + * @chip:	nand chip info structure + * @page:	page number to read + * @sndcmd:	flag whether to issue read command or not + */ +static int nand_read_oob_std(struct mtd_info *mtd, struct nand_chip *chip, +			     int page, int sndcmd) +{ +	if (sndcmd) { +		chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page); +		sndcmd = 0;  	} +	chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); +	return sndcmd; +} -	/* Grab the lock and see if the device is available */ -	nand_get_device (this, mtd , FL_READING); +/** + * nand_read_oob_syndrome - [REPLACABLE] OOB data read function for HW ECC + *			    with syndromes + * @mtd:	mtd info structure + * @chip:	nand chip info structure + * @page:	page number to read + * @sndcmd:	flag whether to issue read command or not + */ +static int nand_read_oob_syndrome(struct mtd_info *mtd, struct nand_chip *chip, +				  int page, int sndcmd) +{ +	uint8_t *buf = chip->oob_poi; +	int length = mtd->oobsize; +	int chunk = chip->ecc.bytes + chip->ecc.prepad + chip->ecc.postpad; +	int eccsize = chip->ecc.size; +	uint8_t *bufpoi = buf; +	int i, toread, sndrnd = 0, pos; -	/* Select the NAND device */ -	this->select_chip(mtd, chipnr); +	chip->cmdfunc(mtd, NAND_CMD_READ0, chip->ecc.size, page); +	for (i = 0; i < chip->ecc.steps; i++) { +		if (sndrnd) { +			pos = eccsize + i * (eccsize + chunk); +			if (mtd->writesize > 512) +				chip->cmdfunc(mtd, NAND_CMD_RNDOUT, pos, -1); +			else +				chip->cmdfunc(mtd, NAND_CMD_READ0, pos, page); +		} else +			sndrnd = 1; +		toread = min_t(int, length, chunk); +		chip->read_buf(mtd, bufpoi, toread); +		bufpoi += toread; +		length -= toread; +	} +	if (length > 0) +		chip->read_buf(mtd, bufpoi, length); -	/* Send the read command */ -	this->cmdfunc (mtd, NAND_CMD_READOOB, col, page & this->pagemask); -	/* -	 * Read the data, if we read more than one page -	 * oob data, let the device transfer the data ! -	 */ -	i = 0; -	while (i < len) { -		int thislen = mtd->oobsize - col; -		thislen = min_t(int, thislen, len); -		this->read_buf(mtd, &buf[i], thislen); -		i += thislen; +	return 1; +} -		/* Apply delay or wait for ready/busy pin -		 * Do this before the AUTOINCR check, so no problems -		 * arise if a chip which does auto increment -		 * is marked as NOAUTOINCR by the board driver. -		*/ -		if (!this->dev_ready) -			udelay (this->chip_delay); -		else -			while (!this->dev_ready(mtd)); +/** + * nand_write_oob_std - [REPLACABLE] the most common OOB data write function + * @mtd:	mtd info structure + * @chip:	nand chip info structure + * @page:	page number to write + */ +static int nand_write_oob_std(struct mtd_info *mtd, struct nand_chip *chip, +			      int page) +{ +	int status = 0; +	const uint8_t *buf = chip->oob_poi; +	int length = mtd->oobsize; -		/* Read more ? */ -		if (i < len) { -			page++; -			col = 0; +	chip->cmdfunc(mtd, NAND_CMD_SEQIN, mtd->writesize, page); +	chip->write_buf(mtd, buf, length); +	/* Send command to program the OOB data */ +	chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); -			/* Check, if we cross a chip boundary */ -			if (!(page & this->pagemask)) { -				chipnr++; -				this->select_chip(mtd, -1); -				this->select_chip(mtd, chipnr); -			} +	status = chip->waitfunc(mtd, chip); + +	return status & NAND_STATUS_FAIL ? -EIO : 0; +} + +/** + * nand_write_oob_syndrome - [REPLACABLE] OOB data write function for HW ECC + *			     with syndrome - only for large page flash ! + * @mtd:	mtd info structure + * @chip:	nand chip info structure + * @page:	page number to write + */ +static int nand_write_oob_syndrome(struct mtd_info *mtd, +				   struct nand_chip *chip, int page) +{ +	int chunk = chip->ecc.bytes + chip->ecc.prepad + chip->ecc.postpad; +	int eccsize = chip->ecc.size, length = mtd->oobsize; +	int i, len, pos, status = 0, sndcmd = 0, steps = chip->ecc.steps; +	const uint8_t *bufpoi = chip->oob_poi; -			/* Check, if the chip supports auto page increment -			 * or if we have hit a block boundary. -			*/ -			if (!NAND_CANAUTOINCR(this) || !(page & blockcheck)) { -				/* For subsequent page reads set offset to 0 */ -				this->cmdfunc (mtd, NAND_CMD_READOOB, 0x0, page & this->pagemask); +	/* +	 * data-ecc-data-ecc ... ecc-oob +	 * or +	 * data-pad-ecc-pad-data-pad .... ecc-pad-oob +	 */ +	if (!chip->ecc.prepad && !chip->ecc.postpad) { +		pos = steps * (eccsize + chunk); +		steps = 0; +	} else +		pos = eccsize; + +	chip->cmdfunc(mtd, NAND_CMD_SEQIN, pos, page); +	for (i = 0; i < steps; i++) { +		if (sndcmd) { +			if (mtd->writesize <= 512) { +				uint32_t fill = 0xFFFFFFFF; + +				len = eccsize; +				while (len > 0) { +					int num = min_t(int, len, 4); +					chip->write_buf(mtd, (uint8_t *)&fill, +							num); +					len -= num; +				} +			} else { +				pos = eccsize + i * (eccsize + chunk); +				chip->cmdfunc(mtd, NAND_CMD_RNDIN, pos, -1);  			} -		} +		} else +			sndcmd = 1; +		len = min_t(int, length, chunk); +		chip->write_buf(mtd, bufpoi, len); +		bufpoi += len; +		length -= len;  	} +	if (length > 0) +		chip->write_buf(mtd, bufpoi, length); -	/* Deselect and wake up anyone waiting on the device */ -	nand_release_device(mtd); +	chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); +	status = chip->waitfunc(mtd, chip); -	/* Return happy */ -	*retlen = len; -	return 0; +	return status & NAND_STATUS_FAIL ? -EIO : 0;  }  /** - * nand_read_raw - [GENERIC] Read raw data including oob into buffer + * nand_do_read_oob - [Intern] NAND read out-of-band   * @mtd:	MTD device structure - * @buf:	temporary buffer   * @from:	offset to read from - * @len:	number of bytes to read - * @ooblen:	number of oob data bytes to read + * @ops:	oob operations description structure   * - * Read raw data including oob into buffer + * NAND read out-of-band data from the spare area   */ -int nand_read_raw (struct mtd_info *mtd, uint8_t *buf, loff_t from, size_t len, size_t ooblen) +static int nand_do_read_oob(struct mtd_info *mtd, loff_t from, +			    struct mtd_oob_ops *ops)  { -	struct nand_chip *this = mtd->priv; -	int page = (int) (from >> this->page_shift); -	int chip = (int) (from >> this->chip_shift); -	int sndcmd = 1; -	int cnt = 0; -	int pagesize = mtd->oobblock + mtd->oobsize; -	int	blockcheck = (1 << (this->phys_erase_shift - this->page_shift)) - 1; +	int page, realpage, chipnr, sndcmd = 1; +	struct nand_chip *chip = mtd->priv; +	int blkcheck = (1 << (chip->phys_erase_shift - chip->page_shift)) - 1; +	int readlen = ops->ooblen; +	int len; +	uint8_t *buf = ops->oobbuf; + +	MTDDEBUG (MTD_DEBUG_LEVEL3, "nand_read_oob: from = 0x%08Lx, len = %i\n", +	          (unsigned long long)from, readlen); + +	if (ops->mode == MTD_OOB_AUTO) +		len = chip->ecc.layout->oobavail; +	else +		len = mtd->oobsize; + +	if (unlikely(ops->ooboffs >= len)) { +		MTDDEBUG (MTD_DEBUG_LEVEL0, "nand_read_oob: " +		          "Attempt to start read outside oob\n"); +		return -EINVAL; +	}  	/* Do not allow reads past end of device */ -	if ((from + len) > mtd->size) { -		MTDDEBUG (MTD_DEBUG_LEVEL0, -		          "nand_read_raw: Attempt read beyond end of device\n"); +	if (unlikely(from >= mtd->size || +		     ops->ooboffs + readlen > ((mtd->size >> chip->page_shift) - +					(from >> chip->page_shift)) * len)) { +		MTDDEBUG (MTD_DEBUG_LEVEL0, "nand_read_oob: " +		          "Attempt read beyond end of device\n");  		return -EINVAL;  	} -	/* Grab the lock and see if the device is available */ -	nand_get_device (this, mtd , FL_READING); +	chipnr = (int)(from >> chip->chip_shift); +	chip->select_chip(mtd, chipnr); -	this->select_chip (mtd, chip); +	/* Shift to get page */ +	realpage = (int)(from >> chip->page_shift); +	page = realpage & chip->pagemask; -	/* Add requested oob length */ -	len += ooblen; +	while(1) { +		sndcmd = chip->ecc.read_oob(mtd, chip, page, sndcmd); -	while (len) { -		if (sndcmd) -			this->cmdfunc (mtd, NAND_CMD_READ0, 0, page & this->pagemask); -		sndcmd = 0; +		len = min(len, readlen); +		buf = nand_transfer_oob(chip, buf, ops, len); + +		if (!(chip->options & NAND_NO_READRDY)) { +			/* +			 * Apply delay or wait for ready/busy pin. Do this +			 * before the AUTOINCR check, so no problems arise if a +			 * chip which does auto increment is marked as +			 * NOAUTOINCR by the board driver. +			 */ +			if (!chip->dev_ready) +				udelay(chip->chip_delay); +			else +				nand_wait_ready(mtd); +		} -		this->read_buf (mtd, &buf[cnt], pagesize); +		readlen -= len; +		if (!readlen) +			break; -		len -= pagesize; -		cnt += pagesize; -		page++; +		/* Increment page address */ +		realpage++; -		if (!this->dev_ready) -			udelay (this->chip_delay); -		else -			while (!this->dev_ready(mtd)); +		page = realpage & chip->pagemask; +		/* Check, if we cross a chip boundary */ +		if (!page) { +			chipnr++; +			chip->select_chip(mtd, -1); +			chip->select_chip(mtd, chipnr); +		} -		/* Check, if the chip supports auto page increment */ -		if (!NAND_CANAUTOINCR(this) || !(page & blockcheck)) +		/* Check, if the chip supports auto page increment +		 * or if we have hit a block boundary. +		 */ +		if (!NAND_CANAUTOINCR(chip) || !(page & blkcheck))  			sndcmd = 1;  	} -	/* Deselect and wake up anyone waiting on the device */ -	nand_release_device(mtd); +	ops->oobretlen = ops->ooblen;  	return 0;  } -  /** - * nand_prepare_oobbuf - [GENERIC] Prepare the out of band buffer + * nand_read_oob - [MTD Interface] NAND read data and/or out-of-band   * @mtd:	MTD device structure - * @fsbuf:	buffer given by fs driver - * @oobsel:	out of band selection structre - * @autoplace:	1 = place given buffer into the oob bytes - * @numpages:	number of pages to prepare - * - * Return: - * 1. Filesystem buffer available and autoplacement is off, - *    return filesystem buffer - * 2. No filesystem buffer or autoplace is off, return internal - *    buffer - * 3. Filesystem buffer is given and autoplace selected - *    put data from fs buffer into internal buffer and - *    retrun internal buffer - * - * Note: The internal buffer is filled with 0xff. This must - * be done only once, when no autoplacement happens - * Autoplacement sets the buffer dirty flag, which - * forces the 0xff fill before using the buffer again. + * @from:	offset to read from + * @ops:	oob operation description structure   * -*/ -static u_char * nand_prepare_oobbuf (struct mtd_info *mtd, u_char *fsbuf, struct nand_oobinfo *oobsel, -		int autoplace, int numpages) + * NAND read data and/or out-of-band data + */ +static int nand_read_oob(struct mtd_info *mtd, loff_t from, +			 struct mtd_oob_ops *ops)  { -	struct nand_chip *this = mtd->priv; -	int i, len, ofs; +	struct nand_chip *chip = mtd->priv; +	int ret = -ENOTSUPP; -	/* Zero copy fs supplied buffer */ -	if (fsbuf && !autoplace) -		return fsbuf; +	ops->retlen = 0; -	/* Check, if the buffer must be filled with ff again */ -	if (this->oobdirty) { -		memset (this->oob_buf, 0xff, -			mtd->oobsize << (this->phys_erase_shift - this->page_shift)); -		this->oobdirty = 0; +	/* Do not allow reads past end of device */ +	if (ops->datbuf && (from + ops->len) > mtd->size) { +		MTDDEBUG (MTD_DEBUG_LEVEL0, "nand_read_oob: " +		          "Attempt read beyond end of device\n"); +		return -EINVAL;  	} -	/* If we have no autoplacement or no fs buffer use the internal one */ -	if (!autoplace || !fsbuf) -		return this->oob_buf; +	nand_get_device(chip, mtd, FL_READING); -	/* Walk through the pages and place the data */ -	this->oobdirty = 1; -	ofs = 0; -	while (numpages--) { -		for (i = 0, len = 0; len < mtd->oobavail; i++) { -			int to = ofs + oobsel->oobfree[i][0]; -			int num = oobsel->oobfree[i][1]; -			memcpy (&this->oob_buf[to], fsbuf, num); -			len += num; -			fsbuf += num; -		} -		ofs += mtd->oobavail; +	switch(ops->mode) { +	case MTD_OOB_PLACE: +	case MTD_OOB_AUTO: +	case MTD_OOB_RAW: +		break; + +	default: +		goto out;  	} -	return this->oob_buf; + +	if (!ops->datbuf) +		ret = nand_do_read_oob(mtd, from, ops); +	else +		ret = nand_do_read_ops(mtd, from, ops); + + out: +	nand_release_device(mtd); +	return ret;  } -#define NOTALIGNED(x) (x & (mtd->oobblock-1)) != 0  /** - * nand_write - [MTD Interface] compability function for nand_write_ecc - * @mtd:	MTD device structure - * @to:		offset to write to - * @len:	number of bytes to write - * @retlen:	pointer to variable to store the number of written bytes - * @buf:	the data to write - * - * This function simply calls nand_write_ecc with oob buffer and oobsel = NULL - * -*/ -static int nand_write (struct mtd_info *mtd, loff_t to, size_t len, size_t * retlen, const u_char * buf) + * nand_write_page_raw - [Intern] raw page write function + * @mtd:	mtd info structure + * @chip:	nand chip info structure + * @buf:	data buffer + */ +static void nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip, +				const uint8_t *buf)  { -	return (nand_write_ecc (mtd, to, len, retlen, buf, NULL, NULL)); +	chip->write_buf(mtd, buf, mtd->writesize); +	chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);  }  /** - * nand_write_ecc - [MTD Interface] NAND write with ECC - * @mtd:	MTD device structure - * @to:		offset to write to - * @len:	number of bytes to write - * @retlen:	pointer to variable to store the number of written bytes - * @buf:	the data to write - * @eccbuf:	filesystem supplied oob data buffer - * @oobsel:	oob selection structure - * - * NAND write with ECC + * nand_write_page_swecc - [REPLACABLE] software ecc based page write function + * @mtd:	mtd info structure + * @chip:	nand chip info structure + * @buf:	data buffer   */ -static int nand_write_ecc (struct mtd_info *mtd, loff_t to, size_t len, -			   size_t * retlen, const u_char * buf, u_char * eccbuf, struct nand_oobinfo *oobsel) +static void nand_write_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, +				  const uint8_t *buf)  { -	int startpage, page, ret = -EIO, oob = 0, written = 0, chipnr; -	int autoplace = 0, numpages, totalpages; -	struct nand_chip *this = mtd->priv; -	u_char *oobbuf, *bufstart; -	int	ppblock = (1 << (this->phys_erase_shift - this->page_shift)); +	int i, eccsize = chip->ecc.size; +	int eccbytes = chip->ecc.bytes; +	int eccsteps = chip->ecc.steps; +	uint8_t *ecc_calc = chip->buffers->ecccalc; +	const uint8_t *p = buf; +	uint32_t *eccpos = chip->ecc.layout->eccpos; -	MTDDEBUG (MTD_DEBUG_LEVEL3, "nand_write_ecc: to = 0x%08x, len = %i\n", -	          (unsigned int) to, (int) len); +	/* Software ecc calculation */ +	for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) +		chip->ecc.calculate(mtd, p, &ecc_calc[i]); -	/* Initialize retlen, in case of early exit */ -	*retlen = 0; +	for (i = 0; i < chip->ecc.total; i++) +		chip->oob_poi[eccpos[i]] = ecc_calc[i]; -	/* Do not allow write past end of device */ -	if ((to + len) > mtd->size) { -		MTDDEBUG (MTD_DEBUG_LEVEL0, -		          "nand_write_ecc: Attempt to write past end of page\n"); -		return -EINVAL; -	} +	chip->ecc.write_page_raw(mtd, chip, buf); +} -	/* reject writes, which are not page aligned */ -	if (NOTALIGNED (to) || NOTALIGNED(len)) { -		printk (KERN_NOTICE "nand_write_ecc: Attempt to write not page aligned data\n"); -		return -EINVAL; +/** + * nand_write_page_hwecc - [REPLACABLE] hardware ecc based page write function + * @mtd:	mtd info structure + * @chip:	nand chip info structure + * @buf:	data buffer + */ +static void nand_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, +				  const uint8_t *buf) +{ +	int i, eccsize = chip->ecc.size; +	int eccbytes = chip->ecc.bytes; +	int eccsteps = chip->ecc.steps; +	uint8_t *ecc_calc = chip->buffers->ecccalc; +	const uint8_t *p = buf; +	uint32_t *eccpos = chip->ecc.layout->eccpos; + +	for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { +		chip->ecc.hwctl(mtd, NAND_ECC_WRITE); +		chip->write_buf(mtd, p, eccsize); +		chip->ecc.calculate(mtd, p, &ecc_calc[i]);  	} -	/* Grab the lock and see if the device is available */ -	nand_get_device (this, mtd, FL_WRITING); +	for (i = 0; i < chip->ecc.total; i++) +		chip->oob_poi[eccpos[i]] = ecc_calc[i]; -	/* Calculate chipnr */ -	chipnr = (int)(to >> this->chip_shift); -	/* Select the NAND device */ -	this->select_chip(mtd, chipnr); +	chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); +} -	/* Check, if it is write protected */ -	if (nand_check_wp(mtd)) { -		printk (KERN_NOTICE "nand_write_ecc: Device is write protected\n"); -		goto out; -	} +/** + * nand_write_page_syndrome - [REPLACABLE] hardware ecc syndrom based page write + * @mtd:	mtd info structure + * @chip:	nand chip info structure + * @buf:	data buffer + * + * The hw generator calculates the error syndrome automatically. Therefor + * we need a special oob layout and handling. + */ +static void nand_write_page_syndrome(struct mtd_info *mtd, +				    struct nand_chip *chip, const uint8_t *buf) +{ +	int i, eccsize = chip->ecc.size; +	int eccbytes = chip->ecc.bytes; +	int eccsteps = chip->ecc.steps; +	const uint8_t *p = buf; +	uint8_t *oob = chip->oob_poi; + +	for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { -	/* if oobsel is NULL, use chip defaults */ -	if (oobsel == NULL) -		oobsel = &mtd->oobinfo; +		chip->ecc.hwctl(mtd, NAND_ECC_WRITE); +		chip->write_buf(mtd, p, eccsize); -	/* Autoplace of oob data ? Use the default placement scheme */ -	if (oobsel->useecc == MTD_NANDECC_AUTOPLACE) { -		oobsel = this->autooob; -		autoplace = 1; +		if (chip->ecc.prepad) { +			chip->write_buf(mtd, oob, chip->ecc.prepad); +			oob += chip->ecc.prepad; +		} + +		chip->ecc.calculate(mtd, p, oob); +		chip->write_buf(mtd, oob, eccbytes); +		oob += eccbytes; + +		if (chip->ecc.postpad) { +			chip->write_buf(mtd, oob, chip->ecc.postpad); +			oob += chip->ecc.postpad; +		}  	} -	if (oobsel->useecc == MTD_NANDECC_AUTOPL_USR) -		autoplace = 1; -	/* Setup variables and oob buffer */ -	totalpages = len >> this->page_shift; -	page = (int) (to >> this->page_shift); -	/* Invalidate the page cache, if we write to the cached page */ -	if (page <= this->pagebuf && this->pagebuf < (page + totalpages)) -		this->pagebuf = -1; +	/* Calculate remaining oob bytes */ +	i = mtd->oobsize - (oob - chip->oob_poi); +	if (i) +		chip->write_buf(mtd, oob, i); +} + +/** + * nand_write_page - [REPLACEABLE] write one page + * @mtd:	MTD device structure + * @chip:	NAND chip descriptor + * @buf:	the data to write + * @page:	page number to write + * @cached:	cached programming + * @raw:	use _raw version of write_page + */ +static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip, +			   const uint8_t *buf, int page, int cached, int raw) +{ +	int status; -	/* Set it relative to chip */ -	page &= this->pagemask; -	startpage = page; -	/* Calc number of pages we can write in one go */ -	numpages = min (ppblock - (startpage  & (ppblock - 1)), totalpages); -	oobbuf = nand_prepare_oobbuf (mtd, eccbuf, oobsel, autoplace, numpages); -	bufstart = (u_char *)buf; +	chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page); -	/* Loop until all data is written */ -	while (written < len) { +	if (unlikely(raw)) +		chip->ecc.write_page_raw(mtd, chip, buf); +	else +		chip->ecc.write_page(mtd, chip, buf); -		this->data_poi = (u_char*) &buf[written]; -		/* Write one page. If this is the last page to write -		 * or the last page in this block, then use the -		 * real pageprogram command, else select cached programming -		 * if supported by the chip. +	/* +	 * Cached progamming disabled for now, Not sure if its worth the +	 * trouble. The speed gain is not very impressive. (2.3->2.6Mib/s) +	 */ +	cached = 0; + +	if (!cached || !(chip->options & NAND_CACHEPRG)) { + +		chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); +		status = chip->waitfunc(mtd, chip); +		/* +		 * See if operation failed and additional status checks are +		 * available  		 */ -		ret = nand_write_page (mtd, this, page, &oobbuf[oob], oobsel, (--numpages > 0)); -		if (ret) { -			MTDDEBUG (MTD_DEBUG_LEVEL0, -			          "nand_write_ecc: write_page failed %d\n", ret); -			goto out; -		} -		/* Next oob page */ -		oob += mtd->oobsize; -		/* Update written bytes count */ -		written += mtd->oobblock; -		if (written == len) -			goto cmp; +		if ((status & NAND_STATUS_FAIL) && (chip->errstat)) +			status = chip->errstat(mtd, chip, FL_WRITING, status, +					       page); -		/* Increment page address */ -		page++; +		if (status & NAND_STATUS_FAIL) +			return -EIO; +	} else { +		chip->cmdfunc(mtd, NAND_CMD_CACHEDPROG, -1, -1); +		status = chip->waitfunc(mtd, chip); +	} -		/* Have we hit a block boundary ? Then we have to verify and -		 * if verify is ok, we have to setup the oob buffer for -		 * the next pages. -		*/ -		if (!(page & (ppblock - 1))){ -			int ofs; -			this->data_poi = bufstart; -			ret = nand_verify_pages (mtd, this, startpage, -				page - startpage, -				oobbuf, oobsel, chipnr, (eccbuf != NULL)); -			if (ret) { -				MTDDEBUG (MTD_DEBUG_LEVEL0, "nand_write_ecc: " -				          "verify_pages failed %d\n", ret); -				goto out; -			} -			*retlen = written; -			bufstart = (u_char*) &buf[written]; +#ifdef CONFIG_MTD_NAND_VERIFY_WRITE +	/* Send command to read back the data */ +	chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page); -			ofs = autoplace ? mtd->oobavail : mtd->oobsize; -			if (eccbuf) -				eccbuf += (page - startpage) * ofs; -			totalpages -= page - startpage; -			numpages = min (totalpages, ppblock); -			page &= this->pagemask; -			startpage = page; -			oob = 0; -			this->oobdirty = 1; -			oobbuf = nand_prepare_oobbuf (mtd, eccbuf, oobsel, -					autoplace, numpages); -			/* Check, if we cross a chip boundary */ -			if (!page) { -				chipnr++; -				this->select_chip(mtd, -1); -				this->select_chip(mtd, chipnr); +	if (chip->verify_buf(mtd, buf, mtd->writesize)) +		return -EIO; +#endif +	return 0; +} + +/** + * nand_fill_oob - [Internal] Transfer client buffer to oob + * @chip:	nand chip structure + * @oob:	oob data buffer + * @ops:	oob ops structure + */ +static uint8_t *nand_fill_oob(struct nand_chip *chip, uint8_t *oob, +				  struct mtd_oob_ops *ops) +{ +	size_t len = ops->ooblen; + +	switch(ops->mode) { + +	case MTD_OOB_PLACE: +	case MTD_OOB_RAW: +		memcpy(chip->oob_poi + ops->ooboffs, oob, len); +		return oob + len; + +	case MTD_OOB_AUTO: { +		struct nand_oobfree *free = chip->ecc.layout->oobfree; +		uint32_t boffs = 0, woffs = ops->ooboffs; +		size_t bytes = 0; + +		for(; free->length && len; free++, len -= bytes) { +			/* Write request not from offset 0 ? */ +			if (unlikely(woffs)) { +				if (woffs >= free->length) { +					woffs -= free->length; +					continue; +				} +				boffs = free->offset + woffs; +				bytes = min_t(size_t, len, +					      (free->length - woffs)); +				woffs = 0; +			} else { +				bytes = min_t(size_t, len, free->length); +				boffs = free->offset;  			} +			memcpy(chip->oob_poi + boffs, oob, bytes); +			oob += bytes;  		} +		return oob;  	} -	/* Verify the remaining pages */ -cmp: -	this->data_poi = bufstart; -	ret = nand_verify_pages (mtd, this, startpage, totalpages, -		oobbuf, oobsel, chipnr, (eccbuf != NULL)); -	if (!ret) -		*retlen = written; -	else -		MTDDEBUG (MTD_DEBUG_LEVEL0, -		          "nand_write_ecc: verify_pages failed %d\n", ret); - -out: -	/* Deselect and wake up anyone waiting on the device */ -	nand_release_device(mtd); - -	return ret; +	default: +		BUG(); +	} +	return NULL;  } +#define NOTALIGNED(x)	(x & (chip->subpagesize - 1)) != 0  /** - * nand_write_oob - [MTD Interface] NAND write out-of-band + * nand_do_write_ops - [Internal] NAND write with ECC   * @mtd:	MTD device structure   * @to:		offset to write to - * @len:	number of bytes to write - * @retlen:	pointer to variable to store the number of written bytes - * @buf:	the data to write + * @ops:	oob operations description structure   * - * NAND write out-of-band + * NAND write with ECC   */ -static int nand_write_oob (struct mtd_info *mtd, loff_t to, size_t len, size_t * retlen, const u_char * buf) +static int nand_do_write_ops(struct mtd_info *mtd, loff_t to, +			     struct mtd_oob_ops *ops)  { -	int column, page, status, ret = -EIO, chipnr; -	struct nand_chip *this = mtd->priv; +	int chipnr, realpage, page, blockmask, column; +	struct nand_chip *chip = mtd->priv; +	uint32_t writelen = ops->len; +	uint8_t *oob = ops->oobbuf; +	uint8_t *buf = ops->datbuf; +	int ret, subpage; -	MTDDEBUG (MTD_DEBUG_LEVEL3, "nand_write_oob: to = 0x%08x, len = %i\n", -	          (unsigned int) to, (int) len); - -	/* Shift to get page */ -	page = (int) (to >> this->page_shift); -	chipnr = (int) (to >> this->chip_shift); +	ops->retlen = 0; +	if (!writelen) +		return 0; -	/* Mask to get column */ -	column = to & (mtd->oobsize - 1); - -	/* Initialize return length value */ -	*retlen = 0; - -	/* Do not allow write past end of page */ -	if ((column + len) > mtd->oobsize) { -		MTDDEBUG (MTD_DEBUG_LEVEL0, "nand_write_oob: " -		          "Attempt to write past end of page\n"); +	/* reject writes, which are not page aligned */ +	if (NOTALIGNED(to) || NOTALIGNED(ops->len)) { +		printk(KERN_NOTICE "nand_write: " +		       "Attempt to write not page aligned data\n");  		return -EINVAL;  	} -	/* Grab the lock and see if the device is available */ -	nand_get_device (this, mtd, FL_WRITING); +	column = to & (mtd->writesize - 1); +	subpage = column || (writelen & (mtd->writesize - 1)); -	/* Select the NAND device */ -	this->select_chip(mtd, chipnr); +	if (subpage && oob) +		return -EINVAL; -	/* Reset the chip. Some chips (like the Toshiba TC5832DC found -	   in one of my DiskOnChip 2000 test units) will clear the whole -	   data page too if we don't do this. I have no clue why, but -	   I seem to have 'fixed' it in the doc2000 driver in -	   August 1999.  dwmw2. */ -	this->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); +	chipnr = (int)(to >> chip->chip_shift); +	chip->select_chip(mtd, chipnr);  	/* Check, if it is write protected */ -	if (nand_check_wp(mtd)) -		goto out; +	if (nand_check_wp(mtd)) { +		printk (KERN_NOTICE "nand_do_write_ops: Device is write protected\n"); +		return -EIO; +	} -	/* Invalidate the page cache, if we write to the cached page */ -	if (page == this->pagebuf) -		this->pagebuf = -1; +	realpage = (int)(to >> chip->page_shift); +	page = realpage & chip->pagemask; +	blockmask = (1 << (chip->phys_erase_shift - chip->page_shift)) - 1; -	if (NAND_MUST_PAD(this)) { -		/* Write out desired data */ -		this->cmdfunc (mtd, NAND_CMD_SEQIN, mtd->oobblock, page & this->pagemask); -		if (!ffchars) { -			if (!(ffchars = kmalloc (mtd->oobsize, GFP_KERNEL))) { -				MTDDEBUG (MTD_DEBUG_LEVEL0, "nand_write_oob: " -				          "No memory for padding array, " -				          "need %d bytes", mtd->oobsize); -				ret = -ENOMEM; -				goto out; -			} -			memset(ffchars, 0xff, mtd->oobsize); +	/* Invalidate the page cache, when we write to the cached page */ +	if (to <= (chip->pagebuf << chip->page_shift) && +	    (chip->pagebuf << chip->page_shift) < (to + ops->len)) +		chip->pagebuf = -1; + +	/* If we're not given explicit OOB data, let it be 0xFF */ +	if (likely(!oob)) +		memset(chip->oob_poi, 0xff, mtd->oobsize); + +	while(1) { +		int bytes = mtd->writesize; +		int cached = writelen > bytes && page != blockmask; +		uint8_t *wbuf = buf; + +		/* Partial page write ? */ +		if (unlikely(column || writelen < (mtd->writesize - 1))) { +			cached = 0; +			bytes = min_t(int, bytes - column, (int) writelen); +			chip->pagebuf = -1; +			memset(chip->buffers->databuf, 0xff, mtd->writesize); +			memcpy(&chip->buffers->databuf[column], buf, bytes); +			wbuf = chip->buffers->databuf;  		} -		/* prepad 0xff for partial programming */ -		this->write_buf(mtd, ffchars, column); -		/* write data */ -		this->write_buf(mtd, buf, len); -		/* postpad 0xff for partial programming */ -		this->write_buf(mtd, ffchars, mtd->oobsize - (len+column)); -	} else { -		/* Write out desired data */ -		this->cmdfunc (mtd, NAND_CMD_SEQIN, mtd->oobblock + column, page & this->pagemask); -		/* write data */ -		this->write_buf(mtd, buf, len); -	} -	/* Send command to program the OOB data */ -	this->cmdfunc (mtd, NAND_CMD_PAGEPROG, -1, -1); -	status = this->waitfunc (mtd, this, FL_WRITING); +		if (unlikely(oob)) +			oob = nand_fill_oob(chip, oob, ops); -	/* See if device thinks it succeeded */ -	if (status & 0x01) { -		MTDDEBUG (MTD_DEBUG_LEVEL0, "nand_write_oob: " -		          "Failed write, page 0x%08x\n", page); -		ret = -EIO; -		goto out; -	} -	/* Return happy */ -	*retlen = len; +		ret = chip->write_page(mtd, chip, wbuf, page, cached, +				       (ops->mode == MTD_OOB_RAW)); +		if (ret) +			break; -#ifdef CONFIG_MTD_NAND_VERIFY_WRITE -	/* Send command to read back the data */ -	this->cmdfunc (mtd, NAND_CMD_READOOB, column, page & this->pagemask); +		writelen -= bytes; +		if (!writelen) +			break; -	if (this->verify_buf(mtd, buf, len)) { -		MTDDEBUG (MTD_DEBUG_LEVEL0, "nand_write_oob: " -		          "Failed write verify, page 0x%08x\n", page); -		ret = -EIO; -		goto out; +		column = 0; +		buf += bytes; +		realpage++; + +		page = realpage & chip->pagemask; +		/* Check, if we cross a chip boundary */ +		if (!page) { +			chipnr++; +			chip->select_chip(mtd, -1); +			chip->select_chip(mtd, chipnr); +		}  	} -#endif -	ret = 0; -out: -	/* Deselect and wake up anyone waiting on the device */ -	nand_release_device(mtd); +	ops->retlen = ops->len - writelen; +	if (unlikely(oob)) +		ops->oobretlen = ops->ooblen;  	return ret;  } -/* XXX U-BOOT XXX */ -#if 0  /** - * nand_writev - [MTD Interface] compabilty function for nand_writev_ecc + * nand_write - [MTD Interface] NAND write with ECC   * @mtd:	MTD device structure - * @vecs:	the iovectors to write - * @count:	number of vectors   * @to:		offset to write to + * @len:	number of bytes to write   * @retlen:	pointer to variable to store the number of written bytes + * @buf:	the data to write   * - * NAND write with kvec. This just calls the ecc function + * NAND write with ECC   */ -static int nand_writev (struct mtd_info *mtd, const struct kvec *vecs, unsigned long count, -		loff_t to, size_t * retlen) +static int nand_write(struct mtd_info *mtd, loff_t to, size_t len, +			  size_t *retlen, const uint8_t *buf)  { -	return (nand_writev_ecc (mtd, vecs, count, to, retlen, NULL, NULL)); +	struct nand_chip *chip = mtd->priv; +	int ret; + +	/* Do not allow reads past end of device */ +	if ((to + len) > mtd->size) +		return -EINVAL; +	if (!len) +		return 0; + +	nand_get_device(chip, mtd, FL_WRITING); + +	chip->ops.len = len; +	chip->ops.datbuf = (uint8_t *)buf; +	chip->ops.oobbuf = NULL; + +	ret = nand_do_write_ops(mtd, to, &chip->ops); + +	*retlen = chip->ops.retlen; + +	nand_release_device(mtd); + +	return ret;  }  /** - * nand_writev_ecc - [MTD Interface] write with iovec with ecc + * nand_do_write_oob - [MTD Interface] NAND write out-of-band   * @mtd:	MTD device structure - * @vecs:	the iovectors to write - * @count:	number of vectors   * @to:		offset to write to - * @retlen:	pointer to variable to store the number of written bytes - * @eccbuf:	filesystem supplied oob data buffer - * @oobsel:	oob selection structure + * @ops:	oob operation description structure   * - * NAND write with iovec with ecc + * NAND write out-of-band   */ -static int nand_writev_ecc (struct mtd_info *mtd, const struct kvec *vecs, unsigned long count, -		loff_t to, size_t * retlen, u_char *eccbuf, struct nand_oobinfo *oobsel) +static int nand_do_write_oob(struct mtd_info *mtd, loff_t to, +			     struct mtd_oob_ops *ops)  { -	int i, page, len, total_len, ret = -EIO, written = 0, chipnr; -	int oob, numpages, autoplace = 0, startpage; -	struct nand_chip *this = mtd->priv; -	int	ppblock = (1 << (this->phys_erase_shift - this->page_shift)); -	u_char *oobbuf, *bufstart; - -	/* Preset written len for early exit */ -	*retlen = 0; +	int chipnr, page, status, len; +	struct nand_chip *chip = mtd->priv; -	/* Calculate total length of data */ -	total_len = 0; -	for (i = 0; i < count; i++) -		total_len += (int) vecs[i].iov_len; +	MTDDEBUG (MTD_DEBUG_LEVEL3, "nand_write_oob: to = 0x%08x, len = %i\n", +	          (unsigned int)to, (int)ops->ooblen); -	MTDDEBUG (MTD_DEBUG_LEVEL3, -	          "nand_writev: to = 0x%08x, len = %i, count = %ld\n", -	          (unsigned int) to, (unsigned int) total_len, count); +	if (ops->mode == MTD_OOB_AUTO) +		len = chip->ecc.layout->oobavail; +	else +		len = mtd->oobsize;  	/* Do not allow write past end of page */ -	if ((to + total_len) > mtd->size) { -		MTDDEBUG (MTD_DEBUG_LEVEL0, -		          "nand_writev: Attempted write past end of device\n"); +	if ((ops->ooboffs + ops->ooblen) > len) { +		MTDDEBUG (MTD_DEBUG_LEVEL0, "nand_write_oob: " +		          "Attempt to write past end of page\n");  		return -EINVAL;  	} -	/* reject writes, which are not page aligned */ -	if (NOTALIGNED (to) || NOTALIGNED(total_len)) { -		printk (KERN_NOTICE "nand_write_ecc: Attempt to write not page aligned data\n"); +	if (unlikely(ops->ooboffs >= len)) { +		MTDDEBUG (MTD_DEBUG_LEVEL0, "nand_read_oob: " +		          "Attempt to start write outside oob\n");  		return -EINVAL;  	} -	/* Grab the lock and see if the device is available */ -	nand_get_device (this, mtd, FL_WRITING); +	/* Do not allow reads past end of device */ +	if (unlikely(to >= mtd->size || +		     ops->ooboffs + ops->ooblen > +			((mtd->size >> chip->page_shift) - +			 (to >> chip->page_shift)) * len)) { +		MTDDEBUG (MTD_DEBUG_LEVEL0, "nand_read_oob: " +		          "Attempt write beyond end of device\n"); +		return -EINVAL; +	} -	/* Get the current chip-nr */ -	chipnr = (int) (to >> this->chip_shift); -	/* Select the NAND device */ -	this->select_chip(mtd, chipnr); +	chipnr = (int)(to >> chip->chip_shift); +	chip->select_chip(mtd, chipnr); + +	/* Shift to get page */ +	page = (int)(to >> chip->page_shift); + +	/* +	 * Reset the chip. Some chips (like the Toshiba TC5832DC found in one +	 * of my DiskOnChip 2000 test units) will clear the whole data page too +	 * if we don't do this. I have no clue why, but I seem to have 'fixed' +	 * it in the doc2000 driver in August 1999.  dwmw2. +	 */ +	chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);  	/* Check, if it is write protected */  	if (nand_check_wp(mtd)) -		goto out; +		return -EROFS; -	/* if oobsel is NULL, use chip defaults */ -	if (oobsel == NULL) -		oobsel = &mtd->oobinfo; +	/* Invalidate the page cache, if we write to the cached page */ +	if (page == chip->pagebuf) +		chip->pagebuf = -1; -	/* Autoplace of oob data ? Use the default placement scheme */ -	if (oobsel->useecc == MTD_NANDECC_AUTOPLACE) { -		oobsel = this->autooob; -		autoplace = 1; -	} -	if (oobsel->useecc == MTD_NANDECC_AUTOPL_USR) -		autoplace = 1; +	memset(chip->oob_poi, 0xff, mtd->oobsize); +	nand_fill_oob(chip, ops->oobbuf, ops); +	status = chip->ecc.write_oob(mtd, chip, page & chip->pagemask); +	memset(chip->oob_poi, 0xff, mtd->oobsize); -	/* Setup start page */ -	page = (int) (to >> this->page_shift); -	/* Invalidate the page cache, if we write to the cached page */ -	if (page <= this->pagebuf && this->pagebuf < ((to + total_len) >> this->page_shift)) -		this->pagebuf = -1; +	if (status) +		return status; -	startpage = page & this->pagemask; +	ops->oobretlen = ops->ooblen; -	/* Loop until all kvec' data has been written */ -	len = 0; -	while (count) { -		/* If the given tuple is >= pagesize then -		 * write it out from the iov -		 */ -		if ((vecs->iov_len - len) >= mtd->oobblock) { -			/* Calc number of pages we can write -			 * out of this iov in one go */ -			numpages = (vecs->iov_len - len) >> this->page_shift; -			/* Do not cross block boundaries */ -			numpages = min (ppblock - (startpage & (ppblock - 1)), numpages); -			oobbuf = nand_prepare_oobbuf (mtd, NULL, oobsel, autoplace, numpages); -			bufstart = (u_char *)vecs->iov_base; -			bufstart += len; -			this->data_poi = bufstart; -			oob = 0; -			for (i = 1; i <= numpages; i++) { -				/* Write one page. If this is the last page to write -				 * then use the real pageprogram command, else select -				 * cached programming if supported by the chip. -				 */ -				ret = nand_write_page (mtd, this, page & this->pagemask, -					&oobbuf[oob], oobsel, i != numpages); -				if (ret) -					goto out; -				this->data_poi += mtd->oobblock; -				len += mtd->oobblock; -				oob += mtd->oobsize; -				page++; -			} -			/* Check, if we have to switch to the next tuple */ -			if (len >= (int) vecs->iov_len) { -				vecs++; -				len = 0; -				count--; -			} -		} else { -			/* We must use the internal buffer, read data out of each -			 * tuple until we have a full page to write -			 */ -			int cnt = 0; -			while (cnt < mtd->oobblock) { -				if (vecs->iov_base != NULL && vecs->iov_len) -					this->data_buf[cnt++] = ((u_char *) vecs->iov_base)[len++]; -				/* Check, if we have to switch to the next tuple */ -				if (len >= (int) vecs->iov_len) { -					vecs++; -					len = 0; -					count--; -				} -			} -			this->pagebuf = page; -			this->data_poi = this->data_buf; -			bufstart = this->data_poi; -			numpages = 1; -			oobbuf = nand_prepare_oobbuf (mtd, NULL, oobsel, autoplace, numpages); -			ret = nand_write_page (mtd, this, page & this->pagemask, -				oobbuf, oobsel, 0); -			if (ret) -				goto out; -			page++; -		} +	return 0; +} -		this->data_poi = bufstart; -		ret = nand_verify_pages (mtd, this, startpage, numpages, oobbuf, oobsel, chipnr, 0); -		if (ret) -			goto out; +/** + * nand_write_oob - [MTD Interface] NAND write data and/or out-of-band + * @mtd:	MTD device structure + * @to:		offset to write to + * @ops:	oob operation description structure + */ +static int nand_write_oob(struct mtd_info *mtd, loff_t to, +			  struct mtd_oob_ops *ops) +{ +	struct nand_chip *chip = mtd->priv; +	int ret = -ENOTSUPP; -		written += mtd->oobblock * numpages; -		/* All done ? */ -		if (!count) -			break; +	ops->retlen = 0; -		startpage = page & this->pagemask; -		/* Check, if we cross a chip boundary */ -		if (!startpage) { -			chipnr++; -			this->select_chip(mtd, -1); -			this->select_chip(mtd, chipnr); -		} +	/* Do not allow writes past end of device */ +	if (ops->datbuf && (to + ops->len) > mtd->size) { +		MTDDEBUG (MTD_DEBUG_LEVEL0, "nand_read_oob: " +		          "Attempt read beyond end of device\n"); +		return -EINVAL;  	} -	ret = 0; -out: -	/* Deselect and wake up anyone waiting on the device */ -	nand_release_device(mtd); -	*retlen = written; +	nand_get_device(chip, mtd, FL_WRITING); + +	switch(ops->mode) { +	case MTD_OOB_PLACE: +	case MTD_OOB_AUTO: +	case MTD_OOB_RAW: +		break; + +	default: +		goto out; +	} + +	if (!ops->datbuf) +		ret = nand_do_write_oob(mtd, to, ops); +	else +		ret = nand_do_write_ops(mtd, to, ops); + + out: +	nand_release_device(mtd);  	return ret;  } -#endif  /**   * single_erease_cmd - [GENERIC] NAND standard block erase command function @@ -2094,12 +2005,12 @@ out:   *   * Standard erase command for NAND chips   */ -static void single_erase_cmd (struct mtd_info *mtd, int page) +static void single_erase_cmd(struct mtd_info *mtd, int page)  { -	struct nand_chip *this = mtd->priv; +	struct nand_chip *chip = mtd->priv;  	/* Send commands to erase a block */ -	this->cmdfunc (mtd, NAND_CMD_ERASE1, -1, page); -	this->cmdfunc (mtd, NAND_CMD_ERASE2, -1, -1); +	chip->cmdfunc(mtd, NAND_CMD_ERASE1, -1, page); +	chip->cmdfunc(mtd, NAND_CMD_ERASE2, -1, -1);  }  /** @@ -2110,15 +2021,15 @@ static void single_erase_cmd (struct mtd_info *mtd, int page)   * AND multi block erase command function   * Erase 4 consecutive blocks   */ -static void multi_erase_cmd (struct mtd_info *mtd, int page) +static void multi_erase_cmd(struct mtd_info *mtd, int page)  { -	struct nand_chip *this = mtd->priv; +	struct nand_chip *chip = mtd->priv;  	/* Send commands to erase a block */ -	this->cmdfunc (mtd, NAND_CMD_ERASE1, -1, page++); -	this->cmdfunc (mtd, NAND_CMD_ERASE1, -1, page++); -	this->cmdfunc (mtd, NAND_CMD_ERASE1, -1, page++); -	this->cmdfunc (mtd, NAND_CMD_ERASE1, -1, page); -	this->cmdfunc (mtd, NAND_CMD_ERASE2, -1, -1); +	chip->cmdfunc(mtd, NAND_CMD_ERASE1, -1, page++); +	chip->cmdfunc(mtd, NAND_CMD_ERASE1, -1, page++); +	chip->cmdfunc(mtd, NAND_CMD_ERASE1, -1, page++); +	chip->cmdfunc(mtd, NAND_CMD_ERASE1, -1, page); +	chip->cmdfunc(mtd, NAND_CMD_ERASE2, -1, -1);  }  /** @@ -2128,35 +2039,39 @@ static void multi_erase_cmd (struct mtd_info *mtd, int page)   *   * Erase one ore more blocks   */ -static int nand_erase (struct mtd_info *mtd, struct erase_info *instr) +static int nand_erase(struct mtd_info *mtd, struct erase_info *instr)  { -	return nand_erase_nand (mtd, instr, 0); +	return nand_erase_nand(mtd, instr, 0);  } +#define BBT_PAGE_MASK	0xffffff3f  /** - * nand_erase_intern - [NAND Interface] erase block(s) + * nand_erase_nand - [Internal] erase block(s)   * @mtd:	MTD device structure   * @instr:	erase instruction   * @allowbbt:	allow erasing the bbt area   *   * Erase one ore more blocks   */ -int nand_erase_nand (struct mtd_info *mtd, struct erase_info *instr, int allowbbt) +int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr, +		    int allowbbt)  {  	int page, len, status, pages_per_block, ret, chipnr; -	struct nand_chip *this = mtd->priv; +	struct nand_chip *chip = mtd->priv; +	int rewrite_bbt[NAND_MAX_CHIPS]={0}; +	unsigned int bbt_masked_page = 0xffffffff;  	MTDDEBUG (MTD_DEBUG_LEVEL3, "nand_erase: start = 0x%08x, len = %i\n",  	          (unsigned int) instr->addr, (unsigned int) instr->len);  	/* Start address must align on block boundary */ -	if (instr->addr & ((1 << this->phys_erase_shift) - 1)) { +	if (instr->addr & ((1 << chip->phys_erase_shift) - 1)) {  		MTDDEBUG (MTD_DEBUG_LEVEL0, "nand_erase: Unaligned address\n");  		return -EINVAL;  	}  	/* Length must align on block boundary */ -	if (instr->len & ((1 << this->phys_erase_shift) - 1)) { +	if (instr->len & ((1 << chip->phys_erase_shift) - 1)) {  		MTDDEBUG (MTD_DEBUG_LEVEL0,  		          "nand_erase: Length not block aligned\n");  		return -EINVAL; @@ -2172,19 +2087,18 @@ int nand_erase_nand (struct mtd_info *mtd, struct erase_info *instr, int allowbb  	instr->fail_addr = 0xffffffff;  	/* Grab the lock and see if the device is available */ -	nand_get_device (this, mtd, FL_ERASING); +	nand_get_device(chip, mtd, FL_ERASING);  	/* Shift to get first page */ -	page = (int) (instr->addr >> this->page_shift); -	chipnr = (int) (instr->addr >> this->chip_shift); +	page = (int)(instr->addr >> chip->page_shift); +	chipnr = (int)(instr->addr >> chip->chip_shift);  	/* Calculate pages in each block */ -	pages_per_block = 1 << (this->phys_erase_shift - this->page_shift); +	pages_per_block = 1 << (chip->phys_erase_shift - chip->page_shift);  	/* Select the NAND device */ -	this->select_chip(mtd, chipnr); +	chip->select_chip(mtd, chipnr); -	/* Check the WP bit */  	/* Check, if it is write protected */  	if (nand_check_wp(mtd)) {  		MTDDEBUG (MTD_DEBUG_LEVEL0, @@ -2193,52 +2107,92 @@ 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. Otherwise the mask is set to 0xffffffff which +	 * can not be matched. This is also done when the bbt is actually +	 * erased to avoid recusrsive updates +	 */ +	if (chip->options & BBT_AUTO_REFRESH && !allowbbt) +		bbt_masked_page = chip->bbt_td->pages[chipnr] & BBT_PAGE_MASK; +  	/* Loop through the pages */  	len = instr->len;  	instr->state = MTD_ERASING;  	while (len) { -#ifndef NAND_ALLOW_ERASE_ALL -		/* Check if we have a bad block, we do not erase bad blocks ! */ -		if (nand_block_checkbad(mtd, ((loff_t) page) << this->page_shift, 0, allowbbt)) { -			printk (KERN_WARNING "nand_erase: attempt to erase a bad block at page 0x%08x\n", page); +		/* +		 * heck if we have a bad block, we do not erase bad blocks ! +		 */ +		if (nand_block_checkbad(mtd, ((loff_t) page) << +					chip->page_shift, 0, allowbbt)) { +			printk(KERN_WARNING "nand_erase: attempt to erase a " +			       "bad block at page 0x%08x\n", page);  			instr->state = MTD_ERASE_FAILED;  			goto erase_exit;  		} -#endif -		/* Invalidate the page cache, if we erase the block which contains -		   the current cached page */ -		if (page <= this->pagebuf && this->pagebuf < (page + pages_per_block)) -			this->pagebuf = -1; -		this->erase_cmd (mtd, page & this->pagemask); +		/* +		 * Invalidate the page cache, if we erase the block which +		 * contains the current cached page +		 */ +		if (page <= chip->pagebuf && chip->pagebuf < +		    (page + pages_per_block)) +			chip->pagebuf = -1; + +		chip->erase_cmd(mtd, page & chip->pagemask); -		status = this->waitfunc (mtd, this, FL_ERASING); +		status = chip->waitfunc(mtd, chip); + +		/* +		 * See if operation failed and additional status checks are +		 * available +		 */ +		if ((status & NAND_STATUS_FAIL) && (chip->errstat)) +			status = chip->errstat(mtd, chip, FL_ERASING, +					       status, page);  		/* See if block erase succeeded */ -		if (status & 0x01) { +		if (status & NAND_STATUS_FAIL) {  			MTDDEBUG (MTD_DEBUG_LEVEL0, "nand_erase: "  			          "Failed erase, page 0x%08x\n", page);  			instr->state = MTD_ERASE_FAILED; -			instr->fail_addr = (page << this->page_shift); +			instr->fail_addr = (page << chip->page_shift);  			goto erase_exit;  		} +		/* +		 * If BBT requires refresh, set the BBT rewrite flag to the +		 * page being erased +		 */ +		if (bbt_masked_page != 0xffffffff && +		    (page & BBT_PAGE_MASK) == bbt_masked_page) +			    rewrite_bbt[chipnr] = (page << chip->page_shift); +  		/* Increment page address and decrement length */ -		len -= (1 << this->phys_erase_shift); +		len -= (1 << chip->phys_erase_shift);  		page += pages_per_block;  		/* Check, if we cross a chip boundary */ -		if (len && !(page & this->pagemask)) { +		if (len && !(page & chip->pagemask)) {  			chipnr++; -			this->select_chip(mtd, -1); -			this->select_chip(mtd, chipnr); +			chip->select_chip(mtd, -1); +			chip->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 (bbt_masked_page != 0xffffffff && +			    (chip->bbt_td->options & NAND_BBT_PERCHIP)) +				bbt_masked_page = chip->bbt_td->pages[chipnr] & +					BBT_PAGE_MASK;  		}  	}  	instr->state = MTD_ERASE_DONE; -erase_exit: + erase_exit:  	ret = instr->state == MTD_ERASE_DONE ? 0 : -EIO;  	/* Do call back function */ @@ -2248,6 +2202,23 @@ 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 (bbt_masked_page == 0xffffffff || ret) +		return ret; + +	for (chipnr = 0; chipnr < chip->numchips; chipnr++) { +		if (!rewrite_bbt[chipnr]) +			continue; +		/* update the BBT for chip */ +		MTDDEBUG (MTD_DEBUG_LEVEL0, "nand_erase_nand: nand_update_bbt " +		          "(%d:0x%0x 0x%0x)\n", chipnr, rewrite_bbt[chipnr], +		          chip->bbt_td->pages[chipnr]); +		nand_update_bbt(mtd, rewrite_bbt[chipnr]); +	} +  	/* Return more or less happy */  	return ret;  } @@ -2258,41 +2229,40 @@ erase_exit:   *   * Sync is actually a wait for chip ready function   */ -static void nand_sync (struct mtd_info *mtd) +static void nand_sync(struct mtd_info *mtd)  { -	struct nand_chip *this = mtd->priv; +	struct nand_chip *chip = mtd->priv;  	MTDDEBUG (MTD_DEBUG_LEVEL3, "nand_sync: called\n");  	/* Grab the lock and see if the device is available */ -	nand_get_device (this, mtd, FL_SYNCING); +	nand_get_device(chip, mtd, FL_SYNCING);  	/* Release it and go back */ -	nand_release_device (mtd); +	nand_release_device(mtd);  } -  /** - * nand_block_isbad - [MTD Interface] Check whether the block at the given offset is bad + * nand_block_isbad - [MTD Interface] Check if block at offset is bad   * @mtd:	MTD device structure - * @ofs:	offset relative to mtd start + * @offs:	offset relative to mtd start   */ -static int nand_block_isbad (struct mtd_info *mtd, loff_t ofs) +static int nand_block_isbad(struct mtd_info *mtd, loff_t offs)  {  	/* Check for invalid offset */ -	if (ofs > mtd->size) +	if (offs > mtd->size)  		return -EINVAL; -	return nand_block_checkbad (mtd, ofs, 1, 0); +	return nand_block_checkbad(mtd, offs, 1, 0);  }  /** - * nand_block_markbad - [MTD Interface] Mark the block at the given offset as bad + * nand_block_markbad - [MTD Interface] Mark block at the given offset as bad   * @mtd:	MTD device structure   * @ofs:	offset relative to mtd start   */ -static int nand_block_markbad (struct mtd_info *mtd, loff_t ofs) +static int nand_block_markbad(struct mtd_info *mtd, loff_t ofs)  { -	struct nand_chip *this = mtd->priv; +	struct nand_chip *chip = mtd->priv;  	int ret;  	if ((ret = nand_block_isbad(mtd, ofs))) { @@ -2302,419 +2272,556 @@ static int nand_block_markbad (struct mtd_info *mtd, loff_t ofs)  		return ret;  	} -	return this->block_markbad(mtd, ofs); +	return chip->block_markbad(mtd, ofs);  }  /** - * nand_scan - [NAND Interface] Scan for the NAND device + * nand_suspend - [MTD Interface] Suspend the NAND flash   * @mtd:	MTD device structure - * @maxchips:	Number of chips to scan for - * - * This fills out all the not initialized function pointers - * with the defaults. - * The flash ID is read and the mtd/chip structures are - * filled with the appropriate values. Buffers are allocated if - * they are not provided by the board driver - *   */ -int nand_scan (struct mtd_info *mtd, int maxchips) +static int nand_suspend(struct mtd_info *mtd)  { -	int i, j, nand_maf_id, nand_dev_id, busw; -	struct nand_chip *this = mtd->priv; +	struct nand_chip *chip = mtd->priv; -	/* Get buswidth to select the correct functions*/ -	busw = this->options & NAND_BUSWIDTH_16; +	return nand_get_device(chip, mtd, FL_PM_SUSPENDED); +} + +/** + * nand_resume - [MTD Interface] Resume the NAND flash + * @mtd:	MTD device structure + */ +static void nand_resume(struct mtd_info *mtd) +{ +	struct nand_chip *chip = mtd->priv; + +	if (chip->state == FL_PM_SUSPENDED) +		nand_release_device(mtd); +	else +		printk(KERN_ERR "nand_resume() called for a chip which is not " +		       "in suspended state\n"); +} +/* + * Set default functions + */ +static void nand_set_defaults(struct nand_chip *chip, int busw) +{  	/* check for proper chip_delay setup, set 20us if not */ -	if (!this->chip_delay) -		this->chip_delay = 20; +	if (!chip->chip_delay) +		chip->chip_delay = 20;  	/* check, if a user supplied command function given */ -	if (this->cmdfunc == NULL) -		this->cmdfunc = nand_command; +	if (chip->cmdfunc == NULL) +		chip->cmdfunc = nand_command;  	/* check, if a user supplied wait function given */ -	if (this->waitfunc == NULL) -		this->waitfunc = nand_wait; +	if (chip->waitfunc == NULL) +		chip->waitfunc = nand_wait; + +	if (!chip->select_chip) +		chip->select_chip = nand_select_chip; +	if (!chip->read_byte) +		chip->read_byte = busw ? nand_read_byte16 : nand_read_byte; +	if (!chip->read_word) +		chip->read_word = nand_read_word; +	if (!chip->block_bad) +		chip->block_bad = nand_block_bad; +	if (!chip->block_markbad) +		chip->block_markbad = nand_default_block_markbad; +	if (!chip->write_buf) +		chip->write_buf = busw ? nand_write_buf16 : nand_write_buf; +	if (!chip->read_buf) +		chip->read_buf = busw ? nand_read_buf16 : nand_read_buf; +	if (!chip->verify_buf) +		chip->verify_buf = busw ? nand_verify_buf16 : nand_verify_buf; +	if (!chip->scan_bbt) +		chip->scan_bbt = nand_default_bbt; -	if (!this->select_chip) -		this->select_chip = nand_select_chip; -	if (!this->write_byte) -		this->write_byte = busw ? nand_write_byte16 : nand_write_byte; -	if (!this->read_byte) -		this->read_byte = busw ? nand_read_byte16 : nand_read_byte; -	if (!this->write_word) -		this->write_word = nand_write_word; -	if (!this->read_word) -		this->read_word = nand_read_word; -	if (!this->block_bad) -		this->block_bad = nand_block_bad; -	if (!this->block_markbad) -		this->block_markbad = nand_default_block_markbad; -	if (!this->write_buf) -		this->write_buf = busw ? nand_write_buf16 : nand_write_buf; -	if (!this->read_buf) -		this->read_buf = busw ? nand_read_buf16 : nand_read_buf; -	if (!this->verify_buf) -		this->verify_buf = busw ? nand_verify_buf16 : nand_verify_buf; -	if (!this->scan_bbt) -		this->scan_bbt = nand_default_bbt; +	if (!chip->controller) { +		chip->controller = &chip->hwcontrol; + +		/* XXX U-BOOT XXX */ +#if 0 +		spin_lock_init(&chip->controller->lock); +		init_waitqueue_head(&chip->controller->wq); +#endif +	} + +} + +/* + * Get the flash and manufacturer id and lookup if the type is supported + */ +static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd, +						  struct nand_chip *chip, +						  int busw, int *maf_id) +{ +	struct nand_flash_dev *type = NULL; +	int i, dev_id, maf_idx;  	/* Select the device */ -	this->select_chip(mtd, 0); +	chip->select_chip(mtd, 0);  	/* Send the command for reading device ID */ -	this->cmdfunc (mtd, NAND_CMD_READID, 0x00, -1); +	chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);  	/* Read manufacturer and device IDs */ -	nand_maf_id = this->read_byte(mtd); -	nand_dev_id = this->read_byte(mtd); +	*maf_id = chip->read_byte(mtd); +	dev_id = chip->read_byte(mtd); -	/* Print and store flash device information */ +	/* Lookup the flash id */  	for (i = 0; nand_flash_ids[i].name != NULL; i++) { +		if (dev_id == nand_flash_ids[i].id) { +			type =  &nand_flash_ids[i]; +			break; +		} +	} -		if (nand_dev_id != nand_flash_ids[i].id) -			continue; +	if (!type) +		return ERR_PTR(-ENODEV); -		if (!mtd->name) mtd->name = nand_flash_ids[i].name; -		this->chipsize = nand_flash_ids[i].chipsize << 20; +	if (!mtd->name) +		mtd->name = type->name; -		/* New devices have all the information in additional id bytes */ -		if (!nand_flash_ids[i].pagesize) { -			int extid; -			/* The 3rd id byte contains non relevant data ATM */ -			extid = this->read_byte(mtd); -			/* The 4th id byte is the important one */ -			extid = this->read_byte(mtd); -			/* Calc pagesize */ -			mtd->oobblock = 1024 << (extid & 0x3); -			extid >>= 2; -			/* Calc oobsize */ -			mtd->oobsize = (8 << (extid & 0x01)) * (mtd->oobblock / 512); -			extid >>= 2; -			/* Calc blocksize. Blocksize is multiples of 64KiB */ -			mtd->erasesize = (64 * 1024)  << (extid & 0x03); -			extid >>= 2; -			/* Get buswidth information */ -			busw = (extid & 0x01) ? NAND_BUSWIDTH_16 : 0; +	chip->chipsize = type->chipsize << 20; -		} else { -			/* Old devices have this data hardcoded in the -			 * device id table */ -			mtd->erasesize = nand_flash_ids[i].erasesize; -			mtd->oobblock = nand_flash_ids[i].pagesize; -			mtd->oobsize = mtd->oobblock / 32; -			busw = nand_flash_ids[i].options & NAND_BUSWIDTH_16; -		} +	/* Newer devices have all the information in additional id bytes */ +	if (!type->pagesize) { +		int extid; +		/* The 3rd id byte holds MLC / multichip data */ +		chip->cellinfo = chip->read_byte(mtd); +		/* The 4th id byte is the important one */ +		extid = chip->read_byte(mtd); +		/* Calc pagesize */ +		mtd->writesize = 1024 << (extid & 0x3); +		extid >>= 2; +		/* Calc oobsize */ +		mtd->oobsize = (8 << (extid & 0x01)) * (mtd->writesize >> 9); +		extid >>= 2; +		/* Calc blocksize. Blocksize is multiples of 64KiB */ +		mtd->erasesize = (64 * 1024) << (extid & 0x03); +		extid >>= 2; +		/* Get buswidth information */ +		busw = (extid & 0x01) ? NAND_BUSWIDTH_16 : 0; -		/* 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); -			printk (KERN_WARNING -				"NAND bus width %d instead %d bit\n", -					(this->options & NAND_BUSWIDTH_16) ? 16 : 8, -					busw ? 16 : 8); -			this->select_chip(mtd, -1); -			return 1; -		} +	} else { +		/* +		 * Old devices have chip data hardcoded in the device id table +		 */ +		mtd->erasesize = type->erasesize; +		mtd->writesize = type->pagesize; +		mtd->oobsize = mtd->writesize / 32; +		busw = type->options & NAND_BUSWIDTH_16; +	} -		/* Calculate the address shift from the page size */ -		this->page_shift = ffs(mtd->oobblock) - 1; -		this->bbt_erase_shift = this->phys_erase_shift = ffs(mtd->erasesize) - 1; -		this->chip_shift = ffs(this->chipsize) - 1; +	/* Try to identify manufacturer */ +	for (maf_idx = 0; nand_manuf_ids[maf_idx].id != 0x0; maf_idx++) { +		if (nand_manuf_ids[maf_idx].id == *maf_id) +			break; +	} -		/* Set the bad block position */ -		this->badblockpos = mtd->oobblock > 512 ? -			NAND_LARGE_BADBLOCK_POS : NAND_SMALL_BADBLOCK_POS; +	/* +	 * Check, if buswidth is correct. Hardware drivers should set +	 * chip correct ! +	 */ +	if (busw != (chip->options & NAND_BUSWIDTH_16)) { +		printk(KERN_INFO "NAND device: Manufacturer ID:" +		       " 0x%02x, Chip ID: 0x%02x (%s %s)\n", *maf_id, +		       dev_id, nand_manuf_ids[maf_idx].name, mtd->name); +		printk(KERN_WARNING "NAND bus width %d instead %d bit\n", +		       (chip->options & NAND_BUSWIDTH_16) ? 16 : 8, +		       busw ? 16 : 8); +		return ERR_PTR(-EINVAL); +	} -		/* Get chip options, preserve non chip based options */ -		this->options &= ~NAND_CHIPOPTIONS_MSK; -		this->options |= nand_flash_ids[i].options & NAND_CHIPOPTIONS_MSK; -		/* Set this as a default. Board drivers can override it, if neccecary */ -		this->options |= NAND_NO_AUTOINCR; -		/* Check if this is a not a samsung device. Do not clear the options -		 * for chips which are not having an extended id. -		 */ -		if (nand_maf_id != NAND_MFR_SAMSUNG && !nand_flash_ids[i].pagesize) -			this->options &= ~NAND_SAMSUNG_LP_OPTIONS; +	/* Calculate the address shift from the page size */ +	chip->page_shift = ffs(mtd->writesize) - 1; +	/* Convert chipsize to number of pages per chip -1. */ +	chip->pagemask = (chip->chipsize >> chip->page_shift) - 1; -		/* Check for AND chips with 4 page planes */ -		if (this->options & NAND_4PAGE_ARRAY) -			this->erase_cmd = multi_erase_cmd; -		else -			this->erase_cmd = single_erase_cmd; +	chip->bbt_erase_shift = chip->phys_erase_shift = +		ffs(mtd->erasesize) - 1; +	chip->chip_shift = ffs(chip->chipsize) - 1; -		/* Do not replace user supplied command function ! */ -		if (mtd->oobblock > 512 && this->cmdfunc == nand_command) -			this->cmdfunc = nand_command_lp; +	/* Set the bad block position */ +	chip->badblockpos = mtd->writesize > 512 ? +		NAND_LARGE_BADBLOCK_POS : NAND_SMALL_BADBLOCK_POS; -		/* Try to identify manufacturer */ -		for (j = 0; nand_manuf_ids[j].id != 0x0; j++) { -			if (nand_manuf_ids[j].id == nand_maf_id) -				break; -		} -		break; -	} +	/* Get chip options, preserve non chip based options */ +	chip->options &= ~NAND_CHIPOPTIONS_MSK; +	chip->options |= type->options & NAND_CHIPOPTIONS_MSK; -	if (!nand_flash_ids[i].name) { -#ifndef CFG_NAND_QUIET_TEST -		printk (KERN_WARNING "No NAND device found!!!\n"); -#endif -		this->select_chip(mtd, -1); -		return 1; -	} +	/* +	 * Set chip as a default. Board drivers can override it, if necessary +	 */ +	chip->options |= NAND_NO_AUTOINCR; -	for (i=1; i < maxchips; i++) { -		this->select_chip(mtd, i); +	/* Check if chip is a not a samsung device. Do not clear the +	 * options for chips which are not having an extended id. +	 */ +	if (*maf_id != NAND_MFR_SAMSUNG && !type->pagesize) +		chip->options &= ~NAND_SAMSUNG_LP_OPTIONS; -		/* Send the command for reading device ID */ -		this->cmdfunc (mtd, NAND_CMD_READID, 0x00, -1); +	/* Check for AND chips with 4 page planes */ +	if (chip->options & NAND_4PAGE_ARRAY) +		chip->erase_cmd = multi_erase_cmd; +	else +		chip->erase_cmd = single_erase_cmd; + +	/* Do not replace user supplied command function ! */ +	if (mtd->writesize > 512 && chip->cmdfunc == nand_command) +		chip->cmdfunc = nand_command_lp; + +	MTDDEBUG (MTD_DEBUG_LEVEL0, "NAND device: Manufacturer ID:" +	          " 0x%02x, Chip ID: 0x%02x (%s %s)\n", *maf_id, dev_id, +	          nand_manuf_ids[maf_idx].name, type->name); +	return type; +} + +/** + * nand_scan_ident - [NAND Interface] Scan for the NAND device + * @mtd:	     MTD device structure + * @maxchips:	     Number of chips to scan for + * + * This is the first phase of the normal nand_scan() function. It + * reads the flash ID and sets up MTD fields accordingly. + * + * The mtd->owner field must be set to the module of the caller. + */ +int nand_scan_ident(struct mtd_info *mtd, int maxchips) +{ +	int i, busw, nand_maf_id; +	struct nand_chip *chip = mtd->priv; +	struct nand_flash_dev *type; + +	/* Get buswidth to select the correct functions */ +	busw = chip->options & NAND_BUSWIDTH_16; +	/* Set the default functions */ +	nand_set_defaults(chip, busw); + +	/* Read the flash type */ +	type = nand_get_flash_type(mtd, chip, busw, &nand_maf_id); + +	if (IS_ERR(type)) { +		printk(KERN_WARNING "No NAND device found!!!\n"); +		chip->select_chip(mtd, -1); +		return PTR_ERR(type); +	} + +	/* Check for a chip array */ +	for (i = 1; i < maxchips; i++) { +		chip->select_chip(mtd, i); +		/* Send the command for reading device ID */ +		chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);  		/* Read manufacturer and device IDs */ -		if (nand_maf_id != this->read_byte(mtd) || -		    nand_dev_id != this->read_byte(mtd)) +		if (nand_maf_id != chip->read_byte(mtd) || +		    type->id != chip->read_byte(mtd))  			break;  	}  	if (i > 1)  		printk(KERN_INFO "%d NAND chips detected\n", i); -	/* Allocate buffers, if neccecary */ -	if (!this->oob_buf) { -		size_t len; -		len = mtd->oobsize << (this->phys_erase_shift - this->page_shift); -		this->oob_buf = kmalloc (len, GFP_KERNEL); -		if (!this->oob_buf) { -			printk (KERN_ERR "nand_scan(): Cannot allocate oob_buf\n"); -			return -ENOMEM; -		} -		this->options |= NAND_OOBBUF_ALLOC; -	} +	/* Store the number of chips and calc total size for mtd */ +	chip->numchips = i; +	mtd->size = i * chip->chipsize; -	if (!this->data_buf) { -		size_t len; -		len = mtd->oobblock + mtd->oobsize; -		this->data_buf = kmalloc (len, GFP_KERNEL); -		if (!this->data_buf) { -			if (this->options & NAND_OOBBUF_ALLOC) -				kfree (this->oob_buf); -			printk (KERN_ERR "nand_scan(): Cannot allocate data_buf\n"); -			return -ENOMEM; -		} -		this->options |= NAND_DATABUF_ALLOC; -	} +	return 0; +} -	/* Store the number of chips and calc total size for mtd */ -	this->numchips = i; -	mtd->size = i * this->chipsize; -	/* Convert chipsize to number of pages per chip -1. */ -	this->pagemask = (this->chipsize >> this->page_shift) - 1; -	/* Preset the internal oob buffer */ -	memset(this->oob_buf, 0xff, mtd->oobsize << (this->phys_erase_shift - this->page_shift)); -	/* If no default placement scheme is given, select an -	 * appropriate one */ -	if (!this->autooob) { -		/* Select the appropriate default oob placement scheme for -		 * placement agnostic filesystems */ +/** + * nand_scan_tail - [NAND Interface] Scan for the NAND device + * @mtd:	    MTD device structure + * @maxchips:	    Number of chips to scan for + * + * This is the second phase of the normal nand_scan() function. It + * fills out all the uninitialized function pointers with the defaults + * and scans for a bad block table if appropriate. + */ +int nand_scan_tail(struct mtd_info *mtd) +{ +	int i; +	struct nand_chip *chip = mtd->priv; + +	if (!(chip->options & NAND_OWN_BUFFERS)) +		chip->buffers = kmalloc(sizeof(*chip->buffers), GFP_KERNEL); +	if (!chip->buffers) +		return -ENOMEM; + +	/* Set the internal oob buffer location, just after the page data */ +	chip->oob_poi = chip->buffers->databuf + mtd->writesize; + +	/* +	 * If no default placement scheme is given, select an appropriate one +	 */ +	if (!chip->ecc.layout) {  		switch (mtd->oobsize) {  		case 8: -			this->autooob = &nand_oob_8; +			chip->ecc.layout = &nand_oob_8;  			break;  		case 16: -			this->autooob = &nand_oob_16; +			chip->ecc.layout = &nand_oob_16;  			break;  		case 64: -			this->autooob = &nand_oob_64; +			chip->ecc.layout = &nand_oob_64;  			break;  		case 128: -			this->autooob = &nand_oob_128; +			chip->ecc.layout = &nand_oob_128;  			break;  		default: -			printk (KERN_WARNING "No oob scheme defined for oobsize %d\n", -				mtd->oobsize); +			printk(KERN_WARNING "No oob scheme defined for " +			       "oobsize %d\n", mtd->oobsize);  /*			BUG(); */  		}  	} -	/* The number of bytes available for the filesystem to place fs dependend -	 * oob data */ -	mtd->oobavail = 0; -	for (i=0; this->autooob->oobfree[i][1]; i++) -		mtd->oobavail += this->autooob->oobfree[i][1]; +	if (!chip->write_page) +		chip->write_page = nand_write_page;  	/* -	 * check ECC mode, default to software -	 * if 3byte/512byte hardware ECC is selected and we have 256 byte pagesize -	 * fallback to software ECC -	*/ -	this->eccsize = 256;	/* set default eccsize */ -	this->eccbytes = 3; +	 * check ECC mode, default to software if 3byte/512byte hardware ECC is +	 * selected and we have 256 byte pagesize fallback to software ECC +	 */ +	if (!chip->ecc.read_page_raw) +		chip->ecc.read_page_raw = nand_read_page_raw; +	if (!chip->ecc.write_page_raw) +		chip->ecc.write_page_raw = nand_write_page_raw; -	switch (this->eccmode) { -	case NAND_ECC_HW12_2048: -		if (mtd->oobblock < 2048) { -			printk(KERN_WARNING "2048 byte HW ECC not possible on %d byte page size, fallback to SW ECC\n", -			       mtd->oobblock); -			this->eccmode = NAND_ECC_SOFT; -			this->calculate_ecc = nand_calculate_ecc; -			this->correct_data = nand_correct_data; -		} else -			this->eccsize = 2048; -		break; +	switch (chip->ecc.mode) { +	case NAND_ECC_HW: +		/* Use standard hwecc read page function ? */ +		if (!chip->ecc.read_page) +			chip->ecc.read_page = nand_read_page_hwecc; +		if (!chip->ecc.write_page) +			chip->ecc.write_page = nand_write_page_hwecc; +		if (!chip->ecc.read_oob) +			chip->ecc.read_oob = nand_read_oob_std; +		if (!chip->ecc.write_oob) +			chip->ecc.write_oob = nand_write_oob_std; -	case NAND_ECC_HW3_512: -	case NAND_ECC_HW6_512: -	case NAND_ECC_HW8_512: -		if (mtd->oobblock == 256) { -			printk (KERN_WARNING "512 byte HW ECC not possible on 256 Byte pagesize, fallback to SW ECC \n"); -			this->eccmode = NAND_ECC_SOFT; -			this->calculate_ecc = nand_calculate_ecc; -			this->correct_data = nand_correct_data; -		} else -			this->eccsize = 512; /* set eccsize to 512 */ -		break; +	case NAND_ECC_HW_SYNDROME: +		if ((!chip->ecc.calculate || !chip->ecc.correct || +		     !chip->ecc.hwctl) && +		    (!chip->ecc.read_page || +		     chip->ecc.read_page == nand_read_page_hwecc || +		     !chip->ecc.write_page || +		     chip->ecc.write_page == nand_write_page_hwecc)) { +			printk(KERN_WARNING "No ECC functions supplied, " +			       "Hardware ECC not possible\n"); +			BUG(); +		} +		/* Use standard syndrome read/write page function ? */ +		if (!chip->ecc.read_page) +			chip->ecc.read_page = nand_read_page_syndrome; +		if (!chip->ecc.write_page) +			chip->ecc.write_page = nand_write_page_syndrome; +		if (!chip->ecc.read_oob) +			chip->ecc.read_oob = nand_read_oob_syndrome; +		if (!chip->ecc.write_oob) +			chip->ecc.write_oob = nand_write_oob_syndrome; -	case NAND_ECC_HW3_256: -		break; +		if (mtd->writesize >= chip->ecc.size) +			break; +		printk(KERN_WARNING "%d byte HW ECC not possible on " +		       "%d byte page size, fallback to SW ECC\n", +		       chip->ecc.size, mtd->writesize); +		chip->ecc.mode = NAND_ECC_SOFT; -	case NAND_ECC_NONE: -		printk (KERN_WARNING "NAND_ECC_NONE selected by board driver. This is not recommended !!\n"); -		this->eccmode = NAND_ECC_NONE; +	case NAND_ECC_SOFT: +		chip->ecc.calculate = nand_calculate_ecc; +		chip->ecc.correct = nand_correct_data; +		chip->ecc.read_page = nand_read_page_swecc; +		chip->ecc.write_page = nand_write_page_swecc; +		chip->ecc.read_oob = nand_read_oob_std; +		chip->ecc.write_oob = nand_write_oob_std; +		chip->ecc.size = 256; +		chip->ecc.bytes = 3;  		break; -	case NAND_ECC_SOFT: -		this->calculate_ecc = nand_calculate_ecc; -		this->correct_data = nand_correct_data; +	case NAND_ECC_NONE: +		printk(KERN_WARNING "NAND_ECC_NONE selected by board driver. " +		       "This is not recommended !!\n"); +		chip->ecc.read_page = nand_read_page_raw; +		chip->ecc.write_page = nand_write_page_raw; +		chip->ecc.read_oob = nand_read_oob_std; +		chip->ecc.write_oob = nand_write_oob_std; +		chip->ecc.size = mtd->writesize; +		chip->ecc.bytes = 0;  		break;  	default: -		printk (KERN_WARNING "Invalid NAND_ECC_MODE %d\n", this->eccmode); -/*		BUG(); */ -	} - -	/* Check hardware ecc function availability and adjust number of ecc bytes per -	 * calculation step -	*/ -	switch (this->eccmode) { -	case NAND_ECC_HW12_2048: -		this->eccbytes += 4; -	case NAND_ECC_HW8_512: -		this->eccbytes += 2; -	case NAND_ECC_HW6_512: -		this->eccbytes += 3; -	case NAND_ECC_HW3_512: -	case NAND_ECC_HW3_256: -		if (this->calculate_ecc && this->correct_data && this->enable_hwecc) -			break; -		printk (KERN_WARNING "No ECC functions supplied, Hardware ECC not possible\n"); -/*		BUG();	*/ +		printk(KERN_WARNING "Invalid NAND_ECC_MODE %d\n", +		       chip->ecc.mode); +		BUG();  	} -	mtd->eccsize = this->eccsize; +	/* +	 * The number of bytes available for a client to place data into +	 * the out of band area +	 */ +	chip->ecc.layout->oobavail = 0; +	for (i = 0; chip->ecc.layout->oobfree[i].length; i++) +		chip->ecc.layout->oobavail += +			chip->ecc.layout->oobfree[i].length; +	mtd->oobavail = chip->ecc.layout->oobavail; -	/* Set the number of read / write steps for one page to ensure ECC generation */ -	switch (this->eccmode) { -	case NAND_ECC_HW12_2048: -		this->eccsteps = mtd->oobblock / 2048; -		break; -	case NAND_ECC_HW3_512: -	case NAND_ECC_HW6_512: -	case NAND_ECC_HW8_512: -		this->eccsteps = mtd->oobblock / 512; -		break; -	case NAND_ECC_HW3_256: -	case NAND_ECC_SOFT: -		this->eccsteps = mtd->oobblock / 256; -		break; +	/* +	 * Set the number of read / write steps for one page depending on ECC +	 * mode +	 */ +	chip->ecc.steps = mtd->writesize / chip->ecc.size; +	if(chip->ecc.steps * chip->ecc.size != mtd->writesize) { +		printk(KERN_WARNING "Invalid ecc parameters\n"); +		BUG(); +	} +	chip->ecc.total = chip->ecc.steps * chip->ecc.bytes; -	case NAND_ECC_NONE: -		this->eccsteps = 1; -		break; +	/* +	 * Allow subpage writes up to ecc.steps. Not possible for MLC +	 * FLASH. +	 */ +	if (!(chip->options & NAND_NO_SUBPAGE_WRITE) && +	    !(chip->cellinfo & NAND_CI_CELLTYPE_MSK)) { +		switch(chip->ecc.steps) { +		case 2: +			mtd->subpage_sft = 1; +			break; +		case 4: +		case 8: +			mtd->subpage_sft = 2; +			break; +		}  	} +	chip->subpagesize = mtd->writesize >> mtd->subpage_sft; -/* XXX U-BOOT XXX */ -#if 0 -	/* Initialize state, waitqueue and spinlock */ -	this->state = FL_READY; -	init_waitqueue_head (&this->wq); -	spin_lock_init (&this->chip_lock); -#endif +	/* Initialize state */ +	chip->state = FL_READY;  	/* De-select the device */ -	this->select_chip(mtd, -1); +	chip->select_chip(mtd, -1);  	/* Invalidate the pagebuffer reference */ -	this->pagebuf = -1; +	chip->pagebuf = -1;  	/* Fill in remaining MTD driver data */  	mtd->type = MTD_NANDFLASH; -	mtd->flags = MTD_CAP_NANDFLASH | MTD_ECC; -	mtd->ecctype = MTD_ECC_SW; +	mtd->flags = MTD_CAP_NANDFLASH;  	mtd->erase = nand_erase;  	mtd->point = NULL;  	mtd->unpoint = NULL;  	mtd->read = nand_read;  	mtd->write = nand_write; -	mtd->read_ecc = nand_read_ecc; -	mtd->write_ecc = nand_write_ecc;  	mtd->read_oob = nand_read_oob;  	mtd->write_oob = nand_write_oob; -/* XXX U-BOOT XXX */ -#if 0 -	mtd->readv = NULL; -	mtd->writev = nand_writev; -	mtd->writev_ecc = nand_writev_ecc; -#endif  	mtd->sync = nand_sync; -/* XXX U-BOOT XXX */ -#if 0  	mtd->lock = NULL;  	mtd->unlock = NULL; -	mtd->suspend = NULL; -	mtd->resume = NULL; -#endif +	mtd->suspend = nand_suspend; +	mtd->resume = nand_resume;  	mtd->block_isbad = nand_block_isbad;  	mtd->block_markbad = nand_block_markbad; -	/* and make the autooob the default one */ -	memcpy(&mtd->oobinfo, this->autooob, sizeof(mtd->oobinfo)); -/* XXX U-BOOT XXX */ +	/* propagate ecc.layout to mtd_info */ +	mtd->ecclayout = chip->ecc.layout; + +	/* Check, if we should skip the bad block table scan */ +	if (chip->options & NAND_SKIP_BBTSCAN) +		chip->options |= NAND_BBT_SCANNED; + +	return 0; +} + +/* module_text_address() isn't exported, and it's mostly a pointless +   test if this is a module _anyway_ -- they'd have to try _really_ hard +   to call us from in-kernel code if the core NAND support is modular. */ +#ifdef MODULE +#define caller_is_module() (1) +#else +#define caller_is_module() \ +	module_text_address((unsigned long)__builtin_return_address(0)) +#endif + +/** + * nand_scan - [NAND Interface] Scan for the NAND device + * @mtd:	MTD device structure + * @maxchips:	Number of chips to scan for + * + * This fills out all the uninitialized function pointers + * with the defaults. + * The flash ID is read and the mtd/chip structures are + * filled with the appropriate values. + * The mtd->owner field must be set to the module of the caller + * + */ +int nand_scan(struct mtd_info *mtd, int maxchips) +{ +	int ret; + +	/* Many callers got this wrong, so check for it for a while... */ +	/* XXX U-BOOT XXX */  #if 0 -	mtd->owner = THIS_MODULE; +	if (!mtd->owner && caller_is_module()) { +		printk(KERN_CRIT "nand_scan() called with NULL mtd->owner!\n"); +		BUG(); +	}  #endif -	/* Build bad block table */ -	return this->scan_bbt (mtd); + +	ret = nand_scan_ident(mtd, maxchips); +	if (!ret) +		ret = nand_scan_tail(mtd); +	return ret;  }  /**   * nand_release - [NAND Interface] Free resources held by the NAND device   * @mtd:	MTD device structure - */ -void nand_release (struct mtd_info *mtd) +*/ +void nand_release(struct mtd_info *mtd)  { -	struct nand_chip *this = mtd->priv; +	struct nand_chip *chip = mtd->priv;  #ifdef CONFIG_MTD_PARTITIONS  	/* Deregister partitions */ -	del_mtd_partitions (mtd); +	del_mtd_partitions(mtd);  #endif  	/* Deregister the device */ -/* XXX U-BOOT XXX */ +	/* XXX U-BOOT XXX */  #if 0 -	del_mtd_device (mtd); +	del_mtd_device(mtd);  #endif -	/* Free bad block table memory, if allocated */ -	if (this->bbt) -		kfree (this->bbt); -	/* Buffer allocated by nand_scan ? */ -	if (this->options & NAND_OOBBUF_ALLOC) -		kfree (this->oob_buf); -	/* Buffer allocated by nand_scan ? */ -	if (this->options & NAND_DATABUF_ALLOC) -		kfree (this->data_buf); + +	/* Free bad block table memory */ +	kfree(chip->bbt); +	if (!(chip->options & NAND_OWN_BUFFERS)) +		kfree(chip->buffers);  } +/* XXX U-BOOT XXX */ +#if 0 +EXPORT_SYMBOL_GPL(nand_scan); +EXPORT_SYMBOL_GPL(nand_scan_ident); +EXPORT_SYMBOL_GPL(nand_scan_tail); +EXPORT_SYMBOL_GPL(nand_release); + +static int __init nand_base_init(void) +{ +	led_trigger_register_simple("nand-disk", &nand_led_trigger); +	return 0; +} + +static void __exit nand_base_exit(void) +{ +	led_trigger_unregister_simple(nand_led_trigger); +} + +module_init(nand_base_init); +module_exit(nand_base_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Steven J. Hill <sjhill@realitydiluted.com>, Thomas Gleixner <tglx@linutronix.de>"); +MODULE_DESCRIPTION("Generic NAND flash driver code");  #endif + +#endif + |