mirror of
https://github.com/NVIDIA/open-gpu-kernel-modules.git
synced 2026-01-30 21:19:49 +00:00
978 lines
33 KiB
C
978 lines
33 KiB
C
/*
|
|
* SPDX-FileCopyrightText: Copyright (c) 2020-2024 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 "common_nvswitch.h"
|
|
#include "ls10/ls10.h"
|
|
#include "ls10/minion_ls10.h"
|
|
#include "ls10/minion_nvlink_defines_public_ls10.h"
|
|
#include "regkey_nvswitch.h"
|
|
|
|
#include "nvswitch/ls10/dev_minion_ip.h"
|
|
#include "nvswitch/ls10/dev_minion_ip_addendum.h"
|
|
#include "nvswitch/ls10/dev_ingress_ip.h"
|
|
#include "nvswitch/ls10/dev_egress_ip.h"
|
|
#include "nvswitch/ls10/dev_riscv_pri.h"
|
|
#include "nvswitch/ls10/dev_nvlphyctl_ip.h"
|
|
|
|
#include "flcn/flcn_nvswitch.h"
|
|
|
|
/*
|
|
* @Brief : Check if MINION is already running.
|
|
*
|
|
* The function assumes that if one of MINIONs is running, all of them are
|
|
* running. This approach needs to be fixed.
|
|
*
|
|
* TODO: Refactor minion code to check for each minion's status individually.
|
|
*
|
|
* @param[in] device Bootstrap MINIONs on this device
|
|
*/
|
|
static NvBool
|
|
_nvswitch_check_running_minions
|
|
(
|
|
nvswitch_device *device
|
|
)
|
|
{
|
|
NvU32 data, i;
|
|
NvBool bMinionRunning = NV_FALSE;
|
|
|
|
for (i = 0; i < NUM_MINION_ENGINE_LS10; i++)
|
|
{
|
|
if (!NVSWITCH_ENG_VALID_LS10(device, MINION, i))
|
|
{
|
|
NVSWITCH_PRINT(device, SETUP,
|
|
"%s: MINION instance %d is not valid.\n",
|
|
__FUNCTION__, i);
|
|
continue;
|
|
}
|
|
|
|
data = NVSWITCH_MINION_RD32_LS10(device, i, _CMINION, _FALCON_IRQSTAT);
|
|
if (FLD_TEST_DRF(_CMINION, _FALCON_IRQSTAT, _HALT, _FALSE, data))
|
|
{
|
|
data = NVSWITCH_MINION_RD32_LS10(device, i, _MINION, _MINION_STATUS);
|
|
if (FLD_TEST_DRF(_MINION, _MINION_STATUS, _STATUS, _BOOT, data))
|
|
{
|
|
//
|
|
// Set initialized flag if MINION is running.
|
|
// We don't want to bootstrap a falcon that is already running.
|
|
//
|
|
nvswitch_set_minion_initialized(device, i, NV_TRUE);
|
|
|
|
NVSWITCH_PRINT(device, SETUP,
|
|
"%s: MINION instance %d is already bootstrapped.\n",
|
|
__FUNCTION__, i);
|
|
bMinionRunning = NV_TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
return bMinionRunning;
|
|
}
|
|
|
|
NvlStatus
|
|
nvswitch_minion_get_dl_status_ls10
|
|
(
|
|
nvswitch_device *device,
|
|
NvU32 linkId,
|
|
NvU32 statusIdx,
|
|
NvU32 statusArgs,
|
|
NvU32 *statusData
|
|
)
|
|
{
|
|
NVSWITCH_TIMEOUT timeout;
|
|
NvBool keepPolling;
|
|
NvU32 regData, localLinkNumber;
|
|
localLinkNumber = linkId % NVSWITCH_LINKS_PER_MINION_LS10;
|
|
|
|
if (NVSWITCH_IS_LINK_ENG_VALID_LS10(device, MINION, linkId) &&
|
|
!nvswitch_is_minion_initialized(device, NVSWITCH_GET_LINK_ENG_INST(device, linkId, MINION)))
|
|
{
|
|
NVSWITCH_PRINT(device, ERROR,
|
|
"%s: MINION %d is not initialized for link %08x.\n",
|
|
__FUNCTION__, NVSWITCH_GET_LINK_ENG_INST(device, linkId, MINION),
|
|
linkId);
|
|
return -NVL_ERR_INVALID_STATE;
|
|
}
|
|
|
|
// Query the DL status interface to get the data
|
|
NVSWITCH_MINION_LINK_WR32_LS10(device, linkId, _MINION, _NVLINK_DL_STAT(localLinkNumber),
|
|
DRF_NUM(_MINION, _NVLINK_DL_STAT, _ARGS, statusArgs) |
|
|
DRF_NUM(_MINION, _NVLINK_DL_STAT, _STATUSIDX, statusIdx));
|
|
|
|
if (IS_FMODEL(device) || IS_EMULATION(device) || IS_RTLSIM(device))
|
|
{
|
|
nvswitch_timeout_create(20 * NVSWITCH_INTERVAL_1SEC_IN_NS, &timeout);
|
|
}
|
|
else
|
|
{
|
|
nvswitch_timeout_create(NVSWITCH_INTERVAL_5MSEC_IN_NS, &timeout);
|
|
}
|
|
|
|
// Poll for READY bit to be set
|
|
do
|
|
{
|
|
keepPolling = (nvswitch_timeout_check(&timeout)) ? NV_FALSE : NV_TRUE;
|
|
|
|
regData = NVSWITCH_MINION_LINK_RD32_LS10(device, linkId, _MINION, _NVLINK_DL_STAT(localLinkNumber));
|
|
if (FLD_TEST_DRF_NUM(_MINION, _NVLINK_DL_STAT, _READY, 1, regData))
|
|
{
|
|
*statusData = NVSWITCH_MINION_LINK_RD32_LS10(device, linkId, _MINION, _NVLINK_DL_STATDATA(localLinkNumber));
|
|
return NVL_SUCCESS;
|
|
}
|
|
if (IS_FMODEL(device) || IS_RTLSIM(device))
|
|
{
|
|
nvswitch_os_sleep(1);
|
|
}
|
|
}
|
|
while (keepPolling);
|
|
|
|
NVSWITCH_PRINT(device, ERROR,
|
|
"%s: Timeout waiting for DL_STAT request to complete"
|
|
" NV_MINION_NVLINK_DL_STAT(%d) = 0x%08x\n",
|
|
__FUNCTION__, linkId, regData);
|
|
return -NVL_ERR_INVALID_STATE;
|
|
}
|
|
|
|
/*
|
|
* @Brief : Send MINION DL CMD for a particular link
|
|
*
|
|
* @param[in] device Send command to MINION on this device
|
|
* @param[in] linkNumber DLCMD will be sent on this link number
|
|
*
|
|
* @return Returns true if the DLCMD passed
|
|
*/
|
|
NvlStatus
|
|
nvswitch_minion_send_command_ls10
|
|
(
|
|
nvswitch_device *device,
|
|
NvU32 linkNumber,
|
|
NvU32 command,
|
|
NvU32 scratch0
|
|
)
|
|
{
|
|
NvU32 data = 0, localLinkNumber, statData = 0;
|
|
NvU32 ingressEccRegVal = 0, egressEccRegVal = 0;
|
|
NVSWITCH_TIMEOUT timeout;
|
|
NvBool keepPolling;
|
|
|
|
localLinkNumber = linkNumber % NVSWITCH_LINKS_PER_MINION_LS10;
|
|
|
|
if (NVSWITCH_IS_LINK_ENG_VALID_LS10(device, MINION, linkNumber) &&
|
|
!nvswitch_is_minion_initialized(device, NVSWITCH_GET_LINK_ENG_INST(device, linkNumber, MINION)))
|
|
{
|
|
NVSWITCH_PRINT(device, ERROR,
|
|
"%s: MINION %d is not initialized for link %08x.\n",
|
|
__FUNCTION__, NVSWITCH_GET_LINK_ENG_INST(device, linkNumber, MINION),
|
|
linkNumber);
|
|
return NVL_SUCCESS;
|
|
}
|
|
|
|
data = NVSWITCH_MINION_LINK_RD32_LS10(device, linkNumber, _MINION, _NVLINK_DL_CMD(localLinkNumber));
|
|
if (FLD_TEST_DRF_NUM(_MINION, _NVLINK_DL_CMD, _FAULT, 1, data))
|
|
{
|
|
NVSWITCH_PRINT(device, ERROR,
|
|
"%s: MINION %d is in fault state. NV_MINION_NVLINK_DL_CMD(%d) = %08x\n",
|
|
__FUNCTION__, NVSWITCH_GET_LINK_ENG_INST(device, linkNumber, MINION),
|
|
linkNumber, data);
|
|
return -NVL_ERR_GENERIC;
|
|
}
|
|
|
|
// Write to minion scratch if needed by command
|
|
switch (command)
|
|
{
|
|
case NV_MINION_NVLINK_DL_CMD_COMMAND_CONFIGEOM:
|
|
data = 0;
|
|
data = FLD_SET_DRF_NUM(_MINION, _MISC_0, _SCRATCH_SWRW_0, scratch0, data);
|
|
NVSWITCH_MINION_WR32_LS10(device,
|
|
NVSWITCH_GET_LINK_ENG_INST(device, linkNumber, MINION), _MINION, _MISC_0, data);
|
|
break;
|
|
case NV_MINION_NVLINK_DL_CMD_COMMAND_INITPHASE1:
|
|
//
|
|
// WAR bug 2708497
|
|
// Before INITPHASE1, we must clear these values, then set back to
|
|
// _PROD after the call
|
|
// NV_INGRESS_ERR_ECC_CTRL_NCISOC_PARITY_ENABLE
|
|
// NV_EGRESS_ERR_ECC_CTRL_NCISOC_PARITY_ENABLE
|
|
//
|
|
|
|
ingressEccRegVal = NVSWITCH_NPORT_RD32_LS10(device, linkNumber, _INGRESS, _ERR_ECC_CTRL);
|
|
NVSWITCH_NPORT_WR32_LS10(device, linkNumber, _INGRESS, _ERR_ECC_CTRL,
|
|
FLD_SET_DRF(_INGRESS, _ERR_ECC_CTRL, _NCISOC_PARITY_ENABLE, _DISABLE, ingressEccRegVal));
|
|
|
|
egressEccRegVal = NVSWITCH_NPORT_RD32_LS10(device, linkNumber, _EGRESS, _ERR_ECC_CTRL);
|
|
NVSWITCH_NPORT_WR32_LS10(device, linkNumber, _EGRESS, _ERR_ECC_CTRL,
|
|
FLD_SET_DRF(_EGRESS, _ERR_ECC_CTRL, _NCISOC_PARITY_ENABLE, _DISABLE, egressEccRegVal));
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
data = FLD_SET_DRF_NUM(_MINION, _NVLINK_DL_CMD, _COMMAND, command, data);
|
|
data = FLD_SET_DRF_NUM(_MINION, _NVLINK_DL_CMD, _FAULT, 1, data);
|
|
NVSWITCH_MINION_LINK_WR32_LS10(device, linkNumber, _MINION, _NVLINK_DL_CMD(localLinkNumber), data);
|
|
|
|
if (IS_FMODEL(device) || IS_EMULATION(device) || IS_RTLSIM(device))
|
|
{
|
|
nvswitch_timeout_create(10 * NVSWITCH_INTERVAL_1SEC_IN_NS, &timeout);
|
|
}
|
|
else
|
|
{
|
|
nvswitch_timeout_create(NVSWITCH_INTERVAL_5MSEC_IN_NS, &timeout);
|
|
}
|
|
|
|
//
|
|
// We will exit this if the command is successful OR
|
|
// if timeout waiting for the READY bit to be set OR
|
|
// if it generates a MINION FAULT
|
|
//
|
|
do
|
|
{
|
|
keepPolling = (nvswitch_timeout_check(&timeout)) ? NV_FALSE : NV_TRUE;
|
|
|
|
data = NVSWITCH_MINION_LINK_RD32_LS10(device, linkNumber, _MINION, _NVLINK_DL_CMD(localLinkNumber));
|
|
if (FLD_TEST_DRF_NUM(_MINION, _NVLINK_DL_CMD, _READY, 1, data))
|
|
{
|
|
// The command has completed, success?
|
|
if (FLD_TEST_DRF_NUM(_MINION, _NVLINK_DL_CMD, _FAULT, 1, data))
|
|
{
|
|
NVSWITCH_PRINT(device, ERROR,
|
|
"%s: NVLink MINION command faulted!"
|
|
" NV_MINION_NVLINK_DL_CMD(%d) = 0x%08x\n",
|
|
__FUNCTION__, linkNumber, data);
|
|
|
|
// Pull fault code and subcode
|
|
if (nvswitch_minion_get_dl_status(device, linkNumber,
|
|
NV_NVLSTAT_MN00, 0, &statData) == NVL_SUCCESS)
|
|
{
|
|
NVSWITCH_PRINT(device, ERROR,
|
|
"%s: Minion DLCMD Fault code = 0x%x, Sub-code = 0x%x\n",
|
|
__FUNCTION__,
|
|
DRF_VAL(_NVLSTAT, _MN00, _LINK_INTR_CODE, statData),
|
|
DRF_VAL(_NVLSTAT, _MN00, _LINK_INTR_SUBCODE, statData));
|
|
}
|
|
else
|
|
{
|
|
NVSWITCH_PRINT(device, ERROR,
|
|
"%s: Failed to get code and subcode from DLSTAT, link %d\n",
|
|
__FUNCTION__, linkNumber);
|
|
}
|
|
|
|
// Clear the fault and return
|
|
NVSWITCH_PRINT(device, ERROR,
|
|
"%s: Clearing NVLink MINION fault for link %d\n",
|
|
__FUNCTION__, linkNumber);
|
|
|
|
data = FLD_SET_DRF_NUM(_MINION, _NVLINK_DL_CMD, _FAULT, 1, 0x0);
|
|
NVSWITCH_MINION_LINK_WR32_LS10(device, linkNumber, _MINION, _NVLINK_DL_CMD(localLinkNumber), data);
|
|
return (DRF_VAL(_NVLSTAT, _MN00, _LINK_INTR_SUBCODE, statData) == MINION_ALARM_BUSY) ?
|
|
-NVL_ERR_STATE_IN_USE : -NVL_ERR_INVALID_STATE;
|
|
}
|
|
else
|
|
{
|
|
NVSWITCH_PRINT(device, SETUP,
|
|
"%s: NVLink MINION command %x was sent successfully for link %d\n",
|
|
__FUNCTION__, command, linkNumber);
|
|
break;
|
|
}
|
|
}
|
|
|
|
nvswitch_os_sleep(1);
|
|
}
|
|
while (keepPolling);
|
|
|
|
if (!FLD_TEST_DRF_NUM(_MINION, _NVLINK_DL_CMD, _READY, 1, data))
|
|
{
|
|
NVSWITCH_PRINT(device, ERROR,
|
|
"%s: Timeout waiting for NVLink MINION command to complete!"
|
|
" NV_MINION_NVLINK_DL_CMD(%d) = 0x%08x\n",
|
|
__FUNCTION__, linkNumber, data);
|
|
return -NVL_ERR_INVALID_STATE;
|
|
}
|
|
|
|
if (command == NV_MINION_NVLINK_DL_CMD_COMMAND_INITPHASE1)
|
|
{
|
|
NVSWITCH_NPORT_WR32_LS10(device, linkNumber, _INGRESS, _ERR_ECC_CTRL, ingressEccRegVal);
|
|
NVSWITCH_NPORT_WR32_LS10(device, linkNumber, _EGRESS, _ERR_ECC_CTRL, egressEccRegVal);
|
|
}
|
|
|
|
return NVL_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
* @Brief : Bootstrap all MINIONs on the specified device
|
|
*
|
|
* @param[in] device Bootstrap MINIONs on this device
|
|
*/
|
|
NvlStatus
|
|
nvswitch_init_minion_ls10
|
|
(
|
|
nvswitch_device *device
|
|
)
|
|
{
|
|
NvlStatus status = NVL_SUCCESS;
|
|
|
|
if (_nvswitch_check_running_minions(device))
|
|
{
|
|
return NVL_SUCCESS;
|
|
}
|
|
|
|
status = -NVL_INITIALIZATION_TOTAL_FAILURE;
|
|
|
|
return status;
|
|
}
|
|
|
|
NvlStatus
|
|
nvswitch_set_minion_initialized_ls10
|
|
(
|
|
nvswitch_device *device,
|
|
NvU32 idx_minion,
|
|
NvBool initialized
|
|
)
|
|
{
|
|
ls10_device *chip_device = NVSWITCH_GET_CHIP_DEVICE_LS10(device);
|
|
|
|
if (!NVSWITCH_ENG_VALID_LS10(device, MINION, idx_minion))
|
|
{
|
|
return -NVL_BAD_ARGS;
|
|
}
|
|
|
|
chip_device->engMINION[idx_minion].initialized = initialized;
|
|
return NVL_SUCCESS;
|
|
}
|
|
|
|
NvBool
|
|
nvswitch_is_minion_initialized_ls10
|
|
(
|
|
nvswitch_device *device,
|
|
NvU32 idx_minion
|
|
)
|
|
{
|
|
ls10_device *chip_device = NVSWITCH_GET_CHIP_DEVICE_LS10(device);
|
|
|
|
if (!NVSWITCH_ENG_VALID_LS10(device, MINION, idx_minion))
|
|
{
|
|
return NV_FALSE;
|
|
}
|
|
return (chip_device->engMINION[idx_minion].initialized != 0);
|
|
}
|
|
|
|
NvlStatus
|
|
nvswitch_minion_set_sim_mode_ls10
|
|
(
|
|
nvswitch_device *device,
|
|
nvlink_link *link
|
|
)
|
|
{
|
|
NvlStatus status = NVL_SUCCESS;
|
|
NvU32 dlcmd;
|
|
NvU32 linkNumber = link->linkNumber;
|
|
NvU32 localLinkNumber = linkNumber % NVSWITCH_LINKS_PER_MINION_LS10;
|
|
|
|
switch (device->regkeys.set_simmode)
|
|
{
|
|
case NV_SWITCH_REGKEY_MINION_SET_SIMMODE_FAST:
|
|
dlcmd = NV_MINION_NVLINK_DL_CMD_COMMAND_DBG_SETSIMMODE_FAST;
|
|
break;
|
|
case NV_SWITCH_REGKEY_MINION_SET_SIMMODE_MEDIUM:
|
|
dlcmd = NV_MINION_NVLINK_DL_CMD_COMMAND_DBG_SETSIMMODE_MEDIUM;
|
|
break;
|
|
case NV_SWITCH_REGKEY_MINION_SET_SIMMODE_SLOW:
|
|
dlcmd = NV_MINION_NVLINK_DL_CMD_COMMAND_DBG_SETSIMMODE_SLOW;
|
|
break;
|
|
default:
|
|
return NVL_SUCCESS;
|
|
}
|
|
|
|
status = nvswitch_minion_send_command(device, linkNumber, dlcmd, 0);
|
|
if (status != NVL_SUCCESS)
|
|
{
|
|
NVSWITCH_PRINT(device, ERROR,
|
|
"%s: DLCMD 0x%x failed on link: %d\n",
|
|
__FUNCTION__, dlcmd, linkNumber);
|
|
return status;
|
|
}
|
|
|
|
// Setting RXCAL_EN_ALARM timer value
|
|
NVSWITCH_MINION_LINK_WR32_LS10(device, linkNumber, _MINION,
|
|
_NVLINK_DL_CMD_DATA(localLinkNumber),
|
|
NV_MINION_DL_CMD_DATA_RXCAL_EN_ALARM);
|
|
|
|
status = nvswitch_minion_send_command(device, linkNumber,
|
|
NV_MINION_NVLINK_DL_CMD_COMMAND_DBG_SETSIMMODE_RXCAL_EN_ALARM, 0);
|
|
if (status != NVL_SUCCESS)
|
|
{
|
|
NVSWITCH_PRINT(device, ERROR,
|
|
"%s: DLCMD DBG_SETSIMMODE_RXCAL_EN_ALARM failed on link: %d\n",
|
|
__FUNCTION__, linkNumber);
|
|
return status;
|
|
}
|
|
|
|
// Setting INIT_CAL_DONE timer value
|
|
NVSWITCH_MINION_LINK_WR32_LS10(device, linkNumber, _MINION,
|
|
_NVLINK_DL_CMD_DATA(localLinkNumber),
|
|
NV_MINION_DL_CMD_DATA_INIT_CAL_DONE);
|
|
|
|
status = nvswitch_minion_send_command(device, linkNumber,
|
|
NV_MINION_NVLINK_DL_CMD_COMMAND_DBG_SETSIMMODE_INIT_CAL_DONE, 0);
|
|
if (status != NVL_SUCCESS)
|
|
{
|
|
NVSWITCH_PRINT(device, ERROR,
|
|
"%s: DLCMD DBG_SETSIMMODE_INIT_CAL_DONE failed on link: %d\n",
|
|
__FUNCTION__, linkNumber);
|
|
return status;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
NvlStatus
|
|
nvswitch_minion_set_smf_settings_ls10
|
|
(
|
|
nvswitch_device *device,
|
|
nvlink_link *link
|
|
)
|
|
{
|
|
NvlStatus status = NVL_SUCCESS;
|
|
NvU32 dlcmd;
|
|
NvU32 linkNumber = link->linkNumber;
|
|
|
|
switch (device->regkeys.set_smf_settings)
|
|
{
|
|
case NV_SWITCH_REGKEY_MINION_SET_SMF_SETTINGS_SLOW:
|
|
dlcmd = NV_MINION_NVLINK_DL_CMD_COMMAND_DBG_SETSIMMODE_SMF_VALUES_SLOW;
|
|
break;
|
|
case NV_SWITCH_REGKEY_MINION_SET_SMF_SETTINGS_MEDIUM:
|
|
dlcmd = NV_MINION_NVLINK_DL_CMD_COMMAND_DBG_SETSIMMODE_SMF_VALUES_MEDIUM;
|
|
break;
|
|
case NV_SWITCH_REGKEY_MINION_SET_SMF_SETTINGS_FAST:
|
|
dlcmd = NV_MINION_NVLINK_DL_CMD_COMMAND_DBG_SETSIMMODE_SMF_VALUES_FAST;
|
|
break;
|
|
case NV_SWITCH_REGKEY_MINION_SET_SMF_SETTINGS_MEDIUM_SERIAL:
|
|
dlcmd = NV_MINION_NVLINK_DL_CMD_COMMAND_DBG_SETSIMMODE_SMF_VALUES_MEDIUM_SERIAL;
|
|
break;
|
|
default:
|
|
return NVL_SUCCESS;
|
|
}
|
|
|
|
status = nvswitch_minion_send_command(device, linkNumber, dlcmd, 0);
|
|
if (status != NVL_SUCCESS)
|
|
{
|
|
NVSWITCH_PRINT(device, ERROR,
|
|
"%s: DLCMD 0x%x failed on link: %d\n",
|
|
__FUNCTION__, dlcmd, linkNumber);
|
|
return status;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
NvlStatus
|
|
nvswitch_minion_select_uphy_tables_ls10
|
|
(
|
|
nvswitch_device *device,
|
|
nvlink_link *link
|
|
)
|
|
{
|
|
NvlStatus status = NVL_SUCCESS;
|
|
NvU32 dlcmd;
|
|
NvU32 linkNumber = link->linkNumber;
|
|
|
|
switch (device->regkeys.select_uphy_tables)
|
|
{
|
|
case NV_SWITCH_REGKEY_MINION_SELECT_UPHY_TABLES_SHORT:
|
|
dlcmd = NV_MINION_NVLINK_DL_CMD_COMMAND_DBG_SETSIMMODE_UPHY_TABLES_SHORT;
|
|
break;
|
|
case NV_SWITCH_REGKEY_MINION_SELECT_UPHY_TABLES_FAST:
|
|
dlcmd = NV_MINION_NVLINK_DL_CMD_COMMAND_DBG_SETSIMMODE_UPHY_TABLES_FAST;
|
|
break;
|
|
default:
|
|
return NVL_SUCCESS;
|
|
}
|
|
|
|
status = nvswitch_minion_send_command(device, linkNumber, dlcmd, 0);
|
|
if (status != NVL_SUCCESS)
|
|
{
|
|
NVSWITCH_PRINT(device, ERROR,
|
|
"%s: DLCMD 0x%x failed on link: %d\n",
|
|
__FUNCTION__, dlcmd, linkNumber);
|
|
return status;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
NvlStatus
|
|
nvswitch_minion_get_rxdet_status_ls10
|
|
(
|
|
nvswitch_device *device,
|
|
NvU32 linkId
|
|
)
|
|
{
|
|
NvU32 statData;
|
|
NvlStatus status;
|
|
NVSWITCH_TIMEOUT timeout;
|
|
NvBool keepPolling;
|
|
|
|
if (IS_FMODEL(device) || IS_EMULATION(device) || IS_RTLSIM(device))
|
|
{
|
|
nvswitch_timeout_create(30*NVSWITCH_INTERVAL_1SEC_IN_NS, &timeout);
|
|
}
|
|
else
|
|
{
|
|
nvswitch_timeout_create(20 * NVSWITCH_INTERVAL_1MSEC_IN_NS, &timeout);
|
|
}
|
|
|
|
// Poll for READY bit to be set
|
|
do
|
|
{
|
|
keepPolling = (nvswitch_timeout_check(&timeout)) ? NV_FALSE : NV_TRUE;
|
|
|
|
// Check RXDET status on MINION DL STAT interface
|
|
status = nvswitch_minion_get_dl_status(device, linkId, NV_NVLSTAT_LNK2, 0, &statData);
|
|
if (status != NVL_SUCCESS)
|
|
{
|
|
return status;
|
|
}
|
|
|
|
if (FLD_TEST_DRF(_NVLSTAT, _LNK2, _RXDET_LINK_STATUS, _FOUND, statData))
|
|
{
|
|
NVSWITCH_PRINT(device, INFO,
|
|
"%s: RXDET LINK_STATUS = FOUND on link: %d\n",
|
|
__FUNCTION__, linkId);
|
|
|
|
// Retrieve which lanes were found (should be all)
|
|
device->link[linkId].lane_rxdet_status_mask =
|
|
DRF_VAL(_NVLSTAT, _LNK2, _RXDET_LANE_STATUS, statData);
|
|
|
|
//
|
|
// MINION doesn't have knowledge of lane reversal and therefore
|
|
// reports logical lanes. We must reverse the bitmask here if applicable
|
|
// since RM reports physical lanes.
|
|
//
|
|
if (nvswitch_link_lane_reversed_lr10(device, linkId))
|
|
{
|
|
NVSWITCH_REVERSE_BITMASK_32(NVSWITCH_NUM_LANES_LS10,
|
|
device->link[linkId].lane_rxdet_status_mask);
|
|
}
|
|
|
|
return NVL_SUCCESS;
|
|
}
|
|
|
|
if (FLD_TEST_DRF(_NVLSTAT, _LNK2, _RXDET_LINK_STATUS, _TIMEOUT, statData))
|
|
{
|
|
NVSWITCH_PRINT(device, ERROR,
|
|
"%s: RXDET LINK_STATUS = TIMEOUT on link: %d\n",
|
|
__FUNCTION__, linkId);
|
|
|
|
// Retrieve which lanes were found
|
|
device->link[linkId].lane_rxdet_status_mask =
|
|
DRF_VAL(_NVLSTAT, _LNK2, _RXDET_LANE_STATUS, statData);
|
|
|
|
//
|
|
// MINION doesn't have knowledge of lane reversal and therefore
|
|
// reports logical lanes. We must reverse the bitmask here if applicable
|
|
// since RM reports physical lanes.
|
|
//
|
|
if (nvswitch_link_lane_reversed_lr10(device, linkId))
|
|
{
|
|
NVSWITCH_REVERSE_BITMASK_32(NVSWITCH_NUM_LANES_LS10,
|
|
device->link[linkId].lane_rxdet_status_mask);
|
|
}
|
|
|
|
return -NVL_ERR_INVALID_STATE;
|
|
}
|
|
|
|
nvswitch_os_sleep(1);
|
|
}
|
|
while (keepPolling);
|
|
|
|
NVSWITCH_PRINT(device, ERROR,
|
|
"%s: Timeout waiting for RXDET STATUS on link: %d\n",
|
|
__FUNCTION__, linkId);
|
|
|
|
return -NVL_ERR_INVALID_STATE;
|
|
}
|
|
|
|
/*
|
|
* @Brief : Check if the RISCV CPU has started
|
|
*
|
|
* @param[in] device The Nvswitch device
|
|
* @param[in] idx_minion MINION instance to use
|
|
*/
|
|
NvBool
|
|
nvswitch_minion_is_riscv_active_ls10
|
|
(
|
|
nvswitch_device *device,
|
|
NvU32 idx_minion
|
|
)
|
|
{
|
|
NvU32 val;
|
|
|
|
val = NVSWITCH_MINION_RD32_LS10(device, idx_minion, _CMINION_RISCV, _CPUCTL);
|
|
|
|
return FLD_TEST_DRF(_CMINION, _RISCV_CPUCTL, _ACTIVE_STAT, _ACTIVE, val);
|
|
|
|
}
|
|
|
|
NvlStatus
|
|
nvswitch_minion_clear_dl_error_counters_ls10
|
|
(
|
|
nvswitch_device *device,
|
|
NvU32 linkId
|
|
)
|
|
{
|
|
NvlStatus status;
|
|
|
|
status = nvswitch_minion_send_command(device, linkId,
|
|
NV_MINION_NVLINK_DL_CMD_COMMAND_DLSTAT_CLR_DLERRCNT, 0);
|
|
if (status != NVL_SUCCESS)
|
|
{
|
|
NVSWITCH_PRINT(device, ERROR, "%s : Failed to clear error count to MINION for link # %d\n",
|
|
__FUNCTION__, linkId);
|
|
}
|
|
return status;
|
|
}
|
|
|
|
NvlStatus
|
|
nvswitch_minion_send_inband_data_ls10
|
|
(
|
|
nvswitch_device *device,
|
|
NvU32 linkId,
|
|
nvswitch_inband_send_data *inBandData
|
|
)
|
|
{
|
|
NvlStatus status = NVL_SUCCESS;
|
|
#if defined(INCLUDE_NVLINK_LIB)
|
|
NvlStatus tempStatus = NVL_SUCCESS;
|
|
NvU32 localLinkNumber = linkId % NVSWITCH_LINKS_PER_MINION_LS10;
|
|
NvU8 *sendBuffer = inBandData->sendBuffer;
|
|
NvU32 bufferSize = inBandData->bufferSize;
|
|
NvU32 data = 0;
|
|
NvU32 regval = 0;
|
|
NvU32 statData = 0;
|
|
NVSWITCH_TIMEOUT timeout;
|
|
NvBool bKeepPolling = NV_TRUE;
|
|
|
|
if (bufferSize == 0 || bufferSize > NVLINK_INBAND_MAX_XFER_SIZE)
|
|
{
|
|
NVSWITCH_PRINT(device, ERROR, "Bad Inband data size %d. Skipping Inband Send\n", bufferSize);
|
|
return -NVL_ERR_INVALID_STATE;
|
|
}
|
|
|
|
// Buffer Size must be reduced by 1 as per the minion protocol
|
|
regval = DRF_NUM(_MINION, _INBAND_SEND_DATA, _BUFFER_SIZE, (bufferSize - 1));
|
|
|
|
regval |= DRF_NUM(_MINION, _INBAND_SEND_DATA, _FLAGS,inBandData->hdr.data);
|
|
|
|
NVSWITCH_MINION_WR32_LS10(device,
|
|
NVSWITCH_GET_LINK_ENG_INST(device, linkId, MINION),
|
|
_MINION, _NVLINK_DL_CMD_DATA(localLinkNumber),
|
|
regval);
|
|
|
|
status = nvswitch_minion_send_command(device, linkId,
|
|
NV_MINION_NVLINK_DL_CMD_COMMAND_WRITE_TX_BUFFER_START, 0);
|
|
|
|
if (status != NVL_SUCCESS)
|
|
{
|
|
NVSWITCH_PRINT(device, ERROR, "Link %d Inband Buffer transfer for TX_BUFFER_START failed\n", linkId);
|
|
return status;
|
|
}
|
|
|
|
while (bufferSize != 0)
|
|
{
|
|
nvswitch_os_memcpy(&data, sendBuffer, NV_MIN(sizeof(data), bufferSize));
|
|
|
|
NVSWITCH_MINION_WR32_LS10(device,
|
|
NVSWITCH_GET_LINK_ENG_INST(device, linkId, MINION),
|
|
_MINION, _NVLINK_DL_CMD_DATA(localLinkNumber),
|
|
data);
|
|
|
|
status = nvswitch_minion_send_command(device, linkId,
|
|
NV_MINION_NVLINK_DL_CMD_COMMAND_WRITE_TX_BUFFER_MIDDLE, 0);
|
|
|
|
if (status != NVL_SUCCESS)
|
|
{
|
|
NVSWITCH_PRINT(device, ERROR, "Link %d Inband Buffer transfer failed\n", linkId);
|
|
goto clear_buffer;
|
|
}
|
|
|
|
bufferSize -= NV_MIN(sizeof(data), bufferSize);
|
|
sendBuffer += NV_MIN(sizeof(data), bufferSize);
|
|
}
|
|
|
|
status = nvswitch_minion_send_command(device, linkId,
|
|
NV_MINION_NVLINK_DL_CMD_COMMAND_WRITE_TX_BUFFER_END, 0);
|
|
|
|
if (status != NVL_SUCCESS)
|
|
{
|
|
NVSWITCH_PRINT(device, ERROR, "Link %d Inband Buffer transfer for TX_BUFFER_END\n", linkId);
|
|
goto clear_buffer;
|
|
}
|
|
|
|
// Wait for buffer complete or buffer fail
|
|
nvswitch_timeout_create(2 * NVSWITCH_INTERVAL_4SEC_IN_NS, &timeout);
|
|
do
|
|
{
|
|
bKeepPolling = !nvswitch_timeout_check(&timeout);
|
|
// DLSTAT need to explicitly checked
|
|
status = nvswitch_minion_get_dl_status(device, linkId, NV_NVLSTAT_UC01, 0, &statData);
|
|
|
|
if (status != NVL_SUCCESS)
|
|
{
|
|
NVSWITCH_PRINT(device, INFO,"%s : Failed to poll DLSTAT register for (%s):(%d)\n",
|
|
__FUNCTION__, device->name, linkId);
|
|
status = -NVL_ERR_INVALID_STATE;
|
|
goto clear_buffer;
|
|
}
|
|
|
|
if (FLD_TEST_DRF(_NVLSTAT, _UC01, _INBAND_BUFFER_COMPLETE, _TRUE, statData))
|
|
{
|
|
return NVL_SUCCESS;
|
|
}
|
|
|
|
if (FLD_TEST_DRF(_NVLSTAT, _UC01, _INBAND_BUFFER_FAIL, _TRUE, statData))
|
|
{
|
|
NVSWITCH_PRINT(device, ERROR, "Link %d Inband Buffer transfer BUFFER_FAILED\n", linkId);
|
|
status = -NVL_ERR_INVALID_STATE;
|
|
goto clear_buffer;
|
|
}
|
|
//
|
|
// Consider a case where both FM and RM trying to write on the same NVLink at the same time.
|
|
// Both are waiting on each other to read the buffer, but they have also blocked the ISR,
|
|
// as while waiting they hold the device lock. Thus, it is necessary to unblock one of them.
|
|
// And to do that we have service BUFFER_AVAILABLE while waiting.
|
|
//
|
|
nvswitch_service_minion_all_links_ls10(device);
|
|
|
|
nvswitch_os_sleep(10);
|
|
|
|
} while (bKeepPolling);
|
|
|
|
NVSWITCH_PRINT(device, ERROR, "Link %d Inband Neither got BUFFER_FAIL nor BUFFER_COMPLETE\n", linkId);
|
|
|
|
clear_buffer:
|
|
tempStatus = nvswitch_minion_send_command(device, linkId,
|
|
NV_MINION_NVLINK_DL_CMD_COMMAND_CLEAR_TX_BUFFER,
|
|
0);
|
|
if (tempStatus != NVL_SUCCESS)
|
|
{
|
|
NVSWITCH_PRINT(device, ERROR, "Link %d Inband Buffer transfer for TX_BUFFER_CLEAR\n", linkId);
|
|
return status;
|
|
}
|
|
|
|
// Check if we received BUFFER_COMPLETE is seen while doing a BUFFER_CLEAR
|
|
tempStatus = nvswitch_minion_get_dl_status(device, linkId, NV_NVLSTAT_UC01, 0, &statData);
|
|
|
|
if (tempStatus != NVL_SUCCESS)
|
|
{
|
|
NVSWITCH_PRINT(device, INFO,"%s : Failed to poll DLSTAT register for (%s):(%d)\n",
|
|
__FUNCTION__, device->name, linkId);
|
|
return status;
|
|
}
|
|
|
|
if (FLD_TEST_DRF(_NVLSTAT, _UC01, _INBAND_BUFFER_COMPLETE, _TRUE, statData))
|
|
{
|
|
status = NVL_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
status = bKeepPolling ? status: -NVL_ERR_STATE_IN_USE;
|
|
}
|
|
#endif
|
|
return status;
|
|
}
|
|
|
|
void
|
|
nvswitch_minion_receive_inband_data_ls10
|
|
(
|
|
nvswitch_device *device,
|
|
NvU32 linkId
|
|
)
|
|
{
|
|
NvlStatus status = NVL_SUCCESS;
|
|
#if defined(INCLUDE_NVLINK_LIB)
|
|
NvlStatus tempStatus = NVL_SUCCESS;
|
|
NvU32 numEntries = 0;
|
|
NvU32 i;
|
|
NvU32 localLinkNumber = linkId % NVSWITCH_LINKS_PER_MINION_LS10;
|
|
NvU8 *receiveBuffer;
|
|
NvU32 regVal, dataSize, remainingBuffer, bytesToXfer;
|
|
nvlink_inband_drv_hdr_t hdr;
|
|
|
|
status = nvswitch_minion_send_command(device, linkId,
|
|
NV_MINION_NVLINK_DL_CMD_COMMAND_READ_RX_BUFFER_START,
|
|
0);
|
|
if (status != NV_OK)
|
|
goto cleanup;
|
|
|
|
regVal = NVSWITCH_MINION_RD32_LS10(device,
|
|
NVSWITCH_GET_LINK_ENG_INST(device, linkId, MINION),
|
|
_MINION, _NVLINK_DL_CMD_DATA(localLinkNumber));
|
|
|
|
// Add 1 to the data as per minion protocol
|
|
dataSize = DRF_VAL(_MINION, _INBAND_SEND_DATA, _BUFFER_SIZE, regVal) + 1;
|
|
hdr.data = DRF_VAL(_MINION, _INBAND_SEND_DATA, _FLAGS, regVal);
|
|
numEntries = NV_ALIGN_UP(dataSize, NVLINK_INBAND_MAX_XFER_AT_ONCE)/
|
|
NVLINK_INBAND_MAX_XFER_AT_ONCE;
|
|
remainingBuffer = dataSize;
|
|
|
|
if ((hdr.data & (NVLINK_INBAND_DRV_HDR_TYPE_START |
|
|
NVLINK_INBAND_DRV_HDR_TYPE_MID |
|
|
NVLINK_INBAND_DRV_HDR_TYPE_END)) == 0)
|
|
{
|
|
NVSWITCH_PRINT(device, ERROR, "InBand: HDR is wrong\n");
|
|
goto cleanup;
|
|
}
|
|
|
|
if (hdr.data & NVLINK_INBAND_DRV_HDR_TYPE_START)
|
|
{
|
|
if (device->link[linkId].inbandData.message != NULL)
|
|
{
|
|
NVSWITCH_PRINT(device, ERROR, "InBand: Got TYPE_START for existing data\n");
|
|
NVSWITCH_ASSERT(0);
|
|
goto cleanup;
|
|
}
|
|
|
|
device->link[linkId].inbandData.message =
|
|
nvswitch_os_malloc(sizeof(nvswitch_inband_data_list));
|
|
if (device->link[linkId].inbandData.message == NULL)
|
|
{
|
|
status = -NVL_NO_MEM;
|
|
goto cleanup;
|
|
}
|
|
|
|
device->link[linkId].inbandData.message->dataSize = 0;
|
|
}
|
|
|
|
if (device->link[linkId].inbandData.message == NULL)
|
|
{
|
|
NVSWITCH_PRINT(device, ERROR, "InBand: Data being sent without _START\n");
|
|
goto cleanup;
|
|
}
|
|
|
|
receiveBuffer = device->link[linkId].inbandData.message->data;
|
|
|
|
receiveBuffer += device->link[linkId].inbandData.message->dataSize;
|
|
|
|
if (((dataSize + device->link[linkId].inbandData.message->dataSize) >
|
|
NVSWITCH_INBAND_DATA_SIZE) ||
|
|
(dataSize > NVLINK_INBAND_MAX_XFER_SIZE) ||
|
|
(dataSize == 0))
|
|
{
|
|
NVSWITCH_PRINT(device, ERROR, "InBand: Msg is of wrong Size :DataSize = %d Msg Size= %d\n",
|
|
dataSize, device->link[linkId].inbandData.message->dataSize);
|
|
NVSWITCH_ASSERT(0);
|
|
goto cleanup;
|
|
}
|
|
|
|
for (i = 0; i < numEntries; i++)
|
|
{
|
|
status = nvswitch_minion_send_command(device, linkId,
|
|
NV_MINION_NVLINK_DL_CMD_COMMAND_READ_RX_BUFFER_MIDDLE, 0);
|
|
if (status != NV_OK)
|
|
{
|
|
NVSWITCH_PRINT(device, ERROR, "Link %d Inband Buffer receive"
|
|
"for entry %d failed\n", linkId, i);
|
|
goto cleanup;
|
|
}
|
|
|
|
regVal = NVSWITCH_MINION_RD32_LS10(device,
|
|
NVSWITCH_GET_LINK_ENG_INST(device, linkId, MINION),
|
|
_MINION, _NVLINK_DL_CMD_DATA(localLinkNumber));
|
|
|
|
bytesToXfer = NV_MIN(remainingBuffer, NVLINK_INBAND_MAX_XFER_AT_ONCE);
|
|
|
|
nvswitch_os_memcpy(receiveBuffer, ®Val, bytesToXfer);
|
|
|
|
receiveBuffer += bytesToXfer;
|
|
remainingBuffer -= bytesToXfer;
|
|
}
|
|
|
|
status = nvswitch_minion_send_command(device, linkId,
|
|
NV_MINION_NVLINK_DL_CMD_COMMAND_READ_RX_BUFFER_END, 0);
|
|
if (status != NV_OK)
|
|
{
|
|
NVSWITCH_PRINT(device, ERROR, "Link %d Inband Buffer receive"
|
|
"for entry %d failed\n", linkId, numEntries);
|
|
goto cleanup;
|
|
}
|
|
|
|
device->link[linkId].inbandData.message->dataSize += dataSize;
|
|
|
|
if (hdr.data & NVLINK_INBAND_DRV_HDR_TYPE_END)
|
|
{
|
|
nvswitch_filter_messages(device, linkId);
|
|
}
|
|
|
|
return;
|
|
|
|
cleanup:
|
|
tempStatus = nvswitch_minion_send_command(device, linkId,
|
|
NV_MINION_NVLINK_DL_CMD_COMMAND_CLEAR_RX_BUFFER,
|
|
0);
|
|
if (tempStatus != NVL_SUCCESS)
|
|
{
|
|
NVSWITCH_PRINT(device, ERROR, "Link %d Inband Buffer transfer for RX_BUFFER_CLEAR\n", linkId);
|
|
return;
|
|
}
|
|
if (device->link[linkId].inbandData.message != NULL)
|
|
{
|
|
nvswitch_os_free(device->link[linkId].inbandData.message);
|
|
device->link[linkId].inbandData.message = NULL;
|
|
}
|
|
//TODO: Check if we need to send a failure msg to client?
|
|
#endif
|
|
}
|
|
|
|
NvlStatus
|
|
nvswitch_minion_get_ali_debug_registers_ls10
|
|
(
|
|
nvswitch_device *device,
|
|
nvlink_link *link,
|
|
NVSWITCH_MINION_ALI_DEBUG_REGISTERS *params
|
|
)
|
|
{
|
|
NvU32 localLinkNumber = link->linkNumber % NVSWITCH_LINKS_PER_MINION_LS10;
|
|
if (nvswitch_minion_get_dl_status(device, link->linkNumber,
|
|
NV_NVLSTAT_MN00, 0, &(params->dlstatMn00)) != NVL_SUCCESS)
|
|
{
|
|
NVSWITCH_PRINT(device, INFO,"%s : Failed to poll DLSTAT _MN00 register for (%s):(%d)\n",
|
|
__FUNCTION__, device->name, link->linkNumber);
|
|
}
|
|
|
|
if (nvswitch_minion_get_dl_status(device, link->linkNumber,
|
|
NV_NVLSTAT_UC01, 0, &(params->dlstatUc01)) != NVL_SUCCESS)
|
|
{
|
|
NVSWITCH_PRINT(device, INFO,"%s : Failed to poll DLSTAT UC01 register for (%s):(%d)\n",
|
|
__FUNCTION__, device->name, link->linkNumber);
|
|
}
|
|
|
|
params->dlstatLinkIntr = NVSWITCH_MINION_LINK_RD32_LS10(device, link->linkNumber,
|
|
_MINION, _NVLINK_LINK_INTR(localLinkNumber));
|
|
|
|
return NVL_SUCCESS;
|
|
}
|
|
|