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