sparc64: Add interface for registering a performance counter IRQ handler.
authorDavid S. Miller <davem@davemloft.net>
Fri, 21 Nov 2008 10:06:07 +0000 (02:06 -0800)
committerDavid S. Miller <davem@davemloft.net>
Thu, 4 Dec 2008 17:17:00 +0000 (09:17 -0800)
Signed-off-by: David S. Miller <davem@davemloft.net>
arch/sparc/include/asm/irq_64.h
arch/sparc64/kernel/irq.c
arch/sparc64/kernel/ttable.S

index 71673eca3660be0dcce7359c88aa9edcae0e7ee1..d47d4a1955a974169b8a9bf432915c5d4263807c 100644 (file)
@@ -66,6 +66,9 @@ extern void virt_irq_free(unsigned int virt_irq);
 extern void __init init_IRQ(void);
 extern void fixup_irqs(void);
 
+extern int register_perfctr_intr(void (*handler)(struct pt_regs *));
+extern void release_perfctr_intr(void (*handler)(struct pt_regs *));
+
 static inline void set_softint(unsigned long bits)
 {
        __asm__ __volatile__("wr        %0, 0x0, %%set_softint"
index 52fc836f464d979655c330b43b15736fc6c840a2..a3ea2bcb95de6a39ebb7bf297ee2355f507f178c 100644 (file)
@@ -775,6 +775,69 @@ void do_softirq(void)
        local_irq_restore(flags);
 }
 
+static void unhandled_perf_irq(struct pt_regs *regs)
+{
+       unsigned long pcr, pic;
+
+       read_pcr(pcr);
+       read_pic(pic);
+
+       write_pcr(0);
+
+       printk(KERN_EMERG "CPU %d: Got unexpected perf counter IRQ.\n",
+              smp_processor_id());
+       printk(KERN_EMERG "CPU %d: PCR[%016lx] PIC[%016lx]\n",
+              smp_processor_id(), pcr, pic);
+}
+
+/* Almost a direct copy of the powerpc PMC code.  */
+static DEFINE_SPINLOCK(perf_irq_lock);
+static void *perf_irq_owner_caller; /* mostly for debugging */
+static void (*perf_irq)(struct pt_regs *regs) = unhandled_perf_irq;
+
+/* Invoked from level 15 PIL handler in trap table.  */
+void perfctr_irq(int irq, struct pt_regs *regs)
+{
+       clear_softint(1 << irq);
+       perf_irq(regs);
+}
+
+int register_perfctr_intr(void (*handler)(struct pt_regs *))
+{
+       int ret;
+
+       if (!handler)
+               return -EINVAL;
+
+       spin_lock(&perf_irq_lock);
+       if (perf_irq != unhandled_perf_irq) {
+               printk(KERN_WARNING "register_perfctr_intr: "
+                      "perf IRQ busy (reserved by caller %p)\n",
+                      perf_irq_owner_caller);
+               ret = -EBUSY;
+               goto out;
+       }
+
+       perf_irq_owner_caller = __builtin_return_address(0);
+       perf_irq = handler;
+
+       ret = 0;
+out:
+       spin_unlock(&perf_irq_lock);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(register_perfctr_intr);
+
+void release_perfctr_intr(void (*handler)(struct pt_regs *))
+{
+       spin_lock(&perf_irq_lock);
+       perf_irq_owner_caller = NULL;
+       perf_irq = unhandled_perf_irq;
+       spin_unlock(&perf_irq_lock);
+}
+EXPORT_SYMBOL_GPL(release_perfctr_intr);
+
 #ifdef CONFIG_HOTPLUG_CPU
 void fixup_irqs(void)
 {
index 1ade3d6fb7fc7d1bd748a7c47852c027328efa76..2a31ffa4c28d9385a8eb211dd174048c9cfee368 100644 (file)
@@ -66,7 +66,7 @@ tl0_irq6:     BTRAP(0x46)
 tl0_irq7:      BTRAP(0x47) BTRAP(0x48) BTRAP(0x49)
 tl0_irq10:     BTRAP(0x4a) BTRAP(0x4b) BTRAP(0x4c) BTRAP(0x4d)
 tl0_irq14:     TRAP_IRQ(timer_interrupt, 14)
-tl0_irq15:     TRAP_IRQ(handler_irq, 15)
+tl0_irq15:     TRAP_IRQ(perfctr_irq, 15)
 tl0_resv050:   BTRAP(0x50) BTRAP(0x51) BTRAP(0x52) BTRAP(0x53) BTRAP(0x54) BTRAP(0x55)
 tl0_resv056:   BTRAP(0x56) BTRAP(0x57) BTRAP(0x58) BTRAP(0x59) BTRAP(0x5a) BTRAP(0x5b)
 tl0_resv05c:   BTRAP(0x5c) BTRAP(0x5d) BTRAP(0x5e) BTRAP(0x5f)