diff options
author | Benjamin Herrenschmidt <benh@kernel.crashing.org> | 2005-04-29 10:40:12 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-29 10:40:12 -0400 |
commit | 42d4dc3f4e1ec1396371aac89d0dccfdd977191b (patch) | |
tree | c5b67100cde9769c56f6872a5675d67f0e5f0df5 | |
parent | c60c390620e0abb60d4ae8c43583714bda27763f (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.c | 94 | ||||
-rw-r--r-- | include/linux/cpufreq.h | 5 |
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 | |||
872 | static 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 | */ |
875 | static int cpufreq_resume(struct sys_device * sysdev) | 954 | static 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: | |||
945 | static struct sysdev_driver cpufreq_sysdev_driver = { | 1028 | static 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 | ||
107 | struct cpufreq_freqs { | 108 | struct 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 | ||
216 | int cpufreq_register_driver(struct cpufreq_driver *driver_data); | 219 | int cpufreq_register_driver(struct cpufreq_driver *driver_data); |
217 | int cpufreq_unregister_driver(struct cpufreq_driver *driver_data); | 220 | int cpufreq_unregister_driver(struct cpufreq_driver *driver_data); |