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