===================================================================
@@ -101,8 +101,8 @@ typedef irqreturn_t (*irq_handler_t)(int
* @thread_flags: flags related to @thread
* @thread_mask: bitmask for keeping track of @thread activity
* @dir: pointer to the proc/irq/NN/name entry
- * @s_handler: original interrupt handler for suspend mode interrupts
- * @s_dev_id: original device identification cookie for suspend mode
+ * @s_handler: original interrupt handler for suspend/wakeup mode interrupts
+ * @s_dev_id: original device identification cookie for suspend/wakeup mode
* @prev_ret: suspend mode return code from the previous action
*/
struct irqaction {
@@ -201,6 +201,7 @@ extern void irq_wake_thread(unsigned int
/* The following three functions are for the core kernel use only. */
extern void suspend_device_irqs(void);
extern void resume_device_irqs(void);
+extern void wakeup_mode_for_irqs(bool enable);
#ifdef CONFIG_PM_SLEEP
extern int check_wakeup_irqs(void);
#else
===================================================================
@@ -28,6 +28,7 @@
#include <linux/ftrace.h>
#include <trace/events/power.h>
#include <linux/compiler.h>
+#include <linux/interrupt.h>
#include "power.h"
@@ -55,7 +56,9 @@ static void freeze_enter(void)
{
cpuidle_use_deepest_state(true);
cpuidle_resume();
+ wakeup_mode_for_irqs(true);
wait_event(suspend_freeze_wait_head, suspend_freeze_wake);
+ wakeup_mode_for_irqs(false);
cpuidle_pause();
cpuidle_use_deepest_state(false);
}
===================================================================
@@ -14,6 +14,17 @@
#include "internals.h"
+static void irq_pm_replace_handler(struct irqaction *action,
+ irq_handler_t new_handler)
+{
+ if (!action->s_handler) {
+ action->s_handler = action->handler;
+ action->handler = new_handler;
+ action->s_dev_id = action->dev_id;
+ action->dev_id = action;
+ }
+}
+
static void irq_pm_restore_handler(struct irqaction *action)
{
if (action->s_handler) {
@@ -24,14 +35,36 @@ static void irq_pm_restore_handler(struc
}
}
-static void irq_pm_disable_and_wakeup(int irq)
+static void irq_pm_disable_and_wakeup(struct irq_desc *desc)
{
- struct irq_desc *desc = irq_to_desc(irq);
+ if (!(desc->istate & IRQS_SUSPENDED)) {
+ desc->istate |= IRQS_SUSPENDED;
+ desc->depth++;
+ irq_disable(desc);
+ pm_system_wakeup();
+ }
+}
+
+static irqreturn_t irq_wakeup_mode_handler(int irq, void *dev_id)
+{
+ struct irqaction *action = dev_id;
+ struct irq_desc *desc;
+
+ if (action->next)
+ return IRQ_NONE;
+
+ desc = irq_to_desc(irq);
+ desc->istate |= IRQS_PENDING;
+ irq_pm_disable_and_wakeup(desc);
+ return IRQ_HANDLED;
+}
+
+static void irq_pm_wakeup_mode(struct irq_desc *desc)
+{
+ struct irqaction *action;
- desc->istate |= IRQS_SUSPENDED;
- desc->depth++;
- irq_disable(desc);
- pm_system_wakeup();
+ for (action = desc->action; action; action = action->next)
+ irq_pm_replace_handler(action, irq_wakeup_mode_handler);
}
static irqreturn_t irq_suspend_mode_handler(int irq, void *dev_id)
@@ -52,7 +85,7 @@ static irqreturn_t irq_suspend_mode_hand
* trigger wakeup.
*/
pr_err("IRQ %d: Unhandled while suspended\n", irq);
- irq_pm_disable_and_wakeup(irq);
+ irq_pm_disable_and_wakeup(irq_to_desc(irq));
}
return ret;
}
@@ -69,10 +102,7 @@ bool irq_pm_suspend_mode(struct irq_desc
return true;
for (action = desc->action; action; action = action->next) {
- action->s_handler = action->handler;
- action->handler = irq_suspend_mode_handler;
- action->s_dev_id = action->dev_id;
- action->dev_id = action;
+ irq_pm_replace_handler(action, irq_suspend_mode_handler);
action->prev_ret = IRQ_NONE;
}
return true;
@@ -213,3 +243,35 @@ int check_wakeup_irqs(void)
return 0;
}
+
+void wakeup_mode_for_irqs(bool enable)
+{
+ struct irq_desc *desc;
+ int irq;
+
+ for_each_irq_desc(irq, desc) {
+ struct irqaction *action = desc->action;
+ unsigned long flags;
+
+ raw_spin_lock_irqsave(&desc->lock, flags);
+
+ if (action && irqd_is_wakeup_set(&desc->irq_data) &&
+ !desc->skip_suspend_depth) {
+ if (enable) {
+ irq_pm_wakeup_mode(desc);
+ if (desc->istate & IRQS_SUSPENDED) {
+ desc->istate &= ~IRQS_SUSPENDED;
+ __enable_irq(desc, irq, false);
+ }
+ } else {
+ if (!(desc->istate & IRQS_SUSPENDED)) {
+ __disable_irq(desc, irq, false);
+ desc->istate |= IRQS_SUSPENDED;
+ }
+ irq_pm_normal_mode(desc);
+ }
+ }
+
+ raw_spin_unlock_irqrestore(&desc->lock, flags);
+ }
+}