diff mbox series

[v5,04/17] mtd: rawnand: omap2: convert driver to nand_scan()

Message ID 20180725133152.30898-5-miquel.raynal@bootlin.com
State Accepted
Headers show
Series Allow dynamic allocations during NAND chip identification phase | expand

Commit Message

Miquel Raynal July 25, 2018, 1:31 p.m. UTC
Two helpers have been added to the core to do all kind of controller
side configuration/initialization between the detection phase and the
final NAND scan. Implement these hooks so that we can convert the driver
to just use nand_scan() instead of the nand_scan_ident() +
nand_scan_tail() pair.

Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
---
 drivers/mtd/nand/raw/omap2.c | 533 +++++++++++++++++++++----------------------
 1 file changed, 265 insertions(+), 268 deletions(-)

Comments

Boris Brezillon July 25, 2018, 3:39 p.m. UTC | #1
On Wed, 25 Jul 2018 15:31:39 +0200
Miquel Raynal <miquel.raynal@bootlin.com> wrote:

> Two helpers have been added to the core to do all kind of controller
> side configuration/initialization between the detection phase and the
> final NAND scan. Implement these hooks so that we can convert the driver
> to just use nand_scan() instead of the nand_scan_ident() +
> nand_scan_tail() pair.
> 
> Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>

Reviewed-by: Boris Brezillon <boris.brezillon@bootlin.com>

> ---
>  drivers/mtd/nand/raw/omap2.c | 533 +++++++++++++++++++++----------------------
>  1 file changed, 265 insertions(+), 268 deletions(-)
> 
> diff --git a/drivers/mtd/nand/raw/omap2.c b/drivers/mtd/nand/raw/omap2.c
> index e943b2e5a5e2..4546ac0bed4a 100644
> --- a/drivers/mtd/nand/raw/omap2.c
> +++ b/drivers/mtd/nand/raw/omap2.c
> @@ -144,12 +144,6 @@ static u_char bch8_vector[] = {0xf3, 0xdb, 0x14, 0x16, 0x8b, 0xd2, 0xbe, 0xcc,
>  	0xac, 0x6b, 0xff, 0x99, 0x7b};
>  static u_char bch4_vector[] = {0x00, 0x6b, 0x31, 0xdd, 0x41, 0xbc, 0x10};
>  
> -/* Shared among all NAND instances to synchronize access to the ECC Engine */
> -static struct nand_controller omap_gpmc_controller = {
> -	.lock = __SPIN_LOCK_UNLOCKED(omap_gpmc_controller.lock),
> -	.wq = __WAIT_QUEUE_HEAD_INITIALIZER(omap_gpmc_controller.wq),
> -};
> -
>  struct omap_nand_info {
>  	struct nand_chip		nand;
>  	struct platform_device		*pdev;
> @@ -1915,17 +1909,278 @@ static const struct mtd_ooblayout_ops omap_sw_ooblayout_ops = {
>  	.free = omap_sw_ooblayout_free,
>  };
>  
> +static int omap_nand_attach_chip(struct nand_chip *chip)
> +{
> +	struct mtd_info *mtd = nand_to_mtd(chip);
> +	struct omap_nand_info *info = mtd_to_omap(mtd);
> +	struct device *dev = &info->pdev->dev;
> +	int min_oobbytes = BADBLOCK_MARKER_LENGTH;
> +	int oobbytes_per_step;
> +	dma_cap_mask_t mask;
> +	int err;
> +
> +	if (chip->bbt_options & NAND_BBT_USE_FLASH)
> +		chip->bbt_options |= NAND_BBT_NO_OOB;
> +	else
> +		chip->options |= NAND_SKIP_BBTSCAN;
> +
> +	/* Re-populate low-level callbacks based on xfer modes */
> +	switch (info->xfer_type) {
> +	case NAND_OMAP_PREFETCH_POLLED:
> +		chip->read_buf = omap_read_buf_pref;
> +		chip->write_buf = omap_write_buf_pref;
> +		break;
> +
> +	case NAND_OMAP_POLLED:
> +		/* Use nand_base defaults for {read,write}_buf */
> +		break;
> +
> +	case NAND_OMAP_PREFETCH_DMA:
> +		dma_cap_zero(mask);
> +		dma_cap_set(DMA_SLAVE, mask);
> +		info->dma = dma_request_chan(dev, "rxtx");
> +
> +		if (IS_ERR(info->dma)) {
> +			dev_err(dev, "DMA engine request failed\n");
> +			return PTR_ERR(info->dma);
> +		} else {
> +			struct dma_slave_config cfg;
> +
> +			memset(&cfg, 0, sizeof(cfg));
> +			cfg.src_addr = info->phys_base;
> +			cfg.dst_addr = info->phys_base;
> +			cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
> +			cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
> +			cfg.src_maxburst = 16;
> +			cfg.dst_maxburst = 16;
> +			err = dmaengine_slave_config(info->dma, &cfg);
> +			if (err) {
> +				dev_err(dev,
> +					"DMA engine slave config failed: %d\n",
> +					err);
> +				return err;
> +			}
> +			chip->read_buf = omap_read_buf_dma_pref;
> +			chip->write_buf = omap_write_buf_dma_pref;
> +		}
> +		break;
> +
> +	case NAND_OMAP_PREFETCH_IRQ:
> +		info->gpmc_irq_fifo = platform_get_irq(info->pdev, 0);
> +		if (info->gpmc_irq_fifo <= 0) {
> +			dev_err(dev, "Error getting fifo IRQ\n");
> +			return -ENODEV;
> +		}
> +		err = devm_request_irq(dev, info->gpmc_irq_fifo,
> +				       omap_nand_irq, IRQF_SHARED,
> +				       "gpmc-nand-fifo", info);
> +		if (err) {
> +			dev_err(dev, "Requesting IRQ %d, error %d\n",
> +				info->gpmc_irq_fifo, err);
> +			info->gpmc_irq_fifo = 0;
> +			return err;
> +		}
> +
> +		info->gpmc_irq_count = platform_get_irq(info->pdev, 1);
> +		if (info->gpmc_irq_count <= 0) {
> +			dev_err(dev, "Error getting IRQ count\n");
> +			return -ENODEV;
> +		}
> +		err = devm_request_irq(dev, info->gpmc_irq_count,
> +				       omap_nand_irq, IRQF_SHARED,
> +				       "gpmc-nand-count", info);
> +		if (err) {
> +			dev_err(dev, "Requesting IRQ %d, error %d\n",
> +				info->gpmc_irq_count, err);
> +			info->gpmc_irq_count = 0;
> +			return err;
> +		}
> +
> +		chip->read_buf = omap_read_buf_irq_pref;
> +		chip->write_buf = omap_write_buf_irq_pref;
> +
> +		break;
> +
> +	default:
> +		dev_err(dev, "xfer_type %d not supported!\n", info->xfer_type);
> +		return -EINVAL;
> +	}
> +
> +	if (!omap2_nand_ecc_check(info))
> +		return -EINVAL;
> +
> +	/*
> +	 * Bail out earlier to let NAND_ECC_SOFT code create its own
> +	 * ooblayout instead of using ours.
> +	 */
> +	if (info->ecc_opt == OMAP_ECC_HAM1_CODE_SW) {
> +		chip->ecc.mode = NAND_ECC_SOFT;
> +		chip->ecc.algo = NAND_ECC_HAMMING;
> +		return 0;
> +	}
> +
> +	/* Populate MTD interface based on ECC scheme */
> +	switch (info->ecc_opt) {
> +	case OMAP_ECC_HAM1_CODE_HW:
> +		dev_info(dev, "nand: using OMAP_ECC_HAM1_CODE_HW\n");
> +		chip->ecc.mode		= NAND_ECC_HW;
> +		chip->ecc.bytes		= 3;
> +		chip->ecc.size		= 512;
> +		chip->ecc.strength	= 1;
> +		chip->ecc.calculate	= omap_calculate_ecc;
> +		chip->ecc.hwctl		= omap_enable_hwecc;
> +		chip->ecc.correct	= omap_correct_data;
> +		mtd_set_ooblayout(mtd, &omap_ooblayout_ops);
> +		oobbytes_per_step	= chip->ecc.bytes;
> +
> +		if (!(chip->options & NAND_BUSWIDTH_16))
> +			min_oobbytes	= 1;
> +
> +		break;
> +
> +	case OMAP_ECC_BCH4_CODE_HW_DETECTION_SW:
> +		pr_info("nand: using OMAP_ECC_BCH4_CODE_HW_DETECTION_SW\n");
> +		chip->ecc.mode		= NAND_ECC_HW;
> +		chip->ecc.size		= 512;
> +		chip->ecc.bytes		= 7;
> +		chip->ecc.strength	= 4;
> +		chip->ecc.hwctl		= omap_enable_hwecc_bch;
> +		chip->ecc.correct	= nand_bch_correct_data;
> +		chip->ecc.calculate	= omap_calculate_ecc_bch_sw;
> +		mtd_set_ooblayout(mtd, &omap_sw_ooblayout_ops);
> +		/* Reserve one byte for the OMAP marker */
> +		oobbytes_per_step	= chip->ecc.bytes + 1;
> +		/* Software BCH library is used for locating errors */
> +		chip->ecc.priv		= nand_bch_init(mtd);
> +		if (!chip->ecc.priv) {
> +			dev_err(dev, "Unable to use BCH library\n");
> +			return -EINVAL;
> +		}
> +		break;
> +
> +	case OMAP_ECC_BCH4_CODE_HW:
> +		pr_info("nand: using OMAP_ECC_BCH4_CODE_HW ECC scheme\n");
> +		chip->ecc.mode		= NAND_ECC_HW;
> +		chip->ecc.size		= 512;
> +		/* 14th bit is kept reserved for ROM-code compatibility */
> +		chip->ecc.bytes		= 7 + 1;
> +		chip->ecc.strength	= 4;
> +		chip->ecc.hwctl		= omap_enable_hwecc_bch;
> +		chip->ecc.correct	= omap_elm_correct_data;
> +		chip->ecc.read_page	= omap_read_page_bch;
> +		chip->ecc.write_page	= omap_write_page_bch;
> +		chip->ecc.write_subpage	= omap_write_subpage_bch;
> +		mtd_set_ooblayout(mtd, &omap_ooblayout_ops);
> +		oobbytes_per_step	= chip->ecc.bytes;
> +
> +		err = elm_config(info->elm_dev, BCH4_ECC,
> +				 mtd->writesize / chip->ecc.size,
> +				 chip->ecc.size, chip->ecc.bytes);
> +		if (err < 0)
> +			return err;
> +		break;
> +
> +	case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW:
> +		pr_info("nand: using OMAP_ECC_BCH8_CODE_HW_DETECTION_SW\n");
> +		chip->ecc.mode		= NAND_ECC_HW;
> +		chip->ecc.size		= 512;
> +		chip->ecc.bytes		= 13;
> +		chip->ecc.strength	= 8;
> +		chip->ecc.hwctl		= omap_enable_hwecc_bch;
> +		chip->ecc.correct	= nand_bch_correct_data;
> +		chip->ecc.calculate	= omap_calculate_ecc_bch_sw;
> +		mtd_set_ooblayout(mtd, &omap_sw_ooblayout_ops);
> +		/* Reserve one byte for the OMAP marker */
> +		oobbytes_per_step	= chip->ecc.bytes + 1;
> +		/* Software BCH library is used for locating errors */
> +		chip->ecc.priv		= nand_bch_init(mtd);
> +		if (!chip->ecc.priv) {
> +			dev_err(dev, "unable to use BCH library\n");
> +			return -EINVAL;
> +		}
> +		break;
> +
> +	case OMAP_ECC_BCH8_CODE_HW:
> +		pr_info("nand: using OMAP_ECC_BCH8_CODE_HW ECC scheme\n");
> +		chip->ecc.mode		= NAND_ECC_HW;
> +		chip->ecc.size		= 512;
> +		/* 14th bit is kept reserved for ROM-code compatibility */
> +		chip->ecc.bytes		= 13 + 1;
> +		chip->ecc.strength	= 8;
> +		chip->ecc.hwctl		= omap_enable_hwecc_bch;
> +		chip->ecc.correct	= omap_elm_correct_data;
> +		chip->ecc.read_page	= omap_read_page_bch;
> +		chip->ecc.write_page	= omap_write_page_bch;
> +		chip->ecc.write_subpage	= omap_write_subpage_bch;
> +		mtd_set_ooblayout(mtd, &omap_ooblayout_ops);
> +		oobbytes_per_step	= chip->ecc.bytes;
> +
> +		err = elm_config(info->elm_dev, BCH8_ECC,
> +				 mtd->writesize / chip->ecc.size,
> +				 chip->ecc.size, chip->ecc.bytes);
> +		if (err < 0)
> +			return err;
> +
> +		break;
> +
> +	case OMAP_ECC_BCH16_CODE_HW:
> +		pr_info("Using OMAP_ECC_BCH16_CODE_HW ECC scheme\n");
> +		chip->ecc.mode		= NAND_ECC_HW;
> +		chip->ecc.size		= 512;
> +		chip->ecc.bytes		= 26;
> +		chip->ecc.strength	= 16;
> +		chip->ecc.hwctl		= omap_enable_hwecc_bch;
> +		chip->ecc.correct	= omap_elm_correct_data;
> +		chip->ecc.read_page	= omap_read_page_bch;
> +		chip->ecc.write_page	= omap_write_page_bch;
> +		chip->ecc.write_subpage	= omap_write_subpage_bch;
> +		mtd_set_ooblayout(mtd, &omap_ooblayout_ops);
> +		oobbytes_per_step	= chip->ecc.bytes;
> +
> +		err = elm_config(info->elm_dev, BCH16_ECC,
> +				 mtd->writesize / chip->ecc.size,
> +				 chip->ecc.size, chip->ecc.bytes);
> +		if (err < 0)
> +			return err;
> +
> +		break;
> +	default:
> +		dev_err(dev, "Invalid or unsupported ECC scheme\n");
> +		return -EINVAL;
> +	}
> +
> +	/* Check if NAND device's OOB is enough to store ECC signatures */
> +	min_oobbytes += (oobbytes_per_step *
> +			 (mtd->writesize / chip->ecc.size));
> +	if (mtd->oobsize < min_oobbytes) {
> +		dev_err(dev,
> +			"Not enough OOB bytes: required = %d, available=%d\n",
> +			min_oobbytes, mtd->oobsize);
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +static const struct nand_controller_ops omap_nand_controller_ops = {
> +	.attach_chip = omap_nand_attach_chip,
> +};
> +
> +/* Shared among all NAND instances to synchronize access to the ECC Engine */
> +static struct nand_controller omap_gpmc_controller = {
> +	.lock = __SPIN_LOCK_UNLOCKED(omap_gpmc_controller.lock),
> +	.wq = __WAIT_QUEUE_HEAD_INITIALIZER(omap_gpmc_controller.wq),
> +	.ops = &omap_nand_controller_ops,
> +};
> +
>  static int omap_nand_probe(struct platform_device *pdev)
>  {
>  	struct omap_nand_info		*info;
>  	struct mtd_info			*mtd;
>  	struct nand_chip		*nand_chip;
>  	int				err;
> -	dma_cap_mask_t			mask;
>  	struct resource			*res;
>  	struct device			*dev = &pdev->dev;
> -	int				min_oobbytes = BADBLOCK_MARKER_LENGTH;
> -	int				oobbytes_per_step;
>  
>  	info = devm_kzalloc(&pdev->dev, sizeof(struct omap_nand_info),
>  				GFP_KERNEL);
> @@ -1998,266 +2253,8 @@ static int omap_nand_probe(struct platform_device *pdev)
>  
>  	/* scan NAND device connected to chip controller */
>  	nand_chip->options |= info->devsize & NAND_BUSWIDTH_16;
> -	err = nand_scan_ident(mtd, 1, NULL);
> -	if (err) {
> -		dev_err(&info->pdev->dev,
> -			"scan failed, may be bus-width mismatch\n");
> -		goto return_error;
> -	}
>  
> -	if (nand_chip->bbt_options & NAND_BBT_USE_FLASH)
> -		nand_chip->bbt_options |= NAND_BBT_NO_OOB;
> -	else
> -		nand_chip->options |= NAND_SKIP_BBTSCAN;
> -
> -	/* re-populate low-level callbacks based on xfer modes */
> -	switch (info->xfer_type) {
> -	case NAND_OMAP_PREFETCH_POLLED:
> -		nand_chip->read_buf   = omap_read_buf_pref;
> -		nand_chip->write_buf  = omap_write_buf_pref;
> -		break;
> -
> -	case NAND_OMAP_POLLED:
> -		/* Use nand_base defaults for {read,write}_buf */
> -		break;
> -
> -	case NAND_OMAP_PREFETCH_DMA:
> -		dma_cap_zero(mask);
> -		dma_cap_set(DMA_SLAVE, mask);
> -		info->dma = dma_request_chan(pdev->dev.parent, "rxtx");
> -
> -		if (IS_ERR(info->dma)) {
> -			dev_err(&pdev->dev, "DMA engine request failed\n");
> -			err = PTR_ERR(info->dma);
> -			goto return_error;
> -		} else {
> -			struct dma_slave_config cfg;
> -
> -			memset(&cfg, 0, sizeof(cfg));
> -			cfg.src_addr = info->phys_base;
> -			cfg.dst_addr = info->phys_base;
> -			cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
> -			cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
> -			cfg.src_maxburst = 16;
> -			cfg.dst_maxburst = 16;
> -			err = dmaengine_slave_config(info->dma, &cfg);
> -			if (err) {
> -				dev_err(&pdev->dev, "DMA engine slave config failed: %d\n",
> -					err);
> -				goto return_error;
> -			}
> -			nand_chip->read_buf   = omap_read_buf_dma_pref;
> -			nand_chip->write_buf  = omap_write_buf_dma_pref;
> -		}
> -		break;
> -
> -	case NAND_OMAP_PREFETCH_IRQ:
> -		info->gpmc_irq_fifo = platform_get_irq(pdev, 0);
> -		if (info->gpmc_irq_fifo <= 0) {
> -			dev_err(&pdev->dev, "error getting fifo irq\n");
> -			err = -ENODEV;
> -			goto return_error;
> -		}
> -		err = devm_request_irq(&pdev->dev, info->gpmc_irq_fifo,
> -					omap_nand_irq, IRQF_SHARED,
> -					"gpmc-nand-fifo", info);
> -		if (err) {
> -			dev_err(&pdev->dev, "requesting irq(%d) error:%d",
> -						info->gpmc_irq_fifo, err);
> -			info->gpmc_irq_fifo = 0;
> -			goto return_error;
> -		}
> -
> -		info->gpmc_irq_count = platform_get_irq(pdev, 1);
> -		if (info->gpmc_irq_count <= 0) {
> -			dev_err(&pdev->dev, "error getting count irq\n");
> -			err = -ENODEV;
> -			goto return_error;
> -		}
> -		err = devm_request_irq(&pdev->dev, info->gpmc_irq_count,
> -					omap_nand_irq, IRQF_SHARED,
> -					"gpmc-nand-count", info);
> -		if (err) {
> -			dev_err(&pdev->dev, "requesting irq(%d) error:%d",
> -						info->gpmc_irq_count, err);
> -			info->gpmc_irq_count = 0;
> -			goto return_error;
> -		}
> -
> -		nand_chip->read_buf  = omap_read_buf_irq_pref;
> -		nand_chip->write_buf = omap_write_buf_irq_pref;
> -
> -		break;
> -
> -	default:
> -		dev_err(&pdev->dev,
> -			"xfer_type(%d) not supported!\n", info->xfer_type);
> -		err = -EINVAL;
> -		goto return_error;
> -	}
> -
> -	if (!omap2_nand_ecc_check(info)) {
> -		err = -EINVAL;
> -		goto return_error;
> -	}
> -
> -	/*
> -	 * Bail out earlier to let NAND_ECC_SOFT code create its own
> -	 * ooblayout instead of using ours.
> -	 */
> -	if (info->ecc_opt == OMAP_ECC_HAM1_CODE_SW) {
> -		nand_chip->ecc.mode = NAND_ECC_SOFT;
> -		nand_chip->ecc.algo = NAND_ECC_HAMMING;
> -		goto scan_tail;
> -	}
> -
> -	/* populate MTD interface based on ECC scheme */
> -	switch (info->ecc_opt) {
> -	case OMAP_ECC_HAM1_CODE_HW:
> -		pr_info("nand: using OMAP_ECC_HAM1_CODE_HW\n");
> -		nand_chip->ecc.mode             = NAND_ECC_HW;
> -		nand_chip->ecc.bytes            = 3;
> -		nand_chip->ecc.size             = 512;
> -		nand_chip->ecc.strength         = 1;
> -		nand_chip->ecc.calculate        = omap_calculate_ecc;
> -		nand_chip->ecc.hwctl            = omap_enable_hwecc;
> -		nand_chip->ecc.correct          = omap_correct_data;
> -		mtd_set_ooblayout(mtd, &omap_ooblayout_ops);
> -		oobbytes_per_step		= nand_chip->ecc.bytes;
> -
> -		if (!(nand_chip->options & NAND_BUSWIDTH_16))
> -			min_oobbytes		= 1;
> -
> -		break;
> -
> -	case OMAP_ECC_BCH4_CODE_HW_DETECTION_SW:
> -		pr_info("nand: using OMAP_ECC_BCH4_CODE_HW_DETECTION_SW\n");
> -		nand_chip->ecc.mode		= NAND_ECC_HW;
> -		nand_chip->ecc.size		= 512;
> -		nand_chip->ecc.bytes		= 7;
> -		nand_chip->ecc.strength		= 4;
> -		nand_chip->ecc.hwctl		= omap_enable_hwecc_bch;
> -		nand_chip->ecc.correct		= nand_bch_correct_data;
> -		nand_chip->ecc.calculate	= omap_calculate_ecc_bch_sw;
> -		mtd_set_ooblayout(mtd, &omap_sw_ooblayout_ops);
> -		/* Reserve one byte for the OMAP marker */
> -		oobbytes_per_step		= nand_chip->ecc.bytes + 1;
> -		/* software bch library is used for locating errors */
> -		nand_chip->ecc.priv		= nand_bch_init(mtd);
> -		if (!nand_chip->ecc.priv) {
> -			dev_err(&info->pdev->dev, "unable to use BCH library\n");
> -			err = -EINVAL;
> -			goto return_error;
> -		}
> -		break;
> -
> -	case OMAP_ECC_BCH4_CODE_HW:
> -		pr_info("nand: using OMAP_ECC_BCH4_CODE_HW ECC scheme\n");
> -		nand_chip->ecc.mode		= NAND_ECC_HW;
> -		nand_chip->ecc.size		= 512;
> -		/* 14th bit is kept reserved for ROM-code compatibility */
> -		nand_chip->ecc.bytes		= 7 + 1;
> -		nand_chip->ecc.strength		= 4;
> -		nand_chip->ecc.hwctl		= omap_enable_hwecc_bch;
> -		nand_chip->ecc.correct		= omap_elm_correct_data;
> -		nand_chip->ecc.read_page	= omap_read_page_bch;
> -		nand_chip->ecc.write_page	= omap_write_page_bch;
> -		nand_chip->ecc.write_subpage	= omap_write_subpage_bch;
> -		mtd_set_ooblayout(mtd, &omap_ooblayout_ops);
> -		oobbytes_per_step		= nand_chip->ecc.bytes;
> -
> -		err = elm_config(info->elm_dev, BCH4_ECC,
> -				 mtd->writesize / nand_chip->ecc.size,
> -				 nand_chip->ecc.size, nand_chip->ecc.bytes);
> -		if (err < 0)
> -			goto return_error;
> -		break;
> -
> -	case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW:
> -		pr_info("nand: using OMAP_ECC_BCH8_CODE_HW_DETECTION_SW\n");
> -		nand_chip->ecc.mode		= NAND_ECC_HW;
> -		nand_chip->ecc.size		= 512;
> -		nand_chip->ecc.bytes		= 13;
> -		nand_chip->ecc.strength		= 8;
> -		nand_chip->ecc.hwctl		= omap_enable_hwecc_bch;
> -		nand_chip->ecc.correct		= nand_bch_correct_data;
> -		nand_chip->ecc.calculate	= omap_calculate_ecc_bch_sw;
> -		mtd_set_ooblayout(mtd, &omap_sw_ooblayout_ops);
> -		/* Reserve one byte for the OMAP marker */
> -		oobbytes_per_step		= nand_chip->ecc.bytes + 1;
> -		/* software bch library is used for locating errors */
> -		nand_chip->ecc.priv		= nand_bch_init(mtd);
> -		if (!nand_chip->ecc.priv) {
> -			dev_err(&info->pdev->dev, "unable to use BCH library\n");
> -			err = -EINVAL;
> -			goto return_error;
> -		}
> -		break;
> -
> -	case OMAP_ECC_BCH8_CODE_HW:
> -		pr_info("nand: using OMAP_ECC_BCH8_CODE_HW ECC scheme\n");
> -		nand_chip->ecc.mode		= NAND_ECC_HW;
> -		nand_chip->ecc.size		= 512;
> -		/* 14th bit is kept reserved for ROM-code compatibility */
> -		nand_chip->ecc.bytes		= 13 + 1;
> -		nand_chip->ecc.strength		= 8;
> -		nand_chip->ecc.hwctl		= omap_enable_hwecc_bch;
> -		nand_chip->ecc.correct		= omap_elm_correct_data;
> -		nand_chip->ecc.read_page	= omap_read_page_bch;
> -		nand_chip->ecc.write_page	= omap_write_page_bch;
> -		nand_chip->ecc.write_subpage	= omap_write_subpage_bch;
> -		mtd_set_ooblayout(mtd, &omap_ooblayout_ops);
> -		oobbytes_per_step		= nand_chip->ecc.bytes;
> -
> -		err = elm_config(info->elm_dev, BCH8_ECC,
> -				 mtd->writesize / nand_chip->ecc.size,
> -				 nand_chip->ecc.size, nand_chip->ecc.bytes);
> -		if (err < 0)
> -			goto return_error;
> -
> -		break;
> -
> -	case OMAP_ECC_BCH16_CODE_HW:
> -		pr_info("using OMAP_ECC_BCH16_CODE_HW ECC scheme\n");
> -		nand_chip->ecc.mode		= NAND_ECC_HW;
> -		nand_chip->ecc.size		= 512;
> -		nand_chip->ecc.bytes		= 26;
> -		nand_chip->ecc.strength		= 16;
> -		nand_chip->ecc.hwctl		= omap_enable_hwecc_bch;
> -		nand_chip->ecc.correct		= omap_elm_correct_data;
> -		nand_chip->ecc.read_page	= omap_read_page_bch;
> -		nand_chip->ecc.write_page	= omap_write_page_bch;
> -		nand_chip->ecc.write_subpage	= omap_write_subpage_bch;
> -		mtd_set_ooblayout(mtd, &omap_ooblayout_ops);
> -		oobbytes_per_step		= nand_chip->ecc.bytes;
> -
> -		err = elm_config(info->elm_dev, BCH16_ECC,
> -				 mtd->writesize / nand_chip->ecc.size,
> -				 nand_chip->ecc.size, nand_chip->ecc.bytes);
> -		if (err < 0)
> -			goto return_error;
> -
> -		break;
> -	default:
> -		dev_err(&info->pdev->dev, "invalid or unsupported ECC scheme\n");
> -		err = -EINVAL;
> -		goto return_error;
> -	}
> -
> -	/* check if NAND device's OOB is enough to store ECC signatures */
> -	min_oobbytes += (oobbytes_per_step *
> -			 (mtd->writesize / nand_chip->ecc.size));
> -	if (mtd->oobsize < min_oobbytes) {
> -		dev_err(&info->pdev->dev,
> -			"not enough OOB bytes required = %d, available=%d\n",
> -			min_oobbytes, mtd->oobsize);
> -		err = -EINVAL;
> -		goto return_error;
> -	}
> -
> -scan_tail:
> -	/* second phase scan */
> -	err = nand_scan_tail(mtd);
> +	err = nand_scan(mtd, 1);
>  	if (err)
>  		goto return_error;
>
Alexander Sverdlin Dec. 13, 2018, 6:01 p.m. UTC | #2
Hello Miquel, Boris,

On Wed, 25 Jul 2018 15:31:39 +0200
Miquel Raynal <miquel.raynal@bootlin.com> wrote:

> Two helpers have been added to the core to do all kind of controller
> side configuration/initialization between the detection phase and the
> final NAND scan. Implement these hooks so that we can convert the driver
> to just use nand_scan() instead of the nand_scan_ident() +
> nand_scan_tail() pair.
> 
> Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
> Reviewed-by: Boris Brezillon <boris.brezillon@bootlin.com>

I've bisected this patch to brake Beagle Bone Black boot
(from NAND, at least in DMA mode):

[    0.243337] edma 49000000.edma: TI EDMA DMA engine driver

... skipped...

[    1.888170] omap-gpmc 50000000.gpmc: GPMC revision 6.0
[    1.893597] gpmc_mem_init: disabling cs 0 mapped at 0x0-0x1000000
[    1.901776] nand: device found, Manufacturer ID: 0x2c, Chip ID: 0xda
[    1.908589] nand: Micron MT29F2G08ABAEAWP
[    1.912807] nand: 256 MiB, SLC, erase size: 128 KiB, page size: 2048, OOB size: 64
[    1.920802] omap2-nand 8000000.nand: DMA engine request failed

... unrelated...

[    1.985544] UBI error: cannot open mtd NAND.root-squashfs2, error -2
[    1.992432] UBI error: cannot open mtd NAND.root-ubifs, error -2
[    1.998897] UBI: block: can't open volume on ubi0_0, err=-19

... unrelated...

[    2.025751] VFS: Cannot open root device "ubiblock0_0" or unknown-block(0,0): error -6
[    2.034168] Please append a correct "root=" boot option; here are the available partitions:
[    2.042997] Kernel panic - not syncing: VFS: Unable to mount root fs on unknown-block(0,0)

EDMA driver is here, yet the channel cannot be obtained after
commit e1e6255c31.

> ---
>  drivers/mtd/nand/raw/omap2.c | 533 +++++++++++++++++++++----------------------
>  1 file changed, 265 insertions(+), 268 deletions(-)
> 
> diff --git a/drivers/mtd/nand/raw/omap2.c b/drivers/mtd/nand/raw/omap2.c
> index e943b2e5a5e2..4546ac0bed4a 100644
> --- a/drivers/mtd/nand/raw/omap2.c
> +++ b/drivers/mtd/nand/raw/omap2.c
> @@ -144,12 +144,6 @@ static u_char bch8_vector[] = {0xf3, 0xdb, 0x14, 0x16, 0x8b, 0xd2, 0xbe, 0xcc,
>  	0xac, 0x6b, 0xff, 0x99, 0x7b};
>  static u_char bch4_vector[] = {0x00, 0x6b, 0x31, 0xdd, 0x41, 0xbc, 0x10};
>  
> -/* Shared among all NAND instances to synchronize access to the ECC Engine */
> -static struct nand_controller omap_gpmc_controller = {
> -	.lock = __SPIN_LOCK_UNLOCKED(omap_gpmc_controller.lock),
> -	.wq = __WAIT_QUEUE_HEAD_INITIALIZER(omap_gpmc_controller.wq),
> -};
> -
>  struct omap_nand_info {
>  	struct nand_chip		nand;
>  	struct platform_device		*pdev;
> @@ -1915,17 +1909,278 @@ static const struct mtd_ooblayout_ops omap_sw_ooblayout_ops = {
>  	.free = omap_sw_ooblayout_free,
>  };
>  
> +static int omap_nand_attach_chip(struct nand_chip *chip)
> +{
> +	struct mtd_info *mtd = nand_to_mtd(chip);
> +	struct omap_nand_info *info = mtd_to_omap(mtd);
> +	struct device *dev = &info->pdev->dev;
> +	int min_oobbytes = BADBLOCK_MARKER_LENGTH;
> +	int oobbytes_per_step;
> +	dma_cap_mask_t mask;
> +	int err;
> +
> +	if (chip->bbt_options & NAND_BBT_USE_FLASH)
> +		chip->bbt_options |= NAND_BBT_NO_OOB;
> +	else
> +		chip->options |= NAND_SKIP_BBTSCAN;
> +
> +	/* Re-populate low-level callbacks based on xfer modes */
> +	switch (info->xfer_type) {
> +	case NAND_OMAP_PREFETCH_POLLED:
> +		chip->read_buf = omap_read_buf_pref;
> +		chip->write_buf = omap_write_buf_pref;
> +		break;
> +
> +	case NAND_OMAP_POLLED:
> +		/* Use nand_base defaults for {read,write}_buf */
> +		break;
> +
> +	case NAND_OMAP_PREFETCH_DMA:
> +		dma_cap_zero(mask);
> +		dma_cap_set(DMA_SLAVE, mask);
> +		info->dma = dma_request_chan(dev, "rxtx");
> +
> +		if (IS_ERR(info->dma)) {
> +			dev_err(dev, "DMA engine request failed\n");
> +			return PTR_ERR(info->dma);
> +		} else {
> +			struct dma_slave_config cfg;
> +
> +			memset(&cfg, 0, sizeof(cfg));
> +			cfg.src_addr = info->phys_base;
> +			cfg.dst_addr = info->phys_base;
> +			cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
> +			cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
> +			cfg.src_maxburst = 16;
> +			cfg.dst_maxburst = 16;
> +			err = dmaengine_slave_config(info->dma, &cfg);
> +			if (err) {
> +				dev_err(dev,
> +					"DMA engine slave config failed: %d\n",
> +					err);
> +				return err;
> +			}
> +			chip->read_buf = omap_read_buf_dma_pref;
> +			chip->write_buf = omap_write_buf_dma_pref;
> +		}
> +		break;
> +
> +	case NAND_OMAP_PREFETCH_IRQ:
> +		info->gpmc_irq_fifo = platform_get_irq(info->pdev, 0);
> +		if (info->gpmc_irq_fifo <= 0) {
> +			dev_err(dev, "Error getting fifo IRQ\n");
> +			return -ENODEV;
> +		}
> +		err = devm_request_irq(dev, info->gpmc_irq_fifo,
> +				       omap_nand_irq, IRQF_SHARED,
> +				       "gpmc-nand-fifo", info);
> +		if (err) {
> +			dev_err(dev, "Requesting IRQ %d, error %d\n",
> +				info->gpmc_irq_fifo, err);
> +			info->gpmc_irq_fifo = 0;
> +			return err;
> +		}
> +
> +		info->gpmc_irq_count = platform_get_irq(info->pdev, 1);
> +		if (info->gpmc_irq_count <= 0) {
> +			dev_err(dev, "Error getting IRQ count\n");
> +			return -ENODEV;
> +		}
> +		err = devm_request_irq(dev, info->gpmc_irq_count,
> +				       omap_nand_irq, IRQF_SHARED,
> +				       "gpmc-nand-count", info);
> +		if (err) {
> +			dev_err(dev, "Requesting IRQ %d, error %d\n",
> +				info->gpmc_irq_count, err);
> +			info->gpmc_irq_count = 0;
> +			return err;
> +		}
> +
> +		chip->read_buf = omap_read_buf_irq_pref;
> +		chip->write_buf = omap_write_buf_irq_pref;
> +
> +		break;
> +
> +	default:
> +		dev_err(dev, "xfer_type %d not supported!\n", info->xfer_type);
> +		return -EINVAL;
> +	}
> +
> +	if (!omap2_nand_ecc_check(info))
> +		return -EINVAL;
> +
> +	/*
> +	 * Bail out earlier to let NAND_ECC_SOFT code create its own
> +	 * ooblayout instead of using ours.
> +	 */
> +	if (info->ecc_opt == OMAP_ECC_HAM1_CODE_SW) {
> +		chip->ecc.mode = NAND_ECC_SOFT;
> +		chip->ecc.algo = NAND_ECC_HAMMING;
> +		return 0;
> +	}
> +
> +	/* Populate MTD interface based on ECC scheme */
> +	switch (info->ecc_opt) {
> +	case OMAP_ECC_HAM1_CODE_HW:
> +		dev_info(dev, "nand: using OMAP_ECC_HAM1_CODE_HW\n");
> +		chip->ecc.mode		= NAND_ECC_HW;
> +		chip->ecc.bytes		= 3;
> +		chip->ecc.size		= 512;
> +		chip->ecc.strength	= 1;
> +		chip->ecc.calculate	= omap_calculate_ecc;
> +		chip->ecc.hwctl		= omap_enable_hwecc;
> +		chip->ecc.correct	= omap_correct_data;
> +		mtd_set_ooblayout(mtd, &omap_ooblayout_ops);
> +		oobbytes_per_step	= chip->ecc.bytes;
> +
> +		if (!(chip->options & NAND_BUSWIDTH_16))
> +			min_oobbytes	= 1;
> +
> +		break;
> +
> +	case OMAP_ECC_BCH4_CODE_HW_DETECTION_SW:
> +		pr_info("nand: using OMAP_ECC_BCH4_CODE_HW_DETECTION_SW\n");
> +		chip->ecc.mode		= NAND_ECC_HW;
> +		chip->ecc.size		= 512;
> +		chip->ecc.bytes		= 7;
> +		chip->ecc.strength	= 4;
> +		chip->ecc.hwctl		= omap_enable_hwecc_bch;
> +		chip->ecc.correct	= nand_bch_correct_data;
> +		chip->ecc.calculate	= omap_calculate_ecc_bch_sw;
> +		mtd_set_ooblayout(mtd, &omap_sw_ooblayout_ops);
> +		/* Reserve one byte for the OMAP marker */
> +		oobbytes_per_step	= chip->ecc.bytes + 1;
> +		/* Software BCH library is used for locating errors */
> +		chip->ecc.priv		= nand_bch_init(mtd);
> +		if (!chip->ecc.priv) {
> +			dev_err(dev, "Unable to use BCH library\n");
> +			return -EINVAL;
> +		}
> +		break;
> +
> +	case OMAP_ECC_BCH4_CODE_HW:
> +		pr_info("nand: using OMAP_ECC_BCH4_CODE_HW ECC scheme\n");
> +		chip->ecc.mode		= NAND_ECC_HW;
> +		chip->ecc.size		= 512;
> +		/* 14th bit is kept reserved for ROM-code compatibility */
> +		chip->ecc.bytes		= 7 + 1;
> +		chip->ecc.strength	= 4;
> +		chip->ecc.hwctl		= omap_enable_hwecc_bch;
> +		chip->ecc.correct	= omap_elm_correct_data;
> +		chip->ecc.read_page	= omap_read_page_bch;
> +		chip->ecc.write_page	= omap_write_page_bch;
> +		chip->ecc.write_subpage	= omap_write_subpage_bch;
> +		mtd_set_ooblayout(mtd, &omap_ooblayout_ops);
> +		oobbytes_per_step	= chip->ecc.bytes;
> +
> +		err = elm_config(info->elm_dev, BCH4_ECC,
> +				 mtd->writesize / chip->ecc.size,
> +				 chip->ecc.size, chip->ecc.bytes);
> +		if (err < 0)
> +			return err;
> +		break;
> +
> +	case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW:
> +		pr_info("nand: using OMAP_ECC_BCH8_CODE_HW_DETECTION_SW\n");
> +		chip->ecc.mode		= NAND_ECC_HW;
> +		chip->ecc.size		= 512;
> +		chip->ecc.bytes		= 13;
> +		chip->ecc.strength	= 8;
> +		chip->ecc.hwctl		= omap_enable_hwecc_bch;
> +		chip->ecc.correct	= nand_bch_correct_data;
> +		chip->ecc.calculate	= omap_calculate_ecc_bch_sw;
> +		mtd_set_ooblayout(mtd, &omap_sw_ooblayout_ops);
> +		/* Reserve one byte for the OMAP marker */
> +		oobbytes_per_step	= chip->ecc.bytes + 1;
> +		/* Software BCH library is used for locating errors */
> +		chip->ecc.priv		= nand_bch_init(mtd);
> +		if (!chip->ecc.priv) {
> +			dev_err(dev, "unable to use BCH library\n");
> +			return -EINVAL;
> +		}
> +		break;
> +
> +	case OMAP_ECC_BCH8_CODE_HW:
> +		pr_info("nand: using OMAP_ECC_BCH8_CODE_HW ECC scheme\n");
> +		chip->ecc.mode		= NAND_ECC_HW;
> +		chip->ecc.size		= 512;
> +		/* 14th bit is kept reserved for ROM-code compatibility */
> +		chip->ecc.bytes		= 13 + 1;
> +		chip->ecc.strength	= 8;
> +		chip->ecc.hwctl		= omap_enable_hwecc_bch;
> +		chip->ecc.correct	= omap_elm_correct_data;
> +		chip->ecc.read_page	= omap_read_page_bch;
> +		chip->ecc.write_page	= omap_write_page_bch;
> +		chip->ecc.write_subpage	= omap_write_subpage_bch;
> +		mtd_set_ooblayout(mtd, &omap_ooblayout_ops);
> +		oobbytes_per_step	= chip->ecc.bytes;
> +
> +		err = elm_config(info->elm_dev, BCH8_ECC,
> +				 mtd->writesize / chip->ecc.size,
> +				 chip->ecc.size, chip->ecc.bytes);
> +		if (err < 0)
> +			return err;
> +
> +		break;
> +
> +	case OMAP_ECC_BCH16_CODE_HW:
> +		pr_info("Using OMAP_ECC_BCH16_CODE_HW ECC scheme\n");
> +		chip->ecc.mode		= NAND_ECC_HW;
> +		chip->ecc.size		= 512;
> +		chip->ecc.bytes		= 26;
> +		chip->ecc.strength	= 16;
> +		chip->ecc.hwctl		= omap_enable_hwecc_bch;
> +		chip->ecc.correct	= omap_elm_correct_data;
> +		chip->ecc.read_page	= omap_read_page_bch;
> +		chip->ecc.write_page	= omap_write_page_bch;
> +		chip->ecc.write_subpage	= omap_write_subpage_bch;
> +		mtd_set_ooblayout(mtd, &omap_ooblayout_ops);
> +		oobbytes_per_step	= chip->ecc.bytes;
> +
> +		err = elm_config(info->elm_dev, BCH16_ECC,
> +				 mtd->writesize / chip->ecc.size,
> +				 chip->ecc.size, chip->ecc.bytes);
> +		if (err < 0)
> +			return err;
> +
> +		break;
> +	default:
> +		dev_err(dev, "Invalid or unsupported ECC scheme\n");
> +		return -EINVAL;
> +	}
> +
> +	/* Check if NAND device's OOB is enough to store ECC signatures */
> +	min_oobbytes += (oobbytes_per_step *
> +			 (mtd->writesize / chip->ecc.size));
> +	if (mtd->oobsize < min_oobbytes) {
> +		dev_err(dev,
> +			"Not enough OOB bytes: required = %d, available=%d\n",
> +			min_oobbytes, mtd->oobsize);
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +static const struct nand_controller_ops omap_nand_controller_ops = {
> +	.attach_chip = omap_nand_attach_chip,
> +};
> +
> +/* Shared among all NAND instances to synchronize access to the ECC Engine */
> +static struct nand_controller omap_gpmc_controller = {
> +	.lock = __SPIN_LOCK_UNLOCKED(omap_gpmc_controller.lock),
> +	.wq = __WAIT_QUEUE_HEAD_INITIALIZER(omap_gpmc_controller.wq),
> +	.ops = &omap_nand_controller_ops,
> +};
> +
>  static int omap_nand_probe(struct platform_device *pdev)
>  {
>  	struct omap_nand_info		*info;
>  	struct mtd_info			*mtd;
>  	struct nand_chip		*nand_chip;
>  	int				err;
> -	dma_cap_mask_t			mask;
>  	struct resource			*res;
>  	struct device			*dev = &pdev->dev;
> -	int				min_oobbytes = BADBLOCK_MARKER_LENGTH;
> -	int				oobbytes_per_step;
>  
>  	info = devm_kzalloc(&pdev->dev, sizeof(struct omap_nand_info),
>  				GFP_KERNEL);
> @@ -1998,266 +2253,8 @@ static int omap_nand_probe(struct platform_device *pdev)
>  
>  	/* scan NAND device connected to chip controller */
>  	nand_chip->options |= info->devsize & NAND_BUSWIDTH_16;
> -	err = nand_scan_ident(mtd, 1, NULL);
> -	if (err) {
> -		dev_err(&info->pdev->dev,
> -			"scan failed, may be bus-width mismatch\n");
> -		goto return_error;
> -	}
>  
> -	if (nand_chip->bbt_options & NAND_BBT_USE_FLASH)
> -		nand_chip->bbt_options |= NAND_BBT_NO_OOB;
> -	else
> -		nand_chip->options |= NAND_SKIP_BBTSCAN;
> -
> -	/* re-populate low-level callbacks based on xfer modes */
> -	switch (info->xfer_type) {
> -	case NAND_OMAP_PREFETCH_POLLED:
> -		nand_chip->read_buf   = omap_read_buf_pref;
> -		nand_chip->write_buf  = omap_write_buf_pref;
> -		break;
> -
> -	case NAND_OMAP_POLLED:
> -		/* Use nand_base defaults for {read,write}_buf */
> -		break;
> -
> -	case NAND_OMAP_PREFETCH_DMA:
> -		dma_cap_zero(mask);
> -		dma_cap_set(DMA_SLAVE, mask);
> -		info->dma = dma_request_chan(pdev->dev.parent, "rxtx");
> -
> -		if (IS_ERR(info->dma)) {
> -			dev_err(&pdev->dev, "DMA engine request failed\n");
> -			err = PTR_ERR(info->dma);
> -			goto return_error;
> -		} else {
> -			struct dma_slave_config cfg;
> -
> -			memset(&cfg, 0, sizeof(cfg));
> -			cfg.src_addr = info->phys_base;
> -			cfg.dst_addr = info->phys_base;
> -			cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
> -			cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
> -			cfg.src_maxburst = 16;
> -			cfg.dst_maxburst = 16;
> -			err = dmaengine_slave_config(info->dma, &cfg);
> -			if (err) {
> -				dev_err(&pdev->dev, "DMA engine slave config failed: %d\n",
> -					err);
> -				goto return_error;
> -			}
> -			nand_chip->read_buf   = omap_read_buf_dma_pref;
> -			nand_chip->write_buf  = omap_write_buf_dma_pref;
> -		}
> -		break;
> -
> -	case NAND_OMAP_PREFETCH_IRQ:
> -		info->gpmc_irq_fifo = platform_get_irq(pdev, 0);
> -		if (info->gpmc_irq_fifo <= 0) {
> -			dev_err(&pdev->dev, "error getting fifo irq\n");
> -			err = -ENODEV;
> -			goto return_error;
> -		}
> -		err = devm_request_irq(&pdev->dev, info->gpmc_irq_fifo,
> -					omap_nand_irq, IRQF_SHARED,
> -					"gpmc-nand-fifo", info);
> -		if (err) {
> -			dev_err(&pdev->dev, "requesting irq(%d) error:%d",
> -						info->gpmc_irq_fifo, err);
> -			info->gpmc_irq_fifo = 0;
> -			goto return_error;
> -		}
> -
> -		info->gpmc_irq_count = platform_get_irq(pdev, 1);
> -		if (info->gpmc_irq_count <= 0) {
> -			dev_err(&pdev->dev, "error getting count irq\n");
> -			err = -ENODEV;
> -			goto return_error;
> -		}
> -		err = devm_request_irq(&pdev->dev, info->gpmc_irq_count,
> -					omap_nand_irq, IRQF_SHARED,
> -					"gpmc-nand-count", info);
> -		if (err) {
> -			dev_err(&pdev->dev, "requesting irq(%d) error:%d",
> -						info->gpmc_irq_count, err);
> -			info->gpmc_irq_count = 0;
> -			goto return_error;
> -		}
> -
> -		nand_chip->read_buf  = omap_read_buf_irq_pref;
> -		nand_chip->write_buf = omap_write_buf_irq_pref;
> -
> -		break;
> -
> -	default:
> -		dev_err(&pdev->dev,
> -			"xfer_type(%d) not supported!\n", info->xfer_type);
> -		err = -EINVAL;
> -		goto return_error;
> -	}
> -
> -	if (!omap2_nand_ecc_check(info)) {
> -		err = -EINVAL;
> -		goto return_error;
> -	}
> -
> -	/*
> -	 * Bail out earlier to let NAND_ECC_SOFT code create its own
> -	 * ooblayout instead of using ours.
> -	 */
> -	if (info->ecc_opt == OMAP_ECC_HAM1_CODE_SW) {
> -		nand_chip->ecc.mode = NAND_ECC_SOFT;
> -		nand_chip->ecc.algo = NAND_ECC_HAMMING;
> -		goto scan_tail;
> -	}
> -
> -	/* populate MTD interface based on ECC scheme */
> -	switch (info->ecc_opt) {
> -	case OMAP_ECC_HAM1_CODE_HW:
> -		pr_info("nand: using OMAP_ECC_HAM1_CODE_HW\n");
> -		nand_chip->ecc.mode             = NAND_ECC_HW;
> -		nand_chip->ecc.bytes            = 3;
> -		nand_chip->ecc.size             = 512;
> -		nand_chip->ecc.strength         = 1;
> -		nand_chip->ecc.calculate        = omap_calculate_ecc;
> -		nand_chip->ecc.hwctl            = omap_enable_hwecc;
> -		nand_chip->ecc.correct          = omap_correct_data;
> -		mtd_set_ooblayout(mtd, &omap_ooblayout_ops);
> -		oobbytes_per_step		= nand_chip->ecc.bytes;
> -
> -		if (!(nand_chip->options & NAND_BUSWIDTH_16))
> -			min_oobbytes		= 1;
> -
> -		break;
> -
> -	case OMAP_ECC_BCH4_CODE_HW_DETECTION_SW:
> -		pr_info("nand: using OMAP_ECC_BCH4_CODE_HW_DETECTION_SW\n");
> -		nand_chip->ecc.mode		= NAND_ECC_HW;
> -		nand_chip->ecc.size		= 512;
> -		nand_chip->ecc.bytes		= 7;
> -		nand_chip->ecc.strength		= 4;
> -		nand_chip->ecc.hwctl		= omap_enable_hwecc_bch;
> -		nand_chip->ecc.correct		= nand_bch_correct_data;
> -		nand_chip->ecc.calculate	= omap_calculate_ecc_bch_sw;
> -		mtd_set_ooblayout(mtd, &omap_sw_ooblayout_ops);
> -		/* Reserve one byte for the OMAP marker */
> -		oobbytes_per_step		= nand_chip->ecc.bytes + 1;
> -		/* software bch library is used for locating errors */
> -		nand_chip->ecc.priv		= nand_bch_init(mtd);
> -		if (!nand_chip->ecc.priv) {
> -			dev_err(&info->pdev->dev, "unable to use BCH library\n");
> -			err = -EINVAL;
> -			goto return_error;
> -		}
> -		break;
> -
> -	case OMAP_ECC_BCH4_CODE_HW:
> -		pr_info("nand: using OMAP_ECC_BCH4_CODE_HW ECC scheme\n");
> -		nand_chip->ecc.mode		= NAND_ECC_HW;
> -		nand_chip->ecc.size		= 512;
> -		/* 14th bit is kept reserved for ROM-code compatibility */
> -		nand_chip->ecc.bytes		= 7 + 1;
> -		nand_chip->ecc.strength		= 4;
> -		nand_chip->ecc.hwctl		= omap_enable_hwecc_bch;
> -		nand_chip->ecc.correct		= omap_elm_correct_data;
> -		nand_chip->ecc.read_page	= omap_read_page_bch;
> -		nand_chip->ecc.write_page	= omap_write_page_bch;
> -		nand_chip->ecc.write_subpage	= omap_write_subpage_bch;
> -		mtd_set_ooblayout(mtd, &omap_ooblayout_ops);
> -		oobbytes_per_step		= nand_chip->ecc.bytes;
> -
> -		err = elm_config(info->elm_dev, BCH4_ECC,
> -				 mtd->writesize / nand_chip->ecc.size,
> -				 nand_chip->ecc.size, nand_chip->ecc.bytes);
> -		if (err < 0)
> -			goto return_error;
> -		break;
> -
> -	case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW:
> -		pr_info("nand: using OMAP_ECC_BCH8_CODE_HW_DETECTION_SW\n");
> -		nand_chip->ecc.mode		= NAND_ECC_HW;
> -		nand_chip->ecc.size		= 512;
> -		nand_chip->ecc.bytes		= 13;
> -		nand_chip->ecc.strength		= 8;
> -		nand_chip->ecc.hwctl		= omap_enable_hwecc_bch;
> -		nand_chip->ecc.correct		= nand_bch_correct_data;
> -		nand_chip->ecc.calculate	= omap_calculate_ecc_bch_sw;
> -		mtd_set_ooblayout(mtd, &omap_sw_ooblayout_ops);
> -		/* Reserve one byte for the OMAP marker */
> -		oobbytes_per_step		= nand_chip->ecc.bytes + 1;
> -		/* software bch library is used for locating errors */
> -		nand_chip->ecc.priv		= nand_bch_init(mtd);
> -		if (!nand_chip->ecc.priv) {
> -			dev_err(&info->pdev->dev, "unable to use BCH library\n");
> -			err = -EINVAL;
> -			goto return_error;
> -		}
> -		break;
> -
> -	case OMAP_ECC_BCH8_CODE_HW:
> -		pr_info("nand: using OMAP_ECC_BCH8_CODE_HW ECC scheme\n");
> -		nand_chip->ecc.mode		= NAND_ECC_HW;
> -		nand_chip->ecc.size		= 512;
> -		/* 14th bit is kept reserved for ROM-code compatibility */
> -		nand_chip->ecc.bytes		= 13 + 1;
> -		nand_chip->ecc.strength		= 8;
> -		nand_chip->ecc.hwctl		= omap_enable_hwecc_bch;
> -		nand_chip->ecc.correct		= omap_elm_correct_data;
> -		nand_chip->ecc.read_page	= omap_read_page_bch;
> -		nand_chip->ecc.write_page	= omap_write_page_bch;
> -		nand_chip->ecc.write_subpage	= omap_write_subpage_bch;
> -		mtd_set_ooblayout(mtd, &omap_ooblayout_ops);
> -		oobbytes_per_step		= nand_chip->ecc.bytes;
> -
> -		err = elm_config(info->elm_dev, BCH8_ECC,
> -				 mtd->writesize / nand_chip->ecc.size,
> -				 nand_chip->ecc.size, nand_chip->ecc.bytes);
> -		if (err < 0)
> -			goto return_error;
> -
> -		break;
> -
> -	case OMAP_ECC_BCH16_CODE_HW:
> -		pr_info("using OMAP_ECC_BCH16_CODE_HW ECC scheme\n");
> -		nand_chip->ecc.mode		= NAND_ECC_HW;
> -		nand_chip->ecc.size		= 512;
> -		nand_chip->ecc.bytes		= 26;
> -		nand_chip->ecc.strength		= 16;
> -		nand_chip->ecc.hwctl		= omap_enable_hwecc_bch;
> -		nand_chip->ecc.correct		= omap_elm_correct_data;
> -		nand_chip->ecc.read_page	= omap_read_page_bch;
> -		nand_chip->ecc.write_page	= omap_write_page_bch;
> -		nand_chip->ecc.write_subpage	= omap_write_subpage_bch;
> -		mtd_set_ooblayout(mtd, &omap_ooblayout_ops);
> -		oobbytes_per_step		= nand_chip->ecc.bytes;
> -
> -		err = elm_config(info->elm_dev, BCH16_ECC,
> -				 mtd->writesize / nand_chip->ecc.size,
> -				 nand_chip->ecc.size, nand_chip->ecc.bytes);
> -		if (err < 0)
> -			goto return_error;
> -
> -		break;
> -	default:
> -		dev_err(&info->pdev->dev, "invalid or unsupported ECC scheme\n");
> -		err = -EINVAL;
> -		goto return_error;
> -	}
> -
> -	/* check if NAND device's OOB is enough to store ECC signatures */
> -	min_oobbytes += (oobbytes_per_step *
> -			 (mtd->writesize / nand_chip->ecc.size));
> -	if (mtd->oobsize < min_oobbytes) {
> -		dev_err(&info->pdev->dev,
> -			"not enough OOB bytes required = %d, available=%d\n",
> -			min_oobbytes, mtd->oobsize);
> -		err = -EINVAL;
> -		goto return_error;
> -	}
> -
> -scan_tail:
> -	/* second phase scan */
> -	err = nand_scan_tail(mtd);
> +	err = nand_scan(mtd, 1);
>  	if (err)
>  		goto return_error;
>
Boris Brezillon Dec. 13, 2018, 6:30 p.m. UTC | #3
On Thu, 13 Dec 2018 19:01:11 +0100
Alexander Sverdlin <alexander.sverdlin@gmail.com> wrote:

> Hello Miquel, Boris,
> 
> On Wed, 25 Jul 2018 15:31:39 +0200
> Miquel Raynal <miquel.raynal@bootlin.com> wrote:
> 
> > Two helpers have been added to the core to do all kind of controller
> > side configuration/initialization between the detection phase and the
> > final NAND scan. Implement these hooks so that we can convert the driver
> > to just use nand_scan() instead of the nand_scan_ident() +
> > nand_scan_tail() pair.
> > 
> > Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
> > Reviewed-by: Boris Brezillon <boris.brezillon@bootlin.com>  
> 
> I've bisected this patch to brake Beagle Bone Black boot
> (from NAND, at least in DMA mode):
> 
> [    0.243337] edma 49000000.edma: TI EDMA DMA engine driver
> 
> ... skipped...
> 
> [    1.888170] omap-gpmc 50000000.gpmc: GPMC revision 6.0
> [    1.893597] gpmc_mem_init: disabling cs 0 mapped at 0x0-0x1000000
> [    1.901776] nand: device found, Manufacturer ID: 0x2c, Chip ID: 0xda
> [    1.908589] nand: Micron MT29F2G08ABAEAWP
> [    1.912807] nand: 256 MiB, SLC, erase size: 128 KiB, page size: 2048, OOB size: 64
> [    1.920802] omap2-nand 8000000.nand: DMA engine request failed
> 
> ... unrelated...
> 
> [    1.985544] UBI error: cannot open mtd NAND.root-squashfs2, error -2
> [    1.992432] UBI error: cannot open mtd NAND.root-ubifs, error -2
> [    1.998897] UBI: block: can't open volume on ubi0_0, err=-19
> 
> ... unrelated...
> 
> [    2.025751] VFS: Cannot open root device "ubiblock0_0" or unknown-block(0,0): error -6
> [    2.034168] Please append a correct "root=" boot option; here are the available partitions:
> [    2.042997] Kernel panic - not syncing: VFS: Unable to mount root fs on unknown-block(0,0)
> 
> EDMA driver is here, yet the channel cannot be obtained after
> commit e1e6255c31.

Can you provide the full logs before and after this commit?
Boris Brezillon Dec. 13, 2018, 6:37 p.m. UTC | #4
On Thu, 13 Dec 2018 19:01:11 +0100
Alexander Sverdlin <alexander.sverdlin@gmail.com> wrote:

> > +	/* Re-populate low-level callbacks based on xfer modes */
> > +	switch (info->xfer_type) {
> > +	case NAND_OMAP_PREFETCH_POLLED:
> > +		chip->read_buf = omap_read_buf_pref;
> > +		chip->write_buf = omap_write_buf_pref;
> > +		break;
> > +
> > +	case NAND_OMAP_POLLED:
> > +		/* Use nand_base defaults for {read,write}_buf */
> > +		break;
> > +
> > +	case NAND_OMAP_PREFETCH_DMA:
> > +		dma_cap_zero(mask);
> > +		dma_cap_set(DMA_SLAVE, mask);
> > +		info->dma = dma_request_chan(dev, "rxtx");
> > +
> > +		if (IS_ERR(info->dma)) {
> > +			dev_err(dev, "DMA engine request failed\n");

Can you print the error code here?
Boris Brezillon Dec. 13, 2018, 7:06 p.m. UTC | #5
On Thu, 13 Dec 2018 19:01:11 +0100
Alexander Sverdlin <alexander.sverdlin@gmail.com> wrote:

> > +static int omap_nand_attach_chip(struct nand_chip *chip)
> > +{
> > +	struct mtd_info *mtd = nand_to_mtd(chip);
> > +	struct omap_nand_info *info = mtd_to_omap(mtd);
> > +	struct device *dev = &info->pdev->dev;
> > +	int min_oobbytes = BADBLOCK_MARKER_LENGTH;
> > +	int oobbytes_per_step;
> > +	dma_cap_mask_t mask;
> > +	int err;
> > +
> > +	if (chip->bbt_options & NAND_BBT_USE_FLASH)
> > +		chip->bbt_options |= NAND_BBT_NO_OOB;
> > +	else
> > +		chip->options |= NAND_SKIP_BBTSCAN;
> > +
> > +	/* Re-populate low-level callbacks based on xfer modes */
> > +	switch (info->xfer_type) {
> > +	case NAND_OMAP_PREFETCH_POLLED:
> > +		chip->read_buf = omap_read_buf_pref;
> > +		chip->write_buf = omap_write_buf_pref;
> > +		break;
> > +
> > +	case NAND_OMAP_POLLED:
> > +		/* Use nand_base defaults for {read,write}_buf */
> > +		break;
> > +
> > +	case NAND_OMAP_PREFETCH_DMA:
> > +		dma_cap_zero(mask);
> > +		dma_cap_set(DMA_SLAVE, mask);
> > +		info->dma = dma_request_chan(dev, "rxtx");
> > +

Can you try with

		info->dma = dma_request_chan(dev->parent, "rxtx");

?
diff mbox series

Patch

diff --git a/drivers/mtd/nand/raw/omap2.c b/drivers/mtd/nand/raw/omap2.c
index e943b2e5a5e2..4546ac0bed4a 100644
--- a/drivers/mtd/nand/raw/omap2.c
+++ b/drivers/mtd/nand/raw/omap2.c
@@ -144,12 +144,6 @@  static u_char bch8_vector[] = {0xf3, 0xdb, 0x14, 0x16, 0x8b, 0xd2, 0xbe, 0xcc,
 	0xac, 0x6b, 0xff, 0x99, 0x7b};
 static u_char bch4_vector[] = {0x00, 0x6b, 0x31, 0xdd, 0x41, 0xbc, 0x10};
 
-/* Shared among all NAND instances to synchronize access to the ECC Engine */
-static struct nand_controller omap_gpmc_controller = {
-	.lock = __SPIN_LOCK_UNLOCKED(omap_gpmc_controller.lock),
-	.wq = __WAIT_QUEUE_HEAD_INITIALIZER(omap_gpmc_controller.wq),
-};
-
 struct omap_nand_info {
 	struct nand_chip		nand;
 	struct platform_device		*pdev;
@@ -1915,17 +1909,278 @@  static const struct mtd_ooblayout_ops omap_sw_ooblayout_ops = {
 	.free = omap_sw_ooblayout_free,
 };
 
+static int omap_nand_attach_chip(struct nand_chip *chip)
+{
+	struct mtd_info *mtd = nand_to_mtd(chip);
+	struct omap_nand_info *info = mtd_to_omap(mtd);
+	struct device *dev = &info->pdev->dev;
+	int min_oobbytes = BADBLOCK_MARKER_LENGTH;
+	int oobbytes_per_step;
+	dma_cap_mask_t mask;
+	int err;
+
+	if (chip->bbt_options & NAND_BBT_USE_FLASH)
+		chip->bbt_options |= NAND_BBT_NO_OOB;
+	else
+		chip->options |= NAND_SKIP_BBTSCAN;
+
+	/* Re-populate low-level callbacks based on xfer modes */
+	switch (info->xfer_type) {
+	case NAND_OMAP_PREFETCH_POLLED:
+		chip->read_buf = omap_read_buf_pref;
+		chip->write_buf = omap_write_buf_pref;
+		break;
+
+	case NAND_OMAP_POLLED:
+		/* Use nand_base defaults for {read,write}_buf */
+		break;
+
+	case NAND_OMAP_PREFETCH_DMA:
+		dma_cap_zero(mask);
+		dma_cap_set(DMA_SLAVE, mask);
+		info->dma = dma_request_chan(dev, "rxtx");
+
+		if (IS_ERR(info->dma)) {
+			dev_err(dev, "DMA engine request failed\n");
+			return PTR_ERR(info->dma);
+		} else {
+			struct dma_slave_config cfg;
+
+			memset(&cfg, 0, sizeof(cfg));
+			cfg.src_addr = info->phys_base;
+			cfg.dst_addr = info->phys_base;
+			cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+			cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+			cfg.src_maxburst = 16;
+			cfg.dst_maxburst = 16;
+			err = dmaengine_slave_config(info->dma, &cfg);
+			if (err) {
+				dev_err(dev,
+					"DMA engine slave config failed: %d\n",
+					err);
+				return err;
+			}
+			chip->read_buf = omap_read_buf_dma_pref;
+			chip->write_buf = omap_write_buf_dma_pref;
+		}
+		break;
+
+	case NAND_OMAP_PREFETCH_IRQ:
+		info->gpmc_irq_fifo = platform_get_irq(info->pdev, 0);
+		if (info->gpmc_irq_fifo <= 0) {
+			dev_err(dev, "Error getting fifo IRQ\n");
+			return -ENODEV;
+		}
+		err = devm_request_irq(dev, info->gpmc_irq_fifo,
+				       omap_nand_irq, IRQF_SHARED,
+				       "gpmc-nand-fifo", info);
+		if (err) {
+			dev_err(dev, "Requesting IRQ %d, error %d\n",
+				info->gpmc_irq_fifo, err);
+			info->gpmc_irq_fifo = 0;
+			return err;
+		}
+
+		info->gpmc_irq_count = platform_get_irq(info->pdev, 1);
+		if (info->gpmc_irq_count <= 0) {
+			dev_err(dev, "Error getting IRQ count\n");
+			return -ENODEV;
+		}
+		err = devm_request_irq(dev, info->gpmc_irq_count,
+				       omap_nand_irq, IRQF_SHARED,
+				       "gpmc-nand-count", info);
+		if (err) {
+			dev_err(dev, "Requesting IRQ %d, error %d\n",
+				info->gpmc_irq_count, err);
+			info->gpmc_irq_count = 0;
+			return err;
+		}
+
+		chip->read_buf = omap_read_buf_irq_pref;
+		chip->write_buf = omap_write_buf_irq_pref;
+
+		break;
+
+	default:
+		dev_err(dev, "xfer_type %d not supported!\n", info->xfer_type);
+		return -EINVAL;
+	}
+
+	if (!omap2_nand_ecc_check(info))
+		return -EINVAL;
+
+	/*
+	 * Bail out earlier to let NAND_ECC_SOFT code create its own
+	 * ooblayout instead of using ours.
+	 */
+	if (info->ecc_opt == OMAP_ECC_HAM1_CODE_SW) {
+		chip->ecc.mode = NAND_ECC_SOFT;
+		chip->ecc.algo = NAND_ECC_HAMMING;
+		return 0;
+	}
+
+	/* Populate MTD interface based on ECC scheme */
+	switch (info->ecc_opt) {
+	case OMAP_ECC_HAM1_CODE_HW:
+		dev_info(dev, "nand: using OMAP_ECC_HAM1_CODE_HW\n");
+		chip->ecc.mode		= NAND_ECC_HW;
+		chip->ecc.bytes		= 3;
+		chip->ecc.size		= 512;
+		chip->ecc.strength	= 1;
+		chip->ecc.calculate	= omap_calculate_ecc;
+		chip->ecc.hwctl		= omap_enable_hwecc;
+		chip->ecc.correct	= omap_correct_data;
+		mtd_set_ooblayout(mtd, &omap_ooblayout_ops);
+		oobbytes_per_step	= chip->ecc.bytes;
+
+		if (!(chip->options & NAND_BUSWIDTH_16))
+			min_oobbytes	= 1;
+
+		break;
+
+	case OMAP_ECC_BCH4_CODE_HW_DETECTION_SW:
+		pr_info("nand: using OMAP_ECC_BCH4_CODE_HW_DETECTION_SW\n");
+		chip->ecc.mode		= NAND_ECC_HW;
+		chip->ecc.size		= 512;
+		chip->ecc.bytes		= 7;
+		chip->ecc.strength	= 4;
+		chip->ecc.hwctl		= omap_enable_hwecc_bch;
+		chip->ecc.correct	= nand_bch_correct_data;
+		chip->ecc.calculate	= omap_calculate_ecc_bch_sw;
+		mtd_set_ooblayout(mtd, &omap_sw_ooblayout_ops);
+		/* Reserve one byte for the OMAP marker */
+		oobbytes_per_step	= chip->ecc.bytes + 1;
+		/* Software BCH library is used for locating errors */
+		chip->ecc.priv		= nand_bch_init(mtd);
+		if (!chip->ecc.priv) {
+			dev_err(dev, "Unable to use BCH library\n");
+			return -EINVAL;
+		}
+		break;
+
+	case OMAP_ECC_BCH4_CODE_HW:
+		pr_info("nand: using OMAP_ECC_BCH4_CODE_HW ECC scheme\n");
+		chip->ecc.mode		= NAND_ECC_HW;
+		chip->ecc.size		= 512;
+		/* 14th bit is kept reserved for ROM-code compatibility */
+		chip->ecc.bytes		= 7 + 1;
+		chip->ecc.strength	= 4;
+		chip->ecc.hwctl		= omap_enable_hwecc_bch;
+		chip->ecc.correct	= omap_elm_correct_data;
+		chip->ecc.read_page	= omap_read_page_bch;
+		chip->ecc.write_page	= omap_write_page_bch;
+		chip->ecc.write_subpage	= omap_write_subpage_bch;
+		mtd_set_ooblayout(mtd, &omap_ooblayout_ops);
+		oobbytes_per_step	= chip->ecc.bytes;
+
+		err = elm_config(info->elm_dev, BCH4_ECC,
+				 mtd->writesize / chip->ecc.size,
+				 chip->ecc.size, chip->ecc.bytes);
+		if (err < 0)
+			return err;
+		break;
+
+	case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW:
+		pr_info("nand: using OMAP_ECC_BCH8_CODE_HW_DETECTION_SW\n");
+		chip->ecc.mode		= NAND_ECC_HW;
+		chip->ecc.size		= 512;
+		chip->ecc.bytes		= 13;
+		chip->ecc.strength	= 8;
+		chip->ecc.hwctl		= omap_enable_hwecc_bch;
+		chip->ecc.correct	= nand_bch_correct_data;
+		chip->ecc.calculate	= omap_calculate_ecc_bch_sw;
+		mtd_set_ooblayout(mtd, &omap_sw_ooblayout_ops);
+		/* Reserve one byte for the OMAP marker */
+		oobbytes_per_step	= chip->ecc.bytes + 1;
+		/* Software BCH library is used for locating errors */
+		chip->ecc.priv		= nand_bch_init(mtd);
+		if (!chip->ecc.priv) {
+			dev_err(dev, "unable to use BCH library\n");
+			return -EINVAL;
+		}
+		break;
+
+	case OMAP_ECC_BCH8_CODE_HW:
+		pr_info("nand: using OMAP_ECC_BCH8_CODE_HW ECC scheme\n");
+		chip->ecc.mode		= NAND_ECC_HW;
+		chip->ecc.size		= 512;
+		/* 14th bit is kept reserved for ROM-code compatibility */
+		chip->ecc.bytes		= 13 + 1;
+		chip->ecc.strength	= 8;
+		chip->ecc.hwctl		= omap_enable_hwecc_bch;
+		chip->ecc.correct	= omap_elm_correct_data;
+		chip->ecc.read_page	= omap_read_page_bch;
+		chip->ecc.write_page	= omap_write_page_bch;
+		chip->ecc.write_subpage	= omap_write_subpage_bch;
+		mtd_set_ooblayout(mtd, &omap_ooblayout_ops);
+		oobbytes_per_step	= chip->ecc.bytes;
+
+		err = elm_config(info->elm_dev, BCH8_ECC,
+				 mtd->writesize / chip->ecc.size,
+				 chip->ecc.size, chip->ecc.bytes);
+		if (err < 0)
+			return err;
+
+		break;
+
+	case OMAP_ECC_BCH16_CODE_HW:
+		pr_info("Using OMAP_ECC_BCH16_CODE_HW ECC scheme\n");
+		chip->ecc.mode		= NAND_ECC_HW;
+		chip->ecc.size		= 512;
+		chip->ecc.bytes		= 26;
+		chip->ecc.strength	= 16;
+		chip->ecc.hwctl		= omap_enable_hwecc_bch;
+		chip->ecc.correct	= omap_elm_correct_data;
+		chip->ecc.read_page	= omap_read_page_bch;
+		chip->ecc.write_page	= omap_write_page_bch;
+		chip->ecc.write_subpage	= omap_write_subpage_bch;
+		mtd_set_ooblayout(mtd, &omap_ooblayout_ops);
+		oobbytes_per_step	= chip->ecc.bytes;
+
+		err = elm_config(info->elm_dev, BCH16_ECC,
+				 mtd->writesize / chip->ecc.size,
+				 chip->ecc.size, chip->ecc.bytes);
+		if (err < 0)
+			return err;
+
+		break;
+	default:
+		dev_err(dev, "Invalid or unsupported ECC scheme\n");
+		return -EINVAL;
+	}
+
+	/* Check if NAND device's OOB is enough to store ECC signatures */
+	min_oobbytes += (oobbytes_per_step *
+			 (mtd->writesize / chip->ecc.size));
+	if (mtd->oobsize < min_oobbytes) {
+		dev_err(dev,
+			"Not enough OOB bytes: required = %d, available=%d\n",
+			min_oobbytes, mtd->oobsize);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static const struct nand_controller_ops omap_nand_controller_ops = {
+	.attach_chip = omap_nand_attach_chip,
+};
+
+/* Shared among all NAND instances to synchronize access to the ECC Engine */
+static struct nand_controller omap_gpmc_controller = {
+	.lock = __SPIN_LOCK_UNLOCKED(omap_gpmc_controller.lock),
+	.wq = __WAIT_QUEUE_HEAD_INITIALIZER(omap_gpmc_controller.wq),
+	.ops = &omap_nand_controller_ops,
+};
+
 static int omap_nand_probe(struct platform_device *pdev)
 {
 	struct omap_nand_info		*info;
 	struct mtd_info			*mtd;
 	struct nand_chip		*nand_chip;
 	int				err;
-	dma_cap_mask_t			mask;
 	struct resource			*res;
 	struct device			*dev = &pdev->dev;
-	int				min_oobbytes = BADBLOCK_MARKER_LENGTH;
-	int				oobbytes_per_step;
 
 	info = devm_kzalloc(&pdev->dev, sizeof(struct omap_nand_info),
 				GFP_KERNEL);
@@ -1998,266 +2253,8 @@  static int omap_nand_probe(struct platform_device *pdev)
 
 	/* scan NAND device connected to chip controller */
 	nand_chip->options |= info->devsize & NAND_BUSWIDTH_16;
-	err = nand_scan_ident(mtd, 1, NULL);
-	if (err) {
-		dev_err(&info->pdev->dev,
-			"scan failed, may be bus-width mismatch\n");
-		goto return_error;
-	}
 
-	if (nand_chip->bbt_options & NAND_BBT_USE_FLASH)
-		nand_chip->bbt_options |= NAND_BBT_NO_OOB;
-	else
-		nand_chip->options |= NAND_SKIP_BBTSCAN;
-
-	/* re-populate low-level callbacks based on xfer modes */
-	switch (info->xfer_type) {
-	case NAND_OMAP_PREFETCH_POLLED:
-		nand_chip->read_buf   = omap_read_buf_pref;
-		nand_chip->write_buf  = omap_write_buf_pref;
-		break;
-
-	case NAND_OMAP_POLLED:
-		/* Use nand_base defaults for {read,write}_buf */
-		break;
-
-	case NAND_OMAP_PREFETCH_DMA:
-		dma_cap_zero(mask);
-		dma_cap_set(DMA_SLAVE, mask);
-		info->dma = dma_request_chan(pdev->dev.parent, "rxtx");
-
-		if (IS_ERR(info->dma)) {
-			dev_err(&pdev->dev, "DMA engine request failed\n");
-			err = PTR_ERR(info->dma);
-			goto return_error;
-		} else {
-			struct dma_slave_config cfg;
-
-			memset(&cfg, 0, sizeof(cfg));
-			cfg.src_addr = info->phys_base;
-			cfg.dst_addr = info->phys_base;
-			cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
-			cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
-			cfg.src_maxburst = 16;
-			cfg.dst_maxburst = 16;
-			err = dmaengine_slave_config(info->dma, &cfg);
-			if (err) {
-				dev_err(&pdev->dev, "DMA engine slave config failed: %d\n",
-					err);
-				goto return_error;
-			}
-			nand_chip->read_buf   = omap_read_buf_dma_pref;
-			nand_chip->write_buf  = omap_write_buf_dma_pref;
-		}
-		break;
-
-	case NAND_OMAP_PREFETCH_IRQ:
-		info->gpmc_irq_fifo = platform_get_irq(pdev, 0);
-		if (info->gpmc_irq_fifo <= 0) {
-			dev_err(&pdev->dev, "error getting fifo irq\n");
-			err = -ENODEV;
-			goto return_error;
-		}
-		err = devm_request_irq(&pdev->dev, info->gpmc_irq_fifo,
-					omap_nand_irq, IRQF_SHARED,
-					"gpmc-nand-fifo", info);
-		if (err) {
-			dev_err(&pdev->dev, "requesting irq(%d) error:%d",
-						info->gpmc_irq_fifo, err);
-			info->gpmc_irq_fifo = 0;
-			goto return_error;
-		}
-
-		info->gpmc_irq_count = platform_get_irq(pdev, 1);
-		if (info->gpmc_irq_count <= 0) {
-			dev_err(&pdev->dev, "error getting count irq\n");
-			err = -ENODEV;
-			goto return_error;
-		}
-		err = devm_request_irq(&pdev->dev, info->gpmc_irq_count,
-					omap_nand_irq, IRQF_SHARED,
-					"gpmc-nand-count", info);
-		if (err) {
-			dev_err(&pdev->dev, "requesting irq(%d) error:%d",
-						info->gpmc_irq_count, err);
-			info->gpmc_irq_count = 0;
-			goto return_error;
-		}
-
-		nand_chip->read_buf  = omap_read_buf_irq_pref;
-		nand_chip->write_buf = omap_write_buf_irq_pref;
-
-		break;
-
-	default:
-		dev_err(&pdev->dev,
-			"xfer_type(%d) not supported!\n", info->xfer_type);
-		err = -EINVAL;
-		goto return_error;
-	}
-
-	if (!omap2_nand_ecc_check(info)) {
-		err = -EINVAL;
-		goto return_error;
-	}
-
-	/*
-	 * Bail out earlier to let NAND_ECC_SOFT code create its own
-	 * ooblayout instead of using ours.
-	 */
-	if (info->ecc_opt == OMAP_ECC_HAM1_CODE_SW) {
-		nand_chip->ecc.mode = NAND_ECC_SOFT;
-		nand_chip->ecc.algo = NAND_ECC_HAMMING;
-		goto scan_tail;
-	}
-
-	/* populate MTD interface based on ECC scheme */
-	switch (info->ecc_opt) {
-	case OMAP_ECC_HAM1_CODE_HW:
-		pr_info("nand: using OMAP_ECC_HAM1_CODE_HW\n");
-		nand_chip->ecc.mode             = NAND_ECC_HW;
-		nand_chip->ecc.bytes            = 3;
-		nand_chip->ecc.size             = 512;
-		nand_chip->ecc.strength         = 1;
-		nand_chip->ecc.calculate        = omap_calculate_ecc;
-		nand_chip->ecc.hwctl            = omap_enable_hwecc;
-		nand_chip->ecc.correct          = omap_correct_data;
-		mtd_set_ooblayout(mtd, &omap_ooblayout_ops);
-		oobbytes_per_step		= nand_chip->ecc.bytes;
-
-		if (!(nand_chip->options & NAND_BUSWIDTH_16))
-			min_oobbytes		= 1;
-
-		break;
-
-	case OMAP_ECC_BCH4_CODE_HW_DETECTION_SW:
-		pr_info("nand: using OMAP_ECC_BCH4_CODE_HW_DETECTION_SW\n");
-		nand_chip->ecc.mode		= NAND_ECC_HW;
-		nand_chip->ecc.size		= 512;
-		nand_chip->ecc.bytes		= 7;
-		nand_chip->ecc.strength		= 4;
-		nand_chip->ecc.hwctl		= omap_enable_hwecc_bch;
-		nand_chip->ecc.correct		= nand_bch_correct_data;
-		nand_chip->ecc.calculate	= omap_calculate_ecc_bch_sw;
-		mtd_set_ooblayout(mtd, &omap_sw_ooblayout_ops);
-		/* Reserve one byte for the OMAP marker */
-		oobbytes_per_step		= nand_chip->ecc.bytes + 1;
-		/* software bch library is used for locating errors */
-		nand_chip->ecc.priv		= nand_bch_init(mtd);
-		if (!nand_chip->ecc.priv) {
-			dev_err(&info->pdev->dev, "unable to use BCH library\n");
-			err = -EINVAL;
-			goto return_error;
-		}
-		break;
-
-	case OMAP_ECC_BCH4_CODE_HW:
-		pr_info("nand: using OMAP_ECC_BCH4_CODE_HW ECC scheme\n");
-		nand_chip->ecc.mode		= NAND_ECC_HW;
-		nand_chip->ecc.size		= 512;
-		/* 14th bit is kept reserved for ROM-code compatibility */
-		nand_chip->ecc.bytes		= 7 + 1;
-		nand_chip->ecc.strength		= 4;
-		nand_chip->ecc.hwctl		= omap_enable_hwecc_bch;
-		nand_chip->ecc.correct		= omap_elm_correct_data;
-		nand_chip->ecc.read_page	= omap_read_page_bch;
-		nand_chip->ecc.write_page	= omap_write_page_bch;
-		nand_chip->ecc.write_subpage	= omap_write_subpage_bch;
-		mtd_set_ooblayout(mtd, &omap_ooblayout_ops);
-		oobbytes_per_step		= nand_chip->ecc.bytes;
-
-		err = elm_config(info->elm_dev, BCH4_ECC,
-				 mtd->writesize / nand_chip->ecc.size,
-				 nand_chip->ecc.size, nand_chip->ecc.bytes);
-		if (err < 0)
-			goto return_error;
-		break;
-
-	case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW:
-		pr_info("nand: using OMAP_ECC_BCH8_CODE_HW_DETECTION_SW\n");
-		nand_chip->ecc.mode		= NAND_ECC_HW;
-		nand_chip->ecc.size		= 512;
-		nand_chip->ecc.bytes		= 13;
-		nand_chip->ecc.strength		= 8;
-		nand_chip->ecc.hwctl		= omap_enable_hwecc_bch;
-		nand_chip->ecc.correct		= nand_bch_correct_data;
-		nand_chip->ecc.calculate	= omap_calculate_ecc_bch_sw;
-		mtd_set_ooblayout(mtd, &omap_sw_ooblayout_ops);
-		/* Reserve one byte for the OMAP marker */
-		oobbytes_per_step		= nand_chip->ecc.bytes + 1;
-		/* software bch library is used for locating errors */
-		nand_chip->ecc.priv		= nand_bch_init(mtd);
-		if (!nand_chip->ecc.priv) {
-			dev_err(&info->pdev->dev, "unable to use BCH library\n");
-			err = -EINVAL;
-			goto return_error;
-		}
-		break;
-
-	case OMAP_ECC_BCH8_CODE_HW:
-		pr_info("nand: using OMAP_ECC_BCH8_CODE_HW ECC scheme\n");
-		nand_chip->ecc.mode		= NAND_ECC_HW;
-		nand_chip->ecc.size		= 512;
-		/* 14th bit is kept reserved for ROM-code compatibility */
-		nand_chip->ecc.bytes		= 13 + 1;
-		nand_chip->ecc.strength		= 8;
-		nand_chip->ecc.hwctl		= omap_enable_hwecc_bch;
-		nand_chip->ecc.correct		= omap_elm_correct_data;
-		nand_chip->ecc.read_page	= omap_read_page_bch;
-		nand_chip->ecc.write_page	= omap_write_page_bch;
-		nand_chip->ecc.write_subpage	= omap_write_subpage_bch;
-		mtd_set_ooblayout(mtd, &omap_ooblayout_ops);
-		oobbytes_per_step		= nand_chip->ecc.bytes;
-
-		err = elm_config(info->elm_dev, BCH8_ECC,
-				 mtd->writesize / nand_chip->ecc.size,
-				 nand_chip->ecc.size, nand_chip->ecc.bytes);
-		if (err < 0)
-			goto return_error;
-
-		break;
-
-	case OMAP_ECC_BCH16_CODE_HW:
-		pr_info("using OMAP_ECC_BCH16_CODE_HW ECC scheme\n");
-		nand_chip->ecc.mode		= NAND_ECC_HW;
-		nand_chip->ecc.size		= 512;
-		nand_chip->ecc.bytes		= 26;
-		nand_chip->ecc.strength		= 16;
-		nand_chip->ecc.hwctl		= omap_enable_hwecc_bch;
-		nand_chip->ecc.correct		= omap_elm_correct_data;
-		nand_chip->ecc.read_page	= omap_read_page_bch;
-		nand_chip->ecc.write_page	= omap_write_page_bch;
-		nand_chip->ecc.write_subpage	= omap_write_subpage_bch;
-		mtd_set_ooblayout(mtd, &omap_ooblayout_ops);
-		oobbytes_per_step		= nand_chip->ecc.bytes;
-
-		err = elm_config(info->elm_dev, BCH16_ECC,
-				 mtd->writesize / nand_chip->ecc.size,
-				 nand_chip->ecc.size, nand_chip->ecc.bytes);
-		if (err < 0)
-			goto return_error;
-
-		break;
-	default:
-		dev_err(&info->pdev->dev, "invalid or unsupported ECC scheme\n");
-		err = -EINVAL;
-		goto return_error;
-	}
-
-	/* check if NAND device's OOB is enough to store ECC signatures */
-	min_oobbytes += (oobbytes_per_step *
-			 (mtd->writesize / nand_chip->ecc.size));
-	if (mtd->oobsize < min_oobbytes) {
-		dev_err(&info->pdev->dev,
-			"not enough OOB bytes required = %d, available=%d\n",
-			min_oobbytes, mtd->oobsize);
-		err = -EINVAL;
-		goto return_error;
-	}
-
-scan_tail:
-	/* second phase scan */
-	err = nand_scan_tail(mtd);
+	err = nand_scan(mtd, 1);
 	if (err)
 		goto return_error;