mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-28 10:41:38 -04:00
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: Thomas Gleixner <[email protected]> Cc: Mark Brown <[email protected]> Cc: Dmitry Torokhov <[email protected]> Cc: Trilok Soni <[email protected]> Cc: Pavel Machek <[email protected]> Cc: Brian Swetland <[email protected]> Cc: Joonyoung Shim <[email protected]> Cc: [email protected] Cc: [email protected] Cc: [email protected], Cc: David Brownell <[email protected]> Cc: Daniel Ribeiro <[email protected]> Cc: [email protected] Cc: Barry Song <[email protected]>
This commit is contained in:
parent
b2add73dbf
commit
b25c340c19
4 changed files with 61 additions and 7 deletions
|
|
@ -382,7 +382,10 @@ handle_level_irq(unsigned int irq, struct irq_desc *desc)
|
|||
|
||||
spin_lock(&desc->lock);
|
||||
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);
|
||||
out_unlock:
|
||||
spin_unlock(&desc->lock);
|
||||
|
|
@ -478,8 +481,13 @@ handle_edge_irq(unsigned int irq, struct irq_desc *desc)
|
|||
kstat_incr_irqs_this_cpu(irq, desc);
|
||||
|
||||
/* Start handling the irq */
|
||||
if (desc->chip->ack)
|
||||
desc->chip->ack(irq);
|
||||
if (unlikely(desc->status & IRQ_ONESHOT)) {
|
||||
desc->status |= IRQ_MASKED;
|
||||
mask_ack_irq(desc, irq);
|
||||
} else {
|
||||
if (desc->chip->ack)
|
||||
desc->chip->ack(irq);
|
||||
}
|
||||
|
||||
/* Mark the IRQ currently in progress.*/
|
||||
desc->status |= IRQ_INPROGRESS;
|
||||
|
|
|
|||
|
|
@ -436,6 +436,16 @@ int __irq_set_trigger(struct irq_desc *desc, unsigned int irq,
|
|||
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)
|
||||
{
|
||||
while (!kthread_should_stop()) {
|
||||
|
|
@ -451,6 +461,21 @@ static int irq_wait_for_interrupt(struct irqaction *action)
|
|||
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
|
||||
/*
|
||||
* Check whether we need to change the affinity of the interrupt thread.
|
||||
|
|
@ -492,7 +517,7 @@ static int irq_thread(void *data)
|
|||
struct sched_param param = { .sched_priority = MAX_USER_RT_PRIO/2, };
|
||||
struct irqaction *action = data;
|
||||
struct irq_desc *desc = irq_to_desc(action->irq);
|
||||
int wake;
|
||||
int wake, oneshot = desc->status & IRQ_ONESHOT;
|
||||
|
||||
sched_setscheduler(current, SCHED_FIFO, ¶m);
|
||||
current->irqaction = action;
|
||||
|
|
@ -518,6 +543,9 @@ static int irq_thread(void *data)
|
|||
spin_unlock_irq(&desc->lock);
|
||||
|
||||
action->thread_fn(action->irq, action->dev_id);
|
||||
|
||||
if (oneshot)
|
||||
irq_finalize_oneshot(action->irq, desc);
|
||||
}
|
||||
|
||||
wake = atomic_dec_and_test(&desc->threads_active);
|
||||
|
|
@ -590,6 +618,10 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new)
|
|||
rand_initialize_irq(irq);
|
||||
}
|
||||
|
||||
/* Oneshot interrupts are not allowed with shared */
|
||||
if ((new->flags & IRQF_ONESHOT) && (new->flags & IRQF_SHARED))
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* Threaded handler ?
|
||||
*/
|
||||
|
|
@ -663,9 +695,12 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new)
|
|||
desc->status |= IRQ_PER_CPU;
|
||||
#endif
|
||||
|
||||
desc->status &= ~(IRQ_AUTODETECT | IRQ_WAITING |
|
||||
desc->status &= ~(IRQ_AUTODETECT | IRQ_WAITING | IRQ_ONESHOT |
|
||||
IRQ_INPROGRESS | IRQ_SPURIOUS_DISABLED);
|
||||
|
||||
if (new->flags & IRQF_ONESHOT)
|
||||
desc->status |= IRQ_ONESHOT;
|
||||
|
||||
if (!(desc->status & IRQ_NOAUTOEN)) {
|
||||
desc->depth = 0;
|
||||
desc->status &= ~IRQ_DISABLED;
|
||||
|
|
@ -878,6 +913,8 @@ EXPORT_SYMBOL(free_irq);
|
|||
* @irq: Interrupt line to allocate
|
||||
* @handler: Function to be called when the IRQ occurs.
|
||||
* 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
|
||||
* If NULL, no irq thread is created
|
||||
* @irqflags: Interrupt type flags
|
||||
|
|
@ -957,8 +994,12 @@ int request_threaded_irq(unsigned int irq, irq_handler_t handler,
|
|||
|
||||
if (desc->status & IRQ_NOREQUEST)
|
||||
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);
|
||||
if (!action)
|
||||
|
|
|
|||
Loading…
Reference in a new issue