scoutos / prex-0.9.0 / bsp / drv / dev / cpufreq / cpufreq.c @ 03e9c04a
History | View | Annotate | Download (9.33 KB)
1 |
/*
|
---|---|
2 |
* Copyright (c) 2007-2009, Kohsuke Ohtani
|
3 |
* All rights reserved.
|
4 |
*
|
5 |
* Redistribution and use in source and binary forms, with or without
|
6 |
* modification, are permitted provided that the following conditions
|
7 |
* are met:
|
8 |
* 1. Redistributions of source code must retain the above copyright
|
9 |
* notice, this list of conditions and the following disclaimer.
|
10 |
* 2. Redistributions in binary form must reproduce the above copyright
|
11 |
* notice, this list of conditions and the following disclaimer in the
|
12 |
* documentation and/or other materials provided with the distribution.
|
13 |
* 3. Neither the name of the author nor the names of any co-contributors
|
14 |
* may be used to endorse or promote products derived from this software
|
15 |
* without specific prior written permission.
|
16 |
*
|
17 |
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
18 |
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
19 |
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
20 |
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
21 |
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
22 |
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
23 |
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
24 |
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
25 |
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
26 |
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
27 |
* SUCH DAMAGE.
|
28 |
*/
|
29 |
|
30 |
/*
|
31 |
* cpufreq.c - CPU frequency control driver
|
32 |
*/
|
33 |
|
34 |
/*
|
35 |
* Dynamic voltage scaling (DVS)
|
36 |
*
|
37 |
* DVS is widely used with mobile systems to save the processor
|
38 |
* power consumption, with minimum impact on performance.
|
39 |
* The basic idea is come from the fact the power consumption is
|
40 |
* proportional to V^2 x f, where V is voltage and f is frequency.
|
41 |
* Since processor does not always require the full performance,
|
42 |
* we can reduce power consumption by lowering voltage and frequeceny.
|
43 |
*/
|
44 |
|
45 |
#include <sys/ioctl.h> |
46 |
#include <sys/power.h> |
47 |
#include <driver.h> |
48 |
#include <sys/sysinfo.h> |
49 |
#include <devctl.h> |
50 |
#include <cpufreq.h> |
51 |
|
52 |
/* #define DEBUG_CPUFREQ 1 */
|
53 |
|
54 |
#ifdef DEBUG_CPUFREQ
|
55 |
#define DPRINTF(a) printf a
|
56 |
#else
|
57 |
#define DPRINTF(a)
|
58 |
#endif
|
59 |
|
60 |
/*
|
61 |
* DVS parameters
|
62 |
*/
|
63 |
#define SAMPLING_RATE 50 /* msec */ |
64 |
#define SAMPLING_TICK mstohz(SAMPLING_RATE)
|
65 |
#define WEIGHT 3 |
66 |
|
67 |
struct cpufreq_softc {
|
68 |
int enable; /* true if enabled */ |
69 |
device_t dev; /* device object */
|
70 |
struct timer timer; /* performance sampling timer */ |
71 |
struct cpufreq_ops *ops; /* low level h/w operations */ |
72 |
}; |
73 |
|
74 |
static int cpufreq_ioctl(device_t, u_long, void *); |
75 |
static int cpufreq_devctl(device_t, u_long, void *); |
76 |
static int cpufreq_init(struct driver *); |
77 |
|
78 |
|
79 |
static struct devops cpufreq_devops= { |
80 |
/* open */ no_open,
|
81 |
/* close */ no_close,
|
82 |
/* read */ no_read,
|
83 |
/* write */ no_write,
|
84 |
/* ioctl */ cpufreq_ioctl,
|
85 |
/* devctl */ cpufreq_devctl,
|
86 |
}; |
87 |
|
88 |
struct driver cpufreq_driver = {
|
89 |
/* name */ "cpufreq", |
90 |
/* devops */ &cpufreq_devops,
|
91 |
/* devsz */ sizeof(struct cpufreq_softc), |
92 |
/* flags */ 0, |
93 |
/* probe */ NULL, |
94 |
/* init */ cpufreq_init,
|
95 |
/* shutdown */ NULL, |
96 |
}; |
97 |
|
98 |
/*
|
99 |
* DVS related data
|
100 |
*/
|
101 |
static u_long last_cputicks;
|
102 |
static u_long last_idleticks;
|
103 |
static int cur_speed; /* current CPU speed (%) */ |
104 |
static int max_speed; /* maximum CPU speed (%) */ |
105 |
static int min_speed; /* minimum CPU speed (%) */ |
106 |
static u_long avg_workload; /* average workload */ |
107 |
static u_long avg_deadline; /* average deadline */ |
108 |
static u_long excess_cycles; /* cycles left over from the last interval */ |
109 |
|
110 |
/*
|
111 |
* Predict max CPU speed.
|
112 |
*
|
113 |
* DVS Algorithm: AVG<3>
|
114 |
*
|
115 |
* Computes an exponentially moving average of the previous intervals.
|
116 |
* <wight> is the relative weighting of past intervals relative to
|
117 |
* the current interval.
|
118 |
*
|
119 |
* predict = (weight x current + past) / (weight + 1)
|
120 |
*
|
121 |
* Refernce:
|
122 |
* K.Govil, E.Chan, H.Wasserman,
|
123 |
* "Comparing Algorithm for Dynamic Speed-Setting of a Low-Power CPU".
|
124 |
* Proc. 1st Int'l Conference on Mobile Computing and Networking,
|
125 |
* Nov 1995.
|
126 |
*/
|
127 |
static void |
128 |
cpufreq_predict_max_speed(u_long run_cycles, u_long idle_cycles) |
129 |
{ |
130 |
u_long new_workload, new_deadline; |
131 |
|
132 |
new_workload = run_cycles * cur_speed; |
133 |
new_deadline = (run_cycles + idle_cycles) * cur_speed; |
134 |
|
135 |
avg_workload = (avg_workload * WEIGHT + new_workload) / (WEIGHT + 1);
|
136 |
avg_deadline = (avg_deadline * WEIGHT + new_deadline) / (WEIGHT + 1);
|
137 |
|
138 |
max_speed = (int)(avg_workload * 100 / avg_deadline); |
139 |
if (max_speed < 50) |
140 |
max_speed = 50;
|
141 |
|
142 |
DPRINTF(("cpufreq: new_workload=%u new_deadline=%u\n",
|
143 |
new_workload, new_deadline)); |
144 |
DPRINTF(("cpufreq: avg_workload=%u avg_deadline=%u\n",
|
145 |
avg_workload, avg_deadline)); |
146 |
DPRINTF(("cpufreq: max_speed=%d\n", max_speed));
|
147 |
} |
148 |
|
149 |
/*
|
150 |
* Predict CPU speed.
|
151 |
*
|
152 |
* DVS Algorithm: Weiser Style
|
153 |
*
|
154 |
* If the utilization prediction x is high (over 70%), increase the
|
155 |
* speed by 20% of the maximum speed. If the utilization prediction
|
156 |
* is low (under 50%), decrease the speed by (60 - x)% of the
|
157 |
* maximum speed.
|
158 |
*
|
159 |
* excess_cycles is defined as the number of uncompleted run cycles
|
160 |
* from the last interval. For example, if we find 70% activity
|
161 |
* when runnig at full speed, and their processor speed was set to
|
162 |
* 50% during that interval, excess_cycles is set to 20%. This
|
163 |
* value (20%) is used to calculate the processor speed in the next
|
164 |
* interval.
|
165 |
*
|
166 |
* Refernce:
|
167 |
* M.Weiser, B.Welch, A.Demers, and S.Shenker,
|
168 |
* "Scheduling for Reduced CPU Energy", In Proceedings of the
|
169 |
* 1st Symposium on Operating Systems Design and Implementation,
|
170 |
* pages 13-23, November 1994.
|
171 |
*/
|
172 |
static int |
173 |
cpufreq_predict_cpu_speed(u_long run_cycles, u_long idle_cycles) |
174 |
{ |
175 |
u_long next_excess; |
176 |
u_int run_percent; |
177 |
u_int new_speed = cur_speed; |
178 |
|
179 |
run_cycles += excess_cycles; |
180 |
run_percent = (int)((run_cycles * 100) / (idle_cycles + run_cycles)); |
181 |
|
182 |
next_excess = run_cycles - |
183 |
cur_speed * (run_cycles + idle_cycles) / 100;
|
184 |
if (next_excess < 0) |
185 |
next_excess = 0;
|
186 |
|
187 |
if (excess_cycles > idle_cycles)
|
188 |
new_speed = 100;
|
189 |
else if (run_percent > 70) |
190 |
new_speed = cur_speed + 20;
|
191 |
else if (run_percent < 50) |
192 |
new_speed = cur_speed - (60 - run_percent);
|
193 |
|
194 |
if (new_speed > max_speed)
|
195 |
new_speed = max_speed; |
196 |
if (new_speed < min_speed)
|
197 |
new_speed = min_speed; |
198 |
|
199 |
DPRINTF(("cpufreq: run_percent=%d next_excess=%d new_speed=%d\n\n",
|
200 |
run_percent, next_excess, new_speed)); |
201 |
|
202 |
excess_cycles = next_excess; |
203 |
|
204 |
return new_speed;
|
205 |
} |
206 |
|
207 |
/*
|
208 |
* Timer callback routine.
|
209 |
*/
|
210 |
static void |
211 |
cpufreq_timeout(void *arg)
|
212 |
{ |
213 |
struct cpufreq_softc *sc = arg;
|
214 |
struct timerinfo info;
|
215 |
int new_speed;
|
216 |
u_long idle_cycles, run_cycles; |
217 |
|
218 |
/*
|
219 |
* Get run/idle cycles.
|
220 |
*/
|
221 |
sysinfo(INFO_TIMER, &info); |
222 |
idle_cycles = info.idleticks - last_idleticks; |
223 |
run_cycles = info.cputicks - last_cputicks - idle_cycles; |
224 |
|
225 |
DPRINTF(("cpufreq: run_cycles=%d idle_cycles=%d cur_speed=%d\n",
|
226 |
run_cycles, idle_cycles, cur_speed)); |
227 |
|
228 |
/*
|
229 |
* Predict max CPU speed.
|
230 |
*/
|
231 |
cpufreq_predict_max_speed(run_cycles, idle_cycles); |
232 |
|
233 |
/*
|
234 |
* Predict next CPU speed.
|
235 |
*/
|
236 |
new_speed = cpufreq_predict_cpu_speed(run_cycles, idle_cycles); |
237 |
if (new_speed != cur_speed) {
|
238 |
sc->ops->setperf(new_speed); |
239 |
cur_speed = sc->ops->getperf(); |
240 |
} |
241 |
|
242 |
last_cputicks = info.cputicks; |
243 |
last_idleticks = info.idleticks; |
244 |
|
245 |
timer_callout(&sc->timer, SAMPLING_RATE, &cpufreq_timeout, sc); |
246 |
} |
247 |
|
248 |
/*
|
249 |
* Enable DVS operation
|
250 |
*/
|
251 |
static void |
252 |
cpufreq_enable(struct cpufreq_softc *sc)
|
253 |
{ |
254 |
struct timerinfo info;
|
255 |
|
256 |
ASSERT(sc->ops != NULL);
|
257 |
|
258 |
DPRINTF(("cpufreq: enable\n"));
|
259 |
|
260 |
if (sc->enable)
|
261 |
return;
|
262 |
sc->enable = 1;
|
263 |
|
264 |
/*
|
265 |
* Initialize DVS parameters.
|
266 |
*/
|
267 |
sysinfo(INFO_TIMER, &info); |
268 |
last_cputicks = info.cputicks; |
269 |
last_idleticks = info.idleticks; |
270 |
|
271 |
max_speed = 100; /* max 100% */ |
272 |
min_speed = 5; /* min 5% */ |
273 |
cur_speed = sc->ops->getperf(); |
274 |
|
275 |
excess_cycles = 0;
|
276 |
avg_workload = SAMPLING_TICK * 100;
|
277 |
avg_deadline = SAMPLING_TICK * 100;
|
278 |
|
279 |
timer_callout(&sc->timer, SAMPLING_RATE, &cpufreq_timeout, sc); |
280 |
} |
281 |
|
282 |
/*
|
283 |
* Disable DVS operation
|
284 |
*/
|
285 |
static void |
286 |
cpufreq_disable(struct cpufreq_softc *sc)
|
287 |
{ |
288 |
|
289 |
DPRINTF(("cpufreq: disable\n"));
|
290 |
|
291 |
if (!sc->enable)
|
292 |
return;
|
293 |
sc->enable = 0;
|
294 |
|
295 |
timer_stop(&sc->timer); |
296 |
|
297 |
/* Set CPU speed to 100% */
|
298 |
sc->ops->setperf(100);
|
299 |
cur_speed = 100;
|
300 |
} |
301 |
|
302 |
static int |
303 |
cpufreq_ioctl(device_t dev, u_long cmd, void *arg)
|
304 |
{ |
305 |
struct cpufreq_softc *sc = device_private(dev);
|
306 |
struct cpufreqinfo info;
|
307 |
|
308 |
if (sc->ops == NULL) |
309 |
return EINVAL;
|
310 |
|
311 |
switch (cmd) {
|
312 |
case CFIOC_GET_INFO:
|
313 |
sc->ops->getinfo(&info); |
314 |
if (copyout(&info, arg, sizeof(info))) |
315 |
return EFAULT;
|
316 |
break;
|
317 |
default:
|
318 |
return EINVAL;
|
319 |
} |
320 |
return 0; |
321 |
} |
322 |
|
323 |
static int |
324 |
cpufreq_devctl(device_t dev, u_long cmd, void *arg)
|
325 |
{ |
326 |
struct cpufreq_softc *sc = device_private(dev);
|
327 |
int error = 0; |
328 |
int policy;
|
329 |
|
330 |
DPRINTF(("cpufreq: devctl cmd=%d\n", cmd));
|
331 |
|
332 |
if (sc->ops == NULL) |
333 |
return 0; |
334 |
|
335 |
switch (cmd) {
|
336 |
case DEVCTL_PM_CHGPOLICY:
|
337 |
DPRINTF(("cpufreq: change policy\n"));
|
338 |
policy = *(int *)arg;
|
339 |
DPRINTF(("cpufreq: policy=%d\n", policy));
|
340 |
if (policy == PM_POWERSAVE)
|
341 |
cpufreq_enable(sc); |
342 |
else
|
343 |
cpufreq_disable(sc); |
344 |
break;
|
345 |
} |
346 |
return error;
|
347 |
} |
348 |
|
349 |
void
|
350 |
cpufreq_attach(struct cpufreq_ops *ops)
|
351 |
{ |
352 |
struct cpufreq_softc *sc;
|
353 |
device_t dev; |
354 |
int policy;
|
355 |
|
356 |
DPRINTF(("cpufreq: attach ops=%x\n", ops));
|
357 |
|
358 |
dev = device_create(&cpufreq_driver, "cpufreq", D_CHR|D_PROT);
|
359 |
|
360 |
sc = device_private(dev); |
361 |
sc->dev = dev; |
362 |
sc->enable = 0;
|
363 |
sc->ops = ops; |
364 |
cur_speed = 100;
|
365 |
|
366 |
policy = DEFAULT_POWER_POLICY; |
367 |
if (policy == PM_POWERSAVE)
|
368 |
cpufreq_enable(sc); |
369 |
} |
370 |
|
371 |
static int |
372 |
cpufreq_init(struct driver *self)
|
373 |
{ |
374 |
|
375 |
return 0; |
376 |
} |