1 /* $NetBSD: acpi_apm.c,v 1.15 2010/03/05 14:00:16 jruoho Exp $ */ 2 3 /*- 4 * Copyright (c) 2006 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Christos Zoulas and by Jared McNeill. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 /* 33 * Autoconfiguration support for the Intel ACPI Component Architecture 34 * ACPI reference implementation. 35 */ 36 37 #include <sys/cdefs.h> 38 __KERNEL_RCSID(0, "$NetBSD: acpi_apm.c,v 1.15 2010/03/05 14:00:16 jruoho Exp $"); 39 40 #include <sys/param.h> 41 #include <sys/device.h> 42 #include <sys/sysctl.h> 43 #include <sys/systm.h> 44 45 #include <dev/acpi/acpivar.h> 46 #include <dev/apm/apmvar.h> 47 48 static void acpiapm_disconnect(void *); 49 static void acpiapm_enable(void *, int); 50 static int acpiapm_set_powstate(void *, u_int, u_int); 51 static int acpiapm_get_powstat(void *, u_int, struct apm_power_info *); 52 static int acpiapm_get_event(void *, u_int *, u_int *); 53 static void acpiapm_cpu_busy(void *); 54 static void acpiapm_cpu_idle(void *); 55 static void acpiapm_get_capabilities(void *, u_int *, u_int *); 56 57 struct apm_accessops acpiapm_accessops = { 58 acpiapm_disconnect, 59 acpiapm_enable, 60 acpiapm_set_powstate, 61 acpiapm_get_powstat, 62 acpiapm_get_event, 63 acpiapm_cpu_busy, 64 acpiapm_cpu_idle, 65 acpiapm_get_capabilities, 66 }; 67 68 #ifdef ACPI_APM_DEBUG 69 #define DPRINTF(a) uprintf a 70 #else 71 #define DPRINTF(a) 72 #endif 73 74 #ifndef ACPI_APM_DEFAULT_STANDBY_STATE 75 #define ACPI_APM_DEFAULT_STANDBY_STATE (1) 76 #endif 77 #ifndef ACPI_APM_DEFAULT_SUSPEND_STATE 78 #define ACPI_APM_DEFAULT_SUSPEND_STATE (3) 79 #endif 80 #define ACPI_APM_DEFAULT_CAP \ 81 ((ACPI_APM_DEFAULT_STANDBY_STATE!=0 ? APM_GLOBAL_STANDBY : 0) | \ 82 (ACPI_APM_DEFAULT_SUSPEND_STATE!=0 ? APM_GLOBAL_SUSPEND : 0)) 83 #define ACPI_APM_STATE_MIN (0) 84 #define ACPI_APM_STATE_MAX (4) 85 86 /* It is assumed that there is only acpiapm instance. */ 87 static int resumed = 0, capability_changed = 0; 88 static int standby_state = ACPI_APM_DEFAULT_STANDBY_STATE; 89 static int suspend_state = ACPI_APM_DEFAULT_SUSPEND_STATE; 90 static int capabilities = ACPI_APM_DEFAULT_CAP; 91 static int acpiapm_node = CTL_EOL, standby_node = CTL_EOL; 92 93 struct acpi_softc; 94 extern ACPI_STATUS acpi_enter_sleep_state(struct acpi_softc *, int); 95 static int acpiapm_match(device_t, cfdata_t , void *); 96 static void acpiapm_attach(device_t, device_t, void *); 97 static int sysctl_state(SYSCTLFN_PROTO); 98 99 CFATTACH_DECL_NEW(acpiapm, sizeof(struct apm_softc), 100 acpiapm_match, acpiapm_attach, NULL, NULL); 101 102 static int 103 /*ARGSUSED*/ 104 acpiapm_match(device_t parent, cfdata_t match, void *aux) 105 { 106 return apm_match(); 107 } 108 109 static void 110 /*ARGSUSED*/ 111 acpiapm_attach(device_t parent, device_t self, void *aux) 112 { 113 struct apm_softc *sc = device_private(self); 114 115 sc->sc_dev = self; 116 sc->sc_ops = &acpiapm_accessops; 117 sc->sc_cookie = parent; 118 sc->sc_vers = 0x0102; 119 sc->sc_detail = 0; 120 sc->sc_hwflags = APM_F_DONT_RUN_HOOKS; 121 apm_attach(sc); 122 } 123 124 static int 125 get_state_value(int id) 126 { 127 const int states[] = { 128 ACPI_STATE_S0, 129 ACPI_STATE_S1, 130 ACPI_STATE_S2, 131 ACPI_STATE_S3, 132 ACPI_STATE_S4 133 }; 134 135 if (id < ACPI_APM_STATE_MIN || id > ACPI_APM_STATE_MAX) 136 return ACPI_STATE_S0; 137 138 return states[id]; 139 } 140 141 static int 142 sysctl_state(SYSCTLFN_ARGS) 143 { 144 int newstate, error, *ref, cap, oldcap; 145 struct sysctlnode node; 146 147 if (rnode->sysctl_num == standby_node) { 148 ref = &standby_state; 149 cap = APM_GLOBAL_STANDBY; 150 } else { 151 ref = &suspend_state; 152 cap = APM_GLOBAL_SUSPEND; 153 } 154 155 newstate = *ref; 156 node = *rnode; 157 node.sysctl_data = &newstate; 158 error = sysctl_lookup(SYSCTLFN_CALL(&node)); 159 if (error || newp == NULL) 160 return error; 161 162 if (newstate < ACPI_APM_STATE_MIN || newstate > ACPI_APM_STATE_MAX) 163 return EINVAL; 164 165 *ref = newstate; 166 oldcap = capabilities; 167 capabilities = newstate != 0 ? oldcap | cap : oldcap & ~cap; 168 if ((capabilities ^ oldcap) != 0) 169 capability_changed = 1; 170 171 return 0; 172 } 173 174 SYSCTL_SETUP(sysctl_acpiapm_setup, "sysctl machdep.acpiapm subtree setup") 175 { 176 const struct sysctlnode *node; 177 178 if (sysctl_createv(clog, 0, NULL, NULL, 179 CTLFLAG_PERMANENT, 180 CTLTYPE_NODE, "machdep", NULL, 181 NULL, 0, NULL, 0, CTL_MACHDEP, CTL_EOL)) 182 return; 183 184 if (sysctl_createv(clog, 0, NULL, &node, 185 CTLFLAG_PERMANENT, 186 CTLTYPE_NODE, "acpiapm", NULL, 187 NULL, 0, NULL, 0, 188 CTL_MACHDEP, CTL_CREATE, CTL_EOL)) 189 return; 190 acpiapm_node = node->sysctl_num; 191 192 if (sysctl_createv(clog, 0, NULL, &node, 193 CTLFLAG_READWRITE, 194 CTLTYPE_INT, "standby", NULL, 195 &sysctl_state, 0, NULL, 0, 196 CTL_MACHDEP, acpiapm_node, CTL_CREATE, CTL_EOL)) 197 return; 198 standby_node = node->sysctl_num; 199 200 if (sysctl_createv(clog, 0, NULL, NULL, 201 CTLFLAG_READWRITE, 202 CTLTYPE_INT, "suspend", NULL, 203 &sysctl_state, 0, NULL, 0, 204 CTL_MACHDEP, acpiapm_node, CTL_CREATE, CTL_EOL)) 205 return; 206 } 207 208 /***************************************************************************** 209 * Minimalistic ACPI /dev/apm emulation support, for ACPI suspend 210 *****************************************************************************/ 211 212 static void 213 /*ARGSUSED*/ 214 acpiapm_disconnect(void *opaque) 215 { 216 return; 217 } 218 219 static void 220 /*ARGSUSED*/ 221 acpiapm_enable(void *opaque, int onoff) 222 { 223 return; 224 } 225 226 static int 227 acpiapm_set_powstate(void *opaque, u_int devid, u_int powstat) 228 { 229 struct acpi_softc *sc = device_private((device_t)opaque); 230 231 if (devid != APM_DEV_ALLDEVS) 232 return APM_ERR_UNRECOG_DEV; 233 234 switch (powstat) { 235 case APM_SYS_READY: 236 break; 237 case APM_SYS_STANDBY: 238 acpi_enter_sleep_state(sc, get_state_value(standby_state)); 239 resumed = 1; 240 break; 241 case APM_SYS_SUSPEND: 242 acpi_enter_sleep_state(sc, get_state_value(suspend_state)); 243 resumed = 1; 244 break; 245 case APM_SYS_OFF: 246 break; 247 case APM_LASTREQ_INPROG: 248 break; 249 case APM_LASTREQ_REJECTED: 250 break; 251 } 252 253 return 0; 254 } 255 256 static int 257 /*ARGSUSED*/ 258 acpiapm_get_powstat(void *opaque, u_int batteryid, 259 struct apm_power_info *pinfo) 260 { 261 #define APM_BATT_FLAG_WATERMARK_MASK (APM_BATT_FLAG_CRITICAL | \ 262 APM_BATT_FLAG_LOW | \ 263 APM_BATT_FLAG_HIGH) 264 int i, curcap, lowcap, warncap, cap, descap, lastcap, discharge; 265 int cap_valid, lastcap_valid, discharge_valid; 266 envsys_tre_data_t etds; 267 envsys_basic_info_t ebis; 268 269 /* Denote most variables as unitialized. */ 270 curcap = lowcap = warncap = descap = -1; 271 272 /* Prepare to aggregate these two variables over all batteries. */ 273 cap = lastcap = discharge = 0; 274 cap_valid = lastcap_valid = discharge_valid = 0; 275 276 (void)memset(pinfo, 0, sizeof(*pinfo)); 277 pinfo->ac_state = APM_AC_UNKNOWN; 278 pinfo->minutes_valid = 0; 279 pinfo->minutes_left = 0; 280 pinfo->batteryid = 0; 281 pinfo->nbattery = 0; /* to be incremented as batteries are found */ 282 pinfo->battery_flags = 0; 283 pinfo->battery_state = APM_BATT_UNKNOWN; /* ignored */ 284 pinfo->battery_life = APM_BATT_LIFE_UNKNOWN; 285 286 sysmonopen_envsys(0, 0, 0, &lwp0); 287 288 for (i = 0;; i++) { 289 const char *desc; 290 int data; 291 int flags; 292 293 ebis.sensor = i; 294 if (sysmonioctl_envsys(0, ENVSYS_GTREINFO, (void *)&ebis, 0, 295 NULL) || (ebis.validflags & ENVSYS_FVALID) == 0) 296 break; 297 etds.sensor = i; 298 if (sysmonioctl_envsys(0, ENVSYS_GTREDATA, (void *)&etds, 0, 299 NULL)) 300 continue; 301 desc = ebis.desc; 302 flags = etds.validflags; 303 data = etds.cur.data_s; 304 305 DPRINTF(("%d %s %d %d\n", i, desc, data, flags)); 306 if ((flags & ENVSYS_FCURVALID) == 0) 307 continue; 308 if (strstr(desc, " connected")) { 309 pinfo->ac_state = data ? APM_AC_ON : APM_AC_OFF; 310 } else if (strstr(desc, " present") && data == 0) 311 pinfo->battery_flags |= APM_BATT_FLAG_NO_SYSTEM_BATTERY; 312 else if (strstr(desc, " charging") && data) 313 pinfo->battery_flags |= APM_BATT_FLAG_CHARGING; 314 else if (strstr(desc, " charging") && !data) 315 pinfo->battery_flags &= ~APM_BATT_FLAG_CHARGING; 316 else if (strstr(desc, " warn cap")) 317 warncap = data / 1000; 318 else if (strstr(desc, " low cap")) 319 lowcap = data / 1000; 320 else if (strstr(desc, " last full cap")) { 321 lastcap += data / 1000; 322 lastcap_valid = 1; 323 } 324 else if (strstr(desc, " design cap")) 325 descap = data / 1000; 326 else if (strstr(desc, " charge") && 327 strstr(desc, " charge rate") == NULL && 328 strstr(desc, " charge state") == NULL) { 329 cap += data / 1000; 330 cap_valid = 1; 331 pinfo->nbattery++; 332 } 333 else if (strstr(desc, " discharge rate")) { 334 discharge += data / 1000; 335 discharge_valid = 1; 336 } 337 } 338 sysmonclose_envsys(0, 0, 0, &lwp0); 339 340 if (cap_valid > 0) { 341 if (warncap != -1 && cap < warncap) 342 pinfo->battery_flags |= APM_BATT_FLAG_CRITICAL; 343 else if (lowcap != -1) { 344 if (cap < lowcap) 345 pinfo->battery_flags |= APM_BATT_FLAG_LOW; 346 else 347 pinfo->battery_flags |= APM_BATT_FLAG_HIGH; 348 } 349 if (lastcap_valid > 0 && lastcap != 0) 350 pinfo->battery_life = 100 * cap / lastcap; 351 else if (descap != -1 && descap != 0) 352 pinfo->battery_life = 100 * cap / descap; 353 } 354 355 if ((pinfo->battery_flags & APM_BATT_FLAG_CHARGING) == 0) { 356 /* discharging */ 357 if (discharge != -1 && cap != -1 && discharge != 0) 358 pinfo->minutes_left = 60 * cap / discharge; 359 } 360 if ((pinfo->battery_flags & APM_BATT_FLAG_WATERMARK_MASK) == 0 && 361 (pinfo->battery_flags & APM_BATT_FLAG_NO_SYSTEM_BATTERY) == 0) { 362 if (pinfo->ac_state == APM_AC_ON) 363 pinfo->battery_flags |= APM_BATT_FLAG_HIGH; 364 else 365 pinfo->battery_flags |= APM_BATT_FLAG_LOW; 366 } 367 368 DPRINTF(("%d %d %d %d %d %d\n", cap, warncap, lowcap, lastcap, descap, 369 discharge)); 370 DPRINTF(("pinfo %d %d %d\n", pinfo->battery_flags, 371 pinfo->battery_life, pinfo->battery_life)); 372 return 0; 373 } 374 375 static int 376 /*ARGSUSED*/ 377 acpiapm_get_event(void *opaque, u_int *event_type, u_int *event_info) 378 { 379 if (capability_changed) { 380 capability_changed = 0; 381 *event_type = APM_CAP_CHANGE; 382 *event_info = 0; 383 return 0; 384 } 385 if (resumed) { 386 resumed = 0; 387 *event_type = APM_NORMAL_RESUME; 388 *event_info = 0; 389 return 0; 390 } 391 392 return APM_ERR_NOEVENTS; 393 } 394 395 static void 396 /*ARGSUSED*/ 397 acpiapm_cpu_busy(void *opaque) 398 { 399 return; 400 } 401 402 static void 403 /*ARGSUSED*/ 404 acpiapm_cpu_idle(void *opaque) 405 { 406 return; 407 } 408 409 static void 410 /*ARGSUSED*/ 411 acpiapm_get_capabilities(void *opaque, u_int *numbatts, 412 u_int *capflags) 413 { 414 *numbatts = 1; 415 *capflags = capabilities; 416 return; 417 } 418