Request failed link recovery with any upstream 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. Signed-off-by: Maciej W. Rozycki <macro@xxxxxxxxxxx> --- New change in v9, factored out from 7/7: - Remove duplicate succesful completion report previously added (not sure where it came from, possibly an unnoticed leftover from experiments). - Make the type of `retrain' variable `bool' rather than `int' and invert the logic used. - Rename `pcie_downstream_link_retrain' to `pcie_failed_link_retrain'. - Rename `pcie_upstream_link_retrain' to `pcie_parent_link_retrain'. Add documentation. --- drivers/pci/pci.c | 39 ++++++++++++++++++++++++++++++++++----- 1 file changed, 34 insertions(+), 5 deletions(-) linux-pcie-dev-wait-link-retrain.diff Index: linux-macro/drivers/pci/pci.c =================================================================== --- linux-macro.orig/drivers/pci/pci.c +++ linux-macro/drivers/pci/pci.c @@ -1146,10 +1146,27 @@ void pci_resume_bus(struct pci_bus *bus) pci_walk_bus(bus, pci_resume_one, NULL); } +/** + * pcie_parent_link_retrain - Check and retrain link we are downstream from + * @dev: PCI device to handle. + * + * Return TRUE if the link was retrained, FALSE otherwise. + */ +static bool pcie_parent_link_retrain(struct pci_dev *dev) +{ + struct pci_dev *bridge; + + bridge = pci_upstream_bridge(dev); + if (bridge) + return pcie_failed_link_retrain(bridge); + else + return false; +} + static int pci_dev_wait(struct pci_dev *dev, char *reset_type, int timeout) { + bool retrain = true; int delay = 1; - u32 id; /* * After reset, the device should not silently discard config @@ -1163,21 +1180,33 @@ static int pci_dev_wait(struct pci_dev * * 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_parent_link_retrain(dev)) { + 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)