aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/mtd
diff options
context:
space:
mode:
authorBen Dooks <ben-linux@fluff.org>2007-02-02 11:59:33 -0500
committerDavid Woodhouse <dwmw2@infradead.org>2007-02-09 12:12:59 -0500
commita2593247d747954cd12c32da8c5a3aecb9cd19a3 (patch)
tree76b80f5626784a3c8fc88346eb8189a7b38bc22a /drivers/mtd
parenta7a6ace1406f95c3edb8365788f85984377f3832 (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>
Diffstat (limited to 'drivers/mtd')
-rw-r--r--drivers/mtd/nand/Kconfig4
-rw-r--r--drivers/mtd/nand/s3c2410.c71
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
133config MTD_NAND_NDFC 129config 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)
337static int s3c2410_nand_correct_data(struct mtd_info *mtd, u_char *dat, 337static 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}