From a07d229497f9672d1212700d85ce719ecf63e2af Mon Sep 17 00:00:00 2001 From: "Wu, Josh" Date: Thu, 4 Jul 2013 15:36:23 +0800 Subject: [PATCH] ARM: at91: atmel_nand: add code to check the ONFI parameter ECC requirement MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit 1. if CONFIG_SYS_NAND_ONFI_DETECTION is defined, driver will check NAND flash's ecc minimum requirement in ONFI parameter. a) if CONFIG_PMECC_CAP, CONFIG_PMECC_SECTOR_SIZE are defined. then use it. Driver will display a WARNING if the values are different from ONFI parameters. b) if CONFIG_PMECC_CAP, CONFIG_PMECC_SECTOR_SIZE are not defined, then use the value from ONFI parameters. * If ONFI ECC parameters are in ONFI extended parameter page, since we are not support it, so assume the minimum ecc requirement is 2 bits in 512 bytes. * For non-ONFI support nand flash, also assume the minimum ecc requirement is 2 bits in 512 bytes. 2. if CONFIG_SYS_NAND_ONFI_DETECTION is not defined, just use CONFIG_PMECC_CAP and CONFIG_PMECC_SECTOR_SIZE. Signed-off-by: Josh Wu Acked-by: Scott Wood Signed-off-by: Andreas Bießmann --- drivers/mtd/nand/atmel_nand.c | 130 +++++++++++++++++++++++++++++++++- 1 file changed, 128 insertions(+), 2 deletions(-) diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c index 15fb4fb38d..96aca00ebc 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 -- 2.39.5