Commit de718ac7 authored by Mika Westerberg's avatar Mika Westerberg
Browse files

thunderbolt: Add Display Port CM handshake for Titan Ridge devices



Titan Ridge needs an additional connection manager handshake in order to
do proper Display Port tunneling so implement it here.

Signed-off-by: default avatarMika Westerberg <mika.westerberg@linux.intel.com>
parent 7bffd97e
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -252,6 +252,9 @@ struct tb_regs_port_header {
#define ADP_DP_CS_3_HDPC			BIT(9)
#define DP_LOCAL_CAP				0x04
#define DP_REMOTE_CAP				0x05
#define DP_STATUS_CTRL				0x06
#define DP_STATUS_CTRL_CMHS			BIT(25)
#define DP_STATUS_CTRL_UF			BIT(26)

/* PCIe adapter registers */
#define ADP_PCIE_CS_0				0x00
+45 −0
Original line number Diff line number Diff line
@@ -6,6 +6,7 @@
 * Copyright (C) 2019, Intel Corporation
 */

#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/list.h>

@@ -242,6 +243,42 @@ struct tb_tunnel *tb_tunnel_alloc_pci(struct tb *tb, struct tb_port *up,
	return tunnel;
}

static int tb_dp_cm_handshake(struct tb_port *in, struct tb_port *out)
{
	int timeout = 10;
	u32 val;
	int ret;

	/* Both ends need to support this */
	if (!tb_switch_is_titan_ridge(in->sw) ||
	    !tb_switch_is_titan_ridge(out->sw))
		return 0;

	ret = tb_port_read(out, &val, TB_CFG_PORT,
			   out->cap_adap + DP_STATUS_CTRL, 1);
	if (ret)
		return ret;

	val |= DP_STATUS_CTRL_UF | DP_STATUS_CTRL_CMHS;

	ret = tb_port_write(out, &val, TB_CFG_PORT,
			    out->cap_adap + DP_STATUS_CTRL, 1);
	if (ret)
		return ret;

	do {
		ret = tb_port_read(out, &val, TB_CFG_PORT,
				   out->cap_adap + DP_STATUS_CTRL, 1);
		if (ret)
			return ret;
		if (!(val & DP_STATUS_CTRL_CMHS))
			return 0;
		usleep_range(10, 100);
	} while (timeout--);

	return -ETIMEDOUT;
}

static int tb_dp_xchg_caps(struct tb_tunnel *tunnel)
{
	struct tb_port *out = tunnel->dst_port;
@@ -256,6 +293,14 @@ static int tb_dp_xchg_caps(struct tb_tunnel *tunnel)
	if (in->sw->generation < 2 || out->sw->generation < 2)
		return 0;

	/*
	 * Perform connection manager handshake between IN and OUT ports
	 * before capabilities exchange can take place.
	 */
	ret = tb_dp_cm_handshake(in, out);
	if (ret)
		return ret;

	/* Read both DP_LOCAL_CAP registers */
	ret = tb_port_read(in, &in_dp_cap, TB_CFG_PORT,
			   in->cap_adap + DP_LOCAL_CAP, 1);