Commit e1e6255c authored by Miquel Raynal's avatar Miquel Raynal

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
...@@ -144,12 +144,6 @@ static u_char bch8_vector[] = {0xf3, 0xdb, 0x14, 0x16, 0x8b, 0xd2, 0xbe, 0xcc, ...@@ -144,12 +144,6 @@ static u_char bch8_vector[] = {0xf3, 0xdb, 0x14, 0x16, 0x8b, 0xd2, 0xbe, 0xcc,
0xac, 0x6b, 0xff, 0x99, 0x7b}; 0xac, 0x6b, 0xff, 0x99, 0x7b};
static u_char bch4_vector[] = {0x00, 0x6b, 0x31, 0xdd, 0x41, 0xbc, 0x10}; 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 omap_nand_info {
struct nand_chip nand; struct nand_chip nand;
struct platform_device *pdev; struct platform_device *pdev;
...@@ -1915,106 +1909,26 @@ static const struct mtd_ooblayout_ops omap_sw_ooblayout_ops = { ...@@ -1915,106 +1909,26 @@ static const struct mtd_ooblayout_ops omap_sw_ooblayout_ops = {
.free = omap_sw_ooblayout_free, .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 = nand_to_mtd(chip);
struct mtd_info *mtd; struct omap_nand_info *info = mtd_to_omap(mtd);
struct nand_chip *nand_chip; struct device *dev = &info->pdev->dev;
int err;
dma_cap_mask_t mask;
struct resource *res;
struct device *dev = &pdev->dev;
int min_oobbytes = BADBLOCK_MARKER_LENGTH; int min_oobbytes = BADBLOCK_MARKER_LENGTH;
int oobbytes_per_step; int oobbytes_per_step;
dma_cap_mask_t mask;
int err;
info = devm_kzalloc(&pdev->dev, sizeof(struct omap_nand_info), if (chip->bbt_options & NAND_BBT_USE_FLASH)
GFP_KERNEL); chip->bbt_options |= NAND_BBT_NO_OOB;
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;
else 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) { switch (info->xfer_type) {
case NAND_OMAP_PREFETCH_POLLED: case NAND_OMAP_PREFETCH_POLLED:
nand_chip->read_buf = omap_read_buf_pref; chip->read_buf = omap_read_buf_pref;
nand_chip->write_buf = omap_write_buf_pref; chip->write_buf = omap_write_buf_pref;
break; break;
case NAND_OMAP_POLLED: case NAND_OMAP_POLLED:
...@@ -2024,12 +1938,11 @@ static int omap_nand_probe(struct platform_device *pdev) ...@@ -2024,12 +1938,11 @@ static int omap_nand_probe(struct platform_device *pdev)
case NAND_OMAP_PREFETCH_DMA: case NAND_OMAP_PREFETCH_DMA:
dma_cap_zero(mask); dma_cap_zero(mask);
dma_cap_set(DMA_SLAVE, 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)) { if (IS_ERR(info->dma)) {
dev_err(&pdev->dev, "DMA engine request failed\n"); dev_err(dev, "DMA engine request failed\n");
err = PTR_ERR(info->dma); return PTR_ERR(info->dma);
goto return_error;
} else { } else {
struct dma_slave_config cfg; struct dma_slave_config cfg;
...@@ -2042,222 +1955,306 @@ static int omap_nand_probe(struct platform_device *pdev) ...@@ -2042,222 +1955,306 @@ static int omap_nand_probe(struct platform_device *pdev)
cfg.dst_maxburst = 16; cfg.dst_maxburst = 16;
err = dmaengine_slave_config(info->dma, &cfg); err = dmaengine_slave_config(info->dma, &cfg);
if (err) { if (err) {
dev_err(&pdev->dev, "DMA engine slave config failed: %d\n", dev_err(dev,
"DMA engine slave config failed: %d\n",
err); err);
goto return_error; return err;
} }
nand_chip->read_buf = omap_read_buf_dma_pref; chip->read_buf = omap_read_buf_dma_pref;
nand_chip->write_buf = omap_write_buf_dma_pref; chip->write_buf = omap_write_buf_dma_pref;
} }
break; break;
case NAND_OMAP_PREFETCH_IRQ: 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) { if (info->gpmc_irq_fifo <= 0) {
dev_err(&pdev->dev, "error getting fifo irq\n"); dev_err(dev, "Error getting fifo IRQ\n");
err = -ENODEV; return -ENODEV;
goto return_error;
} }
err = devm_request_irq(&pdev->dev, info->gpmc_irq_fifo, err = devm_request_irq(dev, info->gpmc_irq_fifo,
omap_nand_irq, IRQF_SHARED, omap_nand_irq, IRQF_SHARED,
"gpmc-nand-fifo", info); "gpmc-nand-fifo", info);
if (err) { 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, err);
info->gpmc_irq_fifo = 0; 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) { if (info->gpmc_irq_count <= 0) {
dev_err(&pdev->dev, "error getting count irq\n"); dev_err(dev, "Error getting IRQ count\n");
err = -ENODEV; return -ENODEV;
goto return_error;
} }
err = devm_request_irq(&pdev->dev, info->gpmc_irq_count, err = devm_request_irq(dev, info->gpmc_irq_count,
omap_nand_irq, IRQF_SHARED, omap_nand_irq, IRQF_SHARED,
"gpmc-nand-count", info); "gpmc-nand-count", info);
if (err) { 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, err);
info->gpmc_irq_count = 0; info->gpmc_irq_count = 0;
goto return_error; return err;
} }
nand_chip->read_buf = omap_read_buf_irq_pref; chip->read_buf = omap_read_buf_irq_pref;
nand_chip->write_buf = omap_write_buf_irq_pref; chip->write_buf = omap_write_buf_irq_pref;
break; break;
default: default:
dev_err(&pdev->dev, dev_err(dev, "xfer_type %d not supported!\n", info->xfer_type);
"xfer_type(%d) not supported!\n", info->xfer_type); return -EINVAL;
err = -EINVAL;
goto return_error;
} }
if (!omap2_nand_ecc_check(info)) { if (!omap2_nand_ecc_check(info))
err = -EINVAL; return -EINVAL;
goto return_error;
}
/* /*
* Bail out earlier to let NAND_ECC_SOFT code create its own * Bail out earlier to let NAND_ECC_SOFT code create its own
* ooblayout instead of using ours. * ooblayout instead of using ours.
*/ */
if (info->ecc_opt == OMAP_ECC_HAM1_CODE_SW) { if (info->ecc_opt == OMAP_ECC_HAM1_CODE_SW) {
nand_chip->ecc.mode = NAND_ECC_SOFT; chip->ecc.mode = NAND_ECC_SOFT;
nand_chip->ecc.algo = NAND_ECC_HAMMING; chip->ecc.algo = NAND_ECC_HAMMING;
goto scan_tail; return 0;
} }
/* populate MTD interface based on ECC scheme */ /* Populate MTD interface based on ECC scheme */
switch (info->ecc_opt) { switch (info->ecc_opt) {
case OMAP_ECC_HAM1_CODE_HW: case OMAP_ECC_HAM1_CODE_HW:
pr_info("nand: using OMAP_ECC_HAM1_CODE_HW\n"); dev_info(dev, "nand: using OMAP_ECC_HAM1_CODE_HW\n");
nand_chip->ecc.mode = NAND_ECC_HW; chip->ecc.mode = NAND_ECC_HW;
nand_chip->ecc.bytes = 3; chip->ecc.bytes = 3;
nand_chip->ecc.size = 512; chip->ecc.size = 512;
nand_chip->ecc.strength = 1; chip->ecc.strength = 1;
nand_chip->ecc.calculate = omap_calculate_ecc; chip->ecc.calculate = omap_calculate_ecc;
nand_chip->ecc.hwctl = omap_enable_hwecc; chip->ecc.hwctl = omap_enable_hwecc;
nand_chip->ecc.correct = omap_correct_data; chip->ecc.correct = omap_correct_data;
mtd_set_ooblayout(mtd, &omap_ooblayout_ops); 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; min_oobbytes = 1;
break; break;
case OMAP_ECC_BCH4_CODE_HW_DETECTION_SW: case OMAP_ECC_BCH4_CODE_HW_DETECTION_SW:
pr_info("nand: using OMAP_ECC_BCH4_CODE_HW_DETECTION_SW\n"); pr_info("nand: using OMAP_ECC_BCH4_CODE_HW_DETECTION_SW\n");
nand_chip->ecc.mode = NAND_ECC_HW; chip->ecc.mode = NAND_ECC_HW;
nand_chip->ecc.size = 512; chip->ecc.size = 512;
nand_chip->ecc.bytes = 7; chip->ecc.bytes = 7;
nand_chip->ecc.strength = 4; chip->ecc.strength = 4;
nand_chip->ecc.hwctl = omap_enable_hwecc_bch; chip->ecc.hwctl = omap_enable_hwecc_bch;
nand_chip->ecc.correct = nand_bch_correct_data; chip->ecc.correct = nand_bch_correct_data;
nand_chip->ecc.calculate = omap_calculate_ecc_bch_sw; chip->ecc.calculate = omap_calculate_ecc_bch_sw;
mtd_set_ooblayout(mtd, &omap_sw_ooblayout_ops); mtd_set_ooblayout(mtd, &omap_sw_ooblayout_ops);
/* Reserve one byte for the OMAP marker */ /* Reserve one byte for the OMAP marker */
oobbytes_per_step = nand_chip->ecc.bytes + 1; oobbytes_per_step = chip->ecc.bytes + 1;
/* software bch library is used for locating errors */ /* Software BCH library is used for locating errors */
nand_chip->ecc.priv = nand_bch_init(mtd); chip->ecc.priv = nand_bch_init(mtd);
if (!nand_chip->ecc.priv) { if (!chip->ecc.priv) {
dev_err(&info->pdev->dev, "unable to use BCH library\n"); dev_err(dev, "Unable to use BCH library\n");
err = -EINVAL; return -EINVAL;
goto return_error;
} }
break; break;
case OMAP_ECC_BCH4_CODE_HW: case OMAP_ECC_BCH4_CODE_HW:
pr_info("nand: using OMAP_ECC_BCH4_CODE_HW ECC scheme\n"); pr_info("nand: using OMAP_ECC_BCH4_CODE_HW ECC scheme\n");
nand_chip->ecc.mode = NAND_ECC_HW; chip->ecc.mode = NAND_ECC_HW;
nand_chip->ecc.size = 512; chip->ecc.size = 512;
/* 14th bit is kept reserved for ROM-code compatibility */ /* 14th bit is kept reserved for ROM-code compatibility */
nand_chip->ecc.bytes = 7 + 1; chip->ecc.bytes = 7 + 1;
nand_chip->ecc.strength = 4; chip->ecc.strength = 4;
nand_chip->ecc.hwctl = omap_enable_hwecc_bch; chip->ecc.hwctl = omap_enable_hwecc_bch;
nand_chip->ecc.correct = omap_elm_correct_data; chip->ecc.correct = omap_elm_correct_data;
nand_chip->ecc.read_page = omap_read_page_bch; chip->ecc.read_page = omap_read_page_bch;
nand_chip->ecc.write_page = omap_write_page_bch; chip->ecc.write_page = omap_write_page_bch;
nand_chip->ecc.write_subpage = omap_write_subpage_bch; chip->ecc.write_subpage = omap_write_subpage_bch;
mtd_set_ooblayout(mtd, &omap_ooblayout_ops); 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, err = elm_config(info->elm_dev, BCH4_ECC,
mtd->writesize / nand_chip->ecc.size, mtd->writesize / chip->ecc.size,
nand_chip->ecc.size, nand_chip->ecc.bytes); chip->ecc.size, chip->ecc.bytes);
if (err < 0) if (err < 0)
goto return_error; return err;
break; break;
case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW: case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW:
pr_info("nand: using OMAP_ECC_BCH8_CODE_HW_DETECTION_SW\n"); pr_info("nand: using OMAP_ECC_BCH8_CODE_HW_DETECTION_SW\n");
nand_chip->ecc.mode = NAND_ECC_HW; chip->ecc.mode = NAND_ECC_HW;
nand_chip->ecc.size = 512; chip->ecc.size = 512;
nand_chip->ecc.bytes = 13; chip->ecc.bytes = 13;
nand_chip->ecc.strength = 8; chip->ecc.strength = 8;
nand_chip->ecc.hwctl = omap_enable_hwecc_bch; chip->ecc.hwctl = omap_enable_hwecc_bch;
nand_chip->ecc.correct = nand_bch_correct_data; chip->ecc.correct = nand_bch_correct_data;
nand_chip->ecc.calculate = omap_calculate_ecc_bch_sw; chip->ecc.calculate = omap_calculate_ecc_bch_sw;
mtd_set_ooblayout(mtd, &omap_sw_ooblayout_ops); mtd_set_ooblayout(mtd, &omap_sw_ooblayout_ops);
/* Reserve one byte for the OMAP marker */ /* Reserve one byte for the OMAP marker */
oobbytes_per_step = nand_chip->ecc.bytes + 1; oobbytes_per_step = chip->ecc.bytes + 1;
/* software bch library is used for locating errors */ /* Software BCH library is used for locating errors */
nand_chip->ecc.priv = nand_bch_init(mtd); chip->ecc.priv = nand_bch_init(mtd);
if (!nand_chip->ecc.priv) { if (!chip->ecc.priv) {
dev_err(&info->pdev->dev, "unable to use BCH library\n"); dev_err(dev, "unable to use BCH library\n");
err = -EINVAL; return -EINVAL;
goto return_error;
} }
break; break;
case OMAP_ECC_BCH8_CODE_HW: case OMAP_ECC_BCH8_CODE_HW:
pr_info("nand: using OMAP_ECC_BCH8_CODE_HW ECC scheme\n"); pr_info("nand: using OMAP_ECC_BCH8_CODE_HW ECC scheme\n");
nand_chip->ecc.mode = NAND_ECC_HW; chip->ecc.mode = NAND_ECC_HW;
nand_chip->ecc.size = 512; chip->ecc.size = 512;
/* 14th bit is kept reserved for ROM-code compatibility */ /* 14th bit is kept reserved for ROM-code compatibility */
nand_chip->ecc.bytes = 13 + 1; chip->ecc.bytes = 13 + 1;
nand_chip->ecc.strength = 8; chip->ecc.strength = 8;
nand_chip->ecc.hwctl = omap_enable_hwecc_bch; chip->ecc.hwctl = omap_enable_hwecc_bch;
nand_chip->ecc.correct = omap_elm_correct_data; chip->ecc.correct = omap_elm_correct_data;
nand_chip->ecc.read_page = omap_read_page_bch; chip->ecc.read_page = omap_read_page_bch;
nand_chip->ecc.write_page = omap_write_page_bch; chip->ecc.write_page = omap_write_page_bch;
nand_chip->ecc.write_subpage = omap_write_subpage_bch; chip->ecc.write_subpage = omap_write_subpage_bch;
mtd_set_ooblayout(mtd, &omap_ooblayout_ops); 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, err = elm_config(info->elm_dev, BCH8_ECC,
mtd->writesize / nand_chip->ecc.size, mtd->writesize / chip->ecc.size,
nand_chip->ecc.size, nand_chip->ecc.bytes); chip->ecc.size, chip->ecc.bytes);
if (err < 0) if (err < 0)
goto return_error; return err;
break; break;
case OMAP_ECC_BCH16_CODE_HW: case OMAP_ECC_BCH16_CODE_HW:
pr_info("using OMAP_ECC_BCH16_CODE_HW ECC scheme\n"); pr_info("Using OMAP_ECC_BCH16_CODE_HW ECC scheme\n");
nand_chip->ecc.mode = NAND_ECC_HW; chip->ecc.mode = NAND_ECC_HW;
nand_chip->ecc.size = 512; chip->ecc.size = 512;
nand_chip->ecc.bytes = 26; chip->ecc.bytes = 26;
nand_chip->ecc.strength = 16; chip->ecc.strength = 16;
nand_chip->ecc.hwctl = omap_enable_hwecc_bch; chip->ecc.hwctl = omap_enable_hwecc_bch;
nand_chip->ecc.correct = omap_elm_correct_data; chip->ecc.correct = omap_elm_correct_data;
nand_chip->ecc.read_page = omap_read_page_bch; chip->ecc.read_page = omap_read_page_bch;
nand_chip->ecc.write_page = omap_write_page_bch; chip->ecc.write_page = omap_write_page_bch;
nand_chip->ecc.write_subpage = omap_write_subpage_bch; chip->ecc.write_subpage = omap_write_subpage_bch;
mtd_set_ooblayout(mtd, &omap_ooblayout_ops); 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, err = elm_config(info->elm_dev, BCH16_ECC,
mtd->writesize / nand_chip->ecc.size, mtd->writesize / chip->ecc.size,
nand_chip->ecc.size, nand_chip->ecc.bytes); chip->ecc.size, chip->ecc.bytes);
if (err < 0) if (err < 0)
goto return_error; return err;
break; break;
default: default:
dev_err(&info->pdev->dev, "invalid or unsupported ECC scheme\n"); dev_err(dev, "Invalid or unsupported ECC scheme\n");
err = -EINVAL; return -EINVAL;
goto return_error;
} }
/* 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 * min_oobbytes += (oobbytes_per_step *
(mtd->writesize / nand_chip->ecc.size)); (mtd->writesize / chip->ecc.size));
if (mtd->oobsize < min_oobbytes) { if (mtd->oobsize < min_oobbytes) {
dev_err(&info->pdev->dev, dev_err(dev,
"not enough OOB bytes required = %d, available=%d\n", "Not enough OOB bytes: required = %d, available=%d\n",
min_oobbytes, mtd->oobsize); min_oobbytes, mtd->oobsize);
err = -EINVAL; return -EINVAL;
goto return_error; }
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 */ * If RDY/BSY line is connected to OMAP then use the omap ready
err = nand_scan_tail(mtd); * 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) if (err)
goto return_error; goto return_error;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment