diff options
| -rw-r--r-- | include/linux/irq.h | 11 | ||||
| -rw-r--r-- | kernel/irq/generic-chip.c | 93 |
2 files changed, 104 insertions, 0 deletions
diff --git a/include/linux/irq.h b/include/linux/irq.h index 2ba2f1216790..8b4538446636 100644 --- a/include/linux/irq.h +++ b/include/linux/irq.h | |||
| @@ -280,6 +280,9 @@ static inline void irqd_clr_chained_irq_inprogress(struct irq_data *d) | |||
| 280 | * @irq_bus_sync_unlock:function to sync and unlock slow bus (i2c) chips | 280 | * @irq_bus_sync_unlock:function to sync and unlock slow bus (i2c) chips |
| 281 | * @irq_cpu_online: configure an interrupt source for a secondary CPU | 281 | * @irq_cpu_online: configure an interrupt source for a secondary CPU |
| 282 | * @irq_cpu_offline: un-configure an interrupt source for a secondary CPU | 282 | * @irq_cpu_offline: un-configure an interrupt source for a secondary CPU |
| 283 | * @irq_suspend: function called from core code on suspend once per chip | ||
| 284 | * @irq_resume: function called from core code on resume once per chip | ||
| 285 | * @irq_pm_shutdown: function called from core code on shutdown once per chip | ||
| 283 | * @irq_print_chip: optional to print special chip info in show_interrupts | 286 | * @irq_print_chip: optional to print special chip info in show_interrupts |
| 284 | * @flags: chip specific flags | 287 | * @flags: chip specific flags |
| 285 | * | 288 | * |
| @@ -309,6 +312,10 @@ struct irq_chip { | |||
| 309 | void (*irq_cpu_online)(struct irq_data *data); | 312 | void (*irq_cpu_online)(struct irq_data *data); |
| 310 | void (*irq_cpu_offline)(struct irq_data *data); | 313 | void (*irq_cpu_offline)(struct irq_data *data); |
| 311 | 314 | ||
| 315 | void (*irq_suspend)(struct irq_data *data); | ||
| 316 | void (*irq_resume)(struct irq_data *data); | ||
| 317 | void (*irq_pm_shutdown)(struct irq_data *data); | ||
| 318 | |||
| 312 | void (*irq_print_chip)(struct irq_data *data, struct seq_file *p); | 319 | void (*irq_print_chip)(struct irq_data *data, struct seq_file *p); |
| 313 | 320 | ||
| 314 | unsigned long flags; | 321 | unsigned long flags; |
| @@ -626,6 +633,7 @@ struct irq_chip_type { | |||
| 626 | * @wake_active: Interrupt is marked as an wakeup from suspend source | 633 | * @wake_active: Interrupt is marked as an wakeup from suspend source |
| 627 | * @num_ct: Number of available irq_chip_type instances (usually 1) | 634 | * @num_ct: Number of available irq_chip_type instances (usually 1) |
| 628 | * @private: Private data for non generic chip callbacks | 635 | * @private: Private data for non generic chip callbacks |
| 636 | * @list: List head for keeping track of instances | ||
| 629 | * @chip_types: Array of interrupt irq_chip_types | 637 | * @chip_types: Array of interrupt irq_chip_types |
| 630 | * | 638 | * |
| 631 | * Note, that irq_chip_generic can have multiple irq_chip_type | 639 | * Note, that irq_chip_generic can have multiple irq_chip_type |
| @@ -646,6 +654,7 @@ struct irq_chip_generic { | |||
| 646 | u32 wake_active; | 654 | u32 wake_active; |
| 647 | unsigned int num_ct; | 655 | unsigned int num_ct; |
| 648 | void *private; | 656 | void *private; |
| 657 | struct list_head list; | ||
| 649 | struct irq_chip_type chip_types[0]; | 658 | struct irq_chip_type chip_types[0]; |
| 650 | }; | 659 | }; |
| 651 | 660 | ||
| @@ -680,6 +689,8 @@ void irq_setup_generic_chip(struct irq_chip_generic *gc, u32 msk, | |||
| 680 | enum irq_gc_flags flags, unsigned int clr, | 689 | enum irq_gc_flags flags, unsigned int clr, |
| 681 | unsigned int set); | 690 | unsigned int set); |
| 682 | int irq_setup_alt_chip(struct irq_data *d, unsigned int type); | 691 | int irq_setup_alt_chip(struct irq_data *d, unsigned int type); |
| 692 | void irq_remove_generic_chip(struct irq_chip_generic *gc, u32 msk, | ||
| 693 | unsigned int clr, unsigned int set); | ||
| 683 | 694 | ||
| 684 | static inline struct irq_chip_type *irq_data_get_chip_type(struct irq_data *d) | 695 | static inline struct irq_chip_type *irq_data_get_chip_type(struct irq_data *d) |
| 685 | { | 696 | { |
diff --git a/kernel/irq/generic-chip.c b/kernel/irq/generic-chip.c index eb23e5924260..31a9db711906 100644 --- a/kernel/irq/generic-chip.c +++ b/kernel/irq/generic-chip.c | |||
| @@ -8,9 +8,13 @@ | |||
| 8 | #include <linux/slab.h> | 8 | #include <linux/slab.h> |
| 9 | #include <linux/interrupt.h> | 9 | #include <linux/interrupt.h> |
| 10 | #include <linux/kernel_stat.h> | 10 | #include <linux/kernel_stat.h> |
| 11 | #include <linux/syscore_ops.h> | ||
| 11 | 12 | ||
| 12 | #include "internals.h" | 13 | #include "internals.h" |
| 13 | 14 | ||
| 15 | static LIST_HEAD(gc_list); | ||
| 16 | static DEFINE_RAW_SPINLOCK(gc_lock); | ||
| 17 | |||
| 14 | static inline struct irq_chip_regs *cur_regs(struct irq_data *d) | 18 | static inline struct irq_chip_regs *cur_regs(struct irq_data *d) |
| 15 | { | 19 | { |
| 16 | return &container_of(d->chip, struct irq_chip_type, chip)->regs; | 20 | return &container_of(d->chip, struct irq_chip_type, chip)->regs; |
| @@ -219,6 +223,10 @@ void irq_setup_generic_chip(struct irq_chip_generic *gc, u32 msk, | |||
| 219 | struct irq_chip_type *ct = gc->chip_types; | 223 | struct irq_chip_type *ct = gc->chip_types; |
| 220 | unsigned int i; | 224 | unsigned int i; |
| 221 | 225 | ||
| 226 | raw_spin_lock(&gc_lock); | ||
| 227 | list_add_tail(&gc->list, &gc_list); | ||
| 228 | raw_spin_unlock(&gc_lock); | ||
| 229 | |||
| 222 | /* Init mask cache ? */ | 230 | /* Init mask cache ? */ |
| 223 | if (flags & IRQ_GC_INIT_MASK_CACHE) | 231 | if (flags & IRQ_GC_INIT_MASK_CACHE) |
| 224 | gc->mask_cache = irq_reg_readl(gc->reg_base + ct->regs.mask); | 232 | gc->mask_cache = irq_reg_readl(gc->reg_base + ct->regs.mask); |
| @@ -259,3 +267,88 @@ int irq_setup_alt_chip(struct irq_data *d, unsigned int type) | |||
| 259 | } | 267 | } |
| 260 | return -EINVAL; | 268 | return -EINVAL; |
| 261 | } | 269 | } |
| 270 | |||
| 271 | /** | ||
| 272 | * irq_remove_generic_chip - Remove a chip | ||
| 273 | * @gc: Generic irq chip holding all data | ||
| 274 | * @msk: Bitmask holding the irqs to initialize relative to gc->irq_base | ||
| 275 | * @clr: IRQ_* bits to clear | ||
| 276 | * @set: IRQ_* bits to set | ||
| 277 | * | ||
| 278 | * Remove up to 32 interrupts starting from gc->irq_base. | ||
| 279 | */ | ||
| 280 | void irq_remove_generic_chip(struct irq_chip_generic *gc, u32 msk, | ||
| 281 | unsigned int clr, unsigned int set) | ||
| 282 | { | ||
| 283 | unsigned int i = gc->irq_base; | ||
| 284 | |||
| 285 | raw_spin_lock(&gc_lock); | ||
| 286 | list_del(&gc->list); | ||
| 287 | raw_spin_unlock(&gc_lock); | ||
| 288 | |||
| 289 | for (; msk; msk >>= 1, i++) { | ||
| 290 | if (!msk & 0x01) | ||
| 291 | continue; | ||
| 292 | |||
| 293 | /* Remove handler first. That will mask the irq line */ | ||
| 294 | irq_set_handler(i, NULL); | ||
| 295 | irq_set_chip(i, &no_irq_chip); | ||
| 296 | irq_set_chip_data(i, NULL); | ||
| 297 | irq_modify_status(i, clr, set); | ||
| 298 | } | ||
| 299 | } | ||
| 300 | |||
| 301 | #ifdef CONFIG_PM | ||
| 302 | static int irq_gc_suspend(void) | ||
| 303 | { | ||
| 304 | struct irq_chip_generic *gc; | ||
| 305 | |||
| 306 | list_for_each_entry(gc, &gc_list, list) { | ||
| 307 | struct irq_chip_type *ct = gc->chip_types; | ||
| 308 | |||
| 309 | if (ct->chip.irq_suspend) | ||
| 310 | ct->chip.irq_suspend(irq_get_irq_data(gc->irq_base)); | ||
| 311 | } | ||
| 312 | return 0; | ||
| 313 | } | ||
| 314 | |||
| 315 | static void irq_gc_resume(void) | ||
| 316 | { | ||
| 317 | struct irq_chip_generic *gc; | ||
| 318 | |||
| 319 | list_for_each_entry(gc, &gc_list, list) { | ||
| 320 | struct irq_chip_type *ct = gc->chip_types; | ||
| 321 | |||
| 322 | if (ct->chip.irq_resume) | ||
| 323 | ct->chip.irq_resume(irq_get_irq_data(gc->irq_base)); | ||
| 324 | } | ||
| 325 | } | ||
| 326 | #else | ||
| 327 | #define irq_gc_suspend NULL | ||
| 328 | #define irq_gc_resume NULL | ||
| 329 | #endif | ||
| 330 | |||
| 331 | static void irq_gc_shutdown(void) | ||
| 332 | { | ||
| 333 | struct irq_chip_generic *gc; | ||
| 334 | |||
| 335 | list_for_each_entry(gc, &gc_list, list) { | ||
| 336 | struct irq_chip_type *ct = gc->chip_types; | ||
| 337 | |||
| 338 | if (ct->chip.irq_pm_shutdown) | ||
| 339 | ct->chip.irq_pm_shutdown(irq_get_irq_data(gc->irq_base)); | ||
| 340 | } | ||
| 341 | } | ||
| 342 | |||
| 343 | static struct syscore_ops irq_gc_syscore_ops = { | ||
| 344 | .suspend = irq_gc_suspend, | ||
| 345 | .resume = irq_gc_resume, | ||
| 346 | .shutdown = irq_gc_shutdown, | ||
| 347 | }; | ||
| 348 | |||
| 349 | static int __init irq_gc_init_ops(void) | ||
| 350 | { | ||
| 351 | register_syscore_ops(&irq_gc_syscore_ops); | ||
| 352 | return 0; | ||
| 353 | } | ||
| 354 | device_initcall(irq_gc_init_ops); | ||
