Merge branch 'x86-mm-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git...
[pandora-kernel.git] / drivers / cpufreq / speedstep-smi.c
1 /*
2  * Intel SpeedStep SMI driver.
3  *
4  * (C) 2003  Hiroshi Miura <miura@da-cha.org>
5  *
6  *  Licensed under the terms of the GNU GPL License version 2.
7  *
8  */
9
10
11 /*********************************************************************
12  *                        SPEEDSTEP - DEFINITIONS                    *
13  *********************************************************************/
14
15 #include <linux/kernel.h>
16 #include <linux/module.h>
17 #include <linux/moduleparam.h>
18 #include <linux/init.h>
19 #include <linux/cpufreq.h>
20 #include <linux/delay.h>
21 #include <linux/io.h>
22 #include <asm/ist.h>
23
24 #include "speedstep-lib.h"
25
26 /* speedstep system management interface port/command.
27  *
28  * These parameters are got from IST-SMI BIOS call.
29  * If user gives it, these are used.
30  *
31  */
32 static int smi_port;
33 static int smi_cmd;
34 static unsigned int smi_sig;
35
36 /* info about the processor */
37 static enum speedstep_processor speedstep_processor;
38
39 /*
40  * There are only two frequency states for each processor. Values
41  * are in kHz for the time being.
42  */
43 static struct cpufreq_frequency_table speedstep_freqs[] = {
44         {SPEEDSTEP_HIGH,        0},
45         {SPEEDSTEP_LOW,         0},
46         {0,                     CPUFREQ_TABLE_END},
47 };
48
49 #define GET_SPEEDSTEP_OWNER 0
50 #define GET_SPEEDSTEP_STATE 1
51 #define SET_SPEEDSTEP_STATE 2
52 #define GET_SPEEDSTEP_FREQS 4
53
54 /* how often shall the SMI call be tried if it failed, e.g. because
55  * of DMA activity going on? */
56 #define SMI_TRIES 5
57
58 /**
59  * speedstep_smi_ownership
60  */
61 static int speedstep_smi_ownership(void)
62 {
63         u32 command, result, magic, dummy;
64         u32 function = GET_SPEEDSTEP_OWNER;
65         unsigned char magic_data[] = "Copyright (c) 1999 Intel Corporation";
66
67         command = (smi_sig & 0xffffff00) | (smi_cmd & 0xff);
68         magic = virt_to_phys(magic_data);
69
70         pr_debug("trying to obtain ownership with command %x at port %x\n",
71                         command, smi_port);
72
73         __asm__ __volatile__(
74                 "push %%ebp\n"
75                 "out %%al, (%%dx)\n"
76                 "pop %%ebp\n"
77                 : "=D" (result),
78                   "=a" (dummy), "=b" (dummy), "=c" (dummy), "=d" (dummy),
79                   "=S" (dummy)
80                 : "a" (command), "b" (function), "c" (0), "d" (smi_port),
81                   "D" (0), "S" (magic)
82                 : "memory"
83         );
84
85         pr_debug("result is %x\n", result);
86
87         return result;
88 }
89
90 /**
91  * speedstep_smi_get_freqs - get SpeedStep preferred & current freq.
92  * @low: the low frequency value is placed here
93  * @high: the high frequency value is placed here
94  *
95  * Only available on later SpeedStep-enabled systems, returns false results or
96  * even hangs [cf. bugme.osdl.org # 1422] on earlier systems. Empirical testing
97  * shows that the latter occurs if !(ist_info.event & 0xFFFF).
98  */
99 static int speedstep_smi_get_freqs(unsigned int *low, unsigned int *high)
100 {
101         u32 command, result = 0, edi, high_mhz, low_mhz, dummy;
102         u32 state = 0;
103         u32 function = GET_SPEEDSTEP_FREQS;
104
105         if (!(ist_info.event & 0xFFFF)) {
106                 pr_debug("bug #1422 -- can't read freqs from BIOS\n");
107                 return -ENODEV;
108         }
109
110         command = (smi_sig & 0xffffff00) | (smi_cmd & 0xff);
111
112         pr_debug("trying to determine frequencies with command %x at port %x\n",
113                         command, smi_port);
114
115         __asm__ __volatile__(
116                 "push %%ebp\n"
117                 "out %%al, (%%dx)\n"
118                 "pop %%ebp"
119                 : "=a" (result),
120                   "=b" (high_mhz),
121                   "=c" (low_mhz),
122                   "=d" (state), "=D" (edi), "=S" (dummy)
123                 : "a" (command),
124                   "b" (function),
125                   "c" (state),
126                   "d" (smi_port), "S" (0), "D" (0)
127         );
128
129         pr_debug("result %x, low_freq %u, high_freq %u\n",
130                         result, low_mhz, high_mhz);
131
132         /* abort if results are obviously incorrect... */
133         if ((high_mhz + low_mhz) < 600)
134                 return -EINVAL;
135
136         *high = high_mhz * 1000;
137         *low  = low_mhz  * 1000;
138
139         return result;
140 }
141
142 /**
143  * speedstep_get_state - set the SpeedStep state
144  * @state: processor frequency state (SPEEDSTEP_LOW or SPEEDSTEP_HIGH)
145  *
146  */
147 static int speedstep_get_state(void)
148 {
149         u32 function = GET_SPEEDSTEP_STATE;
150         u32 result, state, edi, command, dummy;
151
152         command = (smi_sig & 0xffffff00) | (smi_cmd & 0xff);
153
154         pr_debug("trying to determine current setting with command %x "
155                 "at port %x\n", command, smi_port);
156
157         __asm__ __volatile__(
158                 "push %%ebp\n"
159                 "out %%al, (%%dx)\n"
160                 "pop %%ebp\n"
161                 : "=a" (result),
162                   "=b" (state), "=D" (edi),
163                   "=c" (dummy), "=d" (dummy), "=S" (dummy)
164                 : "a" (command), "b" (function), "c" (0),
165                   "d" (smi_port), "S" (0), "D" (0)
166         );
167
168         pr_debug("state is %x, result is %x\n", state, result);
169
170         return state & 1;
171 }
172
173
174 /**
175  * speedstep_set_state - set the SpeedStep state
176  * @state: new processor frequency state (SPEEDSTEP_LOW or SPEEDSTEP_HIGH)
177  *
178  */
179 static void speedstep_set_state(unsigned int state)
180 {
181         unsigned int result = 0, command, new_state, dummy;
182         unsigned long flags;
183         unsigned int function = SET_SPEEDSTEP_STATE;
184         unsigned int retry = 0;
185
186         if (state > 0x1)
187                 return;
188
189         /* Disable IRQs */
190         local_irq_save(flags);
191
192         command = (smi_sig & 0xffffff00) | (smi_cmd & 0xff);
193
194         pr_debug("trying to set frequency to state %u "
195                 "with command %x at port %x\n",
196                 state, command, smi_port);
197
198         do {
199                 if (retry) {
200                         pr_debug("retry %u, previous result %u, waiting...\n",
201                                         retry, result);
202                         mdelay(retry * 50);
203                 }
204                 retry++;
205                 __asm__ __volatile__(
206                         "push %%ebp\n"
207                         "out %%al, (%%dx)\n"
208                         "pop %%ebp"
209                         : "=b" (new_state), "=D" (result),
210                           "=c" (dummy), "=a" (dummy),
211                           "=d" (dummy), "=S" (dummy)
212                         : "a" (command), "b" (function), "c" (state),
213                           "d" (smi_port), "S" (0), "D" (0)
214                         );
215         } while ((new_state != state) && (retry <= SMI_TRIES));
216
217         /* enable IRQs */
218         local_irq_restore(flags);
219
220         if (new_state == state)
221                 pr_debug("change to %u MHz succeeded after %u tries "
222                         "with result %u\n",
223                         (speedstep_freqs[new_state].frequency / 1000),
224                         retry, result);
225         else
226                 printk(KERN_ERR "cpufreq: change to state %u "
227                         "failed with new_state %u and result %u\n",
228                         state, new_state, result);
229
230         return;
231 }
232
233
234 /**
235  * speedstep_target - set a new CPUFreq policy
236  * @policy: new policy
237  * @target_freq: new freq
238  * @relation:
239  *
240  * Sets a new CPUFreq policy/freq.
241  */
242 static int speedstep_target(struct cpufreq_policy *policy,
243                         unsigned int target_freq, unsigned int relation)
244 {
245         unsigned int newstate = 0;
246         struct cpufreq_freqs freqs;
247
248         if (cpufreq_frequency_table_target(policy, &speedstep_freqs[0],
249                                 target_freq, relation, &newstate))
250                 return -EINVAL;
251
252         freqs.old = speedstep_freqs[speedstep_get_state()].frequency;
253         freqs.new = speedstep_freqs[newstate].frequency;
254         freqs.cpu = 0; /* speedstep.c is UP only driver */
255
256         if (freqs.old == freqs.new)
257                 return 0;
258
259         cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
260         speedstep_set_state(newstate);
261         cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
262
263         return 0;
264 }
265
266
267 /**
268  * speedstep_verify - verifies a new CPUFreq policy
269  * @policy: new policy
270  *
271  * Limit must be within speedstep_low_freq and speedstep_high_freq, with
272  * at least one border included.
273  */
274 static int speedstep_verify(struct cpufreq_policy *policy)
275 {
276         return cpufreq_frequency_table_verify(policy, &speedstep_freqs[0]);
277 }
278
279
280 static int speedstep_cpu_init(struct cpufreq_policy *policy)
281 {
282         int result;
283         unsigned int speed, state;
284         unsigned int *low, *high;
285
286         /* capability check */
287         if (policy->cpu != 0)
288                 return -ENODEV;
289
290         result = speedstep_smi_ownership();
291         if (result) {
292                 pr_debug("fails in acquiring ownership of a SMI interface.\n");
293                 return -EINVAL;
294         }
295
296         /* detect low and high frequency */
297         low = &speedstep_freqs[SPEEDSTEP_LOW].frequency;
298         high = &speedstep_freqs[SPEEDSTEP_HIGH].frequency;
299
300         result = speedstep_smi_get_freqs(low, high);
301         if (result) {
302                 /* fall back to speedstep_lib.c dection mechanism:
303                  * try both states out */
304                 pr_debug("could not detect low and high frequencies "
305                                 "by SMI call.\n");
306                 result = speedstep_get_freqs(speedstep_processor,
307                                 low, high,
308                                 NULL,
309                                 &speedstep_set_state);
310
311                 if (result) {
312                         pr_debug("could not detect two different speeds"
313                                         " -- aborting.\n");
314                         return result;
315                 } else
316                         pr_debug("workaround worked.\n");
317         }
318
319         /* get current speed setting */
320         state = speedstep_get_state();
321         speed = speedstep_freqs[state].frequency;
322
323         pr_debug("currently at %s speed setting - %i MHz\n",
324                 (speed == speedstep_freqs[SPEEDSTEP_LOW].frequency)
325                 ? "low" : "high",
326                 (speed / 1000));
327
328         /* cpuinfo and default policy values */
329         policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL;
330         policy->cur = speed;
331
332         result = cpufreq_frequency_table_cpuinfo(policy, speedstep_freqs);
333         if (result)
334                 return result;
335
336         cpufreq_frequency_table_get_attr(speedstep_freqs, policy->cpu);
337
338         return 0;
339 }
340
341 static int speedstep_cpu_exit(struct cpufreq_policy *policy)
342 {
343         cpufreq_frequency_table_put_attr(policy->cpu);
344         return 0;
345 }
346
347 static unsigned int speedstep_get(unsigned int cpu)
348 {
349         if (cpu)
350                 return -ENODEV;
351         return speedstep_get_frequency(speedstep_processor);
352 }
353
354
355 static int speedstep_resume(struct cpufreq_policy *policy)
356 {
357         int result = speedstep_smi_ownership();
358
359         if (result)
360                 pr_debug("fails in re-acquiring ownership of a SMI interface.\n");
361
362         return result;
363 }
364
365 static struct freq_attr *speedstep_attr[] = {
366         &cpufreq_freq_attr_scaling_available_freqs,
367         NULL,
368 };
369
370 static struct cpufreq_driver speedstep_driver = {
371         .name           = "speedstep-smi",
372         .verify         = speedstep_verify,
373         .target         = speedstep_target,
374         .init           = speedstep_cpu_init,
375         .exit           = speedstep_cpu_exit,
376         .get            = speedstep_get,
377         .resume         = speedstep_resume,
378         .owner          = THIS_MODULE,
379         .attr           = speedstep_attr,
380 };
381
382 /**
383  * speedstep_init - initializes the SpeedStep CPUFreq driver
384  *
385  *   Initializes the SpeedStep support. Returns -ENODEV on unsupported
386  * BIOS, -EINVAL on problems during initiatization, and zero on
387  * success.
388  */
389 static int __init speedstep_init(void)
390 {
391         speedstep_processor = speedstep_detect_processor();
392
393         switch (speedstep_processor) {
394         case SPEEDSTEP_CPU_PIII_T:
395         case SPEEDSTEP_CPU_PIII_C:
396         case SPEEDSTEP_CPU_PIII_C_EARLY:
397                 break;
398         default:
399                 speedstep_processor = 0;
400         }
401
402         if (!speedstep_processor) {
403                 pr_debug("No supported Intel CPU detected.\n");
404                 return -ENODEV;
405         }
406
407         pr_debug("signature:0x%.8ulx, command:0x%.8ulx, "
408                 "event:0x%.8ulx, perf_level:0x%.8ulx.\n",
409                 ist_info.signature, ist_info.command,
410                 ist_info.event, ist_info.perf_level);
411
412         /* Error if no IST-SMI BIOS or no PARM
413                  sig= 'ISGE' aka 'Intel Speedstep Gate E' */
414         if ((ist_info.signature !=  0x47534943) && (
415             (smi_port == 0) || (smi_cmd == 0)))
416                 return -ENODEV;
417
418         if (smi_sig == 1)
419                 smi_sig = 0x47534943;
420         else
421                 smi_sig = ist_info.signature;
422
423         /* setup smi_port from MODLULE_PARM or BIOS */
424         if ((smi_port > 0xff) || (smi_port < 0))
425                 return -EINVAL;
426         else if (smi_port == 0)
427                 smi_port = ist_info.command & 0xff;
428
429         if ((smi_cmd > 0xff) || (smi_cmd < 0))
430                 return -EINVAL;
431         else if (smi_cmd == 0)
432                 smi_cmd = (ist_info.command >> 16) & 0xff;
433
434         return cpufreq_register_driver(&speedstep_driver);
435 }
436
437
438 /**
439  * speedstep_exit - unregisters SpeedStep support
440  *
441  *   Unregisters SpeedStep support.
442  */
443 static void __exit speedstep_exit(void)
444 {
445         cpufreq_unregister_driver(&speedstep_driver);
446 }
447
448 module_param(smi_port, int, 0444);
449 module_param(smi_cmd,  int, 0444);
450 module_param(smi_sig, uint, 0444);
451
452 MODULE_PARM_DESC(smi_port, "Override the BIOS-given IST port with this value "
453                 "-- Intel's default setting is 0xb2");
454 MODULE_PARM_DESC(smi_cmd, "Override the BIOS-given IST command with this value "
455                 "-- Intel's default setting is 0x82");
456 MODULE_PARM_DESC(smi_sig, "Set to 1 to fake the IST signature when using the "
457                 "SMI interface.");
458
459 MODULE_AUTHOR("Hiroshi Miura");
460 MODULE_DESCRIPTION("Speedstep driver for IST applet SMI interface.");
461 MODULE_LICENSE("GPL");
462
463 module_init(speedstep_init);
464 module_exit(speedstep_exit);