mtd: nand: omap: add support for BCH16_ECC in ELM driver
With increase in NAND flash densities occurence of bit-flips has increased. Thus stronger ECC schemes are required for detecting and correcting multiple simultaneous bit-flips in same NAND page. But stronger ECC schemes have large ECC syndrome which require more space in OOB/Spare. This patch add support for BCH16_ECC: (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>
This commit is contained in:
parent
726c782423
commit
a72c2ee835
|
@ -25,31 +25,27 @@
|
|||
struct elm *elm_cfg;
|
||||
|
||||
/**
|
||||
* elm_load_syndromes - Load BCH syndromes based on nibble selection
|
||||
* elm_load_syndromes - Load calc_ecc in ELM registers for error detection
|
||||
* @syndrome: BCH syndrome
|
||||
* @nibbles:
|
||||
* @poly: Syndrome Polynomial set to use
|
||||
*
|
||||
* Load BCH syndromes based on nibble selection
|
||||
*/
|
||||
static void elm_load_syndromes(u8 *syndrome, u32 nibbles, u8 poly)
|
||||
static int elm_load_syndromes(u8 *syndrome, u32 bch_type, u8 poly)
|
||||
{
|
||||
u32 *ptr;
|
||||
u32 val;
|
||||
|
||||
/* reg 0 */
|
||||
ptr = &elm_cfg->syndrome_fragments[poly].syndrome_fragment_x[0];
|
||||
val = syndrome[0] | (syndrome[1] << 8) | (syndrome[2] << 16) |
|
||||
(syndrome[3] << 24);
|
||||
writel(val, ptr);
|
||||
/* reg 1 */
|
||||
ptr = &elm_cfg->syndrome_fragments[poly].syndrome_fragment_x[1];
|
||||
val = syndrome[4] | (syndrome[5] << 8) | (syndrome[6] << 16) |
|
||||
(syndrome[7] << 24);
|
||||
writel(val, ptr);
|
||||
|
||||
/* BCH 8-bit with 26 nibbles (4*8=32) */
|
||||
if (nibbles > 13) {
|
||||
switch (bch_type) {
|
||||
case ECC_BCH16:
|
||||
/* reg 0 */
|
||||
ptr = &elm_cfg->syndrome_fragments[poly].syndrome_fragment_x[0];
|
||||
val = syndrome[0] | (syndrome[1] << 8) | (syndrome[2] << 16) |
|
||||
(syndrome[3] << 24);
|
||||
writel(val, ptr);
|
||||
/* reg 1 */
|
||||
ptr = &elm_cfg->syndrome_fragments[poly].syndrome_fragment_x[1];
|
||||
val = syndrome[4] | (syndrome[5] << 8) | (syndrome[6] << 16) |
|
||||
(syndrome[7] << 24);
|
||||
writel(val, ptr);
|
||||
/* reg 2 */
|
||||
ptr = &elm_cfg->syndrome_fragments[poly].syndrome_fragment_x[2];
|
||||
val = syndrome[8] | (syndrome[9] << 8) | (syndrome[10] << 16) |
|
||||
|
@ -60,34 +56,65 @@ static void elm_load_syndromes(u8 *syndrome, u32 nibbles, u8 poly)
|
|||
val = syndrome[12] | (syndrome[13] << 8) |
|
||||
(syndrome[14] << 16) | (syndrome[15] << 24);
|
||||
writel(val, ptr);
|
||||
}
|
||||
|
||||
/* BCH 16-bit with 52 nibbles (7*8=56) */
|
||||
if (nibbles > 26) {
|
||||
/* reg 4 */
|
||||
ptr = &elm_cfg->syndrome_fragments[poly].syndrome_fragment_x[4];
|
||||
val = syndrome[16] | (syndrome[17] << 8) |
|
||||
(syndrome[18] << 16) | (syndrome[19] << 24);
|
||||
writel(val, ptr);
|
||||
|
||||
/* reg 5 */
|
||||
ptr = &elm_cfg->syndrome_fragments[poly].syndrome_fragment_x[5];
|
||||
val = syndrome[20] | (syndrome[21] << 8) |
|
||||
(syndrome[22] << 16) | (syndrome[23] << 24);
|
||||
writel(val, ptr);
|
||||
|
||||
/* reg 6 */
|
||||
ptr = &elm_cfg->syndrome_fragments[poly].syndrome_fragment_x[6];
|
||||
val = syndrome[24] | (syndrome[25] << 8) |
|
||||
(syndrome[26] << 16) | (syndrome[27] << 24);
|
||||
val = syndrome[24] | (syndrome[25] << 8);
|
||||
writel(val, ptr);
|
||||
break;
|
||||
case ECC_BCH8:
|
||||
/* reg 0 */
|
||||
ptr = &elm_cfg->syndrome_fragments[poly].syndrome_fragment_x[0];
|
||||
val = syndrome[0] | (syndrome[1] << 8) | (syndrome[2] << 16) |
|
||||
(syndrome[3] << 24);
|
||||
writel(val, ptr);
|
||||
/* reg 1 */
|
||||
ptr = &elm_cfg->syndrome_fragments[poly].syndrome_fragment_x[1];
|
||||
val = syndrome[4] | (syndrome[5] << 8) | (syndrome[6] << 16) |
|
||||
(syndrome[7] << 24);
|
||||
writel(val, ptr);
|
||||
/* reg 2 */
|
||||
ptr = &elm_cfg->syndrome_fragments[poly].syndrome_fragment_x[2];
|
||||
val = syndrome[8] | (syndrome[9] << 8) | (syndrome[10] << 16) |
|
||||
(syndrome[11] << 24);
|
||||
writel(val, ptr);
|
||||
/* reg 3 */
|
||||
ptr = &elm_cfg->syndrome_fragments[poly].syndrome_fragment_x[3];
|
||||
val = syndrome[12];
|
||||
writel(val, ptr);
|
||||
break;
|
||||
case ECC_BCH4:
|
||||
/* reg 0 */
|
||||
ptr = &elm_cfg->syndrome_fragments[poly].syndrome_fragment_x[0];
|
||||
val = syndrome[0] | (syndrome[1] << 8) | (syndrome[2] << 16) |
|
||||
(syndrome[3] << 24);
|
||||
writel(val, ptr);
|
||||
/* reg 1 */
|
||||
ptr = &elm_cfg->syndrome_fragments[poly].syndrome_fragment_x[1];
|
||||
val = syndrome[4] | (syndrome[5] << 8) | (syndrome[6] << 16) |
|
||||
(syndrome[7] << 24);
|
||||
writel(val, ptr);
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* elm_check_errors - Check for BCH errors and return error locations
|
||||
* elm_check_error - Check for BCH errors and return error locations
|
||||
* @syndrome: BCH syndrome
|
||||
* @nibbles:
|
||||
* @bch_type: BCH4/BCH8/BCH16
|
||||
* @error_count: Returns number of errrors in the syndrome
|
||||
* @error_locations: Returns error locations (in decimal) in this array
|
||||
*
|
||||
|
@ -95,15 +122,17 @@ static void elm_load_syndromes(u8 *syndrome, u32 nibbles, u8 poly)
|
|||
* and locations in the array passed. Returns -1 if error is not correctable,
|
||||
* else returns 0
|
||||
*/
|
||||
int elm_check_error(u8 *syndrome, u32 nibbles, u32 *error_count,
|
||||
int elm_check_error(u8 *syndrome, u32 bch_type, u32 *error_count,
|
||||
u32 *error_locations)
|
||||
{
|
||||
u8 poly = ELM_DEFAULT_POLY;
|
||||
s8 i;
|
||||
u32 location_status;
|
||||
|
||||
elm_load_syndromes(syndrome, nibbles, poly);
|
||||
|
||||
if (elm_load_syndromes(syndrome, bch_type, poly)) {
|
||||
printf("ELM: *Error: invalid driver configuration\n");
|
||||
return -1;
|
||||
}
|
||||
/* start processing */
|
||||
writel((readl(&elm_cfg->syndrome_fragments[poly].syndrome_fragment_x[6])
|
||||
| ELM_SYNDROME_FRAGMENT_6_SYNDROME_VALID),
|
||||
|
@ -143,14 +172,14 @@ int elm_check_error(u8 *syndrome, u32 nibbles, u32 *error_count,
|
|||
* Currently we are using only syndrome 0 and syndromes 1 to 6 are not used.
|
||||
* Also, the mode is set only for syndrome 0
|
||||
*/
|
||||
int elm_config(enum bch_level level)
|
||||
int elm_config(u32 bch_type)
|
||||
{
|
||||
u32 val;
|
||||
u8 poly = ELM_DEFAULT_POLY;
|
||||
u32 buffer_size = 0x7FF;
|
||||
|
||||
/* config size and level */
|
||||
val = (u32)(level) & ELM_LOCATION_CONFIG_ECC_BCH_LEVEL_MASK;
|
||||
val = bch_type & ELM_LOCATION_CONFIG_ECC_BCH_LEVEL_MASK;
|
||||
val |= ((buffer_size << ELM_LOCATION_CONFIG_ECC_SIZE_POS) &
|
||||
ELM_LOCATION_CONFIG_ECC_SIZE_MASK);
|
||||
writel(val, &elm_cfg->location_config);
|
||||
|
|
|
@ -382,8 +382,8 @@ static int omap_correct_data_bch(struct mtd_info *mtd, uint8_t *dat,
|
|||
return -EINVAL;
|
||||
}
|
||||
/* use elm module to check for errors */
|
||||
elm_config((enum bch_level)(bch->type));
|
||||
if (elm_check_error(calc_ecc, bch->nibbles, &error_count, error_loc)) {
|
||||
elm_config(bch->type);
|
||||
if (elm_check_error(calc_ecc, bch->type, &error_count, error_loc)) {
|
||||
printf("nand: error: uncorrectable ECC errors\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
|
|
@ -24,15 +24,13 @@
|
|||
#define ELM_LOCATION_STATUS_ECC_CORRECTABLE_MASK (0x100)
|
||||
#define ELM_LOCATION_STATUS_ECC_NB_ERRORS_MASK (0x1F)
|
||||
|
||||
/* bch types */
|
||||
#define ECC_BCH4 0
|
||||
#define ECC_BCH8 1
|
||||
#define ECC_BCH16 2
|
||||
|
||||
#ifndef __ASSEMBLY__
|
||||
|
||||
enum bch_level {
|
||||
BCH_4_BIT = 0,
|
||||
BCH_8_BIT,
|
||||
BCH_16_BIT
|
||||
};
|
||||
|
||||
|
||||
/* BCH syndrome registers */
|
||||
struct syndrome {
|
||||
u32 syndrome_fragment_x[7]; /* 0x400, 0x404.... 0x418 */
|
||||
|
@ -68,9 +66,9 @@ struct elm {
|
|||
struct location error_location[8]; /* 0x800 */
|
||||
};
|
||||
|
||||
int elm_check_error(u8 *syndrome, u32 nibbles, u32 *error_count,
|
||||
int elm_check_error(u8 *syndrome, u32 bch_type, u32 *error_count,
|
||||
u32 *error_locations);
|
||||
int elm_config(enum bch_level level);
|
||||
int elm_config(u32 bch_type);
|
||||
void elm_reset(void);
|
||||
void elm_init(void);
|
||||
#endif /* __ASSEMBLY__ */
|
||||
|
|
Loading…
Reference in New Issue