diff options
author | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2017-12-15 20:05:48 -0500 |
---|---|---|
committer | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2017-12-15 20:05:48 -0500 |
commit | c51a024e3913e9dbaf4dfcb9aaba825668a89ace (patch) | |
tree | 9772af495ced0bde0d3c58b6e6fb5b395b3afae9 /drivers/base/power/main.c | |
parent | 3487972d7fa6c5143951436ada5933dcf0ec659d (diff) | |
parent | 34fb8f0ba9ceea88e116688f9f53e3802c38aafb (diff) |
Merge back PM core material for v4.16.
Diffstat (limited to 'drivers/base/power/main.c')
-rw-r--r-- | drivers/base/power/main.c | 102 |
1 files changed, 84 insertions, 18 deletions
diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index 08744b572af6..6e8cc5de93fd 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c | |||
@@ -18,7 +18,6 @@ | |||
18 | */ | 18 | */ |
19 | 19 | ||
20 | #include <linux/device.h> | 20 | #include <linux/device.h> |
21 | #include <linux/kallsyms.h> | ||
22 | #include <linux/export.h> | 21 | #include <linux/export.h> |
23 | #include <linux/mutex.h> | 22 | #include <linux/mutex.h> |
24 | #include <linux/pm.h> | 23 | #include <linux/pm.h> |
@@ -541,6 +540,18 @@ void dev_pm_skip_next_resume_phases(struct device *dev) | |||
541 | } | 540 | } |
542 | 541 | ||
543 | /** | 542 | /** |
543 | * dev_pm_may_skip_resume - System-wide device resume optimization check. | ||
544 | * @dev: Target device. | ||
545 | * | ||
546 | * Checks whether or not the device may be left in suspend after a system-wide | ||
547 | * transition to the working state. | ||
548 | */ | ||
549 | bool dev_pm_may_skip_resume(struct device *dev) | ||
550 | { | ||
551 | return !dev->power.must_resume && pm_transition.event != PM_EVENT_RESTORE; | ||
552 | } | ||
553 | |||
554 | /** | ||
544 | * device_resume_noirq - Execute a "noirq resume" callback for given device. | 555 | * device_resume_noirq - Execute a "noirq resume" callback for given device. |
545 | * @dev: Device to handle. | 556 | * @dev: Device to handle. |
546 | * @state: PM transition of the system being carried out. | 557 | * @state: PM transition of the system being carried out. |
@@ -588,6 +599,18 @@ static int device_resume_noirq(struct device *dev, pm_message_t state, bool asyn | |||
588 | error = dpm_run_callback(callback, dev, state, info); | 599 | error = dpm_run_callback(callback, dev, state, info); |
589 | dev->power.is_noirq_suspended = false; | 600 | dev->power.is_noirq_suspended = false; |
590 | 601 | ||
602 | if (dev_pm_may_skip_resume(dev)) { | ||
603 | /* | ||
604 | * The device is going to be left in suspend, but it might not | ||
605 | * have been in runtime suspend before the system suspended, so | ||
606 | * its runtime PM status needs to be updated to avoid confusing | ||
607 | * the runtime PM framework when runtime PM is enabled for the | ||
608 | * device again. | ||
609 | */ | ||
610 | pm_runtime_set_suspended(dev); | ||
611 | dev_pm_skip_next_resume_phases(dev); | ||
612 | } | ||
613 | |||
591 | Out: | 614 | Out: |
592 | complete_all(&dev->power.completion); | 615 | complete_all(&dev->power.completion); |
593 | TRACE_RESUME(error); | 616 | TRACE_RESUME(error); |
@@ -1089,6 +1112,22 @@ static pm_message_t resume_event(pm_message_t sleep_state) | |||
1089 | return PMSG_ON; | 1112 | return PMSG_ON; |
1090 | } | 1113 | } |
1091 | 1114 | ||
1115 | static void dpm_superior_set_must_resume(struct device *dev) | ||
1116 | { | ||
1117 | struct device_link *link; | ||
1118 | int idx; | ||
1119 | |||
1120 | if (dev->parent) | ||
1121 | dev->parent->power.must_resume = true; | ||
1122 | |||
1123 | idx = device_links_read_lock(); | ||
1124 | |||
1125 | list_for_each_entry_rcu(link, &dev->links.suppliers, c_node) | ||
1126 | link->supplier->power.must_resume = true; | ||
1127 | |||
1128 | device_links_read_unlock(idx); | ||
1129 | } | ||
1130 | |||
1092 | /** | 1131 | /** |
1093 | * __device_suspend_noirq - Execute a "noirq suspend" callback for given device. | 1132 | * __device_suspend_noirq - Execute a "noirq suspend" callback for given device. |
1094 | * @dev: Device to handle. | 1133 | * @dev: Device to handle. |
@@ -1140,10 +1179,28 @@ static int __device_suspend_noirq(struct device *dev, pm_message_t state, bool a | |||
1140 | } | 1179 | } |
1141 | 1180 | ||
1142 | error = dpm_run_callback(callback, dev, state, info); | 1181 | error = dpm_run_callback(callback, dev, state, info); |
1143 | if (!error) | 1182 | if (error) { |
1144 | dev->power.is_noirq_suspended = true; | ||
1145 | else | ||
1146 | async_error = error; | 1183 | async_error = error; |
1184 | goto Complete; | ||
1185 | } | ||
1186 | |||
1187 | dev->power.is_noirq_suspended = true; | ||
1188 | |||
1189 | if (dev_pm_test_driver_flags(dev, DPM_FLAG_LEAVE_SUSPENDED)) { | ||
1190 | /* | ||
1191 | * The only safe strategy here is to require that if the device | ||
1192 | * may not be left in suspend, resume callbacks must be invoked | ||
1193 | * for it. | ||
1194 | */ | ||
1195 | dev->power.must_resume = dev->power.must_resume || | ||
1196 | !dev->power.may_skip_resume || | ||
1197 | atomic_read(&dev->power.usage_count) > 1; | ||
1198 | } else { | ||
1199 | dev->power.must_resume = true; | ||
1200 | } | ||
1201 | |||
1202 | if (dev->power.must_resume) | ||
1203 | dpm_superior_set_must_resume(dev); | ||
1147 | 1204 | ||
1148 | Complete: | 1205 | Complete: |
1149 | complete_all(&dev->power.completion); | 1206 | complete_all(&dev->power.completion); |
@@ -1435,6 +1492,22 @@ static int legacy_suspend(struct device *dev, pm_message_t state, | |||
1435 | return error; | 1492 | return error; |
1436 | } | 1493 | } |
1437 | 1494 | ||
1495 | static void dpm_propagate_to_parent(struct device *dev) | ||
1496 | { | ||
1497 | struct device *parent = dev->parent; | ||
1498 | |||
1499 | if (!parent) | ||
1500 | return; | ||
1501 | |||
1502 | spin_lock_irq(&parent->power.lock); | ||
1503 | |||
1504 | parent->power.direct_complete = false; | ||
1505 | if (dev->power.wakeup_path && !parent->power.ignore_children) | ||
1506 | parent->power.wakeup_path = true; | ||
1507 | |||
1508 | spin_unlock_irq(&parent->power.lock); | ||
1509 | } | ||
1510 | |||
1438 | static void dpm_clear_suppliers_direct_complete(struct device *dev) | 1511 | static void dpm_clear_suppliers_direct_complete(struct device *dev) |
1439 | { | 1512 | { |
1440 | struct device_link *link; | 1513 | struct device_link *link; |
@@ -1500,6 +1573,9 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async) | |||
1500 | dev->power.direct_complete = false; | 1573 | dev->power.direct_complete = false; |
1501 | } | 1574 | } |
1502 | 1575 | ||
1576 | dev->power.may_skip_resume = false; | ||
1577 | dev->power.must_resume = false; | ||
1578 | |||
1503 | dpm_watchdog_set(&wd, dev); | 1579 | dpm_watchdog_set(&wd, dev); |
1504 | device_lock(dev); | 1580 | device_lock(dev); |
1505 | 1581 | ||
@@ -1543,19 +1619,8 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async) | |||
1543 | 1619 | ||
1544 | End: | 1620 | End: |
1545 | if (!error) { | 1621 | if (!error) { |
1546 | struct device *parent = dev->parent; | ||
1547 | |||
1548 | dev->power.is_suspended = true; | 1622 | dev->power.is_suspended = true; |
1549 | if (parent) { | 1623 | dpm_propagate_to_parent(dev); |
1550 | spin_lock_irq(&parent->power.lock); | ||
1551 | |||
1552 | dev->parent->power.direct_complete = false; | ||
1553 | if (dev->power.wakeup_path | ||
1554 | && !dev->parent->power.ignore_children) | ||
1555 | dev->parent->power.wakeup_path = true; | ||
1556 | |||
1557 | spin_unlock_irq(&parent->power.lock); | ||
1558 | } | ||
1559 | dpm_clear_suppliers_direct_complete(dev); | 1624 | dpm_clear_suppliers_direct_complete(dev); |
1560 | } | 1625 | } |
1561 | 1626 | ||
@@ -1665,8 +1730,9 @@ static int device_prepare(struct device *dev, pm_message_t state) | |||
1665 | if (dev->power.syscore) | 1730 | if (dev->power.syscore) |
1666 | return 0; | 1731 | return 0; |
1667 | 1732 | ||
1668 | WARN_ON(dev_pm_test_driver_flags(dev, DPM_FLAG_SMART_SUSPEND) && | 1733 | WARN_ON(!pm_runtime_enabled(dev) && |
1669 | !pm_runtime_enabled(dev)); | 1734 | dev_pm_test_driver_flags(dev, DPM_FLAG_SMART_SUSPEND | |
1735 | DPM_FLAG_LEAVE_SUSPENDED)); | ||
1670 | 1736 | ||
1671 | /* | 1737 | /* |
1672 | * If a device's parent goes into runtime suspend at the wrong time, | 1738 | * If a device's parent goes into runtime suspend at the wrong time, |