summaryrefslogtreecommitdiff
path: root/drivers/thunderbolt/tb.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/thunderbolt/tb.c')
-rw-r--r--drivers/thunderbolt/tb.c71
1 files changed, 69 insertions, 2 deletions
diff --git a/drivers/thunderbolt/tb.c b/drivers/thunderbolt/tb.c
index 7e6dc2b03bed..2897a77d44c3 100644
--- a/drivers/thunderbolt/tb.c
+++ b/drivers/thunderbolt/tb.c
@@ -10,6 +10,7 @@
#include <linux/errno.h>
#include <linux/delay.h>
#include <linux/pm_runtime.h>
+#include <linux/platform_data/x86/apple.h>
#include "tb.h"
#include "tb_regs.h"
@@ -595,7 +596,7 @@ static void tb_scan_port(struct tb_port *port)
return;
}
- tb_retimer_scan(port);
+ tb_retimer_scan(port, true);
sw = tb_switch_alloc(port->sw->tb, &port->sw->dev,
tb_downstream_route(port));
@@ -662,7 +663,7 @@ static void tb_scan_port(struct tb_port *port)
tb_sw_warn(sw, "failed to enable TMU\n");
/* Scan upstream retimers */
- tb_retimer_scan(upstream_port);
+ tb_retimer_scan(upstream_port, true);
/*
* Create USB 3.x tunnels only when the switch is plugged to the
@@ -1571,6 +1572,69 @@ static const struct tb_cm_ops tb_cm_ops = {
.disconnect_xdomain_paths = tb_disconnect_xdomain_paths,
};
+/*
+ * 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));
+ }
+ }
+}
+
struct tb *tb_probe(struct tb_nhi *nhi)
{
struct tb_cm *tcm;
@@ -1594,5 +1658,8 @@ struct tb *tb_probe(struct tb_nhi *nhi)
tb_dbg(tb, "using software connection manager\n");
+ tb_apple_add_links(nhi);
+ tb_acpi_add_links(nhi);
+
return tb;
}