[PATCH 3/4] ARM OMAP2+ GPMC: fix WAITMONITORINGTIME divider bug

From: Robert ABEL
Date: Mon Feb 16 2015 - 10:51:17 EST


WAITMONITORINGTIME is expressed in GPMC_CLK cycles (even for asynchronous accesses).
However the driver currently converts them to GPMC_FCLK cycles, thus waitmonitoringtime in dt
had to be predivided by divider as a workaround. This patch fixes the issue by reading the
current GPMCFCLKDIVIDER and adjusting the divisor for WAITMONITORINGTIME accordingly.

Signed-off-by: Robert ABEL <rabel@xxxxxxxxxxxxxxxxxxxxxxx>
---
arch/arm/mach-omap2/gpmc.c | 78 +++++++++++++++++++++++++++++++++++++---------
1 file changed, 64 insertions(+), 14 deletions(-)

diff --git a/arch/arm/mach-omap2/gpmc.c b/arch/arm/mach-omap2/gpmc.c
index bae4a20..4fa5ff1 100644
--- a/arch/arm/mach-omap2/gpmc.c
+++ b/arch/arm/mach-omap2/gpmc.c
@@ -113,6 +113,9 @@
*/
#define GPMC_NR_IRQ 2

+#define GPMC_FCLK 0
+#define GPMC_CLK 1
+
struct gpmc_client_irq {
unsigned irq;
u32 bitmask;
@@ -208,16 +211,55 @@ static unsigned long gpmc_get_fclk_period(void)
return rate;
}

-static unsigned int gpmc_ns_to_ticks(unsigned int time_ns)
+/**
+ * gpmc_get_clk_period - get period of selected clk in ps
+ * @cs : affected chip select
+ * @clk_sel: either one of GPMC_CLK or GPMC_FCLK
+ *
+ * GPMC_CS_CONFIG1 GPMCFCLKDIVIDER for cs has to be setup
+ * prior to calling this function with GPMC_CLK.
+ *
+ */
+static unsigned long gpmc_get_clk_period(int cs, unsigned int clk_sel)
+{
+
+ unsigned long tick_ps = gpmc_get_fclk_period();
+ u32 l;
+ int div;
+
+ switch (clk_sel) {
+ case GPMC_CLK:
+ /* get current clk divider */
+ l = gpmc_cs_read_reg(cs, GPMC_CS_CONFIG1);
+ div = (l & 0x03) + 1;
+ /* get GPMC_CLK period */
+ tick_ps *= div;
+ break;
+ case GPMC_FCLK:
+ /* FALL-THROUGH */
+ default:
+ break;
+ }
+
+ return tick_ps;
+
+}
+
+static unsigned int gpmc_ns_to_clk_ticks(unsigned int time_ns, int cs, unsigned int clk_sel)
{
unsigned long tick_ps;

/* Calculate in picosecs to yield more exact results */
- tick_ps = gpmc_get_fclk_period();
+ tick_ps = gpmc_get_clk_period(cs, clk_sel);

return (time_ns * 1000 + tick_ps - 1) / tick_ps;
}

+static unsigned int gpmc_ns_to_ticks(unsigned int time_ns)
+{
+ return gpmc_ns_to_clk_ticks(time_ns, 0, GPMC_FCLK);
+}
+
static unsigned int gpmc_ps_to_ticks(unsigned int time_ps)
{
unsigned long tick_ps;
@@ -280,10 +322,10 @@ static void gpmc_cs_bool_timings(int cs, const struct gpmc_bool_timings *p)

#ifdef DEBUG
static int set_gpmc_timing_reg(int cs, int reg, int st_bit, int end_bit,
- int time, const char *name)
+ int time, unsigned int clk_sel, const char *name)
#else
static int set_gpmc_timing_reg(int cs, int reg, int st_bit, int end_bit,
- int time)
+ int time, unsigned int clk_sel)
#endif
{
u32 l;
@@ -292,7 +334,7 @@ static int set_gpmc_timing_reg(int cs, int reg, int st_bit, int end_bit,
if (time == 0)
ticks = 0;
else
- ticks = gpmc_ns_to_ticks(time);
+ ticks = gpmc_ns_to_clk_ticks(time, cs, clk_sel);
nr_bits = end_bit - st_bit + 1;
if (ticks >= 1 << nr_bits) {
#ifdef DEBUG
@@ -307,7 +349,7 @@ static int set_gpmc_timing_reg(int cs, int reg, int st_bit, int end_bit,
#ifdef DEBUG
printk(KERN_INFO
"GPMC CS%d: %-17s: %3d ticks, %3lu ns (was %3i ticks) %3d ns\n",
- cs, name, ticks, gpmc_get_fclk_period() * ticks / 1000,
+ cs, name, ticks, gpmc_get_clk_period(cs, clk_sel) * ticks / 1000,
(l >> st_bit) & mask, time);
#endif
l &= ~(mask << st_bit);
@@ -320,12 +362,19 @@ static int set_gpmc_timing_reg(int cs, int reg, int st_bit, int end_bit,
#ifdef DEBUG
#define GPMC_SET_ONE(reg, st, end, field) \
if (set_gpmc_timing_reg(cs, (reg), (st), (end), \
- t->field, #field) < 0) \
+ t->field, GPMC_FCLK, #field) < 0) \
return -1
+#define GPMC_SET_ONE_C(reg, st, end, field, clk_sel) \
+ if (set_gpmc_timing_reg(cs, (reg), (st), (end), \
+ t->field, clk_sel, #field) < 0) \
+ return -1
#else
#define GPMC_SET_ONE(reg, st, end, field) \
- if (set_gpmc_timing_reg(cs, (reg), (st), (end), t->field) < 0) \
+ if (set_gpmc_timing_reg(cs, (reg), (st), (end), t->field, GPMC_FCLK) < 0) \
return -1
+#define GPMC_SET_ONE_C(reg, st, end, field, clk_sel) \
+ if (set_gpmc_timing_reg(cs, (reg), (st), (end), t->field, clk_sel) < 0) \
+ return -1
#endif

int gpmc_calc_divider(unsigned int sync_clk)
@@ -374,22 +423,23 @@ int gpmc_cs_set_timings(int cs, const struct gpmc_timings *t)
GPMC_SET_ONE(GPMC_CS_CONFIG6, 0, 3, bus_turnaround);
GPMC_SET_ONE(GPMC_CS_CONFIG6, 8, 11, cycle2cycle_delay);

- GPMC_SET_ONE(GPMC_CS_CONFIG1, 18, 19, wait_monitoring);
- GPMC_SET_ONE(GPMC_CS_CONFIG1, 25, 26, clk_activation);
-
if (gpmc_capability & GPMC_HAS_WR_DATA_MUX_BUS)
GPMC_SET_ONE(GPMC_CS_CONFIG6, 16, 19, wr_data_mux_bus);
if (gpmc_capability & GPMC_HAS_WR_ACCESS)
GPMC_SET_ONE(GPMC_CS_CONFIG6, 24, 28, wr_access);

l = gpmc_cs_read_reg(cs, GPMC_CS_CONFIG1);
+ l &= ~0x03;
+ l |= (div - 1);
+ gpmc_cs_write_reg(cs, GPMC_CS_CONFIG1, l);
+
+ GPMC_SET_ONE_C(GPMC_CS_CONFIG1, 18, 19, wait_monitoring, GPMC_CLK);
+ GPMC_SET_ONE(GPMC_CS_CONFIG1, 25, 26, clk_activation);
+
#ifdef DEBUG
printk(KERN_INFO "GPMC CS%d CLK period is %lu ns (div %d)\n",
cs, (div * gpmc_get_fclk_period()) / 1000, div);
#endif
- l &= ~0x03;
- l |= (div - 1);
- gpmc_cs_write_reg(cs, GPMC_CS_CONFIG1, l);

gpmc_cs_bool_timings(cs, &t->bool_timings);

--
2.3.0

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/