[PATCH] powerpc: cleanup of iSeries flat device tree
[powerpc.git] / arch / powerpc / platforms / iseries / irq.c
index 3bd576e..62bbbcf 100644 (file)
 #include <linux/irq.h>
 #include <linux/spinlock.h>
 
+#include <asm/paca.h>
 #include <asm/iseries/hv_types.h>
 #include <asm/iseries/hv_lp_event.h>
 #include <asm/iseries/hv_call_xm.h>
+#include <asm/iseries/it_lp_queue.h>
 
 #include "irq.h"
+#include "pci.h"
 #include "call_pci.h"
 
+#if defined(CONFIG_SMP)
+extern void iSeries_smp_message_recv(struct pt_regs *);
+#endif
+
+#ifdef CONFIG_PCI
+
 enum pci_event_type {
        pe_bus_created          = 0,    /* PHB has been created */
        pe_bus_error            = 1,    /* PHB has failed */
@@ -76,31 +85,29 @@ struct pci_event {
        } data;
 };
 
+static DEFINE_SPINLOCK(pending_irqs_lock);
+static int num_pending_irqs;
+static int pending_irqs[NR_IRQS];
+
 static void int_received(struct pci_event *event, struct pt_regs *regs)
 {
        int irq;
-#ifdef CONFIG_IRQSTACKS
-       struct thread_info *curtp, *irqtp;
-#endif
 
        switch (event->event.xSubtype) {
        case pe_slot_interrupt:
                irq = event->event.xCorrelationToken;
-               /* Dispatch the interrupt handlers for this irq */
-#ifdef CONFIG_IRQSTACKS
-               /* Switch to the irq stack to handle this */
-               curtp = current_thread_info();
-               irqtp = hardirq_ctx[smp_processor_id()];
-               if (curtp != irqtp) {
-                       irqtp->task = curtp->task;
-                       irqtp->flags = 0;
-                       call___do_IRQ(irq, regs, irqtp);
-                       irqtp->task = NULL;
-                       if (irqtp->flags)
-                               set_bits(irqtp->flags, &curtp->flags);
-               } else
-#endif
-                       __do_IRQ(irq, regs);
+               if (irq < NR_IRQS) {
+                       spin_lock(&pending_irqs_lock);
+                       pending_irqs[irq]++;
+                       num_pending_irqs++;
+                       spin_unlock(&pending_irqs_lock);
+               } else {
+                       printk(KERN_WARNING "int_received: bad irq number %d\n",
+                                       irq);
+                       HvCallPci_eoi(event->data.slot.bus_number,
+                                       event->data.slot.sub_bus_number,
+                                       event->data.slot.dev_id);
+               }
                break;
                /* Ignore error recovery events for now */
        case pe_bus_created:
@@ -143,20 +150,11 @@ static void int_received(struct pci_event *event, struct pt_regs *regs)
 static void pci_event_handler(struct HvLpEvent *event, struct pt_regs *regs)
 {
        if (event && (event->xType == HvLpEvent_Type_PciIo)) {
-               switch (event->xFlags.xFunction) {
-               case HvLpEvent_Function_Int:
+               if (hvlpevent_is_int(event))
                        int_received((struct pci_event *)event, regs);
-                       break;
-               case HvLpEvent_Function_Ack:
+               else
                        printk(KERN_ERR
                                "pci_event_handler: unexpected ack received\n");
-                       break;
-               default:
-                       printk(KERN_ERR
-                               "pci_event_handler: unexpected event function %d\n",
-                               (int)event->xFlags.xFunction);
-                       break;
-               }
        } else if (event)
                printk(KERN_ERR
                        "pci_event_handler: Unrecognized PCI event type 0x%x\n",
@@ -315,12 +313,12 @@ static hw_irq_controller iSeries_IRQ_handler = {
  * Note that sub_bus is always 0 (at the moment at least).
  */
 int __init iSeries_allocate_IRQ(HvBusNumber bus,
-               HvSubBusNumber sub_bus, HvAgentId dev_id)
+               HvSubBusNumber sub_bus, u32 bsubbus)
 {
        int virtirq;
        unsigned int realirq;
-       u8 idsel = (dev_id >> 4);
-       u8 function = dev_id & 7;
+       u8 idsel = ISERIES_GET_DEVICE_FROM_SUBBUS(bsubbus);
+       u8 function = ISERIES_GET_FUNCTION_FROM_SUBBUS(bsubbus);
 
        realirq = (((((sub_bus << 8) + (bus - 1)) << 3) + (idsel - 1)) << 3)
                + function;
@@ -329,3 +327,41 @@ int __init iSeries_allocate_IRQ(HvBusNumber bus,
        irq_desc[virtirq].handler = &iSeries_IRQ_handler;
        return virtirq;
 }
+
+#endif /* CONFIG_PCI */
+
+/*
+ * Get the next pending IRQ.
+ */
+int iSeries_get_irq(struct pt_regs *regs)
+{
+       /* -2 means ignore this interrupt */
+       int irq = -2;
+
+#ifdef CONFIG_SMP
+       if (get_lppaca()->int_dword.fields.ipi_cnt) {
+               get_lppaca()->int_dword.fields.ipi_cnt = 0;
+               iSeries_smp_message_recv(regs);
+       }
+#endif /* CONFIG_SMP */
+       if (hvlpevent_is_pending())
+               process_hvlpevents(regs);
+
+#ifdef CONFIG_PCI
+       if (num_pending_irqs) {
+               spin_lock(&pending_irqs_lock);
+               for (irq = 0; irq < NR_IRQS; irq++) {
+                       if (pending_irqs[irq]) {
+                               pending_irqs[irq]--;
+                               num_pending_irqs--;
+                               break;
+                       }
+               }
+               spin_unlock(&pending_irqs_lock);
+               if (irq >= NR_IRQS)
+                       irq = -2;
+       }
+#endif
+
+       return irq;
+}