Commit 46840f66caf564866d191886d2bd86742f982010
Committed by
Tom Rini
1 parent
8d13a730de
Exists in
v2017.01-smarct4x
and in
40 other branches
mtd: nand: omap: add support for BCH16_ECC - NAND driver updates
This patch add support for BCH16_ECC to omap_gpmc driver. *need to BCH16 ECC scheme* With newer SLC Flash technologies and MLC NAND, and large densities, pagesizes Flash devices have become more suspectible to bit-flips. Thus stronger ECC schemes are required for protecting the data. But stronger ECC schemes have come with larger-sized ECC syndromes which require more space in OOB/Spare. This puts constrains like; (a) BCH16_ECC can correct 16 bit-flips per 512Bytes of data. (b) BCH16_ECC 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 following equation: OOBsize per page >= 26 * (page-size / 512) Signed-off-by: Pekon Gupta <pekon@ti.com>
Showing 2 changed files with 86 additions and 1 deletions Side-by-side Diff
drivers/mtd/nand/omap_gpmc.c
... | ... | @@ -224,6 +224,19 @@ |
224 | 224 | eccsize1 = 2; /* non-ECC bits in nibbles per sector */ |
225 | 225 | } |
226 | 226 | break; |
227 | + case OMAP_ECC_BCH16_CODE_HW: | |
228 | + ecc_algo = 0x1; | |
229 | + bch_type = 0x2; | |
230 | + if (mode == NAND_ECC_WRITE) { | |
231 | + bch_wrapmode = 0x01; | |
232 | + eccsize0 = 0; /* extra bits in nibbles per sector */ | |
233 | + eccsize1 = 52; /* OOB bits in nibbles per sector */ | |
234 | + } else { | |
235 | + bch_wrapmode = 0x01; | |
236 | + eccsize0 = 52; /* ECC bits in nibbles per sector */ | |
237 | + eccsize1 = 0; /* non-ECC bits in nibbles per sector */ | |
238 | + } | |
239 | + break; | |
227 | 240 | default: |
228 | 241 | return; |
229 | 242 | } |
... | ... | @@ -290,6 +303,29 @@ |
290 | 303 | ptr--; |
291 | 304 | } |
292 | 305 | break; |
306 | + case OMAP_ECC_BCH16_CODE_HW: | |
307 | + val = readl(&gpmc_cfg->bch_result_4_6[0].bch_result_x[2]); | |
308 | + ecc_code[i++] = (val >> 8) & 0xFF; | |
309 | + ecc_code[i++] = (val >> 0) & 0xFF; | |
310 | + val = readl(&gpmc_cfg->bch_result_4_6[0].bch_result_x[1]); | |
311 | + ecc_code[i++] = (val >> 24) & 0xFF; | |
312 | + ecc_code[i++] = (val >> 16) & 0xFF; | |
313 | + ecc_code[i++] = (val >> 8) & 0xFF; | |
314 | + ecc_code[i++] = (val >> 0) & 0xFF; | |
315 | + val = readl(&gpmc_cfg->bch_result_4_6[0].bch_result_x[0]); | |
316 | + ecc_code[i++] = (val >> 24) & 0xFF; | |
317 | + ecc_code[i++] = (val >> 16) & 0xFF; | |
318 | + ecc_code[i++] = (val >> 8) & 0xFF; | |
319 | + ecc_code[i++] = (val >> 0) & 0xFF; | |
320 | + for (j = 3; j >= 0; j--) { | |
321 | + val = readl(&gpmc_cfg->bch_result_0_3[0].bch_result_x[j] | |
322 | + ); | |
323 | + ecc_code[i++] = (val >> 24) & 0xFF; | |
324 | + ecc_code[i++] = (val >> 16) & 0xFF; | |
325 | + ecc_code[i++] = (val >> 8) & 0xFF; | |
326 | + ecc_code[i++] = (val >> 0) & 0xFF; | |
327 | + } | |
328 | + break; | |
293 | 329 | default: |
294 | 330 | return -EINVAL; |
295 | 331 | } |
... | ... | @@ -308,6 +344,8 @@ |
308 | 344 | case OMAP_ECC_BCH8_CODE_HW: |
309 | 345 | ecc_code[chip->ecc.bytes - 1] = 0x00; |
310 | 346 | break; |
347 | + case OMAP_ECC_BCH16_CODE_HW: | |
348 | + break; | |
311 | 349 | default: |
312 | 350 | return -EINVAL; |
313 | 351 | } |
... | ... | @@ -333,7 +371,7 @@ |
333 | 371 | struct omap_nand_info *info = chip->priv; |
334 | 372 | struct nand_ecc_ctrl *ecc = &chip->ecc; |
335 | 373 | uint32_t error_count = 0, error_max; |
336 | - uint32_t error_loc[8]; | |
374 | + uint32_t error_loc[ELM_MAX_ERROR_COUNT]; | |
337 | 375 | enum bch_level bch_type; |
338 | 376 | uint32_t i, ecc_flag = 0; |
339 | 377 | uint8_t count, err = 0; |
... | ... | @@ -365,6 +403,10 @@ |
365 | 403 | bch_type = BCH_8_BIT; |
366 | 404 | omap_reverse_list(calc_ecc, ecc->bytes - 1); |
367 | 405 | break; |
406 | + case OMAP_ECC_BCH16_CODE_HW: | |
407 | + bch_type = BCH_16_BIT; | |
408 | + omap_reverse_list(calc_ecc, ecc->bytes); | |
409 | + break; | |
368 | 410 | default: |
369 | 411 | return -EINVAL; |
370 | 412 | } |
... | ... | @@ -381,6 +423,9 @@ |
381 | 423 | /* 14th byte in ECC is reserved to match ROM layout */ |
382 | 424 | error_max = SECTOR_BYTES + (ecc->bytes - 1); |
383 | 425 | break; |
426 | + case OMAP_ECC_BCH16_CODE_HW: | |
427 | + error_max = SECTOR_BYTES + ecc->bytes; | |
428 | + break; | |
384 | 429 | default: |
385 | 430 | return -EINVAL; |
386 | 431 | } |
... | ... | @@ -666,6 +711,38 @@ |
666 | 711 | return -EINVAL; |
667 | 712 | #endif |
668 | 713 | |
714 | + case OMAP_ECC_BCH16_CODE_HW: | |
715 | +#ifdef CONFIG_NAND_OMAP_ELM | |
716 | + debug("nand: using OMAP_ECC_BCH16_CODE_HW\n"); | |
717 | + /* check ecc-scheme requirements before updating ecc info */ | |
718 | + if ((26 * eccsteps) + BADBLOCK_MARKER_LENGTH > oobsize) { | |
719 | + printf("nand: error: insufficient OOB: require=%d\n", ( | |
720 | + (26 * eccsteps) + BADBLOCK_MARKER_LENGTH)); | |
721 | + return -EINVAL; | |
722 | + } | |
723 | + /* intialize ELM for ECC error detection */ | |
724 | + elm_init(); | |
725 | + /* populate ecc specific fields */ | |
726 | + nand->ecc.mode = NAND_ECC_HW; | |
727 | + nand->ecc.size = SECTOR_BYTES; | |
728 | + nand->ecc.bytes = 26; | |
729 | + nand->ecc.strength = 16; | |
730 | + nand->ecc.hwctl = omap_enable_hwecc; | |
731 | + nand->ecc.correct = omap_correct_data_bch; | |
732 | + nand->ecc.calculate = omap_calculate_ecc; | |
733 | + nand->ecc.read_page = omap_read_page_bch; | |
734 | + /* define ecc-layout */ | |
735 | + ecclayout->eccbytes = nand->ecc.bytes * eccsteps; | |
736 | + for (i = 0; i < ecclayout->eccbytes; i++) | |
737 | + ecclayout->eccpos[i] = i + BADBLOCK_MARKER_LENGTH; | |
738 | + ecclayout->oobfree[0].offset = i + BADBLOCK_MARKER_LENGTH; | |
739 | + ecclayout->oobfree[0].length = oobsize - nand->ecc.bytes - | |
740 | + BADBLOCK_MARKER_LENGTH; | |
741 | + break; | |
742 | +#else | |
743 | + printf("nand: error: CONFIG_NAND_OMAP_ELM required for ECC\n"); | |
744 | + return -EINVAL; | |
745 | +#endif | |
669 | 746 | default: |
670 | 747 | debug("nand: error: ecc scheme not enabled or supported\n"); |
671 | 748 | return -EINVAL; |
include/linux/mtd/omap_gpmc.h
... | ... | @@ -27,6 +27,8 @@ |
27 | 27 | OMAP_ECC_BCH8_CODE_HW_DETECTION_SW, |
28 | 28 | /* 8-bit ECC calculation by GPMC, Error detection by ELM */ |
29 | 29 | OMAP_ECC_BCH8_CODE_HW, |
30 | + /* 16-bit ECC calculation by GPMC, Error detection by ELM */ | |
31 | + OMAP_ECC_BCH16_CODE_HW, | |
30 | 32 | }; |
31 | 33 | |
32 | 34 | struct gpmc_cs { |
... | ... | @@ -47,6 +49,10 @@ |
47 | 49 | u32 bch_result_x[4]; |
48 | 50 | }; |
49 | 51 | |
52 | +struct bch_res_4_6 { | |
53 | + u32 bch_result_x[3]; | |
54 | +}; | |
55 | + | |
50 | 56 | struct gpmc { |
51 | 57 | u8 res1[0x10]; |
52 | 58 | u32 sysconfig; /* 0x10 */ |
... | ... | @@ -77,6 +83,8 @@ |
77 | 83 | u32 testmomde_ctrl; /* 0x230 */ |
78 | 84 | u8 res8[12]; /* 0x234 */ |
79 | 85 | struct bch_res_0_3 bch_result_0_3[GPMC_MAX_SECTORS]; /* 0x240,0x250, */ |
86 | + u8 res9[16 * 4]; /* 0x2C0 - 0x2FF */ | |
87 | + struct bch_res_4_6 bch_result_4_6[GPMC_MAX_SECTORS]; /* 0x300,0x310, */ | |
80 | 88 | }; |
81 | 89 | |
82 | 90 | /* Used for board specific gpmc initialization */ |