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