diff options
| author | Marc Zyngier <maz@misterjones.org> | 2010-03-15 18:56:33 -0400 |
|---|---|---|
| committer | Thomas Gleixner <tglx@linutronix.de> | 2010-04-13 10:36:39 -0400 |
| commit | ae731f8d0785ccd3380f511bae888933b6562e45 (patch) | |
| tree | 07db03ce79231153a4ae5df75c0ca4dcd96307c2 | |
| parent | 7c7145f6acc68100dbdc5d3c5c64fe3af1c99c89 (diff) | |
genirq: Introduce request_any_context_irq()
Now that we enjoy threaded interrupts, we're starting to see irq_chip
implementations (wm831x, pca953x) that make use of threaded interrupts
for the controller, and nested interrupts for the client interrupt. It
all works very well, with one drawback:
Drivers requesting an IRQ must now know whether the handler will
run in a thread context or not, and call request_threaded_irq() or
request_irq() accordingly.
The problem is that the requesting driver sometimes doesn't know
about the nature of the interrupt, specially when the interrupt
controller is a discrete chip (typically a GPIO expander connected
over I2C) that can be connected to a wide variety of otherwise perfectly
supported hardware.
This patch introduces the request_any_context_irq() function that mostly
mimics the usual request_irq(), except that it checks whether the irq
level is configured as nested or not, and calls the right backend.
On success, it also returns either IRQC_IS_HARDIRQ or IRQC_IS_NESTED.
[ tglx: Made return value an enum, simplified code and made the export
of request_any_context_irq GPL ]
Signed-off-by: Marc Zyngier <maz@misterjones.org>
Cc: <joachim.eastwood@jotron.com>
LKML-Reference: <927ea285bd0c68934ddae1a47e44a9ba@localhost>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
| -rw-r--r-- | include/linux/interrupt.h | 23 | ||||
| -rw-r--r-- | kernel/irq/manage.c | 37 |
2 files changed, 60 insertions, 0 deletions
diff --git a/include/linux/interrupt.h b/include/linux/interrupt.h index 75f3f00ac1e5..d7e7a7660c6c 100644 --- a/include/linux/interrupt.h +++ b/include/linux/interrupt.h | |||
| @@ -77,6 +77,18 @@ enum { | |||
| 77 | IRQTF_AFFINITY, | 77 | IRQTF_AFFINITY, |
| 78 | }; | 78 | }; |
| 79 | 79 | ||
| 80 | /** | ||
| 81 | * These values can be returned by request_any_context_irq() and | ||
| 82 | * describe the context the interrupt will be run in. | ||
| 83 | * | ||
| 84 | * IRQC_IS_HARDIRQ - interrupt runs in hardirq context | ||
| 85 | * IRQC_IS_NESTED - interrupt runs in a nested threaded context | ||
| 86 | */ | ||
| 87 | enum { | ||
| 88 | IRQC_IS_HARDIRQ = 0, | ||
| 89 | IRQC_IS_NESTED, | ||
| 90 | }; | ||
| 91 | |||
| 80 | typedef irqreturn_t (*irq_handler_t)(int, void *); | 92 | typedef irqreturn_t (*irq_handler_t)(int, void *); |
| 81 | 93 | ||
| 82 | /** | 94 | /** |
| @@ -120,6 +132,10 @@ request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags, | |||
| 120 | return request_threaded_irq(irq, handler, NULL, flags, name, dev); | 132 | return request_threaded_irq(irq, handler, NULL, flags, name, dev); |
| 121 | } | 133 | } |
| 122 | 134 | ||
| 135 | extern int __must_check | ||
| 136 | request_any_context_irq(unsigned int irq, irq_handler_t handler, | ||
| 137 | unsigned long flags, const char *name, void *dev_id); | ||
| 138 | |||
| 123 | extern void exit_irq_thread(void); | 139 | extern void exit_irq_thread(void); |
| 124 | #else | 140 | #else |
| 125 | 141 | ||
| @@ -141,6 +157,13 @@ request_threaded_irq(unsigned int irq, irq_handler_t handler, | |||
| 141 | return request_irq(irq, handler, flags, name, dev); | 157 | return request_irq(irq, handler, flags, name, dev); |
| 142 | } | 158 | } |
| 143 | 159 | ||
| 160 | static inline int __must_check | ||
| 161 | request_any_context_irq(unsigned int irq, irq_handler_t handler, | ||
| 162 | unsigned long flags, const char *name, void *dev_id) | ||
| 163 | { | ||
| 164 | return request_irq(irq, handler, flags, name, dev_id); | ||
| 165 | } | ||
| 166 | |||
| 144 | static inline void exit_irq_thread(void) { } | 167 | static inline void exit_irq_thread(void) { } |
| 145 | #endif | 168 | #endif |
| 146 | 169 | ||
diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c index 704e488730a5..84f32278ff1f 100644 --- a/kernel/irq/manage.c +++ b/kernel/irq/manage.c | |||
| @@ -1120,3 +1120,40 @@ int request_threaded_irq(unsigned int irq, irq_handler_t handler, | |||
| 1120 | return retval; | 1120 | return retval; |
| 1121 | } | 1121 | } |
| 1122 | EXPORT_SYMBOL(request_threaded_irq); | 1122 | EXPORT_SYMBOL(request_threaded_irq); |
| 1123 | |||
| 1124 | /** | ||
| 1125 | * request_any_context_irq - allocate an interrupt line | ||
| 1126 | * @irq: Interrupt line to allocate | ||
| 1127 | * @handler: Function to be called when the IRQ occurs. | ||
| 1128 | * Threaded handler for threaded interrupts. | ||
| 1129 | * @flags: Interrupt type flags | ||
| 1130 | * @name: An ascii name for the claiming device | ||
| 1131 | * @dev_id: A cookie passed back to the handler function | ||
| 1132 | * | ||
| 1133 | * This call allocates interrupt resources and enables the | ||
| 1134 | * interrupt line and IRQ handling. It selects either a | ||
| 1135 | * hardirq or threaded handling method depending on the | ||
| 1136 | * context. | ||
| 1137 | * | ||
| 1138 | * On failure, it returns a negative value. On success, | ||
| 1139 | * it returns either IRQC_IS_HARDIRQ or IRQC_IS_NESTED. | ||
| 1140 | */ | ||
| 1141 | int request_any_context_irq(unsigned int irq, irq_handler_t handler, | ||
| 1142 | unsigned long flags, const char *name, void *dev_id) | ||
| 1143 | { | ||
| 1144 | struct irq_desc *desc = irq_to_desc(irq); | ||
| 1145 | int ret; | ||
| 1146 | |||
| 1147 | if (!desc) | ||
| 1148 | return -EINVAL; | ||
| 1149 | |||
| 1150 | if (desc->status & IRQ_NESTED_THREAD) { | ||
| 1151 | ret = request_threaded_irq(irq, NULL, handler, | ||
| 1152 | flags, name, dev_id); | ||
| 1153 | return !ret ? IRQC_IS_NESTED : ret; | ||
| 1154 | } | ||
| 1155 | |||
| 1156 | ret = request_irq(irq, handler, flags, name, dev_id); | ||
| 1157 | return !ret ? IRQC_IS_HARDIRQ : ret; | ||
| 1158 | } | ||
| 1159 | EXPORT_SYMBOL_GPL(request_any_context_irq); | ||
