===================================================================
@@ -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)
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@orcam.me.uk> --- 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