1cf29b9afSRajmohan Mani // SPDX-License-Identifier: GPL-2.0 2cf29b9afSRajmohan Mani /* 3cf29b9afSRajmohan Mani * Thunderbolt Time Management Unit (TMU) support 4cf29b9afSRajmohan Mani * 5cf29b9afSRajmohan Mani * Copyright (C) 2019, Intel Corporation 6cf29b9afSRajmohan Mani * Authors: Mika Westerberg <mika.westerberg@linux.intel.com> 7cf29b9afSRajmohan Mani * Rajmohan Mani <rajmohan.mani@intel.com> 8cf29b9afSRajmohan Mani */ 9cf29b9afSRajmohan Mani 10cf29b9afSRajmohan Mani #include <linux/delay.h> 11cf29b9afSRajmohan Mani 12cf29b9afSRajmohan Mani #include "tb.h" 13cf29b9afSRajmohan Mani 14cf29b9afSRajmohan Mani static const char *tb_switch_tmu_mode_name(const struct tb_switch *sw) 15cf29b9afSRajmohan Mani { 16cf29b9afSRajmohan Mani bool root_switch = !tb_route(sw); 17cf29b9afSRajmohan Mani 18cf29b9afSRajmohan Mani switch (sw->tmu.rate) { 19cf29b9afSRajmohan Mani case TB_SWITCH_TMU_RATE_OFF: 20cf29b9afSRajmohan Mani return "off"; 21cf29b9afSRajmohan Mani 22cf29b9afSRajmohan Mani case TB_SWITCH_TMU_RATE_HIFI: 23cf29b9afSRajmohan Mani /* Root switch does not have upstream directionality */ 24cf29b9afSRajmohan Mani if (root_switch) 25cf29b9afSRajmohan Mani return "HiFi"; 26cf29b9afSRajmohan Mani if (sw->tmu.unidirectional) 27cf29b9afSRajmohan Mani return "uni-directional, HiFi"; 28cf29b9afSRajmohan Mani return "bi-directional, HiFi"; 29cf29b9afSRajmohan Mani 30cf29b9afSRajmohan Mani case TB_SWITCH_TMU_RATE_NORMAL: 31cf29b9afSRajmohan Mani if (root_switch) 32cf29b9afSRajmohan Mani return "normal"; 33cf29b9afSRajmohan Mani return "uni-directional, normal"; 34cf29b9afSRajmohan Mani 35cf29b9afSRajmohan Mani default: 36cf29b9afSRajmohan Mani return "unknown"; 37cf29b9afSRajmohan Mani } 38cf29b9afSRajmohan Mani } 39cf29b9afSRajmohan Mani 40cf29b9afSRajmohan Mani static bool tb_switch_tmu_ucap_supported(struct tb_switch *sw) 41cf29b9afSRajmohan Mani { 42cf29b9afSRajmohan Mani int ret; 43cf29b9afSRajmohan Mani u32 val; 44cf29b9afSRajmohan Mani 45cf29b9afSRajmohan Mani ret = tb_sw_read(sw, &val, TB_CFG_SWITCH, 46cf29b9afSRajmohan Mani sw->tmu.cap + TMU_RTR_CS_0, 1); 47cf29b9afSRajmohan Mani if (ret) 48cf29b9afSRajmohan Mani return false; 49cf29b9afSRajmohan Mani 50cf29b9afSRajmohan Mani return !!(val & TMU_RTR_CS_0_UCAP); 51cf29b9afSRajmohan Mani } 52cf29b9afSRajmohan Mani 53cf29b9afSRajmohan Mani static int tb_switch_tmu_rate_read(struct tb_switch *sw) 54cf29b9afSRajmohan Mani { 55cf29b9afSRajmohan Mani int ret; 56cf29b9afSRajmohan Mani u32 val; 57cf29b9afSRajmohan Mani 58cf29b9afSRajmohan Mani ret = tb_sw_read(sw, &val, TB_CFG_SWITCH, 59cf29b9afSRajmohan Mani sw->tmu.cap + TMU_RTR_CS_3, 1); 60cf29b9afSRajmohan Mani if (ret) 61cf29b9afSRajmohan Mani return ret; 62cf29b9afSRajmohan Mani 63cf29b9afSRajmohan Mani val >>= TMU_RTR_CS_3_TS_PACKET_INTERVAL_SHIFT; 64cf29b9afSRajmohan Mani return val; 65cf29b9afSRajmohan Mani } 66cf29b9afSRajmohan Mani 67cf29b9afSRajmohan Mani static int tb_switch_tmu_rate_write(struct tb_switch *sw, int rate) 68cf29b9afSRajmohan Mani { 69cf29b9afSRajmohan Mani int ret; 70cf29b9afSRajmohan Mani u32 val; 71cf29b9afSRajmohan Mani 72cf29b9afSRajmohan Mani ret = tb_sw_read(sw, &val, TB_CFG_SWITCH, 73cf29b9afSRajmohan Mani sw->tmu.cap + TMU_RTR_CS_3, 1); 74cf29b9afSRajmohan Mani if (ret) 75cf29b9afSRajmohan Mani return ret; 76cf29b9afSRajmohan Mani 77cf29b9afSRajmohan Mani val &= ~TMU_RTR_CS_3_TS_PACKET_INTERVAL_MASK; 78cf29b9afSRajmohan Mani val |= rate << TMU_RTR_CS_3_TS_PACKET_INTERVAL_SHIFT; 79cf29b9afSRajmohan Mani 80cf29b9afSRajmohan Mani return tb_sw_write(sw, &val, TB_CFG_SWITCH, 81cf29b9afSRajmohan Mani sw->tmu.cap + TMU_RTR_CS_3, 1); 82cf29b9afSRajmohan Mani } 83cf29b9afSRajmohan Mani 84cf29b9afSRajmohan Mani static int tb_port_tmu_write(struct tb_port *port, u8 offset, u32 mask, 85cf29b9afSRajmohan Mani u32 value) 86cf29b9afSRajmohan Mani { 87cf29b9afSRajmohan Mani u32 data; 88cf29b9afSRajmohan Mani int ret; 89cf29b9afSRajmohan Mani 90cf29b9afSRajmohan Mani ret = tb_port_read(port, &data, TB_CFG_PORT, port->cap_tmu + offset, 1); 91cf29b9afSRajmohan Mani if (ret) 92cf29b9afSRajmohan Mani return ret; 93cf29b9afSRajmohan Mani 94cf29b9afSRajmohan Mani data &= ~mask; 95cf29b9afSRajmohan Mani data |= value; 96cf29b9afSRajmohan Mani 97cf29b9afSRajmohan Mani return tb_port_write(port, &data, TB_CFG_PORT, 98cf29b9afSRajmohan Mani port->cap_tmu + offset, 1); 99cf29b9afSRajmohan Mani } 100cf29b9afSRajmohan Mani 101cf29b9afSRajmohan Mani static int tb_port_tmu_set_unidirectional(struct tb_port *port, 102cf29b9afSRajmohan Mani bool unidirectional) 103cf29b9afSRajmohan Mani { 104cf29b9afSRajmohan Mani u32 val; 105cf29b9afSRajmohan Mani 106cf29b9afSRajmohan Mani if (!port->sw->tmu.has_ucap) 107cf29b9afSRajmohan Mani return 0; 108cf29b9afSRajmohan Mani 109cf29b9afSRajmohan Mani val = unidirectional ? TMU_ADP_CS_3_UDM : 0; 110cf29b9afSRajmohan Mani return tb_port_tmu_write(port, TMU_ADP_CS_3, TMU_ADP_CS_3_UDM, val); 111cf29b9afSRajmohan Mani } 112cf29b9afSRajmohan Mani 113cf29b9afSRajmohan Mani static inline int tb_port_tmu_unidirectional_disable(struct tb_port *port) 114cf29b9afSRajmohan Mani { 115cf29b9afSRajmohan Mani return tb_port_tmu_set_unidirectional(port, false); 116cf29b9afSRajmohan Mani } 117cf29b9afSRajmohan Mani 118a28ec0e1SGil Fine static inline int tb_port_tmu_unidirectional_enable(struct tb_port *port) 119a28ec0e1SGil Fine { 120a28ec0e1SGil Fine return tb_port_tmu_set_unidirectional(port, true); 121a28ec0e1SGil Fine } 122a28ec0e1SGil Fine 123cf29b9afSRajmohan Mani static bool tb_port_tmu_is_unidirectional(struct tb_port *port) 124cf29b9afSRajmohan Mani { 125cf29b9afSRajmohan Mani int ret; 126cf29b9afSRajmohan Mani u32 val; 127cf29b9afSRajmohan Mani 128cf29b9afSRajmohan Mani ret = tb_port_read(port, &val, TB_CFG_PORT, 129cf29b9afSRajmohan Mani port->cap_tmu + TMU_ADP_CS_3, 1); 130cf29b9afSRajmohan Mani if (ret) 131cf29b9afSRajmohan Mani return false; 132cf29b9afSRajmohan Mani 133cf29b9afSRajmohan Mani return val & TMU_ADP_CS_3_UDM; 134cf29b9afSRajmohan Mani } 135cf29b9afSRajmohan Mani 136a28ec0e1SGil Fine static int tb_port_tmu_time_sync(struct tb_port *port, bool time_sync) 137a28ec0e1SGil Fine { 138a28ec0e1SGil Fine u32 val = time_sync ? TMU_ADP_CS_6_DTS : 0; 139a28ec0e1SGil Fine 140a28ec0e1SGil Fine return tb_port_tmu_write(port, TMU_ADP_CS_6, TMU_ADP_CS_6_DTS, val); 141a28ec0e1SGil Fine } 142a28ec0e1SGil Fine 143a28ec0e1SGil Fine static int tb_port_tmu_time_sync_disable(struct tb_port *port) 144a28ec0e1SGil Fine { 145a28ec0e1SGil Fine return tb_port_tmu_time_sync(port, true); 146a28ec0e1SGil Fine } 147a28ec0e1SGil Fine 148a28ec0e1SGil Fine static int tb_port_tmu_time_sync_enable(struct tb_port *port) 149a28ec0e1SGil Fine { 150a28ec0e1SGil Fine return tb_port_tmu_time_sync(port, false); 151a28ec0e1SGil Fine } 152a28ec0e1SGil Fine 153cf29b9afSRajmohan Mani static int tb_switch_tmu_set_time_disruption(struct tb_switch *sw, bool set) 154cf29b9afSRajmohan Mani { 15523ccd21cSGil Fine u32 val, offset, bit; 156cf29b9afSRajmohan Mani int ret; 157cf29b9afSRajmohan Mani 15823ccd21cSGil Fine if (tb_switch_is_usb4(sw)) { 15923ccd21cSGil Fine offset = sw->tmu.cap + TMU_RTR_CS_0; 16023ccd21cSGil Fine bit = TMU_RTR_CS_0_TD; 16123ccd21cSGil Fine } else { 16223ccd21cSGil Fine offset = sw->cap_vsec_tmu + TB_TIME_VSEC_3_CS_26; 16323ccd21cSGil Fine bit = TB_TIME_VSEC_3_CS_26_TD; 16423ccd21cSGil Fine } 16523ccd21cSGil Fine 16623ccd21cSGil Fine ret = tb_sw_read(sw, &val, TB_CFG_SWITCH, offset, 1); 167cf29b9afSRajmohan Mani if (ret) 168cf29b9afSRajmohan Mani return ret; 169cf29b9afSRajmohan Mani 170cf29b9afSRajmohan Mani if (set) 17123ccd21cSGil Fine val |= bit; 172cf29b9afSRajmohan Mani else 17323ccd21cSGil Fine val &= ~bit; 174cf29b9afSRajmohan Mani 17523ccd21cSGil Fine return tb_sw_write(sw, &val, TB_CFG_SWITCH, offset, 1); 176cf29b9afSRajmohan Mani } 177cf29b9afSRajmohan Mani 178cf29b9afSRajmohan Mani /** 179cf29b9afSRajmohan Mani * tb_switch_tmu_init() - Initialize switch TMU structures 180cf29b9afSRajmohan Mani * @sw: Switch to initialized 181cf29b9afSRajmohan Mani * 182cf29b9afSRajmohan Mani * This function must be called before other TMU related functions to 183cf29b9afSRajmohan Mani * makes the internal structures are filled in correctly. Does not 184cf29b9afSRajmohan Mani * change any hardware configuration. 185cf29b9afSRajmohan Mani */ 186cf29b9afSRajmohan Mani int tb_switch_tmu_init(struct tb_switch *sw) 187cf29b9afSRajmohan Mani { 188cf29b9afSRajmohan Mani struct tb_port *port; 189cf29b9afSRajmohan Mani int ret; 190cf29b9afSRajmohan Mani 191cf29b9afSRajmohan Mani if (tb_switch_is_icm(sw)) 192cf29b9afSRajmohan Mani return 0; 193cf29b9afSRajmohan Mani 194cf29b9afSRajmohan Mani ret = tb_switch_find_cap(sw, TB_SWITCH_CAP_TMU); 195cf29b9afSRajmohan Mani if (ret > 0) 196cf29b9afSRajmohan Mani sw->tmu.cap = ret; 197cf29b9afSRajmohan Mani 198cf29b9afSRajmohan Mani tb_switch_for_each_port(sw, port) { 199cf29b9afSRajmohan Mani int cap; 200cf29b9afSRajmohan Mani 201cf29b9afSRajmohan Mani cap = tb_port_find_cap(port, TB_PORT_CAP_TIME1); 202cf29b9afSRajmohan Mani if (cap > 0) 203cf29b9afSRajmohan Mani port->cap_tmu = cap; 204cf29b9afSRajmohan Mani } 205cf29b9afSRajmohan Mani 206cf29b9afSRajmohan Mani ret = tb_switch_tmu_rate_read(sw); 207cf29b9afSRajmohan Mani if (ret < 0) 208cf29b9afSRajmohan Mani return ret; 209cf29b9afSRajmohan Mani 210cf29b9afSRajmohan Mani sw->tmu.rate = ret; 211cf29b9afSRajmohan Mani 212cf29b9afSRajmohan Mani sw->tmu.has_ucap = tb_switch_tmu_ucap_supported(sw); 213cf29b9afSRajmohan Mani if (sw->tmu.has_ucap) { 214cf29b9afSRajmohan Mani tb_sw_dbg(sw, "TMU: supports uni-directional mode\n"); 215cf29b9afSRajmohan Mani 216cf29b9afSRajmohan Mani if (tb_route(sw)) { 217cf29b9afSRajmohan Mani struct tb_port *up = tb_upstream_port(sw); 218cf29b9afSRajmohan Mani 219cf29b9afSRajmohan Mani sw->tmu.unidirectional = 220cf29b9afSRajmohan Mani tb_port_tmu_is_unidirectional(up); 221cf29b9afSRajmohan Mani } 222cf29b9afSRajmohan Mani } else { 223cf29b9afSRajmohan Mani sw->tmu.unidirectional = false; 224cf29b9afSRajmohan Mani } 225cf29b9afSRajmohan Mani 226cf29b9afSRajmohan Mani tb_sw_dbg(sw, "TMU: current mode: %s\n", tb_switch_tmu_mode_name(sw)); 227cf29b9afSRajmohan Mani return 0; 228cf29b9afSRajmohan Mani } 229cf29b9afSRajmohan Mani 230cf29b9afSRajmohan Mani /** 231cf29b9afSRajmohan Mani * tb_switch_tmu_post_time() - Update switch local time 232cf29b9afSRajmohan Mani * @sw: Switch whose time to update 233cf29b9afSRajmohan Mani * 234cf29b9afSRajmohan Mani * Updates switch local time using time posting procedure. 235cf29b9afSRajmohan Mani */ 236cf29b9afSRajmohan Mani int tb_switch_tmu_post_time(struct tb_switch *sw) 237cf29b9afSRajmohan Mani { 238a28ec0e1SGil Fine unsigned int post_time_high_offset, post_time_high = 0; 239cf29b9afSRajmohan Mani unsigned int post_local_time_offset, post_time_offset; 240cf29b9afSRajmohan Mani struct tb_switch *root_switch = sw->tb->root_switch; 241cf29b9afSRajmohan Mani u64 hi, mid, lo, local_time, post_time; 242cf29b9afSRajmohan Mani int i, ret, retries = 100; 243cf29b9afSRajmohan Mani u32 gm_local_time[3]; 244cf29b9afSRajmohan Mani 245cf29b9afSRajmohan Mani if (!tb_route(sw)) 246cf29b9afSRajmohan Mani return 0; 247cf29b9afSRajmohan Mani 248cf29b9afSRajmohan Mani if (!tb_switch_is_usb4(sw)) 249cf29b9afSRajmohan Mani return 0; 250cf29b9afSRajmohan Mani 251cf29b9afSRajmohan Mani /* Need to be able to read the grand master time */ 252cf29b9afSRajmohan Mani if (!root_switch->tmu.cap) 253cf29b9afSRajmohan Mani return 0; 254cf29b9afSRajmohan Mani 255cf29b9afSRajmohan Mani ret = tb_sw_read(root_switch, gm_local_time, TB_CFG_SWITCH, 256cf29b9afSRajmohan Mani root_switch->tmu.cap + TMU_RTR_CS_1, 257cf29b9afSRajmohan Mani ARRAY_SIZE(gm_local_time)); 258cf29b9afSRajmohan Mani if (ret) 259cf29b9afSRajmohan Mani return ret; 260cf29b9afSRajmohan Mani 261cf29b9afSRajmohan Mani for (i = 0; i < ARRAY_SIZE(gm_local_time); i++) 262cf29b9afSRajmohan Mani tb_sw_dbg(root_switch, "local_time[%d]=0x%08x\n", i, 263cf29b9afSRajmohan Mani gm_local_time[i]); 264cf29b9afSRajmohan Mani 265cf29b9afSRajmohan Mani /* Convert to nanoseconds (drop fractional part) */ 266cf29b9afSRajmohan Mani hi = gm_local_time[2] & TMU_RTR_CS_3_LOCAL_TIME_NS_MASK; 267cf29b9afSRajmohan Mani mid = gm_local_time[1]; 268cf29b9afSRajmohan Mani lo = (gm_local_time[0] & TMU_RTR_CS_1_LOCAL_TIME_NS_MASK) >> 269cf29b9afSRajmohan Mani TMU_RTR_CS_1_LOCAL_TIME_NS_SHIFT; 270cf29b9afSRajmohan Mani local_time = hi << 48 | mid << 16 | lo; 271cf29b9afSRajmohan Mani 272cf29b9afSRajmohan Mani /* Tell the switch that time sync is disrupted for a while */ 273cf29b9afSRajmohan Mani ret = tb_switch_tmu_set_time_disruption(sw, true); 274cf29b9afSRajmohan Mani if (ret) 275cf29b9afSRajmohan Mani return ret; 276cf29b9afSRajmohan Mani 277cf29b9afSRajmohan Mani post_local_time_offset = sw->tmu.cap + TMU_RTR_CS_22; 278cf29b9afSRajmohan Mani post_time_offset = sw->tmu.cap + TMU_RTR_CS_24; 279a28ec0e1SGil Fine post_time_high_offset = sw->tmu.cap + TMU_RTR_CS_25; 280cf29b9afSRajmohan Mani 281cf29b9afSRajmohan Mani /* 282cf29b9afSRajmohan Mani * Write the Grandmaster time to the Post Local Time registers 283cf29b9afSRajmohan Mani * of the new switch. 284cf29b9afSRajmohan Mani */ 285cf29b9afSRajmohan Mani ret = tb_sw_write(sw, &local_time, TB_CFG_SWITCH, 286cf29b9afSRajmohan Mani post_local_time_offset, 2); 287cf29b9afSRajmohan Mani if (ret) 288cf29b9afSRajmohan Mani goto out; 289cf29b9afSRajmohan Mani 290cf29b9afSRajmohan Mani /* 291a28ec0e1SGil Fine * Have the new switch update its local time by: 292a28ec0e1SGil Fine * 1) writing 0x1 to the Post Time Low register and 0xffffffff to 293a28ec0e1SGil Fine * Post Time High register. 294a28ec0e1SGil Fine * 2) write 0 to Post Time High register and then wait for 295a28ec0e1SGil Fine * the completion of the post_time register becomes 0. 296a28ec0e1SGil Fine * This means the time has been converged properly. 297cf29b9afSRajmohan Mani */ 298a28ec0e1SGil Fine post_time = 0xffffffff00000001ULL; 299cf29b9afSRajmohan Mani 300cf29b9afSRajmohan Mani ret = tb_sw_write(sw, &post_time, TB_CFG_SWITCH, post_time_offset, 2); 301cf29b9afSRajmohan Mani if (ret) 302cf29b9afSRajmohan Mani goto out; 303cf29b9afSRajmohan Mani 304a28ec0e1SGil Fine ret = tb_sw_write(sw, &post_time_high, TB_CFG_SWITCH, 305a28ec0e1SGil Fine post_time_high_offset, 1); 306a28ec0e1SGil Fine if (ret) 307a28ec0e1SGil Fine goto out; 308a28ec0e1SGil Fine 309cf29b9afSRajmohan Mani do { 310cf29b9afSRajmohan Mani usleep_range(5, 10); 311cf29b9afSRajmohan Mani ret = tb_sw_read(sw, &post_time, TB_CFG_SWITCH, 312cf29b9afSRajmohan Mani post_time_offset, 2); 313cf29b9afSRajmohan Mani if (ret) 314cf29b9afSRajmohan Mani goto out; 315cf29b9afSRajmohan Mani } while (--retries && post_time); 316cf29b9afSRajmohan Mani 317cf29b9afSRajmohan Mani if (!retries) { 318cf29b9afSRajmohan Mani ret = -ETIMEDOUT; 319cf29b9afSRajmohan Mani goto out; 320cf29b9afSRajmohan Mani } 321cf29b9afSRajmohan Mani 322cf29b9afSRajmohan Mani tb_sw_dbg(sw, "TMU: updated local time to %#llx\n", local_time); 323cf29b9afSRajmohan Mani 324cf29b9afSRajmohan Mani out: 325cf29b9afSRajmohan Mani tb_switch_tmu_set_time_disruption(sw, false); 326cf29b9afSRajmohan Mani return ret; 327cf29b9afSRajmohan Mani } 328cf29b9afSRajmohan Mani 329cf29b9afSRajmohan Mani /** 330cf29b9afSRajmohan Mani * tb_switch_tmu_disable() - Disable TMU of a switch 331cf29b9afSRajmohan Mani * @sw: Switch whose TMU to disable 332cf29b9afSRajmohan Mani * 333cf29b9afSRajmohan Mani * Turns off TMU of @sw if it is enabled. If not enabled does nothing. 334cf29b9afSRajmohan Mani */ 335cf29b9afSRajmohan Mani int tb_switch_tmu_disable(struct tb_switch *sw) 336cf29b9afSRajmohan Mani { 33743f977bcSGil Fine /* 33843f977bcSGil Fine * No need to disable TMU on devices that don't support CLx since 33943f977bcSGil Fine * on these devices e.g. Alpine Ridge and earlier, the TMU mode 34043f977bcSGil Fine * HiFi bi-directional is enabled by default and we don't change it. 34143f977bcSGil Fine */ 34243f977bcSGil Fine if (!tb_switch_is_clx_supported(sw)) 343cf29b9afSRajmohan Mani return 0; 344cf29b9afSRajmohan Mani 345cf29b9afSRajmohan Mani /* Already disabled? */ 346cf29b9afSRajmohan Mani if (sw->tmu.rate == TB_SWITCH_TMU_RATE_OFF) 347cf29b9afSRajmohan Mani return 0; 348cf29b9afSRajmohan Mani 349a28ec0e1SGil Fine 350a28ec0e1SGil Fine if (tb_route(sw)) { 351a28ec0e1SGil Fine bool unidirectional = tb_switch_tmu_hifi_is_enabled(sw, true); 352cf29b9afSRajmohan Mani struct tb_switch *parent = tb_switch_parent(sw); 353a28ec0e1SGil Fine struct tb_port *down, *up; 354a28ec0e1SGil Fine int ret; 355cf29b9afSRajmohan Mani 356cf29b9afSRajmohan Mani down = tb_port_at(tb_route(sw), parent); 357a28ec0e1SGil Fine up = tb_upstream_port(sw); 358a28ec0e1SGil Fine /* 359a28ec0e1SGil Fine * In case of uni-directional time sync, TMU handshake is 360a28ec0e1SGil Fine * initiated by upstream router. In case of bi-directional 361a28ec0e1SGil Fine * time sync, TMU handshake is initiated by downstream router. 362*5fd6b9a5SGil Fine * We change downstream router's rate to off for both uni/bidir 363*5fd6b9a5SGil Fine * cases although it is needed only for the bi-directional mode. 364*5fd6b9a5SGil Fine * We avoid changing upstream router's mode since it might 365*5fd6b9a5SGil Fine * have another downstream router plugged, that is set to 366*5fd6b9a5SGil Fine * uni-directional mode and we don't want to change it's TMU 367*5fd6b9a5SGil Fine * mode. 368a28ec0e1SGil Fine */ 369a28ec0e1SGil Fine tb_switch_tmu_rate_write(sw, TB_SWITCH_TMU_RATE_OFF); 370cf29b9afSRajmohan Mani 371a28ec0e1SGil Fine tb_port_tmu_time_sync_disable(up); 372a28ec0e1SGil Fine ret = tb_port_tmu_time_sync_disable(down); 373a28ec0e1SGil Fine if (ret) 374a28ec0e1SGil Fine return ret; 375a28ec0e1SGil Fine 376a28ec0e1SGil Fine if (unidirectional) { 377cf29b9afSRajmohan Mani /* The switch may be unplugged so ignore any errors */ 378cf29b9afSRajmohan Mani tb_port_tmu_unidirectional_disable(up); 379cf29b9afSRajmohan Mani ret = tb_port_tmu_unidirectional_disable(down); 380cf29b9afSRajmohan Mani if (ret) 381cf29b9afSRajmohan Mani return ret; 382cf29b9afSRajmohan Mani } 383a28ec0e1SGil Fine } else { 384cf29b9afSRajmohan Mani tb_switch_tmu_rate_write(sw, TB_SWITCH_TMU_RATE_OFF); 385a28ec0e1SGil Fine } 386cf29b9afSRajmohan Mani 387cf29b9afSRajmohan Mani sw->tmu.unidirectional = false; 388cf29b9afSRajmohan Mani sw->tmu.rate = TB_SWITCH_TMU_RATE_OFF; 389cf29b9afSRajmohan Mani 390cf29b9afSRajmohan Mani tb_sw_dbg(sw, "TMU: disabled\n"); 391cf29b9afSRajmohan Mani return 0; 392cf29b9afSRajmohan Mani } 393cf29b9afSRajmohan Mani 394a28ec0e1SGil Fine static void __tb_switch_tmu_off(struct tb_switch *sw, bool unidirectional) 395cf29b9afSRajmohan Mani { 396a28ec0e1SGil Fine struct tb_switch *parent = tb_switch_parent(sw); 397a28ec0e1SGil Fine struct tb_port *down, *up; 398a28ec0e1SGil Fine 399a28ec0e1SGil Fine down = tb_port_at(tb_route(sw), parent); 400a28ec0e1SGil Fine up = tb_upstream_port(sw); 401a28ec0e1SGil Fine /* 402a28ec0e1SGil Fine * In case of any failure in one of the steps when setting 403a28ec0e1SGil Fine * bi-directional or uni-directional TMU mode, get back to the TMU 404a28ec0e1SGil Fine * configurations in off mode. In case of additional failures in 405a28ec0e1SGil Fine * the functions below, ignore them since the caller shall already 406a28ec0e1SGil Fine * report a failure. 407a28ec0e1SGil Fine */ 408a28ec0e1SGil Fine tb_port_tmu_time_sync_disable(down); 409a28ec0e1SGil Fine tb_port_tmu_time_sync_disable(up); 410a28ec0e1SGil Fine if (unidirectional) 411a28ec0e1SGil Fine tb_switch_tmu_rate_write(parent, TB_SWITCH_TMU_RATE_OFF); 412a28ec0e1SGil Fine else 413a28ec0e1SGil Fine tb_switch_tmu_rate_write(sw, TB_SWITCH_TMU_RATE_OFF); 414a28ec0e1SGil Fine 415a28ec0e1SGil Fine tb_port_tmu_unidirectional_disable(down); 416a28ec0e1SGil Fine tb_port_tmu_unidirectional_disable(up); 417a28ec0e1SGil Fine } 418a28ec0e1SGil Fine 419a28ec0e1SGil Fine /* 420a28ec0e1SGil Fine * This function is called when the previous TMU mode was 421a28ec0e1SGil Fine * TB_SWITCH_TMU_RATE_OFF. 422a28ec0e1SGil Fine */ 423a28ec0e1SGil Fine static int __tb_switch_tmu_enable_bidirectional(struct tb_switch *sw) 424a28ec0e1SGil Fine { 425a28ec0e1SGil Fine struct tb_switch *parent = tb_switch_parent(sw); 426a28ec0e1SGil Fine struct tb_port *up, *down; 427cf29b9afSRajmohan Mani int ret; 428cf29b9afSRajmohan Mani 429a28ec0e1SGil Fine up = tb_upstream_port(sw); 430a28ec0e1SGil Fine down = tb_port_at(tb_route(sw), parent); 431a28ec0e1SGil Fine 432a28ec0e1SGil Fine ret = tb_port_tmu_unidirectional_disable(up); 433a28ec0e1SGil Fine if (ret) 434a28ec0e1SGil Fine return ret; 435a28ec0e1SGil Fine 436a28ec0e1SGil Fine ret = tb_port_tmu_unidirectional_disable(down); 437a28ec0e1SGil Fine if (ret) 438a28ec0e1SGil Fine goto out; 439a28ec0e1SGil Fine 440a28ec0e1SGil Fine ret = tb_switch_tmu_rate_write(sw, TB_SWITCH_TMU_RATE_HIFI); 441a28ec0e1SGil Fine if (ret) 442a28ec0e1SGil Fine goto out; 443a28ec0e1SGil Fine 444a28ec0e1SGil Fine ret = tb_port_tmu_time_sync_enable(up); 445a28ec0e1SGil Fine if (ret) 446a28ec0e1SGil Fine goto out; 447a28ec0e1SGil Fine 448a28ec0e1SGil Fine ret = tb_port_tmu_time_sync_enable(down); 449a28ec0e1SGil Fine if (ret) 450a28ec0e1SGil Fine goto out; 451a28ec0e1SGil Fine 452a28ec0e1SGil Fine return 0; 453a28ec0e1SGil Fine 454a28ec0e1SGil Fine out: 455a28ec0e1SGil Fine __tb_switch_tmu_off(sw, false); 456a28ec0e1SGil Fine return ret; 457a28ec0e1SGil Fine } 458a28ec0e1SGil Fine 45943f977bcSGil Fine static int tb_switch_tmu_objection_mask(struct tb_switch *sw) 46043f977bcSGil Fine { 46143f977bcSGil Fine u32 val; 46243f977bcSGil Fine int ret; 46343f977bcSGil Fine 46443f977bcSGil Fine ret = tb_sw_read(sw, &val, TB_CFG_SWITCH, 46543f977bcSGil Fine sw->cap_vsec_tmu + TB_TIME_VSEC_3_CS_9, 1); 46643f977bcSGil Fine if (ret) 46743f977bcSGil Fine return ret; 46843f977bcSGil Fine 46943f977bcSGil Fine val &= ~TB_TIME_VSEC_3_CS_9_TMU_OBJ_MASK; 47043f977bcSGil Fine 47143f977bcSGil Fine return tb_sw_write(sw, &val, TB_CFG_SWITCH, 47243f977bcSGil Fine sw->cap_vsec_tmu + TB_TIME_VSEC_3_CS_9, 1); 47343f977bcSGil Fine } 47443f977bcSGil Fine 47543f977bcSGil Fine static int tb_switch_tmu_unidirectional_enable(struct tb_switch *sw) 47643f977bcSGil Fine { 47743f977bcSGil Fine struct tb_port *up = tb_upstream_port(sw); 47843f977bcSGil Fine 47943f977bcSGil Fine return tb_port_tmu_write(up, TMU_ADP_CS_6, 48043f977bcSGil Fine TMU_ADP_CS_6_DISABLE_TMU_OBJ_MASK, 48143f977bcSGil Fine TMU_ADP_CS_6_DISABLE_TMU_OBJ_MASK); 48243f977bcSGil Fine } 48343f977bcSGil Fine 484a28ec0e1SGil Fine /* 485a28ec0e1SGil Fine * This function is called when the previous TMU mode was 486a28ec0e1SGil Fine * TB_SWITCH_TMU_RATE_OFF. 487a28ec0e1SGil Fine */ 488a28ec0e1SGil Fine static int __tb_switch_tmu_enable_unidirectional(struct tb_switch *sw) 489a28ec0e1SGil Fine { 490a28ec0e1SGil Fine struct tb_switch *parent = tb_switch_parent(sw); 491a28ec0e1SGil Fine struct tb_port *up, *down; 492a28ec0e1SGil Fine int ret; 493a28ec0e1SGil Fine 494a28ec0e1SGil Fine up = tb_upstream_port(sw); 495a28ec0e1SGil Fine down = tb_port_at(tb_route(sw), parent); 496a28ec0e1SGil Fine ret = tb_switch_tmu_rate_write(parent, TB_SWITCH_TMU_RATE_HIFI); 497a28ec0e1SGil Fine if (ret) 498a28ec0e1SGil Fine return ret; 499a28ec0e1SGil Fine 500a28ec0e1SGil Fine ret = tb_port_tmu_unidirectional_enable(up); 501a28ec0e1SGil Fine if (ret) 502a28ec0e1SGil Fine goto out; 503a28ec0e1SGil Fine 504a28ec0e1SGil Fine ret = tb_port_tmu_time_sync_enable(up); 505a28ec0e1SGil Fine if (ret) 506a28ec0e1SGil Fine goto out; 507a28ec0e1SGil Fine 508a28ec0e1SGil Fine ret = tb_port_tmu_unidirectional_enable(down); 509a28ec0e1SGil Fine if (ret) 510a28ec0e1SGil Fine goto out; 511a28ec0e1SGil Fine 512a28ec0e1SGil Fine ret = tb_port_tmu_time_sync_enable(down); 513a28ec0e1SGil Fine if (ret) 514a28ec0e1SGil Fine goto out; 515a28ec0e1SGil Fine 516a28ec0e1SGil Fine return 0; 517a28ec0e1SGil Fine 518a28ec0e1SGil Fine out: 519a28ec0e1SGil Fine __tb_switch_tmu_off(sw, true); 520a28ec0e1SGil Fine return ret; 521a28ec0e1SGil Fine } 522a28ec0e1SGil Fine 523a28ec0e1SGil Fine static int tb_switch_tmu_hifi_enable(struct tb_switch *sw) 524a28ec0e1SGil Fine { 525a28ec0e1SGil Fine bool unidirectional = sw->tmu.unidirectional_request; 526a28ec0e1SGil Fine int ret; 527a28ec0e1SGil Fine 528a28ec0e1SGil Fine if (unidirectional && !sw->tmu.has_ucap) 529a28ec0e1SGil Fine return -EOPNOTSUPP; 530a28ec0e1SGil Fine 53143f977bcSGil Fine /* 53243f977bcSGil Fine * No need to enable TMU on devices that don't support CLx since on 53343f977bcSGil Fine * these devices e.g. Alpine Ridge and earlier, the TMU mode HiFi 53443f977bcSGil Fine * bi-directional is enabled by default. 53543f977bcSGil Fine */ 53643f977bcSGil Fine if (!tb_switch_is_clx_supported(sw)) 537cf29b9afSRajmohan Mani return 0; 538cf29b9afSRajmohan Mani 539a28ec0e1SGil Fine if (tb_switch_tmu_hifi_is_enabled(sw, sw->tmu.unidirectional_request)) 540cf29b9afSRajmohan Mani return 0; 541cf29b9afSRajmohan Mani 54243f977bcSGil Fine if (tb_switch_is_titan_ridge(sw) && unidirectional) { 54343f977bcSGil Fine /* Titan Ridge supports only CL0s */ 54443f977bcSGil Fine if (!tb_switch_is_cl0s_enabled(sw)) 54543f977bcSGil Fine return -EOPNOTSUPP; 54643f977bcSGil Fine 54743f977bcSGil Fine ret = tb_switch_tmu_objection_mask(sw); 54843f977bcSGil Fine if (ret) 54943f977bcSGil Fine return ret; 55043f977bcSGil Fine 55143f977bcSGil Fine ret = tb_switch_tmu_unidirectional_enable(sw); 55243f977bcSGil Fine if (ret) 55343f977bcSGil Fine return ret; 55443f977bcSGil Fine } 55543f977bcSGil Fine 556cf29b9afSRajmohan Mani ret = tb_switch_tmu_set_time_disruption(sw, true); 557cf29b9afSRajmohan Mani if (ret) 558cf29b9afSRajmohan Mani return ret; 559cf29b9afSRajmohan Mani 560a28ec0e1SGil Fine if (tb_route(sw)) { 561a28ec0e1SGil Fine /* The used mode changes are from OFF to HiFi-Uni/HiFi-BiDir */ 562a28ec0e1SGil Fine if (sw->tmu.rate == TB_SWITCH_TMU_RATE_OFF) { 563a28ec0e1SGil Fine if (unidirectional) 564a28ec0e1SGil Fine ret = __tb_switch_tmu_enable_unidirectional(sw); 565a28ec0e1SGil Fine else 566a28ec0e1SGil Fine ret = __tb_switch_tmu_enable_bidirectional(sw); 567cf29b9afSRajmohan Mani if (ret) 568cf29b9afSRajmohan Mani return ret; 569a28ec0e1SGil Fine } 570a28ec0e1SGil Fine sw->tmu.unidirectional = unidirectional; 571cf29b9afSRajmohan Mani } else { 572a28ec0e1SGil Fine /* 573a28ec0e1SGil Fine * Host router port configurations are written as 574a28ec0e1SGil Fine * part of configurations for downstream port of the parent 575a28ec0e1SGil Fine * of the child node - see above. 576a28ec0e1SGil Fine * Here only the host router' rate configuration is written. 577a28ec0e1SGil Fine */ 578cf29b9afSRajmohan Mani ret = tb_switch_tmu_rate_write(sw, TB_SWITCH_TMU_RATE_HIFI); 579cf29b9afSRajmohan Mani if (ret) 580cf29b9afSRajmohan Mani return ret; 581cf29b9afSRajmohan Mani } 582cf29b9afSRajmohan Mani 583cf29b9afSRajmohan Mani sw->tmu.rate = TB_SWITCH_TMU_RATE_HIFI; 584cf29b9afSRajmohan Mani 585a28ec0e1SGil Fine tb_sw_dbg(sw, "TMU: mode set to: %s\n", tb_switch_tmu_mode_name(sw)); 586cf29b9afSRajmohan Mani return tb_switch_tmu_set_time_disruption(sw, false); 587cf29b9afSRajmohan Mani } 588a28ec0e1SGil Fine 589a28ec0e1SGil Fine /** 590a28ec0e1SGil Fine * tb_switch_tmu_enable() - Enable TMU on a router 591a28ec0e1SGil Fine * @sw: Router whose TMU to enable 592a28ec0e1SGil Fine * 593a28ec0e1SGil Fine * Enables TMU of a router to be in uni-directional or bi-directional HiFi mode. 594a28ec0e1SGil Fine * Calling tb_switch_tmu_configure() is required before calling this function, 595a28ec0e1SGil Fine * to select the mode HiFi and directionality (uni-directional/bi-directional). 596a28ec0e1SGil Fine * In both modes all tunneling should work. Uni-directional mode is required for 597a28ec0e1SGil Fine * CLx (Link Low-Power) to work. 598a28ec0e1SGil Fine */ 599a28ec0e1SGil Fine int tb_switch_tmu_enable(struct tb_switch *sw) 600a28ec0e1SGil Fine { 601a28ec0e1SGil Fine if (sw->tmu.rate_request == TB_SWITCH_TMU_RATE_NORMAL) 602a28ec0e1SGil Fine return -EOPNOTSUPP; 603a28ec0e1SGil Fine 604a28ec0e1SGil Fine return tb_switch_tmu_hifi_enable(sw); 605a28ec0e1SGil Fine } 606a28ec0e1SGil Fine 607a28ec0e1SGil Fine /** 608a28ec0e1SGil Fine * tb_switch_tmu_configure() - Configure the TMU rate and directionality 609a28ec0e1SGil Fine * @sw: Router whose mode to change 610b4e08d5dSGil Fine * @rate: Rate to configure Off/Normal/HiFi 611a28ec0e1SGil Fine * @unidirectional: If uni-directional (bi-directional otherwise) 612a28ec0e1SGil Fine * 613a28ec0e1SGil Fine * Selects the rate of the TMU and directionality (uni-directional or 614a28ec0e1SGil Fine * bi-directional). Must be called before tb_switch_tmu_enable(). 615a28ec0e1SGil Fine */ 616a28ec0e1SGil Fine void tb_switch_tmu_configure(struct tb_switch *sw, 617a28ec0e1SGil Fine enum tb_switch_tmu_rate rate, bool unidirectional) 618a28ec0e1SGil Fine { 619a28ec0e1SGil Fine sw->tmu.unidirectional_request = unidirectional; 620a28ec0e1SGil Fine sw->tmu.rate_request = rate; 621a28ec0e1SGil Fine } 622