Skip to content

Commit

Permalink
[PATCH] Add suspend method to cpufreq core
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Benjamin Herrenschmidt authored and Linus Torvalds committed Apr 29, 2005
1 parent c60c390 commit 42d4dc3
Show file tree
Hide file tree
Showing 2 changed files with 93 additions and 6 deletions.
94 changes: 89 additions & 5 deletions drivers/cpufreq/cpufreq.c
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@ static inline void adjust_jiffies(unsigned long val, struct cpufreq_freqs *ci)
}
if ((val == CPUFREQ_PRECHANGE && 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);
dprintk("scaling loops_per_jiffy to %lu for frequency %u kHz\n", loops_per_jiffy, ci->new);
}
Expand Down Expand Up @@ -865,12 +865,91 @@ unsigned int cpufreq_get(unsigned int cpu)
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
*
* 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.
* 3.) schedule call cpufreq_update_policy() ASAP as interrupts are
* restored.
*/
static int cpufreq_resume(struct sys_device * sysdev)
{
Expand Down Expand Up @@ -915,21 +994,25 @@ static int cpufreq_resume(struct sys_device * sysdev)
cur_freq = cpufreq_driver->get(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");
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;

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.old = cpu_policy->cur;
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);

cpu_policy->cur = cur_freq;
Expand All @@ -945,6 +1028,7 @@ static int cpufreq_resume(struct sys_device * sysdev)
static struct sysdev_driver cpufreq_sysdev_driver = {
.add = cpufreq_add_dev,
.remove = cpufreq_remove_dev,
.suspend = cpufreq_suspend,
.resume = cpufreq_resume,
};

Expand Down
5 changes: 4 additions & 1 deletion include/linux/cpufreq.h
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ struct cpufreq_policy {
#define CPUFREQ_PRECHANGE (0)
#define CPUFREQ_POSTCHANGE (1)
#define CPUFREQ_RESUMECHANGE (8)
#define CPUFREQ_SUSPENDCHANGE (9)

struct cpufreq_freqs {
unsigned int cpu; /* cpu nr */
Expand Down Expand Up @@ -200,6 +201,7 @@ struct cpufreq_driver {

/* optional */
int (*exit) (struct cpufreq_policy *policy);
int (*suspend) (struct cpufreq_policy *policy, u32 state);
int (*resume) (struct cpufreq_policy *policy);
struct freq_attr **attr;
};
Expand All @@ -211,7 +213,8 @@ struct cpufreq_driver {
#define CPUFREQ_CONST_LOOPS 0x02 /* loops_per_jiffy or other kernel
* "constants" aren't affected by
* 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_unregister_driver(struct cpufreq_driver *driver_data);
Expand Down

0 comments on commit 42d4dc3

Please sign in to comment.