aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBenjamin Herrenschmidt <benh@kernel.crashing.org>2005-04-29 10:40:12 -0400
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-29 10:40:12 -0400
commit42d4dc3f4e1ec1396371aac89d0dccfdd977191b (patch)
treec5b67100cde9769c56f6872a5675d67f0e5f0df5
parentc60c390620e0abb60d4ae8c43583714bda27763f (diff)
[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>
-rw-r--r--drivers/cpufreq/cpufreq.c94
-rw-r--r--include/linux/cpufreq.h5
2 files changed, 93 insertions, 6 deletions
diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c
index b30001f31610..4fc0cb79f18f 100644
--- a/drivers/cpufreq/cpufreq.c
+++ b/drivers/cpufreq/cpufreq.c
@@ -223,7 +223,7 @@ static inline void adjust_jiffies(unsigned long val, struct cpufreq_freqs *ci)
223 } 223 }
224 if ((val == CPUFREQ_PRECHANGE && ci->old < ci->new) || 224 if ((val == CPUFREQ_PRECHANGE && ci->old < ci->new) ||
225 (val == CPUFREQ_POSTCHANGE && ci->old > ci->new) || 225 (val == CPUFREQ_POSTCHANGE && ci->old > ci->new) ||
226 (val == CPUFREQ_RESUMECHANGE)) { 226 (val == CPUFREQ_RESUMECHANGE || val == CPUFREQ_SUSPENDCHANGE)) {
227 loops_per_jiffy = cpufreq_scale(l_p_j_ref, l_p_j_ref_freq, ci->new); 227 loops_per_jiffy = cpufreq_scale(l_p_j_ref, l_p_j_ref_freq, ci->new);
228 dprintk("scaling loops_per_jiffy to %lu for frequency %u kHz\n", loops_per_jiffy, ci->new); 228 dprintk("scaling loops_per_jiffy to %lu for frequency %u kHz\n", loops_per_jiffy, ci->new);
229 } 229 }
@@ -866,11 +866,90 @@ EXPORT_SYMBOL(cpufreq_get);
866 866
867 867
868/** 868/**
869 * cpufreq_suspend - let the low level driver prepare for suspend
870 */
871
872static int cpufreq_suspend(struct sys_device * sysdev, u32 state)
873{
874 int cpu = sysdev->id;
875 unsigned int ret = 0;
876 unsigned int cur_freq = 0;
877 struct cpufreq_policy *cpu_policy;
878
879 dprintk("resuming cpu %u\n", cpu);
880
881 if (!cpu_online(cpu))
882 return 0;
883
884 /* we may be lax here as interrupts are off. Nonetheless
885 * we need to grab the correct cpu policy, as to check
886 * whether we really run on this CPU.
887 */
888
889 cpu_policy = cpufreq_cpu_get(cpu);
890 if (!cpu_policy)
891 return -EINVAL;
892
893 /* only handle each CPU group once */
894 if (unlikely(cpu_policy->cpu != cpu)) {
895 cpufreq_cpu_put(cpu_policy);
896 return 0;
897 }
898
899 if (cpufreq_driver->suspend) {
900 ret = cpufreq_driver->suspend(cpu_policy, state);
901 if (ret) {
902 printk(KERN_ERR "cpufreq: suspend failed in ->suspend "
903 "step on CPU %u\n", cpu_policy->cpu);
904 cpufreq_cpu_put(cpu_policy);
905 return ret;
906 }
907 }
908
909
910 if (cpufreq_driver->flags & CPUFREQ_CONST_LOOPS)
911 goto out;
912
913 if (cpufreq_driver->get)
914 cur_freq = cpufreq_driver->get(cpu_policy->cpu);
915
916 if (!cur_freq || !cpu_policy->cur) {
917 printk(KERN_ERR "cpufreq: suspend failed to assert current "
918 "frequency is what timing core thinks it is.\n");
919 goto out;
920 }
921
922 if (unlikely(cur_freq != cpu_policy->cur)) {
923 struct cpufreq_freqs freqs;
924
925 if (!(cpufreq_driver->flags & CPUFREQ_PM_NO_WARN))
926 printk(KERN_DEBUG "Warning: CPU frequency is %u, "
927 "cpufreq assumed %u kHz.\n",
928 cur_freq, cpu_policy->cur);
929
930 freqs.cpu = cpu;
931 freqs.old = cpu_policy->cur;
932 freqs.new = cur_freq;
933
934 notifier_call_chain(&cpufreq_transition_notifier_list,
935 CPUFREQ_SUSPENDCHANGE, &freqs);
936 adjust_jiffies(CPUFREQ_SUSPENDCHANGE, &freqs);
937
938 cpu_policy->cur = cur_freq;
939 }
940
941 out:
942 cpufreq_cpu_put(cpu_policy);
943 return 0;
944}
945
946/**
869 * cpufreq_resume - restore proper CPU frequency handling after resume 947 * cpufreq_resume - restore proper CPU frequency handling after resume
870 * 948 *
871 * 1.) resume CPUfreq hardware support (cpufreq_driver->resume()) 949 * 1.) resume CPUfreq hardware support (cpufreq_driver->resume())
872 * 2.) if ->target and !CPUFREQ_CONST_LOOPS: verify we're in sync 950 * 2.) if ->target and !CPUFREQ_CONST_LOOPS: verify we're in sync
873 * 3.) schedule call cpufreq_update_policy() ASAP as interrupts are restored. 951 * 3.) schedule call cpufreq_update_policy() ASAP as interrupts are
952 * restored.
874 */ 953 */
875static int cpufreq_resume(struct sys_device * sysdev) 954static int cpufreq_resume(struct sys_device * sysdev)
876{ 955{
@@ -915,7 +994,9 @@ static int cpufreq_resume(struct sys_device * sysdev)
915 cur_freq = cpufreq_driver->get(cpu_policy->cpu); 994 cur_freq = cpufreq_driver->get(cpu_policy->cpu);
916 995
917 if (!cur_freq || !cpu_policy->cur) { 996 if (!cur_freq || !cpu_policy->cur) {
918 printk(KERN_ERR "cpufreq: resume failed to assert current frequency is what timing core thinks it is.\n"); 997 printk(KERN_ERR "cpufreq: resume failed to assert "
998 "current frequency is what timing core "
999 "thinks it is.\n");
919 goto out; 1000 goto out;
920 } 1001 }
921 1002
@@ -923,13 +1004,15 @@ static int cpufreq_resume(struct sys_device * sysdev)
923 struct cpufreq_freqs freqs; 1004 struct cpufreq_freqs freqs;
924 1005
925 printk(KERN_WARNING "Warning: CPU frequency is %u, " 1006 printk(KERN_WARNING "Warning: CPU frequency is %u, "
926 "cpufreq assumed %u kHz.\n", cur_freq, cpu_policy->cur); 1007 "cpufreq assumed %u kHz.\n",
1008 cur_freq, cpu_policy->cur);
927 1009
928 freqs.cpu = cpu; 1010 freqs.cpu = cpu;
929 freqs.old = cpu_policy->cur; 1011 freqs.old = cpu_policy->cur;
930 freqs.new = cur_freq; 1012 freqs.new = cur_freq;
931 1013
932 notifier_call_chain(&cpufreq_transition_notifier_list, CPUFREQ_RESUMECHANGE, &freqs); 1014 notifier_call_chain(&cpufreq_transition_notifier_list,
1015 CPUFREQ_RESUMECHANGE, &freqs);
933 adjust_jiffies(CPUFREQ_RESUMECHANGE, &freqs); 1016 adjust_jiffies(CPUFREQ_RESUMECHANGE, &freqs);
934 1017
935 cpu_policy->cur = cur_freq; 1018 cpu_policy->cur = cur_freq;
@@ -945,6 +1028,7 @@ out:
945static struct sysdev_driver cpufreq_sysdev_driver = { 1028static struct sysdev_driver cpufreq_sysdev_driver = {
946 .add = cpufreq_add_dev, 1029 .add = cpufreq_add_dev,
947 .remove = cpufreq_remove_dev, 1030 .remove = cpufreq_remove_dev,
1031 .suspend = cpufreq_suspend,
948 .resume = cpufreq_resume, 1032 .resume = cpufreq_resume,
949}; 1033};
950 1034
diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h
index 910eca35583d..f21af067d015 100644
--- a/include/linux/cpufreq.h
+++ b/include/linux/cpufreq.h
@@ -103,6 +103,7 @@ struct cpufreq_policy {
103#define CPUFREQ_PRECHANGE (0) 103#define CPUFREQ_PRECHANGE (0)
104#define CPUFREQ_POSTCHANGE (1) 104#define CPUFREQ_POSTCHANGE (1)
105#define CPUFREQ_RESUMECHANGE (8) 105#define CPUFREQ_RESUMECHANGE (8)
106#define CPUFREQ_SUSPENDCHANGE (9)
106 107
107struct cpufreq_freqs { 108struct cpufreq_freqs {
108 unsigned int cpu; /* cpu nr */ 109 unsigned int cpu; /* cpu nr */
@@ -200,6 +201,7 @@ struct cpufreq_driver {
200 201
201 /* optional */ 202 /* optional */
202 int (*exit) (struct cpufreq_policy *policy); 203 int (*exit) (struct cpufreq_policy *policy);
204 int (*suspend) (struct cpufreq_policy *policy, u32 state);
203 int (*resume) (struct cpufreq_policy *policy); 205 int (*resume) (struct cpufreq_policy *policy);
204 struct freq_attr **attr; 206 struct freq_attr **attr;
205}; 207};
@@ -211,7 +213,8 @@ struct cpufreq_driver {
211#define CPUFREQ_CONST_LOOPS 0x02 /* loops_per_jiffy or other kernel 213#define CPUFREQ_CONST_LOOPS 0x02 /* loops_per_jiffy or other kernel
212 * "constants" aren't affected by 214 * "constants" aren't affected by
213 * frequency transitions */ 215 * frequency transitions */
214 216#define CPUFREQ_PM_NO_WARN 0x04 /* don't warn on suspend/resume speed
217 * mismatches */
215 218
216int cpufreq_register_driver(struct cpufreq_driver *driver_data); 219int cpufreq_register_driver(struct cpufreq_driver *driver_data);
217int cpufreq_unregister_driver(struct cpufreq_driver *driver_data); 220int cpufreq_unregister_driver(struct cpufreq_driver *driver_data);