Commit 3ee1e204 authored by Linus Torvalds's avatar Linus Torvalds

Merge bk://linux-dj.bkbits.net/cpufreq

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