diff options
Diffstat (limited to 'drivers/base/power/runtime.c')
-rw-r--r-- | drivers/base/power/runtime.c | 157 |
1 files changed, 137 insertions, 20 deletions
diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c index 8c78443bca8f..541f821d4ea6 100644 --- a/drivers/base/power/runtime.c +++ b/drivers/base/power/runtime.c | |||
@@ -250,6 +250,9 @@ static int rpm_idle(struct device *dev, int rpmflags) | |||
250 | else | 250 | else |
251 | callback = NULL; | 251 | callback = NULL; |
252 | 252 | ||
253 | if (!callback && dev->driver && dev->driver->pm) | ||
254 | callback = dev->driver->pm->runtime_idle; | ||
255 | |||
253 | if (callback) | 256 | if (callback) |
254 | __rpm_callback(callback, dev); | 257 | __rpm_callback(callback, dev); |
255 | 258 | ||
@@ -279,6 +282,47 @@ static int rpm_callback(int (*cb)(struct device *), struct device *dev) | |||
279 | return retval != -EACCES ? retval : -EIO; | 282 | return retval != -EACCES ? retval : -EIO; |
280 | } | 283 | } |
281 | 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 | |||
282 | /** | 326 | /** |
283 | * rpm_suspend - Carry out runtime suspend of given device. | 327 | * rpm_suspend - Carry out runtime suspend of given device. |
284 | * @dev: Device to suspend. | 328 | * @dev: Device to suspend. |
@@ -305,6 +349,7 @@ static int rpm_suspend(struct device *dev, int rpmflags) | |||
305 | { | 349 | { |
306 | int (*callback)(struct device *); | 350 | int (*callback)(struct device *); |
307 | struct device *parent = NULL; | 351 | struct device *parent = NULL; |
352 | struct rpm_qos_data qos; | ||
308 | int retval; | 353 | int retval; |
309 | 354 | ||
310 | trace_rpm_suspend(dev, rpmflags); | 355 | trace_rpm_suspend(dev, rpmflags); |
@@ -400,8 +445,38 @@ static int rpm_suspend(struct device *dev, int rpmflags) | |||
400 | goto out; | 445 | goto out; |
401 | } | 446 | } |
402 | 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 | |||
403 | __update_runtime_status(dev, RPM_SUSPENDING); | 457 | __update_runtime_status(dev, RPM_SUSPENDING); |
404 | 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 | |||
405 | if (dev->pm_domain) | 480 | if (dev->pm_domain) |
406 | callback = dev->pm_domain->ops.runtime_suspend; | 481 | callback = dev->pm_domain->ops.runtime_suspend; |
407 | else if (dev->type && dev->type->pm) | 482 | else if (dev->type && dev->type->pm) |
@@ -413,28 +488,13 @@ static int rpm_suspend(struct device *dev, int rpmflags) | |||
413 | else | 488 | else |
414 | callback = NULL; | 489 | callback = NULL; |
415 | 490 | ||
491 | if (!callback && dev->driver && dev->driver->pm) | ||
492 | callback = dev->driver->pm->runtime_suspend; | ||
493 | |||
416 | retval = rpm_callback(callback, dev); | 494 | retval = rpm_callback(callback, dev); |
417 | if (retval) { | 495 | if (retval) |
418 | __update_runtime_status(dev, RPM_ACTIVE); | 496 | goto fail; |
419 | dev->power.deferred_resume = false; | ||
420 | if (retval == -EAGAIN || retval == -EBUSY) { | ||
421 | dev->power.runtime_error = 0; | ||
422 | 497 | ||
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: | 498 | no_callback: |
439 | __update_runtime_status(dev, RPM_SUSPENDED); | 499 | __update_runtime_status(dev, RPM_SUSPENDED); |
440 | pm_runtime_deactivate_timer(dev); | 500 | pm_runtime_deactivate_timer(dev); |
@@ -466,6 +526,29 @@ static int rpm_suspend(struct device *dev, int rpmflags) | |||
466 | trace_rpm_return_int(dev, _THIS_IP_, retval); | 526 | trace_rpm_return_int(dev, _THIS_IP_, retval); |
467 | 527 | ||
468 | 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; | ||
469 | } | 552 | } |
470 | 553 | ||
471 | /** | 554 | /** |
@@ -620,6 +703,9 @@ static int rpm_resume(struct device *dev, int rpmflags) | |||
620 | if (dev->power.no_callbacks) | 703 | if (dev->power.no_callbacks) |
621 | goto no_callback; /* Assume success. */ | 704 | goto no_callback; /* Assume success. */ |
622 | 705 | ||
706 | dev->power.suspend_time = ktime_set(0, 0); | ||
707 | dev->power.max_time_suspended_ns = -1; | ||
708 | |||
623 | __update_runtime_status(dev, RPM_RESUMING); | 709 | __update_runtime_status(dev, RPM_RESUMING); |
624 | 710 | ||
625 | if (dev->pm_domain) | 711 | if (dev->pm_domain) |
@@ -633,6 +719,9 @@ static int rpm_resume(struct device *dev, int rpmflags) | |||
633 | else | 719 | else |
634 | callback = NULL; | 720 | callback = NULL; |
635 | 721 | ||
722 | if (!callback && dev->driver && dev->driver->pm) | ||
723 | callback = dev->driver->pm->runtime_resume; | ||
724 | |||
636 | retval = rpm_callback(callback, dev); | 725 | retval = rpm_callback(callback, dev); |
637 | if (retval) { | 726 | if (retval) { |
638 | __update_runtime_status(dev, RPM_SUSPENDED); | 727 | __update_runtime_status(dev, RPM_SUSPENDED); |
@@ -1279,6 +1368,9 @@ void pm_runtime_init(struct device *dev) | |||
1279 | setup_timer(&dev->power.suspend_timer, pm_suspend_timer_fn, | 1368 | setup_timer(&dev->power.suspend_timer, pm_suspend_timer_fn, |
1280 | (unsigned long)dev); | 1369 | (unsigned long)dev); |
1281 | 1370 | ||
1371 | dev->power.suspend_time = ktime_set(0, 0); | ||
1372 | dev->power.max_time_suspended_ns = -1; | ||
1373 | |||
1282 | init_waitqueue_head(&dev->power.wait_queue); | 1374 | init_waitqueue_head(&dev->power.wait_queue); |
1283 | } | 1375 | } |
1284 | 1376 | ||
@@ -1296,3 +1388,28 @@ void pm_runtime_remove(struct device *dev) | |||
1296 | if (dev->power.irq_safe && dev->parent) | 1388 | if (dev->power.irq_safe && dev->parent) |
1297 | pm_runtime_put_sync(dev->parent); | 1389 | pm_runtime_put_sync(dev->parent); |
1298 | } | 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 | } | ||