Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  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
 14static const char *tb_switch_tmu_mode_name(const struct tb_switch *sw)
 15{
 16	bool root_switch = !tb_route(sw);
 17
 18	switch (sw->tmu.rate) {
 19	case TB_SWITCH_TMU_RATE_OFF:
 20		return "off";
 21
 22	case TB_SWITCH_TMU_RATE_HIFI:
 23		/* Root switch does not have upstream directionality */
 24		if (root_switch)
 25			return "HiFi";
 26		if (sw->tmu.unidirectional)
 27			return "uni-directional, HiFi";
 28		return "bi-directional, HiFi";
 29
 30	case TB_SWITCH_TMU_RATE_NORMAL:
 31		if (root_switch)
 32			return "normal";
 33		return "uni-directional, normal";
 34
 35	default:
 36		return "unknown";
 37	}
 38}
 39
 40static bool tb_switch_tmu_ucap_supported(struct tb_switch *sw)
 41{
 42	int ret;
 43	u32 val;
 44
 45	ret = tb_sw_read(sw, &val, TB_CFG_SWITCH,
 46			 sw->tmu.cap + TMU_RTR_CS_0, 1);
 47	if (ret)
 48		return false;
 49
 50	return !!(val & TMU_RTR_CS_0_UCAP);
 51}
 52
 53static int tb_switch_tmu_rate_read(struct tb_switch *sw)
 54{
 55	int ret;
 56	u32 val;
 57
 58	ret = tb_sw_read(sw, &val, TB_CFG_SWITCH,
 59			 sw->tmu.cap + TMU_RTR_CS_3, 1);
 60	if (ret)
 61		return ret;
 62
 63	val >>= TMU_RTR_CS_3_TS_PACKET_INTERVAL_SHIFT;
 64	return val;
 65}
 66
 67static int tb_switch_tmu_rate_write(struct tb_switch *sw, int rate)
 68{
 69	int ret;
 70	u32 val;
 71
 72	ret = tb_sw_read(sw, &val, TB_CFG_SWITCH,
 73			 sw->tmu.cap + TMU_RTR_CS_3, 1);
 74	if (ret)
 75		return ret;
 76
 77	val &= ~TMU_RTR_CS_3_TS_PACKET_INTERVAL_MASK;
 78	val |= rate << TMU_RTR_CS_3_TS_PACKET_INTERVAL_SHIFT;
 79
 80	return tb_sw_write(sw, &val, TB_CFG_SWITCH,
 81			   sw->tmu.cap + TMU_RTR_CS_3, 1);
 82}
 83
 84static int tb_port_tmu_write(struct tb_port *port, u8 offset, u32 mask,
 85			     u32 value)
 86{
 87	u32 data;
 88	int ret;
 89
 90	ret = tb_port_read(port, &data, TB_CFG_PORT, port->cap_tmu + offset, 1);
 91	if (ret)
 92		return ret;
 93
 94	data &= ~mask;
 95	data |= value;
 96
 97	return tb_port_write(port, &data, TB_CFG_PORT,
 98			     port->cap_tmu + offset, 1);
 99}
100
101static int tb_port_tmu_set_unidirectional(struct tb_port *port,
102					  bool unidirectional)
103{
104	u32 val;
105
106	if (!port->sw->tmu.has_ucap)
107		return 0;
108
109	val = unidirectional ? TMU_ADP_CS_3_UDM : 0;
110	return tb_port_tmu_write(port, TMU_ADP_CS_3, TMU_ADP_CS_3_UDM, val);
111}
112
113static inline int tb_port_tmu_unidirectional_disable(struct tb_port *port)
114{
115	return tb_port_tmu_set_unidirectional(port, false);
116}
117
118static bool tb_port_tmu_is_unidirectional(struct tb_port *port)
119{
120	int ret;
121	u32 val;
122
123	ret = tb_port_read(port, &val, TB_CFG_PORT,
124			   port->cap_tmu + TMU_ADP_CS_3, 1);
125	if (ret)
126		return false;
127
128	return val & TMU_ADP_CS_3_UDM;
129}
130
131static int tb_switch_tmu_set_time_disruption(struct tb_switch *sw, bool set)
132{
133	int ret;
134	u32 val;
135
136	ret = tb_sw_read(sw, &val, TB_CFG_SWITCH,
137			 sw->tmu.cap + TMU_RTR_CS_0, 1);
138	if (ret)
139		return ret;
140
141	if (set)
142		val |= TMU_RTR_CS_0_TD;
143	else
144		val &= ~TMU_RTR_CS_0_TD;
145
146	return tb_sw_write(sw, &val, TB_CFG_SWITCH,
147			   sw->tmu.cap + TMU_RTR_CS_0, 1);
148}
149
150/**
151 * tb_switch_tmu_init() - Initialize switch TMU structures
152 * @sw: Switch to initialized
153 *
154 * This function must be called before other TMU related functions to
155 * makes the internal structures are filled in correctly. Does not
156 * change any hardware configuration.
157 */
158int tb_switch_tmu_init(struct tb_switch *sw)
159{
160	struct tb_port *port;
161	int ret;
162
163	if (tb_switch_is_icm(sw))
164		return 0;
165
166	ret = tb_switch_find_cap(sw, TB_SWITCH_CAP_TMU);
167	if (ret > 0)
168		sw->tmu.cap = ret;
169
170	tb_switch_for_each_port(sw, port) {
171		int cap;
172
173		cap = tb_port_find_cap(port, TB_PORT_CAP_TIME1);
174		if (cap > 0)
175			port->cap_tmu = cap;
176	}
177
178	ret = tb_switch_tmu_rate_read(sw);
179	if (ret < 0)
180		return ret;
181
182	sw->tmu.rate = ret;
183
184	sw->tmu.has_ucap = tb_switch_tmu_ucap_supported(sw);
185	if (sw->tmu.has_ucap) {
186		tb_sw_dbg(sw, "TMU: supports uni-directional mode\n");
187
188		if (tb_route(sw)) {
189			struct tb_port *up = tb_upstream_port(sw);
190
191			sw->tmu.unidirectional =
192				tb_port_tmu_is_unidirectional(up);
193		}
194	} else {
195		sw->tmu.unidirectional = false;
196	}
197
198	tb_sw_dbg(sw, "TMU: current mode: %s\n", tb_switch_tmu_mode_name(sw));
199	return 0;
200}
201
202/**
203 * tb_switch_tmu_post_time() - Update switch local time
204 * @sw: Switch whose time to update
205 *
206 * Updates switch local time using time posting procedure.
207 */
208int tb_switch_tmu_post_time(struct tb_switch *sw)
209{
210	unsigned int  post_local_time_offset, post_time_offset;
211	struct tb_switch *root_switch = sw->tb->root_switch;
212	u64 hi, mid, lo, local_time, post_time;
213	int i, ret, retries = 100;
214	u32 gm_local_time[3];
215
216	if (!tb_route(sw))
217		return 0;
218
219	if (!tb_switch_is_usb4(sw))
220		return 0;
221
222	/* Need to be able to read the grand master time */
223	if (!root_switch->tmu.cap)
224		return 0;
225
226	ret = tb_sw_read(root_switch, gm_local_time, TB_CFG_SWITCH,
227			 root_switch->tmu.cap + TMU_RTR_CS_1,
228			 ARRAY_SIZE(gm_local_time));
229	if (ret)
230		return ret;
231
232	for (i = 0; i < ARRAY_SIZE(gm_local_time); i++)
233		tb_sw_dbg(root_switch, "local_time[%d]=0x%08x\n", i,
234			  gm_local_time[i]);
235
236	/* Convert to nanoseconds (drop fractional part) */
237	hi = gm_local_time[2] & TMU_RTR_CS_3_LOCAL_TIME_NS_MASK;
238	mid = gm_local_time[1];
239	lo = (gm_local_time[0] & TMU_RTR_CS_1_LOCAL_TIME_NS_MASK) >>
240		TMU_RTR_CS_1_LOCAL_TIME_NS_SHIFT;
241	local_time = hi << 48 | mid << 16 | lo;
242
243	/* Tell the switch that time sync is disrupted for a while */
244	ret = tb_switch_tmu_set_time_disruption(sw, true);
245	if (ret)
246		return ret;
247
248	post_local_time_offset = sw->tmu.cap + TMU_RTR_CS_22;
249	post_time_offset = sw->tmu.cap + TMU_RTR_CS_24;
250
251	/*
252	 * Write the Grandmaster time to the Post Local Time registers
253	 * of the new switch.
254	 */
255	ret = tb_sw_write(sw, &local_time, TB_CFG_SWITCH,
256			  post_local_time_offset, 2);
257	if (ret)
258		goto out;
259
260	/*
261	 * Have the new switch update its local time (by writing 1 to
262	 * the post_time registers) and wait for the completion of the
263	 * same (post_time register becomes 0). This means the time has
264	 * been converged properly.
265	 */
266	post_time = 1;
267
268	ret = tb_sw_write(sw, &post_time, TB_CFG_SWITCH, post_time_offset, 2);
269	if (ret)
270		goto out;
271
272	do {
273		usleep_range(5, 10);
274		ret = tb_sw_read(sw, &post_time, TB_CFG_SWITCH,
275				 post_time_offset, 2);
276		if (ret)
277			goto out;
278	} while (--retries && post_time);
279
280	if (!retries) {
281		ret = -ETIMEDOUT;
282		goto out;
283	}
284
285	tb_sw_dbg(sw, "TMU: updated local time to %#llx\n", local_time);
286
287out:
288	tb_switch_tmu_set_time_disruption(sw, false);
289	return ret;
290}
291
292/**
293 * tb_switch_tmu_disable() - Disable TMU of a switch
294 * @sw: Switch whose TMU to disable
295 *
296 * Turns off TMU of @sw if it is enabled. If not enabled does nothing.
297 */
298int tb_switch_tmu_disable(struct tb_switch *sw)
299{
300	int ret;
301
302	if (!tb_switch_is_usb4(sw))
303		return 0;
304
305	/* Already disabled? */
306	if (sw->tmu.rate == TB_SWITCH_TMU_RATE_OFF)
307		return 0;
308
309	if (sw->tmu.unidirectional) {
310		struct tb_switch *parent = tb_switch_parent(sw);
311		struct tb_port *up, *down;
312
313		up = tb_upstream_port(sw);
314		down = tb_port_at(tb_route(sw), parent);
315
316		/* The switch may be unplugged so ignore any errors */
317		tb_port_tmu_unidirectional_disable(up);
318		ret = tb_port_tmu_unidirectional_disable(down);
319		if (ret)
320			return ret;
321	}
322
323	tb_switch_tmu_rate_write(sw, TB_SWITCH_TMU_RATE_OFF);
324
325	sw->tmu.unidirectional = false;
326	sw->tmu.rate = TB_SWITCH_TMU_RATE_OFF;
327
328	tb_sw_dbg(sw, "TMU: disabled\n");
329	return 0;
330}
331
332/**
333 * tb_switch_tmu_enable() - Enable TMU on a switch
334 * @sw: Switch whose TMU to enable
335 *
336 * Enables TMU of a switch to be in bi-directional, HiFi mode. In this mode
337 * all tunneling should work.
338 */
339int tb_switch_tmu_enable(struct tb_switch *sw)
340{
341	int ret;
342
343	if (!tb_switch_is_usb4(sw))
344		return 0;
345
346	if (tb_switch_tmu_is_enabled(sw))
347		return 0;
348
349	ret = tb_switch_tmu_set_time_disruption(sw, true);
350	if (ret)
351		return ret;
352
353	/* Change mode to bi-directional */
354	if (tb_route(sw) && sw->tmu.unidirectional) {
355		struct tb_switch *parent = tb_switch_parent(sw);
356		struct tb_port *up, *down;
357
358		up = tb_upstream_port(sw);
359		down = tb_port_at(tb_route(sw), parent);
360
361		ret = tb_port_tmu_unidirectional_disable(down);
362		if (ret)
363			return ret;
364
365		ret = tb_switch_tmu_rate_write(sw, TB_SWITCH_TMU_RATE_HIFI);
366		if (ret)
367			return ret;
368
369		ret = tb_port_tmu_unidirectional_disable(up);
370		if (ret)
371			return ret;
372	} else {
373		ret = tb_switch_tmu_rate_write(sw, TB_SWITCH_TMU_RATE_HIFI);
374		if (ret)
375			return ret;
376	}
377
378	sw->tmu.unidirectional = false;
379	sw->tmu.rate = TB_SWITCH_TMU_RATE_HIFI;
380	tb_sw_dbg(sw, "TMU: mode set to: %s\n", tb_switch_tmu_mode_name(sw));
381
382	return tb_switch_tmu_set_time_disruption(sw, false);
383}