summaryrefslogtreecommitdiff
path: root/drivers/pci/pci.c
diff options
context:
space:
mode:
authorMaciej W. Rozycki <macro@orcam.me.uk>2023-06-11 18:19:41 +0100
committerBjorn Helgaas <bhelgaas@google.com>2023-06-20 10:58:53 -0500
commit37edd87eb621a96d33ee4eefe4b54cfc5a7e03df (patch)
tree39f140cd556bd47729cc22fcb51871dd2f1c47c0 /drivers/pci/pci.c
parent33a176abcc4cd4ed3d65512ed96d7b73f2565ed7 (diff)
PCI: Export pcie_retrain_link() for use outside ASPM
Export pcie_retrain_link() for link retrain needs outside ASPM. Struct pcie_link_state is local to ASPM and only used by pcie_retrain_link() to get at the associated PCI device, so change the operand and adjust the lone call site accordingly. Document the interface. No functional change at this point. Link: https://lore.kernel.org/r/alpine.DEB.2.21.2306110229010.64925@angie.orcam.me.uk Signed-off-by: Maciej W. Rozycki <macro@orcam.me.uk> Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Diffstat (limited to 'drivers/pci/pci.c')
-rw-r--r--drivers/pci/pci.c49
1 files changed, 49 insertions, 0 deletions
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index 71645d568986..47ceb8567b2b 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -4857,6 +4857,55 @@ static int pci_pm_reset(struct pci_dev *dev, bool probe)
}
/**
+ * pcie_wait_for_link_status - Wait for link training end
+ * @pdev: Device whose link to wait for.
+ *
+ * Return TRUE if successful, or FALSE if training has not completed
+ * within PCIE_LINK_RETRAIN_TIMEOUT_MS milliseconds.
+ */
+static bool pcie_wait_for_link_status(struct pci_dev *pdev)
+{
+ unsigned long end_jiffies;
+ u16 lnksta;
+
+ end_jiffies = jiffies + msecs_to_jiffies(PCIE_LINK_RETRAIN_TIMEOUT_MS);
+ do {
+ pcie_capability_read_word(pdev, PCI_EXP_LNKSTA, &lnksta);
+ if (!(lnksta & PCI_EXP_LNKSTA_LT))
+ break;
+ msleep(1);
+ } while (time_before(jiffies, end_jiffies));
+ return !(lnksta & PCI_EXP_LNKSTA_LT);
+}
+
+/**
+ * pcie_retrain_link - Request a link retrain and wait for it to complete
+ * @pdev: Device whose link to retrain.
+ *
+ * Return TRUE if successful, or FALSE if training has not completed
+ * within PCIE_LINK_RETRAIN_TIMEOUT_MS milliseconds.
+ */
+bool pcie_retrain_link(struct pci_dev *pdev)
+{
+ u16 lnkctl;
+
+ pcie_capability_read_word(pdev, PCI_EXP_LNKCTL, &lnkctl);
+ lnkctl |= PCI_EXP_LNKCTL_RL;
+ pcie_capability_write_word(pdev, PCI_EXP_LNKCTL, lnkctl);
+ if (pdev->clear_retrain_link) {
+ /*
+ * Due to an erratum in some devices the Retrain Link bit
+ * needs to be cleared again manually to allow the link
+ * training to succeed.
+ */
+ lnkctl &= ~PCI_EXP_LNKCTL_RL;
+ pcie_capability_write_word(pdev, PCI_EXP_LNKCTL, lnkctl);
+ }
+
+ return pcie_wait_for_link_status(pdev);
+}
+
+/**
* pcie_wait_for_link_delay - Wait until link is active or inactive
* @pdev: Bridge device
* @active: waiting for active or inactive?