diff options
author | Kyungmin Park <kyungmin.park@samsung.com> | 2006-11-15 22:03:56 -0500 |
---|---|---|
committer | Kyungmin Park <kyungmin.park@samsung.com> | 2006-11-15 22:03:56 -0500 |
commit | f4f91ac3c833abbd7181ff2122c6b48a653b4e55 (patch) | |
tree | 16202b5e260e08e0b5401d55352d62b94ef8ad62 | |
parent | 08f782b60a633cbd926ef5e49de303a752390719 (diff) |
[MTD] OneNAND: Single bit error detection
Idea from Jarkko Lavinen
Signed-off-by: Jarkko Lavinen <jarkko.lavinen@nokia.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
-rw-r--r-- | drivers/mtd/onenand/onenand_base.c | 18 | ||||
-rw-r--r-- | drivers/mtd/onenand/onenand_bbt.c | 1 | ||||
-rw-r--r-- | include/linux/mtd/onenand_regs.h | 1 |
3 files changed, 15 insertions, 5 deletions
diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index bef4f26ad2ed..fc84ddc4987f 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c | |||
@@ -331,9 +331,12 @@ static int onenand_wait(struct mtd_info *mtd, int state) | |||
331 | 331 | ||
332 | if (interrupt & ONENAND_INT_READ) { | 332 | if (interrupt & ONENAND_INT_READ) { |
333 | ecc = this->read_word(this->base + ONENAND_REG_ECC_STATUS); | 333 | ecc = this->read_word(this->base + ONENAND_REG_ECC_STATUS); |
334 | if (ecc & ONENAND_ECC_2BIT_ALL) { | 334 | if (ecc) { |
335 | DEBUG(MTD_DEBUG_LEVEL0, "onenand_wait: ECC error = 0x%04x\n", ecc); | 335 | DEBUG(MTD_DEBUG_LEVEL0, "onenand_wait: ECC error = 0x%04x\n", ecc); |
336 | return -EBADMSG; | 336 | if (ecc & ONENAND_ECC_2BIT_ALL) |
337 | mtd->ecc_stats.failed++; | ||
338 | else if (ecc & ONENAND_ECC_1BIT_ALL) | ||
339 | mtd->ecc_stats.corrected++; | ||
337 | } | 340 | } |
338 | } | 341 | } |
339 | 342 | ||
@@ -715,6 +718,7 @@ static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len, | |||
715 | size_t *retlen, u_char *buf) | 718 | size_t *retlen, u_char *buf) |
716 | { | 719 | { |
717 | struct onenand_chip *this = mtd->priv; | 720 | struct onenand_chip *this = mtd->priv; |
721 | struct mtd_ecc_stats stats; | ||
718 | int read = 0, column; | 722 | int read = 0, column; |
719 | int thislen; | 723 | int thislen; |
720 | int ret = 0; | 724 | int ret = 0; |
@@ -733,6 +737,7 @@ static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len, | |||
733 | 737 | ||
734 | /* TODO handling oob */ | 738 | /* TODO handling oob */ |
735 | 739 | ||
740 | stats = mtd->ecc_stats; | ||
736 | while (read < len) { | 741 | while (read < len) { |
737 | thislen = min_t(int, mtd->writesize, len - read); | 742 | thislen = min_t(int, mtd->writesize, len - read); |
738 | 743 | ||
@@ -774,7 +779,11 @@ out: | |||
774 | * retlen == desired len and result == -EBADMSG | 779 | * retlen == desired len and result == -EBADMSG |
775 | */ | 780 | */ |
776 | *retlen = read; | 781 | *retlen = read; |
777 | return ret; | 782 | |
783 | if (mtd->ecc_stats.failed - stats.failed) | ||
784 | return -EBADMSG; | ||
785 | |||
786 | return mtd->ecc_stats.corrected - stats.corrected ? -EUCLEAN : 0; | ||
778 | } | 787 | } |
779 | 788 | ||
780 | /** | 789 | /** |
@@ -1390,7 +1399,6 @@ static int onenand_lock(struct mtd_info *mtd, loff_t ofs, size_t len) | |||
1390 | return onenand_do_lock_cmd(mtd, ofs, len, ONENAND_CMD_LOCK); | 1399 | return onenand_do_lock_cmd(mtd, ofs, len, ONENAND_CMD_LOCK); |
1391 | } | 1400 | } |
1392 | 1401 | ||
1393 | |||
1394 | /** | 1402 | /** |
1395 | * onenand_unlock - [MTD Interface] Unlock block(s) | 1403 | * onenand_unlock - [MTD Interface] Unlock block(s) |
1396 | * @param mtd MTD device structure | 1404 | * @param mtd MTD device structure |
@@ -1900,7 +1908,7 @@ static int onenand_probe(struct mtd_info *mtd) | |||
1900 | /* Read manufacturer and device IDs from Register */ | 1908 | /* Read manufacturer and device IDs from Register */ |
1901 | maf_id = this->read_word(this->base + ONENAND_REG_MANUFACTURER_ID); | 1909 | maf_id = this->read_word(this->base + ONENAND_REG_MANUFACTURER_ID); |
1902 | dev_id = this->read_word(this->base + ONENAND_REG_DEVICE_ID); | 1910 | dev_id = this->read_word(this->base + ONENAND_REG_DEVICE_ID); |
1903 | ver_id= this->read_word(this->base + ONENAND_REG_VERSION_ID); | 1911 | ver_id = this->read_word(this->base + ONENAND_REG_VERSION_ID); |
1904 | 1912 | ||
1905 | /* Check OneNAND device */ | 1913 | /* Check OneNAND device */ |
1906 | if (maf_id != bram_maf_id || dev_id != bram_dev_id) | 1914 | if (maf_id != bram_maf_id || dev_id != bram_dev_id) |
diff --git a/drivers/mtd/onenand/onenand_bbt.c b/drivers/mtd/onenand/onenand_bbt.c index 1b00dac3d7d6..c8067b8f20a6 100644 --- a/drivers/mtd/onenand/onenand_bbt.c +++ b/drivers/mtd/onenand/onenand_bbt.c | |||
@@ -100,6 +100,7 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr | |||
100 | bbm->bbt[i >> 3] |= 0x03 << (i & 0x6); | 100 | bbm->bbt[i >> 3] |= 0x03 << (i & 0x6); |
101 | printk(KERN_WARNING "Bad eraseblock %d at 0x%08x\n", | 101 | printk(KERN_WARNING "Bad eraseblock %d at 0x%08x\n", |
102 | i >> 1, (unsigned int) from); | 102 | i >> 1, (unsigned int) from); |
103 | mtd->ecc_stats.badblocks++; | ||
103 | break; | 104 | break; |
104 | } | 105 | } |
105 | } | 106 | } |
diff --git a/include/linux/mtd/onenand_regs.h b/include/linux/mtd/onenand_regs.h index 9e409fe6ded6..e31c8f5d4271 100644 --- a/include/linux/mtd/onenand_regs.h +++ b/include/linux/mtd/onenand_regs.h | |||
@@ -179,6 +179,7 @@ | |||
179 | * ECC Status Reigser FF00h (R) | 179 | * ECC Status Reigser FF00h (R) |
180 | */ | 180 | */ |
181 | #define ONENAND_ECC_1BIT (1 << 0) | 181 | #define ONENAND_ECC_1BIT (1 << 0) |
182 | #define ONENAND_ECC_1BIT_ALL (0x5555) | ||
182 | #define ONENAND_ECC_2BIT (1 << 1) | 183 | #define ONENAND_ECC_2BIT (1 << 1) |
183 | #define ONENAND_ECC_2BIT_ALL (0xAAAA) | 184 | #define ONENAND_ECC_2BIT_ALL (0xAAAA) |
184 | 185 | ||