aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThomas Gleixner <tglx@linutronix.de>2011-04-15 16:36:08 -0400
committerThomas Gleixner <tglx@linutronix.de>2011-04-23 09:56:24 -0400
commitcfefd21e693dca791bf9ecfc9dd3794facad533c (patch)
tree20915250e5c9749eea148cab17534b70c094386f
parent7d8280624797bbe2f5170bd3c85c75a8c9c74242 (diff)
genirq: Add chip suspend and resume callbacks
These callbacks are only called in the syscore suspend/resume code on interrupt chips which have been registered via the generic irq chip mechanism. Calling those callbacks per irq would be rather icky, but with the generic irq chip mechanism we can call this per registered chip. Signed-off-by: Thomas Gleixner <tglx@linutronix.de> Cc: linux-arm-kernel@lists.infradead.org
-rw-r--r--include/linux/irq.h11
-rw-r--r--kernel/irq/generic-chip.c93
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);
682int irq_setup_alt_chip(struct irq_data *d, unsigned int type); 691int irq_setup_alt_chip(struct irq_data *d, unsigned int type);
692void irq_remove_generic_chip(struct irq_chip_generic *gc, u32 msk,
693 unsigned int clr, unsigned int set);
683 694
684static inline struct irq_chip_type *irq_data_get_chip_type(struct irq_data *d) 695static 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
15static LIST_HEAD(gc_list);
16static DEFINE_RAW_SPINLOCK(gc_lock);
17
14static inline struct irq_chip_regs *cur_regs(struct irq_data *d) 18static 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 */
280void 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
302static 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
315static 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
331static 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
343static struct syscore_ops irq_gc_syscore_ops = {
344 .suspend = irq_gc_suspend,
345 .resume = irq_gc_resume,
346 .shutdown = irq_gc_shutdown,
347};
348
349static int __init irq_gc_init_ops(void)
350{
351 register_syscore_ops(&irq_gc_syscore_ops);
352 return 0;
353}
354device_initcall(irq_gc_init_ops);