diff options
Diffstat (limited to 'drivers/mtd/nand/omap2.c')
-rw-r--r-- | drivers/mtd/nand/omap2.c | 108 |
1 files changed, 101 insertions, 7 deletions
diff --git a/drivers/mtd/nand/omap2.c b/drivers/mtd/nand/omap2.c index 1ff49b80bdaf..f0ed92e210a1 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,7 +1179,8 @@ 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; |
1165 | int i; | 1182 | u32 val; |
1183 | int i, j; | ||
1166 | 1184 | ||
1167 | nsectors = ((readl(info->reg.gpmc_ecc_config) >> 4) & 0x7) + 1; | 1185 | nsectors = ((readl(info->reg.gpmc_ecc_config) >> 4) & 0x7) + 1; |
1168 | for (i = 0; i < nsectors; i++) { | 1186 | for (i = 0; i < nsectors; i++) { |
@@ -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 | } |
@@ -1210,8 +1263,8 @@ static int __maybe_unused omap_calculate_ecc_bch(struct mtd_info *mtd, | |||
1210 | case OMAP_ECC_BCH4_CODE_HW_DETECTION_SW: | 1263 | case OMAP_ECC_BCH4_CODE_HW_DETECTION_SW: |
1211 | /* Add constant polynomial to remainder, so that | 1264 | /* Add constant polynomial to remainder, so that |
1212 | * ECC of blank pages results in 0x0 on reading back */ | 1265 | * ECC of blank pages results in 0x0 on reading back */ |
1213 | for (i = 0; i < eccbytes; i++) | 1266 | for (j = 0; j < eccbytes; j++) |
1214 | ecc_calc[i] ^= bch4_polynomial[i]; | 1267 | ecc_calc[j] ^= bch4_polynomial[j]; |
1215 | break; | 1268 | break; |
1216 | case OMAP_ECC_BCH4_CODE_HW: | 1269 | case OMAP_ECC_BCH4_CODE_HW: |
1217 | /* Set 8th ECC byte as 0x0 for ROM compatibility */ | 1270 | /* Set 8th ECC byte as 0x0 for ROM compatibility */ |
@@ -1220,13 +1273,15 @@ static int __maybe_unused omap_calculate_ecc_bch(struct mtd_info *mtd, | |||
1220 | case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW: | 1273 | case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW: |
1221 | /* Add constant polynomial to remainder, so that | 1274 | /* Add constant polynomial to remainder, so that |
1222 | * ECC of blank pages results in 0x0 on reading back */ | 1275 | * ECC of blank pages results in 0x0 on reading back */ |
1223 | for (i = 0; i < eccbytes; i++) | 1276 | for (j = 0; j < eccbytes; j++) |
1224 | ecc_calc[i] ^= bch8_polynomial[i]; | 1277 | ecc_calc[j] ^= bch8_polynomial[j]; |
1225 | break; | 1278 | break; |
1226 | case OMAP_ECC_BCH8_CODE_HW: | 1279 | case OMAP_ECC_BCH8_CODE_HW: |
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 | } |
@@ -1237,6 +1292,7 @@ static int __maybe_unused omap_calculate_ecc_bch(struct mtd_info *mtd, | |||
1237 | return 0; | 1292 | return 0; |
1238 | } | 1293 | } |
1239 | 1294 | ||
1295 | #ifdef CONFIG_MTD_NAND_OMAP_BCH | ||
1240 | /** | 1296 | /** |
1241 | * erased_sector_bitflips - count bit flips | 1297 | * erased_sector_bitflips - count bit flips |
1242 | * @data: data sector buffer | 1298 | * @data: data sector buffer |
@@ -1276,7 +1332,6 @@ static int erased_sector_bitflips(u_char *data, u_char *oob, | |||
1276 | return flip_bits; | 1332 | return flip_bits; |
1277 | } | 1333 | } |
1278 | 1334 | ||
1279 | #ifdef CONFIG_MTD_NAND_OMAP_BCH | ||
1280 | /** | 1335 | /** |
1281 | * omap_elm_correct_data - corrects page data area in case error reported | 1336 | * omap_elm_correct_data - corrects page data area in case error reported |
1282 | * @mtd: MTD device structure | 1337 | * @mtd: MTD device structure |
@@ -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; |
@@ -1382,7 +1441,7 @@ static int omap_elm_correct_data(struct mtd_info *mtd, u_char *data, | |||
1382 | 1441 | ||
1383 | /* Check if any error reported */ | 1442 | /* Check if any error reported */ |
1384 | if (!is_error_reported) | 1443 | if (!is_error_reported) |
1385 | return 0; | 1444 | return stat; |
1386 | 1445 | ||
1387 | /* Decode BCH error using ELM module */ | 1446 | /* Decode BCH error using ELM module */ |
1388 | elm_decode_bch_error_page(info->elm_dev, ecc_vec, err_vec); | 1447 | elm_decode_bch_error_page(info->elm_dev, ecc_vec, err_vec); |
@@ -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; |