File Index
Files and directories
Sign in
FILE

clx.c

File preview with syntax highlighting.

linux-6.19.14/drivers/thunderbolt/clx.c C
  1// SPDX-License-Identifier: GPL-2.0
  2/*
  3 * CLx support
  4 *
  5 * Copyright (C) 2020 - 2023, Intel Corporation
  6 * Authors: Gil Fine <gil.fine@intel.com>
  7 *	    Mika Westerberg <mika.westerberg@linux.intel.com>
  8 */
  9
 10#include <linux/module.h>
 11
 12#include "tb.h"
 13
 14static bool clx_enabled = true;
 15module_param_named(clx, clx_enabled, bool, 0444);
 16MODULE_PARM_DESC(clx, "allow low power states on the high-speed lanes (default: true)");
 17
 18static const char *clx_name(unsigned int clx)
 19{
 20	switch (clx) {
 21	case TB_CL0S | TB_CL1 | TB_CL2:
 22		return "CL0s/CL1/CL2";
 23	case TB_CL1 | TB_CL2:
 24		return "CL1/CL2";
 25	case TB_CL0S | TB_CL2:
 26		return "CL0s/CL2";
 27	case TB_CL0S | TB_CL1:
 28		return "CL0s/CL1";
 29	case TB_CL0S:
 30		return "CL0s";
 31	case 0:
 32		return "disabled";
 33	default:
 34		return "unknown";
 35	}
 36}
 37
 38static int tb_port_pm_secondary_set(struct tb_port *port, bool secondary)
 39{
 40	u32 phy;
 41	int ret;
 42
 43	ret = tb_port_read(port, &phy, TB_CFG_PORT,
 44			   port->cap_phy + LANE_ADP_CS_1, 1);
 45	if (ret)
 46		return ret;
 47
 48	if (secondary)
 49		phy |= LANE_ADP_CS_1_PMS;
 50	else
 51		phy &= ~LANE_ADP_CS_1_PMS;
 52
 53	return tb_port_write(port, &phy, TB_CFG_PORT,
 54			     port->cap_phy + LANE_ADP_CS_1, 1);
 55}
 56
 57static int tb_port_pm_secondary_enable(struct tb_port *port)
 58{
 59	return tb_port_pm_secondary_set(port, true);
 60}
 61
 62static int tb_port_pm_secondary_disable(struct tb_port *port)
 63{
 64	return tb_port_pm_secondary_set(port, false);
 65}
 66
 67/* Called for USB4 or Titan Ridge routers only */
 68static bool tb_port_clx_supported(struct tb_port *port, unsigned int clx)
 69{
 70	u32 val, mask = 0;
 71	bool ret;
 72
 73	/* Don't enable CLx in case of two single-lane links */
 74	if (!port->bonded && port->dual_link_port)
 75		return false;
 76
 77	/* Don't enable CLx in case of inter-domain link */
 78	if (port->xdomain)
 79		return false;
 80
 81	if (tb_switch_is_usb4(port->sw)) {
 82		if (!usb4_port_clx_supported(port))
 83			return false;
 84	} else if (!tb_lc_is_clx_supported(port)) {
 85		return false;
 86	}
 87
 88	if (clx & TB_CL0S)
 89		mask |= LANE_ADP_CS_0_CL0S_SUPPORT;
 90	if (clx & TB_CL1)
 91		mask |= LANE_ADP_CS_0_CL1_SUPPORT;
 92	if (clx & TB_CL2)
 93		mask |= LANE_ADP_CS_0_CL2_SUPPORT;
 94
 95	ret = tb_port_read(port, &val, TB_CFG_PORT,
 96			   port->cap_phy + LANE_ADP_CS_0, 1);
 97	if (ret)
 98		return false;
 99
100	return !!(val & mask);
101}
102
103static int tb_port_clx_set(struct tb_port *port, unsigned int clx, bool enable)
104{
105	u32 phy, mask = 0;
106	int ret;
107
108	if (clx & TB_CL0S)
109		mask |= LANE_ADP_CS_1_CL0S_ENABLE;
110	if (clx & TB_CL1)
111		mask |= LANE_ADP_CS_1_CL1_ENABLE;
112	if (clx & TB_CL2)
113		mask |= LANE_ADP_CS_1_CL2_ENABLE;
114
115	if (!mask)
116		return -EOPNOTSUPP;
117
118	ret = tb_port_read(port, &phy, TB_CFG_PORT,
119			   port->cap_phy + LANE_ADP_CS_1, 1);
120	if (ret)
121		return ret;
122
123	if (enable)
124		phy |= mask;
125	else
126		phy &= ~mask;
127
128	return tb_port_write(port, &phy, TB_CFG_PORT,
129			     port->cap_phy + LANE_ADP_CS_1, 1);
130}
131
132static int tb_port_clx_disable(struct tb_port *port, unsigned int clx)
133{
134	return tb_port_clx_set(port, clx, false);
135}
136
137static int tb_port_clx_enable(struct tb_port *port, unsigned int clx)
138{
139	return tb_port_clx_set(port, clx, true);
140}
141
142static int tb_port_clx(struct tb_port *port)
143{
144	u32 val;
145	int ret;
146
147	if (!tb_port_clx_supported(port, TB_CL0S | TB_CL1 | TB_CL2))
148		return 0;
149
150	ret = tb_port_read(port, &val, TB_CFG_PORT,
151			   port->cap_phy + LANE_ADP_CS_1, 1);
152	if (ret)
153		return ret;
154
155	if (val & LANE_ADP_CS_1_CL0S_ENABLE)
156		ret |= TB_CL0S;
157	if (val & LANE_ADP_CS_1_CL1_ENABLE)
158		ret |= TB_CL1;
159	if (val & LANE_ADP_CS_1_CL2_ENABLE)
160		ret |= TB_CL2;
161
162	return ret;
163}
164
165/**
166 * tb_port_clx_is_enabled() - Is given CL state enabled
167 * @port: USB4 port to check
168 * @clx: Mask of CL states to check
169 *
170 * Return: %true if any of the given CL states is enabled for @port,
171 * %false otherwise.
172 */
173bool tb_port_clx_is_enabled(struct tb_port *port, unsigned int clx)
174{
175	return !!(tb_port_clx(port) & clx);
176}
177
178/**
179 * tb_switch_clx_is_supported() - Is CLx supported on this type of router
180 * @sw: The router to check CLx support for
181 *
182 * Return: %true if CLx is supported, %false otherwise.
183 */
184static bool tb_switch_clx_is_supported(const struct tb_switch *sw)
185{
186	if (!clx_enabled)
187		return false;
188
189	if (sw->quirks & QUIRK_NO_CLX)
190		return false;
191
192	/*
193	 * CLx is not enabled and validated on Intel USB4 platforms
194	 * before Alder Lake.
195	 */
196	if (tb_switch_is_tiger_lake(sw))
197		return false;
198
199	return tb_switch_is_usb4(sw) || tb_switch_is_titan_ridge(sw);
200}
201
202/**
203 * tb_switch_clx_init() - Initialize router CL states
204 * @sw: Router
205 *
206 * Can be called for any router. Initializes the current CL state by
207 * reading it from the hardware.
208 *
209 * Return: %0 on success, negative errno otherwise.
210 */
211int tb_switch_clx_init(struct tb_switch *sw)
212{
213	struct tb_port *up, *down;
214	unsigned int clx, tmp;
215
216	if (tb_switch_is_icm(sw))
217		return 0;
218
219	if (!tb_route(sw))
220		return 0;
221
222	if (!tb_switch_clx_is_supported(sw))
223		return 0;
224
225	up = tb_upstream_port(sw);
226	down = tb_switch_downstream_port(sw);
227
228	clx = tb_port_clx(up);
229	tmp = tb_port_clx(down);
230	if (clx != tmp)
231		tb_sw_warn(sw, "CLx: inconsistent configuration %#x != %#x\n",
232			   clx, tmp);
233
234	tb_sw_dbg(sw, "CLx: current mode: %s\n", clx_name(clx));
235
236	sw->clx = clx;
237	return 0;
238}
239
240static int tb_switch_pm_secondary_resolve(struct tb_switch *sw)
241{
242	struct tb_port *up, *down;
243	int ret;
244
245	if (!tb_route(sw))
246		return 0;
247
248	up = tb_upstream_port(sw);
249	down = tb_switch_downstream_port(sw);
250	ret = tb_port_pm_secondary_enable(up);
251	if (ret)
252		return ret;
253
254	return tb_port_pm_secondary_disable(down);
255}
256
257static int tb_switch_mask_clx_objections(struct tb_switch *sw)
258{
259	int up_port = sw->config.upstream_port_number;
260	u32 offset, val[2], mask_obj, unmask_obj;
261	int ret, i;
262
263	/* Only Titan Ridge of pre-USB4 devices support CLx states */
264	if (!tb_switch_is_titan_ridge(sw))
265		return 0;
266
267	if (!tb_route(sw))
268		return 0;
269
270	/*
271	 * In Titan Ridge there are only 2 dual-lane Thunderbolt ports:
272	 * Port A consists of lane adapters 1,2 and
273	 * Port B consists of lane adapters 3,4
274	 * If upstream port is A, (lanes are 1,2), we mask objections from
275	 * port B (lanes 3,4) and unmask objections from Port A and vice-versa.
276	 */
277	if (up_port == 1) {
278		mask_obj = TB_LOW_PWR_C0_PORT_B_MASK;
279		unmask_obj = TB_LOW_PWR_C1_PORT_A_MASK;
280		offset = TB_LOW_PWR_C1_CL1;
281	} else {
282		mask_obj = TB_LOW_PWR_C1_PORT_A_MASK;
283		unmask_obj = TB_LOW_PWR_C0_PORT_B_MASK;
284		offset = TB_LOW_PWR_C3_CL1;
285	}
286
287	ret = tb_sw_read(sw, &val, TB_CFG_SWITCH,
288			 sw->cap_lp + offset, ARRAY_SIZE(val));
289	if (ret)
290		return ret;
291
292	for (i = 0; i < ARRAY_SIZE(val); i++) {
293		val[i] |= mask_obj;
294		val[i] &= ~unmask_obj;
295	}
296
297	return tb_sw_write(sw, &val, TB_CFG_SWITCH,
298			   sw->cap_lp + offset, ARRAY_SIZE(val));
299}
300
301static bool validate_mask(unsigned int clx)
302{
303	/* Previous states need to be enabled */
304	if (clx & TB_CL1)
305		return (clx & TB_CL0S) == TB_CL0S;
306	return true;
307}
308
309/**
310 * tb_switch_clx_enable() - Enable CLx on upstream port of specified router
311 * @sw: Router to enable CLx for
312 * @clx: The CLx state to enable
313 *
314 * CLx is enabled only if both sides of the link support CLx, and if both sides
315 * of the link are not configured as two single lane links and only if the link
316 * is not inter-domain link. The complete set of conditions is described in CM
317 * Guide 1.0 section 8.1.
318 *
319 * Return: %0 on success, negative errno otherwise.
320 */
321int tb_switch_clx_enable(struct tb_switch *sw, unsigned int clx)
322{
323	bool up_clx_support, down_clx_support;
324	struct tb_switch *parent_sw;
325	struct tb_port *up, *down;
326	int ret;
327
328	if (!clx || sw->clx == clx)
329		return 0;
330
331	if (!validate_mask(clx))
332		return -EINVAL;
333
334	parent_sw = tb_switch_parent(sw);
335	if (!parent_sw)
336		return 0;
337
338	if (!tb_switch_clx_is_supported(parent_sw) ||
339	    !tb_switch_clx_is_supported(sw))
340		return 0;
341
342	/* Only support CL2 for v2 routers */
343	if ((clx & TB_CL2) &&
344	    (usb4_switch_version(parent_sw) < 2 ||
345	     usb4_switch_version(sw) < 2))
346		return -EOPNOTSUPP;
347
348	ret = tb_switch_pm_secondary_resolve(sw);
349	if (ret)
350		return ret;
351
352	up = tb_upstream_port(sw);
353	down = tb_switch_downstream_port(sw);
354
355	up_clx_support = tb_port_clx_supported(up, clx);
356	down_clx_support = tb_port_clx_supported(down, clx);
357
358	tb_port_dbg(up, "CLx: %s %ssupported\n", clx_name(clx),
359		    up_clx_support ? "" : "not ");
360	tb_port_dbg(down, "CLx: %s %ssupported\n", clx_name(clx),
361		    down_clx_support ? "" : "not ");
362
363	if (!up_clx_support || !down_clx_support)
364		return -EOPNOTSUPP;
365
366	ret = tb_port_clx_enable(up, clx);
367	if (ret)
368		return ret;
369
370	ret = tb_port_clx_enable(down, clx);
371	if (ret) {
372		tb_port_clx_disable(up, clx);
373		return ret;
374	}
375
376	ret = tb_switch_mask_clx_objections(sw);
377	if (ret) {
378		tb_port_clx_disable(up, clx);
379		tb_port_clx_disable(down, clx);
380		return ret;
381	}
382
383	sw->clx |= clx;
384
385	tb_sw_dbg(sw, "CLx: %s enabled\n", clx_name(clx));
386	return 0;
387}
388
389/**
390 * tb_switch_clx_disable() - Disable CLx on upstream port of specified router
391 * @sw: Router to disable CLx for
392 *
393 * Disables all CL states of the given router. Can be called on any
394 * router and if the states were not enabled already does nothing.
395 *
396 * Return: CL states that were disabled or negative errno otherwise.
397 */
398int tb_switch_clx_disable(struct tb_switch *sw)
399{
400	unsigned int clx = sw->clx;
401	struct tb_port *up, *down;
402	int ret;
403
404	if (!tb_switch_clx_is_supported(sw))
405		return 0;
406
407	if (!clx)
408		return 0;
409
410	if (sw->is_unplugged)
411		return clx;
412
413	up = tb_upstream_port(sw);
414	down = tb_switch_downstream_port(sw);
415
416	ret = tb_port_clx_disable(up, clx);
417	if (ret)
418		return ret;
419
420	ret = tb_port_clx_disable(down, clx);
421	if (ret)
422		return ret;
423
424	sw->clx = 0;
425
426	tb_sw_dbg(sw, "CLx: %s disabled\n", clx_name(clx));
427	return clx;
428}
~/ Read-only file preview.