diff options
Diffstat (limited to 'drivers/base/power/runtime.c')
-rw-r--r-- | drivers/base/power/runtime.c | 162 |
1 files changed, 123 insertions, 39 deletions
diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c index 72e00e66ecc5..4776cf528d08 100644 --- a/drivers/base/power/runtime.c +++ b/drivers/base/power/runtime.c | |||
@@ -13,6 +13,43 @@ | |||
13 | #include <trace/events/rpm.h> | 13 | #include <trace/events/rpm.h> |
14 | #include "power.h" | 14 | #include "power.h" |
15 | 15 | ||
16 | #define RPM_GET_CALLBACK(dev, cb) \ | ||
17 | ({ \ | ||
18 | int (*__rpm_cb)(struct device *__d); \ | ||
19 | \ | ||
20 | if (dev->pm_domain) \ | ||
21 | __rpm_cb = dev->pm_domain->ops.cb; \ | ||
22 | else if (dev->type && dev->type->pm) \ | ||
23 | __rpm_cb = dev->type->pm->cb; \ | ||
24 | else if (dev->class && dev->class->pm) \ | ||
25 | __rpm_cb = dev->class->pm->cb; \ | ||
26 | else if (dev->bus && dev->bus->pm) \ | ||
27 | __rpm_cb = dev->bus->pm->cb; \ | ||
28 | else \ | ||
29 | __rpm_cb = NULL; \ | ||
30 | \ | ||
31 | if (!__rpm_cb && dev->driver && dev->driver->pm) \ | ||
32 | __rpm_cb = dev->driver->pm->cb; \ | ||
33 | \ | ||
34 | __rpm_cb; \ | ||
35 | }) | ||
36 | |||
37 | static int (*rpm_get_suspend_cb(struct device *dev))(struct device *) | ||
38 | { | ||
39 | return RPM_GET_CALLBACK(dev, runtime_suspend); | ||
40 | } | ||
41 | |||
42 | static int (*rpm_get_resume_cb(struct device *dev))(struct device *) | ||
43 | { | ||
44 | return RPM_GET_CALLBACK(dev, runtime_resume); | ||
45 | } | ||
46 | |||
47 | #ifdef CONFIG_PM_RUNTIME | ||
48 | static int (*rpm_get_idle_cb(struct device *dev))(struct device *) | ||
49 | { | ||
50 | return RPM_GET_CALLBACK(dev, runtime_idle); | ||
51 | } | ||
52 | |||
16 | static int rpm_resume(struct device *dev, int rpmflags); | 53 | static int rpm_resume(struct device *dev, int rpmflags); |
17 | static int rpm_suspend(struct device *dev, int rpmflags); | 54 | static int rpm_suspend(struct device *dev, int rpmflags); |
18 | 55 | ||
@@ -310,19 +347,7 @@ static int rpm_idle(struct device *dev, int rpmflags) | |||
310 | 347 | ||
311 | dev->power.idle_notification = true; | 348 | dev->power.idle_notification = true; |
312 | 349 | ||
313 | if (dev->pm_domain) | 350 | callback = rpm_get_idle_cb(dev); |
314 | callback = dev->pm_domain->ops.runtime_idle; | ||
315 | else if (dev->type && dev->type->pm) | ||
316 | callback = dev->type->pm->runtime_idle; | ||
317 | else if (dev->class && dev->class->pm) | ||
318 | callback = dev->class->pm->runtime_idle; | ||
319 | else if (dev->bus && dev->bus->pm) | ||
320 | callback = dev->bus->pm->runtime_idle; | ||
321 | else | ||
322 | callback = NULL; | ||
323 | |||
324 | if (!callback && dev->driver && dev->driver->pm) | ||
325 | callback = dev->driver->pm->runtime_idle; | ||
326 | 351 | ||
327 | if (callback) | 352 | if (callback) |
328 | retval = __rpm_callback(callback, dev); | 353 | retval = __rpm_callback(callback, dev); |
@@ -492,19 +517,7 @@ static int rpm_suspend(struct device *dev, int rpmflags) | |||
492 | 517 | ||
493 | __update_runtime_status(dev, RPM_SUSPENDING); | 518 | __update_runtime_status(dev, RPM_SUSPENDING); |
494 | 519 | ||
495 | if (dev->pm_domain) | 520 | callback = rpm_get_suspend_cb(dev); |
496 | callback = dev->pm_domain->ops.runtime_suspend; | ||
497 | else if (dev->type && dev->type->pm) | ||
498 | callback = dev->type->pm->runtime_suspend; | ||
499 | else if (dev->class && dev->class->pm) | ||
500 | callback = dev->class->pm->runtime_suspend; | ||
501 | else if (dev->bus && dev->bus->pm) | ||
502 | callback = dev->bus->pm->runtime_suspend; | ||
503 | else | ||
504 | callback = NULL; | ||
505 | |||
506 | if (!callback && dev->driver && dev->driver->pm) | ||
507 | callback = dev->driver->pm->runtime_suspend; | ||
508 | 521 | ||
509 | retval = rpm_callback(callback, dev); | 522 | retval = rpm_callback(callback, dev); |
510 | if (retval) | 523 | if (retval) |
@@ -724,19 +737,7 @@ static int rpm_resume(struct device *dev, int rpmflags) | |||
724 | 737 | ||
725 | __update_runtime_status(dev, RPM_RESUMING); | 738 | __update_runtime_status(dev, RPM_RESUMING); |
726 | 739 | ||
727 | if (dev->pm_domain) | 740 | callback = rpm_get_resume_cb(dev); |
728 | callback = dev->pm_domain->ops.runtime_resume; | ||
729 | else if (dev->type && dev->type->pm) | ||
730 | callback = dev->type->pm->runtime_resume; | ||
731 | else if (dev->class && dev->class->pm) | ||
732 | callback = dev->class->pm->runtime_resume; | ||
733 | else if (dev->bus && dev->bus->pm) | ||
734 | callback = dev->bus->pm->runtime_resume; | ||
735 | else | ||
736 | callback = NULL; | ||
737 | |||
738 | if (!callback && dev->driver && dev->driver->pm) | ||
739 | callback = dev->driver->pm->runtime_resume; | ||
740 | 741 | ||
741 | retval = rpm_callback(callback, dev); | 742 | retval = rpm_callback(callback, dev); |
742 | if (retval) { | 743 | if (retval) { |
@@ -1401,3 +1402,86 @@ void pm_runtime_remove(struct device *dev) | |||
1401 | if (dev->power.irq_safe && dev->parent) | 1402 | if (dev->power.irq_safe && dev->parent) |
1402 | pm_runtime_put(dev->parent); | 1403 | pm_runtime_put(dev->parent); |
1403 | } | 1404 | } |
1405 | #endif | ||
1406 | |||
1407 | /** | ||
1408 | * pm_runtime_force_suspend - Force a device into suspend state if needed. | ||
1409 | * @dev: Device to suspend. | ||
1410 | * | ||
1411 | * Disable runtime PM so we safely can check the device's runtime PM status and | ||
1412 | * if it is active, invoke it's .runtime_suspend callback to bring it into | ||
1413 | * suspend state. Keep runtime PM disabled to preserve the state unless we | ||
1414 | * encounter errors. | ||
1415 | * | ||
1416 | * Typically this function may be invoked from a system suspend callback to make | ||
1417 | * sure the device is put into low power state. | ||
1418 | */ | ||
1419 | int pm_runtime_force_suspend(struct device *dev) | ||
1420 | { | ||
1421 | int (*callback)(struct device *); | ||
1422 | int ret = 0; | ||
1423 | |||
1424 | pm_runtime_disable(dev); | ||
1425 | |||
1426 | /* | ||
1427 | * Note that pm_runtime_status_suspended() returns false while | ||
1428 | * !CONFIG_PM_RUNTIME, which means the device will be put into low | ||
1429 | * power state. | ||
1430 | */ | ||
1431 | if (pm_runtime_status_suspended(dev)) | ||
1432 | return 0; | ||
1433 | |||
1434 | callback = rpm_get_suspend_cb(dev); | ||
1435 | |||
1436 | if (!callback) { | ||
1437 | ret = -ENOSYS; | ||
1438 | goto err; | ||
1439 | } | ||
1440 | |||
1441 | ret = callback(dev); | ||
1442 | if (ret) | ||
1443 | goto err; | ||
1444 | |||
1445 | pm_runtime_set_suspended(dev); | ||
1446 | return 0; | ||
1447 | err: | ||
1448 | pm_runtime_enable(dev); | ||
1449 | return ret; | ||
1450 | } | ||
1451 | EXPORT_SYMBOL_GPL(pm_runtime_force_suspend); | ||
1452 | |||
1453 | /** | ||
1454 | * pm_runtime_force_resume - Force a device into resume state. | ||
1455 | * @dev: Device to resume. | ||
1456 | * | ||
1457 | * Prior invoking this function we expect the user to have brought the device | ||
1458 | * into low power state by a call to pm_runtime_force_suspend(). Here we reverse | ||
1459 | * those actions and brings the device into full power. We update the runtime PM | ||
1460 | * status and re-enables runtime PM. | ||
1461 | * | ||
1462 | * Typically this function may be invoked from a system resume callback to make | ||
1463 | * sure the device is put into full power state. | ||
1464 | */ | ||
1465 | int pm_runtime_force_resume(struct device *dev) | ||
1466 | { | ||
1467 | int (*callback)(struct device *); | ||
1468 | int ret = 0; | ||
1469 | |||
1470 | callback = rpm_get_resume_cb(dev); | ||
1471 | |||
1472 | if (!callback) { | ||
1473 | ret = -ENOSYS; | ||
1474 | goto out; | ||
1475 | } | ||
1476 | |||
1477 | ret = callback(dev); | ||
1478 | if (ret) | ||
1479 | goto out; | ||
1480 | |||
1481 | pm_runtime_set_active(dev); | ||
1482 | pm_runtime_mark_last_busy(dev); | ||
1483 | out: | ||
1484 | pm_runtime_enable(dev); | ||
1485 | return ret; | ||
1486 | } | ||
1487 | EXPORT_SYMBOL_GPL(pm_runtime_force_resume); | ||