Hi, Texas Instruments Finland Oy, Porkkalankatu 22, 00180 Helsinki. Y-tunnus/Business ID: 0615521-4. Kotipaikka/Domicile: Helsinki On 16/10/17 02:21, Ladislav Michl wrote: > OneNAND sets up timings on its own. > > Signed-off-by: Ladislav Michl <ladis@xxxxxxxxxxxxxx> > --- > Note: see cover letter. > > drivers/memory/omap-gpmc.c | 167 ++++++++++++++++++++++++++++++++++++++++++++- > include/linux/omap-gpmc.h | 7 ++ > 2 files changed, 171 insertions(+), 3 deletions(-) We could squash this into patch 7. > > diff --git a/drivers/memory/omap-gpmc.c b/drivers/memory/omap-gpmc.c > index fc7c3c2a0b04..b2fe3be4f914 100644 > --- a/drivers/memory/omap-gpmc.c > +++ b/drivers/memory/omap-gpmc.c > @@ -1143,6 +1143,169 @@ struct gpmc_nand_ops *gpmc_omap_get_nand_ops(struct gpmc_nand_regs *reg, int cs) > } > EXPORT_SYMBOL_GPL(gpmc_omap_get_nand_ops); > > +static void gpmc_omap_onenand_calc_async_timings(struct gpmc_timings *t, > + struct gpmc_settings *s) > +{ > + struct gpmc_device_timings dev_t; > + const int t_cer = 15; > + const int t_avdp = 12; > + const int t_aavdh = 7; > + const int t_ce = 76; > + const int t_aa = 76; > + const int t_oe = 20; > + const int t_cez = 20; /* max of t_cez, t_oez */ > + const int t_wpl = 40; > + const int t_wph = 30; > + > + /* > + * Note that we need to keep sync_write set for the call to > + * omap2_onenand_set_async_mode() to work to detect the onenand > + * supported clock rate for the sync timings. > + */ > + s->sync_read = false; > + s->sync_write = true; > + > + memset(&dev_t, 0, sizeof(dev_t)); > + > + 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, s, &dev_t); > +} Let's get rid of manually setting async timings. The async timings should come from the device tree like it is done now for NAND or any other generic GPMC child. We can populate the same values into all the NAND DT nodes present as of now. > + > +static int gpmc_omap_onenand_calc_sync_timings(struct gpmc_timings *t, > + struct gpmc_settings *s, > + int freq) > +{ > + struct gpmc_device_timings dev_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_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, gpmc_clk_ns, latency; > + > + if (!s->sync_read && !s->sync_write) > + return 0; > + > + switch (freq) { > + case 104: > + min_gpmc_clk_period = 9600; /* 104 MHz */ > + t_ces = 3; > + t_avds = 4; > + t_avdh = 2; > + t_ach = 3; > + t_aavdh = 6; > + t_rdyo = 6; > + break; > + case 83: > + min_gpmc_clk_period = 12000; /* 83 MHz */ > + t_ces = 5; > + t_avds = 4; > + t_avdh = 2; > + t_ach = 6; > + t_aavdh = 6; > + t_rdyo = 9; > + break; > + case 66: > + min_gpmc_clk_period = 15000; /* 66 MHz */ > + t_ces = 6; > + t_avds = 5; > + t_avdh = 2; > + t_ach = 6; > + t_aavdh = 6; > + t_rdyo = 11; > + break; > + default: > + min_gpmc_clk_period = 18500; /* 54 MHz */ > + t_ces = 7; > + t_avds = 7; > + t_avdh = 7; > + t_ach = 9; > + t_aavdh = 7; > + t_rdyo = 15; > + s->sync_write = false; > + s->burst_write = false; > + break; > + } > + > + div = gpmc_calc_divider(min_gpmc_clk_period); > + gpmc_clk_ns = gpmc_ticks_to_ns(div); > + if (gpmc_clk_ns < 12) /* >83 MHz */ > + latency = 8; > + else if (gpmc_clk_ns < 15) /* >66 MHz */ > + latency = 6; > + else if (gpmc_clk_ns >= 25) /* 40 MHz */ > + latency = 3; > + else > + latency = 4; > + > + /* Set synchronous read timings */ > + memset(&dev_t, 0, sizeof(dev_t)); > + > + if (!s->sync_write) { > + 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, s, &dev_t); > + > + return latency; > +} > + > +int gpmc_omap_onenand_set_sync_timings(struct device *dev, int cs, int freq) > +{ > + int latency, ret; > + struct gpmc_timings gpmc_t; > + struct gpmc_settings gpmc_s; > + > + gpmc_read_settings_dt(dev->of_node, &gpmc_s); > + > + latency = gpmc_omap_onenand_calc_sync_timings(&gpmc_t, &gpmc_s, freq); > + if (latency > 0) { > + ret = gpmc_cs_program_settings(cs, &gpmc_s); > + if (ret < 0) > + return ret; > + > + ret = gpmc_cs_set_timings(cs, &gpmc_t, &gpmc_s); > + if (ret < 0) > + return ret; > + } > + > +// if (gpmc_s.sync_read) > +// if (gpmc_s.sync_write) ?? > + > + return latency; > +} > + > +EXPORT_SYMBOL_GPL(gpmc_omap_onenand_set_sync_timings); > + > int gpmc_get_client_irq(unsigned irq_config) > { > if (!gpmc_irq_domain) { > @@ -2097,9 +2260,7 @@ static int gpmc_probe_child(struct platform_device *pdev, > if (ret) > goto err; > > - /* OneNAND driver handles timings on its own */ > - gpmc_cs_enable_mem(cs); > - goto no_timings; > + gpmc_omap_onenand_calc_async_timings(&gpmc_t, &gpmc_s); this should be undone. > } else { > ret = of_property_read_u32(child, "bank-width", > &gpmc_s.device_width); > diff --git a/include/linux/omap-gpmc.h b/include/linux/omap-gpmc.h > index fd0de00c0d77..22a2e6b43669 100644 > --- a/include/linux/omap-gpmc.h > +++ b/include/linux/omap-gpmc.h > @@ -28,12 +28,19 @@ struct gpmc_nand_regs; > #if IS_ENABLED(CONFIG_OMAP_GPMC) > struct gpmc_nand_ops *gpmc_omap_get_nand_ops(struct gpmc_nand_regs *regs, > int cs); > +int gpmc_omap_onenand_set_sync_timings(struct device *dev, int cs, int freq); > #else > static inline struct gpmc_nand_ops *gpmc_omap_get_nand_ops(struct gpmc_nand_regs *regs, > int cs) > { > return NULL; > } > + > +static inline int gpmc_omap_onenand_set_sync_timings(struct device *dev, > + int cs, int freq) > +{ > + return 0; > +} > #endif /* CONFIG_OMAP_GPMC */ > > /*--------------------------------*/ > -- cheers, -roger -- To unsubscribe from this list: send the line "unsubscribe linux-omap" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html