From 919268153ee67f5183ed9dd7b9ea0cef5a34a8b2 Mon Sep 17 00:00:00 2001 From: Dave Jones <davej@codemonkey.org.uk> Date: Thu, 26 Jun 2003 03:47:01 +0100 Subject: [PATCH] [CPUFREQ] speedstep cleanups. From Dominik Brodowski. - separate functions which might be useful for speedstep-piix4, speedstep-via or speedstep-bios into a new speedstep-lib.c. - clean up speedstep-ich as it will only be used for ICH-based chipsets in future. - remove now-obsolete speedstep_coppermine parameter --- Documentation/cpu-freq/user-guide.txt | 10 +- arch/i386/kernel/cpu/cpufreq/Kconfig | 5 + arch/i386/kernel/cpu/cpufreq/Makefile | 1 + arch/i386/kernel/cpu/cpufreq/speedstep-ich.c | 541 +++---------------- arch/i386/kernel/cpu/cpufreq/speedstep-lib.c | 272 ++++++++++ arch/i386/kernel/cpu/cpufreq/speedstep-lib.h | 41 ++ 6 files changed, 406 insertions(+), 464 deletions(-) create mode 100644 arch/i386/kernel/cpu/cpufreq/speedstep-lib.c create mode 100644 arch/i386/kernel/cpu/cpufreq/speedstep-lib.h diff --git a/Documentation/cpu-freq/user-guide.txt b/Documentation/cpu-freq/user-guide.txt index dd93fb46bda1..fc206075944d 100644 --- a/Documentation/cpu-freq/user-guide.txt +++ b/Documentation/cpu-freq/user-guide.txt @@ -56,19 +56,15 @@ AMD mobile K6-3+ AMD mobile Duron AMD mobile Athlon Cyrix Media GXm -Intel mobile PIII [*] and Intel mobile PIII-M on certain chipsets +Intel mobile PIII and Intel mobile PIII-M on certain chipsets Intel Pentium 4, Intel Xeon Intel Pentium M (Centrino) National Semiconductors Geode GX Transmeta Crusoe VIA Cyrix 3 / C3 -various processors on some ACPI 2.0-compatible systems [**] +various processors on some ACPI 2.0-compatible systems [*] -[*] only certain Intel mobile PIII processors are supported. If you -know that you own a speedstep-capable processor, pass the option -"speedstep_coppermine=1" to the module speedstep.o - -[**] Only if "ACPI Processor Performance States" are available +[*] Only if "ACPI Processor Performance States" are available to the ACPI<->BIOS interface. diff --git a/arch/i386/kernel/cpu/cpufreq/Kconfig b/arch/i386/kernel/cpu/cpufreq/Kconfig index da2447779055..4a7f77e136a8 100644 --- a/arch/i386/kernel/cpu/cpufreq/Kconfig +++ b/arch/i386/kernel/cpu/cpufreq/Kconfig @@ -123,6 +123,11 @@ config X86_SPEEDSTEP_CENTRINO If in doubt, say N. +config X86_SPEEDSTEP_LIB + tristate + depends on X86_SPEEDSTEP_ICH + default X86_SPEEDSTEP_ICH + config X86_P4_CLOCKMOD tristate "Intel Pentium 4 clock modulation" depends on CPU_FREQ_TABLE diff --git a/arch/i386/kernel/cpu/cpufreq/Makefile b/arch/i386/kernel/cpu/cpufreq/Makefile index 89b2a63a7581..47bbd040e442 100644 --- a/arch/i386/kernel/cpu/cpufreq/Makefile +++ b/arch/i386/kernel/cpu/cpufreq/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_X86_GX_SUSPMOD) += gx-suspmod.o obj-$(CONFIG_X86_ACPI_CPUFREQ) += acpi.o obj-$(CONFIG_X86_SPEEDSTEP_ICH) += speedstep-ich.o obj-$(CONFIG_X86_SPEEDSTEP_CENTRINO) += speedstep-centrino.o +obj-$(CONFIG_X86_SPEEDSTEP_LIB) += speedstep-lib.o ifdef CONFIG_X86_ACPI_CPUFREQ ifdef CONFIG_ACPI_DEBUG diff --git a/arch/i386/kernel/cpu/cpufreq/speedstep-ich.c b/arch/i386/kernel/cpu/cpufreq/speedstep-ich.c index 18957e60af0d..c7631fde9e20 100644 --- a/arch/i386/kernel/cpu/cpufreq/speedstep-ich.c +++ b/arch/i386/kernel/cpu/cpufreq/speedstep-ich.c @@ -1,6 +1,4 @@ /* - * $Id: speedstep.c,v 1.70 2003/02/22 10:23:46 db Exp $ - * * (C) 2001 Dave Jones, Arjan van de ven. * (C) 2002 - 2003 Dominik Brodowski <linux@brodo.de> * @@ -27,46 +25,32 @@ #include <linux/pci.h> #include <linux/slab.h> -#include <asm/msr.h> +#include "speedstep-lib.h" + /* 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; +static struct pci_dev *speedstep_chipset_dev; -#define SPEEDSTEP_CHIPSET_ICH2M 0x00000002 -#define SPEEDSTEP_CHIPSET_ICH3M 0x00000003 -#define SPEEDSTEP_CHIPSET_ICH4M 0x00000004 /* 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 */ +static unsigned int speedstep_processor = 0; -/* 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 0x00000001 - static 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 @@ -80,149 +64,82 @@ static struct cpufreq_frequency_table speedstep_freqs[] = { #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: - case SPEEDSTEP_CHIPSET_ICH4M: - /* 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) +static void speedstep_set_state (unsigned int state, unsigned int notify) { - u32 pmbase; - u8 pm2_blk; - u8 value; - unsigned long flags; - unsigned int oldstate; - struct cpufreq_freqs freqs; + u32 pmbase; + u8 pm2_blk; + u8 value; + unsigned long flags; + 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.old = speedstep_get_processor_frequency(speedstep_processor); + freqs.new = speedstep_freqs[SPEEDSTEP_LOW].frequency; 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: - case SPEEDSTEP_CHIPSET_ICH4M: - /* 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; - } + /* 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; - } + pmbase &= 0xFFFFFFFE; + if (!pmbase) { + printk(KERN_ERR "cpufreq: could not find speedstep register\n"); + return; + } - /* Disable IRQs */ - local_irq_save(flags); + /* Disable IRQs */ + local_irq_save(flags); - /* read state */ - value = inb(pmbase + 0x50); + /* read state */ + value = inb(pmbase + 0x50); - dprintk(KERN_DEBUG "cpufreq: read at pmbase 0x%x + 0x50 returned 0x%x\n", pmbase, value); + dprintk(KERN_DEBUG "cpufreq: read at pmbase 0x%x + 0x50 returned 0x%x\n", pmbase, value); - /* write new state */ - value &= 0xFE; - value |= state; + /* write new state */ + value &= 0xFE; + value |= state; - dprintk(KERN_DEBUG "cpufreq: writing 0x%x to pmbase 0x%x + 0x50\n", value, pmbase); + 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)); + /* Disable bus master arbitration */ + pm2_blk = inb(pmbase + 0x20); + pm2_blk |= 0x01; + outb(pm2_blk, (pmbase + 0x20)); - /* Actual transition */ - outb(value, (pmbase + 0x50)); + /* Actual transition */ + outb(value, (pmbase + 0x50)); - /* Restore bus master arbitration */ - pm2_blk &= 0xfe; - outb(pm2_blk, (pmbase + 0x20)); + /* Restore bus master arbitration */ + pm2_blk &= 0xfe; + outb(pm2_blk, (pmbase + 0x20)); - /* check if transition was successful */ - value = inb(pmbase + 0x50); + /* check if transition was successful */ + value = inb(pmbase + 0x50); - /* Enable IRQs */ - local_irq_restore(flags); + /* Enable IRQs */ + local_irq_restore(flags); - dprintk(KERN_DEBUG "cpufreq: read at pmbase 0x%x + 0x50 returned 0x%x\n", pmbase, value); + 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 succeeded\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 (state == (value & 0x1)) { + dprintk (KERN_INFO "cpufreq: change to %u MHz succeeded\n", (freqs.new / 1000)); + } else { + printk (KERN_ERR "cpufreq: change failed - I/O error\n"); } if (notify) @@ -240,31 +157,21 @@ static void speedstep_set_state (unsigned int state, int notify) */ static int speedstep_activate (void) { + u16 value = 0; + if (!speedstep_chipset_dev) return -EINVAL; - switch (speedstep_chipset) { - case SPEEDSTEP_CHIPSET_ICH2M: - case SPEEDSTEP_CHIPSET_ICH3M: - case SPEEDSTEP_CHIPSET_ICH4M: - { - 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; - } + 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); } - - printk (KERN_ERR "cpufreq: SpeedStep (TM) on this chipset unsupported.\n"); - return -EINVAL; + + return 0; } @@ -284,7 +191,7 @@ static unsigned int speedstep_detect_chipset (void) PCI_ANY_ID, NULL); if (speedstep_chipset_dev) - return SPEEDSTEP_CHIPSET_ICH4M; + return 4; /* 4-M */ speedstep_chipset_dev = pci_find_subsys(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_12, @@ -292,7 +199,7 @@ static unsigned int speedstep_detect_chipset (void) PCI_ANY_ID, NULL); if (speedstep_chipset_dev) - return SPEEDSTEP_CHIPSET_ICH3M; + return 3; /* 3-M */ speedstep_chipset_dev = pci_find_subsys(PCI_VENDOR_ID_INTEL, @@ -305,7 +212,7 @@ static unsigned int speedstep_detect_chipset (void) * 8100 which use a pretty old revision of the 82815 * host brige. Abort on these systems. */ - static struct pci_dev *hostbridge; + static struct pci_dev *hostbridge; u8 rev = 0; hostbridge = pci_find_subsys(PCI_VENDOR_ID_INTEL, @@ -315,7 +222,7 @@ static unsigned int speedstep_detect_chipset (void) NULL); if (!hostbridge) - return SPEEDSTEP_CHIPSET_ICH2M; + return 2; /* 2-M */ pci_read_config_byte(hostbridge, PCI_REVISION_ID, &rev); if (rev < 5) { @@ -324,270 +231,13 @@ static unsigned int speedstep_detect_chipset (void) return 0; } - return SPEEDSTEP_CHIPSET_ICH2M; + return 2; /* 2-M */ } 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 [] = { - { 66, 0x0 }, - { 100, 0x2 }, - { 133, 0x1 }, - { 0, 0xff} - }; - u32 msr_lo, msr_tmp; - int i = 0, j = 0; - struct cpuinfo_x86 *c = cpu_data; - - /* read MSR 0x2a - we only need the low 32 bits */ - rdmsr(MSR_IA32_EBL_CR_POWERON, msr_lo, msr_tmp); - dprintk(KERN_DEBUG "cpufreq: P3 - MSR_IA32_EBL_CR_POWERON: 0x%x 0x%x\n", msr_lo, msr_tmp); - msr_tmp = msr_lo; - - /* decode the FSB */ - msr_tmp &= 0x00c0000; - msr_tmp >>= 18; - while (msr_tmp != msr_decode_fsb[i].bitmap) { - if (msr_decode_fsb[i].bitmap == 0xff) - return -EINVAL; - i++; - } - - /* decode the multiplier */ - if ((c->x86_model == 0x08) && (c->x86_mask == 0x01)) - /* different on early Coppermine PIII */ - msr_lo &= 0x03c00000; - else - msr_lo &= 0x0bc00000; - msr_lo >>= 22; - while (msr_lo != msr_decode_mult[j].bitmap) { - if (msr_decode_mult[j].bitmap == 0xff) - return -EINVAL; - j++; - } - - return (msr_decode_mult[j].ratio * msr_decode_fsb[i].value * 100); -} - - -/** - * pentium4_get_frequency - get the core frequency for P4-Ms - * - * Should return the core frequency (in kHz) for P4-Ms. - */ -static unsigned int pentium4_get_frequency(void) -{ - u32 msr_lo, msr_hi; - - rdmsr(0x2c, msr_lo, msr_hi); - - dprintk(KERN_DEBUG "cpufreq: P4 - MSR_EBC_FREQUENCY_ID: 0x%x 0x%x\n", msr_lo, msr_hi); - - /* First 12 bits seem to change a lot (0x511, 0x410 and 0x30f seen - * yet). Next 12 bits always seem to be 0x300. If this is not true - * on this CPU, complain. Last 8 bits are frequency (in 100MHz). - */ - if (msr_hi || ((msr_lo & 0x00FFF000) != 0x300000)) { - printk(KERN_DEBUG "cpufreq: P4 - MSR_EBC_FREQUENCY_ID: 0x%x 0x%x\n", msr_lo, msr_hi); - printk(KERN_INFO "cpufreq: problem in initialization. Please contact Dominik Brodowski\n"); - printk(KERN_INFO "cpufreq: <linux@brodo.de> and attach this dmesg. Thanks in advance\n"); - return 0; - } - - msr_lo >>= 24; - return (msr_lo * 100000); -} - - -/** - * speedstep_detect_processor - detect Intel SpeedStep-capable processors. - * - * Returns the SPEEDSTEP_PROCESSOR_-number for the detected processor, - * or zero on failure. - */ -static unsigned int speedstep_detect_processor (void) -{ - struct cpuinfo_x86 *c = cpu_data; - u32 ebx; - - if ((c->x86_vendor != X86_VENDOR_INTEL) || - ((c->x86 != 6) && (c->x86 != 0xF))) - return 0; - - if (c->x86 == 0xF) { - /* Intel Pentium 4 Mobile P4-M */ - if (c->x86_model != 2) - return 0; - - if ((c->x86_mask != 4) && (c->x86_mask != 7)) - return 0; - - ebx = cpuid_ebx(0x00000001); - ebx &= 0x000000FF; - if ((ebx != 0x0e) && (ebx != 0x0f)) - return 0; - - return SPEEDSTEP_PROCESSOR_P4M; - } - - switch (c->x86_model) { - case 0x0B: /* Intel PIII [Tualatin] */ - /* cpuid_ebx(1) is 0x04 for desktop PIII, - 0x06 for mobile PIII-M */ - ebx = cpuid_ebx(0x00000001); - - ebx &= 0x000000FF; - if (ebx != 0x06) - return 0; - - /* So far all PIII-M processors support SpeedStep. See - * Intel's 24540633.pdf of August 2002 - */ - - return SPEEDSTEP_PROCESSOR_PIII_T; - - case 0x08: /* Intel PIII [Coppermine] */ - { - u32 msr_lo, msr_hi; - - /* all mobile PIII Coppermines have FSB 100 MHz - * ==> sort out a few desktop PIIIs. */ - rdmsr(MSR_IA32_EBL_CR_POWERON, msr_lo, msr_hi); - dprintk(KERN_DEBUG "cpufreq: Coppermine: MSR_IA32_EBL_Cr_POWERON is 0x%x, 0x%x\n", msr_lo, msr_hi); - msr_lo &= 0x00c0000; - if (msr_lo != 0x0080000) - return 0; - - if (speedstep_coppermine) - return SPEEDSTEP_PROCESSOR_PIII_C; - - /* - * If the processor is a mobile version, - * platform ID has bit 50 set - * it has SpeedStep technology if either - * bit 56 or 57 is set - */ - rdmsr(MSR_IA32_PLATFORM_ID, msr_lo, msr_hi); - dprintk(KERN_DEBUG "cpufreq: Coppermine: MSR_IA32_PLATFORM ID is 0x%x, 0x%x\n", msr_lo, msr_hi); - if ((msr_hi & (1<<18)) && (msr_hi & (3<<24))) - return SPEEDSTEP_PROCESSOR_PIII_C; - - printk(KERN_INFO "cpufreq: in case this is a SpeedStep-capable Intel Pentium III Coppermine\n"); - printk(KERN_INFO "cpufreq: processor, please pass the boot option or module parameter\n"); - printk(KERN_INFO "cpufreq: `speedstep_coppermine=1` to the kernel. Thanks!\n"); - return 0; - } - - default: - return 0; - } -} - - - -/********************************************************************* - * HIGH LEVEL FUNCTIONS * - *********************************************************************/ - -/** - * speedstep_detect_speeds - detects low and high CPU frequencies. - * - * Detects the low and high CPU frequencies in kHz. Returns 0 on - * success or -EINVAL / -EIO on problems. - */ -static int speedstep_detect_speeds (void) -{ - unsigned long flags; - unsigned int state; - int i, result; - - /* Disable irqs for entire detection process */ - local_irq_save(flags); - - for (i=0; i<2; i++) { - /* read the current state */ - result = speedstep_get_state(&state); - if (result) { - local_irq_restore(flags); - return result; - } - - /* save the correct value, and switch to other */ - if (state == SPEEDSTEP_LOW) { - switch (speedstep_processor) { - case SPEEDSTEP_PROCESSOR_PIII_C: - case SPEEDSTEP_PROCESSOR_PIII_T: - speedstep_low_freq = pentium3_get_frequency(); - break; - case SPEEDSTEP_PROCESSOR_P4M: - speedstep_low_freq = pentium4_get_frequency(); - } - speedstep_set_state(SPEEDSTEP_HIGH, 0); - } else { - switch (speedstep_processor) { - case SPEEDSTEP_PROCESSOR_PIII_C: - case SPEEDSTEP_PROCESSOR_PIII_T: - speedstep_high_freq = pentium3_get_frequency(); - break; - case SPEEDSTEP_PROCESSOR_P4M: - speedstep_high_freq = pentium4_get_frequency(); - } - speedstep_set_state(SPEEDSTEP_LOW, 0); - } - } - - local_irq_restore(flags); - - if (!speedstep_low_freq || !speedstep_high_freq || - (speedstep_low_freq == speedstep_high_freq)) - return -EIO; - - return 0; -} - - /** * speedstep_setpolicy - set a new CPUFreq policy * @policy: new policy @@ -598,7 +248,7 @@ static int speedstep_target (struct cpufreq_policy *policy, unsigned int target_freq, unsigned int relation) { - unsigned int newstate = 0; + unsigned int newstate = 0; if (cpufreq_frequency_table_target(policy, &speedstep_freqs[0], target_freq, relation, &newstate)) return -EINVAL; @@ -632,22 +282,24 @@ static int speedstep_cpu_init(struct cpufreq_policy *policy) return -ENODEV; /* detect low and high frequency */ - result = speedstep_detect_speeds(); + result = speedstep_get_freqs(speedstep_processor, + &speedstep_freqs[SPEEDSTEP_LOW].frequency, + &speedstep_freqs[SPEEDSTEP_HIGH].frequency, + &speedstep_set_state); if (result) return result; /* get current speed setting */ - result = speedstep_get_state(&speed); - if (result) - return result; + speed = speedstep_get_processor_frequency(speedstep_processor); + if (!speed) + return -EIO; - speed = (speed == SPEEDSTEP_LOW) ? speedstep_low_freq : speedstep_high_freq; dprintk(KERN_INFO "cpufreq: currently at %s speed setting - %i MHz\n", (speed == speedstep_low_freq) ? "low" : "high", (speed / 1000)); /* cpuinfo and default policy values */ - policy->policy = (speed == speedstep_low_freq) ? + policy->policy = (speed == speedstep_freqs[SPEEDSTEP_LOW].frequency) ? CPUFREQ_POLICY_POWERSAVE : CPUFREQ_POLICY_PERFORMANCE; policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL; policy->cur = speed; @@ -656,26 +308,6 @@ static int speedstep_cpu_init(struct cpufreq_policy *policy) } -#ifndef MODULE -/** - * speedstep_setup speedstep command line parameter parsing - * - * speedstep command line parameter. Use: - * speedstep_coppermine=1 - * if the CPU in your notebook is a SpeedStep-capable Intel - * Pentium III Coppermine. These processors cannot be detected - * automatically, as Intel continues to consider the detection - * algorithm as proprietary material. - */ -static int __init speedstep_setup(char *str) -{ - speedstep_coppermine = simple_strtoul(str, &str, 0); - return 1; -} -__setup("speedstep_coppermine=", speedstep_setup); -#endif - - static struct cpufreq_driver speedstep_driver = { .name = "speedstep", .verify = speedstep_verify, @@ -694,20 +326,17 @@ static struct cpufreq_driver speedstep_driver = { */ static int __init speedstep_init(void) { - /* detect chipset */ - speedstep_chipset = speedstep_detect_chipset(); + /* detect processor */ + speedstep_processor = speedstep_detect_processor(); + if (!speedstep_processor) + return -ENODEV; /* detect chipset */ - if (speedstep_chipset) - speedstep_processor = speedstep_detect_processor(); - - if ((!speedstep_chipset) || (!speedstep_processor)) { - printk(KERN_INFO "cpufreq: Intel(R) SpeedStep(TM) for this %s not (yet) available.\n", speedstep_chipset ? "processor" : "chipset"); + if (!speedstep_detect_chipset()) { + printk(KERN_INFO "cpufreq: Intel(R) SpeedStep(TM) for this chipset not (yet) available.\n"); return -ENODEV; } - dprintk(KERN_INFO "cpufreq: Intel(R) SpeedStep(TM) support $Revision: 1.70 $\n"); - /* activate speedstep support */ if (speedstep_activate()) return -EINVAL; @@ -727,10 +356,8 @@ static void __exit speedstep_exit(void) } -MODULE_PARM (speedstep_coppermine, "i"); - MODULE_AUTHOR ("Dave Jones <davej@suse.de>, Dominik Brodowski <linux@brodo.de>"); -MODULE_DESCRIPTION ("Speedstep driver for Intel mobile processors."); +MODULE_DESCRIPTION ("Speedstep driver for Intel mobile processors on chipsets with ICH-M southbridges."); MODULE_LICENSE ("GPL"); module_init(speedstep_init); diff --git a/arch/i386/kernel/cpu/cpufreq/speedstep-lib.c b/arch/i386/kernel/cpu/cpufreq/speedstep-lib.c new file mode 100644 index 000000000000..35c9e3760728 --- /dev/null +++ b/arch/i386/kernel/cpu/cpufreq/speedstep-lib.c @@ -0,0 +1,272 @@ +/* + * (C) 2002 - 2003 Dominik Brodowski <linux@brodo.de> + * + * Licensed under the terms of the GNU GPL License version 2. + * + * Library for common functions for Intel SpeedStep v.1 and v.2 support + * + * BIG FAT DISCLAIMER: Work in progress code. Possibly *dangerous* + */ + +#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> +#include "speedstep-lib.h" + + +/* 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 + +/********************************************************************* + * GET PROCESSOR CORE SPEED IN KHZ * + *********************************************************************/ + +static unsigned int pentium3_get_frequency (unsigned int processor) +{ + /* 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 [] = { + { 66, 0x0 }, + { 100, 0x2 }, + { 133, 0x1 }, + { 0, 0xff} + }; + + u32 msr_lo, msr_tmp; + int i = 0, j = 0; + + /* read MSR 0x2a - we only need the low 32 bits */ + rdmsr(MSR_IA32_EBL_CR_POWERON, msr_lo, msr_tmp); + dprintk(KERN_DEBUG "speedstep-lib: P3 - MSR_IA32_EBL_CR_POWERON: 0x%x 0x%x\n", msr_lo, msr_tmp); + msr_tmp = msr_lo; + + /* decode the FSB */ + msr_tmp &= 0x00c0000; + msr_tmp >>= 18; + while (msr_tmp != msr_decode_fsb[i].bitmap) { + if (msr_decode_fsb[i].bitmap == 0xff) + return 0; + i++; + } + + /* decode the multiplier */ + if (processor == SPEEDSTEP_PROCESSOR_PIII_C_EARLY) + msr_lo &= 0x03c00000; + else + msr_lo &= 0x0bc00000; + msr_lo >>= 22; + while (msr_lo != msr_decode_mult[j].bitmap) { + if (msr_decode_mult[j].bitmap == 0xff) + return 0; + j++; + } + + return (msr_decode_mult[j].ratio * msr_decode_fsb[i].value * 100); +} + + +static unsigned int pentium4_get_frequency(void) +{ + u32 msr_lo, msr_hi; + + rdmsr(0x2c, msr_lo, msr_hi); + + dprintk(KERN_DEBUG "speedstep-lib: P4 - MSR_EBC_FREQUENCY_ID: 0x%x 0x%x\n", msr_lo, msr_hi); + + msr_lo >>= 24; + return (msr_lo * 100000); +} + + +unsigned int speedstep_get_processor_frequency(unsigned int processor) +{ + switch (processor) { + case SPEEDSTEP_PROCESSOR_P4M: + return pentium4_get_frequency(); + case SPEEDSTEP_PROCESSOR_PIII_T: + case SPEEDSTEP_PROCESSOR_PIII_C: + case SPEEDSTEP_PROCESSOR_PIII_C_EARLY: + return pentium3_get_frequency(processor); + default: + return 0; + }; + return 0; +} +EXPORT_SYMBOL_GPL(speedstep_get_processor_frequency); + + +/********************************************************************* + * DETECT SPEEDSTEP-CAPABLE PROCESSOR * + *********************************************************************/ + +unsigned int speedstep_detect_processor (void) +{ + struct cpuinfo_x86 *c = cpu_data; + u32 ebx, msr_lo, msr_hi; + + if ((c->x86_vendor != X86_VENDOR_INTEL) || + ((c->x86 != 6) && (c->x86 != 0xF))) + return 0; + + if (c->x86 == 0xF) { + /* Intel Pentium 4 Mobile P4-M */ + if (c->x86_model != 2) + return 0; + + if ((c->x86_mask != 4) && (c->x86_mask != 7)) + return 0; + + ebx = cpuid_ebx(0x00000001); + ebx &= 0x000000FF; + if ((ebx != 0x0e) && (ebx != 0x0f)) + return 0; + + return SPEEDSTEP_PROCESSOR_P4M; + } + + switch (c->x86_model) { + case 0x0B: /* Intel PIII [Tualatin] */ + /* cpuid_ebx(1) is 0x04 for desktop PIII, + 0x06 for mobile PIII-M */ + ebx = cpuid_ebx(0x00000001); + + ebx &= 0x000000FF; + if (ebx != 0x06) + return 0; + + /* So far all PIII-M processors support SpeedStep. See + * Intel's 24540640.pdf of June 2003 + */ + + return SPEEDSTEP_PROCESSOR_PIII_T; + + case 0x08: /* Intel PIII [Coppermine] */ + + /* all mobile PIII Coppermines have FSB 100 MHz + * ==> sort out a few desktop PIIIs. */ + rdmsr(MSR_IA32_EBL_CR_POWERON, msr_lo, msr_hi); + dprintk(KERN_DEBUG "cpufreq: Coppermine: MSR_IA32_EBL_CR_POWERON is 0x%x, 0x%x\n", msr_lo, msr_hi); + msr_lo &= 0x00c0000; + if (msr_lo != 0x0080000) + return 0; + + /* + * If the processor is a mobile version, + * platform ID has bit 50 set + * it has SpeedStep technology if either + * bit 56 or 57 is set + */ + rdmsr(MSR_IA32_PLATFORM_ID, msr_lo, msr_hi); + dprintk(KERN_DEBUG "cpufreq: Coppermine: MSR_IA32_PLATFORM ID is 0x%x, 0x%x\n", msr_lo, msr_hi); + if ((msr_hi & (1<<18)) && (msr_hi & (3<<24))) { + if (c->x86_mask == 0x01) + return SPEEDSTEP_PROCESSOR_PIII_C_EARLY; + else + return SPEEDSTEP_PROCESSOR_PIII_C; + } + + default: + return 0; + } +} +EXPORT_SYMBOL_GPL(speedstep_detect_processor); + + +/********************************************************************* + * DETECT SPEEDSTEP SPEEDS * + *********************************************************************/ + +unsigned int speedstep_get_freqs(unsigned int processor, + unsigned int *low_speed, + unsigned int *high_speed, + void (*set_state) (unsigned int state, + unsigned int notify) + ) +{ + unsigned int prev_speed; + unsigned int ret = 0; + unsigned long flags; + + if ((!processor) || (!low_speed) || (!high_speed) || (!set_state)) + return EINVAL; + + /* get current speed */ + prev_speed = speedstep_get_processor_frequency(processor); + if (!prev_speed) + return EIO; + + local_irq_save(flags); + + /* switch to low state */ + set_state(SPEEDSTEP_LOW, 0); + *low_speed = speedstep_get_processor_frequency(processor); + if (!*low_speed) { + ret = EIO; + goto out; + } + + /* switch to high state */ + set_state(SPEEDSTEP_HIGH, 0); + *high_speed = speedstep_get_processor_frequency(processor); + if (!*high_speed) { + ret = EIO; + goto out; + } + + if (*low_speed == *high_speed) { + ret = ENODEV; + goto out; + } + + /* switch to previous state, if necessary */ + if (*high_speed != prev_speed) + set_state(SPEEDSTEP_LOW, 0); + + out: + local_irq_restore(flags); + return (ret); +} +EXPORT_SYMBOL_GPL(speedstep_get_freqs); + +MODULE_AUTHOR ("Dominik Brodowski <linux@brodo.de>"); +MODULE_DESCRIPTION ("Library for Intel SpeedStep 1 or 2 cpufreq drivers."); +MODULE_LICENSE ("GPL"); diff --git a/arch/i386/kernel/cpu/cpufreq/speedstep-lib.h b/arch/i386/kernel/cpu/cpufreq/speedstep-lib.h new file mode 100644 index 000000000000..04951e0e65d1 --- /dev/null +++ b/arch/i386/kernel/cpu/cpufreq/speedstep-lib.h @@ -0,0 +1,41 @@ +/* + * (C) 2002 - 2003 Dominik Brodowski <linux@brodo.de> + * + * Licensed under the terms of the GNU GPL License version 2. + * + * Library for common functions for Intel SpeedStep v.1 and v.2 support + * + * BIG FAT DISCLAIMER: Work in progress code. Possibly *dangerous* + */ + + + +/* processors */ + +#define SPEEDSTEP_PROCESSOR_PIII_C_EARLY 0x00000001 /* Coppermine core */ +#define SPEEDSTEP_PROCESSOR_PIII_C 0x00000002 /* Coppermine core */ +#define SPEEDSTEP_PROCESSOR_PIII_T 0x00000003 /* Tualatin core */ +#define SPEEDSTEP_PROCESSOR_P4M 0x00000004 /* P4-M with 100 MHz FSB */ + +/* speedstep states -- only two of them */ + +#define SPEEDSTEP_HIGH 0x00000000 +#define SPEEDSTEP_LOW 0x00000001 + + +/* detect a speedstep-capable processor */ +extern unsigned int speedstep_detect_processor (void); + +/* detect the current speed (in khz) of the processor */ +extern unsigned int speedstep_get_processor_frequency(unsigned int processor); + + +/* detect the low and high speeds of the processor. The callback + * set_state"'s first argument is either SPEEDSTEP_HIGH or + * SPEEDSTEP_LOW; the second argument is zero so that no + * cpufreq_notify_transition calls are initiated. + */ +extern unsigned int speedstep_get_freqs(unsigned int processor, + unsigned int *low_speed, + unsigned int *high_speed, + void (*set_state) (unsigned int state, unsigned int notify)); -- 2.30.9