2019-12-17 15:33:43 +03:00
// SPDX-License-Identifier: GPL-2.0
/*
* Thunderbolt Time Management Unit ( TMU ) support
*
* Copyright ( C ) 2019 , Intel Corporation
* Authors : Mika Westerberg < mika . westerberg @ linux . intel . com >
* Rajmohan Mani < rajmohan . mani @ intel . com >
*/
# include <linux/delay.h>
# include "tb.h"
2022-10-11 12:11:09 +03:00
static const unsigned int tmu_rates [ ] = {
[ TB_SWITCH_TMU_MODE_OFF ] = 0 ,
[ TB_SWITCH_TMU_MODE_LOWRES ] = 1000 ,
[ TB_SWITCH_TMU_MODE_HIFI_UNI ] = 16 ,
[ TB_SWITCH_TMU_MODE_HIFI_BI ] = 16 ,
[ TB_SWITCH_TMU_MODE_MEDRES_ENHANCED_UNI ] = 16 ,
} ;
2023-07-02 15:06:08 -04:00
static const struct {
2022-10-11 12:11:09 +03:00
unsigned int freq_meas_window ;
unsigned int avg_const ;
unsigned int delta_avg_const ;
unsigned int repl_timeout ;
unsigned int repl_threshold ;
unsigned int repl_n ;
unsigned int dirswitch_n ;
} tmu_params [ ] = {
[ TB_SWITCH_TMU_MODE_OFF ] = { } ,
[ TB_SWITCH_TMU_MODE_LOWRES ] = { 30 , 4 , } ,
[ TB_SWITCH_TMU_MODE_HIFI_UNI ] = { 800 , 8 , } ,
[ TB_SWITCH_TMU_MODE_HIFI_BI ] = { 800 , 8 , } ,
[ TB_SWITCH_TMU_MODE_MEDRES_ENHANCED_UNI ] = {
800 , 4 , 0 , 3125 , 25 , 128 , 255 ,
} ,
} ;
static const char * tmu_mode_name ( enum tb_switch_tmu_mode mode )
{
switch ( mode ) {
case TB_SWITCH_TMU_MODE_OFF :
return " off " ;
case TB_SWITCH_TMU_MODE_LOWRES :
return " uni-directional, LowRes " ;
case TB_SWITCH_TMU_MODE_HIFI_UNI :
return " uni-directional, HiFi " ;
case TB_SWITCH_TMU_MODE_HIFI_BI :
return " bi-directional, HiFi " ;
case TB_SWITCH_TMU_MODE_MEDRES_ENHANCED_UNI :
return " enhanced uni-directional, MedRes " ;
default :
return " unknown " ;
}
}
static bool tb_switch_tmu_enhanced_is_supported ( const struct tb_switch * sw )
{
return usb4_switch_version ( sw ) > 1 ;
}
2022-05-26 13:59:20 +03:00
static int tb_switch_set_tmu_mode_params ( struct tb_switch * sw ,
2022-10-11 12:11:09 +03:00
enum tb_switch_tmu_mode mode )
2022-05-26 13:59:20 +03:00
{
u32 freq , avg , val ;
int ret ;
2022-10-11 12:11:09 +03:00
freq = tmu_params [ mode ] . freq_meas_window ;
avg = tmu_params [ mode ] . avg_const ;
2022-05-26 13:59:20 +03:00
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 ) ;
2022-10-11 12:11:09 +03:00
ret = tb_sw_write ( sw , & val , TB_CFG_SWITCH ,
sw - > tmu . cap + TMU_RTR_CS_15 , 1 ) ;
if ( ret )
return ret ;
2019-12-17 15:33:43 +03:00
2022-10-11 12:11:09 +03:00
if ( tb_switch_tmu_enhanced_is_supported ( sw ) ) {
u32 delta_avg = tmu_params [ mode ] . delta_avg_const ;
2019-12-17 15:33:43 +03:00
2022-10-11 12:11:09 +03:00
ret = tb_sw_read ( sw , & val , TB_CFG_SWITCH ,
sw - > tmu . cap + TMU_RTR_CS_18 , 1 ) ;
if ( ret )
return ret ;
2019-12-17 15:33:43 +03:00
2022-10-11 12:11:09 +03:00
val & = ~ TMU_RTR_CS_18_DELTA_AVG_CONST_MASK ;
val | = FIELD_PREP ( TMU_RTR_CS_18_DELTA_AVG_CONST_MASK , delta_avg ) ;
2019-12-17 15:33:43 +03:00
2022-10-11 12:11:09 +03:00
ret = tb_sw_write ( sw , & val , TB_CFG_SWITCH ,
sw - > tmu . cap + TMU_RTR_CS_18 , 1 ) ;
2019-12-17 15:33:43 +03:00
}
2022-10-11 12:11:09 +03:00
return ret ;
2019-12-17 15:33:43 +03:00
}
2022-10-11 12:11:09 +03:00
static bool tb_switch_tmu_ucap_is_supported ( struct tb_switch * sw )
2019-12-17 15:33:43 +03:00
{
int ret ;
u32 val ;
ret = tb_sw_read ( sw , & val , TB_CFG_SWITCH ,
sw - > tmu . cap + TMU_RTR_CS_0 , 1 ) ;
if ( ret )
return false ;
return ! ! ( val & TMU_RTR_CS_0_UCAP ) ;
}
static int tb_switch_tmu_rate_read ( struct tb_switch * sw )
{
int ret ;
u32 val ;
ret = tb_sw_read ( sw , & val , TB_CFG_SWITCH ,
sw - > tmu . cap + TMU_RTR_CS_3 , 1 ) ;
if ( ret )
return ret ;
val > > = TMU_RTR_CS_3_TS_PACKET_INTERVAL_SHIFT ;
return val ;
}
static int tb_switch_tmu_rate_write ( struct tb_switch * sw , int rate )
{
int ret ;
u32 val ;
ret = tb_sw_read ( sw , & val , TB_CFG_SWITCH ,
sw - > tmu . cap + TMU_RTR_CS_3 , 1 ) ;
if ( ret )
return ret ;
val & = ~ TMU_RTR_CS_3_TS_PACKET_INTERVAL_MASK ;
val | = rate < < TMU_RTR_CS_3_TS_PACKET_INTERVAL_SHIFT ;
return tb_sw_write ( sw , & val , TB_CFG_SWITCH ,
sw - > tmu . cap + TMU_RTR_CS_3 , 1 ) ;
}
static int tb_port_tmu_write ( struct tb_port * port , u8 offset , u32 mask ,
u32 value )
{
u32 data ;
int ret ;
ret = tb_port_read ( port , & data , TB_CFG_PORT , port - > cap_tmu + offset , 1 ) ;
if ( ret )
return ret ;
data & = ~ mask ;
data | = value ;
return tb_port_write ( port , & data , TB_CFG_PORT ,
port - > cap_tmu + offset , 1 ) ;
}
static int tb_port_tmu_set_unidirectional ( struct tb_port * port ,
bool unidirectional )
{
u32 val ;
if ( ! port - > sw - > tmu . has_ucap )
return 0 ;
val = unidirectional ? TMU_ADP_CS_3_UDM : 0 ;
return tb_port_tmu_write ( port , TMU_ADP_CS_3 , TMU_ADP_CS_3_UDM , val ) ;
}
static inline int tb_port_tmu_unidirectional_disable ( struct tb_port * port )
{
return tb_port_tmu_set_unidirectional ( port , false ) ;
}
2021-12-17 03:16:38 +02:00
static inline int tb_port_tmu_unidirectional_enable ( struct tb_port * port )
{
return tb_port_tmu_set_unidirectional ( port , true ) ;
}
2019-12-17 15:33:43 +03:00
static bool tb_port_tmu_is_unidirectional ( struct tb_port * port )
{
int ret ;
u32 val ;
ret = tb_port_read ( port , & val , TB_CFG_PORT ,
port - > cap_tmu + TMU_ADP_CS_3 , 1 ) ;
if ( ret )
return false ;
return val & TMU_ADP_CS_3_UDM ;
}
2022-10-11 12:11:09 +03:00
static bool tb_port_tmu_is_enhanced ( struct tb_port * port )
{
int ret ;
u32 val ;
ret = tb_port_read ( port , & val , TB_CFG_PORT ,
port - > cap_tmu + TMU_ADP_CS_8 , 1 ) ;
if ( ret )
return false ;
return val & TMU_ADP_CS_8_EUDM ;
}
/* Can be called to non-v2 lane adapters too */
static int tb_port_tmu_enhanced_enable ( struct tb_port * port , bool enable )
{
int ret ;
u32 val ;
if ( ! tb_switch_tmu_enhanced_is_supported ( port - > sw ) )
return 0 ;
ret = tb_port_read ( port , & val , TB_CFG_PORT ,
port - > cap_tmu + TMU_ADP_CS_8 , 1 ) ;
if ( ret )
return ret ;
if ( enable )
val | = TMU_ADP_CS_8_EUDM ;
else
val & = ~ TMU_ADP_CS_8_EUDM ;
return tb_port_write ( port , & val , TB_CFG_PORT ,
port - > cap_tmu + TMU_ADP_CS_8 , 1 ) ;
}
static int tb_port_set_tmu_mode_params ( struct tb_port * port ,
enum tb_switch_tmu_mode mode )
{
u32 repl_timeout , repl_threshold , repl_n , dirswitch_n , val ;
int ret ;
repl_timeout = tmu_params [ mode ] . repl_timeout ;
repl_threshold = tmu_params [ mode ] . repl_threshold ;
repl_n = tmu_params [ mode ] . repl_n ;
dirswitch_n = tmu_params [ mode ] . dirswitch_n ;
ret = tb_port_read ( port , & val , TB_CFG_PORT ,
port - > cap_tmu + TMU_ADP_CS_8 , 1 ) ;
if ( ret )
return ret ;
val & = ~ TMU_ADP_CS_8_REPL_TIMEOUT_MASK ;
val & = ~ TMU_ADP_CS_8_REPL_THRESHOLD_MASK ;
val | = FIELD_PREP ( TMU_ADP_CS_8_REPL_TIMEOUT_MASK , repl_timeout ) ;
val | = FIELD_PREP ( TMU_ADP_CS_8_REPL_THRESHOLD_MASK , repl_threshold ) ;
ret = tb_port_write ( port , & val , TB_CFG_PORT ,
port - > cap_tmu + TMU_ADP_CS_8 , 1 ) ;
if ( ret )
return ret ;
ret = tb_port_read ( port , & val , TB_CFG_PORT ,
port - > cap_tmu + TMU_ADP_CS_9 , 1 ) ;
if ( ret )
return ret ;
val & = ~ TMU_ADP_CS_9_REPL_N_MASK ;
val & = ~ TMU_ADP_CS_9_DIRSWITCH_N_MASK ;
val | = FIELD_PREP ( TMU_ADP_CS_9_REPL_N_MASK , repl_n ) ;
val | = FIELD_PREP ( TMU_ADP_CS_9_DIRSWITCH_N_MASK , dirswitch_n ) ;
return tb_port_write ( port , & val , TB_CFG_PORT ,
port - > cap_tmu + TMU_ADP_CS_9 , 1 ) ;
}
/* Can be called to non-v2 lane adapters too */
static int tb_port_tmu_rate_write ( struct tb_port * port , int rate )
{
int ret ;
u32 val ;
if ( ! tb_switch_tmu_enhanced_is_supported ( port - > sw ) )
return 0 ;
ret = tb_port_read ( port , & val , TB_CFG_PORT ,
port - > cap_tmu + TMU_ADP_CS_9 , 1 ) ;
if ( ret )
return ret ;
val & = ~ TMU_ADP_CS_9_ADP_TS_INTERVAL_MASK ;
val | = FIELD_PREP ( TMU_ADP_CS_9_ADP_TS_INTERVAL_MASK , rate ) ;
return tb_port_write ( port , & val , TB_CFG_PORT ,
port - > cap_tmu + TMU_ADP_CS_9 , 1 ) ;
}
2021-12-17 03:16:38 +02:00
static int tb_port_tmu_time_sync ( struct tb_port * port , bool time_sync )
{
u32 val = time_sync ? TMU_ADP_CS_6_DTS : 0 ;
return tb_port_tmu_write ( port , TMU_ADP_CS_6 , TMU_ADP_CS_6_DTS , val ) ;
}
static int tb_port_tmu_time_sync_disable ( struct tb_port * port )
{
return tb_port_tmu_time_sync ( port , true ) ;
}
static int tb_port_tmu_time_sync_enable ( struct tb_port * port )
{
return tb_port_tmu_time_sync ( port , false ) ;
}
2019-12-17 15:33:43 +03:00
static int tb_switch_tmu_set_time_disruption ( struct tb_switch * sw , bool set )
{
2021-12-17 03:16:41 +02:00
u32 val , offset , bit ;
2019-12-17 15:33:43 +03:00
int ret ;
2021-12-17 03:16:41 +02:00
if ( tb_switch_is_usb4 ( sw ) ) {
offset = sw - > tmu . cap + TMU_RTR_CS_0 ;
bit = TMU_RTR_CS_0_TD ;
} else {
offset = sw - > cap_vsec_tmu + TB_TIME_VSEC_3_CS_26 ;
bit = TB_TIME_VSEC_3_CS_26_TD ;
}
ret = tb_sw_read ( sw , & val , TB_CFG_SWITCH , offset , 1 ) ;
2019-12-17 15:33:43 +03:00
if ( ret )
return ret ;
if ( set )
2021-12-17 03:16:41 +02:00
val | = bit ;
2019-12-17 15:33:43 +03:00
else
2021-12-17 03:16:41 +02:00
val & = ~ bit ;
2019-12-17 15:33:43 +03:00
2021-12-17 03:16:41 +02:00
return tb_sw_write ( sw , & val , TB_CFG_SWITCH , offset , 1 ) ;
2019-12-17 15:33:43 +03:00
}
2022-10-11 12:11:09 +03:00
static int tmu_mode_init ( struct tb_switch * sw )
{
bool enhanced , ucap ;
int ret , rate ;
ucap = tb_switch_tmu_ucap_is_supported ( sw ) ;
if ( ucap )
tb_sw_dbg ( sw , " TMU: supports uni-directional mode \n " ) ;
enhanced = tb_switch_tmu_enhanced_is_supported ( sw ) ;
if ( enhanced )
tb_sw_dbg ( sw , " TMU: supports enhanced uni-directional mode \n " ) ;
ret = tb_switch_tmu_rate_read ( sw ) ;
if ( ret < 0 )
return ret ;
rate = ret ;
/* Off by default */
sw - > tmu . mode = TB_SWITCH_TMU_MODE_OFF ;
if ( tb_route ( sw ) ) {
struct tb_port * up = tb_upstream_port ( sw ) ;
if ( enhanced & & tb_port_tmu_is_enhanced ( up ) ) {
sw - > tmu . mode = TB_SWITCH_TMU_MODE_MEDRES_ENHANCED_UNI ;
} else if ( ucap & & tb_port_tmu_is_unidirectional ( up ) ) {
if ( tmu_rates [ TB_SWITCH_TMU_MODE_LOWRES ] = = rate )
sw - > tmu . mode = TB_SWITCH_TMU_MODE_LOWRES ;
2023-08-31 14:10:46 +03:00
else if ( tmu_rates [ TB_SWITCH_TMU_MODE_HIFI_UNI ] = = rate )
2022-10-11 12:11:09 +03:00
sw - > tmu . mode = TB_SWITCH_TMU_MODE_HIFI_UNI ;
} else if ( rate ) {
sw - > tmu . mode = TB_SWITCH_TMU_MODE_HIFI_BI ;
}
} else if ( rate ) {
sw - > tmu . mode = TB_SWITCH_TMU_MODE_HIFI_BI ;
}
/* Update the initial request to match the current mode */
sw - > tmu . mode_request = sw - > tmu . mode ;
sw - > tmu . has_ucap = ucap ;
return 0 ;
}
2019-12-17 15:33:43 +03:00
/**
* tb_switch_tmu_init ( ) - Initialize switch TMU structures
* @ sw : Switch to initialized
*
* This function must be called before other TMU related functions to
* makes the internal structures are filled in correctly . Does not
* change any hardware configuration .
*/
int tb_switch_tmu_init ( struct tb_switch * sw )
{
struct tb_port * port ;
int ret ;
if ( tb_switch_is_icm ( sw ) )
return 0 ;
ret = tb_switch_find_cap ( sw , TB_SWITCH_CAP_TMU ) ;
if ( ret > 0 )
sw - > tmu . cap = ret ;
tb_switch_for_each_port ( sw , port ) {
int cap ;
cap = tb_port_find_cap ( port , TB_PORT_CAP_TIME1 ) ;
if ( cap > 0 )
port - > cap_tmu = cap ;
}
2022-10-11 12:11:09 +03:00
ret = tmu_mode_init ( sw ) ;
if ( ret )
2019-12-17 15:33:43 +03:00
return ret ;
2022-10-11 12:11:09 +03:00
tb_sw_dbg ( sw , " TMU: current mode: %s \n " , tmu_mode_name ( sw - > tmu . mode ) ) ;
2019-12-17 15:33:43 +03:00
return 0 ;
}
/**
* tb_switch_tmu_post_time ( ) - Update switch local time
* @ sw : Switch whose time to update
*
* Updates switch local time using time posting procedure .
*/
int tb_switch_tmu_post_time ( struct tb_switch * sw )
{
2021-12-17 03:16:38 +02:00
unsigned int post_time_high_offset , post_time_high = 0 ;
unsigned int post_local_time_offset , post_time_offset ;
2019-12-17 15:33:43 +03:00
struct tb_switch * root_switch = sw - > tb - > root_switch ;
u64 hi , mid , lo , local_time , post_time ;
int i , ret , retries = 100 ;
u32 gm_local_time [ 3 ] ;
if ( ! tb_route ( sw ) )
return 0 ;
if ( ! tb_switch_is_usb4 ( sw ) )
return 0 ;
/* Need to be able to read the grand master time */
if ( ! root_switch - > tmu . cap )
return 0 ;
ret = tb_sw_read ( root_switch , gm_local_time , TB_CFG_SWITCH ,
root_switch - > tmu . cap + TMU_RTR_CS_1 ,
ARRAY_SIZE ( gm_local_time ) ) ;
if ( ret )
return ret ;
for ( i = 0 ; i < ARRAY_SIZE ( gm_local_time ) ; i + + )
2023-05-26 13:53:35 +03:00
tb_sw_dbg ( root_switch , " TMU: local_time[%d]=0x%08x \n " , i ,
2019-12-17 15:33:43 +03:00
gm_local_time [ i ] ) ;
/* Convert to nanoseconds (drop fractional part) */
hi = gm_local_time [ 2 ] & TMU_RTR_CS_3_LOCAL_TIME_NS_MASK ;
mid = gm_local_time [ 1 ] ;
lo = ( gm_local_time [ 0 ] & TMU_RTR_CS_1_LOCAL_TIME_NS_MASK ) > >
TMU_RTR_CS_1_LOCAL_TIME_NS_SHIFT ;
local_time = hi < < 48 | mid < < 16 | lo ;
/* Tell the switch that time sync is disrupted for a while */
ret = tb_switch_tmu_set_time_disruption ( sw , true ) ;
if ( ret )
return ret ;
post_local_time_offset = sw - > tmu . cap + TMU_RTR_CS_22 ;
post_time_offset = sw - > tmu . cap + TMU_RTR_CS_24 ;
2021-12-17 03:16:38 +02:00
post_time_high_offset = sw - > tmu . cap + TMU_RTR_CS_25 ;
2019-12-17 15:33:43 +03:00
/*
* Write the Grandmaster time to the Post Local Time registers
* of the new switch .
*/
ret = tb_sw_write ( sw , & local_time , TB_CFG_SWITCH ,
post_local_time_offset , 2 ) ;
if ( ret )
goto out ;
/*
2021-12-17 03:16:38 +02:00
* Have the new switch update its local time by :
* 1 ) writing 0x1 to the Post Time Low register and 0xffffffff to
* Post Time High register .
* 2 ) write 0 to Post Time High register and then wait for
* the completion of the post_time register becomes 0.
* This means the time has been converged properly .
2019-12-17 15:33:43 +03:00
*/
2021-12-17 03:16:38 +02:00
post_time = 0xffffffff00000001ULL ;
2019-12-17 15:33:43 +03:00
ret = tb_sw_write ( sw , & post_time , TB_CFG_SWITCH , post_time_offset , 2 ) ;
if ( ret )
goto out ;
2021-12-17 03:16:38 +02:00
ret = tb_sw_write ( sw , & post_time_high , TB_CFG_SWITCH ,
post_time_high_offset , 1 ) ;
if ( ret )
goto out ;
2019-12-17 15:33:43 +03:00
do {
usleep_range ( 5 , 10 ) ;
ret = tb_sw_read ( sw , & post_time , TB_CFG_SWITCH ,
post_time_offset , 2 ) ;
if ( ret )
goto out ;
} while ( - - retries & & post_time ) ;
if ( ! retries ) {
ret = - ETIMEDOUT ;
goto out ;
}
tb_sw_dbg ( sw , " TMU: updated local time to %#llx \n " , local_time ) ;
out :
tb_switch_tmu_set_time_disruption ( sw , false ) ;
return ret ;
}
2022-10-11 12:11:09 +03:00
static int disable_enhanced ( struct tb_port * up , struct tb_port * down )
{
int ret ;
/*
* Router may already been disconnected so ignore errors on the
* upstream port .
*/
tb_port_tmu_rate_write ( up , 0 ) ;
tb_port_tmu_enhanced_enable ( up , false ) ;
ret = tb_port_tmu_rate_write ( down , 0 ) ;
if ( ret )
return ret ;
return tb_port_tmu_enhanced_enable ( down , false ) ;
}
2019-12-17 15:33:43 +03:00
/**
* tb_switch_tmu_disable ( ) - Disable TMU of a switch
* @ sw : Switch whose TMU to disable
*
* Turns off TMU of @ sw if it is enabled . If not enabled does nothing .
*/
int tb_switch_tmu_disable ( struct tb_switch * sw )
{
/* Already disabled? */
2022-10-11 12:11:09 +03:00
if ( sw - > tmu . mode = = TB_SWITCH_TMU_MODE_OFF )
2019-12-17 15:33:43 +03:00
return 0 ;
2021-12-17 03:16:38 +02:00
if ( tb_route ( sw ) ) {
struct tb_port * down , * up ;
int ret ;
2019-12-17 15:33:43 +03:00
2022-09-23 01:30:38 +03:00
down = tb_switch_downstream_port ( sw ) ;
2021-12-17 03:16:38 +02:00
up = tb_upstream_port ( 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 .
2022-05-26 13:59:19 +03:00
* 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 .
2021-12-17 03:16:38 +02:00
*/
2023-08-02 06:11:49 -05:00
ret = tb_switch_tmu_rate_write ( sw , tmu_rates [ TB_SWITCH_TMU_MODE_OFF ] ) ;
if ( ret )
return ret ;
2021-12-17 03:16:38 +02:00
tb_port_tmu_time_sync_disable ( up ) ;
ret = tb_port_tmu_time_sync_disable ( down ) ;
2019-12-17 15:33:43 +03:00
if ( ret )
return ret ;
2022-10-11 12:11:09 +03:00
switch ( sw - > tmu . mode ) {
case TB_SWITCH_TMU_MODE_LOWRES :
case TB_SWITCH_TMU_MODE_HIFI_UNI :
2021-12-17 03:16:38 +02:00
/* The switch may be unplugged so ignore any errors */
tb_port_tmu_unidirectional_disable ( up ) ;
ret = tb_port_tmu_unidirectional_disable ( down ) ;
if ( ret )
return ret ;
2022-10-11 12:11:09 +03:00
break ;
case TB_SWITCH_TMU_MODE_MEDRES_ENHANCED_UNI :
ret = disable_enhanced ( up , down ) ;
if ( ret )
return ret ;
break ;
default :
break ;
2021-12-17 03:16:38 +02:00
}
} else {
2022-10-11 12:11:09 +03:00
tb_switch_tmu_rate_write ( sw , tmu_rates [ TB_SWITCH_TMU_MODE_OFF ] ) ;
2021-12-17 03:16:38 +02:00
}
2019-12-17 15:33:43 +03:00
2022-10-11 12:11:09 +03:00
sw - > tmu . mode = TB_SWITCH_TMU_MODE_OFF ;
2019-12-17 15:33:43 +03:00
tb_sw_dbg ( sw , " TMU: disabled \n " ) ;
return 0 ;
}
2022-10-11 12:11:09 +03:00
/* Called only when there is failure enabling requested mode */
static void tb_switch_tmu_off ( struct tb_switch * sw )
2021-12-17 03:16:38 +02:00
{
2022-10-11 12:11:09 +03:00
unsigned int rate = tmu_rates [ TB_SWITCH_TMU_MODE_OFF ] ;
2021-12-17 03:16:38 +02:00
struct tb_port * down , * up ;
2022-09-23 01:30:38 +03:00
down = tb_switch_downstream_port ( sw ) ;
2021-12-17 03:16:38 +02:00
up = tb_upstream_port ( sw ) ;
/*
* In case of any failure in one of the steps when setting
* bi - directional or uni - directional TMU mode , get back to the TMU
* configurations in off mode . In case of additional failures in
* the functions below , ignore them since the caller shall already
* report a failure .
*/
tb_port_tmu_time_sync_disable ( down ) ;
tb_port_tmu_time_sync_disable ( up ) ;
2022-10-11 12:11:09 +03:00
switch ( sw - > tmu . mode_request ) {
case TB_SWITCH_TMU_MODE_LOWRES :
case TB_SWITCH_TMU_MODE_HIFI_UNI :
tb_switch_tmu_rate_write ( tb_switch_parent ( sw ) , rate ) ;
break ;
case TB_SWITCH_TMU_MODE_MEDRES_ENHANCED_UNI :
disable_enhanced ( up , down ) ;
break ;
default :
break ;
}
/* Always set the rate to 0 */
tb_switch_tmu_rate_write ( sw , rate ) ;
tb_switch_set_tmu_mode_params ( sw , sw - > tmu . mode ) ;
2021-12-17 03:16:38 +02:00
tb_port_tmu_unidirectional_disable ( down ) ;
tb_port_tmu_unidirectional_disable ( up ) ;
}
/*
* This function is called when the previous TMU mode was
2022-10-11 12:11:09 +03:00
* TB_SWITCH_TMU_MODE_OFF .
2019-12-17 15:33:43 +03:00
*/
2022-10-07 13:14:13 +03:00
static int tb_switch_tmu_enable_bidirectional ( struct tb_switch * sw )
2019-12-17 15:33:43 +03:00
{
2021-12-17 03:16:38 +02:00
struct tb_port * up , * down ;
2019-12-17 15:33:43 +03:00
int ret ;
2021-12-17 03:16:38 +02:00
up = tb_upstream_port ( sw ) ;
2022-09-23 01:30:38 +03:00
down = tb_switch_downstream_port ( sw ) ;
2019-12-17 15:33:43 +03:00
2021-12-17 03:16:38 +02:00
ret = tb_port_tmu_unidirectional_disable ( up ) ;
if ( ret )
return ret ;
2019-12-17 15:33:43 +03:00
2021-12-17 03:16:38 +02:00
ret = tb_port_tmu_unidirectional_disable ( down ) ;
if ( ret )
goto out ;
2022-10-11 12:11:09 +03:00
ret = tb_switch_tmu_rate_write ( sw , tmu_rates [ TB_SWITCH_TMU_MODE_HIFI_BI ] ) ;
2021-12-17 03:16:38 +02:00
if ( ret )
goto out ;
ret = tb_port_tmu_time_sync_enable ( up ) ;
if ( ret )
goto out ;
ret = tb_port_tmu_time_sync_enable ( down ) ;
if ( ret )
goto out ;
return 0 ;
out :
2022-10-11 12:11:09 +03:00
tb_switch_tmu_off ( sw ) ;
2021-12-17 03:16:38 +02:00
return ret ;
}
2023-05-26 09:22:06 +03:00
/* Only needed for Titan Ridge */
static int tb_switch_tmu_disable_objections ( struct tb_switch * sw )
2021-12-17 03:16:43 +02:00
{
2023-05-26 09:22:06 +03:00
struct tb_port * up = tb_upstream_port ( sw ) ;
2021-12-17 03:16:43 +02:00
u32 val ;
int ret ;
ret = tb_sw_read ( sw , & val , TB_CFG_SWITCH ,
sw - > cap_vsec_tmu + TB_TIME_VSEC_3_CS_9 , 1 ) ;
if ( ret )
return ret ;
val & = ~ TB_TIME_VSEC_3_CS_9_TMU_OBJ_MASK ;
2023-05-26 09:22:06 +03:00
ret = tb_sw_write ( sw , & val , TB_CFG_SWITCH ,
sw - > cap_vsec_tmu + TB_TIME_VSEC_3_CS_9 , 1 ) ;
if ( ret )
return ret ;
2021-12-17 03:16:43 +02:00
return tb_port_tmu_write ( up , TMU_ADP_CS_6 ,
TMU_ADP_CS_6_DISABLE_TMU_OBJ_MASK ,
2023-05-26 09:22:06 +03:00
TMU_ADP_CS_6_DISABLE_TMU_OBJ_CL1 |
TMU_ADP_CS_6_DISABLE_TMU_OBJ_CL2 ) ;
2021-12-17 03:16:43 +02:00
}
2021-12-17 03:16:38 +02:00
/*
* This function is called when the previous TMU mode was
2022-10-11 12:11:09 +03:00
* TB_SWITCH_TMU_MODE_OFF .
2021-12-17 03:16:38 +02:00
*/
2022-10-07 13:14:13 +03:00
static int tb_switch_tmu_enable_unidirectional ( struct tb_switch * sw )
2021-12-17 03:16:38 +02:00
{
struct tb_port * up , * down ;
int ret ;
up = tb_upstream_port ( sw ) ;
2022-09-23 01:30:38 +03:00
down = tb_switch_downstream_port ( sw ) ;
ret = tb_switch_tmu_rate_write ( tb_switch_parent ( sw ) ,
2022-10-11 12:11:09 +03:00
tmu_rates [ sw - > tmu . mode_request ] ) ;
2022-05-26 13:59:20 +03:00
if ( ret )
return ret ;
2022-10-11 12:11:09 +03:00
ret = tb_switch_set_tmu_mode_params ( sw , sw - > tmu . mode_request ) ;
2019-12-17 15:33:43 +03:00
if ( ret )
return ret ;
2021-12-17 03:16:38 +02:00
ret = tb_port_tmu_unidirectional_enable ( up ) ;
if ( ret )
goto out ;
2019-12-17 15:33:43 +03:00
2021-12-17 03:16:38 +02:00
ret = tb_port_tmu_time_sync_enable ( up ) ;
if ( ret )
goto out ;
2019-12-17 15:33:43 +03:00
2021-12-17 03:16:38 +02:00
ret = tb_port_tmu_unidirectional_enable ( down ) ;
if ( ret )
goto out ;
2019-12-17 15:33:43 +03:00
2021-12-17 03:16:38 +02:00
ret = tb_port_tmu_time_sync_enable ( down ) ;
if ( ret )
goto out ;
2019-12-17 15:33:43 +03:00
2021-12-17 03:16:38 +02:00
return 0 ;
out :
2022-10-11 12:11:09 +03:00
tb_switch_tmu_off ( sw ) ;
return ret ;
}
/*
* This function is called when the previous TMU mode was
* TB_SWITCH_TMU_RATE_OFF .
*/
static int tb_switch_tmu_enable_enhanced ( struct tb_switch * sw )
{
unsigned int rate = tmu_rates [ sw - > tmu . mode_request ] ;
struct tb_port * up , * down ;
int ret ;
/* Router specific parameters first */
ret = tb_switch_set_tmu_mode_params ( sw , sw - > tmu . mode_request ) ;
if ( ret )
return ret ;
up = tb_upstream_port ( sw ) ;
down = tb_switch_downstream_port ( sw ) ;
ret = tb_port_set_tmu_mode_params ( up , sw - > tmu . mode_request ) ;
if ( ret )
goto out ;
ret = tb_port_tmu_rate_write ( up , rate ) ;
if ( ret )
goto out ;
ret = tb_port_tmu_enhanced_enable ( up , true ) ;
if ( ret )
goto out ;
ret = tb_port_set_tmu_mode_params ( down , sw - > tmu . mode_request ) ;
if ( ret )
goto out ;
ret = tb_port_tmu_rate_write ( down , rate ) ;
if ( ret )
goto out ;
ret = tb_port_tmu_enhanced_enable ( down , true ) ;
if ( ret )
goto out ;
return 0 ;
out :
tb_switch_tmu_off ( sw ) ;
2021-12-17 03:16:38 +02:00
return ret ;
}
2022-10-07 13:14:13 +03:00
static void tb_switch_tmu_change_mode_prev ( struct tb_switch * sw )
2022-05-26 13:59:20 +03:00
{
2022-10-11 12:11:09 +03:00
unsigned int rate = tmu_rates [ sw - > tmu . mode ] ;
2022-05-26 13:59:20 +03:00
struct tb_port * down , * up ;
2022-09-23 01:30:38 +03:00
down = tb_switch_downstream_port ( sw ) ;
2022-05-26 13:59:20 +03:00
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 .
*/
2022-10-11 12:11:09 +03:00
switch ( sw - > tmu . mode ) {
case TB_SWITCH_TMU_MODE_LOWRES :
case TB_SWITCH_TMU_MODE_HIFI_UNI :
tb_port_tmu_set_unidirectional ( down , true ) ;
tb_switch_tmu_rate_write ( tb_switch_parent ( sw ) , rate ) ;
break ;
case TB_SWITCH_TMU_MODE_HIFI_BI :
tb_port_tmu_set_unidirectional ( down , false ) ;
tb_switch_tmu_rate_write ( sw , rate ) ;
break ;
2022-05-26 13:59:20 +03:00
2022-10-11 12:11:09 +03:00
default :
break ;
}
tb_switch_set_tmu_mode_params ( sw , sw - > tmu . mode ) ;
switch ( sw - > tmu . mode ) {
case TB_SWITCH_TMU_MODE_LOWRES :
case TB_SWITCH_TMU_MODE_HIFI_UNI :
tb_port_tmu_set_unidirectional ( up , true ) ;
break ;
case TB_SWITCH_TMU_MODE_HIFI_BI :
tb_port_tmu_set_unidirectional ( up , false ) ;
break ;
default :
break ;
}
2022-05-26 13:59:20 +03:00
}
2022-10-07 13:14:13 +03:00
static int tb_switch_tmu_change_mode ( struct tb_switch * sw )
2022-05-26 13:59:20 +03:00
{
2022-10-11 12:11:09 +03:00
unsigned int rate = tmu_rates [ sw - > tmu . mode_request ] ;
2022-05-26 13:59:20 +03:00
struct tb_port * up , * down ;
int ret ;
up = tb_upstream_port ( sw ) ;
2022-09-23 01:30:38 +03:00
down = tb_switch_downstream_port ( sw ) ;
2022-05-26 13:59:20 +03:00
2022-10-11 12:11:09 +03:00
/* Program the upstream router downstream facing lane adapter */
switch ( sw - > tmu . mode_request ) {
case TB_SWITCH_TMU_MODE_LOWRES :
case TB_SWITCH_TMU_MODE_HIFI_UNI :
ret = tb_port_tmu_set_unidirectional ( down , true ) ;
if ( ret )
goto out ;
ret = tb_switch_tmu_rate_write ( tb_switch_parent ( sw ) , rate ) ;
if ( ret )
goto out ;
break ;
case TB_SWITCH_TMU_MODE_HIFI_BI :
ret = tb_port_tmu_set_unidirectional ( down , false ) ;
if ( ret )
goto out ;
ret = tb_switch_tmu_rate_write ( sw , rate ) ;
if ( ret )
goto out ;
break ;
2022-05-26 13:59:20 +03:00
2022-10-11 12:11:09 +03:00
default :
/* Not allowed to change modes from other than above */
return - EINVAL ;
}
ret = tb_switch_set_tmu_mode_params ( sw , sw - > tmu . mode_request ) ;
2022-05-26 13:59:20 +03:00
if ( ret )
return ret ;
2022-10-11 12:11:09 +03:00
/* Program the new mode and the downstream router lane adapter */
switch ( sw - > tmu . mode_request ) {
case TB_SWITCH_TMU_MODE_LOWRES :
case TB_SWITCH_TMU_MODE_HIFI_UNI :
ret = tb_port_tmu_set_unidirectional ( up , true ) ;
if ( ret )
goto out ;
break ;
case TB_SWITCH_TMU_MODE_HIFI_BI :
ret = tb_port_tmu_set_unidirectional ( up , false ) ;
if ( ret )
goto out ;
break ;
default :
/* Not allowed to change modes from other than above */
return - EINVAL ;
}
2022-05-26 13:59:20 +03:00
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 :
2022-10-07 13:14:13 +03:00
tb_switch_tmu_change_mode_prev ( sw ) ;
2022-05-26 13:59:20 +03:00
return ret ;
}
/**
* tb_switch_tmu_enable ( ) - Enable TMU on a router
* @ sw : Router whose TMU to enable
*
2022-10-07 13:19:09 +03:00
* 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 .
2022-05-26 13:59:20 +03:00
*/
int tb_switch_tmu_enable ( struct tb_switch * sw )
2021-12-17 03:16:38 +02:00
{
int ret ;
2022-10-07 13:19:09 +03:00
if ( tb_switch_tmu_is_enabled ( sw ) )
2021-12-17 03:16:38 +02:00
return 0 ;
2022-10-11 12:11:09 +03:00
if ( tb_switch_is_titan_ridge ( sw ) & &
( sw - > tmu . mode_request = = TB_SWITCH_TMU_MODE_LOWRES | |
sw - > tmu . mode_request = = TB_SWITCH_TMU_MODE_HIFI_UNI ) ) {
2023-05-26 09:22:06 +03:00
ret = tb_switch_tmu_disable_objections ( sw ) ;
2021-12-17 03:16:43 +02:00
if ( ret )
return ret ;
}
2021-12-17 03:16:38 +02:00
ret = tb_switch_tmu_set_time_disruption ( sw , true ) ;
if ( ret )
return ret ;
if ( tb_route ( sw ) ) {
2022-05-26 13:59:20 +03:00
/*
* The used mode changes are from OFF to
* HiFi - Uni / HiFi - BiDir / Normal - Uni or from Normal - Uni to
* HiFi - Uni .
*/
2022-10-11 12:11:09 +03:00
if ( sw - > tmu . mode = = TB_SWITCH_TMU_MODE_OFF ) {
switch ( sw - > tmu . mode_request ) {
case TB_SWITCH_TMU_MODE_LOWRES :
case TB_SWITCH_TMU_MODE_HIFI_UNI :
2022-10-07 13:14:13 +03:00
ret = tb_switch_tmu_enable_unidirectional ( sw ) ;
2022-10-11 12:11:09 +03:00
break ;
case TB_SWITCH_TMU_MODE_HIFI_BI :
2022-10-07 13:14:13 +03:00
ret = tb_switch_tmu_enable_bidirectional ( sw ) ;
2022-10-11 12:11:09 +03:00
break ;
case TB_SWITCH_TMU_MODE_MEDRES_ENHANCED_UNI :
ret = tb_switch_tmu_enable_enhanced ( sw ) ;
break ;
default :
ret = - EINVAL ;
break ;
}
} else if ( sw - > tmu . mode = = TB_SWITCH_TMU_MODE_LOWRES | |
sw - > tmu . mode = = TB_SWITCH_TMU_MODE_HIFI_UNI | |
sw - > tmu . mode = = TB_SWITCH_TMU_MODE_HIFI_BI ) {
2022-10-07 13:14:13 +03:00
ret = tb_switch_tmu_change_mode ( sw ) ;
2022-10-11 12:11:09 +03:00
} else {
ret = - EINVAL ;
2021-12-17 03:16:38 +02:00
}
2019-12-17 15:33:43 +03:00
} else {
2021-12-17 03:16:38 +02:00
/*
* Host router port configurations are written as
* part of configurations for downstream port of the parent
* of the child node - see above .
* Here only the host router ' rate configuration is written .
*/
2022-10-11 12:11:09 +03:00
ret = tb_switch_tmu_rate_write ( sw , tmu_rates [ sw - > tmu . mode_request ] ) ;
2019-12-17 15:33:43 +03:00
}
2022-10-11 12:11:09 +03:00
if ( ret ) {
tb_sw_warn ( sw , " TMU: failed to enable mode %s: %d \n " ,
tmu_mode_name ( sw - > tmu . mode_request ) , ret ) ;
} else {
sw - > tmu . mode = sw - > tmu . mode_request ;
tb_sw_dbg ( sw , " TMU: mode set to: %s \n " , tmu_mode_name ( sw - > tmu . mode ) ) ;
}
2019-12-17 15:33:43 +03:00
return tb_switch_tmu_set_time_disruption ( sw , false ) ;
}
2021-12-17 03:16:38 +02:00
/**
2022-10-11 12:11:09 +03:00
* tb_switch_tmu_configure ( ) - Configure the TMU mode
2021-12-17 03:16:38 +02:00
* @ sw : Router whose mode to change
2022-10-11 12:11:09 +03:00
* @ mode : Mode to configure
2021-12-17 03:16:38 +02:00
*
2022-10-11 12:11:09 +03:00
* Selects the TMU mode that is enabled when tb_switch_tmu_enable ( ) is
* next called .
2022-10-10 18:04:16 +03:00
*
2022-10-11 12:11:09 +03:00
* Returns % 0 in success and negative errno otherwise . Specifically
* returns % - EOPNOTSUPP if the requested mode is not possible ( not
* supported by the router and / or topology ) .
2021-12-17 03:16:38 +02:00
*/
2022-10-11 12:11:09 +03:00
int tb_switch_tmu_configure ( struct tb_switch * sw , enum tb_switch_tmu_mode mode )
2021-12-17 03:16:38 +02:00
{
2022-10-11 12:11:09 +03:00
switch ( mode ) {
case TB_SWITCH_TMU_MODE_OFF :
break ;
case TB_SWITCH_TMU_MODE_LOWRES :
case TB_SWITCH_TMU_MODE_HIFI_UNI :
if ( ! sw - > tmu . has_ucap )
return - EOPNOTSUPP ;
break ;
case TB_SWITCH_TMU_MODE_HIFI_BI :
break ;
case TB_SWITCH_TMU_MODE_MEDRES_ENHANCED_UNI : {
const struct tb_switch * parent_sw = tb_switch_parent ( sw ) ;
if ( ! parent_sw | | ! tb_switch_tmu_enhanced_is_supported ( parent_sw ) )
return - EOPNOTSUPP ;
if ( ! tb_switch_tmu_enhanced_is_supported ( sw ) )
return - EOPNOTSUPP ;
break ;
}
default :
tb_sw_warn ( sw , " TMU: unsupported mode %u \n " , mode ) ;
2022-10-10 18:04:16 +03:00
return - EINVAL ;
2022-10-11 12:11:09 +03:00
}
if ( sw - > tmu . mode_request ! = mode ) {
tb_sw_dbg ( sw , " TMU: mode change %s -> %s requested \n " ,
tmu_mode_name ( sw - > tmu . mode ) , tmu_mode_name ( mode ) ) ;
sw - > tmu . mode_request = mode ;
}
2022-10-10 18:04:16 +03:00
return 0 ;
2021-12-17 03:16:38 +02:00
}