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 #define pr_fmt(fmt) "cpufreq: " fmt 16 17 #include <linux/kernel.h> 18 #include <linux/module.h> 19 #include <linux/moduleparam.h> 20 #include <linux/init.h> 21 #include <linux/cpufreq.h> 22 #include <linux/delay.h> 23 #include <linux/io.h> 24 #include <asm/ist.h> 25 #include <asm/cpu_device_id.h> 26 27 #include "speedstep-lib.h" 28 29 /* speedstep system management interface port/command. 30 * 31 * These parameters are got from IST-SMI BIOS call. 32 * If user gives it, these are used. 33 * 34 */ 35 static int smi_port; 36 static int smi_cmd; 37 static unsigned int smi_sig; 38 39 /* info about the processor */ 40 static enum speedstep_processor speedstep_processor; 41 42 /* 43 * There are only two frequency states for each processor. Values 44 * are in kHz for the time being. 45 */ 46 static struct cpufreq_frequency_table speedstep_freqs[] = { 47 {0, SPEEDSTEP_HIGH, 0}, 48 {0, SPEEDSTEP_LOW, 0}, 49 {0, 0, CPUFREQ_TABLE_END}, 50 }; 51 52 #define GET_SPEEDSTEP_OWNER 0 53 #define GET_SPEEDSTEP_STATE 1 54 #define SET_SPEEDSTEP_STATE 2 55 #define GET_SPEEDSTEP_FREQS 4 56 57 /* how often shall the SMI call be tried if it failed, e.g. because 58 * of DMA activity going on? */ 59 #define SMI_TRIES 5 60 61 /** 62 * speedstep_smi_ownership 63 */ 64 static 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 pr_debug("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 pr_debug("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 */ 102 static 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 pr_debug("bug #1422 -- can't read freqs from BIOS\n"); 110 return -ENODEV; 111 } 112 113 command = (smi_sig & 0xffffff00) | (smi_cmd & 0xff); 114 115 pr_debug("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 pr_debug("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_set_state - set the SpeedStep state 147 * @state: new processor frequency state (SPEEDSTEP_LOW or SPEEDSTEP_HIGH) 148 * 149 */ 150 static void speedstep_set_state(unsigned int state) 151 { 152 unsigned int result = 0, command, new_state, dummy; 153 unsigned long flags; 154 unsigned int function = SET_SPEEDSTEP_STATE; 155 unsigned int retry = 0; 156 157 if (state > 0x1) 158 return; 159 160 /* Disable IRQs */ 161 preempt_disable(); 162 local_irq_save(flags); 163 164 command = (smi_sig & 0xffffff00) | (smi_cmd & 0xff); 165 166 pr_debug("trying to set frequency to state %u " 167 "with command %x at port %x\n", 168 state, command, smi_port); 169 170 do { 171 if (retry) { 172 /* 173 * We need to enable interrupts, otherwise the blockage 174 * won't resolve. 175 * 176 * We disable preemption so that other processes don't 177 * run. If other processes were running, they could 178 * submit more DMA requests, making the blockage worse. 179 */ 180 pr_debug("retry %u, previous result %u, waiting...\n", 181 retry, result); 182 local_irq_enable(); 183 mdelay(retry * 50); 184 local_irq_disable(); 185 } 186 retry++; 187 __asm__ __volatile__( 188 "push %%ebp\n" 189 "out %%al, (%%dx)\n" 190 "pop %%ebp" 191 : "=b" (new_state), "=D" (result), 192 "=c" (dummy), "=a" (dummy), 193 "=d" (dummy), "=S" (dummy) 194 : "a" (command), "b" (function), "c" (state), 195 "d" (smi_port), "S" (0), "D" (0) 196 ); 197 } while ((new_state != state) && (retry <= SMI_TRIES)); 198 199 /* enable IRQs */ 200 local_irq_restore(flags); 201 preempt_enable(); 202 203 if (new_state == state) 204 pr_debug("change to %u MHz succeeded after %u tries " 205 "with result %u\n", 206 (speedstep_freqs[new_state].frequency / 1000), 207 retry, result); 208 else 209 pr_err("change to state %u failed with new_state %u and result %u\n", 210 state, new_state, result); 211 212 return; 213 } 214 215 216 /** 217 * speedstep_target - set a new CPUFreq policy 218 * @policy: new policy 219 * @index: index of new freq 220 * 221 * Sets a new CPUFreq policy/freq. 222 */ 223 static int speedstep_target(struct cpufreq_policy *policy, unsigned int index) 224 { 225 speedstep_set_state(index); 226 227 return 0; 228 } 229 230 231 static int speedstep_cpu_init(struct cpufreq_policy *policy) 232 { 233 int result; 234 unsigned int *low, *high; 235 236 /* capability check */ 237 if (policy->cpu != 0) 238 return -ENODEV; 239 240 result = speedstep_smi_ownership(); 241 if (result) { 242 pr_debug("fails in acquiring ownership of a SMI interface.\n"); 243 return -EINVAL; 244 } 245 246 /* detect low and high frequency */ 247 low = &speedstep_freqs[SPEEDSTEP_LOW].frequency; 248 high = &speedstep_freqs[SPEEDSTEP_HIGH].frequency; 249 250 result = speedstep_smi_get_freqs(low, high); 251 if (result) { 252 /* fall back to speedstep_lib.c dection mechanism: 253 * try both states out */ 254 pr_debug("could not detect low and high frequencies " 255 "by SMI call.\n"); 256 result = speedstep_get_freqs(speedstep_processor, 257 low, high, 258 NULL, 259 &speedstep_set_state); 260 261 if (result) { 262 pr_debug("could not detect two different speeds" 263 " -- aborting.\n"); 264 return result; 265 } else 266 pr_debug("workaround worked.\n"); 267 } 268 269 return cpufreq_table_validate_and_show(policy, speedstep_freqs); 270 } 271 272 static unsigned int speedstep_get(unsigned int cpu) 273 { 274 if (cpu) 275 return -ENODEV; 276 return speedstep_get_frequency(speedstep_processor); 277 } 278 279 280 static int speedstep_resume(struct cpufreq_policy *policy) 281 { 282 int result = speedstep_smi_ownership(); 283 284 if (result) 285 pr_debug("fails in re-acquiring ownership of a SMI interface.\n"); 286 287 return result; 288 } 289 290 static struct cpufreq_driver speedstep_driver = { 291 .name = "speedstep-smi", 292 .flags = CPUFREQ_NO_AUTO_DYNAMIC_SWITCHING, 293 .verify = cpufreq_generic_frequency_table_verify, 294 .target_index = speedstep_target, 295 .init = speedstep_cpu_init, 296 .get = speedstep_get, 297 .resume = speedstep_resume, 298 .attr = cpufreq_generic_attr, 299 }; 300 301 static const struct x86_cpu_id ss_smi_ids[] = { 302 { X86_VENDOR_INTEL, 6, 0xb, }, 303 { X86_VENDOR_INTEL, 6, 0x8, }, 304 { X86_VENDOR_INTEL, 15, 2 }, 305 {} 306 }; 307 #if 0 308 /* Not auto loaded currently */ 309 MODULE_DEVICE_TABLE(x86cpu, ss_smi_ids); 310 #endif 311 312 /** 313 * speedstep_init - initializes the SpeedStep CPUFreq driver 314 * 315 * Initializes the SpeedStep support. Returns -ENODEV on unsupported 316 * BIOS, -EINVAL on problems during initiatization, and zero on 317 * success. 318 */ 319 static int __init speedstep_init(void) 320 { 321 if (!x86_match_cpu(ss_smi_ids)) 322 return -ENODEV; 323 324 speedstep_processor = speedstep_detect_processor(); 325 326 switch (speedstep_processor) { 327 case SPEEDSTEP_CPU_PIII_T: 328 case SPEEDSTEP_CPU_PIII_C: 329 case SPEEDSTEP_CPU_PIII_C_EARLY: 330 break; 331 default: 332 speedstep_processor = 0; 333 } 334 335 if (!speedstep_processor) { 336 pr_debug("No supported Intel CPU detected.\n"); 337 return -ENODEV; 338 } 339 340 pr_debug("signature:0x%.8x, command:0x%.8x, " 341 "event:0x%.8x, perf_level:0x%.8x.\n", 342 ist_info.signature, ist_info.command, 343 ist_info.event, ist_info.perf_level); 344 345 /* Error if no IST-SMI BIOS or no PARM 346 sig= 'ISGE' aka 'Intel Speedstep Gate E' */ 347 if ((ist_info.signature != 0x47534943) && ( 348 (smi_port == 0) || (smi_cmd == 0))) 349 return -ENODEV; 350 351 if (smi_sig == 1) 352 smi_sig = 0x47534943; 353 else 354 smi_sig = ist_info.signature; 355 356 /* setup smi_port from MODLULE_PARM or BIOS */ 357 if ((smi_port > 0xff) || (smi_port < 0)) 358 return -EINVAL; 359 else if (smi_port == 0) 360 smi_port = ist_info.command & 0xff; 361 362 if ((smi_cmd > 0xff) || (smi_cmd < 0)) 363 return -EINVAL; 364 else if (smi_cmd == 0) 365 smi_cmd = (ist_info.command >> 16) & 0xff; 366 367 return cpufreq_register_driver(&speedstep_driver); 368 } 369 370 371 /** 372 * speedstep_exit - unregisters SpeedStep support 373 * 374 * Unregisters SpeedStep support. 375 */ 376 static void __exit speedstep_exit(void) 377 { 378 cpufreq_unregister_driver(&speedstep_driver); 379 } 380 381 module_param_hw(smi_port, int, ioport, 0444); 382 module_param(smi_cmd, int, 0444); 383 module_param(smi_sig, uint, 0444); 384 385 MODULE_PARM_DESC(smi_port, "Override the BIOS-given IST port with this value " 386 "-- Intel's default setting is 0xb2"); 387 MODULE_PARM_DESC(smi_cmd, "Override the BIOS-given IST command with this value " 388 "-- Intel's default setting is 0x82"); 389 MODULE_PARM_DESC(smi_sig, "Set to 1 to fake the IST signature when using the " 390 "SMI interface."); 391 392 MODULE_AUTHOR("Hiroshi Miura"); 393 MODULE_DESCRIPTION("Speedstep driver for IST applet SMI interface."); 394 MODULE_LICENSE("GPL"); 395 396 module_init(speedstep_init); 397 module_exit(speedstep_exit); 398