[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>
This commit is contained in:
parent
a7a6ace140
commit
a2593247d7
2 changed files with 62 additions and 13 deletions
|
@ -126,10 +126,6 @@ config MTD_NAND_S3C2410_HWECC
|
|||
incorrect ECC generation, and if using these, the default of
|
||||
software ECC is preferable.
|
||||
|
||||
If you lay down a device with the hardware ECC, then you will
|
||||
currently not be able to switch to software, as there is no
|
||||
implementation for ECC method used by the S3C2410
|
||||
|
||||
config MTD_NAND_NDFC
|
||||
tristate "NDFC NanD Flash Controller"
|
||||
depends on MTD_NAND && 44x
|
||||
|
|
|
@ -337,17 +337,69 @@ static int s3c2412_nand_devready(struct mtd_info *mtd)
|
|||
static int s3c2410_nand_correct_data(struct mtd_info *mtd, u_char *dat,
|
||||
u_char *read_ecc, u_char *calc_ecc)
|
||||
{
|
||||
pr_debug("s3c2410_nand_correct_data(%p,%p,%p,%p)\n", mtd, dat, read_ecc, calc_ecc);
|
||||
struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
|
||||
unsigned int diff0, diff1, diff2;
|
||||
unsigned int bit, byte;
|
||||
|
||||
pr_debug("eccs: read %02x,%02x,%02x vs calc %02x,%02x,%02x\n",
|
||||
read_ecc[0], read_ecc[1], read_ecc[2], calc_ecc[0], calc_ecc[1], calc_ecc[2]);
|
||||
pr_debug("%s(%p,%p,%p,%p)\n", __func__, mtd, dat, read_ecc, calc_ecc);
|
||||
|
||||
diff0 = read_ecc[0] ^ calc_ecc[0];
|
||||
diff1 = read_ecc[1] ^ calc_ecc[1];
|
||||
diff2 = read_ecc[2] ^ calc_ecc[2];
|
||||
|
||||
pr_debug("%s: rd %02x%02x%02x calc %02x%02x%02x diff %02x%02x%02x\n",
|
||||
__func__,
|
||||
read_ecc[0], read_ecc[1], read_ecc[2],
|
||||
calc_ecc[0], calc_ecc[1], calc_ecc[2],
|
||||
diff0, diff1, diff2);
|
||||
|
||||
if (diff0 == 0 && diff1 == 0 && diff2 == 0)
|
||||
return 0; /* ECC is ok */
|
||||
|
||||
/* Can we correct this ECC (ie, one row and column change).
|
||||
* Note, this is similar to the 256 error code on smartmedia */
|
||||
|
||||
if (((diff0 ^ (diff0 >> 1)) & 0x55) == 0x55 &&
|
||||
((diff1 ^ (diff1 >> 1)) & 0x55) == 0x55 &&
|
||||
((diff2 ^ (diff2 >> 1)) & 0x55) == 0x55) {
|
||||
/* calculate the bit position of the error */
|
||||
|
||||
bit = (diff2 >> 2) & 1;
|
||||
bit |= (diff2 >> 3) & 2;
|
||||
bit |= (diff2 >> 4) & 4;
|
||||
|
||||
/* calculate the byte position of the error */
|
||||
|
||||
byte = (diff1 << 1) & 0x80;
|
||||
byte |= (diff1 << 2) & 0x40;
|
||||
byte |= (diff1 << 3) & 0x20;
|
||||
byte |= (diff1 << 4) & 0x10;
|
||||
|
||||
byte |= (diff0 >> 3) & 0x08;
|
||||
byte |= (diff0 >> 2) & 0x04;
|
||||
byte |= (diff0 >> 1) & 0x02;
|
||||
byte |= (diff0 >> 0) & 0x01;
|
||||
|
||||
byte |= (diff2 << 8) & 0x100;
|
||||
|
||||
dev_dbg(info->device, "correcting error bit %d, byte %d\n",
|
||||
bit, byte);
|
||||
|
||||
dat[byte] ^= (1 << bit);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* if there is only one bit difference in the ECC, then
|
||||
* one of only a row or column parity has changed, which
|
||||
* means the error is most probably in the ECC itself */
|
||||
|
||||
diff0 |= (diff1 << 8);
|
||||
diff0 |= (diff2 << 16);
|
||||
|
||||
if ((diff0 & ~(1<<fls(diff0))) == 0)
|
||||
return 1;
|
||||
|
||||
if (read_ecc[0] == calc_ecc[0] && read_ecc[1] == calc_ecc[1] && read_ecc[2] == calc_ecc[2])
|
||||
return 0;
|
||||
|
||||
/* we curently have no method for correcting the error */
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* ECC functions
|
||||
|
@ -383,7 +435,8 @@ static int s3c2410_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u
|
|||
ecc_code[1] = readb(info->regs + S3C2410_NFECC + 1);
|
||||
ecc_code[2] = readb(info->regs + S3C2410_NFECC + 2);
|
||||
|
||||
pr_debug("calculate_ecc: returning ecc %02x,%02x,%02x\n", ecc_code[0], ecc_code[1], ecc_code[2]);
|
||||
pr_debug("%s: returning ecc %02x%02x%02x\n", __func__,
|
||||
ecc_code[0], ecc_code[1], ecc_code[2]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -397,7 +450,7 @@ static int s3c2440_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u
|
|||
ecc_code[1] = ecc >> 8;
|
||||
ecc_code[2] = ecc >> 16;
|
||||
|
||||
pr_debug("calculate_ecc: returning ecc %02x,%02x,%02x\n", ecc_code[0], ecc_code[1], ecc_code[2]);
|
||||
pr_debug("%s: returning ecc %06x\n", __func__, ecc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue