diff options
Diffstat (limited to 'drivers/thunderbolt/switch.c')
-rw-r--r-- | drivers/thunderbolt/switch.c | 82 |
1 files changed, 75 insertions, 7 deletions
diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c index a8572f49d3ad..b63fecca6c2a 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, |