diff options
author | Thomas Gleixner <tglx@linutronix.de> | 2009-08-13 07:21:38 -0400 |
---|---|---|
committer | Thomas Gleixner <tglx@linutronix.de> | 2009-08-17 04:54:05 -0400 |
commit | 399b5da29b9f851eb7b96e2882097127f003e87c (patch) | |
tree | 0264a7ae22988e8a298407c611b5b639c9315ff7 /kernel/irq/manage.c | |
parent | 70aedd24d20e75198f5a0b11750faabbb56924e2 (diff) |
genirq: Support nested threaded irq handling
Interrupt chips which are behind a slow bus (i2c, spi ...) and
demultiplex other interrupt sources need to run their interrupt
handler in a thread.
The demultiplexed interrupt handlers need to run in thread context as
well and need to finish before the demux handler thread can reenable
the interrupt line. So the easiest way is to run the sub device
handlers in the context of the demultiplexing handler thread.
To avoid that a separate thread is created for the subdevices the
function set_nested_irq_thread() is provided which sets the
IRQ_NESTED_THREAD flag in the interrupt descriptor.
A driver which calls request_threaded_irq() must not be aware of the
fact that the threaded handler is called in the context of the
demultiplexing handler thread. The setup code checks the
IRQ_NESTED_THREAD flag which was set from the irq chip setup code and
does not setup a separate thread for the interrupt. The primary
function which is provided by the device driver is replaced by an
internal dummy function which warns when it is called.
For the demultiplexing handler a helper function handle_nested_irq()
is provided which calls the demux interrupt thread function in the
context of the caller and does the proper interrupt accounting and
takes the interrupt disabled status of the demultiplexed subdevice
into account.
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Cc: Mark Brown <broonie@opensource.wolfsonmicro.com>
Cc: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Cc: Trilok Soni <soni.trilok@gmail.com>
Cc: Pavel Machek <pavel@ucw.cz>
Cc: Brian Swetland <swetland@google.com>
Cc: Joonyoung Shim <jy0922.shim@samsung.com>
Cc: m.szyprowski@samsung.com
Cc: t.fujak@samsung.com
Cc: kyungmin.park@samsung.com,
Cc: David Brownell <david-b@pacbell.net>
Cc: Daniel Ribeiro <drwyrm@gmail.com>
Cc: arve@android.com
Cc: Barry Song <21cnbao@gmail.com>
Diffstat (limited to 'kernel/irq/manage.c')
-rw-r--r-- | kernel/irq/manage.c | 34 |
1 files changed, 31 insertions, 3 deletions
diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c index 0a3fd5b524c9..e485cea04bf1 100644 --- a/kernel/irq/manage.c +++ b/kernel/irq/manage.c | |||
@@ -451,6 +451,16 @@ static irqreturn_t irq_default_primary_handler(int irq, void *dev_id) | |||
451 | return IRQ_WAKE_THREAD; | 451 | return IRQ_WAKE_THREAD; |
452 | } | 452 | } |
453 | 453 | ||
454 | /* | ||
455 | * Primary handler for nested threaded interrupts. Should never be | ||
456 | * called. | ||
457 | */ | ||
458 | static irqreturn_t irq_nested_primary_handler(int irq, void *dev_id) | ||
459 | { | ||
460 | WARN(1, "Primary handler called for nested irq %d\n", irq); | ||
461 | return IRQ_NONE; | ||
462 | } | ||
463 | |||
454 | static int irq_wait_for_interrupt(struct irqaction *action) | 464 | static int irq_wait_for_interrupt(struct irqaction *action) |
455 | { | 465 | { |
456 | while (!kthread_should_stop()) { | 466 | while (!kthread_should_stop()) { |
@@ -600,7 +610,7 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new) | |||
600 | struct irqaction *old, **old_ptr; | 610 | struct irqaction *old, **old_ptr; |
601 | const char *old_name = NULL; | 611 | const char *old_name = NULL; |
602 | unsigned long flags; | 612 | unsigned long flags; |
603 | int shared = 0; | 613 | int nested, shared = 0; |
604 | int ret; | 614 | int ret; |
605 | 615 | ||
606 | if (!desc) | 616 | if (!desc) |
@@ -630,9 +640,27 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new) | |||
630 | return -EINVAL; | 640 | return -EINVAL; |
631 | 641 | ||
632 | /* | 642 | /* |
633 | * Threaded handler ? | 643 | * Check whether the interrupt nests into another interrupt |
644 | * thread. | ||
645 | */ | ||
646 | nested = desc->status & IRQ_NESTED_THREAD; | ||
647 | if (nested) { | ||
648 | if (!new->thread_fn) | ||
649 | return -EINVAL; | ||
650 | /* | ||
651 | * Replace the primary handler which was provided from | ||
652 | * the driver for non nested interrupt handling by the | ||
653 | * dummy function which warns when called. | ||
654 | */ | ||
655 | new->handler = irq_nested_primary_handler; | ||
656 | } | ||
657 | |||
658 | /* | ||
659 | * Create a handler thread when a thread function is supplied | ||
660 | * and the interrupt does not nest into another interrupt | ||
661 | * thread. | ||
634 | */ | 662 | */ |
635 | if (new->thread_fn) { | 663 | if (new->thread_fn && !nested) { |
636 | struct task_struct *t; | 664 | struct task_struct *t; |
637 | 665 | ||
638 | t = kthread_create(irq_thread, new, "irq/%d-%s", irq, | 666 | t = kthread_create(irq_thread, new, "irq/%d-%s", irq, |