1/* $NetBSD: hpcapm.c,v 1.24 2021/08/07 16:19:11 thorpej 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.24 2021/08/07 16:19:11 thorpej 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 57int 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. */ 66static int hpcapm_match(device_t, cfdata_t, void *); 67static void hpcapm_attach(device_t, device_t, void *); 68static int hpcapm_hook(void *, int, long, void *); 69 70static void hpcapm_disconnect(void *); 71static void hpcapm_enable(void *, int); 72static int hpcapm_set_powstate(void *, u_int, u_int); 73static int hpcapm_get_powstat(void *, u_int, struct apm_power_info *); 74static int hpcapm_get_event(void *, u_int *, u_int *); 75static void hpcapm_cpu_busy(void *); 76static void hpcapm_cpu_idle(void *); 77static void hpcapm_get_capabilities(void *, u_int *, u_int *); 78 79struct 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 93CFATTACH_DECL_NEW(hpcapm, sizeof (struct apmhpc_softc), 94 hpcapm_match, hpcapm_attach, NULL, NULL); 95 96struct 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 107static int 108hpcapm_match(device_t parent, cfdata_t cf, void *aux) 109{ 110 111 return 1; 112} 113 114static void 115hpcapm_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(self, &aaa, apmprint, CFARGS_NONE); 153 154 if (!pmf_device_register(self, NULL, NULL)) 155 aprint_error_dev(self, "unable to establish power handler\n"); 156} 157 158static int 159hpcapm_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 294static void 295hpcapm_disconnect(void *scx) 296{ 297} 298 299static void 300hpcapm_enable(void *scx, int onoff) 301{ 302} 303 304static int 305hpcapm_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 364static int 365hpcapm_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 399static int 400hpcapm_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 428static void 429hpcapm_cpu_busy(void *scx) 430{ 431} 432 433static void 434hpcapm_cpu_idle(void *scx) 435{ 436} 437 438static void 439hpcapm_get_capabilities(void *scx, u_int *numbatts, u_int *capflags) 440{ 441 *numbatts = 0; 442 *capflags = APM_GLOBAL_STANDBY | APM_GLOBAL_SUSPEND; 443} 444