aboutsummaryrefslogtreecommitdiff
path: root/drivers/pci
diff options
context:
space:
mode:
authorGravatar Ilpo Järvinen <ilpo.jarvinen@linux.intel.com> 2024-04-23 16:08:19 +0300
committerGravatar Bjorn Helgaas <bhelgaas@google.com> 2024-04-25 12:53:28 -0500
commit73cb3a35f94db723c0211ad099bce55b2155e3f0 (patch)
tree55478c1593f1717031d39a08b5f64d6c23060aa4 /drivers/pci
parentPCI: Clear Secondary Status errors after enumeration (diff)
downloadlinux-73cb3a35f94db723c0211ad099bce55b2155e3f0.tar.gz
linux-73cb3a35f94db723c0211ad099bce55b2155e3f0.tar.bz2
linux-73cb3a35f94db723c0211ad099bce55b2155e3f0.zip
PCI: Wait for Link Training==0 before starting Link retrain
Two changes were made in link retraining logic independent of each other. The commit e7e39756363a ("PCI/ASPM: Avoid link retraining race") added a check to pcie_retrain_link() to ensure no Link Training is currently active to address the Implementation Note in PCIe r6.1 sec 7.5.3.7. At that time pcie_wait_for_retrain() only checked for the Link Training (LT) bit being cleared. The commit 680e9c47a229 ("PCI: Add support for polling DLLLA to pcie_retrain_link()") generalized pcie_wait_for_retrain() into pcie_wait_for_link_status() which can wait either for LT or the Data Link Layer Link Active (DLLLA) bit with 'use_lt' argument and supporting waiting for either cleared or set using 'active' argument. In the merge commit 1abb47390350 ("Merge branch 'pci/enumeration'"), those two divergent branches converged. The merge changed LT bit checking added in the commit e7e39756363a ("PCI/ASPM: Avoid link retraining race") to now wait for completion of any ongoing Link Training using DLLLA bit being set if 'use_lt' is false. When 'use_lt' is false, the pseudo-code steps of what occurs in pcie_retrain_link(): 1. Wait for DLLLA==1 2. Trigger link to retrain 3. Wait for DLLLA==1 Step 3 waits for the link to come up from the retraining triggered by Step 2. As Step 1 is supposed to wait for any ongoing retraining to end, using DLLLA also for it does not make sense because link training being active is still indicated using LT bit, not with DLLLA. Correct the pcie_wait_for_link_status() parameters in Step 1 to only wait for LT==0 to ensure there is no ongoing Link Training. This only impacts the Target Speed quirk, which is the only case where waiting for DLLLA bit is used. It currently works in the problematic case by means of link training getting initiated by hardware repeatedly and respecting the new link parameters set by the caller, which then make training succeed and bring the link up, setting DLLLA and causing pcie_wait_for_link_status() to return success. We are not supposed to rely on luck and need to make sure that LT transitioned through the inactive state though before we initiate link training by hand via RL (Retrain Link) bit. Fixes: 1abb47390350 ("Merge branch 'pci/enumeration'") Link: https://lore.kernel.org/r/20240423130820.43824-1-ilpo.jarvinen@linux.intel.com Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com> Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Diffstat (limited to 'drivers/pci')
-rw-r--r--drivers/pci/pci.c2
1 files changed, 1 insertions, 1 deletions
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index e5f243dd4288..70b8c87055cb 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -4629,7 +4629,7 @@ int pcie_retrain_link(struct pci_dev *pdev, bool use_lt)
* avoid LTSSM race as recommended in Implementation Note at the
* end of PCIe r6.0.1 sec 7.5.3.7.
*/
- rc = pcie_wait_for_link_status(pdev, use_lt, !use_lt);
+ rc = pcie_wait_for_link_status(pdev, true, false);
if (rc)
return rc;