Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

[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>

authored by

Benjamin Herrenschmidt and committed by
Linus Torvalds
42d4dc3f c60c3906

+93 -6
+89 -5
drivers/cpufreq/cpufreq.c
··· 223 223 } 224 224 if ((val == CPUFREQ_PRECHANGE && ci->old < ci->new) || 225 225 (val == CPUFREQ_POSTCHANGE && ci->old > ci->new) || 226 - (val == CPUFREQ_RESUMECHANGE)) { 226 + (val == CPUFREQ_RESUMECHANGE || val == CPUFREQ_SUSPENDCHANGE)) { 227 227 loops_per_jiffy = cpufreq_scale(l_p_j_ref, l_p_j_ref_freq, ci->new); 228 228 dprintk("scaling loops_per_jiffy to %lu for frequency %u kHz\n", loops_per_jiffy, ci->new); 229 229 } ··· 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 947 * cpufreq_resume - restore proper CPU frequency handling after resume 870 948 * 871 949 * 1.) resume CPUfreq hardware support (cpufreq_driver->resume()) 872 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 954 static int cpufreq_resume(struct sys_device * sysdev) 876 955 { ··· 994 915 cur_freq = cpufreq_driver->get(cpu_policy->cpu); 995 916 996 917 if (!cur_freq || !cpu_policy->cur) { 997 - printk(KERN_ERR "cpufreq: resume failed to assert current frequency is what timing core thinks it is.\n"); 918 + printk(KERN_ERR "cpufreq: resume failed to assert " 919 + "current frequency is what timing core " 920 + "thinks it is.\n"); 998 921 goto out; 999 922 } 1000 923 ··· 1004 923 struct cpufreq_freqs freqs; 1005 924 1006 925 printk(KERN_WARNING "Warning: CPU frequency is %u, " 1007 - "cpufreq assumed %u kHz.\n", cur_freq, cpu_policy->cur); 926 + "cpufreq assumed %u kHz.\n", 927 + cur_freq, cpu_policy->cur); 1008 928 1009 929 freqs.cpu = cpu; 1010 930 freqs.old = cpu_policy->cur; 1011 931 freqs.new = cur_freq; 1012 932 1013 - notifier_call_chain(&cpufreq_transition_notifier_list, CPUFREQ_RESUMECHANGE, &freqs); 933 + notifier_call_chain(&cpufreq_transition_notifier_list, 934 + CPUFREQ_RESUMECHANGE, &freqs); 1014 935 adjust_jiffies(CPUFREQ_RESUMECHANGE, &freqs); 1015 936 1016 937 cpu_policy->cur = cur_freq; ··· 1028 945 static struct sysdev_driver cpufreq_sysdev_driver = { 1029 946 .add = cpufreq_add_dev, 1030 947 .remove = cpufreq_remove_dev, 948 + .suspend = cpufreq_suspend, 1031 949 .resume = cpufreq_resume, 1032 950 }; 1033 951
+4 -1
include/linux/cpufreq.h
··· 103 103 #define CPUFREQ_PRECHANGE (0) 104 104 #define CPUFREQ_POSTCHANGE (1) 105 105 #define CPUFREQ_RESUMECHANGE (8) 106 + #define CPUFREQ_SUSPENDCHANGE (9) 106 107 107 108 struct cpufreq_freqs { 108 109 unsigned int cpu; /* cpu nr */ ··· 201 200 202 201 /* optional */ 203 202 int (*exit) (struct cpufreq_policy *policy); 203 + int (*suspend) (struct cpufreq_policy *policy, u32 state); 204 204 int (*resume) (struct cpufreq_policy *policy); 205 205 struct freq_attr **attr; 206 206 }; ··· 213 211 #define CPUFREQ_CONST_LOOPS 0x02 /* loops_per_jiffy or other kernel 214 212 * "constants" aren't affected by 215 213 * frequency transitions */ 216 - 214 + #define CPUFREQ_PM_NO_WARN 0x04 /* don't warn on suspend/resume speed 215 + * mismatches */ 217 216 218 217 int cpufreq_register_driver(struct cpufreq_driver *driver_data); 219 218 int cpufreq_unregister_driver(struct cpufreq_driver *driver_data);