Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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

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