diff options
Diffstat (limited to 'arch/arm/mach-omap2/gpmc.c')
| -rw-r--r-- | arch/arm/mach-omap2/gpmc.c | 257 | 
1 files changed, 241 insertions, 16 deletions
diff --git a/arch/arm/mach-omap2/gpmc.c b/arch/arm/mach-omap2/gpmc.c index 8033cb747c8..6de31739b45 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; @@ -709,7 +716,7 @@ static int gpmc_setup_irq(void)  		return -EINVAL;  	gpmc_irq_start = irq_alloc_descs(-1, 0, GPMC_NR_IRQ, 0); -	if (IS_ERR_VALUE(gpmc_irq_start)) { +	if (gpmc_irq_start < 0) {  		pr_err("irq_alloc_descs failed\n");  		return gpmc_irq_start;  	} @@ -783,9 +790,6 @@ static int gpmc_mem_init(void)  	 * even if we didn't boot from ROM.  	 */  	boot_rom_space = BOOT_ROM_SPACE; -	/* In apollon the CS0 is mapped as 0x0000 0000 */ -	if (machine_is_omap_apollon()) -		boot_rom_space = 0;  	gpmc_mem_root.start = GPMC_MEM_START + boot_rom_space;  	gpmc_mem_root.end = GPMC_MEM_END; @@ -797,7 +801,7 @@ static int gpmc_mem_init(void)  			continue;  		gpmc_cs_get_memconf(cs, &base, &size);  		rc = gpmc_cs_insert_mem(cs, base, size); -		if (IS_ERR_VALUE(rc)) { +		if (rc < 0) {  			while (--cs >= 0)  				if (gpmc_cs_mem_enabled(cs))  					gpmc_cs_delete_mem(cs); @@ -1121,6 +1125,210 @@ int gpmc_calc_timings(struct gpmc_timings *gpmc_t,  	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)  {  	int rc; @@ -1134,11 +1342,9 @@ static int gpmc_probe(struct platform_device *pdev)  	phys_base = res->start;  	mem_size = resource_size(res); -	gpmc_base = devm_request_and_ioremap(&pdev->dev, res); -	if (!gpmc_base) { -		dev_err(&pdev->dev, "error: request memory / ioremap\n"); -		return -EADDRNOTAVAIL; -	} +	gpmc_base = devm_ioremap_resource(&pdev->dev, res); +	if (IS_ERR(gpmc_base)) +		return PTR_ERR(gpmc_base);  	res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);  	if (res == NULL) @@ -1164,16 +1370,27 @@ static int gpmc_probe(struct platform_device *pdev)  		 GPMC_REVISION_MINOR(l));  	rc = gpmc_mem_init(); -	if (IS_ERR_VALUE(rc)) { +	if (rc < 0) {  		clk_disable_unprepare(gpmc_l3_clk);  		clk_put(gpmc_l3_clk);  		dev_err(gpmc_dev, "failed to reserve memory\n");  		return rc;  	} -	if (IS_ERR_VALUE(gpmc_setup_irq())) +	if (gpmc_setup_irq() < 0)  		dev_warn(gpmc_dev, "gpmc_setup_irq failed\n"); +	/* Now the GPMC is initialised, unreserve the chip-selects */ +	gpmc_cs_map = 0; + +	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;  } @@ -1191,6 +1408,7 @@ static struct platform_driver gpmc_driver = {  	.driver		= {  		.name	= DEVICE_NAME,  		.owner	= THIS_MODULE, +		.of_match_table = of_match_ptr(gpmc_dt_ids),  	},  }; @@ -1205,7 +1423,7 @@ static __exit void gpmc_exit(void)  } -postcore_initcall(gpmc_init); +omap_postcore_initcall(gpmc_init);  module_exit(gpmc_exit);  static int __init omap_gpmc_init(void) @@ -1214,18 +1432,25 @@ 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);  		return -ENODEV;  	} -	pdev = omap_device_build(DEVICE_NAME, -1, oh, NULL, 0, NULL, 0, 0); +	pdev = omap_device_build(DEVICE_NAME, -1, oh, NULL, 0);  	WARN(IS_ERR(pdev), "could not build omap_device for %s\n", oh_name);  	return IS_ERR(pdev) ? PTR_ERR(pdev) : 0;  } -postcore_initcall(omap_gpmc_init); +omap_postcore_initcall(omap_gpmc_init);  static irqreturn_t gpmc_handle_irq(int irq, void *dev)  {  |