Commit 068e3c0a authored by David A. Marlin's avatar David A. Marlin Committed by Thomas Gleixner
Browse files

[MTD] NAND Add optional ECC status check callback



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: default avatarDavid A. Marlin <dmarlin@redhat.com>
Signed-off-by: default avatarThomas Gleixner <tglx@linutronix.de>
parent 99f2a8ae
Loading
Loading
Loading
Loading
+54 −11
Original line number Diff line number Diff line
@@ -42,6 +42,10 @@
 * 		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 @@ static int nand_verify_pages (struct mtd_info *mtd, struct nand_chip *this, int
#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,10 +1058,34 @@ 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;
@@ -1076,6 +1111,7 @@ 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 */
	if (flags & NAND_GET_DEVICE)
		nand_get_device (this, mtd, FL_READING);

	/* use userspace supplied oobinfo, if zero */
@@ -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,6 +1326,7 @@ static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len,
	}

	/* Deselect and wake up anyone waiting on the device */
	if (flags & NAND_GET_DEVICE)
		nand_release_device(mtd);

	/*
@@ -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);
+14 −2
Original line number Diff line number Diff line
@@ -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
@@ -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