diff options
author | Ben Dooks <ben-linux@fluff.org> | 2007-02-02 11:59:33 -0500 |
---|---|---|
committer | David Woodhouse <dwmw2@infradead.org> | 2007-02-09 12:12:59 -0500 |
commit | a2593247d747954cd12c32da8c5a3aecb9cd19a3 (patch) | |
tree | 76b80f5626784a3c8fc88346eb8189a7b38bc22a | |
parent | a7a6ace1406f95c3edb8365788f85984377f3832 (diff) |
[MTD] [NAND] S3C2410: Hardware ECC correction code
Add support for correcting errors detected by the
hardware ECC.
Signed-off-by: Ben Dooks <ben-linux@fluff.org>
Signed-off-by: David Woodhouse <dwmw2@infradead.org>
-rw-r--r-- | drivers/mtd/nand/Kconfig | 4 | ||||
-rw-r--r-- | drivers/mtd/nand/s3c2410.c | 71 |
2 files changed, 62 insertions, 13 deletions
diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 9326a56f0fbc..143a7f048257 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig | |||
@@ -126,10 +126,6 @@ config MTD_NAND_S3C2410_HWECC | |||
126 | incorrect ECC generation, and if using these, the default of | 126 | incorrect ECC generation, and if using these, the default of |
127 | software ECC is preferable. | 127 | software ECC is preferable. |
128 | 128 | ||
129 | If you lay down a device with the hardware ECC, then you will | ||
130 | currently not be able to switch to software, as there is no | ||
131 | implementation for ECC method used by the S3C2410 | ||
132 | |||
133 | config MTD_NAND_NDFC | 129 | config MTD_NAND_NDFC |
134 | tristate "NDFC NanD Flash Controller" | 130 | tristate "NDFC NanD Flash Controller" |
135 | depends on MTD_NAND && 44x | 131 | depends on MTD_NAND && 44x |
diff --git a/drivers/mtd/nand/s3c2410.c b/drivers/mtd/nand/s3c2410.c index 8b3203571eeb..e8e030171ec4 100644 --- a/drivers/mtd/nand/s3c2410.c +++ b/drivers/mtd/nand/s3c2410.c | |||
@@ -337,17 +337,69 @@ static int s3c2412_nand_devready(struct mtd_info *mtd) | |||
337 | static int s3c2410_nand_correct_data(struct mtd_info *mtd, u_char *dat, | 337 | static int s3c2410_nand_correct_data(struct mtd_info *mtd, u_char *dat, |
338 | u_char *read_ecc, u_char *calc_ecc) | 338 | u_char *read_ecc, u_char *calc_ecc) |
339 | { | 339 | { |
340 | pr_debug("s3c2410_nand_correct_data(%p,%p,%p,%p)\n", mtd, dat, read_ecc, calc_ecc); | 340 | struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); |
341 | unsigned int diff0, diff1, diff2; | ||
342 | unsigned int bit, byte; | ||
341 | 343 | ||
342 | pr_debug("eccs: read %02x,%02x,%02x vs calc %02x,%02x,%02x\n", | 344 | pr_debug("%s(%p,%p,%p,%p)\n", __func__, mtd, dat, read_ecc, calc_ecc); |
343 | read_ecc[0], read_ecc[1], read_ecc[2], calc_ecc[0], calc_ecc[1], calc_ecc[2]); | ||
344 | 345 | ||
345 | if (read_ecc[0] == calc_ecc[0] && read_ecc[1] == calc_ecc[1] && read_ecc[2] == calc_ecc[2]) | 346 | diff0 = read_ecc[0] ^ calc_ecc[0]; |
346 | return 0; | 347 | diff1 = read_ecc[1] ^ calc_ecc[1]; |
348 | diff2 = read_ecc[2] ^ calc_ecc[2]; | ||
349 | |||
350 | pr_debug("%s: rd %02x%02x%02x calc %02x%02x%02x diff %02x%02x%02x\n", | ||
351 | __func__, | ||
352 | read_ecc[0], read_ecc[1], read_ecc[2], | ||
353 | calc_ecc[0], calc_ecc[1], calc_ecc[2], | ||
354 | diff0, diff1, diff2); | ||
355 | |||
356 | if (diff0 == 0 && diff1 == 0 && diff2 == 0) | ||
357 | return 0; /* ECC is ok */ | ||
358 | |||
359 | /* Can we correct this ECC (ie, one row and column change). | ||
360 | * Note, this is similar to the 256 error code on smartmedia */ | ||
361 | |||
362 | if (((diff0 ^ (diff0 >> 1)) & 0x55) == 0x55 && | ||
363 | ((diff1 ^ (diff1 >> 1)) & 0x55) == 0x55 && | ||
364 | ((diff2 ^ (diff2 >> 1)) & 0x55) == 0x55) { | ||
365 | /* calculate the bit position of the error */ | ||
366 | |||
367 | bit = (diff2 >> 2) & 1; | ||
368 | bit |= (diff2 >> 3) & 2; | ||
369 | bit |= (diff2 >> 4) & 4; | ||
370 | |||
371 | /* calculate the byte position of the error */ | ||
372 | |||
373 | byte = (diff1 << 1) & 0x80; | ||
374 | byte |= (diff1 << 2) & 0x40; | ||
375 | byte |= (diff1 << 3) & 0x20; | ||
376 | byte |= (diff1 << 4) & 0x10; | ||
347 | 377 | ||
348 | /* we curently have no method for correcting the error */ | 378 | byte |= (diff0 >> 3) & 0x08; |
379 | byte |= (diff0 >> 2) & 0x04; | ||
380 | byte |= (diff0 >> 1) & 0x02; | ||
381 | byte |= (diff0 >> 0) & 0x01; | ||
349 | 382 | ||
350 | return -1; | 383 | byte |= (diff2 << 8) & 0x100; |
384 | |||
385 | dev_dbg(info->device, "correcting error bit %d, byte %d\n", | ||
386 | bit, byte); | ||
387 | |||
388 | dat[byte] ^= (1 << bit); | ||
389 | return 1; | ||
390 | } | ||
391 | |||
392 | /* if there is only one bit difference in the ECC, then | ||
393 | * one of only a row or column parity has changed, which | ||
394 | * means the error is most probably in the ECC itself */ | ||
395 | |||
396 | diff0 |= (diff1 << 8); | ||
397 | diff0 |= (diff2 << 16); | ||
398 | |||
399 | if ((diff0 & ~(1<<fls(diff0))) == 0) | ||
400 | return 1; | ||
401 | |||
402 | return 0; | ||
351 | } | 403 | } |
352 | 404 | ||
353 | /* ECC functions | 405 | /* ECC functions |
@@ -383,7 +435,8 @@ static int s3c2410_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u | |||
383 | ecc_code[1] = readb(info->regs + S3C2410_NFECC + 1); | 435 | ecc_code[1] = readb(info->regs + S3C2410_NFECC + 1); |
384 | ecc_code[2] = readb(info->regs + S3C2410_NFECC + 2); | 436 | ecc_code[2] = readb(info->regs + S3C2410_NFECC + 2); |
385 | 437 | ||
386 | pr_debug("calculate_ecc: returning ecc %02x,%02x,%02x\n", ecc_code[0], ecc_code[1], ecc_code[2]); | 438 | pr_debug("%s: returning ecc %02x%02x%02x\n", __func__, |
439 | ecc_code[0], ecc_code[1], ecc_code[2]); | ||
387 | 440 | ||
388 | return 0; | 441 | return 0; |
389 | } | 442 | } |
@@ -397,7 +450,7 @@ static int s3c2440_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u | |||
397 | ecc_code[1] = ecc >> 8; | 450 | ecc_code[1] = ecc >> 8; |
398 | ecc_code[2] = ecc >> 16; | 451 | ecc_code[2] = ecc >> 16; |
399 | 452 | ||
400 | pr_debug("calculate_ecc: returning ecc %02x,%02x,%02x\n", ecc_code[0], ecc_code[1], ecc_code[2]); | 453 | pr_debug("%s: returning ecc %06x\n", __func__, ecc); |
401 | 454 | ||
402 | return 0; | 455 | return 0; |
403 | } | 456 | } |