diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2009-09-11 16:21:31 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2009-09-11 16:21:31 -0400 |
commit | d90a7e86401ffea2163a4337f3a47f3909c4e255 (patch) | |
tree | afe3d4aa24849c561bb1e6c04c4c54808cbedb47 /kernel | |
parent | 12a499612e1ff439bdad240c7f86c55366941d4d (diff) | |
parent | 4dbc9ca219b0f294332e734528f7b82211700170 (diff) |
Merge branch 'irq-threaded-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/linux-2.6-tip
* 'irq-threaded-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/linux-2.6-tip:
genirq: Do not mask oneshot edge type interrupts
genirq: Support nested threaded irq handling
genirq: Add buslock support
genirq: Add oneshot support
Diffstat (limited to 'kernel')
-rw-r--r-- | kernel/irq/chip.c | 74 | ||||
-rw-r--r-- | kernel/irq/internals.h | 13 | ||||
-rw-r--r-- | kernel/irq/manage.c | 102 |
3 files changed, 180 insertions, 9 deletions
diff --git a/kernel/irq/chip.c b/kernel/irq/chip.c index 13c68e71b726..c1660194d115 100644 --- a/kernel/irq/chip.c +++ b/kernel/irq/chip.c | |||
@@ -222,6 +222,34 @@ int set_irq_chip_data(unsigned int irq, void *data) | |||
222 | } | 222 | } |
223 | EXPORT_SYMBOL(set_irq_chip_data); | 223 | EXPORT_SYMBOL(set_irq_chip_data); |
224 | 224 | ||
225 | /** | ||
226 | * set_irq_nested_thread - Set/Reset the IRQ_NESTED_THREAD flag of an irq | ||
227 | * | ||
228 | * @irq: Interrupt number | ||
229 | * @nest: 0 to clear / 1 to set the IRQ_NESTED_THREAD flag | ||
230 | * | ||
231 | * The IRQ_NESTED_THREAD flag indicates that on | ||
232 | * request_threaded_irq() no separate interrupt thread should be | ||
233 | * created for the irq as the handler are called nested in the | ||
234 | * context of a demultiplexing interrupt handler thread. | ||
235 | */ | ||
236 | void set_irq_nested_thread(unsigned int irq, int nest) | ||
237 | { | ||
238 | struct irq_desc *desc = irq_to_desc(irq); | ||
239 | unsigned long flags; | ||
240 | |||
241 | if (!desc) | ||
242 | return; | ||
243 | |||
244 | spin_lock_irqsave(&desc->lock, flags); | ||
245 | if (nest) | ||
246 | desc->status |= IRQ_NESTED_THREAD; | ||
247 | else | ||
248 | desc->status &= ~IRQ_NESTED_THREAD; | ||
249 | spin_unlock_irqrestore(&desc->lock, flags); | ||
250 | } | ||
251 | EXPORT_SYMBOL_GPL(set_irq_nested_thread); | ||
252 | |||
225 | /* | 253 | /* |
226 | * default enable function | 254 | * default enable function |
227 | */ | 255 | */ |
@@ -299,6 +327,45 @@ static inline void mask_ack_irq(struct irq_desc *desc, int irq) | |||
299 | } | 327 | } |
300 | } | 328 | } |
301 | 329 | ||
330 | /* | ||
331 | * handle_nested_irq - Handle a nested irq from a irq thread | ||
332 | * @irq: the interrupt number | ||
333 | * | ||
334 | * Handle interrupts which are nested into a threaded interrupt | ||
335 | * handler. The handler function is called inside the calling | ||
336 | * threads context. | ||
337 | */ | ||
338 | void handle_nested_irq(unsigned int irq) | ||
339 | { | ||
340 | struct irq_desc *desc = irq_to_desc(irq); | ||
341 | struct irqaction *action; | ||
342 | irqreturn_t action_ret; | ||
343 | |||
344 | might_sleep(); | ||
345 | |||
346 | spin_lock_irq(&desc->lock); | ||
347 | |||
348 | kstat_incr_irqs_this_cpu(irq, desc); | ||
349 | |||
350 | action = desc->action; | ||
351 | if (unlikely(!action || (desc->status & IRQ_DISABLED))) | ||
352 | goto out_unlock; | ||
353 | |||
354 | desc->status |= IRQ_INPROGRESS; | ||
355 | spin_unlock_irq(&desc->lock); | ||
356 | |||
357 | action_ret = action->thread_fn(action->irq, action->dev_id); | ||
358 | if (!noirqdebug) | ||
359 | note_interrupt(irq, desc, action_ret); | ||
360 | |||
361 | spin_lock_irq(&desc->lock); | ||
362 | desc->status &= ~IRQ_INPROGRESS; | ||
363 | |||
364 | out_unlock: | ||
365 | spin_unlock_irq(&desc->lock); | ||
366 | } | ||
367 | EXPORT_SYMBOL_GPL(handle_nested_irq); | ||
368 | |||
302 | /** | 369 | /** |
303 | * handle_simple_irq - Simple and software-decoded IRQs. | 370 | * handle_simple_irq - Simple and software-decoded IRQs. |
304 | * @irq: the interrupt number | 371 | * @irq: the interrupt number |
@@ -382,7 +449,10 @@ handle_level_irq(unsigned int irq, struct irq_desc *desc) | |||
382 | 449 | ||
383 | spin_lock(&desc->lock); | 450 | spin_lock(&desc->lock); |
384 | desc->status &= ~IRQ_INPROGRESS; | 451 | desc->status &= ~IRQ_INPROGRESS; |
385 | if (!(desc->status & IRQ_DISABLED) && desc->chip->unmask) | 452 | |
453 | if (unlikely(desc->status & IRQ_ONESHOT)) | ||
454 | desc->status |= IRQ_MASKED; | ||
455 | else if (!(desc->status & IRQ_DISABLED) && desc->chip->unmask) | ||
386 | desc->chip->unmask(irq); | 456 | desc->chip->unmask(irq); |
387 | out_unlock: | 457 | out_unlock: |
388 | spin_unlock(&desc->lock); | 458 | spin_unlock(&desc->lock); |
@@ -572,6 +642,7 @@ __set_irq_handler(unsigned int irq, irq_flow_handler_t handle, int is_chained, | |||
572 | desc->chip = &dummy_irq_chip; | 642 | desc->chip = &dummy_irq_chip; |
573 | } | 643 | } |
574 | 644 | ||
645 | chip_bus_lock(irq, desc); | ||
575 | spin_lock_irqsave(&desc->lock, flags); | 646 | spin_lock_irqsave(&desc->lock, flags); |
576 | 647 | ||
577 | /* Uninstall? */ | 648 | /* Uninstall? */ |
@@ -591,6 +662,7 @@ __set_irq_handler(unsigned int irq, irq_flow_handler_t handle, int is_chained, | |||
591 | desc->chip->startup(irq); | 662 | desc->chip->startup(irq); |
592 | } | 663 | } |
593 | spin_unlock_irqrestore(&desc->lock, flags); | 664 | spin_unlock_irqrestore(&desc->lock, flags); |
665 | chip_bus_sync_unlock(irq, desc); | ||
594 | } | 666 | } |
595 | EXPORT_SYMBOL_GPL(__set_irq_handler); | 667 | EXPORT_SYMBOL_GPL(__set_irq_handler); |
596 | 668 | ||
diff --git a/kernel/irq/internals.h b/kernel/irq/internals.h index e70ed5592eb9..1b5d742c6a77 100644 --- a/kernel/irq/internals.h +++ b/kernel/irq/internals.h | |||
@@ -44,6 +44,19 @@ extern int irq_select_affinity_usr(unsigned int irq); | |||
44 | 44 | ||
45 | extern void irq_set_thread_affinity(struct irq_desc *desc); | 45 | extern void irq_set_thread_affinity(struct irq_desc *desc); |
46 | 46 | ||
47 | /* Inline functions for support of irq chips on slow busses */ | ||
48 | static inline void chip_bus_lock(unsigned int irq, struct irq_desc *desc) | ||
49 | { | ||
50 | if (unlikely(desc->chip->bus_lock)) | ||
51 | desc->chip->bus_lock(irq); | ||
52 | } | ||
53 | |||
54 | static inline void chip_bus_sync_unlock(unsigned int irq, struct irq_desc *desc) | ||
55 | { | ||
56 | if (unlikely(desc->chip->bus_sync_unlock)) | ||
57 | desc->chip->bus_sync_unlock(irq); | ||
58 | } | ||
59 | |||
47 | /* | 60 | /* |
48 | * Debugging printout: | 61 | * Debugging printout: |
49 | */ | 62 | */ |
diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c index 0ec9ed831737..bde4c667d24d 100644 --- a/kernel/irq/manage.c +++ b/kernel/irq/manage.c | |||
@@ -230,9 +230,11 @@ void disable_irq_nosync(unsigned int irq) | |||
230 | if (!desc) | 230 | if (!desc) |
231 | return; | 231 | return; |
232 | 232 | ||
233 | chip_bus_lock(irq, desc); | ||
233 | spin_lock_irqsave(&desc->lock, flags); | 234 | spin_lock_irqsave(&desc->lock, flags); |
234 | __disable_irq(desc, irq, false); | 235 | __disable_irq(desc, irq, false); |
235 | spin_unlock_irqrestore(&desc->lock, flags); | 236 | spin_unlock_irqrestore(&desc->lock, flags); |
237 | chip_bus_sync_unlock(irq, desc); | ||
236 | } | 238 | } |
237 | EXPORT_SYMBOL(disable_irq_nosync); | 239 | EXPORT_SYMBOL(disable_irq_nosync); |
238 | 240 | ||
@@ -294,7 +296,8 @@ void __enable_irq(struct irq_desc *desc, unsigned int irq, bool resume) | |||
294 | * matches the last disable, processing of interrupts on this | 296 | * matches the last disable, processing of interrupts on this |
295 | * IRQ line is re-enabled. | 297 | * IRQ line is re-enabled. |
296 | * | 298 | * |
297 | * This function may be called from IRQ context. | 299 | * This function may be called from IRQ context only when |
300 | * desc->chip->bus_lock and desc->chip->bus_sync_unlock are NULL ! | ||
298 | */ | 301 | */ |
299 | void enable_irq(unsigned int irq) | 302 | void enable_irq(unsigned int irq) |
300 | { | 303 | { |
@@ -304,9 +307,11 @@ void enable_irq(unsigned int irq) | |||
304 | if (!desc) | 307 | if (!desc) |
305 | return; | 308 | return; |
306 | 309 | ||
310 | chip_bus_lock(irq, desc); | ||
307 | spin_lock_irqsave(&desc->lock, flags); | 311 | spin_lock_irqsave(&desc->lock, flags); |
308 | __enable_irq(desc, irq, false); | 312 | __enable_irq(desc, irq, false); |
309 | spin_unlock_irqrestore(&desc->lock, flags); | 313 | spin_unlock_irqrestore(&desc->lock, flags); |
314 | chip_bus_sync_unlock(irq, desc); | ||
310 | } | 315 | } |
311 | EXPORT_SYMBOL(enable_irq); | 316 | EXPORT_SYMBOL(enable_irq); |
312 | 317 | ||
@@ -436,6 +441,26 @@ int __irq_set_trigger(struct irq_desc *desc, unsigned int irq, | |||
436 | return ret; | 441 | return ret; |
437 | } | 442 | } |
438 | 443 | ||
444 | /* | ||
445 | * Default primary interrupt handler for threaded interrupts. Is | ||
446 | * assigned as primary handler when request_threaded_irq is called | ||
447 | * with handler == NULL. Useful for oneshot interrupts. | ||
448 | */ | ||
449 | static irqreturn_t irq_default_primary_handler(int irq, void *dev_id) | ||
450 | { | ||
451 | return IRQ_WAKE_THREAD; | ||
452 | } | ||
453 | |||
454 | /* | ||
455 | * Primary handler for nested threaded interrupts. Should never be | ||
456 | * called. | ||
457 | */ | ||
458 | static irqreturn_t irq_nested_primary_handler(int irq, void *dev_id) | ||
459 | { | ||
460 | WARN(1, "Primary handler called for nested irq %d\n", irq); | ||
461 | return IRQ_NONE; | ||
462 | } | ||
463 | |||
439 | static int irq_wait_for_interrupt(struct irqaction *action) | 464 | static int irq_wait_for_interrupt(struct irqaction *action) |
440 | { | 465 | { |
441 | while (!kthread_should_stop()) { | 466 | while (!kthread_should_stop()) { |
@@ -451,6 +476,23 @@ static int irq_wait_for_interrupt(struct irqaction *action) | |||
451 | return -1; | 476 | return -1; |
452 | } | 477 | } |
453 | 478 | ||
479 | /* | ||
480 | * Oneshot interrupts keep the irq line masked until the threaded | ||
481 | * handler finished. unmask if the interrupt has not been disabled and | ||
482 | * is marked MASKED. | ||
483 | */ | ||
484 | static void irq_finalize_oneshot(unsigned int irq, struct irq_desc *desc) | ||
485 | { | ||
486 | chip_bus_lock(irq, desc); | ||
487 | spin_lock_irq(&desc->lock); | ||
488 | if (!(desc->status & IRQ_DISABLED) && (desc->status & IRQ_MASKED)) { | ||
489 | desc->status &= ~IRQ_MASKED; | ||
490 | desc->chip->unmask(irq); | ||
491 | } | ||
492 | spin_unlock_irq(&desc->lock); | ||
493 | chip_bus_sync_unlock(irq, desc); | ||
494 | } | ||
495 | |||
454 | #ifdef CONFIG_SMP | 496 | #ifdef CONFIG_SMP |
455 | /* | 497 | /* |
456 | * Check whether we need to change the affinity of the interrupt thread. | 498 | * Check whether we need to change the affinity of the interrupt thread. |
@@ -492,7 +534,7 @@ static int irq_thread(void *data) | |||
492 | struct sched_param param = { .sched_priority = MAX_USER_RT_PRIO/2, }; | 534 | struct sched_param param = { .sched_priority = MAX_USER_RT_PRIO/2, }; |
493 | struct irqaction *action = data; | 535 | struct irqaction *action = data; |
494 | struct irq_desc *desc = irq_to_desc(action->irq); | 536 | struct irq_desc *desc = irq_to_desc(action->irq); |
495 | int wake; | 537 | int wake, oneshot = desc->status & IRQ_ONESHOT; |
496 | 538 | ||
497 | sched_setscheduler(current, SCHED_FIFO, ¶m); | 539 | sched_setscheduler(current, SCHED_FIFO, ¶m); |
498 | current->irqaction = action; | 540 | current->irqaction = action; |
@@ -518,6 +560,9 @@ static int irq_thread(void *data) | |||
518 | spin_unlock_irq(&desc->lock); | 560 | spin_unlock_irq(&desc->lock); |
519 | 561 | ||
520 | action->thread_fn(action->irq, action->dev_id); | 562 | action->thread_fn(action->irq, action->dev_id); |
563 | |||
564 | if (oneshot) | ||
565 | irq_finalize_oneshot(action->irq, desc); | ||
521 | } | 566 | } |
522 | 567 | ||
523 | wake = atomic_dec_and_test(&desc->threads_active); | 568 | wake = atomic_dec_and_test(&desc->threads_active); |
@@ -565,7 +610,7 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new) | |||
565 | struct irqaction *old, **old_ptr; | 610 | struct irqaction *old, **old_ptr; |
566 | const char *old_name = NULL; | 611 | const char *old_name = NULL; |
567 | unsigned long flags; | 612 | unsigned long flags; |
568 | int shared = 0; | 613 | int nested, shared = 0; |
569 | int ret; | 614 | int ret; |
570 | 615 | ||
571 | if (!desc) | 616 | if (!desc) |
@@ -590,10 +635,32 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new) | |||
590 | rand_initialize_irq(irq); | 635 | rand_initialize_irq(irq); |
591 | } | 636 | } |
592 | 637 | ||
638 | /* Oneshot interrupts are not allowed with shared */ | ||
639 | if ((new->flags & IRQF_ONESHOT) && (new->flags & IRQF_SHARED)) | ||
640 | return -EINVAL; | ||
641 | |||
642 | /* | ||
643 | * Check whether the interrupt nests into another interrupt | ||
644 | * thread. | ||
645 | */ | ||
646 | nested = desc->status & IRQ_NESTED_THREAD; | ||
647 | if (nested) { | ||
648 | if (!new->thread_fn) | ||
649 | return -EINVAL; | ||
650 | /* | ||
651 | * Replace the primary handler which was provided from | ||
652 | * the driver for non nested interrupt handling by the | ||
653 | * dummy function which warns when called. | ||
654 | */ | ||
655 | new->handler = irq_nested_primary_handler; | ||
656 | } | ||
657 | |||
593 | /* | 658 | /* |
594 | * Threaded handler ? | 659 | * Create a handler thread when a thread function is supplied |
660 | * and the interrupt does not nest into another interrupt | ||
661 | * thread. | ||
595 | */ | 662 | */ |
596 | if (new->thread_fn) { | 663 | if (new->thread_fn && !nested) { |
597 | struct task_struct *t; | 664 | struct task_struct *t; |
598 | 665 | ||
599 | t = kthread_create(irq_thread, new, "irq/%d-%s", irq, | 666 | t = kthread_create(irq_thread, new, "irq/%d-%s", irq, |
@@ -662,9 +729,12 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new) | |||
662 | desc->status |= IRQ_PER_CPU; | 729 | desc->status |= IRQ_PER_CPU; |
663 | #endif | 730 | #endif |
664 | 731 | ||
665 | desc->status &= ~(IRQ_AUTODETECT | IRQ_WAITING | | 732 | desc->status &= ~(IRQ_AUTODETECT | IRQ_WAITING | IRQ_ONESHOT | |
666 | IRQ_INPROGRESS | IRQ_SPURIOUS_DISABLED); | 733 | IRQ_INPROGRESS | IRQ_SPURIOUS_DISABLED); |
667 | 734 | ||
735 | if (new->flags & IRQF_ONESHOT) | ||
736 | desc->status |= IRQ_ONESHOT; | ||
737 | |||
668 | if (!(desc->status & IRQ_NOAUTOEN)) { | 738 | if (!(desc->status & IRQ_NOAUTOEN)) { |
669 | desc->depth = 0; | 739 | desc->depth = 0; |
670 | desc->status &= ~IRQ_DISABLED; | 740 | desc->status &= ~IRQ_DISABLED; |
@@ -875,7 +945,14 @@ EXPORT_SYMBOL_GPL(remove_irq); | |||
875 | */ | 945 | */ |
876 | void free_irq(unsigned int irq, void *dev_id) | 946 | void free_irq(unsigned int irq, void *dev_id) |
877 | { | 947 | { |
948 | struct irq_desc *desc = irq_to_desc(irq); | ||
949 | |||
950 | if (!desc) | ||
951 | return; | ||
952 | |||
953 | chip_bus_lock(irq, desc); | ||
878 | kfree(__free_irq(irq, dev_id)); | 954 | kfree(__free_irq(irq, dev_id)); |
955 | chip_bus_sync_unlock(irq, desc); | ||
879 | } | 956 | } |
880 | EXPORT_SYMBOL(free_irq); | 957 | EXPORT_SYMBOL(free_irq); |
881 | 958 | ||
@@ -884,6 +961,8 @@ EXPORT_SYMBOL(free_irq); | |||
884 | * @irq: Interrupt line to allocate | 961 | * @irq: Interrupt line to allocate |
885 | * @handler: Function to be called when the IRQ occurs. | 962 | * @handler: Function to be called when the IRQ occurs. |
886 | * Primary handler for threaded interrupts | 963 | * Primary handler for threaded interrupts |
964 | * If NULL and thread_fn != NULL the default | ||
965 | * primary handler is installed | ||
887 | * @thread_fn: Function called from the irq handler thread | 966 | * @thread_fn: Function called from the irq handler thread |
888 | * If NULL, no irq thread is created | 967 | * If NULL, no irq thread is created |
889 | * @irqflags: Interrupt type flags | 968 | * @irqflags: Interrupt type flags |
@@ -963,8 +1042,12 @@ int request_threaded_irq(unsigned int irq, irq_handler_t handler, | |||
963 | 1042 | ||
964 | if (desc->status & IRQ_NOREQUEST) | 1043 | if (desc->status & IRQ_NOREQUEST) |
965 | return -EINVAL; | 1044 | return -EINVAL; |
966 | if (!handler) | 1045 | |
967 | return -EINVAL; | 1046 | if (!handler) { |
1047 | if (!thread_fn) | ||
1048 | return -EINVAL; | ||
1049 | handler = irq_default_primary_handler; | ||
1050 | } | ||
968 | 1051 | ||
969 | action = kzalloc(sizeof(struct irqaction), GFP_KERNEL); | 1052 | action = kzalloc(sizeof(struct irqaction), GFP_KERNEL); |
970 | if (!action) | 1053 | if (!action) |
@@ -976,7 +1059,10 @@ int request_threaded_irq(unsigned int irq, irq_handler_t handler, | |||
976 | action->name = devname; | 1059 | action->name = devname; |
977 | action->dev_id = dev_id; | 1060 | action->dev_id = dev_id; |
978 | 1061 | ||
1062 | chip_bus_lock(irq, desc); | ||
979 | retval = __setup_irq(irq, desc, action); | 1063 | retval = __setup_irq(irq, desc, action); |
1064 | chip_bus_sync_unlock(irq, desc); | ||
1065 | |||
980 | if (retval) | 1066 | if (retval) |
981 | kfree(action); | 1067 | kfree(action); |
982 | 1068 | ||