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 /drivers/base/power | |
parent | b7ba68c4a072c9aa8f04b8cf7838b6cd2f48d918 (diff) | |
parent | 00dc9ad18d707f36b2fb4af98fd2cf0548d2b258 (diff) |
Merge branch 'pm-runtime' into pm-for-linus
* pm-runtime:
PM / Runtime: Use device PM QoS constraints (v2)
Diffstat (limited to 'drivers/base/power')
-rw-r--r-- | drivers/base/power/qos.c | 24 | ||||
-rw-r--r-- | drivers/base/power/runtime.c | 148 |
2 files changed, 144 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 | } | ||