diff options
author | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2022-03-11 16:31:12 +0100 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2022-03-11 16:31:12 +0100 |
commit | 7d2c8a13c925403b5d4e3d121c00e2b25c0cd715 (patch) | |
tree | bc68452df0866805a08daa6904d4248325ec7d98 /drivers | |
parent | Merge tag 'usb-serial-5.18-rc1' of https://git.kernel.org/pub/scm/linux/kerne... (diff) | |
parent | thunderbolt: Rename EEPROM handling bits to match USB4 spec (diff) | |
download | linux-7d2c8a13c925403b5d4e3d121c00e2b25c0cd715.tar.xz linux-7d2c8a13c925403b5d4e3d121c00e2b25c0cd715.zip |
Merge tag 'thunderbolt-for-v5.18-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/westeri/thunderbolt into usb-next
Mika writes:
thunderbolt: Changes for v5.18 merge window
This includes following Thunderbolt/USB4 changes for the v5.18 merge
window:
* Improvements for Intel Alpine and Titan Ridge support
* Replace acpi_bus_get_device() with acpi_fetch_acpi_dev()
* Improvements around DROM handling on AMD hardware
* A couple of cleanups.
All these have been in linux-next with no reported issues.
* tag 'thunderbolt-for-v5.18-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/westeri/thunderbolt:
thunderbolt: Rename EEPROM handling bits to match USB4 spec
thunderbolt: Clarify register definitions for `tb_cap_plug_events`
thunderbolt: Do not make DROM read success compulsory
thunderbolt: Do not resume routers if UID is not set
thunderbolt: Retry DROM reads for more failure scenarios
thunderbolt: Replace acpi_bus_get_device()
thunderbolt: Add internal xHCI connect flows for Thunderbolt 3 devices
thunderbolt: Add missing device ID to tb_switch_is_alpine_ridge()
thunderbolt: Disable LTTPR on Intel Titan Ridge
thunderbolt: Remove useless DMA-32 fallback configuration
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/thunderbolt/acpi.c | 4 | ||||
-rw-r--r-- | drivers/thunderbolt/eeprom.c | 41 | ||||
-rw-r--r-- | drivers/thunderbolt/lc.c | 110 | ||||
-rw-r--r-- | drivers/thunderbolt/nhi.c | 3 | ||||
-rw-r--r-- | drivers/thunderbolt/switch.c | 81 | ||||
-rw-r--r-- | drivers/thunderbolt/tb.c | 11 | ||||
-rw-r--r-- | drivers/thunderbolt/tb.h | 8 | ||||
-rw-r--r-- | drivers/thunderbolt/tb_regs.h | 37 | ||||
-rw-r--r-- | drivers/thunderbolt/tunnel.c | 10 |
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); } |