[U-Boot] [PATCH] Fix s3c24x0 timer

Ilya Averyanov averyanovin+uboot at gmail.com
Thu Apr 19 22:23:12 CEST 2012


Timer in s3c24x0 arch very old and use global variable.
This patch fix it and use DECLARE_GLOBAL_DATA_PTR

diff --git a/arch/arm/cpu/arm920t/s3c24x0/timer.c
b/arch/arm/cpu/arm920t/s3c24x0/timer.c
index 9571870..3ded465 100644
--- a/arch/arm/cpu/arm920t/s3c24x0/timer.c
+++ b/arch/arm/cpu/arm920t/s3c24x0/timer.c
@@ -34,20 +34,43 @@

 #include <asm/io.h>
 #include <asm/arch/s3c24x0_cpu.h>
+#include <div64.h>

-int timer_load_val = 0;
-static ulong timer_clk;
+DECLARE_GLOBAL_DATA_PTR;

-/* macro to read the 16 bit timer */
-static inline ulong READ_TIMER(void)
+#define TIMER_PREDIV_2 0
+#define TIMER_PREDIV_4 1
+#define TIMER_PREDIV_8 2
+#define TIMER_PREDIV_16 3
+
+/*
+ * Timer is 16-bit
+ * So we can have good (small) timer resolution OR good (big) overlap time
+ * PCLK usualy 66 MHz
+ * PCLK / 2 / 33 = 1 MHz, 1 uS @ tick, overlap after 65 mS
+ * PCLK / 4 / 165 = 0.1 MHz ,10 uS @ tick, overlap after 650 mS
+ * PCLK / 16 / 165 = 0.025 MHz, 40 uS @ tick, overlap after 2,5 S
+ * ...etc
+ */
+
+#define TIMER_PRESCALER 165
+#define TIMER_PREDIV TIMER_PREDIV_4 //2, 4, 8, 16
+
+static inline unsigned long long tick_to_time(unsigned long long tick)
 {
-    struct s3c24x0_timers *timers = s3c24x0_get_base_timers();
+    tick *= CONFIG_SYS_HZ;
+    do_div(tick, gd->timer_rate_hz);

-    return readl(&timers->tcnto4) & 0xffff;
+    return tick;
 }

-static ulong timestamp;
-static ulong lastdec;
+static inline unsigned long long usec_to_tick(unsigned long long usec)
+{
+    usec *= gd->timer_rate_hz;
+    do_div(usec, 1000000);
+
+    return usec;
+}

 int timer_init(void)
 {
@@ -55,57 +78,47 @@ int timer_init(void)
     ulong tmr;

     /* use PWM Timer 4 because it has no output */
-    /* prescaler for Timer 4 is 16 */
-    writel(0x0f00, &timers->tcfg0);
-    if (timer_load_val == 0) {
-        /*
-         * for 10 ms clock period @ PCLK with 4 bit divider = 1/2
-         * (default) and prescaler = 16. Should be 10390
-         * @33.25MHz and 15625 @ 50 MHz
-         */
-        timer_load_val = get_PCLK() / (2 * 16 * 100);
-        timer_clk = get_PCLK() / (2 * 16);
-    }
-    /* load value for 10 ms timeout */
-    lastdec = timer_load_val;
-    writel(timer_load_val, &timers->tcntb4);
-    /* auto load, manual update of timer 4 */
+    /* prescaler for Timer */
+    writel(((TIMER_PRESCALER - 1) << 8) | (TIMER_PRESCALER - 1),
&timers->tcfg0);
+    /* PREDIV */
+    writel((readl(&timers->tcfg1) & ~(0x0F << 16)) | (TIMER_PREDIV << 16),
&timers->tcfg1);
+    /* Reload value */
+    writel(0xFFFF, &timers->tcntb4);
+    /* auto load, manual update of Timer 4 */
     tmr = (readl(&timers->tcon) & ~0x0700000) | 0x0600000;
     writel(tmr, &timers->tcon);
-    /* auto load, start timer 4 */
+    /* auto load, start Timer 4 */
     tmr = (tmr & ~0x0700000) | 0x0500000;
     writel(tmr, &timers->tcon);
-    timestamp = 0;
+
+    gd->lastinc = 0;
+    gd->timer_rate_hz = get_PCLK() / (TIMER_PRESCALER * (1 <<
(TIMER_PREDIV + 1)));
+    gd->tbu = gd->tbl = 0;

     return (0);
 }

 /*
- * timer without interrupts
+ * macro to read the count-down 16 bit timer
  */
-ulong get_timer(ulong base)
+static inline ulong READ_TIMER16(void)
 {
-    return get_timer_masked() - base;
-}
-
-void __udelay (unsigned long usec)
-{
-    ulong tmo;
-    ulong start = get_ticks();
-
-    tmo = usec / 1000;
-    tmo *= (timer_load_val * 100);
-    tmo /= 1000;
+    struct s3c24x0_timers *timers = s3c24x0_get_base_timers();

-    while ((ulong) (get_ticks() - start) < tmo)
-        /*NOP*/;
+    return (0xFFFF - ((readl(&timers->tcnto4) & 0xFFFF)));
 }

-ulong get_timer_masked(void)
+static inline ulong READ_TIMER32(void)
 {
-    ulong tmr = get_ticks();
+    ulong now = READ_TIMER16();
+    ulong tbl = gd->tbl;

-    return tmr / (timer_clk / CONFIG_SYS_HZ);
+    if (now >= (tbl & 0xFFFF))
+        tbl = (tbl & 0xFFFF0000) | now;
+    else
+        tbl = ((tbl & 0xFFFF0000) | now) + 0x00010000;
+
+    return tbl;
 }

 void udelay_masked(unsigned long usec)
@@ -114,68 +127,71 @@ void udelay_masked(unsigned long usec)
     ulong endtime;
     signed long diff;

-    if (usec >= 1000) {
-        tmo = usec / 1000;
-        tmo *= (timer_load_val * 100);
-        tmo /= 1000;
-    } else {
-        tmo = usec * (timer_load_val * 100);
-        tmo /= (1000 * 1000);
-    }
+    tmo = usec_to_tick(usec); /* convert usecs to ticks */

     endtime = get_ticks() + tmo;

     do {
-        ulong now = get_ticks();
-        diff = endtime - now;
+            ulong now = get_ticks();
+            diff = endtime - now;
     } while (diff >= 0);
 }

 /*
- * This function is derived from PowerPC code (read timebase as long long).
- * On ARM it just returns the timer value.
+ * Get the current 64 bit timer tick count
  */
 unsigned long long get_ticks(void)
 {
-    ulong now = READ_TIMER();
-
-    if (lastdec >= now) {
-        /* normal mode */
-        timestamp += lastdec - now;
-    } else {
-        /* we have an overflow ... */
-        timestamp += lastdec + timer_load_val - now;
-    }
-    lastdec = now;
-
-    return timestamp;
+    ulong now = READ_TIMER32();
+
+    /* increment tbu if tbl has rolled over */
+    if (now < gd->tbl)
+        gd->tbu++;
+    gd->tbl = now;
+    return (((unsigned long long)gd->tbu) << 32) | gd->tbl;
+}
+
+void __udelay(unsigned long usec)
+{
+    unsigned long long start;
+    unsigned long long tmo;
+
+    start = get_ticks(); /* get current timestamp */
+    tmo = usec_to_tick(usec); /* convert usecs to ticks */
+    if (tmo == 0)
+        tmo = 1;
+    while ((get_ticks() - start) < tmo)
+        ; /* loop till time has passed */
 }

 /*
- * This function is derived from PowerPC code (timebase clock frequency).
- * On ARM it returns the number of timer ticks per second.
+ * get_timer(base) can be used to check for timeouts or
+ * to measure elasped time relative to an event:
+ *
+ * ulong start_time = get_timer(0) sets start_time to the current
+ * time value.
+ * get_timer(start_time) returns the time elapsed since then.
+ *
+ * The time is used in CONFIG_SYS_HZ units!
  */
-ulong get_tbclk(void)
+ulong get_timer(ulong base)
 {
-    ulong tbclk;
-
-#if defined(CONFIG_SMDK2400)
-    tbclk = timer_load_val * 100;
-#elif defined(CONFIG_SBC2410X) || \
-      defined(CONFIG_SMDK2410) || \
-    defined(CONFIG_S3C2440) || \
-      defined(CONFIG_VCMA9)
-    tbclk = CONFIG_SYS_HZ;
-#else
-#    error "tbclk not configured"
-#endif
-
-    return tbclk;
+    return get_timer_masked() - base;
+}
+
+ulong get_timer_masked (void)
+{
+    return tick_to_time(get_ticks());
 }

 /*
- * reset the cpu by setting up the watchdog timer and let him time out
+ * Return the number of timer ticks per second.
  */
+ulong get_tbclk(void)
+{
+    return gd->timer_rate_hz;
+}
+
 void reset_cpu(ulong ignored)
 {
     struct s3c24x0_watchdog *watchdog;


More information about the U-Boot mailing list