[PATCH v9 07/14] PCI: Export `pcie_retrain_link' for use outside ASPM

Maciej W. Rozycki
2023-06-11T17:20:38Z

Export `pcie_retrain_link' for link retrain needs outside ASPM. There
is no functional change at this point, but `struct pcie_link_state' is
local to ASPM and not used within `pcie_retrain_link' other than to get
at the associated PCI device, so change the operand and adjust the lone
call site accordingly. Document the interface.

Signed-off-by: Maciej W. Rozycki <macro@orcam.me.uk>
---
There's a missing full stop added in a comment in the course of the move,
not worth mentioning in the change description IMHO and not worth its own
change either. This comment will go away in a subsequent change anyway.

New change in v9.
---
drivers/pci/pci.c | 36 ++++++++++++++++++++++++++++++++++++
drivers/pci/pci.h | 1 +
drivers/pci/pcie/aspm.c | 32 +-------------------------------
3 files changed, 38 insertions(+), 31 deletions(-)

linux-pcie-retrain-link-export.diff
Index: linux-macro/drivers/pci/pci.c
===================================================================
--- linux-macro.orig/drivers/pci/pci.c
+++ linux-macro/drivers/pci/pci.c
@@ -4912,6 +4912,42 @@ bool pcie_wait_for_link(struct pci_dev *
return pcie_wait_for_link_delay(pdev, active, 100);
}

+/**
+ * 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)
+{
+ unsigned long end_jiffies;
+ u16 reg16;
+
+ pcie_capability_read_word(pdev, PCI_EXP_LNKCTL, &reg16);
+ reg16 |= PCI_EXP_LNKCTL_RL;
+ pcie_capability_write_word(pdev, PCI_EXP_LNKCTL, reg16);
+ 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.
+ */
+ reg16 &= ~PCI_EXP_LNKCTL_RL;
+ pcie_capability_write_word(pdev, PCI_EXP_LNKCTL, reg16);
+ }
+
+ /* Wait for link training end. Break out after waiting for timeout. */
+ end_jiffies = jiffies + msecs_to_jiffies(PCIE_LINK_RETRAIN_TIMEOUT_MS);
+ do {
+ pcie_capability_read_word(pdev, PCI_EXP_LNKSTA, &reg16);
+ if (!(reg16 & PCI_EXP_LNKSTA_LT))
+ break;
+ msleep(1);
+ } while (time_before(jiffies, end_jiffies));
+ return !(reg16 & PCI_EXP_LNKSTA_LT);
+}
+
/*
* Find maximum D3cold delay required by all the devices on the bus. The
* spec says 100 ms, but firmware can lower it and we allow drivers to
Index: linux-macro/drivers/pci/pci.h
===================================================================
--- linux-macro.orig/drivers/pci/pci.h
+++ linux-macro/drivers/pci/pci.h
@@ -561,6 +561,7 @@ pci_ers_result_t pcie_do_recovery(struct
pci_ers_result_t (*reset_subordinates)(struct pci_dev *pdev));

bool pcie_wait_for_link(struct pci_dev *pdev, bool active);
+bool pcie_retrain_link(struct pci_dev *pdev);
#ifdef CONFIG_PCIEASPM
void pcie_aspm_init_link_state(struct pci_dev *pdev);
void pcie_aspm_exit_link_state(struct pci_dev *pdev);
Index: linux-macro/drivers/pci/pcie/aspm.c
===================================================================
--- linux-macro.orig/drivers/pci/pcie/aspm.c
+++ linux-macro/drivers/pci/pcie/aspm.c
@@ -191,36 +191,6 @@ static void pcie_clkpm_cap_init(struct p
link->clkpm_disable = blacklist ? 1 : 0;
}

-static bool pcie_retrain_link(struct pcie_link_state *link)
-{
- struct pci_dev *parent = link->pdev;
- unsigned long end_jiffies;
- u16 reg16;
-
- pcie_capability_read_word(parent, PCI_EXP_LNKCTL, &reg16);
- reg16 |= PCI_EXP_LNKCTL_RL;
- pcie_capability_write_word(parent, PCI_EXP_LNKCTL, reg16);
- if (parent->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.
- */
- reg16 &= ~PCI_EXP_LNKCTL_RL;
- pcie_capability_write_word(parent, PCI_EXP_LNKCTL, reg16);
- }
-
- /* Wait for link training end. Break out after waiting for timeout */
- end_jiffies = jiffies + msecs_to_jiffies(PCIE_LINK_RETRAIN_TIMEOUT_MS);
- do {
- pcie_capability_read_word(parent, PCI_EXP_LNKSTA, &reg16);
- if (!(reg16 & PCI_EXP_LNKSTA_LT))
- break;
- msleep(1);
- } while (time_before(jiffies, end_jiffies));
- return !(reg16 & PCI_EXP_LNKSTA_LT);
-}
-
/*
* pcie_aspm_configure_common_clock: check if the 2 ends of a link
* could use common clock. If they are, configure them to use the
@@ -287,7 +257,7 @@ static void pcie_aspm_configure_common_c
reg16 &= ~PCI_EXP_LNKCTL_CCC;
pcie_capability_write_word(parent, PCI_EXP_LNKCTL, reg16);

- if (pcie_retrain_link(link))
+ if (pcie_retrain_link(link->pdev))
return;

/* Training failed. Restore common clock configurations */