aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/base/power/main.c
diff options
context:
space:
mode:
authorRafael J. Wysocki <rafael.j.wysocki@intel.com>2017-12-15 20:05:48 -0500
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>2017-12-15 20:05:48 -0500
commitc51a024e3913e9dbaf4dfcb9aaba825668a89ace (patch)
tree9772af495ced0bde0d3c58b6e6fb5b395b3afae9 /drivers/base/power/main.c
parent3487972d7fa6c5143951436ada5933dcf0ec659d (diff)
parent34fb8f0ba9ceea88e116688f9f53e3802c38aafb (diff)
Merge back PM core material for v4.16.
Diffstat (limited to 'drivers/base/power/main.c')
-rw-r--r--drivers/base/power/main.c102
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 */
549bool 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
1115static 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
1148Complete: 1205Complete:
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
1495static 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
1438static void dpm_clear_suppliers_direct_complete(struct device *dev) 1511static 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,