Prex Home / Browse Source - Prex Version: 0.9.0

root/bsp/drv/dev/cpufreq/cpufreq.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. cpufreq_predict_max_speed
  2. cpufreq_predict_cpu_speed
  3. cpufreq_timeout
  4. cpufreq_enable
  5. cpufreq_disable
  6. cpufreq_ioctl
  7. cpufreq_devctl
  8. cpufreq_attach
  9. cpufreq_init

   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 }

/* [<][>][^][v][top][bottom][index][help] */