summaryrefslogtreecommitdiffstats
path: root/drivers/thunderbolt
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/thunderbolt')
-rw-r--r--drivers/thunderbolt/acpi.c4
-rw-r--r--drivers/thunderbolt/eeprom.c41
-rw-r--r--drivers/thunderbolt/lc.c110
-rw-r--r--drivers/thunderbolt/nhi.c3
-rw-r--r--drivers/thunderbolt/switch.c81
-rw-r--r--drivers/thunderbolt/tb.c11
-rw-r--r--drivers/thunderbolt/tb.h8
-rw-r--r--drivers/thunderbolt/tb_regs.h37
-rw-r--r--drivers/thunderbolt/tunnel.c10
9 files changed, 265 insertions, 40 deletions
diff --git a/drivers/thunderbolt/acpi.c b/drivers/thunderbolt/acpi.c
index 79b5abf9d042..c89daac0ad8c 100644
--- a/drivers/thunderbolt/acpi.c
+++ b/drivers/thunderbolt/acpi.c
@@ -14,15 +14,15 @@
static acpi_status tb_acpi_add_link(acpi_handle handle, u32 level, void *data,
void **return_value)
{
+ struct acpi_device *adev = acpi_fetch_acpi_dev(handle);
struct fwnode_reference_args args;
struct fwnode_handle *fwnode;
struct tb_nhi *nhi = data;
- struct acpi_device *adev;
struct pci_dev *pdev;
struct device *dev;
int ret;
- if (acpi_bus_get_device(handle, &adev))
+ if (!adev)
return AE_OK;
fwnode = acpi_fwnode_handle(adev);
diff --git a/drivers/thunderbolt/eeprom.c b/drivers/thunderbolt/eeprom.c
index 470885e6f1c8..c90d22f56d4e 100644
--- a/drivers/thunderbolt/eeprom.c
+++ b/drivers/thunderbolt/eeprom.c
@@ -17,7 +17,7 @@
*/
static int tb_eeprom_ctl_write(struct tb_switch *sw, struct tb_eeprom_ctl *ctl)
{
- return tb_sw_write(sw, ctl, TB_CFG_SWITCH, sw->cap_plug_events + 4, 1);
+ return tb_sw_write(sw, ctl, TB_CFG_SWITCH, sw->cap_plug_events + ROUTER_CS_4, 1);
}
/*
@@ -25,7 +25,7 @@ static int tb_eeprom_ctl_write(struct tb_switch *sw, struct tb_eeprom_ctl *ctl)
*/
static int tb_eeprom_ctl_read(struct tb_switch *sw, struct tb_eeprom_ctl *ctl)
{
- return tb_sw_read(sw, ctl, TB_CFG_SWITCH, sw->cap_plug_events + 4, 1);
+ return tb_sw_read(sw, ctl, TB_CFG_SWITCH, sw->cap_plug_events + ROUTER_CS_4, 1);
}
enum tb_eeprom_transfer {
@@ -46,18 +46,18 @@ static int tb_eeprom_active(struct tb_switch *sw, bool enable)
if (res)
return res;
if (enable) {
- ctl.access_high = 1;
+ ctl.bit_banging_enable = 1;
res = tb_eeprom_ctl_write(sw, &ctl);
if (res)
return res;
- ctl.access_low = 0;
+ ctl.fl_cs = 0;
return tb_eeprom_ctl_write(sw, &ctl);
} else {
- ctl.access_low = 1;
+ ctl.fl_cs = 1;
res = tb_eeprom_ctl_write(sw, &ctl);
if (res)
return res;
- ctl.access_high = 0;
+ ctl.bit_banging_enable = 0;
return tb_eeprom_ctl_write(sw, &ctl);
}
}
@@ -65,8 +65,8 @@ static int tb_eeprom_active(struct tb_switch *sw, bool enable)
/*
* tb_eeprom_transfer - transfer one bit
*
- * If TB_EEPROM_IN is passed, then the bit can be retrieved from ctl->data_in.
- * If TB_EEPROM_OUT is passed, then ctl->data_out will be written.
+ * If TB_EEPROM_IN is passed, then the bit can be retrieved from ctl->fl_do.
+ * If TB_EEPROM_OUT is passed, then ctl->fl_di will be written.
*/
static int tb_eeprom_transfer(struct tb_switch *sw, struct tb_eeprom_ctl *ctl,
enum tb_eeprom_transfer direction)
@@ -77,7 +77,7 @@ static int tb_eeprom_transfer(struct tb_switch *sw, struct tb_eeprom_ctl *ctl,
if (res)
return res;
}
- ctl->clock = 1;
+ ctl->fl_sk = 1;
res = tb_eeprom_ctl_write(sw, ctl);
if (res)
return res;
@@ -86,7 +86,7 @@ static int tb_eeprom_transfer(struct tb_switch *sw, struct tb_eeprom_ctl *ctl,
if (res)
return res;
}
- ctl->clock = 0;
+ ctl->fl_sk = 0;
return tb_eeprom_ctl_write(sw, ctl);
}
@@ -101,7 +101,7 @@ static int tb_eeprom_out(struct tb_switch *sw, u8 val)
if (res)
return res;
for (i = 0; i < 8; i++) {
- ctl.data_out = val & 0x80;
+ ctl.fl_di = val & 0x80;
res = tb_eeprom_transfer(sw, &ctl, TB_EEPROM_OUT);
if (res)
return res;
@@ -126,7 +126,7 @@ static int tb_eeprom_in(struct tb_switch *sw, u8 *val)
res = tb_eeprom_transfer(sw, &ctl, TB_EEPROM_IN);
if (res)
return res;
- *val |= ctl.data_in;
+ *val |= ctl.fl_do;
}
return 0;
}
@@ -553,9 +553,9 @@ static int tb_drom_parse(struct tb_switch *sw)
crc = tb_crc8((u8 *) &header->uid, 8);
if (crc != header->uid_crc8) {
tb_sw_warn(sw,
- "DROM UID CRC8 mismatch (expected: %#x, got: %#x), aborting\n",
+ "DROM UID CRC8 mismatch (expected: %#x, got: %#x)\n",
header->uid_crc8, crc);
- return -EINVAL;
+ return -EILSEQ;
}
if (!sw->uid)
sw->uid = header->uid;
@@ -654,6 +654,7 @@ int tb_drom_read(struct tb_switch *sw)
sw->drom = kzalloc(size, GFP_KERNEL);
if (!sw->drom)
return -ENOMEM;
+read:
res = tb_drom_read_n(sw, 0, sw->drom, size);
if (res)
goto err;
@@ -662,7 +663,11 @@ parse:
header = (void *) sw->drom;
if (header->data_len + TB_DROM_DATA_START != size) {
- tb_sw_warn(sw, "drom size mismatch, aborting\n");
+ tb_sw_warn(sw, "drom size mismatch\n");
+ if (retries--) {
+ msleep(100);
+ goto read;
+ }
goto err;
}
@@ -683,11 +688,9 @@ parse:
/* If the DROM parsing fails, wait a moment and retry once */
if (res == -EILSEQ && retries--) {
- tb_sw_warn(sw, "parsing DROM failed, retrying\n");
+ tb_sw_warn(sw, "parsing DROM failed\n");
msleep(100);
- res = tb_drom_read_n(sw, 0, sw->drom, size);
- if (!res)
- goto parse;
+ goto read;
}
if (!res)
diff --git a/drivers/thunderbolt/lc.c b/drivers/thunderbolt/lc.c
index 53495a38b4eb..633970fbe9b0 100644
--- a/drivers/thunderbolt/lc.c
+++ b/drivers/thunderbolt/lc.c
@@ -217,6 +217,116 @@ bool tb_lc_is_clx_supported(struct tb_port *port)
return !!(val & TB_LC_LINK_ATTR_CPS);
}
+/**
+ * tb_lc_is_usb_plugged() - Is there USB device connected to port
+ * @port: Device router lane 0 adapter
+ *
+ * Returns true if the @port has USB type-C device connected.
+ */
+bool tb_lc_is_usb_plugged(struct tb_port *port)
+{
+ struct tb_switch *sw = port->sw;
+ int cap, ret;
+ u32 val;
+
+ if (sw->generation != 3)
+ return false;
+
+ cap = find_port_lc_cap(port);
+ if (cap < 0)
+ return false;
+
+ ret = tb_sw_read(sw, &val, TB_CFG_SWITCH, cap + TB_LC_CS_42, 1);
+ if (ret)
+ return false;
+
+ return !!(val & TB_LC_CS_42_USB_PLUGGED);
+}
+
+/**
+ * tb_lc_is_xhci_connected() - Is the internal xHCI connected
+ * @port: Device router lane 0 adapter
+ *
+ * Returns true if the internal xHCI has been connected to @port.
+ */
+bool tb_lc_is_xhci_connected(struct tb_port *port)
+{
+ struct tb_switch *sw = port->sw;
+ int cap, ret;
+ u32 val;
+
+ if (sw->generation != 3)
+ return false;
+
+ cap = find_port_lc_cap(port);
+ if (cap < 0)
+ return false;
+
+ ret = tb_sw_read(sw, &val, TB_CFG_SWITCH, cap + TB_LC_LINK_REQ, 1);
+ if (ret)
+ return false;
+
+ return !!(val & TB_LC_LINK_REQ_XHCI_CONNECT);
+}
+
+static int __tb_lc_xhci_connect(struct tb_port *port, bool connect)
+{
+ struct tb_switch *sw = port->sw;
+ int cap, ret;
+ u32 val;
+
+ if (sw->generation != 3)
+ return -EINVAL;
+
+ cap = find_port_lc_cap(port);
+ if (cap < 0)
+ return cap;
+
+ ret = tb_sw_read(sw, &val, TB_CFG_SWITCH, cap + TB_LC_LINK_REQ, 1);
+ if (ret)
+ return ret;
+
+ if (connect)
+ val |= TB_LC_LINK_REQ_XHCI_CONNECT;
+ else
+ val &= ~TB_LC_LINK_REQ_XHCI_CONNECT;
+
+ return tb_sw_write(sw, &val, TB_CFG_SWITCH, cap + TB_LC_LINK_REQ, 1);
+}
+
+/**
+ * tb_lc_xhci_connect() - Connect internal xHCI
+ * @port: Device router lane 0 adapter
+ *
+ * Tells LC to connect the internal xHCI to @port. Returns %0 on success
+ * and negative errno in case of failure. Can be called for Thunderbolt 3
+ * routers only.
+ */
+int tb_lc_xhci_connect(struct tb_port *port)
+{
+ int ret;
+
+ ret = __tb_lc_xhci_connect(port, true);
+ if (ret)
+ return ret;
+
+ tb_port_dbg(port, "xHCI connected\n");
+ return 0;
+}
+
+/**
+ * tb_lc_xhci_disconnect() - Disconnect internal xHCI
+ * @port: Device router lane 0 adapter
+ *
+ * Tells LC to disconnect the internal xHCI from @port. Can be called
+ * for Thunderbolt 3 routers only.
+ */
+void tb_lc_xhci_disconnect(struct tb_port *port)
+{
+ __tb_lc_xhci_connect(port, false);
+ tb_port_dbg(port, "xHCI disconnected\n");
+}
+
static int tb_lc_set_wake_one(struct tb_switch *sw, unsigned int offset,
unsigned int flags)
{
diff --git a/drivers/thunderbolt/nhi.c b/drivers/thunderbolt/nhi.c
index c73da0532be4..4a582183f675 100644
--- a/drivers/thunderbolt/nhi.c
+++ b/drivers/thunderbolt/nhi.c
@@ -13,6 +13,7 @@
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/pci.h>
+#include <linux/dma-mapping.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/delay.h>
@@ -1229,8 +1230,6 @@ static int nhi_probe(struct pci_dev *pdev, const struct pci_device_id *id)
spin_lock_init(&nhi->lock);
res = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
- if (res)
- res = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
if (res) {
dev_err(&pdev->dev, "failed to set DMA mask\n");
return res;
diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c
index d026e305fe5d..ac87e8b50e52 100644
--- a/drivers/thunderbolt/switch.c
+++ b/drivers/thunderbolt/switch.c
@@ -1528,7 +1528,13 @@ static int tb_plug_events_active(struct tb_switch *sw, bool active)
case PCI_DEVICE_ID_INTEL_PORT_RIDGE:
break;
default:
- data |= 4;
+ /*
+ * Skip Alpine Ridge, it needs to have vendor
+ * specific USB hotplug event enabled for the
+ * internal xHCI to work.
+ */
+ if (!tb_switch_is_alpine_ridge(sw))
+ data |= TB_PLUG_EVENTS_USB_DISABLE;
}
} else {
data = data | 0x7c;
@@ -2778,10 +2784,8 @@ int tb_switch_add(struct tb_switch *sw)
/* read drom */
ret = tb_drom_read(sw);
- if (ret) {
- dev_err(&sw->dev, "reading DROM failed\n");
- return ret;
- }
+ if (ret)
+ dev_warn(&sw->dev, "reading DROM failed: %d\n", ret);
tb_sw_dbg(sw, "uid: %#llx\n", sw->uid);
tb_check_quirks(sw);
@@ -2974,6 +2978,10 @@ int tb_switch_resume(struct tb_switch *sw)
return err;
}
+ /* We don't have any way to confirm this was the same device */
+ if (!sw->uid)
+ return -ENODEV;
+
if (tb_switch_is_usb4(sw))
err = usb4_switch_read_uid(sw, &uid);
else
@@ -3689,3 +3697,66 @@ int tb_switch_pcie_l1_enable(struct tb_switch *sw)
/* Write to Upstream PCIe bridge #0 aka Up0 */
return tb_switch_pcie_bridge_write(sw, 0, 0x143, 0x0c5806b1);
}
+
+/**
+ * tb_switch_xhci_connect() - Connect internal xHCI
+ * @sw: Router whose xHCI to connect
+ *
+ * Can be called to any router. For Alpine Ridge and Titan Ridge
+ * performs special flows that bring the xHCI functional for any device
+ * connected to the type-C port. Call only after PCIe tunnel has been
+ * established. The function only does the connect if not done already
+ * so can be called several times for the same router.
+ */
+int tb_switch_xhci_connect(struct tb_switch *sw)
+{
+ bool usb_port1, usb_port3, xhci_port1, xhci_port3;
+ struct tb_port *port1, *port3;
+ int ret;
+
+ port1 = &sw->ports[1];
+ port3 = &sw->ports[3];
+
+ if (tb_switch_is_alpine_ridge(sw)) {
+ usb_port1 = tb_lc_is_usb_plugged(port1);
+ usb_port3 = tb_lc_is_usb_plugged(port3);
+ xhci_port1 = tb_lc_is_xhci_connected(port1);
+ xhci_port3 = tb_lc_is_xhci_connected(port3);
+
+ /* Figure out correct USB port to connect */
+ if (usb_port1 && !xhci_port1) {
+ ret = tb_lc_xhci_connect(port1);
+ if (ret)
+ return ret;
+ }
+ if (usb_port3 && !xhci_port3)
+ return tb_lc_xhci_connect(port3);
+ } else if (tb_switch_is_titan_ridge(sw)) {
+ ret = tb_lc_xhci_connect(port1);
+ if (ret)
+ return ret;
+ return tb_lc_xhci_connect(port3);
+ }
+
+ return 0;
+}
+
+/**
+ * tb_switch_xhci_disconnect() - Disconnect internal xHCI
+ * @sw: Router whose xHCI to disconnect
+ *
+ * The opposite of tb_switch_xhci_connect(). Disconnects xHCI on both
+ * ports.
+ */
+void tb_switch_xhci_disconnect(struct tb_switch *sw)
+{
+ if (sw->generation == 3) {
+ struct tb_port *port1 = &sw->ports[1];
+ struct tb_port *port3 = &sw->ports[3];
+
+ tb_lc_xhci_disconnect(port1);
+ tb_port_dbg(port1, "disconnected xHCI\n");
+ tb_lc_xhci_disconnect(port3);
+ tb_port_dbg(port3, "disconnected xHCI\n");
+ }
+}
diff --git a/drivers/thunderbolt/tb.c b/drivers/thunderbolt/tb.c
index cbd0ad85ffb1..9beb47b31c75 100644
--- a/drivers/thunderbolt/tb.c
+++ b/drivers/thunderbolt/tb.c
@@ -1054,6 +1054,8 @@ static int tb_disconnect_pci(struct tb *tb, struct tb_switch *sw)
if (WARN_ON(!tunnel))
return -ENODEV;
+ tb_switch_xhci_disconnect(sw);
+
tb_tunnel_deactivate(tunnel);
list_del(&tunnel->list);
tb_tunnel_free(tunnel);
@@ -1099,6 +1101,9 @@ static int tb_tunnel_pci(struct tb *tb, struct tb_switch *sw)
if (tb_switch_pcie_l1_enable(sw))
tb_sw_warn(sw, "failed to enable PCIe L1 for Titan Ridge\n");
+ if (tb_switch_xhci_connect(sw))
+ tb_sw_warn(sw, "failed to connect xHCI\n");
+
list_add_tail(&tunnel->list, &tcm->tunnel_list);
return 0;
}
@@ -1256,12 +1261,18 @@ static void tb_handle_hotplug(struct work_struct *work)
tb_port_unconfigure_xdomain(port);
} else if (tb_port_is_dpout(port) || tb_port_is_dpin(port)) {
tb_dp_resource_unavailable(tb, port);
+ } else if (!port->port) {
+ tb_sw_dbg(sw, "xHCI disconnect request\n");
+ tb_switch_xhci_disconnect(sw);
} else {
tb_port_dbg(port,
"got unplug event for disconnected port, ignoring\n");
}
} else if (port->remote) {
tb_port_dbg(port, "got plug event for connected port, ignoring\n");
+ } else if (!port->port && sw->authorized) {
+ tb_sw_dbg(sw, "xHCI connect request\n");
+ tb_switch_xhci_connect(sw);
} else {
if (tb_port_is_null(port)) {
tb_port_dbg(port, "hotplug: scanning\n");
diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h
index 74d3b14f004e..b6fcd8d45324 100644
--- a/drivers/thunderbolt/tb.h
+++ b/drivers/thunderbolt/tb.h
@@ -855,6 +855,7 @@ static inline bool tb_switch_is_alpine_ridge(const struct tb_switch *sw)
if (sw->config.vendor_id == PCI_VENDOR_ID_INTEL) {
switch (sw->config.device_id) {
case PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_2C_BRIDGE:
+ case PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_4C_BRIDGE:
case PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_LP_BRIDGE:
case PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_C_4C_BRIDGE:
case PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_C_2C_BRIDGE:
@@ -987,6 +988,9 @@ int tb_switch_mask_clx_objections(struct tb_switch *sw);
int tb_switch_pcie_l1_enable(struct tb_switch *sw);
+int tb_switch_xhci_connect(struct tb_switch *sw);
+void tb_switch_xhci_disconnect(struct tb_switch *sw);
+
int tb_wait_for_port(struct tb_port *port, bool wait_if_unplugged);
int tb_port_add_nfc_credits(struct tb_port *port, int credits);
int tb_port_clear_counter(struct tb_port *port, int counter);
@@ -1081,6 +1085,10 @@ int tb_lc_configure_xdomain(struct tb_port *port);
void tb_lc_unconfigure_xdomain(struct tb_port *port);
int tb_lc_start_lane_initialization(struct tb_port *port);
bool tb_lc_is_clx_supported(struct tb_port *port);
+bool tb_lc_is_usb_plugged(struct tb_port *port);
+bool tb_lc_is_xhci_connected(struct tb_port *port);
+int tb_lc_xhci_connect(struct tb_port *port);
+void tb_lc_xhci_disconnect(struct tb_port *port);
int tb_lc_set_wake(struct tb_switch *sw, unsigned int flags);
int tb_lc_set_sleep(struct tb_switch *sw);
bool tb_lc_lane_bonding_possible(struct tb_switch *sw);
diff --git a/drivers/thunderbolt/tb_regs.h b/drivers/thunderbolt/tb_regs.h
index a74f4878d3e7..b301eeb0c89b 100644
--- a/drivers/thunderbolt/tb_regs.h
+++ b/drivers/thunderbolt/tb_regs.h
@@ -133,11 +133,11 @@ struct tb_cap_phy {
} __packed;
struct tb_eeprom_ctl {
- bool clock:1; /* send pulse to transfer one bit */
- bool access_low:1; /* set to 0 before access */
- bool data_out:1; /* to eeprom */
- bool data_in:1; /* from eeprom */
- bool access_high:1; /* set to 1 before access */
+ bool fl_sk:1; /* send pulse to transfer one bit */
+ bool fl_cs:1; /* set to 0 before access */
+ bool fl_di:1; /* to eeprom */
+ bool fl_do:1; /* from eeprom */
+ bool bit_banging_enable:1; /* set to 1 before access */
bool not_present:1; /* should be 0 */
bool unknown1:1;
bool present:1; /* should be 1 */
@@ -146,14 +146,14 @@ struct tb_eeprom_ctl {
struct tb_cap_plug_events {
struct tb_cap_extended_short cap_header;
- u32 __unknown1:2;
- u32 plug_events:5;
- u32 __unknown2:25;
- u32 __unknown3;
- u32 __unknown4;
+ u32 __unknown1:2; /* VSC_CS_1 */
+ u32 plug_events:5; /* VSC_CS_1 */
+ u32 __unknown2:25; /* VSC_CS_1 */
+ u32 vsc_cs_2;
+ u32 vsc_cs_3;
struct tb_eeprom_ctl eeprom_ctl;
- u32 __unknown5[7];
- u32 drom_offset; /* 32 bit register, but eeprom addresses are 16 bit */
+ u32 __unknown5[7]; /* VSC_CS_5 -> VSC_CS_11 */
+ u32 drom_offset; /* VSC_CS_12: 32 bit register, but eeprom addresses are 16 bit */
} __packed;
/* device headers */
@@ -389,6 +389,7 @@ struct tb_regs_port_header {
#define DP_COMMON_CAP_1_LANE 0x0
#define DP_COMMON_CAP_2_LANES 0x1
#define DP_COMMON_CAP_4_LANES 0x2
+#define DP_COMMON_CAP_LTTPR_NS BIT(27)
#define DP_COMMON_CAP_DPRX_DONE BIT(31)
/* PCIe adapter registers */
@@ -462,6 +463,12 @@ struct tb_regs_hop {
#define TMU_ADP_CS_6_DISABLE_TMU_OBJ_CL2 BIT(3)
/* Plug Events registers */
+#define TB_PLUG_EVENTS_USB_DISABLE BIT(2)
+#define TB_PLUG_EVENTS_CS_1_LANE_DISABLE BIT(3)
+#define TB_PLUG_EVENTS_CS_1_DPOUT_DISABLE BIT(4)
+#define TB_PLUG_EVENTS_CS_1_LOW_DPIN_DISABLE BIT(5)
+#define TB_PLUG_EVENTS_CS_1_HIGH_DPIN_DISABLE BIT(6)
+
#define TB_PLUG_EVENTS_PCIE_WR_DATA 0x1b
#define TB_PLUG_EVENTS_PCIE_CMD 0x1c
#define TB_PLUG_EVENTS_PCIE_CMD_DW_OFFSET_MASK GENMASK(9, 0)
@@ -501,6 +508,9 @@ struct tb_regs_hop {
#define TB_LC_POWER 0x740
/* Link controller registers */
+#define TB_LC_CS_42 0x2a
+#define TB_LC_CS_42_USB_PLUGGED BIT(31)
+
#define TB_LC_PORT_ATTR 0x8d
#define TB_LC_PORT_ATTR_BE BIT(12)
@@ -521,4 +531,7 @@ struct tb_regs_hop {
#define TB_LC_LINK_ATTR 0x97
#define TB_LC_LINK_ATTR_CPS BIT(18)
+#define TB_LC_LINK_REQ 0xad
+#define TB_LC_LINK_REQ_XHCI_CONNECT BIT(31)
+
#endif
diff --git a/drivers/thunderbolt/tunnel.c b/drivers/thunderbolt/tunnel.c
index a473cc7d9a8d..118742ec93ed 100644
--- a/drivers/thunderbolt/tunnel.c
+++ b/drivers/thunderbolt/tunnel.c
@@ -580,6 +580,16 @@ static int tb_dp_xchg_caps(struct tb_tunnel *tunnel)
out_dp_cap = tb_dp_cap_set_lanes(out_dp_cap, new_lanes);
}
+ /*
+ * Titan Ridge does not disable AUX timers when it gets
+ * SET_CONFIG with SET_LTTPR_MODE set. This causes problems with
+ * DP tunneling.
+ */
+ if (tb_route(out->sw) && tb_switch_is_titan_ridge(out->sw)) {
+ out_dp_cap |= DP_COMMON_CAP_LTTPR_NS;
+ tb_port_dbg(out, "disabling LTTPR\n");
+ }
+
return tb_port_write(in, &out_dp_cap, TB_CFG_PORT,
in->cap_adap + DP_REMOTE_CAP, 1);
}