summaryrefslogtreecommitdiffstats
path: root/drivers/pci/pci.c
diff options
context:
space:
mode:
authorMaciej W. Rozycki <macro@orcam.me.uk>2023-06-11 19:20:06 +0200
committerBjorn Helgaas <bhelgaas@google.com>2023-06-20 17:58:53 +0200
commit08e3ed12ca8615b078ea19488fb45b084e5de16b (patch)
treeee1204eec8f49db171191c967102a2bbe75b87ca /drivers/pci/pci.c
parentPCI: Work around PCIe link training failures (diff)
downloadlinux-08e3ed12ca8615b078ea19488fb45b084e5de16b.tar.xz
linux-08e3ed12ca8615b078ea19488fb45b084e5de16b.zip
PCI: Add failed link recovery for device reset events
Request failed link recovery with any upstream PCIe bridge where a device has not come back after reset within PCI_RESET_WAIT time. Reset the polling interval if recovery succeeded, otherwise continue as usual. [bhelgaas: inline pcie_parent_link_retrain()] Link: https://lore.kernel.org/r/alpine.DEB.2.21.2306111631050.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.c29
1 files changed, 24 insertions, 5 deletions
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index f599d321c881..64f1a87902d8 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -1156,7 +1156,14 @@ void pci_resume_bus(struct pci_bus *bus)
static int pci_dev_wait(struct pci_dev *dev, char *reset_type, int timeout)
{
int delay = 1;
- u32 id;
+ bool retrain = false;
+ struct pci_dev *bridge;
+
+ if (pci_is_pcie(dev)) {
+ bridge = pci_upstream_bridge(dev);
+ if (bridge)
+ retrain = true;
+ }
/*
* After reset, the device should not silently discard config
@@ -1170,21 +1177,33 @@ static int pci_dev_wait(struct pci_dev *dev, char *reset_type, int timeout)
* Command register instead of Vendor ID so we don't have to
* contend with the CRS SV value.
*/
- pci_read_config_dword(dev, PCI_COMMAND, &id);
- while (PCI_POSSIBLE_ERROR(id)) {
+ for (;;) {
+ u32 id;
+
+ pci_read_config_dword(dev, PCI_COMMAND, &id);
+ if (!PCI_POSSIBLE_ERROR(id))
+ break;
+
if (delay > timeout) {
pci_warn(dev, "not ready %dms after %s; giving up\n",
delay - 1, reset_type);
return -ENOTTY;
}
- if (delay > PCI_RESET_WAIT)
+ if (delay > PCI_RESET_WAIT) {
+ if (retrain) {
+ retrain = false;
+ if (pcie_failed_link_retrain(bridge)) {
+ delay = 1;
+ continue;
+ }
+ }
pci_info(dev, "not ready %dms after %s; waiting\n",
delay - 1, reset_type);
+ }
msleep(delay);
delay *= 2;
- pci_read_config_dword(dev, PCI_COMMAND, &id);
}
if (delay > PCI_RESET_WAIT)