[PATCH v5] MTD: atmel_nand: Update driver to support Programmable Multibit ECC controller

Jean-Christophe PLAGNIOL-VILLARD plagnioj at jcrosoft.com
Thu May 3 09:42:54 EDT 2012


On 20:37 Thu 03 May     , Josh Wu wrote:
> The Programmable Multibit ECC (PMECC) controller is a programmable binary
> BCH(Bose, Chaudhuri and Hocquenghem) encoder and decoder. This controller
> can be used to support both SLC and MLC NAND Flash devices. It supports to
> generate ECC to correct 2, 4, 8, 12 or 24 bits of error per sector of data.
> 
> To use this driver, the user needs to pass in the correction capability and
> the sector size.
> 
> This driver has been tested on AT91SAM9X5-EK and AT91SAM9N12-EK with JFFS2,
> YAFFS2, UBIFS and mtd-utils.
> 
> Signed-off-by: Hong Xu <hong.xu at atmel.com>
> Signed-off-by: Josh Wu <josh.wu at atmel.com>
> ---
> Hi, 
> 
> Since Hong Xu will not work on pmecc part. so I send out this patch base Hong Xu's latest work.
> 
> Changes since v4,
> 	fix typo and checkpatch warnings.
> 	fix according to JC's suggestion. replace cpu_is_xxx() with DT
> 	modify dt binding atmel nand document to add pmecc support.
> 	tested in sam9263 without break hw ecc.
> 	add ecc.strength.
> 
>  .../devicetree/bindings/mtd/atmel-nand.txt         |    5 +
>  drivers/mtd/nand/atmel_nand.c                      |  955 ++++++++++++++++++--
>  drivers/mtd/nand/atmel_nand_ecc.h                  |  123 ++-
>  3 files changed, 1004 insertions(+), 79 deletions(-)
> 
> diff --git a/Documentation/devicetree/bindings/mtd/atmel-nand.txt b/Documentation/devicetree/bindings/mtd/atmel-nand.txt
> index a200695..8936175 100644
> --- a/Documentation/devicetree/bindings/mtd/atmel-nand.txt
> +++ b/Documentation/devicetree/bindings/mtd/atmel-nand.txt
> @@ -16,9 +16,14 @@ Optional properties:
>  - nand-ecc-mode : String, operation mode of the NAND ecc mode, soft by default.
>    Supported values are: "none", "soft", "hw", "hw_syndrome", "hw_oob_first",
>    "soft_bch".
> +- atmel,pmecc-cap : error correct capability for Programmable Multibit ECC
> +  Controller. Supported values are: 2, 4, 8, 12, 24.
you need to use it in the code instead of hardcoding it so
> +- atmel,sector-size : sector size for ECC computation. Supported values are:
> +  512, 1024.
>  - nand-bus-width : 8 or 16 bus width if not present 8
>  - nand-on-flash-bbt: boolean to enable on flash bbt option if not present false
>  
> +
>  Examples:
>  nand0: nand at 40000000,0 {
>  	compatible = "atmel,at91rm9200-nand";
<snip>
> +
> +	cap = host->correction_cap;
> +	sector_size = host->sector_size;
> +	dev_info(host->dev, "Initialize PMECC params, cap: %d, sector: %d\n",
> +		 cap, sector_size);
> +
> +	/* Sanity check */
> +	if ((sector_size != 512) && (sector_size != 1024)) {
> +		dev_err(host->dev, "Unsupported PMECC sector size: %d; " \
> +			"Valid sector size: 512 or 1024 bytes\n", sector_size);
> +		return -EINVAL;
> +	}
> +	if ((cap != 2) && (cap != 4) && (cap != 8) && (cap != 12) &&
> +	    (cap != 24)) {
> +		dev_err(host->dev, "Unsupported PMECC correction capability," \
> +			" Valid one is either 2, 4, 8, 12 or 24\n");
> +		return -EINVAL;
> +	}
> +
> +	nand_chip = &host->nand_chip;
> +	mtd = &host->mtd;
> +
> +	nand_chip->ecc.mode = NAND_ECC_SOFT;	/* By default */
> +
> +	regs = platform_get_resource(pdev, IORESOURCE_MEM, 1);
> +	if (!regs) {
> +		dev_warn(host->dev, "Can't get I/O resource regs, " \
> +				"rolling back on software ECC\n");
> +		return 0;
> +	}
> +
> +	host->ecc = ioremap(regs->start, resource_size(regs));
> +	if (host->ecc == NULL) {
> +		dev_err(host->dev, "ioremap failed\n");
> +		err_no = -EIO;
> +		goto err_pmecc_ioremap;
> +	}
> +
> +	regs_pmerr = platform_get_resource(pdev, IORESOURCE_MEM, 2);
> +	regs_rom = platform_get_resource(pdev, IORESOURCE_MEM, 3);
> +	if (regs_pmerr && regs_rom) {
> +		host->pmerrloc_base = ioremap(regs_pmerr->start,
> +			resource_size(regs_pmerr));
> +		host->rom_base = ioremap(regs_rom->start,
> +			resource_size(regs_rom));
> +
> +		if (host->pmerrloc_base && host->rom_base) {
> +			nand_chip->ecc.mode = NAND_ECC_HW;
> +			nand_chip->ecc.read_page =
> +				atmel_nand_pmecc_read_page;
> +			nand_chip->ecc.write_page =
> +				atmel_nand_pmecc_write_page;
> +		} else {
> +			dev_err(host->dev, "Can not get I/O resource" \
> +				" for HW PMECC controller!\n");
> +			err_no = -EIO;
> +			goto err_pmloc_ioremap;
> +		}
> +	}
> +
> +	/* ECC is calculated for the whole page (1 step) */
> +	nand_chip->ecc.size = mtd->writesize;
> +
> +	/* set ECC page size and oob layout */
> +	switch (mtd->writesize) {
> +	case 2048:
> +		host->degree = GF_DIMENSION_13;
> +		host->cw_len = (1 << host->degree) - 1;
> +		host->cap = cap;
> +		host->sector_number = mtd->writesize / sector_size;
> +		host->ecc_bytes_per_sector = pmecc_get_ecc_bytes(
> +			host->cap, sector_size);
> +		host->alpha_to = pmecc_get_alpha_to(host);
> +		host->index_of = pmecc_get_index_of(host);
> +
> +		nand_chip->ecc.steps = 1;
> +		nand_chip->ecc.strength = cap;
> +		nand_chip->ecc.bytes = host->ecc_bytes_per_sector *
> +				       host->sector_number;
> +		if (nand_chip->ecc.bytes > mtd->oobsize - 2) {
> +			dev_err(host->dev, "No room for ECC bytes\n");
> +			err_no = -EINVAL;
> +			goto err;
> +		}
> +		pmecc_config_ecc_layout(&atmel_pmecc_oobinfo,
> +					mtd->oobsize,
> +					nand_chip->ecc.bytes);
> +		nand_chip->ecc.layout = &atmel_pmecc_oobinfo;
> +		break;
> +	case 512:
> +	case 1024:
> +	case 4096:
> +		/* TODO */
> +		dev_warn(host->dev, "Only 2048 page size is currently " \
> +			"supported for PMECC, rolling back to Software ECC\n");
> +	default:
> +		/* page size not handled by HW ECC */
> +		/* switching back to soft ECC */
> +		nand_chip->ecc.mode = NAND_ECC_SOFT;
> +		nand_chip->ecc.calculate = NULL;
> +		nand_chip->ecc.correct = NULL;
> +		nand_chip->ecc.hwctl = NULL;
> +		nand_chip->ecc.read_page = NULL;
> +		nand_chip->ecc.write_page = NULL;
> +		nand_chip->ecc.postpad = 0;
> +		nand_chip->ecc.prepad = 0;
> +		nand_chip->ecc.bytes = 0;
> +		err_no = 0;
> +		goto err;
> +	}
> +
> +	atmel_pmecc_core_init(mtd);
> +
> +	return 0;
> +
> +err:
> +err_pmloc_ioremap:
> +	iounmap(host->ecc);
> +	if (host->pmerrloc_base)
> +		iounmap(host->pmerrloc_base);
> +	if (host->rom_base)
> +		iounmap(host->rom_base);
> +err_pmecc_ioremap:
> +	return err_no;
> +}
> +
> +/*
>   * Calculate HW ECC
>   *
>   * function called after a write
> @@ -474,6 +1212,15 @@ static void atmel_nand_hwctl(struct mtd_info *mtd, int mode)
>  }
>  
>  #if defined(CONFIG_OF)
> +static int cpu_has_pmecc(void)
> +{
> +	unsigned long dt_root;
> +
> +	dt_root = of_get_flat_dt_root();
> +	return of_flat_dt_is_compatible(dt_root, "atmel,at91sam9n12") ||
> +		of_flat_dt_is_compatible(dt_root, "atmel,at91sam9x5");
you need to detect it at probe time

and you need to put the first compatbile only no need to put a list

Best Regards,
J.



More information about the linux-arm-kernel mailing list