diff options
| -rw-r--r-- | drivers/mtd/nand/atmel_nand.c | 130 | 
1 files changed, 128 insertions, 2 deletions
| diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c index 15fb4fb38..96aca00eb 100644 --- a/drivers/mtd/nand/atmel_nand.c +++ b/drivers/mtd/nand/atmel_nand.c @@ -665,6 +665,99 @@ static void atmel_pmecc_core_init(struct mtd_info *mtd)  	pmecc_writel(host->pmecc, ctrl, PMECC_CTRL_ENABLE);  } +#ifdef CONFIG_SYS_NAND_ONFI_DETECTION +/* + * get_onfi_ecc_param - Get ECC requirement from ONFI parameters + * @ecc_bits: store the ONFI ECC correct bits capbility + * @sector_size: in how many bytes that ONFI require to correct @ecc_bits + * + * Returns -1 if ONFI parameters is not supported. In this case @ecc_bits, + * @sector_size are initialize to 0. + * Return 0 if success to get the ECC requirement. + */ +static int get_onfi_ecc_param(struct nand_chip *chip, +		int *ecc_bits, int *sector_size) +{ +	*ecc_bits = *sector_size = 0; + +	if (chip->onfi_params.ecc_bits == 0xff) +		/* TODO: the sector_size and ecc_bits need to be find in +		 * extended ecc parameter, currently we don't support it. +		 */ +		return -1; + +	*ecc_bits = chip->onfi_params.ecc_bits; + +	/* The default sector size (ecc codeword size) is 512 */ +	*sector_size = 512; + +	return 0; +} + +/* + * pmecc_choose_ecc - Get ecc requirement from ONFI parameters. If + *                    pmecc_corr_cap or pmecc_sector_size is 0, then set it as + *                    ONFI ECC parameters. + * @host: point to an atmel_nand_host structure. + *        if host->pmecc_corr_cap is 0 then set it as the ONFI ecc_bits. + *        if host->pmecc_sector_size is 0 then set it as the ONFI sector_size. + * @chip: point to an nand_chip structure. + * @cap: store the ONFI ECC correct bits capbility + * @sector_size: in how many bytes that ONFI require to correct @ecc_bits + * + * Return 0 if success. otherwise return the error code. + */ +static int pmecc_choose_ecc(struct atmel_nand_host *host, +		struct nand_chip *chip, +		int *cap, int *sector_size) +{ +	/* Get ECC requirement from ONFI parameters */ +	*cap = *sector_size = 0; +	if (chip->onfi_version) { +		if (!get_onfi_ecc_param(chip, cap, sector_size)) { +			MTDDEBUG(MTD_DEBUG_LEVEL1, "ONFI params, minimum required ECC: %d bits in %d bytes\n", +				*cap, *sector_size); +		} else { +			dev_info(host->dev, "NAND chip ECC reqirement is in Extended ONFI parameter, we don't support yet.\n"); +		} +	} else { +		dev_info(host->dev, "NAND chip is not ONFI compliant, assume ecc_bits is 2 in 512 bytes"); +	} +	if (*cap == 0 && *sector_size == 0) { +		/* Non-ONFI compliant or use extended ONFI parameters */ +		*cap = 2; +		*sector_size = 512; +	} + +	/* If head file doesn't specify then use the one in ONFI parameters */ +	if (host->pmecc_corr_cap == 0) { +		/* use the most fitable ecc bits (the near bigger one ) */ +		if (*cap <= 2) +			host->pmecc_corr_cap = 2; +		else if (*cap <= 4) +			host->pmecc_corr_cap = 4; +		else if (*cap <= 8) +			host->pmecc_corr_cap = 8; +		else if (*cap <= 12) +			host->pmecc_corr_cap = 12; +		else if (*cap <= 24) +			host->pmecc_corr_cap = 24; +		else +			return -EINVAL; +	} +	if (host->pmecc_sector_size == 0) { +		/* use the most fitable sector size (the near smaller one ) */ +		if (*sector_size >= 1024) +			host->pmecc_sector_size = 1024; +		else if (*sector_size >= 512) +			host->pmecc_sector_size = 512; +		else +			return -EINVAL; +	} +	return 0; +} +#endif +  static int atmel_pmecc_nand_init_params(struct nand_chip *nand,  		struct mtd_info *mtd)  { @@ -678,8 +771,41 @@ static int atmel_pmecc_nand_init_params(struct nand_chip *nand,  	nand->ecc.correct = NULL;  	nand->ecc.hwctl = NULL; -	cap = host->pmecc_corr_cap = CONFIG_PMECC_CAP; -	sector_size = host->pmecc_sector_size = CONFIG_PMECC_SECTOR_SIZE; +#ifdef CONFIG_SYS_NAND_ONFI_DETECTION +	host->pmecc_corr_cap = host->pmecc_sector_size = 0; + +#ifdef CONFIG_PMECC_CAP +	host->pmecc_corr_cap = CONFIG_PMECC_CAP; +#endif +#ifdef CONFIG_PMECC_SECTOR_SIZE +	host->pmecc_sector_size = CONFIG_PMECC_SECTOR_SIZE; +#endif +	/* Get ECC requirement of ONFI parameters. And if CONFIG_PMECC_CAP or +	 * CONFIG_PMECC_SECTOR_SIZE not defined, then use ecc_bits, sector_size +	 * from ONFI. +	 */ +	if (pmecc_choose_ecc(host, nand, &cap, §or_size)) { +		dev_err(host->dev, "The NAND flash's ECC requirement(ecc_bits: %d, sector_size: %d) are not support!", +				cap, sector_size); +		return -EINVAL; +	} + +	if (cap > host->pmecc_corr_cap) +		dev_info(host->dev, "WARNING: Using different ecc correct bits(%d bit) from Nand ONFI ECC reqirement (%d bit).\n", +				host->pmecc_corr_cap, cap); +	if (sector_size < host->pmecc_sector_size) +		dev_info(host->dev, "WARNING: Using different ecc correct sector size (%d bytes) from Nand ONFI ECC reqirement (%d bytes).\n", +				host->pmecc_sector_size, sector_size); +#else	/* CONFIG_SYS_NAND_ONFI_DETECTION */ +	host->pmecc_corr_cap = CONFIG_PMECC_CAP; +	host->pmecc_sector_size = CONFIG_PMECC_SECTOR_SIZE; +#endif + +	cap = host->pmecc_corr_cap; +	sector_size = host->pmecc_sector_size; + +	/* TODO: need check whether cap & sector_size is validate */ +  	if (host->pmecc_sector_size == 512)  		host->pmecc_index_table_offset = ATMEL_PMECC_INDEX_OFFSET_512;  	else |