xref: /openbmc/linux/drivers/thunderbolt/tmu.c (revision 43f977bc)
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 {
337*43f977bcSGil Fine 	/*
338*43f977bcSGil Fine 	 * No need to disable TMU on devices that don't support CLx since
339*43f977bcSGil Fine 	 * on these devices e.g. Alpine Ridge and earlier, the TMU mode
340*43f977bcSGil Fine 	 * HiFi bi-directional is enabled by default and we don't change it.
341*43f977bcSGil Fine 	 */
342*43f977bcSGil 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.
362a28ec0e1SGil Fine 		 * Therefore, we change the rate to off in the respective
363a28ec0e1SGil Fine 		 * router.
364a28ec0e1SGil Fine 		 */
365a28ec0e1SGil Fine 		if (unidirectional)
366a28ec0e1SGil Fine 			tb_switch_tmu_rate_write(parent, TB_SWITCH_TMU_RATE_OFF);
367a28ec0e1SGil Fine 		else
368a28ec0e1SGil Fine 			tb_switch_tmu_rate_write(sw, TB_SWITCH_TMU_RATE_OFF);
369cf29b9afSRajmohan Mani 
370a28ec0e1SGil Fine 		tb_port_tmu_time_sync_disable(up);
371a28ec0e1SGil Fine 		ret = tb_port_tmu_time_sync_disable(down);
372a28ec0e1SGil Fine 		if (ret)
373a28ec0e1SGil Fine 			return ret;
374a28ec0e1SGil Fine 
375a28ec0e1SGil Fine 		if (unidirectional) {
376cf29b9afSRajmohan Mani 			/* The switch may be unplugged so ignore any errors */
377cf29b9afSRajmohan Mani 			tb_port_tmu_unidirectional_disable(up);
378cf29b9afSRajmohan Mani 			ret = tb_port_tmu_unidirectional_disable(down);
379cf29b9afSRajmohan Mani 			if (ret)
380cf29b9afSRajmohan Mani 				return ret;
381cf29b9afSRajmohan Mani 		}
382a28ec0e1SGil Fine 	} else {
383cf29b9afSRajmohan Mani 		tb_switch_tmu_rate_write(sw, TB_SWITCH_TMU_RATE_OFF);
384a28ec0e1SGil Fine 	}
385cf29b9afSRajmohan Mani 
386cf29b9afSRajmohan Mani 	sw->tmu.unidirectional = false;
387cf29b9afSRajmohan Mani 	sw->tmu.rate = TB_SWITCH_TMU_RATE_OFF;
388cf29b9afSRajmohan Mani 
389cf29b9afSRajmohan Mani 	tb_sw_dbg(sw, "TMU: disabled\n");
390cf29b9afSRajmohan Mani 	return 0;
391cf29b9afSRajmohan Mani }
392cf29b9afSRajmohan Mani 
393a28ec0e1SGil Fine static void __tb_switch_tmu_off(struct tb_switch *sw, bool unidirectional)
394cf29b9afSRajmohan Mani {
395a28ec0e1SGil Fine 	struct tb_switch *parent = tb_switch_parent(sw);
396a28ec0e1SGil Fine 	struct tb_port *down, *up;
397a28ec0e1SGil Fine 
398a28ec0e1SGil Fine 	down = tb_port_at(tb_route(sw), parent);
399a28ec0e1SGil Fine 	up = tb_upstream_port(sw);
400a28ec0e1SGil Fine 	/*
401a28ec0e1SGil Fine 	 * In case of any failure in one of the steps when setting
402a28ec0e1SGil Fine 	 * bi-directional or uni-directional TMU mode, get back to the TMU
403a28ec0e1SGil Fine 	 * configurations in off mode. In case of additional failures in
404a28ec0e1SGil Fine 	 * the functions below, ignore them since the caller shall already
405a28ec0e1SGil Fine 	 * report a failure.
406a28ec0e1SGil Fine 	 */
407a28ec0e1SGil Fine 	tb_port_tmu_time_sync_disable(down);
408a28ec0e1SGil Fine 	tb_port_tmu_time_sync_disable(up);
409a28ec0e1SGil Fine 	if (unidirectional)
410a28ec0e1SGil Fine 		tb_switch_tmu_rate_write(parent, TB_SWITCH_TMU_RATE_OFF);
411a28ec0e1SGil Fine 	else
412a28ec0e1SGil Fine 		tb_switch_tmu_rate_write(sw, TB_SWITCH_TMU_RATE_OFF);
413a28ec0e1SGil Fine 
414a28ec0e1SGil Fine 	tb_port_tmu_unidirectional_disable(down);
415a28ec0e1SGil Fine 	tb_port_tmu_unidirectional_disable(up);
416a28ec0e1SGil Fine }
417a28ec0e1SGil Fine 
418a28ec0e1SGil Fine /*
419a28ec0e1SGil Fine  * This function is called when the previous TMU mode was
420a28ec0e1SGil Fine  * TB_SWITCH_TMU_RATE_OFF.
421a28ec0e1SGil Fine  */
422a28ec0e1SGil Fine static int __tb_switch_tmu_enable_bidirectional(struct tb_switch *sw)
423a28ec0e1SGil Fine {
424a28ec0e1SGil Fine 	struct tb_switch *parent = tb_switch_parent(sw);
425a28ec0e1SGil Fine 	struct tb_port *up, *down;
426cf29b9afSRajmohan Mani 	int ret;
427cf29b9afSRajmohan Mani 
428a28ec0e1SGil Fine 	up = tb_upstream_port(sw);
429a28ec0e1SGil Fine 	down = tb_port_at(tb_route(sw), parent);
430a28ec0e1SGil Fine 
431a28ec0e1SGil Fine 	ret = tb_port_tmu_unidirectional_disable(up);
432a28ec0e1SGil Fine 	if (ret)
433a28ec0e1SGil Fine 		return ret;
434a28ec0e1SGil Fine 
435a28ec0e1SGil Fine 	ret = tb_port_tmu_unidirectional_disable(down);
436a28ec0e1SGil Fine 	if (ret)
437a28ec0e1SGil Fine 		goto out;
438a28ec0e1SGil Fine 
439a28ec0e1SGil Fine 	ret = tb_switch_tmu_rate_write(sw, TB_SWITCH_TMU_RATE_HIFI);
440a28ec0e1SGil Fine 	if (ret)
441a28ec0e1SGil Fine 		goto out;
442a28ec0e1SGil Fine 
443a28ec0e1SGil Fine 	ret = tb_port_tmu_time_sync_enable(up);
444a28ec0e1SGil Fine 	if (ret)
445a28ec0e1SGil Fine 		goto out;
446a28ec0e1SGil Fine 
447a28ec0e1SGil Fine 	ret = tb_port_tmu_time_sync_enable(down);
448a28ec0e1SGil Fine 	if (ret)
449a28ec0e1SGil Fine 		goto out;
450a28ec0e1SGil Fine 
451a28ec0e1SGil Fine 	return 0;
452a28ec0e1SGil Fine 
453a28ec0e1SGil Fine out:
454a28ec0e1SGil Fine 	__tb_switch_tmu_off(sw, false);
455a28ec0e1SGil Fine 	return ret;
456a28ec0e1SGil Fine }
457a28ec0e1SGil Fine 
458*43f977bcSGil Fine static int tb_switch_tmu_objection_mask(struct tb_switch *sw)
459*43f977bcSGil Fine {
460*43f977bcSGil Fine 	u32 val;
461*43f977bcSGil Fine 	int ret;
462*43f977bcSGil Fine 
463*43f977bcSGil Fine 	ret = tb_sw_read(sw, &val, TB_CFG_SWITCH,
464*43f977bcSGil Fine 			 sw->cap_vsec_tmu + TB_TIME_VSEC_3_CS_9, 1);
465*43f977bcSGil Fine 	if (ret)
466*43f977bcSGil Fine 		return ret;
467*43f977bcSGil Fine 
468*43f977bcSGil Fine 	val &= ~TB_TIME_VSEC_3_CS_9_TMU_OBJ_MASK;
469*43f977bcSGil Fine 
470*43f977bcSGil Fine 	return tb_sw_write(sw, &val, TB_CFG_SWITCH,
471*43f977bcSGil Fine 			   sw->cap_vsec_tmu + TB_TIME_VSEC_3_CS_9, 1);
472*43f977bcSGil Fine }
473*43f977bcSGil Fine 
474*43f977bcSGil Fine static int tb_switch_tmu_unidirectional_enable(struct tb_switch *sw)
475*43f977bcSGil Fine {
476*43f977bcSGil Fine 	struct tb_port *up = tb_upstream_port(sw);
477*43f977bcSGil Fine 
478*43f977bcSGil Fine 	return tb_port_tmu_write(up, TMU_ADP_CS_6,
479*43f977bcSGil Fine 				 TMU_ADP_CS_6_DISABLE_TMU_OBJ_MASK,
480*43f977bcSGil Fine 				 TMU_ADP_CS_6_DISABLE_TMU_OBJ_MASK);
481*43f977bcSGil Fine }
482*43f977bcSGil Fine 
483a28ec0e1SGil Fine /*
484a28ec0e1SGil Fine  * This function is called when the previous TMU mode was
485a28ec0e1SGil Fine  * TB_SWITCH_TMU_RATE_OFF.
486a28ec0e1SGil Fine  */
487a28ec0e1SGil Fine static int __tb_switch_tmu_enable_unidirectional(struct tb_switch *sw)
488a28ec0e1SGil Fine {
489a28ec0e1SGil Fine 	struct tb_switch *parent = tb_switch_parent(sw);
490a28ec0e1SGil Fine 	struct tb_port *up, *down;
491a28ec0e1SGil Fine 	int ret;
492a28ec0e1SGil Fine 
493a28ec0e1SGil Fine 	up = tb_upstream_port(sw);
494a28ec0e1SGil Fine 	down = tb_port_at(tb_route(sw), parent);
495a28ec0e1SGil Fine 	ret = tb_switch_tmu_rate_write(parent, TB_SWITCH_TMU_RATE_HIFI);
496a28ec0e1SGil Fine 	if (ret)
497a28ec0e1SGil Fine 		return ret;
498a28ec0e1SGil Fine 
499a28ec0e1SGil Fine 	ret = tb_port_tmu_unidirectional_enable(up);
500a28ec0e1SGil Fine 	if (ret)
501a28ec0e1SGil Fine 		goto out;
502a28ec0e1SGil Fine 
503a28ec0e1SGil Fine 	ret = tb_port_tmu_time_sync_enable(up);
504a28ec0e1SGil Fine 	if (ret)
505a28ec0e1SGil Fine 		goto out;
506a28ec0e1SGil Fine 
507a28ec0e1SGil Fine 	ret = tb_port_tmu_unidirectional_enable(down);
508a28ec0e1SGil Fine 	if (ret)
509a28ec0e1SGil Fine 		goto out;
510a28ec0e1SGil Fine 
511a28ec0e1SGil Fine 	ret = tb_port_tmu_time_sync_enable(down);
512a28ec0e1SGil Fine 	if (ret)
513a28ec0e1SGil Fine 		goto out;
514a28ec0e1SGil Fine 
515a28ec0e1SGil Fine 	return 0;
516a28ec0e1SGil Fine 
517a28ec0e1SGil Fine out:
518a28ec0e1SGil Fine 	__tb_switch_tmu_off(sw, true);
519a28ec0e1SGil Fine 	return ret;
520a28ec0e1SGil Fine }
521a28ec0e1SGil Fine 
522a28ec0e1SGil Fine static int tb_switch_tmu_hifi_enable(struct tb_switch *sw)
523a28ec0e1SGil Fine {
524a28ec0e1SGil Fine 	bool unidirectional = sw->tmu.unidirectional_request;
525a28ec0e1SGil Fine 	int ret;
526a28ec0e1SGil Fine 
527a28ec0e1SGil Fine 	if (unidirectional && !sw->tmu.has_ucap)
528a28ec0e1SGil Fine 		return -EOPNOTSUPP;
529a28ec0e1SGil Fine 
530*43f977bcSGil Fine 	/*
531*43f977bcSGil Fine 	 * No need to enable TMU on devices that don't support CLx since on
532*43f977bcSGil Fine 	 * these devices e.g. Alpine Ridge and earlier, the TMU mode HiFi
533*43f977bcSGil Fine 	 * bi-directional is enabled by default.
534*43f977bcSGil Fine 	 */
535*43f977bcSGil Fine 	if (!tb_switch_is_clx_supported(sw))
536cf29b9afSRajmohan Mani 		return 0;
537cf29b9afSRajmohan Mani 
538a28ec0e1SGil Fine 	if (tb_switch_tmu_hifi_is_enabled(sw, sw->tmu.unidirectional_request))
539cf29b9afSRajmohan Mani 		return 0;
540cf29b9afSRajmohan Mani 
541*43f977bcSGil Fine 	if (tb_switch_is_titan_ridge(sw) && unidirectional) {
542*43f977bcSGil Fine 		/* Titan Ridge supports only CL0s */
543*43f977bcSGil Fine 		if (!tb_switch_is_cl0s_enabled(sw))
544*43f977bcSGil Fine 			return -EOPNOTSUPP;
545*43f977bcSGil Fine 
546*43f977bcSGil Fine 		ret = tb_switch_tmu_objection_mask(sw);
547*43f977bcSGil Fine 		if (ret)
548*43f977bcSGil Fine 			return ret;
549*43f977bcSGil Fine 
550*43f977bcSGil Fine 		ret = tb_switch_tmu_unidirectional_enable(sw);
551*43f977bcSGil Fine 		if (ret)
552*43f977bcSGil Fine 			return ret;
553*43f977bcSGil Fine 	}
554*43f977bcSGil Fine 
555cf29b9afSRajmohan Mani 	ret = tb_switch_tmu_set_time_disruption(sw, true);
556cf29b9afSRajmohan Mani 	if (ret)
557cf29b9afSRajmohan Mani 		return ret;
558cf29b9afSRajmohan Mani 
559a28ec0e1SGil Fine 	if (tb_route(sw)) {
560a28ec0e1SGil Fine 		/* The used mode changes are from OFF to HiFi-Uni/HiFi-BiDir */
561a28ec0e1SGil Fine 		if (sw->tmu.rate == TB_SWITCH_TMU_RATE_OFF) {
562a28ec0e1SGil Fine 			if (unidirectional)
563a28ec0e1SGil Fine 				ret = __tb_switch_tmu_enable_unidirectional(sw);
564a28ec0e1SGil Fine 			else
565a28ec0e1SGil Fine 				ret = __tb_switch_tmu_enable_bidirectional(sw);
566cf29b9afSRajmohan Mani 			if (ret)
567cf29b9afSRajmohan Mani 				return ret;
568a28ec0e1SGil Fine 		}
569a28ec0e1SGil Fine 		sw->tmu.unidirectional = unidirectional;
570cf29b9afSRajmohan Mani 	} else {
571a28ec0e1SGil Fine 		/*
572a28ec0e1SGil Fine 		 * Host router port configurations are written as
573a28ec0e1SGil Fine 		 * part of configurations for downstream port of the parent
574a28ec0e1SGil Fine 		 * of the child node - see above.
575a28ec0e1SGil Fine 		 * Here only the host router' rate configuration is written.
576a28ec0e1SGil Fine 		 */
577cf29b9afSRajmohan Mani 		ret = tb_switch_tmu_rate_write(sw, TB_SWITCH_TMU_RATE_HIFI);
578cf29b9afSRajmohan Mani 		if (ret)
579cf29b9afSRajmohan Mani 			return ret;
580cf29b9afSRajmohan Mani 	}
581cf29b9afSRajmohan Mani 
582cf29b9afSRajmohan Mani 	sw->tmu.rate = TB_SWITCH_TMU_RATE_HIFI;
583cf29b9afSRajmohan Mani 
584a28ec0e1SGil Fine 	tb_sw_dbg(sw, "TMU: mode set to: %s\n", tb_switch_tmu_mode_name(sw));
585cf29b9afSRajmohan Mani 	return tb_switch_tmu_set_time_disruption(sw, false);
586cf29b9afSRajmohan Mani }
587a28ec0e1SGil Fine 
588a28ec0e1SGil Fine /**
589a28ec0e1SGil Fine  * tb_switch_tmu_enable() - Enable TMU on a router
590a28ec0e1SGil Fine  * @sw: Router whose TMU to enable
591a28ec0e1SGil Fine  *
592a28ec0e1SGil Fine  * Enables TMU of a router to be in uni-directional or bi-directional HiFi mode.
593a28ec0e1SGil Fine  * Calling tb_switch_tmu_configure() is required before calling this function,
594a28ec0e1SGil Fine  * to select the mode HiFi and directionality (uni-directional/bi-directional).
595a28ec0e1SGil Fine  * In both modes all tunneling should work. Uni-directional mode is required for
596a28ec0e1SGil Fine  * CLx (Link Low-Power) to work.
597a28ec0e1SGil Fine  */
598a28ec0e1SGil Fine int tb_switch_tmu_enable(struct tb_switch *sw)
599a28ec0e1SGil Fine {
600a28ec0e1SGil Fine 	if (sw->tmu.rate_request == TB_SWITCH_TMU_RATE_NORMAL)
601a28ec0e1SGil Fine 		return -EOPNOTSUPP;
602a28ec0e1SGil Fine 
603a28ec0e1SGil Fine 	return tb_switch_tmu_hifi_enable(sw);
604a28ec0e1SGil Fine }
605a28ec0e1SGil Fine 
606a28ec0e1SGil Fine /**
607a28ec0e1SGil Fine  * tb_switch_tmu_configure() - Configure the TMU rate and directionality
608a28ec0e1SGil Fine  * @sw: Router whose mode to change
609a28ec0e1SGil Fine  * @rate: Rate to configure Off/LowRes/HiFi
610a28ec0e1SGil Fine  * @unidirectional: If uni-directional (bi-directional otherwise)
611a28ec0e1SGil Fine  *
612a28ec0e1SGil Fine  * Selects the rate of the TMU and directionality (uni-directional or
613a28ec0e1SGil Fine  * bi-directional). Must be called before tb_switch_tmu_enable().
614a28ec0e1SGil Fine  */
615a28ec0e1SGil Fine void tb_switch_tmu_configure(struct tb_switch *sw,
616a28ec0e1SGil Fine 			     enum tb_switch_tmu_rate rate, bool unidirectional)
617a28ec0e1SGil Fine {
618a28ec0e1SGil Fine 	sw->tmu.unidirectional_request = unidirectional;
619a28ec0e1SGil Fine 	sw->tmu.rate_request = rate;
620a28ec0e1SGil Fine }
621