Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a2593247 authored by Ben Dooks's avatar Ben Dooks Committed by David Woodhouse
Browse files

[MTD] [NAND] S3C2410: Hardware ECC correction code



Add support for correcting errors detected by the
hardware ECC.

Signed-off-by: default avatarBen Dooks <ben-linux@fluff.org>
Signed-off-by: default avatarDavid Woodhouse <dwmw2@infradead.org>
parent a7a6ace1
Loading
Loading
Loading
Loading
+0 −4
Original line number Diff line number Diff line
@@ -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
+62 −9
Original line number Diff line number Diff line
@@ -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);

	if (read_ecc[0] == calc_ecc[0] && read_ecc[1] == calc_ecc[1] && read_ecc[2] == calc_ecc[2])
		return 0;
	diff0 = read_ecc[0] ^ calc_ecc[0];
	diff1 = read_ecc[1] ^ calc_ecc[1];
	diff2 = read_ecc[2] ^ calc_ecc[2];

	/* we curently have no method for correcting the error */
	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);

	return -1;
	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;

	return 0;
}

/* 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;
}