diff options
-rw-r--r-- | drivers/pci/quirks.c | 57 | ||||
-rw-r--r-- | drivers/thunderbolt/nhi.c | 66 |
2 files changed, 66 insertions, 57 deletions
diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index 2a589b6d6ed8..01f23e30bd8f 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -3673,63 +3673,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 (!x86_apple_machine) - return; - if (pci_pcie_type(dev) != PCI_EXP_TYPE_DOWNSTREAM) - return; - - /* - * Find the NHI and confirm that we are a bridge on the Thunderbolt - * host controller and not on a Thunderbolt 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; - pci_info(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 /* diff --git a/drivers/thunderbolt/nhi.c b/drivers/thunderbolt/nhi.c index 24d2b7eff59b..e499fe78756b 100644 --- a/drivers/thunderbolt/nhi.c +++ b/drivers/thunderbolt/nhi.c @@ -17,6 +17,7 @@ #include <linux/module.h> #include <linux/delay.h> #include <linux/property.h> +#include <linux/platform_data/x86/apple.h> #include "nhi.h" #include "nhi_regs.h" @@ -1069,6 +1070,69 @@ static bool nhi_imr_valid(struct pci_dev *pdev) return true; } +/* + * During suspend the Thunderbolt controller is reset and all PCIe + * tunnels are lost. The NHI driver will try to reestablish all tunnels + * during resume. This adds device links between the tunneled PCIe + * downstream ports and the NHI so that the device core will make sure + * NHI is resumed first before the rest. + */ +static void tb_apple_add_links(struct tb_nhi *nhi) +{ + struct pci_dev *upstream, *pdev; + + if (!x86_apple_machine) + return; + + switch (nhi->pdev->device) { + case PCI_DEVICE_ID_INTEL_LIGHT_RIDGE: + case PCI_DEVICE_ID_INTEL_CACTUS_RIDGE_4C: + case PCI_DEVICE_ID_INTEL_FALCON_RIDGE_2C_NHI: + case PCI_DEVICE_ID_INTEL_FALCON_RIDGE_4C_NHI: + break; + default: + return; + } + + upstream = pci_upstream_bridge(nhi->pdev); + while (upstream) { + if (!pci_is_pcie(upstream)) + return; + if (pci_pcie_type(upstream) == PCI_EXP_TYPE_UPSTREAM) + break; + upstream = pci_upstream_bridge(upstream); + } + + if (!upstream) + return; + + /* + * For each hotplug downstream port, create add device link + * back to NHI so that PCIe tunnels can be re-established after + * sleep. + */ + for_each_pci_bridge(pdev, upstream->subordinate) { + const struct device_link *link; + + if (!pci_is_pcie(pdev)) + continue; + if (pci_pcie_type(pdev) != PCI_EXP_TYPE_DOWNSTREAM || + !pdev->is_hotplug_bridge) + continue; + + link = device_link_add(&pdev->dev, &nhi->pdev->dev, + DL_FLAG_AUTOREMOVE_SUPPLIER | + DL_FLAG_PM_RUNTIME); + if (link) { + dev_dbg(&nhi->pdev->dev, "created link from %s\n", + dev_name(&pdev->dev)); + } else { + dev_warn(&nhi->pdev->dev, "device link creation from %s failed\n", + dev_name(&pdev->dev)); + } + } +} + static int nhi_probe(struct pci_dev *pdev, const struct pci_device_id *id) { struct tb_nhi *nhi; @@ -1134,6 +1198,8 @@ static int nhi_probe(struct pci_dev *pdev, const struct pci_device_id *id) return res; } + tb_apple_add_links(nhi); + tb = icm_probe(nhi); if (!tb) tb = tb_probe(nhi); |