On Tue, 11 Sep 2012 15:20:53 +0200 (CEST) Thomas Gleixner
We could have a 'mask my non-wakeup interrupts' library call that interested
drivers could call when appropriate, rather than having a flag to be set when
appropriate.
In the case of the gpio_omap driver, simply disabling wakeup requires less
talking to hardware than a full 'irq_mask' call. So if we give the driver
more information about what is happening, it can implement it more
efficiently.
(I generally prefer providing code to call rather than flags to set. It
gives more control with introducing more complexity).
>
> > The the very least I think we need a big comment saying the
> > IRQCHIP_MASK_ON_SUSPEND can only be used for irqchips which can always be
> > programmed, even when they are suspended from an runtime-PM perspective,
> > and that those chips must handle masking in their 'suspend' callback.
>
> Sigh, no. Either we make IRQCHIP_MASK_ON_SUSPEND into an
> implementation which masks the interrupts early, if the existing users
> find this acceptable or have a separate IRQCHIP_MASK_BEFORE_SUSPEND
> flag.
Something like this may be? Just compile-tested so far.
I'll see if I can understand the two current users of IRQCHIP_MASK_ON_SUSPEND
well enough to see if they should work with this flag. I suspect they will.
Thanks,
NeilBrown
diff --git a/include/linux/interrupt.h b/include/linux/interrupt.h
index e68a8e5..d46f6c1 100644
--- a/include/linux/interrupt.h
+++ b/include/linux/interrupt.h
@@ -228,8 +228,12 @@ extern void suspend_device_irqs(void);
extern void resume_device_irqs(void);
#ifdef CONFIG_PM_SLEEP
extern int check_wakeup_irqs(void);
+void mask_non_wakeup_irqs(void);
+void unmask_non_wakeup_irqs(void);
#else
static inline int check_wakeup_irqs(void) { return 0; }
+static inline void mask_non_wakeup_irqs(void) { return 0; }
+static inline void unmask_non_wakeup_irqs(void) { return 0; }
#endif
#else
static inline void suspend_device_irqs(void) { };
diff --git a/include/linux/irq.h b/include/linux/irq.h
index a5261e3..fbd9d7b 100644
--- a/include/linux/irq.h
+++ b/include/linux/irq.h
@@ -340,7 +340,10 @@ struct irq_chip {
*
* IRQCHIP_SET_TYPE_MASKED: Mask before calling chip.irq_set_type()
* IRQCHIP_EOI_IF_HANDLED: Only issue irq_eoi() when irq was handled
- * IRQCHIP_MASK_ON_SUSPEND: Mask non wake irqs in the suspend path
+ * IRQCHIP_MASK_ON_SUSPEND: Mask non wake irqs late in the suspend path
+ * IRQCHIP_MASK_PRE_SUSPEND: Mask non wake irqs early in the suspend path
+ * before devices are powered off or interrupts are
+ * disabled.
* IRQCHIP_ONOFFLINE_ENABLED: Only call irq_on/off_line callbacks
* when irq enabled
* IRQCHIP_SKIP_SET_WAKE: Skip chip.irq_set_wake(), for this irq chip
@@ -351,6 +354,7 @@ enum {
IRQCHIP_MASK_ON_SUSPEND = (1 << 2),
IRQCHIP_ONOFFLINE_ENABLED = (1 << 3),
IRQCHIP_SKIP_SET_WAKE = (1 << 4),
+ IRQCHIP_MASK_PRE_SUSPEND = (1 << 5),
};
/* This include will go away once we isolated irq_desc usage to core code */
diff --git a/kernel/irq/pm.c b/kernel/irq/pm.c
index 3728c97..836737b 100644
--- a/kernel/irq/pm.c
+++ b/kernel/irq/pm.c
@@ -128,3 +128,35 @@ int check_wakeup_irqs(void)
return 0;
}
+
+/**
+ * mask_non_wakeup_irqs - irqs that should not wake from suspend should be
+ * masked now.
+ * This is called after devices have been suspended, but before suspend_late
+ * and before interrupts are disabled. This means it should still be possible
+ * to talk to the interrupt controller to effect the mask.
+ */
+void mask_non_wakeup_irqs(void)
+{
+ struct irq_desc *desc;
+ int irq;
+
+ for_each_irq_desc(irq, desc)
+ if ((irq_desc_get_chip(desc)->flags & IRQCHIP_MASK_PRE_SUSPEND)
+ && !irqd_is_wakeup_set(&desc->irq_data)
+ && !irqd_irq_masked(&desc->irq_data))
+ mask_irq(desc);
+}
+
+void unmask_non_wakeup_irqs(void)
+{
+ struct irq_desc *desc;
+ int irq;
+
+ for_each_irq_desc(irq, desc)
+ if ((irq_desc_get_chip(desc)->flags & IRQCHIP_MASK_PRE_SUSPEND)
+ && !irqd_is_wakeup_set(&desc->irq_data)
+ && irqd_irq_masked(&desc->irq_data)
+ && !irqd_irq_disabled(&desc->irq_data))
+ unmask_irq(desc);
+}
diff --git a/kernel/power/suspend.c b/kernel/power/suspend.c
index 16a0f77..05054a4 100644
--- a/kernel/power/suspend.c
+++ b/kernel/power/suspend.c
@@ -23,6 +23,7 @@
#include <linux/slab.h>
#include <linux/export.h>
#include <linux/suspend.h>
+#include <linux/interrupt.h>
#include <linux/syscore_ops.h>
#include <trace/events/power.h>
@@ -222,11 +223,12 @@ int suspend_devices_and_enter(suspend_state_t state)
if (suspend_test(TEST_DEVICES))
goto Recover_platform;
+ mask_non_wakeup_irqs();
do {
error = suspend_enter(state, &wakeup);
} while (!error && !wakeup
&& suspend_ops->suspend_again && suspend_ops->suspend_again());
-
+ unmask_non_wakeup_irqs();
Resume_devices:
suspend_test_start();
dpm_resume_end(PMSG_RESUME);