thunderbolt: Use scale field when allocating USB3 bandwidth
authorMika Westerberg <mika.westerberg@linux.intel.com>
Tue, 27 Dec 2022 09:55:26 +0000 (11:55 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 30 Mar 2023 10:49:14 +0000 (12:49 +0200)
commit c82510b1d87bdebfe916048857d2ef46f1778aa5 upstream.

When tunneling aggregated USB3 (20 Gb/s) the bandwidth values that are
programmed to the ADP_USB3_CS_2 go higher than 4096 and that does not
fit anymore to the 12-bit field. Fix this by scaling the value using
the scale field accordingly.

Fixes: 3b1d8d577ca8 ("thunderbolt: Implement USB3 bandwidth negotiation routines")
Cc: stable@vger.kernel.org
Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/thunderbolt/usb4.c

index f986854..a7da2d4 100644 (file)
@@ -2050,18 +2050,30 @@ static int usb4_usb3_port_write_allocated_bandwidth(struct tb_port *port,
                                                    int downstream_bw)
 {
        u32 val, ubw, dbw, scale;
-       int ret;
+       int ret, max_bw;
 
-       /* Read the used scale, hardware default is 0 */
-       ret = tb_port_read(port, &scale, TB_CFG_PORT,
-                          port->cap_adap + ADP_USB3_CS_3, 1);
+       /* Figure out suitable scale */
+       scale = 0;
+       max_bw = max(upstream_bw, downstream_bw);
+       while (scale < 64) {
+               if (mbps_to_usb3_bw(max_bw, scale) < 4096)
+                       break;
+               scale++;
+       }
+
+       if (WARN_ON(scale >= 64))
+               return -EINVAL;
+
+       ret = tb_port_write(port, &scale, TB_CFG_PORT,
+                           port->cap_adap + ADP_USB3_CS_3, 1);
        if (ret)
                return ret;
 
-       scale &= ADP_USB3_CS_3_SCALE_MASK;
        ubw = mbps_to_usb3_bw(upstream_bw, scale);
        dbw = mbps_to_usb3_bw(downstream_bw, scale);
 
+       tb_port_dbg(port, "scaled bandwidth %u/%u, scale %u\n", ubw, dbw, scale);
+
        ret = tb_port_read(port, &val, TB_CFG_PORT,
                           port->cap_adap + ADP_USB3_CS_2, 1);
        if (ret)