summaryrefslogtreecommitdiff
path: root/drivers/thunderbolt/switch.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/thunderbolt/switch.c')
-rw-r--r--drivers/thunderbolt/switch.c594
1 files changed, 161 insertions, 433 deletions
diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c
index 51e86b5171c7..7ea63bb31714 100644
--- a/drivers/thunderbolt/switch.c
+++ b/drivers/thunderbolt/switch.c
@@ -26,10 +26,6 @@ struct nvm_auth_status {
u32 status;
};
-static bool clx_enabled = true;
-module_param_named(clx, clx_enabled, bool, 0444);
-MODULE_PARM_DESC(clx, "allow low power states on the high-speed lanes (default: true)");
-
/*
* Hold NVM authentication failure status per switch This information
* needs to stay around even when the switch gets power cycled so we
@@ -727,7 +723,7 @@ static int tb_init_port(struct tb_port *port)
* can be read from the path config space. Legacy
* devices we use hard-coded value.
*/
- if (tb_switch_is_usb4(port->sw)) {
+ if (port->cap_usb4) {
struct tb_regs_hop hop;
if (!tb_port_read(port, &hop, TB_CFG_HOPS, 0, 2))
@@ -907,15 +903,23 @@ int tb_port_get_link_speed(struct tb_port *port)
speed = (val & LANE_ADP_CS_1_CURRENT_SPEED_MASK) >>
LANE_ADP_CS_1_CURRENT_SPEED_SHIFT;
- return speed == LANE_ADP_CS_1_CURRENT_SPEED_GEN3 ? 20 : 10;
+
+ switch (speed) {
+ case LANE_ADP_CS_1_CURRENT_SPEED_GEN4:
+ return 40;
+ case LANE_ADP_CS_1_CURRENT_SPEED_GEN3:
+ return 20;
+ default:
+ return 10;
+ }
}
/**
* tb_port_get_link_width() - Get current link width
* @port: Port to check (USB4 or CIO)
*
- * Returns link width. Return values can be 1 (Single-Lane), 2 (Dual-Lane)
- * or negative errno in case of failure.
+ * Returns link width. Return the link width as encoded in &enum
+ * tb_link_width or negative errno in case of failure.
*/
int tb_port_get_link_width(struct tb_port *port)
{
@@ -930,11 +934,13 @@ int tb_port_get_link_width(struct tb_port *port)
if (ret)
return ret;
+ /* Matches the values in enum tb_link_width */
return (val & LANE_ADP_CS_1_CURRENT_WIDTH_MASK) >>
LANE_ADP_CS_1_CURRENT_WIDTH_SHIFT;
}
-static bool tb_port_is_width_supported(struct tb_port *port, int width)
+static bool tb_port_is_width_supported(struct tb_port *port,
+ unsigned int width_mask)
{
u32 phy, widths;
int ret;
@@ -950,20 +956,25 @@ static bool tb_port_is_width_supported(struct tb_port *port, int width)
widths = (phy & LANE_ADP_CS_0_SUPPORTED_WIDTH_MASK) >>
LANE_ADP_CS_0_SUPPORTED_WIDTH_SHIFT;
- return !!(widths & width);
+ return widths & width_mask;
+}
+
+static bool is_gen4_link(struct tb_port *port)
+{
+ return tb_port_get_link_speed(port) > 20;
}
/**
* tb_port_set_link_width() - Set target link width of the lane adapter
* @port: Lane adapter
- * @width: Target link width (%1 or %2)
+ * @width: Target link width
*
* Sets the target link width of the lane adapter to @width. Does not
* enable/disable lane bonding. For that call tb_port_set_lane_bonding().
*
* Return: %0 in case of success and negative errno in case of error
*/
-int tb_port_set_link_width(struct tb_port *port, unsigned int width)
+int tb_port_set_link_width(struct tb_port *port, enum tb_link_width width)
{
u32 val;
int ret;
@@ -978,11 +989,14 @@ int tb_port_set_link_width(struct tb_port *port, unsigned int width)
val &= ~LANE_ADP_CS_1_TARGET_WIDTH_MASK;
switch (width) {
- case 1:
+ case TB_LINK_WIDTH_SINGLE:
+ /* Gen 4 link cannot be single */
+ if (is_gen4_link(port))
+ return -EOPNOTSUPP;
val |= LANE_ADP_CS_1_TARGET_WIDTH_SINGLE <<
LANE_ADP_CS_1_TARGET_WIDTH_SHIFT;
break;
- case 2:
+ case TB_LINK_WIDTH_DUAL:
val |= LANE_ADP_CS_1_TARGET_WIDTH_DUAL <<
LANE_ADP_CS_1_TARGET_WIDTH_SHIFT;
break;
@@ -1004,12 +1018,9 @@ int tb_port_set_link_width(struct tb_port *port, unsigned int width)
* cases one should use tb_port_lane_bonding_enable() instead to enable
* lane bonding.
*
- * As a side effect sets @port->bonding accordingly (and does the same
- * for lane 1 too).
- *
* Return: %0 in case of success and negative errno in case of error
*/
-int tb_port_set_lane_bonding(struct tb_port *port, bool bonding)
+static int tb_port_set_lane_bonding(struct tb_port *port, bool bonding)
{
u32 val;
int ret;
@@ -1027,19 +1038,8 @@ int tb_port_set_lane_bonding(struct tb_port *port, bool bonding)
else
val &= ~LANE_ADP_CS_1_LB;
- ret = tb_port_write(port, &val, TB_CFG_PORT,
- port->cap_phy + LANE_ADP_CS_1, 1);
- if (ret)
- return ret;
-
- /*
- * When lane 0 bonding is set it will affect lane 1 too so
- * update both.
- */
- port->bonded = bonding;
- port->dual_link_port->bonded = bonding;
-
- return 0;
+ return tb_port_write(port, &val, TB_CFG_PORT,
+ port->cap_phy + LANE_ADP_CS_1, 1);
}
/**
@@ -1056,36 +1056,52 @@ int tb_port_set_lane_bonding(struct tb_port *port, bool bonding)
*/
int tb_port_lane_bonding_enable(struct tb_port *port)
{
+ enum tb_link_width width;
int ret;
/*
* Enable lane bonding for both links if not already enabled by
* for example the boot firmware.
*/
- ret = tb_port_get_link_width(port);
- if (ret == 1) {
- ret = tb_port_set_link_width(port, 2);
+ width = tb_port_get_link_width(port);
+ if (width == TB_LINK_WIDTH_SINGLE) {
+ ret = tb_port_set_link_width(port, TB_LINK_WIDTH_DUAL);
if (ret)
goto err_lane0;
}
- ret = tb_port_get_link_width(port->dual_link_port);
- if (ret == 1) {
- ret = tb_port_set_link_width(port->dual_link_port, 2);
+ width = tb_port_get_link_width(port->dual_link_port);
+ if (width == TB_LINK_WIDTH_SINGLE) {
+ ret = tb_port_set_link_width(port->dual_link_port,
+ TB_LINK_WIDTH_DUAL);
if (ret)
goto err_lane0;
}
- ret = tb_port_set_lane_bonding(port, true);
- if (ret)
- goto err_lane1;
+ /*
+ * Only set bonding if the link was not already bonded. This
+ * avoids the lane adapter to re-enter bonding state.
+ */
+ if (width == TB_LINK_WIDTH_SINGLE) {
+ ret = tb_port_set_lane_bonding(port, true);
+ if (ret)
+ goto err_lane1;
+ }
+
+ /*
+ * When lane 0 bonding is set it will affect lane 1 too so
+ * update both.
+ */
+ port->bonded = true;
+ port->dual_link_port->bonded = true;
return 0;
err_lane1:
- tb_port_set_link_width(port->dual_link_port, 1);
+ tb_port_set_link_width(port->dual_link_port, TB_LINK_WIDTH_SINGLE);
err_lane0:
- tb_port_set_link_width(port, 1);
+ tb_port_set_link_width(port, TB_LINK_WIDTH_SINGLE);
+
return ret;
}
@@ -1099,27 +1115,34 @@ err_lane0:
void tb_port_lane_bonding_disable(struct tb_port *port)
{
tb_port_set_lane_bonding(port, false);
- tb_port_set_link_width(port->dual_link_port, 1);
- tb_port_set_link_width(port, 1);
+ tb_port_set_link_width(port->dual_link_port, TB_LINK_WIDTH_SINGLE);
+ tb_port_set_link_width(port, TB_LINK_WIDTH_SINGLE);
+ port->dual_link_port->bonded = false;
+ port->bonded = false;
}
/**
* tb_port_wait_for_link_width() - Wait until link reaches specific width
* @port: Port to wait for
- * @width: Expected link width (%1 or %2)
+ * @width_mask: Expected link width mask
* @timeout_msec: Timeout in ms how long to wait
*
* Should be used after both ends of the link have been bonded (or
* bonding has been disabled) to wait until the link actually reaches
- * the expected state. Returns %-ETIMEDOUT if the @width was not reached
- * within the given timeout, %0 if it did.
+ * the expected state. Returns %-ETIMEDOUT if the width was not reached
+ * within the given timeout, %0 if it did. Can be passed a mask of
+ * expected widths and succeeds if any of the widths is reached.
*/
-int tb_port_wait_for_link_width(struct tb_port *port, int width,
+int tb_port_wait_for_link_width(struct tb_port *port, unsigned int width_mask,
int timeout_msec)
{
ktime_t timeout = ktime_add_ms(ktime_get(), timeout_msec);
int ret;
+ /* Gen 4 link does not support single lane */
+ if ((width_mask & TB_LINK_WIDTH_SINGLE) && is_gen4_link(port))
+ return -EOPNOTSUPP;
+
do {
ret = tb_port_get_link_width(port);
if (ret < 0) {
@@ -1130,7 +1153,7 @@ int tb_port_wait_for_link_width(struct tb_port *port, int width,
*/
if (ret != -EACCES)
return ret;
- } else if (ret == width) {
+ } else if (ret & width_mask) {
return 0;
}
@@ -1183,135 +1206,6 @@ int tb_port_update_credits(struct tb_port *port)
return tb_port_do_update_credits(port->dual_link_port);
}
-static int __tb_port_pm_secondary_set(struct tb_port *port, bool secondary)
-{
- u32 phy;
- int ret;
-
- ret = tb_port_read(port, &phy, TB_CFG_PORT,
- port->cap_phy + LANE_ADP_CS_1, 1);
- if (ret)
- return ret;
-
- if (secondary)
- phy |= LANE_ADP_CS_1_PMS;
- else
- phy &= ~LANE_ADP_CS_1_PMS;
-
- return tb_port_write(port, &phy, TB_CFG_PORT,
- port->cap_phy + LANE_ADP_CS_1, 1);
-}
-
-static int tb_port_pm_secondary_enable(struct tb_port *port)
-{
- return __tb_port_pm_secondary_set(port, true);
-}
-
-static int tb_port_pm_secondary_disable(struct tb_port *port)
-{
- return __tb_port_pm_secondary_set(port, false);
-}
-
-/* Called for USB4 or Titan Ridge routers only */
-static bool tb_port_clx_supported(struct tb_port *port, unsigned int clx_mask)
-{
- u32 val, mask = 0;
- bool ret;
-
- /* Don't enable CLx in case of two single-lane links */
- if (!port->bonded && port->dual_link_port)
- return false;
-
- /* Don't enable CLx in case of inter-domain link */
- if (port->xdomain)
- return false;
-
- if (tb_switch_is_usb4(port->sw)) {
- if (!usb4_port_clx_supported(port))
- return false;
- } else if (!tb_lc_is_clx_supported(port)) {
- return false;
- }
-
- if (clx_mask & TB_CL1) {
- /* CL0s and CL1 are enabled and supported together */
- mask |= LANE_ADP_CS_0_CL0S_SUPPORT | LANE_ADP_CS_0_CL1_SUPPORT;
- }
- if (clx_mask & TB_CL2)
- mask |= LANE_ADP_CS_0_CL2_SUPPORT;
-
- ret = tb_port_read(port, &val, TB_CFG_PORT,
- port->cap_phy + LANE_ADP_CS_0, 1);
- if (ret)
- return false;
-
- return !!(val & mask);
-}
-
-static int __tb_port_clx_set(struct tb_port *port, enum tb_clx clx, bool enable)
-{
- u32 phy, mask;
- int ret;
-
- /* CL0s and CL1 are enabled and supported together */
- if (clx == TB_CL1)
- mask = LANE_ADP_CS_1_CL0S_ENABLE | LANE_ADP_CS_1_CL1_ENABLE;
- else
- /* For now we support only CL0s and CL1. Not CL2 */
- return -EOPNOTSUPP;
-
- ret = tb_port_read(port, &phy, TB_CFG_PORT,
- port->cap_phy + LANE_ADP_CS_1, 1);
- if (ret)
- return ret;
-
- if (enable)
- phy |= mask;
- else
- phy &= ~mask;
-
- return tb_port_write(port, &phy, TB_CFG_PORT,
- port->cap_phy + LANE_ADP_CS_1, 1);
-}
-
-static int tb_port_clx_disable(struct tb_port *port, enum tb_clx clx)
-{
- return __tb_port_clx_set(port, clx, false);
-}
-
-static int tb_port_clx_enable(struct tb_port *port, enum tb_clx clx)
-{
- return __tb_port_clx_set(port, clx, true);
-}
-
-/**
- * tb_port_is_clx_enabled() - Is given CL state enabled
- * @port: USB4 port to check
- * @clx_mask: Mask of CL states to check
- *
- * Returns true if any of the given CL states is enabled for @port.
- */
-bool tb_port_is_clx_enabled(struct tb_port *port, unsigned int clx_mask)
-{
- u32 val, mask = 0;
- int ret;
-
- if (!tb_port_clx_supported(port, clx_mask))
- return false;
-
- if (clx_mask & TB_CL1)
- mask |= LANE_ADP_CS_1_CL0S_ENABLE | LANE_ADP_CS_1_CL1_ENABLE;
- if (clx_mask & TB_CL2)
- mask |= LANE_ADP_CS_1_CL2_ENABLE;
-
- ret = tb_port_read(port, &val, TB_CFG_PORT,
- port->cap_phy + LANE_ADP_CS_1, 1);
- if (ret)
- return false;
-
- return !!(val & mask);
-}
-
static int tb_port_start_lane_initialization(struct tb_port *port)
{
int ret;
@@ -1911,20 +1805,57 @@ static ssize_t speed_show(struct device *dev, struct device_attribute *attr,
static DEVICE_ATTR(rx_speed, 0444, speed_show, NULL);
static DEVICE_ATTR(tx_speed, 0444, speed_show, NULL);
-static ssize_t lanes_show(struct device *dev, struct device_attribute *attr,
- char *buf)
+static ssize_t rx_lanes_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
{
struct tb_switch *sw = tb_to_switch(dev);
+ unsigned int width;
+
+ switch (sw->link_width) {
+ case TB_LINK_WIDTH_SINGLE:
+ case TB_LINK_WIDTH_ASYM_TX:
+ width = 1;
+ break;
+ case TB_LINK_WIDTH_DUAL:
+ width = 2;
+ break;
+ case TB_LINK_WIDTH_ASYM_RX:
+ width = 3;
+ break;
+ default:
+ WARN_ON_ONCE(1);
+ return -EINVAL;
+ }
- return sysfs_emit(buf, "%u\n", sw->link_width);
+ return sysfs_emit(buf, "%u\n", width);
}
+static DEVICE_ATTR(rx_lanes, 0444, rx_lanes_show, NULL);
-/*
- * Currently link has same amount of lanes both directions (1 or 2) but
- * expose them separately to allow possible asymmetric links in the future.
- */
-static DEVICE_ATTR(rx_lanes, 0444, lanes_show, NULL);
-static DEVICE_ATTR(tx_lanes, 0444, lanes_show, NULL);
+static ssize_t tx_lanes_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct tb_switch *sw = tb_to_switch(dev);
+ unsigned int width;
+
+ switch (sw->link_width) {
+ case TB_LINK_WIDTH_SINGLE:
+ case TB_LINK_WIDTH_ASYM_RX:
+ width = 1;
+ break;
+ case TB_LINK_WIDTH_DUAL:
+ width = 2;
+ break;
+ case TB_LINK_WIDTH_ASYM_TX:
+ width = 3;
+ break;
+ default:
+ WARN_ON_ONCE(1);
+ return -EINVAL;
+ }
+
+ return sysfs_emit(buf, "%u\n", width);
+}
+static DEVICE_ATTR(tx_lanes, 0444, tx_lanes_show, NULL);
static ssize_t nvm_authenticate_show(struct device *dev,
struct device_attribute *attr, char *buf)
@@ -2189,8 +2120,9 @@ static int tb_switch_uevent(const struct device *dev, struct kobj_uevent_env *en
const struct tb_switch *sw = tb_to_switch(dev);
const char *type;
- if (sw->config.thunderbolt_version == USB4_VERSION_1_0) {
- if (add_uevent_var(env, "USB4_VERSION=1.0"))
+ if (tb_switch_is_usb4(sw)) {
+ if (add_uevent_var(env, "USB4_VERSION=%u.0",
+ usb4_switch_version(sw)))
return -ENOMEM;
}
@@ -2498,9 +2430,13 @@ int tb_switch_configure(struct tb_switch *sw)
/*
* For USB4 devices, we need to program the CM version
* accordingly so that it knows to expose all the
- * additional capabilities.
+ * additional capabilities. Program it according to USB4
+ * version to avoid changing existing (v1) routers behaviour.
*/
- sw->config.cmuv = USB4_VERSION_1_0;
+ if (usb4_switch_version(sw) < 2)
+ sw->config.cmuv = ROUTER_CS_4_CMUV_V1;
+ else
+ sw->config.cmuv = ROUTER_CS_4_CMUV_V2;
sw->config.plug_events_delay = 0xa;
/* Enumerate the switch */
@@ -2530,6 +2466,22 @@ int tb_switch_configure(struct tb_switch *sw)
return tb_plug_events_active(sw, true);
}
+/**
+ * tb_switch_configuration_valid() - Set the tunneling configuration to be valid
+ * @sw: Router to configure
+ *
+ * Needs to be called before any tunnels can be setup through the
+ * router. Can be called to any router.
+ *
+ * Returns %0 in success and negative errno otherwise.
+ */
+int tb_switch_configuration_valid(struct tb_switch *sw)
+{
+ if (tb_switch_is_usb4(sw))
+ return usb4_switch_configuration_valid(sw);
+ return 0;
+}
+
static int tb_switch_set_uuid(struct tb_switch *sw)
{
bool uid = false;
@@ -2754,9 +2706,9 @@ static int tb_switch_update_link_attributes(struct tb_switch *sw)
*/
int tb_switch_lane_bonding_enable(struct tb_switch *sw)
{
- struct tb_switch *parent = tb_to_switch(sw->dev.parent);
struct tb_port *up, *down;
u64 route = tb_route(sw);
+ unsigned int width_mask;
int ret;
if (!route)
@@ -2766,10 +2718,10 @@ int tb_switch_lane_bonding_enable(struct tb_switch *sw)
return 0;
up = tb_upstream_port(sw);
- down = tb_port_at(route, parent);
+ down = tb_switch_downstream_port(sw);
- if (!tb_port_is_width_supported(up, 2) ||
- !tb_port_is_width_supported(down, 2))
+ if (!tb_port_is_width_supported(up, TB_LINK_WIDTH_DUAL) ||
+ !tb_port_is_width_supported(down, TB_LINK_WIDTH_DUAL))
return 0;
ret = tb_port_lane_bonding_enable(up);
@@ -2785,7 +2737,11 @@ int tb_switch_lane_bonding_enable(struct tb_switch *sw)
return ret;
}
- ret = tb_port_wait_for_link_width(down, 2, 100);
+ /* Any of the widths are all bonded */
+ width_mask = TB_LINK_WIDTH_DUAL | TB_LINK_WIDTH_ASYM_TX |
+ TB_LINK_WIDTH_ASYM_RX;
+
+ ret = tb_port_wait_for_link_width(down, width_mask, 100);
if (ret) {
tb_port_warn(down, "timeout enabling lane bonding\n");
return ret;
@@ -2808,8 +2764,8 @@ int tb_switch_lane_bonding_enable(struct tb_switch *sw)
*/
void tb_switch_lane_bonding_disable(struct tb_switch *sw)
{
- struct tb_switch *parent = tb_to_switch(sw->dev.parent);
struct tb_port *up, *down;
+ int ret;
if (!tb_route(sw))
return;
@@ -2818,7 +2774,7 @@ void tb_switch_lane_bonding_disable(struct tb_switch *sw)
if (!up->bonded)
return;
- down = tb_port_at(tb_route(sw), parent);
+ down = tb_switch_downstream_port(sw);
tb_port_lane_bonding_disable(up);
tb_port_lane_bonding_disable(down);
@@ -2827,7 +2783,8 @@ void tb_switch_lane_bonding_disable(struct tb_switch *sw)
* It is fine if we get other errors as the router might have
* been unplugged.
*/
- if (tb_port_wait_for_link_width(down, 1, 100) == -ETIMEDOUT)
+ ret = tb_port_wait_for_link_width(down, TB_LINK_WIDTH_SINGLE, 100);
+ if (ret == -ETIMEDOUT)
tb_sw_warn(sw, "timeout disabling lane bonding\n");
tb_port_update_credits(down);
@@ -2994,6 +2951,10 @@ int tb_switch_add(struct tb_switch *sw)
if (ret)
return ret;
+ ret = tb_switch_clx_init(sw);
+ if (ret)
+ return ret;
+
ret = tb_switch_tmu_init(sw);
if (ret)
return ret;
@@ -3246,13 +3207,8 @@ void tb_switch_suspend(struct tb_switch *sw, bool runtime)
/*
* Actually only needed for Titan Ridge but for simplicity can be
* done for USB4 device too as CLx is re-enabled at resume.
- * CL0s and CL1 are enabled and supported together.
*/
- if (tb_switch_is_clx_enabled(sw, TB_CL1)) {
- if (tb_switch_disable_clx(sw, TB_CL1))
- tb_sw_warn(sw, "failed to disable %s on upstream port\n",
- tb_switch_clx_name(TB_CL1));
- }
+ tb_switch_clx_disable(sw);
err = tb_plug_events_active(sw, false);
if (err)
@@ -3474,234 +3430,6 @@ struct tb_port *tb_switch_find_port(struct tb_switch *sw,
return NULL;
}
-static int tb_switch_pm_secondary_resolve(struct tb_switch *sw)
-{
- struct tb_switch *parent = tb_switch_parent(sw);
- struct tb_port *up, *down;
- int ret;
-
- if (!tb_route(sw))
- return 0;
-
- up = tb_upstream_port(sw);
- down = tb_port_at(tb_route(sw), parent);
- ret = tb_port_pm_secondary_enable(up);
- if (ret)
- return ret;
-
- return tb_port_pm_secondary_disable(down);
-}
-
-static int __tb_switch_enable_clx(struct tb_switch *sw, enum tb_clx clx)
-{
- struct tb_switch *parent = tb_switch_parent(sw);
- bool up_clx_support, down_clx_support;
- struct tb_port *up, *down;
- int ret;
-
- if (!tb_switch_is_clx_supported(sw))
- return 0;
-
- /*
- * Enable CLx for host router's downstream port as part of the
- * downstream router enabling procedure.
- */
- if (!tb_route(sw))
- return 0;
-
- /* Enable CLx only for first hop router (depth = 1) */
- if (tb_route(parent))
- return 0;
-
- ret = tb_switch_pm_secondary_resolve(sw);
- if (ret)
- return ret;
-
- up = tb_upstream_port(sw);
- down = tb_port_at(tb_route(sw), parent);
-
- up_clx_support = tb_port_clx_supported(up, clx);
- down_clx_support = tb_port_clx_supported(down, clx);
-
- tb_port_dbg(up, "%s %ssupported\n", tb_switch_clx_name(clx),
- up_clx_support ? "" : "not ");
- tb_port_dbg(down, "%s %ssupported\n", tb_switch_clx_name(clx),
- down_clx_support ? "" : "not ");
-
- if (!up_clx_support || !down_clx_support)
- return -EOPNOTSUPP;
-
- ret = tb_port_clx_enable(up, clx);
- if (ret)
- return ret;
-
- ret = tb_port_clx_enable(down, clx);
- if (ret) {
- tb_port_clx_disable(up, clx);
- return ret;
- }
-
- ret = tb_switch_mask_clx_objections(sw);
- if (ret) {
- tb_port_clx_disable(up, clx);
- tb_port_clx_disable(down, clx);
- return ret;
- }
-
- sw->clx = clx;
-
- tb_port_dbg(up, "%s enabled\n", tb_switch_clx_name(clx));
- return 0;
-}
-
-/**
- * tb_switch_enable_clx() - Enable CLx on upstream port of specified router
- * @sw: Router to enable CLx for
- * @clx: The CLx state to enable
- *
- * Enable CLx state only for first hop router. That is the most common
- * use-case, that is intended for better thermal management, and so helps
- * to improve performance. CLx is enabled only if both sides of the link
- * support CLx, and if both sides of the link are not configured as two
- * single lane links and only if the link is not inter-domain link. The
- * complete set of conditions is described in CM Guide 1.0 section 8.1.
- *
- * Return: Returns 0 on success or an error code on failure.
- */
-int tb_switch_enable_clx(struct tb_switch *sw, enum tb_clx clx)
-{
- struct tb_switch *root_sw = sw->tb->root_switch;
-
- if (!clx_enabled)
- return 0;
-
- /*
- * CLx is not enabled and validated on Intel USB4 platforms before
- * Alder Lake.
- */
- if (root_sw->generation < 4 || tb_switch_is_tiger_lake(root_sw))
- return 0;
-
- switch (clx) {
- case TB_CL1:
- /* CL0s and CL1 are enabled and supported together */
- return __tb_switch_enable_clx(sw, clx);
-
- default:
- return -EOPNOTSUPP;
- }
-}
-
-static int __tb_switch_disable_clx(struct tb_switch *sw, enum tb_clx clx)
-{
- struct tb_switch *parent = tb_switch_parent(sw);
- struct tb_port *up, *down;
- int ret;
-
- if (!tb_switch_is_clx_supported(sw))
- return 0;
-
- /*
- * Disable CLx for host router's downstream port as part of the
- * downstream router enabling procedure.
- */
- if (!tb_route(sw))
- return 0;
-
- /* Disable CLx only for first hop router (depth = 1) */
- if (tb_route(parent))
- return 0;
-
- up = tb_upstream_port(sw);
- down = tb_port_at(tb_route(sw), parent);
- ret = tb_port_clx_disable(up, clx);
- if (ret)
- return ret;
-
- ret = tb_port_clx_disable(down, clx);
- if (ret)
- return ret;
-
- sw->clx = TB_CLX_DISABLE;
-
- tb_port_dbg(up, "%s disabled\n", tb_switch_clx_name(clx));
- return 0;
-}
-
-/**
- * tb_switch_disable_clx() - Disable CLx on upstream port of specified router
- * @sw: Router to disable CLx for
- * @clx: The CLx state to disable
- *
- * Return: Returns 0 on success or an error code on failure.
- */
-int tb_switch_disable_clx(struct tb_switch *sw, enum tb_clx clx)
-{
- if (!clx_enabled)
- return 0;
-
- switch (clx) {
- case TB_CL1:
- /* CL0s and CL1 are enabled and supported together */
- return __tb_switch_disable_clx(sw, clx);
-
- default:
- return -EOPNOTSUPP;
- }
-}
-
-/**
- * tb_switch_mask_clx_objections() - Mask CLx objections for a router
- * @sw: Router to mask objections for
- *
- * Mask the objections coming from the second depth routers in order to
- * stop these objections from interfering with the CLx states of the first
- * depth link.
- */
-int tb_switch_mask_clx_objections(struct tb_switch *sw)
-{
- int up_port = sw->config.upstream_port_number;
- u32 offset, val[2], mask_obj, unmask_obj;
- int ret, i;
-
- /* Only Titan Ridge of pre-USB4 devices support CLx states */
- if (!tb_switch_is_titan_ridge(sw))
- return 0;
-
- if (!tb_route(sw))
- return 0;
-
- /*
- * In Titan Ridge there are only 2 dual-lane Thunderbolt ports:
- * Port A consists of lane adapters 1,2 and
- * Port B consists of lane adapters 3,4
- * If upstream port is A, (lanes are 1,2), we mask objections from
- * port B (lanes 3,4) and unmask objections from Port A and vice-versa.
- */
- if (up_port == 1) {
- mask_obj = TB_LOW_PWR_C0_PORT_B_MASK;
- unmask_obj = TB_LOW_PWR_C1_PORT_A_MASK;
- offset = TB_LOW_PWR_C1_CL1;
- } else {
- mask_obj = TB_LOW_PWR_C1_PORT_A_MASK;
- unmask_obj = TB_LOW_PWR_C0_PORT_B_MASK;
- offset = TB_LOW_PWR_C3_CL1;
- }
-
- ret = tb_sw_read(sw, &val, TB_CFG_SWITCH,
- sw->cap_lp + offset, ARRAY_SIZE(val));
- if (ret)
- return ret;
-
- for (i = 0; i < ARRAY_SIZE(val); i++) {
- val[i] |= mask_obj;
- val[i] &= ~unmask_obj;
- }
-
- return tb_sw_write(sw, &val, TB_CFG_SWITCH,
- sw->cap_lp + offset, ARRAY_SIZE(val));
-}
-
/*
* Can be used for read/write a specified PCIe bridge for any Thunderbolt 3
* device. For now used only for Titan Ridge.