📄 speedstep.c
字号:
/* * $Id: speedstep.c,v 1.64 2003/01/02 22:16:26 db Exp $ * * (C) 2001 Dave Jones, Arjan van de ven. * (C) 2002 Dominik Brodowski <linux@brodo.de> * * Licensed under the terms of the GNU GPL License version 2. * Based upon reverse engineered information, and on Intel documentation * for chipsets ICH2-M and ICH3-M. * * Many thanks to Ducrot Bruno for finding and fixing the last * "missing link" for ICH2-M/ICH3-M support, and to Thomas Winkler * for extensive testing. * * BIG FAT DISCLAIMER: Work in progress code. Possibly *dangerous* *//********************************************************************* * SPEEDSTEP - DEFINITIONS * *********************************************************************/#include <linux/kernel.h>#include <linux/module.h> #include <linux/init.h>#include <linux/cpufreq.h>#include <linux/pci.h>#include <linux/slab.h>#include <asm/msr.h>static struct cpufreq_driver *speedstep_driver;/* speedstep_chipset: * It is necessary to know which chipset is used. As accesses to * this device occur at various places in this module, we need a * static struct pci_dev * pointing to that device. */static unsigned int speedstep_chipset;static struct pci_dev *speedstep_chipset_dev;#define SPEEDSTEP_CHIPSET_ICH2M 0x00000002#define SPEEDSTEP_CHIPSET_ICH3M 0x00000003/* speedstep_processor */static unsigned int speedstep_processor = 0;static int speedstep_coppermine = 0;#define SPEEDSTEP_PROCESSOR_PIII_C 0x00000001 /* Coppermine core */#define SPEEDSTEP_PROCESSOR_PIII_T 0x00000002 /* Tualatin core */#define SPEEDSTEP_PROCESSOR_P4M 0x00000003 /* P4-M with 100 MHz FSB *//* speedstep_[low,high]_freq * There are only two frequency states for each processor. Values * are in kHz for the time being. */#define SPEEDSTEP_HIGH 0x00000000#define SPEEDSTEP_LOW 0x00000001static struct cpufreq_frequency_table speedstep_freqs[] = { {SPEEDSTEP_HIGH, 0}, {SPEEDSTEP_LOW, 0}, {0, CPUFREQ_TABLE_END},};#define speedstep_low_freq speedstep_freqs[SPEEDSTEP_LOW].frequency#define speedstep_high_freq speedstep_freqs[SPEEDSTEP_HIGH].frequency/* DEBUG * Define it if you want verbose debug output, e.g. for bug reporting *///#define SPEEDSTEP_DEBUG#ifdef SPEEDSTEP_DEBUG#define dprintk(msg...) printk(msg)#else#define dprintk(msg...) do { } while(0)#endif/********************************************************************* * LOW LEVEL CHIPSET INTERFACE * *********************************************************************//** * speedstep_get_state - read the current SpeedStep state * @state: Speedstep state (SPEEDSTEP_LOW or SPEEDSTEP_HIGH) * * Tries to read the SpeedStep state. Returns -EIO when there has been * trouble to read the status or write to the control register, -EINVAL * on an unsupported chipset, and zero on success. */static int speedstep_get_state (unsigned int *state){ unsigned long flags; u32 pmbase; u8 value; if (!speedstep_chipset_dev || !state) return -EINVAL; switch (speedstep_chipset) { case SPEEDSTEP_CHIPSET_ICH2M: case SPEEDSTEP_CHIPSET_ICH3M: /* get PMBASE */ pci_read_config_dword(speedstep_chipset_dev, 0x40, &pmbase); if (!(pmbase & 0x01)) return -EIO; pmbase &= 0xFFFFFFFE; if (!pmbase) return -EIO; /* read state */ local_irq_save(flags); value = inb(pmbase + 0x50); local_irq_restore(flags); dprintk(KERN_DEBUG "cpufreq: read at pmbase 0x%x + 0x50 returned 0x%x\n", pmbase, value); *state = value & 0x01; return 0; } printk (KERN_ERR "cpufreq: setting CPU frequency on this chipset unsupported.\n"); return -EINVAL;}/** * speedstep_set_state - set the SpeedStep state * @state: new processor frequency state (SPEEDSTEP_LOW or SPEEDSTEP_HIGH) * * Tries to change the SpeedStep state. */static void speedstep_set_state (unsigned int state, int notify){ u32 pmbase; u8 pm2_blk; u8 value; unsigned long flags; unsigned int oldstate; struct cpufreq_freqs freqs; if (!speedstep_chipset_dev || (state > 0x1)) return; if (speedstep_get_state(&oldstate)) return; if (oldstate == state) return; freqs.old = (oldstate == SPEEDSTEP_HIGH) ? speedstep_high_freq : speedstep_low_freq; freqs.new = (state == SPEEDSTEP_HIGH) ? speedstep_high_freq : speedstep_low_freq; freqs.cpu = 0; /* speedstep.c is UP only driver */ if (notify) cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); switch (speedstep_chipset) { case SPEEDSTEP_CHIPSET_ICH2M: case SPEEDSTEP_CHIPSET_ICH3M: /* get PMBASE */ pci_read_config_dword(speedstep_chipset_dev, 0x40, &pmbase); if (!(pmbase & 0x01)) { printk(KERN_ERR "cpufreq: could not find speedstep register\n"); return; } pmbase &= 0xFFFFFFFE; if (!pmbase) { printk(KERN_ERR "cpufreq: could not find speedstep register\n"); return; } /* Disable IRQs */ local_irq_save(flags); /* read state */ value = inb(pmbase + 0x50); dprintk(KERN_DEBUG "cpufreq: read at pmbase 0x%x + 0x50 returned 0x%x\n", pmbase, value); /* write new state */ value &= 0xFE; value |= state; dprintk(KERN_DEBUG "cpufreq: writing 0x%x to pmbase 0x%x + 0x50\n", value, pmbase); /* Disable bus master arbitration */ pm2_blk = inb(pmbase + 0x20); pm2_blk |= 0x01; outb(pm2_blk, (pmbase + 0x20)); /* Actual transition */ outb(value, (pmbase + 0x50)); /* Restore bus master arbitration */ pm2_blk &= 0xfe; outb(pm2_blk, (pmbase + 0x20)); /* check if transition was sucessful */ value = inb(pmbase + 0x50); /* Enable IRQs */ local_irq_restore(flags); dprintk(KERN_DEBUG "cpufreq: read at pmbase 0x%x + 0x50 returned 0x%x\n", pmbase, value); if (state == (value & 0x1)) { dprintk (KERN_INFO "cpufreq: change to %u MHz succeded\n", (freqs.new / 1000)); } else { printk (KERN_ERR "cpufreq: change failed - I/O error\n"); } break; default: printk (KERN_ERR "cpufreq: setting CPU frequency on this chipset unsupported.\n"); } if (notify) cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); return;}/** * speedstep_activate - activate SpeedStep control in the chipset * * Tries to activate the SpeedStep status and control registers. * Returns -EINVAL on an unsupported chipset, and zero on success. */static int speedstep_activate (void){ if (!speedstep_chipset_dev) return -EINVAL; switch (speedstep_chipset) { case SPEEDSTEP_CHIPSET_ICH2M: case SPEEDSTEP_CHIPSET_ICH3M: { u16 value = 0; pci_read_config_word(speedstep_chipset_dev, 0x00A0, &value); if (!(value & 0x08)) { value |= 0x08; dprintk(KERN_DEBUG "cpufreq: activating SpeedStep (TM) registers\n"); pci_write_config_word(speedstep_chipset_dev, 0x00A0, value); } return 0; } } printk (KERN_ERR "cpufreq: SpeedStep (TM) on this chipset unsupported.\n"); return -EINVAL;}/** * speedstep_detect_chipset - detect the Southbridge which contains SpeedStep logic * * Detects PIIX4, ICH2-M and ICH3-M so far. The pci_dev points to * the LPC bridge / PM module which contains all power-management * functions. Returns the SPEEDSTEP_CHIPSET_-number for the detected * chipset, or zero on failure. */static unsigned int speedstep_detect_chipset (void){ speedstep_chipset_dev = pci_find_subsys(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_12, PCI_ANY_ID, PCI_ANY_ID, NULL); if (speedstep_chipset_dev) return SPEEDSTEP_CHIPSET_ICH3M; speedstep_chipset_dev = pci_find_subsys(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_10, PCI_ANY_ID, PCI_ANY_ID, NULL); if (speedstep_chipset_dev) { /* speedstep.c causes lockups on Dell Inspirons 8000 and * 8100 which use a pretty old revision of the 82815 * host brige. Abort on these systems. */ static struct pci_dev *hostbridge; u8 rev = 0; hostbridge = pci_find_subsys(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82815_MC, PCI_ANY_ID, PCI_ANY_ID, NULL); if (!hostbridge) return SPEEDSTEP_CHIPSET_ICH2M; pci_read_config_byte(hostbridge, PCI_REVISION_ID, &rev); if (rev < 5) { dprintk(KERN_INFO "cpufreq: hostbrige does not support speedstep\n"); speedstep_chipset_dev = NULL; return 0; } return SPEEDSTEP_CHIPSET_ICH2M; } return 0;}/********************************************************************* * LOW LEVEL PROCESSOR INTERFACE * *********************************************************************//** * pentium3_get_frequency - get the core frequencies for PIIIs * * Returns the core frequency of a Pentium III processor (in kHz) */static unsigned int pentium3_get_frequency (void){ /* See table 14 of p3_ds.pdf and table 22 of 29834003.pdf */ struct { unsigned int ratio; /* Frequency Multiplier (x10) */ u8 bitmap; /* power on configuration bits [27, 25:22] (in MSR 0x2a) */ } msr_decode_mult [] = { { 30, 0x01 }, { 35, 0x05 }, { 40, 0x02 }, { 45, 0x06 }, { 50, 0x00 }, { 55, 0x04 }, { 60, 0x0b }, { 65, 0x0f }, { 70, 0x09 }, { 75, 0x0d }, { 80, 0x0a }, { 85, 0x26 }, { 90, 0x20 }, { 100, 0x2b }, { 0, 0xff } /* error or unknown value */ }; /* PIII(-M) FSB settings: see table b1-b of 24547206.pdf */ struct { unsigned int value; /* Front Side Bus speed in MHz */ u8 bitmap; /* power on configuration bits [18: 19] (in MSR 0x2a) */ } msr_decode_fsb [] = {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -