]> git.kernelconcepts.de Git - karo-tx-uboot.git/commitdiff
Fix broken timer code
authorLothar Waßmann <LW@KARO-electronics.de>
Thu, 14 Jun 2012 08:09:37 +0000 (10:09 +0200)
committerLothar Waßmann <LW@KARO-electronics.de>
Thu, 14 Jun 2012 08:09:37 +0000 (10:09 +0200)
arch/arm/cpu/armv7/imx-common/timer.c

index 1645ff83f47be8548e4bf713222e3766d7a42e99..b304a7d2292764df0ad80cf4e2e470f0651f22d8 100644 (file)
@@ -46,25 +46,37 @@ static struct mxc_gpt *cur_gpt = (struct mxc_gpt *)GPT1_BASE_ADDR;
 #define GPTCR_TEN              1               /* Timer enable */
 #define CLK_32KHZ              32768           /* 32Khz input */
 
-DECLARE_GLOBAL_DATA_PTR;
+#ifdef DEBUG_TIMER_WRAP
+/*
+ * Let the timer wrap 15 seconds after start to catch misbehaving
+ * timer related code early
+ */
+#define TIMER_START            (-us_to_tick(10000000))
+#else
+#define TIMER_START            0UL
+#endif
 
-#define timestamp (gd->tbl)
-#define lastinc (gd->lastinc)
+DECLARE_GLOBAL_DATA_PTR;
 
-static inline unsigned long long tick_to_time(unsigned long long tick)
+static inline unsigned long tick_to_time(unsigned long tick)
 {
-       tick *= CONFIG_SYS_HZ;
-       do_div(tick, CLK_32KHZ);
-
-       return tick;
+       return tick * CONFIG_SYS_HZ / CLK_32KHZ;
 }
 
-static inline unsigned long long us_to_tick(unsigned long long usec)
+static inline unsigned long time_to_tick(unsigned long time)
 {
-       usec *= CLK_32KHZ;
-       do_div(usec, 1000000);
+       unsigned long long ticks = (unsigned long long)time;
+       ticks *= CLK_32KHZ;
+       do_div(ticks, CONFIG_SYS_HZ);
+       return ticks;
+}
 
-       return usec;
+static inline unsigned long us_to_tick(unsigned long usec)
+{
+       unsigned long long ticks = (unsigned long long)usec;
+       ticks *= CLK_32KHZ;
+       do_div(ticks, 1000 * CONFIG_SYS_HZ);
+       return ticks;
 }
 
 int timer_init(void)
@@ -86,8 +98,10 @@ int timer_init(void)
        __raw_writel(i | GPTCR_CLKSOURCE_32 | GPTCR_TEN, &cur_gpt->control);
 
        val = __raw_readl(&cur_gpt->counter);
-       lastinc = val / (CLK_32KHZ / CONFIG_SYS_HZ);
-       timestamp = 0;
+       gd->lastinc = val / (CLK_32KHZ / CONFIG_SYS_HZ);
+       gd->tbu = 0;
+       gd->tbl = TIMER_START;
+       gd->timer_rate_hz = CLK_32KHZ;
 
        return 0;
 }
@@ -95,19 +109,11 @@ int timer_init(void)
 unsigned long long get_ticks(void)
 {
        ulong now = __raw_readl(&cur_gpt->counter); /* current tick value */
+       ulong inc = now - gd->lastinc;
 
-       if (now >= lastinc) {
-               /*
-                * normal mode (non roll)
-                * move stamp forward with absolut diff ticks
-                */
-               timestamp += (now - lastinc);
-       } else {
-               /* we have rollover of incrementer */
-               timestamp += (0xFFFFFFFF - lastinc) + now;
-       }
-       lastinc = now;
-       return timestamp;
+       gd->tbl += inc;
+       gd->lastinc = now;
+       return gd->tbl;
 }
 
 ulong get_timer_masked(void)
@@ -118,24 +124,31 @@ ulong get_timer_masked(void)
         * 5 * 10^9 days... and get_ticks() * CONFIG_SYS_HZ wraps in
         * 5 * 10^6 days - long enough.
         */
+       /*
+        * LW: get_ticks() returns a long long with the top 32 bits always ZERO!
+        * Thus the calculation above is not true.
+        * A 64bit timer value would only make sense if it was
+        * consistently used throughout the code. Thus also the parameter
+        * to get_timer() and its return value would need to be 64bit wide!
+        */
        return tick_to_time(get_ticks());
 }
 
 ulong get_timer(ulong base)
 {
-       return get_timer_masked() - base;
+       return tick_to_time(get_ticks() - time_to_tick(base));
 }
 
 /* delay x useconds AND preserve advance timstamp value */
 void __udelay(unsigned long usec)
 {
-       unsigned long long tmp;
-       ulong tmo;
+       unsigned long start = get_ticks();      /* get current timestamp */
+       unsigned long ticks = us_to_tick(usec);
 
-       tmo = us_to_tick(usec);
-       tmp = get_ticks() + tmo;        /* get current timestamp */
+       if (ticks == 0)
+               ticks++;
 
-       while (get_ticks() < tmp)       /* loop till event */
+       while (get_ticks() - start < ticks)     /* loop till event */
                 /*NOP*/;
 }
 
@@ -145,5 +158,5 @@ void __udelay(unsigned long usec)
  */
 ulong get_tbclk(void)
 {
-       return CLK_32KHZ;
+       return gd->timer_rate_hz;
 }