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