summaryrefslogtreecommitdiff
path: root/drivers/thunderbolt
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/thunderbolt')
-rw-r--r--drivers/thunderbolt/debugfs.c7
-rw-r--r--drivers/thunderbolt/icm.c1
-rw-r--r--drivers/thunderbolt/retimer.c12
-rw-r--r--drivers/thunderbolt/switch.c48
-rw-r--r--drivers/thunderbolt/tb.c19
-rw-r--r--drivers/thunderbolt/tb.h3
-rw-r--r--drivers/thunderbolt/tb_msgs.h6
-rw-r--r--drivers/thunderbolt/trace.h13
-rw-r--r--drivers/thunderbolt/tunnel.c39
-rw-r--r--drivers/thunderbolt/usb4.c35
-rw-r--r--drivers/thunderbolt/xdomain.c2
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: