1 /* 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2024-2026 The FreeBSD Foundation 5 * 6 * This software was developed by Aymeric Wibo <obiwac@freebsd.org> 7 * under sponsorship from the FreeBSD Foundation. 8 * 9 * Portions of this software were developed by Olivier Certner 10 * <olce@FreeBSD.org> at Kumacom SARL under sponsorship from the FreeBSD 11 * Foundation. 12 */ 13 14 #include <sys/param.h> 15 #include <sys/bus.h> 16 #include <sys/eventhandler.h> 17 #include <sys/kernel.h> 18 #include <sys/malloc.h> 19 #include <sys/module.h> 20 #include <sys/uuid.h> 21 22 #include <machine/_inttypes.h> 23 24 #include <contrib/dev/acpica/include/acpi.h> 25 #include <contrib/dev/acpica/include/accommon.h> 26 27 #include <dev/acpica/acpivar.h> 28 29 30 /* Hooks for the ACPI CA debugging infrastructure */ 31 #define _COMPONENT ACPI_SPMC 32 ACPI_MODULE_NAME("SPMC") 33 34 static char *spmc_ids[] = { 35 "PNP0D80", 36 NULL 37 }; 38 39 /* sysctl(8) knobs */ 40 static SYSCTL_NODE(_debug_acpi, OID_AUTO, spmc, CTLFLAG_RD | CTLFLAG_MPSAFE, 41 NULL, "SPMC debugging"); 42 43 int8_t dsm_intel_revision = -15; 44 SYSCTL_S8(_debug_acpi_spmc, OID_AUTO, intel_dsm_revision, CTLFLAG_RW, 45 &dsm_intel_revision, 0, 46 "Revision to use with the Intel DSM " 47 "(negative: auto, try from 0 to minus the value)"); 48 49 int8_t dsm_amd_revision = -15; 50 SYSCTL_S8(_debug_acpi_spmc, OID_AUTO, amd_dsm_revision, CTLFLAG_RW, 51 &dsm_amd_revision, 0, 52 "Revision to use with the AMD DSM " 53 "(negative: auto, try from 0 to minus the value)"); 54 55 int8_t dsm_ms_revision = -15; 56 SYSCTL_S8(_debug_acpi_spmc, OID_AUTO, ms_dsm_revision, CTLFLAG_RW, 57 &dsm_ms_revision, 0, 58 "Revision to use with the Microsoft DSM " 59 "(negative: auto, try from 0 to minus the value)"); 60 61 static int verbose; 62 SYSCTL_INT(_debug_acpi_spmc, OID_AUTO, verbose, CTLFLAG_RW, 63 &verbose, 0, "acpi_spmc(4) verbosity"); 64 65 #define VERBOSE() (verbose || bootverbose) 66 67 static bool force_call_expected_functions; 68 SYSCTL_BOOL(_debug_acpi_spmc, OID_AUTO, always_call_expected_functions, 69 CTLFLAG_RW, &force_call_expected_functions, 0, 70 "Call all expected functions on a present DSM, even those not enumerated."); 71 72 /* Conversion of an index to a mask. */ 73 #define IDX_TO_BIT(idx) (1ull << (idx)) 74 75 /* List of supported DSMs. */ 76 #define DSM_INTEL 0 77 #define DSM_MS 1 78 #define DSM_AMD 2 79 80 /* List of DSM function indices. */ 81 #define DSM_ENUM_FUNCTIONS 0 /* Common to all DSMs */ 82 #define DSM_GET_DEVICE_CONSTRAINTS 1 /* AMD and Intel, MS N/A */ 83 84 #define DSM_GET_CRASH_DUMP_DEVICE 2 /* Intel, MS N/A */ 85 #define DSM_INTEL_MS_DISPLAY_OFF_NOTIF 3 86 #define DSM_INTEL_MS_DISPLAY_ON_NOTIF 4 87 #define DSM_INTEL_MS_LPI_ENTRY_NOTIF 5 88 #define DSM_INTEL_MS_LPI_EXIT_NOTIF 6 89 90 #define DSM_MS_SLEEP_ENTRY_NOTIF 7 91 #define DSM_MS_SLEEP_EXIT_NOTIF 8 92 #define DSM_MS_TURN_ON_DISPLAY 9 93 94 #define DSM_AMD_LPI_ENTRY_NOTIF 2 95 #define DSM_AMD_LPI_EXIT_NOTIF 3 96 #define DSM_AMD_DISPLAY_OFF_NOTIF 4 97 #define DSM_AMD_DISPLAY_ON_NOTIF 5 98 99 100 /* Descriptors for the DSMs we support. */ 101 102 struct dsm_desc { 103 const char *name; 104 struct uuid uuid; 105 /* 106 * Points to an integer which, if negative, indicates to auto-detect the 107 * revision by trying all revisions between 0 and minus the value, else 108 * is the sole revision to try. 109 */ 110 const int8_t *revision_spec; 111 uint64_t expected_functions; 112 uint64_t extra_functions; 113 /* Human-friendly names of known functions. */ 114 const char *const *function_names; 115 int function_names_nb; 116 /* Index in the dsms[] array below. */ 117 int index; 118 }; 119 120 static const char *const dsm_intel_function_names[] = { 121 [DSM_GET_DEVICE_CONSTRAINTS] = "DEVICE_CONSTRAINTS", 122 [DSM_GET_CRASH_DUMP_DEVICE] = "CRASH_DUMP_DEVICE", 123 [DSM_INTEL_MS_DISPLAY_OFF_NOTIF] = "DISPLAY_OFF", 124 [DSM_INTEL_MS_DISPLAY_ON_NOTIF] = "DISPLAY_ON", 125 [DSM_INTEL_MS_LPI_ENTRY_NOTIF] = "LPI_ENTRY", 126 [DSM_INTEL_MS_LPI_EXIT_NOTIF] = "LPI_EXIT", 127 }; 128 129 static const struct dsm_desc dsm_intel = { 130 .index = DSM_INTEL, 131 .name = "Intel", 132 .uuid = { /* c4eb40a0-6cd2-11e2-bcfd-0800200c9a66 */ 133 0xc4eb40a0, 0x6cd2, 0x11e2, 0xbc, 0xfd, 134 {0x08, 0x00, 0x20, 0x0c, 0x9a, 0x66} 135 }, 136 .revision_spec = &dsm_intel_revision, 137 .expected_functions = 138 IDX_TO_BIT(DSM_GET_DEVICE_CONSTRAINTS) | 139 IDX_TO_BIT(DSM_INTEL_MS_DISPLAY_OFF_NOTIF) | 140 IDX_TO_BIT(DSM_INTEL_MS_DISPLAY_ON_NOTIF) | 141 IDX_TO_BIT(DSM_INTEL_MS_LPI_ENTRY_NOTIF) | 142 IDX_TO_BIT(DSM_INTEL_MS_LPI_EXIT_NOTIF), 143 .extra_functions = 144 IDX_TO_BIT(DSM_GET_CRASH_DUMP_DEVICE), /* Not used. */ 145 .function_names = dsm_intel_function_names, 146 .function_names_nb = nitems(dsm_intel_function_names), 147 }; 148 149 static const char *const dsm_ms_function_names[] = { 150 [DSM_INTEL_MS_DISPLAY_OFF_NOTIF] = "DISPLAY_OFF", 151 [DSM_INTEL_MS_DISPLAY_ON_NOTIF] = "DISPLAY_ON", 152 [DSM_INTEL_MS_LPI_ENTRY_NOTIF] = "LPI_ENTRY", 153 [DSM_INTEL_MS_LPI_EXIT_NOTIF] = "LPI_EXIT", 154 [DSM_MS_SLEEP_ENTRY_NOTIF] = "SLEEP_ENTRY", 155 [DSM_MS_SLEEP_EXIT_NOTIF] = "SLEEP_EXIT", 156 [DSM_MS_TURN_ON_DISPLAY] = "TURN_ON", 157 }; 158 159 static const struct dsm_desc dsm_ms = { 160 .index = DSM_MS, 161 .name = "Microsoft", 162 .uuid = { /* 11e00d56-ce64-47ce-837b-1f898f9aa461 */ 163 0x11e00d56, 0xce64, 0x47ce, 0x83, 0x7b, 164 {0x1f, 0x89, 0x8f, 0x9a, 0xa4, 0x61} 165 }, 166 .revision_spec = &dsm_ms_revision, 167 .expected_functions = 168 IDX_TO_BIT(DSM_INTEL_MS_DISPLAY_OFF_NOTIF) | 169 IDX_TO_BIT(DSM_INTEL_MS_DISPLAY_ON_NOTIF) | 170 IDX_TO_BIT(DSM_INTEL_MS_LPI_ENTRY_NOTIF) | 171 IDX_TO_BIT(DSM_INTEL_MS_LPI_EXIT_NOTIF) | 172 IDX_TO_BIT(DSM_MS_SLEEP_ENTRY_NOTIF) | 173 IDX_TO_BIT(DSM_MS_SLEEP_EXIT_NOTIF), 174 .extra_functions = 175 IDX_TO_BIT(DSM_MS_TURN_ON_DISPLAY), 176 .function_names = dsm_ms_function_names, 177 .function_names_nb = nitems(dsm_ms_function_names), 178 }; 179 180 static const char *const dsm_amd_function_names[] = { 181 [DSM_GET_DEVICE_CONSTRAINTS] = "DEVICE_CONSTRAINTS", 182 [DSM_AMD_DISPLAY_OFF_NOTIF] = "DISPLAY_OFF", 183 [DSM_AMD_DISPLAY_ON_NOTIF] = "DISPLAY_ON", 184 [DSM_AMD_LPI_ENTRY_NOTIF] = "LPI_ENTRY", 185 [DSM_AMD_LPI_EXIT_NOTIF] = "LPI_EXIT", 186 }; 187 188 static const struct dsm_desc dsm_amd = { 189 .index = DSM_AMD, 190 .name = "AMD", 191 .uuid = { /* e3f32452-febc-43ce-9039-932122d37721 */ 192 0xe3f32452, 0xfebc, 0x43ce, 0x90, 0x39, 193 {0x93, 0x21, 0x22, 0xd3, 0x77, 0x21} 194 }, 195 .revision_spec = &dsm_amd_revision, 196 .expected_functions = 197 IDX_TO_BIT(DSM_GET_DEVICE_CONSTRAINTS) | 198 IDX_TO_BIT(DSM_AMD_DISPLAY_OFF_NOTIF) | 199 IDX_TO_BIT(DSM_AMD_DISPLAY_ON_NOTIF) | 200 IDX_TO_BIT(DSM_AMD_LPI_ENTRY_NOTIF) | 201 IDX_TO_BIT(DSM_AMD_LPI_EXIT_NOTIF), 202 .function_names = dsm_amd_function_names, 203 .function_names_nb = nitems(dsm_amd_function_names), 204 }; 205 206 static const struct dsm_desc *const dsms[] = { 207 [DSM_INTEL] = &dsm_intel, 208 [DSM_MS] = &dsm_ms, 209 [DSM_AMD] = &dsm_amd, 210 }; 211 212 /* Per DSM probed information. */ 213 struct dsm_info { 214 uint64_t supported_functions; 215 /* 216 * Revisions are zero or a positive number. Strictly speaking, next 217 * field should be a 'uint64_t' as per the ACPI spec, but our ACPI DSM 218 * interface takes an 'int' and anyway actual revision numbers never 219 * even exceed the limits of a 'uint8_t'. 220 */ 221 uint8_t revision; 222 }; 223 224 struct acpi_spmc_constraint { 225 bool enabled; 226 char *name; 227 int min_d_state; 228 ACPI_HANDLE handle; 229 230 /* Intel only. Currently filled but unused. */ 231 uint64_t lpi_uid; 232 uint64_t min_dev_specific_state; 233 234 /* AMD only. Currently filled but unused. */ 235 uint64_t function_states; 236 }; 237 238 struct acpi_spmc_softc { 239 device_t dev; 240 ACPI_HANDLE handle; 241 struct dsm_info dsms_info[nitems(dsms)]; 242 243 struct eventhandler_entry *eh_suspend; 244 struct eventhandler_entry *eh_resume; 245 246 #ifdef INVARIANTS 247 bool get_constraints_succeeded; 248 #endif 249 size_t constraint_count; 250 struct acpi_spmc_constraint *constraints; 251 }; 252 253 254 static const struct dsm_desc * 255 resolve_dsm(int dsm_index) 256 { 257 MPASS(0 <= dsm_index && dsm_index < nitems(dsms)); 258 return (dsms[dsm_index]); 259 } 260 261 static struct dsm_info * 262 get_dsm_info(struct acpi_spmc_softc *const sc, const int dsm_index) 263 { 264 MPASS(0 <= dsm_index && dsm_index < nitems(dsms)); 265 return (&sc->dsms_info[dsm_index]); 266 } 267 268 static const struct dsm_info * 269 get_const_dsm_info(const struct acpi_spmc_softc *const sc, const int dsm_index) 270 { 271 MPASS(0 <= dsm_index && dsm_index < nitems(dsms)); 272 return (&sc->dsms_info[dsm_index]); 273 } 274 275 static const uint64_t 276 get_supported_functions(const struct acpi_spmc_softc *const sc, 277 const int dsm_index) 278 { 279 return (get_const_dsm_info(sc, dsm_index)->supported_functions); 280 } 281 282 static const uint8_t 283 get_revision(const struct acpi_spmc_softc *const sc, const int dsm_index) 284 { 285 return (get_const_dsm_info(sc, dsm_index)->revision); 286 } 287 288 static bool 289 supports_function_bitset(const uint64_t supported_functions, 290 const int function_index) 291 { 292 return ((supported_functions & IDX_TO_BIT(function_index)) != 0); 293 } 294 295 static bool 296 supports_function(const struct acpi_spmc_softc *const sc, const int dsm_index, 297 const int function_index) 298 { 299 return (supports_function_bitset(get_supported_functions(sc, dsm_index), 300 function_index)); 301 } 302 303 static bool 304 has_dsm_bitset(const uint64_t supported_functions) 305 { 306 /* DSM is supported if bit DSM_ENUM_FUNCTIONS (0) is set. */ 307 return (supports_function_bitset(supported_functions, 308 DSM_ENUM_FUNCTIONS)); 309 } 310 311 static bool 312 has_dsm(const struct acpi_spmc_softc *const sc, const int dsm_index) 313 { 314 return (has_dsm_bitset(get_supported_functions(sc, dsm_index))); 315 } 316 317 typedef const char *pbf_get_name_t(const int, const void *const); 318 319 static const char * 320 pbf_dsm_name(const int dsm_index, const void *const opaque __unused) 321 { 322 return (resolve_dsm(dsm_index)->name); 323 } 324 325 static const char * 326 dsm_function_name(const struct dsm_desc *const dsm, const int function_index) 327 { 328 MPASS(function_index >= 0); 329 if (function_index >= dsm->function_names_nb) 330 return (NULL); 331 /* May be NULL. */ 332 return (dsm->function_names[function_index]); 333 } 334 335 static const char * 336 pbf_function_name(const int function_index, const void *const opaque) 337 { 338 return (dsm_function_name(opaque, function_index)); 339 } 340 341 static int 342 print_bit_field(char *const buf, const size_t buf_size, 343 const uint64_t bit_field, const char *const fallback_prefix, 344 pbf_get_name_t get_name, const void *const opaque) 345 { 346 uint64_t bf = bit_field; 347 char *const buf_end = buf + buf_size; 348 char *p = buf; 349 int ret = 0; 350 bool one_set = false; 351 352 #define PBF_PRINT(...) \ 353 do { \ 354 const __ptrdiff_t rem = MAX(buf_end - p, 0); \ 355 const int lret = snprintf(p, rem, __VA_ARGS__); \ 356 \ 357 MPASS(lret >= 0); \ 358 p += MIN(lret, rem); \ 359 ret += lret; \ 360 } while (0) 361 362 if (bf == 0) { 363 PBF_PRINT(""); 364 return (ret); 365 } 366 367 do { 368 const int b_idx = ffsll(bf) - 1; 369 const char *const name = get_name(b_idx, opaque); 370 371 PBF_PRINT(one_set ? "," : "<"); 372 one_set = true; 373 if (name != NULL) 374 PBF_PRINT("%s", name); 375 else 376 PBF_PRINT("%s_%d", fallback_prefix, b_idx); 377 378 bf &= ~IDX_TO_BIT(b_idx); 379 } while (bf != 0); 380 PBF_PRINT(">"); 381 #undef PBF_PRINT 382 383 return (ret); 384 } 385 386 static void 387 failed_to_call_dsm(const struct acpi_spmc_softc *const sc, 388 const struct dsm_desc *const dsm, const int function_index) 389 { 390 (void)device_printf(sc->dev, 391 "Failed to call DSM %s (rev %u) function %s\n", 392 dsm->name, get_revision(sc, dsm->index), 393 dsm_function_name(dsm, function_index)); 394 } 395 396 static void acpi_spmc_probe_dsm(struct acpi_spmc_softc *const sc, 397 const struct dsm_desc *const dsm); 398 static void acpi_spmc_dsm_print( 399 const struct acpi_spmc_softc *const sc, 400 const struct dsm_desc *const dsm); 401 static int acpi_spmc_get_constraints(struct acpi_spmc_softc *const sc); 402 static void acpi_spmc_free_constraints(struct acpi_spmc_softc *const sc); 403 404 static void acpi_spmc_suspend(device_t dev, enum power_stype stype); 405 static void acpi_spmc_resume(device_t dev, enum power_stype stype); 406 407 static int 408 acpi_spmc_probe(device_t dev) 409 { 410 char *name; 411 412 /* Check that this is an enabled device. */ 413 if (acpi_get_type(dev) != ACPI_TYPE_DEVICE || acpi_disabled("spmc")) 414 return (ENXIO); 415 416 if (ACPI_ID_PROBE(device_get_parent(dev), dev, spmc_ids, &name) > 0) 417 return (ENXIO); 418 419 device_set_desc(dev, "System Power Management Controller"); 420 421 return (BUS_PROBE_DEFAULT); 422 } 423 424 static int 425 acpi_spmc_attach(device_t dev) 426 { 427 struct acpi_spmc_softc *const sc = device_get_softc(dev); 428 const ACPI_HANDLE handle = acpi_get_handle(dev); 429 int supported_dsms; 430 char buf[32]; 431 int error; 432 433 /* 434 * ACPI_ID_PROBE() in acpi_spmc_probe() cannot succeed without a handle. 435 */ 436 MPASS(handle != NULL); 437 438 sc->dev = dev; 439 sc->handle = handle; 440 441 supported_dsms = 0; 442 for (int i = 0; i < nitems(dsms); ++i) { 443 KASSERT(dsms[i] != NULL, ("%s: Sparse dsms[]!", __func__)); 444 KASSERT(dsms[i]->index == i, 445 ("%s: Inconsistent indices for DSM %s", __func__, 446 dsms[i]->name)); 447 448 acpi_spmc_probe_dsm(sc, dsms[i]); 449 if (has_dsm(sc, i)) 450 supported_dsms |= IDX_TO_BIT(i); 451 } 452 453 if (supported_dsms == 0) { 454 device_printf(dev, "No DSM supported!"); 455 return (ENXIO); 456 } 457 458 print_bit_field(buf, sizeof(buf), supported_dsms, "DSM", 459 pbf_dsm_name, NULL); 460 device_printf(dev, "DSMs supported: %s\n", buf); 461 462 /* Print supported functions of usable DSMs. */ 463 for (int i = 0; i < nitems(dsms); ++i) 464 if (has_dsm(sc, i)) 465 acpi_spmc_dsm_print(sc, dsms[i]); 466 467 /* Get device constraints. We can only call this once so do this now. */ 468 error = acpi_spmc_get_constraints(sc); 469 if (error != 0) 470 /* acpi_spmc_get_constraints() takes care of cleaning up. */ 471 device_printf(dev, 472 "Could not parse power state constraints (%d), " 473 "will not check for them before suspend\n", error); 474 475 sc->eh_suspend = EVENTHANDLER_REGISTER(acpi_post_dev_suspend, 476 acpi_spmc_suspend, dev, EVENTHANDLER_PRI_ANY); 477 sc->eh_resume = EVENTHANDLER_REGISTER(acpi_pre_dev_resume, 478 acpi_spmc_resume, dev, EVENTHANDLER_PRI_ANY); 479 480 return (0); 481 } 482 483 static int 484 acpi_spmc_detach(device_t dev) 485 { 486 struct acpi_spmc_softc *sc = device_get_softc(dev); 487 488 EVENTHANDLER_DEREGISTER(acpi_post_dev_suspend, sc->eh_suspend); 489 EVENTHANDLER_DEREGISTER(acpi_pre_dev_resume, sc->eh_resume); 490 acpi_spmc_free_constraints(sc); 491 return (0); 492 } 493 494 static uint64_t 495 dsm_missing_functions(const struct dsm_desc *const dsm, 496 uint64_t supported_functions) 497 { 498 return (dsm->expected_functions & ~supported_functions); 499 } 500 501 static void 502 acpi_spmc_dsm_print(const struct acpi_spmc_softc *const sc, 503 const struct dsm_desc *const dsm) 504 { 505 /* 506 * Remove the enumeration function bit, which we do not care about when 507 * printing which functions are supported and which we do not want to 508 * report as unknown. 509 */ 510 const uint64_t supported_functions = ~IDX_TO_BIT(DSM_ENUM_FUNCTIONS) & 511 get_supported_functions(sc, dsm->index); 512 const uint64_t missing = dsm_missing_functions(dsm, supported_functions); 513 const uint64_t unknown = supported_functions & 514 ~(dsm->expected_functions | dsm->extra_functions); 515 char buf[128]; 516 517 print_bit_field(buf, sizeof(buf), supported_functions, 518 "FUNC", pbf_function_name, dsm); 519 device_printf(sc->dev, 520 "DSM %s, revision %d: Supported functions: %#" PRIx64 "%s\n", 521 dsm->name, get_revision(sc, dsm->index), supported_functions, buf); 522 523 if (VERBOSE() && missing != 0) { 524 print_bit_field(buf, sizeof(buf), missing, "FUNC", 525 pbf_function_name, dsm); 526 device_printf(sc->dev, "DSM %s: Does not enumerate expected " 527 "functions %#" PRIx64 "%s. Will skip calling them.\n", 528 dsm->name, missing, buf); 529 } 530 531 if (VERBOSE() && unknown != 0) { 532 print_bit_field(buf, sizeof(buf), unknown, "FUNC", 533 pbf_function_name, dsm); 534 device_printf(sc->dev, "DSM %s: Supports more functions than " 535 "used (%#" PRIx64 "%s), driver might need an upgrade.\n", 536 dsm->name, unknown, buf); 537 } 538 } 539 540 /* Returns whether the DSM is supported (enumeration succeeds). */ 541 static bool 542 probe_dsm_revision(const struct acpi_spmc_softc *const sc, 543 const struct dsm_desc *const dsm, const uint8_t revision, 544 uint64_t *const supported_functions) 545 { 546 *supported_functions = acpi_DSMQuery(sc->handle, 547 (const uint8_t *)&dsm->uuid, revision); 548 return (has_dsm_bitset(*supported_functions)); 549 } 550 551 static void 552 set_dsm_revision(struct acpi_spmc_softc *const sc, 553 const struct dsm_desc *const dsm, const uint8_t revision, 554 uint64_t supported_functions) 555 { 556 struct dsm_info *const dsm_info = get_dsm_info(sc, dsm->index); 557 558 MPASS(has_dsm_bitset(supported_functions)); 559 dsm_info->supported_functions = supported_functions; 560 dsm_info->revision = revision; 561 } 562 563 static void 564 acpi_spmc_probe_dsm(struct acpi_spmc_softc *const sc, 565 const struct dsm_desc *const dsm) 566 { 567 const int8_t revision_spec = *dsm->revision_spec; 568 uint64_t supported_functions; 569 570 if (revision_spec >= 0) { 571 /* Specific revision specified. */ 572 if (probe_dsm_revision(sc, dsm, revision_spec, 573 &supported_functions)) 574 set_dsm_revision(sc, dsm, revision_spec, 575 supported_functions); 576 return; 577 } 578 579 /* 580 * Auto-detect. We try revisions in ascending order, selecting the 581 * first that has all the functions we expect in the hope to avoid potential 582 * backwards-compatibility problems, else continuing with higher ones 583 * but adopting them only if they actually add new functions (it seems 584 * common that firmwares do not care about the revision, or will return 585 * the same supported functions after a revision limit). 586 */ 587 for (uint8_t revision = 0; revision <= -revision_spec; ++revision) { 588 if (!probe_dsm_revision(sc, dsm, revision, 589 &supported_functions)) 590 continue; 591 if ((~get_supported_functions(sc, dsm->index) & 592 supported_functions) == 0) 593 /* This revision adds no new function, skip it. */ 594 continue; 595 596 set_dsm_revision(sc, dsm, revision, supported_functions); 597 598 if (dsm_missing_functions(dsm, ~IDX_TO_BIT(DSM_ENUM_FUNCTIONS) & 599 supported_functions) == 0) 600 /* We have all expected functions, bail out. */ 601 break; 602 } 603 } 604 605 static void 606 acpi_spmc_free_constraints(struct acpi_spmc_softc *const sc) 607 { 608 for (size_t i = 0; i < sc->constraint_count; i++) 609 free(sc->constraints[i].name, M_TEMP); 610 sc->constraint_count = 0; 611 612 free(sc->constraints, M_TEMP); 613 sc->constraints = NULL; 614 } 615 616 static int 617 acpi_spmc_parse_constraints_intel(struct acpi_spmc_softc *sc, ACPI_OBJECT *object) 618 { 619 struct acpi_spmc_constraint *constraint; 620 int revision; 621 ACPI_OBJECT *constraint_obj; 622 ACPI_OBJECT *name_obj; 623 ACPI_OBJECT *detail; 624 ACPI_OBJECT *constraint_package; 625 626 sc->constraint_count = object->Package.Count; 627 sc->constraints = malloc(sc->constraint_count * sizeof *sc->constraints, 628 M_TEMP, M_WAITOK | M_ZERO); 629 630 /* 631 * The value of sc->constraint_count can change during the loop, so 632 * iterate until object->Package.Count so we actually go over all 633 * elements in the package. 634 */ 635 for (size_t i = 0, j = 0; i < object->Package.Count; i++) { 636 constraint_obj = &object->Package.Elements[i]; 637 constraint = &sc->constraints[j]; 638 639 constraint->enabled = 640 constraint_obj->Package.Elements[1].Integer.Value; 641 642 name_obj = &constraint_obj->Package.Elements[0]; 643 constraint->name = strdup(name_obj->String.Pointer, M_TEMP); 644 645 detail = &constraint_obj->Package.Elements[2]; 646 /* 647 * The first element in the device constraint detail package is 648 * the revision, and should always be zero. 649 */ 650 revision = detail->Package.Elements[0].Integer.Value; 651 if (revision != 0) { 652 /* Only print this error message once if not verbose. */ 653 if (VERBOSE() || sc->constraint_count == 654 object->Package.Count) 655 device_printf(sc->dev, 656 "Intel: Unknown revision %d for " 657 "constraint %zu's detail package\n", 658 revision, i); 659 sc->constraint_count--; 660 continue; 661 } 662 663 constraint_package = &detail->Package.Elements[1]; 664 665 constraint->lpi_uid = 666 constraint_package->Package.Elements[0].Integer.Value; 667 constraint->min_d_state = 668 constraint_package->Package.Elements[1].Integer.Value; 669 constraint->min_dev_specific_state = 670 constraint_package->Package.Elements[2].Integer.Value; 671 672 j++; 673 } 674 675 return (0); 676 } 677 678 static int 679 acpi_spmc_parse_constraints_amd(struct acpi_spmc_softc *sc, ACPI_OBJECT *object) 680 { 681 size_t constraint_count; 682 ACPI_OBJECT *constraint_obj; 683 ACPI_OBJECT *constraints; 684 struct acpi_spmc_constraint *constraint; 685 ACPI_OBJECT *name_obj; 686 687 /* 688 * First element in the package is unknown. 689 * Second element is the number of device constraints. 690 * Third element is the list of device constraints itself. 691 */ 692 constraint_count = object->Package.Elements[1].Integer.Value; 693 constraints = &object->Package.Elements[2]; 694 695 if (constraints->Package.Count != constraint_count) { 696 device_printf(sc->dev, 697 "AMD: Constraints: Count mismatch (%d to %zu)\n", 698 constraints->Package.Count, constraint_count); 699 return (ENXIO); 700 } 701 702 sc->constraint_count = constraint_count; 703 sc->constraints = malloc(constraint_count * sizeof *sc->constraints, 704 M_TEMP, M_WAITOK | M_ZERO); 705 706 for (size_t i = 0; i < constraint_count; i++) { 707 /* Parse the constraint package. */ 708 constraint_obj = &constraints->Package.Elements[i]; 709 if (constraint_obj->Package.Count != 4) { 710 device_printf(sc->dev, 711 "AMD: Constraint %zu has %d elements, not 4\n", 712 i, constraint_obj->Package.Count); 713 acpi_spmc_free_constraints(sc); 714 return (ENXIO); 715 } 716 717 constraint = &sc->constraints[i]; 718 constraint->enabled = 719 constraint_obj->Package.Elements[0].Integer.Value; 720 721 name_obj = &constraint_obj->Package.Elements[1]; 722 constraint->name = strdup(name_obj->String.Pointer, M_TEMP); 723 724 constraint->function_states = 725 constraint_obj->Package.Elements[2].Integer.Value; 726 constraint->min_d_state = 727 constraint_obj->Package.Elements[3].Integer.Value; 728 } 729 730 return (0); 731 } 732 733 static int 734 acpi_spmc_get_constraints(struct acpi_spmc_softc *const sc) 735 { 736 const struct dsm_desc *dsm; 737 ACPI_STATUS status; 738 ACPI_BUFFER result; 739 ACPI_OBJECT *object; 740 int rv; 741 struct acpi_spmc_constraint *constraint; 742 743 744 MPASS(!sc->get_constraints_succeeded); 745 /* 746 * Constraints are not supported by the Microsoft DSM. Since we do not 747 * expect both Intel and AMD DSMs to be present at once, we only have 748 * a single storage for common information ('min_d_state'). In case 749 * some day both happen to be present, warn the user so that he can 750 * report that condition to us, and somewhat arbitrarily favor the Intel 751 * one because it at least has a written specification. 752 */ 753 if (supports_function(sc, DSM_INTEL, DSM_GET_DEVICE_CONSTRAINTS)) { 754 dsm = &dsm_intel; 755 756 if (supports_function(sc, DSM_AMD, DSM_GET_DEVICE_CONSTRAINTS)) 757 device_printf(sc->dev, "Constraints: Both Intel and " 758 "AMD DSMs support getting them!\n" 759 "Using constraints from Intel.\nPlease report.\n"); 760 } else if (supports_function(sc, DSM_AMD, DSM_GET_DEVICE_CONSTRAINTS)) 761 dsm = &dsm_amd; 762 else 763 return (0); 764 765 /* It seems like this DSM can fail if called more than once. */ 766 status = acpi_EvaluateDSMTyped(sc->handle, (const uint8_t *)&dsm->uuid, 767 get_revision(sc, dsm->index), DSM_GET_DEVICE_CONSTRAINTS, NULL, 768 &result, ACPI_TYPE_PACKAGE); 769 if (ACPI_FAILURE(status)) { 770 failed_to_call_dsm(sc, dsm, DSM_GET_DEVICE_CONSTRAINTS); 771 return (ENXIO); 772 } else if (VERBOSE()) 773 device_printf(sc->dev, "Constraints: Retrieved successfully\n"); 774 775 object = (ACPI_OBJECT *)result.Pointer; 776 if (dsm == &dsm_intel) 777 rv = acpi_spmc_parse_constraints_intel(sc, object); 778 else { 779 MPASS(dsm == &dsm_amd); 780 rv = acpi_spmc_parse_constraints_amd(sc, object); 781 } 782 AcpiOsFree(object); 783 if (rv != 0) 784 return (rv); 785 786 /* Get handles for each constraint device. */ 787 for (size_t i = 0; i < sc->constraint_count; i++) { 788 constraint = &sc->constraints[i]; 789 790 status = acpi_GetHandleInScope(sc->handle, constraint->name, 791 &constraint->handle); 792 if (ACPI_FAILURE(status)) { 793 if (VERBOSE()) 794 device_printf(sc->dev, 795 "Constraints: Cannot get handle for %s, " 796 "ignoring\n", 797 constraint->name); 798 constraint->handle = NULL; 799 } 800 } 801 802 #ifdef INVARIANTS 803 sc->get_constraints_succeeded = true; 804 #endif 805 return (0); 806 } 807 808 static void 809 acpi_spmc_check_constraints(device_t dev) 810 { 811 const struct acpi_spmc_softc *const sc = device_get_softc(dev); 812 #ifdef notyet 813 bool violation = false; 814 #endif 815 816 /* 817 * Avoid printing that constraints are respected when there are no 818 * constraints at all. 819 */ 820 if (sc->constraint_count == 0) 821 return; 822 for (size_t i = 0; i < sc->constraint_count; i++) { 823 const struct acpi_spmc_constraint *constraint = 824 &sc->constraints[i]; 825 826 if (!constraint->enabled) 827 continue; 828 if (constraint->handle == NULL) 829 continue; 830 831 #ifdef notyet 832 int d_state; 833 if (ACPI_FAILURE(acpi_pwr_get_state(constraint->handle, &d_state))) 834 continue; 835 if (d_state < constraint->min_d_state) { 836 device_printf(sc->dev, "Constraint for device %s" 837 " violated (current D-state: %s, " 838 "required minimum D-state: %s).\n" 839 constraint->name, 840 acpi_d_state_to_str(d_state), 841 acpi_d_state_to_str(constraint->min_d_state)); 842 violation = true; 843 } 844 #endif 845 } 846 #ifdef notyet 847 if (violation) 848 device_printf(sc->dev, "Some constraints violated, " 849 "might fail to enter a Low-Power Idle state\n"); 850 else 851 device_printf(sc->dev, 852 "All device power constraints respected!\n"); 853 #endif 854 } 855 856 /* 857 * Run a single DSM function. 858 * 859 * Only runs the function if it was reported present during enumeration. 860 * Discards the result, but prints a message on error. 861 */ 862 static void 863 acpi_spmc_run(device_t dev, const struct dsm_desc *const dsm, 864 const int function_index) 865 { 866 const struct acpi_spmc_softc *const sc = device_get_softc(dev); 867 ACPI_STATUS status; 868 ACPI_BUFFER result; 869 870 if (!(supports_function(sc, dsm->index, function_index) || 871 (force_call_expected_functions && has_dsm(sc, dsm->index)))) 872 return; 873 874 if (VERBOSE()) 875 device_printf(dev, "DSM %s: Calling function %s\n", 876 dsm->name, dsm_function_name(dsm, function_index)); 877 status = acpi_EvaluateDSMTyped(sc->handle, (const uint8_t *)&dsm->uuid, 878 get_revision(sc, dsm->index), function_index, NULL, 879 &result, ACPI_TYPE_ANY); 880 881 if (ACPI_FAILURE(status)) 882 failed_to_call_dsm(sc, dsm, function_index); 883 else { 884 if (VERBOSE()) 885 device_printf(dev, "DSM %s: Function %s successful\n", 886 dsm->name, dsm_function_name(dsm, function_index)); 887 AcpiOsFree(result.Pointer); 888 } 889 } 890 891 /* 892 * Try running the functions from all the DSMs we have, as them failing costs us 893 * nothing, and it seems like on AMD platforms, both the AMD entry and Microsoft 894 * "modern" functions are required for it to enter modern standby. 895 * 896 * This is what Linux does too. 897 */ 898 static void 899 acpi_spmc_display_off_notif(device_t dev) 900 { 901 acpi_spmc_run(dev, &dsm_intel, DSM_INTEL_MS_DISPLAY_OFF_NOTIF); 902 acpi_spmc_run(dev, &dsm_ms, DSM_INTEL_MS_DISPLAY_OFF_NOTIF); 903 acpi_spmc_run(dev, &dsm_amd, DSM_AMD_DISPLAY_OFF_NOTIF); 904 } 905 906 static void 907 acpi_spmc_display_on_notif(device_t dev) 908 { 909 acpi_spmc_run(dev, &dsm_intel, DSM_INTEL_MS_DISPLAY_ON_NOTIF); 910 acpi_spmc_run(dev, &dsm_ms, DSM_INTEL_MS_DISPLAY_ON_NOTIF); 911 acpi_spmc_run(dev, &dsm_amd, DSM_AMD_DISPLAY_ON_NOTIF); 912 } 913 914 static void 915 acpi_spmc_entry_notif(device_t dev) 916 { 917 /* XXX - No real check currently. Check return code when it does. */ 918 acpi_spmc_check_constraints(dev); 919 920 acpi_spmc_run(dev, &dsm_amd, DSM_AMD_LPI_ENTRY_NOTIF); 921 acpi_spmc_run(dev, &dsm_ms, DSM_MS_SLEEP_ENTRY_NOTIF); 922 acpi_spmc_run(dev, &dsm_ms, DSM_INTEL_MS_LPI_ENTRY_NOTIF); 923 acpi_spmc_run(dev, &dsm_intel, DSM_INTEL_MS_LPI_ENTRY_NOTIF); 924 } 925 926 static void 927 acpi_spmc_exit_notif(device_t dev) 928 { 929 acpi_spmc_run(dev, &dsm_intel, DSM_INTEL_MS_LPI_EXIT_NOTIF); 930 acpi_spmc_run(dev, &dsm_amd, DSM_AMD_LPI_EXIT_NOTIF); 931 acpi_spmc_run(dev, &dsm_ms, DSM_INTEL_MS_LPI_EXIT_NOTIF); 932 /* Hint to the platform we are soon going to turn on the display. */ 933 acpi_spmc_run(dev, &dsm_ms, DSM_MS_TURN_ON_DISPLAY); 934 acpi_spmc_run(dev, &dsm_ms, DSM_MS_SLEEP_EXIT_NOTIF); 935 } 936 937 static void 938 acpi_spmc_suspend(device_t dev, enum power_stype stype) 939 { 940 if (stype != POWER_STYPE_SUSPEND_TO_IDLE) 941 return; 942 943 acpi_spmc_display_off_notif(dev); 944 acpi_spmc_entry_notif(dev); 945 } 946 947 static void 948 acpi_spmc_resume(device_t dev, enum power_stype stype) 949 { 950 if (stype != POWER_STYPE_SUSPEND_TO_IDLE) 951 return; 952 953 acpi_spmc_exit_notif(dev); 954 acpi_spmc_display_on_notif(dev); 955 } 956 957 static device_method_t acpi_spmc_methods[] = { 958 DEVMETHOD(device_probe, acpi_spmc_probe), 959 DEVMETHOD(device_attach, acpi_spmc_attach), 960 DEVMETHOD(device_detach, acpi_spmc_detach), 961 DEVMETHOD_END 962 }; 963 964 static driver_t acpi_spmc_driver = { 965 "acpi_spmc", 966 acpi_spmc_methods, 967 sizeof(struct acpi_spmc_softc), 968 }; 969 970 DRIVER_MODULE_ORDERED(acpi_spmc, acpi, acpi_spmc_driver, NULL, NULL, SI_ORDER_ANY); 971 MODULE_DEPEND(acpi_spmc, acpi, 1, 1, 1); 972