/* * SPDX-FileCopyrightText: Copyright (c) 2020-2022 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_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" 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_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; if (device->regkeys.enable_pm == NV_SWITCH_REGKEY_ENABLE_PM_NO) { return; } // bios_config = nvswitch_get_bios_nvlink_config(device); // IC Enter Threshold if (device->regkeys.lp_threshold == NV_SWITCH_REGKEY_SET_LP_THRESHOLD_DEFAULT) { // TODO: get from bios. Refer Bug 3626523 for more info. lpEntryThreshold = 100; } 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; // TO-DO: The write to the AN1 register is not working. The logic here needs to be re-visited. tempRegVal = NVSWITCH_LINK_RD32_LS10(device, linkNum, NVLIPT_LNK, _NVLIPT_LNK, _CTRL_SYSTEM_LINK_AN1_CTRL); tempRegVal = FLD_SET_DRF_NUM(_NVLIPT, _LNK_CTRL_SYSTEM_LINK_AN1_CTRL, _PWRM_L1_ENABLE, softwareDesired, tempRegVal); NVSWITCH_LINK_WR32_LS10(device, linkNum, NVLIPT_LNK, _NVLIPT_LNK, _CTRL_SYSTEM_LINK_AN1_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_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 regData; NvU32 nvliptWarmResetDelayUs = (IS_RTLSIM(device) || IS_EMULATION(device)) ? 800:8; regData = NVSWITCH_LINK_RD32_LS10(device, linkNumber, NVLIPT_LNK, _NVLIPT_LNK, _DEBUG_CLEAR); regData = FLD_SET_DRF_NUM(_NVLIPT_LNK, _DEBUG_CLEAR, _CLEAR, NV_NVLIPT_LNK_DEBUG_CLEAR_CLEAR_ASSERT, regData); NVSWITCH_LINK_WR32_LS10(device, linkNumber, NVLIPT_LNK, _NVLIPT_LNK, _DEBUG_CLEAR, regData); NVSWITCH_NSEC_DELAY(nvliptWarmResetDelayUs * NVSWITCH_INTERVAL_1USEC_IN_NS); regData = NVSWITCH_LINK_RD32_LS10(device, linkNumber, NVLIPT_LNK, _NVLIPT_LNK, _DEBUG_CLEAR); regData = FLD_SET_DRF_NUM(_NVLIPT_LNK, _DEBUG_CLEAR, _CLEAR, NV_NVLIPT_LNK_DEBUG_CLEAR_CLEAR_DEASSERT, regData); NVSWITCH_LINK_WR32_LS10(device, linkNumber, NVLIPT_LNK, _NVLIPT_LNK, _DEBUG_CLEAR, regData); NVSWITCH_NSEC_DELAY(nvliptWarmResetDelayUs * NVSWITCH_INTERVAL_1USEC_IN_NS); } 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_init_dlpl_interrupts_ls10 ( nvlink_link *link ) { nvswitch_device *device = link->dev->pDevInfo; NvU32 linkNumber = link->linkNumber; NvU32 intrRegVal; NvU32 crcRegVal; NvU32 shortRateMask; NvU32 crcShortRegkeyVal = device->regkeys.crc_bit_error_rate_short; 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)); // 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); // 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, _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)); intrRegVal = NVSWITCH_LINK_RD32_LS10(device, linkNumber, NVLDL, _NVLDL_TOP, _INTR_NONSTALL_EN); crcRegVal = NVSWITCH_LINK_RD32_LS10(device, linkNumber, NVLDL, _NVLDL_RX, _ERROR_RATE_CTRL); // // Enable RX error rate short interrupt. // Please refer to Bug 3365481 for details about the CRC_BIT_ERROR_RATE_SHORT // values used below. // // Enable RX error rate short interrupt if the regkey is set if (crcShortRegkeyVal != NV_SWITCH_REGKEY_CRC_BIT_ERROR_RATE_SHORT_DEFAULT) { 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); intrRegVal |= DRF_DEF(_NVLDL_TOP, _INTR_NONSTALL_EN, _RX_SHORT_ERROR_RATE, _ENABLE); crcRegVal &= ~shortRateMask; crcRegVal |= crcShortRegkeyVal; } else { shortRateMask = DRF_SHIFTMASK(NV_NVLDL_CRC_BIT_ERROR_RATE_SHORT_THRESHOLD_MAN) | DRF_SHIFTMASK(NV_NVLDL_CRC_BIT_ERROR_RATE_SHORT_THRESHOLD_EXP) | DRF_SHIFTMASK(NV_NVLDL_CRC_BIT_ERROR_RATE_SHORT_TIMESCALE_MAN) | DRF_SHIFTMASK(NV_NVLDL_CRC_BIT_ERROR_RATE_SHORT_TIMESCALE_EXP); intrRegVal |= DRF_DEF(_NVLDL_TOP, _INTR_NONSTALL_EN, _RX_SHORT_ERROR_RATE, _ENABLE); crcRegVal &= ~shortRateMask; } NVSWITCH_LINK_WR32_LS10(device, linkNumber, NVLDL, _NVLDL_TOP, _INTR_NONSTALL_EN, intrRegVal); NVSWITCH_LINK_WR32_LS10(device, linkNumber, NVLDL, _NVLDL_RX, _ERROR_RATE_CTRL, crcRegVal); } 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; NvU32 link_intr_subcode; 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_lr10(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; } } while (retry_count); NVSWITCH_PRINT(device, ERROR, "%s: NvLink Shutdown has failed for link %d\n", __FUNCTION__, link->linkNumber); return; }