From 990f4b85d7d9bb954a24ada7145947dec501fe3d Mon Sep 17 00:00:00 2001 From: Gil Fine Date: Thu, 26 May 2022 13:59:16 +0300 Subject: thunderbolt: Silently ignore CLx enabling in case CLx is not supported We can't enable CLx if it is not supported either by the host or device, or by the USB4/TBT link (e.g. when an optical cable is used). We silently ignore CLx enabling in this case. Signed-off-by: Gil Fine Signed-off-by: Mika Westerberg --- drivers/thunderbolt/tb.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'drivers/thunderbolt') diff --git a/drivers/thunderbolt/tb.c b/drivers/thunderbolt/tb.c index 9a3214fb5038..8ab3530dfa0d 100644 --- a/drivers/thunderbolt/tb.c +++ b/drivers/thunderbolt/tb.c @@ -575,6 +575,7 @@ static void tb_scan_port(struct tb_port *port) struct tb_cm *tcm = tb_priv(port->sw->tb); struct tb_port *upstream_port; struct tb_switch *sw; + int ret; if (tb_is_upstream_port(port)) return; @@ -663,7 +664,9 @@ static void tb_scan_port(struct tb_port *port) tb_switch_lane_bonding_enable(sw); /* Set the link configured */ tb_switch_configure_link(sw); - if (tb_switch_enable_clx(sw, TB_CL0S)) + /* Silently ignore CLx enabling in case CLx is not supported */ + ret = tb_switch_enable_clx(sw, TB_CL0S); + if (ret && ret != -EOPNOTSUPP) tb_sw_warn(sw, "failed to enable CLx on upstream port\n"); tb_switch_tmu_configure(sw, TB_SWITCH_TMU_RATE_HIFI, @@ -1446,12 +1449,15 @@ static int tb_suspend_noirq(struct tb *tb) static void tb_restore_children(struct tb_switch *sw) { struct tb_port *port; + int ret; /* No need to restore if the router is already unplugged */ if (sw->is_unplugged) return; - if (tb_switch_enable_clx(sw, TB_CL0S)) + /* Silently ignore CLx re-enabling in case CLx is not supported */ + ret = tb_switch_enable_clx(sw, TB_CL0S); + if (ret && ret != -EOPNOTSUPP) tb_sw_warn(sw, "failed to re-enable CLx on upstream port\n"); /* -- cgit v1.2.3 From 418a5a3d6596f62424b24192b642d959d4c73d25 Mon Sep 17 00:00:00 2001 From: Gil Fine Date: Thu, 26 May 2022 13:59:17 +0300 Subject: thunderbolt: CLx disable before system suspend only if previously enabled Disable CLx before system suspended only if previously was enabled. Signed-off-by: Gil Fine Signed-off-by: Mika Westerberg --- drivers/thunderbolt/switch.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers/thunderbolt') diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c index 561e1d77240e..0395229a3562 100644 --- a/drivers/thunderbolt/switch.c +++ b/drivers/thunderbolt/switch.c @@ -3134,8 +3134,10 @@ void tb_switch_suspend(struct tb_switch *sw, bool runtime) * Actually only needed for Titan Ridge but for simplicity can be * done for USB4 device too as CLx is re-enabled at resume. */ - if (tb_switch_disable_clx(sw, TB_CL0S)) - tb_sw_warn(sw, "failed to disable CLx on upstream port\n"); + if (tb_switch_is_clx_enabled(sw)) { + if (tb_switch_disable_clx(sw, TB_CL0S)) + tb_sw_warn(sw, "failed to disable CLx on upstream port\n"); + } err = tb_plug_events_active(sw, false); if (err) -- cgit v1.2.3 From b4e08d5d08192699e68ffa796bd2c3ab58af5730 Mon Sep 17 00:00:00 2001 From: Gil Fine Date: Thu, 26 May 2022 13:59:18 +0300 Subject: thunderbolt: Fix typos in CLx enabling Fix few typos in CLx enabling. Signed-off-by: Gil Fine Signed-off-by: Mika Westerberg --- drivers/thunderbolt/switch.c | 2 +- drivers/thunderbolt/tmu.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/thunderbolt') diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c index 0395229a3562..e584077b2f4f 100644 --- a/drivers/thunderbolt/switch.c +++ b/drivers/thunderbolt/switch.c @@ -3556,7 +3556,7 @@ static int tb_switch_enable_cl0s(struct tb_switch *sw) * to improve performance. CLx is enabled only if both sides of the link * support CLx, and if both sides of the link are not configured as two * single lane links and only if the link is not inter-domain link. The - * complete set of conditions is descibed in CM Guide 1.0 section 8.1. + * complete set of conditions is described in CM Guide 1.0 section 8.1. * * Return: Returns 0 on success or an error code on failure. */ diff --git a/drivers/thunderbolt/tmu.c b/drivers/thunderbolt/tmu.c index e4a07a26f693..b656659d02fb 100644 --- a/drivers/thunderbolt/tmu.c +++ b/drivers/thunderbolt/tmu.c @@ -606,7 +606,7 @@ int tb_switch_tmu_enable(struct tb_switch *sw) /** * tb_switch_tmu_configure() - Configure the TMU rate and directionality * @sw: Router whose mode to change - * @rate: Rate to configure Off/LowRes/HiFi + * @rate: Rate to configure Off/Normal/HiFi * @unidirectional: If uni-directional (bi-directional otherwise) * * Selects the rate of the TMU and directionality (uni-directional or -- cgit v1.2.3 From 5fd6b9a5cbe63fea4c490fee8af34144a139a266 Mon Sep 17 00:00:00 2001 From: Gil Fine Date: Thu, 26 May 2022 13:59:19 +0300 Subject: thunderbolt: Change downstream router's TMU rate in both TMU uni/bidir mode In case of uni-directional time sync, TMU handshake is initiated by upstream router. In case of bi-directional time sync, TMU handshake is initiated by downstream router. In order to handle correctly the case of uni-directional mode, we avoid changing the upstream router's rate to off, because it might have another downstream router plugged that is set to uni-directional mode (and we don't want to change its mode). Instead, we always change downstream router's rate. Signed-off-by: Gil Fine Signed-off-by: Mika Westerberg --- drivers/thunderbolt/tmu.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) (limited to 'drivers/thunderbolt') diff --git a/drivers/thunderbolt/tmu.c b/drivers/thunderbolt/tmu.c index b656659d02fb..985ca43b8f39 100644 --- a/drivers/thunderbolt/tmu.c +++ b/drivers/thunderbolt/tmu.c @@ -359,13 +359,14 @@ int tb_switch_tmu_disable(struct tb_switch *sw) * In case of uni-directional time sync, TMU handshake is * initiated by upstream router. In case of bi-directional * time sync, TMU handshake is initiated by downstream router. - * Therefore, we change the rate to off in the respective - * router. + * We change downstream router's rate to off for both uni/bidir + * cases although it is needed only for the bi-directional mode. + * We avoid changing upstream router's mode since it might + * have another downstream router plugged, that is set to + * uni-directional mode and we don't want to change it's TMU + * mode. */ - if (unidirectional) - tb_switch_tmu_rate_write(parent, TB_SWITCH_TMU_RATE_OFF); - else - tb_switch_tmu_rate_write(sw, TB_SWITCH_TMU_RATE_OFF); + tb_switch_tmu_rate_write(sw, TB_SWITCH_TMU_RATE_OFF); tb_port_tmu_time_sync_disable(up); ret = tb_port_tmu_time_sync_disable(down); -- cgit v1.2.3 From b017a46d486cd4113b1856f3fd611f54cd0f9c03 Mon Sep 17 00:00:00 2001 From: Gil Fine Date: Thu, 26 May 2022 13:59:20 +0300 Subject: thunderbolt: Add CL1 support for USB4 and Titan Ridge routers In this patch we add support for a second low power state of the link: CL1. Low power states (called collectively CLx) are used to reduce transmitter and receiver power when a high-speed lane is idle. We enable it, if both sides of the link support it, and only for the first hop router (i.e. the first device that connected to the host router). This is needed for better thermal management. Signed-off-by: Gil Fine Signed-off-by: Mika Westerberg --- drivers/thunderbolt/switch.c | 89 +++++++++++---------- drivers/thunderbolt/tb.c | 55 +++++++++---- drivers/thunderbolt/tb.h | 46 +++++------ drivers/thunderbolt/tb_regs.h | 6 ++ drivers/thunderbolt/tmu.c | 177 +++++++++++++++++++++++++++++++++++------- 5 files changed, 266 insertions(+), 107 deletions(-) (limited to 'drivers/thunderbolt') diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c index e584077b2f4f..244f8cd38b25 100644 --- a/drivers/thunderbolt/switch.c +++ b/drivers/thunderbolt/switch.c @@ -3133,10 +3133,12 @@ void tb_switch_suspend(struct tb_switch *sw, bool runtime) /* * Actually only needed for Titan Ridge but for simplicity can be * done for USB4 device too as CLx is re-enabled at resume. + * CL0s and CL1 are enabled and supported together. */ - if (tb_switch_is_clx_enabled(sw)) { - if (tb_switch_disable_clx(sw, TB_CL0S)) - tb_sw_warn(sw, "failed to disable CLx on upstream port\n"); + if (tb_switch_is_clx_enabled(sw, TB_CL1)) { + if (tb_switch_disable_clx(sw, TB_CL1)) + tb_sw_warn(sw, "failed to disable %s on upstream port\n", + tb_switch_clx_name(TB_CL1)); } err = tb_plug_events_active(sw, false); @@ -3428,13 +3430,12 @@ static bool tb_port_clx_supported(struct tb_port *port, enum tb_clx clx) } switch (clx) { - case TB_CL0S: - /* CL0s support requires also CL1 support */ + case TB_CL1: + /* CL0s and CL1 are enabled and supported together */ mask = LANE_ADP_CS_0_CL0S_SUPPORT | LANE_ADP_CS_0_CL1_SUPPORT; break; - /* For now we support only CL0s. Not CL1, CL2 */ - case TB_CL1: + /* For now we support only CL0s and CL1. Not CL2 */ case TB_CL2: default: return false; @@ -3448,18 +3449,18 @@ static bool tb_port_clx_supported(struct tb_port *port, enum tb_clx clx) return !!(val & mask); } -static inline bool tb_port_cl0s_supported(struct tb_port *port) -{ - return tb_port_clx_supported(port, TB_CL0S); -} - -static int __tb_port_cl0s_set(struct tb_port *port, bool enable) +static int __tb_port_clx_set(struct tb_port *port, enum tb_clx clx, bool enable) { u32 phy, mask; int ret; - /* To enable CL0s also required to enable CL1 */ - mask = LANE_ADP_CS_1_CL0S_ENABLE | LANE_ADP_CS_1_CL1_ENABLE; + /* CL0s and CL1 are enabled and supported together */ + if (clx == TB_CL1) + mask = LANE_ADP_CS_1_CL0S_ENABLE | LANE_ADP_CS_1_CL1_ENABLE; + else + /* For now we support only CL0s and CL1. Not CL2 */ + return -EOPNOTSUPP; + ret = tb_port_read(port, &phy, TB_CFG_PORT, port->cap_phy + LANE_ADP_CS_1, 1); if (ret) @@ -3474,20 +3475,20 @@ static int __tb_port_cl0s_set(struct tb_port *port, bool enable) port->cap_phy + LANE_ADP_CS_1, 1); } -static int tb_port_cl0s_disable(struct tb_port *port) +static int tb_port_clx_disable(struct tb_port *port, enum tb_clx clx) { - return __tb_port_cl0s_set(port, false); + return __tb_port_clx_set(port, clx, false); } -static int tb_port_cl0s_enable(struct tb_port *port) +static int tb_port_clx_enable(struct tb_port *port, enum tb_clx clx) { - return __tb_port_cl0s_set(port, true); + return __tb_port_clx_set(port, clx, true); } -static int tb_switch_enable_cl0s(struct tb_switch *sw) +static int __tb_switch_enable_clx(struct tb_switch *sw, enum tb_clx clx) { struct tb_switch *parent = tb_switch_parent(sw); - bool up_cl0s_support, down_cl0s_support; + bool up_clx_support, down_clx_support; struct tb_port *up, *down; int ret; @@ -3512,37 +3513,37 @@ static int tb_switch_enable_cl0s(struct tb_switch *sw) up = tb_upstream_port(sw); down = tb_port_at(tb_route(sw), parent); - up_cl0s_support = tb_port_cl0s_supported(up); - down_cl0s_support = tb_port_cl0s_supported(down); + up_clx_support = tb_port_clx_supported(up, clx); + down_clx_support = tb_port_clx_supported(down, clx); - tb_port_dbg(up, "CL0s %ssupported\n", - up_cl0s_support ? "" : "not "); - tb_port_dbg(down, "CL0s %ssupported\n", - down_cl0s_support ? "" : "not "); + tb_port_dbg(up, "%s %ssupported\n", tb_switch_clx_name(clx), + up_clx_support ? "" : "not "); + tb_port_dbg(down, "%s %ssupported\n", tb_switch_clx_name(clx), + down_clx_support ? "" : "not "); - if (!up_cl0s_support || !down_cl0s_support) + if (!up_clx_support || !down_clx_support) return -EOPNOTSUPP; - ret = tb_port_cl0s_enable(up); + ret = tb_port_clx_enable(up, clx); if (ret) return ret; - ret = tb_port_cl0s_enable(down); + ret = tb_port_clx_enable(down, clx); if (ret) { - tb_port_cl0s_disable(up); + tb_port_clx_disable(up, clx); return ret; } ret = tb_switch_mask_clx_objections(sw); if (ret) { - tb_port_cl0s_disable(up); - tb_port_cl0s_disable(down); + tb_port_clx_disable(up, clx); + tb_port_clx_disable(down, clx); return ret; } - sw->clx = TB_CL0S; + sw->clx = clx; - tb_port_dbg(up, "CL0s enabled\n"); + tb_port_dbg(up, "%s enabled\n", tb_switch_clx_name(clx)); return 0; } @@ -3575,15 +3576,16 @@ int tb_switch_enable_clx(struct tb_switch *sw, enum tb_clx clx) return 0; switch (clx) { - case TB_CL0S: - return tb_switch_enable_cl0s(sw); + case TB_CL1: + /* CL0s and CL1 are enabled and supported together */ + return __tb_switch_enable_clx(sw, clx); default: return -EOPNOTSUPP; } } -static int tb_switch_disable_cl0s(struct tb_switch *sw) +static int __tb_switch_disable_clx(struct tb_switch *sw, enum tb_clx clx) { struct tb_switch *parent = tb_switch_parent(sw); struct tb_port *up, *down; @@ -3605,17 +3607,17 @@ static int tb_switch_disable_cl0s(struct tb_switch *sw) up = tb_upstream_port(sw); down = tb_port_at(tb_route(sw), parent); - ret = tb_port_cl0s_disable(up); + ret = tb_port_clx_disable(up, clx); if (ret) return ret; - ret = tb_port_cl0s_disable(down); + ret = tb_port_clx_disable(down, clx); if (ret) return ret; sw->clx = TB_CLX_DISABLE; - tb_port_dbg(up, "CL0s disabled\n"); + tb_port_dbg(up, "%s disabled\n", tb_switch_clx_name(clx)); return 0; } @@ -3632,8 +3634,9 @@ int tb_switch_disable_clx(struct tb_switch *sw, enum tb_clx clx) return 0; switch (clx) { - case TB_CL0S: - return tb_switch_disable_cl0s(sw); + case TB_CL1: + /* CL0s and CL1 are enabled and supported together */ + return __tb_switch_disable_clx(sw, clx); default: return -EOPNOTSUPP; diff --git a/drivers/thunderbolt/tb.c b/drivers/thunderbolt/tb.c index 8ab3530dfa0d..8e9fdc4e0650 100644 --- a/drivers/thunderbolt/tb.c +++ b/drivers/thunderbolt/tb.c @@ -215,7 +215,7 @@ static int tb_enable_tmu(struct tb_switch *sw) int ret; /* If it is already enabled in correct mode, don't touch it */ - if (tb_switch_tmu_hifi_is_enabled(sw, sw->tmu.unidirectional_request)) + if (tb_switch_tmu_is_enabled(sw, sw->tmu.unidirectional_request)) return 0; ret = tb_switch_tmu_disable(sw); @@ -664,13 +664,24 @@ static void tb_scan_port(struct tb_port *port) tb_switch_lane_bonding_enable(sw); /* Set the link configured */ tb_switch_configure_link(sw); - /* Silently ignore CLx enabling in case CLx is not supported */ - ret = tb_switch_enable_clx(sw, TB_CL0S); + /* + * CL0s and CL1 are enabled and supported together. + * Silently ignore CLx enabling in case CLx is not supported. + */ + ret = tb_switch_enable_clx(sw, TB_CL1); if (ret && ret != -EOPNOTSUPP) - tb_sw_warn(sw, "failed to enable CLx on upstream port\n"); + tb_sw_warn(sw, "failed to enable %s on upstream port\n", + tb_switch_clx_name(TB_CL1)); - tb_switch_tmu_configure(sw, TB_SWITCH_TMU_RATE_HIFI, - tb_switch_is_clx_enabled(sw)); + if (tb_switch_is_clx_enabled(sw, TB_CL1)) + /* + * To support highest CLx state, we set router's TMU to + * Normal-Uni mode. + */ + tb_switch_tmu_configure(sw, TB_SWITCH_TMU_RATE_NORMAL, true); + else + /* If CLx disabled, configure router's TMU to HiFi-Bidir mode*/ + tb_switch_tmu_configure(sw, TB_SWITCH_TMU_RATE_HIFI, false); if (tb_enable_tmu(sw)) tb_sw_warn(sw, "failed to enable TMU\n"); @@ -1410,7 +1421,12 @@ static int tb_start(struct tb *tb) return ret; } - tb_switch_tmu_configure(tb->root_switch, TB_SWITCH_TMU_RATE_HIFI, false); + /* + * To support highest CLx state, we set host router's TMU to + * Normal mode. + */ + tb_switch_tmu_configure(tb->root_switch, TB_SWITCH_TMU_RATE_NORMAL, + false); /* Enable TMU if it is off */ tb_switch_tmu_enable(tb->root_switch); /* Full scan to discover devices added before the driver was loaded. */ @@ -1455,16 +1471,25 @@ static void tb_restore_children(struct tb_switch *sw) if (sw->is_unplugged) return; - /* Silently ignore CLx re-enabling in case CLx is not supported */ - ret = tb_switch_enable_clx(sw, TB_CL0S); - if (ret && ret != -EOPNOTSUPP) - tb_sw_warn(sw, "failed to re-enable CLx on upstream port\n"); - /* - * tb_switch_tmu_configure() was already called when the switch was - * added before entering system sleep or runtime suspend, - * so no need to call it again before enabling TMU. + * CL0s and CL1 are enabled and supported together. + * Silently ignore CLx re-enabling in case CLx is not supported. */ + ret = tb_switch_enable_clx(sw, TB_CL1); + if (ret && ret != -EOPNOTSUPP) + tb_sw_warn(sw, "failed to re-enable %s on upstream port\n", + tb_switch_clx_name(TB_CL1)); + + if (tb_switch_is_clx_enabled(sw, TB_CL1)) + /* + * To support highest CLx state, we set router's TMU to + * Normal-Uni mode. + */ + tb_switch_tmu_configure(sw, TB_SWITCH_TMU_RATE_NORMAL, true); + else + /* If CLx disabled, configure router's TMU to HiFi-Bidir mode*/ + tb_switch_tmu_configure(sw, TB_SWITCH_TMU_RATE_HIFI, false); + if (tb_enable_tmu(sw)) tb_sw_warn(sw, "failed to restore TMU configuration\n"); diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h index 4602c69913fa..3882b6eb9f51 100644 --- a/drivers/thunderbolt/tb.h +++ b/drivers/thunderbolt/tb.h @@ -13,6 +13,7 @@ #include #include #include +#include #include "tb_regs.h" #include "ctl.h" @@ -111,7 +112,7 @@ struct tb_switch_tmu { enum tb_clx { TB_CLX_DISABLE, - TB_CL0S, + /* CL0s and CL1 are enabled and supported together */ TB_CL1, TB_CL2, }; @@ -934,45 +935,46 @@ void tb_switch_tmu_configure(struct tb_switch *sw, enum tb_switch_tmu_rate rate, bool unidirectional); /** - * tb_switch_tmu_hifi_is_enabled() - Checks if the specified TMU mode is enabled + * tb_switch_tmu_is_enabled() - Checks if the specified TMU mode is enabled * @sw: Router whose TMU mode to check * @unidirectional: If uni-directional (bi-directional otherwise) * * Return true if hardware TMU configuration matches the one passed in - * as parameter. That is HiFi and either uni-directional or bi-directional. + * as parameter. That is HiFi/Normal and either uni-directional or bi-directional. */ -static inline bool tb_switch_tmu_hifi_is_enabled(const struct tb_switch *sw, - bool unidirectional) +static inline bool tb_switch_tmu_is_enabled(const struct tb_switch *sw, + bool unidirectional) { - return sw->tmu.rate == TB_SWITCH_TMU_RATE_HIFI && + return sw->tmu.rate == sw->tmu.rate_request && sw->tmu.unidirectional == unidirectional; } +static inline const char *tb_switch_clx_name(enum tb_clx clx) +{ + switch (clx) { + /* CL0s and CL1 are enabled and supported together */ + case TB_CL1: + return "CL0s/CL1"; + default: + return "unknown"; + } +} + int tb_switch_enable_clx(struct tb_switch *sw, enum tb_clx clx); int tb_switch_disable_clx(struct tb_switch *sw, enum tb_clx clx); /** * tb_switch_is_clx_enabled() - Checks if the CLx is enabled - * @sw: Router to check the CLx state for - * - * Checks if the CLx is enabled on the router upstream link. - * Not applicable for a host router. - */ -static inline bool tb_switch_is_clx_enabled(const struct tb_switch *sw) -{ - return sw->clx != TB_CLX_DISABLE; -} - -/** - * tb_switch_is_cl0s_enabled() - Checks if the CL0s is enabled - * @sw: Router to check for the CL0s + * @sw: Router to check for the CLx + * @clx: The CLx state to check for * - * Checks if the CL0s is enabled on the router upstream link. + * Checks if the specified CLx is enabled on the router upstream link. * Not applicable for a host router. */ -static inline bool tb_switch_is_cl0s_enabled(const struct tb_switch *sw) +static inline bool tb_switch_is_clx_enabled(const struct tb_switch *sw, + enum tb_clx clx) { - return sw->clx == TB_CL0S; + return sw->clx == clx; } /** diff --git a/drivers/thunderbolt/tb_regs.h b/drivers/thunderbolt/tb_regs.h index 6a16f61a72a1..166054110388 100644 --- a/drivers/thunderbolt/tb_regs.h +++ b/drivers/thunderbolt/tb_regs.h @@ -234,6 +234,7 @@ enum usb4_switch_op { /* Router TMU configuration */ #define TMU_RTR_CS_0 0x00 +#define TMU_RTR_CS_0_FREQ_WIND_MASK GENMASK(26, 16) #define TMU_RTR_CS_0_TD BIT(27) #define TMU_RTR_CS_0_UCAP BIT(30) #define TMU_RTR_CS_1 0x01 @@ -244,6 +245,11 @@ enum usb4_switch_op { #define TMU_RTR_CS_3_LOCAL_TIME_NS_MASK GENMASK(15, 0) #define TMU_RTR_CS_3_TS_PACKET_INTERVAL_MASK GENMASK(31, 16) #define TMU_RTR_CS_3_TS_PACKET_INTERVAL_SHIFT 16 +#define TMU_RTR_CS_15 0xf +#define TMU_RTR_CS_15_FREQ_AVG_MASK GENMASK(5, 0) +#define TMU_RTR_CS_15_DELAY_AVG_MASK GENMASK(11, 6) +#define TMU_RTR_CS_15_OFFSET_AVG_MASK GENMASK(17, 12) +#define TMU_RTR_CS_15_ERROR_AVG_MASK GENMASK(23, 18) #define TMU_RTR_CS_22 0x16 #define TMU_RTR_CS_24 0x18 #define TMU_RTR_CS_25 0x19 diff --git a/drivers/thunderbolt/tmu.c b/drivers/thunderbolt/tmu.c index 985ca43b8f39..e822ab90338b 100644 --- a/drivers/thunderbolt/tmu.c +++ b/drivers/thunderbolt/tmu.c @@ -11,6 +11,55 @@ #include "tb.h" +static int tb_switch_set_tmu_mode_params(struct tb_switch *sw, + enum tb_switch_tmu_rate rate) +{ + u32 freq_meas_wind[2] = { 30, 800 }; + u32 avg_const[2] = { 4, 8 }; + u32 freq, avg, val; + int ret; + + if (rate == TB_SWITCH_TMU_RATE_NORMAL) { + freq = freq_meas_wind[0]; + avg = avg_const[0]; + } else if (rate == TB_SWITCH_TMU_RATE_HIFI) { + freq = freq_meas_wind[1]; + avg = avg_const[1]; + } else { + return 0; + } + + ret = tb_sw_read(sw, &val, TB_CFG_SWITCH, + sw->tmu.cap + TMU_RTR_CS_0, 1); + if (ret) + return ret; + + val &= ~TMU_RTR_CS_0_FREQ_WIND_MASK; + val |= FIELD_PREP(TMU_RTR_CS_0_FREQ_WIND_MASK, freq); + + ret = tb_sw_write(sw, &val, TB_CFG_SWITCH, + sw->tmu.cap + TMU_RTR_CS_0, 1); + if (ret) + return ret; + + ret = tb_sw_read(sw, &val, TB_CFG_SWITCH, + sw->tmu.cap + TMU_RTR_CS_15, 1); + if (ret) + return ret; + + val &= ~TMU_RTR_CS_15_FREQ_AVG_MASK & + ~TMU_RTR_CS_15_DELAY_AVG_MASK & + ~TMU_RTR_CS_15_OFFSET_AVG_MASK & + ~TMU_RTR_CS_15_ERROR_AVG_MASK; + val |= FIELD_PREP(TMU_RTR_CS_15_FREQ_AVG_MASK, avg) | + FIELD_PREP(TMU_RTR_CS_15_DELAY_AVG_MASK, avg) | + FIELD_PREP(TMU_RTR_CS_15_OFFSET_AVG_MASK, avg) | + FIELD_PREP(TMU_RTR_CS_15_ERROR_AVG_MASK, avg); + + return tb_sw_write(sw, &val, TB_CFG_SWITCH, + sw->tmu.cap + TMU_RTR_CS_15, 1); +} + static const char *tb_switch_tmu_mode_name(const struct tb_switch *sw) { bool root_switch = !tb_route(sw); @@ -348,7 +397,7 @@ int tb_switch_tmu_disable(struct tb_switch *sw) if (tb_route(sw)) { - bool unidirectional = tb_switch_tmu_hifi_is_enabled(sw, true); + bool unidirectional = sw->tmu.unidirectional; struct tb_switch *parent = tb_switch_parent(sw); struct tb_port *down, *up; int ret; @@ -412,6 +461,7 @@ static void __tb_switch_tmu_off(struct tb_switch *sw, bool unidirectional) else tb_switch_tmu_rate_write(sw, TB_SWITCH_TMU_RATE_OFF); + tb_switch_set_tmu_mode_params(sw, sw->tmu.rate); tb_port_tmu_unidirectional_disable(down); tb_port_tmu_unidirectional_disable(up); } @@ -493,7 +543,11 @@ static int __tb_switch_tmu_enable_unidirectional(struct tb_switch *sw) up = tb_upstream_port(sw); down = tb_port_at(tb_route(sw), parent); - ret = tb_switch_tmu_rate_write(parent, TB_SWITCH_TMU_RATE_HIFI); + ret = tb_switch_tmu_rate_write(parent, sw->tmu.rate_request); + if (ret) + return ret; + + ret = tb_switch_set_tmu_mode_params(sw, sw->tmu.rate_request); if (ret) return ret; @@ -520,7 +574,83 @@ out: return ret; } -static int tb_switch_tmu_hifi_enable(struct tb_switch *sw) +static void __tb_switch_tmu_change_mode_prev(struct tb_switch *sw) +{ + struct tb_switch *parent = tb_switch_parent(sw); + struct tb_port *down, *up; + + down = tb_port_at(tb_route(sw), parent); + up = tb_upstream_port(sw); + /* + * In case of any failure in one of the steps when change mode, + * get back to the TMU configurations in previous mode. + * In case of additional failures in the functions below, + * ignore them since the caller shall already report a failure. + */ + tb_port_tmu_set_unidirectional(down, sw->tmu.unidirectional); + if (sw->tmu.unidirectional_request) + tb_switch_tmu_rate_write(parent, sw->tmu.rate); + else + tb_switch_tmu_rate_write(sw, sw->tmu.rate); + + tb_switch_set_tmu_mode_params(sw, sw->tmu.rate); + tb_port_tmu_set_unidirectional(up, sw->tmu.unidirectional); +} + +static int __tb_switch_tmu_change_mode(struct tb_switch *sw) +{ + struct tb_switch *parent = tb_switch_parent(sw); + struct tb_port *up, *down; + int ret; + + up = tb_upstream_port(sw); + down = tb_port_at(tb_route(sw), parent); + ret = tb_port_tmu_set_unidirectional(down, sw->tmu.unidirectional_request); + if (ret) + goto out; + + if (sw->tmu.unidirectional_request) + ret = tb_switch_tmu_rate_write(parent, sw->tmu.rate_request); + else + ret = tb_switch_tmu_rate_write(sw, sw->tmu.rate_request); + if (ret) + return ret; + + ret = tb_switch_set_tmu_mode_params(sw, sw->tmu.rate_request); + if (ret) + return ret; + + ret = tb_port_tmu_set_unidirectional(up, sw->tmu.unidirectional_request); + if (ret) + goto out; + + ret = tb_port_tmu_time_sync_enable(down); + if (ret) + goto out; + + ret = tb_port_tmu_time_sync_enable(up); + if (ret) + goto out; + + return 0; + +out: + __tb_switch_tmu_change_mode_prev(sw); + return ret; +} + +/** + * tb_switch_tmu_enable() - Enable TMU on a router + * @sw: Router whose TMU to enable + * + * Enables TMU of a router to be in uni-directional Normal/HiFi + * or bi-directional HiFi mode. Calling tb_switch_tmu_configure() is required + * before calling this function, to select the mode Normal/HiFi and + * directionality (uni-directional/bi-directional). + * In HiFi mode all tunneling should work. In Normal mode, DP tunneling can't + * work. Uni-directional mode is required for CLx (Link Low-Power) to work. + */ +int tb_switch_tmu_enable(struct tb_switch *sw) { bool unidirectional = sw->tmu.unidirectional_request; int ret; @@ -536,12 +666,15 @@ static int tb_switch_tmu_hifi_enable(struct tb_switch *sw) if (!tb_switch_is_clx_supported(sw)) return 0; - if (tb_switch_tmu_hifi_is_enabled(sw, sw->tmu.unidirectional_request)) + if (tb_switch_tmu_is_enabled(sw, sw->tmu.unidirectional_request)) return 0; if (tb_switch_is_titan_ridge(sw) && unidirectional) { - /* Titan Ridge supports only CL0s */ - if (!tb_switch_is_cl0s_enabled(sw)) + /* + * Titan Ridge supports CL0s and CL1 only. CL0s and CL1 are + * enabled and supported together. + */ + if (!tb_switch_is_clx_enabled(sw, TB_CL1)) return -EOPNOTSUPP; ret = tb_switch_tmu_objection_mask(sw); @@ -558,7 +691,11 @@ static int tb_switch_tmu_hifi_enable(struct tb_switch *sw) return ret; if (tb_route(sw)) { - /* The used mode changes are from OFF to HiFi-Uni/HiFi-BiDir */ + /* + * The used mode changes are from OFF to + * HiFi-Uni/HiFi-BiDir/Normal-Uni or from Normal-Uni to + * HiFi-Uni. + */ if (sw->tmu.rate == TB_SWITCH_TMU_RATE_OFF) { if (unidirectional) ret = __tb_switch_tmu_enable_unidirectional(sw); @@ -566,6 +703,10 @@ static int tb_switch_tmu_hifi_enable(struct tb_switch *sw) ret = __tb_switch_tmu_enable_bidirectional(sw); if (ret) return ret; + } else if (sw->tmu.rate == TB_SWITCH_TMU_RATE_NORMAL) { + ret = __tb_switch_tmu_change_mode(sw); + if (ret) + return ret; } sw->tmu.unidirectional = unidirectional; } else { @@ -575,35 +716,17 @@ static int tb_switch_tmu_hifi_enable(struct tb_switch *sw) * of the child node - see above. * Here only the host router' rate configuration is written. */ - ret = tb_switch_tmu_rate_write(sw, TB_SWITCH_TMU_RATE_HIFI); + ret = tb_switch_tmu_rate_write(sw, sw->tmu.rate_request); if (ret) return ret; } - sw->tmu.rate = TB_SWITCH_TMU_RATE_HIFI; + sw->tmu.rate = sw->tmu.rate_request; tb_sw_dbg(sw, "TMU: mode set to: %s\n", tb_switch_tmu_mode_name(sw)); return tb_switch_tmu_set_time_disruption(sw, false); } -/** - * tb_switch_tmu_enable() - Enable TMU on a router - * @sw: Router whose TMU to enable - * - * Enables TMU of a router to be in uni-directional or bi-directional HiFi mode. - * Calling tb_switch_tmu_configure() is required before calling this function, - * to select the mode HiFi and directionality (uni-directional/bi-directional). - * In both modes all tunneling should work. Uni-directional mode is required for - * CLx (Link Low-Power) to work. - */ -int tb_switch_tmu_enable(struct tb_switch *sw) -{ - if (sw->tmu.rate_request == TB_SWITCH_TMU_RATE_NORMAL) - return -EOPNOTSUPP; - - return tb_switch_tmu_hifi_enable(sw); -} - /** * tb_switch_tmu_configure() - Configure the TMU rate and directionality * @sw: Router whose mode to change -- cgit v1.2.3 From 3084b48fa13931400f316e2b9ffdd98b8ba6600e Mon Sep 17 00:00:00 2001 From: Gil Fine Date: Thu, 26 May 2022 13:59:21 +0300 Subject: thunderbolt: Change TMU mode to HiFi uni-directional once DisplayPort tunneled Here we configure TMU mode to HiFi uni-directional once DP tunnel is created. This is due to accuracy requirement for DP tunneling as appears in CM guide 1.0, section 7.3.2. Due to Intel hardware limitation, once we changed the TMU mode to HiFi uni-directional (when DP tunnel exists), we don't change TMU mode back to normal uni-directional, even if DP tunnel is torn down later. Signed-off-by: Gil Fine Signed-off-by: Mika Westerberg --- drivers/thunderbolt/tb.c | 13 +++++++++++++ drivers/thunderbolt/tb.h | 2 ++ drivers/thunderbolt/tmu.c | 29 +++++++++++++++++++++++++++++ 3 files changed, 44 insertions(+) (limited to 'drivers/thunderbolt') diff --git a/drivers/thunderbolt/tb.c b/drivers/thunderbolt/tb.c index 8e9fdc4e0650..9853f6c7e81d 100644 --- a/drivers/thunderbolt/tb.c +++ b/drivers/thunderbolt/tb.c @@ -118,6 +118,13 @@ static void tb_switch_discover_tunnels(struct tb_switch *sw, switch (port->config.type) { case TB_TYPE_DP_HDMI_IN: tunnel = tb_tunnel_discover_dp(tb, port, alloc_hopids); + /* + * In case of DP tunnel exists, change host router's + * 1st children TMU mode to HiFi for CL0s to work. + */ + if (tunnel) + tb_switch_enable_tmu_1st_child(tb->root_switch, + TB_SWITCH_TMU_RATE_HIFI); break; case TB_TYPE_PCIE_DOWN: @@ -979,6 +986,12 @@ static void tb_tunnel_dp(struct tb *tb) list_add_tail(&tunnel->list, &tcm->tunnel_list); tb_reclaim_usb3_bandwidth(tb, in, out); + /* + * In case of DP tunnel exists, change host router's 1st children + * TMU mode to HiFi for CL0s to work. + */ + tb_switch_enable_tmu_1st_child(tb->root_switch, TB_SWITCH_TMU_RATE_HIFI); + return; err_free: diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h index 3882b6eb9f51..e2f50fd90ba9 100644 --- a/drivers/thunderbolt/tb.h +++ b/drivers/thunderbolt/tb.h @@ -934,6 +934,8 @@ int tb_switch_tmu_enable(struct tb_switch *sw); void tb_switch_tmu_configure(struct tb_switch *sw, enum tb_switch_tmu_rate rate, bool unidirectional); +void tb_switch_enable_tmu_1st_child(struct tb_switch *sw, + enum tb_switch_tmu_rate rate); /** * tb_switch_tmu_is_enabled() - Checks if the specified TMU mode is enabled * @sw: Router whose TMU mode to check diff --git a/drivers/thunderbolt/tmu.c b/drivers/thunderbolt/tmu.c index e822ab90338b..626aca3124b1 100644 --- a/drivers/thunderbolt/tmu.c +++ b/drivers/thunderbolt/tmu.c @@ -742,3 +742,32 @@ void tb_switch_tmu_configure(struct tb_switch *sw, sw->tmu.unidirectional_request = unidirectional; sw->tmu.rate_request = rate; } + +static int tb_switch_tmu_config_enable(struct device *dev, void *rate) +{ + if (tb_is_switch(dev)) { + struct tb_switch *sw = tb_to_switch(dev); + + tb_switch_tmu_configure(sw, *(enum tb_switch_tmu_rate *)rate, + tb_switch_is_clx_enabled(sw, TB_CL1)); + if (tb_switch_tmu_enable(sw)) + tb_sw_dbg(sw, "fail switching TMU mode for 1st depth router\n"); + } + + return 0; +} + +/** + * tb_switch_enable_tmu_1st_child - Configure and enable TMU for 1st chidren + * @sw: The router to configure and enable it's children TMU + * @rate: Rate of the TMU to configure the router's chidren to + * + * Configures and enables the TMU mode of 1st depth children of the specified + * router to the specified rate. + */ +void tb_switch_enable_tmu_1st_child(struct tb_switch *sw, + enum tb_switch_tmu_rate rate) +{ + device_for_each_child(&sw->dev, &rate, + tb_switch_tmu_config_enable); +} -- cgit v1.2.3 From e173b7d46c0413f004450bfc48f45bfc0f0f2fc2 Mon Sep 17 00:00:00 2001 From: Yang Li Date: Thu, 26 May 2022 21:01:54 +0800 Subject: thunderbolt: Fix some kernel-doc comments Remove some warnings found by running scripts/kernel-doc, which is caused by using 'make W=1'. drivers/thunderbolt/ctl.c:701: warning: expecting prototype for tb_cfg_start(). Prototype was for tb_ctl_start() instead drivers/thunderbolt/ctl.c:722: warning: expecting prototype for tb_ctrl_stop(). Prototype was for tb_ctl_stop() instead drivers/thunderbolt/ctl.c:930: warning: expecting prototype for tb_cfg_write(). Prototype was for tb_cfg_write_raw() instead Reported-by: Abaci Robot Signed-off-by: Yang Li Signed-off-by: Mika Westerberg --- drivers/thunderbolt/ctl.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/thunderbolt') diff --git a/drivers/thunderbolt/ctl.c b/drivers/thunderbolt/ctl.c index e92c658dba1c..e5ede5debfb0 100644 --- a/drivers/thunderbolt/ctl.c +++ b/drivers/thunderbolt/ctl.c @@ -694,7 +694,7 @@ void tb_ctl_free(struct tb_ctl *ctl) } /** - * tb_cfg_start() - start/resume the control channel + * tb_ctl_start() - start/resume the control channel * @ctl: Control channel to start */ void tb_ctl_start(struct tb_ctl *ctl) @@ -710,7 +710,7 @@ void tb_ctl_start(struct tb_ctl *ctl) } /** - * tb_ctrl_stop() - pause the control channel + * tb_ctl_stop() - pause the control channel * @ctl: Control channel to stop * * All invocations of ctl->callback will have finished after this method @@ -912,7 +912,7 @@ struct tb_cfg_result tb_cfg_read_raw(struct tb_ctl *ctl, void *buffer, } /** - * tb_cfg_write() - write from buffer into config space + * tb_cfg_write_raw() - write from buffer into config space * @ctl: Pointer to the control channel * @buffer: Data to write * @route: Route string of the router -- cgit v1.2.3 From 7ec58378a985618909ffae18e4ac0de2ae625f33 Mon Sep 17 00:00:00 2001 From: George D Sworo Date: Wed, 1 Jun 2022 15:41:02 -0700 Subject: thunderbolt: Add support for Intel Raptor Lake Intel Raptor Lake has the same integrated Thunderbolt/USB4 controller as Intel Alder Lake. By default it is still using firmware based connection manager so we can use most of the Alder Lake flows. Signed-off-by: George D Sworo Signed-off-by: Mika Westerberg --- drivers/thunderbolt/icm.c | 2 ++ drivers/thunderbolt/nhi.c | 4 ++++ drivers/thunderbolt/nhi.h | 2 ++ 3 files changed, 8 insertions(+) (limited to 'drivers/thunderbolt') diff --git a/drivers/thunderbolt/icm.c b/drivers/thunderbolt/icm.c index fff0c740c8f3..ae38f0d25a8d 100644 --- a/drivers/thunderbolt/icm.c +++ b/drivers/thunderbolt/icm.c @@ -2516,6 +2516,8 @@ struct tb *icm_probe(struct tb_nhi *nhi) case PCI_DEVICE_ID_INTEL_TGL_H_NHI1: case PCI_DEVICE_ID_INTEL_ADL_NHI0: case PCI_DEVICE_ID_INTEL_ADL_NHI1: + case PCI_DEVICE_ID_INTEL_RPL_NHI0: + case PCI_DEVICE_ID_INTEL_RPL_NHI1: icm->is_supported = icm_tgl_is_supported; icm->driver_ready = icm_icl_driver_ready; icm->set_uuid = icm_icl_set_uuid; diff --git a/drivers/thunderbolt/nhi.c b/drivers/thunderbolt/nhi.c index 1333b158a95e..cb8c9c4ae93a 100644 --- a/drivers/thunderbolt/nhi.c +++ b/drivers/thunderbolt/nhi.c @@ -1410,6 +1410,10 @@ static struct pci_device_id nhi_ids[] = { .driver_data = (kernel_ulong_t)&icl_nhi_ops }, { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ADL_NHI1), .driver_data = (kernel_ulong_t)&icl_nhi_ops }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_RPL_NHI0), + .driver_data = (kernel_ulong_t)&icl_nhi_ops }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_RPL_NHI1), + .driver_data = (kernel_ulong_t)&icl_nhi_ops }, /* Any USB4 compliant host */ { PCI_DEVICE_CLASS(PCI_CLASS_SERIAL_USB_USB4, ~0) }, diff --git a/drivers/thunderbolt/nhi.h b/drivers/thunderbolt/nhi.h index 69083aab2736..f09da5b62233 100644 --- a/drivers/thunderbolt/nhi.h +++ b/drivers/thunderbolt/nhi.h @@ -80,6 +80,8 @@ extern const struct tb_nhi_ops icl_nhi_ops; #define PCI_DEVICE_ID_INTEL_TGL_NHI1 0x9a1d #define PCI_DEVICE_ID_INTEL_TGL_H_NHI0 0x9a1f #define PCI_DEVICE_ID_INTEL_TGL_H_NHI1 0x9a21 +#define PCI_DEVICE_ID_INTEL_RPL_NHI0 0xa73e +#define PCI_DEVICE_ID_INTEL_RPL_NHI1 0xa76d #define PCI_CLASS_SERIAL_USB_USB4 0x0c0340 -- cgit v1.2.3 From 34b9715b7caee2e2b7d74bb4230f2be2c2765c0a Mon Sep 17 00:00:00 2001 From: Xiang Wangx Date: Fri, 17 Jun 2022 00:38:30 +0800 Subject: thunderbolt: Fix typo in comment Delete the redundant word 'the'. Signed-off-by: Xiang Wangx Signed-off-by: Mika Westerberg --- drivers/thunderbolt/ctl.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/thunderbolt') diff --git a/drivers/thunderbolt/ctl.h b/drivers/thunderbolt/ctl.h index e8c64898dfce..7c7d80f96c0c 100644 --- a/drivers/thunderbolt/ctl.h +++ b/drivers/thunderbolt/ctl.h @@ -35,7 +35,7 @@ struct tb_cfg_result { * If err = 1 then this is the port that send the * error. * If err = 0 and if this was a cfg_read/write then - * this is the the upstream port of the responding + * this is the upstream port of the responding * switch. * Otherwise the field is set to zero. */ -- cgit v1.2.3