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);
465