Prex Home / Browse Source - Prex Version: 0.9.0

root/bsp/drv/dev/base/pm.c

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

DEFINITIONS

This source file includes following definitions.
  1. pm_open
  2. pm_close
  3. pm_ioctl
  4. pm_stop_timer
  5. pm_update_timer
  6. pm_suspend
  7. pm_resume
  8. pm_poweroff
  9. pm_reboot
  10. pm_lcd_off
  11. pm_lcd_on
  12. pm_timeout
  13. pm_set_power
  14. pm_notify
  15. pm_attach_lcd
  16. pm_init

   1 /*-
   2  * Copyright (c) 2005-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  * pm.c - power management driver
  32  */
  33 
  34 #include <sys/ioctl.h>
  35 #include <sys/capability.h>
  36 #include <sys/power.h>
  37 #include <sys/signal.h>
  38 #include <driver.h>
  39 #include <devctl.h>
  40 #include <cons.h>
  41 #include <pm.h>
  42 
  43 /* #define DEBUG_PM 1 */
  44 
  45 #ifdef DEBUG_PM
  46 #define DPRINTF(a) printf a
  47 #else
  48 #define DPRINTF(a)
  49 #endif
  50 
  51 struct pm_softc {
  52         device_t        dev;            /* device object */
  53         int             isopen;         /* number of open counts */
  54         int             policy;         /* power management policy */
  55         int             timer_active;   /* true if pm timer is staring */
  56         timer_t         timer;          /* pm timer */
  57         u_long          idlecnt;        /* idle counter in sec */
  58         u_long          dimtime;        /* auto dim (lcd off) time in sec */
  59         u_long          sustime;        /* auto suspend time in sec */
  60         task_t          powtask;        /* task for power server */
  61         int             lcd_on;         /* true if lcd is off */
  62         device_t        lcd_dev;        /* lcd device */
  63         int             lastevt;        /* last event */
  64 };
  65 
  66 static int      pm_open(device_t, int);
  67 static int      pm_close(device_t);
  68 static int      pm_ioctl(device_t, u_long, void *);
  69 static int      pm_init(struct driver *);
  70 static void     pm_stop_timer(void);
  71 static void     pm_update_timer(void);
  72 static void     pm_timeout(void *);
  73 
  74 
  75 static struct devops pm_devops = {
  76         /* open */      pm_open,
  77         /* close */     pm_close,
  78         /* read */      no_read,
  79         /* write */     no_write,
  80         /* ioctl */     pm_ioctl,
  81         /* devctl */    no_devctl,
  82 };
  83 
  84 struct driver pm_driver = {
  85         /* name */      "pm",
  86         /* devops */    &pm_devops,
  87         /* devsz */     sizeof(struct pm_softc),
  88         /* flags */     0,
  89         /* probe */     NULL,
  90         /* init */      pm_init,
  91         /* unload */    NULL,
  92 };
  93 
  94 /*
  95  * Pointer to the PM state.  There can be only one PM instance.
  96  */
  97 static struct pm_softc *pm_softc;
  98 
  99 static int
 100 pm_open(device_t dev, int mode)
 101 {
 102         struct pm_softc *sc = pm_softc;
 103 
 104         if (!task_capable(CAP_POWERMGMT))
 105                 return EPERM;
 106 
 107         if (sc->isopen > 0)
 108                 return EBUSY;
 109 
 110         sc->isopen++;
 111         return 0;
 112 }
 113 
 114 static int
 115 pm_close(device_t dev)
 116 {
 117         struct pm_softc *sc = pm_softc;
 118 
 119         if (!task_capable(CAP_POWERMGMT))
 120                 return EPERM;
 121 
 122         if (sc->isopen != 1)
 123                 return EINVAL;
 124 
 125         sc->isopen--;
 126         return 0;
 127 }
 128 
 129 static int
 130 pm_ioctl(device_t dev, u_long cmd, void *arg)
 131 {
 132         struct pm_softc *sc = pm_softc;
 133         int error = 0;
 134         int policy, state, event;
 135 
 136         if (!task_capable(CAP_POWERMGMT))
 137                 return EPERM;
 138 
 139         switch (cmd) {
 140 
 141         case PMIOC_CONNECT:
 142                 /* Connection request from the power server */
 143                 if (copyin(arg, &sc->powtask, sizeof(task_t)))
 144                         return EFAULT;
 145                 DPRINTF(("pm: connect power server\n"));
 146                 break;
 147 
 148         case PMIOC_QUERY_EVENT:
 149                 event = sc->lastevt;
 150                 sc->lastevt = PME_NO_EVENT;
 151                 if (copyout(&event, arg, sizeof(int)))
 152                         return EFAULT;
 153                 DPRINTF(("pm: query event=%d\n", event));
 154                 break;
 155 
 156         case PMIOC_SET_POWER:
 157                 if (copyin(arg, &state, sizeof(int)))
 158                         return EFAULT;
 159 
 160                 switch (state) {
 161                 case PWR_SUSPEND:
 162                 case PWR_OFF:
 163                 case PWR_REBOOT:
 164                         pm_set_power(state);
 165                         break;
 166                 default:
 167                         error = EINVAL;
 168                         break;
 169                 }
 170                 break;
 171 
 172         case PMIOC_GET_POLICY:
 173                 if (copyout(&sc->policy, arg, sizeof(int)))
 174                         return EFAULT;
 175                 DPRINTF(("pm: get policy %d\n", sc->policy));
 176                 break;
 177 
 178         case PMIOC_SET_POLICY:
 179                 if (copyin(arg, &policy, sizeof(int)))
 180                         return EFAULT;
 181                 if (policy != PM_POWERSAVE && policy != PM_PERFORMANCE)
 182                         return EINVAL;
 183 
 184                 DPRINTF(("pm: set policy %d\n", policy));
 185 
 186                 if (policy == sc->policy) {
 187                         /* same policy */
 188                         break;
 189                 }
 190                 /* Call devctl() routine for all devices */
 191                 device_broadcast(DEVCTL_PM_CHGPOLICY, &policy, 1);
 192 
 193                 sc->policy = policy;
 194                 if (policy == PM_POWERSAVE)
 195                         pm_update_timer();
 196                 else
 197                         pm_stop_timer();
 198                 break;
 199 
 200         case PMIOC_GET_SUSTMR:
 201                 if (copyout(&sc->sustime, arg, sizeof(u_long)))
 202                         return EFAULT;
 203                 break;
 204 
 205         case PMIOC_SET_SUSTMR:
 206                 if (copyin(arg, &sc->sustime, sizeof(u_long)))
 207                         return EFAULT;
 208                 DPRINTF(("pm: set sustmr=%d\n", sc->sustime));
 209                 pm_update_timer();
 210                 break;
 211 
 212         case PMIOC_GET_DIMTMR:
 213                 if (copyout(&sc->dimtime, arg, sizeof(u_long)))
 214                         return EFAULT;
 215                 break;
 216 
 217         case PMIOC_SET_DIMTMR:
 218                 if (copyin(arg, &sc->dimtime, sizeof(u_long)))
 219                         return EFAULT;
 220                 DPRINTF(("pm: set dimtmr=%d\n", sc->dimtime));
 221                 pm_update_timer();
 222                 break;
 223 
 224         default:
 225                 return EINVAL;
 226         }
 227         return error;
 228 }
 229 
 230 
 231 static void
 232 pm_stop_timer(void)
 233 {
 234         struct pm_softc *sc = pm_softc;
 235         int s;
 236 
 237         DPRINTF(("pm: stop timer\n"));
 238 
 239         s = splhigh();
 240         if (sc->timer_active) {
 241                 timer_stop(&sc->timer);
 242                 sc->idlecnt = 0;
 243                 sc->timer_active = 0;
 244         }
 245         splx(s);
 246 }
 247 
 248 static void
 249 pm_update_timer(void)
 250 {
 251         struct pm_softc *sc = pm_softc;
 252         int s;
 253 
 254         if (sc->policy != PM_POWERSAVE)
 255                 return;
 256 
 257         s = splhigh();
 258         sc->idlecnt = 0;
 259         if (sc->timer_active) {
 260                 if (sc->sustime == 0 && sc->dimtime == 0)
 261                         timer_stop(&sc->timer);
 262         } else {
 263                 if (sc->sustime != 0 || sc->dimtime != 0) {
 264                         DPRINTF(("pm: start timer\n"));
 265                         timer_callout(&sc->timer, 1000, &pm_timeout, sc);
 266                         sc->timer_active = 1;
 267                 }
 268         }
 269 }
 270 
 271 
 272 static int
 273 pm_suspend(void)
 274 {
 275         int error;
 276 
 277         DPRINTF(("pm: suspend system...\n"));
 278 
 279         pm_stop_timer();
 280         error = device_broadcast(DEVCTL_PM_POWERDOWN, NULL, 1);
 281         if (error) {
 282                 device_broadcast(DEVCTL_PM_POWERUP, NULL, 1);
 283                 return error;
 284         }
 285         machine_powerdown(PWR_SUSPEND);
 286         return 0;
 287 }
 288 
 289 static int
 290 pm_resume(void)
 291 {
 292 
 293         DPRINTF(("pm: resume...\n"));
 294 
 295         device_broadcast(DEVCTL_PM_POWERUP, NULL, 1);
 296         pm_update_timer();
 297         return 0;
 298 }
 299 
 300 static int
 301 pm_poweroff(void)
 302 {
 303 
 304         DPRINTF(("pm: power off...\n"));
 305 
 306         pm_stop_timer();
 307         device_broadcast(DEVCTL_PM_POWERDOWN, NULL, 1);
 308         driver_shutdown();
 309 
 310 #ifdef CONFIG_CONS
 311         cons_puts("\nThe system is halted. You can turn off power.");
 312 #endif
 313         machine_powerdown(PWR_OFF);
 314 
 315         /* NOTREACHED */
 316         return 0;
 317 }
 318 
 319 static int
 320 pm_reboot(void)
 321 {
 322 
 323         DPRINTF(("pm: rebooting...\n"));
 324 
 325         pm_stop_timer();
 326         device_broadcast(DEVCTL_PM_POWERDOWN, NULL, 1);
 327         driver_shutdown();
 328         machine_powerdown(PWR_REBOOT);
 329 
 330         /* NOTREACHED */
 331         return 0;
 332 }
 333 
 334 static void
 335 pm_lcd_off(void)
 336 {
 337         struct pm_softc *sc = pm_softc;
 338 
 339         DPRINTF(("pm: LCD off\n"));
 340 
 341         if (sc->lcd_dev != NODEV && sc->lcd_on) {
 342                 device_control(sc->lcd_dev, DEVCTL_PM_LCDOFF, NULL);
 343                 if (sc->sustime == 0)
 344                         pm_stop_timer();
 345                 sc->lcd_on = 0;
 346         }
 347 }
 348 
 349 static void
 350 pm_lcd_on(void)
 351 {
 352         struct pm_softc *sc = pm_softc;
 353 
 354         DPRINTF(("pm: LCD on\n"));
 355 
 356         if (sc->lcd_dev != NODEV && !sc->lcd_on) {
 357                 device_control(sc->lcd_dev, DEVCTL_PM_LCDON, NULL);
 358                 pm_update_timer();
 359                 sc->lcd_on = 1;
 360         }
 361 }
 362 
 363 static void
 364 pm_timeout(void *arg)
 365 {
 366         struct pm_softc *sc = arg;
 367         int s, reload;
 368 
 369         s = splhigh();
 370         sc->idlecnt++;
 371         splx(s);
 372 
 373         DPRINTF(("pm: idlecnt=%d\n", sc->idlecnt));
 374 
 375         if (sc->sustime != 0 && sc->idlecnt >= sc->sustime) {
 376 #ifdef CONFIG_CONS
 377                 cons_puts("\nThe system is about to suspend...");
 378 #endif
 379                 pm_suspend();
 380         } else {
 381                 reload = 0;
 382                 if (sc->dimtime != 0 && sc->idlecnt >= sc->dimtime) {
 383                         pm_lcd_off();
 384                         if (sc->sustime != 0)
 385                                 reload = 1;
 386                 } else
 387                         reload = 1;
 388 
 389                 if (reload)
 390                         timer_callout(&sc->timer, 1000, &pm_timeout, sc);
 391         }
 392 }
 393 
 394 /*
 395  * PM service for other drivers.
 396  */
 397 int
 398 pm_set_power(int state)
 399 {
 400         int error;
 401 
 402         switch (state) {
 403         case PWR_ON:
 404                 error = pm_resume();
 405                 break;
 406         case PWR_SUSPEND:
 407                 error = pm_suspend();
 408                 break;
 409         case PWR_OFF:
 410                 error = pm_poweroff();
 411                 break;
 412         case PWR_REBOOT:
 413                 error = pm_reboot();
 414                 break;
 415         default:
 416                 error = EINVAL;
 417         }
 418         return error;
 419 }
 420 
 421 /*
 422  * PM event notification.
 423  */
 424 void
 425 pm_notify(int event)
 426 {
 427         struct pm_softc *sc = pm_softc;
 428         int s;
 429 
 430         if (event == PME_USER_ACTIVITY) {
 431                 /*
 432                  * Reload suspend timer for user activity.
 433                  */
 434                 s = splhigh();
 435                 sc->idlecnt = 0;
 436                 splx(s);
 437 
 438                 if (!sc->lcd_on)
 439                         pm_lcd_on();
 440                 return;
 441         }
 442 
 443         DPRINTF(("pm: notify %d\n", event));
 444 
 445         if (sc->powtask != TASK_NULL) {
 446                 /*
 447                  * Power server exists.
 448                  */
 449                 switch (event) {
 450                 case PME_PWRBTN_PRESS:
 451                 case PME_SLPBTN_PRESS:
 452                 case PME_LOW_BATTERY:
 453                 case PME_LCD_CLOSE:
 454                         /*
 455                          * Post an exception to the power server.
 456                          * Then, the power server will query PM event.
 457                          */
 458                         sc->lastevt = event;
 459                         DPRINTF(("pm: post %d\n", event));
 460                         exception_post(sc->powtask, SIGPWR);
 461                         break;
 462                 case PME_LCD_OPEN:
 463                         sc->lastevt = PME_NO_EVENT;
 464                         pm_lcd_on();
 465                         break;
 466                 }
 467         } else {
 468                 /*
 469                  * No power server.
 470                  * Map power event to default action.
 471                  */
 472                 switch (event) {
 473                 case PME_PWRBTN_PRESS:
 474                         pm_poweroff();
 475                         break;
 476                 case PME_SLPBTN_PRESS:
 477                 case PME_LOW_BATTERY:
 478                         pm_suspend();
 479                         break;
 480                 case PME_LCD_OPEN:
 481                         pm_lcd_on();
 482                         break;
 483                 case PME_LCD_CLOSE:
 484                         pm_lcd_off();
 485                         break;
 486                 }
 487         }
 488 }
 489 
 490 void
 491 pm_attach_lcd(device_t dev)
 492 {
 493 
 494         ASSERT(pm_softc != NULL);
 495 
 496         pm_softc->lcd_dev = dev;
 497 }
 498 
 499 static int
 500 pm_init(struct driver *self)
 501 {
 502         struct pm_softc *sc;
 503         device_t dev;
 504 
 505         /* Create device object */
 506         dev = device_create(self, "pm", D_CHR|D_PROT);
 507 
 508         sc = device_private(dev);
 509         sc->dev = dev;
 510         sc->isopen = 0;
 511         sc->policy = DEFAULT_POWER_POLICY;
 512         sc->idlecnt = 0;
 513         sc->dimtime = 0;
 514         sc->sustime = 0;
 515         sc->timer_active = 0;
 516         sc->powtask = TASK_NULL;
 517         sc->lcd_dev = NODEV;
 518         sc->lcd_on = 1;
 519         sc->lastevt = PME_NO_EVENT;
 520 
 521         pm_softc = sc;
 522 
 523         DPRINTF(("Power policy: %s mode\n",
 524                  (sc->policy == PM_POWERSAVE) ? "power save" : "performance"));
 525         return 0;
 526 }

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