Add suspend routine to core
PATCH 1/1 Add suspend routine to core
Hi !
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).
Here is the patch to the cpufreq core. If it's ok with everybody, please submit it asap to Andrew as my fixes depend on it. I would like those to get in 2.6.12.
drivers/cpufreq/cpufreq.c +107 -24
@@ -223,7 +223,7 @@ }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); }
@@ -866,16 +866,96 @@/** +* 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) {int cpu = sysdev->id;unsigned int ret =0 ; +unsigned int cur_freq =0 ;struct cpufreq_policy *cpu_policy;dprintk ("resuming cpu %u\n" , cpu);
@@ -908,32 +988,34 @@ } } -if (!(cpufreq_driver->flags &CPUFREQ_CONST_LOOPS )) { -unsigned int cur_freq =0 ; - -if (cpufreq_driver->get) - 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" ); - goto out; - } - -if (unlikely (cur_freq != cpu_policy->cur)) { -struct cpufreq_freqs freqs; +if (cpufreq_driver->flags &CPUFREQ_CONST_LOOPS ) + goto out; -printk (KERN_WARNING "Warning: CPU frequency is %u, " -"cpufreq assumed %u kHz.\n" , cur_freq, cpu_policy->cur); +if (cpufreq_driver->get) + cur_freq = cpufreq_driver->get (cpu_policy->cpu); - freqs.cpu = cpu; - freqs.old = cpu_policy->cur; - freqs.new = cur_freq; +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; + } -notifier_call_chain (&cpufreq_transition_notifier_list,CPUFREQ_RESUMECHANGE , &freqs); -adjust_jiffies (CPUFREQ_RESUMECHANGE , &freqs); +if (unlikely (cur_freq != cpu_policy->cur)) { +struct cpufreq_freqs freqs; - cpu_policy->cur = cur_freq; - } +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_RESUMECHANGE , &freqs); +adjust_jiffies (CPUFREQ_RESUMECHANGE , &freqs); + cpu_policy->cur = cur_freq; } out:
@@ -945,6 +1027,7 @@static struct sysdev_driver cpufreq_sysdev_driver = { .add = cpufreq_add_dev, .remove = cpufreq_remove_dev, + .suspend = cpufreq_suspend, .resume = cpufreq_resume, };
include/linux/cpufreq.h +4 -1
@@ -103,6 +103,7 @@#define CPUFREQ_PRECHANGE (0)#define CPUFREQ_POSTCHANGE (1)#define CPUFREQ_RESUMECHANGE (8) +#define CPUFREQ_SUSPENDCHANGE (9)struct cpufreq_freqs {unsigned int cpu;/* cpu nr */
@@ -200,6 +201,7 @@/* 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; };
@@ -211,7 +213,8 @@#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);
Quoted 7 lines · Benjamin Herrenschmidt
Hi !
In order to properly fix some issues with cpufreq vs. sleep on PowerBooks, I had to add a suspend callback to the
pmac_cpufreqdriver. I must force a switch to full speed before sleep and I switch back to previous speed on resume.
Quoted 5 lines
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).
.../...
No feedback at all ? Without any feedback next week, I'll submit myself to andrew ;)
Ben.
Hi.
Quoted 8 lines · Benjamin Herrenschmidt
Quoted 7 lines · Benjamin Herrenschmidt
Hi !
In order to properly fix some issues with cpufreq vs. sleep on PowerBooks, I had to add a suspend callback to the
pmac_cpufreqdriver. I must force a switch to full speed before sleep and I switch back to previous speed on resume.
Quoted 8 lines
Quoted 5 lines
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).
.../...
No feedback at all ? Without any feedback next week, I'll submit myself to andrew ;)
My feedback: Yay! (Not looking at or knowing about the details, just liking the concept).
Regards,
Nigel
Signature · 6 lines
Nigel Cunningham Software Engineer, Canberra, Australia http://www.cyclades.com Bus: +61 (2) 6291 9554; Hme: +61 (2) 6292 8028; Mob: +61 (417) 100 574 Maintainer of Suspend2 Kernel Patches http://suspend2.net