summaryrefslogtreecommitdiff
path: root/drivers/thunderbolt
diff options
context:
space:
mode:
authorMika Westerberg <mika.westerberg@linux.intel.com>2020-04-09 14:23:32 +0300
committerMika Westerberg <mika.westerberg@linux.intel.com>2020-09-03 12:06:41 +0300
commit284652a4a49917e121277a6cacbefed9f65b94ca (patch)
treed126165f7d01a7c19443ba8aefd3ac810a700a1b /drivers/thunderbolt
parente28178bf566cf7281b513856aa71a8d41aa81154 (diff)
downloadlinux-284652a4a49917e121277a6cacbefed9f65b94ca.tar.xz
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>
Diffstat (limited to 'drivers/thunderbolt')
-rw-r--r--drivers/thunderbolt/lc.c54
-rw-r--r--drivers/thunderbolt/tb.c32
-rw-r--r--drivers/thunderbolt/tb.h4
-rw-r--r--drivers/thunderbolt/tb_regs.h3
-rw-r--r--drivers/thunderbolt/usb4.c45
5 files changed, 134 insertions, 4 deletions
diff --git a/drivers/thunderbolt/lc.c b/drivers/thunderbolt/lc.c
index 5c209a570360..44647fa1ec1c 100644
--- a/drivers/thunderbolt/lc.c
+++ b/drivers/thunderbolt/lc.c
@@ -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
diff --git a/drivers/thunderbolt/tb.c b/drivers/thunderbolt/tb.c
index 54a4daf0b1b4..602e00e0b45e 100644
--- a/drivers/thunderbolt/tb.c
+++ b/drivers/thunderbolt/tb.c
@@ -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) {
diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h
index 0a0317e56043..c80b11dba756 100644
--- a/drivers/thunderbolt/tb.h
+++ b/drivers/thunderbolt/tb.h
@@ -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,
diff --git a/drivers/thunderbolt/tb_regs.h b/drivers/thunderbolt/tb_regs.h
index fd4fc144d17f..a553be24f1c0 100644
--- a/drivers/thunderbolt/tb_regs.h
+++ b/drivers/thunderbolt/tb_regs.h
@@ -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)
diff --git a/drivers/thunderbolt/usb4.c b/drivers/thunderbolt/usb4.c
index b2677427789f..59b8b51d1fa4 100644
--- a/drivers/thunderbolt/usb4.c
+++ b/drivers/thunderbolt/usb4.c
@@ -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)
{