diff --git a/drivers/net/spnic/base/spnic_hw_comm.c b/drivers/net/spnic/base/spnic_hw_comm.c
index 5cb607cf03..1c751f2403 100644
--- a/drivers/net/spnic/base/spnic_hw_comm.c
+++ b/drivers/net/spnic/base/spnic_hw_comm.c
@@ -217,6 +217,107 @@ int spnic_func_reset(void *hwdev, u64 reset_flag)
 	return 0;
 }
 
+int spnic_convert_rx_buf_size(u32 rx_buf_sz, u32 *match_sz)
+{
+	u32 i, num_hw_types, best_match_sz;
+
+	if (unlikely(!match_sz || rx_buf_sz < SPNIC_RX_BUF_SIZE_32B))
+		return -EINVAL;
+
+	if (rx_buf_sz >= SPNIC_RX_BUF_SIZE_16K) {
+		best_match_sz =  SPNIC_RX_BUF_SIZE_16K;
+		goto size_matched;
+	}
+
+	num_hw_types = sizeof(spnic_hw_rx_buf_size) /
+		sizeof(spnic_hw_rx_buf_size[0]);
+	best_match_sz = spnic_hw_rx_buf_size[0];
+	for (i = 0; i < num_hw_types; i++) {
+		if (rx_buf_sz == spnic_hw_rx_buf_size[i]) {
+			best_match_sz = spnic_hw_rx_buf_size[i];
+			break;
+		} else if (rx_buf_sz < spnic_hw_rx_buf_size[i]) {
+			break;
+		}
+		best_match_sz = spnic_hw_rx_buf_size[i];
+	}
+
+size_matched:
+	*match_sz = best_match_sz;
+
+	return 0;
+}
+
+static u16 get_hw_rx_buf_size(u32 rx_buf_sz)
+{
+	u16 num_hw_types = sizeof(spnic_hw_rx_buf_size) /
+			   sizeof(spnic_hw_rx_buf_size[0]);
+	u16 i;
+
+	for (i = 0; i < num_hw_types; i++) {
+		if (spnic_hw_rx_buf_size[i] == rx_buf_sz)
+			return i;
+	}
+
+	PMD_DRV_LOG(WARNING, "Chip can't support rx buf size of %d", rx_buf_sz);
+
+	return DEFAULT_RX_BUF_SIZE; /* Default 2K */
+}
+
+int spnic_set_root_ctxt(void *hwdev, u32 rq_depth, u32 sq_depth, u16 rx_buf_sz)
+{
+	struct spnic_cmd_root_ctxt root_ctxt;
+	u16 out_size = sizeof(root_ctxt);
+	int err;
+
+	if (!hwdev)
+		return -EINVAL;
+
+	memset(&root_ctxt, 0, sizeof(root_ctxt));
+	root_ctxt.func_idx = spnic_global_func_id(hwdev);
+	root_ctxt.set_cmdq_depth = 0;
+	root_ctxt.cmdq_depth = 0;
+	root_ctxt.lro_en = 1;
+	root_ctxt.rq_depth  = (u16)ilog2(rq_depth);
+	root_ctxt.rx_buf_sz = get_hw_rx_buf_size(rx_buf_sz);
+	root_ctxt.sq_depth  = (u16)ilog2(sq_depth);
+
+	err = spnic_msg_to_mgmt_sync(hwdev, SPNIC_MOD_COMM, MGMT_CMD_SET_VAT,
+				     &root_ctxt, sizeof(root_ctxt),
+				     &root_ctxt, &out_size, 0);
+	if (err || !out_size || root_ctxt.status) {
+		PMD_DRV_LOG(ERR, "Set root context failed, err: %d, status: 0x%x, out_size: 0x%x",
+			    err, root_ctxt.status, out_size);
+		return -EFAULT;
+	}
+
+	return 0;
+}
+
+int spnic_clean_root_ctxt(void *hwdev)
+{
+	struct spnic_cmd_root_ctxt root_ctxt;
+	u16 out_size = sizeof(root_ctxt);
+	int err;
+
+	if (!hwdev)
+		return -EINVAL;
+
+	memset(&root_ctxt, 0, sizeof(root_ctxt));
+	root_ctxt.func_idx = spnic_global_func_id(hwdev);
+
+	err = spnic_msg_to_mgmt_sync(hwdev, SPNIC_MOD_COMM, MGMT_CMD_SET_VAT,
+				     &root_ctxt, sizeof(root_ctxt),
+				     &root_ctxt, &out_size, 0);
+	if (err || !out_size || root_ctxt.status) {
+		PMD_DRV_LOG(ERR, "Clean root context failed, err: %d, status: 0x%x, out_size: 0x%x",
+			    err, root_ctxt.status, out_size);
+		return -EFAULT;
+	}
+
+	return 0;
+}
+
 int spnic_set_cmdq_depth(void *hwdev, u16 cmdq_depth)
 {
 	struct spnic_cmd_root_ctxt root_ctxt;
diff --git a/drivers/net/spnic/base/spnic_hw_comm.h b/drivers/net/spnic/base/spnic_hw_comm.h
index 207f0aaeae..f960fbb53f 100644
--- a/drivers/net/spnic/base/spnic_hw_comm.h
+++ b/drivers/net/spnic/base/spnic_hw_comm.h
@@ -180,6 +180,10 @@ int spnic_get_mgmt_version(void *hwdev, char *mgmt_ver, int max_mgmt_len);
 
 int spnic_get_board_info(void *hwdev, struct spnic_board_info *info);
 
+int spnic_set_root_ctxt(void *hwdev, u32 rq_depth, u32 sq_depth, u16 rx_buf_sz);
+
+int spnic_clean_root_ctxt(void *hwdev);
+
 int spnic_get_interrupt_cfg(void *dev, struct interrupt_info *info);
 
 int spnic_set_interrupt_cfg(void *dev, struct interrupt_info info);
@@ -188,6 +192,8 @@ int spnic_set_wq_page_size(void *hwdev, u16 func_idx, u32 page_size);
 
 int spnic_set_cmdq_depth(void *hwdev, u16 cmdq_depth);
 
+int spnic_convert_rx_buf_size(u32 rx_buf_sz, u32 *match_sz);
+
 int spnic_get_comm_features(void *hwdev, u64 *s_feature, u16 size);
 
 int spnic_set_comm_features(void *hwdev, u64 *s_feature, u16 size);
diff --git a/drivers/net/spnic/base/spnic_nic_cfg.c b/drivers/net/spnic/base/spnic_nic_cfg.c
index 886aaea384..25d98d67dd 100644
--- a/drivers/net/spnic/base/spnic_nic_cfg.c
+++ b/drivers/net/spnic/base/spnic_nic_cfg.c
@@ -75,6 +75,42 @@ int l2nic_msg_to_mgmt_sync(void *hwdev, u16 cmd, void *buf_in, u16 in_size,
 				      in_size, buf_out, out_size, 0);
 }
 
+int spnic_set_ci_table(void *hwdev, struct spnic_sq_attr *attr)
+{
+	struct spnic_cmd_cons_idx_attr cons_idx_attr;
+	u16 out_size = sizeof(cons_idx_attr);
+	int err;
+
+	if (!hwdev || !attr)
+		return -EINVAL;
+
+	memset(&cons_idx_attr, 0, sizeof(cons_idx_attr));
+	cons_idx_attr.func_idx = spnic_global_func_id(hwdev);
+	cons_idx_attr.dma_attr_off  = attr->dma_attr_off;
+	cons_idx_attr.pending_limit = attr->pending_limit;
+	cons_idx_attr.coalescing_time  = attr->coalescing_time;
+
+	if (attr->intr_en) {
+		cons_idx_attr.intr_en = attr->intr_en;
+		cons_idx_attr.intr_idx = attr->intr_idx;
+	}
+
+	cons_idx_attr.l2nic_sqn = attr->l2nic_sqn;
+	cons_idx_attr.ci_addr = attr->ci_dma_base;
+
+	err = l2nic_msg_to_mgmt_sync(hwdev, SPNIC_CMD_SQ_CI_ATTR_SET,
+				     &cons_idx_attr, sizeof(cons_idx_attr),
+				     &cons_idx_attr, &out_size);
+	if (err || !out_size || cons_idx_attr.msg_head.status) {
+		PMD_DRV_LOG(ERR, "Set ci attribute table failed, err: %d, "
+			    "status: 0x%x, out_size: 0x%x",
+			    err, cons_idx_attr.msg_head.status, out_size);
+		return -EFAULT;
+	}
+
+	return 0;
+}
+
 static int spnic_check_mac_info(u8 status, u16 vlan_id)
 {
 	if ((status && status != SPNIC_MGMT_STATUS_EXIST &&
@@ -406,6 +442,46 @@ int spnic_set_port_mtu(void *hwdev, u16 new_mtu)
 					&func_tbl_cfg);
 }
 
+static int nic_feature_nego(void *hwdev, u8 opcode, u64 *s_feature, u16 size)
+{
+	struct spnic_cmd_feature_nego feature_nego;
+	u16 out_size = sizeof(feature_nego);
+	int err;
+
+	if (!hwdev || !s_feature || size > MAX_FEATURE_QWORD)
+		return -EINVAL;
+
+	memset(&feature_nego, 0, sizeof(feature_nego));
+	feature_nego.func_id = spnic_global_func_id(hwdev);
+	feature_nego.opcode = opcode;
+	if (opcode == SPNIC_CMD_OP_SET)
+		memcpy(feature_nego.s_feature, s_feature, size * sizeof(u64));
+
+	err = l2nic_msg_to_mgmt_sync(hwdev, SPNIC_CMD_FEATURE_NEGO,
+				     &feature_nego, sizeof(feature_nego),
+				     &feature_nego, &out_size);
+	if (err || !out_size || feature_nego.msg_head.status) {
+		PMD_DRV_LOG(ERR, "Failed to negotiate nic feature, err:%d, status: 0x%x, out_size: 0x%x\n",
+			    err, feature_nego.msg_head.status, out_size);
+		return -EFAULT;
+	}
+
+	if (opcode == SPNIC_CMD_OP_GET)
+		memcpy(s_feature, feature_nego.s_feature, size * sizeof(u64));
+
+	return 0;
+}
+
+int spnic_get_feature_from_hw(void *hwdev, u64 *s_feature, u16 size)
+{
+	return nic_feature_nego(hwdev, SPNIC_CMD_OP_GET, s_feature, size);
+}
+
+int spnic_set_feature_to_hw(void *hwdev, u64 *s_feature, u16 size)
+{
+	return nic_feature_nego(hwdev, SPNIC_CMD_OP_SET, s_feature, size);
+}
+
 static int spnic_vf_func_init(void *hwdev)
 {
 	struct spnic_cmd_register_vf register_info;
diff --git a/drivers/net/spnic/base/spnic_nic_cfg.h b/drivers/net/spnic/base/spnic_nic_cfg.h
index 98cad645d2..ce9792f8ee 100644
--- a/drivers/net/spnic/base/spnic_nic_cfg.h
+++ b/drivers/net/spnic/base/spnic_nic_cfg.h
@@ -36,6 +36,15 @@
 #define SPNIC_MGMT_STATUS_EXIST		0x6
 #define CHECK_IPSU_15BIT		0x8000
 
+struct spnic_cmd_feature_nego {
+	struct mgmt_msg_head msg_head;
+
+	u16 func_id;
+	u8 opcode;	/* 1: set, 0: get */
+	u8 rsvd;
+	u64 s_feature[MAX_FEATURE_QWORD];
+};
+
 /* Structures for port info */
 struct nic_port_info {
 	u8 port_type;
@@ -69,6 +78,30 @@ enum nic_speed_level {
 	LINK_SPEED_LEVELS,
 };
 
+struct spnic_sq_attr {
+	u8 dma_attr_off;
+	u8 pending_limit;
+	u8 coalescing_time;
+	u8 intr_en;
+	u16 intr_idx;
+	u32 l2nic_sqn;
+	u64 ci_dma_base;
+};
+
+struct spnic_cmd_cons_idx_attr {
+	struct mgmt_msg_head msg_head;
+
+	u16 func_idx;
+	u8 dma_attr_off;
+	u8 pending_limit;
+	u8 coalescing_time;
+	u8 intr_en;
+	u16 intr_idx;
+	u32 l2nic_sqn;
+	u32 rsvd;
+	u64 ci_addr;
+};
+
 struct spnic_port_mac_set {
 	struct mgmt_msg_head msg_head;
 
@@ -88,7 +121,6 @@ struct spnic_port_mac_update {
 	u16 rsvd2;
 	u8 new_mac[ETH_ALEN];
 };
-
 struct spnic_cmd_port_info {
 	struct mgmt_msg_head msg_head;
 
@@ -193,6 +225,9 @@ struct spnic_cmd_set_func_tbl {
 	struct spnic_func_tbl_cfg tbl_cfg;
 };
 
+#define SPNIC_CMD_OP_GET	0
+#define SPNIC_CMD_OP_SET	1
+
 enum {
 	SPNIC_IFLA_VF_LINK_STATE_AUTO,	/* Link state of the uplink */
 	SPNIC_IFLA_VF_LINK_STATE_ENABLE, /* Link always up */
@@ -223,6 +258,8 @@ struct spnic_cmd_register_vf {
 int l2nic_msg_to_mgmt_sync(void *hwdev, u16 cmd, void *buf_in, u16 in_size,
 			   void *buf_out, u16 *out_size);
 
+int spnic_set_ci_table(void *hwdev, struct spnic_sq_attr *attr);
+
 /**
  * Update MAC address to hardware
  *
@@ -390,4 +427,30 @@ int spnic_init_function_table(void *hwdev, u16 rx_buff_len);
  * @retval non-zero : Failure
  */
 int spnic_vf_get_default_cos(void *hwdev, u8 *cos_id);
+
+/**
+ * Get service feature HW supported
+ *
+ * @param[in] dev
+ *   Device pointer to hwdev
+ * @param[in] size
+ *   s_feature's array size
+ * @param[out] s_feature
+ *   s_feature HW supported
+ * @retval zero: Success
+ * @retval non-zero: Failure
+ */
+int spnic_get_feature_from_hw(void *hwdev, u64 *s_feature, u16 size);
+
+/**
+ * Set service feature driver supported to hardware
+ *
+ * @param[in] dev
+ *   Device pointer to hwdev
+ *
+ * @retval zero: Success
+ * @retval non-zero: Failure
+ */
+int spnic_set_feature_to_hw(void *hwdev, u64 *s_feature, u16 size);
+
 #endif /* _SPNIC_NIC_CFG_H_ */
diff --git a/drivers/net/spnic/meson.build b/drivers/net/spnic/meson.build
index 042d2fe6e1..20d5151a8d 100644
--- a/drivers/net/spnic/meson.build
+++ b/drivers/net/spnic/meson.build
@@ -6,6 +6,7 @@ objs = [base_objs]
 
 sources = files(
 	'spnic_ethdev.c',
-	)
+	'spnic_io.c',
+)
 
 includes += include_directories('base')
diff --git a/drivers/net/spnic/spnic_ethdev.c b/drivers/net/spnic/spnic_ethdev.c
index 7f73e70df1..4205ab43a4 100644
--- a/drivers/net/spnic/spnic_ethdev.c
+++ b/drivers/net/spnic/spnic_ethdev.c
@@ -22,14 +22,25 @@
 #include "base/spnic_hw_comm.h"
 #include "base/spnic_nic_cfg.h"
 #include "base/spnic_nic_event.h"
+#include "spnic_io.h"
+#include "spnic_tx.h"
+#include "spnic_rx.h"
 #include "spnic_ethdev.h"
 
 /* Driver-specific log messages type */
 int spnic_logtype;
 
+#define SPNIC_DEFAULT_RX_FREE_THRESH	32
+#define SPNIC_DEFAULT_TX_FREE_THRESH	32
+
 #define SPNIC_MAX_UC_MAC_ADDRS		128
 #define SPNIC_MAX_MC_MAC_ADDRS		128
 
+#define SPNIC_MAX_QUEUE_DEPTH		16384
+#define SPNIC_MIN_QUEUE_DEPTH		128
+#define SPNIC_TXD_ALIGN			1
+#define SPNIC_RXD_ALIGN			1
+
 /**
  * Deinit mac_vlan table in hardware.
  *
@@ -219,6 +230,7 @@ static void spnic_deinit_sw_rxtxqs(struct spnic_nic_dev *nic_dev)
 static int spnic_dev_start(struct rte_eth_dev *eth_dev)
 {
 	struct spnic_nic_dev *nic_dev;
+	u64 nic_features;
 	int err;
 
 	nic_dev = SPNIC_ETH_DEV_TO_PRIVATE_NIC_DEV(eth_dev);
@@ -230,6 +242,26 @@ static int spnic_dev_start(struct rte_eth_dev *eth_dev)
 		goto init_func_tbl_fail;
 	}
 
+	nic_features = spnic_get_driver_feature(nic_dev->hwdev);
+	nic_features &= DEFAULT_DRV_FEATURE;
+	spnic_update_driver_feature(nic_dev->hwdev, nic_features);
+
+	err = spnic_set_feature_to_hw(nic_dev->hwdev, &nic_dev->feature_cap, 1);
+	if (err) {
+		PMD_DRV_LOG(ERR, "Failed to set nic features to hardware, err %d\n",
+			    err);
+		goto get_feature_err;
+	}
+
+
+	/* Init txq and rxq context */
+	err = spnic_init_qp_ctxts(nic_dev);
+	if (err) {
+		PMD_DRV_LOG(ERR, "Init qp context failed, dev_name: %s",
+			    eth_dev->data->name);
+		goto init_qp_fail;
+	}
+
 	/* Set default mtu */
 	err = spnic_set_port_mtu(nic_dev->hwdev, nic_dev->mtu_size);
 	if (err) {
@@ -238,7 +270,6 @@ static int spnic_dev_start(struct rte_eth_dev *eth_dev)
 		goto set_mtu_fail;
 	}
 
-
 	/* Update eth_dev link status */
 	if (eth_dev->data->dev_conf.intr_conf.lsc != 0)
 		(void)spnic_link_update(eth_dev, 0);
@@ -248,6 +279,10 @@ static int spnic_dev_start(struct rte_eth_dev *eth_dev)
 	return 0;
 
 set_mtu_fail:
+	spnic_free_qp_ctxts(nic_dev->hwdev);
+
+init_qp_fail:
+get_feature_err:
 init_func_tbl_fail:
 
 	return err;
@@ -278,6 +313,9 @@ static int spnic_dev_stop(struct rte_eth_dev *dev)
 	memset(&link, 0, sizeof(link));
 	(void)rte_eth_linkstatus_set(dev, &link);
 
+	/* Clean root context */
+	spnic_free_qp_ctxts(nic_dev->hwdev);
+
 	return 0;
 }
 
@@ -290,7 +328,7 @@ static int spnic_dev_stop(struct rte_eth_dev *dev)
 static int spnic_dev_close(struct rte_eth_dev *eth_dev)
 {
 	struct spnic_nic_dev *nic_dev =
-		SPNIC_ETH_DEV_TO_PRIVATE_NIC_DEV(eth_dev);
+	SPNIC_ETH_DEV_TO_PRIVATE_NIC_DEV(eth_dev);
 
 	if (rte_bit_relaxed_test_and_set32(SPNIC_DEV_CLOSE, &nic_dev->dev_status)) {
 		PMD_DRV_LOG(WARNING, "Device %s already closed",
@@ -306,7 +344,6 @@ static int spnic_dev_close(struct rte_eth_dev *eth_dev)
 
 	rte_bit_relaxed_clear32(SPNIC_DEV_INTR_EN, &nic_dev->dev_status);
 
-
 	/* Destroy rx mode mutex */
 	spnic_mutex_destroy(&nic_dev->rx_mode_mutex);
 
@@ -736,6 +773,12 @@ static int spnic_func_init(struct rte_eth_dev *eth_dev)
 		goto init_hwdev_fail;
 	}
 
+	if (!spnic_support_nic(nic_dev->hwdev)) {
+		PMD_DRV_LOG(ERR, "Hw of %s don't support nic\n",
+			    eth_dev->data->name);
+		goto init_hwdev_fail;
+	}
+
 	nic_dev->max_sqs = spnic_func_max_sqs(nic_dev->hwdev);
 	nic_dev->max_rqs = spnic_func_max_rqs(nic_dev->hwdev);
 
@@ -751,6 +794,13 @@ static int spnic_func_init(struct rte_eth_dev *eth_dev)
 		goto init_nic_hwdev_fail;
 	}
 
+	err = spnic_get_feature_from_hw(nic_dev->hwdev, &nic_dev->feature_cap, 1);
+	if (err) {
+		PMD_DRV_LOG(ERR, "Get nic feature from hardware failed, dev_name: %s",
+			    eth_dev->data->name);
+		goto get_cap_fail;
+	}
+
 	err = spnic_init_sw_rxtxqs(nic_dev);
 	if (err) {
 		PMD_DRV_LOG(ERR, "Init sw rxqs or txqs failed, dev_name: %s",
@@ -792,6 +842,7 @@ static int spnic_func_init(struct rte_eth_dev *eth_dev)
 init_sw_rxtxqs_fail:
 	spnic_free_nic_hwdev(nic_dev->hwdev);
 
+get_cap_fail:
 init_nic_hwdev_fail:
 	spnic_free_hwdev(nic_dev->hwdev);
 	eth_dev->dev_ops = NULL;
diff --git a/drivers/net/spnic/spnic_io.c b/drivers/net/spnic/spnic_io.c
new file mode 100644
index 0000000000..b4358f530a
--- /dev/null
+++ b/drivers/net/spnic/spnic_io.c
@@ -0,0 +1,738 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2021 Ramaxel Memory Technology, Ltd
+ */
+
+#include <rte_io.h>
+#include <rte_pci.h>
+#include <rte_bus_pci.h>
+#include <ethdev_pci.h>
+#include <rte_mbuf.h>
+#include <rte_malloc.h>
+#include <rte_memcpy.h>
+#include <rte_mempool.h>
+#include <rte_errno.h>
+#include <rte_ether.h>
+#include <rte_ethdev_core.h>
+#include <ethdev_driver.h>
+
+#include "base/spnic_compat.h"
+#include "base/spnic_cmd.h"
+#include "base/spnic_wq.h"
+#include "base/spnic_mgmt.h"
+#include "base/spnic_cmdq.h"
+#include "base/spnic_hwdev.h"
+#include "base/spnic_hw_comm.h"
+#include "base/spnic_nic_cfg.h"
+#include "base/spnic_hw_cfg.h"
+#include "spnic_io.h"
+#include "spnic_tx.h"
+#include "spnic_rx.h"
+#include "spnic_ethdev.h"
+
+#define SPNIC_DEAULT_TX_CI_PENDING_LIMIT	0
+#define SPNIC_DEAULT_TX_CI_COALESCING_TIME	0
+#define SPNIC_DEAULT_DROP_THD_ON		0xFFFF
+#define SPNIC_DEAULT_DROP_THD_OFF		0
+
+#define WQ_PREFETCH_MAX			4
+#define WQ_PREFETCH_MIN			1
+#define WQ_PREFETCH_THRESHOLD		256
+
+#define SPNIC_Q_CTXT_MAX		31
+
+enum spnic_qp_ctxt_type {
+	SPNIC_QP_CTXT_TYPE_SQ,
+	SPNIC_QP_CTXT_TYPE_RQ,
+};
+
+struct spnic_qp_ctxt_header {
+	u16 num_queues;
+	u16 queue_type;
+	u16 start_qid;
+	u16 rsvd;
+};
+
+struct spnic_sq_ctxt {
+	u32 ci_pi;
+	u32 drop_mode_sp;
+	u32 wq_pfn_hi_owner;
+	u32 wq_pfn_lo;
+
+	u32 rsvd0;
+	u32 pkt_drop_thd;
+	u32 global_sq_id;
+	u32 vlan_ceq_attr;
+
+	u32 pref_cache;
+	u32 pref_ci_owner;
+	u32 pref_wq_pfn_hi_ci;
+	u32 pref_wq_pfn_lo;
+
+	u32 rsvd8;
+	u32 rsvd9;
+	u32 wq_block_pfn_hi;
+	u32 wq_block_pfn_lo;
+};
+
+struct spnic_rq_ctxt {
+	u32 ci_pi;
+	u32 ceq_attr;
+	u32 wq_pfn_hi_type_owner;
+	u32 wq_pfn_lo;
+
+	u32 rsvd[3];
+	u32 cqe_sge_len;
+
+	u32 pref_cache;
+	u32 pref_ci_owner;
+	u32 pref_wq_pfn_hi_ci;
+	u32 pref_wq_pfn_lo;
+
+	u32 pi_paddr_hi;
+	u32 pi_paddr_lo;
+	u32 wq_block_pfn_hi;
+	u32 wq_block_pfn_lo;
+};
+
+struct spnic_sq_ctxt_block {
+	struct spnic_qp_ctxt_header cmdq_hdr;
+	struct spnic_sq_ctxt sq_ctxt[SPNIC_Q_CTXT_MAX];
+};
+
+struct spnic_rq_ctxt_block {
+	struct spnic_qp_ctxt_header cmdq_hdr;
+	struct spnic_rq_ctxt rq_ctxt[SPNIC_Q_CTXT_MAX];
+};
+
+struct spnic_clean_queue_ctxt {
+	struct spnic_qp_ctxt_header cmdq_hdr;
+	u32 rsvd;
+};
+
+#define SQ_CTXT_SIZE(num_sqs)	((u16)(sizeof(struct spnic_qp_ctxt_header) \
+				+ (num_sqs) * sizeof(struct spnic_sq_ctxt)))
+
+#define RQ_CTXT_SIZE(num_rqs)	((u16)(sizeof(struct spnic_qp_ctxt_header) \
+				+ (num_rqs) * sizeof(struct spnic_rq_ctxt)))
+
+#define CI_IDX_HIGH_SHIFH				12
+
+#define CI_HIGN_IDX(val)		((val) >> CI_IDX_HIGH_SHIFH)
+
+#define SQ_CTXT_PI_IDX_SHIFT				0
+#define SQ_CTXT_CI_IDX_SHIFT				16
+
+#define SQ_CTXT_PI_IDX_MASK				0xFFFFU
+#define SQ_CTXT_CI_IDX_MASK				0xFFFFU
+
+#define SQ_CTXT_CI_PI_SET(val, member)			(((val) & \
+					SQ_CTXT_##member##_MASK) \
+					<< SQ_CTXT_##member##_SHIFT)
+
+#define SQ_CTXT_MODE_SP_FLAG_SHIFT			0
+#define SQ_CTXT_MODE_PKT_DROP_SHIFT			1
+
+#define SQ_CTXT_MODE_SP_FLAG_MASK			0x1U
+#define SQ_CTXT_MODE_PKT_DROP_MASK			0x1U
+
+#define SQ_CTXT_MODE_SET(val, member)	(((val) & \
+					SQ_CTXT_MODE_##member##_MASK) \
+					<< SQ_CTXT_MODE_##member##_SHIFT)
+
+#define SQ_CTXT_WQ_PAGE_HI_PFN_SHIFT			0
+#define SQ_CTXT_WQ_PAGE_OWNER_SHIFT			23
+
+#define SQ_CTXT_WQ_PAGE_HI_PFN_MASK			0xFFFFFU
+#define SQ_CTXT_WQ_PAGE_OWNER_MASK			0x1U
+
+#define SQ_CTXT_WQ_PAGE_SET(val, member)		(((val) & \
+					SQ_CTXT_WQ_PAGE_##member##_MASK) \
+					<< SQ_CTXT_WQ_PAGE_##member##_SHIFT)
+
+#define SQ_CTXT_PKT_DROP_THD_ON_SHIFT			0
+#define SQ_CTXT_PKT_DROP_THD_OFF_SHIFT			16
+
+#define SQ_CTXT_PKT_DROP_THD_ON_MASK			0xFFFFU
+#define SQ_CTXT_PKT_DROP_THD_OFF_MASK			0xFFFFU
+
+#define SQ_CTXT_PKT_DROP_THD_SET(val, member)		(((val) & \
+					SQ_CTXT_PKT_DROP_##member##_MASK) \
+					<< SQ_CTXT_PKT_DROP_##member##_SHIFT)
+
+#define SQ_CTXT_GLOBAL_SQ_ID_SHIFT			0
+
+#define SQ_CTXT_GLOBAL_SQ_ID_MASK			0x1FFFU
+
+#define SQ_CTXT_GLOBAL_QUEUE_ID_SET(val, member)	(((val) & \
+					SQ_CTXT_##member##_MASK) \
+					<< SQ_CTXT_##member##_SHIFT)
+
+
+#define SQ_CTXT_VLAN_TAG_SHIFT				0
+#define SQ_CTXT_VLAN_TYPE_SEL_SHIFT			16
+#define SQ_CTXT_VLAN_INSERT_MODE_SHIFT			19
+#define SQ_CTXT_VLAN_CEQ_EN_SHIFT			23
+
+#define SQ_CTXT_VLAN_TAG_MASK				0xFFFFU
+#define SQ_CTXT_VLAN_TYPE_SEL_MASK			0x7U
+#define SQ_CTXT_VLAN_INSERT_MODE_MASK			0x3U
+#define SQ_CTXT_VLAN_CEQ_EN_MASK			0x1U
+
+#define SQ_CTXT_VLAN_CEQ_SET(val, member)		(((val) & \
+					SQ_CTXT_VLAN_##member##_MASK) \
+					<< SQ_CTXT_VLAN_##member##_SHIFT)
+
+#define SQ_CTXT_PREF_CACHE_THRESHOLD_SHIFT		0
+#define SQ_CTXT_PREF_CACHE_MAX_SHIFT			14
+#define SQ_CTXT_PREF_CACHE_MIN_SHIFT			25
+
+#define SQ_CTXT_PREF_CACHE_THRESHOLD_MASK		0x3FFFU
+#define SQ_CTXT_PREF_CACHE_MAX_MASK			0x7FFU
+#define SQ_CTXT_PREF_CACHE_MIN_MASK			0x7FU
+
+#define SQ_CTXT_PREF_CI_HI_SHIFT			0
+#define SQ_CTXT_PREF_OWNER_SHIFT			4
+
+#define SQ_CTXT_PREF_CI_HI_MASK				0xFU
+#define SQ_CTXT_PREF_OWNER_MASK				0x1U
+
+#define SQ_CTXT_PREF_WQ_PFN_HI_SHIFT			0
+#define SQ_CTXT_PREF_CI_LOW_SHIFT			20
+
+#define SQ_CTXT_PREF_WQ_PFN_HI_MASK			0xFFFFFU
+#define SQ_CTXT_PREF_CI_LOW_MASK			0xFFFU
+
+#define SQ_CTXT_PREF_SET(val, member)			(((val) & \
+					SQ_CTXT_PREF_##member##_MASK) \
+					<< SQ_CTXT_PREF_##member##_SHIFT)
+
+#define SQ_CTXT_WQ_BLOCK_PFN_HI_SHIFT			0
+
+#define SQ_CTXT_WQ_BLOCK_PFN_HI_MASK			0x7FFFFFU
+
+#define SQ_CTXT_WQ_BLOCK_SET(val, member)		(((val) & \
+					SQ_CTXT_WQ_BLOCK_##member##_MASK) \
+					<< SQ_CTXT_WQ_BLOCK_##member##_SHIFT)
+
+#define RQ_CTXT_PI_IDX_SHIFT				0
+#define RQ_CTXT_CI_IDX_SHIFT				16
+
+#define RQ_CTXT_PI_IDX_MASK				0xFFFFU
+#define RQ_CTXT_CI_IDX_MASK				0xFFFFU
+
+#define RQ_CTXT_CI_PI_SET(val, member)			(((val) & \
+					RQ_CTXT_##member##_MASK) \
+					<< RQ_CTXT_##member##_SHIFT)
+
+#define RQ_CTXT_CEQ_ATTR_INTR_SHIFT			21
+#define RQ_CTXT_CEQ_ATTR_INTR_ARM_SHIFT			30
+#define RQ_CTXT_CEQ_ATTR_EN_SHIFT			31
+
+#define RQ_CTXT_CEQ_ATTR_INTR_MASK			0x3FFU
+#define RQ_CTXT_CEQ_ATTR_INTR_ARM_MASK			0x1U
+#define RQ_CTXT_CEQ_ATTR_EN_MASK			0x1U
+
+#define RQ_CTXT_CEQ_ATTR_SET(val, member)		(((val) & \
+					RQ_CTXT_CEQ_ATTR_##member##_MASK) \
+					<< RQ_CTXT_CEQ_ATTR_##member##_SHIFT)
+
+#define RQ_CTXT_WQ_PAGE_HI_PFN_SHIFT			0
+#define RQ_CTXT_WQ_PAGE_WQE_TYPE_SHIFT			28
+#define RQ_CTXT_WQ_PAGE_OWNER_SHIFT			31
+
+#define RQ_CTXT_WQ_PAGE_HI_PFN_MASK			0xFFFFFU
+#define RQ_CTXT_WQ_PAGE_WQE_TYPE_MASK			0x3U
+#define RQ_CTXT_WQ_PAGE_OWNER_MASK			0x1U
+
+#define RQ_CTXT_WQ_PAGE_SET(val, member)		(((val) & \
+					RQ_CTXT_WQ_PAGE_##member##_MASK) << \
+					RQ_CTXT_WQ_PAGE_##member##_SHIFT)
+
+#define RQ_CTXT_CQE_LEN_SHIFT				28
+
+#define RQ_CTXT_CQE_LEN_MASK				0x3U
+
+#define RQ_CTXT_CQE_LEN_SET(val, member)		(((val) & \
+					RQ_CTXT_##member##_MASK) << \
+					RQ_CTXT_##member##_SHIFT)
+
+#define RQ_CTXT_PREF_CACHE_THRESHOLD_SHIFT		0
+#define RQ_CTXT_PREF_CACHE_MAX_SHIFT			14
+#define RQ_CTXT_PREF_CACHE_MIN_SHIFT			25
+
+#define RQ_CTXT_PREF_CACHE_THRESHOLD_MASK		0x3FFFU
+#define RQ_CTXT_PREF_CACHE_MAX_MASK			0x7FFU
+#define RQ_CTXT_PREF_CACHE_MIN_MASK			0x7FU
+
+#define RQ_CTXT_PREF_CI_HI_SHIFT			0
+#define RQ_CTXT_PREF_OWNER_SHIFT			4
+
+#define RQ_CTXT_PREF_CI_HI_MASK				0xFU
+#define RQ_CTXT_PREF_OWNER_MASK				0x1U
+
+#define RQ_CTXT_PREF_WQ_PFN_HI_SHIFT			0
+#define RQ_CTXT_PREF_CI_LOW_SHIFT			20
+
+#define RQ_CTXT_PREF_WQ_PFN_HI_MASK			0xFFFFFU
+#define RQ_CTXT_PREF_CI_LOW_MASK			0xFFFU
+
+#define RQ_CTXT_PREF_SET(val, member)			(((val) & \
+					RQ_CTXT_PREF_##member##_MASK) << \
+					RQ_CTXT_PREF_##member##_SHIFT)
+
+#define RQ_CTXT_WQ_BLOCK_PFN_HI_SHIFT			0
+
+#define RQ_CTXT_WQ_BLOCK_PFN_HI_MASK			0x7FFFFFU
+
+#define RQ_CTXT_WQ_BLOCK_SET(val, member)		(((val) & \
+					RQ_CTXT_WQ_BLOCK_##member##_MASK) << \
+					RQ_CTXT_WQ_BLOCK_##member##_SHIFT)
+
+#define SIZE_16BYTES(size)		(RTE_ALIGN((size), 16) >> 4)
+
+#define	WQ_PAGE_PFN_SHIFT				12
+#define	WQ_BLOCK_PFN_SHIFT				9
+
+#define WQ_PAGE_PFN(page_addr)		((page_addr) >> WQ_PAGE_PFN_SHIFT)
+#define WQ_BLOCK_PFN(page_addr)		((page_addr) >> WQ_BLOCK_PFN_SHIFT)
+
+static void
+spnic_qp_prepare_cmdq_header(struct spnic_qp_ctxt_header *qp_ctxt_hdr,
+			     enum spnic_qp_ctxt_type ctxt_type,
+			     u16 num_queues, u16 q_id)
+{
+	qp_ctxt_hdr->queue_type = ctxt_type;
+	qp_ctxt_hdr->num_queues = num_queues;
+	qp_ctxt_hdr->start_qid = q_id;
+	qp_ctxt_hdr->rsvd = 0;
+
+	spnic_cpu_to_be32(qp_ctxt_hdr, sizeof(*qp_ctxt_hdr));
+}
+
+static void spnic_sq_prepare_ctxt(struct spnic_txq *sq, u16 sq_id,
+				  struct spnic_sq_ctxt *sq_ctxt)
+{
+	u64 wq_page_addr;
+	u64 wq_page_pfn, wq_block_pfn;
+	u32 wq_page_pfn_hi, wq_page_pfn_lo;
+	u32 wq_block_pfn_hi, wq_block_pfn_lo;
+	u16 pi_start, ci_start;
+
+	ci_start = sq->cons_idx & sq->q_mask;
+	pi_start = sq->prod_idx & sq->q_mask;
+
+	/* Read the first page from hardware table */
+	wq_page_addr = sq->queue_buf_paddr;
+
+	wq_page_pfn = WQ_PAGE_PFN(wq_page_addr);
+	wq_page_pfn_hi = upper_32_bits(wq_page_pfn);
+	wq_page_pfn_lo = lower_32_bits(wq_page_pfn);
+
+	/* Use 0-level CLA */
+	wq_block_pfn = WQ_BLOCK_PFN(wq_page_addr);
+	wq_block_pfn_hi = upper_32_bits(wq_block_pfn);
+	wq_block_pfn_lo = lower_32_bits(wq_block_pfn);
+
+	sq_ctxt->ci_pi = SQ_CTXT_CI_PI_SET(ci_start, CI_IDX) |
+			 SQ_CTXT_CI_PI_SET(pi_start, PI_IDX);
+
+	sq_ctxt->drop_mode_sp = SQ_CTXT_MODE_SET(0, SP_FLAG) |
+				SQ_CTXT_MODE_SET(0, PKT_DROP);
+
+	sq_ctxt->wq_pfn_hi_owner = SQ_CTXT_WQ_PAGE_SET(wq_page_pfn_hi, HI_PFN) |
+				   SQ_CTXT_WQ_PAGE_SET(1, OWNER);
+
+	sq_ctxt->wq_pfn_lo = wq_page_pfn_lo;
+
+	sq_ctxt->pkt_drop_thd =
+		SQ_CTXT_PKT_DROP_THD_SET(SPNIC_DEAULT_DROP_THD_ON, THD_ON) |
+		SQ_CTXT_PKT_DROP_THD_SET(SPNIC_DEAULT_DROP_THD_OFF, THD_OFF);
+
+	sq_ctxt->global_sq_id =
+		SQ_CTXT_GLOBAL_QUEUE_ID_SET(sq_id, GLOBAL_SQ_ID);
+
+	/* Insert c-vlan in default */
+	sq_ctxt->vlan_ceq_attr = SQ_CTXT_VLAN_CEQ_SET(0, CEQ_EN) |
+				 SQ_CTXT_VLAN_CEQ_SET(1, INSERT_MODE);
+
+	sq_ctxt->rsvd0 = 0;
+
+	sq_ctxt->pref_cache = SQ_CTXT_PREF_SET(WQ_PREFETCH_MIN, CACHE_MIN) |
+			      SQ_CTXT_PREF_SET(WQ_PREFETCH_MAX, CACHE_MAX) |
+			      SQ_CTXT_PREF_SET(WQ_PREFETCH_THRESHOLD,
+					       CACHE_THRESHOLD);
+
+	sq_ctxt->pref_ci_owner =
+		SQ_CTXT_PREF_SET(CI_HIGN_IDX(ci_start), CI_HI) |
+		SQ_CTXT_PREF_SET(1, OWNER);
+
+	sq_ctxt->pref_wq_pfn_hi_ci =
+		SQ_CTXT_PREF_SET(ci_start, CI_LOW) |
+		SQ_CTXT_PREF_SET(wq_page_pfn_hi, WQ_PFN_HI);
+
+	sq_ctxt->pref_wq_pfn_lo = wq_page_pfn_lo;
+
+	sq_ctxt->wq_block_pfn_hi =
+		SQ_CTXT_WQ_BLOCK_SET(wq_block_pfn_hi, PFN_HI);
+
+	sq_ctxt->wq_block_pfn_lo = wq_block_pfn_lo;
+
+	spnic_cpu_to_be32(sq_ctxt, sizeof(*sq_ctxt));
+}
+
+static void spnic_rq_prepare_ctxt(struct spnic_rxq *rq,
+				  struct spnic_rq_ctxt *rq_ctxt)
+{
+	u64 wq_page_addr;
+	u64 wq_page_pfn, wq_block_pfn;
+	u32 wq_page_pfn_hi, wq_page_pfn_lo;
+	u32 wq_block_pfn_hi, wq_block_pfn_lo;
+	u16 pi_start, ci_start;
+	u16 wqe_type = rq->wqebb_shift - SPNIC_RQ_WQEBB_SHIFT;
+	u8 intr_disable;
+
+	/* RQ depth is in unit of 8 Bytes */
+	ci_start = (u16)((rq->cons_idx & rq->q_mask) << wqe_type);
+	pi_start = (u16)((rq->prod_idx & rq->q_mask) << wqe_type);
+
+	/* Read the first page from hardware table */
+	wq_page_addr = rq->queue_buf_paddr;
+
+	wq_page_pfn = WQ_PAGE_PFN(wq_page_addr);
+	wq_page_pfn_hi = upper_32_bits(wq_page_pfn);
+	wq_page_pfn_lo = lower_32_bits(wq_page_pfn);
+
+	/* Use 0-level CLA */
+	wq_block_pfn = WQ_BLOCK_PFN(wq_page_addr);
+
+	wq_block_pfn_hi = upper_32_bits(wq_block_pfn);
+	wq_block_pfn_lo = lower_32_bits(wq_block_pfn);
+
+	rq_ctxt->ci_pi = RQ_CTXT_CI_PI_SET(ci_start, CI_IDX) |
+			 RQ_CTXT_CI_PI_SET(pi_start, PI_IDX);
+
+	intr_disable = rq->dp_intr_en ? 0 : 1;
+	rq_ctxt->ceq_attr = RQ_CTXT_CEQ_ATTR_SET(intr_disable, EN) |
+			    RQ_CTXT_CEQ_ATTR_SET(0, INTR_ARM) |
+			    RQ_CTXT_CEQ_ATTR_SET(rq->msix_entry_idx, INTR);
+
+	/* Use 32Byte WQE with SGE for CQE in default */
+	rq_ctxt->wq_pfn_hi_type_owner =
+		RQ_CTXT_WQ_PAGE_SET(wq_page_pfn_hi, HI_PFN) |
+		RQ_CTXT_WQ_PAGE_SET(1, OWNER);
+
+	switch (wqe_type) {
+	case SPNIC_EXTEND_RQ_WQE:
+		/* Use 32Byte WQE with SGE for CQE */
+		rq_ctxt->wq_pfn_hi_type_owner |=
+			RQ_CTXT_WQ_PAGE_SET(0, WQE_TYPE);
+		break;
+	case SPNIC_NORMAL_RQ_WQE:
+		/* Use 16Byte WQE with 32Bytes SGE for CQE */
+		rq_ctxt->wq_pfn_hi_type_owner |=
+			RQ_CTXT_WQ_PAGE_SET(2, WQE_TYPE);
+		rq_ctxt->cqe_sge_len = RQ_CTXT_CQE_LEN_SET(1, CQE_LEN);
+		break;
+	default:
+		PMD_DRV_LOG(INFO, "Invalid rq wqe type: %u", wqe_type);
+	}
+
+	rq_ctxt->wq_pfn_lo = wq_page_pfn_lo;
+
+	rq_ctxt->pref_cache =
+		RQ_CTXT_PREF_SET(WQ_PREFETCH_MIN, CACHE_MIN) |
+		RQ_CTXT_PREF_SET(WQ_PREFETCH_MAX, CACHE_MAX) |
+		RQ_CTXT_PREF_SET(WQ_PREFETCH_THRESHOLD, CACHE_THRESHOLD);
+
+	rq_ctxt->pref_ci_owner =
+		RQ_CTXT_PREF_SET(CI_HIGN_IDX(ci_start), CI_HI) |
+		RQ_CTXT_PREF_SET(1, OWNER);
+
+	rq_ctxt->pref_wq_pfn_hi_ci =
+		RQ_CTXT_PREF_SET(wq_page_pfn_hi, WQ_PFN_HI) |
+		RQ_CTXT_PREF_SET(ci_start, CI_LOW);
+
+	rq_ctxt->pref_wq_pfn_lo = wq_page_pfn_lo;
+
+	rq_ctxt->pi_paddr_hi = upper_32_bits(rq->pi_dma_addr);
+	rq_ctxt->pi_paddr_lo = lower_32_bits(rq->pi_dma_addr);
+
+	rq_ctxt->wq_block_pfn_hi =
+		RQ_CTXT_WQ_BLOCK_SET(wq_block_pfn_hi, PFN_HI);
+
+	rq_ctxt->wq_block_pfn_lo = wq_block_pfn_lo;
+
+	spnic_cpu_to_be32(rq_ctxt, sizeof(*rq_ctxt));
+}
+
+static int init_sq_ctxts(struct spnic_nic_dev *nic_dev)
+{
+	struct spnic_sq_ctxt_block *sq_ctxt_block = NULL;
+	struct spnic_sq_ctxt *sq_ctxt = NULL;
+	struct spnic_cmd_buf *cmd_buf = NULL;
+	struct spnic_txq *sq = NULL;
+	u64 out_param = 0;
+	u16 q_id, curr_id, max_ctxts, i;
+	int err = 0;
+
+	cmd_buf = spnic_alloc_cmd_buf(nic_dev->hwdev);
+	if (!cmd_buf) {
+		PMD_DRV_LOG(ERR, "Allocate cmd buf for sq ctx failed");
+		return -ENOMEM;
+	}
+
+	q_id = 0;
+	while (q_id < nic_dev->num_sqs) {
+		sq_ctxt_block = cmd_buf->buf;
+		sq_ctxt = sq_ctxt_block->sq_ctxt;
+
+		max_ctxts = (nic_dev->num_sqs - q_id) > SPNIC_Q_CTXT_MAX ?
+			     SPNIC_Q_CTXT_MAX : (nic_dev->num_sqs - q_id);
+
+		spnic_qp_prepare_cmdq_header(&sq_ctxt_block->cmdq_hdr,
+					      SPNIC_QP_CTXT_TYPE_SQ,
+					      max_ctxts, q_id);
+
+		for (i = 0; i < max_ctxts; i++) {
+			curr_id = q_id + i;
+			sq = nic_dev->txqs[curr_id];
+			spnic_sq_prepare_ctxt(sq, curr_id, &sq_ctxt[i]);
+		}
+
+		cmd_buf->size = SQ_CTXT_SIZE(max_ctxts);
+		err = spnic_cmdq_direct_resp(nic_dev->hwdev, SPNIC_MOD_L2NIC,
+					      SPNIC_UCODE_CMD_MODIFY_QUEUE_CTX,
+					      cmd_buf, &out_param, 0);
+		if (err || out_param != 0) {
+			PMD_DRV_LOG(ERR, "Set SQ ctxts failed, "
+				    "err: %d, out_param: %"PRIu64"",
+				    err, out_param);
+
+			err = -EFAULT;
+			break;
+		}
+
+		q_id += max_ctxts;
+	}
+
+	spnic_free_cmd_buf(cmd_buf);
+	return err;
+}
+
+static int init_rq_ctxts(struct spnic_nic_dev *nic_dev)
+{
+	struct spnic_rq_ctxt_block *rq_ctxt_block = NULL;
+	struct spnic_rq_ctxt *rq_ctxt = NULL;
+	struct spnic_cmd_buf *cmd_buf = NULL;
+	struct spnic_rxq *rq = NULL;
+	u64 out_param = 0;
+	u16 q_id, curr_id, max_ctxts, i;
+	int err = 0;
+
+	cmd_buf = spnic_alloc_cmd_buf(nic_dev->hwdev);
+	if (!cmd_buf) {
+		PMD_DRV_LOG(ERR, "Allocate cmd buf for rq ctx failed");
+		return -ENOMEM;
+	}
+
+	q_id = 0;
+	while (q_id < nic_dev->num_rqs) {
+		rq_ctxt_block = cmd_buf->buf;
+		rq_ctxt = rq_ctxt_block->rq_ctxt;
+
+		max_ctxts = (nic_dev->num_rqs - q_id) > SPNIC_Q_CTXT_MAX ?
+			    SPNIC_Q_CTXT_MAX : (nic_dev->num_rqs - q_id);
+
+		spnic_qp_prepare_cmdq_header(&rq_ctxt_block->cmdq_hdr,
+					      SPNIC_QP_CTXT_TYPE_RQ, max_ctxts,
+					      q_id);
+
+		for (i = 0; i < max_ctxts; i++) {
+			curr_id = q_id + i;
+			rq = nic_dev->rxqs[curr_id];
+
+			spnic_rq_prepare_ctxt(rq, &rq_ctxt[i]);
+		}
+
+		cmd_buf->size = RQ_CTXT_SIZE(max_ctxts);
+		err = spnic_cmdq_direct_resp(nic_dev->hwdev, SPNIC_MOD_L2NIC,
+					      SPNIC_UCODE_CMD_MODIFY_QUEUE_CTX,
+					      cmd_buf, &out_param, 0);
+		if (err || out_param != 0) {
+			PMD_DRV_LOG(ERR, "Set RQ ctxts failed, "
+				    "err: %d, out_param: %"PRIu64"",
+				    err, out_param);
+			err = -EFAULT;
+			break;
+		}
+
+		q_id += max_ctxts;
+	}
+
+	spnic_free_cmd_buf(cmd_buf);
+	return err;
+}
+
+static int clean_queue_offload_ctxt(struct spnic_nic_dev *nic_dev,
+				    enum spnic_qp_ctxt_type ctxt_type)
+{
+	struct spnic_clean_queue_ctxt *ctxt_block = NULL;
+	struct spnic_cmd_buf *cmd_buf;
+	u64 out_param = 0;
+	int err;
+
+	cmd_buf = spnic_alloc_cmd_buf(nic_dev->hwdev);
+	if (!cmd_buf) {
+		PMD_DRV_LOG(ERR, "Allocate cmd buf for LRO/TSO space failed");
+		return -ENOMEM;
+	}
+
+	ctxt_block = cmd_buf->buf;
+	ctxt_block->cmdq_hdr.num_queues = nic_dev->max_sqs;
+	ctxt_block->cmdq_hdr.queue_type = ctxt_type;
+	ctxt_block->cmdq_hdr.start_qid = 0;
+
+	spnic_cpu_to_be32(ctxt_block, sizeof(*ctxt_block));
+
+	cmd_buf->size = sizeof(*ctxt_block);
+
+	err = spnic_cmdq_direct_resp(nic_dev->hwdev, SPNIC_MOD_L2NIC,
+				      SPNIC_UCODE_CMD_CLEAN_QUEUE_CONTEXT,
+				      cmd_buf, &out_param, 0);
+	if ((err) || (out_param)) {
+		PMD_DRV_LOG(ERR, "Clean queue offload ctxts failed, "
+			    "err: %d, out_param: %"PRIu64"", err, out_param);
+		err = -EFAULT;
+	}
+
+	spnic_free_cmd_buf(cmd_buf);
+	return err;
+}
+
+static int clean_qp_offload_ctxt(struct spnic_nic_dev *nic_dev)
+{
+	/* Clean LRO/TSO context space */
+	return (clean_queue_offload_ctxt(nic_dev, SPNIC_QP_CTXT_TYPE_SQ) ||
+		clean_queue_offload_ctxt(nic_dev, SPNIC_QP_CTXT_TYPE_RQ));
+}
+
+void spnic_get_func_rx_buf_size(void *dev)
+{
+	struct spnic_nic_dev *nic_dev = (struct spnic_nic_dev *)dev;
+	struct spnic_rxq *rxq = NULL;
+	u16 q_id;
+	u16 buf_size = 0;
+
+	for (q_id = 0; q_id < nic_dev->num_rqs; q_id++) {
+		rxq = nic_dev->rxqs[q_id];
+
+		if (rxq == NULL)
+			continue;
+
+		if (q_id == 0)
+			buf_size = rxq->buf_len;
+
+		buf_size = buf_size > rxq->buf_len ? rxq->buf_len : buf_size;
+	}
+
+	nic_dev->rx_buff_len = buf_size;
+}
+
+/* Init qps ctxt and set sq ci attr and arm all sq */
+int spnic_init_qp_ctxts(void *dev)
+{
+	struct spnic_nic_dev *nic_dev = NULL;
+	struct spnic_hwdev *hwdev = NULL;
+	struct spnic_sq_attr sq_attr;
+	u32 rq_depth;
+	u16 q_id;
+	int err;
+
+	if (!dev)
+		return -EINVAL;
+
+	nic_dev = (struct spnic_nic_dev *)dev;
+	hwdev = nic_dev->hwdev;
+
+	err = init_sq_ctxts(nic_dev);
+	if (err) {
+		PMD_DRV_LOG(ERR, "Init SQ ctxts failed");
+		return err;
+	}
+
+	err = init_rq_ctxts(nic_dev);
+	if (err) {
+		PMD_DRV_LOG(ERR, "Init RQ ctxts failed");
+		return err;
+	}
+
+	err = clean_qp_offload_ctxt(nic_dev);
+	if (err) {
+		PMD_DRV_LOG(ERR, "Clean qp offload ctxts failed");
+		return err;
+	}
+
+	rq_depth = ((u32)nic_dev->rxqs[0]->q_depth) <<
+		   nic_dev->rxqs[0]->wqe_type;
+	err = spnic_set_root_ctxt(hwdev, rq_depth, nic_dev->txqs[0]->q_depth,
+				   nic_dev->rx_buff_len);
+	if (err) {
+		PMD_DRV_LOG(ERR, "Set root context failed");
+		return err;
+	}
+
+	for (q_id = 0; q_id < nic_dev->num_sqs; q_id++) {
+		sq_attr.ci_dma_base = nic_dev->txqs[q_id]->ci_dma_base >> 2;
+		sq_attr.pending_limit = SPNIC_DEAULT_TX_CI_PENDING_LIMIT;
+		sq_attr.coalescing_time = SPNIC_DEAULT_TX_CI_COALESCING_TIME;
+		sq_attr.intr_en = 0;
+		sq_attr.intr_idx = 0; /* Tx doesn't need intr */
+		sq_attr.l2nic_sqn = q_id;
+		sq_attr.dma_attr_off = 0;
+		err = spnic_set_ci_table(hwdev, &sq_attr);
+		if (err) {
+			PMD_DRV_LOG(ERR, "Set ci table failed");
+			goto set_cons_idx_table_err;
+		}
+	}
+
+	return 0;
+
+set_cons_idx_table_err:
+	spnic_clean_root_ctxt(hwdev);
+	return err;
+}
+
+void spnic_free_qp_ctxts(void *hwdev)
+{
+	if (!hwdev)
+		return;
+
+	spnic_clean_root_ctxt(hwdev);
+}
+
+void spnic_update_driver_feature(void *dev, u64 s_feature)
+{
+	struct spnic_nic_dev *nic_dev = NULL;
+
+	if (!dev)
+		return;
+
+	nic_dev = (struct spnic_nic_dev *)dev;
+	nic_dev->feature_cap = s_feature;
+
+	PMD_DRV_LOG(INFO, "Update nic feature to %"PRIu64"\n",
+		    nic_dev->feature_cap);
+}
+
+u64 spnic_get_driver_feature(void *dev)
+{
+	struct spnic_nic_dev *nic_dev = NULL;
+
+	if (!dev)
+		return -EINVAL;
+
+	nic_dev = (struct spnic_nic_dev *)dev;
+
+	return nic_dev->feature_cap;
+}
diff --git a/drivers/net/spnic/spnic_io.h b/drivers/net/spnic/spnic_io.h
new file mode 100644
index 0000000000..c59b2f42ec
--- /dev/null
+++ b/drivers/net/spnic/spnic_io.h
@@ -0,0 +1,154 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2021 Ramaxel Memory Technology, Ltd
+ */
+
+#ifndef _SPNIC_IO_H_
+#define _SPNIC_IO_H_
+
+#define SPNIC_SQ_WQEBB_SHIFT			4
+#define SPNIC_RQ_WQEBB_SHIFT			3
+
+#define SPNIC_SQ_WQEBB_SIZE	BIT(SPNIC_SQ_WQEBB_SHIFT)
+#define SPNIC_CQE_SIZE_SHIFT			4
+
+/* Ci addr should RTE_CACHE_SIZE(64B) alignment for performance */
+#define SPNIC_CI_Q_ADDR_SIZE			64
+
+#define CI_TABLE_SIZE(num_qps, pg_sz)	\
+			(RTE_ALIGN((num_qps) * SPNIC_CI_Q_ADDR_SIZE, pg_sz))
+
+#define SPNIC_CI_VADDR(base_addr, q_id)	((u8 *)(base_addr) + \
+						(q_id) * SPNIC_CI_Q_ADDR_SIZE)
+
+#define SPNIC_CI_PADDR(base_paddr, q_id)	((base_paddr) + \
+						(q_id) * SPNIC_CI_Q_ADDR_SIZE)
+
+enum spnic_rq_wqe_type {
+	SPNIC_COMPACT_RQ_WQE,
+	SPNIC_NORMAL_RQ_WQE,
+	SPNIC_EXTEND_RQ_WQE,
+};
+
+enum spnic_queue_type {
+	SPNIC_SQ,
+	SPNIC_RQ,
+	SPNIC_MAX_QUEUE_TYPE
+};
+
+/* Doorbell info */
+struct spnic_db {
+	u32 db_info;
+	u32 pi_hi;
+};
+
+#define DB_INFO_QID_SHIFT			0
+#define DB_INFO_NON_FILTER_SHIFT		22
+#define DB_INFO_CFLAG_SHIFT			23
+#define DB_INFO_COS_SHIFT			24
+#define DB_INFO_TYPE_SHIFT			27
+
+#define DB_INFO_QID_MASK			0x1FFFU
+#define DB_INFO_NON_FILTER_MASK			0x1U
+#define DB_INFO_CFLAG_MASK			0x1U
+#define DB_INFO_COS_MASK			0x7U
+#define DB_INFO_TYPE_MASK			0x1FU
+#define DB_INFO_SET(val, member)		(((u32)(val) & \
+					DB_INFO_##member##_MASK) << \
+					DB_INFO_##member##_SHIFT)
+
+#define DB_PI_LOW_MASK	0xFFU
+#define DB_PI_HIGH_MASK	0xFFU
+#define DB_PI_LOW(pi)	((pi) & DB_PI_LOW_MASK)
+#define DB_PI_HI_SHIFT	8
+#define DB_PI_HIGH(pi)	(((pi) >> DB_PI_HI_SHIFT) & DB_PI_HIGH_MASK)
+#define DB_INFO_UPPER_32(val) (((u64)val) << 32)
+
+#define DB_ADDR(db_addr, pi)	((u64 *)(db_addr) + DB_PI_LOW(pi))
+#define SRC_TYPE		1
+
+/* Cflag data path */
+#define SQ_CFLAG_DP		0
+#define RQ_CFLAG_DP		1
+
+#define MASKED_QUEUE_IDX(queue, idx) ((idx) & (queue)->q_mask)
+
+#define	NIC_WQE_ADDR(queue, idx) ((void *)((u64)((queue)->queue_buf_vaddr) + \
+				       ((idx) << (queue)->wqebb_shift)))
+
+#define SPNIC_FLUSH_QUEUE_TIMEOUT	3000
+
+/**
+ * Write send queue doorbell
+ *
+ * @param[in] db_addr
+ *   Doorbell address
+ * @param[in] q_id
+ *   Send queue id
+ * @param[in] cos
+ *   Send queue cos
+ * @param[in] cflag
+ *   Cflag data path
+ * @param[in] pi
+ *   Send queue pi
+ */
+static inline void spnic_write_db(void *db_addr, u16 q_id, int cos, u8 cflag,
+				  u16 pi)
+{
+	u64 db;
+
+	/* Hardware will do endianness coverting */
+	db = DB_PI_HIGH(pi);
+	db = DB_INFO_UPPER_32(db) | DB_INFO_SET(SRC_TYPE, TYPE) |
+	     DB_INFO_SET(cflag, CFLAG) | DB_INFO_SET(cos, COS) |
+	     DB_INFO_SET(q_id, QID);
+
+	rte_wmb(); /* Write all before the doorbell */
+
+	rte_write64(*((u64 *)&db), DB_ADDR(db_addr, pi));
+}
+
+void spnic_get_func_rx_buf_size(void *dev);
+
+/**
+ * Init queue pair context
+ *
+ * @param[in] dev
+ *   Device pointer to nic device
+ *
+ * @retval zero: Success
+ * @retval non-zero: Failure
+ */
+int spnic_init_qp_ctxts(void *dev);
+
+/**
+ * Free queue pair context
+ *
+ * @param[in] hwdev
+ *   Device pointer to hwdev
+ */
+void spnic_free_qp_ctxts(void *hwdev);
+
+/**
+ * Update service feature driver supported
+ *
+ * @param[in] dev
+ *   Device pointer to nic device
+ * @param[out] s_feature
+ *   s_feature driver supported
+ * @retval zero: Success
+ * @retval non-zero: Failure
+ */
+void spnic_update_driver_feature(void *dev, u64 s_feature);
+
+/**
+ * Get service feature driver supported
+ *
+ * @param[in] dev
+ *   Device pointer to nic device
+ * @param[out] s_feature
+ *   s_feature driver supported
+ * @retval zero: Success
+ * @retval non-zero: Failure
+ */
+u64 spnic_get_driver_feature(void *dev);
+#endif /* _SPNIC_IO_H_ */
diff --git a/drivers/net/spnic/spnic_rx.h b/drivers/net/spnic/spnic_rx.h
new file mode 100644
index 0000000000..b2f0052533
--- /dev/null
+++ b/drivers/net/spnic/spnic_rx.h
@@ -0,0 +1,113 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2021 Ramaxel Memory Technology, Ltd
+ */
+
+#ifndef _SPNIC_RX_H_
+#define _SPNIC_RX_H_
+
+struct spnic_rxq_stats {
+	u64 packets;
+	u64 bytes;
+	u64 errors;
+	u64 csum_errors;
+	u64 other_errors;
+	u64 unlock_bp;
+	u64 dropped;
+
+	u64 rx_nombuf;
+	u64 rx_discards;
+	u64 burst_pkts;
+};
+
+struct spnic_rq_cqe {
+	u32 status;
+	u32 vlan_len;
+
+	u32 offload_type;
+	u32 hash_val;
+	u32 xid;
+	u32 decrypt_info;
+	u32 rsvd6;
+	u32 pkt_info;
+};
+
+/*
+ * Attention: please do not add any member in spnic_rx_info because rxq bulk
+ * rearm mode will write mbuf in rx_info
+ */
+struct spnic_rx_info {
+	struct rte_mbuf *mbuf;
+};
+
+struct spnic_sge_sect {
+	struct spnic_sge sge;
+	u32 rsvd;
+};
+
+struct spnic_rq_extend_wqe {
+	struct spnic_sge_sect buf_desc;
+	struct spnic_sge_sect cqe_sect;
+};
+
+struct spnic_rq_normal_wqe {
+	u32 buf_hi_addr;
+	u32 buf_lo_addr;
+	u32 cqe_hi_addr;
+	u32 cqe_lo_addr;
+};
+
+struct spnic_rq_wqe {
+	union {
+		struct spnic_rq_normal_wqe normal_wqe;
+		struct spnic_rq_extend_wqe extend_wqe;
+	};
+};
+
+struct spnic_rxq {
+	struct spnic_nic_dev *nic_dev;
+
+	u16 q_id;
+	u16 q_depth;
+	u16 q_mask;
+	u16 buf_len;
+
+	u32 rx_buff_shift;
+
+	u16 rx_free_thresh;
+	u16 rxinfo_align_end;
+	u16 wqebb_shift;
+	u16 wqebb_size;
+
+	u16 wqe_type;
+	u16 cons_idx;
+	u16 prod_idx;
+	u16 delta;
+
+	u16 next_to_update;
+	u16 port_id;
+
+	const struct rte_memzone *rq_mz;
+	void *queue_buf_vaddr; /* Rq dma info */
+	rte_iova_t queue_buf_paddr;
+
+	const struct rte_memzone *pi_mz;
+	u16 *pi_virt_addr;
+	void *db_addr;
+	rte_iova_t pi_dma_addr;
+
+	struct spnic_rx_info *rx_info;
+	struct spnic_rq_cqe *rx_cqe;
+	struct rte_mempool *mb_pool;
+
+	const struct rte_memzone *cqe_mz;
+	rte_iova_t cqe_start_paddr;
+	void *cqe_start_vaddr;
+	u8 dp_intr_en;
+	u16 msix_entry_idx;
+
+	unsigned long status;
+
+	struct spnic_rxq_stats	rxq_stats;
+} __rte_cache_aligned;
+
+#endif /* _SPNIC_RX_H_ */
diff --git a/drivers/net/spnic/spnic_tx.h b/drivers/net/spnic/spnic_tx.h
new file mode 100644
index 0000000000..7528b27bd9
--- /dev/null
+++ b/drivers/net/spnic/spnic_tx.h
@@ -0,0 +1,62 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2021 Ramaxel Memory Technology, Ltd
+ */
+
+#ifndef _SPNIC_TX_H_
+#define _SPNIC_TX_H_
+
+/* Txq info */
+struct spnic_txq_stats {
+	u64 packets;
+	u64 bytes;
+	u64 tx_busy;
+	u64 off_errs;
+	u64 burst_pkts;
+	u64 sge_len0;
+	u64 mbuf_null;
+	u64 cpy_pkts;
+	u64 sge_len_too_large;
+};
+
+struct spnic_tx_info {
+	struct rte_mbuf *mbuf;
+	struct rte_mbuf *cpy_mbuf;
+	int wqebb_cnt;
+};
+
+struct spnic_txq {
+	struct spnic_nic_dev *nic_dev;
+
+	u16 q_id;
+	u16 q_depth;
+	u16 q_mask;
+	u16 wqebb_size;
+
+	u16 wqebb_shift;
+	u16 cons_idx;
+	u16 prod_idx;
+
+	u16 tx_free_thresh;
+	u16 owner; /* Used for sq */
+
+	void *db_addr;
+
+	struct spnic_tx_info *tx_info;
+
+	const struct rte_memzone *sq_mz;
+	void *queue_buf_vaddr;
+	rte_iova_t queue_buf_paddr; /* Sq dma info */
+
+	const struct rte_memzone *ci_mz;
+	void *ci_vaddr_base;
+	rte_iova_t ci_dma_base;
+
+	u64 sq_head_addr;
+	u64 sq_bot_sge_addr;
+
+	u32 cos;
+
+	struct spnic_txq_stats txq_stats;
+} __rte_cache_aligned;
+
+#endif /* _SPNIC_TX_H_ */
