123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510 |
- /*
- * linux/kernel/irq/handle.c
- *
- * Copyright (C) 1992, 1998-2006 Linus Torvalds, Ingo Molnar
- * Copyright (C) 2005-2006, Thomas Gleixner, Russell King
- *
- * This file contains the core interrupt handling code.
- *
- * Detailed information is available in Documentation/DocBook/genericirq
- *
- */
- #include <linux/irq.h>
- #include <linux/module.h>
- #include <linux/random.h>
- #include <linux/interrupt.h>
- #include <linux/kernel_stat.h>
- #include "internals.h"
- /*
- * lockdep: we want to handle all irq_desc locks as a single lock-class:
- */
- static struct lock_class_key irq_desc_lock_class;
- /**
- * handle_bad_irq - handle spurious and unhandled irqs
- * @irq: the interrupt number
- * @desc: description of the interrupt
- *
- * Handles spurious and unhandled IRQ's. It also prints a debugmessage.
- */
- void
- handle_bad_irq(unsigned int irq, struct irq_desc *desc)
- {
- print_irq_desc(irq, desc);
- kstat_irqs_this_cpu(desc)++;
- ack_bad_irq(irq);
- }
- /*
- * Linux has a controller-independent interrupt architecture.
- * Every controller has a 'controller-template', that is used
- * by the main code to do the right thing. Each driver-visible
- * interrupt source is transparently wired to the appropriate
- * controller. Thus drivers need not be aware of the
- * interrupt-controller.
- *
- * The code is designed to be easily extended with new/different
- * interrupt controllers, without having to do assembly magic or
- * having to touch the generic code.
- *
- * Controller mappings for all interrupt sources:
- */
- int nr_irqs = NR_IRQS;
- EXPORT_SYMBOL_GPL(nr_irqs);
- #ifdef CONFIG_HAVE_DYN_ARRAY
- static struct irq_desc irq_desc_init = {
- .irq = -1U,
- .status = IRQ_DISABLED,
- .chip = &no_irq_chip,
- .handle_irq = handle_bad_irq,
- .depth = 1,
- .lock = __SPIN_LOCK_UNLOCKED(irq_desc_init.lock),
- #ifdef CONFIG_SMP
- .affinity = CPU_MASK_ALL
- #endif
- };
- static void init_one_irq_desc(struct irq_desc *desc)
- {
- memcpy(desc, &irq_desc_init, sizeof(struct irq_desc));
- lockdep_set_class(&desc->lock, &irq_desc_lock_class);
- }
- extern int after_bootmem;
- extern void *__alloc_bootmem_nopanic(unsigned long size,
- unsigned long align,
- unsigned long goal);
- static void init_kstat_irqs(struct irq_desc *desc, int nr_desc, int nr)
- {
- unsigned long bytes, total_bytes;
- char *ptr;
- int i;
- unsigned long phys;
- /* Compute how many bytes we need per irq and allocate them */
- bytes = nr * sizeof(unsigned int);
- total_bytes = bytes * nr_desc;
- if (after_bootmem)
- ptr = kzalloc(total_bytes, GFP_ATOMIC);
- else
- ptr = __alloc_bootmem_nopanic(total_bytes, PAGE_SIZE, 0);
- if (!ptr)
- panic(" can not allocate kstat_irqs\n");
- phys = __pa(ptr);
- printk(KERN_DEBUG "kstat_irqs ==> [%#lx - %#lx]\n", phys, phys + total_bytes);
- for (i = 0; i < nr_desc; i++) {
- desc[i].kstat_irqs = (unsigned int *)ptr;
- ptr += bytes;
- }
- }
- /*
- * Protect the sparse_irqs_free freelist:
- */
- static DEFINE_SPINLOCK(sparse_irq_lock);
- #ifdef CONFIG_HAVE_SPARSE_IRQ
- static struct irq_desc *sparse_irqs_free;
- struct irq_desc *sparse_irqs;
- #endif
- static void __init init_work(void *data)
- {
- struct dyn_array *da = data;
- int i;
- struct irq_desc *desc;
- desc = *da->name;
- for (i = 0; i < *da->nr; i++) {
- init_one_irq_desc(&desc[i]);
- #ifndef CONFIG_HAVE_SPARSE_IRQ
- desc[i].irq = i;
- #endif
- }
- /* init kstat_irqs, nr_cpu_ids is ready already */
- init_kstat_irqs(desc, *da->nr, nr_cpu_ids);
- #ifdef CONFIG_HAVE_SPARSE_IRQ
- for (i = 1; i < *da->nr; i++)
- desc[i-1].next = &desc[i];
- sparse_irqs_free = sparse_irqs;
- sparse_irqs = NULL;
- #endif
- }
- #ifdef CONFIG_HAVE_SPARSE_IRQ
- static int nr_irq_desc = 32;
- static int __init parse_nr_irq_desc(char *arg)
- {
- if (arg)
- nr_irq_desc = simple_strtoul(arg, NULL, 0);
- return 0;
- }
- early_param("nr_irq_desc", parse_nr_irq_desc);
- DEFINE_DYN_ARRAY(sparse_irqs, sizeof(struct irq_desc), nr_irq_desc, PAGE_SIZE, init_work);
- struct irq_desc *irq_to_desc(unsigned int irq)
- {
- struct irq_desc *desc;
- desc = sparse_irqs;
- while (desc) {
- if (desc->irq == irq)
- return desc;
- desc = desc->next;
- }
- return NULL;
- }
- struct irq_desc *irq_to_desc_alloc(unsigned int irq)
- {
- struct irq_desc *desc, *desc_pri;
- unsigned long flags;
- int count = 0;
- int i;
- desc_pri = desc = sparse_irqs;
- while (desc) {
- if (desc->irq == irq)
- return desc;
- desc_pri = desc;
- desc = desc->next;
- count++;
- }
- spin_lock_irqsave(&sparse_irq_lock, flags);
- /*
- * we run out of pre-allocate ones, allocate more
- */
- if (!sparse_irqs_free) {
- unsigned long phys;
- unsigned long total_bytes;
- printk(KERN_DEBUG "try to get more irq_desc %d\n", nr_irq_desc);
- total_bytes = sizeof(struct irq_desc) * nr_irq_desc;
- if (after_bootmem)
- desc = kzalloc(total_bytes, GFP_ATOMIC);
- else
- desc = __alloc_bootmem_nopanic(total_bytes, PAGE_SIZE, 0);
- if (!desc)
- panic("please boot with nr_irq_desc= %d\n", count * 2);
- phys = __pa(desc);
- printk(KERN_DEBUG "irq_desc ==> [%#lx - %#lx]\n", phys, phys + total_bytes);
- for (i = 0; i < nr_irq_desc; i++)
- init_one_irq_desc(&desc[i]);
- for (i = 1; i < nr_irq_desc; i++)
- desc[i-1].next = &desc[i];
- /* init kstat_irqs, nr_cpu_ids is ready already */
- init_kstat_irqs(desc, nr_irq_desc, nr_cpu_ids);
- sparse_irqs_free = desc;
- }
- desc = sparse_irqs_free;
- sparse_irqs_free = sparse_irqs_free->next;
- desc->next = NULL;
- if (desc_pri)
- desc_pri->next = desc;
- else
- sparse_irqs = desc;
- desc->irq = irq;
- spin_unlock_irqrestore(&sparse_irq_lock, flags);
- printk(KERN_DEBUG "found new irq_desc for irq %d\n", desc->irq);
- #ifdef CONFIG_HAVE_SPARSE_IRQ_DEBUG
- {
- /* dump the results */
- struct irq_desc *desc;
- unsigned long phys;
- unsigned long bytes = sizeof(struct irq_desc);
- unsigned int irqx;
- printk(KERN_DEBUG "=========================== %d\n", irq);
- printk(KERN_DEBUG "irq_desc dump after get that for %d\n", irq);
- for_each_irq_desc(irqx, desc) {
- phys = __pa(desc);
- printk(KERN_DEBUG "irq_desc %d ==> [%#lx - %#lx]\n", irqx, phys, phys + bytes);
- }
- printk(KERN_DEBUG "===========================\n");
- }
- #endif
- return desc;
- }
- #else
- struct irq_desc *irq_desc;
- DEFINE_DYN_ARRAY(irq_desc, sizeof(struct irq_desc), nr_irqs, PAGE_SIZE, init_work);
- #endif
- #else
- struct irq_desc irq_desc[NR_IRQS] __cacheline_aligned_in_smp = {
- [0 ... NR_IRQS-1] = {
- .status = IRQ_DISABLED,
- .chip = &no_irq_chip,
- .handle_irq = handle_bad_irq,
- .depth = 1,
- .lock = __SPIN_LOCK_UNLOCKED(sparse_irqs->lock),
- #ifdef CONFIG_SMP
- .affinity = CPU_MASK_ALL
- #endif
- }
- };
- #endif
- #ifndef CONFIG_HAVE_SPARSE_IRQ
- struct irq_desc *irq_to_desc(unsigned int irq)
- {
- if (irq < nr_irqs)
- return &irq_desc[irq];
- return NULL;
- }
- struct irq_desc *irq_to_desc_alloc(unsigned int irq)
- {
- return irq_to_desc(irq);
- }
- #endif
- /*
- * What should we do if we get a hw irq event on an illegal vector?
- * Each architecture has to answer this themself.
- */
- static void ack_bad(unsigned int irq)
- {
- struct irq_desc *desc;
- desc = irq_to_desc(irq);
- print_irq_desc(irq, desc);
- ack_bad_irq(irq);
- }
- /*
- * NOP functions
- */
- static void noop(unsigned int irq)
- {
- }
- static unsigned int noop_ret(unsigned int irq)
- {
- return 0;
- }
- /*
- * Generic no controller implementation
- */
- struct irq_chip no_irq_chip = {
- .name = "none",
- .startup = noop_ret,
- .shutdown = noop,
- .enable = noop,
- .disable = noop,
- .ack = ack_bad,
- .end = noop,
- };
- /*
- * Generic dummy implementation which can be used for
- * real dumb interrupt sources
- */
- struct irq_chip dummy_irq_chip = {
- .name = "dummy",
- .startup = noop_ret,
- .shutdown = noop,
- .enable = noop,
- .disable = noop,
- .ack = noop,
- .mask = noop,
- .unmask = noop,
- .end = noop,
- };
- /*
- * Special, empty irq handler:
- */
- irqreturn_t no_action(int cpl, void *dev_id)
- {
- return IRQ_NONE;
- }
- /**
- * handle_IRQ_event - irq action chain handler
- * @irq: the interrupt number
- * @action: the interrupt action chain for this irq
- *
- * Handles the action chain of an irq event
- */
- irqreturn_t handle_IRQ_event(unsigned int irq, struct irqaction *action)
- {
- irqreturn_t ret, retval = IRQ_NONE;
- unsigned int status = 0;
- if (!(action->flags & IRQF_DISABLED))
- local_irq_enable_in_hardirq();
- do {
- ret = action->handler(irq, action->dev_id);
- if (ret == IRQ_HANDLED)
- status |= action->flags;
- retval |= ret;
- action = action->next;
- } while (action);
- if (status & IRQF_SAMPLE_RANDOM)
- add_interrupt_randomness(irq);
- local_irq_disable();
- return retval;
- }
- #ifndef CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ
- /**
- * __do_IRQ - original all in one highlevel IRQ handler
- * @irq: the interrupt number
- *
- * __do_IRQ handles all normal device IRQ's (the special
- * SMP cross-CPU interrupts have their own specific
- * handlers).
- *
- * This is the original x86 implementation which is used for every
- * interrupt type.
- */
- unsigned int __do_IRQ(unsigned int irq)
- {
- struct irq_desc *desc = irq_to_desc(irq);
- struct irqaction *action;
- unsigned int status;
- kstat_irqs_this_cpu(desc)++;
- if (CHECK_IRQ_PER_CPU(desc->status)) {
- irqreturn_t action_ret;
- /*
- * No locking required for CPU-local interrupts:
- */
- if (desc->chip->ack)
- desc->chip->ack(irq);
- if (likely(!(desc->status & IRQ_DISABLED))) {
- action_ret = handle_IRQ_event(irq, desc->action);
- if (!noirqdebug)
- note_interrupt(irq, desc, action_ret);
- }
- desc->chip->end(irq);
- return 1;
- }
- spin_lock(&desc->lock);
- if (desc->chip->ack)
- desc->chip->ack(irq);
- /*
- * REPLAY is when Linux resends an IRQ that was dropped earlier
- * WAITING is used by probe to mark irqs that are being tested
- */
- status = desc->status & ~(IRQ_REPLAY | IRQ_WAITING);
- status |= IRQ_PENDING; /* we _want_ to handle it */
- /*
- * If the IRQ is disabled for whatever reason, we cannot
- * use the action we have.
- */
- action = NULL;
- if (likely(!(status & (IRQ_DISABLED | IRQ_INPROGRESS)))) {
- action = desc->action;
- status &= ~IRQ_PENDING; /* we commit to handling */
- status |= IRQ_INPROGRESS; /* we are handling it */
- }
- desc->status = status;
- /*
- * If there is no IRQ handler or it was disabled, exit early.
- * Since we set PENDING, if another processor is handling
- * a different instance of this same irq, the other processor
- * will take care of it.
- */
- if (unlikely(!action))
- goto out;
- /*
- * Edge triggered interrupts need to remember
- * pending events.
- * This applies to any hw interrupts that allow a second
- * instance of the same irq to arrive while we are in do_IRQ
- * or in the handler. But the code here only handles the _second_
- * instance of the irq, not the third or fourth. So it is mostly
- * useful for irq hardware that does not mask cleanly in an
- * SMP environment.
- */
- for (;;) {
- irqreturn_t action_ret;
- spin_unlock(&desc->lock);
- action_ret = handle_IRQ_event(irq, action);
- if (!noirqdebug)
- note_interrupt(irq, desc, action_ret);
- spin_lock(&desc->lock);
- if (likely(!(desc->status & IRQ_PENDING)))
- break;
- desc->status &= ~IRQ_PENDING;
- }
- desc->status &= ~IRQ_INPROGRESS;
- out:
- /*
- * The ->end() handler has to deal with interrupts which got
- * disabled while the handler was running.
- */
- desc->chip->end(irq);
- spin_unlock(&desc->lock);
- return 1;
- }
- #endif
- #ifdef CONFIG_TRACE_IRQFLAGS
- void early_init_irq_lock_class(void)
- {
- #ifndef CONFIG_HAVE_DYN_ARRAY
- int i;
- for (i = 0; i < nr_irqs; i++)
- lockdep_set_class(&irq_desc[i].lock, &irq_desc_lock_class);
- #endif
- }
- #endif
- unsigned int kstat_irqs_cpu(unsigned int irq, int cpu)
- {
- struct irq_desc *desc = irq_to_desc(irq);
- return desc->kstat_irqs[cpu];
- }
- EXPORT_SYMBOL(kstat_irqs_cpu);
|