diff options
Diffstat (limited to 'drivers/thunderbolt')
-rw-r--r-- | drivers/thunderbolt/debugfs.c | 7 | ||||
-rw-r--r-- | drivers/thunderbolt/icm.c | 1 | ||||
-rw-r--r-- | drivers/thunderbolt/retimer.c | 12 | ||||
-rw-r--r-- | drivers/thunderbolt/switch.c | 48 | ||||
-rw-r--r-- | drivers/thunderbolt/tb.c | 19 | ||||
-rw-r--r-- | drivers/thunderbolt/tb.h | 3 | ||||
-rw-r--r-- | drivers/thunderbolt/tb_msgs.h | 6 | ||||
-rw-r--r-- | drivers/thunderbolt/trace.h | 13 | ||||
-rw-r--r-- | drivers/thunderbolt/tunnel.c | 39 | ||||
-rw-r--r-- | drivers/thunderbolt/usb4.c | 35 | ||||
-rw-r--r-- | drivers/thunderbolt/xdomain.c | 2 |
11 files changed, 118 insertions, 67 deletions
diff --git a/drivers/thunderbolt/debugfs.c b/drivers/thunderbolt/debugfs.c index e324cd899719..70b52aac3d97 100644 --- a/drivers/thunderbolt/debugfs.c +++ b/drivers/thunderbolt/debugfs.c @@ -943,8 +943,9 @@ static void margining_port_init(struct tb_port *port) debugfs_create_file("run", 0600, dir, port, &margining_run_fops); debugfs_create_file("results", 0600, dir, port, &margining_results_fops); debugfs_create_file("test", 0600, dir, port, &margining_test_fops); - if (independent_voltage_margins(usb4) || - (supports_time(usb4) && independent_time_margins(usb4))) + if (independent_voltage_margins(usb4) == USB4_MARGIN_CAP_0_VOLTAGE_HL || + (supports_time(usb4) && + independent_time_margins(usb4) == USB4_MARGIN_CAP_1_TIME_LR)) debugfs_create_file("margin", 0600, dir, port, &margining_margin_fops); } @@ -1346,7 +1347,7 @@ static int switch_basic_regs_show(struct tb_switch *sw, struct seq_file *s) if (tb_switch_is_usb4(sw)) dwords = ARRAY_SIZE(data); else - dwords = 7; + dwords = 5; ret = tb_sw_read(sw, data, TB_CFG_SWITCH, 0, dwords); if (ret) diff --git a/drivers/thunderbolt/icm.c b/drivers/thunderbolt/icm.c index baf10d099c77..7859bccc592d 100644 --- a/drivers/thunderbolt/icm.c +++ b/drivers/thunderbolt/icm.c @@ -2532,6 +2532,7 @@ struct tb *icm_probe(struct tb_nhi *nhi) case PCI_DEVICE_ID_INTEL_MAPLE_RIDGE_2C_NHI: case PCI_DEVICE_ID_INTEL_MAPLE_RIDGE_4C_NHI: + icm->can_upgrade_nvm = true; icm->is_supported = icm_tgl_is_supported; icm->get_mode = icm_ar_get_mode; icm->driver_ready = icm_tr_driver_ready; diff --git a/drivers/thunderbolt/retimer.c b/drivers/thunderbolt/retimer.c index 6bb49bdcd6c1..6eaaa5074ce8 100644 --- a/drivers/thunderbolt/retimer.c +++ b/drivers/thunderbolt/retimer.c @@ -199,8 +199,10 @@ static void tb_retimer_nvm_authenticate_status(struct tb_port *port, u32 *status * If the retimer has it set, store it for the new retimer * device instance. */ - for (i = 1; i <= TB_MAX_RETIMER_INDEX; i++) - usb4_port_retimer_nvm_authenticate_status(port, i, &status[i]); + for (i = 1; i <= TB_MAX_RETIMER_INDEX; i++) { + if (usb4_port_retimer_nvm_authenticate_status(port, i, &status[i])) + break; + } } static void tb_retimer_set_inbound_sbtx(struct tb_port *port) @@ -234,8 +236,10 @@ static void tb_retimer_unset_inbound_sbtx(struct tb_port *port) tb_port_dbg(port, "disabling sideband transactions\n"); - for (i = TB_MAX_RETIMER_INDEX; i >= 1; i--) - usb4_port_retimer_unset_inbound_sbtx(port, i); + for (i = TB_MAX_RETIMER_INDEX; i >= 1; i--) { + if (usb4_port_retimer_unset_inbound_sbtx(port, i)) + break; + } } static ssize_t nvm_authenticate_store(struct device *dev, diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c index 6ffc4e81ffed..326433df5880 100644 --- a/drivers/thunderbolt/switch.c +++ b/drivers/thunderbolt/switch.c @@ -3180,22 +3180,29 @@ void tb_switch_unconfigure_link(struct tb_switch *sw) { struct tb_port *up, *down; - if (sw->is_unplugged) - return; if (!tb_route(sw) || tb_switch_is_icm(sw)) return; + /* + * Unconfigure downstream port so that wake-on-connect can be + * configured after router unplug. No need to unconfigure upstream port + * since its router is unplugged. + */ up = tb_upstream_port(sw); - if (tb_switch_is_usb4(up->sw)) - usb4_port_unconfigure(up); - else - tb_lc_unconfigure_port(up); - down = up->remote; if (tb_switch_is_usb4(down->sw)) usb4_port_unconfigure(down); else tb_lc_unconfigure_port(down); + + if (sw->is_unplugged) + return; + + up = tb_upstream_port(sw); + if (tb_switch_is_usb4(up->sw)) + usb4_port_unconfigure(up); + else + tb_lc_unconfigure_port(up); } static void tb_switch_credits_init(struct tb_switch *sw) @@ -3441,7 +3448,26 @@ static int tb_switch_set_wake(struct tb_switch *sw, unsigned int flags) return tb_lc_set_wake(sw, flags); } -int tb_switch_resume(struct tb_switch *sw) +static void tb_switch_check_wakes(struct tb_switch *sw) +{ + if (device_may_wakeup(&sw->dev)) { + if (tb_switch_is_usb4(sw)) + usb4_switch_check_wakes(sw); + } +} + +/** + * tb_switch_resume() - Resume a switch after sleep + * @sw: Switch to resume + * @runtime: Is this resume from runtime suspend or system sleep + * + * Resumes and re-enumerates router (and all its children), if still plugged + * after suspend. Don't enumerate device router whose UID was changed during + * suspend. If this is resume from system sleep, notifies PM core about the + * wakes occurred during suspend. Disables all wakes, except USB4 wake of + * upstream port for USB4 routers that shall be always enabled. + */ +int tb_switch_resume(struct tb_switch *sw, bool runtime) { struct tb_port *port; int err; @@ -3490,6 +3516,9 @@ int tb_switch_resume(struct tb_switch *sw) if (err) return err; + if (!runtime) + tb_switch_check_wakes(sw); + /* Disable wakes */ tb_switch_set_wake(sw, 0); @@ -3519,7 +3548,8 @@ int tb_switch_resume(struct tb_switch *sw) */ if (tb_port_unlock(port)) tb_port_warn(port, "failed to unlock port\n"); - if (port->remote && tb_switch_resume(port->remote->sw)) { + if (port->remote && + tb_switch_resume(port->remote->sw, runtime)) { tb_port_warn(port, "lost during suspend, disconnecting\n"); tb_sw_set_unplugged(port->remote->sw); diff --git a/drivers/thunderbolt/tb.c b/drivers/thunderbolt/tb.c index c5ce7a694b27..10e719dd837c 100644 --- a/drivers/thunderbolt/tb.c +++ b/drivers/thunderbolt/tb.c @@ -498,8 +498,9 @@ static struct tb_tunnel *tb_find_first_usb3_tunnel(struct tb *tb, * @consumed_down: Consumed downstream bandwidth (Mb/s) * * Calculates consumed USB3 and PCIe bandwidth at @port between path - * from @src_port to @dst_port. Does not take tunnel starting from - * @src_port and ending from @src_port into account. + * from @src_port to @dst_port. Does not take USB3 tunnel starting from + * @src_port and ending on @src_port into account because that bandwidth is + * already included in as part of the "first hop" USB3 tunnel. */ static int tb_consumed_usb3_pcie_bandwidth(struct tb *tb, struct tb_port *src_port, @@ -514,8 +515,8 @@ static int tb_consumed_usb3_pcie_bandwidth(struct tb *tb, *consumed_up = *consumed_down = 0; tunnel = tb_find_first_usb3_tunnel(tb, src_port, dst_port); - if (tunnel && tunnel->src_port != src_port && - tunnel->dst_port != dst_port) { + if (tunnel && !tb_port_is_usb3_down(src_port) && + !tb_port_is_usb3_up(dst_port)) { int ret; ret = tb_tunnel_consumed_bandwidth(tunnel, consumed_up, @@ -1801,6 +1802,12 @@ static struct tb_port *tb_find_dp_out(struct tb *tb, struct tb_port *in) continue; } + /* Needs to be on different routers */ + if (in->sw == port->sw) { + tb_port_dbg(port, "skipping DP OUT on same router\n"); + continue; + } + tb_port_dbg(port, "DP OUT available\n"); /* @@ -2936,7 +2943,7 @@ static int tb_resume_noirq(struct tb *tb) if (!tb_switch_is_usb4(tb->root_switch)) tb_switch_reset(tb->root_switch); - tb_switch_resume(tb->root_switch); + tb_switch_resume(tb->root_switch, false); tb_free_invalid_tunnels(tb); tb_free_unplugged_children(tb->root_switch); tb_restore_children(tb->root_switch); @@ -3062,7 +3069,7 @@ static int tb_runtime_resume(struct tb *tb) struct tb_tunnel *tunnel, *n; mutex_lock(&tb->lock); - tb_switch_resume(tb->root_switch); + tb_switch_resume(tb->root_switch, true); tb_free_invalid_tunnels(tb); tb_restore_children(tb->root_switch); list_for_each_entry_safe(tunnel, n, &tcm->tunnel_list, list) diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h index feed8ecaf712..18aae4ccaed5 100644 --- a/drivers/thunderbolt/tb.h +++ b/drivers/thunderbolt/tb.h @@ -827,7 +827,7 @@ int tb_switch_configuration_valid(struct tb_switch *sw); int tb_switch_add(struct tb_switch *sw); void tb_switch_remove(struct tb_switch *sw); void tb_switch_suspend(struct tb_switch *sw, bool runtime); -int tb_switch_resume(struct tb_switch *sw); +int tb_switch_resume(struct tb_switch *sw, bool runtime); int tb_switch_reset(struct tb_switch *sw); int tb_switch_wait_for_bit(struct tb_switch *sw, u32 offset, u32 bit, u32 value, int timeout_msec); @@ -1288,6 +1288,7 @@ static inline bool tb_switch_is_usb4(const struct tb_switch *sw) return usb4_switch_version(sw) > 0; } +void usb4_switch_check_wakes(struct tb_switch *sw); int usb4_switch_setup(struct tb_switch *sw); int usb4_switch_configuration_valid(struct tb_switch *sw); int usb4_switch_read_uid(struct tb_switch *sw, u64 *uid); diff --git a/drivers/thunderbolt/tb_msgs.h b/drivers/thunderbolt/tb_msgs.h index cd750e4b3440..a1670a96cbdc 100644 --- a/drivers/thunderbolt/tb_msgs.h +++ b/drivers/thunderbolt/tb_msgs.h @@ -98,12 +98,6 @@ struct cfg_reset_pkg { struct tb_cfg_header header; } __packed; -/* TB_CFG_PKG_PREPARE_TO_SLEEP */ -struct cfg_pts_pkg { - struct tb_cfg_header header; - u32 data; -} __packed; - /* ICM messages */ enum icm_pkg_code { diff --git a/drivers/thunderbolt/trace.h b/drivers/thunderbolt/trace.h index 4dccfcf7af6a..6d0776514d12 100644 --- a/drivers/thunderbolt/trace.h +++ b/drivers/thunderbolt/trace.h @@ -87,23 +87,32 @@ static inline const char *show_data(struct trace_seq *p, u8 type, const char *prefix = ""; int i; - show_route(p, data); - switch (type) { case TB_CFG_PKG_READ: case TB_CFG_PKG_WRITE: + show_route(p, data); show_data_read_write(p, data); break; case TB_CFG_PKG_ERROR: + show_route(p, data); show_data_error(p, data); break; case TB_CFG_PKG_EVENT: + show_route(p, data); show_data_event(p, data); break; + case TB_CFG_PKG_ICM_EVENT: + case TB_CFG_PKG_ICM_CMD: + case TB_CFG_PKG_ICM_RESP: + /* ICM messages always target the host router */ + trace_seq_puts(p, "route=0, "); + break; + default: + show_route(p, data); break; } diff --git a/drivers/thunderbolt/tunnel.c b/drivers/thunderbolt/tunnel.c index cb6609a56a03..41cf6378ad25 100644 --- a/drivers/thunderbolt/tunnel.c +++ b/drivers/thunderbolt/tunnel.c @@ -1435,10 +1435,10 @@ err_free: * @in: DP in adapter port * @out: DP out adapter port * @link_nr: Preferred lane adapter when the link is not bonded - * @max_up: Maximum available upstream bandwidth for the DP tunnel (%0 - * if not limited) - * @max_down: Maximum available downstream bandwidth for the DP tunnel - * (%0 if not limited) + * @max_up: Maximum available upstream bandwidth for the DP tunnel. + * %0 if no available bandwidth. + * @max_down: Maximum available downstream bandwidth for the DP tunnel. + * %0 if no available bandwidth. * * Allocates a tunnel between @in and @out that is capable of tunneling * Display Port traffic. @@ -2048,10 +2048,10 @@ err_free: * @tb: Pointer to the domain structure * @up: USB3 upstream adapter port * @down: USB3 downstream adapter port - * @max_up: Maximum available upstream bandwidth for the USB3 tunnel (%0 - * if not limited). - * @max_down: Maximum available downstream bandwidth for the USB3 tunnel - * (%0 if not limited). + * @max_up: Maximum available upstream bandwidth for the USB3 tunnel. + * %0 if no available bandwidth. + * @max_down: Maximum available downstream bandwidth for the USB3 tunnel. + * %0 if no available bandwidth. * * Allocate an USB3 tunnel. The ports must be of type @TB_TYPE_USB3_UP and * @TB_TYPE_USB3_DOWN. @@ -2066,24 +2066,19 @@ struct tb_tunnel *tb_tunnel_alloc_usb3(struct tb *tb, struct tb_port *up, struct tb_path *path; int max_rate = 0; - /* - * Check that we have enough bandwidth available for the new - * USB3 tunnel. - */ - if (max_up > 0 || max_down > 0) { + if (!tb_route(down->sw) && (max_up > 0 || max_down > 0)) { + /* + * For USB3 isochronous transfers, we allow bandwidth which is + * not higher than 90% of maximum supported bandwidth by USB3 + * adapters. + */ max_rate = tb_usb3_max_link_rate(down, up); if (max_rate < 0) return NULL; - /* Only 90% can be allocated for USB3 isochronous transfers */ max_rate = max_rate * 90 / 100; - tb_port_dbg(up, "required bandwidth for USB3 tunnel %d Mb/s\n", + tb_port_dbg(up, "maximum required bandwidth for USB3 tunnel %d Mb/s\n", max_rate); - - if (max_rate > max_up || max_rate > max_down) { - tb_port_warn(up, "not enough bandwidth for USB3 tunnel\n"); - return NULL; - } } tunnel = tb_tunnel_alloc(tb, 2, TB_TUNNEL_USB3); @@ -2115,8 +2110,8 @@ struct tb_tunnel *tb_tunnel_alloc_usb3(struct tb *tb, struct tb_port *up, tunnel->paths[TB_USB3_PATH_UP] = path; if (!tb_route(down->sw)) { - tunnel->allocated_up = max_rate; - tunnel->allocated_down = max_rate; + tunnel->allocated_up = min(max_rate, max_up); + tunnel->allocated_down = min(max_rate, max_down); tunnel->init = tb_usb3_init; tunnel->consumed_bandwidth = tb_usb3_consumed_bandwidth; diff --git a/drivers/thunderbolt/usb4.c b/drivers/thunderbolt/usb4.c index 9860b49d7a2b..de480bf2a53d 100644 --- a/drivers/thunderbolt/usb4.c +++ b/drivers/thunderbolt/usb4.c @@ -52,6 +52,10 @@ enum usb4_ba_index { #define USB4_BA_VALUE_MASK GENMASK(31, 16) #define USB4_BA_VALUE_SHIFT 16 +/* Delays in us used with usb4_port_wait_for_bit() */ +#define USB4_PORT_DELAY 50 +#define USB4_PORT_SB_DELAY 5000 + static int usb4_native_switch_op(struct tb_switch *sw, u16 opcode, u32 *metadata, u8 *status, const void *tx_data, size_t tx_dwords, @@ -155,7 +159,13 @@ static inline int usb4_switch_op_data(struct tb_switch *sw, u16 opcode, tx_dwords, rx_data, rx_dwords); } -static void usb4_switch_check_wakes(struct tb_switch *sw) +/** + * usb4_switch_check_wakes() - Check for wakes and notify PM core about them + * @sw: Router whose wakes to check + * + * Checks wakes occurred during suspend and notify the PM core about them. + */ +void usb4_switch_check_wakes(struct tb_switch *sw) { bool wakeup_usb4 = false; struct usb4_port *usb4; @@ -163,9 +173,6 @@ static void usb4_switch_check_wakes(struct tb_switch *sw) bool wakeup = false; u32 val; - if (!device_may_wakeup(&sw->dev)) - return; - if (tb_route(sw)) { if (tb_sw_read(sw, &val, TB_CFG_SWITCH, ROUTER_CS_6, 1)) return; @@ -244,8 +251,6 @@ int usb4_switch_setup(struct tb_switch *sw) u32 val = 0; int ret; - usb4_switch_check_wakes(sw); - if (!tb_route(sw)) return 0; @@ -1244,7 +1249,7 @@ void usb4_port_unconfigure_xdomain(struct tb_port *port) } static int usb4_port_wait_for_bit(struct tb_port *port, u32 offset, u32 bit, - u32 value, int timeout_msec) + u32 value, int timeout_msec, unsigned long delay_usec) { ktime_t timeout = ktime_add_ms(ktime_get(), timeout_msec); @@ -1259,7 +1264,7 @@ static int usb4_port_wait_for_bit(struct tb_port *port, u32 offset, u32 bit, if ((val & bit) == value) return 0; - usleep_range(50, 100); + fsleep(delay_usec); } while (ktime_before(ktime_get(), timeout)); return -ETIMEDOUT; @@ -1307,7 +1312,7 @@ static int usb4_port_sb_read(struct tb_port *port, enum usb4_sb_target target, return ret; ret = usb4_port_wait_for_bit(port, port->cap_usb4 + PORT_CS_1, - PORT_CS_1_PND, 0, 500); + PORT_CS_1_PND, 0, 500, USB4_PORT_SB_DELAY); if (ret) return ret; @@ -1354,7 +1359,7 @@ static int usb4_port_sb_write(struct tb_port *port, enum usb4_sb_target target, return ret; ret = usb4_port_wait_for_bit(port, port->cap_usb4 + PORT_CS_1, - PORT_CS_1_PND, 0, 500); + PORT_CS_1_PND, 0, 500, USB4_PORT_SB_DELAY); if (ret) return ret; @@ -1409,6 +1414,8 @@ static int usb4_port_sb_op(struct tb_port *port, enum usb4_sb_target target, if (val != opcode) return usb4_port_sb_opcode_err_to_errno(val); + + fsleep(USB4_PORT_SB_DELAY); } while (ktime_before(ktime_get(), timeout)); return -ETIMEDOUT; @@ -1590,13 +1597,14 @@ int usb4_port_asym_start(struct tb_port *port) * port started the symmetry transition. */ ret = usb4_port_wait_for_bit(port, port->cap_usb4 + PORT_CS_19, - PORT_CS_19_START_ASYM, 0, 1000); + PORT_CS_19_START_ASYM, 0, 1000, + USB4_PORT_DELAY); if (ret) return ret; /* Then wait for the transtion to be completed */ return usb4_port_wait_for_bit(port, port->cap_usb4 + PORT_CS_18, - PORT_CS_18_TIP, 0, 5000); + PORT_CS_18_TIP, 0, 5000, USB4_PORT_DELAY); } /** @@ -2122,7 +2130,8 @@ static int usb4_usb3_port_cm_request(struct tb_port *port, bool request) */ val &= ADP_USB3_CS_2_CMR; return usb4_port_wait_for_bit(port, port->cap_adap + ADP_USB3_CS_1, - ADP_USB3_CS_1_HCA, val, 1500); + ADP_USB3_CS_1_HCA, val, 1500, + USB4_PORT_DELAY); } static inline int usb4_usb3_port_set_cm_request(struct tb_port *port) diff --git a/drivers/thunderbolt/xdomain.c b/drivers/thunderbolt/xdomain.c index 940ae97987ff..11a50c86a1e4 100644 --- a/drivers/thunderbolt/xdomain.c +++ b/drivers/thunderbolt/xdomain.c @@ -250,7 +250,7 @@ static int tb_xdp_handle_error(const struct tb_xdp_error_response *res) case ERROR_UNKNOWN_DOMAIN: return -EIO; case ERROR_NOT_SUPPORTED: - return -ENOTSUPP; + return -EOPNOTSUPP; case ERROR_NOT_READY: return -EAGAIN; default: |