/*
* The power resources settings may indicate a power state
- * shallower than the actual power state of the device.
+ * shallower than the actual power state of the device, because
+ * the same power resources may be referenced by other devices.
*
- * Moreover, on systems predating ACPI 4.0, if the device
- * doesn't depend on any power resources and _PSC returns 3,
- * that means "power off". We need to maintain compatibility
- * with those systems.
+ * For systems predating ACPI 4.0 we assume that D3hot is the
+ * deepest state that can be supported.
*/
if (psc > result && psc < ACPI_STATE_D3_COLD)
result = psc;
else if (result == ACPI_STATE_UNKNOWN)
- result = psc > ACPI_STATE_D2 ? ACPI_STATE_D3_COLD : psc;
+ result = psc > ACPI_STATE_D2 ? ACPI_STATE_D3_HOT : psc;
}
/*
*/
int acpi_device_set_power(struct acpi_device *device, int state)
{
+ int target_state = state;
int result = 0;
- bool cut_power = false;
if (!device || !device->flags.power_manageable
|| (state < ACPI_STATE_D0) || (state > ACPI_STATE_D3_COLD))
return 0;
}
- if (!device->power.states[state].flags.valid) {
+ if (state == ACPI_STATE_D3_COLD) {
+ /*
+ * For transitions to D3cold we need to execute _PS3 and then
+ * possibly drop references to the power resources in use.
+ */
+ state = ACPI_STATE_D3_HOT;
+ /* If _PR3 is not available, use D3hot as the target state. */
+ if (!device->power.states[ACPI_STATE_D3_COLD].flags.valid)
+ target_state = state;
+ } else if (!device->power.states[state].flags.valid) {
dev_warn(&device->dev, "Power state %s not supported\n",
acpi_power_state_string(state));
return -ENODEV;
}
+
if (!device->power.flags.ignore_parent &&
device->parent && (state < device->parent->power.state)) {
dev_warn(&device->dev,
return -ENODEV;
}
- /* For D3cold we should first transition into D3hot. */
- if (state == ACPI_STATE_D3_COLD
- && device->power.states[ACPI_STATE_D3_COLD].flags.os_accessible) {
- state = ACPI_STATE_D3_HOT;
- cut_power = true;
- }
-
- if (state < device->power.state && state != ACPI_STATE_D0
- && device->power.state >= ACPI_STATE_D3_HOT) {
- dev_warn(&device->dev,
- "Cannot transition to non-D0 state from D3\n");
- return -ENODEV;
- }
-
/*
* Transition Power
* ----------------
- * In accordance with the ACPI specification first apply power (via
- * power resources) and then evaluate _PSx.
+ * In accordance with ACPI 6, _PSx is executed before manipulating power
+ * resources, unless the target state is D0, in which case _PS0 is
+ * supposed to be executed after turning the power resources on.
*/
- if (device->power.flags.power_resources) {
- result = acpi_power_transition(device, state);
+ if (state > ACPI_STATE_D0) {
+ /*
+ * According to ACPI 6, devices cannot go from lower-power
+ * (deeper) states to higher-power (shallower) states.
+ */
+ if (state < device->power.state) {
+ dev_warn(&device->dev, "Cannot transition from %s to %s\n",
+ acpi_power_state_string(device->power.state),
+ acpi_power_state_string(state));
+ return -ENODEV;
+ }
+
+ result = acpi_dev_pm_explicit_set(device, state);
if (result)
goto end;
- }
- result = acpi_dev_pm_explicit_set(device, state);
- if (result)
- goto end;
- if (cut_power) {
- device->power.state = state;
- state = ACPI_STATE_D3_COLD;
- result = acpi_power_transition(device, state);
+ if (device->power.flags.power_resources)
+ result = acpi_power_transition(device, target_state);
+ } else {
+ if (device->power.flags.power_resources) {
+ result = acpi_power_transition(device, ACPI_STATE_D0);
+ if (result)
+ goto end;
+ }
+ result = acpi_dev_pm_explicit_set(device, ACPI_STATE_D0);
}
end:
return result;
if (state < ACPI_STATE_D3_COLD && device->power.flags.power_resources) {
+ /* Reference count the power resources. */
result = acpi_power_on_resources(device, state);
if (result)
return result;
- result = acpi_dev_pm_explicit_set(device, state);
- if (result)
- return result;
+ if (state == ACPI_STATE_D0) {
+ /*
+ * If _PSC is not present and the state inferred from
+ * power resources appears to be D0, it still may be
+ * necessary to execute _PS0 at this point, because
+ * another device using the same power resources may
+ * have been put into D0 previously and that's why we
+ * see D0 here.
+ */
+ result = acpi_dev_pm_explicit_set(device, state);
+ if (result)
+ return result;
+ }
} else if (state == ACPI_STATE_UNKNOWN) {
/*
* No power resources and missing _PSC? Cross fingers and make
if (d_max_in < ACPI_STATE_D0 || d_max_in > ACPI_STATE_D3_COLD)
return -EINVAL;
- if (d_max_in > ACPI_STATE_D3_HOT) {
+ if (d_max_in > ACPI_STATE_D2) {
enum pm_qos_flags_status stat;
stat = dev_pm_qos_flags(dev, PM_QOS_FLAG_NO_POWER_OFF);
if (stat == PM_QOS_FLAGS_ALL)
- d_max_in = ACPI_STATE_D3_HOT;
+ d_max_in = ACPI_STATE_D2;
}
adev = ACPI_COMPANION(dev);
if (acpi_has_method(device->handle, pathname))
ps->flags.explicit_set = 1;
- /*
- * State is valid if there are means to put the device into it.
- * D3hot is only valid if _PR3 present.
- */
- if (!list_empty(&ps->resources)
- || (ps->flags.explicit_set && state < ACPI_STATE_D3_HOT)) {
+ /* State is valid if there are means to put the device into it. */
+ if (!list_empty(&ps->resources) || ps->flags.explicit_set)
ps->flags.valid = 1;
- ps->flags.os_accessible = 1;
- }
ps->power = -1; /* Unknown - driver assigned */
ps->latency = -1; /* Unknown - driver assigned */
acpi_bus_init_power_state(device, i);
INIT_LIST_HEAD(&device->power.states[ACPI_STATE_D3_COLD].resources);
+ if (!list_empty(&device->power.states[ACPI_STATE_D3_HOT].resources))
+ device->power.states[ACPI_STATE_D3_COLD].flags.valid = 1;
- /* Set defaults for D0 and D3 states (always valid) */
+ /* Set defaults for D0 and D3hot states (always valid) */
device->power.states[ACPI_STATE_D0].flags.valid = 1;
device->power.states[ACPI_STATE_D0].power = 100;
- device->power.states[ACPI_STATE_D3_COLD].flags.valid = 1;
- device->power.states[ACPI_STATE_D3_COLD].power = 0;
-
- /* Set D3cold's explicit_set flag if _PS3 exists. */
- if (device->power.states[ACPI_STATE_D3_HOT].flags.explicit_set)
- device->power.states[ACPI_STATE_D3_COLD].flags.explicit_set = 1;
-
- /* Presence of _PS3 or _PRx means we can put the device into D3 cold */
- if (device->power.states[ACPI_STATE_D3_HOT].flags.explicit_set ||
- device->power.flags.power_resources)
- device->power.states[ACPI_STATE_D3_COLD].flags.os_accessible = 1;
+ device->power.states[ACPI_STATE_D3_HOT].flags.valid = 1;
if (acpi_bus_init_power(device))
device->flags.power_manageable = 0;