diff options
| author | Tony Lindgren <tony@atomide.com> | 2016-12-05 19:38:16 -0500 |
|---|---|---|
| committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2017-01-12 05:39:31 -0500 |
| commit | 65f796837e0051e11334b0c075b153d5d3d4d2c5 (patch) | |
| tree | 5756ebad10bed166ec74b29e9c59903c8cc1747e /drivers/base/power | |
| parent | 3b198ddd5855a5073bac27571394963b87d26613 (diff) | |
PM / wakeirq: Fix dedicated wakeirq for drivers not using autosuspend
commit bed570307ed78f21b77cb04a1df781dee4a8f05a upstream.
I noticed some wakeirq flakeyness with consumer drivers not using
autosuspend. For drivers not using autosuspend, the wakeirq may never
get unmasked in rpm_suspend() because of irq desc->depth.
We are configuring dedicated wakeirqs to start with IRQ_NOAUTOEN as we
naturally don't want them running until rpm_suspend() is called.
However, when a consumer driver initially calls pm_runtime_get(), we
now wrongly start with disable_irq_nosync() call on the dedicated
wakeirq that is disabled to start with.
This causes desc->depth to toggle between 1 and 2 instead of the usual
0 and 1. This can prevent enable_irq() from unmasking the wakeirq as
that only happens at desc->depth 1.
This does not necessarily show up with drivers using autosuspend as
there is time for disable_irq_nosync() before rpm_suspend() gets called
after the autosuspend timeout.
Let's fix the issue by adding wirq->status that lazily gets set on
the first rpm_suspend(). We also need PM runtime core private functions
for dev_pm_enable_wake_irq_check() and dev_pm_disable_wake_irq_check()
so we can enable the dedicated wakeirq on the first rpm_suspend().
While at it, let's also fix the comments for dev_pm_enable_wake_irq()
and dev_pm_disable_wake_irq(). Those can still be used by the consumer
drivers as needed because the IRQ core manages the interrupt usecount
for us.
Fixes: 4990d4fe327b (PM / Wakeirq: Add automated device wake IRQ handling)
Signed-off-by: Tony Lindgren <tony@atomide.com>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/base/power')
| -rw-r--r-- | drivers/base/power/power.h | 19 | ||||
| -rw-r--r-- | drivers/base/power/runtime.c | 8 | ||||
| -rw-r--r-- | drivers/base/power/wakeirq.c | 76 |
3 files changed, 88 insertions, 15 deletions
diff --git a/drivers/base/power/power.h b/drivers/base/power/power.h index 50e30e7b059d..a84332aefc2d 100644 --- a/drivers/base/power/power.h +++ b/drivers/base/power/power.h | |||
| @@ -21,14 +21,22 @@ extern void pm_runtime_init(struct device *dev); | |||
| 21 | extern void pm_runtime_reinit(struct device *dev); | 21 | extern void pm_runtime_reinit(struct device *dev); |
| 22 | extern void pm_runtime_remove(struct device *dev); | 22 | extern void pm_runtime_remove(struct device *dev); |
| 23 | 23 | ||
| 24 | #define WAKE_IRQ_DEDICATED_ALLOCATED BIT(0) | ||
| 25 | #define WAKE_IRQ_DEDICATED_MANAGED BIT(1) | ||
| 26 | #define WAKE_IRQ_DEDICATED_MASK (WAKE_IRQ_DEDICATED_ALLOCATED | \ | ||
| 27 | WAKE_IRQ_DEDICATED_MANAGED) | ||
| 28 | |||
| 24 | struct wake_irq { | 29 | struct wake_irq { |
| 25 | struct device *dev; | 30 | struct device *dev; |
| 31 | unsigned int status; | ||
| 26 | int irq; | 32 | int irq; |
| 27 | bool dedicated_irq:1; | ||
| 28 | }; | 33 | }; |
| 29 | 34 | ||
| 30 | extern void dev_pm_arm_wake_irq(struct wake_irq *wirq); | 35 | extern void dev_pm_arm_wake_irq(struct wake_irq *wirq); |
| 31 | extern void dev_pm_disarm_wake_irq(struct wake_irq *wirq); | 36 | extern void dev_pm_disarm_wake_irq(struct wake_irq *wirq); |
| 37 | extern void dev_pm_enable_wake_irq_check(struct device *dev, | ||
| 38 | bool can_change_status); | ||
| 39 | extern void dev_pm_disable_wake_irq_check(struct device *dev); | ||
| 32 | 40 | ||
| 33 | #ifdef CONFIG_PM_SLEEP | 41 | #ifdef CONFIG_PM_SLEEP |
| 34 | 42 | ||
| @@ -104,6 +112,15 @@ static inline void dev_pm_disarm_wake_irq(struct wake_irq *wirq) | |||
| 104 | { | 112 | { |
| 105 | } | 113 | } |
| 106 | 114 | ||
| 115 | static inline void dev_pm_enable_wake_irq_check(struct device *dev, | ||
| 116 | bool can_change_status) | ||
| 117 | { | ||
| 118 | } | ||
| 119 | |||
| 120 | static inline void dev_pm_disable_wake_irq_check(struct device *dev) | ||
| 121 | { | ||
| 122 | } | ||
| 123 | |||
| 107 | #endif | 124 | #endif |
| 108 | 125 | ||
| 109 | #ifdef CONFIG_PM_SLEEP | 126 | #ifdef CONFIG_PM_SLEEP |
diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c index 82a081ea4317..23f3b95a1158 100644 --- a/drivers/base/power/runtime.c +++ b/drivers/base/power/runtime.c | |||
| @@ -515,7 +515,7 @@ static int rpm_suspend(struct device *dev, int rpmflags) | |||
| 515 | 515 | ||
| 516 | callback = RPM_GET_CALLBACK(dev, runtime_suspend); | 516 | callback = RPM_GET_CALLBACK(dev, runtime_suspend); |
| 517 | 517 | ||
| 518 | dev_pm_enable_wake_irq(dev); | 518 | dev_pm_enable_wake_irq_check(dev, true); |
| 519 | retval = rpm_callback(callback, dev); | 519 | retval = rpm_callback(callback, dev); |
| 520 | if (retval) | 520 | if (retval) |
| 521 | goto fail; | 521 | goto fail; |
| @@ -554,7 +554,7 @@ static int rpm_suspend(struct device *dev, int rpmflags) | |||
| 554 | return retval; | 554 | return retval; |
| 555 | 555 | ||
| 556 | fail: | 556 | fail: |
| 557 | dev_pm_disable_wake_irq(dev); | 557 | dev_pm_disable_wake_irq_check(dev); |
| 558 | __update_runtime_status(dev, RPM_ACTIVE); | 558 | __update_runtime_status(dev, RPM_ACTIVE); |
| 559 | dev->power.deferred_resume = false; | 559 | dev->power.deferred_resume = false; |
| 560 | wake_up_all(&dev->power.wait_queue); | 560 | wake_up_all(&dev->power.wait_queue); |
| @@ -737,12 +737,12 @@ static int rpm_resume(struct device *dev, int rpmflags) | |||
| 737 | 737 | ||
| 738 | callback = RPM_GET_CALLBACK(dev, runtime_resume); | 738 | callback = RPM_GET_CALLBACK(dev, runtime_resume); |
| 739 | 739 | ||
| 740 | dev_pm_disable_wake_irq(dev); | 740 | dev_pm_disable_wake_irq_check(dev); |
| 741 | retval = rpm_callback(callback, dev); | 741 | retval = rpm_callback(callback, dev); |
| 742 | if (retval) { | 742 | if (retval) { |
| 743 | __update_runtime_status(dev, RPM_SUSPENDED); | 743 | __update_runtime_status(dev, RPM_SUSPENDED); |
| 744 | pm_runtime_cancel_pending(dev); | 744 | pm_runtime_cancel_pending(dev); |
| 745 | dev_pm_enable_wake_irq(dev); | 745 | dev_pm_enable_wake_irq_check(dev, false); |
| 746 | } else { | 746 | } else { |
| 747 | no_callback: | 747 | no_callback: |
| 748 | __update_runtime_status(dev, RPM_ACTIVE); | 748 | __update_runtime_status(dev, RPM_ACTIVE); |
diff --git a/drivers/base/power/wakeirq.c b/drivers/base/power/wakeirq.c index 0d77cd6fd8d1..404d94c6c8bc 100644 --- a/drivers/base/power/wakeirq.c +++ b/drivers/base/power/wakeirq.c | |||
| @@ -110,8 +110,10 @@ void dev_pm_clear_wake_irq(struct device *dev) | |||
| 110 | dev->power.wakeirq = NULL; | 110 | dev->power.wakeirq = NULL; |
| 111 | spin_unlock_irqrestore(&dev->power.lock, flags); | 111 | spin_unlock_irqrestore(&dev->power.lock, flags); |
| 112 | 112 | ||
| 113 | if (wirq->dedicated_irq) | 113 | if (wirq->status & WAKE_IRQ_DEDICATED_ALLOCATED) { |
| 114 | free_irq(wirq->irq, wirq); | 114 | free_irq(wirq->irq, wirq); |
| 115 | wirq->status &= ~WAKE_IRQ_DEDICATED_MASK; | ||
| 116 | } | ||
| 115 | kfree(wirq); | 117 | kfree(wirq); |
| 116 | } | 118 | } |
| 117 | EXPORT_SYMBOL_GPL(dev_pm_clear_wake_irq); | 119 | EXPORT_SYMBOL_GPL(dev_pm_clear_wake_irq); |
| @@ -179,7 +181,6 @@ int dev_pm_set_dedicated_wake_irq(struct device *dev, int irq) | |||
| 179 | 181 | ||
| 180 | wirq->dev = dev; | 182 | wirq->dev = dev; |
| 181 | wirq->irq = irq; | 183 | wirq->irq = irq; |
| 182 | wirq->dedicated_irq = true; | ||
| 183 | irq_set_status_flags(irq, IRQ_NOAUTOEN); | 184 | irq_set_status_flags(irq, IRQ_NOAUTOEN); |
| 184 | 185 | ||
| 185 | /* | 186 | /* |
| @@ -195,6 +196,8 @@ int dev_pm_set_dedicated_wake_irq(struct device *dev, int irq) | |||
| 195 | if (err) | 196 | if (err) |
| 196 | goto err_free_irq; | 197 | goto err_free_irq; |
| 197 | 198 | ||
| 199 | wirq->status = WAKE_IRQ_DEDICATED_ALLOCATED; | ||
| 200 | |||
| 198 | return err; | 201 | return err; |
| 199 | 202 | ||
| 200 | err_free_irq: | 203 | err_free_irq: |
| @@ -210,9 +213,9 @@ EXPORT_SYMBOL_GPL(dev_pm_set_dedicated_wake_irq); | |||
| 210 | * dev_pm_enable_wake_irq - Enable device wake-up interrupt | 213 | * dev_pm_enable_wake_irq - Enable device wake-up interrupt |
| 211 | * @dev: Device | 214 | * @dev: Device |
| 212 | * | 215 | * |
| 213 | * Called from the bus code or the device driver for | 216 | * Optionally called from the bus code or the device driver for |
| 214 | * runtime_suspend() to enable the wake-up interrupt while | 217 | * runtime_resume() to override the PM runtime core managed wake-up |
| 215 | * the device is running. | 218 | * interrupt handling to enable the wake-up interrupt. |
| 216 | * | 219 | * |
| 217 | * Note that for runtime_suspend()) the wake-up interrupts | 220 | * Note that for runtime_suspend()) the wake-up interrupts |
| 218 | * should be unconditionally enabled unlike for suspend() | 221 | * should be unconditionally enabled unlike for suspend() |
| @@ -222,7 +225,7 @@ void dev_pm_enable_wake_irq(struct device *dev) | |||
| 222 | { | 225 | { |
| 223 | struct wake_irq *wirq = dev->power.wakeirq; | 226 | struct wake_irq *wirq = dev->power.wakeirq; |
| 224 | 227 | ||
| 225 | if (wirq && wirq->dedicated_irq) | 228 | if (wirq && (wirq->status & WAKE_IRQ_DEDICATED_ALLOCATED)) |
| 226 | enable_irq(wirq->irq); | 229 | enable_irq(wirq->irq); |
| 227 | } | 230 | } |
| 228 | EXPORT_SYMBOL_GPL(dev_pm_enable_wake_irq); | 231 | EXPORT_SYMBOL_GPL(dev_pm_enable_wake_irq); |
| @@ -231,20 +234,73 @@ EXPORT_SYMBOL_GPL(dev_pm_enable_wake_irq); | |||
| 231 | * dev_pm_disable_wake_irq - Disable device wake-up interrupt | 234 | * dev_pm_disable_wake_irq - Disable device wake-up interrupt |
| 232 | * @dev: Device | 235 | * @dev: Device |
| 233 | * | 236 | * |
| 234 | * Called from the bus code or the device driver for | 237 | * Optionally called from the bus code or the device driver for |
| 235 | * runtime_resume() to disable the wake-up interrupt while | 238 | * runtime_suspend() to override the PM runtime core managed wake-up |
| 236 | * the device is running. | 239 | * interrupt handling to disable the wake-up interrupt. |
| 237 | */ | 240 | */ |
| 238 | void dev_pm_disable_wake_irq(struct device *dev) | 241 | void dev_pm_disable_wake_irq(struct device *dev) |
| 239 | { | 242 | { |
| 240 | struct wake_irq *wirq = dev->power.wakeirq; | 243 | struct wake_irq *wirq = dev->power.wakeirq; |
| 241 | 244 | ||
| 242 | if (wirq && wirq->dedicated_irq) | 245 | if (wirq && (wirq->status & WAKE_IRQ_DEDICATED_ALLOCATED)) |
| 243 | disable_irq_nosync(wirq->irq); | 246 | disable_irq_nosync(wirq->irq); |
| 244 | } | 247 | } |
| 245 | EXPORT_SYMBOL_GPL(dev_pm_disable_wake_irq); | 248 | EXPORT_SYMBOL_GPL(dev_pm_disable_wake_irq); |
| 246 | 249 | ||
| 247 | /** | 250 | /** |
| 251 | * dev_pm_enable_wake_irq_check - Checks and enables wake-up interrupt | ||
| 252 | * @dev: Device | ||
| 253 | * @can_change_status: Can change wake-up interrupt status | ||
| 254 | * | ||
| 255 | * Enables wakeirq conditionally. We need to enable wake-up interrupt | ||
| 256 | * lazily on the first rpm_suspend(). This is needed as the consumer device | ||
| 257 | * starts in RPM_SUSPENDED state, and the the first pm_runtime_get() would | ||
| 258 | * otherwise try to disable already disabled wakeirq. The wake-up interrupt | ||
| 259 | * starts disabled with IRQ_NOAUTOEN set. | ||
| 260 | * | ||
| 261 | * Should be only called from rpm_suspend() and rpm_resume() path. | ||
| 262 | * Caller must hold &dev->power.lock to change wirq->status | ||
| 263 | */ | ||
| 264 | void dev_pm_enable_wake_irq_check(struct device *dev, | ||
| 265 | bool can_change_status) | ||
| 266 | { | ||
| 267 | struct wake_irq *wirq = dev->power.wakeirq; | ||
| 268 | |||
| 269 | if (!wirq || !((wirq->status & WAKE_IRQ_DEDICATED_MASK))) | ||
| 270 | return; | ||
| 271 | |||
| 272 | if (likely(wirq->status & WAKE_IRQ_DEDICATED_MANAGED)) { | ||
| 273 | goto enable; | ||
| 274 | } else if (can_change_status) { | ||
| 275 | wirq->status |= WAKE_IRQ_DEDICATED_MANAGED; | ||
| 276 | goto enable; | ||
| 277 | } | ||
| 278 | |||
| 279 | return; | ||
| 280 | |||
| 281 | enable: | ||
| 282 | enable_irq(wirq->irq); | ||
| 283 | } | ||
| 284 | |||
| 285 | /** | ||
| 286 | * dev_pm_disable_wake_irq_check - Checks and disables wake-up interrupt | ||
| 287 | * @dev: Device | ||
| 288 | * | ||
| 289 | * Disables wake-up interrupt conditionally based on status. | ||
| 290 | * Should be only called from rpm_suspend() and rpm_resume() path. | ||
| 291 | */ | ||
| 292 | void dev_pm_disable_wake_irq_check(struct device *dev) | ||
| 293 | { | ||
| 294 | struct wake_irq *wirq = dev->power.wakeirq; | ||
| 295 | |||
| 296 | if (!wirq || !((wirq->status & WAKE_IRQ_DEDICATED_MASK))) | ||
| 297 | return; | ||
| 298 | |||
| 299 | if (wirq->status & WAKE_IRQ_DEDICATED_MANAGED) | ||
| 300 | disable_irq_nosync(wirq->irq); | ||
| 301 | } | ||
| 302 | |||
| 303 | /** | ||
| 248 | * dev_pm_arm_wake_irq - Arm device wake-up | 304 | * dev_pm_arm_wake_irq - Arm device wake-up |
| 249 | * @wirq: Device wake-up interrupt | 305 | * @wirq: Device wake-up interrupt |
| 250 | * | 306 | * |
