Commit | Line | Data |
---|---|---|
cf29b9af RM |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* | |
3 | * Thunderbolt Time Management Unit (TMU) support | |
4 | * | |
5 | * Copyright (C) 2019, Intel Corporation | |
6 | * Authors: Mika Westerberg <mika.westerberg@linux.intel.com> | |
7 | * Rajmohan Mani <rajmohan.mani@intel.com> | |
8 | */ | |
9 | ||
10 | #include <linux/delay.h> | |
11 | ||
12 | #include "tb.h" | |
13 | ||
b017a46d GF |
14 | static int tb_switch_set_tmu_mode_params(struct tb_switch *sw, |
15 | enum tb_switch_tmu_rate rate) | |
16 | { | |
17 | u32 freq_meas_wind[2] = { 30, 800 }; | |
18 | u32 avg_const[2] = { 4, 8 }; | |
19 | u32 freq, avg, val; | |
20 | int ret; | |
21 | ||
22 | if (rate == TB_SWITCH_TMU_RATE_NORMAL) { | |
23 | freq = freq_meas_wind[0]; | |
24 | avg = avg_const[0]; | |
25 | } else if (rate == TB_SWITCH_TMU_RATE_HIFI) { | |
26 | freq = freq_meas_wind[1]; | |
27 | avg = avg_const[1]; | |
28 | } else { | |
29 | return 0; | |
30 | } | |
31 | ||
32 | ret = tb_sw_read(sw, &val, TB_CFG_SWITCH, | |
33 | sw->tmu.cap + TMU_RTR_CS_0, 1); | |
34 | if (ret) | |
35 | return ret; | |
36 | ||
37 | val &= ~TMU_RTR_CS_0_FREQ_WIND_MASK; | |
38 | val |= FIELD_PREP(TMU_RTR_CS_0_FREQ_WIND_MASK, freq); | |
39 | ||
40 | ret = tb_sw_write(sw, &val, TB_CFG_SWITCH, | |
41 | sw->tmu.cap + TMU_RTR_CS_0, 1); | |
42 | if (ret) | |
43 | return ret; | |
44 | ||
45 | ret = tb_sw_read(sw, &val, TB_CFG_SWITCH, | |
46 | sw->tmu.cap + TMU_RTR_CS_15, 1); | |
47 | if (ret) | |
48 | return ret; | |
49 | ||
50 | val &= ~TMU_RTR_CS_15_FREQ_AVG_MASK & | |
51 | ~TMU_RTR_CS_15_DELAY_AVG_MASK & | |
52 | ~TMU_RTR_CS_15_OFFSET_AVG_MASK & | |
53 | ~TMU_RTR_CS_15_ERROR_AVG_MASK; | |
54 | val |= FIELD_PREP(TMU_RTR_CS_15_FREQ_AVG_MASK, avg) | | |
55 | FIELD_PREP(TMU_RTR_CS_15_DELAY_AVG_MASK, avg) | | |
56 | FIELD_PREP(TMU_RTR_CS_15_OFFSET_AVG_MASK, avg) | | |
57 | FIELD_PREP(TMU_RTR_CS_15_ERROR_AVG_MASK, avg); | |
58 | ||
59 | return tb_sw_write(sw, &val, TB_CFG_SWITCH, | |
60 | sw->tmu.cap + TMU_RTR_CS_15, 1); | |
61 | } | |
62 | ||
cf29b9af RM |
63 | static const char *tb_switch_tmu_mode_name(const struct tb_switch *sw) |
64 | { | |
65 | bool root_switch = !tb_route(sw); | |
66 | ||
67 | switch (sw->tmu.rate) { | |
68 | case TB_SWITCH_TMU_RATE_OFF: | |
69 | return "off"; | |
70 | ||
71 | case TB_SWITCH_TMU_RATE_HIFI: | |
72 | /* Root switch does not have upstream directionality */ | |
73 | if (root_switch) | |
74 | return "HiFi"; | |
75 | if (sw->tmu.unidirectional) | |
76 | return "uni-directional, HiFi"; | |
77 | return "bi-directional, HiFi"; | |
78 | ||
79 | case TB_SWITCH_TMU_RATE_NORMAL: | |
80 | if (root_switch) | |
81 | return "normal"; | |
82 | return "uni-directional, normal"; | |
83 | ||
84 | default: | |
85 | return "unknown"; | |
86 | } | |
87 | } | |
88 | ||
89 | static bool tb_switch_tmu_ucap_supported(struct tb_switch *sw) | |
90 | { | |
91 | int ret; | |
92 | u32 val; | |
93 | ||
94 | ret = tb_sw_read(sw, &val, TB_CFG_SWITCH, | |
95 | sw->tmu.cap + TMU_RTR_CS_0, 1); | |
96 | if (ret) | |
97 | return false; | |
98 | ||
99 | return !!(val & TMU_RTR_CS_0_UCAP); | |
100 | } | |
101 | ||
102 | static int tb_switch_tmu_rate_read(struct tb_switch *sw) | |
103 | { | |
104 | int ret; | |
105 | u32 val; | |
106 | ||
107 | ret = tb_sw_read(sw, &val, TB_CFG_SWITCH, | |
108 | sw->tmu.cap + TMU_RTR_CS_3, 1); | |
109 | if (ret) | |
110 | return ret; | |
111 | ||
112 | val >>= TMU_RTR_CS_3_TS_PACKET_INTERVAL_SHIFT; | |
113 | return val; | |
114 | } | |
115 | ||
116 | static int tb_switch_tmu_rate_write(struct tb_switch *sw, int rate) | |
117 | { | |
118 | int ret; | |
119 | u32 val; | |
120 | ||
121 | ret = tb_sw_read(sw, &val, TB_CFG_SWITCH, | |
122 | sw->tmu.cap + TMU_RTR_CS_3, 1); | |
123 | if (ret) | |
124 | return ret; | |
125 | ||
126 | val &= ~TMU_RTR_CS_3_TS_PACKET_INTERVAL_MASK; | |
127 | val |= rate << TMU_RTR_CS_3_TS_PACKET_INTERVAL_SHIFT; | |
128 | ||
129 | return tb_sw_write(sw, &val, TB_CFG_SWITCH, | |
130 | sw->tmu.cap + TMU_RTR_CS_3, 1); | |
131 | } | |
132 | ||
133 | static int tb_port_tmu_write(struct tb_port *port, u8 offset, u32 mask, | |
134 | u32 value) | |
135 | { | |
136 | u32 data; | |
137 | int ret; | |
138 | ||
139 | ret = tb_port_read(port, &data, TB_CFG_PORT, port->cap_tmu + offset, 1); | |
140 | if (ret) | |
141 | return ret; | |
142 | ||
143 | data &= ~mask; | |
144 | data |= value; | |
145 | ||
146 | return tb_port_write(port, &data, TB_CFG_PORT, | |
147 | port->cap_tmu + offset, 1); | |
148 | } | |
149 | ||
150 | static int tb_port_tmu_set_unidirectional(struct tb_port *port, | |
151 | bool unidirectional) | |
152 | { | |
153 | u32 val; | |
154 | ||
155 | if (!port->sw->tmu.has_ucap) | |
156 | return 0; | |
157 | ||
158 | val = unidirectional ? TMU_ADP_CS_3_UDM : 0; | |
159 | return tb_port_tmu_write(port, TMU_ADP_CS_3, TMU_ADP_CS_3_UDM, val); | |
160 | } | |
161 | ||
162 | static inline int tb_port_tmu_unidirectional_disable(struct tb_port *port) | |
163 | { | |
164 | return tb_port_tmu_set_unidirectional(port, false); | |
165 | } | |
166 | ||
a28ec0e1 GF |
167 | static inline int tb_port_tmu_unidirectional_enable(struct tb_port *port) |
168 | { | |
169 | return tb_port_tmu_set_unidirectional(port, true); | |
170 | } | |
171 | ||
cf29b9af RM |
172 | static bool tb_port_tmu_is_unidirectional(struct tb_port *port) |
173 | { | |
174 | int ret; | |
175 | u32 val; | |
176 | ||
177 | ret = tb_port_read(port, &val, TB_CFG_PORT, | |
178 | port->cap_tmu + TMU_ADP_CS_3, 1); | |
179 | if (ret) | |
180 | return false; | |
181 | ||
182 | return val & TMU_ADP_CS_3_UDM; | |
183 | } | |
184 | ||
a28ec0e1 GF |
185 | static int tb_port_tmu_time_sync(struct tb_port *port, bool time_sync) |
186 | { | |
187 | u32 val = time_sync ? TMU_ADP_CS_6_DTS : 0; | |
188 | ||
189 | return tb_port_tmu_write(port, TMU_ADP_CS_6, TMU_ADP_CS_6_DTS, val); | |
190 | } | |
191 | ||
192 | static int tb_port_tmu_time_sync_disable(struct tb_port *port) | |
193 | { | |
194 | return tb_port_tmu_time_sync(port, true); | |
195 | } | |
196 | ||
197 | static int tb_port_tmu_time_sync_enable(struct tb_port *port) | |
198 | { | |
199 | return tb_port_tmu_time_sync(port, false); | |
200 | } | |
201 | ||
cf29b9af RM |
202 | static int tb_switch_tmu_set_time_disruption(struct tb_switch *sw, bool set) |
203 | { | |
23ccd21c | 204 | u32 val, offset, bit; |
cf29b9af | 205 | int ret; |
cf29b9af | 206 | |
23ccd21c GF |
207 | if (tb_switch_is_usb4(sw)) { |
208 | offset = sw->tmu.cap + TMU_RTR_CS_0; | |
209 | bit = TMU_RTR_CS_0_TD; | |
210 | } else { | |
211 | offset = sw->cap_vsec_tmu + TB_TIME_VSEC_3_CS_26; | |
212 | bit = TB_TIME_VSEC_3_CS_26_TD; | |
213 | } | |
214 | ||
215 | ret = tb_sw_read(sw, &val, TB_CFG_SWITCH, offset, 1); | |
cf29b9af RM |
216 | if (ret) |
217 | return ret; | |
218 | ||
219 | if (set) | |
23ccd21c | 220 | val |= bit; |
cf29b9af | 221 | else |
23ccd21c | 222 | val &= ~bit; |
cf29b9af | 223 | |
23ccd21c | 224 | return tb_sw_write(sw, &val, TB_CFG_SWITCH, offset, 1); |
cf29b9af RM |
225 | } |
226 | ||
227 | /** | |
228 | * tb_switch_tmu_init() - Initialize switch TMU structures | |
229 | * @sw: Switch to initialized | |
230 | * | |
231 | * This function must be called before other TMU related functions to | |
232 | * makes the internal structures are filled in correctly. Does not | |
233 | * change any hardware configuration. | |
234 | */ | |
235 | int tb_switch_tmu_init(struct tb_switch *sw) | |
236 | { | |
237 | struct tb_port *port; | |
238 | int ret; | |
239 | ||
240 | if (tb_switch_is_icm(sw)) | |
241 | return 0; | |
242 | ||
243 | ret = tb_switch_find_cap(sw, TB_SWITCH_CAP_TMU); | |
244 | if (ret > 0) | |
245 | sw->tmu.cap = ret; | |
246 | ||
247 | tb_switch_for_each_port(sw, port) { | |
248 | int cap; | |
249 | ||
250 | cap = tb_port_find_cap(port, TB_PORT_CAP_TIME1); | |
251 | if (cap > 0) | |
252 | port->cap_tmu = cap; | |
253 | } | |
254 | ||
255 | ret = tb_switch_tmu_rate_read(sw); | |
256 | if (ret < 0) | |
257 | return ret; | |
258 | ||
259 | sw->tmu.rate = ret; | |
260 | ||
261 | sw->tmu.has_ucap = tb_switch_tmu_ucap_supported(sw); | |
262 | if (sw->tmu.has_ucap) { | |
263 | tb_sw_dbg(sw, "TMU: supports uni-directional mode\n"); | |
264 | ||
265 | if (tb_route(sw)) { | |
266 | struct tb_port *up = tb_upstream_port(sw); | |
267 | ||
268 | sw->tmu.unidirectional = | |
269 | tb_port_tmu_is_unidirectional(up); | |
270 | } | |
271 | } else { | |
272 | sw->tmu.unidirectional = false; | |
273 | } | |
274 | ||
275 | tb_sw_dbg(sw, "TMU: current mode: %s\n", tb_switch_tmu_mode_name(sw)); | |
276 | return 0; | |
277 | } | |
278 | ||
279 | /** | |
280 | * tb_switch_tmu_post_time() - Update switch local time | |
281 | * @sw: Switch whose time to update | |
282 | * | |
283 | * Updates switch local time using time posting procedure. | |
284 | */ | |
285 | int tb_switch_tmu_post_time(struct tb_switch *sw) | |
286 | { | |
a28ec0e1 GF |
287 | unsigned int post_time_high_offset, post_time_high = 0; |
288 | unsigned int post_local_time_offset, post_time_offset; | |
cf29b9af RM |
289 | struct tb_switch *root_switch = sw->tb->root_switch; |
290 | u64 hi, mid, lo, local_time, post_time; | |
291 | int i, ret, retries = 100; | |
292 | u32 gm_local_time[3]; | |
293 | ||
294 | if (!tb_route(sw)) | |
295 | return 0; | |
296 | ||
297 | if (!tb_switch_is_usb4(sw)) | |
298 | return 0; | |
299 | ||
300 | /* Need to be able to read the grand master time */ | |
301 | if (!root_switch->tmu.cap) | |
302 | return 0; | |
303 | ||
304 | ret = tb_sw_read(root_switch, gm_local_time, TB_CFG_SWITCH, | |
305 | root_switch->tmu.cap + TMU_RTR_CS_1, | |
306 | ARRAY_SIZE(gm_local_time)); | |
307 | if (ret) | |
308 | return ret; | |
309 | ||
310 | for (i = 0; i < ARRAY_SIZE(gm_local_time); i++) | |
311 | tb_sw_dbg(root_switch, "local_time[%d]=0x%08x\n", i, | |
312 | gm_local_time[i]); | |
313 | ||
314 | /* Convert to nanoseconds (drop fractional part) */ | |
315 | hi = gm_local_time[2] & TMU_RTR_CS_3_LOCAL_TIME_NS_MASK; | |
316 | mid = gm_local_time[1]; | |
317 | lo = (gm_local_time[0] & TMU_RTR_CS_1_LOCAL_TIME_NS_MASK) >> | |
318 | TMU_RTR_CS_1_LOCAL_TIME_NS_SHIFT; | |
319 | local_time = hi << 48 | mid << 16 | lo; | |
320 | ||
321 | /* Tell the switch that time sync is disrupted for a while */ | |
322 | ret = tb_switch_tmu_set_time_disruption(sw, true); | |
323 | if (ret) | |
324 | return ret; | |
325 | ||
326 | post_local_time_offset = sw->tmu.cap + TMU_RTR_CS_22; | |
327 | post_time_offset = sw->tmu.cap + TMU_RTR_CS_24; | |
a28ec0e1 | 328 | post_time_high_offset = sw->tmu.cap + TMU_RTR_CS_25; |
cf29b9af RM |
329 | |
330 | /* | |
331 | * Write the Grandmaster time to the Post Local Time registers | |
332 | * of the new switch. | |
333 | */ | |
334 | ret = tb_sw_write(sw, &local_time, TB_CFG_SWITCH, | |
335 | post_local_time_offset, 2); | |
336 | if (ret) | |
337 | goto out; | |
338 | ||
339 | /* | |
a28ec0e1 GF |
340 | * Have the new switch update its local time by: |
341 | * 1) writing 0x1 to the Post Time Low register and 0xffffffff to | |
342 | * Post Time High register. | |
343 | * 2) write 0 to Post Time High register and then wait for | |
344 | * the completion of the post_time register becomes 0. | |
345 | * This means the time has been converged properly. | |
cf29b9af | 346 | */ |
a28ec0e1 | 347 | post_time = 0xffffffff00000001ULL; |
cf29b9af RM |
348 | |
349 | ret = tb_sw_write(sw, &post_time, TB_CFG_SWITCH, post_time_offset, 2); | |
350 | if (ret) | |
351 | goto out; | |
352 | ||
a28ec0e1 GF |
353 | ret = tb_sw_write(sw, &post_time_high, TB_CFG_SWITCH, |
354 | post_time_high_offset, 1); | |
355 | if (ret) | |
356 | goto out; | |
357 | ||
cf29b9af RM |
358 | do { |
359 | usleep_range(5, 10); | |
360 | ret = tb_sw_read(sw, &post_time, TB_CFG_SWITCH, | |
361 | post_time_offset, 2); | |
362 | if (ret) | |
363 | goto out; | |
364 | } while (--retries && post_time); | |
365 | ||
366 | if (!retries) { | |
367 | ret = -ETIMEDOUT; | |
368 | goto out; | |
369 | } | |
370 | ||
371 | tb_sw_dbg(sw, "TMU: updated local time to %#llx\n", local_time); | |
372 | ||
373 | out: | |
374 | tb_switch_tmu_set_time_disruption(sw, false); | |
375 | return ret; | |
376 | } | |
377 | ||
378 | /** | |
379 | * tb_switch_tmu_disable() - Disable TMU of a switch | |
380 | * @sw: Switch whose TMU to disable | |
381 | * | |
382 | * Turns off TMU of @sw if it is enabled. If not enabled does nothing. | |
383 | */ | |
384 | int tb_switch_tmu_disable(struct tb_switch *sw) | |
385 | { | |
43f977bc GF |
386 | /* |
387 | * No need to disable TMU on devices that don't support CLx since | |
388 | * on these devices e.g. Alpine Ridge and earlier, the TMU mode | |
389 | * HiFi bi-directional is enabled by default and we don't change it. | |
390 | */ | |
391 | if (!tb_switch_is_clx_supported(sw)) | |
cf29b9af RM |
392 | return 0; |
393 | ||
394 | /* Already disabled? */ | |
395 | if (sw->tmu.rate == TB_SWITCH_TMU_RATE_OFF) | |
396 | return 0; | |
397 | ||
a28ec0e1 GF |
398 | |
399 | if (tb_route(sw)) { | |
b017a46d | 400 | bool unidirectional = sw->tmu.unidirectional; |
cf29b9af | 401 | struct tb_switch *parent = tb_switch_parent(sw); |
a28ec0e1 GF |
402 | struct tb_port *down, *up; |
403 | int ret; | |
cf29b9af | 404 | |
cf29b9af | 405 | down = tb_port_at(tb_route(sw), parent); |
a28ec0e1 GF |
406 | up = tb_upstream_port(sw); |
407 | /* | |
408 | * In case of uni-directional time sync, TMU handshake is | |
409 | * initiated by upstream router. In case of bi-directional | |
410 | * time sync, TMU handshake is initiated by downstream router. | |
5fd6b9a5 GF |
411 | * We change downstream router's rate to off for both uni/bidir |
412 | * cases although it is needed only for the bi-directional mode. | |
413 | * We avoid changing upstream router's mode since it might | |
414 | * have another downstream router plugged, that is set to | |
415 | * uni-directional mode and we don't want to change it's TMU | |
416 | * mode. | |
a28ec0e1 | 417 | */ |
5fd6b9a5 | 418 | tb_switch_tmu_rate_write(sw, TB_SWITCH_TMU_RATE_OFF); |
a28ec0e1 GF |
419 | |
420 | tb_port_tmu_time_sync_disable(up); | |
421 | ret = tb_port_tmu_time_sync_disable(down); | |
cf29b9af RM |
422 | if (ret) |
423 | return ret; | |
cf29b9af | 424 | |
a28ec0e1 GF |
425 | if (unidirectional) { |
426 | /* The switch may be unplugged so ignore any errors */ | |
427 | tb_port_tmu_unidirectional_disable(up); | |
428 | ret = tb_port_tmu_unidirectional_disable(down); | |
429 | if (ret) | |
430 | return ret; | |
431 | } | |
432 | } else { | |
433 | tb_switch_tmu_rate_write(sw, TB_SWITCH_TMU_RATE_OFF); | |
434 | } | |
cf29b9af RM |
435 | |
436 | sw->tmu.unidirectional = false; | |
437 | sw->tmu.rate = TB_SWITCH_TMU_RATE_OFF; | |
438 | ||
439 | tb_sw_dbg(sw, "TMU: disabled\n"); | |
440 | return 0; | |
441 | } | |
442 | ||
a28ec0e1 GF |
443 | static void __tb_switch_tmu_off(struct tb_switch *sw, bool unidirectional) |
444 | { | |
445 | struct tb_switch *parent = tb_switch_parent(sw); | |
446 | struct tb_port *down, *up; | |
447 | ||
448 | down = tb_port_at(tb_route(sw), parent); | |
449 | up = tb_upstream_port(sw); | |
450 | /* | |
451 | * In case of any failure in one of the steps when setting | |
452 | * bi-directional or uni-directional TMU mode, get back to the TMU | |
453 | * configurations in off mode. In case of additional failures in | |
454 | * the functions below, ignore them since the caller shall already | |
455 | * report a failure. | |
456 | */ | |
457 | tb_port_tmu_time_sync_disable(down); | |
458 | tb_port_tmu_time_sync_disable(up); | |
459 | if (unidirectional) | |
460 | tb_switch_tmu_rate_write(parent, TB_SWITCH_TMU_RATE_OFF); | |
461 | else | |
462 | tb_switch_tmu_rate_write(sw, TB_SWITCH_TMU_RATE_OFF); | |
463 | ||
b017a46d | 464 | tb_switch_set_tmu_mode_params(sw, sw->tmu.rate); |
a28ec0e1 GF |
465 | tb_port_tmu_unidirectional_disable(down); |
466 | tb_port_tmu_unidirectional_disable(up); | |
467 | } | |
468 | ||
469 | /* | |
470 | * This function is called when the previous TMU mode was | |
471 | * TB_SWITCH_TMU_RATE_OFF. | |
cf29b9af | 472 | */ |
a28ec0e1 | 473 | static int __tb_switch_tmu_enable_bidirectional(struct tb_switch *sw) |
cf29b9af | 474 | { |
a28ec0e1 GF |
475 | struct tb_switch *parent = tb_switch_parent(sw); |
476 | struct tb_port *up, *down; | |
cf29b9af RM |
477 | int ret; |
478 | ||
a28ec0e1 GF |
479 | up = tb_upstream_port(sw); |
480 | down = tb_port_at(tb_route(sw), parent); | |
cf29b9af | 481 | |
a28ec0e1 GF |
482 | ret = tb_port_tmu_unidirectional_disable(up); |
483 | if (ret) | |
484 | return ret; | |
cf29b9af | 485 | |
a28ec0e1 GF |
486 | ret = tb_port_tmu_unidirectional_disable(down); |
487 | if (ret) | |
488 | goto out; | |
489 | ||
490 | ret = tb_switch_tmu_rate_write(sw, TB_SWITCH_TMU_RATE_HIFI); | |
491 | if (ret) | |
492 | goto out; | |
493 | ||
494 | ret = tb_port_tmu_time_sync_enable(up); | |
495 | if (ret) | |
496 | goto out; | |
497 | ||
498 | ret = tb_port_tmu_time_sync_enable(down); | |
499 | if (ret) | |
500 | goto out; | |
501 | ||
502 | return 0; | |
503 | ||
504 | out: | |
505 | __tb_switch_tmu_off(sw, false); | |
506 | return ret; | |
507 | } | |
508 | ||
43f977bc GF |
509 | static int tb_switch_tmu_objection_mask(struct tb_switch *sw) |
510 | { | |
511 | u32 val; | |
512 | int ret; | |
513 | ||
514 | ret = tb_sw_read(sw, &val, TB_CFG_SWITCH, | |
515 | sw->cap_vsec_tmu + TB_TIME_VSEC_3_CS_9, 1); | |
516 | if (ret) | |
517 | return ret; | |
518 | ||
519 | val &= ~TB_TIME_VSEC_3_CS_9_TMU_OBJ_MASK; | |
520 | ||
521 | return tb_sw_write(sw, &val, TB_CFG_SWITCH, | |
522 | sw->cap_vsec_tmu + TB_TIME_VSEC_3_CS_9, 1); | |
523 | } | |
524 | ||
525 | static int tb_switch_tmu_unidirectional_enable(struct tb_switch *sw) | |
526 | { | |
527 | struct tb_port *up = tb_upstream_port(sw); | |
528 | ||
529 | return tb_port_tmu_write(up, TMU_ADP_CS_6, | |
530 | TMU_ADP_CS_6_DISABLE_TMU_OBJ_MASK, | |
531 | TMU_ADP_CS_6_DISABLE_TMU_OBJ_MASK); | |
532 | } | |
533 | ||
a28ec0e1 GF |
534 | /* |
535 | * This function is called when the previous TMU mode was | |
536 | * TB_SWITCH_TMU_RATE_OFF. | |
537 | */ | |
538 | static int __tb_switch_tmu_enable_unidirectional(struct tb_switch *sw) | |
539 | { | |
540 | struct tb_switch *parent = tb_switch_parent(sw); | |
541 | struct tb_port *up, *down; | |
542 | int ret; | |
543 | ||
544 | up = tb_upstream_port(sw); | |
545 | down = tb_port_at(tb_route(sw), parent); | |
b017a46d GF |
546 | ret = tb_switch_tmu_rate_write(parent, sw->tmu.rate_request); |
547 | if (ret) | |
548 | return ret; | |
549 | ||
550 | ret = tb_switch_set_tmu_mode_params(sw, sw->tmu.rate_request); | |
cf29b9af RM |
551 | if (ret) |
552 | return ret; | |
553 | ||
a28ec0e1 GF |
554 | ret = tb_port_tmu_unidirectional_enable(up); |
555 | if (ret) | |
556 | goto out; | |
cf29b9af | 557 | |
a28ec0e1 GF |
558 | ret = tb_port_tmu_time_sync_enable(up); |
559 | if (ret) | |
560 | goto out; | |
cf29b9af | 561 | |
a28ec0e1 GF |
562 | ret = tb_port_tmu_unidirectional_enable(down); |
563 | if (ret) | |
564 | goto out; | |
cf29b9af | 565 | |
a28ec0e1 GF |
566 | ret = tb_port_tmu_time_sync_enable(down); |
567 | if (ret) | |
568 | goto out; | |
cf29b9af | 569 | |
a28ec0e1 GF |
570 | return 0; |
571 | ||
572 | out: | |
573 | __tb_switch_tmu_off(sw, true); | |
574 | return ret; | |
575 | } | |
576 | ||
b017a46d GF |
577 | static void __tb_switch_tmu_change_mode_prev(struct tb_switch *sw) |
578 | { | |
579 | struct tb_switch *parent = tb_switch_parent(sw); | |
580 | struct tb_port *down, *up; | |
581 | ||
582 | down = tb_port_at(tb_route(sw), parent); | |
583 | up = tb_upstream_port(sw); | |
584 | /* | |
585 | * In case of any failure in one of the steps when change mode, | |
586 | * get back to the TMU configurations in previous mode. | |
587 | * In case of additional failures in the functions below, | |
588 | * ignore them since the caller shall already report a failure. | |
589 | */ | |
590 | tb_port_tmu_set_unidirectional(down, sw->tmu.unidirectional); | |
591 | if (sw->tmu.unidirectional_request) | |
592 | tb_switch_tmu_rate_write(parent, sw->tmu.rate); | |
593 | else | |
594 | tb_switch_tmu_rate_write(sw, sw->tmu.rate); | |
595 | ||
596 | tb_switch_set_tmu_mode_params(sw, sw->tmu.rate); | |
597 | tb_port_tmu_set_unidirectional(up, sw->tmu.unidirectional); | |
598 | } | |
599 | ||
600 | static int __tb_switch_tmu_change_mode(struct tb_switch *sw) | |
601 | { | |
602 | struct tb_switch *parent = tb_switch_parent(sw); | |
603 | struct tb_port *up, *down; | |
604 | int ret; | |
605 | ||
606 | up = tb_upstream_port(sw); | |
607 | down = tb_port_at(tb_route(sw), parent); | |
608 | ret = tb_port_tmu_set_unidirectional(down, sw->tmu.unidirectional_request); | |
609 | if (ret) | |
610 | goto out; | |
611 | ||
612 | if (sw->tmu.unidirectional_request) | |
613 | ret = tb_switch_tmu_rate_write(parent, sw->tmu.rate_request); | |
614 | else | |
615 | ret = tb_switch_tmu_rate_write(sw, sw->tmu.rate_request); | |
616 | if (ret) | |
617 | return ret; | |
618 | ||
619 | ret = tb_switch_set_tmu_mode_params(sw, sw->tmu.rate_request); | |
620 | if (ret) | |
621 | return ret; | |
622 | ||
623 | ret = tb_port_tmu_set_unidirectional(up, sw->tmu.unidirectional_request); | |
624 | if (ret) | |
625 | goto out; | |
626 | ||
627 | ret = tb_port_tmu_time_sync_enable(down); | |
628 | if (ret) | |
629 | goto out; | |
630 | ||
631 | ret = tb_port_tmu_time_sync_enable(up); | |
632 | if (ret) | |
633 | goto out; | |
634 | ||
635 | return 0; | |
636 | ||
637 | out: | |
638 | __tb_switch_tmu_change_mode_prev(sw); | |
639 | return ret; | |
640 | } | |
641 | ||
642 | /** | |
643 | * tb_switch_tmu_enable() - Enable TMU on a router | |
644 | * @sw: Router whose TMU to enable | |
645 | * | |
646 | * Enables TMU of a router to be in uni-directional Normal/HiFi | |
647 | * or bi-directional HiFi mode. Calling tb_switch_tmu_configure() is required | |
648 | * before calling this function, to select the mode Normal/HiFi and | |
649 | * directionality (uni-directional/bi-directional). | |
650 | * In HiFi mode all tunneling should work. In Normal mode, DP tunneling can't | |
651 | * work. Uni-directional mode is required for CLx (Link Low-Power) to work. | |
652 | */ | |
653 | int tb_switch_tmu_enable(struct tb_switch *sw) | |
a28ec0e1 GF |
654 | { |
655 | bool unidirectional = sw->tmu.unidirectional_request; | |
656 | int ret; | |
657 | ||
658 | if (unidirectional && !sw->tmu.has_ucap) | |
659 | return -EOPNOTSUPP; | |
660 | ||
43f977bc GF |
661 | /* |
662 | * No need to enable TMU on devices that don't support CLx since on | |
663 | * these devices e.g. Alpine Ridge and earlier, the TMU mode HiFi | |
664 | * bi-directional is enabled by default. | |
665 | */ | |
666 | if (!tb_switch_is_clx_supported(sw)) | |
a28ec0e1 GF |
667 | return 0; |
668 | ||
b017a46d | 669 | if (tb_switch_tmu_is_enabled(sw, sw->tmu.unidirectional_request)) |
a28ec0e1 GF |
670 | return 0; |
671 | ||
43f977bc | 672 | if (tb_switch_is_titan_ridge(sw) && unidirectional) { |
b017a46d GF |
673 | /* |
674 | * Titan Ridge supports CL0s and CL1 only. CL0s and CL1 are | |
675 | * enabled and supported together. | |
676 | */ | |
677 | if (!tb_switch_is_clx_enabled(sw, TB_CL1)) | |
43f977bc GF |
678 | return -EOPNOTSUPP; |
679 | ||
680 | ret = tb_switch_tmu_objection_mask(sw); | |
681 | if (ret) | |
682 | return ret; | |
683 | ||
684 | ret = tb_switch_tmu_unidirectional_enable(sw); | |
685 | if (ret) | |
686 | return ret; | |
687 | } | |
688 | ||
a28ec0e1 GF |
689 | ret = tb_switch_tmu_set_time_disruption(sw, true); |
690 | if (ret) | |
691 | return ret; | |
692 | ||
693 | if (tb_route(sw)) { | |
b017a46d GF |
694 | /* |
695 | * The used mode changes are from OFF to | |
696 | * HiFi-Uni/HiFi-BiDir/Normal-Uni or from Normal-Uni to | |
697 | * HiFi-Uni. | |
698 | */ | |
a28ec0e1 GF |
699 | if (sw->tmu.rate == TB_SWITCH_TMU_RATE_OFF) { |
700 | if (unidirectional) | |
701 | ret = __tb_switch_tmu_enable_unidirectional(sw); | |
702 | else | |
703 | ret = __tb_switch_tmu_enable_bidirectional(sw); | |
704 | if (ret) | |
705 | return ret; | |
b017a46d GF |
706 | } else if (sw->tmu.rate == TB_SWITCH_TMU_RATE_NORMAL) { |
707 | ret = __tb_switch_tmu_change_mode(sw); | |
708 | if (ret) | |
709 | return ret; | |
a28ec0e1 GF |
710 | } |
711 | sw->tmu.unidirectional = unidirectional; | |
cf29b9af | 712 | } else { |
a28ec0e1 GF |
713 | /* |
714 | * Host router port configurations are written as | |
715 | * part of configurations for downstream port of the parent | |
716 | * of the child node - see above. | |
717 | * Here only the host router' rate configuration is written. | |
718 | */ | |
b017a46d | 719 | ret = tb_switch_tmu_rate_write(sw, sw->tmu.rate_request); |
cf29b9af RM |
720 | if (ret) |
721 | return ret; | |
722 | } | |
723 | ||
b017a46d | 724 | sw->tmu.rate = sw->tmu.rate_request; |
cf29b9af | 725 | |
a28ec0e1 | 726 | tb_sw_dbg(sw, "TMU: mode set to: %s\n", tb_switch_tmu_mode_name(sw)); |
cf29b9af RM |
727 | return tb_switch_tmu_set_time_disruption(sw, false); |
728 | } | |
a28ec0e1 | 729 | |
a28ec0e1 GF |
730 | /** |
731 | * tb_switch_tmu_configure() - Configure the TMU rate and directionality | |
732 | * @sw: Router whose mode to change | |
b4e08d5d | 733 | * @rate: Rate to configure Off/Normal/HiFi |
a28ec0e1 GF |
734 | * @unidirectional: If uni-directional (bi-directional otherwise) |
735 | * | |
736 | * Selects the rate of the TMU and directionality (uni-directional or | |
737 | * bi-directional). Must be called before tb_switch_tmu_enable(). | |
738 | */ | |
739 | void tb_switch_tmu_configure(struct tb_switch *sw, | |
740 | enum tb_switch_tmu_rate rate, bool unidirectional) | |
741 | { | |
742 | sw->tmu.unidirectional_request = unidirectional; | |
743 | sw->tmu.rate_request = rate; | |
744 | } | |
3084b48f GF |
745 | |
746 | static int tb_switch_tmu_config_enable(struct device *dev, void *rate) | |
747 | { | |
748 | if (tb_is_switch(dev)) { | |
749 | struct tb_switch *sw = tb_to_switch(dev); | |
750 | ||
751 | tb_switch_tmu_configure(sw, *(enum tb_switch_tmu_rate *)rate, | |
752 | tb_switch_is_clx_enabled(sw, TB_CL1)); | |
753 | if (tb_switch_tmu_enable(sw)) | |
754 | tb_sw_dbg(sw, "fail switching TMU mode for 1st depth router\n"); | |
755 | } | |
756 | ||
757 | return 0; | |
758 | } | |
759 | ||
760 | /** | |
761 | * tb_switch_enable_tmu_1st_child - Configure and enable TMU for 1st chidren | |
762 | * @sw: The router to configure and enable it's children TMU | |
763 | * @rate: Rate of the TMU to configure the router's chidren to | |
764 | * | |
765 | * Configures and enables the TMU mode of 1st depth children of the specified | |
766 | * router to the specified rate. | |
767 | */ | |
768 | void tb_switch_enable_tmu_1st_child(struct tb_switch *sw, | |
769 | enum tb_switch_tmu_rate rate) | |
770 | { | |
771 | device_for_each_child(&sw->dev, &rate, | |
772 | tb_switch_tmu_config_enable); | |
773 | } |