patch-2.4.6 linux/drivers/mtd/nand/nand.c

Next file: linux/drivers/mtd/nand/nand_ecc.c
Previous file: linux/drivers/mtd/nand/Makefile
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.4.5/linux/drivers/mtd/nand/nand.c linux/drivers/mtd/nand/nand.c
@@ -0,0 +1,1369 @@
+/*
+ *  drivers/mtd/nand.c
+ *
+ *  Copyright (C) 2000 Steven J. Hill (sjhill@cotw.com)
+ *
+ * $Id: nand.c,v 1.10 2001/03/20 07:26:01 dwmw2 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.
+ *
+ *  Overview:
+ *   This is the generic MTD driver for NAND flash devices. It should be
+ *   capable of working with almost all NAND chips currently available.
+ */
+
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/nand_ids.h>
+#include <asm/io.h>
+
+#ifdef CONFIG_MTD_NAND_ECC
+#include <linux/mtd/nand_ecc.h>
+#endif
+
+/*
+ * Macros for low-level register control
+ */
+#define NAND_CTRL (*(volatile unsigned char *) \
+			((struct nand_chip *) mtd->priv)->CTRL_ADDR)
+#define nand_select()	NAND_CTRL &= ~this->NCE; \
+			nand_command(mtd, NAND_CMD_RESET, -1, -1); \
+			udelay (10);
+#define nand_deselect() NAND_CTRL |= ~this->NCE;
+
+/*
+ * NAND low-level MTD interface functions
+ */
+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 *ecc_code);
+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 *ecc_code);
+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_writev (struct mtd_info *mtd, const struct iovec *vecs,
+				unsigned long count, loff_t to, size_t *retlen);
+static int nand_erase (struct mtd_info *mtd, struct erase_info *instr);
+static void nand_sync (struct mtd_info *mtd);
+
+/*
+ * Send command to NAND device
+ */
+static void nand_command (struct mtd_info *mtd, unsigned command,
+				int column, int page_addr)
+{
+	register struct nand_chip *this = mtd->priv;
+	register unsigned long NAND_IO_ADDR = this->IO_ADDR;
+
+	/* Begin command latch cycle */
+	NAND_CTRL |= this->CLE;
+
+	/*
+	 * Write out the command to the device.
+	 */
+	if (command != NAND_CMD_SEQIN)	
+		writeb (command, NAND_IO_ADDR);
+	else {
+		if (mtd->oobblock == 256 && column >= 256) {
+			column -= 256;
+			writeb(NAND_CMD_RESET, NAND_IO_ADDR);
+			writeb(NAND_CMD_READOOB, NAND_IO_ADDR);
+			writeb(NAND_CMD_SEQIN, NAND_IO_ADDR);
+		}
+		else if (mtd->oobblock == 512 && column >= 256) {
+			if (column < 512) {
+				column -= 256;
+				writeb(NAND_CMD_READ1, NAND_IO_ADDR);
+				writeb(NAND_CMD_SEQIN, NAND_IO_ADDR);
+			}
+			else {
+				column -= 512;
+				writeb(NAND_CMD_READOOB, NAND_IO_ADDR);
+				writeb(NAND_CMD_SEQIN, NAND_IO_ADDR);
+			}
+		}
+		else {
+			writeb(NAND_CMD_READ0, NAND_IO_ADDR);
+			writeb(NAND_CMD_SEQIN, NAND_IO_ADDR);
+		}
+	}
+
+	/* Set ALE and clear CLE to start address cycle */
+	NAND_CTRL &= ~this->CLE;
+	NAND_CTRL |= this->ALE;
+
+	/* Serially input address */
+	if (column != -1)
+		writeb (column, NAND_IO_ADDR);
+	if (page_addr != -1) {
+		writeb ((unsigned char) (page_addr & 0xff), NAND_IO_ADDR);
+		writeb ((unsigned char) ((page_addr >> 8) & 0xff), NAND_IO_ADDR);
+		/* One more address cycle for higher density devices */
+		if (mtd->size & 0x0c000000) {
+			writeb ((unsigned char) ((page_addr >> 16) & 0x0f),
+					NAND_IO_ADDR);
+		}
+	}
+
+	/* Latch in address */
+	NAND_CTRL &= ~this->ALE;
+
+	/* Pause for 15us */
+	udelay (15);
+}
+
+/*
+ * NAND read
+ */
+static int nand_read (struct mtd_info *mtd, loff_t from, size_t len,
+			size_t *retlen, u_char *buf)
+{
+#ifdef CONFIG_MTD_NAND_ECC
+	struct nand_chip *this = mtd->priv;
+	
+	return nand_read_ecc (mtd, from, len, retlen, buf, this->ecc_code_buf);
+#else
+	return nand_read_ecc (mtd, from, len, retlen, buf, NULL);
+#endif
+}
+
+/*
+ * NAND read with ECC
+ */
+static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len,
+				size_t *retlen, u_char *buf, u_char *ecc_code)
+{
+	int j, col, page, state;
+	int erase_state = 0;
+	struct nand_chip *this = mtd->priv;
+	DECLARE_WAITQUEUE(wait, current);
+#ifdef CONFIG_MTD_NAND_ECC
+	int ecc_result;
+	u_char ecc_calc[6];
+#endif
+
+	DEBUG (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) {
+		DEBUG (MTD_DEBUG_LEVEL0,
+			"nand_read_ecc: Attempt read beyond end of device\n");
+		*retlen = 0;
+		return -EINVAL;
+	}
+
+	/* Grab the lock and see if the device is available */
+retry:
+	spin_lock_bh (&this->chip_lock);
+
+	switch (this->state) {
+	case FL_READY:
+		this->state = FL_READING;
+		spin_unlock_bh (&this->chip_lock);
+		break;
+
+	case FL_ERASING:
+		this->state = FL_READING;
+		erase_state = 1;
+		spin_unlock_bh (&this->chip_lock);
+		break;
+
+	default:
+		set_current_state (TASK_UNINTERRUPTIBLE);
+		add_wait_queue (&this->wq, &wait);
+		spin_unlock_bh (&this->chip_lock);
+		schedule();
+
+		remove_wait_queue (&this->wq, &wait);
+		goto retry;
+	};
+
+	/* First we calculate the starting page */
+	page = from >> this->page_shift;
+
+	/* Get raw starting column */
+	col = from & (mtd->oobblock - 1);
+
+	/* State machine for devices having pages larger than 256 bytes */
+	state = (col < mtd->eccsize) ? 0 : 1;
+
+	/* Calculate column address within ECC block context */
+	col = (col >= mtd->eccsize) ? (col - mtd->eccsize) : col;
+
+	/* Initialize return value */
+	*retlen = 0;
+
+	/* Select the NAND device */
+	nand_select ();
+
+	/* Loop until all data read */
+	while (*retlen < len) {
+
+#ifdef CONFIG_MTD_NAND_ECC
+		/* Send the read command */
+		if (!state)
+			nand_command (mtd, NAND_CMD_READ0, 0x00, page);
+		else 
+			nand_command (mtd, NAND_CMD_READ1, 0x00, page);
+
+		/* Read in a block big enough for ECC */
+		for (j=0 ; j < mtd->eccsize ; j++)
+			this->data_buf[j] = readb (this->IO_ADDR);
+
+		/* Read in the out-of-band data */
+		if (!state) {
+			nand_command (mtd, NAND_CMD_READOOB, 0x00, page);
+			for (j=0 ; j<3 ; j++)
+				ecc_code[j] = readb(this->IO_ADDR);
+			nand_command (mtd, NAND_CMD_READ0, 0x00, page);
+		}
+		else {
+			nand_command (mtd, NAND_CMD_READOOB, 0x03, page);
+			for (j=3 ; j<6 ; j++)
+				ecc_code[j] = readb(this->IO_ADDR);
+			nand_command (mtd, NAND_CMD_READ0, 0x00, page);
+		}
+
+		/* Calculate the ECC and verify it */
+		if (!state) {
+			nand_calculate_ecc (&this->data_buf[0],
+						&ecc_calc[0]);
+			ecc_result = nand_correct_data (&this->data_buf[0],
+						&ecc_code[0], &ecc_calc[0]);
+		}
+		else {
+			nand_calculate_ecc (&this->data_buf[0],
+						&ecc_calc[3]);
+			ecc_result = nand_correct_data (&this->data_buf[0],
+						&ecc_code[3], &ecc_calc[3]);
+		}
+		if (ecc_result == -1) {
+			DEBUG (MTD_DEBUG_LEVEL0,
+				"nand_read_ecc: " \
+				"Failed ECC read, page 0x%08x\n", page);
+			nand_deselect ();
+			spin_lock_bh (&this->chip_lock);
+			if (erase_state)
+				this->state = FL_ERASING;
+			else
+				this->state = FL_READY;
+			wake_up (&this->wq);
+			spin_unlock_bh (&this->chip_lock);
+			return -EIO;
+		}
+
+		/* Read the data from ECC data buffer into return buffer */
+		if ((*retlen + (mtd->eccsize - col)) >= len) {
+			while (*retlen < len)
+				buf[(*retlen)++] = this->data_buf[col++];
+			/* We're done */
+			continue;
+		}
+		else
+			for (j=col ; j < mtd->eccsize ; j++)
+				buf[(*retlen)++] = this->data_buf[j];
+#else
+		/* Send the read command */
+		if (!state)
+			nand_command (mtd, NAND_CMD_READ0, col, page);
+		else 
+			nand_command (mtd, NAND_CMD_READ1, col, page);
+
+		/* Read the data directly into the return buffer */ 
+		if ((*retlen + (mtd->eccsize - col)) >= len) {
+			while (*retlen < len)
+				buf[(*retlen)++] = readb (this->IO_ADDR);
+			/* We're done */
+			continue;
+		}
+		else
+			for (j=col ; j < mtd->eccsize ; j++)
+				buf[(*retlen)++] = readb (this->IO_ADDR);
+#endif
+
+		/*
+		 * If the amount of data to be read is greater than
+		 * (256 - col), then all subsequent reads will take
+		 * place on page or half-page (in the case of 512 byte
+		 * page devices) aligned boundaries and the column
+		 * address will be zero. Setting the column address to
+		 * to zero after the first read allows us to simplify
+		 * the reading of data and the if/else statements above.
+		 */
+		if (col)
+			col = 0x00;
+
+		/* Increment page address */
+		if ((mtd->oobblock == 256) || state)
+			page++;
+
+		/* Toggle state machine */
+		if (mtd->oobblock == 512)
+			state = state ? 0 : 1;
+	}
+
+	/* De-select the NAND device */
+	nand_deselect ();
+
+	/* Wake up anyone waiting on the device */
+	spin_lock_bh (&this->chip_lock);
+	if (erase_state)
+		this->state = FL_ERASING;
+	else
+		this->state = FL_READY;
+	wake_up (&this->wq);
+	spin_unlock_bh (&this->chip_lock);
+	
+	/* Return happy */
+	return 0;
+}
+
+/*
+ * NAND read out-of-band
+ */
+static int nand_read_oob (struct mtd_info *mtd, loff_t from, size_t len,
+				size_t *retlen, u_char *buf)
+{
+	int i, col, page;
+	int erase_state = 0;
+	struct nand_chip *this = mtd->priv;
+	DECLARE_WAITQUEUE(wait, current);
+	
+	DEBUG (MTD_DEBUG_LEVEL3,
+		"nand_read_oob: from = 0x%08x, len = %i\n", (unsigned int) from,
+		(int) len);
+
+	/* Shift to get page */
+	page = ((int) from) >> this->page_shift;
+
+	/* Mask to get column */
+	col = from & 0x0f;
+
+	/* Initialize return length value */
+	*retlen = 0;
+
+	/* Do not allow read past end of page */
+	if ((col + len) > mtd->oobsize) {
+		DEBUG (MTD_DEBUG_LEVEL0,
+			"nand_read_oob: Attempt read past end of page " \
+			"0x%08x, column %i, length %i\n", page, col, len);
+		return -EINVAL;
+	}
+
+retry:
+	/* Grab the lock and see if the device is available */
+	spin_lock_bh (&this->chip_lock);
+
+	switch (this->state) {
+	case FL_READY:
+		this->state = FL_READING;
+		spin_unlock_bh (&this->chip_lock);
+		break;
+
+	case FL_ERASING:
+		this->state = FL_READING;
+		erase_state = 1;
+		spin_unlock_bh (&this->chip_lock);
+		break;
+
+	default:
+		set_current_state (TASK_UNINTERRUPTIBLE);
+		add_wait_queue (&this->wq, &wait);
+		spin_unlock_bh (&this->chip_lock);
+		schedule();
+
+		remove_wait_queue (&this->wq, &wait);
+		goto retry;
+	};
+
+	/* Select the NAND device */
+	nand_select ();
+
+	/* Send the read command */
+	nand_command (mtd, NAND_CMD_READOOB, col, page);	
+
+	/* Read the data */
+	for (i = 0 ; i < len ; i++)
+		buf[i] = readb (this->IO_ADDR);
+
+	/* De-select the NAND device */
+	nand_deselect ();
+
+	/* Wake up anyone waiting on the device */
+	spin_lock_bh (&this->chip_lock);
+	if (erase_state)
+		this->state = FL_ERASING;
+	else
+		this->state = FL_READY;
+	wake_up (&this->wq);
+	spin_unlock_bh (&this->chip_lock);
+
+	/* Return happy */
+	*retlen = len;
+	return 0;
+}
+
+/*
+ * NAND write
+ */
+static int nand_write (struct mtd_info *mtd, loff_t to, size_t len,
+			size_t *retlen, const u_char *buf)
+{
+#ifdef CONFIG_MTD_NAND_ECC
+	struct nand_chip *this = mtd->priv;
+	
+	return nand_write_ecc (mtd, to, len, retlen, buf, this->ecc_code_buf);
+#else
+	return nand_write_ecc (mtd, to, len, retlen, buf, NULL);
+#endif
+}
+
+/*
+ * NAND write with ECC
+ */
+static int nand_write_ecc (struct mtd_info *mtd, loff_t to, size_t len,
+				size_t *retlen, const u_char *buf,
+				u_char *ecc_code)
+{
+	int i, page, col, cnt, status;
+	struct nand_chip *this = mtd->priv;
+	DECLARE_WAITQUEUE(wait, current);
+#ifdef CONFIG_MTD_NAND_ECC
+	int ecc_bytes = (mtd->oobblock == 512) ? 6 : 3;
+#endif
+
+	DEBUG (MTD_DEBUG_LEVEL3,
+		"nand_write_ecc: to = 0x%08x, len = %i\n", (unsigned int) to,
+		(int) len);
+
+	/* Do not allow write past end of page */
+	if ((to + len) > mtd->size) {
+		DEBUG (MTD_DEBUG_LEVEL0,
+			"nand_write_ecc: Attempted write past end of device\n");
+		return -EINVAL;
+	}
+
+retry:
+	/* Grab the lock and see if the device is available */
+	spin_lock_bh (&this->chip_lock);
+
+	switch (this->state) {
+	case FL_READY:
+		this->state = FL_WRITING;
+		spin_unlock_bh (&this->chip_lock);
+		break;
+
+	default:
+		set_current_state (TASK_UNINTERRUPTIBLE);
+		add_wait_queue (&this->wq, &wait);
+		spin_unlock_bh (&this->chip_lock);
+		schedule();
+
+		remove_wait_queue (&this->wq, &wait);
+		goto retry;
+	};
+
+	/* Shift to get page */
+	page = ((int) to) >> this->page_shift;
+
+	/* Get the starting column */
+	col = to & (mtd->oobblock - 1);
+
+	/* Initialize return length value */
+	*retlen = 0;
+
+	/* Select the NAND device */
+	nand_select ();
+
+	/* Check the WP bit */
+	nand_command (mtd, NAND_CMD_STATUS, -1, -1);
+	if (!(readb (this->IO_ADDR) & 0x80)) {
+		DEBUG (MTD_DEBUG_LEVEL0,
+			"nand_write_ecc: Device is write protected!!!\n");
+		nand_deselect ();
+		spin_lock_bh (&this->chip_lock);
+		this->state = FL_READY;
+		wake_up (&this->wq);
+		spin_unlock_bh (&this->chip_lock);
+		return -EIO;
+	}
+
+	/* Loop until all data is written */
+	while (*retlen < len) {
+		/* Write data into buffer */
+		if ((col + len) >= mtd->oobblock)
+			for(i=col, cnt=0 ; i < mtd->oobblock ; i++, cnt++)
+				this->data_buf[i] = buf[(*retlen + cnt)];
+		else
+			for(i=col, cnt=0 ; cnt < (len - *retlen) ; i++, cnt++)
+				this->data_buf[i] = buf[(*retlen + cnt)];
+		
+#ifdef CONFIG_MTD_NAND_ECC
+		/* Zero out the ECC array */
+		for (i=0 ; i < 6 ; i++)
+			ecc_code[i] = 0x00;
+
+		/* Calculate and write the ECC if we have enough data */
+		if ((col < mtd->eccsize) &&
+			((col + (len - *retlen)) >= mtd->eccsize)) {
+			nand_command (mtd, NAND_CMD_READ0, col, page);
+			for (i=0 ; i < col ; i++)
+				this->data_buf[i] = readb (this->IO_ADDR); 
+			nand_calculate_ecc (&this->data_buf[0], &ecc_code[0]);
+			for (i=0 ; i<3 ; i++)
+				this->data_buf[(mtd->oobblock + i)] =
+					ecc_code[i];
+		}
+
+		/* Calculate and write the second ECC if we have enough data */
+		if ((mtd->oobblock == 512) &&
+			((col + (len - *retlen)) >= mtd->oobblock)) {
+			nand_calculate_ecc (&this->data_buf[256], &ecc_code[3]);
+			for (i=3 ; i<6 ; i++)
+				this->data_buf[(mtd->oobblock + i)] =
+					ecc_code[i];
+		}
+
+		/* Write ones for partial page programming */
+		for (i=ecc_bytes ; i < mtd->oobsize ; i++)
+			this->data_buf[(mtd->oobblock + i)] = 0xff;
+#else
+		/* Write ones for partial page programming */
+		for (i=mtd->oobblock ; i < (mtd->oobblock + mtd->oobsize) ; i++)
+			this->data_buf[i] = 0xff;
+#endif
+
+		/* Write pre-padding bytes into buffer */
+		for (i=0 ; i < col ; i++)
+			this->data_buf[i] = 0xff;
+
+		/* Write post-padding bytes into buffer */
+		if ((col + (len - *retlen)) < mtd->oobblock) {
+			for(i=(col + cnt) ; i < mtd->oobblock ; i++)
+				this->data_buf[i] = 0xff;
+		}
+
+		/* Send command to begin auto page programming */
+		nand_command (mtd, NAND_CMD_SEQIN, 0x00, page);
+
+		/* Write out complete page of data */
+		for (i=0 ; i < (mtd->oobblock + mtd->oobsize) ; i++)
+			writeb (this->data_buf[i], this->IO_ADDR);
+
+		/* Send command to actually program the data */
+		nand_command (mtd, NAND_CMD_PAGEPROG, -1, -1);
+
+		/*
+		 * Wait for program operation to complete. This could
+		 * take up to 3000us (3ms) on some devices, so we try
+		 * and exit as quickly as possible.
+		 */
+		status = 0;
+		for (i=0 ; i<24 ; i++) {
+			/* Delay for 125us */
+			udelay (125);
+
+			/* Check the status */
+			nand_command (mtd, NAND_CMD_STATUS, -1, -1);
+			status = (int) readb (this->IO_ADDR);
+			if (status & 0x40)
+				break;
+		}
+
+		/* See if device thinks it succeeded */
+		if (status & 0x01) {
+			DEBUG (MTD_DEBUG_LEVEL0,
+				"nand_write_ecc: " \
+				"Failed write, page 0x%08x, " \
+				"%6i bytes were succesful\n", page, *retlen);
+			nand_deselect ();
+			spin_lock_bh (&this->chip_lock);
+			this->state = FL_READY;
+			wake_up (&this->wq);
+			spin_unlock_bh (&this->chip_lock);
+			return -EIO;
+		}
+
+#ifdef CONFIG_MTD_NAND_VERIFY_WRITE
+		/*
+		 * 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.
+		 */
+		
+		/* Send command to read back the page */
+		if (col < mtd->eccsize)
+			nand_command (mtd, NAND_CMD_READ0, col, page);
+		else
+			nand_command (mtd, NAND_CMD_READ1, col - 256, page);
+
+		/* Loop through and verify the data */
+		for (i=col ; i < cnt ; i++) {
+			if (this->data_buf[i] != readb (this->IO_ADDR)) {
+				DEBUG (MTD_DEBUG_LEVEL0,
+					"nand_write_ecc: " \
+					"Failed write verify, page 0x%08x, " \
+					"%6i bytes were succesful\n",
+					page, *retlen);
+				nand_deselect ();
+				spin_lock_bh (&this->chip_lock);
+				this->state = FL_READY;
+				wake_up (&this->wq);
+				spin_unlock_bh (&this->chip_lock);
+				return -EIO;
+			}
+		}
+
+#ifdef CONFIG_MTD_NAND_ECC
+		/*
+		 * We also want to check that the ECC bytes wrote
+		 * correctly for the same reasons stated above.
+		 */
+		nand_command (mtd, NAND_CMD_READOOB, 0x00, page);
+		for (i=0 ; i < ecc_bytes ; i++) {
+			if ((readb (this->IO_ADDR) != ecc_code[i]) &&
+					ecc_code[i]) {
+				DEBUG (MTD_DEBUG_LEVEL0,
+					"nand_write_ecc: Failed ECC write " \
+					"verify, page 0x%08x, " \
+					"%6i bytes were succesful\n",
+					page, i);
+				nand_deselect ();
+				spin_lock_bh (&this->chip_lock);
+				this->state = FL_READY;
+				wake_up (&this->wq);
+				spin_unlock_bh (&this->chip_lock);
+				return -EIO;
+			}
+		}
+#endif
+
+#endif
+
+		/*
+		 * If we are writing a large amount of data and/or it
+		 * crosses page or half-page boundaries, we set the
+		 * the column to zero. It simplifies the program logic.
+		 */
+		if (col)
+			col = 0x00;
+
+		/* Update written bytes count */
+		*retlen += cnt;
+
+		/* Increment page address */
+		page++;
+	}
+
+	/* De-select the NAND device */
+	nand_deselect ();
+
+	/* Wake up anyone waiting on the device */
+	spin_lock_bh (&this->chip_lock);
+	this->state = FL_READY;
+	wake_up (&this->wq);
+	spin_unlock_bh (&this->chip_lock);
+
+	/* Return happy */
+	*retlen = len;
+	return 0;
+}
+
+/*
+ * NAND write out-of-band
+ */
+static int nand_write_oob (struct mtd_info *mtd, loff_t to, size_t len,
+				size_t *retlen, const u_char *buf)
+{
+	int i, column, page, status;
+	struct nand_chip *this = mtd->priv;
+	DECLARE_WAITQUEUE(wait, current);
+	
+	DEBUG (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;
+
+	/* Mask to get column */
+	column = to & 0x1f;
+
+	/* Initialize return length value */
+	*retlen = 0;
+
+	/* Do not allow write past end of page */
+	if ((column + len) > mtd->oobsize) {
+		DEBUG (MTD_DEBUG_LEVEL0,
+			"nand_write_oob: Attempt to write past end of page\n");
+		return -EINVAL;
+	}
+
+retry:
+	/* Grab the lock and see if the device is available */
+	spin_lock_bh (&this->chip_lock);
+
+	switch (this->state) {
+	case FL_READY:
+		this->state = FL_WRITING;
+		spin_unlock_bh (&this->chip_lock);
+		break;
+
+	default:
+		set_current_state (TASK_UNINTERRUPTIBLE);
+		add_wait_queue (&this->wq, &wait);
+		spin_unlock_bh (&this->chip_lock);
+		schedule();
+
+		remove_wait_queue (&this->wq, &wait);
+		goto retry;
+	};
+
+	/* Select the NAND device */
+	nand_select ();
+
+	/* Check the WP bit */
+	nand_command (mtd, NAND_CMD_STATUS, -1, -1);
+	if (!(readb (this->IO_ADDR) & 0x80)) {
+		DEBUG (MTD_DEBUG_LEVEL0,
+			"nand_write_oob: Device is write protected!!!\n");
+		nand_deselect ();
+		spin_lock_bh (&this->chip_lock);
+		this->state = FL_READY;
+		wake_up (&this->wq);
+		spin_unlock_bh (&this->chip_lock);
+		return -EIO;
+	}
+
+	/* Write out desired data */
+	nand_command (mtd, NAND_CMD_SEQIN, column + 512, page);
+	for (i=0 ; i<len ; i++)
+		writeb (buf[i], this->IO_ADDR);
+
+	/* Send command to program the OOB data */
+	nand_command (mtd, NAND_CMD_PAGEPROG, -1, -1);
+
+	/*
+	 * Wait for program operation to complete. This could
+	 * take up to 3000us (3ms) on some devices, so we try
+	 * and exit as quickly as possible.
+	 */
+	status = 0;
+	for (i=0 ; i<24 ; i++) {
+		/* Delay for 125us */
+		udelay (125);
+
+		/* Check the status */
+		nand_command (mtd, NAND_CMD_STATUS, -1, -1);
+		status = (int) readb (this->IO_ADDR);
+		if (status & 0x40)
+			break;
+	}
+
+	/* See if device thinks it succeeded */
+	if (status & 0x01) {
+		DEBUG (MTD_DEBUG_LEVEL0,
+			"nand_write_oob: " \
+			"Failed write, page 0x%08x\n", page);
+		nand_deselect ();
+		spin_lock_bh (&this->chip_lock);
+		this->state = FL_READY;
+		wake_up (&this->wq);
+		spin_unlock_bh (&this->chip_lock);
+		return -EIO;
+	}
+
+#ifdef CONFIG_MTD_NAND_VERIFY_WRITE
+	/* Send command to read back the data */
+	nand_command (mtd, NAND_CMD_READOOB, column, page);
+
+	/* Loop through and verify the data */
+	for (i=0 ; i<len ; i++) {
+		if (buf[i] != readb (this->IO_ADDR)) {
+			DEBUG (MTD_DEBUG_LEVEL0,
+				"nand_write_oob: " \
+				"Failed write verify, page 0x%08x\n", page);
+			nand_deselect ();
+			spin_lock_bh (&this->chip_lock);
+			this->state = FL_READY;
+			wake_up (&this->wq);
+			spin_unlock_bh (&this->chip_lock);
+			return -EIO;
+		}
+	}
+#endif
+
+	/* De-select the NAND device */
+	nand_deselect ();
+
+	/* Wake up anyone waiting on the device */
+	spin_lock_bh (&this->chip_lock);
+	this->state = FL_READY;
+	wake_up (&this->wq);
+	spin_unlock_bh (&this->chip_lock);
+
+	/* Return happy */
+	*retlen = len;
+	return 0;
+}
+
+/*
+ * NAND write with iovec
+ */
+static int nand_writev (struct mtd_info *mtd, const struct iovec *vecs,
+				unsigned long count, loff_t to, size_t *retlen)
+{
+	int i, page, col, cnt, len, total_len, status;
+	struct nand_chip *this = mtd->priv;
+	DECLARE_WAITQUEUE(wait, current);
+#ifdef CONFIG_MTD_NAND_ECC
+	int ecc_bytes = (mtd->oobblock == 512) ? 6 : 3;
+#endif
+
+	/* Calculate total length of data */
+	total_len = 0;
+	for (i=0 ; i < count ; i++)
+		total_len += (int) vecs[i].iov_len;
+
+	DEBUG (MTD_DEBUG_LEVEL3,
+		"nand_writev: to = 0x%08x, len = %i\n", (unsigned int) to,
+			(unsigned int) total_len);
+
+	/* Do not allow write past end of page */
+	if ((to + total_len) > mtd->size) {
+		DEBUG (MTD_DEBUG_LEVEL0,
+			"nand_writev: Attempted write past end of device\n");
+		return -EINVAL;
+	}
+
+retry:
+	/* Grab the lock and see if the device is available */
+	spin_lock_bh (&this->chip_lock);
+
+	switch (this->state) {
+	case FL_READY:
+		this->state = FL_WRITING;
+		spin_unlock_bh (&this->chip_lock);
+		break;
+
+	default:
+		set_current_state (TASK_UNINTERRUPTIBLE);
+		add_wait_queue (&this->wq, &wait);
+		spin_unlock_bh (&this->chip_lock);
+		schedule();
+
+		remove_wait_queue (&this->wq, &wait);
+		goto retry;
+	};
+
+	/* Shift to get page */
+	page = ((int) to) >> this->page_shift;
+
+	/* Get the starting column */
+	col = to & (mtd->oobblock - 1);
+
+	/* Initialize return length value */
+	*retlen = 0;
+
+	/* Select the NAND device */
+	nand_select ();
+
+	/* Check the WP bit */
+	nand_command (mtd, NAND_CMD_STATUS, -1, -1);
+	if (!(readb (this->IO_ADDR) & 0x80)) {
+		DEBUG (MTD_DEBUG_LEVEL0,
+			"nand_writev: Device is write protected!!!\n");
+		nand_deselect ();
+		spin_lock_bh (&this->chip_lock);
+		this->state = FL_READY;
+		wake_up (&this->wq);
+		spin_unlock_bh (&this->chip_lock);
+		return -EIO;
+	}
+
+	/* Loop until all iovecs' data has been written */
+	cnt = col;
+	len = 0;
+	while (count) {
+		/* Do any need pre-fill for partial page programming */
+		for (i=0 ; i < cnt ; i++)
+			this->data_buf[i] = 0xff;
+
+		/*
+		 * Read data out of each tuple until we have a full page
+		 * to write or we've read all the tuples.
+		 */
+		while ((cnt < mtd->oobblock) && count) {
+			this->data_buf[cnt++] =
+				((u_char *) vecs->iov_base)[len++];
+			if (len >= (int) vecs->iov_len) {
+				vecs++;
+				len = 0;
+				count--;
+			}
+		}
+		
+		/* Do any need post-fill for partial page programming */
+		for (i=cnt ; i < mtd->oobblock ; i++)
+			this->data_buf[i] = 0xff;
+
+#ifdef CONFIG_MTD_NAND_ECC
+		/* Zero out the ECC array */
+		for (i=0 ; i < 6 ; i++)
+			this->ecc_code_buf[i] = 0x00;
+
+		/* Calculate and write the first ECC */
+		if (col >= mtd->eccsize) {
+			nand_command (mtd, NAND_CMD_READ0, col, page);
+			for (i=0 ; i < col ; i++)
+				this->data_buf[i] = readb (this->IO_ADDR); 
+			nand_calculate_ecc (&this->data_buf[0],
+				&(this->ecc_code_buf[0]));
+			for (i=0 ; i<3 ; i++)
+				this->data_buf[(mtd->oobblock + i)] =
+					this->ecc_code_buf[i];
+		}
+
+		/* Calculate and write the second ECC */
+		if ((mtd->oobblock == 512) && (cnt == mtd->oobblock)) {
+			nand_calculate_ecc (&this->data_buf[256],
+				&(this->ecc_code_buf[3]));
+			for (i=3 ; i<6 ; i++)
+				this->data_buf[(mtd->oobblock + i)] =
+					this->ecc_code_buf[i];
+		}
+
+		/* Write ones for partial page programming */
+		for (i=ecc_bytes ; i < mtd->oobsize ; i++)
+			this->data_buf[(mtd->oobblock + i)] = 0xff;
+#else
+		/* Write ones for partial page programming */
+		for (i=mtd->oobblock ; i < (mtd->oobblock + mtd->oobsize) ; i++)
+			this->data_buf[i] = 0xff;
+#endif
+		/* Send command to begin auto page programming */
+		nand_command (mtd, NAND_CMD_SEQIN, 0x00, page);
+
+		/* Write out complete page of data */
+		for (i=0 ; i < (mtd->oobblock + mtd->oobsize) ; i++)
+			writeb (this->data_buf[i], this->IO_ADDR);
+
+		/* Send command to actually program the data */
+		nand_command (mtd, NAND_CMD_PAGEPROG, -1, -1);
+
+		/*
+		 * Wait for program operation to complete. This could
+		 * take up to 3000us (3ms) on some devices, so we try
+		 * and exit as quickly as possible.
+		 */
+		status = 0;
+		for (i=0 ; i<24 ; i++) {
+			/* Delay for 125us */
+			udelay (125);
+
+			/* Check the status */
+			nand_command (mtd, NAND_CMD_STATUS, -1, -1);
+			status = (int) readb (this->IO_ADDR);
+			if (status & 0x40)
+				break;
+		}
+
+		/* See if device thinks it succeeded */
+		if (status & 0x01) {
+			DEBUG (MTD_DEBUG_LEVEL0,
+				"nand_writev: " \
+				"Failed write, page 0x%08x, " \
+				"%6i bytes were succesful\n", page, *retlen);
+			nand_deselect ();
+			spin_lock_bh (&this->chip_lock);
+			this->state = FL_READY;
+			wake_up (&this->wq);
+			spin_unlock_bh (&this->chip_lock);
+			return -EIO;
+		}
+
+#ifdef CONFIG_MTD_NAND_VERIFY_WRITE
+		/*
+		 * 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.
+		 */
+		
+		/* Send command to read back the page */
+		if (col < mtd->eccsize)
+			nand_command (mtd, NAND_CMD_READ0, col, page);
+		else
+			nand_command (mtd, NAND_CMD_READ1, col - 256, page);
+
+		/* Loop through and verify the data */
+		for (i=col ; i < cnt ; i++) {
+			if (this->data_buf[i] != readb (this->IO_ADDR)) {
+				DEBUG (MTD_DEBUG_LEVEL0,
+					"nand_writev: " \
+					"Failed write verify, page 0x%08x, " \
+					"%6i bytes were succesful\n",
+					page, *retlen);
+				nand_deselect ();
+				spin_lock_bh (&this->chip_lock);
+				this->state = FL_READY;
+				wake_up (&this->wq);
+				spin_unlock_bh (&this->chip_lock);
+				return -EIO;
+			}
+		}
+
+#ifdef CONFIG_MTD_NAND_ECC
+		/*
+		 * We also want to check that the ECC bytes wrote
+		 * correctly for the same reasons stated above.
+		 */
+		nand_command (mtd, NAND_CMD_READOOB, 0x00, page);
+		for (i=0 ; i < ecc_bytes ; i++) {
+			if ((readb (this->IO_ADDR) != this->ecc_code_buf[i]) &&
+					this->ecc_code_buf[i]) {
+				DEBUG (MTD_DEBUG_LEVEL0,
+					"nand_writev: Failed ECC write " \
+					"verify, page 0x%08x, " \
+					"%6i bytes were succesful\n",
+					page, i);
+				nand_deselect ();
+				spin_lock_bh (&this->chip_lock);
+				this->state = FL_READY;
+				wake_up (&this->wq);
+				spin_unlock_bh (&this->chip_lock);
+				return -EIO;
+			}
+		}
+#endif
+
+#endif
+		/* Update written bytes count */
+		*retlen += (cnt - col);
+
+		/* Reset written byte counter and column */
+		col = cnt = 0;
+
+		/* Increment page address */
+		page++;
+	}
+
+	/* De-select the NAND device */
+	nand_deselect ();
+
+	/* Wake up anyone waiting on the device */
+	spin_lock_bh (&this->chip_lock);
+	this->state = FL_READY;
+	wake_up (&this->wq);
+	spin_unlock_bh (&this->chip_lock);
+
+	/* Return happy */
+	return 0;
+}
+
+/*
+ * NAND erase a block
+ */
+static int nand_erase (struct mtd_info *mtd, struct erase_info *instr)
+{
+	int i, page, len, status, pages_per_block;
+	struct nand_chip *this = mtd->priv;
+	DECLARE_WAITQUEUE(wait, current);
+
+	DEBUG (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 & (mtd->erasesize - 1)) {
+		DEBUG (MTD_DEBUG_LEVEL0,
+			"nand_erase: Unaligned address\n");
+		return -EINVAL;
+	}
+
+	/* Length must align on block boundary */
+	if (instr->len & (mtd->erasesize - 1)) {
+		DEBUG (MTD_DEBUG_LEVEL0,
+			"nand_erase: Length not block aligned\n");
+		return -EINVAL;
+	}
+
+	/* Do not allow erase past end of device */
+	if ((instr->len + instr->addr) > mtd->size) {
+		DEBUG (MTD_DEBUG_LEVEL0,
+			"nand_erase: Erase past end of device\n");
+		return -EINVAL;
+	}
+
+retry:
+	/* Grab the lock and see if the device is available */
+	spin_lock_bh (&this->chip_lock);
+
+	switch (this->state) {
+	case FL_READY:
+		this->state = FL_ERASING;
+		break;
+
+	default:
+		set_current_state (TASK_UNINTERRUPTIBLE);
+		add_wait_queue (&this->wq, &wait);
+		spin_unlock_bh (&this->chip_lock);
+		schedule();
+
+		remove_wait_queue (&this->wq, &wait);
+		goto retry;
+	};
+
+	/* Shift to get first page */
+	page = (int) (instr->addr >> this->page_shift);
+
+	/* Calculate pages in each block */
+	pages_per_block = mtd->erasesize / mtd->oobblock;
+
+	/* Select the NAND device */
+	nand_select ();
+
+	/* Check the WP bit */
+	nand_command (mtd, NAND_CMD_STATUS, -1, -1);
+	if (!(readb (this->IO_ADDR) & 0x80)) {
+		DEBUG (MTD_DEBUG_LEVEL0,
+			"nand_erase: Device is write protected!!!\n");
+		nand_deselect ();
+		this->state = FL_READY;
+		spin_unlock_bh (&this->chip_lock);
+		return -EIO;
+	}
+
+	/* Loop through the pages */
+	len = instr->len;
+	while (len) {
+		/* Send commands to erase a page */
+		nand_command(mtd, NAND_CMD_ERASE1, -1, page);
+		nand_command(mtd, NAND_CMD_ERASE2, -1, -1);
+
+		/*
+		 * Wait for program operation to complete. This could
+		 * take up to 4000us (4ms) on some devices, so we try
+		 * and exit as quickly as possible.
+		 */
+		status = 0;
+		for (i=0 ; i<32 ; i++) {
+			/* Delay for 125us */
+			udelay (125);
+
+			/* Check the status */
+			nand_command (mtd, NAND_CMD_STATUS, -1, -1);
+			status = (int) readb (this->IO_ADDR);
+			if (status & 0x40)
+				break;
+		}
+
+		/* See if block erase succeeded */
+		if (status & 0x01) {
+			DEBUG (MTD_DEBUG_LEVEL0,
+				"nand_erase: " \
+				"Failed erase, page 0x%08x\n", page);
+			nand_deselect ();
+			this->state = FL_READY;
+			spin_unlock_bh (&this->chip_lock);
+			return -EIO;
+		}
+
+		/* Increment page address and decrement length */
+		len -= mtd->erasesize;
+		page += pages_per_block;
+
+		/* Release the spin lock */
+		spin_unlock_bh (&this->chip_lock);
+
+erase_retry:
+		/* Check the state and sleep if it changed */
+		spin_lock_bh (&this->chip_lock);
+		if (this->state == FL_ERASING) {
+			continue;
+		}
+		else {
+			set_current_state (TASK_UNINTERRUPTIBLE);
+			add_wait_queue (&this->wq, &wait);
+			spin_unlock_bh (&this->chip_lock);
+			schedule();
+
+			remove_wait_queue (&this->wq, &wait);
+			goto erase_retry;
+		}
+	}
+	spin_unlock_bh (&this->chip_lock);
+
+	/* De-select the NAND device */
+	nand_deselect ();
+
+	/* Do call back function */
+	if (instr->callback)
+		instr->callback (instr);
+
+	/* The device is ready */
+	spin_lock_bh (&this->chip_lock);
+	this->state = FL_READY;
+	spin_unlock_bh (&this->chip_lock);
+
+	/* Return happy */
+	return 0;
+}
+
+/*
+ * NAND sync
+ */
+static void nand_sync (struct mtd_info *mtd)
+{
+	struct nand_chip *this = mtd->priv;
+	DECLARE_WAITQUEUE(wait, current);
+
+	DEBUG (MTD_DEBUG_LEVEL3, "nand_sync: called\n");
+
+retry:
+	/* Grab the spinlock */
+	spin_lock_bh(&this->chip_lock);
+
+	/* See what's going on */
+	switch(this->state) {
+	case FL_READY:
+	case FL_SYNCING:
+		this->state = FL_SYNCING;
+		spin_unlock_bh (&this->chip_lock);
+		break;
+
+	default:
+		/* Not an idle state */
+		add_wait_queue (&this->wq, &wait);
+		spin_unlock_bh (&this->chip_lock);
+		schedule ();
+
+		remove_wait_queue (&this->wq, &wait);
+		goto retry;
+	}
+
+        /* Lock the device */
+	spin_lock_bh (&this->chip_lock);
+
+	/* Set the device to be ready again */
+	if (this->state == FL_SYNCING) {
+		this->state = FL_READY;
+		wake_up (&this->wq);
+	}
+
+        /* Unlock the device */
+	spin_unlock_bh (&this->chip_lock);
+}
+
+/*
+ * Scan for the NAND device
+ */
+int nand_scan (struct mtd_info *mtd)
+{
+	int i, nand_maf_id, nand_dev_id;
+	struct nand_chip *this = mtd->priv;
+
+	/* Select the device */
+	nand_select ();
+
+	/* Send the command for reading device ID */
+	nand_command (mtd, NAND_CMD_READID, 0x00, -1);
+
+	/* Read manufacturer and device IDs */
+	nand_maf_id = readb (this->IO_ADDR);
+	nand_dev_id = readb (this->IO_ADDR);
+
+	/* Print and store flash device information */
+	for (i = 0; nand_flash_ids[i].name != NULL; i++) {
+		if (nand_maf_id == nand_flash_ids[i].manufacture_id &&
+		    nand_dev_id == nand_flash_ids[i].model_id) {
+			if (!mtd->size) {
+				mtd->name = nand_flash_ids[i].name;
+				mtd->erasesize = nand_flash_ids[i].erasesize;
+				mtd->size = (1 << nand_flash_ids[i].chipshift);
+				mtd->eccsize = 256;
+				if (nand_flash_ids[i].page256) {
+					mtd->oobblock = 256;
+					mtd->oobsize = 8;
+					this->page_shift = 8;
+				}
+				else {
+					mtd->oobblock = 512;
+					mtd->oobsize = 16;
+					this->page_shift = 9;
+				}
+			}
+			printk (KERN_INFO "NAND device: Manufacture ID:" \
+				" 0x%02x, Chip ID: 0x%02x (%s)\n",
+			       nand_maf_id, nand_dev_id, mtd->name);
+			break;
+		}
+	}
+
+	/* Initialize state and spinlock */
+	this->state = FL_READY;
+	spin_lock_init(&this->chip_lock);
+
+	/* De-select the device */
+	nand_deselect ();
+
+	/* Print warning message for no device */
+	if (!mtd->size) {
+		printk (KERN_WARNING "No NAND device found!!!\n");
+		return 1;
+	}
+
+	/* Fill in remaining MTD driver data */
+	mtd->type = MTD_NANDFLASH;
+	mtd->flags = MTD_CAP_NANDFLASH | MTD_ECC;
+	mtd->module = THIS_MODULE;
+	mtd->ecctype = MTD_ECC_SW;
+	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;
+	mtd->readv = NULL;
+	mtd->writev = nand_writev;
+	mtd->sync = nand_sync;
+	mtd->lock = NULL;
+	mtd->unlock = NULL;
+	mtd->suspend = NULL;
+	mtd->resume = NULL;
+
+	/* Return happy */
+	return 0;
+}
+
+EXPORT_SYMBOL(nand_scan);

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)