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