This commit was manufactured by cvs2svn to create tag
[linux-2.6.git] / arch / ppc64 / kernel / irq.c
index 8461687..2f777d5 100644 (file)
@@ -143,6 +143,47 @@ EXPORT_SYMBOL(synchronize_irq);
 
 #endif /* CONFIG_SMP */
 
+/* XXX Make this into free_irq() - Anton */
+
+/* This could be promoted to a real free_irq() ... */
+static int
+do_free_irq(int irq, void* dev_id)
+{
+       irq_desc_t *desc = get_irq_desc(irq);
+       struct irqaction **p;
+       unsigned long flags;
+
+       spin_lock_irqsave(&desc->lock,flags);
+       p = &desc->action;
+       for (;;) {
+               struct irqaction * action = *p;
+               if (action) {
+                       struct irqaction **pp = p;
+                       p = &action->next;
+                       if (action->dev_id != dev_id)
+                               continue;
+
+                       /* Found it - now remove it from the list of entries */
+                       *pp = action->next;
+                       if (!desc->action) {
+                               desc->status |= IRQ_DISABLED;
+                               mask_irq(irq);
+                       }
+                       spin_unlock_irqrestore(&desc->lock,flags);
+
+                       /* Wait to make sure it's not being used on another CPU */
+                       synchronize_irq(irq);
+                       kfree(action);
+                       return 0;
+               }
+               printk("Trying to free free IRQ%d\n",irq);
+               spin_unlock_irqrestore(&desc->lock,flags);
+               break;
+       }
+       return -ENOENT;
+}
+
+
 int request_irq(unsigned int irq,
        irqreturn_t (*handler)(int, void *, struct pt_regs *),
        unsigned long irqflags, const char * devname, void *dev_id)
@@ -153,7 +194,8 @@ int request_irq(unsigned int irq,
        if (irq >= NR_IRQS)
                return -EINVAL;
        if (!handler)
-               return -EINVAL;
+               /* We could implement really free_irq() instead of that... */
+               return do_free_irq(irq, dev_id);
 
        action = (struct irqaction *)
                kmalloc(sizeof(struct irqaction), GFP_KERNEL);
@@ -180,38 +222,7 @@ EXPORT_SYMBOL(request_irq);
 
 void free_irq(unsigned int irq, void *dev_id)
 {
-       irq_desc_t *desc = get_irq_desc(irq);
-       struct irqaction **p;
-       unsigned long flags;
-
-       spin_lock_irqsave(&desc->lock,flags);
-       p = &desc->action;
-       for (;;) {
-               struct irqaction * action = *p;
-               if (action) {
-                       struct irqaction **pp = p;
-                       p = &action->next;
-                       if (action->dev_id != dev_id)
-                               continue;
-
-                       /* Found it - now remove it from the list of entries */
-                       *pp = action->next;
-                       if (!desc->action) {
-                               desc->status |= IRQ_DISABLED;
-                               mask_irq(irq);
-                       }
-                       spin_unlock_irqrestore(&desc->lock,flags);
-
-                       /* Wait to make sure it's not being used on another CPU */
-                       synchronize_irq(irq);
-                       kfree(action);
-                       return;
-               }
-               printk("Trying to free free IRQ%d\n",irq);
-               spin_unlock_irqrestore(&desc->lock,flags);
-               break;
-       }
-       return;
+       request_irq(irq, NULL, 0, NULL, dev_id);
 }
 
 EXPORT_SYMBOL(free_irq);
@@ -578,7 +589,7 @@ out:
 }
 
 #ifdef CONFIG_PPC_ISERIES
-void do_IRQ(struct pt_regs *regs)
+int do_IRQ(struct pt_regs *regs)
 {
        struct paca_struct *lpaca;
        struct ItLpQueue *lpq;
@@ -618,13 +629,15 @@ void do_IRQ(struct pt_regs *regs)
                /* Signal a fake decrementer interrupt */
                timer_interrupt(regs);
        }
+
+       return 1; /* lets ret_from_int know we can do checks */
 }
 
 #else  /* CONFIG_PPC_ISERIES */
 
-void do_IRQ(struct pt_regs *regs)
+int do_IRQ(struct pt_regs *regs)
 {
-       int irq;
+       int irq, first = 1;
 
        irq_enter();
 
@@ -643,15 +656,25 @@ void do_IRQ(struct pt_regs *regs)
        }
 #endif
 
-       irq = ppc_md.get_irq(regs);
-
-       if (irq >= 0)
+       /*
+        * Every arch is required to implement ppc_md.get_irq.
+        * This function will either return an irq number or -1 to
+        * indicate there are no more pending.  But the first time
+        * through the loop this means there wasn't an IRQ pending.
+        * The value -2 is for buggy hardware and means that this IRQ
+        * has already been handled. -- Tom
+        */
+       while ((irq = ppc_md.get_irq(regs)) >= 0) {
                ppc_irq_dispatch_handler(regs, irq);
-       else
+               first = 0;
+       }
+       if (irq != -2 && first)
                /* That's not SMP safe ... but who cares ? */
                ppc_spurious_interrupts++;
 
        irq_exit();
+
+       return 1; /* lets ret_from_int know we can do checks */
 }
 #endif /* CONFIG_PPC_ISERIES */