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 <ma...@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
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)

Reply via email to