diff options
-rw-r--r-- | drivers/pci/pci.c | 17 | ||||
-rw-r--r-- | drivers/pci/pcie/bwctrl.c | 13 |
2 files changed, 18 insertions, 12 deletions
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 3d94cf33c1b6..eb0c55078d5e 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -4718,6 +4718,11 @@ static int pcie_wait_for_link_status(struct pci_dev *pdev, * @pdev: Device whose link to retrain. * @use_lt: Use the LT bit if TRUE, or the DLLLA bit if FALSE, for status. * + * Trigger retraining of the PCIe Link and wait for the completion of the + * retraining. As link retraining is known to asserts LBMS and may change + * the Link Speed, LBMS is cleared after the retraining and the Link Speed + * of the subordinate bus is updated. + * * Retrain completion status is retrieved from the Link Status Register * according to @use_lt. It is not verified whether the use of the DLLLA * bit is valid. @@ -4758,6 +4763,18 @@ int pcie_retrain_link(struct pci_dev *pdev, bool use_lt) * in attempt to correct unreliable link operation. */ pcie_reset_lbms(pdev); + + /* + * Ensure the Link Speed updates after retraining in case the Link + * Speed was changed because of the retraining. While the bwctrl's + * IRQ handler normally picks up the new Link Speed, clearing LBMS + * races with the IRQ handler reading the Link Status register and + * can result in the handler returning early without updating the + * Link Speed. + */ + if (pdev->subordinate) + pcie_update_link_speed(pdev->subordinate); + return rc; } diff --git a/drivers/pci/pcie/bwctrl.c b/drivers/pci/pcie/bwctrl.c index f31fbbd51490..36f939f23d34 100644 --- a/drivers/pci/pcie/bwctrl.c +++ b/drivers/pci/pcie/bwctrl.c @@ -117,18 +117,7 @@ static int pcie_bwctrl_change_speed(struct pci_dev *port, u16 target_speed, bool if (ret != PCIBIOS_SUCCESSFUL) return pcibios_err_to_errno(ret); - ret = pcie_retrain_link(port, use_lt); - if (ret < 0) - return ret; - - /* - * Ensure link speed updates also with platforms that have problems - * with notifications. - */ - if (port->subordinate) - pcie_update_link_speed(port->subordinate); - - return 0; + return pcie_retrain_link(port, use_lt); } /** |