summaryrefslogtreecommitdiffstats
path: root/kernel/irq/manage.c
diff options
context:
space:
mode:
authorThomas Gleixner <tglx@linutronix.de>2009-08-13 13:21:38 +0200
committerThomas Gleixner <tglx@linutronix.de>2009-08-17 10:54:05 +0200
commit399b5da29b9f851eb7b96e2882097127f003e87c (patch)
tree0264a7ae22988e8a298407c611b5b639c9315ff7 /kernel/irq/manage.c
parent70aedd24d20e75198f5a0b11750faabbb56924e2 (diff)
downloadtalos-obmc-linux-399b5da29b9f851eb7b96e2882097127f003e87c.tar.gz
talos-obmc-linux-399b5da29b9f851eb7b96e2882097127f003e87c.zip
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.c34
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)
return IRQ_WAKE_THREAD;
}
+/*
+ * Primary handler for nested threaded interrupts. Should never be
+ * called.
+ */
+static irqreturn_t irq_nested_primary_handler(int irq, void *dev_id)
+{
+ WARN(1, "Primary handler called for nested irq %d\n", irq);
+ return IRQ_NONE;
+}
+
static int irq_wait_for_interrupt(struct irqaction *action)
{
while (!kthread_should_stop()) {
@@ -600,7 +610,7 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new)
struct irqaction *old, **old_ptr;
const char *old_name = NULL;
unsigned long flags;
- int shared = 0;
+ int nested, shared = 0;
int ret;
if (!desc)
@@ -630,9 +640,27 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new)
return -EINVAL;
/*
- * Threaded handler ?
+ * Check whether the interrupt nests into another interrupt
+ * thread.
+ */
+ nested = desc->status & IRQ_NESTED_THREAD;
+ if (nested) {
+ if (!new->thread_fn)
+ return -EINVAL;
+ /*
+ * Replace the primary handler which was provided from
+ * the driver for non nested interrupt handling by the
+ * dummy function which warns when called.
+ */
+ new->handler = irq_nested_primary_handler;
+ }
+
+ /*
+ * Create a handler thread when a thread function is supplied
+ * and the interrupt does not nest into another interrupt
+ * thread.
*/
- if (new->thread_fn) {
+ if (new->thread_fn && !nested) {
struct task_struct *t;
t = kthread_create(irq_thread, new, "irq/%d-%s", irq,
OpenPOWER on IntegriCloud