Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * Intel SpeedStep SMI driver.
0004  *
0005  * (C) 2003  Hiroshi Miura <miura@da-cha.org>
0006  */
0007 
0008 
0009 /*********************************************************************
0010  *                        SPEEDSTEP - DEFINITIONS                    *
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 /* speedstep system management interface port/command.
0028  *
0029  * These parameters are got from IST-SMI BIOS call.
0030  * If user gives it, these are used.
0031  *
0032  */
0033 static int smi_port;
0034 static int smi_cmd;
0035 static unsigned int smi_sig;
0036 
0037 /* info about the processor */
0038 static enum speedstep_processor speedstep_processor;
0039 
0040 /*
0041  * There are only two frequency states for each processor. Values
0042  * are in kHz for the time being.
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 /* how often shall the SMI call be tried if it failed, e.g. because
0056  * of DMA activity going on? */
0057 #define SMI_TRIES 5
0058 
0059 /**
0060  * speedstep_smi_ownership
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  * speedstep_smi_get_freqs - get SpeedStep preferred & current freq.
0093  * @low: the low frequency value is placed here
0094  * @high: the high frequency value is placed here
0095  *
0096  * Only available on later SpeedStep-enabled systems, returns false results or
0097  * even hangs [cf. bugme.osdl.org # 1422] on earlier systems. Empirical testing
0098  * shows that the latter occurs if !(ist_info.event & 0xFFFF).
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     /* abort if results are obviously incorrect... */
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  * speedstep_set_state - set the SpeedStep state
0145  * @state: new processor frequency state (SPEEDSTEP_LOW or SPEEDSTEP_HIGH)
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     /* Disable IRQs */
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              * We need to enable interrupts, otherwise the blockage
0172              * won't resolve.
0173              *
0174              * We disable preemption so that other processes don't
0175              * run. If other processes were running, they could
0176              * submit more DMA requests, making the blockage worse.
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     /* enable IRQs */
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  * speedstep_target - set a new CPUFreq policy
0216  * @policy: new policy
0217  * @index: index of new freq
0218  *
0219  * Sets a new CPUFreq policy/freq.
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     /* capability check */
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     /* detect low and high frequency */
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         /* fall back to speedstep_lib.c dection mechanism:
0251          * try both states out */
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  * speedstep_init - initializes the SpeedStep CPUFreq driver
0310  *
0311  *   Initializes the SpeedStep support. Returns -ENODEV on unsupported
0312  * BIOS, -EINVAL on problems during initiatization, and zero on
0313  * success.
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     /* Error if no IST-SMI BIOS or no PARM
0342          sig= 'ISGE' aka 'Intel Speedstep Gate E' */
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     /* setup smi_port from MODLULE_PARM or BIOS */
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  * speedstep_exit - unregisters SpeedStep support
0369  *
0370  *   Unregisters SpeedStep support.
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);