diff options
-rw-r--r-- | include/linux/irq.h | 3 | ||||
-rw-r--r-- | kernel/irq/Makefile | 2 | ||||
-rw-r--r-- | kernel/irq/manage.c | 10 | ||||
-rw-r--r-- | kernel/irq/resend.c | 78 |
4 files changed, 83 insertions, 10 deletions
diff --git a/include/linux/irq.h b/include/linux/irq.h index b2688157b51b..9a39756bfd31 100644 --- a/include/linux/irq.h +++ b/include/linux/irq.h @@ -246,6 +246,9 @@ extern void note_interrupt(unsigned int irq, struct irq_desc *desc, int action_ret, struct pt_regs *regs); extern int can_request_irq(unsigned int irq, unsigned long irqflags); +/* Resending of interrupts :*/ +void check_irq_resend(struct irq_desc *desc, unsigned int irq); + extern void init_irq_proc(void); #endif /* CONFIG_GENERIC_HARDIRQS */ diff --git a/kernel/irq/Makefile b/kernel/irq/Makefile index 9f77f50d8143..627ace98d4a2 100644 --- a/kernel/irq/Makefile +++ b/kernel/irq/Makefile @@ -1,5 +1,5 @@ -obj-y := handle.o manage.o spurious.o +obj-y := handle.o manage.o spurious.o resend.o obj-$(CONFIG_GENERIC_IRQ_PROBE) += autoprobe.o obj-$(CONFIG_PROC_FS) += proc.o obj-$(CONFIG_GENERIC_PENDING_IRQ) += migration.o diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c index 19b438e09f12..cffde4843897 100644 --- a/kernel/irq/manage.c +++ b/kernel/irq/manage.c @@ -118,15 +118,7 @@ void enable_irq(unsigned int irq) WARN_ON(1); break; case 1: { - unsigned int status = desc->status & ~IRQ_DISABLED; - - desc->status = status; - if ((status & (IRQ_PENDING | IRQ_REPLAY)) == IRQ_PENDING) { - desc->status = status | IRQ_REPLAY; - if (desc->chip && desc->chip->retrigger) - desc->chip->retrigger(irq); - } - desc->chip->enable(irq); + check_irq_resend(desc, irq); /* fall-through */ } default: diff --git a/kernel/irq/resend.c b/kernel/irq/resend.c new file mode 100644 index 000000000000..096b102fb392 --- /dev/null +++ b/kernel/irq/resend.c @@ -0,0 +1,78 @@ +/* + * linux/kernel/irq/resend.c + * + * Copyright (C) 1992, 1998-2006 Linus Torvalds, Ingo Molnar + * Copyright (C) 2005-2006, Thomas Gleixner + * + * This file contains the IRQ-resend code + * + * If the interrupt is waiting to be processed, we try to re-run it. + * We can't directly run it from here since the caller might be in an + * interrupt-protected region. Not all irq controller chips can + * retrigger interrupts at the hardware level, so in those cases + * we allow the resending of IRQs via a tasklet. + */ + +#include <linux/irq.h> +#include <linux/module.h> +#include <linux/random.h> +#include <linux/interrupt.h> + +#include "internals.h" + +#ifdef CONFIG_HARDIRQS_SW_RESEND + +/* Bitmap to handle software resend of interrupts: */ +static DECLARE_BITMAP(irqs_resend, NR_IRQS); + +/* + * Run software resends of IRQ's + */ +static void resend_irqs(unsigned long arg) +{ + struct irq_desc *desc; + int irq; + + while (!bitmap_empty(irqs_resend, NR_IRQS)) { + irq = find_first_bit(irqs_resend, NR_IRQS); + clear_bit(irq, irqs_resend); + desc = irq_desc + irq; + spin_lock_irqsave(&desc->lock, flags); + desc->handle_irq(irq, desc, NULL); + spin_unlock_irqrestore(&desc->lock, flags); + } +} + +/* Tasklet to handle resend: */ +static DECLARE_TASKLET(resend_tasklet, resend_irqs, 0); + +#endif + +/* + * IRQ resend + * + * Is called with interrupts disabled and desc->lock held. + */ +void check_irq_resend(struct irq_desc *desc, unsigned int irq) +{ + unsigned int status = desc->status; + + /* + * Make sure the interrupt is enabled, before resending it: + */ + desc->chip->enable(irq); + + if ((status & (IRQ_PENDING | IRQ_REPLAY)) == IRQ_PENDING) { + desc->status &= ~IRQ_PENDING; + desc->status = status | IRQ_REPLAY; + + if (!desc->chip || !desc->chip->retrigger || + !desc->chip->retrigger(irq)) { +#ifdef CONFIG_HARDIRQS_SW_RESEND + /* Set it pending and activate the softirq: */ + set_bit(irq, irqs_resend); + tasklet_schedule(&resend_tasklet); +#endif + } + } +} |