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
kernel "constant" loops_per_jiffy is updated on frequency changes
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
====================
......
......@@ -63,6 +63,9 @@ And optionally
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
......
......@@ -114,9 +114,9 @@ the processor shall run at.
------------------------------
The preferred interface is located in the sysfs filesystem. If you
mounted it at /sys, the cpufreq interface is located in the
cpu-device directory (e.g. /sys/devices/sys/cpu0/ for the first
CPU).
mounted it at /sys, the cpufreq interface is located in a subdirectory
"cpufreq" within the cpu-device directory
(e.g. /sys/devices/sys/cpu0/cpufreq/ for the first CPU).
cpuinfo_min_freq : this file shows the minimum operating
frequency the processor can run at(in kHz)
......@@ -125,7 +125,7 @@ cpuinfo_max_freq : this file shows the maximum operating
scaling_driver : this file shows what cpufreq driver is
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
currently activated governor in
......
......@@ -619,6 +619,7 @@ static struct cpufreq_driver acpi_cpufreq_driver = {
.init = acpi_cpufreq_cpu_init,
.exit = acpi_cpufreq_cpu_exit,
.name = "acpi-cpufreq",
.owner = THIS_MODULE,
};
......
......@@ -250,6 +250,7 @@ static struct cpufreq_driver elanfreq_driver = {
.target = elanfreq_target,
.init = elanfreq_cpu_init,
.name = "elanfreq",
.owner = THIS_MODULE,
};
......
......@@ -451,6 +451,7 @@ static struct cpufreq_driver gx_suspmod_driver = {
.target = cpufreq_gx_target,
.init = cpufreq_gx_cpu_init,
.name = "gx-suspmod",
.owner = THIS_MODULE,
};
static int __init cpufreq_gx_init(void)
......
......@@ -649,6 +649,7 @@ static struct cpufreq_driver longhaul_driver = {
.target = longhaul_target,
.init = longhaul_cpu_init,
.name = "longhaul",
.owner = THIS_MODULE,
};
static int __init longhaul_init (void)
......
......@@ -253,6 +253,7 @@ static struct cpufreq_driver longrun_driver = {
.setpolicy = longrun_set_policy,
.init = longrun_cpu_init,
.name = "longrun",
.owner = THIS_MODULE,
};
......
......@@ -214,6 +214,7 @@ static int cpufreq_p4_cpu_init(struct cpufreq_policy *policy)
else
p4clockmod_table[i].frequency = (stock_freq * i)/8;
}
cpufreq_frequency_table_get_attr(p4clockmod_table, policy->cpu);
/* cpuinfo and default policy values */
policy->policy = CPUFREQ_POLICY_PERFORMANCE;
......@@ -226,9 +227,14 @@ static int cpufreq_p4_cpu_init(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);
}
static struct freq_attr* p4clockmod_attr[] = {
&cpufreq_freq_attr_scaling_available_freqs,
NULL,
};
static struct cpufreq_driver p4clockmod_driver = {
.verify = cpufreq_p4_verify,
......@@ -236,6 +242,8 @@ static struct cpufreq_driver p4clockmod_driver = {
.init = cpufreq_p4_cpu_init,
.exit = cpufreq_p4_cpu_exit,
.name = "p4-clockmod",
.owner = THIS_MODULE,
.attr = p4clockmod_attr,
};
......
......@@ -190,6 +190,7 @@ static struct cpufreq_driver powernow_k6_driver = {
.init = powernow_k6_cpu_init,
.exit = powernow_k6_cpu_exit,
.name = "powernow-k6",
.owner = THIS_MODULE,
};
......
......@@ -377,6 +377,7 @@ static struct cpufreq_driver powernow_driver = {
.target = powernow_target,
.init = powernow_cpu_init,
.name = "powernow-k7",
.owner = THIS_MODULE,
};
static int __init powernow_init (void)
......
......@@ -29,7 +29,6 @@
#include <asm/msr.h>
/* speedstep_chipset:
* It is necessary to know which chipset is used. As accesses to
* this device occur at various places in this module, we need a
......@@ -40,7 +39,7 @@ static struct pci_dev *speedstep_chipset_dev;
#define SPEEDSTEP_CHIPSET_ICH2M 0x00000002
#define SPEEDSTEP_CHIPSET_ICH3M 0x00000003
#define SPEEDSTEP_CHIPSET_ICH4M 0x00000004
/* speedstep_processor
*/
......@@ -106,6 +105,7 @@ static int speedstep_get_state (unsigned int *state)
switch (speedstep_chipset) {
case SPEEDSTEP_CHIPSET_ICH2M:
case SPEEDSTEP_CHIPSET_ICH3M:
case SPEEDSTEP_CHIPSET_ICH4M:
/* get PMBASE */
pci_read_config_dword(speedstep_chipset_dev, 0x40, &pmbase);
if (!(pmbase & 0x01))
......@@ -166,6 +166,7 @@ static void speedstep_set_state (unsigned int state, int notify)
switch (speedstep_chipset) {
case SPEEDSTEP_CHIPSET_ICH2M:
case SPEEDSTEP_CHIPSET_ICH3M:
case SPEEDSTEP_CHIPSET_ICH4M:
/* get PMBASE */
pci_read_config_dword(speedstep_chipset_dev, 0x40, &pmbase);
if (!(pmbase & 0x01))
......@@ -245,6 +246,7 @@ static int speedstep_activate (void)
switch (speedstep_chipset) {
case SPEEDSTEP_CHIPSET_ICH2M:
case SPEEDSTEP_CHIPSET_ICH3M:
case SPEEDSTEP_CHIPSET_ICH4M:
{
u16 value = 0;
......@@ -276,6 +278,14 @@ static int speedstep_activate (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,
PCI_DEVICE_ID_INTEL_82801CA_12,
PCI_ANY_ID,
......@@ -658,6 +668,7 @@ static struct cpufreq_driver speedstep_driver = {
.verify = speedstep_verify,
.target = speedstep_target,
.init = speedstep_cpu_init,
.owner = THIS_MODULE,
};
......
......@@ -276,6 +276,7 @@ static int __init us3freq_init(void)
driver->target = us3freq_target;
driver->init = us3freq_cpu_init;
driver->exit = us3freq_cpu_exit;
driver->owner = THIS_MODULE,
strcpy(driver->name, "UltraSPARC-III");
cpufreq_us3_driver = driver;
......
......@@ -77,56 +77,6 @@ int cpufreq_frequency_table_verify(struct cpufreq_policy *policy,
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,
struct cpufreq_frequency_table *table,
unsigned int target_freq,
......@@ -197,6 +147,56 @@ int cpufreq_frequency_table_target(struct cpufreq_policy *policy,
}
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_DESCRIPTION ("CPUfreq frequency table helpers");
......
......@@ -23,6 +23,7 @@
#include <linux/sysctl.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/sysfs.h>
#include <asm/uaccess.h>
......@@ -112,7 +113,7 @@ int cpufreq_set(unsigned int freq, unsigned int cpu)
if (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);
err:
......@@ -465,23 +466,14 @@ static inline void cpufreq_sysctl_exit(void)
/************************** 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 (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]);
return sprintf (buf, "%u\n", cpu_cur_freq[policy->cpu]);
}
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 ret;
......@@ -489,13 +481,16 @@ store_speed (struct device *dev, const char *buf, size_t count)
if (ret != 1)
return -EINVAL;
cpufreq_set(freq, cpu);
cpufreq_set(freq, policy->cpu);
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,
unsigned int event)
......@@ -511,7 +506,7 @@ static int cpufreq_governor_userspace(struct cpufreq_policy *policy,
cpu_min_freq[cpu] = policy->min;
cpu_max_freq[cpu] = policy->max;
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));
up(&userspace_sem);
break;
......@@ -520,7 +515,7 @@ static int cpufreq_governor_userspace(struct cpufreq_policy *policy,
cpu_is_managed[cpu] = 0;
cpu_min_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);
module_put(THIS_MODULE);
break;
......
......@@ -18,7 +18,8 @@
#include <linux/notifier.h>
#include <linux/threads.h>
#include <linux/device.h>
#include <linux/kobject.h>
#include <linux/sysfs.h>
#define CPUFREQ_NAME_LEN 16
......@@ -69,6 +70,8 @@ struct cpufreq_policy {
struct cpufreq_cpuinfo cpuinfo; /* see above */
struct device * dev;
struct kobject kobj;
struct semaphore lock; /* CPU ->setpolicy or ->target may
only be called once a time */
};
#define CPUFREQ_ADJUST (0)
......@@ -131,18 +134,13 @@ struct cpufreq_governor {
};
/* pass a target to the cpufreq driver
* _l : (cpufreq_driver_sem is not held)
*/
inline int cpufreq_driver_target(struct cpufreq_policy *policy,
unsigned int target_freq,
unsigned int relation);
inline int cpufreq_driver_target_l(struct cpufreq_policy *policy,
unsigned int target_freq,
unsigned int relation);
/* 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);
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_H 1 /* highest frequency below or at target */
struct freq_attr;
struct cpufreq_driver {
/* needed by all drivers */
int (*verify) (struct cpufreq_policy *policy);
......@@ -164,16 +164,15 @@ struct cpufreq_driver {
int (*target) (struct cpufreq_policy *policy,
unsigned int target_freq,
unsigned int relation);
struct module *owner;
/* optional, for the moment */
int (*init) (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_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);
......@@ -194,6 +193,13 @@ static inline void cpufreq_verify_within_limits(struct cpufreq_policy *policy, u
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 *
*********************************************************************/
......@@ -289,16 +295,21 @@ int cpufreq_frequency_table_cpuinfo(struct cpufreq_policy *policy,
int cpufreq_frequency_table_verify(struct cpufreq_policy *policy,
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,
struct cpufreq_frequency_table *table,
unsigned int target_freq,
unsigned int relation,
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 /* _LINUX_CPUFREQ_H */
......@@ -1854,6 +1854,7 @@
#define PCI_DEVICE_ID_INTEL_82801DB_7 0x24c7
#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_12 0x24cc
#define PCI_DEVICE_ID_INTEL_82801DB_13 0x24cd
#define PCI_DEVICE_ID_INTEL_82820_HB 0x2500
#define PCI_DEVICE_ID_INTEL_82820_UP_HB 0x2501
......
......@@ -43,12 +43,40 @@ static DECLARE_MUTEX (cpufreq_driver_sem);
*/
static struct notifier_block *cpufreq_policy_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);
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 *
......@@ -67,19 +95,19 @@ int cpufreq_parse_governor (char *str_governor, unsigned int *policy, struct cpu
return 0;
} else {
struct cpufreq_governor *t;
down(&cpufreq_driver_sem);
down(&cpufreq_governor_sem);
if (!cpufreq_driver || !cpufreq_driver->target)
goto out;
list_for_each_entry(t, &cpufreq_governor_list, governor_list) {
if (!strnicmp(str_governor,t->name,CPUFREQ_NAME_LEN)) {
*governor = t;
*policy = CPUFREQ_POLICY_GOVERNOR;
up(&cpufreq_driver_sem);
up(&cpufreq_governor_sem);
return 0;
}
}
out:
up(&cpufreq_driver_sem);
up(&cpufreq_governor_sem);
}
return -EINVAL;
}
......@@ -120,14 +148,7 @@ static inline int to_cpu_nr (struct device *dev)
static ssize_t show_##file_name \
(struct cpufreq_policy * policy, char *buf) \
{ \
unsigned int value = 0; \
\
down(&cpufreq_driver_sem); \
if (cpufreq_driver) \
value = policy->object; \
up(&cpufreq_driver_sem); \
\
return sprintf (buf, "%u\n", value); \
return sprintf (buf, "%u\n", policy->object); \
}
show_one(cpuinfo_min_freq, cpuinfo.min_freq);
......@@ -143,12 +164,17 @@ static ssize_t store_##file_name \
(struct cpufreq_policy * policy, const char *buf, size_t count) \
{ \
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) \
return -EINVAL; \
\
ret = cpufreq_set_policy(policy); \
ret = cpufreq_set_policy(&new_policy); \
\
return ret ? ret : count; \
}
......@@ -161,26 +187,16 @@ store_one(scaling_max_freq,max);
*/
static ssize_t show_scaling_governor (struct cpufreq_policy * policy, char *buf)
{
unsigned int value = 0;
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) {
switch (policy->policy) {
case CPUFREQ_POLICY_POWERSAVE:
return sprintf(buf, "powersave\n");
case CPUFREQ_POLICY_PERFORMANCE:
return sprintf(buf, "performance\n");
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,
{
unsigned int ret = -EINVAL;
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);
if (ret != 1)
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;
ret = cpufreq_set_policy(policy);
ret = cpufreq_set_policy(&new_policy);
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) \
struct freq_attr _name = { \
......@@ -227,6 +273,8 @@ struct freq_attr _name = { \
define_one_ro(cpuinfo_min_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_max_freq);
define_one_rw(scaling_governor);
......@@ -237,10 +285,11 @@ static struct attribute * default_attrs[] = {
&scaling_min_freq.attr,
&scaling_max_freq.attr,
&scaling_governor.attr,
&scaling_driver.attr,
&scaling_available_governors.attr,
NULL
};
#define to_policy(k) container_of(k,struct cpufreq_policy,kobj)
#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)
{
struct cpufreq_policy * policy = to_policy(kobj);
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,
......@@ -256,7 +310,12 @@ static ssize_t store(struct kobject * kobj, struct attribute * attr,
{
struct cpufreq_policy * policy = to_policy(kobj);
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 = {
......@@ -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
*
......@@ -329,57 +338,62 @@ static int cpufreq_add_dev (struct device * dev)
{
unsigned int cpu = to_cpu_nr(dev);
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 (!cpufreq_driver) {
up(&cpufreq_driver_sem);
if (!kset_get(&cpufreq_interface.kset))
return -EINVAL;
if (!try_module_get(cpufreq_driver->owner)) {
kset_put(&cpufreq_interface.kset);
return -EINVAL;
}
/* call driver. From then on the cpufreq must be able
* 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) {
ret = cpufreq_driver->init(&cpufreq_driver->policy[cpu]);
if (ret) {
up(&cpufreq_driver_sem);
return -ENODEV;
}
ret = cpufreq_driver->init(policy);
if (ret)
goto out;
}
/* set default policy on this CPU */
memcpy(&policy,
&cpufreq_driver->policy[cpu],
down(&cpufreq_driver_sem);
memcpy(&new_policy,
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);
ret = cpufreq_set_policy(&policy);
if (ret)
return -EINVAL;
down(&cpufreq_driver_sem);
init_MUTEX(&policy->lock);
/* prepare interface data */
policy.kobj.parent = &dev->kobj;
policy.kobj.ktype = &ktype_cpufreq;
policy.dev = dev;
strncpy(policy.kobj.name,
policy->kobj.parent = &dev->kobj;
policy->kobj.ktype = &ktype_cpufreq;
policy->dev = dev;
strncpy(policy->kobj.name,
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;
}
......@@ -387,21 +401,39 @@ static int cpufreq_add_dev (struct device * dev)
/**
* cpufreq_remove_dev - remove a CPU device
*
* Removes the cpufreq interface for a CPU device. Is called with
* cpufreq_driver_sem locked.
* Removes the cpufreq interface for a CPU device.
*/
static int cpufreq_remove_dev (struct device * dev)
{
unsigned int cpu = to_cpu_nr(dev);
if (cpufreq_driver->target)
cpufreq_governor(cpu, CPUFREQ_GOV_STOP);
if (!kset_get(&cpufreq_interface.kset))
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)
cpufreq_driver->exit(&cpufreq_driver->policy[cpu]);
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;
}
......@@ -427,7 +459,7 @@ int cpufreq_register_notifier(struct notifier_block *nb, unsigned int list)
{
int ret;
down(&cpufreq_notifier_sem);
down_write(&cpufreq_notifier_rwsem);
switch (list) {
case CPUFREQ_TRANSITION_NOTIFIER:
ret = notifier_chain_register(&cpufreq_transition_notifier_list, nb);
......@@ -438,7 +470,7 @@ int cpufreq_register_notifier(struct notifier_block *nb, unsigned int list)
default:
ret = -EINVAL;
}
up(&cpufreq_notifier_sem);
up_write(&cpufreq_notifier_rwsem);
return ret;
}
......@@ -459,7 +491,7 @@ int cpufreq_unregister_notifier(struct notifier_block *nb, unsigned int list)
{
int ret;
down(&cpufreq_notifier_sem);
down_write(&cpufreq_notifier_rwsem);
switch (list) {
case CPUFREQ_TRANSITION_NOTIFIER:
ret = notifier_chain_unregister(&cpufreq_transition_notifier_list, nb);
......@@ -470,7 +502,7 @@ int cpufreq_unregister_notifier(struct notifier_block *nb, unsigned int list)
default:
ret = -EINVAL;
}
up(&cpufreq_notifier_sem);
up_write(&cpufreq_notifier_rwsem);
return ret;
}
......@@ -481,71 +513,72 @@ EXPORT_SYMBOL(cpufreq_unregister_notifier);
* 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,
unsigned int target_freq,
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);
static int cpufreq_governor(unsigned int cpu, unsigned int event)
int cpufreq_governor(unsigned int cpu, unsigned int event)
{
int ret = 0;
struct cpufreq_policy *policy = &cpufreq_driver->policy[cpu];
if (!cpufreq_cpu_get(cpu))
return -EINVAL;
switch (policy->policy) {
case CPUFREQ_POLICY_POWERSAVE:
if ((event == CPUFREQ_GOV_LIMITS) || (event == CPUFREQ_GOV_START))
if ((event == CPUFREQ_GOV_LIMITS) || (event == CPUFREQ_GOV_START)) {
down(&cpufreq_driver->policy[cpu].lock);
ret = cpufreq_driver->target(policy, policy->min, CPUFREQ_RELATION_L);
up(&cpufreq_driver->policy[cpu].lock);
}
break;
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);
up(&cpufreq_driver->policy[cpu].lock);
}
break;
case CPUFREQ_POLICY_GOVERNOR:
ret = -EINVAL;
if (event == CPUFREQ_GOV_START)
if (!try_module_get(cpufreq_driver->policy[cpu].governor->owner))
break;
if (!try_module_get(cpufreq_driver->policy[cpu].governor->owner))
break;
ret = cpufreq_driver->policy[cpu].governor->governor(policy, event);
if ((event == CPUFREQ_GOV_STOP) ||
(ret && (event == CPUFREQ_GOV_START)))
/* we keep one module reference alive for each CPU governed by this CPU */
if ((event != CPUFREQ_GOV_START) || ret)
module_put(cpufreq_driver->policy[cpu].governor->owner);
if ((event == CPUFREQ_GOV_STOP) && !ret)
module_put(cpufreq_driver->policy[cpu].governor->owner);
break;
default:
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;
}
EXPORT_SYMBOL_GPL(cpufreq_governor_l);
EXPORT_SYMBOL_GPL(cpufreq_governor);
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))
return -EBUSY;
down(&cpufreq_driver_sem);
down(&cpufreq_governor_sem);
list_for_each_entry(t, &cpufreq_governor_list, governor_list) {
if (!strnicmp(governor->name,t->name,CPUFREQ_NAME_LEN)) {
up(&cpufreq_driver_sem);
up(&cpufreq_governor_sem);
return -EBUSY;
}
}
list_add(&governor->governor_list, &cpufreq_governor_list);
up(&cpufreq_driver_sem);
up(&cpufreq_governor_sem);
return 0;
}
......@@ -583,7 +617,8 @@ void cpufreq_unregister_governor(struct cpufreq_governor *governor)
if (!governor)
return;
down(&cpufreq_driver_sem);
down(&cpufreq_governor_sem);
/*
* Unless the user uses rmmod -f, we can be safe. But we never
* know, so check whether if it's currently used. If so,
......@@ -591,17 +626,21 @@ void cpufreq_unregister_governor(struct cpufreq_governor *governor)
*/
for (i=0; i<NR_CPUS; i++)
{
if (cpufreq_driver &&
(cpufreq_driver->policy[i].policy == CPUFREQ_POLICY_GOVERNOR) &&
if (!cpufreq_cpu_get(i))
continue;
if ((cpufreq_driver->policy[i].policy == CPUFREQ_POLICY_GOVERNOR) &&
(cpufreq_driver->policy[i].governor == governor)) {
cpufreq_governor(i, CPUFREQ_GOV_STOP);
cpufreq_driver->policy[i].policy = CPUFREQ_POLICY_PERFORMANCE;
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 */
list_del(&governor->governor_list);
up(&cpufreq_driver_sem);
up(&cpufreq_governor_sem);
return;
}
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)
{
down(&cpufreq_driver_sem);
if (!cpufreq_driver || !policy ||
(cpu >= NR_CPUS) || (!cpu_online(cpu))) {
up(&cpufreq_driver_sem);
if (!policy || !cpufreq_cpu_get(cpu))
return -EINVAL;
}
down(&cpufreq_driver_sem);
memcpy(policy,
&cpufreq_driver->policy[cpu],
sizeof(struct cpufreq_policy));
up(&cpufreq_driver_sem);
cpufreq_cpu_put(cpu);
return 0;
}
EXPORT_SYMBOL(cpufreq_get_policy);
......@@ -646,27 +683,23 @@ EXPORT_SYMBOL(cpufreq_get_policy);
*/
int cpufreq_set_policy(struct cpufreq_policy *policy)
{
int ret;
int ret = 0;
down(&cpufreq_driver_sem);
if (!cpufreq_driver || !policy ||
(policy->cpu >= NR_CPUS) || (!cpu_online(policy->cpu))) {
up(&cpufreq_driver_sem);
if (!policy || !cpufreq_cpu_get(policy->cpu))
return -EINVAL;
}
down(&cpufreq_driver_sem);
memcpy(&policy->cpuinfo,
&cpufreq_driver->policy[policy->cpu].cpuinfo,
sizeof(struct cpufreq_cpuinfo));
up(&cpufreq_driver_sem);
/* verify the cpu speed can be set within this limit */
ret = cpufreq_driver->verify(policy);
if (ret) {
up(&cpufreq_driver_sem);
return ret;
}
if (ret)
goto error_out;
down(&cpufreq_notifier_sem);
down_read(&cpufreq_notifier_rwsem);
/* adjust if necessary - all reasons */
notifier_call_chain(&cpufreq_policy_notifier_list, CPUFREQ_ADJUST,
......@@ -680,17 +713,18 @@ int cpufreq_set_policy(struct cpufreq_policy *policy)
which might be different to the first one */
ret = cpufreq_driver->verify(policy);
if (ret) {
up(&cpufreq_notifier_sem);
up(&cpufreq_driver_sem);
return ret;
up_read(&cpufreq_notifier_rwsem);
goto error_out;
}
/* notification of the new policy */
notifier_call_chain(&cpufreq_policy_notifier_list, CPUFREQ_NOTIFY,
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].max = policy->max;
......@@ -718,9 +752,11 @@ int cpufreq_set_policy(struct cpufreq_policy *policy)
cpufreq_governor(policy->cpu, CPUFREQ_GOV_LIMITS);
}
}
up(&cpufreq_driver_sem);
error_out:
cpufreq_cpu_put(policy->cpu);
return ret;
}
EXPORT_SYMBOL(cpufreq_set_policy);
......@@ -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)
{
down(&cpufreq_notifier_sem);
down_read(&cpufreq_notifier_rwsem);
switch (state) {
case CPUFREQ_PRECHANGE:
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)
cpufreq_driver->policy[freqs->cpu].cur = freqs->new;
break;
}
up(&cpufreq_notifier_sem);
up_read(&cpufreq_notifier_rwsem);
}
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 ret = 0;
if (cpufreq_driver)
return -EBUSY;
if (!driver_data || !driver_data->verify ||
if (!driver_data || !driver_data->verify || !driver_data->init ||
((!driver_data->setpolicy) && (!driver_data->target)))
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;
up(&cpufreq_driver_sem);
cpufreq_driver->policy = kmalloc(NR_CPUS * sizeof(struct cpufreq_policy), GFP_KERNEL);
if (!cpufreq_driver->policy) {
/* then we need per-CPU init */
if (!cpufreq_driver->init) {
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));
cpufreq_driver = NULL;
return -ENOMEM;
}
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);
......@@ -846,20 +877,20 @@ EXPORT_SYMBOL_GPL(cpufreq_register_driver);
*/
int cpufreq_unregister_driver(struct cpufreq_driver *driver)
{
down(&cpufreq_driver_sem);
if (!kset_get(&cpufreq_interface.kset))
return 0;
if (!cpufreq_driver ||
((driver != cpufreq_driver) && (driver != NULL))) { /* compat */
up(&cpufreq_driver_sem);
if (!cpufreq_driver || (driver != cpufreq_driver)) {
kset_put(&cpufreq_interface.kset);
return -EINVAL;
}
kset_put(&cpufreq_interface.kset);
interface_unregister(&cpufreq_interface);
if (driver)
kfree(cpufreq_driver->policy);
down(&cpufreq_driver_sem);
kfree(cpufreq_driver->policy);
cpufreq_driver = NULL;
up(&cpufreq_driver_sem);
return 0;
......@@ -883,22 +914,28 @@ int cpufreq_restore(void)
if (in_interrupt())
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++) {
if (!cpu_online(i))
if (!cpu_online(i) || !cpufreq_cpu_get(i))
continue;
down(&cpufreq_driver_sem);
if (!cpufreq_driver) {
up(&cpufreq_driver_sem);
return 0;
}
memcpy(&policy, &cpufreq_driver->policy[i], sizeof(struct cpufreq_policy));
up(&cpufreq_driver_sem);
ret += cpufreq_set_policy(&policy);
cpufreq_cpu_put(i);
}
module_put(cpufreq_driver->owner);
error_out:
kset_put(&cpufreq_interface.kset);
return ret;
}
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