1 /* $NetBSD: hpcapm.c,v 1.22 2017/10/28 04:53:56 riastradh 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.22 2017/10/28 04:53:56 riastradh 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 #include "ioconf.h" 52 53 #ifdef HPCAPMDEBUG 54 #ifndef HPCAPMDEBUG_CONF 55 #define HPCAPMDEBUG_CONF 1 56 #endif 57 int hpcapm_debug = HPCAPMDEBUG_CONF; 58 #define DPRINTF(arg) do { if (hpcapm_debug) printf arg; } while(0); 59 #define DPRINTFN(n, arg) do { if (hpcapm_debug > (n)) printf arg; } while (0); 60 #else 61 #define DPRINTF(arg) do { } while (0); 62 #define DPRINTFN(n, arg) do { } while (0); 63 #endif 64 65 /* Definition of the driver for autoconfig. */ 66 static int hpcapm_match(device_t, cfdata_t, void *); 67 static void hpcapm_attach(device_t, device_t, void *); 68 static int hpcapm_hook(void *, int, long, void *); 69 70 static void hpcapm_disconnect(void *); 71 static void hpcapm_enable(void *, int); 72 static int hpcapm_set_powstate(void *, u_int, u_int); 73 static int hpcapm_get_powstat(void *, u_int, struct apm_power_info *); 74 static int hpcapm_get_event(void *, u_int *, u_int *); 75 static void hpcapm_cpu_busy(void *); 76 static void hpcapm_cpu_idle(void *); 77 static void hpcapm_get_capabilities(void *, u_int *, u_int *); 78 79 struct apmhpc_softc { 80 void *sc_apmdev; 81 volatile unsigned int events; 82 volatile int power_state; 83 volatile int battery_flags; 84 volatile int ac_state; 85 config_hook_tag sc_standby_hook; 86 config_hook_tag sc_suspend_hook; 87 config_hook_tag sc_battery_hook; 88 config_hook_tag sc_ac_hook; 89 int battery_life; 90 int minutes_left; 91 }; 92 93 CFATTACH_DECL_NEW(hpcapm, sizeof (struct apmhpc_softc), 94 hpcapm_match, hpcapm_attach, NULL, NULL); 95 96 struct apm_accessops hpcapm_accessops = { 97 hpcapm_disconnect, 98 hpcapm_enable, 99 hpcapm_set_powstate, 100 hpcapm_get_powstat, 101 hpcapm_get_event, 102 hpcapm_cpu_busy, 103 hpcapm_cpu_idle, 104 hpcapm_get_capabilities, 105 }; 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 } 298 299 static void 300 hpcapm_enable(void *scx, int onoff) 301 { 302 } 303 304 static int 305 hpcapm_set_powstate(void *scx, u_int devid, u_int powstat) 306 { 307 struct apmhpc_softc *sc; 308 int s; 309 310 sc = scx; 311 312 if (devid != APM_DEV_ALLDEVS) 313 return APM_ERR_UNRECOG_DEV; 314 315 switch (powstat) { 316 case APM_SYS_READY: 317 DPRINTF(("hpcapm: set power state READY\n")); 318 sc->power_state = APM_SYS_READY; 319 break; 320 case APM_SYS_STANDBY: 321 DPRINTF(("hpcapm: set power state STANDBY\n")); 322 s = splhigh(); 323 config_hook_call(CONFIG_HOOK_PMEVENT, 324 CONFIG_HOOK_PMEVENT_HARDPOWER, 325 (void *)PWR_STANDBY); 326 sc->power_state = APM_SYS_STANDBY; 327 machine_standby(); 328 config_hook_call_reverse(CONFIG_HOOK_PMEVENT, 329 CONFIG_HOOK_PMEVENT_HARDPOWER, 330 (void *)PWR_RESUME); 331 DPRINTF(("hpcapm: resume\n")); 332 splx(s); 333 break; 334 case APM_SYS_SUSPEND: 335 DPRINTF(("hpcapm: set power state SUSPEND...\n")); 336 s = splhigh(); 337 config_hook_call(CONFIG_HOOK_PMEVENT, 338 CONFIG_HOOK_PMEVENT_HARDPOWER, 339 (void *)PWR_SUSPEND); 340 sc->power_state = APM_SYS_SUSPEND; 341 machine_sleep(); 342 config_hook_call_reverse(CONFIG_HOOK_PMEVENT, 343 CONFIG_HOOK_PMEVENT_HARDPOWER, 344 (void *)PWR_RESUME); 345 DPRINTF(("hpcapm: resume\n")); 346 splx(s); 347 break; 348 case APM_SYS_OFF: 349 DPRINTF(("hpcapm: set power state OFF\n")); 350 sc->power_state = APM_SYS_OFF; 351 break; 352 case APM_LASTREQ_INPROG: 353 /*DPRINTF(("hpcapm: set power state INPROG\n")); 354 */ 355 break; 356 case APM_LASTREQ_REJECTED: 357 DPRINTF(("hpcapm: set power state REJECTED\n")); 358 break; 359 } 360 361 return (0); 362 } 363 364 static int 365 hpcapm_get_powstat(void *scx, u_int batteryid, struct apm_power_info *pinfo) 366 { 367 struct apmhpc_softc *sc; 368 int val; 369 370 sc = scx; 371 372 pinfo->nbattery = 0; 373 pinfo->batteryid = 0; 374 pinfo->minutes_valid = 0; 375 pinfo->minutes_left = 0; 376 pinfo->battery_state = APM_BATT_UNKNOWN; /* XXX: ignored */ 377 378 if (config_hook_call(CONFIG_HOOK_GET, 379 CONFIG_HOOK_ACADAPTER, &val) != -1) 380 pinfo->ac_state = val; 381 else 382 pinfo->ac_state = sc->ac_state; 383 384 if (config_hook_call(CONFIG_HOOK_GET, 385 CONFIG_HOOK_CHARGE, &val) != -1) 386 pinfo->battery_flags = val; 387 else 388 pinfo->battery_flags = sc->battery_flags; 389 390 if (config_hook_call(CONFIG_HOOK_GET, 391 CONFIG_HOOK_BATTERYVAL, &val) != -1) 392 pinfo->battery_life = val; 393 else 394 pinfo->battery_life = sc->battery_life; 395 396 return (0); 397 } 398 399 static int 400 hpcapm_get_event(void *scx, u_int *event_type, u_int *event_info) 401 { 402 struct apmhpc_softc *sc; 403 int s, i; 404 405 sc = scx; 406 s = splhigh(); 407 for (i = APM_STANDBY_REQ; i <= APM_CAP_CHANGE; i++) { 408 if (sc->events & (1 << i)) { 409 sc->events &= ~(1 << i); 410 *event_type = i; 411 if (*event_type == APM_NORMAL_RESUME || 412 *event_type == APM_CRIT_RESUME) { 413 /* pccard power off in the suspend state */ 414 *event_info = 1; 415 sc->power_state = APM_SYS_READY; 416 } else 417 *event_info = 0; 418 splx(s); 419 420 return (0); 421 } 422 } 423 splx(s); 424 425 return APM_ERR_NOEVENTS; 426 } 427 428 static void 429 hpcapm_cpu_busy(void *scx) 430 { 431 } 432 433 static void 434 hpcapm_cpu_idle(void *scx) 435 { 436 } 437 438 static void 439 hpcapm_get_capabilities(void *scx, u_int *numbatts, u_int *capflags) 440 { 441 *numbatts = 0; 442 *capflags = APM_GLOBAL_STANDBY | APM_GLOBAL_SUSPEND; 443 } 444