genirq: Add chip_[suspend|resume] PM support to irq_chip
authorBrian Norris <computersforpeace@gmail.com>
Wed, 22 Jul 2015 23:21:39 +0000 (16:21 -0700)
committerThomas Gleixner <tglx@linutronix.de>
Mon, 27 Jul 2015 06:09:38 +0000 (08:09 +0200)
Some (admittedly odd) irqchips perform functions that are not directly
related to any of their child IRQ lines, and therefore need to perform
some tasks during suspend/resume regardless of whether there are
any "installed" interrupts for the irqchip. However, the current
generic-chip framework does not call the chip's irq_{suspend,resume}
when there are no interrupts installed (this makes sense, because there
are no irq_data objects for such a call to be made).

More specifically, irq-bcm7120-l2 configures both a forwarding mask
(which affects other top-level GIC IRQs) and a second-level interrupt
mask (for managing its own child interrupts). The former must be
saved/restored on suspend/resume, even when there's nothing to do for
the latter.

This patch adds a new set of suspend/resume hooks to irq_chip_generic,
to help represent *chip* suspend/resume, rather than IRQ suspend/resume.
These callbacks will always be called for an IRQ chip (regardless of the
installed interrupts) and are based on the per-chip irq_chip_generic
struct, rather than the per-IRQ irq_data struct.

The original problem report is described in extra detail here:
http://lkml.kernel.org/g/20150619224123.GL4917@ld-irv-0074

Signed-off-by: Brian Norris <computersforpeace@gmail.com>
Tested-by: Florian Fainelli <f.fainelli@gmail.com>
Cc: Gregory Fong <gregory.0xf0@gmail.com>
Cc: bcm-kernel-feedback-list@broadcom.com
Cc: linux-mips@linux-mips.org
Cc: Kevin Cernekee <cernekee@chromium.org>
Cc: Jason Cooper <jason@lakedaemon.net>
Link: http://lkml.kernel.org/r/1437607300-40858-1-git-send-email-computersforpeace@gmail.com
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
include/linux/irq.h
kernel/irq/generic-chip.c

index 5284cb166d9073f6f3cfd534d2983aaca1790e06..2c8730a108be2777a7471b883cb062e548b623b1 100644 (file)
@@ -324,8 +324,10 @@ static inline irq_hw_number_t irqd_to_hwirq(struct irq_data *d)
  * @irq_bus_sync_unlock:function to sync and unlock slow bus (i2c) chips
  * @irq_cpu_online:    configure an interrupt source for a secondary CPU
  * @irq_cpu_offline:   un-configure an interrupt source for a secondary CPU
- * @irq_suspend:       function called from core code on suspend once per chip
- * @irq_resume:                function called from core code on resume once per chip
+ * @irq_suspend:       function called from core code on suspend once per
+ *                     chip, when one or more interrupts are installed
+ * @irq_resume:                function called from core code on resume once per chip,
+ *                     when one ore more interrupts are installed
  * @irq_pm_shutdown:   function called from core code on shutdown once per chip
  * @irq_calc_mask:     Optional function to set irq_data.mask for special cases
  * @irq_print_chip:    optional to print special chip info in show_interrupts
@@ -760,6 +762,12 @@ struct irq_chip_type {
  * @reg_base:          Register base address (virtual)
  * @reg_readl:         Alternate I/O accessor (defaults to readl if NULL)
  * @reg_writel:                Alternate I/O accessor (defaults to writel if NULL)
+ * @suspend:           Function called from core code on suspend once per
+ *                     chip; can be useful instead of irq_chip::suspend to
+ *                     handle chip details even when no interrupts are in use
+ * @resume:            Function called from core code on resume once per chip;
+ *                     can be useful instead of irq_chip::suspend to handle
+ *                     chip details even when no interrupts are in use
  * @irq_base:          Interrupt base nr for this chip
  * @irq_cnt:           Number of interrupts handled by this chip
  * @mask_cache:                Cached mask register shared between all chip types
@@ -786,6 +794,8 @@ struct irq_chip_generic {
        void __iomem            *reg_base;
        u32                     (*reg_readl)(void __iomem *addr);
        void                    (*reg_writel)(u32 val, void __iomem *addr);
+       void                    (*suspend)(struct irq_chip_generic *gc);
+       void                    (*resume)(struct irq_chip_generic *gc);
        unsigned int            irq_base;
        unsigned int            irq_cnt;
        u32                     mask_cache;
index 15b370daf23446d3a6b5213cbeac4d9559fb095d..abd286afbd2732544e54a4653053a4d9ce785286 100644 (file)
@@ -553,6 +553,9 @@ static int irq_gc_suspend(void)
                        if (data)
                                ct->chip.irq_suspend(data);
                }
+
+               if (gc->suspend)
+                       gc->suspend(gc);
        }
        return 0;
 }
@@ -564,6 +567,9 @@ static void irq_gc_resume(void)
        list_for_each_entry(gc, &gc_list, list) {
                struct irq_chip_type *ct = gc->chip_types;
 
+               if (gc->resume)
+                       gc->resume(gc);
+
                if (ct->chip.irq_resume) {
                        struct irq_data *data = irq_gc_get_irq_data(gc);