[MIPS] Patch to arch/mips/mips-boards/generic/time.c
authorKevin D. Kissell <kevink@mips.com>
Tue, 12 Sep 2006 10:08:08 +0000 (12:08 +0200)
committerRalf Baechle <ralf@linux-mips.org>
Wed, 27 Sep 2006 12:37:41 +0000 (13:37 +0100)
In hooking up the perf counter overflow interrupt to the experimental
deprecated-real-soon-now /proc/perf interface last night, I had to
revisit arch/mips/mips-boards/generic/time.c, and discovered that
when the 2.6.9-based SMTC prototype was merged with the more
recent tree, it was missed that arch/mips/kernel/time.c had changed
so that even in SMP kernels, timer_interrupt() calls
local_timer_interrupt(), so there is no longer a need to invoke it
directly from mips_timer_interrupt() in those cases where
timer_interrupt() has been called.  So I got rid of that, and added the
invocation of perf_irq() if Cause.PCI is set, more-or-less following the
same logic as in the non-SMTC case, with the modifications that (a) a
runtime check for Release 2 isn't done, because it's redundant in SMTC),
and (b) we check for a clock interrupt regardless of the value returned
by the perf counter service - I don't understand why we'd want to control
that with perf_irq(), but maybe one of you knows the story.  I also got
rid of the stupid warning about the unused variable when compiled for
SMTC (another artifact of the merge). The result hasn't been beaten to
death, but boots, seems stable, and supports extended precision event
counting.

Signed-off-by: Kevin D. Kissell <kevink@mips.com>
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
arch/mips/mips-boards/generic/time.c

index 557bf96..de5798e 100644 (file)
@@ -92,10 +92,9 @@ extern int (*perf_irq)(struct pt_regs *regs);
 irqreturn_t mips_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
 {
        int cpu = smp_processor_id();
-       int r2 = cpu_has_mips_r2;
 
 #ifdef CONFIG_MIPS_MT_SMTC
-        /*
+       /*
         *  In an SMTC system, one Count/Compare set exists per VPE.
         *  Which TC within a VPE gets the interrupt is essentially
         *  random - we only know that it shouldn't be one with
@@ -108,29 +107,46 @@ irqreturn_t mips_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
         *  the general MIPS timer_interrupt routine.
         */
 
+       int vpflags;
+
        /*
-        * DVPE is necessary so long as cross-VPE interrupts
-        * are done via read-modify-write of Cause register.
+        * We could be here due to timer interrupt,
+        * perf counter overflow, or both.
         */
-       int vpflags = dvpe();
-       write_c0_compare (read_c0_count() - 1);
-       clear_c0_cause(CPUCTR_IMASKBIT);
-       evpe(vpflags);
-
-       if (cpu_data[cpu].vpe_id == 0) {
-               timer_interrupt(irq, dev_id, regs);
-               scroll_display_message();
-       } else
-               write_c0_compare (read_c0_count() + ( mips_hpt_frequency/HZ));
-       smtc_timer_broadcast(cpu_data[cpu].vpe_id);
+       if (read_c0_cause() & (1 << 26))
+               perf_irq(regs);
 
-       if (cpu != 0)
+       if (read_c0_cause() & (1 << 30)) {
+               /* If timer interrupt, make it de-assert */
+               write_c0_compare (read_c0_count() - 1);
                /*
-                * Other CPUs should do profiling and process accounting
+                * DVPE is necessary so long as cross-VPE interrupts
+                * are done via read-modify-write of Cause register.
                 */
-               local_timer_interrupt(irq, dev_id, regs);
-
+               vpflags = dvpe();
+               clear_c0_cause(CPUCTR_IMASKBIT);
+               evpe(vpflags);
+               /*
+                * There are things we only want to do once per tick
+                * in an "MP" system.   One TC of each VPE will take
+                * the actual timer interrupt.  The others will get
+                * timer broadcast IPIs. We use whoever it is that takes
+                * the tick on VPE 0 to run the full timer_interrupt().
+                */
+               if (cpu_data[cpu].vpe_id == 0) {
+                               timer_interrupt(irq, NULL, regs);
+                               smtc_timer_broadcast(cpu_data[cpu].vpe_id);
+                               scroll_display_message();
+               } else {
+                       write_c0_compare(read_c0_count() +
+                                        (mips_hpt_frequency/HZ));
+                       local_timer_interrupt(irq, dev_id, regs);
+                       smtc_timer_broadcast(cpu_data[cpu].vpe_id);
+               }
+       }
 #else /* CONFIG_MIPS_MT_SMTC */
+       int r2 = cpu_has_mips_r2;
+
        if (cpu == 0) {
                /*
                 * CPU 0 handles the global timer interrupt job and process
@@ -161,9 +177,8 @@ irqreturn_t mips_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
                 */
                local_timer_interrupt(irq, dev_id, regs);
        }
-#endif /* CONFIG_MIPS_MT_SMTC */
-
 out:
+#endif /* CONFIG_MIPS_MT_SMTC */
        return IRQ_HANDLED;
 }