diff options
| author | Linus Torvalds <torvalds@home.osdl.org> | 2003-08-01 20:34:58 -0700 |
|---|---|---|
| committer | Linus Torvalds <torvalds@home.osdl.org> | 2003-08-01 20:34:58 -0700 |
| commit | 3ee1e20491bebd574738aea137fdccfcee87e89a (patch) | |
| tree | b5c30ebec02d07d465829d98a306e2fb164370a2 | |
| parent | 6fd6bef3a5b8a900c771bb1c4580fad24d50b9a9 (diff) | |
| parent | b9ecdfd31f689c82becbb7e5502969269a349d95 (diff) | |
Merge bk://linux-dj.bkbits.net/cpufreq
into home.osdl.org:/home/torvalds/v2.5/linux
| -rw-r--r-- | CREDITS | 7 | ||||
| -rw-r--r-- | Documentation/cpu-freq/core.txt | 8 | ||||
| -rw-r--r-- | Documentation/cpu-freq/cpu-drivers.txt | 14 | ||||
| -rw-r--r-- | Documentation/cpu-freq/governors.txt | 14 | ||||
| -rw-r--r-- | Documentation/cpu-freq/user-guide.txt | 2 | ||||
| -rw-r--r-- | MAINTAINERS | 10 | ||||
| -rw-r--r-- | arch/i386/kernel/cpu/common.c | 21 | ||||
| -rw-r--r-- | arch/i386/kernel/cpu/cpufreq/acpi.c | 2 | ||||
| -rw-r--r-- | arch/i386/kernel/cpu/cpufreq/longhaul.c | 206 | ||||
| -rw-r--r-- | arch/i386/kernel/cpu/cpufreq/longhaul.h | 49 | ||||
| -rw-r--r-- | arch/i386/kernel/cpu/cpufreq/longrun.c | 4 | ||||
| -rw-r--r-- | arch/i386/kernel/cpu/cpufreq/powernow-k6.c | 7 | ||||
| -rw-r--r-- | arch/i386/kernel/cpu/cpufreq/powernow-k7.c | 32 | ||||
| -rw-r--r-- | arch/i386/kernel/cpu/cpufreq/powernow-k7.h | 8 | ||||
| -rw-r--r-- | arch/i386/kernel/cpu/cpufreq/speedstep-ich.c | 8 | ||||
| -rw-r--r-- | drivers/cpufreq/userspace.c | 12 | ||||
| -rw-r--r-- | include/asm-i386/msr.h | 21 | ||||
| -rw-r--r-- | include/linux/cpufreq.h | 8 | ||||
| -rw-r--r-- | kernel/cpufreq.c | 355 |
19 files changed, 459 insertions, 329 deletions
@@ -1522,17 +1522,12 @@ S: USA N: Dave Jones E: davej@codemonkey.org.uk -E: davej@suse.de W: http://www.codemonkey.org.uk D: x86 errata/setup maintenance. D: AGPGART driver. +D: CPUFREQ maintenance. D: Backport/Forwardport merge monkey. D: Various Janitor work. -S: c/o SuSE Linux UK Ltd -S: Appleton House -S: 139 King Street -S: Hammersmith -S: W6 9JG S: United Kingdom N: Ani Joshi diff --git a/Documentation/cpu-freq/core.txt b/Documentation/cpu-freq/core.txt index 787a49b3b15c..1ad90c39753c 100644 --- a/Documentation/cpu-freq/core.txt +++ b/Documentation/cpu-freq/core.txt @@ -73,9 +73,9 @@ The phase is specified in the second argument to the notifier. The third argument, a void *pointer, points to a struct cpufreq_policy consisting of five values: cpu, min, max, policy and max_cpu_freq. min and max are the lower and upper frequencies (in kHz) of the new -policy, policy the new policy, cpu the number of the affected CPU or -CPUFREQ_ALL_CPUS for all CPUs; and max_cpu_freq the maximum supported -CPU frequency. This value is given for informational purposes only. +policy, policy the new policy, cpu the number of the affected CPU; and +max_cpu_freq the maximum supported CPU frequency. This value is given +for informational purposes only. 2.2 CPUFreq transition notifiers @@ -89,6 +89,6 @@ CPUFREQ_POSTCHANGE. The third argument is a struct cpufreq_freqs with the following values: -cpu - number of the affected CPU or CPUFREQ_ALL_CPUS +cpu - number of the affected CPU old - old frequency new - new frequency diff --git a/Documentation/cpu-freq/cpu-drivers.txt b/Documentation/cpu-freq/cpu-drivers.txt index b9bab37848a8..be5c94d0f753 100644 --- a/Documentation/cpu-freq/cpu-drivers.txt +++ b/Documentation/cpu-freq/cpu-drivers.txt @@ -41,16 +41,17 @@ on what is necessary: 1.1 Initialization ------------------ -First of all, in an __initcall level 7 or later (preferrably -module_init() so that your driver is modularized) function check -whether this kernel runs on the right CPU and the right chipset. If -so, register a struct cpufreq_driver with the CPUfreq core using -cpufreq_register_driver() +First of all, in an __initcall level 7 (module_init()) or later +function check whether this kernel runs on the right CPU and the right +chipset. If so, register a struct cpufreq_driver with the CPUfreq core +using cpufreq_register_driver() What shall this struct cpufreq_driver contain? cpufreq_driver.name - The name of this driver. +cpufreq_driver.owner - THIS_MODULE; + cpufreq_driver.init - A pointer to the per-CPU initialization function. @@ -76,8 +77,7 @@ cpufreq driver registers itself, the per-CPU initialization function cpufreq_driver.init is called. It takes a struct cpufreq_policy *policy as argument. What to do now? -If necessary, activate the CPUfreq support on your CPU (unlock that -register etc.). +If necessary, activate the CPUfreq support on your CPU. Then, the driver must fill in the following values: diff --git a/Documentation/cpu-freq/governors.txt b/Documentation/cpu-freq/governors.txt index f50fbd0c9ab2..b85481acd0ca 100644 --- a/Documentation/cpu-freq/governors.txt +++ b/Documentation/cpu-freq/governors.txt @@ -135,21 +135,21 @@ CPUfreq core to ensure proper locking. The CPUfreq governor may call the CPU processor driver using one of these two functions: -inline int cpufreq_driver_target(struct cpufreq_policy *policy, +int cpufreq_driver_target(struct cpufreq_policy *policy, unsigned int target_freq, unsigned int relation); -inline int cpufreq_driver_target_l(struct cpufreq_policy *policy, +int __cpufreq_driver_target(struct cpufreq_policy *policy, unsigned int target_freq, unsigned int relation); target_freq must be within policy->min and policy->max, of course. What's the difference between these two functions? When your governor still is in a direct code path of a call to governor->governor, the -cpufreq_driver_sem lock is still held in the cpufreq core, and there's +per-CPU cpufreq lock is still held in the cpufreq core, and there's no need to lock it again (in fact, this would cause a deadlock). So -use cpufreq_driver_target only in these cases. In all other cases (for -example, when there's a "daemonized" function that wakes up every -second), use cpufreq_driver_target_l to lock the cpufreq_driver_sem -before the command is passed to the cpufreq processor driver. +use __cpufreq_driver_target only in these cases. In all other cases +(for example, when there's a "daemonized" function that wakes up +every second), use cpufreq_driver_target to lock the cpufreq per-CPU +lock before the command is passed to the cpufreq processor driver. diff --git a/Documentation/cpu-freq/user-guide.txt b/Documentation/cpu-freq/user-guide.txt index 76934ca5030c..3123b978dfee 100644 --- a/Documentation/cpu-freq/user-guide.txt +++ b/Documentation/cpu-freq/user-guide.txt @@ -132,7 +132,7 @@ the processor shall run at. The preferred interface is located in the sysfs filesystem. If you mounted it at /sys, the cpufreq interface is located in a subdirectory "cpufreq" within the cpu-device directory -(e.g. /sys/class/cpu/cpu0/cpufreq/ for the first CPU). +(e.g. /sys/devices/system/cpu/cpu0/cpufreq/ for the first CPU). cpuinfo_min_freq : this file shows the minimum operating frequency the processor can run at(in kHz) diff --git a/MAINTAINERS b/MAINTAINERS index 47683229b5c6..4f927612306c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -431,9 +431,11 @@ W: http://www.fi.muni.cz/~kas/cosa/ S: Maintained CPU FREQUENCY DRIVERS -L: cpufreq@www.linux.org.uk -W: http://www.brodo.de/cpufreq/ -S: Maintained +P: Dave Jones +M: davej@codemonkey.org.uk +L: cpufreq@www.linux.org.uk +W: http://www.codemonkey.org.uk/cpufreq/ +S: Maintained CPUID/MSR DRIVER P: H. Peter Anvin @@ -840,7 +842,7 @@ S: Maintained i386 SETUP CODE / CPU ERRATA WORKAROUNDS P: Dave Jones -M: davej@suse.de +M: davej@codemonkey.org.uk P: H. Peter Anvin M: hpa@zytor.com S: Maintained diff --git a/arch/i386/kernel/cpu/common.c b/arch/i386/kernel/cpu/common.c index 99167880abbd..b23e821d172b 100644 --- a/arch/i386/kernel/cpu/common.c +++ b/arch/i386/kernel/cpu/common.c @@ -288,10 +288,23 @@ void __init identify_cpu(struct cpuinfo_x86 *c) c->x86 = 3; } - if (this_cpu->c_identify) + generic_identify(c); + + printk(KERN_DEBUG "CPU: After generic identify, caps: %08lx %08lx %08lx %08lx\n", + c->x86_capability[0], + c->x86_capability[1], + c->x86_capability[2], + c->x86_capability[3]); + + if (this_cpu->c_identify) { this_cpu->c_identify(c); - else - generic_identify(c); + + printk(KERN_DEBUG "CPU: After vendor identify, caps: %08lx %08lx %08lx %08lx\n", + c->x86_capability[0], + c->x86_capability[1], + c->x86_capability[2], + c->x86_capability[3]); +} /* * Vendor-specific initialization. In this section we @@ -341,7 +354,7 @@ void __init identify_cpu(struct cpuinfo_x86 *c) /* Now the feature flags better reflect actual CPU features! */ - printk(KERN_DEBUG "CPU: After generic, caps: %08lx %08lx %08lx %08lx\n", + printk(KERN_DEBUG "CPU: After all inits, caps: %08lx %08lx %08lx %08lx\n", c->x86_capability[0], c->x86_capability[1], c->x86_capability[2], diff --git a/arch/i386/kernel/cpu/cpufreq/acpi.c b/arch/i386/kernel/cpu/cpufreq/acpi.c index ce5e63859de5..36cbb5015deb 100644 --- a/arch/i386/kernel/cpu/cpufreq/acpi.c +++ b/arch/i386/kernel/cpu/cpufreq/acpi.c @@ -380,7 +380,7 @@ static int acpi_processor_perf_open_fs(struct inode *inode, struct file *file) static int acpi_processor_write_performance ( struct file *file, - const char *buffer, + const char __user *buffer, size_t count, loff_t *data) { diff --git a/arch/i386/kernel/cpu/cpufreq/longhaul.c b/arch/i386/kernel/cpu/cpufreq/longhaul.c index 0cd3a969094e..808f7f5079b1 100644 --- a/arch/i386/kernel/cpu/cpufreq/longhaul.c +++ b/arch/i386/kernel/cpu/cpufreq/longhaul.c @@ -1,7 +1,5 @@ /* - * $Id: longhaul.c,v 1.87 2003/02/22 10:23:46 db Exp $ - * - * (C) 2001 Dave Jones. <davej@suse.de> + * (C) 2001-2003 Dave Jones. <davej@codemonkey.org.uk> * (C) 2002 Padraig Brady. <padraig@antefacto.com> * * Licensed under the terms of the GNU GPL License version 2. @@ -32,6 +30,8 @@ #include <asm/timex.h> #include <asm/io.h> +#include "longhaul.h" + #define DEBUG #ifdef DEBUG @@ -40,6 +40,8 @@ #define dprintk(msg...) do { } while(0) #endif +#define PFX "longhaul: " + static unsigned int numscales=16, numvscales; static int minvid, maxvid; static int can_scale_voltage; @@ -248,7 +250,7 @@ static int clock_ratio[32]; static int eblcr_table[32]; static int voltage_table[32]; static unsigned int highest_speed, lowest_speed; /* kHz */ -static int longhaul; /* version. */ +static int longhaul_version; static struct cpufreq_frequency_table *longhaul_table; @@ -273,7 +275,7 @@ static int longhaul_get_cpu_mult (void) rdmsr (MSR_IA32_EBL_CR_POWERON, lo, hi); invalue = (lo & (1<<22|1<<23|1<<24|1<<25)) >>22; - if (longhaul==3) { + if (longhaul_version==3) { if (lo & (1<<27)) invalue+=16; } @@ -283,19 +285,18 @@ static int longhaul_get_cpu_mult (void) /** * longhaul_set_cpu_frequency() - * @clock_ratio_index : index of clock_ratio[] for new frequency + * @clock_ratio_index : bitpattern of the new multiplier. * * Sets a new clock ratio, and -if applicable- a new Front Side Bus */ static void longhaul_setstate (unsigned int clock_ratio_index) { - unsigned long lo, hi; - unsigned int bits; - int revkey; int vidindex, i; struct cpufreq_freqs freqs; - + union msr_longhaul longhaul; + union msr_bcr2 bcr2; + if (clock_ratio[clock_ratio_index] == -1) return; @@ -309,39 +310,33 @@ static void longhaul_setstate (unsigned int clock_ratio_index) cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); - dprintk (KERN_INFO "longhaul: FSB:%d Mult(x10):%d\n", + dprintk (KERN_INFO PFX "FSB:%d Mult(x10):%d\n", fsb * 100, clock_ratio[clock_ratio_index]); - bits = clock_ratio_index; - /* "bits" contains the bitpattern of the new multiplier. - we now need to transform it to the desired format. */ - - switch (longhaul) { + switch (longhaul_version) { case 1: - rdmsr (MSR_VIA_BCR2, lo, hi); - revkey = (lo & 0xf)<<4; /* Rev key. */ - lo &= ~(1<<23|1<<24|1<<25|1<<26); - lo |= (1<<19); /* Enable software clock multiplier */ - lo |= (bits<<23); /* desired multiplier */ - lo |= revkey; - wrmsr (MSR_VIA_BCR2, lo, hi); + rdmsrl (MSR_VIA_BCR2, bcr2.val); + /* Enable software clock multiplier */ + bcr2.bits.ESOFTBF = 1; + bcr2.bits.CLOCKMUL = clock_ratio_index; + wrmsrl (MSR_VIA_BCR2, bcr2.val); __hlt(); /* Disable software clock multiplier */ - rdmsr (MSR_VIA_BCR2, lo, hi); - lo &= ~(1<<19); - lo |= revkey; - wrmsr (MSR_VIA_BCR2, lo, hi); + rdmsrl (MSR_VIA_BCR2, bcr2.val); + bcr2.bits.ESOFTBF = 0; + wrmsrl (MSR_VIA_BCR2, bcr2.val); break; case 2: - rdmsr (MSR_VIA_LONGHAUL, lo, hi); - revkey = (lo & 0xf)<<4; /* Rev key. */ - lo &= 0xfff0bf0f; /* reset [19:16,14](bus ratio) and [7:4](rev key) to 0 */ - lo |= (bits<<16); - lo |= (1<<8); /* EnableSoftBusRatio */ - lo |= revkey; + rdmsrl (MSR_VIA_LONGHAUL, longhaul.val); + longhaul.bits.SoftBusRatio = clock_ratio_index & 0xf; + longhaul.bits.SoftBusRatio4 = (clock_ratio_index & 0x10) >> 4; + longhaul.bits.EnableSoftBusRatio = 1; + /* We must program the revision key only with values we + * know about, not blindly copy it from 0:3 */ + longhaul.bits.RevisionKey = 1; if (can_scale_voltage) { /* PB: TODO fix this up */ @@ -356,41 +351,41 @@ static void longhaul_setstate (unsigned int clock_ratio_index) if (i==32) goto bad_voltage; - dprintk (KERN_INFO "longhaul: Desired vid index=%d\n", i); + dprintk (KERN_INFO PFX "Desired vid index=%d\n", i); #if 0 - lo &= 0xfe0fffff;/* reset [24:20](voltage) to 0 */ - lo |= (i<<20); /* set voltage */ - lo |= (1<<9); /* EnableSoftVID */ + longhaul.bits.SoftVID = i; + longhaul.bits.EnableSoftVID = 1; #endif } - +/* FIXME: Do voltage and freq seperatly like we do in powernow-k7 */ bad_voltage: - wrmsr (MSR_VIA_LONGHAUL, lo, hi); + wrmsrl (MSR_VIA_LONGHAUL, longhaul.val); __hlt(); - rdmsr (MSR_VIA_LONGHAUL, lo, hi); - lo &= ~(1<<8); + rdmsrl (MSR_VIA_LONGHAUL, longhaul.val); + longhaul.bits.EnableSoftBusRatio = 0; if (can_scale_voltage) - lo &= ~(1<<9); - lo |= revkey; - wrmsr (MSR_VIA_LONGHAUL, lo, hi); + longhaul.bits.EnableSoftVID = 0; + longhaul.bits.RevisionKey = 1; + wrmsrl (MSR_VIA_LONGHAUL, longhaul.val); break; case 3: - rdmsr (MSR_VIA_LONGHAUL, lo, hi); - revkey = (lo & 0xf)<<4; /* Rev key. */ - lo &= 0xfff0bf0f; /* reset longhaul[19:16,14] to 0 */ - lo |= (bits<<16); - lo |= (1<<8); /* EnableSoftBusRatio */ - lo |= revkey; - - wrmsr (MSR_VIA_LONGHAUL, lo, hi); + rdmsrl (MSR_VIA_LONGHAUL, longhaul.val); + longhaul.bits.SoftBusRatio = clock_ratio_index & 0xf; + longhaul.bits.SoftBusRatio4 = (clock_ratio_index & 0x10) >> 4; + longhaul.bits.EnableSoftBusRatio = 1; + /* We must program the revision key only with values we + * know about, not blindly copy it from 0:3 */ + longhaul.bits.RevisionKey = 3; /* SoftVID & SoftBSEL */ + + wrmsrl(MSR_VIA_LONGHAUL, longhaul.val); __hlt(); - rdmsr (MSR_VIA_LONGHAUL, lo, hi); - lo &= ~(1<<8); - lo |= revkey; - wrmsr (MSR_VIA_LONGHAUL, lo, hi); + rdmsrl (MSR_VIA_LONGHAUL, longhaul.val); + longhaul.bits.EnableSoftBusRatio = 0; + longhaul.bits.RevisionKey = 3; + wrmsrl (MSR_VIA_LONGHAUL, longhaul.val); break; } @@ -400,14 +395,15 @@ bad_voltage: static int __init longhaul_get_ranges (void) { - unsigned long lo, hi, invalue; + unsigned long invalue; unsigned int minmult=0, maxmult=0; unsigned int multipliers[32]= { 50,30,40,100,55,35,45,95,90,70,80,60,120,75,85,65, -1,110,120,-1,135,115,125,105,130,150,160,140,-1,155,-1,145 }; unsigned int j, k = 0; + union msr_longhaul longhaul; - switch (longhaul) { + switch (longhaul_version) { case 1: /* Ugh, Longhaul v1 didn't have the min/max MSRs. Assume min=3.0x & max = whatever we booted at. */ @@ -416,17 +412,17 @@ static int __init longhaul_get_ranges (void) break; case 2 ... 3: - rdmsr (MSR_VIA_LONGHAUL, lo, hi); + rdmsrl (MSR_VIA_LONGHAUL, longhaul.val); - invalue = (hi & (1<<0|1<<1|1<<2|1<<3)); - if (hi & (1<<11)) + invalue = longhaul.bits.MaxMHzBR; + if (longhaul.bits.MaxMHzBR4) invalue += 16; maxmult=multipliers[invalue]; -#if 0 /* This is MaxMhz @ Min Voltage. Ignore for now */ - invalue = (hi & (1<<16|1<<17|1<<18|1<<19)) >> 16; - if (hi & (1<<27)) - invalue += 16; +#if 0 + invalue = longhaul.bits.MinMHzBR; + if (longhaul.bits.MinMHzBR4); + invalue += 16; minmult = multipliers[invalue]; #else minmult = 30; /* as per spec */ @@ -436,9 +432,9 @@ static int __init longhaul_get_ranges (void) highest_speed = maxmult * fsb * 100; lowest_speed = minmult * fsb * 100; - dprintk (KERN_INFO "longhaul: MinMult(x10)=%d MaxMult(x10)=%d\n", + dprintk (KERN_INFO PFX "MinMult(x10)=%d MaxMult(x10)=%d\n", minmult, maxmult); - dprintk (KERN_INFO "longhaul: Lowestspeed=%d Highestspeed=%d\n", + dprintk (KERN_INFO PFX "Lowestspeed=%d Highestspeed=%d\n", lowest_speed, highest_speed); longhaul_table = kmalloc((numscales + 1) * sizeof(struct cpufreq_frequency_table), GFP_KERNEL); @@ -460,43 +456,62 @@ static int __init longhaul_get_ranges (void) kfree (longhaul_table); return -EINVAL; } - + return 0; } -static void __init longhaul_setup_voltagescaling (unsigned long lo, unsigned long hi) +static void __init longhaul_setup_voltagescaling(void) { - int revkey; + union msr_longhaul longhaul; - can_scale_voltage = 1; + rdmsrl (MSR_VIA_LONGHAUL, longhaul.val); - minvid = (hi & (1<<20|1<<21|1<<22|1<<23|1<<24)) >> 20; /* 56:52 */ - maxvid = (hi & (1<<4|1<<5|1<<6|1<<7|1<<8)) >> 4; /* 40:36 */ - vrmrev = (lo & (1<<15))>>15; + if (!(longhaul.bits.RevisionID & 1)) + return; + + minvid = longhaul.bits.MinimumVID; + maxvid = longhaul.bits.MaximumVID; + vrmrev = longhaul.bits.VRMRev; + + if (minvid == 0 || maxvid == 0) { + printk (KERN_INFO PFX "Bogus values Min:%d.%03d Max:%d.%03d. " + "Voltage scaling disabled.\n", + minvid/1000, minvid%1000, maxvid/1000, maxvid%1000); + return; + } + + if (minvid == maxvid) { + printk (KERN_INFO PFX "Claims to support voltage scaling but min & max are " + "both %d.%03d. Voltage scaling disabled\n", + maxvid/1000, maxvid%1000); + return; + } if (vrmrev==0) { - dprintk (KERN_INFO "longhaul: VRM 8.5 : "); + dprintk (KERN_INFO PFX "VRM 8.5 : "); memcpy (voltage_table, vrm85scales, sizeof(voltage_table)); numvscales = (voltage_table[maxvid]-voltage_table[minvid])/25; } else { - dprintk (KERN_INFO "longhaul: Mobile VRM : "); + dprintk (KERN_INFO PFX "Mobile VRM : "); memcpy (voltage_table, mobilevrmscales, sizeof(voltage_table)); numvscales = (voltage_table[maxvid]-voltage_table[minvid])/5; } /* Current voltage isn't readable at first, so we need to set it to a known value. The spec says to use maxvid */ - revkey = (lo & 0xf)<<4; /* Rev key. */ - lo &= 0xfe0fff0f; /* Mask unneeded bits */ - lo |= (1<<9); /* EnableSoftVID */ - lo |= revkey; /* Reinsert key */ - lo |= maxvid << 20; - wrmsr (MSR_VIA_LONGHAUL, lo, hi); + longhaul.bits.RevisionKey = longhaul.bits.RevisionID; /* FIXME: This is bad. */ + longhaul.bits.EnableSoftVID = 1; + longhaul.bits.SoftVID = maxvid; + wrmsrl (MSR_VIA_LONGHAUL, longhaul.val); + minvid = voltage_table[minvid]; maxvid = voltage_table[maxvid]; + dprintk ("Min VID=%d.%03d Max VID=%d.%03d, %d possible voltage scales\n", maxvid/1000, maxvid%1000, minvid/1000, minvid%1000, numvscales); + + can_scale_voltage = 1; } @@ -510,8 +525,8 @@ static int longhaul_target (struct cpufreq_policy *policy, unsigned int target_freq, unsigned int relation) { - unsigned int table_index = 0; - unsigned int new_clock_ratio = 0; + unsigned int table_index = 0; + unsigned int new_clock_ratio = 0; if (cpufreq_frequency_table_target(policy, longhaul_table, target_freq, relation, &table_index)) return -EINVAL; @@ -530,7 +545,7 @@ static int longhaul_cpu_init (struct cpufreq_policy *policy) switch (c->x86_model) { case 6: /* VIA C3 Samuel C5A */ - longhaul=1; + longhaul_version=1; memcpy (clock_ratio, longhaul1_clock_ratio, sizeof(longhaul1_clock_ratio)); memcpy (eblcr_table, samuel1_eblcr, sizeof(samuel1_eblcr)); break; @@ -538,12 +553,12 @@ static int longhaul_cpu_init (struct cpufreq_policy *policy) case 7: /* C5B / C5C */ switch (c->x86_mask) { case 0: - longhaul=1; + longhaul_version=1; memcpy (clock_ratio, longhaul1_clock_ratio, sizeof(longhaul1_clock_ratio)); memcpy (eblcr_table, samuel2_eblcr, sizeof(samuel2_eblcr)); break; case 1 ... 15: - longhaul=2; + longhaul_version=2; memcpy (clock_ratio, longhaul2_clock_ratio, sizeof(longhaul2_clock_ratio)); memcpy (eblcr_table, ezra_eblcr, sizeof(ezra_eblcr)); break; @@ -552,21 +567,18 @@ static int longhaul_cpu_init (struct cpufreq_policy *policy) case 8: /* C5M/C5N */ return -ENODEV; // Waiting on updated docs from VIA before this is usable - longhaul=3; + longhaul_version=3; numscales=32; memcpy (clock_ratio, longhaul3_clock_ratio, sizeof(longhaul3_clock_ratio)); memcpy (eblcr_table, c5m_eblcr, sizeof(c5m_eblcr)); break; } - printk (KERN_INFO "longhaul: VIA CPU detected. Longhaul version %d supported\n", longhaul); + printk (KERN_INFO PFX "VIA CPU detected. Longhaul version %d supported\n", + longhaul_version); - if (longhaul==2 || longhaul==3) { - unsigned long lo, hi; - rdmsr (MSR_VIA_LONGHAUL, lo, hi); - if ((lo & (1<<0)) && (dont_scale_voltage==0)) - longhaul_setup_voltagescaling (lo, hi); - } + if ((longhaul_version==2 || longhaul_version==3) && (dont_scale_voltage==0)) + longhaul_setup_voltagescaling(); ret = longhaul_get_ranges(); if (ret != 0) @@ -601,7 +613,7 @@ static int __init longhaul_init (void) case 8: return -ENODEV; default: - printk (KERN_INFO "longhaul: Unknown VIA CPU. Contact davej@suse.de\n"); + printk (KERN_INFO PFX "Unknown VIA CPU. Contact davej@codemonkey.org.uk\n"); } return -ENODEV; @@ -615,7 +627,7 @@ static void __exit longhaul_exit (void) MODULE_PARM (dont_scale_voltage, "i"); -MODULE_AUTHOR ("Dave Jones <davej@suse.de>"); +MODULE_AUTHOR ("Dave Jones <davej@codemonkey.org.uk>"); MODULE_DESCRIPTION ("Longhaul driver for VIA Cyrix processors."); MODULE_LICENSE ("GPL"); diff --git a/arch/i386/kernel/cpu/cpufreq/longhaul.h b/arch/i386/kernel/cpu/cpufreq/longhaul.h new file mode 100644 index 000000000000..3070d708cb56 --- /dev/null +++ b/arch/i386/kernel/cpu/cpufreq/longhaul.h @@ -0,0 +1,49 @@ +/* + * longhaul.h + * (C) 2003 Dave Jones. + * + * Licensed under the terms of the GNU GPL License version 2. + * + * VIA-specific information + */ + +union msr_bcr2 { + struct { + unsigned Reseved:19, // 18:0 + ESOFTBF:1, // 19 + Reserved2:3, // 22:20 + CLOCKMUL:4, // 26:23 + Reserved3:5; // 31:27 + } bits; + unsigned long val; +}; + +union msr_longhaul { + struct { + unsigned RevisionID:4, // 3:0 + RevisionKey:4, // 7:4 + EnableSoftBusRatio:1, // 8 + EnableSoftVID:1, // 9 + EnableSoftBSEL:1, // 10 + Reserved:3, // 11:13 + SoftBusRatio4:1, // 14 + VRMRev:1, // 15 + SoftBusRatio:4, // 19:16 + SoftVID:5, // 24:20 + Reserved2:3, // 27:25 + SoftBSEL:2, // 29:28 + Reserved3:2, // 31:30 + MaxMHzBR:4, // 35:32 + MaximumVID:5, // 40:36 + MaxMHzFSB:2, // 42:41 + MaxMHzBR4:1, // 43 + Reserved4:4, // 47:44 + MinMHzBR:4, // 51:48 + MinimumVID:5, // 56:52 + MinMHzFSB:2, // 58:57 + MinMHzBR4:1, // 59 + Reserved5:4; // 63:60 + } bits; + unsigned long long val; +}; + diff --git a/arch/i386/kernel/cpu/cpufreq/longrun.c b/arch/i386/kernel/cpu/cpufreq/longrun.c index 67fb82ed92ee..2adb51a8d3d4 100644 --- a/arch/i386/kernel/cpu/cpufreq/longrun.c +++ b/arch/i386/kernel/cpu/cpufreq/longrun.c @@ -1,6 +1,4 @@ /* - * $Id: longrun.c,v 1.25 2003/02/28 16:03:50 db Exp $ - * * (C) 2002 - 2003 Dominik Brodowski <linux@brodo.de> * * Licensed under the terms of the GNU GPL License version 2. @@ -123,7 +121,7 @@ static int longrun_verify_policy(struct cpufreq_policy *policy) policy->cpuinfo.max_freq); if (policy->policy == CPUFREQ_POLICY_GOVERNOR) - policy->policy = longrun_driver.policy[0].policy; + return -EINVAL; return 0; } diff --git a/arch/i386/kernel/cpu/cpufreq/powernow-k6.c b/arch/i386/kernel/cpu/cpufreq/powernow-k6.c index e914a1d0efd0..5d967dfe4dbc 100644 --- a/arch/i386/kernel/cpu/cpufreq/powernow-k6.c +++ b/arch/i386/kernel/cpu/cpufreq/powernow-k6.c @@ -1,8 +1,5 @@ /* - * $Id: powernow-k6.c,v 1.48 2003/02/22 10:23:46 db Exp $ - * This file was part of Powertweak Linux (http://powertweak.sf.net) - * and is shared with the Linux Kernel module. - * + * This file was based upon code in Powertweak Linux (http://powertweak.sf.net) * (C) 2000-2003 Dave Jones, Arjan van de Ven, Janne Pänkälä, Dominik Brodowski. * * Licensed under the terms of the GNU GPL License version 2. @@ -230,7 +227,7 @@ static void __exit powernow_k6_exit(void) } -MODULE_AUTHOR ("Arjan van de Ven <arjanv@redhat.com>, Dave Jones <davej@suse.de>, Dominik Brodowski <linux@brodo.de>"); +MODULE_AUTHOR ("Arjan van de Ven <arjanv@redhat.com>, Dave Jones <davej@codemonkey.org.uk>, Dominik Brodowski <linux@brodo.de>"); MODULE_DESCRIPTION ("PowerNow! driver for AMD K6-2+ / K6-3+ processors."); MODULE_LICENSE ("GPL"); diff --git a/arch/i386/kernel/cpu/cpufreq/powernow-k7.c b/arch/i386/kernel/cpu/cpufreq/powernow-k7.c index 36181c7a0c64..f1dce06bc217 100644 --- a/arch/i386/kernel/cpu/cpufreq/powernow-k7.c +++ b/arch/i386/kernel/cpu/cpufreq/powernow-k7.c @@ -1,7 +1,6 @@ /* - * $Id: powernow-k7.c,v 1.34 2003/02/22 10:23:46 db Exp $ - * - * (C) 2003 Dave Jones <davej@suse.de> + * AMD K7 Powernow driver. + * (C) 2003 Dave Jones <davej@codemonkey.org.uk> on behalf of SuSE Labs. * * Licensed under the terms of the GNU GPL License version 2. * Based upon datasheets & sample CPUs kindly provided by AMD. @@ -85,27 +84,6 @@ static unsigned int latency; static char have_a0; -#ifndef rdmsrl -#define rdmsrl(msr,val) do {unsigned long l__,h__; \ - rdmsr (msr, l__, h__); \ - val = l__; \ - val |= ((u64)h__<<32); \ -} while(0) -#endif - -#ifndef wrmsrl -static void wrmsrl (u32 msr, u64 val) -{ - u32 lo, hi; - - lo = (u32) val; - hi = val >> 32; - wrmsr (msr, lo, hi); -} -#endif - - - static int check_powernow(void) { struct cpuinfo_x86 *c = cpu_data; @@ -240,6 +218,7 @@ static void change_speed (unsigned int index) u8 fid, vid; struct cpufreq_freqs freqs; union msr_fidvidstatus fidvidstatus; + int cfid; /* fid are the lower 8 bits of the index we stored into * the cpufreq frequency table in powernow_decode_bios, @@ -251,8 +230,9 @@ static void change_speed (unsigned int index) freqs.cpu = 0; + cfid = fidvidstatus.bits.CFID; rdmsrl (MSR_K7_FID_VID_STATUS, fidvidstatus.val); - freqs.old = fsb * fid_codes[fidvidstatus.bits.CFID] * 100; + freqs.old = fsb * fid_codes[cfid] * 100; freqs.new = powernow_table[index].frequency; cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); @@ -426,7 +406,7 @@ static void __exit powernow_exit (void) kfree(powernow_table); } -MODULE_AUTHOR ("Dave Jones <davej@suse.de>"); +MODULE_AUTHOR ("Dave Jones <davej@codemonkey.org.uk>"); MODULE_DESCRIPTION ("Powernow driver for AMD K7 processors."); MODULE_LICENSE ("GPL"); diff --git a/arch/i386/kernel/cpu/cpufreq/powernow-k7.h b/arch/i386/kernel/cpu/cpufreq/powernow-k7.h index ac408007303a..f8a63b3664e3 100644 --- a/arch/i386/kernel/cpu/cpufreq/powernow-k7.h +++ b/arch/i386/kernel/cpu/cpufreq/powernow-k7.h @@ -8,14 +8,6 @@ * */ -#ifndef MSR_K7_FID_VID_CTL -#define MSR_K7_FID_VID_CTL 0xc0010041 -#endif -#ifndef MSR_K7_FID_VID_STATUS -#define MSR_K7_FID_VID_STATUS 0xc0010042 -#endif - - union msr_fidvidctl { struct { unsigned FID:5, // 4:0 diff --git a/arch/i386/kernel/cpu/cpufreq/speedstep-ich.c b/arch/i386/kernel/cpu/cpufreq/speedstep-ich.c index c7631fde9e20..500b95d66a79 100644 --- a/arch/i386/kernel/cpu/cpufreq/speedstep-ich.c +++ b/arch/i386/kernel/cpu/cpufreq/speedstep-ich.c @@ -82,7 +82,7 @@ static void speedstep_set_state (unsigned int state, unsigned int notify) return; freqs.old = speedstep_get_processor_frequency(speedstep_processor); - freqs.new = speedstep_freqs[SPEEDSTEP_LOW].frequency; + freqs.new = speedstep_freqs[state].frequency; freqs.cpu = 0; /* speedstep.c is UP only driver */ if (notify) @@ -137,7 +137,7 @@ static void speedstep_set_state (unsigned int state, unsigned int notify) 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)); + dprintk (KERN_INFO "cpufreq: change to %u MHz succeeded\n", (speedstep_get_processor_frequency(speedstep_processor) / 1000)); } else { printk (KERN_ERR "cpufreq: change failed - I/O error\n"); } @@ -295,7 +295,7 @@ static int speedstep_cpu_init(struct cpufreq_policy *policy) return -EIO; dprintk(KERN_INFO "cpufreq: currently at %s speed setting - %i MHz\n", - (speed == speedstep_low_freq) ? "low" : "high", + (speed == speedstep_freqs[SPEEDSTEP_LOW].frequency) ? "low" : "high", (speed / 1000)); /* cpuinfo and default policy values */ @@ -356,7 +356,7 @@ static void __exit speedstep_exit(void) } -MODULE_AUTHOR ("Dave Jones <davej@suse.de>, Dominik Brodowski <linux@brodo.de>"); +MODULE_AUTHOR ("Dave Jones <davej@codemonkey.org.uk>, Dominik Brodowski <linux@brodo.de>"); MODULE_DESCRIPTION ("Speedstep driver for Intel mobile processors on chipsets with ICH-M southbridges."); MODULE_LICENSE ("GPL"); diff --git a/drivers/cpufreq/userspace.c b/drivers/cpufreq/userspace.c index 0cd13925340c..7df32a97c50a 100644 --- a/drivers/cpufreq/userspace.c +++ b/drivers/cpufreq/userspace.c @@ -158,7 +158,7 @@ EXPORT_SYMBOL(cpufreq_get); /*********************** cpufreq_sysctl interface ********************/ static int cpufreq_procctl(ctl_table *ctl, int write, struct file *filp, - void *buffer, size_t *lenp) + void __user *buffer, size_t *lenp) { char buf[16], *p; int cpu = (int) ctl->extra1; @@ -195,9 +195,9 @@ cpufreq_procctl(ctl_table *ctl, int write, struct file *filp, } static int -cpufreq_sysctl(ctl_table *table, int *name, int nlen, - void *oldval, size_t *oldlenp, - void *newval, size_t newlen, void **context) +cpufreq_sysctl(ctl_table *table, int __user *name, int nlen, + void __user *oldval, size_t __user *oldlenp, + void __user *newval, size_t newlen, void **context) { int cpu = (int) table->extra1; @@ -524,10 +524,10 @@ static int cpufreq_governor_userspace(struct cpufreq_policy *policy, cpu_min_freq[cpu] = policy->min; cpu_max_freq[cpu] = policy->max; if (policy->max < cpu_cur_freq[cpu]) - cpufreq_driver_target(¤t_policy[cpu], policy->max, + __cpufreq_driver_target(¤t_policy[cpu], policy->max, CPUFREQ_RELATION_H); else if (policy->min > cpu_cur_freq[cpu]) - cpufreq_driver_target(¤t_policy[cpu], policy->min, + __cpufreq_driver_target(¤t_policy[cpu], policy->min, CPUFREQ_RELATION_L); memcpy (¤t_policy[cpu], policy, sizeof(struct cpufreq_policy)); up(&userspace_sem); diff --git a/include/asm-i386/msr.h b/include/asm-i386/msr.h index b89b597e960c..ffdeea9d1775 100644 --- a/include/asm-i386/msr.h +++ b/include/asm-i386/msr.h @@ -8,15 +8,30 @@ */ #define rdmsr(msr,val1,val2) \ - __asm__ __volatile__("rdmsr" \ + __asm__ __volatile__("rdmsr" \ : "=a" (val1), "=d" (val2) \ : "c" (msr)) #define wrmsr(msr,val1,val2) \ - __asm__ __volatile__("wrmsr" \ + __asm__ __volatile__("wrmsr" \ : /* no outputs */ \ : "c" (msr), "a" (val1), "d" (val2)) +#define rdmsrl(msr,val) do { \ + unsigned long l__,h__; \ + rdmsr (msr, l__, h__); \ + val = l__; \ + val |= ((u64)h__<<32); \ +} while(0) + +static inline void wrmsrl (unsigned long msr, unsigned long long val) +{ + unsigned long lo, hi; + lo = (unsigned long) val; + hi = val >> 32; + wrmsr (msr, lo, hi); +} + #define rdtsc(low,high) \ __asm__ __volatile__("rdtsc" : "=a" (low), "=d" (high)) @@ -200,7 +215,7 @@ #define MSR_K7_HWCR 0xC0010015 #define MSR_K7_CLK_CTL 0xC001001b #define MSR_K7_FID_VID_CTL 0xC0010041 -#define MSR_K7_VID_STATUS 0xC0010042 +#define MSR_K7_FID_VID_STATUS 0xC0010042 /* Centaur-Hauls/IDT defined MSRs. */ #define MSR_IDT_FCR1 0x107 diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h index dc29d5c4e713..3d466f6680cb 100644 --- a/include/linux/cpufreq.h +++ b/include/linux/cpufreq.h @@ -137,9 +137,13 @@ struct cpufreq_governor { /* pass a target to the cpufreq driver */ -inline int cpufreq_driver_target(struct cpufreq_policy *policy, +extern int cpufreq_driver_target(struct cpufreq_policy *policy, unsigned int target_freq, unsigned int relation); +extern int __cpufreq_driver_target(struct cpufreq_policy *policy, + unsigned int target_freq, + unsigned int relation); + /* pass an event to the cpufreq governor */ int cpufreq_governor(unsigned int cpu, unsigned int event); @@ -160,8 +164,6 @@ struct cpufreq_driver { struct module *owner; char name[CPUFREQ_NAME_LEN]; - struct cpufreq_policy *policy; - /* needed by all drivers */ int (*init) (struct cpufreq_policy *policy); int (*verify) (struct cpufreq_policy *policy); diff --git a/kernel/cpufreq.c b/kernel/cpufreq.c index bb6fa8053241..3f2f06b6bead 100644 --- a/kernel/cpufreq.c +++ b/kernel/cpufreq.c @@ -27,20 +27,23 @@ /** * The "cpufreq driver" - the arch- or hardware-dependend low - * level driver of CPUFreq support, and its locking mutex. - * cpu_max_freq is in kHz. + * level driver of CPUFreq support, and its spinlock. This lock + * also protects the cpufreq_cpu_data array. */ static struct cpufreq_driver *cpufreq_driver; -static DECLARE_MUTEX (cpufreq_driver_sem); +static struct cpufreq_policy *cpufreq_cpu_data[NR_CPUS]; +static spinlock_t cpufreq_driver_lock = SPIN_LOCK_UNLOCKED; + +/* internal prototype */ +static int __cpufreq_governor(struct cpufreq_policy *policy, unsigned int event); + /** * Two notifier lists: the "policy" list is involved in the * validation process for a new CPU frequency policy; the * "transition" list for kernel code that needs to handle * changes to devices when the CPU clock speed changes. - * The mutex locks both lists. If both cpufreq_driver_sem - * and cpufreq_notifier_sem need to be hold, get cpufreq_driver_sem - * first. + * The mutex locks both lists. */ static struct notifier_block *cpufreq_policy_notifier_list; static struct notifier_block *cpufreq_transition_notifier_list; @@ -50,28 +53,49 @@ static DECLARE_RWSEM (cpufreq_notifier_rwsem); static LIST_HEAD(cpufreq_governor_list); static DECLARE_MUTEX (cpufreq_governor_sem); -static int cpufreq_cpu_get(unsigned int cpu) +static struct cpufreq_policy * cpufreq_cpu_get(unsigned int cpu) { + struct cpufreq_policy *data; + unsigned long flags; + if (cpu >= NR_CPUS) - return 0; + goto err_out; + + /* get the cpufreq driver */ + spin_lock_irqsave(&cpufreq_driver_lock, flags); if (!cpufreq_driver) - return 0; + goto err_out_unlock; if (!try_module_get(cpufreq_driver->owner)) - return 0; + goto err_out_unlock; - if (!kobject_get(&cpufreq_driver->policy[cpu].kobj)) { - module_put(cpufreq_driver->owner); - return 0; - } - return 1; + /* get the CPU */ + data = cpufreq_cpu_data[cpu]; + + if (!data) + goto err_out_put_module; + + if (!kobject_get(&data->kobj)) + goto err_out_put_module; + + + spin_unlock_irqrestore(&cpufreq_driver_lock, flags); + + return data; + + err_out_put_module: + module_put(cpufreq_driver->owner); + err_out_unlock: + spin_unlock_irqrestore(&cpufreq_driver_lock, flags); + err_out: + return NULL; } -static void cpufreq_cpu_put(unsigned int cpu) +static void cpufreq_cpu_put(struct cpufreq_policy *data) { - kobject_put(&cpufreq_driver->policy[cpu].kobj); + kobject_put(&data->kobj); module_put(cpufreq_driver->owner); } @@ -278,10 +302,11 @@ static ssize_t show(struct kobject * kobj, struct attribute * attr ,char * buf) struct cpufreq_policy * policy = to_policy(kobj); struct freq_attr * fattr = to_attr(attr); ssize_t ret; - if (!cpufreq_cpu_get(policy->cpu)) + policy = cpufreq_cpu_get(policy->cpu); + if (!policy) return -EINVAL; ret = fattr->show ? fattr->show(policy,buf) : 0; - cpufreq_cpu_put(policy->cpu); + cpufreq_cpu_put(policy); return ret; } @@ -291,10 +316,11 @@ static ssize_t store(struct kobject * kobj, struct attribute * attr, struct cpufreq_policy * policy = to_policy(kobj); struct freq_attr * fattr = to_attr(attr); ssize_t ret; - if (!cpufreq_cpu_get(policy->cpu)) + policy = cpufreq_cpu_get(policy->cpu); + if (!policy) return -EINVAL; ret = fattr->store ? fattr->store(policy,buf,count) : 0; - cpufreq_cpu_put(policy->cpu); + cpufreq_cpu_put(policy); return ret; } @@ -328,28 +354,28 @@ static int cpufreq_add_dev (struct sys_device * sys_dev) struct cpufreq_policy new_policy; struct cpufreq_policy *policy; struct freq_attr **drv_attr; + unsigned long flags; if (!try_module_get(cpufreq_driver->owner)) return -EINVAL; + policy = kmalloc(sizeof(struct cpufreq_policy), GFP_KERNEL); + if (!policy) + return -ENOMEM; + memset(policy, 0, sizeof(struct cpufreq_policy)); + + policy->cpu = cpu; + init_MUTEX_LOCKED(&policy->lock); + init_completion(&policy->kobj_unregister); + /* call driver. From then on the cpufreq must be able * to accept all calls to ->verify and ->setpolicy for this CPU */ - policy = &cpufreq_driver->policy[cpu]; - policy->cpu = cpu; ret = cpufreq_driver->init(policy); if (ret) - goto out; - - /* set default policy on this CPU */ - down(&cpufreq_driver_sem); - memcpy(&new_policy, - policy, - sizeof(struct cpufreq_policy)); - up(&cpufreq_driver_sem); + goto err_out; - init_MUTEX(&policy->lock); - init_completion(&policy->kobj_unregister); + memcpy(&new_policy, policy, sizeof(struct cpufreq_policy)); /* prepare interface data */ policy->kobj.parent = &sys_dev->kobj; @@ -358,7 +384,7 @@ static int cpufreq_add_dev (struct sys_device * sys_dev) ret = kobject_register(&policy->kobj); if (ret) - goto out; + goto err_out; /* set up files for this cpu device */ drv_attr = cpufreq_driver->attr; @@ -366,15 +392,32 @@ static int cpufreq_add_dev (struct sys_device * sys_dev) sysfs_create_file(&policy->kobj, &((*drv_attr)->attr)); drv_attr++; } + + spin_lock_irqsave(&cpufreq_driver_lock, flags); + cpufreq_cpu_data[cpu] = policy; + spin_unlock_irqrestore(&cpufreq_driver_lock, flags); + + up(&policy->lock); /* set default policy */ ret = cpufreq_set_policy(&new_policy); - if (ret) { - kobject_unregister(&policy->kobj); - wait_for_completion(&policy->kobj_unregister); - } + if (ret) + goto err_out_unregister; - out: + module_put(cpufreq_driver->owner); + return 0; + + + err_out_unregister: + spin_lock_irqsave(&cpufreq_driver_lock, flags); + cpufreq_cpu_data[cpu] = NULL; + spin_unlock_irqrestore(&cpufreq_driver_lock, flags); + + kobject_unregister(&policy->kobj); + wait_for_completion(&policy->kobj_unregister); + + err_out: + kfree(policy); module_put(cpufreq_driver->owner); return ret; } @@ -388,34 +431,39 @@ static int cpufreq_add_dev (struct sys_device * sys_dev) static int cpufreq_remove_dev (struct sys_device * sys_dev) { unsigned int cpu = sys_dev->id; + unsigned long flags; + struct cpufreq_policy *data; - if (!kobject_get(&cpufreq_driver->policy[cpu].kobj)) - return -EFAULT; + spin_lock_irqsave(&cpufreq_driver_lock, flags); + data = cpufreq_cpu_data[cpu]; - down(&cpufreq_driver_sem); - if ((cpufreq_driver->target) && - (cpufreq_driver->policy[cpu].policy == CPUFREQ_POLICY_GOVERNOR)) { - cpufreq_driver->policy[cpu].governor->governor(&cpufreq_driver->policy[cpu], CPUFREQ_GOV_STOP); - module_put(cpufreq_driver->policy[cpu].governor->owner); + if (!data) { + spin_unlock_irqrestore(&cpufreq_driver_lock, flags); + return -EINVAL; } + cpufreq_cpu_data[cpu] = NULL; + spin_unlock_irqrestore(&cpufreq_driver_lock, flags); - /* we may call driver->exit here without checking for try_module_exit - * as it's either the driver which wants to unload or we have a CPU - * removal AND driver removal at the same time... - */ - if (cpufreq_driver->exit) - cpufreq_driver->exit(&cpufreq_driver->policy[cpu]); + if (!kobject_get(&data->kobj)) + return -EFAULT; - kobject_unregister(&cpufreq_driver->policy[cpu].kobj); + kobject_unregister(&data->kobj); - up(&cpufreq_driver_sem); - kobject_put(&cpufreq_driver->policy[cpu].kobj); + kobject_put(&data->kobj); /* we need to make sure that the underlying kobj is actually - * destroyed before we proceed e.g. with cpufreq driver module - * unloading + * not referenced anymore by anybody before we proceed with + * unloading. */ - wait_for_completion(&cpufreq_driver->policy[cpu].kobj_unregister); + wait_for_completion(&data->kobj_unregister); + + if (cpufreq_driver->target) + __cpufreq_governor(data, CPUFREQ_GOV_STOP); + + if (cpufreq_driver->exit) + cpufreq_driver->exit(data); + + kfree(data); return 0; } @@ -431,15 +479,20 @@ static int cpufreq_restore(struct sys_device * sysdev) int cpu = sysdev->id; unsigned int ret = 0; struct cpufreq_policy policy; + struct cpufreq_policy *cpu_policy; - if (cpu_online(cpu) && cpufreq_cpu_get(cpu)) { - down(&cpufreq_driver_sem); - memcpy(&policy, &cpufreq_driver->policy[cpu], - sizeof(struct cpufreq_policy)); - up(&cpufreq_driver_sem); - ret = cpufreq_set_policy(&policy); - cpufreq_cpu_put(cpu); - } + if (!cpu_online(cpu)) + return 0; + + cpu_policy = cpufreq_cpu_get(cpu); + + down(&cpu_policy->lock); + memcpy(&policy, cpu_policy, sizeof(struct cpufreq_policy)); + up(&cpu_policy->lock); + + ret = cpufreq_set_policy(&policy); + + cpufreq_cpu_put(cpu_policy); return ret; } @@ -526,68 +579,86 @@ EXPORT_SYMBOL(cpufreq_unregister_notifier); * GOVERNORS * *********************************************************************/ -inline int cpufreq_driver_target(struct cpufreq_policy *policy, - unsigned int target_freq, - unsigned int relation) + +int __cpufreq_driver_target(struct cpufreq_policy *policy, + unsigned int target_freq, + unsigned int relation) +{ + return cpufreq_driver->target(policy, target_freq, relation); +} +EXPORT_SYMBOL_GPL(__cpufreq_driver_target); + + +int cpufreq_driver_target(struct cpufreq_policy *policy, + unsigned int target_freq, + unsigned int relation) { unsigned int ret; - unsigned int cpu = policy->cpu; - if (!cpufreq_cpu_get(cpu)) + policy = cpufreq_cpu_get(policy->cpu); + if (!policy) return -EINVAL; - down(&cpufreq_driver->policy[cpu].lock); + down(&policy->lock); - ret = cpufreq_driver->target(policy, target_freq, relation); + ret = __cpufreq_driver_target(policy, target_freq, relation); - up(&cpufreq_driver->policy[cpu].lock); + up(&policy->lock); - cpufreq_cpu_put(cpu); + cpufreq_cpu_put(policy); return ret; } EXPORT_SYMBOL_GPL(cpufreq_driver_target); -int cpufreq_governor(unsigned int cpu, unsigned int event) +static int __cpufreq_governor(struct cpufreq_policy *policy, unsigned int event) { int ret = 0; - struct cpufreq_policy *policy = &cpufreq_driver->policy[cpu]; - - if (!cpufreq_cpu_get(cpu)) - return -EINVAL; switch (policy->policy) { case CPUFREQ_POLICY_POWERSAVE: if ((event == CPUFREQ_GOV_LIMITS) || (event == CPUFREQ_GOV_START)) { - down(&cpufreq_driver->policy[cpu].lock); - ret = cpufreq_driver->target(policy, policy->min, CPUFREQ_RELATION_L); - up(&cpufreq_driver->policy[cpu].lock); + ret = __cpufreq_driver_target(policy, policy->min, CPUFREQ_RELATION_L); } break; case CPUFREQ_POLICY_PERFORMANCE: if ((event == CPUFREQ_GOV_LIMITS) || (event == CPUFREQ_GOV_START)) { - down(&cpufreq_driver->policy[cpu].lock); - ret = cpufreq_driver->target(policy, policy->max, CPUFREQ_RELATION_H); - up(&cpufreq_driver->policy[cpu].lock); + ret = __cpufreq_driver_target(policy, policy->max, CPUFREQ_RELATION_H); } break; case CPUFREQ_POLICY_GOVERNOR: ret = -EINVAL; - if (!try_module_get(cpufreq_driver->policy[cpu].governor->owner)) + if (!try_module_get(policy->governor->owner)) break; - ret = cpufreq_driver->policy[cpu].governor->governor(policy, event); + ret = policy->governor->governor(policy, event); /* we keep one module reference alive for each CPU governed by this CPU */ if ((event != CPUFREQ_GOV_START) || ret) - module_put(cpufreq_driver->policy[cpu].governor->owner); + module_put(policy->governor->owner); if ((event == CPUFREQ_GOV_STOP) && !ret) - module_put(cpufreq_driver->policy[cpu].governor->owner); + module_put(policy->governor->owner); break; default: ret = -EINVAL; } - cpufreq_cpu_put(cpu); + return ret; +} + + +int cpufreq_governor(unsigned int cpu, unsigned int event) +{ + int ret = 0; + struct cpufreq_policy *policy = cpufreq_cpu_get(cpu); + + if (!policy) + return -EINVAL; + + down(&policy->lock); + ret = __cpufreq_governor(policy, event); + up(&policy->lock); + + cpufreq_cpu_put(policy); return ret; } @@ -649,16 +720,19 @@ EXPORT_SYMBOL_GPL(cpufreq_unregister_governor); */ int cpufreq_get_policy(struct cpufreq_policy *policy, unsigned int cpu) { - if (!policy || !cpufreq_cpu_get(cpu)) + struct cpufreq_policy *cpu_policy; + if (!policy) return -EINVAL; - down(&cpufreq_driver_sem); - memcpy(policy, - &cpufreq_driver->policy[cpu], - sizeof(struct cpufreq_policy)); - up(&cpufreq_driver_sem); + cpu_policy = cpufreq_cpu_get(cpu); + if (!cpu_policy) + return -EINVAL; - cpufreq_cpu_put(cpu); + down(&cpu_policy->lock); + memcpy(policy, cpu_policy, sizeof(struct cpufreq_policy)); + up(&cpu_policy->lock); + + cpufreq_cpu_put(cpu_policy); return 0; } @@ -674,15 +748,21 @@ EXPORT_SYMBOL(cpufreq_get_policy); int cpufreq_set_policy(struct cpufreq_policy *policy) { int ret = 0; + struct cpufreq_policy *data; - if (!policy || !cpufreq_cpu_get(policy->cpu)) + if (!policy) return -EINVAL; - down(&cpufreq_driver_sem); + data = cpufreq_cpu_get(policy->cpu); + if (!data) + return -EINVAL; + + /* lock this CPU */ + down(&data->lock); + memcpy(&policy->cpuinfo, - &cpufreq_driver->policy[policy->cpu].cpuinfo, + &data->cpuinfo, sizeof(struct cpufreq_cpuinfo)); - up(&cpufreq_driver_sem); /* verify the cpu speed can be set within this limit */ ret = cpufreq_driver->verify(policy); @@ -713,39 +793,39 @@ int cpufreq_set_policy(struct cpufreq_policy *policy) up_read(&cpufreq_notifier_rwsem); - /* from here on we limit it to one limit and/or governor change running at the moment */ - down(&cpufreq_driver_sem); - cpufreq_driver->policy[policy->cpu].min = policy->min; - cpufreq_driver->policy[policy->cpu].max = policy->max; + data->min = policy->min; + data->max = policy->max; if (cpufreq_driver->setpolicy) { - cpufreq_driver->policy[policy->cpu].policy = policy->policy; + data->policy = policy->policy; ret = cpufreq_driver->setpolicy(policy); } else { - if ((policy->policy != cpufreq_driver->policy[policy->cpu].policy) || - ((policy->policy == CPUFREQ_POLICY_GOVERNOR) && (policy->governor != cpufreq_driver->policy[policy->cpu].governor))) { - unsigned int old_pol = cpufreq_driver->policy[policy->cpu].policy; - struct cpufreq_governor *old_gov = cpufreq_driver->policy[policy->cpu].governor; + if ((policy->policy != data->policy) || + ((policy->policy == CPUFREQ_POLICY_GOVERNOR) && (policy->governor != data->governor))) { + /* save old, working values */ + unsigned int old_pol = data->policy; + struct cpufreq_governor *old_gov = data->governor; + /* end old governor */ - cpufreq_governor(policy->cpu, CPUFREQ_GOV_STOP); - cpufreq_driver->policy[policy->cpu].policy = policy->policy; - cpufreq_driver->policy[policy->cpu].governor = policy->governor; + __cpufreq_governor(data, CPUFREQ_GOV_STOP); + /* start new governor */ - if (cpufreq_governor(policy->cpu, CPUFREQ_GOV_START)) { - cpufreq_driver->policy[policy->cpu].policy = old_pol; - cpufreq_driver->policy[policy->cpu].governor = old_gov; - cpufreq_governor(policy->cpu, CPUFREQ_GOV_START); + data->policy = policy->policy; + data->governor = policy->governor; + if (__cpufreq_governor(data, CPUFREQ_GOV_START)) { + /* new governor failed, so re-start old one */ + data->policy = old_pol; + data->governor = old_gov; + __cpufreq_governor(data, CPUFREQ_GOV_START); } - /* might be a policy change, too */ - cpufreq_governor(policy->cpu, CPUFREQ_GOV_LIMITS); - } else { - cpufreq_governor(policy->cpu, CPUFREQ_GOV_LIMITS); + /* might be a policy change, too, so fall through */ } + __cpufreq_governor(data, CPUFREQ_GOV_LIMITS); } - up(&cpufreq_driver_sem); - error_out: - cpufreq_cpu_put(policy->cpu); + error_out: + up(&data->lock); + cpufreq_cpu_put(data); return ret; } @@ -801,7 +881,7 @@ void cpufreq_notify_transition(struct cpufreq_freqs *freqs, unsigned int state) case CPUFREQ_POSTCHANGE: adjust_jiffies(CPUFREQ_POSTCHANGE, freqs); notifier_call_chain(&cpufreq_transition_notifier_list, CPUFREQ_POSTCHANGE, freqs); - cpufreq_driver->policy[freqs->cpu].cur = freqs->new; + cpufreq_cpu_data[freqs->cpu]->cur = freqs->new; break; } up_read(&cpufreq_notifier_rwsem); @@ -826,25 +906,19 @@ EXPORT_SYMBOL_GPL(cpufreq_notify_transition); */ int cpufreq_register_driver(struct cpufreq_driver *driver_data) { + unsigned long flags; + if (!driver_data || !driver_data->verify || !driver_data->init || ((!driver_data->setpolicy) && (!driver_data->target))) return -EINVAL; - down(&cpufreq_driver_sem); + spin_lock_irqsave(&cpufreq_driver_lock, flags); if (cpufreq_driver) { - up(&cpufreq_driver_sem); + spin_unlock_irqrestore(&cpufreq_driver_lock, flags); return -EBUSY; } cpufreq_driver = driver_data; - up(&cpufreq_driver_sem); - - cpufreq_driver->policy = kmalloc(NR_CPUS * sizeof(struct cpufreq_policy), GFP_KERNEL); - if (!cpufreq_driver->policy) { - cpufreq_driver = NULL; - return -ENOMEM; - } - - memset(cpufreq_driver->policy, 0, NR_CPUS * sizeof(struct cpufreq_policy)); + spin_unlock_irqrestore(&cpufreq_driver_lock, flags); return sysdev_driver_register(&cpu_sysdev_class,&cpufreq_sysdev_driver); } @@ -861,15 +935,16 @@ EXPORT_SYMBOL_GPL(cpufreq_register_driver); */ int cpufreq_unregister_driver(struct cpufreq_driver *driver) { + unsigned long flags; + if (!cpufreq_driver || (driver != cpufreq_driver)) return -EINVAL; sysdev_driver_unregister(&cpu_sysdev_class, &cpufreq_sysdev_driver); - down(&cpufreq_driver_sem); - kfree(cpufreq_driver->policy); + spin_lock_irqsave(&cpufreq_driver_lock, flags); cpufreq_driver = NULL; - up(&cpufreq_driver_sem); + spin_unlock_irqrestore(&cpufreq_driver_lock, flags); return 0; } |
