Merge remote-tracking branch 'stable/linux-5.15.y' into rpi-5.15.y
[platform/kernel/linux-rpi.git] / drivers / thunderbolt / tb.c
index 2897a77..0c3e1d1 100644 (file)
@@ -105,10 +105,37 @@ static void tb_remove_dp_resources(struct tb_switch *sw)
        }
 }
 
-static void tb_discover_tunnels(struct tb_switch *sw)
+static void tb_discover_dp_resource(struct tb *tb, struct tb_port *port)
 {
-       struct tb *tb = sw->tb;
        struct tb_cm *tcm = tb_priv(tb);
+       struct tb_port *p;
+
+       list_for_each_entry(p, &tcm->dp_resources, list) {
+               if (p == port)
+                       return;
+       }
+
+       tb_port_dbg(port, "DP %s resource available discovered\n",
+                   tb_port_is_dpin(port) ? "IN" : "OUT");
+       list_add_tail(&port->list, &tcm->dp_resources);
+}
+
+static void tb_discover_dp_resources(struct tb *tb)
+{
+       struct tb_cm *tcm = tb_priv(tb);
+       struct tb_tunnel *tunnel;
+
+       list_for_each_entry(tunnel, &tcm->tunnel_list, list) {
+               if (tb_tunnel_is_dp(tunnel))
+                       tb_discover_dp_resource(tb, tunnel->dst_port);
+       }
+}
+
+static void tb_switch_discover_tunnels(struct tb_switch *sw,
+                                      struct list_head *list,
+                                      bool alloc_hopids)
+{
+       struct tb *tb = sw->tb;
        struct tb_port *port;
 
        tb_switch_for_each_port(sw, port) {
@@ -116,24 +143,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 +190,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);
        }
 }
 
@@ -851,7 +888,7 @@ static struct tb_port *tb_find_dp_out(struct tb *tb, struct tb_port *in)
 
 static void tb_tunnel_dp(struct tb *tb)
 {
-       int available_up, available_down, ret;
+       int available_up, available_down, ret, link_nr;
        struct tb_cm *tcm = tb_priv(tb);
        struct tb_port *port, *in, *out;
        struct tb_tunnel *tunnel;
@@ -897,6 +934,20 @@ static void tb_tunnel_dp(struct tb *tb)
        }
 
        /*
+        * This is only applicable to links that are not bonded (so
+        * when Thunderbolt 1 hardware is involved somewhere in the
+        * topology). For these try to share the DP bandwidth between
+        * the two lanes.
+        */
+       link_nr = 1;
+       list_for_each_entry(tunnel, &tcm->tunnel_list, list) {
+               if (tb_tunnel_is_dp(tunnel)) {
+                       link_nr = 0;
+                       break;
+               }
+       }
+
+       /*
         * DP stream needs the domain to be active so runtime resume
         * both ends of the tunnel.
         *
@@ -927,7 +978,8 @@ static void tb_tunnel_dp(struct tb *tb)
        tb_dbg(tb, "available bandwidth for new DP tunnel %u/%u Mb/s\n",
               available_up, available_down);
 
-       tunnel = tb_tunnel_alloc_dp(tb, in, out, available_up, available_down);
+       tunnel = tb_tunnel_alloc_dp(tb, in, out, link_nr, available_up,
+                                   available_down);
        if (!tunnel) {
                tb_port_dbg(out, "could not allocate DP tunnel\n");
                goto err_reclaim;
@@ -1369,7 +1421,9 @@ 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);
+       /* Add DP resources from the DP tunnels created by the boot firmware */
+       tb_discover_dp_resources(tb);
        /*
         * If the boot firmware did not create USB 3.x tunnels create them
         * now for the whole topology.
@@ -1429,6 +1483,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 +1495,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.