Commit 46840f66caf564866d191886d2bd86742f982010

Authored by pekon gupta
Committed by Tom Rini
1 parent 8d13a730de

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 */