diff options
Diffstat (limited to 'kernel/irq')
| -rw-r--r-- | kernel/irq/chip.c | 14 | ||||
| -rw-r--r-- | kernel/irq/manage.c | 49 |
2 files changed, 56 insertions, 7 deletions
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) |
