/*
 * Copyright (c) 2018-2019 Cavium, Inc.
 * All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions
 *  are met:
 *
 *  1. Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *  2. Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */


#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");

#include "bcm_osal.h"
#include "ecore.h"
#include "ecore_status.h"
#include "ecore_mcp.h"
#include "ecore_hw.h"
#include "reg_addr.h"

#define TLV_TYPE(p)	(p[0])
#define TLV_LENGTH(p)	(p[1])
#define TLV_FLAGS(p)	(p[3])

#define ECORE_TLV_DATA_MAX (14)
struct ecore_tlv_parsed_buf {
	/* To be filled with the address to set in Value field */
	u8 *p_val;

	/* To be used internally in case the value has to be modified */
	u8 data[ECORE_TLV_DATA_MAX];
};

static enum _ecore_status_t
ecore_mfw_get_tlv_group(u8 tlv_type, u8 *tlv_group)
{
	switch(tlv_type) {
	case DRV_TLV_FEATURE_FLAGS:
	case DRV_TLV_LOCAL_ADMIN_ADDR:
	case DRV_TLV_ADDITIONAL_MAC_ADDR_1:
	case DRV_TLV_ADDITIONAL_MAC_ADDR_2:
	case DRV_TLV_OS_DRIVER_STATES:
	case DRV_TLV_PXE_BOOT_PROGRESS:
	case DRV_TLV_RX_FRAMES_RECEIVED:
	case DRV_TLV_RX_BYTES_RECEIVED:
	case DRV_TLV_TX_FRAMES_SENT:
	case DRV_TLV_TX_BYTES_SENT:
	case DRV_TLV_NPIV_ENABLED:
	case DRV_TLV_PCIE_BUS_RX_UTILIZATION:
	case DRV_TLV_PCIE_BUS_TX_UTILIZATION:
	case DRV_TLV_DEVICE_CPU_CORES_UTILIZATION:
	case DRV_TLV_LAST_VALID_DCC_TLV_RECEIVED:
	case DRV_TLV_NCSI_RX_BYTES_RECEIVED:
    case DRV_TLV_NCSI_TX_BYTES_SENT:
		*tlv_group |= ECORE_MFW_TLV_GENERIC;
		break;
	case DRV_TLV_LSO_MAX_OFFLOAD_SIZE:
	case DRV_TLV_LSO_MIN_SEGMENT_COUNT:
	case DRV_TLV_PROMISCUOUS_MODE:
	case DRV_TLV_TX_DESCRIPTORS_QUEUE_SIZE:
	case DRV_TLV_RX_DESCRIPTORS_QUEUE_SIZE:
	case DRV_TLV_NUM_OF_NET_QUEUE_VMQ_CFG:
	case DRV_TLV_NUM_OFFLOADED_CONNECTIONS_TCP_IPV4:
	case DRV_TLV_NUM_OFFLOADED_CONNECTIONS_TCP_IPV6:
	case DRV_TLV_TX_DESCRIPTOR_QUEUE_AVG_DEPTH:
	case DRV_TLV_RX_DESCRIPTORS_QUEUE_AVG_DEPTH:
	case DRV_TLV_IOV_OFFLOAD:
	case DRV_TLV_TX_QUEUES_EMPTY:
	case DRV_TLV_RX_QUEUES_EMPTY:
	case DRV_TLV_TX_QUEUES_FULL:
	case DRV_TLV_RX_QUEUES_FULL:
		*tlv_group |= ECORE_MFW_TLV_ETH;
		break;
	case DRV_TLV_SCSI_TO:
	case DRV_TLV_R_T_TOV:
	case DRV_TLV_R_A_TOV:
	case DRV_TLV_E_D_TOV:
	case DRV_TLV_CR_TOV:
	case DRV_TLV_BOOT_TYPE:
	case DRV_TLV_NPIV_STATE:
	case DRV_TLV_NUM_OF_NPIV_IDS:
	case DRV_TLV_SWITCH_NAME:
	case DRV_TLV_SWITCH_PORT_NUM:
	case DRV_TLV_SWITCH_PORT_ID:
	case DRV_TLV_VENDOR_NAME:
	case DRV_TLV_SWITCH_MODEL:
	case DRV_TLV_SWITCH_FW_VER:
	case DRV_TLV_QOS_PRIORITY_PER_802_1P:
	case DRV_TLV_PORT_ALIAS:
	case DRV_TLV_PORT_STATE:
	case DRV_TLV_FIP_TX_DESCRIPTORS_QUEUE_SIZE:
	case DRV_TLV_FCOE_RX_DESCRIPTORS_QUEUE_SIZE:
	case DRV_TLV_LINK_FAILURE_COUNT:
	case DRV_TLV_FCOE_BOOT_PROGRESS:
	case DRV_TLV_RX_BROADCAST_PACKETS:
	case DRV_TLV_TX_BROADCAST_PACKETS:
	case DRV_TLV_FCOE_TX_DESCRIPTOR_QUEUE_AVG_DEPTH:
	case DRV_TLV_FCOE_RX_DESCRIPTORS_QUEUE_AVG_DEPTH:
	case DRV_TLV_FCOE_RX_FRAMES_RECEIVED:
	case DRV_TLV_FCOE_RX_BYTES_RECEIVED:
	case DRV_TLV_FCOE_TX_FRAMES_SENT:
	case DRV_TLV_FCOE_TX_BYTES_SENT:
	case DRV_TLV_CRC_ERROR_COUNT:
	case DRV_TLV_CRC_ERROR_1_RECEIVED_SOURCE_FC_ID:
	case DRV_TLV_CRC_ERROR_1_TIMESTAMP:
	case DRV_TLV_CRC_ERROR_2_RECEIVED_SOURCE_FC_ID:
	case DRV_TLV_CRC_ERROR_2_TIMESTAMP:
	case DRV_TLV_CRC_ERROR_3_RECEIVED_SOURCE_FC_ID:
	case DRV_TLV_CRC_ERROR_3_TIMESTAMP:
	case DRV_TLV_CRC_ERROR_4_RECEIVED_SOURCE_FC_ID:
	case DRV_TLV_CRC_ERROR_4_TIMESTAMP:
	case DRV_TLV_CRC_ERROR_5_RECEIVED_SOURCE_FC_ID:
	case DRV_TLV_CRC_ERROR_5_TIMESTAMP:
	case DRV_TLV_LOSS_OF_SYNC_ERROR_COUNT:
	case DRV_TLV_LOSS_OF_SIGNAL_ERRORS:
	case DRV_TLV_PRIMITIVE_SEQUENCE_PROTOCOL_ERROR_COUNT:
	case DRV_TLV_DISPARITY_ERROR_COUNT:
	case DRV_TLV_CODE_VIOLATION_ERROR_COUNT:
	case DRV_TLV_LAST_FLOGI_ISSUED_COMMON_PARAMETERS_WORD_1:
	case DRV_TLV_LAST_FLOGI_ISSUED_COMMON_PARAMETERS_WORD_2:
	case DRV_TLV_LAST_FLOGI_ISSUED_COMMON_PARAMETERS_WORD_3:
	case DRV_TLV_LAST_FLOGI_ISSUED_COMMON_PARAMETERS_WORD_4:
	case DRV_TLV_LAST_FLOGI_TIMESTAMP:
	case DRV_TLV_LAST_FLOGI_ACC_COMMON_PARAMETERS_WORD_1:
	case DRV_TLV_LAST_FLOGI_ACC_COMMON_PARAMETERS_WORD_2:
	case DRV_TLV_LAST_FLOGI_ACC_COMMON_PARAMETERS_WORD_3:
	case DRV_TLV_LAST_FLOGI_ACC_COMMON_PARAMETERS_WORD_4:
	case DRV_TLV_LAST_FLOGI_ACC_TIMESTAMP:
	case DRV_TLV_LAST_FLOGI_RJT:
	case DRV_TLV_LAST_FLOGI_RJT_TIMESTAMP:
	case DRV_TLV_FDISCS_SENT_COUNT:
	case DRV_TLV_FDISC_ACCS_RECEIVED:
	case DRV_TLV_FDISC_RJTS_RECEIVED:
	case DRV_TLV_PLOGI_SENT_COUNT:
	case DRV_TLV_PLOGI_ACCS_RECEIVED:
	case DRV_TLV_PLOGI_RJTS_RECEIVED:
	case DRV_TLV_PLOGI_1_SENT_DESTINATION_FC_ID:
	case DRV_TLV_PLOGI_1_TIMESTAMP:
	case DRV_TLV_PLOGI_2_SENT_DESTINATION_FC_ID:
	case DRV_TLV_PLOGI_2_TIMESTAMP:
	case DRV_TLV_PLOGI_3_SENT_DESTINATION_FC_ID:
	case DRV_TLV_PLOGI_3_TIMESTAMP:
	case DRV_TLV_PLOGI_4_SENT_DESTINATION_FC_ID:
	case DRV_TLV_PLOGI_4_TIMESTAMP:
	case DRV_TLV_PLOGI_5_SENT_DESTINATION_FC_ID:
	case DRV_TLV_PLOGI_5_TIMESTAMP:
	case DRV_TLV_PLOGI_1_ACC_RECEIVED_SOURCE_FC_ID:
	case DRV_TLV_PLOGI_1_ACC_TIMESTAMP:
	case DRV_TLV_PLOGI_2_ACC_RECEIVED_SOURCE_FC_ID:
	case DRV_TLV_PLOGI_2_ACC_TIMESTAMP:
	case DRV_TLV_PLOGI_3_ACC_RECEIVED_SOURCE_FC_ID:
	case DRV_TLV_PLOGI_3_ACC_TIMESTAMP:
	case DRV_TLV_PLOGI_4_ACC_RECEIVED_SOURCE_FC_ID:
	case DRV_TLV_PLOGI_4_ACC_TIMESTAMP:
	case DRV_TLV_PLOGI_5_ACC_RECEIVED_SOURCE_FC_ID:
	case DRV_TLV_PLOGI_5_ACC_TIMESTAMP:
	case DRV_TLV_LOGOS_ISSUED:
	case DRV_TLV_LOGO_ACCS_RECEIVED:
	case DRV_TLV_LOGO_RJTS_RECEIVED:
	case DRV_TLV_LOGO_1_RECEIVED_SOURCE_FC_ID:
	case DRV_TLV_LOGO_1_TIMESTAMP:
	case DRV_TLV_LOGO_2_RECEIVED_SOURCE_FC_ID:
	case DRV_TLV_LOGO_2_TIMESTAMP:
	case DRV_TLV_LOGO_3_RECEIVED_SOURCE_FC_ID:
	case DRV_TLV_LOGO_3_TIMESTAMP:
	case DRV_TLV_LOGO_4_RECEIVED_SOURCE_FC_ID:
	case DRV_TLV_LOGO_4_TIMESTAMP:
	case DRV_TLV_LOGO_5_RECEIVED_SOURCE_FC_ID:
	case DRV_TLV_LOGO_5_TIMESTAMP:
	case DRV_TLV_LOGOS_RECEIVED:
	case DRV_TLV_ACCS_ISSUED:
	case DRV_TLV_PRLIS_ISSUED:
	case DRV_TLV_ACCS_RECEIVED:
	case DRV_TLV_ABTS_SENT_COUNT:
	case DRV_TLV_ABTS_ACCS_RECEIVED:
	case DRV_TLV_ABTS_RJTS_RECEIVED:
	case DRV_TLV_ABTS_1_SENT_DESTINATION_FC_ID:
	case DRV_TLV_ABTS_1_TIMESTAMP:
	case DRV_TLV_ABTS_2_SENT_DESTINATION_FC_ID:
	case DRV_TLV_ABTS_2_TIMESTAMP:
	case DRV_TLV_ABTS_3_SENT_DESTINATION_FC_ID:
	case DRV_TLV_ABTS_3_TIMESTAMP:
	case DRV_TLV_ABTS_4_SENT_DESTINATION_FC_ID:
	case DRV_TLV_ABTS_4_TIMESTAMP:
	case DRV_TLV_ABTS_5_SENT_DESTINATION_FC_ID:
	case DRV_TLV_ABTS_5_TIMESTAMP:
	case DRV_TLV_RSCNS_RECEIVED:
	case DRV_TLV_LAST_RSCN_RECEIVED_N_PORT_1:
	case DRV_TLV_LAST_RSCN_RECEIVED_N_PORT_2:
	case DRV_TLV_LAST_RSCN_RECEIVED_N_PORT_3:
	case DRV_TLV_LAST_RSCN_RECEIVED_N_PORT_4:
	case DRV_TLV_LUN_RESETS_ISSUED:
	case DRV_TLV_ABORT_TASK_SETS_ISSUED:
	case DRV_TLV_TPRLOS_SENT:
	case DRV_TLV_NOS_SENT_COUNT:
	case DRV_TLV_NOS_RECEIVED_COUNT:
	case DRV_TLV_OLS_COUNT:
	case DRV_TLV_LR_COUNT:
	case DRV_TLV_LRR_COUNT:
	case DRV_TLV_LIP_SENT_COUNT:
	case DRV_TLV_LIP_RECEIVED_COUNT:
	case DRV_TLV_EOFA_COUNT:
	case DRV_TLV_EOFNI_COUNT:
	case DRV_TLV_SCSI_STATUS_CHECK_CONDITION_COUNT:
	case DRV_TLV_SCSI_STATUS_CONDITION_MET_COUNT:
	case DRV_TLV_SCSI_STATUS_BUSY_COUNT:
	case DRV_TLV_SCSI_STATUS_INTERMEDIATE_COUNT:
	case DRV_TLV_SCSI_STATUS_INTERMEDIATE_CONDITION_MET_COUNT:
	case DRV_TLV_SCSI_STATUS_RESERVATION_CONFLICT_COUNT:
	case DRV_TLV_SCSI_STATUS_TASK_SET_FULL_COUNT:
	case DRV_TLV_SCSI_STATUS_ACA_ACTIVE_COUNT:
	case DRV_TLV_SCSI_STATUS_TASK_ABORTED_COUNT:
	case DRV_TLV_SCSI_CHECK_CONDITION_1_RECEIVED_SK_ASC_ASCQ:
	case DRV_TLV_SCSI_CHECK_1_TIMESTAMP:
	case DRV_TLV_SCSI_CHECK_CONDITION_2_RECEIVED_SK_ASC_ASCQ:
	case DRV_TLV_SCSI_CHECK_2_TIMESTAMP:
	case DRV_TLV_SCSI_CHECK_CONDITION_3_RECEIVED_SK_ASC_ASCQ:
	case DRV_TLV_SCSI_CHECK_3_TIMESTAMP:
	case DRV_TLV_SCSI_CHECK_CONDITION_4_RECEIVED_SK_ASC_ASCQ:
	case DRV_TLV_SCSI_CHECK_4_TIMESTAMP:
	case DRV_TLV_SCSI_CHECK_CONDITION_5_RECEIVED_SK_ASC_ASCQ:
	case DRV_TLV_SCSI_CHECK_5_TIMESTAMP:
		*tlv_group = ECORE_MFW_TLV_FCOE;
		break;
	case DRV_TLV_TARGET_LLMNR_ENABLED:
	case DRV_TLV_HEADER_DIGEST_FLAG_ENABLED:
	case DRV_TLV_DATA_DIGEST_FLAG_ENABLED:
	case DRV_TLV_AUTHENTICATION_METHOD:
	case DRV_TLV_ISCSI_BOOT_TARGET_PORTAL:
	case DRV_TLV_MAX_FRAME_SIZE:
	case DRV_TLV_PDU_TX_DESCRIPTORS_QUEUE_SIZE:
	case DRV_TLV_PDU_RX_DESCRIPTORS_QUEUE_SIZE:
	case DRV_TLV_ISCSI_BOOT_PROGRESS:
	case DRV_TLV_PDU_TX_DESCRIPTOR_QUEUE_AVG_DEPTH:
	case DRV_TLV_PDU_RX_DESCRIPTORS_QUEUE_AVG_DEPTH:
	case DRV_TLV_ISCSI_PDU_RX_FRAMES_RECEIVED:
	case DRV_TLV_ISCSI_PDU_RX_BYTES_RECEIVED:
	case DRV_TLV_ISCSI_PDU_TX_FRAMES_SENT:
	case DRV_TLV_ISCSI_PDU_TX_BYTES_SENT:
		*tlv_group |= ECORE_MFW_TLV_ISCSI;
		break;
	default:
		return ECORE_INVAL;
	}

	return ECORE_SUCCESS;
}

static int
ecore_mfw_get_gen_tlv_value(struct ecore_drv_tlv_hdr *p_tlv,
			    struct ecore_mfw_tlv_generic *p_drv_buf,
			    struct ecore_tlv_parsed_buf *p_buf)
{
	switch (p_tlv->tlv_type) {
	case DRV_TLV_FEATURE_FLAGS:
		if (p_drv_buf->flags.b_set) {
			OSAL_MEM_ZERO(p_buf->data,
				      sizeof(u8) * ECORE_TLV_DATA_MAX);
			p_buf->data[0] = p_drv_buf->flags.ipv4_csum_offload ?
					 1 : 0;
			p_buf->data[0] |= (p_drv_buf->flags.lso_supported ?
					   1 : 0) << 1;
			p_buf->p_val = p_buf->data;
			return 2;
		}
		break;

	case DRV_TLV_LOCAL_ADMIN_ADDR:
	case DRV_TLV_ADDITIONAL_MAC_ADDR_1:
	case DRV_TLV_ADDITIONAL_MAC_ADDR_2:
	{
		int idx = p_tlv->tlv_type - DRV_TLV_LOCAL_ADMIN_ADDR;

		if (p_drv_buf->mac_set[idx]) {
			p_buf->p_val = p_drv_buf->mac[idx];
			return 6;
		}
		break;
	}

	case DRV_TLV_RX_FRAMES_RECEIVED:
		if (p_drv_buf->rx_frames_set) {
			p_buf->p_val = (u8 *)&p_drv_buf->rx_frames;
			return sizeof(p_drv_buf->rx_frames);
		}
		break;
	case DRV_TLV_RX_BYTES_RECEIVED:
		if (p_drv_buf->rx_bytes_set) {
			p_buf->p_val = (u8 *)&p_drv_buf->rx_bytes;
			return sizeof(p_drv_buf->rx_bytes);
		}
		break;
	case DRV_TLV_TX_FRAMES_SENT:
		if (p_drv_buf->tx_frames_set) {
			p_buf->p_val = (u8 *)&p_drv_buf->tx_frames;
			return sizeof(p_drv_buf->tx_frames);
		}
		break;
	case DRV_TLV_TX_BYTES_SENT:
		if (p_drv_buf->tx_bytes_set) {
			p_buf->p_val = (u8 *)&p_drv_buf->tx_bytes;
			return sizeof(p_drv_buf->tx_bytes);
		}
		break;
	default:
		break;
	}

	return -1;
}

static int
ecore_mfw_get_eth_tlv_value(struct ecore_drv_tlv_hdr *p_tlv,
			    struct ecore_mfw_tlv_eth *p_drv_buf,
			    struct ecore_tlv_parsed_buf *p_buf)
{
	switch (p_tlv->tlv_type) {
	case DRV_TLV_LSO_MAX_OFFLOAD_SIZE:
		if (p_drv_buf->lso_maxoff_size_set) {
			p_buf->p_val = (u8 *)&p_drv_buf->lso_maxoff_size;
			return sizeof(p_drv_buf->lso_maxoff_size);
		}
		break;
	case DRV_TLV_LSO_MIN_SEGMENT_COUNT:
		if (p_drv_buf->lso_minseg_size_set) {
			p_buf->p_val = (u8 *)&p_drv_buf->lso_minseg_size;
			return sizeof(p_drv_buf->lso_minseg_size);
		}
		break;
	case DRV_TLV_PROMISCUOUS_MODE:
		if (p_drv_buf->prom_mode_set) {
			p_buf->p_val = (u8 *)&p_drv_buf->prom_mode;
			return sizeof(p_drv_buf->prom_mode);
		}
		break;
	case DRV_TLV_TX_DESCRIPTORS_QUEUE_SIZE:
		if (p_drv_buf->tx_descr_size_set) {
			p_buf->p_val = (u8 *)&p_drv_buf->tx_descr_size;
			return sizeof(p_drv_buf->tx_descr_size);
		}
		break;
	case DRV_TLV_RX_DESCRIPTORS_QUEUE_SIZE:
		if (p_drv_buf->rx_descr_size_set) {
			p_buf->p_val = (u8 *)&p_drv_buf->rx_descr_size;
			return sizeof(p_drv_buf->rx_descr_size);
		}
		break;
	case DRV_TLV_NUM_OF_NET_QUEUE_VMQ_CFG:
		if (p_drv_buf->netq_count_set) {
			p_buf->p_val = (u8 *)&p_drv_buf->netq_count;
			return sizeof(p_drv_buf->netq_count);
		}
		break;
	case DRV_TLV_NUM_OFFLOADED_CONNECTIONS_TCP_IPV4:
		if (p_drv_buf->tcp4_offloads_set) {
			p_buf->p_val = (u8 *)&p_drv_buf->tcp4_offloads;
			return sizeof(p_drv_buf->tcp4_offloads);
		}
		break;
	case DRV_TLV_NUM_OFFLOADED_CONNECTIONS_TCP_IPV6:
		if (p_drv_buf->tcp6_offloads_set) {
			p_buf->p_val = (u8 *)&p_drv_buf->tcp6_offloads;
			return sizeof(p_drv_buf->tcp6_offloads);
		}
		break;
	case DRV_TLV_TX_DESCRIPTOR_QUEUE_AVG_DEPTH:
		if (p_drv_buf->tx_descr_qdepth_set) {
			p_buf->p_val = (u8 *)&p_drv_buf->tx_descr_qdepth;
			return sizeof(p_drv_buf->tx_descr_qdepth);
		}
		break;
	case DRV_TLV_RX_DESCRIPTORS_QUEUE_AVG_DEPTH:
		if (p_drv_buf->rx_descr_qdepth_set) {
			p_buf->p_val = (u8 *)&p_drv_buf->rx_descr_qdepth;
			return sizeof(p_drv_buf->rx_descr_qdepth);
		}
		break;
	case DRV_TLV_IOV_OFFLOAD:
		if (p_drv_buf->iov_offload_set) {
			p_buf->p_val = (u8 *)&p_drv_buf->iov_offload;
			return sizeof(p_drv_buf->iov_offload);
		}
		break;
	case DRV_TLV_TX_QUEUES_EMPTY:
		if (p_drv_buf->txqs_empty_set) {
			p_buf->p_val = (u8 *)&p_drv_buf->txqs_empty;
			return sizeof(p_drv_buf->txqs_empty);
		}
		break;
	case DRV_TLV_RX_QUEUES_EMPTY:
		if (p_drv_buf->rxqs_empty_set) {
			p_buf->p_val = (u8 *)&p_drv_buf->rxqs_empty;
			return sizeof(p_drv_buf->rxqs_empty);
		}
		break;
	case DRV_TLV_TX_QUEUES_FULL:
		if (p_drv_buf->num_txqs_full_set) {
			p_buf->p_val = (u8 *)&p_drv_buf->num_txqs_full;
			return sizeof(p_drv_buf->num_txqs_full);
		}
		break;
	case DRV_TLV_RX_QUEUES_FULL:
		if (p_drv_buf->num_rxqs_full_set) {
			p_buf->p_val = (u8 *)&p_drv_buf->num_rxqs_full;
			return sizeof(p_drv_buf->num_rxqs_full);
		}
		break;
	default:
		break;
	}

	return -1;
}

static int
ecore_mfw_get_tlv_time_value(struct ecore_mfw_tlv_time *p_time,
			     struct ecore_tlv_parsed_buf *p_buf)
{
	if (!p_time->b_set)
		return -1;

	/* Validate numbers */
	if (p_time->month > 12)
		p_time->month = 0;
	if (p_time->day > 31)
		p_time->day = 0;
	if (p_time->hour > 23)
		p_time->hour = 0;
	if (p_time->min > 59)
		p_time->hour = 0;
	if (p_time->msec > 999)
		p_time->msec = 0;
	if (p_time->usec > 999)
		p_time->usec = 0;

	OSAL_MEM_ZERO(p_buf->data, sizeof(u8) * ECORE_TLV_DATA_MAX);
	OSAL_SNPRINTF(p_buf->data, 14, "%d%d%d%d%d%d",
		      p_time->month, p_time->day,
		      p_time->hour, p_time->min,
		      p_time->msec, p_time->usec);

	p_buf->p_val = p_buf->data;
	return 14;
}

static int
ecore_mfw_get_fcoe_tlv_value(struct ecore_drv_tlv_hdr *p_tlv,
			     struct ecore_mfw_tlv_fcoe *p_drv_buf,
			     struct ecore_tlv_parsed_buf *p_buf)
{
	switch (p_tlv->tlv_type) {
	case DRV_TLV_SCSI_TO:
		if (p_drv_buf->scsi_timeout_set) {
			p_buf->p_val = (u8 *)&p_drv_buf->scsi_timeout;
			return sizeof(p_drv_buf->scsi_timeout);
		}
		break;
	case DRV_TLV_R_T_TOV:
		if (p_drv_buf->rt_tov_set) {
			p_buf->p_val = (u8 *)&p_drv_buf->rt_tov;
			return sizeof(p_drv_buf->rt_tov);
		}
		break;
	case DRV_TLV_R_A_TOV:
		if (p_drv_buf->ra_tov_set) {
			p_buf->p_val = (u8 *)&p_drv_buf->ra_tov;
			return sizeof(p_drv_buf->ra_tov);
		}
		break;
	case DRV_TLV_E_D_TOV:
		if (p_drv_buf->ed_tov_set) {
			p_buf->p_val = (u8 *)&p_drv_buf->ed_tov;
			return sizeof(p_drv_buf->ed_tov);
		}
		break;
	case DRV_TLV_CR_TOV:
		if (p_drv_buf->cr_tov_set) {
			p_buf->p_val = (u8 *)&p_drv_buf->cr_tov;
			return sizeof(p_drv_buf->cr_tov);
		}
		break;
	case DRV_TLV_BOOT_TYPE:
		if (p_drv_buf->boot_type_set) {
			p_buf->p_val = (u8 *)&p_drv_buf->boot_type;
			return sizeof(p_drv_buf->boot_type);
		}
		break;
	case DRV_TLV_NPIV_STATE:
		if (p_drv_buf->npiv_state_set) {
			p_buf->p_val = (u8 *)&p_drv_buf->npiv_state;
			return sizeof(p_drv_buf->npiv_state);
		}
		break;
	case DRV_TLV_NUM_OF_NPIV_IDS:
		if (p_drv_buf->num_npiv_ids_set) {
			p_buf->p_val = (u8 *)&p_drv_buf->num_npiv_ids;
			return sizeof(p_drv_buf->num_npiv_ids);
		}
		break;
	case DRV_TLV_SWITCH_NAME:
		if (p_drv_buf->switch_name_set) {
			p_buf->p_val = (u8 *)&p_drv_buf->switch_name;
			return sizeof(p_drv_buf->switch_name);
		}
		break;
	case DRV_TLV_SWITCH_PORT_NUM:
		if (p_drv_buf->switch_portnum_set) {
			p_buf->p_val = (u8 *)&p_drv_buf->switch_portnum;
			return sizeof(p_drv_buf->switch_portnum);
		}
		break;
	case DRV_TLV_SWITCH_PORT_ID:
		if (p_drv_buf->switch_portid_set) {
			p_buf->p_val = (u8 *)&p_drv_buf->switch_portid;
			return sizeof(p_drv_buf->switch_portid);
		}
		break;
	case DRV_TLV_VENDOR_NAME:
		if (p_drv_buf->vendor_name_set) {
			p_buf->p_val = (u8 *)&p_drv_buf->vendor_name;
			return sizeof(p_drv_buf->vendor_name);
		}
		break;
	case DRV_TLV_SWITCH_MODEL:
		if (p_drv_buf->switch_model_set) {
			p_buf->p_val = (u8 *)&p_drv_buf->switch_model;
			return sizeof(p_drv_buf->switch_model);
		}
		break;
	case DRV_TLV_SWITCH_FW_VER:
		if (p_drv_buf->switch_fw_version_set) {
			p_buf->p_val = (u8 *)&p_drv_buf->switch_fw_version;
			return sizeof(p_drv_buf->switch_fw_version);
		}
		break;
	case DRV_TLV_QOS_PRIORITY_PER_802_1P:
		if (p_drv_buf->qos_pri_set) {
			p_buf->p_val = (u8 *)&p_drv_buf->qos_pri;
			return sizeof(p_drv_buf->qos_pri);
		}
		break;
	case DRV_TLV_PORT_ALIAS:
		if (p_drv_buf->port_alias_set) {
			p_buf->p_val = (u8 *)&p_drv_buf->port_alias;
			return sizeof(p_drv_buf->port_alias);
		}
		break;
	case DRV_TLV_PORT_STATE:
		if (p_drv_buf->port_state_set) {
			p_buf->p_val = (u8 *)&p_drv_buf->port_state;
			return sizeof(p_drv_buf->port_state);
		}
		break;
	case DRV_TLV_FIP_TX_DESCRIPTORS_QUEUE_SIZE:
		if (p_drv_buf->fip_tx_descr_size_set) {
			p_buf->p_val = (u8 *)&p_drv_buf->fip_tx_descr_size;
			return sizeof(p_drv_buf->fip_tx_descr_size);
		}
		break;
	case DRV_TLV_FCOE_RX_DESCRIPTORS_QUEUE_SIZE:
		if (p_drv_buf->fip_rx_descr_size_set) {
			p_buf->p_val = (u8 *)&p_drv_buf->fip_rx_descr_size;
			return sizeof(p_drv_buf->fip_rx_descr_size);
		}
		break;
	case DRV_TLV_LINK_FAILURE_COUNT:
		if (p_drv_buf->link_failures_set) {
			p_buf->p_val = (u8 *)&p_drv_buf->link_failures;
			return sizeof(p_drv_buf->link_failures);
		}
		break;
	case DRV_TLV_FCOE_BOOT_PROGRESS:
		if (p_drv_buf->fcoe_boot_progress_set) {
			p_buf->p_val = (u8 *)&p_drv_buf->fcoe_boot_progress;
			return sizeof(p_drv_buf->fcoe_boot_progress);
		}
		break;
	case DRV_TLV_RX_BROADCAST_PACKETS:
		if (p_drv_buf->rx_bcast_set) {
			p_buf->p_val = (u8 *)&p_drv_buf->rx_bcast;
			return sizeof(p_drv_buf->rx_bcast);
		}
		break;
	case DRV_TLV_TX_BROADCAST_PACKETS:
		if (p_drv_buf->tx_bcast_set) {
			p_buf->p_val = (u8 *)&p_drv_buf->tx_bcast;
			return sizeof(p_drv_buf->tx_bcast);
		}
		break;
	case DRV_TLV_FCOE_TX_DESCRIPTOR_QUEUE_AVG_DEPTH:
		if (p_drv_buf->fcoe_txq_depth_set) {
			p_buf->p_val = (u8 *)&p_drv_buf->fcoe_txq_depth;
			return sizeof(p_drv_buf->fcoe_txq_depth);
		}
		break;
	case DRV_TLV_FCOE_RX_DESCRIPTORS_QUEUE_AVG_DEPTH:
		if (p_drv_buf->fcoe_rxq_depth_set) {
			p_buf->p_val = (u8 *)&p_drv_buf->fcoe_rxq_depth;
			return sizeof(p_drv_buf->fcoe_rxq_depth);
		}
		break;
	case DRV_TLV_FCOE_RX_FRAMES_RECEIVED:
		if (p_drv_buf->fcoe_rx_frames_set) {
			p_buf->p_val = (u8 *)&p_drv_buf->fcoe_rx_frames;
			return sizeof(p_drv_buf->fcoe_rx_frames);
		}
		break;
	case DRV_TLV_FCOE_RX_BYTES_RECEIVED:
		if (p_drv_buf->fcoe_rx_bytes_set) {
			p_buf->p_val = (u8 *)&p_drv_buf->fcoe_rx_bytes;
			return sizeof(p_drv_buf->fcoe_rx_bytes);
		}
		break;
	case DRV_TLV_FCOE_TX_FRAMES_SENT:
		if (p_drv_buf->fcoe_tx_frames_set) {
			p_buf->p_val = (u8 *)&p_drv_buf->fcoe_tx_frames;
			return sizeof(p_drv_buf->fcoe_tx_frames);
		}
		break;
	case DRV_TLV_FCOE_TX_BYTES_SENT:
		if (p_drv_buf->fcoe_tx_bytes_set) {
			p_buf->p_val = (u8 *)&p_drv_buf->fcoe_tx_bytes;
			return sizeof(p_drv_buf->fcoe_tx_bytes);
		}
		break;
	case DRV_TLV_CRC_ERROR_COUNT:
		if (p_drv_buf->crc_count_set) {
			p_buf->p_val = (u8 *)&p_drv_buf->crc_count;
			return sizeof(p_drv_buf->crc_count);
		}
		break;
	case DRV_TLV_CRC_ERROR_1_RECEIVED_SOURCE_FC_ID:
	case DRV_TLV_CRC_ERROR_2_RECEIVED_SOURCE_FC_ID:
	case DRV_TLV_CRC_ERROR_3_RECEIVED_SOURCE_FC_ID:
	case DRV_TLV_CRC_ERROR_4_RECEIVED_SOURCE_FC_ID:
	case DRV_TLV_CRC_ERROR_5_RECEIVED_SOURCE_FC_ID:
	{
		u8 idx = (p_tlv->tlv_type -
			  DRV_TLV_CRC_ERROR_1_RECEIVED_SOURCE_FC_ID) / 2;

		if (p_drv_buf->crc_err_src_fcid_set[idx]) {
			p_buf->p_val = (u8 *)&p_drv_buf->crc_err_src_fcid[idx];
			return sizeof(p_drv_buf->crc_err_src_fcid[idx]);
		}
		break;
	}

	case DRV_TLV_CRC_ERROR_1_TIMESTAMP:
	case DRV_TLV_CRC_ERROR_2_TIMESTAMP:
	case DRV_TLV_CRC_ERROR_3_TIMESTAMP:
	case DRV_TLV_CRC_ERROR_4_TIMESTAMP:
	case DRV_TLV_CRC_ERROR_5_TIMESTAMP:
	{
		u8 idx = (p_tlv->tlv_type -
			  DRV_TLV_CRC_ERROR_1_TIMESTAMP) / 2;

		return ecore_mfw_get_tlv_time_value(&p_drv_buf->crc_err[idx],
						    p_buf);
	}

	case DRV_TLV_LOSS_OF_SYNC_ERROR_COUNT:
		if (p_drv_buf->losync_err_set) {
			p_buf->p_val = (u8 *)&p_drv_buf->losync_err;
			return sizeof(p_drv_buf->losync_err);
		}
		break;
	case DRV_TLV_LOSS_OF_SIGNAL_ERRORS:
		if (p_drv_buf->losig_err_set) {
			p_buf->p_val = (u8 *)&p_drv_buf->losig_err;
			return sizeof(p_drv_buf->losig_err);
		}
		break;
	case DRV_TLV_PRIMITIVE_SEQUENCE_PROTOCOL_ERROR_COUNT:
		if (p_drv_buf->primtive_err_set) {
			p_buf->p_val = (u8 *)&p_drv_buf->primtive_err;
			return sizeof(p_drv_buf->primtive_err);
		}
		break;
	case DRV_TLV_DISPARITY_ERROR_COUNT:
		if (p_drv_buf->disparity_err_set) {
			p_buf->p_val = (u8 *)&p_drv_buf->disparity_err;
			return sizeof(p_drv_buf->disparity_err);
		}
		break;
	case DRV_TLV_CODE_VIOLATION_ERROR_COUNT:
		if (p_drv_buf->code_violation_err_set) {
			p_buf->p_val = (u8 *)&p_drv_buf->code_violation_err;
			return sizeof(p_drv_buf->code_violation_err);
		}
		break;
	case DRV_TLV_LAST_FLOGI_ISSUED_COMMON_PARAMETERS_WORD_1:
	case DRV_TLV_LAST_FLOGI_ISSUED_COMMON_PARAMETERS_WORD_2:
	case DRV_TLV_LAST_FLOGI_ISSUED_COMMON_PARAMETERS_WORD_3:
	case DRV_TLV_LAST_FLOGI_ISSUED_COMMON_PARAMETERS_WORD_4:
	{
		u8 idx = p_tlv->tlv_type -
			 DRV_TLV_LAST_FLOGI_ISSUED_COMMON_PARAMETERS_WORD_1;
		if (p_drv_buf->flogi_param_set[idx]) {
			p_buf->p_val = (u8 *)&p_drv_buf->flogi_param[idx];
			return sizeof(p_drv_buf->flogi_param[idx]);
		}
		break;
	}
	case DRV_TLV_LAST_FLOGI_TIMESTAMP:
		return ecore_mfw_get_tlv_time_value(&p_drv_buf->flogi_tstamp,
						    p_buf);
	case DRV_TLV_LAST_FLOGI_ACC_COMMON_PARAMETERS_WORD_1:
	case DRV_TLV_LAST_FLOGI_ACC_COMMON_PARAMETERS_WORD_2:
	case DRV_TLV_LAST_FLOGI_ACC_COMMON_PARAMETERS_WORD_3:
	case DRV_TLV_LAST_FLOGI_ACC_COMMON_PARAMETERS_WORD_4:
	{
		u8 idx = p_tlv->tlv_type -
			 DRV_TLV_LAST_FLOGI_ACC_COMMON_PARAMETERS_WORD_1;

		if (p_drv_buf->flogi_acc_param_set[idx]) {
			p_buf->p_val = (u8 *)&p_drv_buf->flogi_acc_param[idx];
			return sizeof(p_drv_buf->flogi_acc_param[idx]);
		}
		break;
	}
	case DRV_TLV_LAST_FLOGI_ACC_TIMESTAMP:
		return ecore_mfw_get_tlv_time_value(&p_drv_buf->flogi_acc_tstamp,
						    p_buf);
	case DRV_TLV_LAST_FLOGI_RJT:
		if (p_drv_buf->flogi_rjt_set) {
			p_buf->p_val = (u8 *)&p_drv_buf->flogi_rjt;
			return sizeof(p_drv_buf->flogi_rjt);
		}
		break;
	case DRV_TLV_LAST_FLOGI_RJT_TIMESTAMP:
		return ecore_mfw_get_tlv_time_value(&p_drv_buf->flogi_rjt_tstamp,
						    p_buf);
	case DRV_TLV_FDISCS_SENT_COUNT:
		if (p_drv_buf->fdiscs_set) {
			p_buf->p_val = (u8 *)&p_drv_buf->fdiscs;
			return sizeof(p_drv_buf->fdiscs);
		}
		break;
	case DRV_TLV_FDISC_ACCS_RECEIVED:
		if (p_drv_buf->fdisc_acc_set) {
			p_buf->p_val = (u8 *)&p_drv_buf->fdisc_acc;
			return sizeof(p_drv_buf->fdisc_acc);
		}
		break;
	case DRV_TLV_FDISC_RJTS_RECEIVED:
		if (p_drv_buf->fdisc_rjt_set) {
			p_buf->p_val = (u8 *)&p_drv_buf->fdisc_rjt;
			return sizeof(p_drv_buf->fdisc_rjt);
		}
		break;
	case DRV_TLV_PLOGI_SENT_COUNT:
		if (p_drv_buf->plogi_set) {
			p_buf->p_val = (u8 *)&p_drv_buf->plogi;
			return sizeof(p_drv_buf->plogi);
		}
		break;
	case DRV_TLV_PLOGI_ACCS_RECEIVED:
		if (p_drv_buf->plogi_acc_set) {
			p_buf->p_val = (u8 *)&p_drv_buf->plogi_acc;
			return sizeof(p_drv_buf->plogi_acc);
		}
		break;
	case DRV_TLV_PLOGI_RJTS_RECEIVED:
		if (p_drv_buf->plogi_rjt_set) {
			p_buf->p_val = (u8 *)&p_drv_buf->plogi_rjt;
			return sizeof(p_drv_buf->plogi_rjt);
		}
		break;
	case DRV_TLV_PLOGI_1_SENT_DESTINATION_FC_ID:
	case DRV_TLV_PLOGI_2_SENT_DESTINATION_FC_ID:
	case DRV_TLV_PLOGI_3_SENT_DESTINATION_FC_ID:
	case DRV_TLV_PLOGI_4_SENT_DESTINATION_FC_ID:
	case DRV_TLV_PLOGI_5_SENT_DESTINATION_FC_ID:
	{
		u8 idx = (p_tlv->tlv_type -
			  DRV_TLV_PLOGI_1_SENT_DESTINATION_FC_ID) / 2;

		if (p_drv_buf->plogi_dst_fcid_set[idx]) {
			p_buf->p_val = (u8 *)&p_drv_buf->plogi_dst_fcid[idx];
			return sizeof(p_drv_buf->plogi_dst_fcid[idx]);
		}
		break;
	}
	case DRV_TLV_PLOGI_1_TIMESTAMP:
	case DRV_TLV_PLOGI_2_TIMESTAMP:
	case DRV_TLV_PLOGI_3_TIMESTAMP:
	case DRV_TLV_PLOGI_4_TIMESTAMP:
	case DRV_TLV_PLOGI_5_TIMESTAMP:
	{
		u8 idx = (p_tlv->tlv_type -
			  DRV_TLV_PLOGI_1_TIMESTAMP) / 2;

		return ecore_mfw_get_tlv_time_value(&p_drv_buf->plogi_tstamp[idx],
						    p_buf);
	}

	case DRV_TLV_PLOGI_1_ACC_RECEIVED_SOURCE_FC_ID:
	case DRV_TLV_PLOGI_2_ACC_RECEIVED_SOURCE_FC_ID:
	case DRV_TLV_PLOGI_3_ACC_RECEIVED_SOURCE_FC_ID:
	case DRV_TLV_PLOGI_4_ACC_RECEIVED_SOURCE_FC_ID:
	case DRV_TLV_PLOGI_5_ACC_RECEIVED_SOURCE_FC_ID:
	{
		u8 idx = (p_tlv->tlv_type -
			  DRV_TLV_PLOGI_1_ACC_RECEIVED_SOURCE_FC_ID) / 2;

		if (p_drv_buf->plogi_acc_src_fcid_set[idx]) {
			p_buf->p_val = (u8 *)&p_drv_buf->plogi_acc_src_fcid[idx];
			return sizeof(p_drv_buf->plogi_acc_src_fcid[idx]);
		}
		break;
	}
	case DRV_TLV_PLOGI_1_ACC_TIMESTAMP:
	case DRV_TLV_PLOGI_2_ACC_TIMESTAMP:
	case DRV_TLV_PLOGI_3_ACC_TIMESTAMP:
	case DRV_TLV_PLOGI_4_ACC_TIMESTAMP:
	case DRV_TLV_PLOGI_5_ACC_TIMESTAMP:
	{
		u8 idx = (p_tlv->tlv_type -
			  DRV_TLV_PLOGI_1_ACC_TIMESTAMP) / 2;

		return ecore_mfw_get_tlv_time_value(&p_drv_buf->plogi_acc_tstamp[idx],
						    p_buf);
	}

	case DRV_TLV_LOGOS_ISSUED:
		if (p_drv_buf->tx_plogos_set) {
			p_buf->p_val = (u8 *)&p_drv_buf->tx_plogos;
			return sizeof(p_drv_buf->tx_plogos);
		}
		break;
	case DRV_TLV_LOGO_ACCS_RECEIVED:
		if (p_drv_buf->plogo_acc_set) {
			p_buf->p_val = (u8 *)&p_drv_buf->plogo_acc;
			return sizeof(p_drv_buf->plogo_acc);
		}
		break;
	case DRV_TLV_LOGO_RJTS_RECEIVED:
		if (p_drv_buf->plogo_rjt_set) {
			p_buf->p_val = (u8 *)&p_drv_buf->plogo_rjt;
			return sizeof(p_drv_buf->plogo_rjt);
		}
		break;
	case DRV_TLV_LOGO_1_RECEIVED_SOURCE_FC_ID:
	case DRV_TLV_LOGO_2_RECEIVED_SOURCE_FC_ID:
	case DRV_TLV_LOGO_3_RECEIVED_SOURCE_FC_ID:
	case DRV_TLV_LOGO_4_RECEIVED_SOURCE_FC_ID:
	case DRV_TLV_LOGO_5_RECEIVED_SOURCE_FC_ID:
	{
		u8 idx = (p_tlv->tlv_type -
			  DRV_TLV_LOGO_1_RECEIVED_SOURCE_FC_ID) / 2;

		if (p_drv_buf->plogo_src_fcid_set[idx]) {
			p_buf->p_val = (u8 *)&p_drv_buf->plogo_src_fcid[idx];
			return sizeof(p_drv_buf->plogo_src_fcid[idx]);
		}
		break;
	}
	case DRV_TLV_LOGO_1_TIMESTAMP:
	case DRV_TLV_LOGO_2_TIMESTAMP:
	case DRV_TLV_LOGO_3_TIMESTAMP:
	case DRV_TLV_LOGO_4_TIMESTAMP:
	case DRV_TLV_LOGO_5_TIMESTAMP:
	{
		u8 idx = (p_tlv->tlv_type -
			  DRV_TLV_LOGO_1_TIMESTAMP) / 2;

		return ecore_mfw_get_tlv_time_value(&p_drv_buf->plogo_tstamp[idx],
						    p_buf);
	}
	case DRV_TLV_LOGOS_RECEIVED:
		if (p_drv_buf->rx_logos_set) {
			p_buf->p_val = (u8 *)&p_drv_buf->rx_logos;
			return sizeof(p_drv_buf->rx_logos);
		}
		break;
	case DRV_TLV_ACCS_ISSUED:
		if (p_drv_buf->tx_accs_set) {
			p_buf->p_val = (u8 *)&p_drv_buf->tx_accs;
			return sizeof(p_drv_buf->tx_accs);
		}
		break;
	case DRV_TLV_PRLIS_ISSUED:
		if (p_drv_buf->tx_prlis_set) {
			p_buf->p_val = (u8 *)&p_drv_buf->tx_prlis;
			return sizeof(p_drv_buf->tx_prlis);
		}
		break;
	case DRV_TLV_ACCS_RECEIVED:
		if (p_drv_buf->rx_accs_set) {
			p_buf->p_val = (u8 *)&p_drv_buf->rx_accs;
			return sizeof(p_drv_buf->rx_accs);
		}
		break;
	case DRV_TLV_ABTS_SENT_COUNT:
		if (p_drv_buf->tx_abts_set) {
			p_buf->p_val = (u8 *)&p_drv_buf->tx_abts;
			return sizeof(p_drv_buf->tx_abts);
		}
		break;
	case DRV_TLV_ABTS_ACCS_RECEIVED:
		if (p_drv_buf->rx_abts_acc_set) {
			p_buf->p_val = (u8 *)&p_drv_buf->rx_abts_acc;
			return sizeof(p_drv_buf->rx_abts_acc);
		}
		break;
	case DRV_TLV_ABTS_RJTS_RECEIVED:
		if (p_drv_buf->rx_abts_rjt_set) {
			p_buf->p_val = (u8 *)&p_drv_buf->rx_abts_rjt;
			return sizeof(p_drv_buf->rx_abts_rjt);
		}
		break;
	case DRV_TLV_ABTS_1_SENT_DESTINATION_FC_ID:
	case DRV_TLV_ABTS_2_SENT_DESTINATION_FC_ID:
	case DRV_TLV_ABTS_3_SENT_DESTINATION_FC_ID:
	case DRV_TLV_ABTS_4_SENT_DESTINATION_FC_ID:
	case DRV_TLV_ABTS_5_SENT_DESTINATION_FC_ID:
	{
		u8 idx = (p_tlv->tlv_type -
			  DRV_TLV_ABTS_1_SENT_DESTINATION_FC_ID) / 2;

		if (p_drv_buf->abts_dst_fcid_set[idx]) {
			p_buf->p_val = (u8 *)&p_drv_buf->abts_dst_fcid[idx];
			return sizeof(p_drv_buf->abts_dst_fcid[idx]);
		}
		break;
	}
	case DRV_TLV_ABTS_1_TIMESTAMP:
	case DRV_TLV_ABTS_2_TIMESTAMP:
	case DRV_TLV_ABTS_3_TIMESTAMP:
	case DRV_TLV_ABTS_4_TIMESTAMP:
	case DRV_TLV_ABTS_5_TIMESTAMP:
	{
		u8 idx = (p_tlv->tlv_type -
			  DRV_TLV_ABTS_1_TIMESTAMP) / 2;

		return ecore_mfw_get_tlv_time_value(&p_drv_buf->abts_tstamp[idx],
						    p_buf);
	}

	case DRV_TLV_RSCNS_RECEIVED:
		if (p_drv_buf->rx_rscn_set) {
			p_buf->p_val = (u8 *)&p_drv_buf->rx_rscn;
			return sizeof(p_drv_buf->rx_rscn);
		}
		break;
	case DRV_TLV_LAST_RSCN_RECEIVED_N_PORT_1:
	case DRV_TLV_LAST_RSCN_RECEIVED_N_PORT_2:
	case DRV_TLV_LAST_RSCN_RECEIVED_N_PORT_3:
	case DRV_TLV_LAST_RSCN_RECEIVED_N_PORT_4:
	{
		u8 idx = p_tlv->tlv_type - DRV_TLV_LAST_RSCN_RECEIVED_N_PORT_1;

		if (p_drv_buf->rx_rscn_nport_set[idx]) {
			p_buf->p_val = (u8 *)&p_drv_buf->rx_rscn_nport[idx];
			return sizeof(p_drv_buf->rx_rscn_nport[idx]);
		}
		break;
	}
	case DRV_TLV_LUN_RESETS_ISSUED:
		if (p_drv_buf->tx_lun_rst_set) {
			p_buf->p_val = (u8 *)&p_drv_buf->tx_lun_rst;
			return sizeof(p_drv_buf->tx_lun_rst);
		}
		break;
	case DRV_TLV_ABORT_TASK_SETS_ISSUED:
		if (p_drv_buf->abort_task_sets_set) {
			p_buf->p_val = (u8 *)&p_drv_buf->abort_task_sets;
			return sizeof(p_drv_buf->abort_task_sets);
		}
		break;
	case DRV_TLV_TPRLOS_SENT:
		if (p_drv_buf->tx_tprlos_set) {
			p_buf->p_val = (u8 *)&p_drv_buf->tx_tprlos;
			return sizeof(p_drv_buf->tx_tprlos);
		}
		break;
	case DRV_TLV_NOS_SENT_COUNT:
		if (p_drv_buf->tx_nos_set) {
			p_buf->p_val = (u8 *)&p_drv_buf->tx_nos;
			return sizeof(p_drv_buf->tx_nos);
		}
		break;
	case DRV_TLV_NOS_RECEIVED_COUNT:
		if (p_drv_buf->rx_nos_set) {
			p_buf->p_val = (u8 *)&p_drv_buf->rx_nos;
			return sizeof(p_drv_buf->rx_nos);
		}
		break;
	case DRV_TLV_OLS_COUNT:
		if (p_drv_buf->ols_set) {
			p_buf->p_val = (u8 *)&p_drv_buf->ols;
			return sizeof(p_drv_buf->ols);
		}
		break;
	case DRV_TLV_LR_COUNT:
		if (p_drv_buf->lr_set) {
			p_buf->p_val = (u8 *)&p_drv_buf->lr;
			return sizeof(p_drv_buf->lr);
		}
		break;
	case DRV_TLV_LRR_COUNT:
		if (p_drv_buf->lrr_set) {
			p_buf->p_val = (u8 *)&p_drv_buf->lrr;
			return sizeof(p_drv_buf->lrr);
		}
		break;
	case DRV_TLV_LIP_SENT_COUNT:
		if (p_drv_buf->tx_lip_set) {
			p_buf->p_val = (u8 *)&p_drv_buf->tx_lip;
			return sizeof(p_drv_buf->tx_lip);
		}
		break;
	case DRV_TLV_LIP_RECEIVED_COUNT:
		if (p_drv_buf->rx_lip_set) {
			p_buf->p_val = (u8 *)&p_drv_buf->rx_lip;
			return sizeof(p_drv_buf->rx_lip);
		}
		break;
	case DRV_TLV_EOFA_COUNT:
		if (p_drv_buf->eofa_set) {
			p_buf->p_val = (u8 *)&p_drv_buf->eofa;
			return sizeof(p_drv_buf->eofa);
		}
		break;
	case DRV_TLV_EOFNI_COUNT:
		if (p_drv_buf->eofni_set) {
			p_buf->p_val = (u8 *)&p_drv_buf->eofni;
			return sizeof(p_drv_buf->eofni);
		}
		break;
	case DRV_TLV_SCSI_STATUS_CHECK_CONDITION_COUNT:
		if (p_drv_buf->scsi_chks_set) {
			p_buf->p_val = (u8 *)&p_drv_buf->scsi_chks;
			return sizeof(p_drv_buf->scsi_chks);
		}
		break;
	case DRV_TLV_SCSI_STATUS_CONDITION_MET_COUNT:
		if (p_drv_buf->scsi_cond_met_set) {
			p_buf->p_val = (u8 *)&p_drv_buf->scsi_cond_met;
			return sizeof(p_drv_buf->scsi_cond_met);
		}
		break;
	case DRV_TLV_SCSI_STATUS_BUSY_COUNT:
		if (p_drv_buf->scsi_busy_set) {
			p_buf->p_val = (u8 *)&p_drv_buf->scsi_busy;
			return sizeof(p_drv_buf->scsi_busy);
		}
		break;
	case DRV_TLV_SCSI_STATUS_INTERMEDIATE_COUNT:
		if (p_drv_buf->scsi_inter_set) {
			p_buf->p_val = (u8 *)&p_drv_buf->scsi_inter;
			return sizeof(p_drv_buf->scsi_inter);
		}
		break;
	case DRV_TLV_SCSI_STATUS_INTERMEDIATE_CONDITION_MET_COUNT:
		if (p_drv_buf->scsi_inter_cond_met_set) {
			p_buf->p_val = (u8 *)&p_drv_buf->scsi_inter_cond_met;
			return sizeof(p_drv_buf->scsi_inter_cond_met);
		}
		break;
	case DRV_TLV_SCSI_STATUS_RESERVATION_CONFLICT_COUNT:
		if (p_drv_buf->scsi_rsv_conflicts_set) {
			p_buf->p_val = (u8 *)&p_drv_buf->scsi_rsv_conflicts;
			return sizeof(p_drv_buf->scsi_rsv_conflicts);
		}
		break;
	case DRV_TLV_SCSI_STATUS_TASK_SET_FULL_COUNT:
		if (p_drv_buf->scsi_tsk_full_set) {
			p_buf->p_val = (u8 *)&p_drv_buf->scsi_tsk_full;
			return sizeof(p_drv_buf->scsi_tsk_full);
		}
		break;
	case DRV_TLV_SCSI_STATUS_ACA_ACTIVE_COUNT:
		if (p_drv_buf->scsi_aca_active_set) {
			p_buf->p_val = (u8 *)&p_drv_buf->scsi_aca_active;
			return sizeof(p_drv_buf->scsi_aca_active);
		}
		break;
	case DRV_TLV_SCSI_STATUS_TASK_ABORTED_COUNT:
		if (p_drv_buf->scsi_tsk_abort_set) {
			p_buf->p_val = (u8 *)&p_drv_buf->scsi_tsk_abort;
			return sizeof(p_drv_buf->scsi_tsk_abort);
		}
		break;
	case DRV_TLV_SCSI_CHECK_CONDITION_1_RECEIVED_SK_ASC_ASCQ:
	case DRV_TLV_SCSI_CHECK_CONDITION_2_RECEIVED_SK_ASC_ASCQ:
	case DRV_TLV_SCSI_CHECK_CONDITION_3_RECEIVED_SK_ASC_ASCQ:
	case DRV_TLV_SCSI_CHECK_CONDITION_4_RECEIVED_SK_ASC_ASCQ:
	case DRV_TLV_SCSI_CHECK_CONDITION_5_RECEIVED_SK_ASC_ASCQ:
	{
		u8 idx = (p_tlv->tlv_type -
			  DRV_TLV_SCSI_CHECK_CONDITION_1_RECEIVED_SK_ASC_ASCQ) / 2;

		if (p_drv_buf->scsi_rx_chk_set[idx]) {
			p_buf->p_val = (u8 *)&p_drv_buf->scsi_rx_chk[idx];
			return sizeof(p_drv_buf->scsi_rx_chk[idx]);
		}
		break;
	}
	case DRV_TLV_SCSI_CHECK_1_TIMESTAMP:
	case DRV_TLV_SCSI_CHECK_2_TIMESTAMP:
	case DRV_TLV_SCSI_CHECK_3_TIMESTAMP:
	case DRV_TLV_SCSI_CHECK_4_TIMESTAMP:
	case DRV_TLV_SCSI_CHECK_5_TIMESTAMP:
	{
		u8 idx = (p_tlv->tlv_type -
			  DRV_TLV_SCSI_CHECK_1_TIMESTAMP) / 2;

		return ecore_mfw_get_tlv_time_value(&p_drv_buf->scsi_chk_tstamp[idx],
						    p_buf);
	}

	default:
		break;
	}

	return -1;
}

static int
ecore_mfw_get_iscsi_tlv_value(struct ecore_drv_tlv_hdr *p_tlv,
			      struct ecore_mfw_tlv_iscsi *p_drv_buf,
			      struct ecore_tlv_parsed_buf *p_buf)
{
	switch (p_tlv->tlv_type) {
	case DRV_TLV_TARGET_LLMNR_ENABLED:
		if (p_drv_buf->target_llmnr_set) {
			p_buf->p_val = (u8 *)&p_drv_buf->target_llmnr;
			return sizeof(p_drv_buf->target_llmnr);
		}
		break;
	case DRV_TLV_HEADER_DIGEST_FLAG_ENABLED:
		if (p_drv_buf->header_digest_set) {
			p_buf->p_val = (u8 *)&p_drv_buf->header_digest;
			return sizeof(p_drv_buf->header_digest);
		}
		break;
	case DRV_TLV_DATA_DIGEST_FLAG_ENABLED:
		if (p_drv_buf->data_digest_set) {
			p_buf->p_val = (u8 *)&p_drv_buf->data_digest;
			return sizeof(p_drv_buf->data_digest);
		}
		break;
	case DRV_TLV_AUTHENTICATION_METHOD:
		if (p_drv_buf->auth_method_set) {
			p_buf->p_val = (u8 *)&p_drv_buf->auth_method;
			return sizeof(p_drv_buf->auth_method);
		}
		break;
	case DRV_TLV_ISCSI_BOOT_TARGET_PORTAL:
		if (p_drv_buf->boot_taget_portal_set) {
			p_buf->p_val = (u8 *)&p_drv_buf->boot_taget_portal;
			return sizeof(p_drv_buf->boot_taget_portal);
		}
		break;
	case DRV_TLV_MAX_FRAME_SIZE:
		if (p_drv_buf->frame_size_set) {
			p_buf->p_val = (u8 *)&p_drv_buf->frame_size;
			return sizeof(p_drv_buf->frame_size);
		}
		break;
	case DRV_TLV_PDU_TX_DESCRIPTORS_QUEUE_SIZE:
		if (p_drv_buf->tx_desc_size_set) {
			p_buf->p_val = (u8 *)&p_drv_buf->tx_desc_size;
			return sizeof(p_drv_buf->tx_desc_size);
		}
		break;
	case DRV_TLV_PDU_RX_DESCRIPTORS_QUEUE_SIZE:
		if (p_drv_buf->rx_desc_size_set) {
			p_buf->p_val = (u8 *)&p_drv_buf->rx_desc_size;
			return sizeof(p_drv_buf->rx_desc_size);
		}
		break;
	case DRV_TLV_ISCSI_BOOT_PROGRESS:
		if (p_drv_buf->boot_progress_set) {
			p_buf->p_val = (u8 *)&p_drv_buf->boot_progress;
			return sizeof(p_drv_buf->boot_progress);
		}
		break;
	case DRV_TLV_PDU_TX_DESCRIPTOR_QUEUE_AVG_DEPTH:
		if (p_drv_buf->tx_desc_qdepth_set) {
			p_buf->p_val = (u8 *)&p_drv_buf->tx_desc_qdepth;
			return sizeof(p_drv_buf->tx_desc_qdepth);
		}
		break;
	case DRV_TLV_PDU_RX_DESCRIPTORS_QUEUE_AVG_DEPTH:
		if (p_drv_buf->rx_desc_qdepth_set) {
			p_buf->p_val = (u8 *)&p_drv_buf->rx_desc_qdepth;
			return sizeof(p_drv_buf->rx_desc_qdepth);
		}
		break;
	case DRV_TLV_ISCSI_PDU_RX_FRAMES_RECEIVED:
		if (p_drv_buf->rx_frames_set) {
			p_buf->p_val = (u8 *)&p_drv_buf->rx_frames;
			return sizeof(p_drv_buf->rx_frames);
		}
		break;
	case DRV_TLV_ISCSI_PDU_RX_BYTES_RECEIVED:
		if (p_drv_buf->rx_bytes_set) {
			p_buf->p_val = (u8 *)&p_drv_buf->rx_bytes;
			return sizeof(p_drv_buf->rx_bytes);
		}
		break;
	case DRV_TLV_ISCSI_PDU_TX_FRAMES_SENT:
		if (p_drv_buf->tx_frames_set) {
			p_buf->p_val = (u8 *)&p_drv_buf->tx_frames;
			return sizeof(p_drv_buf->tx_frames);
		}
		break;
	case DRV_TLV_ISCSI_PDU_TX_BYTES_SENT:
		if (p_drv_buf->tx_bytes_set) {
			p_buf->p_val = (u8 *)&p_drv_buf->tx_bytes;
			return sizeof(p_drv_buf->tx_bytes);
		}
		break;
	default:
		break;
	}

	return -1;
}

static enum _ecore_status_t ecore_mfw_update_tlvs(struct ecore_hwfn *p_hwfn,
						  u8 tlv_group, u8 *p_mfw_buf,
						  u32 size)
{
	union ecore_mfw_tlv_data *p_tlv_data;
	struct ecore_tlv_parsed_buf buffer;
	struct ecore_drv_tlv_hdr tlv;
	u32 offset;
	int len;
	u8 *p_tlv;

	p_tlv_data = OSAL_VZALLOC(p_hwfn->p_dev, sizeof(*p_tlv_data));
	if (!p_tlv_data)
		return ECORE_NOMEM;

	if (OSAL_MFW_FILL_TLV_DATA(p_hwfn,tlv_group, p_tlv_data)) {
		OSAL_VFREE(p_hwfn->p_dev, p_tlv_data);
		return ECORE_INVAL;
	}

	OSAL_MEMSET(&tlv, 0, sizeof(tlv));
	for (offset = 0; offset < size;
	     offset += sizeof(tlv) + sizeof(u32) * tlv.tlv_length) {
		p_tlv = &p_mfw_buf[offset];
		tlv.tlv_type = TLV_TYPE(p_tlv);
		tlv.tlv_length = TLV_LENGTH(p_tlv);
		tlv.tlv_flags = TLV_FLAGS(p_tlv);

		DP_VERBOSE(p_hwfn, ECORE_MSG_SP,
			   "Type %d length = %d flags = 0x%x\n", tlv.tlv_type,
			   tlv.tlv_length, tlv.tlv_flags);

		if (tlv_group == ECORE_MFW_TLV_GENERIC)
			len = ecore_mfw_get_gen_tlv_value(&tlv, &p_tlv_data->generic, &buffer);
		else if (tlv_group == ECORE_MFW_TLV_ETH)
			len = ecore_mfw_get_eth_tlv_value(&tlv, &p_tlv_data->eth, &buffer);
		else if (tlv_group == ECORE_MFW_TLV_FCOE)
			len = ecore_mfw_get_fcoe_tlv_value(&tlv, &p_tlv_data->fcoe, &buffer);
		else
			len = ecore_mfw_get_iscsi_tlv_value(&tlv, &p_tlv_data->iscsi, &buffer);

		if (len > 0) {
			OSAL_WARN(len > 4 * tlv.tlv_length,
				  "Incorrect MFW TLV length %d, it shouldn't be greater than %d\n",
				  len, 4 * tlv.tlv_length);
			len = OSAL_MIN_T(int, len, 4 * tlv.tlv_length);
			tlv.tlv_flags |= ECORE_DRV_TLV_FLAGS_CHANGED;
			TLV_FLAGS(p_tlv) = tlv.tlv_flags;
			OSAL_MEMCPY(p_mfw_buf + offset + sizeof(tlv),
				    buffer.p_val, len);
		}
	}

	OSAL_VFREE(p_hwfn->p_dev, p_tlv_data);

	return ECORE_SUCCESS;
}

enum _ecore_status_t
ecore_mfw_process_tlv_req(struct ecore_hwfn *p_hwfn, struct ecore_ptt *p_ptt)
{
	u32 addr, size, offset, resp, param, val;
	u8 tlv_group = 0, id, *p_mfw_buf = OSAL_NULL, *p_temp;
	u32 global_offsize, global_addr;
	enum _ecore_status_t rc;
	struct ecore_drv_tlv_hdr tlv;

	addr = SECTION_OFFSIZE_ADDR(p_hwfn->mcp_info->public_base,
				    PUBLIC_GLOBAL);
	global_offsize = ecore_rd(p_hwfn, p_ptt, addr);
	global_addr = SECTION_ADDR(global_offsize, 0);
	addr = global_addr + OFFSETOF(struct public_global, data_ptr); 
	addr = ecore_rd(p_hwfn, p_ptt, addr);
	size = ecore_rd(p_hwfn, p_ptt, global_addr +
			OFFSETOF(struct public_global, data_size));

	if (!size) {
		DP_NOTICE(p_hwfn, false, "Invalid TLV req size = %d\n", size);
		goto drv_done;
	}

	p_mfw_buf = (void *)OSAL_VZALLOC(p_hwfn->p_dev, size);
	if (!p_mfw_buf) {
		DP_NOTICE(p_hwfn, false, "Failed allocate memory for p_mfw_buf\n");
		goto drv_done;
	}

	/* Read the TLV request to local buffer. MFW represents the TLV in
	 * little endian format and mcp returns it bigendian format. Hence
	 * driver need to convert data to little endian first and then do the
	 * memcpy (casting) to preserve the MFW TLV format in the driver buffer.
	 *
	 */
	for (offset = 0; offset < size; offset += sizeof(u32)) {
		val = ecore_rd(p_hwfn, p_ptt, addr + offset);
		val = OSAL_BE32_TO_CPU(val);
		OSAL_MEMCPY(&p_mfw_buf[offset], &val, sizeof(u32));
	}

	/* Parse the headers to enumerate the requested TLV groups */
	for (offset = 0; offset < size; 
	     offset += sizeof(tlv) + sizeof(u32) * tlv.tlv_length) {
		p_temp = &p_mfw_buf[offset];
		tlv.tlv_type = TLV_TYPE(p_temp);
		tlv.tlv_length = TLV_LENGTH(p_temp);
		if (ecore_mfw_get_tlv_group(tlv.tlv_type, &tlv_group))
			DP_VERBOSE(p_hwfn, ECORE_MSG_DRV,
				   "Un recognized TLV %d\n", tlv.tlv_type);
	}

	/* Sanitize the TLV groups according to personality */
	if ((tlv_group & ECORE_MFW_TLV_FCOE)  &&
	    p_hwfn->hw_info.personality != ECORE_PCI_FCOE) {
		DP_VERBOSE(p_hwfn, ECORE_MSG_SP,
			   "Skipping FCoE TLVs for non-FCoE function\n");
		tlv_group &= ~ECORE_MFW_TLV_FCOE;
	}

	if ((tlv_group & ECORE_MFW_TLV_ISCSI) &&
	    p_hwfn->hw_info.personality != ECORE_PCI_ISCSI) {
		DP_VERBOSE(p_hwfn, ECORE_MSG_SP,
			   "Skipping iSCSI TLVs for non-iSCSI function\n");
		tlv_group &= ~ECORE_MFW_TLV_ISCSI;
	}

	if ((tlv_group & ECORE_MFW_TLV_ETH) &&
	    !ECORE_IS_L2_PERSONALITY(p_hwfn)) {
		DP_VERBOSE(p_hwfn, ECORE_MSG_SP,
			   "Skipping L2 TLVs for non-L2 function\n");
		tlv_group &= ~ECORE_MFW_TLV_ETH;
	}

	/* Update the TLV values in the local buffer */
	for (id = ECORE_MFW_TLV_GENERIC; id < ECORE_MFW_TLV_MAX; id <<= 1) {
		if (tlv_group & id) {
			if (ecore_mfw_update_tlvs(p_hwfn, id, p_mfw_buf, size))
				goto drv_done;
		}
	}

	/* Write the TLV data to shared memory. The stream of 4 bytes first need
	 * to be mem-copied to u32 element to make it as LSB format. And then
	 * converted to big endian as required by mcp-write.
	 */
	for (offset = 0; offset < size; offset += sizeof(u32)) {
		OSAL_MEMCPY(&val, &p_mfw_buf[offset], sizeof(u32));
		val = OSAL_CPU_TO_BE32(val);
		ecore_wr(p_hwfn, p_ptt, addr + offset, val);
	}

drv_done:
	rc = ecore_mcp_cmd(p_hwfn, p_ptt, DRV_MSG_CODE_GET_TLV_DONE, 0, &resp,
			   &param);

	OSAL_VFREE(p_hwfn->p_dev, p_mfw_buf);

	return rc;
}
