Commit 405c230a authored by Dominik Brodowski's avatar Dominik Brodowski Committed by Linus Torvalds

[PATCH] cpufreq (3/5): "userspace" governor

The old /proc/sys/cpu/ - based interface is nothing else than a
cpufreq governor which decides what frequency to use within a policy
based on userspace input. So, convert this interface (and add a
corresponding sysfs file, /sys/devices/sys/cpu0/scaling_setspeed) to
become a cpufreq governor, and move it out to an extra module (which
partly explains the size of this patch).
parent 3537d161
......@@ -9,3 +9,30 @@ config CPU_FREQ_PROC_INTF
For details, take a look at linux/Documentation/cpufreq.
If in doubt, say N.
config CPU_FREQ_GOV_USERSPACE
tristate "'userspace' governor for userspace frequency scaling"
depends on CPU_FREQ
help
Enable this cpufreq governor when you either want to set the
CPU frequency manually or when an userspace programm shall
be able to set the CPU dynamically, like on LART
( http://www.lart.tudelft.nl/ )
For details, take a look at linux/Documentation/cpufreq.
If in doubt, say Y.
config CPU_FREQ_24_API
bool "/proc/sys/cpu/ interface (2.4. / OLD)"
depends on CPU_FREQ && SYSCTL && CPU_FREQ_GOV_USERSPACE
help
This enables the /proc/sys/cpu/ sysctl interface for controlling
the CPUFreq,"userspace" governor. This is the same interface
as known from the.4.-kernel patches for CPUFreq, and offers
the same functionality as long as "userspace" is the
selected governor for the specified CPU.
For details, take a look at linux/Documentation/cpufreq.
If in doubt, say N.
#CPUfreq governors and cross-arch helpers
obj-$(CONFIG_CPU_FREQ_TABLE) += freq_table.o
obj-$(CONFIG_CPU_FREQ_PROC_INTF) += proc_intf.o
obj-$(CONFIG_CPU_FREQ_GOV_USERSPACE) += userspace.o
/*
* drivers/cpufreq/userspace.c
*
* Copyright (C) 2001 Russell King
* (C) 2002 - 2003 Dominik Brodowski <linux@brodo.de>
*
* $Id:$
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/smp.h>
#include <linux/init.h>
#include <linux/spinlock.h>
#include <linux/interrupt.h>
#include <linux/ctype.h>
#include <linux/cpufreq.h>
#include <linux/sysctl.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <asm/uaccess.h>
#define CTL_CPU_VARS_SPEED_MAX(cpunr) { \
.ctl_name = CPU_NR_FREQ_MAX, \
.data = &cpu_max_freq[cpunr], \
.procname = "speed-max", \
.maxlen = sizeof(cpu_max_freq[cpunr]),\
.mode = 0444, \
.proc_handler = proc_dointvec, }
#define CTL_CPU_VARS_SPEED_MIN(cpunr) { \
.ctl_name = CPU_NR_FREQ_MIN, \
.data = &cpu_min_freq[cpunr], \
.procname = "speed-min", \
.maxlen = sizeof(cpu_min_freq[cpunr]),\
.mode = 0444, \
.proc_handler = proc_dointvec, }
#define CTL_CPU_VARS_SPEED(cpunr) { \
.ctl_name = CPU_NR_FREQ, \
.procname = "speed", \
.mode = 0644, \
.proc_handler = cpufreq_procctl, \
.strategy = cpufreq_sysctl, \
.extra1 = (void*) (cpunr), }
#define CTL_TABLE_CPU_VARS(cpunr) static ctl_table ctl_cpu_vars_##cpunr[] = {\
CTL_CPU_VARS_SPEED_MAX(cpunr), \
CTL_CPU_VARS_SPEED_MIN(cpunr), \
CTL_CPU_VARS_SPEED(cpunr), \
{ .ctl_name = 0, }, }
/* the ctl_table entry for each CPU */
#define CPU_ENUM(s) { \
.ctl_name = (CPU_NR + s), \
.procname = #s, \
.mode = 0555, \
.child = ctl_cpu_vars_##s }
/**
* A few values needed by the userspace governor
*/
static unsigned int cpu_max_freq[NR_CPUS];
static unsigned int cpu_min_freq[NR_CPUS];
static unsigned int cpu_cur_freq[NR_CPUS];
static unsigned int cpu_is_managed[NR_CPUS];
static struct cpufreq_policy current_policy[NR_CPUS];
static DECLARE_MUTEX (userspace_sem);
/* keep track of frequency transitions */
static int
userspace_cpufreq_notifier(struct notifier_block *nb, unsigned long val,
void *data)
{
struct cpufreq_freqs *freq = data;
cpu_cur_freq[freq->cpu] = freq->new;
return 0;
}
static struct notifier_block userspace_cpufreq_notifier_block = {
.notifier_call = userspace_cpufreq_notifier
};
/**
* cpufreq_set - set the CPU frequency
* @freq: target frequency in kHz
* @cpu: CPU for which the frequency is to be set
*
* Sets the CPU frequency to freq.
*/
int cpufreq_set(unsigned int freq, unsigned int cpu)
{
int ret = -EINVAL;
down(&userspace_sem);
if (!cpu_is_managed[cpu])
goto err;
if (freq < cpu_min_freq[cpu])
freq = cpu_min_freq[cpu];
if (freq > cpu_max_freq[cpu])
freq = cpu_max_freq[cpu];
ret = cpufreq_driver_target_l(&current_policy[cpu], freq,
CPUFREQ_RELATION_L);
err:
up(&userspace_sem);
return ret;
}
EXPORT_SYMBOL_GPL(cpufreq_set);
/**
* cpufreq_setmax - set the CPU to the maximum frequency
* @cpu - affected cpu;
*
* Sets the CPU frequency to the maximum frequency supported by
* this CPU.
*/
int cpufreq_setmax(unsigned int cpu)
{
if (!cpu_is_managed[cpu] || !cpu_online(cpu))
return -EINVAL;
return cpufreq_set(cpu_max_freq[cpu], cpu);
}
EXPORT_SYMBOL_GPL(cpufreq_setmax);
/**
* cpufreq_get - get the current CPU frequency (in kHz)
* @cpu: CPU number
*
* Get the CPU current (static) CPU frequency
*/
unsigned int cpufreq_get(unsigned int cpu)
{
return cpu_cur_freq[cpu];
}
EXPORT_SYMBOL(cpufreq_get);
#ifdef CONFIG_CPU_FREQ_24_API
/*********************** cpufreq_sysctl interface ********************/
static int
cpufreq_procctl(ctl_table *ctl, int write, struct file *filp,
void *buffer, size_t *lenp)
{
char buf[16], *p;
int cpu = (int) ctl->extra1;
int len, left = *lenp;
if (!left || (filp->f_pos && !write) || !cpu_online(cpu)) {
*lenp = 0;
return 0;
}
if (write) {
unsigned int freq;
len = left;
if (left > sizeof(buf))
left = sizeof(buf);
if (copy_from_user(buf, buffer, left))
return -EFAULT;
buf[sizeof(buf) - 1] = '\0';
freq = simple_strtoul(buf, &p, 0);
cpufreq_set(freq, cpu);
} else {
len = sprintf(buf, "%d\n", cpufreq_get(cpu));
if (len > left)
len = left;
if (copy_to_user(buffer, buf, len))
return -EFAULT;
}
*lenp = len;
filp->f_pos += len;
return 0;
}
static int
cpufreq_sysctl(ctl_table *table, int *name, int nlen,
void *oldval, size_t *oldlenp,
void *newval, size_t newlen, void **context)
{
int cpu = (int) table->extra1;
if (!cpu_online(cpu))
return -EINVAL;
if (oldval && oldlenp) {
size_t oldlen;
if (get_user(oldlen, oldlenp))
return -EFAULT;
if (oldlen != sizeof(unsigned int))
return -EINVAL;
if (put_user(cpufreq_get(cpu), (unsigned int *)oldval) ||
put_user(sizeof(unsigned int), oldlenp))
return -EFAULT;
}
if (newval && newlen) {
unsigned int freq;
if (newlen != sizeof(unsigned int))
return -EINVAL;
if (get_user(freq, (unsigned int *)newval))
return -EFAULT;
cpufreq_set(freq, cpu);
}
return 1;
}
/* ctl_table ctl_cpu_vars_{0,1,...,(NR_CPUS-1)} */
/* due to NR_CPUS tweaking, a lot of if/endifs are required, sorry */
CTL_TABLE_CPU_VARS(0);
#if NR_CPUS > 1
CTL_TABLE_CPU_VARS(1);
#endif
#if NR_CPUS > 2
CTL_TABLE_CPU_VARS(2);
#endif
#if NR_CPUS > 3
CTL_TABLE_CPU_VARS(3);
#endif
#if NR_CPUS > 4
CTL_TABLE_CPU_VARS(4);
#endif
#if NR_CPUS > 5
CTL_TABLE_CPU_VARS(5);
#endif
#if NR_CPUS > 6
CTL_TABLE_CPU_VARS(6);
#endif
#if NR_CPUS > 7
CTL_TABLE_CPU_VARS(7);
#endif
#if NR_CPUS > 8
CTL_TABLE_CPU_VARS(8);
#endif
#if NR_CPUS > 9
CTL_TABLE_CPU_VARS(9);
#endif
#if NR_CPUS > 10
CTL_TABLE_CPU_VARS(10);
#endif
#if NR_CPUS > 11
CTL_TABLE_CPU_VARS(11);
#endif
#if NR_CPUS > 12
CTL_TABLE_CPU_VARS(12);
#endif
#if NR_CPUS > 13
CTL_TABLE_CPU_VARS(13);
#endif
#if NR_CPUS > 14
CTL_TABLE_CPU_VARS(14);
#endif
#if NR_CPUS > 15
CTL_TABLE_CPU_VARS(15);
#endif
#if NR_CPUS > 16
CTL_TABLE_CPU_VARS(16);
#endif
#if NR_CPUS > 17
CTL_TABLE_CPU_VARS(17);
#endif
#if NR_CPUS > 18
CTL_TABLE_CPU_VARS(18);
#endif
#if NR_CPUS > 19
CTL_TABLE_CPU_VARS(19);
#endif
#if NR_CPUS > 20
CTL_TABLE_CPU_VARS(20);
#endif
#if NR_CPUS > 21
CTL_TABLE_CPU_VARS(21);
#endif
#if NR_CPUS > 22
CTL_TABLE_CPU_VARS(22);
#endif
#if NR_CPUS > 23
CTL_TABLE_CPU_VARS(23);
#endif
#if NR_CPUS > 24
CTL_TABLE_CPU_VARS(24);
#endif
#if NR_CPUS > 25
CTL_TABLE_CPU_VARS(25);
#endif
#if NR_CPUS > 26
CTL_TABLE_CPU_VARS(26);
#endif
#if NR_CPUS > 27
CTL_TABLE_CPU_VARS(27);
#endif
#if NR_CPUS > 28
CTL_TABLE_CPU_VARS(28);
#endif
#if NR_CPUS > 29
CTL_TABLE_CPU_VARS(29);
#endif
#if NR_CPUS > 30
CTL_TABLE_CPU_VARS(30);
#endif
#if NR_CPUS > 31
CTL_TABLE_CPU_VARS(31);
#endif
#if NR_CPUS > 32
#error please extend CPU enumeration
#endif
/* due to NR_CPUS tweaking, a lot of if/endifs are required, sorry */
static ctl_table ctl_cpu_table[NR_CPUS + 1] = {
CPU_ENUM(0),
#if NR_CPUS > 1
CPU_ENUM(1),
#endif
#if NR_CPUS > 2
CPU_ENUM(2),
#endif
#if NR_CPUS > 3
CPU_ENUM(3),
#endif
#if NR_CPUS > 4
CPU_ENUM(4),
#endif
#if NR_CPUS > 5
CPU_ENUM(5),
#endif
#if NR_CPUS > 6
CPU_ENUM(6),
#endif
#if NR_CPUS > 7
CPU_ENUM(7),
#endif
#if NR_CPUS > 8
CPU_ENUM(8),
#endif
#if NR_CPUS > 9
CPU_ENUM(9),
#endif
#if NR_CPUS > 10
CPU_ENUM(10),
#endif
#if NR_CPUS > 11
CPU_ENUM(11),
#endif
#if NR_CPUS > 12
CPU_ENUM(12),
#endif
#if NR_CPUS > 13
CPU_ENUM(13),
#endif
#if NR_CPUS > 14
CPU_ENUM(14),
#endif
#if NR_CPUS > 15
CPU_ENUM(15),
#endif
#if NR_CPUS > 16
CPU_ENUM(16),
#endif
#if NR_CPUS > 17
CPU_ENUM(17),
#endif
#if NR_CPUS > 18
CPU_ENUM(18),
#endif
#if NR_CPUS > 19
CPU_ENUM(19),
#endif
#if NR_CPUS > 20
CPU_ENUM(20),
#endif
#if NR_CPUS > 21
CPU_ENUM(21),
#endif
#if NR_CPUS > 22
CPU_ENUM(22),
#endif
#if NR_CPUS > 23
CPU_ENUM(23),
#endif
#if NR_CPUS > 24
CPU_ENUM(24),
#endif
#if NR_CPUS > 25
CPU_ENUM(25),
#endif
#if NR_CPUS > 26
CPU_ENUM(26),
#endif
#if NR_CPUS > 27
CPU_ENUM(27),
#endif
#if NR_CPUS > 28
CPU_ENUM(28),
#endif
#if NR_CPUS > 29
CPU_ENUM(29),
#endif
#if NR_CPUS > 30
CPU_ENUM(30),
#endif
#if NR_CPUS > 31
CPU_ENUM(31),
#endif
#if NR_CPUS > 32
#error please extend CPU enumeration
#endif
{
.ctl_name = 0,
}
};
static ctl_table ctl_cpu[2] = {
{
.ctl_name = CTL_CPU,
.procname = "cpu",
.mode = 0555,
.child = ctl_cpu_table,
},
{
.ctl_name = 0,
}
};
struct ctl_table_header *cpufreq_sysctl_table;
static inline void cpufreq_sysctl_init(void)
{
cpufreq_sysctl_table = register_sysctl_table(ctl_cpu, 0);
}
static inline void cpufreq_sysctl_exit(void)
{
unregister_sysctl_table(cpufreq_sysctl_table);
}
#else
#define cpufreq_sysctl_init() do {} while(0)
#define cpufreq_sysctl_exit() do {} while(0)
#endif /* CONFIG_CPU_FREQ_24API */
/************************** sysfs interface ************************/
static inline int to_cpu_nr (struct device *dev)
{
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]);
}
static ssize_t
store_speed (struct device *dev, const char *buf, size_t count)
{
unsigned int cpu = to_cpu_nr(dev);
unsigned int freq = 0;
unsigned int ret;
ret = sscanf (buf, "%u", &freq);
if (ret != 1)
return -EINVAL;
cpufreq_set(freq, cpu);
return count;
}
static DEVICE_ATTR(scaling_setspeed, (S_IRUGO | S_IWUSR), show_speed, store_speed);
static int cpufreq_governor_userspace(struct cpufreq_policy *policy,
unsigned int event)
{
unsigned int cpu = policy->cpu;
switch (event) {
case CPUFREQ_GOV_START:
if ((!cpu_online(cpu)) || (!try_module_get(THIS_MODULE)) ||
!policy->cur)
return -EINVAL;
down(&userspace_sem);
cpu_is_managed[cpu] = 1;
cpu_min_freq[cpu] = policy->min;
cpu_max_freq[cpu] = policy->max;
cpu_cur_freq[cpu] = policy->cur;
device_create_file (policy->intf.dev, &dev_attr_scaling_setspeed);
memcpy (&current_policy[cpu], policy, sizeof(struct cpufreq_policy));
up(&userspace_sem);
break;
case CPUFREQ_GOV_STOP:
down(&userspace_sem);
cpu_is_managed[cpu] = 0;
cpu_min_freq[cpu] = 0;
cpu_max_freq[cpu] = 0;
device_remove_file (policy->intf.dev, &dev_attr_scaling_setspeed);
up(&userspace_sem);
module_put(THIS_MODULE);
break;
case CPUFREQ_GOV_LIMITS:
down(&userspace_sem);
cpu_min_freq[cpu] = policy->min;
cpu_max_freq[cpu] = policy->max;
if (policy->max < cpu_cur_freq[cpu])
cpufreq_driver_target(&current_policy[cpu], policy->max,
CPUFREQ_RELATION_H);
else if (policy->min > cpu_cur_freq[cpu])
cpufreq_driver_target(&current_policy[cpu], policy->min,
CPUFREQ_RELATION_L);
memcpy (&current_policy[cpu], policy, sizeof(struct cpufreq_policy));
up(&userspace_sem);
break;
}
return 0;
}
/* on ARM SA1100 we need to rely on the values of cpufreq_get() - because
* of this, cpu_cur_freq[] needs to be set early.
*/
#if defined(CONFIG_ARM) && defined(CONFIG_ARCH_SA1100)
extern unsigned int sa11x0_getspeed(void);
static void cpufreq_sa11x0_compat(void)
{
cpu_cur_freq[0] = sa11x0_getspeed();
}
#else
#define cpufreq_sa11x0_compat() do {} while(0)
#endif
static struct cpufreq_governor cpufreq_gov_userspace = {
.name = "userspace",
.governor = cpufreq_governor_userspace,
.owner = THIS_MODULE,
};
EXPORT_SYMBOL(cpufreq_gov_userspace);
static int already_init = 0;
int cpufreq_gov_userspace_init(void)
{
if (!already_init) {
down(&userspace_sem);
cpufreq_sa11x0_compat();
cpufreq_sysctl_init();
cpufreq_register_notifier(&userspace_cpufreq_notifier_block, CPUFREQ_TRANSITION_NOTIFIER);
already_init = 1;
up(&userspace_sem);
}
return cpufreq_register_governor(&cpufreq_gov_userspace);
}
EXPORT_SYMBOL(cpufreq_gov_userspace_init);
static void __exit cpufreq_gov_userspace_exit(void)
{
cpufreq_unregister_governor(&cpufreq_gov_userspace);
cpufreq_unregister_notifier(&userspace_cpufreq_notifier_block, CPUFREQ_TRANSITION_NOTIFIER);
cpufreq_sysctl_exit();
}
MODULE_AUTHOR ("Dominik Brodowski <linux@brodo.de>, Russell King <rmk@arm.linux.org.uk>");
MODULE_DESCRIPTION ("CPUfreq policy governor 'userspace'");
MODULE_LICENSE ("GPL");
module_init(cpufreq_gov_userspace_init);
module_exit(cpufreq_gov_userspace_exit);
......@@ -62,6 +62,8 @@ struct cpufreq_policy {
unsigned int cpu; /* cpu nr or CPUFREQ_ALL_CPUS */
unsigned int min; /* in kHz */
unsigned int max; /* in kHz */
unsigned int cur; /* in kHz, only needed if cpufreq
* governors are used */
unsigned int policy; /* see above */
struct cpufreq_governor *governor; /* see below */
struct cpufreq_cpuinfo cpuinfo; /* see above */
......@@ -164,10 +166,6 @@ struct cpufreq_driver {
/* optional, for the moment */
int (*init) (struct cpufreq_policy *policy);
int (*exit) (struct cpufreq_policy *policy);
/* 2.4. compatible API */
#ifdef CONFIG_CPU_FREQ_24_API
unsigned int cpu_cur_freq[NR_CPUS];
#endif
};
int cpufreq_register_driver(struct cpufreq_driver *driver_data);
......@@ -208,14 +206,19 @@ int cpufreq_restore(void);
/* the proc_intf.c needs this */
int cpufreq_parse_governor (char *str_governor, unsigned int *policy, struct cpufreq_governor **governor);
#ifdef CONFIG_CPU_FREQ_24_API
#if defined(CONFIG_CPU_FREQ_GOV_USERSPACE) || defined(CONFIG_CPU_FREQ_GOV_USERSPACE_MODULE)
/*********************************************************************
* CPUFREQ 2.4. INTERFACE *
* CPUFREQ USERSPACE GOVERNOR *
*********************************************************************/
extern struct cpufreq_governor cpufreq_gov_userspace;
int cpufreq_gov_userspace_init(void);
int cpufreq_setmax(unsigned int cpu);
int cpufreq_set(unsigned int kHz, unsigned int cpu);
unsigned int cpufreq_get(unsigned int cpu);
#ifdef CONFIG_CPU_FREQ_24_API
/* /proc/sys/cpu */
enum {
CPU_NR = 1, /* compatibilty reasons */
......@@ -260,45 +263,10 @@ enum {
CPU_NR_FREQ = 3,
};
#define CTL_CPU_VARS_SPEED_MAX(cpunr) { \
.ctl_name = CPU_NR_FREQ_MAX, \
.data = &cpu_max_freq[cpunr], \
.procname = "speed-max", \
.maxlen = sizeof(cpu_max_freq[cpunr]),\
.mode = 0444, \
.proc_handler = proc_dointvec, }
#define CTL_CPU_VARS_SPEED_MIN(cpunr) { \
.ctl_name = CPU_NR_FREQ_MIN, \
.data = &cpu_min_freq[cpunr], \
.procname = "speed-min", \
.maxlen = sizeof(cpu_min_freq[cpunr]),\
.mode = 0444, \
.proc_handler = proc_dointvec, }
#define CTL_CPU_VARS_SPEED(cpunr) { \
.ctl_name = CPU_NR_FREQ, \
.procname = "speed", \
.mode = 0644, \
.proc_handler = cpufreq_procctl, \
.strategy = cpufreq_sysctl, \
.extra1 = (void*) (cpunr), }
#define CTL_TABLE_CPU_VARS(cpunr) static ctl_table ctl_cpu_vars_##cpunr[] = {\
CTL_CPU_VARS_SPEED_MAX(cpunr), \
CTL_CPU_VARS_SPEED_MIN(cpunr), \
CTL_CPU_VARS_SPEED(cpunr), \
{ .ctl_name = 0, }, }
/* the ctl_table entry for each CPU */
#define CPU_ENUM(s) { \
.ctl_name = (CPU_NR + s), \
.procname = #s, \
.mode = 0555, \
.child = ctl_cpu_vars_##s }
#endif /* CONFIG_CPU_FREQ_24_API */
#endif /* CONFIG_CPU_FREQ_GOV_USERSPACE */
/*********************************************************************
* FREQUENCY TABLE HELPERS *
......
......@@ -23,11 +23,6 @@
#include <linux/device.h>
#include <linux/slab.h>
#ifdef CONFIG_CPU_FREQ_24_API
#include <linux/proc_fs.h>
#include <linux/sysctl.h>
#endif
/**
* The "cpufreq driver" - the arch- or hardware-dependend low
* level driver of CPUFreq support, and its locking mutex.
......@@ -51,15 +46,6 @@ static struct notifier_block *cpufreq_transition_notifier_list;
static DECLARE_MUTEX (cpufreq_notifier_sem);
#ifdef CONFIG_CPU_FREQ_24_API
/**
* A few values needed by the 2.4.-compatible API
*/
static unsigned int cpu_max_freq[NR_CPUS];
static unsigned int cpu_min_freq[NR_CPUS];
static unsigned int cpu_cur_freq[NR_CPUS];
#endif
LIST_HEAD(cpufreq_governor_list);
static int cpufreq_governor(unsigned int cpu, unsigned int event);
......@@ -350,13 +336,6 @@ static int cpufreq_add_dev (struct device * dev)
&cpufreq_driver->policy[cpu],
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);
......@@ -424,398 +403,6 @@ static int cpufreq_remove_dev (struct intf_data *intf)
}
/*********************************************************************
* /proc/sys/cpu/ INTERFACE *
*********************************************************************/
#ifdef CONFIG_CPU_FREQ_24_API
/**
* cpufreq_set - set the CPU frequency
* @freq: target frequency in kHz
* @cpu: CPU for which the frequency is to be set
*
* Sets the CPU frequency to freq.
*/
int cpufreq_set(unsigned int freq, unsigned int cpu)
{
struct cpufreq_policy policy;
down(&cpufreq_driver_sem);
if (!cpufreq_driver || !freq || (cpu > NR_CPUS)) {
up(&cpufreq_driver_sem);
return -EINVAL;
}
policy.min = freq;
policy.max = freq;
policy.policy = CPUFREQ_POLICY_POWERSAVE;
policy.cpu = cpu;
up(&cpufreq_driver_sem);
if (policy.cpu == CPUFREQ_ALL_CPUS)
{
unsigned int i;
unsigned int ret = 0;
for (i=0; i<NR_CPUS; i++)
{
policy.cpu = i;
if (cpu_online(i))
ret |= cpufreq_set_policy(&policy);
}
return ret;
}
else
return cpufreq_set_policy(&policy);
}
EXPORT_SYMBOL_GPL(cpufreq_set);
/**
* cpufreq_setmax - set the CPU to the maximum frequency
* @cpu - affected cpu;
*
* Sets the CPU frequency to the maximum frequency supported by
* this CPU.
*/
int cpufreq_setmax(unsigned int cpu)
{
if (!cpu_online(cpu) && (cpu != CPUFREQ_ALL_CPUS))
return -EINVAL;
return cpufreq_set(cpu_max_freq[cpu], cpu);
}
EXPORT_SYMBOL_GPL(cpufreq_setmax);
/**
* cpufreq_get - get the current CPU frequency (in kHz)
* @cpu: CPU number - currently without effect.
*
* Get the CPU current (static) CPU frequency
*/
unsigned int cpufreq_get(unsigned int cpu)
{
if (!cpu_online(cpu))
return -EINVAL;
return cpu_cur_freq[cpu];
}
EXPORT_SYMBOL(cpufreq_get);
#ifdef CONFIG_SYSCTL
/*********************** cpufreq_sysctl interface ********************/
static int
cpufreq_procctl(ctl_table *ctl, int write, struct file *filp,
void *buffer, size_t *lenp)
{
char buf[16], *p;
int cpu = (int) ctl->extra1;
int len, left = *lenp;
if (!left || (filp->f_pos && !write) || !cpu_online(cpu)) {
*lenp = 0;
return 0;
}
if (write) {
unsigned int freq;
len = left;
if (left > sizeof(buf))
left = sizeof(buf);
if (copy_from_user(buf, buffer, left))
return -EFAULT;
buf[sizeof(buf) - 1] = '\0';
freq = simple_strtoul(buf, &p, 0);
cpufreq_set(freq, cpu);
} else {
len = sprintf(buf, "%d\n", cpufreq_get(cpu));
if (len > left)
len = left;
if (copy_to_user(buffer, buf, len))
return -EFAULT;
}
*lenp = len;
filp->f_pos += len;
return 0;
}
static int
cpufreq_sysctl(ctl_table *table, int *name, int nlen,
void *oldval, size_t *oldlenp,
void *newval, size_t newlen, void **context)
{
int cpu = (int) table->extra1;
if (!cpu_online(cpu))
return -EINVAL;
if (oldval && oldlenp) {
size_t oldlen;
if (get_user(oldlen, oldlenp))
return -EFAULT;
if (oldlen != sizeof(unsigned int))
return -EINVAL;
if (put_user(cpufreq_get(cpu), (unsigned int *)oldval) ||
put_user(sizeof(unsigned int), oldlenp))
return -EFAULT;
}
if (newval && newlen) {
unsigned int freq;
if (newlen != sizeof(unsigned int))
return -EINVAL;
if (get_user(freq, (unsigned int *)newval))
return -EFAULT;
cpufreq_set(freq, cpu);
}
return 1;
}
/* ctl_table ctl_cpu_vars_{0,1,...,(NR_CPUS-1)} */
/* due to NR_CPUS tweaking, a lot of if/endifs are required, sorry */
CTL_TABLE_CPU_VARS(0);
#if NR_CPUS > 1
CTL_TABLE_CPU_VARS(1);
#endif
#if NR_CPUS > 2
CTL_TABLE_CPU_VARS(2);
#endif
#if NR_CPUS > 3
CTL_TABLE_CPU_VARS(3);
#endif
#if NR_CPUS > 4
CTL_TABLE_CPU_VARS(4);
#endif
#if NR_CPUS > 5
CTL_TABLE_CPU_VARS(5);
#endif
#if NR_CPUS > 6
CTL_TABLE_CPU_VARS(6);
#endif
#if NR_CPUS > 7
CTL_TABLE_CPU_VARS(7);
#endif
#if NR_CPUS > 8
CTL_TABLE_CPU_VARS(8);
#endif
#if NR_CPUS > 9
CTL_TABLE_CPU_VARS(9);
#endif
#if NR_CPUS > 10
CTL_TABLE_CPU_VARS(10);
#endif
#if NR_CPUS > 11
CTL_TABLE_CPU_VARS(11);
#endif
#if NR_CPUS > 12
CTL_TABLE_CPU_VARS(12);
#endif
#if NR_CPUS > 13
CTL_TABLE_CPU_VARS(13);
#endif
#if NR_CPUS > 14
CTL_TABLE_CPU_VARS(14);
#endif
#if NR_CPUS > 15
CTL_TABLE_CPU_VARS(15);
#endif
#if NR_CPUS > 16
CTL_TABLE_CPU_VARS(16);
#endif
#if NR_CPUS > 17
CTL_TABLE_CPU_VARS(17);
#endif
#if NR_CPUS > 18
CTL_TABLE_CPU_VARS(18);
#endif
#if NR_CPUS > 19
CTL_TABLE_CPU_VARS(19);
#endif
#if NR_CPUS > 20
CTL_TABLE_CPU_VARS(20);
#endif
#if NR_CPUS > 21
CTL_TABLE_CPU_VARS(21);
#endif
#if NR_CPUS > 22
CTL_TABLE_CPU_VARS(22);
#endif
#if NR_CPUS > 23
CTL_TABLE_CPU_VARS(23);
#endif
#if NR_CPUS > 24
CTL_TABLE_CPU_VARS(24);
#endif
#if NR_CPUS > 25
CTL_TABLE_CPU_VARS(25);
#endif
#if NR_CPUS > 26
CTL_TABLE_CPU_VARS(26);
#endif
#if NR_CPUS > 27
CTL_TABLE_CPU_VARS(27);
#endif
#if NR_CPUS > 28
CTL_TABLE_CPU_VARS(28);
#endif
#if NR_CPUS > 29
CTL_TABLE_CPU_VARS(29);
#endif
#if NR_CPUS > 30
CTL_TABLE_CPU_VARS(30);
#endif
#if NR_CPUS > 31
CTL_TABLE_CPU_VARS(31);
#endif
#if NR_CPUS > 32
#error please extend CPU enumeration
#endif
/* due to NR_CPUS tweaking, a lot of if/endifs are required, sorry */
static ctl_table ctl_cpu_table[NR_CPUS + 1] = {
CPU_ENUM(0),
#if NR_CPUS > 1
CPU_ENUM(1),
#endif
#if NR_CPUS > 2
CPU_ENUM(2),
#endif
#if NR_CPUS > 3
CPU_ENUM(3),
#endif
#if NR_CPUS > 4
CPU_ENUM(4),
#endif
#if NR_CPUS > 5
CPU_ENUM(5),
#endif
#if NR_CPUS > 6
CPU_ENUM(6),
#endif
#if NR_CPUS > 7
CPU_ENUM(7),
#endif
#if NR_CPUS > 8
CPU_ENUM(8),
#endif
#if NR_CPUS > 9
CPU_ENUM(9),
#endif
#if NR_CPUS > 10
CPU_ENUM(10),
#endif
#if NR_CPUS > 11
CPU_ENUM(11),
#endif
#if NR_CPUS > 12
CPU_ENUM(12),
#endif
#if NR_CPUS > 13
CPU_ENUM(13),
#endif
#if NR_CPUS > 14
CPU_ENUM(14),
#endif
#if NR_CPUS > 15
CPU_ENUM(15),
#endif
#if NR_CPUS > 16
CPU_ENUM(16),
#endif
#if NR_CPUS > 17
CPU_ENUM(17),
#endif
#if NR_CPUS > 18
CPU_ENUM(18),
#endif
#if NR_CPUS > 19
CPU_ENUM(19),
#endif
#if NR_CPUS > 20
CPU_ENUM(20),
#endif
#if NR_CPUS > 21
CPU_ENUM(21),
#endif
#if NR_CPUS > 22
CPU_ENUM(22),
#endif
#if NR_CPUS > 23
CPU_ENUM(23),
#endif
#if NR_CPUS > 24
CPU_ENUM(24),
#endif
#if NR_CPUS > 25
CPU_ENUM(25),
#endif
#if NR_CPUS > 26
CPU_ENUM(26),
#endif
#if NR_CPUS > 27
CPU_ENUM(27),
#endif
#if NR_CPUS > 28
CPU_ENUM(28),
#endif
#if NR_CPUS > 29
CPU_ENUM(29),
#endif
#if NR_CPUS > 30
CPU_ENUM(30),
#endif
#if NR_CPUS > 31
CPU_ENUM(31),
#endif
#if NR_CPUS > 32
#error please extend CPU enumeration
#endif
{
.ctl_name = 0,
}
};
static ctl_table ctl_cpu[2] = {
{
.ctl_name = CTL_CPU,
.procname = "cpu",
.mode = 0555,
.child = ctl_cpu_table,
},
{
.ctl_name = 0,
}
};
struct ctl_table_header *cpufreq_sysctl_table;
static inline void cpufreq_sysctl_init(void)
{
cpufreq_sysctl_table = register_sysctl_table(ctl_cpu, 0);
}
static inline void cpufreq_sysctl_exit(void)
{
unregister_sysctl_table(cpufreq_sysctl_table);
}
#else
#define cpufreq_sysctl_init() do {} while(0)
#define cpufreq_sysctl_exit() do {} while(0)
#endif /* CONFIG_SYSCTL */
#endif /* CONFIG_CPU_FREQ_24_API */
/*********************************************************************
* NOTIFIER LISTS INTERFACE *
*********************************************************************/
......@@ -1104,10 +691,6 @@ int cpufreq_set_policy(struct cpufreq_policy *policy)
cpufreq_driver->policy[policy->cpu].min = policy->min;
cpufreq_driver->policy[policy->cpu].max = policy->max;
#ifdef CONFIG_CPU_FREQ_24_API
cpu_cur_freq[policy->cpu] = policy->max;
#endif
if (cpufreq_driver->setpolicy) {
cpufreq_driver->policy[policy->cpu].policy = policy->policy;
ret = cpufreq_driver->setpolicy(policy);
......@@ -1189,9 +772,7 @@ void cpufreq_notify_transition(struct cpufreq_freqs *freqs, unsigned int state)
case CPUFREQ_POSTCHANGE:
adjust_jiffies(CPUFREQ_POSTCHANGE, freqs);
notifier_call_chain(&cpufreq_transition_notifier_list, CPUFREQ_POSTCHANGE, freqs);
#ifdef CONFIG_CPU_FREQ_24_API
cpu_cur_freq[freqs->cpu] = freqs->new;
#endif
cpufreq_driver->policy[freqs->cpu].cur = freqs->new;
break;
}
up(&cpufreq_notifier_sem);
......@@ -1245,10 +826,6 @@ int cpufreq_register_driver(struct cpufreq_driver *driver_data)
up(&cpufreq_driver_sem);
#ifdef CONFIG_CPU_FREQ_24_API
cpufreq_sysctl_init();
#endif
ret = interface_register(&cpufreq_interface);
return ret;
......@@ -1274,10 +851,6 @@ int cpufreq_unregister_driver(struct cpufreq_driver *driver)
return -EINVAL;
}
#ifdef CONFIG_CPU_FREQ_24_API
cpufreq_sysctl_exit();
#endif
/* remove this workaround as soon as interface_add_data works */
{
unsigned int i;
......
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