X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=arch%2Farm%2Fmach-pxa%2Ftime.c;h=ee2beb400414653cdba4166df543633c62ca83b3;hb=refs%2Fheads%2Fvserver;hp=7e89235632f883ba2a7d5b52a052231bc4b30c27;hpb=5fc42a6ed0ec81088c37caadb45898ae6cd0ad2c;p=linux-2.6.git diff --git a/arch/arm/mach-pxa/time.c b/arch/arm/mach-pxa/time.c index 7e8923563..ee2beb400 100644 --- a/arch/arm/mach-pxa/time.c +++ b/arch/arm/mach-pxa/time.c @@ -10,7 +10,6 @@ * published by the Free Software Foundation. */ -#include #include #include #include @@ -19,6 +18,7 @@ #include #include #include +#include #include #include @@ -27,6 +27,7 @@ #include #include #include +#include static inline unsigned long pxa_get_rtc_time(void) @@ -48,37 +49,31 @@ static int pxa_set_rtc(void) return 0; } -/* IRQs are disabled before entering here from do_gettimeofday() */ -static unsigned long pxa_gettimeoffset (void) -{ - long ticks_to_match, elapsed, usec; - - /* Get ticks before next timer match */ - ticks_to_match = OSMR0 - OSCR; - - /* We need elapsed ticks since last match */ - elapsed = LATCH - ticks_to_match; - - /* don't get fooled by the workaround in pxa_timer_interrupt() */ - if (elapsed <= 0) - return 0; - - /* Now convert them to usec */ - usec = (unsigned long)(elapsed * (tick_nsec / 1000))/LATCH; - - return usec; -} +#ifdef CONFIG_NO_IDLE_HZ +static unsigned long initial_match; +static int match_posponed; +#endif static irqreturn_t -pxa_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) +pxa_timer_interrupt(int irq, void *dev_id) { int next_match; + write_seqlock(&xtime_lock); + +#ifdef CONFIG_NO_IDLE_HZ + if (match_posponed) { + match_posponed = 0; + OSMR0 = initial_match; + } +#endif + /* Loop until we get ahead of the free running timer. * This ensures an exact clock tick count and time accuracy. - * IRQs are disabled inside the loop to ensure coherence between - * lost_ticks (updated in do_timer()) and the match reg value, so we - * can use do_gettimeofday() from interrupt handlers. + * Since IRQs are disabled at this point, coherence between + * lost_ticks(updated in do_timer()) and the match reg value is + * ensured, hence we can use do_gettimeofday() from interrupt + * handlers. * * HACK ALERT: it seems that the PXA timer regs aren't updated right * away in all cases when a write occurs. We therefore compare with @@ -90,35 +85,135 @@ pxa_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) * exactly one tick period which should be a pretty rare event. */ do { - timer_tick(regs); + timer_tick(); OSSR = OSSR_M0; /* Clear match on timer 0 */ next_match = (OSMR0 += LATCH); } while( (signed long)(next_match - OSCR) <= 8 ); + write_sequnlock(&xtime_lock); + return IRQ_HANDLED; } static struct irqaction pxa_timer_irq = { .name = "PXA Timer Tick", - .flags = SA_INTERRUPT, - .handler = pxa_timer_interrupt + .flags = IRQF_DISABLED | IRQF_TIMER, + .handler = pxa_timer_interrupt, +}; + +static cycle_t pxa_get_cycles(void) +{ + return OSCR; +} + +static struct clocksource clocksource_pxa = { + .name = "pxa_timer", + .rating = 200, + .read = pxa_get_cycles, + .mask = CLOCKSOURCE_MASK(32), + .shift = 20, + .is_continuous = 1, }; -void __init pxa_init_time(void) +static void __init pxa_timer_init(void) { struct timespec tv; + unsigned long flags; - gettimeoffset = pxa_gettimeoffset; set_rtc = pxa_set_rtc; tv.tv_nsec = 0; tv.tv_sec = pxa_get_rtc_time(); do_settimeofday(&tv); - OSMR0 = 0; /* set initial match at 0 */ + OIER = 0; /* disable any timer interrupts */ OSSR = 0xf; /* clear status on all timers */ setup_irq(IRQ_OST0, &pxa_timer_irq); - OIER |= OIER_E0; /* enable match on timer 0 to cause interrupts */ - OSCR = 0; /* initialize free-running timer, force first match */ + local_irq_save(flags); + OIER = OIER_E0; /* enable match on timer 0 to cause interrupts */ + OSMR0 = OSCR + LATCH; /* set initial match */ + local_irq_restore(flags); + + /* + * OSCR runs continuously on PXA and is not written to, + * so we can use it as clock source directly. + */ + clocksource_pxa.mult = + clocksource_hz2mult(CLOCK_TICK_RATE, clocksource_pxa.shift); + clocksource_register(&clocksource_pxa); +} + +#ifdef CONFIG_NO_IDLE_HZ +static int pxa_dyn_tick_enable_disable(void) +{ + /* nothing to do */ + return 0; +} + +static void pxa_dyn_tick_reprogram(unsigned long ticks) +{ + if (ticks > 1) { + initial_match = OSMR0; + OSMR0 = initial_match + ticks * LATCH; + match_posponed = 1; + } +} + +static irqreturn_t +pxa_dyn_tick_handler(int irq, void *dev_id) +{ + if (match_posponed) { + match_posponed = 0; + OSMR0 = initial_match; + if ( (signed long)(initial_match - OSCR) <= 8 ) + return pxa_timer_interrupt(irq, dev_id); + } + return IRQ_NONE; } +static struct dyn_tick_timer pxa_dyn_tick = { + .enable = pxa_dyn_tick_enable_disable, + .disable = pxa_dyn_tick_enable_disable, + .reprogram = pxa_dyn_tick_reprogram, + .handler = pxa_dyn_tick_handler, +}; +#endif + +#ifdef CONFIG_PM +static unsigned long osmr[4], oier; + +static void pxa_timer_suspend(void) +{ + osmr[0] = OSMR0; + osmr[1] = OSMR1; + osmr[2] = OSMR2; + osmr[3] = OSMR3; + oier = OIER; +} + +static void pxa_timer_resume(void) +{ + OSMR0 = osmr[0]; + OSMR1 = osmr[1]; + OSMR2 = osmr[2]; + OSMR3 = osmr[3]; + OIER = oier; + + /* + * OSMR0 is the system timer: make sure OSCR is sufficiently behind + */ + OSCR = OSMR0 - LATCH; +} +#else +#define pxa_timer_suspend NULL +#define pxa_timer_resume NULL +#endif + +struct sys_timer pxa_timer = { + .init = pxa_timer_init, + .suspend = pxa_timer_suspend, + .resume = pxa_timer_resume, +#ifdef CONFIG_NO_IDLE_HZ + .dyn_tick = &pxa_dyn_tick, +#endif +};