[MTD] NAND Add optional ECC status check callback
authorDavid A. Marlin <dmarlin@redhat.com>
Mon, 24 Jan 2005 03:07:46 +0000 (03:07 +0000)
committerThomas Gleixner <tglx@mtd.linutronix.de>
Mon, 23 May 2005 10:08:59 +0000 (12:08 +0200)
Add optional hardware specific callback routine to perform extra error
status checks on erase and write failures for devices with hardware ECC.

Signed-off-by: David A. Marlin <dmarlin@redhat.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
drivers/mtd/nand/nand_base.c
include/linux/mtd/nand.h

index 9f7c42c..7094dd5 100644 (file)
  *             a "device recovery" operation must be performed when power is restored
  *             to ensure correct operation.
  *
+ *  01-20-2005 dmarlin: added support for optional hardware specific callback routine to 
+ *             perform extra error status checks on erase and write failures.  This required
+ *             adding a wrapper function for nand_read_ecc.
+ *
  * Credits:
  *     David Woodhouse for adding multichip support  
  *     
@@ -55,7 +59,7 @@
  *     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.129 2005/01/23 18:30:50 dmarlin Exp $
+ * $Id: nand_base.c,v 1.130 2005/01/24 03:07:43 dmarlin 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
@@ -896,6 +900,12 @@ static int nand_write_page (struct mtd_info *mtd, struct nand_chip *this, int pa
        if (!cached) {
                /* call wait ready function */
                status = this->waitfunc (mtd, this, FL_WRITING);
+
+               /* See if operation failed and additional status checks are available */
+               if ((status & NAND_STATUS_FAIL) && (this->errstat)) {
+                       status = this->errstat(mtd, this, FL_WRITING, status, page);
+               }
+
                /* See if device thinks it succeeded */
                if (status & NAND_STATUS_FAIL) {
                        DEBUG (MTD_DEBUG_LEVEL0, "%s: " "Failed write, page 0x%08x, ", __FUNCTION__, page);
@@ -1022,23 +1032,24 @@ out:
 #endif
 
 /**
- * nand_read - [MTD Interface] MTD compability function for nand_read_ecc
+ * nand_read - [MTD Interface] MTD compability function for nand_do_read_ecc
  * @mtd:       MTD device structure
  * @from:      offset to read from
  * @len:       number of bytes to read
  * @retlen:    pointer to variable to store the number of read bytes
  * @buf:       the databuffer to put data
  *
- * This function simply calls nand_read_ecc with oob buffer and oobsel = NULL
-*/
+ * This function simply calls nand_do_read_ecc with oob buffer and oobsel = NULL
+ * and flags = 0xff
+ */
 static int nand_read (struct mtd_info *mtd, loff_t from, size_t len, size_t * retlen, u_char * buf)
 {
-       return nand_read_ecc (mtd, from, len, retlen, buf, NULL, NULL);
+       return nand_do_read_ecc (mtd, from, len, retlen, buf, NULL, NULL, 0xff);
 }                         
 
 
 /**
- * nand_read_ecc - [MTD Interface] Read data with ECC
+ * nand_read_ecc - [MTD Interface] MTD compability function for nand_do_read_ecc
  * @mtd:       MTD device structure
  * @from:      offset to read from
  * @len:       number of bytes to read
@@ -1047,11 +1058,35 @@ static int nand_read (struct mtd_info *mtd, loff_t from, size_t len, size_t * re
  * @oob_buf:   filesystem supplied oob data buffer
  * @oobsel:    oob selection structure
  *
- * NAND read with ECC
+ * This function simply calls nand_do_read_ecc with flags = 0xff
  */
 static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len,
                          size_t * retlen, u_char * buf, u_char * oob_buf, struct nand_oobinfo *oobsel)
 {
+       return nand_do_read_ecc(mtd, from, len, retlen, buf, oob_buf, oobsel, 0xff);
+}
+
+
+/**
+ * nand_do_read_ecc - [MTD Interface] Read data with ECC
+ * @mtd:       MTD device structure
+ * @from:      offset to read from
+ * @len:       number of bytes to read
+ * @retlen:    pointer to variable to store the number of read bytes
+ * @buf:       the databuffer to put data
+ * @oob_buf:   filesystem supplied oob data buffer
+ * @oobsel:    oob selection structure
+ * @flags:     flag to indicate if nand_get_device/nand_release_device should be preformed
+ *             and how many corrected error bits are acceptable:
+ *               bits 0..7 - number of tolerable errors
+ *               bit  8    - 0 == do not get/release chip, 1 == get/release chip
+ *
+ * NAND read with ECC
+ */
+int nand_do_read_ecc (struct mtd_info *mtd, loff_t from, size_t len,
+                            size_t * retlen, u_char * buf, u_char * oob_buf, 
+                            struct nand_oobinfo *oobsel, int flags)
+{
        int i, j, col, realpage, page, end, ecc, chipnr, sndcmd = 1;
        int read = 0, oob = 0, ecc_status = 0, ecc_failed = 0;
        struct nand_chip *this = mtd->priv;
@@ -1076,7 +1111,8 @@ static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len,
        }
 
        /* Grab the lock and see if the device is available */
-       nand_get_device (this, mtd, FL_READING);
+       if (flags & NAND_GET_DEVICE)
+               nand_get_device (this, mtd, FL_READING);
 
        /* use userspace supplied oobinfo, if zero */
        if (oobsel == NULL)
@@ -1180,7 +1216,8 @@ static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len,
                                        /* We calc error correction directly, it checks the hw
                                         * generator for an error, reads back the syndrome and
                                         * does the error correction on the fly */
-                                       if (this->correct_data(mtd, &data_poi[datidx], &oob_data[i], &ecc_code[i]) == -1) {
+                                       ecc_status = this->correct_data(mtd, &data_poi[datidx], &oob_data[i], &ecc_code[i]);
+                                       if ((ecc_status == -1) || (ecc_status > (flags && 0xff))) {
                                                DEBUG (MTD_DEBUG_LEVEL0, "nand_read_ecc: " 
                                                        "Failed ECC read, page 0x%08x on chip %d\n", page, chipnr);
                                                ecc_failed++;
@@ -1219,7 +1256,7 @@ static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len,
                                p[i] = ecc_status;
                        }
                        
-                       if (ecc_status == -1) { 
+                       if ((ecc_status == -1) || (ecc_status > (flags && 0xff))) {     
                                DEBUG (MTD_DEBUG_LEVEL0, "nand_read_ecc: " "Failed ECC read, page 0x%08x\n", page);
                                ecc_failed++;
                        }
@@ -1289,7 +1326,8 @@ static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len,
        }
 
        /* Deselect and wake up anyone waiting on the device */
-       nand_release_device(mtd);
+       if (flags & NAND_GET_DEVICE)
+               nand_release_device(mtd);
 
        /*
         * Return success, if no ECC failures, else -EBADMSG
@@ -2103,6 +2141,11 @@ int nand_erase_nand (struct mtd_info *mtd, struct erase_info *instr, int allowbb
                
                status = this->waitfunc (mtd, this, FL_ERASING);
 
+               /* See if operation failed and additional status checks are available */
+               if ((status & NAND_STATUS_FAIL) && (this->errstat)) {
+                       status = this->errstat(mtd, this, FL_ERASING, status, page);
+               }
+
                /* See if block erase succeeded */
                if (status & NAND_STATUS_FAIL) {
                        DEBUG (MTD_DEBUG_LEVEL0, "nand_erase: " "Failed erase, page 0x%08x\n", page);
index 0118128..cf52f20 100644 (file)
@@ -5,7 +5,7 @@
  *                     Steven J. Hill <sjhill@realitydiluted.com>
  *                    Thomas Gleixner <tglx@linutronix.de>
  *
- * $Id: nand.h,v 1.69 2005/01/17 18:29:18 dmarlin Exp $
+ * $Id: nand.h,v 1.70 2005/01/24 03:07:42 dmarlin 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
@@ -50,6 +50,8 @@
  *                     update of nand_chip structure description
  *  01-17-2005 dmarlin added extended commands for AG-AND device and added option 
  *                     for BBT_AUTO_REFRESH.
+ *  01-20-2005 dmarlin added optional pointer to hardware specific callback for 
+ *                     extra error status checks.
  */
 #ifndef __LINUX_MTD_NAND_H
 #define __LINUX_MTD_NAND_H
@@ -164,7 +166,7 @@ extern int nand_read_raw (struct mtd_info *mtd, uint8_t *buf, loff_t from, size_
 
 /*
  * Constants for Hardware ECC
-*/
+ */
 /* Reset Hardware ECC for read */
 #define NAND_ECC_READ          0
 /* Reset Hardware ECC for write */
@@ -172,6 +174,10 @@ extern int nand_read_raw (struct mtd_info *mtd, uint8_t *buf, loff_t from, size_
 /* Enable Hardware ECC before syndrom is read back from flash */
 #define NAND_ECC_READSYN       2
 
+/* Bit mask for flags passed to do_nand_read_ecc */
+#define NAND_GET_DEVICE                0x80
+
+
 /* Option constants for bizarre disfunctionality and real
 *  features
 */
@@ -308,6 +314,8 @@ struct nand_hw_control {
  * @badblock_pattern:  [REPLACEABLE] bad block scan pattern used for initial bad block scan 
  * @controller:                [OPTIONAL] a pointer to a hardware controller structure which is shared among multiple independend devices
  * @priv:              [OPTIONAL] pointer to private chip date
+ * @errstat:           [OPTIONAL] hardware specific function to perform additional error status checks 
+ *                     (determine if errors are correctable)
  */
  
 struct nand_chip {
@@ -363,6 +371,7 @@ struct nand_chip {
        struct nand_bbt_descr   *badblock_pattern;
        struct nand_hw_control  *controller;
        void            *priv;
+       int             (*errstat)(struct mtd_info *mtd, struct nand_chip *this, int state, int status, int page);
 };
 
 /*
@@ -484,6 +493,9 @@ extern int nand_update_bbt (struct mtd_info *mtd, loff_t offs);
 extern int nand_default_bbt (struct mtd_info *mtd);
 extern int nand_isbad_bbt (struct mtd_info *mtd, loff_t offs, int allowbbt);
 extern int nand_erase_nand (struct mtd_info *mtd, struct erase_info *instr, int allowbbt);
+extern int nand_do_read_ecc (struct mtd_info *mtd, loff_t from, size_t len,
+                             size_t * retlen, u_char * buf, u_char * oob_buf,
+                             struct nand_oobinfo *oobsel, int flags);
 
 /*
 * Constants for oob configuration