diff options
author | Olof Johansson <olof@lixom.net> | 2013-02-06 02:03:31 +0400 |
---|---|---|
committer | Olof Johansson <olof@lixom.net> | 2013-02-06 02:03:45 +0400 |
commit | b6a03d0492dedb5c10b8a5708ee92e04b0590c07 (patch) | |
tree | 21774ee93c50847fe1ea044f2cabae0746ac39d3 /arch | |
parent | 3e93093ecd0c144e86a86cc1f165221b6cd3e7fb (diff) | |
parent | 97c794a1e37b1ca128ef38f17c069186bfa5fb1b (diff) | |
download | linux-b6a03d0492dedb5c10b8a5708ee92e04b0590c07.tar.xz |
Merge tag 'omap-for-v3.9/gpmc-signed' of git://git.kernel.org/pub/scm/linux/kernel/git/tmlind/linux-omap into next/drivers
From Tony Lindgren:
OMAP GPMC (General Purpose Memory Controller) changes to add
device tree bindings.
* tag 'omap-for-v3.9/gpmc-signed' of git://git.kernel.org/pub/scm/linux/kernel/git/tmlind/linux-omap:
ARM: OMAP2+: gpmc: Add device tree documentation for elm handle
ARM: OMAP2+: gpmc: add DT bindings for OneNAND
ARM: OMAP2+: gpmc-onenand: drop __init annotation
mtd: omap-onenand: pass device_node in platform data
ARM: OMAP2+: Prevent potential crash if GPMC probe fails
ARM: OMAP2+: gpmc: Remove unneeded of_node_put()
ARM: OMAP: gpmc: add DT bindings for GPMC timings and NAND
ARM: OMAP: gpmc: enable hwecc for AM33xx SoCs
ARM: OMAP: gpmc-nand: drop __init annotation
mtd: omap-nand: pass device_node in platform data
ARM: OMAP: gpmc: don't create devices from initcall on DT
Signed-off-by: Olof Johansson <olof@lixom.net>
Diffstat (limited to 'arch')
-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 db969a5c4998..afc1e8c32d6c 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 94a349e4dc96..fadd87435cd0 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 8033cb747c86..1adb2d4496f6 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,9 +1125,216 @@ 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) { int rc; @@ -1174,6 +1388,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; } @@ -1191,6 +1413,7 @@ static struct platform_driver gpmc_driver = { .driver = { .name = DEVICE_NAME, .owner = THIS_MODULE, + .of_match_table = of_match_ptr(gpmc_dt_ids), }, }; @@ -1214,6 +1437,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); |