mtd: nand: omap: Clean up device tree support

Move NAND specific device tree parsing to NAND driver.

The NAND controller node must have a compatible id, register space
resource and interrupt resource.

Signed-off-by: Roger Quadros <rogerq@ti.com>
Acked-by: Brian Norris <computersforpeace@gmail.com>
Acked-by: Tony Lindgren <tony@atomide.com>
This commit is contained in:
Roger Quadros
2014-05-21 07:29:03 +03:00
parent 01b95fc6b2
commit c9711ec525
4 changed files with 153 additions and 132 deletions

View File

@@ -24,6 +24,7 @@
#include <linux/slab.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_mtd.h>
#include <linux/mtd/nand_bch.h>
#include <linux/platform_data/elm.h>
@@ -176,11 +177,11 @@ struct omap_nand_info {
/* Interface to GPMC */
struct gpmc_nand_regs reg;
struct gpmc_nand_ops *ops;
bool flash_bbt;
/* generated at runtime depending on ECC algorithm and layout selected */
struct nand_ecclayout oobinfo;
/* fields specific for BCHx_HW ECC scheme */
struct device *elm_dev;
struct device_node *of_node;
};
static inline struct omap_nand_info *mtd_to_omap(struct mtd_info *mtd)
@@ -1643,10 +1644,86 @@ static bool omap2_nand_ecc_check(struct omap_nand_info *info,
return true;
}
static const char * const nand_xfer_types[] = {
[NAND_OMAP_PREFETCH_POLLED] = "prefetch-polled",
[NAND_OMAP_POLLED] = "polled",
[NAND_OMAP_PREFETCH_DMA] = "prefetch-dma",
[NAND_OMAP_PREFETCH_IRQ] = "prefetch-irq",
};
static int omap_get_dt_info(struct device *dev, struct omap_nand_info *info)
{
struct device_node *child = dev->of_node;
int i;
const char *s;
u32 cs;
if (of_property_read_u32(child, "reg", &cs) < 0) {
dev_err(dev, "reg not found in DT\n");
return -EINVAL;
}
info->gpmc_cs = cs;
/* detect availability of ELM module. Won't be present pre-OMAP4 */
info->elm_of_node = of_parse_phandle(child, "ti,elm-id", 0);
if (!info->elm_of_node)
dev_dbg(dev, "ti,elm-id not in DT\n");
/* select ecc-scheme for NAND */
if (of_property_read_string(child, "ti,nand-ecc-opt", &s)) {
dev_err(dev, "ti,nand-ecc-opt not found\n");
return -EINVAL;
}
if (!strcmp(s, "sw")) {
info->ecc_opt = OMAP_ECC_HAM1_CODE_SW;
} else if (!strcmp(s, "ham1") ||
!strcmp(s, "hw") || !strcmp(s, "hw-romcode")) {
info->ecc_opt = OMAP_ECC_HAM1_CODE_HW;
} else if (!strcmp(s, "bch4")) {
if (info->elm_of_node)
info->ecc_opt = OMAP_ECC_BCH4_CODE_HW;
else
info->ecc_opt = OMAP_ECC_BCH4_CODE_HW_DETECTION_SW;
} else if (!strcmp(s, "bch8")) {
if (info->elm_of_node)
info->ecc_opt = OMAP_ECC_BCH8_CODE_HW;
else
info->ecc_opt = OMAP_ECC_BCH8_CODE_HW_DETECTION_SW;
} else if (!strcmp(s, "bch16")) {
info->ecc_opt = OMAP_ECC_BCH16_CODE_HW;
} else {
dev_err(dev, "unrecognized value for ti,nand-ecc-opt\n");
return -EINVAL;
}
/* select data transfer mode */
if (!of_property_read_string(child, "ti,nand-xfer-type", &s)) {
for (i = 0; i < ARRAY_SIZE(nand_xfer_types); i++) {
if (!strcasecmp(s, nand_xfer_types[i])) {
info->xfer_type = i;
goto next;
}
}
dev_err(dev, "unrecognized value for ti,nand-xfer-type\n");
return -EINVAL;
}
next:
of_get_nand_on_flash_bbt(child);
if (of_get_nand_bus_width(child) == 16)
info->devsize = NAND_BUSWIDTH_16;
return 0;
}
static int omap_nand_probe(struct platform_device *pdev)
{
struct omap_nand_info *info;
struct omap_nand_platform_data *pdata;
struct omap_nand_platform_data *pdata = NULL;
struct mtd_info *mtd;
struct nand_chip *nand_chip;
struct nand_ecclayout *ecclayout;
@@ -1656,39 +1733,47 @@ static int omap_nand_probe(struct platform_device *pdev)
unsigned sig;
unsigned oob_index;
struct resource *res;
pdata = dev_get_platdata(&pdev->dev);
if (pdata == NULL) {
dev_err(&pdev->dev, "platform data missing\n");
return -ENODEV;
}
struct device *dev = &pdev->dev;
info = devm_kzalloc(&pdev->dev, sizeof(struct omap_nand_info),
GFP_KERNEL);
if (!info)
return -ENOMEM;
platform_set_drvdata(pdev, info);
info->pdev = pdev;
if (dev->of_node) {
if (omap_get_dt_info(dev, info))
return -EINVAL;
} else {
pdata = dev_get_platdata(&pdev->dev);
if (!pdata) {
dev_err(&pdev->dev, "platform data missing\n");
return -EINVAL;
}
info->gpmc_cs = pdata->cs;
info->reg = pdata->reg;
info->ecc_opt = pdata->ecc_opt;
info->dev_ready = pdata->dev_ready;
info->xfer_type = pdata->xfer_type;
info->devsize = pdata->devsize;
info->elm_of_node = pdata->elm_of_node;
info->flash_bbt = pdata->flash_bbt;
}
platform_set_drvdata(pdev, info);
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;
}
info->pdev = pdev;
info->gpmc_cs = pdata->cs;
info->of_node = pdata->of_node;
info->ecc_opt = pdata->ecc_opt;
info->dev_ready = pdata->dev_ready;
info->xfer_type = pdata->xfer_type;
info->devsize = pdata->devsize;
info->elm_of_node = pdata->elm_of_node;
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, pdata->of_node);
nand_set_flash_node(nand_chip, dev->of_node);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
nand_chip->IO_ADDR_R = devm_ioremap_resource(&pdev->dev, res);
@@ -1717,7 +1802,7 @@ static int omap_nand_probe(struct platform_device *pdev)
nand_chip->chip_delay = 50;
}
if (pdata->flash_bbt)
if (info->flash_bbt)
nand_chip->bbt_options |= NAND_BBT_USE_FLASH | NAND_BBT_NO_OOB;
else
nand_chip->options |= NAND_SKIP_BBTSCAN;
@@ -2035,7 +2120,10 @@ scan_tail:
goto return_error;
}
mtd_device_register(mtd, pdata->parts, pdata->nr_parts);
if (dev->of_node)
mtd_device_register(mtd, NULL, 0);
else
mtd_device_register(mtd, pdata->parts, pdata->nr_parts);
platform_set_drvdata(pdev, mtd);
@@ -2066,11 +2154,17 @@ static int omap_nand_remove(struct platform_device *pdev)
return 0;
}
static const struct of_device_id omap_nand_ids[] = {
{ .compatible = "ti,omap2-nand", },
{},
};
static struct platform_driver omap_nand_driver = {
.probe = omap_nand_probe,
.remove = omap_nand_remove,
.driver = {
.name = DRIVER_NAME,
.of_match_table = of_match_ptr(omap_nand_ids),
},
};