]> git.kernelconcepts.de Git - karo-tx-linux.git/blobdiff - kernel/irq/manage.c
Merge branch 'linus' into x86/x2apic
[karo-tx-linux.git] / kernel / irq / manage.c
index 909b2231fa93cd82e69f4432e1eb2f99c5146a99..63b93a9355656d99fd216d59a6734b30c8517f93 100644 (file)
@@ -224,6 +224,17 @@ void enable_irq(unsigned int irq)
 }
 EXPORT_SYMBOL(enable_irq);
 
+int set_irq_wake_real(unsigned int irq, unsigned int on)
+{
+       struct irq_desc *desc = irq_desc + irq;
+       int ret = -ENXIO;
+
+       if (desc->chip->set_wake)
+               ret = desc->chip->set_wake(irq, on);
+
+       return ret;
+}
+
 /**
  *     set_irq_wake - control irq power management wakeup
  *     @irq:   interrupt to control
@@ -240,30 +251,34 @@ 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;
+       int ret = 0;
 
        /* 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 (on) {
-               if (desc->wake_depth++ == 0)
-                       desc->status |= IRQ_WAKEUP;
-               else
-                       set_wake = NULL;
+               if (desc->wake_depth++ == 0) {
+                       ret = set_irq_wake_real(irq, on);
+                       if (ret)
+                               desc->wake_depth = 0;
+                       else
+                               desc->status |= IRQ_WAKEUP;
+               }
        } 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;
+               } else if (--desc->wake_depth == 0) {
+                       ret = set_irq_wake_real(irq, on);
+                       if (ret)
+                               desc->wake_depth = 1;
+                       else
+                               desc->status &= ~IRQ_WAKEUP;
+               }
        }
-       if (set_wake)
-               ret = desc->chip->set_wake(irq, on);
+
        spin_unlock_irqrestore(&desc->lock, flags);
        return ret;
 }
@@ -300,6 +315,30 @@ void compat_irq_chip_set_default_handler(struct irq_desc *desc)
                desc->handle_irq = NULL;
 }
 
+static int __irq_set_trigger(struct irq_chip *chip, unsigned int irq,
+               unsigned long flags)
+{
+       int ret;
+
+       if (!chip || !chip->set_type) {
+               /*
+                * IRQF_TRIGGER_* but the PIC does not support multiple
+                * flow-types?
+                */
+               pr_warning("No set_type function for IRQ %d (%s)\n", irq,
+                               chip ? (chip->name ? : "unknown") : "unknown");
+               return 0;
+       }
+
+       ret = chip->set_type(irq, flags & IRQF_TRIGGER_MASK);
+
+       if (ret)
+               pr_err("setting flow type for irq %u failed (%pF)\n",
+                               irq, chip->set_type);
+
+       return ret;
+}
+
 /*
  * Internal function to register an irqaction - typically used to
  * allocate special interrupts that are part of the architecture.
@@ -311,6 +350,7 @@ int setup_irq(unsigned int irq, struct irqaction *new)
        const char *old_name = NULL;
        unsigned long flags;
        int shared = 0;
+       int ret;
 
        if (irq >= NR_IRQS)
                return -EINVAL;
@@ -368,35 +408,23 @@ int setup_irq(unsigned int irq, struct irqaction *new)
                shared = 1;
        }
 
-       *p = new;
-
-       /* Exclude IRQ from balancing */
-       if (new->flags & IRQF_NOBALANCING)
-               desc->status |= IRQ_NO_BALANCING;
-
        if (!shared) {
                irq_chip_set_defaults(desc->chip);
 
-#if defined(CONFIG_IRQ_PER_CPU)
-               if (new->flags & IRQF_PERCPU)
-                       desc->status |= IRQ_PER_CPU;
-#endif
-
                /* Setup the type (level, edge polarity) if configured: */
                if (new->flags & IRQF_TRIGGER_MASK) {
-                       if (desc->chip->set_type)
-                               desc->chip->set_type(irq,
-                                               new->flags & IRQF_TRIGGER_MASK);
-                       else
-                               /*
-                                * IRQF_TRIGGER_* but the PIC does not support
-                                * multiple flow-types?
-                                */
-                               printk(KERN_WARNING "No IRQF_TRIGGER set_type "
-                                      "function for IRQ %d (%s)\n", irq,
-                                      desc->chip->name);
+                       ret = __irq_set_trigger(desc->chip, irq, new->flags);
+
+                       if (ret) {
+                               spin_unlock_irqrestore(&desc->lock, flags);
+                               return ret;
+                       }
                } else
                        compat_irq_chip_set_default_handler(desc);
+#if defined(CONFIG_IRQ_PER_CPU)
+               if (new->flags & IRQF_PERCPU)
+                       desc->status |= IRQ_PER_CPU;
+#endif
 
                desc->status &= ~(IRQ_AUTODETECT | IRQ_WAITING |
                                  IRQ_INPROGRESS | IRQ_SPURIOUS_DISABLED);
@@ -415,6 +443,13 @@ int setup_irq(unsigned int irq, struct irqaction *new)
                /* Set default affinity mask once everything is setup */
                irq_select_affinity(irq);
        }
+
+       *p = new;
+
+       /* Exclude IRQ from balancing */
+       if (new->flags & IRQF_NOBALANCING)
+               desc->status |= IRQ_NO_BALANCING;
+
        /* Reset broken irq detection when installing new handler */
        desc->irq_count = 0;
        desc->irqs_unhandled = 0;