xref: /freebsd/sys/dev/acpica/acpi_spmc.c (revision a259b98fa211ed87bfee58c575de4e2de94ee0fa)
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