mirror of
				https://github.com/linux-sunxi/u-boot-sunxi.git
				synced 2024-02-12 11:16:03 +08:00 
			
		
		
		
	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>
This commit is contained in:
		@ -224,6 +224,19 @@ static void omap_enable_hwecc(struct mtd_info *mtd, int32_t mode)
 | 
			
		||||
			eccsize1 = 2;  /* non-ECC bits in nibbles per sector */
 | 
			
		||||
		}
 | 
			
		||||
		break;
 | 
			
		||||
	case OMAP_ECC_BCH16_CODE_HW:
 | 
			
		||||
		ecc_algo = 0x1;
 | 
			
		||||
		bch_type = 0x2;
 | 
			
		||||
		if (mode == NAND_ECC_WRITE) {
 | 
			
		||||
			bch_wrapmode = 0x01;
 | 
			
		||||
			eccsize0 = 0;  /* extra bits in nibbles per sector */
 | 
			
		||||
			eccsize1 = 52; /* OOB bits in nibbles per sector */
 | 
			
		||||
		} else {
 | 
			
		||||
			bch_wrapmode = 0x01;
 | 
			
		||||
			eccsize0 = 52; /* ECC bits in nibbles per sector */
 | 
			
		||||
			eccsize1 = 0;  /* non-ECC bits in nibbles per sector */
 | 
			
		||||
		}
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
@ -290,6 +303,29 @@ static int omap_calculate_ecc(struct mtd_info *mtd, const uint8_t *dat,
 | 
			
		||||
			ptr--;
 | 
			
		||||
		}
 | 
			
		||||
		break;
 | 
			
		||||
	case OMAP_ECC_BCH16_CODE_HW:
 | 
			
		||||
		val = readl(&gpmc_cfg->bch_result_4_6[0].bch_result_x[2]);
 | 
			
		||||
		ecc_code[i++] = (val >>  8) & 0xFF;
 | 
			
		||||
		ecc_code[i++] = (val >>  0) & 0xFF;
 | 
			
		||||
		val = readl(&gpmc_cfg->bch_result_4_6[0].bch_result_x[1]);
 | 
			
		||||
		ecc_code[i++] = (val >> 24) & 0xFF;
 | 
			
		||||
		ecc_code[i++] = (val >> 16) & 0xFF;
 | 
			
		||||
		ecc_code[i++] = (val >>  8) & 0xFF;
 | 
			
		||||
		ecc_code[i++] = (val >>  0) & 0xFF;
 | 
			
		||||
		val = readl(&gpmc_cfg->bch_result_4_6[0].bch_result_x[0]);
 | 
			
		||||
		ecc_code[i++] = (val >> 24) & 0xFF;
 | 
			
		||||
		ecc_code[i++] = (val >> 16) & 0xFF;
 | 
			
		||||
		ecc_code[i++] = (val >>  8) & 0xFF;
 | 
			
		||||
		ecc_code[i++] = (val >>  0) & 0xFF;
 | 
			
		||||
		for (j = 3; j >= 0; j--) {
 | 
			
		||||
			val = readl(&gpmc_cfg->bch_result_0_3[0].bch_result_x[j]
 | 
			
		||||
									);
 | 
			
		||||
			ecc_code[i++] = (val >> 24) & 0xFF;
 | 
			
		||||
			ecc_code[i++] = (val >> 16) & 0xFF;
 | 
			
		||||
			ecc_code[i++] = (val >>  8) & 0xFF;
 | 
			
		||||
			ecc_code[i++] = (val >>  0) & 0xFF;
 | 
			
		||||
		}
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
@ -308,6 +344,8 @@ static int omap_calculate_ecc(struct mtd_info *mtd, const uint8_t *dat,
 | 
			
		||||
	case OMAP_ECC_BCH8_CODE_HW:
 | 
			
		||||
		ecc_code[chip->ecc.bytes - 1] = 0x00;
 | 
			
		||||
		break;
 | 
			
		||||
	case OMAP_ECC_BCH16_CODE_HW:
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
@ -333,7 +371,7 @@ static int omap_correct_data_bch(struct mtd_info *mtd, uint8_t *dat,
 | 
			
		||||
	struct omap_nand_info *info = chip->priv;
 | 
			
		||||
	struct nand_ecc_ctrl *ecc = &chip->ecc;
 | 
			
		||||
	uint32_t error_count = 0, error_max;
 | 
			
		||||
	uint32_t error_loc[8];
 | 
			
		||||
	uint32_t error_loc[ELM_MAX_ERROR_COUNT];
 | 
			
		||||
	enum bch_level bch_type;
 | 
			
		||||
	uint32_t i, ecc_flag = 0;
 | 
			
		||||
	uint8_t count, err = 0;
 | 
			
		||||
@ -365,6 +403,10 @@ static int omap_correct_data_bch(struct mtd_info *mtd, uint8_t *dat,
 | 
			
		||||
		bch_type = BCH_8_BIT;
 | 
			
		||||
		omap_reverse_list(calc_ecc, ecc->bytes - 1);
 | 
			
		||||
		break;
 | 
			
		||||
	case OMAP_ECC_BCH16_CODE_HW:
 | 
			
		||||
		bch_type = BCH_16_BIT;
 | 
			
		||||
		omap_reverse_list(calc_ecc, ecc->bytes);
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
@ -381,6 +423,9 @@ static int omap_correct_data_bch(struct mtd_info *mtd, uint8_t *dat,
 | 
			
		||||
			/* 14th byte in ECC is reserved to match ROM layout */
 | 
			
		||||
			error_max = SECTOR_BYTES + (ecc->bytes - 1);
 | 
			
		||||
			break;
 | 
			
		||||
		case OMAP_ECC_BCH16_CODE_HW:
 | 
			
		||||
			error_max = SECTOR_BYTES + ecc->bytes;
 | 
			
		||||
			break;
 | 
			
		||||
		default:
 | 
			
		||||
			return -EINVAL;
 | 
			
		||||
		}
 | 
			
		||||
@ -666,6 +711,38 @@ static int omap_select_ecc_scheme(struct nand_chip *nand,
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	case OMAP_ECC_BCH16_CODE_HW:
 | 
			
		||||
#ifdef CONFIG_NAND_OMAP_ELM
 | 
			
		||||
		debug("nand: using OMAP_ECC_BCH16_CODE_HW\n");
 | 
			
		||||
		/* check ecc-scheme requirements before updating ecc info */
 | 
			
		||||
		if ((26 * eccsteps) + BADBLOCK_MARKER_LENGTH > oobsize) {
 | 
			
		||||
			printf("nand: error: insufficient OOB: require=%d\n", (
 | 
			
		||||
				(26 * eccsteps) + BADBLOCK_MARKER_LENGTH));
 | 
			
		||||
			return -EINVAL;
 | 
			
		||||
		}
 | 
			
		||||
		/* intialize ELM for ECC error detection */
 | 
			
		||||
		elm_init();
 | 
			
		||||
		/* populate ecc specific fields */
 | 
			
		||||
		nand->ecc.mode		= NAND_ECC_HW;
 | 
			
		||||
		nand->ecc.size		= SECTOR_BYTES;
 | 
			
		||||
		nand->ecc.bytes		= 26;
 | 
			
		||||
		nand->ecc.strength	= 16;
 | 
			
		||||
		nand->ecc.hwctl		= omap_enable_hwecc;
 | 
			
		||||
		nand->ecc.correct	= omap_correct_data_bch;
 | 
			
		||||
		nand->ecc.calculate	= omap_calculate_ecc;
 | 
			
		||||
		nand->ecc.read_page	= omap_read_page_bch;
 | 
			
		||||
		/* define ecc-layout */
 | 
			
		||||
		ecclayout->eccbytes	= nand->ecc.bytes * eccsteps;
 | 
			
		||||
		for (i = 0; i < ecclayout->eccbytes; i++)
 | 
			
		||||
			ecclayout->eccpos[i] = i + BADBLOCK_MARKER_LENGTH;
 | 
			
		||||
		ecclayout->oobfree[0].offset = i + BADBLOCK_MARKER_LENGTH;
 | 
			
		||||
		ecclayout->oobfree[0].length = oobsize - nand->ecc.bytes -
 | 
			
		||||
						BADBLOCK_MARKER_LENGTH;
 | 
			
		||||
		break;
 | 
			
		||||
#else
 | 
			
		||||
		printf("nand: error: CONFIG_NAND_OMAP_ELM required for ECC\n");
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
#endif
 | 
			
		||||
	default:
 | 
			
		||||
		debug("nand: error: ecc scheme not enabled or supported\n");
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
@ -27,6 +27,8 @@ enum omap_ecc {
 | 
			
		||||
	OMAP_ECC_BCH8_CODE_HW_DETECTION_SW,
 | 
			
		||||
	/* 8-bit  ECC calculation by GPMC, Error detection by ELM */
 | 
			
		||||
	OMAP_ECC_BCH8_CODE_HW,
 | 
			
		||||
	/* 16-bit  ECC calculation by GPMC, Error detection by ELM */
 | 
			
		||||
	OMAP_ECC_BCH16_CODE_HW,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct gpmc_cs {
 | 
			
		||||
@ -47,6 +49,10 @@ struct bch_res_0_3 {
 | 
			
		||||
	u32 bch_result_x[4];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct bch_res_4_6 {
 | 
			
		||||
	u32 bch_result_x[3];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct gpmc {
 | 
			
		||||
	u8 res1[0x10];
 | 
			
		||||
	u32 sysconfig;		/* 0x10 */
 | 
			
		||||
@ -77,6 +83,8 @@ struct gpmc {
 | 
			
		||||
	u32 testmomde_ctrl;	/* 0x230 */
 | 
			
		||||
	u8 res8[12];		/* 0x234 */
 | 
			
		||||
	struct bch_res_0_3 bch_result_0_3[GPMC_MAX_SECTORS]; /* 0x240,0x250, */
 | 
			
		||||
	u8 res9[16 * 4];	/* 0x2C0 - 0x2FF */
 | 
			
		||||
	struct bch_res_4_6 bch_result_4_6[GPMC_MAX_SECTORS]; /* 0x300,0x310, */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Used for board specific gpmc initialization */
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user