@@ -3293,62 +3293,6 @@ static void quirk_apple_poweroff_thunderbolt(struct pci_dev *dev)
DECLARE_PCI_FIXUP_SUSPEND_LATE(PCI_VENDOR_ID_INTEL,
PCI_DEVICE_ID_INTEL_CACTUS_RIDGE_4C,
quirk_apple_poweroff_thunderbolt);
-
-/*
- * Apple: Wait for the thunderbolt controller to reestablish pci tunnels.
- *
- * During suspend the thunderbolt controller is reset and all pci
- * tunnels are lost. The NHI driver will try to reestablish all tunnels
- * during resume. We have to manually wait for the NHI since there is
- * no parent child relationship between the NHI and the tunneled
- * bridges.
- */
-static void quirk_apple_wait_for_thunderbolt(struct pci_dev *dev)
-{
- struct pci_dev *sibling = NULL;
- struct pci_dev *nhi = NULL;
-
- if (!dmi_match(DMI_BOARD_VENDOR, "Apple Inc."))
- return;
- if (pci_pcie_type(dev) != PCI_EXP_TYPE_DOWNSTREAM)
- return;
- /*
- * Find the NHI and confirm that we are a bridge on the tb host
- * controller and not on a tb endpoint.
- */
- sibling = pci_get_slot(dev->bus, 0x0);
- if (sibling == dev)
- goto out; /* we are the downstream bridge to the NHI */
- if (!sibling || !sibling->subordinate)
- goto out;
- nhi = pci_get_slot(sibling->subordinate, 0x0);
- if (!nhi)
- goto out;
- if (nhi->vendor != PCI_VENDOR_ID_INTEL
- || (nhi->device != PCI_DEVICE_ID_INTEL_LIGHT_RIDGE &&
- nhi->device != PCI_DEVICE_ID_INTEL_CACTUS_RIDGE_4C &&
- nhi->device != PCI_DEVICE_ID_INTEL_FALCON_RIDGE_2C_NHI &&
- nhi->device != PCI_DEVICE_ID_INTEL_FALCON_RIDGE_4C_NHI)
- || nhi->class != PCI_CLASS_SYSTEM_OTHER << 8)
- goto out;
- dev_info(&dev->dev, "quirk: waiting for thunderbolt to reestablish PCI tunnels...\n");
- device_pm_wait_for_dev(&dev->dev, &nhi->dev);
-out:
- pci_dev_put(nhi);
- pci_dev_put(sibling);
-}
-DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_INTEL,
- PCI_DEVICE_ID_INTEL_LIGHT_RIDGE,
- quirk_apple_wait_for_thunderbolt);
-DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_INTEL,
- PCI_DEVICE_ID_INTEL_CACTUS_RIDGE_4C,
- quirk_apple_wait_for_thunderbolt);
-DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_INTEL,
- PCI_DEVICE_ID_INTEL_FALCON_RIDGE_2C_BRIDGE,
- quirk_apple_wait_for_thunderbolt);
-DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_INTEL,
- PCI_DEVICE_ID_INTEL_FALCON_RIDGE_4C_BRIDGE,
- quirk_apple_wait_for_thunderbolt);
#endif
static void pci_do_fixups(struct pci_dev *dev, struct pci_fixup *f,
@@ -533,6 +533,21 @@ static void nhi_shutdown(struct tb_nhi *nhi)
mutex_destroy(&nhi->lock);
}
+static int nhi_device_link_add_cb(struct pci_dev *pdev, void *ptr)
+{
+ struct tb *tb = ptr;
+
+ /*
+ * Let all downstream bridges of the switch depend on the NHI, except
+ * for the NHI's parent bridge. This forces them to dpm_wait() in the
+ * ->resume_noirq phase until the NHI has re-established the tunnels.
+ */
+ if (pdev->bus == tb->downstream0->bus && pdev != tb->downstream0)
+ device_link_add(&pdev->dev, &tb->nhi->pdev->dev,
+ DEVICE_LINK_NO_STATE, DEVICE_LINK_STATELESS);
+ return 0;
+}
+
static int nhi_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{
struct tb_nhi *nhi;
@@ -605,6 +620,10 @@ static int nhi_probe(struct pci_dev *pdev, const struct pci_device_id *id)
}
pci_set_drvdata(pdev, tb);
+ tb->downstream0 = pci_upstream_bridge(pdev);
+ if (tb->downstream0)
+ pci_walk_bus(tb->downstream0->bus, nhi_device_link_add_cb, tb);
+
return 0;
}
@@ -612,14 +631,19 @@ static void nhi_remove(struct pci_dev *pdev)
{
struct tb *tb = pci_get_drvdata(pdev);
struct tb_nhi *nhi = tb->nhi;
+ struct device_link *link, *ln;
+
+ list_for_each_entry_safe(link, ln, &pdev->dev.links_to_consumers, s_node)
+ device_link_del(link);
+
thunderbolt_shutdown_and_free(tb);
nhi_shutdown(nhi);
}
/*
* The tunneled pci bridges are siblings of us. Use resume_noirq to reenable
- * the tunnels asap. A corresponding pci quirk blocks the downstream bridges
- * resume_noirq until we are done.
+ * the tunnels asap. Device links block the downstream bridges' resume_noirq
+ * until we are done.
*/
static const struct dev_pm_ops nhi_pm_ops = {
.suspend_noirq = nhi_suspend_noirq,
@@ -106,6 +106,7 @@ struct tb {
struct workqueue_struct *wq; /* ordered workqueue for plug events */
struct tb_switch *root_switch;
struct list_head tunnel_list; /* list of active PCIe tunnels */
+ struct pci_dev *downstream0; /* downstream bridge to the NHI */
bool hotplug_active; /*
* tb_handle_hotplug will stop progressing plug
* events and exit if this is not set (it needs to