diff options
author | Arnd Bergmann <arnd@arndb.de> | 2013-04-08 19:40:57 +0200 |
---|---|---|
committer | Arnd Bergmann <arnd@arndb.de> | 2013-04-08 19:40:57 +0200 |
commit | 86feb64f5bfe75da74f4a2faf18b5e57be9cde5e (patch) | |
tree | 410e5dbd7e23b32671cc297e52b42a0d6595f454 /arch | |
parent | dc2d3db8137fba0f62d7517e1bea8a47f69fcbc4 (diff) | |
parent | aecb65a34a780cd07fbaa652cc5d640f13f3ed5f (diff) | |
download | blackbird-op-linux-86feb64f5bfe75da74f4a2faf18b5e57be9cde5e.tar.gz blackbird-op-linux-86feb64f5bfe75da74f4a2faf18b5e57be9cde5e.zip |
Merge tag 'omap-for-v3.10/gpmc-signed' of git://git.kernel.org/pub/scm/linux/kernel/git/tmlind/linux-omap into next/drivers
From Tony Lindgren <tony@atomide.com>:
GPMC updates from Jon Hunter <jon-hunter@ti.com>:
Adds GPMC (General Purpose Memory Controller) DT support for
NOR flash and Ethernet and includes various GPMC cleans-up
and fixes.
This series is dependent on commit 7185684 (ARM: OMAP: use
consistent error checking) from RMK's clean-up branch and commit
31d9adc (ARM: OMAP2+: Fix broken gpmc support).
* tag 'omap-for-v3.10/gpmc-signed' of git://git.kernel.org/pub/scm/linux/kernel/git/tmlind/linux-omap: (29 commits)
ARM: OMAP2+: Add GPMC DT support for Ethernet child nodes
ARM: OMAP2+: rename gpmc_probe_nor_child() to gpmc_probe_generic_child()
ARM: OMAP2+: return -ENODEV if GPMC child device creation fails
ARM: OMAP2+: Allow GPMC probe to complete even if CS mapping fails
ARM: OMAP2+: Remove unnecesssary GPMC definitions and variable
ARM: OMAP2+: Detect incorrectly aligned GPMC base address
ARM: OMAP2+: Convert ONENAND to retrieve GPMC settings from DT
ARM: OMAP2+: Convert NAND to retrieve GPMC settings from DT
ARM: OMAP2+: Add device-tree support for NOR flash
ARM: OMAP2+: Add additional GPMC timing parameters
ARM: OMAP2+: Add function to read GPMC settings from device-tree
ARM: OMAP2+: Don't configure of chip-select options in gpmc_cs_configure()
ARM: OMAP2+: Convert TUSB to use gpmc_cs_program_settings()
ARM: OMAP2+: Convert SMC91x to use gpmc_cs_program_settings()
ARM: OMAP2+: Convert NAND to use gpmc_cs_program_settings()
ARM: OMAP2+: Convert ONENAND to use gpmc_cs_program_settings()
ARM: OMAP2+: Add function for configuring GPMC settings
ARM: OMAP2+: Add structure for storing GPMC settings
ARM: OMAP2+: Add variable to store number of GPMC waitpins
ARM: OMAP2+: Simplify code configuring ONENAND devices
...
Signed-off-by: Arnd Bergmann <arnd@arndb.de>
Diffstat (limited to 'arch')
-rw-r--r-- | arch/arm/mach-omap2/gpmc-nand.c | 42 | ||||
-rw-r--r-- | arch/arm/mach-omap2/gpmc-onenand.c | 118 | ||||
-rw-r--r-- | arch/arm/mach-omap2/gpmc-smc91x.c | 30 | ||||
-rw-r--r-- | arch/arm/mach-omap2/gpmc.c | 544 | ||||
-rw-r--r-- | arch/arm/mach-omap2/gpmc.h | 43 | ||||
-rw-r--r-- | arch/arm/mach-omap2/usb-tusb6010.c | 62 |
6 files changed, 558 insertions, 281 deletions
diff --git a/arch/arm/mach-omap2/gpmc-nand.c b/arch/arm/mach-omap2/gpmc-nand.c index afc1e8c32d6c..d9c27195caf0 100644 --- a/arch/arm/mach-omap2/gpmc-nand.c +++ b/arch/arm/mach-omap2/gpmc-nand.c @@ -74,14 +74,6 @@ static int omap2_nand_gpmc_retime( t.cs_wr_off = gpmc_t->cs_wr_off; t.wr_cycle = gpmc_t->wr_cycle; - /* Configure GPMC */ - if (gpmc_nand_data->devsize == NAND_BUSWIDTH_16) - gpmc_cs_configure(gpmc_nand_data->cs, GPMC_CONFIG_DEV_SIZE, 1); - else - gpmc_cs_configure(gpmc_nand_data->cs, GPMC_CONFIG_DEV_SIZE, 0); - gpmc_cs_configure(gpmc_nand_data->cs, - GPMC_CONFIG_DEV_TYPE, GPMC_DEVICETYPE_NAND); - gpmc_cs_configure(gpmc_nand_data->cs, GPMC_CONFIG_WP, 0); err = gpmc_cs_set_timings(gpmc_nand_data->cs, &t); if (err) return err; @@ -115,14 +107,18 @@ int gpmc_nand_init(struct omap_nand_platform_data *gpmc_nand_data, struct gpmc_timings *gpmc_t) { int err = 0; + struct gpmc_settings s; struct device *dev = &gpmc_nand_device.dev; + memset(&s, 0, sizeof(struct gpmc_settings)); + gpmc_nand_device.dev.platform_data = gpmc_nand_data; err = gpmc_cs_request(gpmc_nand_data->cs, NAND_IO_SIZE, (unsigned long *)&gpmc_nand_resource[0].start); if (err < 0) { - dev_err(dev, "Cannot request GPMC CS\n"); + dev_err(dev, "Cannot request GPMC CS %d, error %d\n", + gpmc_nand_data->cs, err); return err; } @@ -140,11 +136,31 @@ int gpmc_nand_init(struct omap_nand_platform_data *gpmc_nand_data, dev_err(dev, "Unable to set gpmc timings: %d\n", err); return err; } - } - /* Enable RD PIN Monitoring Reg */ - if (gpmc_nand_data->dev_ready) { - gpmc_cs_configure(gpmc_nand_data->cs, GPMC_CONFIG_RDY_BSY, 1); + if (gpmc_nand_data->of_node) { + gpmc_read_settings_dt(gpmc_nand_data->of_node, &s); + } else { + s.device_nand = true; + + /* Enable RD PIN Monitoring Reg */ + if (gpmc_nand_data->dev_ready) { + s.wait_on_read = true; + s.wait_on_write = true; + } + } + + if (gpmc_nand_data->devsize == NAND_BUSWIDTH_16) + s.device_width = GPMC_DEVWIDTH_16BIT; + else + s.device_width = GPMC_DEVWIDTH_8BIT; + + err = gpmc_cs_program_settings(gpmc_nand_data->cs, &s); + if (err < 0) + goto out_free_cs; + + err = gpmc_configure(GPMC_CONFIG_WP, 0); + if (err < 0) + goto out_free_cs; } gpmc_update_nand_reg(&gpmc_nand_data->reg, gpmc_nand_data->cs); diff --git a/arch/arm/mach-omap2/gpmc-onenand.c b/arch/arm/mach-omap2/gpmc-onenand.c index 0d75889c0a6f..64b5a8346982 100644 --- a/arch/arm/mach-omap2/gpmc-onenand.c +++ b/arch/arm/mach-omap2/gpmc-onenand.c @@ -47,11 +47,23 @@ static struct platform_device gpmc_onenand_device = { .resource = &gpmc_onenand_resource, }; -static struct gpmc_timings omap2_onenand_calc_async_timings(void) +static struct gpmc_settings onenand_async = { + .device_width = GPMC_DEVWIDTH_16BIT, + .mux_add_data = GPMC_MUX_AD, +}; + +static struct gpmc_settings onenand_sync = { + .burst_read = true, + .burst_wrap = true, + .burst_len = GPMC_BURST_16, + .device_width = GPMC_DEVWIDTH_16BIT, + .mux_add_data = GPMC_MUX_AD, + .wait_pin = 0, +}; + +static void omap2_onenand_calc_async_timings(struct gpmc_timings *t) { struct gpmc_device_timings dev_t; - struct gpmc_timings t; - const int t_cer = 15; const int t_avdp = 12; const int t_aavdh = 7; @@ -64,7 +76,6 @@ static struct gpmc_timings omap2_onenand_calc_async_timings(void) memset(&dev_t, 0, sizeof(dev_t)); - dev_t.mux = true; dev_t.t_avdp_r = max_t(int, t_avdp, t_cer) * 1000; dev_t.t_avdp_w = dev_t.t_avdp_r; dev_t.t_aavdh = t_aavdh * 1000; @@ -76,19 +87,7 @@ static struct gpmc_timings omap2_onenand_calc_async_timings(void) dev_t.t_wpl = t_wpl * 1000; dev_t.t_wph = t_wph * 1000; - gpmc_calc_timings(&t, &dev_t); - - return t; -} - -static int gpmc_set_async_mode(int cs, struct gpmc_timings *t) -{ - /* Configure GPMC for asynchronous read */ - gpmc_cs_write_reg(cs, GPMC_CS_CONFIG1, - GPMC_CONFIG1_DEVICESIZE_16 | - GPMC_CONFIG1_MUXADDDATA); - - return gpmc_cs_set_timings(cs, t); + gpmc_calc_timings(t, &onenand_async, &dev_t); } static void omap2_onenand_set_async_mode(void __iomem *onenand_base) @@ -158,12 +157,11 @@ static int omap2_onenand_get_freq(struct omap_onenand_platform_data *cfg, return freq; } -static struct gpmc_timings -omap2_onenand_calc_sync_timings(struct omap_onenand_platform_data *cfg, - int freq) +static void omap2_onenand_calc_sync_timings(struct gpmc_timings *t, + unsigned int flags, + int freq) { struct gpmc_device_timings dev_t; - struct gpmc_timings t; const int t_cer = 15; const int t_avdp = 12; const int t_cez = 20; /* max of t_cez, t_oez */ @@ -172,9 +170,9 @@ omap2_onenand_calc_sync_timings(struct omap_onenand_platform_data *cfg, int min_gpmc_clk_period, t_ces, t_avds, t_avdh, t_ach, t_aavdh, t_rdyo; int div, gpmc_clk_ns; - if (cfg->flags & ONENAND_SYNC_READ) + if (flags & ONENAND_SYNC_READ) onenand_flags = ONENAND_FLAG_SYNCREAD; - else if (cfg->flags & ONENAND_SYNC_READWRITE) + else if (flags & ONENAND_SYNC_READWRITE) onenand_flags = ONENAND_FLAG_SYNCREAD | ONENAND_FLAG_SYNCWRITE; switch (freq) { @@ -239,10 +237,11 @@ omap2_onenand_calc_sync_timings(struct omap_onenand_platform_data *cfg, /* Set synchronous read timings */ memset(&dev_t, 0, sizeof(dev_t)); - dev_t.mux = true; - dev_t.sync_read = true; + if (onenand_flags & ONENAND_FLAG_SYNCREAD) + onenand_sync.sync_read = true; if (onenand_flags & ONENAND_FLAG_SYNCWRITE) { - dev_t.sync_write = true; + onenand_sync.sync_write = true; + onenand_sync.burst_write = true; } else { dev_t.t_avdp_w = max(t_avdp, t_cer) * 1000; dev_t.t_wpl = t_wpl * 1000; @@ -265,32 +264,7 @@ omap2_onenand_calc_sync_timings(struct omap_onenand_platform_data *cfg, dev_t.cyc_aavdh_oe = 1; dev_t.t_rdyo = t_rdyo * 1000 + min_gpmc_clk_period; - gpmc_calc_timings(&t, &dev_t); - - return t; -} - -static int gpmc_set_sync_mode(int cs, struct gpmc_timings *t) -{ - unsigned sync_read = onenand_flags & ONENAND_FLAG_SYNCREAD; - unsigned sync_write = onenand_flags & ONENAND_FLAG_SYNCWRITE; - - /* Configure GPMC for synchronous read */ - gpmc_cs_write_reg(cs, GPMC_CS_CONFIG1, - GPMC_CONFIG1_WRAPBURST_SUPP | - GPMC_CONFIG1_READMULTIPLE_SUPP | - (sync_read ? GPMC_CONFIG1_READTYPE_SYNC : 0) | - (sync_write ? GPMC_CONFIG1_WRITEMULTIPLE_SUPP : 0) | - (sync_write ? GPMC_CONFIG1_WRITETYPE_SYNC : 0) | - GPMC_CONFIG1_PAGE_LEN(2) | - (cpu_is_omap34xx() ? 0 : - (GPMC_CONFIG1_WAIT_READ_MON | - GPMC_CONFIG1_WAIT_PIN_SEL(0))) | - GPMC_CONFIG1_DEVICESIZE_16 | - GPMC_CONFIG1_DEVICETYPE_NOR | - GPMC_CONFIG1_MUXADDDATA); - - return gpmc_cs_set_timings(cs, t); + gpmc_calc_timings(t, &onenand_sync, &dev_t); } static int omap2_onenand_setup_async(void __iomem *onenand_base) @@ -298,11 +272,19 @@ static int omap2_onenand_setup_async(void __iomem *onenand_base) struct gpmc_timings t; int ret; + if (gpmc_onenand_data->of_node) + gpmc_read_settings_dt(gpmc_onenand_data->of_node, + &onenand_async); + omap2_onenand_set_async_mode(onenand_base); - t = omap2_onenand_calc_async_timings(); + omap2_onenand_calc_async_timings(&t); + + ret = gpmc_cs_program_settings(gpmc_onenand_data->cs, &onenand_async); + if (ret < 0) + return ret; - ret = gpmc_set_async_mode(gpmc_onenand_data->cs, &t); + ret = gpmc_cs_set_timings(gpmc_onenand_data->cs, &t); if (ret < 0) return ret; @@ -322,9 +304,25 @@ static int omap2_onenand_setup_sync(void __iomem *onenand_base, int *freq_ptr) set_onenand_cfg(onenand_base); } - t = omap2_onenand_calc_sync_timings(gpmc_onenand_data, freq); + if (gpmc_onenand_data->of_node) { + gpmc_read_settings_dt(gpmc_onenand_data->of_node, + &onenand_sync); + } else { + /* + * FIXME: Appears to be legacy code from initial ONENAND commit. + * Unclear what boards this is for and if this can be removed. + */ + if (!cpu_is_omap34xx()) + onenand_sync.wait_on_read = true; + } - ret = gpmc_set_sync_mode(gpmc_onenand_data->cs, &t); + omap2_onenand_calc_sync_timings(&t, gpmc_onenand_data->flags, freq); + + ret = gpmc_cs_program_settings(gpmc_onenand_data->cs, &onenand_sync); + if (ret < 0) + return ret; + + ret = gpmc_cs_set_timings(gpmc_onenand_data->cs, &t); if (ret < 0) return ret; @@ -359,6 +357,7 @@ static int gpmc_onenand_setup(void __iomem *onenand_base, int *freq_ptr) void gpmc_onenand_init(struct omap_onenand_platform_data *_onenand_data) { int err; + struct device *dev = &gpmc_onenand_device.dev; gpmc_onenand_data = _onenand_data; gpmc_onenand_data->onenand_setup = gpmc_onenand_setup; @@ -366,7 +365,7 @@ void gpmc_onenand_init(struct omap_onenand_platform_data *_onenand_data) if (cpu_is_omap24xx() && (gpmc_onenand_data->flags & ONENAND_SYNC_READWRITE)) { - printk(KERN_ERR "Onenand using only SYNC_READ on 24xx\n"); + dev_warn(dev, "OneNAND using only SYNC_READ on 24xx\n"); gpmc_onenand_data->flags &= ~ONENAND_SYNC_READWRITE; gpmc_onenand_data->flags |= ONENAND_SYNC_READ; } @@ -379,7 +378,8 @@ void gpmc_onenand_init(struct omap_onenand_platform_data *_onenand_data) err = gpmc_cs_request(gpmc_onenand_data->cs, ONENAND_IO_SIZE, (unsigned long *)&gpmc_onenand_resource.start); if (err < 0) { - pr_err("%s: Cannot request GPMC CS\n", __func__); + dev_err(dev, "Cannot request GPMC CS %d, error %d\n", + gpmc_onenand_data->cs, err); return; } @@ -387,7 +387,7 @@ void gpmc_onenand_init(struct omap_onenand_platform_data *_onenand_data) ONENAND_IO_SIZE - 1; if (platform_device_register(&gpmc_onenand_device) < 0) { - pr_err("%s: Unable to register OneNAND device\n", __func__); + dev_err(dev, "Unable to register OneNAND device\n"); gpmc_cs_free(gpmc_onenand_data->cs); return; } diff --git a/arch/arm/mach-omap2/gpmc-smc91x.c b/arch/arm/mach-omap2/gpmc-smc91x.c index 11d0b756f098..61a063595e66 100644 --- a/arch/arm/mach-omap2/gpmc-smc91x.c +++ b/arch/arm/mach-omap2/gpmc-smc91x.c @@ -49,6 +49,10 @@ static struct platform_device gpmc_smc91x_device = { .resource = gpmc_smc91x_resources, }; +static struct gpmc_settings smc91x_settings = { + .device_width = GPMC_DEVWIDTH_16BIT, +}; + /* * Set the gpmc timings for smc91c96. The timings are taken * from the data sheet available at: @@ -67,18 +71,6 @@ static int smc91c96_gpmc_retime(void) const int t7 = 5; /* Figure 12.4 write */ const int t8 = 5; /* Figure 12.4 write */ const int t20 = 185; /* Figure 12.2 read and 12.4 write */ - u32 l; - - l = GPMC_CONFIG1_DEVICESIZE_16; - if (gpmc_cfg->flags & GPMC_MUX_ADD_DATA) - l |= GPMC_CONFIG1_MUXADDDATA; - if (gpmc_cfg->flags & GPMC_READ_MON) - l |= GPMC_CONFIG1_WAIT_READ_MON; - if (gpmc_cfg->flags & GPMC_WRITE_MON) - l |= GPMC_CONFIG1_WAIT_WRITE_MON; - if (gpmc_cfg->wait_pin) - l |= GPMC_CONFIG1_WAIT_PIN_SEL(gpmc_cfg->wait_pin); - gpmc_cs_write_reg(gpmc_cfg->cs, GPMC_CS_CONFIG1, l); /* * FIXME: Calculate the address and data bus muxed timings. @@ -104,7 +96,7 @@ static int smc91c96_gpmc_retime(void) dev_t.t_cez_w = t4_w * 1000; dev_t.t_wr_cycle = (t20 - t3) * 1000; - gpmc_calc_timings(&t, &dev_t); + gpmc_calc_timings(&t, &smc91x_settings, &dev_t); return gpmc_cs_set_timings(gpmc_cfg->cs, &t); } @@ -133,6 +125,18 @@ void __init gpmc_smc91x_init(struct omap_smc91x_platform_data *board_data) gpmc_smc91x_resources[0].end = cs_mem_base + 0x30f; gpmc_smc91x_resources[1].flags |= (gpmc_cfg->flags & IRQF_TRIGGER_MASK); + if (gpmc_cfg->flags & GPMC_MUX_ADD_DATA) + smc91x_settings.mux_add_data = GPMC_MUX_AD; + if (gpmc_cfg->flags & GPMC_READ_MON) + smc91x_settings.wait_on_read = true; + if (gpmc_cfg->flags & GPMC_WRITE_MON) + smc91x_settings.wait_on_write = true; + if (gpmc_cfg->wait_pin) + smc91x_settings.wait_pin = gpmc_cfg->wait_pin; + ret = gpmc_cs_program_settings(gpmc_cfg->cs, &smc91x_settings); + if (ret < 0) + goto free1; + if (gpmc_cfg->retime) { ret = gpmc_cfg->retime(); if (ret != 0) diff --git a/arch/arm/mach-omap2/gpmc.c b/arch/arm/mach-omap2/gpmc.c index 6de31739b45c..ed946df5ad8a 100644 --- a/arch/arm/mach-omap2/gpmc.c +++ b/arch/arm/mach-omap2/gpmc.c @@ -26,6 +26,7 @@ #include <linux/interrupt.h> #include <linux/platform_device.h> #include <linux/of.h> +#include <linux/of_address.h> #include <linux/of_mtd.h> #include <linux/of_device.h> #include <linux/mtd/nand.h> @@ -91,9 +92,7 @@ #define GPMC_CS_SIZE 0x30 #define GPMC_BCH_SIZE 0x10 -#define GPMC_MEM_START 0x00000000 #define GPMC_MEM_END 0x3FFFFFFF -#define BOOT_ROM_SPACE 0x100000 /* 1MB */ #define GPMC_CHUNK_SHIFT 24 /* 16 MB */ #define GPMC_SECTION_SHIFT 28 /* 128 MB */ @@ -107,6 +106,9 @@ #define GPMC_HAS_WR_ACCESS 0x1 #define GPMC_HAS_WR_DATA_MUX_BUS 0x2 +#define GPMC_HAS_MUX_AAD 0x4 + +#define GPMC_NR_WAITPINS 4 /* XXX: Only NAND irq has been considered,currently these are the only ones used */ @@ -153,6 +155,7 @@ static struct resource gpmc_cs_mem[GPMC_CS_NUM]; static DEFINE_SPINLOCK(gpmc_mem_lock); /* Define chip-selects as reserved by default until probe completes */ static unsigned int gpmc_cs_map = ((1 << GPMC_CS_NUM) - 1); +static unsigned int gpmc_nr_waitpins; static struct device *gpmc_dev; static int gpmc_irq; static resource_size_t phys_base, mem_size; @@ -181,7 +184,7 @@ void gpmc_cs_write_reg(int cs, int idx, u32 val) __raw_writel(val, reg_addr); } -u32 gpmc_cs_read_reg(int cs, int idx) +static u32 gpmc_cs_read_reg(int cs, int idx) { void __iomem *reg_addr; @@ -190,7 +193,7 @@ u32 gpmc_cs_read_reg(int cs, int idx) } /* TODO: Add support for gpmc_fck to clock framework and use it */ -unsigned long gpmc_get_fclk_period(void) +static unsigned long gpmc_get_fclk_period(void) { unsigned long rate = clk_get_rate(gpmc_l3_clk); @@ -205,7 +208,7 @@ unsigned long gpmc_get_fclk_period(void) return rate; } -unsigned int gpmc_ns_to_ticks(unsigned int time_ns) +static unsigned int gpmc_ns_to_ticks(unsigned int time_ns) { unsigned long tick_ps; @@ -215,7 +218,7 @@ unsigned int gpmc_ns_to_ticks(unsigned int time_ns) return (time_ns * 1000 + tick_ps - 1) / tick_ps; } -unsigned int gpmc_ps_to_ticks(unsigned int time_ps) +static unsigned int gpmc_ps_to_ticks(unsigned int time_ps) { unsigned long tick_ps; @@ -230,13 +233,6 @@ unsigned int gpmc_ticks_to_ns(unsigned int ticks) return ticks * gpmc_get_fclk_period() / 1000; } -unsigned int gpmc_round_ns_to_ticks(unsigned int time_ns) -{ - unsigned long ticks = gpmc_ns_to_ticks(time_ns); - - return ticks * gpmc_get_fclk_period() / 1000; -} - static unsigned int gpmc_ticks_to_ps(unsigned int ticks) { return ticks * gpmc_get_fclk_period(); @@ -405,11 +401,18 @@ int gpmc_cs_set_timings(int cs, const struct gpmc_timings *t) return 0; } -static void gpmc_cs_enable_mem(int cs, u32 base, u32 size) +static int gpmc_cs_enable_mem(int cs, u32 base, u32 size) { u32 l; u32 mask; + /* + * Ensure that base address is aligned on a + * boundary equal to or greater than size. + */ + if (base & (size - 1)) + return -EINVAL; + mask = (1 << GPMC_SECTION_SHIFT) - size; l = gpmc_cs_read_reg(cs, GPMC_CS_CONFIG7); l &= ~0x3f; @@ -418,6 +421,8 @@ static void gpmc_cs_enable_mem(int cs, u32 base, u32 size) l |= ((mask >> GPMC_CHUNK_SHIFT) & 0x0f) << 8; l |= GPMC_CONFIG7_CSVALID; gpmc_cs_write_reg(cs, GPMC_CS_CONFIG7, l); + + return 0; } static void gpmc_cs_disable_mem(int cs) @@ -448,22 +453,14 @@ static int gpmc_cs_mem_enabled(int cs) return l & GPMC_CONFIG7_CSVALID; } -int gpmc_cs_set_reserved(int cs, int reserved) +static void gpmc_cs_set_reserved(int cs, int reserved) { - if (cs > GPMC_CS_NUM) - return -ENODEV; - gpmc_cs_map &= ~(1 << cs); gpmc_cs_map |= (reserved ? 1 : 0) << cs; - - return 0; } -int gpmc_cs_reserved(int cs) +static bool gpmc_cs_reserved(int cs) { - if (cs > GPMC_CS_NUM) - return -ENODEV; - return gpmc_cs_map & (1 << cs); } @@ -510,6 +507,39 @@ static int gpmc_cs_delete_mem(int cs) return r; } +/** + * gpmc_cs_remap - remaps a chip-select physical base address + * @cs: chip-select to remap + * @base: physical base address to re-map chip-select to + * + * Re-maps a chip-select to a new physical base address specified by + * "base". Returns 0 on success and appropriate negative error code + * on failure. + */ +static int gpmc_cs_remap(int cs, u32 base) +{ + int ret; + u32 old_base, size; + + if (cs > GPMC_CS_NUM) + return -ENODEV; + gpmc_cs_get_memconf(cs, &old_base, &size); + if (base == old_base) + return 0; + gpmc_cs_disable_mem(cs); + ret = gpmc_cs_delete_mem(cs); + if (ret < 0) + return ret; + ret = gpmc_cs_insert_mem(cs, base, size); + if (ret < 0) + return ret; + ret = gpmc_cs_enable_mem(cs, base, size); + if (ret < 0) + return ret; + + return 0; +} + int gpmc_cs_request(int cs, unsigned long size, unsigned long *base) { struct resource *res = &gpmc_cs_mem[cs]; @@ -535,7 +565,12 @@ int gpmc_cs_request(int cs, unsigned long size, unsigned long *base) if (r < 0) goto out; - gpmc_cs_enable_mem(cs, res->start, resource_size(res)); + r = gpmc_cs_enable_mem(cs, res->start, resource_size(res)); + if (r < 0) { + release_resource(res); + goto out; + } + *base = res->start; gpmc_cs_set_reserved(cs, 1); out: @@ -561,16 +596,14 @@ void gpmc_cs_free(int cs) EXPORT_SYMBOL(gpmc_cs_free); /** - * gpmc_cs_configure - write request to configure gpmc - * @cs: chip select number + * gpmc_configure - write request to configure gpmc * @cmd: command type * @wval: value to write * @return status of the operation */ -int gpmc_cs_configure(int cs, int cmd, int wval) +int gpmc_configure(int cmd, int wval) { - int err = 0; - u32 regval = 0; + u32 regval; switch (cmd) { case GPMC_ENABLE_IRQ: @@ -590,43 +623,14 @@ int gpmc_cs_configure(int cs, int cmd, int wval) gpmc_write_reg(GPMC_CONFIG, regval); break; - case GPMC_CONFIG_RDY_BSY: - regval = gpmc_cs_read_reg(cs, GPMC_CS_CONFIG1); - if (wval) - regval |= WR_RD_PIN_MONITORING; - else - regval &= ~WR_RD_PIN_MONITORING; - gpmc_cs_write_reg(cs, GPMC_CS_CONFIG1, regval); - break; - - case GPMC_CONFIG_DEV_SIZE: - regval = gpmc_cs_read_reg(cs, GPMC_CS_CONFIG1); - - /* clear 2 target bits */ - regval &= ~GPMC_CONFIG1_DEVICESIZE(3); - - /* set the proper value */ - regval |= GPMC_CONFIG1_DEVICESIZE(wval); - - gpmc_cs_write_reg(cs, GPMC_CS_CONFIG1, regval); - break; - - case GPMC_CONFIG_DEV_TYPE: - regval = gpmc_cs_read_reg(cs, GPMC_CS_CONFIG1); - regval |= GPMC_CONFIG1_DEVICETYPE(wval); - if (wval == GPMC_DEVICETYPE_NOR) - regval |= GPMC_CONFIG1_MUXADDDATA; - gpmc_cs_write_reg(cs, GPMC_CS_CONFIG1, regval); - break; - default: - printk(KERN_ERR "gpmc_configure_cs: Not supported\n"); - err = -EINVAL; + pr_err("%s: command not supported\n", __func__); + return -EINVAL; } - return err; + return 0; } -EXPORT_SYMBOL(gpmc_cs_configure); +EXPORT_SYMBOL(gpmc_configure); void gpmc_update_nand_reg(struct gpmc_nand_regs *reg, int cs) { @@ -781,16 +785,16 @@ static void gpmc_mem_exit(void) } -static int gpmc_mem_init(void) +static void gpmc_mem_init(void) { - int cs, rc; - unsigned long boot_rom_space = 0; + int cs; - /* never allocate the first page, to facilitate bug detection; - * even if we didn't boot from ROM. + /* + * The first 1MB of GPMC address space is typically mapped to + * the internal ROM. Never allocate the first page, to + * facilitate bug detection; even if we didn't boot from ROM. */ - boot_rom_space = BOOT_ROM_SPACE; - gpmc_mem_root.start = GPMC_MEM_START + boot_rom_space; + gpmc_mem_root.start = SZ_1M; gpmc_mem_root.end = GPMC_MEM_END; /* Reserve all regions that has been set up by bootloader */ @@ -800,16 +804,12 @@ static int gpmc_mem_init(void) if (!gpmc_cs_mem_enabled(cs)) continue; gpmc_cs_get_memconf(cs, &base, &size); - rc = gpmc_cs_insert_mem(cs, base, size); - if (rc < 0) { - while (--cs >= 0) - if (gpmc_cs_mem_enabled(cs)) - gpmc_cs_delete_mem(cs); - return rc; + if (gpmc_cs_insert_mem(cs, base, size)) { + pr_warn("%s: disabling cs %d mapped at 0x%x-0x%x\n", + __func__, cs, base, base + size); + gpmc_cs_disable_mem(cs); } } - - return 0; } static u32 gpmc_round_ps_to_sync_clk(u32 time_ps, u32 sync_clk) @@ -825,9 +825,9 @@ static u32 gpmc_round_ps_to_sync_clk(u32 time_ps, u32 sync_clk) /* XXX: can the cycles be avoided ? */ static int gpmc_calc_sync_read_timings(struct gpmc_timings *gpmc_t, - struct gpmc_device_timings *dev_t) + struct gpmc_device_timings *dev_t, + bool mux) { - bool mux = dev_t->mux; u32 temp; /* adv_rd_off */ @@ -880,9 +880,9 @@ static int gpmc_calc_sync_read_timings(struct gpmc_timings *gpmc_t, } static int gpmc_calc_sync_write_timings(struct gpmc_timings *gpmc_t, - struct gpmc_device_timings *dev_t) + struct gpmc_device_timings *dev_t, + bool mux) { - bool mux = dev_t->mux; u32 temp; /* adv_wr_off */ @@ -942,9 +942,9 @@ static int gpmc_calc_sync_write_timings(struct gpmc_timings *gpmc_t, } static int gpmc_calc_async_read_timings(struct gpmc_timings *gpmc_t, - struct gpmc_device_timings *dev_t) + struct gpmc_device_timings *dev_t, + bool mux) { - bool mux = dev_t->mux; u32 temp; /* adv_rd_off */ @@ -982,9 +982,9 @@ static int gpmc_calc_async_read_timings(struct gpmc_timings *gpmc_t, } static int gpmc_calc_async_write_timings(struct gpmc_timings *gpmc_t, - struct gpmc_device_timings *dev_t) + struct gpmc_device_timings *dev_t, + bool mux) { - bool mux = dev_t->mux; u32 temp; /* adv_wr_off */ @@ -1054,7 +1054,8 @@ static int gpmc_calc_sync_common_timings(struct gpmc_timings *gpmc_t, } static int gpmc_calc_common_timings(struct gpmc_timings *gpmc_t, - struct gpmc_device_timings *dev_t) + struct gpmc_device_timings *dev_t, + bool sync) { u32 temp; @@ -1068,7 +1069,7 @@ static int gpmc_calc_common_timings(struct gpmc_timings *gpmc_t, gpmc_t->cs_on + dev_t->t_ce_avd); gpmc_t->adv_on = gpmc_round_ps_to_ticks(temp); - if (dev_t->sync_write || dev_t->sync_read) + if (sync) gpmc_calc_sync_common_timings(gpmc_t, dev_t); return 0; @@ -1103,21 +1104,29 @@ static void gpmc_convert_ps_to_ns(struct gpmc_timings *t) } int gpmc_calc_timings(struct gpmc_timings *gpmc_t, - struct gpmc_device_timings *dev_t) + struct gpmc_settings *gpmc_s, + struct gpmc_device_timings *dev_t) { + bool mux = false, sync = false; + + if (gpmc_s) { + mux = gpmc_s->mux_add_data ? true : false; + sync = (gpmc_s->sync_read || gpmc_s->sync_write); + } + memset(gpmc_t, 0, sizeof(*gpmc_t)); - gpmc_calc_common_timings(gpmc_t, dev_t); + gpmc_calc_common_timings(gpmc_t, dev_t, sync); - if (dev_t->sync_read) - gpmc_calc_sync_read_timings(gpmc_t, dev_t); + if (gpmc_s && gpmc_s->sync_read) + gpmc_calc_sync_read_timings(gpmc_t, dev_t, mux); else - gpmc_calc_async_read_timings(gpmc_t, dev_t); + gpmc_calc_async_read_timings(gpmc_t, dev_t, mux); - if (dev_t->sync_write) - gpmc_calc_sync_write_timings(gpmc_t, dev_t); + if (gpmc_s && gpmc_s->sync_write) + gpmc_calc_sync_write_timings(gpmc_t, dev_t, mux); else - gpmc_calc_async_write_timings(gpmc_t, dev_t); + gpmc_calc_async_write_timings(gpmc_t, dev_t, mux); /* TODO: remove, see function definition */ gpmc_convert_ps_to_ns(gpmc_t); @@ -1125,6 +1134,90 @@ int gpmc_calc_timings(struct gpmc_timings *gpmc_t, return 0; } +/** + * gpmc_cs_program_settings - programs non-timing related settings + * @cs: GPMC chip-select to program + * @p: pointer to GPMC settings structure + * + * Programs non-timing related settings for a GPMC chip-select, such as + * bus-width, burst configuration, etc. Function should be called once + * for each chip-select that is being used and must be called before + * calling gpmc_cs_set_timings() as timing parameters in the CONFIG1 + * register will be initialised to zero by this function. Returns 0 on + * success and appropriate negative error code on failure. + */ +int gpmc_cs_program_settings(int cs, struct gpmc_settings *p) +{ + u32 config1; + + if ((!p->device_width) || (p->device_width > GPMC_DEVWIDTH_16BIT)) { + pr_err("%s: invalid width %d!", __func__, p->device_width); + return -EINVAL; + } + + /* Address-data multiplexing not supported for NAND devices */ + if (p->device_nand && p->mux_add_data) { + pr_err("%s: invalid configuration!\n", __func__); + return -EINVAL; + } + + if ((p->mux_add_data > GPMC_MUX_AD) || + ((p->mux_add_data == GPMC_MUX_AAD) && + !(gpmc_capability & GPMC_HAS_MUX_AAD))) { + pr_err("%s: invalid multiplex configuration!\n", __func__); + return -EINVAL; + } + + /* Page/burst mode supports lengths of 4, 8 and 16 bytes */ + if (p->burst_read || p->burst_write) { + switch (p->burst_len) { + case GPMC_BURST_4: + case GPMC_BURST_8: + case GPMC_BURST_16: + break; + default: + pr_err("%s: invalid page/burst-length (%d)\n", + __func__, p->burst_len); + return -EINVAL; + } + } + + if ((p->wait_on_read || p->wait_on_write) && + (p->wait_pin > gpmc_nr_waitpins)) { + pr_err("%s: invalid wait-pin (%d)\n", __func__, p->wait_pin); + return -EINVAL; + } + + config1 = GPMC_CONFIG1_DEVICESIZE((p->device_width - 1)); + + if (p->sync_read) + config1 |= GPMC_CONFIG1_READTYPE_SYNC; + if (p->sync_write) + config1 |= GPMC_CONFIG1_WRITETYPE_SYNC; + if (p->wait_on_read) + config1 |= GPMC_CONFIG1_WAIT_READ_MON; + if (p->wait_on_write) + config1 |= GPMC_CONFIG1_WAIT_WRITE_MON; + if (p->wait_on_read || p->wait_on_write) + config1 |= GPMC_CONFIG1_WAIT_PIN_SEL(p->wait_pin); + if (p->device_nand) + config1 |= GPMC_CONFIG1_DEVICETYPE(GPMC_DEVICETYPE_NAND); + if (p->mux_add_data) + config1 |= GPMC_CONFIG1_MUXTYPE(p->mux_add_data); + if (p->burst_read) + config1 |= GPMC_CONFIG1_READMULTIPLE_SUPP; + if (p->burst_write) + config1 |= GPMC_CONFIG1_WRITEMULTIPLE_SUPP; + if (p->burst_read || p->burst_write) { + config1 |= GPMC_CONFIG1_PAGE_LEN(p->burst_len >> 3); + config1 |= p->burst_wrap ? GPMC_CONFIG1_WRAPBURST_SUPP : 0; + } + + gpmc_cs_write_reg(cs, GPMC_CS_CONFIG1, config1); + + return 0; +} + #ifdef CONFIG_OF static struct of_device_id gpmc_dt_ids[] = { { .compatible = "ti,omap2420-gpmc" }, @@ -1136,70 +1229,110 @@ static struct of_device_id gpmc_dt_ids[] = { }; MODULE_DEVICE_TABLE(of, gpmc_dt_ids); +/** + * gpmc_read_settings_dt - read gpmc settings from device-tree + * @np: pointer to device-tree node for a gpmc child device + * @p: pointer to gpmc settings structure + * + * Reads the GPMC settings for a GPMC child device from device-tree and + * stores them in the GPMC settings structure passed. The GPMC settings + * structure is initialised to zero by this function and so any + * previously stored settings will be cleared. + */ +void gpmc_read_settings_dt(struct device_node *np, struct gpmc_settings *p) +{ + memset(p, 0, sizeof(struct gpmc_settings)); + + p->sync_read = of_property_read_bool(np, "gpmc,sync-read"); + p->sync_write = of_property_read_bool(np, "gpmc,sync-write"); + p->device_nand = of_property_read_bool(np, "gpmc,device-nand"); + of_property_read_u32(np, "gpmc,device-width", &p->device_width); + of_property_read_u32(np, "gpmc,mux-add-data", &p->mux_add_data); + + if (!of_property_read_u32(np, "gpmc,burst-length", &p->burst_len)) { + p->burst_wrap = of_property_read_bool(np, "gpmc,burst-wrap"); + p->burst_read = of_property_read_bool(np, "gpmc,burst-read"); + p->burst_write = of_property_read_bool(np, "gpmc,burst-write"); + if (!p->burst_read && !p->burst_write) + pr_warn("%s: page/burst-length set but not used!\n", + __func__); + } + + if (!of_property_read_u32(np, "gpmc,wait-pin", &p->wait_pin)) { + p->wait_on_read = of_property_read_bool(np, + "gpmc,wait-on-read"); + p->wait_on_write = of_property_read_bool(np, + "gpmc,wait-on-write"); + if (!p->wait_on_read && !p->wait_on_write) + pr_warn("%s: read/write wait monitoring not enabled!\n", + __func__); + } +} + static void __maybe_unused gpmc_read_timings_dt(struct device_node *np, struct gpmc_timings *gpmc_t) { - u32 val; + struct gpmc_bool_timings *p; + + if (!np || !gpmc_t) + return; 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; + of_property_read_u32(np, "gpmc,sync-clk-ps", &gpmc_t->sync_clk); /* 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; + of_property_read_u32(np, "gpmc,cs-on-ns", &gpmc_t->cs_on); + of_property_read_u32(np, "gpmc,cs-rd-off-ns", &gpmc_t->cs_rd_off); + of_property_read_u32(np, "gpmc,cs-wr-off-ns", &gpmc_t->cs_wr_off); /* 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; + of_property_read_u32(np, "gpmc,adv-on-ns", &gpmc_t->adv_on); + of_property_read_u32(np, "gpmc,adv-rd-off-ns", &gpmc_t->adv_rd_off); + of_property_read_u32(np, "gpmc,adv-wr-off-ns", &gpmc_t->adv_wr_off); /* 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; + of_property_read_u32(np, "gpmc,we-on-ns", &gpmc_t->we_on); + of_property_read_u32(np, "gpmc,we-off-ns", &gpmc_t->we_off); /* 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; + of_property_read_u32(np, "gpmc,oe-on-ns", &gpmc_t->oe_on); + of_property_read_u32(np, "gpmc,oe-off-ns", &gpmc_t->oe_off); /* 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; + of_property_read_u32(np, "gpmc,page-burst-access-ns", + &gpmc_t->page_burst_access); + of_property_read_u32(np, "gpmc,access-ns", &gpmc_t->access); + of_property_read_u32(np, "gpmc,rd-cycle-ns", &gpmc_t->rd_cycle); + of_property_read_u32(np, "gpmc,wr-cycle-ns", &gpmc_t->wr_cycle); + of_property_read_u32(np, "gpmc,bus-turnaround-ns", + &gpmc_t->bus_turnaround); + of_property_read_u32(np, "gpmc,cycle2cycle-delay-ns", + &gpmc_t->cycle2cycle_delay); + of_property_read_u32(np, "gpmc,wait-monitoring-ns", + &gpmc_t->wait_monitoring); + of_property_read_u32(np, "gpmc,clk-activation-ns", + &gpmc_t->clk_activation); + + /* only applicable to OMAP3+ */ + of_property_read_u32(np, "gpmc,wr-access-ns", &gpmc_t->wr_access); + of_property_read_u32(np, "gpmc,wr-data-mux-bus-ns", + &gpmc_t->wr_data_mux_bus); + + /* bool timing parameters */ + p = &gpmc_t->bool_timings; + + p->cycle2cyclediffcsen = + of_property_read_bool(np, "gpmc,cycle2cycle-diffcsen"); + p->cycle2cyclesamecsen = + of_property_read_bool(np, "gpmc,cycle2cycle-samecsen"); + p->we_extra_delay = of_property_read_bool(np, "gpmc,we-extra-delay"); + p->oe_extra_delay = of_property_read_bool(np, "gpmc,oe-extra-delay"); + p->adv_extra_delay = of_property_read_bool(np, "gpmc,adv-extra-delay"); + p->cs_extra_delay = of_property_read_bool(np, "gpmc,cs-extra-delay"); + p->time_para_granularity = + of_property_read_bool(np, "gpmc,time-para-granularity"); } #ifdef CONFIG_MTD_NAND @@ -1295,6 +1428,81 @@ static int gpmc_probe_onenand_child(struct platform_device *pdev, } #endif +/** + * gpmc_probe_generic_child - configures the gpmc for a child device + * @pdev: pointer to gpmc platform device + * @child: pointer to device-tree node for child device + * + * Allocates and configures a GPMC chip-select for a child device. + * Returns 0 on success and appropriate negative error code on failure. + */ +static int gpmc_probe_generic_child(struct platform_device *pdev, + struct device_node *child) +{ + struct gpmc_settings gpmc_s; + struct gpmc_timings gpmc_t; + struct resource res; + unsigned long base; + int ret, cs; + + if (of_property_read_u32(child, "reg", &cs) < 0) { + dev_err(&pdev->dev, "%s has no 'reg' property\n", + child->full_name); + return -ENODEV; + } + + if (of_address_to_resource(child, 0, &res) < 0) { + dev_err(&pdev->dev, "%s has malformed 'reg' property\n", + child->full_name); + return -ENODEV; + } + + ret = gpmc_cs_request(cs, resource_size(&res), &base); + if (ret < 0) { + dev_err(&pdev->dev, "cannot request GPMC CS %d\n", cs); + return ret; + } + + /* + * FIXME: gpmc_cs_request() will map the CS to an arbitary + * location in the gpmc address space. When booting with + * device-tree we want the NOR flash to be mapped to the + * location specified in the device-tree blob. So remap the + * CS to this location. Once DT migration is complete should + * just make gpmc_cs_request() map a specific address. + */ + ret = gpmc_cs_remap(cs, res.start); + if (ret < 0) { + dev_err(&pdev->dev, "cannot remap GPMC CS %d to 0x%x\n", + cs, res.start); + goto err; + } + + gpmc_read_settings_dt(child, &gpmc_s); + + ret = of_property_read_u32(child, "bank-width", &gpmc_s.device_width); + if (ret < 0) + goto err; + + ret = gpmc_cs_program_settings(cs, &gpmc_s); + if (ret < 0) + goto err; + + gpmc_read_timings_dt(child, &gpmc_t); + gpmc_cs_set_timings(cs, &gpmc_t); + + if (of_platform_device_create(child, NULL, &pdev->dev)) + return 0; + + dev_err(&pdev->dev, "failed to create gpmc child %s\n", child->name); + ret = -ENODEV; + +err: + gpmc_cs_free(cs); + + return ret; +} + static int gpmc_probe_dt(struct platform_device *pdev) { int ret; @@ -1305,6 +1513,13 @@ static int gpmc_probe_dt(struct platform_device *pdev) if (!of_id) return 0; + ret = of_property_read_u32(pdev->dev.of_node, "gpmc,num-waitpins", + &gpmc_nr_waitpins); + if (ret < 0) { + pr_err("%s: number of wait pins not found!\n", __func__); + return ret; + } + for_each_node_by_name(child, "nand") { ret = gpmc_probe_nand_child(pdev, child); if (ret < 0) { @@ -1320,6 +1535,23 @@ static int gpmc_probe_dt(struct platform_device *pdev) return ret; } } + + for_each_node_by_name(child, "nor") { + ret = gpmc_probe_generic_child(pdev, child); + if (ret < 0) { + of_node_put(child); + return ret; + } + } + + for_each_node_by_name(child, "ethernet") { + ret = gpmc_probe_generic_child(pdev, child); + if (ret < 0) { + of_node_put(child); + return ret; + } + } + return 0; } #else @@ -1364,18 +1596,27 @@ static int gpmc_probe(struct platform_device *pdev) gpmc_dev = &pdev->dev; l = gpmc_read_reg(GPMC_REVISION); + + /* + * FIXME: Once device-tree migration is complete the below flags + * should be populated based upon the device-tree compatible + * string. For now just use the IP revision. OMAP3+ devices have + * the wr_access and wr_data_mux_bus register fields. OMAP4+ + * devices support the addr-addr-data multiplex protocol. + * + * GPMC IP revisions: + * - OMAP24xx = 2.0 + * - OMAP3xxx = 5.0 + * - OMAP44xx/54xx/AM335x = 6.0 + */ if (GPMC_REVISION_MAJOR(l) > 0x4) gpmc_capability = GPMC_HAS_WR_ACCESS | GPMC_HAS_WR_DATA_MUX_BUS; + if (GPMC_REVISION_MAJOR(l) > 0x5) + gpmc_capability |= GPMC_HAS_MUX_AAD; dev_info(gpmc_dev, "GPMC revision %d.%d\n", GPMC_REVISION_MAJOR(l), GPMC_REVISION_MINOR(l)); - rc = gpmc_mem_init(); - 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; - } + gpmc_mem_init(); if (gpmc_setup_irq() < 0) dev_warn(gpmc_dev, "gpmc_setup_irq failed\n"); @@ -1383,6 +1624,9 @@ static int gpmc_probe(struct platform_device *pdev) /* Now the GPMC is initialised, unreserve the chip-selects */ gpmc_cs_map = 0; + if (!pdev->dev.of_node) + gpmc_nr_waitpins = GPMC_NR_WAITPINS; + rc = gpmc_probe_dt(pdev); if (rc < 0) { clk_disable_unprepare(gpmc_l3_clk); diff --git a/arch/arm/mach-omap2/gpmc.h b/arch/arm/mach-omap2/gpmc.h index fe0a844d5007..707f6d58edd5 100644 --- a/arch/arm/mach-omap2/gpmc.h +++ b/arch/arm/mach-omap2/gpmc.h @@ -58,7 +58,7 @@ #define GPMC_CONFIG1_DEVICESIZE_16 GPMC_CONFIG1_DEVICESIZE(1) #define GPMC_CONFIG1_DEVICETYPE(val) ((val & 3) << 10) #define GPMC_CONFIG1_DEVICETYPE_NOR GPMC_CONFIG1_DEVICETYPE(0) -#define GPMC_CONFIG1_MUXADDDATA (1 << 9) +#define GPMC_CONFIG1_MUXTYPE(val) ((val & 3) << 8) #define GPMC_CONFIG1_TIME_PARA_GRAN (1 << 4) #define GPMC_CONFIG1_FCLK_DIV(val) (val & 3) #define GPMC_CONFIG1_FCLK_DIV2 (GPMC_CONFIG1_FCLK_DIV(1)) @@ -73,6 +73,13 @@ #define GPMC_IRQ_FIFOEVENTENABLE 0x01 #define GPMC_IRQ_COUNT_EVENT 0x02 +#define GPMC_BURST_4 4 /* 4 word burst */ +#define GPMC_BURST_8 8 /* 8 word burst */ +#define GPMC_BURST_16 16 /* 16 word burst */ +#define GPMC_DEVWIDTH_8BIT 1 /* 8-bit device width */ +#define GPMC_DEVWIDTH_16BIT 2 /* 16-bit device width */ +#define GPMC_MUX_AAD 1 /* Addr-Addr-Data multiplex */ +#define GPMC_MUX_AD 2 /* Addr-Data multiplex */ /* bool type time settings */ struct gpmc_bool_timings { @@ -178,10 +185,6 @@ struct gpmc_device_timings { u8 cyc_wpl; /* write deassertion time in cycles */ u32 cyc_iaa; /* initial access time in cycles */ - bool mux; /* address & data muxed */ - bool sync_write;/* synchronous write */ - bool sync_read; /* synchronous read */ - /* extra delays */ bool ce_xdelay; bool avd_xdelay; @@ -189,28 +192,40 @@ struct gpmc_device_timings { bool we_xdelay; }; +struct gpmc_settings { + bool burst_wrap; /* enables wrap bursting */ + bool burst_read; /* enables read page/burst mode */ + bool burst_write; /* enables write page/burst mode */ + bool device_nand; /* device is NAND */ + bool sync_read; /* enables synchronous reads */ + bool sync_write; /* enables synchronous writes */ + bool wait_on_read; /* monitor wait on reads */ + bool wait_on_write; /* monitor wait on writes */ + u32 burst_len; /* page/burst length */ + u32 device_width; /* device bus width (8 or 16 bit) */ + u32 mux_add_data; /* multiplex address & data */ + u32 wait_pin; /* wait-pin to be used */ +}; + extern int gpmc_calc_timings(struct gpmc_timings *gpmc_t, - struct gpmc_device_timings *dev_t); + struct gpmc_settings *gpmc_s, + struct gpmc_device_timings *dev_t); extern void gpmc_update_nand_reg(struct gpmc_nand_regs *reg, int cs); extern int gpmc_get_client_irq(unsigned irq_config); -extern unsigned int gpmc_ns_to_ticks(unsigned int time_ns); -extern unsigned int gpmc_ps_to_ticks(unsigned int time_ps); extern unsigned int gpmc_ticks_to_ns(unsigned int ticks); -extern unsigned int gpmc_round_ns_to_ticks(unsigned int time_ns); -extern unsigned long gpmc_get_fclk_period(void); extern void gpmc_cs_write_reg(int cs, int idx, u32 val); -extern u32 gpmc_cs_read_reg(int cs, int idx); extern int gpmc_calc_divider(unsigned int sync_clk); extern int gpmc_cs_set_timings(int cs, const struct gpmc_timings *t); +extern int gpmc_cs_program_settings(int cs, struct gpmc_settings *p); extern int gpmc_cs_request(int cs, unsigned long size, unsigned long *base); extern void gpmc_cs_free(int cs); -extern int gpmc_cs_set_reserved(int cs, int reserved); -extern int gpmc_cs_reserved(int cs); extern void omap3_gpmc_save_context(void); extern void omap3_gpmc_restore_context(void); -extern int gpmc_cs_configure(int cs, int cmd, int wval); +extern int gpmc_configure(int cmd, int wval); +extern void gpmc_read_settings_dt(struct device_node *np, + struct gpmc_settings *p); #endif diff --git a/arch/arm/mach-omap2/usb-tusb6010.c b/arch/arm/mach-omap2/usb-tusb6010.c index c5a3c6f9504e..e832bc7b8e2d 100644 --- a/arch/arm/mach-omap2/usb-tusb6010.c +++ b/arch/arm/mach-omap2/usb-tusb6010.c @@ -8,6 +8,7 @@ * published by the Free Software Foundation. */ +#include <linux/err.h> #include <linux/string.h> #include <linux/types.h> #include <linux/errno.h> @@ -26,6 +27,24 @@ static u8 async_cs, sync_cs; static unsigned refclk_psec; +static struct gpmc_settings tusb_async = { + .wait_on_read = true, + .wait_on_write = true, + .device_width = GPMC_DEVWIDTH_16BIT, + .mux_add_data = GPMC_MUX_AD, +}; + +static struct gpmc_settings tusb_sync = { + .burst_read = true, + .burst_write = true, + .sync_read = true, + .sync_write = true, + .wait_on_read = true, + .wait_on_write = true, + .burst_len = GPMC_BURST_16, + .device_width = GPMC_DEVWIDTH_16BIT, + .mux_add_data = GPMC_MUX_AD, +}; /* NOTE: timings are from tusb 6010 datasheet Rev 1.8, 12-Sept 2006 */ @@ -37,8 +56,6 @@ static int tusb_set_async_mode(unsigned sysclk_ps) memset(&dev_t, 0, sizeof(dev_t)); - dev_t.mux = true; - dev_t.t_ceasu = 8 * 1000; dev_t.t_avdasu = t_acsnh_advnh - 7000; dev_t.t_ce_avd = 1000; @@ -52,7 +69,7 @@ static int tusb_set_async_mode(unsigned sysclk_ps) dev_t.t_wpl = 300; dev_t.cyc_aavdh_we = 1; - gpmc_calc_timings(&t, &dev_t); + gpmc_calc_timings(&t, &tusb_async, &dev_t); return gpmc_cs_set_timings(async_cs, &t); } @@ -65,10 +82,6 @@ static int tusb_set_sync_mode(unsigned sysclk_ps) memset(&dev_t, 0, sizeof(dev_t)); - dev_t.mux = true; - dev_t.sync_read = true; - dev_t.sync_write = true; - dev_t.clk = 11100; dev_t.t_bacc = 1000; dev_t.t_ces = 1000; @@ -84,7 +97,7 @@ static int tusb_set_sync_mode(unsigned sysclk_ps) dev_t.cyc_wpl = 6; dev_t.t_ce_rdyz = 7000; - gpmc_calc_timings(&t, &dev_t); + gpmc_calc_timings(&t, &tusb_sync, &dev_t); return gpmc_cs_set_timings(sync_cs, &t); } @@ -165,18 +178,12 @@ tusb6010_setup_interface(struct musb_hdrc_platform_data *data, return status; } tusb_resources[0].end = tusb_resources[0].start + 0x9ff; + tusb_async.wait_pin = waitpin; async_cs = async; - gpmc_cs_write_reg(async, GPMC_CS_CONFIG1, - GPMC_CONFIG1_PAGE_LEN(2) - | GPMC_CONFIG1_WAIT_READ_MON - | GPMC_CONFIG1_WAIT_WRITE_MON - | GPMC_CONFIG1_WAIT_PIN_SEL(waitpin) - | GPMC_CONFIG1_READTYPE_ASYNC - | GPMC_CONFIG1_WRITETYPE_ASYNC - | GPMC_CONFIG1_DEVICESIZE_16 - | GPMC_CONFIG1_DEVICETYPE_NOR - | GPMC_CONFIG1_MUXADDDATA); + status = gpmc_cs_program_settings(async_cs, &tusb_async); + if (status < 0) + return status; /* SYNC region, primarily for DMA */ status = gpmc_cs_request(sync, SZ_16M, (unsigned long *) @@ -186,21 +193,12 @@ tusb6010_setup_interface(struct musb_hdrc_platform_data *data, return status; } tusb_resources[1].end = tusb_resources[1].start + 0x9ff; + tusb_sync.wait_pin = waitpin; sync_cs = sync; - gpmc_cs_write_reg(sync, GPMC_CS_CONFIG1, - GPMC_CONFIG1_READMULTIPLE_SUPP - | GPMC_CONFIG1_READTYPE_SYNC - | GPMC_CONFIG1_WRITEMULTIPLE_SUPP - | GPMC_CONFIG1_WRITETYPE_SYNC - | GPMC_CONFIG1_PAGE_LEN(2) - | GPMC_CONFIG1_WAIT_READ_MON - | GPMC_CONFIG1_WAIT_WRITE_MON - | GPMC_CONFIG1_WAIT_PIN_SEL(waitpin) - | GPMC_CONFIG1_DEVICESIZE_16 - | GPMC_CONFIG1_DEVICETYPE_NOR - | GPMC_CONFIG1_MUXADDDATA - /* fclk divider gets set later */ - ); + + status = gpmc_cs_program_settings(sync_cs, &tusb_sync); + if (status < 0) + return status; /* IRQ */ status = gpio_request_one(irq, GPIOF_IN, "TUSB6010 irq"); |