mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-09 20:32:04 +09:00
Merge branch 'v3.10/topic/cpufreq' of git://git.linaro.org/kernel/linux-linaro-stable into linux-linaro-lsk
This commit is contained in:
@@ -28,6 +28,8 @@
|
||||
#include <linux/sched.h>
|
||||
#include <linux/async.h>
|
||||
#include <linux/suspend.h>
|
||||
#include <trace/events/power.h>
|
||||
#include <linux/cpufreq.h>
|
||||
#include <linux/cpuidle.h>
|
||||
#include "../base.h"
|
||||
#include "power.h"
|
||||
@@ -713,6 +715,8 @@ void dpm_resume(pm_message_t state)
|
||||
mutex_unlock(&dpm_list_mtx);
|
||||
async_synchronize_full();
|
||||
dpm_show_time(starttime, state, NULL);
|
||||
|
||||
cpufreq_resume();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1177,6 +1181,8 @@ int dpm_suspend(pm_message_t state)
|
||||
|
||||
might_sleep();
|
||||
|
||||
cpufreq_suspend();
|
||||
|
||||
mutex_lock(&dpm_list_mtx);
|
||||
pm_transition = state;
|
||||
async_error = 0;
|
||||
|
||||
@@ -31,6 +31,8 @@
|
||||
#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>
|
||||
|
||||
@@ -48,6 +50,14 @@ 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.
|
||||
@@ -1275,83 +1285,90 @@ static struct subsys_interface cpufreq_interface = {
|
||||
|
||||
|
||||
/**
|
||||
* cpufreq_bp_suspend - Prepare the boot CPU for system suspend.
|
||||
* cpufreq_suspend() - Suspend CPUFreq governors
|
||||
*
|
||||
* This function is only executed for the boot processor. The other CPUs
|
||||
* have been put offline by means of CPU hotplug.
|
||||
* 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.
|
||||
*/
|
||||
static int cpufreq_bp_suspend(void)
|
||||
void cpufreq_suspend(void)
|
||||
{
|
||||
int ret = 0;
|
||||
struct cpufreq_policy *policy;
|
||||
int cpu;
|
||||
|
||||
int cpu = smp_processor_id();
|
||||
struct cpufreq_policy *cpu_policy;
|
||||
if (!cpufreq_driver)
|
||||
return;
|
||||
|
||||
pr_debug("suspending cpu %u\n", cpu);
|
||||
if (!has_target())
|
||||
return;
|
||||
|
||||
/* 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;
|
||||
pr_debug("%s: Suspending Governors\n", __func__);
|
||||
|
||||
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);
|
||||
for_each_possible_cpu(cpu) {
|
||||
if (!cpu_online(cpu))
|
||||
continue;
|
||||
|
||||
policy = cpufreq_cpu_get(cpu);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
cpufreq_cpu_put(cpu_policy);
|
||||
return ret;
|
||||
cpufreq_suspended = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* cpufreq_bp_resume - Restore proper frequency handling of the boot CPU.
|
||||
* cpufreq_resume() - Resume CPUFreq governors
|
||||
*
|
||||
* 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...
|
||||
*
|
||||
* This function is only executed for the boot CPU. The other CPUs have not
|
||||
* been turned on yet.
|
||||
* Called during system wide Suspend/Hibernate cycle for resuming governors that
|
||||
* are suspended with cpufreq_suspend().
|
||||
*/
|
||||
static void cpufreq_bp_resume(void)
|
||||
void cpufreq_resume(void)
|
||||
{
|
||||
int ret = 0;
|
||||
struct cpufreq_policy *policy;
|
||||
int cpu;
|
||||
|
||||
int cpu = smp_processor_id();
|
||||
struct cpufreq_policy *cpu_policy;
|
||||
|
||||
pr_debug("resuming cpu %u\n", cpu);
|
||||
|
||||
/* If there's no policy for the boot CPU, we have nothing to do. */
|
||||
cpu_policy = cpufreq_cpu_get(cpu);
|
||||
if (!cpu_policy)
|
||||
if (!cpufreq_driver)
|
||||
return;
|
||||
|
||||
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 (!has_target())
|
||||
return;
|
||||
|
||||
pr_debug("%s: Resuming Governors\n", __func__);
|
||||
|
||||
cpufreq_suspended = false;
|
||||
|
||||
for_each_possible_cpu(cpu) {
|
||||
if (!cpu_online(cpu))
|
||||
continue;
|
||||
|
||||
policy = cpufreq_cpu_get(cpu);
|
||||
|
||||
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 call cpufreq_update_policy() for boot CPU, i.e. last
|
||||
* policy in list. It will verify that the current freq is in
|
||||
* sync with what we believe it to be.
|
||||
*/
|
||||
if (cpu == 0)
|
||||
schedule_work(&policy->update);
|
||||
}
|
||||
|
||||
schedule_work(&cpu_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
|
||||
*
|
||||
@@ -1544,6 +1561,10 @@ 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) {
|
||||
@@ -2001,7 +2022,6 @@ 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;
|
||||
}
|
||||
|
||||
@@ -307,6 +307,18 @@ struct freq_attr {
|
||||
static struct freq_attr _name = \
|
||||
__ATTR(_name, 0444, show_##_name, NULL)
|
||||
|
||||
#ifdef CONFIG_CPU_FREQ
|
||||
void cpufreq_suspend(void);
|
||||
void cpufreq_resume(void);
|
||||
#else
|
||||
static inline void cpufreq_suspend(void) {}
|
||||
static inline void cpufreq_resume(void) {}
|
||||
#endif
|
||||
|
||||
/*********************************************************************
|
||||
* CPUFREQ NOTIFIER INTERFACE *
|
||||
*********************************************************************/
|
||||
|
||||
#define cpufreq_freq_attr_ro_perm(_name, _perm) \
|
||||
static struct freq_attr _name = \
|
||||
__ATTR(_name, _perm, show_##_name, NULL)
|
||||
|
||||
Reference in New Issue
Block a user