|
@@ -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;
|
|
|
- int min_oobbytes = BADBLOCK_MARKER_LENGTH;
|
|
|
- int oobbytes_per_step;
|
|
|
-
|
|
|
- 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;
|
|
|
- }
|
|
|
+ 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 (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,
|
|
|
- omap_nand_irq, IRQF_SHARED,
|
|
|
- "gpmc-nand-fifo", info);
|
|
|
+ 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",
|
|
|
- info->gpmc_irq_fifo, err);
|
|
|
+ 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,
|
|
|
- omap_nand_irq, IRQF_SHARED,
|
|
|
- "gpmc-nand-count", info);
|
|
|
+ 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",
|
|
|
- info->gpmc_irq_count, err);
|
|
|
+ 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))
|
|
|
- min_oobbytes = 1;
|
|
|
+ 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);
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * 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;
|
|
|
}
|
|
|
|
|
|
-scan_tail:
|
|
|
- /* second phase scan */
|
|
|
- err = nand_scan_tail(mtd);
|
|
|
+ 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;
|
|
|
|