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
- Lines: 1370
- Date:
Tue Jun 12 10:30:27 2001
- Orig file:
v2.4.5/linux/drivers/mtd/nand/nand.c
- Orig date:
Wed Dec 31 16:00:00 1969
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)