[PATCH 01/12] thunderbolt: Avoid reserved fields in path config space for USB4 routers

Mika Westerberg
2026-04-27T08:11:14Z

From: Gil Fine <gil.fine@linux.intel.com>

According to USB4 spec, USB4 Connection Manager shall not change value
of any fields that are defined as "RsvdZ" or "VD". Specifically fields:
Path Credits Allocated, IFC, ISE fields in path config space shall not
be written by CM. To handle this, CM shall first read current path
config space from the hardware, change only the fields that can be
changed, and then write back the path config space.

Signed-off-by: Gil Fine <gil.fine@linux.intel.com>
Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
---
drivers/thunderbolt/path.c | 31 ++++++++++++++++++++++---------
1 file changed, 22 insertions(+), 9 deletions(-)

diff --git a/drivers/thunderbolt/path.c b/drivers/thunderbolt/path.c
index 8713ea0f47c1..0092b2ec7873 100644
--- a/drivers/thunderbolt/path.c
+++ b/drivers/thunderbolt/path.c
@@ -412,7 +412,8 @@ static int __tb_path_deactivate_hop(struct tb_port *port, int hop_index,
* in the USB4 spec so we clear them
* only for pre-USB4 adapters.
*/
- if (!tb_switch_is_usb4(port->sw)) {
+ if (tb_port_is_null(port) ||
+ !tb_switch_is_usb4(port->sw)) {
hop.ingress_fc = 0;
hop.ingress_shared_buffer = 0;
}
@@ -532,15 +533,18 @@ int tb_path_activate(struct tb_path *path)
__tb_path_deactivate_hop(path->hops[i].in_port,
path->hops[i].in_hop_index, path->clear_fc);

- /* dword 0 */
+ /* Needed for USB4 routers, read path config space before write */
+ res = tb_port_read(path->hops[i].in_port, &hop, TB_CFG_HOPS,
+ 2 * path->hops[i].in_hop_index, 2);
+ if (res)
+ goto err;
+
hop.next_hop = path->hops[i].next_hop_index;
hop.out_port = path->hops[i].out_port->port;
- hop.initial_credits = path->hops[i].initial_credits;
hop.pmps = path->hops[i].pm_support;
hop.unknown1 = 0;
hop.enable = 1;

- /* dword 1 */
out_mask = (i == path->path_length - 1) ?
TB_PATH_DESTINATION : TB_PATH_INTERNAL;
in_mask = (i == 0) ? TB_PATH_SOURCE : TB_PATH_INTERNAL;
@@ -550,12 +554,21 @@ int tb_path_activate(struct tb_path *path)
hop.drop_packages = path->drop_packages;
hop.counter = path->hops[i].in_counter_index;
hop.counter_enable = path->hops[i].in_counter_index != -1;
- hop.ingress_fc = path->ingress_fc_enable & in_mask;
hop.egress_fc = path->egress_fc_enable & out_mask;
- hop.ingress_shared_buffer = path->ingress_shared_buffer
- & in_mask;
- hop.egress_shared_buffer = path->egress_shared_buffer
- & out_mask;
+ hop.egress_shared_buffer = path->egress_shared_buffer & out_mask;
+ /*
+ * Protocol adapters IFC and ISE bits, and Path Credits
+ * Allocated are vendor defined in the USB4 spec so we
+ * program them only for pre-USB4 and lane adapters.
+ */
+ if (tb_port_is_null(path->hops[i].in_port) ||
+ !tb_switch_is_usb4(path->hops[i].in_port->sw)) {
+ hop.initial_credits = path->hops[i].initial_credits;
+ hop.ingress_fc = path->ingress_fc_enable & in_mask;
+ hop.ingress_shared_buffer =
+ path->ingress_shared_buffer & in_mask;
+ }
+
hop.unknown3 = 0;

tb_port_dbg(path->hops[i].in_port, "Writing hop %d\n", i);
--
2.50.1