thunderbolt: Do not tunnel USB3 if link is not USB4
authorMika Westerberg <mika.westerberg@linux.intel.com>
Wed, 4 Mar 2020 15:09:14 +0000 (17:09 +0200)
committerMika Westerberg <mika.westerberg@linux.intel.com>
Mon, 22 Jun 2020 16:58:20 +0000 (19:58 +0300)
USB3 tunneling is possible only over USB4 link so don't create USB3
tunnels if that's not the case.

Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
drivers/thunderbolt/tb.c
drivers/thunderbolt/tb.h
drivers/thunderbolt/tb_regs.h
drivers/thunderbolt/usb4.c

index 55daa7f..2da8225 100644 (file)
@@ -235,6 +235,9 @@ static int tb_tunnel_usb3(struct tb *tb, struct tb_switch *sw)
        if (!up)
                return 0;
 
+       if (!sw->link_usb4)
+               return 0;
+
        /*
         * Look up available down port. Since we are chaining it should
         * be found right above this switch.
index b53ef5b..de81249 100644 (file)
@@ -97,6 +97,7 @@ struct tb_switch_tmu {
  * @device_name: Name of the device (or %NULL if not known)
  * @link_speed: Speed of the link in Gb/s
  * @link_width: Width of the link (1 or 2)
+ * @link_usb4: Upstream link is USB4
  * @generation: Switch Thunderbolt generation
  * @cap_plug_events: Offset to the plug events capability (%0 if not found)
  * @cap_lc: Offset to the link controller capability (%0 if not found)
@@ -136,6 +137,7 @@ struct tb_switch {
        const char *device_name;
        unsigned int link_speed;
        unsigned int link_width;
+       bool link_usb4;
        unsigned int generation;
        int cap_plug_events;
        int cap_lc;
index c29c507..77d4b85 100644 (file)
@@ -290,6 +290,7 @@ struct tb_regs_port_header {
 /* USB4 port registers */
 #define PORT_CS_18                             0x12
 #define PORT_CS_18_BE                          BIT(8)
+#define PORT_CS_18_TCM                         BIT(9)
 #define PORT_CS_19                             0x13
 #define PORT_CS_19_PC                          BIT(3)
 
index 50c7534..393771d 100644 (file)
@@ -192,6 +192,20 @@ static int usb4_switch_op(struct tb_switch *sw, u16 opcode, u8 *status)
        return 0;
 }
 
+static bool link_is_usb4(struct tb_port *port)
+{
+       u32 val;
+
+       if (!port->cap_usb4)
+               return false;
+
+       if (tb_port_read(port, &val, TB_CFG_PORT,
+                        port->cap_usb4 + PORT_CS_18, 1))
+               return false;
+
+       return !(val & PORT_CS_18_TCM);
+}
+
 /**
  * usb4_switch_setup() - Additional setup for USB4 device
  * @sw: USB4 router to setup
@@ -205,6 +219,7 @@ static int usb4_switch_op(struct tb_switch *sw, u16 opcode, u8 *status)
  */
 int usb4_switch_setup(struct tb_switch *sw)
 {
+       struct tb_port *downstream_port;
        struct tb_switch *parent;
        bool tbt3, xhci;
        u32 val = 0;
@@ -217,6 +232,11 @@ int usb4_switch_setup(struct tb_switch *sw)
        if (ret)
                return ret;
 
+       parent = tb_switch_parent(sw);
+       downstream_port = tb_port_at(tb_route(sw), parent);
+       sw->link_usb4 = link_is_usb4(downstream_port);
+       tb_sw_dbg(sw, "link: %s\n", sw->link_usb4 ? "USB4" : "TBT3");
+
        xhci = val & ROUTER_CS_6_HCI;
        tbt3 = !(val & ROUTER_CS_6_TNS);
 
@@ -227,9 +247,7 @@ int usb4_switch_setup(struct tb_switch *sw)
        if (ret)
                return ret;
 
-       parent = tb_switch_parent(sw);
-
-       if (tb_switch_find_port(parent, TB_TYPE_USB3_DOWN)) {
+       if (sw->link_usb4 && tb_switch_find_port(parent, TB_TYPE_USB3_DOWN)) {
                val |= ROUTER_CS_5_UTO;
                xhci = false;
        }