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.c68
1 files changed, 52 insertions, 16 deletions
diff --git a/drivers/thunderbolt/tb.c b/drivers/thunderbolt/tb.c
index 2897a77d44c3..a231191b06c6 100644
--- a/drivers/thunderbolt/tb.c
+++ b/drivers/thunderbolt/tb.c
@@ -105,10 +105,11 @@ static void tb_remove_dp_resources(struct tb_switch *sw)
}
}
-static void tb_discover_tunnels(struct tb_switch *sw)
+static void tb_switch_discover_tunnels(struct tb_switch *sw,
+ struct list_head *list,
+ bool alloc_hopids)
{
struct tb *tb = sw->tb;
- struct tb_cm *tcm = tb_priv(tb);
struct tb_port *port;
tb_switch_for_each_port(sw, port) {
@@ -116,24 +117,41 @@ static void tb_discover_tunnels(struct tb_switch *sw)
switch (port->config.type) {
case TB_TYPE_DP_HDMI_IN:
- tunnel = tb_tunnel_discover_dp(tb, port);
+ tunnel = tb_tunnel_discover_dp(tb, port, alloc_hopids);
break;
case TB_TYPE_PCIE_DOWN:
- tunnel = tb_tunnel_discover_pci(tb, port);
+ tunnel = tb_tunnel_discover_pci(tb, port, alloc_hopids);
break;
case TB_TYPE_USB3_DOWN:
- tunnel = tb_tunnel_discover_usb3(tb, port);
+ tunnel = tb_tunnel_discover_usb3(tb, port, alloc_hopids);
break;
default:
break;
}
- if (!tunnel)
- continue;
+ if (tunnel)
+ list_add_tail(&tunnel->list, list);
+ }
+ tb_switch_for_each_port(sw, port) {
+ if (tb_port_has_remote(port)) {
+ tb_switch_discover_tunnels(port->remote->sw, list,
+ alloc_hopids);
+ }
+ }
+}
+
+static void tb_discover_tunnels(struct tb *tb)
+{
+ struct tb_cm *tcm = tb_priv(tb);
+ struct tb_tunnel *tunnel;
+
+ tb_switch_discover_tunnels(tb->root_switch, &tcm->tunnel_list, true);
+
+ list_for_each_entry(tunnel, &tcm->tunnel_list, list) {
if (tb_tunnel_is_pci(tunnel)) {
struct tb_switch *parent = tunnel->dst_port->sw;
@@ -146,13 +164,6 @@ static void tb_discover_tunnels(struct tb_switch *sw)
pm_runtime_get_sync(&tunnel->src_port->sw->dev);
pm_runtime_get_sync(&tunnel->dst_port->sw->dev);
}
-
- list_add_tail(&tunnel->list, &tcm->tunnel_list);
- }
-
- tb_switch_for_each_port(sw, port) {
- if (tb_port_has_remote(port))
- tb_discover_tunnels(port->remote->sw);
}
}
@@ -1369,7 +1380,7 @@ static int tb_start(struct tb *tb)
/* Full scan to discover devices added before the driver was loaded. */
tb_scan_switch(tb->root_switch);
/* Find out tunnels created by the boot firmware */
- tb_discover_tunnels(tb->root_switch);
+ tb_discover_tunnels(tb);
/*
* If the boot firmware did not create USB 3.x tunnels create them
* now for the whole topology.
@@ -1429,6 +1440,8 @@ static int tb_resume_noirq(struct tb *tb)
{
struct tb_cm *tcm = tb_priv(tb);
struct tb_tunnel *tunnel, *n;
+ unsigned int usb3_delay = 0;
+ LIST_HEAD(tunnels);
tb_dbg(tb, "resuming...\n");
@@ -1439,8 +1452,31 @@ static int tb_resume_noirq(struct tb *tb)
tb_free_invalid_tunnels(tb);
tb_free_unplugged_children(tb->root_switch);
tb_restore_children(tb->root_switch);
- list_for_each_entry_safe(tunnel, n, &tcm->tunnel_list, list)
+
+ /*
+ * If we get here from suspend to disk the boot firmware or the
+ * restore kernel might have created tunnels of its own. Since
+ * we cannot be sure they are usable for us we find and tear
+ * them down.
+ */
+ tb_switch_discover_tunnels(tb->root_switch, &tunnels, false);
+ list_for_each_entry_safe_reverse(tunnel, n, &tunnels, list) {
+ if (tb_tunnel_is_usb3(tunnel))
+ usb3_delay = 500;
+ tb_tunnel_deactivate(tunnel);
+ tb_tunnel_free(tunnel);
+ }
+
+ /* Re-create our tunnels now */
+ list_for_each_entry_safe(tunnel, n, &tcm->tunnel_list, list) {
+ /* USB3 requires delay before it can be re-activated */
+ if (tb_tunnel_is_usb3(tunnel)) {
+ msleep(usb3_delay);
+ /* Only need to do it once */
+ usb3_delay = 0;
+ }
tb_tunnel_restart(tunnel);
+ }
if (!list_empty(&tcm->tunnel_list)) {
/*
* the pcie links need some time to get going.