/* * SPDX-FileCopyrightText: Copyright (c) 2020-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved. * SPDX-License-Identifier: MIT * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #include "nvlink_export.h" #include "export_nvswitch.h" #include "common_nvswitch.h" #include "regkey_nvswitch.h" #include "ls10/ls10.h" #include "nvswitch/ls10/dev_nvldl_ip_addendum.h" #include "nvswitch/ls10/dev_nvldl_ip.h" #include "nvswitch/ls10/dev_nvlipt_lnk_ip.h" #include "nvswitch/ls10/dev_nvlphyctl_ip.h" #include "nvswitch/ls10/dev_nvltlc_ip.h" #include "nvswitch/ls10/dev_minion_ip.h" #include "nvswitch/ls10/dev_minion_ip_addendum.h" #include "nvswitch/ls10/dev_nvlipt_lnk_ip.h" #include "nvswitch/ls10/dev_nvlipt_ip.h" #include "nvswitch/ls10/dev_nport_ip.h" #include "nvswitch/ls10/dev_minion_ip_addendum.h" #include "ls10/minion_nvlink_defines_public_ls10.h" #define NV_NVLINK_TLREQ_TIMEOUT_ACTIVE 10000 #define NV_NVLINK_TLREQ_TIMEOUT_SHUTDOWN 10 #define NV_NVLINK_TLREQ_TIMEOUT_RESET 4 #define NV_NVLINK_TLREQ_TIMEOUT_L2 5 static void _nvswitch_configure_reserved_throughput_counters ( nvlink_link *link ) { nvswitch_device *device = link->dev->pDevInfo; NvU32 linkNum = link->linkNumber; if (!NVSWITCH_IS_LINK_ENG_VALID_LS10(device, NVLTLC, link->linkNumber)) { NVSWITCH_PRINT(device, INFO, "Invalid link, skipping NVLink throughput counter config for link %d\n", link->linkNumber); return; } // // Counters 0 and 2 will be reserved for monitoring tools // Counters 1 and 3 will be user-configurable and used by devtools // // Rx0 config NVSWITCH_LINK_WR32_IDX_LS10(device, linkNum, NVLTLC, _NVLTLC_RX_LNK, _DEBUG_TP_CNTR_CTRL_0, 0, DRF_DEF(_NVLTLC_RX_LNK, _DEBUG_TP_CNTR_CTRL_0, _UNIT, _FLITS) | DRF_DEF(_NVLTLC_RX_LNK, _DEBUG_TP_CNTR_CTRL_0, _FLITFILTER, _DATA) | DRF_DEF(_NVLTLC_RX_LNK, _DEBUG_TP_CNTR_CTRL_0, _VCSETFILTERMODE, _INIT) | DRF_DEF(_NVLTLC_RX_LNK, _DEBUG_TP_CNTR_CTRL_0, _ENABLE, _ENABLE)); // Tx0 config NVSWITCH_LINK_WR32_IDX_LS10(device, linkNum, NVLTLC, _NVLTLC_TX_LNK, _DEBUG_TP_CNTR_CTRL_0, 0, DRF_DEF(_NVLTLC_TX_LNK, _DEBUG_TP_CNTR_CTRL_0, _UNIT, _FLITS) | DRF_DEF(_NVLTLC_TX_LNK, _DEBUG_TP_CNTR_CTRL_0, _FLITFILTER, _DATA) | DRF_DEF(_NVLTLC_TX_LNK, _DEBUG_TP_CNTR_CTRL_0, _VCSETFILTERMODE, _INIT) | DRF_DEF(_NVLTLC_TX_LNK, _DEBUG_TP_CNTR_CTRL_0, _ENABLE, _ENABLE)); // Rx2 config NVSWITCH_LINK_WR32_IDX_LS10(device, linkNum, NVLTLC, _NVLTLC_RX_LNK, _DEBUG_TP_CNTR_CTRL_0, 2, DRF_DEF(_NVLTLC_RX_LNK, _DEBUG_TP_CNTR_CTRL_0, _UNIT, _FLITS) | DRF_DEF(_NVLTLC_RX_LNK, _DEBUG_TP_CNTR_CTRL_0, _FLITFILTER, _HEAD) | DRF_DEF(_NVLTLC_RX_LNK, _DEBUG_TP_CNTR_CTRL_0, _FLITFILTER, _AE) | DRF_DEF(_NVLTLC_RX_LNK, _DEBUG_TP_CNTR_CTRL_0, _FLITFILTER, _BE) | DRF_DEF(_NVLTLC_RX_LNK, _DEBUG_TP_CNTR_CTRL_0, _FLITFILTER, _DATA) | DRF_DEF(_NVLTLC_RX_LNK, _DEBUG_TP_CNTR_CTRL_0, _VCSETFILTERMODE, _INIT) | DRF_DEF(_NVLTLC_RX_LNK, _DEBUG_TP_CNTR_CTRL_0, _ENABLE, _ENABLE)); // Tx2 config NVSWITCH_LINK_WR32_IDX_LS10(device, linkNum, NVLTLC, _NVLTLC_TX_LNK, _DEBUG_TP_CNTR_CTRL_0, 2, DRF_DEF(_NVLTLC_TX_LNK, _DEBUG_TP_CNTR_CTRL_0, _UNIT, _FLITS) | DRF_DEF(_NVLTLC_TX_LNK, _DEBUG_TP_CNTR_CTRL_0, _FLITFILTER, _HEAD) | DRF_DEF(_NVLTLC_TX_LNK, _DEBUG_TP_CNTR_CTRL_0, _FLITFILTER, _AE) | DRF_DEF(_NVLTLC_TX_LNK, _DEBUG_TP_CNTR_CTRL_0, _FLITFILTER, _BE) | DRF_DEF(_NVLTLC_TX_LNK, _DEBUG_TP_CNTR_CTRL_0, _FLITFILTER, _DATA) | DRF_DEF(_NVLTLC_TX_LNK, _DEBUG_TP_CNTR_CTRL_0, _VCSETFILTERMODE, _INIT) | DRF_DEF(_NVLTLC_TX_LNK, _DEBUG_TP_CNTR_CTRL_0, _ENABLE, _ENABLE)); } void nvswitch_program_l1_scratch_reg_ls10 ( nvswitch_device *device, NvU32 linkNumber ) { NvU32 scrRegVal; NvU32 tempRegVal; // Read L1 register and store initial/VBIOS L1 Threshold Value in Scratch register tempRegVal = NVSWITCH_LINK_RD32_LS10(device, linkNumber, NVLIPT_LNK, _NVLIPT_LNK, _PWRM_L1_ENTER_THRESHOLD); scrRegVal = NVSWITCH_LINK_RD32_LS10(device, linkNumber, NVLIPT_LNK, _NVLIPT_LNK, _SCRATCH_WARM); // Update the scratch register value only if it has not been written to before if (scrRegVal == NV_NVLIPT_LNK_SCRATCH_WARM_DATA_INIT) { NVSWITCH_LINK_WR32_LS10(device, linkNumber, NVLIPT_LNK, _NVLIPT_LNK, _SCRATCH_WARM, tempRegVal); } } #define BUG_3797211_LS10_VBIOS_VERSION 0x9610410000 void nvswitch_init_lpwr_regs_ls10 ( nvlink_link *link ) { nvswitch_device *device = link->dev->pDevInfo; // NVSWITCH_BIOS_NVLINK_CONFIG *bios_config; NvU32 linkNum = link->linkNumber; NvU32 tempRegVal, lpEntryThreshold; NvU8 softwareDesired; NvBool bLpEnable; NvU64 biosVersion; if (device->regkeys.enable_pm == NV_SWITCH_REGKEY_ENABLE_PM_NO) { return; } if (nvswitch_lib_get_bios_version(device, &biosVersion) != NVL_SUCCESS) { NVSWITCH_PRINT(device, WARN, "%s Get VBIOS version failed.\n", __FUNCTION__); biosVersion = 0; } // bios_config = nvswitch_get_bios_nvlink_config(device); if (biosVersion >= BUG_3797211_LS10_VBIOS_VERSION) { // IC Enter Threshold if (device->regkeys.lp_threshold == NV_SWITCH_REGKEY_SET_LP_THRESHOLD_DEFAULT) { // // Do nothing since VBIOS (version 96.10.41.00.00 and above) // sets the default L1 threshold. // Refer Bug 3797211 for more info. // } else { lpEntryThreshold = device->regkeys.lp_threshold; tempRegVal = 0; tempRegVal = FLD_SET_DRF_NUM(_NVLIPT, _LNK_PWRM_L1_ENTER_THRESHOLD, _THRESHOLD, lpEntryThreshold, tempRegVal); NVSWITCH_LINK_WR32_LS10(device, linkNum, NVLIPT_LNK, _NVLIPT_LNK, _PWRM_L1_ENTER_THRESHOLD, tempRegVal); } } else { // IC Enter Threshold if (device->regkeys.lp_threshold == NV_SWITCH_REGKEY_SET_LP_THRESHOLD_DEFAULT) { lpEntryThreshold = 1; } else { lpEntryThreshold = device->regkeys.lp_threshold; } tempRegVal = 0; tempRegVal = FLD_SET_DRF_NUM(_NVLIPT, _LNK_PWRM_L1_ENTER_THRESHOLD, _THRESHOLD, lpEntryThreshold, tempRegVal); NVSWITCH_LINK_WR32_LS10(device, linkNum, NVLIPT_LNK, _NVLIPT_LNK, _PWRM_L1_ENTER_THRESHOLD, tempRegVal); } //LP Entry Enable bLpEnable = NV_TRUE; softwareDesired = (bLpEnable) ? 0x1 : 0x0; tempRegVal = NVSWITCH_LINK_RD32_LS10(device, linkNum, NVLIPT_LNK, _NVLIPT_LNK, _PWRM_CTRL); tempRegVal = FLD_SET_DRF_NUM(_NVLIPT, _LNK_PWRM_CTRL, _L1_SOFTWARE_DESIRED, softwareDesired, tempRegVal); NVSWITCH_LINK_WR32_LS10(device, linkNum, NVLIPT_LNK, _NVLIPT_LNK, _PWRM_CTRL, tempRegVal); } void nvswitch_corelib_training_complete_ls10 ( nvlink_link *link ) { nvswitch_device *device = link->dev->pDevInfo; nvswitch_init_dlpl_interrupts(link); _nvswitch_configure_reserved_throughput_counters(link); if (nvswitch_lib_notify_client_events(device, NVSWITCH_DEVICE_EVENT_PORT_UP) != NVL_SUCCESS) { NVSWITCH_PRINT(device, ERROR, "%s: Failed to notify PORT_UP event\n", __FUNCTION__); } return; } NvlStatus nvswitch_wait_for_tl_request_ready_ls10 ( nvlink_link *link ) { nvswitch_device *device = link->dev->pDevInfo; NVSWITCH_MINION_ALI_DEBUG_REGISTERS params; NvU32 nvldlErrCntl, nvldlTopLinkState, nvldlTopIntr, linkStateRequest; NvlStatus status = nvswitch_wait_for_tl_request_ready_lr10(link); if(status == -NVL_ERR_GENERIC) { linkStateRequest = NVSWITCH_LINK_RD32_LS10(device, link->linkNumber, NVLIPT_LNK , _NVLIPT_LNK , _CTRL_LINK_STATE_REQUEST); nvldlErrCntl = NVSWITCH_LINK_RD32_LS10(device, link->linkNumber, NVLDL, _NVLDL_RX_RXSLSM , _ERR_CNTL); nvldlTopLinkState = NVSWITCH_LINK_RD32_LS10(device, link->linkNumber, NVLDL, _NVLDL_TOP , _LINK_STATE); nvldlTopIntr = NVSWITCH_LINK_RD32_LS10(device, link->linkNumber, NVLDL, _NVLDL_TOP , _INTR); nvswitch_minion_get_ali_debug_registers_ls10(device, link, ¶ms); NVSWITCH_PRINT(device, ERROR, "%s: Ali Training failed on link #%d!:\n" "NV_NVLIPT_LNK_CTRL_LINK_STATE_REQUEST = 0x%x, " "NV_NVLDL_RXSLSM_ERR_CNTL = 0x%x,\n" "NV_NVLDL_TOP_LINK_STATE = 0x%x,\n" "NV_NVLDL_TOP_INTR = 0x%x,\n" "Minion DLSTAT MN00 = 0x%x\n" "Minion DLSTAT UC01 = 0x%x\n" "Minion DLSTAT UC01 = 0x%x\n", __FUNCTION__, link->linkNumber, linkStateRequest, nvldlErrCntl, nvldlTopLinkState, nvldlTopIntr, params.dlstatMn00, params.dlstatUc01, params.dlstatLinkIntr); NVSWITCH_PRINT_SXID(device, NVSWITCH_ERR_HW_NVLIPT_LNK_ALI_TRAINING_FAIL, "ALI Training failure. Info 0x%x%x%x%x%x%x%x\n", params.dlstatMn00, params.dlstatUc01, params.dlstatLinkIntr, nvldlTopLinkState, nvldlTopIntr, nvldlErrCntl, linkStateRequest); } return status; } static NvlStatus _nvswitch_init_dl_pll ( nvlink_link *link ) { nvswitch_device *device = link->dev->pDevInfo; NvlStatus status; status = nvswitch_minion_send_command(device, link->linkNumber, NV_MINION_NVLINK_DL_CMD_COMMAND_INITPLL, 0); if (status != NVL_SUCCESS) { NVSWITCH_PRINT(device, ERROR, "%s: INITPLL failed for link %d.\n", __FUNCTION__, link->linkNumber); NVSWITCH_ASSERT_INFO(NV_ERR_NVLINK_CLOCK_ERROR, NVBIT64(link->linkNumber), INITPLL_ERROR); return NV_ERR_NVLINK_CLOCK_ERROR; } status = nvswitch_minion_send_command(device, link->linkNumber, NV_MINION_NVLINK_DL_CMD_COMMAND_INITPHY, 0); if (status != NVL_SUCCESS) { NVSWITCH_PRINT(device, ERROR, "%s: INITPHY failed for link %d.\n", __FUNCTION__, link->linkNumber); NVSWITCH_ASSERT_INFO(NV_ERR_NVLINK_INIT_ERROR, NVBIT64(link->linkNumber), INITPHY_ERROR); return NV_ERR_NVLINK_INIT_ERROR; } return NVL_SUCCESS; } NvlStatus nvswitch_corelib_set_tx_mode_ls10 ( nvlink_link *link, NvU64 mode, NvU32 flags ) { nvswitch_device *device = link->dev->pDevInfo; NvU32 val; NvlStatus status = NVL_SUCCESS; if (!NVSWITCH_IS_LINK_ENG_VALID_LS10(device, NVLDL, link->linkNumber)) { NVSWITCH_PRINT(device, ERROR, "%s: link #%d invalid\n", __FUNCTION__, link->linkNumber); return -NVL_UNBOUND_DEVICE; } // check if link is in reset if (nvswitch_is_link_in_reset(device, link)) { NVSWITCH_PRINT(device, ERROR, "%s: link #%d is still in reset, cannot change sub-link state\n", __FUNCTION__, link->linkNumber); return -NVL_ERR_INVALID_STATE; } val = NVSWITCH_LINK_RD32_LS10(device, link->linkNumber, NVLDL, _NVLDL_TX, _SLSM_STATUS_TX); // Check if Sublink State Machine is ready to accept a sublink change request. status = nvswitch_poll_sublink_state(device, link); if (status != NVL_SUCCESS) { NVSWITCH_PRINT(device, ERROR, "%s : SLSM not ready to accept a state change request for(%s):(%s).\n", __FUNCTION__, device->name, link->linkName); return status; } switch (mode) { case NVLINK_SUBLINK_STATE_TX_COMMON_MODE: { val = _nvswitch_init_dl_pll(link); if (val != NVL_SUCCESS) { return val; } break; } default: { status = nvswitch_corelib_set_tx_mode_lr10(link, mode, flags); } } return status; } NvU32 nvswitch_get_sublink_width_ls10 ( nvswitch_device *device, NvU32 linkNumber ) { NvU32 data = NVSWITCH_LINK_RD32_LS10(device, linkNumber, NVLIPT, _NVLIPT_COMMON, _TOPOLOGY_LOCAL_LINK_CONFIGURATION); return DRF_VAL(_NVLIPT_COMMON, _TOPOLOGY_LOCAL_LINK_CONFIGURATION, _NUM_LANES_PER_LINK, data); } void nvswitch_corelib_get_uphy_load_ls10 ( nvlink_link *link, NvBool *bUnlocked ) { *bUnlocked = NV_FALSE; } NvlStatus nvswitch_corelib_set_dl_link_mode_ls10 ( nvlink_link *link, NvU64 mode, NvU32 flags ) { nvswitch_device *device = link->dev->pDevInfo; NvU32 val; NvlStatus status = NVL_SUCCESS; NvBool keepPolling; NVSWITCH_TIMEOUT timeout; if (!NVSWITCH_IS_LINK_ENG_VALID_LS10(device, NVLDL, link->linkNumber)) { NVSWITCH_PRINT(device, ERROR, "%s: link #%d invalid\n", __FUNCTION__, link->linkNumber); return -NVL_UNBOUND_DEVICE; } switch (mode) { case NVLINK_LINKSTATE_SAFE: { // check if link is in reset if (nvswitch_is_link_in_reset(device, link)) { NVSWITCH_PRINT(device, ERROR, "%s: link #%d is still in reset, cannot change link state\n", __FUNCTION__, link->linkNumber); return NVL_ERR_INVALID_STATE; } NVSWITCH_PRINT(device, INFO, "%s : Link state request to Safe for (%s):(%s) not needed. Skipping.\n", __FUNCTION__, device->name, link->linkName); break; } case NVLINK_LINKSTATE_HS: { // check if link is in reset if (nvswitch_is_link_in_reset(device, link)) { NVSWITCH_PRINT(device, ERROR, "%s: link #%d is still in reset, cannot change link state\n", __FUNCTION__, link->linkNumber); return -NVL_ERR_INVALID_STATE; } NVSWITCH_PRINT(device, INFO, "%s : Link state request to Active for (%s):(%s) not needed. Skipping.\n", __FUNCTION__, device->name, link->linkName); break; } case NVLINK_LINKSTATE_INITPHASE1: { // Apply appropriate SIMMODE settings status = nvswitch_minion_set_sim_mode_ls10(device, link); if (status != NVL_SUCCESS) { return NV_ERR_NVLINK_CONFIGURATION_ERROR; } // Apply appropriate SMF settings status = nvswitch_minion_set_smf_settings_ls10(device, link); if (status != NVL_SUCCESS) { return NV_ERR_NVLINK_CONFIGURATION_ERROR; } // Apply appropriate UPHY Table settings status = nvswitch_minion_select_uphy_tables_ls10(device, link); if (status != NVL_SUCCESS) { return NV_ERR_NVLINK_CONFIGURATION_ERROR; } // Before INITPHASE1, apply NEA setting nvswitch_setup_link_loopback_mode(device, link->linkNumber); status = nvswitch_minion_send_command(device, link->linkNumber, NV_MINION_NVLINK_DL_CMD_COMMAND_INITPHASE1, 0); if (status != NVL_SUCCESS) { NVSWITCH_PRINT(device, ERROR, "%s : INITPHASE1 failed for link (%s):(%s).\n", __FUNCTION__, device->name, link->linkName); NVSWITCH_ASSERT_INFO(NV_ERR_NVLINK_CONFIGURATION_ERROR, NVBIT64(link->linkNumber), INITPHASE1_ERROR); return NV_ERR_NVLINK_CONFIGURATION_ERROR; } break; } case NVLINK_LINKSTATE_POST_INITOPTIMIZE: { // Poll for TRAINING_GOOD status = nvswitch_minion_get_initoptimize_status_ls10(device, link->linkNumber); if (status != NVL_SUCCESS) { NVSWITCH_PRINT(device, ERROR, "%s Error polling for INITOPTIMIZE TRAINING_GOOD. Link (%s):(%s)\n", __FUNCTION__, device->name, link->linkName); NVSWITCH_ASSERT_INFO(NV_ERR_NVLINK_TRAINING_ERROR, NVBIT64(link->linkNumber), INITOPTIMIZE_ERROR); return NV_ERR_NVLINK_TRAINING_ERROR; } break; } case NVLINK_LINKSTATE_INITTL: { status = nvswitch_minion_send_command(device, link->linkNumber, NV_MINION_NVLINK_DL_CMD_COMMAND_INITTL, 0); if (status != NVL_SUCCESS) { NVSWITCH_PRINT(device, ERROR, "%s : INITTL failed for link (%s):(%s).\n", __FUNCTION__, device->name, link->linkName); NVSWITCH_ASSERT_INFO(NV_ERR_NVLINK_TRAINING_ERROR, NVBIT64(link->linkNumber), INITTL_ERROR); return NV_ERR_NVLINK_CONFIGURATION_ERROR; } break; } case NVLINK_LINKSTATE_INITOPTIMIZE: { return nvswitch_corelib_set_dl_link_mode_lr10(link, mode, flags); } case NVLINK_LINKSTATE_INITPHASE5: { status = nvswitch_minion_send_command(device, link->linkNumber, NV_MINION_NVLINK_DL_CMD_COMMAND_INITPHASE5A, 0); if (status != NVL_SUCCESS) { NVSWITCH_PRINT(device, ERROR, "%s : INITPHASE5A failed to be called for link (%s):(%s).\n", __FUNCTION__, device->name, link->linkName); NVSWITCH_ASSERT_INFO(NV_ERR_NVLINK_TRAINING_ERROR, NVBIT64(link->linkNumber), INITPHASE5_ERROR); return NV_ERR_NVLINK_CONFIGURATION_ERROR; } nvswitch_timeout_create(10 * NVSWITCH_INTERVAL_1MSEC_IN_NS, &timeout); do { keepPolling = (nvswitch_timeout_check(&timeout)) ? NV_FALSE : NV_TRUE; val = NVSWITCH_LINK_RD32_LS10(device, link->linkNumber, NVLDL, _NVLPHYCTL_COMMON, _PSAVE_UCODE_CTRL_STS); if(FLD_TEST_DRF(_NVLPHYCTL_COMMON, _PSAVE_UCODE_CTRL_STS, _PMSTS, _PSL0, val)) { break; } if(!keepPolling) { NVSWITCH_PRINT(device, ERROR, "%s : Failed to poll for L0 on link (%s):(%s).\n", __FUNCTION__, device->name, link->linkName); NVSWITCH_ASSERT_INFO(NV_ERR_NVLINK_TRAINING_ERROR, NVBIT64(link->linkNumber), INITPHASE5_ERROR); return NV_ERR_NVLINK_CONFIGURATION_ERROR; } } while (keepPolling); break; } default: { status = nvswitch_corelib_set_dl_link_mode_lr10(link, mode, flags); } } return status; } NvlStatus nvswitch_corelib_get_rx_detect_ls10 ( nvlink_link *link ) { NvlStatus status; nvswitch_device *device = link->dev->pDevInfo; status = nvswitch_minion_get_rxdet_status_ls10(device, link->linkNumber); if (status != NVL_SUCCESS) { NVSWITCH_PRINT(device, WARN, "%s: Get RXDET failed for link %d.\n", __FUNCTION__, link->linkNumber); return status; } return NVL_SUCCESS; } void nvswitch_reset_persistent_link_hw_state_ls10 ( nvswitch_device *device, NvU32 linkNumber ) { NvU32 clocksMask = NVSWITCH_PER_LINK_CLOCK_SET(RXCLK)|NVSWITCH_PER_LINK_CLOCK_SET(TXCLK)| NVSWITCH_PER_LINK_CLOCK_SET(NCISOCCLK); nvlink_link *link = nvswitch_get_link(device, linkNumber); if ((link == NULL) || nvswitch_is_link_in_reset(device, link)) { return; } // clear DL error counters (void)nvswitch_minion_send_command(device, linkNumber, NV_MINION_NVLINK_DL_CMD_COMMAND_DLSTAT_CLR_DLERRCNT, 0); // If TLC is not up then return if (!nvswitch_are_link_clocks_on_ls10(device, link, clocksMask)) { return; } // SETUPTC called to reset and setup throughput counters (void)nvswitch_minion_send_command(device, linkNumber, NV_MINION_NVLINK_DL_CMD_COMMAND_SETUPTC , 0x4); // clear miscellaneous TLC counters and registers (void)nvswitch_minion_send_command(device, linkNumber, NV_MINION_NVLINK_DL_CMD_COMMAND_CLR_TLC_MISC_REGS, 0); } NvlStatus nvswitch_corelib_get_tl_link_mode_ls10 ( nvlink_link *link, NvU64 *mode ) { #if defined(INCLUDE_NVLINK_LIB) nvswitch_device *device = link->dev->pDevInfo; NvU32 link_state; NvU32 val = 0; NvlStatus status = NVL_SUCCESS; *mode = NVLINK_LINKSTATE_OFF; if (!NVSWITCH_IS_LINK_ENG_VALID_LS10(device, NVLDL, link->linkNumber)) { NVSWITCH_PRINT(device, ERROR, "%s: link #%d invalid\n", __FUNCTION__, link->linkNumber); return -NVL_UNBOUND_DEVICE; } // check if links are in reset if (nvswitch_is_link_in_reset(device, link)) { *mode = NVLINK_LINKSTATE_RESET; return NVL_SUCCESS; } // Read state from NVLIPT HW val = NVSWITCH_LINK_RD32_LS10(device, link->linkNumber, NVLIPT_LNK, _NVLIPT_LNK, _CTRL_LINK_STATE_STATUS); link_state = DRF_VAL(_NVLIPT_LNK, _CTRL_LINK_STATE_STATUS, _CURRENTLINKSTATE, val); switch(link_state) { case NV_NVLIPT_LNK_CTRL_LINK_STATE_STATUS_CURRENTLINKSTATE_ACTIVE: // If using ALI, ensure that the request to active completed if (link->dev->enableALI) { status = nvswitch_wait_for_tl_request_ready_ls10(link); } *mode = (status == NVL_SUCCESS) ? NVLINK_LINKSTATE_HS:NVLINK_LINKSTATE_OFF; break; case NV_NVLIPT_LNK_CTRL_LINK_STATE_STATUS_CURRENTLINKSTATE_L2: *mode = NVLINK_LINKSTATE_SLEEP; break; case NV_NVLIPT_LNK_CTRL_LINK_STATE_STATUS_CURRENTLINKSTATE_CONTAIN: *mode = NVLINK_LINKSTATE_CONTAIN; break; case NV_NVLIPT_LNK_CTRL_LINK_STATE_STATUS_CURRENTLINKSTATE_ACTIVE_PENDING: *mode = NVLINK_LINKSTATE_ACTIVE_PENDING; break; default: // Currently, only ACTIVE, L2 and CONTAIN states are supported return NVL_ERR_INVALID_STATE; break; } #endif return status; } NvBool nvswitch_is_link_in_reset_ls10 ( nvswitch_device *device, nvlink_link *link ) { NvU32 clkStatus; NvU32 resetRequestStatus; clkStatus = NVSWITCH_LINK_RD32_LS10(device, link->linkNumber, NVLIPT_LNK, _NVLIPT_LNK, _CTRL_CLK_CTRL); // Read the reset request register resetRequestStatus = NVSWITCH_LINK_RD32_LS10(device, link->linkNumber, NVLIPT_LNK, _NVLIPT_LNK, _RESET_RSTSEQ_LINK_RESET); // // For link to be in reset either of 2 conditions should be true // 1. On a cold-boot the RESET_RSTSEQ status should be ASSERTED reset // 2. A link's current TL link state should be _RESET // and all of the per link clocks, RXCLK, TXCLK and NCISOCCLK, should be off // if ((DRF_VAL(_NVLIPT_LNK, _RESET_RSTSEQ_LINK_RESET, _LINK_RESET_STATUS, resetRequestStatus) == NV_NVLIPT_LNK_RESET_RSTSEQ_LINK_RESET_LINK_RESET_STATUS_ASSERTED) || (FLD_TEST_DRF(_NVLIPT_LNK, _CTRL_CLK_CTRL, _RXCLK_STS, _OFF, clkStatus) && FLD_TEST_DRF(_NVLIPT_LNK, _CTRL_CLK_CTRL, _TXCLK_STS, _OFF, clkStatus) && FLD_TEST_DRF(_NVLIPT_LNK, _CTRL_CLK_CTRL, _NCISOCCLK_STS, _OFF, clkStatus))) { return NV_TRUE; } return NV_FALSE; } void nvswitch_init_buffer_ready_ls10 ( nvswitch_device *device, nvlink_link *link, NvBool bNportBufferReady ) { NvU32 val; NvU32 linkNum = link->linkNumber; NvU64 forcedConfigLinkMask; NvU32 localLinkNumber = linkNum % NVSWITCH_LINKS_PER_MINION_LS10; NvU32 regData; regData = NVSWITCH_LINK_RD32_LS10(device, link->linkNumber, NVLTLC, _NVLTLC_TX_SYS, _CTRL_BUFFER_READY); // If buffer ready is set then return if (FLD_TEST_DRF(_NVLTLC, _TX_SYS_CTRL_BUFFER_READY, _BUFFERRDY, _ENABLE, regData)) { return; } forcedConfigLinkMask = ((NvU64)device->regkeys.chiplib_forced_config_link_mask) + ((NvU64)device->regkeys.chiplib_forced_config_link_mask2 << 32); // // Use legacy LS10 function to set buffer ready if // running with forced config since MINION is not // booted // if (forcedConfigLinkMask != 0) { nvswitch_init_buffer_ready_lr10(device, link, bNportBufferReady); } if (FLD_TEST_DRF(_SWITCH_REGKEY, _SKIP_BUFFER_READY, _TLC, _NO, device->regkeys.skip_buffer_ready)) { NVSWITCH_MINION_WR32_LS10(device, NVSWITCH_GET_LINK_ENG_INST(device, linkNum, MINION), _MINION, _NVLINK_DL_CMD_DATA(localLinkNumber), NV_MINION_NVLINK_DL_CMD_DATA_DATA_SET_BUFFER_READY_TX_AND_RX); nvswitch_minion_send_command(device, linkNum, NV_MINION_NVLINK_DL_CMD_COMMAND_SET_BUFFER_READY, 0); } if (bNportBufferReady && FLD_TEST_DRF(_SWITCH_REGKEY, _SKIP_BUFFER_READY, _NPORT, _NO, device->regkeys.skip_buffer_ready)) { val = DRF_NUM(_NPORT, _CTRL_BUFFER_READY, _BUFFERRDY, 0x1); NVSWITCH_LINK_WR32_LS10(device, linkNum, NPORT, _NPORT, _CTRL_BUFFER_READY, val); } } void nvswitch_apply_recal_settings_ls10 ( nvswitch_device *device, nvlink_link *link ) { NvU32 linkNumber = link->linkNumber; NvU32 regVal; NvU32 settingVal; // If no recal settings are set then return early if (device->regkeys.link_recal_settings == NV_SWITCH_REGKEY_LINK_RECAL_SETTINGS_NOP) { return; } regVal = NVSWITCH_LINK_RD32_LS10(device, linkNumber, NVLIPT_LNK, _NVLIPT_LNK, _CTRL_SYSTEM_LINK_CHANNEL_CTRL2); settingVal = DRF_VAL(_SWITCH_REGKEY, _LINK_RECAL_SETTINGS, _MIN_RECAL_TIME_MANTISSA, device->regkeys.link_recal_settings); if (settingVal != NV_SWITCH_REGKEY_LINK_RECAL_SETTINGS_NOP) { regVal = FLD_SET_DRF_NUM(_NVLIPT_LNK, _CTRL_SYSTEM_LINK_CHANNEL_CTRL2, _L1_MINIMUM_RECALIBRATION_TIME_MANTISSA, settingVal, regVal); } settingVal = DRF_VAL(_SWITCH_REGKEY, _LINK_RECAL_SETTINGS, _MIN_RECAL_TIME_EXPONENT, device->regkeys.link_recal_settings); if (settingVal != NV_SWITCH_REGKEY_LINK_RECAL_SETTINGS_NOP) { regVal = FLD_SET_DRF_NUM(_NVLIPT_LNK, _CTRL_SYSTEM_LINK_CHANNEL_CTRL2, _L1_MINIMUM_RECALIBRATION_TIME_EXPONENT, 0x2, regVal); } settingVal = DRF_VAL(_SWITCH_REGKEY, _LINK_RECAL_SETTINGS, _MAX_RECAL_PERIOD_MANTISSA, device->regkeys.link_recal_settings); if (settingVal != NV_SWITCH_REGKEY_LINK_RECAL_SETTINGS_NOP) { regVal = FLD_SET_DRF_NUM(_NVLIPT_LNK, _CTRL_SYSTEM_LINK_CHANNEL_CTRL2, _L1_MAXIMUM_RECALIBRATION_PERIOD_MANTISSA, 0xf, regVal); } settingVal = DRF_VAL(_SWITCH_REGKEY, _LINK_RECAL_SETTINGS, _MAX_RECAL_PERIOD_EXPONENT, device->regkeys.link_recal_settings); if (settingVal != NV_SWITCH_REGKEY_LINK_RECAL_SETTINGS_NOP) { regVal = FLD_SET_DRF_NUM(_NVLIPT_LNK, _CTRL_SYSTEM_LINK_CHANNEL_CTRL2, _L1_MAXIMUM_RECALIBRATION_PERIOD_EXPONENT, 0x3, regVal); } NVSWITCH_LINK_WR32_LS10(device, linkNumber, NVLIPT_LNK, _NVLIPT_LNK, _CTRL_SYSTEM_LINK_CHANNEL_CTRL2, regVal); return; } NvlStatus nvswitch_corelib_get_dl_link_mode_ls10 ( nvlink_link *link, NvU64 *mode ) { nvswitch_device *device = link->dev->pDevInfo; NvU32 link_state; NvU32 val = 0; NvU64 tlLinkMode; *mode = NVLINK_LINKSTATE_OFF; if (!NVSWITCH_IS_LINK_ENG_VALID_LS10(device, NVLDL, link->linkNumber)) { NVSWITCH_PRINT(device, ERROR, "%s: link #%d invalid\n", __FUNCTION__, link->linkNumber); return -NVL_UNBOUND_DEVICE; } // check if links are in reset if (nvswitch_is_link_in_reset(device, link)) { *mode = NVLINK_LINKSTATE_RESET; return NVL_SUCCESS; } val = NVSWITCH_LINK_RD32_LS10(device, link->linkNumber, NVLDL, _NVLDL_TOP, _LINK_STATE); link_state = DRF_VAL(_NVLDL_TOP, _LINK_STATE, _STATE, val); switch (link_state) { case NV_NVLDL_TOP_LINK_STATE_STATE_INIT: *mode = NVLINK_LINKSTATE_OFF; break; case NV_NVLDL_TOP_LINK_STATE_STATE_HWPCFG: case NV_NVLDL_TOP_LINK_STATE_STATE_HWCFG: *mode = NVLINK_LINKSTATE_DETECT; break; case NV_NVLDL_TOP_LINK_STATE_STATE_SWCFG: *mode = NVLINK_LINKSTATE_SAFE; break; case NV_NVLDL_TOP_LINK_STATE_STATE_ACTIVE: *mode = NVLINK_LINKSTATE_HS; break; case NV_NVLDL_TOP_LINK_STATE_STATE_SLEEP: if (device->hal.nvswitch_corelib_get_tl_link_mode(link, &tlLinkMode) != NVL_SUCCESS || tlLinkMode == NVLINK_LINKSTATE_SLEEP) { *mode = NVLINK_LINKSTATE_SLEEP; } else { *mode = NVLINK_LINKSTATE_HS; } break; case NV_NVLDL_TOP_LINK_STATE_STATE_FAULT: *mode = NVLINK_LINKSTATE_FAULT; break; case NV_NVLDL_TOP_LINK_STATE_STATE_RCVY_AC: case NV_NVLDL_TOP_LINK_STATE_STATE_RCVY_RX: *mode = NVLINK_LINKSTATE_RECOVERY; break; default: *mode = NVLINK_LINKSTATE_OFF; break; } return NVL_SUCCESS; } NvlStatus nvswitch_corelib_get_rx_mode_ls10 ( nvlink_link *link, NvU64 *mode, NvU32 *subMode ) { nvswitch_device *device = link->dev->pDevInfo; NvU32 rx_sublink_state; NvU32 data = 0; NvU64 dlLinkMode; *mode = NVLINK_SUBLINK_STATE_RX_OFF; if (!NVSWITCH_IS_LINK_ENG_VALID_LS10(device, NVLDL, link->linkNumber)) { NVSWITCH_PRINT(device, ERROR, "%s: link #%d invalid\n", __FUNCTION__, link->linkNumber); return -NVL_UNBOUND_DEVICE; } // check if link is in reset if (nvswitch_is_link_in_reset(device, link)) { *mode = NVLINK_SUBLINK_STATE_RX_OFF; return NVL_SUCCESS; } data = NVSWITCH_LINK_RD32_LS10(device, link->linkNumber, NVLDL, _NVLDL_RX, _SLSM_STATUS_RX); rx_sublink_state = DRF_VAL(_NVLDL_RX, _SLSM_STATUS_RX, _PRIMARY_STATE, data); // Return NVLINK_SUBLINK_SUBSTATE_RX_STABLE for sub-state *subMode = NVLINK_SUBLINK_SUBSTATE_RX_STABLE; switch (rx_sublink_state) { case NV_NVLDL_RX_SLSM_STATUS_RX_PRIMARY_STATE_HS: *mode = NVLINK_SUBLINK_STATE_RX_HS; break; case NV_NVLDL_RX_SLSM_STATUS_RX_PRIMARY_STATE_TRAIN: *mode = NVLINK_SUBLINK_STATE_RX_TRAIN; break; case NV_NVLDL_RX_SLSM_STATUS_RX_PRIMARY_STATE_SAFE: *mode = NVLINK_SUBLINK_STATE_RX_SAFE; break; case NV_NVLDL_RX_SLSM_STATUS_RX_PRIMARY_STATE_OFF: if (device->hal.nvswitch_corelib_get_dl_link_mode(link, &dlLinkMode) != NVL_SUCCESS || dlLinkMode != NVLINK_LINKSTATE_HS) { *mode = NVLINK_SUBLINK_STATE_RX_OFF; } else { *mode = NVLINK_SUBLINK_STATE_RX_LOW_POWER; } break; default: *mode = NVLINK_SUBLINK_STATE_RX_OFF; break; } return NVL_SUCCESS; } NvlStatus nvswitch_corelib_get_tx_mode_ls10 ( nvlink_link *link, NvU64 *mode, NvU32 *subMode ) { nvswitch_device *device = link->dev->pDevInfo; NvU32 tx_sublink_state; NvU64 dlLinkMode; NvU32 data = 0; *mode = NVLINK_SUBLINK_STATE_TX_OFF; if (!NVSWITCH_IS_LINK_ENG_VALID_LS10(device, NVLDL, link->linkNumber)) { NVSWITCH_PRINT(device, ERROR, "%s: link #%d invalid\n", __FUNCTION__, link->linkNumber); return -NVL_UNBOUND_DEVICE; } // check if link is in reset if (nvswitch_is_link_in_reset(device, link)) { *mode = NVLINK_SUBLINK_STATE_TX_OFF; return NVL_SUCCESS; } data = NVSWITCH_LINK_RD32_LS10(device, link->linkNumber, NVLDL, _NVLDL_TX, _SLSM_STATUS_TX); tx_sublink_state = DRF_VAL(_NVLDL_TX, _SLSM_STATUS_TX, _PRIMARY_STATE, data); // Return NVLINK_SUBLINK_SUBSTATE_TX_STABLE for sub-state *subMode = NVLINK_SUBLINK_SUBSTATE_TX_STABLE; switch (tx_sublink_state) { case NV_NVLDL_TX_SLSM_STATUS_TX_PRIMARY_STATE_HS: *mode = NVLINK_SUBLINK_STATE_TX_HS; break; case NV_NVLDL_TX_SLSM_STATUS_TX_PRIMARY_STATE_TRAIN: *mode = NVLINK_SUBLINK_STATE_TX_TRAIN; break; case NV_NVLDL_TX_SLSM_STATUS_TX_PRIMARY_STATE_SAFE: *mode = NVLINK_SUBLINK_STATE_TX_SAFE; break; case NV_NVLDL_TX_SLSM_STATUS_TX_PRIMARY_STATE_OFF: if (device->hal.nvswitch_corelib_get_dl_link_mode(link, &dlLinkMode) != NVL_SUCCESS || dlLinkMode != NVLINK_LINKSTATE_HS) { *mode = NVLINK_SUBLINK_STATE_TX_OFF; } else { *mode = NVLINK_SUBLINK_STATE_TX_LOW_POWER; } break; default: *mode = NVLINK_SUBLINK_STATE_TX_OFF; break; } return NVL_SUCCESS; } NvlStatus nvswitch_launch_ALI_link_training_ls10 ( nvswitch_device *device, nvlink_link *link, NvBool bSync ) { NvlStatus status = NVL_SUCCESS; if ((link == NULL) || !NVSWITCH_IS_LINK_ENG_VALID_LS10(device, NVLIPT_LNK, link->linkNumber) || (link->linkNumber >= NVSWITCH_NVLINK_MAX_LINKS)) { return -NVL_UNBOUND_DEVICE; } if (!nvswitch_is_link_in_reset(device, link)) { return NVL_SUCCESS; } NVSWITCH_PRINT(device, INFO, "%s: ALI launching on link: 0x%x\n", __FUNCTION__, link->linkNumber); // Apply appropriate SIMMODE settings status = nvswitch_minion_set_sim_mode_ls10(device, link); if (status != NVL_SUCCESS) { return NV_ERR_NVLINK_CONFIGURATION_ERROR; } // Apply appropriate SMF settings status = nvswitch_minion_set_smf_settings_ls10(device, link); if (status != NVL_SUCCESS) { return NV_ERR_NVLINK_CONFIGURATION_ERROR; } // Apply appropriate UPHY Table settings status = nvswitch_minion_select_uphy_tables_ls10(device, link); if (status != NVL_SUCCESS) { return NV_ERR_NVLINK_CONFIGURATION_ERROR; } // Before INITPHASE1, apply NEA setting nvswitch_setup_link_loopback_mode(device, link->linkNumber); // // Request active, but don't block. FM will come back and check // active link status by blocking on this TLREQ's completion // status = nvswitch_request_tl_link_state_ls10(link, NV_NVLIPT_LNK_CTRL_LINK_STATE_REQUEST_REQUEST_ACTIVE, bSync); if (status != NVL_SUCCESS) { NVSWITCH_PRINT(device, ERROR, "%s: TL link state request to active for ALI failed for link: 0x%x\n", __FUNCTION__, link->linkNumber); } return status; } void nvswitch_store_topology_information_ls10 ( nvswitch_device *device, nvlink_link *link ) { NvU32 tempval; link->bInitnegotiateConfigGood = NV_TRUE; link->remoteSid = NVSWITCH_LINK_RD32_LS10(device, link->linkNumber, NVLIPT_LNK, _NVLIPT_LNK, _TOPOLOGY_REMOTE_CHIP_SID_HI); link->remoteSid = link->remoteSid << 32; link->remoteSid |= NVSWITCH_LINK_RD32_LS10(device, link->linkNumber, NVLIPT_LNK, _NVLIPT_LNK, _TOPOLOGY_REMOTE_CHIP_SID_LO); tempval = NVSWITCH_LINK_RD32_LS10(device, link->linkNumber, NVLIPT_LNK, _NVLIPT_LNK, _TOPOLOGY_REMOTE_LINK_INFO); link->remoteLinkId = DRF_VAL(_NVLIPT_LNK, _TOPOLOGY_REMOTE_LINK_INFO, _LINK_NUMBER, tempval); link->localSid = NVSWITCH_LINK_RD32_LS10(device, link->linkNumber, NVLIPT, _NVLIPT_COMMON, _TOPOLOGY_LOCAL_CHIP_SID_HI); link->localSid = link->localSid << 32; link->localSid |= NVSWITCH_LINK_RD32_LS10(device, link->linkNumber, NVLIPT, _NVLIPT_COMMON, _TOPOLOGY_LOCAL_CHIP_SID_LO); tempval = NVSWITCH_LINK_RD32_LS10(device, link->linkNumber, NVLIPT_LNK, _NVLIPT_LNK, _TOPOLOGY_REMOTE_CHIP_TYPE); // Update the remoteDeviceType with NV2080_CTRL_NVLINK_DEVICE_INFO_DEVICE_TYPE values. switch(tempval) { case NV_NVLIPT_LNK_TOPOLOGY_REMOTE_CHIP_TYPE_TYPE_NV3P0AMP: case NV_NVLIPT_LNK_TOPOLOGY_REMOTE_CHIP_TYPE_TYPE_NV4P0HOP: link->remoteDeviceType = NVSWITCH_NVLINK_DEVICE_INFO_DEVICE_TYPE_GPU; break; case NV_NVLIPT_LNK_TOPOLOGY_REMOTE_CHIP_TYPE_TYPE_NV3P0LRK: case NV_NVLIPT_LNK_TOPOLOGY_REMOTE_CHIP_TYPE_TYPE_NV4P0LAG: link->remoteDeviceType = NVSWITCH_NVLINK_DEVICE_INFO_DEVICE_TYPE_SWITCH; break; default: link->remoteDeviceType = NVSWITCH_NVLINK_DEVICE_INFO_DEVICE_TYPE_NONE; break; } } void nvswitch_get_error_rate_threshold_ls10 ( nvlink_link *link ) { nvswitch_device *device = link->dev->pDevInfo; NvU32 linkNumber = link->linkNumber; NvU32 crcRegVal; crcRegVal = NVSWITCH_LINK_RD32_LS10(device, linkNumber, NVLDL, _NVLDL_RX, _ERROR_RATE_CTRL); link->errorThreshold.thresholdMan = DRF_VAL(_NVLDL_RX, _ERROR_RATE_CTRL, _SHORT_THRESHOLD_MAN, crcRegVal); link->errorThreshold.thresholdExp = DRF_VAL(_NVLDL_RX, _ERROR_RATE_CTRL, _SHORT_THRESHOLD_EXP, crcRegVal); link->errorThreshold.timescaleMan = DRF_VAL(_NVLDL_RX, _ERROR_RATE_CTRL, _SHORT_TIMESCALE_MAN, crcRegVal); link->errorThreshold.timescaleExp = DRF_VAL(_NVLDL_RX, _ERROR_RATE_CTRL, _SHORT_TIMESCALE_EXP, crcRegVal); } void nvswitch_set_error_rate_threshold_ls10 ( nvlink_link *link, NvBool bSetDefault ) { nvswitch_device *device = link->dev->pDevInfo; NvU32 linkNumber = link->linkNumber; NvU32 crcShortRegkeyVal = device->regkeys.crc_bit_error_rate_short; NvU32 crcRegVal; ct_assert(DRF_BASE(NV_SWITCH_REGKEY_CRC_BIT_ERROR_RATE_SHORT_THRESHOLD_MAN) == DRF_BASE(NV_NVLDL_RX_ERROR_RATE_CTRL_SHORT_THRESHOLD_MAN)); ct_assert(DRF_EXTENT(NV_SWITCH_REGKEY_CRC_BIT_ERROR_RATE_SHORT_THRESHOLD_MAN) == DRF_EXTENT(NV_NVLDL_RX_ERROR_RATE_CTRL_SHORT_THRESHOLD_MAN)); ct_assert(DRF_BASE(NV_SWITCH_REGKEY_CRC_BIT_ERROR_RATE_SHORT_THRESHOLD_EXP) == DRF_BASE(NV_NVLDL_RX_ERROR_RATE_CTRL_SHORT_THRESHOLD_EXP)); ct_assert(DRF_EXTENT(NV_SWITCH_REGKEY_CRC_BIT_ERROR_RATE_SHORT_THRESHOLD_EXP) == DRF_EXTENT(NV_NVLDL_RX_ERROR_RATE_CTRL_SHORT_THRESHOLD_EXP)); ct_assert(DRF_BASE(NV_SWITCH_REGKEY_CRC_BIT_ERROR_RATE_SHORT_TIMESCALE_MAN) == DRF_BASE(NV_NVLDL_RX_ERROR_RATE_CTRL_SHORT_TIMESCALE_MAN)); ct_assert(DRF_EXTENT(NV_SWITCH_REGKEY_CRC_BIT_ERROR_RATE_SHORT_TIMESCALE_MAN) == DRF_EXTENT(NV_NVLDL_RX_ERROR_RATE_CTRL_SHORT_TIMESCALE_MAN)); ct_assert(DRF_BASE(NV_SWITCH_REGKEY_CRC_BIT_ERROR_RATE_SHORT_TIMESCALE_EXP) == DRF_BASE(NV_NVLDL_RX_ERROR_RATE_CTRL_SHORT_TIMESCALE_EXP)); ct_assert(DRF_EXTENT(NV_SWITCH_REGKEY_CRC_BIT_ERROR_RATE_SHORT_TIMESCALE_EXP) == DRF_EXTENT(NV_NVLDL_RX_ERROR_RATE_CTRL_SHORT_TIMESCALE_EXP)); crcRegVal = NVSWITCH_LINK_RD32_LS10(device, linkNumber, NVLDL, _NVLDL_RX, _ERROR_RATE_CTRL); // // Case 1: When a Regkey is provided. We use it to calculate crcRegVal. // // Case 2: When the bSetDefault variable is set to NV_FALSE. This can happen // when any client/application like NSCQ would provide specific values for // the error threshold. In this case we use those values to calculate crcRegVal. // // Case 3: In all other cases, we want the default values to be used, which are // provided in Bug 3365481. // if(crcShortRegkeyVal != NV_SWITCH_REGKEY_CRC_BIT_ERROR_RATE_SHORT_DEFAULT) { NvU32 shortRateMask; shortRateMask = DRF_SHIFTMASK(NV_SWITCH_REGKEY_CRC_BIT_ERROR_RATE_SHORT_THRESHOLD_MAN) | DRF_SHIFTMASK(NV_SWITCH_REGKEY_CRC_BIT_ERROR_RATE_SHORT_THRESHOLD_EXP) | DRF_SHIFTMASK(NV_SWITCH_REGKEY_CRC_BIT_ERROR_RATE_SHORT_TIMESCALE_MAN) | DRF_SHIFTMASK(NV_SWITCH_REGKEY_CRC_BIT_ERROR_RATE_SHORT_TIMESCALE_EXP); crcRegVal &= ~shortRateMask; crcRegVal |= crcShortRegkeyVal; link->errorThreshold.bUserConfig = NV_FALSE; link->errorThreshold.bInterruptTrigerred = NV_FALSE; } else if (!bSetDefault) { crcRegVal = FLD_SET_DRF_NUM(_NVLDL_RX, _ERROR_RATE_CTRL, _SHORT_THRESHOLD_MAN, link->errorThreshold.thresholdMan, crcRegVal); crcRegVal = FLD_SET_DRF_NUM(_NVLDL_RX, _ERROR_RATE_CTRL, _SHORT_THRESHOLD_EXP, link->errorThreshold.thresholdExp, crcRegVal); crcRegVal = FLD_SET_DRF_NUM(_NVLDL_RX, _ERROR_RATE_CTRL, _SHORT_TIMESCALE_MAN, link->errorThreshold.timescaleMan, crcRegVal); crcRegVal = FLD_SET_DRF_NUM(_NVLDL_RX, _ERROR_RATE_CTRL, _SHORT_TIMESCALE_EXP, link->errorThreshold.timescaleExp, crcRegVal); } else { // // Please refer to Bug 3365481 for details about the CRC_BIT_ERROR_RATE_SHORT // default values used below. // link->errorThreshold.thresholdMan = NV_NVLDL_CRC_BIT_ERROR_RATE_SHORT_THRESHOLD_MAN_DEFAULT; link->errorThreshold.thresholdExp = NV_NVLDL_CRC_BIT_ERROR_RATE_SHORT_THRESHOLD_EXP_DEFAULT; link->errorThreshold.timescaleMan = NV_NVLDL_CRC_BIT_ERROR_RATE_SHORT_TIMESCALE_MAN_DEFAULT; link->errorThreshold.timescaleExp = NV_NVLDL_CRC_BIT_ERROR_RATE_SHORT_TIMESCALE_EXP_DEFAULT; link->errorThreshold.bUserConfig = NV_FALSE; link->errorThreshold.bInterruptTrigerred = NV_FALSE; crcRegVal = FLD_SET_DRF_NUM(_NVLDL_RX, _ERROR_RATE_CTRL, _SHORT_THRESHOLD_MAN, link->errorThreshold.thresholdMan, crcRegVal); crcRegVal = FLD_SET_DRF_NUM(_NVLDL_RX, _ERROR_RATE_CTRL, _SHORT_THRESHOLD_EXP, link->errorThreshold.thresholdExp, crcRegVal); crcRegVal = FLD_SET_DRF_NUM(_NVLDL_RX, _ERROR_RATE_CTRL, _SHORT_TIMESCALE_MAN, link->errorThreshold.timescaleMan, crcRegVal); crcRegVal = FLD_SET_DRF_NUM(_NVLDL_RX, _ERROR_RATE_CTRL, _SHORT_TIMESCALE_EXP, link->errorThreshold.timescaleExp, crcRegVal); } NVSWITCH_LINK_WR32_LS10(device, linkNumber, NVLDL, _NVLDL_RX, _ERROR_RATE_CTRL, crcRegVal); } void nvswitch_configure_error_rate_threshold_interrupt_ls10 ( nvlink_link *link, NvBool bEnable ) { nvswitch_device *device = link->dev->pDevInfo; NvU32 linkNumber = link->linkNumber; NvU32 intrRegVal; link->errorThreshold.bInterruptEn = bEnable; intrRegVal = NVSWITCH_LINK_RD32_LS10(device, linkNumber, NVLDL, _NVLDL_TOP, _INTR_NONSTALL_EN); if (bEnable) { link->errorThreshold.bInterruptTrigerred = NV_FALSE; intrRegVal = FLD_SET_DRF_NUM(_NVLDL_TOP, _INTR_NONSTALL_EN, _RX_SHORT_ERROR_RATE, 1, intrRegVal); } else { intrRegVal = FLD_SET_DRF_NUM(_NVLDL_TOP, _INTR_NONSTALL_EN, _RX_SHORT_ERROR_RATE, 0, intrRegVal); } NVSWITCH_LINK_WR32_LS10(device, linkNumber, NVLDL, _NVLDL_TOP, _INTR_NONSTALL_EN, intrRegVal); } void nvswitch_init_dlpl_interrupts_ls10 ( nvlink_link *link ) { nvswitch_device *device = link->dev->pDevInfo; NvU32 linkNumber = link->linkNumber; // W1C any stale state. NVSWITCH_LINK_WR32_LS10(device, linkNumber, NVLDL, _NVLDL_TOP, _INTR, 0xffffffff); NVSWITCH_LINK_WR32_LS10(device, linkNumber, NVLDL, _NVLDL_TOP, _INTR_SW2, 0xffffffff); // Set the interrupt bits nvswitch_set_dlpl_interrupts_ls10(link); // Setup error rate thresholds nvswitch_set_error_rate_threshold_ls10(link, NV_TRUE); nvswitch_configure_error_rate_threshold_interrupt_ls10(link, NV_TRUE); } void nvswitch_set_dlpl_interrupts_ls10 ( nvlink_link *link ) { nvswitch_device *device = link->dev->pDevInfo; NvU32 linkNumber = link->linkNumber; // Stall tree routes to INTR_A which is connected to NVLIPT fatal tree NVSWITCH_LINK_WR32_LS10(device, linkNumber, NVLDL, _NVLDL_TOP, _INTR_STALL_EN, DRF_DEF(_NVLDL_TOP, _INTR_STALL_EN, _TX_REPLAY, _DISABLE) | DRF_DEF(_NVLDL_TOP, _INTR_STALL_EN, _TX_RECOVERY_SHORT, _DISABLE) | DRF_DEF(_NVLDL_TOP, _INTR_STALL_EN, _LTSSM_FAULT_UP, _ENABLE) | DRF_DEF(_NVLDL_TOP, _INTR_STALL_EN, _LTSSM_FAULT_DOWN, _ENABLE) | DRF_DEF(_NVLDL_TOP, _INTR_STALL_EN, _TX_FAULT_RAM, _ENABLE) | DRF_DEF(_NVLDL_TOP, _INTR_STALL_EN, _TX_FAULT_INTERFACE, _ENABLE) | DRF_DEF(_NVLDL_TOP, _INTR_STALL_EN, _TX_FAULT_SUBLINK_CHANGE, _DISABLE) | DRF_DEF(_NVLDL_TOP, _INTR_STALL_EN, _RX_FAULT_SUBLINK_CHANGE, _DISABLE) | DRF_DEF(_NVLDL_TOP, _INTR_STALL_EN, _RX_FAULT_DL_PROTOCOL, _ENABLE) | DRF_DEF(_NVLDL_TOP, _INTR_STALL_EN, _RX_SHORT_ERROR_RATE, _DISABLE) | DRF_DEF(_NVLDL_TOP, _INTR_STALL_EN, _RX_ILA_TRIGGER, _DISABLE) | DRF_DEF(_NVLDL_TOP, _INTR_STALL_EN, _RX_CRC_COUNTER, _DISABLE) | DRF_DEF(_NVLDL_TOP, _INTR_STALL_EN, _LTSSM_PROTOCOL, _DISABLE) | DRF_DEF(_NVLDL_TOP, _INTR_STALL_EN, _MINION_REQUEST, _DISABLE)); // NONSTALL -> NONFATAL NVSWITCH_LINK_WR32_LS10(device, linkNumber, NVLDL, _NVLDL_TOP, _INTR_NONSTALL_EN, DRF_DEF(_NVLDL_TOP, _INTR_NONSTALL_EN, _TX_REPLAY, _DISABLE) | DRF_DEF(_NVLDL_TOP, _INTR_NONSTALL_EN, _TX_RECOVERY_SHORT, _DISABLE) | DRF_DEF(_NVLDL_TOP, _INTR_NONSTALL_EN, _LTSSM_FAULT_UP, _DISABLE) | DRF_DEF(_NVLDL_TOP, _INTR_NONSTALL_EN, _TX_FAULT_RAM, _DISABLE) | DRF_DEF(_NVLDL_TOP, _INTR_NONSTALL_EN, _TX_FAULT_INTERFACE, _DISABLE) | DRF_DEF(_NVLDL_TOP, _INTR_NONSTALL_EN, _TX_FAULT_SUBLINK_CHANGE, _DISABLE) | DRF_DEF(_NVLDL_TOP, _INTR_NONSTALL_EN, _RX_FAULT_SUBLINK_CHANGE, _DISABLE) | DRF_DEF(_NVLDL_TOP, _INTR_NONSTALL_EN, _RX_FAULT_DL_PROTOCOL, _DISABLE) | DRF_DEF(_NVLDL_TOP, _INTR_NONSTALL_EN, _RX_SHORT_ERROR_RATE, _DISABLE) | DRF_DEF(_NVLDL_TOP, _INTR_NONSTALL_EN, _RX_ILA_TRIGGER, _DISABLE) | DRF_DEF(_NVLDL_TOP, _INTR_NONSTALL_EN, _RX_CRC_COUNTER, _ENABLE) | DRF_DEF(_NVLDL_TOP, _INTR_NONSTALL_EN, _LTSSM_PROTOCOL, _DISABLE) | DRF_DEF(_NVLDL_TOP, _INTR_NONSTALL_EN, _MINION_REQUEST, _DISABLE)); } static NvU32 _nvswitch_get_nvlink_linerate_ls10 ( nvswitch_device *device, NvU32 val ) { NvU32 lineRate = 0; switch (val) { case NV_SWITCH_REGKEY_SPEED_CONTROL_SPEED_100_00000G: lineRate = NV_NVLIPT_LNK_CTRL_SYSTEM_LINK_CLK_CTRL_LINE_RATE_100_00000_GBPS; break; case NV_SWITCH_REGKEY_SPEED_CONTROL_SPEED_106_25000G: lineRate = NV_NVLIPT_LNK_CTRL_SYSTEM_LINK_CLK_CTRL_LINE_RATE_106_25000_GBPS; break; default: NVSWITCH_PRINT(device, SETUP, "%s:ERROR LINE_RATE = 0x%x requested by regkey\n", __FUNCTION__, lineRate); lineRate = NV_NVLIPT_LNK_CTRL_SYSTEM_LINK_CLK_CTRL_LINE_RATE_ILLEGAL_LINE_RATE; } return lineRate; } void nvswitch_setup_link_system_registers_ls10 ( nvswitch_device *device, nvlink_link *link ) { NvU32 regval, fldval; NvU32 lineRate = 0; // LINE_RATE SYSTEM register if (device->regkeys.nvlink_speed_control != NV_SWITCH_REGKEY_SPEED_CONTROL_SPEED_DEFAULT) { regval = NVSWITCH_LINK_RD32_LS10(device, link->linkNumber, NVLIPT_LNK, _NVLIPT_LNK_CTRL_SYSTEM_LINK, _CLK_CTRL); lineRate = _nvswitch_get_nvlink_linerate_ls10(device, device->regkeys.nvlink_speed_control); regval = FLD_SET_DRF_NUM(_NVLIPT_LNK_CTRL_SYSTEM_LINK, _CLK_CTRL, _LINE_RATE, lineRate, regval); NVSWITCH_PRINT(device, SETUP, "%s: LINE_RATE = 0x%x requested by regkey\n", __FUNCTION__, lineRate); NVSWITCH_LINK_WR32_LS10(device, link->linkNumber, NVLIPT_LNK, _NVLIPT_LNK_CTRL_SYSTEM_LINK, _CLK_CTRL, regval); } // TXTRAIN SYSTEM register regval = NVSWITCH_LINK_RD32_LS10(device, link->linkNumber, NVLIPT_LNK, _NVLIPT_LNK_CTRL_SYSTEM_LINK, _CHANNEL_CTRL); fldval = DRF_VAL(_SWITCH_REGKEY, _TXTRAIN_CONTROL, _FOM_FORMAT, device->regkeys.txtrain_control); if (fldval != NV_SWITCH_REGKEY_TXTRAIN_CONTROL_FOM_FORMAT_NOP) { NVSWITCH_PRINT(device, SETUP, "%s: FOM_FORMAT = 0x%x requested by regkey\n", __FUNCTION__, fldval); regval = FLD_SET_DRF_NUM(_NVLIPT_LNK_CTRL_SYSTEM_LINK, _CHANNEL_CTRL, _TXTRAIN_FOM_FORMAT, fldval, regval); } fldval = DRF_VAL(_SWITCH_REGKEY, _TXTRAIN_CONTROL, _OPTIMIZATION_ALGORITHM, device->regkeys.txtrain_control); if (fldval != NV_SWITCH_REGKEY_TXTRAIN_CONTROL_OPTIMIZATION_ALGORITHM_NOP) { NVSWITCH_PRINT(device, SETUP, "%s: OPTIMIZATION_ALGORITHM = 0x%x requested by regkey\n", __FUNCTION__, fldval); regval = FLD_SET_DRF_NUM(_NVLIPT_LNK_CTRL_SYSTEM_LINK, _CHANNEL_CTRL, _TXTRAIN_OPTIMIZATION_ALGORITHM, fldval, regval); } fldval = DRF_VAL(_SWITCH_REGKEY, _TXTRAIN_CONTROL, _ADJUSTMENT_ALGORITHM, device->regkeys.txtrain_control); if (fldval != NV_SWITCH_REGKEY_TXTRAIN_CONTROL_ADJUSTMENT_ALGORITHM_NOP) { NVSWITCH_PRINT(device, SETUP, "%s: ADJUSTMENT_ALGORITHM = 0x%x requested by regkey\n", __FUNCTION__, fldval); regval = FLD_SET_DRF_NUM(_NVLIPT_LNK_CTRL_SYSTEM_LINK, _CHANNEL_CTRL, _TXTRAIN_ADJUSTMENT_ALGORITHM, fldval, regval); } fldval = DRF_VAL(_SWITCH_REGKEY, _TXTRAIN_CONTROL, _MINIMUM_TRAIN_TIME_MANTISSA, device->regkeys.txtrain_control); if (fldval != NV_SWITCH_REGKEY_TXTRAIN_CONTROL_MINIMUM_TRAIN_TIME_MANTISSA_NOP) { NVSWITCH_PRINT(device, SETUP, "%s: MINIMUM_TRAIN_TIME_MANTISSA = 0x%x requested by regkey\n", __FUNCTION__, fldval); regval = FLD_SET_DRF_NUM(_NVLIPT_LNK_CTRL_SYSTEM_LINK, _CHANNEL_CTRL, _TXTRAIN_MINIMUM_TRAIN_TIME_MANTISSA, fldval, regval); } fldval = DRF_VAL(_SWITCH_REGKEY, _TXTRAIN_CONTROL, _MINIMUM_TRAIN_TIME_EXPONENT, device->regkeys.txtrain_control); if (fldval != NV_SWITCH_REGKEY_TXTRAIN_CONTROL_MINIMUM_TRAIN_TIME_EXPONENT_NOP) { NVSWITCH_PRINT(device, SETUP, "%s: MINIMUM_TRAIN_TIME_EXPONENT = 0x%x requested by regkey\n", __FUNCTION__, fldval); regval = FLD_SET_DRF_NUM(_NVLIPT_LNK_CTRL_SYSTEM_LINK, _CHANNEL_CTRL, _TXTRAIN_MINIMUM_TRAIN_TIME_EXPONENT, fldval, regval); } NVSWITCH_LINK_WR32_LS10(device, link->linkNumber, NVLIPT_LNK, _NVLIPT_LNK_CTRL_SYSTEM_LINK, _CHANNEL_CTRL, regval); nvswitch_apply_recal_settings(device, link); return; } void nvswitch_load_link_disable_settings_ls10 ( nvswitch_device *device, nvlink_link *link ) { NvU32 regVal; // Read state from NVLIPT HW regVal = NVSWITCH_LINK_RD32_LS10(device, link->linkNumber, NVLIPT_LNK, _NVLIPT_LNK, _CTRL_LINK_STATE_STATUS); if (FLD_TEST_DRF(_NVLIPT_LNK, _CTRL_LINK_STATE_STATUS, _CURRENTLINKSTATE, _DISABLE, regVal)) { // Set link to invalid and unregister from corelib device->link[link->linkNumber].valid = NV_FALSE; nvlink_lib_unregister_link(link); nvswitch_destroy_link(link); } return; } void nvswitch_execute_unilateral_link_shutdown_ls10 ( nvlink_link *link ) { nvswitch_device *device = link->dev->pDevInfo; NvlStatus status = NVL_SUCCESS; NvU32 retry_count = 3; NvU32 link_state_request; NvU32 link_state; NvU32 stat_data = 0; NvU32 link_intr_subcode = MINION_OK; if (!NVSWITCH_IS_LINK_ENG_VALID_LS10(device, NVLDL, link->linkNumber)) { NVSWITCH_PRINT(device, ERROR, "%s: link #%d invalid\n", __FUNCTION__, link->linkNumber); return; } do { // // Perform unilateral shutdown // This follows "Unilateral variant" from NVLink 4.x Shutdown // // Status is explicitly ignored here since we are required to soldier-on // in this scenario // status = nvswitch_request_tl_link_state_ls10(link, NV_NVLIPT_LNK_CTRL_LINK_STATE_REQUEST_REQUEST_SHUTDOWN, NV_TRUE); if (status == NVL_SUCCESS) { return; } link_state_request = NVSWITCH_LINK_RD32_LS10(device, link->linkNumber, NVLIPT_LNK , _NVLIPT_LNK , _CTRL_LINK_STATE_REQUEST); link_state = DRF_VAL(_NVLIPT_LNK, _CTRL_LINK_STATE_REQUEST, _STATUS, link_state_request); if (nvswitch_minion_get_dl_status(device, link->linkNumber, NV_NVLSTAT_MN00, 0, &stat_data) == NVL_SUCCESS) { link_intr_subcode = DRF_VAL(_NVLSTAT, _MN00, _LINK_INTR_SUBCODE, stat_data); if ((link_state == NV_NVLIPT_LNK_CTRL_LINK_STATE_REQUEST_STATUS_MINION_REQUEST_FAIL) && (link_intr_subcode == MINION_ALARM_BUSY)) { NVSWITCH_PRINT(device, INFO, "%s: Retrying shutdown due to Minion DLCMD Fault subcode = 0x%x\n", __FUNCTION__, link_intr_subcode); // // We retry the shutdown sequence 3 times when we see a MINION_REQUEST_FAIL // or MINION_ALARM_BUSY // retry_count--; } else { break; } } else { // Querying MINION for link_intr_subcode failed so retry retry_count--; } } while (retry_count); NVSWITCH_PRINT(device, ERROR, "%s: NvLink Shutdown has failed for link %d\n", __FUNCTION__, link->linkNumber); return; } NvlStatus nvswitch_reset_and_train_link_ls10 ( nvswitch_device *device, nvlink_link *link ) { NvlStatus status = NVL_SUCCESS; NvU32 retry_count = 3; NvU32 link_state_request; NvU32 link_state; NvU32 stat_data; NvU32 link_intr_subcode = MINION_OK; nvswitch_execute_unilateral_link_shutdown_ls10(link); nvswitch_corelib_clear_link_state_ls10(link); // // When a link faults there could be a race between the driver requesting // reset and MINION processing Emergency Shutdown. Minion will notify if // such a collision happens and will deny the reset request, so try the // request up to 3 times // do { status = nvswitch_request_tl_link_state_ls10(link, NV_NVLIPT_LNK_CTRL_LINK_STATE_REQUEST_REQUEST_RESET, NV_TRUE); if (status == NVL_SUCCESS) { break; } else { link_state_request = NVSWITCH_LINK_RD32_LS10(device, link->linkNumber, NVLIPT_LNK , _NVLIPT_LNK , _CTRL_LINK_STATE_REQUEST); link_state = DRF_VAL(_NVLIPT_LNK, _CTRL_LINK_STATE_REQUEST, _STATUS, link_state_request); if (nvswitch_minion_get_dl_status(device, link->linkNumber, NV_NVLSTAT_MN00, 0, &stat_data) == NVL_SUCCESS) { link_intr_subcode = DRF_VAL(_NVLSTAT, _MN00, _LINK_INTR_SUBCODE, stat_data); if ((link_state == NV_NVLIPT_LNK_CTRL_LINK_STATE_REQUEST_STATUS_MINION_REQUEST_FAIL) && (link_intr_subcode == MINION_ALARM_BUSY)) { status = nvswitch_request_tl_link_state_ls10(link, NV_NVLIPT_LNK_CTRL_LINK_STATE_REQUEST_REQUEST_RESET, NV_TRUE); // // We retry the shutdown sequence 3 times when we see a MINION_REQUEST_FAIL // or MINION_ALARM_BUSY // retry_count--; } else { break; } } else { // failed to query minion for the link_intr_subcode so retry retry_count--; } } } while(retry_count); if (status != NVL_SUCCESS) { NVSWITCH_PRINT(device, ERROR, "%s: NvLink Reset has failed for link %d\n", __FUNCTION__, link->linkNumber); return status; } status = nvswitch_launch_ALI_link_training(device, link, NV_FALSE); if (status != NVL_SUCCESS) { NVSWITCH_PRINT(device, ERROR, "%s: NvLink failed to request ACTIVE for link %d\n", __FUNCTION__, link->linkNumber); return status; } return NVL_SUCCESS; } NvBool nvswitch_are_link_clocks_on_ls10 ( nvswitch_device *device, nvlink_link *link, NvU32 clocksMask ) { NvU32 clockStatus; NvU32 clk; NvBool bIsOff = NV_FALSE; clockStatus = NVSWITCH_LINK_RD32_LS10(device, link->linkNumber, NVLIPT_LNK, _NVLIPT_LNK, _CTRL_CLK_CTRL); FOR_EACH_INDEX_IN_MASK(32, clk, clocksMask) { switch(clk) { case NVSWITCH_PER_LINK_CLOCK_RXCLK: { bIsOff = FLD_TEST_DRF(_NVLIPT_LNK, _CTRL_CLK_CTRL, _RXCLK_STS, _OFF, clockStatus); break; } case NVSWITCH_PER_LINK_CLOCK_TXCLK: { bIsOff = FLD_TEST_DRF(_NVLIPT_LNK, _CTRL_CLK_CTRL, _TXCLK_STS, _OFF, clockStatus); break; } case NVSWITCH_PER_LINK_CLOCK_NCISOCCLK: { bIsOff = FLD_TEST_DRF(_NVLIPT_LNK, _CTRL_CLK_CTRL, _NCISOCCLK_STS, _OFF, clockStatus); break; } default: return NV_FALSE; } if (bIsOff) { return NV_FALSE; } } FOR_EACH_INDEX_IN_MASK_END; return NV_TRUE; } static NvlStatus _nvswitch_tl_request_get_timeout_value_ls10 ( nvswitch_device *device, NvU32 tlLinkState, NvU32 *timeoutVal ) { switch (tlLinkState) { case NV_NVLIPT_LNK_CTRL_LINK_STATE_REQUEST_REQUEST_ACTIVE: *timeoutVal = NV_NVLINK_TLREQ_TIMEOUT_ACTIVE; break; case NV_NVLIPT_LNK_CTRL_LINK_STATE_REQUEST_REQUEST_RESET: *timeoutVal = NV_NVLINK_TLREQ_TIMEOUT_RESET; break; case NV_NVLIPT_LNK_CTRL_LINK_STATE_REQUEST_REQUEST_SHUTDOWN: *timeoutVal = NV_NVLINK_TLREQ_TIMEOUT_SHUTDOWN; break; case NV_NVLIPT_LNK_CTRL_LINK_STATE_REQUEST_REQUEST_L2: *timeoutVal = NV_NVLINK_TLREQ_TIMEOUT_L2; break; default: NVSWITCH_PRINT(device, ERROR, "%s: Invalid tlLinkState %d provided!\n", __FUNCTION__, tlLinkState); return NVL_BAD_ARGS; } return NVL_SUCCESS; } NvlStatus nvswitch_request_tl_link_state_ls10 ( nvlink_link *link, NvU32 tlLinkState, NvBool bSync ) { nvswitch_device *device = link->dev->pDevInfo; NvlStatus status = NVL_SUCCESS; NvU32 linkStatus; NvU32 lnkErrStatus; NvU32 bit; NvU32 timeoutVal; NVSWITCH_TIMEOUT timeout; NvBool keepPolling; if (!NVSWITCH_IS_LINK_ENG_VALID_LS10(device, NVLIPT_LNK, link->linkNumber)) { NVSWITCH_PRINT(device, ERROR, "%s: link #%d invalid\n", __FUNCTION__, link->linkNumber); return -NVL_UNBOUND_DEVICE; } // Wait for the TL link state register to report ready status = nvswitch_wait_for_tl_request_ready_lr10(link); if (status != NVL_SUCCESS) { return status; } // Clear any pending FAILEDMINIONREQUEST status that maybe populated as it is stale now bit = DRF_NUM(_NVLIPT_LNK, _ERR_STATUS_0, _FAILEDMINIONREQUEST, 1); lnkErrStatus = NVSWITCH_LINK_RD32(device, link->linkNumber, NVLIPT_LNK, _NVLIPT_LNK, _ERR_STATUS_0); if (nvswitch_test_flags(lnkErrStatus, bit)) { NVSWITCH_LINK_WR32(device, link->linkNumber, NVLIPT_LNK, _NVLIPT_LNK, _ERR_STATUS_0, bit); } // Request state through CTRL_LINK_STATE_REQUEST NVSWITCH_LINK_WR32_LS10(device, link->linkNumber, NVLIPT_LNK, _NVLIPT_LNK, _CTRL_LINK_STATE_REQUEST, DRF_NUM(_NVLIPT_LNK, _CTRL_LINK_STATE_REQUEST, _REQUEST, tlLinkState)); if (bSync) { // setup timeouts for the TL request status = _nvswitch_tl_request_get_timeout_value_ls10(device, tlLinkState, &timeoutVal); if (status != NVL_SUCCESS) { return NVL_ERR_INVALID_STATE; } nvswitch_timeout_create(NVSWITCH_INTERVAL_1MSEC_IN_NS * timeoutVal, &timeout); status = NVL_MORE_PROCESSING_REQUIRED; do { keepPolling = (nvswitch_timeout_check(&timeout)) ? NV_FALSE : NV_TRUE; // Check for state requested linkStatus = NVSWITCH_LINK_RD32_LS10(device, link->linkNumber, NVLIPT_LNK , _NVLIPT_LNK , _CTRL_LINK_STATE_STATUS); if (DRF_VAL(_NVLIPT_LNK, _CTRL_LINK_STATE_STATUS, _CURRENTLINKSTATE, linkStatus) == tlLinkState) { status = NVL_SUCCESS; break; } nvswitch_os_sleep(1); } while(keepPolling); // Do one final check if the polling loop didn't see the target linkState if (status == NVL_MORE_PROCESSING_REQUIRED) { // Check for state requested linkStatus = NVSWITCH_LINK_RD32_LS10(device, link->linkNumber, NVLIPT_LNK , _NVLIPT_LNK , _CTRL_LINK_STATE_STATUS); if (DRF_VAL(_NVLIPT_LNK, _CTRL_LINK_STATE_STATUS, _CURRENTLINKSTATE, linkStatus) != tlLinkState) { NVSWITCH_PRINT(device, ERROR, "%s: TL link state request to state 0x%x for link #%d did not complete!\n", __FUNCTION__, tlLinkState, link->linkNumber); return -NVL_ERR_GENERIC; } } } return status; } NvBool nvswitch_does_link_need_termination_enabled_ls10 ( nvswitch_device *device, nvlink_link *link ) { // Not defined for LS10 return NV_FALSE; } NvlStatus nvswitch_link_termination_setup_ls10 ( nvswitch_device *device, nvlink_link* link ) { // Not supported for LS10 return -NVL_ERR_NOT_SUPPORTED; }