1
2
3
4
5
6
7
8
9
10
11
12
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/slab.h>
21#include <linux/delay.h>
22#include <asm/ist.h>
23#include <asm/io.h>
24
25#include "speedstep-lib.h"
26
27
28
29
30
31
32
33static int smi_port = 0;
34static int smi_cmd = 0;
35static unsigned int smi_sig = 0;
36
37
38static unsigned int speedstep_processor = 0;
39
40
41
42
43
44static struct cpufreq_frequency_table speedstep_freqs[] = {
45 {SPEEDSTEP_HIGH, 0},
46 {SPEEDSTEP_LOW, 0},
47 {0, CPUFREQ_TABLE_END},
48};
49
50#define GET_SPEEDSTEP_OWNER 0
51#define GET_SPEEDSTEP_STATE 1
52#define SET_SPEEDSTEP_STATE 2
53#define GET_SPEEDSTEP_FREQS 4
54
55
56
57#define SMI_TRIES 5
58
59#define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_DRIVER, "speedstep-smi", msg)
60
61
62
63
64static int speedstep_smi_ownership (void)
65{
66 u32 command, result, magic, dummy;
67 u32 function = GET_SPEEDSTEP_OWNER;
68 unsigned char magic_data[] = "Copyright (c) 1999 Intel Corporation";
69
70 command = (smi_sig & 0xffffff00) | (smi_cmd & 0xff);
71 magic = virt_to_phys(magic_data);
72
73 dprintk("trying to obtain ownership with command %x at port %x\n", command, smi_port);
74
75 __asm__ __volatile__(
76 "push %%ebp\n"
77 "out %%al, (%%dx)\n"
78 "pop %%ebp\n"
79 : "=D" (result), "=a" (dummy), "=b" (dummy), "=c" (dummy), "=d" (dummy),
80 "=S" (dummy)
81 : "a" (command), "b" (function), "c" (0), "d" (smi_port),
82 "D" (0), "S" (magic)
83 : "memory"
84 );
85
86 dprintk("result is %x\n", result);
87
88 return result;
89}
90
91
92
93
94
95
96
97
98
99
100static int speedstep_smi_get_freqs (unsigned int *low, unsigned int *high)
101{
102 u32 command, result = 0, edi, high_mhz, low_mhz, dummy;
103 u32 state=0;
104 u32 function = GET_SPEEDSTEP_FREQS;
105
106 if (!(ist_info.event & 0xFFFF)) {
107 dprintk("bug #1422 -- can't read freqs from BIOS\n");
108 return -ENODEV;
109 }
110
111 command = (smi_sig & 0xffffff00) | (smi_cmd & 0xff);
112
113 dprintk("trying to determine frequencies with command %x at port %x\n", command, smi_port);
114
115 __asm__ __volatile__(
116 "push %%ebp\n"
117 "out %%al, (%%dx)\n"
118 "pop %%ebp"
119 : "=a" (result), "=b" (high_mhz), "=c" (low_mhz), "=d" (state), "=D" (edi), "=S" (dummy)
120 : "a" (command), "b" (function), "c" (state), "d" (smi_port), "S" (0), "D" (0)
121 );
122
123 dprintk("result %x, low_freq %u, high_freq %u\n", result, low_mhz, high_mhz);
124
125
126 if ((high_mhz + low_mhz) < 600)
127 return -EINVAL;
128
129 *high = high_mhz * 1000;
130 *low = low_mhz * 1000;
131
132 return result;
133}
134
135
136
137
138
139
140static int speedstep_get_state (void)
141{
142 u32 function=GET_SPEEDSTEP_STATE;
143 u32 result, state, edi, command, dummy;
144
145 command = (smi_sig & 0xffffff00) | (smi_cmd & 0xff);
146
147 dprintk("trying to determine current setting with command %x at port %x\n", command, smi_port);
148
149 __asm__ __volatile__(
150 "push %%ebp\n"
151 "out %%al, (%%dx)\n"
152 "pop %%ebp\n"
153 : "=a" (result), "=b" (state), "=D" (edi), "=c" (dummy), "=d" (dummy), "=S" (dummy)
154 : "a" (command), "b" (function), "c" (0), "d" (smi_port), "S" (0), "D" (0)
155 );
156
157 dprintk("state is %x, result is %x\n", state, result);
158
159 return (state & 1);
160}
161
162
163
164
165
166
167
168static void speedstep_set_state (unsigned int state)
169{
170 unsigned int result = 0, command, new_state, dummy;
171 unsigned long flags;
172 unsigned int function=SET_SPEEDSTEP_STATE;
173 unsigned int retry = 0;
174
175 if (state > 0x1)
176 return;
177
178
179 local_irq_save(flags);
180
181 command = (smi_sig & 0xffffff00) | (smi_cmd & 0xff);
182
183 dprintk("trying to set frequency to state %u with command %x at port %x\n", state, command, smi_port);
184
185 do {
186 if (retry) {
187 dprintk("retry %u, previous result %u, waiting...\n", retry, result);
188 mdelay(retry * 50);
189 }
190 retry++;
191 __asm__ __volatile__(
192 "push %%ebp\n"
193 "out %%al, (%%dx)\n"
194 "pop %%ebp"
195 : "=b" (new_state), "=D" (result), "=c" (dummy), "=a" (dummy),
196 "=d" (dummy), "=S" (dummy)
197 : "a" (command), "b" (function), "c" (state), "d" (smi_port), "S" (0), "D" (0)
198 );
199 } while ((new_state != state) && (retry <= SMI_TRIES));
200
201
202 local_irq_restore(flags);
203
204 if (new_state == state) {
205 dprintk("change to %u MHz succeeded after %u tries with result %u\n", (speedstep_freqs[new_state].frequency / 1000), retry, result);
206 } else {
207 printk(KERN_ERR "cpufreq: change to state %u failed with new_state %u and result %u\n", state, new_state, result);
208 }
209
210 return;
211}
212
213
214
215
216
217
218
219
220
221
222static int speedstep_target (struct cpufreq_policy *policy,
223 unsigned int target_freq, unsigned int relation)
224{
225 unsigned int newstate = 0;
226 struct cpufreq_freqs freqs;
227
228 if (cpufreq_frequency_table_target(policy, &speedstep_freqs[0], target_freq, relation, &newstate))
229 return -EINVAL;
230
231 freqs.old = speedstep_freqs[speedstep_get_state()].frequency;
232 freqs.new = speedstep_freqs[newstate].frequency;
233 freqs.cpu = 0;
234
235 if (freqs.old == freqs.new)
236 return 0;
237
238 cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
239 speedstep_set_state(newstate);
240 cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
241
242 return 0;
243}
244
245
246
247
248
249
250
251
252
253static int speedstep_verify (struct cpufreq_policy *policy)
254{
255 return cpufreq_frequency_table_verify(policy, &speedstep_freqs[0]);
256}
257
258
259static int speedstep_cpu_init(struct cpufreq_policy *policy)
260{
261 int result;
262 unsigned int speed,state;
263
264
265 if (policy->cpu != 0)
266 return -ENODEV;
267
268 result = speedstep_smi_ownership();
269 if (result) {
270 dprintk("fails in aquiring ownership of a SMI interface.\n");
271 return -EINVAL;
272 }
273
274
275 result = speedstep_smi_get_freqs(&speedstep_freqs[SPEEDSTEP_LOW].frequency,
276 &speedstep_freqs[SPEEDSTEP_HIGH].frequency);
277 if (result) {
278
279 dprintk("could not detect low and high frequencies by SMI call.\n");
280 result = speedstep_get_freqs(speedstep_processor,
281 &speedstep_freqs[SPEEDSTEP_LOW].frequency,
282 &speedstep_freqs[SPEEDSTEP_HIGH].frequency,
283 NULL,
284 &speedstep_set_state);
285
286 if (result) {
287 dprintk("could not detect two different speeds -- aborting.\n");
288 return result;
289 } else
290 dprintk("workaround worked.\n");
291 }
292
293
294 state = speedstep_get_state();
295 speed = speedstep_freqs[state].frequency;
296
297 dprintk("currently at %s speed setting - %i MHz\n",
298 (speed == speedstep_freqs[SPEEDSTEP_LOW].frequency) ? "low" : "high",
299 (speed / 1000));
300
301
302 policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL;
303 policy->cur = speed;
304
305 result = cpufreq_frequency_table_cpuinfo(policy, speedstep_freqs);
306 if (result)
307 return (result);
308
309 cpufreq_frequency_table_get_attr(speedstep_freqs, policy->cpu);
310
311 return 0;
312}
313
314static int speedstep_cpu_exit(struct cpufreq_policy *policy)
315{
316 cpufreq_frequency_table_put_attr(policy->cpu);
317 return 0;
318}
319
320static unsigned int speedstep_get(unsigned int cpu)
321{
322 if (cpu)
323 return -ENODEV;
324 return speedstep_get_processor_frequency(speedstep_processor);
325}
326
327
328static int speedstep_resume(struct cpufreq_policy *policy)
329{
330 int result = speedstep_smi_ownership();
331
332 if (result)
333 dprintk("fails in re-aquiring ownership of a SMI interface.\n");
334
335 return result;
336}
337
338static struct freq_attr* speedstep_attr[] = {
339 &cpufreq_freq_attr_scaling_available_freqs,
340 NULL,
341};
342
343static struct cpufreq_driver speedstep_driver = {
344 .name = "speedstep-smi",
345 .verify = speedstep_verify,
346 .target = speedstep_target,
347 .init = speedstep_cpu_init,
348 .exit = speedstep_cpu_exit,
349 .get = speedstep_get,
350 .resume = speedstep_resume,
351 .owner = THIS_MODULE,
352 .attr = speedstep_attr,
353};
354
355
356
357
358
359
360
361
362static int __init speedstep_init(void)
363{
364 speedstep_processor = speedstep_detect_processor();
365
366 switch (speedstep_processor) {
367 case SPEEDSTEP_PROCESSOR_PIII_T:
368 case SPEEDSTEP_PROCESSOR_PIII_C:
369 case SPEEDSTEP_PROCESSOR_PIII_C_EARLY:
370 break;
371 default:
372 speedstep_processor = 0;
373 }
374
375 if (!speedstep_processor) {
376 dprintk ("No supported Intel CPU detected.\n");
377 return -ENODEV;
378 }
379
380 dprintk("signature:0x%.8lx, command:0x%.8lx, event:0x%.8lx, perf_level:0x%.8lx.\n",
381 ist_info.signature, ist_info.command, ist_info.event, ist_info.perf_level);
382
383
384
385 if ((ist_info.signature != 0x47534943) && (
386 (smi_port == 0) || (smi_cmd == 0)))
387 return -ENODEV;
388
389 if (smi_sig == 1)
390 smi_sig = 0x47534943;
391 else
392 smi_sig = ist_info.signature;
393
394
395 if ((smi_port > 0xff) || (smi_port < 0))
396 return -EINVAL;
397 else if (smi_port == 0)
398 smi_port = ist_info.command & 0xff;
399
400 if ((smi_cmd > 0xff) || (smi_cmd < 0))
401 return -EINVAL;
402 else if (smi_cmd == 0)
403 smi_cmd = (ist_info.command >> 16) & 0xff;
404
405 return cpufreq_register_driver(&speedstep_driver);
406}
407
408
409
410
411
412
413
414static void __exit speedstep_exit(void)
415{
416 cpufreq_unregister_driver(&speedstep_driver);
417}
418
419module_param(smi_port, int, 0444);
420module_param(smi_cmd, int, 0444);
421module_param(smi_sig, uint, 0444);
422
423MODULE_PARM_DESC(smi_port, "Override the BIOS-given IST port with this value -- Intel's default setting is 0xb2");
424MODULE_PARM_DESC(smi_cmd, "Override the BIOS-given IST command with this value -- Intel's default setting is 0x82");
425MODULE_PARM_DESC(smi_sig, "Set to 1 to fake the IST signature when using the SMI interface.");
426
427MODULE_AUTHOR ("Hiroshi Miura");
428MODULE_DESCRIPTION ("Speedstep driver for IST applet SMI interface.");
429MODULE_LICENSE ("GPL");
430
431module_init(speedstep_init);
432module_exit(speedstep_exit);
433