diff options
-rw-r--r-- | drivers/base/power/qos.c | 24 | ||||
-rw-r--r-- | drivers/base/power/runtime.c | 148 | ||||
-rw-r--r-- | include/linux/pm.h | 2 | ||||
-rw-r--r-- | include/linux/pm_qos.h | 3 | ||||
-rw-r--r-- | include/linux/pm_runtime.h | 5 |
5 files changed, 154 insertions, 28 deletions
diff --git a/drivers/base/power/qos.c b/drivers/base/power/qos.c index 86de6c50fc41..03f4bd069ca8 100644 --- a/drivers/base/power/qos.c +++ b/drivers/base/power/qos.c | |||
@@ -47,21 +47,29 @@ static DEFINE_MUTEX(dev_pm_qos_mtx); | |||
47 | static BLOCKING_NOTIFIER_HEAD(dev_pm_notifiers); | 47 | static BLOCKING_NOTIFIER_HEAD(dev_pm_notifiers); |
48 | 48 | ||
49 | /** | 49 | /** |
50 | * dev_pm_qos_read_value - Get PM QoS constraint for a given device. | 50 | * __dev_pm_qos_read_value - Get PM QoS constraint for a given device. |
51 | * @dev: Device to get the PM QoS constraint value for. | ||
52 | * | ||
53 | * This routine must be called with dev->power.lock held. | ||
54 | */ | ||
55 | s32 __dev_pm_qos_read_value(struct device *dev) | ||
56 | { | ||
57 | struct pm_qos_constraints *c = dev->power.constraints; | ||
58 | |||
59 | return c ? pm_qos_read_value(c) : 0; | ||
60 | } | ||
61 | |||
62 | /** | ||
63 | * dev_pm_qos_read_value - Get PM QoS constraint for a given device (locked). | ||
51 | * @dev: Device to get the PM QoS constraint value for. | 64 | * @dev: Device to get the PM QoS constraint value for. |
52 | */ | 65 | */ |
53 | s32 dev_pm_qos_read_value(struct device *dev) | 66 | s32 dev_pm_qos_read_value(struct device *dev) |
54 | { | 67 | { |
55 | struct pm_qos_constraints *c; | ||
56 | unsigned long flags; | 68 | unsigned long flags; |
57 | s32 ret = 0; | 69 | s32 ret; |
58 | 70 | ||
59 | spin_lock_irqsave(&dev->power.lock, flags); | 71 | spin_lock_irqsave(&dev->power.lock, flags); |
60 | 72 | ret = __dev_pm_qos_read_value(dev); | |
61 | c = dev->power.constraints; | ||
62 | if (c) | ||
63 | ret = pm_qos_read_value(c); | ||
64 | |||
65 | spin_unlock_irqrestore(&dev->power.lock, flags); | 73 | spin_unlock_irqrestore(&dev->power.lock, flags); |
66 | 74 | ||
67 | return ret; | 75 | return ret; |
diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c index 8c78443bca8f..068f7ed1f009 100644 --- a/drivers/base/power/runtime.c +++ b/drivers/base/power/runtime.c | |||
@@ -279,6 +279,47 @@ static int rpm_callback(int (*cb)(struct device *), struct device *dev) | |||
279 | return retval != -EACCES ? retval : -EIO; | 279 | return retval != -EACCES ? retval : -EIO; |
280 | } | 280 | } |
281 | 281 | ||
282 | struct rpm_qos_data { | ||
283 | ktime_t time_now; | ||
284 | s64 constraint_ns; | ||
285 | }; | ||
286 | |||
287 | /** | ||
288 | * rpm_update_qos_constraint - Update a given PM QoS constraint data. | ||
289 | * @dev: Device whose timing data to use. | ||
290 | * @data: PM QoS constraint data to update. | ||
291 | * | ||
292 | * Use the suspend timing data of @dev to update PM QoS constraint data pointed | ||
293 | * to by @data. | ||
294 | */ | ||
295 | static int rpm_update_qos_constraint(struct device *dev, void *data) | ||
296 | { | ||
297 | struct rpm_qos_data *qos = data; | ||
298 | unsigned long flags; | ||
299 | s64 delta_ns; | ||
300 | int ret = 0; | ||
301 | |||
302 | spin_lock_irqsave(&dev->power.lock, flags); | ||
303 | |||
304 | if (dev->power.max_time_suspended_ns < 0) | ||
305 | goto out; | ||
306 | |||
307 | delta_ns = dev->power.max_time_suspended_ns - | ||
308 | ktime_to_ns(ktime_sub(qos->time_now, dev->power.suspend_time)); | ||
309 | if (delta_ns <= 0) { | ||
310 | ret = -EBUSY; | ||
311 | goto out; | ||
312 | } | ||
313 | |||
314 | if (qos->constraint_ns > delta_ns || qos->constraint_ns == 0) | ||
315 | qos->constraint_ns = delta_ns; | ||
316 | |||
317 | out: | ||
318 | spin_unlock_irqrestore(&dev->power.lock, flags); | ||
319 | |||
320 | return ret; | ||
321 | } | ||
322 | |||
282 | /** | 323 | /** |
283 | * rpm_suspend - Carry out runtime suspend of given device. | 324 | * rpm_suspend - Carry out runtime suspend of given device. |
284 | * @dev: Device to suspend. | 325 | * @dev: Device to suspend. |
@@ -305,6 +346,7 @@ static int rpm_suspend(struct device *dev, int rpmflags) | |||
305 | { | 346 | { |
306 | int (*callback)(struct device *); | 347 | int (*callback)(struct device *); |
307 | struct device *parent = NULL; | 348 | struct device *parent = NULL; |
349 | struct rpm_qos_data qos; | ||
308 | int retval; | 350 | int retval; |
309 | 351 | ||
310 | trace_rpm_suspend(dev, rpmflags); | 352 | trace_rpm_suspend(dev, rpmflags); |
@@ -400,8 +442,38 @@ static int rpm_suspend(struct device *dev, int rpmflags) | |||
400 | goto out; | 442 | goto out; |
401 | } | 443 | } |
402 | 444 | ||
445 | qos.constraint_ns = __dev_pm_qos_read_value(dev); | ||
446 | if (qos.constraint_ns < 0) { | ||
447 | /* Negative constraint means "never suspend". */ | ||
448 | retval = -EPERM; | ||
449 | goto out; | ||
450 | } | ||
451 | qos.constraint_ns *= NSEC_PER_USEC; | ||
452 | qos.time_now = ktime_get(); | ||
453 | |||
403 | __update_runtime_status(dev, RPM_SUSPENDING); | 454 | __update_runtime_status(dev, RPM_SUSPENDING); |
404 | 455 | ||
456 | if (!dev->power.ignore_children) { | ||
457 | if (dev->power.irq_safe) | ||
458 | spin_unlock(&dev->power.lock); | ||
459 | else | ||
460 | spin_unlock_irq(&dev->power.lock); | ||
461 | |||
462 | retval = device_for_each_child(dev, &qos, | ||
463 | rpm_update_qos_constraint); | ||
464 | |||
465 | if (dev->power.irq_safe) | ||
466 | spin_lock(&dev->power.lock); | ||
467 | else | ||
468 | spin_lock_irq(&dev->power.lock); | ||
469 | |||
470 | if (retval) | ||
471 | goto fail; | ||
472 | } | ||
473 | |||
474 | dev->power.suspend_time = qos.time_now; | ||
475 | dev->power.max_time_suspended_ns = qos.constraint_ns ? : -1; | ||
476 | |||
405 | if (dev->pm_domain) | 477 | if (dev->pm_domain) |
406 | callback = dev->pm_domain->ops.runtime_suspend; | 478 | callback = dev->pm_domain->ops.runtime_suspend; |
407 | else if (dev->type && dev->type->pm) | 479 | else if (dev->type && dev->type->pm) |
@@ -414,27 +486,9 @@ static int rpm_suspend(struct device *dev, int rpmflags) | |||
414 | callback = NULL; | 486 | callback = NULL; |
415 | 487 | ||
416 | retval = rpm_callback(callback, dev); | 488 | retval = rpm_callback(callback, dev); |
417 | if (retval) { | 489 | if (retval) |
418 | __update_runtime_status(dev, RPM_ACTIVE); | 490 | goto fail; |
419 | dev->power.deferred_resume = false; | ||
420 | if (retval == -EAGAIN || retval == -EBUSY) { | ||
421 | dev->power.runtime_error = 0; | ||
422 | 491 | ||
423 | /* | ||
424 | * If the callback routine failed an autosuspend, and | ||
425 | * if the last_busy time has been updated so that there | ||
426 | * is a new autosuspend expiration time, automatically | ||
427 | * reschedule another autosuspend. | ||
428 | */ | ||
429 | if ((rpmflags & RPM_AUTO) && | ||
430 | pm_runtime_autosuspend_expiration(dev) != 0) | ||
431 | goto repeat; | ||
432 | } else { | ||
433 | pm_runtime_cancel_pending(dev); | ||
434 | } | ||
435 | wake_up_all(&dev->power.wait_queue); | ||
436 | goto out; | ||
437 | } | ||
438 | no_callback: | 492 | no_callback: |
439 | __update_runtime_status(dev, RPM_SUSPENDED); | 493 | __update_runtime_status(dev, RPM_SUSPENDED); |
440 | pm_runtime_deactivate_timer(dev); | 494 | pm_runtime_deactivate_timer(dev); |
@@ -466,6 +520,29 @@ static int rpm_suspend(struct device *dev, int rpmflags) | |||
466 | trace_rpm_return_int(dev, _THIS_IP_, retval); | 520 | trace_rpm_return_int(dev, _THIS_IP_, retval); |
467 | 521 | ||
468 | return retval; | 522 | return retval; |
523 | |||
524 | fail: | ||
525 | __update_runtime_status(dev, RPM_ACTIVE); | ||
526 | dev->power.suspend_time = ktime_set(0, 0); | ||
527 | dev->power.max_time_suspended_ns = -1; | ||
528 | dev->power.deferred_resume = false; | ||
529 | if (retval == -EAGAIN || retval == -EBUSY) { | ||
530 | dev->power.runtime_error = 0; | ||
531 | |||
532 | /* | ||
533 | * If the callback routine failed an autosuspend, and | ||
534 | * if the last_busy time has been updated so that there | ||
535 | * is a new autosuspend expiration time, automatically | ||
536 | * reschedule another autosuspend. | ||
537 | */ | ||
538 | if ((rpmflags & RPM_AUTO) && | ||
539 | pm_runtime_autosuspend_expiration(dev) != 0) | ||
540 | goto repeat; | ||
541 | } else { | ||
542 | pm_runtime_cancel_pending(dev); | ||
543 | } | ||
544 | wake_up_all(&dev->power.wait_queue); | ||
545 | goto out; | ||
469 | } | 546 | } |
470 | 547 | ||
471 | /** | 548 | /** |
@@ -620,6 +697,9 @@ static int rpm_resume(struct device *dev, int rpmflags) | |||
620 | if (dev->power.no_callbacks) | 697 | if (dev->power.no_callbacks) |
621 | goto no_callback; /* Assume success. */ | 698 | goto no_callback; /* Assume success. */ |
622 | 699 | ||
700 | dev->power.suspend_time = ktime_set(0, 0); | ||
701 | dev->power.max_time_suspended_ns = -1; | ||
702 | |||
623 | __update_runtime_status(dev, RPM_RESUMING); | 703 | __update_runtime_status(dev, RPM_RESUMING); |
624 | 704 | ||
625 | if (dev->pm_domain) | 705 | if (dev->pm_domain) |
@@ -1279,6 +1359,9 @@ void pm_runtime_init(struct device *dev) | |||
1279 | setup_timer(&dev->power.suspend_timer, pm_suspend_timer_fn, | 1359 | setup_timer(&dev->power.suspend_timer, pm_suspend_timer_fn, |
1280 | (unsigned long)dev); | 1360 | (unsigned long)dev); |
1281 | 1361 | ||
1362 | dev->power.suspend_time = ktime_set(0, 0); | ||
1363 | dev->power.max_time_suspended_ns = -1; | ||
1364 | |||
1282 | init_waitqueue_head(&dev->power.wait_queue); | 1365 | init_waitqueue_head(&dev->power.wait_queue); |
1283 | } | 1366 | } |
1284 | 1367 | ||
@@ -1296,3 +1379,28 @@ void pm_runtime_remove(struct device *dev) | |||
1296 | if (dev->power.irq_safe && dev->parent) | 1379 | if (dev->power.irq_safe && dev->parent) |
1297 | pm_runtime_put_sync(dev->parent); | 1380 | pm_runtime_put_sync(dev->parent); |
1298 | } | 1381 | } |
1382 | |||
1383 | /** | ||
1384 | * pm_runtime_update_max_time_suspended - Update device's suspend time data. | ||
1385 | * @dev: Device to handle. | ||
1386 | * @delta_ns: Value to subtract from the device's max_time_suspended_ns field. | ||
1387 | * | ||
1388 | * Update the device's power.max_time_suspended_ns field by subtracting | ||
1389 | * @delta_ns from it. The resulting value of power.max_time_suspended_ns is | ||
1390 | * never negative. | ||
1391 | */ | ||
1392 | void pm_runtime_update_max_time_suspended(struct device *dev, s64 delta_ns) | ||
1393 | { | ||
1394 | unsigned long flags; | ||
1395 | |||
1396 | spin_lock_irqsave(&dev->power.lock, flags); | ||
1397 | |||
1398 | if (delta_ns > 0 && dev->power.max_time_suspended_ns > 0) { | ||
1399 | if (dev->power.max_time_suspended_ns > delta_ns) | ||
1400 | dev->power.max_time_suspended_ns -= delta_ns; | ||
1401 | else | ||
1402 | dev->power.max_time_suspended_ns = 0; | ||
1403 | } | ||
1404 | |||
1405 | spin_unlock_irqrestore(&dev->power.lock, flags); | ||
1406 | } | ||
diff --git a/include/linux/pm.h b/include/linux/pm.h index 3f3ed83a9aa5..a7676efa6831 100644 --- a/include/linux/pm.h +++ b/include/linux/pm.h | |||
@@ -521,6 +521,8 @@ struct dev_pm_info { | |||
521 | unsigned long active_jiffies; | 521 | unsigned long active_jiffies; |
522 | unsigned long suspended_jiffies; | 522 | unsigned long suspended_jiffies; |
523 | unsigned long accounting_timestamp; | 523 | unsigned long accounting_timestamp; |
524 | ktime_t suspend_time; | ||
525 | s64 max_time_suspended_ns; | ||
524 | #endif | 526 | #endif |
525 | struct pm_subsys_data *subsys_data; /* Owned by the subsystem. */ | 527 | struct pm_subsys_data *subsys_data; /* Owned by the subsystem. */ |
526 | struct pm_qos_constraints *constraints; | 528 | struct pm_qos_constraints *constraints; |
diff --git a/include/linux/pm_qos.h b/include/linux/pm_qos.h index 83b0ea302a80..775a3236343d 100644 --- a/include/linux/pm_qos.h +++ b/include/linux/pm_qos.h | |||
@@ -78,6 +78,7 @@ int pm_qos_remove_notifier(int pm_qos_class, struct notifier_block *notifier); | |||
78 | int pm_qos_request_active(struct pm_qos_request *req); | 78 | int pm_qos_request_active(struct pm_qos_request *req); |
79 | s32 pm_qos_read_value(struct pm_qos_constraints *c); | 79 | s32 pm_qos_read_value(struct pm_qos_constraints *c); |
80 | 80 | ||
81 | s32 __dev_pm_qos_read_value(struct device *dev); | ||
81 | s32 dev_pm_qos_read_value(struct device *dev); | 82 | s32 dev_pm_qos_read_value(struct device *dev); |
82 | int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req, | 83 | int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req, |
83 | s32 value); | 84 | s32 value); |
@@ -119,6 +120,8 @@ static inline int pm_qos_request_active(struct pm_qos_request *req) | |||
119 | static inline s32 pm_qos_read_value(struct pm_qos_constraints *c) | 120 | static inline s32 pm_qos_read_value(struct pm_qos_constraints *c) |
120 | { return 0; } | 121 | { return 0; } |
121 | 122 | ||
123 | static inline s32 __dev_pm_qos_read_value(struct device *dev) | ||
124 | { return 0; } | ||
122 | static inline s32 dev_pm_qos_read_value(struct device *dev) | 125 | static inline s32 dev_pm_qos_read_value(struct device *dev) |
123 | { return 0; } | 126 | { return 0; } |
124 | static inline int dev_pm_qos_add_request(struct device *dev, | 127 | static inline int dev_pm_qos_add_request(struct device *dev, |
diff --git a/include/linux/pm_runtime.h b/include/linux/pm_runtime.h index d3085e72a0ee..609daae7a014 100644 --- a/include/linux/pm_runtime.h +++ b/include/linux/pm_runtime.h | |||
@@ -45,6 +45,8 @@ extern void pm_runtime_irq_safe(struct device *dev); | |||
45 | extern void __pm_runtime_use_autosuspend(struct device *dev, bool use); | 45 | extern void __pm_runtime_use_autosuspend(struct device *dev, bool use); |
46 | extern void pm_runtime_set_autosuspend_delay(struct device *dev, int delay); | 46 | extern void pm_runtime_set_autosuspend_delay(struct device *dev, int delay); |
47 | extern unsigned long pm_runtime_autosuspend_expiration(struct device *dev); | 47 | extern unsigned long pm_runtime_autosuspend_expiration(struct device *dev); |
48 | extern void pm_runtime_update_max_time_suspended(struct device *dev, | ||
49 | s64 delta_ns); | ||
48 | 50 | ||
49 | static inline bool pm_children_suspended(struct device *dev) | 51 | static inline bool pm_children_suspended(struct device *dev) |
50 | { | 52 | { |
@@ -148,6 +150,9 @@ static inline void pm_runtime_set_autosuspend_delay(struct device *dev, | |||
148 | static inline unsigned long pm_runtime_autosuspend_expiration( | 150 | static inline unsigned long pm_runtime_autosuspend_expiration( |
149 | struct device *dev) { return 0; } | 151 | struct device *dev) { return 0; } |
150 | 152 | ||
153 | static inline void pm_runtime_update_max_time_suspended(struct device *dev, | ||
154 | s64 delta_ns) {} | ||
155 | |||
151 | #endif /* !CONFIG_PM_RUNTIME */ | 156 | #endif /* !CONFIG_PM_RUNTIME */ |
152 | 157 | ||
153 | static inline int pm_runtime_idle(struct device *dev) | 158 | static inline int pm_runtime_idle(struct device *dev) |