[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