diff options
Diffstat (limited to 'drivers')
| -rw-r--r-- | drivers/mtd/nand/diskonchip.c | 552 | ||||
| -rw-r--r-- | drivers/mtd/nand/nand_base.c | 3431 | ||||
| -rw-r--r-- | drivers/mtd/nand/nand_bbt.c | 548 | ||||
| -rw-r--r-- | drivers/mtd/nand/nand_ecc.c | 21 | ||||
| -rw-r--r-- | drivers/mtd/nand/nand_ids.c | 89 | ||||
| -rw-r--r-- | drivers/mtd/nand/nand_util.c | 359 | 
6 files changed, 2727 insertions, 2273 deletions
| diff --git a/drivers/mtd/nand/diskonchip.c b/drivers/mtd/nand/diskonchip.c index fdd85c159..a03f982be 100644 --- a/drivers/mtd/nand/diskonchip.c +++ b/drivers/mtd/nand/diskonchip.c @@ -16,7 +16,7 @@   *   * Interface to generic NAND code for M-Systems DiskOnChip devices   * - * $Id: diskonchip.c,v 1.45 2005/01/05 18:05:14 dwmw2 Exp $ + * $Id: diskonchip.c,v 1.55 2005/11/07 11:14:30 gleixner Exp $   */  #include <common.h> @@ -39,13 +39,13 @@  #include <linux/mtd/inftl.h>  /* Where to look for the devices? */ -#ifndef CONFIG_MTD_DISKONCHIP_PROBE_ADDRESS -#define CONFIG_MTD_DISKONCHIP_PROBE_ADDRESS 0 +#ifndef CONFIG_MTD_NAND_DISKONCHIP_PROBE_ADDRESS +#define CONFIG_MTD_NAND_DISKONCHIP_PROBE_ADDRESS 0  #endif  static unsigned long __initdata doc_locations[] = {  #if defined (__alpha__) || defined(__i386__) || defined(__x86_64__) -#ifdef CONFIG_MTD_DISKONCHIP_PROBE_HIGH +#ifdef CONFIG_MTD_NAND_DISKONCHIP_PROBE_HIGH  	0xfffc8000, 0xfffca000, 0xfffcc000, 0xfffce000,  	0xfffd0000, 0xfffd2000, 0xfffd4000, 0xfffd6000,  	0xfffd8000, 0xfffda000, 0xfffdc000, 0xfffde000, @@ -65,7 +65,7 @@ static unsigned long __initdata doc_locations[] = {  	0xff000000,  #elif defined(CONFIG_MOMENCO_OCELOT_G) || defined (CONFIG_MOMENCO_OCELOT_C)  	0xff000000, -##else +#else  #warning Unknown architecture for DiskOnChip. No default probe locations defined  #endif  	0xffffffff }; @@ -77,7 +77,7 @@ struct doc_priv {  	unsigned long physadr;  	u_char ChipID;  	u_char CDSNControl; -	int chips_per_floor; /* The number of chips detected on each floor */ +	int chips_per_floor;	/* The number of chips detected on each floor */  	int curfloor;  	int curchip;  	int mh0_page; @@ -85,14 +85,10 @@ struct doc_priv {  	struct mtd_info *nextdoc;  }; -/* Max number of eraseblocks to scan (from start of device) for the (I)NFTL -   MediaHeader.  The spec says to just keep going, I think, but that's just -   silly. */ -#define MAX_MEDIAHEADER_SCAN 8 -  /* This is the syndrome computed by the HW ecc generator upon reading an empty     page, one with all 0xff for data and stored ecc code. */  static u_char empty_read_syndrome[6] = { 0x26, 0xff, 0x6d, 0x47, 0x73, 0x7a }; +  /* This is the ecc value computed by the HW ecc generator upon writing an empty     page, one with all 0xff for data. */  static u_char empty_write_ecc[6] = { 0x4b, 0x00, 0xe2, 0x0e, 0x93, 0xf7 }; @@ -103,35 +99,36 @@ static u_char empty_write_ecc[6] = { 0x4b, 0x00, 0xe2, 0x0e, 0x93, 0xf7 };  #define DoC_is_Millennium(doc) ((doc)->ChipID == DOC_ChipID_DocMil)  #define DoC_is_2000(doc) ((doc)->ChipID == DOC_ChipID_Doc2k) -static void doc200x_hwcontrol(struct mtd_info *mtd, int cmd); +static void doc200x_hwcontrol(struct mtd_info *mtd, int cmd, +			      unsigned int bitmask);  static void doc200x_select_chip(struct mtd_info *mtd, int chip); -static int debug=0; +static int debug = 0;  module_param(debug, int, 0); -static int try_dword=1; +static int try_dword = 1;  module_param(try_dword, int, 0); -static int no_ecc_failures=0; +static int no_ecc_failures = 0;  module_param(no_ecc_failures, int, 0); -#ifdef CONFIG_MTD_PARTITIONS -static int no_autopart=0; +static int no_autopart = 0;  module_param(no_autopart, int, 0); -#endif -#ifdef MTD_NAND_DISKONCHIP_BBTWRITE -static int inftl_bbt_write=1; +static int show_firmware_partition = 0; +module_param(show_firmware_partition, int, 0); + +#ifdef CONFIG_MTD_NAND_DISKONCHIP_BBTWRITE +static int inftl_bbt_write = 1;  #else -static int inftl_bbt_write=0; +static int inftl_bbt_write = 0;  #endif  module_param(inftl_bbt_write, int, 0); -static unsigned long doc_config_location = CONFIG_MTD_DISKONCHIP_PROBE_ADDRESS; +static unsigned long doc_config_location = CONFIG_MTD_NAND_DISKONCHIP_PROBE_ADDRESS;  module_param(doc_config_location, ulong, 0);  MODULE_PARM_DESC(doc_config_location, "Physical memory address at which to probe for DiskOnChip"); -  /* Sector size for HW ECC */  #define SECTOR_SIZE 512  /* The sector bytes are packed into NB_DATA 10 bit words */ @@ -155,7 +152,7 @@ static struct rs_control *rs_decoder;   * some comments, improved a minor bit and converted it to make use   * of the generic Reed-Solomon libary. tglx   */ -static int doc_ecc_decode (struct rs_control *rs, uint8_t *data, uint8_t *ecc) +static int doc_ecc_decode(struct rs_control *rs, uint8_t *data, uint8_t *ecc)  {  	int i, j, nerr, errpos[8];  	uint8_t parity; @@ -176,11 +173,11 @@ static int doc_ecc_decode (struct rs_control *rs, uint8_t *data, uint8_t *ecc)  	 *  s[i] = ds[3]x^3 + ds[2]x^2 + ds[1]x^1 + ds[0]  	 *  where x = alpha^(FCR + i)  	 */ -	for(j = 1; j < NROOTS; j++) { -		if(ds[j] == 0) +	for (j = 1; j < NROOTS; j++) { +		if (ds[j] == 0)  			continue;  		tmp = rs->index_of[ds[j]]; -		for(i = 0; i < NROOTS; i++) +		for (i = 0; i < NROOTS; i++)  			s[i] ^= rs->alpha_to[rs_modnn(rs, tmp + (FCR + i) * j)];  	} @@ -201,7 +198,7 @@ static int doc_ecc_decode (struct rs_control *rs, uint8_t *data, uint8_t *ecc)  	 * but they are given by the design of the de/encoder circuit  	 * in the DoC ASIC's.  	 */ -	for(i = 0;i < nerr; i++) { +	for (i = 0; i < nerr; i++) {  		int index, bitpos, pos = 1015 - errpos[i];  		uint8_t val;  		if (pos >= NB_DATA && pos < 1019) @@ -213,8 +210,7 @@ static int doc_ecc_decode (struct rs_control *rs, uint8_t *data, uint8_t *ecc)  			   can be modified since pos is even */  			index = (pos >> 3) ^ 1;  			bitpos = pos & 7; -			if ((index >= 0 && index < SECTOR_SIZE) || -			    index == (SECTOR_SIZE + 1)) { +			if ((index >= 0 && index < SECTOR_SIZE) || index == (SECTOR_SIZE + 1)) {  				val = (uint8_t) (errval[i] >> (2 + bitpos));  				parity ^= val;  				if (index < SECTOR_SIZE) @@ -224,9 +220,8 @@ static int doc_ecc_decode (struct rs_control *rs, uint8_t *data, uint8_t *ecc)  			bitpos = (bitpos + 10) & 7;  			if (bitpos == 0)  				bitpos = 8; -			if ((index >= 0 && index < SECTOR_SIZE) || -			    index == (SECTOR_SIZE + 1)) { -				val = (uint8_t)(errval[i] << (8 - bitpos)); +			if ((index >= 0 && index < SECTOR_SIZE) || index == (SECTOR_SIZE + 1)) { +				val = (uint8_t) (errval[i] << (8 - bitpos));  				parity ^= val;  				if (index < SECTOR_SIZE)  					data[index] ^= val; @@ -261,7 +256,8 @@ static int _DoC_WaitReady(struct doc_priv *doc)  	void __iomem *docptr = doc->virtadr;  	unsigned long timeo = jiffies + (HZ * 10); -	if(debug) printk("_DoC_WaitReady...\n"); +	if (debug) +		printk("_DoC_WaitReady...\n");  	/* Out-of-line routine to wait for chip response */  	if (DoC_is_MillenniumPlus(doc)) {  		while ((ReadDOC(docptr, Mplus_FlashControl) & CDSN_CTRL_FR_B_MASK) != CDSN_CTRL_FR_B_MASK) { @@ -306,7 +302,8 @@ static inline int DoC_WaitReady(struct doc_priv *doc)  		DoC_Delay(doc, 2);  	} -	if(debug) printk("DoC_WaitReady OK\n"); +	if (debug) +		printk("DoC_WaitReady OK\n");  	return ret;  } @@ -316,7 +313,8 @@ static void doc2000_write_byte(struct mtd_info *mtd, u_char datum)  	struct doc_priv *doc = this->priv;  	void __iomem *docptr = doc->virtadr; -	if(debug)printk("write_byte %02x\n", datum); +	if (debug) +		printk("write_byte %02x\n", datum);  	WriteDOC(datum, docptr, CDSNSlowIO);  	WriteDOC(datum, docptr, 2k_CDSN_IO);  } @@ -331,37 +329,39 @@ static u_char doc2000_read_byte(struct mtd_info *mtd)  	ReadDOC(docptr, CDSNSlowIO);  	DoC_Delay(doc, 2);  	ret = ReadDOC(docptr, 2k_CDSN_IO); -	if (debug) printk("read_byte returns %02x\n", ret); +	if (debug) +		printk("read_byte returns %02x\n", ret);  	return ret;  } -static void doc2000_writebuf(struct mtd_info *mtd, -			     const u_char *buf, int len) +static void doc2000_writebuf(struct mtd_info *mtd, const u_char *buf, int len)  {  	struct nand_chip *this = mtd->priv;  	struct doc_priv *doc = this->priv;  	void __iomem *docptr = doc->virtadr;  	int i; -	if (debug)printk("writebuf of %d bytes: ", len); -	for (i=0; i < len; i++) { +	if (debug) +		printk("writebuf of %d bytes: ", len); +	for (i = 0; i < len; i++) {  		WriteDOC_(buf[i], docptr, DoC_2k_CDSN_IO + i);  		if (debug && i < 16)  			printk("%02x ", buf[i]);  	} -	if (debug) printk("\n"); +	if (debug) +		printk("\n");  } -static void doc2000_readbuf(struct mtd_info *mtd, -			    u_char *buf, int len) +static void doc2000_readbuf(struct mtd_info *mtd, u_char *buf, int len)  {  	struct nand_chip *this = mtd->priv;  	struct doc_priv *doc = this->priv;  	void __iomem *docptr = doc->virtadr;  	int i; -	if (debug)printk("readbuf of %d bytes: ", len); +	if (debug) +		printk("readbuf of %d bytes: ", len); -	for (i=0; i < len; i++) { +	for (i = 0; i < len; i++) {  		buf[i] = ReadDOC(docptr, 2k_CDSN_IO + i);  	}  } @@ -374,28 +374,28 @@ static void doc2000_readbuf_dword(struct mtd_info *mtd,  	void __iomem *docptr = doc->virtadr;  	int i; -	if (debug) printk("readbuf_dword of %d bytes: ", len); +	if (debug) +		printk("readbuf_dword of %d bytes: ", len); -	if (unlikely((((unsigned long)buf)|len) & 3)) { -		for (i=0; i < len; i++) { -			*(uint8_t *)(&buf[i]) = ReadDOC(docptr, 2k_CDSN_IO + i); +	if (unlikely((((unsigned long)buf) | len) & 3)) { +		for (i = 0; i < len; i++) { +			*(uint8_t *) (&buf[i]) = ReadDOC(docptr, 2k_CDSN_IO + i);  		}  	} else { -		for (i=0; i < len; i+=4) { -			*(uint32_t*)(&buf[i]) = readl(docptr + DoC_2k_CDSN_IO + i); +		for (i = 0; i < len; i += 4) { +			*(uint32_t*) (&buf[i]) = readl(docptr + DoC_2k_CDSN_IO + i);  		}  	}  } -static int doc2000_verifybuf(struct mtd_info *mtd, -			      const u_char *buf, int len) +static int doc2000_verifybuf(struct mtd_info *mtd, const u_char *buf, int len)  {  	struct nand_chip *this = mtd->priv;  	struct doc_priv *doc = this->priv;  	void __iomem *docptr = doc->virtadr;  	int i; -	for (i=0; i < len; i++) +	for (i = 0; i < len; i++)  		if (buf[i] != ReadDOC(docptr, 2k_CDSN_IO))  			return -EFAULT;  	return 0; @@ -408,12 +408,15 @@ static uint16_t __init doc200x_ident_chip(struct mtd_info *mtd, int nr)  	uint16_t ret;  	doc200x_select_chip(mtd, nr); -	doc200x_hwcontrol(mtd, NAND_CTL_SETCLE); -	this->write_byte(mtd, NAND_CMD_READID); -	doc200x_hwcontrol(mtd, NAND_CTL_CLRCLE); -	doc200x_hwcontrol(mtd, NAND_CTL_SETALE); -	this->write_byte(mtd, 0); -	doc200x_hwcontrol(mtd, NAND_CTL_CLRALE); +	doc200x_hwcontrol(mtd, NAND_CMD_READID, +			  NAND_CTRL_CLE | NAND_CTRL_CHANGE); +	doc200x_hwcontrol(mtd, 0, NAND_CTRL_ALE | NAND_CTRL_CHANGE); +	doc200x_hwcontrol(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); + +	/* We cant' use dev_ready here, but at least we wait for the +	 * command to complete +	 */ +	udelay(50);  	ret = this->read_byte(mtd) << 8;  	ret |= this->read_byte(mtd); @@ -426,12 +429,13 @@ static uint16_t __init doc200x_ident_chip(struct mtd_info *mtd, int nr)  		} ident;  		void __iomem *docptr = doc->virtadr; -		doc200x_hwcontrol(mtd, NAND_CTL_SETCLE); -		doc2000_write_byte(mtd, NAND_CMD_READID); -		doc200x_hwcontrol(mtd, NAND_CTL_CLRCLE); -		doc200x_hwcontrol(mtd, NAND_CTL_SETALE); -		doc2000_write_byte(mtd, 0); -		doc200x_hwcontrol(mtd, NAND_CTL_CLRALE); +		doc200x_hwcontrol(mtd, NAND_CMD_READID, +				  NAND_CTRL_CLE | NAND_CTRL_CHANGE); +		doc200x_hwcontrol(mtd, 0, NAND_CTRL_ALE | NAND_CTRL_CHANGE); +		doc200x_hwcontrol(mtd, NAND_CMD_NONE, +				  NAND_NCE | NAND_CTRL_CHANGE); + +		udelay(50);  		ident.dword = readl(docptr + DoC_2k_CDSN_IO);  		if (((ident.byte[0] << 8) | ident.byte[1]) == ret) { @@ -465,7 +469,7 @@ static void __init doc2000_count_chips(struct mtd_info *mtd)  	printk(KERN_DEBUG "Detected %d chips per floor.\n", i);  } -static int doc200x_wait(struct mtd_info *mtd, struct nand_chip *this, int state) +static int doc200x_wait(struct mtd_info *mtd, struct nand_chip *this)  {  	struct doc_priv *doc = this->priv; @@ -496,30 +500,28 @@ static u_char doc2001_read_byte(struct mtd_info *mtd)  	struct doc_priv *doc = this->priv;  	void __iomem *docptr = doc->virtadr; -	/*ReadDOC(docptr, CDSNSlowIO); */ +	//ReadDOC(docptr, CDSNSlowIO);  	/* 11.4.5 -- delay twice to allow extended length cycle */  	DoC_Delay(doc, 2);  	ReadDOC(docptr, ReadPipeInit); -	/*return ReadDOC(docptr, Mil_CDSN_IO); */ +	//return ReadDOC(docptr, Mil_CDSN_IO);  	return ReadDOC(docptr, LastDataRead);  } -static void doc2001_writebuf(struct mtd_info *mtd, -			     const u_char *buf, int len) +static void doc2001_writebuf(struct mtd_info *mtd, const u_char *buf, int len)  {  	struct nand_chip *this = mtd->priv;  	struct doc_priv *doc = this->priv;  	void __iomem *docptr = doc->virtadr;  	int i; -	for (i=0; i < len; i++) +	for (i = 0; i < len; i++)  		WriteDOC_(buf[i], docptr, DoC_Mil_CDSN_IO + i);  	/* Terminate write pipeline */  	WriteDOC(0x00, docptr, WritePipeTerm);  } -static void doc2001_readbuf(struct mtd_info *mtd, -			    u_char *buf, int len) +static void doc2001_readbuf(struct mtd_info *mtd, u_char *buf, int len)  {  	struct nand_chip *this = mtd->priv;  	struct doc_priv *doc = this->priv; @@ -529,15 +531,14 @@ static void doc2001_readbuf(struct mtd_info *mtd,  	/* Start read pipeline */  	ReadDOC(docptr, ReadPipeInit); -	for (i=0; i < len-1; i++) +	for (i = 0; i < len - 1; i++)  		buf[i] = ReadDOC(docptr, Mil_CDSN_IO + (i & 0xff));  	/* Terminate read pipeline */  	buf[i] = ReadDOC(docptr, LastDataRead);  } -static int doc2001_verifybuf(struct mtd_info *mtd, -			     const u_char *buf, int len) +static int doc2001_verifybuf(struct mtd_info *mtd, const u_char *buf, int len)  {  	struct nand_chip *this = mtd->priv;  	struct doc_priv *doc = this->priv; @@ -547,7 +548,7 @@ static int doc2001_verifybuf(struct mtd_info *mtd,  	/* Start read pipeline */  	ReadDOC(docptr, ReadPipeInit); -	for (i=0; i < len-1; i++) +	for (i = 0; i < len - 1; i++)  		if (buf[i] != ReadDOC(docptr, Mil_CDSN_IO)) {  			ReadDOC(docptr, LastDataRead);  			return i; @@ -567,81 +568,84 @@ static u_char doc2001plus_read_byte(struct mtd_info *mtd)  	ReadDOC(docptr, Mplus_ReadPipeInit);  	ReadDOC(docptr, Mplus_ReadPipeInit);  	ret = ReadDOC(docptr, Mplus_LastDataRead); -	if (debug) printk("read_byte returns %02x\n", ret); +	if (debug) +		printk("read_byte returns %02x\n", ret);  	return ret;  } -static void doc2001plus_writebuf(struct mtd_info *mtd, -			     const u_char *buf, int len) +static void doc2001plus_writebuf(struct mtd_info *mtd, const u_char *buf, int len)  {  	struct nand_chip *this = mtd->priv;  	struct doc_priv *doc = this->priv;  	void __iomem *docptr = doc->virtadr;  	int i; -	if (debug)printk("writebuf of %d bytes: ", len); -	for (i=0; i < len; i++) { +	if (debug) +		printk("writebuf of %d bytes: ", len); +	for (i = 0; i < len; i++) {  		WriteDOC_(buf[i], docptr, DoC_Mil_CDSN_IO + i);  		if (debug && i < 16)  			printk("%02x ", buf[i]);  	} -	if (debug) printk("\n"); +	if (debug) +		printk("\n");  } -static void doc2001plus_readbuf(struct mtd_info *mtd, -			    u_char *buf, int len) +static void doc2001plus_readbuf(struct mtd_info *mtd, u_char *buf, int len)  {  	struct nand_chip *this = mtd->priv;  	struct doc_priv *doc = this->priv;  	void __iomem *docptr = doc->virtadr;  	int i; -	if (debug)printk("readbuf of %d bytes: ", len); +	if (debug) +		printk("readbuf of %d bytes: ", len);  	/* Start read pipeline */  	ReadDOC(docptr, Mplus_ReadPipeInit);  	ReadDOC(docptr, Mplus_ReadPipeInit); -	for (i=0; i < len-2; i++) { +	for (i = 0; i < len - 2; i++) {  		buf[i] = ReadDOC(docptr, Mil_CDSN_IO);  		if (debug && i < 16)  			printk("%02x ", buf[i]);  	}  	/* Terminate read pipeline */ -	buf[len-2] = ReadDOC(docptr, Mplus_LastDataRead); +	buf[len - 2] = ReadDOC(docptr, Mplus_LastDataRead);  	if (debug && i < 16) -		printk("%02x ", buf[len-2]); -	buf[len-1] = ReadDOC(docptr, Mplus_LastDataRead); +		printk("%02x ", buf[len - 2]); +	buf[len - 1] = ReadDOC(docptr, Mplus_LastDataRead);  	if (debug && i < 16) -		printk("%02x ", buf[len-1]); -	if (debug) printk("\n"); +		printk("%02x ", buf[len - 1]); +	if (debug) +		printk("\n");  } -static int doc2001plus_verifybuf(struct mtd_info *mtd, -			     const u_char *buf, int len) +static int doc2001plus_verifybuf(struct mtd_info *mtd, const u_char *buf, int len)  {  	struct nand_chip *this = mtd->priv;  	struct doc_priv *doc = this->priv;  	void __iomem *docptr = doc->virtadr;  	int i; -	if (debug)printk("verifybuf of %d bytes: ", len); +	if (debug) +		printk("verifybuf of %d bytes: ", len);  	/* Start read pipeline */  	ReadDOC(docptr, Mplus_ReadPipeInit);  	ReadDOC(docptr, Mplus_ReadPipeInit); -	for (i=0; i < len-2; i++) +	for (i = 0; i < len - 2; i++)  		if (buf[i] != ReadDOC(docptr, Mil_CDSN_IO)) {  			ReadDOC(docptr, Mplus_LastDataRead);  			ReadDOC(docptr, Mplus_LastDataRead);  			return i;  		} -	if (buf[len-2] != ReadDOC(docptr, Mplus_LastDataRead)) -		return len-2; -	if (buf[len-1] != ReadDOC(docptr, Mplus_LastDataRead)) -		return len-1; +	if (buf[len - 2] != ReadDOC(docptr, Mplus_LastDataRead)) +		return len - 2; +	if (buf[len - 1] != ReadDOC(docptr, Mplus_LastDataRead)) +		return len - 1;  	return 0;  } @@ -652,7 +656,8 @@ static void doc2001plus_select_chip(struct mtd_info *mtd, int chip)  	void __iomem *docptr = doc->virtadr;  	int floor = 0; -	if(debug)printk("select chip (%d)\n", chip); +	if (debug) +		printk("select chip (%d)\n", chip);  	if (chip == -1) {  		/* Disable flash internally */ @@ -661,7 +666,7 @@ static void doc2001plus_select_chip(struct mtd_info *mtd, int chip)  	}  	floor = chip / doc->chips_per_floor; -	chip -= (floor *  doc->chips_per_floor); +	chip -= (floor * doc->chips_per_floor);  	/* Assert ChipEnable and deassert WriteProtect */  	WriteDOC((DOC_FLASH_CE), docptr, Mplus_FlashSelect); @@ -678,65 +683,54 @@ static void doc200x_select_chip(struct mtd_info *mtd, int chip)  	void __iomem *docptr = doc->virtadr;  	int floor = 0; -	if(debug)printk("select chip (%d)\n", chip); +	if (debug) +		printk("select chip (%d)\n", chip);  	if (chip == -1)  		return;  	floor = chip / doc->chips_per_floor; -	chip -= (floor *  doc->chips_per_floor); +	chip -= (floor * doc->chips_per_floor);  	/* 11.4.4 -- deassert CE before changing chip */ -	doc200x_hwcontrol(mtd, NAND_CTL_CLRNCE); +	doc200x_hwcontrol(mtd, NAND_CMD_NONE, 0 | NAND_CTRL_CHANGE);  	WriteDOC(floor, docptr, FloorSelect);  	WriteDOC(chip, docptr, CDSNDeviceSelect); -	doc200x_hwcontrol(mtd, NAND_CTL_SETNCE); +	doc200x_hwcontrol(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE);  	doc->curchip = chip;  	doc->curfloor = floor;  } -static void doc200x_hwcontrol(struct mtd_info *mtd, int cmd) +#define CDSN_CTRL_MSK (CDSN_CTRL_CE | CDSN_CTRL_CLE | CDSN_CTRL_ALE) + +static void doc200x_hwcontrol(struct mtd_info *mtd, int cmd, +			      unsigned int ctrl)  {  	struct nand_chip *this = mtd->priv;  	struct doc_priv *doc = this->priv;  	void __iomem *docptr = doc->virtadr; -	switch(cmd) { -	case NAND_CTL_SETNCE: -		doc->CDSNControl |= CDSN_CTRL_CE; -		break; -	case NAND_CTL_CLRNCE: -		doc->CDSNControl &= ~CDSN_CTRL_CE; -		break; -	case NAND_CTL_SETCLE: -		doc->CDSNControl |= CDSN_CTRL_CLE; -		break; -	case NAND_CTL_CLRCLE: -		doc->CDSNControl &= ~CDSN_CTRL_CLE; -		break; -	case NAND_CTL_SETALE: -		doc->CDSNControl |= CDSN_CTRL_ALE; -		break; -	case NAND_CTL_CLRALE: -		doc->CDSNControl &= ~CDSN_CTRL_ALE; -		break; -	case NAND_CTL_SETWP: -		doc->CDSNControl |= CDSN_CTRL_WP; -		break; -	case NAND_CTL_CLRWP: -		doc->CDSNControl &= ~CDSN_CTRL_WP; -		break; +	if (ctrl & NAND_CTRL_CHANGE) { +		doc->CDSNControl &= ~CDSN_CTRL_MSK; +		doc->CDSNControl |= ctrl & CDSN_CTRL_MSK; +		if (debug) +			printk("hwcontrol(%d): %02x\n", cmd, doc->CDSNControl); +		WriteDOC(doc->CDSNControl, docptr, CDSNControl); +		/* 11.4.3 -- 4 NOPs after CSDNControl write */ +		DoC_Delay(doc, 4); +	} +	if (cmd != NAND_CMD_NONE) { +		if (DoC_is_2000(doc)) +			doc2000_write_byte(mtd, cmd); +		else +			doc2001_write_byte(mtd, cmd);  	} -	if (debug)printk("hwcontrol(%d): %02x\n", cmd, doc->CDSNControl); -	WriteDOC(doc->CDSNControl, docptr, CDSNControl); -	/* 11.4.3 -- 4 NOPs after CSDNControl write */ -	DoC_Delay(doc, 4);  } -static void doc2001plus_command (struct mtd_info *mtd, unsigned command, int column, int page_addr) +static void doc2001plus_command(struct mtd_info *mtd, unsigned command, int column, int page_addr)  {  	struct nand_chip *this = mtd->priv;  	struct doc_priv *doc = this->priv; @@ -757,9 +751,9 @@ static void doc2001plus_command (struct mtd_info *mtd, unsigned command, int col  	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 */ @@ -783,25 +777,26 @@ static void doc2001plus_command (struct mtd_info *mtd, unsigned command, int col  			WriteDOC(column, docptr, Mplus_FlashAddress);  		}  		if (page_addr != -1) { -			WriteDOC((unsigned char) (page_addr & 0xff), docptr, Mplus_FlashAddress); -			WriteDOC((unsigned char) ((page_addr >> 8) & 0xff), docptr, Mplus_FlashAddress); +			WriteDOC((unsigned char)(page_addr & 0xff), docptr, Mplus_FlashAddress); +			WriteDOC((unsigned char)((page_addr >> 8) & 0xff), docptr, Mplus_FlashAddress);  			/* One more address cycle for higher density devices */  			if (this->chipsize & 0x0c000000) { -				WriteDOC((unsigned char) ((page_addr >> 16) & 0x0f), docptr, Mplus_FlashAddress); +				WriteDOC((unsigned char)((page_addr >> 16) & 0x0f), docptr, Mplus_FlashAddress);  				printk("high density\n");  			}  		}  		WriteDOC(0, docptr, Mplus_WritePipeTerm);  		WriteDOC(0, docptr, Mplus_WritePipeTerm);  		/* deassert ALE */ -		if (command == NAND_CMD_READ0 || command == NAND_CMD_READ1 || command == NAND_CMD_READOOB || command == NAND_CMD_READID) +		if (command == NAND_CMD_READ0 || command == NAND_CMD_READ1 || +		    command == NAND_CMD_READOOB || command == NAND_CMD_READID)  			WriteDOC(0, docptr, Mplus_FlashControl);  	}  	/*  	 * program and erase have their own busy handlers  	 * status and sequential in needs no delay -	*/ +	 */  	switch (command) {  	case NAND_CMD_PAGEPROG: @@ -818,26 +813,26 @@ static void doc2001plus_command (struct mtd_info *mtd, unsigned command, int col  		WriteDOC(NAND_CMD_STATUS, docptr, Mplus_FlashCmd);  		WriteDOC(0, docptr, Mplus_WritePipeTerm);  		WriteDOC(0, docptr, Mplus_WritePipeTerm); -		while ( !(this->read_byte(mtd) & 0x40)); +		while (!(this->read_byte(mtd) & 0x40)) ;  		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); +			udelay(this->chip_delay);  			return;  		}  	}  	/* Apply this short delay always to ensure that we do wait tWB in  	 * any case on any machine. */ -	ndelay (100); +	ndelay(100);  	/* wait until command is processed */ -	while (!this->dev_ready(mtd)); +	while (!this->dev_ready(mtd)) ;  }  static int doc200x_dev_ready(struct mtd_info *mtd) @@ -850,23 +845,25 @@ static int doc200x_dev_ready(struct mtd_info *mtd)  		/* 11.4.2 -- must NOP four times before checking FR/B# */  		DoC_Delay(doc, 4);  		if ((ReadDOC(docptr, Mplus_FlashControl) & CDSN_CTRL_FR_B_MASK) != CDSN_CTRL_FR_B_MASK) { -			if(debug) +			if (debug)  				printk("not ready\n");  			return 0;  		} -		if (debug)printk("was ready\n"); +		if (debug) +			printk("was ready\n");  		return 1;  	} else {  		/* 11.4.2 -- must NOP four times before checking FR/B# */  		DoC_Delay(doc, 4);  		if (!(ReadDOC(docptr, CDSNControl) & CDSN_CTRL_FR_B)) { -			if(debug) +			if (debug)  				printk("not ready\n");  			return 0;  		}  		/* 11.4.2 -- Must NOP twice if it's ready */  		DoC_Delay(doc, 2); -		if (debug)printk("was ready\n"); +		if (debug) +			printk("was ready\n");  		return 1;  	}  } @@ -885,7 +882,7 @@ static void doc200x_enable_hwecc(struct mtd_info *mtd, int mode)  	void __iomem *docptr = doc->virtadr;  	/* Prime the ECC engine */ -	switch(mode) { +	switch (mode) {  	case NAND_ECC_READ:  		WriteDOC(DOC_ECC_RESET, docptr, ECCConf);  		WriteDOC(DOC_ECC_EN, docptr, ECCConf); @@ -904,7 +901,7 @@ static void doc2001plus_enable_hwecc(struct mtd_info *mtd, int mode)  	void __iomem *docptr = doc->virtadr;  	/* Prime the ECC engine */ -	switch(mode) { +	switch (mode) {  	case NAND_ECC_READ:  		WriteDOC(DOC_ECC_RESET, docptr, Mplus_ECCConf);  		WriteDOC(DOC_ECC_EN, docptr, Mplus_ECCConf); @@ -917,8 +914,7 @@ static void doc2001plus_enable_hwecc(struct mtd_info *mtd, int mode)  }  /* This code is only called on write */ -static int doc200x_calculate_ecc(struct mtd_info *mtd, const u_char *dat, -				 unsigned char *ecc_code) +static int doc200x_calculate_ecc(struct mtd_info *mtd, const u_char *dat, unsigned char *ecc_code)  {  	struct nand_chip *this = mtd->priv;  	struct doc_priv *doc = this->priv; @@ -962,7 +958,8 @@ static int doc200x_calculate_ecc(struct mtd_info *mtd, const u_char *dat,  		   often.  It could be optimized away by examining the data in  		   the writebuf routine, and remembering the result. */  		for (i = 0; i < 512; i++) { -			if (dat[i] == 0xff) continue; +			if (dat[i] == 0xff) +				continue;  			emptymatch = 0;  			break;  		} @@ -970,17 +967,20 @@ static int doc200x_calculate_ecc(struct mtd_info *mtd, const u_char *dat,  	/* If emptymatch still =1, we do have an all-0xff data buffer.  	   Return all-0xff ecc value instead of the computed one, so  	   it'll look just like a freshly-erased page. */ -	if (emptymatch) memset(ecc_code, 0xff, 6); +	if (emptymatch) +		memset(ecc_code, 0xff, 6);  #endif  	return 0;  } -static int doc200x_correct_data(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, u_char *calc_ecc) +static int doc200x_correct_data(struct mtd_info *mtd, u_char *dat, +				u_char *read_ecc, u_char *isnull)  {  	int i, ret = 0;  	struct nand_chip *this = mtd->priv;  	struct doc_priv *doc = this->priv;  	void __iomem *docptr = doc->virtadr; +	uint8_t calc_ecc[6];  	volatile u_char dummy;  	int emptymatch = 1; @@ -1013,18 +1013,20 @@ static int doc200x_correct_data(struct mtd_info *mtd, u_char *dat, u_char *read_  		   all-0xff data and stored ecc block.  Check the stored ecc. */  		if (emptymatch) {  			for (i = 0; i < 6; i++) { -				if (read_ecc[i] == 0xff) continue; +				if (read_ecc[i] == 0xff) +					continue;  				emptymatch = 0;  				break;  			}  		}  		/* If emptymatch still =1, check the data block. */  		if (emptymatch) { -		/* Note: this somewhat expensive test should not be triggered -		   often.  It could be optimized away by examining the data in -		   the readbuf routine, and remembering the result. */ +			/* Note: this somewhat expensive test should not be triggered +			   often.  It could be optimized away by examining the data in +			   the readbuf routine, and remembering the result. */  			for (i = 0; i < 512; i++) { -				if (dat[i] == 0xff) continue; +				if (dat[i] == 0xff) +					continue;  				emptymatch = 0;  				break;  			} @@ -1033,7 +1035,8 @@ static int doc200x_correct_data(struct mtd_info *mtd, u_char *dat, u_char *read_  		   erased block, in which case the ECC will not come out right.  		   We'll suppress the error and tell the caller everything's  		   OK.  Because it is. */ -		if (!emptymatch) ret = doc_ecc_decode (rs_decoder, dat, calc_ecc); +		if (!emptymatch) +			ret = doc_ecc_decode(rs_decoder, dat, calc_ecc);  		if (ret > 0)  			printk(KERN_ERR "doc200x_correct_data corrected %d errors\n", ret);  	} @@ -1048,13 +1051,22 @@ static int doc200x_correct_data(struct mtd_info *mtd, u_char *dat, u_char *read_  	return ret;  } -/*u_char mydatabuf[528]; */ +//u_char mydatabuf[528]; -static struct nand_oobinfo doc200x_oobinfo = { -	.useecc = MTD_NANDECC_AUTOPLACE, +/* The strange out-of-order .oobfree list below is a (possibly unneeded) + * attempt to retain compatibility.  It used to read: + * 	.oobfree = { {8, 8} } + * Since that leaves two bytes unusable, it was changed.  But the following + * scheme might affect existing jffs2 installs by moving the cleanmarker: + * 	.oobfree = { {6, 10} } + * jffs2 seems to handle the above gracefully, but the current scheme seems + * safer.  The only problem with it is that any code that parses oobfree must + * be able to handle out-of-order segments. + */ +static struct nand_ecclayout doc200x_oobinfo = {  	.eccbytes = 6,  	.eccpos = {0, 1, 2, 3, 4, 5}, -	.oobfree = { {8, 8} } +	.oobfree = {{8, 8}, {6, 2}}  };  /* Find the (I)NFTL Media Header, and optionally also the mirror media header. @@ -1063,28 +1075,28 @@ static struct nand_oobinfo doc200x_oobinfo = {     either "ANAND" or "BNAND".  If findmirror=1, also look for the mirror media     header.  The page #s of the found media headers are placed in mh0_page and     mh1_page in the DOC private structure. */ -static int __init find_media_headers(struct mtd_info *mtd, u_char *buf, -				     const char *id, int findmirror) +static int __init find_media_headers(struct mtd_info *mtd, u_char *buf, const char *id, int findmirror)  {  	struct nand_chip *this = mtd->priv;  	struct doc_priv *doc = this->priv; -	unsigned offs, end = (MAX_MEDIAHEADER_SCAN << this->phys_erase_shift); +	unsigned offs;  	int ret;  	size_t retlen; -	end = min(end, mtd->size); /* paranoia */ -	for (offs = 0; offs < end; offs += mtd->erasesize) { -		ret = mtd->read(mtd, offs, mtd->oobblock, &retlen, buf); -		if (retlen != mtd->oobblock) continue; +	for (offs = 0; offs < mtd->size; offs += mtd->erasesize) { +		ret = mtd->read(mtd, offs, mtd->writesize, &retlen, buf); +		if (retlen != mtd->writesize) +			continue;  		if (ret) { -			printk(KERN_WARNING "ECC error scanning DOC at 0x%x\n", -				offs); +			printk(KERN_WARNING "ECC error scanning DOC at 0x%x\n", offs);  		} -		if (memcmp(buf, id, 6)) continue; +		if (memcmp(buf, id, 6)) +			continue;  		printk(KERN_INFO "Found DiskOnChip %s Media Header at 0x%x\n", id, offs);  		if (doc->mh0_page == -1) {  			doc->mh0_page = offs >> this->page_shift; -			if (!findmirror) return 1; +			if (!findmirror) +				return 1;  			continue;  		}  		doc->mh1_page = offs >> this->page_shift; @@ -1097,8 +1109,8 @@ static int __init find_media_headers(struct mtd_info *mtd, u_char *buf,  	/* Only one mediaheader was found.  We want buf to contain a  	   mediaheader on return, so we'll have to re-read the one we found. */  	offs = doc->mh0_page << this->page_shift; -	ret = mtd->read(mtd, offs, mtd->oobblock, &retlen, buf); -	if (retlen != mtd->oobblock) { +	ret = mtd->read(mtd, offs, mtd->writesize, &retlen, buf); +	if (retlen != mtd->writesize) {  		/* Insanity.  Give up. */  		printk(KERN_ERR "Read DiskOnChip Media Header once, but can't reread it???\n");  		return 0; @@ -1106,8 +1118,7 @@ static int __init find_media_headers(struct mtd_info *mtd, u_char *buf,  	return 1;  } -static inline int __init nftl_partscan(struct mtd_info *mtd, -				struct mtd_partition *parts) +static inline int __init nftl_partscan(struct mtd_info *mtd, struct mtd_partition *parts)  {  	struct nand_chip *this = mtd->priv;  	struct doc_priv *doc = this->priv; @@ -1115,19 +1126,23 @@ static inline int __init nftl_partscan(struct mtd_info *mtd,  	u_char *buf;  	struct NFTLMediaHeader *mh;  	const unsigned psize = 1 << this->page_shift; +	int numparts = 0;  	unsigned blocks, maxblocks;  	int offs, numheaders; -	buf = kmalloc(mtd->oobblock, GFP_KERNEL); +	buf = kmalloc(mtd->writesize, GFP_KERNEL);  	if (!buf) {  		printk(KERN_ERR "DiskOnChip mediaheader kmalloc failed!\n");  		return 0;  	} -	if (!(numheaders=find_media_headers(mtd, buf, "ANAND", 1))) goto out; -	mh = (struct NFTLMediaHeader *) buf; +	if (!(numheaders = find_media_headers(mtd, buf, "ANAND", 1))) +		goto out; +	mh = (struct NFTLMediaHeader *)buf; + +	mh->NumEraseUnits = le16_to_cpu(mh->NumEraseUnits); +	mh->FirstPhysicalEUN = le16_to_cpu(mh->FirstPhysicalEUN); +	mh->FormattedSize = le32_to_cpu(mh->FormattedSize); -/*#ifdef CONFIG_MTD_DEBUG_VERBOSE */ -/*	if (CONFIG_MTD_DEBUG_VERBOSE >= 2) */  	printk(KERN_INFO "    DataOrgID        = %s\n"  			 "    NumEraseUnits    = %d\n"  			 "    FirstPhysicalEUN = %d\n" @@ -1136,7 +1151,6 @@ static inline int __init nftl_partscan(struct mtd_info *mtd,  		mh->DataOrgID, mh->NumEraseUnits,  		mh->FirstPhysicalEUN, mh->FormattedSize,  		mh->UnitSizeFactor); -/*#endif */  	blocks = mtd->size >> this->phys_erase_shift;  	maxblocks = min(32768U, mtd->erasesize - psize); @@ -1145,8 +1159,8 @@ static inline int __init nftl_partscan(struct mtd_info *mtd,  		/* Auto-determine UnitSizeFactor.  The constraints are:  		   - There can be at most 32768 virtual blocks.  		   - There can be at most (virtual block size - page size) -		     virtual blocks (because MediaHeader+BBT must fit in 1). -		*/ +		   virtual blocks (because MediaHeader+BBT must fit in 1). +		 */  		mh->UnitSizeFactor = 0xff;  		while (blocks > maxblocks) {  			blocks >>= 1; @@ -1179,31 +1193,35 @@ static inline int __init nftl_partscan(struct mtd_info *mtd,  	offs <<= this->page_shift;  	offs += mtd->erasesize; -	/*parts[0].name = " DiskOnChip Boot / Media Header partition"; */ -	/*parts[0].offset = 0; */ -	/*parts[0].size = offs; */ +	if (show_firmware_partition == 1) { +		parts[0].name = " DiskOnChip Firmware / Media Header partition"; +		parts[0].offset = 0; +		parts[0].size = offs; +		numparts = 1; +	} + +	parts[numparts].name = " DiskOnChip BDTL partition"; +	parts[numparts].offset = offs; +	parts[numparts].size = (mh->NumEraseUnits - numheaders) << this->bbt_erase_shift; -	parts[0].name = " DiskOnChip BDTL partition"; -	parts[0].offset = offs; -	parts[0].size = (mh->NumEraseUnits - numheaders) << this->bbt_erase_shift; +	offs += parts[numparts].size; +	numparts++; -	offs += parts[0].size;  	if (offs < mtd->size) { -		parts[1].name = " DiskOnChip Remainder partition"; -		parts[1].offset = offs; -		parts[1].size = mtd->size - offs; -		ret = 2; -		goto out; +		parts[numparts].name = " DiskOnChip Remainder partition"; +		parts[numparts].offset = offs; +		parts[numparts].size = mtd->size - offs; +		numparts++;  	} -	ret = 1; -out: + +	ret = numparts; + out:  	kfree(buf);  	return ret;  }  /* This is a stripped-down copy of the code in inftlmount.c */ -static inline int __init inftl_partscan(struct mtd_info *mtd, -				 struct mtd_partition *parts) +static inline int __init inftl_partscan(struct mtd_info *mtd, struct mtd_partition *parts)  {  	struct nand_chip *this = mtd->priv;  	struct doc_priv *doc = this->priv; @@ -1220,15 +1238,16 @@ static inline int __init inftl_partscan(struct mtd_info *mtd,  	if (inftl_bbt_write)  		end -= (INFTL_BBT_RESERVED_BLOCKS << this->phys_erase_shift); -	buf = kmalloc(mtd->oobblock, GFP_KERNEL); +	buf = kmalloc(mtd->writesize, GFP_KERNEL);  	if (!buf) {  		printk(KERN_ERR "DiskOnChip mediaheader kmalloc failed!\n");  		return 0;  	} -	if (!find_media_headers(mtd, buf, "BNAND", 0)) goto out; +	if (!find_media_headers(mtd, buf, "BNAND", 0)) +		goto out;  	doc->mh1_page = doc->mh0_page + (4096 >> this->page_shift); -	mh = (struct INFTLMediaHeader *) buf; +	mh = (struct INFTLMediaHeader *)buf;  	mh->NoOfBootImageBlocks = le32_to_cpu(mh->NoOfBootImageBlocks);  	mh->NoOfBinaryPartitions = le32_to_cpu(mh->NoOfBinaryPartitions); @@ -1237,8 +1256,6 @@ static inline int __init inftl_partscan(struct mtd_info *mtd,  	mh->FormatFlags = le32_to_cpu(mh->FormatFlags);  	mh->PercentUsed = le32_to_cpu(mh->PercentUsed); -/*#ifdef CONFIG_MTD_DEBUG_VERBOSE */ -/*	if (CONFIG_MTD_DEBUG_VERBOSE >= 2) */  	printk(KERN_INFO "    bootRecordID          = %s\n"  			 "    NoOfBootImageBlocks   = %d\n"  			 "    NoOfBinaryPartitions  = %d\n" @@ -1256,7 +1273,6 @@ static inline int __init inftl_partscan(struct mtd_info *mtd,  		((unsigned char *) &mh->OsakVersion)[2] & 0xf,  		((unsigned char *) &mh->OsakVersion)[3] & 0xf,  		mh->PercentUsed); -/*#endif */  	vshift = this->phys_erase_shift + mh->BlockMultiplierBits; @@ -1282,8 +1298,6 @@ static inline int __init inftl_partscan(struct mtd_info *mtd,  		ip->spareUnits = le32_to_cpu(ip->spareUnits);  		ip->Reserved0 = le32_to_cpu(ip->Reserved0); -/*#ifdef CONFIG_MTD_DEBUG_VERBOSE */ -/*		if (CONFIG_MTD_DEBUG_VERBOSE >= 2) */  		printk(KERN_INFO	"    PARTITION[%d] ->\n"  			"        virtualUnits    = %d\n"  			"        firstUnit       = %d\n" @@ -1293,16 +1307,14 @@ static inline int __init inftl_partscan(struct mtd_info *mtd,  			i, ip->virtualUnits, ip->firstUnit,  			ip->lastUnit, ip->flags,  			ip->spareUnits); -/*#endif */ -/* -		if ((i == 0) && (ip->firstUnit > 0)) { +		if ((show_firmware_partition == 1) && +		    (i == 0) && (ip->firstUnit > 0)) {  			parts[0].name = " DiskOnChip IPL / Media Header partition";  			parts[0].offset = 0;  			parts[0].size = mtd->erasesize * ip->firstUnit;  			numparts = 1;  		} -*/  		if (ip->flags & INFTL_BINARY)  			parts[numparts].name = " DiskOnChip BDK partition"; @@ -1311,8 +1323,10 @@ static inline int __init inftl_partscan(struct mtd_info *mtd,  		parts[numparts].offset = ip->firstUnit << vshift;  		parts[numparts].size = (1 + ip->lastUnit - ip->firstUnit) << vshift;  		numparts++; -		if (ip->lastUnit > lastvunit) lastvunit = ip->lastUnit; -		if (ip->flags & INFTL_LAST) break; +		if (ip->lastUnit > lastvunit) +			lastvunit = ip->lastUnit; +		if (ip->flags & INFTL_LAST) +			break;  	}  	lastvunit++;  	if ((lastvunit << vshift) < end) { @@ -1322,7 +1336,7 @@ static inline int __init inftl_partscan(struct mtd_info *mtd,  		numparts++;  	}  	ret = numparts; -out: + out:  	kfree(buf);  	return ret;  } @@ -1334,11 +1348,12 @@ static int __init nftl_scan_bbt(struct mtd_info *mtd)  	struct doc_priv *doc = this->priv;  	struct mtd_partition parts[2]; -	memset((char *) parts, 0, sizeof(parts)); +	memset((char *)parts, 0, sizeof(parts));  	/* On NFTL, we have to find the media headers before we can read the  	   BBTs, since they're stored in the media header eraseblocks. */  	numparts = nftl_partscan(mtd, parts); -	if (!numparts) return -EIO; +	if (!numparts) +		return -EIO;  	this->bbt_td->options = NAND_BBT_ABSPAGE | NAND_BBT_8BIT |  				NAND_BBT_SAVECONTENT | NAND_BBT_WRITE |  				NAND_BBT_VERSION; @@ -1385,8 +1400,7 @@ static int __init inftl_scan_bbt(struct mtd_info *mtd)  		this->bbt_td->pages[0] = 2;  		this->bbt_md = NULL;  	} else { -		this->bbt_td->options = NAND_BBT_LASTBLOCK | NAND_BBT_8BIT | -					NAND_BBT_VERSION; +		this->bbt_td->options = NAND_BBT_LASTBLOCK | NAND_BBT_8BIT | NAND_BBT_VERSION;  		if (inftl_bbt_write)  			this->bbt_td->options |= NAND_BBT_WRITE;  		this->bbt_td->offs = 8; @@ -1396,8 +1410,7 @@ static int __init inftl_scan_bbt(struct mtd_info *mtd)  		this->bbt_td->reserved_block_code = 0x01;  		this->bbt_td->pattern = "MSYS_BBT"; -		this->bbt_md->options = NAND_BBT_LASTBLOCK | NAND_BBT_8BIT | -					NAND_BBT_VERSION; +		this->bbt_md->options = NAND_BBT_LASTBLOCK | NAND_BBT_8BIT | NAND_BBT_VERSION;  		if (inftl_bbt_write)  			this->bbt_md->options |= NAND_BBT_WRITE;  		this->bbt_md->offs = 8; @@ -1412,12 +1425,13 @@ static int __init inftl_scan_bbt(struct mtd_info *mtd)  	   At least as nand_bbt.c is currently written. */  	if ((ret = nand_scan_bbt(mtd, NULL)))  		return ret; -	memset((char *) parts, 0, sizeof(parts)); +	memset((char *)parts, 0, sizeof(parts));  	numparts = inftl_partscan(mtd, parts);  	/* At least for now, require the INFTL Media Header.  We could probably  	   do without it for non-INFTL use, since all it gives us is  	   autopartitioning, but I want to give it more thought. */ -	if (!numparts) return -EIO; +	if (!numparts) +		return -EIO;  	add_mtd_device(mtd);  #ifdef CONFIG_MTD_PARTITIONS  	if (!no_autopart) @@ -1431,7 +1445,6 @@ static inline int __init doc2000_init(struct mtd_info *mtd)  	struct nand_chip *this = mtd->priv;  	struct doc_priv *doc = this->priv; -	this->write_byte = doc2000_write_byte;  	this->read_byte = doc2000_read_byte;  	this->write_buf = doc2000_writebuf;  	this->read_buf = doc2000_readbuf; @@ -1449,7 +1462,6 @@ static inline int __init doc2001_init(struct mtd_info *mtd)  	struct nand_chip *this = mtd->priv;  	struct doc_priv *doc = this->priv; -	this->write_byte = doc2001_write_byte;  	this->read_byte = doc2001_read_byte;  	this->write_buf = doc2001_writebuf;  	this->read_buf = doc2001_readbuf; @@ -1481,16 +1493,15 @@ static inline int __init doc2001plus_init(struct mtd_info *mtd)  	struct nand_chip *this = mtd->priv;  	struct doc_priv *doc = this->priv; -	this->write_byte = NULL;  	this->read_byte = doc2001plus_read_byte;  	this->write_buf = doc2001plus_writebuf;  	this->read_buf = doc2001plus_readbuf;  	this->verify_buf = doc2001plus_verifybuf;  	this->scan_bbt = inftl_scan_bbt; -	this->hwcontrol = NULL; +	this->cmd_ctrl = NULL;  	this->select_chip = doc2001plus_select_chip;  	this->cmdfunc = doc2001plus_command; -	this->enable_hwecc = doc2001plus_enable_hwecc; +	this->ecc.hwctl = doc2001plus_enable_hwecc;  	doc->chips_per_floor = 1;  	mtd->name = "DiskOnChip Millennium Plus"; @@ -1498,7 +1509,7 @@ static inline int __init doc2001plus_init(struct mtd_info *mtd)  	return 1;  } -static inline int __init doc_probe(unsigned long physadr) +static int __init doc_probe(unsigned long physadr)  {  	unsigned char ChipID;  	struct mtd_info *mtd; @@ -1527,20 +1538,16 @@ static inline int __init doc_probe(unsigned long physadr)  	save_control = ReadDOC(virtadr, DOCControl);  	/* Reset the DiskOnChip ASIC */ -	WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_RESET, -		 virtadr, DOCControl); -	WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_RESET, -		 virtadr, DOCControl); +	WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_RESET, virtadr, DOCControl); +	WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_RESET, virtadr, DOCControl);  	/* Enable the DiskOnChip ASIC */ -	WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_NORMAL, -		 virtadr, DOCControl); -	WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_NORMAL, -		 virtadr, DOCControl); +	WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_NORMAL, virtadr, DOCControl); +	WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_NORMAL, virtadr, DOCControl);  	ChipID = ReadDOC(virtadr, ChipID); -	switch(ChipID) { +	switch (ChipID) {  	case DOC_ChipID_Doc2k:  		reg = DoC_2k_ECCStatus;  		break; @@ -1556,15 +1563,13 @@ static inline int __init doc_probe(unsigned long physadr)  			ReadDOC(virtadr, Mplus_Power);  		/* Reset the Millennium Plus ASIC */ -		tmp = DOC_MODE_RESET | DOC_MODE_MDWREN | DOC_MODE_RST_LAT | -			DOC_MODE_BDECT; +		tmp = DOC_MODE_RESET | DOC_MODE_MDWREN | DOC_MODE_RST_LAT | DOC_MODE_BDECT;  		WriteDOC(tmp, virtadr, Mplus_DOCControl);  		WriteDOC(~tmp, virtadr, Mplus_CtrlConfirm);  		mdelay(1);  		/* Enable the Millennium Plus ASIC */ -		tmp = DOC_MODE_NORMAL | DOC_MODE_MDWREN | DOC_MODE_RST_LAT | -			DOC_MODE_BDECT; +		tmp = DOC_MODE_NORMAL | DOC_MODE_MDWREN | DOC_MODE_RST_LAT | DOC_MODE_BDECT;  		WriteDOC(tmp, virtadr, Mplus_DOCControl);  		WriteDOC(~tmp, virtadr, Mplus_CtrlConfirm);  		mdelay(1); @@ -1588,7 +1593,7 @@ static inline int __init doc_probe(unsigned long physadr)  		goto notfound;  	}  	/* Check the TOGGLE bit in the ECC register */ -	tmp  = ReadDOC_(virtadr, reg) & DOC_TOGGLE_BIT; +	tmp = ReadDOC_(virtadr, reg) & DOC_TOGGLE_BIT;  	tmpb = ReadDOC_(virtadr, reg) & DOC_TOGGLE_BIT;  	tmpc = ReadDOC_(virtadr, reg) & DOC_TOGGLE_BIT;  	if ((tmp == tmpb) || (tmp != tmpc)) { @@ -1618,11 +1623,11 @@ static inline int __init doc_probe(unsigned long physadr)  		if (ChipID == DOC_ChipID_DocMilPlus16) {  			WriteDOC(~newval, virtadr, Mplus_AliasResolution);  			oldval = ReadDOC(doc->virtadr, Mplus_AliasResolution); -			WriteDOC(newval, virtadr, Mplus_AliasResolution); /* restore it */ +			WriteDOC(newval, virtadr, Mplus_AliasResolution);	// restore it  		} else {  			WriteDOC(~newval, virtadr, AliasResolution);  			oldval = ReadDOC(doc->virtadr, AliasResolution); -			WriteDOC(newval, virtadr, AliasResolution); /* restore it */ +			WriteDOC(newval, virtadr, AliasResolution);	// restore it  		}  		newval = ~newval;  		if (oldval == newval) { @@ -1634,16 +1639,13 @@ static inline int __init doc_probe(unsigned long physadr)  	printk(KERN_NOTICE "DiskOnChip found at 0x%lx\n", physadr);  	len = sizeof(struct mtd_info) + -	      sizeof(struct nand_chip) + -	      sizeof(struct doc_priv) + -	      (2 * sizeof(struct nand_bbt_descr)); -	mtd =  kmalloc(len, GFP_KERNEL); +	    sizeof(struct nand_chip) + sizeof(struct doc_priv) + (2 * sizeof(struct nand_bbt_descr)); +	mtd = kzalloc(len, GFP_KERNEL);  	if (!mtd) {  		printk(KERN_ERR "DiskOnChip kmalloc (%d bytes) failed!\n", len);  		ret = -ENOMEM;  		goto fail;  	} -	memset(mtd, 0, len);  	nand			= (struct nand_chip *) (mtd + 1);  	doc			= (struct doc_priv *) (nand + 1); @@ -1655,17 +1657,19 @@ static inline int __init doc_probe(unsigned long physadr)  	nand->priv		= doc;  	nand->select_chip	= doc200x_select_chip; -	nand->hwcontrol		= doc200x_hwcontrol; +	nand->cmd_ctrl		= doc200x_hwcontrol;  	nand->dev_ready		= doc200x_dev_ready;  	nand->waitfunc		= doc200x_wait;  	nand->block_bad		= doc200x_block_bad; -	nand->enable_hwecc	= doc200x_enable_hwecc; -	nand->calculate_ecc	= doc200x_calculate_ecc; -	nand->correct_data	= doc200x_correct_data; +	nand->ecc.hwctl		= doc200x_enable_hwecc; +	nand->ecc.calculate	= doc200x_calculate_ecc; +	nand->ecc.correct	= doc200x_correct_data; -	nand->autooob		= &doc200x_oobinfo; -	nand->eccmode		= NAND_ECC_HW6_512; -	nand->options		= NAND_USE_FLASH_BBT | NAND_HWECC_SYNDROME; +	nand->ecc.layout	= &doc200x_oobinfo; +	nand->ecc.mode		= NAND_ECC_HW_SYNDROME; +	nand->ecc.size		= 512; +	nand->ecc.bytes		= 6; +	nand->options		= NAND_USE_FLASH_BBT;  	doc->physadr		= physadr;  	doc->virtadr		= virtadr; @@ -1699,11 +1703,11 @@ static inline int __init doc_probe(unsigned long physadr)  	doclist = mtd;  	return 0; -notfound: + notfound:  	/* Put back the contents of the DOCControl register, in case it's not  	   actually a DiskOnChip.  */  	WriteDOC(save_control, virtadr, DOCControl); -fail: + fail:  	iounmap(virtadr);  	return ret;  } @@ -1740,7 +1744,7 @@ static int __init init_nanddoc(void)  	 */  	rs_decoder = init_rs(10, 0x409, FCR, 1, NROOTS);  	if (!rs_decoder) { -		printk (KERN_ERR "DiskOnChip: Could not create a RS decoder\n"); +		printk(KERN_ERR "DiskOnChip: Could not create a RS decoder\n");  		return -ENOMEM;  	} @@ -1750,7 +1754,7 @@ static int __init init_nanddoc(void)  		if (ret < 0)  			goto outerr;  	} else { -		for (i=0; (doc_locations[i] != 0xffffffff); i++) { +		for (i = 0; (doc_locations[i] != 0xffffffff); i++) {  			doc_probe(doc_locations[i]);  		}  	} @@ -1762,7 +1766,7 @@ static int __init init_nanddoc(void)  		goto outerr;  	}  	return 0; -outerr: + outerr:  	free_rs(rs_decoder);  	return ret;  } diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 6416d1529..aeb179731 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) +static uint8_t 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) -{ -	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) +static uint8_t 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) -{ -	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,47 @@ 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->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; +	nand_wait(mtd, chip); +} +#endif +  /**   * nand_command - [DEFAULT] Send command to NAND device   * @mtd:	MTD device structure @@ -542,21 +503,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 +526,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 +567,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 +602,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 +656,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 +726,96 @@ 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;  	} -	set_current_state (TASK_UNINTERRUPTIBLE); -	add_wait_queue (&active->wq, &wait); -	spin_unlock (&active->chip_lock); -	schedule (); -	remove_wait_queue (&active->wq, &wait); +	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(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) +{ +	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 +853,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]; +	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; -	hweccbytes = (this->options & NAND_HWECC_SYNDROME) ? (oobsel->eccbytes / eccsteps) : 0; - -	/* Send command to read back the first page */ -	this->cmdfunc (mtd, NAND_CMD_READ0, 0, page); - -	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; 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); -		/* 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); +	for (i = 0; i < chip->ecc.total; i++) +		ecc_code[i] = chip->oob_poi[eccpos[i]]; -			if (oobsel->useecc != MTD_NANDECC_OFF && !hweccbytes) { -				int ecccnt = oobsel->eccbytes; +	eccsteps = chip->ecc.steps; +	p = buf; -				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--; +	for (i = 0 ; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { +		int stat; -		/* 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; - - -	MTDDEBUG (MTD_DEBUG_LEVEL3, "nand_read_ecc: from = 0x%08x, len = %i\n", -	          (unsigned int) from, (int) len); - -	/* 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; -	} +	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; -	/* Grab the lock and see if the device is available */ -	nand_get_device (this, mtd ,FL_READING); +	for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { +		int stat; -	/* use userspace supplied oobinfo, if zero */ -	if (oobsel == NULL) -		oobsel = &mtd->oobinfo; +		chip->ecc.hwctl(mtd, NAND_ECC_READ); +		chip->read_buf(mtd, p, eccsize); -	/* Autoplace of oob data ? Use the default placement scheme */ -	if (oobsel->useecc == MTD_NANDECC_AUTOPLACE) -		oobsel = this->autooob; - -	eccmode = oobsel->useecc ? this->eccmode : NAND_ECC_NONE; -	oob_config = oobsel->eccpos; +		if (chip->ecc.prepad) { +			chip->read_buf(mtd, oob, chip->ecc.prepad); +			oob += chip->ecc.prepad; +		} -	/* Select the NAND device */ -	chipnr = (int)(from >> this->chip_shift); -	this->select_chip(mtd, chipnr); +		chip->ecc.hwctl(mtd, NAND_ECC_READSYN); +		chip->read_buf(mtd, oob, eccbytes); +		stat = chip->ecc.correct(mtd, p, oob, NULL); -	/* First we calculate the starting page */ -	realpage = (int) (from >> this->page_shift); -	page = realpage & this->pagemask; +		if (stat == -1) +			mtd->ecc_stats.failed++; +		else +			mtd->ecc_stats.corrected += stat; -	/* Get raw starting column */ -	col = from & (mtd->oobblock - 1); +		oob += eccbytes; -	end = mtd->oobblock; -	ecc = this->eccsize; -	eccbytes = this->eccbytes; +		if (chip->ecc.postpad) { +			chip->read_buf(mtd, oob, chip->ecc.postpad); +			oob += chip->ecc.postpad; +		} +	} -	if ((eccmode == NAND_ECC_NONE) || (this->options & NAND_HWECC_SYNDROME)) -		compareecc = 0; +	/* Calculate remaining oob bytes */ +	i = mtd->oobsize - (oob - chip->oob_poi); +	if (i) +		chip->read_buf(mtd, oob, i); -	oobreadlen = mtd->oobsize; -	if (this->options & NAND_HWECC_SYNDROME) -		oobreadlen -= oobsel->eccbytes; +	return 0; +} -	/* Loop until all data read */ -	while (read < len) { +/** + * 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) { -		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_PLACE: +	case MTD_OOB_RAW: +		memcpy(oob, chip->oob_poi + ops->ooboffs, len); +		return oob + len; -		/* 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; -		} +	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 must send the read command */ -		if (sndcmd) { -			this->cmdfunc (mtd, NAND_CMD_READ0, 0x00, page); -			sndcmd = 0; +		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; +} -		/* 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]; - -		eccsteps = this->eccsteps; +/** + * 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; -		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; -		} +	stats = mtd->ecc_stats; -		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; +	chipnr = (int)(from >> chip->chip_shift); +	chip->select_chip(mtd, chipnr); -		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); +	realpage = (int)(from >> chip->page_shift); +	page = realpage & chip->pagemask; -				/* 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; -		} +	col = (int)(from & (mtd->writesize - 1)); -		/* read oobdata */ -		this->read_buf(mtd, &oob_data[mtd->oobsize - oobreadlen], oobreadlen); +	buf = ops->datbuf; +	oob = ops->oobbuf; -		/* 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 +1159,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; + +	/* +	 * 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; -			/* 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); +				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); -		this->read_buf (mtd, &buf[cnt], pagesize); +		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); +		} -		len -= pagesize; -		cnt += pagesize; -		page++; +		readlen -= len; +		if (!readlen) +			break; -		if (!this->dev_ready) -			udelay (this->chip_delay); -		else -			while (!this->dev_ready(mtd)); +		/* Increment page address */ +		realpage++; -		/* Check, if the chip supports auto page increment */ -		if (!NAND_CANAUTOINCR(this) || !(page & blockcheck)) +		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 +		 * 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); + +		if (chip->ecc.prepad) { +			chip->write_buf(mtd, oob, chip->ecc.prepad); +			oob += chip->ecc.prepad; +		} -	/* Autoplace of oob data ? Use the default placement scheme */ -	if (oobsel->useecc == MTD_NANDECC_AUTOPLACE) { -		oobsel = this->autooob; -		autoplace = 1; +		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); +} -	/* 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; +/** + * 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; -	/* Loop until all data is written */ -	while (written < len) { +	chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page); -		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. +	if (unlikely(raw)) +		chip->ecc.write_page_raw(mtd, chip, buf); +	else +		chip->ecc.write_page(mtd, chip, buf); + +	/* +	 * 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); + +	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) { -			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); +	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; + +	/* Invalidate the page cache, if we write to the cached page */ +	if (page == chip->pagebuf) +		chip->pagebuf = -1; -	/* if oobsel is NULL, use chip defaults */ -	if (oobsel == NULL) -		oobsel = &mtd->oobinfo; +	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); -	/* 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; +	if (status) +		return status; -	/* 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; +	ops->oobretlen = ops->ooblen; -	startpage = page & this->pagemask; +	return 0; +} -	/* 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++; -		} +/** + * 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; -		this->data_poi = bufstart; -		ret = nand_verify_pages (mtd, this, startpage, numpages, oobbuf, oobsel, chipnr, 0); -		if (ret) -			goto out; +	ops->retlen = 0; -		written += mtd->oobblock * numpages; -		/* All done ? */ -		if (!count) -			break; +	/* 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; +	} -		startpage = page & this->pagemask; -		/* Check, if we cross a chip boundary */ -		if (!startpage) { -			chipnr++; -			this->select_chip(mtd, -1); -			this->select_chip(mtd, chipnr); -		} +	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;  	} -	ret = 0; -out: -	/* Deselect and wake up anyone waiting on the device */ -	nand_release_device(mtd); -	*retlen = written; +	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 +1990,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 +2006,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 +2024,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 +2072,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 +2092,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 +2187,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 +2214,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 +2257,553 @@ 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; + +	return nand_get_device(chip, mtd, FL_PM_SUSPENDED); +} -	/* Get buswidth to select the correct functions*/ -	busw = this->options & NAND_BUSWIDTH_16; +/** + * 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 (!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 +	} + +} -	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; +/* + * 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; + +	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, 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); -/*			BUG(); */ +			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) { +			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) +		return 0; + +	/* Build bad block table */ +	return chip->scan_bbt(mtd); +} + +/* 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 + diff --git a/drivers/mtd/nand/nand_bbt.c b/drivers/mtd/nand/nand_bbt.c index a97743b45..acf1cf543 100644 --- a/drivers/mtd/nand/nand_bbt.c +++ b/drivers/mtd/nand/nand_bbt.c @@ -6,7 +6,7 @@   *   *  Copyright (C) 2004 Thomas Gleixner (tglx@linutronix.de)   * - * $Id: nand_bbt.c,v 1.28 2004/11/13 10:19:09 gleixner Exp $ + * $Id: nand_bbt.c,v 1.36 2005/11/07 11:14:30 gleixner Exp $   *   * This program is free software; you can redistribute it and/or modify   * it under the terms of the GNU General Public License version 2 as @@ -48,7 +48,7 @@   *   * Following assumptions are made:   * - bbts start at a page boundary, if autolocated on a block boundary - * - the space neccecary for a bbt in FLASH does not exceed a block boundary + * - the space necessary for a bbt in FLASH does not exceed a block boundary   *   */ @@ -63,6 +63,19 @@  #include <asm/errno.h> +/* XXX U-BOOT XXX */ +#if 0 +#include <linux/slab.h> +#include <linux/types.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/nand.h> +#include <linux/mtd/nand_ecc.h> +#include <linux/mtd/compatmac.h> +#include <linux/bitops.h> +#include <linux/delay.h> +#include <linux/vmalloc.h> +#endif +  /**   * check_pattern - [GENERIC] check if a pattern is in the buffer   * @buf:	the buffer to search @@ -76,9 +89,9 @@   * pattern area contain 0xff   *  */ -static int check_pattern (uint8_t *buf, int len, int paglen, struct nand_bbt_descr *td) +static int check_pattern(uint8_t *buf, int len, int paglen, struct nand_bbt_descr *td)  { -	int i, end; +	int i, end = 0;  	uint8_t *p = buf;  	end = paglen + td->offs; @@ -96,9 +109,9 @@ static int check_pattern (uint8_t *buf, int len, int paglen, struct nand_bbt_des  			return -1;  	} -	p += td->len; -	end += td->len;  	if (td->options & NAND_BBT_SCANEMPTY) { +		p += td->len; +		end += td->len;  		for (i = end; i < len; i++) {  			if (*p++ != 0xff)  				return -1; @@ -108,6 +121,29 @@ static int check_pattern (uint8_t *buf, int len, int paglen, struct nand_bbt_des  }  /** + * check_short_pattern - [GENERIC] check if a pattern is in the buffer + * @buf:	the buffer to search + * @td:		search pattern descriptor + * + * Check for a pattern at the given place. Used to search bad block + * tables and good / bad block identifiers. Same as check_pattern, but + * no optional empty check + * +*/ +static int check_short_pattern(uint8_t *buf, struct nand_bbt_descr *td) +{ +	int i; +	uint8_t *p = buf; + +	/* Compare the pattern */ +	for (i = 0; i < td->len; i++) { +		if (p[td->offs + i] != td->pattern[i]) +			return -1; +	} +	return 0; +} + +/**   * read_bbt - [GENERIC] Read the bad block table starting from page   * @mtd:	MTD device structure   * @buf:	temporary buffer @@ -120,8 +156,8 @@ static int check_pattern (uint8_t *buf, int len, int paglen, struct nand_bbt_des   * Read the bad block table starting from page.   *   */ -static int read_bbt (struct mtd_info *mtd, uint8_t *buf, int page, int num, -	int bits, int offs, int reserved_block_code) +static int read_bbt(struct mtd_info *mtd, uint8_t *buf, int page, int num, +		    int bits, int offs, int reserved_block_code)  {  	int res, i, j, act = 0;  	struct nand_chip *this = mtd->priv; @@ -130,17 +166,17 @@ static int read_bbt (struct mtd_info *mtd, uint8_t *buf, int page, int num,  	uint8_t msk = (uint8_t) ((1 << bits) - 1);  	totlen = (num * bits) >> 3; -	from = ((loff_t)page) << this->page_shift; +	from = ((loff_t) page) << this->page_shift;  	while (totlen) { -		len = min (totlen, (size_t) (1 << this->bbt_erase_shift)); -		res = mtd->read_ecc (mtd, from, len, &retlen, buf, NULL, this->autooob); +		len = min(totlen, (size_t) (1 << this->bbt_erase_shift)); +		res = mtd->read(mtd, from, len, &retlen, buf);  		if (res < 0) {  			if (retlen != len) { -				printk (KERN_INFO "nand_bbt: Error reading bad block table\n"); +				printk(KERN_INFO "nand_bbt: Error reading bad block table\n");  				return res;  			} -			printk (KERN_WARNING "nand_bbt: ECC error while reading bad block table\n"); +			printk(KERN_WARNING "nand_bbt: ECC error while reading bad block table\n");  		}  		/* Analyse data */ @@ -150,22 +186,23 @@ static int read_bbt (struct mtd_info *mtd, uint8_t *buf, int page, int num,  				uint8_t tmp = (dat >> j) & msk;  				if (tmp == msk)  					continue; -				if (reserved_block_code && -				    (tmp == reserved_block_code)) { -					printk (KERN_DEBUG "nand_read_bbt: Reserved block at 0x%08x\n", -						((offs << 2) + (act >> 1)) << this->bbt_erase_shift); +				if (reserved_block_code && (tmp == reserved_block_code)) { +					printk(KERN_DEBUG "nand_read_bbt: Reserved block at 0x%08x\n", +					       ((offs << 2) + (act >> 1)) << this->bbt_erase_shift);  					this->bbt[offs + (act >> 3)] |= 0x2 << (act & 0x06); +					mtd->ecc_stats.bbtblocks++;  					continue;  				}  				/* Leave it for now, if its matured we can move this  				 * message to MTD_DEBUG_LEVEL0 */ -				printk (KERN_DEBUG "nand_read_bbt: Bad block at 0x%08x\n", -					((offs << 2) + (act >> 1)) << this->bbt_erase_shift); +				printk(KERN_DEBUG "nand_read_bbt: Bad block at 0x%08x\n", +				       ((offs << 2) + (act >> 1)) << this->bbt_erase_shift);  				/* Factory marked bad or worn out ? */  				if (tmp == 0)  					this->bbt[offs + (act >> 3)] |= 0x3 << (act & 0x06);  				else  					this->bbt[offs + (act >> 3)] |= 0x1 << (act & 0x06); +				mtd->ecc_stats.badblocks++;  			}  		}  		totlen -= len; @@ -185,7 +222,7 @@ static int read_bbt (struct mtd_info *mtd, uint8_t *buf, int page, int num,   * Read the bad block table for all chips starting at a given page   * We assume that the bbt bits are in consecutive order.  */ -static int read_abs_bbt (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *td, int chip) +static int read_abs_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *td, int chip)  {  	struct nand_chip *this = mtd->priv;  	int res = 0, i; @@ -209,6 +246,42 @@ static int read_abs_bbt (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_des  	return 0;  } +/* + * Scan read raw data from flash + */ +static int scan_read_raw(struct mtd_info *mtd, uint8_t *buf, loff_t offs, +			 size_t len) +{ +	struct mtd_oob_ops ops; + +	ops.mode = MTD_OOB_RAW; +	ops.ooboffs = 0; +	ops.ooblen = mtd->oobsize; +	ops.oobbuf = buf; +	ops.datbuf = buf; +	ops.len = len; + +	return mtd->read_oob(mtd, offs, &ops); +} + +/* + * Scan write data with oob to flash + */ +static int scan_write_bbt(struct mtd_info *mtd, loff_t offs, size_t len, +			  uint8_t *buf, uint8_t *oob) +{ +	struct mtd_oob_ops ops; + +	ops.mode = MTD_OOB_PLACE; +	ops.ooboffs = 0; +	ops.ooblen = mtd->oobsize; +	ops.datbuf = buf; +	ops.oobbuf = oob; +	ops.len = len; + +	return mtd->write_oob(mtd, offs, &ops); +} +  /**   * read_abs_bbts - [GENERIC] Read the bad block table(s) for all chips starting at a given page   * @mtd:	MTD device structure @@ -220,28 +293,84 @@ static int read_abs_bbt (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_des   * We assume that the bbt bits are in consecutive order.   *  */ -static int read_abs_bbts (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *td, -	struct nand_bbt_descr *md) +static int read_abs_bbts(struct mtd_info *mtd, uint8_t *buf, +			 struct nand_bbt_descr *td, struct nand_bbt_descr *md)  {  	struct nand_chip *this = mtd->priv;  	/* Read the primary version, if available */  	if (td->options & NAND_BBT_VERSION) { -		nand_read_raw (mtd, buf, td->pages[0] << this->page_shift, mtd->oobblock, mtd->oobsize); -		td->version[0] = buf[mtd->oobblock + td->veroffs]; -		printk (KERN_DEBUG "Bad block table at page %d, version 0x%02X\n", td->pages[0], td->version[0]); +		scan_read_raw(mtd, buf, td->pages[0] << this->page_shift, +			      mtd->writesize); +		td->version[0] = buf[mtd->writesize + td->veroffs]; +		printk(KERN_DEBUG "Bad block table at page %d, version 0x%02X\n", +		       td->pages[0], td->version[0]);  	}  	/* Read the mirror version, if available */  	if (md && (md->options & NAND_BBT_VERSION)) { -		nand_read_raw (mtd, buf, md->pages[0] << this->page_shift, mtd->oobblock, mtd->oobsize); -		md->version[0] = buf[mtd->oobblock + md->veroffs]; -		printk (KERN_DEBUG "Bad block table at page %d, version 0x%02X\n", md->pages[0], md->version[0]); +		scan_read_raw(mtd, buf, md->pages[0] << this->page_shift, +			      mtd->writesize); +		md->version[0] = buf[mtd->writesize + md->veroffs]; +		printk(KERN_DEBUG "Bad block table at page %d, version 0x%02X\n", +		       md->pages[0], md->version[0]);  	} -  	return 1;  } +/* + * Scan a given block full + */ +static int scan_block_full(struct mtd_info *mtd, struct nand_bbt_descr *bd, +			   loff_t offs, uint8_t *buf, size_t readlen, +			   int scanlen, int len) +{ +	int ret, j; + +	ret = scan_read_raw(mtd, buf, offs, readlen); +	if (ret) +		return ret; + +	for (j = 0; j < len; j++, buf += scanlen) { +		if (check_pattern(buf, scanlen, mtd->writesize, bd)) +			return 1; +	} +	return 0; +} + +/* + * Scan a given block partially + */ +static int scan_block_fast(struct mtd_info *mtd, struct nand_bbt_descr *bd, +			   loff_t offs, uint8_t *buf, int len) +{ +	struct mtd_oob_ops ops; +	int j, ret; + +	ops.ooblen = mtd->oobsize; +	ops.oobbuf = buf; +	ops.ooboffs = 0; +	ops.datbuf = NULL; +	ops.mode = MTD_OOB_PLACE; + +	for (j = 0; j < len; j++) { +		/* +		 * Read the full oob until read_oob is fixed to +		 * handle single byte reads for 16 bit +		 * buswidth +		 */ +		ret = mtd->read_oob(mtd, offs, &ops); +		if (ret) +			return ret; + +		if (check_short_pattern(buf, bd)) +			return 1; + +		offs += mtd->writesize; +	} +	return 0; +} +  /**   * create_bbt - [GENERIC] Create a bad block table by scanning the device   * @mtd:	MTD device structure @@ -253,13 +382,16 @@ static int read_abs_bbts (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_de   * Create a bad block table by scanning the device   * for the given good/bad block identify pattern   */ -static void create_bbt (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *bd, int chip) +static int create_bbt(struct mtd_info *mtd, uint8_t *buf, +	struct nand_bbt_descr *bd, int chip)  {  	struct nand_chip *this = mtd->priv; -	int i, j, numblocks, len, scanlen; +	int i, numblocks, len, scanlen;  	int startblock;  	loff_t from; -	size_t readlen, ooblen; +	size_t readlen; + +	printk(KERN_INFO "Scanning device for bad blocks\n");  	if (bd->options & NAND_BBT_SCANALLPAGES)  		len = 1 << (this->bbt_erase_shift - this->page_shift); @@ -269,21 +401,28 @@ static void create_bbt (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_desc  		else  			len = 1;  	} -	scanlen	= mtd->oobblock + mtd->oobsize; -	readlen = len * mtd->oobblock; -	ooblen = len * mtd->oobsize; + +	if (!(bd->options & NAND_BBT_SCANEMPTY)) { +		/* We need only read few bytes from the OOB area */ +		scanlen = 0; +		readlen = bd->len; +	} else { +		/* Full page content should be read */ +		scanlen = mtd->writesize + mtd->oobsize; +		readlen = len * mtd->writesize; +	}  	if (chip == -1) { -		/* Note that numblocks is 2 * (real numblocks) here, see i+=2 below as it -		 * makes shifting and masking less painful */ +		/* Note that numblocks is 2 * (real numblocks) here, see i+=2 +		 * below as it makes shifting and masking less painful */  		numblocks = mtd->size >> (this->bbt_erase_shift - 1);  		startblock = 0;  		from = 0;  	} else {  		if (chip >= this->numchips) { -			printk (KERN_WARNING "create_bbt(): chipnr (%d) > available chips (%d)\n", -				chip + 1, this->numchips); -			return; +			printk(KERN_WARNING "create_bbt(): chipnr (%d) > available chips (%d)\n", +			       chip + 1, this->numchips); +			return -EINVAL;  		}  		numblocks = this->chipsize >> (this->bbt_erase_shift - 1);  		startblock = chip * numblocks; @@ -292,16 +431,28 @@ static void create_bbt (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_desc  	}  	for (i = startblock; i < numblocks;) { -		nand_read_raw (mtd, buf, from, readlen, ooblen); -		for (j = 0; j < len; j++) { -			if (check_pattern (&buf[j * scanlen], scanlen, mtd->oobblock, bd)) { -				this->bbt[i >> 3] |= 0x03 << (i & 0x6); -				break; -			} +		int ret; + +		if (bd->options & NAND_BBT_SCANALLPAGES) +			ret = scan_block_full(mtd, bd, from, buf, readlen, +					      scanlen, len); +		else +			ret = scan_block_fast(mtd, bd, from, buf, len); + +		if (ret < 0) +			return ret; + +		if (ret) { +			this->bbt[i >> 3] |= 0x03 << (i & 0x6); +			printk(KERN_WARNING "Bad eraseblock %d at 0x%08x\n", +			       i >> 1, (unsigned int)from); +			mtd->ecc_stats.badblocks++;  		} +  		i += 2;  		from += (1 << this->bbt_erase_shift);  	} +	return 0;  }  /** @@ -316,22 +467,23 @@ static void create_bbt (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_desc   * block.   * If the option NAND_BBT_PERCHIP is given, each chip is searched   * for a bbt, which contains the bad block information of this chip. - * This is neccecary to provide support for certain DOC devices. + * This is necessary to provide support for certain DOC devices.   *   * The bbt ident pattern resides in the oob area of the first page   * in a block.   */ -static int search_bbt (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *td) +static int search_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *td)  {  	struct nand_chip *this = mtd->priv;  	int i, chips;  	int bits, startblock, block, dir; -	int scanlen = mtd->oobblock + mtd->oobsize; +	int scanlen = mtd->writesize + mtd->oobsize;  	int bbtblocks; +	int blocktopage = this->bbt_erase_shift - this->page_shift;  	/* Search direction top -> down ? */  	if (td->options & NAND_BBT_LASTBLOCK) { -		startblock = (mtd->size >> this->bbt_erase_shift) -1; +		startblock = (mtd->size >> this->bbt_erase_shift) - 1;  		dir = -1;  	} else {  		startblock = 0; @@ -357,13 +509,16 @@ static int search_bbt (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr  		td->pages[i] = -1;  		/* Scan the maximum number of blocks */  		for (block = 0; block < td->maxblocks; block++) { +  			int actblock = startblock + dir * block; +			loff_t offs = actblock << this->bbt_erase_shift; +  			/* Read first page */ -			nand_read_raw (mtd, buf, actblock << this->bbt_erase_shift, mtd->oobblock, mtd->oobsize); -			if (!check_pattern(buf, scanlen, mtd->oobblock, td)) { -				td->pages[i] = actblock << (this->bbt_erase_shift - this->page_shift); +			scan_read_raw(mtd, buf, offs, mtd->writesize); +			if (!check_pattern(buf, scanlen, mtd->writesize, td)) { +				td->pages[i] = actblock << blocktopage;  				if (td->options & NAND_BBT_VERSION) { -					td->version[i] = buf[mtd->oobblock + td->veroffs]; +					td->version[i] = buf[mtd->writesize + td->veroffs];  				}  				break;  			} @@ -373,9 +528,10 @@ static int search_bbt (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr  	/* Check, if we found a bbt for each requested chip */  	for (i = 0; i < chips; i++) {  		if (td->pages[i] == -1) -			printk (KERN_WARNING "Bad block table not found for chip %d\n", i); +			printk(KERN_WARNING "Bad block table not found for chip %d\n", i);  		else -			printk (KERN_DEBUG "Bad block table found at page %d, version 0x%02X\n", td->pages[i], td->version[i]); +			printk(KERN_DEBUG "Bad block table found at page %d, version 0x%02X\n", td->pages[i], +			       td->version[i]);  	}  	return 0;  } @@ -389,21 +545,19 @@ static int search_bbt (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr   *   * Search and read the bad block table(s)  */ -static int search_read_bbts (struct mtd_info *mtd, uint8_t *buf, -	struct nand_bbt_descr *td, struct nand_bbt_descr *md) +static int search_read_bbts(struct mtd_info *mtd, uint8_t * buf, struct nand_bbt_descr *td, struct nand_bbt_descr *md)  {  	/* Search the primary table */ -	search_bbt (mtd, buf, td); +	search_bbt(mtd, buf, td);  	/* Search the mirror table */  	if (md) -		search_bbt (mtd, buf, md); +		search_bbt(mtd, buf, md);  	/* Force result check */  	return 1;  } -  /**   * write_bbt - [GENERIC] (Re)write the bad block table   * @@ -416,25 +570,31 @@ static int search_read_bbts (struct mtd_info *mtd, uint8_t *buf,   * (Re)write the bad block table   *  */ -static int write_bbt (struct mtd_info *mtd, uint8_t *buf, -	struct nand_bbt_descr *td, struct nand_bbt_descr *md, int chipsel) +static int write_bbt(struct mtd_info *mtd, uint8_t *buf, +		     struct nand_bbt_descr *td, struct nand_bbt_descr *md, +		     int chipsel)  {  	struct nand_chip *this = mtd->priv; -	struct nand_oobinfo oobinfo;  	struct erase_info einfo;  	int i, j, res, chip = 0;  	int bits, startblock, dir, page, offs, numblocks, sft, sftmsk; -	int nrchips, bbtoffs, pageoffs; +	int nrchips, bbtoffs, pageoffs, ooboffs;  	uint8_t msk[4];  	uint8_t rcode = td->reserved_block_code;  	size_t retlen, len = 0;  	loff_t to; +	struct mtd_oob_ops ops; + +	ops.ooblen = mtd->oobsize; +	ops.ooboffs = 0; +	ops.datbuf = NULL; +	ops.mode = MTD_OOB_PLACE;  	if (!rcode)  		rcode = 0xff;  	/* Write bad block table per chip rather than per device ? */  	if (td->options & NAND_BBT_PERCHIP) { -		numblocks = (int) (this->chipsize >> this->bbt_erase_shift); +		numblocks = (int)(this->chipsize >> this->bbt_erase_shift);  		/* Full device write or specific chip ? */  		if (chipsel == -1) {  			nrchips = this->numchips; @@ -443,7 +603,7 @@ static int write_bbt (struct mtd_info *mtd, uint8_t *buf,  			chip = chipsel;  		}  	} else { -		numblocks = (int) (mtd->size >> this->bbt_erase_shift); +		numblocks = (int)(mtd->size >> this->bbt_erase_shift);  		nrchips = 1;  	} @@ -472,27 +632,38 @@ static int write_bbt (struct mtd_info *mtd, uint8_t *buf,  		for (i = 0; i < td->maxblocks; i++) {  			int block = startblock + dir * i;  			/* Check, if the block is bad */ -			switch ((this->bbt[block >> 2] >> (2 * (block & 0x03))) & 0x03) { +			switch ((this->bbt[block >> 2] >> +				 (2 * (block & 0x03))) & 0x03) {  			case 0x01:  			case 0x03:  				continue;  			} -			page = block << (this->bbt_erase_shift - this->page_shift); +			page = block << +				(this->bbt_erase_shift - this->page_shift);  			/* Check, if the block is used by the mirror table */  			if (!md || md->pages[chip] != page)  				goto write;  		} -		printk (KERN_ERR "No space left to write bad block table\n"); +		printk(KERN_ERR "No space left to write bad block table\n");  		return -ENOSPC; -write: +	write:  		/* Set up shift count and masks for the flash table */  		bits = td->options & NAND_BBT_NRBITS_MSK; +		msk[2] = ~rcode;  		switch (bits) { -		case 1: sft = 3; sftmsk = 0x07; msk[0] = 0x00; msk[1] = 0x01; msk[2] = ~rcode; msk[3] = 0x01; break; -		case 2: sft = 2; sftmsk = 0x06; msk[0] = 0x00; msk[1] = 0x01; msk[2] = ~rcode; msk[3] = 0x03; break; -		case 4: sft = 1; sftmsk = 0x04; msk[0] = 0x00; msk[1] = 0x0C; msk[2] = ~rcode; msk[3] = 0x0f; break; -		case 8: sft = 0; sftmsk = 0x00; msk[0] = 0x00; msk[1] = 0x0F; msk[2] = ~rcode; msk[3] = 0xff; break; +		case 1: sft = 3; sftmsk = 0x07; msk[0] = 0x00; msk[1] = 0x01; +			msk[3] = 0x01; +			break; +		case 2: sft = 2; sftmsk = 0x06; msk[0] = 0x00; msk[1] = 0x01; +			msk[3] = 0x03; +			break; +		case 4: sft = 1; sftmsk = 0x04; msk[0] = 0x00; msk[1] = 0x0C; +			msk[3] = 0x0f; +			break; +		case 8: sft = 0; sftmsk = 0x00; msk[0] = 0x00; msk[1] = 0x0F; +			msk[3] = 0xff; +			break;  		default: return -EINVAL;  		} @@ -500,82 +671,92 @@ write:  		to = ((loff_t) page) << this->page_shift; -		memcpy (&oobinfo, this->autooob, sizeof(oobinfo)); -		oobinfo.useecc = MTD_NANDECC_PLACEONLY; -  		/* Must we save the block contents ? */  		if (td->options & NAND_BBT_SAVECONTENT) {  			/* Make it block aligned */  			to &= ~((loff_t) ((1 << this->bbt_erase_shift) - 1));  			len = 1 << this->bbt_erase_shift; -			res = mtd->read_ecc (mtd, to, len, &retlen, buf, &buf[len], &oobinfo); +			res = mtd->read(mtd, to, len, &retlen, buf);  			if (res < 0) {  				if (retlen != len) { -					printk (KERN_INFO "nand_bbt: Error reading block for writing the bad block table\n"); +					printk(KERN_INFO "nand_bbt: Error " +					       "reading block for writing " +					       "the bad block table\n");  					return res;  				} -				printk (KERN_WARNING "nand_bbt: ECC error while reading block for writing bad block table\n"); +				printk(KERN_WARNING "nand_bbt: ECC error " +				       "while reading block for writing " +				       "bad block table\n");  			} +			/* Read oob data */ +			ops.ooblen = (len >> this->page_shift) * mtd->oobsize; +			ops.oobbuf = &buf[len]; +			res = mtd->read_oob(mtd, to + mtd->writesize, &ops); +			if (res < 0 || ops.oobretlen != ops.ooblen) +				goto outerr; +  			/* Calc the byte offset in the buffer */  			pageoffs = page - (int)(to >> this->page_shift);  			offs = pageoffs << this->page_shift;  			/* Preset the bbt area with 0xff */ -			memset (&buf[offs], 0xff, (size_t)(numblocks >> sft)); -			/* Preset the bbt's oob area with 0xff */ -			memset (&buf[len + pageoffs * mtd->oobsize], 0xff, -				((len >> this->page_shift) - pageoffs) * mtd->oobsize); -			if (td->options & NAND_BBT_VERSION) { -				buf[len + (pageoffs * mtd->oobsize) + td->veroffs] = td->version[chip]; -			} +			memset(&buf[offs], 0xff, (size_t) (numblocks >> sft)); +			ooboffs = len + (pageoffs * mtd->oobsize); +  		} else {  			/* Calc length */  			len = (size_t) (numblocks >> sft);  			/* Make it page aligned ! */ -			len = (len + (mtd->oobblock-1)) & ~(mtd->oobblock-1); +			len = (len + (mtd->writesize - 1)) & +				~(mtd->writesize - 1);  			/* Preset the buffer with 0xff */ -			memset (buf, 0xff, len + (len >> this->page_shift) * mtd->oobsize); +			memset(buf, 0xff, len + +			       (len >> this->page_shift)* mtd->oobsize);  			offs = 0; +			ooboffs = len;  			/* Pattern is located in oob area of first page */ -			memcpy (&buf[len + td->offs], td->pattern, td->len); -			if (td->options & NAND_BBT_VERSION) { -				buf[len + td->veroffs] = td->version[chip]; -			} +			memcpy(&buf[ooboffs + td->offs], td->pattern, td->len);  		} +		if (td->options & NAND_BBT_VERSION) +			buf[ooboffs + td->veroffs] = td->version[chip]; +  		/* walk through the memory table */ -		for (i = 0; i < numblocks; ) { +		for (i = 0; i < numblocks;) {  			uint8_t dat;  			dat = this->bbt[bbtoffs + (i >> 2)]; -			for (j = 0; j < 4; j++ , i++) { +			for (j = 0; j < 4; j++, i++) {  				int sftcnt = (i << (3 - sft)) & sftmsk;  				/* Do not store the reserved bbt blocks ! */ -				buf[offs + (i >> sft)] &= ~(msk[dat & 0x03] << sftcnt); +				buf[offs + (i >> sft)] &= +					~(msk[dat & 0x03] << sftcnt);  				dat >>= 2;  			}  		} -		memset (&einfo, 0, sizeof (einfo)); +		memset(&einfo, 0, sizeof(einfo));  		einfo.mtd = mtd; -		einfo.addr = (unsigned long) to; +		einfo.addr = (unsigned long)to;  		einfo.len = 1 << this->bbt_erase_shift; -		res = nand_erase_nand (mtd, &einfo, 1); -		if (res < 0) { -			printk (KERN_WARNING "nand_bbt: Error during block erase: %d\n", res); -			return res; -		} +		res = nand_erase_nand(mtd, &einfo, 1); +		if (res < 0) +			goto outerr; -		res = mtd->write_ecc (mtd, to, len, &retlen, buf, &buf[len], &oobinfo); -		if (res < 0) { -			printk (KERN_WARNING "nand_bbt: Error while writing bad block table %d\n", res); -			return res; -		} -		printk (KERN_DEBUG "Bad block table written to 0x%08x, version 0x%02X\n", -			(unsigned int) to, td->version[chip]); +		res = scan_write_bbt(mtd, to, len, buf, &buf[len]); +		if (res < 0) +			goto outerr; + +		printk(KERN_DEBUG "Bad block table written to 0x%08x, version " +		       "0x%02X\n", (unsigned int)to, td->version[chip]);  		/* Mark it as used */  		td->pages[chip] = page;  	}  	return 0; + + outerr: +	printk(KERN_WARNING +	       "nand_bbt: Error while writing bad block table %d\n", res); +	return res;  }  /** @@ -586,29 +767,27 @@ write:   * The function creates a memory based bbt by scanning the device   * for manufacturer / software marked good / bad blocks  */ -static int nand_memory_bbt (struct mtd_info *mtd, struct nand_bbt_descr *bd) +static inline int nand_memory_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd)  {  	struct nand_chip *this = mtd->priv; -	/* Ensure that we only scan for the pattern and nothing else */ -	bd->options = 0; -	create_bbt (mtd, this->data_buf, bd, -1); -	return 0; +	bd->options &= ~NAND_BBT_SCANEMPTY; +	return create_bbt(mtd, this->buffers->databuf, bd, -1);  }  /** - * check_create - [GENERIC] create and write bbt(s) if neccecary + * check_create - [GENERIC] create and write bbt(s) if necessary   * @mtd:	MTD device structure   * @buf:	temporary buffer   * @bd:		descriptor for the good/bad block search pattern   *   * The function checks the results of the previous call to read_bbt - * and creates / updates the bbt(s) if neccecary - * Creation is neccecary if no bbt was found for the chip/device - * Update is neccecary if one of the tables is missing or the + * and creates / updates the bbt(s) if necessary + * Creation is necessary if no bbt was found for the chip/device + * Update is necessary if one of the tables is missing or the   * version nr. of one table is less than the other  */ -static int check_create (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *bd) +static int check_create(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *bd)  {  	int i, chips, writeops, chipsel, res;  	struct nand_chip *this = mtd->priv; @@ -676,35 +855,35 @@ static int check_create (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_des  			rd = td;  			goto writecheck;  		} -create: +	create:  		/* Create the bad block table by scanning the device ? */  		if (!(td->options & NAND_BBT_CREATE))  			continue;  		/* Create the table in memory by scanning the chip(s) */ -		create_bbt (mtd, buf, bd, chipsel); +		create_bbt(mtd, buf, bd, chipsel);  		td->version[i] = 1;  		if (md)  			md->version[i] = 1; -writecheck: +	writecheck:  		/* read back first ? */  		if (rd) -			read_abs_bbt (mtd, buf, rd, chipsel); +			read_abs_bbt(mtd, buf, rd, chipsel);  		/* If they weren't versioned, read both. */  		if (rd2) -			read_abs_bbt (mtd, buf, rd2, chipsel); +			read_abs_bbt(mtd, buf, rd2, chipsel);  		/* Write the bad block table to the device ? */  		if ((writeops & 0x01) && (td->options & NAND_BBT_WRITE)) { -			res = write_bbt (mtd, buf, td, md, chipsel); +			res = write_bbt(mtd, buf, td, md, chipsel);  			if (res < 0)  				return res;  		}  		/* Write the mirror bad block table to the device ? */  		if ((writeops & 0x02) && md && (md->options & NAND_BBT_WRITE)) { -			res = write_bbt (mtd, buf, md, td, chipsel); +			res = write_bbt(mtd, buf, md, td, chipsel);  			if (res < 0)  				return res;  		} @@ -721,7 +900,7 @@ writecheck:   * accidental erasures / writes. The regions are identified by   * the mark 0x02.  */ -static void mark_bbt_region (struct mtd_info *mtd, struct nand_bbt_descr *td) +static void mark_bbt_region(struct mtd_info *mtd, struct nand_bbt_descr *td)  {  	struct nand_chip *this = mtd->priv;  	int i, j, chips, block, nrblocks, update; @@ -739,7 +918,8 @@ static void mark_bbt_region (struct mtd_info *mtd, struct nand_bbt_descr *td)  	for (i = 0; i < chips; i++) {  		if ((td->options & NAND_BBT_ABSPAGE) ||  		    !(td->options & NAND_BBT_WRITE)) { -			if (td->pages[i] == -1) continue; +			if (td->pages[i] == -1) +				continue;  			block = td->pages[i] >> (this->bbt_erase_shift - this->page_shift);  			block <<= 1;  			oldval = this->bbt[(block >> 3)]; @@ -759,7 +939,8 @@ static void mark_bbt_region (struct mtd_info *mtd, struct nand_bbt_descr *td)  			oldval = this->bbt[(block >> 3)];  			newval = oldval | (0x2 << (block & 0x06));  			this->bbt[(block >> 3)] = newval; -			if (oldval != newval) update = 1; +			if (oldval != newval) +				update = 1;  			block += 2;  		}  		/* If we want reserved blocks to be recorded to flash, and some @@ -784,7 +965,7 @@ static void mark_bbt_region (struct mtd_info *mtd, struct nand_bbt_descr *td)   * by calling the nand_free_bbt function.   *  */ -int nand_scan_bbt (struct mtd_info *mtd, struct nand_bbt_descr *bd) +int nand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd)  {  	struct nand_chip *this = mtd->priv;  	int len, res = 0; @@ -793,53 +974,56 @@ int nand_scan_bbt (struct mtd_info *mtd, struct nand_bbt_descr *bd)  	struct nand_bbt_descr *md = this->bbt_md;  	len = mtd->size >> (this->bbt_erase_shift + 2); -	/* Allocate memory (2bit per block) */ -	this->bbt = kmalloc (len, GFP_KERNEL); +	/* Allocate memory (2bit per block) and clear the memory bad block table */ +	this->bbt = kzalloc(len, GFP_KERNEL);  	if (!this->bbt) { -		printk (KERN_ERR "nand_scan_bbt: Out of memory\n"); +		printk(KERN_ERR "nand_scan_bbt: Out of memory\n");  		return -ENOMEM;  	} -	/* Clear the memory bad block table */ -	memset (this->bbt, 0x00, len);  	/* If no primary table decriptor is given, scan the device  	 * to build a memory based bad block table  	 */ -	if (!td) -		return nand_memory_bbt(mtd, bd); +	if (!td) { +		if ((res = nand_memory_bbt(mtd, bd))) { +			printk(KERN_ERR "nand_bbt: Can't scan flash and build the RAM-based BBT\n"); +			kfree(this->bbt); +			this->bbt = NULL; +		} +		return res; +	}  	/* Allocate a temporary buffer for one eraseblock incl. oob */  	len = (1 << this->bbt_erase_shift);  	len += (len >> this->page_shift) * mtd->oobsize; -	buf = kmalloc (len, GFP_KERNEL); +	buf = vmalloc(len);  	if (!buf) { -		printk (KERN_ERR "nand_bbt: Out of memory\n"); -		kfree (this->bbt); +		printk(KERN_ERR "nand_bbt: Out of memory\n"); +		kfree(this->bbt);  		this->bbt = NULL;  		return -ENOMEM;  	}  	/* Is the bbt at a given page ? */  	if (td->options & NAND_BBT_ABSPAGE) { -		res = read_abs_bbts (mtd, buf, td, md); +		res = read_abs_bbts(mtd, buf, td, md);  	} else {  		/* Search the bad block table using a pattern in oob */ -		res = search_read_bbts (mtd, buf, td, md); +		res = search_read_bbts(mtd, buf, td, md);  	}  	if (res) -		res = check_create (mtd, buf, bd); +		res = check_create(mtd, buf, bd);  	/* Prevent the bbt regions from erasing / writing */ -	mark_bbt_region (mtd, td); +	mark_bbt_region(mtd, td);  	if (md) -		mark_bbt_region (mtd, md); +		mark_bbt_region(mtd, md); -	kfree (buf); +	vfree(buf);  	return res;  } -  /**   * nand_update_bbt - [NAND Interface] update bad block table(s)   * @mtd:	MTD device structure @@ -847,7 +1031,7 @@ int nand_scan_bbt (struct mtd_info *mtd, struct nand_bbt_descr *bd)   *   * The function updates the bad block table(s)  */ -int nand_update_bbt (struct mtd_info *mtd, loff_t offs) +int nand_update_bbt(struct mtd_info *mtd, loff_t offs)  {  	struct nand_chip *this = mtd->priv;  	int len, res = 0, writeops = 0; @@ -863,9 +1047,9 @@ int nand_update_bbt (struct mtd_info *mtd, loff_t offs)  	/* Allocate a temporary buffer for one eraseblock incl. oob */  	len = (1 << this->bbt_erase_shift);  	len += (len >> this->page_shift) * mtd->oobsize; -	buf = kmalloc (len, GFP_KERNEL); +	buf = kmalloc(len, GFP_KERNEL);  	if (!buf) { -		printk (KERN_ERR "nand_update_bbt: Out of memory\n"); +		printk(KERN_ERR "nand_update_bbt: Out of memory\n");  		return -ENOMEM;  	} @@ -873,7 +1057,7 @@ int nand_update_bbt (struct mtd_info *mtd, loff_t offs)  	/* Do we have a bbt per chip ? */  	if (td->options & NAND_BBT_PERCHIP) { -		chip = (int) (offs >> this->chip_shift); +		chip = (int)(offs >> this->chip_shift);  		chipsel = chip;  	} else {  		chip = 0; @@ -886,29 +1070,26 @@ int nand_update_bbt (struct mtd_info *mtd, loff_t offs)  	/* Write the bad block table to the device ? */  	if ((writeops & 0x01) && (td->options & NAND_BBT_WRITE)) { -		res = write_bbt (mtd, buf, td, md, chipsel); +		res = write_bbt(mtd, buf, td, md, chipsel);  		if (res < 0)  			goto out;  	}  	/* Write the mirror bad block table to the device ? */  	if ((writeops & 0x02) && md && (md->options & NAND_BBT_WRITE)) { -		res = write_bbt (mtd, buf, md, td, chipsel); +		res = write_bbt(mtd, buf, md, td, chipsel);  	} -out: -	kfree (buf); + out: +	kfree(buf);  	return res;  }  /* Define some generic bad / good block scan pattern which are used - * while scanning a device for factory marked good / bad blocks - * - * The memory based patterns just - */ + * while scanning a device for factory marked good / bad blocks. */  static uint8_t scan_ff_pattern[] = { 0xff, 0xff };  static struct nand_bbt_descr smallpage_memorybased = { -	.options = 0, +	.options = NAND_BBT_SCAN2NDPAGE,  	.offs = 5,  	.len = 1,  	.pattern = scan_ff_pattern @@ -922,14 +1103,14 @@ static struct nand_bbt_descr largepage_memorybased = {  };  static struct nand_bbt_descr smallpage_flashbased = { -	.options = NAND_BBT_SCANEMPTY | NAND_BBT_SCANALLPAGES, +	.options = NAND_BBT_SCAN2NDPAGE,  	.offs = 5,  	.len = 1,  	.pattern = scan_ff_pattern  };  static struct nand_bbt_descr largepage_flashbased = { -	.options = NAND_BBT_SCANEMPTY | NAND_BBT_SCANALLPAGES, +	.options = NAND_BBT_SCAN2NDPAGE,  	.offs = 0,  	.len = 2,  	.pattern = scan_ff_pattern @@ -977,7 +1158,7 @@ static struct nand_bbt_descr bbt_mirror_descr = {   * support for the device and calls the nand_scan_bbt function   *  */ -int nand_default_bbt (struct mtd_info *mtd) +int nand_default_bbt(struct mtd_info *mtd)  {  	struct nand_chip *this = mtd->priv; @@ -987,7 +1168,7 @@ int nand_default_bbt (struct mtd_info *mtd)  	 * of the good / bad information, so we _must_ store  	 * this information in a good / bad table during  	 * startup -	*/ +	 */  	if (this->options & NAND_IS_AND) {  		/* Use the default pattern descriptors */  		if (!this->bbt_td) { @@ -995,10 +1176,9 @@ int nand_default_bbt (struct mtd_info *mtd)  			this->bbt_md = &bbt_mirror_descr;  		}  		this->options |= NAND_USE_FLASH_BBT; -		return nand_scan_bbt (mtd, &agand_flashbased); +		return nand_scan_bbt(mtd, &agand_flashbased);  	} -  	/* Is a flash based bad block table requested ? */  	if (this->options & NAND_USE_FLASH_BBT) {  		/* Use the default pattern descriptors */ @@ -1007,18 +1187,17 @@ int nand_default_bbt (struct mtd_info *mtd)  			this->bbt_md = &bbt_mirror_descr;  		}  		if (!this->badblock_pattern) { -			this->badblock_pattern = (mtd->oobblock > 512) ? -				&largepage_flashbased : &smallpage_flashbased; +			this->badblock_pattern = (mtd->writesize > 512) ? &largepage_flashbased : &smallpage_flashbased;  		}  	} else {  		this->bbt_td = NULL;  		this->bbt_md = NULL;  		if (!this->badblock_pattern) { -			this->badblock_pattern = (mtd->oobblock > 512) ? -				&largepage_memorybased : &smallpage_memorybased; +			this->badblock_pattern = (mtd->writesize > 512) ? +			    &largepage_memorybased : &smallpage_memorybased;  		}  	} -	return nand_scan_bbt (mtd, this->badblock_pattern); +	return nand_scan_bbt(mtd, this->badblock_pattern);  }  /** @@ -1027,26 +1206,35 @@ int nand_default_bbt (struct mtd_info *mtd)   * @offs:	offset in the device   * @allowbbt:	allow access to bad block table region   * - */ -int nand_isbad_bbt (struct mtd_info *mtd, loff_t offs, int allowbbt) +*/ +int nand_isbad_bbt(struct mtd_info *mtd, loff_t offs, int allowbbt)  {  	struct nand_chip *this = mtd->priv;  	int block; -	uint8_t	res; +	uint8_t res;  	/* Get block number * 2 */ -	block = (int) (offs >> (this->bbt_erase_shift - 1)); +	block = (int)(offs >> (this->bbt_erase_shift - 1));  	res = (this->bbt[block >> 3] >> (block & 0x06)) & 0x03;  	MTDDEBUG (MTD_DEBUG_LEVEL2, "nand_isbad_bbt(): bbt info for offs 0x%08x: "  	          "(block %d) 0x%02x\n", (unsigned int)offs, res, block >> 1);  	switch ((int)res) { -	case 0x00:	return 0; -	case 0x01:	return 1; -	case 0x02:	return allowbbt ? 0 : 1; +	case 0x00: +		return 0; +	case 0x01: +		return 1; +	case 0x02: +		return allowbbt ? 0 : 1;  	}  	return 1;  } +/* XXX U-BOOT XXX */ +#if 0 +EXPORT_SYMBOL(nand_scan_bbt); +EXPORT_SYMBOL(nand_default_bbt); +#endif +  #endif diff --git a/drivers/mtd/nand/nand_ecc.c b/drivers/mtd/nand/nand_ecc.c index 4c532b079..e1d5154db 100644 --- a/drivers/mtd/nand/nand_ecc.c +++ b/drivers/mtd/nand/nand_ecc.c @@ -7,7 +7,9 @@   * Copyright (C) 2000-2004 Steven J. Hill (sjhill@realitydiluted.com)   *                         Toshiba America Electronics Components, Inc.   * - * $Id: nand_ecc.c,v 1.14 2004/06/16 15:34:37 gleixner Exp $ + * Copyright (C) 2006 Thomas Gleixner <tglx@linutronix.de> + * + * $Id: nand_ecc.c,v 1.15 2005/11/07 11:14:30 gleixner Exp $   *   * This file is free software; you can redistribute it and/or modify it   * under the terms of the GNU General Public License as published by the @@ -39,6 +41,14 @@  #if defined(CONFIG_CMD_NAND) && !defined(CFG_NAND_LEGACY) +/* XXX U-BOOT XXX */ +#if 0 +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/mtd/nand_ecc.h> +#endif +  #include<linux/mtd/mtd.h>  /* @@ -128,6 +138,10 @@ int nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat,  	return 0;  } +/* XXX U-BOOT XXX */ +#if 0 +EXPORT_SYMBOL(nand_calculate_ecc); +#endif  #endif /* CONFIG_NAND_SPL */  static inline int countbits(uint32_t byte) @@ -197,4 +211,9 @@ int nand_correct_data(struct mtd_info *mtd, u_char *dat,  	return -1;  } +/* XXX U-BOOT XXX */ +#if 0 +EXPORT_SYMBOL(nand_correct_data); +#endif +  #endif diff --git a/drivers/mtd/nand/nand_ids.c b/drivers/mtd/nand/nand_ids.c index 736349039..f8b96cf02 100644 --- a/drivers/mtd/nand/nand_ids.c +++ b/drivers/mtd/nand/nand_ids.c @@ -2,8 +2,8 @@   *  drivers/mtd/nandids.c   *   *  Copyright (C) 2002 Thomas Gleixner (tglx@linutronix.de) -  * - * $Id: nand_ids.c,v 1.10 2004/05/26 13:40:12 gleixner Exp $ + * + * $Id: nand_ids.c,v 1.16 2005/11/07 11:14:31 gleixner Exp $   *   * This program is free software; you can redistribute it and/or modify   * it under the terms of the GNU General Public License version 2 as @@ -16,7 +16,6 @@  #if defined(CONFIG_CMD_NAND) && !defined(CFG_NAND_LEGACY)  #include <linux/mtd/nand.h> -  /*  *	Chip ID list  * @@ -29,13 +28,15 @@  *	512	512 Byte page size  */  struct nand_flash_dev nand_flash_ids[] = { + +#ifdef CONFIG_MTD_NAND_MUSEUM_IDS  	{"NAND 1MiB 5V 8-bit",		0x6e, 256, 1, 0x1000, 0},  	{"NAND 2MiB 5V 8-bit",		0x64, 256, 2, 0x1000, 0},  	{"NAND 4MiB 5V 8-bit",		0x6b, 512, 4, 0x2000, 0},  	{"NAND 1MiB 3,3V 8-bit",	0xe8, 256, 1, 0x1000, 0},  	{"NAND 1MiB 3,3V 8-bit",	0xec, 256, 1, 0x1000, 0},  	{"NAND 2MiB 3,3V 8-bit",	0xea, 256, 2, 0x1000, 0}, -	{"NAND 4MiB 3,3V 8-bit",	0xd5, 512, 4, 0x2000, 0}, +	{"NAND 4MiB 3,3V 8-bit", 	0xd5, 512, 4, 0x2000, 0},  	{"NAND 4MiB 3,3V 8-bit",	0xe3, 512, 4, 0x2000, 0},  	{"NAND 4MiB 3,3V 8-bit",	0xe5, 512, 4, 0x2000, 0},  	{"NAND 8MiB 3,3V 8-bit",	0xd6, 512, 8, 0x2000, 0}, @@ -44,6 +45,7 @@ struct nand_flash_dev nand_flash_ids[] = {  	{"NAND 8MiB 3,3V 8-bit",	0xe6, 512, 8, 0x2000, 0},  	{"NAND 8MiB 1,8V 16-bit",	0x49, 512, 8, 0x2000, NAND_BUSWIDTH_16},  	{"NAND 8MiB 3,3V 16-bit",	0x59, 512, 8, 0x2000, NAND_BUSWIDTH_16}, +#endif  	{"NAND 16MiB 1,8V 8-bit",	0x33, 512, 16, 0x4000, 0},  	{"NAND 16MiB 3,3V 8-bit",	0x73, 512, 16, 0x4000, 0}, @@ -61,52 +63,72 @@ struct nand_flash_dev nand_flash_ids[] = {  	{"NAND 64MiB 3,3V 16-bit",	0x56, 512, 64, 0x4000, NAND_BUSWIDTH_16},  	{"NAND 128MiB 1,8V 8-bit",	0x78, 512, 128, 0x4000, 0}, +	{"NAND 128MiB 1,8V 8-bit",	0x39, 512, 128, 0x4000, 0},  	{"NAND 128MiB 3,3V 8-bit",	0x79, 512, 128, 0x4000, 0},  	{"NAND 128MiB 1,8V 16-bit",	0x72, 512, 128, 0x4000, NAND_BUSWIDTH_16}, +	{"NAND 128MiB 1,8V 16-bit",	0x49, 512, 128, 0x4000, NAND_BUSWIDTH_16},  	{"NAND 128MiB 3,3V 16-bit",	0x74, 512, 128, 0x4000, NAND_BUSWIDTH_16}, +	{"NAND 128MiB 3,3V 16-bit",	0x59, 512, 128, 0x4000, NAND_BUSWIDTH_16},  	{"NAND 256MiB 3,3V 8-bit",	0x71, 512, 256, 0x4000, 0}, -	/* These are the new chips with large page size. The pagesize -	* and the erasesize is determined from the extended id bytes -	*/ +	/* +	 * These are the new chips with large page size. The pagesize and the +	 * erasesize is determined from the extended id bytes +	 */ +#define LP_OPTIONS (NAND_SAMSUNG_LP_OPTIONS | NAND_NO_READRDY | NAND_NO_AUTOINCR) +#define LP_OPTIONS16 (LP_OPTIONS | NAND_BUSWIDTH_16) + +	/*512 Megabit */ +	{"NAND 64MiB 1,8V 8-bit",	0xA2, 0,  64, 0, LP_OPTIONS}, +	{"NAND 64MiB 3,3V 8-bit",	0xF2, 0,  64, 0, LP_OPTIONS}, +	{"NAND 64MiB 1,8V 16-bit",	0xB2, 0,  64, 0, LP_OPTIONS16}, +	{"NAND 64MiB 3,3V 16-bit",	0xC2, 0,  64, 0, LP_OPTIONS16}, +  	/* 1 Gigabit */ -	{"NAND 128MiB 1,8V 8-bit",	0xA1, 0, 128, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR}, -	{"NAND 128MiB 3,3V 8-bit",	0xF1, 0, 128, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR}, -	{"NAND 128MiB 1,8V 16-bit",	0xB1, 0, 128, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR}, -	{"NAND 128MiB 3,3V 16-bit",	0xC1, 0, 128, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR}, +	{"NAND 128MiB 1,8V 8-bit",	0xA1, 0, 128, 0, LP_OPTIONS}, +	{"NAND 128MiB 3,3V 8-bit",	0xF1, 0, 128, 0, LP_OPTIONS}, +	{"NAND 128MiB 1,8V 16-bit",	0xB1, 0, 128, 0, LP_OPTIONS16}, +	{"NAND 128MiB 3,3V 16-bit",	0xC1, 0, 128, 0, LP_OPTIONS16},  	/* 2 Gigabit */ -	{"NAND 256MiB 1,8V 8-bit",	0xAA, 0, 256, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR}, -	{"NAND 256MiB 3,3V 8-bit",	0xDA, 0, 256, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR}, -	{"NAND 256MiB 1,8V 16-bit",	0xBA, 0, 256, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR}, -	{"NAND 256MiB 3,3V 16-bit",	0xCA, 0, 256, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR}, +	{"NAND 256MiB 1,8V 8-bit",	0xAA, 0, 256, 0, LP_OPTIONS}, +	{"NAND 256MiB 3,3V 8-bit",	0xDA, 0, 256, 0, LP_OPTIONS}, +	{"NAND 256MiB 1,8V 16-bit",	0xBA, 0, 256, 0, LP_OPTIONS16}, +	{"NAND 256MiB 3,3V 16-bit",	0xCA, 0, 256, 0, LP_OPTIONS16},  	/* 4 Gigabit */ -	{"NAND 512MiB 1,8V 8-bit",	0xAC, 0, 512, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR}, -	{"NAND 512MiB 3,3V 8-bit",	0xDC, 0, 512, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR}, -	{"NAND 512MiB 1,8V 16-bit",	0xBC, 0, 512, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR}, -	{"NAND 512MiB 3,3V 16-bit",	0xCC, 0, 512, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR}, +	{"NAND 512MiB 1,8V 8-bit",	0xAC, 0, 512, 0, LP_OPTIONS}, +	{"NAND 512MiB 3,3V 8-bit",	0xDC, 0, 512, 0, LP_OPTIONS}, +	{"NAND 512MiB 1,8V 16-bit",	0xBC, 0, 512, 0, LP_OPTIONS16}, +	{"NAND 512MiB 3,3V 16-bit",	0xCC, 0, 512, 0, LP_OPTIONS16},  	/* 8 Gigabit */ -	{"NAND 1GiB 1,8V 8-bit",	0xA3, 0, 1024, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR}, -	{"NAND 1GiB 3,3V 8-bit",	0xD3, 0, 1024, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR}, -	{"NAND 1GiB 1,8V 16-bit",	0xB3, 0, 1024, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR}, -	{"NAND 1GiB 3,3V 16-bit",	0xC3, 0, 1024, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR}, +	{"NAND 1GiB 1,8V 8-bit",	0xA3, 0, 1024, 0, LP_OPTIONS}, +	{"NAND 1GiB 3,3V 8-bit",	0xD3, 0, 1024, 0, LP_OPTIONS}, +	{"NAND 1GiB 1,8V 16-bit",	0xB3, 0, 1024, 0, LP_OPTIONS16}, +	{"NAND 1GiB 3,3V 16-bit",	0xC3, 0, 1024, 0, LP_OPTIONS16},  	/* 16 Gigabit */ -	{"NAND 2GiB 1,8V 8-bit",	0xA5, 0, 2048, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR}, -	{"NAND 2GiB 3,3V 8-bit",	0xD5, 0, 2048, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR}, -	{"NAND 2GiB 1,8V 16-bit",	0xB5, 0, 2048, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR}, -	{"NAND 2GiB 3,3V 16-bit",	0xC5, 0, 2048, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR}, +	{"NAND 2GiB 1,8V 8-bit",	0xA5, 0, 2048, 0, LP_OPTIONS}, +	{"NAND 2GiB 3,3V 8-bit",	0xD5, 0, 2048, 0, LP_OPTIONS}, +	{"NAND 2GiB 1,8V 16-bit",	0xB5, 0, 2048, 0, LP_OPTIONS16}, +	{"NAND 2GiB 3,3V 16-bit",	0xC5, 0, 2048, 0, LP_OPTIONS16}, -	/* Renesas AND 1 Gigabit. Those chips do not support extended id and have a strange page/block layout ! -	 * The chosen minimum erasesize is 4 * 2 * 2048 = 16384 Byte, as those chips have an array of 4 page planes -	 * 1 block = 2 pages, but due to plane arrangement the blocks 0-3 consists of page 0 + 4,1 + 5, 2 + 6, 3 + 7 -	 * Anyway JFFS2 would increase the eraseblock size so we chose a combined one which can be erased in one go -	 * There are more speed improvements for reads and writes possible, but not implemented now +	/* +	 * Renesas AND 1 Gigabit. Those chips do not support extended id and +	 * have a strange page/block layout !  The chosen minimum erasesize is +	 * 4 * 2 * 2048 = 16384 Byte, as those chips have an array of 4 page +	 * planes 1 block = 2 pages, but due to plane arrangement the blocks +	 * 0-3 consists of page 0 + 4,1 + 5, 2 + 6, 3 + 7 Anyway JFFS2 would +	 * increase the eraseblock size so we chose a combined one which can be +	 * erased in one go There are more speed improvements for reads and +	 * writes possible, but not implemented now  	 */ -	{"AND 128MiB 3,3V 8-bit",	0x01, 2048, 128, 0x4000, NAND_IS_AND | NAND_NO_AUTOINCR | NAND_4PAGE_ARRAY}, +	{"AND 128MiB 3,3V 8-bit",	0x01, 2048, 128, 0x4000, +	 NAND_IS_AND | NAND_NO_AUTOINCR |NAND_NO_READRDY | NAND_4PAGE_ARRAY | +	 BBT_AUTO_REFRESH +	},  	{NULL,}  }; @@ -121,6 +143,7 @@ struct nand_manufacturers nand_manuf_ids[] = {  	{NAND_MFR_NATIONAL, "National"},  	{NAND_MFR_RENESAS, "Renesas"},  	{NAND_MFR_STMICRO, "ST Micro"}, +	{NAND_MFR_HYNIX, "Hynix"},  	{NAND_MFR_MICRON, "Micron"},  	{0x0, "Unknown"}  }; diff --git a/drivers/mtd/nand/nand_util.c b/drivers/mtd/nand/nand_util.c index 828cc338a..78e70cc80 100644 --- a/drivers/mtd/nand/nand_util.c +++ b/drivers/mtd/nand/nand_util.c @@ -39,6 +39,9 @@  #include <malloc.h>  #include <div64.h> + +#include <asm/errno.h> +#include <linux/mtd/mtd.h>  #include <nand.h>  #include <jffs2/jffs2.h> @@ -69,71 +72,33 @@ static int nand_block_bad_scrub(struct mtd_info *mtd, loff_t ofs, int getchip)  int nand_erase_opts(nand_info_t *meminfo, const nand_erase_options_t *opts)  {  	struct jffs2_unknown_node cleanmarker; -	int clmpos = 0; -	int clmlen = 8;  	erase_info_t erase;  	ulong erase_length; -	int isNAND;  	int bbtest = 1;  	int result;  	int percent_complete = -1;  	int (*nand_block_bad_old)(struct mtd_info *, loff_t, int) = NULL;  	const char *mtd_device = meminfo->name; +	struct mtd_oob_ops oob_opts; +	struct nand_chip *chip = meminfo->priv; +	uint8_t buf[64]; +	memset(buf, 0, sizeof(buf));  	memset(&erase, 0, sizeof(erase)); +	memset(&oob_opts, 0, sizeof(oob_opts));  	erase.mtd = meminfo;  	erase.len  = meminfo->erasesize;  	erase.addr = opts->offset;  	erase_length = opts->length; -	isNAND = meminfo->type == MTD_NANDFLASH ? 1 : 0; - -	if (opts->jffs2) { -		cleanmarker.magic = cpu_to_je16 (JFFS2_MAGIC_BITMASK); -		cleanmarker.nodetype = cpu_to_je16 (JFFS2_NODETYPE_CLEANMARKER); -		if (isNAND) { -			struct nand_oobinfo *oobinfo = &meminfo->oobinfo; -			/* check for autoplacement */ -			if (oobinfo->useecc == MTD_NANDECC_AUTOPLACE) { -				/* get the position of the free bytes */ -				if (!oobinfo->oobfree[0][1]) { -					printf(" Eeep. Autoplacement selected " -					       "and no empty space in oob\n"); -					return -1; -				} -				clmpos = oobinfo->oobfree[0][0]; -				clmlen = oobinfo->oobfree[0][1]; -				if (clmlen > 8) -					clmlen = 8; -			} else { -				/* legacy mode */ -				switch (meminfo->oobsize) { -				case 8: -					clmpos = 6; -					clmlen = 2; -					break; -				case 16: -					clmpos = 8; -					clmlen = 8; -					break; -				case 64: -					clmpos = 16; -					clmlen = 8; -					break; -				} -			} - -			cleanmarker.totlen = cpu_to_je32(8); -		} else { -			cleanmarker.totlen = -				cpu_to_je32(sizeof(struct jffs2_unknown_node)); -		} -		cleanmarker.hdr_crc =  cpu_to_je32( -			crc32_no_comp(0, (unsigned char *) &cleanmarker, -				      sizeof(struct jffs2_unknown_node) - 4)); -	} +	cleanmarker.magic = cpu_to_je16 (JFFS2_MAGIC_BITMASK); +	cleanmarker.nodetype = cpu_to_je16 (JFFS2_NODETYPE_CLEANMARKER); +	cleanmarker.totlen = cpu_to_je32(8); +	cleanmarker.hdr_crc = cpu_to_je32( +	crc32_no_comp(0, (unsigned char *) &cleanmarker, +	sizeof(struct jffs2_unknown_node) - 4));  	/* scrub option allows to erase badblock. To prevent internal  	 * check from erase() method, set block check method to dummy @@ -163,7 +128,7 @@ int nand_erase_opts(nand_info_t *meminfo, const nand_erase_options_t *opts)  	for (;  	     erase.addr < opts->offset + erase_length;  	     erase.addr += meminfo->erasesize) { - +		  		WATCHDOG_RESET ();  		if (!opts->scrub && bbtest) { @@ -194,25 +159,21 @@ int nand_erase_opts(nand_info_t *meminfo, const nand_erase_options_t *opts)  		/* format for JFFS2 ? */  		if (opts->jffs2) { -			/* write cleanmarker */ -			if (isNAND) { -				size_t written; -				result = meminfo->write_oob(meminfo, -							    erase.addr + clmpos, -							    clmlen, -							    &written, -							    (unsigned char *) -							    &cleanmarker); -				if (result != 0) { -					printf("\n%s: MTD writeoob failure: %d\n", -					       mtd_device, result); -					continue; -				} -			} else { -				printf("\n%s: this erase routine only supports" -				       " NAND devices!\n", -				       mtd_device); +			chip->ops.len = chip->ops.ooblen = 64; +			chip->ops.datbuf = NULL; +			chip->ops.oobbuf = buf; +			chip->ops.ooboffs = chip->badblockpos & ~0x01; +			 +			result = meminfo->write_oob(meminfo, +							erase.addr + meminfo->oobsize, +							&chip->ops); +			if (result != 0) { +				printf("\n%s: MTD writeoob failure: %d\n", +				mtd_device, result); +				continue;  			} +			else +				printf("%s: MTD writeoob at 0x%08x\n",mtd_device, erase.addr + meminfo->oobsize );  		}  		if (!opts->quiet) { @@ -232,11 +193,11 @@ int nand_erase_opts(nand_info_t *meminfo, const nand_erase_options_t *opts)  				percent_complete = percent;  				printf("\rErasing at 0x%x -- %3d%% complete.", -				       erase.addr, percent); +				erase.addr, percent);  				if (opts->jffs2 && result == 0) -					printf(" Cleanmarker written at 0x%x.", -					       erase.addr); +				printf(" Cleanmarker written at 0x%x.", +				erase.addr);  			}  		}  	} @@ -253,6 +214,9 @@ int nand_erase_opts(nand_info_t *meminfo, const nand_erase_options_t *opts)  	return 0;  } +/* XXX U-BOOT XXX */ +#if 0 +  #define MAX_PAGE_SIZE	2048  #define MAX_OOB_SIZE	64 @@ -263,27 +227,190 @@ static unsigned char data_buf[MAX_PAGE_SIZE];  static unsigned char oob_buf[MAX_OOB_SIZE];  /* OOB layouts to pass into the kernel as default */ -static struct nand_oobinfo none_oobinfo = { +static struct nand_ecclayout none_ecclayout = {  	.useecc = MTD_NANDECC_OFF,  }; -static struct nand_oobinfo jffs2_oobinfo = { +static struct nand_ecclayout jffs2_ecclayout = {  	.useecc = MTD_NANDECC_PLACE,  	.eccbytes = 6,  	.eccpos = { 0, 1, 2, 3, 6, 7 }  }; -static struct nand_oobinfo yaffs_oobinfo = { +static struct nand_ecclayout yaffs_ecclayout = {  	.useecc = MTD_NANDECC_PLACE,  	.eccbytes = 6,  	.eccpos = { 8, 9, 10, 13, 14, 15}  }; -static struct nand_oobinfo autoplace_oobinfo = { +static struct nand_ecclayout autoplace_ecclayout = {  	.useecc = MTD_NANDECC_AUTOPLACE  }; +#endif +  /** + * nand_fill_oob - [Internal] Transfer client buffer to oob + * @chip:	nand chip structure + * @oob:	oob data buffer + * @ops:	oob ops structure + *  + * Copied from nand_base.c + */ +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; +	} +	default: +		BUG(); +	} +	return NULL; +} + +#define NOTALIGNED(x)	(x & (chip->subpagesize - 1)) != 0 + + +/* copied from nand_base.c: nand_do_write_ops() + * Only very small changes + */ +int nand_write_opts(nand_info_t *mtd, loff_t to, mtd_oob_ops_t *ops) +{ +	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; +	 +	ops->retlen = 0; +	if (!writelen) +		return 0; + +	printk("nand_write_opts: to: 0x%08x, ops->len: 0x%08x\n", to, ops->len); +	 +	/* 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; +	} + +	column = to & (mtd->writesize - 1); +	subpage = column || (writelen & (mtd->writesize - 1)); + +	if (subpage && oob) { +		printk(KERN_NOTICE "nand_write: " +		       "Attempt to write oob to subpage\n"); +		return -EINVAL; +	} + +	chipnr = (int)(to >> chip->chip_shift); +	chip->select_chip(mtd, chipnr); + +	/* XXX U-BOOT XXX */ +#if 0 +	/* Check, if it is write protected */ +	if (nand_check_wp(mtd)) +		return -EIO; +#endif + +	realpage = (int)(to >> chip->page_shift); +	page = realpage & chip->pagemask; +	blockmask = (1 << (chip->phys_erase_shift - chip->page_shift)) - 1; + +	/* 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)) { +		printf("!oob, writing %d bytes with 0xff to chip->oob_poi (0x%08x)\n", mtd->oobsize, chip->oob_poi); +		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; +		} + +		if (unlikely(oob)) +			oob = nand_fill_oob(chip, oob, ops); + +		ret = chip->write_page(mtd, chip, wbuf, page, cached, +				       (ops->mode == MTD_OOB_RAW)); +		if (ret) +			break; + +		writelen -= bytes; +		if (!writelen) +			break; + +		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); +		} +	} + +	ops->retlen = ops->len - writelen; +	if (unlikely(oob)) +		ops->oobretlen = ops->ooblen; +	return ret; +} + +/* XXX U-BOOT XXX */ +#if 0 +/**   * nand_write_opts: - write image to NAND flash with support for various options   *   * @param meminfo	NAND device to erase @@ -301,9 +428,9 @@ int nand_write_opts(nand_info_t *meminfo, const nand_write_options_t *opts)  	int blockstart = -1;  	loff_t offs;  	int readlen; -	int oobinfochanged = 0; +	int ecclayoutchanged = 0;  	int percent_complete = -1; -	struct nand_oobinfo old_oobinfo; +	struct nand_ecclayout old_ecclayout;  	ulong mtdoffset = opts->offset;  	ulong erasesize_blockalign;  	u_char *buffer = opts->buffer; @@ -324,35 +451,35 @@ int nand_write_opts(nand_info_t *meminfo, const nand_write_options_t *opts)  	}  	/* make sure device page sizes are valid */ -	if (!(meminfo->oobsize == 16 && meminfo->oobblock == 512) -	    && !(meminfo->oobsize == 8 && meminfo->oobblock == 256) -	    && !(meminfo->oobsize == 64 && meminfo->oobblock == 2048)) { +	if (!(meminfo->oobsize == 16 && meminfo->writesize == 512) +	    && !(meminfo->oobsize == 8 && meminfo->writesize == 256) +	    && !(meminfo->oobsize == 64 && meminfo->writesize == 2048)) {  		printf("Unknown flash (not normal NAND)\n");  		return -1;  	}  	/* read the current oob info */ -	memcpy(&old_oobinfo, &meminfo->oobinfo, sizeof(old_oobinfo)); +	memcpy(&old_ecclayout, &meminfo->ecclayout, sizeof(old_ecclayout));  	/* write without ecc? */  	if (opts->noecc) { -		memcpy(&meminfo->oobinfo, &none_oobinfo, -		       sizeof(meminfo->oobinfo)); -		oobinfochanged = 1; +		memcpy(&meminfo->ecclayout, &none_ecclayout, +		       sizeof(meminfo->ecclayout)); +		ecclayoutchanged = 1;  	}  	/* autoplace ECC? */ -	if (opts->autoplace && (old_oobinfo.useecc != MTD_NANDECC_AUTOPLACE)) { +	if (opts->autoplace && (old_ecclayout.useecc != MTD_NANDECC_AUTOPLACE)) { -		memcpy(&meminfo->oobinfo, &autoplace_oobinfo, -		       sizeof(meminfo->oobinfo)); -		oobinfochanged = 1; +		memcpy(&meminfo->ecclayout, &autoplace_ecclayout, +		       sizeof(meminfo->ecclayout)); +		ecclayoutchanged = 1;  	}  	/* force OOB layout for jffs2 or yaffs? */  	if (opts->forcejffs2 || opts->forceyaffs) { -		struct nand_oobinfo *oobsel = -			opts->forcejffs2 ? &jffs2_oobinfo : &yaffs_oobinfo; +		struct nand_ecclayout *oobsel = +			opts->forcejffs2 ? &jffs2_ecclayout : &yaffs_ecclayout;  		if (meminfo->oobsize == 8) {  			if (opts->forceyaffs) { @@ -361,15 +488,15 @@ int nand_write_opts(nand_info_t *meminfo, const nand_write_options_t *opts)  				goto restoreoob;  			}  			/* Adjust number of ecc bytes */ -			jffs2_oobinfo.eccbytes = 3; +			jffs2_ecclayout.eccbytes = 3;  		} -		memcpy(&meminfo->oobinfo, oobsel, sizeof(meminfo->oobinfo)); +		memcpy(&meminfo->ecclayout, oobsel, sizeof(meminfo->ecclayout));  	}  	/* get image length */  	imglen = opts->length; -	pagelen = meminfo->oobblock +	pagelen = meminfo->writesize  		+ ((opts->writeoob != 0) ? meminfo->oobsize : 0);  	/* check, if file is pagealigned */ @@ -379,11 +506,11 @@ int nand_write_opts(nand_info_t *meminfo, const nand_write_options_t *opts)  	}  	/* check, if length fits into device */ -	if (((imglen / pagelen) * meminfo->oobblock) +	if (((imglen / pagelen) * meminfo->writesize)  	     > (meminfo->size - opts->offset)) {  		printf("Image %d bytes, NAND page %d bytes, "  		       "OOB area %u bytes, device size %u bytes\n", -		       imglen, pagelen, meminfo->oobblock, meminfo->size); +		       imglen, pagelen, meminfo->writesize, meminfo->size);  		printf("Input block does not fit into device\n");  		goto restoreoob;  	} @@ -437,11 +564,11 @@ int nand_write_opts(nand_info_t *meminfo, const nand_write_options_t *opts)  			} while (offs < blockstart + erasesize_blockalign);  		} -		readlen = meminfo->oobblock; +		readlen = meminfo->writesize;  		if (opts->pad && (imglen < readlen)) {  			readlen = imglen;  			memset(data_buf + readlen, 0xff, -			       meminfo->oobblock - readlen); +			       meminfo->writesize - readlen);  		}  		/* read page data from input memory buffer */ @@ -474,7 +601,7 @@ int nand_write_opts(nand_info_t *meminfo, const nand_write_options_t *opts)  		/* write out the page data */  		result = meminfo->write(meminfo,  					mtdoffset, -					meminfo->oobblock, +					meminfo->writesize,  					&written,  					(unsigned char *) &data_buf); @@ -505,16 +632,16 @@ int nand_write_opts(nand_info_t *meminfo, const nand_write_options_t *opts)  			}  		} -		mtdoffset += meminfo->oobblock; +		mtdoffset += meminfo->writesize;  	}  	if (!opts->quiet)  		printf("\n");  restoreoob: -	if (oobinfochanged) { -		memcpy(&meminfo->oobinfo, &old_oobinfo, -		       sizeof(meminfo->oobinfo)); +	if (ecclayoutchanged) { +		memcpy(&meminfo->ecclayout, &old_ecclayout, +		       sizeof(meminfo->ecclayout));  	}  	if (imglen > 0) { @@ -548,22 +675,22 @@ int nand_read_opts(nand_info_t *meminfo, const nand_read_options_t *opts)  	int result;  	/* make sure device page sizes are valid */ -	if (!(meminfo->oobsize == 16 && meminfo->oobblock == 512) -	    && !(meminfo->oobsize == 8 && meminfo->oobblock == 256) -	    && !(meminfo->oobsize == 64 && meminfo->oobblock == 2048)) { +	if (!(meminfo->oobsize == 16 && meminfo->writesize == 512) +	    && !(meminfo->oobsize == 8 && meminfo->writesize == 256) +	    && !(meminfo->oobsize == 64 && meminfo->writesize == 2048)) {  		printf("Unknown flash (not normal NAND)\n");  		return -1;  	} -	pagelen = meminfo->oobblock +	pagelen = meminfo->writesize  		+ ((opts->readoob != 0) ? meminfo->oobsize : 0);  	/* check, if length is not larger than device */ -	if (((imglen / pagelen) * meminfo->oobblock) +	if (((imglen / pagelen) * meminfo->writesize)  	     > (meminfo->size - opts->offset)) {  		printf("Image %d bytes, NAND page %d bytes, "  		       "OOB area %u bytes, device size %u bytes\n", -		       imglen, pagelen, meminfo->oobblock, meminfo->size); +		       imglen, pagelen, meminfo->writesize, meminfo->size);  		printf("Input block is larger than device\n");  		return -1;  	} @@ -621,7 +748,7 @@ int nand_read_opts(nand_info_t *meminfo, const nand_read_options_t *opts)  		/* read page data to memory buffer */  		result = meminfo->read(meminfo,  				       mtdoffset, -				       meminfo->oobblock, +				       meminfo->writesize,  				       &readlen,  				       (unsigned char *) &data_buf); @@ -685,7 +812,7 @@ int nand_read_opts(nand_info_t *meminfo, const nand_read_options_t *opts)  			}  		} -		mtdoffset += meminfo->oobblock; +		mtdoffset += meminfo->writesize;  	}  	if (!opts->quiet) @@ -699,7 +826,10 @@ int nand_read_opts(nand_info_t *meminfo, const nand_read_options_t *opts)  	/* return happy */  	return 0;  } +#endif +/* XXX U-BOOT XXX */ +#if 0  /******************************************************************************   * Support for locking / unlocking operations of some NAND devices   *****************************************************************************/ @@ -784,7 +914,7 @@ int nand_get_lock_status(nand_info_t *meminfo, ulong offset)  	this->select_chip(meminfo, chipnr); -	if ((offset & (meminfo->oobblock - 1)) != 0) { +	if ((offset & (meminfo->writesize - 1)) != 0) {  		printf ("nand_get_lock_status: "  			"Start address must be beginning of "  			"nand page!\n"); @@ -813,7 +943,7 @@ int nand_get_lock_status(nand_info_t *meminfo, ulong offset)   * @param meminfo	nand mtd instance   * @param start		start byte address   * @param length	number of bytes to unlock (must be a multiple of - *			page size nand->oobblock) + *			page size nand->writesize)   *   * @return		0 on success, -1 in case of error   */ @@ -839,14 +969,14 @@ int nand_unlock(nand_info_t *meminfo, ulong start, ulong length)  		goto out;  	} -	if ((start & (meminfo->oobblock - 1)) != 0) { +	if ((start & (meminfo->writesize - 1)) != 0) {  		printf ("nand_unlock: Start address must be beginning of "  			"nand page!\n");  		ret = -1;  		goto out;  	} -	if (length == 0 || (length & (meminfo->oobblock - 1)) != 0) { +	if (length == 0 || (length & (meminfo->writesize - 1)) != 0) {  		printf ("nand_unlock: Length must be a multiple of nand page "  			"size!\n");  		ret = -1; @@ -875,5 +1005,6 @@ int nand_unlock(nand_info_t *meminfo, ulong start, ulong length)  	this->select_chip(meminfo, -1);  	return ret;  } +#endif  #endif |