thunderbolt: Configure port for XDomain

When the port is connected to another host it should be marked as such
in the USB4 port capability. This information is used by the router
during sleep and wakeup.

Also do the same for legacy switches via link controller vendor specific
registers.

Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
This commit is contained in:
Mika Westerberg 2020-04-09 14:23:32 +03:00
parent e28178bf56
commit 284652a4a4
5 changed files with 134 additions and 4 deletions

View File

@ -104,6 +104,60 @@ void tb_lc_unconfigure_port(struct tb_port *port)
tb_lc_set_port_configured(port, false);
}
static int tb_lc_set_xdomain_configured(struct tb_port *port, bool configure)
{
struct tb_switch *sw = port->sw;
u32 ctrl, lane;
int cap, ret;
if (sw->generation < 2)
return 0;
cap = find_port_lc_cap(port);
if (cap < 0)
return cap;
ret = tb_sw_read(sw, &ctrl, TB_CFG_SWITCH, cap + TB_LC_SX_CTRL, 1);
if (ret)
return ret;
/* Resolve correct lane */
if (port->port % 2)
lane = TB_LC_SX_CTRL_L1D;
else
lane = TB_LC_SX_CTRL_L2D;
if (configure)
ctrl |= lane;
else
ctrl &= ~lane;
return tb_sw_write(sw, &ctrl, TB_CFG_SWITCH, cap + TB_LC_SX_CTRL, 1);
}
/**
* tb_lc_configure_xdomain() - Inform LC that the link is XDomain
* @port: Switch downstream port connected to another host
*
* Sets the lane configured for XDomain accordingly so that the LC knows
* about this. Returns %0 in success and negative errno in failure.
*/
int tb_lc_configure_xdomain(struct tb_port *port)
{
return tb_lc_set_xdomain_configured(port, true);
}
/**
* tb_lc_unconfigure_xdomain() - Unconfigure XDomain from port
* @port: Switch downstream port that was connected to another host
*
* Unsets the lane XDomain configuration.
*/
void tb_lc_unconfigure_xdomain(struct tb_port *port)
{
tb_lc_set_xdomain_configured(port, false);
}
/**
* tb_lc_set_sleep() - Inform LC that the switch is going to sleep
* @sw: Switch to set sleep

View File

@ -140,6 +140,21 @@ static void tb_discover_tunnels(struct tb_switch *sw)
}
}
static int tb_port_configure_xdomain(struct tb_port *port)
{
if (tb_switch_is_usb4(port->sw))
return usb4_port_configure_xdomain(port);
return tb_lc_configure_xdomain(port);
}
static void tb_port_unconfigure_xdomain(struct tb_port *port)
{
if (tb_switch_is_usb4(port->sw))
usb4_port_unconfigure_xdomain(port);
else
tb_lc_unconfigure_xdomain(port);
}
static void tb_scan_xdomain(struct tb_port *port)
{
struct tb_switch *sw = port->sw;
@ -158,6 +173,7 @@ static void tb_scan_xdomain(struct tb_port *port)
NULL);
if (xd) {
tb_port_at(route, sw)->xdomain = xd;
tb_port_configure_xdomain(port);
tb_xdomain_add(xd);
}
}
@ -566,6 +582,7 @@ static void tb_scan_port(struct tb_port *port)
*/
if (port->xdomain) {
tb_xdomain_remove(port->xdomain);
tb_port_unconfigure_xdomain(port);
port->xdomain = NULL;
}
@ -1047,6 +1064,7 @@ static void tb_handle_hotplug(struct work_struct *work)
struct tb_cm *tcm = tb_priv(tb);
struct tb_switch *sw;
struct tb_port *port;
mutex_lock(&tb->lock);
if (!tcm->hotplug_active)
goto out; /* during init, suspend or shutdown */
@ -1103,6 +1121,7 @@ static void tb_handle_hotplug(struct work_struct *work)
port->xdomain = NULL;
__tb_disconnect_xdomain_paths(tb, xd);
tb_xdomain_put(xd);
tb_port_unconfigure_xdomain(port);
} else if (tb_port_is_dpout(port) || tb_port_is_dpin(port)) {
tb_dp_resource_unavailable(tb, port);
} else {
@ -1269,13 +1288,17 @@ static void tb_restore_children(struct tb_switch *sw)
tb_sw_warn(sw, "failed to restore TMU configuration\n");
tb_switch_for_each_port(sw, port) {
if (!tb_port_has_remote(port))
if (!tb_port_has_remote(port) && !port->xdomain)
continue;
tb_switch_lane_bonding_enable(port->remote->sw);
tb_switch_configure_link(port->remote->sw);
if (port->remote) {
tb_switch_lane_bonding_enable(port->remote->sw);
tb_switch_configure_link(port->remote->sw);
tb_restore_children(port->remote->sw);
tb_restore_children(port->remote->sw);
} else if (port->xdomain) {
tb_port_configure_xdomain(port);
}
}
}
@ -1321,6 +1344,7 @@ static int tb_free_unplugged_xdomains(struct tb_switch *sw)
if (port->xdomain && port->xdomain->is_unplugged) {
tb_retimer_remove_all(port);
tb_xdomain_remove(port->xdomain);
tb_port_unconfigure_xdomain(port);
port->xdomain = NULL;
ret++;
} else if (port->remote) {

View File

@ -848,6 +848,8 @@ 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_configure_port(struct tb_port *port);
void tb_lc_unconfigure_port(struct tb_port *port);
int tb_lc_configure_xdomain(struct tb_port *port);
void tb_lc_unconfigure_xdomain(struct tb_port *port);
int tb_lc_set_sleep(struct tb_switch *sw);
bool tb_lc_lane_bonding_possible(struct tb_switch *sw);
bool tb_lc_dp_sink_query(struct tb_switch *sw, struct tb_port *in);
@ -921,6 +923,8 @@ struct tb_port *usb4_switch_map_usb3_down(struct tb_switch *sw,
int usb4_port_unlock(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);
void usb4_port_unconfigure_xdomain(struct tb_port *port);
int usb4_port_enumerate_retimers(struct tb_port *port);
int usb4_port_retimer_read(struct tb_port *port, u8 index, u8 reg, void *buf,

View File

@ -303,6 +303,7 @@ struct tb_regs_port_header {
#define PORT_CS_18_TCM BIT(9)
#define PORT_CS_19 0x13
#define PORT_CS_19_PC BIT(3)
#define PORT_CS_19_PID BIT(4)
/* Display Port adapter registers */
#define ADP_DP_CS_0 0x00
@ -417,7 +418,9 @@ struct tb_regs_hop {
#define TB_LC_SX_CTRL 0x96
#define TB_LC_SX_CTRL_L1C BIT(16)
#define TB_LC_SX_CTRL_L1D BIT(17)
#define TB_LC_SX_CTRL_L2C BIT(20)
#define TB_LC_SX_CTRL_L2D BIT(21)
#define TB_LC_SX_CTRL_UPSTREAM BIT(30)
#define TB_LC_SX_CTRL_SLP BIT(31)

View File

@ -785,6 +785,51 @@ void usb4_port_unconfigure(struct tb_port *port)
usb4_port_set_configured(port, false);
}
static int usb4_set_xdomain_configured(struct tb_port *port, bool configured)
{
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;
if (configured)
val |= PORT_CS_19_PID;
else
val &= ~PORT_CS_19_PID;
return tb_port_write(port, &val, TB_CFG_PORT,
port->cap_usb4 + PORT_CS_19, 1);
}
/**
* usb4_port_configure_xdomain() - Configure port for XDomain
* @port: USB4 port connected to another host
*
* Marks the USB4 port as being connected to another host. Returns %0 in
* success and negative errno in failure.
*/
int usb4_port_configure_xdomain(struct tb_port *port)
{
return usb4_set_xdomain_configured(port, true);
}
/**
* usb4_port_unconfigure_xdomain() - Unconfigure port for XDomain
* @port: USB4 port that was connected to another host
*
* Clears USB4 port from being marked as XDomain.
*/
void usb4_port_unconfigure_xdomain(struct tb_port *port)
{
usb4_set_xdomain_configured(port, false);
}
static int usb4_port_wait_for_bit(struct tb_port *port, u32 offset, u32 bit,
u32 value, int timeout_msec)
{