diff options
| author | Rafael J. Wysocki <rjw@sisk.pl> | 2011-12-25 17:43:05 -0500 |
|---|---|---|
| committer | Rafael J. Wysocki <rjw@sisk.pl> | 2011-12-25 17:43:05 -0500 |
| commit | 0015afaa1f818d38ea9f8e81a84a6aeeca5fdaf0 (patch) | |
| tree | 8f8279cf0117d210230ef9fcacb05f960bf6f8f5 | |
| parent | b7ba68c4a072c9aa8f04b8cf7838b6cd2f48d918 (diff) | |
| parent | 00dc9ad18d707f36b2fb4af98fd2cf0548d2b258 (diff) | |
Merge branch 'pm-runtime' into pm-for-linus
* pm-runtime:
PM / Runtime: Use device PM QoS constraints (v2)
| -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 c56efd756531..541f821d4ea6 100644 --- a/drivers/base/power/runtime.c +++ b/drivers/base/power/runtime.c | |||
| @@ -282,6 +282,47 @@ static int rpm_callback(int (*cb)(struct device *), struct device *dev) | |||
| 282 | return retval != -EACCES ? retval : -EIO; | 282 | return retval != -EACCES ? retval : -EIO; |
| 283 | } | 283 | } |
| 284 | 284 | ||
| 285 | struct rpm_qos_data { | ||
| 286 | ktime_t time_now; | ||
| 287 | s64 constraint_ns; | ||
| 288 | }; | ||
| 289 | |||
| 290 | /** | ||
| 291 | * rpm_update_qos_constraint - Update a given PM QoS constraint data. | ||
| 292 | * @dev: Device whose timing data to use. | ||
| 293 | * @data: PM QoS constraint data to update. | ||
| 294 | * | ||
| 295 | * Use the suspend timing data of @dev to update PM QoS constraint data pointed | ||
| 296 | * to by @data. | ||
| 297 | */ | ||
| 298 | static int rpm_update_qos_constraint(struct device *dev, void *data) | ||
| 299 | { | ||
| 300 | struct rpm_qos_data *qos = data; | ||
| 301 | unsigned long flags; | ||
| 302 | s64 delta_ns; | ||
| 303 | int ret = 0; | ||
| 304 | |||
| 305 | spin_lock_irqsave(&dev->power.lock, flags); | ||
| 306 | |||
| 307 | if (dev->power.max_time_suspended_ns < 0) | ||
| 308 | goto out; | ||
| 309 | |||
| 310 | delta_ns = dev->power.max_time_suspended_ns - | ||
| 311 | ktime_to_ns(ktime_sub(qos->time_now, dev->power.suspend_time)); | ||
| 312 | if (delta_ns <= 0) { | ||
| 313 | ret = -EBUSY; | ||
| 314 | goto out; | ||
| 315 | } | ||
| 316 | |||
| 317 | if (qos->constraint_ns > delta_ns || qos->constraint_ns == 0) | ||
| 318 | qos->constraint_ns = delta_ns; | ||
| 319 | |||
| 320 | out: | ||
| 321 | spin_unlock_irqrestore(&dev->power.lock, flags); | ||
| 322 | |||
| 323 | return ret; | ||
| 324 | } | ||
| 325 | |||
| 285 | /** | 326 | /** |
| 286 | * rpm_suspend - Carry out runtime suspend of given device. | 327 | * rpm_suspend - Carry out runtime suspend of given device. |
| 287 | * @dev: Device to suspend. | 328 | * @dev: Device to suspend. |
| @@ -308,6 +349,7 @@ static int rpm_suspend(struct device *dev, int rpmflags) | |||
| 308 | { | 349 | { |
| 309 | int (*callback)(struct device *); | 350 | int (*callback)(struct device *); |
| 310 | struct device *parent = NULL; | 351 | struct device *parent = NULL; |
| 352 | struct rpm_qos_data qos; | ||
| 311 | int retval; | 353 | int retval; |
| 312 | 354 | ||
| 313 | trace_rpm_suspend(dev, rpmflags); | 355 | trace_rpm_suspend(dev, rpmflags); |
| @@ -403,8 +445,38 @@ static int rpm_suspend(struct device *dev, int rpmflags) | |||
| 403 | goto out; | 445 | goto out; |
| 404 | } | 446 | } |
| 405 | 447 | ||
| 448 | qos.constraint_ns = __dev_pm_qos_read_value(dev); | ||
| 449 | if (qos.constraint_ns < 0) { | ||
| 450 | /* Negative constraint means "never suspend". */ | ||
| 451 | retval = -EPERM; | ||
| 452 | goto out; | ||
| 453 | } | ||
| 454 | qos.constraint_ns *= NSEC_PER_USEC; | ||
| 455 | qos.time_now = ktime_get(); | ||
| 456 | |||
| 406 | __update_runtime_status(dev, RPM_SUSPENDING); | 457 | __update_runtime_status(dev, RPM_SUSPENDING); |
| 407 | 458 | ||
| 459 | if (!dev->power.ignore_children) { | ||
| 460 | if (dev->power.irq_safe) | ||
| 461 | spin_unlock(&dev->power.lock); | ||
| 462 | else | ||
| 463 | spin_unlock_irq(&dev->power.lock); | ||
| 464 | |||
| 465 | retval = device_for_each_child(dev, &qos, | ||
| 466 | rpm_update_qos_constraint); | ||
| 467 | |||
| 468 | if (dev->power.irq_safe) | ||
| 469 | spin_lock(&dev->power.lock); | ||
| 470 | else | ||
| 471 | spin_lock_irq(&dev->power.lock); | ||
| 472 | |||
| 473 | if (retval) | ||
| 474 | goto fail; | ||
| 475 | } | ||
| 476 | |||
| 477 | dev->power.suspend_time = qos.time_now; | ||
| 478 | dev->power.max_time_suspended_ns = qos.constraint_ns ? : -1; | ||
| 479 | |||
| 408 | if (dev->pm_domain) | 480 | if (dev->pm_domain) |
| 409 | callback = dev->pm_domain->ops.runtime_suspend; | 481 | callback = dev->pm_domain->ops.runtime_suspend; |
| 410 | else if (dev->type && dev->type->pm) | 482 | else if (dev->type && dev->type->pm) |
| @@ -420,27 +492,9 @@ static int rpm_suspend(struct device *dev, int rpmflags) | |||
| 420 | callback = dev->driver->pm->runtime_suspend; | 492 | callback = dev->driver->pm->runtime_suspend; |
| 421 | 493 | ||
| 422 | retval = rpm_callback(callback, dev); | 494 | retval = rpm_callback(callback, dev); |
| 423 | if (retval) { | 495 | if (retval) |
| 424 | __update_runtime_status(dev, RPM_ACTIVE); | 496 | goto fail; |
| 425 | dev->power.deferred_resume = false; | ||
| 426 | if (retval == -EAGAIN || retval == -EBUSY) { | ||
| 427 | dev->power.runtime_error = 0; | ||
| 428 | 497 | ||
| 429 | /* | ||
| 430 | * If the callback routine failed an autosuspend, and | ||
| 431 | * if the last_busy time has been updated so that there | ||
| 432 | * is a new autosuspend expiration time, automatically | ||
| 433 | * reschedule another autosuspend. | ||
| 434 | */ | ||
| 435 | if ((rpmflags & RPM_AUTO) && | ||
| 436 | pm_runtime_autosuspend_expiration(dev) != 0) | ||
| 437 | goto repeat; | ||
| 438 | } else { | ||
| 439 | pm_runtime_cancel_pending(dev); | ||
| 440 | } | ||
| 441 | wake_up_all(&dev->power.wait_queue); | ||
| 442 | goto out; | ||
| 443 | } | ||
| 444 | no_callback: | 498 | no_callback: |
| 445 | __update_runtime_status(dev, RPM_SUSPENDED); | 499 | __update_runtime_status(dev, RPM_SUSPENDED); |
| 446 | pm_runtime_deactivate_timer(dev); | 500 | pm_runtime_deactivate_timer(dev); |
| @@ -472,6 +526,29 @@ static int rpm_suspend(struct device *dev, int rpmflags) | |||
| 472 | trace_rpm_return_int(dev, _THIS_IP_, retval); | 526 | trace_rpm_return_int(dev, _THIS_IP_, retval); |
| 473 | 527 | ||
| 474 | return retval; | 528 | return retval; |
| 529 | |||
| 530 | fail: | ||
| 531 | __update_runtime_status(dev, RPM_ACTIVE); | ||
| 532 | dev->power.suspend_time = ktime_set(0, 0); | ||
| 533 | dev->power.max_time_suspended_ns = -1; | ||
| 534 | dev->power.deferred_resume = false; | ||
| 535 | if (retval == -EAGAIN || retval == -EBUSY) { | ||
| 536 | dev->power.runtime_error = 0; | ||
| 537 | |||
| 538 | /* | ||
| 539 | * If the callback routine failed an autosuspend, and | ||
| 540 | * if the last_busy time has been updated so that there | ||
| 541 | * is a new autosuspend expiration time, automatically | ||
| 542 | * reschedule another autosuspend. | ||
| 543 | */ | ||
| 544 | if ((rpmflags & RPM_AUTO) && | ||
| 545 | pm_runtime_autosuspend_expiration(dev) != 0) | ||
| 546 | goto repeat; | ||
| 547 | } else { | ||
| 548 | pm_runtime_cancel_pending(dev); | ||
| 549 | } | ||
| 550 | wake_up_all(&dev->power.wait_queue); | ||
| 551 | goto out; | ||
| 475 | } | 552 | } |
| 476 | 553 | ||
| 477 | /** | 554 | /** |
| @@ -626,6 +703,9 @@ static int rpm_resume(struct device *dev, int rpmflags) | |||
| 626 | if (dev->power.no_callbacks) | 703 | if (dev->power.no_callbacks) |
| 627 | goto no_callback; /* Assume success. */ | 704 | goto no_callback; /* Assume success. */ |
| 628 | 705 | ||
| 706 | dev->power.suspend_time = ktime_set(0, 0); | ||
| 707 | dev->power.max_time_suspended_ns = -1; | ||
| 708 | |||
| 629 | __update_runtime_status(dev, RPM_RESUMING); | 709 | __update_runtime_status(dev, RPM_RESUMING); |
| 630 | 710 | ||
| 631 | if (dev->pm_domain) | 711 | if (dev->pm_domain) |
| @@ -1288,6 +1368,9 @@ void pm_runtime_init(struct device *dev) | |||
| 1288 | setup_timer(&dev->power.suspend_timer, pm_suspend_timer_fn, | 1368 | setup_timer(&dev->power.suspend_timer, pm_suspend_timer_fn, |
| 1289 | (unsigned long)dev); | 1369 | (unsigned long)dev); |
| 1290 | 1370 | ||
| 1371 | dev->power.suspend_time = ktime_set(0, 0); | ||
| 1372 | dev->power.max_time_suspended_ns = -1; | ||
| 1373 | |||
| 1291 | init_waitqueue_head(&dev->power.wait_queue); | 1374 | init_waitqueue_head(&dev->power.wait_queue); |
| 1292 | } | 1375 | } |
| 1293 | 1376 | ||
| @@ -1305,3 +1388,28 @@ void pm_runtime_remove(struct device *dev) | |||
| 1305 | if (dev->power.irq_safe && dev->parent) | 1388 | if (dev->power.irq_safe && dev->parent) |
| 1306 | pm_runtime_put_sync(dev->parent); | 1389 | pm_runtime_put_sync(dev->parent); |
| 1307 | } | 1390 | } |
| 1391 | |||
| 1392 | /** | ||
| 1393 | * pm_runtime_update_max_time_suspended - Update device's suspend time data. | ||
| 1394 | * @dev: Device to handle. | ||
| 1395 | * @delta_ns: Value to subtract from the device's max_time_suspended_ns field. | ||
| 1396 | * | ||
| 1397 | * Update the device's power.max_time_suspended_ns field by subtracting | ||
| 1398 | * @delta_ns from it. The resulting value of power.max_time_suspended_ns is | ||
| 1399 | * never negative. | ||
| 1400 | */ | ||
| 1401 | void pm_runtime_update_max_time_suspended(struct device *dev, s64 delta_ns) | ||
| 1402 | { | ||
| 1403 | unsigned long flags; | ||
| 1404 | |||
| 1405 | spin_lock_irqsave(&dev->power.lock, flags); | ||
| 1406 | |||
| 1407 | if (delta_ns > 0 && dev->power.max_time_suspended_ns > 0) { | ||
| 1408 | if (dev->power.max_time_suspended_ns > delta_ns) | ||
| 1409 | dev->power.max_time_suspended_ns -= delta_ns; | ||
| 1410 | else | ||
| 1411 | dev->power.max_time_suspended_ns = 0; | ||
| 1412 | } | ||
| 1413 | |||
| 1414 | spin_unlock_irqrestore(&dev->power.lock, flags); | ||
| 1415 | } | ||
diff --git a/include/linux/pm.h b/include/linux/pm.h index 21e04dd72a84..e4982ac3fbbc 100644 --- a/include/linux/pm.h +++ b/include/linux/pm.h | |||
| @@ -508,6 +508,8 @@ struct dev_pm_info { | |||
| 508 | unsigned long active_jiffies; | 508 | unsigned long active_jiffies; |
| 509 | unsigned long suspended_jiffies; | 509 | unsigned long suspended_jiffies; |
| 510 | unsigned long accounting_timestamp; | 510 | unsigned long accounting_timestamp; |
| 511 | ktime_t suspend_time; | ||
| 512 | s64 max_time_suspended_ns; | ||
| 511 | #endif | 513 | #endif |
| 512 | struct pm_subsys_data *subsys_data; /* Owned by the subsystem. */ | 514 | struct pm_subsys_data *subsys_data; /* Owned by the subsystem. */ |
| 513 | struct pm_qos_constraints *constraints; | 515 | 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) |
