diff options
author | David A. Marlin <dmarlin@redhat.com> | 2005-01-23 22:07:46 -0500 |
---|---|---|
committer | Thomas Gleixner <tglx@mtd.linutronix.de> | 2005-05-23 06:08:59 -0400 |
commit | 068e3c0a002c79a5e3cc7c42cb749c4bb126288c (patch) | |
tree | fdf2db2edf03c4520a204476b0edc652ae2c9c47 | |
parent | 99f2a8aea18c9779c141050c6f95a8f1da63bbe4 (diff) |
[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: David A. Marlin <dmarlin@redhat.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
-rw-r--r-- | drivers/mtd/nand/nand_base.c | 65 | ||||
-rw-r--r-- | include/linux/mtd/nand.h | 16 |
2 files changed, 68 insertions, 13 deletions
diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 9f7c42ceecfa..7094dd5716dc 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c | |||
@@ -42,6 +42,10 @@ | |||
42 | * a "device recovery" operation must be performed when power is restored | 42 | * a "device recovery" operation must be performed when power is restored |
43 | * to ensure correct operation. | 43 | * to ensure correct operation. |
44 | * | 44 | * |
45 | * 01-20-2005 dmarlin: added support for optional hardware specific callback routine to | ||
46 | * perform extra error status checks on erase and write failures. This required | ||
47 | * adding a wrapper function for nand_read_ecc. | ||
48 | * | ||
45 | * Credits: | 49 | * Credits: |
46 | * David Woodhouse for adding multichip support | 50 | * David Woodhouse for adding multichip support |
47 | * | 51 | * |
@@ -55,7 +59,7 @@ | |||
55 | * The AG-AND chips have nice features for speed improvement, | 59 | * The AG-AND chips have nice features for speed improvement, |
56 | * which are not supported yet. Read / program 4 pages in one go. | 60 | * which are not supported yet. Read / program 4 pages in one go. |
57 | * | 61 | * |
58 | * $Id: nand_base.c,v 1.129 2005/01/23 18:30:50 dmarlin Exp $ | 62 | * $Id: nand_base.c,v 1.130 2005/01/24 03:07:43 dmarlin Exp $ |
59 | * | 63 | * |
60 | * This program is free software; you can redistribute it and/or modify | 64 | * This program is free software; you can redistribute it and/or modify |
61 | * it under the terms of the GNU General Public License version 2 as | 65 | * 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 | |||
896 | if (!cached) { | 900 | if (!cached) { |
897 | /* call wait ready function */ | 901 | /* call wait ready function */ |
898 | status = this->waitfunc (mtd, this, FL_WRITING); | 902 | status = this->waitfunc (mtd, this, FL_WRITING); |
903 | |||
904 | /* See if operation failed and additional status checks are available */ | ||
905 | if ((status & NAND_STATUS_FAIL) && (this->errstat)) { | ||
906 | status = this->errstat(mtd, this, FL_WRITING, status, page); | ||
907 | } | ||
908 | |||
899 | /* See if device thinks it succeeded */ | 909 | /* See if device thinks it succeeded */ |
900 | if (status & NAND_STATUS_FAIL) { | 910 | if (status & NAND_STATUS_FAIL) { |
901 | DEBUG (MTD_DEBUG_LEVEL0, "%s: " "Failed write, page 0x%08x, ", __FUNCTION__, page); | 911 | DEBUG (MTD_DEBUG_LEVEL0, "%s: " "Failed write, page 0x%08x, ", __FUNCTION__, page); |
@@ -1022,23 +1032,24 @@ out: | |||
1022 | #endif | 1032 | #endif |
1023 | 1033 | ||
1024 | /** | 1034 | /** |
1025 | * nand_read - [MTD Interface] MTD compability function for nand_read_ecc | 1035 | * nand_read - [MTD Interface] MTD compability function for nand_do_read_ecc |
1026 | * @mtd: MTD device structure | 1036 | * @mtd: MTD device structure |
1027 | * @from: offset to read from | 1037 | * @from: offset to read from |
1028 | * @len: number of bytes to read | 1038 | * @len: number of bytes to read |
1029 | * @retlen: pointer to variable to store the number of read bytes | 1039 | * @retlen: pointer to variable to store the number of read bytes |
1030 | * @buf: the databuffer to put data | 1040 | * @buf: the databuffer to put data |
1031 | * | 1041 | * |
1032 | * This function simply calls nand_read_ecc with oob buffer and oobsel = NULL | 1042 | * This function simply calls nand_do_read_ecc with oob buffer and oobsel = NULL |
1033 | */ | 1043 | * and flags = 0xff |
1044 | */ | ||
1034 | static int nand_read (struct mtd_info *mtd, loff_t from, size_t len, size_t * retlen, u_char * buf) | 1045 | static int nand_read (struct mtd_info *mtd, loff_t from, size_t len, size_t * retlen, u_char * buf) |
1035 | { | 1046 | { |
1036 | return nand_read_ecc (mtd, from, len, retlen, buf, NULL, NULL); | 1047 | return nand_do_read_ecc (mtd, from, len, retlen, buf, NULL, NULL, 0xff); |
1037 | } | 1048 | } |
1038 | 1049 | ||
1039 | 1050 | ||
1040 | /** | 1051 | /** |
1041 | * nand_read_ecc - [MTD Interface] Read data with ECC | 1052 | * nand_read_ecc - [MTD Interface] MTD compability function for nand_do_read_ecc |
1042 | * @mtd: MTD device structure | 1053 | * @mtd: MTD device structure |
1043 | * @from: offset to read from | 1054 | * @from: offset to read from |
1044 | * @len: number of bytes to read | 1055 | * @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 | |||
1047 | * @oob_buf: filesystem supplied oob data buffer | 1058 | * @oob_buf: filesystem supplied oob data buffer |
1048 | * @oobsel: oob selection structure | 1059 | * @oobsel: oob selection structure |
1049 | * | 1060 | * |
1050 | * NAND read with ECC | 1061 | * This function simply calls nand_do_read_ecc with flags = 0xff |
1051 | */ | 1062 | */ |
1052 | static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len, | 1063 | static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len, |
1053 | size_t * retlen, u_char * buf, u_char * oob_buf, struct nand_oobinfo *oobsel) | 1064 | size_t * retlen, u_char * buf, u_char * oob_buf, struct nand_oobinfo *oobsel) |
1054 | { | 1065 | { |
1066 | return nand_do_read_ecc(mtd, from, len, retlen, buf, oob_buf, oobsel, 0xff); | ||
1067 | } | ||
1068 | |||
1069 | |||
1070 | /** | ||
1071 | * nand_do_read_ecc - [MTD Interface] Read data with ECC | ||
1072 | * @mtd: MTD device structure | ||
1073 | * @from: offset to read from | ||
1074 | * @len: number of bytes to read | ||
1075 | * @retlen: pointer to variable to store the number of read bytes | ||
1076 | * @buf: the databuffer to put data | ||
1077 | * @oob_buf: filesystem supplied oob data buffer | ||
1078 | * @oobsel: oob selection structure | ||
1079 | * @flags: flag to indicate if nand_get_device/nand_release_device should be preformed | ||
1080 | * and how many corrected error bits are acceptable: | ||
1081 | * bits 0..7 - number of tolerable errors | ||
1082 | * bit 8 - 0 == do not get/release chip, 1 == get/release chip | ||
1083 | * | ||
1084 | * NAND read with ECC | ||
1085 | */ | ||
1086 | int nand_do_read_ecc (struct mtd_info *mtd, loff_t from, size_t len, | ||
1087 | size_t * retlen, u_char * buf, u_char * oob_buf, | ||
1088 | struct nand_oobinfo *oobsel, int flags) | ||
1089 | { | ||
1055 | int i, j, col, realpage, page, end, ecc, chipnr, sndcmd = 1; | 1090 | int i, j, col, realpage, page, end, ecc, chipnr, sndcmd = 1; |
1056 | int read = 0, oob = 0, ecc_status = 0, ecc_failed = 0; | 1091 | int read = 0, oob = 0, ecc_status = 0, ecc_failed = 0; |
1057 | struct nand_chip *this = mtd->priv; | 1092 | 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, | |||
1076 | } | 1111 | } |
1077 | 1112 | ||
1078 | /* Grab the lock and see if the device is available */ | 1113 | /* Grab the lock and see if the device is available */ |
1079 | nand_get_device (this, mtd, FL_READING); | 1114 | if (flags & NAND_GET_DEVICE) |
1115 | nand_get_device (this, mtd, FL_READING); | ||
1080 | 1116 | ||
1081 | /* use userspace supplied oobinfo, if zero */ | 1117 | /* use userspace supplied oobinfo, if zero */ |
1082 | if (oobsel == NULL) | 1118 | if (oobsel == NULL) |
@@ -1180,7 +1216,8 @@ static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len, | |||
1180 | /* We calc error correction directly, it checks the hw | 1216 | /* We calc error correction directly, it checks the hw |
1181 | * generator for an error, reads back the syndrome and | 1217 | * generator for an error, reads back the syndrome and |
1182 | * does the error correction on the fly */ | 1218 | * does the error correction on the fly */ |
1183 | if (this->correct_data(mtd, &data_poi[datidx], &oob_data[i], &ecc_code[i]) == -1) { | 1219 | ecc_status = this->correct_data(mtd, &data_poi[datidx], &oob_data[i], &ecc_code[i]); |
1220 | if ((ecc_status == -1) || (ecc_status > (flags && 0xff))) { | ||
1184 | DEBUG (MTD_DEBUG_LEVEL0, "nand_read_ecc: " | 1221 | DEBUG (MTD_DEBUG_LEVEL0, "nand_read_ecc: " |
1185 | "Failed ECC read, page 0x%08x on chip %d\n", page, chipnr); | 1222 | "Failed ECC read, page 0x%08x on chip %d\n", page, chipnr); |
1186 | ecc_failed++; | 1223 | ecc_failed++; |
@@ -1219,7 +1256,7 @@ static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len, | |||
1219 | p[i] = ecc_status; | 1256 | p[i] = ecc_status; |
1220 | } | 1257 | } |
1221 | 1258 | ||
1222 | if (ecc_status == -1) { | 1259 | if ((ecc_status == -1) || (ecc_status > (flags && 0xff))) { |
1223 | DEBUG (MTD_DEBUG_LEVEL0, "nand_read_ecc: " "Failed ECC read, page 0x%08x\n", page); | 1260 | DEBUG (MTD_DEBUG_LEVEL0, "nand_read_ecc: " "Failed ECC read, page 0x%08x\n", page); |
1224 | ecc_failed++; | 1261 | ecc_failed++; |
1225 | } | 1262 | } |
@@ -1289,7 +1326,8 @@ static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len, | |||
1289 | } | 1326 | } |
1290 | 1327 | ||
1291 | /* Deselect and wake up anyone waiting on the device */ | 1328 | /* Deselect and wake up anyone waiting on the device */ |
1292 | nand_release_device(mtd); | 1329 | if (flags & NAND_GET_DEVICE) |
1330 | nand_release_device(mtd); | ||
1293 | 1331 | ||
1294 | /* | 1332 | /* |
1295 | * Return success, if no ECC failures, else -EBADMSG | 1333 | * 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 | |||
2103 | 2141 | ||
2104 | status = this->waitfunc (mtd, this, FL_ERASING); | 2142 | status = this->waitfunc (mtd, this, FL_ERASING); |
2105 | 2143 | ||
2144 | /* See if operation failed and additional status checks are available */ | ||
2145 | if ((status & NAND_STATUS_FAIL) && (this->errstat)) { | ||
2146 | status = this->errstat(mtd, this, FL_ERASING, status, page); | ||
2147 | } | ||
2148 | |||
2106 | /* See if block erase succeeded */ | 2149 | /* See if block erase succeeded */ |
2107 | if (status & NAND_STATUS_FAIL) { | 2150 | if (status & NAND_STATUS_FAIL) { |
2108 | DEBUG (MTD_DEBUG_LEVEL0, "nand_erase: " "Failed erase, page 0x%08x\n", page); | 2151 | DEBUG (MTD_DEBUG_LEVEL0, "nand_erase: " "Failed erase, page 0x%08x\n", page); |
diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index 0118128ae384..cf52f20c6de2 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h | |||
@@ -5,7 +5,7 @@ | |||
5 | * Steven J. Hill <sjhill@realitydiluted.com> | 5 | * Steven J. Hill <sjhill@realitydiluted.com> |
6 | * Thomas Gleixner <tglx@linutronix.de> | 6 | * Thomas Gleixner <tglx@linutronix.de> |
7 | * | 7 | * |
8 | * $Id: nand.h,v 1.69 2005/01/17 18:29:18 dmarlin Exp $ | 8 | * $Id: nand.h,v 1.70 2005/01/24 03:07:42 dmarlin Exp $ |
9 | * | 9 | * |
10 | * This program is free software; you can redistribute it and/or modify | 10 | * This program is free software; you can redistribute it and/or modify |
11 | * it under the terms of the GNU General Public License version 2 as | 11 | * it under the terms of the GNU General Public License version 2 as |
@@ -50,6 +50,8 @@ | |||
50 | * update of nand_chip structure description | 50 | * update of nand_chip structure description |
51 | * 01-17-2005 dmarlin added extended commands for AG-AND device and added option | 51 | * 01-17-2005 dmarlin added extended commands for AG-AND device and added option |
52 | * for BBT_AUTO_REFRESH. | 52 | * for BBT_AUTO_REFRESH. |
53 | * 01-20-2005 dmarlin added optional pointer to hardware specific callback for | ||
54 | * extra error status checks. | ||
53 | */ | 55 | */ |
54 | #ifndef __LINUX_MTD_NAND_H | 56 | #ifndef __LINUX_MTD_NAND_H |
55 | #define __LINUX_MTD_NAND_H | 57 | #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_ | |||
164 | 166 | ||
165 | /* | 167 | /* |
166 | * Constants for Hardware ECC | 168 | * Constants for Hardware ECC |
167 | */ | 169 | */ |
168 | /* Reset Hardware ECC for read */ | 170 | /* Reset Hardware ECC for read */ |
169 | #define NAND_ECC_READ 0 | 171 | #define NAND_ECC_READ 0 |
170 | /* Reset Hardware ECC for write */ | 172 | /* 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_ | |||
172 | /* Enable Hardware ECC before syndrom is read back from flash */ | 174 | /* Enable Hardware ECC before syndrom is read back from flash */ |
173 | #define NAND_ECC_READSYN 2 | 175 | #define NAND_ECC_READSYN 2 |
174 | 176 | ||
177 | /* Bit mask for flags passed to do_nand_read_ecc */ | ||
178 | #define NAND_GET_DEVICE 0x80 | ||
179 | |||
180 | |||
175 | /* Option constants for bizarre disfunctionality and real | 181 | /* Option constants for bizarre disfunctionality and real |
176 | * features | 182 | * features |
177 | */ | 183 | */ |
@@ -308,6 +314,8 @@ struct nand_hw_control { | |||
308 | * @badblock_pattern: [REPLACEABLE] bad block scan pattern used for initial bad block scan | 314 | * @badblock_pattern: [REPLACEABLE] bad block scan pattern used for initial bad block scan |
309 | * @controller: [OPTIONAL] a pointer to a hardware controller structure which is shared among multiple independend devices | 315 | * @controller: [OPTIONAL] a pointer to a hardware controller structure which is shared among multiple independend devices |
310 | * @priv: [OPTIONAL] pointer to private chip date | 316 | * @priv: [OPTIONAL] pointer to private chip date |
317 | * @errstat: [OPTIONAL] hardware specific function to perform additional error status checks | ||
318 | * (determine if errors are correctable) | ||
311 | */ | 319 | */ |
312 | 320 | ||
313 | struct nand_chip { | 321 | struct nand_chip { |
@@ -363,6 +371,7 @@ struct nand_chip { | |||
363 | struct nand_bbt_descr *badblock_pattern; | 371 | struct nand_bbt_descr *badblock_pattern; |
364 | struct nand_hw_control *controller; | 372 | struct nand_hw_control *controller; |
365 | void *priv; | 373 | void *priv; |
374 | int (*errstat)(struct mtd_info *mtd, struct nand_chip *this, int state, int status, int page); | ||
366 | }; | 375 | }; |
367 | 376 | ||
368 | /* | 377 | /* |
@@ -484,6 +493,9 @@ extern int nand_update_bbt (struct mtd_info *mtd, loff_t offs); | |||
484 | extern int nand_default_bbt (struct mtd_info *mtd); | 493 | extern int nand_default_bbt (struct mtd_info *mtd); |
485 | extern int nand_isbad_bbt (struct mtd_info *mtd, loff_t offs, int allowbbt); | 494 | extern int nand_isbad_bbt (struct mtd_info *mtd, loff_t offs, int allowbbt); |
486 | extern int nand_erase_nand (struct mtd_info *mtd, struct erase_info *instr, int allowbbt); | 495 | extern int nand_erase_nand (struct mtd_info *mtd, struct erase_info *instr, int allowbbt); |
496 | extern int nand_do_read_ecc (struct mtd_info *mtd, loff_t from, size_t len, | ||
497 | size_t * retlen, u_char * buf, u_char * oob_buf, | ||
498 | struct nand_oobinfo *oobsel, int flags); | ||
487 | 499 | ||
488 | /* | 500 | /* |
489 | * Constants for oob configuration | 501 | * Constants for oob configuration |