PM: sleep: wakeirq: fix wake irq arming
authorJohan Hovold <johan+linaro@kernel.org>
Thu, 13 Jul 2023 14:57:39 +0000 (16:57 +0200)
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>
Mon, 24 Jul 2023 07:51:51 +0000 (09:51 +0200)
The decision whether to enable a wake irq during suspend can not be done
based on the runtime PM state directly as a driver may use wake irqs
without implementing runtime PM. Such drivers specifically leave the
state set to the default 'suspended' and the wake irq is thus never
enabled at suspend.

Add a new wake irq flag to track whether a dedicated wake irq has been
enabled at runtime suspend and therefore must not be enabled at system
suspend.

Note that pm_runtime_enabled() can not be used as runtime PM is always
disabled during late suspend.

Fixes: 69728051f5bf ("PM / wakeirq: Fix unbalanced IRQ enable for wakeirq")
Cc: 4.16+ <stable@vger.kernel.org> # 4.16+
Signed-off-by: Johan Hovold <johan+linaro@kernel.org>
Reviewed-by: Tony Lindgren <tony@atomide.com>
Tested-by: Tony Lindgren <tony@atomide.com>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
drivers/base/power/power.h
drivers/base/power/wakeirq.c

index 0eb7f02b3ad59218e3f4212de51783cab711aeed..922ed457db191249f2c0223a822bc76e27f16d22 100644 (file)
@@ -29,6 +29,7 @@ extern u64 pm_runtime_active_time(struct device *dev);
 #define WAKE_IRQ_DEDICATED_MASK                (WAKE_IRQ_DEDICATED_ALLOCATED | \
                                         WAKE_IRQ_DEDICATED_MANAGED | \
                                         WAKE_IRQ_DEDICATED_REVERSE)
+#define WAKE_IRQ_DEDICATED_ENABLED     BIT(3)
 
 struct wake_irq {
        struct device *dev;
index d487a6bac630f062e48a0f5b51b092e4a87b17eb..afd094dec5ca387505e517ba88847c06e3d53b5c 100644 (file)
@@ -314,8 +314,10 @@ void dev_pm_enable_wake_irq_check(struct device *dev,
        return;
 
 enable:
-       if (!can_change_status || !(wirq->status & WAKE_IRQ_DEDICATED_REVERSE))
+       if (!can_change_status || !(wirq->status & WAKE_IRQ_DEDICATED_REVERSE)) {
                enable_irq(wirq->irq);
+               wirq->status |= WAKE_IRQ_DEDICATED_ENABLED;
+       }
 }
 
 /**
@@ -336,8 +338,10 @@ void dev_pm_disable_wake_irq_check(struct device *dev, bool cond_disable)
        if (cond_disable && (wirq->status & WAKE_IRQ_DEDICATED_REVERSE))
                return;
 
-       if (wirq->status & WAKE_IRQ_DEDICATED_MANAGED)
+       if (wirq->status & WAKE_IRQ_DEDICATED_MANAGED) {
+               wirq->status &= ~WAKE_IRQ_DEDICATED_ENABLED;
                disable_irq_nosync(wirq->irq);
+       }
 }
 
 /**
@@ -376,7 +380,7 @@ void dev_pm_arm_wake_irq(struct wake_irq *wirq)
 
        if (device_may_wakeup(wirq->dev)) {
                if (wirq->status & WAKE_IRQ_DEDICATED_ALLOCATED &&
-                   !pm_runtime_status_suspended(wirq->dev))
+                   !(wirq->status & WAKE_IRQ_DEDICATED_ENABLED))
                        enable_irq(wirq->irq);
 
                enable_irq_wake(wirq->irq);
@@ -399,7 +403,7 @@ void dev_pm_disarm_wake_irq(struct wake_irq *wirq)
                disable_irq_wake(wirq->irq);
 
                if (wirq->status & WAKE_IRQ_DEDICATED_ALLOCATED &&
-                   !pm_runtime_status_suspended(wirq->dev))
+                   !(wirq->status & WAKE_IRQ_DEDICATED_ENABLED))
                        disable_irq_nosync(wirq->irq);
        }
 }