iPXE
Defines | Functions
mlx_device.c File Reference
#include "../include/mlx_device.h"
#include "../include/mlx_cmd.h"
#include "../../mlx_utils/include/public/mlx_bail.h"
#include "../../mlx_utils/include/public/mlx_pci.h"
#include "../../mlx_utils/include/public/mlx_memory.h"
#include "../../mlx_utils/include/public/mlx_logging.h"

Go to the source code of this file.

Defines

#define CHECK_BIT(field, offset)   (((field) & ((mlx_uint32)1 << (offset))) != 0)
#define CHECK_DEVICE_INIT_TRIES   10
#define NODNIC_DEVICE_SUPPORT_MAC_FILTERS_OFFSET   15
#define NODNIC_DEVICE_SUPPORT_PROMISC_FILTER_OFFSET   14
#define NODNIC_DEVICE_SUPPORT_PROMISC_MULT_FILTER_OFFSET   13
#define NODNIC_DEVICE_LOG_WORKING_BUFFER_SIZE_OFFSET   8
#define NODNIC_DEVICE_LOG_WORKING_BUFFER_SIZE_MASK   0x7
#define NODNIC_DEVICE_LOG_PKEY_TABLE_SIZE_OFFSET   4
#define NODNIC_DEVICE_LOG_PKEY_TABLE_SIZE_MASK   0xF
#define NODNIC_DEVICE_NUM_PORTS_OFFSET   0
#define NODNIC_DEVICE_LOG_MAX_RING_SIZE_OFFSET   24
#define NODNIC_DEVICE_LOG_MAX_RING_SIZE_MASK   0x3F
#define NODNIC_DEVICE_PD_MASK   0xFFFFFF
#define NODNIC_DEVICE_SUPPORT_RX_PI_DMA_OFFSET   31
#define NODNIC_DEVICE_SUPPORT_RX_PI_DMA_MASK   0x1
#define NODNIC_DEVICE_SUPPORT_UAR_TRX_DB_OFFSET   29
#define NODNIC_DEVICE_SUPPORT_UAR_TRX_DB_MASK   0x1
#define NODNIC_DEVICE_SUPPORT_BAR_CQ_CONTROL_OFFSET   27
#define NODNIC_DEVICE_SUPPORT_BAR_CQ_CONTROL_MASK   0x1
#define NODNIC_DEVICE_LOG_UAR_PAGE_SIZE_OFFSET   0
#define NODNIC_DEVICE_LOG_UAR_PAGE_SIZE_MASK   0xFF

Functions

 FILE_LICENCE (GPL2_OR_LATER)
static mlx_status check_nodnic_interface_supported (IN nodnic_device_priv *device_priv, OUT mlx_boolean *out)
static mlx_status wait_for_device_initialization (IN nodnic_device_priv *device_priv)
static mlx_status disable_nodnic_inteface (IN nodnic_device_priv *device_priv)
static mlx_status nodnic_device_start_nodnic (IN nodnic_device_priv *device_priv)
static mlx_status nodnic_device_get_nodnic_data (IN nodnic_device_priv *device_priv)
mlx_status nodnic_device_clear_int (IN nodnic_device_priv *device_priv)
mlx_status nodnic_device_init (IN nodnic_device_priv *device_priv)
mlx_status nodnic_device_teardown (IN nodnic_device_priv *device_priv)
mlx_status nodnic_device_get_cap (IN nodnic_device_priv *device_priv)
mlx_status nodnic_device_get_fw_version (IN nodnic_device_priv *device_priv, OUT mlx_uint16 *fw_ver_minor, OUT mlx_uint16 *fw_ver_sub_minor, OUT mlx_uint16 *fw_ver_major)

Define Documentation

#define CHECK_BIT (   field,
  offset 
)    (((field) & ((mlx_uint32)1 << (offset))) != 0)
#define CHECK_DEVICE_INIT_TRIES   10

Referenced by nodnic_device_get_cap().

Referenced by nodnic_device_get_cap().

Referenced by nodnic_device_get_cap().

Referenced by nodnic_device_get_cap().

Referenced by nodnic_device_get_cap().

Referenced by nodnic_device_get_cap().

Referenced by nodnic_device_get_cap().

Referenced by nodnic_device_get_cap().

Referenced by nodnic_device_get_cap().

Referenced by nodnic_device_get_cap().

#define NODNIC_DEVICE_PD_MASK   0xFFFFFF

Referenced by nodnic_device_get_cap().

Referenced by nodnic_device_get_cap().

Referenced by nodnic_device_get_cap().

Referenced by nodnic_device_get_cap().

Referenced by nodnic_device_get_cap().

Referenced by nodnic_device_get_cap().


Function Documentation

FILE_LICENCE ( GPL2_OR_LATER  )
static mlx_status check_nodnic_interface_supported ( IN nodnic_device_priv device_priv,
OUT mlx_boolean out 
) [static]

Definition at line 33 of file mlx_device.c.

References CHECK_BIT, MLX_FATAL_CHECK_STATUS, MLX_SUCCESS, nodnic_cmd_read(), NODNIC_NIC_INTERFACE_SUPPORTED_BIT, NODNIC_NIC_INTERFACE_SUPPORTED_OFFSET, and status.

Referenced by nodnic_device_start_nodnic().

{
        mlx_status status = MLX_SUCCESS;
        mlx_uint32      output = 0;
        status = nodnic_cmd_read(device_priv, NODNIC_NIC_INTERFACE_SUPPORTED_OFFSET,
                        &output);
        MLX_FATAL_CHECK_STATUS(status, read_error, "failed to read nic_interface_supported");
        *out = CHECK_BIT(output, NODNIC_NIC_INTERFACE_SUPPORTED_BIT);
read_error:
        return status;
}
static mlx_status wait_for_device_initialization ( IN nodnic_device_priv device_priv) [static]

Definition at line 50 of file mlx_device.c.

References buffer, CHECK_BIT, CHECK_DEVICE_INIT_TRIES, MLX_CHECK_STATUS, MLX_FAILED, MLX_SUCCESS, mlx_utils_delay_in_ms(), nodnic_cmd_read(), NODNIC_INITIALIZING_BIT, NODNIC_INITIALIZING_OFFSET, and status.

Referenced by disable_nodnic_inteface(), and nodnic_device_start_nodnic().

{
        mlx_status status = MLX_SUCCESS;
        mlx_uint8 try = 0;
        mlx_uint32                      buffer = 0;

#define CHECK_DEVICE_INIT_TRIES 10
        for( ; try < CHECK_DEVICE_INIT_TRIES ; try++){
                status = nodnic_cmd_read(device_priv, NODNIC_INITIALIZING_OFFSET, &buffer);
                MLX_CHECK_STATUS(device_priv, status, read_error, "failed to read initializing");
                if( !CHECK_BIT(buffer, NODNIC_INITIALIZING_BIT)){
                        goto init_done;
                }
                mlx_utils_delay_in_ms(100);
        }
        status = MLX_FAILED;
read_error:
init_done:
        return status;
}
static mlx_status disable_nodnic_inteface ( IN nodnic_device_priv device_priv) [static]

Definition at line 75 of file mlx_device.c.

References buffer, MLX_FATAL_CHECK_STATUS, MLX_SUCCESS, nodnic_cmd_write(), NODNIC_CMDQ_PHY_ADDR_LOW_OFFSET, NODNIC_DISABLE_INTERFACE_BIT, status, and wait_for_device_initialization().

Referenced by nodnic_device_get_nodnic_data(), and nodnic_device_teardown().

{
        mlx_status                      status = MLX_SUCCESS;
        mlx_uint32                      buffer = 0;

        buffer = (1 << NODNIC_DISABLE_INTERFACE_BIT);
        status = nodnic_cmd_write(device_priv, NODNIC_CMDQ_PHY_ADDR_LOW_OFFSET, buffer);
        MLX_FATAL_CHECK_STATUS(status, write_err, "failed to write cmdq_phy_addr + nic_interface");

        status = wait_for_device_initialization(device_priv);
        MLX_FATAL_CHECK_STATUS(status, init_err, "failed to initialize device");
init_err:
write_err:
        return status;
}
static mlx_status nodnic_device_start_nodnic ( IN nodnic_device_priv device_priv) [static]

Definition at line 94 of file mlx_device.c.

References buffer, check_nodnic_interface_supported(), MLX_FATAL_CHECK_STATUS, MLX_SUCCESS, MLX_UNSUPPORTED, nodnic_cmd_write(), NODNIC_NIC_INTERFACE_BIT, NODNIC_NIC_INTERFACE_OFFSET, status, and wait_for_device_initialization().

Referenced by nodnic_device_init().

{
        mlx_status                      status = MLX_SUCCESS;
        mlx_uint32                      buffer = 0;
        mlx_boolean                     nodnic_supported = 0;

        status = wait_for_device_initialization(device_priv);
        MLX_FATAL_CHECK_STATUS(status, wait_for_fw_err, "failed to initialize device");

        status = check_nodnic_interface_supported(device_priv, &nodnic_supported);
        MLX_FATAL_CHECK_STATUS(status, read_err,"failed to check nic_interface_supported");

        if(     nodnic_supported == 0 ){
                status = MLX_UNSUPPORTED;
                goto nodnic_unsupported;
        }
        buffer =  (1 << NODNIC_NIC_INTERFACE_BIT);
        status = nodnic_cmd_write(device_priv, NODNIC_NIC_INTERFACE_OFFSET, buffer);
        MLX_FATAL_CHECK_STATUS(status, write_err, "failed to write cmdq_phy_addr + nic_interface");

        status = wait_for_device_initialization(device_priv);
        MLX_FATAL_CHECK_STATUS(status, init_err, "failed to initialize device");
init_err:
read_err:
write_err:
nodnic_unsupported:
wait_for_fw_err:
        return status;
}
static mlx_status nodnic_device_get_nodnic_data ( IN nodnic_device_priv device_priv) [static]

Definition at line 128 of file mlx_device.c.

References buffer, disable_nodnic_inteface(), MLX_DEBUG_ERROR, MLX_FATAL_CHECK_STATUS, MLX_SUCCESS, MLX_UNSUPPORTED, NODIC_SUPPORTED_REVISION, nodnic_cmd_read(), NODNIC_HARDWARE_FORMAT_OFFSET, NODNIC_LOCATION_OFFSET, NODNIC_REVISION_OFFSET, and status.

Referenced by nodnic_device_init().

{
        mlx_status                      status = MLX_SUCCESS;
        mlx_uint32                      buffer = 0;

        status = nodnic_cmd_read(device_priv, NODNIC_LOCATION_OFFSET, &device_priv->device_offset);
        MLX_FATAL_CHECK_STATUS(status, nodnic_offset_read_err, "failed to read nodnic offset");

        status = nodnic_cmd_read(device_priv,
                        device_priv->device_offset + NODNIC_REVISION_OFFSET, &buffer);
        MLX_FATAL_CHECK_STATUS(status, nodnic_revision_read_err, "failed to read nodnic revision");

        device_priv->nodnic_revision = (buffer >> 24) & 0xFF;
        if( device_priv->nodnic_revision != NODIC_SUPPORTED_REVISION ){
                MLX_DEBUG_ERROR(device_priv, "nodnic revision not supported\n");
                status = MLX_UNSUPPORTED;
                goto unsupported_revision;
        }

        status = nodnic_cmd_read(device_priv,
                        device_priv->device_offset + NODNIC_HARDWARE_FORMAT_OFFSET, &buffer);
        MLX_FATAL_CHECK_STATUS(status, nodnic_hardware_format_read_err, "failed to read nodnic revision");
        device_priv->hardware_format = (buffer >> 16) & 0xFF;

        return status;

unsupported_revision:
nodnic_hardware_format_read_err:
nodnic_offset_read_err:
nodnic_revision_read_err:
        disable_nodnic_inteface(device_priv);
        return status;
}

Definition at line 165 of file mlx_device.c.

References MLX_CHECK_STATUS, mlx_memory_cpu_to_be32(), mlx_pci_mem_read(), mlx_pci_mem_write(), MLX_SUCCESS, MlxPciWidthUint32, nodnic_cmd_write(), NODNIC_NIC_DISABLE_INT_OFFSET, and status.

Referenced by flexboot_nodnic_eth_irq().

{
        mlx_status                      status = MLX_SUCCESS;
        mlx_uint32                      disable = 1;
#ifndef DEVICE_CX3
        status = nodnic_cmd_write(device_priv, NODNIC_NIC_DISABLE_INT_OFFSET, disable);
        MLX_CHECK_STATUS(device_priv, status, clear_int_done, "failed writing to disable_bit");
#else
        mlx_utils *utils = device_priv->utils;
        mlx_uint64 clear_int = (mlx_uintn)(device_priv->crspace_clear_int);
        mlx_uint32 swapped = 0;

        if (device_priv->device_cap.crspace_doorbells == 0) {
                status = nodnic_cmd_write(device_priv, NODNIC_NIC_DISABLE_INT_OFFSET, disable);
                MLX_CHECK_STATUS(device_priv, status, clear_int_done, "failed writing to disable_bit");
        } else {
                /* Write the new index and update FW that new data was submitted */
                disable = 0x80000000;
                mlx_memory_cpu_to_be32(utils, disable, &swapped);
                mlx_pci_mem_write (utils, MlxPciWidthUint32, 0, clear_int, 1, &swapped);
                mlx_pci_mem_read (utils, MlxPciWidthUint32, 0, clear_int, 1, &swapped);
        }
#endif
clear_int_done:
        return status;
}

Definition at line 195 of file mlx_device.c.

References MLX_FATAL_CHECK_STATUS, MLX_INVALID_PARAMETER, MLX_SUCCESS, nodnic_device_get_nodnic_data(), nodnic_device_start_nodnic(), NULL, and status.

Referenced by flexboot_nodnic_probe().

{
        mlx_status                      status = MLX_SUCCESS;

        if( device_priv == NULL ){
                status = MLX_INVALID_PARAMETER;
                goto parm_err;
        }
        status = nodnic_device_start_nodnic(device_priv);
        MLX_FATAL_CHECK_STATUS(status, start_nodnic_err, "nodnic_device_start_nodnic failed");

        status = nodnic_device_get_nodnic_data(device_priv);
        MLX_FATAL_CHECK_STATUS(status, data_err, "nodnic_device_get_nodnic_data failed");
        return status;
data_err:
start_nodnic_err:
parm_err:
        return status;
}

Definition at line 218 of file mlx_device.c.

References disable_nodnic_inteface(), MLX_FATAL_CHECK_STATUS, MLX_SUCCESS, and status.

Referenced by flexboot_nodnic_probe(), and flexboot_nodnic_remove().

{
        mlx_status                      status = MLX_SUCCESS;
        status = disable_nodnic_inteface(device_priv);
        MLX_FATAL_CHECK_STATUS(status, disable_failed, "failed to disable nodnic interface");
disable_failed:
        return status;
}

Definition at line 230 of file mlx_device.c.

References buffer, CHECK_BIT, FALSE, _nodnic_device_capabilites::log_max_ring_size, _nodnic_device_capabilites::log_pkey_table_size, _nodnic_device_capabilites::log_uar_page_size, _nodnic_device_capabilites::log_working_buffer_size, MLX_FATAL_CHECK_STATUS, MLX_INVALID_PARAMETER, MLX_SUCCESS, nodnic_cmd_read(), NODNIC_DEVICE_LOG_MAX_RING_SIZE_MASK, NODNIC_DEVICE_LOG_MAX_RING_SIZE_OFFSET, NODNIC_DEVICE_LOG_PKEY_TABLE_SIZE_MASK, NODNIC_DEVICE_LOG_PKEY_TABLE_SIZE_OFFSET, NODNIC_DEVICE_LOG_UAR_PAGE_SIZE_MASK, NODNIC_DEVICE_LOG_UAR_PAGE_SIZE_OFFSET, NODNIC_DEVICE_LOG_WORKING_BUFFER_SIZE_MASK, NODNIC_DEVICE_LOG_WORKING_BUFFER_SIZE_OFFSET, NODNIC_DEVICE_NUM_PORTS_OFFSET, NODNIC_DEVICE_PD_MASK, NODNIC_DEVICE_SUPPORT_BAR_CQ_CONTROL_OFFSET, NODNIC_DEVICE_SUPPORT_MAC_FILTERS_OFFSET, NODNIC_DEVICE_SUPPORT_PROMISC_FILTER_OFFSET, NODNIC_DEVICE_SUPPORT_PROMISC_MULT_FILTER_OFFSET, NODNIC_DEVICE_SUPPORT_RX_PI_DMA_OFFSET, NODNIC_DEVICE_SUPPORT_UAR_TRX_DB_OFFSET, NULL, _nodnic_device_capabilites::num_ports, status, _nodnic_device_capabilites::support_bar_cq_ctrl, _nodnic_device_capabilites::support_mac_filters, _nodnic_device_capabilites::support_promisc_filter, _nodnic_device_capabilites::support_promisc_multicast_filter, _nodnic_device_capabilites::support_rx_pi_dma, and _nodnic_device_capabilites::support_uar_tx_db.

Referenced by flexboot_nodnic_probe().

{
        mlx_status                                      status = MLX_SUCCESS;
        nodnic_device_capabilites       *device_cap = NULL;
        mlx_uint32                                      buffer = 0;
        mlx_uint64                                      guid_l = 0;
        mlx_uint64                                      guid_h = 0;
        if( device_priv == NULL ){
                status = MLX_INVALID_PARAMETER;
                goto parm_err;
        }

        device_cap = &device_priv->device_cap;

        //get device capabilities
        status = nodnic_cmd_read(device_priv, device_priv->device_offset + 0x0, &buffer);
        MLX_FATAL_CHECK_STATUS(status, read_err, "failed to read nodnic first dword");

#define NODNIC_DEVICE_SUPPORT_MAC_FILTERS_OFFSET 15
#define NODNIC_DEVICE_SUPPORT_PROMISC_FILTER_OFFSET 14
#define NODNIC_DEVICE_SUPPORT_PROMISC_MULT_FILTER_OFFSET 13
#define NODNIC_DEVICE_LOG_WORKING_BUFFER_SIZE_OFFSET 8
#define NODNIC_DEVICE_LOG_WORKING_BUFFER_SIZE_MASK 0x7
#define NODNIC_DEVICE_LOG_PKEY_TABLE_SIZE_OFFSET 4
#define NODNIC_DEVICE_LOG_PKEY_TABLE_SIZE_MASK 0xF
#define NODNIC_DEVICE_NUM_PORTS_OFFSET 0
        device_cap->support_mac_filters = CHECK_BIT(buffer, NODNIC_DEVICE_SUPPORT_MAC_FILTERS_OFFSET);

        device_cap->support_promisc_filter = CHECK_BIT(buffer, NODNIC_DEVICE_SUPPORT_PROMISC_FILTER_OFFSET);

        device_cap->support_promisc_multicast_filter = CHECK_BIT(buffer, NODNIC_DEVICE_SUPPORT_PROMISC_MULT_FILTER_OFFSET);

        device_cap->log_working_buffer_size =
                        (buffer >> NODNIC_DEVICE_LOG_WORKING_BUFFER_SIZE_OFFSET) & NODNIC_DEVICE_LOG_WORKING_BUFFER_SIZE_MASK;

        device_cap->log_pkey_table_size =
                        (buffer >> NODNIC_DEVICE_LOG_PKEY_TABLE_SIZE_OFFSET) & NODNIC_DEVICE_LOG_PKEY_TABLE_SIZE_MASK;

        device_cap->num_ports = CHECK_BIT(buffer, NODNIC_DEVICE_NUM_PORTS_OFFSET) + 1;

#ifdef DEVICE_CX3
#define NODNIC_DEVICE_CRSPACE_DB_OFFSET 12
        device_cap->crspace_doorbells = CHECK_BIT(buffer, NODNIC_DEVICE_CRSPACE_DB_OFFSET);
#endif

        status = nodnic_cmd_read(device_priv, device_priv->device_offset + 0x4, &buffer);
        MLX_FATAL_CHECK_STATUS(status, read_err, "failed to read nodnic second dword");

#define NODNIC_DEVICE_LOG_MAX_RING_SIZE_OFFSET 24
#define NODNIC_DEVICE_LOG_MAX_RING_SIZE_MASK 0x3F
#define NODNIC_DEVICE_PD_MASK 0xFFFFFF
        device_cap->log_max_ring_size =
                        (buffer >> NODNIC_DEVICE_LOG_MAX_RING_SIZE_OFFSET) & NODNIC_DEVICE_LOG_MAX_RING_SIZE_MASK;

        //get device magic numbers
        device_priv->pd = buffer & NODNIC_DEVICE_PD_MASK;

        status = nodnic_cmd_read(device_priv, device_priv->device_offset + 0x8, &buffer);
        MLX_FATAL_CHECK_STATUS(status, read_err, "failed to read nodnic third dword");
        device_priv->lkey = buffer;

#ifdef DEVICE_CX3
        if ( device_cap->crspace_doorbells ) {
                status = nodnic_cmd_read(device_priv, device_priv->device_offset + 0x18, &buffer);
                MLX_FATAL_CHECK_STATUS(status, read_err, "failed to read nodnic_crspace_clear_int address");
                device_priv->crspace_clear_int = device_priv->utils->config + buffer;
        }
#endif

        status = nodnic_cmd_read(device_priv, device_priv->device_offset + 0x10, (mlx_uint32*)&guid_h);
        MLX_FATAL_CHECK_STATUS(status, read_err, "failed to read nodnic guid_h");
        status = nodnic_cmd_read(device_priv, device_priv->device_offset + 0x14, (mlx_uint32*)&guid_l);
        MLX_FATAL_CHECK_STATUS(status, read_err, "failed to read nodnic guid_l");
        device_priv->device_guid = guid_l | (guid_h << 32);

#define NODNIC_DEVICE_SUPPORT_RX_PI_DMA_OFFSET 31
#define NODNIC_DEVICE_SUPPORT_RX_PI_DMA_MASK 0x1
#define NODNIC_DEVICE_SUPPORT_UAR_TRX_DB_OFFSET 29
#define NODNIC_DEVICE_SUPPORT_UAR_TRX_DB_MASK 0x1
#define NODNIC_DEVICE_SUPPORT_BAR_CQ_CONTROL_OFFSET 27
#define NODNIC_DEVICE_SUPPORT_BAR_CQ_CONTROL_MASK 0x1
        status = nodnic_cmd_read(device_priv, device_priv->device_offset + 0x1c, &buffer);
        MLX_FATAL_CHECK_STATUS(status, read_err, "failed to read nodnic support_rx_pi_dma");
        if ( sizeof ( mlx_uintn ) == sizeof ( mlx_uint32 ) ) {
                device_cap->support_rx_pi_dma = FALSE;
                device_cap->support_uar_tx_db = FALSE;
                device_cap->support_bar_cq_ctrl = FALSE;
        } else {
                device_cap->support_rx_pi_dma = CHECK_BIT(buffer, NODNIC_DEVICE_SUPPORT_RX_PI_DMA_OFFSET);
                device_cap->support_uar_tx_db = CHECK_BIT(buffer, NODNIC_DEVICE_SUPPORT_UAR_TRX_DB_OFFSET);
                device_cap->support_bar_cq_ctrl = CHECK_BIT(buffer, NODNIC_DEVICE_SUPPORT_BAR_CQ_CONTROL_OFFSET);
        }

#define NODNIC_DEVICE_LOG_UAR_PAGE_SIZE_OFFSET 0
#define NODNIC_DEVICE_LOG_UAR_PAGE_SIZE_MASK 0xFF
        status = nodnic_cmd_read(device_priv, device_priv->device_offset + 0x20, &buffer);
        MLX_FATAL_CHECK_STATUS(status, read_err, "failed to read nodnic log_uar_page_size");
        device_cap->log_uar_page_size = ( buffer >> NODNIC_DEVICE_LOG_UAR_PAGE_SIZE_OFFSET) & NODNIC_DEVICE_LOG_UAR_PAGE_SIZE_MASK;
read_err:
parm_err:
        return status;
}
mlx_status nodnic_device_get_fw_version ( IN nodnic_device_priv device_priv,
OUT mlx_uint16 fw_ver_minor,
OUT mlx_uint16 fw_ver_sub_minor,
OUT mlx_uint16 fw_ver_major 
)

Definition at line 336 of file mlx_device.c.

References buffer, MLX_CHECK_STATUS, MLX_INVALID_PARAMETER, MLX_SUCCESS, nodnic_cmd_read(), NULL, and status.

                                 {
        mlx_status              status = MLX_SUCCESS;
        mlx_uint32              buffer = 0;

        if( device_priv == NULL ){
                status = MLX_INVALID_PARAMETER;
                goto parm_err;
        }

        status = nodnic_cmd_read(device_priv, 0x0, &buffer);
        MLX_CHECK_STATUS(device_priv, status, read_err, "failed to read fw revision major and minor");

        *fw_ver_minor = (mlx_uint16)(buffer >> 16);
        *fw_ver_major = (mlx_uint16)buffer;

        status = nodnic_cmd_read(device_priv, 0x4, &buffer);
        MLX_CHECK_STATUS(device_priv, status, read_err, "failed to read fw revision sub minor");

        *fw_ver_sub_minor = (mlx_uint16)buffer;
read_err:
parm_err:
        return status;
}