162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * CLx support 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2020 - 2023, Intel Corporation 662306a36Sopenharmony_ci * Authors: Gil Fine <gil.fine@intel.com> 762306a36Sopenharmony_ci * Mika Westerberg <mika.westerberg@linux.intel.com> 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/module.h> 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include "tb.h" 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_cistatic bool clx_enabled = true; 1562306a36Sopenharmony_cimodule_param_named(clx, clx_enabled, bool, 0444); 1662306a36Sopenharmony_ciMODULE_PARM_DESC(clx, "allow low power states on the high-speed lanes (default: true)"); 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_cistatic const char *clx_name(unsigned int clx) 1962306a36Sopenharmony_ci{ 2062306a36Sopenharmony_ci switch (clx) { 2162306a36Sopenharmony_ci case TB_CL0S | TB_CL1 | TB_CL2: 2262306a36Sopenharmony_ci return "CL0s/CL1/CL2"; 2362306a36Sopenharmony_ci case TB_CL1 | TB_CL2: 2462306a36Sopenharmony_ci return "CL1/CL2"; 2562306a36Sopenharmony_ci case TB_CL0S | TB_CL2: 2662306a36Sopenharmony_ci return "CL0s/CL2"; 2762306a36Sopenharmony_ci case TB_CL0S | TB_CL1: 2862306a36Sopenharmony_ci return "CL0s/CL1"; 2962306a36Sopenharmony_ci case TB_CL0S: 3062306a36Sopenharmony_ci return "CL0s"; 3162306a36Sopenharmony_ci case 0: 3262306a36Sopenharmony_ci return "disabled"; 3362306a36Sopenharmony_ci default: 3462306a36Sopenharmony_ci return "unknown"; 3562306a36Sopenharmony_ci } 3662306a36Sopenharmony_ci} 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_cistatic int tb_port_pm_secondary_set(struct tb_port *port, bool secondary) 3962306a36Sopenharmony_ci{ 4062306a36Sopenharmony_ci u32 phy; 4162306a36Sopenharmony_ci int ret; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci ret = tb_port_read(port, &phy, TB_CFG_PORT, 4462306a36Sopenharmony_ci port->cap_phy + LANE_ADP_CS_1, 1); 4562306a36Sopenharmony_ci if (ret) 4662306a36Sopenharmony_ci return ret; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci if (secondary) 4962306a36Sopenharmony_ci phy |= LANE_ADP_CS_1_PMS; 5062306a36Sopenharmony_ci else 5162306a36Sopenharmony_ci phy &= ~LANE_ADP_CS_1_PMS; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci return tb_port_write(port, &phy, TB_CFG_PORT, 5462306a36Sopenharmony_ci port->cap_phy + LANE_ADP_CS_1, 1); 5562306a36Sopenharmony_ci} 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_cistatic int tb_port_pm_secondary_enable(struct tb_port *port) 5862306a36Sopenharmony_ci{ 5962306a36Sopenharmony_ci return tb_port_pm_secondary_set(port, true); 6062306a36Sopenharmony_ci} 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_cistatic int tb_port_pm_secondary_disable(struct tb_port *port) 6362306a36Sopenharmony_ci{ 6462306a36Sopenharmony_ci return tb_port_pm_secondary_set(port, false); 6562306a36Sopenharmony_ci} 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci/* Called for USB4 or Titan Ridge routers only */ 6862306a36Sopenharmony_cistatic bool tb_port_clx_supported(struct tb_port *port, unsigned int clx) 6962306a36Sopenharmony_ci{ 7062306a36Sopenharmony_ci u32 val, mask = 0; 7162306a36Sopenharmony_ci bool ret; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci /* Don't enable CLx in case of two single-lane links */ 7462306a36Sopenharmony_ci if (!port->bonded && port->dual_link_port) 7562306a36Sopenharmony_ci return false; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci /* Don't enable CLx in case of inter-domain link */ 7862306a36Sopenharmony_ci if (port->xdomain) 7962306a36Sopenharmony_ci return false; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci if (tb_switch_is_usb4(port->sw)) { 8262306a36Sopenharmony_ci if (!usb4_port_clx_supported(port)) 8362306a36Sopenharmony_ci return false; 8462306a36Sopenharmony_ci } else if (!tb_lc_is_clx_supported(port)) { 8562306a36Sopenharmony_ci return false; 8662306a36Sopenharmony_ci } 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci if (clx & TB_CL0S) 8962306a36Sopenharmony_ci mask |= LANE_ADP_CS_0_CL0S_SUPPORT; 9062306a36Sopenharmony_ci if (clx & TB_CL1) 9162306a36Sopenharmony_ci mask |= LANE_ADP_CS_0_CL1_SUPPORT; 9262306a36Sopenharmony_ci if (clx & TB_CL2) 9362306a36Sopenharmony_ci mask |= LANE_ADP_CS_0_CL2_SUPPORT; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci ret = tb_port_read(port, &val, TB_CFG_PORT, 9662306a36Sopenharmony_ci port->cap_phy + LANE_ADP_CS_0, 1); 9762306a36Sopenharmony_ci if (ret) 9862306a36Sopenharmony_ci return false; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci return !!(val & mask); 10162306a36Sopenharmony_ci} 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_cistatic int tb_port_clx_set(struct tb_port *port, unsigned int clx, bool enable) 10462306a36Sopenharmony_ci{ 10562306a36Sopenharmony_ci u32 phy, mask = 0; 10662306a36Sopenharmony_ci int ret; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci if (clx & TB_CL0S) 10962306a36Sopenharmony_ci mask |= LANE_ADP_CS_1_CL0S_ENABLE; 11062306a36Sopenharmony_ci if (clx & TB_CL1) 11162306a36Sopenharmony_ci mask |= LANE_ADP_CS_1_CL1_ENABLE; 11262306a36Sopenharmony_ci if (clx & TB_CL2) 11362306a36Sopenharmony_ci mask |= LANE_ADP_CS_1_CL2_ENABLE; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci if (!mask) 11662306a36Sopenharmony_ci return -EOPNOTSUPP; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci ret = tb_port_read(port, &phy, TB_CFG_PORT, 11962306a36Sopenharmony_ci port->cap_phy + LANE_ADP_CS_1, 1); 12062306a36Sopenharmony_ci if (ret) 12162306a36Sopenharmony_ci return ret; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci if (enable) 12462306a36Sopenharmony_ci phy |= mask; 12562306a36Sopenharmony_ci else 12662306a36Sopenharmony_ci phy &= ~mask; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci return tb_port_write(port, &phy, TB_CFG_PORT, 12962306a36Sopenharmony_ci port->cap_phy + LANE_ADP_CS_1, 1); 13062306a36Sopenharmony_ci} 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_cistatic int tb_port_clx_disable(struct tb_port *port, unsigned int clx) 13362306a36Sopenharmony_ci{ 13462306a36Sopenharmony_ci return tb_port_clx_set(port, clx, false); 13562306a36Sopenharmony_ci} 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_cistatic int tb_port_clx_enable(struct tb_port *port, unsigned int clx) 13862306a36Sopenharmony_ci{ 13962306a36Sopenharmony_ci return tb_port_clx_set(port, clx, true); 14062306a36Sopenharmony_ci} 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_cistatic int tb_port_clx(struct tb_port *port) 14362306a36Sopenharmony_ci{ 14462306a36Sopenharmony_ci u32 val; 14562306a36Sopenharmony_ci int ret; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci if (!tb_port_clx_supported(port, TB_CL0S | TB_CL1 | TB_CL2)) 14862306a36Sopenharmony_ci return 0; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci ret = tb_port_read(port, &val, TB_CFG_PORT, 15162306a36Sopenharmony_ci port->cap_phy + LANE_ADP_CS_1, 1); 15262306a36Sopenharmony_ci if (ret) 15362306a36Sopenharmony_ci return ret; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci if (val & LANE_ADP_CS_1_CL0S_ENABLE) 15662306a36Sopenharmony_ci ret |= TB_CL0S; 15762306a36Sopenharmony_ci if (val & LANE_ADP_CS_1_CL1_ENABLE) 15862306a36Sopenharmony_ci ret |= TB_CL1; 15962306a36Sopenharmony_ci if (val & LANE_ADP_CS_1_CL2_ENABLE) 16062306a36Sopenharmony_ci ret |= TB_CL2; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci return ret; 16362306a36Sopenharmony_ci} 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci/** 16662306a36Sopenharmony_ci * tb_port_clx_is_enabled() - Is given CL state enabled 16762306a36Sopenharmony_ci * @port: USB4 port to check 16862306a36Sopenharmony_ci * @clx: Mask of CL states to check 16962306a36Sopenharmony_ci * 17062306a36Sopenharmony_ci * Returns true if any of the given CL states is enabled for @port. 17162306a36Sopenharmony_ci */ 17262306a36Sopenharmony_cibool tb_port_clx_is_enabled(struct tb_port *port, unsigned int clx) 17362306a36Sopenharmony_ci{ 17462306a36Sopenharmony_ci return !!(tb_port_clx(port) & clx); 17562306a36Sopenharmony_ci} 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci/** 17862306a36Sopenharmony_ci * tb_switch_clx_init() - Initialize router CL states 17962306a36Sopenharmony_ci * @sw: Router 18062306a36Sopenharmony_ci * 18162306a36Sopenharmony_ci * Can be called for any router. Initializes the current CL state by 18262306a36Sopenharmony_ci * reading it from the hardware. 18362306a36Sopenharmony_ci * 18462306a36Sopenharmony_ci * Returns %0 in case of success and negative errno in case of failure. 18562306a36Sopenharmony_ci */ 18662306a36Sopenharmony_ciint tb_switch_clx_init(struct tb_switch *sw) 18762306a36Sopenharmony_ci{ 18862306a36Sopenharmony_ci struct tb_port *up, *down; 18962306a36Sopenharmony_ci unsigned int clx, tmp; 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci if (tb_switch_is_icm(sw)) 19262306a36Sopenharmony_ci return 0; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci if (!tb_route(sw)) 19562306a36Sopenharmony_ci return 0; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci if (!tb_switch_clx_is_supported(sw)) 19862306a36Sopenharmony_ci return 0; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci up = tb_upstream_port(sw); 20162306a36Sopenharmony_ci down = tb_switch_downstream_port(sw); 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci clx = tb_port_clx(up); 20462306a36Sopenharmony_ci tmp = tb_port_clx(down); 20562306a36Sopenharmony_ci if (clx != tmp) 20662306a36Sopenharmony_ci tb_sw_warn(sw, "CLx: inconsistent configuration %#x != %#x\n", 20762306a36Sopenharmony_ci clx, tmp); 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci tb_sw_dbg(sw, "CLx: current mode: %s\n", clx_name(clx)); 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci sw->clx = clx; 21262306a36Sopenharmony_ci return 0; 21362306a36Sopenharmony_ci} 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_cistatic int tb_switch_pm_secondary_resolve(struct tb_switch *sw) 21662306a36Sopenharmony_ci{ 21762306a36Sopenharmony_ci struct tb_port *up, *down; 21862306a36Sopenharmony_ci int ret; 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci if (!tb_route(sw)) 22162306a36Sopenharmony_ci return 0; 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci up = tb_upstream_port(sw); 22462306a36Sopenharmony_ci down = tb_switch_downstream_port(sw); 22562306a36Sopenharmony_ci ret = tb_port_pm_secondary_enable(up); 22662306a36Sopenharmony_ci if (ret) 22762306a36Sopenharmony_ci return ret; 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci return tb_port_pm_secondary_disable(down); 23062306a36Sopenharmony_ci} 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_cistatic int tb_switch_mask_clx_objections(struct tb_switch *sw) 23362306a36Sopenharmony_ci{ 23462306a36Sopenharmony_ci int up_port = sw->config.upstream_port_number; 23562306a36Sopenharmony_ci u32 offset, val[2], mask_obj, unmask_obj; 23662306a36Sopenharmony_ci int ret, i; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci /* Only Titan Ridge of pre-USB4 devices support CLx states */ 23962306a36Sopenharmony_ci if (!tb_switch_is_titan_ridge(sw)) 24062306a36Sopenharmony_ci return 0; 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci if (!tb_route(sw)) 24362306a36Sopenharmony_ci return 0; 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci /* 24662306a36Sopenharmony_ci * In Titan Ridge there are only 2 dual-lane Thunderbolt ports: 24762306a36Sopenharmony_ci * Port A consists of lane adapters 1,2 and 24862306a36Sopenharmony_ci * Port B consists of lane adapters 3,4 24962306a36Sopenharmony_ci * If upstream port is A, (lanes are 1,2), we mask objections from 25062306a36Sopenharmony_ci * port B (lanes 3,4) and unmask objections from Port A and vice-versa. 25162306a36Sopenharmony_ci */ 25262306a36Sopenharmony_ci if (up_port == 1) { 25362306a36Sopenharmony_ci mask_obj = TB_LOW_PWR_C0_PORT_B_MASK; 25462306a36Sopenharmony_ci unmask_obj = TB_LOW_PWR_C1_PORT_A_MASK; 25562306a36Sopenharmony_ci offset = TB_LOW_PWR_C1_CL1; 25662306a36Sopenharmony_ci } else { 25762306a36Sopenharmony_ci mask_obj = TB_LOW_PWR_C1_PORT_A_MASK; 25862306a36Sopenharmony_ci unmask_obj = TB_LOW_PWR_C0_PORT_B_MASK; 25962306a36Sopenharmony_ci offset = TB_LOW_PWR_C3_CL1; 26062306a36Sopenharmony_ci } 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci ret = tb_sw_read(sw, &val, TB_CFG_SWITCH, 26362306a36Sopenharmony_ci sw->cap_lp + offset, ARRAY_SIZE(val)); 26462306a36Sopenharmony_ci if (ret) 26562306a36Sopenharmony_ci return ret; 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(val); i++) { 26862306a36Sopenharmony_ci val[i] |= mask_obj; 26962306a36Sopenharmony_ci val[i] &= ~unmask_obj; 27062306a36Sopenharmony_ci } 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci return tb_sw_write(sw, &val, TB_CFG_SWITCH, 27362306a36Sopenharmony_ci sw->cap_lp + offset, ARRAY_SIZE(val)); 27462306a36Sopenharmony_ci} 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci/** 27762306a36Sopenharmony_ci * tb_switch_clx_is_supported() - Is CLx supported on this type of router 27862306a36Sopenharmony_ci * @sw: The router to check CLx support for 27962306a36Sopenharmony_ci */ 28062306a36Sopenharmony_cibool tb_switch_clx_is_supported(const struct tb_switch *sw) 28162306a36Sopenharmony_ci{ 28262306a36Sopenharmony_ci if (!clx_enabled) 28362306a36Sopenharmony_ci return false; 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci if (sw->quirks & QUIRK_NO_CLX) 28662306a36Sopenharmony_ci return false; 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci /* 28962306a36Sopenharmony_ci * CLx is not enabled and validated on Intel USB4 platforms 29062306a36Sopenharmony_ci * before Alder Lake. 29162306a36Sopenharmony_ci */ 29262306a36Sopenharmony_ci if (tb_switch_is_tiger_lake(sw)) 29362306a36Sopenharmony_ci return false; 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci return tb_switch_is_usb4(sw) || tb_switch_is_titan_ridge(sw); 29662306a36Sopenharmony_ci} 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_cistatic bool validate_mask(unsigned int clx) 29962306a36Sopenharmony_ci{ 30062306a36Sopenharmony_ci /* Previous states need to be enabled */ 30162306a36Sopenharmony_ci if (clx & TB_CL1) 30262306a36Sopenharmony_ci return (clx & TB_CL0S) == TB_CL0S; 30362306a36Sopenharmony_ci return true; 30462306a36Sopenharmony_ci} 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci/** 30762306a36Sopenharmony_ci * tb_switch_clx_enable() - Enable CLx on upstream port of specified router 30862306a36Sopenharmony_ci * @sw: Router to enable CLx for 30962306a36Sopenharmony_ci * @clx: The CLx state to enable 31062306a36Sopenharmony_ci * 31162306a36Sopenharmony_ci * CLx is enabled only if both sides of the link support CLx, and if both sides 31262306a36Sopenharmony_ci * of the link are not configured as two single lane links and only if the link 31362306a36Sopenharmony_ci * is not inter-domain link. The complete set of conditions is described in CM 31462306a36Sopenharmony_ci * Guide 1.0 section 8.1. 31562306a36Sopenharmony_ci * 31662306a36Sopenharmony_ci * Returns %0 on success or an error code on failure. 31762306a36Sopenharmony_ci */ 31862306a36Sopenharmony_ciint tb_switch_clx_enable(struct tb_switch *sw, unsigned int clx) 31962306a36Sopenharmony_ci{ 32062306a36Sopenharmony_ci bool up_clx_support, down_clx_support; 32162306a36Sopenharmony_ci struct tb_switch *parent_sw; 32262306a36Sopenharmony_ci struct tb_port *up, *down; 32362306a36Sopenharmony_ci int ret; 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci if (!clx || sw->clx == clx) 32662306a36Sopenharmony_ci return 0; 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci if (!validate_mask(clx)) 32962306a36Sopenharmony_ci return -EINVAL; 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci parent_sw = tb_switch_parent(sw); 33262306a36Sopenharmony_ci if (!parent_sw) 33362306a36Sopenharmony_ci return 0; 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci if (!tb_switch_clx_is_supported(parent_sw) || 33662306a36Sopenharmony_ci !tb_switch_clx_is_supported(sw)) 33762306a36Sopenharmony_ci return 0; 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci /* Only support CL2 for v2 routers */ 34062306a36Sopenharmony_ci if ((clx & TB_CL2) && 34162306a36Sopenharmony_ci (usb4_switch_version(parent_sw) < 2 || 34262306a36Sopenharmony_ci usb4_switch_version(sw) < 2)) 34362306a36Sopenharmony_ci return -EOPNOTSUPP; 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci ret = tb_switch_pm_secondary_resolve(sw); 34662306a36Sopenharmony_ci if (ret) 34762306a36Sopenharmony_ci return ret; 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci up = tb_upstream_port(sw); 35062306a36Sopenharmony_ci down = tb_switch_downstream_port(sw); 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci up_clx_support = tb_port_clx_supported(up, clx); 35362306a36Sopenharmony_ci down_clx_support = tb_port_clx_supported(down, clx); 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci tb_port_dbg(up, "CLx: %s %ssupported\n", clx_name(clx), 35662306a36Sopenharmony_ci up_clx_support ? "" : "not "); 35762306a36Sopenharmony_ci tb_port_dbg(down, "CLx: %s %ssupported\n", clx_name(clx), 35862306a36Sopenharmony_ci down_clx_support ? "" : "not "); 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci if (!up_clx_support || !down_clx_support) 36162306a36Sopenharmony_ci return -EOPNOTSUPP; 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci ret = tb_port_clx_enable(up, clx); 36462306a36Sopenharmony_ci if (ret) 36562306a36Sopenharmony_ci return ret; 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci ret = tb_port_clx_enable(down, clx); 36862306a36Sopenharmony_ci if (ret) { 36962306a36Sopenharmony_ci tb_port_clx_disable(up, clx); 37062306a36Sopenharmony_ci return ret; 37162306a36Sopenharmony_ci } 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci ret = tb_switch_mask_clx_objections(sw); 37462306a36Sopenharmony_ci if (ret) { 37562306a36Sopenharmony_ci tb_port_clx_disable(up, clx); 37662306a36Sopenharmony_ci tb_port_clx_disable(down, clx); 37762306a36Sopenharmony_ci return ret; 37862306a36Sopenharmony_ci } 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci sw->clx |= clx; 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci tb_sw_dbg(sw, "CLx: %s enabled\n", clx_name(clx)); 38362306a36Sopenharmony_ci return 0; 38462306a36Sopenharmony_ci} 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci/** 38762306a36Sopenharmony_ci * tb_switch_clx_disable() - Disable CLx on upstream port of specified router 38862306a36Sopenharmony_ci * @sw: Router to disable CLx for 38962306a36Sopenharmony_ci * 39062306a36Sopenharmony_ci * Disables all CL states of the given router. Can be called on any 39162306a36Sopenharmony_ci * router and if the states were not enabled already does nothing. 39262306a36Sopenharmony_ci * 39362306a36Sopenharmony_ci * Returns the CL states that were disabled or negative errno in case of 39462306a36Sopenharmony_ci * failure. 39562306a36Sopenharmony_ci */ 39662306a36Sopenharmony_ciint tb_switch_clx_disable(struct tb_switch *sw) 39762306a36Sopenharmony_ci{ 39862306a36Sopenharmony_ci unsigned int clx = sw->clx; 39962306a36Sopenharmony_ci struct tb_port *up, *down; 40062306a36Sopenharmony_ci int ret; 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci if (!tb_switch_clx_is_supported(sw)) 40362306a36Sopenharmony_ci return 0; 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci if (!clx) 40662306a36Sopenharmony_ci return 0; 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci up = tb_upstream_port(sw); 40962306a36Sopenharmony_ci down = tb_switch_downstream_port(sw); 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci ret = tb_port_clx_disable(up, clx); 41262306a36Sopenharmony_ci if (ret) 41362306a36Sopenharmony_ci return ret; 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci ret = tb_port_clx_disable(down, clx); 41662306a36Sopenharmony_ci if (ret) 41762306a36Sopenharmony_ci return ret; 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci sw->clx = 0; 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci tb_sw_dbg(sw, "CLx: %s disabled\n", clx_name(clx)); 42262306a36Sopenharmony_ci return clx; 42362306a36Sopenharmony_ci} 424