summaryrefslogtreecommitdiff
path: root/drivers/thunderbolt/icm.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/thunderbolt/icm.c')
-rw-r--r--drivers/thunderbolt/icm.c117
1 files changed, 75 insertions, 42 deletions
diff --git a/drivers/thunderbolt/icm.c b/drivers/thunderbolt/icm.c
index 86521ebb2579..d339ba835376 100644
--- a/drivers/thunderbolt/icm.c
+++ b/drivers/thunderbolt/icm.c
@@ -22,6 +22,7 @@
#include "ctl.h"
#include "nhi_regs.h"
#include "tb.h"
+#include "tunnel.h"
#define PCIE2CIO_CMD 0x30
#define PCIE2CIO_CMD_TIMEOUT BIT(31)
@@ -41,6 +42,7 @@
#define PHY_PORT_CS1_LINK_STATE_SHIFT 26
#define ICM_TIMEOUT 5000 /* ms */
+#define ICM_RETRIES 3
#define ICM_APPROVE_TIMEOUT 10000 /* ms */
#define ICM_MAX_LINK 4
@@ -296,10 +298,9 @@ static bool icm_copy(struct tb_cfg_request *req, const struct ctl_pkg *pkg)
static int icm_request(struct tb *tb, const void *request, size_t request_size,
void *response, size_t response_size, size_t npackets,
- unsigned int timeout_msec)
+ int retries, unsigned int timeout_msec)
{
struct icm *icm = tb_priv(tb);
- int retries = 3;
do {
struct tb_cfg_request *req;
@@ -379,6 +380,27 @@ static bool icm_firmware_running(const struct tb_nhi *nhi)
return !!(val & REG_FW_STS_ICM_EN);
}
+static void icm_xdomain_activated(struct tb_xdomain *xd, bool activated)
+{
+ struct tb_port *nhi_port, *dst_port;
+ struct tb *tb = xd->tb;
+
+ nhi_port = tb_switch_find_port(tb->root_switch, TB_TYPE_NHI);
+ dst_port = tb_xdomain_downstream_port(xd);
+
+ if (activated)
+ tb_tunnel_event(tb, TB_TUNNEL_ACTIVATED, TB_TUNNEL_DMA,
+ nhi_port, dst_port);
+ else
+ tb_tunnel_event(tb, TB_TUNNEL_DEACTIVATED, TB_TUNNEL_DMA,
+ nhi_port, dst_port);
+}
+
+static void icm_dp_event(struct tb *tb)
+{
+ tb_tunnel_event(tb, TB_TUNNEL_CHANGED, TB_TUNNEL_DP, NULL, NULL);
+}
+
static bool icm_fr_is_supported(struct tb *tb)
{
return !x86_apple_machine;
@@ -410,7 +432,7 @@ static int icm_fr_get_route(struct tb *tb, u8 link, u8 depth, u64 *route)
return -ENOMEM;
ret = icm_request(tb, &request, sizeof(request), switches,
- sizeof(*switches), npackets, ICM_TIMEOUT);
+ sizeof(*switches), npackets, ICM_RETRIES, ICM_TIMEOUT);
if (ret)
goto err_free;
@@ -463,7 +485,7 @@ icm_fr_driver_ready(struct tb *tb, enum tb_security_level *security_level,
memset(&reply, 0, sizeof(reply));
ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply),
- 1, ICM_TIMEOUT);
+ 1, ICM_RETRIES, ICM_TIMEOUT);
if (ret)
return ret;
@@ -488,7 +510,7 @@ static int icm_fr_approve_switch(struct tb *tb, struct tb_switch *sw)
memset(&reply, 0, sizeof(reply));
/* Use larger timeout as establishing tunnels can take some time */
ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply),
- 1, ICM_APPROVE_TIMEOUT);
+ 1, ICM_RETRIES, ICM_APPROVE_TIMEOUT);
if (ret)
return ret;
@@ -515,7 +537,7 @@ static int icm_fr_add_switch_key(struct tb *tb, struct tb_switch *sw)
memset(&reply, 0, sizeof(reply));
ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply),
- 1, ICM_TIMEOUT);
+ 1, ICM_RETRIES, ICM_TIMEOUT);
if (ret)
return ret;
@@ -543,7 +565,7 @@ static int icm_fr_challenge_switch_key(struct tb *tb, struct tb_switch *sw,
memset(&reply, 0, sizeof(reply));
ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply),
- 1, ICM_TIMEOUT);
+ 1, ICM_RETRIES, ICM_TIMEOUT);
if (ret)
return ret;
@@ -577,13 +599,14 @@ static int icm_fr_approve_xdomain_paths(struct tb *tb, struct tb_xdomain *xd,
memset(&reply, 0, sizeof(reply));
ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply),
- 1, ICM_TIMEOUT);
+ 1, ICM_RETRIES, ICM_TIMEOUT);
if (ret)
return ret;
if (reply.hdr.flags & ICM_FLAGS_ERROR)
return -EIO;
+ icm_xdomain_activated(xd, true);
return 0;
}
@@ -603,6 +626,8 @@ static int icm_fr_disconnect_xdomain_paths(struct tb *tb, struct tb_xdomain *xd,
nhi_mailbox_cmd(tb->nhi, cmd, 1);
usleep_range(10, 50);
nhi_mailbox_cmd(tb->nhi, cmd, 2);
+
+ icm_xdomain_activated(xd, false);
return 0;
}
@@ -644,13 +669,14 @@ static int add_switch(struct tb_switch *parent_sw, struct tb_switch *sw)
return ret;
}
-static void update_switch(struct tb_switch *parent_sw, struct tb_switch *sw,
- u64 route, u8 connection_id, u8 connection_key,
- u8 link, u8 depth, bool boot)
+static void update_switch(struct tb_switch *sw, u64 route, u8 connection_id,
+ u8 connection_key, u8 link, u8 depth, bool boot)
{
+ struct tb_switch *parent_sw = tb_switch_parent(sw);
+
/* Disconnect from parent */
- tb_port_at(tb_route(sw), parent_sw)->remote = NULL;
- /* Re-connect via updated port*/
+ tb_switch_downstream_port(sw)->remote = NULL;
+ /* Re-connect via updated port */
tb_port_at(route, parent_sw)->remote = tb_upstream_port(sw);
/* Update with the new addressing information */
@@ -671,10 +697,7 @@ static void update_switch(struct tb_switch *parent_sw, struct tb_switch *sw,
static void remove_switch(struct tb_switch *sw)
{
- struct tb_switch *parent_sw;
-
- parent_sw = tb_to_switch(sw->dev.parent);
- tb_port_at(tb_route(sw), parent_sw)->remote = NULL;
+ tb_switch_downstream_port(sw)->remote = NULL;
tb_switch_remove(sw);
}
@@ -755,7 +778,6 @@ icm_fr_device_connected(struct tb *tb, const struct icm_pkg_header *hdr)
if (sw) {
u8 phy_port, sw_phy_port;
- parent_sw = tb_to_switch(sw->dev.parent);
sw_phy_port = tb_phy_port_from_link(sw->link);
phy_port = tb_phy_port_from_link(link);
@@ -765,7 +787,7 @@ icm_fr_device_connected(struct tb *tb, const struct icm_pkg_header *hdr)
* information might have changed for example by the
* fact that a switch on a dual-link connection might
* have been enumerated using the other link now. Make
- * sure our book keeping matches that.
+ * sure our bookkeeping matches that.
*/
if (sw->depth == depth && sw_phy_port == phy_port &&
!!sw->authorized == authorized) {
@@ -785,7 +807,7 @@ icm_fr_device_connected(struct tb *tb, const struct icm_pkg_header *hdr)
route = tb_route(sw);
}
- update_switch(parent_sw, sw, route, pkg->connection_id,
+ update_switch(sw, route, pkg->connection_id,
pkg->connection_key, link, depth, boot);
tb_switch_put(sw);
return;
@@ -853,7 +875,8 @@ icm_fr_device_connected(struct tb *tb, const struct icm_pkg_header *hdr)
sw->security_level = security_level;
sw->boot = boot;
sw->link_speed = speed_gen3 ? 20 : 10;
- sw->link_width = dual_lane ? 2 : 1;
+ sw->link_width = dual_lane ? TB_LINK_WIDTH_DUAL :
+ TB_LINK_WIDTH_SINGLE;
sw->rpm = intel_vss_is_rtd3(pkg->ep_name, sizeof(pkg->ep_name));
if (add_switch(parent_sw, sw))
@@ -946,7 +969,7 @@ icm_fr_xdomain_connected(struct tb *tb, const struct icm_pkg_header *hdr)
/*
* Look if there already exists an XDomain in the same place
- * than the new one and in that case remove it because it is
+ * as the new one and in that case remove it because it is
* most likely another host that got disconnected.
*/
xd = tb_xdomain_find_by_link_depth(tb, link, depth);
@@ -1022,7 +1045,7 @@ icm_tr_driver_ready(struct tb *tb, enum tb_security_level *security_level,
memset(&reply, 0, sizeof(reply));
ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply),
- 1, 20000);
+ 1, 10, 250);
if (ret)
return ret;
@@ -1055,7 +1078,7 @@ static int icm_tr_approve_switch(struct tb *tb, struct tb_switch *sw)
memset(&reply, 0, sizeof(reply));
ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply),
- 1, ICM_APPROVE_TIMEOUT);
+ 1, ICM_RETRIES, ICM_APPROVE_TIMEOUT);
if (ret)
return ret;
@@ -1083,7 +1106,7 @@ static int icm_tr_add_switch_key(struct tb *tb, struct tb_switch *sw)
memset(&reply, 0, sizeof(reply));
ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply),
- 1, ICM_TIMEOUT);
+ 1, ICM_RETRIES, ICM_TIMEOUT);
if (ret)
return ret;
@@ -1112,7 +1135,7 @@ static int icm_tr_challenge_switch_key(struct tb *tb, struct tb_switch *sw,
memset(&reply, 0, sizeof(reply));
ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply),
- 1, ICM_TIMEOUT);
+ 1, ICM_RETRIES, ICM_TIMEOUT);
if (ret)
return ret;
@@ -1146,13 +1169,14 @@ static int icm_tr_approve_xdomain_paths(struct tb *tb, struct tb_xdomain *xd,
memset(&reply, 0, sizeof(reply));
ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply),
- 1, ICM_TIMEOUT);
+ 1, ICM_RETRIES, ICM_TIMEOUT);
if (ret)
return ret;
if (reply.hdr.flags & ICM_FLAGS_ERROR)
return -EIO;
+ icm_xdomain_activated(xd, true);
return 0;
}
@@ -1172,7 +1196,7 @@ static int icm_tr_xdomain_tear_down(struct tb *tb, struct tb_xdomain *xd,
memset(&reply, 0, sizeof(reply));
ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply),
- 1, ICM_TIMEOUT);
+ 1, ICM_RETRIES, ICM_TIMEOUT);
if (ret)
return ret;
@@ -1193,7 +1217,12 @@ static int icm_tr_disconnect_xdomain_paths(struct tb *tb, struct tb_xdomain *xd,
return ret;
usleep_range(10, 50);
- return icm_tr_xdomain_tear_down(tb, xd, 2);
+ ret = icm_tr_xdomain_tear_down(tb, xd, 2);
+ if (ret)
+ return ret;
+
+ icm_xdomain_activated(xd, false);
+ return 0;
}
static void
@@ -1236,9 +1265,8 @@ __icm_tr_device_connected(struct tb *tb, const struct icm_pkg_header *hdr,
if (sw) {
/* Update the switch if it is still in the same place */
if (tb_route(sw) == route && !!sw->authorized == authorized) {
- parent_sw = tb_to_switch(sw->dev.parent);
- update_switch(parent_sw, sw, route, pkg->connection_id,
- 0, 0, 0, boot);
+ update_switch(sw, route, pkg->connection_id, 0, 0, 0,
+ boot);
tb_switch_put(sw);
return;
}
@@ -1276,7 +1304,8 @@ __icm_tr_device_connected(struct tb *tb, const struct icm_pkg_header *hdr,
sw->security_level = security_level;
sw->boot = boot;
sw->link_speed = speed_gen3 ? 20 : 10;
- sw->link_width = dual_lane ? 2 : 1;
+ sw->link_width = dual_lane ? TB_LINK_WIDTH_DUAL :
+ TB_LINK_WIDTH_SINGLE;
sw->rpm = force_rtd3;
if (!sw->rpm)
sw->rpm = intel_vss_is_rtd3(pkg->ep_name,
@@ -1498,7 +1527,7 @@ icm_ar_driver_ready(struct tb *tb, enum tb_security_level *security_level,
memset(&reply, 0, sizeof(reply));
ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply),
- 1, ICM_TIMEOUT);
+ 1, ICM_RETRIES, ICM_TIMEOUT);
if (ret)
return ret;
@@ -1524,7 +1553,7 @@ static int icm_ar_get_route(struct tb *tb, u8 link, u8 depth, u64 *route)
memset(&reply, 0, sizeof(reply));
ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply),
- 1, ICM_TIMEOUT);
+ 1, ICM_RETRIES, ICM_TIMEOUT);
if (ret)
return ret;
@@ -1545,7 +1574,7 @@ static int icm_ar_get_boot_acl(struct tb *tb, uuid_t *uuids, size_t nuuids)
memset(&reply, 0, sizeof(reply));
ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply),
- 1, ICM_TIMEOUT);
+ 1, ICM_RETRIES, ICM_TIMEOUT);
if (ret)
return ret;
@@ -1606,7 +1635,7 @@ static int icm_ar_set_boot_acl(struct tb *tb, const uuid_t *uuids,
memset(&reply, 0, sizeof(reply));
ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply),
- 1, ICM_TIMEOUT);
+ 1, ICM_RETRIES, ICM_TIMEOUT);
if (ret)
return ret;
@@ -1628,7 +1657,7 @@ icm_icl_driver_ready(struct tb *tb, enum tb_security_level *security_level,
memset(&reply, 0, sizeof(reply));
ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply),
- 1, 20000);
+ 1, ICM_RETRIES, 20000);
if (ret)
return ret;
@@ -1720,6 +1749,9 @@ static void icm_handle_notification(struct work_struct *work)
if (tb_is_xdomain_enabled())
icm->xdomain_disconnected(tb, n->pkg);
break;
+ case ICM_EVENT_DP_CONFIG_CHANGED:
+ icm_dp_event(tb);
+ break;
case ICM_EVENT_RTD3_VETO:
icm->rtd3_veto(tb, n->pkg);
break;
@@ -1968,7 +2000,7 @@ static int icm_driver_ready(struct tb *tb)
if (icm->safe_mode) {
tb_info(tb, "Thunderbolt host controller is in safe mode.\n");
tb_info(tb, "You need to update NVM firmware of the controller before it can be used.\n");
- tb_info(tb, "For latest updates check https://thunderbolttechnology.net/updates.\n");
+ tb_info(tb, "Use fwupd tool to apply update. Check Documentation/admin-guide/thunderbolt.rst for details.\n");
return 0;
}
@@ -2139,14 +2171,14 @@ static int icm_runtime_resume_switch(struct tb_switch *sw)
static int icm_runtime_resume(struct tb *tb)
{
/*
- * We can reuse the same resume functionality than with system
+ * We can reuse the same resume functionality as with system
* suspend.
*/
icm_complete(tb);
return 0;
}
-static int icm_start(struct tb *tb)
+static int icm_start(struct tb *tb, bool not_used)
{
struct icm *icm = tb_priv(tb);
int ret;
@@ -2300,7 +2332,7 @@ static int icm_usb4_switch_op(struct tb_switch *sw, u16 opcode, u32 *metadata,
memset(&reply, 0, sizeof(reply));
ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply),
- 1, ICM_TIMEOUT);
+ 1, ICM_RETRIES, ICM_TIMEOUT);
if (ret)
return ret;
@@ -2534,6 +2566,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;