aboutsummaryrefslogtreecommitdiff
path: root/drivers/thunderbolt/switch.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/thunderbolt/switch.c')
-rw-r--r--drivers/thunderbolt/switch.c71
1 files changed, 70 insertions, 1 deletions
diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c
index d026e305fe5d..b5fb3e76ed09 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;
@@ -3689,3 +3695,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");
+ }
+}