Files
open-gpu-kernel-modules/src/common/nvswitch/kernel/rom_nvswitch.c
Andy Ritger 1739a20efc 515.43.04
2022-05-09 13:18:59 -07:00

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;
}