aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/mtd/nand/s3c2410.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mtd/nand/s3c2410.c')
-rw-r--r--drivers/mtd/nand/s3c2410.c96
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)
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;
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
421static 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
369static void s3c2440_nand_enable_hwecc(struct mtd_info *mtd, int mode) 430static 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
453static 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;