diff options
| author | pekon gupta <pekon@ti.com> | 2014-03-24 07:20:05 -0400 |
|---|---|---|
| committer | Brian Norris <computersforpeace@gmail.com> | 2014-05-20 20:52:15 -0400 |
| commit | 9748fff96484cfc8bb382ef1436823aefe065c9c (patch) | |
| tree | d036ed4f1f6b0cd564fbf7276201d4e89e60413a | |
| parent | 2be589e4b28457f148640dc6addf6da24af64b7f (diff) | |
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>
| -rw-r--r-- | drivers/mtd/nand/omap2.c | 94 |
1 files changed, 94 insertions, 0 deletions
diff --git a/drivers/mtd/nand/omap2.c b/drivers/mtd/nand/omap2.c index 10c319eaad8b..cbe87a8e6218 100644 --- a/drivers/mtd/nand/omap2.c +++ b/drivers/mtd/nand/omap2.c | |||
| @@ -137,6 +137,10 @@ | |||
| 137 | #define BADBLOCK_MARKER_LENGTH 2 | 137 | #define BADBLOCK_MARKER_LENGTH 2 |
| 138 | 138 | ||
| 139 | #ifdef CONFIG_MTD_NAND_OMAP_BCH | 139 | #ifdef CONFIG_MTD_NAND_OMAP_BCH |
| 140 | static u_char bch16_vector[] = {0xf5, 0x24, 0x1c, 0xd0, 0x61, 0xb3, 0xf1, 0x55, | ||
| 141 | 0x2e, 0x2c, 0x86, 0xa3, 0xed, 0x36, 0x1b, 0x78, | ||
| 142 | 0x48, 0x76, 0xa9, 0x3b, 0x97, 0xd1, 0x7a, 0x93, | ||
| 143 | 0x07, 0x0e}; | ||
| 140 | static u_char bch8_vector[] = {0xf3, 0xdb, 0x14, 0x16, 0x8b, 0xd2, 0xbe, 0xcc, | 144 | static u_char bch8_vector[] = {0xf3, 0xdb, 0x14, 0x16, 0x8b, 0xd2, 0xbe, 0xcc, |
| 141 | 0xac, 0x6b, 0xff, 0x99, 0x7b}; | 145 | 0xac, 0x6b, 0xff, 0x99, 0x7b}; |
| 142 | static u_char bch4_vector[] = {0x00, 0x6b, 0x31, 0xdd, 0x41, 0xbc, 0x10}; | 146 | 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) | |||
| 1114 | ecc_size1 = BCH_ECC_SIZE1; | 1118 | ecc_size1 = BCH_ECC_SIZE1; |
| 1115 | } | 1119 | } |
| 1116 | break; | 1120 | break; |
| 1121 | case OMAP_ECC_BCH16_CODE_HW: | ||
| 1122 | bch_type = 0x2; | ||
| 1123 | nsectors = chip->ecc.steps; | ||
| 1124 | if (mode == NAND_ECC_READ) { | ||
| 1125 | wr_mode = 0x01; | ||
| 1126 | ecc_size0 = 52; /* ECC bits in nibbles per sector */ | ||
| 1127 | ecc_size1 = 0; /* non-ECC bits in nibbles per sector */ | ||
| 1128 | } else { | ||
| 1129 | wr_mode = 0x01; | ||
| 1130 | ecc_size0 = 0; /* extra bits in nibbles per sector */ | ||
| 1131 | ecc_size1 = 52; /* OOB bits in nibbles per sector */ | ||
| 1132 | } | ||
| 1133 | break; | ||
| 1117 | default: | 1134 | default: |
| 1118 | return; | 1135 | return; |
| 1119 | } | 1136 | } |
| @@ -1162,6 +1179,7 @@ static int __maybe_unused omap_calculate_ecc_bch(struct mtd_info *mtd, | |||
| 1162 | struct gpmc_nand_regs *gpmc_regs = &info->reg; | 1179 | struct gpmc_nand_regs *gpmc_regs = &info->reg; |
| 1163 | u8 *ecc_code; | 1180 | u8 *ecc_code; |
| 1164 | unsigned long nsectors, bch_val1, bch_val2, bch_val3, bch_val4; | 1181 | unsigned long nsectors, bch_val1, bch_val2, bch_val3, bch_val4; |
| 1182 | u32 val; | ||
| 1165 | int i; | 1183 | int i; |
| 1166 | 1184 | ||
| 1167 | nsectors = ((readl(info->reg.gpmc_ecc_config) >> 4) & 0x7) + 1; | 1185 | 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, | |||
| 1201 | *ecc_code++ = ((bch_val1 >> 4) & 0xFF); | 1219 | *ecc_code++ = ((bch_val1 >> 4) & 0xFF); |
| 1202 | *ecc_code++ = ((bch_val1 & 0xF) << 4); | 1220 | *ecc_code++ = ((bch_val1 & 0xF) << 4); |
| 1203 | break; | 1221 | break; |
| 1222 | case OMAP_ECC_BCH16_CODE_HW: | ||
| 1223 | val = readl(gpmc_regs->gpmc_bch_result6[i]); | ||
| 1224 | ecc_code[0] = ((val >> 8) & 0xFF); | ||
| 1225 | ecc_code[1] = ((val >> 0) & 0xFF); | ||
| 1226 | val = readl(gpmc_regs->gpmc_bch_result5[i]); | ||
| 1227 | ecc_code[2] = ((val >> 24) & 0xFF); | ||
| 1228 | ecc_code[3] = ((val >> 16) & 0xFF); | ||
| 1229 | ecc_code[4] = ((val >> 8) & 0xFF); | ||
| 1230 | ecc_code[5] = ((val >> 0) & 0xFF); | ||
| 1231 | val = readl(gpmc_regs->gpmc_bch_result4[i]); | ||
| 1232 | ecc_code[6] = ((val >> 24) & 0xFF); | ||
| 1233 | ecc_code[7] = ((val >> 16) & 0xFF); | ||
| 1234 | ecc_code[8] = ((val >> 8) & 0xFF); | ||
| 1235 | ecc_code[9] = ((val >> 0) & 0xFF); | ||
| 1236 | val = readl(gpmc_regs->gpmc_bch_result3[i]); | ||
| 1237 | ecc_code[10] = ((val >> 24) & 0xFF); | ||
| 1238 | ecc_code[11] = ((val >> 16) & 0xFF); | ||
| 1239 | ecc_code[12] = ((val >> 8) & 0xFF); | ||
| 1240 | ecc_code[13] = ((val >> 0) & 0xFF); | ||
| 1241 | val = readl(gpmc_regs->gpmc_bch_result2[i]); | ||
| 1242 | ecc_code[14] = ((val >> 24) & 0xFF); | ||
| 1243 | ecc_code[15] = ((val >> 16) & 0xFF); | ||
| 1244 | ecc_code[16] = ((val >> 8) & 0xFF); | ||
| 1245 | ecc_code[17] = ((val >> 0) & 0xFF); | ||
| 1246 | val = readl(gpmc_regs->gpmc_bch_result1[i]); | ||
| 1247 | ecc_code[18] = ((val >> 24) & 0xFF); | ||
| 1248 | ecc_code[19] = ((val >> 16) & 0xFF); | ||
| 1249 | ecc_code[20] = ((val >> 8) & 0xFF); | ||
| 1250 | ecc_code[21] = ((val >> 0) & 0xFF); | ||
| 1251 | val = readl(gpmc_regs->gpmc_bch_result0[i]); | ||
| 1252 | ecc_code[22] = ((val >> 24) & 0xFF); | ||
| 1253 | ecc_code[23] = ((val >> 16) & 0xFF); | ||
| 1254 | ecc_code[24] = ((val >> 8) & 0xFF); | ||
| 1255 | ecc_code[25] = ((val >> 0) & 0xFF); | ||
| 1256 | break; | ||
| 1204 | default: | 1257 | default: |
| 1205 | return -EINVAL; | 1258 | return -EINVAL; |
| 1206 | } | 1259 | } |
| @@ -1227,6 +1280,8 @@ static int __maybe_unused omap_calculate_ecc_bch(struct mtd_info *mtd, | |||
| 1227 | /* Set 14th ECC byte as 0x0 for ROM compatibility */ | 1280 | /* Set 14th ECC byte as 0x0 for ROM compatibility */ |
| 1228 | ecc_calc[eccbytes - 1] = 0x0; | 1281 | ecc_calc[eccbytes - 1] = 0x0; |
| 1229 | break; | 1282 | break; |
| 1283 | case OMAP_ECC_BCH16_CODE_HW: | ||
| 1284 | break; | ||
| 1230 | default: | 1285 | default: |
| 1231 | return -EINVAL; | 1286 | return -EINVAL; |
| 1232 | } | 1287 | } |
| @@ -1318,6 +1373,10 @@ static int omap_elm_correct_data(struct mtd_info *mtd, u_char *data, | |||
| 1318 | actual_eccbytes = ecc->bytes - 1; | 1373 | actual_eccbytes = ecc->bytes - 1; |
| 1319 | erased_ecc_vec = bch8_vector; | 1374 | erased_ecc_vec = bch8_vector; |
| 1320 | break; | 1375 | break; |
| 1376 | case OMAP_ECC_BCH16_CODE_HW: | ||
| 1377 | actual_eccbytes = ecc->bytes; | ||
| 1378 | erased_ecc_vec = bch16_vector; | ||
| 1379 | break; | ||
| 1321 | default: | 1380 | default: |
| 1322 | pr_err("invalid driver configuration\n"); | 1381 | pr_err("invalid driver configuration\n"); |
| 1323 | return -EINVAL; | 1382 | return -EINVAL; |
| @@ -1401,6 +1460,7 @@ static int omap_elm_correct_data(struct mtd_info *mtd, u_char *data, | |||
| 1401 | BCH4_BIT_PAD; | 1460 | BCH4_BIT_PAD; |
| 1402 | break; | 1461 | break; |
| 1403 | case OMAP_ECC_BCH8_CODE_HW: | 1462 | case OMAP_ECC_BCH8_CODE_HW: |
| 1463 | case OMAP_ECC_BCH16_CODE_HW: | ||
| 1404 | pos = err_vec[i].error_loc[j]; | 1464 | pos = err_vec[i].error_loc[j]; |
| 1405 | break; | 1465 | break; |
| 1406 | default: | 1466 | default: |
| @@ -1912,6 +1972,40 @@ static int omap_nand_probe(struct platform_device *pdev) | |||
| 1912 | goto return_error; | 1972 | goto return_error; |
| 1913 | #endif | 1973 | #endif |
| 1914 | 1974 | ||
| 1975 | case OMAP_ECC_BCH16_CODE_HW: | ||
| 1976 | #ifdef CONFIG_MTD_NAND_OMAP_BCH | ||
| 1977 | pr_info("using OMAP_ECC_BCH16_CODE_HW ECC scheme\n"); | ||
| 1978 | nand_chip->ecc.mode = NAND_ECC_HW; | ||
| 1979 | nand_chip->ecc.size = 512; | ||
| 1980 | nand_chip->ecc.bytes = 26; | ||
| 1981 | nand_chip->ecc.strength = 16; | ||
| 1982 | nand_chip->ecc.hwctl = omap_enable_hwecc_bch; | ||
| 1983 | nand_chip->ecc.correct = omap_elm_correct_data; | ||
| 1984 | nand_chip->ecc.calculate = omap_calculate_ecc_bch; | ||
| 1985 | nand_chip->ecc.read_page = omap_read_page_bch; | ||
| 1986 | nand_chip->ecc.write_page = omap_write_page_bch; | ||
| 1987 | /* This ECC scheme requires ELM H/W block */ | ||
| 1988 | err = is_elm_present(info, pdata->elm_of_node, BCH16_ECC); | ||
| 1989 | if (err < 0) { | ||
| 1990 | pr_err("ELM is required for this ECC scheme\n"); | ||
| 1991 | goto return_error; | ||
| 1992 | } | ||
| 1993 | /* define ECC layout */ | ||
| 1994 | ecclayout->eccbytes = nand_chip->ecc.bytes * | ||
| 1995 | (mtd->writesize / | ||
| 1996 | nand_chip->ecc.size); | ||
| 1997 | oob_index = BADBLOCK_MARKER_LENGTH; | ||
| 1998 | for (i = 0; i < ecclayout->eccbytes; i++, oob_index++) | ||
| 1999 | ecclayout->eccpos[i] = oob_index; | ||
| 2000 | /* reserved marker already included in ecclayout->eccbytes */ | ||
| 2001 | ecclayout->oobfree->offset = | ||
| 2002 | ecclayout->eccpos[ecclayout->eccbytes - 1] + 1; | ||
| 2003 | break; | ||
| 2004 | #else | ||
| 2005 | pr_err("nand: error: CONFIG_MTD_NAND_OMAP_BCH not enabled\n"); | ||
| 2006 | err = -EINVAL; | ||
| 2007 | goto return_error; | ||
| 2008 | #endif | ||
| 1915 | default: | 2009 | default: |
| 1916 | pr_err("nand: error: invalid or unsupported ECC scheme\n"); | 2010 | pr_err("nand: error: invalid or unsupported ECC scheme\n"); |
| 1917 | err = -EINVAL; | 2011 | err = -EINVAL; |
