// SPDX-License-Identifier: GPL-2.0 /* * CLx support * * Copyright (C) 2020 - 2023, Intel Corporation * Authors: Gil Fine <gil.fine@intel.com> * Mika Westerberg <mika.westerberg@linux.intel.com>
*/
#include <linux/module.h>
#include"tb.h"
staticbool clx_enabled = true;
module_param_named(clx, clx_enabled, bool, 0444);
MODULE_PARM_DESC(clx, "allow low power states on the high-speed lanes (default: true)");
staticconstchar *clx_name(unsignedint clx)
{ switch (clx) { case TB_CL0S | TB_CL1 | TB_CL2: return"CL0s/CL1/CL2"; case TB_CL1 | TB_CL2: return"CL1/CL2"; case TB_CL0S | TB_CL2: return"CL0s/CL2"; case TB_CL0S | TB_CL1: return"CL0s/CL1"; case TB_CL0S: return"CL0s"; case 0: return"disabled"; default: return"unknown";
}
}
staticint tb_port_clx(struct tb_port *port)
{
u32 val; int ret;
if (!tb_port_clx_supported(port, TB_CL0S | TB_CL1 | TB_CL2)) return 0;
ret = tb_port_read(port, &val, TB_CFG_PORT,
port->cap_phy + LANE_ADP_CS_1, 1); if (ret) return ret;
if (val & LANE_ADP_CS_1_CL0S_ENABLE)
ret |= TB_CL0S; if (val & LANE_ADP_CS_1_CL1_ENABLE)
ret |= TB_CL1; if (val & LANE_ADP_CS_1_CL2_ENABLE)
ret |= TB_CL2;
return ret;
}
/** * tb_port_clx_is_enabled() - Is given CL state enabled * @port: USB4 port to check * @clx: Mask of CL states to check * * Returns true if any of the given CL states is enabled for @port.
*/ bool tb_port_clx_is_enabled(struct tb_port *port, unsignedint clx)
{ return !!(tb_port_clx(port) & clx);
}
/** * tb_switch_clx_is_supported() - Is CLx supported on this type of router * @sw: The router to check CLx support for
*/ staticbool tb_switch_clx_is_supported(conststruct tb_switch *sw)
{ if (!clx_enabled) returnfalse;
if (sw->quirks & QUIRK_NO_CLX) returnfalse;
/* * CLx is not enabled and validated on Intel USB4 platforms * before Alder Lake.
*/ if (tb_switch_is_tiger_lake(sw)) returnfalse;
/** * tb_switch_clx_init() - Initialize router CL states * @sw: Router * * Can be called for any router. Initializes the current CL state by * reading it from the hardware. * * Returns %0 in case of success and negative errno in case of failure.
*/ int tb_switch_clx_init(struct tb_switch *sw)
{ struct tb_port *up, *down; unsignedint clx, tmp;
if (tb_switch_is_icm(sw)) return 0;
if (!tb_route(sw)) return 0;
if (!tb_switch_clx_is_supported(sw)) return 0;
up = tb_upstream_port(sw);
down = tb_switch_downstream_port(sw);
up = tb_upstream_port(sw);
down = tb_switch_downstream_port(sw);
ret = tb_port_pm_secondary_enable(up); if (ret) return ret;
return tb_port_pm_secondary_disable(down);
}
staticint tb_switch_mask_clx_objections(struct tb_switch *sw)
{ int up_port = sw->config.upstream_port_number;
u32 offset, val[2], mask_obj, unmask_obj; int ret, i;
/* Only Titan Ridge of pre-USB4 devices support CLx states */ if (!tb_switch_is_titan_ridge(sw)) return 0;
if (!tb_route(sw)) return 0;
/* * In Titan Ridge there are only 2 dual-lane Thunderbolt ports: * Port A consists of lane adapters 1,2 and * Port B consists of lane adapters 3,4 * If upstream port is A, (lanes are 1,2), we mask objections from * port B (lanes 3,4) and unmask objections from Port A and vice-versa.
*/ if (up_port == 1) {
mask_obj = TB_LOW_PWR_C0_PORT_B_MASK;
unmask_obj = TB_LOW_PWR_C1_PORT_A_MASK;
offset = TB_LOW_PWR_C1_CL1;
} else {
mask_obj = TB_LOW_PWR_C1_PORT_A_MASK;
unmask_obj = TB_LOW_PWR_C0_PORT_B_MASK;
offset = TB_LOW_PWR_C3_CL1;
}
ret = tb_sw_read(sw, &val, TB_CFG_SWITCH,
sw->cap_lp + offset, ARRAY_SIZE(val)); if (ret) return ret;
for (i = 0; i < ARRAY_SIZE(val); i++) {
val[i] |= mask_obj;
val[i] &= ~unmask_obj;
}
staticbool validate_mask(unsignedint clx)
{ /* Previous states need to be enabled */ if (clx & TB_CL1) return (clx & TB_CL0S) == TB_CL0S; returntrue;
}
/** * tb_switch_clx_enable() - Enable CLx on upstream port of specified router * @sw: Router to enable CLx for * @clx: The CLx state to enable * * CLx is enabled only if both sides of the link support CLx, and if both sides * of the link are not configured as two single lane links and only if the link * is not inter-domain link. The complete set of conditions is described in CM * Guide 1.0 section 8.1. * * Returns %0 on success or an error code on failure.
*/ int tb_switch_clx_enable(struct tb_switch *sw, unsignedint clx)
{ bool up_clx_support, down_clx_support; struct tb_switch *parent_sw; struct tb_port *up, *down; int ret;
if (!clx || sw->clx == clx) return 0;
if (!validate_mask(clx)) return -EINVAL;
parent_sw = tb_switch_parent(sw); if (!parent_sw) return 0;
if (!tb_switch_clx_is_supported(parent_sw) ||
!tb_switch_clx_is_supported(sw)) return 0;
/* Only support CL2 for v2 routers */ if ((clx & TB_CL2) &&
(usb4_switch_version(parent_sw) < 2 ||
usb4_switch_version(sw) < 2)) return -EOPNOTSUPP;
ret = tb_switch_pm_secondary_resolve(sw); if (ret) return ret;
up = tb_upstream_port(sw);
down = tb_switch_downstream_port(sw);
/** * tb_switch_clx_disable() - Disable CLx on upstream port of specified router * @sw: Router to disable CLx for * * Disables all CL states of the given router. Can be called on any * router and if the states were not enabled already does nothing. * * Returns the CL states that were disabled or negative errno in case of * failure.
*/ int tb_switch_clx_disable(struct tb_switch *sw)
{ unsignedint clx = sw->clx; struct tb_port *up, *down; int ret;
if (!tb_switch_clx_is_supported(sw)) return 0;
if (!clx) return 0;
if (sw->is_unplugged) return clx;
up = tb_upstream_port(sw);
down = tb_switch_downstream_port(sw);
ret = tb_port_clx_disable(up, clx); if (ret) return ret;
ret = tb_port_clx_disable(down, clx); if (ret) return ret;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.