Skip to content
Snippets Groups Projects
nand_base.c 74.1 KiB
Newer Older
  • Learn to ignore specific revisions
  • /*
     *  drivers/mtd/nand.c
     *
     *  Overview:
     *   This is the generic MTD driver for NAND flash devices. It should be
     *   capable of working with almost all NAND chips currently available.
     *   Basic support for AG-AND chips is provided.
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
     *
    
     *	Additional technical information is available on
     *	http://www.linux-mtd.infradead.org/tech/nand.html
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
     *
    
     *  Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com)
     * 		  2002 Thomas Gleixner (tglx@linutronix.de)
     *
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
     *  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
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
     *
    
     *  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:
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
     *	David Woodhouse for adding multichip support
     *
    
     *	Aleph One Ltd. and Toby Churchill Ltd. for supporting the
     *	rework for 2K page size chips
     *
     * 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.
     *
     */
    
    /* XXX U-BOOT XXX */
    #if 0
    #include <linux/delay.h>
    #include <linux/errno.h>
    #include <linux/sched.h>
    #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/interrupt.h>
    #include <linux/bitops.h>
    #include <asm/io.h>
    
    #ifdef CONFIG_MTD_PARTITIONS
    #include <linux/mtd/partitions.h>
    #endif
    
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    #endif
    
    
    #if (CONFIG_COMMANDS & CFG_CMD_NAND)
    
    #include <malloc.h>
    #include <watchdog.h>
    #include <linux/mtd/compat.h>
    #include <linux/mtd/mtd.h>
    #include <linux/mtd/nand.h>
    #include <linux/mtd/nand_ecc.h>
    
    #include <asm/io.h>
    #include <asm/errno.h>
    
    #ifdef CONFIG_JFFS2_NAND
    #include <jffs2/jffs2.h>
    #endif
    
    /* Define default oob placement schemes for large and small page devices */
    static struct nand_oobinfo nand_oob_8 = {
    	.useecc = MTD_NANDECC_AUTOPLACE,
    	.eccbytes = 3,
    	.eccpos = {0, 1, 2},
    	.oobfree = { {3, 2}, {6, 2} }
    };
    
    static struct nand_oobinfo nand_oob_16 = {
    	.useecc = MTD_NANDECC_AUTOPLACE,
    	.eccbytes = 6,
    	.eccpos = {0, 1, 2, 3, 6, 7},
    	.oobfree = { {8, 8} }
    };
    
    static struct nand_oobinfo nand_oob_64 = {
    	.useecc = MTD_NANDECC_AUTOPLACE,
    	.eccbytes = 24,
    	.eccpos = {
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    		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} }
    };
    
    /* This is used for padding purposes in nand_write_oob */
    static u_char ffchars[] = {
    	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
    	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
    	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
    	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
    	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
    	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
    	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
    	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
    };
    
    /*
     * NAND low-level MTD interface functions
     */
    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
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    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)
    #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
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
     *
     * Deselect, release chip lock and wake up anyone waiting on the device
    
     */
    /* XXX U-BOOT XXX */
    #if 0
    static void nand_release_device (struct mtd_info *mtd)
    {
    	struct nand_chip *this = mtd->priv;
    
    	/* De-select the NAND device */
    	this->select_chip(mtd, -1);
    	/* Do we have a hardware controller ? */
    	if (this->controller) {
    		spin_lock(&this->controller->lock);
    		this->controller->active = NULL;
    		spin_unlock(&this->controller->lock);
    	}
    	/* Release the chip */
    	spin_lock (&this->chip_lock);
    	this->state = FL_READY;
    	wake_up (&this->wq);
    	spin_unlock (&this->chip_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 */
    }
    
    #endif
    
    /**
     * nand_read_byte - [DEFAULT] read one byte from the chip
     * @mtd:	MTD device structure
     *
     * Default read function for 8bit buswith
     */
    static u_char nand_read_byte(struct mtd_info *mtd)
    {
    	struct nand_chip *this = mtd->priv;
    	return readb(this->IO_ADDR_R);
    }
    
    /**
     * nand_write_byte - [DEFAULT] write one byte to the chip
     * @mtd:	MTD device structure
     * @byte:	pointer to data byte to write
     *
     * Default write function for 8it buswith
     */
    static void nand_write_byte(struct mtd_info *mtd, u_char byte)
    {
    	struct nand_chip *this = mtd->priv;
    	writeb(byte, this->IO_ADDR_W);
    }
    
    /**
     * nand_read_byte16 - [DEFAULT] read one byte endianess aware from the chip
     * @mtd:	MTD device structure
     *
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
     * Default read function for 16bit buswith with
    
     * endianess conversion
     */
    static u_char nand_read_byte16(struct mtd_info *mtd)
    {
    	struct nand_chip *this = mtd->priv;
    	return (u_char) cpu_to_le16(readw(this->IO_ADDR_R));
    }
    
    /**
     * nand_write_byte16 - [DEFAULT] write one byte endianess aware to the chip
     * @mtd:	MTD device structure
     * @byte:	pointer to data byte to write
     *
     * Default write function for 16bit buswith with
     * endianess conversion
     */
    static void nand_write_byte16(struct mtd_info *mtd, u_char byte)
    {
    	struct nand_chip *this = mtd->priv;
    	writew(le16_to_cpu((u16) byte), this->IO_ADDR_W);
    }
    
    /**
     * nand_read_word - [DEFAULT] read one word from the chip
     * @mtd:	MTD device structure
     *
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
     * Default read function for 16bit buswith without
    
     * endianess conversion
     */
    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
     *
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
     * 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);
    }
    
    /**
     * nand_select_chip - [DEFAULT] control CE line
     * @mtd:	MTD device structure
     * @chip:	chipnumber to select, -1 for deselect
     *
     * Default select function for 1 chip devices.
     */
    static void nand_select_chip(struct mtd_info *mtd, int chip)
    {
    	struct nand_chip *this = mtd->priv;
    	switch(chip) {
    	case -1:
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    		this->hwcontrol(mtd, NAND_CTL_CLRNCE);
    
    		break;
    	case 0:
    		this->hwcontrol(mtd, NAND_CTL_SETNCE);
    		break;
    
    	default:
    		BUG();
    	}
    }
    
    /**
     * nand_write_buf - [DEFAULT] write buffer to chip
     * @mtd:	MTD device structure
     * @buf:	data buffer
     * @len:	number of bytes to write
     *
     * Default write function for 8bit buswith
     */
    static void nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len)
    {
    	int i;
    	struct nand_chip *this = mtd->priv;
    
    	for (i=0; i<len; i++)
    		writeb(buf[i], this->IO_ADDR_W);
    }
    
    /**
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
     * nand_read_buf - [DEFAULT] read chip data into buffer
    
     * @mtd:	MTD device structure
     * @buf:	buffer to store date
     * @len:	number of bytes to read
     *
     * Default read function for 8bit buswith
     */
    static void nand_read_buf(struct mtd_info *mtd, u_char *buf, int len)
    {
    	int i;
    	struct nand_chip *this = mtd->priv;
    
    	for (i=0; i<len; i++)
    		buf[i] = readb(this->IO_ADDR_R);
    }
    
    /**
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
     * nand_verify_buf - [DEFAULT] Verify chip data against buffer
    
     * @mtd:	MTD device structure
     * @buf:	buffer containing the data to compare
     * @len:	number of bytes to compare
     *
     * Default verify function for 8bit buswith
     */
    static int nand_verify_buf(struct mtd_info *mtd, const u_char *buf, int len)
    {
    	int i;
    	struct nand_chip *this = mtd->priv;
    
    	for (i=0; i<len; i++)
    		if (buf[i] != readb(this->IO_ADDR_R))
    			return -EFAULT;
    
    	return 0;
    }
    
    /**
     * nand_write_buf16 - [DEFAULT] write buffer to chip
     * @mtd:	MTD device structure
     * @buf:	data buffer
     * @len:	number of bytes to write
     *
     * Default write function for 16bit buswith
     */
    static void nand_write_buf16(struct mtd_info *mtd, const u_char *buf, int len)
    {
    	int i;
    	struct nand_chip *this = mtd->priv;
    	u16 *p = (u16 *) buf;
    	len >>= 1;
    
    	for (i=0; i<len; i++)
    		writew(p[i], this->IO_ADDR_W);
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
     * nand_read_buf16 - [DEFAULT] read chip data into buffer
    
     * @mtd:	MTD device structure
     * @buf:	buffer to store date
     * @len:	number of bytes to read
     *
     * Default read function for 16bit buswith
     */
    static void nand_read_buf16(struct mtd_info *mtd, u_char *buf, int len)
    {
    	int i;
    	struct nand_chip *this = mtd->priv;
    	u16 *p = (u16 *) buf;
    	len >>= 1;
    
    	for (i=0; i<len; i++)
    		p[i] = readw(this->IO_ADDR_R);
    }
    
    /**
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
     * nand_verify_buf16 - [DEFAULT] Verify chip data against buffer
    
     * @mtd:	MTD device structure
     * @buf:	buffer containing the data to compare
     * @len:	number of bytes to compare
     *
     * Default verify function for 16bit buswith
     */
    static int nand_verify_buf16(struct mtd_info *mtd, const u_char *buf, int len)
    {
    	int i;
    	struct nand_chip *this = mtd->priv;
    	u16 *p = (u16 *) buf;
    	len >>= 1;
    
    	for (i=0; i<len; i++)
    		if (p[i] != readw(this->IO_ADDR_R))
    			return -EFAULT;
    
    	return 0;
    }
    
    /**
     * nand_block_bad - [DEFAULT] Read bad block marker from the chip
     * @mtd:	MTD device structure
     * @ofs:	offset from device start
     * @getchip:	0, if the chip is already selected
     *
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
     * Check, if the block is bad.
    
     */
    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;
    	u16 bad;
    
    	if (getchip) {
    		page = (int)(ofs >> this->page_shift);
    		chipnr = (int)(ofs >> this->chip_shift);
    
    		/* Grab the lock and see if the device is available */
    		nand_get_device (this, mtd, FL_READING);
    
    		/* Select the NAND device */
    		this->select_chip(mtd, chipnr);
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	} else
    		page = (int) ofs;
    
    
    	if (this->options & NAND_BUSWIDTH_16) {
    		this->cmdfunc (mtd, NAND_CMD_READOOB, this->badblockpos & 0xFE, page & this->pagemask);
    		bad = cpu_to_le16(this->read_word(mtd));
    		if (this->badblockpos & 0x1)
    			bad >>= 1;
    		if ((bad & 0xFF) != 0xff)
    			res = 1;
    	} else {
    		this->cmdfunc (mtd, NAND_CMD_READOOB, this->badblockpos, page & this->pagemask);
    		if (this->read_byte(mtd) != 0xff)
    			res = 1;
    	}
    
    	if (getchip) {
    		/* Deselect and wake up anyone waiting on the device */
    		nand_release_device(mtd);
    
    	return res;
    }
    
    /**
     * nand_default_block_markbad - [DEFAULT] mark a block bad
     * @mtd:	MTD device structure
     * @ofs:	offset from device start
     *
     * This is the default implementation, which can be overridden by
     * a hardware specific driver.
    */
    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;
    
    	/* Get block number */
    	block = ((int) ofs) >> this->bbt_erase_shift;
    	this->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);
    
    	/* 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);
    }
    
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    /**
    
     * nand_check_wp - [GENERIC] check if the chip is write protected
     * @mtd:	MTD device structure
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
     * Check, if the device is write protected
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
     * The function expects, that the device is already selected
    
     */
    static int nand_check_wp (struct mtd_info *mtd)
    {
    	struct nand_chip *this = mtd->priv;
    	/* Check the WP bit */
    	this->cmdfunc (mtd, NAND_CMD_STATUS, -1, -1);
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	return (this->read_byte(mtd) & 0x80) ? 0 : 1;
    
    }
    
    /**
     * nand_block_checkbad - [GENERIC] Check if a block is marked bad
     * @mtd:	MTD device structure
     * @ofs:	offset from device start
     * @getchip:	0, if the chip is already selected
     * @allowbbt:	1, if its allowed to access the bbt area
     *
     * 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)
    {
    	struct nand_chip *this = mtd->priv;
    
    	if (!this->bbt)
    		return this->block_bad(mtd, ofs, getchip);
    
    	/* Return info from the table */
    	return nand_isbad_bbt (mtd, ofs, allowbbt);
    }
    
    /**
     * nand_command - [DEFAULT] Send command to NAND device
     * @mtd:	MTD device structure
     * @command:	the command to be sent
     * @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 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)
    {
    	register struct nand_chip *this = mtd->priv;
    
    	/* Begin command latch cycle */
    	this->hwcontrol(mtd, NAND_CTL_SETCLE);
    	/*
    	 * Write out the command to the device.
    	 */
    	if (command == NAND_CMD_SEQIN) {
    		int readcmd;
    
    		if (column >= mtd->oobblock) {
    			/* OOB area */
    			column -= mtd->oobblock;
    			readcmd = NAND_CMD_READOOB;
    		} else if (column < 256) {
    			/* First 256 bytes --> READ0 */
    			readcmd = NAND_CMD_READ0;
    		} else {
    			column -= 256;
    			readcmd = NAND_CMD_READ1;
    		}
    		this->write_byte(mtd, readcmd);
    	}
    	this->write_byte(mtd, command);
    
    	/* Set ALE and clear CLE to start address cycle */
    	this->hwcontrol(mtd, NAND_CTL_CLRCLE);
    
    	if (column != -1 || page_addr != -1) {
    		this->hwcontrol(mtd, NAND_CTL_SETALE);
    
    		/* Serially input address */
    		if (column != -1) {
    			/* 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);
    	}
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    	/*
    	 * program and erase have their own busy handlers
    
    	 * status and sequential in needs no delay
    	*/
    	switch (command) {
    
    	case NAND_CMD_PAGEPROG:
    	case NAND_CMD_ERASE1:
    	case NAND_CMD_ERASE2:
    	case NAND_CMD_SEQIN:
    	case NAND_CMD_STATUS:
    		return;
    
    	case NAND_CMD_RESET:
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    		if (this->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));
    		return;
    
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	/* This applies to read commands */
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    		/*
    
    		 * If we don't have access to the busy pin, we apply the given
    		 * command delay
    		*/
    		if (!this->dev_ready) {
    			udelay (this->chip_delay);
    			return;
    
    	/* Apply this short delay always to ensure that we do wait tWB in
    	 * any case on any machine. */
    	ndelay (100);
    	/* wait until command is processed */
    	while (!this->dev_ready(mtd));
    }
    
    /**
     * nand_command_lp - [DEFAULT] Send command to NAND large page device
     * @mtd:	MTD device structure
     * @command:	the command to be sent
     * @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.
     *
     */
    static void nand_command_lp (struct mtd_info *mtd, unsigned command, int column, int page_addr)
    {
    	register struct nand_chip *this = mtd->priv;
    
    	/* Emulate NAND_CMD_READOOB */
    	if (command == NAND_CMD_READOOB) {
    		column += mtd->oobblock;
    		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);
    
    	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 & 0xff);
    			this->write_byte(mtd, column >> 8);
    
    		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 > 128MiB */
    			if (this->chipsize > (128 << 20))
    				this->write_byte(mtd, (unsigned char) ((page_addr >> 16) & 0xff));
    		}
    		/* Latch in address */
    		this->hwcontrol(mtd, NAND_CTL_CLRALE);
    	}
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    	/*
    	 * program and erase have their own busy handlers
    
    	 * status and sequential in needs no delay
    	*/
    	switch (command) {
    
    	case NAND_CMD_CACHEDPROG:
    	case NAND_CMD_PAGEPROG:
    	case NAND_CMD_ERASE1:
    	case NAND_CMD_ERASE2:
    	case NAND_CMD_SEQIN:
    	case NAND_CMD_STATUS:
    		return;
    
    
    	case NAND_CMD_RESET:
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    		if (this->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));
    		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 */
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    	/* This applies to read commands */
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    		/*
    
    		 * If we don't have access to the busy pin, we apply the given
    		 * command delay
    		*/
    		if (!this->dev_ready) {
    			udelay (this->chip_delay);
    			return;
    
    	/* Apply this short delay always to ensure that we do wait tWB in
    	 * any case on any machine. */
    	ndelay (100);
    	/* wait until command is processed */
    	while (!this->dev_ready(mtd));
    }
    
    /**
     * nand_get_device - [GENERIC] Get chip for selected access
     * @this:	the nand chip descriptor
     * @mtd:	MTD device structure
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
     * @new_state:	the state which is requested
    
     *
     * Get the device and lock it for exclusive access
     */
    /* XXX U-BOOT XXX */
    #if 0
    static void nand_get_device (struct nand_chip *this, struct mtd_info *mtd, int new_state)
    {
    	struct nand_chip *active = this;
    
    	DECLARE_WAITQUEUE (wait, current);
    
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	/*
    	 * 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);
    	}
    
    	if (active == this) {
    		spin_lock (&this->chip_lock);
    		if (this->state == FL_READY) {
    			this->state = new_state;
    			spin_unlock (&this->chip_lock);
    			return;
    		}
    
    	set_current_state (TASK_UNINTERRUPTIBLE);
    	add_wait_queue (&active->wq, &wait);
    	spin_unlock (&active->chip_lock);
    	schedule ();
    	remove_wait_queue (&active->wq, &wait);
    	goto retry;
    }
    #else
    static void nand_get_device (struct nand_chip *this, struct mtd_info *mtd, int new_state) {}
    #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
     *
     * Wait for command done. This applies to erase and program only
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
     * 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)
    {
    	unsigned long	timeo = jiffies;
    	int	status;
    
    	if (state == FL_ERASING)
    		 timeo += (HZ * 400) / 1000;
    	else
    		 timeo += (HZ * 20) / 1000;
    
    	/* Apply this short delay always to ensure that we do wait tWB in
    	 * any case on any machine. */
    	ndelay (100);
    
    	if ((state == FL_ERASING) && (this->options & NAND_IS_AND))
    		this->cmdfunc (mtd, NAND_CMD_STATUS_MULTI, -1, -1);
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	else
    
    		this->cmdfunc (mtd, NAND_CMD_STATUS, -1, -1);
    
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	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))
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    				break;
    
    		} else {
    			if (this->read_byte(mtd) & NAND_STATUS_READY)
    				break;
    		}
    		yield ();
    	}
    	status = (int) this->read_byte(mtd);
    	return status;
    
    	return 0;
    }
    #else
    static int nand_wait(struct mtd_info *mtd, struct nand_chip *this, int state)
    {
    
    	unsigned long	timeo;
    
    	if (state == FL_ERASING)
    		timeo = CFG_HZ * 400;
    	else
    		timeo = CFG_HZ * 20;
    
    	if ((state == FL_ERASING) && (this->options & NAND_IS_AND))
    		this->cmdfunc(mtd, NAND_CMD_STATUS_MULTI, -1, -1);
    	else
    		this->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1);
    
    
    		if (get_timer(0) > timeo) {
    			printf("Timeout!");
    
    
    		if (this->dev_ready) {
    			if (this->dev_ready(mtd))
    				break;
    		} else {
    			if (this->read_byte(mtd) & NAND_STATUS_READY)
    				break;
    		}
    	}
    
    
    	/* XXX nand device 1 on dave (PPChameleonEVB) needs more time */
    	reset_timer();
    	while (get_timer(0) < 10);
    
    
    	return this->read_byte(mtd);
    
    }
    #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.
     */
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    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)
    {
    	int 	i, status;
    	u_char	ecc_code[32];
    	int	eccmode = oobsel->useecc ? this->eccmode : NAND_ECC_NONE;
    	int  	*oob_config = oobsel->eccpos;
    	int	datidx = 0, eccidx = 0, eccsteps = this->eccsteps;
    	int	eccbytes = 0;
    
    	/* FIXME: Enable cached programming */
    	cached = 0;
    
    	/* Send command to begin auto page programming */
    	this->cmdfunc (mtd, NAND_CMD_SEQIN, 0x00, page);
    
    	/* 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;
    
    	/* 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 immidiately after
    			 * the data bytes (words) */
    			if (this->options & NAND_HWECC_SYNDROME)
    				this->write_buf(mtd, ecc_code, eccbytes);
    			datidx += this->eccsize;
    		}
    		break;
    	}
    
    	/* Write out OOB data */
    	if (this->options & NAND_HWECC_SYNDROME)
    		this->write_buf(mtd, &oob_buf[oobsel->eccbytes], mtd->oobsize - oobsel->eccbytes);
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	else
    
    		this->write_buf(mtd, oob_buf, mtd->oobsize);
    
    	/* Send command to actually program the data */
    	this->cmdfunc (mtd, cached ? NAND_CMD_CACHEDPROG : NAND_CMD_PAGEPROG, -1, -1);
    
    	if (!cached) {
    		/* call wait ready function */
    		status = this->waitfunc (mtd, this, FL_WRITING);
    		/* See if device thinks it succeeded */
    		if (status & 0x01) {
    			DEBUG (MTD_DEBUG_LEVEL0, "%s: " "Failed write, page 0x%08x, ", __FUNCTION__, page);
    			return -EIO;
    		}
    	} else {
    		/* FIXME: Implement cached programming ! */
    		/* wait until cache is ready*/
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    		/* status = this->waitfunc (mtd, this, FL_CACHEDRPG); */
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	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
     *
     * The NAND device assumes that it is always writing to a cleanly erased page.
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
     * 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
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
     * 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.
     */
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    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)
    {
    	int 	i, j, datidx = 0, oobofs = 0, res = -EIO;
    	int	eccsteps = this->eccsteps;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	int	hweccbytes;
    
    	u_char 	oobdata[64];
    
    	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);