[PATCH] setup_irq(): better mismatch debugging
[sfrench/cifs-2.6.git] / kernel / irq / manage.c
index 4e461438e48bfe687f4ced338a0f2c69791fb33e..b385878c6e807924efbf432644f506297d28097a 100644 (file)
@@ -137,16 +137,40 @@ EXPORT_SYMBOL(enable_irq);
  *     @irq:   interrupt to control
  *     @on:    enable/disable power management wakeup
  *
- *     Enable/disable power management wakeup mode
+ *     Enable/disable power management wakeup mode, which is
+ *     disabled by default.  Enables and disables must match,
+ *     just as they match for non-wakeup mode support.
+ *
+ *     Wakeup mode lets this IRQ wake the system from sleep
+ *     states like "suspend to RAM".
  */
 int set_irq_wake(unsigned int irq, unsigned int on)
 {
        struct irq_desc *desc = irq_desc + irq;
        unsigned long flags;
        int ret = -ENXIO;
+       int (*set_wake)(unsigned, unsigned) = desc->chip->set_wake;
 
+       /* wakeup-capable irqs can be shared between drivers that
+        * don't need to have the same sleep mode behaviors.
+        */
        spin_lock_irqsave(&desc->lock, flags);
-       if (desc->chip->set_wake)
+       if (on) {
+               if (desc->wake_depth++ == 0)
+                       desc->status |= IRQ_WAKEUP;
+               else
+                       set_wake = NULL;
+       } else {
+               if (desc->wake_depth == 0) {
+                       printk(KERN_WARNING "Unbalanced IRQ %d "
+                                       "wake disable\n", irq);
+                       WARN_ON(1);
+               } else if (--desc->wake_depth == 0)
+                       desc->status &= ~IRQ_WAKEUP;
+               else
+                       set_wake = NULL;
+       }
+       if (set_wake)
                ret = desc->chip->set_wake(irq, on);
        spin_unlock_irqrestore(&desc->lock, flags);
        return ret;
@@ -192,6 +216,7 @@ int setup_irq(unsigned int irq, struct irqaction *new)
 {
        struct irq_desc *desc = irq_desc + irq;
        struct irqaction *old, **p;
+       const char *old_name = NULL;
        unsigned long flags;
        int shared = 0;
 
@@ -231,8 +256,10 @@ int setup_irq(unsigned int irq, struct irqaction *new)
                 * set the trigger type must match.
                 */
                if (!((old->flags & new->flags) & IRQF_SHARED) ||
-                   ((old->flags ^ new->flags) & IRQF_TRIGGER_MASK))
+                   ((old->flags ^ new->flags) & IRQF_TRIGGER_MASK)) {
+                       old_name = old->name;
                        goto mismatch;
+               }
 
 #if defined(CONFIG_IRQ_PER_CPU)
                /* All handlers must agree on per-cpuness */
@@ -298,11 +325,13 @@ int setup_irq(unsigned int irq, struct irqaction *new)
        return 0;
 
 mismatch:
-       spin_unlock_irqrestore(&desc->lock, flags);
        if (!(new->flags & IRQF_PROBE_SHARED)) {
                printk(KERN_ERR "IRQ handler type mismatch for IRQ %d\n", irq);
+               if (old_name)
+                       printk(KERN_ERR "current handler: %s\n", old_name);
                dump_stack();
        }
+       spin_unlock_irqrestore(&desc->lock, flags);
        return -EBUSY;
 }
 
@@ -403,8 +432,7 @@ EXPORT_SYMBOL(free_irq);
  *     IRQF_SAMPLE_RANDOM      The interrupt can be used for entropy
  *
  */
-int request_irq(unsigned int irq,
-               irqreturn_t (*handler)(int, void *, struct pt_regs *),
+int request_irq(unsigned int irq, irq_handler_t handler,
                unsigned long irqflags, const char *devname, void *dev_id)
 {
        struct irqaction *action;
@@ -451,4 +479,3 @@ int request_irq(unsigned int irq,
        return retval;
 }
 EXPORT_SYMBOL(request_irq);
-