162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Thunderbolt Time Management Unit (TMU) support 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2019, Intel Corporation 662306a36Sopenharmony_ci * Authors: Mika Westerberg <mika.westerberg@linux.intel.com> 762306a36Sopenharmony_ci * Rajmohan Mani <rajmohan.mani@intel.com> 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/delay.h> 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include "tb.h" 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_cistatic const unsigned int tmu_rates[] = { 1562306a36Sopenharmony_ci [TB_SWITCH_TMU_MODE_OFF] = 0, 1662306a36Sopenharmony_ci [TB_SWITCH_TMU_MODE_LOWRES] = 1000, 1762306a36Sopenharmony_ci [TB_SWITCH_TMU_MODE_HIFI_UNI] = 16, 1862306a36Sopenharmony_ci [TB_SWITCH_TMU_MODE_HIFI_BI] = 16, 1962306a36Sopenharmony_ci [TB_SWITCH_TMU_MODE_MEDRES_ENHANCED_UNI] = 16, 2062306a36Sopenharmony_ci}; 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_cistatic const struct { 2362306a36Sopenharmony_ci unsigned int freq_meas_window; 2462306a36Sopenharmony_ci unsigned int avg_const; 2562306a36Sopenharmony_ci unsigned int delta_avg_const; 2662306a36Sopenharmony_ci unsigned int repl_timeout; 2762306a36Sopenharmony_ci unsigned int repl_threshold; 2862306a36Sopenharmony_ci unsigned int repl_n; 2962306a36Sopenharmony_ci unsigned int dirswitch_n; 3062306a36Sopenharmony_ci} tmu_params[] = { 3162306a36Sopenharmony_ci [TB_SWITCH_TMU_MODE_OFF] = { }, 3262306a36Sopenharmony_ci [TB_SWITCH_TMU_MODE_LOWRES] = { 30, 4, }, 3362306a36Sopenharmony_ci [TB_SWITCH_TMU_MODE_HIFI_UNI] = { 800, 8, }, 3462306a36Sopenharmony_ci [TB_SWITCH_TMU_MODE_HIFI_BI] = { 800, 8, }, 3562306a36Sopenharmony_ci [TB_SWITCH_TMU_MODE_MEDRES_ENHANCED_UNI] = { 3662306a36Sopenharmony_ci 800, 4, 0, 3125, 25, 128, 255, 3762306a36Sopenharmony_ci }, 3862306a36Sopenharmony_ci}; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_cistatic const char *tmu_mode_name(enum tb_switch_tmu_mode mode) 4162306a36Sopenharmony_ci{ 4262306a36Sopenharmony_ci switch (mode) { 4362306a36Sopenharmony_ci case TB_SWITCH_TMU_MODE_OFF: 4462306a36Sopenharmony_ci return "off"; 4562306a36Sopenharmony_ci case TB_SWITCH_TMU_MODE_LOWRES: 4662306a36Sopenharmony_ci return "uni-directional, LowRes"; 4762306a36Sopenharmony_ci case TB_SWITCH_TMU_MODE_HIFI_UNI: 4862306a36Sopenharmony_ci return "uni-directional, HiFi"; 4962306a36Sopenharmony_ci case TB_SWITCH_TMU_MODE_HIFI_BI: 5062306a36Sopenharmony_ci return "bi-directional, HiFi"; 5162306a36Sopenharmony_ci case TB_SWITCH_TMU_MODE_MEDRES_ENHANCED_UNI: 5262306a36Sopenharmony_ci return "enhanced uni-directional, MedRes"; 5362306a36Sopenharmony_ci default: 5462306a36Sopenharmony_ci return "unknown"; 5562306a36Sopenharmony_ci } 5662306a36Sopenharmony_ci} 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_cistatic bool tb_switch_tmu_enhanced_is_supported(const struct tb_switch *sw) 5962306a36Sopenharmony_ci{ 6062306a36Sopenharmony_ci return usb4_switch_version(sw) > 1; 6162306a36Sopenharmony_ci} 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_cistatic int tb_switch_set_tmu_mode_params(struct tb_switch *sw, 6462306a36Sopenharmony_ci enum tb_switch_tmu_mode mode) 6562306a36Sopenharmony_ci{ 6662306a36Sopenharmony_ci u32 freq, avg, val; 6762306a36Sopenharmony_ci int ret; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci freq = tmu_params[mode].freq_meas_window; 7062306a36Sopenharmony_ci avg = tmu_params[mode].avg_const; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci ret = tb_sw_read(sw, &val, TB_CFG_SWITCH, 7362306a36Sopenharmony_ci sw->tmu.cap + TMU_RTR_CS_0, 1); 7462306a36Sopenharmony_ci if (ret) 7562306a36Sopenharmony_ci return ret; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci val &= ~TMU_RTR_CS_0_FREQ_WIND_MASK; 7862306a36Sopenharmony_ci val |= FIELD_PREP(TMU_RTR_CS_0_FREQ_WIND_MASK, freq); 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci ret = tb_sw_write(sw, &val, TB_CFG_SWITCH, 8162306a36Sopenharmony_ci sw->tmu.cap + TMU_RTR_CS_0, 1); 8262306a36Sopenharmony_ci if (ret) 8362306a36Sopenharmony_ci return ret; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci ret = tb_sw_read(sw, &val, TB_CFG_SWITCH, 8662306a36Sopenharmony_ci sw->tmu.cap + TMU_RTR_CS_15, 1); 8762306a36Sopenharmony_ci if (ret) 8862306a36Sopenharmony_ci return ret; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci val &= ~TMU_RTR_CS_15_FREQ_AVG_MASK & 9162306a36Sopenharmony_ci ~TMU_RTR_CS_15_DELAY_AVG_MASK & 9262306a36Sopenharmony_ci ~TMU_RTR_CS_15_OFFSET_AVG_MASK & 9362306a36Sopenharmony_ci ~TMU_RTR_CS_15_ERROR_AVG_MASK; 9462306a36Sopenharmony_ci val |= FIELD_PREP(TMU_RTR_CS_15_FREQ_AVG_MASK, avg) | 9562306a36Sopenharmony_ci FIELD_PREP(TMU_RTR_CS_15_DELAY_AVG_MASK, avg) | 9662306a36Sopenharmony_ci FIELD_PREP(TMU_RTR_CS_15_OFFSET_AVG_MASK, avg) | 9762306a36Sopenharmony_ci FIELD_PREP(TMU_RTR_CS_15_ERROR_AVG_MASK, avg); 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci ret = tb_sw_write(sw, &val, TB_CFG_SWITCH, 10062306a36Sopenharmony_ci sw->tmu.cap + TMU_RTR_CS_15, 1); 10162306a36Sopenharmony_ci if (ret) 10262306a36Sopenharmony_ci return ret; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci if (tb_switch_tmu_enhanced_is_supported(sw)) { 10562306a36Sopenharmony_ci u32 delta_avg = tmu_params[mode].delta_avg_const; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci ret = tb_sw_read(sw, &val, TB_CFG_SWITCH, 10862306a36Sopenharmony_ci sw->tmu.cap + TMU_RTR_CS_18, 1); 10962306a36Sopenharmony_ci if (ret) 11062306a36Sopenharmony_ci return ret; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci val &= ~TMU_RTR_CS_18_DELTA_AVG_CONST_MASK; 11362306a36Sopenharmony_ci val |= FIELD_PREP(TMU_RTR_CS_18_DELTA_AVG_CONST_MASK, delta_avg); 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci ret = tb_sw_write(sw, &val, TB_CFG_SWITCH, 11662306a36Sopenharmony_ci sw->tmu.cap + TMU_RTR_CS_18, 1); 11762306a36Sopenharmony_ci } 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci return ret; 12062306a36Sopenharmony_ci} 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_cistatic bool tb_switch_tmu_ucap_is_supported(struct tb_switch *sw) 12362306a36Sopenharmony_ci{ 12462306a36Sopenharmony_ci int ret; 12562306a36Sopenharmony_ci u32 val; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci ret = tb_sw_read(sw, &val, TB_CFG_SWITCH, 12862306a36Sopenharmony_ci sw->tmu.cap + TMU_RTR_CS_0, 1); 12962306a36Sopenharmony_ci if (ret) 13062306a36Sopenharmony_ci return false; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci return !!(val & TMU_RTR_CS_0_UCAP); 13362306a36Sopenharmony_ci} 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_cistatic int tb_switch_tmu_rate_read(struct tb_switch *sw) 13662306a36Sopenharmony_ci{ 13762306a36Sopenharmony_ci int ret; 13862306a36Sopenharmony_ci u32 val; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci ret = tb_sw_read(sw, &val, TB_CFG_SWITCH, 14162306a36Sopenharmony_ci sw->tmu.cap + TMU_RTR_CS_3, 1); 14262306a36Sopenharmony_ci if (ret) 14362306a36Sopenharmony_ci return ret; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci val >>= TMU_RTR_CS_3_TS_PACKET_INTERVAL_SHIFT; 14662306a36Sopenharmony_ci return val; 14762306a36Sopenharmony_ci} 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_cistatic int tb_switch_tmu_rate_write(struct tb_switch *sw, int rate) 15062306a36Sopenharmony_ci{ 15162306a36Sopenharmony_ci int ret; 15262306a36Sopenharmony_ci u32 val; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci ret = tb_sw_read(sw, &val, TB_CFG_SWITCH, 15562306a36Sopenharmony_ci sw->tmu.cap + TMU_RTR_CS_3, 1); 15662306a36Sopenharmony_ci if (ret) 15762306a36Sopenharmony_ci return ret; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci val &= ~TMU_RTR_CS_3_TS_PACKET_INTERVAL_MASK; 16062306a36Sopenharmony_ci val |= rate << TMU_RTR_CS_3_TS_PACKET_INTERVAL_SHIFT; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci return tb_sw_write(sw, &val, TB_CFG_SWITCH, 16362306a36Sopenharmony_ci sw->tmu.cap + TMU_RTR_CS_3, 1); 16462306a36Sopenharmony_ci} 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_cistatic int tb_port_tmu_write(struct tb_port *port, u8 offset, u32 mask, 16762306a36Sopenharmony_ci u32 value) 16862306a36Sopenharmony_ci{ 16962306a36Sopenharmony_ci u32 data; 17062306a36Sopenharmony_ci int ret; 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci ret = tb_port_read(port, &data, TB_CFG_PORT, port->cap_tmu + offset, 1); 17362306a36Sopenharmony_ci if (ret) 17462306a36Sopenharmony_ci return ret; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci data &= ~mask; 17762306a36Sopenharmony_ci data |= value; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci return tb_port_write(port, &data, TB_CFG_PORT, 18062306a36Sopenharmony_ci port->cap_tmu + offset, 1); 18162306a36Sopenharmony_ci} 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_cistatic int tb_port_tmu_set_unidirectional(struct tb_port *port, 18462306a36Sopenharmony_ci bool unidirectional) 18562306a36Sopenharmony_ci{ 18662306a36Sopenharmony_ci u32 val; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci if (!port->sw->tmu.has_ucap) 18962306a36Sopenharmony_ci return 0; 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci val = unidirectional ? TMU_ADP_CS_3_UDM : 0; 19262306a36Sopenharmony_ci return tb_port_tmu_write(port, TMU_ADP_CS_3, TMU_ADP_CS_3_UDM, val); 19362306a36Sopenharmony_ci} 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_cistatic inline int tb_port_tmu_unidirectional_disable(struct tb_port *port) 19662306a36Sopenharmony_ci{ 19762306a36Sopenharmony_ci return tb_port_tmu_set_unidirectional(port, false); 19862306a36Sopenharmony_ci} 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_cistatic inline int tb_port_tmu_unidirectional_enable(struct tb_port *port) 20162306a36Sopenharmony_ci{ 20262306a36Sopenharmony_ci return tb_port_tmu_set_unidirectional(port, true); 20362306a36Sopenharmony_ci} 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_cistatic bool tb_port_tmu_is_unidirectional(struct tb_port *port) 20662306a36Sopenharmony_ci{ 20762306a36Sopenharmony_ci int ret; 20862306a36Sopenharmony_ci u32 val; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci ret = tb_port_read(port, &val, TB_CFG_PORT, 21162306a36Sopenharmony_ci port->cap_tmu + TMU_ADP_CS_3, 1); 21262306a36Sopenharmony_ci if (ret) 21362306a36Sopenharmony_ci return false; 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci return val & TMU_ADP_CS_3_UDM; 21662306a36Sopenharmony_ci} 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_cistatic bool tb_port_tmu_is_enhanced(struct tb_port *port) 21962306a36Sopenharmony_ci{ 22062306a36Sopenharmony_ci int ret; 22162306a36Sopenharmony_ci u32 val; 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci ret = tb_port_read(port, &val, TB_CFG_PORT, 22462306a36Sopenharmony_ci port->cap_tmu + TMU_ADP_CS_8, 1); 22562306a36Sopenharmony_ci if (ret) 22662306a36Sopenharmony_ci return false; 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci return val & TMU_ADP_CS_8_EUDM; 22962306a36Sopenharmony_ci} 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci/* Can be called to non-v2 lane adapters too */ 23262306a36Sopenharmony_cistatic int tb_port_tmu_enhanced_enable(struct tb_port *port, bool enable) 23362306a36Sopenharmony_ci{ 23462306a36Sopenharmony_ci int ret; 23562306a36Sopenharmony_ci u32 val; 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci if (!tb_switch_tmu_enhanced_is_supported(port->sw)) 23862306a36Sopenharmony_ci return 0; 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci ret = tb_port_read(port, &val, TB_CFG_PORT, 24162306a36Sopenharmony_ci port->cap_tmu + TMU_ADP_CS_8, 1); 24262306a36Sopenharmony_ci if (ret) 24362306a36Sopenharmony_ci return ret; 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci if (enable) 24662306a36Sopenharmony_ci val |= TMU_ADP_CS_8_EUDM; 24762306a36Sopenharmony_ci else 24862306a36Sopenharmony_ci val &= ~TMU_ADP_CS_8_EUDM; 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci return tb_port_write(port, &val, TB_CFG_PORT, 25162306a36Sopenharmony_ci port->cap_tmu + TMU_ADP_CS_8, 1); 25262306a36Sopenharmony_ci} 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_cistatic int tb_port_set_tmu_mode_params(struct tb_port *port, 25562306a36Sopenharmony_ci enum tb_switch_tmu_mode mode) 25662306a36Sopenharmony_ci{ 25762306a36Sopenharmony_ci u32 repl_timeout, repl_threshold, repl_n, dirswitch_n, val; 25862306a36Sopenharmony_ci int ret; 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci repl_timeout = tmu_params[mode].repl_timeout; 26162306a36Sopenharmony_ci repl_threshold = tmu_params[mode].repl_threshold; 26262306a36Sopenharmony_ci repl_n = tmu_params[mode].repl_n; 26362306a36Sopenharmony_ci dirswitch_n = tmu_params[mode].dirswitch_n; 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci ret = tb_port_read(port, &val, TB_CFG_PORT, 26662306a36Sopenharmony_ci port->cap_tmu + TMU_ADP_CS_8, 1); 26762306a36Sopenharmony_ci if (ret) 26862306a36Sopenharmony_ci return ret; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci val &= ~TMU_ADP_CS_8_REPL_TIMEOUT_MASK; 27162306a36Sopenharmony_ci val &= ~TMU_ADP_CS_8_REPL_THRESHOLD_MASK; 27262306a36Sopenharmony_ci val |= FIELD_PREP(TMU_ADP_CS_8_REPL_TIMEOUT_MASK, repl_timeout); 27362306a36Sopenharmony_ci val |= FIELD_PREP(TMU_ADP_CS_8_REPL_THRESHOLD_MASK, repl_threshold); 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci ret = tb_port_write(port, &val, TB_CFG_PORT, 27662306a36Sopenharmony_ci port->cap_tmu + TMU_ADP_CS_8, 1); 27762306a36Sopenharmony_ci if (ret) 27862306a36Sopenharmony_ci return ret; 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci ret = tb_port_read(port, &val, TB_CFG_PORT, 28162306a36Sopenharmony_ci port->cap_tmu + TMU_ADP_CS_9, 1); 28262306a36Sopenharmony_ci if (ret) 28362306a36Sopenharmony_ci return ret; 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci val &= ~TMU_ADP_CS_9_REPL_N_MASK; 28662306a36Sopenharmony_ci val &= ~TMU_ADP_CS_9_DIRSWITCH_N_MASK; 28762306a36Sopenharmony_ci val |= FIELD_PREP(TMU_ADP_CS_9_REPL_N_MASK, repl_n); 28862306a36Sopenharmony_ci val |= FIELD_PREP(TMU_ADP_CS_9_DIRSWITCH_N_MASK, dirswitch_n); 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci return tb_port_write(port, &val, TB_CFG_PORT, 29162306a36Sopenharmony_ci port->cap_tmu + TMU_ADP_CS_9, 1); 29262306a36Sopenharmony_ci} 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci/* Can be called to non-v2 lane adapters too */ 29562306a36Sopenharmony_cistatic int tb_port_tmu_rate_write(struct tb_port *port, int rate) 29662306a36Sopenharmony_ci{ 29762306a36Sopenharmony_ci int ret; 29862306a36Sopenharmony_ci u32 val; 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci if (!tb_switch_tmu_enhanced_is_supported(port->sw)) 30162306a36Sopenharmony_ci return 0; 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci ret = tb_port_read(port, &val, TB_CFG_PORT, 30462306a36Sopenharmony_ci port->cap_tmu + TMU_ADP_CS_9, 1); 30562306a36Sopenharmony_ci if (ret) 30662306a36Sopenharmony_ci return ret; 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci val &= ~TMU_ADP_CS_9_ADP_TS_INTERVAL_MASK; 30962306a36Sopenharmony_ci val |= FIELD_PREP(TMU_ADP_CS_9_ADP_TS_INTERVAL_MASK, rate); 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci return tb_port_write(port, &val, TB_CFG_PORT, 31262306a36Sopenharmony_ci port->cap_tmu + TMU_ADP_CS_9, 1); 31362306a36Sopenharmony_ci} 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_cistatic int tb_port_tmu_time_sync(struct tb_port *port, bool time_sync) 31662306a36Sopenharmony_ci{ 31762306a36Sopenharmony_ci u32 val = time_sync ? TMU_ADP_CS_6_DTS : 0; 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci return tb_port_tmu_write(port, TMU_ADP_CS_6, TMU_ADP_CS_6_DTS, val); 32062306a36Sopenharmony_ci} 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_cistatic int tb_port_tmu_time_sync_disable(struct tb_port *port) 32362306a36Sopenharmony_ci{ 32462306a36Sopenharmony_ci return tb_port_tmu_time_sync(port, true); 32562306a36Sopenharmony_ci} 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_cistatic int tb_port_tmu_time_sync_enable(struct tb_port *port) 32862306a36Sopenharmony_ci{ 32962306a36Sopenharmony_ci return tb_port_tmu_time_sync(port, false); 33062306a36Sopenharmony_ci} 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_cistatic int tb_switch_tmu_set_time_disruption(struct tb_switch *sw, bool set) 33362306a36Sopenharmony_ci{ 33462306a36Sopenharmony_ci u32 val, offset, bit; 33562306a36Sopenharmony_ci int ret; 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci if (tb_switch_is_usb4(sw)) { 33862306a36Sopenharmony_ci offset = sw->tmu.cap + TMU_RTR_CS_0; 33962306a36Sopenharmony_ci bit = TMU_RTR_CS_0_TD; 34062306a36Sopenharmony_ci } else { 34162306a36Sopenharmony_ci offset = sw->cap_vsec_tmu + TB_TIME_VSEC_3_CS_26; 34262306a36Sopenharmony_ci bit = TB_TIME_VSEC_3_CS_26_TD; 34362306a36Sopenharmony_ci } 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci ret = tb_sw_read(sw, &val, TB_CFG_SWITCH, offset, 1); 34662306a36Sopenharmony_ci if (ret) 34762306a36Sopenharmony_ci return ret; 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci if (set) 35062306a36Sopenharmony_ci val |= bit; 35162306a36Sopenharmony_ci else 35262306a36Sopenharmony_ci val &= ~bit; 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci return tb_sw_write(sw, &val, TB_CFG_SWITCH, offset, 1); 35562306a36Sopenharmony_ci} 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_cistatic int tmu_mode_init(struct tb_switch *sw) 35862306a36Sopenharmony_ci{ 35962306a36Sopenharmony_ci bool enhanced, ucap; 36062306a36Sopenharmony_ci int ret, rate; 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci ucap = tb_switch_tmu_ucap_is_supported(sw); 36362306a36Sopenharmony_ci if (ucap) 36462306a36Sopenharmony_ci tb_sw_dbg(sw, "TMU: supports uni-directional mode\n"); 36562306a36Sopenharmony_ci enhanced = tb_switch_tmu_enhanced_is_supported(sw); 36662306a36Sopenharmony_ci if (enhanced) 36762306a36Sopenharmony_ci tb_sw_dbg(sw, "TMU: supports enhanced uni-directional mode\n"); 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci ret = tb_switch_tmu_rate_read(sw); 37062306a36Sopenharmony_ci if (ret < 0) 37162306a36Sopenharmony_ci return ret; 37262306a36Sopenharmony_ci rate = ret; 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci /* Off by default */ 37562306a36Sopenharmony_ci sw->tmu.mode = TB_SWITCH_TMU_MODE_OFF; 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci if (tb_route(sw)) { 37862306a36Sopenharmony_ci struct tb_port *up = tb_upstream_port(sw); 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci if (enhanced && tb_port_tmu_is_enhanced(up)) { 38162306a36Sopenharmony_ci sw->tmu.mode = TB_SWITCH_TMU_MODE_MEDRES_ENHANCED_UNI; 38262306a36Sopenharmony_ci } else if (ucap && tb_port_tmu_is_unidirectional(up)) { 38362306a36Sopenharmony_ci if (tmu_rates[TB_SWITCH_TMU_MODE_LOWRES] == rate) 38462306a36Sopenharmony_ci sw->tmu.mode = TB_SWITCH_TMU_MODE_LOWRES; 38562306a36Sopenharmony_ci else if (tmu_rates[TB_SWITCH_TMU_MODE_HIFI_UNI] == rate) 38662306a36Sopenharmony_ci sw->tmu.mode = TB_SWITCH_TMU_MODE_HIFI_UNI; 38762306a36Sopenharmony_ci } else if (rate) { 38862306a36Sopenharmony_ci sw->tmu.mode = TB_SWITCH_TMU_MODE_HIFI_BI; 38962306a36Sopenharmony_ci } 39062306a36Sopenharmony_ci } else if (rate) { 39162306a36Sopenharmony_ci sw->tmu.mode = TB_SWITCH_TMU_MODE_HIFI_BI; 39262306a36Sopenharmony_ci } 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci /* Update the initial request to match the current mode */ 39562306a36Sopenharmony_ci sw->tmu.mode_request = sw->tmu.mode; 39662306a36Sopenharmony_ci sw->tmu.has_ucap = ucap; 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci return 0; 39962306a36Sopenharmony_ci} 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci/** 40262306a36Sopenharmony_ci * tb_switch_tmu_init() - Initialize switch TMU structures 40362306a36Sopenharmony_ci * @sw: Switch to initialized 40462306a36Sopenharmony_ci * 40562306a36Sopenharmony_ci * This function must be called before other TMU related functions to 40662306a36Sopenharmony_ci * makes the internal structures are filled in correctly. Does not 40762306a36Sopenharmony_ci * change any hardware configuration. 40862306a36Sopenharmony_ci */ 40962306a36Sopenharmony_ciint tb_switch_tmu_init(struct tb_switch *sw) 41062306a36Sopenharmony_ci{ 41162306a36Sopenharmony_ci struct tb_port *port; 41262306a36Sopenharmony_ci int ret; 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci if (tb_switch_is_icm(sw)) 41562306a36Sopenharmony_ci return 0; 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci ret = tb_switch_find_cap(sw, TB_SWITCH_CAP_TMU); 41862306a36Sopenharmony_ci if (ret > 0) 41962306a36Sopenharmony_ci sw->tmu.cap = ret; 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci tb_switch_for_each_port(sw, port) { 42262306a36Sopenharmony_ci int cap; 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci cap = tb_port_find_cap(port, TB_PORT_CAP_TIME1); 42562306a36Sopenharmony_ci if (cap > 0) 42662306a36Sopenharmony_ci port->cap_tmu = cap; 42762306a36Sopenharmony_ci } 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci ret = tmu_mode_init(sw); 43062306a36Sopenharmony_ci if (ret) 43162306a36Sopenharmony_ci return ret; 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci tb_sw_dbg(sw, "TMU: current mode: %s\n", tmu_mode_name(sw->tmu.mode)); 43462306a36Sopenharmony_ci return 0; 43562306a36Sopenharmony_ci} 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci/** 43862306a36Sopenharmony_ci * tb_switch_tmu_post_time() - Update switch local time 43962306a36Sopenharmony_ci * @sw: Switch whose time to update 44062306a36Sopenharmony_ci * 44162306a36Sopenharmony_ci * Updates switch local time using time posting procedure. 44262306a36Sopenharmony_ci */ 44362306a36Sopenharmony_ciint tb_switch_tmu_post_time(struct tb_switch *sw) 44462306a36Sopenharmony_ci{ 44562306a36Sopenharmony_ci unsigned int post_time_high_offset, post_time_high = 0; 44662306a36Sopenharmony_ci unsigned int post_local_time_offset, post_time_offset; 44762306a36Sopenharmony_ci struct tb_switch *root_switch = sw->tb->root_switch; 44862306a36Sopenharmony_ci u64 hi, mid, lo, local_time, post_time; 44962306a36Sopenharmony_ci int i, ret, retries = 100; 45062306a36Sopenharmony_ci u32 gm_local_time[3]; 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci if (!tb_route(sw)) 45362306a36Sopenharmony_ci return 0; 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci if (!tb_switch_is_usb4(sw)) 45662306a36Sopenharmony_ci return 0; 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci /* Need to be able to read the grand master time */ 45962306a36Sopenharmony_ci if (!root_switch->tmu.cap) 46062306a36Sopenharmony_ci return 0; 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci ret = tb_sw_read(root_switch, gm_local_time, TB_CFG_SWITCH, 46362306a36Sopenharmony_ci root_switch->tmu.cap + TMU_RTR_CS_1, 46462306a36Sopenharmony_ci ARRAY_SIZE(gm_local_time)); 46562306a36Sopenharmony_ci if (ret) 46662306a36Sopenharmony_ci return ret; 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(gm_local_time); i++) 46962306a36Sopenharmony_ci tb_sw_dbg(root_switch, "TMU: local_time[%d]=0x%08x\n", i, 47062306a36Sopenharmony_ci gm_local_time[i]); 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci /* Convert to nanoseconds (drop fractional part) */ 47362306a36Sopenharmony_ci hi = gm_local_time[2] & TMU_RTR_CS_3_LOCAL_TIME_NS_MASK; 47462306a36Sopenharmony_ci mid = gm_local_time[1]; 47562306a36Sopenharmony_ci lo = (gm_local_time[0] & TMU_RTR_CS_1_LOCAL_TIME_NS_MASK) >> 47662306a36Sopenharmony_ci TMU_RTR_CS_1_LOCAL_TIME_NS_SHIFT; 47762306a36Sopenharmony_ci local_time = hi << 48 | mid << 16 | lo; 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci /* Tell the switch that time sync is disrupted for a while */ 48062306a36Sopenharmony_ci ret = tb_switch_tmu_set_time_disruption(sw, true); 48162306a36Sopenharmony_ci if (ret) 48262306a36Sopenharmony_ci return ret; 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci post_local_time_offset = sw->tmu.cap + TMU_RTR_CS_22; 48562306a36Sopenharmony_ci post_time_offset = sw->tmu.cap + TMU_RTR_CS_24; 48662306a36Sopenharmony_ci post_time_high_offset = sw->tmu.cap + TMU_RTR_CS_25; 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci /* 48962306a36Sopenharmony_ci * Write the Grandmaster time to the Post Local Time registers 49062306a36Sopenharmony_ci * of the new switch. 49162306a36Sopenharmony_ci */ 49262306a36Sopenharmony_ci ret = tb_sw_write(sw, &local_time, TB_CFG_SWITCH, 49362306a36Sopenharmony_ci post_local_time_offset, 2); 49462306a36Sopenharmony_ci if (ret) 49562306a36Sopenharmony_ci goto out; 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci /* 49862306a36Sopenharmony_ci * Have the new switch update its local time by: 49962306a36Sopenharmony_ci * 1) writing 0x1 to the Post Time Low register and 0xffffffff to 50062306a36Sopenharmony_ci * Post Time High register. 50162306a36Sopenharmony_ci * 2) write 0 to Post Time High register and then wait for 50262306a36Sopenharmony_ci * the completion of the post_time register becomes 0. 50362306a36Sopenharmony_ci * This means the time has been converged properly. 50462306a36Sopenharmony_ci */ 50562306a36Sopenharmony_ci post_time = 0xffffffff00000001ULL; 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci ret = tb_sw_write(sw, &post_time, TB_CFG_SWITCH, post_time_offset, 2); 50862306a36Sopenharmony_ci if (ret) 50962306a36Sopenharmony_ci goto out; 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci ret = tb_sw_write(sw, &post_time_high, TB_CFG_SWITCH, 51262306a36Sopenharmony_ci post_time_high_offset, 1); 51362306a36Sopenharmony_ci if (ret) 51462306a36Sopenharmony_ci goto out; 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci do { 51762306a36Sopenharmony_ci usleep_range(5, 10); 51862306a36Sopenharmony_ci ret = tb_sw_read(sw, &post_time, TB_CFG_SWITCH, 51962306a36Sopenharmony_ci post_time_offset, 2); 52062306a36Sopenharmony_ci if (ret) 52162306a36Sopenharmony_ci goto out; 52262306a36Sopenharmony_ci } while (--retries && post_time); 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci if (!retries) { 52562306a36Sopenharmony_ci ret = -ETIMEDOUT; 52662306a36Sopenharmony_ci goto out; 52762306a36Sopenharmony_ci } 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci tb_sw_dbg(sw, "TMU: updated local time to %#llx\n", local_time); 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ciout: 53262306a36Sopenharmony_ci tb_switch_tmu_set_time_disruption(sw, false); 53362306a36Sopenharmony_ci return ret; 53462306a36Sopenharmony_ci} 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_cistatic int disable_enhanced(struct tb_port *up, struct tb_port *down) 53762306a36Sopenharmony_ci{ 53862306a36Sopenharmony_ci int ret; 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci /* 54162306a36Sopenharmony_ci * Router may already been disconnected so ignore errors on the 54262306a36Sopenharmony_ci * upstream port. 54362306a36Sopenharmony_ci */ 54462306a36Sopenharmony_ci tb_port_tmu_rate_write(up, 0); 54562306a36Sopenharmony_ci tb_port_tmu_enhanced_enable(up, false); 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci ret = tb_port_tmu_rate_write(down, 0); 54862306a36Sopenharmony_ci if (ret) 54962306a36Sopenharmony_ci return ret; 55062306a36Sopenharmony_ci return tb_port_tmu_enhanced_enable(down, false); 55162306a36Sopenharmony_ci} 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci/** 55462306a36Sopenharmony_ci * tb_switch_tmu_disable() - Disable TMU of a switch 55562306a36Sopenharmony_ci * @sw: Switch whose TMU to disable 55662306a36Sopenharmony_ci * 55762306a36Sopenharmony_ci * Turns off TMU of @sw if it is enabled. If not enabled does nothing. 55862306a36Sopenharmony_ci */ 55962306a36Sopenharmony_ciint tb_switch_tmu_disable(struct tb_switch *sw) 56062306a36Sopenharmony_ci{ 56162306a36Sopenharmony_ci /* Already disabled? */ 56262306a36Sopenharmony_ci if (sw->tmu.mode == TB_SWITCH_TMU_MODE_OFF) 56362306a36Sopenharmony_ci return 0; 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci if (tb_route(sw)) { 56662306a36Sopenharmony_ci struct tb_port *down, *up; 56762306a36Sopenharmony_ci int ret; 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci down = tb_switch_downstream_port(sw); 57062306a36Sopenharmony_ci up = tb_upstream_port(sw); 57162306a36Sopenharmony_ci /* 57262306a36Sopenharmony_ci * In case of uni-directional time sync, TMU handshake is 57362306a36Sopenharmony_ci * initiated by upstream router. In case of bi-directional 57462306a36Sopenharmony_ci * time sync, TMU handshake is initiated by downstream router. 57562306a36Sopenharmony_ci * We change downstream router's rate to off for both uni/bidir 57662306a36Sopenharmony_ci * cases although it is needed only for the bi-directional mode. 57762306a36Sopenharmony_ci * We avoid changing upstream router's mode since it might 57862306a36Sopenharmony_ci * have another downstream router plugged, that is set to 57962306a36Sopenharmony_ci * uni-directional mode and we don't want to change it's TMU 58062306a36Sopenharmony_ci * mode. 58162306a36Sopenharmony_ci */ 58262306a36Sopenharmony_ci ret = tb_switch_tmu_rate_write(sw, tmu_rates[TB_SWITCH_TMU_MODE_OFF]); 58362306a36Sopenharmony_ci if (ret) 58462306a36Sopenharmony_ci return ret; 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci tb_port_tmu_time_sync_disable(up); 58762306a36Sopenharmony_ci ret = tb_port_tmu_time_sync_disable(down); 58862306a36Sopenharmony_ci if (ret) 58962306a36Sopenharmony_ci return ret; 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci switch (sw->tmu.mode) { 59262306a36Sopenharmony_ci case TB_SWITCH_TMU_MODE_LOWRES: 59362306a36Sopenharmony_ci case TB_SWITCH_TMU_MODE_HIFI_UNI: 59462306a36Sopenharmony_ci /* The switch may be unplugged so ignore any errors */ 59562306a36Sopenharmony_ci tb_port_tmu_unidirectional_disable(up); 59662306a36Sopenharmony_ci ret = tb_port_tmu_unidirectional_disable(down); 59762306a36Sopenharmony_ci if (ret) 59862306a36Sopenharmony_ci return ret; 59962306a36Sopenharmony_ci break; 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci case TB_SWITCH_TMU_MODE_MEDRES_ENHANCED_UNI: 60262306a36Sopenharmony_ci ret = disable_enhanced(up, down); 60362306a36Sopenharmony_ci if (ret) 60462306a36Sopenharmony_ci return ret; 60562306a36Sopenharmony_ci break; 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci default: 60862306a36Sopenharmony_ci break; 60962306a36Sopenharmony_ci } 61062306a36Sopenharmony_ci } else { 61162306a36Sopenharmony_ci tb_switch_tmu_rate_write(sw, tmu_rates[TB_SWITCH_TMU_MODE_OFF]); 61262306a36Sopenharmony_ci } 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci sw->tmu.mode = TB_SWITCH_TMU_MODE_OFF; 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci tb_sw_dbg(sw, "TMU: disabled\n"); 61762306a36Sopenharmony_ci return 0; 61862306a36Sopenharmony_ci} 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci/* Called only when there is failure enabling requested mode */ 62162306a36Sopenharmony_cistatic void tb_switch_tmu_off(struct tb_switch *sw) 62262306a36Sopenharmony_ci{ 62362306a36Sopenharmony_ci unsigned int rate = tmu_rates[TB_SWITCH_TMU_MODE_OFF]; 62462306a36Sopenharmony_ci struct tb_port *down, *up; 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci down = tb_switch_downstream_port(sw); 62762306a36Sopenharmony_ci up = tb_upstream_port(sw); 62862306a36Sopenharmony_ci /* 62962306a36Sopenharmony_ci * In case of any failure in one of the steps when setting 63062306a36Sopenharmony_ci * bi-directional or uni-directional TMU mode, get back to the TMU 63162306a36Sopenharmony_ci * configurations in off mode. In case of additional failures in 63262306a36Sopenharmony_ci * the functions below, ignore them since the caller shall already 63362306a36Sopenharmony_ci * report a failure. 63462306a36Sopenharmony_ci */ 63562306a36Sopenharmony_ci tb_port_tmu_time_sync_disable(down); 63662306a36Sopenharmony_ci tb_port_tmu_time_sync_disable(up); 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci switch (sw->tmu.mode_request) { 63962306a36Sopenharmony_ci case TB_SWITCH_TMU_MODE_LOWRES: 64062306a36Sopenharmony_ci case TB_SWITCH_TMU_MODE_HIFI_UNI: 64162306a36Sopenharmony_ci tb_switch_tmu_rate_write(tb_switch_parent(sw), rate); 64262306a36Sopenharmony_ci break; 64362306a36Sopenharmony_ci case TB_SWITCH_TMU_MODE_MEDRES_ENHANCED_UNI: 64462306a36Sopenharmony_ci disable_enhanced(up, down); 64562306a36Sopenharmony_ci break; 64662306a36Sopenharmony_ci default: 64762306a36Sopenharmony_ci break; 64862306a36Sopenharmony_ci } 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci /* Always set the rate to 0 */ 65162306a36Sopenharmony_ci tb_switch_tmu_rate_write(sw, rate); 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci tb_switch_set_tmu_mode_params(sw, sw->tmu.mode); 65462306a36Sopenharmony_ci tb_port_tmu_unidirectional_disable(down); 65562306a36Sopenharmony_ci tb_port_tmu_unidirectional_disable(up); 65662306a36Sopenharmony_ci} 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci/* 65962306a36Sopenharmony_ci * This function is called when the previous TMU mode was 66062306a36Sopenharmony_ci * TB_SWITCH_TMU_MODE_OFF. 66162306a36Sopenharmony_ci */ 66262306a36Sopenharmony_cistatic int tb_switch_tmu_enable_bidirectional(struct tb_switch *sw) 66362306a36Sopenharmony_ci{ 66462306a36Sopenharmony_ci struct tb_port *up, *down; 66562306a36Sopenharmony_ci int ret; 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ci up = tb_upstream_port(sw); 66862306a36Sopenharmony_ci down = tb_switch_downstream_port(sw); 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci ret = tb_port_tmu_unidirectional_disable(up); 67162306a36Sopenharmony_ci if (ret) 67262306a36Sopenharmony_ci return ret; 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci ret = tb_port_tmu_unidirectional_disable(down); 67562306a36Sopenharmony_ci if (ret) 67662306a36Sopenharmony_ci goto out; 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci ret = tb_switch_tmu_rate_write(sw, tmu_rates[TB_SWITCH_TMU_MODE_HIFI_BI]); 67962306a36Sopenharmony_ci if (ret) 68062306a36Sopenharmony_ci goto out; 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci ret = tb_port_tmu_time_sync_enable(up); 68362306a36Sopenharmony_ci if (ret) 68462306a36Sopenharmony_ci goto out; 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci ret = tb_port_tmu_time_sync_enable(down); 68762306a36Sopenharmony_ci if (ret) 68862306a36Sopenharmony_ci goto out; 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_ci return 0; 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ciout: 69362306a36Sopenharmony_ci tb_switch_tmu_off(sw); 69462306a36Sopenharmony_ci return ret; 69562306a36Sopenharmony_ci} 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci/* Only needed for Titan Ridge */ 69862306a36Sopenharmony_cistatic int tb_switch_tmu_disable_objections(struct tb_switch *sw) 69962306a36Sopenharmony_ci{ 70062306a36Sopenharmony_ci struct tb_port *up = tb_upstream_port(sw); 70162306a36Sopenharmony_ci u32 val; 70262306a36Sopenharmony_ci int ret; 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ci ret = tb_sw_read(sw, &val, TB_CFG_SWITCH, 70562306a36Sopenharmony_ci sw->cap_vsec_tmu + TB_TIME_VSEC_3_CS_9, 1); 70662306a36Sopenharmony_ci if (ret) 70762306a36Sopenharmony_ci return ret; 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_ci val &= ~TB_TIME_VSEC_3_CS_9_TMU_OBJ_MASK; 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ci ret = tb_sw_write(sw, &val, TB_CFG_SWITCH, 71262306a36Sopenharmony_ci sw->cap_vsec_tmu + TB_TIME_VSEC_3_CS_9, 1); 71362306a36Sopenharmony_ci if (ret) 71462306a36Sopenharmony_ci return ret; 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_ci return tb_port_tmu_write(up, TMU_ADP_CS_6, 71762306a36Sopenharmony_ci TMU_ADP_CS_6_DISABLE_TMU_OBJ_MASK, 71862306a36Sopenharmony_ci TMU_ADP_CS_6_DISABLE_TMU_OBJ_CL1 | 71962306a36Sopenharmony_ci TMU_ADP_CS_6_DISABLE_TMU_OBJ_CL2); 72062306a36Sopenharmony_ci} 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ci/* 72362306a36Sopenharmony_ci * This function is called when the previous TMU mode was 72462306a36Sopenharmony_ci * TB_SWITCH_TMU_MODE_OFF. 72562306a36Sopenharmony_ci */ 72662306a36Sopenharmony_cistatic int tb_switch_tmu_enable_unidirectional(struct tb_switch *sw) 72762306a36Sopenharmony_ci{ 72862306a36Sopenharmony_ci struct tb_port *up, *down; 72962306a36Sopenharmony_ci int ret; 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci up = tb_upstream_port(sw); 73262306a36Sopenharmony_ci down = tb_switch_downstream_port(sw); 73362306a36Sopenharmony_ci ret = tb_switch_tmu_rate_write(tb_switch_parent(sw), 73462306a36Sopenharmony_ci tmu_rates[sw->tmu.mode_request]); 73562306a36Sopenharmony_ci if (ret) 73662306a36Sopenharmony_ci return ret; 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci ret = tb_switch_set_tmu_mode_params(sw, sw->tmu.mode_request); 73962306a36Sopenharmony_ci if (ret) 74062306a36Sopenharmony_ci return ret; 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_ci ret = tb_port_tmu_unidirectional_enable(up); 74362306a36Sopenharmony_ci if (ret) 74462306a36Sopenharmony_ci goto out; 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_ci ret = tb_port_tmu_time_sync_enable(up); 74762306a36Sopenharmony_ci if (ret) 74862306a36Sopenharmony_ci goto out; 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ci ret = tb_port_tmu_unidirectional_enable(down); 75162306a36Sopenharmony_ci if (ret) 75262306a36Sopenharmony_ci goto out; 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ci ret = tb_port_tmu_time_sync_enable(down); 75562306a36Sopenharmony_ci if (ret) 75662306a36Sopenharmony_ci goto out; 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_ci return 0; 75962306a36Sopenharmony_ci 76062306a36Sopenharmony_ciout: 76162306a36Sopenharmony_ci tb_switch_tmu_off(sw); 76262306a36Sopenharmony_ci return ret; 76362306a36Sopenharmony_ci} 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_ci/* 76662306a36Sopenharmony_ci * This function is called when the previous TMU mode was 76762306a36Sopenharmony_ci * TB_SWITCH_TMU_RATE_OFF. 76862306a36Sopenharmony_ci */ 76962306a36Sopenharmony_cistatic int tb_switch_tmu_enable_enhanced(struct tb_switch *sw) 77062306a36Sopenharmony_ci{ 77162306a36Sopenharmony_ci unsigned int rate = tmu_rates[sw->tmu.mode_request]; 77262306a36Sopenharmony_ci struct tb_port *up, *down; 77362306a36Sopenharmony_ci int ret; 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_ci /* Router specific parameters first */ 77662306a36Sopenharmony_ci ret = tb_switch_set_tmu_mode_params(sw, sw->tmu.mode_request); 77762306a36Sopenharmony_ci if (ret) 77862306a36Sopenharmony_ci return ret; 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ci up = tb_upstream_port(sw); 78162306a36Sopenharmony_ci down = tb_switch_downstream_port(sw); 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_ci ret = tb_port_set_tmu_mode_params(up, sw->tmu.mode_request); 78462306a36Sopenharmony_ci if (ret) 78562306a36Sopenharmony_ci goto out; 78662306a36Sopenharmony_ci 78762306a36Sopenharmony_ci ret = tb_port_tmu_rate_write(up, rate); 78862306a36Sopenharmony_ci if (ret) 78962306a36Sopenharmony_ci goto out; 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci ret = tb_port_tmu_enhanced_enable(up, true); 79262306a36Sopenharmony_ci if (ret) 79362306a36Sopenharmony_ci goto out; 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_ci ret = tb_port_set_tmu_mode_params(down, sw->tmu.mode_request); 79662306a36Sopenharmony_ci if (ret) 79762306a36Sopenharmony_ci goto out; 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_ci ret = tb_port_tmu_rate_write(down, rate); 80062306a36Sopenharmony_ci if (ret) 80162306a36Sopenharmony_ci goto out; 80262306a36Sopenharmony_ci 80362306a36Sopenharmony_ci ret = tb_port_tmu_enhanced_enable(down, true); 80462306a36Sopenharmony_ci if (ret) 80562306a36Sopenharmony_ci goto out; 80662306a36Sopenharmony_ci 80762306a36Sopenharmony_ci return 0; 80862306a36Sopenharmony_ci 80962306a36Sopenharmony_ciout: 81062306a36Sopenharmony_ci tb_switch_tmu_off(sw); 81162306a36Sopenharmony_ci return ret; 81262306a36Sopenharmony_ci} 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_cistatic void tb_switch_tmu_change_mode_prev(struct tb_switch *sw) 81562306a36Sopenharmony_ci{ 81662306a36Sopenharmony_ci unsigned int rate = tmu_rates[sw->tmu.mode]; 81762306a36Sopenharmony_ci struct tb_port *down, *up; 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_ci down = tb_switch_downstream_port(sw); 82062306a36Sopenharmony_ci up = tb_upstream_port(sw); 82162306a36Sopenharmony_ci /* 82262306a36Sopenharmony_ci * In case of any failure in one of the steps when change mode, 82362306a36Sopenharmony_ci * get back to the TMU configurations in previous mode. 82462306a36Sopenharmony_ci * In case of additional failures in the functions below, 82562306a36Sopenharmony_ci * ignore them since the caller shall already report a failure. 82662306a36Sopenharmony_ci */ 82762306a36Sopenharmony_ci switch (sw->tmu.mode) { 82862306a36Sopenharmony_ci case TB_SWITCH_TMU_MODE_LOWRES: 82962306a36Sopenharmony_ci case TB_SWITCH_TMU_MODE_HIFI_UNI: 83062306a36Sopenharmony_ci tb_port_tmu_set_unidirectional(down, true); 83162306a36Sopenharmony_ci tb_switch_tmu_rate_write(tb_switch_parent(sw), rate); 83262306a36Sopenharmony_ci break; 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_ci case TB_SWITCH_TMU_MODE_HIFI_BI: 83562306a36Sopenharmony_ci tb_port_tmu_set_unidirectional(down, false); 83662306a36Sopenharmony_ci tb_switch_tmu_rate_write(sw, rate); 83762306a36Sopenharmony_ci break; 83862306a36Sopenharmony_ci 83962306a36Sopenharmony_ci default: 84062306a36Sopenharmony_ci break; 84162306a36Sopenharmony_ci } 84262306a36Sopenharmony_ci 84362306a36Sopenharmony_ci tb_switch_set_tmu_mode_params(sw, sw->tmu.mode); 84462306a36Sopenharmony_ci 84562306a36Sopenharmony_ci switch (sw->tmu.mode) { 84662306a36Sopenharmony_ci case TB_SWITCH_TMU_MODE_LOWRES: 84762306a36Sopenharmony_ci case TB_SWITCH_TMU_MODE_HIFI_UNI: 84862306a36Sopenharmony_ci tb_port_tmu_set_unidirectional(up, true); 84962306a36Sopenharmony_ci break; 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_ci case TB_SWITCH_TMU_MODE_HIFI_BI: 85262306a36Sopenharmony_ci tb_port_tmu_set_unidirectional(up, false); 85362306a36Sopenharmony_ci break; 85462306a36Sopenharmony_ci 85562306a36Sopenharmony_ci default: 85662306a36Sopenharmony_ci break; 85762306a36Sopenharmony_ci } 85862306a36Sopenharmony_ci} 85962306a36Sopenharmony_ci 86062306a36Sopenharmony_cistatic int tb_switch_tmu_change_mode(struct tb_switch *sw) 86162306a36Sopenharmony_ci{ 86262306a36Sopenharmony_ci unsigned int rate = tmu_rates[sw->tmu.mode_request]; 86362306a36Sopenharmony_ci struct tb_port *up, *down; 86462306a36Sopenharmony_ci int ret; 86562306a36Sopenharmony_ci 86662306a36Sopenharmony_ci up = tb_upstream_port(sw); 86762306a36Sopenharmony_ci down = tb_switch_downstream_port(sw); 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_ci /* Program the upstream router downstream facing lane adapter */ 87062306a36Sopenharmony_ci switch (sw->tmu.mode_request) { 87162306a36Sopenharmony_ci case TB_SWITCH_TMU_MODE_LOWRES: 87262306a36Sopenharmony_ci case TB_SWITCH_TMU_MODE_HIFI_UNI: 87362306a36Sopenharmony_ci ret = tb_port_tmu_set_unidirectional(down, true); 87462306a36Sopenharmony_ci if (ret) 87562306a36Sopenharmony_ci goto out; 87662306a36Sopenharmony_ci ret = tb_switch_tmu_rate_write(tb_switch_parent(sw), rate); 87762306a36Sopenharmony_ci if (ret) 87862306a36Sopenharmony_ci goto out; 87962306a36Sopenharmony_ci break; 88062306a36Sopenharmony_ci 88162306a36Sopenharmony_ci case TB_SWITCH_TMU_MODE_HIFI_BI: 88262306a36Sopenharmony_ci ret = tb_port_tmu_set_unidirectional(down, false); 88362306a36Sopenharmony_ci if (ret) 88462306a36Sopenharmony_ci goto out; 88562306a36Sopenharmony_ci ret = tb_switch_tmu_rate_write(sw, rate); 88662306a36Sopenharmony_ci if (ret) 88762306a36Sopenharmony_ci goto out; 88862306a36Sopenharmony_ci break; 88962306a36Sopenharmony_ci 89062306a36Sopenharmony_ci default: 89162306a36Sopenharmony_ci /* Not allowed to change modes from other than above */ 89262306a36Sopenharmony_ci return -EINVAL; 89362306a36Sopenharmony_ci } 89462306a36Sopenharmony_ci 89562306a36Sopenharmony_ci ret = tb_switch_set_tmu_mode_params(sw, sw->tmu.mode_request); 89662306a36Sopenharmony_ci if (ret) 89762306a36Sopenharmony_ci return ret; 89862306a36Sopenharmony_ci 89962306a36Sopenharmony_ci /* Program the new mode and the downstream router lane adapter */ 90062306a36Sopenharmony_ci switch (sw->tmu.mode_request) { 90162306a36Sopenharmony_ci case TB_SWITCH_TMU_MODE_LOWRES: 90262306a36Sopenharmony_ci case TB_SWITCH_TMU_MODE_HIFI_UNI: 90362306a36Sopenharmony_ci ret = tb_port_tmu_set_unidirectional(up, true); 90462306a36Sopenharmony_ci if (ret) 90562306a36Sopenharmony_ci goto out; 90662306a36Sopenharmony_ci break; 90762306a36Sopenharmony_ci 90862306a36Sopenharmony_ci case TB_SWITCH_TMU_MODE_HIFI_BI: 90962306a36Sopenharmony_ci ret = tb_port_tmu_set_unidirectional(up, false); 91062306a36Sopenharmony_ci if (ret) 91162306a36Sopenharmony_ci goto out; 91262306a36Sopenharmony_ci break; 91362306a36Sopenharmony_ci 91462306a36Sopenharmony_ci default: 91562306a36Sopenharmony_ci /* Not allowed to change modes from other than above */ 91662306a36Sopenharmony_ci return -EINVAL; 91762306a36Sopenharmony_ci } 91862306a36Sopenharmony_ci 91962306a36Sopenharmony_ci ret = tb_port_tmu_time_sync_enable(down); 92062306a36Sopenharmony_ci if (ret) 92162306a36Sopenharmony_ci goto out; 92262306a36Sopenharmony_ci 92362306a36Sopenharmony_ci ret = tb_port_tmu_time_sync_enable(up); 92462306a36Sopenharmony_ci if (ret) 92562306a36Sopenharmony_ci goto out; 92662306a36Sopenharmony_ci 92762306a36Sopenharmony_ci return 0; 92862306a36Sopenharmony_ci 92962306a36Sopenharmony_ciout: 93062306a36Sopenharmony_ci tb_switch_tmu_change_mode_prev(sw); 93162306a36Sopenharmony_ci return ret; 93262306a36Sopenharmony_ci} 93362306a36Sopenharmony_ci 93462306a36Sopenharmony_ci/** 93562306a36Sopenharmony_ci * tb_switch_tmu_enable() - Enable TMU on a router 93662306a36Sopenharmony_ci * @sw: Router whose TMU to enable 93762306a36Sopenharmony_ci * 93862306a36Sopenharmony_ci * Enables TMU of a router to be in uni-directional Normal/HiFi or 93962306a36Sopenharmony_ci * bi-directional HiFi mode. Calling tb_switch_tmu_configure() is 94062306a36Sopenharmony_ci * required before calling this function. 94162306a36Sopenharmony_ci */ 94262306a36Sopenharmony_ciint tb_switch_tmu_enable(struct tb_switch *sw) 94362306a36Sopenharmony_ci{ 94462306a36Sopenharmony_ci int ret; 94562306a36Sopenharmony_ci 94662306a36Sopenharmony_ci if (tb_switch_tmu_is_enabled(sw)) 94762306a36Sopenharmony_ci return 0; 94862306a36Sopenharmony_ci 94962306a36Sopenharmony_ci if (tb_switch_is_titan_ridge(sw) && 95062306a36Sopenharmony_ci (sw->tmu.mode_request == TB_SWITCH_TMU_MODE_LOWRES || 95162306a36Sopenharmony_ci sw->tmu.mode_request == TB_SWITCH_TMU_MODE_HIFI_UNI)) { 95262306a36Sopenharmony_ci ret = tb_switch_tmu_disable_objections(sw); 95362306a36Sopenharmony_ci if (ret) 95462306a36Sopenharmony_ci return ret; 95562306a36Sopenharmony_ci } 95662306a36Sopenharmony_ci 95762306a36Sopenharmony_ci ret = tb_switch_tmu_set_time_disruption(sw, true); 95862306a36Sopenharmony_ci if (ret) 95962306a36Sopenharmony_ci return ret; 96062306a36Sopenharmony_ci 96162306a36Sopenharmony_ci if (tb_route(sw)) { 96262306a36Sopenharmony_ci /* 96362306a36Sopenharmony_ci * The used mode changes are from OFF to 96462306a36Sopenharmony_ci * HiFi-Uni/HiFi-BiDir/Normal-Uni or from Normal-Uni to 96562306a36Sopenharmony_ci * HiFi-Uni. 96662306a36Sopenharmony_ci */ 96762306a36Sopenharmony_ci if (sw->tmu.mode == TB_SWITCH_TMU_MODE_OFF) { 96862306a36Sopenharmony_ci switch (sw->tmu.mode_request) { 96962306a36Sopenharmony_ci case TB_SWITCH_TMU_MODE_LOWRES: 97062306a36Sopenharmony_ci case TB_SWITCH_TMU_MODE_HIFI_UNI: 97162306a36Sopenharmony_ci ret = tb_switch_tmu_enable_unidirectional(sw); 97262306a36Sopenharmony_ci break; 97362306a36Sopenharmony_ci 97462306a36Sopenharmony_ci case TB_SWITCH_TMU_MODE_HIFI_BI: 97562306a36Sopenharmony_ci ret = tb_switch_tmu_enable_bidirectional(sw); 97662306a36Sopenharmony_ci break; 97762306a36Sopenharmony_ci case TB_SWITCH_TMU_MODE_MEDRES_ENHANCED_UNI: 97862306a36Sopenharmony_ci ret = tb_switch_tmu_enable_enhanced(sw); 97962306a36Sopenharmony_ci break; 98062306a36Sopenharmony_ci default: 98162306a36Sopenharmony_ci ret = -EINVAL; 98262306a36Sopenharmony_ci break; 98362306a36Sopenharmony_ci } 98462306a36Sopenharmony_ci } else if (sw->tmu.mode == TB_SWITCH_TMU_MODE_LOWRES || 98562306a36Sopenharmony_ci sw->tmu.mode == TB_SWITCH_TMU_MODE_HIFI_UNI || 98662306a36Sopenharmony_ci sw->tmu.mode == TB_SWITCH_TMU_MODE_HIFI_BI) { 98762306a36Sopenharmony_ci ret = tb_switch_tmu_change_mode(sw); 98862306a36Sopenharmony_ci } else { 98962306a36Sopenharmony_ci ret = -EINVAL; 99062306a36Sopenharmony_ci } 99162306a36Sopenharmony_ci } else { 99262306a36Sopenharmony_ci /* 99362306a36Sopenharmony_ci * Host router port configurations are written as 99462306a36Sopenharmony_ci * part of configurations for downstream port of the parent 99562306a36Sopenharmony_ci * of the child node - see above. 99662306a36Sopenharmony_ci * Here only the host router' rate configuration is written. 99762306a36Sopenharmony_ci */ 99862306a36Sopenharmony_ci ret = tb_switch_tmu_rate_write(sw, tmu_rates[sw->tmu.mode_request]); 99962306a36Sopenharmony_ci } 100062306a36Sopenharmony_ci 100162306a36Sopenharmony_ci if (ret) { 100262306a36Sopenharmony_ci tb_sw_warn(sw, "TMU: failed to enable mode %s: %d\n", 100362306a36Sopenharmony_ci tmu_mode_name(sw->tmu.mode_request), ret); 100462306a36Sopenharmony_ci } else { 100562306a36Sopenharmony_ci sw->tmu.mode = sw->tmu.mode_request; 100662306a36Sopenharmony_ci tb_sw_dbg(sw, "TMU: mode set to: %s\n", tmu_mode_name(sw->tmu.mode)); 100762306a36Sopenharmony_ci } 100862306a36Sopenharmony_ci 100962306a36Sopenharmony_ci return tb_switch_tmu_set_time_disruption(sw, false); 101062306a36Sopenharmony_ci} 101162306a36Sopenharmony_ci 101262306a36Sopenharmony_ci/** 101362306a36Sopenharmony_ci * tb_switch_tmu_configure() - Configure the TMU mode 101462306a36Sopenharmony_ci * @sw: Router whose mode to change 101562306a36Sopenharmony_ci * @mode: Mode to configure 101662306a36Sopenharmony_ci * 101762306a36Sopenharmony_ci * Selects the TMU mode that is enabled when tb_switch_tmu_enable() is 101862306a36Sopenharmony_ci * next called. 101962306a36Sopenharmony_ci * 102062306a36Sopenharmony_ci * Returns %0 in success and negative errno otherwise. Specifically 102162306a36Sopenharmony_ci * returns %-EOPNOTSUPP if the requested mode is not possible (not 102262306a36Sopenharmony_ci * supported by the router and/or topology). 102362306a36Sopenharmony_ci */ 102462306a36Sopenharmony_ciint tb_switch_tmu_configure(struct tb_switch *sw, enum tb_switch_tmu_mode mode) 102562306a36Sopenharmony_ci{ 102662306a36Sopenharmony_ci switch (mode) { 102762306a36Sopenharmony_ci case TB_SWITCH_TMU_MODE_OFF: 102862306a36Sopenharmony_ci break; 102962306a36Sopenharmony_ci 103062306a36Sopenharmony_ci case TB_SWITCH_TMU_MODE_LOWRES: 103162306a36Sopenharmony_ci case TB_SWITCH_TMU_MODE_HIFI_UNI: 103262306a36Sopenharmony_ci if (!sw->tmu.has_ucap) 103362306a36Sopenharmony_ci return -EOPNOTSUPP; 103462306a36Sopenharmony_ci break; 103562306a36Sopenharmony_ci 103662306a36Sopenharmony_ci case TB_SWITCH_TMU_MODE_HIFI_BI: 103762306a36Sopenharmony_ci break; 103862306a36Sopenharmony_ci 103962306a36Sopenharmony_ci case TB_SWITCH_TMU_MODE_MEDRES_ENHANCED_UNI: { 104062306a36Sopenharmony_ci const struct tb_switch *parent_sw = tb_switch_parent(sw); 104162306a36Sopenharmony_ci 104262306a36Sopenharmony_ci if (!parent_sw || !tb_switch_tmu_enhanced_is_supported(parent_sw)) 104362306a36Sopenharmony_ci return -EOPNOTSUPP; 104462306a36Sopenharmony_ci if (!tb_switch_tmu_enhanced_is_supported(sw)) 104562306a36Sopenharmony_ci return -EOPNOTSUPP; 104662306a36Sopenharmony_ci 104762306a36Sopenharmony_ci break; 104862306a36Sopenharmony_ci } 104962306a36Sopenharmony_ci 105062306a36Sopenharmony_ci default: 105162306a36Sopenharmony_ci tb_sw_warn(sw, "TMU: unsupported mode %u\n", mode); 105262306a36Sopenharmony_ci return -EINVAL; 105362306a36Sopenharmony_ci } 105462306a36Sopenharmony_ci 105562306a36Sopenharmony_ci if (sw->tmu.mode_request != mode) { 105662306a36Sopenharmony_ci tb_sw_dbg(sw, "TMU: mode change %s -> %s requested\n", 105762306a36Sopenharmony_ci tmu_mode_name(sw->tmu.mode), tmu_mode_name(mode)); 105862306a36Sopenharmony_ci sw->tmu.mode_request = mode; 105962306a36Sopenharmony_ci } 106062306a36Sopenharmony_ci 106162306a36Sopenharmony_ci return 0; 106262306a36Sopenharmony_ci} 1063