diff --git a/drivers/net/ethernet/intel/iecm/Makefile b/drivers/net/ethernet/intel/iecm/Makefile
index db8fecb075a6..fcb49402334f 100644
--- a/drivers/net/ethernet/intel/iecm/Makefile
+++ b/drivers/net/ethernet/intel/iecm/Makefile
@@ -7,11 +7,13 @@
 
 obj-$(CONFIG_IECM) += iecm.o
 
-ccflags-y += -I$(srctree)/drivers/net/ethernet/intel/include
+ccflags-y += -I$(srctree)/drivers/net/ethernet/intel/include \
+	     -I$(srctree)/include/linux/avf
 
 iecm-y := \
 	iecm_lib.o \
 	iecm_virtchnl.o \
+	iecm_txrx.o \
 	iecm_controlq.o \
 	iecm_controlq_setup.o \
 	iecm_main.o
diff --git a/drivers/net/ethernet/intel/iecm/iecm_lib.c b/drivers/net/ethernet/intel/iecm/iecm_lib.c
index 64cdbce2c842..e2e523f0700e 100644
--- a/drivers/net/ethernet/intel/iecm/iecm_lib.c
+++ b/drivers/net/ethernet/intel/iecm/iecm_lib.c
@@ -5,6 +5,11 @@
 
 #include "iecm.h"
 
+const char * const iecm_vport_vc_state_str[] = {
+	IECM_FOREACH_VPORT_VC_STATE(IECM_GEN_STRING)
+};
+EXPORT_SYMBOL(iecm_vport_vc_state_str);
+
 /**
  * iecm_cfg_hw - Initialize HW struct
  * @adapter: adapter to setup hw struct for
@@ -24,6 +29,113 @@ static int iecm_cfg_hw(struct iecm_adapter *adapter)
 	return 0;
 }
 
+/**
+ * iecm_get_free_slot - get the next non-NULL location index in array
+ * @array: array to search
+ * @size: size of the array
+ * @curr: last known occupied index to be used as a search hint
+ *
+ * void * is being used to keep the functionality generic. This lets us use this
+ * function on any array of pointers.
+ */
+static int iecm_get_free_slot(void *array, int size, int curr)
+{
+	int **tmp_array = (int **)array;
+	int next;
+
+	if (curr < (size - 1) && !tmp_array[curr + 1]) {
+		next = curr + 1;
+	} else {
+		int i = 0;
+
+		while ((i < size) && (tmp_array[i]))
+			i++;
+		if (i == size)
+			next = IECM_NO_FREE_SLOT;
+		else
+			next = i;
+	}
+	return next;
+}
+
+/**
+ * iecm_vport_rel - Delete a vport and free its resources
+ * @vport: the vport being removed
+ */
+static void iecm_vport_rel(struct iecm_vport *vport)
+{
+	mutex_destroy(&vport->stop_mutex);
+	kfree(vport);
+}
+
+/**
+ * iecm_vport_rel_all - Delete all vports
+ * @adapter: adapter from which all vports are being removed
+ */
+static void iecm_vport_rel_all(struct iecm_adapter *adapter)
+{
+	int i;
+
+	if (!adapter->vports)
+		return;
+
+	for (i = 0; i < adapter->num_alloc_vport; i++) {
+		if (!adapter->vports[i])
+			continue;
+
+		iecm_vport_rel(adapter->vports[i]);
+		adapter->vports[i] = NULL;
+		adapter->next_vport = 0;
+	}
+	adapter->num_alloc_vport = 0;
+}
+
+/**
+ * iecm_vport_alloc - Allocates the next available struct vport in the adapter
+ * @adapter: board private structure
+ * @vport_id: vport identifier
+ *
+ * returns a pointer to a vport on success, NULL on failure.
+ */
+static struct iecm_vport *
+iecm_vport_alloc(struct iecm_adapter *adapter, int vport_id)
+{
+	struct iecm_vport *vport = NULL;
+
+	if (adapter->next_vport == IECM_NO_FREE_SLOT)
+		return vport;
+
+	/* Need to protect the allocation of the vports at the adapter level */
+	mutex_lock(&adapter->sw_mutex);
+
+	vport = kzalloc(sizeof(*vport), GFP_KERNEL);
+	if (!vport)
+		goto unlock_adapter;
+
+	vport->adapter = adapter;
+	vport->idx = adapter->next_vport;
+	vport->compln_clean_budget = IECM_TX_COMPLQ_CLEAN_BUDGET;
+	adapter->num_alloc_vport++;
+
+	/* Setup default MSIX irq handler for the vport */
+	vport->irq_q_handler = iecm_vport_intr_clean_queues;
+	vport->q_vector_base = IECM_NONQ_VEC;
+
+	mutex_init(&vport->stop_mutex);
+
+	/* fill vport slot in the adapter struct */
+	adapter->vports[adapter->next_vport] = vport;
+
+	/* prepare adapter->next_vport for next use */
+	adapter->next_vport = iecm_get_free_slot(adapter->vports,
+						 adapter->num_alloc_vport,
+						 adapter->next_vport);
+
+unlock_adapter:
+	mutex_unlock(&adapter->sw_mutex);
+	return vport;
+}
+
 /**
  * iecm_statistics_task - Delayed task to get statistics over mailbox
  * @work: work_struct handle to our data
@@ -55,7 +167,25 @@ static void iecm_service_task(struct work_struct *work)
  */
 static void iecm_init_task(struct work_struct *work)
 {
-	/* stub */
+	struct iecm_adapter *adapter = container_of(work,
+						    struct iecm_adapter,
+						    init_task.work);
+	struct iecm_vport *vport;
+	struct pci_dev *pdev;
+	int vport_id, err;
+
+	err = adapter->dev_ops.vc_ops.core_init(adapter, &vport_id);
+	if (err)
+		return;
+
+	pdev = adapter->pdev;
+	vport = iecm_vport_alloc(adapter, vport_id);
+	if (!vport) {
+		err = -EFAULT;
+		dev_err(&pdev->dev, "failed to allocate vport: %d\n",
+			err);
+		return;
+	}
 }
 
 /**
@@ -81,6 +211,31 @@ static int iecm_api_init(struct iecm_adapter *adapter)
 		return -EINVAL;
 	}
 
+	if (adapter->dev_ops.vc_ops_init) {
+		struct iecm_virtchnl_ops *vc_ops;
+
+		adapter->dev_ops.vc_ops_init(adapter);
+		vc_ops = &adapter->dev_ops.vc_ops;
+		if (!(vc_ops->core_init &&
+		      vc_ops->vport_init &&
+		      vc_ops->vport_queue_ids_init &&
+		      vc_ops->get_caps &&
+		      vc_ops->config_queues &&
+		      vc_ops->enable_queues &&
+		      vc_ops->disable_queues &&
+		      vc_ops->irq_map_unmap &&
+		      vc_ops->get_set_rss_lut &&
+		      vc_ops->get_set_rss_hash &&
+		      vc_ops->adjust_qs &&
+		      vc_ops->get_ptype &&
+		      vc_ops->init_max_queues)) {
+			dev_err(&pdev->dev, "Invalid device, missing one or more virtchnl functions\n");
+			return -EINVAL;
+		}
+	} else {
+		iecm_vc_ops_init(adapter);
+	}
+
 	return 0;
 }
 
@@ -93,7 +248,15 @@ static int iecm_api_init(struct iecm_adapter *adapter)
  */
 static void iecm_deinit_task(struct iecm_adapter *adapter)
 {
-	/* stub */
+	set_bit(__IECM_REL_RES_IN_PROG, adapter->flags);
+	/* Wait until the init_task is done else this thread might release
+	 * the resources first and the other thread might end up in a bad state
+	 */
+	cancel_delayed_work_sync(&adapter->init_task);
+	iecm_vport_rel_all(adapter);
+
+	cancel_delayed_work_sync(&adapter->serv_task);
+	cancel_delayed_work_sync(&adapter->stats_task);
 }
 
 /**
diff --git a/drivers/net/ethernet/intel/iecm/iecm_txrx.c b/drivers/net/ethernet/intel/iecm/iecm_txrx.c
new file mode 100644
index 000000000000..2f5c16a28266
--- /dev/null
+++ b/drivers/net/ethernet/intel/iecm/iecm_txrx.c
@@ -0,0 +1,22 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* Copyright (C) 2019 Intel Corporation */
+
+#include "iecm.h"
+
+/**
+ * iecm_vport_intr_clean_queues - MSIX mode Interrupt Handler
+ * @irq: interrupt number
+ * @data: pointer to a q_vector
+ *
+ */
+irqreturn_t
+iecm_vport_intr_clean_queues(int __always_unused irq, void *data)
+{
+	struct iecm_q_vector *q_vector = (struct iecm_q_vector *)data;
+
+	q_vector->total_events++;
+	napi_schedule(&q_vector->napi);
+
+	return IRQ_HANDLED;
+}
+
diff --git a/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c b/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c
index b8f54b8c700a..aae06064d706 100644
--- a/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c
+++ b/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c
@@ -3,6 +3,74 @@
 
 #include "iecm.h"
 
+/**
+ * iecm_recv_event_msg - Receive virtchnl event message
+ * @vport: virtual port structure
+ *
+ * Receive virtchnl event message
+ */
+static void iecm_recv_event_msg(struct iecm_vport *vport)
+{
+	struct iecm_adapter *adapter = vport->adapter;
+	struct virtchnl_pf_event *vpe;
+	struct virtchnl2_event *v2e;
+	bool link_status;
+	u32 event;
+
+	if (adapter->virt_ver_maj < VIRTCHNL_VERSION_MAJOR_2) {
+		vpe = (struct virtchnl_pf_event *)adapter->vc_msg;
+		event = vpe->event;
+	} else {
+		v2e = (struct virtchnl2_event *)adapter->vc_msg;
+		event = le32_to_cpu(v2e->event);
+	}
+
+	switch (event) {
+	case VIRTCHNL2_EVENT_LINK_CHANGE:
+		if (adapter->virt_ver_maj < VIRTCHNL_VERSION_MAJOR_2) {
+			if (iecm_is_cap_ena(adapter, IECM_OTHER_CAPS,
+					    VIRTCHNL2_CAP_LINK_SPEED)) {
+				adapter->link_speed_mbps =
+				vpe->event_data.link_event_adv.link_speed;
+				link_status =
+				vpe->event_data.link_event_adv.link_status;
+			} else {
+				adapter->link_speed =
+				vpe->event_data.link_event.link_speed;
+				link_status =
+				vpe->event_data.link_event.link_status;
+			}
+		} else {
+			adapter->link_speed_mbps = le32_to_cpu(v2e->link_speed);
+			link_status = v2e->link_status;
+		}
+		if (adapter->link_up != link_status) {
+			adapter->link_up = link_status;
+			if (adapter->state == __IECM_UP) {
+				if (adapter->link_up) {
+					netif_tx_start_all_queues(vport->netdev);
+					netif_carrier_on(vport->netdev);
+				} else {
+					netif_tx_stop_all_queues(vport->netdev);
+					netif_carrier_off(vport->netdev);
+				}
+			}
+		}
+		break;
+	case VIRTCHNL_EVENT_RESET_IMPENDING:
+		set_bit(__IECM_HR_CORE_RESET, adapter->flags);
+		queue_delayed_work(adapter->vc_event_wq,
+				   &adapter->vc_event_task,
+				   msecs_to_jiffies(10));
+		break;
+	default:
+		dev_err(&vport->adapter->pdev->dev,
+			"Unknown event %d from PF\n", event);
+		break;
+	}
+	clear_bit(__IECM_VC_MSG_PENDING, adapter->flags);
+}
+
 /**
  * iecm_mb_clean - Reclaim the send mailbox queue entries
  * @adapter: Driver specific private structure
@@ -39,6 +107,865 @@ static int iecm_mb_clean(struct iecm_adapter *adapter)
 	return err;
 }
 
+/**
+ * iecm_send_mb_msg - Send message over mailbox
+ * @adapter: Driver specific private structure
+ * @op: virtchnl opcode
+ * @msg_size: size of the payload
+ * @msg: pointer to buffer holding the payload
+ *
+ * Will prepare the control queue message and initiates the send api
+ *
+ * Returns 0 on success, negative on failure
+ */
+int iecm_send_mb_msg(struct iecm_adapter *adapter, enum virtchnl_ops op,
+		     u16 msg_size, u8 *msg)
+{
+	struct iecm_ctlq_msg *ctlq_msg;
+	struct iecm_dma_mem *dma_mem;
+	int err = 0;
+
+	if (iecm_is_reset_detected(adapter))
+		return -EBUSY;
+
+	err = iecm_mb_clean(adapter);
+	if (err)
+		return err;
+
+	ctlq_msg = kzalloc(sizeof(*ctlq_msg), GFP_KERNEL);
+	if (!ctlq_msg)
+		return -ENOMEM;
+
+	dma_mem = kzalloc(sizeof(*dma_mem), GFP_KERNEL);
+	if (!dma_mem) {
+		err = -ENOMEM;
+		goto dma_mem_error;
+	}
+
+	memset(ctlq_msg, 0, sizeof(struct iecm_ctlq_msg));
+	ctlq_msg->opcode = iecm_mbq_opc_send_msg_to_pf;
+	ctlq_msg->func_id = 0;
+	ctlq_msg->data_len = msg_size;
+	ctlq_msg->cookie.mbx.chnl_opcode = op;
+	ctlq_msg->cookie.mbx.chnl_retval = VIRTCHNL_STATUS_SUCCESS;
+	dma_mem->size = IECM_DFLT_MBX_BUF_SIZE;
+	dma_mem->va = dmam_alloc_coherent(&adapter->pdev->dev, dma_mem->size,
+					  &dma_mem->pa, GFP_KERNEL);
+	if (!dma_mem->va) {
+		err = -ENOMEM;
+		goto dma_alloc_error;
+	}
+	memcpy(dma_mem->va, msg, msg_size);
+	ctlq_msg->ctx.indirect.payload = dma_mem;
+
+	err = iecm_ctlq_send(&adapter->hw, adapter->hw.asq, 1, ctlq_msg);
+	if (err)
+		goto send_error;
+
+	return 0;
+send_error:
+	dmam_free_coherent(&adapter->pdev->dev, dma_mem->size, dma_mem->va,
+			   dma_mem->pa);
+dma_alloc_error:
+	kfree(dma_mem);
+dma_mem_error:
+	kfree(ctlq_msg);
+	return err;
+}
+EXPORT_SYMBOL(iecm_send_mb_msg);
+
+/**
+ * iecm_set_msg_pending_bit - Wait for clear and set msg pending
+ * @adapter: driver specific private structure
+ *
+ * If clear sets msg pending bit, otherwise waits for it to clear before
+ * setting it again. Returns 0 on success, negative on failure.
+ */
+static int iecm_set_msg_pending_bit(struct iecm_adapter *adapter)
+{
+	unsigned int retries = 100;
+
+	/* If msg pending bit already set, there's a message waiting to be
+	 * parsed and we must wait for it to be cleared before copying a new
+	 * message into the vc_msg buffer or else we'll stomp all over the
+	 * previous message.
+	 */
+	while (retries) {
+		if (!test_and_set_bit(__IECM_VC_MSG_PENDING,
+				      adapter->flags))
+			break;
+		msleep(20);
+		retries--;
+	}
+	return retries ? 0 : -ETIMEDOUT;
+}
+
+/**
+ * iecm_set_msg_pending - Wait for msg pending bit and copy msg to buf
+ * @adapter: driver specific private structure
+ * @ctlq_msg: msg to copy from
+ * @err_enum: err bit to set on error
+ *
+ * Copies payload from ctlq_msg into vc_msg buf in adapter and sets msg pending
+ * bit. Returns 0 on success, negative on failure.
+ */
+int iecm_set_msg_pending(struct iecm_adapter *adapter,
+			 struct iecm_ctlq_msg *ctlq_msg,
+			 enum iecm_vport_vc_state err_enum)
+{
+	if (ctlq_msg->cookie.mbx.chnl_retval) {
+		set_bit(err_enum, adapter->vc_state);
+		return -EINVAL;
+	}
+
+	if (iecm_set_msg_pending_bit(adapter)) {
+		set_bit(err_enum, adapter->vc_state);
+		dev_info(&adapter->pdev->dev, "Timed out setting msg pending\n");
+		return -ETIMEDOUT;
+	}
+
+	memcpy(adapter->vc_msg, ctlq_msg->ctx.indirect.payload->va,
+	       min_t(int, ctlq_msg->ctx.indirect.payload->size,
+		     IECM_DFLT_MBX_BUF_SIZE));
+	return 0;
+}
+EXPORT_SYMBOL(iecm_set_msg_pending);
+
+/**
+ * iecm_recv_mb_msg - Receive message over mailbox
+ * @adapter: Driver specific private structure
+ * @op: virtchannel operation code
+ * @msg: Received message holding buffer
+ * @msg_size: message size
+ *
+ * Will receive control queue message and posts the receive buffer. Returns 0
+ * on success and negative on failure.
+ */
+int iecm_recv_mb_msg(struct iecm_adapter *adapter, enum virtchnl_ops op,
+		     void *msg, int msg_size)
+{
+	struct iecm_ctlq_msg ctlq_msg;
+	struct iecm_dma_mem *dma_mem;
+	struct iecm_vport *vport;
+	bool work_done = false;
+	int num_retry = 2000;
+	int payload_size = 0;
+	u16 num_q_msg;
+	int err = 0;
+
+	vport = adapter->vports[0];
+	while (1) {
+		/* Try to get one message */
+		num_q_msg = 1;
+		dma_mem = NULL;
+		err = iecm_ctlq_recv(adapter->hw.arq, &num_q_msg, &ctlq_msg);
+		/* If no message then decide if we have to retry based on
+		 * opcode
+		 */
+		if (err || !num_q_msg) {
+			/* Increasing num_retry to consider the delayed
+			 * responses because of large number of VF's mailbox
+			 * messages. If the mailbox message is received from
+			 * the other side, we come out of the sleep cycle
+			 * immediately else we wait for more time.
+			 */
+			if (op && num_retry-- &&
+			    !test_bit(__IECM_REL_RES_IN_PROG, adapter->flags)) {
+				msleep(20);
+				continue;
+			} else {
+				break;
+			}
+		}
+
+		/* If we are here a message is received. Check if we are looking
+		 * for a specific message based on opcode. If it is different
+		 * ignore and post buffers
+		 */
+		if (op && ctlq_msg.cookie.mbx.chnl_opcode != op)
+			goto post_buffs;
+
+		if (ctlq_msg.data_len)
+			payload_size = ctlq_msg.ctx.indirect.payload->size;
+
+		/* All conditions are met. Either a message requested is
+		 * received or we received a message to be processed
+		 */
+		switch (ctlq_msg.cookie.mbx.chnl_opcode) {
+		case VIRTCHNL_OP_VERSION:
+		case VIRTCHNL2_OP_GET_CAPS:
+		case VIRTCHNL2_OP_CREATE_VPORT:
+			if (ctlq_msg.cookie.mbx.chnl_retval) {
+				dev_info(&adapter->pdev->dev, "Failure initializing, vc op: %u retval: %u\n",
+					 ctlq_msg.cookie.mbx.chnl_opcode,
+					 ctlq_msg.cookie.mbx.chnl_retval);
+				err = -EBADMSG;
+			} else if (msg) {
+				memcpy(msg, ctlq_msg.ctx.indirect.payload->va,
+				       min_t(int,
+					     payload_size, msg_size));
+			}
+			work_done = true;
+			break;
+		case VIRTCHNL2_OP_ENABLE_VPORT:
+			if (ctlq_msg.cookie.mbx.chnl_retval)
+				set_bit(IECM_VC_ENA_VPORT_ERR,
+					adapter->vc_state);
+			set_bit(IECM_VC_ENA_VPORT, adapter->vc_state);
+			wake_up(&adapter->vchnl_wq);
+			break;
+		case VIRTCHNL2_OP_DISABLE_VPORT:
+			if (ctlq_msg.cookie.mbx.chnl_retval)
+				set_bit(IECM_VC_DIS_VPORT_ERR,
+					adapter->vc_state);
+			set_bit(IECM_VC_DIS_VPORT, adapter->vc_state);
+			wake_up(&adapter->vchnl_wq);
+			break;
+		case VIRTCHNL2_OP_DESTROY_VPORT:
+			if (ctlq_msg.cookie.mbx.chnl_retval)
+				set_bit(IECM_VC_DESTROY_VPORT_ERR,
+					adapter->vc_state);
+			set_bit(IECM_VC_DESTROY_VPORT, adapter->vc_state);
+			wake_up(&adapter->vchnl_wq);
+			break;
+		case VIRTCHNL2_OP_CONFIG_TX_QUEUES:
+			if (ctlq_msg.cookie.mbx.chnl_retval)
+				set_bit(IECM_VC_CONFIG_TXQ_ERR,
+					adapter->vc_state);
+			set_bit(IECM_VC_CONFIG_TXQ, adapter->vc_state);
+			wake_up(&adapter->vchnl_wq);
+			break;
+		case VIRTCHNL2_OP_CONFIG_RX_QUEUES:
+			if (ctlq_msg.cookie.mbx.chnl_retval)
+				set_bit(IECM_VC_CONFIG_RXQ_ERR,
+					adapter->vc_state);
+			set_bit(IECM_VC_CONFIG_RXQ, adapter->vc_state);
+			wake_up(&adapter->vchnl_wq);
+			break;
+		case VIRTCHNL2_OP_ENABLE_QUEUES:
+			if (ctlq_msg.cookie.mbx.chnl_retval)
+				set_bit(IECM_VC_ENA_QUEUES_ERR,
+					adapter->vc_state);
+			set_bit(IECM_VC_ENA_QUEUES, adapter->vc_state);
+			wake_up(&adapter->vchnl_wq);
+			break;
+		case VIRTCHNL2_OP_DISABLE_QUEUES:
+			if (ctlq_msg.cookie.mbx.chnl_retval)
+				set_bit(IECM_VC_DIS_QUEUES_ERR,
+					adapter->vc_state);
+			set_bit(IECM_VC_DIS_QUEUES, adapter->vc_state);
+			wake_up(&adapter->vchnl_wq);
+			break;
+		case VIRTCHNL2_OP_ADD_QUEUES:
+			iecm_set_msg_pending(adapter, &ctlq_msg,
+					     IECM_VC_ADD_QUEUES_ERR);
+			set_bit(IECM_VC_ADD_QUEUES, adapter->vc_state);
+			wake_up(&adapter->vchnl_wq);
+			break;
+		case VIRTCHNL2_OP_DEL_QUEUES:
+			if (ctlq_msg.cookie.mbx.chnl_retval)
+				set_bit(IECM_VC_DEL_QUEUES_ERR,
+					adapter->vc_state);
+			set_bit(IECM_VC_DEL_QUEUES, adapter->vc_state);
+			wake_up(&adapter->vchnl_wq);
+			break;
+		case VIRTCHNL2_OP_MAP_QUEUE_VECTOR:
+			if (ctlq_msg.cookie.mbx.chnl_retval)
+				set_bit(IECM_VC_MAP_IRQ_ERR,
+					adapter->vc_state);
+			set_bit(IECM_VC_MAP_IRQ, adapter->vc_state);
+			wake_up(&adapter->vchnl_wq);
+			break;
+		case VIRTCHNL2_OP_UNMAP_QUEUE_VECTOR:
+			if (ctlq_msg.cookie.mbx.chnl_retval)
+				set_bit(IECM_VC_UNMAP_IRQ_ERR,
+					adapter->vc_state);
+			set_bit(IECM_VC_UNMAP_IRQ, adapter->vc_state);
+			wake_up(&adapter->vchnl_wq);
+			break;
+		case VIRTCHNL2_OP_GET_STATS:
+			iecm_set_msg_pending(adapter, &ctlq_msg,
+					     IECM_VC_GET_STATS_ERR);
+			set_bit(IECM_VC_GET_STATS, adapter->vc_state);
+			wake_up(&adapter->vchnl_wq);
+			break;
+		case VIRTCHNL2_OP_GET_RSS_HASH:
+			iecm_set_msg_pending(adapter, &ctlq_msg,
+					     IECM_VC_GET_RSS_HASH_ERR);
+			set_bit(IECM_VC_GET_RSS_HASH, adapter->vc_state);
+			wake_up(&adapter->vchnl_wq);
+			break;
+		case VIRTCHNL2_OP_SET_RSS_HASH:
+			if (ctlq_msg.cookie.mbx.chnl_retval)
+				set_bit(IECM_VC_SET_RSS_HASH_ERR,
+					adapter->vc_state);
+			set_bit(IECM_VC_SET_RSS_HASH, adapter->vc_state);
+			wake_up(&adapter->vchnl_wq);
+			break;
+		case VIRTCHNL2_OP_GET_RSS_LUT:
+			iecm_set_msg_pending(adapter, &ctlq_msg,
+					     IECM_VC_GET_RSS_LUT_ERR);
+			set_bit(IECM_VC_GET_RSS_LUT, adapter->vc_state);
+			wake_up(&adapter->vchnl_wq);
+			break;
+		case VIRTCHNL2_OP_SET_RSS_LUT:
+			if (ctlq_msg.cookie.mbx.chnl_retval)
+				set_bit(IECM_VC_SET_RSS_LUT_ERR,
+					adapter->vc_state);
+			set_bit(IECM_VC_SET_RSS_LUT, adapter->vc_state);
+			wake_up(&adapter->vchnl_wq);
+			break;
+		case VIRTCHNL2_OP_GET_RSS_KEY:
+			iecm_set_msg_pending(adapter, &ctlq_msg,
+					     IECM_VC_GET_RSS_KEY_ERR);
+			set_bit(IECM_VC_GET_RSS_KEY, adapter->vc_state);
+			wake_up(&adapter->vchnl_wq);
+			break;
+		case VIRTCHNL2_OP_SET_RSS_KEY:
+			if (ctlq_msg.cookie.mbx.chnl_retval)
+				set_bit(IECM_VC_SET_RSS_KEY_ERR,
+					adapter->vc_state);
+			set_bit(IECM_VC_SET_RSS_KEY, adapter->vc_state);
+			wake_up(&adapter->vchnl_wq);
+			break;
+		case VIRTCHNL2_OP_ALLOC_VECTORS:
+			iecm_set_msg_pending(adapter, &ctlq_msg,
+					     IECM_VC_ALLOC_VECTORS_ERR);
+			set_bit(IECM_VC_ALLOC_VECTORS, adapter->vc_state);
+			wake_up(&adapter->vchnl_wq);
+			break;
+		case VIRTCHNL2_OP_DEALLOC_VECTORS:
+			if (ctlq_msg.cookie.mbx.chnl_retval)
+				set_bit(IECM_VC_DEALLOC_VECTORS_ERR,
+					adapter->vc_state);
+			set_bit(IECM_VC_DEALLOC_VECTORS, adapter->vc_state);
+			wake_up(&adapter->vchnl_wq);
+			break;
+		case VIRTCHNL2_OP_GET_PTYPE_INFO:
+			iecm_set_msg_pending(adapter, &ctlq_msg,
+					     IECM_VC_GET_PTYPE_INFO_ERR);
+			set_bit(IECM_VC_GET_PTYPE_INFO, adapter->vc_state);
+			wake_up(&adapter->vchnl_wq);
+			break;
+		case VIRTCHNL2_OP_EVENT:
+		case VIRTCHNL_OP_EVENT:
+			if (iecm_set_msg_pending_bit(adapter)) {
+				dev_info(&adapter->pdev->dev, "Timed out setting msg pending\n");
+			} else {
+				memcpy(adapter->vc_msg,
+				       ctlq_msg.ctx.indirect.payload->va,
+				       min_t(int, payload_size,
+					     IECM_DFLT_MBX_BUF_SIZE));
+				iecm_recv_event_msg(vport);
+			}
+			break;
+		case VIRTCHNL_OP_ADD_ETH_ADDR:
+			if (test_and_clear_bit(__IECM_ADD_ETH_REQ, adapter->flags)) {
+				/* Message was sent asynchronously. We don't
+				 * normally print errors here, instead
+				 * preferring to handle errors in the function
+				 * calling wait_for_event. However, we will
+				 * have lost the context in which we sent the
+				 * message if asynchronous. We can't really do
+				 * anything about at it this point, but we
+				 * should at a minimum indicate that it looks
+				 * like something went wrong. Also don't bother
+				 * setting ERR bit or waking vchnl_wq since no
+				 * one will be waiting to read the async
+				 * message.
+				 */
+				if (ctlq_msg.cookie.mbx.chnl_retval) {
+					dev_err(&adapter->pdev->dev, "Failed to add MAC address: %d\n",
+						ctlq_msg.cookie.mbx.chnl_retval);
+				}
+				break;
+			}
+			if (ctlq_msg.cookie.mbx.chnl_retval) {
+				set_bit(IECM_VC_ADD_ETH_ADDR_ERR,
+					adapter->vc_state);
+			}
+			set_bit(IECM_VC_ADD_ETH_ADDR, adapter->vc_state);
+			wake_up(&adapter->vchnl_wq);
+			break;
+		case VIRTCHNL_OP_DEL_ETH_ADDR:
+			if (test_and_clear_bit(__IECM_DEL_ETH_REQ, adapter->flags)) {
+				/* Message was sent asynchronously. We don't
+				 * normally print errors here, instead
+				 * preferring to handle errors in the function
+				 * calling wait_for_event. However, we will
+				 * have lost the context in which we sent the
+				 * message if asynchronous. We can't really do
+				 * anything about at it this point, but we
+				 * should at a minimum indicate that it looks
+				 * like something went wrong. Also don't bother
+				 * setting ERR bit or waking vchnl_wq since no
+				 * one will be waiting to read the async
+				 * message.
+				 */
+				if (ctlq_msg.cookie.mbx.chnl_retval) {
+					dev_err(&adapter->pdev->dev, "Failed to delete MAC address: %d\n",
+						ctlq_msg.cookie.mbx.chnl_retval);
+				}
+				break;
+			}
+			if (ctlq_msg.cookie.mbx.chnl_retval) {
+				set_bit(IECM_VC_DEL_ETH_ADDR_ERR,
+					adapter->vc_state);
+			}
+			set_bit(IECM_VC_DEL_ETH_ADDR, adapter->vc_state);
+			wake_up(&adapter->vchnl_wq);
+			break;
+		case VIRTCHNL_OP_GET_OFFLOAD_VLAN_V2_CAPS:
+			if (ctlq_msg.cookie.mbx.chnl_retval) {
+				set_bit(IECM_VC_OFFLOAD_VLAN_V2_CAPS_ERR, adapter->vc_state);
+			} else {
+				memcpy(adapter->vc_msg,
+				       ctlq_msg.ctx.indirect.payload->va,
+				       min_t(int, payload_size,
+					     IECM_DFLT_MBX_BUF_SIZE));
+			}
+			set_bit(IECM_VC_OFFLOAD_VLAN_V2_CAPS, adapter->vc_state);
+			wake_up(&adapter->vchnl_wq);
+			break;
+		case VIRTCHNL_OP_ADD_VLAN_V2:
+			if (ctlq_msg.cookie.mbx.chnl_retval) {
+				dev_err(&adapter->pdev->dev, "Failed to add vlan filter: %d\n",
+					ctlq_msg.cookie.mbx.chnl_retval);
+			}
+			break;
+		case VIRTCHNL_OP_DEL_VLAN_V2:
+			if (ctlq_msg.cookie.mbx.chnl_retval) {
+				dev_err(&adapter->pdev->dev, "Failed to delete vlan filter: %d\n",
+					ctlq_msg.cookie.mbx.chnl_retval);
+			}
+			break;
+		case VIRTCHNL_OP_ENABLE_VLAN_INSERTION_V2:
+			if (ctlq_msg.cookie.mbx.chnl_retval) {
+				set_bit(IECM_VC_INSERTION_ENA_VLAN_V2_ERR,
+					adapter->vc_state);
+			}
+			set_bit(IECM_VC_INSERTION_ENA_VLAN_V2,
+				adapter->vc_state);
+			wake_up(&adapter->vchnl_wq);
+			break;
+		case VIRTCHNL_OP_DISABLE_VLAN_INSERTION_V2:
+			if (ctlq_msg.cookie.mbx.chnl_retval) {
+				set_bit(IECM_VC_INSERTION_DIS_VLAN_V2_ERR,
+					adapter->vc_state);
+			}
+			set_bit(IECM_VC_INSERTION_DIS_VLAN_V2,
+				adapter->vc_state);
+			wake_up(&adapter->vchnl_wq);
+			break;
+		case VIRTCHNL_OP_ENABLE_VLAN_STRIPPING_V2:
+			if (ctlq_msg.cookie.mbx.chnl_retval) {
+				set_bit(IECM_VC_STRIPPING_ENA_VLAN_V2_ERR,
+					adapter->vc_state);
+			}
+			set_bit(IECM_VC_STRIPPING_ENA_VLAN_V2,
+				adapter->vc_state);
+			wake_up(&adapter->vchnl_wq);
+			break;
+		case VIRTCHNL_OP_DISABLE_VLAN_STRIPPING_V2:
+			if (ctlq_msg.cookie.mbx.chnl_retval) {
+				set_bit(IECM_VC_STRIPPING_DIS_VLAN_V2_ERR,
+					adapter->vc_state);
+			}
+			set_bit(IECM_VC_STRIPPING_DIS_VLAN_V2,
+				adapter->vc_state);
+			wake_up(&adapter->vchnl_wq);
+			break;
+		case VIRTCHNL_OP_CONFIG_PROMISCUOUS_MODE:
+			/* This message can only be sent asynchronously. As
+			 * such we'll have lost the context in which it was
+			 * called and thus can only really report if it looks
+			 * like an error occurred. Don't bother setting ERR bit
+			 * or waking chnl_wq since no will be waiting to
+			 * reading message.
+			 */
+			if (ctlq_msg.cookie.mbx.chnl_retval) {
+				dev_err(&adapter->pdev->dev, "Failed to set promiscuous mode: %d\n",
+					ctlq_msg.cookie.mbx.chnl_retval);
+			}
+			break;
+		case VIRTCHNL_OP_ADD_CLOUD_FILTER:
+			if (ctlq_msg.cookie.mbx.chnl_retval) {
+				dev_err(&adapter->pdev->dev, "Failed to add cloud filter: %d\n",
+					ctlq_msg.cookie.mbx.chnl_retval);
+				set_bit(IECM_VC_ADD_CLOUD_FILTER_ERR,
+					adapter->vc_state);
+			}
+			set_bit(IECM_VC_ADD_CLOUD_FILTER, adapter->vc_state);
+			wake_up(&adapter->vchnl_wq);
+			break;
+		case VIRTCHNL_OP_DEL_CLOUD_FILTER:
+			if (ctlq_msg.cookie.mbx.chnl_retval) {
+				dev_err(&adapter->pdev->dev, "Failed to delete cloud filter: %d\n",
+					ctlq_msg.cookie.mbx.chnl_retval);
+				set_bit(IECM_VC_DEL_CLOUD_FILTER_ERR,
+					adapter->vc_state);
+			}
+			set_bit(IECM_VC_DEL_CLOUD_FILTER, adapter->vc_state);
+			wake_up(&adapter->vchnl_wq);
+			break;
+		case VIRTCHNL_OP_ADD_RSS_CFG:
+			if (ctlq_msg.cookie.mbx.chnl_retval) {
+				dev_err(&adapter->pdev->dev, "Failed to add RSS configuration: %d\n",
+					ctlq_msg.cookie.mbx.chnl_retval);
+				set_bit(IECM_VC_ADD_RSS_CFG_ERR,
+					adapter->vc_state);
+			}
+			set_bit(IECM_VC_ADD_RSS_CFG, adapter->vc_state);
+			wake_up(&adapter->vchnl_wq);
+			break;
+		case VIRTCHNL_OP_DEL_RSS_CFG:
+			if (ctlq_msg.cookie.mbx.chnl_retval) {
+				dev_err(&adapter->pdev->dev, "Failed to delete RSS configuration: %d\n",
+					ctlq_msg.cookie.mbx.chnl_retval);
+				set_bit(IECM_VC_DEL_RSS_CFG_ERR,
+					adapter->vc_state);
+			}
+			set_bit(IECM_VC_DEL_RSS_CFG, adapter->vc_state);
+			wake_up(&adapter->vchnl_wq);
+			break;
+		case VIRTCHNL_OP_ADD_FDIR_FILTER:
+			iecm_set_msg_pending(adapter, &ctlq_msg,
+					     IECM_VC_ADD_FDIR_FILTER_ERR);
+			set_bit(IECM_VC_ADD_FDIR_FILTER, adapter->vc_state);
+			wake_up(&adapter->vchnl_wq);
+			break;
+		case VIRTCHNL_OP_DEL_FDIR_FILTER:
+			iecm_set_msg_pending(adapter, &ctlq_msg,
+					     IECM_VC_DEL_FDIR_FILTER_ERR);
+			set_bit(IECM_VC_DEL_FDIR_FILTER, adapter->vc_state);
+			wake_up(&adapter->vchnl_wq);
+			break;
+		case VIRTCHNL_OP_ENABLE_CHANNELS:
+			if (ctlq_msg.cookie.mbx.chnl_retval) {
+				dev_err(&adapter->pdev->dev, "Failed to enable channels: %d\n",
+					ctlq_msg.cookie.mbx.chnl_retval);
+				set_bit(IECM_VC_ENA_CHANNELS_ERR,
+					adapter->vc_state);
+			}
+			set_bit(IECM_VC_ENA_CHANNELS, adapter->vc_state);
+			wake_up(&adapter->vchnl_wq);
+			break;
+		case VIRTCHNL_OP_DISABLE_CHANNELS:
+			if (ctlq_msg.cookie.mbx.chnl_retval) {
+				dev_err(&adapter->pdev->dev, "Failed to disable channels: %d\n",
+					ctlq_msg.cookie.mbx.chnl_retval);
+				set_bit(IECM_VC_DIS_CHANNELS_ERR,
+					adapter->vc_state);
+			}
+			set_bit(IECM_VC_DIS_CHANNELS, adapter->vc_state);
+			wake_up(&adapter->vchnl_wq);
+			break;
+		default:
+			if (adapter->dev_ops.vc_ops.recv_mbx_msg)
+				err =
+				adapter->dev_ops.vc_ops.recv_mbx_msg(adapter,
+								   msg,
+								   msg_size,
+								   &ctlq_msg,
+								   &work_done);
+			else
+				dev_warn(&adapter->pdev->dev,
+					 "Unhandled virtchnl response %d\n",
+					 ctlq_msg.cookie.mbx.chnl_opcode);
+			break;
+		} /* switch v_opcode */
+post_buffs:
+		if (ctlq_msg.data_len)
+			dma_mem = ctlq_msg.ctx.indirect.payload;
+		else
+			num_q_msg = 0;
+
+		err = iecm_ctlq_post_rx_buffs(&adapter->hw, adapter->hw.arq,
+					      &num_q_msg, &dma_mem);
+		/* If post failed clear the only buffer we supplied */
+		if (err && dma_mem)
+			dmam_free_coherent(&adapter->pdev->dev, dma_mem->size,
+					   dma_mem->va, dma_mem->pa);
+		/* Applies only if we are looking for a specific opcode */
+		if (work_done)
+			break;
+	}
+
+	return err;
+}
+EXPORT_SYMBOL(iecm_recv_mb_msg);
+
+/**
+ * iecm_send_ver_msg - send virtchnl version message
+ * @adapter: Driver specific private structure
+ *
+ * Send virtchnl version message.  Returns 0 on success, negative on failure.
+ */
+static int iecm_send_ver_msg(struct iecm_adapter *adapter)
+{
+	struct virtchnl_version_info vvi;
+
+	if (adapter->virt_ver_maj) {
+		vvi.major = adapter->virt_ver_maj;
+		vvi.minor = adapter->virt_ver_min;
+	} else {
+		vvi.major = IECM_VIRTCHNL_VERSION_MAJOR;
+		vvi.minor = IECM_VIRTCHNL_VERSION_MINOR;
+	}
+
+	return iecm_send_mb_msg(adapter, VIRTCHNL_OP_VERSION, sizeof(vvi),
+				(u8 *)&vvi);
+}
+
+/**
+ * iecm_recv_ver_msg - Receive virtchnl version message
+ * @adapter: Driver specific private structure
+ *
+ * Receive virtchnl version message. Returns 0 on success, -EAGAIN if we need
+ * to send version message again, otherwise negative on failure.
+ */
+static int iecm_recv_ver_msg(struct iecm_adapter *adapter)
+{
+	struct virtchnl_version_info vvi;
+	int err = 0;
+
+	err = iecm_recv_mb_msg(adapter, VIRTCHNL_OP_VERSION, &vvi, sizeof(vvi));
+	if (err)
+		return err;
+
+	if (vvi.major > IECM_VIRTCHNL_VERSION_MAJOR) {
+		dev_warn(&adapter->pdev->dev, "Virtchnl major version greater than supported\n");
+		return -EINVAL;
+	}
+	if (vvi.major == IECM_VIRTCHNL_VERSION_MAJOR &&
+	    vvi.minor > IECM_VIRTCHNL_VERSION_MINOR)
+		dev_warn(&adapter->pdev->dev, "Virtchnl minor version not matched\n");
+
+	/* If we have a mismatch, resend version to update receiver on what
+	 * version we will use.
+	 */
+	if (!adapter->virt_ver_maj &&
+	    vvi.major != IECM_VIRTCHNL_VERSION_MAJOR &&
+	    vvi.minor != IECM_VIRTCHNL_VERSION_MINOR)
+		err = -EAGAIN;
+
+	adapter->virt_ver_maj = vvi.major;
+	adapter->virt_ver_min = vvi.minor;
+
+	return err;
+}
+
+/**
+ * iecm_send_get_caps_msg - Send virtchnl get capabilities message
+ * @adapter: Driver specific private structure
+ *
+ * Send virtchl get capabilities message. Returns 0 on success, negative on
+ * failure.
+ */
+int iecm_send_get_caps_msg(struct iecm_adapter *adapter)
+{
+	struct virtchnl2_get_capabilities caps = {0};
+	int buf_size;
+
+	buf_size = sizeof(struct virtchnl2_get_capabilities);
+	adapter->caps = kzalloc(buf_size, GFP_KERNEL);
+	if (!adapter->caps)
+		return -ENOMEM;
+
+	caps.csum_caps =
+		cpu_to_le32(VIRTCHNL2_CAP_TX_CSUM_L3_IPV4	|
+			    VIRTCHNL2_CAP_TX_CSUM_L4_IPV4_TCP	|
+			    VIRTCHNL2_CAP_TX_CSUM_L4_IPV4_UDP	|
+			    VIRTCHNL2_CAP_TX_CSUM_L4_IPV4_SCTP	|
+			    VIRTCHNL2_CAP_TX_CSUM_L4_IPV6_TCP	|
+			    VIRTCHNL2_CAP_TX_CSUM_L4_IPV6_UDP	|
+			    VIRTCHNL2_CAP_TX_CSUM_L4_IPV6_SCTP	|
+			    VIRTCHNL2_CAP_TX_CSUM_GENERIC	|
+			    VIRTCHNL2_CAP_RX_CSUM_L3_IPV4	|
+			    VIRTCHNL2_CAP_RX_CSUM_L4_IPV4_TCP	|
+			    VIRTCHNL2_CAP_RX_CSUM_L4_IPV4_UDP	|
+			    VIRTCHNL2_CAP_RX_CSUM_L4_IPV4_SCTP	|
+			    VIRTCHNL2_CAP_RX_CSUM_L4_IPV6_TCP	|
+			    VIRTCHNL2_CAP_RX_CSUM_L4_IPV6_UDP	|
+			    VIRTCHNL2_CAP_RX_CSUM_L4_IPV6_SCTP	|
+			    VIRTCHNL2_CAP_RX_CSUM_GENERIC);
+
+	caps.seg_caps =
+		cpu_to_le32(VIRTCHNL2_CAP_SEG_IPV4_TCP		|
+			    VIRTCHNL2_CAP_SEG_IPV4_UDP		|
+			    VIRTCHNL2_CAP_SEG_IPV4_SCTP		|
+			    VIRTCHNL2_CAP_SEG_IPV6_TCP		|
+			    VIRTCHNL2_CAP_SEG_IPV6_UDP		|
+			    VIRTCHNL2_CAP_SEG_IPV6_SCTP		|
+			    VIRTCHNL2_CAP_SEG_GENERIC);
+
+	caps.rss_caps =
+		cpu_to_le64(VIRTCHNL2_CAP_RSS_IPV4_TCP		|
+			    VIRTCHNL2_CAP_RSS_IPV4_UDP		|
+			    VIRTCHNL2_CAP_RSS_IPV4_SCTP		|
+			    VIRTCHNL2_CAP_RSS_IPV4_OTHER	|
+			    VIRTCHNL2_CAP_RSS_IPV6_TCP		|
+			    VIRTCHNL2_CAP_RSS_IPV6_UDP		|
+			    VIRTCHNL2_CAP_RSS_IPV6_SCTP		|
+			    VIRTCHNL2_CAP_RSS_IPV6_OTHER	|
+			    VIRTCHNL2_CAP_RSS_IPV4_AH		|
+			    VIRTCHNL2_CAP_RSS_IPV4_ESP		|
+			    VIRTCHNL2_CAP_RSS_IPV4_AH_ESP	|
+			    VIRTCHNL2_CAP_RSS_IPV6_AH		|
+			    VIRTCHNL2_CAP_RSS_IPV6_ESP		|
+			    VIRTCHNL2_CAP_RSS_IPV6_AH_ESP);
+
+	caps.hsplit_caps =
+		cpu_to_le32(VIRTCHNL2_CAP_RX_HSPLIT_AT_L2	|
+			    VIRTCHNL2_CAP_RX_HSPLIT_AT_L3	|
+			    VIRTCHNL2_CAP_RX_HSPLIT_AT_L4V4	|
+			    VIRTCHNL2_CAP_RX_HSPLIT_AT_L4V6);
+
+	caps.rsc_caps =
+		cpu_to_le32(VIRTCHNL2_CAP_RSC_IPV4_TCP		|
+			    VIRTCHNL2_CAP_RSC_IPV4_SCTP		|
+			    VIRTCHNL2_CAP_RSC_IPV6_TCP		|
+			    VIRTCHNL2_CAP_RSC_IPV6_SCTP);
+
+	caps.other_caps =
+		cpu_to_le64(VIRTCHNL2_CAP_RDMA			|
+			    VIRTCHNL2_CAP_SRIOV			|
+			    VIRTCHNL2_CAP_MACFILTER		|
+			    VIRTCHNL2_CAP_FLOW_DIRECTOR		|
+			    VIRTCHNL2_CAP_SPLITQ_QSCHED		|
+			    VIRTCHNL2_CAP_CRC			|
+			    VIRTCHNL2_CAP_ADQ			|
+			    VIRTCHNL2_CAP_WB_ON_ITR		|
+			    VIRTCHNL2_CAP_PROMISC		|
+			    VIRTCHNL2_CAP_INLINE_IPSEC		|
+			    VIRTCHNL2_CAP_VLAN			|
+			    VIRTCHNL2_CAP_RX_FLEX_DESC);
+
+	return iecm_send_mb_msg(adapter, VIRTCHNL2_OP_GET_CAPS, sizeof(caps),
+				(u8 *)&caps);
+}
+EXPORT_SYMBOL(iecm_send_get_caps_msg);
+
+/**
+ * iecm_recv_get_caps_msg - Receive virtchnl get capabilities message
+ * @adapter: Driver specific private structure
+ *
+ * Receive virtchnl get capabilities message.  Returns 0 on succes, negative on
+ * failure.
+ */
+static int iecm_recv_get_caps_msg(struct iecm_adapter *adapter)
+{
+	return iecm_recv_mb_msg(adapter, VIRTCHNL2_OP_GET_CAPS, adapter->caps,
+				sizeof(struct virtchnl2_get_capabilities));
+}
+
+/**
+ * iecm_send_create_vport_msg - Send virtchnl create vport message
+ * @adapter: Driver specific private structure
+ *
+ * send virtchnl creae vport message
+ *
+ * Returns 0 on success, negative on failure
+ */
+static int iecm_send_create_vport_msg(struct iecm_adapter *adapter)
+{
+	/* stub */
+	return 0;
+}
+
+/**
+ * iecm_recv_create_vport_msg - Receive virtchnl create vport message
+ * @adapter: Driver specific private structure
+ * @vport_id: Virtual port identifier
+ *
+ * Receive virtchnl create vport message.  Returns 0 on success, negative on
+ * failure.
+ */
+static int iecm_recv_create_vport_msg(struct iecm_adapter *adapter,
+				      int *vport_id)
+{
+	/* stub */
+	return 0;
+}
+
+/**
+ * __iecm_wait_for_event - wrapper function for wait on virtchannel response
+ * @adapter: Driver private data structure
+ * @state: check on state upon timeout
+ * @err_check: check if this specific error bit is set
+ * @timeout: Max time to wait
+ *
+ * Checks if state is set upon expiry of timeout.  Returns 0 on success,
+ * negative on failure.
+ */
+static int __iecm_wait_for_event(struct iecm_adapter *adapter,
+				 enum iecm_vport_vc_state state,
+				 enum iecm_vport_vc_state err_check,
+				 int timeout)
+{
+	int event;
+
+	event = wait_event_timeout(adapter->vchnl_wq,
+				   test_and_clear_bit(state, adapter->vc_state),
+				   msecs_to_jiffies(timeout));
+	if (event) {
+		if (test_and_clear_bit(err_check, adapter->vc_state)) {
+			dev_err(&adapter->pdev->dev, "VC response error %s\n",
+				iecm_vport_vc_state_str[err_check]);
+			return -EINVAL;
+		}
+		return 0;
+	}
+
+	/* Timeout occurred */
+	dev_err(&adapter->pdev->dev, "VC timeout, state = %s\n",
+		iecm_vport_vc_state_str[state]);
+	return -ETIMEDOUT;
+}
+
+/**
+ * iecm_min_wait_for_event - wait for virtchannel response
+ * @adapter: Driver private data structure
+ * @state: check on state upon timeout
+ * @err_check: check if this specific error bit is set
+ *
+ * Returns 0 on success, negative on failure.
+ */
+int iecm_min_wait_for_event(struct iecm_adapter *adapter,
+			    enum iecm_vport_vc_state state,
+			    enum iecm_vport_vc_state err_check)
+{
+	int timeout = 2000;
+
+	return __iecm_wait_for_event(adapter, state, err_check, timeout);
+}
+EXPORT_SYMBOL(iecm_min_wait_for_event);
+
+/**
+ * iecm_wait_for_event - wait for virtchannel response
+ * @adapter: Driver private data structure
+ * @state: check on state upon timeout after 500ms
+ * @err_check: check if this specific error bit is set
+ *
+ * Returns 0 on success, negative on failure.
+ */
+int iecm_wait_for_event(struct iecm_adapter *adapter,
+			enum iecm_vport_vc_state state,
+			enum iecm_vport_vc_state err_check)
+{
+	/* Increasing the timeout in __IECM_INIT_SW flow to consider large
+	 * number of VF's mailbox message responses. When a message is received
+	 * on mailbox, this thread is wake up by the iecm_recv_mb_msg before the
+	 * timeout expires. Only in the error case i.e. if no message is
+	 * received on mailbox, we wait for the complete timeout which is
+	 * less likely to happen.
+	 */
+	int timeout = 60000;
+
+	return __iecm_wait_for_event(adapter, state, err_check, timeout);
+}
+EXPORT_SYMBOL(iecm_wait_for_event);
+
 /**
  * iecm_find_ctlq - Given a type and id, find ctlq info
  * @hw: hardware struct
@@ -170,3 +1097,375 @@ void iecm_vport_params_buf_rel(struct iecm_adapter *adapter)
 	kfree(adapter->caps);
 	kfree(adapter->config_data.req_qs_chunks);
 }
+
+/**
+ * iecm_vc_core_init - Initialize mailbox and get resources
+ * @adapter: Driver specific private structure
+ * @vport_id: Virtual port identifier
+ *
+ * Will check if HW is ready with reset complete. Initializes the mailbox and
+ * communicate with master to get all the default vport parameters. Returns 0
+ * on success, -EAGAIN function will get called again, otherwise negative on
+ * failure.
+ */
+int iecm_vc_core_init(struct iecm_adapter *adapter, int *vport_id)
+{
+	int err;
+
+	switch (adapter->state) {
+	case __IECM_STARTUP:
+		if (iecm_send_ver_msg(adapter))
+			goto init_failed;
+		adapter->state = __IECM_VER_CHECK;
+		goto restart;
+	case __IECM_VER_CHECK:
+		err = iecm_recv_ver_msg(adapter);
+		if (err == -EAGAIN) {
+			adapter->state = __IECM_STARTUP;
+			goto restart;
+		} else if (err) {
+			goto init_failed;
+		}
+		adapter->state = __IECM_GET_CAPS;
+		if (adapter->dev_ops.vc_ops.get_caps(adapter))
+			goto init_failed;
+		goto restart;
+	case __IECM_GET_CAPS:
+		if (iecm_recv_get_caps_msg(adapter))
+			goto init_failed;
+		if (iecm_send_create_vport_msg(adapter))
+			goto init_failed;
+		adapter->state = __IECM_GET_DFLT_VPORT_PARAMS;
+		goto restart;
+	case __IECM_GET_DFLT_VPORT_PARAMS:
+		if (iecm_recv_create_vport_msg(adapter, vport_id))
+			goto init_failed;
+		adapter->state = __IECM_INIT_SW;
+		break;
+	case __IECM_INIT_SW:
+		break;
+	default:
+		dev_err(&adapter->pdev->dev, "Device is in bad state: %d\n",
+			adapter->state);
+		goto init_failed;
+	}
+
+	return 0;
+restart:
+	queue_delayed_work(adapter->init_wq, &adapter->init_task,
+			   msecs_to_jiffies(30));
+	/* Not an error. Using try again to continue with state machine */
+	return -EAGAIN;
+init_failed:
+	if (++adapter->mb_wait_count > IECM_MB_MAX_ERR) {
+		dev_err(&adapter->pdev->dev, "Failed to establish mailbox communications with hardware\n");
+		return -EFAULT;
+	}
+	adapter->state = __IECM_STARTUP;
+	/* If it reaches here, it is possible that mailbox queue initialization
+	 * register writes might not have taken effect. Retry to initialize
+	 * the mailbox again
+	 */
+	iecm_deinit_dflt_mbx(adapter);
+	set_bit(__IECM_HR_DRV_LOAD, adapter->flags);
+	queue_delayed_work(adapter->vc_event_wq, &adapter->vc_event_task,
+			   msecs_to_jiffies(20));
+	return -EAGAIN;
+}
+EXPORT_SYMBOL(iecm_vc_core_init);
+
+/**
+ * iecm_vport_init - Initialize virtual port
+ * @vport: virtual port to be initialized
+ * @vport_id: Unique identification number of vport
+ *
+ * Will initialize vport with the info received through MB earlier
+ */
+static void iecm_vport_init(struct iecm_vport *vport,
+			    __always_unused int vport_id)
+{
+	struct virtchnl2_create_vport *vport_msg;
+	u16 rx_itr[] = {2, 8, 32, 96, 128};
+	u16 tx_itr[] = {2, 8, 64, 128, 256};
+
+	vport_msg = (struct virtchnl2_create_vport *)
+				vport->adapter->vport_params_recvd[0];
+	vport->txq_model = le16_to_cpu(vport_msg->txq_model);
+	vport->rxq_model = le16_to_cpu(vport_msg->rxq_model);
+	vport->vport_type = le16_to_cpu(vport_msg->vport_type);
+	vport->vport_id = le32_to_cpu(vport_msg->vport_id);
+	vport->adapter->rss_data.rss_key_size =
+				min_t(u16, NETDEV_RSS_KEY_LEN,
+				      le16_to_cpu(vport_msg->rss_key_size));
+	vport->adapter->rss_data.rss_lut_size =
+				le16_to_cpu(vport_msg->rss_lut_size);
+	ether_addr_copy(vport->default_mac_addr, vport_msg->default_mac_addr);
+	vport->max_mtu = IECM_MAX_MTU;
+
+	if (iecm_is_queue_model_split(vport->rxq_model)) {
+		vport->num_bufqs_per_qgrp = IECM_MAX_BUFQS_PER_RXQ_GRP;
+		/* Bufq[0] default buffer size is 4K
+		 * Bufq[1] default buffer size is 2K
+		 */
+		vport->bufq_size[0] = IECM_RX_BUF_4096;
+		vport->bufq_size[1] = IECM_RX_BUF_2048;
+	} else {
+		vport->num_bufqs_per_qgrp = 0;
+		vport->bufq_size[0] = IECM_RX_BUF_2048;
+	}
+
+	/*Initialize Tx and Rx profiles for Dynamic Interrupt Moderation */
+	memcpy(vport->rx_itr_profile, rx_itr, IECM_DIM_PROFILE_SLOTS);
+	memcpy(vport->tx_itr_profile, tx_itr, IECM_DIM_PROFILE_SLOTS);
+}
+
+/**
+ * iecm_get_vec_ids - Initialize vector id from Mailbox parameters
+ * @adapter: adapter structure to get the mailbox vector id
+ * @vecids: Array of vector ids
+ * @num_vecids: number of vector ids
+ * @chunks: vector ids received over mailbox
+ *
+ * Will initialize the mailbox vector id which is received from the
+ * get capabilities and data queue vector ids with ids received as
+ * mailbox parameters.
+ * Returns number of ids filled
+ */
+int iecm_get_vec_ids(struct iecm_adapter *adapter,
+		     u16 *vecids, int num_vecids,
+		     struct virtchnl2_vector_chunks *chunks)
+{
+	u16 num_chunks = le16_to_cpu(chunks->num_vchunks);
+	u16 start_vecid, num_vec;
+	int num_vecid_filled = 0;
+	int i, j;
+
+	vecids[num_vecid_filled] = adapter->mb_vector.v_idx;
+	num_vecid_filled++;
+
+	for (j = 0; j < num_chunks; j++) {
+		struct virtchnl2_vector_chunk *chunk = &chunks->vchunks[j];
+
+		num_vec = le16_to_cpu(chunk->num_vectors);
+		start_vecid = le16_to_cpu(chunk->start_vector_id);
+		for (i = 0; i < num_vec; i++) {
+			if ((num_vecid_filled + i) < num_vecids) {
+				vecids[num_vecid_filled + i] = start_vecid;
+				start_vecid++;
+			} else {
+				break;
+			}
+		}
+		num_vecid_filled = num_vecid_filled + i;
+	}
+
+	return num_vecid_filled;
+}
+
+/**
+ * iecm_vport_get_queue_ids - Initialize queue id from Mailbox parameters
+ * @qids: Array of queue ids
+ * @num_qids: number of queue ids
+ * @q_type: queue model
+ * @chunks: queue ids received over mailbox
+ *
+ * Will initialize all queue ids with ids received as mailbox parameters
+ * Returns number of ids filled
+ */
+static int
+iecm_vport_get_queue_ids(u32 *qids, int num_qids, u16 q_type,
+			 struct virtchnl2_queue_reg_chunks *chunks)
+{
+	u16 num_chunks = le16_to_cpu(chunks->num_chunks);
+	u32 num_q_id_filled = 0, i;
+	u32 start_q_id, num_q;
+
+	while (num_chunks) {
+		struct virtchnl2_queue_reg_chunk *chunk = &chunks->chunks[num_chunks - 1];
+
+		if (le32_to_cpu(chunk->type) == q_type) {
+			num_q = le32_to_cpu(chunk->num_queues);
+			start_q_id = le32_to_cpu(chunk->start_queue_id);
+			for (i = 0; i < num_q; i++) {
+				if ((num_q_id_filled + i) < num_qids) {
+					qids[num_q_id_filled + i] = start_q_id;
+					start_q_id++;
+				} else {
+					break;
+				}
+			}
+			num_q_id_filled = num_q_id_filled + i;
+		}
+		num_chunks--;
+	}
+
+	return num_q_id_filled;
+}
+
+/**
+ * __iecm_vport_queue_ids_init - Initialize queue ids from Mailbox parameters
+ * @vport: virtual port for which the queues ids are initialized
+ * @qids: queue ids
+ * @num_qids: number of queue ids
+ * @q_type: type of queue
+ *
+ * Will initialize all queue ids with ids received as mailbox
+ * parameters. Returns number of queue ids initialized.
+ */
+static int
+__iecm_vport_queue_ids_init(struct iecm_vport *vport, u32 *qids,
+			    int num_qids, u32 q_type)
+{
+	/* stub */
+	return 0;
+}
+
+/**
+ * iecm_vport_queue_ids_init - Initialize queue ids from Mailbox parameters
+ * @vport: virtual port for which the queues ids are initialized
+ *
+ * Will initialize all queue ids with ids received as mailbox parameters.
+ * Returns 0 on success, negative if all the queues are not initialized.
+ */
+static int iecm_vport_queue_ids_init(struct iecm_vport *vport)
+{
+	struct virtchnl2_create_vport *vport_params;
+	struct virtchnl2_queue_reg_chunks *chunks;
+	/* We may never deal with more than 256 same type of queues */
+#define IECM_MAX_QIDS	256
+	u32 qids[IECM_MAX_QIDS];
+	int num_ids;
+	u16 q_type;
+
+	if (vport->adapter->config_data.req_qs_chunks) {
+		struct virtchnl2_add_queues *vc_aq =
+			(struct virtchnl2_add_queues *)
+			vport->adapter->config_data.req_qs_chunks;
+		chunks = &vc_aq->chunks;
+	} else {
+		vport_params = (struct virtchnl2_create_vport *)
+				vport->adapter->vport_params_recvd[0];
+		chunks = &vport_params->chunks;
+	}
+
+	num_ids = iecm_vport_get_queue_ids(qids, IECM_MAX_QIDS,
+					   VIRTCHNL2_QUEUE_TYPE_TX,
+					   chunks);
+	if (num_ids != vport->num_txq)
+		return -EINVAL;
+	num_ids = __iecm_vport_queue_ids_init(vport, qids, num_ids,
+					      VIRTCHNL2_QUEUE_TYPE_TX);
+	if (num_ids != vport->num_txq)
+		return -EINVAL;
+	num_ids = iecm_vport_get_queue_ids(qids, IECM_MAX_QIDS,
+					   VIRTCHNL2_QUEUE_TYPE_RX,
+					   chunks);
+	if (num_ids != vport->num_rxq)
+		return -EINVAL;
+	num_ids = __iecm_vport_queue_ids_init(vport, qids, num_ids,
+					      VIRTCHNL2_QUEUE_TYPE_RX);
+	if (num_ids != vport->num_rxq)
+		return -EINVAL;
+
+	if (iecm_is_queue_model_split(vport->txq_model)) {
+		q_type = VIRTCHNL2_QUEUE_TYPE_TX_COMPLETION;
+		num_ids = iecm_vport_get_queue_ids(qids, IECM_MAX_QIDS, q_type,
+						   chunks);
+		if (num_ids != vport->num_complq)
+			return -EINVAL;
+		num_ids = __iecm_vport_queue_ids_init(vport, qids,
+						      num_ids,
+						      q_type);
+		if (num_ids != vport->num_complq)
+			return -EINVAL;
+	}
+
+	if (iecm_is_queue_model_split(vport->rxq_model)) {
+		q_type = VIRTCHNL2_QUEUE_TYPE_RX_BUFFER;
+		num_ids = iecm_vport_get_queue_ids(qids, IECM_MAX_QIDS, q_type,
+						   chunks);
+		if (num_ids != vport->num_bufq)
+			return -EINVAL;
+		num_ids = __iecm_vport_queue_ids_init(vport, qids, num_ids,
+						      q_type);
+		if (num_ids != vport->num_bufq)
+			return -EINVAL;
+	}
+
+	return 0;
+}
+
+/**
+ * iecm_is_capability_ena - Default implementation of capability checking
+ * @adapter: Private data struct
+ * @all: all or one flag
+ * @field: caps field to check for flags
+ * @flag: flag to check
+ *
+ * Return true if all capabilities are supported, false otherwise
+ */
+static bool iecm_is_capability_ena(struct iecm_adapter *adapter, bool all,
+				   enum iecm_cap_field field, u64 flag)
+{
+	u8 *caps = (u8 *)adapter->caps;
+	u32 *cap_field;
+
+	if (field == IECM_BASE_CAPS)
+		return false;
+	if (field >= IECM_CAP_FIELD_LAST) {
+		dev_err(&adapter->pdev->dev, "Bad capability field: %d\n",
+			field);
+		return false;
+	}
+	cap_field = (u32 *)(caps + field);
+
+	if (all)
+		return (*cap_field & flag) == flag;
+	else
+		return !!(*cap_field & flag);
+}
+
+/**
+ * iecm_vc_ops_init - Initialize virtchnl common api
+ * @adapter: Driver specific private structure
+ *
+ * Initialize the function pointers with the extended feature set functions
+ * as APF will deal only with new set of opcodes.
+ */
+void iecm_vc_ops_init(struct iecm_adapter *adapter)
+{
+	struct iecm_virtchnl_ops *vc_ops = &adapter->dev_ops.vc_ops;
+
+	vc_ops->core_init = iecm_vc_core_init;
+	vc_ops->vport_init = iecm_vport_init;
+	vc_ops->vport_queue_ids_init = iecm_vport_queue_ids_init;
+	vc_ops->get_caps = iecm_send_get_caps_msg;
+	vc_ops->is_cap_ena = iecm_is_capability_ena;
+	vc_ops->get_reserved_vecs = NULL;
+	vc_ops->config_queues = NULL;
+	vc_ops->enable_queues = NULL;
+	vc_ops->disable_queues = NULL;
+	vc_ops->add_queues = NULL;
+	vc_ops->delete_queues = NULL;
+	vc_ops->irq_map_unmap = NULL;
+	vc_ops->enable_vport = NULL;
+	vc_ops->disable_vport = NULL;
+	vc_ops->destroy_vport = NULL;
+	vc_ops->get_ptype = NULL;
+	vc_ops->get_set_rss_key = NULL;
+	vc_ops->get_set_rss_lut = NULL;
+	vc_ops->get_set_rss_hash = NULL;
+	vc_ops->adjust_qs = NULL;
+	vc_ops->add_del_vlans = NULL;
+	vc_ops->strip_vlan_msg = NULL;
+	vc_ops->insert_vlan_msg = NULL;
+	vc_ops->init_max_queues = NULL;
+	vc_ops->get_max_tx_bufs = NULL;
+	vc_ops->vportq_reg_init = NULL;
+	vc_ops->alloc_vectors = NULL;
+	vc_ops->dealloc_vectors = NULL;
+	vc_ops->get_supported_desc_ids = NULL;
+	vc_ops->get_stats_msg = NULL;
+	vc_ops->recv_mbx_msg = NULL;
+}
+EXPORT_SYMBOL(iecm_vc_ops_init);
diff --git a/drivers/net/ethernet/intel/include/iecm.h b/drivers/net/ethernet/intel/include/iecm.h
index ca9029224e06..994664dfe419 100644
--- a/drivers/net/ethernet/intel/include/iecm.h
+++ b/drivers/net/ethernet/intel/include/iecm.h
@@ -7,10 +7,13 @@
 #include <linux/aer.h>
 #include <linux/pci.h>
 #include <linux/netdevice.h>
+#include <linux/etherdevice.h>
 #include <linux/ethtool.h>
+#include <net/tcp.h>
 #include <linux/version.h>
 #include <linux/dim.h>
 
+#include "virtchnl_2.h"
 #include "iecm_txrx.h"
 #include "iecm_controlq.h"
 
@@ -35,10 +38,34 @@
 /* available message levels */
 #define IECM_AVAIL_NETIF_M (NETIF_MSG_DRV | NETIF_MSG_PROBE | NETIF_MSG_LINK)
 
+#define IECM_VIRTCHNL_VERSION_MAJOR VIRTCHNL_VERSION_MAJOR_2
+#define IECM_VIRTCHNL_VERSION_MINOR VIRTCHNL_VERSION_MINOR_0
+
 /* Forward declaration */
 struct iecm_adapter;
 struct iecm_vport;
 
+struct iecm_mac_filter {
+	struct list_head list;
+	u8 macaddr[ETH_ALEN];
+	bool remove;		/* filter needs to be removed */
+	bool add;		/* filter needs to be added */
+};
+
+#define IECM_VLAN(vid, tpid) ((struct iecm_vlan){ vid, tpid })
+
+struct iecm_vlan {
+	u16 vid;
+	u16 tpid;
+};
+
+struct iecm_vlan_filter {
+	struct list_head list;
+	struct iecm_vlan vlan;
+	bool remove;		/* filter needs to be removed */
+	bool add;		/* filter needs to be added */
+};
+
 enum iecm_state {
 	__IECM_STARTUP,
 	__IECM_VER_CHECK,
@@ -90,6 +117,24 @@ enum iecm_flags {
 	__IECM_FLAGS_NBITS,
 };
 
+/* enum used to distinquish which capability field to check */
+enum iecm_cap_field {
+	IECM_BASE_CAPS		= -1,
+	IECM_CSUM_CAPS		= offsetof(struct virtchnl2_get_capabilities,
+					   csum_caps),
+	IECM_SEG_CAPS		= offsetof(struct virtchnl2_get_capabilities,
+					   seg_caps),
+	IECM_RSS_CAPS		= offsetof(struct virtchnl2_get_capabilities,
+					   rss_caps),
+	IECM_HSPLIT_CAPS	= offsetof(struct virtchnl2_get_capabilities,
+					   hsplit_caps),
+	IECM_RSC_CAPS		= offsetof(struct virtchnl2_get_capabilities,
+					   rsc_caps),
+	IECM_OTHER_CAPS		= offsetof(struct virtchnl2_get_capabilities,
+					   other_caps),
+	IECM_CAP_FIELD_LAST,
+};
+
 struct iecm_reset_reg {
 	u32 rstat;
 	u32 rstat_m;
@@ -105,14 +150,229 @@ struct iecm_reg_ops {
 			      enum iecm_flags trig_cause);
 };
 
+struct iecm_virtchnl_ops {
+	int (*core_init)(struct iecm_adapter *adapter, int *vport_id);
+	void (*vport_init)(struct iecm_vport *vport, int vport_id);
+	int (*vport_queue_ids_init)(struct iecm_vport *vport);
+	int (*get_caps)(struct iecm_adapter *adapter);
+	int (*config_queues)(struct iecm_vport *vport);
+	int (*enable_queues)(struct iecm_vport *vport);
+	int (*disable_queues)(struct iecm_vport *vport);
+	int (*add_queues)(struct iecm_vport *vport, u16 num_tx_q,
+			  u16 num_complq, u16 num_rx_q,
+			  u16 num_rx_bufq);
+	int (*delete_queues)(struct iecm_vport *vport);
+	int (*irq_map_unmap)(struct iecm_vport *vport, bool map);
+	int (*enable_vport)(struct iecm_vport *vport);
+	int (*disable_vport)(struct iecm_vport *vport);
+	int (*destroy_vport)(struct iecm_vport *vport);
+	int (*get_ptype)(struct iecm_vport *vport);
+	int (*get_set_rss_key)(struct iecm_vport *vport, bool get);
+	int (*get_set_rss_lut)(struct iecm_vport *vport, bool get);
+	int (*get_set_rss_hash)(struct iecm_vport *vport, bool get);
+	void (*adjust_qs)(struct iecm_vport *vport);
+	int (*recv_mbx_msg)(struct iecm_adapter *adapter,
+			    void *msg, int msg_size,
+			    struct iecm_ctlq_msg *ctlq_msg, bool *work_done);
+	bool (*is_cap_ena)(struct iecm_adapter *adapter, bool all,
+			   enum iecm_cap_field field, u64 flag);
+	u16 (*get_reserved_vecs)(struct iecm_adapter *adapter);
+	void (*add_del_vlans)(struct iecm_vport *vport, bool add);
+	int (*strip_vlan_msg)(struct iecm_vport *vport, bool ena);
+	int (*insert_vlan_msg)(struct iecm_vport *vport, bool ena);
+	void (*init_max_queues)(struct iecm_adapter *adapter);
+	unsigned int (*get_max_tx_bufs)(struct iecm_adapter *adapter);
+	int (*vportq_reg_init)(struct iecm_vport *vport);
+	int (*alloc_vectors)(struct iecm_adapter *adapter, u16 num_vectors);
+	int (*dealloc_vectors)(struct iecm_adapter *adapter);
+	int (*get_supported_desc_ids)(struct iecm_vport *vport);
+	int (*get_stats_msg)(struct iecm_vport *vport);
+};
+
 struct iecm_dev_ops {
 	void (*reg_ops_init)(struct iecm_adapter *adapter);
+	void (*vc_ops_init)(struct iecm_adapter *adapter);
 	void (*crc_enable)(u64 *td_cmd);
 	struct iecm_reg_ops reg_ops;
+	struct iecm_virtchnl_ops vc_ops;
+};
+
+/* These macros allow us to generate an enum and a matching char * array of
+ * stringified enums that are always in sync. Checkpatch issues a bogus warning
+ * about this being a complex macro; but it's wrong, these are never used as a
+ * statement and instead only used to define the enum and array.
+ */
+#define IECM_FOREACH_VPORT_VC_STATE(STATE)	\
+	STATE(IECM_VC_ENA_VPORT)		\
+	STATE(IECM_VC_ENA_VPORT_ERR)		\
+	STATE(IECM_VC_DIS_VPORT)		\
+	STATE(IECM_VC_DIS_VPORT_ERR)		\
+	STATE(IECM_VC_DESTROY_VPORT)		\
+	STATE(IECM_VC_DESTROY_VPORT_ERR)	\
+	STATE(IECM_VC_CONFIG_TXQ)		\
+	STATE(IECM_VC_CONFIG_TXQ_ERR)		\
+	STATE(IECM_VC_CONFIG_RXQ)		\
+	STATE(IECM_VC_CONFIG_RXQ_ERR)		\
+	STATE(IECM_VC_CONFIG_Q)			\
+	STATE(IECM_VC_CONFIG_Q_ERR)		\
+	STATE(IECM_VC_ENA_QUEUES)		\
+	STATE(IECM_VC_ENA_QUEUES_ERR)		\
+	STATE(IECM_VC_DIS_QUEUES)		\
+	STATE(IECM_VC_DIS_QUEUES_ERR)		\
+	STATE(IECM_VC_ENA_CHANNELS)		\
+	STATE(IECM_VC_ENA_CHANNELS_ERR)		\
+	STATE(IECM_VC_DIS_CHANNELS)		\
+	STATE(IECM_VC_DIS_CHANNELS_ERR)		\
+	STATE(IECM_VC_MAP_IRQ)			\
+	STATE(IECM_VC_MAP_IRQ_ERR)		\
+	STATE(IECM_VC_UNMAP_IRQ)		\
+	STATE(IECM_VC_UNMAP_IRQ_ERR)		\
+	STATE(IECM_VC_ADD_QUEUES)		\
+	STATE(IECM_VC_ADD_QUEUES_ERR)		\
+	STATE(IECM_VC_DEL_QUEUES)		\
+	STATE(IECM_VC_REQUEST_QUEUES)		\
+	STATE(IECM_VC_REQUEST_QUEUES_ERR)	\
+	STATE(IECM_VC_DEL_QUEUES_ERR)		\
+	STATE(IECM_VC_ALLOC_VECTORS)		\
+	STATE(IECM_VC_ALLOC_VECTORS_ERR)	\
+	STATE(IECM_VC_DEALLOC_VECTORS)		\
+	STATE(IECM_VC_DEALLOC_VECTORS_ERR)	\
+	STATE(IECM_VC_SET_SRIOV_VFS)		\
+	STATE(IECM_VC_SET_SRIOV_VFS_ERR)	\
+	STATE(IECM_VC_GET_RSS_HASH)		\
+	STATE(IECM_VC_GET_RSS_HASH_ERR)		\
+	STATE(IECM_VC_SET_RSS_HASH)		\
+	STATE(IECM_VC_SET_RSS_HASH_ERR)		\
+	STATE(IECM_VC_GET_RSS_LUT)		\
+	STATE(IECM_VC_GET_RSS_LUT_ERR)		\
+	STATE(IECM_VC_SET_RSS_LUT)		\
+	STATE(IECM_VC_SET_RSS_LUT_ERR)		\
+	STATE(IECM_VC_GET_RSS_KEY)		\
+	STATE(IECM_VC_GET_RSS_KEY_ERR)		\
+	STATE(IECM_VC_SET_RSS_KEY)		\
+	STATE(IECM_VC_SET_RSS_KEY_ERR)		\
+	STATE(IECM_VC_GET_STATS)		\
+	STATE(IECM_VC_GET_STATS_ERR)		\
+	STATE(IECM_VC_ENA_STRIP_VLAN_TAG)	\
+	STATE(IECM_VC_ENA_STRIP_VLAN_TAG_ERR)	\
+	STATE(IECM_VC_DIS_STRIP_VLAN_TAG)	\
+	STATE(IECM_VC_DIS_STRIP_VLAN_TAG_ERR)	\
+	STATE(IECM_VC_IWARP_IRQ_MAP)		\
+	STATE(IECM_VC_IWARP_IRQ_MAP_ERR)	\
+	STATE(IECM_VC_ADD_ETH_ADDR)		\
+	STATE(IECM_VC_ADD_ETH_ADDR_ERR)		\
+	STATE(IECM_VC_DEL_ETH_ADDR)		\
+	STATE(IECM_VC_DEL_ETH_ADDR_ERR)		\
+	STATE(IECM_VC_PROMISC)			\
+	STATE(IECM_VC_ADD_CLOUD_FILTER)		\
+	STATE(IECM_VC_ADD_CLOUD_FILTER_ERR)	\
+	STATE(IECM_VC_DEL_CLOUD_FILTER)		\
+	STATE(IECM_VC_DEL_CLOUD_FILTER_ERR)	\
+	STATE(IECM_VC_ADD_RSS_CFG)		\
+	STATE(IECM_VC_ADD_RSS_CFG_ERR)		\
+	STATE(IECM_VC_DEL_RSS_CFG)		\
+	STATE(IECM_VC_DEL_RSS_CFG_ERR)		\
+	STATE(IECM_VC_ADD_FDIR_FILTER)		\
+	STATE(IECM_VC_ADD_FDIR_FILTER_ERR)	\
+	STATE(IECM_VC_DEL_FDIR_FILTER)		\
+	STATE(IECM_VC_DEL_FDIR_FILTER_ERR)	\
+	STATE(IECM_VC_OFFLOAD_VLAN_V2_CAPS)	\
+	STATE(IECM_VC_OFFLOAD_VLAN_V2_CAPS_ERR)	\
+	STATE(IECM_VC_INSERTION_ENA_VLAN_V2)	\
+	STATE(IECM_VC_INSERTION_ENA_VLAN_V2_ERR)\
+	STATE(IECM_VC_INSERTION_DIS_VLAN_V2)	\
+	STATE(IECM_VC_INSERTION_DIS_VLAN_V2_ERR)\
+	STATE(IECM_VC_STRIPPING_ENA_VLAN_V2)	\
+	STATE(IECM_VC_STRIPPING_ENA_VLAN_V2_ERR)\
+	STATE(IECM_VC_STRIPPING_DIS_VLAN_V2)	\
+	STATE(IECM_VC_STRIPPING_DIS_VLAN_V2_ERR)\
+	STATE(IECM_VC_GET_SUPPORTED_RXDIDS)	\
+	STATE(IECM_VC_GET_SUPPORTED_RXDIDS_ERR)	\
+	STATE(IECM_VC_GET_PTYPE_INFO)		\
+	STATE(IECM_VC_GET_PTYPE_INFO_ERR)	\
+	STATE(IECM_VC_NBITS)
+
+#define IECM_GEN_ENUM(ENUM) ENUM,
+#define IECM_GEN_STRING(STRING) #STRING,
+
+enum iecm_vport_vc_state {
+	IECM_FOREACH_VPORT_VC_STATE(IECM_GEN_ENUM)
+};
+
+extern const char * const iecm_vport_vc_state_str[];
+
+enum iecm_vport_flags {
+	__IECM_VPORT_INIT_PROMISC,
+	__IECM_VPORT_FLAGS_NBITS,
+};
+
+struct iecm_port_stats {
+	struct u64_stats_sync stats_sync;
+	u64 rx_hw_csum_err;
+	u64 rx_hsplit;
+	u64 rx_hsplit_hbo;
+	u64 tx_linearize;
+	u64 rx_bad_descs;
+	struct virtchnl2_vport_stats vport_stats;
+	struct virtchnl_eth_stats eth_stats;
 };
 
-/* stub */
 struct iecm_vport {
+	/* TX */
+	int num_txq;
+	int num_complq;
+	/* It makes more sense for descriptor count to be part of only idpf
+	 * queue structure. But when user changes the count via ethtool, driver
+	 * has to store that value somewhere other than queue structure as the
+	 * queues will be freed and allocated again.
+	 */
+	int txq_desc_count;
+	int complq_desc_count;
+	int compln_clean_budget;
+	int num_txq_grp;
+	struct iecm_txq_group *txq_grps;
+	u32 txq_model;
+	/* Used only in hotpath to get to the right queue very fast */
+	struct iecm_queue **txqs;
+	DECLARE_BITMAP(flags, __IECM_VPORT_FLAGS_NBITS);
+
+	/* RX */
+	int num_rxq;
+	int num_bufq;
+	int rxq_desc_count;
+	u8 num_bufqs_per_qgrp;
+	int bufq_desc_count[IECM_MAX_BUFQS_PER_RXQ_GRP];
+	u32 bufq_size[IECM_MAX_BUFQS_PER_RXQ_GRP];
+	int num_rxq_grp;
+	struct iecm_rxq_group *rxq_grps;
+	u32 rxq_model;
+
+	struct iecm_adapter *adapter;
+	struct net_device *netdev;
+	u16 vport_type;
+	u16 vport_id;
+	u16 idx;		 /* software index in adapter vports struct */
+	bool base_rxd;
+
+	/* handler for hard interrupt */
+	irqreturn_t (*irq_q_handler)(int irq, void *data);
+	struct iecm_q_vector *q_vectors;	/* q vector array */
+	u16 num_q_vectors;
+	u16 q_vector_base;
+	u16 max_mtu;
+	u8 default_mac_addr[ETH_ALEN];
+	u16 qset_handle;
+	/* ITR profiles for the DIM algorithm */
+#define IECM_DIM_PROFILE_SLOTS	5
+	u16 rx_itr_profile[IECM_DIM_PROFILE_SLOTS];
+	u16 tx_itr_profile[IECM_DIM_PROFILE_SLOTS];
+	struct rtnl_link_stats64 netstats;
+	struct iecm_port_stats port_stats;
+
+	/* lock to protect against multiple stop threads, which can happen when
+	 * the driver is in a namespace in a system that is being shutdown
+	 */
+	struct mutex stop_mutex;
 };
 
 enum iecm_user_flags {
@@ -164,6 +424,7 @@ struct iecm_adapter {
 	u16 num_msix_entries;
 	struct msix_entry *msix_entries;
 	struct virtchnl2_alloc_vectors *req_vec_chunks;
+	struct iecm_q_vector mb_vector;
 
 	/* vport structs */
 	struct iecm_vport **vports;	/* vports created by the driver */
@@ -190,6 +451,8 @@ struct iecm_adapter {
 
 	wait_queue_head_t vchnl_wq;
 	wait_queue_head_t sw_marker_wq;
+	DECLARE_BITMAP(vc_state, IECM_VC_NBITS);
+	char vc_msg[IECM_DFLT_MBX_BUF_SIZE];
 	struct iecm_rss_data rss_data;
 	struct iecm_dev_ops dev_ops;
 	s32 link_speed;
@@ -215,6 +478,38 @@ struct iecm_adapter {
 	spinlock_t fdir_fltr_list_lock;
 };
 
+/**
+ * iecm_is_queue_model_split - check if queue model is split
+ * @q_model: queue model single or split
+ *
+ * Returns true if queue model is split else false
+ */
+static inline int iecm_is_queue_model_split(u16 q_model)
+{
+	return (q_model == VIRTCHNL2_QUEUE_MODEL_SPLIT);
+}
+
+#define iecm_is_cap_ena(adapter, field, flag) \
+	__iecm_is_cap_ena(adapter, false, field, flag)
+#define iecm_is_cap_ena_all(adapter, field, flag) \
+	__iecm_is_cap_ena(adapter, true, field, flag)
+/**
+ * __iecm_is_cap_ena - Determine if HW capability is supported
+ * @adapter: private data struct
+ * @all: all or one flag
+ * @field: cap field to check
+ * @flag: Feature flag to check
+ *
+ * iecm_is_cap_ena_all is used to check if all the capability bits are set
+ * ('AND' operation) where as iecm_is_cap_ena is used to check if
+ * any one of the capability bits is set ('OR' operation)
+ */
+static inline bool __iecm_is_cap_ena(struct iecm_adapter *adapter, bool all,
+				     enum iecm_cap_field field, u64 flag)
+{
+	return adapter->dev_ops.vc_ops.is_cap_ena(adapter, all, field, flag);
+}
+
 /**
  * iecm_is_reset_detected - check if we were reset at some point
  * @adapter: driver specific private structure
@@ -233,6 +528,25 @@ int iecm_probe(struct pci_dev *pdev,
 void iecm_remove(struct pci_dev *pdev);
 int iecm_init_dflt_mbx(struct iecm_adapter *adapter);
 void iecm_deinit_dflt_mbx(struct iecm_adapter *adapter);
+void iecm_vc_ops_init(struct iecm_adapter *adapter);
+int iecm_vc_core_init(struct iecm_adapter *adapter, int *vport_id);
+int iecm_wait_for_event(struct iecm_adapter *adapter,
+			enum iecm_vport_vc_state state,
+			enum iecm_vport_vc_state err_check);
+int iecm_min_wait_for_event(struct iecm_adapter *adapter,
+			    enum iecm_vport_vc_state state,
+			    enum iecm_vport_vc_state err_check);
+int iecm_send_get_caps_msg(struct iecm_adapter *adapter);
 int iecm_vport_params_buf_alloc(struct iecm_adapter *adapter);
 void iecm_vport_params_buf_rel(struct iecm_adapter *adapter);
+int iecm_get_vec_ids(struct iecm_adapter *adapter,
+		     u16 *vecids, int num_vecids,
+		     struct virtchnl2_vector_chunks *chunks);
+int iecm_recv_mb_msg(struct iecm_adapter *adapter, enum virtchnl_ops op,
+		     void *msg, int msg_size);
+int iecm_send_mb_msg(struct iecm_adapter *adapter, enum virtchnl_ops op,
+		     u16 msg_size, u8 *msg);
+int iecm_set_msg_pending(struct iecm_adapter *adapter,
+			 struct iecm_ctlq_msg *ctlq_msg,
+			 enum iecm_vport_vc_state err_enum);
 #endif /* !_IECM_H_ */
diff --git a/drivers/net/ethernet/intel/include/iecm_txrx.h b/drivers/net/ethernet/intel/include/iecm_txrx.h
index 602d3b3b19dd..e1348011c991 100644
--- a/drivers/net/ethernet/intel/include/iecm_txrx.h
+++ b/drivers/net/ethernet/intel/include/iecm_txrx.h
@@ -30,4 +30,98 @@
 #define IECM_DFLT_SPLITQ_RX_Q_GROUPS		4
 #define IECM_DFLT_SPLITQ_TXQ_PER_GROUP		1
 #define IECM_DFLT_SPLITQ_RXQ_PER_GROUP		1
+
+/* Default vector sharing */
+#define IECM_NONQ_VEC		1
+#define IECM_MAX_Q_VEC		4 /* For Tx Completion queue and Rx queue */
+#define IECM_MIN_Q_VEC		1
+#define IECM_MAX_RDMA_VEC	2 /* To share with RDMA */
+#define IECM_MIN_RDMA_VEC	1 /* Minimum vectors to be shared with RDMA */
+#define IECM_MIN_VEC		3 /* One for mailbox, one for data queues, one
+				   * for RDMA
+				   */
+
+#define IECM_DFLT_TX_Q_DESC_COUNT		512
+#define IECM_DFLT_TX_COMPLQ_DESC_COUNT		512
+#define IECM_DFLT_RX_Q_DESC_COUNT		512
+/* IMPORTANT: We absolutely _cannot_ have more buffers in the system than a
+ * given RX completion queue has descriptors. This includes _ALL_ buffer
+ * queues. E.g.: If you have two buffer queues of 512 descriptors and buffers,
+ * you have a total of 1024 buffers so your RX queue _must_ have at least that
+ * many descriptors. This macro divides a given number of RX descriptors by
+ * number of buffer queues to calculate how many descriptors each buffer queue
+ * can have without overrunning the RX queue.
+ *
+ * If you give hardware more buffers than completion descriptors what will
+ * happen is that if hardware gets a chance to post more than ring wrap of
+ * descriptors before SW gets an interrupt and overwrites SW head, the gen bit
+ * in the descriptor will be wrong. Any overwritten descriptors' buffers will
+ * be gone forever and SW has no reasonable way to tell that this has happened.
+ * From SW perspective, when we finally get an interrupt, it looks like we're
+ * still waiting for descriptor to be done, stalling forever.
+ */
+#define IECM_RX_BUFQ_DESC_COUNT(RXD, NUM_BUFQ)	((RXD) / (NUM_BUFQ))
+
+#define IECM_RX_BUFQ_WORKING_SET(R)		((R)->desc_count - 1)
+#define IECM_RX_BUFQ_NON_WORKING_SET(R)		((R)->desc_count - \
+						 IECM_RX_BUFQ_WORKING_SET(R))
+
+#define IECM_RX_HDR_SIZE			256
+#define IECM_RX_BUF_2048			2048
+#define IECM_RX_BUF_4096			4096
+#define IECM_RX_BUF_STRIDE			64
+#define IECM_LOW_WATERMARK			64
+#define IECM_HDR_BUF_SIZE			256
+#define IECM_PACKET_HDR_PAD	\
+	(ETH_HLEN + ETH_FCS_LEN + (VLAN_HLEN * 2))
+#define IECM_MAX_RXBUFFER			9728
+#define IECM_MAX_MTU		\
+	(IECM_MAX_RXBUFFER - IECM_PACKET_HDR_PAD)
+#define IECM_INT_NAME_STR_LEN	(IFNAMSIZ + 16)
+
+#define IECM_TX_COMPLQ_CLEAN_BUDGET	256
+
+struct iecm_intr_reg {
+	u32 dyn_ctl;
+	u32 dyn_ctl_intena_m;
+	u32 dyn_ctl_clrpba_m;
+	u32 dyn_ctl_itridx_s;
+	u32 dyn_ctl_itridx_m;
+	u32 dyn_ctl_intrvl_s;
+	u32 rx_itr;
+	u32 tx_itr;
+	u32 icr_ena;
+	u32 icr_ena_ctlq_m;
+};
+
+struct iecm_q_vector {
+	struct iecm_vport *vport;
+	cpumask_t affinity_mask;
+	struct napi_struct napi;
+	u16 v_idx;		/* index in the vport->q_vector array */
+	struct iecm_intr_reg intr_reg;
+
+	int num_txq;
+	struct iecm_queue **tx;
+	struct dim tx_dim;	/* data for net_dim algorithm */
+	u16 tx_itr_value;
+	bool tx_intr_mode;
+	u32 tx_itr_idx;
+
+	int num_rxq;
+	struct iecm_queue **rx;
+	struct dim rx_dim;	/* data for net_dim algorithm */
+	u16 rx_itr_value;
+	bool rx_intr_mode;
+	u32 rx_itr_idx;
+
+	int num_bufq;
+	struct iecm_queue **bufq;
+
+	u16 total_events;       /* net_dim(): number of interrupts processed */
+	char name[IECM_INT_NAME_STR_LEN];
+};
+
+irqreturn_t
+iecm_vport_intr_clean_queues(int __always_unused irq, void *data);
 #endif /* !_IECM_TXRX_H_ */
