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); | ||