diff options
Diffstat (limited to 'drivers/cpufreq/cpufreq.c')
-rw-r--r-- | drivers/cpufreq/cpufreq.c | 114 |
1 files changed, 56 insertions, 58 deletions
diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index df9a2427c233..66f6cf5064ec 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -31,8 +31,6 @@ #include <linux/completion.h> #include <linux/mutex.h> #include <linux/syscore_ops.h> -#include <linux/suspend.h> -#include <linux/tick.h> #include <trace/events/power.h> @@ -50,14 +48,6 @@ static DEFINE_PER_CPU(char[CPUFREQ_NAME_LEN], cpufreq_cpu_governor); static DEFINE_RWLOCK(cpufreq_driver_lock); static DEFINE_MUTEX(cpufreq_governor_lock); -/* Flag to suspend/resume CPUFreq governors */ -static bool cpufreq_suspended; - -static inline bool has_target(void) -{ - return cpufreq_driver->target; -} - /* * cpu_policy_rwsem is a per CPU reader-writer semaphore designed to cure * all cpufreq/hotplug/workqueue/etc related lock issues. @@ -1285,72 +1275,83 @@ static struct subsys_interface cpufreq_interface = { /** - * cpufreq_suspend() - Suspend CPUFreq governors + * cpufreq_bp_suspend - Prepare the boot CPU for system suspend. * - * Called during system wide Suspend/Hibernate cycles for suspending governors - * as some platforms can't change frequency after this point in suspend cycle. - * Because some of the devices (like: i2c, regulators, etc) they use for - * changing frequency are suspended quickly after this point. + * This function is only executed for the boot processor. The other CPUs + * have been put offline by means of CPU hotplug. */ -void cpufreq_suspend(void) +static int cpufreq_bp_suspend(void) { - struct cpufreq_policy *policy; - - if (!cpufreq_driver) - return; + int ret = 0; - if (!has_target()) - return; + int cpu = smp_processor_id(); + struct cpufreq_policy *cpu_policy; - pr_debug("%s: Suspending Governors\n", __func__); + pr_debug("suspending cpu %u\n", cpu); - policy = cpufreq_cpu_get(0); + /* If there's no policy for the boot CPU, we have nothing to do. */ + cpu_policy = cpufreq_cpu_get(cpu); + if (!cpu_policy) + return 0; - if (__cpufreq_governor(policy, CPUFREQ_GOV_STOP)) - pr_err("%s: Failed to stop governor for policy: %p\n", - __func__, policy); - else if (cpufreq_driver->suspend - && cpufreq_driver->suspend(policy)) - pr_err("%s: Failed to suspend driver: %p\n", __func__, - policy); + if (cpufreq_driver->suspend) { + ret = cpufreq_driver->suspend(cpu_policy); + if (ret) + printk(KERN_ERR "cpufreq: suspend failed in ->suspend " + "step on CPU %u\n", cpu_policy->cpu); + } - cpufreq_suspended = true; + cpufreq_cpu_put(cpu_policy); + return ret; } /** - * cpufreq_resume() - Resume CPUFreq governors + * cpufreq_bp_resume - Restore proper frequency handling of the boot CPU. + * + * 1.) resume CPUfreq hardware support (cpufreq_driver->resume()) + * 2.) schedule call cpufreq_update_policy() ASAP as interrupts are + * restored. It will verify that the current freq is in sync with + * what we believe it to be. This is a bit later than when it + * should be, but nonethteless it's better than calling + * cpufreq_driver->get() here which might re-enable interrupts... * - * Called during system wide Suspend/Hibernate cycle for resuming governors that - * are suspended with cpufreq_suspend(). + * This function is only executed for the boot CPU. The other CPUs have not + * been turned on yet. */ -void cpufreq_resume(void) +static void cpufreq_bp_resume(void) { - struct cpufreq_policy *policy; - - if (!cpufreq_driver) - return; + int ret = 0; - if (!has_target()) - return; + int cpu = smp_processor_id(); + struct cpufreq_policy *cpu_policy; - pr_debug("%s: Resuming Governors\n", __func__); + pr_debug("resuming cpu %u\n", cpu); - cpufreq_suspended = false; + /* If there's no policy for the boot CPU, we have nothing to do. */ + cpu_policy = cpufreq_cpu_get(cpu); + if (!cpu_policy) + return; - policy = cpufreq_cpu_get(0); + if (cpufreq_driver->resume) { + ret = cpufreq_driver->resume(cpu_policy); + if (ret) { + printk(KERN_ERR "cpufreq: resume failed in ->resume " + "step on CPU %u\n", cpu_policy->cpu); + goto fail; + } + } - if (__cpufreq_governor(policy, CPUFREQ_GOV_START) - || __cpufreq_governor(policy, CPUFREQ_GOV_LIMITS)) - pr_err("%s: Failed to start governor for policy: %p\n", - __func__, policy); - else if (cpufreq_driver->resume - && cpufreq_driver->resume(policy)) - pr_err("%s: Failed to resume driver: %p\n", __func__, - policy); + schedule_work(&cpu_policy->update); - schedule_work(&policy->update); +fail: + cpufreq_cpu_put(cpu_policy); } +static struct syscore_ops cpufreq_syscore_ops = { + .suspend = cpufreq_bp_suspend, + .resume = cpufreq_bp_resume, +}; + /** * cpufreq_get_current_driver - return current driver's name * @@ -1543,10 +1544,6 @@ static int __cpufreq_governor(struct cpufreq_policy *policy, struct cpufreq_governor *gov = NULL; #endif - /* Don't start any governor operations if we are entering suspend */ - if (cpufreq_suspended) - return 0; - if (policy->governor->max_transition_latency && policy->cpuinfo.transition_latency > policy->governor->max_transition_latency) { @@ -2004,6 +2001,7 @@ static int __init cpufreq_core_init(void) cpufreq_global_kobject = kobject_create_and_add("cpufreq", &cpu_subsys.dev_root->kobj); BUG_ON(!cpufreq_global_kobject); + register_syscore_ops(&cpufreq_syscore_ops); return 0; } |