From 9a966517a83090ee3e26e9a93a92523e2358c5b3 Mon Sep 17 00:00:00 2001 From: Alex James Date: Thu, 4 Apr 2024 23:11:52 -0500 Subject: [PATCH 01/11] thunderbolt: Enable NVM upgrade support on Intel Maple Ridge Intel Maple Ridge supports NVM firmware upgrade with the same flows used on previous discrete Thunderbolt contollers from Intel (such as Titan Ridge). Advertise NVM upgrade support for Maple Ridge in icm_probe() to expose the corresponding files in /sys/bus/thunderbolt. The NVM firmware process was successfully tested on a system with a JHL8540 controller (ASUS ProArt Z790-CREATOR). Signed-off-by: Alex James Signed-off-by: Mika Westerberg --- drivers/thunderbolt/icm.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/thunderbolt/icm.c b/drivers/thunderbolt/icm.c index baf10d099c77..7859bccc592d 100644 --- a/drivers/thunderbolt/icm.c +++ b/drivers/thunderbolt/icm.c @@ -2532,6 +2532,7 @@ struct tb *icm_probe(struct tb_nhi *nhi) case PCI_DEVICE_ID_INTEL_MAPLE_RIDGE_2C_NHI: case PCI_DEVICE_ID_INTEL_MAPLE_RIDGE_4C_NHI: + icm->can_upgrade_nvm = true; icm->is_supported = icm_tgl_is_supported; icm->get_mode = icm_ar_get_mode; icm->driver_ready = icm_tr_driver_ready; From 6016137a964b67a6ac8afb9071ef6e83777afca6 Mon Sep 17 00:00:00 2001 From: Gil Fine Date: Mon, 12 Feb 2024 00:33:25 +0200 Subject: [PATCH 02/11] thunderbolt: Fix calculation of consumed USB3 bandwidth on a path Currently, when setup a new USB3 tunnel that is starting from downstream USB3 adapter of first depth router (or deeper), to upstream USB3 adapter of a second depth router (or deeper), we calculate consumed bandwidth. For this calculation we take into account first USB3 tunnel consumed bandwidth while we shouldn't, because we just recalculating the first USB3 tunnel allocated bandwidth. Fix that, so that more bandwidth is available for the new USB3 tunnel being setup. While there, fix the kernel-doc to decribe more accurately the purpose of the function. Signed-off-by: Gil Fine Signed-off-by: Mika Westerberg --- drivers/thunderbolt/tb.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/thunderbolt/tb.c b/drivers/thunderbolt/tb.c index c5ce7a694b27..ec8c4ef5e3ba 100644 --- a/drivers/thunderbolt/tb.c +++ b/drivers/thunderbolt/tb.c @@ -498,8 +498,9 @@ static struct tb_tunnel *tb_find_first_usb3_tunnel(struct tb *tb, * @consumed_down: Consumed downstream bandwidth (Mb/s) * * Calculates consumed USB3 and PCIe bandwidth at @port between path - * from @src_port to @dst_port. Does not take tunnel starting from - * @src_port and ending from @src_port into account. + * from @src_port to @dst_port. Does not take USB3 tunnel starting from + * @src_port and ending on @src_port into account because that bandwidth is + * already included in as part of the "first hop" USB3 tunnel. */ static int tb_consumed_usb3_pcie_bandwidth(struct tb *tb, struct tb_port *src_port, @@ -514,8 +515,8 @@ static int tb_consumed_usb3_pcie_bandwidth(struct tb *tb, *consumed_up = *consumed_down = 0; tunnel = tb_find_first_usb3_tunnel(tb, src_port, dst_port); - if (tunnel && tunnel->src_port != src_port && - tunnel->dst_port != dst_port) { + if (tunnel && !tb_port_is_usb3_down(src_port) && + !tb_port_is_usb3_up(dst_port)) { int ret; ret = tb_tunnel_consumed_bandwidth(tunnel, consumed_up, From 25d905d2b81973426b73ad943342dfda4e9a6fb5 Mon Sep 17 00:00:00 2001 From: Gil Fine Date: Mon, 12 Feb 2024 00:33:24 +0200 Subject: [PATCH 03/11] thunderbolt: Allow USB3 bandwidth to be lower than maximum supported Currently USB3 tunnel setup fails if USB4 link available bandwidth is too low to allow USB3 Maximum Supported Link Rate. In reality, this limitation is not needed, and may cause failure of USB3 tunnel establishment, if USB4 link available bandwidth is lower than USB3 Maximum Supported Link Rate. E.g. if we connect to USB4 v1 host router, a USB4 v1 device router, via 10 Gb/s cable. Hence, here we discard this limitation, and now we only limit USB3 bandwidth allocation to be not higher than 90% of USB3 Max Supported Link Rate (for first USB3 tunnel only). Signed-off-by: Gil Fine Signed-off-by: Mika Westerberg --- drivers/thunderbolt/tunnel.c | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/drivers/thunderbolt/tunnel.c b/drivers/thunderbolt/tunnel.c index cb6609a56a03..fdc5e8e12ca8 100644 --- a/drivers/thunderbolt/tunnel.c +++ b/drivers/thunderbolt/tunnel.c @@ -2064,26 +2064,21 @@ struct tb_tunnel *tb_tunnel_alloc_usb3(struct tb *tb, struct tb_port *up, { struct tb_tunnel *tunnel; struct tb_path *path; - int max_rate = 0; + int max_rate; - /* - * Check that we have enough bandwidth available for the new - * USB3 tunnel. - */ - if (max_up > 0 || max_down > 0) { + if (!tb_route(down->sw) && (max_up > 0 || max_down > 0)) { + /* + * For USB3 isochronous transfers, we allow bandwidth which is + * not higher than 90% of maximum supported bandwidth by USB3 + * adapters. + */ max_rate = tb_usb3_max_link_rate(down, up); if (max_rate < 0) return NULL; - /* Only 90% can be allocated for USB3 isochronous transfers */ max_rate = max_rate * 90 / 100; - tb_port_dbg(up, "required bandwidth for USB3 tunnel %d Mb/s\n", + tb_port_dbg(up, "maximum required bandwidth for USB3 tunnel %d Mb/s\n", max_rate); - - if (max_rate > max_up || max_rate > max_down) { - tb_port_warn(up, "not enough bandwidth for USB3 tunnel\n"); - return NULL; - } } tunnel = tb_tunnel_alloc(tb, 2, TB_TUNNEL_USB3); @@ -2115,8 +2110,8 @@ struct tb_tunnel *tb_tunnel_alloc_usb3(struct tb *tb, struct tb_port *up, tunnel->paths[TB_USB3_PATH_UP] = path; if (!tb_route(down->sw)) { - tunnel->allocated_up = max_rate; - tunnel->allocated_down = max_rate; + tunnel->allocated_up = min(max_rate, max_up); + tunnel->allocated_down = min(max_rate, max_down); tunnel->init = tb_usb3_init; tunnel->consumed_bandwidth = tb_usb3_consumed_bandwidth; From 668906cf88d5f0c6f124cf4d7d61c38693708681 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Wed, 10 Apr 2024 16:47:51 +0300 Subject: [PATCH 04/11] thunderbolt: Use correct error code with ERROR_NOT_SUPPORTED We check for -EOPNOTSUPP but tb_xdp_handle_error() translated it to -ENOTSUPP instead which is dealt as "transient" error and retried after a while. Fix this so that we bail out early when the other side clearly tells us it is does not support this. Signed-off-by: Mika Westerberg --- drivers/thunderbolt/xdomain.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/thunderbolt/xdomain.c b/drivers/thunderbolt/xdomain.c index 940ae97987ff..11a50c86a1e4 100644 --- a/drivers/thunderbolt/xdomain.c +++ b/drivers/thunderbolt/xdomain.c @@ -250,7 +250,7 @@ static int tb_xdp_handle_error(const struct tb_xdp_error_response *res) case ERROR_UNKNOWN_DOMAIN: return -EIO; case ERROR_NOT_SUPPORTED: - return -ENOTSUPP; + return -EOPNOTSUPP; case ERROR_NOT_READY: return -EAGAIN; default: From c936e287df26929ad6b5fd7fef22b356e434e90e Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Mon, 12 Feb 2024 10:48:34 +0200 Subject: [PATCH 05/11] thunderbolt: Get rid of TB_CFG_PKG_PREPARE_TO_SLEEP This is not used anywhere in the driver so remove it. No functional changes. Signed-off-by: Mika Westerberg --- drivers/thunderbolt/tb_msgs.h | 6 ------ include/linux/thunderbolt.h | 1 - 2 files changed, 7 deletions(-) diff --git a/drivers/thunderbolt/tb_msgs.h b/drivers/thunderbolt/tb_msgs.h index cd750e4b3440..a1670a96cbdc 100644 --- a/drivers/thunderbolt/tb_msgs.h +++ b/drivers/thunderbolt/tb_msgs.h @@ -98,12 +98,6 @@ struct cfg_reset_pkg { struct tb_cfg_header header; } __packed; -/* TB_CFG_PKG_PREPARE_TO_SLEEP */ -struct cfg_pts_pkg { - struct tb_cfg_header header; - u32 data; -} __packed; - /* ICM messages */ enum icm_pkg_code { diff --git a/include/linux/thunderbolt.h b/include/linux/thunderbolt.h index 4338ea9ac4fd..7d902d8c054b 100644 --- a/include/linux/thunderbolt.h +++ b/include/linux/thunderbolt.h @@ -33,7 +33,6 @@ enum tb_cfg_pkg_type { TB_CFG_PKG_ICM_EVENT = 10, TB_CFG_PKG_ICM_CMD = 11, TB_CFG_PKG_ICM_RESP = 12, - TB_CFG_PKG_PREPARE_TO_SLEEP = 13, }; /** From c6ca1ac9f4722c6aa940d61baa2416624472b7f0 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Fri, 5 Apr 2024 07:50:50 +0300 Subject: [PATCH 06/11] thunderbolt: Increase sideband access polling delay The USB4 sideband access is slow compared to the high-speed link and the access timing parameters are tens of milliseconds according the spec. To avoid too much unnecessary polling for the sideband pass the wait delay to usb4_port_wait_for_bit() and use larger (5ms) value compared to the high-speed access. Signed-off-by: Mika Westerberg --- drivers/thunderbolt/usb4.c | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/drivers/thunderbolt/usb4.c b/drivers/thunderbolt/usb4.c index 9860b49d7a2b..cc62587736e5 100644 --- a/drivers/thunderbolt/usb4.c +++ b/drivers/thunderbolt/usb4.c @@ -52,6 +52,10 @@ enum usb4_ba_index { #define USB4_BA_VALUE_MASK GENMASK(31, 16) #define USB4_BA_VALUE_SHIFT 16 +/* Delays in us used with usb4_port_wait_for_bit() */ +#define USB4_PORT_DELAY 50 +#define USB4_PORT_SB_DELAY 5000 + static int usb4_native_switch_op(struct tb_switch *sw, u16 opcode, u32 *metadata, u8 *status, const void *tx_data, size_t tx_dwords, @@ -1244,7 +1248,7 @@ void usb4_port_unconfigure_xdomain(struct tb_port *port) } static int usb4_port_wait_for_bit(struct tb_port *port, u32 offset, u32 bit, - u32 value, int timeout_msec) + u32 value, int timeout_msec, unsigned long delay_usec) { ktime_t timeout = ktime_add_ms(ktime_get(), timeout_msec); @@ -1259,7 +1263,7 @@ static int usb4_port_wait_for_bit(struct tb_port *port, u32 offset, u32 bit, if ((val & bit) == value) return 0; - usleep_range(50, 100); + fsleep(delay_usec); } while (ktime_before(ktime_get(), timeout)); return -ETIMEDOUT; @@ -1307,7 +1311,7 @@ static int usb4_port_sb_read(struct tb_port *port, enum usb4_sb_target target, return ret; ret = usb4_port_wait_for_bit(port, port->cap_usb4 + PORT_CS_1, - PORT_CS_1_PND, 0, 500); + PORT_CS_1_PND, 0, 500, USB4_PORT_SB_DELAY); if (ret) return ret; @@ -1354,7 +1358,7 @@ static int usb4_port_sb_write(struct tb_port *port, enum usb4_sb_target target, return ret; ret = usb4_port_wait_for_bit(port, port->cap_usb4 + PORT_CS_1, - PORT_CS_1_PND, 0, 500); + PORT_CS_1_PND, 0, 500, USB4_PORT_SB_DELAY); if (ret) return ret; @@ -1409,6 +1413,8 @@ static int usb4_port_sb_op(struct tb_port *port, enum usb4_sb_target target, if (val != opcode) return usb4_port_sb_opcode_err_to_errno(val); + + fsleep(USB4_PORT_SB_DELAY); } while (ktime_before(ktime_get(), timeout)); return -ETIMEDOUT; @@ -1590,13 +1596,14 @@ int usb4_port_asym_start(struct tb_port *port) * port started the symmetry transition. */ ret = usb4_port_wait_for_bit(port, port->cap_usb4 + PORT_CS_19, - PORT_CS_19_START_ASYM, 0, 1000); + PORT_CS_19_START_ASYM, 0, 1000, + USB4_PORT_DELAY); if (ret) return ret; /* Then wait for the transtion to be completed */ return usb4_port_wait_for_bit(port, port->cap_usb4 + PORT_CS_18, - PORT_CS_18_TIP, 0, 5000); + PORT_CS_18_TIP, 0, 5000, USB4_PORT_DELAY); } /** @@ -2122,7 +2129,8 @@ static int usb4_usb3_port_cm_request(struct tb_port *port, bool request) */ val &= ADP_USB3_CS_2_CMR; return usb4_port_wait_for_bit(port, port->cap_adap + ADP_USB3_CS_1, - ADP_USB3_CS_1_HCA, val, 1500); + ADP_USB3_CS_1_HCA, val, 1500, + USB4_PORT_DELAY); } static inline int usb4_usb3_port_set_cm_request(struct tb_port *port) From d4d336f8c4d5a4609217ac83116ba3d06b7e918c Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Fri, 5 Apr 2024 07:47:40 +0300 Subject: [PATCH 07/11] thunderbolt: No need to loop over all retimers if access fails When we read the NVM authentication status or unsetting the inbound SBTX there is no point to continue the loop after first access to a retimer fails because there won't be any more retimers after this anyway so bail out from the loops early. Signed-off-by: Mika Westerberg --- drivers/thunderbolt/retimer.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/drivers/thunderbolt/retimer.c b/drivers/thunderbolt/retimer.c index 6bb49bdcd6c1..6eaaa5074ce8 100644 --- a/drivers/thunderbolt/retimer.c +++ b/drivers/thunderbolt/retimer.c @@ -199,8 +199,10 @@ static void tb_retimer_nvm_authenticate_status(struct tb_port *port, u32 *status * If the retimer has it set, store it for the new retimer * device instance. */ - for (i = 1; i <= TB_MAX_RETIMER_INDEX; i++) - usb4_port_retimer_nvm_authenticate_status(port, i, &status[i]); + for (i = 1; i <= TB_MAX_RETIMER_INDEX; i++) { + if (usb4_port_retimer_nvm_authenticate_status(port, i, &status[i])) + break; + } } static void tb_retimer_set_inbound_sbtx(struct tb_port *port) @@ -234,8 +236,10 @@ static void tb_retimer_unset_inbound_sbtx(struct tb_port *port) tb_port_dbg(port, "disabling sideband transactions\n"); - for (i = TB_MAX_RETIMER_INDEX; i >= 1; i--) - usb4_port_retimer_unset_inbound_sbtx(port, i); + for (i = TB_MAX_RETIMER_INDEX; i >= 1; i--) { + if (usb4_port_retimer_unset_inbound_sbtx(port, i)) + break; + } } static ssize_t nvm_authenticate_store(struct device *dev, From a3ad3a90e0a722d9a50c01cfb40e6cfbb975e529 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Fri, 19 Apr 2024 08:17:58 +0300 Subject: [PATCH 08/11] thunderbolt: There are only 5 basic router registers in pre-USB4 routers Intel pre-USB4 routers only have ROUTER_CS_0 up to ROUTER_CS_4 and it immediately follows the TMU router registers. Correct this accordingly. Reported-by: Rajaram Regupathy Signed-off-by: Mika Westerberg --- drivers/thunderbolt/debugfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/thunderbolt/debugfs.c b/drivers/thunderbolt/debugfs.c index e324cd899719..193e9dfc983b 100644 --- a/drivers/thunderbolt/debugfs.c +++ b/drivers/thunderbolt/debugfs.c @@ -1346,7 +1346,7 @@ static int switch_basic_regs_show(struct tb_switch *sw, struct seq_file *s) if (tb_switch_is_usb4(sw)) dwords = ARRAY_SIZE(data); else - dwords = 7; + dwords = 5; ret = tb_sw_read(sw, data, TB_CFG_SWITCH, 0, dwords); if (ret) From 61684c0ff94ca356ef82220173860223908f1e04 Mon Sep 17 00:00:00 2001 From: Gil Fine Date: Fri, 26 Apr 2024 02:24:17 +0300 Subject: [PATCH 09/11] thunderbolt: Fix uninitialized variable in tb_tunnel_alloc_usb3() Currently in case of no bandwidth available for USB3 tunnel, we are left with uninitialized variable that can lead to huge negative allocated bandwidth. Fix this by initializing the variable to zero. While there, fix the kernel-doc to describe more accurately the purpose of the function tb_tunnel_alloc_usb3(). Reported-by: Dan Carpenter Closes: https://lore.kernel.org/linux-usb/6289898b-cd63-4fb8-906a-1b6977321af9@moroto.mountain/ Fixes: 25d905d2b819 ("thunderbolt: Allow USB3 bandwidth to be lower than maximum supported") Signed-off-by: Gil Fine Signed-off-by: Mika Westerberg --- drivers/thunderbolt/tunnel.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/thunderbolt/tunnel.c b/drivers/thunderbolt/tunnel.c index fdc5e8e12ca8..1a3b197001da 100644 --- a/drivers/thunderbolt/tunnel.c +++ b/drivers/thunderbolt/tunnel.c @@ -2048,10 +2048,10 @@ err_free: * @tb: Pointer to the domain structure * @up: USB3 upstream adapter port * @down: USB3 downstream adapter port - * @max_up: Maximum available upstream bandwidth for the USB3 tunnel (%0 - * if not limited). - * @max_down: Maximum available downstream bandwidth for the USB3 tunnel - * (%0 if not limited). + * @max_up: Maximum available upstream bandwidth for the USB3 tunnel. + * %0 if no available bandwidth. + * @max_down: Maximum available downstream bandwidth for the USB3 tunnel. + * %0 if no available bandwidth. * * Allocate an USB3 tunnel. The ports must be of type @TB_TYPE_USB3_UP and * @TB_TYPE_USB3_DOWN. @@ -2064,7 +2064,7 @@ struct tb_tunnel *tb_tunnel_alloc_usb3(struct tb *tb, struct tb_port *up, { struct tb_tunnel *tunnel; struct tb_path *path; - int max_rate; + int max_rate = 0; if (!tb_route(down->sw) && (max_up > 0 || max_down > 0)) { /* From 2a0ed2da17d70fb57456fd78bf0798492d44cc17 Mon Sep 17 00:00:00 2001 From: Gil Fine Date: Fri, 26 Apr 2024 02:37:54 +0300 Subject: [PATCH 10/11] thunderbolt: Fix kernel-doc for tb_tunnel_alloc_dp() In case of no bandwidth available for DP tunnel, the function get the arguments @max_up and @max_down set to zero. Fix the kernel-doc to describe more accurately the purpose of the function. No functional changes. Signed-off-by: Gil Fine Signed-off-by: Mika Westerberg --- drivers/thunderbolt/tunnel.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/thunderbolt/tunnel.c b/drivers/thunderbolt/tunnel.c index 1a3b197001da..41cf6378ad25 100644 --- a/drivers/thunderbolt/tunnel.c +++ b/drivers/thunderbolt/tunnel.c @@ -1435,10 +1435,10 @@ err_free: * @in: DP in adapter port * @out: DP out adapter port * @link_nr: Preferred lane adapter when the link is not bonded - * @max_up: Maximum available upstream bandwidth for the DP tunnel (%0 - * if not limited) - * @max_down: Maximum available downstream bandwidth for the DP tunnel - * (%0 if not limited) + * @max_up: Maximum available upstream bandwidth for the DP tunnel. + * %0 if no available bandwidth. + * @max_down: Maximum available downstream bandwidth for the DP tunnel. + * %0 if no available bandwidth. * * Allocates a tunnel between @in and @out that is capable of tunneling * Display Port traffic. From a3dc6d82de9bd88871dbc4ac511409e69ecacbfb Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Fri, 26 Apr 2024 07:58:58 +0300 Subject: [PATCH 11/11] thunderbolt: Correct trace output of firmware connection manager packets These are special packets that the drivers sends directly to the firmware connection manager (ICM). These do not have route string because they are always consumed by the firmware connection manager running on the host router, so hard-code that in the output accordingly. Signed-off-by: Mika Westerberg --- drivers/thunderbolt/trace.h | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/drivers/thunderbolt/trace.h b/drivers/thunderbolt/trace.h index 4dccfcf7af6a..6d0776514d12 100644 --- a/drivers/thunderbolt/trace.h +++ b/drivers/thunderbolt/trace.h @@ -87,23 +87,32 @@ static inline const char *show_data(struct trace_seq *p, u8 type, const char *prefix = ""; int i; - show_route(p, data); - switch (type) { case TB_CFG_PKG_READ: case TB_CFG_PKG_WRITE: + show_route(p, data); show_data_read_write(p, data); break; case TB_CFG_PKG_ERROR: + show_route(p, data); show_data_error(p, data); break; case TB_CFG_PKG_EVENT: + show_route(p, data); show_data_event(p, data); break; + case TB_CFG_PKG_ICM_EVENT: + case TB_CFG_PKG_ICM_CMD: + case TB_CFG_PKG_ICM_RESP: + /* ICM messages always target the host router */ + trace_seq_puts(p, "route=0, "); + break; + default: + show_route(p, data); break; }