version 1.1.1.1, 2008/06/03 10:38:42 |
version 1.1.1.1.2.1, 2008/08/13 17:12:25 |
|
|
/*- |
/*- |
* Copyright (c) 2007, Kohsuke Ohtani |
* Copyright (c) 2007-2008, Kohsuke Ohtani |
* All rights reserved. |
* All rights reserved. |
* |
* |
* Redistribution and use in source and binary forms, with or without |
* Redistribution and use in source and binary forms, with or without |
|
|
* cpufreq.c - CPU frequency control |
* cpufreq.c - CPU frequency control |
*/ |
*/ |
|
|
|
/* |
|
* Dynamic voltage scaling (DVS) |
|
* |
|
* DVS is widely used with mobile systems to save the processor |
|
* power consumption, with minimum impact on performance. |
|
* The basic idea is come from the fact the power consumption is |
|
* proportional to V^2 x f, where V is voltage and f is frequency. |
|
* Since processor does not always require the full performance, |
|
* we can reduce power consumption by lowering voltage and frequeceny. |
|
*/ |
|
|
#include <sys/ioctl.h> |
#include <sys/ioctl.h> |
#include <driver.h> |
#include <driver.h> |
#include <pm.h> |
#include <pm.h> |
#include "dvs.h" |
#include <cpu.h> |
|
#include <cpufreq.h> |
|
|
/* #define DEBUG_CPUFREQ 1 */ |
/* #define DEBUG_CPUFREQ 1 */ |
|
|
#ifdef DEBUG_CPUFREQ |
#ifdef DEBUG_CPUFREQ |
#define cf_printf(fmt, args...) printk("cpufreq: " fmt, ## args) |
#define DPRINTF(a) printf a |
#else |
#else |
#define cf_printf(fmt...) do {} while (0) |
#define DPRINTF(a) |
#endif |
#endif |
|
|
|
/* |
|
* DVS parameters |
|
*/ |
|
#define INTERVAL_MSEC 50 |
|
#define INTERVAL_TICK msec_to_tick(INTERVAL_MSEC) |
|
#define WEIGHT 20 |
|
|
static int cpufreq_open(device_t dev, int mode); |
static int cpufreq_open(device_t dev, int mode); |
static int cpufreq_ioctl(device_t dev, int cmd, u_long arg); |
static int cpufreq_ioctl(device_t dev, u_long cmd, void *arg); |
static int cpufreq_close(device_t dev); |
static int cpufreq_close(device_t dev); |
static int cpufreq_init(void); |
static int cpufreq_init(void); |
|
|
|
|
/* init */ cpufreq_init, |
/* init */ cpufreq_init, |
}; |
}; |
|
|
|
/* |
|
* Device I/O table |
|
*/ |
static struct devio cpufreq_io = { |
static struct devio cpufreq_io = { |
/* open */ cpufreq_open, |
/* open */ cpufreq_open, |
/* close */ cpufreq_close, |
/* close */ cpufreq_close, |
|
|
/* event */ NULL, |
/* event */ NULL, |
}; |
}; |
|
|
static device_t cpufreq_dev; /* Device object */ |
static device_t cpufreq_dev; /* Device object */ |
|
|
|
static int cpufreq_policy; /* Frequecy control policy */ |
|
|
|
static struct dpc dvs_dpc; /* DPC object */ |
|
|
|
static int dvs_capable; /* True if the system has dvs capability */ |
|
static int dvs_enabled; /* True if dvs is enabled */ |
|
|
|
static int cur_speed; /* Current CPU speed (%) */ |
|
static int max_speed; /* Maximum CPU speed (%) */ |
|
static int min_speed; /* Minimum CPU speed (%) */ |
|
|
|
static int run_cycles; /* The non-idle CPU cycles in the last interval */ |
|
static int idle_cycles; /* The idle CPU cycles in the last interval */ |
|
static int excess_cycles; /* The cycles left over from the last interval */ |
|
|
|
static int avg_workload; /* Average workload */ |
|
static int avg_deadline; /* Average deadline */ |
|
|
|
static u_long elapsed_ticks; |
|
|
|
|
/* |
/* |
* Frequecy control policy |
* Predict CPU speed. |
|
* |
|
* DVS Algorithm: Weiser Style |
|
* |
|
* If the utilization prediction x is high (over 70%), increase the |
|
* speed by 20% of the maximum speed. If the utilization prediction |
|
* is low (under 50%), decrease the speed by (60 - x)% of the |
|
* maximum speed. |
|
* |
|
* excess_cycles is defined as the number of uncompleted run cycles |
|
* from the last interval. For example, if we find 70% activity |
|
* when runnig at full speed, and their processor speed was set to |
|
* 50% during that interval, excess_cycles is set to 20%. This |
|
* value (20%) is used to calculate the processor speed in the next |
|
* interval. |
|
* |
|
* Refernce: |
|
* M.Weiser, B.Welch, A.Demers, and S.Shenker, |
|
* "Scheduling for Reduced CPU Energy", In Proceedings of the |
|
* 1st Symposium on Operating Systems Design and Implementation, |
|
* pages 13-23, November 1994. |
*/ |
*/ |
static int cpufreq_policy; |
static int |
|
predict_cpu_speed(int speed) |
|
{ |
|
int next_excess; |
|
int run_percent; |
|
int newspeed = speed; |
|
|
|
run_cycles += excess_cycles; |
|
run_percent = (run_cycles * 100) / (idle_cycles + run_cycles); |
|
|
|
next_excess = run_cycles - speed * (run_cycles + idle_cycles) / 100; |
|
if (next_excess < 0) |
|
next_excess = 0; |
|
|
|
if (excess_cycles > idle_cycles) |
|
newspeed = 100; |
|
else if (run_percent > 70) |
|
newspeed = speed + 20; |
|
else if (run_percent < 50) |
|
newspeed = speed - (60 - run_percent); |
|
|
|
if (newspeed > max_speed) |
|
newspeed = max_speed; |
|
if (newspeed < min_speed) |
|
newspeed = min_speed; |
|
|
|
DPRINTF(("DVS: run_percent=%d next_excess=%d newspeed=%d\n\n", |
|
run_percent, next_excess, newspeed)); |
|
|
|
excess_cycles = next_excess; |
|
return newspeed; |
|
} |
|
|
|
/* |
|
* Predict max CPU speed. |
|
* |
|
* DVS Algorithm: AVG<3> |
|
* |
|
* Computes an exponentially moving average of the previous intervals. |
|
* <wight> is the relative weighting of past intervals relative to |
|
* the current interval. |
|
* |
|
* predict = (weight x current + past) / (weight + 1) |
|
* |
|
* Refernce: |
|
* K.Govil, E.Chan, H.Wasserman, |
|
* "Comparing Algorithm for Dynamic Speed-Setting of a Low-Power CPU". |
|
* Proc. 1st Int'l Conference on Mobile Computing and Networking, |
|
* Nov 1995. |
|
*/ |
static int |
static int |
|
predict_max_speed(int speed) |
|
{ |
|
int new_workload; |
|
int new_deadline; |
|
int newspeed; |
|
|
|
new_workload = run_cycles * speed; |
|
new_deadline = (run_cycles + idle_cycles) * speed; |
|
|
|
avg_workload = (avg_workload * WEIGHT + new_workload) / (WEIGHT + 1); |
|
avg_deadline = (avg_deadline * WEIGHT + new_deadline) / (WEIGHT + 1); |
|
|
|
newspeed = avg_workload * 100 / avg_deadline; |
|
|
|
DPRINTF(("DVS: new_workload=%u new_deadline=%u\n", |
|
new_workload, new_deadline)); |
|
DPRINTF(("DVS: avg_workload=%u avg_deadline=%u\n", |
|
avg_workload, avg_deadline)); |
|
return newspeed; |
|
} |
|
|
|
/* |
|
* DPC routine to set CPU speed. |
|
* |
|
* This is kicked by dvs_tick() if it needed. |
|
*/ |
|
static void |
|
dpc_adjust_speed(void *arg) |
|
{ |
|
int newspeed = (int)arg; |
|
|
|
DPRINTF(("DVS: new speed=%d\n", newspeed)); |
|
cpu_setperf(newspeed); |
|
cur_speed = cpu_getperf(); |
|
} |
|
|
|
/* |
|
* Timer hook routine called by tick handler. |
|
*/ |
|
static void |
|
dvs_tick(int idle) |
|
{ |
|
int newspeed; |
|
|
|
elapsed_ticks++; |
|
if (idle) |
|
idle_cycles++; |
|
else |
|
run_cycles++; |
|
|
|
if (elapsed_ticks < INTERVAL_TICK) |
|
return; |
|
|
|
/* Predict max CPU speed */ |
|
max_speed = predict_max_speed(cur_speed); |
|
|
|
DPRINTF(("DVS: run_cycles=%d idle_cycles=%d cur_speed=%d " |
|
"max_speed=%d\n", |
|
run_cycles, idle_cycles, cur_speed, max_speed)); |
|
/* |
|
* Predict next CPU speed |
|
*/ |
|
newspeed = predict_cpu_speed(cur_speed); |
|
if (newspeed != cur_speed) { |
|
sched_dpc(&dvs_dpc, &dpc_adjust_speed, (void *)newspeed); |
|
} |
|
run_cycles = 0; |
|
idle_cycles = 0; |
|
elapsed_ticks = 0; |
|
} |
|
|
|
/* |
|
* Enable DVS operation |
|
*/ |
|
static void |
|
dvs_enable(void) |
|
{ |
|
|
|
if (!dvs_capable) |
|
return; |
|
|
|
run_cycles = 0; |
|
idle_cycles = 0; |
|
elapsed_ticks = 0; |
|
|
|
max_speed = 100; /* max 100% */ |
|
min_speed = 5; /* min 5% */ |
|
cur_speed = cpu_getperf(); |
|
|
|
timer_hook(dvs_tick); |
|
dvs_enabled = 1; |
|
} |
|
|
|
/* |
|
* Disable DVS operation |
|
*/ |
|
static void |
|
dvs_disable(void) |
|
{ |
|
|
|
if (!dvs_capable) |
|
return; |
|
|
|
timer_hook(NULL); |
|
|
|
/* Set CPU speed to 100% */ |
|
cpu_setperf(100); |
|
dvs_enabled = 0; |
|
} |
|
|
|
static int |
cpufreq_open(device_t dev, int mode) |
cpufreq_open(device_t dev, int mode) |
{ |
{ |
|
|
|
|
} |
} |
|
|
static int |
static int |
cpufreq_ioctl(device_t dev, int cmd, u_long arg) |
cpufreq_ioctl(device_t dev, u_long cmd, void *arg) |
{ |
{ |
|
|
return 0; |
return 0; |
|
|
{ |
{ |
int policy; |
int policy; |
|
|
|
if (cpu_initperf()) |
|
return -1; |
|
|
/* Create device object */ |
/* Create device object */ |
cpufreq_dev = device_create(&cpufreq_io, "cpufreq", DF_CHR); |
cpufreq_dev = device_create(&cpufreq_io, "cpufreq", DF_CHR); |
ASSERT(cpufreq_dev); |
ASSERT(cpufreq_dev); |
|
|
dvs_init(); |
dvs_capable = 1; |
|
|
policy = pm_getpolicy(); |
policy = pm_getpolicy(); |
if (policy == PM_POWERSAVE) |
if (policy == PM_POWERSAVE) |