X-Git-Url: https://git.karo-electronics.de/?a=blobdiff_plain;f=arch%2Farm%2Fcpu%2Farm926ejs%2Fmxs%2Ftimer.c;h=7b343ed375996bab6b8a7f0a5e6ab19a0f12c873;hb=638de780ae4ee566808c98b7c83a8304700143da;hp=f2e72257d15b5d77fb7d583152f2b19b1f32a95f;hpb=256d83cd6d8caf345ffba414a0f77f30d68caf8a;p=karo-tx-uboot.git diff --git a/arch/arm/cpu/arm926ejs/mxs/timer.c b/arch/arm/cpu/arm926ejs/mxs/timer.c index f2e72257d1..7b343ed375 100644 --- a/arch/arm/cpu/arm926ejs/mxs/timer.c +++ b/arch/arm/cpu/arm926ejs/mxs/timer.c @@ -16,16 +16,28 @@ #include /* Maximum fixed count */ -#if defined(CONFIG_MX23) +#if defined(CONFIG_SOC_MX23) #define TIMER_LOAD_VAL 0xffff -#elif defined(CONFIG_MX28) +#elif defined(CONFIG_SOC_MX28) #define TIMER_LOAD_VAL 0xffffffff #endif DECLARE_GLOBAL_DATA_PTR; -#define timestamp (gd->arch.tbl) -#define lastdec (gd->arch.lastinc) +/* Enable this to verify that the code can correctly + * handle the timer rollover + */ +/* #define DEBUG_TIMER_WRAP */ + +#ifdef DEBUG_TIMER_WRAP +/* + * Let the timer wrap 15 seconds after start to catch misbehaving + * timer related code early + */ +#define TIMER_START (-time_to_tick(15 * CONFIG_SYS_HZ)) +#else +#define TIMER_START 0UL +#endif /* * This driver uses 1kHz clock source. @@ -42,12 +54,6 @@ static inline unsigned long time_to_tick(unsigned long time) return time * (MXS_INCREMENTER_HZ / CONFIG_SYS_HZ); } -/* Calculate how many ticks happen in "us" microseconds */ -static inline unsigned long us_to_tick(unsigned long us) -{ - return (us * MXS_INCREMENTER_HZ) / 1000000; -} - int timer_init(void) { struct mxs_timrot_regs *timrot_regs = @@ -57,9 +63,9 @@ int timer_init(void) mxs_reset_block(&timrot_regs->hw_timrot_rotctrl_reg); /* Set fixed_count to 0 */ -#if defined(CONFIG_MX23) +#if defined(CONFIG_SOC_MX23) writel(0, &timrot_regs->hw_timrot_timcount0); -#elif defined(CONFIG_MX28) +#elif defined(CONFIG_SOC_MX28) writel(0, &timrot_regs->hw_timrot_fixed_count0); #endif @@ -68,47 +74,73 @@ int timer_init(void) TIMROT_TIMCTRLn_SELECT_1KHZ_XTAL, &timrot_regs->hw_timrot_timctrl0); - /* Set fixed_count to maximal value */ -#if defined(CONFIG_MX23) +#ifndef DEBUG_TIMER_WRAP + /* Set fixed_count to maximum value */ +#if defined(CONFIG_SOC_MX23) writel(TIMER_LOAD_VAL - 1, &timrot_regs->hw_timrot_timcount0); -#elif defined(CONFIG_MX28) +#elif defined(CONFIG_SOC_MX28) writel(TIMER_LOAD_VAL, &timrot_regs->hw_timrot_fixed_count0); #endif - +#else /* DEBUG_TIMER_WRAP */ + /* Set fixed_count so that the counter will wrap after 20 seconds */ +#if defined(CONFIG_SOC_MX23) + writel(20 * MXS_INCREMENTER_HZ - 1, &timrot_regs->hw_timrot_timcount0); +#elif defined(CONFIG_SOC_MX28) + writel(20 * MXS_INCREMENTER_HZ, + &timrot_regs->hw_timrot_fixed_count0); +#endif + gd->arch.lastinc = TIMER_LOAD_VAL - 20 * MXS_INCREMENTER_HZ; + + /* Make the usec counter roll over 30 seconds after startup */ + writel(-30000000, MXS_HW_DIGCTL_MICROSECONDS); +#endif /* DEBUG_TIMER_WRAP */ + writel(TIMROT_TIMCTRLn_UPDATE, + &timrot_regs->hw_timrot_timctrl0_clr); +#ifdef DEBUG_TIMER_WRAP + /* Set fixed_count to maximum value for subsequent loads */ +#if defined(CONFIG_SOC_MX23) + writel(20 * MXS_INCREMENTER_HZ - 1, &timrot_regs->hw_timrot_timcount0); +#elif defined(CONFIG_SOC_MX28) + writel(TIMER_LOAD_VAL, &timrot_regs->hw_timrot_fixed_count0); +#endif +#endif /* DEBUG_TIMER_WRAP */ + gd->arch.timer_rate_hz = MXS_INCREMENTER_HZ; + gd->arch.tbl = TIMER_START; + gd->arch.tbu = 0; return 0; } +/* Note: This function works correctly for TIMER_LOAD_VAL == 0xffffffff! + * The rollover is handled automagically due to the properties of + * two's complement arithmetic. + * For any other value of TIMER_LOAD_VAL the calculations would have + * to be done modulus(TIMER_LOAD_VAL + 1). + */ unsigned long long get_ticks(void) { struct mxs_timrot_regs *timrot_regs = (struct mxs_timrot_regs *)MXS_TIMROT_BASE; - uint32_t now; - - /* Current tick value */ -#if defined(CONFIG_MX23) + unsigned long now; +#if defined(CONFIG_SOC_MX23) /* Upper bits are the valid ones. */ now = readl(&timrot_regs->hw_timrot_timcount0) >> TIMROT_RUNNING_COUNTn_RUNNING_COUNT_OFFSET; -#elif defined(CONFIG_MX28) - now = readl(&timrot_regs->hw_timrot_running_count0); +#elif defined(CONFIG_SOC_MX28) + /* The timer is counting down, so subtract the register value from + * the counter period length (implicitly 2^32) to get an incrementing + * timestamp + */ + now = -readl(&timrot_regs->hw_timrot_running_count0); #else #error "Don't know how to read timrot_regs" #endif + ulong inc = now - gd->arch.lastinc; - if (lastdec >= now) { - /* - * normal mode (non roll) - * move stamp forward with absolut diff ticks - */ - timestamp += (lastdec - now); - } else { - /* we have rollover of decrementer */ - timestamp += (TIMER_LOAD_VAL - now) + lastdec; - - } - lastdec = now; - - return timestamp; + if (gd->arch.tbl + inc < gd->arch.tbl) + gd->arch.tbu++; + gd->arch.tbl += inc; + gd->arch.lastinc = now; + return ((unsigned long long)gd->arch.tbu << 32) | gd->arch.tbl; } ulong get_timer_masked(void) @@ -118,44 +150,27 @@ ulong get_timer_masked(void) ulong get_timer(ulong base) { - return get_timer_masked() - base; + /* NOTE: time_to_tick(base) is required to correctly handle rollover! */ + return tick_to_time(get_ticks() - time_to_tick(base)); } /* We use the HW_DIGCTL_MICROSECONDS register for sub-millisecond timer. */ -#define MXS_HW_DIGCTL_MICROSECONDS 0x8001c0c0 - void __udelay(unsigned long usec) { - uint32_t old, new, incr; - uint32_t counter = 0; - - old = readl(MXS_HW_DIGCTL_MICROSECONDS); - - while (counter < usec) { - new = readl(MXS_HW_DIGCTL_MICROSECONDS); - - /* Check if the timer wrapped. */ - if (new < old) { - incr = 0xffffffff - old; - incr += new; - } else { - incr = new - old; - } - - /* - * Check if we are close to the maximum time and the counter - * would wrap if incremented. If that's the case, break out - * from the loop as the requested delay time passed. + struct mxs_digctl_regs *digctl_regs = (void *)MXS_DIGCTL_BASE; + u32 start = readl(&digctl_regs->hw_digctl_microseconds); + + while (readl(&digctl_regs->hw_digctl_microseconds) - start <= usec) + /* use '<=' to guarantee a delay of _at least_ + * the given number of microseconds. + * No need for fancy rollover checks + * Two's complement arithmetic applied correctly + * does everything that's needed automagically! */ - if (counter + incr < counter) - break; - - counter += incr; - old = new; - } + ; } ulong get_tbclk(void) { - return MXS_INCREMENTER_HZ; + return gd->arch.timer_rate_hz; }