Merge branch 'for-next' into for-linus
[safe/jmp/linux-2.6] / kernel / irq / chip.c
index bba66e0..42ec11b 100644 (file)
 
 #include "internals.h"
 
-/**
- *     dynamic_irq_init - initialize a dynamically allocated irq
- *     @irq:   irq number to initialize
- */
-void dynamic_irq_init(unsigned int irq)
+static void dynamic_irq_init_x(unsigned int irq, bool keep_chip_data)
 {
        struct irq_desc *desc;
        unsigned long flags;
 
-       if (irq >= nr_irqs) {
+       desc = irq_to_desc(irq);
+       if (!desc) {
                WARN(1, KERN_ERR "Trying to initialize invalid IRQ%d\n", irq);
                return;
        }
 
        /* Ensure we don't have left over values from a previous use of this irq */
-       desc = irq_desc + irq;
-       spin_lock_irqsave(&desc->lock, flags);
+       raw_spin_lock_irqsave(&desc->lock, flags);
        desc->status = IRQ_DISABLED;
        desc->chip = &no_irq_chip;
        desc->handle_irq = handle_bad_irq;
        desc->depth = 1;
        desc->msi_desc = NULL;
        desc->handler_data = NULL;
-       desc->chip_data = NULL;
+       if (!keep_chip_data)
+               desc->chip_data = NULL;
        desc->action = NULL;
        desc->irq_count = 0;
        desc->irqs_unhandled = 0;
 #ifdef CONFIG_SMP
-       cpus_setall(desc->affinity);
+       cpumask_setall(desc->affinity);
+#ifdef CONFIG_GENERIC_PENDING_IRQ
+       cpumask_clear(desc->pending_mask);
+#endif
 #endif
-       spin_unlock_irqrestore(&desc->lock, flags);
+       raw_spin_unlock_irqrestore(&desc->lock, flags);
 }
 
 /**
- *     dynamic_irq_cleanup - cleanup a dynamically allocated irq
+ *     dynamic_irq_init - initialize a dynamically allocated irq
  *     @irq:   irq number to initialize
  */
-void dynamic_irq_cleanup(unsigned int irq)
+void dynamic_irq_init(unsigned int irq)
 {
-       struct irq_desc *desc;
+       dynamic_irq_init_x(irq, false);
+}
+
+/**
+ *     dynamic_irq_init_keep_chip_data - initialize a dynamically allocated irq
+ *     @irq:   irq number to initialize
+ *
+ *     does not set irq_to_desc(irq)->chip_data to NULL
+ */
+void dynamic_irq_init_keep_chip_data(unsigned int irq)
+{
+       dynamic_irq_init_x(irq, true);
+}
+
+static void dynamic_irq_cleanup_x(unsigned int irq, bool keep_chip_data)
+{
+       struct irq_desc *desc = irq_to_desc(irq);
        unsigned long flags;
 
-       if (irq >= nr_irqs) {
+       if (!desc) {
                WARN(1, KERN_ERR "Trying to cleanup invalid IRQ%d\n", irq);
                return;
        }
 
-       desc = irq_desc + irq;
-       spin_lock_irqsave(&desc->lock, flags);
+       raw_spin_lock_irqsave(&desc->lock, flags);
        if (desc->action) {
-               spin_unlock_irqrestore(&desc->lock, flags);
+               raw_spin_unlock_irqrestore(&desc->lock, flags);
                WARN(1, KERN_ERR "Destroying IRQ%d without calling free_irq\n",
                        irq);
                return;
        }
        desc->msi_desc = NULL;
        desc->handler_data = NULL;
-       desc->chip_data = NULL;
+       if (!keep_chip_data)
+               desc->chip_data = NULL;
        desc->handle_irq = handle_bad_irq;
        desc->chip = &no_irq_chip;
-       spin_unlock_irqrestore(&desc->lock, flags);
+       desc->name = NULL;
+       clear_kstat_irqs(desc);
+       raw_spin_unlock_irqrestore(&desc->lock, flags);
+}
+
+/**
+ *     dynamic_irq_cleanup - cleanup a dynamically allocated irq
+ *     @irq:   irq number to initialize
+ */
+void dynamic_irq_cleanup(unsigned int irq)
+{
+       dynamic_irq_cleanup_x(irq, false);
+}
+
+/**
+ *     dynamic_irq_cleanup_keep_chip_data - cleanup a dynamically allocated irq
+ *     @irq:   irq number to initialize
+ *
+ *     does not set irq_to_desc(irq)->chip_data to NULL
+ */
+void dynamic_irq_cleanup_keep_chip_data(unsigned int irq)
+{
+       dynamic_irq_cleanup_x(irq, true);
 }
 
 
@@ -89,10 +127,10 @@ void dynamic_irq_cleanup(unsigned int irq)
  */
 int set_irq_chip(unsigned int irq, struct irq_chip *chip)
 {
-       struct irq_desc *desc;
+       struct irq_desc *desc = irq_to_desc(irq);
        unsigned long flags;
 
-       if (irq >= nr_irqs) {
+       if (!desc) {
                WARN(1, KERN_ERR "Trying to install chip for IRQ%d\n", irq);
                return -EINVAL;
        }
@@ -100,11 +138,10 @@ int set_irq_chip(unsigned int irq, struct irq_chip *chip)
        if (!chip)
                chip = &no_irq_chip;
 
-       desc = irq_desc + irq;
-       spin_lock_irqsave(&desc->lock, flags);
+       raw_spin_lock_irqsave(&desc->lock, flags);
        irq_chip_set_defaults(chip);
        desc->chip = chip;
-       spin_unlock_irqrestore(&desc->lock, flags);
+       raw_spin_unlock_irqrestore(&desc->lock, flags);
 
        return 0;
 }
@@ -117,22 +154,22 @@ EXPORT_SYMBOL(set_irq_chip);
  */
 int set_irq_type(unsigned int irq, unsigned int type)
 {
-       struct irq_desc *desc;
+       struct irq_desc *desc = irq_to_desc(irq);
        unsigned long flags;
        int ret = -ENXIO;
 
-       if (irq >= nr_irqs) {
+       if (!desc) {
                printk(KERN_ERR "Trying to set irq type for IRQ%d\n", irq);
                return -ENODEV;
        }
 
-       desc = irq_desc + irq;
+       type &= IRQ_TYPE_SENSE_MASK;
        if (type == IRQ_TYPE_NONE)
                return 0;
 
-       spin_lock_irqsave(&desc->lock, flags);
-       ret = __irq_set_trigger(desc, irq, flags);
-       spin_unlock_irqrestore(&desc->lock, flags);
+       raw_spin_lock_irqsave(&desc->lock, flags);
+       ret = __irq_set_trigger(desc, irq, type);
+       raw_spin_unlock_irqrestore(&desc->lock, flags);
        return ret;
 }
 EXPORT_SYMBOL(set_irq_type);
@@ -146,46 +183,45 @@ EXPORT_SYMBOL(set_irq_type);
  */
 int set_irq_data(unsigned int irq, void *data)
 {
-       struct irq_desc *desc;
+       struct irq_desc *desc = irq_to_desc(irq);
        unsigned long flags;
 
-       if (irq >= nr_irqs) {
+       if (!desc) {
                printk(KERN_ERR
                       "Trying to install controller data for IRQ%d\n", irq);
                return -EINVAL;
        }
 
-       desc = irq_desc + irq;
-       spin_lock_irqsave(&desc->lock, flags);
+       raw_spin_lock_irqsave(&desc->lock, flags);
        desc->handler_data = data;
-       spin_unlock_irqrestore(&desc->lock, flags);
+       raw_spin_unlock_irqrestore(&desc->lock, flags);
        return 0;
 }
 EXPORT_SYMBOL(set_irq_data);
 
 /**
- *     set_irq_data - set irq type data for an irq
+ *     set_irq_msi - set MSI descriptor data for an irq
  *     @irq:   Interrupt number
  *     @entry: Pointer to MSI descriptor data
  *
- *     Set the hardware irq controller data for an irq
+ *     Set the MSI descriptor entry for an irq
  */
 int set_irq_msi(unsigned int irq, struct msi_desc *entry)
 {
-       struct irq_desc *desc;
+       struct irq_desc *desc = irq_to_desc(irq);
        unsigned long flags;
 
-       if (irq >= nr_irqs) {
+       if (!desc) {
                printk(KERN_ERR
                       "Trying to install msi data for IRQ%d\n", irq);
                return -EINVAL;
        }
-       desc = irq_desc + irq;
-       spin_lock_irqsave(&desc->lock, flags);
+
+       raw_spin_lock_irqsave(&desc->lock, flags);
        desc->msi_desc = entry;
        if (entry)
                entry->irq = irq;
-       spin_unlock_irqrestore(&desc->lock, flags);
+       raw_spin_unlock_irqrestore(&desc->lock, flags);
        return 0;
 }
 
@@ -198,28 +234,62 @@ int set_irq_msi(unsigned int irq, struct msi_desc *entry)
  */
 int set_irq_chip_data(unsigned int irq, void *data)
 {
-       struct irq_desc *desc = irq_desc + irq;
+       struct irq_desc *desc = irq_to_desc(irq);
        unsigned long flags;
 
-       if (irq >= nr_irqs || !desc->chip) {
+       if (!desc) {
+               printk(KERN_ERR
+                      "Trying to install chip data for IRQ%d\n", irq);
+               return -EINVAL;
+       }
+
+       if (!desc->chip) {
                printk(KERN_ERR "BUG: bad set_irq_chip_data(IRQ#%d)\n", irq);
                return -EINVAL;
        }
 
-       spin_lock_irqsave(&desc->lock, flags);
+       raw_spin_lock_irqsave(&desc->lock, flags);
        desc->chip_data = data;
-       spin_unlock_irqrestore(&desc->lock, flags);
+       raw_spin_unlock_irqrestore(&desc->lock, flags);
 
        return 0;
 }
 EXPORT_SYMBOL(set_irq_chip_data);
 
+/**
+ *     set_irq_nested_thread - Set/Reset the IRQ_NESTED_THREAD flag of an irq
+ *
+ *     @irq:   Interrupt number
+ *     @nest:  0 to clear / 1 to set the IRQ_NESTED_THREAD flag
+ *
+ *     The IRQ_NESTED_THREAD flag indicates that on
+ *     request_threaded_irq() no separate interrupt thread should be
+ *     created for the irq as the handler are called nested in the
+ *     context of a demultiplexing interrupt handler thread.
+ */
+void set_irq_nested_thread(unsigned int irq, int nest)
+{
+       struct irq_desc *desc = irq_to_desc(irq);
+       unsigned long flags;
+
+       if (!desc)
+               return;
+
+       raw_spin_lock_irqsave(&desc->lock, flags);
+       if (nest)
+               desc->status |= IRQ_NESTED_THREAD;
+       else
+               desc->status &= ~IRQ_NESTED_THREAD;
+       raw_spin_unlock_irqrestore(&desc->lock, flags);
+}
+EXPORT_SYMBOL_GPL(set_irq_nested_thread);
+
 /*
  * default enable function
  */
 static void default_enable(unsigned int irq)
 {
-       struct irq_desc *desc = irq_desc + irq;
+       struct irq_desc *desc = irq_to_desc(irq);
 
        desc->chip->unmask(irq);
        desc->status &= ~IRQ_MASKED;
@@ -237,8 +307,9 @@ static void default_disable(unsigned int irq)
  */
 static unsigned int default_startup(unsigned int irq)
 {
-       irq_desc[irq].chip->enable(irq);
+       struct irq_desc *desc = irq_to_desc(irq);
 
+       desc->chip->enable(irq);
        return 0;
 }
 
@@ -247,7 +318,7 @@ static unsigned int default_startup(unsigned int irq)
  */
 static void default_shutdown(unsigned int irq)
 {
-       struct irq_desc *desc = irq_desc + irq;
+       struct irq_desc *desc = irq_to_desc(irq);
 
        desc->chip->mask(irq);
        desc->status |= IRQ_MASKED;
@@ -285,10 +356,50 @@ static inline void mask_ack_irq(struct irq_desc *desc, int irq)
                desc->chip->mask_ack(irq);
        else {
                desc->chip->mask(irq);
-               desc->chip->ack(irq);
+               if (desc->chip->ack)
+                       desc->chip->ack(irq);
        }
 }
 
+/*
+ *     handle_nested_irq - Handle a nested irq from a irq thread
+ *     @irq:   the interrupt number
+ *
+ *     Handle interrupts which are nested into a threaded interrupt
+ *     handler. The handler function is called inside the calling
+ *     threads context.
+ */
+void handle_nested_irq(unsigned int irq)
+{
+       struct irq_desc *desc = irq_to_desc(irq);
+       struct irqaction *action;
+       irqreturn_t action_ret;
+
+       might_sleep();
+
+       raw_spin_lock_irq(&desc->lock);
+
+       kstat_incr_irqs_this_cpu(irq, desc);
+
+       action = desc->action;
+       if (unlikely(!action || (desc->status & IRQ_DISABLED)))
+               goto out_unlock;
+
+       desc->status |= IRQ_INPROGRESS;
+       raw_spin_unlock_irq(&desc->lock);
+
+       action_ret = action->thread_fn(action->irq, action->dev_id);
+       if (!noirqdebug)
+               note_interrupt(irq, desc, action_ret);
+
+       raw_spin_lock_irq(&desc->lock);
+       desc->status &= ~IRQ_INPROGRESS;
+
+out_unlock:
+       raw_spin_unlock_irq(&desc->lock);
+}
+EXPORT_SYMBOL_GPL(handle_nested_irq);
+
 /**
  *     handle_simple_irq - Simple and software-decoded IRQs.
  *     @irq:   the interrupt number
@@ -306,30 +417,29 @@ handle_simple_irq(unsigned int irq, struct irq_desc *desc)
 {
        struct irqaction *action;
        irqreturn_t action_ret;
-       const unsigned int cpu = smp_processor_id();
 
-       spin_lock(&desc->lock);
+       raw_spin_lock(&desc->lock);
 
        if (unlikely(desc->status & IRQ_INPROGRESS))
                goto out_unlock;
        desc->status &= ~(IRQ_REPLAY | IRQ_WAITING);
-       kstat_cpu(cpu).irqs[irq]++;
+       kstat_incr_irqs_this_cpu(irq, desc);
 
        action = desc->action;
        if (unlikely(!action || (desc->status & IRQ_DISABLED)))
                goto out_unlock;
 
        desc->status |= IRQ_INPROGRESS;
-       spin_unlock(&desc->lock);
+       raw_spin_unlock(&desc->lock);
 
        action_ret = handle_IRQ_event(irq, action);
        if (!noirqdebug)
                note_interrupt(irq, desc, action_ret);
 
-       spin_lock(&desc->lock);
+       raw_spin_lock(&desc->lock);
        desc->status &= ~IRQ_INPROGRESS;
 out_unlock:
-       spin_unlock(&desc->lock);
+       raw_spin_unlock(&desc->lock);
 }
 
 /**
@@ -345,17 +455,16 @@ out_unlock:
 void
 handle_level_irq(unsigned int irq, struct irq_desc *desc)
 {
-       unsigned int cpu = smp_processor_id();
        struct irqaction *action;
        irqreturn_t action_ret;
 
-       spin_lock(&desc->lock);
+       raw_spin_lock(&desc->lock);
        mask_ack_irq(desc, irq);
 
        if (unlikely(desc->status & IRQ_INPROGRESS))
                goto out_unlock;
        desc->status &= ~(IRQ_REPLAY | IRQ_WAITING);
-       kstat_cpu(cpu).irqs[irq]++;
+       kstat_incr_irqs_this_cpu(irq, desc);
 
        /*
         * If its disabled or no action available
@@ -366,19 +475,23 @@ handle_level_irq(unsigned int irq, struct irq_desc *desc)
                goto out_unlock;
 
        desc->status |= IRQ_INPROGRESS;
-       spin_unlock(&desc->lock);
+       raw_spin_unlock(&desc->lock);
 
        action_ret = handle_IRQ_event(irq, action);
        if (!noirqdebug)
                note_interrupt(irq, desc, action_ret);
 
-       spin_lock(&desc->lock);
+       raw_spin_lock(&desc->lock);
        desc->status &= ~IRQ_INPROGRESS;
-       if (!(desc->status & IRQ_DISABLED) && desc->chip->unmask)
+
+       if (unlikely(desc->status & IRQ_ONESHOT))
+               desc->status |= IRQ_MASKED;
+       else if (!(desc->status & IRQ_DISABLED) && desc->chip->unmask)
                desc->chip->unmask(irq);
 out_unlock:
-       spin_unlock(&desc->lock);
+       raw_spin_unlock(&desc->lock);
 }
+EXPORT_SYMBOL_GPL(handle_level_irq);
 
 /**
  *     handle_fasteoi_irq - irq handler for transparent controllers
@@ -393,17 +506,16 @@ out_unlock:
 void
 handle_fasteoi_irq(unsigned int irq, struct irq_desc *desc)
 {
-       unsigned int cpu = smp_processor_id();
        struct irqaction *action;
        irqreturn_t action_ret;
 
-       spin_lock(&desc->lock);
+       raw_spin_lock(&desc->lock);
 
        if (unlikely(desc->status & IRQ_INPROGRESS))
                goto out;
 
        desc->status &= ~(IRQ_REPLAY | IRQ_WAITING);
-       kstat_cpu(cpu).irqs[irq]++;
+       kstat_incr_irqs_this_cpu(irq, desc);
 
        /*
         * If its disabled or no action available
@@ -419,18 +531,18 @@ handle_fasteoi_irq(unsigned int irq, struct irq_desc *desc)
 
        desc->status |= IRQ_INPROGRESS;
        desc->status &= ~IRQ_PENDING;
-       spin_unlock(&desc->lock);
+       raw_spin_unlock(&desc->lock);
 
        action_ret = handle_IRQ_event(irq, action);
        if (!noirqdebug)
                note_interrupt(irq, desc, action_ret);
 
-       spin_lock(&desc->lock);
+       raw_spin_lock(&desc->lock);
        desc->status &= ~IRQ_INPROGRESS;
 out:
        desc->chip->eoi(irq);
 
-       spin_unlock(&desc->lock);
+       raw_spin_unlock(&desc->lock);
 }
 
 /**
@@ -442,7 +554,7 @@ out:
  *     signal. The occurence is latched into the irq controller hardware
  *     and must be acked in order to be reenabled. After the ack another
  *     interrupt can happen on the same source even before the first one
- *     is handled by the assosiacted event handler. If this happens it
+ *     is handled by the associated event handler. If this happens it
  *     might be necessary to disable (mask) the interrupt depending on the
  *     controller hardware. This requires to reenable the interrupt inside
  *     of the loop which handles the interrupts which have arrived while
@@ -452,9 +564,7 @@ out:
 void
 handle_edge_irq(unsigned int irq, struct irq_desc *desc)
 {
-       const unsigned int cpu = smp_processor_id();
-
-       spin_lock(&desc->lock);
+       raw_spin_lock(&desc->lock);
 
        desc->status &= ~(IRQ_REPLAY | IRQ_WAITING);
 
@@ -469,11 +579,11 @@ handle_edge_irq(unsigned int irq, struct irq_desc *desc)
                mask_ack_irq(desc, irq);
                goto out_unlock;
        }
-
-       kstat_cpu(cpu).irqs[irq]++;
+       kstat_incr_irqs_this_cpu(irq, desc);
 
        /* Start handling the irq */
-       desc->chip->ack(irq);
+       if (desc->chip->ack)
+               desc->chip->ack(irq);
 
        /* Mark the IRQ currently in progress.*/
        desc->status |= IRQ_INPROGRESS;
@@ -500,21 +610,21 @@ handle_edge_irq(unsigned int irq, struct irq_desc *desc)
                }
 
                desc->status &= ~IRQ_PENDING;
-               spin_unlock(&desc->lock);
+               raw_spin_unlock(&desc->lock);
                action_ret = handle_IRQ_event(irq, action);
                if (!noirqdebug)
                        note_interrupt(irq, desc, action_ret);
-               spin_lock(&desc->lock);
+               raw_spin_lock(&desc->lock);
 
        } while ((desc->status & (IRQ_PENDING | IRQ_DISABLED)) == IRQ_PENDING);
 
        desc->status &= ~IRQ_INPROGRESS;
 out_unlock:
-       spin_unlock(&desc->lock);
+       raw_spin_unlock(&desc->lock);
 }
 
 /**
- *     handle_percpu_IRQ - Per CPU local irq handler
+ *     handle_percpu_irq - Per CPU local irq handler
  *     @irq:   the interrupt number
  *     @desc:  the interrupt description structure for this irq
  *
@@ -525,7 +635,7 @@ handle_percpu_irq(unsigned int irq, struct irq_desc *desc)
 {
        irqreturn_t action_ret;
 
-       kstat_this_cpu.irqs[irq]++;
+       kstat_incr_irqs_this_cpu(irq, desc);
 
        if (desc->chip->ack)
                desc->chip->ack(irq);
@@ -542,17 +652,15 @@ void
 __set_irq_handler(unsigned int irq, irq_flow_handler_t handle, int is_chained,
                  const char *name)
 {
-       struct irq_desc *desc;
+       struct irq_desc *desc = irq_to_desc(irq);
        unsigned long flags;
 
-       if (irq >= nr_irqs) {
+       if (!desc) {
                printk(KERN_ERR
                       "Trying to install type control for IRQ%d\n", irq);
                return;
        }
 
-       desc = irq_desc + irq;
-
        if (!handle)
                handle = handle_bad_irq;
        else if (desc->chip == &no_irq_chip) {
@@ -568,7 +676,8 @@ __set_irq_handler(unsigned int irq, irq_flow_handler_t handle, int is_chained,
                desc->chip = &dummy_irq_chip;
        }
 
-       spin_lock_irqsave(&desc->lock, flags);
+       chip_bus_lock(irq, desc);
+       raw_spin_lock_irqsave(&desc->lock, flags);
 
        /* Uninstall? */
        if (handle == handle_bad_irq) {
@@ -586,8 +695,10 @@ __set_irq_handler(unsigned int irq, irq_flow_handler_t handle, int is_chained,
                desc->depth = 0;
                desc->chip->startup(irq);
        }
-       spin_unlock_irqrestore(&desc->lock, flags);
+       raw_spin_unlock_irqrestore(&desc->lock, flags);
+       chip_bus_sync_unlock(irq, desc);
 }
+EXPORT_SYMBOL_GPL(__set_irq_handler);
 
 void
 set_irq_chip_and_handler(unsigned int irq, struct irq_chip *chip,
@@ -607,36 +718,30 @@ set_irq_chip_and_handler_name(unsigned int irq, struct irq_chip *chip,
 
 void __init set_irq_noprobe(unsigned int irq)
 {
-       struct irq_desc *desc;
+       struct irq_desc *desc = irq_to_desc(irq);
        unsigned long flags;
 
-       if (irq >= nr_irqs) {
+       if (!desc) {
                printk(KERN_ERR "Trying to mark IRQ%d non-probeable\n", irq);
-
                return;
        }
 
-       desc = irq_desc + irq;
-
-       spin_lock_irqsave(&desc->lock, flags);
+       raw_spin_lock_irqsave(&desc->lock, flags);
        desc->status |= IRQ_NOPROBE;
-       spin_unlock_irqrestore(&desc->lock, flags);
+       raw_spin_unlock_irqrestore(&desc->lock, flags);
 }
 
 void __init set_irq_probe(unsigned int irq)
 {
-       struct irq_desc *desc;
+       struct irq_desc *desc = irq_to_desc(irq);
        unsigned long flags;
 
-       if (irq >= nr_irqs) {
+       if (!desc) {
                printk(KERN_ERR "Trying to mark IRQ%d probeable\n", irq);
-
                return;
        }
 
-       desc = irq_desc + irq;
-
-       spin_lock_irqsave(&desc->lock, flags);
+       raw_spin_lock_irqsave(&desc->lock, flags);
        desc->status &= ~IRQ_NOPROBE;
-       spin_unlock_irqrestore(&desc->lock, flags);
+       raw_spin_unlock_irqrestore(&desc->lock, flags);
 }