Commit ec411e02 authored by Mika Westerberg's avatar Mika Westerberg Committed by Bjorn Helgaas

PCI/PM: Assume ports without DLL Link Active train links in 100 ms

Kai-Heng Feng reported that it takes a long time (> 1 s) to resume
Thunderbolt-connected devices from both runtime suspend and system sleep
(s2idle).

This was because some Downstream Ports that support > 5 GT/s do not also
support Data Link Layer Link Active reporting.  Per PCIe r5.0 sec 6.6.1:

  With a Downstream Port that supports Link speeds greater than 5.0 GT/s,
  software must wait a minimum of 100 ms after Link training completes
  before sending a Configuration Request to the device immediately below
  that Port. Software can determine when Link training completes by polling
  the Data Link Layer Link Active bit or by setting up an associated
  interrupt (see Section 6.7.3.3).

Sec 7.5.3.6 requires such Ports to support DLL Link Active reporting, but
at least the Intel JHL6240 Thunderbolt 3 Bridge [8086:15c0] and the Intel
JHL7540 Thunderbolt 3 Bridge [8086:15ea] do not.

Previously we tried to wait for Link training to complete, but since there
was no DLL Link Active reporting, all we could do was wait the worst-case
1000 ms, then another 100 ms.

Instead of using the supported speeds to determine whether to wait for Link
training, check whether the port supports DLL Link Active reporting.  The
Ports in question do not, so we'll wait only the 100 ms required for Ports
that support Link speeds <= 5 GT/s.

This of course assumes these Ports always train the Link within 100 ms even
if they are operating at > 5 GT/s, which is not required by the spec.

[bhelgaas: commit log, comment]
Link: https://bugzilla.kernel.org/show_bug.cgi?id=206837
Link: https://lore.kernel.org/r/20200514133043.27429-1-mika.westerberg@linux.intel.comReported-by: default avatarKai-Heng Feng <kai.heng.feng@canonical.com>
Tested-by: default avatarKai-Heng Feng <kai.heng.feng@canonical.com>
Signed-off-by: default avatarMika Westerberg <mika.westerberg@linux.intel.com>
Signed-off-by: default avatarBjorn Helgaas <bhelgaas@google.com>
parent f044baaf
...@@ -4662,7 +4662,8 @@ static int pci_pm_reset(struct pci_dev *dev, int probe) ...@@ -4662,7 +4662,8 @@ static int pci_pm_reset(struct pci_dev *dev, int probe)
* pcie_wait_for_link_delay - Wait until link is active or inactive * pcie_wait_for_link_delay - Wait until link is active or inactive
* @pdev: Bridge device * @pdev: Bridge device
* @active: waiting for active or inactive? * @active: waiting for active or inactive?
* @delay: Delay to wait after link has become active (in ms) * @delay: Delay to wait after link has become active (in ms). Specify %0
* for no delay.
* *
* Use this to wait till link becomes active or inactive. * Use this to wait till link becomes active or inactive.
*/ */
...@@ -4703,7 +4704,7 @@ static bool pcie_wait_for_link_delay(struct pci_dev *pdev, bool active, ...@@ -4703,7 +4704,7 @@ static bool pcie_wait_for_link_delay(struct pci_dev *pdev, bool active,
msleep(10); msleep(10);
timeout -= 10; timeout -= 10;
} }
if (active && ret) if (active && ret && delay)
msleep(delay); msleep(delay);
else if (ret != active) else if (ret != active)
pci_info(pdev, "Data Link Layer Link Active not %s in 1000 msec\n", pci_info(pdev, "Data Link Layer Link Active not %s in 1000 msec\n",
...@@ -4824,17 +4825,28 @@ void pci_bridge_wait_for_secondary_bus(struct pci_dev *dev) ...@@ -4824,17 +4825,28 @@ void pci_bridge_wait_for_secondary_bus(struct pci_dev *dev)
if (!pcie_downstream_port(dev)) if (!pcie_downstream_port(dev))
return; return;
if (pcie_get_speed_cap(dev) <= PCIE_SPEED_5_0GT) { /*
pci_dbg(dev, "waiting %d ms for downstream link\n", delay); * Per PCIe r5.0, sec 6.6.1, for downstream ports that support
msleep(delay); * speeds > 5 GT/s, we must wait for link training to complete
} else { * before the mandatory delay.
pci_dbg(dev, "waiting %d ms for downstream link, after activation\n", *
delay); * We can only tell when link training completes via DLL Link
if (!pcie_wait_for_link_delay(dev, true, delay)) { * Active, which is required for downstream ports that support
* speeds > 5 GT/s (sec 7.5.3.6). Unfortunately some common
* devices do not implement Link Active reporting even when it's
* required, so we'll check for that directly instead of checking
* the supported link speed. We assume devices without Link Active
* reporting can train in 100 ms regardless of speed.
*/
if (dev->link_active_reporting) {
pci_dbg(dev, "waiting for link to train\n");
if (!pcie_wait_for_link_delay(dev, true, 0)) {
/* Did not train, no need to wait any further */ /* Did not train, no need to wait any further */
return; return;
} }
} }
pci_dbg(child, "waiting %d ms to become accessible\n", delay);
msleep(delay);
if (!pci_device_is_present(child)) { if (!pci_device_is_present(child)) {
pci_dbg(child, "waiting additional %d ms to become accessible\n", delay); pci_dbg(child, "waiting additional %d ms to become accessible\n", delay);
......
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