// SPDX-License-Identifier: GPL-2.0
/* Huawei HiNIC PCI Express Linux driver
 * Copyright(c) 2017 Huawei Technologies Co., Ltd
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms and conditions of the GNU General Public License,
 * version 2, as published by the Free Software Foundation.
 *
 * This program is distributed in the hope it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * for more details.
 *
 */
#define pr_fmt(fmt) KBUILD_MODNAME ": [COMM]" fmt

#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/mutex.h>
#include <linux/device.h>
#include <linux/pci.h>
#include <linux/module.h>
#include <linux/completion.h>
#include <linux/semaphore.h>
#include <linux/vmalloc.h>

#include "ossl_knl.h"
#include "hinic_hw.h"
#include "hinic_hwdev.h"
#include "hinic_hw_mgmt.h"
#include "hinic_hwif.h"
#include "hinic_mbox.h"
#include "hinic_cfg.h"
#include "hinic_nic_cfg.h"
#include "hinic_mgmt_interface.h"
#include "hinic_multi_host_mgmt.h"

uint g_rdma_mtts_num;
uint g_rdma_qps_num;
uint g_rdma_mpts_num;
uint g_vfs_num;
module_param(g_rdma_mtts_num, uint, 0444);
MODULE_PARM_DESC(g_rdma_mtts_num, "number of roce used mtts, use default value when pass 0");
module_param(g_rdma_qps_num, uint, 0444);
MODULE_PARM_DESC(g_rdma_qps_num, "number of roce used qps, use default value when pass 0");
module_param(g_rdma_mpts_num, uint, 0444);
MODULE_PARM_DESC(g_rdma_mpts_num, "number of roce used mpts, use default value when pass 0");
module_param(g_vfs_num, uint, 0444);
MODULE_PARM_DESC(g_vfs_num, "number of used vfs, use default value when pass 0 ");

static uint intr_mode;

uint timer_enable = 1;
uint bloomfilter_enable;
uint g_test_qpc_num;
uint g_test_qpc_resvd_num;
uint g_test_pagesize_reorder;
uint g_test_xid_alloc_mode = 1;
uint g_test_gpa_check_enable = 1;
uint g_test_qpc_alloc_mode = 2;
uint g_test_scqc_alloc_mode = 2;
uint g_test_max_conn;
uint g_test_max_cache_conn;
uint g_test_scqc_num;
uint g_test_mpt_num;
uint g_test_mpt_resvd;
uint g_test_scq_resvd;
uint g_test_hash_num;
uint g_test_reorder_num;

static void set_cfg_test_param(struct cfg_mgmt_info *cfg_mgmt)
{
	cfg_mgmt->svc_cap.timer_en = (u8)timer_enable;
	cfg_mgmt->svc_cap.bloomfilter_en = (u8)bloomfilter_enable;
	cfg_mgmt->svc_cap.test_qpc_num = g_test_qpc_num;
	cfg_mgmt->svc_cap.test_qpc_resvd_num = g_test_qpc_resvd_num;
	cfg_mgmt->svc_cap.test_page_size_reorder = g_test_pagesize_reorder;
	cfg_mgmt->svc_cap.test_xid_alloc_mode = (bool)g_test_xid_alloc_mode;
	cfg_mgmt->svc_cap.test_gpa_check_enable = (bool)g_test_gpa_check_enable;
	cfg_mgmt->svc_cap.test_qpc_alloc_mode = (u8)g_test_qpc_alloc_mode;
	cfg_mgmt->svc_cap.test_scqc_alloc_mode = (u8)g_test_scqc_alloc_mode;
	cfg_mgmt->svc_cap.test_max_conn_num = g_test_max_conn;
	cfg_mgmt->svc_cap.test_max_cache_conn_num = g_test_max_cache_conn;
	cfg_mgmt->svc_cap.test_scqc_num = g_test_scqc_num;
	cfg_mgmt->svc_cap.test_mpt_num = g_test_mpt_num;
	cfg_mgmt->svc_cap.test_scq_resvd_num = g_test_scq_resvd;
	cfg_mgmt->svc_cap.test_mpt_recvd_num = g_test_mpt_resvd;
	cfg_mgmt->svc_cap.test_hash_num = g_test_hash_num;
	cfg_mgmt->svc_cap.test_reorder_num = g_test_reorder_num;
}

int hinic_sync_time(void *hwdev, u64 time)
{
	struct hinic_sync_time_info time_info = {0};
	u16 out_size = sizeof(time_info);
	int err;

	time_info.mstime = time;
	err = hinic_msg_to_mgmt_sync(hwdev, HINIC_MOD_COMM,
				     HINIC_MGMT_CMD_SYNC_TIME, &time_info,
				     sizeof(time_info), &time_info, &out_size,
				     0);
	if (err || time_info.status || !out_size) {
		sdk_err(((struct hinic_hwdev *)hwdev)->dev_hdl,
			"Failed to sync time to mgmt, err: %d, status: 0x%x, out size: 0x%x\n",
			err, time_info.status, out_size);
		return -EFAULT;
	}

	return err;
}

void hinic_sync_time_async(void *hwdev, u64 time)
{
	struct hinic_sync_time_info time_info = { 0 };

	time_info.mstime = time;
	hinic_msg_to_mgmt_async(hwdev, HINIC_MOD_COMM,
				HINIC_MGMT_CMD_SYNC_TIME, &time_info,
				sizeof(time_info));
}

static void parse_sf_en_cap(struct service_cap *cap,
			    struct hinic_dev_cap *dev_cap, enum func_type type)
{
	struct dev_sf_svc_attr *attr = &cap->sf_svc_attr;

	if (type == TYPE_PPF) {
		/* For PPF's SF EN flag, we assign it in get_dynamic_res_cap().
		 * we only save its VF's flag.
		 */
		attr->sf_en_vf = dev_cap->sf_en_vf;
	} else if (type == TYPE_PF) {
		if (dev_cap->sf_en_pf)
			cap->sf_en = true;
		else
			cap->sf_en = false;

		attr->sf_en_vf = dev_cap->sf_en_vf;
	} else {
		/* VF gets SF_EN_VF from PPF/PF */
		if (dev_cap->sf_en_vf)
			cap->sf_en = true;
		else
			cap->sf_en = false;

		attr->sf_en_vf = 0;
	}
}

static void parse_pub_res_cap(struct hinic_hwdev *hwdev,
			      struct service_cap *cap,
			      struct hinic_dev_cap *dev_cap,
			      enum func_type type)
{
	struct dev_sf_svc_attr *attr = &cap->sf_svc_attr;

	cap->svc_type = dev_cap->svc_cap_en;
	cap->chip_svc_type = cap->svc_type;

	if (dev_cap->sf_svc_attr & SF_SVC_FT_BIT)
		attr->ft_en = true;
	else
		attr->ft_en = false;

	if (dev_cap->sf_svc_attr & SF_SVC_RDMA_BIT)
		attr->rdma_en = true;
	else
		attr->rdma_en = false;

	cap->host_id = dev_cap->host_id;
	cap->ep_id = dev_cap->ep_id;

	cap->max_cos_id = dev_cap->max_cos_id;
	cap->cos_valid_bitmap = dev_cap->valid_cos_bitmap;
	cap->er_id = dev_cap->er_id;
	cap->port_id = dev_cap->port_id;
	cap->force_up = dev_cap->force_up;

	parse_sf_en_cap(cap, dev_cap, type);

	/* PF/PPF */
	if (type == TYPE_PF || type == TYPE_PPF) {
		cap->max_vf = dev_cap->max_vf;
		cap->pf_num = dev_cap->pf_num;
		cap->pf_id_start = dev_cap->pf_id_start;
		cap->vf_num = dev_cap->vf_num;
		cap->vf_id_start = dev_cap->vf_id_start;

		/* FC need max queue number, but max queue number info is in
		 * l2nic cap, we also put max queue num info in public cap, so
		 * FC can get correct max queue number info.
		 */
		cap->max_sqs = dev_cap->nic_max_sq + 1;
		cap->max_rqs = dev_cap->nic_max_rq + 1;
	} else {
		cap->max_vf = 0;
		cap->max_sqs = dev_cap->nic_max_sq;
		cap->max_rqs = dev_cap->nic_max_rq;
	}

	cap->host_total_function = dev_cap->host_total_func;
	cap->host_oq_id_mask_val = dev_cap->host_oq_id_mask_val;
	cap->max_connect_num = dev_cap->max_conn_num;
	cap->max_stick2cache_num = dev_cap->max_stick2cache_num;
	cap->bfilter_start_addr = dev_cap->max_bfilter_start_addr;
	cap->bfilter_len = dev_cap->bfilter_len;
	cap->hash_bucket_num = dev_cap->hash_bucket_num;
	cap->dev_ver_info.cfg_file_ver = dev_cap->cfg_file_ver;
	cap->net_port_mode = dev_cap->net_port_mode;

	/* FC does not use VF */
	if (cap->net_port_mode == CFG_NET_MODE_FC)
		cap->max_vf = 0;

	sdk_info(hwdev->dev_hdl, "Get public resource capbility, svc_cap_en: 0x%x\n",
		 dev_cap->svc_cap_en);
	sdk_info(hwdev->dev_hdl, "Host_id=0x%x, ep_id=0x%x, max_cos_id=0x%x, cos_bitmap=0x%x, er_id=0x%x, port_id=0x%x\n",
		 cap->host_id, cap->ep_id,
		 cap->max_cos_id, cap->cos_valid_bitmap,
		 cap->er_id, cap->port_id);
	sdk_info(hwdev->dev_hdl, "Host_total_function=0x%x, host_oq_id_mask_val=0x%x, net_port_mode=0x%x, max_vf=0x%x\n",
		 cap->host_total_function, cap->host_oq_id_mask_val,
		 cap->net_port_mode, cap->max_vf);

	sdk_info(hwdev->dev_hdl, "Pf_num=0x%x, pf_id_start=0x%x, vf_num=0x%x, vf_id_start=0x%x\n",
		 cap->pf_num, cap->pf_id_start,
		 cap->vf_num, cap->vf_id_start);

	/* Check parameters from firmware */
	if (cap->max_sqs > HINIC_CFG_MAX_QP ||
	    cap->max_rqs > HINIC_CFG_MAX_QP) {
		sdk_info(hwdev->dev_hdl, "Number of qp exceed limit[1-%d]: sq: %d, rq: %d\n",
			 HINIC_CFG_MAX_QP, cap->max_sqs, cap->max_rqs);
		cap->max_sqs = HINIC_CFG_MAX_QP;
		cap->max_rqs = HINIC_CFG_MAX_QP;
	}
}

static void parse_dynamic_share_res_cap(struct hinic_hwdev *hwdev,
					struct service_cap *cap,
					struct hinic_dev_cap *dev_cap,
					enum func_type type)
{
	struct host_shared_resource_cap *shared_cap = &cap->shared_res_cap;

	shared_cap->host_pctxs = dev_cap->host_pctx_num;

	if (dev_cap->host_sf_en)
		cap->sf_en = true;
	else
		cap->sf_en = false;

	shared_cap->host_cctxs = dev_cap->host_ccxt_num;
	shared_cap->host_scqs = dev_cap->host_scq_num;
	shared_cap->host_srqs = dev_cap->host_srq_num;
	shared_cap->host_mpts = dev_cap->host_mpt_num;

	sdk_info(hwdev->dev_hdl, "Dynamic share resource capbility, host_pctxs=0x%x, host_cctxs=0x%x, host_scqs=0x%x, host_srqs=0x%x, host_mpts=0x%x\n",
		 shared_cap->host_pctxs, shared_cap->host_cctxs,
		 shared_cap->host_scqs, shared_cap->host_srqs,
		 shared_cap->host_mpts);
}

static void parse_l2nic_res_cap(struct hinic_hwdev *hwdev,
				struct service_cap *cap,
				struct hinic_dev_cap *dev_cap,
				enum func_type type)
{
	struct nic_service_cap *nic_cap = &cap->nic_cap;

	/* PF/PPF */
	if (type == TYPE_PF || type == TYPE_PPF) {
		nic_cap->max_sqs = dev_cap->nic_max_sq + 1;
		nic_cap->max_rqs = dev_cap->nic_max_rq + 1;
		nic_cap->vf_max_sqs = dev_cap->nic_vf_max_sq + 1;
		nic_cap->vf_max_rqs = dev_cap->nic_vf_max_rq + 1;
		nic_cap->max_queue_allowed = 0;
		nic_cap->dynamic_qp = 0;
	} else {
		nic_cap->max_sqs = dev_cap->nic_max_sq;
		nic_cap->max_rqs = dev_cap->nic_max_rq;
		nic_cap->vf_max_sqs = 0;
		nic_cap->vf_max_rqs = 0;
		nic_cap->max_queue_allowed = dev_cap->max_queue_allowed;
		nic_cap->dynamic_qp = dev_cap->ovs_dq_en;
	}

	if (dev_cap->nic_lro_en)
		nic_cap->lro_en = true;
	else
		nic_cap->lro_en = false;

	nic_cap->lro_sz = dev_cap->nic_lro_sz;
	nic_cap->tso_sz = dev_cap->nic_tso_sz;

	sdk_info(hwdev->dev_hdl, "L2nic resource capbility, max_sqs=0x%x, max_rqs=0x%x, vf_max_sqs=0x%x, vf_max_rqs=0x%x, max_queue_allowed=0x%x\n",
		 nic_cap->max_sqs, nic_cap->max_rqs,
		 nic_cap->vf_max_sqs, nic_cap->vf_max_rqs,
		 nic_cap->max_queue_allowed);

	/* Check parameters from firmware */
	if (nic_cap->max_sqs > HINIC_CFG_MAX_QP ||
	    nic_cap->max_rqs > HINIC_CFG_MAX_QP) {
		sdk_info(hwdev->dev_hdl, "Number of qp exceed limit[1-%d]: sq: %d, rq: %d\n",
			 HINIC_CFG_MAX_QP, nic_cap->max_sqs, nic_cap->max_rqs);
		nic_cap->max_sqs = HINIC_CFG_MAX_QP;
		nic_cap->max_rqs = HINIC_CFG_MAX_QP;
	}
}

static void parse_roce_res_cap(struct hinic_hwdev *hwdev,
			       struct service_cap *cap,
			       struct hinic_dev_cap *dev_cap,
			       enum func_type type)
{
	struct dev_roce_svc_own_cap *roce_cap =
		&cap->rdma_cap.dev_rdma_cap.roce_own_cap;

	roce_cap->max_qps = dev_cap->roce_max_qp;
	roce_cap->max_cqs = dev_cap->roce_max_cq;
	roce_cap->max_srqs = dev_cap->roce_max_srq;
	roce_cap->max_mpts = dev_cap->roce_max_mpt;
	roce_cap->num_cos = dev_cap->max_cos_id + 1;

	/* PF/PPF */
	if (type == TYPE_PF || type == TYPE_PPF) {
		roce_cap->vf_max_qps = dev_cap->roce_vf_max_qp;
		roce_cap->vf_max_cqs = dev_cap->roce_vf_max_cq;
		roce_cap->vf_max_srqs = dev_cap->roce_vf_max_srq;
		roce_cap->vf_max_mpts = dev_cap->roce_vf_max_mpt;
	} else {
		roce_cap->vf_max_qps = 0;
		roce_cap->vf_max_cqs = 0;
		roce_cap->vf_max_srqs = 0;
		roce_cap->vf_max_mpts = 0;
	}

	roce_cap->cmtt_cl_start = dev_cap->roce_cmtt_cl_start;
	roce_cap->cmtt_cl_end = dev_cap->roce_cmtt_cl_end;
	roce_cap->cmtt_cl_sz = dev_cap->roce_cmtt_cl_size;

	roce_cap->dmtt_cl_start = dev_cap->roce_dmtt_cl_start;
	roce_cap->dmtt_cl_end = dev_cap->roce_dmtt_cl_end;
	roce_cap->dmtt_cl_sz = dev_cap->roce_dmtt_cl_size;

	roce_cap->wqe_cl_start = dev_cap->roce_wqe_cl_start;
	roce_cap->wqe_cl_end = dev_cap->roce_wqe_cl_end;
	roce_cap->wqe_cl_sz = dev_cap->roce_wqe_cl_size;

	sdk_info(hwdev->dev_hdl, "Get roce resource capbility\n");
	sdk_info(hwdev->dev_hdl, "Max_qps=0x%x, max_cqs=0x%x, max_srqs=0x%x, max_mpts=0x%x\n",
		 roce_cap->max_qps, roce_cap->max_cqs,
		 roce_cap->max_srqs, roce_cap->max_mpts);

	sdk_info(hwdev->dev_hdl, "Vf_max_qps=0x%x, vf_max_cqs=0x%x, vf_max_srqs= 0x%x, vf_max_mpts= 0x%x\n",
		 roce_cap->vf_max_qps, roce_cap->vf_max_cqs,
		 roce_cap->vf_max_srqs, roce_cap->vf_max_mpts);

	sdk_info(hwdev->dev_hdl, "Cmtt_start=0x%x, cmtt_end=0x%x, cmtt_sz=0x%x\n",
		 roce_cap->cmtt_cl_start, roce_cap->cmtt_cl_end,
		 roce_cap->cmtt_cl_sz);

	sdk_info(hwdev->dev_hdl, "Dmtt_start=0x%x, dmtt_end=0x%x, dmtt_sz=0x%x\n",
		 roce_cap->dmtt_cl_start, roce_cap->dmtt_cl_end,
		 roce_cap->dmtt_cl_sz);

	sdk_info(hwdev->dev_hdl, "Wqe_start=0x%x, wqe_end=0x%x, wqe_sz=0x%x\n",
		 roce_cap->wqe_cl_start, roce_cap->wqe_cl_end,
		 roce_cap->wqe_cl_sz);

	if (roce_cap->max_qps == 0) {
		roce_cap->max_qps = 1024;
		roce_cap->max_cqs = 2048;
		roce_cap->max_srqs = 1024;
		roce_cap->max_mpts = 1024;

		if (type == TYPE_PF || type == TYPE_PPF) {
			roce_cap->vf_max_qps = 512;
			roce_cap->vf_max_cqs = 1024;
			roce_cap->vf_max_srqs = 512;
			roce_cap->vf_max_mpts = 512;
		}
	}
}

static void parse_iwarp_res_cap(struct hinic_hwdev *hwdev,
				struct service_cap *cap,
				struct hinic_dev_cap *dev_cap,
				enum func_type type)

{
	struct dev_iwarp_svc_own_cap *iwarp_cap =
		&cap->rdma_cap.dev_rdma_cap.iwarp_own_cap;

	iwarp_cap->max_qps = dev_cap->iwarp_max_qp;
	iwarp_cap->max_cqs = dev_cap->iwarp_max_cq;
	iwarp_cap->max_mpts = dev_cap->iwarp_max_mpt;
	iwarp_cap->num_cos = dev_cap->max_cos_id + 1;

	/* PF/PPF */
	if (type == TYPE_PF || type == TYPE_PPF) {
		iwarp_cap->vf_max_qps = dev_cap->iwarp_vf_max_qp;
		iwarp_cap->vf_max_cqs = dev_cap->iwarp_vf_max_cq;
		iwarp_cap->vf_max_mpts = dev_cap->iwarp_vf_max_mpt;
	} else {
		iwarp_cap->vf_max_qps = 0;
		iwarp_cap->vf_max_cqs = 0;
		iwarp_cap->vf_max_mpts = 0;
	}

	iwarp_cap->cmtt_cl_start = dev_cap->iwarp_cmtt_cl_start;
	iwarp_cap->cmtt_cl_end = dev_cap->iwarp_cmtt_cl_end;
	iwarp_cap->cmtt_cl_sz = dev_cap->iwarp_cmtt_cl_size;

	iwarp_cap->dmtt_cl_start = dev_cap->iwarp_dmtt_cl_start;
	iwarp_cap->dmtt_cl_end = dev_cap->iwarp_dmtt_cl_end;
	iwarp_cap->dmtt_cl_sz = dev_cap->iwarp_dmtt_cl_size;

	iwarp_cap->wqe_cl_start = dev_cap->iwarp_wqe_cl_start;
	iwarp_cap->wqe_cl_end = dev_cap->iwarp_wqe_cl_end;
	iwarp_cap->wqe_cl_sz = dev_cap->iwarp_wqe_cl_size;

	sdk_info(hwdev->dev_hdl, "Get iwrap resource capbility\n");
	sdk_info(hwdev->dev_hdl, "Max_qps=0x%x, max_cqs=0x%x, max_mpts=0x%x\n",
		 iwarp_cap->max_qps, iwarp_cap->max_cqs,
		 iwarp_cap->max_mpts);
	sdk_info(hwdev->dev_hdl, "Vf_max_qps=0x%x, vf_max_cqs=0x%x, vf_max_mpts=0x%x\n",
		 iwarp_cap->vf_max_qps, iwarp_cap->vf_max_cqs,
		 iwarp_cap->vf_max_mpts);

	sdk_info(hwdev->dev_hdl, "Cmtt_start=0x%x, cmtt_end=0x%x, cmtt_sz=0x%x\n",
		 iwarp_cap->cmtt_cl_start, iwarp_cap->cmtt_cl_end,
		 iwarp_cap->cmtt_cl_sz);

	sdk_info(hwdev->dev_hdl, "Dmtt_start=0x%x, dmtt_end=0x%x, dmtt_sz=0x%x\n",
		 iwarp_cap->dmtt_cl_start, iwarp_cap->dmtt_cl_end,
		 iwarp_cap->dmtt_cl_sz);

	sdk_info(hwdev->dev_hdl, "Wqe_start=0x%x, wqe_end=0x%x, wqe_sz=0x%x\n",
		 iwarp_cap->wqe_cl_start, iwarp_cap->wqe_cl_end,
		 iwarp_cap->wqe_cl_sz);

	if (iwarp_cap->max_qps == 0) {
		iwarp_cap->max_qps = 8;
		iwarp_cap->max_cqs = 16;
		iwarp_cap->max_mpts = 8;

		if (type == TYPE_PF || type == TYPE_PPF) {
			iwarp_cap->vf_max_qps = 8;
			iwarp_cap->vf_max_cqs = 16;
			iwarp_cap->vf_max_mpts = 8;
		}
	}
}

static void parse_fcoe_res_cap(struct hinic_hwdev *hwdev,
			       struct service_cap *cap,
			       struct hinic_dev_cap *dev_cap,
			       enum func_type type)
{
	struct dev_fcoe_svc_cap *fcoe_cap = &cap->fcoe_cap.dev_fcoe_cap;

	fcoe_cap->max_qps = dev_cap->fcoe_max_qp;
	fcoe_cap->max_cqs = dev_cap->fcoe_max_cq;
	fcoe_cap->max_srqs = dev_cap->fcoe_max_srq;
	fcoe_cap->max_cctxs = dev_cap->fcoe_max_cctx;
	fcoe_cap->cctxs_id_start = dev_cap->fcoe_cctx_id_start;
	fcoe_cap->vp_id_start = dev_cap->fcoe_vp_id_start;
	fcoe_cap->vp_id_end = dev_cap->fcoe_vp_id_end;

	sdk_info(hwdev->dev_hdl, "Get fcoe resource capbility\n");
	sdk_info(hwdev->dev_hdl, "Max_qps=0x%x, max_cqs=0x%x, max_srqs=0x%x, max_cctxs=0x%x, cctxs_id_start=0x%x\n",
		 fcoe_cap->max_qps, fcoe_cap->max_cqs, fcoe_cap->max_srqs,
		 fcoe_cap->max_cctxs, fcoe_cap->cctxs_id_start);
	sdk_info(hwdev->dev_hdl, "Vp_id_start=0x%x, vp_id_end=0x%x\n",
		 fcoe_cap->vp_id_start, fcoe_cap->vp_id_end);
}

static void parse_toe_res_cap(struct hinic_hwdev *hwdev,
			      struct service_cap *cap,
			      struct hinic_dev_cap *dev_cap,
			      enum func_type type)
{
	struct dev_toe_svc_cap *toe_cap = &cap->toe_cap.dev_toe_cap;

	toe_cap->max_pctxs = dev_cap->toe_max_pctx;
	toe_cap->max_cqs = dev_cap->toe_max_cq;
	toe_cap->max_srqs = dev_cap->toe_max_srq;
	toe_cap->srq_id_start = dev_cap->toe_srq_id_start;
	toe_cap->num_cos = dev_cap->max_cos_id + 1;

	sdk_info(hwdev->dev_hdl, "Get toe resource capbility, max_pctxs=0x%x, max_cqs=0x%x, max_srqs=0x%x, srq_id_start=0x%x\n",
		 toe_cap->max_pctxs, toe_cap->max_cqs, toe_cap->max_srqs,
		 toe_cap->srq_id_start);
}

static void parse_fc_res_cap(struct hinic_hwdev *hwdev,
			     struct service_cap *cap,
			     struct hinic_dev_cap *dev_cap,
			     enum func_type type)
{
	struct dev_fc_svc_cap *fc_cap = &cap->fc_cap.dev_fc_cap;

	fc_cap->max_parent_qpc_num = dev_cap->fc_max_pctx;
	fc_cap->scq_num = dev_cap->fc_max_scq;
	fc_cap->srq_num = dev_cap->fc_max_srq;
	fc_cap->max_child_qpc_num = dev_cap->fc_max_cctx;
	fc_cap->child_qpc_id_start = dev_cap->fc_cctx_id_start;
	fc_cap->vp_id_start = dev_cap->fc_vp_id_start;
	fc_cap->vp_id_end = dev_cap->fc_vp_id_end;

	sdk_info(hwdev->dev_hdl, "Get fc resource capbility\n");
	sdk_info(hwdev->dev_hdl, "Max_parent_qpc_num=0x%x, scq_num=0x%x, srq_num=0x%x, max_child_qpc_num=0x%x, child_qpc_id_start=0x%x\n",
		 fc_cap->max_parent_qpc_num, fc_cap->scq_num, fc_cap->srq_num,
		 fc_cap->max_child_qpc_num, fc_cap->child_qpc_id_start);
	sdk_info(hwdev->dev_hdl, "Vp_id_start=0x%x, vp_id_end=0x%x\n",
		 fc_cap->vp_id_start, fc_cap->vp_id_end);
}

static void parse_ovs_res_cap(struct hinic_hwdev *hwdev,
			      struct service_cap *cap,
			      struct hinic_dev_cap *dev_cap,
			      enum func_type type)
{
	struct ovs_service_cap *ovs_cap = &cap->ovs_cap;

	ovs_cap->dev_ovs_cap.max_pctxs = dev_cap->ovs_max_qpc;
	ovs_cap->dev_ovs_cap.max_cqs = 0;

	if (type == TYPE_PF || type == TYPE_PPF)
		ovs_cap->dev_ovs_cap.dynamic_qp_en = dev_cap->ovs_dq_en;

	sdk_info(hwdev->dev_hdl, "Get ovs resource capbility, max_qpc: 0x%x\n",
		 ovs_cap->dev_ovs_cap.max_pctxs);
}

static void parse_acl_res_cap(struct service_cap *cap,
			      struct hinic_dev_cap *dev_cap,
			      enum func_type type)
{
	struct acl_service_cap *acl_cap = &cap->acl_cap;

	acl_cap->dev_acl_cap.max_pctxs = 1024 * 1024;
	acl_cap->dev_acl_cap.max_cqs = 8;
}

static void parse_dev_cap(struct hinic_hwdev *dev,
			  struct hinic_dev_cap *dev_cap, enum func_type type)
{
	struct service_cap *cap = &dev->cfg_mgmt->svc_cap;

	/* Public resource */
	parse_pub_res_cap(dev, cap, dev_cap, type);

	/* PPF managed dynamic resource */
	if (type == TYPE_PPF)
		parse_dynamic_share_res_cap(dev, cap, dev_cap, type);

	/* L2 NIC resource */
	if (IS_NIC_TYPE(dev))
		parse_l2nic_res_cap(dev, cap, dev_cap, type);

	/* FCoE/IOE/TOE/FC without virtulization */
	if (type == TYPE_PF || type == TYPE_PPF) {
		if (IS_FC_TYPE(dev))
			parse_fc_res_cap(dev, cap, dev_cap, type);

		if (IS_FCOE_TYPE(dev))
			parse_fcoe_res_cap(dev, cap, dev_cap, type);

		if (IS_TOE_TYPE(dev))
			parse_toe_res_cap(dev, cap, dev_cap, type);
	}

	/* RoCE resource */
	if (IS_ROCE_TYPE(dev))
		parse_roce_res_cap(dev, cap, dev_cap, type);

	/* iWARP resource */
	if (IS_IWARP_TYPE(dev))
		parse_iwarp_res_cap(dev, cap, dev_cap, type);

	if (IS_OVS_TYPE(dev))
		parse_ovs_res_cap(dev, cap, dev_cap, type);

	if (IS_ACL_TYPE(dev))
		parse_acl_res_cap(cap, dev_cap, type);
}

static int get_cap_from_fw(struct hinic_hwdev *dev, enum func_type type)
{
	struct hinic_dev_cap dev_cap = {0};
	u16 out_len = sizeof(dev_cap);
	int err;

	dev_cap.version = HINIC_CMD_VER_FUNC_ID;
	err = hinic_global_func_id_get(dev, &dev_cap.func_id);
	if (err)
		return err;

	sdk_info(dev->dev_hdl, "Get cap from fw, func_idx: %d\n",
		 dev_cap.func_id);

	err = hinic_msg_to_mgmt_sync(dev, HINIC_MOD_CFGM, HINIC_CFG_NIC_CAP,
				     &dev_cap, sizeof(dev_cap),
				     &dev_cap, &out_len, 0);
	if (err || dev_cap.status || !out_len) {
		sdk_err(dev->dev_hdl,
			"Failed to get capability from FW, err: %d, status: 0x%x, out size: 0x%x\n",
			err, dev_cap.status, out_len);
		return -EFAULT;
	}

	parse_dev_cap(dev, &dev_cap, type);
	return 0;
}

static int get_cap_from_pf(struct hinic_hwdev *dev, enum func_type type)
{
	struct hinic_dev_cap dev_cap = {0};
	u16 in_len, out_len;
	int err;

	in_len = sizeof(dev_cap);
	out_len = in_len;

	err = hinic_msg_to_mgmt_sync(dev, HINIC_MOD_CFGM, HINIC_CFG_MBOX_CAP,
				     &dev_cap, in_len, &dev_cap, &out_len, 0);
	if (err || dev_cap.status || !out_len) {
		sdk_err(dev->dev_hdl, "Failed to get capability from PF, err: %d, status: 0x%x, out size: 0x%x\n",
			err, dev_cap.status, out_len);
		return -EFAULT;
	}

	parse_dev_cap(dev, &dev_cap, type);
	return 0;
}

static int get_dev_cap(struct hinic_hwdev *dev)
{
	int err;
	enum func_type type = HINIC_FUNC_TYPE(dev);

	switch (type) {
	case TYPE_PF:
	case TYPE_PPF:
		err = get_cap_from_fw(dev, type);
		if (err) {
			sdk_err(dev->dev_hdl, "Failed to get PF/PPF capability\n");
			return err;
		}
		break;
	case TYPE_VF:
		err = get_cap_from_pf(dev, type);
		if (err) {
			sdk_err(dev->dev_hdl, "Failed to get VF capability\n");
			return err;
		}
		break;
	default:
		sdk_err(dev->dev_hdl, "Unsupported PCI Function type: %d\n",
			type);
		return -EINVAL;
	}

	return 0;
}

static void nic_param_fix(struct hinic_hwdev *dev)
{
	struct nic_service_cap *nic_cap = &dev->cfg_mgmt->svc_cap.nic_cap;

	if ((hinic_func_type(dev) == TYPE_VF) &&
	    nic_cap->max_queue_allowed != 0) {
		nic_cap->max_rqs = nic_cap->max_queue_allowed;
		nic_cap->max_sqs = nic_cap->max_queue_allowed;
	}
}

static void rdma_param_fix(struct hinic_hwdev *dev)
{
	struct service_cap *cap = &dev->cfg_mgmt->svc_cap;
	struct rdma_service_cap *rdma_cap = &cap->rdma_cap;
	struct dev_roce_svc_own_cap *roce_cap =
		&rdma_cap->dev_rdma_cap.roce_own_cap;
	struct dev_iwarp_svc_own_cap *iwarp_cap =
		&rdma_cap->dev_rdma_cap.iwarp_own_cap;

	rdma_cap->log_mtt = LOG_MTT_SEG;
	rdma_cap->log_rdmarc = LOG_RDMARC_SEG;
	rdma_cap->reserved_qps = RDMA_RSVD_QPS;
	rdma_cap->max_sq_sg = RDMA_MAX_SQ_SGE;

	/* RoCE */
	if (IS_ROCE_TYPE(dev)) {
		roce_cap->qpc_entry_sz = ROCE_QPC_ENTRY_SZ;
		roce_cap->max_wqes = ROCE_MAX_WQES;
		roce_cap->max_rq_sg = ROCE_MAX_RQ_SGE;
		roce_cap->max_sq_inline_data_sz = ROCE_MAX_SQ_INLINE_DATA_SZ;
		roce_cap->max_rq_desc_sz = ROCE_MAX_RQ_DESC_SZ;
		roce_cap->rdmarc_entry_sz = ROCE_RDMARC_ENTRY_SZ;
		roce_cap->max_qp_init_rdma = ROCE_MAX_QP_INIT_RDMA;
		roce_cap->max_qp_dest_rdma = ROCE_MAX_QP_DEST_RDMA;
		roce_cap->max_srq_wqes = ROCE_MAX_SRQ_WQES;
		roce_cap->reserved_srqs = ROCE_RSVD_SRQS;
		roce_cap->max_srq_sge = ROCE_MAX_SRQ_SGE;
		roce_cap->srqc_entry_sz = ROCE_SRQC_ENTERY_SZ;
		roce_cap->max_msg_sz = ROCE_MAX_MSG_SZ;
	} else {
		iwarp_cap->qpc_entry_sz = IWARP_QPC_ENTRY_SZ;
		iwarp_cap->max_wqes = IWARP_MAX_WQES;
		iwarp_cap->max_rq_sg = IWARP_MAX_RQ_SGE;
		iwarp_cap->max_sq_inline_data_sz = IWARP_MAX_SQ_INLINE_DATA_SZ;
		iwarp_cap->max_rq_desc_sz = IWARP_MAX_RQ_DESC_SZ;
		iwarp_cap->max_irq_depth = IWARP_MAX_IRQ_DEPTH;
		iwarp_cap->irq_entry_size = IWARP_IRQ_ENTRY_SZ;
		iwarp_cap->max_orq_depth = IWARP_MAX_ORQ_DEPTH;
		iwarp_cap->orq_entry_size = IWARP_ORQ_ENTRY_SZ;
		iwarp_cap->max_rtoq_depth = IWARP_MAX_RTOQ_DEPTH;
		iwarp_cap->rtoq_entry_size = IWARP_RTOQ_ENTRY_SZ;
		iwarp_cap->max_ackq_depth = IWARP_MAX_ACKQ_DEPTH;
		iwarp_cap->ackq_entry_size = IWARP_ACKQ_ENTRY_SZ;
		iwarp_cap->max_msg_sz = IWARP_MAX_MSG_SZ;
	}

	rdma_cap->max_sq_desc_sz = RDMA_MAX_SQ_DESC_SZ;
	rdma_cap->wqebb_size = WQEBB_SZ;
	rdma_cap->max_cqes = RDMA_MAX_CQES;
	rdma_cap->reserved_cqs = RDMA_RSVD_CQS;
	rdma_cap->cqc_entry_sz = RDMA_CQC_ENTRY_SZ;
	rdma_cap->cqe_size = RDMA_CQE_SZ;
	rdma_cap->reserved_mrws = RDMA_RSVD_MRWS;
	rdma_cap->mpt_entry_sz = RDMA_MPT_ENTRY_SZ;

	/* 2^8 - 1
	 *	+------------------------+-----------+
	 *	|   4B   |      1M(20b)  | Key(8b)   |
	 *	+------------------------+-----------+
	 * key = 8bit key + 24bit index,
	 * now Lkey of SGE uses 2bit(bit31 and bit30), so key only have 10bit,
	 * we use original 8bits directly for simpilification
	 */
	rdma_cap->max_fmr_maps = 255;
	rdma_cap->num_mtts = (g_rdma_mtts_num > 0 ?
				g_rdma_mtts_num : RDMA_NUM_MTTS);
	rdma_cap->log_mtt_seg = LOG_MTT_SEG;
	rdma_cap->mtt_entry_sz = MTT_ENTRY_SZ;
	rdma_cap->log_rdmarc_seg = LOG_RDMARC_SEG;
	rdma_cap->local_ca_ack_delay = LOCAL_ACK_DELAY;
	rdma_cap->num_ports = RDMA_NUM_PORTS;
	rdma_cap->db_page_size = DB_PAGE_SZ;
	rdma_cap->direct_wqe_size = DWQE_SZ;
	rdma_cap->num_pds = NUM_PD;
	rdma_cap->reserved_pds = RSVD_PD;
	rdma_cap->max_xrcds = MAX_XRCDS;
	rdma_cap->reserved_xrcds = RSVD_XRCDS;
	rdma_cap->max_gid_per_port = MAX_GID_PER_PORT;
	rdma_cap->gid_entry_sz = GID_ENTRY_SZ;
	rdma_cap->reserved_lkey = RSVD_LKEY;
	rdma_cap->num_comp_vectors = (u32)dev->cfg_mgmt->eq_info.num_ceq;
	rdma_cap->page_size_cap = PAGE_SZ_CAP;
	rdma_cap->flags = (RDMA_BMME_FLAG_LOCAL_INV |
			   RDMA_BMME_FLAG_REMOTE_INV |
			   RDMA_BMME_FLAG_FAST_REG_WR |
			   RDMA_DEV_CAP_FLAG_XRC |
			   RDMA_DEV_CAP_FLAG_MEM_WINDOW |
			   RDMA_BMME_FLAG_TYPE_2_WIN |
			   RDMA_BMME_FLAG_WIN_TYPE_2B |
			   RDMA_DEV_CAP_FLAG_ATOMIC);
	rdma_cap->max_frpl_len = MAX_FRPL_LEN;
	rdma_cap->max_pkeys = MAX_PKEYS;
}

static void fcoe_param_fix(struct hinic_hwdev *dev)
{
	struct service_cap *cap = &dev->cfg_mgmt->svc_cap;
	struct fcoe_service_cap *fcoe_cap = &cap->fcoe_cap;

	fcoe_cap->qpc_basic_size = FCOE_PCTX_SZ;
	fcoe_cap->childc_basic_size = FCOE_CCTX_SZ;
	fcoe_cap->sqe_size = FCOE_SQE_SZ;

	fcoe_cap->scqc_basic_size = FCOE_SCQC_SZ;
	fcoe_cap->scqe_size = FCOE_SCQE_SZ;

	fcoe_cap->srqc_size = FCOE_SRQC_SZ;
	fcoe_cap->srqe_size = FCOE_SRQE_SZ;
}

static void toe_param_fix(struct hinic_hwdev *dev)
{
	struct service_cap *cap = &dev->cfg_mgmt->svc_cap;
	struct toe_service_cap *toe_cap = &cap->toe_cap;

	toe_cap->pctx_sz = TOE_PCTX_SZ;
	toe_cap->scqc_sz = TOE_CQC_SZ;
}

static void fc_param_fix(struct hinic_hwdev *dev)
{
	struct service_cap *cap = &dev->cfg_mgmt->svc_cap;
	struct fc_service_cap *fc_cap = &cap->fc_cap;

	fc_cap->parent_qpc_size = FC_PCTX_SZ;
	fc_cap->child_qpc_size = FC_CCTX_SZ;
	fc_cap->sqe_size = FC_SQE_SZ;

	fc_cap->scqc_size = FC_SCQC_SZ;
	fc_cap->scqe_size = FC_SCQE_SZ;

	fc_cap->srqc_size = FC_SRQC_SZ;
	fc_cap->srqe_size = FC_SRQE_SZ;
}

static void ovs_param_fix(struct hinic_hwdev *dev)
{
	struct service_cap *cap = &dev->cfg_mgmt->svc_cap;
	struct ovs_service_cap *ovs_cap = &cap->ovs_cap;

	ovs_cap->pctx_sz = OVS_PCTX_SZ;
	ovs_cap->scqc_sz = OVS_SCQC_SZ;
}

static void acl_param_fix(struct hinic_hwdev *dev)
{
	struct service_cap *cap = &dev->cfg_mgmt->svc_cap;
	struct acl_service_cap *acl_cap = &cap->acl_cap;

	acl_cap->pctx_sz = ACL_PCTX_SZ;
	acl_cap->scqc_sz = ACL_SCQC_SZ;
}

static void init_service_param(struct hinic_hwdev *dev)
{
	if (IS_NIC_TYPE(dev))
		nic_param_fix(dev);

	if (IS_RDMA_TYPE(dev))
		rdma_param_fix(dev);

	if (IS_FCOE_TYPE(dev))
		fcoe_param_fix(dev);

	if (IS_TOE_TYPE(dev))
		toe_param_fix(dev);

	if (IS_FC_TYPE(dev))
		fc_param_fix(dev);

	if (IS_OVS_TYPE(dev))
		ovs_param_fix(dev);

	if (IS_ACL_TYPE(dev))
		acl_param_fix(dev);
}

static void cfg_get_eq_num(struct hinic_hwdev *dev)
{
	struct cfg_eq_info *eq_info = &dev->cfg_mgmt->eq_info;

	eq_info->num_ceq = dev->hwif->attr.num_ceqs;
	eq_info->num_ceq_remain = eq_info->num_ceq;
}

static int cfg_init_eq(struct hinic_hwdev *dev)
{
	struct cfg_mgmt_info *cfg_mgmt = dev->cfg_mgmt;
	struct cfg_eq *eq;
	u8 num_ceq, i = 0;

	cfg_get_eq_num(dev);
	num_ceq = cfg_mgmt->eq_info.num_ceq;

	sdk_info(dev->dev_hdl, "Cfg mgmt: ceqs=0x%x, remain=0x%x\n",
		 cfg_mgmt->eq_info.num_ceq, cfg_mgmt->eq_info.num_ceq_remain);

	if (!num_ceq) {
		sdk_err(dev->dev_hdl, "Ceq num cfg in fw is zero\n");
		return -EFAULT;
	}
	eq = kcalloc(num_ceq, sizeof(*eq), GFP_KERNEL);
	if (!eq)
		return -ENOMEM;

	for (i = 0; i < num_ceq; ++i) {
		eq[i].eqn = i;
		eq[i].free = CFG_FREE;
		eq[i].type = SERVICE_T_MAX;
	}

	cfg_mgmt->eq_info.eq = eq;
	mutex_init(&cfg_mgmt->eq_info.eq_mutex);

	return 0;
}

int hinic_dev_ver_info(void *hwdev, struct dev_version_info *ver)
{
	struct hinic_hwdev *dev = hwdev;
	struct cfg_mgmt_info *cfg_mgmt;

	if (!hwdev || !ver)
		return -EINVAL;

	cfg_mgmt = dev->cfg_mgmt;

	memcpy(ver, &cfg_mgmt->svc_cap.dev_ver_info, sizeof(*ver));

	return 0;
}
EXPORT_SYMBOL(hinic_dev_ver_info);

int hinic_vector_to_eqn(void *hwdev, enum hinic_service_type type, int vector)
{
	struct hinic_hwdev *dev = hwdev;
	struct cfg_mgmt_info *cfg_mgmt;
	struct cfg_eq *eq;
	int eqn = -EINVAL;

	if (!hwdev || vector < 0)
		return -EINVAL;

	if (type != SERVICE_T_ROCE && type != SERVICE_T_IWARP) {
		sdk_err(dev->dev_hdl,
			"Service type: %d, only RDMA service could get eqn by vector\n",
			type);
		return -EINVAL;
	}

	cfg_mgmt = dev->cfg_mgmt;
	vector = (vector % cfg_mgmt->eq_info.num_ceq) + CFG_RDMA_CEQ_BASE;

	eq = cfg_mgmt->eq_info.eq;
	if ((eq[vector].type == SERVICE_T_ROCE ||
	     eq[vector].type == SERVICE_T_IWARP) &&
	     eq[vector].free == CFG_BUSY)
		eqn = eq[vector].eqn;

	return eqn;
}
EXPORT_SYMBOL(hinic_vector_to_eqn);

static int cfg_init_interrupt(struct hinic_hwdev *dev)
{
	struct cfg_mgmt_info *cfg_mgmt = dev->cfg_mgmt;
	struct cfg_irq_info *irq_info = &cfg_mgmt->irq_param_info;
	u16 intr_num = dev->hwif->attr.num_irqs;

	if (!intr_num) {
		sdk_err(dev->dev_hdl, "Irq num cfg in fw is zero\n");
		return -EFAULT;
	}
	irq_info->alloc_info = kcalloc(intr_num, sizeof(*irq_info->alloc_info),
				       GFP_KERNEL);
	if (!irq_info->alloc_info)
		return -ENOMEM;

	irq_info->num_irq_hw = intr_num;

	/* Production requires VF only surppots MSI-X */
	if (HINIC_FUNC_TYPE(dev) == TYPE_VF)
		cfg_mgmt->svc_cap.interrupt_type = INTR_TYPE_MSIX;
	else
		cfg_mgmt->svc_cap.interrupt_type = intr_mode;
	mutex_init(&irq_info->irq_mutex);
	return 0;
}

static int cfg_enable_interrupt(struct hinic_hwdev *dev)
{
	struct cfg_mgmt_info *cfg_mgmt = dev->cfg_mgmt;
	u16 nreq = cfg_mgmt->irq_param_info.num_irq_hw;

	void *pcidev = dev->pcidev_hdl;
	struct irq_alloc_info_st *irq_info;
	struct msix_entry *entry;
	u16 i = 0;
	int actual_irq;

	irq_info = cfg_mgmt->irq_param_info.alloc_info;

	sdk_info(dev->dev_hdl, "Interrupt type: %d, irq num: %d\n",
		 cfg_mgmt->svc_cap.interrupt_type, nreq);

	switch (cfg_mgmt->svc_cap.interrupt_type) {
	case INTR_TYPE_MSIX:
		if (!nreq) {
			sdk_err(dev->dev_hdl, "Interrupt number cannot be zero\n");
			return -EINVAL;
		}
		entry = kcalloc(nreq, sizeof(*entry), GFP_KERNEL);
		if (!entry)
			return -ENOMEM;

		for (i = 0; i < nreq; i++)
			entry[i].entry = i;

		actual_irq = pci_enable_msix_range(pcidev, entry,
						   VECTOR_THRESHOLD, nreq);
		if (actual_irq < 0) {
			sdk_err(dev->dev_hdl, "Alloc msix entries with threshold 2 failed\n");
			kfree(entry);
			return -ENOMEM;
		}

		nreq = (u16)actual_irq;
		cfg_mgmt->irq_param_info.num_total = nreq;
		cfg_mgmt->irq_param_info.num_irq_remain = nreq;
		sdk_info(dev->dev_hdl, "Request %d msix vector success\n",
			 nreq);

		for (i = 0; i < nreq; ++i) {
			/* u16 driver uses to specify entry, OS writes */
			irq_info[i].info.msix_entry_idx = entry[i].entry;
			/* u32 kernel uses to write allocated vector */
			irq_info[i].info.irq_id = entry[i].vector;
			irq_info[i].type = SERVICE_T_MAX;
			irq_info[i].free = CFG_FREE;
		}

		kfree(entry);

		break;

	default:
		sdk_err(dev->dev_hdl, "Unsupport interrupt type %d\n",
			cfg_mgmt->svc_cap.interrupt_type);
		break;
	}

	return 0;
}

int hinic_alloc_irqs(void *hwdev, enum hinic_service_type type, u16 num,
		     struct irq_info *irq_info_array, u16 *act_num)
{
	struct hinic_hwdev *dev = hwdev;
	struct cfg_mgmt_info *cfg_mgmt;
	struct cfg_irq_info *irq_info;
	struct irq_alloc_info_st *alloc_info;
	int max_num_irq;
	u16 free_num_irq;
	int i, j;

	if (!hwdev || !irq_info_array || !act_num)
		return -EINVAL;

	cfg_mgmt = dev->cfg_mgmt;
	irq_info = &cfg_mgmt->irq_param_info;
	alloc_info = irq_info->alloc_info;
	max_num_irq = irq_info->num_total;
	free_num_irq = irq_info->num_irq_remain;

	mutex_lock(&irq_info->irq_mutex);

	if (num > free_num_irq) {
		if (free_num_irq == 0) {
			sdk_err(dev->dev_hdl,
				"no free irq resource in cfg mgmt\n");
			mutex_unlock(&irq_info->irq_mutex);
			return -ENOMEM;
		}

		sdk_warn(dev->dev_hdl, "only %d irq resource in cfg mgmt\n",
			 free_num_irq);
		num = free_num_irq;
	}

	*act_num = 0;

	for (i = 0; i < num; i++) {
		for (j = 0; j < max_num_irq; j++) {
			if (alloc_info[j].free == CFG_FREE) {
				if (irq_info->num_irq_remain == 0) {
					sdk_err(dev->dev_hdl, "No free irq resource in cfg mgmt\n");
					mutex_unlock(&irq_info->irq_mutex);
					return -EINVAL;
				}
				alloc_info[j].type = type;
				alloc_info[j].free = CFG_BUSY;

				irq_info_array[i].msix_entry_idx =
					alloc_info[j].info.msix_entry_idx;
				irq_info_array[i].irq_id =
					alloc_info[j].info.irq_id;
				(*act_num)++;
				irq_info->num_irq_remain--;

				break;
			}
		}
	}

	mutex_unlock(&irq_info->irq_mutex);
	return 0;
}
EXPORT_SYMBOL(hinic_alloc_irqs);

void hinic_free_irq(void *hwdev, enum hinic_service_type type, u32 irq_id)
{
	struct hinic_hwdev *dev = hwdev;
	struct cfg_mgmt_info *cfg_mgmt;
	struct cfg_irq_info *irq_info;
	struct irq_alloc_info_st *alloc_info;
	int max_num_irq;
	int i;

	if (!hwdev)
		return;

	cfg_mgmt = dev->cfg_mgmt;
	irq_info = &cfg_mgmt->irq_param_info;
	alloc_info = irq_info->alloc_info;
	max_num_irq = irq_info->num_total;

	mutex_lock(&irq_info->irq_mutex);

	for (i = 0; i < max_num_irq; i++) {
		if (irq_id == alloc_info[i].info.irq_id &&
		    type == alloc_info[i].type) {
			if (alloc_info[i].free == CFG_BUSY) {
				alloc_info[i].free = CFG_FREE;
				irq_info->num_irq_remain++;
				if (irq_info->num_irq_remain > max_num_irq) {
					sdk_err(dev->dev_hdl, "Find target, but over range\n");
					mutex_unlock(&irq_info->irq_mutex);
					return;
				}
				break;
			}
		}
	}

	if (i >= max_num_irq)
		sdk_warn(dev->dev_hdl, "Irq %d don't need to free\n", irq_id);

	mutex_unlock(&irq_info->irq_mutex);
}
EXPORT_SYMBOL(hinic_free_irq);

int hinic_vector_to_irq(void *hwdev, enum hinic_service_type type, int vector)
{
	struct hinic_hwdev *dev = hwdev;
	struct cfg_mgmt_info *cfg_mgmt;
	struct irq_alloc_info_st *irq_info;
	int irq = -EINVAL;

	if (!hwdev)
		return -EINVAL;

	cfg_mgmt = dev->cfg_mgmt;
	if (type != SERVICE_T_ROCE && type != SERVICE_T_IWARP) {
		sdk_err(dev->dev_hdl,
			"Service type: %u, only RDMA service could get eqn by vector\n",
			type);
		return -EINVAL;
	}

	/* Current RDMA CEQ are 2 - 31, will change in the future */
	vector = ((vector % cfg_mgmt->eq_info.num_ceq) + CFG_RDMA_CEQ_BASE);

	irq_info = cfg_mgmt->irq_param_info.alloc_info;
	if (irq_info[vector].type == SERVICE_T_ROCE ||
	    irq_info[vector].type == SERVICE_T_IWARP)
		if (irq_info[vector].free == CFG_BUSY)
			irq = (int)irq_info[vector].info.irq_id;

	return irq;
}
EXPORT_SYMBOL(hinic_vector_to_irq);

int hinic_alloc_ceqs(void *hwdev, enum hinic_service_type type, int num,
		     int *ceq_id_array, int *act_num)
{
	struct hinic_hwdev *dev = hwdev;
	struct cfg_mgmt_info *cfg_mgmt;
	struct cfg_eq_info *eq;
	int free_ceq;
	int i, j;

	if (!hwdev || !ceq_id_array || !act_num)
		return -EINVAL;

	cfg_mgmt = dev->cfg_mgmt;
	eq = &cfg_mgmt->eq_info;
	free_ceq = eq->num_ceq_remain;

	mutex_lock(&eq->eq_mutex);

	if (num > free_ceq) {
		if (free_ceq <= 0) {
			sdk_err(dev->dev_hdl, "No free ceq resource in cfg mgmt\n");
			mutex_unlock(&eq->eq_mutex);
			return -ENOMEM;
		}

		sdk_warn(dev->dev_hdl, "Only %d ceq resource in cfg mgmt\n",
			 free_ceq);
	}

	*act_num = 0;

	num = min(num, eq->num_ceq - CFG_RDMA_CEQ_BASE);
	for (i = 0; i < num; i++) {
		if (eq->num_ceq_remain == 0) {
			sdk_warn(dev->dev_hdl, "Alloc %d ceqs, less than required %d ceqs\n",
				 *act_num, num);
			mutex_unlock(&eq->eq_mutex);
			return 0;
		}

		for (j = CFG_RDMA_CEQ_BASE; j < eq->num_ceq; j++) {
			if (eq->eq[j].free == CFG_FREE) {
				eq->eq[j].type = type;
				eq->eq[j].free = CFG_BUSY;
				eq->num_ceq_remain--;
				ceq_id_array[i] = eq->eq[j].eqn;
				(*act_num)++;
				break;
			}
		}
	}

	mutex_unlock(&eq->eq_mutex);
	return 0;
}
EXPORT_SYMBOL(hinic_alloc_ceqs);

void hinic_free_ceq(void *hwdev, enum hinic_service_type type, int ceq_id)
{
	struct hinic_hwdev *dev = hwdev;
	struct cfg_mgmt_info *cfg_mgmt;
	struct cfg_eq_info *eq;
	u8 num_ceq;
	u8 i = 0;

	if (!hwdev)
		return;

	cfg_mgmt = dev->cfg_mgmt;
	eq = &cfg_mgmt->eq_info;
	num_ceq = eq->num_ceq;

	mutex_lock(&eq->eq_mutex);

	for (i = 0; i < num_ceq; i++) {
		if (ceq_id == eq->eq[i].eqn &&
		    type == cfg_mgmt->eq_info.eq[i].type) {
			if (eq->eq[i].free == CFG_BUSY) {
				eq->eq[i].free = CFG_FREE;
				eq->num_ceq_remain++;
				if (eq->num_ceq_remain > num_ceq)
					eq->num_ceq_remain %= num_ceq;

				mutex_unlock(&eq->eq_mutex);
				return;
			}
		}
	}

	if (i >= num_ceq)
		sdk_warn(dev->dev_hdl, "ceq %d don't need to free\n", ceq_id);

	mutex_unlock(&eq->eq_mutex);
}
EXPORT_SYMBOL(hinic_free_ceq);

static int cfg_mbx_pf_proc_vf_msg(void *hwdev, u16 vf_id, u8 cmd, void *buf_in,
				  u16 in_size, void *buf_out, u16 *out_size)
{
	struct hinic_hwdev *dev = hwdev;
	struct hinic_dev_cap *dev_cap = buf_out;
	struct service_cap *cap = &dev->cfg_mgmt->svc_cap;
	struct nic_service_cap *nic_cap = &cap->nic_cap;
	struct dev_roce_svc_own_cap *roce_cap =
		&cap->rdma_cap.dev_rdma_cap.roce_own_cap;
	struct dev_iwarp_svc_own_cap *iwarp_cap =
		&cap->rdma_cap.dev_rdma_cap.iwarp_own_cap;
	struct dev_ovs_svc_cap *ovs_cap = &cap->ovs_cap.dev_ovs_cap;
	struct hinic_dev_cap dev_cap_tmp = {0};
	u16 out_len = 0;
	u16 func_id;
	int err;

	memset(dev_cap, 0, sizeof(*dev_cap));

	if (cap->sf_svc_attr.ft_en)
		dev_cap->sf_svc_attr |= SF_SVC_FT_BIT;
	else
		dev_cap->sf_svc_attr &= ~SF_SVC_FT_BIT;

	if (cap->sf_svc_attr.rdma_en)
		dev_cap->sf_svc_attr |= SF_SVC_RDMA_BIT;
	else
		dev_cap->sf_svc_attr &= ~SF_SVC_RDMA_BIT;

	dev_cap->sf_en_vf = cap->sf_svc_attr.sf_en_vf;

	dev_cap->host_id = cap->host_id;
	dev_cap->ep_id = cap->ep_id;
	dev_cap->intr_type = cap->interrupt_type;
	dev_cap->max_cos_id = cap->max_cos_id;
	dev_cap->er_id = cap->er_id;
	dev_cap->port_id = cap->port_id;
	dev_cap->max_vf = cap->max_vf;
	dev_cap->svc_cap_en = cap->chip_svc_type;
	dev_cap->host_total_func = cap->host_total_function;
	dev_cap->host_oq_id_mask_val = cap->host_oq_id_mask_val;
	dev_cap->net_port_mode = cap->net_port_mode;

	/* Parameters below is uninitialized because NIC and ROCE not use it
	 * max_connect_num
	 * max_stick2cache_num
	 * bfilter_start_addr
	 * bfilter_len
	 * hash_bucket_num
	 * cfg_file_ver
	 */

	/* NIC VF resources */
	dev_cap->nic_max_sq = nic_cap->vf_max_sqs;
	dev_cap->nic_max_rq = nic_cap->vf_max_rqs;

	/* ROCE VF resources */
	dev_cap->roce_max_qp = roce_cap->vf_max_qps;
	dev_cap->roce_max_cq = roce_cap->vf_max_cqs;
	dev_cap->roce_max_srq = roce_cap->vf_max_srqs;
	dev_cap->roce_max_mpt = roce_cap->vf_max_mpts;

	dev_cap->roce_cmtt_cl_start = roce_cap->cmtt_cl_start;
	dev_cap->roce_cmtt_cl_end = roce_cap->cmtt_cl_end;
	dev_cap->roce_cmtt_cl_size = roce_cap->cmtt_cl_sz;

	dev_cap->roce_dmtt_cl_start = roce_cap->dmtt_cl_start;
	dev_cap->roce_dmtt_cl_end = roce_cap->dmtt_cl_end;
	dev_cap->roce_dmtt_cl_size = roce_cap->dmtt_cl_sz;

	dev_cap->roce_wqe_cl_start = roce_cap->wqe_cl_start;
	dev_cap->roce_wqe_cl_end = roce_cap->wqe_cl_end;
	dev_cap->roce_wqe_cl_size = roce_cap->wqe_cl_sz;

	/* Iwarp VF resources */
	dev_cap->iwarp_max_qp = iwarp_cap->vf_max_qps;
	dev_cap->iwarp_max_cq = iwarp_cap->vf_max_cqs;
	dev_cap->iwarp_max_mpt = iwarp_cap->vf_max_mpts;

	/* OVS VF resources */
	dev_cap->ovs_max_qpc = ovs_cap->max_pctxs;
	dev_cap->ovs_dq_en = ovs_cap->dynamic_qp_en;

	*out_size = sizeof(*dev_cap);

	if (!IS_OVS_TYPE(dev))
		return 0;

	out_len = sizeof(dev_cap_tmp);
	/* fixed qnum in ovs mode */
	func_id = vf_id + hinic_glb_pf_vf_offset(hwdev);
	dev_cap_tmp.func_id = func_id;
	err = hinic_pf_msg_to_mgmt_sync(dev, HINIC_MOD_CFGM, HINIC_CFG_FUNC_CAP,
					&dev_cap_tmp, sizeof(dev_cap_tmp),
					&dev_cap_tmp, &out_len, 0);
	if (err && err != HINIC_DEV_BUSY_ACTIVE_FW &&
	    err != HINIC_MBOX_PF_BUSY_ACTIVE_FW) {
		sdk_err(dev->dev_hdl,
			"Get func_id: %u capability from FW failed, err: %d, status: 0x%x, out_size: 0x%x\n",
			func_id, err, dev_cap_tmp.status, out_len);
		return -EFAULT;
	} else if (err) {
		return err;
	}

	dev_cap->nic_max_sq = dev_cap_tmp.nic_max_sq + 1;
	dev_cap->nic_max_rq = dev_cap_tmp.nic_max_rq + 1;
	dev_cap->max_queue_allowed = dev_cap_tmp.max_queue_allowed;

	sdk_info(dev->dev_hdl, "func_id(%u) %s qnum %u max_queue_allowed %u\n",
		 func_id, (ovs_cap->dynamic_qp_en ? "dynamic" : "fixed"),
		 dev_cap->nic_max_sq, dev_cap->max_queue_allowed);

	return 0;
}

static int cfg_mbx_ppf_proc_msg(void *hwdev, u16 pf_id, u16 vf_id, u8 cmd,
				void *buf_in, u16 in_size, void *buf_out,
				u16 *out_size)
{
	struct hinic_hwdev *dev = hwdev;

	sdk_info(dev->dev_hdl, "ppf receive other pf cfgmgmt cmd %d mbox msg\n",
		 cmd);

	return hinic_ppf_process_mbox_msg(hwdev, pf_id, vf_id, HINIC_MOD_CFGM,
					  cmd, buf_in, in_size, buf_out,
					  out_size);
}

static int cfg_mbx_vf_proc_msg(void *hwdev, u8 cmd, void *buf_in, u16 in_size,
			       void *buf_out, u16 *out_size)
{
	struct hinic_hwdev *dev = hwdev;

	*out_size = 0;
	sdk_err(dev->dev_hdl, "VF msg callback not supported\n");

	return -EOPNOTSUPP;
}

static int cfg_mbx_init(struct hinic_hwdev *dev, struct cfg_mgmt_info *cfg_mgmt)
{
	int err;
	enum func_type type = dev->hwif->attr.func_type;

	if (type == TYPE_PF) {
		err = hinic_register_pf_mbox_cb(dev, HINIC_MOD_CFGM,
						cfg_mbx_pf_proc_vf_msg);
		if (err) {
			sdk_err(dev->dev_hdl,
				"PF: Register PF mailbox callback failed\n");
			return err;
		}
	} else if (type == TYPE_PPF) {
		err = hinic_register_ppf_mbox_cb(dev, HINIC_MOD_CFGM,
						 cfg_mbx_ppf_proc_msg);
		if (err) {
			sdk_err(dev->dev_hdl,
				"PPF: Register PPF mailbox callback failed\n");
			return err;
		}

		err = hinic_register_pf_mbox_cb(dev, HINIC_MOD_CFGM,
						cfg_mbx_pf_proc_vf_msg);
		if (err) {
			sdk_err(dev->dev_hdl,
				"PPF: Register PF mailbox callback failed\n");
			hinic_unregister_ppf_mbox_cb(dev, HINIC_MOD_CFGM);
			return err;
		}
	} else if (type == TYPE_VF) {
		err = hinic_register_vf_mbox_cb(dev, HINIC_MOD_CFGM,
						cfg_mbx_vf_proc_msg);
		if (err) {
			sdk_err(dev->dev_hdl,
				"VF: Register VF mailbox callback failed\n");
			return err;
		}
	} else {
		sdk_err(dev->dev_hdl, "Invalid func_type: %d, not supported\n",
			type);
		return -EINVAL;
	}

	return 0;
}

static void cfg_mbx_cleanup(struct hinic_hwdev *dev)
{
	hinic_unregister_ppf_mbox_cb(dev, HINIC_MOD_CFGM);
	hinic_unregister_pf_mbox_cb(dev, HINIC_MOD_CFGM);
	hinic_unregister_vf_mbox_cb(dev, HINIC_MOD_CFGM);
}

static int init_cfg_mgmt(struct hinic_hwdev *dev)
{
	int err;
	struct cfg_mgmt_info *cfg_mgmt;

	cfg_mgmt = kzalloc(sizeof(*cfg_mgmt), GFP_KERNEL);
	if (!cfg_mgmt)
		return -ENOMEM;

	dev->cfg_mgmt = cfg_mgmt;
	cfg_mgmt->hwdev = dev;

	err = cfg_init_eq(dev);
	if (err) {
		sdk_err(dev->dev_hdl, "Failed to init cfg event queue, err: %d\n",
			err);
		goto free_mgmt_mem;
	}

	err = cfg_init_interrupt(dev);
	if (err) {
		sdk_err(dev->dev_hdl, "Failed to init cfg interrupt, err: %d\n",
			err);
		goto free_eq_mem;
	}

	err = cfg_enable_interrupt(dev);
	if (err) {
		sdk_err(dev->dev_hdl, "Failed to enable cfg interrupt, err: %d\n",
			err);
		goto free_interrupt_mem;
	}

	return 0;

free_interrupt_mem:
	kfree(cfg_mgmt->irq_param_info.alloc_info);
	cfg_mgmt->irq_param_info.alloc_info = NULL;

free_eq_mem:
	kfree(cfg_mgmt->eq_info.eq);
	cfg_mgmt->eq_info.eq = NULL;

free_mgmt_mem:
	kfree(cfg_mgmt);
	return err;
}

static void free_cfg_mgmt(struct hinic_hwdev *dev)
{
	struct cfg_mgmt_info *cfg_mgmt = dev->cfg_mgmt;

	/* if the allocated resource were recycled */
	if (cfg_mgmt->irq_param_info.num_irq_remain !=
	    cfg_mgmt->irq_param_info.num_total ||
	    cfg_mgmt->eq_info.num_ceq_remain != cfg_mgmt->eq_info.num_ceq)
		sdk_err(dev->dev_hdl, "Can't reclaim all irq and event queue, please check\n");

	switch (cfg_mgmt->svc_cap.interrupt_type) {
	case INTR_TYPE_MSIX:
		pci_disable_msix(dev->pcidev_hdl);
		break;

	case INTR_TYPE_MSI:
		pci_disable_msi(dev->pcidev_hdl);
		break;

	case INTR_TYPE_INT:
	default:
		break;
	}

	kfree(cfg_mgmt->irq_param_info.alloc_info);
	cfg_mgmt->irq_param_info.alloc_info = NULL;

	kfree(cfg_mgmt->eq_info.eq);
	cfg_mgmt->eq_info.eq = NULL;

	kfree(cfg_mgmt);
}

static int init_capability(struct hinic_hwdev *dev)
{
	struct cfg_mgmt_info *cfg_mgmt = dev->cfg_mgmt;
	int err;

	set_cfg_test_param(cfg_mgmt);

	err = cfg_mbx_init(dev, cfg_mgmt);
	if (err) {
		sdk_err(dev->dev_hdl, "Configure mailbox init failed, err: %d\n",
			err);
		return err;
	}

	cfg_mgmt->svc_cap.sf_svc_attr.ft_pf_en = false;
	cfg_mgmt->svc_cap.sf_svc_attr.rdma_pf_en = false;

	err = get_dev_cap(dev);
	if (err) {
		cfg_mbx_cleanup(dev);
		return err;
	}

	init_service_param(dev);

	sdk_info(dev->dev_hdl, "Init capability success\n");
	return 0;
}

static void free_capability(struct hinic_hwdev *dev)
{
	cfg_mbx_cleanup(dev);
	sdk_info(dev->dev_hdl, "Free capability success");
}

/* 0 - MSIx, 1 - MSI, 2 - INTx */
enum intr_type hinic_intr_type(void *hwdev)
{
	struct hinic_hwdev *dev = hwdev;

	if (!hwdev)
		return INTR_TYPE_NONE;

	return dev->cfg_mgmt->svc_cap.interrupt_type;
}
EXPORT_SYMBOL(hinic_intr_type);

bool hinic_support_nic(void *hwdev, struct nic_service_cap *cap)
{
	struct hinic_hwdev *dev = hwdev;

	if (!hwdev)
		return false;

	if (!IS_NIC_TYPE(dev))
		return false;

	if (cap)
		memcpy(cap, &dev->cfg_mgmt->svc_cap.nic_cap, sizeof(*cap));

	return true;
}
EXPORT_SYMBOL(hinic_support_nic);

bool hinic_support_roce(void *hwdev, struct rdma_service_cap *cap)
{
	struct hinic_hwdev *dev = hwdev;

	if (!hwdev)
		return false;

	if (!IS_ROCE_TYPE(dev))
		return false;

	if (cap)
		memcpy(cap, &dev->cfg_mgmt->svc_cap.rdma_cap, sizeof(*cap));

	return true;
}
EXPORT_SYMBOL(hinic_support_roce);

bool hinic_support_fcoe(void *hwdev, struct fcoe_service_cap *cap)
{
	struct hinic_hwdev *dev = hwdev;

	if (!hwdev)
		return false;

	if (!IS_FCOE_TYPE(dev))
		return false;

	if (cap)
		memcpy(cap, &dev->cfg_mgmt->svc_cap.fcoe_cap, sizeof(*cap));

	return true;
}
EXPORT_SYMBOL(hinic_support_fcoe);

/* Only PPF support it, PF is not */
bool hinic_support_toe(void *hwdev, struct toe_service_cap *cap)
{
	struct hinic_hwdev *dev = hwdev;

	if (!hwdev)
		return false;

	if (!IS_TOE_TYPE(dev))
		return false;

	if (cap)
		memcpy(cap, &dev->cfg_mgmt->svc_cap.toe_cap, sizeof(*cap));

	return true;
}
EXPORT_SYMBOL(hinic_support_toe);

bool hinic_support_iwarp(void *hwdev, struct rdma_service_cap *cap)
{
	struct hinic_hwdev *dev = hwdev;

	if (!hwdev)
		return false;

	if (!IS_IWARP_TYPE(dev))
		return false;

	if (cap)
		memcpy(cap, &dev->cfg_mgmt->svc_cap.rdma_cap, sizeof(*cap));

	return true;
}
EXPORT_SYMBOL(hinic_support_iwarp);

bool hinic_support_fc(void *hwdev, struct fc_service_cap *cap)
{
	struct hinic_hwdev *dev = hwdev;

	if (!hwdev)
		return false;

	if (!IS_FC_TYPE(dev))
		return false;

	if (cap)
		memcpy(cap, &dev->cfg_mgmt->svc_cap.fc_cap, sizeof(*cap));

	return true;
}
EXPORT_SYMBOL(hinic_support_fc);

bool hinic_support_fic(void *hwdev)
{
	struct hinic_hwdev *dev = hwdev;

	if (!hwdev)
		return false;

	if (!IS_FIC_TYPE(dev))
		return false;

	return true;
}
EXPORT_SYMBOL(hinic_support_fic);

bool hinic_support_ovs(void *hwdev, struct ovs_service_cap *cap)
{
	struct hinic_hwdev *dev = hwdev;

	if (!hwdev)
		return false;

	if (!IS_OVS_TYPE(dev))
		return false;

	if (cap)
		memcpy(cap, &dev->cfg_mgmt->svc_cap.ovs_cap, sizeof(*cap));

	return true;
}
EXPORT_SYMBOL(hinic_support_ovs);

bool hinic_support_acl(void *hwdev, struct acl_service_cap *cap)
{
	struct hinic_hwdev *dev = hwdev;

	if (!hwdev)
		return false;

	if (!IS_ACL_TYPE(dev))
		return false;

	if (cap)
		memcpy(cap, &dev->cfg_mgmt->svc_cap.acl_cap, sizeof(*cap));

	return true;
}
EXPORT_SYMBOL(hinic_support_acl);

bool hinic_support_rdma(void *hwdev, struct rdma_service_cap *cap)
{
	struct hinic_hwdev *dev = hwdev;

	if (!hwdev)
		return false;

	if (!IS_RDMA_TYPE(dev))
		return false;

	if (cap)
		memcpy(cap, &dev->cfg_mgmt->svc_cap.rdma_cap, sizeof(*cap));

	return true;
}
EXPORT_SYMBOL(hinic_support_rdma);

bool hinic_support_ft(void *hwdev)
{
	struct hinic_hwdev *dev = hwdev;

	if (!hwdev)
		return false;

	if (!IS_FT_TYPE(dev))
		return false;

	return true;
}
EXPORT_SYMBOL(hinic_support_ft);

bool hinic_support_dynamic_q(void *hwdev)
{
	struct hinic_hwdev *dev = hwdev;

	if (!hwdev)
		return false;

	return dev->cfg_mgmt->svc_cap.nic_cap.dynamic_qp ? true : false;
}

bool hinic_func_for_mgmt(void *hwdev)
{
	struct hinic_hwdev *dev = hwdev;

	if (!hwdev)
		return false;

	if (dev->cfg_mgmt->svc_cap.chip_svc_type >= CFG_SVC_NIC_BIT0)
		return false;
	else
		return true;
}

bool hinic_func_for_hwpt(void *hwdev)
{
	struct hinic_hwdev *dev = hwdev;

	if (!hwdev)
		return false;

	if (IS_HWPT_TYPE(dev))
		return true;
	else
		return false;
}

bool hinic_func_for_pt(void *hwdev)
{
	struct hinic_hwdev *dev = hwdev;

	if (!hwdev)
		return false;

	if (dev->cfg_mgmt->svc_cap.force_up)
		return true;
	else
		return false;
}

int cfg_set_func_sf_en(void *hwdev, u32 enbits, u32 enmask)
{
	struct hinic_hwdev *dev = hwdev;
	struct nic_misc_func_sf_enbits *func_sf_enbits;
	u16 out_size = sizeof(*func_sf_enbits);
	u16 glb_func_idx;
	u16 api_info_len;
	int err;

	api_info_len = sizeof(struct nic_misc_func_sf_enbits);
	func_sf_enbits = kzalloc(api_info_len, GFP_KERNEL);
	if (!func_sf_enbits) {
		sdk_err(dev->dev_hdl, "Alloc cfg api info failed\n");
		return -ENOMEM;
	}

	err = hinic_global_func_id_get(dev, &glb_func_idx);
	if (err) {
		kfree(func_sf_enbits);
		return err;
	}

	func_sf_enbits->stateful_enbits = enbits;
	func_sf_enbits->stateful_enmask = enmask;
	func_sf_enbits->function_id = glb_func_idx;

	err = hinic_msg_to_mgmt_sync(hwdev, HINIC_MOD_L2NIC,
				     HINIC_MISC_SET_FUNC_SF_ENBITS,
				     (void *)func_sf_enbits, api_info_len,
				     (void *)func_sf_enbits, &out_size,
				     VSW_UP_CFG_TIMEOUT);
	if (err || !out_size || func_sf_enbits->status) {
		sdk_err(dev->dev_hdl,
			"Failed to set stateful enable, err: %d, status: 0x%x, out_size: 0x%x\n",
			err, func_sf_enbits->status, out_size);
		kfree(func_sf_enbits);
		return -EFAULT;
	}

	kfree(func_sf_enbits);
	return 0;
}

int cfg_get_func_sf_en(void *hwdev, u32 *enbits)
{
	struct nic_misc_func_sf_enbits *func_sf_enbits;
	struct hinic_hwdev *dev = hwdev;
	u16 out_size = sizeof(*func_sf_enbits);
	u16 glb_func_idx;
	u16 api_info_len;
	int err;

	api_info_len   = sizeof(struct nic_misc_func_sf_enbits);
	func_sf_enbits = kzalloc(api_info_len, GFP_KERNEL);
	if (!func_sf_enbits) {
		sdk_err(dev->dev_hdl, "Alloc cfg api info failed\n");
		return -ENOMEM;
	}

	err = hinic_global_func_id_get(dev, &glb_func_idx);
	if (err) {
		kfree(func_sf_enbits);
		return err;
	}

	func_sf_enbits->function_id = glb_func_idx;

	err = hinic_msg_to_mgmt_sync(hwdev, HINIC_MOD_L2NIC,
				     HINIC_MISC_GET_FUNC_SF_ENBITS,
				     (void *)func_sf_enbits, api_info_len,
				     (void *)func_sf_enbits, &out_size,
				     VSW_UP_CFG_TIMEOUT);
	if (err || !out_size || func_sf_enbits->status) {
		sdk_err(dev->dev_hdl, "Failed to get stateful enable, err: %d, status: 0x%x, out_size: 0x%x\n",
			err, func_sf_enbits->status, out_size);
		kfree(func_sf_enbits);
		return -EFAULT;
	}

	*enbits = func_sf_enbits->stateful_enbits;

	kfree(func_sf_enbits);
	return 0;
}

int hinic_set_toe_enable(void *hwdev, bool enable)
{
	u32 enbits;
	u32 enmask;

	if (!hwdev)
		return -EINVAL;

	enbits = VSW_SET_STATEFUL_BITS_TOE((u16)enable);
	enmask = VSW_SET_STATEFUL_BITS_TOE(0x1U);

	return cfg_set_func_sf_en(hwdev, enbits, enmask);
}
EXPORT_SYMBOL(hinic_set_toe_enable);

bool hinic_get_toe_enable(void *hwdev)
{
	int err;
	u32 enbits;

	if (!hwdev)
		return false;

	err = cfg_get_func_sf_en(hwdev, &enbits);
	if (err)
		return false;

	return VSW_GET_STATEFUL_BITS_TOE(enbits);
}
EXPORT_SYMBOL(hinic_get_toe_enable);

int hinic_set_fcoe_enable(void *hwdev, bool enable)
{
	u32 enbits;
	u32 enmask;

	if (!hwdev)
		return -EINVAL;

	enbits = VSW_SET_STATEFUL_BITS_FCOE((u16)enable);
	enmask = VSW_SET_STATEFUL_BITS_FCOE(0x1U);

	return cfg_set_func_sf_en(hwdev, enbits, enmask);
}
EXPORT_SYMBOL(hinic_set_fcoe_enable);

bool hinic_get_fcoe_enable(void *hwdev)
{
	int err;
	u32 enbits;

	if (!hwdev)
		return false;

	err = cfg_get_func_sf_en(hwdev, &enbits);
	if (err)
		return false;

	return VSW_GET_STATEFUL_BITS_FCOE(enbits);
}
EXPORT_SYMBOL(hinic_get_fcoe_enable);

bool hinic_get_stateful_enable(void *hwdev)
{
	struct hinic_hwdev *dev = hwdev;

	if (!hwdev)
		return false;

	return dev->cfg_mgmt->svc_cap.sf_en;
}
EXPORT_SYMBOL(hinic_get_stateful_enable);

u8 hinic_host_oq_id_mask(void *hwdev)
{
	struct hinic_hwdev *dev = hwdev;

	if (!dev) {
		pr_err("Hwdev pointer is NULL for getting host oq id mask\n");
		return 0;
	}
	return dev->cfg_mgmt->svc_cap.host_oq_id_mask_val;
}
EXPORT_SYMBOL(hinic_host_oq_id_mask);

u8 hinic_host_id(void *hwdev)
{
	struct hinic_hwdev *dev = hwdev;

	if (!dev) {
		pr_err("Hwdev pointer is NULL for getting host id\n");
		return 0;
	}
	return dev->cfg_mgmt->svc_cap.host_id;
}
EXPORT_SYMBOL(hinic_host_id);

u16 hinic_host_total_func(void *hwdev)
{
	struct hinic_hwdev *dev = hwdev;

	if (!dev) {
		pr_err("Hwdev pointer is NULL for getting host total function number\n");
		return 0;
	}
	return dev->cfg_mgmt->svc_cap.host_total_function;
}
EXPORT_SYMBOL(hinic_host_total_func);

u16 hinic_func_max_qnum(void *hwdev)
{
	struct hinic_hwdev *dev = hwdev;

	if (!dev) {
		pr_err("Hwdev pointer is NULL for getting function max queue number\n");
		return 0;
	}
	return dev->cfg_mgmt->svc_cap.max_sqs;
}
EXPORT_SYMBOL(hinic_func_max_qnum);

u16 hinic_func_max_nic_qnum(void *hwdev)
{
	struct hinic_hwdev *dev = hwdev;

	if (!dev) {
		pr_err("Hwdev pointer is NULL for getting function max queue number\n");
		return 0;
	}
	return dev->cfg_mgmt->svc_cap.nic_cap.max_sqs;
}
EXPORT_SYMBOL(hinic_func_max_nic_qnum);

u8 hinic_ep_id(void *hwdev)
{
	struct hinic_hwdev *dev = hwdev;

	if (!dev) {
		pr_err("Hwdev pointer is NULL for getting ep id\n");
		return 0;
	}
	return dev->cfg_mgmt->svc_cap.ep_id;
}
EXPORT_SYMBOL(hinic_ep_id);

u8 hinic_er_id(void *hwdev)
{
	struct hinic_hwdev *dev = hwdev;

	if (!dev) {
		pr_err("Hwdev pointer is NULL for getting er id\n");
		return 0;
	}
	return dev->cfg_mgmt->svc_cap.er_id;
}
EXPORT_SYMBOL(hinic_er_id);

u8 hinic_physical_port_id(void *hwdev)
{
	struct hinic_hwdev *dev = hwdev;

	if (!dev) {
		pr_err("Hwdev pointer is NULL for getting physical port id\n");
		return 0;
	}
	return dev->cfg_mgmt->svc_cap.port_id;
}
EXPORT_SYMBOL(hinic_physical_port_id);

u8 hinic_func_max_vf(void *hwdev)
{
	struct hinic_hwdev *dev = hwdev;

	if (!dev) {
		pr_err("Hwdev pointer is NULL for getting max vf number\n");
		return 0;
	}
	return dev->cfg_mgmt->svc_cap.max_vf;
}
EXPORT_SYMBOL(hinic_func_max_vf);

u8 hinic_max_num_cos(void *hwdev)
{
	struct hinic_hwdev *dev = hwdev;

	if (!dev) {
		pr_err("Hwdev pointer is NULL for getting max cos number\n");
		return 0;
	}
	return (u8)(dev->cfg_mgmt->svc_cap.max_cos_id + 1);
}
EXPORT_SYMBOL(hinic_max_num_cos);

u8 hinic_cos_valid_bitmap(void *hwdev)
{
	struct hinic_hwdev *dev = hwdev;

	if (!dev) {
		pr_err("Hwdev pointer is NULL for getting cos valid bitmap\n");
		return 0;
	}
	return (u8)(dev->cfg_mgmt->svc_cap.cos_valid_bitmap);
}
EXPORT_SYMBOL(hinic_cos_valid_bitmap);

u8 hinic_net_port_mode(void *hwdev)
{
	struct hinic_hwdev *dev = hwdev;

	if (!dev) {
		pr_err("Hwdev pointer is NULL for getting net port mode\n");
		return 0;
	}
	return dev->cfg_mgmt->svc_cap.net_port_mode;
}
EXPORT_SYMBOL(hinic_net_port_mode);

bool hinic_is_hwdev_mod_inited(void *hwdev, enum hinic_hwdev_init_state state)
{
	struct hinic_hwdev *dev = hwdev;

	if (!hwdev || state >= HINIC_HWDEV_MAX_INVAL_INITED)
		return false;

	return !!test_bit(state, &dev->func_state);
}

static int hinic_os_dep_init(struct hinic_hwdev *hwdev)
{
	hwdev->workq = create_singlethread_workqueue(HINIC_HW_WQ_NAME);
	if (!hwdev->workq) {
		sdk_err(hwdev->dev_hdl, "Failed to initialize hardware workqueue\n");
		return -EFAULT;
	}

	return 0;
}

static void hinic_os_dep_deinit(struct hinic_hwdev *hwdev)
{
	destroy_workqueue(hwdev->workq);
}

void hinic_ppf_hwdev_unreg(void *hwdev)
{
	struct hinic_hwdev *dev = hwdev;

	if (!hwdev)
		return;

	down(&dev->ppf_sem);
	dev->ppf_hwdev = NULL;
	up(&dev->ppf_sem);

	sdk_info(dev->dev_hdl, "Unregister PPF hwdev\n");
}

void hinic_ppf_hwdev_reg(void *hwdev, void *ppf_hwdev)
{
	struct hinic_hwdev *dev = hwdev;

	if (!hwdev)
		return;

	down(&dev->ppf_sem);
	dev->ppf_hwdev = ppf_hwdev;
	up(&dev->ppf_sem);

	sdk_info(dev->dev_hdl, "Register PPF hwdev\n");
}

static int __vf_func_init(struct hinic_hwdev *hwdev)
{
	int err;

	err = hinic_vf_mbox_random_id_init(hwdev);
	if (err) {
		sdk_err(hwdev->dev_hdl, "Failed to init vf mbox random id\n");
		return err;
	}
	err = hinic_vf_func_init(hwdev);
	if (err)
		nic_err(hwdev->dev_hdl, "Failed to init nic mbox\n");

	return err;
}

static int __hilink_phy_init(struct hinic_hwdev *hwdev)
{
	int err;

	if (!HINIC_IS_VF(hwdev)) {
		err = hinic_phy_init_status_judge(hwdev);
		if (err) {
			sdk_info(hwdev->dev_hdl, "Phy init failed\n");
			return err;
		}

		if (hinic_support_nic(hwdev, NULL))
			hinic_hilink_info_show(hwdev);
	}

	return 0;
}

/* Return:
 * 0: all success
 * >0: partitial success
 * <0: all failed
 */
int hinic_init_hwdev(struct hinic_init_para *para)
{
	struct hinic_hwdev *hwdev;
	int err;

	if (!(*para->hwdev)) {
		hwdev = kzalloc(sizeof(*hwdev), GFP_KERNEL);
		if (!hwdev)
			return -ENOMEM;

		*para->hwdev = hwdev;
		hwdev->adapter_hdl = para->adapter_hdl;
		hwdev->pcidev_hdl = para->pcidev_hdl;
		hwdev->dev_hdl = para->dev_hdl;
		hwdev->chip_node = para->chip_node;
		hwdev->ppf_hwdev = para->ppf_hwdev;
		sema_init(&hwdev->ppf_sem, 1);
		sema_init(&hwdev->func_sem, 1);
		hwdev->func_ref = 0;

		hwdev->chip_fault_stats = vzalloc(HINIC_CHIP_FAULT_SIZE);
		if (!hwdev->chip_fault_stats)
			goto alloc_chip_fault_stats_err;

		err = hinic_init_hwif(hwdev, para->cfg_reg_base,
				      para->intr_reg_base,
				      para->db_base_phy, para->db_base,
				      para->dwqe_mapping);
		if (err) {
			sdk_err(hwdev->dev_hdl, "Failed to init hwif\n");
			goto init_hwif_err;
		}
	} else {
		hwdev = *para->hwdev;
	}

	/* detect slave host according to BAR reg */
	detect_host_mode_pre(hwdev);

	if (IS_BMGW_SLAVE_HOST(hwdev) &&
	    (!hinic_get_master_host_mbox_enable(hwdev))) {
		set_bit(HINIC_HWDEV_NONE_INITED, &hwdev->func_state);
		sdk_info(hwdev->dev_hdl, "Master host not ready, init hwdev later\n");
		return (1 << HINIC_HWDEV_ALL_INITED);
	}

	err = hinic_os_dep_init(hwdev);
	if (err) {
		sdk_err(hwdev->dev_hdl, "Failed to init os dependent\n");
		goto os_dep_init_err;
	}

	hinic_set_chip_present(hwdev);
	hinic_init_heartbeat(hwdev);

	err = init_cfg_mgmt(hwdev);
	if (err) {
		sdk_err(hwdev->dev_hdl, "Failed to init config mgmt\n");
		goto init_cfg_mgmt_err;
	}

	err = hinic_init_comm_ch(hwdev);
	if (err) {
		if (!(hwdev->func_state & HINIC_HWDEV_INIT_MODES_MASK)) {
			sdk_err(hwdev->dev_hdl, "Failed to init communication channel\n");
			goto init_comm_ch_err;
		} else {
			sdk_err(hwdev->dev_hdl, "Init communication channel partitail failed\n");
			return hwdev->func_state & HINIC_HWDEV_INIT_MODES_MASK;
		}
	}

	err = init_capability(hwdev);
	if (err) {
		sdk_err(hwdev->dev_hdl, "Failed to init capability\n");
		goto init_cap_err;
	}

	if (hwdev->cfg_mgmt->svc_cap.force_up)
		hwdev->feature_cap |= HINIC_FUNC_FORCE_LINK_UP;

	err = __vf_func_init(hwdev);
	if (err)
		goto vf_func_init_err;

	err = __hilink_phy_init(hwdev);
	if (err)
		goto hilink_phy_init_err;

	set_bit(HINIC_HWDEV_ALL_INITED, &hwdev->func_state);

	sdk_info(hwdev->dev_hdl, "Init hwdev success\n");

	return 0;

hilink_phy_init_err:

	hinic_vf_func_free(hwdev);
vf_func_init_err:
	free_capability(hwdev);
init_cap_err:
	return (hwdev->func_state & HINIC_HWDEV_INIT_MODES_MASK);

init_comm_ch_err:
	free_cfg_mgmt(hwdev);

init_cfg_mgmt_err:
	hinic_destroy_heartbeat(hwdev);
	hinic_os_dep_deinit(hwdev);

os_dep_init_err:
	hinic_free_hwif(hwdev);

init_hwif_err:
	vfree(hwdev->chip_fault_stats);

alloc_chip_fault_stats_err:
	kfree(hwdev);
	*para->hwdev = NULL;

	return -EFAULT;
}

/**
 * hinic_set_vf_dev_cap - Set max queue num for VF
 * @hwdev: the HW device for VF
 */
int hinic_set_vf_dev_cap(void *hwdev)
{
	int err;
	struct hinic_hwdev *dev;
	enum func_type type;

	if (!hwdev)
		return -EFAULT;

	dev = (struct hinic_hwdev *)hwdev;
	type = HINIC_FUNC_TYPE(dev);
	if (type != TYPE_VF)
		return -EPERM;

	err = get_dev_cap(dev);
	if (err)
		return err;

	nic_param_fix(dev);

	return 0;
}

void hinic_free_hwdev(void *hwdev)
{
	struct hinic_hwdev *dev = hwdev;
	enum hinic_hwdev_init_state state = HINIC_HWDEV_ALL_INITED;
	int flag = 0;

	if (!hwdev)
		return;

	if (test_bit(HINIC_HWDEV_ALL_INITED, &dev->func_state)) {
		clear_bit(HINIC_HWDEV_ALL_INITED, &dev->func_state);

		/* BM slave function not need to exec rx_tx_flush */
		if (dev->func_mode != FUNC_MOD_MULTI_BM_SLAVE)
			hinic_func_rx_tx_flush(hwdev);

		hinic_vf_func_free(hwdev);

		free_capability(dev);
	}
	while (state > HINIC_HWDEV_NONE_INITED) {
		if (test_bit(state, &dev->func_state)) {
			flag = 1;
			break;
		}
		state--;
	}
	if (flag) {
		hinic_uninit_comm_ch(dev);
		free_cfg_mgmt(dev);
		hinic_destroy_heartbeat(dev);
		hinic_os_dep_deinit(dev);
	}
	clear_bit(HINIC_HWDEV_NONE_INITED, &dev->func_state);
	hinic_free_hwif(dev);
	vfree(dev->chip_fault_stats);
	kfree(dev);
}

void hinic_set_api_stop(void *hwdev)
{
	struct hinic_hwdev *dev = hwdev;

	if (!hwdev)
		return;

	dev->chip_present_flag = HINIC_CHIP_ABSENT;
	sdk_info(dev->dev_hdl, "Set card absent\n");
	hinic_force_complete_all(dev);
	sdk_info(dev->dev_hdl, "All messages interacting with the chip will stop\n");
}

void hinic_shutdown_hwdev(void *hwdev)
{
	struct hinic_hwdev *dev = hwdev;

	if (!hwdev)
		return;

	if (IS_SLAVE_HOST(dev))
		set_slave_host_enable(hwdev, hinic_pcie_itf_id(hwdev), false);
}

u32 hinic_func_pf_num(void *hwdev)
{
	struct hinic_hwdev *dev = hwdev;

	if (!dev) {
		pr_err("Hwdev pointer is NULL for getting pf number capability\n");
		return 0;
	}

	return dev->cfg_mgmt->svc_cap.pf_num;
}

u64 hinic_get_func_feature_cap(void *hwdev)
{
	struct hinic_hwdev *dev = hwdev;

	if (!dev) {
		pr_err("Hwdev pointer is NULL for getting function feature capability\n");
		return 0;
	}

	return dev->feature_cap;
}

enum hinic_func_mode hinic_get_func_mode(void *hwdev)
{
	struct hinic_hwdev *dev = hwdev;

	if (!dev) {
		pr_err("Hwdev pointer is NULL for getting function mode\n");
		return 0;
	}

	return dev->func_mode;
}
EXPORT_SYMBOL(hinic_get_func_mode);

enum hinic_service_mode hinic_get_service_mode(void *hwdev)
{
	struct hinic_hwdev *dev = hwdev;

	if (!dev) {
		pr_err("Hwdev pointer is NULL for getting service mode\n");
		return HINIC_WORK_MODE_INVALID;
	}

	return dev->board_info.service_mode;
}
