diff options
Diffstat (limited to 'arch/arm/mach-omap2')
| -rw-r--r-- | arch/arm/mach-omap2/gpmc-nand.c | 15 | ||||
| -rw-r--r-- | arch/arm/mach-omap2/gpmc-onenand.c | 2 | ||||
| -rw-r--r-- | arch/arm/mach-omap2/gpmc.c | 232 | 
3 files changed, 240 insertions, 9 deletions
diff --git a/arch/arm/mach-omap2/gpmc-nand.c b/arch/arm/mach-omap2/gpmc-nand.c index db969a5c499..afc1e8c32d6 100644 --- a/arch/arm/mach-omap2/gpmc-nand.c +++ b/arch/arm/mach-omap2/gpmc-nand.c @@ -89,20 +89,21 @@ static int omap2_nand_gpmc_retime(  	return 0;  } -static bool __init gpmc_hwecc_bch_capable(enum omap_ecc ecc_opt) +static bool gpmc_hwecc_bch_capable(enum omap_ecc ecc_opt)  {  	/* support only OMAP3 class */ -	if (!cpu_is_omap34xx()) { +	if (!cpu_is_omap34xx() && !soc_is_am33xx()) {  		pr_err("BCH ecc is not supported on this CPU\n");  		return 0;  	}  	/* -	 * For now, assume 4-bit mode is only supported on OMAP3630 ES1.x, x>=1. -	 * Other chips may be added if confirmed to work. +	 * For now, assume 4-bit mode is only supported on OMAP3630 ES1.x, x>=1 +	 * and AM33xx derivates. Other chips may be added if confirmed to work.  	 */  	if ((ecc_opt == OMAP_ECC_BCH4_CODE_HW) && -	    (!cpu_is_omap3630() || (GET_OMAP_REVISION() == 0))) { +	    (!cpu_is_omap3630() || (GET_OMAP_REVISION() == 0)) && +	    (!soc_is_am33xx())) {  		pr_err("BCH 4-bit mode is not supported on this CPU\n");  		return 0;  	} @@ -110,8 +111,8 @@ static bool __init gpmc_hwecc_bch_capable(enum omap_ecc ecc_opt)  	return 1;  } -int __init gpmc_nand_init(struct omap_nand_platform_data *gpmc_nand_data, -			  struct gpmc_timings *gpmc_t) +int gpmc_nand_init(struct omap_nand_platform_data *gpmc_nand_data, +		   struct gpmc_timings *gpmc_t)  {  	int err	= 0;  	struct device *dev = &gpmc_nand_device.dev; diff --git a/arch/arm/mach-omap2/gpmc-onenand.c b/arch/arm/mach-omap2/gpmc-onenand.c index 94a349e4dc9..fadd87435cd 100644 --- a/arch/arm/mach-omap2/gpmc-onenand.c +++ b/arch/arm/mach-omap2/gpmc-onenand.c @@ -356,7 +356,7 @@ static int gpmc_onenand_setup(void __iomem *onenand_base, int *freq_ptr)  	return ret;  } -void __init gpmc_onenand_init(struct omap_onenand_platform_data *_onenand_data) +void gpmc_onenand_init(struct omap_onenand_platform_data *_onenand_data)  {  	int err; diff --git a/arch/arm/mach-omap2/gpmc.c b/arch/arm/mach-omap2/gpmc.c index 64bac53da0e..03d771349be 100644 --- a/arch/arm/mach-omap2/gpmc.c +++ b/arch/arm/mach-omap2/gpmc.c @@ -25,6 +25,10 @@  #include <linux/module.h>  #include <linux/interrupt.h>  #include <linux/platform_device.h> +#include <linux/of.h> +#include <linux/of_mtd.h> +#include <linux/of_device.h> +#include <linux/mtd/nand.h>  #include <linux/platform_data/mtd-nand-omap2.h> @@ -34,6 +38,8 @@  #include "common.h"  #include "omap_device.h"  #include "gpmc.h" +#include "gpmc-nand.h" +#include "gpmc-onenand.h"  #define	DEVICE_NAME		"omap-gpmc" @@ -145,7 +151,8 @@ static unsigned gpmc_irq_start;  static struct resource	gpmc_mem_root;  static struct resource	gpmc_cs_mem[GPMC_CS_NUM];  static DEFINE_SPINLOCK(gpmc_mem_lock); -static unsigned int gpmc_cs_map;	/* flag for cs which are initialized */ +/* Define chip-selects as reserved by default until probe completes */ +static unsigned int gpmc_cs_map = ((1 << GPMC_CS_NUM) - 1);  static struct device *gpmc_dev;  static int gpmc_irq;  static resource_size_t phys_base, mem_size; @@ -1118,8 +1125,215 @@ int gpmc_calc_timings(struct gpmc_timings *gpmc_t,  	/* TODO: remove, see function definition */  	gpmc_convert_ps_to_ns(gpmc_t); +	/* Now the GPMC is initialised, unreserve the chip-selects */ +	gpmc_cs_map = 0; + +	return 0; +} + +#ifdef CONFIG_OF +static struct of_device_id gpmc_dt_ids[] = { +	{ .compatible = "ti,omap2420-gpmc" }, +	{ .compatible = "ti,omap2430-gpmc" }, +	{ .compatible = "ti,omap3430-gpmc" },	/* omap3430 & omap3630 */ +	{ .compatible = "ti,omap4430-gpmc" },	/* omap4430 & omap4460 & omap543x */ +	{ .compatible = "ti,am3352-gpmc" },	/* am335x devices */ +	{ } +}; +MODULE_DEVICE_TABLE(of, gpmc_dt_ids); + +static void __maybe_unused gpmc_read_timings_dt(struct device_node *np, +						struct gpmc_timings *gpmc_t) +{ +	u32 val; + +	memset(gpmc_t, 0, sizeof(*gpmc_t)); + +	/* minimum clock period for syncronous mode */ +	if (!of_property_read_u32(np, "gpmc,sync-clk", &val)) +		gpmc_t->sync_clk = val; + +	/* chip select timtings */ +	if (!of_property_read_u32(np, "gpmc,cs-on", &val)) +		gpmc_t->cs_on = val; + +	if (!of_property_read_u32(np, "gpmc,cs-rd-off", &val)) +		gpmc_t->cs_rd_off = val; + +	if (!of_property_read_u32(np, "gpmc,cs-wr-off", &val)) +		gpmc_t->cs_wr_off = val; + +	/* ADV signal timings */ +	if (!of_property_read_u32(np, "gpmc,adv-on", &val)) +		gpmc_t->adv_on = val; + +	if (!of_property_read_u32(np, "gpmc,adv-rd-off", &val)) +		gpmc_t->adv_rd_off = val; + +	if (!of_property_read_u32(np, "gpmc,adv-wr-off", &val)) +		gpmc_t->adv_wr_off = val; + +	/* WE signal timings */ +	if (!of_property_read_u32(np, "gpmc,we-on", &val)) +		gpmc_t->we_on = val; + +	if (!of_property_read_u32(np, "gpmc,we-off", &val)) +		gpmc_t->we_off = val; + +	/* OE signal timings */ +	if (!of_property_read_u32(np, "gpmc,oe-on", &val)) +		gpmc_t->oe_on = val; + +	if (!of_property_read_u32(np, "gpmc,oe-off", &val)) +		gpmc_t->oe_off = val; + +	/* access and cycle timings */ +	if (!of_property_read_u32(np, "gpmc,page-burst-access", &val)) +		gpmc_t->page_burst_access = val; + +	if (!of_property_read_u32(np, "gpmc,access", &val)) +		gpmc_t->access = val; + +	if (!of_property_read_u32(np, "gpmc,rd-cycle", &val)) +		gpmc_t->rd_cycle = val; + +	if (!of_property_read_u32(np, "gpmc,wr-cycle", &val)) +		gpmc_t->wr_cycle = val; + +	/* only for OMAP3430 */ +	if (!of_property_read_u32(np, "gpmc,wr-access", &val)) +		gpmc_t->wr_access = val; + +	if (!of_property_read_u32(np, "gpmc,wr-data-mux-bus", &val)) +		gpmc_t->wr_data_mux_bus = val; +} + +#ifdef CONFIG_MTD_NAND + +static const char * const nand_ecc_opts[] = { +	[OMAP_ECC_HAMMING_CODE_DEFAULT]		= "sw", +	[OMAP_ECC_HAMMING_CODE_HW]		= "hw", +	[OMAP_ECC_HAMMING_CODE_HW_ROMCODE]	= "hw-romcode", +	[OMAP_ECC_BCH4_CODE_HW]			= "bch4", +	[OMAP_ECC_BCH8_CODE_HW]			= "bch8", +}; + +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; + +	if (!of_property_read_string(child, "ti,nand-ecc-opt", &s)) +		for (val = 0; val < ARRAY_SIZE(nand_ecc_opts); val++) +			if (!strcasecmp(s, nand_ecc_opts[val])) { +				gpmc_nand_data->ecc_opt = val; +				break; +			} + +	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 + +#ifdef CONFIG_MTD_ONENAND +static int gpmc_probe_onenand_child(struct platform_device *pdev, +				 struct device_node *child) +{ +	u32 val; +	struct omap_onenand_platform_data *gpmc_onenand_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_onenand_data = devm_kzalloc(&pdev->dev, sizeof(*gpmc_onenand_data), +					 GFP_KERNEL); +	if (!gpmc_onenand_data) +		return -ENOMEM; + +	gpmc_onenand_data->cs = val; +	gpmc_onenand_data->of_node = child; +	gpmc_onenand_data->dma_channel = -1; + +	if (!of_property_read_u32(child, "dma-channel", &val)) +		gpmc_onenand_data->dma_channel = val; + +	gpmc_onenand_init(gpmc_onenand_data); + +	return 0; +} +#else +static int gpmc_probe_onenand_child(struct platform_device *pdev, +				    struct device_node *child) +{ +	return 0; +} +#endif + +static int gpmc_probe_dt(struct platform_device *pdev) +{ +	int ret; +	struct device_node *child; +	const struct of_device_id *of_id = +		of_match_device(gpmc_dt_ids, &pdev->dev); + +	if (!of_id) +		return 0; + +	for_each_node_by_name(child, "nand") { +		ret = gpmc_probe_nand_child(pdev, child); +		if (ret < 0) { +			of_node_put(child); +			return ret; +		} +	} + +	for_each_node_by_name(child, "onenand") { +		ret = gpmc_probe_onenand_child(pdev, child); +		if (ret < 0) { +			of_node_put(child); +			return ret; +		} +	} +	return 0; +} +#else +static int gpmc_probe_dt(struct platform_device *pdev) +{ +	return 0; +} +#endif  static int gpmc_probe(struct platform_device *pdev)  { @@ -1172,6 +1386,14 @@ static int gpmc_probe(struct platform_device *pdev)  	if (IS_ERR_VALUE(gpmc_setup_irq()))  		dev_warn(gpmc_dev, "gpmc_setup_irq failed\n"); +	rc = gpmc_probe_dt(pdev); +	if (rc < 0) { +		clk_disable_unprepare(gpmc_l3_clk); +		clk_put(gpmc_l3_clk); +		dev_err(gpmc_dev, "failed to probe DT parameters\n"); +		return rc; +	} +  	return 0;  } @@ -1189,6 +1411,7 @@ static struct platform_driver gpmc_driver = {  	.driver		= {  		.name	= DEVICE_NAME,  		.owner	= THIS_MODULE, +		.of_match_table = of_match_ptr(gpmc_dt_ids),  	},  }; @@ -1212,6 +1435,13 @@ static int __init omap_gpmc_init(void)  	struct platform_device *pdev;  	char *oh_name = "gpmc"; +	/* +	 * if the board boots up with a populated DT, do not +	 * manually add the device from this initcall +	 */ +	if (of_have_populated_dt()) +		return -ENODEV; +  	oh = omap_hwmod_lookup(oh_name);  	if (!oh) {  		pr_err("Could not look up %s\n", oh_name);  |