Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F148634269
D55118.id171353.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
6 KB
Referenced Files
None
Subscribers
None
D55118.id171353.diff
View Options
diff --git a/sys/conf/files b/sys/conf/files
--- a/sys/conf/files
+++ b/sys/conf/files
@@ -777,6 +777,7 @@
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/acpica/acpi_spmc_if.m 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
--- a/sys/dev/acpica/acpi.c
+++ b/sys/dev/acpica/acpi.c
@@ -82,6 +82,8 @@
#include <vm/vm_param.h>
+#include "acpi_spmc_if.h"
+
static MALLOC_DEFINE(M_ACPIDEV, "acpidev", "ACPI devices");
/* Hooks for the ACPI CA debugging infrastructure */
@@ -2130,44 +2132,93 @@
}
}
+static int
+acpi_device_pwr_for_sleep_spmc(ACPI_HANDLE handle, int *dstate_out)
+{
+ devclass_t dc = devclass_find("acpi_spmc");
+ device_t spmc;
+ int dstate;
+
+ if (dc == NULL)
+ return (-1);
+ spmc = devclass_get_device(dc, 0);
+ if (spmc == NULL)
+ return (-1);
+
+ dstate = ACPI_SPMC_GET_MIN_D_STATE(spmc, handle);
+ if (dstate < 0)
+ return (-1);
+ *dstate_out = dstate;
+ return (0);
+}
+
+static int
+acpi_device_pwr_for_sleep_sxd(device_t dev, ACPI_HANDLE handle, int state,
+ int *dstate)
+{
+ ACPI_STATUS status;
+ char sxd[8];
+
+ /*
+ * TODO Move this comment, because it is confusing.
+ *
+ * Override next state with the value from _SxD, if present.
+ * Note illegal _S0D is evaluated because some systems expect this.
+ */
+ snprintf(sxd, sizeof(sxd), "_S%dD", state);
+ status = acpi_GetInteger(handle, sxd, dstate);
+ if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) {
+ device_printf(dev, "failed to get %s on %s: %s\n", sxd,
+ acpi_name(handle), AcpiFormatException(status));
+ return (ENXIO);
+ }
+ return (0);
+}
+
+/*
+ * Get the D-state we need to set the device to for entry into the sleep type
+ * we are currently entering (sc->acpi_stype is set in acpi_EnterSleepState
+ * before the ACPI bus gets suspended, and thus before this function is called).
+ *
+ * If entering s2idle, we will try to get the D-state from the SPMC's reported
+ * device constraints, but if that fails or we are entering an ACPI S-state, we
+ * evaluate the relevant _SxD state instead (ACPI 7.3.16 - 7.3.19).
+ */
int
acpi_device_pwr_for_sleep(device_t bus, device_t dev, int *dstate)
{
- struct acpi_softc *sc;
- ACPI_HANDLE handle;
- ACPI_STATUS status;
- char sxd[8];
-
- handle = acpi_get_handle(dev);
+ struct acpi_softc *sc = device_get_softc(bus);
+ ACPI_HANDLE handle = acpi_get_handle(dev);
+ int state;
- /*
- * XXX If we find these devices, don't try to power them down.
- * The serial and IRDA ports on my T23 hang the system when
- * set to D3 and it appears that such legacy devices may
- * need special handling in their drivers.
- */
- if (dstate == NULL || handle == NULL ||
- acpi_MatchHid(handle, "PNP0500") ||
- acpi_MatchHid(handle, "PNP0501") ||
- acpi_MatchHid(handle, "PNP0502") ||
- acpi_MatchHid(handle, "PNP0510") ||
- acpi_MatchHid(handle, "PNP0511"))
- return (ENXIO);
+ if (dstate == NULL)
+ return (EINVAL);
- /*
- * Override next state with the value from _SxD, if present.
- * Note illegal _S0D is evaluated because some systems expect this.
- */
- sc = device_get_softc(bus);
- snprintf(sxd, sizeof(sxd), "_S%dD", acpi_stype_to_sstate(sc, sc->acpi_stype));
- status = acpi_GetInteger(handle, sxd, dstate);
- if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) {
- device_printf(dev, "failed to get %s on %s: %s\n", sxd,
- acpi_name(handle), AcpiFormatException(status));
- return (ENXIO);
- }
+ /*
+ * XXX If we find these devices, don't try to power them down.
+ * The serial and IRDA ports on my T23 hang the system when
+ * set to D3 and it appears that such legacy devices may
+ * need special handling in their drivers.
+ */
+ if (handle == NULL ||
+ acpi_MatchHid(handle, "PNP0500") ||
+ acpi_MatchHid(handle, "PNP0501") ||
+ acpi_MatchHid(handle, "PNP0502") ||
+ acpi_MatchHid(handle, "PNP0510") ||
+ acpi_MatchHid(handle, "PNP0511"))
+ return (ENXIO);
- return (0);
+ if (sc->acpi_stype == POWER_STYPE_SUSPEND_TO_IDLE) {
+ if (acpi_device_pwr_for_sleep_spmc(handle, dstate) == 0)
+ return (0);
+ /*
+ * If we couldn't get the D-state from the SPMC, the next best
+ * thing we can do is evaluate the _S3D object.
+ */
+ state = ACPI_STATE_S3;
+ } else
+ state = acpi_stype_to_sstate(sc, sc->acpi_stype);
+ return (acpi_device_pwr_for_sleep_sxd(bus, dev, state, dstate));
}
/* Callback arg for our implementation of walking the namespace. */
diff --git a/sys/dev/acpica/acpi_spmc.c b/sys/dev/acpica/acpi_spmc.c
--- a/sys/dev/acpica/acpi_spmc.c
+++ b/sys/dev/acpica/acpi_spmc.c
@@ -22,6 +22,8 @@
#include <dev/acpica/acpivar.h>
+#include "acpi_spmc_if.h"
+
/* Hooks for the ACPI CA debugging infrastructure */
#define _COMPONENT ACPI_SPMC
ACPI_MODULE_NAME("SPMC")
@@ -621,10 +623,28 @@
acpi_spmc_display_on_notif(dev);
}
+static int
+get_min_d_state(device_t dev, ACPI_HANDLE handle)
+{
+ struct acpi_spmc_softc *sc = device_get_softc(dev);
+
+ if (!sc->constraints_populated)
+ return (-1);
+
+ for (size_t i = 0; i < sc->constraint_count; i++) {
+ struct acpi_spmc_constraint *constraint = &sc->constraints[i];
+ if (constraint->handle == handle)
+ return (constraint->min_d_state);
+ }
+ return (-1);
+}
+
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(acpi_spmc_get_min_d_state, get_min_d_state),
DEVMETHOD_END
};
diff --git a/sys/dev/acpica/acpi_spmc_if.m b/sys/dev/acpica/acpi_spmc_if.m
new file mode 100644
--- /dev/null
+++ b/sys/dev/acpica/acpi_spmc_if.m
@@ -0,0 +1,39 @@
+#-
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2026 The FreeBSD Foundation
+#
+# This software was developed by Aymeric Wibo <obiwac@freebsd.org>
+# under sponsorship from the FreeBSD Foundation.
+#
+
+#include <sys/types.h>
+
+#include <contrib/dev/acpica/include/acpi.h>
+
+/**
+ * @defgroup ACPI_SPMC acpi_spmc - KObj methods for the acpi_spmc driver
+ * @brief Get D-state device constraint method implemented by acpi SPMC.
+ *
+ * The SPMC reports the minimum D-state constraint each device has to be put in
+ * during s2idle to enter a S0ix state. This can then be used by the ACPI code
+ * (e.g. in acpi_device_pwr_for_sleep_spmc) to know what the minimum D-state it
+ * needs to put that device in is.
+ * @{
+ */
+INTERFACE acpi_spmc;
+
+/**
+ * @brief Get minimum D-state constraint for a device.
+ *
+ * @param spmc_dev the SPMC device to ask for constraint
+ * @param dev the device to ask the constraint of
+ *
+ * @retval 0 or above minimum D-state constraint
+ * @retval negative constraints not populated or no constraint
+ * @see DEVICE_PROBE()
+ */
+METHOD int get_min_d_state {
+ device_t spmc_dev;
+ ACPI_HANDLE dev;
+};
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Fri, Mar 20, 6:49 AM (17 h, 47 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
30005121
Default Alt Text
D55118.id171353.diff (6 KB)
Attached To
Mode
D55118: acpi: Ask SPMC for D-state in s2idle instead of _S255D
Attached
Detach File
Event Timeline
Log In to Comment