mirror of
https://github.com/NVIDIA/open-gpu-kernel-modules.git
synced 2026-01-31 13:39:47 +00:00
1086 lines
34 KiB
C
1086 lines
34 KiB
C
/*
|
|
* SPDX-FileCopyrightText: Copyright (c) 2018-2019 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 "export_nvswitch.h"
|
|
#include "ctrl_dev_nvswitch.h"
|
|
#include "rom_nvswitch.h"
|
|
#include "common_nvswitch.h"
|
|
#include "haldef_nvswitch.h"
|
|
|
|
static NvU8
|
|
_nvswitch_calculate_checksum
|
|
(
|
|
NvU8 *data,
|
|
NvU32 size
|
|
)
|
|
{
|
|
NvU32 i;
|
|
NvU8 checksum = 0;
|
|
|
|
for (i = 0; i < size; i++)
|
|
{
|
|
checksum += data[i];
|
|
}
|
|
return -checksum;
|
|
}
|
|
|
|
static NvlStatus
|
|
_nvswitch_read_rom_bytes
|
|
(
|
|
nvswitch_device *device,
|
|
NVSWITCH_EEPROM_TYPE *eeprom,
|
|
NvU32 offset,
|
|
NvU8 *buffer,
|
|
NvU32 buffer_size
|
|
)
|
|
{
|
|
NVSWITCH_CTRL_I2C_INDEXED_PARAMS i2cIndexed = {0};
|
|
NvU32 i;
|
|
NvlStatus retval;
|
|
|
|
if (offset + buffer_size > (NvU32)(1 << eeprom->index_size))
|
|
{
|
|
NVSWITCH_PRINT(device, SETUP,
|
|
"EEPROM offset 0x%x..0x%x out of range\n",
|
|
offset, offset + buffer_size - 1);
|
|
return -NVL_BAD_ARGS;
|
|
}
|
|
|
|
if (buffer_size > NVSWITCH_CTRL_I2C_MESSAGE_LENGTH_MAX)
|
|
{
|
|
NVSWITCH_PRINT(device, SETUP,
|
|
"EEPROM read buffer (0x%x bytes) larger than max (0x%x bytes)\n",
|
|
buffer_size, NVSWITCH_CTRL_I2C_MESSAGE_LENGTH_MAX);
|
|
return -NVL_BAD_ARGS;
|
|
}
|
|
|
|
i2cIndexed.port = (NvU8)eeprom->i2c_port;
|
|
i2cIndexed.bIsRead = NV_TRUE;
|
|
i2cIndexed.address = (NvU16)eeprom->i2c_address;
|
|
i2cIndexed.flags =
|
|
DRF_DEF(SWITCH_CTRL, _I2C_FLAGS, _START, _SEND) |
|
|
DRF_DEF(SWITCH_CTRL, _I2C_FLAGS, _RESTART, _SEND) |
|
|
DRF_DEF(SWITCH_CTRL, _I2C_FLAGS, _STOP, _SEND) |
|
|
DRF_DEF(SWITCH_CTRL, _I2C_FLAGS, _ADDRESS_MODE, _7BIT) |
|
|
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, _NORMAL) |
|
|
0;
|
|
|
|
if (eeprom->index_size <= 8)
|
|
{
|
|
i2cIndexed.flags |=
|
|
DRF_DEF(SWITCH_CTRL, _I2C_FLAGS, _INDEX_LENGTH, _ONE);
|
|
i2cIndexed.index[0] = offset & 0x000FF; // Read [eeprom_offset]
|
|
}
|
|
else
|
|
{
|
|
i2cIndexed.address |= ((offset & 0x30000) >> 15);
|
|
i2cIndexed.flags |=
|
|
DRF_DEF(SWITCH_CTRL, _I2C_FLAGS, _INDEX_LENGTH, _TWO);
|
|
i2cIndexed.index[0] = (offset & 0x0FF00) >> 8; // Read [eeprom_offset]
|
|
i2cIndexed.index[1] = (offset & 0x000FF);
|
|
}
|
|
|
|
i2cIndexed.messageLength = NV_MIN(buffer_size, NVSWITCH_CTRL_I2C_MESSAGE_LENGTH_MAX);
|
|
|
|
retval = nvswitch_ctrl_i2c_indexed(device, &i2cIndexed);
|
|
if (retval != NVL_SUCCESS)
|
|
{
|
|
return retval;
|
|
}
|
|
|
|
for (i = 0; i < i2cIndexed.messageLength; i++)
|
|
{
|
|
buffer[i] = i2cIndexed.message[i];
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
//
|
|
// Parse EEPROM header, if present
|
|
//
|
|
static NvlStatus
|
|
_nvswitch_read_rom_header
|
|
(
|
|
nvswitch_device *device,
|
|
NVSWITCH_EEPROM_TYPE *eeprom,
|
|
NVSWITCH_FIRMWARE *firmware,
|
|
NvU32 *offset
|
|
)
|
|
{
|
|
NVSWITCH_EEPROM_HEADER eeprom_header = {{0}};
|
|
NvlStatus retval;
|
|
|
|
firmware->firmware_size = 0;
|
|
*offset = 0x0000;
|
|
|
|
retval = _nvswitch_read_rom_bytes(device,
|
|
eeprom, *offset,
|
|
(NvU8 *) &eeprom_header, sizeof(eeprom_header));
|
|
if (retval != NVL_SUCCESS)
|
|
{
|
|
NVSWITCH_PRINT(device, SETUP,
|
|
"Unable to read ROM header\n");
|
|
return retval;
|
|
}
|
|
|
|
if ((eeprom_header.signature[0] == 'N') &&
|
|
(eeprom_header.signature[1] == 'V') &&
|
|
(eeprom_header.signature[2] == 'L') &&
|
|
(eeprom_header.signature[3] == 'S') &&
|
|
(_nvswitch_calculate_checksum((NvU8 *) &eeprom_header, sizeof(eeprom_header)) == 0x00))
|
|
{
|
|
// Assume eeprom_header is version 1
|
|
|
|
*offset += eeprom_header.header_size;
|
|
|
|
firmware->pci_vendor_id = eeprom_header.pci_vendor_id;
|
|
firmware->pci_device_id = eeprom_header.pci_device_id;
|
|
firmware->pci_system_vendor_id = eeprom_header.pci_system_vendor_id;
|
|
firmware->pci_system_device_id = eeprom_header.pci_system_device_id;
|
|
|
|
// EEPROM header firmware size field is in 512 byte blocks
|
|
firmware->firmware_size = eeprom_header.firmware_size * 512;
|
|
}
|
|
else
|
|
{
|
|
NVSWITCH_PRINT(device, SETUP,
|
|
"Firmware header not found\n");
|
|
return -NVL_NOT_FOUND;
|
|
}
|
|
|
|
return NVL_SUCCESS;
|
|
}
|
|
|
|
static NvlStatus
|
|
_nvswitch_rom_parse_bit_bridge_fw_data
|
|
(
|
|
nvswitch_device *device,
|
|
NVSWITCH_EEPROM_TYPE *eeprom,
|
|
NVSWITCH_FIRMWARE *firmware,
|
|
NVSWITCH_BIT_TOKEN *bit_token
|
|
)
|
|
{
|
|
NVSWITCH_BIT_BRIDGE_FW_DATA bit_bridge_fw = {0};
|
|
NvU32 copy_size;
|
|
NvU32 bridge_fw_size;
|
|
NvlStatus retval;
|
|
|
|
firmware->bridge.bridge_fw_found = NV_FALSE;
|
|
|
|
if (bit_token->data_size != sizeof(bit_bridge_fw))
|
|
{
|
|
NVSWITCH_PRINT(device, SETUP,
|
|
"BIT_BRIDGE_FW_DATA: Expected data size 0x%x but found 0x%x\n",
|
|
(NvU32) sizeof(bit_bridge_fw), bit_token->data_size);
|
|
}
|
|
|
|
bridge_fw_size = NV_MIN(bit_token->data_size, sizeof(bit_bridge_fw));
|
|
|
|
// Get basic bridge-specific firmware info
|
|
retval = _nvswitch_read_rom_bytes(device, eeprom, bit_token->data_offset,
|
|
(NvU8 *) &bit_bridge_fw, bridge_fw_size);
|
|
if (retval != NVL_SUCCESS)
|
|
{
|
|
NVSWITCH_PRINT(device, SETUP,
|
|
"Failed to read BIT_BRIDGE_FW_DATA\n");
|
|
return -NVL_ERR_NOT_SUPPORTED;
|
|
}
|
|
|
|
firmware->bridge.bridge_fw_found = NV_TRUE;
|
|
|
|
firmware->bridge.firmware_version =
|
|
NVSWITCH_ELEMENT_READ(&bit_bridge_fw, firmware_version, bridge_fw_size, 0);
|
|
|
|
firmware->bridge.oem_version =
|
|
NVSWITCH_ELEMENT_READ(&bit_bridge_fw, oem_version, bridge_fw_size, 0);
|
|
|
|
NVSWITCH_ELEMENT_VALIDATE(&bit_bridge_fw, firmware_size, bridge_fw_size, 0,
|
|
firmware->firmware_size/512);
|
|
|
|
if (NVSWITCH_ELEMENT_PRESENT(&bit_bridge_fw, BIOS_MOD_date, bridge_fw_size))
|
|
{
|
|
nvswitch_os_memcpy(firmware->bridge.BIOS_MOD_date, bit_bridge_fw.BIOS_MOD_date,
|
|
sizeof(firmware->bridge.BIOS_MOD_date));
|
|
}
|
|
|
|
firmware->bridge.fw_release_build =
|
|
(NVSWITCH_ELEMENT_PRESENT(&bit_bridge_fw, firmware_flags, bridge_fw_size) ?
|
|
FLD_TEST_DRF(SWITCH_BIT_BRIDGE_FW_DATA, _FLAGS, _BUILD, _REL,
|
|
bit_bridge_fw.firmware_flags) :
|
|
NV_FALSE);
|
|
|
|
copy_size = NV_MIN(NVSWITCH_PRODUCT_NAME_MAX_LEN,
|
|
NVSWITCH_ELEMENT_READ(&bit_bridge_fw, eng_product_name_size, bridge_fw_size, 0));
|
|
if (copy_size > 0)
|
|
{
|
|
retval = _nvswitch_read_rom_bytes(device, eeprom,
|
|
bit_bridge_fw.eng_product_name,
|
|
(NvU8 *) firmware->bridge.product_name, copy_size);
|
|
if (retval != NVL_SUCCESS)
|
|
{
|
|
// Failed to read product name string
|
|
copy_size = 0;
|
|
}
|
|
}
|
|
firmware->bridge.product_name[copy_size] = 0;
|
|
|
|
firmware->bridge.instance_id = NVSWITCH_ELEMENT_READ(
|
|
&bit_bridge_fw,
|
|
nvswitch_instance_id,
|
|
bridge_fw_size,
|
|
NVSWITCH_FIRMWARE_BRIDGE_INSTANCE_ID_UNKNOWN);
|
|
|
|
return retval;
|
|
}
|
|
|
|
static NvlStatus
|
|
_nvswitch_rom_parse_bit_clock_ptrs
|
|
(
|
|
nvswitch_device *device,
|
|
NVSWITCH_EEPROM_TYPE *eeprom,
|
|
NVSWITCH_FIRMWARE *firmware,
|
|
NVSWITCH_BIT_TOKEN *bit_token
|
|
)
|
|
{
|
|
NVSWITCH_BIT_CLOCK_PTRS bit_clock_ptrs = {0};
|
|
NVSWITCH_PLL_INFO_HEADER pll_info_header;
|
|
NVSWITCH_PLL_INFO_ENTRY pll_info;
|
|
NvU32 pll_info_offset;
|
|
NvU32 idx_pll;
|
|
NvU32 clock_ptrs_size;
|
|
NvU32 pll_info_table;
|
|
NvlStatus retval;
|
|
|
|
firmware->clocks.clocks_found = NV_FALSE;
|
|
|
|
if (bit_token->data_size != sizeof(bit_clock_ptrs))
|
|
{
|
|
NVSWITCH_PRINT(device, SETUP,
|
|
"CLOCK_PTRS: Expected data size 0x%x but found 0x%x\n",
|
|
(NvU32) sizeof(bit_clock_ptrs), bit_token->data_size);
|
|
}
|
|
|
|
clock_ptrs_size = NV_MIN(bit_token->data_size, sizeof(bit_clock_ptrs));
|
|
|
|
// Get PLL limits
|
|
retval = _nvswitch_read_rom_bytes(device, eeprom, bit_token->data_offset,
|
|
(NvU8 *) &bit_clock_ptrs, clock_ptrs_size);
|
|
if (retval != NVL_SUCCESS)
|
|
{
|
|
NVSWITCH_PRINT(device, SETUP,
|
|
"NVINIT_PTRS: Failed to read BIT_TOKEN_CLOCK_PTRS\n");
|
|
return -NVL_ERR_NOT_SUPPORTED;
|
|
}
|
|
|
|
pll_info_table = NVSWITCH_ELEMENT_READ(&bit_clock_ptrs, pll_info_table, clock_ptrs_size, 0);
|
|
|
|
if ((pll_info_table == 0) ||
|
|
(pll_info_table + sizeof(pll_info_header) > firmware->firmware_size))
|
|
{
|
|
NVSWITCH_PRINT(device, SETUP,
|
|
"NVINIT_PTRS: BIT_TOKEN_CLOCK_PTRS not preset or out of range (0x%x)\n",
|
|
bit_clock_ptrs.pll_info_table);
|
|
return -NVL_ERR_NOT_SUPPORTED;
|
|
}
|
|
|
|
retval = _nvswitch_read_rom_bytes(device, eeprom, pll_info_table,
|
|
(NvU8 *) &pll_info_header, sizeof(pll_info_header));
|
|
if (retval != NVL_SUCCESS)
|
|
{
|
|
NVSWITCH_PRINT(device, SETUP,
|
|
"CLOCK_PTRS: Failed to read NVSWITCH_PLL_INFO_HEADER\n");
|
|
return -NVL_ERR_NOT_SUPPORTED;
|
|
}
|
|
|
|
if (pll_info_header.version != NVSWITCH_CLOCK_PTRS_PLL_INFO_VERSION)
|
|
{
|
|
NVSWITCH_PRINT(device, SETUP,
|
|
"PLL_INFO version (0x%x) != expected version (0x%x)\n",
|
|
pll_info_header.version, NVSWITCH_CLOCK_PTRS_PLL_INFO_VERSION);
|
|
return -NVL_ERR_NOT_SUPPORTED;
|
|
}
|
|
if (pll_info_header.header_size != sizeof(NVSWITCH_PLL_INFO_HEADER))
|
|
{
|
|
NVSWITCH_PRINT(device, SETUP,
|
|
"PLL_INFO header size (0x%x) != expected (0x%x)\n",
|
|
pll_info_header.header_size, (NvU32) sizeof(NVSWITCH_PLL_INFO_HEADER));
|
|
return -NVL_ERR_NOT_SUPPORTED;
|
|
}
|
|
if (pll_info_header.entry_size != sizeof(NVSWITCH_PLL_INFO_ENTRY))
|
|
{
|
|
NVSWITCH_PRINT(device, SETUP,
|
|
"PLL_INFO: Expected entry size 0x%x but found 0x%x\n",
|
|
(NvU32) sizeof(NVSWITCH_PLL_INFO_ENTRY), pll_info_header.entry_size);
|
|
return -NVL_ERR_NOT_SUPPORTED;
|
|
}
|
|
|
|
firmware->clocks.clocks_found = NV_TRUE;
|
|
firmware->clocks.sys_pll.valid = NV_FALSE;
|
|
|
|
for (idx_pll = 0; idx_pll < pll_info_header.entry_count; idx_pll++)
|
|
{
|
|
pll_info_offset =
|
|
bit_clock_ptrs.pll_info_table + pll_info_header.header_size +
|
|
idx_pll*pll_info_header.entry_size;
|
|
if (pll_info_offset + sizeof(pll_info) > firmware->firmware_size)
|
|
{
|
|
NVSWITCH_PRINT(device, SETUP,
|
|
"PLL info #%d out of range (%x+%x > %x)\n", idx_pll,
|
|
pll_info_offset, (NvU32) sizeof(pll_info), firmware->firmware_size);
|
|
retval = -NVL_NOT_FOUND;
|
|
break;
|
|
}
|
|
|
|
retval = _nvswitch_read_rom_bytes(device, eeprom, pll_info_offset,
|
|
(NvU8 *) &pll_info, sizeof(pll_info));
|
|
if (retval != NVL_SUCCESS)
|
|
{
|
|
NVSWITCH_PRINT(device, SETUP,
|
|
"CLOCK_PTRS: Failed to read NVSWITCH_PLL_INFO_ENTRY\n");
|
|
retval = -NVL_ERR_NOT_SUPPORTED;
|
|
break;
|
|
}
|
|
|
|
if (pll_info.pll_id == NVSWITCH_PLL_ID_SYSPLL)
|
|
{
|
|
if (firmware->clocks.sys_pll.valid)
|
|
{
|
|
NVSWITCH_PRINT(device, SETUP,
|
|
"NVINIT_PTRS: More than 1 SYSPLL entry found. Skipping\n");
|
|
}
|
|
else
|
|
{
|
|
firmware->clocks.sys_pll.valid = NV_TRUE;
|
|
firmware->clocks.sys_pll.ref_min_mhz = pll_info.ref_min_mhz;
|
|
firmware->clocks.sys_pll.ref_max_mhz = pll_info.ref_max_mhz;
|
|
firmware->clocks.sys_pll.vco_min_mhz = pll_info.vco_min_mhz;
|
|
firmware->clocks.sys_pll.vco_max_mhz = pll_info.vco_max_mhz;
|
|
firmware->clocks.sys_pll.update_min_mhz = pll_info.update_min_mhz;
|
|
firmware->clocks.sys_pll.update_max_mhz = pll_info.update_max_mhz;
|
|
firmware->clocks.sys_pll.m_min = pll_info.m_min;
|
|
firmware->clocks.sys_pll.m_max = pll_info.m_max;
|
|
firmware->clocks.sys_pll.n_min = pll_info.n_min;
|
|
firmware->clocks.sys_pll.n_max = pll_info.n_max;
|
|
firmware->clocks.sys_pll.pl_min = pll_info.pl_min;
|
|
firmware->clocks.sys_pll.pl_max = pll_info.pl_max;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
NVSWITCH_PRINT(device, SETUP,
|
|
"Ignoring PLL ID 0x%x\n", pll_info.pll_id);
|
|
}
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
static NvlStatus
|
|
_nvswitch_rom_parse_bit_nvinit_ptrs
|
|
(
|
|
nvswitch_device *device,
|
|
NVSWITCH_EEPROM_TYPE *eeprom,
|
|
NVSWITCH_FIRMWARE *firmware,
|
|
NVSWITCH_BIT_TOKEN *bit_token
|
|
)
|
|
{
|
|
NVSWITCH_BIT_NVINIT_PTRS bit_nvinit_ptrs = {0};
|
|
NVSWITCH_NVLINK_CONFIG nvlink_config;
|
|
NvU32 nvinit_ptrs_size;
|
|
NvU32 nvlink_config_offset;
|
|
NvU32 nvlink_config_size;
|
|
NvlStatus retval;
|
|
|
|
firmware->nvlink.link_config_found = NV_FALSE;
|
|
|
|
if (bit_token->data_size != sizeof(bit_nvinit_ptrs))
|
|
{
|
|
NVSWITCH_PRINT(device, SETUP,
|
|
"NVINIT_PTRS: Expected data size 0x%x but found 0x%x\n",
|
|
(NvU32) sizeof(bit_nvinit_ptrs), bit_token->data_size);
|
|
}
|
|
|
|
nvinit_ptrs_size = NV_MIN(bit_token->data_size, sizeof(bit_nvinit_ptrs));
|
|
|
|
// Get basic NVLink settings
|
|
retval = _nvswitch_read_rom_bytes(device, eeprom, bit_token->data_offset,
|
|
(NvU8 *) &bit_nvinit_ptrs, nvinit_ptrs_size);
|
|
if (retval != NVL_SUCCESS)
|
|
{
|
|
NVSWITCH_PRINT(device, SETUP,
|
|
"NVINIT_PTRS: Failed to read NVSWITCH_BIT_NVINIT_PTRS\n");
|
|
return -NVL_ERR_NOT_SUPPORTED;
|
|
}
|
|
|
|
nvlink_config_offset = NVSWITCH_ELEMENT_READ(&bit_nvinit_ptrs, nvlink_config, nvinit_ptrs_size, 0);
|
|
if ((nvlink_config_offset == 0) ||
|
|
(nvlink_config_offset + sizeof(nvlink_config) > firmware->firmware_size))
|
|
{
|
|
NVSWITCH_PRINT(device, SETUP,
|
|
"NVINIT_PTRS: NVSWITCH_BIT_NVINIT_PTRS NVLink config absent or out of range (0x%x)\n",
|
|
bit_nvinit_ptrs.nvlink_config);
|
|
return -NVL_ERR_NOT_SUPPORTED;
|
|
}
|
|
|
|
retval = _nvswitch_read_rom_bytes(device, eeprom, nvlink_config_offset,
|
|
(NvU8 *) &nvlink_config, sizeof(nvlink_config));
|
|
if (retval != NVL_SUCCESS)
|
|
{
|
|
NVSWITCH_PRINT(device, SETUP,
|
|
"NVINIT_PTRS: Failed to read NVSWITCH_NVLINK_CONFIG\n");
|
|
return -NVL_ERR_NOT_SUPPORTED;
|
|
}
|
|
|
|
nvlink_config_size = NV_MIN(nvlink_config.size, sizeof(nvlink_config));
|
|
|
|
if (0x01 != NVSWITCH_ELEMENT_READ(&nvlink_config, version, nvlink_config_size, 0))
|
|
{
|
|
NVSWITCH_PRINT(device, SETUP,
|
|
"NVINIT_PTRS: NVLINK_CONFIG version mismatch (0x01 != 0x%x)\n",
|
|
NVSWITCH_ELEMENT_READ(&nvlink_config, version, nvlink_config_size, 0));
|
|
return -NVL_ERR_NOT_SUPPORTED;
|
|
}
|
|
|
|
NVSWITCH_ELEMENT_CHECK(&nvlink_config, flags, nvlink_config_size, 0x0);
|
|
NVSWITCH_ELEMENT_CHECK(&nvlink_config, link_speed_mask, nvlink_config_size, 0x0);
|
|
NVSWITCH_ELEMENT_CHECK(&nvlink_config, link_refclk_mask, nvlink_config_size, 0x0);
|
|
|
|
firmware->nvlink.link_config_found = NV_TRUE;
|
|
|
|
//
|
|
// If nvlink_config is incomplete, assume:
|
|
// 1) all links enabled
|
|
// 2) DC coupled
|
|
//
|
|
firmware->nvlink.link_enable_mask = ~NVSWITCH_ELEMENT_READ(&nvlink_config, link_disable_mask, nvlink_config_size, 0);
|
|
firmware->nvlink.link_ac_coupled_mask = NVSWITCH_ELEMENT_READ(&nvlink_config, ac_coupled_mask, nvlink_config_size, 0);
|
|
|
|
return retval;
|
|
}
|
|
|
|
static void
|
|
_nvswitch_rom_parse_bit_dcb_ccb_block
|
|
(
|
|
nvswitch_device *device,
|
|
NVSWITCH_EEPROM_TYPE *eeprom,
|
|
NVSWITCH_FIRMWARE *firmware,
|
|
NvU32 ccb_block_offset
|
|
)
|
|
{
|
|
NVSWITCH_CCB_TABLE ccb;
|
|
NVSWITCH_CCB_ENTRY ccb_entry;
|
|
NvU32 ccb_table_offset;
|
|
NvU32 idx_ccb;
|
|
NvU32 retval;
|
|
|
|
// dcb:ccb_block_ptr
|
|
if ((ccb_block_offset == 0) ||
|
|
(ccb_block_offset + sizeof(NVSWITCH_CCB_TABLE) > firmware->firmware_size))
|
|
{
|
|
NVSWITCH_PRINT(device, SETUP,
|
|
"DCB_PTRS: CCB_BLOCK absent or out of range (0x%x)\n",
|
|
ccb_block_offset);
|
|
return;
|
|
}
|
|
|
|
retval = _nvswitch_read_rom_bytes(device, eeprom, ccb_block_offset,
|
|
(NvU8 *) &ccb, sizeof(ccb));
|
|
if (retval != NVL_SUCCESS)
|
|
{
|
|
NVSWITCH_PRINT(device, SETUP, "DCB_PTRS: CCB header read failure\n");
|
|
return;
|
|
}
|
|
|
|
if ((ccb.version != NVSWITCH_CCB_VERSION) ||
|
|
(ccb.header_size != sizeof(ccb)))
|
|
{
|
|
NVSWITCH_PRINT(device, SETUP,
|
|
"DCB_PTRS: CCB_BLOCK version (0x%x) or size mismatch (0x%x)\n",
|
|
ccb.version, ccb.header_size);
|
|
return;
|
|
}
|
|
|
|
ccb_table_offset = ccb_block_offset + ccb.header_size;
|
|
|
|
for (idx_ccb = 0; idx_ccb < ccb.entry_count; idx_ccb++)
|
|
{
|
|
NvU32 ccb_entry_offset = ccb_table_offset + idx_ccb*ccb.entry_size;
|
|
NvU32 i2c_bus_idx;
|
|
NvU32 idx_i2c_port;
|
|
|
|
if (ccb_entry_offset + sizeof(ccb_entry) > firmware->firmware_size)
|
|
{
|
|
NVSWITCH_PRINT(device, SETUP,
|
|
"DCB_PTRS: CCB out of range\n");
|
|
break;
|
|
}
|
|
|
|
retval = _nvswitch_read_rom_bytes(device, eeprom, ccb_entry_offset,
|
|
(NvU8 *) &ccb_entry, sizeof(ccb_entry));
|
|
if (retval != NVL_SUCCESS)
|
|
{
|
|
NVSWITCH_PRINT(device, SETUP,
|
|
"DCB_PTRS: CCB entry[%d] read failure\n",
|
|
idx_ccb);
|
|
break;
|
|
}
|
|
|
|
i2c_bus_idx = DRF_VAL(SWITCH_CCB, _DEVICE, _I2C_PORT, ccb_entry.device);
|
|
if (i2c_bus_idx >= NVSWITCH_MAX_I2C_PORTS)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
for (idx_i2c_port = 0; idx_i2c_port < NVSWITCH_MAX_I2C_PORTS; idx_i2c_port++)
|
|
{
|
|
if (ccb.comm_port[idx_i2c_port] == i2c_bus_idx)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (idx_i2c_port >= NVSWITCH_MAX_I2C_PORTS)
|
|
{
|
|
NVSWITCH_PRINT(device, SETUP,
|
|
"DCB_PTRS: CCB entry[%d] I2C port %x out of range\n",
|
|
idx_ccb, idx_i2c_port);
|
|
continue;
|
|
}
|
|
|
|
firmware->dcb.i2c[idx_i2c_port].valid = NV_TRUE;
|
|
firmware->dcb.i2c[idx_i2c_port].i2c_speed = DRF_VAL(SWITCH_CCB, _DEVICE, _I2C_SPEED, ccb_entry.device);
|
|
firmware->dcb.i2c[idx_i2c_port].i2c_33v = DRF_VAL(SWITCH_CCB, _DEVICE, _VOLTAGE, ccb_entry.device);
|
|
}
|
|
}
|
|
|
|
static void
|
|
_nvswitch_rom_parse_bit_dcb_gpio_table
|
|
(
|
|
nvswitch_device *device,
|
|
NVSWITCH_EEPROM_TYPE *eeprom,
|
|
NVSWITCH_FIRMWARE *firmware,
|
|
NvU32 gpio_table_offset
|
|
)
|
|
{
|
|
NVSWITCH_GPIO_TABLE gpio;
|
|
NVSWITCH_GPIO_ENTRY gpio_entry;
|
|
NvU32 idx_gpio;
|
|
NvU32 retval;
|
|
|
|
// gpio_tables
|
|
if ((gpio_table_offset == 0) ||
|
|
(gpio_table_offset + sizeof(NVSWITCH_GPIO_TABLE) > firmware->firmware_size))
|
|
{
|
|
NVSWITCH_PRINT(device, SETUP,
|
|
"DCB_PTRS: GPIO_TABLE absent or out of range (0x%x)\n",
|
|
gpio_table_offset);
|
|
return;
|
|
}
|
|
|
|
retval = _nvswitch_read_rom_bytes(device, eeprom, gpio_table_offset,
|
|
(NvU8 *) &gpio, sizeof(gpio));
|
|
if (retval != NVL_SUCCESS)
|
|
{
|
|
NVSWITCH_PRINT(device, SETUP,
|
|
"DCB_PTRS: GPIO table read failure\n");
|
|
return;
|
|
}
|
|
|
|
if ((gpio.version != NVSWITCH_GPIO_TABLE_VERSION_42) ||
|
|
(gpio.header_size != sizeof(gpio)))
|
|
{
|
|
NVSWITCH_PRINT(device, SETUP,
|
|
"DCB_PTRS: GPIO_TABLE version (0x%x) or size mismatch (0x%x)\n",
|
|
gpio.version, gpio.header_size);
|
|
return;
|
|
}
|
|
|
|
if (gpio.entry_size != sizeof(gpio_entry))
|
|
{
|
|
NVSWITCH_PRINT(device, SETUP,
|
|
"DCB_PTRS: GPIO_ENTRY size mismatch (0x%x != 0x%x)\n",
|
|
gpio.entry_size, (NvU32) sizeof(gpio_entry));
|
|
return;
|
|
}
|
|
|
|
NVSWITCH_ELEMENT_CHECK(&gpio, ext_gpio_master, gpio.header_size, 0x0000);
|
|
|
|
gpio_table_offset += gpio.header_size;
|
|
firmware->dcb.gpio_pin_count = 0;
|
|
|
|
for (idx_gpio = 0; idx_gpio < gpio.entry_count; idx_gpio++)
|
|
{
|
|
NVSWITCH_GPIO_INFO *gpio_pin;
|
|
NvU32 gpio_entry_offset = gpio_table_offset + idx_gpio*gpio.entry_size;
|
|
|
|
if (gpio_entry_offset + gpio.entry_size > firmware->firmware_size)
|
|
{
|
|
NVSWITCH_PRINT(device, SETUP,
|
|
"DCB_PTRS: GPIO entry[%d] out of range\n",
|
|
idx_gpio);
|
|
break;
|
|
}
|
|
|
|
if (firmware->dcb.gpio_pin_count == NVSWITCH_MAX_GPIO_PINS)
|
|
{
|
|
NVSWITCH_PRINT(device, SETUP,
|
|
"DCB_PTRS: Too many GPIO pins listed\n");
|
|
break;
|
|
}
|
|
|
|
retval = _nvswitch_read_rom_bytes(device, eeprom, gpio_entry_offset,
|
|
(NvU8 *) &gpio_entry, sizeof(gpio_entry));
|
|
if (retval != NVL_SUCCESS)
|
|
{
|
|
NVSWITCH_PRINT(device, SETUP,
|
|
"DCB_PTRS: GPIO entry read failure\n");
|
|
break;
|
|
}
|
|
|
|
if (!FLD_TEST_DRF(SWITCH_GPIO_ENTRY, , _FUNCTION, _SKIP_ENTRY, gpio_entry.function))
|
|
{
|
|
gpio_pin = &firmware->dcb.gpio_pin[firmware->dcb.gpio_pin_count];
|
|
firmware->dcb.gpio_pin_count++;
|
|
|
|
gpio_pin->pin = DRF_VAL(SWITCH_GPIO_ENTRY, _PIN, _NUM, gpio_entry.pin);
|
|
gpio_pin->function = DRF_VAL(SWITCH_GPIO_ENTRY, , _FUNCTION, gpio_entry.function);
|
|
gpio_pin->hw_select = DRF_VAL(SWITCH_GPIO_ENTRY, _INPUT, _HW_SELECT, gpio_entry.input);
|
|
gpio_pin->misc = DRF_VAL(SWITCH_GPIO_ENTRY, _MISC, _IO, gpio_entry.misc);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
_nvswitch_rom_parse_bit_dcb_i2c_devices
|
|
(
|
|
nvswitch_device *device,
|
|
NVSWITCH_EEPROM_TYPE *eeprom,
|
|
NVSWITCH_FIRMWARE *firmware,
|
|
NvU32 i2c_devices_offset
|
|
)
|
|
{
|
|
NVSWITCH_I2C_TABLE i2c;
|
|
NVSWITCH_I2C_ENTRY i2c_entry;
|
|
NvU32 i2c_table_offset;
|
|
NvU32 idx_i2c;
|
|
NvU32 retval;
|
|
|
|
// i2c_devices
|
|
if ((i2c_devices_offset == 0) ||
|
|
(i2c_devices_offset + sizeof(NVSWITCH_I2C_TABLE) > firmware->firmware_size))
|
|
{
|
|
NVSWITCH_PRINT(device, SETUP,
|
|
"DCB_PTRS: I2C_DEVICES absent or out of range (0x%x)\n",
|
|
i2c_devices_offset);
|
|
return;
|
|
}
|
|
|
|
retval = _nvswitch_read_rom_bytes(device, eeprom, i2c_devices_offset,
|
|
(NvU8 *) &i2c, sizeof(i2c));
|
|
if (retval != NVL_SUCCESS)
|
|
{
|
|
NVSWITCH_PRINT(device, SETUP,
|
|
"DCB_PTRS: I2C device read failure\n");
|
|
return;
|
|
}
|
|
|
|
if ((i2c.version != NVSWITCH_I2C_VERSION) ||
|
|
(i2c.header_size != sizeof(i2c)) ||
|
|
(i2c.entry_size != sizeof(i2c_entry)))
|
|
{
|
|
NVSWITCH_PRINT(device, SETUP,
|
|
"DCB_PTRS: I2C header version (0x%x) or header/entry size mismatch (0x%x/0x%x)\n",
|
|
i2c.version, i2c.header_size, i2c.entry_size);
|
|
return;
|
|
}
|
|
|
|
i2c_table_offset = i2c_devices_offset + i2c.header_size;
|
|
|
|
firmware->dcb.i2c_device_count = 0;
|
|
|
|
for (idx_i2c = 0; idx_i2c < i2c.entry_count; idx_i2c++)
|
|
{
|
|
NvU32 i2c_entry_offset = i2c_table_offset + idx_i2c*i2c.entry_size;
|
|
NVSWITCH_I2C_DEVICE_DESCRIPTOR_TYPE *i2c_device;
|
|
|
|
if (i2c_entry_offset + sizeof(i2c_entry) > firmware->firmware_size)
|
|
{
|
|
NVSWITCH_PRINT(device, SETUP,
|
|
"DCB_PTRS: I2C[%d] out of range\n",
|
|
idx_i2c);
|
|
break;
|
|
}
|
|
|
|
if (firmware->dcb.i2c_device_count >= NVSWITCH_MAX_I2C_DEVICES)
|
|
{
|
|
NVSWITCH_PRINT(device, SETUP,
|
|
"DCB_PTRS: Too many I2C devices listed\n");
|
|
break;
|
|
}
|
|
|
|
retval = _nvswitch_read_rom_bytes(device, eeprom, i2c_entry_offset,
|
|
(NvU8 *) &i2c_entry, sizeof(i2c_entry));
|
|
if (retval != NVL_SUCCESS)
|
|
{
|
|
NVSWITCH_PRINT(device, SETUP,
|
|
"DCB_PTRS: I2C read failure\n");
|
|
break;
|
|
}
|
|
|
|
if (NVSWITCH_I2C_DEVICE_SKIP != DRF_VAL(SWITCH_I2C, _ENTRY, _TYPE, i2c_entry.device))
|
|
{
|
|
i2c_device = &firmware->dcb.i2c_device[firmware->dcb.i2c_device_count];
|
|
firmware->dcb.i2c_device_count++;
|
|
|
|
i2c_device->i2cDeviceType = DRF_VAL(SWITCH_I2C, _ENTRY, _TYPE, i2c_entry.device);
|
|
i2c_device->i2cAddress = DRF_VAL(SWITCH_I2C, _ENTRY, _ADDRESS, i2c_entry.device);
|
|
i2c_device->i2cPortLogical =
|
|
(DRF_VAL(SWITCH_I2C, _ENTRY, _PORT_2, i2c_entry.device) << 1) |
|
|
DRF_VAL(SWITCH_I2C, _ENTRY, _PORT_1, i2c_entry.device);
|
|
}
|
|
}
|
|
}
|
|
|
|
static NvlStatus
|
|
_nvswitch_rom_parse_bit_dcb_ptrs
|
|
(
|
|
nvswitch_device *device,
|
|
NVSWITCH_EEPROM_TYPE *eeprom,
|
|
NVSWITCH_FIRMWARE *firmware,
|
|
NVSWITCH_BIT_TOKEN *bit_token
|
|
)
|
|
{
|
|
NVSWITCH_BIT_DCB_PTRS dcb_ptrs;
|
|
NVSWITCH_DCB_HEADER dcb;
|
|
NvU32 dcb_ptrs_size;
|
|
NvU32 dcb_version;
|
|
NvU32 dcb_signature;
|
|
NvlStatus retval = NVL_SUCCESS;
|
|
|
|
firmware->dcb.dcb_found = NV_FALSE;
|
|
|
|
if (bit_token->data_size != sizeof(dcb_ptrs))
|
|
{
|
|
NVSWITCH_PRINT(device, SETUP,
|
|
"DCB_PTRS: Expected data size 0x%x but found 0x%x\n",
|
|
(NvU32) sizeof(dcb_ptrs), bit_token->data_size);
|
|
}
|
|
|
|
dcb_ptrs_size = NV_MIN(bit_token->data_size, sizeof(dcb_ptrs));
|
|
|
|
// Get I2C & GPIO tables
|
|
retval = _nvswitch_read_rom_bytes(device, eeprom, bit_token->data_offset,
|
|
(NvU8 *) &dcb_ptrs, dcb_ptrs_size);
|
|
if (retval != NVL_SUCCESS)
|
|
{
|
|
NVSWITCH_PRINT(device, SETUP,
|
|
"DCB_PTRS: Failed to read NVSWITCH_BIT_DCB_PTRS\n");
|
|
return retval;
|
|
}
|
|
|
|
if ((dcb_ptrs.dcb_header_ptr == 0) ||
|
|
(dcb_ptrs.dcb_header_ptr >= firmware->firmware_size))
|
|
{
|
|
NVSWITCH_PRINT(device, SETUP,
|
|
"DCB_PTRS: DCB header absent or out of range (0x%x)\n",
|
|
dcb_ptrs.dcb_header_ptr);
|
|
return -NVL_ERR_NOT_SUPPORTED;
|
|
}
|
|
|
|
retval = _nvswitch_read_rom_bytes(device, eeprom, dcb_ptrs.dcb_header_ptr,
|
|
(NvU8 *) &dcb, sizeof(dcb));
|
|
if (retval != NVL_SUCCESS)
|
|
{
|
|
NVSWITCH_PRINT(device, SETUP,
|
|
"DCB_PTRS: DCB header read failure\n");
|
|
return retval;
|
|
}
|
|
|
|
dcb_version = NVSWITCH_ELEMENT_READ(&dcb, version, dcb.header_size, 0x0);
|
|
dcb_signature = NVSWITCH_ELEMENT_READ(&dcb, dcb_signature, dcb.header_size, 0x0);
|
|
if ((dcb_version != NVSWITCH_DCB_HEADER_VERSION_41) ||
|
|
(dcb_signature != NVSWITCH_DCB_HEADER_SIGNATURE))
|
|
{
|
|
NVSWITCH_PRINT(device, SETUP,
|
|
"DCB_PTRS: DCB header version (0x%x) or signature mismatch (0x%x)\n",
|
|
dcb_version, dcb_signature);
|
|
return -NVL_ERR_NOT_SUPPORTED;
|
|
}
|
|
|
|
_nvswitch_rom_parse_bit_dcb_ccb_block(device, eeprom, firmware, dcb.ccb_block_ptr);
|
|
_nvswitch_rom_parse_bit_dcb_i2c_devices(device, eeprom, firmware, dcb.i2c_devices);
|
|
_nvswitch_rom_parse_bit_dcb_gpio_table(device, eeprom, firmware, dcb.gpio_table);
|
|
|
|
return retval;
|
|
}
|
|
|
|
//
|
|
// Parse BIT tokens, if present
|
|
//
|
|
static NvlStatus
|
|
_nvswitch_read_bit_tokens
|
|
(
|
|
nvswitch_device *device,
|
|
NVSWITCH_EEPROM_TYPE *eeprom,
|
|
NVSWITCH_FIRMWARE *firmware,
|
|
NVSWITCH_BIT_HEADER *bit_header,
|
|
NvU32 *offset
|
|
)
|
|
{
|
|
NvU32 idx_token;
|
|
NvU32 bit_entry_offset;
|
|
NVSWITCH_BIT_TOKEN bit_token;
|
|
NvlStatus retval = NVL_SUCCESS;
|
|
|
|
for (idx_token = 0; idx_token < bit_header->token_entries; idx_token++)
|
|
{
|
|
bit_entry_offset = *offset + idx_token*bit_header->token_size;
|
|
if (bit_entry_offset >= firmware->firmware_size)
|
|
{
|
|
NVSWITCH_PRINT(device, SETUP,
|
|
"BIT token out of range (%x >= %x)\n",
|
|
bit_entry_offset, firmware->firmware_size);
|
|
return -NVL_NOT_FOUND;
|
|
}
|
|
|
|
retval = _nvswitch_read_rom_bytes(device,
|
|
eeprom, bit_entry_offset,
|
|
(NvU8 *) &bit_token, sizeof(bit_token));
|
|
if (retval != NVL_SUCCESS)
|
|
{
|
|
NVSWITCH_PRINT(device, SETUP,
|
|
"Error reading BIT token[%d]\n", idx_token);
|
|
return -NVL_NOT_FOUND;
|
|
}
|
|
|
|
if (bit_token.data_offset >= firmware->firmware_size)
|
|
{
|
|
NVSWITCH_PRINT(device, SETUP,
|
|
"BIT 0x%x target data out of range (%x >= %x)\n",
|
|
bit_token.id,
|
|
bit_token.data_offset, firmware->firmware_size);
|
|
// Soldier on to the next one. Hopefully it's valid
|
|
continue;
|
|
}
|
|
|
|
switch (bit_token.id)
|
|
{
|
|
case NVSWITCH_BIT_TOKEN_CLOCK_PTRS:
|
|
{
|
|
retval = _nvswitch_rom_parse_bit_clock_ptrs(device, eeprom, firmware, &bit_token);
|
|
break;
|
|
}
|
|
case NVSWITCH_BIT_TOKEN_NVINIT_PTRS:
|
|
{
|
|
retval = _nvswitch_rom_parse_bit_nvinit_ptrs(device, eeprom, firmware, &bit_token);
|
|
break;
|
|
}
|
|
case NVSWITCH_BIT_TOKEN_NOP:
|
|
{
|
|
// Ignore
|
|
break;
|
|
}
|
|
case NVSWITCH_BIT_TOKEN_PERF_PTRS:
|
|
{
|
|
NVSWITCH_PRINT(device, INFO, "Skipping parsing BIT_TOKEN_PERF_PTRS\n");
|
|
break;
|
|
}
|
|
case NVSWITCH_BIT_TOKEN_BRIDGE_FW_DATA:
|
|
{
|
|
retval = _nvswitch_rom_parse_bit_bridge_fw_data(device, eeprom, firmware, &bit_token);
|
|
break;
|
|
}
|
|
case NVSWITCH_BIT_TOKEN_DCB_PTRS:
|
|
{
|
|
retval = _nvswitch_rom_parse_bit_dcb_ptrs(device, eeprom, firmware, &bit_token);
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
NVSWITCH_PRINT(device, SETUP,
|
|
"Unrecognized BIT_TOKEN 0x%02x\n", bit_token.id);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
//
|
|
// Parse BIT table, if present
|
|
//
|
|
static NvlStatus
|
|
_nvswitch_read_bit_table
|
|
(
|
|
nvswitch_device *device,
|
|
NVSWITCH_EEPROM_TYPE *eeprom,
|
|
NVSWITCH_FIRMWARE *firmware,
|
|
NvU32 *offset
|
|
)
|
|
{
|
|
NVSWITCH_BIT_HEADER bit_header = {0};
|
|
NvlStatus retval;
|
|
|
|
retval = _nvswitch_read_rom_bytes(device,
|
|
eeprom, *offset,
|
|
(NvU8 *) &bit_header, sizeof(bit_header));
|
|
if (retval != NVL_SUCCESS)
|
|
{
|
|
NVSWITCH_PRINT(device, SETUP,
|
|
"Unable to read BIT header @%04x\n",
|
|
*offset);
|
|
return retval;
|
|
}
|
|
|
|
if ((bit_header.id == 0xB8FF) &&
|
|
(bit_header.signature[0] == 'B') &&
|
|
(bit_header.signature[1] == 'I') &&
|
|
(bit_header.signature[2] == 'T') &&
|
|
(bit_header.signature[3] == 0x00) &&
|
|
(bit_header.bcd_version == 0x0100) &&
|
|
(_nvswitch_calculate_checksum((NvU8 *) &bit_header, sizeof(bit_header)) == 0x00))
|
|
{
|
|
*offset += bit_header.header_size;
|
|
if (*offset >= firmware->firmware_size)
|
|
{
|
|
NVSWITCH_PRINT(device, SETUP,
|
|
"BIT token table out of range (%x >= %x)\n",
|
|
*offset, firmware->firmware_size);
|
|
return -NVL_NOT_FOUND;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
NVSWITCH_PRINT(device, SETUP,
|
|
"BIT header not found @%04x\n",
|
|
*offset);
|
|
return -NVL_NOT_FOUND;
|
|
}
|
|
|
|
retval = _nvswitch_read_bit_tokens(device, eeprom, firmware, &bit_header, offset);
|
|
if (retval != NVL_SUCCESS)
|
|
{
|
|
NVSWITCH_PRINT(device, SETUP,
|
|
"Unable to read BIT tokens\n");
|
|
return retval;
|
|
}
|
|
|
|
return NVL_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// Print BIT table information
|
|
//
|
|
static void
|
|
_nvswitch_print_bit_table_info
|
|
(
|
|
nvswitch_device *device,
|
|
NVSWITCH_FIRMWARE *firmware
|
|
)
|
|
{
|
|
if (firmware->firmware_size > 0)
|
|
{
|
|
NVSWITCH_PRINT(device, SETUP, "PCI ID: %04x/%04x\n",
|
|
firmware->pci_vendor_id,
|
|
firmware->pci_device_id);
|
|
NVSWITCH_PRINT(device, SETUP, "Subsystem PCI ID: %04x/%04x\n",
|
|
firmware->pci_system_vendor_id,
|
|
firmware->pci_system_device_id);
|
|
|
|
if (firmware->bridge.bridge_fw_found)
|
|
{
|
|
NVSWITCH_PRINT(device, SETUP, "firmware_version: %08x\n",
|
|
firmware->bridge.firmware_version);
|
|
NVSWITCH_PRINT(device, SETUP, "oem_version: %02x\n",
|
|
firmware->bridge.oem_version);
|
|
NVSWITCH_PRINT(device, SETUP, "BIOS_MOD_date: '%.8s'\n",
|
|
firmware->bridge.BIOS_MOD_date);
|
|
NVSWITCH_PRINT(device, SETUP, "fw_release_build: %s\n",
|
|
(firmware->bridge.fw_release_build ? "REL" : "ENG"));
|
|
NVSWITCH_PRINT(device, SETUP, "product_name: '%s'\n",
|
|
firmware->bridge.product_name);
|
|
if (firmware->bridge.instance_id != NVSWITCH_FIRMWARE_BRIDGE_INSTANCE_ID_UNKNOWN)
|
|
{
|
|
NVSWITCH_PRINT(device, SETUP, "instance_id: %04x\n",
|
|
firmware->bridge.instance_id);
|
|
}
|
|
}
|
|
|
|
if (firmware->nvlink.link_config_found)
|
|
{
|
|
NVSWITCH_PRINT(device, SETUP, "link_enable: %016llx\n", firmware->nvlink.link_enable_mask);
|
|
NVSWITCH_PRINT(device, SETUP, "ac_coupled: %016llx\n", firmware->nvlink.link_ac_coupled_mask);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Parse EEPROM BIT tables, if present
|
|
//
|
|
void
|
|
nvswitch_read_rom_tables
|
|
(
|
|
nvswitch_device *device,
|
|
NVSWITCH_FIRMWARE *firmware
|
|
)
|
|
{
|
|
NVSWITCH_EEPROM_TYPE eeprom = {0};
|
|
NvU32 offset;
|
|
NvlStatus retval;
|
|
|
|
retval = nvswitch_get_rom_info(device, &eeprom);
|
|
if (retval != NVL_SUCCESS)
|
|
{
|
|
NVSWITCH_PRINT(device, SETUP,
|
|
"ROM configuration not supported\n");
|
|
return;
|
|
}
|
|
|
|
retval = _nvswitch_read_rom_header(device, &eeprom, firmware, &offset);
|
|
if (retval != NVL_SUCCESS)
|
|
{
|
|
NVSWITCH_PRINT(device, SETUP,
|
|
"Unable to read ROM header\n");
|
|
return;
|
|
}
|
|
|
|
retval = _nvswitch_read_bit_table(device, &eeprom, firmware, &offset);
|
|
if (retval != NVL_SUCCESS)
|
|
{
|
|
NVSWITCH_PRINT(device, SETUP,
|
|
"Unable to read BIT table\n");
|
|
return;
|
|
}
|
|
|
|
_nvswitch_print_bit_table_info(device, firmware);
|
|
|
|
return;
|
|
}
|
|
|