Skip to content

Commit 2f0aea9

Browse files
vireshkrafaeljw
authored andcommitted
cpufreq: suspend governors on system suspend/hibernate
This patch adds cpufreq suspend/resume calls to dpm_{suspend|resume}() for handling suspend/resume of cpufreq governors. Lan Tianyu (Intel) & Jinhyuk Choi (Broadcom) found an issue where the tunables configuration for clusters/sockets with non-boot CPUs was lost after system suspend/resume, as we were notifying governors with CPUFREQ_GOV_POLICY_EXIT on removal of the last CPU for that policy which caused the tunables memory to be freed. This is fixed by preventing any governor operations from being carried out between the device suspend and device resume stages of system suspend and resume, respectively. We could have added these callbacks at dpm_{suspend|resume}_noirq() level, but there is an additional problem that the majority of I/O devices is already suspended at that point and if cpufreq drivers want to change the frequency before suspending, then that not be possible on some platforms (which depend on peripherals like i2c, regulators, etc). Reported-and-tested-by: Lan Tianyu <tianyu.lan@intel.com> Reported-by: Jinhyuk Choi <jinchoi@broadcom.com> Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> [rjw: Changelog] Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
1 parent 6e2c89d commit 2f0aea9

File tree

3 files changed

+69
-55
lines changed

3 files changed

+69
-55
lines changed

drivers/base/power/main.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
#include <linux/async.h>
3030
#include <linux/suspend.h>
3131
#include <trace/events/power.h>
32+
#include <linux/cpufreq.h>
3233
#include <linux/cpuidle.h>
3334
#include <linux/timer.h>
3435

@@ -789,6 +790,8 @@ void dpm_resume(pm_message_t state)
789790
mutex_unlock(&dpm_list_mtx);
790791
async_synchronize_full();
791792
dpm_show_time(starttime, state, NULL);
793+
794+
cpufreq_resume();
792795
}
793796

794797
/**
@@ -1259,6 +1262,8 @@ int dpm_suspend(pm_message_t state)
12591262

12601263
might_sleep();
12611264

1265+
cpufreq_suspend();
1266+
12621267
mutex_lock(&dpm_list_mtx);
12631268
pm_transition = state;
12641269
async_error = 0;

drivers/cpufreq/cpufreq.c

Lines changed: 56 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
#include <linux/module.h>
2727
#include <linux/mutex.h>
2828
#include <linux/slab.h>
29-
#include <linux/syscore_ops.h>
29+
#include <linux/suspend.h>
3030
#include <linux/tick.h>
3131
#include <trace/events/power.h>
3232

@@ -45,6 +45,9 @@ static LIST_HEAD(cpufreq_policy_list);
4545
/* This one keeps track of the previously set governor of a removed CPU */
4646
static DEFINE_PER_CPU(char[CPUFREQ_NAME_LEN], cpufreq_cpu_governor);
4747

48+
/* Flag to suspend/resume CPUFreq governors */
49+
static bool cpufreq_suspended;
50+
4851
static inline bool has_target(void)
4952
{
5053
return cpufreq_driver->target_index || cpufreq_driver->target;
@@ -1565,82 +1568,77 @@ static struct subsys_interface cpufreq_interface = {
15651568
};
15661569

15671570
/**
1568-
* cpufreq_bp_suspend - Prepare the boot CPU for system suspend.
1571+
* cpufreq_suspend() - Suspend CPUFreq governors
15691572
*
1570-
* This function is only executed for the boot processor. The other CPUs
1571-
* have been put offline by means of CPU hotplug.
1573+
* Called during system wide Suspend/Hibernate cycles for suspending governors
1574+
* as some platforms can't change frequency after this point in suspend cycle.
1575+
* Because some of the devices (like: i2c, regulators, etc) they use for
1576+
* changing frequency are suspended quickly after this point.
15721577
*/
1573-
static int cpufreq_bp_suspend(void)
1578+
void cpufreq_suspend(void)
15741579
{
1575-
int ret = 0;
1576-
1577-
int cpu = smp_processor_id();
15781580
struct cpufreq_policy *policy;
15791581

1580-
pr_debug("suspending cpu %u\n", cpu);
1582+
if (!cpufreq_driver)
1583+
return;
15811584

1582-
/* If there's no policy for the boot CPU, we have nothing to do. */
1583-
policy = cpufreq_cpu_get(cpu);
1584-
if (!policy)
1585-
return 0;
1585+
if (!has_target())
1586+
return;
15861587

1587-
if (cpufreq_driver->suspend) {
1588-
ret = cpufreq_driver->suspend(policy);
1589-
if (ret)
1590-
printk(KERN_ERR "cpufreq: suspend failed in ->suspend "
1591-
"step on CPU %u\n", policy->cpu);
1588+
pr_debug("%s: Suspending Governors\n", __func__);
1589+
1590+
list_for_each_entry(policy, &cpufreq_policy_list, policy_list) {
1591+
if (__cpufreq_governor(policy, CPUFREQ_GOV_STOP))
1592+
pr_err("%s: Failed to stop governor for policy: %p\n",
1593+
__func__, policy);
1594+
else if (cpufreq_driver->suspend
1595+
&& cpufreq_driver->suspend(policy))
1596+
pr_err("%s: Failed to suspend driver: %p\n", __func__,
1597+
policy);
15921598
}
15931599

1594-
cpufreq_cpu_put(policy);
1595-
return ret;
1600+
cpufreq_suspended = true;
15961601
}
15971602

15981603
/**
1599-
* cpufreq_bp_resume - Restore proper frequency handling of the boot CPU.
1604+
* cpufreq_resume() - Resume CPUFreq governors
16001605
*
1601-
* 1.) resume CPUfreq hardware support (cpufreq_driver->resume())
1602-
* 2.) schedule call cpufreq_update_policy() ASAP as interrupts are
1603-
* restored. It will verify that the current freq is in sync with
1604-
* what we believe it to be. This is a bit later than when it
1605-
* should be, but nonethteless it's better than calling
1606-
* cpufreq_driver->get() here which might re-enable interrupts...
1607-
*
1608-
* This function is only executed for the boot CPU. The other CPUs have not
1609-
* been turned on yet.
1606+
* Called during system wide Suspend/Hibernate cycle for resuming governors that
1607+
* are suspended with cpufreq_suspend().
16101608
*/
1611-
static void cpufreq_bp_resume(void)
1609+
void cpufreq_resume(void)
16121610
{
1613-
int ret = 0;
1614-
1615-
int cpu = smp_processor_id();
16161611
struct cpufreq_policy *policy;
16171612

1618-
pr_debug("resuming cpu %u\n", cpu);
1613+
if (!cpufreq_driver)
1614+
return;
16191615

1620-
/* If there's no policy for the boot CPU, we have nothing to do. */
1621-
policy = cpufreq_cpu_get(cpu);
1622-
if (!policy)
1616+
if (!has_target())
16231617
return;
16241618

1625-
if (cpufreq_driver->resume) {
1626-
ret = cpufreq_driver->resume(policy);
1627-
if (ret) {
1628-
printk(KERN_ERR "cpufreq: resume failed in ->resume "
1629-
"step on CPU %u\n", policy->cpu);
1630-
goto fail;
1631-
}
1632-
}
1619+
pr_debug("%s: Resuming Governors\n", __func__);
16331620

1634-
schedule_work(&policy->update);
1621+
cpufreq_suspended = false;
16351622

1636-
fail:
1637-
cpufreq_cpu_put(policy);
1638-
}
1623+
list_for_each_entry(policy, &cpufreq_policy_list, policy_list) {
1624+
if (__cpufreq_governor(policy, CPUFREQ_GOV_START)
1625+
|| __cpufreq_governor(policy, CPUFREQ_GOV_LIMITS))
1626+
pr_err("%s: Failed to start governor for policy: %p\n",
1627+
__func__, policy);
1628+
else if (cpufreq_driver->resume
1629+
&& cpufreq_driver->resume(policy))
1630+
pr_err("%s: Failed to resume driver: %p\n", __func__,
1631+
policy);
16391632

1640-
static struct syscore_ops cpufreq_syscore_ops = {
1641-
.suspend = cpufreq_bp_suspend,
1642-
.resume = cpufreq_bp_resume,
1643-
};
1633+
/*
1634+
* schedule call cpufreq_update_policy() for boot CPU, i.e. last
1635+
* policy in list. It will verify that the current freq is in
1636+
* sync with what we believe it to be.
1637+
*/
1638+
if (list_is_last(&policy->policy_list, &cpufreq_policy_list))
1639+
schedule_work(&policy->update);
1640+
}
1641+
}
16441642

16451643
/**
16461644
* cpufreq_get_current_driver - return current driver's name
@@ -1857,6 +1855,10 @@ static int __cpufreq_governor(struct cpufreq_policy *policy,
18571855
struct cpufreq_governor *gov = NULL;
18581856
#endif
18591857

1858+
/* Don't start any governor operations if we are entering suspend */
1859+
if (cpufreq_suspended)
1860+
return 0;
1861+
18601862
if (policy->governor->max_transition_latency &&
18611863
policy->cpuinfo.transition_latency >
18621864
policy->governor->max_transition_latency) {
@@ -2392,7 +2394,6 @@ static int __init cpufreq_core_init(void)
23922394

23932395
cpufreq_global_kobject = kobject_create();
23942396
BUG_ON(!cpufreq_global_kobject);
2395-
register_syscore_ops(&cpufreq_syscore_ops);
23962397

23972398
return 0;
23982399
}

include/linux/cpufreq.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,14 @@ cpufreq_verify_within_cpu_limits(struct cpufreq_policy *policy)
296296
policy->cpuinfo.max_freq);
297297
}
298298

299+
#ifdef CONFIG_CPU_FREQ
300+
void cpufreq_suspend(void);
301+
void cpufreq_resume(void);
302+
#else
303+
static inline void cpufreq_suspend(void) {}
304+
static inline void cpufreq_resume(void) {}
305+
#endif
306+
299307
/*********************************************************************
300308
* CPUFREQ NOTIFIER INTERFACE *
301309
*********************************************************************/

0 commit comments

Comments
 (0)