Commit c733b774 authored by Huang Ying's avatar Huang Ying Committed by Bjorn Helgaas

PCI/PM: Do not suspend port if any subordinate device needs PME polling

Ulrich reported that his USB3 cardreader does not work reliably when
connected to the USB3 port.  It turns out that USB3 controller failed to
awaken when plugging in the USB3 cardreader.  Further experiments found
that the USB3 host controller can only be awakened via polling, not via PME
interrupt.  But if the PCIe port to which the USB3 host controller is
connected is suspended, we cannot poll the controller because its config
space is not accessible when the PCIe port is in a low power state.

To solve the issue, the PCIe port will not be suspended if any subordinate
device needs PME polling.

[bhelgaas: use bool consistently rather than mixing int/bool]
Reference: http://lkml.kernel.org/r/50841CCC.9030809@uli-eckhardt.deReported-by: default avatarUlrich Eckhardt <usb@uli-eckhardt.de>
Tested-by: default avatarSarah Sharp <sarah.a.sharp@linux.intel.com>
Signed-off-by: default avatarHuang Ying <ying.huang@intel.com>
Signed-off-by: default avatarBjorn Helgaas <bhelgaas@google.com>
Acked-by: default avatarRafael J. Wysocki <rafael.j.wysocki@intel.com>
CC: stable@vger.kernel.org	# v3.6+
parent 130f1b8f
...@@ -134,10 +134,28 @@ static int pcie_port_runtime_resume(struct device *dev) ...@@ -134,10 +134,28 @@ static int pcie_port_runtime_resume(struct device *dev)
return 0; return 0;
} }
static int pci_dev_pme_poll(struct pci_dev *pdev, void *data)
{
bool *pme_poll = data;
if (pdev->pme_poll)
*pme_poll = true;
return 0;
}
static int pcie_port_runtime_idle(struct device *dev) static int pcie_port_runtime_idle(struct device *dev)
{ {
struct pci_dev *pdev = to_pci_dev(dev);
bool pme_poll = false;
/*
* If any subordinate device needs pme poll, we should keep
* the port in D0, because we need port in D0 to poll it.
*/
pci_walk_bus(pdev->subordinate, pci_dev_pme_poll, &pme_poll);
/* Delay for a short while to prevent too frequent suspend/resume */ /* Delay for a short while to prevent too frequent suspend/resume */
pm_schedule_suspend(dev, 10); if (!pme_poll)
pm_schedule_suspend(dev, 10);
return -EBUSY; return -EBUSY;
} }
#else #else
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment