Commit b25c340c authored by Thomas Gleixner's avatar Thomas Gleixner

genirq: Add oneshot support

For threaded interrupt handlers we expect the hard interrupt handler
part to mask the interrupt on the originating device. The interrupt
line itself is reenabled after the hard interrupt handler has
executed.

This requires access to the originating device from hard interrupt
context which is not always possible. There are devices which can only
be accessed via a bus (i2c, spi, ...). The bus access requires thread
context. For such devices we need to keep the interrupt line masked
until the threaded handler has executed.

Add a new flag IRQF_ONESHOT which allows drivers to request that the
interrupt is not unmasked after the hard interrupt context handler has
been executed and the thread has been woken. The interrupt line is
unmasked after the thread handler function has been executed.

Note that for now IRQF_ONESHOT cannot be used with IRQF_SHARED to
avoid complex accounting mechanisms.

For oneshot interrupts the primary handler simply returns
IRQ_WAKE_THREAD and does nothing else. A generic implementation
irq_default_primary_handler() is provided to avoid useless copies all
over the place. It is automatically installed when
request_threaded_irq() is called with handler=NULL and
thread_fn!=NULL.
Signed-off-by: default avatarThomas 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>
parent b2add73d
...@@ -50,6 +50,9 @@ ...@@ -50,6 +50,9 @@
* IRQF_IRQPOLL - Interrupt is used for polling (only the interrupt that is * IRQF_IRQPOLL - Interrupt is used for polling (only the interrupt that is
* registered first in an shared interrupt is considered for * registered first in an shared interrupt is considered for
* performance reasons) * performance reasons)
* IRQF_ONESHOT - Interrupt is not reenabled after the hardirq handler finished.
* Used by threaded interrupts which need to keep the
* irq line disabled until the threaded handler has been run.
*/ */
#define IRQF_DISABLED 0x00000020 #define IRQF_DISABLED 0x00000020
#define IRQF_SAMPLE_RANDOM 0x00000040 #define IRQF_SAMPLE_RANDOM 0x00000040
...@@ -59,6 +62,7 @@ ...@@ -59,6 +62,7 @@
#define IRQF_PERCPU 0x00000400 #define IRQF_PERCPU 0x00000400
#define IRQF_NOBALANCING 0x00000800 #define IRQF_NOBALANCING 0x00000800
#define IRQF_IRQPOLL 0x00001000 #define IRQF_IRQPOLL 0x00001000
#define IRQF_ONESHOT 0x00002000
/* /*
* Bits used by threaded handlers: * Bits used by threaded handlers:
......
...@@ -69,6 +69,7 @@ typedef void (*irq_flow_handler_t)(unsigned int irq, ...@@ -69,6 +69,7 @@ typedef void (*irq_flow_handler_t)(unsigned int irq,
#define IRQ_MOVE_PCNTXT 0x01000000 /* IRQ migration from process context */ #define IRQ_MOVE_PCNTXT 0x01000000 /* IRQ migration from process context */
#define IRQ_AFFINITY_SET 0x02000000 /* IRQ affinity was set from userspace*/ #define IRQ_AFFINITY_SET 0x02000000 /* IRQ affinity was set from userspace*/
#define IRQ_SUSPENDED 0x04000000 /* IRQ has gone through suspend sequence */ #define IRQ_SUSPENDED 0x04000000 /* IRQ has gone through suspend sequence */
#define IRQ_ONESHOT 0x08000000 /* IRQ is not unmasked after hardirq */
#ifdef CONFIG_IRQ_PER_CPU #ifdef CONFIG_IRQ_PER_CPU
# define CHECK_IRQ_PER_CPU(var) ((var) & IRQ_PER_CPU) # define CHECK_IRQ_PER_CPU(var) ((var) & IRQ_PER_CPU)
......
...@@ -382,7 +382,10 @@ handle_level_irq(unsigned int irq, struct irq_desc *desc) ...@@ -382,7 +382,10 @@ handle_level_irq(unsigned int irq, struct irq_desc *desc)
spin_lock(&desc->lock); spin_lock(&desc->lock);
desc->status &= ~IRQ_INPROGRESS; 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); desc->chip->unmask(irq);
out_unlock: out_unlock:
spin_unlock(&desc->lock); spin_unlock(&desc->lock);
...@@ -478,8 +481,13 @@ handle_edge_irq(unsigned int irq, struct irq_desc *desc) ...@@ -478,8 +481,13 @@ handle_edge_irq(unsigned int irq, struct irq_desc *desc)
kstat_incr_irqs_this_cpu(irq, desc); kstat_incr_irqs_this_cpu(irq, desc);
/* Start handling the irq */ /* Start handling the irq */
if (desc->chip->ack) if (unlikely(desc->status & IRQ_ONESHOT)) {
desc->chip->ack(irq); desc->status |= IRQ_MASKED;
mask_ack_irq(desc, irq);
} else {
if (desc->chip->ack)
desc->chip->ack(irq);
}
/* Mark the IRQ currently in progress.*/ /* Mark the IRQ currently in progress.*/
desc->status |= IRQ_INPROGRESS; desc->status |= IRQ_INPROGRESS;
......
...@@ -436,6 +436,16 @@ int __irq_set_trigger(struct irq_desc *desc, unsigned int irq, ...@@ -436,6 +436,16 @@ int __irq_set_trigger(struct irq_desc *desc, unsigned int irq,
return ret; return ret;
} }
/*
* Default primary interrupt handler for threaded interrupts. Is
* assigned as primary handler when request_threaded_irq is called
* with handler == NULL. Useful for oneshot interrupts.
*/
static irqreturn_t irq_default_primary_handler(int irq, void *dev_id)
{
return IRQ_WAKE_THREAD;
}
static int irq_wait_for_interrupt(struct irqaction *action) static int irq_wait_for_interrupt(struct irqaction *action)
{ {
while (!kthread_should_stop()) { while (!kthread_should_stop()) {
...@@ -451,6 +461,21 @@ static int irq_wait_for_interrupt(struct irqaction *action) ...@@ -451,6 +461,21 @@ static int irq_wait_for_interrupt(struct irqaction *action)
return -1; return -1;
} }
/*
* Oneshot interrupts keep the irq line masked until the threaded
* handler finished. unmask if the interrupt has not been disabled and
* is marked MASKED.
*/
static void irq_finalize_oneshot(unsigned int irq, struct irq_desc *desc)
{
spin_lock_irq(&desc->lock);
if (!(desc->status & IRQ_DISABLED) && (desc->status & IRQ_MASKED)) {
desc->status &= ~IRQ_MASKED;
desc->chip->unmask(irq);
}
spin_unlock_irq(&desc->lock);
}
#ifdef CONFIG_SMP #ifdef CONFIG_SMP
/* /*
* Check whether we need to change the affinity of the interrupt thread. * Check whether we need to change the affinity of the interrupt thread.
...@@ -492,7 +517,7 @@ static int irq_thread(void *data) ...@@ -492,7 +517,7 @@ static int irq_thread(void *data)
struct sched_param param = { .sched_priority = MAX_USER_RT_PRIO/2, }; struct sched_param param = { .sched_priority = MAX_USER_RT_PRIO/2, };
struct irqaction *action = data; struct irqaction *action = data;
struct irq_desc *desc = irq_to_desc(action->irq); struct irq_desc *desc = irq_to_desc(action->irq);
int wake; int wake, oneshot = desc->status & IRQ_ONESHOT;
sched_setscheduler(current, SCHED_FIFO, &param); sched_setscheduler(current, SCHED_FIFO, &param);
current->irqaction = action; current->irqaction = action;
...@@ -518,6 +543,9 @@ static int irq_thread(void *data) ...@@ -518,6 +543,9 @@ static int irq_thread(void *data)
spin_unlock_irq(&desc->lock); spin_unlock_irq(&desc->lock);
action->thread_fn(action->irq, action->dev_id); action->thread_fn(action->irq, action->dev_id);
if (oneshot)
irq_finalize_oneshot(action->irq, desc);
} }
wake = atomic_dec_and_test(&desc->threads_active); wake = atomic_dec_and_test(&desc->threads_active);
...@@ -590,6 +618,10 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new) ...@@ -590,6 +618,10 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new)
rand_initialize_irq(irq); rand_initialize_irq(irq);
} }
/* Oneshot interrupts are not allowed with shared */
if ((new->flags & IRQF_ONESHOT) && (new->flags & IRQF_SHARED))
return -EINVAL;
/* /*
* Threaded handler ? * Threaded handler ?
*/ */
...@@ -663,9 +695,12 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new) ...@@ -663,9 +695,12 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new)
desc->status |= IRQ_PER_CPU; desc->status |= IRQ_PER_CPU;
#endif #endif
desc->status &= ~(IRQ_AUTODETECT | IRQ_WAITING | desc->status &= ~(IRQ_AUTODETECT | IRQ_WAITING | IRQ_ONESHOT |
IRQ_INPROGRESS | IRQ_SPURIOUS_DISABLED); IRQ_INPROGRESS | IRQ_SPURIOUS_DISABLED);
if (new->flags & IRQF_ONESHOT)
desc->status |= IRQ_ONESHOT;
if (!(desc->status & IRQ_NOAUTOEN)) { if (!(desc->status & IRQ_NOAUTOEN)) {
desc->depth = 0; desc->depth = 0;
desc->status &= ~IRQ_DISABLED; desc->status &= ~IRQ_DISABLED;
...@@ -878,6 +913,8 @@ EXPORT_SYMBOL(free_irq); ...@@ -878,6 +913,8 @@ EXPORT_SYMBOL(free_irq);
* @irq: Interrupt line to allocate * @irq: Interrupt line to allocate
* @handler: Function to be called when the IRQ occurs. * @handler: Function to be called when the IRQ occurs.
* Primary handler for threaded interrupts * Primary handler for threaded interrupts
* If NULL and thread_fn != NULL the default
* primary handler is installed
* @thread_fn: Function called from the irq handler thread * @thread_fn: Function called from the irq handler thread
* If NULL, no irq thread is created * If NULL, no irq thread is created
* @irqflags: Interrupt type flags * @irqflags: Interrupt type flags
...@@ -957,8 +994,12 @@ int request_threaded_irq(unsigned int irq, irq_handler_t handler, ...@@ -957,8 +994,12 @@ int request_threaded_irq(unsigned int irq, irq_handler_t handler,
if (desc->status & IRQ_NOREQUEST) if (desc->status & IRQ_NOREQUEST)
return -EINVAL; return -EINVAL;
if (!handler)
return -EINVAL; if (!handler) {
if (!thread_fn)
return -EINVAL;
handler = irq_default_primary_handler;
}
action = kzalloc(sizeof(struct irqaction), GFP_KERNEL); action = kzalloc(sizeof(struct irqaction), GFP_KERNEL);
if (!action) if (!action)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment