aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPaul Mackerras <paulus@samba.org>2007-10-08 19:59:17 -0400
committerPaul Mackerras <paulus@samba.org>2007-10-11 07:39:31 -0400
commitd968014b7280e2c447b20363e576999040ac72ef (patch)
tree579a94f3b9e76fdd88113d4ac707425926e4dfaa
parent87a72f9e171e558a0288aa83ef1dc6ae4af32224 (diff)
[POWERPC] Prevent decrementer clockevents from firing early
On old powermacs, we sometimes set the decrementer to 1 in order to trigger a decrementer interrupt, which we use to handle an interrupt that was pending at the time when it was re-enabled. This was causing the decrementer clock event device to call the event function for the next event early, which was causing problems when high-res timers were not enabled. This fixes the problem by recording the timebase value at which the next event should occur, and checking the current timebase against the recorded value in timer_interrupt. If it isn't time for the next event, it just reprograms the decrementer and returns. This also subtracts 1 from the value stored into the decrementer, which is appropriate because the decrementer interrupts on the transition from 0 to -1, not when the decrementer reaches 0. Signed-off-by: Paul Mackerras <paulus@samba.org>
-rw-r--r--arch/powerpc/kernel/time.c14
1 files changed, 14 insertions, 0 deletions
diff --git a/arch/powerpc/kernel/time.c b/arch/powerpc/kernel/time.c
index d20947cf1735..64b503c82a31 100644
--- a/arch/powerpc/kernel/time.c
+++ b/arch/powerpc/kernel/time.c
@@ -118,6 +118,7 @@ static struct clock_event_device decrementer_clockevent = {
118 118
119static DEFINE_PER_CPU(struct clock_event_device, decrementers); 119static DEFINE_PER_CPU(struct clock_event_device, decrementers);
120void init_decrementer_clockevent(void); 120void init_decrementer_clockevent(void);
121static DEFINE_PER_CPU(u64, decrementer_next_tb);
121 122
122#ifdef CONFIG_PPC_ISERIES 123#ifdef CONFIG_PPC_ISERIES
123static unsigned long __initdata iSeries_recal_titan; 124static unsigned long __initdata iSeries_recal_titan;
@@ -541,6 +542,7 @@ void timer_interrupt(struct pt_regs * regs)
541 struct pt_regs *old_regs; 542 struct pt_regs *old_regs;
542 int cpu = smp_processor_id(); 543 int cpu = smp_processor_id();
543 struct clock_event_device *evt = &per_cpu(decrementers, cpu); 544 struct clock_event_device *evt = &per_cpu(decrementers, cpu);
545 u64 now;
544 546
545 /* Ensure a positive value is written to the decrementer, or else 547 /* Ensure a positive value is written to the decrementer, or else
546 * some CPUs will continuue to take decrementer exceptions */ 548 * some CPUs will continuue to take decrementer exceptions */
@@ -551,6 +553,14 @@ void timer_interrupt(struct pt_regs * regs)
551 do_IRQ(regs); 553 do_IRQ(regs);
552#endif 554#endif
553 555
556 now = get_tb_or_rtc();
557 if (now < per_cpu(decrementer_next_tb, cpu)) {
558 /* not time for this event yet */
559 now = per_cpu(decrementer_next_tb, cpu) - now;
560 if (now <= DECREMENTER_MAX)
561 set_dec((unsigned int)now - 1);
562 return;
563 }
554 old_regs = set_irq_regs(regs); 564 old_regs = set_irq_regs(regs);
555 irq_enter(); 565 irq_enter();
556 566
@@ -797,6 +807,10 @@ void __init clocksource_init(void)
797static int decrementer_set_next_event(unsigned long evt, 807static int decrementer_set_next_event(unsigned long evt,
798 struct clock_event_device *dev) 808 struct clock_event_device *dev)
799{ 809{
810 __get_cpu_var(decrementer_next_tb) = get_tb_or_rtc() + evt;
811 /* The decrementer interrupts on the 0 -> -1 transition */
812 if (evt)
813 --evt;
800 set_dec(evt); 814 set_dec(evt);
801 return 0; 815 return 0;
802} 816}