[PATCH] PCI/portdrv: Disallow runtime suspend when waekup is required but PME service isn't supported

From: Kai-Heng Feng
Date: Mon Aug 09 2021 - 00:24:29 EST


Some platforms cannot detect ethernet hotplug once its upstream port is
runtime suspended because PME isn't enabled in _OSC. The issue can be
workarounded by "pcie_ports=native".

The vendor confirmed that the PME in _OSC is disabled intentionally for
stability issues on the other OS, so we should also honor the PME
setting here.

Disallow port runtime suspend when any child device requires wakeup, so
pci_pme_list_scan() can still read the PME status from the devices
behind the port.

Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=213873
Signed-off-by: Kai-Heng Feng <kai.heng.feng@xxxxxxxxxxxxx>
---
drivers/pci/pcie/portdrv_pci.c | 18 +++++++++++++++++-
1 file changed, 17 insertions(+), 1 deletion(-)

diff --git a/drivers/pci/pcie/portdrv_pci.c b/drivers/pci/pcie/portdrv_pci.c
index c7ff1eea225a..e693d243c90d 100644
--- a/drivers/pci/pcie/portdrv_pci.c
+++ b/drivers/pci/pcie/portdrv_pci.c
@@ -59,14 +59,30 @@ static int pcie_port_runtime_suspend(struct device *dev)
return pcie_port_device_runtime_suspend(dev);
}

+static int pcie_port_wakeup_check(struct device *dev, void *data)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+
+ if (!pdev)
+ return 0;
+
+ return pdev->wakeup_prepared;
+}
+
static int pcie_port_runtime_idle(struct device *dev)
{
+ struct pci_dev *pdev = to_pci_dev(dev);
+
+ if (!pcie_port_find_device(pdev, PCIE_PORT_SERVICE_PME) &&
+ device_for_each_child(dev, NULL, pcie_port_wakeup_check))
+ return -EBUSY;
+
/*
* Assume the PCI core has set bridge_d3 whenever it thinks the port
* should be good to go to D3. Everything else, including moving
* the port to D3, is handled by the PCI core.
*/
- return to_pci_dev(dev)->bridge_d3 ? 0 : -EBUSY;
+ return pdev->bridge_d3 ? 0 : -EBUSY;
}

static const struct dev_pm_ops pcie_portdrv_pm_ops = {
--
2.31.1