mirror of
https://github.com/adulau/aha.git
synced 2024-12-28 11:46:19 +00:00
[PATCH] Add suspend method to cpufreq core
In order to properly fix some issues with cpufreq vs. sleep on PowerBooks, I had to add a suspend callback to the pmac_cpufreq driver. I must force a switch to full speed before sleep and I switch back to previous speed on resume. I also added a driver flag to disable the warnings in suspend/resume since it is expected in this case to have different speed (and I want it to fixup the jiffies properly). Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
This commit is contained in:
parent
c60c390620
commit
42d4dc3f4e
2 changed files with 93 additions and 6 deletions
|
@ -223,7 +223,7 @@ static inline void adjust_jiffies(unsigned long val, struct cpufreq_freqs *ci)
|
||||||
}
|
}
|
||||||
if ((val == CPUFREQ_PRECHANGE && ci->old < ci->new) ||
|
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)) {
|
(val == CPUFREQ_RESUMECHANGE || val == CPUFREQ_SUSPENDCHANGE)) {
|
||||||
loops_per_jiffy = cpufreq_scale(l_p_j_ref, l_p_j_ref_freq, ci->new);
|
loops_per_jiffy = cpufreq_scale(l_p_j_ref, l_p_j_ref_freq, ci->new);
|
||||||
dprintk("scaling loops_per_jiffy to %lu for frequency %u kHz\n", loops_per_jiffy, ci->new);
|
dprintk("scaling loops_per_jiffy to %lu for frequency %u kHz\n", loops_per_jiffy, ci->new);
|
||||||
}
|
}
|
||||||
|
@ -865,12 +865,91 @@ unsigned int cpufreq_get(unsigned int cpu)
|
||||||
EXPORT_SYMBOL(cpufreq_get);
|
EXPORT_SYMBOL(cpufreq_get);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* cpufreq_suspend - let the low level driver prepare for suspend
|
||||||
|
*/
|
||||||
|
|
||||||
|
static int cpufreq_suspend(struct sys_device * sysdev, u32 state)
|
||||||
|
{
|
||||||
|
int cpu = sysdev->id;
|
||||||
|
unsigned int ret = 0;
|
||||||
|
unsigned int cur_freq = 0;
|
||||||
|
struct cpufreq_policy *cpu_policy;
|
||||||
|
|
||||||
|
dprintk("resuming cpu %u\n", cpu);
|
||||||
|
|
||||||
|
if (!cpu_online(cpu))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* we may be lax here as interrupts are off. Nonetheless
|
||||||
|
* we need to grab the correct cpu policy, as to check
|
||||||
|
* whether we really run on this CPU.
|
||||||
|
*/
|
||||||
|
|
||||||
|
cpu_policy = cpufreq_cpu_get(cpu);
|
||||||
|
if (!cpu_policy)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
/* only handle each CPU group once */
|
||||||
|
if (unlikely(cpu_policy->cpu != cpu)) {
|
||||||
|
cpufreq_cpu_put(cpu_policy);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cpufreq_driver->suspend) {
|
||||||
|
ret = cpufreq_driver->suspend(cpu_policy, state);
|
||||||
|
if (ret) {
|
||||||
|
printk(KERN_ERR "cpufreq: suspend failed in ->suspend "
|
||||||
|
"step on CPU %u\n", cpu_policy->cpu);
|
||||||
|
cpufreq_cpu_put(cpu_policy);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (cpufreq_driver->flags & CPUFREQ_CONST_LOOPS)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
if (cpufreq_driver->get)
|
||||||
|
cur_freq = cpufreq_driver->get(cpu_policy->cpu);
|
||||||
|
|
||||||
|
if (!cur_freq || !cpu_policy->cur) {
|
||||||
|
printk(KERN_ERR "cpufreq: suspend 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_PM_NO_WARN))
|
||||||
|
printk(KERN_DEBUG "Warning: CPU frequency is %u, "
|
||||||
|
"cpufreq assumed %u kHz.\n",
|
||||||
|
cur_freq, cpu_policy->cur);
|
||||||
|
|
||||||
|
freqs.cpu = cpu;
|
||||||
|
freqs.old = cpu_policy->cur;
|
||||||
|
freqs.new = cur_freq;
|
||||||
|
|
||||||
|
notifier_call_chain(&cpufreq_transition_notifier_list,
|
||||||
|
CPUFREQ_SUSPENDCHANGE, &freqs);
|
||||||
|
adjust_jiffies(CPUFREQ_SUSPENDCHANGE, &freqs);
|
||||||
|
|
||||||
|
cpu_policy->cur = cur_freq;
|
||||||
|
}
|
||||||
|
|
||||||
|
out:
|
||||||
|
cpufreq_cpu_put(cpu_policy);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* cpufreq_resume - restore proper CPU frequency handling after resume
|
* cpufreq_resume - restore proper CPU frequency handling after resume
|
||||||
*
|
*
|
||||||
* 1.) resume CPUfreq hardware support (cpufreq_driver->resume())
|
* 1.) resume CPUfreq hardware support (cpufreq_driver->resume())
|
||||||
* 2.) if ->target and !CPUFREQ_CONST_LOOPS: verify we're in sync
|
* 2.) if ->target and !CPUFREQ_CONST_LOOPS: verify we're in sync
|
||||||
* 3.) schedule call cpufreq_update_policy() ASAP as interrupts are restored.
|
* 3.) schedule call cpufreq_update_policy() ASAP as interrupts are
|
||||||
|
* restored.
|
||||||
*/
|
*/
|
||||||
static int cpufreq_resume(struct sys_device * sysdev)
|
static int cpufreq_resume(struct sys_device * sysdev)
|
||||||
{
|
{
|
||||||
|
@ -915,7 +994,9 @@ static int cpufreq_resume(struct sys_device * sysdev)
|
||||||
cur_freq = cpufreq_driver->get(cpu_policy->cpu);
|
cur_freq = cpufreq_driver->get(cpu_policy->cpu);
|
||||||
|
|
||||||
if (!cur_freq || !cpu_policy->cur) {
|
if (!cur_freq || !cpu_policy->cur) {
|
||||||
printk(KERN_ERR "cpufreq: resume failed to assert current frequency is what timing core thinks it is.\n");
|
printk(KERN_ERR "cpufreq: resume failed to assert "
|
||||||
|
"current frequency is what timing core "
|
||||||
|
"thinks it is.\n");
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -923,13 +1004,15 @@ static int cpufreq_resume(struct sys_device * sysdev)
|
||||||
struct cpufreq_freqs freqs;
|
struct cpufreq_freqs freqs;
|
||||||
|
|
||||||
printk(KERN_WARNING "Warning: CPU frequency is %u, "
|
printk(KERN_WARNING "Warning: CPU frequency is %u, "
|
||||||
"cpufreq assumed %u kHz.\n", cur_freq, cpu_policy->cur);
|
"cpufreq assumed %u kHz.\n",
|
||||||
|
cur_freq, cpu_policy->cur);
|
||||||
|
|
||||||
freqs.cpu = cpu;
|
freqs.cpu = cpu;
|
||||||
freqs.old = cpu_policy->cur;
|
freqs.old = cpu_policy->cur;
|
||||||
freqs.new = cur_freq;
|
freqs.new = cur_freq;
|
||||||
|
|
||||||
notifier_call_chain(&cpufreq_transition_notifier_list, CPUFREQ_RESUMECHANGE, &freqs);
|
notifier_call_chain(&cpufreq_transition_notifier_list,
|
||||||
|
CPUFREQ_RESUMECHANGE, &freqs);
|
||||||
adjust_jiffies(CPUFREQ_RESUMECHANGE, &freqs);
|
adjust_jiffies(CPUFREQ_RESUMECHANGE, &freqs);
|
||||||
|
|
||||||
cpu_policy->cur = cur_freq;
|
cpu_policy->cur = cur_freq;
|
||||||
|
@ -945,6 +1028,7 @@ out:
|
||||||
static struct sysdev_driver cpufreq_sysdev_driver = {
|
static struct sysdev_driver cpufreq_sysdev_driver = {
|
||||||
.add = cpufreq_add_dev,
|
.add = cpufreq_add_dev,
|
||||||
.remove = cpufreq_remove_dev,
|
.remove = cpufreq_remove_dev,
|
||||||
|
.suspend = cpufreq_suspend,
|
||||||
.resume = cpufreq_resume,
|
.resume = cpufreq_resume,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -103,6 +103,7 @@ struct cpufreq_policy {
|
||||||
#define CPUFREQ_PRECHANGE (0)
|
#define CPUFREQ_PRECHANGE (0)
|
||||||
#define CPUFREQ_POSTCHANGE (1)
|
#define CPUFREQ_POSTCHANGE (1)
|
||||||
#define CPUFREQ_RESUMECHANGE (8)
|
#define CPUFREQ_RESUMECHANGE (8)
|
||||||
|
#define CPUFREQ_SUSPENDCHANGE (9)
|
||||||
|
|
||||||
struct cpufreq_freqs {
|
struct cpufreq_freqs {
|
||||||
unsigned int cpu; /* cpu nr */
|
unsigned int cpu; /* cpu nr */
|
||||||
|
@ -200,6 +201,7 @@ struct cpufreq_driver {
|
||||||
|
|
||||||
/* optional */
|
/* optional */
|
||||||
int (*exit) (struct cpufreq_policy *policy);
|
int (*exit) (struct cpufreq_policy *policy);
|
||||||
|
int (*suspend) (struct cpufreq_policy *policy, u32 state);
|
||||||
int (*resume) (struct cpufreq_policy *policy);
|
int (*resume) (struct cpufreq_policy *policy);
|
||||||
struct freq_attr **attr;
|
struct freq_attr **attr;
|
||||||
};
|
};
|
||||||
|
@ -211,7 +213,8 @@ struct cpufreq_driver {
|
||||||
#define CPUFREQ_CONST_LOOPS 0x02 /* loops_per_jiffy or other kernel
|
#define CPUFREQ_CONST_LOOPS 0x02 /* loops_per_jiffy or other kernel
|
||||||
* "constants" aren't affected by
|
* "constants" aren't affected by
|
||||||
* frequency transitions */
|
* frequency transitions */
|
||||||
|
#define CPUFREQ_PM_NO_WARN 0x04 /* don't warn on suspend/resume speed
|
||||||
|
* mismatches */
|
||||||
|
|
||||||
int cpufreq_register_driver(struct cpufreq_driver *driver_data);
|
int cpufreq_register_driver(struct cpufreq_driver *driver_data);
|
||||||
int cpufreq_unregister_driver(struct cpufreq_driver *driver_data);
|
int cpufreq_unregister_driver(struct cpufreq_driver *driver_data);
|
||||||
|
|
Loading…
Reference in a new issue