xref: /src/sys/dev/acpica/acpi_spmc.c (revision 4d876fc5fc427f9e4a110da5cc2ce62a21998b6c)
1 /*
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2024-2025 The FreeBSD Foundation
5  *
6  * This software was developed by Aymeric Wibo <obiwac@freebsd.org>
7  * under sponsorship from the FreeBSD Foundation.
8  */
9 
10 #include <sys/param.h>
11 #include <sys/bus.h>
12 #include <sys/eventhandler.h>
13 #include <sys/kernel.h>
14 #include <sys/malloc.h>
15 #include <sys/module.h>
16 #include <sys/uuid.h>
17 
18 #include <machine/_inttypes.h>
19 
20 #include <contrib/dev/acpica/include/acpi.h>
21 #include <contrib/dev/acpica/include/accommon.h>
22 
23 #include <dev/acpica/acpivar.h>
24 
25 /* Hooks for the ACPI CA debugging infrastructure */
26 #define _COMPONENT	ACPI_SPMC
27 ACPI_MODULE_NAME("SPMC")
28 
29 static SYSCTL_NODE(_debug_acpi, OID_AUTO, spmc, CTLFLAG_RD | CTLFLAG_MPSAFE,
30     NULL, "SPMC debugging");
31 
32 static char *spmc_ids[] = {
33 	"PNP0D80",
34 	NULL
35 };
36 
37 enum intel_dsm_index {
38 	DSM_ENUM_FUNCTIONS		= 0,
39 	DSM_GET_DEVICE_CONSTRAINTS	= 1,
40 	DSM_GET_CRASH_DUMP_DEVICE	= 2,
41 	DSM_DISPLAY_OFF_NOTIF		= 3,
42 	DSM_DISPLAY_ON_NOTIF		= 4,
43 	DSM_ENTRY_NOTIF			= 5,
44 	DSM_EXIT_NOTIF			= 6,
45 	/* Only for Microsoft DSM set. */
46 	DSM_MODERN_ENTRY_NOTIF		= 7,
47 	DSM_MODERN_EXIT_NOTIF		= 8,
48 };
49 
50 enum amd_dsm_index {
51 	AMD_DSM_ENUM_FUNCTIONS		= 0,
52 	AMD_DSM_GET_DEVICE_CONSTRAINTS	= 1,
53 	AMD_DSM_ENTRY_NOTIF		= 2,
54 	AMD_DSM_EXIT_NOTIF		= 3,
55 	AMD_DSM_DISPLAY_OFF_NOTIF	= 4,
56 	AMD_DSM_DISPLAY_ON_NOTIF	= 5,
57 };
58 
59 enum dsm_set_flags {
60 	DSM_SET_INTEL	= 1 << 0,
61 	DSM_SET_MS	= 1 << 1,
62 	DSM_SET_AMD	= 1 << 2,
63 };
64 
65 struct dsm_set {
66 	enum dsm_set_flags	flag;
67 	const char		*name;
68 	int			revision;
69 	struct uuid		uuid;
70 	uint64_t		dsms_expected;
71 };
72 
73 static struct dsm_set intel_dsm_set = {
74 	.flag = DSM_SET_INTEL,
75 	.name = "Intel",
76 	/*
77 	 * XXX Linux uses 1 for the revision on Intel DSMs, but doesn't explain
78 	 * why.  The commit that introduces this links to a document mentioning
79 	 * revision 0, so default this to 0.
80 	 *
81 	 * The debug.acpi.spmc.intel_dsm_revision sysctl may be used to configure
82 	 * this just in case.
83 	 */
84 	.revision = 0,
85 	.uuid = { /* c4eb40a0-6cd2-11e2-bcfd-0800200c9a66 */
86 		0xc4eb40a0, 0x6cd2, 0x11e2, 0xbc, 0xfd,
87 		{0x08, 0x00, 0x20, 0x0c, 0x9a, 0x66},
88 	},
89 	.dsms_expected = DSM_GET_DEVICE_CONSTRAINTS | DSM_DISPLAY_OFF_NOTIF |
90 	    DSM_DISPLAY_ON_NOTIF | DSM_ENTRY_NOTIF | DSM_EXIT_NOTIF,
91 };
92 
93 SYSCTL_INT(_debug_acpi_spmc, OID_AUTO, intel_dsm_revision, CTLFLAG_RW,
94     &intel_dsm_set.revision, 0,
95     "Revision to use when evaluating Intel SPMC DSMs");
96 
97 static struct dsm_set ms_dsm_set = {
98 	.flag = DSM_SET_MS,
99 	.name = "Microsoft",
100 	.revision = 0,
101 	.uuid = { /* 11e00d56-ce64-47ce-837b-1f898f9aa461 */
102 		0x11e00d56, 0xce64, 0x47ce, 0x83, 0x7b,
103 		{0x1f, 0x89, 0x8f, 0x9a, 0xa4, 0x61},
104 	},
105 	.dsms_expected = DSM_DISPLAY_OFF_NOTIF | DSM_DISPLAY_ON_NOTIF |
106 	    DSM_ENTRY_NOTIF | DSM_EXIT_NOTIF | DSM_MODERN_ENTRY_NOTIF |
107 	    DSM_MODERN_EXIT_NOTIF,
108 };
109 
110 static struct dsm_set amd_dsm_set = {
111 	.flag = DSM_SET_AMD,
112 	.name = "AMD",
113 	/*
114 	 * XXX Linux uses 0 for the revision on AMD DSMs, but at least on the
115 	 * Framework 13 AMD 7040 series, the enum functions DSM only returns a
116 	 * function mask that covers all the DSMs we need to call when called
117 	 * with revision 2.
118 	 *
119 	 * The debug.acpi.spmc.amd_dsm_revision sysctl may be used to configure
120 	 * this just in case.
121 	 */
122 	.revision = 2,
123 	.uuid = { /* e3f32452-febc-43ce-9039-932122d37721 */
124 		0xe3f32452, 0xfebc, 0x43ce, 0x90, 0x39,
125 		{0x93, 0x21, 0x22, 0xd3, 0x77, 0x21},
126 	},
127 	.dsms_expected = AMD_DSM_GET_DEVICE_CONSTRAINTS | AMD_DSM_ENTRY_NOTIF |
128 	    AMD_DSM_EXIT_NOTIF | AMD_DSM_DISPLAY_OFF_NOTIF |
129 	    AMD_DSM_DISPLAY_ON_NOTIF,
130 };
131 
132 SYSCTL_INT(_debug_acpi_spmc, OID_AUTO, amd_dsm_revision, CTLFLAG_RW,
133     &amd_dsm_set.revision, 0, "Revision to use when evaluating AMD SPMC DSMs");
134 
135 union dsm_index {
136 	int			i;
137 	enum intel_dsm_index	regular;
138 	enum amd_dsm_index	amd;
139 };
140 
141 struct acpi_spmc_constraint {
142 	bool		enabled;
143 	char		*name;
144 	int		min_d_state;
145 	ACPI_HANDLE	handle;
146 
147 	/* Unused, spec only. */
148 	uint64_t	lpi_uid;
149 	uint64_t	min_dev_specific_state;
150 
151 	/* Unused, AMD only. */
152 	uint64_t	function_states;
153 };
154 
155 struct acpi_spmc_softc {
156 	device_t		dev;
157 	ACPI_HANDLE		handle;
158 	ACPI_OBJECT		*obj;
159 	enum dsm_set_flags	dsm_sets;
160 
161 	struct eventhandler_entry	*eh_suspend;
162 	struct eventhandler_entry	*eh_resume;
163 
164 	bool				constraints_populated;
165 	size_t				constraint_count;
166 	struct acpi_spmc_constraint	*constraints;
167 };
168 
169 static void	acpi_spmc_check_dsm_set(struct acpi_spmc_softc *sc,
170 		    ACPI_HANDLE handle, struct dsm_set *dsm_set);
171 static int	acpi_spmc_get_constraints(device_t dev);
172 static void	acpi_spmc_free_constraints(struct acpi_spmc_softc *sc);
173 
174 static void	acpi_spmc_suspend(device_t dev, enum power_stype stype);
175 static void	acpi_spmc_resume(device_t dev, enum power_stype stype);
176 
177 static int
acpi_spmc_probe(device_t dev)178 acpi_spmc_probe(device_t dev)
179 {
180 	char			*name;
181 	ACPI_HANDLE		handle;
182 	struct acpi_spmc_softc	*sc;
183 
184 	/* Check that this is an enabled device. */
185 	if (acpi_get_type(dev) != ACPI_TYPE_DEVICE || acpi_disabled("spmc"))
186 		return (ENXIO);
187 
188 	if (ACPI_ID_PROBE(device_get_parent(dev), dev, spmc_ids, &name) > 0)
189 		return (ENXIO);
190 
191 	handle = acpi_get_handle(dev);
192 	if (handle == NULL)
193 		return (ENXIO);
194 
195 	sc = device_get_softc(dev);
196 
197 	/* Check which sets of DSM's are supported. */
198 	sc->dsm_sets = 0;
199 
200 	acpi_spmc_check_dsm_set(sc, handle, &intel_dsm_set);
201 	acpi_spmc_check_dsm_set(sc, handle, &ms_dsm_set);
202 	acpi_spmc_check_dsm_set(sc, handle, &amd_dsm_set);
203 
204 	if (sc->dsm_sets == 0)
205 		return (ENXIO);
206 
207 	device_set_descf(dev, "Low Power S0 Idle (DSM sets 0x%x)",
208 	    sc->dsm_sets);
209 
210 	return (0);
211 }
212 
213 static int
acpi_spmc_attach(device_t dev)214 acpi_spmc_attach(device_t dev)
215 {
216 	struct acpi_spmc_softc *sc = device_get_softc(dev);
217 
218 	sc->dev = dev;
219 
220 	sc->handle = acpi_get_handle(dev);
221 	if (sc->handle == NULL)
222 		return (ENXIO);
223 
224 	sc->constraints_populated = false;
225 	sc->constraint_count = 0;
226 	sc->constraints = NULL;
227 
228 	/* Get device constraints. We can only call this once so do this now. */
229 	acpi_spmc_get_constraints(dev);
230 
231 	sc->eh_suspend = EVENTHANDLER_REGISTER(acpi_post_dev_suspend,
232 	    acpi_spmc_suspend, dev, 0);
233 	sc->eh_resume = EVENTHANDLER_REGISTER(acpi_pre_dev_resume,
234 	    acpi_spmc_resume, dev, 0);
235 
236 	return (0);
237 }
238 
239 static int
acpi_spmc_detach(device_t dev)240 acpi_spmc_detach(device_t dev)
241 {
242 	struct acpi_spmc_softc *sc = device_get_softc(dev);
243 
244 	EVENTHANDLER_DEREGISTER(acpi_post_dev_suspend, sc->eh_suspend);
245 	EVENTHANDLER_DEREGISTER(acpi_pre_dev_resume, sc->eh_resume);
246 
247 	acpi_spmc_free_constraints(device_get_softc(dev));
248 	return (0);
249 }
250 
251 static void
acpi_spmc_check_dsm_set(struct acpi_spmc_softc * sc,ACPI_HANDLE handle,struct dsm_set * dsm_set)252 acpi_spmc_check_dsm_set(struct acpi_spmc_softc *sc, ACPI_HANDLE handle,
253     struct dsm_set *dsm_set)
254 {
255 	const uint64_t dsms_supported = acpi_DSMQuery(handle,
256 	    (uint8_t *)&dsm_set->uuid, dsm_set->revision);
257 
258 	/*
259 	 * Check if DSM set supported at all.  We do this by checking the
260 	 * existence of "enum functions".
261 	 */
262 	if ((dsms_supported & 1) == 0)
263 		return;
264 	if ((dsms_supported & dsm_set->dsms_expected)
265 	    != dsm_set->dsms_expected) {
266 		device_printf(sc->dev, "DSM set %s does not support expected "
267 		    "DSMs (%#" PRIx64 " vs %#" PRIx64 "). "
268 		    "Some methods may fail.\n",
269 		    dsm_set->name, dsms_supported, dsm_set->dsms_expected);
270 	}
271 	sc->dsm_sets |= dsm_set->flag;
272 }
273 
274 static void
acpi_spmc_free_constraints(struct acpi_spmc_softc * sc)275 acpi_spmc_free_constraints(struct acpi_spmc_softc *sc)
276 {
277 	if (sc->constraints == NULL)
278 		return;
279 
280 	for (size_t i = 0; i < sc->constraint_count; i++) {
281 		if (sc->constraints[i].name != NULL)
282 			free(sc->constraints[i].name, M_TEMP);
283 	}
284 
285 	free(sc->constraints, M_TEMP);
286 	sc->constraints = NULL;
287 }
288 
289 static int
acpi_spmc_get_constraints_spec(struct acpi_spmc_softc * sc,ACPI_OBJECT * object)290 acpi_spmc_get_constraints_spec(struct acpi_spmc_softc *sc, ACPI_OBJECT *object)
291 {
292 	struct acpi_spmc_constraint *constraint;
293 	int		revision;
294 	ACPI_OBJECT	*constraint_obj;
295 	ACPI_OBJECT	*name_obj;
296 	ACPI_OBJECT	*detail;
297 	ACPI_OBJECT	*constraint_package;
298 
299 	KASSERT(sc->constraints_populated == false,
300 	    ("constraints already populated"));
301 
302 	sc->constraint_count = object->Package.Count;
303 	sc->constraints = malloc(sc->constraint_count * sizeof *sc->constraints,
304 	    M_TEMP, M_WAITOK | M_ZERO);
305 
306 	/*
307 	 * The value of sc->constraint_count can change during the loop, so
308 	 * iterate until object->Package.Count so we actually go over all
309 	 * elements in the package.
310 	 */
311 	for (size_t i = 0; i < object->Package.Count; i++) {
312 		constraint_obj = &object->Package.Elements[i];
313 		constraint = &sc->constraints[i];
314 
315 		constraint->enabled =
316 		    constraint_obj->Package.Elements[1].Integer.Value;
317 
318 		name_obj = &constraint_obj->Package.Elements[0];
319 		constraint->name = strdup(name_obj->String.Pointer, M_TEMP);
320 		if (constraint->name == NULL) {
321 			acpi_spmc_free_constraints(sc);
322 			return (ENOMEM);
323 		}
324 
325 		detail = &constraint_obj->Package.Elements[2];
326 		/*
327 		 * The first element in the device constraint detail package is
328 		 * the revision, and should always be zero.
329 		 */
330 		revision = detail->Package.Elements[0].Integer.Value;
331 		if (revision != 0) {
332 			device_printf(sc->dev, "Unknown revision %d for "
333 			    "device constraint detail package\n", revision);
334 			sc->constraint_count--;
335 			continue;
336 		}
337 
338 		constraint_package = &detail->Package.Elements[1];
339 
340 		constraint->lpi_uid =
341 		    constraint_package->Package.Elements[0].Integer.Value;
342 		constraint->min_d_state =
343 		    constraint_package->Package.Elements[1].Integer.Value;
344 		constraint->min_dev_specific_state =
345 		    constraint_package->Package.Elements[2].Integer.Value;
346 	}
347 
348 	sc->constraints_populated = true;
349 	return (0);
350 }
351 
352 static int
acpi_spmc_get_constraints_amd(struct acpi_spmc_softc * sc,ACPI_OBJECT * object)353 acpi_spmc_get_constraints_amd(struct acpi_spmc_softc *sc, ACPI_OBJECT *object)
354 {
355 	size_t		constraint_count;
356 	ACPI_OBJECT	*constraint_obj;
357 	ACPI_OBJECT	*constraints;
358 	struct acpi_spmc_constraint *constraint;
359 	ACPI_OBJECT	*name_obj;
360 
361 	KASSERT(sc->constraints_populated == false,
362 	    ("constraints already populated"));
363 
364 	/*
365 	 * First element in the package is unknown.
366 	 * Second element is the number of device constraints.
367 	 * Third element is the list of device constraints itself.
368 	 */
369 	constraint_count = object->Package.Elements[1].Integer.Value;
370 	constraints = &object->Package.Elements[2];
371 
372 	if (constraints->Package.Count != constraint_count) {
373 		device_printf(sc->dev, "constraint count mismatch (%d to %zu)\n",
374 		    constraints->Package.Count, constraint_count);
375 		return (ENXIO);
376 	}
377 
378 	sc->constraint_count = constraint_count;
379 	sc->constraints = malloc(constraint_count * sizeof *sc->constraints,
380 	    M_TEMP, M_WAITOK | M_ZERO);
381 
382 	for (size_t i = 0; i < constraint_count; i++) {
383 		/* Parse the constraint package. */
384 		constraint_obj = &constraints->Package.Elements[i];
385 		if (constraint_obj->Package.Count != 4) {
386 			device_printf(sc->dev, "constraint %zu has %d elements\n",
387 			    i, constraint_obj->Package.Count);
388 			acpi_spmc_free_constraints(sc);
389 			return (ENXIO);
390 		}
391 
392 		constraint = &sc->constraints[i];
393 		constraint->enabled =
394 		    constraint_obj->Package.Elements[0].Integer.Value;
395 
396 		name_obj = &constraint_obj->Package.Elements[1];
397 		constraint->name = strdup(name_obj->String.Pointer, M_TEMP);
398 		if (constraint->name == NULL) {
399 			acpi_spmc_free_constraints(sc);
400 			return (ENOMEM);
401 		}
402 
403 		constraint->function_states =
404 		    constraint_obj->Package.Elements[2].Integer.Value;
405 		constraint->min_d_state =
406 		    constraint_obj->Package.Elements[3].Integer.Value;
407 	}
408 
409 	sc->constraints_populated = true;
410 	return (0);
411 }
412 
413 static int
acpi_spmc_get_constraints(device_t dev)414 acpi_spmc_get_constraints(device_t dev)
415 {
416 	struct acpi_spmc_softc	*sc;
417 	union dsm_index		dsm_index;
418 	struct dsm_set		*dsm_set;
419 	ACPI_STATUS		status;
420 	ACPI_BUFFER		result;
421 	ACPI_OBJECT		*object;
422 	bool			is_amd;
423 	int			rv;
424 	struct acpi_spmc_constraint *constraint;
425 
426 	sc = device_get_softc(dev);
427 	if (sc->constraints_populated)
428 		return (0);
429 
430 	/* The Microsoft DSM set doesn't have this DSM. */
431 	is_amd = (sc->dsm_sets & DSM_SET_AMD) != 0;
432 	if (is_amd) {
433 		dsm_set = &amd_dsm_set;
434 		dsm_index.amd = AMD_DSM_GET_DEVICE_CONSTRAINTS;
435 	} else {
436 		dsm_set = &intel_dsm_set;
437 		dsm_index.regular = DSM_GET_DEVICE_CONSTRAINTS;
438 	}
439 
440 	/* XXX It seems like this DSM fails if called more than once. */
441 	status = acpi_EvaluateDSMTyped(sc->handle, (uint8_t *)&dsm_set->uuid,
442 	    dsm_set->revision, dsm_index.i, NULL, &result,
443 	    ACPI_TYPE_PACKAGE);
444 	if (ACPI_FAILURE(status)) {
445 		device_printf(dev, "%s failed to call %s DSM %d (rev %d)\n",
446 		    __func__, dsm_set->name, dsm_index.i, dsm_set->revision);
447 		return (ENXIO);
448 	}
449 
450 	object = (ACPI_OBJECT *)result.Pointer;
451 	if (is_amd)
452 		rv = acpi_spmc_get_constraints_amd(sc, object);
453 	else
454 		rv = acpi_spmc_get_constraints_spec(sc, object);
455 	AcpiOsFree(object);
456 	if (rv != 0)
457 		return (rv);
458 
459 	/* Get handles for each constraint device. */
460 	for (size_t i = 0; i < sc->constraint_count; i++) {
461 		constraint = &sc->constraints[i];
462 
463 		status = acpi_GetHandleInScope(sc->handle,
464 		    __DECONST(char *, constraint->name), &constraint->handle);
465 		if (ACPI_FAILURE(status)) {
466 			device_printf(dev, "failed to get handle for %s\n",
467 			    constraint->name);
468 			constraint->handle = NULL;
469 		}
470 	}
471 	return (0);
472 }
473 
474 static void
acpi_spmc_check_constraints(struct acpi_spmc_softc * sc)475 acpi_spmc_check_constraints(struct acpi_spmc_softc *sc)
476 {
477 	bool violation = false;
478 
479 	KASSERT(sc->constraints_populated, ("constraints not populated"));
480 	for (size_t i = 0; i < sc->constraint_count; i++) {
481 		struct acpi_spmc_constraint *constraint = &sc->constraints[i];
482 
483 		if (!constraint->enabled)
484 			continue;
485 		if (constraint->handle == NULL)
486 			continue;
487 
488 		ACPI_STATUS status = acpi_GetHandleInScope(sc->handle,
489 		    __DECONST(char *, constraint->name), &constraint->handle);
490 		if (ACPI_FAILURE(status)) {
491 			device_printf(sc->dev, "failed to get handle for %s\n",
492 			    constraint->name);
493 			constraint->handle = NULL;
494 		}
495 		if (constraint->handle == NULL)
496 			continue;
497 
498 #ifdef notyet
499 		int d_state;
500 		if (ACPI_FAILURE(acpi_pwr_get_state(constraint->handle, &d_state)))
501 			continue;
502 		if (d_state < constraint->min_d_state) {
503 			device_printf(sc->dev, "constraint for device %s"
504 			    " violated (minimum D-state required was %s, actual"
505 			    " D-state is %s), might fail to enter LPI state\n",
506 			    constraint->name,
507 			    acpi_d_state_to_str(constraint->min_d_state),
508 			    acpi_d_state_to_str(d_state));
509 			violation = true;
510 		}
511 #endif
512 	}
513 	if (!violation)
514 		device_printf(sc->dev,
515 		    "all device power constraints respected!\n");
516 }
517 
518 static void
acpi_spmc_run_dsm(device_t dev,struct dsm_set * dsm_set,int index)519 acpi_spmc_run_dsm(device_t dev, struct dsm_set *dsm_set, int index)
520 {
521 	struct acpi_spmc_softc	*sc;
522 	ACPI_STATUS		status;
523 	ACPI_BUFFER		result;
524 
525 	sc = device_get_softc(dev);
526 
527 	status = acpi_EvaluateDSMTyped(sc->handle, (uint8_t *)&dsm_set->uuid,
528 	    dsm_set->revision, index, NULL, &result, ACPI_TYPE_ANY);
529 
530 	if (ACPI_FAILURE(status)) {
531 		device_printf(dev, "%s failed to call %s DSM %d (rev %d)\n",
532 		    __func__, dsm_set->name, index, dsm_set->revision);
533 		return;
534 	}
535 
536 	AcpiOsFree(result.Pointer);
537 }
538 
539 /*
540  * Try running the DSMs from all the DSM sets we have, as them failing costs us
541  * nothing, and it seems like on AMD platforms, both the AMD entry and Microsoft
542  * "modern" DSM's are required for it to enter modern standby.
543  *
544  * This is what Linux does too.
545  */
546 static void
acpi_spmc_display_off_notif(device_t dev)547 acpi_spmc_display_off_notif(device_t dev)
548 {
549 	struct acpi_spmc_softc *sc = device_get_softc(dev);
550 
551 	if ((sc->dsm_sets & DSM_SET_INTEL) != 0)
552 		acpi_spmc_run_dsm(dev, &intel_dsm_set, DSM_DISPLAY_OFF_NOTIF);
553 	if ((sc->dsm_sets & DSM_SET_MS) != 0)
554 		acpi_spmc_run_dsm(dev, &ms_dsm_set, DSM_DISPLAY_OFF_NOTIF);
555 	if ((sc->dsm_sets & DSM_SET_AMD) != 0)
556 		acpi_spmc_run_dsm(dev, &amd_dsm_set, AMD_DSM_DISPLAY_OFF_NOTIF);
557 }
558 
559 static void
acpi_spmc_display_on_notif(device_t dev)560 acpi_spmc_display_on_notif(device_t dev)
561 {
562 	struct acpi_spmc_softc *sc = device_get_softc(dev);
563 
564 	if ((sc->dsm_sets & DSM_SET_INTEL) != 0)
565 		acpi_spmc_run_dsm(dev, &intel_dsm_set, DSM_DISPLAY_ON_NOTIF);
566 	if ((sc->dsm_sets & DSM_SET_MS) != 0)
567 		acpi_spmc_run_dsm(dev, &ms_dsm_set, DSM_DISPLAY_ON_NOTIF);
568 	if ((sc->dsm_sets & DSM_SET_AMD) != 0)
569 		acpi_spmc_run_dsm(dev, &amd_dsm_set, AMD_DSM_DISPLAY_ON_NOTIF);
570 }
571 
572 static void
acpi_spmc_entry_notif(device_t dev)573 acpi_spmc_entry_notif(device_t dev)
574 {
575 	struct acpi_spmc_softc *sc = device_get_softc(dev);
576 
577 	acpi_spmc_check_constraints(sc);
578 
579 	if ((sc->dsm_sets & DSM_SET_AMD) != 0)
580 		acpi_spmc_run_dsm(dev, &amd_dsm_set, AMD_DSM_ENTRY_NOTIF);
581 	if ((sc->dsm_sets & DSM_SET_MS) != 0) {
582 		acpi_spmc_run_dsm(dev, &ms_dsm_set, DSM_MODERN_ENTRY_NOTIF);
583 		acpi_spmc_run_dsm(dev, &ms_dsm_set, DSM_ENTRY_NOTIF);
584 	}
585 	if ((sc->dsm_sets & DSM_SET_INTEL) != 0)
586 		acpi_spmc_run_dsm(dev, &intel_dsm_set, DSM_ENTRY_NOTIF);
587 }
588 
589 static void
acpi_spmc_exit_notif(device_t dev)590 acpi_spmc_exit_notif(device_t dev)
591 {
592 	struct acpi_spmc_softc *sc = device_get_softc(dev);
593 
594 	if ((sc->dsm_sets & DSM_SET_INTEL) != 0)
595 		acpi_spmc_run_dsm(dev, &intel_dsm_set, DSM_EXIT_NOTIF);
596 	if ((sc->dsm_sets & DSM_SET_AMD) != 0)
597 		acpi_spmc_run_dsm(dev, &amd_dsm_set, AMD_DSM_EXIT_NOTIF);
598 	if ((sc->dsm_sets & DSM_SET_MS) != 0) {
599 		acpi_spmc_run_dsm(dev, &ms_dsm_set, DSM_EXIT_NOTIF);
600 		acpi_spmc_run_dsm(dev, &ms_dsm_set, DSM_MODERN_EXIT_NOTIF);
601 	}
602 }
603 
604 static void
acpi_spmc_suspend(device_t dev,enum power_stype stype)605 acpi_spmc_suspend(device_t dev, enum power_stype stype)
606 {
607 	if (stype != POWER_STYPE_SUSPEND_TO_IDLE)
608 		return;
609 
610 	acpi_spmc_display_off_notif(dev);
611 	acpi_spmc_entry_notif(dev);
612 }
613 
614 static void
acpi_spmc_resume(device_t dev,enum power_stype stype)615 acpi_spmc_resume(device_t dev, enum power_stype stype)
616 {
617 	if (stype != POWER_STYPE_SUSPEND_TO_IDLE)
618 		return;
619 
620 	acpi_spmc_exit_notif(dev);
621 	acpi_spmc_display_on_notif(dev);
622 }
623 
624 static device_method_t acpi_spmc_methods[] = {
625 	DEVMETHOD(device_probe,		acpi_spmc_probe),
626 	DEVMETHOD(device_attach,	acpi_spmc_attach),
627 	DEVMETHOD(device_detach,	acpi_spmc_detach),
628 	DEVMETHOD_END
629 };
630 
631 static driver_t acpi_spmc_driver = {
632 	"acpi_spmc",
633 	acpi_spmc_methods,
634 	sizeof(struct acpi_spmc_softc),
635 };
636 
637 DRIVER_MODULE_ORDERED(acpi_spmc, acpi, acpi_spmc_driver, NULL, NULL, SI_ORDER_ANY);
638 MODULE_DEPEND(acpi_spmc, acpi, 1, 1, 1);
639