Merge git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6
[wrt350n-kernel.git] / arch / x86 / kernel / cpu / cpufreq / e_powersaver.c
blob969ad89dbd5d5877c1ceaf05aebf64096f9df155
1 /*
2 * Based on documentation provided by Dave Jones. Thanks!
4 * Licensed under the terms of the GNU GPL License version 2.
6 * BIG FAT DISCLAIMER: Work in progress code. Possibly *dangerous*
7 */
9 #include <linux/kernel.h>
10 #include <linux/module.h>
11 #include <linux/init.h>
12 #include <linux/cpufreq.h>
13 #include <linux/ioport.h>
14 #include <linux/slab.h>
16 #include <asm/msr.h>
17 #include <asm/tsc.h>
18 #include <asm/timex.h>
19 #include <asm/io.h>
20 #include <asm/delay.h>
22 #define EPS_BRAND_C7M 0
23 #define EPS_BRAND_C7 1
24 #define EPS_BRAND_EDEN 2
25 #define EPS_BRAND_C3 3
26 #define EPS_BRAND_C7D 4
28 struct eps_cpu_data {
29 u32 fsb;
30 struct cpufreq_frequency_table freq_table[];
33 static struct eps_cpu_data *eps_cpu[NR_CPUS];
36 static unsigned int eps_get(unsigned int cpu)
38 struct eps_cpu_data *centaur;
39 u32 lo, hi;
41 if (cpu)
42 return 0;
43 centaur = eps_cpu[cpu];
44 if (centaur == NULL)
45 return 0;
47 /* Return current frequency */
48 rdmsr(MSR_IA32_PERF_STATUS, lo, hi);
49 return centaur->fsb * ((lo >> 8) & 0xff);
52 static int eps_set_state(struct eps_cpu_data *centaur,
53 unsigned int cpu,
54 u32 dest_state)
56 struct cpufreq_freqs freqs;
57 u32 lo, hi;
58 <<<<<<< HEAD:arch/x86/kernel/cpu/cpufreq/e_powersaver.c
59 u8 current_multiplier, current_voltage;
60 =======
61 >>>>>>> 264e3e889d86e552b4191d69bb60f4f3b383135a:arch/x86/kernel/cpu/cpufreq/e_powersaver.c
62 int err = 0;
63 int i;
65 freqs.old = eps_get(cpu);
66 freqs.new = centaur->fsb * ((dest_state >> 8) & 0xff);
67 freqs.cpu = cpu;
68 cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
70 /* Wait while CPU is busy */
71 rdmsr(MSR_IA32_PERF_STATUS, lo, hi);
72 i = 0;
73 while (lo & ((1 << 16) | (1 << 17))) {
74 udelay(16);
75 rdmsr(MSR_IA32_PERF_STATUS, lo, hi);
76 i++;
77 if (unlikely(i > 64)) {
78 err = -ENODEV;
79 goto postchange;
82 /* Set new multiplier and voltage */
83 wrmsr(MSR_IA32_PERF_CTL, dest_state & 0xffff, 0);
84 /* Wait until transition end */
85 i = 0;
86 do {
87 udelay(16);
88 rdmsr(MSR_IA32_PERF_STATUS, lo, hi);
89 i++;
90 if (unlikely(i > 64)) {
91 err = -ENODEV;
92 goto postchange;
94 } while (lo & ((1 << 16) | (1 << 17)));
96 /* Return current frequency */
97 postchange:
98 rdmsr(MSR_IA32_PERF_STATUS, lo, hi);
99 freqs.new = centaur->fsb * ((lo >> 8) & 0xff);
101 <<<<<<< HEAD:arch/x86/kernel/cpu/cpufreq/e_powersaver.c
102 =======
103 #ifdef DEBUG
105 u8 current_multiplier, current_voltage;
107 >>>>>>> 264e3e889d86e552b4191d69bb60f4f3b383135a:arch/x86/kernel/cpu/cpufreq/e_powersaver.c
108 /* Print voltage and multiplier */
109 rdmsr(MSR_IA32_PERF_STATUS, lo, hi);
110 current_voltage = lo & 0xff;
111 printk(KERN_INFO "eps: Current voltage = %dmV\n",
112 current_voltage * 16 + 700);
113 current_multiplier = (lo >> 8) & 0xff;
114 printk(KERN_INFO "eps: Current multiplier = %d\n",
115 current_multiplier);
116 <<<<<<< HEAD:arch/x86/kernel/cpu/cpufreq/e_powersaver.c
118 =======
120 #endif
121 >>>>>>> 264e3e889d86e552b4191d69bb60f4f3b383135a:arch/x86/kernel/cpu/cpufreq/e_powersaver.c
122 cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
123 return err;
126 static int eps_target(struct cpufreq_policy *policy,
127 unsigned int target_freq,
128 unsigned int relation)
130 struct eps_cpu_data *centaur;
131 unsigned int newstate = 0;
132 unsigned int cpu = policy->cpu;
133 unsigned int dest_state;
134 int ret;
136 if (unlikely(eps_cpu[cpu] == NULL))
137 return -ENODEV;
138 centaur = eps_cpu[cpu];
140 if (unlikely(cpufreq_frequency_table_target(policy,
141 &eps_cpu[cpu]->freq_table[0],
142 target_freq,
143 relation,
144 &newstate))) {
145 return -EINVAL;
148 /* Make frequency transition */
149 dest_state = centaur->freq_table[newstate].index & 0xffff;
150 ret = eps_set_state(centaur, cpu, dest_state);
151 if (ret)
152 printk(KERN_ERR "eps: Timeout!\n");
153 return ret;
156 static int eps_verify(struct cpufreq_policy *policy)
158 return cpufreq_frequency_table_verify(policy,
159 &eps_cpu[policy->cpu]->freq_table[0]);
162 static int eps_cpu_init(struct cpufreq_policy *policy)
164 unsigned int i;
165 u32 lo, hi;
166 u64 val;
167 u8 current_multiplier, current_voltage;
168 u8 max_multiplier, max_voltage;
169 u8 min_multiplier, min_voltage;
170 u8 brand = 0;
171 u32 fsb;
172 struct eps_cpu_data *centaur;
173 struct cpuinfo_x86 *c = &cpu_data(0);
174 struct cpufreq_frequency_table *f_table;
175 int k, step, voltage;
176 int ret;
177 int states;
179 if (policy->cpu != 0)
180 return -ENODEV;
182 /* Check brand */
183 printk(KERN_INFO "eps: Detected VIA ");
185 switch (c->x86_model) {
186 case 10:
187 rdmsr(0x1153, lo, hi);
188 brand = (((lo >> 2) ^ lo) >> 18) & 3;
189 printk(KERN_CONT "Model A ");
190 break;
191 case 13:
192 rdmsr(0x1154, lo, hi);
193 brand = (((lo >> 4) ^ (lo >> 2))) & 0x000000ff;
194 printk(KERN_CONT "Model D ");
195 break;
198 switch(brand) {
199 case EPS_BRAND_C7M:
200 printk(KERN_CONT "C7-M\n");
201 break;
202 case EPS_BRAND_C7:
203 printk(KERN_CONT "C7\n");
204 break;
205 case EPS_BRAND_EDEN:
206 printk(KERN_CONT "Eden\n");
207 break;
208 case EPS_BRAND_C7D:
209 printk(KERN_CONT "C7-D\n");
210 break;
211 case EPS_BRAND_C3:
212 printk(KERN_CONT "C3\n");
213 return -ENODEV;
214 break;
216 /* Enable Enhanced PowerSaver */
217 rdmsrl(MSR_IA32_MISC_ENABLE, val);
218 if (!(val & 1 << 16)) {
219 val |= 1 << 16;
220 wrmsrl(MSR_IA32_MISC_ENABLE, val);
221 /* Can be locked at 0 */
222 rdmsrl(MSR_IA32_MISC_ENABLE, val);
223 if (!(val & 1 << 16)) {
224 printk(KERN_INFO "eps: Can't enable Enhanced PowerSaver\n");
225 return -ENODEV;
229 /* Print voltage and multiplier */
230 rdmsr(MSR_IA32_PERF_STATUS, lo, hi);
231 current_voltage = lo & 0xff;
232 printk(KERN_INFO "eps: Current voltage = %dmV\n", current_voltage * 16 + 700);
233 current_multiplier = (lo >> 8) & 0xff;
234 printk(KERN_INFO "eps: Current multiplier = %d\n", current_multiplier);
236 /* Print limits */
237 max_voltage = hi & 0xff;
238 printk(KERN_INFO "eps: Highest voltage = %dmV\n", max_voltage * 16 + 700);
239 max_multiplier = (hi >> 8) & 0xff;
240 printk(KERN_INFO "eps: Highest multiplier = %d\n", max_multiplier);
241 min_voltage = (hi >> 16) & 0xff;
242 printk(KERN_INFO "eps: Lowest voltage = %dmV\n", min_voltage * 16 + 700);
243 min_multiplier = (hi >> 24) & 0xff;
244 printk(KERN_INFO "eps: Lowest multiplier = %d\n", min_multiplier);
246 /* Sanity checks */
247 if (current_multiplier == 0 || max_multiplier == 0
248 || min_multiplier == 0)
249 return -EINVAL;
250 if (current_multiplier > max_multiplier
251 || max_multiplier <= min_multiplier)
252 return -EINVAL;
253 if (current_voltage > 0x1f || max_voltage > 0x1f)
254 return -EINVAL;
255 if (max_voltage < min_voltage)
256 return -EINVAL;
258 /* Calc FSB speed */
259 fsb = cpu_khz / current_multiplier;
260 /* Calc number of p-states supported */
261 if (brand == EPS_BRAND_C7M)
262 states = max_multiplier - min_multiplier + 1;
263 else
264 states = 2;
266 /* Allocate private data and frequency table for current cpu */
267 centaur = kzalloc(sizeof(struct eps_cpu_data)
268 + (states + 1) * sizeof(struct cpufreq_frequency_table),
269 GFP_KERNEL);
270 if (!centaur)
271 return -ENOMEM;
272 eps_cpu[0] = centaur;
274 /* Copy basic values */
275 centaur->fsb = fsb;
277 /* Fill frequency and MSR value table */
278 f_table = &centaur->freq_table[0];
279 if (brand != EPS_BRAND_C7M) {
280 f_table[0].frequency = fsb * min_multiplier;
281 f_table[0].index = (min_multiplier << 8) | min_voltage;
282 f_table[1].frequency = fsb * max_multiplier;
283 f_table[1].index = (max_multiplier << 8) | max_voltage;
284 f_table[2].frequency = CPUFREQ_TABLE_END;
285 } else {
286 k = 0;
287 step = ((max_voltage - min_voltage) * 256)
288 / (max_multiplier - min_multiplier);
289 for (i = min_multiplier; i <= max_multiplier; i++) {
290 voltage = (k * step) / 256 + min_voltage;
291 f_table[k].frequency = fsb * i;
292 f_table[k].index = (i << 8) | voltage;
293 k++;
295 f_table[k].frequency = CPUFREQ_TABLE_END;
298 policy->cpuinfo.transition_latency = 140000; /* 844mV -> 700mV in ns */
299 policy->cur = fsb * current_multiplier;
301 ret = cpufreq_frequency_table_cpuinfo(policy, &centaur->freq_table[0]);
302 if (ret) {
303 kfree(centaur);
304 return ret;
307 cpufreq_frequency_table_get_attr(&centaur->freq_table[0], policy->cpu);
308 return 0;
311 static int eps_cpu_exit(struct cpufreq_policy *policy)
313 unsigned int cpu = policy->cpu;
314 struct eps_cpu_data *centaur;
315 u32 lo, hi;
317 if (eps_cpu[cpu] == NULL)
318 return -ENODEV;
319 centaur = eps_cpu[cpu];
321 /* Get max frequency */
322 rdmsr(MSR_IA32_PERF_STATUS, lo, hi);
323 /* Set max frequency */
324 eps_set_state(centaur, cpu, hi & 0xffff);
325 /* Bye */
326 cpufreq_frequency_table_put_attr(policy->cpu);
327 kfree(eps_cpu[cpu]);
328 eps_cpu[cpu] = NULL;
329 return 0;
332 static struct freq_attr* eps_attr[] = {
333 &cpufreq_freq_attr_scaling_available_freqs,
334 NULL,
337 static struct cpufreq_driver eps_driver = {
338 .verify = eps_verify,
339 .target = eps_target,
340 .init = eps_cpu_init,
341 .exit = eps_cpu_exit,
342 .get = eps_get,
343 .name = "e_powersaver",
344 .owner = THIS_MODULE,
345 .attr = eps_attr,
348 static int __init eps_init(void)
350 struct cpuinfo_x86 *c = &cpu_data(0);
352 /* This driver will work only on Centaur C7 processors with
353 * Enhanced SpeedStep/PowerSaver registers */
354 if (c->x86_vendor != X86_VENDOR_CENTAUR
355 || c->x86 != 6 || c->x86_model < 10)
356 return -ENODEV;
357 if (!cpu_has(c, X86_FEATURE_EST))
358 return -ENODEV;
360 if (cpufreq_register_driver(&eps_driver))
361 return -EINVAL;
362 return 0;
365 static void __exit eps_exit(void)
367 cpufreq_unregister_driver(&eps_driver);
370 MODULE_AUTHOR("Rafa³ Bilski <rafalbilski@interia.pl>");
371 MODULE_DESCRIPTION("Enhanced PowerSaver driver for VIA C7 CPU's.");
372 MODULE_LICENSE("GPL");
374 module_init(eps_init);
375 module_exit(eps_exit);