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 | |
14 | static const unsigned int tmu_rates[] = { |
15 | [TB_SWITCH_TMU_MODE_OFF] = 0, |
16 | [TB_SWITCH_TMU_MODE_LOWRES] = 1000, |
17 | [TB_SWITCH_TMU_MODE_HIFI_UNI] = 16, |
18 | [TB_SWITCH_TMU_MODE_HIFI_BI] = 16, |
19 | [TB_SWITCH_TMU_MODE_MEDRES_ENHANCED_UNI] = 16, |
20 | }; |
21 | |
22 | static const struct { |
23 | unsigned int freq_meas_window; |
24 | unsigned int avg_const; |
25 | unsigned int delta_avg_const; |
26 | unsigned int repl_timeout; |
27 | unsigned int repl_threshold; |
28 | unsigned int repl_n; |
29 | unsigned int dirswitch_n; |
30 | } tmu_params[] = { |
31 | [TB_SWITCH_TMU_MODE_OFF] = { }, |
32 | [TB_SWITCH_TMU_MODE_LOWRES] = { .freq_meas_window: 30, .avg_const: 4, }, |
33 | [TB_SWITCH_TMU_MODE_HIFI_UNI] = { .freq_meas_window: 800, .avg_const: 8, }, |
34 | [TB_SWITCH_TMU_MODE_HIFI_BI] = { .freq_meas_window: 800, .avg_const: 8, }, |
35 | [TB_SWITCH_TMU_MODE_MEDRES_ENHANCED_UNI] = { |
36 | .freq_meas_window: 800, .avg_const: 4, .delta_avg_const: 0, .repl_timeout: 3125, .repl_threshold: 25, .repl_n: 128, .dirswitch_n: 255, |
37 | }, |
38 | }; |
39 | |
40 | static const char *tmu_mode_name(enum tb_switch_tmu_mode mode) |
41 | { |
42 | switch (mode) { |
43 | case TB_SWITCH_TMU_MODE_OFF: |
44 | return "off" ; |
45 | case TB_SWITCH_TMU_MODE_LOWRES: |
46 | return "uni-directional, LowRes" ; |
47 | case TB_SWITCH_TMU_MODE_HIFI_UNI: |
48 | return "uni-directional, HiFi" ; |
49 | case TB_SWITCH_TMU_MODE_HIFI_BI: |
50 | return "bi-directional, HiFi" ; |
51 | case TB_SWITCH_TMU_MODE_MEDRES_ENHANCED_UNI: |
52 | return "enhanced uni-directional, MedRes" ; |
53 | default: |
54 | return "unknown" ; |
55 | } |
56 | } |
57 | |
58 | static bool tb_switch_tmu_enhanced_is_supported(const struct tb_switch *sw) |
59 | { |
60 | return usb4_switch_version(sw) > 1; |
61 | } |
62 | |
63 | static int tb_switch_set_tmu_mode_params(struct tb_switch *sw, |
64 | enum tb_switch_tmu_mode mode) |
65 | { |
66 | u32 freq, avg, val; |
67 | int ret; |
68 | |
69 | freq = tmu_params[mode].freq_meas_window; |
70 | avg = tmu_params[mode].avg_const; |
71 | |
72 | ret = tb_sw_read(sw, buffer: &val, space: TB_CFG_SWITCH, |
73 | offset: sw->tmu.cap + TMU_RTR_CS_0, length: 1); |
74 | if (ret) |
75 | return ret; |
76 | |
77 | val &= ~TMU_RTR_CS_0_FREQ_WIND_MASK; |
78 | val |= FIELD_PREP(TMU_RTR_CS_0_FREQ_WIND_MASK, freq); |
79 | |
80 | ret = tb_sw_write(sw, buffer: &val, space: TB_CFG_SWITCH, |
81 | offset: sw->tmu.cap + TMU_RTR_CS_0, length: 1); |
82 | if (ret) |
83 | return ret; |
84 | |
85 | ret = tb_sw_read(sw, buffer: &val, space: TB_CFG_SWITCH, |
86 | offset: sw->tmu.cap + TMU_RTR_CS_15, length: 1); |
87 | if (ret) |
88 | return ret; |
89 | |
90 | val &= ~TMU_RTR_CS_15_FREQ_AVG_MASK & |
91 | ~TMU_RTR_CS_15_DELAY_AVG_MASK & |
92 | ~TMU_RTR_CS_15_OFFSET_AVG_MASK & |
93 | ~TMU_RTR_CS_15_ERROR_AVG_MASK; |
94 | val |= FIELD_PREP(TMU_RTR_CS_15_FREQ_AVG_MASK, avg) | |
95 | FIELD_PREP(TMU_RTR_CS_15_DELAY_AVG_MASK, avg) | |
96 | FIELD_PREP(TMU_RTR_CS_15_OFFSET_AVG_MASK, avg) | |
97 | FIELD_PREP(TMU_RTR_CS_15_ERROR_AVG_MASK, avg); |
98 | |
99 | ret = tb_sw_write(sw, buffer: &val, space: TB_CFG_SWITCH, |
100 | offset: sw->tmu.cap + TMU_RTR_CS_15, length: 1); |
101 | if (ret) |
102 | return ret; |
103 | |
104 | if (tb_switch_tmu_enhanced_is_supported(sw)) { |
105 | u32 delta_avg = tmu_params[mode].delta_avg_const; |
106 | |
107 | ret = tb_sw_read(sw, buffer: &val, space: TB_CFG_SWITCH, |
108 | offset: sw->tmu.cap + TMU_RTR_CS_18, length: 1); |
109 | if (ret) |
110 | return ret; |
111 | |
112 | val &= ~TMU_RTR_CS_18_DELTA_AVG_CONST_MASK; |
113 | val |= FIELD_PREP(TMU_RTR_CS_18_DELTA_AVG_CONST_MASK, delta_avg); |
114 | |
115 | ret = tb_sw_write(sw, buffer: &val, space: TB_CFG_SWITCH, |
116 | offset: sw->tmu.cap + TMU_RTR_CS_18, length: 1); |
117 | } |
118 | |
119 | return ret; |
120 | } |
121 | |
122 | static bool tb_switch_tmu_ucap_is_supported(struct tb_switch *sw) |
123 | { |
124 | int ret; |
125 | u32 val; |
126 | |
127 | ret = tb_sw_read(sw, buffer: &val, space: TB_CFG_SWITCH, |
128 | offset: sw->tmu.cap + TMU_RTR_CS_0, length: 1); |
129 | if (ret) |
130 | return false; |
131 | |
132 | return !!(val & TMU_RTR_CS_0_UCAP); |
133 | } |
134 | |
135 | static int tb_switch_tmu_rate_read(struct tb_switch *sw) |
136 | { |
137 | int ret; |
138 | u32 val; |
139 | |
140 | ret = tb_sw_read(sw, buffer: &val, space: TB_CFG_SWITCH, |
141 | offset: sw->tmu.cap + TMU_RTR_CS_3, length: 1); |
142 | if (ret) |
143 | return ret; |
144 | |
145 | val >>= TMU_RTR_CS_3_TS_PACKET_INTERVAL_SHIFT; |
146 | return val; |
147 | } |
148 | |
149 | static int tb_switch_tmu_rate_write(struct tb_switch *sw, int rate) |
150 | { |
151 | int ret; |
152 | u32 val; |
153 | |
154 | ret = tb_sw_read(sw, buffer: &val, space: TB_CFG_SWITCH, |
155 | offset: sw->tmu.cap + TMU_RTR_CS_3, length: 1); |
156 | if (ret) |
157 | return ret; |
158 | |
159 | val &= ~TMU_RTR_CS_3_TS_PACKET_INTERVAL_MASK; |
160 | val |= rate << TMU_RTR_CS_3_TS_PACKET_INTERVAL_SHIFT; |
161 | |
162 | return tb_sw_write(sw, buffer: &val, space: TB_CFG_SWITCH, |
163 | offset: sw->tmu.cap + TMU_RTR_CS_3, length: 1); |
164 | } |
165 | |
166 | static int tb_port_tmu_write(struct tb_port *port, u8 offset, u32 mask, |
167 | u32 value) |
168 | { |
169 | u32 data; |
170 | int ret; |
171 | |
172 | ret = tb_port_read(port, buffer: &data, space: TB_CFG_PORT, offset: port->cap_tmu + offset, length: 1); |
173 | if (ret) |
174 | return ret; |
175 | |
176 | data &= ~mask; |
177 | data |= value; |
178 | |
179 | return tb_port_write(port, buffer: &data, space: TB_CFG_PORT, |
180 | offset: port->cap_tmu + offset, length: 1); |
181 | } |
182 | |
183 | static int tb_port_tmu_set_unidirectional(struct tb_port *port, |
184 | bool unidirectional) |
185 | { |
186 | u32 val; |
187 | |
188 | if (!port->sw->tmu.has_ucap) |
189 | return 0; |
190 | |
191 | val = unidirectional ? TMU_ADP_CS_3_UDM : 0; |
192 | return tb_port_tmu_write(port, TMU_ADP_CS_3, TMU_ADP_CS_3_UDM, value: val); |
193 | } |
194 | |
195 | static inline int tb_port_tmu_unidirectional_disable(struct tb_port *port) |
196 | { |
197 | return tb_port_tmu_set_unidirectional(port, unidirectional: false); |
198 | } |
199 | |
200 | static inline int tb_port_tmu_unidirectional_enable(struct tb_port *port) |
201 | { |
202 | return tb_port_tmu_set_unidirectional(port, unidirectional: true); |
203 | } |
204 | |
205 | static bool tb_port_tmu_is_unidirectional(struct tb_port *port) |
206 | { |
207 | int ret; |
208 | u32 val; |
209 | |
210 | ret = tb_port_read(port, buffer: &val, space: TB_CFG_PORT, |
211 | offset: port->cap_tmu + TMU_ADP_CS_3, length: 1); |
212 | if (ret) |
213 | return false; |
214 | |
215 | return val & TMU_ADP_CS_3_UDM; |
216 | } |
217 | |
218 | static bool tb_port_tmu_is_enhanced(struct tb_port *port) |
219 | { |
220 | int ret; |
221 | u32 val; |
222 | |
223 | ret = tb_port_read(port, buffer: &val, space: TB_CFG_PORT, |
224 | offset: port->cap_tmu + TMU_ADP_CS_8, length: 1); |
225 | if (ret) |
226 | return false; |
227 | |
228 | return val & TMU_ADP_CS_8_EUDM; |
229 | } |
230 | |
231 | /* Can be called to non-v2 lane adapters too */ |
232 | static int tb_port_tmu_enhanced_enable(struct tb_port *port, bool enable) |
233 | { |
234 | int ret; |
235 | u32 val; |
236 | |
237 | if (!tb_switch_tmu_enhanced_is_supported(sw: port->sw)) |
238 | return 0; |
239 | |
240 | ret = tb_port_read(port, buffer: &val, space: TB_CFG_PORT, |
241 | offset: port->cap_tmu + TMU_ADP_CS_8, length: 1); |
242 | if (ret) |
243 | return ret; |
244 | |
245 | if (enable) |
246 | val |= TMU_ADP_CS_8_EUDM; |
247 | else |
248 | val &= ~TMU_ADP_CS_8_EUDM; |
249 | |
250 | return tb_port_write(port, buffer: &val, space: TB_CFG_PORT, |
251 | offset: port->cap_tmu + TMU_ADP_CS_8, length: 1); |
252 | } |
253 | |
254 | static int tb_port_set_tmu_mode_params(struct tb_port *port, |
255 | enum tb_switch_tmu_mode mode) |
256 | { |
257 | u32 repl_timeout, repl_threshold, repl_n, dirswitch_n, val; |
258 | int ret; |
259 | |
260 | repl_timeout = tmu_params[mode].repl_timeout; |
261 | repl_threshold = tmu_params[mode].repl_threshold; |
262 | repl_n = tmu_params[mode].repl_n; |
263 | dirswitch_n = tmu_params[mode].dirswitch_n; |
264 | |
265 | ret = tb_port_read(port, buffer: &val, space: TB_CFG_PORT, |
266 | offset: port->cap_tmu + TMU_ADP_CS_8, length: 1); |
267 | if (ret) |
268 | return ret; |
269 | |
270 | val &= ~TMU_ADP_CS_8_REPL_TIMEOUT_MASK; |
271 | val &= ~TMU_ADP_CS_8_REPL_THRESHOLD_MASK; |
272 | val |= FIELD_PREP(TMU_ADP_CS_8_REPL_TIMEOUT_MASK, repl_timeout); |
273 | val |= FIELD_PREP(TMU_ADP_CS_8_REPL_THRESHOLD_MASK, repl_threshold); |
274 | |
275 | ret = tb_port_write(port, buffer: &val, space: TB_CFG_PORT, |
276 | offset: port->cap_tmu + TMU_ADP_CS_8, length: 1); |
277 | if (ret) |
278 | return ret; |
279 | |
280 | ret = tb_port_read(port, buffer: &val, space: TB_CFG_PORT, |
281 | offset: port->cap_tmu + TMU_ADP_CS_9, length: 1); |
282 | if (ret) |
283 | return ret; |
284 | |
285 | val &= ~TMU_ADP_CS_9_REPL_N_MASK; |
286 | val &= ~TMU_ADP_CS_9_DIRSWITCH_N_MASK; |
287 | val |= FIELD_PREP(TMU_ADP_CS_9_REPL_N_MASK, repl_n); |
288 | val |= FIELD_PREP(TMU_ADP_CS_9_DIRSWITCH_N_MASK, dirswitch_n); |
289 | |
290 | return tb_port_write(port, buffer: &val, space: TB_CFG_PORT, |
291 | offset: port->cap_tmu + TMU_ADP_CS_9, length: 1); |
292 | } |
293 | |
294 | /* Can be called to non-v2 lane adapters too */ |
295 | static int tb_port_tmu_rate_write(struct tb_port *port, int rate) |
296 | { |
297 | int ret; |
298 | u32 val; |
299 | |
300 | if (!tb_switch_tmu_enhanced_is_supported(sw: port->sw)) |
301 | return 0; |
302 | |
303 | ret = tb_port_read(port, buffer: &val, space: TB_CFG_PORT, |
304 | offset: port->cap_tmu + TMU_ADP_CS_9, length: 1); |
305 | if (ret) |
306 | return ret; |
307 | |
308 | val &= ~TMU_ADP_CS_9_ADP_TS_INTERVAL_MASK; |
309 | val |= FIELD_PREP(TMU_ADP_CS_9_ADP_TS_INTERVAL_MASK, rate); |
310 | |
311 | return tb_port_write(port, buffer: &val, space: TB_CFG_PORT, |
312 | offset: port->cap_tmu + TMU_ADP_CS_9, length: 1); |
313 | } |
314 | |
315 | static int tb_port_tmu_time_sync(struct tb_port *port, bool time_sync) |
316 | { |
317 | u32 val = time_sync ? TMU_ADP_CS_6_DTS : 0; |
318 | |
319 | return tb_port_tmu_write(port, TMU_ADP_CS_6, TMU_ADP_CS_6_DTS, value: val); |
320 | } |
321 | |
322 | static int tb_port_tmu_time_sync_disable(struct tb_port *port) |
323 | { |
324 | return tb_port_tmu_time_sync(port, time_sync: true); |
325 | } |
326 | |
327 | static int tb_port_tmu_time_sync_enable(struct tb_port *port) |
328 | { |
329 | return tb_port_tmu_time_sync(port, time_sync: false); |
330 | } |
331 | |
332 | static int tb_switch_tmu_set_time_disruption(struct tb_switch *sw, bool set) |
333 | { |
334 | u32 val, offset, bit; |
335 | int ret; |
336 | |
337 | if (tb_switch_is_usb4(sw)) { |
338 | offset = sw->tmu.cap + TMU_RTR_CS_0; |
339 | bit = TMU_RTR_CS_0_TD; |
340 | } else { |
341 | offset = sw->cap_vsec_tmu + TB_TIME_VSEC_3_CS_26; |
342 | bit = TB_TIME_VSEC_3_CS_26_TD; |
343 | } |
344 | |
345 | ret = tb_sw_read(sw, buffer: &val, space: TB_CFG_SWITCH, offset, length: 1); |
346 | if (ret) |
347 | return ret; |
348 | |
349 | if (set) |
350 | val |= bit; |
351 | else |
352 | val &= ~bit; |
353 | |
354 | return tb_sw_write(sw, buffer: &val, space: TB_CFG_SWITCH, offset, length: 1); |
355 | } |
356 | |
357 | static int tmu_mode_init(struct tb_switch *sw) |
358 | { |
359 | bool enhanced, ucap; |
360 | int ret, rate; |
361 | |
362 | ucap = tb_switch_tmu_ucap_is_supported(sw); |
363 | if (ucap) |
364 | tb_sw_dbg(sw, "TMU: supports uni-directional mode\n" ); |
365 | enhanced = tb_switch_tmu_enhanced_is_supported(sw); |
366 | if (enhanced) |
367 | tb_sw_dbg(sw, "TMU: supports enhanced uni-directional mode\n" ); |
368 | |
369 | ret = tb_switch_tmu_rate_read(sw); |
370 | if (ret < 0) |
371 | return ret; |
372 | rate = ret; |
373 | |
374 | /* Off by default */ |
375 | sw->tmu.mode = TB_SWITCH_TMU_MODE_OFF; |
376 | |
377 | if (tb_route(sw)) { |
378 | struct tb_port *up = tb_upstream_port(sw); |
379 | |
380 | if (enhanced && tb_port_tmu_is_enhanced(port: up)) { |
381 | sw->tmu.mode = TB_SWITCH_TMU_MODE_MEDRES_ENHANCED_UNI; |
382 | } else if (ucap && tb_port_tmu_is_unidirectional(port: up)) { |
383 | if (tmu_rates[TB_SWITCH_TMU_MODE_LOWRES] == rate) |
384 | sw->tmu.mode = TB_SWITCH_TMU_MODE_LOWRES; |
385 | else if (tmu_rates[TB_SWITCH_TMU_MODE_HIFI_UNI] == rate) |
386 | sw->tmu.mode = TB_SWITCH_TMU_MODE_HIFI_UNI; |
387 | } else if (rate) { |
388 | sw->tmu.mode = TB_SWITCH_TMU_MODE_HIFI_BI; |
389 | } |
390 | } else if (rate) { |
391 | sw->tmu.mode = TB_SWITCH_TMU_MODE_HIFI_BI; |
392 | } |
393 | |
394 | /* Update the initial request to match the current mode */ |
395 | sw->tmu.mode_request = sw->tmu.mode; |
396 | sw->tmu.has_ucap = ucap; |
397 | |
398 | return 0; |
399 | } |
400 | |
401 | /** |
402 | * tb_switch_tmu_init() - Initialize switch TMU structures |
403 | * @sw: Switch to initialized |
404 | * |
405 | * This function must be called before other TMU related functions to |
406 | * makes the internal structures are filled in correctly. Does not |
407 | * change any hardware configuration. |
408 | */ |
409 | int tb_switch_tmu_init(struct tb_switch *sw) |
410 | { |
411 | struct tb_port *port; |
412 | int ret; |
413 | |
414 | if (tb_switch_is_icm(sw)) |
415 | return 0; |
416 | |
417 | ret = tb_switch_find_cap(sw, cap: TB_SWITCH_CAP_TMU); |
418 | if (ret > 0) |
419 | sw->tmu.cap = ret; |
420 | |
421 | tb_switch_for_each_port(sw, port) { |
422 | int cap; |
423 | |
424 | cap = tb_port_find_cap(port, cap: TB_PORT_CAP_TIME1); |
425 | if (cap > 0) |
426 | port->cap_tmu = cap; |
427 | } |
428 | |
429 | ret = tmu_mode_init(sw); |
430 | if (ret) |
431 | return ret; |
432 | |
433 | tb_sw_dbg(sw, "TMU: current mode: %s\n" , tmu_mode_name(sw->tmu.mode)); |
434 | return 0; |
435 | } |
436 | |
437 | /** |
438 | * tb_switch_tmu_post_time() - Update switch local time |
439 | * @sw: Switch whose time to update |
440 | * |
441 | * Updates switch local time using time posting procedure. |
442 | */ |
443 | int tb_switch_tmu_post_time(struct tb_switch *sw) |
444 | { |
445 | unsigned int post_time_high_offset, post_time_high = 0; |
446 | unsigned int post_local_time_offset, post_time_offset; |
447 | struct tb_switch *root_switch = sw->tb->root_switch; |
448 | u64 hi, mid, lo, local_time, post_time; |
449 | int i, ret, retries = 100; |
450 | u32 gm_local_time[3]; |
451 | |
452 | if (!tb_route(sw)) |
453 | return 0; |
454 | |
455 | if (!tb_switch_is_usb4(sw)) |
456 | return 0; |
457 | |
458 | /* Need to be able to read the grand master time */ |
459 | if (!root_switch->tmu.cap) |
460 | return 0; |
461 | |
462 | ret = tb_sw_read(sw: root_switch, buffer: gm_local_time, space: TB_CFG_SWITCH, |
463 | offset: root_switch->tmu.cap + TMU_RTR_CS_1, |
464 | ARRAY_SIZE(gm_local_time)); |
465 | if (ret) |
466 | return ret; |
467 | |
468 | for (i = 0; i < ARRAY_SIZE(gm_local_time); i++) |
469 | tb_sw_dbg(root_switch, "TMU: local_time[%d]=0x%08x\n" , i, |
470 | gm_local_time[i]); |
471 | |
472 | /* Convert to nanoseconds (drop fractional part) */ |
473 | hi = gm_local_time[2] & TMU_RTR_CS_3_LOCAL_TIME_NS_MASK; |
474 | mid = gm_local_time[1]; |
475 | lo = (gm_local_time[0] & TMU_RTR_CS_1_LOCAL_TIME_NS_MASK) >> |
476 | TMU_RTR_CS_1_LOCAL_TIME_NS_SHIFT; |
477 | local_time = hi << 48 | mid << 16 | lo; |
478 | |
479 | /* Tell the switch that time sync is disrupted for a while */ |
480 | ret = tb_switch_tmu_set_time_disruption(sw, set: true); |
481 | if (ret) |
482 | return ret; |
483 | |
484 | post_local_time_offset = sw->tmu.cap + TMU_RTR_CS_22; |
485 | post_time_offset = sw->tmu.cap + TMU_RTR_CS_24; |
486 | post_time_high_offset = sw->tmu.cap + TMU_RTR_CS_25; |
487 | |
488 | /* |
489 | * Write the Grandmaster time to the Post Local Time registers |
490 | * of the new switch. |
491 | */ |
492 | ret = tb_sw_write(sw, buffer: &local_time, space: TB_CFG_SWITCH, |
493 | offset: post_local_time_offset, length: 2); |
494 | if (ret) |
495 | goto out; |
496 | |
497 | /* |
498 | * Have the new switch update its local time by: |
499 | * 1) writing 0x1 to the Post Time Low register and 0xffffffff to |
500 | * Post Time High register. |
501 | * 2) write 0 to Post Time High register and then wait for |
502 | * the completion of the post_time register becomes 0. |
503 | * This means the time has been converged properly. |
504 | */ |
505 | post_time = 0xffffffff00000001ULL; |
506 | |
507 | ret = tb_sw_write(sw, buffer: &post_time, space: TB_CFG_SWITCH, offset: post_time_offset, length: 2); |
508 | if (ret) |
509 | goto out; |
510 | |
511 | ret = tb_sw_write(sw, buffer: &post_time_high, space: TB_CFG_SWITCH, |
512 | offset: post_time_high_offset, length: 1); |
513 | if (ret) |
514 | goto out; |
515 | |
516 | do { |
517 | usleep_range(min: 5, max: 10); |
518 | ret = tb_sw_read(sw, buffer: &post_time, space: TB_CFG_SWITCH, |
519 | offset: post_time_offset, length: 2); |
520 | if (ret) |
521 | goto out; |
522 | } while (--retries && post_time); |
523 | |
524 | if (!retries) { |
525 | ret = -ETIMEDOUT; |
526 | goto out; |
527 | } |
528 | |
529 | tb_sw_dbg(sw, "TMU: updated local time to %#llx\n" , local_time); |
530 | |
531 | out: |
532 | tb_switch_tmu_set_time_disruption(sw, set: false); |
533 | return ret; |
534 | } |
535 | |
536 | static int disable_enhanced(struct tb_port *up, struct tb_port *down) |
537 | { |
538 | int ret; |
539 | |
540 | /* |
541 | * Router may already been disconnected so ignore errors on the |
542 | * upstream port. |
543 | */ |
544 | tb_port_tmu_rate_write(port: up, rate: 0); |
545 | tb_port_tmu_enhanced_enable(port: up, enable: false); |
546 | |
547 | ret = tb_port_tmu_rate_write(port: down, rate: 0); |
548 | if (ret) |
549 | return ret; |
550 | return tb_port_tmu_enhanced_enable(port: down, enable: false); |
551 | } |
552 | |
553 | /** |
554 | * tb_switch_tmu_disable() - Disable TMU of a switch |
555 | * @sw: Switch whose TMU to disable |
556 | * |
557 | * Turns off TMU of @sw if it is enabled. If not enabled does nothing. |
558 | */ |
559 | int tb_switch_tmu_disable(struct tb_switch *sw) |
560 | { |
561 | /* Already disabled? */ |
562 | if (sw->tmu.mode == TB_SWITCH_TMU_MODE_OFF) |
563 | return 0; |
564 | |
565 | if (tb_route(sw)) { |
566 | struct tb_port *down, *up; |
567 | int ret; |
568 | |
569 | down = tb_switch_downstream_port(sw); |
570 | up = tb_upstream_port(sw); |
571 | /* |
572 | * In case of uni-directional time sync, TMU handshake is |
573 | * initiated by upstream router. In case of bi-directional |
574 | * time sync, TMU handshake is initiated by downstream router. |
575 | * We change downstream router's rate to off for both uni/bidir |
576 | * cases although it is needed only for the bi-directional mode. |
577 | * We avoid changing upstream router's mode since it might |
578 | * have another downstream router plugged, that is set to |
579 | * uni-directional mode and we don't want to change it's TMU |
580 | * mode. |
581 | */ |
582 | ret = tb_switch_tmu_rate_write(sw, rate: tmu_rates[TB_SWITCH_TMU_MODE_OFF]); |
583 | if (ret) |
584 | return ret; |
585 | |
586 | tb_port_tmu_time_sync_disable(port: up); |
587 | ret = tb_port_tmu_time_sync_disable(port: down); |
588 | if (ret) |
589 | return ret; |
590 | |
591 | switch (sw->tmu.mode) { |
592 | case TB_SWITCH_TMU_MODE_LOWRES: |
593 | case TB_SWITCH_TMU_MODE_HIFI_UNI: |
594 | /* The switch may be unplugged so ignore any errors */ |
595 | tb_port_tmu_unidirectional_disable(port: up); |
596 | ret = tb_port_tmu_unidirectional_disable(port: down); |
597 | if (ret) |
598 | return ret; |
599 | break; |
600 | |
601 | case TB_SWITCH_TMU_MODE_MEDRES_ENHANCED_UNI: |
602 | ret = disable_enhanced(up, down); |
603 | if (ret) |
604 | return ret; |
605 | break; |
606 | |
607 | default: |
608 | break; |
609 | } |
610 | } else { |
611 | tb_switch_tmu_rate_write(sw, rate: tmu_rates[TB_SWITCH_TMU_MODE_OFF]); |
612 | } |
613 | |
614 | sw->tmu.mode = TB_SWITCH_TMU_MODE_OFF; |
615 | |
616 | tb_sw_dbg(sw, "TMU: disabled\n" ); |
617 | return 0; |
618 | } |
619 | |
620 | /* Called only when there is failure enabling requested mode */ |
621 | static void tb_switch_tmu_off(struct tb_switch *sw) |
622 | { |
623 | unsigned int rate = tmu_rates[TB_SWITCH_TMU_MODE_OFF]; |
624 | struct tb_port *down, *up; |
625 | |
626 | down = tb_switch_downstream_port(sw); |
627 | up = tb_upstream_port(sw); |
628 | /* |
629 | * In case of any failure in one of the steps when setting |
630 | * bi-directional or uni-directional TMU mode, get back to the TMU |
631 | * configurations in off mode. In case of additional failures in |
632 | * the functions below, ignore them since the caller shall already |
633 | * report a failure. |
634 | */ |
635 | tb_port_tmu_time_sync_disable(port: down); |
636 | tb_port_tmu_time_sync_disable(port: up); |
637 | |
638 | switch (sw->tmu.mode_request) { |
639 | case TB_SWITCH_TMU_MODE_LOWRES: |
640 | case TB_SWITCH_TMU_MODE_HIFI_UNI: |
641 | tb_switch_tmu_rate_write(sw: tb_switch_parent(sw), rate); |
642 | break; |
643 | case TB_SWITCH_TMU_MODE_MEDRES_ENHANCED_UNI: |
644 | disable_enhanced(up, down); |
645 | break; |
646 | default: |
647 | break; |
648 | } |
649 | |
650 | /* Always set the rate to 0 */ |
651 | tb_switch_tmu_rate_write(sw, rate); |
652 | |
653 | tb_switch_set_tmu_mode_params(sw, mode: sw->tmu.mode); |
654 | tb_port_tmu_unidirectional_disable(port: down); |
655 | tb_port_tmu_unidirectional_disable(port: up); |
656 | } |
657 | |
658 | /* |
659 | * This function is called when the previous TMU mode was |
660 | * TB_SWITCH_TMU_MODE_OFF. |
661 | */ |
662 | static int tb_switch_tmu_enable_bidirectional(struct tb_switch *sw) |
663 | { |
664 | struct tb_port *up, *down; |
665 | int ret; |
666 | |
667 | up = tb_upstream_port(sw); |
668 | down = tb_switch_downstream_port(sw); |
669 | |
670 | ret = tb_port_tmu_unidirectional_disable(port: up); |
671 | if (ret) |
672 | return ret; |
673 | |
674 | ret = tb_port_tmu_unidirectional_disable(port: down); |
675 | if (ret) |
676 | goto out; |
677 | |
678 | ret = tb_switch_tmu_rate_write(sw, rate: tmu_rates[TB_SWITCH_TMU_MODE_HIFI_BI]); |
679 | if (ret) |
680 | goto out; |
681 | |
682 | ret = tb_port_tmu_time_sync_enable(port: up); |
683 | if (ret) |
684 | goto out; |
685 | |
686 | ret = tb_port_tmu_time_sync_enable(port: down); |
687 | if (ret) |
688 | goto out; |
689 | |
690 | return 0; |
691 | |
692 | out: |
693 | tb_switch_tmu_off(sw); |
694 | return ret; |
695 | } |
696 | |
697 | /* Only needed for Titan Ridge */ |
698 | static int tb_switch_tmu_disable_objections(struct tb_switch *sw) |
699 | { |
700 | struct tb_port *up = tb_upstream_port(sw); |
701 | u32 val; |
702 | int ret; |
703 | |
704 | ret = tb_sw_read(sw, buffer: &val, space: TB_CFG_SWITCH, |
705 | offset: sw->cap_vsec_tmu + TB_TIME_VSEC_3_CS_9, length: 1); |
706 | if (ret) |
707 | return ret; |
708 | |
709 | val &= ~TB_TIME_VSEC_3_CS_9_TMU_OBJ_MASK; |
710 | |
711 | ret = tb_sw_write(sw, buffer: &val, space: TB_CFG_SWITCH, |
712 | offset: sw->cap_vsec_tmu + TB_TIME_VSEC_3_CS_9, length: 1); |
713 | if (ret) |
714 | return ret; |
715 | |
716 | return tb_port_tmu_write(port: up, TMU_ADP_CS_6, |
717 | TMU_ADP_CS_6_DISABLE_TMU_OBJ_MASK, |
718 | TMU_ADP_CS_6_DISABLE_TMU_OBJ_CL1 | |
719 | TMU_ADP_CS_6_DISABLE_TMU_OBJ_CL2); |
720 | } |
721 | |
722 | /* |
723 | * This function is called when the previous TMU mode was |
724 | * TB_SWITCH_TMU_MODE_OFF. |
725 | */ |
726 | static int tb_switch_tmu_enable_unidirectional(struct tb_switch *sw) |
727 | { |
728 | struct tb_port *up, *down; |
729 | int ret; |
730 | |
731 | up = tb_upstream_port(sw); |
732 | down = tb_switch_downstream_port(sw); |
733 | ret = tb_switch_tmu_rate_write(sw: tb_switch_parent(sw), |
734 | rate: tmu_rates[sw->tmu.mode_request]); |
735 | if (ret) |
736 | return ret; |
737 | |
738 | ret = tb_switch_set_tmu_mode_params(sw, mode: sw->tmu.mode_request); |
739 | if (ret) |
740 | return ret; |
741 | |
742 | ret = tb_port_tmu_unidirectional_enable(port: up); |
743 | if (ret) |
744 | goto out; |
745 | |
746 | ret = tb_port_tmu_time_sync_enable(port: up); |
747 | if (ret) |
748 | goto out; |
749 | |
750 | ret = tb_port_tmu_unidirectional_enable(port: down); |
751 | if (ret) |
752 | goto out; |
753 | |
754 | ret = tb_port_tmu_time_sync_enable(port: down); |
755 | if (ret) |
756 | goto out; |
757 | |
758 | return 0; |
759 | |
760 | out: |
761 | tb_switch_tmu_off(sw); |
762 | return ret; |
763 | } |
764 | |
765 | /* |
766 | * This function is called when the previous TMU mode was |
767 | * TB_SWITCH_TMU_RATE_OFF. |
768 | */ |
769 | static int tb_switch_tmu_enable_enhanced(struct tb_switch *sw) |
770 | { |
771 | unsigned int rate = tmu_rates[sw->tmu.mode_request]; |
772 | struct tb_port *up, *down; |
773 | int ret; |
774 | |
775 | /* Router specific parameters first */ |
776 | ret = tb_switch_set_tmu_mode_params(sw, mode: sw->tmu.mode_request); |
777 | if (ret) |
778 | return ret; |
779 | |
780 | up = tb_upstream_port(sw); |
781 | down = tb_switch_downstream_port(sw); |
782 | |
783 | ret = tb_port_set_tmu_mode_params(port: up, mode: sw->tmu.mode_request); |
784 | if (ret) |
785 | goto out; |
786 | |
787 | ret = tb_port_tmu_rate_write(port: up, rate); |
788 | if (ret) |
789 | goto out; |
790 | |
791 | ret = tb_port_tmu_enhanced_enable(port: up, enable: true); |
792 | if (ret) |
793 | goto out; |
794 | |
795 | ret = tb_port_set_tmu_mode_params(port: down, mode: sw->tmu.mode_request); |
796 | if (ret) |
797 | goto out; |
798 | |
799 | ret = tb_port_tmu_rate_write(port: down, rate); |
800 | if (ret) |
801 | goto out; |
802 | |
803 | ret = tb_port_tmu_enhanced_enable(port: down, enable: true); |
804 | if (ret) |
805 | goto out; |
806 | |
807 | return 0; |
808 | |
809 | out: |
810 | tb_switch_tmu_off(sw); |
811 | return ret; |
812 | } |
813 | |
814 | static void tb_switch_tmu_change_mode_prev(struct tb_switch *sw) |
815 | { |
816 | unsigned int rate = tmu_rates[sw->tmu.mode]; |
817 | struct tb_port *down, *up; |
818 | |
819 | down = tb_switch_downstream_port(sw); |
820 | up = tb_upstream_port(sw); |
821 | /* |
822 | * In case of any failure in one of the steps when change mode, |
823 | * get back to the TMU configurations in previous mode. |
824 | * In case of additional failures in the functions below, |
825 | * ignore them since the caller shall already report a failure. |
826 | */ |
827 | switch (sw->tmu.mode) { |
828 | case TB_SWITCH_TMU_MODE_LOWRES: |
829 | case TB_SWITCH_TMU_MODE_HIFI_UNI: |
830 | tb_port_tmu_set_unidirectional(port: down, unidirectional: true); |
831 | tb_switch_tmu_rate_write(sw: tb_switch_parent(sw), rate); |
832 | break; |
833 | |
834 | case TB_SWITCH_TMU_MODE_HIFI_BI: |
835 | tb_port_tmu_set_unidirectional(port: down, unidirectional: false); |
836 | tb_switch_tmu_rate_write(sw, rate); |
837 | break; |
838 | |
839 | default: |
840 | break; |
841 | } |
842 | |
843 | tb_switch_set_tmu_mode_params(sw, mode: sw->tmu.mode); |
844 | |
845 | switch (sw->tmu.mode) { |
846 | case TB_SWITCH_TMU_MODE_LOWRES: |
847 | case TB_SWITCH_TMU_MODE_HIFI_UNI: |
848 | tb_port_tmu_set_unidirectional(port: up, unidirectional: true); |
849 | break; |
850 | |
851 | case TB_SWITCH_TMU_MODE_HIFI_BI: |
852 | tb_port_tmu_set_unidirectional(port: up, unidirectional: false); |
853 | break; |
854 | |
855 | default: |
856 | break; |
857 | } |
858 | } |
859 | |
860 | static int tb_switch_tmu_change_mode(struct tb_switch *sw) |
861 | { |
862 | unsigned int rate = tmu_rates[sw->tmu.mode_request]; |
863 | struct tb_port *up, *down; |
864 | int ret; |
865 | |
866 | up = tb_upstream_port(sw); |
867 | down = tb_switch_downstream_port(sw); |
868 | |
869 | /* Program the upstream router downstream facing lane adapter */ |
870 | switch (sw->tmu.mode_request) { |
871 | case TB_SWITCH_TMU_MODE_LOWRES: |
872 | case TB_SWITCH_TMU_MODE_HIFI_UNI: |
873 | ret = tb_port_tmu_set_unidirectional(port: down, unidirectional: true); |
874 | if (ret) |
875 | goto out; |
876 | ret = tb_switch_tmu_rate_write(sw: tb_switch_parent(sw), rate); |
877 | if (ret) |
878 | goto out; |
879 | break; |
880 | |
881 | case TB_SWITCH_TMU_MODE_HIFI_BI: |
882 | ret = tb_port_tmu_set_unidirectional(port: down, unidirectional: false); |
883 | if (ret) |
884 | goto out; |
885 | ret = tb_switch_tmu_rate_write(sw, rate); |
886 | if (ret) |
887 | goto out; |
888 | break; |
889 | |
890 | default: |
891 | /* Not allowed to change modes from other than above */ |
892 | return -EINVAL; |
893 | } |
894 | |
895 | ret = tb_switch_set_tmu_mode_params(sw, mode: sw->tmu.mode_request); |
896 | if (ret) |
897 | goto out; |
898 | |
899 | /* Program the new mode and the downstream router lane adapter */ |
900 | switch (sw->tmu.mode_request) { |
901 | case TB_SWITCH_TMU_MODE_LOWRES: |
902 | case TB_SWITCH_TMU_MODE_HIFI_UNI: |
903 | ret = tb_port_tmu_set_unidirectional(port: up, unidirectional: true); |
904 | if (ret) |
905 | goto out; |
906 | break; |
907 | |
908 | case TB_SWITCH_TMU_MODE_HIFI_BI: |
909 | ret = tb_port_tmu_set_unidirectional(port: up, unidirectional: false); |
910 | if (ret) |
911 | goto out; |
912 | break; |
913 | |
914 | default: |
915 | /* Not allowed to change modes from other than above */ |
916 | return -EINVAL; |
917 | } |
918 | |
919 | ret = tb_port_tmu_time_sync_enable(port: down); |
920 | if (ret) |
921 | goto out; |
922 | |
923 | ret = tb_port_tmu_time_sync_enable(port: up); |
924 | if (ret) |
925 | goto out; |
926 | |
927 | return 0; |
928 | |
929 | out: |
930 | tb_switch_tmu_change_mode_prev(sw); |
931 | return ret; |
932 | } |
933 | |
934 | /** |
935 | * tb_switch_tmu_enable() - Enable TMU on a router |
936 | * @sw: Router whose TMU to enable |
937 | * |
938 | * Enables TMU of a router to be in uni-directional Normal/HiFi or |
939 | * bi-directional HiFi mode. Calling tb_switch_tmu_configure() is |
940 | * required before calling this function. |
941 | */ |
942 | int tb_switch_tmu_enable(struct tb_switch *sw) |
943 | { |
944 | int ret; |
945 | |
946 | if (tb_switch_tmu_is_enabled(sw)) |
947 | return 0; |
948 | |
949 | if (tb_switch_is_titan_ridge(sw) && |
950 | (sw->tmu.mode_request == TB_SWITCH_TMU_MODE_LOWRES || |
951 | sw->tmu.mode_request == TB_SWITCH_TMU_MODE_HIFI_UNI)) { |
952 | ret = tb_switch_tmu_disable_objections(sw); |
953 | if (ret) |
954 | return ret; |
955 | } |
956 | |
957 | ret = tb_switch_tmu_set_time_disruption(sw, set: true); |
958 | if (ret) |
959 | return ret; |
960 | |
961 | if (tb_route(sw)) { |
962 | /* |
963 | * The used mode changes are from OFF to |
964 | * HiFi-Uni/HiFi-BiDir/Normal-Uni or from Normal-Uni to |
965 | * HiFi-Uni. |
966 | */ |
967 | if (sw->tmu.mode == TB_SWITCH_TMU_MODE_OFF) { |
968 | switch (sw->tmu.mode_request) { |
969 | case TB_SWITCH_TMU_MODE_LOWRES: |
970 | case TB_SWITCH_TMU_MODE_HIFI_UNI: |
971 | ret = tb_switch_tmu_enable_unidirectional(sw); |
972 | break; |
973 | |
974 | case TB_SWITCH_TMU_MODE_HIFI_BI: |
975 | ret = tb_switch_tmu_enable_bidirectional(sw); |
976 | break; |
977 | case TB_SWITCH_TMU_MODE_MEDRES_ENHANCED_UNI: |
978 | ret = tb_switch_tmu_enable_enhanced(sw); |
979 | break; |
980 | default: |
981 | ret = -EINVAL; |
982 | break; |
983 | } |
984 | } else if (sw->tmu.mode == TB_SWITCH_TMU_MODE_LOWRES || |
985 | sw->tmu.mode == TB_SWITCH_TMU_MODE_HIFI_UNI || |
986 | sw->tmu.mode == TB_SWITCH_TMU_MODE_HIFI_BI) { |
987 | ret = tb_switch_tmu_change_mode(sw); |
988 | } else { |
989 | ret = -EINVAL; |
990 | } |
991 | } else { |
992 | /* |
993 | * Host router port configurations are written as |
994 | * part of configurations for downstream port of the parent |
995 | * of the child node - see above. |
996 | * Here only the host router' rate configuration is written. |
997 | */ |
998 | ret = tb_switch_tmu_rate_write(sw, rate: tmu_rates[sw->tmu.mode_request]); |
999 | } |
1000 | |
1001 | if (ret) { |
1002 | tb_sw_warn(sw, "TMU: failed to enable mode %s: %d\n" , |
1003 | tmu_mode_name(sw->tmu.mode_request), ret); |
1004 | } else { |
1005 | sw->tmu.mode = sw->tmu.mode_request; |
1006 | tb_sw_dbg(sw, "TMU: mode set to: %s\n" , tmu_mode_name(sw->tmu.mode)); |
1007 | } |
1008 | |
1009 | return tb_switch_tmu_set_time_disruption(sw, set: false); |
1010 | } |
1011 | |
1012 | /** |
1013 | * tb_switch_tmu_configure() - Configure the TMU mode |
1014 | * @sw: Router whose mode to change |
1015 | * @mode: Mode to configure |
1016 | * |
1017 | * Selects the TMU mode that is enabled when tb_switch_tmu_enable() is |
1018 | * next called. |
1019 | * |
1020 | * Returns %0 in success and negative errno otherwise. Specifically |
1021 | * returns %-EOPNOTSUPP if the requested mode is not possible (not |
1022 | * supported by the router and/or topology). |
1023 | */ |
1024 | int tb_switch_tmu_configure(struct tb_switch *sw, enum tb_switch_tmu_mode mode) |
1025 | { |
1026 | switch (mode) { |
1027 | case TB_SWITCH_TMU_MODE_OFF: |
1028 | break; |
1029 | |
1030 | case TB_SWITCH_TMU_MODE_LOWRES: |
1031 | case TB_SWITCH_TMU_MODE_HIFI_UNI: |
1032 | if (!sw->tmu.has_ucap) |
1033 | return -EOPNOTSUPP; |
1034 | break; |
1035 | |
1036 | case TB_SWITCH_TMU_MODE_HIFI_BI: |
1037 | break; |
1038 | |
1039 | case TB_SWITCH_TMU_MODE_MEDRES_ENHANCED_UNI: { |
1040 | const struct tb_switch *parent_sw = tb_switch_parent(sw); |
1041 | |
1042 | if (!parent_sw || !tb_switch_tmu_enhanced_is_supported(sw: parent_sw)) |
1043 | return -EOPNOTSUPP; |
1044 | if (!tb_switch_tmu_enhanced_is_supported(sw)) |
1045 | return -EOPNOTSUPP; |
1046 | |
1047 | break; |
1048 | } |
1049 | |
1050 | default: |
1051 | tb_sw_warn(sw, "TMU: unsupported mode %u\n" , mode); |
1052 | return -EINVAL; |
1053 | } |
1054 | |
1055 | if (sw->tmu.mode_request != mode) { |
1056 | tb_sw_dbg(sw, "TMU: mode change %s -> %s requested\n" , |
1057 | tmu_mode_name(sw->tmu.mode), tmu_mode_name(mode)); |
1058 | sw->tmu.mode_request = mode; |
1059 | } |
1060 | |
1061 | return 0; |
1062 | } |
1063 | |