diff options
Diffstat (limited to 'drivers/mtd/nand/s3c2410.c')
-rw-r--r-- | drivers/mtd/nand/s3c2410.c | 96 |
1 files changed, 88 insertions, 8 deletions
diff --git a/drivers/mtd/nand/s3c2410.c b/drivers/mtd/nand/s3c2410.c index 8b3203571eeb..0ddfd6de75c5 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; | ||
377 | |||
378 | byte |= (diff0 >> 3) & 0x08; | ||
379 | byte |= (diff0 >> 2) & 0x04; | ||
380 | byte |= (diff0 >> 1) & 0x02; | ||
381 | byte |= (diff0 >> 0) & 0x01; | ||
347 | 382 | ||
348 | /* we curently have no method for correcting the error */ | 383 | byte |= (diff2 << 8) & 0x100; |
349 | 384 | ||
350 | return -1; | 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 |
@@ -366,6 +418,15 @@ static void s3c2410_nand_enable_hwecc(struct mtd_info *mtd, int mode) | |||
366 | writel(ctrl, info->regs + S3C2410_NFCONF); | 418 | writel(ctrl, info->regs + S3C2410_NFCONF); |
367 | } | 419 | } |
368 | 420 | ||
421 | static void s3c2412_nand_enable_hwecc(struct mtd_info *mtd, int mode) | ||
422 | { | ||
423 | struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); | ||
424 | unsigned long ctrl; | ||
425 | |||
426 | ctrl = readl(info->regs + S3C2440_NFCONT); | ||
427 | writel(ctrl | S3C2412_NFCONT_INIT_MAIN_ECC, info->regs + S3C2440_NFCONT); | ||
428 | } | ||
429 | |||
369 | static void s3c2440_nand_enable_hwecc(struct mtd_info *mtd, int mode) | 430 | static void s3c2440_nand_enable_hwecc(struct mtd_info *mtd, int mode) |
370 | { | 431 | { |
371 | struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); | 432 | struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); |
@@ -383,6 +444,21 @@ 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); | 444 | ecc_code[1] = readb(info->regs + S3C2410_NFECC + 1); |
384 | ecc_code[2] = readb(info->regs + S3C2410_NFECC + 2); | 445 | ecc_code[2] = readb(info->regs + S3C2410_NFECC + 2); |
385 | 446 | ||
447 | pr_debug("%s: returning ecc %02x%02x%02x\n", __func__, | ||
448 | ecc_code[0], ecc_code[1], ecc_code[2]); | ||
449 | |||
450 | return 0; | ||
451 | } | ||
452 | |||
453 | static int s3c2412_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code) | ||
454 | { | ||
455 | struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); | ||
456 | unsigned long ecc = readl(info->regs + S3C2412_NFMECC0); | ||
457 | |||
458 | ecc_code[0] = ecc; | ||
459 | ecc_code[1] = ecc >> 8; | ||
460 | ecc_code[2] = ecc >> 16; | ||
461 | |||
386 | pr_debug("calculate_ecc: returning ecc %02x,%02x,%02x\n", ecc_code[0], ecc_code[1], ecc_code[2]); | 462 | pr_debug("calculate_ecc: returning ecc %02x,%02x,%02x\n", ecc_code[0], ecc_code[1], ecc_code[2]); |
387 | 463 | ||
388 | return 0; | 464 | return 0; |
@@ -397,7 +473,7 @@ static int s3c2440_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u | |||
397 | ecc_code[1] = ecc >> 8; | 473 | ecc_code[1] = ecc >> 8; |
398 | ecc_code[2] = ecc >> 16; | 474 | ecc_code[2] = ecc >> 16; |
399 | 475 | ||
400 | pr_debug("calculate_ecc: returning ecc %02x,%02x,%02x\n", ecc_code[0], ecc_code[1], ecc_code[2]); | 476 | pr_debug("%s: returning ecc %06x\n", __func__, ecc); |
401 | 477 | ||
402 | return 0; | 478 | return 0; |
403 | } | 479 | } |
@@ -565,6 +641,10 @@ static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info, | |||
565 | break; | 641 | break; |
566 | 642 | ||
567 | case TYPE_S3C2412: | 643 | case TYPE_S3C2412: |
644 | chip->ecc.hwctl = s3c2412_nand_enable_hwecc; | ||
645 | chip->ecc.calculate = s3c2412_nand_calculate_ecc; | ||
646 | break; | ||
647 | |||
568 | case TYPE_S3C2440: | 648 | case TYPE_S3C2440: |
569 | chip->ecc.hwctl = s3c2440_nand_enable_hwecc; | 649 | chip->ecc.hwctl = s3c2440_nand_enable_hwecc; |
570 | chip->ecc.calculate = s3c2440_nand_calculate_ecc; | 650 | chip->ecc.calculate = s3c2440_nand_calculate_ecc; |