summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@home.osdl.org>2003-08-01 20:34:58 -0700
committerLinus Torvalds <torvalds@home.osdl.org>2003-08-01 20:34:58 -0700
commit3ee1e20491bebd574738aea137fdccfcee87e89a (patch)
treeb5c30ebec02d07d465829d98a306e2fb164370a2
parent6fd6bef3a5b8a900c771bb1c4580fad24d50b9a9 (diff)
parentb9ecdfd31f689c82becbb7e5502969269a349d95 (diff)
Merge bk://linux-dj.bkbits.net/cpufreq
into home.osdl.org:/home/torvalds/v2.5/linux
-rw-r--r--CREDITS7
-rw-r--r--Documentation/cpu-freq/core.txt8
-rw-r--r--Documentation/cpu-freq/cpu-drivers.txt14
-rw-r--r--Documentation/cpu-freq/governors.txt14
-rw-r--r--Documentation/cpu-freq/user-guide.txt2
-rw-r--r--MAINTAINERS10
-rw-r--r--arch/i386/kernel/cpu/common.c21
-rw-r--r--arch/i386/kernel/cpu/cpufreq/acpi.c2
-rw-r--r--arch/i386/kernel/cpu/cpufreq/longhaul.c206
-rw-r--r--arch/i386/kernel/cpu/cpufreq/longhaul.h49
-rw-r--r--arch/i386/kernel/cpu/cpufreq/longrun.c4
-rw-r--r--arch/i386/kernel/cpu/cpufreq/powernow-k6.c7
-rw-r--r--arch/i386/kernel/cpu/cpufreq/powernow-k7.c32
-rw-r--r--arch/i386/kernel/cpu/cpufreq/powernow-k7.h8
-rw-r--r--arch/i386/kernel/cpu/cpufreq/speedstep-ich.c8
-rw-r--r--drivers/cpufreq/userspace.c12
-rw-r--r--include/asm-i386/msr.h21
-rw-r--r--include/linux/cpufreq.h8
-rw-r--r--kernel/cpufreq.c355
19 files changed, 459 insertions, 329 deletions
diff --git a/CREDITS b/CREDITS
index 617e71aa702d..d2f588851516 100644
--- a/CREDITS
+++ b/CREDITS
@@ -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(&current_policy[cpu], policy->max,
+ __cpufreq_driver_target(&current_policy[cpu], policy->max,
CPUFREQ_RELATION_H);
else if (policy->min > cpu_cur_freq[cpu])
- cpufreq_driver_target(&current_policy[cpu], policy->min,
+ __cpufreq_driver_target(&current_policy[cpu], policy->min,
CPUFREQ_RELATION_L);
memcpy (&current_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;
}