[PATCH] irq: add disable_irq_handler()/enable_irq_handler() functions

From: Boris Brezillon
Date: Mon Aug 03 2015 - 09:18:08 EST


Sometime we need to disable a specific handler on a shared irq line.
Currently, the only way this can be achieved is by freeing the irq handler
when we want to disable the irq and creating a new one when we want to
enable it.
This is not only adding some overhead to the disable/enable operations, but
the request_irq function cannot be called in atomic context, which means
it prevents disabling the interrupt in such situation.

This patch introduces three new functions: disable_irq_handler(),
disable_irq_handler_nosync() and enable_irq_handler() to allow disabling
a specific handler on a shared irq line.

Signed-off-by: Boris Brezillon <boris.brezillon@xxxxxxxxxxxxxxxxxx>
---
include/linux/irqdesc.h | 1 +
kernel/irq/manage.c | 82 +++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 83 insertions(+)

diff --git a/include/linux/irqdesc.h b/include/linux/irqdesc.h
index fcea4e4..c8bd055 100644
--- a/include/linux/irqdesc.h
+++ b/include/linux/irqdesc.h
@@ -52,6 +52,7 @@ struct irq_desc {
irq_preflow_handler_t preflow_handler;
#endif
struct irqaction *action; /* IRQ action list */
+ struct irqaction *disabled_actions; /* IRQ disabled action list */
unsigned int status_use_accessors;
unsigned int core_internal_state__do_not_mess_with_it;
unsigned int depth; /* nested irq disables */
diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c
index f974485..4690ab4 100644
--- a/kernel/irq/manage.c
+++ b/kernel/irq/manage.c
@@ -458,6 +458,47 @@ void disable_irq_nosync(unsigned int irq)
}
EXPORT_SYMBOL(disable_irq_nosync);

+static int __disable_irq_handler_nosync(unsigned int irq, void *dev_id)
+{
+ unsigned long flags;
+ struct irqaction *action, **prev;
+ struct irq_desc *desc = irq_get_desc_buslock(irq, &flags, IRQ_GET_DESC_CHECK_GLOBAL);
+ int ret = 0;
+
+ if (!desc)
+ return -EINVAL;
+
+ for (action = desc->action, prev = &desc->action; action; action = action->next) {
+ if (action->dev_id == dev_id)
+ break;
+
+ prev = &action->next;
+ }
+
+ if (!action)
+ goto out;
+
+ *prev = action->next;
+
+ action->next = desc->disabled_actions;
+ desc->disabled_actions = action;
+ if (!desc->action) {
+ __disable_irq(desc, irq);
+ ret = 1;
+ }
+
+out:
+ irq_put_desc_busunlock(desc, flags);
+ return ret;
+}
+
+static void disable_irq_handler_nosync(unsigned int irq, void *dev_id)
+{
+ __disable_irq_handler_nosync(irq, dev_id);
+}
+EXPORT_SYMBOL(disable_irq_handler_nosync);
+
+
/**
* disable_irq - disable an irq and wait for completion
* @irq: Interrupt to disable
@@ -477,6 +518,13 @@ void disable_irq(unsigned int irq)
}
EXPORT_SYMBOL(disable_irq);

+void disable_irq_handler(unsigned int irq, )
+{
+ if (__disable_irq_handler_nosync(irq) > 0)
+ synchronize_irq(irq);
+}
+EXPORT_SYMBOL(disable_irq);
+
/**
* disable_hardirq - disables an irq and waits for hardirq completion
* @irq: Interrupt to disable
@@ -552,6 +600,40 @@ out:
}
EXPORT_SYMBOL(enable_irq);

+void enable_irq_handler(unsigned int irq, void *dev_id)
+{
+ unsigned long flags;
+ struct irqaction *action, **prev;
+ struct irq_desc *desc = irq_get_desc_buslock(irq, &flags, IRQ_GET_DESC_CHECK_GLOBAL);
+
+ if (!desc)
+ return -EINVAL;
+
+ for (action = desc->disabled_actions, prev = &desc->action; action;
+ action = action->next) {
+ if (action->dev_id == dev_id)
+ break;
+
+ prev = &action->next;
+ }
+
+ if (!action)
+ goto out;
+
+ *prev = action->next;
+
+ action->next = desc->action;
+ desc->action = action;
+
+ if (!action->next)
+ __enable_irq(desc, irq);
+
+out:
+ irq_put_desc_busunlock(desc, flags);
+
+}
+EXPORT_SYMBOL(enable_irq_handler);
+
static int set_irq_wake_real(unsigned int irq, unsigned int on)
{
struct irq_desc *desc = irq_to_desc(irq);
--
1.9.1
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/