diff options
Diffstat (limited to 'kernel/irq/manage.c')
-rw-r--r-- | kernel/irq/manage.c | 49 |
1 files changed, 45 insertions, 4 deletions
diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c index d222515a5a06..d7f7b5fd2476 100644 --- a/kernel/irq/manage.c +++ b/kernel/irq/manage.c | |||
@@ -436,6 +436,16 @@ int __irq_set_trigger(struct irq_desc *desc, unsigned int irq, | |||
436 | return ret; | 436 | return ret; |
437 | } | 437 | } |
438 | 438 | ||
439 | /* | ||
440 | * Default primary interrupt handler for threaded interrupts. Is | ||
441 | * assigned as primary handler when request_threaded_irq is called | ||
442 | * with handler == NULL. Useful for oneshot interrupts. | ||
443 | */ | ||
444 | static irqreturn_t irq_default_primary_handler(int irq, void *dev_id) | ||
445 | { | ||
446 | return IRQ_WAKE_THREAD; | ||
447 | } | ||
448 | |||
439 | static int irq_wait_for_interrupt(struct irqaction *action) | 449 | static int irq_wait_for_interrupt(struct irqaction *action) |
440 | { | 450 | { |
441 | while (!kthread_should_stop()) { | 451 | while (!kthread_should_stop()) { |
@@ -451,6 +461,21 @@ static int irq_wait_for_interrupt(struct irqaction *action) | |||
451 | return -1; | 461 | return -1; |
452 | } | 462 | } |
453 | 463 | ||
464 | /* | ||
465 | * Oneshot interrupts keep the irq line masked until the threaded | ||
466 | * handler finished. unmask if the interrupt has not been disabled and | ||
467 | * is marked MASKED. | ||
468 | */ | ||
469 | static void irq_finalize_oneshot(unsigned int irq, struct irq_desc *desc) | ||
470 | { | ||
471 | spin_lock_irq(&desc->lock); | ||
472 | if (!(desc->status & IRQ_DISABLED) && (desc->status & IRQ_MASKED)) { | ||
473 | desc->status &= ~IRQ_MASKED; | ||
474 | desc->chip->unmask(irq); | ||
475 | } | ||
476 | spin_unlock_irq(&desc->lock); | ||
477 | } | ||
478 | |||
454 | #ifdef CONFIG_SMP | 479 | #ifdef CONFIG_SMP |
455 | /* | 480 | /* |
456 | * Check whether we need to change the affinity of the interrupt thread. | 481 | * Check whether we need to change the affinity of the interrupt thread. |
@@ -492,7 +517,7 @@ static int irq_thread(void *data) | |||
492 | struct sched_param param = { .sched_priority = MAX_USER_RT_PRIO/2, }; | 517 | struct sched_param param = { .sched_priority = MAX_USER_RT_PRIO/2, }; |
493 | struct irqaction *action = data; | 518 | struct irqaction *action = data; |
494 | struct irq_desc *desc = irq_to_desc(action->irq); | 519 | struct irq_desc *desc = irq_to_desc(action->irq); |
495 | int wake; | 520 | int wake, oneshot = desc->status & IRQ_ONESHOT; |
496 | 521 | ||
497 | sched_setscheduler(current, SCHED_FIFO, ¶m); | 522 | sched_setscheduler(current, SCHED_FIFO, ¶m); |
498 | current->irqaction = action; | 523 | current->irqaction = action; |
@@ -518,6 +543,9 @@ static int irq_thread(void *data) | |||
518 | spin_unlock_irq(&desc->lock); | 543 | spin_unlock_irq(&desc->lock); |
519 | 544 | ||
520 | action->thread_fn(action->irq, action->dev_id); | 545 | action->thread_fn(action->irq, action->dev_id); |
546 | |||
547 | if (oneshot) | ||
548 | irq_finalize_oneshot(action->irq, desc); | ||
521 | } | 549 | } |
522 | 550 | ||
523 | wake = atomic_dec_and_test(&desc->threads_active); | 551 | 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 | rand_initialize_irq(irq); | 618 | rand_initialize_irq(irq); |
591 | } | 619 | } |
592 | 620 | ||
621 | /* Oneshot interrupts are not allowed with shared */ | ||
622 | if ((new->flags & IRQF_ONESHOT) && (new->flags & IRQF_SHARED)) | ||
623 | return -EINVAL; | ||
624 | |||
593 | /* | 625 | /* |
594 | * Threaded handler ? | 626 | * Threaded handler ? |
595 | */ | 627 | */ |
@@ -663,9 +695,12 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new) | |||
663 | desc->status |= IRQ_PER_CPU; | 695 | desc->status |= IRQ_PER_CPU; |
664 | #endif | 696 | #endif |
665 | 697 | ||
666 | desc->status &= ~(IRQ_AUTODETECT | IRQ_WAITING | | 698 | desc->status &= ~(IRQ_AUTODETECT | IRQ_WAITING | IRQ_ONESHOT | |
667 | IRQ_INPROGRESS | IRQ_SPURIOUS_DISABLED); | 699 | IRQ_INPROGRESS | IRQ_SPURIOUS_DISABLED); |
668 | 700 | ||
701 | if (new->flags & IRQF_ONESHOT) | ||
702 | desc->status |= IRQ_ONESHOT; | ||
703 | |||
669 | if (!(desc->status & IRQ_NOAUTOEN)) { | 704 | if (!(desc->status & IRQ_NOAUTOEN)) { |
670 | desc->depth = 0; | 705 | desc->depth = 0; |
671 | desc->status &= ~IRQ_DISABLED; | 706 | desc->status &= ~IRQ_DISABLED; |
@@ -878,6 +913,8 @@ EXPORT_SYMBOL(free_irq); | |||
878 | * @irq: Interrupt line to allocate | 913 | * @irq: Interrupt line to allocate |
879 | * @handler: Function to be called when the IRQ occurs. | 914 | * @handler: Function to be called when the IRQ occurs. |
880 | * Primary handler for threaded interrupts | 915 | * Primary handler for threaded interrupts |
916 | * If NULL and thread_fn != NULL the default | ||
917 | * primary handler is installed | ||
881 | * @thread_fn: Function called from the irq handler thread | 918 | * @thread_fn: Function called from the irq handler thread |
882 | * If NULL, no irq thread is created | 919 | * If NULL, no irq thread is created |
883 | * @irqflags: Interrupt type flags | 920 | * @irqflags: Interrupt type flags |
@@ -957,8 +994,12 @@ int request_threaded_irq(unsigned int irq, irq_handler_t handler, | |||
957 | 994 | ||
958 | if (desc->status & IRQ_NOREQUEST) | 995 | if (desc->status & IRQ_NOREQUEST) |
959 | return -EINVAL; | 996 | return -EINVAL; |
960 | if (!handler) | 997 | |
961 | return -EINVAL; | 998 | if (!handler) { |
999 | if (!thread_fn) | ||
1000 | return -EINVAL; | ||
1001 | handler = irq_default_primary_handler; | ||
1002 | } | ||
962 | 1003 | ||
963 | action = kzalloc(sizeof(struct irqaction), GFP_KERNEL); | 1004 | action = kzalloc(sizeof(struct irqaction), GFP_KERNEL); |
964 | if (!action) | 1005 | if (!action) |