diff options
| -rw-r--r-- | include/linux/irq.h | 3 | ||||
| -rw-r--r-- | kernel/irq/chip.c | 67 | ||||
| -rw-r--r-- | kernel/irq/manage.c | 34 |
3 files changed, 101 insertions, 3 deletions
diff --git a/include/linux/irq.h b/include/linux/irq.h index ce8171bc6fac..8778ee993937 100644 --- a/include/linux/irq.h +++ b/include/linux/irq.h | |||
| @@ -70,6 +70,7 @@ typedef void (*irq_flow_handler_t)(unsigned int irq, | |||
| 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 | #define IRQ_ONESHOT 0x08000000 /* IRQ is not unmasked after hardirq */ |
| 73 | #define IRQ_NESTED_THREAD 0x10000000 /* IRQ is nested into another, no own handler thread */ | ||
| 73 | 74 | ||
| 74 | #ifdef CONFIG_IRQ_PER_CPU | 75 | #ifdef CONFIG_IRQ_PER_CPU |
| 75 | # define CHECK_IRQ_PER_CPU(var) ((var) & IRQ_PER_CPU) | 76 | # define CHECK_IRQ_PER_CPU(var) ((var) & IRQ_PER_CPU) |
| @@ -386,6 +387,8 @@ set_irq_chained_handler(unsigned int irq, | |||
| 386 | __set_irq_handler(irq, handle, 1, NULL); | 387 | __set_irq_handler(irq, handle, 1, NULL); |
| 387 | } | 388 | } |
| 388 | 389 | ||
| 390 | extern void set_irq_nested_thread(unsigned int irq, int nest); | ||
| 391 | |||
| 389 | extern void set_irq_noprobe(unsigned int irq); | 392 | extern void set_irq_noprobe(unsigned int irq); |
| 390 | extern void set_irq_probe(unsigned int irq); | 393 | extern void set_irq_probe(unsigned int irq); |
| 391 | 394 | ||
diff --git a/kernel/irq/chip.c b/kernel/irq/chip.c index f856330e684a..5765aad94998 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 |
diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c index 0a3fd5b524c9..e485cea04bf1 100644 --- a/kernel/irq/manage.c +++ b/kernel/irq/manage.c | |||
| @@ -451,6 +451,16 @@ static irqreturn_t irq_default_primary_handler(int irq, void *dev_id) | |||
| 451 | return IRQ_WAKE_THREAD; | 451 | return IRQ_WAKE_THREAD; |
| 452 | } | 452 | } |
| 453 | 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 | |||
| 454 | static int irq_wait_for_interrupt(struct irqaction *action) | 464 | static int irq_wait_for_interrupt(struct irqaction *action) |
| 455 | { | 465 | { |
| 456 | while (!kthread_should_stop()) { | 466 | while (!kthread_should_stop()) { |
| @@ -600,7 +610,7 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new) | |||
| 600 | struct irqaction *old, **old_ptr; | 610 | struct irqaction *old, **old_ptr; |
| 601 | const char *old_name = NULL; | 611 | const char *old_name = NULL; |
| 602 | unsigned long flags; | 612 | unsigned long flags; |
| 603 | int shared = 0; | 613 | int nested, shared = 0; |
| 604 | int ret; | 614 | int ret; |
| 605 | 615 | ||
| 606 | if (!desc) | 616 | if (!desc) |
| @@ -630,9 +640,27 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new) | |||
| 630 | return -EINVAL; | 640 | return -EINVAL; |
| 631 | 641 | ||
| 632 | /* | 642 | /* |
| 633 | * Threaded handler ? | 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 | |||
| 658 | /* | ||
| 659 | * Create a handler thread when a thread function is supplied | ||
| 660 | * and the interrupt does not nest into another interrupt | ||
| 661 | * thread. | ||
| 634 | */ | 662 | */ |
| 635 | if (new->thread_fn) { | 663 | if (new->thread_fn && !nested) { |
| 636 | struct task_struct *t; | 664 | struct task_struct *t; |
| 637 | 665 | ||
| 638 | t = kthread_create(irq_thread, new, "irq/%d-%s", irq, | 666 | t = kthread_create(irq_thread, new, "irq/%d-%s", irq, |
