Commit ba420e28 authored by Dave Jones's avatar Dave Jones

Merge delerium.kernelslacker.org:/mnt/data/src/bk/bk-linus

into delerium.kernelslacker.org:/mnt/data/src/bk/cpufreq
parents 812ab7f3 c24a8e84
......@@ -35,10 +35,10 @@ Mailing List
------------
There is a CPU frequency changing CVS commit and general list where
you can report bugs, problems or submit patches. To post a message,
send an email to cpufreq@www.linux.org.uk, to subscribe go to
http://www.linux.org.uk/mailman/listinfo/cpufreq. Previous post to the
send an email to cpufreq@lists.linux.org.uk, to subscribe go to
http://lists.linux.org.uk/mailman/listinfo/cpufreq. Previous post to the
mailing list are available to subscribers at
http://www.linux.org.uk/mailman/private/cpufreq/.
http://lists.linux.org.uk/mailman/private/cpufreq/.
Links
......@@ -50,7 +50,7 @@ how to access the CVS repository:
* http://cvs.arm.linux.org.uk/
the CPUFreq Mailing list:
* http://www.linux.org.uk/mailman/listinfo/cpufreq
* http://lists.linux.org.uk/mailman/listinfo/cpufreq
Clock and voltage scaling for the SA-1100:
* http://www.lart.tudelft.nl/projects/scaling
......@@ -575,7 +575,7 @@ S: Maintained
CPU FREQUENCY DRIVERS
P: Dave Jones
M: davej@codemonkey.org.uk
L: cpufreq@www.linux.org.uk
L: cpufreq@lists.linux.org.uk
W: http://www.codemonkey.org.uk/projects/cpufreq/
S: Maintained
......
......@@ -38,6 +38,8 @@
#include <linux/acpi.h>
#include <acpi/processor.h>
#include "speedstep-est-common.h"
#define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_DRIVER, "acpi-cpufreq", msg)
MODULE_AUTHOR("Paul Diefenbaugh, Dominik Brodowski");
......@@ -48,10 +50,12 @@ MODULE_LICENSE("GPL");
struct cpufreq_acpi_io {
struct acpi_processor_performance acpi_data;
struct cpufreq_frequency_table *freq_table;
unsigned int resume;
};
static struct cpufreq_acpi_io *acpi_io_data[NR_CPUS];
static struct cpufreq_driver acpi_cpufreq_driver;
static int
acpi_processor_write_port(
......@@ -119,10 +123,15 @@ acpi_processor_set_performance (
}
if (state == data->acpi_data.state) {
if (unlikely(data->resume)) {
dprintk("Called after resume, resetting to P%d\n", state);
data->resume = 0;
} else {
dprintk("Already at target state (P%d)\n", state);
retval = 0;
goto migrate_end;
}
}
dprintk("Transitioning from P%d to P%d\n",
data->acpi_data.state, state);
......@@ -368,6 +377,10 @@ acpi_cpufreq_cpu_init (
if (result)
goto err_free;
if (is_const_loops_cpu(cpu)) {
acpi_cpufreq_driver.flags |= CPUFREQ_CONST_LOOPS;
}
/* capability check */
if (data->acpi_data.state_count <= 1) {
dprintk("No P-States\n");
......@@ -462,6 +475,20 @@ acpi_cpufreq_cpu_exit (
return (0);
}
static int
acpi_cpufreq_resume (
struct cpufreq_policy *policy)
{
struct cpufreq_acpi_io *data = acpi_io_data[policy->cpu];
dprintk("acpi_cpufreq_resume\n");
data->resume = 1;
return (0);
}
static struct freq_attr* acpi_cpufreq_attr[] = {
&cpufreq_freq_attr_scaling_available_freqs,
......@@ -473,6 +500,7 @@ static struct cpufreq_driver acpi_cpufreq_driver = {
.target = acpi_cpufreq_target,
.init = acpi_cpufreq_cpu_init,
.exit = acpi_cpufreq_cpu_exit,
.resume = acpi_cpufreq_resume,
.name = "acpi-cpufreq",
.owner = THIS_MODULE,
.attr = acpi_cpufreq_attr,
......
......@@ -55,16 +55,7 @@ MODULE_PARM_DESC(fid, "CPU multiplier to use (11.5 = 115)");
MODULE_PARM_DESC(min_fsb,
"Minimum FSB to use, if not defined: current FSB - 50");
/* DEBUG
* Define it if you want verbose debug output, e.g. for bug reporting
*/
//#define NFORCE2_DEBUG
#ifdef NFORCE2_DEBUG
#define dprintk(msg...) printk(msg)
#else
#define dprintk(msg...) do { } while(0)
#endif
#define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_DRIVER, "cpufreq-nforce2", msg)
/*
* nforce2_calc_fsb - calculate FSB
......
......@@ -209,7 +209,7 @@ static unsigned int gx_get_cpuspeed(unsigned int cpu)
if ((gx_params->pci_suscfg & SUSMOD) == 0)
return stock_freq;
return (stock_freq * gx_params->on_duration)
return (stock_freq * gx_params->off_duration)
/ (gx_params->on_duration + gx_params->off_duration);
}
......
......@@ -171,7 +171,7 @@ static unsigned int cpufreq_p4_get_frequency(struct cpuinfo_x86 *c)
return speedstep_get_processor_frequency(SPEEDSTEP_PROCESSOR_PM);
}
if ((c->x86 == 0x06) && (c->x86_model == 0x13)) {
if ((c->x86 == 0x06) && (c->x86_model == 0x0D)) {
/* Pentium M (Dothan) */
printk(KERN_WARNING PFX "Warning: Pentium M detected. "
"The speedstep_centrino module offers voltage scaling"
......
......@@ -635,6 +635,17 @@ static int __init powernow_cpu_init (struct cpufreq_policy *policy)
static int powernow_cpu_exit (struct cpufreq_policy *policy) {
cpufreq_frequency_table_put_attr(policy->cpu);
#ifdef CONFIG_X86_POWERNOW_K7_ACPI
if (acpi_processor_perf) {
acpi_processor_unregister_performance(acpi_processor_perf, 0);
kfree(acpi_processor_perf);
}
#endif
if (powernow_table)
kfree(powernow_table);
return 0;
}
......@@ -664,15 +675,7 @@ static int __init powernow_init (void)
static void __exit powernow_exit (void)
{
#ifdef CONFIG_X86_POWERNOW_K7_ACPI
if (acpi_processor_perf) {
acpi_processor_unregister_performance(acpi_processor_perf, 0);
kfree(acpi_processor_perf);
}
#endif
cpufreq_unregister_driver(&powernow_driver);
if (powernow_table)
kfree(powernow_table);
}
module_param(acpi_force, int, 0444);
......
......@@ -18,6 +18,9 @@
* Processor information obtained from Chapter 9 (Power and Thermal Management)
* of the "BIOS and Kernel Developer's Guide for the AMD Athlon 64 and AMD
* Opteron Processors" available for download from www.amd.com
*
* Tables for specific CPUs can be infrerred from
* http://www.amd.com/us-en/assets/content_type/white_papers_and_tech_docs/30430.pdf
*/
#include <linux/kernel.h>
......@@ -65,7 +68,12 @@ static u32 find_millivolts_from_vid(struct powernow_k8_data *data, u32 vid)
return 1550-vid*25;
}
/* Return the vco fid for an input fid */
/* Return the vco fid for an input fid
*
* Each "low" fid has corresponding "high" fid, and you can get to "low" fids
* only from corresponding high fids. This returns "high" fid corresponding to
* "low" one.
*/
static u32 convert_fid_to_vco_fid(u32 fid)
{
if (fid < HI_FID_TABLE_BOTTOM) {
......@@ -278,7 +286,7 @@ static int core_voltage_pre_transition(struct powernow_k8_data *data, u32 reqvid
return 1;
}
while (rvosteps > 0) {
while ((rvosteps > 0) && ((data->rvo + data->currvid) > reqvid)) {
if (data->currvid == 0) {
rvosteps = 0;
} else {
......@@ -307,10 +315,7 @@ static int core_voltage_pre_transition(struct powernow_k8_data *data, u32 reqvid
/* Phase 2 - core frequency transition */
static int core_frequency_transition(struct powernow_k8_data *data, u32 reqfid)
{
u32 vcoreqfid;
u32 vcocurrfid;
u32 vcofiddiff;
u32 savevid = data->currvid;
u32 vcoreqfid, vcocurrfid, vcofiddiff, savevid = data->currvid;
if ((reqfid < HI_FID_TABLE_BOTTOM) && (data->currfid < HI_FID_TABLE_BOTTOM)) {
printk(KERN_ERR PFX "ph2: illegal lo-lo transition 0x%x 0x%x\n",
......@@ -498,7 +503,7 @@ static int check_pst_table(struct powernow_k8_data *data, struct pst_s *pst, u8
|| (pst[j].fid & 1)
|| (j && (pst[j].fid < HI_FID_TABLE_BOTTOM))) {
/* Only first fid is allowed to be in "low" range */
printk(KERN_ERR PFX "fid %d invalid : 0x%x\n", j, pst[j].fid);
printk(KERN_ERR PFX "two low fids - %d : 0x%x\n", j, pst[j].fid);
return -EINVAL;
}
if (pst[j].fid < lastfid)
......@@ -618,7 +623,7 @@ static int find_psb_table(struct powernow_k8_data *data)
return -ENODEV;
}
data->vstable = psb->voltagestabilizationtime;
data->vstable = psb->vstable;
dprintk("voltage stabilization time: %d(*20us)\n", data->vstable);
dprintk("flags2: 0x%x\n", psb->flags2);
......@@ -632,8 +637,8 @@ static int find_psb_table(struct powernow_k8_data *data)
dprintk("isochronous relief time: %d\n", data->irt);
dprintk("maximum voltage step: %d - 0x%x\n", mvs, data->vidmvs);
dprintk("numpst: 0x%x\n", psb->numpst);
cpst = psb->numpst;
dprintk("numpst: 0x%x\n", psb->num_tables);
cpst = psb->num_tables;
if ((psb->cpuid == 0x00000fc0) || (psb->cpuid == 0x00000fe0) ){
thiscpuid = cpuid_eax(CPUID_PROCESSOR_SIGNATURE);
if ((thiscpuid == 0x00000fc0) || (thiscpuid == 0x00000fe0) ) {
......@@ -651,7 +656,7 @@ static int find_psb_table(struct powernow_k8_data *data)
dprintk("maxvid: 0x%x\n", psb->maxvid);
maxvid = psb->maxvid;
data->numps = psb->numpstates;
data->numps = psb->numps;
dprintk("numpstates: 0x%x\n", data->numps);
return fill_powernow_table(data, (struct pst_s *)(psb+1), maxvid);
}
......@@ -1010,6 +1015,7 @@ static int __init powernowk8_cpu_init(struct cpufreq_policy *pol)
/* min/max the cpu is capable of */
if (cpufreq_frequency_table_cpuinfo(pol, data->powernow_table)) {
printk(KERN_ERR PFX "invalid powernow_table\n");
powernow_k8_cpu_exit_acpi(data);
kfree(data->powernow_table);
kfree(data);
return -EINVAL;
......@@ -1027,6 +1033,7 @@ static int __init powernowk8_cpu_init(struct cpufreq_policy *pol)
err_out:
set_cpus_allowed(current, oldmask);
schedule();
powernow_k8_cpu_exit_acpi(data);
kfree(data);
return -ENODEV;
......
......@@ -21,8 +21,7 @@ struct powernow_k8_data {
u32 plllock; /* pll lock time, units 1 us */
/* keep track of the current fid / vid */
u32 currvid;
u32 currfid;
u32 currvid, currfid;
/* the powernow_table includes all frequency and vid/fid pairings:
* fid are the lower 8 bits of the index, vid are the upper 8 bits.
......@@ -152,14 +151,14 @@ struct psb_s {
u8 signature[10];
u8 tableversion;
u8 flags1;
u16 voltagestabilizationtime;
u16 vstable;
u8 flags2;
u8 numpst;
u8 num_tables;
u32 cpuid;
u8 plllocktime;
u8 maxfid;
u8 maxvid;
u8 numpstates;
u8 numps;
};
/* Pairs of fid/vid values are appended to the version 1.4 PSB table. */
......
/*
* Routines common for drivers handling Enhanced Speedstep Technology
* Copyright (C) 2004 Venkatesh Pallipadi <venkatesh.pallipadi@intel.com>
*
* Licensed under the terms of the GNU GPL License version 2 -- see
* COPYING for details.
*/
static inline int is_const_loops_cpu(unsigned int cpu)
{
struct cpuinfo_x86 *c = cpu_data + cpu;
if (c->x86_vendor != X86_VENDOR_INTEL || !cpu_has(c, X86_FEATURE_EST))
return 0;
/*
* on P-4s, the TSC runs with constant frequency independent of cpu freq
* when we use EST
*/
if (c->x86 == 0xf)
return 1;
return 0;
}
......@@ -27,17 +27,21 @@ config CPU_FREQ_DEBUG
2 to activate CPUfreq drivers debugging, and
4 to activate CPUfreq governor debugging
config CPU_FREQ_PROC_INTF
tristate "/proc/cpufreq interface (deprecated)"
depends on CPU_FREQ && PROC_FS
config CPU_FREQ_STAT
tristate "CPU frequency translation statistics"
depends on CPU_FREQ && CPU_FREQ_TABLE
default y
help
This enables the /proc/cpufreq interface for controlling
CPUFreq. Please note that it is recommended to use the sysfs
interface instead (which is built automatically).
This driver exports CPU frequency statistics information through sysfs
file system
For details, take a look at <file:Documentation/cpu-freq/>.
If in doubt, say N.
config CPU_FREQ_STAT_DETAILS
bool "CPU frequency translation statistics details"
depends on CPU_FREQ && CPU_FREQ_STAT
default n
help
This will show detail CPU frequency translation table in sysfs file
system
choice
prompt "Default CPUFreq governor"
......@@ -98,21 +102,6 @@ config CPU_FREQ_GOV_USERSPACE
If in doubt, say Y.
config CPU_FREQ_24_API
bool "/proc/sys/cpu/ interface (2.4. / OLD)"
depends on CPU_FREQ_GOV_USERSPACE
depends on SYSCTL
help
This enables the /proc/sys/cpu/ sysctl interface for controlling
the CPUFreq,"userspace" governor. This is the same interface
as known from the 2.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 <file:Documentation/cpu-freq/>.
If in doubt, say N.
config CPU_FREQ_GOV_ONDEMAND
tristate "'ondemand' cpufreq policy governor"
depends on CPU_FREQ
......
# CPUfreq core
obj-$(CONFIG_CPU_FREQ) += cpufreq.o
# CPUfreq stats
obj-$(CONFIG_CPU_FREQ_STAT) += cpufreq_stats.o
# CPUfreq governors
obj-$(CONFIG_CPU_FREQ_GOV_PERFORMANCE) += cpufreq_performance.o
......@@ -9,5 +11,4 @@ obj-$(CONFIG_CPU_FREQ_GOV_ONDEMAND) += cpufreq_ondemand.o
# CPUfreq cross-arch helpers
obj-$(CONFIG_CPU_FREQ_TABLE) += freq_table.o
obj-$(CONFIG_CPU_FREQ_PROC_INTF) += proc_intf.o
......@@ -63,7 +63,7 @@ static DECLARE_RWSEM (cpufreq_notifier_rwsem);
static LIST_HEAD(cpufreq_governor_list);
static DECLARE_MUTEX (cpufreq_governor_sem);
static struct cpufreq_policy * cpufreq_cpu_get(unsigned int cpu)
struct cpufreq_policy * cpufreq_cpu_get(unsigned int cpu)
{
struct cpufreq_policy *data;
unsigned long flags;
......@@ -102,12 +102,14 @@ static struct cpufreq_policy * cpufreq_cpu_get(unsigned int cpu)
err_out:
return NULL;
}
EXPORT_SYMBOL_GPL(cpufreq_cpu_get);
static void cpufreq_cpu_put(struct cpufreq_policy *data)
void cpufreq_cpu_put(struct cpufreq_policy *data)
{
kobject_put(&data->kobj);
module_put(cpufreq_driver->owner);
}
EXPORT_SYMBOL_GPL(cpufreq_cpu_put);
/*********************************************************************
......@@ -285,7 +287,7 @@ EXPORT_SYMBOL_GPL(cpufreq_notify_transition);
/**
* cpufreq_parse_governor - parse a governor string
*/
int cpufreq_parse_governor (char *str_governor, unsigned int *policy,
static int cpufreq_parse_governor (char *str_governor, unsigned int *policy,
struct cpufreq_governor **governor)
{
if (!cpufreq_driver)
......@@ -763,8 +765,11 @@ static int cpufreq_remove_dev (struct sys_device * sys_dev)
spin_unlock_irqrestore(&cpufreq_driver_lock, flags);
#endif
down(&data->lock);
if (cpufreq_driver->target)
__cpufreq_governor(data, CPUFREQ_GOV_STOP);
cpufreq_driver->target = NULL;
up(&data->lock);
kobject_unregister(&data->kobj);
......@@ -893,6 +898,13 @@ static int cpufreq_resume(struct sys_device * sysdev)
return 0;
}
if (cpufreq_driver->resume) {
ret = cpufreq_driver->resume(cpu_policy);
printk(KERN_ERR "cpufreq: resume failed in ->resume step on CPU %u\n", cpu_policy->cpu);
cpufreq_cpu_put(cpu_policy);
return (ret);
}
if (!(cpufreq_driver->flags & CPUFREQ_CONST_LOOPS)) {
unsigned int cur_freq = 0;
......@@ -1018,7 +1030,7 @@ int __cpufreq_driver_target(struct cpufreq_policy *policy,
lock_cpu_hotplug();
dprintk("target for CPU %u: %u kHz, relation %u\n", policy->cpu,
target_freq, relation);
if (cpu_online(policy->cpu))
if (cpu_online(policy->cpu) && cpufreq_driver->target)
retval = cpufreq_driver->target(policy, target_freq, relation);
unlock_cpu_hotplug();
return retval;
......
......@@ -229,10 +229,14 @@ static void dbs_check_cpu(int cpu)
static int down_skip[NR_CPUS];
struct cpu_dbs_info_s *this_dbs_info;
struct cpufreq_policy *policy;
unsigned int j;
this_dbs_info = &per_cpu(cpu_dbs_info, cpu);
if (!this_dbs_info->enable)
return;
policy = this_dbs_info->cur_policy;
/*
* The default safe range is 20% to 80%
* Every sampling_rate, we check
......@@ -246,6 +250,7 @@ static void dbs_check_cpu(int cpu)
* Frequency reduction happens at minimum steps of
* 5% of max_frequency
*/
/* Check for frequency increase */
total_idle_ticks = kstat_cpu(cpu).cpustat.idle +
kstat_cpu(cpu).cpustat.iowait;
......@@ -253,14 +258,33 @@ static void dbs_check_cpu(int cpu)
this_dbs_info->prev_cpu_idle_up;
this_dbs_info->prev_cpu_idle_up = total_idle_ticks;
for_each_cpu_mask(j, policy->cpus) {
unsigned int tmp_idle_ticks;
struct cpu_dbs_info_s *j_dbs_info;
if (j == cpu)
continue;
j_dbs_info = &per_cpu(cpu_dbs_info, j);
/* Check for frequency increase */
total_idle_ticks = kstat_cpu(j).cpustat.idle +
kstat_cpu(j).cpustat.iowait;
tmp_idle_ticks = total_idle_ticks -
j_dbs_info->prev_cpu_idle_up;
j_dbs_info->prev_cpu_idle_up = total_idle_ticks;
if (tmp_idle_ticks < idle_ticks)
idle_ticks = tmp_idle_ticks;
}
/* Scale idle ticks by 100 and compare with up and down ticks */
idle_ticks *= 100;
up_idle_ticks = (100 - dbs_tuners_ins.up_threshold) *
sampling_rate_in_HZ(dbs_tuners_ins.sampling_rate);
if (idle_ticks < up_idle_ticks) {
__cpufreq_driver_target(this_dbs_info->cur_policy,
this_dbs_info->cur_policy->max,
__cpufreq_driver_target(policy, policy->max,
CPUFREQ_RELATION_H);
down_skip[cpu] = 0;
this_dbs_info->prev_cpu_idle_down = total_idle_ticks;
......@@ -272,12 +296,34 @@ static void dbs_check_cpu(int cpu)
if (down_skip[cpu] < dbs_tuners_ins.sampling_down_factor)
return;
total_idle_ticks = kstat_cpu(cpu).cpustat.idle +
kstat_cpu(cpu).cpustat.iowait;
idle_ticks = total_idle_ticks -
this_dbs_info->prev_cpu_idle_down;
this_dbs_info->prev_cpu_idle_down = total_idle_ticks;
for_each_cpu_mask(j, policy->cpus) {
unsigned int tmp_idle_ticks;
struct cpu_dbs_info_s *j_dbs_info;
if (j == cpu)
continue;
j_dbs_info = &per_cpu(cpu_dbs_info, j);
/* Check for frequency increase */
total_idle_ticks = kstat_cpu(j).cpustat.idle +
kstat_cpu(j).cpustat.iowait;
tmp_idle_ticks = total_idle_ticks -
j_dbs_info->prev_cpu_idle_down;
j_dbs_info->prev_cpu_idle_down = total_idle_ticks;
if (tmp_idle_ticks < idle_ticks)
idle_ticks = tmp_idle_ticks;
}
/* Scale idle ticks by 100 and compare with up and down ticks */
idle_ticks *= 100;
down_skip[cpu] = 0;
this_dbs_info->prev_cpu_idle_down = total_idle_ticks;
freq_down_sampling_rate = dbs_tuners_ins.sampling_rate *
dbs_tuners_ins.sampling_down_factor;
......@@ -285,14 +331,14 @@ static void dbs_check_cpu(int cpu)
sampling_rate_in_HZ(freq_down_sampling_rate);
if (idle_ticks > down_idle_ticks ) {
freq_down_step = (5 * this_dbs_info->cur_policy->max) / 100;
freq_down_step = (5 * policy->max) / 100;
/* max freq cannot be less than 100. But who knows.... */
if (unlikely(freq_down_step == 0))
freq_down_step = 5;
__cpufreq_driver_target(this_dbs_info->cur_policy,
this_dbs_info->cur_policy->cur - freq_down_step,
__cpufreq_driver_target(policy,
policy->cur - freq_down_step,
CPUFREQ_RELATION_H);
return;
}
......@@ -313,7 +359,8 @@ static void do_dbs_timer(void *data)
static inline void dbs_timer_init(void)
{
INIT_WORK(&dbs_work, do_dbs_timer, NULL);
schedule_work(&dbs_work);
schedule_delayed_work(&dbs_work,
sampling_rate_in_HZ(dbs_tuners_ins.sampling_rate));
return;
}
......@@ -328,6 +375,7 @@ static int cpufreq_governor_dbs(struct cpufreq_policy *policy,
{
unsigned int cpu = policy->cpu;
struct cpu_dbs_info_s *this_dbs_info;
unsigned int j;
this_dbs_info = &per_cpu(cpu_dbs_info, cpu);
......@@ -344,14 +392,18 @@ static int cpufreq_governor_dbs(struct cpufreq_policy *policy,
break;
down(&dbs_sem);
this_dbs_info->cur_policy = policy;
this_dbs_info->prev_cpu_idle_up =
kstat_cpu(cpu).cpustat.idle +
kstat_cpu(cpu).cpustat.iowait;
this_dbs_info->prev_cpu_idle_down =
kstat_cpu(cpu).cpustat.idle +
kstat_cpu(cpu).cpustat.iowait;
for_each_cpu_mask(j, policy->cpus) {
struct cpu_dbs_info_s *j_dbs_info;
j_dbs_info = &per_cpu(cpu_dbs_info, j);
j_dbs_info->cur_policy = policy;
j_dbs_info->prev_cpu_idle_up =
kstat_cpu(j).cpustat.idle +
kstat_cpu(j).cpustat.iowait;
j_dbs_info->prev_cpu_idle_down =
kstat_cpu(j).cpustat.idle +
kstat_cpu(j).cpustat.iowait;
}
this_dbs_info->enable = 1;
sysfs_create_group(&policy->kobj, &dbs_attr_group);
dbs_enable++;
......
/*
* drivers/cpufreq/cpufreq_stats.c
*
* Copyright (C) 2003-2004 Venkatesh Pallipadi <venkatesh.pallipadi@intel.com>.
* (C) 2004 Zou Nan hai <nanhai.zou@intel.com>.
*
* 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/config.h>
#include <linux/kernel.h>
#include <linux/sysdev.h>
#include <linux/cpu.h>
#include <linux/sysfs.h>
#include <linux/cpufreq.h>
#include <linux/jiffies.h>
#include <linux/percpu.h>
#include <linux/kobject.h>
#include <linux/spinlock.h>
static spinlock_t cpufreq_stats_lock;
#define CPUFREQ_STATDEVICE_ATTR(_name,_mode,_show) \
static struct freq_attr _attr_##_name = {\
.attr = {.name = __stringify(_name), .owner = THIS_MODULE, \
.mode = _mode, }, \
.show = _show,\
};
static unsigned long
delta_time(unsigned long old, unsigned long new)
{
return (old > new) ? (old - new): (new + ~old + 1);
}
struct cpufreq_stats {
unsigned int cpu;
unsigned int total_trans;
unsigned long long last_time;
unsigned int max_state;
unsigned int state_num;
unsigned int last_index;
unsigned long long *time_in_state;
unsigned int *freq_table;
#ifdef CONFIG_CPU_FREQ_STAT_DETAILS
unsigned int *trans_table;
#endif
};
static struct cpufreq_stats *cpufreq_stats_table[NR_CPUS];
struct cpufreq_stats_attribute {
struct attribute attr;
ssize_t(*show) (struct cpufreq_stats *, char *);
};
static int
cpufreq_stats_update (unsigned int cpu)
{
struct cpufreq_stats *stat;
spin_lock(&cpufreq_stats_lock);
stat = cpufreq_stats_table[cpu];
if (stat->time_in_state)
stat->time_in_state[stat->last_index] +=
delta_time(stat->last_time, jiffies);
stat->last_time = jiffies;
spin_unlock(&cpufreq_stats_lock);
return 0;
}
static ssize_t
show_total_trans(struct cpufreq_policy *policy, char *buf)
{
struct cpufreq_stats *stat = cpufreq_stats_table[policy->cpu];
if(!stat)
return 0;
return sprintf(buf, "%d\n",
cpufreq_stats_table[stat->cpu]->total_trans);
}
static ssize_t
show_time_in_state(struct cpufreq_policy *policy, char *buf)
{
ssize_t len = 0;
int i;
struct cpufreq_stats *stat = cpufreq_stats_table[policy->cpu];
if(!stat)
return 0;
cpufreq_stats_update(stat->cpu);
for (i = 0; i < stat->state_num; i++) {
len += sprintf(buf + len, "%u %llu\n",
stat->freq_table[i], stat->time_in_state[i]);
}
return len;
}
#ifdef CONFIG_CPU_FREQ_STAT_DETAILS
static ssize_t
show_trans_table(struct cpufreq_policy *policy, char *buf)
{
ssize_t len = 0;
int i, j;
struct cpufreq_stats *stat = cpufreq_stats_table[policy->cpu];
if(!stat)
return 0;
cpufreq_stats_update(stat->cpu);
for (i = 0; i < stat->state_num; i++) {
if (len >= PAGE_SIZE)
break;
len += snprintf(buf + len, PAGE_SIZE - len, "%9u:\t",
stat->freq_table[i]);
for (j = 0; j < stat->state_num; j++) {
if (len >= PAGE_SIZE)
break;
len += snprintf(buf + len, PAGE_SIZE - len, "%u\t",
stat->trans_table[i*stat->max_state+j]);
}
len += snprintf(buf + len, PAGE_SIZE - len, "\n");
}
return len;
}
CPUFREQ_STATDEVICE_ATTR(trans_table,0444,show_trans_table);
#endif
CPUFREQ_STATDEVICE_ATTR(total_trans,0444,show_total_trans);
CPUFREQ_STATDEVICE_ATTR(time_in_state,0444,show_time_in_state);
static struct attribute *default_attrs[] = {
&_attr_total_trans.attr,
&_attr_time_in_state.attr,
#ifdef CONFIG_CPU_FREQ_STAT_DETAILS
&_attr_trans_table.attr,
#endif
NULL
};
static struct attribute_group stats_attr_group = {
.attrs = default_attrs,
.name = "stats"
};
static int
freq_table_get_index(struct cpufreq_stats *stat, unsigned int freq)
{
int index;
for (index = 0; index < stat->max_state; index++)
if (stat->freq_table[index] == freq)
return index;
return -1;
}
static void
cpufreq_stats_free_table (unsigned int cpu)
{
struct cpufreq_stats *stat = cpufreq_stats_table[cpu];
struct cpufreq_policy *policy = cpufreq_cpu_get(cpu);
if (policy && policy->cpu == cpu)
sysfs_remove_group(&policy->kobj, &stats_attr_group);
if (stat) {
kfree(stat->time_in_state);
kfree(stat);
}
cpufreq_stats_table[cpu] = NULL;
if (policy)
cpufreq_cpu_put(policy);
}
static int
cpufreq_stats_create_table (struct cpufreq_policy *policy,
struct cpufreq_frequency_table *table)
{
unsigned int i, j, count = 0, ret = 0;
struct cpufreq_stats *stat;
struct cpufreq_policy *data;
unsigned int alloc_size;
unsigned int cpu = policy->cpu;
if (cpufreq_stats_table[cpu])
return -EBUSY;
if ((stat = kmalloc(sizeof(struct cpufreq_stats), GFP_KERNEL)) == NULL)
return -ENOMEM;
memset(stat, 0, sizeof (struct cpufreq_stats));
data = cpufreq_cpu_get(cpu);
if ((ret = sysfs_create_group(&data->kobj, &stats_attr_group)))
goto error_out;
stat->cpu = cpu;
cpufreq_stats_table[cpu] = stat;
for (i=0; table[i].frequency != CPUFREQ_TABLE_END; i++) {
unsigned int freq = table[i].frequency;
if (freq == CPUFREQ_ENTRY_INVALID)
continue;
count++;
}
alloc_size = count * sizeof(int) + count * sizeof(long long);
#ifdef CONFIG_CPU_FREQ_STAT_DETAILS
alloc_size += count * count * sizeof(int);
#endif
stat->max_state = count;
stat->time_in_state = kmalloc(alloc_size, GFP_KERNEL);
if (!stat->time_in_state) {
ret = -ENOMEM;
goto error_out;
}
memset(stat->time_in_state, 0, alloc_size);
stat->freq_table = (unsigned int *)(stat->time_in_state + count);
#ifdef CONFIG_CPU_FREQ_STAT_DETAILS
stat->trans_table = stat->freq_table + count;
#endif
j = 0;
for (i = 0; table[i].frequency != CPUFREQ_TABLE_END; i++) {
unsigned int freq = table[i].frequency;
if (freq == CPUFREQ_ENTRY_INVALID)
continue;
if (freq_table_get_index(stat, freq) == -1)
stat->freq_table[j++] = freq;
}
stat->state_num = j;
spin_lock(&cpufreq_stats_lock);
stat->last_time = jiffies;
stat->last_index = freq_table_get_index(stat, policy->cur);
spin_unlock(&cpufreq_stats_lock);
cpufreq_cpu_put(data);
return 0;
error_out:
cpufreq_cpu_put(data);
kfree(stat);
cpufreq_stats_table[cpu] = NULL;
return ret;
}
static int
cpufreq_stat_notifier_policy (struct notifier_block *nb, unsigned long val,
void *data)
{
int ret;
struct cpufreq_policy *policy = data;
struct cpufreq_frequency_table *table;
unsigned int cpu = policy->cpu;
if (val != CPUFREQ_NOTIFY)
return 0;
table = cpufreq_frequency_get_table(cpu);
if (!table)
return 0;
if ((ret = cpufreq_stats_create_table(policy, table)))
return ret;
return 0;
}
static int
cpufreq_stat_notifier_trans (struct notifier_block *nb, unsigned long val,
void *data)
{
struct cpufreq_freqs *freq = data;
struct cpufreq_stats *stat;
int old_index, new_index;
if (val != CPUFREQ_POSTCHANGE)
return 0;
stat = cpufreq_stats_table[freq->cpu];
if (!stat)
return 0;
old_index = freq_table_get_index(stat, freq->old);
new_index = freq_table_get_index(stat, freq->new);
cpufreq_stats_update(freq->cpu);
if (old_index == new_index)
return 0;
spin_lock(&cpufreq_stats_lock);
stat->last_index = new_index;
#ifdef CONFIG_CPU_FREQ_STAT_DETAILS
stat->trans_table[old_index * stat->max_state + new_index]++;
#endif
stat->total_trans++;
spin_unlock(&cpufreq_stats_lock);
return 0;
}
static struct notifier_block notifier_policy_block = {
.notifier_call = cpufreq_stat_notifier_policy
};
static struct notifier_block notifier_trans_block = {
.notifier_call = cpufreq_stat_notifier_trans
};
static int
__init cpufreq_stats_init(void)
{
int ret;
unsigned int cpu;
spin_lock_init(&cpufreq_stats_lock);
if ((ret = cpufreq_register_notifier(&notifier_policy_block,
CPUFREQ_POLICY_NOTIFIER)))
return ret;
if ((ret = cpufreq_register_notifier(&notifier_trans_block,
CPUFREQ_TRANSITION_NOTIFIER))) {
cpufreq_unregister_notifier(&notifier_policy_block,
CPUFREQ_POLICY_NOTIFIER);
return ret;
}
for_each_cpu(cpu)
cpufreq_update_policy(cpu);
return 0;
}
static void
__exit cpufreq_stats_exit(void)
{
unsigned int cpu;
cpufreq_unregister_notifier(&notifier_policy_block,
CPUFREQ_POLICY_NOTIFIER);
cpufreq_unregister_notifier(&notifier_trans_block,
CPUFREQ_TRANSITION_NOTIFIER);
for_each_cpu(cpu)
cpufreq_stats_free_table(cpu);
}
MODULE_AUTHOR ("Zou Nan hai <nanhai.zou@intel.com>");
MODULE_DESCRIPTION ("'cpufreq_stats' - A driver to export cpufreq stats through sysfs filesystem");
MODULE_LICENSE ("GPL");
module_init(cpufreq_stats_init);
module_exit(cpufreq_stats_exit);
......@@ -17,51 +17,13 @@
#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 <linux/sysfs.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
......@@ -96,17 +58,17 @@ static struct notifier_block userspace_cpufreq_notifier_block = {
/**
* _cpufreq_set - set the CPU frequency
* 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.
*/
static int _cpufreq_set(unsigned int freq, unsigned int cpu)
static int cpufreq_set(unsigned int freq, unsigned int cpu)
{
int ret = -EINVAL;
dprintk("_cpufreq_set for cpu %u, freq %u kHz\n", cpu, freq);
dprintk("cpufreq_set for cpu %u, freq %u kHz\n", cpu, freq);
down(&userspace_sem);
if (!cpu_is_managed[cpu])
......@@ -135,358 +97,6 @@ static int _cpufreq_set(unsigned int freq, unsigned int cpu)
}
#ifdef CONFIG_CPU_FREQ_24_API
#warning The /proc/sys/cpu/ and sysctl interface to cpufreq will be removed from the 2.6. kernel series soon after 2005-01-01
static unsigned int warning_print = 0;
int __deprecated cpufreq_set(unsigned int freq, unsigned int cpu)
{
return _cpufreq_set(freq, cpu);
}
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 __deprecated 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_sysctl interface ********************/
static int
cpufreq_procctl(ctl_table *ctl, int write, struct file *filp,
void __user *buffer, size_t *lenp, loff_t *ppos)
{
char buf[16], *p;
int cpu = (long) ctl->extra1;
unsigned int len, left = *lenp;
if (!left || (*ppos && !write) || !cpu_online(cpu)) {
*lenp = 0;
return 0;
}
if (!warning_print) {
warning_print++;
printk(KERN_INFO "Access to /proc/sys/cpu/ is deprecated and "
"will be removed from (new) 2.6. kernels soon "
"after 2005-01-01\n");
}
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;
*ppos += len;
return 0;
}
static int
cpufreq_sysctl(ctl_table *table, int __user *name, int nlen,
void __user *oldval, size_t __user *oldlenp,
void __user *newval, size_t newlen, void **context)
{
int cpu = (long) table->extra1;
if (!cpu_online(cpu))
return -EINVAL;
if (!warning_print) {
warning_print++;
printk(KERN_INFO "Access to /proc/sys/cpu/ is deprecated and "
"will be removed from (new) 2.6. kernels soon "
"after 2005-01-01\n");
}
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 __user *)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 __user *)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,
}
};
static 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 ssize_t show_speed (struct cpufreq_policy *policy, char *buf)
{
......@@ -503,7 +113,7 @@ store_speed (struct cpufreq_policy *policy, const char *buf, size_t count)
if (ret != 1)
return -EINVAL;
_cpufreq_set(freq, policy->cpu);
cpufreq_set(freq, policy->cpu);
return count;
}
......@@ -577,7 +187,6 @@ EXPORT_SYMBOL(cpufreq_gov_userspace);
static int __init cpufreq_gov_userspace_init(void)
{
cpufreq_sysctl_init();
cpufreq_register_notifier(&userspace_cpufreq_notifier_block, CPUFREQ_TRANSITION_NOTIFIER);
return cpufreq_register_governor(&cpufreq_gov_userspace);
}
......@@ -587,7 +196,6 @@ 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();
}
......
......@@ -214,6 +214,11 @@ void cpufreq_frequency_table_put_attr(unsigned int cpu)
}
EXPORT_SYMBOL_GPL(cpufreq_frequency_table_put_attr);
struct cpufreq_frequency_table *cpufreq_frequency_get_table(unsigned int cpu)
{
return show_table[cpu];
}
EXPORT_SYMBOL_GPL(cpufreq_frequency_get_table);
MODULE_AUTHOR ("Dominik Brodowski <linux@brodo.de>");
MODULE_DESCRIPTION ("CPUfreq frequency table helpers");
......
/*
* linux/drivers/cpufreq/proc_intf.c
*
* Copyright (C) 2002 - 2003 Dominik Brodowski
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/cpufreq.h>
#include <linux/ctype.h>
#include <linux/proc_fs.h>
#include <asm/uaccess.h>
#warning This module will be removed from the 2.6. kernel series soon after 2005-01-01
#define CPUFREQ_ALL_CPUS ((NR_CPUS))
static unsigned int warning_print = 0;
/**
* cpufreq_parse_policy - parse a policy string
* @input_string: the string to parse.
* @policy: the policy written inside input_string
*
* This function parses a "policy string" - something the user echo'es into
* /proc/cpufreq or gives as boot parameter - into a struct cpufreq_policy.
* If there are invalid/missing entries, they are replaced with current
* cpufreq policy.
*/
static int cpufreq_parse_policy(char input_string[42], struct cpufreq_policy *policy)
{
unsigned int min = 0;
unsigned int max = 0;
unsigned int cpu = 0;
char str_governor[16];
struct cpufreq_policy current_policy;
unsigned int result = -EFAULT;
if (!policy)
return -EINVAL;
policy->min = 0;
policy->max = 0;
policy->policy = 0;
policy->cpu = CPUFREQ_ALL_CPUS;
if (sscanf(input_string, "%d:%d:%d:%15s", &cpu, &min, &max, str_governor) == 4)
{
policy->min = min;
policy->max = max;
policy->cpu = cpu;
result = 0;
goto scan_policy;
}
if (sscanf(input_string, "%d%%%d%%%d%%%15s", &cpu, &min, &max, str_governor) == 4)
{
if (!cpufreq_get_policy(&current_policy, cpu)) {
policy->min = (min * current_policy.cpuinfo.max_freq) / 100;
policy->max = (max * current_policy.cpuinfo.max_freq) / 100;
policy->cpu = cpu;
result = 0;
goto scan_policy;
}
}
if (sscanf(input_string, "%d:%d:%15s", &min, &max, str_governor) == 3)
{
policy->min = min;
policy->max = max;
result = 0;
goto scan_policy;
}
if (sscanf(input_string, "%d%%%d%%%15s", &min, &max, str_governor) == 3)
{
if (!cpufreq_get_policy(&current_policy, cpu)) {
policy->min = (min * current_policy.cpuinfo.max_freq) / 100;
policy->max = (max * current_policy.cpuinfo.max_freq) / 100;
result = 0;
goto scan_policy;
}
}
return -EINVAL;
scan_policy:
result = cpufreq_parse_governor(str_governor, &policy->policy, &policy->governor);
return result;
}
/**
* cpufreq_proc_read - read /proc/cpufreq
*
* This function prints out the current cpufreq policy.
*/
static int cpufreq_proc_read (
char *page,
char **start,
off_t off,
int count,
int *eof,
void *data)
{
char *p = page;
int len = 0;
struct cpufreq_policy policy;
unsigned int min_pctg = 0;
unsigned int max_pctg = 0;
unsigned int i = 0;
if (off != 0)
goto end;
if (!warning_print) {
warning_print++;
printk(KERN_INFO "Access to /proc/cpufreq is deprecated and "
"will be removed from (new) 2.6. kernels soon "
"after 2005-01-01\n");
}
p += sprintf(p, " minimum CPU frequency - maximum CPU frequency - policy\n");
for (i=0;i<NR_CPUS;i++) {
if (!cpu_online(i))
continue;
if (cpufreq_get_policy(&policy, i))
continue;
if (!policy.cpuinfo.max_freq)
continue;
min_pctg = (policy.min * 100) / policy.cpuinfo.max_freq;
max_pctg = (policy.max * 100) / policy.cpuinfo.max_freq;
p += sprintf(p, "CPU%3d %9d kHz (%3d %%) - %9d kHz (%3d %%) - ",
i , policy.min, min_pctg, policy.max, max_pctg);
if (policy.policy) {
switch (policy.policy) {
case CPUFREQ_POLICY_POWERSAVE:
p += sprintf(p, "powersave\n");
break;
case CPUFREQ_POLICY_PERFORMANCE:
p += sprintf(p, "performance\n");
break;
default:
p += sprintf(p, "INVALID\n");
break;
}
} else
p += scnprintf(p, CPUFREQ_NAME_LEN, "%s\n", policy.governor->name);
}
end:
len = (p - page);
if (len <= off+count)
*eof = 1;
*start = page + off;
len -= off;
if (len>count)
len = count;
if (len<0)
len = 0;
return len;
}
/**
* cpufreq_proc_write - handles writing into /proc/cpufreq
*
* This function calls the parsing script and then sets the policy
* accordingly.
*/
static int cpufreq_proc_write (
struct file *file,
const char __user *buffer,
unsigned long count,
void *data)
{
int result = 0;
char proc_string[42] = {'\0'};
struct cpufreq_policy policy;
unsigned int i = 0;
if ((count > sizeof(proc_string) - 1))
return -EINVAL;
if (copy_from_user(proc_string, buffer, count))
return -EFAULT;
if (!warning_print) {
warning_print++;
printk(KERN_INFO "Access to /proc/cpufreq is deprecated and "
"will be removed from (new) 2.6. kernels soon "
"after 2005-01-01\n");
}
proc_string[count] = '\0';
result = cpufreq_parse_policy(proc_string, &policy);
if (result)
return -EFAULT;
if (policy.cpu == CPUFREQ_ALL_CPUS)
{
for (i=0; i<NR_CPUS; i++)
{
policy.cpu = i;
if (cpu_online(i))
cpufreq_set_policy(&policy);
}
}
else
cpufreq_set_policy(&policy);
return count;
}
/**
* cpufreq_proc_init - add "cpufreq" to the /proc root directory
*
* This function adds "cpufreq" to the /proc root directory.
*/
static int __init cpufreq_proc_init (void)
{
struct proc_dir_entry *entry = NULL;
/* are these acceptable values? */
entry = create_proc_entry("cpufreq", S_IFREG|S_IRUGO|S_IWUSR,
&proc_root);
if (!entry) {
printk(KERN_ERR "unable to create /proc/cpufreq entry\n");
return -EIO;
} else {
entry->read_proc = cpufreq_proc_read;
entry->write_proc = cpufreq_proc_write;
}
return 0;
}
/**
* cpufreq_proc_exit - removes "cpufreq" from the /proc root directory.
*
* This function removes "cpufreq" from the /proc root directory.
*/
static void __exit cpufreq_proc_exit (void)
{
remove_proc_entry("cpufreq", &proc_root);
return;
}
MODULE_AUTHOR ("Dominik Brodowski <linux@brodo.de>");
MODULE_DESCRIPTION ("CPUfreq /proc/cpufreq interface");
MODULE_LICENSE ("GPL");
module_init(cpufreq_proc_init);
module_exit(cpufreq_proc_exit);
......@@ -252,65 +252,6 @@ int cpufreq_update_policy(unsigned int cpu);
/* query the current CPU frequency (in kHz). If zero, cpufreq couldn't detect it */
unsigned int cpufreq_get(unsigned int cpu);
/* the proc_intf.c needs this */
int cpufreq_parse_governor (char *str_governor, unsigned int *policy, struct cpufreq_governor **governor);
/*********************************************************************
* CPUFREQ USERSPACE GOVERNOR *
*********************************************************************/
#ifdef CONFIG_CPU_FREQ_24_API
int __deprecated cpufreq_setmax(unsigned int cpu);
int __deprecated cpufreq_set(unsigned int kHz, unsigned int cpu);
/* /proc/sys/cpu */
enum {
CPU_NR = 1, /* compatibilty reasons */
CPU_NR_0 = 1,
CPU_NR_1 = 2,
CPU_NR_2 = 3,
CPU_NR_3 = 4,
CPU_NR_4 = 5,
CPU_NR_5 = 6,
CPU_NR_6 = 7,
CPU_NR_7 = 8,
CPU_NR_8 = 9,
CPU_NR_9 = 10,
CPU_NR_10 = 11,
CPU_NR_11 = 12,
CPU_NR_12 = 13,
CPU_NR_13 = 14,
CPU_NR_14 = 15,
CPU_NR_15 = 16,
CPU_NR_16 = 17,
CPU_NR_17 = 18,
CPU_NR_18 = 19,
CPU_NR_19 = 20,
CPU_NR_20 = 21,
CPU_NR_21 = 22,
CPU_NR_22 = 23,
CPU_NR_23 = 24,
CPU_NR_24 = 25,
CPU_NR_25 = 26,
CPU_NR_26 = 27,
CPU_NR_27 = 28,
CPU_NR_28 = 29,
CPU_NR_29 = 30,
CPU_NR_30 = 31,
CPU_NR_31 = 32,
};
/* /proc/sys/cpu/{0,1,...,(NR_CPUS-1)} */
enum {
CPU_NR_FREQ_MAX = 1,
CPU_NR_FREQ_MIN = 2,
CPU_NR_FREQ = 3,
};
#endif /* CONFIG_CPU_FREQ_24_API */
/*********************************************************************
* CPUFREQ DEFAULT GOVERNOR *
......@@ -351,6 +292,11 @@ int cpufreq_frequency_table_target(struct cpufreq_policy *policy,
unsigned int relation,
unsigned int *index);
/* the following 3 funtions are for cpufreq core use only */
struct cpufreq_frequency_table *cpufreq_frequency_get_table(unsigned int cpu);
struct cpufreq_policy *cpufreq_cpu_get(unsigned int cpu);
void cpufreq_cpu_put (struct cpufreq_policy *data);
/* the following are really really optional */
extern struct freq_attr cpufreq_freq_attr_scaling_available_freqs;
......
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