Commit 3cc2a2b2 authored by Rafael J. Wysocki's avatar Rafael J. Wysocki Committed by Bjorn Helgaas
Browse files

PCI/PM: Rearrange pci_set_power_state()

The part of pci_set_power_state() related to transitions into
low-power states is unnecessary convoluted, so clearly divide it
into the D3cold special case and the general case covering all of
the other states.

Also fix a potential issue with calling pci_bus_set_current_state()
to set the current state of all devices on the subordinate bus to
D3cold without checking if the power state of the parent bridge has
really changed to D3cold.

Link: https://lore.kernel.org/r/2139440.Mh6RI2rZIc@kreacher


Signed-off-by: default avatarRafael J. Wysocki <rafael.j.wysocki@intel.com>
Signed-off-by: default avatarBjorn Helgaas <bhelgaas@google.com>
Reviewed-by: default avatarMika Westerberg <mika.westerberg@linux.intel.com>
parent 0aacdc95
Loading
Loading
Loading
Loading
+17 −11
Original line number Diff line number Diff line
@@ -1446,19 +1446,25 @@ int pci_set_power_state(struct pci_dev *dev, pci_power_t state)
	if (state >= PCI_D3hot && (dev->dev_flags & PCI_DEV_FLAGS_NO_D3))
		return 0;

	if (state == PCI_D3cold) {
		/*
	 * To put device in D3cold, we put device into D3hot in native
	 * way, then put device into D3cold with platform ops
		 * To put the device in D3cold, put it into D3hot in the native
		 * way, then put it into D3cold using platform ops.
		 */
	error = pci_set_low_power_state(dev, state > PCI_D3hot ?
					PCI_D3hot : state);
		error = pci_set_low_power_state(dev, PCI_D3hot);

	if (pci_platform_power_transition(dev, state))
		if (pci_platform_power_transition(dev, PCI_D3cold))
			return error;

		/* Powering off a bridge may power off the whole hierarchy */
	if (state == PCI_D3cold)
		if (dev->current_state == PCI_D3cold)
			pci_bus_set_current_state(dev->subordinate, PCI_D3cold);
	} else {
		error = pci_set_low_power_state(dev, state);

		if (pci_platform_power_transition(dev, state))
			return error;
	}

	return 0;
}