Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

thunderbolt: Add CL0s support for USB4 routers

In this patch we add enabling of CL0s - a low power state of the link.
Low power states (called collectively CLx) are used to reduce
transmitter and receiver power when a high-speed lane is idle. For now,
we add support only for first low power state: CL0s. We enable it, if
both sides of the link support it, and only for the first hop router.
(i.e. the first device that connected to the host router). This is
needed for better thermal management.

Signed-off-by: Gil Fine <gil.fine@intel.com>
Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>

authored by

Gil Fine and committed by
Mika Westerberg
8a90e4fa a28ec0e1

+348 -1
+263
drivers/thunderbolt/switch.c
··· 3223 3223 3224 3224 return NULL; 3225 3225 } 3226 + 3227 + static int __tb_port_pm_secondary_set(struct tb_port *port, bool secondary) 3228 + { 3229 + u32 phy; 3230 + int ret; 3231 + 3232 + ret = tb_port_read(port, &phy, TB_CFG_PORT, 3233 + port->cap_phy + LANE_ADP_CS_1, 1); 3234 + if (ret) 3235 + return ret; 3236 + 3237 + if (secondary) 3238 + phy |= LANE_ADP_CS_1_PMS; 3239 + else 3240 + phy &= ~LANE_ADP_CS_1_PMS; 3241 + 3242 + return tb_port_write(port, &phy, TB_CFG_PORT, 3243 + port->cap_phy + LANE_ADP_CS_1, 1); 3244 + } 3245 + 3246 + static int tb_port_pm_secondary_enable(struct tb_port *port) 3247 + { 3248 + return __tb_port_pm_secondary_set(port, true); 3249 + } 3250 + 3251 + static int tb_port_pm_secondary_disable(struct tb_port *port) 3252 + { 3253 + return __tb_port_pm_secondary_set(port, false); 3254 + } 3255 + 3256 + static int tb_switch_pm_secondary_resolve(struct tb_switch *sw) 3257 + { 3258 + struct tb_switch *parent = tb_switch_parent(sw); 3259 + struct tb_port *up, *down; 3260 + int ret; 3261 + 3262 + if (!tb_route(sw)) 3263 + return 0; 3264 + 3265 + up = tb_upstream_port(sw); 3266 + down = tb_port_at(tb_route(sw), parent); 3267 + ret = tb_port_pm_secondary_enable(up); 3268 + if (ret) 3269 + return ret; 3270 + 3271 + return tb_port_pm_secondary_disable(down); 3272 + } 3273 + 3274 + static bool tb_port_clx_supported(struct tb_port *port, enum tb_clx clx) 3275 + { 3276 + u32 mask, val; 3277 + bool ret; 3278 + 3279 + /* Don't enable CLx in case of two single-lane links */ 3280 + if (!port->bonded && port->dual_link_port) 3281 + return false; 3282 + 3283 + /* Don't enable CLx in case of inter-domain link */ 3284 + if (port->xdomain) 3285 + return false; 3286 + 3287 + if (!usb4_port_clx_supported(port)) 3288 + return false; 3289 + 3290 + switch (clx) { 3291 + case TB_CL0S: 3292 + /* CL0s support requires also CL1 support */ 3293 + mask = LANE_ADP_CS_0_CL0S_SUPPORT | LANE_ADP_CS_0_CL1_SUPPORT; 3294 + break; 3295 + 3296 + /* For now we support only CL0s. Not CL1, CL2 */ 3297 + case TB_CL1: 3298 + case TB_CL2: 3299 + default: 3300 + return false; 3301 + } 3302 + 3303 + ret = tb_port_read(port, &val, TB_CFG_PORT, 3304 + port->cap_phy + LANE_ADP_CS_0, 1); 3305 + if (ret) 3306 + return false; 3307 + 3308 + return !!(val & mask); 3309 + } 3310 + 3311 + static inline bool tb_port_cl0s_supported(struct tb_port *port) 3312 + { 3313 + return tb_port_clx_supported(port, TB_CL0S); 3314 + } 3315 + 3316 + static int __tb_port_cl0s_set(struct tb_port *port, bool enable) 3317 + { 3318 + u32 phy, mask; 3319 + int ret; 3320 + 3321 + /* To enable CL0s also required to enable CL1 */ 3322 + mask = LANE_ADP_CS_1_CL0S_ENABLE | LANE_ADP_CS_1_CL1_ENABLE; 3323 + ret = tb_port_read(port, &phy, TB_CFG_PORT, 3324 + port->cap_phy + LANE_ADP_CS_1, 1); 3325 + if (ret) 3326 + return ret; 3327 + 3328 + if (enable) 3329 + phy |= mask; 3330 + else 3331 + phy &= ~mask; 3332 + 3333 + return tb_port_write(port, &phy, TB_CFG_PORT, 3334 + port->cap_phy + LANE_ADP_CS_1, 1); 3335 + } 3336 + 3337 + static int tb_port_cl0s_disable(struct tb_port *port) 3338 + { 3339 + return __tb_port_cl0s_set(port, false); 3340 + } 3341 + 3342 + static int tb_port_cl0s_enable(struct tb_port *port) 3343 + { 3344 + return __tb_port_cl0s_set(port, true); 3345 + } 3346 + 3347 + static int tb_switch_enable_cl0s(struct tb_switch *sw) 3348 + { 3349 + struct tb_switch *parent = tb_switch_parent(sw); 3350 + bool up_cl0s_support, down_cl0s_support; 3351 + struct tb_port *up, *down; 3352 + int ret; 3353 + 3354 + if (!tb_switch_is_usb4(sw)) 3355 + return 0; 3356 + 3357 + /* 3358 + * Enable CLx for host router's downstream port as part of the 3359 + * downstream router enabling procedure. 3360 + */ 3361 + if (!tb_route(sw)) 3362 + return 0; 3363 + 3364 + /* Enable CLx only for first hop router (depth = 1) */ 3365 + if (tb_route(parent)) 3366 + return 0; 3367 + 3368 + ret = tb_switch_pm_secondary_resolve(sw); 3369 + if (ret) 3370 + return ret; 3371 + 3372 + up = tb_upstream_port(sw); 3373 + down = tb_port_at(tb_route(sw), parent); 3374 + 3375 + up_cl0s_support = tb_port_cl0s_supported(up); 3376 + down_cl0s_support = tb_port_cl0s_supported(down); 3377 + 3378 + tb_port_dbg(up, "CL0s %ssupported\n", 3379 + up_cl0s_support ? "" : "not "); 3380 + tb_port_dbg(down, "CL0s %ssupported\n", 3381 + down_cl0s_support ? "" : "not "); 3382 + 3383 + if (!up_cl0s_support || !down_cl0s_support) 3384 + return -EOPNOTSUPP; 3385 + 3386 + ret = tb_port_cl0s_enable(up); 3387 + if (ret) 3388 + return ret; 3389 + 3390 + ret = tb_port_cl0s_enable(down); 3391 + if (ret) { 3392 + tb_port_cl0s_disable(up); 3393 + return ret; 3394 + } 3395 + 3396 + sw->clx = TB_CL0S; 3397 + 3398 + tb_port_dbg(up, "CL0s enabled\n"); 3399 + return 0; 3400 + } 3401 + 3402 + /** 3403 + * tb_switch_enable_clx() - Enable CLx on upstream port of specified router 3404 + * @sw: Router to enable CLx for 3405 + * @clx: The CLx state to enable 3406 + * 3407 + * Enable CLx state only for first hop router. That is the most common 3408 + * use-case, that is intended for better thermal management, and so helps 3409 + * to improve performance. CLx is enabled only if both sides of the link 3410 + * support CLx, and if both sides of the link are not configured as two 3411 + * single lane links and only if the link is not inter-domain link. The 3412 + * complete set of conditions is descibed in CM Guide 1.0 section 8.1. 3413 + * 3414 + * Return: Returns 0 on success or an error code on failure. 3415 + */ 3416 + int tb_switch_enable_clx(struct tb_switch *sw, enum tb_clx clx) 3417 + { 3418 + struct tb_switch *root_sw = sw->tb->root_switch; 3419 + 3420 + /* 3421 + * CLx is not enabled and validated on Intel USB4 platforms before 3422 + * Alder Lake. 3423 + */ 3424 + if (root_sw->generation < 4 || tb_switch_is_tiger_lake(root_sw)) 3425 + return 0; 3426 + 3427 + switch (clx) { 3428 + case TB_CL0S: 3429 + return tb_switch_enable_cl0s(sw); 3430 + 3431 + default: 3432 + return -EOPNOTSUPP; 3433 + } 3434 + } 3435 + 3436 + static int tb_switch_disable_cl0s(struct tb_switch *sw) 3437 + { 3438 + struct tb_switch *parent = tb_switch_parent(sw); 3439 + struct tb_port *up, *down; 3440 + int ret; 3441 + 3442 + if (!tb_switch_is_usb4(sw)) 3443 + return 0; 3444 + 3445 + /* 3446 + * Disable CLx for host router's downstream port as part of the 3447 + * downstream router enabling procedure. 3448 + */ 3449 + if (!tb_route(sw)) 3450 + return 0; 3451 + 3452 + /* Disable CLx only for first hop router (depth = 1) */ 3453 + if (tb_route(parent)) 3454 + return 0; 3455 + 3456 + up = tb_upstream_port(sw); 3457 + down = tb_port_at(tb_route(sw), parent); 3458 + ret = tb_port_cl0s_disable(up); 3459 + if (ret) 3460 + return ret; 3461 + 3462 + ret = tb_port_cl0s_disable(down); 3463 + if (ret) 3464 + return ret; 3465 + 3466 + sw->clx = TB_CLX_DISABLE; 3467 + 3468 + tb_port_dbg(up, "CL0s disabled\n"); 3469 + return 0; 3470 + } 3471 + 3472 + /** 3473 + * tb_switch_disable_clx() - Disable CLx on upstream port of specified router 3474 + * @sw: Router to disable CLx for 3475 + * @clx: The CLx state to disable 3476 + * 3477 + * Return: Returns 0 on success or an error code on failure. 3478 + */ 3479 + int tb_switch_disable_clx(struct tb_switch *sw, enum tb_clx clx) 3480 + { 3481 + switch (clx) { 3482 + case TB_CL0S: 3483 + return tb_switch_disable_cl0s(sw); 3484 + 3485 + default: 3486 + return -EOPNOTSUPP; 3487 + } 3488 + }
+8 -1
drivers/thunderbolt/tb.c
··· 669 669 tb_switch_lane_bonding_enable(sw); 670 670 /* Set the link configured */ 671 671 tb_switch_configure_link(sw); 672 - tb_switch_tmu_configure(sw, TB_SWITCH_TMU_RATE_HIFI, false); 672 + if (tb_switch_enable_clx(sw, TB_CL0S)) 673 + tb_sw_warn(sw, "failed to enable CLx on upstream port\n"); 674 + 675 + tb_switch_tmu_configure(sw, TB_SWITCH_TMU_RATE_HIFI, 676 + tb_switch_is_clx_enabled(sw)); 673 677 674 678 if (tb_enable_tmu(sw)) 675 679 tb_sw_warn(sw, "failed to enable TMU\n"); ··· 1423 1419 /* No need to restore if the router is already unplugged */ 1424 1420 if (sw->is_unplugged) 1425 1421 return; 1422 + 1423 + if (tb_switch_enable_clx(sw, TB_CL0S)) 1424 + tb_sw_warn(sw, "failed to re-enable CLx on upstream port\n"); 1426 1425 1427 1426 /* 1428 1427 * tb_switch_tmu_configure() was already called when the switch was
+51
drivers/thunderbolt/tb.h
··· 109 109 enum tb_switch_tmu_rate rate_request; 110 110 }; 111 111 112 + enum tb_clx { 113 + TB_CLX_DISABLE, 114 + TB_CL0S, 115 + TB_CL1, 116 + TB_CL2, 117 + }; 118 + 112 119 /** 113 120 * struct tb_switch - a thunderbolt switch 114 121 * @dev: Device for the switch ··· 164 157 * @min_dp_main_credits: Router preferred minimum number of buffers for DP MAIN 165 158 * @max_pcie_credits: Router preferred number of buffers for PCIe 166 159 * @max_dma_credits: Router preferred number of buffers for DMA/P2P 160 + * @clx: CLx state on the upstream link of the router 167 161 * 168 162 * When the switch is being added or removed to the domain (other 169 163 * switches) you need to have domain lock held. ··· 213 205 unsigned int min_dp_main_credits; 214 206 unsigned int max_pcie_credits; 215 207 unsigned int max_dma_credits; 208 + enum tb_clx clx; 216 209 }; 217 210 218 211 /** ··· 871 862 return false; 872 863 } 873 864 865 + static inline bool tb_switch_is_tiger_lake(const struct tb_switch *sw) 866 + { 867 + if (sw->config.vendor_id == PCI_VENDOR_ID_INTEL) { 868 + switch (sw->config.device_id) { 869 + case PCI_DEVICE_ID_INTEL_TGL_NHI0: 870 + case PCI_DEVICE_ID_INTEL_TGL_NHI1: 871 + case PCI_DEVICE_ID_INTEL_TGL_H_NHI0: 872 + case PCI_DEVICE_ID_INTEL_TGL_H_NHI1: 873 + return true; 874 + } 875 + } 876 + return false; 877 + } 878 + 874 879 /** 875 880 * tb_switch_is_usb4() - Is the switch USB4 compliant 876 881 * @sw: Switch to check ··· 939 916 { 940 917 return sw->tmu.rate == TB_SWITCH_TMU_RATE_HIFI && 941 918 sw->tmu.unidirectional == unidirectional; 919 + } 920 + 921 + int tb_switch_enable_clx(struct tb_switch *sw, enum tb_clx clx); 922 + int tb_switch_disable_clx(struct tb_switch *sw, enum tb_clx clx); 923 + 924 + /** 925 + * tb_switch_is_clx_enabled() - Checks if the CLx is enabled 926 + * @sw: Router to check the CLx state for 927 + * 928 + * Checks if the CLx is enabled on the router upstream link. 929 + * Not applicable for a host router. 930 + */ 931 + static inline bool tb_switch_is_clx_enabled(const struct tb_switch *sw) 932 + { 933 + return sw->clx != TB_CLX_DISABLE; 934 + } 935 + 936 + /** 937 + * tb_switch_is_cl0s_enabled() - Checks if the CL0s is enabled 938 + * @sw: Router to check for the CL0s 939 + * 940 + * Checks if the CL0s is enabled on the router upstream link. 941 + * Not applicable for a host router. 942 + */ 943 + static inline bool tb_switch_is_cl0s_enabled(const struct tb_switch *sw) 944 + { 945 + return sw->clx == TB_CL0S; 942 946 } 943 947 944 948 int tb_wait_for_port(struct tb_port *port, bool wait_if_unplugged); ··· 1147 1097 int usb4_port_router_offline(struct tb_port *port); 1148 1098 int usb4_port_router_online(struct tb_port *port); 1149 1099 int usb4_port_enumerate_retimers(struct tb_port *port); 1100 + bool usb4_port_clx_supported(struct tb_port *port); 1150 1101 1151 1102 int usb4_port_retimer_set_inbound_sbtx(struct tb_port *port, u8 index); 1152 1103 int usb4_port_retimer_read(struct tb_port *port, u8 index, u8 reg, void *buf,
+6
drivers/thunderbolt/tb_regs.h
··· 313 313 #define LANE_ADP_CS_0 0x00 314 314 #define LANE_ADP_CS_0_SUPPORTED_WIDTH_MASK GENMASK(25, 20) 315 315 #define LANE_ADP_CS_0_SUPPORTED_WIDTH_SHIFT 20 316 + #define LANE_ADP_CS_0_CL0S_SUPPORT BIT(26) 317 + #define LANE_ADP_CS_0_CL1_SUPPORT BIT(27) 316 318 #define LANE_ADP_CS_1 0x01 317 319 #define LANE_ADP_CS_1_TARGET_WIDTH_MASK GENMASK(9, 4) 318 320 #define LANE_ADP_CS_1_TARGET_WIDTH_SHIFT 4 319 321 #define LANE_ADP_CS_1_TARGET_WIDTH_SINGLE 0x1 320 322 #define LANE_ADP_CS_1_TARGET_WIDTH_DUAL 0x3 323 + #define LANE_ADP_CS_1_CL0S_ENABLE BIT(10) 324 + #define LANE_ADP_CS_1_CL1_ENABLE BIT(11) 321 325 #define LANE_ADP_CS_1_LD BIT(14) 322 326 #define LANE_ADP_CS_1_LB BIT(15) 323 327 #define LANE_ADP_CS_1_CURRENT_SPEED_MASK GENMASK(19, 16) ··· 330 326 #define LANE_ADP_CS_1_CURRENT_SPEED_GEN3 0x4 331 327 #define LANE_ADP_CS_1_CURRENT_WIDTH_MASK GENMASK(25, 20) 332 328 #define LANE_ADP_CS_1_CURRENT_WIDTH_SHIFT 20 329 + #define LANE_ADP_CS_1_PMS BIT(30) 333 330 334 331 /* USB4 port registers */ 335 332 #define PORT_CS_1 0x01 ··· 346 341 #define PORT_CS_18 0x12 347 342 #define PORT_CS_18_BE BIT(8) 348 343 #define PORT_CS_18_TCM BIT(9) 344 + #define PORT_CS_18_CPS BIT(10) 349 345 #define PORT_CS_18_WOU4S BIT(18) 350 346 #define PORT_CS_19 0x13 351 347 #define PORT_CS_19_PC BIT(3)
+20
drivers/thunderbolt/usb4.c
··· 1386 1386 USB4_SB_OPCODE, &val, sizeof(val)); 1387 1387 } 1388 1388 1389 + /** 1390 + * usb4_port_clx_supported() - Check if CLx is supported by the link 1391 + * @port: Port to check for CLx support for 1392 + * 1393 + * PORT_CS_18_CPS bit reflects if the link supports CLx including 1394 + * active cables (if connected on the link). 1395 + */ 1396 + bool usb4_port_clx_supported(struct tb_port *port) 1397 + { 1398 + int ret; 1399 + u32 val; 1400 + 1401 + ret = tb_port_read(port, &val, TB_CFG_PORT, 1402 + port->cap_usb4 + PORT_CS_18, 1); 1403 + if (ret) 1404 + return false; 1405 + 1406 + return !!(val & PORT_CS_18_CPS); 1407 + } 1408 + 1389 1409 static inline int usb4_port_retimer_op(struct tb_port *port, u8 index, 1390 1410 enum usb4_sb_opcode opcode, 1391 1411 int timeout_msec)