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
This commit is contained in:
Aymeric Wibo 2025-06-14 17:29:25 +02:00
parent 7988e7e167
commit 84bbfc32a3
No known key found for this signature in database
GPG key ID: 4CC540EC0B39382D
9 changed files with 98 additions and 50 deletions

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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);
}

View file

@ -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);

View file

@ -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;

View file

@ -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);

View file

@ -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);

View file

@ -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