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