[PATCH v2 3/4] mtd: nand: pxa3xx: Use ECC strength and step size devicetree binding
Brian Norris
computersforpeace at gmail.com
Mon May 12 11:01:04 PDT 2014
Hi Ezequiel,
On Fri, Mar 21, 2014 at 08:34:49AM -0300, Ezequiel Garcia wrote:
> This commit adds support for the user to specify the ECC strength
> and step size through the devicetree. We keep the previous behavior,
> when there is no DT parameter provided.
>
> Also, if there is an ONFI-specified minimum ECC strength requirement,
> and the DT-specified ECC strength is weaker, print a warning and try to
> select a strength compatible with the ONFI requirement.
Are you sure you want to override? That seems contrary to the idea of a
DT property for specifying ECC. But maybe you have a good reason?
If you'd rather just warn the user, it's possible we could move that to
common code in nand_base.c.
>
> Signed-off-by: Ezequiel Garcia <ezequiel.garcia at free-electrons.com>
> ---
> drivers/mtd/nand/pxa3xx_nand.c | 47 +++++++++++++++++++--------
> include/linux/platform_data/mtd-nand-pxa3xx.h | 3 ++
> 2 files changed, 36 insertions(+), 14 deletions(-)
>
> diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c
> index cf7d757..cc13896 100644
> --- a/drivers/mtd/nand/pxa3xx_nand.c
> +++ b/drivers/mtd/nand/pxa3xx_nand.c
> @@ -1344,8 +1344,30 @@ static int pxa3xx_nand_sensing(struct pxa3xx_nand_info *info)
> }
>
> static int pxa_ecc_init(struct pxa3xx_nand_info *info,
> - struct nand_ecc_ctrl *ecc, int strength, int page_size)
> + struct nand_chip *chip,
> + struct pxa3xx_nand_platform_data *pdata,
> + unsigned int page_size)
> {
> + struct nand_ecc_ctrl *ecc = &chip->ecc;
> + unsigned int strength, onfi_strength;
> +
> + /* We need to normalize the ECC strengths, in order to compare them. */
> + strength = (pdata->ecc_strength * 512) / pdata->ecc_step_size;
> + onfi_strength = (chip->ecc_strength_ds * 512) / chip->ecc_step_ds;
This is not necessarily an "ONFI" property; we also have the full-ID
table in which a minimum ECC strength can be specified. Maybe you can
match the existing naming with "strength" and "strength_ds" (for
"datasheet").
> +
> + /*
> + * The user requested ECC strength cannot be weaker than the ONFI
> + * minimum specified ECC strength.
> + */
> + if (strength && strength < onfi_strength) {
> + dev_warn(&info->pdev->dev,
> + "requested ECC strength is too weak\n");
> + strength = onfi_strength;
> + }
> +
> + if (!strength)
> + strength = onfi_strength ? onfi_strength : 1;
> +
I'm also not sure your "normalization" is generally correct. You can't
really normalize correction strengths to get a fully valid comparison.
Remember, 4-bit/2048B correction is not the same as 1-bit/512B
correction; it is at least as strong, yes. But then, the reverse is
*not* a good comparison. So your code might say that a flash which
requires 4-bit/2KB can be satisfied by 1-bit/512B. (These numbers may
not be seen in practice, but you get my point, right?)
By my understanding, a correct comparison requires two parts, which I've
summarized like this in my own code:
/*
* Does the given configuration meet the requirements?
*
* If our configuration corrects A bits per B bytes and the minimum
* required correction level is X bits per Y bytes, then we must ensure
* both of the following are true:
*
* (1) A / B >= X / Y
* (2) A >= X
*
* Requirement (1) ensures we can correct for the required bitflip
* density.
* Requirement (2) ensures we can correct even when all bitflips are
* clumped in the same sector.
*/
I think you are doing (1), but not (2).
> if (strength == 1 && page_size == 2048) {
> info->chunk_size = 2048;
> info->spare_size = 40;
> @@ -1375,7 +1397,6 @@ static int pxa_ecc_init(struct pxa3xx_nand_info *info,
> ecc->size = info->chunk_size;
> ecc->layout = &ecc_layout_2KB_bch4bit;
> ecc->strength = 16;
> - return 1;
>
> } else if (strength == 4 && page_size == 4096) {
> info->ecc_bch = 1;
> @@ -1424,7 +1445,6 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
> uint32_t id = -1;
> uint64_t chipsize;
> int i, ret, num;
> - uint16_t ecc_strength, ecc_step;
>
> if (pdata->keep_config && !pxa3xx_nand_detect_config(info))
> goto KEEP_CONFIG;
> @@ -1519,17 +1539,8 @@ KEEP_CONFIG:
> }
> }
>
> - ecc_strength = chip->ecc_strength_ds;
> - ecc_step = chip->ecc_step_ds;
> -
> - /* Set default ECC strength requirements on non-ONFI devices */
> - if (ecc_strength < 1 && ecc_step < 1) {
> - ecc_strength = 1;
> - ecc_step = 512;
> - }
> -
> - ret = pxa_ecc_init(info, &chip->ecc, (ecc_strength * 512) / ecc_step,
> - mtd->writesize);
> + /* Selects an ECC and fills pxa3xx_nand_info and nand_chip */
> + ret = pxa_ecc_init(info, chip, pdata, mtd->writesize);
> if (ret)
> return ret;
>
> @@ -1729,6 +1740,14 @@ static int pxa3xx_nand_probe_dt(struct platform_device *pdev)
> of_property_read_u32(np, "num-cs", &pdata->num_cs);
> pdata->flash_bbt = of_get_nand_on_flash_bbt(np);
>
> + pdata->ecc_strength = of_get_nand_ecc_strength(np);
> + if (pdata->ecc_strength < 0)
ecc_strength is unsigned, so this error check doesn't work. Maybe you
want a local 'int ret' to temporarily stash the return value?
> + pdata->ecc_strength = 0;
> +
> + pdata->ecc_step_size = of_get_nand_ecc_step_size(np);
> + if (pdata->ecc_step_size < 0)
Ditto for ecc_step_size.
> + pdata->ecc_step_size = 0;
> +
> pdev->dev.platform_data = pdata;
>
> return 0;
> diff --git a/include/linux/platform_data/mtd-nand-pxa3xx.h b/include/linux/platform_data/mtd-nand-pxa3xx.h
> index a941471..5c0f2e3 100644
> --- a/include/linux/platform_data/mtd-nand-pxa3xx.h
> +++ b/include/linux/platform_data/mtd-nand-pxa3xx.h
> @@ -58,6 +58,9 @@ struct pxa3xx_nand_platform_data {
> /* use an flash-based bad block table */
> bool flash_bbt;
>
> + /* requested ECC strength and ECC step size */
> + unsigned int ecc_strength, ecc_step_size;
> +
> const struct mtd_partition *parts[NUM_CHIP_SELECT];
> unsigned int nr_parts[NUM_CHIP_SELECT];
>
Brian
More information about the linux-mtd
mailing list