brintos

brintos / linux-shallow public Read only

0
0
Text · 9.3 KiB · 787dfd1 Raw
427 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * CLx support4 *5 * Copyright (C) 2020 - 2023, Intel Corporation6 * 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	else51		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	else126		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 enabled167 * @port: USB4 port to check168 * @clx: Mask of CL states to check169 *170 * Returns true if any of the given CL states is enabled for @port.171 */172bool tb_port_clx_is_enabled(struct tb_port *port, unsigned int clx)173{174	return !!(tb_port_clx(port) & clx);175}176 177/**178 * tb_switch_clx_is_supported() - Is CLx supported on this type of router179 * @sw: The router to check CLx support for180 */181static bool tb_switch_clx_is_supported(const struct tb_switch *sw)182{183	if (!clx_enabled)184		return false;185 186	if (sw->quirks & QUIRK_NO_CLX)187		return false;188 189	/*190	 * CLx is not enabled and validated on Intel USB4 platforms191	 * before Alder Lake.192	 */193	if (tb_switch_is_tiger_lake(sw))194		return false;195 196	return tb_switch_is_usb4(sw) || tb_switch_is_titan_ridge(sw);197}198 199/**200 * tb_switch_clx_init() - Initialize router CL states201 * @sw: Router202 *203 * Can be called for any router. Initializes the current CL state by204 * reading it from the hardware.205 *206 * Returns %0 in case of success and negative errno in case of failure.207 */208int tb_switch_clx_init(struct tb_switch *sw)209{210	struct tb_port *up, *down;211	unsigned int clx, tmp;212 213	if (tb_switch_is_icm(sw))214		return 0;215 216	if (!tb_route(sw))217		return 0;218 219	if (!tb_switch_clx_is_supported(sw))220		return 0;221 222	up = tb_upstream_port(sw);223	down = tb_switch_downstream_port(sw);224 225	clx = tb_port_clx(up);226	tmp = tb_port_clx(down);227	if (clx != tmp)228		tb_sw_warn(sw, "CLx: inconsistent configuration %#x != %#x\n",229			   clx, tmp);230 231	tb_sw_dbg(sw, "CLx: current mode: %s\n", clx_name(clx));232 233	sw->clx = clx;234	return 0;235}236 237static int tb_switch_pm_secondary_resolve(struct tb_switch *sw)238{239	struct tb_port *up, *down;240	int ret;241 242	if (!tb_route(sw))243		return 0;244 245	up = tb_upstream_port(sw);246	down = tb_switch_downstream_port(sw);247	ret = tb_port_pm_secondary_enable(up);248	if (ret)249		return ret;250 251	return tb_port_pm_secondary_disable(down);252}253 254static int tb_switch_mask_clx_objections(struct tb_switch *sw)255{256	int up_port = sw->config.upstream_port_number;257	u32 offset, val[2], mask_obj, unmask_obj;258	int ret, i;259 260	/* Only Titan Ridge of pre-USB4 devices support CLx states */261	if (!tb_switch_is_titan_ridge(sw))262		return 0;263 264	if (!tb_route(sw))265		return 0;266 267	/*268	 * In Titan Ridge there are only 2 dual-lane Thunderbolt ports:269	 * Port A consists of lane adapters 1,2 and270	 * Port B consists of lane adapters 3,4271	 * If upstream port is A, (lanes are 1,2), we mask objections from272	 * port B (lanes 3,4) and unmask objections from Port A and vice-versa.273	 */274	if (up_port == 1) {275		mask_obj = TB_LOW_PWR_C0_PORT_B_MASK;276		unmask_obj = TB_LOW_PWR_C1_PORT_A_MASK;277		offset = TB_LOW_PWR_C1_CL1;278	} else {279		mask_obj = TB_LOW_PWR_C1_PORT_A_MASK;280		unmask_obj = TB_LOW_PWR_C0_PORT_B_MASK;281		offset = TB_LOW_PWR_C3_CL1;282	}283 284	ret = tb_sw_read(sw, &val, TB_CFG_SWITCH,285			 sw->cap_lp + offset, ARRAY_SIZE(val));286	if (ret)287		return ret;288 289	for (i = 0; i < ARRAY_SIZE(val); i++) {290		val[i] |= mask_obj;291		val[i] &= ~unmask_obj;292	}293 294	return tb_sw_write(sw, &val, TB_CFG_SWITCH,295			   sw->cap_lp + offset, ARRAY_SIZE(val));296}297 298static bool validate_mask(unsigned int clx)299{300	/* Previous states need to be enabled */301	if (clx & TB_CL1)302		return (clx & TB_CL0S) == TB_CL0S;303	return true;304}305 306/**307 * tb_switch_clx_enable() - Enable CLx on upstream port of specified router308 * @sw: Router to enable CLx for309 * @clx: The CLx state to enable310 *311 * CLx is enabled only if both sides of the link support CLx, and if both sides312 * of the link are not configured as two single lane links and only if the link313 * is not inter-domain link. The complete set of conditions is described in CM314 * Guide 1.0 section 8.1.315 *316 * Returns %0 on success or an error code on failure.317 */318int tb_switch_clx_enable(struct tb_switch *sw, unsigned int clx)319{320	bool up_clx_support, down_clx_support;321	struct tb_switch *parent_sw;322	struct tb_port *up, *down;323	int ret;324 325	if (!clx || sw->clx == clx)326		return 0;327 328	if (!validate_mask(clx))329		return -EINVAL;330 331	parent_sw = tb_switch_parent(sw);332	if (!parent_sw)333		return 0;334 335	if (!tb_switch_clx_is_supported(parent_sw) ||336	    !tb_switch_clx_is_supported(sw))337		return 0;338 339	/* Only support CL2 for v2 routers */340	if ((clx & TB_CL2) &&341	    (usb4_switch_version(parent_sw) < 2 ||342	     usb4_switch_version(sw) < 2))343		return -EOPNOTSUPP;344 345	ret = tb_switch_pm_secondary_resolve(sw);346	if (ret)347		return ret;348 349	up = tb_upstream_port(sw);350	down = tb_switch_downstream_port(sw);351 352	up_clx_support = tb_port_clx_supported(up, clx);353	down_clx_support = tb_port_clx_supported(down, clx);354 355	tb_port_dbg(up, "CLx: %s %ssupported\n", clx_name(clx),356		    up_clx_support ? "" : "not ");357	tb_port_dbg(down, "CLx: %s %ssupported\n", clx_name(clx),358		    down_clx_support ? "" : "not ");359 360	if (!up_clx_support || !down_clx_support)361		return -EOPNOTSUPP;362 363	ret = tb_port_clx_enable(up, clx);364	if (ret)365		return ret;366 367	ret = tb_port_clx_enable(down, clx);368	if (ret) {369		tb_port_clx_disable(up, clx);370		return ret;371	}372 373	ret = tb_switch_mask_clx_objections(sw);374	if (ret) {375		tb_port_clx_disable(up, clx);376		tb_port_clx_disable(down, clx);377		return ret;378	}379 380	sw->clx |= clx;381 382	tb_sw_dbg(sw, "CLx: %s enabled\n", clx_name(clx));383	return 0;384}385 386/**387 * tb_switch_clx_disable() - Disable CLx on upstream port of specified router388 * @sw: Router to disable CLx for389 *390 * Disables all CL states of the given router. Can be called on any391 * router and if the states were not enabled already does nothing.392 *393 * Returns the CL states that were disabled or negative errno in case of394 * failure.395 */396int tb_switch_clx_disable(struct tb_switch *sw)397{398	unsigned int clx = sw->clx;399	struct tb_port *up, *down;400	int ret;401 402	if (!tb_switch_clx_is_supported(sw))403		return 0;404 405	if (!clx)406		return 0;407 408	if (sw->is_unplugged)409		return clx;410 411	up = tb_upstream_port(sw);412	down = tb_switch_downstream_port(sw);413 414	ret = tb_port_clx_disable(up, clx);415	if (ret)416		return ret;417 418	ret = tb_port_clx_disable(down, clx);419	if (ret)420		return ret;421 422	sw->clx = 0;423 424	tb_sw_dbg(sw, "CLx: %s disabled\n", clx_name(clx));425	return clx;426}427