The branch main has been updated by obiwac:

URL: 
https://cgit.FreeBSD.org/src/commit/?id=c5daa5a4c32c9b1ecb506ddf1a80579c93c3ea6d

commit c5daa5a4c32c9b1ecb506ddf1a80579c93c3ea6d
Author:     Aymeric Wibo <[email protected]>
AuthorDate: 2025-06-14 15:30:44 +0000
Commit:     Aymeric Wibo <[email protected]>
CommitDate: 2026-01-26 13:42:11 +0000

    acpi_spmc: Add system power management controller driver
    
    Add SPMC (system power management controller) driver as acpi_spmc. This
    is the device which provides the LPI device D-state constraints and
    allows for OSPM to send S0ix/modern standby entry/exit notifications.
    This supports the original Intel DSM
    
(https://uefi.org/sites/default/files/resources/Intel_ACPI_Low_Power_S0_Idle.pdf,
    untested), the AMD DSM (tested), and the Microsoft DSM (tested).
    
    Before entry, acpi_spmc_check_constraints is called to notify of any
    violated power constraints. This will use acpi_pwr_get_state to get
    current device D-states when that gets added back.
    
    Reviewed by:    olce
    Tested by:      jkim, Oleksandr Kryvulia, Matthias Lanter
    Approved by:    olce
    Sponsored by:   The FreeBSD Foundation
    Differential Revision:  https://reviews.freebsd.org/D48387
---
 share/man/man4/acpi.4      |   4 +-
 sys/conf/files             |   1 +
 sys/dev/acpica/acpi.c      |   1 +
 sys/dev/acpica/acpi_spmc.c | 618 +++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 623 insertions(+), 1 deletion(-)

diff --git a/share/man/man4/acpi.4 b/share/man/man4/acpi.4
index cdad3ceeedfc..9c24635c561d 100644
--- a/share/man/man4/acpi.4
+++ b/share/man/man4/acpi.4
@@ -486,10 +486,12 @@ Embedded controller driver
 Fan driver
 .It Li ACPI_OEM
 Platform-specific driver for hotkeys, LED, etc.
-.It Li ACPI_POWER
+.It Li ACPI_POWERRES
 Power resource driver
 .It Li ACPI_PROCESSOR
 CPU driver
+.It Li ACPI_SPMC
+System power management controller driver
 .It Li ACPI_THERMAL
 Thermal zone driver
 .It Li ACPI_TIMER
diff --git a/sys/conf/files b/sys/conf/files
index 66b84fea1bc0..97834f05431d 100644
--- a/sys/conf/files
+++ b/sys/conf/files
@@ -776,6 +776,7 @@ dev/acpica/acpi_thermal.c   optional acpi
 dev/acpica/acpi_throttle.c     optional acpi
 dev/acpica/acpi_video.c                optional acpi_video acpi
 dev/acpica/acpi_dock.c         optional acpi_dock acpi
+dev/acpica/acpi_spmc.c         optional acpi
 dev/adlink/adlink.c            optional adlink
 dev/ae/if_ae.c                 optional ae pci
 dev/age/if_age.c               optional age pci
diff --git a/sys/dev/acpica/acpi.c b/sys/dev/acpica/acpi.c
index c76bc8477537..e43ef72ca9d2 100644
--- a/sys/dev/acpica/acpi.c
+++ b/sys/dev/acpica/acpi.c
@@ -4618,6 +4618,7 @@ static struct debugtag    dbg_layer[] = {
     {"ACPI_FAN",               ACPI_FAN},
     {"ACPI_POWERRES",          ACPI_POWERRES},
     {"ACPI_PROCESSOR",         ACPI_PROCESSOR},
+    {"ACPI_SPMC",              ACPI_SPMC},
     {"ACPI_THERMAL",           ACPI_THERMAL},
     {"ACPI_TIMER",             ACPI_TIMER},
     {"ACPI_ALL_DRIVERS",       ACPI_ALL_DRIVERS},
diff --git a/sys/dev/acpica/acpi_spmc.c b/sys/dev/acpica/acpi_spmc.c
new file mode 100644
index 000000000000..57593d9ccae1
--- /dev/null
+++ b/sys/dev/acpica/acpi_spmc.c
@@ -0,0 +1,618 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2024-2025 The FreeBSD Foundation
+ *
+ * This software was developed by Aymeric Wibo <[email protected]>
+ * under sponsorship from the FreeBSD Foundation.
+ */
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/malloc.h>
+#include <sys/uuid.h>
+#include <sys/kdb.h>
+
+#include <contrib/dev/acpica/include/acpi.h>
+#include <contrib/dev/acpica/include/accommon.h>
+
+#include <dev/acpica/acpivar.h>
+
+/* Hooks for the ACPI CA debugging infrastructure */
+#define _COMPONENT     ACPI_SPMC
+ACPI_MODULE_NAME("SPMC")
+
+static SYSCTL_NODE(_debug_acpi, OID_AUTO, spmc, CTLFLAG_RD | CTLFLAG_MPSAFE,
+    NULL, "SPMC debugging");
+
+static char *spmc_ids[] = {
+       "PNP0D80",
+       NULL
+};
+
+enum intel_dsm_index {
+       DSM_ENUM_FUNCTIONS              = 0,
+       DSM_GET_DEVICE_CONSTRAINTS      = 1,
+       DSM_GET_CRASH_DUMP_DEVICE       = 2,
+       DSM_DISPLAY_OFF_NOTIF           = 3,
+       DSM_DISPLAY_ON_NOTIF            = 4,
+       DSM_ENTRY_NOTIF                 = 5,
+       DSM_EXIT_NOTIF                  = 6,
+       /* Only for Microsoft DSM set. */
+       DSM_MODERN_ENTRY_NOTIF          = 7,
+       DSM_MODERN_EXIT_NOTIF           = 8,
+};
+
+enum amd_dsm_index {
+       AMD_DSM_ENUM_FUNCTIONS          = 0,
+       AMD_DSM_GET_DEVICE_CONSTRAINTS  = 1,
+       AMD_DSM_ENTRY_NOTIF             = 2,
+       AMD_DSM_EXIT_NOTIF              = 3,
+       AMD_DSM_DISPLAY_OFF_NOTIF       = 4,
+       AMD_DSM_DISPLAY_ON_NOTIF        = 5,
+};
+
+enum dsm_set_flags {
+       DSM_SET_INTEL   = 1 << 0,
+       DSM_SET_MS      = 1 << 1,
+       DSM_SET_AMD     = 1 << 2,
+};
+
+struct dsm_set {
+       enum dsm_set_flags      flag;
+       const char              *name;
+       int                     revision;
+       struct uuid             uuid;
+       uint64_t                dsms_expected;
+};
+
+static struct dsm_set intel_dsm_set = {
+       .flag = DSM_SET_INTEL,
+       .name = "Intel",
+       /*
+        * XXX Linux uses 1 for the revision on Intel DSMs, but doesn't explain
+        * why.  The commit that introduces this links to a document mentioning
+        * revision 0, so default this to 0.
+        *
+        * The debug.acpi.spmc.intel_dsm_revision sysctl may be used to 
configure
+        * this just in case.
+        */
+       .revision = 0,
+       .uuid = { /* c4eb40a0-6cd2-11e2-bcfd-0800200c9a66 */
+               0xc4eb40a0, 0x6cd2, 0x11e2, 0xbc, 0xfd,
+               {0x08, 0x00, 0x20, 0x0c, 0x9a, 0x66},
+       },
+       .dsms_expected = DSM_GET_DEVICE_CONSTRAINTS | DSM_DISPLAY_OFF_NOTIF |
+           DSM_DISPLAY_ON_NOTIF | DSM_ENTRY_NOTIF | DSM_EXIT_NOTIF,
+};
+
+SYSCTL_INT(_debug_acpi_spmc, OID_AUTO, intel_dsm_revision, CTLFLAG_RW,
+    &intel_dsm_set.revision, 0,
+    "Revision to use when evaluating Intel SPMC DSMs");
+
+static struct dsm_set ms_dsm_set = {
+       .flag = DSM_SET_MS,
+       .name = "Microsoft",
+       .revision = 0,
+       .uuid = { /* 11e00d56-ce64-47ce-837b-1f898f9aa461 */
+               0x11e00d56, 0xce64, 0x47ce, 0x83, 0x7b,
+               {0x1f, 0x89, 0x8f, 0x9a, 0xa4, 0x61},
+       },
+       .dsms_expected = DSM_DISPLAY_OFF_NOTIF | DSM_DISPLAY_ON_NOTIF |
+           DSM_ENTRY_NOTIF | DSM_EXIT_NOTIF | DSM_MODERN_ENTRY_NOTIF |
+           DSM_MODERN_EXIT_NOTIF,
+};
+
+static struct dsm_set amd_dsm_set = {
+       .flag = DSM_SET_AMD,
+       .name = "AMD",
+       /*
+        * XXX Linux uses 0 for the revision on AMD DSMs, but at least on the
+        * Framework 13 AMD 7040 series, the enum functions DSM only returns a
+        * function mask that covers all the DSMs we need to call when called
+        * with revision 2.
+        *
+        * The debug.acpi.spmc.amd_dsm_revision sysctl may be used to configure
+        * this just in case.
+        */
+       .revision = 2,
+       .uuid = { /* e3f32452-febc-43ce-9039-932122d37721 */
+               0xe3f32452, 0xfebc, 0x43ce, 0x90, 0x39,
+               {0x93, 0x21, 0x22, 0xd3, 0x77, 0x21},
+       },
+       .dsms_expected = AMD_DSM_GET_DEVICE_CONSTRAINTS | AMD_DSM_ENTRY_NOTIF |
+           AMD_DSM_EXIT_NOTIF | AMD_DSM_DISPLAY_OFF_NOTIF |
+           AMD_DSM_DISPLAY_ON_NOTIF,
+};
+
+SYSCTL_INT(_debug_acpi_spmc, OID_AUTO, amd_dsm_revision, CTLFLAG_RW,
+    &amd_dsm_set.revision, 0, "Revision to use when evaluating AMD SPMC DSMs");
+
+union dsm_index {
+       int                     i;
+       enum intel_dsm_index    regular;
+       enum amd_dsm_index      amd;
+};
+
+struct acpi_spmc_constraint {
+       bool            enabled;
+       char            *name;
+       int             min_d_state;
+       ACPI_HANDLE     handle;
+
+       /* Unused, spec only. */
+       uint64_t        lpi_uid;
+       uint64_t        min_dev_specific_state;
+
+       /* Unused, AMD only. */
+       uint64_t        function_states;
+};
+
+struct acpi_spmc_softc {
+       device_t                dev;
+       ACPI_HANDLE             handle;
+       ACPI_OBJECT             *obj;
+       enum dsm_set_flags      dsm_sets;
+
+       bool                            constraints_populated;
+       size_t                          constraint_count;
+       struct acpi_spmc_constraint     *constraints;
+};
+
+static void    acpi_spmc_check_dsm_set(struct acpi_spmc_softc *sc,
+                   ACPI_HANDLE handle, struct dsm_set *dsm_set);
+static int     acpi_spmc_get_constraints(device_t dev);
+static void    acpi_spmc_free_constraints(struct acpi_spmc_softc *sc);
+
+static int
+acpi_spmc_probe(device_t dev)
+{
+       char                    *name;
+       ACPI_HANDLE             handle;
+       struct acpi_spmc_softc  *sc;
+
+       /* Check that this is an enabled device. */
+       if (acpi_get_type(dev) != ACPI_TYPE_DEVICE || acpi_disabled("spmc"))
+               return (ENXIO);
+
+       if (ACPI_ID_PROBE(device_get_parent(dev), dev, spmc_ids, &name) > 0)
+               return (ENXIO);
+
+       handle = acpi_get_handle(dev);
+       if (handle == NULL)
+               return (ENXIO);
+
+       sc = device_get_softc(dev);
+
+       /* Check which sets of DSM's are supported. */
+       sc->dsm_sets = 0;
+
+       acpi_spmc_check_dsm_set(sc, handle, &intel_dsm_set);
+       acpi_spmc_check_dsm_set(sc, handle, &ms_dsm_set);
+       acpi_spmc_check_dsm_set(sc, handle, &amd_dsm_set);
+
+       if (sc->dsm_sets == 0)
+               return (ENXIO);
+
+       device_set_descf(dev, "Low Power S0 Idle (DSM sets 0x%x)",
+           sc->dsm_sets);
+
+       return (0);
+}
+
+static int
+acpi_spmc_attach(device_t dev)
+{
+       struct acpi_spmc_softc *sc;
+
+       sc = device_get_softc(dev);
+       sc->dev = dev;
+
+       sc->handle = acpi_get_handle(dev);
+       if (sc->handle == NULL)
+               return (ENXIO);
+
+       sc->constraints_populated = false;
+       sc->constraint_count = 0;
+       sc->constraints = NULL;
+
+       /* Get device constraints. We can only call this once so do this now. */
+       acpi_spmc_get_constraints(sc->dev);
+
+       return (0);
+}
+
+static int
+acpi_spmc_detach(device_t dev)
+{
+       acpi_spmc_free_constraints(device_get_softc(dev));
+       return (0);
+}
+
+static void
+acpi_spmc_check_dsm_set(struct acpi_spmc_softc *sc, ACPI_HANDLE handle,
+    struct dsm_set *dsm_set)
+{
+       const uint64_t dsms_supported = acpi_DSMQuery(handle,
+           (uint8_t *)&dsm_set->uuid, dsm_set->revision);
+
+       /*
+        * Check if DSM set supported at all.  We do this by checking the
+        * existence of "enum functions".
+        */
+       if ((dsms_supported & 1) == 0)
+               return;
+       if ((dsms_supported & dsm_set->dsms_expected)
+           != dsm_set->dsms_expected) {
+               device_printf(sc->dev, "DSM set %s does not support expected "
+                   "DSMs (0x%lx vs 0x%lx). Some methods may fail.\n",
+                   dsm_set->name, dsms_supported, dsm_set->dsms_expected);
+       }
+       sc->dsm_sets |= dsm_set->flag;
+}
+
+static void
+acpi_spmc_free_constraints(struct acpi_spmc_softc *sc)
+{
+       if (sc->constraints == NULL)
+               return;
+
+       for (size_t i = 0; i < sc->constraint_count; i++) {
+               if (sc->constraints[i].name != NULL)
+                       free(sc->constraints[i].name, M_TEMP);
+       }
+
+       free(sc->constraints, M_TEMP);
+       sc->constraints = NULL;
+}
+
+static int
+acpi_spmc_get_constraints_spec(struct acpi_spmc_softc *sc, ACPI_OBJECT *object)
+{
+       struct acpi_spmc_constraint *constraint;
+       int             revision;
+       ACPI_OBJECT     *constraint_obj;
+       ACPI_OBJECT     *name_obj;
+       ACPI_OBJECT     *detail;
+       ACPI_OBJECT     *constraint_package;
+
+       KASSERT(sc->constraints_populated == false,
+           ("constraints already populated"));
+
+       sc->constraint_count = object->Package.Count;
+       sc->constraints = malloc(sc->constraint_count * sizeof *sc->constraints,
+           M_TEMP, M_WAITOK | M_ZERO);
+
+       /*
+        * The value of sc->constraint_count can change during the loop, so
+        * iterate until object->Package.Count so we actually go over all
+        * elements in the package.
+        */
+       for (size_t i = 0; i < object->Package.Count; i++) {
+               constraint_obj = &object->Package.Elements[i];
+               constraint = &sc->constraints[i];
+
+               constraint->enabled =
+                   constraint_obj->Package.Elements[1].Integer.Value;
+
+               name_obj = &constraint_obj->Package.Elements[0];
+               constraint->name = strdup(name_obj->String.Pointer, M_TEMP);
+               if (constraint->name == NULL) {
+                       acpi_spmc_free_constraints(sc);
+                       return (ENOMEM);
+               }
+
+               /*
+                * The first element in the device constraint detail package is
+                * the revision, and should always be zero.
+                */
+               revision = constraint_obj->Package.Elements[0].Integer.Value;
+               if (revision != 0) {
+                       device_printf(sc->dev, "Unknown revision %d for "
+                           "device constraint detail package\n", revision);
+                       sc->constraint_count--;
+                       continue;
+               }
+
+               detail = &constraint_obj->Package.Elements[2];
+               constraint_package = &detail->Package.Elements[1];
+
+               constraint->lpi_uid =
+                   constraint_package->Package.Elements[0].Integer.Value;
+               constraint->min_d_state =
+                   constraint_package->Package.Elements[1].Integer.Value;
+               constraint->min_dev_specific_state =
+                   constraint_package->Package.Elements[2].Integer.Value;
+       }
+
+       sc->constraints_populated = true;
+       return (0);
+}
+
+static int
+acpi_spmc_get_constraints_amd(struct acpi_spmc_softc *sc, ACPI_OBJECT *object)
+{
+       size_t          constraint_count;
+       ACPI_OBJECT     *constraint_obj;
+       ACPI_OBJECT     *constraints;
+       struct acpi_spmc_constraint *constraint;
+       ACPI_OBJECT     *name_obj;
+
+       KASSERT(sc->constraints_populated == false,
+           ("constraints already populated"));
+
+       /*
+        * First element in the package is unknown.
+        * Second element is the number of device constraints.
+        * Third element is the list of device constraints itself.
+        */
+       constraint_count = object->Package.Elements[1].Integer.Value;
+       constraints = &object->Package.Elements[2];
+
+       if (constraints->Package.Count != constraint_count) {
+               device_printf(sc->dev, "constraint count mismatch (%d to 
%zu)\n",
+                   constraints->Package.Count, constraint_count);
+               return (ENXIO);
+       }
+
+       sc->constraint_count = constraint_count;
+       sc->constraints = malloc(constraint_count * sizeof *sc->constraints,
+           M_TEMP, M_WAITOK | M_ZERO);
+
+       for (size_t i = 0; i < constraint_count; i++) {
+               /* Parse the constraint package. */
+               constraint_obj = &constraints->Package.Elements[i];
+               if (constraint_obj->Package.Count != 4) {
+                       device_printf(sc->dev, "constraint %zu has %d 
elements\n",
+                           i, constraint_obj->Package.Count);
+                       acpi_spmc_free_constraints(sc);
+                       return (ENXIO);
+               }
+
+               constraint = &sc->constraints[i];
+               constraint->enabled =
+                   constraint_obj->Package.Elements[0].Integer.Value;
+
+               name_obj = &constraint_obj->Package.Elements[1];
+               constraint->name = strdup(name_obj->String.Pointer, M_TEMP);
+               if (constraint->name == NULL) {
+                       acpi_spmc_free_constraints(sc);
+                       return (ENOMEM);
+               }
+
+               constraint->function_states =
+                   constraint_obj->Package.Elements[2].Integer.Value;
+               constraint->min_d_state =
+                   constraint_obj->Package.Elements[3].Integer.Value;
+       }
+
+       sc->constraints_populated = true;
+       return (0);
+}
+
+static int
+acpi_spmc_get_constraints(device_t dev)
+{
+       struct acpi_spmc_softc  *sc;
+       union dsm_index         dsm_index;
+       struct dsm_set          *dsm_set;
+       ACPI_STATUS             status;
+       ACPI_BUFFER             result;
+       ACPI_OBJECT             *object;
+       bool                    is_amd;
+       int                     rv;
+       struct acpi_spmc_constraint *constraint;
+
+       sc = device_get_softc(dev);
+       if (sc->constraints_populated)
+               return (0);
+
+       /* The Microsoft DSM set doesn't have this DSM. */
+       is_amd = (sc->dsm_sets & DSM_SET_AMD) != 0;
+       if (is_amd) {
+               dsm_set = &amd_dsm_set;
+               dsm_index.amd = AMD_DSM_GET_DEVICE_CONSTRAINTS;
+       } else {
+               dsm_set = &intel_dsm_set;
+               dsm_index.regular = DSM_GET_DEVICE_CONSTRAINTS;
+       }
+
+       /* XXX It seems like this DSM fails if called more than once. */
+       status = acpi_EvaluateDSMTyped(sc->handle, (uint8_t *)&dsm_set->uuid,
+           dsm_set->revision, dsm_index.i, NULL, &result,
+           ACPI_TYPE_PACKAGE);
+       if (ACPI_FAILURE(status)) {
+               device_printf(dev, "%s failed to call %s DSM %d (rev %d)\n",
+                   __func__, dsm_set->name, dsm_index.i, dsm_set->revision);
+               return (ENXIO);
+       }
+
+       object = (ACPI_OBJECT *)result.Pointer;
+       if (is_amd)
+               rv = acpi_spmc_get_constraints_amd(sc, object);
+       else
+               rv = acpi_spmc_get_constraints_spec(sc, object);
+       AcpiOsFree(object);
+       if (rv != 0)
+               return (rv);
+
+       /* Get handles for each constraint device. */
+       for (size_t i = 0; i < sc->constraint_count; i++) {
+               constraint = &sc->constraints[i];
+
+               status = acpi_GetHandleInScope(sc->handle,
+                   __DECONST(char *, constraint->name), &constraint->handle);
+               if (ACPI_FAILURE(status)) {
+                       device_printf(dev, "failed to get handle for %s\n",
+                           constraint->name);
+                       constraint->handle = NULL;
+               }
+       }
+       return (0);
+}
+
+static void
+acpi_spmc_check_constraints(struct acpi_spmc_softc *sc)
+{
+       bool violation = false;
+
+       KASSERT(sc->constraints_populated, ("constraints not populated"));
+       for (size_t i = 0; i < sc->constraint_count; i++) {
+               struct acpi_spmc_constraint *constraint = &sc->constraints[i];
+
+               if (!constraint->enabled)
+                       continue;
+               if (constraint->handle == NULL)
+                       continue;
+
+               ACPI_STATUS status = acpi_GetHandleInScope(sc->handle,
+                   __DECONST(char *, constraint->name), &constraint->handle);
+               if (ACPI_FAILURE(status)) {
+                       device_printf(sc->dev, "failed to get handle for %s\n",
+                           constraint->name);
+                       constraint->handle = NULL;
+               }
+               if (constraint->handle == NULL)
+                       continue;
+
+#ifdef notyet
+               int d_state;
+               if (ACPI_FAILURE(acpi_pwr_get_state(constraint->handle, 
&d_state)))
+                       continue;
+               if (d_state < constraint->min_d_state) {
+                       device_printf(sc->dev, "constraint for device %s"
+                           " violated (minimum D-state required was %s, actual"
+                           " D-state is %s), might fail to enter LPI state\n",
+                           constraint->name,
+                           acpi_d_state_to_str(constraint->min_d_state),
+                           acpi_d_state_to_str(d_state));
+                       violation = true;
+               }
+#endif
+       }
+       if (!violation)
+               device_printf(sc->dev,
+                   "all device power constraints respected!\n");
+}
+
+static void
+acpi_spmc_run_dsm(device_t dev, struct dsm_set *dsm_set, int index)
+{
+       struct acpi_spmc_softc  *sc;
+       ACPI_STATUS             status;
+       ACPI_BUFFER             result;
+
+       sc = device_get_softc(dev);
+
+       status = acpi_EvaluateDSMTyped(sc->handle, (uint8_t *)&dsm_set->uuid,
+           dsm_set->revision, index, NULL, &result, ACPI_TYPE_ANY);
+
+       if (ACPI_FAILURE(status)) {
+               device_printf(dev, "%s failed to call %s DSM %d (rev %d)\n",
+                   __func__, dsm_set->name, index, dsm_set->revision);
+               return;
+       }
+
+       AcpiOsFree(result.Pointer);
+}
+
+/*
+ * Try running the DSMs from all the DSM sets we have, as them failing costs us
+ * nothing, and it seems like on AMD platforms, both the AMD entry and 
Microsoft
+ * "modern" DSM's are required for it to enter modern standby.
+ *
+ * This is what Linux does too.
+ */
+static void
+acpi_spmc_display_off_notif(device_t dev)
+{
+       struct acpi_spmc_softc *sc = device_get_softc(dev);
+
+       if ((sc->dsm_sets & DSM_SET_INTEL) != 0)
+               acpi_spmc_run_dsm(dev, &intel_dsm_set, DSM_DISPLAY_OFF_NOTIF);
+       if ((sc->dsm_sets & DSM_SET_MS) != 0)
+               acpi_spmc_run_dsm(dev, &ms_dsm_set, DSM_DISPLAY_OFF_NOTIF);
+       if ((sc->dsm_sets & DSM_SET_AMD) != 0)
+               acpi_spmc_run_dsm(dev, &amd_dsm_set, AMD_DSM_DISPLAY_OFF_NOTIF);
+}
+
+static void
+acpi_spmc_display_on_notif(device_t dev)
+{
+       struct acpi_spmc_softc *sc = device_get_softc(dev);
+
+       if ((sc->dsm_sets & DSM_SET_INTEL) != 0)
+               acpi_spmc_run_dsm(dev, &intel_dsm_set, DSM_DISPLAY_ON_NOTIF);
+       if ((sc->dsm_sets & DSM_SET_MS) != 0)
+               acpi_spmc_run_dsm(dev, &ms_dsm_set, DSM_DISPLAY_ON_NOTIF);
+       if ((sc->dsm_sets & DSM_SET_AMD) != 0)
+               acpi_spmc_run_dsm(dev, &amd_dsm_set, AMD_DSM_DISPLAY_ON_NOTIF);
+}
+
+static void
+acpi_spmc_entry_notif(device_t dev)
+{
+       struct acpi_spmc_softc *sc = device_get_softc(dev);
+
+       acpi_spmc_check_constraints(sc);
+
+       if ((sc->dsm_sets & DSM_SET_AMD) != 0)
+               acpi_spmc_run_dsm(dev, &amd_dsm_set, AMD_DSM_ENTRY_NOTIF);
+       if ((sc->dsm_sets & DSM_SET_MS) != 0) {
+               acpi_spmc_run_dsm(dev, &ms_dsm_set, DSM_MODERN_ENTRY_NOTIF);
+               acpi_spmc_run_dsm(dev, &ms_dsm_set, DSM_ENTRY_NOTIF);
+       }
+       if ((sc->dsm_sets & DSM_SET_INTEL) != 0)
+               acpi_spmc_run_dsm(dev, &intel_dsm_set, DSM_ENTRY_NOTIF);
+}
+
+static void
+acpi_spmc_exit_notif(device_t dev)
+{
+       struct acpi_spmc_softc *sc = device_get_softc(dev);
+
+       if ((sc->dsm_sets & DSM_SET_INTEL) != 0)
+               acpi_spmc_run_dsm(dev, &intel_dsm_set, DSM_EXIT_NOTIF);
+       if ((sc->dsm_sets & DSM_SET_AMD) != 0)
+               acpi_spmc_run_dsm(dev, &amd_dsm_set, AMD_DSM_EXIT_NOTIF);
+       if ((sc->dsm_sets & DSM_SET_MS) != 0) {
+               acpi_spmc_run_dsm(dev, &ms_dsm_set, DSM_EXIT_NOTIF);
+               acpi_spmc_run_dsm(dev, &ms_dsm_set, DSM_MODERN_EXIT_NOTIF);
+       }
+}
+
+static int
+acpi_spmc_suspend(device_t dev)
+{
+       acpi_spmc_display_off_notif(dev);
+       acpi_spmc_entry_notif(dev);
+
+       return (0);
+}
+
+static int
+acpi_spmc_resume(device_t dev)
+{
+       acpi_spmc_exit_notif(dev);
+       acpi_spmc_display_on_notif(dev);
+
+       return (0);
+}
+
+static device_method_t acpi_spmc_methods[] = {
+       DEVMETHOD(device_probe,         acpi_spmc_probe),
+       DEVMETHOD(device_attach,        acpi_spmc_attach),
+       DEVMETHOD(device_detach,        acpi_spmc_detach),
+       DEVMETHOD_END
+};
+
+static driver_t acpi_spmc_driver = {
+       "acpi_spmc",
+       acpi_spmc_methods,
+       sizeof(struct acpi_spmc_softc),
+};
+
+DRIVER_MODULE_ORDERED(acpi_spmc, acpi, acpi_spmc_driver, NULL, NULL, 
SI_ORDER_ANY);
+MODULE_DEPEND(acpi_spmc, acpi, 1, 1, 1);

Reply via email to