Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * (C) 2001  Dave Jones, Arjan van de ven.
0004  * (C) 2002 - 2003  Dominik Brodowski <linux@brodo.de>
0005  *
0006  *  Based upon reverse engineered information, and on Intel documentation
0007  *  for chipsets ICH2-M and ICH3-M.
0008  *
0009  *  Many thanks to Ducrot Bruno for finding and fixing the last
0010  *  "missing link" for ICH2-M/ICH3-M support, and to Thomas Winkler
0011  *  for extensive testing.
0012  *
0013  *  BIG FAT DISCLAIMER: Work in progress code. Possibly *dangerous*
0014  */
0015 
0016 
0017 /*********************************************************************
0018  *                        SPEEDSTEP - DEFINITIONS                    *
0019  *********************************************************************/
0020 
0021 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
0022 
0023 #include <linux/kernel.h>
0024 #include <linux/module.h>
0025 #include <linux/init.h>
0026 #include <linux/cpufreq.h>
0027 #include <linux/pci.h>
0028 #include <linux/sched.h>
0029 
0030 #include <asm/cpu_device_id.h>
0031 
0032 #include "speedstep-lib.h"
0033 
0034 
0035 /* speedstep_chipset:
0036  *   It is necessary to know which chipset is used. As accesses to
0037  * this device occur at various places in this module, we need a
0038  * static struct pci_dev * pointing to that device.
0039  */
0040 static struct pci_dev *speedstep_chipset_dev;
0041 
0042 
0043 /* speedstep_processor
0044  */
0045 static enum speedstep_processor speedstep_processor;
0046 
0047 static u32 pmbase;
0048 
0049 /*
0050  *   There are only two frequency states for each processor. Values
0051  * are in kHz for the time being.
0052  */
0053 static struct cpufreq_frequency_table speedstep_freqs[] = {
0054     {0, SPEEDSTEP_HIGH, 0},
0055     {0, SPEEDSTEP_LOW,  0},
0056     {0, 0,          CPUFREQ_TABLE_END},
0057 };
0058 
0059 
0060 /**
0061  * speedstep_find_register - read the PMBASE address
0062  *
0063  * Returns: -ENODEV if no register could be found
0064  */
0065 static int speedstep_find_register(void)
0066 {
0067     if (!speedstep_chipset_dev)
0068         return -ENODEV;
0069 
0070     /* get PMBASE */
0071     pci_read_config_dword(speedstep_chipset_dev, 0x40, &pmbase);
0072     if (!(pmbase & 0x01)) {
0073         pr_err("could not find speedstep register\n");
0074         return -ENODEV;
0075     }
0076 
0077     pmbase &= 0xFFFFFFFE;
0078     if (!pmbase) {
0079         pr_err("could not find speedstep register\n");
0080         return -ENODEV;
0081     }
0082 
0083     pr_debug("pmbase is 0x%x\n", pmbase);
0084     return 0;
0085 }
0086 
0087 /**
0088  * speedstep_set_state - set the SpeedStep state
0089  * @state: new processor frequency state (SPEEDSTEP_LOW or SPEEDSTEP_HIGH)
0090  *
0091  *   Tries to change the SpeedStep state.  Can be called from
0092  *   smp_call_function_single.
0093  */
0094 static void speedstep_set_state(unsigned int state)
0095 {
0096     u8 pm2_blk;
0097     u8 value;
0098     unsigned long flags;
0099 
0100     if (state > 0x1)
0101         return;
0102 
0103     /* Disable IRQs */
0104     local_irq_save(flags);
0105 
0106     /* read state */
0107     value = inb(pmbase + 0x50);
0108 
0109     pr_debug("read at pmbase 0x%x + 0x50 returned 0x%x\n", pmbase, value);
0110 
0111     /* write new state */
0112     value &= 0xFE;
0113     value |= state;
0114 
0115     pr_debug("writing 0x%x to pmbase 0x%x + 0x50\n", value, pmbase);
0116 
0117     /* Disable bus master arbitration */
0118     pm2_blk = inb(pmbase + 0x20);
0119     pm2_blk |= 0x01;
0120     outb(pm2_blk, (pmbase + 0x20));
0121 
0122     /* Actual transition */
0123     outb(value, (pmbase + 0x50));
0124 
0125     /* Restore bus master arbitration */
0126     pm2_blk &= 0xfe;
0127     outb(pm2_blk, (pmbase + 0x20));
0128 
0129     /* check if transition was successful */
0130     value = inb(pmbase + 0x50);
0131 
0132     /* Enable IRQs */
0133     local_irq_restore(flags);
0134 
0135     pr_debug("read at pmbase 0x%x + 0x50 returned 0x%x\n", pmbase, value);
0136 
0137     if (state == (value & 0x1))
0138         pr_debug("change to %u MHz succeeded\n",
0139             speedstep_get_frequency(speedstep_processor) / 1000);
0140     else
0141         pr_err("change failed - I/O error\n");
0142 
0143     return;
0144 }
0145 
0146 /* Wrapper for smp_call_function_single. */
0147 static void _speedstep_set_state(void *_state)
0148 {
0149     speedstep_set_state(*(unsigned int *)_state);
0150 }
0151 
0152 /**
0153  * speedstep_activate - activate SpeedStep control in the chipset
0154  *
0155  *   Tries to activate the SpeedStep status and control registers.
0156  * Returns -EINVAL on an unsupported chipset, and zero on success.
0157  */
0158 static int speedstep_activate(void)
0159 {
0160     u16 value = 0;
0161 
0162     if (!speedstep_chipset_dev)
0163         return -EINVAL;
0164 
0165     pci_read_config_word(speedstep_chipset_dev, 0x00A0, &value);
0166     if (!(value & 0x08)) {
0167         value |= 0x08;
0168         pr_debug("activating SpeedStep (TM) registers\n");
0169         pci_write_config_word(speedstep_chipset_dev, 0x00A0, value);
0170     }
0171 
0172     return 0;
0173 }
0174 
0175 
0176 /**
0177  * speedstep_detect_chipset - detect the Southbridge which contains SpeedStep logic
0178  *
0179  *   Detects ICH2-M, ICH3-M and ICH4-M so far. The pci_dev points to
0180  * the LPC bridge / PM module which contains all power-management
0181  * functions. Returns the SPEEDSTEP_CHIPSET_-number for the detected
0182  * chipset, or zero on failure.
0183  */
0184 static unsigned int speedstep_detect_chipset(void)
0185 {
0186     speedstep_chipset_dev = pci_get_subsys(PCI_VENDOR_ID_INTEL,
0187                   PCI_DEVICE_ID_INTEL_82801DB_12,
0188                   PCI_ANY_ID, PCI_ANY_ID,
0189                   NULL);
0190     if (speedstep_chipset_dev)
0191         return 4; /* 4-M */
0192 
0193     speedstep_chipset_dev = pci_get_subsys(PCI_VENDOR_ID_INTEL,
0194                   PCI_DEVICE_ID_INTEL_82801CA_12,
0195                   PCI_ANY_ID, PCI_ANY_ID,
0196                   NULL);
0197     if (speedstep_chipset_dev)
0198         return 3; /* 3-M */
0199 
0200 
0201     speedstep_chipset_dev = pci_get_subsys(PCI_VENDOR_ID_INTEL,
0202                   PCI_DEVICE_ID_INTEL_82801BA_10,
0203                   PCI_ANY_ID, PCI_ANY_ID,
0204                   NULL);
0205     if (speedstep_chipset_dev) {
0206         /* speedstep.c causes lockups on Dell Inspirons 8000 and
0207          * 8100 which use a pretty old revision of the 82815
0208          * host bridge. Abort on these systems.
0209          */
0210         struct pci_dev *hostbridge;
0211 
0212         hostbridge  = pci_get_subsys(PCI_VENDOR_ID_INTEL,
0213                   PCI_DEVICE_ID_INTEL_82815_MC,
0214                   PCI_ANY_ID, PCI_ANY_ID,
0215                   NULL);
0216 
0217         if (!hostbridge)
0218             return 2; /* 2-M */
0219 
0220         if (hostbridge->revision < 5) {
0221             pr_debug("hostbridge does not support speedstep\n");
0222             speedstep_chipset_dev = NULL;
0223             pci_dev_put(hostbridge);
0224             return 0;
0225         }
0226 
0227         pci_dev_put(hostbridge);
0228         return 2; /* 2-M */
0229     }
0230 
0231     return 0;
0232 }
0233 
0234 static void get_freq_data(void *_speed)
0235 {
0236     unsigned int *speed = _speed;
0237 
0238     *speed = speedstep_get_frequency(speedstep_processor);
0239 }
0240 
0241 static unsigned int speedstep_get(unsigned int cpu)
0242 {
0243     unsigned int speed;
0244 
0245     /* You're supposed to ensure CPU is online. */
0246     BUG_ON(smp_call_function_single(cpu, get_freq_data, &speed, 1));
0247 
0248     pr_debug("detected %u kHz as current frequency\n", speed);
0249     return speed;
0250 }
0251 
0252 /**
0253  * speedstep_target - set a new CPUFreq policy
0254  * @policy: new policy
0255  * @index: index of target frequency
0256  *
0257  * Sets a new CPUFreq policy.
0258  */
0259 static int speedstep_target(struct cpufreq_policy *policy, unsigned int index)
0260 {
0261     unsigned int policy_cpu;
0262 
0263     policy_cpu = cpumask_any_and(policy->cpus, cpu_online_mask);
0264 
0265     smp_call_function_single(policy_cpu, _speedstep_set_state, &index,
0266                  true);
0267 
0268     return 0;
0269 }
0270 
0271 
0272 struct get_freqs {
0273     struct cpufreq_policy *policy;
0274     int ret;
0275 };
0276 
0277 static void get_freqs_on_cpu(void *_get_freqs)
0278 {
0279     struct get_freqs *get_freqs = _get_freqs;
0280 
0281     get_freqs->ret =
0282         speedstep_get_freqs(speedstep_processor,
0283                 &speedstep_freqs[SPEEDSTEP_LOW].frequency,
0284                 &speedstep_freqs[SPEEDSTEP_HIGH].frequency,
0285                 &get_freqs->policy->cpuinfo.transition_latency,
0286                 &speedstep_set_state);
0287 }
0288 
0289 static int speedstep_cpu_init(struct cpufreq_policy *policy)
0290 {
0291     unsigned int policy_cpu;
0292     struct get_freqs gf;
0293 
0294     /* only run on CPU to be set, or on its sibling */
0295 #ifdef CONFIG_SMP
0296     cpumask_copy(policy->cpus, topology_sibling_cpumask(policy->cpu));
0297 #endif
0298     policy_cpu = cpumask_any_and(policy->cpus, cpu_online_mask);
0299 
0300     /* detect low and high frequency and transition latency */
0301     gf.policy = policy;
0302     smp_call_function_single(policy_cpu, get_freqs_on_cpu, &gf, 1);
0303     if (gf.ret)
0304         return gf.ret;
0305 
0306     policy->freq_table = speedstep_freqs;
0307 
0308     return 0;
0309 }
0310 
0311 
0312 static struct cpufreq_driver speedstep_driver = {
0313     .name   = "speedstep-ich",
0314     .verify = cpufreq_generic_frequency_table_verify,
0315     .target_index = speedstep_target,
0316     .init   = speedstep_cpu_init,
0317     .get    = speedstep_get,
0318     .attr   = cpufreq_generic_attr,
0319 };
0320 
0321 static const struct x86_cpu_id ss_smi_ids[] = {
0322     X86_MATCH_VENDOR_FAM_MODEL(INTEL,  6, 0x8, 0),
0323     X86_MATCH_VENDOR_FAM_MODEL(INTEL,  6, 0xb, 0),
0324     X86_MATCH_VENDOR_FAM_MODEL(INTEL, 15, 0x2, 0),
0325     {}
0326 };
0327 
0328 /**
0329  * speedstep_init - initializes the SpeedStep CPUFreq driver
0330  *
0331  *   Initializes the SpeedStep support. Returns -ENODEV on unsupported
0332  * devices, -EINVAL on problems during initiatization, and zero on
0333  * success.
0334  */
0335 static int __init speedstep_init(void)
0336 {
0337     if (!x86_match_cpu(ss_smi_ids))
0338         return -ENODEV;
0339 
0340     /* detect processor */
0341     speedstep_processor = speedstep_detect_processor();
0342     if (!speedstep_processor) {
0343         pr_debug("Intel(R) SpeedStep(TM) capable processor "
0344                 "not found\n");
0345         return -ENODEV;
0346     }
0347 
0348     /* detect chipset */
0349     if (!speedstep_detect_chipset()) {
0350         pr_debug("Intel(R) SpeedStep(TM) for this chipset not "
0351                 "(yet) available.\n");
0352         return -ENODEV;
0353     }
0354 
0355     /* activate speedstep support */
0356     if (speedstep_activate()) {
0357         pci_dev_put(speedstep_chipset_dev);
0358         return -EINVAL;
0359     }
0360 
0361     if (speedstep_find_register())
0362         return -ENODEV;
0363 
0364     return cpufreq_register_driver(&speedstep_driver);
0365 }
0366 
0367 
0368 /**
0369  * speedstep_exit - unregisters SpeedStep support
0370  *
0371  *   Unregisters SpeedStep support.
0372  */
0373 static void __exit speedstep_exit(void)
0374 {
0375     pci_dev_put(speedstep_chipset_dev);
0376     cpufreq_unregister_driver(&speedstep_driver);
0377 }
0378 
0379 
0380 MODULE_AUTHOR("Dave Jones, Dominik Brodowski <linux@brodo.de>");
0381 MODULE_DESCRIPTION("Speedstep driver for Intel mobile processors on chipsets "
0382         "with ICH-M southbridges.");
0383 MODULE_LICENSE("GPL");
0384 
0385 module_init(speedstep_init);
0386 module_exit(speedstep_exit);