nand: atmel: Correct bitflips in erased pages

Not correcting anything in case of empty ECC data area
is not an appropriate strategy, because an uncorrected bit-flip
in an empty sector may cause upper layers (namely UBI) fail to work
properly. Therefore the approach chosen in Linux kernel and other
u-boot mtd drivers has been adopted, where a heuristic implemented
by nand_check_erased_ecc_chunk() is used in order to detect and
correct empty sectors.

Tested with sama5d3_xplained and sam9x60-ek.

Signed-off-by: Kai Stuhlemmer (ebee Engineering) <kai.stuhlemmer@ebee.de>
Tested-by: Tudor Ambarus <tudor.ambarus@microchip.com>
[ta: reorder if conditions, change commit subject, s/uint8_t/u8.]
Signed-off-by: Tudor Ambarus <tudor.ambarus@microchip.com>
This commit is contained in:
Kai Stuhlemmer (ebee Engineering) 2021-05-21 11:52:06 +03:00 committed by Eugen Hristev
parent 55661ee0e3
commit 32cc2368f8

View File

@ -493,21 +493,9 @@ static int pmecc_correction(struct mtd_info *mtd, u32 pmecc_stat, uint8_t *buf,
{ {
struct nand_chip *nand_chip = mtd_to_nand(mtd); struct nand_chip *nand_chip = mtd_to_nand(mtd);
struct atmel_nand_host *host = nand_get_controller_data(nand_chip); struct atmel_nand_host *host = nand_get_controller_data(nand_chip);
int i, err_nbr, eccbytes; int i, err_nbr;
uint8_t *buf_pos; u8 *buf_pos, *ecc_pos;
/* SAMA5D4 PMECC IP can correct errors for all 0xff page */
if (host->pmecc_version >= PMECC_VERSION_SAMA5D4)
goto normal_check;
eccbytes = nand_chip->ecc.bytes;
for (i = 0; i < eccbytes; i++)
if (ecc[i] != 0xff)
goto normal_check;
/* Erased page, return OK */
return 0;
normal_check:
for (i = 0; i < host->pmecc_sector_number; i++) { for (i = 0; i < host->pmecc_sector_number; i++) {
err_nbr = 0; err_nbr = 0;
if (pmecc_stat & 0x1) { if (pmecc_stat & 0x1) {
@ -518,15 +506,26 @@ normal_check:
pmecc_get_sigma(mtd); pmecc_get_sigma(mtd);
err_nbr = pmecc_err_location(mtd); err_nbr = pmecc_err_location(mtd);
if (err_nbr == -1) { if (err_nbr >= 0) {
pmecc_correct_data(mtd, buf_pos, ecc, i,
host->pmecc_bytes_per_sector,
err_nbr);
} else if (host->pmecc_version < PMECC_VERSION_SAMA5D4) {
ecc_pos = ecc + i * host->pmecc_bytes_per_sector;
err_nbr = nand_check_erased_ecc_chunk(
buf_pos, host->pmecc_sector_size,
ecc_pos, host->pmecc_bytes_per_sector,
NULL, 0, host->pmecc_corr_cap);
}
if (err_nbr < 0) {
dev_err(mtd->dev, "PMECC: Too many errors\n"); dev_err(mtd->dev, "PMECC: Too many errors\n");
mtd->ecc_stats.failed++; mtd->ecc_stats.failed++;
return -EBADMSG; return -EBADMSG;
} else {
pmecc_correct_data(mtd, buf_pos, ecc, i,
host->pmecc_bytes_per_sector, err_nbr);
mtd->ecc_stats.corrected += err_nbr;
} }
mtd->ecc_stats.corrected += err_nbr;
} }
pmecc_stat >>= 1; pmecc_stat >>= 1;
} }