Commit 90428a8e authored by Maulik Shah's avatar Maulik Shah Committed by Marc Zyngier

genirq/PM: Introduce IRQCHIP_ENABLE_WAKEUP_ON_SUSPEND flag

An interrupt that is disabled/masked but set for wakeup may still need to
be able to wake up the system from sleep states like "suspend to RAM".

To that effect, introduce the IRQCHIP_ENABLE_WAKEUP_ON_SUSPEND flag.
If the irqchip have this flag set, the irq PM code will enable/unmask
the irqs that are marked for wakeup, but that are in a disabled state.

On resume, such irqs will be restored back to their disabled state.
Suggested-by: default avatarThomas Gleixner <tglx@linutronix.de>
Signed-off-by: default avatarMaulik Shah <mkshah@codeaurora.org>
[maz: commit message fix-up]
Signed-off-by: default avatarMarc Zyngier <maz@kernel.org>
Tested-by: default avatarStephen Boyd <swboyd@chromium.org>
Reviewed-by: default avatarThomas Gleixner <tglx@linutronix.de>
Reviewed-by: default avatarDouglas Anderson <dianders@chromium.org>
Link: https://lore.kernel.org/r/1601267524-20199-4-git-send-email-mkshah@codeaurora.org
parent f41aaca5
...@@ -215,6 +215,8 @@ struct irq_data { ...@@ -215,6 +215,8 @@ struct irq_data {
* from actual interrupt context. * from actual interrupt context.
* IRQD_AFFINITY_ON_ACTIVATE - Affinity is set on activation. Don't call * IRQD_AFFINITY_ON_ACTIVATE - Affinity is set on activation. Don't call
* irq_chip::irq_set_affinity() when deactivated. * irq_chip::irq_set_affinity() when deactivated.
* IRQD_IRQ_ENABLED_ON_SUSPEND - Interrupt is enabled on suspend by irq pm if
* irqchip have flag IRQCHIP_ENABLE_WAKEUP_ON_SUSPEND set.
*/ */
enum { enum {
IRQD_TRIGGER_MASK = 0xf, IRQD_TRIGGER_MASK = 0xf,
...@@ -240,6 +242,7 @@ enum { ...@@ -240,6 +242,7 @@ enum {
IRQD_MSI_NOMASK_QUIRK = (1 << 27), IRQD_MSI_NOMASK_QUIRK = (1 << 27),
IRQD_HANDLE_ENFORCE_IRQCTX = (1 << 28), IRQD_HANDLE_ENFORCE_IRQCTX = (1 << 28),
IRQD_AFFINITY_ON_ACTIVATE = (1 << 29), IRQD_AFFINITY_ON_ACTIVATE = (1 << 29),
IRQD_IRQ_ENABLED_ON_SUSPEND = (1 << 30),
}; };
#define __irqd_to_state(d) ACCESS_PRIVATE((d)->common, state_use_accessors) #define __irqd_to_state(d) ACCESS_PRIVATE((d)->common, state_use_accessors)
...@@ -319,6 +322,11 @@ static inline bool irqd_is_handle_enforce_irqctx(struct irq_data *d) ...@@ -319,6 +322,11 @@ static inline bool irqd_is_handle_enforce_irqctx(struct irq_data *d)
return __irqd_to_state(d) & IRQD_HANDLE_ENFORCE_IRQCTX; return __irqd_to_state(d) & IRQD_HANDLE_ENFORCE_IRQCTX;
} }
static inline bool irqd_is_enabled_on_suspend(struct irq_data *d)
{
return __irqd_to_state(d) & IRQD_IRQ_ENABLED_ON_SUSPEND;
}
static inline bool irqd_is_wakeup_set(struct irq_data *d) static inline bool irqd_is_wakeup_set(struct irq_data *d)
{ {
return __irqd_to_state(d) & IRQD_WAKEUP_STATE; return __irqd_to_state(d) & IRQD_WAKEUP_STATE;
...@@ -545,27 +553,30 @@ struct irq_chip { ...@@ -545,27 +553,30 @@ struct irq_chip {
/* /*
* irq_chip specific flags * irq_chip specific flags
* *
* IRQCHIP_SET_TYPE_MASKED: Mask before calling chip.irq_set_type() * IRQCHIP_SET_TYPE_MASKED: Mask before calling chip.irq_set_type()
* IRQCHIP_EOI_IF_HANDLED: Only issue irq_eoi() when irq was handled * 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 in the suspend path
* IRQCHIP_ONOFFLINE_ENABLED: Only call irq_on/off_line callbacks * IRQCHIP_ONOFFLINE_ENABLED: Only call irq_on/off_line callbacks
* when irq enabled * when irq enabled
* IRQCHIP_SKIP_SET_WAKE: Skip chip.irq_set_wake(), for this irq chip * IRQCHIP_SKIP_SET_WAKE: Skip chip.irq_set_wake(), for this irq chip
* IRQCHIP_ONESHOT_SAFE: One shot does not require mask/unmask * IRQCHIP_ONESHOT_SAFE: One shot does not require mask/unmask
* IRQCHIP_EOI_THREADED: Chip requires eoi() on unmask in threaded mode * IRQCHIP_EOI_THREADED: Chip requires eoi() on unmask in threaded mode
* IRQCHIP_SUPPORTS_LEVEL_MSI Chip can provide two doorbells for Level MSIs * IRQCHIP_SUPPORTS_LEVEL_MSI: Chip can provide two doorbells for Level MSIs
* IRQCHIP_SUPPORTS_NMI: Chip can deliver NMIs, only for root irqchips * IRQCHIP_SUPPORTS_NMI: Chip can deliver NMIs, only for root irqchips
* IRQCHIP_ENABLE_WAKEUP_ON_SUSPEND: Invokes __enable_irq()/__disable_irq() for wake irqs
* in the suspend path if they are in disabled state
*/ */
enum { enum {
IRQCHIP_SET_TYPE_MASKED = (1 << 0), IRQCHIP_SET_TYPE_MASKED = (1 << 0),
IRQCHIP_EOI_IF_HANDLED = (1 << 1), IRQCHIP_EOI_IF_HANDLED = (1 << 1),
IRQCHIP_MASK_ON_SUSPEND = (1 << 2), IRQCHIP_MASK_ON_SUSPEND = (1 << 2),
IRQCHIP_ONOFFLINE_ENABLED = (1 << 3), IRQCHIP_ONOFFLINE_ENABLED = (1 << 3),
IRQCHIP_SKIP_SET_WAKE = (1 << 4), IRQCHIP_SKIP_SET_WAKE = (1 << 4),
IRQCHIP_ONESHOT_SAFE = (1 << 5), IRQCHIP_ONESHOT_SAFE = (1 << 5),
IRQCHIP_EOI_THREADED = (1 << 6), IRQCHIP_EOI_THREADED = (1 << 6),
IRQCHIP_SUPPORTS_LEVEL_MSI = (1 << 7), IRQCHIP_SUPPORTS_LEVEL_MSI = (1 << 7),
IRQCHIP_SUPPORTS_NMI = (1 << 8), IRQCHIP_SUPPORTS_NMI = (1 << 8),
IRQCHIP_ENABLE_WAKEUP_ON_SUSPEND = (1 << 9),
}; };
#include <linux/irqdesc.h> #include <linux/irqdesc.h>
......
...@@ -57,6 +57,7 @@ static const struct irq_bit_descr irqchip_flags[] = { ...@@ -57,6 +57,7 @@ static const struct irq_bit_descr irqchip_flags[] = {
BIT_MASK_DESCR(IRQCHIP_EOI_THREADED), BIT_MASK_DESCR(IRQCHIP_EOI_THREADED),
BIT_MASK_DESCR(IRQCHIP_SUPPORTS_LEVEL_MSI), BIT_MASK_DESCR(IRQCHIP_SUPPORTS_LEVEL_MSI),
BIT_MASK_DESCR(IRQCHIP_SUPPORTS_NMI), BIT_MASK_DESCR(IRQCHIP_SUPPORTS_NMI),
BIT_MASK_DESCR(IRQCHIP_ENABLE_WAKEUP_ON_SUSPEND),
}; };
static void static void
...@@ -125,6 +126,8 @@ static const struct irq_bit_descr irqdata_states[] = { ...@@ -125,6 +126,8 @@ static const struct irq_bit_descr irqdata_states[] = {
BIT_MASK_DESCR(IRQD_DEFAULT_TRIGGER_SET), BIT_MASK_DESCR(IRQD_DEFAULT_TRIGGER_SET),
BIT_MASK_DESCR(IRQD_HANDLE_ENFORCE_IRQCTX), BIT_MASK_DESCR(IRQD_HANDLE_ENFORCE_IRQCTX),
BIT_MASK_DESCR(IRQD_IRQ_ENABLED_ON_SUSPEND),
}; };
static const struct irq_bit_descr irqdesc_states[] = { static const struct irq_bit_descr irqdesc_states[] = {
......
...@@ -69,12 +69,26 @@ void irq_pm_remove_action(struct irq_desc *desc, struct irqaction *action) ...@@ -69,12 +69,26 @@ void irq_pm_remove_action(struct irq_desc *desc, struct irqaction *action)
static bool suspend_device_irq(struct irq_desc *desc) static bool suspend_device_irq(struct irq_desc *desc)
{ {
unsigned long chipflags = irq_desc_get_chip(desc)->flags;
struct irq_data *irqd = &desc->irq_data;
if (!desc->action || irq_desc_is_chained(desc) || if (!desc->action || irq_desc_is_chained(desc) ||
desc->no_suspend_depth) desc->no_suspend_depth)
return false; return false;
if (irqd_is_wakeup_set(&desc->irq_data)) { if (irqd_is_wakeup_set(irqd)) {
irqd_set(&desc->irq_data, IRQD_WAKEUP_ARMED); irqd_set(irqd, IRQD_WAKEUP_ARMED);
if ((chipflags & IRQCHIP_ENABLE_WAKEUP_ON_SUSPEND) &&
irqd_irq_disabled(irqd)) {
/*
* Interrupt marked for wakeup is in disabled state.
* Enable interrupt here to unmask/enable in irqchip
* to be able to resume with such interrupts.
*/
__enable_irq(desc);
irqd_set(irqd, IRQD_IRQ_ENABLED_ON_SUSPEND);
}
/* /*
* We return true here to force the caller to issue * We return true here to force the caller to issue
* synchronize_irq(). We need to make sure that the * synchronize_irq(). We need to make sure that the
...@@ -93,7 +107,7 @@ static bool suspend_device_irq(struct irq_desc *desc) ...@@ -93,7 +107,7 @@ static bool suspend_device_irq(struct irq_desc *desc)
* chip level. The chip implementation indicates that with * chip level. The chip implementation indicates that with
* IRQCHIP_MASK_ON_SUSPEND. * IRQCHIP_MASK_ON_SUSPEND.
*/ */
if (irq_desc_get_chip(desc)->flags & IRQCHIP_MASK_ON_SUSPEND) if (chipflags & IRQCHIP_MASK_ON_SUSPEND)
mask_irq(desc); mask_irq(desc);
return true; return true;
} }
...@@ -137,7 +151,19 @@ EXPORT_SYMBOL_GPL(suspend_device_irqs); ...@@ -137,7 +151,19 @@ EXPORT_SYMBOL_GPL(suspend_device_irqs);
static void resume_irq(struct irq_desc *desc) static void resume_irq(struct irq_desc *desc)
{ {
irqd_clear(&desc->irq_data, IRQD_WAKEUP_ARMED); struct irq_data *irqd = &desc->irq_data;
irqd_clear(irqd, IRQD_WAKEUP_ARMED);
if (irqd_is_enabled_on_suspend(irqd)) {
/*
* Interrupt marked for wakeup was enabled during suspend
* entry. Disable such interrupts to restore them back to
* original state.
*/
__disable_irq(desc);
irqd_clear(irqd, IRQD_IRQ_ENABLED_ON_SUSPEND);
}
if (desc->istate & IRQS_SUSPENDED) if (desc->istate & IRQS_SUSPENDED)
goto resume; goto resume;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment