0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013 #define pr_fmt(fmt) "cpufreq: " fmt
0014
0015 #include <linux/kernel.h>
0016 #include <linux/module.h>
0017 #include <linux/moduleparam.h>
0018 #include <linux/init.h>
0019 #include <linux/cpufreq.h>
0020 #include <linux/delay.h>
0021 #include <linux/io.h>
0022 #include <asm/ist.h>
0023 #include <asm/cpu_device_id.h>
0024
0025 #include "speedstep-lib.h"
0026
0027
0028
0029
0030
0031
0032
0033 static int smi_port;
0034 static int smi_cmd;
0035 static unsigned int smi_sig;
0036
0037
0038 static enum speedstep_processor speedstep_processor;
0039
0040
0041
0042
0043
0044 static struct cpufreq_frequency_table speedstep_freqs[] = {
0045 {0, SPEEDSTEP_HIGH, 0},
0046 {0, SPEEDSTEP_LOW, 0},
0047 {0, 0, CPUFREQ_TABLE_END},
0048 };
0049
0050 #define GET_SPEEDSTEP_OWNER 0
0051 #define GET_SPEEDSTEP_STATE 1
0052 #define SET_SPEEDSTEP_STATE 2
0053 #define GET_SPEEDSTEP_FREQS 4
0054
0055
0056
0057 #define SMI_TRIES 5
0058
0059
0060
0061
0062 static int speedstep_smi_ownership(void)
0063 {
0064 u32 command, result, magic, dummy;
0065 u32 function = GET_SPEEDSTEP_OWNER;
0066 unsigned char magic_data[] = "Copyright (c) 1999 Intel Corporation";
0067
0068 command = (smi_sig & 0xffffff00) | (smi_cmd & 0xff);
0069 magic = virt_to_phys(magic_data);
0070
0071 pr_debug("trying to obtain ownership with command %x at port %x\n",
0072 command, smi_port);
0073
0074 __asm__ __volatile__(
0075 "push %%ebp\n"
0076 "out %%al, (%%dx)\n"
0077 "pop %%ebp\n"
0078 : "=D" (result),
0079 "=a" (dummy), "=b" (dummy), "=c" (dummy), "=d" (dummy),
0080 "=S" (dummy)
0081 : "a" (command), "b" (function), "c" (0), "d" (smi_port),
0082 "D" (0), "S" (magic)
0083 : "memory"
0084 );
0085
0086 pr_debug("result is %x\n", result);
0087
0088 return result;
0089 }
0090
0091
0092
0093
0094
0095
0096
0097
0098
0099
0100 static int speedstep_smi_get_freqs(unsigned int *low, unsigned int *high)
0101 {
0102 u32 command, result = 0, edi, high_mhz, low_mhz, dummy;
0103 u32 state = 0;
0104 u32 function = GET_SPEEDSTEP_FREQS;
0105
0106 if (!(ist_info.event & 0xFFFF)) {
0107 pr_debug("bug #1422 -- can't read freqs from BIOS\n");
0108 return -ENODEV;
0109 }
0110
0111 command = (smi_sig & 0xffffff00) | (smi_cmd & 0xff);
0112
0113 pr_debug("trying to determine frequencies with command %x at port %x\n",
0114 command, smi_port);
0115
0116 __asm__ __volatile__(
0117 "push %%ebp\n"
0118 "out %%al, (%%dx)\n"
0119 "pop %%ebp"
0120 : "=a" (result),
0121 "=b" (high_mhz),
0122 "=c" (low_mhz),
0123 "=d" (state), "=D" (edi), "=S" (dummy)
0124 : "a" (command),
0125 "b" (function),
0126 "c" (state),
0127 "d" (smi_port), "S" (0), "D" (0)
0128 );
0129
0130 pr_debug("result %x, low_freq %u, high_freq %u\n",
0131 result, low_mhz, high_mhz);
0132
0133
0134 if ((high_mhz + low_mhz) < 600)
0135 return -EINVAL;
0136
0137 *high = high_mhz * 1000;
0138 *low = low_mhz * 1000;
0139
0140 return result;
0141 }
0142
0143
0144
0145
0146
0147
0148 static void speedstep_set_state(unsigned int state)
0149 {
0150 unsigned int result = 0, command, new_state, dummy;
0151 unsigned long flags;
0152 unsigned int function = SET_SPEEDSTEP_STATE;
0153 unsigned int retry = 0;
0154
0155 if (state > 0x1)
0156 return;
0157
0158
0159 preempt_disable();
0160 local_irq_save(flags);
0161
0162 command = (smi_sig & 0xffffff00) | (smi_cmd & 0xff);
0163
0164 pr_debug("trying to set frequency to state %u "
0165 "with command %x at port %x\n",
0166 state, command, smi_port);
0167
0168 do {
0169 if (retry) {
0170
0171
0172
0173
0174
0175
0176
0177
0178 pr_debug("retry %u, previous result %u, waiting...\n",
0179 retry, result);
0180 local_irq_enable();
0181 mdelay(retry * 50);
0182 local_irq_disable();
0183 }
0184 retry++;
0185 __asm__ __volatile__(
0186 "push %%ebp\n"
0187 "out %%al, (%%dx)\n"
0188 "pop %%ebp"
0189 : "=b" (new_state), "=D" (result),
0190 "=c" (dummy), "=a" (dummy),
0191 "=d" (dummy), "=S" (dummy)
0192 : "a" (command), "b" (function), "c" (state),
0193 "d" (smi_port), "S" (0), "D" (0)
0194 );
0195 } while ((new_state != state) && (retry <= SMI_TRIES));
0196
0197
0198 local_irq_restore(flags);
0199 preempt_enable();
0200
0201 if (new_state == state)
0202 pr_debug("change to %u MHz succeeded after %u tries "
0203 "with result %u\n",
0204 (speedstep_freqs[new_state].frequency / 1000),
0205 retry, result);
0206 else
0207 pr_err("change to state %u failed with new_state %u and result %u\n",
0208 state, new_state, result);
0209
0210 return;
0211 }
0212
0213
0214
0215
0216
0217
0218
0219
0220
0221 static int speedstep_target(struct cpufreq_policy *policy, unsigned int index)
0222 {
0223 speedstep_set_state(index);
0224
0225 return 0;
0226 }
0227
0228
0229 static int speedstep_cpu_init(struct cpufreq_policy *policy)
0230 {
0231 int result;
0232 unsigned int *low, *high;
0233
0234
0235 if (policy->cpu != 0)
0236 return -ENODEV;
0237
0238 result = speedstep_smi_ownership();
0239 if (result) {
0240 pr_debug("fails in acquiring ownership of a SMI interface.\n");
0241 return -EINVAL;
0242 }
0243
0244
0245 low = &speedstep_freqs[SPEEDSTEP_LOW].frequency;
0246 high = &speedstep_freqs[SPEEDSTEP_HIGH].frequency;
0247
0248 result = speedstep_smi_get_freqs(low, high);
0249 if (result) {
0250
0251
0252 pr_debug("could not detect low and high frequencies "
0253 "by SMI call.\n");
0254 result = speedstep_get_freqs(speedstep_processor,
0255 low, high,
0256 NULL,
0257 &speedstep_set_state);
0258
0259 if (result) {
0260 pr_debug("could not detect two different speeds"
0261 " -- aborting.\n");
0262 return result;
0263 } else
0264 pr_debug("workaround worked.\n");
0265 }
0266
0267 policy->freq_table = speedstep_freqs;
0268
0269 return 0;
0270 }
0271
0272 static unsigned int speedstep_get(unsigned int cpu)
0273 {
0274 if (cpu)
0275 return -ENODEV;
0276 return speedstep_get_frequency(speedstep_processor);
0277 }
0278
0279
0280 static int speedstep_resume(struct cpufreq_policy *policy)
0281 {
0282 int result = speedstep_smi_ownership();
0283
0284 if (result)
0285 pr_debug("fails in re-acquiring ownership of a SMI interface.\n");
0286
0287 return result;
0288 }
0289
0290 static struct cpufreq_driver speedstep_driver = {
0291 .name = "speedstep-smi",
0292 .flags = CPUFREQ_NO_AUTO_DYNAMIC_SWITCHING,
0293 .verify = cpufreq_generic_frequency_table_verify,
0294 .target_index = speedstep_target,
0295 .init = speedstep_cpu_init,
0296 .get = speedstep_get,
0297 .resume = speedstep_resume,
0298 .attr = cpufreq_generic_attr,
0299 };
0300
0301 static const struct x86_cpu_id ss_smi_ids[] = {
0302 X86_MATCH_VENDOR_FAM_MODEL(INTEL, 6, 0x8, 0),
0303 X86_MATCH_VENDOR_FAM_MODEL(INTEL, 6, 0xb, 0),
0304 X86_MATCH_VENDOR_FAM_MODEL(INTEL, 15, 0x2, 0),
0305 {}
0306 };
0307
0308
0309
0310
0311
0312
0313
0314
0315 static int __init speedstep_init(void)
0316 {
0317 if (!x86_match_cpu(ss_smi_ids))
0318 return -ENODEV;
0319
0320 speedstep_processor = speedstep_detect_processor();
0321
0322 switch (speedstep_processor) {
0323 case SPEEDSTEP_CPU_PIII_T:
0324 case SPEEDSTEP_CPU_PIII_C:
0325 case SPEEDSTEP_CPU_PIII_C_EARLY:
0326 break;
0327 default:
0328 speedstep_processor = 0;
0329 }
0330
0331 if (!speedstep_processor) {
0332 pr_debug("No supported Intel CPU detected.\n");
0333 return -ENODEV;
0334 }
0335
0336 pr_debug("signature:0x%.8x, command:0x%.8x, "
0337 "event:0x%.8x, perf_level:0x%.8x.\n",
0338 ist_info.signature, ist_info.command,
0339 ist_info.event, ist_info.perf_level);
0340
0341
0342
0343 if ((ist_info.signature != 0x47534943) && (
0344 (smi_port == 0) || (smi_cmd == 0)))
0345 return -ENODEV;
0346
0347 if (smi_sig == 1)
0348 smi_sig = 0x47534943;
0349 else
0350 smi_sig = ist_info.signature;
0351
0352
0353 if ((smi_port > 0xff) || (smi_port < 0))
0354 return -EINVAL;
0355 else if (smi_port == 0)
0356 smi_port = ist_info.command & 0xff;
0357
0358 if ((smi_cmd > 0xff) || (smi_cmd < 0))
0359 return -EINVAL;
0360 else if (smi_cmd == 0)
0361 smi_cmd = (ist_info.command >> 16) & 0xff;
0362
0363 return cpufreq_register_driver(&speedstep_driver);
0364 }
0365
0366
0367
0368
0369
0370
0371
0372 static void __exit speedstep_exit(void)
0373 {
0374 cpufreq_unregister_driver(&speedstep_driver);
0375 }
0376
0377 module_param_hw(smi_port, int, ioport, 0444);
0378 module_param(smi_cmd, int, 0444);
0379 module_param(smi_sig, uint, 0444);
0380
0381 MODULE_PARM_DESC(smi_port, "Override the BIOS-given IST port with this value "
0382 "-- Intel's default setting is 0xb2");
0383 MODULE_PARM_DESC(smi_cmd, "Override the BIOS-given IST command with this value "
0384 "-- Intel's default setting is 0x82");
0385 MODULE_PARM_DESC(smi_sig, "Set to 1 to fake the IST signature when using the "
0386 "SMI interface.");
0387
0388 MODULE_AUTHOR("Hiroshi Miura");
0389 MODULE_DESCRIPTION("Speedstep driver for IST applet SMI interface.");
0390 MODULE_LICENSE("GPL");
0391
0392 module_init(speedstep_init);
0393 module_exit(speedstep_exit);