thunderbolt: Handle lane bonding of Gen 4 XDomain links properly
authorGil Fine <gil.fine@linux.intel.com>
Wed, 15 Nov 2023 13:01:36 +0000 (15:01 +0200)
committerMika Westerberg <mika.westerberg@linux.intel.com>
Thu, 14 Dec 2023 06:07:44 +0000 (08:07 +0200)
Gen 4 links come up as bonded already so we are not supposed to initiate
lane bonding on them. However, we should still update the port
structures accordingly. Split these into their own functions to make it
easier to follow.

Signed-off-by: Gil Fine <gil.fine@linux.intel.com>
Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
drivers/thunderbolt/tb.c
drivers/thunderbolt/xdomain.c

index 5acdeb766860da51f912677a37c99c6b0166d922..6ff3e9624301ca76635da4234f51ae06044b787a 100644 (file)
@@ -503,8 +503,6 @@ static void tb_port_unconfigure_xdomain(struct tb_port *port)
                usb4_port_unconfigure_xdomain(port);
        else
                tb_lc_unconfigure_xdomain(port);
-
-       tb_port_enable(port->dual_link_port);
 }
 
 static void tb_scan_xdomain(struct tb_port *port)
index 9803f0bbf20d14fe07efc408b99a9e7a5e6cfef8..0a885ee5788d5445118f1cf7d76e0c72c13baaa7 100644 (file)
@@ -1895,6 +1895,50 @@ struct device_type tb_xdomain_type = {
 };
 EXPORT_SYMBOL_GPL(tb_xdomain_type);
 
+static void tb_xdomain_link_init(struct tb_xdomain *xd, struct tb_port *down)
+{
+       if (!down->dual_link_port)
+               return;
+
+       /*
+        * Gen 4 links come up already as bonded so only update the port
+        * structures here.
+        */
+       if (tb_port_get_link_generation(down) >= 4) {
+               down->bonded = true;
+               down->dual_link_port->bonded = true;
+       } else {
+               xd->bonding_possible = true;
+       }
+}
+
+static void tb_xdomain_link_exit(struct tb_xdomain *xd)
+{
+       struct tb_port *down = tb_xdomain_downstream_port(xd);
+
+       if (!down->dual_link_port)
+               return;
+
+       if (tb_port_get_link_generation(down) >= 4) {
+               down->bonded = false;
+               down->dual_link_port->bonded = false;
+       } else if (xd->link_width > TB_LINK_WIDTH_SINGLE) {
+               /*
+                * Just return port structures back to way they were and
+                * update credits. No need to update userspace because
+                * the XDomain is removed soon anyway.
+                */
+               tb_port_lane_bonding_disable(down);
+               tb_port_update_credits(down);
+       } else if (down->dual_link_port) {
+               /*
+                * Re-enable the lane 1 adapter we disabled at the end
+                * of tb_xdomain_get_properties().
+                */
+               tb_port_enable(down->dual_link_port);
+       }
+}
+
 /**
  * tb_xdomain_alloc() - Allocate new XDomain object
  * @tb: Domain where the XDomain belongs
@@ -1945,7 +1989,8 @@ struct tb_xdomain *tb_xdomain_alloc(struct tb *tb, struct device *parent,
                        goto err_free_local_uuid;
        } else {
                xd->needs_uuid = true;
-               xd->bonding_possible = !!down->dual_link_port;
+
+               tb_xdomain_link_init(xd, down);
        }
 
        device_initialize(&xd->dev);
@@ -2014,6 +2059,8 @@ void tb_xdomain_remove(struct tb_xdomain *xd)
 
        device_for_each_child_reverse(&xd->dev, xd, unregister_service);
 
+       tb_xdomain_link_exit(xd);
+
        /*
         * Undo runtime PM here explicitly because it is possible that
         * the XDomain was never added to the bus and thus device_del()