As drivers can report an optin or veto for a given PCI device it's possible that pci_bridge_d3_possible() reports different values while calling pci_bridge_d3_update(). Take these values into account while updating the ability for a bridge to go into D3. Signed-off-by: Mario Limonciello <mario.limonciello@xxxxxxx> --- drivers/pci/pci.c | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 06ab73f58adf..9004d8ea460a 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -3100,6 +3100,14 @@ static int pci_dev_check_d3cold(struct pci_dev *dev, void *data) return !*d3cold_ok; } +static void pci_bridge_d3_propagate(struct pci_dev *bridge, bool d3_ok) +{ + if (bridge->bridge_d3 != d3_ok) { + bridge->bridge_d3 = d3_ok; + pci_bridge_d3_propagate(bridge, d3_ok); + } +} + /* * pci_bridge_d3_update - Update bridge D3 capabilities * @dev: PCI device which is changed @@ -3112,12 +3120,16 @@ void pci_bridge_d3_update(struct pci_dev *dev) { bool remove = !device_is_registered(&dev->dev); struct pci_dev *bridge; - bool d3cold_ok = true; + bool d3_ok = true; bridge = pci_upstream_bridge(dev); - if (!bridge || !pci_bridge_d3_possible(bridge)) + if (!bridge) return; + /* Propagate change to upstream bridges */ + d3_ok = pci_bridge_d3_possible(bridge); + pci_bridge_d3_propagate(bridge, d3_ok); + /* * If D3 is currently allowed for the bridge, removing one of its * children won't change that. @@ -3134,7 +3146,7 @@ void pci_bridge_d3_update(struct pci_dev *dev) * first may allow us to skip checking its siblings. */ if (!remove) - pci_dev_check_d3cold(dev, &d3cold_ok); + pci_dev_check_d3cold(dev, &d3_ok); /* * If D3 is currently not allowed for the bridge, this may be caused @@ -3142,15 +3154,12 @@ void pci_bridge_d3_update(struct pci_dev *dev) * so we need to go through all children to find out if one of them * continues to block D3. */ - if (d3cold_ok && !bridge->bridge_d3) + if (d3_ok && !bridge->bridge_d3) pci_walk_bus(bridge->subordinate, pci_dev_check_d3cold, - &d3cold_ok); + &d3_ok); - if (bridge->bridge_d3 != d3cold_ok) { - bridge->bridge_d3 = d3cold_ok; - /* Propagate change to upstream bridges */ - pci_bridge_d3_update(bridge); - } + /* Propagate change to upstream bridges */ + pci_bridge_d3_propagate(bridge, d3_ok); } /** -- 2.34.1