Commit 01da6b99 authored by Sanath S's avatar Sanath S Committed by Mika Westerberg
Browse files

thunderbolt: Introduce tb_port_reset()



Introduce a function that issues Downstream Port Reset to a USB4 port.
This supports Thunderbolt 2, 3 and USB4 routers.

Signed-off-by: default avatarSanath S <Sanath.S@amd.com>
Signed-off-by: default avatarMika Westerberg <mika.westerberg@linux.intel.com>
parent 6613476e
Loading
Loading
Loading
Loading
+45 −0
Original line number Diff line number Diff line
@@ -6,6 +6,8 @@
 * Author: Mika Westerberg <mika.westerberg@linux.intel.com>
 */

#include <linux/delay.h>

#include "tb.h"

/**
@@ -45,6 +47,49 @@ static int find_port_lc_cap(struct tb_port *port)
	return sw->cap_lc + start + phys * size;
}

/**
 * tb_lc_reset_port() - Trigger downstream port reset through LC
 * @port: Port that is reset
 *
 * Triggers downstream port reset through link controller registers.
 * Returns %0 in case of success negative errno otherwise. Only supports
 * non-USB4 routers with link controller (that's Thunderbolt 2 and
 * Thunderbolt 3).
 */
int tb_lc_reset_port(struct tb_port *port)
{
	struct tb_switch *sw = port->sw;
	int cap, ret;
	u32 mode;

	if (sw->generation < 2)
		return -EINVAL;

	cap = find_port_lc_cap(port);
	if (cap < 0)
		return cap;

	ret = tb_sw_read(sw, &mode, TB_CFG_SWITCH, cap + TB_LC_PORT_MODE, 1);
	if (ret)
		return ret;

	mode |= TB_LC_PORT_MODE_DPR;

	ret = tb_sw_write(sw, &mode, TB_CFG_SWITCH, cap + TB_LC_PORT_MODE, 1);
	if (ret)
		return ret;

	fsleep(10000);

	ret = tb_sw_read(sw, &mode, TB_CFG_SWITCH, cap + TB_LC_PORT_MODE, 1);
	if (ret)
		return ret;

	mode &= ~TB_LC_PORT_MODE_DPR;

	return tb_sw_write(sw, &mode, TB_CFG_SWITCH, cap + TB_LC_PORT_MODE, 1);
}

static int tb_lc_set_port_configured(struct tb_port *port, bool configured)
{
	bool upstream = tb_is_upstream_port(port);
+7 −0
Original line number Diff line number Diff line
@@ -676,6 +676,13 @@ int tb_port_disable(struct tb_port *port)
	return __tb_port_enable(port, false);
}

static int tb_port_reset(struct tb_port *port)
{
	if (tb_switch_is_usb4(port->sw))
		return port->cap_usb4 ? usb4_port_reset(port) : 0;
	return tb_lc_reset_port(port);
}

/*
 * tb_init_port() - initialize a port
 *
+2 −0
Original line number Diff line number Diff line
@@ -1169,6 +1169,7 @@ int tb_drom_read(struct tb_switch *sw);
int tb_drom_read_uid_only(struct tb_switch *sw, u64 *uid);

int tb_lc_read_uuid(struct tb_switch *sw, u32 *uuid);
int tb_lc_reset_port(struct tb_port *port);
int tb_lc_configure_port(struct tb_port *port);
void tb_lc_unconfigure_port(struct tb_port *port);
int tb_lc_configure_xdomain(struct tb_port *port);
@@ -1301,6 +1302,7 @@ void usb4_switch_remove_ports(struct tb_switch *sw);

int usb4_port_unlock(struct tb_port *port);
int usb4_port_hotplug_enable(struct tb_port *port);
int usb4_port_reset(struct tb_port *port);
int usb4_port_configure(struct tb_port *port);
void usb4_port_unconfigure(struct tb_port *port);
int usb4_port_configure_xdomain(struct tb_port *port, struct tb_xdomain *xd);
+4 −0
Original line number Diff line number Diff line
@@ -389,6 +389,7 @@ struct tb_regs_port_header {
#define PORT_CS_18_CSA				BIT(22)
#define PORT_CS_18_TIP				BIT(24)
#define PORT_CS_19				0x13
#define PORT_CS_19_DPR				BIT(0)
#define PORT_CS_19_PC				BIT(3)
#define PORT_CS_19_PID				BIT(4)
#define PORT_CS_19_WOC				BIT(16)
@@ -584,6 +585,9 @@ struct tb_regs_hop {
#define TB_LC_POWER				0x740

/* Link controller registers */
#define TB_LC_PORT_MODE				0x26
#define TB_LC_PORT_MODE_DPR			BIT(0)

#define TB_LC_CS_42				0x2a
#define TB_LC_CS_42_USB_PLUGGED			BIT(31)

+39 −0
Original line number Diff line number Diff line
@@ -1113,6 +1113,45 @@ int usb4_port_hotplug_enable(struct tb_port *port)
	return tb_port_write(port, &val, TB_CFG_PORT, ADP_CS_5, 1);
}

/**
 * usb4_port_reset() - Issue downstream port reset
 * @port: USB4 port to reset
 *
 * Issues downstream port reset to @port.
 */
int usb4_port_reset(struct tb_port *port)
{
	int ret;
	u32 val;

	if (!port->cap_usb4)
		return -EINVAL;

	ret = tb_port_read(port, &val, TB_CFG_PORT,
			   port->cap_usb4 + PORT_CS_19, 1);
	if (ret)
		return ret;

	val |= PORT_CS_19_DPR;

	ret = tb_port_write(port, &val, TB_CFG_PORT,
			    port->cap_usb4 + PORT_CS_19, 1);
	if (ret)
		return ret;

	fsleep(10000);

	ret = tb_port_read(port, &val, TB_CFG_PORT,
			   port->cap_usb4 + PORT_CS_19, 1);
	if (ret)
		return ret;

	val &= ~PORT_CS_19_DPR;

	return tb_port_write(port, &val, TB_CFG_PORT,
			     port->cap_usb4 + PORT_CS_19, 1);
}

static int usb4_port_set_configured(struct tb_port *port, bool configured)
{
	int ret;