1 /* $NetBSD: acpi_pmtr.c,v 1.9 2021/01/29 15:49:55 thorpej Exp $ */ 2 3 /*- 4 * Copyright (c) 2011 Jukka Ruohonen <jruohonen@iki.fi> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 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 AUTHOR 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 AUTHOR 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 #include <sys/cdefs.h> 30 __KERNEL_RCSID(0, "$NetBSD: acpi_pmtr.c,v 1.9 2021/01/29 15:49:55 thorpej Exp $"); 31 32 #include <sys/param.h> 33 #include <sys/module.h> 34 #include <sys/mutex.h> 35 36 #include <dev/acpi/acpireg.h> 37 #include <dev/acpi/acpivar.h> 38 39 #include <dev/sysmon/sysmonvar.h> 40 41 #define _COMPONENT ACPI_RESOURCE_COMPONENT 42 ACPI_MODULE_NAME ("acpi_pmtr") 43 44 #define ACPIPMTR_CAP_FLAGS 0 45 #define ACPIPMTR_CAP_UNIT 1 46 #define ACPIPMTR_CAP_TYPE 2 47 #define ACPIPMTR_CAP_ACCURACY 3 48 #define ACPIPMTR_CAP_SAMPLING 4 49 #define ACPIPMTR_CAP_IVAL_MIN 5 50 #define ACPIPMTR_CAP_IVAL_MAX 6 51 #define ACPIPMTR_CAP_HYSTERESIS 7 52 #define ACPIPMTR_CAP_HWLIMIT 8 53 #define ACPIPMTR_CAP_HWLIMIT_MIN 9 54 #define ACPIPMTR_CAP_HWLIMIT_MAX 10 55 #define ACPIPMTR_CAP_COUNT 11 56 /* ACPIPMTR_CAP_MODEL 11 */ 57 /* ACPIPMTR_CAP_SERIAL 12 */ 58 /* ACPIPMTR_CAP_OEM 13 */ 59 60 #define ACPIPMTR_FLAGS_MEASURE __BIT(0) 61 #define ACPIPMTR_FLAGS_TRIP __BIT(1) 62 #define ACPIPMTR_FLAGS_HWLIMIT __BIT(2) 63 #define ACPIPMTR_FLAGS_NOTIFY __BIT(3) 64 #define ACPIPMTR_FLAGS_DISCHARGE __BIT(8) 65 66 #define ACPIPMTR_POWER_INPUT 0x00 67 #define ACPIPMTR_POWER_OUTPUT 0x01 68 69 #define ACPIPMTR_NOTIFY_CAP 0x80 70 #define ACPIPMTR_NOTIFY_TRIP 0x81 71 #define ACPIPMTR_NOTIFY_HWLIMIT1 0x82 72 #define ACPIPMTR_NOTIFY_HWLIMIT2 0x83 73 #define ACPIPMTR_NOTIFY_INTERVAL 0x84 74 75 struct acpipmtr_softc { 76 device_t sc_dev; 77 struct acpi_devnode *sc_node; 78 struct sysmon_envsys *sc_sme; 79 envsys_data_t sc_sensor_i; 80 envsys_data_t sc_sensor_o; 81 uint32_t sc_cap[ACPIPMTR_CAP_COUNT]; 82 int32_t sc_interval; 83 kmutex_t sc_mtx; 84 }; 85 86 static const struct device_compatible_entry compat_data[] = { 87 { .compat = "ACPI000D" }, 88 DEVICE_COMPAT_EOL 89 }; 90 91 static int acpipmtr_match(device_t, cfdata_t, void *); 92 static void acpipmtr_attach(device_t, device_t, void *); 93 static int acpipmtr_detach(device_t, int); 94 static bool acpipmtr_cap_get(device_t, bool); 95 static bool acpipmtr_dev_print(device_t); 96 static bool acpipmtr_sensor_init(device_t); 97 static void acpipmtr_sensor_type(device_t); 98 static int32_t acpipmtr_sensor_get(device_t, const char *); 99 static int32_t acpipmtr_sensor_get_reading(device_t); 100 static int32_t acpipmtr_sensor_get_interval(device_t); 101 static void acpipmtr_sensor_refresh(struct sysmon_envsys*,envsys_data_t *); 102 static void acpipmtr_notify(ACPI_HANDLE, uint32_t, void *); 103 104 CFATTACH_DECL_NEW(acpipmtr, sizeof(struct acpipmtr_softc), 105 acpipmtr_match, acpipmtr_attach, acpipmtr_detach, NULL); 106 107 static int 108 acpipmtr_match(device_t parent, cfdata_t match, void *aux) 109 { 110 struct acpi_attach_args *aa = aux; 111 112 return acpi_compatible_match(aa, compat_data); 113 } 114 115 static void 116 acpipmtr_attach(device_t parent, device_t self, void *aux) 117 { 118 struct acpipmtr_softc *sc = device_private(self); 119 struct acpi_attach_args *aa = aux; 120 uint32_t acc; 121 122 sc->sc_sme = NULL; 123 sc->sc_dev = self; 124 sc->sc_node = aa->aa_node; 125 126 aprint_naive("\n"); 127 aprint_normal(": ACPI Power Meter\n"); 128 129 (void)pmf_device_register(self, NULL, NULL); 130 mutex_init(&sc->sc_mtx, MUTEX_DEFAULT, IPL_NONE); 131 132 if (acpipmtr_cap_get(self, true) != true) 133 return; 134 135 if (acpipmtr_sensor_init(self) != true) 136 return; 137 138 (void)acpipmtr_dev_print(self); 139 (void)acpi_register_notify(sc->sc_node, acpipmtr_notify); 140 141 if ((acc = sc->sc_cap[ACPIPMTR_CAP_ACCURACY]) == 0) 142 acc = 100000; 143 144 aprint_verbose_dev(self, 145 "measuring %s power at %u.%u %% accuracy, %u ms sampling\n", 146 (sc->sc_cap[ACPIPMTR_CAP_TYPE] != 0) ? "output" : "input", 147 acc / 1000, acc % 1000, sc->sc_cap[ACPIPMTR_CAP_SAMPLING]); 148 149 aprint_debug_dev(self, "%s hw-limits, capabilities 0x%02x\n", 150 (sc->sc_cap[ACPIPMTR_CAP_HWLIMIT] != 0) ? "rw" : "ro", 151 sc->sc_cap[ACPIPMTR_CAP_FLAGS]); 152 } 153 154 static int 155 acpipmtr_detach(device_t self, int flags) 156 { 157 struct acpipmtr_softc *sc = device_private(self); 158 159 pmf_device_deregister(self); 160 acpi_deregister_notify(sc->sc_node); 161 162 if (sc->sc_sme != NULL) 163 sysmon_envsys_unregister(sc->sc_sme); 164 165 mutex_destroy(&sc->sc_mtx); 166 167 return 0; 168 } 169 170 static bool 171 acpipmtr_cap_get(device_t self, bool print) 172 { 173 struct acpipmtr_softc *sc = device_private(self); 174 ACPI_OBJECT *elm, *obj; 175 ACPI_BUFFER buf; 176 ACPI_STATUS rv; 177 uint32_t i; 178 179 for (i = 0; i < __arraycount(sc->sc_cap); i++) 180 sc->sc_cap[i] = 0; 181 182 rv = acpi_eval_struct(sc->sc_node->ad_handle, "_PMC", &buf); 183 184 if (ACPI_FAILURE(rv)) 185 goto out; 186 187 obj = buf.Pointer; 188 189 if (obj->Type != ACPI_TYPE_PACKAGE) { 190 rv = AE_TYPE; 191 goto out; 192 } 193 194 elm = obj->Package.Elements; 195 196 if (obj->Package.Count != 14) { 197 rv = AE_LIMIT; 198 goto out; 199 } 200 201 CTASSERT(__arraycount(sc->sc_cap) == 11); 202 203 for (i = 0; i < __arraycount(sc->sc_cap); i++) { 204 205 if (elm[i].Type != ACPI_TYPE_INTEGER) { 206 rv = AE_TYPE; 207 goto out; 208 } 209 210 if (elm[i].Integer.Value > UINT32_MAX) { 211 rv = AE_AML_NUMERIC_OVERFLOW; 212 goto out; 213 } 214 215 sc->sc_cap[i] = elm[i].Integer.Value; 216 } 217 218 if (print != true) 219 goto out; 220 221 for (; i < 14; i++) { 222 223 if (elm[i].Type != ACPI_TYPE_STRING) 224 goto out; 225 226 if (elm[i].String.Pointer == NULL) 227 goto out; 228 229 if (elm[i].String.Pointer[0] == '\0') 230 goto out; 231 } 232 233 aprint_debug_dev(self, "%s, serial %s, " 234 "model %s\n", elm[13].String.Pointer, 235 elm[12].String.Pointer, elm[11].String.Pointer); 236 237 out: 238 if (ACPI_FAILURE(rv)) 239 aprint_error_dev(self, "failed to evaluate _PMC: %s\n", 240 AcpiFormatException(rv)); 241 242 if (buf.Pointer != NULL) 243 ACPI_FREE(buf.Pointer); 244 245 return (rv != AE_OK) ? false : true; 246 } 247 248 static bool 249 acpipmtr_dev_print(device_t self) 250 { 251 struct acpipmtr_softc *sc = device_private(self); 252 struct acpi_devnode *ad; 253 ACPI_OBJECT *elm, *obj; 254 ACPI_BUFFER buf; 255 ACPI_HANDLE hdl; 256 ACPI_STATUS rv; 257 uint32_t i, n; 258 259 /* 260 * The _PMD method returns a package of devices whose total power 261 * drawn should roughly correspond with the readings from the meter. 262 */ 263 rv = acpi_eval_struct(sc->sc_node->ad_handle, "_PMD", &buf); 264 265 if (ACPI_FAILURE(rv)) 266 goto out; 267 268 obj = buf.Pointer; 269 270 if (obj->Type != ACPI_TYPE_PACKAGE) { 271 rv = AE_TYPE; 272 goto out; 273 } 274 275 n = obj->Package.Count; 276 277 if (n == 0) { 278 rv = AE_LIMIT; 279 goto out; 280 } 281 282 aprint_debug_dev(self, "measured devices: "); 283 284 for (i = 0; i < n; i++) { 285 286 elm = &obj->Package.Elements[i]; 287 rv = acpi_eval_reference_handle(elm, &hdl); 288 289 if (ACPI_FAILURE(rv)) 290 continue; 291 292 ad = acpi_match_node(hdl); 293 294 if (ad == NULL) 295 continue; 296 297 aprint_debug("%s ", ad->ad_name); 298 } 299 300 aprint_debug("\n"); 301 302 out: 303 if (ACPI_FAILURE(rv)) 304 aprint_debug_dev(self, "failed to evaluate _PMD: %s\n", 305 AcpiFormatException(rv)); 306 307 if (buf.Pointer != NULL) 308 ACPI_FREE(buf.Pointer); 309 310 return (rv != AE_OK) ? false : true; 311 } 312 313 static bool 314 acpipmtr_sensor_init(device_t self) 315 { 316 struct acpipmtr_softc *sc = device_private(self); 317 const size_t siz = sizeof(sc->sc_sensor_i.desc); 318 int32_t val; 319 320 val = acpipmtr_sensor_get_reading(self); 321 sc->sc_interval = acpipmtr_sensor_get_interval(self); 322 323 if (val < 0) { 324 aprint_error_dev(self, "failed to get sensor reading\n"); 325 return false; 326 } 327 328 /* Always mW in ACPI 4.0. */ 329 if (sc->sc_cap[ACPIPMTR_CAP_UNIT] != 0) 330 aprint_error_dev(self, "invalid measurement unit\n"); 331 332 sc->sc_sme = sysmon_envsys_create(); 333 334 sc->sc_sensor_i.units = ENVSYS_SWATTS; 335 sc->sc_sensor_o.units = ENVSYS_SWATTS; 336 sc->sc_sensor_i.value_cur = val * 1000; 337 sc->sc_sensor_o.value_cur = val * 1000; 338 339 acpipmtr_sensor_type(self); 340 341 (void)strlcpy(sc->sc_sensor_i.desc, "input power", siz); 342 (void)strlcpy(sc->sc_sensor_o.desc, "output power", siz); 343 344 sc->sc_sme->sme_cookie = self; 345 sc->sc_sme->sme_flags = SME_POLL_ONLY; 346 sc->sc_sme->sme_name = device_xname(self); 347 sc->sc_sme->sme_refresh = acpipmtr_sensor_refresh; 348 349 if (sysmon_envsys_sensor_attach(sc->sc_sme, &sc->sc_sensor_i) != 0) 350 goto fail; 351 352 if (sysmon_envsys_sensor_attach(sc->sc_sme, &sc->sc_sensor_o) != 0) 353 goto fail; 354 355 if (sysmon_envsys_register(sc->sc_sme) != 0) 356 goto fail; 357 358 return true; 359 360 fail: 361 aprint_error_dev(self, "failed to initialize sysmon\n"); 362 363 sysmon_envsys_destroy(sc->sc_sme); 364 sc->sc_sme = NULL; 365 366 return false; 367 } 368 369 static void 370 acpipmtr_sensor_type(device_t self) 371 { 372 struct acpipmtr_softc *sc = device_private(self); 373 374 mutex_enter(&sc->sc_mtx); 375 376 switch (sc->sc_cap[ACPIPMTR_CAP_TYPE]) { 377 378 case ACPIPMTR_POWER_INPUT: 379 sc->sc_sensor_i.state = ENVSYS_SVALID; 380 sc->sc_sensor_o.state = ENVSYS_SINVALID; 381 break; 382 383 case ACPIPMTR_POWER_OUTPUT: 384 sc->sc_sensor_i.state = ENVSYS_SINVALID; 385 sc->sc_sensor_o.state = ENVSYS_SVALID; 386 break; 387 388 default: 389 sc->sc_sensor_i.state = ENVSYS_SINVALID; 390 sc->sc_sensor_o.state = ENVSYS_SINVALID; 391 break; 392 } 393 394 mutex_exit(&sc->sc_mtx); 395 } 396 397 static int32_t 398 acpipmtr_sensor_get(device_t self, const char *path) 399 { 400 struct acpipmtr_softc *sc = device_private(self); 401 ACPI_INTEGER val = 0; 402 ACPI_STATUS rv; 403 404 rv = acpi_eval_integer(sc->sc_node->ad_handle, path, &val); 405 406 if (ACPI_FAILURE(rv)) 407 goto fail; 408 409 if (val == 0 || val > INT32_MAX) { 410 rv = AE_LIMIT; 411 goto fail; 412 } 413 414 return val; 415 416 fail: 417 aprint_debug_dev(self, "failed to evaluate " 418 "%s: %s\n", path, AcpiFormatException(rv)); 419 420 return -1; 421 } 422 423 static int32_t 424 acpipmtr_sensor_get_reading(device_t self) 425 { 426 return acpipmtr_sensor_get(self, "_PMM"); 427 } 428 429 static int32_t 430 acpipmtr_sensor_get_interval(device_t self) 431 { 432 return acpipmtr_sensor_get(self, "_GAI"); 433 } 434 435 static void 436 acpipmtr_sensor_refresh(struct sysmon_envsys *sme, envsys_data_t *edata) 437 { 438 device_t self = sme->sme_cookie; 439 struct acpipmtr_softc *sc; 440 int32_t val; 441 442 sc = device_private(self); 443 444 sc->sc_sensor_i.state = ENVSYS_SINVALID; 445 sc->sc_sensor_o.state = ENVSYS_SINVALID; 446 447 val = acpipmtr_sensor_get_reading(self) * 1000; 448 449 if (val < 0) 450 return; 451 452 sc->sc_sensor_i.value_cur = val; 453 sc->sc_sensor_o.value_cur = val; 454 455 acpipmtr_sensor_type(self); 456 } 457 458 static void 459 acpipmtr_notify(ACPI_HANDLE hdl, uint32_t evt, void *aux) 460 { 461 struct acpipmtr_softc *sc; 462 device_t self = aux; 463 int32_t val; 464 465 sc = device_private(self); 466 467 switch (evt) { 468 469 case ACPIPMTR_NOTIFY_CAP: 470 471 mutex_enter(&sc->sc_mtx); 472 473 if (acpipmtr_cap_get(self, false) != true) { 474 mutex_exit(&sc->sc_mtx); 475 break; 476 } 477 478 mutex_exit(&sc->sc_mtx); 479 480 acpipmtr_sensor_type(self); 481 break; 482 483 case ACPIPMTR_NOTIFY_INTERVAL: 484 val = acpipmtr_sensor_get_interval(self); 485 486 if (val < 0 || val == sc->sc_interval) 487 break; 488 489 aprint_debug_dev(self, "averaging interval changed " 490 "from %u ms to %u ms\n", sc->sc_interval, val); 491 492 sc->sc_interval = val; 493 break; 494 495 case ACPIPMTR_NOTIFY_TRIP: /* AE_SUPPORT */ 496 case ACPIPMTR_NOTIFY_HWLIMIT1: /* AE_SUPPORT */ 497 case ACPIPMTR_NOTIFY_HWLIMIT2: /* AE_SUPPORT */ 498 break; 499 500 default: 501 aprint_debug_dev(self, "unknown notify 0x%02x\n", evt); 502 } 503 } 504 505 MODULE(MODULE_CLASS_DRIVER, acpipmtr, "sysmon_envsys"); 506 507 #ifdef _MODULE 508 #include "ioconf.c" 509 #endif 510 511 static int 512 acpipmtr_modcmd(modcmd_t cmd, void *aux) 513 { 514 int rv = 0; 515 516 switch (cmd) { 517 518 case MODULE_CMD_INIT: 519 520 #ifdef _MODULE 521 rv = config_init_component(cfdriver_ioconf_acpipmtr, 522 cfattach_ioconf_acpipmtr, cfdata_ioconf_acpipmtr); 523 #endif 524 break; 525 526 case MODULE_CMD_FINI: 527 528 #ifdef _MODULE 529 rv = config_fini_component(cfdriver_ioconf_acpipmtr, 530 cfattach_ioconf_acpipmtr, cfdata_ioconf_acpipmtr); 531 #endif 532 break; 533 534 default: 535 rv = ENOTTY; 536 } 537 538 return rv; 539 } 540