root / prex-0.9.0 / bsp / drv / dev / cpufreq / cpufreq.c @ 03e9c04a
History | View | Annotate | Download (9.33 KB)
1 | 03e9c04a | Brad Neuman | /*
|
---|---|---|---|
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 | } |