diff options
author | Jesper Nilsson <jesper.nilsson@axis.com> | 2010-07-30 15:33:07 (GMT) |
---|---|---|
committer | Jesper Nilsson <jesper.nilsson@axis.com> | 2010-08-04 10:58:55 (GMT) |
commit | 60dbd6633178a8625ed71329da0167c6d50c559c (patch) | |
tree | bcbc1004dfe356ce7cf30f183725dbbc9dad3e4f /arch/cris/arch-v32/kernel/time.c | |
parent | 26bfeea38a4a5daf52c8f01c986ca8680bf1f6a1 (diff) | |
download | linux-60dbd6633178a8625ed71329da0167c6d50c559c.tar.xz |
CRIS: GENERIC_TIME fixes
GENERIC_TIME was not functional for CRIS, giving random backward
time jumps.
For CRISv32 implement a new clocksource using the free running counter
and ditch the arch_gettimeoffset.
The random time jumps still existed, but turned out to be the write_seqlock
which was missing around our do_timer() call.
So switch over to GENERIC_TIME using the clocksource for CRISv32.
CRISv10 doesn't have the free running counter needed for the
clocksource trick, but we can still use GENERIC_TIME with
arch_gettimeoffset.
Unfortunately, there were problems in using the prescaler register
to timer0 for the gettimeoffset calculation, so it is now ignored,
making our resolution worse by the tune of 40usec (0.4%) worst case.
At the same time, clean up some formatting and use NSEC_PER_SEC
instead of 1000000000.
Signed-off-by: Jesper Nilsson <jesper.nilsson@axis.com>
Diffstat (limited to 'arch/cris/arch-v32/kernel/time.c')
-rw-r--r-- | arch/cris/arch-v32/kernel/time.c | 85 |
1 files changed, 34 insertions, 51 deletions
diff --git a/arch/cris/arch-v32/kernel/time.c b/arch/cris/arch-v32/kernel/time.c index 1ee0e10..a545211 100644 --- a/arch/cris/arch-v32/kernel/time.c +++ b/arch/cris/arch-v32/kernel/time.c @@ -1,13 +1,13 @@ /* * linux/arch/cris/arch-v32/kernel/time.c * - * Copyright (C) 2003-2007 Axis Communications AB + * Copyright (C) 2003-2010 Axis Communications AB * */ #include <linux/timex.h> #include <linux/time.h> -#include <linux/jiffies.h> +#include <linux/clocksource.h> #include <linux/interrupt.h> #include <linux/swap.h> #include <linux/sched.h> @@ -36,6 +36,30 @@ /* Number of 763 counts before watchdog bites */ #define ETRAX_WD_CNT ((2*ETRAX_WD_HZ)/HZ + 1) +/* Register the continuos readonly timer available in FS and ARTPEC-3. */ +static cycle_t read_cont_rotime(struct clocksource *cs) +{ + return (u32)REG_RD(timer, regi_timer0, r_time); +} + +static struct clocksource cont_rotime = { + .name = "crisv32_rotime", + .rating = 300, + .read = read_cont_rotime, + .mask = CLOCKSOURCE_MASK(32), + .shift = 10, + .flags = CLOCK_SOURCE_IS_CONTINUOUS, +}; + +static int __init etrax_init_cont_rotime(void) +{ + cont_rotime.mult = clocksource_khz2mult(100000, cont_rotime.shift); + clocksource_register(&cont_rotime); + return 0; +} +arch_initcall(etrax_init_cont_rotime); + + unsigned long timer_regs[NR_CPUS] = { regi_timer0, @@ -67,43 +91,6 @@ unsigned long get_ns_in_jiffie(void) return ns; } -unsigned long do_slow_gettimeoffset(void) -{ - unsigned long count; - unsigned long usec_count = 0; - - /* For the first call after boot */ - static unsigned long count_p = TIMER0_DIV; - static unsigned long jiffies_p = 0; - - /* Cache volatile jiffies temporarily; we have IRQs turned off. */ - unsigned long jiffies_t; - - /* The timer interrupt comes from Etrax timer 0. In order to get - * better precision, we check the current value. It might have - * underflowed already though. */ - count = REG_RD(timer, regi_timer0, r_tmr0_data); - jiffies_t = jiffies; - - /* Avoiding timer inconsistencies (they are rare, but they happen) - * There is one problem that must be avoided here: - * 1. the timer counter underflows - */ - if( jiffies_t == jiffies_p ) { - if( count > count_p ) { - /* Timer wrapped, use new count and prescale. - * Increase the time corresponding to one jiffy. - */ - usec_count = 1000000/HZ; - } - } else - jiffies_p = jiffies_t; - count_p = count; - /* Convert timer value to usec */ - /* 100 MHz timer, divide by 100 to get usec */ - usec_count += (TIMER0_DIV - count) / 100; - return usec_count; -} /* From timer MDS describing the hardware watchdog: * 4.3.1 Watchdog Operation @@ -126,8 +113,7 @@ static short int watchdog_key = 42; /* arbitrary 7 bit number */ * is used though, so set this really low. */ #define WATCHDOG_MIN_FREE_PAGES 8 -void -reset_watchdog(void) +void reset_watchdog(void) { #if defined(CONFIG_ETRAX_WATCHDOG) reg_timer_rw_wd_ctrl wd_ctrl = { 0 }; @@ -147,8 +133,7 @@ reset_watchdog(void) /* stop the watchdog - we still need the correct key */ -void -stop_watchdog(void) +void stop_watchdog(void) { #if defined(CONFIG_ETRAX_WATCHDOG) reg_timer_rw_wd_ctrl wd_ctrl = { 0 }; @@ -162,8 +147,7 @@ stop_watchdog(void) extern void show_registers(struct pt_regs *regs); -void -handle_watchdog_bite(struct pt_regs* regs) +void handle_watchdog_bite(struct pt_regs *regs) { #if defined(CONFIG_ETRAX_WATCHDOG) extern int cause_of_death; @@ -203,8 +187,7 @@ handle_watchdog_bite(struct pt_regs* regs) */ extern void cris_do_profile(struct pt_regs *regs); -static inline irqreturn_t -timer_interrupt(int irq, void *dev_id) +static inline irqreturn_t timer_interrupt(int irq, void *dev_id) { struct pt_regs *regs = get_irq_regs(); int cpu = smp_processor_id(); @@ -233,7 +216,9 @@ timer_interrupt(int irq, void *dev_id) return IRQ_HANDLED; /* Call the real timer interrupt handler */ + write_seqlock(&xtime_lock); do_timer(1); + write_sequnlock(&xtime_lock); return IRQ_HANDLED; } @@ -246,8 +231,7 @@ static struct irqaction irq_timer = { .name = "timer" }; -void __init -cris_timer_init(void) +void __init cris_timer_init(void) { int cpu = smp_processor_id(); reg_timer_rw_tmr0_ctrl tmr0_ctrl = { 0 }; @@ -273,8 +257,7 @@ cris_timer_init(void) REG_WR(timer, timer_regs[cpu], rw_intr_mask, timer_intr_mask); } -void __init -time_init(void) +void __init time_init(void) { reg_intr_vect_rw_mask intr_mask; |