diff options
| author | Tom Rini <trini@ti.com> | 2013-09-06 20:25:35 -0400 | 
|---|---|---|
| committer | Tom Rini <trini@ti.com> | 2013-09-06 20:25:35 -0400 | 
| commit | 47f75cf2e1d8648e3438630f3a4bddf9b5caa25d (patch) | |
| tree | 1d0f8f8943d44245381f255a059e258c696bc5a8 /drivers/mtd/nand/atmel_nand.c | |
| parent | 1affd4d4a3fe512050e1ad1636d9360c670da531 (diff) | |
| parent | 68e1747f9c0506159e8ecc9a4feb58e9c65a7b39 (diff) | |
| download | olio-uboot-2014.01-47f75cf2e1d8648e3438630f3a4bddf9b5caa25d.tar.xz olio-uboot-2014.01-47f75cf2e1d8648e3438630f3a4bddf9b5caa25d.zip | |
Merge branch 'master' of git://git.denx.de/u-boot-arm
Diffstat (limited to 'drivers/mtd/nand/atmel_nand.c')
| -rw-r--r-- | drivers/mtd/nand/atmel_nand.c | 201 | 
1 files changed, 190 insertions, 11 deletions
| diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c index f844990e3..da83f06e4 100644 --- a/drivers/mtd/nand/atmel_nand.c +++ b/drivers/mtd/nand/atmel_nand.c @@ -16,6 +16,7 @@  #include <asm/arch/gpio.h>  #include <asm/arch/at91_pio.h> +#include <malloc.h>  #include <nand.h>  #include <watchdog.h> @@ -50,13 +51,13 @@ struct atmel_nand_host {  	void __iomem	*pmecc_index_of;  	/* data for pmecc computation */ -	int16_t	pmecc_smu[(CONFIG_PMECC_CAP + 2) * (2 * CONFIG_PMECC_CAP + 1)]; -	int16_t	pmecc_partial_syn[2 * CONFIG_PMECC_CAP + 1]; -	int16_t	pmecc_si[2 * CONFIG_PMECC_CAP + 1]; -	int16_t	pmecc_lmu[CONFIG_PMECC_CAP + 1]; /* polynomal order */ -	int	pmecc_mu[CONFIG_PMECC_CAP + 1]; -	int	pmecc_dmu[CONFIG_PMECC_CAP + 1]; -	int	pmecc_delta[CONFIG_PMECC_CAP + 1]; +	int16_t	*pmecc_smu; +	int16_t	*pmecc_partial_syn; +	int16_t	*pmecc_si; +	int16_t	*pmecc_lmu; /* polynomal order */ +	int	*pmecc_mu; +	int	*pmecc_dmu; +	int	*pmecc_delta;  };  static struct atmel_nand_host pmecc_host; @@ -109,6 +110,48 @@ static void __iomem *pmecc_get_alpha_to(struct atmel_nand_host *host)  			table_size * sizeof(int16_t);  } +static void pmecc_data_free(struct atmel_nand_host *host) +{ +	free(host->pmecc_partial_syn); +	free(host->pmecc_si); +	free(host->pmecc_lmu); +	free(host->pmecc_smu); +	free(host->pmecc_mu); +	free(host->pmecc_dmu); +	free(host->pmecc_delta); +} + +static int pmecc_data_alloc(struct atmel_nand_host *host) +{ +	const int cap = host->pmecc_corr_cap; +	int size; + +	size = (2 * cap + 1) * sizeof(int16_t); +	host->pmecc_partial_syn = malloc(size); +	host->pmecc_si = malloc(size); +	host->pmecc_lmu = malloc((cap + 1) * sizeof(int16_t)); +	host->pmecc_smu = malloc((cap + 2) * size); + +	size = (cap + 1) * sizeof(int); +	host->pmecc_mu = malloc(size); +	host->pmecc_dmu = malloc(size); +	host->pmecc_delta = malloc(size); + +	if (host->pmecc_partial_syn && +			host->pmecc_si && +			host->pmecc_lmu && +			host->pmecc_smu && +			host->pmecc_mu && +			host->pmecc_dmu && +			host->pmecc_delta) +		return 0; + +	/* error happened */ +	pmecc_data_free(host); +	return -ENOMEM; + +} +  static void pmecc_gen_syndrome(struct mtd_info *mtd, int sector)  {  	struct nand_chip *nand_chip = mtd->priv; @@ -622,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)  { @@ -635,9 +771,45 @@ 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; -	host->pmecc_index_table_offset = CONFIG_PMECC_INDEX_TABLE_OFFSET; +#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 +		host->pmecc_index_table_offset = ATMEL_PMECC_INDEX_OFFSET_1024;  	MTDDEBUG(MTD_DEBUG_LEVEL1,  		"Initialize PMECC params, cap: %d, sector: %d\n", @@ -655,7 +827,8 @@ static int atmel_pmecc_nand_init_params(struct nand_chip *nand,  	switch (mtd->writesize) {  	case 2048:  	case 4096: -		host->pmecc_degree = PMECC_GF_DIMENSION_13; +		host->pmecc_degree = (sector_size == 512) ? +			PMECC_GF_DIMENSION_13 : PMECC_GF_DIMENSION_14;  		host->pmecc_cw_len = (1 << host->pmecc_degree) - 1;  		host->pmecc_sector_number = mtd->writesize / sector_size;  		host->pmecc_bytes_per_sector = pmecc_get_ecc_bytes( @@ -691,6 +864,12 @@ static int atmel_pmecc_nand_init_params(struct nand_chip *nand,  		return 0;  	} +	/* Allocate data for PMECC computation */ +	if (pmecc_data_alloc(host)) { +		dev_err(host->dev, "Cannot allocate memory for PMECC computation!\n"); +		return -ENOMEM; +	} +  	nand->ecc.read_page = atmel_nand_pmecc_read_page;  	nand->ecc.write_page = atmel_nand_pmecc_write_page;  	nand->ecc.strength = cap; |