summaryrefslogtreecommitdiff
path: root/drivers/base/power/runtime.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2018-12-25 13:47:41 -0800
committerLinus Torvalds <torvalds@linux-foundation.org>2018-12-25 13:47:41 -0800
commit1e2af254ef130e37d9fb3cb1bc9bfbf6ea184b4a (patch)
treeda653d3e38f54ed497d3fae7d7f223fdbedb4ac8 /drivers/base/power/runtime.c
parentb271b2127e6654a72dc1685f0825fe1cc2f36939 (diff)
parenta465d38fa3dce6a0dc2d5814cb3aa7b0d2982c6b (diff)
Merge tag 'pm-4.21-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm
Pull power management updates from Rafael Wysocki: "These add sysadmin documentation for cpuidle, extend the cpuidle subsystem somewhat, improve the handling of performance states in the generic power domains (genpd) and operating performance points (OPP) frameworks, add a new cpufreq driver for Qualcomm SoCs, update some other cpufreq drivers, switch over the runtime PM framework to using high-res timers for device autosuspend, fix a problem with suspend-to-idle on ACPI-based platforms, add system-wide suspend and resume handling to the devfreq framework, do some janitorial cleanups all over and update some utilities. Specifics: - Add sysadmin documentation for cpuidle (Rafael Wysocki). - Make it possible to specify a cpuidle governor from kernel command line, add new cpuidle state sysfs attributes for governor evaluation, and improve the "polling" idle state handling (Rafael Wysocki). - Fix the handling of the "required-opps" DT property in the operating performance points (OPP) framework, improve the integration of it with the generic power domains (genpd) framework, improve the handling of performance states in them and clean up the idle states vs performance states separation in genpd (Viresh Kumar, Ulf Hansson). - Add a cpufreq driver called "qcom-hw" for Qualcomm SoCs using a hardware engine to control CPU frequency transitions along with DT bindings for it (Taniya Das). - Fix an intel_pstate driver issue related to CPU offline and update the documentation of it (Srinivas Pandruvada). - Clean up the imx6q cpufreq driver (Anson Huang). - Add SPDX license IDs to cpufreq schedutil governor files (Daniel Lezcano). - Switch over the runtime PM framework to using high-res timers for device autosuspend to allow the control of it to be more precise (Vincent Guittot). - Disable non-wakeup ACPI GPEs during suspend-to-idle so that they don't prevent the system from reaching the target low-power state and simplify the suspend-to-idle handling on ACPI platforms without full Low-Power S0 Idle (LPS0) support (Rafael Wysocki). - Add system-wide suspend and resume support to the devfreq framework (Lukasz Luba). - Clean up the SmartReflex adaptive voltage scaling (AVS) driver and add an SPDX license ID to it (Nishanth Menon, Uwe Kleine-König, Thomas Meyer). - Get rid of code duplication by using the DEFINE_SHOW_ATTRIBUTE macro in some places, fix some DT node refcount leaks, and do some other janitorial cleanups (Yangtao Li). - Update the cpupower, intel_pstate_tracer and turbosat utilities (Abhishek Goel, Doug Smythies, Len Brown)" * tag 'pm-4.21-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm: (54 commits) PM / Domains: remove define_genpd_open_function() and define_genpd_debugfs_fops() PM-runtime: Switch autosuspend over to using hrtimers cpufreq: qcom-hw: Add support for QCOM cpufreq HW driver dt-bindings: cpufreq: Introduce QCOM cpufreq firmware bindings ACPI: PM: Loop in full LPS0 mode only ACPI: EC / PM: Disable non-wakeup GPEs for suspend-to-idle tools/power/x86/intel_pstate_tracer: Fix non root execution for post processing a trace file tools/power turbostat: consolidate duplicate model numbers tools/power turbostat: fix goldmont C-state limit decoding PM / Domains: Propagate performance state updates PM / Domains: Factorize dev_pm_genpd_set_performance_state() PM / Domains: Save OPP table pointer in genpd OPP: Don't return 0 on error from of_get_required_opp_performance_state() OPP: Add dev_pm_opp_xlate_performance_state() helper OPP: Improve _find_table_of_opp_np() PM / Domains: Make genpd performance states orthogonal to the idlestates PM / sleep: convert to DEFINE_SHOW_ATTRIBUTE cpuidle: Add 'above' and 'below' idle state metrics PM / AVS: SmartReflex: Switch to SPDX Licence ID PM / AVS: SmartReflex: NULL check before some freeing functions is not needed ...
Diffstat (limited to 'drivers/base/power/runtime.c')
-rw-r--r--drivers/base/power/runtime.c63
1 files changed, 34 insertions, 29 deletions
diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c
index beb85c31f3fa..70624695b6d5 100644
--- a/drivers/base/power/runtime.c
+++ b/drivers/base/power/runtime.c
@@ -8,6 +8,8 @@
*/
#include <linux/sched/mm.h>
+#include <linux/ktime.h>
+#include <linux/hrtimer.h>
#include <linux/export.h>
#include <linux/pm_runtime.h>
#include <linux/pm_wakeirq.h>
@@ -93,7 +95,7 @@ static void __update_runtime_status(struct device *dev, enum rpm_status status)
static void pm_runtime_deactivate_timer(struct device *dev)
{
if (dev->power.timer_expires > 0) {
- del_timer(&dev->power.suspend_timer);
+ hrtimer_cancel(&dev->power.suspend_timer);
dev->power.timer_expires = 0;
}
}
@@ -124,12 +126,11 @@ static void pm_runtime_cancel_pending(struct device *dev)
* This function may be called either with or without dev->power.lock held.
* Either way it can be racy, since power.last_busy may be updated at any time.
*/
-unsigned long pm_runtime_autosuspend_expiration(struct device *dev)
+u64 pm_runtime_autosuspend_expiration(struct device *dev)
{
int autosuspend_delay;
- long elapsed;
- unsigned long last_busy;
- unsigned long expires = 0;
+ u64 last_busy, expires = 0;
+ u64 now = ktime_to_ns(ktime_get());
if (!dev->power.use_autosuspend)
goto out;
@@ -139,19 +140,9 @@ unsigned long pm_runtime_autosuspend_expiration(struct device *dev)
goto out;
last_busy = READ_ONCE(dev->power.last_busy);
- elapsed = jiffies - last_busy;
- if (elapsed < 0)
- goto out; /* jiffies has wrapped around. */
- /*
- * If the autosuspend_delay is >= 1 second, align the timer by rounding
- * up to the nearest second.
- */
- expires = last_busy + msecs_to_jiffies(autosuspend_delay);
- if (autosuspend_delay >= 1000)
- expires = round_jiffies(expires);
- expires += !expires;
- if (elapsed >= expires - last_busy)
+ expires = last_busy + autosuspend_delay * NSEC_PER_MSEC;
+ if (expires <= now)
expires = 0; /* Already expired. */
out:
@@ -515,7 +506,7 @@ static int rpm_suspend(struct device *dev, int rpmflags)
/* If the autosuspend_delay time hasn't expired yet, reschedule. */
if ((rpmflags & RPM_AUTO)
&& dev->power.runtime_status != RPM_SUSPENDING) {
- unsigned long expires = pm_runtime_autosuspend_expiration(dev);
+ u64 expires = pm_runtime_autosuspend_expiration(dev);
if (expires != 0) {
/* Pending requests need to be canceled. */
@@ -528,10 +519,20 @@ static int rpm_suspend(struct device *dev, int rpmflags)
* expire; pm_suspend_timer_fn() will take care of the
* rest.
*/
- if (!(dev->power.timer_expires && time_before_eq(
- dev->power.timer_expires, expires))) {
+ if (!(dev->power.timer_expires &&
+ dev->power.timer_expires <= expires)) {
+ /*
+ * We add a slack of 25% to gather wakeups
+ * without sacrificing the granularity.
+ */
+ u64 slack = READ_ONCE(dev->power.autosuspend_delay) *
+ (NSEC_PER_MSEC >> 2);
+
dev->power.timer_expires = expires;
- mod_timer(&dev->power.suspend_timer, expires);
+ hrtimer_start_range_ns(&dev->power.suspend_timer,
+ ns_to_ktime(expires),
+ slack,
+ HRTIMER_MODE_ABS);
}
dev->power.timer_autosuspends = 1;
goto out;
@@ -895,23 +896,25 @@ static void pm_runtime_work(struct work_struct *work)
*
* Check if the time is right and queue a suspend request.
*/
-static void pm_suspend_timer_fn(struct timer_list *t)
+static enum hrtimer_restart pm_suspend_timer_fn(struct hrtimer *timer)
{
- struct device *dev = from_timer(dev, t, power.suspend_timer);
+ struct device *dev = container_of(timer, struct device, power.suspend_timer);
unsigned long flags;
- unsigned long expires;
+ u64 expires;
spin_lock_irqsave(&dev->power.lock, flags);
expires = dev->power.timer_expires;
/* If 'expire' is after 'jiffies' we've been called too early. */
- if (expires > 0 && !time_after(expires, jiffies)) {
+ if (expires > 0 && expires < ktime_to_ns(ktime_get())) {
dev->power.timer_expires = 0;
rpm_suspend(dev, dev->power.timer_autosuspends ?
(RPM_ASYNC | RPM_AUTO) : RPM_ASYNC);
}
spin_unlock_irqrestore(&dev->power.lock, flags);
+
+ return HRTIMER_NORESTART;
}
/**
@@ -922,6 +925,7 @@ static void pm_suspend_timer_fn(struct timer_list *t)
int pm_schedule_suspend(struct device *dev, unsigned int delay)
{
unsigned long flags;
+ ktime_t expires;
int retval;
spin_lock_irqsave(&dev->power.lock, flags);
@@ -938,10 +942,10 @@ int pm_schedule_suspend(struct device *dev, unsigned int delay)
/* Other scheduled or pending requests need to be canceled. */
pm_runtime_cancel_pending(dev);
- dev->power.timer_expires = jiffies + msecs_to_jiffies(delay);
- dev->power.timer_expires += !dev->power.timer_expires;
+ expires = ktime_add(ktime_get(), ms_to_ktime(delay));
+ dev->power.timer_expires = ktime_to_ns(expires);
dev->power.timer_autosuspends = 0;
- mod_timer(&dev->power.suspend_timer, dev->power.timer_expires);
+ hrtimer_start(&dev->power.suspend_timer, expires, HRTIMER_MODE_ABS);
out:
spin_unlock_irqrestore(&dev->power.lock, flags);
@@ -1491,7 +1495,8 @@ void pm_runtime_init(struct device *dev)
INIT_WORK(&dev->power.work, pm_runtime_work);
dev->power.timer_expires = 0;
- timer_setup(&dev->power.suspend_timer, pm_suspend_timer_fn, 0);
+ hrtimer_init(&dev->power.suspend_timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS);
+ dev->power.suspend_timer.function = pm_suspend_timer_fn;
init_waitqueue_head(&dev->power.wait_queue);
}