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

Commit c9711ec5 authored by Roger Quadros's avatar Roger Quadros
Browse files

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: default avatarRoger Quadros <rogerq@ti.com>
Acked-by: default avatarBrian Norris <computersforpeace@gmail.com>
Acked-by: default avatarTony Lindgren <tony@atomide.com>
parent 01b95fc6
Loading
Loading
Loading
Loading
+1 −4
Original line number Original line Diff line number Diff line
@@ -97,9 +97,6 @@ int gpmc_nand_init(struct omap_nand_platform_data *gpmc_nand_data,
	gpmc_nand_res[2].start = gpmc_get_client_irq(GPMC_IRQ_COUNT_EVENT);
	gpmc_nand_res[2].start = gpmc_get_client_irq(GPMC_IRQ_COUNT_EVENT);


	memset(&s, 0, sizeof(struct gpmc_settings));
	memset(&s, 0, sizeof(struct gpmc_settings));
	if (gpmc_nand_data->of_node)
		gpmc_read_settings_dt(gpmc_nand_data->of_node, &s);
	else
	gpmc_set_legacy(gpmc_nand_data, &s);
	gpmc_set_legacy(gpmc_nand_data, &s);


	s.device_nand = true;
	s.device_nand = true;
+37 −106
Original line number Original line Diff line number Diff line
@@ -30,7 +30,6 @@
#include <linux/of_device.h>
#include <linux/of_device.h>
#include <linux/of_platform.h>
#include <linux/of_platform.h>
#include <linux/omap-gpmc.h>
#include <linux/omap-gpmc.h>
#include <linux/mtd/nand.h>
#include <linux/pm_runtime.h>
#include <linux/pm_runtime.h>


#include <linux/platform_data/mtd-nand-omap2.h>
#include <linux/platform_data/mtd-nand-omap2.h>
@@ -1852,105 +1851,6 @@ static void __maybe_unused gpmc_read_timings_dt(struct device_node *np,
		of_property_read_bool(np, "gpmc,time-para-granularity");
		of_property_read_bool(np, "gpmc,time-para-granularity");
}
}


#if IS_ENABLED(CONFIG_MTD_NAND)

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 gpmc_probe_nand_child(struct platform_device *pdev,
				 struct device_node *child)
{
	u32 val;
	const char *s;
	struct gpmc_timings gpmc_t;
	struct omap_nand_platform_data *gpmc_nand_data;

	if (of_property_read_u32(child, "reg", &val) < 0) {
		dev_err(&pdev->dev, "%s has no 'reg' property\n",
			child->full_name);
		return -ENODEV;
	}

	gpmc_nand_data = devm_kzalloc(&pdev->dev, sizeof(*gpmc_nand_data),
				      GFP_KERNEL);
	if (!gpmc_nand_data)
		return -ENOMEM;

	gpmc_nand_data->cs = val;
	gpmc_nand_data->of_node = child;

	/* Detect availability of ELM module */
	gpmc_nand_data->elm_of_node = of_parse_phandle(child, "ti,elm-id", 0);
	if (gpmc_nand_data->elm_of_node == NULL)
		gpmc_nand_data->elm_of_node =
					of_parse_phandle(child, "elm_id", 0);

	/* select ecc-scheme for NAND */
	if (of_property_read_string(child, "ti,nand-ecc-opt", &s)) {
		pr_err("%s: ti,nand-ecc-opt not found\n", __func__);
		return -ENODEV;
	}

	if (!strcmp(s, "sw"))
		gpmc_nand_data->ecc_opt = OMAP_ECC_HAM1_CODE_SW;
	else if (!strcmp(s, "ham1") ||
		 !strcmp(s, "hw") || !strcmp(s, "hw-romcode"))
		gpmc_nand_data->ecc_opt =
				OMAP_ECC_HAM1_CODE_HW;
	else if (!strcmp(s, "bch4"))
		if (gpmc_nand_data->elm_of_node)
			gpmc_nand_data->ecc_opt =
				OMAP_ECC_BCH4_CODE_HW;
		else
			gpmc_nand_data->ecc_opt =
				OMAP_ECC_BCH4_CODE_HW_DETECTION_SW;
	else if (!strcmp(s, "bch8"))
		if (gpmc_nand_data->elm_of_node)
			gpmc_nand_data->ecc_opt =
				OMAP_ECC_BCH8_CODE_HW;
		else
			gpmc_nand_data->ecc_opt =
				OMAP_ECC_BCH8_CODE_HW_DETECTION_SW;
	else if (!strcmp(s, "bch16"))
		if (gpmc_nand_data->elm_of_node)
			gpmc_nand_data->ecc_opt =
				OMAP_ECC_BCH16_CODE_HW;
		else
			pr_err("%s: BCH16 requires ELM support\n", __func__);
	else
		pr_err("%s: ti,nand-ecc-opt invalid value\n", __func__);

	/* select data transfer mode for NAND controller */
	if (!of_property_read_string(child, "ti,nand-xfer-type", &s))
		for (val = 0; val < ARRAY_SIZE(nand_xfer_types); val++)
			if (!strcasecmp(s, nand_xfer_types[val])) {
				gpmc_nand_data->xfer_type = val;
				break;
			}

	gpmc_nand_data->flash_bbt = of_get_nand_on_flash_bbt(child);

	val = of_get_nand_bus_width(child);
	if (val == 16)
		gpmc_nand_data->devsize = NAND_BUSWIDTH_16;

	gpmc_read_timings_dt(child, &gpmc_t);
	gpmc_nand_init(gpmc_nand_data, &gpmc_t);

	return 0;
}
#else
static int gpmc_probe_nand_child(struct platform_device *pdev,
				 struct device_node *child)
{
	return 0;
}
#endif

#if IS_ENABLED(CONFIG_MTD_ONENAND)
#if IS_ENABLED(CONFIG_MTD_ONENAND)
static int gpmc_probe_onenand_child(struct platform_device *pdev,
static int gpmc_probe_onenand_child(struct platform_device *pdev,
				 struct device_node *child)
				 struct device_node *child)
@@ -2069,9 +1969,42 @@ static int gpmc_probe_generic_child(struct platform_device *pdev,
		goto err;
		goto err;
	}
	}


	ret = of_property_read_u32(child, "bank-width", &gpmc_s.device_width);
	if (of_node_cmp(child->name, "nand") == 0) {
		/* Warn about older DT blobs with no compatible property */
		if (!of_property_read_bool(child, "compatible")) {
			dev_warn(&pdev->dev,
				 "Incompatible NAND node: missing compatible");
			ret = -EINVAL;
			goto err;
		}
	}

	if (of_device_is_compatible(child, "ti,omap2-nand")) {
		/* NAND specific setup */
		val = of_get_nand_bus_width(child);
		switch (val) {
		case 8:
			gpmc_s.device_width = GPMC_DEVWIDTH_8BIT;
			break;
		case 16:
			gpmc_s.device_width = GPMC_DEVWIDTH_16BIT;
			break;
		default:
			dev_err(&pdev->dev, "%s: invalid 'nand-bus-width'\n",
				child->name);
			ret = -EINVAL;
			goto err;
		}

		/* disable write protect */
		gpmc_configure(GPMC_CONFIG_WP, 0);
		gpmc_s.device_nand = true;
	} else {
		ret = of_property_read_u32(child, "bank-width",
					   &gpmc_s.device_width);
		if (ret < 0)
		if (ret < 0)
			goto err;
			goto err;
	}


	gpmc_cs_show_timings(cs, "before gpmc_cs_program_settings");
	gpmc_cs_show_timings(cs, "before gpmc_cs_program_settings");
	ret = gpmc_cs_program_settings(cs, &gpmc_s);
	ret = gpmc_cs_program_settings(cs, &gpmc_s);
@@ -2155,9 +2088,7 @@ static int gpmc_probe_dt(struct platform_device *pdev)
		if (!child->name)
		if (!child->name)
			continue;
			continue;


		if (of_node_cmp(child->name, "nand") == 0)
		if (of_node_cmp(child->name, "onenand") == 0)
			ret = gpmc_probe_nand_child(pdev, child);
		else if (of_node_cmp(child->name, "onenand") == 0)
			ret = gpmc_probe_onenand_child(pdev, child);
			ret = gpmc_probe_onenand_child(pdev, child);
		else
		else
			ret = gpmc_probe_generic_child(pdev, child);
			ret = gpmc_probe_generic_child(pdev, child);
+114 −20
Original line number Original line Diff line number Diff line
@@ -24,6 +24,7 @@
#include <linux/slab.h>
#include <linux/slab.h>
#include <linux/of.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_device.h>
#include <linux/of_mtd.h>


#include <linux/mtd/nand_bch.h>
#include <linux/mtd/nand_bch.h>
#include <linux/platform_data/elm.h>
#include <linux/platform_data/elm.h>
@@ -176,11 +177,11 @@ struct omap_nand_info {
	/* Interface to GPMC */
	/* Interface to GPMC */
	struct gpmc_nand_regs		reg;
	struct gpmc_nand_regs		reg;
	struct gpmc_nand_ops		*ops;
	struct gpmc_nand_ops		*ops;
	bool				flash_bbt;
	/* generated at runtime depending on ECC algorithm and layout selected */
	/* generated at runtime depending on ECC algorithm and layout selected */
	struct nand_ecclayout		oobinfo;
	struct nand_ecclayout		oobinfo;
	/* fields specific for BCHx_HW ECC scheme */
	/* fields specific for BCHx_HW ECC scheme */
	struct device			*elm_dev;
	struct device			*elm_dev;
	struct device_node		*of_node;
};
};


static inline struct omap_nand_info *mtd_to_omap(struct mtd_info *mtd)
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;
	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)
static int omap_nand_probe(struct platform_device *pdev)
{
{
	struct omap_nand_info		*info;
	struct omap_nand_info		*info;
	struct omap_nand_platform_data	*pdata;
	struct omap_nand_platform_data	*pdata = NULL;
	struct mtd_info			*mtd;
	struct mtd_info			*mtd;
	struct nand_chip		*nand_chip;
	struct nand_chip		*nand_chip;
	struct nand_ecclayout		*ecclayout;
	struct nand_ecclayout		*ecclayout;
@@ -1656,39 +1733,47 @@ static int omap_nand_probe(struct platform_device *pdev)
	unsigned			sig;
	unsigned			sig;
	unsigned			oob_index;
	unsigned			oob_index;
	struct resource			*res;
	struct resource			*res;

	struct device			*dev = &pdev->dev;
	pdata = dev_get_platdata(&pdev->dev);
	if (pdata == NULL) {
		dev_err(&pdev->dev, "platform data missing\n");
		return -ENODEV;
	}


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


	platform_set_drvdata(pdev, info);
	info->pdev = pdev;


	info->ops = gpmc_omap_get_nand_ops(&info->reg, info->gpmc_cs);
	if (dev->of_node) {
	if (!info->ops) {
		if (omap_get_dt_info(dev, info))
		dev_err(&pdev->dev, "Failed to get GPMC->NAND interface\n");
			return -EINVAL;
		return -ENODEV;
	} else {
		pdata = dev_get_platdata(&pdev->dev);
		if (!pdata) {
			dev_err(&pdev->dev, "platform data missing\n");
			return -EINVAL;
		}
		}
	info->pdev		= pdev;

		info->gpmc_cs = pdata->cs;
		info->gpmc_cs = pdata->cs;
	info->of_node		= pdata->of_node;
		info->reg = pdata->reg;
		info->ecc_opt = pdata->ecc_opt;
		info->ecc_opt = pdata->ecc_opt;
		info->dev_ready	= pdata->dev_ready;
		info->dev_ready	= pdata->dev_ready;
		info->xfer_type = pdata->xfer_type;
		info->xfer_type = pdata->xfer_type;
		info->devsize = pdata->devsize;
		info->devsize = pdata->devsize;
		info->elm_of_node = pdata->elm_of_node;
		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;
	}


	nand_chip		= &info->nand;
	nand_chip		= &info->nand;
	mtd			= nand_to_mtd(nand_chip);
	mtd			= nand_to_mtd(nand_chip);
	mtd->dev.parent		= &pdev->dev;
	mtd->dev.parent		= &pdev->dev;
	nand_chip->ecc.priv	= NULL;
	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);
	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	nand_chip->IO_ADDR_R = devm_ioremap_resource(&pdev->dev, res);
	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;
		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;
		nand_chip->bbt_options |= NAND_BBT_USE_FLASH | NAND_BBT_NO_OOB;
	else
	else
		nand_chip->options |= NAND_SKIP_BBTSCAN;
		nand_chip->options |= NAND_SKIP_BBTSCAN;
@@ -2035,6 +2120,9 @@ static int omap_nand_probe(struct platform_device *pdev)
		goto return_error;
		goto return_error;
	}
	}


	if (dev->of_node)
		mtd_device_register(mtd, NULL, 0);
	else
		mtd_device_register(mtd, pdata->parts, pdata->nr_parts);
		mtd_device_register(mtd, pdata->parts, pdata->nr_parts);


	platform_set_drvdata(pdev, mtd);
	platform_set_drvdata(pdev, mtd);
@@ -2066,11 +2154,17 @@ static int omap_nand_remove(struct platform_device *pdev)
	return 0;
	return 0;
}
}


static const struct of_device_id omap_nand_ids[] = {
	{ .compatible = "ti,omap2-nand", },
	{},
};

static struct platform_driver omap_nand_driver = {
static struct platform_driver omap_nand_driver = {
	.probe		= omap_nand_probe,
	.probe		= omap_nand_probe,
	.remove		= omap_nand_remove,
	.remove		= omap_nand_remove,
	.driver		= {
	.driver		= {
		.name	= DRIVER_NAME,
		.name	= DRIVER_NAME,
		.of_match_table = of_match_ptr(omap_nand_ids),
	},
	},
};
};


+1 −2
Original line number Original line Diff line number Diff line
@@ -76,11 +76,10 @@ struct omap_nand_platform_data {
	int			devsize;
	int			devsize;
	enum omap_ecc           ecc_opt;
	enum omap_ecc           ecc_opt;


	/* for passing the partitions */
	struct device_node	*of_node;
	struct device_node	*elm_of_node;
	struct device_node	*elm_of_node;


	/* deprecated */
	/* deprecated */
	struct gpmc_nand_regs	reg;
	struct gpmc_nand_regs	reg;
	struct device_node	*of_node;
};
};
#endif
#endif