Skip to contentSkip to conversation

thunderbolt: Improvements for Alpine and Titan Ridge devices

PATCH In review Mika Westerberg 2 participants 6 messages Jan 26, 2022 – Feb 2, 2022

Subsystems (2)
lore
Share & tools
lore
#0 lore raw

cover thunderbolt: Improvements for Alpine and Titan Ridge devices

Hi all,

The first two patches implement flows that are needed to bring up the internal xHCI controller when there is USB device connected to the downstream Thunderbolt port of the the router. The last patch disables LTTPR (LT-tunable PHY Repeater, DisplayPort term) when Titan Ridge device router is connected to avoid tunneling issues.

Mika Westerberg (3):
thunderbolt: Add missing device ID to tb_switch_is_alpine_ridge()
thunderbolt: Add internal xHCI connect flows for Thunderbolt 3 devices
thunderbolt: Disable LTTPR on Intel Titan Ridge

 drivers/thunderbolt/lc.c      | 110 ++++++++++++++++++++++++++++++++++
 drivers/thunderbolt/switch.c  |  71 +++++++++++++++++++++-
 drivers/thunderbolt/tb.c      |  11 ++++
 drivers/thunderbolt/tb.h      |   8 +++
 drivers/thunderbolt/tb_regs.h |   9 +++
 drivers/thunderbolt/tunnel.c  |  10 ++++
 6 files changed, 218 insertions(+), 1 deletion(-)
Signature · 1 line
2.34.1
#1 lore raw

PATCH 1/3 thunderbolt: Add missing device ID to tb_switch_is_alpine_ridge()

tb_switch_is_alpine_ridge() is missing device ID for Intel Alpine Ridge dual port version so add this.

---
 drivers/thunderbolt/tb.h | 1 +
 1 file changed, 1 insertion(+)
drivers/thunderbolt/tb.h +1 -0
index 74d3b14f004e..44e36498b261 100644
@@ -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:
Signature · 1 line
2.34.1
#2 lore raw

PATCH 2/3 thunderbolt: Add internal xHCI connect flows for Thunderbolt 3 devices

In reply to Mika Westerberg

Both Alpine Ridge and Titan Ridge require special flows in order to activate the internal xHCI controller when there is USB device connected to the downstream type-C port. This implements the missing flows for both.

---
 drivers/thunderbolt/lc.c      | 110 ++++++++++++++++++++++++++++++++++
 drivers/thunderbolt/switch.c  |  71 +++++++++++++++++++++-
 drivers/thunderbolt/tb.c      |  11 ++++
 drivers/thunderbolt/tb.h      |   7 +++
 drivers/thunderbolt/tb_regs.h |   8 +++
 5 files changed, 206 insertions(+), 1 deletion(-)
drivers/thunderbolt/lc.c +110 -0
index 53495a38b4eb..7f97adc4f803 100644
@@ -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 in 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)
 {
drivers/thunderbolt/switch.c +70 -1

80 lines not on this page · raw message

drivers/thunderbolt/tb.c +11 -0
index cbd0ad85ffb1..9beb47b31c75 100644
@@ -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");
drivers/thunderbolt/tb.h +7 -0
index 44e36498b261..b6fcd8d45324 100644
@@ -988,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);
@@ -1082,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);
drivers/thunderbolt/tb_regs.h +8 -0
index a74f4878d3e7..dc00cbd69d36 100644
@@ -462,6 +462,8 @@ 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_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 +503,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 +526,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
Signature · 1 line
2.34.1
#3 lore raw

Re: [PATCH 2/3] thunderbolt: Add internal xHCI connect flows for Thunderbolt 3 devices

Quoted 5 lines · Mika Westerberg

+/**
+ * tb_lc_xhci_connect() - Connect internal xHCI
+ * @port: Device router lane 0 adapter
+ *
+ * Tells LC to connect the internal xHCI to @port. Returns %0 in success

                                                                 ^^
								 on
Quoted 3 lines

+ * and negative errno in case of failure. Can be called for Thunderbolt 3
+ * routers only.
+ */

#4 lore raw

PATCH 3/3 thunderbolt: Disable LTTPR on Intel Titan Ridge

In reply to Mika Westerberg

Intel Titan Ridge does not disable AUX timers when it gets SET_CONFIG with SET_LTTPR_MODE set which makes DP tunneling to fail. For this reason disable LTTPR on Titan Ridge device side.

---
 drivers/thunderbolt/tb_regs.h |  1 +
 drivers/thunderbolt/tunnel.c  | 10 ++++++++++
 2 files changed, 11 insertions(+)
drivers/thunderbolt/tb_regs.h +1 -0
index dc00cbd69d36..70795a2aa9bb 100644
@@ -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 */
drivers/thunderbolt/tunnel.c +10 -0
index a473cc7d9a8d..118742ec93ed 100644
@@ -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);
 }
Signature · 1 line
2.34.1
#5 lore raw
In reply to Mika Westerberg
Quoted 8 lines · Mika Westerberg

Hi all,

The first two patches implement flows that are needed to bring up the internal xHCI controller when there is USB device connected to the downstream Thunderbolt port of the the router. The last patch disables LTTPR (LT-tunable PHY Repeater, DisplayPort term) when Titan Ridge device router is connected to avoid tunneling issues.

Quoted 4 lines

Mika Westerberg (3):
thunderbolt: Add missing device ID to tb_switch_is_alpine_ridge()
thunderbolt: Add internal xHCI connect flows for Thunderbolt 3 devices
thunderbolt: Disable LTTPR on Intel Titan Ridge

Fixed the typo pointed out by Lukas and applied to thunderbolt.git/next.