mtd: nand: omap: add support for BCH16_ECC - NAND driver updates
This patch add support for BCH16 ecc-scheme in OMAP NAND driver, by extending following functions: - omap_enable_hwecc (nand_chip->ecc.hwctl): configure GPMC controller - omap_calculate_ecc_bch (nand_chip->ecc.calculate): fetch ECC signature from GPMC controller - omap_elm_correct_data (nand_chip->ecc.correct): detect and correct ECC errors using ELM (a) BCH16 ecc-scheme can detect and correct 16 bit-flips per 512Bytes of data. (b) BCH16 ecc-scheme generates 26-bytes of ECC syndrome / 512B. Due to (b) this scheme can only be used with NAND devices which have enough OOB to satisfy the relation: "OOBsize per page >= 26 * (page-size / 512)" Signed-off-by: Pekon Gupta <pekon@ti.com> Signed-off-by: Brian Norris <computersforpeace@gmail.com>
This commit is contained in:
parent
2be589e4b2
commit
9748fff964
|
@ -137,6 +137,10 @@
|
||||||
#define BADBLOCK_MARKER_LENGTH 2
|
#define BADBLOCK_MARKER_LENGTH 2
|
||||||
|
|
||||||
#ifdef CONFIG_MTD_NAND_OMAP_BCH
|
#ifdef CONFIG_MTD_NAND_OMAP_BCH
|
||||||
|
static u_char bch16_vector[] = {0xf5, 0x24, 0x1c, 0xd0, 0x61, 0xb3, 0xf1, 0x55,
|
||||||
|
0x2e, 0x2c, 0x86, 0xa3, 0xed, 0x36, 0x1b, 0x78,
|
||||||
|
0x48, 0x76, 0xa9, 0x3b, 0x97, 0xd1, 0x7a, 0x93,
|
||||||
|
0x07, 0x0e};
|
||||||
static u_char bch8_vector[] = {0xf3, 0xdb, 0x14, 0x16, 0x8b, 0xd2, 0xbe, 0xcc,
|
static u_char bch8_vector[] = {0xf3, 0xdb, 0x14, 0x16, 0x8b, 0xd2, 0xbe, 0xcc,
|
||||||
0xac, 0x6b, 0xff, 0x99, 0x7b};
|
0xac, 0x6b, 0xff, 0x99, 0x7b};
|
||||||
static u_char bch4_vector[] = {0x00, 0x6b, 0x31, 0xdd, 0x41, 0xbc, 0x10};
|
static u_char bch4_vector[] = {0x00, 0x6b, 0x31, 0xdd, 0x41, 0xbc, 0x10};
|
||||||
|
@ -1114,6 +1118,19 @@ static void __maybe_unused omap_enable_hwecc_bch(struct mtd_info *mtd, int mode)
|
||||||
ecc_size1 = BCH_ECC_SIZE1;
|
ecc_size1 = BCH_ECC_SIZE1;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case OMAP_ECC_BCH16_CODE_HW:
|
||||||
|
bch_type = 0x2;
|
||||||
|
nsectors = chip->ecc.steps;
|
||||||
|
if (mode == NAND_ECC_READ) {
|
||||||
|
wr_mode = 0x01;
|
||||||
|
ecc_size0 = 52; /* ECC bits in nibbles per sector */
|
||||||
|
ecc_size1 = 0; /* non-ECC bits in nibbles per sector */
|
||||||
|
} else {
|
||||||
|
wr_mode = 0x01;
|
||||||
|
ecc_size0 = 0; /* extra bits in nibbles per sector */
|
||||||
|
ecc_size1 = 52; /* OOB bits in nibbles per sector */
|
||||||
|
}
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1162,6 +1179,7 @@ static int __maybe_unused omap_calculate_ecc_bch(struct mtd_info *mtd,
|
||||||
struct gpmc_nand_regs *gpmc_regs = &info->reg;
|
struct gpmc_nand_regs *gpmc_regs = &info->reg;
|
||||||
u8 *ecc_code;
|
u8 *ecc_code;
|
||||||
unsigned long nsectors, bch_val1, bch_val2, bch_val3, bch_val4;
|
unsigned long nsectors, bch_val1, bch_val2, bch_val3, bch_val4;
|
||||||
|
u32 val;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
nsectors = ((readl(info->reg.gpmc_ecc_config) >> 4) & 0x7) + 1;
|
nsectors = ((readl(info->reg.gpmc_ecc_config) >> 4) & 0x7) + 1;
|
||||||
|
@ -1201,6 +1219,41 @@ static int __maybe_unused omap_calculate_ecc_bch(struct mtd_info *mtd,
|
||||||
*ecc_code++ = ((bch_val1 >> 4) & 0xFF);
|
*ecc_code++ = ((bch_val1 >> 4) & 0xFF);
|
||||||
*ecc_code++ = ((bch_val1 & 0xF) << 4);
|
*ecc_code++ = ((bch_val1 & 0xF) << 4);
|
||||||
break;
|
break;
|
||||||
|
case OMAP_ECC_BCH16_CODE_HW:
|
||||||
|
val = readl(gpmc_regs->gpmc_bch_result6[i]);
|
||||||
|
ecc_code[0] = ((val >> 8) & 0xFF);
|
||||||
|
ecc_code[1] = ((val >> 0) & 0xFF);
|
||||||
|
val = readl(gpmc_regs->gpmc_bch_result5[i]);
|
||||||
|
ecc_code[2] = ((val >> 24) & 0xFF);
|
||||||
|
ecc_code[3] = ((val >> 16) & 0xFF);
|
||||||
|
ecc_code[4] = ((val >> 8) & 0xFF);
|
||||||
|
ecc_code[5] = ((val >> 0) & 0xFF);
|
||||||
|
val = readl(gpmc_regs->gpmc_bch_result4[i]);
|
||||||
|
ecc_code[6] = ((val >> 24) & 0xFF);
|
||||||
|
ecc_code[7] = ((val >> 16) & 0xFF);
|
||||||
|
ecc_code[8] = ((val >> 8) & 0xFF);
|
||||||
|
ecc_code[9] = ((val >> 0) & 0xFF);
|
||||||
|
val = readl(gpmc_regs->gpmc_bch_result3[i]);
|
||||||
|
ecc_code[10] = ((val >> 24) & 0xFF);
|
||||||
|
ecc_code[11] = ((val >> 16) & 0xFF);
|
||||||
|
ecc_code[12] = ((val >> 8) & 0xFF);
|
||||||
|
ecc_code[13] = ((val >> 0) & 0xFF);
|
||||||
|
val = readl(gpmc_regs->gpmc_bch_result2[i]);
|
||||||
|
ecc_code[14] = ((val >> 24) & 0xFF);
|
||||||
|
ecc_code[15] = ((val >> 16) & 0xFF);
|
||||||
|
ecc_code[16] = ((val >> 8) & 0xFF);
|
||||||
|
ecc_code[17] = ((val >> 0) & 0xFF);
|
||||||
|
val = readl(gpmc_regs->gpmc_bch_result1[i]);
|
||||||
|
ecc_code[18] = ((val >> 24) & 0xFF);
|
||||||
|
ecc_code[19] = ((val >> 16) & 0xFF);
|
||||||
|
ecc_code[20] = ((val >> 8) & 0xFF);
|
||||||
|
ecc_code[21] = ((val >> 0) & 0xFF);
|
||||||
|
val = readl(gpmc_regs->gpmc_bch_result0[i]);
|
||||||
|
ecc_code[22] = ((val >> 24) & 0xFF);
|
||||||
|
ecc_code[23] = ((val >> 16) & 0xFF);
|
||||||
|
ecc_code[24] = ((val >> 8) & 0xFF);
|
||||||
|
ecc_code[25] = ((val >> 0) & 0xFF);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
@ -1227,6 +1280,8 @@ static int __maybe_unused omap_calculate_ecc_bch(struct mtd_info *mtd,
|
||||||
/* Set 14th ECC byte as 0x0 for ROM compatibility */
|
/* Set 14th ECC byte as 0x0 for ROM compatibility */
|
||||||
ecc_calc[eccbytes - 1] = 0x0;
|
ecc_calc[eccbytes - 1] = 0x0;
|
||||||
break;
|
break;
|
||||||
|
case OMAP_ECC_BCH16_CODE_HW:
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
@ -1318,6 +1373,10 @@ static int omap_elm_correct_data(struct mtd_info *mtd, u_char *data,
|
||||||
actual_eccbytes = ecc->bytes - 1;
|
actual_eccbytes = ecc->bytes - 1;
|
||||||
erased_ecc_vec = bch8_vector;
|
erased_ecc_vec = bch8_vector;
|
||||||
break;
|
break;
|
||||||
|
case OMAP_ECC_BCH16_CODE_HW:
|
||||||
|
actual_eccbytes = ecc->bytes;
|
||||||
|
erased_ecc_vec = bch16_vector;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
pr_err("invalid driver configuration\n");
|
pr_err("invalid driver configuration\n");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
@ -1401,6 +1460,7 @@ static int omap_elm_correct_data(struct mtd_info *mtd, u_char *data,
|
||||||
BCH4_BIT_PAD;
|
BCH4_BIT_PAD;
|
||||||
break;
|
break;
|
||||||
case OMAP_ECC_BCH8_CODE_HW:
|
case OMAP_ECC_BCH8_CODE_HW:
|
||||||
|
case OMAP_ECC_BCH16_CODE_HW:
|
||||||
pos = err_vec[i].error_loc[j];
|
pos = err_vec[i].error_loc[j];
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@ -1912,6 +1972,40 @@ static int omap_nand_probe(struct platform_device *pdev)
|
||||||
goto return_error;
|
goto return_error;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
case OMAP_ECC_BCH16_CODE_HW:
|
||||||
|
#ifdef CONFIG_MTD_NAND_OMAP_BCH
|
||||||
|
pr_info("using OMAP_ECC_BCH16_CODE_HW ECC scheme\n");
|
||||||
|
nand_chip->ecc.mode = NAND_ECC_HW;
|
||||||
|
nand_chip->ecc.size = 512;
|
||||||
|
nand_chip->ecc.bytes = 26;
|
||||||
|
nand_chip->ecc.strength = 16;
|
||||||
|
nand_chip->ecc.hwctl = omap_enable_hwecc_bch;
|
||||||
|
nand_chip->ecc.correct = omap_elm_correct_data;
|
||||||
|
nand_chip->ecc.calculate = omap_calculate_ecc_bch;
|
||||||
|
nand_chip->ecc.read_page = omap_read_page_bch;
|
||||||
|
nand_chip->ecc.write_page = omap_write_page_bch;
|
||||||
|
/* This ECC scheme requires ELM H/W block */
|
||||||
|
err = is_elm_present(info, pdata->elm_of_node, BCH16_ECC);
|
||||||
|
if (err < 0) {
|
||||||
|
pr_err("ELM is required for this ECC scheme\n");
|
||||||
|
goto return_error;
|
||||||
|
}
|
||||||
|
/* define ECC layout */
|
||||||
|
ecclayout->eccbytes = nand_chip->ecc.bytes *
|
||||||
|
(mtd->writesize /
|
||||||
|
nand_chip->ecc.size);
|
||||||
|
oob_index = BADBLOCK_MARKER_LENGTH;
|
||||||
|
for (i = 0; i < ecclayout->eccbytes; i++, oob_index++)
|
||||||
|
ecclayout->eccpos[i] = oob_index;
|
||||||
|
/* reserved marker already included in ecclayout->eccbytes */
|
||||||
|
ecclayout->oobfree->offset =
|
||||||
|
ecclayout->eccpos[ecclayout->eccbytes - 1] + 1;
|
||||||
|
break;
|
||||||
|
#else
|
||||||
|
pr_err("nand: error: CONFIG_MTD_NAND_OMAP_BCH not enabled\n");
|
||||||
|
err = -EINVAL;
|
||||||
|
goto return_error;
|
||||||
|
#endif
|
||||||
default:
|
default:
|
||||||
pr_err("nand: error: invalid or unsupported ECC scheme\n");
|
pr_err("nand: error: invalid or unsupported ECC scheme\n");
|
||||||
err = -EINVAL;
|
err = -EINVAL;
|
||||||
|
|
Loading…
Reference in a new issue