File: [local] / prex / dev / power / cpufreq.c (download)
Revision 1.1.1.1 (vendor branch), Tue Aug 19 12:46:47 2008 UTC (15 years, 9 months ago) by nbrk
Branch: MAIN, KOHSUKE
CVS Tags: PREX_0_8_BASE, HEAD Changes since 1.1: +0 -0 lines
Initial import of Prex, Portable Real-time Embedded POSIX microkernel system.
I have totally new directions in my development (more focused on real hardware, not virtual one).
Old hacks are available in prex-old module. They will be carefully re-designed and merged soon.
|
/*-
* Copyright (c) 2007-2008, Kohsuke Ohtani
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the author nor the names of any co-contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*
* 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 <driver.h>
#include <pm.h>
#include <cpu.h>
#include <cpufreq.h>
/* #define DEBUG_CPUFREQ 1 */
#ifdef DEBUG_CPUFREQ
#define DPRINTF(a) printf a
#else
#define DPRINTF(a)
#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_ioctl(device_t dev, u_long cmd, void *arg);
static int cpufreq_close(device_t dev);
static int cpufreq_init(void);
/*
* Driver structure
*/
struct driver cpufreq_drv = {
/* name */ "CPU Frequency Control",
/* order */ 3, /* Must larger than pm driver */
/* init */ cpufreq_init,
};
/*
* Device I/O table
*/
static struct devio cpufreq_io = {
/* open */ cpufreq_open,
/* close */ cpufreq_close,
/* read */ NULL,
/* write */ NULL,
/* ioctl */ cpufreq_ioctl,
/* event */ NULL,
};
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;
/*
* 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
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
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)
{
return 0;
}
static int
cpufreq_close(device_t dev)
{
return 0;
}
static int
cpufreq_ioctl(device_t dev, u_long cmd, void *arg)
{
return 0;
}
void
cpufreq_setpolicy(int policy)
{
switch (cpufreq_policy) {
case CPUFREQ_ONDEMAND:
if (policy == PM_POWERSAVE)
dvs_enable();
else
dvs_disable();
break;
case CPUFREQ_MAXSPEED:
break;
case CPUFREQ_MINSPEED:
break;
}
}
static int
cpufreq_init(void)
{
int policy;
if (cpu_initperf())
return -1;
/* Create device object */
cpufreq_dev = device_create(&cpufreq_io, "cpufreq", DF_CHR);
ASSERT(cpufreq_dev);
dvs_capable = 1;
policy = pm_getpolicy();
if (policy == PM_POWERSAVE)
dvs_enable();
return 0;
}