aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/base/power
diff options
context:
space:
mode:
authorTony Lindgren <tony@atomide.com>2016-12-05 19:38:16 -0500
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2017-01-12 05:39:31 -0500
commit65f796837e0051e11334b0c075b153d5d3d4d2c5 (patch)
tree5756ebad10bed166ec74b29e9c59903c8cc1747e /drivers/base/power
parent3b198ddd5855a5073bac27571394963b87d26613 (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.h19
-rw-r--r--drivers/base/power/runtime.c8
-rw-r--r--drivers/base/power/wakeirq.c76
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);
21extern void pm_runtime_reinit(struct device *dev); 21extern void pm_runtime_reinit(struct device *dev);
22extern void pm_runtime_remove(struct device *dev); 22extern 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
24struct wake_irq { 29struct 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
30extern void dev_pm_arm_wake_irq(struct wake_irq *wirq); 35extern void dev_pm_arm_wake_irq(struct wake_irq *wirq);
31extern void dev_pm_disarm_wake_irq(struct wake_irq *wirq); 36extern void dev_pm_disarm_wake_irq(struct wake_irq *wirq);
37extern void dev_pm_enable_wake_irq_check(struct device *dev,
38 bool can_change_status);
39extern 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
115static inline void dev_pm_enable_wake_irq_check(struct device *dev,
116 bool can_change_status)
117{
118}
119
120static 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}
117EXPORT_SYMBOL_GPL(dev_pm_clear_wake_irq); 119EXPORT_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
200err_free_irq: 203err_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}
228EXPORT_SYMBOL_GPL(dev_pm_enable_wake_irq); 231EXPORT_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 */
238void dev_pm_disable_wake_irq(struct device *dev) 241void 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}
245EXPORT_SYMBOL_GPL(dev_pm_disable_wake_irq); 248EXPORT_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 */
264void 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
281enable:
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 */
292void 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 *