1 /* $NetBSD: hpcapm.c,v 1.1 2004/07/03 12:46:57 uch Exp $ */ 2 3 /* 4 * Copyright (c) 2000 Takemura Shin 5 * Copyright (c) 2000-2001 SATO Kazumi 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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 #include <sys/cdefs.h> 32 __KERNEL_RCSID(0, "$NetBSD: hpcapm.c,v 1.1 2004/07/03 12:46:57 uch Exp $"); 33 34 #include <sys/param.h> 35 #include <sys/device.h> 36 #include <sys/kernel.h> 37 #include <sys/systm.h> 38 39 #include <dev/hpc/apm/apmvar.h> 40 41 #include <machine/bus.h> 42 #include <machine/autoconf.h> 43 #include <machine/config_hook.h> 44 #include <machine/platid.h> 45 #include <machine/platid_mask.h> 46 47 #define HPCAPMDEBUG 48 #ifdef HPCAPMDEBUG 49 #ifndef HPCAPMDEBUG_CONF 50 #define HPCAPMDEBUG_CONF 1 51 #endif 52 int hpcapm_debug = HPCAPMDEBUG_CONF; 53 #define DPRINTF(arg) do { if (hpcapm_debug) printf arg; } while(0); 54 #define DPRINTFN(n, arg) do { if (hpcapm_debug > (n)) printf arg; } while (0); 55 #else 56 #define DPRINTF(arg) do { } while (0); 57 #define DPRINTFN(n, arg) do { } while (0); 58 #endif 59 60 /* Definition of the driver for autoconfig. */ 61 static int hpcapm_match(struct device *, struct cfdata *, void *); 62 static void hpcapm_attach(struct device *, struct device *, void *); 63 static int hpcapm_hook(void *, int, long, void *); 64 65 static void hpcapm_disconnect(void *); 66 static void hpcapm_enable(void *, int); 67 static int hpcapm_set_powstate(void *, u_int, u_int); 68 static int hpcapm_get_powstat(void *, struct apm_power_info *); 69 static int hpcapm_get_event(void *, u_int *, u_int *); 70 static void hpcapm_cpu_busy(void *); 71 static void hpcapm_cpu_idle(void *); 72 static void hpcapm_get_capabilities(void *, u_int *, u_int *); 73 74 struct apmhpc_softc { 75 struct device sc_dev; 76 void *sc_apmdev; 77 volatile unsigned int events; 78 volatile int power_state; 79 volatile int battery_state; 80 volatile int ac_state; 81 config_hook_tag sc_standby_hook; 82 config_hook_tag sc_suspend_hook; 83 config_hook_tag sc_battery_hook; 84 config_hook_tag sc_ac_hook; 85 int battery_life; 86 int minutes_left; 87 }; 88 89 CFATTACH_DECL(hpcapm, sizeof (struct apmhpc_softc), 90 hpcapm_match, hpcapm_attach, NULL, NULL); 91 92 struct apm_accessops hpcapm_accessops = { 93 hpcapm_disconnect, 94 hpcapm_enable, 95 hpcapm_set_powstate, 96 hpcapm_get_powstat, 97 hpcapm_get_event, 98 hpcapm_cpu_busy, 99 hpcapm_cpu_idle, 100 hpcapm_get_capabilities, 101 }; 102 103 extern struct cfdriver hpcapm_cd; 104 105 static int 106 hpcapm_match(struct device *parent, struct cfdata *cf, void *aux) 107 { 108 struct mainbus_attach_args *ma = aux; 109 110 if (strcmp(ma->ma_name, hpcapm_cd.cd_name) != 0) { 111 return (0); 112 } 113 return (1); 114 } 115 116 static void 117 hpcapm_attach(struct device *parent, struct device *self, void *aux) 118 { 119 struct apmhpc_softc *sc; 120 struct apmdev_attach_args aaa; 121 122 sc = (struct apmhpc_softc *)self; 123 printf(": pseudo power management module\n"); 124 125 sc->events = 0; 126 sc->power_state = APM_SYS_READY; 127 sc->battery_state = APM_BATT_FLAG_UNKNOWN; 128 sc->ac_state = APM_AC_UNKNOWN; 129 sc->battery_life = APM_BATT_LIFE_UNKNOWN; 130 sc->minutes_left = 0; 131 sc->sc_standby_hook = config_hook(CONFIG_HOOK_PMEVENT, 132 CONFIG_HOOK_PMEVENT_STANDBYREQ, 133 CONFIG_HOOK_EXCLUSIVE, 134 hpcapm_hook, sc); 135 sc->sc_suspend_hook = config_hook(CONFIG_HOOK_PMEVENT, 136 CONFIG_HOOK_PMEVENT_SUSPENDREQ, 137 CONFIG_HOOK_EXCLUSIVE, 138 hpcapm_hook, sc); 139 140 sc->sc_battery_hook = config_hook(CONFIG_HOOK_PMEVENT, 141 CONFIG_HOOK_PMEVENT_BATTERY, 142 CONFIG_HOOK_SHARE, 143 hpcapm_hook, sc); 144 145 sc->sc_battery_hook = config_hook(CONFIG_HOOK_PMEVENT, 146 CONFIG_HOOK_PMEVENT_AC, 147 CONFIG_HOOK_SHARE, 148 hpcapm_hook, sc); 149 150 aaa.accessops = &hpcapm_accessops; 151 aaa.accesscookie = sc; 152 aaa.apm_detail = 0x0102; 153 154 sc->sc_apmdev = config_found(self, &aaa, apmprint); 155 } 156 157 static int 158 hpcapm_hook(void *ctx, int type, long id, void *msg) 159 { 160 struct apmhpc_softc *sc; 161 int s; 162 int charge; 163 int message; 164 165 sc = ctx; 166 167 if (type != CONFIG_HOOK_PMEVENT) 168 return 1; 169 170 if (CONFIG_HOOK_VALUEP(msg)) 171 message = (int)msg; 172 else 173 message = *(int *)msg; 174 175 s = splhigh(); 176 switch (id) { 177 case CONFIG_HOOK_PMEVENT_STANDBYREQ: 178 if (sc->power_state != APM_SYS_STANDBY) { 179 sc->events |= (1 << APM_USER_STANDBY_REQ); 180 } else { 181 sc->events |= (1 << APM_NORMAL_RESUME); 182 } 183 break; 184 case CONFIG_HOOK_PMEVENT_SUSPENDREQ: 185 if (sc->power_state != APM_SYS_SUSPEND) { 186 DPRINTF(("hpcapm: suspend req\n")); 187 sc->events |= (1 << APM_USER_SUSPEND_REQ); 188 } else { 189 sc->events |= (1 << APM_NORMAL_RESUME); 190 } 191 break; 192 case CONFIG_HOOK_PMEVENT_BATTERY: 193 switch (message) { 194 case CONFIG_HOOK_BATT_CRITICAL: 195 DPRINTF(("hpcapm: battery state critical\n")); 196 charge = sc->battery_state&APM_BATT_FLAG_CHARGING; 197 sc->battery_state = APM_BATT_FLAG_CRITICAL; 198 sc->battery_state |= charge; 199 sc->battery_life = 0; 200 break; 201 case CONFIG_HOOK_BATT_LOW: 202 DPRINTF(("hpcapm: battery state low\n")); 203 charge = sc->battery_state&APM_BATT_FLAG_CHARGING; 204 sc->battery_state = APM_BATT_FLAG_LOW; 205 sc->battery_state |= charge; 206 break; 207 case CONFIG_HOOK_BATT_HIGH: 208 DPRINTF(("hpcapm: battery state high\n")); 209 charge = sc->battery_state&APM_BATT_FLAG_CHARGING; 210 sc->battery_state = APM_BATT_FLAG_HIGH; 211 sc->battery_state |= charge; 212 break; 213 case CONFIG_HOOK_BATT_10P: 214 DPRINTF(("hpcapm: battery life 10%%\n")); 215 sc->battery_life = 10; 216 break; 217 case CONFIG_HOOK_BATT_20P: 218 DPRINTF(("hpcapm: battery life 20%%\n")); 219 sc->battery_life = 20; 220 break; 221 case CONFIG_HOOK_BATT_30P: 222 DPRINTF(("hpcapm: battery life 30%%\n")); 223 sc->battery_life = 30; 224 break; 225 case CONFIG_HOOK_BATT_40P: 226 DPRINTF(("hpcapm: battery life 40%%\n")); 227 sc->battery_life = 40; 228 break; 229 case CONFIG_HOOK_BATT_50P: 230 DPRINTF(("hpcapm: battery life 50%%\n")); 231 sc->battery_life = 50; 232 break; 233 case CONFIG_HOOK_BATT_60P: 234 DPRINTF(("hpcapm: battery life 60%%\n")); 235 sc->battery_life = 60; 236 break; 237 case CONFIG_HOOK_BATT_70P: 238 DPRINTF(("hpcapm: battery life 70%%\n")); 239 sc->battery_life = 70; 240 break; 241 case CONFIG_HOOK_BATT_80P: 242 DPRINTF(("hpcapm: battery life 80%%\n")); 243 sc->battery_life = 80; 244 break; 245 case CONFIG_HOOK_BATT_90P: 246 DPRINTF(("hpcapm: battery life 90%%\n")); 247 sc->battery_life = 90; 248 break; 249 case CONFIG_HOOK_BATT_100P: 250 DPRINTF(("hpcapm: battery life 100%%\n")); 251 sc->battery_life = 100; 252 break; 253 case CONFIG_HOOK_BATT_UNKNOWN: 254 DPRINTF(("hpcapm: battery state unknown\n")); 255 sc->battery_state = APM_BATT_FLAG_UNKNOWN; 256 sc->battery_life = APM_BATT_LIFE_UNKNOWN; 257 break; 258 case CONFIG_HOOK_BATT_NO_SYSTEM_BATTERY: 259 DPRINTF(("hpcapm: battery state no system battery?\n")); 260 sc->battery_state = APM_BATT_FLAG_NO_SYSTEM_BATTERY; 261 sc->battery_life = APM_BATT_LIFE_UNKNOWN; 262 break; 263 } 264 break; 265 case CONFIG_HOOK_PMEVENT_AC: 266 switch (message) { 267 case CONFIG_HOOK_AC_OFF: 268 DPRINTF(("hpcapm: ac not connect\n")); 269 sc->battery_state &= ~APM_BATT_FLAG_CHARGING; 270 sc->ac_state = APM_AC_OFF; 271 break; 272 case CONFIG_HOOK_AC_ON_CHARGE: 273 DPRINTF(("hpcapm: charging\n")); 274 sc->battery_state |= APM_BATT_FLAG_CHARGING; 275 sc->ac_state = APM_AC_ON; 276 break; 277 case CONFIG_HOOK_AC_ON_NOCHARGE: 278 DPRINTF(("hpcapm: ac connect\n")); 279 sc->battery_state &= ~APM_BATT_FLAG_CHARGING; 280 sc->ac_state = APM_AC_ON; 281 break; 282 case CONFIG_HOOK_AC_UNKNOWN: 283 sc->ac_state = APM_AC_UNKNOWN; 284 break; 285 } 286 break; 287 } 288 splx(s); 289 290 return (0); 291 } 292 293 static void 294 hpcapm_disconnect(void *scx) 295 { 296 struct apmhpc_softc *sc; 297 298 sc = scx; 299 } 300 301 static void 302 hpcapm_enable(void *scx, int onoff) 303 { 304 struct apmhpc_softc *sc; 305 306 sc = scx; 307 } 308 309 static int 310 hpcapm_set_powstate(void *scx, u_int devid, u_int powstat) 311 { 312 struct apmhpc_softc *sc; 313 int s; 314 315 sc = scx; 316 317 if (devid != APM_DEV_ALLDEVS) 318 return APM_ERR_UNRECOG_DEV; 319 320 switch (powstat) { 321 case APM_SYS_READY: 322 DPRINTF(("hpcapm: set power state READY\n")); 323 sc->power_state = APM_SYS_READY; 324 break; 325 case APM_SYS_STANDBY: 326 DPRINTF(("hpcapm: set power state STANDBY\n")); 327 s = splhigh(); 328 config_hook_call(CONFIG_HOOK_PMEVENT, 329 CONFIG_HOOK_PMEVENT_HARDPOWER, 330 (void *)PWR_STANDBY); 331 sc->power_state = APM_SYS_STANDBY; 332 machine_standby(); 333 config_hook_call_reverse(CONFIG_HOOK_PMEVENT, 334 CONFIG_HOOK_PMEVENT_HARDPOWER, 335 (void *)PWR_RESUME); 336 DPRINTF(("hpcapm: resume\n")); 337 splx(s); 338 break; 339 case APM_SYS_SUSPEND: 340 DPRINTF(("hpcapm: set power state SUSPEND...\n")); 341 s = splhigh(); 342 config_hook_call(CONFIG_HOOK_PMEVENT, 343 CONFIG_HOOK_PMEVENT_HARDPOWER, 344 (void *)PWR_SUSPEND); 345 sc->power_state = APM_SYS_SUSPEND; 346 machine_sleep(); 347 config_hook_call_reverse(CONFIG_HOOK_PMEVENT, 348 CONFIG_HOOK_PMEVENT_HARDPOWER, 349 (void *)PWR_RESUME); 350 DPRINTF(("hpcapm: resume\n")); 351 splx(s); 352 break; 353 case APM_SYS_OFF: 354 DPRINTF(("hpcapm: set power state OFF\n")); 355 sc->power_state = APM_SYS_OFF; 356 break; 357 case APM_LASTREQ_INPROG: 358 /*DPRINTF(("hpcapm: set power state INPROG\n")); 359 */ 360 break; 361 case APM_LASTREQ_REJECTED: 362 DPRINTF(("hpcapm: set power state REJECTED\n")); 363 break; 364 } 365 366 return (0); 367 } 368 369 static int 370 hpcapm_get_powstat(void *scx, struct apm_power_info *pinfo) 371 { 372 struct apmhpc_softc *sc; 373 374 sc = scx; 375 376 pinfo->ac_state = sc->ac_state; 377 pinfo->battery_state = sc->battery_state; 378 pinfo->battery_life = sc->battery_life; 379 return (0); 380 } 381 382 static int 383 hpcapm_get_event(void *scx, u_int *event_type, u_int *event_info) 384 { 385 struct apmhpc_softc *sc; 386 int s, i; 387 388 sc = scx; 389 s = splhigh(); 390 for (i = APM_STANDBY_REQ; i <= APM_CAP_CHANGE; i++) { 391 if (sc->events & (1 << i)) { 392 sc->events &= ~(1 << i); 393 *event_type = i; 394 if (*event_type == APM_NORMAL_RESUME || 395 *event_type == APM_CRIT_RESUME) { 396 /* pccard power off in the suspend state */ 397 *event_info = 1; 398 sc->power_state = APM_SYS_READY; 399 } else 400 *event_info = 0; 401 return (0); 402 } 403 } 404 splx(s); 405 406 return APM_ERR_NOEVENTS; 407 } 408 409 static void 410 hpcapm_cpu_busy(void *scx) 411 { 412 struct apmhpc_softc *sc; 413 414 sc = scx; 415 } 416 417 static void 418 hpcapm_cpu_idle(void *scx) 419 { 420 struct apmhpc_softc *sc; 421 422 sc = scx; 423 } 424 425 static void 426 hpcapm_get_capabilities(void *scx, u_int *numbatts, u_int *capflags) 427 { 428 struct apmhpc_softc *sc; 429 430 *numbatts = 0; 431 *capflags = APM_GLOBAL_STANDBY | APM_GLOBAL_SUSPEND; 432 433 sc = scx; 434 } 435