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