Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e1e6255c authored by Miquel Raynal's avatar Miquel Raynal
Browse files

mtd: rawnand: omap2: convert driver to nand_scan()



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: default avatarMiquel Raynal <miquel.raynal@bootlin.com>
Reviewed-by: default avatarBoris Brezillon <boris.brezillon@bootlin.com>
parent c49f3bee
Loading
Loading
Loading
Loading
+230 −233
Original line number Diff line number Diff line
@@ -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,106 +1909,26 @@ static const struct mtd_ooblayout_ops omap_sw_ooblayout_ops = {
	.free = omap_sw_ooblayout_free,
};

static int omap_nand_probe(struct platform_device *pdev)
static int omap_nand_attach_chip(struct nand_chip *chip)
{
	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;
	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;

	info = devm_kzalloc(&pdev->dev, sizeof(struct omap_nand_info),
				GFP_KERNEL);
	if (!info)
		return -ENOMEM;

	info->pdev = pdev;

	err = omap_get_dt_info(dev, info);
	if (err)
		return err;

	info->ops = gpmc_omap_get_nand_ops(&info->reg, info->gpmc_cs);
	if (!info->ops) {
		dev_err(&pdev->dev, "Failed to get GPMC->NAND interface\n");
		return -ENODEV;
	}

	nand_chip		= &info->nand;
	mtd			= nand_to_mtd(nand_chip);
	mtd->dev.parent		= &pdev->dev;
	nand_chip->ecc.priv	= NULL;
	nand_set_flash_node(nand_chip, dev->of_node);

	if (!mtd->name) {
		mtd->name = devm_kasprintf(&pdev->dev, GFP_KERNEL,
					   "omap2-nand.%d", info->gpmc_cs);
		if (!mtd->name) {
			dev_err(&pdev->dev, "Failed to set MTD name\n");
			return -ENOMEM;
		}
	}

	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	nand_chip->IO_ADDR_R = devm_ioremap_resource(&pdev->dev, res);
	if (IS_ERR(nand_chip->IO_ADDR_R))
		return PTR_ERR(nand_chip->IO_ADDR_R);

	info->phys_base = res->start;

	nand_chip->controller = &omap_gpmc_controller;

	nand_chip->IO_ADDR_W = nand_chip->IO_ADDR_R;
	nand_chip->cmd_ctrl  = omap_hwcontrol;

	info->ready_gpiod = devm_gpiod_get_optional(&pdev->dev, "rb",
						    GPIOD_IN);
	if (IS_ERR(info->ready_gpiod)) {
		dev_err(dev, "failed to get ready gpio\n");
		return PTR_ERR(info->ready_gpiod);
	}

	/*
	 * If RDY/BSY line is connected to OMAP then use the omap ready
	 * function and the generic nand_wait function which reads the status
	 * register after monitoring the RDY/BSY line. Otherwise use a standard
	 * chip delay which is slightly more than tR (AC Timing) of the NAND
	 * device and read status register until you get a failure or success
	 */
	if (info->ready_gpiod) {
		nand_chip->dev_ready = omap_dev_ready;
		nand_chip->chip_delay = 0;
	} else {
		nand_chip->waitfunc = omap_wait;
		nand_chip->chip_delay = 50;
	}

	if (info->flash_bbt)
		nand_chip->bbt_options |= NAND_BBT_USE_FLASH;

	/* 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;
	if (chip->bbt_options & NAND_BBT_USE_FLASH)
		chip->bbt_options |= NAND_BBT_NO_OOB;
	else
		nand_chip->options |= NAND_SKIP_BBTSCAN;
		chip->options |= NAND_SKIP_BBTSCAN;

	/* re-populate low-level callbacks based on xfer modes */
	/* 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;
		chip->read_buf = omap_read_buf_pref;
		chip->write_buf = omap_write_buf_pref;
		break;

	case NAND_OMAP_POLLED:
@@ -2024,12 +1938,11 @@ static int omap_nand_probe(struct platform_device *pdev)
	case NAND_OMAP_PREFETCH_DMA:
		dma_cap_zero(mask);
		dma_cap_set(DMA_SLAVE, mask);
		info->dma = dma_request_chan(pdev->dev.parent, "rxtx");
		info->dma = dma_request_chan(dev, "rxtx");

		if (IS_ERR(info->dma)) {
			dev_err(&pdev->dev, "DMA engine request failed\n");
			err = PTR_ERR(info->dma);
			goto return_error;
			dev_err(dev, "DMA engine request failed\n");
			return PTR_ERR(info->dma);
		} else {
			struct dma_slave_config cfg;

@@ -2042,222 +1955,306 @@ static int omap_nand_probe(struct platform_device *pdev)
			cfg.dst_maxburst = 16;
			err = dmaengine_slave_config(info->dma, &cfg);
			if (err) {
				dev_err(&pdev->dev, "DMA engine slave config failed: %d\n",
				dev_err(dev,
					"DMA engine slave config failed: %d\n",
					err);
				goto return_error;
				return err;
			}
			nand_chip->read_buf   = omap_read_buf_dma_pref;
			nand_chip->write_buf  = omap_write_buf_dma_pref;
			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(pdev, 0);
		info->gpmc_irq_fifo = platform_get_irq(info->pdev, 0);
		if (info->gpmc_irq_fifo <= 0) {
			dev_err(&pdev->dev, "error getting fifo irq\n");
			err = -ENODEV;
			goto return_error;
			dev_err(dev, "Error getting fifo IRQ\n");
			return -ENODEV;
		}
		err = devm_request_irq(&pdev->dev, info->gpmc_irq_fifo,
		err = devm_request_irq(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",
			dev_err(dev, "Requesting IRQ %d, error %d\n",
				info->gpmc_irq_fifo, err);
			info->gpmc_irq_fifo = 0;
			goto return_error;
			return err;
		}

		info->gpmc_irq_count = platform_get_irq(pdev, 1);
		info->gpmc_irq_count = platform_get_irq(info->pdev, 1);
		if (info->gpmc_irq_count <= 0) {
			dev_err(&pdev->dev, "error getting count irq\n");
			err = -ENODEV;
			goto return_error;
			dev_err(dev, "Error getting IRQ count\n");
			return -ENODEV;
		}
		err = devm_request_irq(&pdev->dev, info->gpmc_irq_count,
		err = devm_request_irq(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",
			dev_err(dev, "Requesting IRQ %d, error %d\n",
				info->gpmc_irq_count, err);
			info->gpmc_irq_count = 0;
			goto return_error;
			return err;
		}

		nand_chip->read_buf  = omap_read_buf_irq_pref;
		nand_chip->write_buf = omap_write_buf_irq_pref;
		chip->read_buf = omap_read_buf_irq_pref;
		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;
		dev_err(dev, "xfer_type %d not supported!\n", info->xfer_type);
		return -EINVAL;
	}

	if (!omap2_nand_ecc_check(info)) {
		err = -EINVAL;
		goto return_error;
	}
	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) {
		nand_chip->ecc.mode = NAND_ECC_SOFT;
		nand_chip->ecc.algo = NAND_ECC_HAMMING;
		goto scan_tail;
		chip->ecc.mode = NAND_ECC_SOFT;
		chip->ecc.algo = NAND_ECC_HAMMING;
		return 0;
	}

	/* populate MTD interface based on ECC scheme */
	/* 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;
		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		= nand_chip->ecc.bytes;
		oobbytes_per_step	= chip->ecc.bytes;

		if (!(nand_chip->options & NAND_BUSWIDTH_16))
		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");
		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;
		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		= 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;
		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");
		nand_chip->ecc.mode		= NAND_ECC_HW;
		nand_chip->ecc.size		= 512;
		chip->ecc.mode		= NAND_ECC_HW;
		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;
		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		= nand_chip->ecc.bytes;
		oobbytes_per_step	= 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);
				 mtd->writesize / chip->ecc.size,
				 chip->ecc.size, chip->ecc.bytes);
		if (err < 0)
			goto return_error;
			return err;
		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;
		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		= 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;
		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");
		nand_chip->ecc.mode		= NAND_ECC_HW;
		nand_chip->ecc.size		= 512;
		chip->ecc.mode		= NAND_ECC_HW;
		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;
		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		= nand_chip->ecc.bytes;
		oobbytes_per_step	= 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);
				 mtd->writesize / chip->ecc.size,
				 chip->ecc.size, chip->ecc.bytes);
		if (err < 0)
			goto return_error;
			return err;

		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;
		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		= nand_chip->ecc.bytes;
		oobbytes_per_step	= 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);
				 mtd->writesize / chip->ecc.size,
				 chip->ecc.size, chip->ecc.bytes);
		if (err < 0)
			goto return_error;
			return err;

		break;
	default:
		dev_err(&info->pdev->dev, "invalid or unsupported ECC scheme\n");
		err = -EINVAL;
		goto return_error;
		dev_err(dev, "Invalid or unsupported ECC scheme\n");
		return -EINVAL;
	}

	/* check if NAND device's OOB is enough to store ECC signatures */
	/* Check if NAND device's OOB is enough to store ECC signatures */
	min_oobbytes += (oobbytes_per_step *
			 (mtd->writesize / nand_chip->ecc.size));
			 (mtd->writesize / chip->ecc.size));
	if (mtd->oobsize < min_oobbytes) {
		dev_err(&info->pdev->dev,
			"not enough OOB bytes required = %d, available=%d\n",
		dev_err(dev,
			"Not enough OOB bytes: required = %d, available=%d\n",
			min_oobbytes, mtd->oobsize);
		err = -EINVAL;
		goto return_error;
		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;
	struct resource			*res;
	struct device			*dev = &pdev->dev;

	info = devm_kzalloc(&pdev->dev, sizeof(struct omap_nand_info),
				GFP_KERNEL);
	if (!info)
		return -ENOMEM;

	info->pdev = pdev;

	err = omap_get_dt_info(dev, info);
	if (err)
		return err;

	info->ops = gpmc_omap_get_nand_ops(&info->reg, info->gpmc_cs);
	if (!info->ops) {
		dev_err(&pdev->dev, "Failed to get GPMC->NAND interface\n");
		return -ENODEV;
	}

	nand_chip		= &info->nand;
	mtd			= nand_to_mtd(nand_chip);
	mtd->dev.parent		= &pdev->dev;
	nand_chip->ecc.priv	= NULL;
	nand_set_flash_node(nand_chip, dev->of_node);

	if (!mtd->name) {
		mtd->name = devm_kasprintf(&pdev->dev, GFP_KERNEL,
					   "omap2-nand.%d", info->gpmc_cs);
		if (!mtd->name) {
			dev_err(&pdev->dev, "Failed to set MTD name\n");
			return -ENOMEM;
		}
	}

	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	nand_chip->IO_ADDR_R = devm_ioremap_resource(&pdev->dev, res);
	if (IS_ERR(nand_chip->IO_ADDR_R))
		return PTR_ERR(nand_chip->IO_ADDR_R);

	info->phys_base = res->start;

	nand_chip->controller = &omap_gpmc_controller;

	nand_chip->IO_ADDR_W = nand_chip->IO_ADDR_R;
	nand_chip->cmd_ctrl  = omap_hwcontrol;

	info->ready_gpiod = devm_gpiod_get_optional(&pdev->dev, "rb",
						    GPIOD_IN);
	if (IS_ERR(info->ready_gpiod)) {
		dev_err(dev, "failed to get ready gpio\n");
		return PTR_ERR(info->ready_gpiod);
	}

scan_tail:
	/* second phase scan */
	err = nand_scan_tail(mtd);
	/*
	 * If RDY/BSY line is connected to OMAP then use the omap ready
	 * function and the generic nand_wait function which reads the status
	 * register after monitoring the RDY/BSY line. Otherwise use a standard
	 * chip delay which is slightly more than tR (AC Timing) of the NAND
	 * device and read status register until you get a failure or success
	 */
	if (info->ready_gpiod) {
		nand_chip->dev_ready = omap_dev_ready;
		nand_chip->chip_delay = 0;
	} else {
		nand_chip->waitfunc = omap_wait;
		nand_chip->chip_delay = 50;
	}

	if (info->flash_bbt)
		nand_chip->bbt_options |= NAND_BBT_USE_FLASH;

	/* scan NAND device connected to chip controller */
	nand_chip->options |= info->devsize & NAND_BUSWIDTH_16;

	err = nand_scan(mtd, 1);
	if (err)
		goto return_error;