Commit ceccad84 authored by Dave Jones's avatar Dave Jones Committed by Dave Jones

[CPUFREQ] (Hopefully) fix cpufreq resume support.

 
Upon resuming, first CPUfreq hardware support needs to be re-enabled in certain cases
(call to cpufreq_driver->resume()).
 
Then, two different paths may need to be taken:
a) frequency during suspend equals frequency during resume ==> everything is fine,
 
b) frequency differ ==> either we can't handle it, then panic (see flag
   CPUFREQ_PANIC_RESUME_OUTOFSYNC). Or we can handle it, then notify all
parent 6a4a93f9
......@@ -92,3 +92,7 @@ values:
cpu - number of the affected CPU
old - old frequency
new - new frequency
If the cpufreq core detects the frequency has changed while the system
was suspended, these notifiers are called with CPUFREQ_RESUMECHANGE as
second argument.
......@@ -33,8 +33,10 @@ static struct cpufreq_driver *cpufreq_driver;
static struct cpufreq_policy *cpufreq_cpu_data[NR_CPUS];
static spinlock_t cpufreq_driver_lock = SPIN_LOCK_UNLOCKED;
/* internal prototype */
/* internal prototypes */
static int __cpufreq_governor(struct cpufreq_policy *policy, unsigned int event);
static void handle_update(void *data);
static inline void adjust_jiffies(unsigned long val, struct cpufreq_freqs *ci);
/**
......@@ -362,7 +364,6 @@ static struct kobj_type ktype_cpufreq = {
.release = cpufreq_sysfs_release,
};
static void handle_update(void *data);
/**
* cpufreq_add_dev - add a CPU device
......@@ -576,10 +577,11 @@ EXPORT_SYMBOL(cpufreq_get);
/**
* cpufreq_resume - restore the CPU clock frequency after resume
* cpufreq_resume - restore proper CPU frequency handling after resume
*
* Restore the CPU clock frequency so that our idea of the current
* frequency reflects the actual hardware.
* 1.) resume CPUfreq hardware support (cpufreq_driver->resume())
* 2.) if ->target and !CPUFREQ_CONST_LOOPS: verify we're in sync
* 3.) schedule call cpufreq_update_policy() ASAP as interrupts are restored.
*/
static int cpufreq_resume(struct sys_device * sysdev)
{
......@@ -599,25 +601,37 @@ static int cpufreq_resume(struct sys_device * sysdev)
if (!cpu_policy)
return -EINVAL;
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 out;
}
if (!cpufreq_driver->flags & CPUFREQ_CONST_LOOPS) {
unsigned int cur_freq = 0;
if (cpufreq_driver->setpolicy)
ret = cpufreq_driver->setpolicy(cpu_policy);
else
/* CPUFREQ_RELATION_H or CPUFREQ_RELATION_L have the same effect here, as cpu_policy->cur is known
* to be a valid and exact target frequency
*/
ret = cpufreq_driver->target(cpu_policy, cpu_policy->cur, CPUFREQ_RELATION_H);
if (cpufreq_driver->get)
cur_freq = cpufreq_driver->get(cpu_policy->cpu);
if (ret)
printk(KERN_ERR "cpufreq: resume failed in ->setpolicy/target step on CPU %u\n", cpu_policy->cpu);
if (!cur_freq || !cpu_policy->cur) {
printk(KERN_ERR "cpufreq: resume failed to assert current frequency is what timing core thinks it is.\n");
goto out;
}
if (unlikely(cur_freq != cpu_policy->cur)) {
struct cpufreq_freqs freqs;
if (cpufreq_driver->flags & CPUFREQ_PANIC_RESUME_OUTOFSYNC)
panic("CPU Frequency is out of sync.");
printk(KERN_WARNING "Warning: CPU frequency out of sync: cpufreq and timing"
"core thinks of %u, is %u kHz.\n", cpu_policy->cur, cur_freq);
freqs.cpu = cpu;
freqs.old = cpu_policy->cur;
freqs.new = cur_freq;
notifier_call_chain(&cpufreq_transition_notifier_list, CPUFREQ_RESUMECHANGE, &freqs);
adjust_jiffies(CPUFREQ_RESUMECHANGE, &freqs);
}
}
out:
schedule_work(&cpu_policy->update);
cpufreq_cpu_put(cpu_policy);
return ret;
}
......@@ -1013,11 +1027,12 @@ static inline void adjust_jiffies(unsigned long val, struct cpufreq_freqs *ci)
l_p_j_ref_freq = ci->old;
}
if ((val == CPUFREQ_PRECHANGE && ci->old < ci->new) ||
(val == CPUFREQ_POSTCHANGE && ci->old > ci->new))
(val == CPUFREQ_POSTCHANGE && ci->old > ci->new) ||
(val == CPUFREQ_RESUMECHANGE))
loops_per_jiffy = cpufreq_scale(l_p_j_ref, l_p_j_ref_freq, ci->new);
}
#else
#define adjust_jiffies(x...) do {} while (0)
static inline void adjust_jiffies(unsigned long val, struct cpufreq_freqs *ci) { return; }
#endif
......@@ -1029,9 +1044,7 @@ static inline void adjust_jiffies(unsigned long val, struct cpufreq_freqs *ci)
*/
void cpufreq_notify_transition(struct cpufreq_freqs *freqs, unsigned int state)
{
if (irqs_disabled())
return; /* Only valid if we're in the resume process where
* everyone knows what CPU frequency we are at */
BUG_ON(irqs_disabled());
down_read(&cpufreq_notifier_rwsem);
switch (state) {
......
......@@ -100,6 +100,7 @@ struct cpufreq_policy {
#define CPUFREQ_PRECHANGE (0)
#define CPUFREQ_POSTCHANGE (1)
#define CPUFREQ_RESUMECHANGE (8)
struct cpufreq_freqs {
unsigned int cpu; /* cpu nr */
......@@ -210,6 +211,10 @@ struct cpufreq_driver {
#define CPUFREQ_PANIC_OUTOFSYNC 0x04 /* panic if cpufreq's opinion of
* current frequency differs from
* actual frequency */
#define CPUFREQ_PANIC_RESUME_OUTOFSYNC 0x08 /* panic if cpufreq's opinion of
* current frequency differs from
* actual frequency on resume
* from sleep. */
int cpufreq_register_driver(struct cpufreq_driver *driver_data);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment