Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 38c92fff authored by Rafael J. Wysocki's avatar Rafael J. Wysocki
Browse files

ACPI / PM: Make __acpi_bus_get_power() cover D3cold correctly



After recent changes of the ACPI device power states definitions, if
power resources are not used for the device's power management, the
state returned by __acpi_bus_get_power() cannot exceed D3hot, because
the return values of _PSC are 0 through 3.  However, if the _PR3
method is not present for the device and _PS3 returns 3, we have to
assume that the device is in D3cold, so the value returned by
__acpi_bus_get_power() in that case should be 4.

Similarly, acpi_power_get_inferred_state() should take the power
resources for the D3hot state into account in general, so that it
can return 3 if those resources are "on" or 4 (D3cold) otherwise.

Fix the the above two issues and make sure that if both _PSC and
_PR3 are present for the device, the power resources listed by _PR3
will be used to determine if the number 3 returned by _PSC is meant
to represent D3cold or D3hot.

Signed-off-by: default avatarRafael J. Wysocki <rjw@sisk.pl>
parent 63a1a765
Loading
Loading
Loading
Loading
+29 −22
Original line number Original line Diff line number Diff line
@@ -202,37 +202,44 @@ static const char *state_string(int state)


static int __acpi_bus_get_power(struct acpi_device *device, int *state)
static int __acpi_bus_get_power(struct acpi_device *device, int *state)
{
{
	int result = 0;
	int result = ACPI_STATE_UNKNOWN;
	acpi_status status = 0;
	unsigned long long psc = 0;


	if (!device || !state)
	if (!device || !state)
		return -EINVAL;
		return -EINVAL;


	*state = ACPI_STATE_UNKNOWN;
	if (!device->flags.power_manageable) {
		/* TBD: Non-recursive algorithm for walking up hierarchy. */
		*state = device->parent ?
			device->parent->power.state : ACPI_STATE_D0;
		goto out;
	}


	if (device->flags.power_manageable) {
	/*
	/*
	 * Get the device's power state either directly (via _PSC) or
	 * Get the device's power state either directly (via _PSC) or
	 * indirectly (via power resources).
	 * indirectly (via power resources).
	 */
	 */
		if (device->power.flags.power_resources) {
	if (device->power.flags.explicit_get) {
			result = acpi_power_get_inferred_state(device, state);
		unsigned long long psc;
			if (result)
		acpi_status status = acpi_evaluate_integer(device->handle,
				return result;
							   "_PSC", NULL, &psc);
		} else if (device->power.flags.explicit_get) {
			status = acpi_evaluate_integer(device->handle, "_PSC",
						       NULL, &psc);
		if (ACPI_FAILURE(status))
		if (ACPI_FAILURE(status))
			return -ENODEV;
			return -ENODEV;
			*state = (int)psc;

		result = psc;
	}
	}
	} else {
	/* The test below covers ACPI_STATE_UNKNOWN too. */
		/* TBD: Non-recursive algorithm for walking up hierarchy. */
	if (result <= ACPI_STATE_D2) {
		*state = device->parent ?
	  ; /* Do nothing. */
			device->parent->power.state : ACPI_STATE_D0;
	} else if (device->power.flags.power_resources) {
		int error = acpi_power_get_inferred_state(device, &result);
		if (error)
			return error;
	} else if (result == ACPI_STATE_D3_HOT) {
		result = ACPI_STATE_D3;
	}
	}
	*state = result;


 out:
	ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device [%s] power state is %s\n",
	ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device [%s] power state is %s\n",
			  device->pnp.bus_id, state_string(*state)));
			  device->pnp.bus_id, state_string(*state)));


+1 −1
Original line number Original line Diff line number Diff line
@@ -631,7 +631,7 @@ int acpi_power_get_inferred_state(struct acpi_device *device, int *state)
	 * We know a device's inferred power state when all the resources
	 * We know a device's inferred power state when all the resources
	 * required for a given D-state are 'on'.
	 * required for a given D-state are 'on'.
	 */
	 */
	for (i = ACPI_STATE_D0; i < ACPI_STATE_D3_HOT; i++) {
	for (i = ACPI_STATE_D0; i <= ACPI_STATE_D3_HOT; i++) {
		list = &device->power.states[i].resources;
		list = &device->power.states[i].resources;
		if (list->count < 1)
		if (list->count < 1)
			continue;
			continue;