aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/thunderbolt/switch.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/thunderbolt/switch.c')
-rw-r--r--drivers/thunderbolt/switch.c82
1 files changed, 75 insertions, 7 deletions
diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c
index a8572f49d3adc4..b63fecca6c2a1f 100644
--- a/drivers/thunderbolt/switch.c
+++ b/drivers/thunderbolt/switch.c
@@ -525,6 +525,8 @@ int tb_port_state(struct tb_port *port)
/**
* tb_wait_for_port() - wait for a port to become ready
+ * @port: Port to wait
+ * @wait_if_unplugged: Wait also when port is unplugged
*
* Wait up to 1 second for a port to reach state TB_PORT_UP. If
* wait_if_unplugged is set then we also wait if the port is in state
@@ -589,6 +591,8 @@ int tb_wait_for_port(struct tb_port *port, bool wait_if_unplugged)
/**
* tb_port_add_nfc_credits() - add/remove non flow controlled credits to port
+ * @port: Port to add/remove NFC credits
+ * @credits: Credits to add/remove
*
* Change the number of NFC credits allocated to @port by @credits. To remove
* NFC credits pass a negative amount of credits.
@@ -646,6 +650,8 @@ int tb_port_set_initial_credits(struct tb_port *port, u32 credits)
/**
* tb_port_clear_counter() - clear a counter in TB_CFG_COUNTER
+ * @port: Port whose counters to clear
+ * @counter: Counter index to clear
*
* Return: Returns 0 on success or an error code on failure.
*/
@@ -718,7 +724,7 @@ int tb_port_disable(struct tb_port *port)
return __tb_port_enable(port, false);
}
-/**
+/*
* tb_init_port() - initialize a port
*
* This is a helper method for tb_switch_alloc. Does not check or initialize
@@ -1065,6 +1071,17 @@ void tb_port_lane_bonding_disable(struct tb_port *port)
tb_port_set_link_width(port, 1);
}
+static int tb_port_start_lane_initialization(struct tb_port *port)
+{
+ int ret;
+
+ if (tb_switch_is_usb4(port->sw))
+ return 0;
+
+ ret = tb_lc_start_lane_initialization(port);
+ return ret == -EINVAL ? 0 : ret;
+}
+
/**
* tb_port_is_enabled() - Is the adapter port enabled
* @port: Port to check
@@ -1302,7 +1319,7 @@ static void tb_dump_switch(const struct tb *tb, const struct tb_switch *sw)
}
/**
- * reset_switch() - reconfigure route, enable and send TB_CFG_PKG_RESET
+ * tb_switch_reset() - reconfigure route, enable and send TB_CFG_PKG_RESET
* @sw: Switch to reset
*
* Return: Returns 0 on success or an error code on failure.
@@ -1326,7 +1343,7 @@ int tb_switch_reset(struct tb_switch *sw)
return res.err;
}
-/**
+/*
* tb_plug_events_active() - enable/disable plug events on a switch
*
* Also configures a sane plug_events_delay of 255ms.
@@ -1376,6 +1393,30 @@ static ssize_t authorized_show(struct device *dev,
return sprintf(buf, "%u\n", sw->authorized);
}
+static int disapprove_switch(struct device *dev, void *not_used)
+{
+ struct tb_switch *sw;
+
+ sw = tb_to_switch(dev);
+ if (sw && sw->authorized) {
+ int ret;
+
+ /* First children */
+ ret = device_for_each_child_reverse(&sw->dev, NULL, disapprove_switch);
+ if (ret)
+ return ret;
+
+ ret = tb_domain_disapprove_switch(sw->tb, sw);
+ if (ret)
+ return ret;
+
+ sw->authorized = 0;
+ kobject_uevent(&sw->dev.kobj, KOBJ_CHANGE);
+ }
+
+ return 0;
+}
+
static int tb_switch_set_authorized(struct tb_switch *sw, unsigned int val)
{
int ret = -EINVAL;
@@ -1383,10 +1424,18 @@ static int tb_switch_set_authorized(struct tb_switch *sw, unsigned int val)
if (!mutex_trylock(&sw->tb->lock))
return restart_syscall();
- if (sw->authorized)
+ if (!!sw->authorized == !!val)
goto unlock;
switch (val) {
+ /* Disapprove switch */
+ case 0:
+ if (tb_route(sw)) {
+ ret = disapprove_switch(&sw->dev, NULL);
+ goto unlock;
+ }
+ break;
+
/* Approve switch */
case 1:
if (sw->key)
@@ -1725,7 +1774,11 @@ static umode_t switch_attr_is_visible(struct kobject *kobj,
struct device *dev = kobj_to_dev(kobj);
struct tb_switch *sw = tb_to_switch(dev);
- if (attr == &dev_attr_device.attr) {
+ if (attr == &dev_attr_authorized.attr) {
+ if (sw->tb->security_level == TB_SECURITY_NOPCIE ||
+ sw->tb->security_level == TB_SECURITY_DPONLY)
+ return 0;
+ } else if (attr == &dev_attr_device.attr) {
if (!sw->device)
return 0;
} else if (attr == &dev_attr_device_name.attr) {
@@ -1771,7 +1824,7 @@ static umode_t switch_attr_is_visible(struct kobject *kobj,
return sw->safe_mode ? 0 : attr->mode;
}
-static struct attribute_group switch_group = {
+static const struct attribute_group switch_group = {
.is_visible = switch_attr_is_visible,
.attrs = switch_attrs,
};
@@ -2606,6 +2659,7 @@ void tb_switch_remove(struct tb_switch *sw)
/**
* tb_sw_set_unplugged() - set is_unplugged on switch and downstream switches
+ * @sw: Router to mark unplugged
*/
void tb_sw_set_unplugged(struct tb_switch *sw)
{
@@ -2694,8 +2748,22 @@ int tb_switch_resume(struct tb_switch *sw)
/* check for surviving downstream switches */
tb_switch_for_each_port(sw, port) {
- if (!tb_port_has_remote(port) && !port->xdomain)
+ if (!tb_port_has_remote(port) && !port->xdomain) {
+ /*
+ * For disconnected downstream lane adapters
+ * start lane initialization now so we detect
+ * future connects.
+ */
+ if (!tb_is_upstream_port(port) && tb_port_is_null(port))
+ tb_port_start_lane_initialization(port);
continue;
+ } else if (port->xdomain) {
+ /*
+ * Start lane initialization for XDomain so the
+ * link gets re-established.
+ */
+ tb_port_start_lane_initialization(port);
+ }
if (tb_wait_for_port(port, true) <= 0) {
tb_port_warn(port,