diff options
-rw-r--r-- | include/linux/interrupt.h | 4 | ||||
-rw-r--r-- | include/linux/irq.h | 1 | ||||
-rw-r--r-- | kernel/irq/chip.c | 14 | ||||
-rw-r--r-- | kernel/irq/manage.c | 49 |
4 files changed, 61 insertions, 7 deletions
diff --git a/include/linux/interrupt.h b/include/linux/interrupt.h index 35e7df1e9f30..1ac57e522a1f 100644 --- a/include/linux/interrupt.h +++ b/include/linux/interrupt.h | |||
@@ -50,6 +50,9 @@ | |||
50 | * IRQF_IRQPOLL - Interrupt is used for polling (only the interrupt that is | 50 | * IRQF_IRQPOLL - Interrupt is used for polling (only the interrupt that is |
51 | * registered first in an shared interrupt is considered for | 51 | * registered first in an shared interrupt is considered for |
52 | * performance reasons) | 52 | * performance reasons) |
53 | * IRQF_ONESHOT - Interrupt is not reenabled after the hardirq handler finished. | ||
54 | * Used by threaded interrupts which need to keep the | ||
55 | * irq line disabled until the threaded handler has been run. | ||
53 | */ | 56 | */ |
54 | #define IRQF_DISABLED 0x00000020 | 57 | #define IRQF_DISABLED 0x00000020 |
55 | #define IRQF_SAMPLE_RANDOM 0x00000040 | 58 | #define IRQF_SAMPLE_RANDOM 0x00000040 |
@@ -59,6 +62,7 @@ | |||
59 | #define IRQF_PERCPU 0x00000400 | 62 | #define IRQF_PERCPU 0x00000400 |
60 | #define IRQF_NOBALANCING 0x00000800 | 63 | #define IRQF_NOBALANCING 0x00000800 |
61 | #define IRQF_IRQPOLL 0x00001000 | 64 | #define IRQF_IRQPOLL 0x00001000 |
65 | #define IRQF_ONESHOT 0x00002000 | ||
62 | 66 | ||
63 | /* | 67 | /* |
64 | * Bits used by threaded handlers: | 68 | * Bits used by threaded handlers: |
diff --git a/include/linux/irq.h b/include/linux/irq.h index cb2e77a3f7f7..5e7c6ee8c35c 100644 --- a/include/linux/irq.h +++ b/include/linux/irq.h | |||
@@ -69,6 +69,7 @@ typedef void (*irq_flow_handler_t)(unsigned int irq, | |||
69 | #define IRQ_MOVE_PCNTXT 0x01000000 /* IRQ migration from process context */ | 69 | #define IRQ_MOVE_PCNTXT 0x01000000 /* IRQ migration from process context */ |
70 | #define IRQ_AFFINITY_SET 0x02000000 /* IRQ affinity was set from userspace*/ | 70 | #define IRQ_AFFINITY_SET 0x02000000 /* IRQ affinity was set from userspace*/ |
71 | #define IRQ_SUSPENDED 0x04000000 /* IRQ has gone through suspend sequence */ | 71 | #define IRQ_SUSPENDED 0x04000000 /* IRQ has gone through suspend sequence */ |
72 | #define IRQ_ONESHOT 0x08000000 /* IRQ is not unmasked after hardirq */ | ||
72 | 73 | ||
73 | #ifdef CONFIG_IRQ_PER_CPU | 74 | #ifdef CONFIG_IRQ_PER_CPU |
74 | # define CHECK_IRQ_PER_CPU(var) ((var) & IRQ_PER_CPU) | 75 | # define CHECK_IRQ_PER_CPU(var) ((var) & IRQ_PER_CPU) |
diff --git a/kernel/irq/chip.c b/kernel/irq/chip.c index 13c68e71b726..b08c0d24f202 100644 --- a/kernel/irq/chip.c +++ b/kernel/irq/chip.c | |||
@@ -382,7 +382,10 @@ handle_level_irq(unsigned int irq, struct irq_desc *desc) | |||
382 | 382 | ||
383 | spin_lock(&desc->lock); | 383 | spin_lock(&desc->lock); |
384 | desc->status &= ~IRQ_INPROGRESS; | 384 | desc->status &= ~IRQ_INPROGRESS; |
385 | if (!(desc->status & IRQ_DISABLED) && desc->chip->unmask) | 385 | |
386 | if (unlikely(desc->status & IRQ_ONESHOT)) | ||
387 | desc->status |= IRQ_MASKED; | ||
388 | else if (!(desc->status & IRQ_DISABLED) && desc->chip->unmask) | ||
386 | desc->chip->unmask(irq); | 389 | desc->chip->unmask(irq); |
387 | out_unlock: | 390 | out_unlock: |
388 | spin_unlock(&desc->lock); | 391 | spin_unlock(&desc->lock); |
@@ -478,8 +481,13 @@ handle_edge_irq(unsigned int irq, struct irq_desc *desc) | |||
478 | kstat_incr_irqs_this_cpu(irq, desc); | 481 | kstat_incr_irqs_this_cpu(irq, desc); |
479 | 482 | ||
480 | /* Start handling the irq */ | 483 | /* Start handling the irq */ |
481 | if (desc->chip->ack) | 484 | if (unlikely(desc->status & IRQ_ONESHOT)) { |
482 | desc->chip->ack(irq); | 485 | desc->status |= IRQ_MASKED; |
486 | mask_ack_irq(desc, irq); | ||
487 | } else { | ||
488 | if (desc->chip->ack) | ||
489 | desc->chip->ack(irq); | ||
490 | } | ||
483 | 491 | ||
484 | /* Mark the IRQ currently in progress.*/ | 492 | /* Mark the IRQ currently in progress.*/ |
485 | desc->status |= IRQ_INPROGRESS; | 493 | desc->status |= IRQ_INPROGRESS; |
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) |