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 *
resolve_dsm(int dsm_index)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 *
get_dsm_info(struct acpi_spmc_softc * const sc,const int dsm_index)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 *
get_const_dsm_info(const struct acpi_spmc_softc * const sc,const int dsm_index)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
get_supported_functions(const struct acpi_spmc_softc * const sc,const int dsm_index)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
get_revision(const struct acpi_spmc_softc * const sc,const int dsm_index)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
supports_function_bitset(const uint64_t supported_functions,const int function_index)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
supports_function(const struct acpi_spmc_softc * const sc,const int dsm_index,const int function_index)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
has_dsm_bitset(const uint64_t supported_functions)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
has_dsm(const struct acpi_spmc_softc * const sc,const int dsm_index)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 *
pbf_dsm_name(const int dsm_index,const void * const opaque __unused)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 *
dsm_function_name(const struct dsm_desc * const dsm,const int function_index)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 *
pbf_function_name(const int function_index,const void * const opaque)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
print_bit_field(char * const buf,const size_t buf_size,const uint64_t bit_field,const char * const fallback_prefix,pbf_get_name_t get_name,const void * const opaque)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
failed_to_call_dsm(const struct acpi_spmc_softc * const sc,const struct dsm_desc * const dsm,const int function_index)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
acpi_spmc_probe(device_t dev)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
acpi_spmc_attach(device_t dev)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
acpi_spmc_detach(device_t dev)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
dsm_missing_functions(const struct dsm_desc * const dsm,uint64_t supported_functions)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
acpi_spmc_dsm_print(const struct acpi_spmc_softc * const sc,const struct dsm_desc * const dsm)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
probe_dsm_revision(const struct acpi_spmc_softc * const sc,const struct dsm_desc * const dsm,const uint8_t revision,uint64_t * const supported_functions)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
set_dsm_revision(struct acpi_spmc_softc * const sc,const struct dsm_desc * const dsm,const uint8_t revision,uint64_t supported_functions)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
acpi_spmc_probe_dsm(struct acpi_spmc_softc * const sc,const struct dsm_desc * const dsm)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
acpi_spmc_free_constraints(struct acpi_spmc_softc * const sc)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
acpi_spmc_parse_constraints_intel(struct acpi_spmc_softc * sc,ACPI_OBJECT * object)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 if (constraint_obj->Type != ACPI_TYPE_PACKAGE) {
640 device_printf(sc->dev, "Intel: Wrong element type for "
641 "constraint %zu.\n", i);
642 goto skip;
643 }
644 if (constraint_obj->Package.Count != 3) {
645 device_printf(sc->dev, "Intel: Wrong package length "
646 "for constraint %zu's package.\n", i);
647 goto skip;
648 }
649
650 name_obj = &constraint_obj->Package.Elements[0];
651 constraint->enabled =
652 constraint_obj->Package.Elements[1].Integer.Value;
653 detail = &constraint_obj->Package.Elements[2];
654
655 if (name_obj->Type != ACPI_TYPE_STRING) {
656 device_printf(sc->dev,
657 "Intel: Constraint %zu's name is not string.\n", i);
658 goto skip;
659 }
660 if (detail->Type != ACPI_TYPE_PACKAGE) {
661 device_printf(sc->dev, "Intel: Wrong element type for "
662 "constraint %zu's detail package.\n", i);
663 goto skip;
664 }
665 if (detail->Package.Count != 2) {
666 device_printf(sc->dev, "Intel: Wrong package length "
667 "for constraint %zu's detail package.\n", i);
668 goto skip;
669 }
670
671 /*
672 * The first element in the device constraint detail package is
673 * the revision, and should always be zero.
674 */
675 revision = detail->Package.Elements[0].Integer.Value;
676 if (revision != 0) {
677 /* Only print this error message once if not verbose. */
678 if (VERBOSE() || sc->constraint_count ==
679 object->Package.Count)
680 device_printf(sc->dev,
681 "Intel: Unknown revision %d for "
682 "constraint %zu's detail package\n",
683 revision, i);
684 goto skip;
685 }
686
687 constraint_package = &detail->Package.Elements[1];
688
689 if (constraint_package->Type != ACPI_TYPE_PACKAGE) {
690 device_printf(sc->dev, "Intel: Wrong element type for "
691 "constraint %zu's constraint package.\n", i);
692 goto skip;
693 }
694 if (constraint_package->Package.Count != 3) {
695 device_printf(sc->dev, "Intel: Wrong package length "
696 "for constraint %zu's constraint package.\n", i);
697 goto skip;
698 }
699
700 constraint->name = strdup(name_obj->String.Pointer, M_TEMP);
701
702 constraint->lpi_uid =
703 constraint_package->Package.Elements[0].Integer.Value;
704 constraint->min_d_state =
705 constraint_package->Package.Elements[1].Integer.Value;
706 constraint->min_dev_specific_state =
707 constraint_package->Package.Elements[2].Integer.Value;
708
709 j++;
710 continue;
711 skip:
712 sc->constraint_count--;
713 }
714
715 return (0);
716 }
717
718 static int
acpi_spmc_parse_constraints_amd(struct acpi_spmc_softc * sc,ACPI_OBJECT * object)719 acpi_spmc_parse_constraints_amd(struct acpi_spmc_softc *sc, ACPI_OBJECT *object)
720 {
721 size_t constraint_count;
722 ACPI_OBJECT *constraint_obj;
723 ACPI_OBJECT *constraints;
724 struct acpi_spmc_constraint *constraint;
725 ACPI_OBJECT *name_obj;
726
727 if (object->Type != ACPI_TYPE_PACKAGE) {
728 device_printf(sc->dev, "AMD: Constraints: object not "
729 "package.\n");
730 return (ENXIO);
731 }
732 if (object->Package.Count != 3) {
733 device_printf(sc->dev,
734 "AMD: Constraints: Package has %d elements\n",
735 object->Package.Count);
736 return (ENXIO);
737 }
738
739 /*
740 * First element in the package is unknown.
741 * Second element is the number of device constraints.
742 * Third element is the list of device constraints itself.
743 */
744 constraint_count = object->Package.Elements[1].Integer.Value;
745 constraints = &object->Package.Elements[2];
746
747 if (constraints->Type != ACPI_TYPE_PACKAGE) {
748 device_printf(sc->dev,
749 "AMD: Constraints: Third element not a package\n");
750 return (ENXIO);
751 }
752 if (constraints->Package.Count != constraint_count) {
753 device_printf(sc->dev,
754 "AMD: Constraints: Count mismatch (%d to %zu)\n",
755 constraints->Package.Count, constraint_count);
756 return (ENXIO);
757 }
758
759 sc->constraint_count = constraint_count;
760 sc->constraints = malloc(constraint_count * sizeof *sc->constraints,
761 M_TEMP, M_WAITOK | M_ZERO);
762
763 for (size_t i = 0, j = 0; i < constraint_count; i++) {
764 /* Parse the constraint package. */
765 constraint_obj = &constraints->Package.Elements[i];
766 constraint = &sc->constraints[j];
767
768 if (constraint_obj->Type != ACPI_TYPE_PACKAGE) {
769 device_printf(sc->dev,
770 "AMD: Constraint %zu not a package\n", i);
771 goto skip;
772 }
773 if (constraint_obj->Package.Count != 4) {
774 device_printf(sc->dev,
775 "AMD: Constraint %zu has %d elements, not 4\n",
776 i, constraint_obj->Package.Count);
777 goto skip;
778 }
779
780 constraint->enabled =
781 constraint_obj->Package.Elements[0].Integer.Value;
782 name_obj = &constraint_obj->Package.Elements[1];
783
784 if (name_obj->Type != ACPI_TYPE_STRING) {
785 device_printf(sc->dev,
786 "AMD: Constraint %zu's name is not string.\n", i);
787 goto skip;
788 }
789
790 constraint->name = strdup(name_obj->String.Pointer, M_TEMP);
791 constraint->function_states =
792 constraint_obj->Package.Elements[2].Integer.Value;
793 constraint->min_d_state =
794 constraint_obj->Package.Elements[3].Integer.Value;
795
796 j++;
797 continue;
798 skip:
799 sc->constraint_count--;
800 }
801
802 return (0);
803 }
804
805 static int
acpi_spmc_get_constraints(struct acpi_spmc_softc * const sc)806 acpi_spmc_get_constraints(struct acpi_spmc_softc *const sc)
807 {
808 const struct dsm_desc *dsm;
809 ACPI_STATUS status;
810 ACPI_BUFFER result;
811 ACPI_OBJECT *object;
812 int rv;
813 struct acpi_spmc_constraint *constraint;
814
815
816 MPASS(!sc->get_constraints_succeeded);
817 /*
818 * Constraints are not supported by the Microsoft DSM. Since we do not
819 * expect both Intel and AMD DSMs to be present at once, we only have
820 * a single storage for common information ('min_d_state'). In case
821 * some day both happen to be present, warn the user so that he can
822 * report that condition to us, and somewhat arbitrarily favor the Intel
823 * one because it at least has a written specification.
824 */
825 if (supports_function(sc, DSM_INTEL, DSM_GET_DEVICE_CONSTRAINTS)) {
826 dsm = &dsm_intel;
827
828 if (supports_function(sc, DSM_AMD, DSM_GET_DEVICE_CONSTRAINTS))
829 device_printf(sc->dev, "Constraints: Both Intel and "
830 "AMD DSMs support getting them!\n"
831 "Using constraints from Intel.\nPlease report.\n");
832 } else if (supports_function(sc, DSM_AMD, DSM_GET_DEVICE_CONSTRAINTS))
833 dsm = &dsm_amd;
834 else
835 return (0);
836
837 /* It seems like this DSM can fail if called more than once. */
838 status = acpi_EvaluateDSMTyped(sc->handle, (const uint8_t *)&dsm->uuid,
839 get_revision(sc, dsm->index), DSM_GET_DEVICE_CONSTRAINTS, NULL,
840 &result, ACPI_TYPE_PACKAGE);
841 if (ACPI_FAILURE(status)) {
842 failed_to_call_dsm(sc, dsm, DSM_GET_DEVICE_CONSTRAINTS);
843 return (ENXIO);
844 } else if (VERBOSE())
845 device_printf(sc->dev, "Constraints: Retrieved successfully\n");
846
847 object = (ACPI_OBJECT *)result.Pointer;
848 if (dsm == &dsm_intel)
849 rv = acpi_spmc_parse_constraints_intel(sc, object);
850 else {
851 MPASS(dsm == &dsm_amd);
852 rv = acpi_spmc_parse_constraints_amd(sc, object);
853 }
854 AcpiOsFree(object);
855 if (rv != 0)
856 return (rv);
857
858 /* Get handles for each constraint device. */
859 for (size_t i = 0; i < sc->constraint_count; i++) {
860 constraint = &sc->constraints[i];
861
862 status = acpi_GetHandleInScope(sc->handle, constraint->name,
863 &constraint->handle);
864 if (ACPI_FAILURE(status)) {
865 if (VERBOSE())
866 device_printf(sc->dev,
867 "Constraints: Cannot get handle for %s, "
868 "ignoring\n",
869 constraint->name);
870 constraint->handle = NULL;
871 }
872 }
873
874 #ifdef INVARIANTS
875 sc->get_constraints_succeeded = true;
876 #endif
877 return (0);
878 }
879
880 static void
acpi_spmc_check_constraints(device_t dev)881 acpi_spmc_check_constraints(device_t dev)
882 {
883 const struct acpi_spmc_softc *const sc = device_get_softc(dev);
884 #ifdef notyet
885 bool violation = false;
886 #endif
887
888 /*
889 * Avoid printing that constraints are respected when there are no
890 * constraints at all.
891 */
892 if (sc->constraint_count == 0)
893 return;
894 for (size_t i = 0; i < sc->constraint_count; i++) {
895 const struct acpi_spmc_constraint *constraint =
896 &sc->constraints[i];
897
898 if (!constraint->enabled)
899 continue;
900 if (constraint->handle == NULL)
901 continue;
902
903 #ifdef notyet
904 int d_state;
905 if (ACPI_FAILURE(acpi_pwr_get_state(constraint->handle, &d_state)))
906 continue;
907 if (d_state < constraint->min_d_state) {
908 device_printf(sc->dev, "Constraint for device %s"
909 " violated (current D-state: %s, "
910 "required minimum D-state: %s).\n"
911 constraint->name,
912 acpi_d_state_to_str(d_state),
913 acpi_d_state_to_str(constraint->min_d_state));
914 violation = true;
915 }
916 #endif
917 }
918 #ifdef notyet
919 if (violation)
920 device_printf(sc->dev, "Some constraints violated, "
921 "might fail to enter a Low-Power Idle state\n");
922 else
923 device_printf(sc->dev,
924 "All device power constraints respected!\n");
925 #endif
926 }
927
928 /*
929 * Run a single DSM function.
930 *
931 * Only runs the function if it was reported present during enumeration.
932 * Discards the result, but prints a message on error.
933 */
934 static void
acpi_spmc_run(device_t dev,const struct dsm_desc * const dsm,const int function_index)935 acpi_spmc_run(device_t dev, const struct dsm_desc *const dsm,
936 const int function_index)
937 {
938 const struct acpi_spmc_softc *const sc = device_get_softc(dev);
939 ACPI_STATUS status;
940 ACPI_BUFFER result;
941
942 if (!(supports_function(sc, dsm->index, function_index) ||
943 (force_call_expected_functions && has_dsm(sc, dsm->index))))
944 return;
945
946 if (VERBOSE())
947 device_printf(dev, "DSM %s: Calling function %s\n",
948 dsm->name, dsm_function_name(dsm, function_index));
949 status = acpi_EvaluateDSMTyped(sc->handle, (const uint8_t *)&dsm->uuid,
950 get_revision(sc, dsm->index), function_index, NULL,
951 &result, ACPI_TYPE_ANY);
952
953 if (ACPI_FAILURE(status))
954 failed_to_call_dsm(sc, dsm, function_index);
955 else {
956 if (VERBOSE())
957 device_printf(dev, "DSM %s: Function %s successful\n",
958 dsm->name, dsm_function_name(dsm, function_index));
959 AcpiOsFree(result.Pointer);
960 }
961 }
962
963 /*
964 * Try running the functions from all the DSMs we have, as them failing costs us
965 * nothing, and it seems like on AMD platforms, both the AMD entry and Microsoft
966 * "modern" functions are required for it to enter modern standby.
967 *
968 * This is what Linux does too.
969 */
970 static void
acpi_spmc_display_off_notif(device_t dev)971 acpi_spmc_display_off_notif(device_t dev)
972 {
973 acpi_spmc_run(dev, &dsm_intel, DSM_INTEL_MS_DISPLAY_OFF_NOTIF);
974 acpi_spmc_run(dev, &dsm_ms, DSM_INTEL_MS_DISPLAY_OFF_NOTIF);
975 acpi_spmc_run(dev, &dsm_amd, DSM_AMD_DISPLAY_OFF_NOTIF);
976 }
977
978 static void
acpi_spmc_display_on_notif(device_t dev)979 acpi_spmc_display_on_notif(device_t dev)
980 {
981 acpi_spmc_run(dev, &dsm_intel, DSM_INTEL_MS_DISPLAY_ON_NOTIF);
982 acpi_spmc_run(dev, &dsm_ms, DSM_INTEL_MS_DISPLAY_ON_NOTIF);
983 acpi_spmc_run(dev, &dsm_amd, DSM_AMD_DISPLAY_ON_NOTIF);
984 }
985
986 static void
acpi_spmc_entry_notif(device_t dev)987 acpi_spmc_entry_notif(device_t dev)
988 {
989 /* XXX - No real check currently. Check return code when it does. */
990 acpi_spmc_check_constraints(dev);
991
992 acpi_spmc_run(dev, &dsm_amd, DSM_AMD_LPI_ENTRY_NOTIF);
993 acpi_spmc_run(dev, &dsm_ms, DSM_MS_SLEEP_ENTRY_NOTIF);
994 acpi_spmc_run(dev, &dsm_ms, DSM_INTEL_MS_LPI_ENTRY_NOTIF);
995 acpi_spmc_run(dev, &dsm_intel, DSM_INTEL_MS_LPI_ENTRY_NOTIF);
996 }
997
998 static void
acpi_spmc_exit_notif(device_t dev)999 acpi_spmc_exit_notif(device_t dev)
1000 {
1001 acpi_spmc_run(dev, &dsm_intel, DSM_INTEL_MS_LPI_EXIT_NOTIF);
1002 acpi_spmc_run(dev, &dsm_amd, DSM_AMD_LPI_EXIT_NOTIF);
1003 acpi_spmc_run(dev, &dsm_ms, DSM_INTEL_MS_LPI_EXIT_NOTIF);
1004 /* Hint to the platform we are soon going to turn on the display. */
1005 acpi_spmc_run(dev, &dsm_ms, DSM_MS_TURN_ON_DISPLAY);
1006 acpi_spmc_run(dev, &dsm_ms, DSM_MS_SLEEP_EXIT_NOTIF);
1007 }
1008
1009 static void
acpi_spmc_suspend(device_t dev,enum power_stype stype)1010 acpi_spmc_suspend(device_t dev, enum power_stype stype)
1011 {
1012 if (stype != POWER_STYPE_SUSPEND_TO_IDLE)
1013 return;
1014
1015 acpi_spmc_display_off_notif(dev);
1016 acpi_spmc_entry_notif(dev);
1017 }
1018
1019 static void
acpi_spmc_resume(device_t dev,enum power_stype stype)1020 acpi_spmc_resume(device_t dev, enum power_stype stype)
1021 {
1022 if (stype != POWER_STYPE_SUSPEND_TO_IDLE)
1023 return;
1024
1025 acpi_spmc_exit_notif(dev);
1026 acpi_spmc_display_on_notif(dev);
1027 }
1028
1029 static device_method_t acpi_spmc_methods[] = {
1030 DEVMETHOD(device_probe, acpi_spmc_probe),
1031 DEVMETHOD(device_attach, acpi_spmc_attach),
1032 DEVMETHOD(device_detach, acpi_spmc_detach),
1033 DEVMETHOD_END
1034 };
1035
1036 static driver_t acpi_spmc_driver = {
1037 "acpi_spmc",
1038 acpi_spmc_methods,
1039 sizeof(struct acpi_spmc_softc),
1040 };
1041
1042 DRIVER_MODULE_ORDERED(acpi_spmc, acpi, acpi_spmc_driver, NULL, NULL, SI_ORDER_ANY);
1043 MODULE_DEPEND(acpi_spmc, acpi, 1, 1, 1);
1044