Statistics
| Branch: | Revision:

root / 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
}