diff options
Diffstat (limited to 'arch/arm/mach-omap2/gpmc-onenand.c')
-rw-r--r-- | arch/arm/mach-omap2/gpmc-onenand.c | 343 |
1 files changed, 154 insertions, 189 deletions
diff --git a/arch/arm/mach-omap2/gpmc-onenand.c b/arch/arm/mach-omap2/gpmc-onenand.c index 916716e1da3b..94a349e4dc96 100644 --- a/arch/arm/mach-omap2/gpmc-onenand.c +++ b/arch/arm/mach-omap2/gpmc-onenand.c @@ -16,15 +16,24 @@ #include <linux/mtd/onenand_regs.h> #include <linux/io.h> #include <linux/platform_data/mtd-onenand-omap2.h> +#include <linux/err.h> #include <asm/mach/flash.h> -#include <plat/gpmc.h> - +#include "gpmc.h" #include "soc.h" +#include "gpmc-onenand.h" #define ONENAND_IO_SIZE SZ_128K +#define ONENAND_FLAG_SYNCREAD (1 << 0) +#define ONENAND_FLAG_SYNCWRITE (1 << 1) +#define ONENAND_FLAG_HF (1 << 2) +#define ONENAND_FLAG_VHF (1 << 3) + +static unsigned onenand_flags; +static unsigned latency; + static struct omap_onenand_platform_data *gpmc_onenand_data; static struct resource gpmc_onenand_resource = { @@ -38,11 +47,10 @@ static struct platform_device gpmc_onenand_device = { .resource = &gpmc_onenand_resource, }; -static int omap2_onenand_set_async_mode(int cs, void __iomem *onenand_base) +static struct gpmc_timings omap2_onenand_calc_async_timings(void) { + struct gpmc_device_timings dev_t; struct gpmc_timings t; - u32 reg; - int err; const int t_cer = 15; const int t_avdp = 12; @@ -51,60 +59,49 @@ static int omap2_onenand_set_async_mode(int cs, void __iomem *onenand_base) const int t_aa = 76; const int t_oe = 20; const int t_cez = 20; /* max of t_cez, t_oez */ - const int t_ds = 30; const int t_wpl = 40; const int t_wph = 30; - /* Ensure sync read and sync write are disabled */ - reg = readw(onenand_base + ONENAND_REG_SYS_CFG1); - reg &= ~ONENAND_SYS_CFG1_SYNC_READ & ~ONENAND_SYS_CFG1_SYNC_WRITE; - writew(reg, onenand_base + ONENAND_REG_SYS_CFG1); + memset(&dev_t, 0, sizeof(dev_t)); - memset(&t, 0, sizeof(t)); - t.sync_clk = 0; - t.cs_on = 0; - t.adv_on = 0; - - /* Read */ - t.adv_rd_off = gpmc_round_ns_to_ticks(max_t(int, t_avdp, t_cer)); - t.oe_on = t.adv_rd_off + gpmc_round_ns_to_ticks(t_aavdh); - t.access = t.adv_on + gpmc_round_ns_to_ticks(t_aa); - t.access = max_t(int, t.access, t.cs_on + gpmc_round_ns_to_ticks(t_ce)); - t.access = max_t(int, t.access, t.oe_on + gpmc_round_ns_to_ticks(t_oe)); - t.oe_off = t.access + gpmc_round_ns_to_ticks(1); - t.cs_rd_off = t.oe_off; - t.rd_cycle = t.cs_rd_off + gpmc_round_ns_to_ticks(t_cez); - - /* Write */ - t.adv_wr_off = t.adv_rd_off; - t.we_on = t.oe_on; - if (cpu_is_omap34xx()) { - t.wr_data_mux_bus = t.we_on; - t.wr_access = t.we_on + gpmc_round_ns_to_ticks(t_ds); - } - t.we_off = t.we_on + gpmc_round_ns_to_ticks(t_wpl); - t.cs_wr_off = t.we_off + gpmc_round_ns_to_ticks(t_wph); - t.wr_cycle = t.cs_wr_off + gpmc_round_ns_to_ticks(t_cez); + 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; + dev_t.t_aa = t_aa * 1000; + dev_t.t_ce = t_ce * 1000; + dev_t.t_oe = t_oe * 1000; + dev_t.t_cez_r = t_cez * 1000; + dev_t.t_cez_w = dev_t.t_cez_r; + 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); - err = gpmc_cs_set_timings(cs, &t); - if (err) - return err; + return gpmc_cs_set_timings(cs, t); +} + +static void omap2_onenand_set_async_mode(void __iomem *onenand_base) +{ + u32 reg; /* Ensure sync read and sync write are disabled */ reg = readw(onenand_base + ONENAND_REG_SYS_CFG1); reg &= ~ONENAND_SYS_CFG1_SYNC_READ & ~ONENAND_SYS_CFG1_SYNC_WRITE; writew(reg, onenand_base + ONENAND_REG_SYS_CFG1); - - return 0; } -static void set_onenand_cfg(void __iomem *onenand_base, int latency, - int sync_read, int sync_write, int hf, int vhf) +static void set_onenand_cfg(void __iomem *onenand_base) { u32 reg; @@ -112,19 +109,19 @@ static void set_onenand_cfg(void __iomem *onenand_base, int latency, reg &= ~((0x7 << ONENAND_SYS_CFG1_BRL_SHIFT) | (0x7 << 9)); reg |= (latency << ONENAND_SYS_CFG1_BRL_SHIFT) | ONENAND_SYS_CFG1_BL_16; - if (sync_read) + if (onenand_flags & ONENAND_FLAG_SYNCREAD) reg |= ONENAND_SYS_CFG1_SYNC_READ; else reg &= ~ONENAND_SYS_CFG1_SYNC_READ; - if (sync_write) + if (onenand_flags & ONENAND_FLAG_SYNCWRITE) reg |= ONENAND_SYS_CFG1_SYNC_WRITE; else reg &= ~ONENAND_SYS_CFG1_SYNC_WRITE; - if (hf) + if (onenand_flags & ONENAND_FLAG_HF) reg |= ONENAND_SYS_CFG1_HF; else reg &= ~ONENAND_SYS_CFG1_HF; - if (vhf) + if (onenand_flags & ONENAND_FLAG_VHF) reg |= ONENAND_SYS_CFG1_VHF; else reg &= ~ONENAND_SYS_CFG1_VHF; @@ -132,21 +129,10 @@ static void set_onenand_cfg(void __iomem *onenand_base, int latency, } static int omap2_onenand_get_freq(struct omap_onenand_platform_data *cfg, - void __iomem *onenand_base, bool *clk_dep) + void __iomem *onenand_base) { u16 ver = readw(onenand_base + ONENAND_REG_VERSION_ID); - int freq = 0; - - if (cfg->get_freq) { - struct onenand_freq_info fi; - - fi.maf_id = readw(onenand_base + ONENAND_REG_MANUFACTURER_ID); - fi.dev_id = readw(onenand_base + ONENAND_REG_DEVICE_ID); - fi.ver_id = ver; - freq = cfg->get_freq(&fi, clk_dep); - if (freq) - return freq; - } + int freq; switch ((ver >> 4) & 0xf) { case 0: @@ -172,41 +158,24 @@ static int omap2_onenand_get_freq(struct omap_onenand_platform_data *cfg, return freq; } -static int omap2_onenand_set_sync_mode(struct omap_onenand_platform_data *cfg, - void __iomem *onenand_base, - int *freq_ptr) +static struct gpmc_timings +omap2_onenand_calc_sync_timings(struct omap_onenand_platform_data *cfg, + 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 */ - const int t_ds = 30; const int t_wpl = 40; const int t_wph = 30; int min_gpmc_clk_period, t_ces, t_avds, t_avdh, t_ach, t_aavdh, t_rdyo; - int div, fclk_offset_ns, fclk_offset, gpmc_clk_ns, latency; - int first_time = 0, hf = 0, vhf = 0, sync_read = 0, sync_write = 0; - int err, ticks_cez; - int cs = cfg->cs, freq = *freq_ptr; - u32 reg; - bool clk_dep = false; + int div, gpmc_clk_ns; - if (cfg->flags & ONENAND_SYNC_READ) { - sync_read = 1; - } else if (cfg->flags & ONENAND_SYNC_READWRITE) { - sync_read = 1; - sync_write = 1; - } else - return omap2_onenand_set_async_mode(cs, onenand_base); - - if (!freq) { - /* Very first call freq is not known */ - err = omap2_onenand_set_async_mode(cs, onenand_base); - if (err) - return err; - freq = omap2_onenand_get_freq(cfg, onenand_base, &clk_dep); - first_time = 1; - } + if (cfg->flags & ONENAND_SYNC_READ) + onenand_flags = ONENAND_FLAG_SYNCREAD; + else if (cfg->flags & ONENAND_SYNC_READWRITE) + onenand_flags = ONENAND_FLAG_SYNCREAD | ONENAND_FLAG_SYNCWRITE; switch (freq) { case 104: @@ -244,116 +213,67 @@ static int omap2_onenand_set_sync_mode(struct omap_onenand_platform_data *cfg, t_ach = 9; t_aavdh = 7; t_rdyo = 15; - sync_write = 0; + onenand_flags &= ~ONENAND_FLAG_SYNCWRITE; break; } - div = gpmc_cs_calc_divider(cs, min_gpmc_clk_period); + div = gpmc_calc_divider(min_gpmc_clk_period); gpmc_clk_ns = gpmc_ticks_to_ns(div); if (gpmc_clk_ns < 15) /* >66Mhz */ - hf = 1; + onenand_flags |= ONENAND_FLAG_HF; + else + onenand_flags &= ~ONENAND_FLAG_HF; if (gpmc_clk_ns < 12) /* >83Mhz */ - vhf = 1; - if (vhf) + onenand_flags |= ONENAND_FLAG_VHF; + else + onenand_flags &= ~ONENAND_FLAG_VHF; + if (onenand_flags & ONENAND_FLAG_VHF) latency = 8; - else if (hf) + else if (onenand_flags & ONENAND_FLAG_HF) latency = 6; else if (gpmc_clk_ns >= 25) /* 40 MHz*/ latency = 3; else latency = 4; - if (clk_dep) { - if (gpmc_clk_ns < 12) { /* >83Mhz */ - t_ces = 3; - t_avds = 4; - } else if (gpmc_clk_ns < 15) { /* >66Mhz */ - t_ces = 5; - t_avds = 4; - } else if (gpmc_clk_ns < 25) { /* >40Mhz */ - t_ces = 6; - t_avds = 5; - } else { - t_ces = 7; - t_avds = 7; - } - } + /* Set synchronous read timings */ + memset(&dev_t, 0, sizeof(dev_t)); - if (first_time) - set_onenand_cfg(onenand_base, latency, - sync_read, sync_write, hf, vhf); - - if (div == 1) { - reg = gpmc_cs_read_reg(cs, GPMC_CS_CONFIG2); - reg |= (1 << 7); - gpmc_cs_write_reg(cs, GPMC_CS_CONFIG2, reg); - reg = gpmc_cs_read_reg(cs, GPMC_CS_CONFIG3); - reg |= (1 << 7); - gpmc_cs_write_reg(cs, GPMC_CS_CONFIG3, reg); - reg = gpmc_cs_read_reg(cs, GPMC_CS_CONFIG4); - reg |= (1 << 7); - reg |= (1 << 23); - gpmc_cs_write_reg(cs, GPMC_CS_CONFIG4, reg); + dev_t.mux = true; + dev_t.sync_read = true; + if (onenand_flags & ONENAND_FLAG_SYNCWRITE) { + dev_t.sync_write = true; } else { - reg = gpmc_cs_read_reg(cs, GPMC_CS_CONFIG2); - reg &= ~(1 << 7); - gpmc_cs_write_reg(cs, GPMC_CS_CONFIG2, reg); - reg = gpmc_cs_read_reg(cs, GPMC_CS_CONFIG3); - reg &= ~(1 << 7); - gpmc_cs_write_reg(cs, GPMC_CS_CONFIG3, reg); - reg = gpmc_cs_read_reg(cs, GPMC_CS_CONFIG4); - reg &= ~(1 << 7); - reg &= ~(1 << 23); - gpmc_cs_write_reg(cs, GPMC_CS_CONFIG4, reg); + dev_t.t_avdp_w = max(t_avdp, t_cer) * 1000; + dev_t.t_wpl = t_wpl * 1000; + dev_t.t_wph = t_wph * 1000; + dev_t.t_aavdh = t_aavdh * 1000; } + dev_t.ce_xdelay = true; + dev_t.avd_xdelay = true; + dev_t.oe_xdelay = true; + dev_t.we_xdelay = true; + dev_t.clk = min_gpmc_clk_period; + dev_t.t_bacc = dev_t.clk; + dev_t.t_ces = t_ces * 1000; + dev_t.t_avds = t_avds * 1000; + dev_t.t_avdh = t_avdh * 1000; + dev_t.t_ach = t_ach * 1000; + dev_t.cyc_iaa = (latency + 1); + dev_t.t_cez_r = t_cez * 1000; + dev_t.t_cez_w = dev_t.t_cez_r; + 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; +} - /* Set synchronous read timings */ - memset(&t, 0, sizeof(t)); - t.sync_clk = min_gpmc_clk_period; - t.cs_on = 0; - t.adv_on = 0; - fclk_offset_ns = gpmc_round_ns_to_ticks(max_t(int, t_ces, t_avds)); - fclk_offset = gpmc_ns_to_ticks(fclk_offset_ns); - t.page_burst_access = gpmc_clk_ns; - - /* Read */ - t.adv_rd_off = gpmc_ticks_to_ns(fclk_offset + gpmc_ns_to_ticks(t_avdh)); - t.oe_on = gpmc_ticks_to_ns(fclk_offset + gpmc_ns_to_ticks(t_ach)); - /* Force at least 1 clk between AVD High to OE Low */ - if (t.oe_on <= t.adv_rd_off) - t.oe_on = t.adv_rd_off + gpmc_round_ns_to_ticks(1); - t.access = gpmc_ticks_to_ns(fclk_offset + (latency + 1) * div); - t.oe_off = t.access + gpmc_round_ns_to_ticks(1); - t.cs_rd_off = t.oe_off; - ticks_cez = ((gpmc_ns_to_ticks(t_cez) + div - 1) / div) * div; - t.rd_cycle = gpmc_ticks_to_ns(fclk_offset + (latency + 1) * div + - ticks_cez); - - /* Write */ - if (sync_write) { - t.adv_wr_off = t.adv_rd_off; - t.we_on = 0; - t.we_off = t.cs_rd_off; - t.cs_wr_off = t.cs_rd_off; - t.wr_cycle = t.rd_cycle; - if (cpu_is_omap34xx()) { - t.wr_data_mux_bus = gpmc_ticks_to_ns(fclk_offset + - gpmc_ps_to_ticks(min_gpmc_clk_period + - t_rdyo * 1000)); - t.wr_access = t.access; - } - } else { - t.adv_wr_off = gpmc_round_ns_to_ticks(max_t(int, - t_avdp, t_cer)); - t.we_on = t.adv_wr_off + gpmc_round_ns_to_ticks(t_aavdh); - t.we_off = t.we_on + gpmc_round_ns_to_ticks(t_wpl); - t.cs_wr_off = t.we_off + gpmc_round_ns_to_ticks(t_wph); - t.wr_cycle = t.cs_wr_off + gpmc_round_ns_to_ticks(t_cez); - if (cpu_is_omap34xx()) { - t.wr_data_mux_bus = t.we_on; - t.wr_access = t.we_on + gpmc_round_ns_to_ticks(t_ds); - } - } +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, @@ -362,7 +282,6 @@ static int omap2_onenand_set_sync_mode(struct omap_onenand_platform_data *cfg, (sync_read ? GPMC_CONFIG1_READTYPE_SYNC : 0) | (sync_write ? GPMC_CONFIG1_WRITEMULTIPLE_SUPP : 0) | (sync_write ? GPMC_CONFIG1_WRITETYPE_SYNC : 0) | - GPMC_CONFIG1_CLKACTIVATIONTIME(fclk_offset) | GPMC_CONFIG1_PAGE_LEN(2) | (cpu_is_omap34xx() ? 0 : (GPMC_CONFIG1_WAIT_READ_MON | @@ -371,11 +290,45 @@ static int omap2_onenand_set_sync_mode(struct omap_onenand_platform_data *cfg, GPMC_CONFIG1_DEVICETYPE_NOR | GPMC_CONFIG1_MUXADDDATA); - err = gpmc_cs_set_timings(cs, &t); - if (err) - return err; + return gpmc_cs_set_timings(cs, t); +} + +static int omap2_onenand_setup_async(void __iomem *onenand_base) +{ + struct gpmc_timings t; + int ret; + + omap2_onenand_set_async_mode(onenand_base); + + t = omap2_onenand_calc_async_timings(); + + ret = gpmc_set_async_mode(gpmc_onenand_data->cs, &t); + if (IS_ERR_VALUE(ret)) + return ret; + + omap2_onenand_set_async_mode(onenand_base); + + return 0; +} + +static int omap2_onenand_setup_sync(void __iomem *onenand_base, int *freq_ptr) +{ + int ret, freq = *freq_ptr; + struct gpmc_timings t; - set_onenand_cfg(onenand_base, latency, sync_read, sync_write, hf, vhf); + if (!freq) { + /* Very first call freq is not known */ + freq = omap2_onenand_get_freq(gpmc_onenand_data, onenand_base); + set_onenand_cfg(onenand_base); + } + + t = omap2_onenand_calc_sync_timings(gpmc_onenand_data, freq); + + ret = gpmc_set_sync_mode(gpmc_onenand_data->cs, &t); + if (IS_ERR_VALUE(ret)) + return ret; + + set_onenand_cfg(onenand_base); *freq_ptr = freq; @@ -385,15 +338,22 @@ static int omap2_onenand_set_sync_mode(struct omap_onenand_platform_data *cfg, static int gpmc_onenand_setup(void __iomem *onenand_base, int *freq_ptr) { struct device *dev = &gpmc_onenand_device.dev; + unsigned l = ONENAND_SYNC_READ | ONENAND_SYNC_READWRITE; + int ret; - /* Set sync timings in GPMC */ - if (omap2_onenand_set_sync_mode(gpmc_onenand_data, onenand_base, - freq_ptr) < 0) { - dev_err(dev, "Unable to set synchronous mode\n"); - return -EINVAL; + ret = omap2_onenand_setup_async(onenand_base); + if (ret) { + dev_err(dev, "unable to set to async mode\n"); + return ret; } - return 0; + if (!(gpmc_onenand_data->flags & l)) + return 0; + + ret = omap2_onenand_setup_sync(onenand_base, freq_ptr); + if (ret) + dev_err(dev, "unable to set to sync mode\n"); + return ret; } void __init gpmc_onenand_init(struct omap_onenand_platform_data *_onenand_data) @@ -411,6 +371,11 @@ void __init gpmc_onenand_init(struct omap_onenand_platform_data *_onenand_data) gpmc_onenand_data->flags |= ONENAND_SYNC_READ; } + if (cpu_is_omap34xx()) + gpmc_onenand_data->flags |= ONENAND_IN_OMAP34XX; + else + gpmc_onenand_data->flags &= ~ONENAND_IN_OMAP34XX; + err = gpmc_cs_request(gpmc_onenand_data->cs, ONENAND_IO_SIZE, (unsigned long *)&gpmc_onenand_resource.start); if (err < 0) { |