Page MenuHomeFreeBSD

D55118.id171353.diff
No OneTemporary

D55118.id171353.diff

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

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)

Event Timeline