1 /* $NetBSD: acpi_bat.c,v 1.123 2024/04/27 00:40:06 christos Exp $ */ 2 3 /*- 4 * Copyright (c) 2003 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Charles M. Hannum of By Noon Software, Inc. 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 * Copyright 2001 Bill Sommerfeld. 34 * All rights reserved. 35 * 36 * Redistribution and use in source and binary forms, with or without 37 * modification, are permitted provided that the following conditions 38 * are met: 39 * 1. Redistributions of source code must retain the above copyright 40 * notice, this list of conditions and the following disclaimer. 41 * 2. Redistributions in binary form must reproduce the above copyright 42 * notice, this list of conditions and the following disclaimer in the 43 * documentation and/or other materials provided with the distribution. 44 * 3. All advertising materials mentioning features or use of this software 45 * must display the following acknowledgement: 46 * This product includes software developed for the NetBSD Project by 47 * Wasabi Systems, Inc. 48 * 4. The name of Wasabi Systems, Inc. may not be used to endorse 49 * or promote products derived from this software without specific prior 50 * written permission. 51 * 52 * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND 53 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 54 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 55 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC 56 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 57 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 58 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 59 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 60 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 61 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 62 * POSSIBILITY OF SUCH DAMAGE. 63 */ 64 65 /* 66 * ACPI Battery Driver. 67 * 68 * ACPI defines two different battery device interfaces: "Control 69 * Method" batteries, in which AML methods are defined in order to get 70 * battery status and set battery alarm thresholds, and a "Smart 71 * Battery" device, which is an SMbus device accessed through the ACPI 72 * Embedded Controller device. 73 * 74 * This driver is for the "Control Method"-style battery only. 75 */ 76 77 #include <sys/cdefs.h> 78 __KERNEL_RCSID(0, "$NetBSD: acpi_bat.c,v 1.123 2024/04/27 00:40:06 christos Exp $"); 79 80 #include <sys/param.h> 81 #include <sys/condvar.h> 82 #include <sys/device.h> 83 #include <sys/kernel.h> 84 #include <sys/kmem.h> 85 #include <sys/module.h> 86 #include <sys/mutex.h> 87 #include <sys/systm.h> 88 89 #include <dev/acpi/acpireg.h> 90 #include <dev/acpi/acpivar.h> 91 92 #define _COMPONENT ACPI_BAT_COMPONENT 93 ACPI_MODULE_NAME ("acpi_bat") 94 95 #define ACPI_NOTIFY_BAT_STATUS 0x80 96 #define ACPI_NOTIFY_BAT_INFO 0x81 97 98 /* 99 * Sensor indexes. 100 */ 101 enum { 102 ACPIBAT_PRESENT = 0, 103 ACPIBAT_DVOLTAGE = 1, 104 ACPIBAT_VOLTAGE = 2, 105 ACPIBAT_DCAPACITY = 3, 106 ACPIBAT_LFCCAPACITY = 4, 107 ACPIBAT_CAPACITY = 5, 108 ACPIBAT_CHARGERATE = 6, 109 ACPIBAT_DISCHARGERATE = 7, 110 ACPIBAT_CHARGING = 8, 111 ACPIBAT_CHARGE_STATE = 9, 112 ACPIBAT_COUNT = 10 113 }; 114 115 /* 116 * Battery Information, _BIF 117 * (ACPI 3.0, sec. 10.2.2.1). 118 */ 119 enum { 120 ACPIBAT_BIF_UNIT = 0, 121 ACPIBAT_BIF_DCAPACITY = 1, 122 ACPIBAT_BIF_LFCCAPACITY = 2, 123 ACPIBAT_BIF_TECHNOLOGY = 3, 124 ACPIBAT_BIF_DVOLTAGE = 4, 125 ACPIBAT_BIF_WCAPACITY = 5, 126 ACPIBAT_BIF_LCAPACITY = 6, 127 ACPIBAT_BIF_GRANULARITY1 = 7, 128 ACPIBAT_BIF_GRANULARITY2 = 8, 129 ACPIBAT_BIF_MODEL = 9, 130 ACPIBAT_BIF_SERIAL = 10, 131 ACPIBAT_BIF_TYPE = 11, 132 ACPIBAT_BIF_OEM = 12, 133 ACPIBAT_BIF_COUNT = 13 134 }; 135 136 /* 137 * Battery Status, _BST 138 * (ACPI 3.0, sec. 10.2.2.3). 139 */ 140 enum { 141 ACPIBAT_BST_STATE = 0, 142 ACPIBAT_BST_RATE = 1, 143 ACPIBAT_BST_CAPACITY = 2, 144 ACPIBAT_BST_VOLTAGE = 3, 145 ACPIBAT_BST_COUNT = 4 146 }; 147 148 struct acpibat_softc { 149 struct acpi_devnode *sc_node; 150 struct sysmon_envsys *sc_sme; 151 struct timeval sc_last; 152 envsys_data_t *sc_sensor; 153 kmutex_t sc_mutex; 154 kcondvar_t sc_condvar; 155 int32_t sc_dcapacity; 156 int32_t sc_dvoltage; 157 int32_t sc_lcapacity; 158 int32_t sc_wcapacity; 159 int sc_present; 160 bool sc_dying; 161 }; 162 163 static const struct device_compatible_entry compat_data[] = { 164 { .compat = "PNP0C0A" }, 165 DEVICE_COMPAT_EOL 166 }; 167 168 #define ACPIBAT_PWRUNIT_MA 0x00000001 /* mA not mW */ 169 #define ACPIBAT_ST_DISCHARGING 0x00000001 /* battery is discharging */ 170 #define ACPIBAT_ST_CHARGING 0x00000002 /* battery is charging */ 171 #define ACPIBAT_ST_CRITICAL 0x00000004 /* battery is critical */ 172 173 /* 174 * A value used when _BST or _BIF is temporarily unknown. 175 */ 176 #define ACPIBAT_VAL_UNKNOWN 0xFFFFFFFF 177 178 #define ACPIBAT_VAL_ISVALID(x) \ 179 (((x) != ACPIBAT_VAL_UNKNOWN) ? ENVSYS_SVALID : ENVSYS_SINVALID) 180 181 static int acpibat_match(device_t, cfdata_t, void *); 182 static void acpibat_attach(device_t, device_t, void *); 183 static int acpibat_detach(device_t, int); 184 static int acpibat_get_sta(device_t); 185 static ACPI_OBJECT *acpibat_get_object(ACPI_HANDLE, const char *, uint32_t); 186 static void acpibat_get_info(device_t); 187 static void acpibat_print_info(device_t, ACPI_OBJECT *); 188 static void acpibat_get_status(device_t); 189 static void acpibat_update_info(void *); 190 static void acpibat_update_status(void *); 191 static void acpibat_init_envsys(device_t); 192 static void acpibat_notify_handler(ACPI_HANDLE, uint32_t, void *); 193 static void acpibat_refresh(struct sysmon_envsys *, envsys_data_t *); 194 static bool acpibat_resume(device_t, const pmf_qual_t *); 195 static void acpibat_get_limits(struct sysmon_envsys *, envsys_data_t *, 196 sysmon_envsys_lim_t *, uint32_t *); 197 198 CFATTACH_DECL_NEW(acpibat, sizeof(struct acpibat_softc), 199 acpibat_match, acpibat_attach, acpibat_detach, NULL); 200 201 /* 202 * acpibat_match: 203 * 204 * Autoconfiguration `match' routine. 205 */ 206 static int 207 acpibat_match(device_t parent, cfdata_t match, void *aux) 208 { 209 struct acpi_attach_args *aa = aux; 210 211 return acpi_compatible_match(aa, compat_data); 212 } 213 214 /* 215 * acpibat_attach: 216 * 217 * Autoconfiguration `attach' routine. 218 */ 219 static void 220 acpibat_attach(device_t parent, device_t self, void *aux) 221 { 222 struct acpibat_softc *sc = device_private(self); 223 struct acpi_attach_args *aa = aux; 224 ACPI_HANDLE tmp; 225 ACPI_STATUS rv; 226 227 aprint_naive(": ACPI Battery\n"); 228 aprint_normal(": ACPI Battery\n"); 229 230 sc->sc_node = aa->aa_node; 231 232 mutex_init(&sc->sc_mutex, MUTEX_DEFAULT, IPL_NONE); 233 cv_init(&sc->sc_condvar, device_xname(self)); 234 235 sc->sc_sensor = kmem_zalloc(ACPIBAT_COUNT * 236 sizeof(*sc->sc_sensor), KM_SLEEP); 237 238 config_interrupts(self, acpibat_init_envsys); 239 240 /* 241 * If this is ever seen, the driver should be extended. 242 */ 243 rv = AcpiGetHandle(sc->sc_node->ad_handle, "_BIX", &tmp); 244 if (ACPI_SUCCESS(rv)) 245 aprint_verbose_dev(self, "ACPI 4.0 functionality present\n"); 246 } 247 248 /* 249 * acpibat_detach: 250 * 251 * Autoconfiguration `detach' routine. 252 */ 253 static int 254 acpibat_detach(device_t self, int flags) 255 { 256 struct acpibat_softc *sc = device_private(self); 257 258 /* Prevent further use of sc->sc_sme in acpibat_update_info. */ 259 mutex_enter(&sc->sc_mutex); 260 sc->sc_dying = true; 261 mutex_exit(&sc->sc_mutex); 262 263 /* Prevent further calls to acpibat_resume. */ 264 pmf_device_deregister(self); 265 266 /* Prevent further calls to acpibat_notify_handler. */ 267 acpi_deregister_notify(sc->sc_node); 268 269 /* Detach sensors and prevent further calls to acpibat_refresh. */ 270 if (sc->sc_sme != NULL) 271 sysmon_envsys_unregister(sc->sc_sme); 272 273 /* 274 * Wait for calls to acpibat_update_info/status in case sysmon 275 * envsys refreshed the sensors and queued them but they didn't 276 * run before sysmon_envsys_unregister. After this point, no 277 * asynchronous access to the softc is possible. 278 */ 279 AcpiOsWaitEventsComplete(); 280 281 if (sc->sc_sensor != NULL) 282 kmem_free(sc->sc_sensor, ACPIBAT_COUNT * 283 sizeof(*sc->sc_sensor)); 284 285 cv_destroy(&sc->sc_condvar); 286 mutex_destroy(&sc->sc_mutex); 287 288 return 0; 289 } 290 291 /* 292 * acpibat_get_sta: 293 * 294 * Evaluate whether the battery is present or absent. 295 * 296 * Returns: 0 for no battery, 1 for present, and -1 on error. 297 */ 298 static int 299 acpibat_get_sta(device_t dv) 300 { 301 struct acpibat_softc *sc = device_private(dv); 302 ACPI_INTEGER val; 303 ACPI_STATUS rv; 304 305 rv = acpi_eval_integer(sc->sc_node->ad_handle, "_STA", &val); 306 if (ACPI_FAILURE(rv)) { 307 aprint_error_dev(dv, "failed to evaluate _STA: %s\n", 308 AcpiFormatException(rv)); 309 return -1; 310 } 311 312 sc->sc_sensor[ACPIBAT_PRESENT].state = ENVSYS_SVALID; 313 314 if ((val & ACPI_STA_BATTERY_PRESENT) == 0) { 315 sc->sc_sensor[ACPIBAT_PRESENT].value_cur = 0; 316 return 0; 317 } 318 319 sc->sc_sensor[ACPIBAT_PRESENT].value_cur = 1; 320 321 return 1; 322 } 323 324 static ACPI_OBJECT * 325 acpibat_get_object(ACPI_HANDLE hdl, const char *pth, uint32_t count) 326 { 327 ACPI_OBJECT *obj; 328 ACPI_BUFFER buf; 329 ACPI_STATUS rv; 330 331 rv = acpi_eval_struct(hdl, pth, &buf); 332 if (ACPI_FAILURE(rv)) 333 return NULL; 334 335 obj = buf.Pointer; 336 if (obj->Type != ACPI_TYPE_PACKAGE) { 337 ACPI_FREE(buf.Pointer); 338 return NULL; 339 } 340 if (obj->Package.Count != count) { 341 ACPI_FREE(buf.Pointer); 342 return NULL; 343 } 344 345 return obj; 346 } 347 348 /* 349 * acpibat_get_info: 350 * 351 * Get the battery info. 352 */ 353 static void 354 acpibat_get_info(device_t dv) 355 { 356 struct acpibat_softc *sc = device_private(dv); 357 ACPI_HANDLE hdl = sc->sc_node->ad_handle; 358 ACPI_OBJECT *elm, *obj; 359 ACPI_STATUS rv = AE_OK; 360 int capunit, i, rateunit; 361 uint64_t val; 362 363 obj = acpibat_get_object(hdl, "_BIF", ACPIBAT_BIF_COUNT); 364 if (obj == NULL) { 365 rv = AE_ERROR; 366 goto out; 367 } 368 369 elm = obj->Package.Elements; 370 for (i = ACPIBAT_BIF_UNIT; i < ACPIBAT_BIF_MODEL; i++) { 371 if (elm[i].Type != ACPI_TYPE_INTEGER) { 372 rv = AE_TYPE; 373 goto out; 374 } 375 if (elm[i].Integer.Value != ACPIBAT_VAL_UNKNOWN && 376 elm[i].Integer.Value >= INT_MAX) { 377 rv = AE_LIMIT; 378 goto out; 379 } 380 } 381 382 switch (elm[ACPIBAT_BIF_UNIT].Integer.Value) { 383 case ACPIBAT_PWRUNIT_MA: 384 capunit = ENVSYS_SAMPHOUR; 385 rateunit = ENVSYS_SAMPS; 386 break; 387 default: 388 capunit = ENVSYS_SWATTHOUR; 389 rateunit = ENVSYS_SWATTS; 390 break; 391 } 392 393 sc->sc_sensor[ACPIBAT_DCAPACITY].units = capunit; 394 sc->sc_sensor[ACPIBAT_LFCCAPACITY].units = capunit; 395 sc->sc_sensor[ACPIBAT_CHARGERATE].units = rateunit; 396 sc->sc_sensor[ACPIBAT_DISCHARGERATE].units = rateunit; 397 sc->sc_sensor[ACPIBAT_CAPACITY].units = capunit; 398 399 /* Design capacity. */ 400 val = elm[ACPIBAT_BIF_DCAPACITY].Integer.Value; 401 sc->sc_sensor[ACPIBAT_DCAPACITY].value_cur = val * 1000; 402 sc->sc_sensor[ACPIBAT_DCAPACITY].state = ACPIBAT_VAL_ISVALID(val); 403 404 /* Last full charge capacity. */ 405 val = elm[ACPIBAT_BIF_LFCCAPACITY].Integer.Value; 406 sc->sc_sensor[ACPIBAT_LFCCAPACITY].value_cur = val * 1000; 407 sc->sc_sensor[ACPIBAT_LFCCAPACITY].state = ACPIBAT_VAL_ISVALID(val); 408 409 /* Design voltage. */ 410 val = elm[ACPIBAT_BIF_DVOLTAGE].Integer.Value; 411 sc->sc_sensor[ACPIBAT_DVOLTAGE].value_cur = val * 1000; 412 sc->sc_sensor[ACPIBAT_DVOLTAGE].state = ACPIBAT_VAL_ISVALID(val); 413 414 /* Design low and warning capacity. */ 415 sc->sc_lcapacity = elm[ACPIBAT_BIF_LCAPACITY].Integer.Value * 1000; 416 sc->sc_wcapacity = elm[ACPIBAT_BIF_WCAPACITY].Integer.Value * 1000; 417 418 /* 419 * Initialize the maximum of current capacity 420 * to the last known full charge capacity. 421 */ 422 val = sc->sc_sensor[ACPIBAT_LFCCAPACITY].value_cur; 423 sc->sc_sensor[ACPIBAT_CAPACITY].value_max = val; 424 425 acpibat_print_info(dv, elm); 426 427 out: 428 if (obj != NULL) 429 ACPI_FREE(obj); 430 431 if (ACPI_FAILURE(rv)) 432 aprint_error_dev(dv, "failed to evaluate _BIF: %s\n", 433 AcpiFormatException(rv)); 434 } 435 436 /* 437 * acpibat_print_info: 438 * 439 * Display the battery info. 440 */ 441 static void 442 acpibat_print_info(device_t dv, ACPI_OBJECT *elm) 443 { 444 struct acpibat_softc *sc = device_private(dv); 445 const char *tech, *unit; 446 int32_t dcap, dvol; 447 int i; 448 449 for (i = ACPIBAT_BIF_OEM; i > ACPIBAT_BIF_GRANULARITY2; i--) { 450 if (elm[i].Type != ACPI_TYPE_STRING) 451 return; 452 if (elm[i].String.Pointer == NULL) 453 return; 454 if (elm[i].String.Pointer[0] == '\0') 455 return; 456 } 457 458 dcap = elm[ACPIBAT_BIF_DCAPACITY].Integer.Value; 459 dvol = elm[ACPIBAT_BIF_DVOLTAGE].Integer.Value; 460 461 /* 462 * Try to detect whether the battery was switched. 463 */ 464 if (sc->sc_dcapacity == dcap && sc->sc_dvoltage == dvol) 465 return; 466 else { 467 sc->sc_dcapacity = dcap; 468 sc->sc_dvoltage = dvol; 469 } 470 471 tech = (elm[ACPIBAT_BIF_TECHNOLOGY].Integer.Value != 0) ? 472 "rechargeable" : "non-rechargeable"; 473 474 aprint_normal_dev(dv, "%s %s %s battery\n", 475 elm[ACPIBAT_BIF_OEM].String.Pointer, 476 elm[ACPIBAT_BIF_TYPE].String.Pointer, tech); 477 478 aprint_debug_dev(dv, "model number %s, serial number %s\n", 479 elm[ACPIBAT_BIF_MODEL].String.Pointer, 480 elm[ACPIBAT_BIF_SERIAL].String.Pointer); 481 482 #define SCALE(x) (((int)x) / 1000000), ((((int)x) % 1000000) / 1000) 483 484 /* 485 * These values are defined as follows (ACPI 4.0, p. 388): 486 * 487 * Granularity 1. "Battery capacity granularity between low 488 * and warning in [mAh] or [mWh]. That is, 489 * this is the smallest increment in capacity 490 * that the battery is capable of measuring." 491 * 492 * Granularity 2. "Battery capacity granularity between warning 493 * and full in [mAh] or [mWh]. [...]" 494 */ 495 switch (elm[ACPIBAT_BIF_UNIT].Integer.Value) { 496 case ACPIBAT_PWRUNIT_MA: 497 unit = "Ah"; 498 break; 499 default: 500 unit = "Wh"; 501 break; 502 } 503 504 aprint_verbose_dev(dv, "granularity: " 505 "low->warn %d.%03d %s, warn->full %d.%03d %s\n", 506 SCALE(elm[ACPIBAT_BIF_GRANULARITY1].Integer.Value * 1000), unit, 507 SCALE(elm[ACPIBAT_BIF_GRANULARITY2].Integer.Value * 1000), unit); 508 } 509 510 /* 511 * acpibat_get_status: 512 * 513 * Get the current battery status. 514 */ 515 static void 516 acpibat_get_status(device_t dv) 517 { 518 struct acpibat_softc *sc = device_private(dv); 519 ACPI_HANDLE hdl = sc->sc_node->ad_handle; 520 ACPI_OBJECT *elm, *obj; 521 ACPI_STATUS rv = AE_OK; 522 int i, rate, state; 523 uint64_t val; 524 525 obj = acpibat_get_object(hdl, "_BST", ACPIBAT_BST_COUNT); 526 if (obj == NULL) { 527 rv = AE_ERROR; 528 goto out; 529 } 530 531 elm = obj->Package.Elements; 532 for (i = ACPIBAT_BST_STATE; i < ACPIBAT_BST_COUNT; i++) { 533 if (elm[i].Type != ACPI_TYPE_INTEGER) { 534 rv = AE_TYPE; 535 goto out; 536 } 537 } 538 539 state = elm[ACPIBAT_BST_STATE].Integer.Value; 540 if ((state & ACPIBAT_ST_CHARGING) != 0) { 541 /* XXX rate can be invalid */ 542 rate = elm[ACPIBAT_BST_RATE].Integer.Value; 543 sc->sc_sensor[ACPIBAT_CHARGERATE].state = ENVSYS_SVALID; 544 sc->sc_sensor[ACPIBAT_CHARGERATE].value_cur = rate * 1000; 545 sc->sc_sensor[ACPIBAT_DISCHARGERATE].state = ENVSYS_SINVALID; 546 sc->sc_sensor[ACPIBAT_CHARGING].state = ENVSYS_SVALID; 547 sc->sc_sensor[ACPIBAT_CHARGING].value_cur = 1; 548 } else if ((state & ACPIBAT_ST_DISCHARGING) != 0) { 549 rate = elm[ACPIBAT_BST_RATE].Integer.Value; 550 sc->sc_sensor[ACPIBAT_DISCHARGERATE].state = ENVSYS_SVALID; 551 sc->sc_sensor[ACPIBAT_DISCHARGERATE].value_cur = rate * 1000; 552 sc->sc_sensor[ACPIBAT_CHARGERATE].state = ENVSYS_SINVALID; 553 sc->sc_sensor[ACPIBAT_CHARGING].state = ENVSYS_SVALID; 554 sc->sc_sensor[ACPIBAT_CHARGING].value_cur = 0; 555 } else { 556 sc->sc_sensor[ACPIBAT_CHARGING].state = ENVSYS_SVALID; 557 sc->sc_sensor[ACPIBAT_CHARGING].value_cur = 0; 558 sc->sc_sensor[ACPIBAT_CHARGERATE].state = ENVSYS_SINVALID; 559 sc->sc_sensor[ACPIBAT_DISCHARGERATE].state = ENVSYS_SINVALID; 560 } 561 562 /* Remaining capacity. */ 563 val = elm[ACPIBAT_BST_CAPACITY].Integer.Value; 564 sc->sc_sensor[ACPIBAT_CAPACITY].value_cur = val * 1000; 565 sc->sc_sensor[ACPIBAT_CAPACITY].state = ACPIBAT_VAL_ISVALID(val); 566 567 /* Battery voltage. */ 568 val = elm[ACPIBAT_BST_VOLTAGE].Integer.Value; 569 sc->sc_sensor[ACPIBAT_VOLTAGE].value_cur = val * 1000; 570 sc->sc_sensor[ACPIBAT_VOLTAGE].state = ACPIBAT_VAL_ISVALID(val); 571 572 sc->sc_sensor[ACPIBAT_CHARGE_STATE].state = ENVSYS_SVALID; 573 sc->sc_sensor[ACPIBAT_CHARGE_STATE].value_cur = 574 ENVSYS_BATTERY_CAPACITY_NORMAL; 575 576 if (sc->sc_sensor[ACPIBAT_CAPACITY].value_cur < sc->sc_wcapacity) { 577 sc->sc_sensor[ACPIBAT_CAPACITY].state = ENVSYS_SWARNUNDER; 578 sc->sc_sensor[ACPIBAT_CHARGE_STATE].value_cur = 579 ENVSYS_BATTERY_CAPACITY_WARNING; 580 } 581 582 if (sc->sc_sensor[ACPIBAT_CAPACITY].value_cur < sc->sc_lcapacity) { 583 sc->sc_sensor[ACPIBAT_CAPACITY].state = ENVSYS_SCRITUNDER; 584 sc->sc_sensor[ACPIBAT_CHARGE_STATE].value_cur = 585 ENVSYS_BATTERY_CAPACITY_LOW; 586 } 587 588 if ((state & ACPIBAT_ST_CRITICAL) != 0) { 589 sc->sc_sensor[ACPIBAT_CAPACITY].state = ENVSYS_SCRITICAL; 590 sc->sc_sensor[ACPIBAT_CHARGE_STATE].value_cur = 591 ENVSYS_BATTERY_CAPACITY_CRITICAL; 592 } 593 594 out: 595 if (obj != NULL) 596 ACPI_FREE(obj); 597 598 if (ACPI_FAILURE(rv)) 599 aprint_error_dev(dv, "failed to evaluate _BST: %s\n", 600 AcpiFormatException(rv)); 601 } 602 603 static void 604 acpibat_update_info(void *arg) 605 { 606 device_t dv = arg; 607 struct acpibat_softc *sc = device_private(dv); 608 int i, rv; 609 610 mutex_enter(&sc->sc_mutex); 611 612 /* Don't touch sc_sme if we're detaching. */ 613 if (sc->sc_dying) 614 goto out; 615 616 rv = acpibat_get_sta(dv); 617 if (rv > 0) { 618 acpibat_get_info(dv); 619 620 /* 621 * If the status changed, update the limits. 622 */ 623 if (sc->sc_present == 0 && 624 sc->sc_sensor[ACPIBAT_CAPACITY].value_max > 0) 625 sysmon_envsys_update_limits(sc->sc_sme, 626 &sc->sc_sensor[ACPIBAT_CAPACITY]); 627 } else { 628 i = (rv < 0) ? 0 : ACPIBAT_DVOLTAGE; 629 while (i < ACPIBAT_COUNT) { 630 sc->sc_sensor[i].state = ENVSYS_SINVALID; 631 i++; 632 } 633 } 634 635 sc->sc_present = rv; 636 out: 637 mutex_exit(&sc->sc_mutex); 638 } 639 640 static void 641 acpibat_update_status(void *arg) 642 { 643 device_t dv = arg; 644 struct acpibat_softc *sc = device_private(dv); 645 int i, rv; 646 647 mutex_enter(&sc->sc_mutex); 648 649 rv = acpibat_get_sta(dv); 650 if (rv > 0) { 651 if (sc->sc_present == 0) 652 acpibat_get_info(dv); 653 acpibat_get_status(dv); 654 } else { 655 i = (rv < 0) ? 0 : ACPIBAT_DVOLTAGE; 656 while (i < ACPIBAT_COUNT) { 657 sc->sc_sensor[i].state = ENVSYS_SINVALID; 658 i++; 659 } 660 } 661 662 sc->sc_present = rv; 663 microtime(&sc->sc_last); 664 665 cv_broadcast(&sc->sc_condvar); 666 mutex_exit(&sc->sc_mutex); 667 } 668 669 /* 670 * acpibat_notify_handler: 671 * 672 * Callback from ACPI interrupt handler to notify us of an event. 673 */ 674 static void 675 acpibat_notify_handler(ACPI_HANDLE handle, uint32_t notify, void *context) 676 { 677 static const int handler = OSL_NOTIFY_HANDLER; 678 device_t dv = context; 679 680 switch (notify) { 681 case ACPI_NOTIFY_BUS_CHECK: 682 break; 683 case ACPI_NOTIFY_BAT_INFO: 684 case ACPI_NOTIFY_DEVICE_CHECK: 685 (void)AcpiOsExecute(handler, acpibat_update_info, dv); 686 break; 687 case ACPI_NOTIFY_BAT_STATUS: 688 (void)AcpiOsExecute(handler, acpibat_update_status, dv); 689 break; 690 default: 691 aprint_error_dev(dv, "unknown notify: 0x%02X\n", notify); 692 } 693 } 694 695 static void 696 acpibat_init_envsys(device_t dv) 697 { 698 struct acpibat_softc *sc = device_private(dv); 699 int i; 700 701 #define INITDATA(index, unit, string) \ 702 do { \ 703 sc->sc_sensor[index].state = ENVSYS_SVALID; \ 704 sc->sc_sensor[index].units = unit; \ 705 (void)strlcpy(sc->sc_sensor[index].desc, string, \ 706 sizeof(sc->sc_sensor[index].desc)); \ 707 } while (/* CONSTCOND */ 0) 708 709 INITDATA(ACPIBAT_PRESENT, ENVSYS_INDICATOR, "present"); 710 INITDATA(ACPIBAT_DCAPACITY, ENVSYS_SWATTHOUR, "design cap"); 711 INITDATA(ACPIBAT_LFCCAPACITY, ENVSYS_SWATTHOUR, "last full cap"); 712 INITDATA(ACPIBAT_DVOLTAGE, ENVSYS_SVOLTS_DC, "design voltage"); 713 INITDATA(ACPIBAT_VOLTAGE, ENVSYS_SVOLTS_DC, "voltage"); 714 INITDATA(ACPIBAT_CHARGERATE, ENVSYS_SWATTS, "charge rate"); 715 INITDATA(ACPIBAT_DISCHARGERATE, ENVSYS_SWATTS, "discharge rate"); 716 INITDATA(ACPIBAT_CAPACITY, ENVSYS_SWATTHOUR, "charge"); 717 INITDATA(ACPIBAT_CHARGING, ENVSYS_BATTERY_CHARGE, "charging"); 718 INITDATA(ACPIBAT_CHARGE_STATE, ENVSYS_BATTERY_CAPACITY, "charge state"); 719 720 #undef INITDATA 721 722 sc->sc_sensor[ACPIBAT_CHARGE_STATE].value_cur = 723 ENVSYS_BATTERY_CAPACITY_NORMAL; 724 725 sc->sc_sensor[ACPIBAT_CAPACITY].flags |= 726 ENVSYS_FPERCENT | ENVSYS_FVALID_MAX | ENVSYS_FMONLIMITS; 727 728 sc->sc_sensor[ACPIBAT_CHARGE_STATE].flags |= ENVSYS_FMONSTCHANGED; 729 730 /* Disable userland monitoring on these sensors. */ 731 sc->sc_sensor[ACPIBAT_VOLTAGE].flags = ENVSYS_FMONNOTSUPP; 732 sc->sc_sensor[ACPIBAT_CHARGERATE].flags = ENVSYS_FMONNOTSUPP; 733 sc->sc_sensor[ACPIBAT_DISCHARGERATE].flags = ENVSYS_FMONNOTSUPP; 734 sc->sc_sensor[ACPIBAT_DCAPACITY].flags = ENVSYS_FMONNOTSUPP; 735 sc->sc_sensor[ACPIBAT_LFCCAPACITY].flags = ENVSYS_FMONNOTSUPP; 736 sc->sc_sensor[ACPIBAT_DVOLTAGE].flags = ENVSYS_FMONNOTSUPP; 737 738 /* Attach rnd(9) to the (dis)charge rates. */ 739 sc->sc_sensor[ACPIBAT_CHARGERATE].flags |= ENVSYS_FHAS_ENTROPY; 740 sc->sc_sensor[ACPIBAT_DISCHARGERATE].flags |= ENVSYS_FHAS_ENTROPY; 741 742 sc->sc_sme = sysmon_envsys_create(); 743 744 for (i = 0; i < ACPIBAT_COUNT; i++) { 745 if (sysmon_envsys_sensor_attach(sc->sc_sme, 746 &sc->sc_sensor[i])) 747 goto fail; 748 } 749 750 sc->sc_sme->sme_name = device_xname(dv); 751 sc->sc_sme->sme_cookie = dv; 752 sc->sc_sme->sme_refresh = acpibat_refresh; 753 sc->sc_sme->sme_class = SME_CLASS_BATTERY; 754 sc->sc_sme->sme_flags = SME_POLL_ONLY; 755 sc->sc_sme->sme_get_limits = acpibat_get_limits; 756 757 if (sysmon_envsys_register(sc->sc_sme)) 758 goto fail; 759 760 (void)acpi_register_notify(sc->sc_node, acpibat_notify_handler); 761 acpibat_update_info(dv); 762 acpibat_update_status(dv); 763 764 (void)pmf_device_register(dv, NULL, acpibat_resume); 765 766 return; 767 fail: 768 aprint_error_dev(dv, "failed to initialize sysmon\n"); 769 770 sysmon_envsys_destroy(sc->sc_sme); 771 kmem_free(sc->sc_sensor, ACPIBAT_COUNT * sizeof(*sc->sc_sensor)); 772 773 sc->sc_sme = NULL; 774 sc->sc_sensor = NULL; 775 } 776 777 static void 778 acpibat_refresh(struct sysmon_envsys *sme, envsys_data_t *edata) 779 { 780 device_t self = sme->sme_cookie; 781 struct acpibat_softc *sc; 782 struct timeval tv, tmp; 783 ACPI_STATUS rv; 784 785 sc = device_private(self); 786 787 tmp.tv_sec = 10; 788 tmp.tv_usec = 0; 789 790 microtime(&tv); 791 timersub(&tv, &tmp, &tv); 792 if (timercmp(&tv, &sc->sc_last, <) != 0) 793 return; 794 795 if (mutex_tryenter(&sc->sc_mutex) == 0) 796 return; 797 798 rv = AcpiOsExecute(OSL_NOTIFY_HANDLER, acpibat_update_status, self); 799 if (ACPI_SUCCESS(rv)) 800 cv_timedwait(&sc->sc_condvar, &sc->sc_mutex, hz); 801 802 mutex_exit(&sc->sc_mutex); 803 } 804 805 static bool 806 acpibat_resume(device_t dv, const pmf_qual_t *qual) 807 { 808 809 (void)AcpiOsExecute(OSL_NOTIFY_HANDLER, acpibat_update_info, dv); 810 (void)AcpiOsExecute(OSL_NOTIFY_HANDLER, acpibat_update_status, dv); 811 812 return true; 813 } 814 815 static void 816 acpibat_get_limits(struct sysmon_envsys *sme, envsys_data_t *edata, 817 sysmon_envsys_lim_t *limits, uint32_t *props) 818 { 819 device_t dv = sme->sme_cookie; 820 struct acpibat_softc *sc = device_private(dv); 821 822 if (edata->sensor != ACPIBAT_CAPACITY) 823 return; 824 825 limits->sel_critmin = sc->sc_lcapacity; 826 limits->sel_warnmin = sc->sc_wcapacity; 827 828 *props |= PROP_BATTCAP | PROP_BATTWARN | PROP_DRIVER_LIMITS; 829 } 830 831 MODULE(MODULE_CLASS_DRIVER, acpibat, "sysmon_envsys"); 832 833 #ifdef _MODULE 834 #include "ioconf.c" 835 #endif 836 837 static int 838 acpibat_modcmd(modcmd_t cmd, void *aux) 839 { 840 int rv = 0; 841 842 switch (cmd) { 843 case MODULE_CMD_INIT: 844 #ifdef _MODULE 845 rv = config_init_component(cfdriver_ioconf_acpibat, 846 cfattach_ioconf_acpibat, cfdata_ioconf_acpibat); 847 #endif 848 break; 849 case MODULE_CMD_FINI: 850 #ifdef _MODULE 851 rv = config_fini_component(cfdriver_ioconf_acpibat, 852 cfattach_ioconf_acpibat, cfdata_ioconf_acpibat); 853 #endif 854 break; 855 default: 856 rv = ENOTTY; 857 } 858 859 return rv; 860 } 861