mirror of
https://github.com/NVIDIA/open-gpu-kernel-modules.git
synced 2026-02-01 14:09:47 +00:00
3068 lines
73 KiB
C
3068 lines
73 KiB
C
/*
|
|
* 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 "common_nvswitch.h"
|
|
#include "cci/cci_priv_nvswitch.h"
|
|
|
|
/* -------------------- Object construction/initialization ------------------- */
|
|
|
|
NvBool
|
|
nvswitch_is_cci_supported
|
|
(
|
|
nvswitch_device *device
|
|
)
|
|
{
|
|
return device->hal.nvswitch_is_cci_supported(device);
|
|
}
|
|
|
|
NvlStatus
|
|
nvswitch_cci_discovery
|
|
(
|
|
nvswitch_device *device
|
|
)
|
|
{
|
|
return device->hal.nvswitch_cci_discovery(device);
|
|
}
|
|
|
|
void
|
|
nvswitch_cci_setup_gpio_pins
|
|
(
|
|
nvswitch_device *device
|
|
)
|
|
{
|
|
device->hal.nvswitch_cci_setup_gpio_pins(device);
|
|
}
|
|
|
|
NvlStatus
|
|
nvswitch_cci_ports_cpld_read
|
|
(
|
|
nvswitch_device *device,
|
|
NvU8 reg,
|
|
NvU8 *pData
|
|
)
|
|
{
|
|
return device->hal.nvswitch_cci_ports_cpld_read(device, reg, pData);
|
|
}
|
|
|
|
NvlStatus
|
|
nvswitch_cci_ports_cpld_write
|
|
(
|
|
nvswitch_device *device,
|
|
NvU8 reg,
|
|
NvU8 data
|
|
)
|
|
{
|
|
return device->hal.nvswitch_cci_ports_cpld_write(device, reg, data);
|
|
}
|
|
|
|
NvlStatus
|
|
nvswitch_cci_reset
|
|
(
|
|
nvswitch_device *device
|
|
)
|
|
{
|
|
return device->hal.nvswitch_cci_reset(device);
|
|
}
|
|
|
|
NvlStatus
|
|
nvswitch_cci_reset_links
|
|
(
|
|
nvswitch_device *device,
|
|
NvU64 linkMask
|
|
)
|
|
{
|
|
return device->hal.nvswitch_cci_reset_links(device, linkMask);
|
|
}
|
|
|
|
void
|
|
nvswitch_cci_get_xcvrs_present
|
|
(
|
|
nvswitch_device *device,
|
|
NvU32 *pMaskPresent
|
|
)
|
|
{
|
|
device->hal.nvswitch_cci_get_xcvrs_present(device, pMaskPresent);
|
|
}
|
|
|
|
void
|
|
nvswitch_cci_get_xcvrs_present_change
|
|
(
|
|
nvswitch_device *device,
|
|
NvU32 *pMaskPresentChange
|
|
)
|
|
{
|
|
device->hal.nvswitch_cci_get_xcvrs_present_change(device, pMaskPresentChange);
|
|
}
|
|
|
|
void
|
|
nvswitch_cci_update_link_state_led
|
|
(
|
|
nvswitch_device *device
|
|
)
|
|
{
|
|
device->hal.nvswitch_cci_update_link_state_led(device);
|
|
}
|
|
|
|
NvlStatus
|
|
nvswitch_cci_set_xcvr_led_state
|
|
(
|
|
nvswitch_device *device,
|
|
NvU32 client,
|
|
NvU32 osfp,
|
|
NvBool bSetLocate
|
|
)
|
|
{
|
|
return device->hal.nvswitch_cci_set_xcvr_led_state(device, client, osfp, bSetLocate);
|
|
}
|
|
|
|
NvlStatus
|
|
nvswitch_cci_get_xcvr_led_state
|
|
(
|
|
nvswitch_device *device,
|
|
NvU32 client,
|
|
NvU32 osfp,
|
|
NvU8 *pLedState
|
|
)
|
|
{
|
|
return device->hal.nvswitch_cci_get_xcvr_led_state(device, client, osfp, pLedState);
|
|
}
|
|
|
|
NvlStatus
|
|
nvswitch_cci_setup_onboard
|
|
(
|
|
nvswitch_device *device
|
|
)
|
|
{
|
|
return device->hal.nvswitch_cci_setup_onboard(device);
|
|
}
|
|
|
|
NvlStatus
|
|
nvswitch_cci_setup_module_path
|
|
(
|
|
nvswitch_device *device,
|
|
NvU32 client,
|
|
NvU32 osfp
|
|
)
|
|
{
|
|
return device->hal.nvswitch_cci_setup_module_path(device, client, osfp);
|
|
}
|
|
|
|
NvlStatus
|
|
nvswitch_cci_module_access_cmd
|
|
(
|
|
nvswitch_device *device,
|
|
NvU32 client,
|
|
NvU32 osfp,
|
|
NvU32 addr,
|
|
NvU32 length,
|
|
NvU8 *pValArray,
|
|
NvBool bRead,
|
|
NvBool bBlk
|
|
)
|
|
{
|
|
return device->hal.nvswitch_cci_module_access_cmd(device, client, osfp, addr, length,
|
|
pValArray, bRead, bBlk);
|
|
}
|
|
|
|
NvlStatus
|
|
nvswitch_cci_apply_control_set_values
|
|
(
|
|
nvswitch_device *device,
|
|
NvU32 client,
|
|
NvU32 moduleMask
|
|
)
|
|
{
|
|
return device->hal.nvswitch_cci_apply_control_set_values(device, client, moduleMask);
|
|
}
|
|
|
|
NvlStatus
|
|
nvswitch_cci_cmis_cage_bezel_marking
|
|
(
|
|
nvswitch_device *device,
|
|
NvU8 cageIndex,
|
|
char *pBezelMarking
|
|
)
|
|
{
|
|
return device->hal.nvswitch_cci_cmis_cage_bezel_marking(device, cageIndex, pBezelMarking);
|
|
}
|
|
|
|
NvlStatus
|
|
nvswitch_cci_get_grading_values
|
|
(
|
|
nvswitch_device *device,
|
|
NvU32 client,
|
|
NvU32 linkId,
|
|
NvU8 *laneMask,
|
|
NVSWITCH_CCI_GRADING_VALUES *pGrading
|
|
)
|
|
{
|
|
return device->hal.nvswitch_cci_get_grading_values(device, client, linkId, laneMask, pGrading);
|
|
}
|
|
|
|
NvlStatus
|
|
nvswitch_cci_get_xcvr_mask
|
|
(
|
|
nvswitch_device *device,
|
|
NvU32 *pMaskAll,
|
|
NvU32 *pMaskPresent
|
|
)
|
|
{
|
|
return device->hal.nvswitch_cci_get_xcvr_mask(device, pMaskAll, pMaskPresent);
|
|
}
|
|
|
|
void
|
|
nvswitch_cci_set_xcvr_present
|
|
(
|
|
nvswitch_device *device,
|
|
NvU32 maskPresent
|
|
)
|
|
{
|
|
device->hal.nvswitch_cci_set_xcvr_present(device, maskPresent);
|
|
}
|
|
|
|
void
|
|
nvswitch_cci_destroy
|
|
(
|
|
nvswitch_device *device
|
|
)
|
|
{
|
|
device->hal.nvswitch_cci_destroy(device);
|
|
}
|
|
|
|
NvBool
|
|
cciSupported
|
|
(
|
|
nvswitch_device *device
|
|
)
|
|
{
|
|
PCCI pCci = device->pCci;
|
|
NvlStatus retval;
|
|
|
|
if (pCci == NULL)
|
|
{
|
|
return NV_FALSE;
|
|
}
|
|
|
|
if (pCci->bDiscovered)
|
|
{
|
|
return pCci->bSupported;
|
|
}
|
|
|
|
// Discover if CCI supported board
|
|
retval = nvswitch_cci_discovery(device);
|
|
if (retval == NVL_SUCCESS)
|
|
{
|
|
pCci->bSupported = NV_TRUE;
|
|
}
|
|
else
|
|
{
|
|
pCci->bSupported = NV_FALSE;
|
|
}
|
|
|
|
pCci->bDiscovered = NV_TRUE;
|
|
|
|
return pCci->bSupported;
|
|
}
|
|
|
|
CCI *
|
|
cciAllocNew(void)
|
|
{
|
|
CCI *pCci = nvswitch_os_malloc(sizeof(*pCci));
|
|
if (pCci != NULL)
|
|
{
|
|
nvswitch_os_memset(pCci, 0, sizeof(*pCci));
|
|
}
|
|
|
|
return pCci;
|
|
}
|
|
|
|
static void
|
|
_nvswitch_cci_poll_callback
|
|
(
|
|
nvswitch_device *device
|
|
)
|
|
{
|
|
PCCI pCci = device->pCci;
|
|
NvU32 i;
|
|
|
|
// call all functions at specified frequencies
|
|
for (i = 0; i < NVSWITCH_CCI_CALLBACK_NUM_MAX; i++)
|
|
{
|
|
if ((pCci->callbackList[i].functionPtr != NULL) &&
|
|
((pCci->callbackCounter % pCci->callbackList[i].interval) == 0))
|
|
{
|
|
pCci->callbackList[i].functionPtr(device);
|
|
}
|
|
}
|
|
pCci->callbackCounter++;
|
|
}
|
|
|
|
NvlStatus
|
|
cciInit
|
|
(
|
|
nvswitch_device *device,
|
|
CCI *pCci,
|
|
NvU32 pci_device_id
|
|
)
|
|
{
|
|
if (!nvswitch_is_tnvl_mode_enabled(device))
|
|
{
|
|
nvswitch_task_create(device, _nvswitch_cci_poll_callback,
|
|
NVSWITCH_INTERVAL_1SEC_IN_NS / NVSWITCH_CCI_POLLING_RATE_HZ,
|
|
0);
|
|
}
|
|
else
|
|
{
|
|
NVSWITCH_PRINT(device, INFO, "Skipping CCI background task when TNVL is enabled\n");
|
|
}
|
|
|
|
return NVL_SUCCESS;
|
|
}
|
|
|
|
// reverse of cciInit()
|
|
void
|
|
cciDestroy
|
|
(
|
|
nvswitch_device *device,
|
|
CCI *pCci
|
|
)
|
|
{
|
|
nvswitch_cci_destroy(device);
|
|
}
|
|
|
|
static NvBool
|
|
_nvswitch_cci_module_present
|
|
(
|
|
nvswitch_device *device,
|
|
NvU32 osfp
|
|
)
|
|
{
|
|
return !!(device->pCci->osfpMaskPresent & NVBIT(osfp));
|
|
}
|
|
|
|
static NvlStatus
|
|
_nvswitch_cci_get_module_id
|
|
(
|
|
nvswitch_device *device,
|
|
NvU32 linkId,
|
|
NvU32 *osfp
|
|
)
|
|
{
|
|
PCCI pCci = device->pCci;
|
|
NvU32 i;
|
|
|
|
for (i = 0; i < pCci->osfp_map_size; i++)
|
|
{
|
|
if (pCci->osfp_map[i].linkId == linkId)
|
|
{
|
|
*osfp = pCci->osfp_map[i].moduleId;
|
|
|
|
if (!(device->pCci->cagesMask & NVBIT(*osfp)))
|
|
{
|
|
NVSWITCH_PRINT(device, ERROR,
|
|
"%s: osfp %d associated with link %d is not supported\n",
|
|
__FUNCTION__, linkId, *osfp);
|
|
return -NVL_NOT_FOUND;
|
|
}
|
|
|
|
return NVL_SUCCESS;
|
|
}
|
|
}
|
|
|
|
return -NVL_NOT_FOUND;
|
|
}
|
|
|
|
NvlStatus
|
|
cciWrite
|
|
(
|
|
nvswitch_device *device,
|
|
NvU32 client,
|
|
NvU32 osfp,
|
|
NvU32 addr,
|
|
NvU32 length,
|
|
NvU8 *pVal
|
|
)
|
|
{
|
|
NvlStatus status = NVL_SUCCESS;
|
|
NvBool bRead = NV_FALSE;
|
|
NvBool bBlk = NV_FALSE;
|
|
|
|
if (!pVal)
|
|
{
|
|
NVSWITCH_PRINT(device, ERROR,
|
|
"%s: Bad Args!\n",
|
|
__FUNCTION__);
|
|
return -NVL_BAD_ARGS;
|
|
}
|
|
|
|
if (!device->pCci->bInitialized)
|
|
{
|
|
NVSWITCH_PRINT(device, ERROR,
|
|
"%s: CCI is not supported\n",
|
|
__FUNCTION__);
|
|
return -NVL_ERR_NOT_SUPPORTED;
|
|
}
|
|
|
|
if (!cciModulePresent(device, osfp))
|
|
{
|
|
return -NVL_NOT_FOUND;
|
|
}
|
|
|
|
status = nvswitch_cci_setup_module_path(device, client, osfp);
|
|
if (status != NVL_SUCCESS)
|
|
{
|
|
return status;
|
|
}
|
|
|
|
status = nvswitch_cci_module_access_cmd(device, client, osfp, addr,
|
|
length, pVal, bRead, bBlk);
|
|
if (status != NVL_SUCCESS)
|
|
{
|
|
return status;
|
|
}
|
|
|
|
return NVL_SUCCESS;
|
|
}
|
|
|
|
NvlStatus
|
|
cciRead
|
|
(
|
|
nvswitch_device *device,
|
|
NvU32 client,
|
|
NvU32 osfp,
|
|
NvU32 addr,
|
|
NvU32 length,
|
|
NvU8 *pVal
|
|
)
|
|
{
|
|
NvlStatus status = NVL_SUCCESS;
|
|
NvBool bRead = NV_TRUE;
|
|
NvBool bBlk = NV_FALSE;
|
|
|
|
if (!pVal)
|
|
{
|
|
NVSWITCH_PRINT(device, ERROR,
|
|
"%s: Bad Args!\n",
|
|
__FUNCTION__);
|
|
return -NVL_BAD_ARGS;
|
|
}
|
|
|
|
if (!device->pCci->bInitialized)
|
|
{
|
|
NVSWITCH_PRINT(device, ERROR,
|
|
"%s: CCI is not supported\n",
|
|
__FUNCTION__);
|
|
return -NVL_ERR_NOT_SUPPORTED;
|
|
}
|
|
|
|
if (!cciModulePresent(device, osfp))
|
|
{
|
|
return -NVL_NOT_FOUND;
|
|
}
|
|
|
|
status = nvswitch_cci_setup_module_path(device, client, osfp);
|
|
if (status != NVL_SUCCESS)
|
|
{
|
|
return status;
|
|
}
|
|
|
|
status = nvswitch_cci_module_access_cmd(device, client, osfp, addr,
|
|
length, pVal, bRead, bBlk);
|
|
if (status != NVL_SUCCESS)
|
|
{
|
|
return status;
|
|
}
|
|
|
|
return NVL_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
* @brief Set bank an page in the CMIS memory table.
|
|
*
|
|
* CMIS4 states, "For a bank change, the host shall write the Bank Select
|
|
* and Page Select registers in the same TWI transaction".
|
|
*
|
|
* Write to Page 0h, byte 126 sets the bank and page.
|
|
*/
|
|
NvlStatus
|
|
cciSetBankAndPage
|
|
(
|
|
nvswitch_device *device,
|
|
NvU32 client,
|
|
NvU32 osfp,
|
|
NvU8 bank,
|
|
NvU8 page
|
|
)
|
|
{
|
|
NvU8 temp[2];
|
|
|
|
// Modules with flat memory will fail setting of page and bank
|
|
if (device->pCci->isFlatMemory[osfp])
|
|
{
|
|
if ((page > 0x0) || (bank > 0x0))
|
|
{
|
|
return -NVL_BAD_ARGS;
|
|
}
|
|
return NVL_SUCCESS;
|
|
}
|
|
|
|
temp[0] = bank;
|
|
temp[1] = page;
|
|
|
|
return cciWrite(device, client, osfp, 126, 2, temp);
|
|
}
|
|
|
|
/*
|
|
* @brief Gets the current bank and page in the CMIS memory table.
|
|
*
|
|
* Read from Page 0h, byte 126 to get the bank and page.
|
|
*/
|
|
NvlStatus
|
|
cciGetBankAndPage
|
|
(
|
|
nvswitch_device *device,
|
|
NvU32 client,
|
|
NvU32 osfp,
|
|
NvU8 *pBank,
|
|
NvU8 *pPage
|
|
)
|
|
{
|
|
NvlStatus status;
|
|
NvU8 temp[2] = {0};
|
|
|
|
status = cciRead(device, client, osfp, 126, 2, temp);
|
|
|
|
if (pBank != NULL)
|
|
{
|
|
*pBank = temp[0];
|
|
}
|
|
|
|
if (pPage != NULL)
|
|
{
|
|
*pPage = temp[1];
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
#define NVSWITCH_CCI_MAX_CDB_LENGTH 128
|
|
|
|
/*
|
|
* @brief Send commands for Command Data Block(CDB) communication.
|
|
*
|
|
* CDB reads and writes are performed on memory map pages 9Fh-AFh.
|
|
*
|
|
* Page 9Fh is used to specify the CDB command and use
|
|
* local payload (LPL) of 120 bytes.
|
|
*
|
|
* Pages A0h-AFh contain up to 2048 bytes of extended payload (EPL).
|
|
*
|
|
* Payload may be a zero-length array if no payload to send.
|
|
*
|
|
* (ref CMIS rev4.0, sections 7.2, 8.13, 8.2.7, 8.4.11).
|
|
*/
|
|
NvlStatus
|
|
cciSendCDBCommand
|
|
(
|
|
nvswitch_device *device,
|
|
NvU32 client,
|
|
NvU32 osfp,
|
|
NvU32 command,
|
|
NvU32 length,
|
|
NvU8 *payload,
|
|
NvBool padding
|
|
)
|
|
{
|
|
NvU8 cmd_msb = (command >> 8) & 0xff;
|
|
NvU8 cmd_lsb = command & 0xff;
|
|
NvU32 chkcode;
|
|
NvU32 i;
|
|
NvU8 temp[8];
|
|
|
|
if (length > NVSWITCH_CCI_MAX_CDB_LENGTH)
|
|
{
|
|
NVSWITCH_PRINT(device, ERROR,
|
|
"%s: Command length %d exceeded max length %d. "
|
|
"CDB yet to support extended payloads\n",
|
|
__FUNCTION__, length, NVSWITCH_CCI_MAX_CDB_LENGTH);
|
|
return -NVL_ERR_GENERIC;
|
|
}
|
|
|
|
//
|
|
// Compute checksum over payload, including header bytes(command, length,...)
|
|
//
|
|
// CdbChkCode is the ones complement of the summation of page 9Fh,
|
|
// bytes 128 to (135+LPL_Length) with bytes 133,134 and 135 equal to 0.
|
|
// (ref CMIS 4.0, sec 8.13)
|
|
//
|
|
chkcode = cmd_msb + cmd_lsb + length;
|
|
|
|
// now add payload bytes
|
|
if (length > 0)
|
|
{
|
|
for (i = 0; i < length; i++)
|
|
{
|
|
chkcode += payload[i];
|
|
}
|
|
}
|
|
chkcode = (~(chkcode & 0xff)) & 0xff;
|
|
|
|
// # nw: page 198 of cmis4 spec.
|
|
// # nw:
|
|
|
|
// Set page to 0x9F to setup CDB command
|
|
cciSetBankAndPage(device, client, osfp, 0, 0x9f);
|
|
|
|
//
|
|
// Send CDB message
|
|
//
|
|
// Fill page 9Fh bytes 128-135 in the order -
|
|
// [cmd_msb, cmd_lsb, epl_length msb, epl_length lsb, lpl_length,
|
|
// chkcode, resp_length = 0, resp_chkcode = 0]
|
|
//
|
|
// LPL starts from Bytes 136 upto 120 bytes.
|
|
// The command is triggered when byte 129 is written. So fill bytes 128, 129 at the end.
|
|
//
|
|
|
|
// #1. Write bytes 130-135. The "header portion", minus the first two bytes
|
|
// which is the command code.
|
|
temp[0] = 0; // epl_length msb
|
|
temp[1] = 0; // epl_length lsb
|
|
temp[2] = length; // lpl_length
|
|
temp[3] = chkcode; // cdb chkcode
|
|
temp[4] = 0; // response length
|
|
temp[5] = 0; // response chkcode
|
|
cciWrite(device, client, osfp, 130, 6, temp);
|
|
|
|
// #2. If payload's not empty, write the payload (bytes 136 to 255).
|
|
// If payload is empty, infer the command is payload-less and skip.
|
|
if ((length > 0) && padding)
|
|
{
|
|
for (i = length; i < CMIS_CDB_LPL_MAX_SIZE; i++)
|
|
{
|
|
payload[i] = 0;
|
|
}
|
|
cciWrite(device, client, osfp, 136, CMIS_CDB_LPL_MAX_SIZE, payload);
|
|
}
|
|
else if ((length > 0) && !padding)
|
|
{
|
|
cciWrite(device, client, osfp, 136, length, payload);
|
|
}
|
|
|
|
// # 3. Write the command code (bytes 128,129), which additionally
|
|
// kicks off processing of the command by the module.
|
|
temp[0] = cmd_msb;
|
|
temp[1] = cmd_lsb;
|
|
cciWrite(device, client, osfp, 128, 2, temp);
|
|
|
|
return NVL_SUCCESS;
|
|
}
|
|
|
|
/*!
|
|
* @brief Waits for CDB command completion and returns status.
|
|
*
|
|
* Page 00h byte 37 contains status bits.
|
|
* (see CMIS rev4.0, Table 9-3, CDB Command 0000h: QUERY-Status)
|
|
*/
|
|
NvlStatus
|
|
cciGetCDBStatus
|
|
(
|
|
nvswitch_device *device,
|
|
NvU32 client,
|
|
NvU32 osfp,
|
|
NvU8 *pStatus
|
|
)
|
|
{
|
|
NvBool status_busy;
|
|
NvBool status_fail;
|
|
NvU8 cdb_result;
|
|
NvU8 status;
|
|
NVSWITCH_TIMEOUT timeout;
|
|
|
|
status = 0;
|
|
|
|
nvswitch_timeout_create(10 * NVSWITCH_INTERVAL_1SEC_IN_NS, &timeout);
|
|
|
|
do
|
|
{
|
|
cciRead(device, client, osfp, CMIS_CDB_BLOCK_STATUS_BYTE(0), 1, &status);
|
|
*pStatus = status;
|
|
|
|
// Quit when the STS_BUSY bit goes to 0
|
|
if (FLD_TEST_REF(CMIS_CDB_BLOCK_STATUS_BYTE_BUSY, _FALSE, status))
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (nvswitch_timeout_check(&timeout))
|
|
{
|
|
NVSWITCH_PRINT(device, ERROR,
|
|
"%s: Timeout waiting for CDB command to complete! "
|
|
"STATUS = 0x%x\n",
|
|
__FUNCTION__, status);
|
|
break;
|
|
}
|
|
|
|
nvswitch_os_sleep(10);
|
|
} while (NV_TRUE);
|
|
|
|
status_busy = (status >> 7) & 0x1;
|
|
status_fail = (status >> 6) & 0x1;
|
|
cdb_result = status & 0x3f;
|
|
|
|
if (status_busy) // status is busy
|
|
{
|
|
if (cdb_result == 0x01)
|
|
{
|
|
NVSWITCH_PRINT(device, INFO,
|
|
"%s: CDB status = BUSY. Last Command Result: "
|
|
"'Command is captured but not processed'\n",
|
|
__FUNCTION__);
|
|
}
|
|
else if (cdb_result == 0x02)
|
|
{
|
|
NVSWITCH_PRINT(device, INFO,
|
|
"%s: CDB status = BUSY. Last Command Result: "
|
|
"'Command checking is in progress'\n",
|
|
__FUNCTION__);
|
|
}
|
|
else if (cdb_result == 0x03)
|
|
{
|
|
NVSWITCH_PRINT(device, ERROR,
|
|
"%s: CDB status = BUSY. Last Command Result: "
|
|
"'Command execution is in progress'\n",
|
|
__FUNCTION__);
|
|
}
|
|
else
|
|
{
|
|
NVSWITCH_PRINT(device, ERROR,
|
|
"%s: CDB status = BUSY. Last Command Result: "
|
|
"Unknown (0x%x)\n",
|
|
__FUNCTION__, cdb_result);
|
|
}
|
|
|
|
return -NVL_ERR_GENERIC;
|
|
}
|
|
|
|
if (status_fail) // status failed
|
|
{
|
|
if (cdb_result == 0x01)
|
|
{
|
|
NVSWITCH_PRINT(device, ERROR,
|
|
"%s: CDB status = FAIL. Last Command Result: "
|
|
"'CMD Code unknown'\n",
|
|
__FUNCTION__);
|
|
}
|
|
else if (cdb_result == 0x02)
|
|
{
|
|
NVSWITCH_PRINT(device, ERROR,
|
|
"%s: CDB status = FAIL. Last Command Result: "
|
|
"'Parameter range error or not supported'\n",
|
|
__FUNCTION__);
|
|
}
|
|
else if (cdb_result == 0x03)
|
|
{
|
|
NVSWITCH_PRINT(device, ERROR,
|
|
"%s: CDB status = FAIL. Last Command Result: "
|
|
"'Previous CMD was not ABORTED by CMD Abort'\n",
|
|
__FUNCTION__);
|
|
}
|
|
else if (cdb_result == 0x04)
|
|
{
|
|
NVSWITCH_PRINT(device, ERROR,
|
|
"%s: CDB status = FAIL. Last Command Result: "
|
|
"'Command checking time out'\n",
|
|
__FUNCTION__);
|
|
}
|
|
else if (cdb_result == 0x05)
|
|
{
|
|
NVSWITCH_PRINT(device, ERROR,
|
|
"%s: CDB status = FAIL. Last Command Result: "
|
|
"'CdbCheckCode Error'\n",
|
|
__FUNCTION__);
|
|
}
|
|
else if (cdb_result == 0x06)
|
|
{
|
|
NVSWITCH_PRINT(device, ERROR,
|
|
"%s: CDB status = FAIL. Last Command Result: "
|
|
"'Password Error'\n",
|
|
__FUNCTION__);
|
|
}
|
|
else
|
|
{
|
|
NVSWITCH_PRINT(device, ERROR,
|
|
"%s: CDB status = FAIL. Last Command Result: "
|
|
"Unknown (0x%x)\n",
|
|
__FUNCTION__, cdb_result);
|
|
}
|
|
|
|
return -NVL_ERR_GENERIC;
|
|
}
|
|
|
|
return NVL_SUCCESS;
|
|
}
|
|
|
|
/*!
|
|
* @brief Waits for CDB command completion.
|
|
*
|
|
* Page 00h byte 37 contains status bits. BIT 7 is the busy bit.
|
|
* (see CMIS rev4.0, Table 9-3, CDB Command 0000h: QUERY-Status)
|
|
*/
|
|
NvlStatus
|
|
cciWaitForCDBComplete
|
|
(
|
|
nvswitch_device *device,
|
|
NvU32 client,
|
|
NvU32 osfp
|
|
)
|
|
{
|
|
NVSWITCH_TIMEOUT timeout;
|
|
NvU8 status;
|
|
|
|
status = 0;
|
|
|
|
nvswitch_timeout_create(NVSWITCH_INTERVAL_1SEC_IN_NS, &timeout);
|
|
|
|
do
|
|
{
|
|
cciRead(device, client, osfp, CMIS_CDB_BLOCK_STATUS_BYTE(0), 1, &status);
|
|
|
|
// Return when the STS_BUSY bit goes to 0
|
|
if (FLD_TEST_REF(CMIS_CDB_BLOCK_STATUS_BYTE_BUSY, _FALSE, status))
|
|
{
|
|
return NVL_SUCCESS;
|
|
}
|
|
|
|
if (nvswitch_timeout_check(&timeout))
|
|
{
|
|
NVSWITCH_PRINT(device, ERROR,
|
|
"%s: Timeout waiting for CDB command to complete! STATUS = 0x%x\n",
|
|
__FUNCTION__, status);
|
|
return -NVL_ERR_GENERIC;
|
|
}
|
|
|
|
nvswitch_os_sleep(10);
|
|
} while (NV_TRUE);
|
|
}
|
|
|
|
/*!
|
|
* @brief Get the CDB response data.
|
|
*
|
|
* This function must be sent after CDB status is success.
|
|
*
|
|
* Page 9Fh, bytes 134-255 contains response data
|
|
* Byte 134 : Response LPL Length of the data returned by CDB command code.
|
|
* Byte 135 : Response LPL ChkCode
|
|
* Bytes 136-255 : Local payload of the module response.
|
|
*/
|
|
NvlStatus
|
|
cciGetCDBResponse
|
|
(
|
|
nvswitch_device *device,
|
|
NvU32 client,
|
|
NvU32 osfp,
|
|
NvU8 *response,
|
|
NvU32 *resLength
|
|
)
|
|
{
|
|
NvU8 header[8] = {0};
|
|
NvU32 rlpllen; // Response local payload length
|
|
NvU8 rlplchkcode; // Response local payload check code
|
|
NvU8 chksum = 0;
|
|
NvU32 i;
|
|
NvBool bSkipChecksum = NV_FALSE;
|
|
|
|
cciSetBankAndPage(device, client, osfp, 0, 0x9f);
|
|
|
|
// get header
|
|
cciRead(device, client, osfp, 128, 8, header);
|
|
|
|
// get reported response length
|
|
rlpllen = header[6];
|
|
rlplchkcode = header[7];
|
|
|
|
// TODO : Remove this once FW support improves
|
|
if (rlpllen == 0)
|
|
{
|
|
// bug with earlier Stallion FW, presume hit it an read maximum-sized lpl.
|
|
// Assume the maximum length of 120 and skip checksum because it will also
|
|
// be zero
|
|
rlpllen = CMIS_CDB_LPL_MAX_SIZE;
|
|
bSkipChecksum = NV_TRUE;
|
|
}
|
|
|
|
if (rlpllen > CMIS_CDB_LPL_MAX_SIZE)
|
|
{
|
|
NVSWITCH_PRINT(device, ERROR,
|
|
"%s: Error: Invalid CDB response length: %d\n",
|
|
__FUNCTION__, rlpllen);
|
|
return -NVL_ERR_GENERIC;
|
|
}
|
|
|
|
if (rlpllen != 0)
|
|
{
|
|
// get response
|
|
cciRead(device, client, osfp, 136, rlpllen, response);
|
|
|
|
if (!bSkipChecksum)
|
|
{
|
|
// compute checksum of response
|
|
for (i = 0; i < rlpllen; i++)
|
|
{
|
|
chksum += response[i];
|
|
}
|
|
|
|
// and compare against rlplchkcode (byte 7 of page 9Fh)
|
|
if ((~chksum & 0xff) != rlplchkcode)
|
|
{
|
|
NVSWITCH_PRINT(device, ERROR,
|
|
"%s: Error: RLPLChkCode incorrect for returned data\n",
|
|
__FUNCTION__);
|
|
return -NVL_ERR_GENERIC;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (resLength != NULL)
|
|
{
|
|
*resLength = rlpllen;
|
|
}
|
|
|
|
return NVL_SUCCESS;
|
|
}
|
|
|
|
/*!
|
|
* @brief Get the CDB command and get response.
|
|
*/
|
|
NvlStatus
|
|
cciSendCDBCommandAndGetResponse
|
|
(
|
|
nvswitch_device *device,
|
|
NvU32 client,
|
|
NvU32 osfp,
|
|
NvU32 command,
|
|
NvU32 payLength,
|
|
NvU8 *payload,
|
|
NvU32 *resLength,
|
|
NvU8 *response,
|
|
NvBool padding
|
|
)
|
|
{
|
|
NvlStatus retval;
|
|
NvU8 status = 0;
|
|
|
|
if (!cciModulePresent(device, osfp))
|
|
{
|
|
NVSWITCH_PRINT(device, INFO,
|
|
"%s: osfp %d is missing\n",
|
|
__FUNCTION__, osfp);
|
|
return -NVL_NOT_FOUND;
|
|
}
|
|
|
|
// Wait for CDB status to be free
|
|
retval = cciWaitForCDBComplete(device, client, osfp);
|
|
if (retval != NVL_SUCCESS)
|
|
{
|
|
NVSWITCH_PRINT(device, INFO,
|
|
"%s: CDB is busy!!\n",
|
|
__FUNCTION__);
|
|
return -NVL_ERR_GENERIC;
|
|
}
|
|
|
|
retval = cciSendCDBCommand(device, client, osfp, command, payLength, payload, padding);
|
|
if (retval != NVL_SUCCESS)
|
|
{
|
|
NVSWITCH_PRINT(device, INFO,
|
|
"%s: Failed to send CDB Command: 0x%x\n",
|
|
__FUNCTION__, command);
|
|
return -NVL_ERR_GENERIC;
|
|
}
|
|
|
|
retval = cciGetCDBStatus(device, client, osfp, &status);
|
|
if (retval != NVL_SUCCESS)
|
|
{
|
|
NVSWITCH_PRINT(device, ERROR,
|
|
"%s: CDB command failed! result = 0x%x\n",
|
|
__FUNCTION__, status);
|
|
return -NVL_ERR_GENERIC;
|
|
}
|
|
|
|
retval = cciGetCDBResponse(device, client, osfp, response, resLength);
|
|
if (retval != NVL_SUCCESS)
|
|
{
|
|
NVSWITCH_PRINT(device, ERROR,
|
|
"%s: Failed to get CDB command response\n",
|
|
__FUNCTION__);
|
|
return -NVL_ERR_GENERIC;
|
|
}
|
|
|
|
return NVL_SUCCESS;
|
|
}
|
|
|
|
NvlStatus
|
|
cciRegisterCallback
|
|
(
|
|
nvswitch_device *device,
|
|
NvU32 callbackId,
|
|
void (*functionPtr)(nvswitch_device*),
|
|
NvU32 rateHz
|
|
)
|
|
{
|
|
PCCI pCci = device->pCci;
|
|
|
|
if ((callbackId >= NVSWITCH_CCI_CALLBACK_NUM_MAX) ||
|
|
(functionPtr == NULL))
|
|
{
|
|
return -NVL_BAD_ARGS;
|
|
}
|
|
|
|
if ((rateHz == 0) || ((NVSWITCH_CCI_POLLING_RATE_HZ % rateHz) != 0))
|
|
{
|
|
NVSWITCH_PRINT(device, ERROR,
|
|
"%s: Input rate must divide main polling rate: %d\n",
|
|
__FUNCTION__, NVSWITCH_CCI_POLLING_RATE_HZ);
|
|
return -NVL_BAD_ARGS;
|
|
}
|
|
|
|
if (pCci->callbackList[callbackId].functionPtr != NULL)
|
|
{
|
|
NVSWITCH_PRINT(device, SETUP,
|
|
"%s: CCI callback previously set.\n",
|
|
__FUNCTION__);
|
|
}
|
|
|
|
pCci->callbackList[callbackId].interval = NVSWITCH_CCI_POLLING_RATE_HZ/rateHz;
|
|
pCci->callbackList[callbackId].functionPtr = functionPtr;
|
|
|
|
return NVL_SUCCESS;
|
|
}
|
|
|
|
// CCI CONTROL CALLS
|
|
NvlStatus
|
|
nvswitch_ctrl_get_cci_fw_revisions
|
|
(
|
|
nvswitch_device *device,
|
|
NVSWITCH_CCI_GET_FW_REVISION_PARAMS *pParams
|
|
)
|
|
{
|
|
return cciGetFWRevisions(device, 0, pParams->linkId,
|
|
pParams->revisions);
|
|
}
|
|
|
|
NvlStatus
|
|
nvswitch_ctrl_get_grading_values
|
|
(
|
|
nvswitch_device *device,
|
|
NVSWITCH_CCI_GET_GRADING_VALUES_PARAMS *pParams
|
|
)
|
|
{
|
|
return cciGetGradingValues(device, 0, pParams->linkId,
|
|
&pParams->laneMask, &pParams->grading);
|
|
}
|
|
|
|
NvlStatus
|
|
nvswitch_ctrl_get_ports_cpld_info
|
|
(
|
|
nvswitch_device *device,
|
|
NVSWITCH_CCI_GET_PORTS_CPLD_INFO_PARAMS *pParams
|
|
)
|
|
{
|
|
NvlStatus retval = NVL_SUCCESS;
|
|
NvU8 val;
|
|
|
|
if ((device->pCci == NULL) || (!device->pCci->bInitialized))
|
|
{
|
|
NVSWITCH_PRINT(device, ERROR,
|
|
"%s: CCI not supported\n",
|
|
__FUNCTION__);
|
|
return -NVL_ERR_NOT_SUPPORTED;
|
|
}
|
|
|
|
retval = nvswitch_cci_ports_cpld_read(device, CPLD_MACHXO3_VERSION_MAJOR, &val);
|
|
if (retval != NVL_SUCCESS)
|
|
{
|
|
return -NVL_IO_ERROR;
|
|
}
|
|
pParams->versionMajor = val;
|
|
|
|
retval = nvswitch_cci_ports_cpld_read(device, CPLD_MACHXO3_VERSION_MINOR, &val);
|
|
if (retval != NVL_SUCCESS)
|
|
{
|
|
return -NVL_IO_ERROR;
|
|
}
|
|
pParams->versionMinor = val;
|
|
|
|
return NVL_SUCCESS;
|
|
}
|
|
|
|
NvlStatus
|
|
nvswitch_ctrl_set_locate_led
|
|
(
|
|
nvswitch_device *device,
|
|
NVSWITCH_CCI_SET_LOCATE_LED_PARAMS *pParams
|
|
)
|
|
{
|
|
NvlStatus retval = NVL_SUCCESS;
|
|
NvU32 cagesMask;
|
|
NvU8 ledState;
|
|
|
|
if ((device->pCci == NULL) || (!device->pCci->bInitialized))
|
|
{
|
|
NVSWITCH_PRINT(device, ERROR,
|
|
"%s: CCI not supported\n",
|
|
__FUNCTION__);
|
|
return -NVL_ERR_NOT_SUPPORTED;
|
|
}
|
|
|
|
retval = cciGetXcvrMask(device, &cagesMask, NULL);
|
|
if (retval != NVL_SUCCESS)
|
|
{
|
|
return retval;
|
|
}
|
|
|
|
if (!(cagesMask & NVBIT(pParams->cageIndex)))
|
|
{
|
|
NVSWITCH_PRINT(device, ERROR,
|
|
"%s: Module cage does not exist\n",
|
|
__FUNCTION__);
|
|
return -NVL_BAD_ARGS;
|
|
}
|
|
|
|
if (pParams->portNum > 1)
|
|
{
|
|
NVSWITCH_PRINT(device, ERROR,
|
|
"%s: Invalid port number\n",
|
|
__FUNCTION__);
|
|
return -NVL_BAD_ARGS;
|
|
}
|
|
|
|
ledState = device->pCci->xcvrCurrentLedState[pParams->cageIndex];
|
|
|
|
if (pParams->portNum == 0)
|
|
{
|
|
ledState = FLD_SET_REF_NUM(CCI_LED_STATE_LED_A,
|
|
pParams->bSetLocateOn ? CCI_LED_STATE_LOCATE :
|
|
CCI_LED_STATE_OFF,
|
|
ledState);
|
|
}
|
|
else
|
|
{
|
|
ledState = FLD_SET_REF_NUM(CCI_LED_STATE_LED_B,
|
|
pParams->bSetLocateOn ? CCI_LED_STATE_LOCATE :
|
|
CCI_LED_STATE_OFF,
|
|
ledState);
|
|
}
|
|
|
|
cciSetNextXcvrLedState(device, NVSWITCH_I2C_ACQUIRER_CCI_UX,
|
|
pParams->cageIndex, ledState);
|
|
cciSetXcvrLedState(device, NVSWITCH_I2C_ACQUIRER_CCI_UX,
|
|
pParams->cageIndex, NV_FALSE);
|
|
|
|
return NVL_SUCCESS;
|
|
}
|
|
|
|
NvlStatus
|
|
nvswitch_ctrl_cci_request_ali
|
|
(
|
|
nvswitch_device *device,
|
|
NVSWITCH_REQUEST_ALI_PARAMS *pParams
|
|
)
|
|
{
|
|
if ((device->pCci == NULL) || (!device->pCci->bInitialized))
|
|
{
|
|
NVSWITCH_PRINT(device, ERROR,
|
|
"%s: CCI not supported\n",
|
|
__FUNCTION__);
|
|
return -NVL_ERR_NOT_SUPPORTED;
|
|
}
|
|
|
|
return cciRequestALI(device, pParams->linkMaskTrain);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
/*
|
|
* @Brief : Reset CCI on this device.
|
|
*
|
|
* This is temporary and eventually moved to bios.
|
|
* Tracking this in bug 200668898.
|
|
*
|
|
*/
|
|
static NvlStatus
|
|
_nvswitch_reset_cci
|
|
(
|
|
nvswitch_device *device
|
|
)
|
|
{
|
|
return nvswitch_cci_reset(device);
|
|
}
|
|
|
|
/*
|
|
* @Brief : Execute CCI pre-reset sequence for secure reset.
|
|
*/
|
|
static NvlStatus
|
|
_nvswitch_cci_prepare_for_reset
|
|
(
|
|
nvswitch_device *device
|
|
)
|
|
{
|
|
nvswitch_cci_setup_gpio_pins(device);
|
|
|
|
return NVL_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
* Check for CMIS boards by pinging on OSFP devices
|
|
*/
|
|
static NvlStatus
|
|
_nvswitch_identify_cci_devices
|
|
(
|
|
nvswitch_device *device
|
|
)
|
|
{
|
|
PCCI pCci = device->pCci;
|
|
NvU32 maskPresent;
|
|
|
|
maskPresent = 0;
|
|
|
|
nvswitch_cci_get_xcvrs_present(device, &maskPresent);
|
|
pCci->osfpMaskPresent = maskPresent;
|
|
pCci->osfpMaskAll = pCci->cagesMask;
|
|
|
|
NVSWITCH_PRINT(device, SETUP,
|
|
"%s: Identified modules mask: 0x%x\n",
|
|
__FUNCTION__, pCci->osfpMaskPresent);
|
|
|
|
pCci->bInitialized = NV_TRUE;
|
|
|
|
return NVL_SUCCESS;
|
|
}
|
|
|
|
NvBool
|
|
cciIsLinkManaged
|
|
(
|
|
nvswitch_device *device,
|
|
NvU32 linkNumber
|
|
)
|
|
{
|
|
if ((device->pCci == NULL) || (!device->pCci->bInitialized))
|
|
{
|
|
return NV_FALSE;
|
|
}
|
|
|
|
return !!(device->pCci->linkMask & NVBIT64(linkNumber));
|
|
}
|
|
|
|
NvlStatus
|
|
cciGetLinkMode
|
|
(
|
|
nvswitch_device *device,
|
|
NvU32 linkNumber,
|
|
NvU64 *mode
|
|
)
|
|
{
|
|
if ((device->pCci == NULL) || (!device->pCci->bInitialized))
|
|
{
|
|
NVSWITCH_PRINT(device, ERROR,
|
|
"%s: CCI not supported\n",
|
|
__FUNCTION__);
|
|
return -NVL_ERR_NOT_SUPPORTED;
|
|
}
|
|
|
|
if (mode == NULL)
|
|
{
|
|
return -NVL_BAD_ARGS;
|
|
}
|
|
|
|
if (!cciIsLinkManaged(device, linkNumber))
|
|
{
|
|
return -NVL_BAD_ARGS;
|
|
}
|
|
|
|
if (cciLinkTrainIdle(device, linkNumber))
|
|
{
|
|
*mode = NVLINK_LINKSTATE_OFF;
|
|
}
|
|
else
|
|
{
|
|
*mode = NVLINK_LINKSTATE_TRAINING_CCI;
|
|
}
|
|
|
|
return NVL_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
* @Brief : Bootstrap CCI on the specified device
|
|
*
|
|
* @param[in] device Bootstrap CCI on this device
|
|
*/
|
|
NvlStatus
|
|
cciLoad
|
|
(
|
|
nvswitch_device *device
|
|
)
|
|
{
|
|
NvlStatus status;
|
|
|
|
if (IS_FMODEL(device) || IS_RTLSIM(device))
|
|
{
|
|
NVSWITCH_PRINT(device, SETUP,
|
|
"%s: Skipping CCI init on preSilicon.\n",
|
|
__FUNCTION__);
|
|
return -NVL_ERR_NOT_SUPPORTED;
|
|
}
|
|
|
|
if (device->pCci == NULL)
|
|
{
|
|
NVSWITCH_PRINT_SXID(device, NVSWITCH_ERR_HW_CCI_INIT,
|
|
"Failed to init CCI(0)\n");
|
|
return -NVL_BAD_ARGS;
|
|
}
|
|
|
|
// Prepare CCI for reset.
|
|
status = _nvswitch_cci_prepare_for_reset(device);
|
|
if (status != NVL_SUCCESS)
|
|
{
|
|
NVSWITCH_PRINT_SXID(device, NVSWITCH_ERR_HW_CCI_RESET,
|
|
"Failed to reset CCI(0)\n");
|
|
return status;
|
|
}
|
|
|
|
// Reset CCI
|
|
status = _nvswitch_reset_cci(device);
|
|
if (status != NVL_SUCCESS)
|
|
{
|
|
NVSWITCH_PRINT_SXID(device, NVSWITCH_ERR_HW_CCI_RESET,
|
|
"Failed to reset CCI(1)\n");
|
|
return status;
|
|
}
|
|
|
|
// Identify CCI devices
|
|
status = _nvswitch_identify_cci_devices(device);
|
|
if (status != NVL_SUCCESS)
|
|
{
|
|
NVSWITCH_PRINT_SXID(device, NVSWITCH_ERR_HW_CCI_INIT,
|
|
"Failed to init CCI(1)\n");
|
|
return status;
|
|
}
|
|
|
|
// Complete CCI setup
|
|
status = nvswitch_cci_setup_onboard(device);
|
|
if (status != NVL_SUCCESS)
|
|
{
|
|
NVSWITCH_PRINT_SXID(device, NVSWITCH_ERR_HW_CCI_INIT,
|
|
"Failed to init CCI(2)\n");
|
|
return status;
|
|
}
|
|
|
|
NVSWITCH_PRINT(device, SETUP,
|
|
"%s: CCI load successful.\n",
|
|
__FUNCTION__);
|
|
|
|
return status;
|
|
}
|
|
|
|
/*
|
|
* @brief Get FW revisions of the osfp device by link Id
|
|
*
|
|
* Module FW revision is obtained from CDB command 0x100.
|
|
* (ref CMIS rev4.0, Table 9-16 CDB Command 0100h: Get firmware Info)
|
|
*/
|
|
NvlStatus
|
|
cciGetFWRevisions
|
|
(
|
|
nvswitch_device *device,
|
|
NvU32 client,
|
|
NvU32 linkId,
|
|
NVSWITCH_CCI_GET_FW_REVISIONS *pRevisions
|
|
)
|
|
{
|
|
NvU32 osfp;
|
|
|
|
if ((device->pCci == NULL) || (!device->pCci->bInitialized))
|
|
{
|
|
NVSWITCH_PRINT(device, ERROR,
|
|
"%s: CCI not supported\n",
|
|
__FUNCTION__);
|
|
return -NVL_ERR_NOT_SUPPORTED;
|
|
}
|
|
|
|
if (cciGetModuleId(device, linkId, &osfp) != NVL_SUCCESS)
|
|
{
|
|
NVSWITCH_PRINT(device, ERROR,
|
|
"%s: Failed to get moduleid associated with link %d\n",
|
|
__FUNCTION__, linkId);
|
|
return -NVL_ERR_NOT_SUPPORTED;
|
|
}
|
|
|
|
return cciGetXcvrFWRevisions(device, client, osfp, pRevisions);
|
|
}
|
|
|
|
static NvlStatus
|
|
_cciGetXcvrFWRevisionsFlatMem
|
|
(
|
|
nvswitch_device *device,
|
|
NvU32 client,
|
|
NvU32 osfp,
|
|
NVSWITCH_CCI_GET_FW_REVISIONS *pRevisions
|
|
)
|
|
{
|
|
NvlStatus retVal;
|
|
NvU8 versionByte;
|
|
|
|
retVal = cciCmisRead(device, osfp, 0, 0,
|
|
CMIS_ACTIVE_FW_MAJOR_REVISION, 1, &versionByte);
|
|
if (retVal != NVL_SUCCESS)
|
|
{
|
|
return retVal;
|
|
}
|
|
|
|
pRevisions[NVSWITCH_CCI_FW_IMAGE_FACTORY].major = versionByte;
|
|
|
|
retVal = cciCmisRead(device, osfp, 0, 0,
|
|
CMIS_ACTIVE_FW_MINOR_REVISION, 1, &versionByte);
|
|
if (retVal != NVL_SUCCESS)
|
|
{
|
|
return retVal;
|
|
}
|
|
|
|
pRevisions[NVSWITCH_CCI_FW_IMAGE_FACTORY].minor = versionByte;
|
|
|
|
if (pRevisions[NVSWITCH_CCI_FW_IMAGE_FACTORY].major ||
|
|
pRevisions[NVSWITCH_CCI_FW_IMAGE_FACTORY].minor)
|
|
{
|
|
pRevisions[NVSWITCH_CCI_FW_IMAGE_FACTORY].flags =
|
|
FLD_SET_DRF(SWITCH, _CCI_FW_FLAGS, _ACTIVE, _YES,
|
|
pRevisions[NVSWITCH_CCI_FW_IMAGE_FACTORY].flags);
|
|
pRevisions[NVSWITCH_CCI_FW_IMAGE_FACTORY].flags =
|
|
FLD_SET_DRF(SWITCH, _CCI_FW_FLAGS, _PRESENT, _YES,
|
|
pRevisions[NVSWITCH_CCI_FW_IMAGE_FACTORY].flags);
|
|
}
|
|
|
|
// Entries for images A and B will be left as 0
|
|
|
|
return NVL_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
* @brief Get FW revisions of the osfp device by OSFP xceiver index
|
|
*
|
|
* Module FW revision is obtained from CDB command 0x100.
|
|
* (ref CMIS rev4.0, Table 9-16 CDB Command 0100h: Get firmware Info)
|
|
*/
|
|
NvlStatus
|
|
cciGetXcvrFWRevisions
|
|
(
|
|
nvswitch_device *device,
|
|
NvU32 client,
|
|
NvU32 osfp,
|
|
NVSWITCH_CCI_GET_FW_REVISIONS *pRevisions
|
|
)
|
|
{
|
|
NvU8 response[CMIS_CDB_LPL_MAX_SIZE];
|
|
NvU32 resLength;
|
|
NvlStatus retVal;
|
|
NvU8 status;
|
|
|
|
if ((device->pCci == NULL) || (!device->pCci->bInitialized))
|
|
{
|
|
NVSWITCH_PRINT(device, ERROR,
|
|
"%s: CCI not supported\n",
|
|
__FUNCTION__);
|
|
return -NVL_ERR_NOT_SUPPORTED;
|
|
}
|
|
|
|
if (osfp >= (sizeof(device->pCci->osfpMaskAll) * 8))
|
|
{
|
|
return -NVL_BAD_ARGS;
|
|
}
|
|
|
|
if (!(device->pCci->osfpMaskPresent & NVBIT(osfp)))
|
|
{
|
|
NVSWITCH_PRINT(device, ERROR,
|
|
"%s: osfp %d is missing\n",
|
|
__FUNCTION__, osfp);
|
|
return -NVL_ERR_NOT_SUPPORTED;
|
|
}
|
|
|
|
nvswitch_os_memset(pRevisions, 0, sizeof(NVSWITCH_CCI_GET_FW_REVISIONS));
|
|
|
|
if (device->pCci->isFlatMemory[osfp])
|
|
{
|
|
retVal = _cciGetXcvrFWRevisionsFlatMem(device, client, osfp, pRevisions);
|
|
if (retVal != NVL_SUCCESS)
|
|
{
|
|
NVSWITCH_PRINT(device, ERROR,
|
|
"%s: Failed to get FW revisions\n",
|
|
__FUNCTION__);
|
|
return -NVL_ERR_GENERIC;
|
|
}
|
|
|
|
return NVL_SUCCESS;
|
|
}
|
|
|
|
retVal = cciSendCDBCommandAndGetResponse(device, client, osfp,
|
|
0x100, 0, NULL, &resLength, response, NV_FALSE);
|
|
if (retVal != NVL_SUCCESS)
|
|
{
|
|
NVSWITCH_PRINT(device, ERROR,
|
|
"%s: Failed to get FW revisions\n",
|
|
__FUNCTION__);
|
|
return -NVL_ERR_GENERIC;
|
|
}
|
|
|
|
// Byte 0(or 136) contains FW status
|
|
status = response[0];
|
|
|
|
pRevisions[NVSWITCH_CCI_FW_IMAGE_FACTORY].flags = 0;
|
|
if (status == 0)
|
|
{
|
|
NVSWITCH_PRINT(device, INFO,
|
|
"%s: Factory Boot Image is Running\n",
|
|
__FUNCTION__);
|
|
|
|
pRevisions[NVSWITCH_CCI_FW_IMAGE_FACTORY].flags =
|
|
FLD_SET_DRF(SWITCH, _CCI_FW_FLAGS, _ACTIVE, _YES,
|
|
pRevisions[NVSWITCH_CCI_FW_IMAGE_FACTORY].flags);
|
|
}
|
|
|
|
if (response[1] & NVBIT(2))
|
|
{
|
|
//
|
|
// For Factory Image,
|
|
// Byte 74(or 210) contains major revision
|
|
// Byte 75(or 211) contains minor revision
|
|
// Byte 76, 77(or 212, 213) contains build number
|
|
//
|
|
pRevisions[NVSWITCH_CCI_FW_IMAGE_FACTORY].flags =
|
|
FLD_SET_DRF(SWITCH, _CCI_FW_FLAGS, _PRESENT, _YES,
|
|
pRevisions[NVSWITCH_CCI_FW_IMAGE_FACTORY].flags);
|
|
pRevisions[NVSWITCH_CCI_FW_IMAGE_FACTORY].major = response[74];
|
|
pRevisions[NVSWITCH_CCI_FW_IMAGE_FACTORY].minor = response[75];
|
|
pRevisions[NVSWITCH_CCI_FW_IMAGE_FACTORY].build = (response[76] << 4 | response[77]);
|
|
}
|
|
|
|
pRevisions[NVSWITCH_CCI_FW_IMAGE_A].flags = 0;
|
|
if (status & NVBIT(0))
|
|
{
|
|
NVSWITCH_PRINT(device, INFO,
|
|
"%s: Image A is Running\n",
|
|
__FUNCTION__);
|
|
|
|
pRevisions[NVSWITCH_CCI_FW_IMAGE_A].flags =
|
|
FLD_SET_DRF(SWITCH, _CCI_FW_FLAGS, _ACTIVE, _YES,
|
|
pRevisions[NVSWITCH_CCI_FW_IMAGE_A].flags);
|
|
}
|
|
if (status & NVBIT(1))
|
|
{
|
|
NVSWITCH_PRINT(device, INFO,
|
|
"%s: Image A is committed, module boots from Image A\n",
|
|
__FUNCTION__);
|
|
pRevisions[NVSWITCH_CCI_FW_IMAGE_A].flags =
|
|
FLD_SET_DRF(SWITCH, _CCI_FW_FLAGS, _COMMITED, _YES,
|
|
pRevisions[NVSWITCH_CCI_FW_IMAGE_A].flags);
|
|
}
|
|
if (status & NVBIT(2))
|
|
{
|
|
NVSWITCH_PRINT(device, INFO,
|
|
"%s: Image A is erased/empty\n",
|
|
__FUNCTION__);
|
|
pRevisions[NVSWITCH_CCI_FW_IMAGE_A].flags =
|
|
FLD_SET_DRF(SWITCH, _CCI_FW_FLAGS, _EMPTY, _YES,
|
|
pRevisions[NVSWITCH_CCI_FW_IMAGE_A].flags);
|
|
}
|
|
|
|
if (response[1] & NVBIT(0))
|
|
{
|
|
pRevisions[NVSWITCH_CCI_FW_IMAGE_A].flags =
|
|
FLD_SET_DRF(SWITCH, _CCI_FW_FLAGS, _PRESENT, _YES,
|
|
pRevisions[NVSWITCH_CCI_FW_IMAGE_A].flags);
|
|
//
|
|
// For Image A,
|
|
// Byte 2(or 138) contains major revision
|
|
// Byte 3(or 139) contains minor revision
|
|
// Byte 4, 5(or 140, 141) contains build number
|
|
//
|
|
pRevisions[NVSWITCH_CCI_FW_IMAGE_A].major = response[2];
|
|
pRevisions[NVSWITCH_CCI_FW_IMAGE_A].minor = response[3];
|
|
pRevisions[NVSWITCH_CCI_FW_IMAGE_A].build = (response[4] << 4 | response[5]);
|
|
}
|
|
|
|
pRevisions[NVSWITCH_CCI_FW_IMAGE_B].flags = 0;
|
|
if (status & NVBIT(4))
|
|
{
|
|
NVSWITCH_PRINT(device, INFO,
|
|
"%s: Image B is Running\n",
|
|
__FUNCTION__);
|
|
|
|
pRevisions[NVSWITCH_CCI_FW_IMAGE_B].flags =
|
|
FLD_SET_DRF(SWITCH, _CCI_FW_FLAGS, _ACTIVE, _YES,
|
|
pRevisions[NVSWITCH_CCI_FW_IMAGE_B].flags);
|
|
}
|
|
if (status & NVBIT(5))
|
|
{
|
|
NVSWITCH_PRINT(device, INFO,
|
|
"%s: Image B is committed, module boots from Image B\n",
|
|
__FUNCTION__);
|
|
pRevisions[NVSWITCH_CCI_FW_IMAGE_B].flags =
|
|
FLD_SET_DRF(SWITCH, _CCI_FW_FLAGS, _COMMITED, _YES,
|
|
pRevisions[NVSWITCH_CCI_FW_IMAGE_B].flags);
|
|
}
|
|
if (status & NVBIT(6))
|
|
{
|
|
NVSWITCH_PRINT(device, INFO,
|
|
"%s: Image B is erased/empty\n",
|
|
__FUNCTION__);
|
|
pRevisions[NVSWITCH_CCI_FW_IMAGE_B].flags =
|
|
FLD_SET_DRF(SWITCH, _CCI_FW_FLAGS, _EMPTY, _YES,
|
|
pRevisions[NVSWITCH_CCI_FW_IMAGE_B].flags);
|
|
}
|
|
|
|
if (response[1] & NVBIT(1))
|
|
{
|
|
pRevisions[NVSWITCH_CCI_FW_IMAGE_B].flags =
|
|
FLD_SET_DRF(SWITCH, _CCI_FW_FLAGS, _PRESENT, _YES,
|
|
pRevisions[NVSWITCH_CCI_FW_IMAGE_B].flags);
|
|
//
|
|
// For Image B,
|
|
// Byte 38(or 174) contains major revision
|
|
// Byte 39(or 175) contains minor revision
|
|
// Byte 40, 41(or 176, 177) contains build number
|
|
//
|
|
pRevisions[NVSWITCH_CCI_FW_IMAGE_B].major = response[38];
|
|
pRevisions[NVSWITCH_CCI_FW_IMAGE_B].minor = response[39];
|
|
pRevisions[NVSWITCH_CCI_FW_IMAGE_B].build = (response[40] << 8 | response[41]);
|
|
}
|
|
|
|
return NVL_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
* @brief Get xceiver LED state by OSFP xceiver index.
|
|
*
|
|
*/
|
|
NvlStatus
|
|
cciGetXcvrLedState
|
|
(
|
|
nvswitch_device *device,
|
|
NvU32 client,
|
|
NvU32 osfp,
|
|
NvU8 *pLedState
|
|
)
|
|
{
|
|
NvlStatus status;
|
|
|
|
if ((device->pCci == NULL) || (!device->pCci->bInitialized))
|
|
{
|
|
NVSWITCH_PRINT(device, ERROR,
|
|
"%s: CCI not supported\n",
|
|
__FUNCTION__);
|
|
return -NVL_ERR_NOT_SUPPORTED;
|
|
}
|
|
|
|
status = nvswitch_cci_get_xcvr_led_state(device, client, osfp, pLedState);
|
|
if (status != NVL_SUCCESS)
|
|
{
|
|
return status;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
/*
|
|
* @brief Set the next LED state for the given OSFP.
|
|
* The HW will reflect this state on the next iteration of link
|
|
* state update callback.
|
|
*/
|
|
NvlStatus
|
|
cciSetNextXcvrLedState
|
|
(
|
|
nvswitch_device *device,
|
|
NvU32 client,
|
|
NvU32 osfp,
|
|
NvU8 nextLedState
|
|
)
|
|
{
|
|
NvlStatus status;
|
|
NvU32 cagesMask;
|
|
|
|
if ((device->pCci == NULL) || (!device->pCci->bInitialized))
|
|
{
|
|
NVSWITCH_PRINT(device, ERROR,
|
|
"%s: CCI not supported\n",
|
|
__FUNCTION__);
|
|
return -NVL_ERR_NOT_SUPPORTED;
|
|
}
|
|
|
|
status = cciGetXcvrMask(device, &cagesMask, NULL);
|
|
if (status != NVL_SUCCESS)
|
|
{
|
|
return status;
|
|
}
|
|
|
|
if ((cagesMask & NVBIT32(osfp)) == 0)
|
|
{
|
|
NVSWITCH_PRINT(device, ERROR,
|
|
"%s: Module cage not found\n",
|
|
__FUNCTION__);
|
|
return -NVL_BAD_ARGS;
|
|
}
|
|
|
|
device->pCci->xcvrNextLedState[osfp] = nextLedState;
|
|
|
|
return status;
|
|
}
|
|
|
|
/*
|
|
* @brief Set HW xceiver LED state (Locate On/Off) by OSFP xceiver index.
|
|
* If Locate is off then set LED state based on link state.
|
|
*
|
|
*/
|
|
NvlStatus
|
|
cciSetXcvrLedState
|
|
(
|
|
nvswitch_device *device,
|
|
NvU32 client,
|
|
NvU32 osfp,
|
|
NvBool bSetLocate
|
|
)
|
|
{
|
|
NvlStatus status;
|
|
NvU32 cagesMask;
|
|
|
|
if ((device->pCci == NULL) || (!device->pCci->bInitialized))
|
|
{
|
|
NVSWITCH_PRINT(device, ERROR,
|
|
"%s: CCI not supported\n",
|
|
__FUNCTION__);
|
|
return -NVL_ERR_NOT_SUPPORTED;
|
|
}
|
|
|
|
status = cciGetXcvrMask(device, &cagesMask, NULL);
|
|
if (status != NVL_SUCCESS)
|
|
{
|
|
return status;
|
|
}
|
|
|
|
if ((cagesMask & NVBIT32(osfp)) == 0)
|
|
{
|
|
return -NVL_BAD_ARGS;
|
|
}
|
|
|
|
status = nvswitch_cci_set_xcvr_led_state(device, client, osfp, bSetLocate);
|
|
if (status != NVL_SUCCESS)
|
|
{
|
|
return status;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
/*
|
|
* @brief Determine which OSFP transceivers are connected
|
|
*
|
|
*/
|
|
void
|
|
cciDetectXcvrsPresent
|
|
(
|
|
nvswitch_device *device
|
|
)
|
|
{
|
|
NvU32 maskPresent;
|
|
|
|
if ((device->pCci == NULL) || (!device->pCci->bInitialized))
|
|
{
|
|
NVSWITCH_PRINT(device, ERROR,
|
|
"%s: CCI not supported\n",
|
|
__FUNCTION__);
|
|
return;
|
|
}
|
|
|
|
maskPresent = 0;
|
|
|
|
nvswitch_cci_get_xcvrs_present(device, &maskPresent);
|
|
nvswitch_cci_set_xcvr_present(device, maskPresent);
|
|
}
|
|
|
|
/*
|
|
* @brief Get the bitset mask of connected OSFP transceivers
|
|
*
|
|
*/
|
|
NvlStatus
|
|
cciGetXcvrMask
|
|
(
|
|
nvswitch_device *device,
|
|
NvU32 *pMaskAll,
|
|
NvU32 *pMaskPresent
|
|
)
|
|
{
|
|
if ((device->pCci == NULL) || (!device->pCci->bInitialized))
|
|
{
|
|
NVSWITCH_PRINT(device, ERROR,
|
|
"%s: CCI not supported\n",
|
|
__FUNCTION__);
|
|
return -NVL_ERR_NOT_SUPPORTED;
|
|
}
|
|
|
|
return nvswitch_cci_get_xcvr_mask(device, pMaskAll, pMaskPresent);
|
|
}
|
|
|
|
NvlStatus
|
|
cciConfigureNvlinkModeModule
|
|
(
|
|
nvswitch_device *device,
|
|
NvU32 client,
|
|
NvU8 moduleId,
|
|
NvU64 linkMask,
|
|
NvBool freeze_maintenance,
|
|
NvBool restart_training,
|
|
NvBool nvlink_mode
|
|
)
|
|
{
|
|
NvlStatus status;
|
|
NvU8 payload[CMIS_CDB_LPL_MAX_SIZE];
|
|
NvU8 response[CMIS_CDB_LPL_MAX_SIZE];
|
|
NvU32 resLength;
|
|
NvU8 linkId;
|
|
NvU8 laneMask;
|
|
NvU8 laneMaskTemp;
|
|
|
|
laneMask = 0;
|
|
|
|
if ((device->pCci == NULL) || (!device->pCci->bInitialized))
|
|
{
|
|
NVSWITCH_PRINT(device, ERROR,
|
|
"%s: CCI not supported\n",
|
|
__FUNCTION__);
|
|
return -NVL_ERR_NOT_SUPPORTED;
|
|
}
|
|
|
|
FOR_EACH_INDEX_IN_MASK(64, linkId, linkMask)
|
|
{
|
|
status = cciGetLaneMask(device, linkId, &laneMaskTemp);
|
|
if (status != NVL_SUCCESS)
|
|
{
|
|
return status;
|
|
}
|
|
laneMask |= laneMaskTemp;
|
|
}
|
|
FOR_EACH_INDEX_IN_MASK_END;
|
|
|
|
payload[0] = 0;
|
|
payload[1] = (freeze_maintenance << 4) + (restart_training << 1) + nvlink_mode;
|
|
payload[2] = 0;
|
|
|
|
// Tx
|
|
payload[3] = laneMask;
|
|
payload[4] = 0;
|
|
|
|
// Rx
|
|
payload[5] = laneMask;
|
|
|
|
status = cciSendCDBCommandAndGetResponse(device, client, moduleId, NVSWITCH_CCI_CDB_CMD_ID, 6,
|
|
payload, &resLength, response, NV_FALSE);
|
|
if (status != NVL_SUCCESS)
|
|
{
|
|
NVSWITCH_PRINT(device, ERROR, "%s: Failed to configure nvlink mode\n",
|
|
__FUNCTION__);
|
|
return status;
|
|
}
|
|
|
|
return NVL_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
* @brief Configures individual RX and TX osfp lanes to NVLink mode
|
|
*
|
|
* Freeze Maintenance(FM) : When pre-training starts this should be set, and later
|
|
* cleared once link becomes active.
|
|
*
|
|
* Restart Training(RT) : must be set only when linkup flow is reset and pre-training
|
|
* should be performed.
|
|
*
|
|
* Nvlink Mode(NV) : Must be set to 0x1 for NVLink Mode.
|
|
*
|
|
* CDB address 0xCD19. Write sequence -
|
|
* [0, (0,0,0,0,FM,0,0,RT,NV), TX CH Mask 15..8(00h), TX CH Mask 7..0(FFh), RX CH Mask 15..8 (00h), RX CH Mask 7..0(FFh)]
|
|
*
|
|
* (ref cdb_prospector.pdf)
|
|
*/
|
|
NvlStatus
|
|
cciConfigureNvlinkMode
|
|
(
|
|
nvswitch_device *device,
|
|
NvU32 client,
|
|
NvU32 linkId,
|
|
NvBool bTx,
|
|
NvBool freeze_maintenance,
|
|
NvBool restart_training,
|
|
NvBool nvlink_mode
|
|
)
|
|
{
|
|
NvU8 payload[CMIS_CDB_LPL_MAX_SIZE];
|
|
NvU8 response[CMIS_CDB_LPL_MAX_SIZE];
|
|
NvU32 resLength;
|
|
NvlStatus status;
|
|
NvU32 osfp;
|
|
NvU8 laneMask;
|
|
|
|
if ((device->pCci == NULL) || (!device->pCci->bInitialized))
|
|
{
|
|
NVSWITCH_PRINT(device, ERROR,
|
|
"%s: CCI not supported\n",
|
|
__FUNCTION__);
|
|
return -NVL_ERR_NOT_SUPPORTED;
|
|
}
|
|
|
|
if (cciGetModuleId(device, linkId, &osfp) != NVL_SUCCESS)
|
|
{
|
|
NVSWITCH_PRINT(device, ERROR,
|
|
"%s: Failed to get moduleId associated with link %d\n",
|
|
__FUNCTION__, linkId);
|
|
return -NVL_ERR_NOT_SUPPORTED;
|
|
}
|
|
|
|
if (!cciModulePresent(device, osfp))
|
|
{
|
|
return -NVL_NOT_FOUND;
|
|
}
|
|
|
|
if (cciGetLaneMask(device, linkId, &laneMask) != NVL_SUCCESS)
|
|
{
|
|
NVSWITCH_PRINT(device, ERROR,
|
|
"%s: Failed to get osfp lanemask associated with link %d\n",
|
|
__FUNCTION__, linkId);
|
|
return -NVL_ERR_NOT_SUPPORTED;
|
|
}
|
|
|
|
payload[0] = 0;
|
|
payload[1] = (freeze_maintenance<<4)+(restart_training<<1)+nvlink_mode;
|
|
payload[2] = 0;
|
|
payload[3] = bTx ? laneMask : 0;
|
|
payload[4] = 0;
|
|
payload[5] = bTx ? 0 : laneMask;
|
|
|
|
status = cciSendCDBCommandAndGetResponse(device, client, osfp,
|
|
NVSWITCH_CCI_CDB_CMD_ID, 6, payload, &resLength, response, NV_FALSE);
|
|
if (status != NVL_SUCCESS)
|
|
{
|
|
NVSWITCH_PRINT(device, ERROR,
|
|
"%s: Failed to configure nvlink mode\n",
|
|
__FUNCTION__);
|
|
return status;
|
|
}
|
|
|
|
return NVL_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
* @brief Check for optical pre training completion
|
|
*
|
|
* CDB address CD20h - Retrieves individual RX and TX channels training status.
|
|
* Read sequence -
|
|
* [TX CH Mask 15..8(00h), TX CH Mask 7..0(FFh), RX CH Mask 15..8 (00h), RX CH Mask 7..0(FFh)]
|
|
*
|
|
* (ref cdb_prospector.pdf)
|
|
*/
|
|
NvBool
|
|
cciCheckForPreTraining
|
|
(
|
|
nvswitch_device *device,
|
|
NvU32 client,
|
|
NvU32 linkId,
|
|
NvBool bTx
|
|
)
|
|
{
|
|
NvlStatus status;
|
|
NvU8 response[CMIS_CDB_LPL_MAX_SIZE];
|
|
NvU32 resLength;
|
|
NvU32 osfp;
|
|
NvU8 train_mask;
|
|
NvU8 lane_mask;
|
|
|
|
if ((device->pCci == NULL) || (!device->pCci->bInitialized))
|
|
{
|
|
NVSWITCH_PRINT(device, ERROR,
|
|
"%s: CCI not supported\n",
|
|
__FUNCTION__);
|
|
return NV_FALSE;
|
|
}
|
|
|
|
if (cciGetModuleId(device, linkId, &osfp) != NVL_SUCCESS)
|
|
{
|
|
NVSWITCH_PRINT(device, ERROR,
|
|
"%s: Failed to get moduleId associated with link %d\n",
|
|
__FUNCTION__, linkId);
|
|
return NV_FALSE;
|
|
}
|
|
|
|
if (!cciModulePresent(device, osfp))
|
|
{
|
|
return NV_FALSE;
|
|
}
|
|
|
|
if (cciGetLaneMask(device, linkId, &lane_mask) != NVL_SUCCESS)
|
|
{
|
|
NVSWITCH_PRINT(device, ERROR,
|
|
"%s: Failed to get osfp lanemask associated with link %d\n",
|
|
__FUNCTION__, linkId);
|
|
return NV_FALSE;
|
|
}
|
|
|
|
status = cciSendCDBCommandAndGetResponse(device, client, osfp,
|
|
0xcd20, 0, NULL, &resLength, response, NV_FALSE);
|
|
if (status != NVL_SUCCESS)
|
|
{
|
|
NVSWITCH_PRINT(device, ERROR,
|
|
"%s: Failed to get nvlink status\n",
|
|
__FUNCTION__);
|
|
return NV_FALSE;
|
|
}
|
|
|
|
train_mask = bTx ? response[1] : response[3];
|
|
if ((lane_mask & train_mask) == lane_mask)
|
|
{
|
|
NVSWITCH_PRINT(device, INFO,
|
|
"%s: pre-training completed on link %d!\n",
|
|
__FUNCTION__, linkId);
|
|
return NV_TRUE;
|
|
}
|
|
|
|
return NV_FALSE;
|
|
}
|
|
|
|
NvlStatus
|
|
cciApplyControlSetValues
|
|
(
|
|
nvswitch_device *device,
|
|
NvU32 client,
|
|
NvU32 moduleMask
|
|
)
|
|
{
|
|
if ((device->pCci == NULL) || (!device->pCci->bInitialized))
|
|
{
|
|
NVSWITCH_PRINT(device, ERROR,
|
|
"%s: CCI not supported\n",
|
|
__FUNCTION__);
|
|
return -NVL_ERR_NOT_SUPPORTED;
|
|
}
|
|
|
|
return nvswitch_cci_apply_control_set_values(device, client, moduleMask);
|
|
}
|
|
|
|
NvlStatus
|
|
cciGetGradingValues
|
|
(
|
|
nvswitch_device *device,
|
|
NvU32 client,
|
|
NvU32 linkId,
|
|
NvU8 *laneMask,
|
|
NVSWITCH_CCI_GRADING_VALUES *pGrading
|
|
)
|
|
{
|
|
return nvswitch_cci_get_grading_values(device, client, linkId, laneMask, pGrading);
|
|
}
|
|
|
|
/*
|
|
* @brief Gets the mapping between cageIndex and link Ids
|
|
* Returns a bitmask containing all links mapped to the given
|
|
* cage. Also returns a value that encodes other information
|
|
* including the mapping between OSFP link lane and Nvlink link
|
|
*/
|
|
NvlStatus
|
|
cciGetCageMapping
|
|
(
|
|
nvswitch_device *device,
|
|
NvU8 cageIndex,
|
|
NvU64 *pLinkMask,
|
|
NvU64 *pEncodedValue
|
|
)
|
|
{
|
|
NVSWITCH_CCI_MODULE_LINK_LANE_MAP *p_nvswitch_cci_osfp_map;
|
|
NvU64 linkMask;
|
|
NvU64 encodedValue;
|
|
NvU8 *pEncodedByte;
|
|
NvU32 nvswitch_cci_osfp_map_size;
|
|
NvU32 i;
|
|
NvU8 linkId;
|
|
NvU8 osfpLane;
|
|
|
|
if ((device->pCci == NULL) || (!device->pCci->bInitialized))
|
|
{
|
|
NVSWITCH_PRINT(device, ERROR,
|
|
"%s: CCI not initialized\n",
|
|
__FUNCTION__);
|
|
return -NVL_ERR_NOT_SUPPORTED;
|
|
}
|
|
|
|
linkMask = 0;
|
|
encodedValue = 0;
|
|
pEncodedByte = (NvU8 *)&encodedValue;
|
|
p_nvswitch_cci_osfp_map = device->pCci->osfp_map;
|
|
nvswitch_cci_osfp_map_size = device->pCci->osfp_map_size;
|
|
|
|
for (i = 0; i < nvswitch_cci_osfp_map_size; i++)
|
|
{
|
|
if (p_nvswitch_cci_osfp_map[i].moduleId == cageIndex)
|
|
{
|
|
linkId = p_nvswitch_cci_osfp_map[i].linkId;
|
|
NVSWITCH_ASSERT(linkId <= 63);
|
|
|
|
linkMask |= NVBIT64(linkId);
|
|
FOR_EACH_INDEX_IN_MASK(8, osfpLane, p_nvswitch_cci_osfp_map[i].laneMask)
|
|
{
|
|
pEncodedByte[osfpLane] =
|
|
REF_NUM(NVSWITCH_CCI_CMIS_NVLINK_MAPPING_ENCODED_VALUE_LINK_ID, linkId);
|
|
}
|
|
FOR_EACH_INDEX_IN_MASK_END;
|
|
}
|
|
}
|
|
|
|
if (pLinkMask != NULL)
|
|
{
|
|
*pLinkMask = linkMask;
|
|
}
|
|
|
|
if (pEncodedValue != NULL)
|
|
{
|
|
*pEncodedValue = encodedValue;
|
|
}
|
|
|
|
return NVL_SUCCESS;
|
|
}
|
|
|
|
static NvlStatus
|
|
_cciCmisAccessSetup
|
|
(
|
|
nvswitch_device *device,
|
|
NvU8 cageIndex,
|
|
NvU8 bank,
|
|
NvU8 page,
|
|
NvU8 address,
|
|
NvU8 count,
|
|
NvU8 *pSavedBank,
|
|
NvU8 *pSavedPage
|
|
)
|
|
{
|
|
NvlStatus status;
|
|
NvU32 cagesMask;
|
|
|
|
status = cciGetXcvrMask(device, &cagesMask, NULL);
|
|
if (status != NVL_SUCCESS)
|
|
{
|
|
return status;
|
|
}
|
|
|
|
if (!(cagesMask & NVBIT(cageIndex)))
|
|
{
|
|
NVSWITCH_PRINT(device, ERROR,
|
|
"%s: Provided cage index does not exist.\n",
|
|
__FUNCTION__);
|
|
return -NVL_BAD_ARGS;
|
|
}
|
|
|
|
if (count == 0 ||
|
|
count > NVSWITCH_CCI_CMIS_MEMORY_ACCESS_BUF_SIZE)
|
|
{
|
|
NVSWITCH_PRINT(device, ERROR,
|
|
"%s: Invalid byte count.\n",
|
|
__FUNCTION__);
|
|
return -NVL_BAD_ARGS;
|
|
}
|
|
|
|
if (device->pCci->isFlatMemory[cageIndex])
|
|
{
|
|
if (page != 0 || bank !=0)
|
|
{
|
|
return -NVL_ERR_NOT_SUPPORTED;
|
|
}
|
|
else
|
|
{
|
|
return NVL_SUCCESS;
|
|
}
|
|
}
|
|
|
|
if (address >= 0x80)
|
|
{
|
|
// Save previous bank and page
|
|
status = cciGetBankAndPage(device, NVSWITCH_I2C_ACQUIRER_CCI_UX,
|
|
cageIndex, pSavedBank, pSavedPage);
|
|
if (status != NVL_SUCCESS)
|
|
{
|
|
NVSWITCH_PRINT(device, ERROR,
|
|
"%s: Failed to save bank and page.\n",
|
|
__FUNCTION__);
|
|
return status;
|
|
}
|
|
|
|
// Don't change bank and page unnecessarily
|
|
if (*pSavedBank != bank || *pSavedPage != page)
|
|
{
|
|
status = cciSetBankAndPage(device, NVSWITCH_I2C_ACQUIRER_CCI_UX,
|
|
cageIndex, bank, page);
|
|
if (status != NVL_SUCCESS)
|
|
{
|
|
NVSWITCH_PRINT(device, ERROR,
|
|
"%s: Failed to set bank and page.\n",
|
|
__FUNCTION__);
|
|
return status;
|
|
}
|
|
}
|
|
}
|
|
|
|
return NVL_SUCCESS;
|
|
}
|
|
|
|
static NvlStatus
|
|
_cciCmisAccessRestore
|
|
(
|
|
nvswitch_device *device,
|
|
NvU8 cageIndex,
|
|
NvU8 bank,
|
|
NvU8 page,
|
|
NvU8 address,
|
|
NvU8 savedBank,
|
|
NvU8 savedPage
|
|
)
|
|
{
|
|
NvlStatus status;
|
|
|
|
if (device->pCci->isFlatMemory[cageIndex])
|
|
{
|
|
if (page != 0 || bank !=0)
|
|
{
|
|
return -NVL_ERR_NOT_SUPPORTED;
|
|
}
|
|
else
|
|
{
|
|
return NVL_SUCCESS;
|
|
}
|
|
}
|
|
|
|
// Restore previous bank and page
|
|
if (address >= 0x80)
|
|
{
|
|
if (savedBank != bank || savedPage != page)
|
|
{
|
|
status = cciSetBankAndPage(device, NVSWITCH_I2C_ACQUIRER_CCI_UX,
|
|
cageIndex, savedBank, savedPage);
|
|
if (status != NVL_SUCCESS)
|
|
{
|
|
NVSWITCH_PRINT(device, ERROR,
|
|
"%s: Failed to restore bank and page.\n",
|
|
__FUNCTION__);
|
|
return status;
|
|
}
|
|
}
|
|
}
|
|
|
|
return NVL_SUCCESS;
|
|
}
|
|
|
|
static NvBool
|
|
_cciCmisAccessAllowed
|
|
(
|
|
nvswitch_device *device,
|
|
NvU8 cageIndex
|
|
)
|
|
{
|
|
PCCI pCci = device->pCci;
|
|
NvU32 pid;
|
|
NvlStatus status;
|
|
|
|
status = nvswitch_os_get_pid(&pid);
|
|
if (status != NVL_SUCCESS)
|
|
{
|
|
NVSWITCH_PRINT(device, WARN,
|
|
"%s: CCI CMIS lock not supported\n",
|
|
__FUNCTION__);
|
|
return NV_TRUE;
|
|
}
|
|
|
|
// Reject clients with different PIDs that attempt access
|
|
if (pCci->cmisAccessLock[cageIndex].bLocked &&
|
|
(pid != pCci->cmisAccessLock[cageIndex].pid))
|
|
{
|
|
return NV_FALSE;
|
|
}
|
|
|
|
return NV_TRUE;
|
|
}
|
|
|
|
/*!
|
|
* @brief Try to obtain lock for CMIS accesses
|
|
*/
|
|
NvBool
|
|
cciCmisAccessTryLock
|
|
(
|
|
nvswitch_device *device,
|
|
NvU8 cageIndex
|
|
)
|
|
{
|
|
PCCI pCci = device->pCci;
|
|
NvU64 timestampCurr;
|
|
NvU64 timestampSaved;
|
|
NvU32 pid;
|
|
NvlStatus status;
|
|
|
|
status = nvswitch_os_get_pid(&pid);
|
|
if (status != NVL_SUCCESS)
|
|
{
|
|
NVSWITCH_PRINT(device, WARN,
|
|
"%s: CCI CMIS lock not supported\n",
|
|
__FUNCTION__);
|
|
return NV_TRUE;
|
|
}
|
|
|
|
timestampCurr = nvswitch_os_get_platform_time();
|
|
timestampSaved = pCci->cmisAccessLock[cageIndex].timestamp;
|
|
|
|
// Release lock if modules accesses have been idle
|
|
if ((timestampCurr - timestampSaved) >= NVSWITCH_CCI_CMIS_LOCK_TIMEOUT)
|
|
{
|
|
cciCmisAccessReleaseLock(device, cageIndex);
|
|
}
|
|
|
|
if (!_cciCmisAccessAllowed(device, cageIndex))
|
|
{
|
|
return NV_FALSE;
|
|
}
|
|
|
|
// Lock CMIS access to module. External clients may attempt to lock multiple times
|
|
pCci->cmisAccessLock[cageIndex].bLocked = NV_TRUE;
|
|
pCci->cmisAccessLock[cageIndex].timestamp = timestampCurr;
|
|
pCci->cmisAccessLock[cageIndex].pid = pid;
|
|
|
|
return NV_TRUE;
|
|
}
|
|
|
|
/*!
|
|
* @brief Release lock for CMIS accesses
|
|
*/
|
|
void
|
|
cciCmisAccessReleaseLock
|
|
(
|
|
nvswitch_device *device,
|
|
NvU8 cageIndex
|
|
)
|
|
{
|
|
PCCI pCci = device->pCci;
|
|
NvU32 pid;
|
|
NvlStatus status;
|
|
|
|
status = nvswitch_os_get_pid(&pid);
|
|
if (status != NVL_SUCCESS)
|
|
{
|
|
NVSWITCH_PRINT(device, WARN,
|
|
"%s: CCI CMIS lock not supported\n",
|
|
__FUNCTION__);
|
|
return;
|
|
}
|
|
|
|
pCci->cmisAccessLock[cageIndex].bLocked = NV_FALSE;
|
|
}
|
|
|
|
/*!
|
|
* @brief Read from specified module cage.
|
|
* Sets the bank and page if necessary.
|
|
*/
|
|
NvlStatus
|
|
cciCmisRead
|
|
(
|
|
nvswitch_device *device,
|
|
NvU8 cageIndex,
|
|
NvU8 bank,
|
|
NvU8 page,
|
|
NvU8 address,
|
|
NvU8 count,
|
|
NvU8 *pData
|
|
)
|
|
{
|
|
NvU8 savedBank;
|
|
NvU8 savedPage;
|
|
NvlStatus status;
|
|
|
|
if ((device->pCci == NULL) || (!device->pCci->bInitialized))
|
|
{
|
|
NVSWITCH_PRINT(device, ERROR,
|
|
"%s: CCI not supported\n",
|
|
__FUNCTION__);
|
|
return -NVL_ERR_NOT_SUPPORTED;
|
|
}
|
|
|
|
// Prevent internal driver CMIS reads from occuring while access is locked
|
|
if (!_cciCmisAccessAllowed(device, cageIndex))
|
|
{
|
|
return -NVL_ERR_STATE_IN_USE;
|
|
}
|
|
|
|
// Perform checks and save bank/page if needed
|
|
status = _cciCmisAccessSetup(device, cageIndex, bank, page,
|
|
address, count, &savedBank, &savedPage);
|
|
if (status != NVL_SUCCESS)
|
|
{
|
|
return status;
|
|
}
|
|
|
|
status = cciRead(device, NVSWITCH_I2C_ACQUIRER_CCI_UX,
|
|
cageIndex, address, count, pData);
|
|
if (status != NVL_SUCCESS)
|
|
{
|
|
NVSWITCH_PRINT(device, ERROR,
|
|
"%s: Failed to read from module cage %d\n",
|
|
__FUNCTION__, cageIndex);
|
|
return status;
|
|
}
|
|
|
|
status = _cciCmisAccessRestore(device, cageIndex, bank, page,
|
|
address, savedBank, savedPage);
|
|
if (status != NVL_SUCCESS)
|
|
{
|
|
return status;
|
|
}
|
|
|
|
return NVL_SUCCESS;
|
|
}
|
|
|
|
/*!
|
|
* @brief Write to specified module cage.
|
|
* Sets the bank and page if necessary.
|
|
*/
|
|
NvlStatus
|
|
cciCmisWrite
|
|
(
|
|
nvswitch_device *device,
|
|
NvU8 cageIndex,
|
|
NvU8 bank,
|
|
NvU8 page,
|
|
NvU8 address,
|
|
NvU8 count,
|
|
NvU8 *pData
|
|
)
|
|
{
|
|
NvU8 savedBank;
|
|
NvU8 savedPage;
|
|
NvlStatus status;
|
|
|
|
if ((device->pCci == NULL) || (!device->pCci->bInitialized))
|
|
{
|
|
NVSWITCH_PRINT(device, ERROR,
|
|
"%s: CCI not supported\n",
|
|
__FUNCTION__);
|
|
return -NVL_ERR_NOT_SUPPORTED;
|
|
}
|
|
|
|
// Prevent internal driver CMIS reads from occuring while access is locked
|
|
if (!_cciCmisAccessAllowed(device, cageIndex))
|
|
{
|
|
return -NVL_ERR_STATE_IN_USE;
|
|
}
|
|
|
|
// Perform checks and save bank/page if needed
|
|
status = _cciCmisAccessSetup(device, cageIndex, bank, page,
|
|
address, count, &savedBank, &savedPage);
|
|
if (status != NVL_SUCCESS)
|
|
{
|
|
return status;
|
|
}
|
|
|
|
status = cciWrite(device, NVSWITCH_I2C_ACQUIRER_CCI_UX,
|
|
cageIndex, address, count, pData);
|
|
if (status != NVL_SUCCESS)
|
|
{
|
|
NVSWITCH_PRINT(device, ERROR,
|
|
"%s: Failed to write to module cage %d\n",
|
|
__FUNCTION__, cageIndex);
|
|
return status;
|
|
}
|
|
|
|
status = _cciCmisAccessRestore(device, cageIndex, bank, page,
|
|
address, savedBank, savedPage);
|
|
if (status != NVL_SUCCESS)
|
|
{
|
|
return status;
|
|
}
|
|
|
|
return NVL_SUCCESS;
|
|
}
|
|
|
|
NvlStatus
|
|
cciCmisCageBezelMarking
|
|
(
|
|
nvswitch_device *device,
|
|
NvU8 cageIndex,
|
|
char *pBezelMarking
|
|
)
|
|
{
|
|
return nvswitch_cci_cmis_cage_bezel_marking(device, cageIndex, pBezelMarking);
|
|
}
|
|
|
|
// Helper functions for CCI subcomponents
|
|
NvlStatus
|
|
cciGetModuleId
|
|
(
|
|
nvswitch_device *device,
|
|
NvU32 linkId,
|
|
NvU32 *osfp
|
|
)
|
|
{
|
|
return _nvswitch_cci_get_module_id(device, linkId, osfp);
|
|
}
|
|
|
|
NvBool
|
|
cciModulePresent
|
|
(
|
|
nvswitch_device *device,
|
|
NvU32 osfp
|
|
)
|
|
{
|
|
return _nvswitch_cci_module_present(device, osfp);
|
|
}
|
|
|
|
void
|
|
cciGetModulePresenceChange
|
|
(
|
|
nvswitch_device *device,
|
|
NvU32 *pModuleMask
|
|
)
|
|
{
|
|
nvswitch_cci_get_xcvrs_present_change(device, pModuleMask);
|
|
}
|
|
|
|
NvlStatus
|
|
cciResetModule
|
|
(
|
|
nvswitch_device *device,
|
|
NvU32 moduleId
|
|
)
|
|
{
|
|
NvlStatus retval;
|
|
NvU8 regByte;
|
|
|
|
// Assert OSFP reset
|
|
regByte = NVBIT32(moduleId);
|
|
retval = nvswitch_cci_ports_cpld_write(device, CPLD_MACHXO3_PORTS_RESET_REG1, regByte);
|
|
if (retval != NVL_SUCCESS)
|
|
{
|
|
return retval;
|
|
}
|
|
|
|
regByte = (NVBIT32(moduleId) >> 8);
|
|
retval = nvswitch_cci_ports_cpld_write(device, CPLD_MACHXO3_PORTS_RESET_REG2, regByte);
|
|
if (retval != NVL_SUCCESS)
|
|
{
|
|
return retval;
|
|
}
|
|
|
|
nvswitch_os_sleep(1);
|
|
|
|
// De-assert OSFP reset
|
|
retval = nvswitch_cci_ports_cpld_write(device, CPLD_MACHXO3_PORTS_RESET_REG1, 0);
|
|
if (retval != NVL_SUCCESS)
|
|
{
|
|
return retval;
|
|
}
|
|
|
|
retval = nvswitch_cci_ports_cpld_write(device, CPLD_MACHXO3_PORTS_RESET_REG2, 0);
|
|
if (retval != NVL_SUCCESS)
|
|
{
|
|
return retval;
|
|
}
|
|
|
|
// Assert lpmode
|
|
retval = cciSetLPMode(device, moduleId, NV_TRUE);
|
|
if (retval != NVL_SUCCESS)
|
|
{
|
|
return retval;
|
|
}
|
|
|
|
return NVL_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
* @brief Get FW info
|
|
* Module FW info is obtained from CDB command 0x100.
|
|
* (ref CMIS rev4.0, Table 9-16 CDB Command 0100h: Get firmware Info)
|
|
*
|
|
* @pre pInfo points to a buffer of size CMIS_CDB_LPL_MAX_SIZE bytes
|
|
*/
|
|
NvlStatus
|
|
cciGetXcvrFWInfo
|
|
(
|
|
nvswitch_device *device,
|
|
NvU32 moduleId,
|
|
NvU8 *pInfo
|
|
)
|
|
{
|
|
NvlStatus retVal;
|
|
|
|
if ((device->pCci == NULL) || (!device->pCci->bInitialized))
|
|
{
|
|
NVSWITCH_PRINT(device, ERROR,
|
|
"%s: CCI not supported\n",
|
|
__FUNCTION__);
|
|
return -NVL_ERR_NOT_SUPPORTED;
|
|
}
|
|
|
|
if (pInfo == NULL)
|
|
{
|
|
return -NVL_BAD_ARGS;
|
|
}
|
|
|
|
if (!cciModulePresent(device, moduleId))
|
|
{
|
|
NVSWITCH_PRINT(device, ERROR,
|
|
"%s: Module %d is missing\n",
|
|
__FUNCTION__, moduleId);
|
|
return -NVL_NOT_FOUND;
|
|
}
|
|
|
|
retVal = cciSendCDBCommandAndGetResponse(device, 0, moduleId,
|
|
0x100, 0, NULL, NULL, pInfo, NV_FALSE);
|
|
if (retVal != NVL_SUCCESS)
|
|
{
|
|
NVSWITCH_PRINT(device, ERROR,
|
|
"%s: Failed to get FW revisions\n",
|
|
__FUNCTION__);
|
|
return -NVL_ERR_GENERIC;
|
|
}
|
|
|
|
return NVL_SUCCESS;
|
|
}
|
|
|
|
NvlStatus
|
|
cciSetLPMode
|
|
(
|
|
nvswitch_device *device,
|
|
NvU8 moduleId,
|
|
NvBool bAssert
|
|
)
|
|
{
|
|
NvlStatus retval;
|
|
NvU8 valReg1;
|
|
NvU8 valReg2;
|
|
|
|
retval = nvswitch_cci_ports_cpld_read(device, CPLD_MACHXO3_PORTS_LPMODE_REG1, &valReg1);
|
|
if (retval != NVL_SUCCESS)
|
|
{
|
|
return retval;
|
|
}
|
|
|
|
retval = nvswitch_cci_ports_cpld_read(device, CPLD_MACHXO3_PORTS_LPMODE_REG2, &valReg2);
|
|
if (retval != NVL_SUCCESS)
|
|
{
|
|
return retval;
|
|
}
|
|
|
|
if (bAssert)
|
|
{
|
|
valReg1 = valReg1 | NVBIT32(moduleId);
|
|
valReg2 = valReg2 | (NVBIT32(moduleId) >> 8);
|
|
}
|
|
else
|
|
{
|
|
valReg1 = valReg1 & ~NVBIT32(moduleId);
|
|
valReg2 = valReg2 & ~(NVBIT32(moduleId) >> 8);
|
|
}
|
|
|
|
retval = nvswitch_cci_ports_cpld_write(device, CPLD_MACHXO3_PORTS_LPMODE_REG1, valReg1);
|
|
if (retval != NVL_SUCCESS)
|
|
{
|
|
return retval;
|
|
}
|
|
|
|
retval = nvswitch_cci_ports_cpld_write(device, CPLD_MACHXO3_PORTS_LPMODE_REG2, valReg2);
|
|
if (retval != NVL_SUCCESS)
|
|
{
|
|
return retval;
|
|
}
|
|
|
|
return NVL_SUCCESS;
|
|
}
|
|
|
|
NvBool
|
|
cciCheckLPMode
|
|
(
|
|
nvswitch_device *device,
|
|
NvU8 moduleId
|
|
)
|
|
{
|
|
NvlStatus retval;
|
|
NvU8 valReg1;
|
|
NvU8 valReg2;
|
|
|
|
retval = nvswitch_cci_ports_cpld_read(device, CPLD_MACHXO3_PORTS_LPMODE_REG1, &valReg1);
|
|
if (retval != NVL_SUCCESS)
|
|
{
|
|
return NV_FALSE;
|
|
}
|
|
|
|
retval = nvswitch_cci_ports_cpld_read(device, CPLD_MACHXO3_PORTS_LPMODE_REG2, &valReg2);
|
|
if (retval != NVL_SUCCESS)
|
|
{
|
|
return NV_FALSE;
|
|
}
|
|
|
|
if ((valReg1 & NVBIT32(moduleId)) ||
|
|
(valReg2 & (NVBIT32(moduleId) >> 8)))
|
|
{
|
|
return NV_TRUE;
|
|
}
|
|
|
|
return NV_FALSE;
|
|
}
|
|
|
|
void
|
|
cciPingModules
|
|
(
|
|
nvswitch_device *device,
|
|
NvU32 *pMaskPresent
|
|
)
|
|
{
|
|
NVSWITCH_CTRL_I2C_INDEXED_PARAMS i2c_params = { 0 };
|
|
NvU32 idx_i2cdevice;
|
|
PCCI pCci;
|
|
NvU32 presentMask;
|
|
NvU32 client;
|
|
NvlStatus retval;
|
|
|
|
pCci = device->pCci;
|
|
presentMask = 0;
|
|
client = NVSWITCH_I2C_ACQUIRER_CCI_INITIALIZE;
|
|
|
|
for (idx_i2cdevice = 0; idx_i2cdevice < pCci->osfp_num; idx_i2cdevice++)
|
|
{
|
|
retval = nvswitch_cci_setup_module_path(device, client, idx_i2cdevice);
|
|
if (retval != NVL_SUCCESS)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
i2c_params.port = pCci->osfp_i2c_info[idx_i2cdevice].i2cPortLogical;
|
|
i2c_params.address = (NvU16) pCci->osfp_i2c_info[idx_i2cdevice].i2cAddress;
|
|
i2c_params.bIsRead = NV_FALSE;
|
|
i2c_params.flags =
|
|
DRF_DEF(SWITCH_CTRL, _I2C_FLAGS, _START, _SEND) |
|
|
DRF_DEF(SWITCH_CTRL, _I2C_FLAGS, _RESTART, _NONE) |
|
|
DRF_DEF(SWITCH_CTRL, _I2C_FLAGS, _STOP, _SEND) |
|
|
DRF_DEF(SWITCH_CTRL, _I2C_FLAGS, _ADDRESS_MODE, _7BIT) |
|
|
DRF_DEF(SWITCH_CTRL, _I2C_FLAGS, _INDEX_LENGTH, _ZERO) |
|
|
DRF_DEF(SWITCH_CTRL, _I2C_FLAGS, _FLAVOR, _HW) |
|
|
DRF_DEF(SWITCH_CTRL, _I2C_FLAGS, _SPEED_MODE, _400KHZ) |
|
|
DRF_DEF(SWITCH_CTRL, _I2C_FLAGS, _BLOCK_PROTOCOL, _DISABLED) |
|
|
DRF_DEF(SWITCH_CTRL, _I2C_FLAGS, _TRANSACTION_MODE, _PING);
|
|
i2c_params.messageLength = 0;
|
|
i2c_params.acquirer = client;
|
|
|
|
if (nvswitch_ctrl_i2c_indexed(device, &i2c_params) == NVL_SUCCESS)
|
|
{
|
|
// Only print if newly present OSFP
|
|
if (!cciModulePresent(device, idx_i2cdevice))
|
|
{
|
|
NVSWITCH_PRINT(device, INFO,
|
|
"%s: Identified osfp = %d, port = %d, addr = 0x%x\n",
|
|
__FUNCTION__, idx_i2cdevice, i2c_params.port, i2c_params.address);
|
|
}
|
|
|
|
presentMask |= NVBIT(idx_i2cdevice);
|
|
}
|
|
}
|
|
|
|
if (pMaskPresent != NULL)
|
|
{
|
|
*pMaskPresent = presentMask;
|
|
}
|
|
}
|
|
|
|
void
|
|
cciGetAllLinks
|
|
(
|
|
nvswitch_device *device,
|
|
NvU64 *pLinkMaskAll
|
|
)
|
|
{
|
|
PCCI pCci = device->pCci;
|
|
NvU64 linkMaskAll;
|
|
NvU32 i;
|
|
|
|
linkMaskAll = 0;
|
|
for (i = 0; i < pCci->osfp_map_size; i++)
|
|
{
|
|
linkMaskAll |= NVBIT64(pCci->osfp_map[i].linkId);
|
|
}
|
|
|
|
if (pLinkMaskAll != NULL)
|
|
{
|
|
*pLinkMaskAll = linkMaskAll;
|
|
}
|
|
}
|
|
|
|
NvlStatus
|
|
cciGetModuleMask
|
|
(
|
|
nvswitch_device *device,
|
|
NvU64 linkMask,
|
|
NvU32 *pModuleMask
|
|
)
|
|
{
|
|
NvU32 cciModuleMask;
|
|
NvU32 moduleId;
|
|
NvU8 linkId;
|
|
NvlStatus retval;
|
|
|
|
cciModuleMask = 0;
|
|
|
|
FOR_EACH_INDEX_IN_MASK(64, linkId, linkMask)
|
|
{
|
|
retval = cciGetModuleId(device, linkId, &moduleId);
|
|
if (retval != NVL_SUCCESS)
|
|
{
|
|
NVSWITCH_PRINT(device, ERROR,
|
|
"%s: Failed to get moduleId associated with link %d\n",
|
|
__FUNCTION__, linkId);
|
|
return retval;
|
|
}
|
|
|
|
cciModuleMask |= NVBIT32(moduleId);
|
|
}
|
|
FOR_EACH_INDEX_IN_MASK_END;
|
|
|
|
*pModuleMask = cciModuleMask;
|
|
|
|
return NVL_SUCCESS;
|
|
}
|
|
|
|
NvBool
|
|
cciCheckXcvrForLinkTraffic
|
|
(
|
|
nvswitch_device *device,
|
|
NvU32 osfp,
|
|
NvU64 linkMask
|
|
)
|
|
{
|
|
NVSWITCH_GET_THROUGHPUT_COUNTERS_PARAMS *pCounterParams = NULL;
|
|
NvU64 *pCounterValues;
|
|
NvU64 tpCounterPreviousSum;
|
|
NvU64 tpCounterCurrentSum;
|
|
NvBool bTraffic = NV_FALSE;
|
|
NvU8 linkNum;
|
|
|
|
pCounterParams = nvswitch_os_malloc(sizeof(*pCounterParams));
|
|
if (pCounterParams == NULL)
|
|
goto out;
|
|
|
|
pCounterParams->counterMask = NVSWITCH_THROUGHPUT_COUNTERS_TYPE_DATA_TX |
|
|
NVSWITCH_THROUGHPUT_COUNTERS_TYPE_DATA_RX;
|
|
pCounterParams->linkMask = linkMask;
|
|
if (nvswitch_ctrl_get_throughput_counters(device,
|
|
pCounterParams) != NVL_SUCCESS)
|
|
{
|
|
goto out;
|
|
}
|
|
|
|
// Sum TX/RX traffic for each link
|
|
FOR_EACH_INDEX_IN_MASK(64, linkNum, linkMask)
|
|
{
|
|
pCounterValues = pCounterParams->counters[linkNum].values;
|
|
|
|
tpCounterPreviousSum = device->pCci->tpCounterPreviousSum[linkNum];
|
|
|
|
// Sum taken to save space as it is unlikely to overflow before system is reset
|
|
tpCounterCurrentSum = pCounterValues[NVSWITCH_THROUGHPUT_COUNTERS_TYPE_DATA_TX] +
|
|
pCounterValues[NVSWITCH_THROUGHPUT_COUNTERS_TYPE_DATA_RX];
|
|
|
|
device->pCci->tpCounterPreviousSum[linkNum] = tpCounterCurrentSum;
|
|
|
|
// Skip traffic check in first call on system start up
|
|
if (device->pCci->callbackCounter == 0)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (tpCounterCurrentSum > tpCounterPreviousSum)
|
|
{
|
|
bTraffic = NV_TRUE;
|
|
}
|
|
}
|
|
FOR_EACH_INDEX_IN_MASK_END;
|
|
|
|
out:
|
|
nvswitch_os_free(pCounterParams);
|
|
return bTraffic;
|
|
}
|
|
|
|
NvlStatus
|
|
cciGetLaneMask
|
|
(
|
|
nvswitch_device *device,
|
|
NvU32 linkId,
|
|
NvU8 *laneMask
|
|
)
|
|
{
|
|
NVSWITCH_CCI_MODULE_LINK_LANE_MAP * module_map = device->pCci->osfp_map;
|
|
NvU32 module_map_size = device->pCci->osfp_map_size;
|
|
NvU32 i;
|
|
|
|
for (i = 0; i < module_map_size; i++)
|
|
{
|
|
if (module_map[i].linkId == linkId)
|
|
{
|
|
*laneMask = module_map[i].laneMask;
|
|
|
|
return NVL_SUCCESS;
|
|
}
|
|
}
|
|
|
|
return -NVL_NOT_FOUND;
|
|
}
|
|
|
|
void
|
|
cciSetModulePower
|
|
(
|
|
nvswitch_device *device,
|
|
NvU32 moduleId,
|
|
NvBool bPowerOn
|
|
)
|
|
{
|
|
NvlStatus status;
|
|
NvU8 regVal;
|
|
|
|
if (moduleId < 8)
|
|
{
|
|
status = nvswitch_cci_ports_cpld_read(device,
|
|
CPLD_MACHXO3_LD_SW_EN_REG1,
|
|
®Val);
|
|
if (status == NVL_SUCCESS)
|
|
{
|
|
regVal = bPowerOn ? regVal | NVBIT(moduleId) :
|
|
regVal & ~NVBIT(moduleId);
|
|
nvswitch_cci_ports_cpld_write(device,
|
|
CPLD_MACHXO3_LD_SW_EN_REG1,
|
|
regVal);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
status = nvswitch_cci_ports_cpld_read(device,
|
|
CPLD_MACHXO3_LD_SW_EN_REG2,
|
|
®Val);
|
|
if (status == NVL_SUCCESS)
|
|
{
|
|
regVal = bPowerOn ? regVal | NVBIT(moduleId - 8) :
|
|
regVal & ~NVBIT(moduleId - 8);
|
|
nvswitch_cci_ports_cpld_write(device,
|
|
CPLD_MACHXO3_LD_SW_EN_REG2,
|
|
regVal);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
_cciClearModuleFault
|
|
(
|
|
nvswitch_device *device,
|
|
NvU32 moduleId
|
|
)
|
|
{
|
|
if (moduleId < 8)
|
|
{
|
|
nvswitch_cci_ports_cpld_write(device,
|
|
CPLD_MACHXO3_LD_SW_FAULT_REG1,
|
|
~NVBIT(moduleId));
|
|
}
|
|
else
|
|
{
|
|
nvswitch_cci_ports_cpld_write(device,
|
|
CPLD_MACHXO3_LD_SW_FAULT_REG2,
|
|
~NVBIT(moduleId - 8));
|
|
}
|
|
}
|
|
|
|
static NvBool
|
|
_cciCheckModuleFault
|
|
(
|
|
nvswitch_device *device,
|
|
NvU32 moduleId
|
|
)
|
|
{
|
|
NvlStatus status;
|
|
NvU8 regVal;
|
|
|
|
if (moduleId < 8)
|
|
{
|
|
status = nvswitch_cci_ports_cpld_read(device, CPLD_MACHXO3_LD_SW_FAULT_REG1, ®Val);
|
|
if (status != NVL_SUCCESS)
|
|
{
|
|
return NV_TRUE;
|
|
}
|
|
|
|
if (regVal & NVBIT(moduleId))
|
|
{
|
|
return NV_TRUE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
status = nvswitch_cci_ports_cpld_read(device, CPLD_MACHXO3_LD_SW_FAULT_REG2, ®Val);
|
|
if (status != NVL_SUCCESS)
|
|
{
|
|
return NV_TRUE;
|
|
}
|
|
|
|
if (regVal & NVBIT(moduleId - 8))
|
|
{
|
|
return NV_TRUE;
|
|
}
|
|
}
|
|
|
|
return NV_FALSE;
|
|
}
|
|
|
|
NvBool
|
|
cciModuleHWGood
|
|
(
|
|
nvswitch_device *device,
|
|
NvU32 moduleId
|
|
)
|
|
{
|
|
if (_cciCheckModuleFault(device, moduleId))
|
|
{
|
|
// Attempt to recover module by power cycling
|
|
cciSetModulePower(device, moduleId, NV_FALSE);
|
|
nvswitch_os_sleep(1);
|
|
cciSetModulePower(device, moduleId, NV_TRUE);
|
|
|
|
// Clear fault flag and check again
|
|
_cciClearModuleFault(device, moduleId);
|
|
nvswitch_os_sleep(1);
|
|
|
|
if (_cciCheckModuleFault(device, moduleId))
|
|
{
|
|
return NV_FALSE;
|
|
}
|
|
}
|
|
|
|
return NV_TRUE;
|
|
}
|
|
|