Commit 08a67024 authored by Bjorn Helgaas's avatar Bjorn Helgaas
Browse files

Merge branch 'pci/pm'

- Account for _S0W when deciding whether to put bridges in D3 to avoid
  missing hotplug events (Rafael J. Wysocki)

* pci/pm:
  PCI/ACPI: Account for _S0W of the target bridge in acpi_pci_bridge_d3()
parents 7260675a 8133844a
Loading
Loading
Loading
Loading
+19 −0
Original line number Diff line number Diff line
@@ -484,6 +484,25 @@ void acpi_dev_power_up_children_with_adr(struct acpi_device *adev)
	acpi_dev_for_each_child(adev, acpi_power_up_if_adr_present, NULL);
}

/**
 * acpi_dev_power_state_for_wake - Deepest power state for wakeup signaling
 * @adev: ACPI companion of the target device.
 *
 * Evaluate _S0W for @adev and return the value produced by it or return
 * ACPI_STATE_UNKNOWN on errors (including _S0W not present).
 */
u8 acpi_dev_power_state_for_wake(struct acpi_device *adev)
{
	unsigned long long state;
	acpi_status status;

	status = acpi_evaluate_integer(adev->handle, "_S0W", NULL, &state);
	if (ACPI_FAILURE(status))
		return ACPI_STATE_UNKNOWN;

	return state;
}

#ifdef CONFIG_PM
static DEFINE_MUTEX(acpi_pm_notifier_lock);
static DEFINE_MUTEX(acpi_pm_notifier_install_lock);
+31 −14
Original line number Diff line number Diff line
@@ -976,24 +976,41 @@ bool acpi_pci_power_manageable(struct pci_dev *dev)
bool acpi_pci_bridge_d3(struct pci_dev *dev)
{
	struct pci_dev *rpdev;
	struct acpi_device *adev;
	acpi_status status;
	unsigned long long state;
	struct acpi_device *adev, *rpadev;
	const union acpi_object *obj;

	if (acpi_pci_disabled || !dev->is_hotplug_bridge)
		return false;

	/* Assume D3 support if the bridge is power-manageable by ACPI. */
	if (acpi_pci_power_manageable(dev))
	adev = ACPI_COMPANION(&dev->dev);
	if (adev) {
		/*
		 * If the bridge has _S0W, whether or not it can go into D3
		 * depends on what is returned by that object.  In particular,
		 * if the power state returned by _S0W is D2 or shallower,
		 * entering D3 should not be allowed.
		 */
		if (acpi_dev_power_state_for_wake(adev) <= ACPI_STATE_D2)
			return false;

		/*
		 * Otherwise, assume that the bridge can enter D3 so long as it
		 * is power-manageable via ACPI.
		 */
		if (acpi_device_power_manageable(adev))
			return true;
	}

	rpdev = pcie_find_root_port(dev);
	if (!rpdev)
		return false;

	adev = ACPI_COMPANION(&rpdev->dev);
	if (!adev)
	if (rpdev == dev)
		rpadev = adev;
	else
		rpadev = ACPI_COMPANION(&rpdev->dev);

	if (!rpadev)
		return false;

	/*
@@ -1001,15 +1018,15 @@ bool acpi_pci_bridge_d3(struct pci_dev *dev)
	 * doesn't supply a wakeup GPE via _PRW, it cannot signal hotplug
	 * events from low-power states including D3hot and D3cold.
	 */
	if (!adev->wakeup.flags.valid)
	if (!rpadev->wakeup.flags.valid)
		return false;

	/*
	 * If the Root Port cannot wake itself from D3hot or D3cold, we
	 * can't use D3.
	 * In the bridge-below-a-Root-Port case, evaluate _S0W for the Root Port
	 * to verify whether or not it can signal wakeup from D3.
	 */
	status = acpi_evaluate_integer(adev->handle, "_S0W", NULL, &state);
	if (ACPI_SUCCESS(status) && state < ACPI_STATE_D3_HOT)
	if (rpadev != adev &&
	    acpi_dev_power_state_for_wake(rpadev) <= ACPI_STATE_D2)
		return false;

	/*
@@ -1018,7 +1035,7 @@ bool acpi_pci_bridge_d3(struct pci_dev *dev)
	 * bridges *below* that Root Port can also signal hotplug events
	 * while in D3.
	 */
	if (!acpi_dev_get_property(adev, "HotPlugSupportInD3",
	if (!acpi_dev_get_property(rpadev, "HotPlugSupportInD3",
				   ACPI_TYPE_INTEGER, &obj) &&
	    obj->integer.value == 1)
		return true;
+1 −0
Original line number Diff line number Diff line
@@ -533,6 +533,7 @@ int acpi_bus_update_power(acpi_handle handle, int *state_p);
int acpi_device_update_power(struct acpi_device *device, int *state_p);
bool acpi_bus_power_manageable(acpi_handle handle);
void acpi_dev_power_up_children_with_adr(struct acpi_device *adev);
u8 acpi_dev_power_state_for_wake(struct acpi_device *adev);
int acpi_device_power_add_dependent(struct acpi_device *adev,
				    struct device *dev);
void acpi_device_power_remove_dependent(struct acpi_device *adev,