The branch main has been updated by obiwac:

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

commit 84bbfc32a3f47bd2e32741247afe516cbeff7789
Author:     Aymeric Wibo <obi...@freebsd.org>
AuthorDate: 2025-06-14 15:29:25 +0000
Commit:     Aymeric Wibo <obi...@freebsd.org>
CommitDate: 2025-08-08 09:42:44 +0000

    acpi_powerres: D3cold support
    
    Cherry-pick commit 0b76c0a from ACPICA (actypes: Distinguish between
    D3hot/cold, and default `ACPI_STATE_D3` to D3cold).
    
    The same distinction is made between `PCI_POWERSTATE_D3_HOT` and
    `PCI_POWERSTATE_D3_COLD`, as they're defined by ACPI (and are asserted
    to be the same).
    
    D3cold is essentially the same as D3hot except the power resources are
    turned off.  Add support for D3cold to `acpi_pwr_switch_consumer`.
    
    `acpi_d_state_to_str` replaces the `printf("D%d", d_state)` pattern,
    allowing for "D3hot" and "D3cold" strings to be printed instead of just
    "D3".
    
    Reviewed by:    markj, ziaee, mckusick (mentor)
    Approved by:    markj, mckusick (mentor)
    Sponsored by:   The FreeBSD Foundation
    Differential Revision:  https://reviews.freebsd.org/D48384
---
 share/man/man9/pci.9                           |  8 +++-
 sys/compat/linuxkpi/common/include/linux/pci.h | 10 ++---
 sys/contrib/dev/acpica/include/actypes.h       |  8 ++--
 sys/dev/acpica/acpi.c                          |  9 ++--
 sys/dev/acpica/acpi_pci.c                      | 12 +++---
 sys/dev/acpica/acpi_powerres.c                 | 58 +++++++++++++++++++-------
 sys/dev/acpica/acpivar.h                       |  9 ++++
 sys/dev/pci/pci.c                              |  7 +++-
 sys/dev/pci/pcivar.h                           | 27 ++++++------
 9 files changed, 98 insertions(+), 50 deletions(-)

diff --git a/share/man/man9/pci.9 b/share/man/man9/pci.9
index 8f772e76ba99..eeb62a63a2bd 100644
--- a/share/man/man9/pci.9
+++ b/share/man/man9/pci.9
@@ -664,10 +664,14 @@ Buses in this state can cause devices to lose some 
context.
 Devices
 .Em must
 be prepared for the bus to be in this state or higher.
-.It Dv PCI_POWERSTATE_D3
+.It Dv PCI_POWERSTATE_D3_HOT
 State in which the device is off and not running.
 Device context is lost, and power from the device can
-be removed.
+be (but is not necessarily) removed.
+.It Dv PCI_POWERSTATE_D3_COLD
+Same as
+.Dv PCI_POWERSTATE_D3_HOT ,
+except power has been removed from the device.
 .It Dv PCI_POWERSTATE_UNKNOWN
 State of the device is unknown.
 .El
diff --git a/sys/compat/linuxkpi/common/include/linux/pci.h 
b/sys/compat/linuxkpi/common/include/linux/pci.h
index ba1c0d2ac99e..3fd4191b9917 100644
--- a/sys/compat/linuxkpi/common/include/linux/pci.h
+++ b/sys/compat/linuxkpi/common/include/linux/pci.h
@@ -223,11 +223,11 @@ enum pcie_link_width {
 
 typedef int pci_power_t;
 
-#define PCI_D0 PCI_POWERSTATE_D0
-#define PCI_D1 PCI_POWERSTATE_D1
-#define PCI_D2 PCI_POWERSTATE_D2
-#define PCI_D3hot      PCI_POWERSTATE_D3
-#define PCI_D3cold     4
+#define PCI_D0         PCI_POWERSTATE_D0
+#define PCI_D1         PCI_POWERSTATE_D1
+#define PCI_D2         PCI_POWERSTATE_D2
+#define PCI_D3hot      PCI_POWERSTATE_D3_HOT
+#define PCI_D3cold     PCI_POWERSTATE_D3_COLD
 
 #define PCI_POWER_ERROR        PCI_POWERSTATE_UNKNOWN
 
diff --git a/sys/contrib/dev/acpica/include/actypes.h 
b/sys/contrib/dev/acpica/include/actypes.h
index 66333243907b..3c95887b1678 100644
--- a/sys/contrib/dev/acpica/include/actypes.h
+++ b/sys/contrib/dev/acpica/include/actypes.h
@@ -741,9 +741,11 @@ typedef UINT64                          ACPI_INTEGER;
 #define ACPI_STATE_D0                   (UINT8) 0
 #define ACPI_STATE_D1                   (UINT8) 1
 #define ACPI_STATE_D2                   (UINT8) 2
-#define ACPI_STATE_D3                   (UINT8) 3
-#define ACPI_D_STATES_MAX               ACPI_STATE_D3
-#define ACPI_D_STATE_COUNT              4
+#define ACPI_STATE_D3_HOT               (UINT8) 3
+#define ACPI_STATE_D3_COLD              (UINT8) 4
+#define ACPI_STATE_D3                   ACPI_STATE_D3_COLD
+#define ACPI_D_STATES_MAX               ACPI_STATE_D3_COLD
+#define ACPI_D_STATE_COUNT              5
 
 #define ACPI_STATE_C0                   (UINT8) 0
 #define ACPI_STATE_C1                   (UINT8) 1
diff --git a/sys/dev/acpica/acpi.c b/sys/dev/acpica/acpi.c
index f2ff1d59ccc7..a2159b12876f 100644
--- a/sys/dev/acpica/acpi.c
+++ b/sys/dev/acpica/acpi.c
@@ -2139,12 +2139,13 @@ acpi_set_powerstate(device_t child, int state)
     status = acpi_pwr_switch_consumer(h, state);
     if (ACPI_SUCCESS(status)) {
        if (bootverbose)
-           device_printf(child, "set ACPI power state D%d on %s\n",
-               state, acpi_name(h));
+           device_printf(child, "set ACPI power state %s on %s\n",
+               acpi_d_state_to_str(state), acpi_name(h));
     } else if (status != AE_NOT_FOUND)
        device_printf(child,
-           "failed to set ACPI power state D%d on %s: %s\n", state,
-           acpi_name(h), AcpiFormatException(status));
+           "failed to set ACPI power state %s on %s: %s\n",
+           acpi_d_state_to_str(state), acpi_name(h),
+           AcpiFormatException(status));
 
     return (0);
 }
diff --git a/sys/dev/acpica/acpi_pci.c b/sys/dev/acpica/acpi_pci.c
index 646295f9eecc..1912350bbc48 100644
--- a/sys/dev/acpica/acpi_pci.c
+++ b/sys/dev/acpica/acpi_pci.c
@@ -53,9 +53,6 @@
 
 #include <dev/iommu/iommu.h>
 
-#include "pcib_if.h"
-#include "pci_if.h"
-
 /* Hooks for the ACPI CA debugging infrastructure. */
 #define _COMPONENT     ACPI_BUS
 ACPI_MODULE_NAME("PCI")
@@ -266,12 +263,13 @@ acpi_pci_set_powerstate_method(device_t dev, device_t 
child, int state)
        status = acpi_pwr_switch_consumer(h, state);
        if (ACPI_SUCCESS(status)) {
                if (bootverbose)
-                       device_printf(dev, "set ACPI power state D%d on %s\n",
-                           state, acpi_name(h));
+                       device_printf(dev, "set ACPI power state %s on %s\n",
+                           acpi_d_state_to_str(state), acpi_name(h));
        } else if (status != AE_NOT_FOUND)
                device_printf(dev,
-                   "failed to set ACPI power state D%d on %s: %s\n",
-                   state, acpi_name(h), AcpiFormatException(status));
+                   "failed to set ACPI power state %s on %s: %s\n",
+                   acpi_d_state_to_str(state), acpi_name(h),
+                   AcpiFormatException(status));
        if (old_state > state && pci_do_power_resume)
                error = pci_set_powerstate_method(dev, child, state);
 
diff --git a/sys/dev/acpica/acpi_powerres.c b/sys/dev/acpica/acpi_powerres.c
index 0f2a25b1d02b..29d1690f1bdd 100644
--- a/sys/dev/acpica/acpi_powerres.c
+++ b/sys/dev/acpica/acpi_powerres.c
@@ -299,7 +299,7 @@ acpi_pwr_switch_consumer(ACPI_HANDLE consumer, int state)
     ACPI_BUFFER                        reslist_buffer;
     ACPI_OBJECT                        *reslist_object;
     ACPI_STATUS                        status;
-    char                       *method_name, *reslist_name;
+    char                       *method_name, *reslist_name = NULL;
 
     ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
 
@@ -318,9 +318,26 @@ acpi_pwr_switch_consumer(ACPI_HANDLE consumer, int state)
            panic("acpi added power consumer but can't find it");
     }
 
-    /* Check for valid transitions.  We can only go to D0 from D3. */
+    /* Stop here if we're already at the target D-state. */
+    if (pc->ac_state == state) {
+       status = AE_OK;
+       goto out;
+    }
+
+    /*
+     * Check for valid transitions.  From D3hot or D3cold, we can only go to 
D0.
+     * The exception to this is going from D3hot to D3cold or the other way
+     * around.  This is because they both use _PS3, so the only difference when
+     * doing these transitions is whether or not the power resources for _PR3
+     * are on for devices which support D3cold, and turning these power
+     * resources on/off is always perfectly fine (ACPI 7.3.11).
+     */
     status = AE_BAD_PARAMETER;
-    if (pc->ac_state == ACPI_STATE_D3 && state != ACPI_STATE_D0)
+    if (pc->ac_state == ACPI_STATE_D3_HOT && state != ACPI_STATE_D0 &&
+       state != ACPI_STATE_D3_COLD)
+       goto out;
+    if (pc->ac_state == ACPI_STATE_D3_COLD && state != ACPI_STATE_D0 &&
+       state != ACPI_STATE_D3_HOT)
        goto out;
 
     /* Find transition mechanism(s) */
@@ -337,15 +354,20 @@ acpi_pwr_switch_consumer(ACPI_HANDLE consumer, int state)
        method_name = "_PS2";
        reslist_name = "_PR2";
        break;
-    case ACPI_STATE_D3:
+    case ACPI_STATE_D3_HOT:
        method_name = "_PS3";
        reslist_name = "_PR3";
        break;
+    case ACPI_STATE_D3_COLD:
+       method_name = "_PS3";
+       reslist_name = NULL;
+       break;
     default:
        goto out;
     }
-    ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "setup to switch %s D%d -> D%d\n",
-                    acpi_name(consumer), pc->ac_state, state));
+    ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "setup to switch %s %s -> %s\n",
+                    acpi_name(consumer), acpi_d_state_to_str(pc->ac_state),
+                    acpi_d_state_to_str(state)));
 
     /*
      * Verify that this state is supported, ie. one of method or
@@ -359,7 +381,8 @@ acpi_pwr_switch_consumer(ACPI_HANDLE consumer, int state)
      */
     if (ACPI_FAILURE(AcpiGetHandle(consumer, method_name, &method_handle)))
        method_handle = NULL;
-    if (ACPI_FAILURE(AcpiGetHandle(consumer, reslist_name, &reslist_handle)))
+    if (reslist_name == NULL ||
+       ACPI_FAILURE(AcpiGetHandle(consumer, reslist_name, &reslist_handle)))
        reslist_handle = NULL;
     if (reslist_handle == NULL && method_handle == NULL) {
        if (state == ACPI_STATE_D0) {
@@ -367,9 +390,12 @@ acpi_pwr_switch_consumer(ACPI_HANDLE consumer, int state)
            status = AE_OK;
            goto out;
        }
-       if (state != ACPI_STATE_D3) {
+       if (state == ACPI_STATE_D3_COLD)
+           state = ACPI_STATE_D3_HOT;
+       if (state != ACPI_STATE_D3_HOT) {
            ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS,
-               "attempt to set unsupported state D%d\n", state));
+               "attempt to set unsupported state %s\n",
+               acpi_d_state_to_str(state)));
            goto out;
        }
 
@@ -380,21 +406,23 @@ acpi_pwr_switch_consumer(ACPI_HANDLE consumer, int state)
        if (ACPI_FAILURE(AcpiGetHandle(consumer, "_PR0", &pr0_handle))) {
            status = AE_NOT_FOUND;
            ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS,
-               "device missing _PR0 (desired state was D%d)\n", state));
+               "device missing _PR0 (desired state was %s)\n",
+               acpi_d_state_to_str(state)));
            goto out;
        }
        reslist_buffer.Length = ACPI_ALLOCATE_BUFFER;
        status = AcpiEvaluateObject(pr0_handle, NULL, NULL, &reslist_buffer);
        if (ACPI_FAILURE(status)) {
            ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS,
-               "can't evaluate _PR0 for device %s, state D%d\n",
-               acpi_name(consumer), state));
+               "can't evaluate _PR0 for device %s, state %s\n",
+               acpi_name(consumer), acpi_d_state_to_str(state)));
            goto out;
        }
        reslist_object = (ACPI_OBJECT *)reslist_buffer.Pointer;
        if (!ACPI_PKG_VALID(reslist_object, 1)) {
            ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS,
-               "invalid package object for state D%d\n", state));
+               "invalid package object for state %s\n",
+               acpi_d_state_to_str(state)));
            status = AE_TYPE;
            goto out;
        }
@@ -450,8 +478,8 @@ acpi_pwr_switch_consumer(ACPI_HANDLE consumer, int state)
      */
     if (ACPI_FAILURE(status = acpi_pwr_switch_power())) {
        ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS,
-                        "failed to switch resources from %s to D%d\n",
-                         acpi_name(consumer), state));
+                        "failed to switch resources from %s to %s\n",
+                         acpi_name(consumer), acpi_d_state_to_str(state)));
 
        /* XXX is this appropriate?  Should we return to previous state? */
        goto out;
diff --git a/sys/dev/acpica/acpivar.h b/sys/dev/acpica/acpivar.h
index 106ec9038820..39432731cbcc 100644
--- a/sys/dev/acpica/acpivar.h
+++ b/sys/dev/acpica/acpivar.h
@@ -517,6 +517,15 @@ acpi_get_verbose(struct acpi_softc *sc)
     return (0);
 }
 
+static __inline const char *
+acpi_d_state_to_str(int state)
+{
+    const char *strs[ACPI_D_STATE_COUNT] = {"D0", "D1", "D2", "D3", "D3cold"};
+
+    MPASS(state >= ACPI_STATE_D0 && state <= ACPI_D_STATES_MAX);
+    return (strs[state]);
+}
+
 char           *acpi_name(ACPI_HANDLE handle);
 int            acpi_avoid(ACPI_HANDLE handle);
 int            acpi_disabled(char *subsys);
diff --git a/sys/dev/pci/pci.c b/sys/dev/pci/pci.c
index f94438cda041..4629165f34b2 100644
--- a/sys/dev/pci/pci.c
+++ b/sys/dev/pci/pci.c
@@ -81,6 +81,9 @@
 
 #include <dev/iommu/iommu.h>
 
+#include <contrib/dev/acpica/include/acpi.h>
+#include <dev/acpica/acpivar.h>
+
 #include "pcib_if.h"
 #include "pci_if.h"
 
@@ -2896,8 +2899,8 @@ pci_set_powerstate_method(device_t dev, device_t child, 
int state)
        }
 
        if (bootverbose)
-               pci_printf(cfg, "Transition from D%d to D%d\n", oldstate,
-                   state);
+               pci_printf(cfg, "Transition from %s to %s\n",
+                   acpi_d_state_to_str(oldstate), acpi_d_state_to_str(state));
 
        PCI_WRITE_CONFIG(dev, child, cfg->pp.pp_location + PCIR_POWER_STATUS,
            status, 2);
diff --git a/sys/dev/pci/pcivar.h b/sys/dev/pci/pcivar.h
index d1b7d28eae91..832305b9adee 100644
--- a/sys/dev/pci/pcivar.h
+++ b/sys/dev/pci/pcivar.h
@@ -497,22 +497,25 @@ pci_is_vga_memory_range(rman_res_t start, rman_res_t end)
 /*
  * PCI power states are as defined by ACPI:
  *
- * D0  State in which device is on and running.  It is receiving full
- *     power from the system and delivering full functionality to the user.
- * D1  Class-specific low-power state in which device context may or may not
- *     be lost.  Buses in D1 cannot do anything to the bus that would force
- *     devices on that bus to lose context.
- * D2  Class-specific low-power state in which device context may or may
- *     not be lost.  Attains greater power savings than D1.  Buses in D2
- *     can cause devices on that bus to lose some context.  Devices in D2
- *     must be prepared for the bus to be in D2 or higher.
- * D3  State in which the device is off and not running.  Device context is
- *     lost.  Power can be removed from the device.
+ * D0    State in which device is on and running.  It is receiving full
+ *       power from the system and delivering full functionality to the user.
+ * D1    Class-specific low-power state in which device context may or may not
+ *       be lost.  Buses in D1 cannot do anything to the bus that would force
+ *       devices on that bus to lose context.
+ * D2    Class-specific low-power state in which device context may or may
+ *       not be lost.  Attains greater power savings than D1.  Buses in D2
+ *       can cause devices on that bus to lose some context.  Devices in D2
+ *       must be prepared for the bus to be in D2 or higher.
+ * D3hot  State in which the device is off and not running.  Device context is
+ *       lost.  Power can be removed from the device.
+ * D3cold Same as D3hot, but power has been removed from the device.
  */
 #define        PCI_POWERSTATE_D0       0
 #define        PCI_POWERSTATE_D1       1
 #define        PCI_POWERSTATE_D2       2
-#define        PCI_POWERSTATE_D3       3
+#define        PCI_POWERSTATE_D3_HOT   3
+#define        PCI_POWERSTATE_D3_COLD  4
+#define        PCI_POWERSTATE_D3       PCI_POWERSTATE_D3_COLD
 #define        PCI_POWERSTATE_UNKNOWN  -1
 
 static __inline int

Reply via email to