Commit 055f27cd authored by Linus Torvalds's avatar Linus Torvalds

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

into home.transmeta.com:/home/torvalds/v2.5/linux
parents f8546efb 4656c707
...@@ -35,6 +35,10 @@ speed limits (like LCD drivers on ARM architecture). Additionally, the ...@@ -35,6 +35,10 @@ speed limits (like LCD drivers on ARM architecture). Additionally, the
kernel "constant" loops_per_jiffy is updated on frequency changes kernel "constant" loops_per_jiffy is updated on frequency changes
here. here.
Reference counting is done by cpufreq_get_cpu and cpufreq_put_cpu,
which make sure that the cpufreq processor driver is correctly
registered with the core, and will not be unloaded until
cpufreq_put_cpu is called.
2. CPUFreq notifiers 2. CPUFreq notifiers
==================== ====================
......
...@@ -63,6 +63,9 @@ And optionally ...@@ -63,6 +63,9 @@ And optionally
cpufreq_driver.exit - A pointer to a per-CPU cleanup function. cpufreq_driver.exit - A pointer to a per-CPU cleanup function.
cpufreq_driver.attr - A pointer to a NULL-terminated list of
"struct freq_attr" which allow to
export values to sysfs.
1.2 Per-CPU Initialization 1.2 Per-CPU Initialization
......
...@@ -114,9 +114,9 @@ the processor shall run at. ...@@ -114,9 +114,9 @@ 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 the mounted it at /sys, the cpufreq interface is located in a subdirectory
cpu-device directory (e.g. /sys/devices/sys/cpu0/ for the first "cpufreq" within the cpu-device directory
CPU). (e.g. /sys/devices/sys/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)
...@@ -125,7 +125,7 @@ cpuinfo_max_freq : this file shows the maximum operating ...@@ -125,7 +125,7 @@ cpuinfo_max_freq : this file shows the maximum operating
scaling_driver : this file shows what cpufreq driver is scaling_driver : this file shows what cpufreq driver is
used to set the frequency on this CPU used to set the frequency on this CPU
available_scaling_governors : this file shows the CPUfreq governors scaling_available_governors : this file shows the CPUfreq governors
available in this kernel. You can see the available in this kernel. You can see the
currently activated governor in currently activated governor in
......
...@@ -619,6 +619,7 @@ static struct cpufreq_driver acpi_cpufreq_driver = { ...@@ -619,6 +619,7 @@ static struct cpufreq_driver acpi_cpufreq_driver = {
.init = acpi_cpufreq_cpu_init, .init = acpi_cpufreq_cpu_init,
.exit = acpi_cpufreq_cpu_exit, .exit = acpi_cpufreq_cpu_exit,
.name = "acpi-cpufreq", .name = "acpi-cpufreq",
.owner = THIS_MODULE,
}; };
......
...@@ -250,6 +250,7 @@ static struct cpufreq_driver elanfreq_driver = { ...@@ -250,6 +250,7 @@ static struct cpufreq_driver elanfreq_driver = {
.target = elanfreq_target, .target = elanfreq_target,
.init = elanfreq_cpu_init, .init = elanfreq_cpu_init,
.name = "elanfreq", .name = "elanfreq",
.owner = THIS_MODULE,
}; };
......
...@@ -451,6 +451,7 @@ static struct cpufreq_driver gx_suspmod_driver = { ...@@ -451,6 +451,7 @@ static struct cpufreq_driver gx_suspmod_driver = {
.target = cpufreq_gx_target, .target = cpufreq_gx_target,
.init = cpufreq_gx_cpu_init, .init = cpufreq_gx_cpu_init,
.name = "gx-suspmod", .name = "gx-suspmod",
.owner = THIS_MODULE,
}; };
static int __init cpufreq_gx_init(void) static int __init cpufreq_gx_init(void)
......
...@@ -649,6 +649,7 @@ static struct cpufreq_driver longhaul_driver = { ...@@ -649,6 +649,7 @@ static struct cpufreq_driver longhaul_driver = {
.target = longhaul_target, .target = longhaul_target,
.init = longhaul_cpu_init, .init = longhaul_cpu_init,
.name = "longhaul", .name = "longhaul",
.owner = THIS_MODULE,
}; };
static int __init longhaul_init (void) static int __init longhaul_init (void)
......
...@@ -253,6 +253,7 @@ static struct cpufreq_driver longrun_driver = { ...@@ -253,6 +253,7 @@ static struct cpufreq_driver longrun_driver = {
.setpolicy = longrun_set_policy, .setpolicy = longrun_set_policy,
.init = longrun_cpu_init, .init = longrun_cpu_init,
.name = "longrun", .name = "longrun",
.owner = THIS_MODULE,
}; };
......
...@@ -214,6 +214,7 @@ static int cpufreq_p4_cpu_init(struct cpufreq_policy *policy) ...@@ -214,6 +214,7 @@ static int cpufreq_p4_cpu_init(struct cpufreq_policy *policy)
else else
p4clockmod_table[i].frequency = (stock_freq * i)/8; p4clockmod_table[i].frequency = (stock_freq * i)/8;
} }
cpufreq_frequency_table_get_attr(p4clockmod_table, policy->cpu);
/* cpuinfo and default policy values */ /* cpuinfo and default policy values */
policy->policy = CPUFREQ_POLICY_PERFORMANCE; policy->policy = CPUFREQ_POLICY_PERFORMANCE;
...@@ -226,9 +227,14 @@ static int cpufreq_p4_cpu_init(struct cpufreq_policy *policy) ...@@ -226,9 +227,14 @@ static int cpufreq_p4_cpu_init(struct cpufreq_policy *policy)
static int cpufreq_p4_cpu_exit(struct cpufreq_policy *policy) static int cpufreq_p4_cpu_exit(struct cpufreq_policy *policy)
{ {
cpufreq_frequency_table_put_attr(policy->cpu);
return cpufreq_p4_setdc(policy->cpu, DC_DISABLE); return cpufreq_p4_setdc(policy->cpu, DC_DISABLE);
} }
static struct freq_attr* p4clockmod_attr[] = {
&cpufreq_freq_attr_scaling_available_freqs,
NULL,
};
static struct cpufreq_driver p4clockmod_driver = { static struct cpufreq_driver p4clockmod_driver = {
.verify = cpufreq_p4_verify, .verify = cpufreq_p4_verify,
...@@ -236,6 +242,8 @@ static struct cpufreq_driver p4clockmod_driver = { ...@@ -236,6 +242,8 @@ static struct cpufreq_driver p4clockmod_driver = {
.init = cpufreq_p4_cpu_init, .init = cpufreq_p4_cpu_init,
.exit = cpufreq_p4_cpu_exit, .exit = cpufreq_p4_cpu_exit,
.name = "p4-clockmod", .name = "p4-clockmod",
.owner = THIS_MODULE,
.attr = p4clockmod_attr,
}; };
......
...@@ -190,6 +190,7 @@ static struct cpufreq_driver powernow_k6_driver = { ...@@ -190,6 +190,7 @@ static struct cpufreq_driver powernow_k6_driver = {
.init = powernow_k6_cpu_init, .init = powernow_k6_cpu_init,
.exit = powernow_k6_cpu_exit, .exit = powernow_k6_cpu_exit,
.name = "powernow-k6", .name = "powernow-k6",
.owner = THIS_MODULE,
}; };
......
...@@ -377,6 +377,7 @@ static struct cpufreq_driver powernow_driver = { ...@@ -377,6 +377,7 @@ static struct cpufreq_driver powernow_driver = {
.target = powernow_target, .target = powernow_target,
.init = powernow_cpu_init, .init = powernow_cpu_init,
.name = "powernow-k7", .name = "powernow-k7",
.owner = THIS_MODULE,
}; };
static int __init powernow_init (void) static int __init powernow_init (void)
......
...@@ -29,7 +29,6 @@ ...@@ -29,7 +29,6 @@
#include <asm/msr.h> #include <asm/msr.h>
/* speedstep_chipset: /* speedstep_chipset:
* It is necessary to know which chipset is used. As accesses to * It is necessary to know which chipset is used. As accesses to
* this device occur at various places in this module, we need a * this device occur at various places in this module, we need a
...@@ -40,7 +39,7 @@ static struct pci_dev *speedstep_chipset_dev; ...@@ -40,7 +39,7 @@ static struct pci_dev *speedstep_chipset_dev;
#define SPEEDSTEP_CHIPSET_ICH2M 0x00000002 #define SPEEDSTEP_CHIPSET_ICH2M 0x00000002
#define SPEEDSTEP_CHIPSET_ICH3M 0x00000003 #define SPEEDSTEP_CHIPSET_ICH3M 0x00000003
#define SPEEDSTEP_CHIPSET_ICH4M 0x00000004
/* speedstep_processor /* speedstep_processor
*/ */
...@@ -106,6 +105,7 @@ static int speedstep_get_state (unsigned int *state) ...@@ -106,6 +105,7 @@ static int speedstep_get_state (unsigned int *state)
switch (speedstep_chipset) { switch (speedstep_chipset) {
case SPEEDSTEP_CHIPSET_ICH2M: case SPEEDSTEP_CHIPSET_ICH2M:
case SPEEDSTEP_CHIPSET_ICH3M: case SPEEDSTEP_CHIPSET_ICH3M:
case SPEEDSTEP_CHIPSET_ICH4M:
/* get PMBASE */ /* get PMBASE */
pci_read_config_dword(speedstep_chipset_dev, 0x40, &pmbase); pci_read_config_dword(speedstep_chipset_dev, 0x40, &pmbase);
if (!(pmbase & 0x01)) if (!(pmbase & 0x01))
...@@ -166,6 +166,7 @@ static void speedstep_set_state (unsigned int state, int notify) ...@@ -166,6 +166,7 @@ static void speedstep_set_state (unsigned int state, int notify)
switch (speedstep_chipset) { switch (speedstep_chipset) {
case SPEEDSTEP_CHIPSET_ICH2M: case SPEEDSTEP_CHIPSET_ICH2M:
case SPEEDSTEP_CHIPSET_ICH3M: case SPEEDSTEP_CHIPSET_ICH3M:
case SPEEDSTEP_CHIPSET_ICH4M:
/* get PMBASE */ /* get PMBASE */
pci_read_config_dword(speedstep_chipset_dev, 0x40, &pmbase); pci_read_config_dword(speedstep_chipset_dev, 0x40, &pmbase);
if (!(pmbase & 0x01)) if (!(pmbase & 0x01))
...@@ -245,6 +246,7 @@ static int speedstep_activate (void) ...@@ -245,6 +246,7 @@ static int speedstep_activate (void)
switch (speedstep_chipset) { switch (speedstep_chipset) {
case SPEEDSTEP_CHIPSET_ICH2M: case SPEEDSTEP_CHIPSET_ICH2M:
case SPEEDSTEP_CHIPSET_ICH3M: case SPEEDSTEP_CHIPSET_ICH3M:
case SPEEDSTEP_CHIPSET_ICH4M:
{ {
u16 value = 0; u16 value = 0;
...@@ -276,6 +278,14 @@ static int speedstep_activate (void) ...@@ -276,6 +278,14 @@ static int speedstep_activate (void)
*/ */
static unsigned int speedstep_detect_chipset (void) static unsigned int speedstep_detect_chipset (void)
{ {
speedstep_chipset_dev = pci_find_subsys(PCI_VENDOR_ID_INTEL,
PCI_DEVICE_ID_INTEL_82801DB_12,
PCI_ANY_ID,
PCI_ANY_ID,
NULL);
if (speedstep_chipset_dev)
return SPEEDSTEP_CHIPSET_ICH4M;
speedstep_chipset_dev = pci_find_subsys(PCI_VENDOR_ID_INTEL, speedstep_chipset_dev = pci_find_subsys(PCI_VENDOR_ID_INTEL,
PCI_DEVICE_ID_INTEL_82801CA_12, PCI_DEVICE_ID_INTEL_82801CA_12,
PCI_ANY_ID, PCI_ANY_ID,
...@@ -658,6 +668,7 @@ static struct cpufreq_driver speedstep_driver = { ...@@ -658,6 +668,7 @@ static struct cpufreq_driver speedstep_driver = {
.verify = speedstep_verify, .verify = speedstep_verify,
.target = speedstep_target, .target = speedstep_target,
.init = speedstep_cpu_init, .init = speedstep_cpu_init,
.owner = THIS_MODULE,
}; };
......
...@@ -276,6 +276,7 @@ static int __init us3freq_init(void) ...@@ -276,6 +276,7 @@ static int __init us3freq_init(void)
driver->target = us3freq_target; driver->target = us3freq_target;
driver->init = us3freq_cpu_init; driver->init = us3freq_cpu_init;
driver->exit = us3freq_cpu_exit; driver->exit = us3freq_cpu_exit;
driver->owner = THIS_MODULE,
strcpy(driver->name, "UltraSPARC-III"); strcpy(driver->name, "UltraSPARC-III");
cpufreq_us3_driver = driver; cpufreq_us3_driver = driver;
......
...@@ -77,56 +77,6 @@ int cpufreq_frequency_table_verify(struct cpufreq_policy *policy, ...@@ -77,56 +77,6 @@ int cpufreq_frequency_table_verify(struct cpufreq_policy *policy,
EXPORT_SYMBOL_GPL(cpufreq_frequency_table_verify); EXPORT_SYMBOL_GPL(cpufreq_frequency_table_verify);
int cpufreq_frequency_table_setpolicy(struct cpufreq_policy *policy,
struct cpufreq_frequency_table *table,
unsigned int *index)
{
struct cpufreq_frequency_table optimal = { .index = ~0, };
unsigned int i;
switch (policy->policy) {
case CPUFREQ_POLICY_PERFORMANCE:
optimal.frequency = 0;
break;
case CPUFREQ_POLICY_POWERSAVE:
optimal.frequency = ~0;
break;
}
if (!cpu_online(policy->cpu))
return -EINVAL;
for (i=0; (table[i].frequency != CPUFREQ_TABLE_END); i++) {
unsigned int freq = table[i].frequency;
if (freq == CPUFREQ_ENTRY_INVALID)
continue;
if ((freq < policy->min) || (freq > policy->max))
continue;
switch(policy->policy) {
case CPUFREQ_POLICY_PERFORMANCE:
if (optimal.frequency <= freq) {
optimal.frequency = freq;
optimal.index = i;
}
break;
case CPUFREQ_POLICY_POWERSAVE:
if (optimal.frequency >= freq) {
optimal.frequency = freq;
optimal.index = i;
}
break;
}
}
if (optimal.index > i)
return -EINVAL;
*index = optimal.index;
return 0;
}
EXPORT_SYMBOL_GPL(cpufreq_frequency_table_setpolicy);
int cpufreq_frequency_table_target(struct cpufreq_policy *policy, int cpufreq_frequency_table_target(struct cpufreq_policy *policy,
struct cpufreq_frequency_table *table, struct cpufreq_frequency_table *table,
unsigned int target_freq, unsigned int target_freq,
...@@ -197,6 +147,56 @@ int cpufreq_frequency_table_target(struct cpufreq_policy *policy, ...@@ -197,6 +147,56 @@ int cpufreq_frequency_table_target(struct cpufreq_policy *policy,
} }
EXPORT_SYMBOL_GPL(cpufreq_frequency_table_target); EXPORT_SYMBOL_GPL(cpufreq_frequency_table_target);
static struct cpufreq_frequency_table *show_table[NR_CPUS];
/**
* show_scaling_governor - show the current policy for the specified CPU
*/
static ssize_t show_available_freqs (struct cpufreq_policy *policy, char *buf)
{
unsigned int i = 0;
unsigned int cpu = policy->cpu;
ssize_t count = 0;
struct cpufreq_frequency_table *table;
if (!show_table[cpu])
return -ENODEV;
table = show_table[cpu];
for (i=0; (table[i].frequency != CPUFREQ_TABLE_END); i++) {
if (table[i].frequency == CPUFREQ_ENTRY_INVALID)
continue;
count += sprintf(&buf[count], "%d ", table[i].frequency);
}
count += sprintf(&buf[count], "\n");
return count;
}
struct freq_attr cpufreq_freq_attr_scaling_available_freqs = {
.attr = { .name = "scaling_available_frequencies", .mode = 0444 },
.show = show_available_freqs,
};
EXPORT_SYMBOL_GPL(cpufreq_freq_attr_scaling_available_freqs);
/*
* if you use these, you must assure that the frequency table is valid
* all the time between get_attr and put_attr!
*/
void cpufreq_frequency_table_get_attr(struct cpufreq_frequency_table *table,
unsigned int cpu)
{
show_table[cpu] = table;
}
EXPORT_SYMBOL_GPL(cpufreq_frequency_table_get_attr);
void cpufreq_frequency_table_put_attr(unsigned int cpu)
{
show_table[cpu] = NULL;
}
EXPORT_SYMBOL_GPL(cpufreq_frequency_table_put_attr);
MODULE_AUTHOR ("Dominik Brodowski <linux@brodo.de>"); MODULE_AUTHOR ("Dominik Brodowski <linux@brodo.de>");
MODULE_DESCRIPTION ("CPUfreq frequency table helpers"); MODULE_DESCRIPTION ("CPUfreq frequency table helpers");
......
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
#include <linux/sysctl.h> #include <linux/sysctl.h>
#include <linux/types.h> #include <linux/types.h>
#include <linux/fs.h> #include <linux/fs.h>
#include <linux/sysfs.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
...@@ -112,7 +113,7 @@ int cpufreq_set(unsigned int freq, unsigned int cpu) ...@@ -112,7 +113,7 @@ int cpufreq_set(unsigned int freq, unsigned int cpu)
if (freq > cpu_max_freq[cpu]) if (freq > cpu_max_freq[cpu])
freq = cpu_max_freq[cpu]; freq = cpu_max_freq[cpu];
ret = cpufreq_driver_target_l(&current_policy[cpu], freq, ret = cpufreq_driver_target(&current_policy[cpu], freq,
CPUFREQ_RELATION_L); CPUFREQ_RELATION_L);
err: err:
...@@ -465,23 +466,14 @@ static inline void cpufreq_sysctl_exit(void) ...@@ -465,23 +466,14 @@ static inline void cpufreq_sysctl_exit(void)
/************************** sysfs interface ************************/ /************************** sysfs interface ************************/
static inline int to_cpu_nr (struct device *dev) static ssize_t show_speed (struct cpufreq_policy *policy, char *buf)
{ {
struct sys_device * cpu_sys_dev = container_of(dev, struct sys_device, dev); return sprintf (buf, "%u\n", cpu_cur_freq[policy->cpu]);
return (cpu_sys_dev->id);
}
static ssize_t show_speed (struct device *dev, char *buf)
{
unsigned int cpu = to_cpu_nr(dev);
return sprintf (buf, "%u\n", cpu_cur_freq[cpu]);
} }
static ssize_t static ssize_t
store_speed (struct device *dev, const char *buf, size_t count) store_speed (struct cpufreq_policy *policy, const char *buf, size_t count)
{ {
unsigned int cpu = to_cpu_nr(dev);
unsigned int freq = 0; unsigned int freq = 0;
unsigned int ret; unsigned int ret;
...@@ -489,13 +481,16 @@ store_speed (struct device *dev, const char *buf, size_t count) ...@@ -489,13 +481,16 @@ store_speed (struct device *dev, const char *buf, size_t count)
if (ret != 1) if (ret != 1)
return -EINVAL; return -EINVAL;
cpufreq_set(freq, cpu); cpufreq_set(freq, policy->cpu);
return count; return count;
} }
static DEVICE_ATTR(scaling_setspeed, (S_IRUGO | S_IWUSR), show_speed, store_speed); static struct freq_attr freq_attr_scaling_setspeed = {
.attr = { .name = "scaling_setspeed", .mode = 0644 },
.show = show_speed,
.store = store_speed,
};
static int cpufreq_governor_userspace(struct cpufreq_policy *policy, static int cpufreq_governor_userspace(struct cpufreq_policy *policy,
unsigned int event) unsigned int event)
...@@ -511,7 +506,7 @@ static int cpufreq_governor_userspace(struct cpufreq_policy *policy, ...@@ -511,7 +506,7 @@ 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;
cpu_cur_freq[cpu] = policy->cur; cpu_cur_freq[cpu] = policy->cur;
device_create_file (policy->dev, &dev_attr_scaling_setspeed); sysfs_create_file (&policy->kobj, &freq_attr_scaling_setspeed.attr);
memcpy (&current_policy[cpu], policy, sizeof(struct cpufreq_policy)); memcpy (&current_policy[cpu], policy, sizeof(struct cpufreq_policy));
up(&userspace_sem); up(&userspace_sem);
break; break;
...@@ -520,7 +515,7 @@ static int cpufreq_governor_userspace(struct cpufreq_policy *policy, ...@@ -520,7 +515,7 @@ static int cpufreq_governor_userspace(struct cpufreq_policy *policy,
cpu_is_managed[cpu] = 0; cpu_is_managed[cpu] = 0;
cpu_min_freq[cpu] = 0; cpu_min_freq[cpu] = 0;
cpu_max_freq[cpu] = 0; cpu_max_freq[cpu] = 0;
device_remove_file (policy->dev, &dev_attr_scaling_setspeed); sysfs_remove_file (&policy->kobj, &freq_attr_scaling_setspeed.attr);
up(&userspace_sem); up(&userspace_sem);
module_put(THIS_MODULE); module_put(THIS_MODULE);
break; break;
......
...@@ -18,7 +18,8 @@ ...@@ -18,7 +18,8 @@
#include <linux/notifier.h> #include <linux/notifier.h>
#include <linux/threads.h> #include <linux/threads.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/kobject.h>
#include <linux/sysfs.h>
#define CPUFREQ_NAME_LEN 16 #define CPUFREQ_NAME_LEN 16
...@@ -69,6 +70,8 @@ struct cpufreq_policy { ...@@ -69,6 +70,8 @@ struct cpufreq_policy {
struct cpufreq_cpuinfo cpuinfo; /* see above */ struct cpufreq_cpuinfo cpuinfo; /* see above */
struct device * dev; struct device * dev;
struct kobject kobj; struct kobject kobj;
struct semaphore lock; /* CPU ->setpolicy or ->target may
only be called once a time */
}; };
#define CPUFREQ_ADJUST (0) #define CPUFREQ_ADJUST (0)
...@@ -131,18 +134,13 @@ struct cpufreq_governor { ...@@ -131,18 +134,13 @@ struct cpufreq_governor {
}; };
/* pass a target to the cpufreq driver /* pass a target to the cpufreq driver
* _l : (cpufreq_driver_sem is not held)
*/ */
inline int cpufreq_driver_target(struct cpufreq_policy *policy, inline 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,
unsigned int target_freq,
unsigned int relation);
/* pass an event to the cpufreq governor */ /* pass an event to the cpufreq governor */
int cpufreq_governor_l(unsigned int cpu, unsigned int event); int cpufreq_governor(unsigned int cpu, unsigned int event);
int cpufreq_register_governor(struct cpufreq_governor *governor); int cpufreq_register_governor(struct cpufreq_governor *governor);
void cpufreq_unregister_governor(struct cpufreq_governor *governor); void cpufreq_unregister_governor(struct cpufreq_governor *governor);
...@@ -154,6 +152,8 @@ void cpufreq_unregister_governor(struct cpufreq_governor *governor); ...@@ -154,6 +152,8 @@ void cpufreq_unregister_governor(struct cpufreq_governor *governor);
#define CPUFREQ_RELATION_L 0 /* lowest frequency at or above target */ #define CPUFREQ_RELATION_L 0 /* lowest frequency at or above target */
#define CPUFREQ_RELATION_H 1 /* highest frequency below or at target */ #define CPUFREQ_RELATION_H 1 /* highest frequency below or at target */
struct freq_attr;
struct cpufreq_driver { struct cpufreq_driver {
/* needed by all drivers */ /* needed by all drivers */
int (*verify) (struct cpufreq_policy *policy); int (*verify) (struct cpufreq_policy *policy);
...@@ -164,16 +164,15 @@ struct cpufreq_driver { ...@@ -164,16 +164,15 @@ struct cpufreq_driver {
int (*target) (struct cpufreq_policy *policy, int (*target) (struct cpufreq_policy *policy,
unsigned int target_freq, unsigned int target_freq,
unsigned int relation); unsigned int relation);
struct module *owner;
/* optional, for the moment */ /* optional, for the moment */
int (*init) (struct cpufreq_policy *policy); int (*init) (struct cpufreq_policy *policy);
int (*exit) (struct cpufreq_policy *policy); int (*exit) (struct cpufreq_policy *policy);
struct freq_attr **attr;
}; };
int cpufreq_register_driver(struct cpufreq_driver *driver_data); int cpufreq_register_driver(struct cpufreq_driver *driver_data);
int cpufreq_unregister_driver(struct cpufreq_driver *driver_data); int cpufreq_unregister_driver(struct cpufreq_driver *driver_data);
/* deprecated */
#define cpufreq_register(x) cpufreq_register_driver(x)
#define cpufreq_unregister() cpufreq_unregister_driver(NULL)
void cpufreq_notify_transition(struct cpufreq_freqs *freqs, unsigned int state); void cpufreq_notify_transition(struct cpufreq_freqs *freqs, unsigned int state);
...@@ -194,6 +193,13 @@ static inline void cpufreq_verify_within_limits(struct cpufreq_policy *policy, u ...@@ -194,6 +193,13 @@ static inline void cpufreq_verify_within_limits(struct cpufreq_policy *policy, u
return; return;
} }
struct freq_attr {
struct attribute attr;
ssize_t (*show)(struct cpufreq_policy *, char *);
ssize_t (*store)(struct cpufreq_policy *, const char *, size_t count);
};
/********************************************************************* /*********************************************************************
* CPUFREQ 2.6. INTERFACE * * CPUFREQ 2.6. INTERFACE *
*********************************************************************/ *********************************************************************/
...@@ -289,16 +295,21 @@ int cpufreq_frequency_table_cpuinfo(struct cpufreq_policy *policy, ...@@ -289,16 +295,21 @@ int cpufreq_frequency_table_cpuinfo(struct cpufreq_policy *policy,
int cpufreq_frequency_table_verify(struct cpufreq_policy *policy, int cpufreq_frequency_table_verify(struct cpufreq_policy *policy,
struct cpufreq_frequency_table *table); struct cpufreq_frequency_table *table);
int cpufreq_frequency_table_setpolicy(struct cpufreq_policy *policy,
struct cpufreq_frequency_table *table,
unsigned int *index);
int cpufreq_frequency_table_target(struct cpufreq_policy *policy, int cpufreq_frequency_table_target(struct cpufreq_policy *policy,
struct cpufreq_frequency_table *table, struct cpufreq_frequency_table *table,
unsigned int target_freq, unsigned int target_freq,
unsigned int relation, unsigned int relation,
unsigned int *index); unsigned int *index);
/* the following are really really optional */
extern struct freq_attr cpufreq_freq_attr_scaling_available_freqs;
void cpufreq_frequency_table_get_attr(struct cpufreq_frequency_table *table,
unsigned int cpu);
void cpufreq_frequency_table_put_attr(unsigned int cpu);
#endif /* CONFIG_CPU_FREQ_TABLE */ #endif /* CONFIG_CPU_FREQ_TABLE */
#endif /* _LINUX_CPUFREQ_H */ #endif /* _LINUX_CPUFREQ_H */
...@@ -1854,6 +1854,7 @@ ...@@ -1854,6 +1854,7 @@
#define PCI_DEVICE_ID_INTEL_82801DB_7 0x24c7 #define PCI_DEVICE_ID_INTEL_82801DB_7 0x24c7
#define PCI_DEVICE_ID_INTEL_82801DB_9 0x24cb #define PCI_DEVICE_ID_INTEL_82801DB_9 0x24cb
#define PCI_DEVICE_ID_INTEL_82801DB_11 PCI_DEVICE_ID_INTEL_82801DB_9 #define PCI_DEVICE_ID_INTEL_82801DB_11 PCI_DEVICE_ID_INTEL_82801DB_9
#define PCI_DEVICE_ID_INTEL_82801DB_12 0x24cc
#define PCI_DEVICE_ID_INTEL_82801DB_13 0x24cd #define PCI_DEVICE_ID_INTEL_82801DB_13 0x24cd
#define PCI_DEVICE_ID_INTEL_82820_HB 0x2500 #define PCI_DEVICE_ID_INTEL_82820_HB 0x2500
#define PCI_DEVICE_ID_INTEL_82820_UP_HB 0x2501 #define PCI_DEVICE_ID_INTEL_82820_UP_HB 0x2501
......
...@@ -43,12 +43,40 @@ static DECLARE_MUTEX (cpufreq_driver_sem); ...@@ -43,12 +43,40 @@ static DECLARE_MUTEX (cpufreq_driver_sem);
*/ */
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;
static DECLARE_MUTEX (cpufreq_notifier_sem); static DECLARE_RWSEM (cpufreq_notifier_rwsem);
LIST_HEAD(cpufreq_governor_list); LIST_HEAD(cpufreq_governor_list);
static DECLARE_MUTEX (cpufreq_governor_sem);
static int cpufreq_governor(unsigned int cpu, unsigned int event); static struct device_interface cpufreq_interface;
static int cpufreq_cpu_get(unsigned int cpu) {
if (cpu >= NR_CPUS)
return 0;
if (!kset_get(&cpufreq_interface.kset))
return 0;
if (!try_module_get(cpufreq_driver->owner)) {
kset_put(&cpufreq_interface.kset);
return 0;
}
if (!kobject_get(&cpufreq_driver->policy[cpu].kobj)) {
module_put(cpufreq_driver->owner);
kset_put(&cpufreq_interface.kset);
return 0;
}
return 1;
}
static void cpufreq_cpu_put(unsigned int cpu) {
kobject_put(&cpufreq_driver->policy[cpu].kobj);
module_put(cpufreq_driver->owner);
kset_put(&cpufreq_interface.kset);
}
/********************************************************************* /*********************************************************************
* SYSFS INTERFACE * * SYSFS INTERFACE *
...@@ -67,19 +95,19 @@ int cpufreq_parse_governor (char *str_governor, unsigned int *policy, struct cpu ...@@ -67,19 +95,19 @@ int cpufreq_parse_governor (char *str_governor, unsigned int *policy, struct cpu
return 0; return 0;
} else { } else {
struct cpufreq_governor *t; struct cpufreq_governor *t;
down(&cpufreq_driver_sem); down(&cpufreq_governor_sem);
if (!cpufreq_driver || !cpufreq_driver->target) if (!cpufreq_driver || !cpufreq_driver->target)
goto out; goto out;
list_for_each_entry(t, &cpufreq_governor_list, governor_list) { list_for_each_entry(t, &cpufreq_governor_list, governor_list) {
if (!strnicmp(str_governor,t->name,CPUFREQ_NAME_LEN)) { if (!strnicmp(str_governor,t->name,CPUFREQ_NAME_LEN)) {
*governor = t; *governor = t;
*policy = CPUFREQ_POLICY_GOVERNOR; *policy = CPUFREQ_POLICY_GOVERNOR;
up(&cpufreq_driver_sem); up(&cpufreq_governor_sem);
return 0; return 0;
} }
} }
out: out:
up(&cpufreq_driver_sem); up(&cpufreq_governor_sem);
} }
return -EINVAL; return -EINVAL;
} }
...@@ -120,14 +148,7 @@ static inline int to_cpu_nr (struct device *dev) ...@@ -120,14 +148,7 @@ static inline int to_cpu_nr (struct device *dev)
static ssize_t show_##file_name \ static ssize_t show_##file_name \
(struct cpufreq_policy * policy, char *buf) \ (struct cpufreq_policy * policy, char *buf) \
{ \ { \
unsigned int value = 0; \ return sprintf (buf, "%u\n", policy->object); \
\
down(&cpufreq_driver_sem); \
if (cpufreq_driver) \
value = policy->object; \
up(&cpufreq_driver_sem); \
\
return sprintf (buf, "%u\n", value); \
} }
show_one(cpuinfo_min_freq, cpuinfo.min_freq); show_one(cpuinfo_min_freq, cpuinfo.min_freq);
...@@ -143,12 +164,17 @@ static ssize_t store_##file_name \ ...@@ -143,12 +164,17 @@ static ssize_t store_##file_name \
(struct cpufreq_policy * policy, const char *buf, size_t count) \ (struct cpufreq_policy * policy, const char *buf, size_t count) \
{ \ { \
unsigned int ret = -EINVAL; \ unsigned int ret = -EINVAL; \
struct cpufreq_policy new_policy; \
\ \
ret = sscanf (buf, "%u", &policy->object); \ ret = cpufreq_get_policy(&new_policy, policy->cpu); \
if (ret) \
return -EINVAL; \
\
ret = sscanf (buf, "%u", &new_policy.object); \
if (ret != 1) \ if (ret != 1) \
return -EINVAL; \ return -EINVAL; \
\ \
ret = cpufreq_set_policy(policy); \ ret = cpufreq_set_policy(&new_policy); \
\ \
return ret ? ret : count; \ return ret ? ret : count; \
} }
...@@ -161,26 +187,16 @@ store_one(scaling_max_freq,max); ...@@ -161,26 +187,16 @@ store_one(scaling_max_freq,max);
*/ */
static ssize_t show_scaling_governor (struct cpufreq_policy * policy, char *buf) static ssize_t show_scaling_governor (struct cpufreq_policy * policy, char *buf)
{ {
unsigned int value = 0; switch (policy->policy) {
char value2[CPUFREQ_NAME_LEN];
down(&cpufreq_driver_sem);
if (cpufreq_driver)
value = policy->policy;
if (value == CPUFREQ_POLICY_GOVERNOR)
strncpy(value2, policy->governor->name, CPUFREQ_NAME_LEN);
up(&cpufreq_driver_sem);
switch (value) {
case CPUFREQ_POLICY_POWERSAVE: case CPUFREQ_POLICY_POWERSAVE:
return sprintf(buf, "powersave\n"); return sprintf(buf, "powersave\n");
case CPUFREQ_POLICY_PERFORMANCE: case CPUFREQ_POLICY_PERFORMANCE:
return sprintf(buf, "performance\n"); return sprintf(buf, "performance\n");
case CPUFREQ_POLICY_GOVERNOR: case CPUFREQ_POLICY_GOVERNOR:
return sprintf(buf, "%s\n", value2); return snprintf(buf, CPUFREQ_NAME_LEN, "%s\n", policy->governor->name);
default:
return -EINVAL;
} }
return -EINVAL;
} }
...@@ -192,25 +208,55 @@ static ssize_t store_scaling_governor (struct cpufreq_policy * policy, ...@@ -192,25 +208,55 @@ static ssize_t store_scaling_governor (struct cpufreq_policy * policy,
{ {
unsigned int ret = -EINVAL; unsigned int ret = -EINVAL;
char str_governor[16]; char str_governor[16];
struct cpufreq_policy new_policy;
ret = cpufreq_get_policy(&new_policy, policy->cpu);
if (ret)
return ret;
ret = sscanf (buf, "%15s", str_governor); ret = sscanf (buf, "%15s", str_governor);
if (ret != 1) if (ret != 1)
return -EINVAL; return -EINVAL;
if (cpufreq_parse_governor(str_governor, &policy->policy, &policy->governor)) if (cpufreq_parse_governor(str_governor, &new_policy.policy, &new_policy.governor))
return -EINVAL; return -EINVAL;
ret = cpufreq_set_policy(policy); ret = cpufreq_set_policy(&new_policy);
return ret ? ret : count; return ret ? ret : count;
} }
/**
* show_scaling_driver - show the cpufreq driver currently loaded
*/
static ssize_t show_scaling_driver (struct cpufreq_policy * policy, char *buf)
{
return snprintf(buf, CPUFREQ_NAME_LEN, "%s\n", cpufreq_driver->name);
}
/**
* show_scaling_available_governors - show the available CPUfreq governors
*/
static ssize_t show_scaling_available_governors(struct cpufreq_policy * policy, char *buf)
{
ssize_t i = 0;
struct cpufreq_governor *t;
i += sprintf(buf, "performance powersave");
if (!cpufreq_driver->target)
goto out;
list_for_each_entry(t, &cpufreq_governor_list, governor_list) {
if (i >= (ssize_t) ((PAGE_SIZE / sizeof(char)) - (CPUFREQ_NAME_LEN + 2)))
goto out;
i += snprintf(&buf[i], CPUFREQ_NAME_LEN, " %s", t->name);
}
out:
i += sprintf(&buf[i], "\n");
return i;
}
struct freq_attr {
struct attribute attr;
ssize_t (*show)(struct cpufreq_policy *, char *);
ssize_t (*store)(struct cpufreq_policy *, const char *, size_t count);
};
#define define_one_ro(_name) \ #define define_one_ro(_name) \
struct freq_attr _name = { \ struct freq_attr _name = { \
...@@ -227,6 +273,8 @@ struct freq_attr _name = { \ ...@@ -227,6 +273,8 @@ struct freq_attr _name = { \
define_one_ro(cpuinfo_min_freq); define_one_ro(cpuinfo_min_freq);
define_one_ro(cpuinfo_max_freq); define_one_ro(cpuinfo_max_freq);
define_one_ro(scaling_available_governors);
define_one_ro(scaling_driver);
define_one_rw(scaling_min_freq); define_one_rw(scaling_min_freq);
define_one_rw(scaling_max_freq); define_one_rw(scaling_max_freq);
define_one_rw(scaling_governor); define_one_rw(scaling_governor);
...@@ -237,10 +285,11 @@ static struct attribute * default_attrs[] = { ...@@ -237,10 +285,11 @@ static struct attribute * default_attrs[] = {
&scaling_min_freq.attr, &scaling_min_freq.attr,
&scaling_max_freq.attr, &scaling_max_freq.attr,
&scaling_governor.attr, &scaling_governor.attr,
&scaling_driver.attr,
&scaling_available_governors.attr,
NULL NULL
}; };
#define to_policy(k) container_of(k,struct cpufreq_policy,kobj) #define to_policy(k) container_of(k,struct cpufreq_policy,kobj)
#define to_attr(a) container_of(a,struct freq_attr,attr) #define to_attr(a) container_of(a,struct freq_attr,attr)
...@@ -248,7 +297,12 @@ static ssize_t show(struct kobject * kobj, struct attribute * attr ,char * buf) ...@@ -248,7 +297,12 @@ 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);
return fattr->show ? fattr->show(policy,buf) : 0; ssize_t ret;
if (!cpufreq_cpu_get(policy->cpu))
return -EINVAL;
ret = fattr->show ? fattr->show(policy,buf) : 0;
cpufreq_cpu_put(policy->cpu);
return ret;
} }
static ssize_t store(struct kobject * kobj, struct attribute * attr, static ssize_t store(struct kobject * kobj, struct attribute * attr,
...@@ -256,7 +310,12 @@ static ssize_t store(struct kobject * kobj, struct attribute * attr, ...@@ -256,7 +310,12 @@ 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);
return fattr->store ? fattr->store(policy,buf,count) : 0; ssize_t ret;
if (!cpufreq_cpu_get(policy->cpu))
return -EINVAL;
ret = fattr->store ? fattr->store(policy,buf,count) : 0;
cpufreq_cpu_put(policy->cpu);
return ret;
} }
static struct sysfs_ops sysfs_ops = { static struct sysfs_ops sysfs_ops = {
...@@ -270,56 +329,6 @@ static struct kobj_type ktype_cpufreq = { ...@@ -270,56 +329,6 @@ static struct kobj_type ktype_cpufreq = {
}; };
/**
* show_scaling_governor - show the current policy for the specified CPU
*/
static ssize_t show_scaling_driver (struct device *dev, char *buf)
{
char value[CPUFREQ_NAME_LEN];
if (!dev)
return 0;
down(&cpufreq_driver_sem);
if (cpufreq_driver)
strncpy(value, cpufreq_driver->name, CPUFREQ_NAME_LEN);
up(&cpufreq_driver_sem);
return sprintf(buf, "%s\n", value);
}
/**
* show_available_govs - show the available CPUfreq governors
*/
static ssize_t show_available_govs(struct device *dev, char *buf)
{
ssize_t i = 0;
struct cpufreq_governor *t;
if (!dev)
return 0;
i += sprintf(buf, "performance powersave");
down(&cpufreq_driver_sem);
if (!cpufreq_driver || !cpufreq_driver->target)
goto out;
list_for_each_entry(t, &cpufreq_governor_list, governor_list) {
if (i >= (ssize_t) ((PAGE_SIZE / sizeof(char)) - (CPUFREQ_NAME_LEN + 2)))
goto out;
i += snprintf(&buf[i], CPUFREQ_NAME_LEN, " %s", t->name);
}
out:
up(&cpufreq_driver_sem);
i += sprintf(&buf[i], "\n");
return i;
}
static DEVICE_ATTR(scaling_driver, S_IRUGO, show_scaling_driver, NULL);
static DEVICE_ATTR(available_scaling_governors, S_IRUGO, show_available_govs, NULL);
/** /**
* cpufreq_add_dev - add a CPU device * cpufreq_add_dev - add a CPU device
* *
...@@ -329,57 +338,62 @@ static int cpufreq_add_dev (struct device * dev) ...@@ -329,57 +338,62 @@ static int cpufreq_add_dev (struct device * dev)
{ {
unsigned int cpu = to_cpu_nr(dev); unsigned int cpu = to_cpu_nr(dev);
int ret = 0; int ret = 0;
struct cpufreq_policy policy; struct cpufreq_policy new_policy;
struct cpufreq_policy *policy;
struct freq_attr **drv_attr;
down(&cpufreq_driver_sem); if (!kset_get(&cpufreq_interface.kset))
if (!cpufreq_driver) { return -EINVAL;
up(&cpufreq_driver_sem);
if (!try_module_get(cpufreq_driver->owner)) {
kset_put(&cpufreq_interface.kset);
return -EINVAL; return -EINVAL;
} }
/* 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
*/ */
cpufreq_driver->policy[cpu].cpu = cpu; policy = &cpufreq_driver->policy[cpu];
policy->cpu = cpu;
if (cpufreq_driver->init) { if (cpufreq_driver->init) {
ret = cpufreq_driver->init(&cpufreq_driver->policy[cpu]); ret = cpufreq_driver->init(policy);
if (ret) { if (ret)
up(&cpufreq_driver_sem); goto out;
return -ENODEV;
}
} }
/* set default policy on this CPU */ /* set default policy on this CPU */
memcpy(&policy, down(&cpufreq_driver_sem);
&cpufreq_driver->policy[cpu], memcpy(&new_policy,
policy,
sizeof(struct cpufreq_policy)); sizeof(struct cpufreq_policy));
/* 2.4-API init for this CPU */
#ifdef CONFIG_CPU_FREQ_24_API
cpu_min_freq[cpu] = cpufreq_driver->policy[cpu].cpuinfo.min_freq;
cpu_max_freq[cpu] = cpufreq_driver->policy[cpu].cpuinfo.max_freq;
cpu_cur_freq[cpu] = cpufreq_driver->cpu_cur_freq[cpu];
#endif
if (cpufreq_driver->target)
cpufreq_governor(cpu, CPUFREQ_GOV_START);
up(&cpufreq_driver_sem); up(&cpufreq_driver_sem);
ret = cpufreq_set_policy(&policy);
if (ret)
return -EINVAL;
down(&cpufreq_driver_sem);
init_MUTEX(&policy->lock);
/* prepare interface data */ /* prepare interface data */
policy.kobj.parent = &dev->kobj; policy->kobj.parent = &dev->kobj;
policy.kobj.ktype = &ktype_cpufreq; policy->kobj.ktype = &ktype_cpufreq;
policy.dev = dev; policy->dev = dev;
strncpy(policy.kobj.name, strncpy(policy->kobj.name,
cpufreq_interface.name, KOBJ_NAME_LEN); cpufreq_interface.name, KOBJ_NAME_LEN);
ret = kobject_register(&policy.kobj); ret = kobject_register(&policy->kobj);
if (ret)
goto out;
drv_attr = cpufreq_driver->attr;
while ((drv_attr) && (*drv_attr)) {
sysfs_create_file(&policy->kobj, &((*drv_attr)->attr));
drv_attr++;
}
up(&cpufreq_driver_sem); /* set default policy */
ret = cpufreq_set_policy(&new_policy);
if (ret)
kobject_unregister(&policy->kobj);
out:
module_put(cpufreq_driver->owner);
kset_put(&cpufreq_interface.kset);
return ret; return ret;
} }
...@@ -387,21 +401,39 @@ static int cpufreq_add_dev (struct device * dev) ...@@ -387,21 +401,39 @@ static int cpufreq_add_dev (struct device * dev)
/** /**
* cpufreq_remove_dev - remove a CPU device * cpufreq_remove_dev - remove a CPU device
* *
* Removes the cpufreq interface for a CPU device. Is called with * Removes the cpufreq interface for a CPU device.
* cpufreq_driver_sem locked.
*/ */
static int cpufreq_remove_dev (struct device * dev) static int cpufreq_remove_dev (struct device * dev)
{ {
unsigned int cpu = to_cpu_nr(dev); unsigned int cpu = to_cpu_nr(dev);
if (cpufreq_driver->target) if (!kset_get(&cpufreq_interface.kset))
cpufreq_governor(cpu, CPUFREQ_GOV_STOP); return -EINVAL;
if (!kobject_get(&cpufreq_driver->policy[cpu].kobj)) {
kset_put(&cpufreq_interface.kset);
return -EINVAL;
}
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);
}
/* 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) if (cpufreq_driver->exit)
cpufreq_driver->exit(&cpufreq_driver->policy[cpu]); cpufreq_driver->exit(&cpufreq_driver->policy[cpu]);
kobject_unregister(&cpufreq_driver->policy[cpu].kobj); kobject_unregister(&cpufreq_driver->policy[cpu].kobj);
up(&cpufreq_driver_sem);
kobject_put(&cpufreq_driver->policy[cpu].kobj);
kset_put(&cpufreq_interface.kset);
return 0; return 0;
} }
...@@ -427,7 +459,7 @@ int cpufreq_register_notifier(struct notifier_block *nb, unsigned int list) ...@@ -427,7 +459,7 @@ int cpufreq_register_notifier(struct notifier_block *nb, unsigned int list)
{ {
int ret; int ret;
down(&cpufreq_notifier_sem); down_write(&cpufreq_notifier_rwsem);
switch (list) { switch (list) {
case CPUFREQ_TRANSITION_NOTIFIER: case CPUFREQ_TRANSITION_NOTIFIER:
ret = notifier_chain_register(&cpufreq_transition_notifier_list, nb); ret = notifier_chain_register(&cpufreq_transition_notifier_list, nb);
...@@ -438,7 +470,7 @@ int cpufreq_register_notifier(struct notifier_block *nb, unsigned int list) ...@@ -438,7 +470,7 @@ int cpufreq_register_notifier(struct notifier_block *nb, unsigned int list)
default: default:
ret = -EINVAL; ret = -EINVAL;
} }
up(&cpufreq_notifier_sem); up_write(&cpufreq_notifier_rwsem);
return ret; return ret;
} }
...@@ -459,7 +491,7 @@ int cpufreq_unregister_notifier(struct notifier_block *nb, unsigned int list) ...@@ -459,7 +491,7 @@ int cpufreq_unregister_notifier(struct notifier_block *nb, unsigned int list)
{ {
int ret; int ret;
down(&cpufreq_notifier_sem); down_write(&cpufreq_notifier_rwsem);
switch (list) { switch (list) {
case CPUFREQ_TRANSITION_NOTIFIER: case CPUFREQ_TRANSITION_NOTIFIER:
ret = notifier_chain_unregister(&cpufreq_transition_notifier_list, nb); ret = notifier_chain_unregister(&cpufreq_transition_notifier_list, nb);
...@@ -470,7 +502,7 @@ int cpufreq_unregister_notifier(struct notifier_block *nb, unsigned int list) ...@@ -470,7 +502,7 @@ int cpufreq_unregister_notifier(struct notifier_block *nb, unsigned int list)
default: default:
ret = -EINVAL; ret = -EINVAL;
} }
up(&cpufreq_notifier_sem); up_write(&cpufreq_notifier_rwsem);
return ret; return ret;
} }
...@@ -481,71 +513,72 @@ EXPORT_SYMBOL(cpufreq_unregister_notifier); ...@@ -481,71 +513,72 @@ EXPORT_SYMBOL(cpufreq_unregister_notifier);
* GOVERNORS * * GOVERNORS *
*********************************************************************/ *********************************************************************/
inline int cpufreq_driver_target_l(struct cpufreq_policy *policy,
unsigned int target_freq,
unsigned int relation)
{
unsigned int ret;
down(&cpufreq_driver_sem);
if (!cpufreq_driver)
ret = -EINVAL;
else
ret = cpufreq_driver->target(policy, target_freq, relation);
up(&cpufreq_driver_sem);
return ret;
}
EXPORT_SYMBOL_GPL(cpufreq_driver_target_l);
inline int cpufreq_driver_target(struct cpufreq_policy *policy, inline int cpufreq_driver_target(struct cpufreq_policy *policy,
unsigned int target_freq, unsigned int target_freq,
unsigned int relation) unsigned int relation)
{ {
return cpufreq_driver->target(policy, target_freq, relation); unsigned int ret;
unsigned int cpu = policy->cpu;
if (!cpufreq_cpu_get(cpu))
return -EINVAL;
down(&cpufreq_driver->policy[cpu].lock);
ret = cpufreq_driver->target(policy, target_freq, relation);
up(&cpufreq_driver->policy[cpu].lock);
cpufreq_cpu_put(cpu);
return ret;
} }
EXPORT_SYMBOL_GPL(cpufreq_driver_target); EXPORT_SYMBOL_GPL(cpufreq_driver_target);
static int cpufreq_governor(unsigned int cpu, unsigned int event) int cpufreq_governor(unsigned int cpu, unsigned int event)
{ {
int ret = 0; int ret = 0;
struct cpufreq_policy *policy = &cpufreq_driver->policy[cpu]; 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 (event == CPUFREQ_GOV_START) if (!try_module_get(cpufreq_driver->policy[cpu].governor->owner))
if (!try_module_get(cpufreq_driver->policy[cpu].governor->owner)) break;
break;
ret = cpufreq_driver->policy[cpu].governor->governor(policy, event); ret = cpufreq_driver->policy[cpu].governor->governor(policy, event);
if ((event == CPUFREQ_GOV_STOP) || /* we keep one module reference alive for each CPU governed by this CPU */
(ret && (event == CPUFREQ_GOV_START))) if ((event != CPUFREQ_GOV_START) || ret)
module_put(cpufreq_driver->policy[cpu].governor->owner);
if ((event == CPUFREQ_GOV_STOP) && !ret)
module_put(cpufreq_driver->policy[cpu].governor->owner); module_put(cpufreq_driver->policy[cpu].governor->owner);
break; break;
default: default:
ret = -EINVAL; ret = -EINVAL;
} }
return ret;
}
cpufreq_cpu_put(cpu);
int cpufreq_governor_l(unsigned int cpu, unsigned int event)
{
int ret = 0;
down(&cpufreq_driver_sem);
ret = cpufreq_governor(cpu, event);
up(&cpufreq_driver_sem);
return ret; return ret;
} }
EXPORT_SYMBOL_GPL(cpufreq_governor_l); EXPORT_SYMBOL_GPL(cpufreq_governor);
int cpufreq_register_governor(struct cpufreq_governor *governor) int cpufreq_register_governor(struct cpufreq_governor *governor)
...@@ -560,16 +593,17 @@ int cpufreq_register_governor(struct cpufreq_governor *governor) ...@@ -560,16 +593,17 @@ int cpufreq_register_governor(struct cpufreq_governor *governor)
if (!strnicmp(governor->name,"performance",CPUFREQ_NAME_LEN)) if (!strnicmp(governor->name,"performance",CPUFREQ_NAME_LEN))
return -EBUSY; return -EBUSY;
down(&cpufreq_driver_sem); down(&cpufreq_governor_sem);
list_for_each_entry(t, &cpufreq_governor_list, governor_list) { list_for_each_entry(t, &cpufreq_governor_list, governor_list) {
if (!strnicmp(governor->name,t->name,CPUFREQ_NAME_LEN)) { if (!strnicmp(governor->name,t->name,CPUFREQ_NAME_LEN)) {
up(&cpufreq_driver_sem); up(&cpufreq_governor_sem);
return -EBUSY; return -EBUSY;
} }
} }
list_add(&governor->governor_list, &cpufreq_governor_list); list_add(&governor->governor_list, &cpufreq_governor_list);
up(&cpufreq_driver_sem);
up(&cpufreq_governor_sem);
return 0; return 0;
} }
...@@ -583,7 +617,8 @@ void cpufreq_unregister_governor(struct cpufreq_governor *governor) ...@@ -583,7 +617,8 @@ void cpufreq_unregister_governor(struct cpufreq_governor *governor)
if (!governor) if (!governor)
return; return;
down(&cpufreq_driver_sem); down(&cpufreq_governor_sem);
/* /*
* Unless the user uses rmmod -f, we can be safe. But we never * Unless the user uses rmmod -f, we can be safe. But we never
* know, so check whether if it's currently used. If so, * know, so check whether if it's currently used. If so,
...@@ -591,17 +626,21 @@ void cpufreq_unregister_governor(struct cpufreq_governor *governor) ...@@ -591,17 +626,21 @@ void cpufreq_unregister_governor(struct cpufreq_governor *governor)
*/ */
for (i=0; i<NR_CPUS; i++) for (i=0; i<NR_CPUS; i++)
{ {
if (cpufreq_driver && if (!cpufreq_cpu_get(i))
(cpufreq_driver->policy[i].policy == CPUFREQ_POLICY_GOVERNOR) && continue;
if ((cpufreq_driver->policy[i].policy == CPUFREQ_POLICY_GOVERNOR) &&
(cpufreq_driver->policy[i].governor == governor)) { (cpufreq_driver->policy[i].governor == governor)) {
cpufreq_governor(i, CPUFREQ_GOV_STOP); cpufreq_governor(i, CPUFREQ_GOV_STOP);
cpufreq_driver->policy[i].policy = CPUFREQ_POLICY_PERFORMANCE; cpufreq_driver->policy[i].policy = CPUFREQ_POLICY_PERFORMANCE;
cpufreq_governor(i, CPUFREQ_GOV_START); cpufreq_governor(i, CPUFREQ_GOV_START);
cpufreq_governor(i, CPUFREQ_GOV_LIMITS);
} }
cpufreq_cpu_put(i);
} }
/* now we can safely remove it from the list */ /* now we can safely remove it from the list */
list_del(&governor->governor_list); list_del(&governor->governor_list);
up(&cpufreq_driver_sem); up(&cpufreq_governor_sem);
return; return;
} }
EXPORT_SYMBOL_GPL(cpufreq_unregister_governor); EXPORT_SYMBOL_GPL(cpufreq_unregister_governor);
...@@ -620,19 +659,17 @@ EXPORT_SYMBOL_GPL(cpufreq_unregister_governor); ...@@ -620,19 +659,17 @@ 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)
{ {
down(&cpufreq_driver_sem); if (!policy || !cpufreq_cpu_get(cpu))
if (!cpufreq_driver || !policy ||
(cpu >= NR_CPUS) || (!cpu_online(cpu))) {
up(&cpufreq_driver_sem);
return -EINVAL; return -EINVAL;
}
down(&cpufreq_driver_sem);
memcpy(policy, memcpy(policy,
&cpufreq_driver->policy[cpu], &cpufreq_driver->policy[cpu],
sizeof(struct cpufreq_policy)); sizeof(struct cpufreq_policy));
up(&cpufreq_driver_sem); up(&cpufreq_driver_sem);
cpufreq_cpu_put(cpu);
return 0; return 0;
} }
EXPORT_SYMBOL(cpufreq_get_policy); EXPORT_SYMBOL(cpufreq_get_policy);
...@@ -646,27 +683,23 @@ EXPORT_SYMBOL(cpufreq_get_policy); ...@@ -646,27 +683,23 @@ EXPORT_SYMBOL(cpufreq_get_policy);
*/ */
int cpufreq_set_policy(struct cpufreq_policy *policy) int cpufreq_set_policy(struct cpufreq_policy *policy)
{ {
int ret; int ret = 0;
down(&cpufreq_driver_sem); if (!policy || !cpufreq_cpu_get(policy->cpu))
if (!cpufreq_driver || !policy ||
(policy->cpu >= NR_CPUS) || (!cpu_online(policy->cpu))) {
up(&cpufreq_driver_sem);
return -EINVAL; return -EINVAL;
}
down(&cpufreq_driver_sem);
memcpy(&policy->cpuinfo, memcpy(&policy->cpuinfo,
&cpufreq_driver->policy[policy->cpu].cpuinfo, &cpufreq_driver->policy[policy->cpu].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);
if (ret) { if (ret)
up(&cpufreq_driver_sem); goto error_out;
return ret;
}
down(&cpufreq_notifier_sem); down_read(&cpufreq_notifier_rwsem);
/* adjust if necessary - all reasons */ /* adjust if necessary - all reasons */
notifier_call_chain(&cpufreq_policy_notifier_list, CPUFREQ_ADJUST, notifier_call_chain(&cpufreq_policy_notifier_list, CPUFREQ_ADJUST,
...@@ -680,17 +713,18 @@ int cpufreq_set_policy(struct cpufreq_policy *policy) ...@@ -680,17 +713,18 @@ int cpufreq_set_policy(struct cpufreq_policy *policy)
which might be different to the first one */ which might be different to the first one */
ret = cpufreq_driver->verify(policy); ret = cpufreq_driver->verify(policy);
if (ret) { if (ret) {
up(&cpufreq_notifier_sem); up_read(&cpufreq_notifier_rwsem);
up(&cpufreq_driver_sem); goto error_out;
return ret;
} }
/* notification of the new policy */ /* notification of the new policy */
notifier_call_chain(&cpufreq_policy_notifier_list, CPUFREQ_NOTIFY, notifier_call_chain(&cpufreq_policy_notifier_list, CPUFREQ_NOTIFY,
policy); policy);
up(&cpufreq_notifier_sem); 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].min = policy->min;
cpufreq_driver->policy[policy->cpu].max = policy->max; cpufreq_driver->policy[policy->cpu].max = policy->max;
...@@ -718,9 +752,11 @@ int cpufreq_set_policy(struct cpufreq_policy *policy) ...@@ -718,9 +752,11 @@ int cpufreq_set_policy(struct cpufreq_policy *policy)
cpufreq_governor(policy->cpu, CPUFREQ_GOV_LIMITS); cpufreq_governor(policy->cpu, CPUFREQ_GOV_LIMITS);
} }
} }
up(&cpufreq_driver_sem); up(&cpufreq_driver_sem);
error_out:
cpufreq_cpu_put(policy->cpu);
return ret; return ret;
} }
EXPORT_SYMBOL(cpufreq_set_policy); EXPORT_SYMBOL(cpufreq_set_policy);
...@@ -766,7 +802,7 @@ static inline void adjust_jiffies(unsigned long val, struct cpufreq_freqs *ci) ...@@ -766,7 +802,7 @@ static inline void adjust_jiffies(unsigned long val, struct cpufreq_freqs *ci)
*/ */
void cpufreq_notify_transition(struct cpufreq_freqs *freqs, unsigned int state) void cpufreq_notify_transition(struct cpufreq_freqs *freqs, unsigned int state)
{ {
down(&cpufreq_notifier_sem); down_read(&cpufreq_notifier_rwsem);
switch (state) { switch (state) {
case CPUFREQ_PRECHANGE: case CPUFREQ_PRECHANGE:
notifier_call_chain(&cpufreq_transition_notifier_list, CPUFREQ_PRECHANGE, freqs); notifier_call_chain(&cpufreq_transition_notifier_list, CPUFREQ_PRECHANGE, freqs);
...@@ -778,7 +814,7 @@ void cpufreq_notify_transition(struct cpufreq_freqs *freqs, unsigned int state) ...@@ -778,7 +814,7 @@ void cpufreq_notify_transition(struct cpufreq_freqs *freqs, unsigned int state)
cpufreq_driver->policy[freqs->cpu].cur = freqs->new; cpufreq_driver->policy[freqs->cpu].cur = freqs->new;
break; break;
} }
up(&cpufreq_notifier_sem); up_read(&cpufreq_notifier_rwsem);
} }
EXPORT_SYMBOL_GPL(cpufreq_notify_transition); EXPORT_SYMBOL_GPL(cpufreq_notify_transition);
...@@ -800,38 +836,33 @@ EXPORT_SYMBOL_GPL(cpufreq_notify_transition); ...@@ -800,38 +836,33 @@ EXPORT_SYMBOL_GPL(cpufreq_notify_transition);
*/ */
int cpufreq_register_driver(struct cpufreq_driver *driver_data) int cpufreq_register_driver(struct cpufreq_driver *driver_data)
{ {
int ret = 0; if (!driver_data || !driver_data->verify || !driver_data->init ||
if (cpufreq_driver)
return -EBUSY;
if (!driver_data || !driver_data->verify ||
((!driver_data->setpolicy) && (!driver_data->target))) ((!driver_data->setpolicy) && (!driver_data->target)))
return -EINVAL; return -EINVAL;
down(&cpufreq_driver_sem);
if (kset_get(&cpufreq_interface.kset)) {
kset_put(&cpufreq_interface.kset);
return -EBUSY;
}
down(&cpufreq_driver_sem);
if (cpufreq_driver) {
up(&cpufreq_driver_sem);
return -EBUSY;
}
cpufreq_driver = driver_data; cpufreq_driver = driver_data;
up(&cpufreq_driver_sem);
cpufreq_driver->policy = kmalloc(NR_CPUS * sizeof(struct cpufreq_policy), GFP_KERNEL);
if (!cpufreq_driver->policy) { if (!cpufreq_driver->policy) {
/* then we need per-CPU init */ cpufreq_driver = NULL;
if (!cpufreq_driver->init) { return -ENOMEM;
up(&cpufreq_driver_sem);
return -EINVAL;
}
cpufreq_driver->policy = kmalloc(NR_CPUS * sizeof(struct cpufreq_policy), GFP_KERNEL);
if (!cpufreq_driver->policy) {
up(&cpufreq_driver_sem);
return -ENOMEM;
}
memset(cpufreq_driver->policy, 0, NR_CPUS * sizeof(struct cpufreq_policy));
} }
up(&cpufreq_driver_sem);
ret = interface_register(&cpufreq_interface); memset(cpufreq_driver->policy, 0, NR_CPUS * sizeof(struct cpufreq_policy));
return ret; return interface_register(&cpufreq_interface);
} }
EXPORT_SYMBOL_GPL(cpufreq_register_driver); EXPORT_SYMBOL_GPL(cpufreq_register_driver);
...@@ -846,20 +877,20 @@ EXPORT_SYMBOL_GPL(cpufreq_register_driver); ...@@ -846,20 +877,20 @@ EXPORT_SYMBOL_GPL(cpufreq_register_driver);
*/ */
int cpufreq_unregister_driver(struct cpufreq_driver *driver) int cpufreq_unregister_driver(struct cpufreq_driver *driver)
{ {
down(&cpufreq_driver_sem); if (!kset_get(&cpufreq_interface.kset))
return 0;
if (!cpufreq_driver || if (!cpufreq_driver || (driver != cpufreq_driver)) {
((driver != cpufreq_driver) && (driver != NULL))) { /* compat */ kset_put(&cpufreq_interface.kset);
up(&cpufreq_driver_sem);
return -EINVAL; return -EINVAL;
} }
kset_put(&cpufreq_interface.kset);
interface_unregister(&cpufreq_interface); interface_unregister(&cpufreq_interface);
if (driver) down(&cpufreq_driver_sem);
kfree(cpufreq_driver->policy); kfree(cpufreq_driver->policy);
cpufreq_driver = NULL; cpufreq_driver = NULL;
up(&cpufreq_driver_sem); up(&cpufreq_driver_sem);
return 0; return 0;
...@@ -883,22 +914,28 @@ int cpufreq_restore(void) ...@@ -883,22 +914,28 @@ int cpufreq_restore(void)
if (in_interrupt()) if (in_interrupt())
panic("cpufreq_restore() called from interrupt context!"); panic("cpufreq_restore() called from interrupt context!");
if (!kset_get(&cpufreq_interface.kset))
return 0;
if (!try_module_get(cpufreq_driver->owner))
goto error_out;
for (i=0;i<NR_CPUS;i++) { for (i=0;i<NR_CPUS;i++) {
if (!cpu_online(i)) if (!cpu_online(i) || !cpufreq_cpu_get(i))
continue; continue;
down(&cpufreq_driver_sem); down(&cpufreq_driver_sem);
if (!cpufreq_driver) {
up(&cpufreq_driver_sem);
return 0;
}
memcpy(&policy, &cpufreq_driver->policy[i], sizeof(struct cpufreq_policy)); memcpy(&policy, &cpufreq_driver->policy[i], sizeof(struct cpufreq_policy));
up(&cpufreq_driver_sem); up(&cpufreq_driver_sem);
ret += cpufreq_set_policy(&policy); ret += cpufreq_set_policy(&policy);
cpufreq_cpu_put(i);
} }
module_put(cpufreq_driver->owner);
error_out:
kset_put(&cpufreq_interface.kset);
return ret; return ret;
} }
EXPORT_SYMBOL_GPL(cpufreq_restore); EXPORT_SYMBOL_GPL(cpufreq_restore);
......
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