diff --git a/drivers/net/cxgbe/cxgbe.h b/drivers/net/cxgbe/cxgbe.h
index 90d1db0..bf08baf 100644
--- a/drivers/net/cxgbe/cxgbe.h
+++ b/drivers/net/cxgbe/cxgbe.h
@@ -44,6 +44,10 @@
 #define CXGBE_DEFAULT_RX_DESC_SIZE    1024 /* Default RX ring size */
 
 int cxgbe_probe(struct adapter *adapter);
+int cxgbe_up(struct adapter *adap);
+int cxgbe_down(struct port_info *pi);
+void cxgbe_close(struct adapter *adapter);
+int link_start(struct port_info *pi);
 void init_rspq(struct adapter *adap, struct sge_rspq *q, unsigned int us,
 	       unsigned int cnt, unsigned int size, unsigned int iqe_size);
 int setup_sge_fwevtq(struct adapter *adapter);
diff --git a/drivers/net/cxgbe/cxgbe_ethdev.c b/drivers/net/cxgbe/cxgbe_ethdev.c
index b6e17e4..cb100fc 100644
--- a/drivers/net/cxgbe/cxgbe_ethdev.c
+++ b/drivers/net/cxgbe/cxgbe_ethdev.c
@@ -171,6 +171,117 @@ static int cxgbe_dev_rx_queue_start(struct rte_eth_dev *eth_dev,
 static void cxgbe_dev_tx_queue_release(void *q);
 static void cxgbe_dev_rx_queue_release(void *q);
 
+/*
+ * Stop device.
+ */
+static void cxgbe_dev_close(struct rte_eth_dev *eth_dev)
+{
+	struct port_info *pi = (struct port_info *)(eth_dev->data->dev_private);
+	struct adapter *adapter = pi->adapter;
+	int i, dev_down = 0;
+
+	CXGBE_FUNC_TRACE();
+
+	if (!(adapter->flags & FULL_INIT_DONE))
+		return;
+
+	cxgbe_down(pi);
+
+	/*
+	 *  We clear queues only if both tx and rx path of the port
+	 *  have been disabled
+	 */
+	t4_sge_eth_clear_queues(pi);
+
+	/*  See if all ports are down */
+	for_each_port(adapter, i) {
+		pi = adap2pinfo(adapter, i);
+		/*
+		 * Skip first port of the adapter since it will be closed
+		 * by DPDK
+		 */
+		if (i == 0)
+			continue;
+		dev_down += (pi->eth_dev->data->dev_started == 0) ? 1 : 0;
+	}
+
+	/* If rest of the ports are stopped, then free up resources */
+	if (dev_down == (adapter->params.nports - 1))
+		cxgbe_close(adapter);
+}
+
+/* Start the device.
+ * It returns 0 on success.
+ */
+static int cxgbe_dev_start(struct rte_eth_dev *eth_dev)
+{
+	struct port_info *pi = (struct port_info *)(eth_dev->data->dev_private);
+	struct adapter *adapter = pi->adapter;
+	int err = 0, i;
+
+	CXGBE_FUNC_TRACE();
+
+	/*
+	 * If we don't have a connection to the firmware there's nothing we
+	 * can do.
+	 */
+	if (!(adapter->flags & FW_OK)) {
+		err = -ENXIO;
+		goto out;
+	}
+
+	if (!(adapter->flags & FULL_INIT_DONE)) {
+		err = cxgbe_up(adapter);
+		if (err < 0)
+			goto out;
+	}
+
+	err = setup_rss(pi);
+	if (err)
+		goto out;
+
+	for (i = 0; i < pi->n_tx_qsets; i++) {
+		err = cxgbe_dev_tx_queue_start(eth_dev, i);
+		if (err)
+			goto out;
+	}
+
+	for (i = 0; i < pi->n_rx_qsets; i++) {
+		err = cxgbe_dev_rx_queue_start(eth_dev, i);
+		if (err)
+			goto out;
+	}
+
+	err = link_start(pi);
+	if (err)
+		goto out;
+
+out:
+	return err;
+}
+
+/*
+ * Stop device: disable rx and tx functions to allow for reconfiguring.
+ */
+static void cxgbe_dev_stop(struct rte_eth_dev *eth_dev)
+{
+	struct port_info *pi = (struct port_info *)(eth_dev->data->dev_private);
+	struct adapter *adapter = pi->adapter;
+
+	CXGBE_FUNC_TRACE();
+
+	if (!(adapter->flags & FULL_INIT_DONE))
+		return;
+
+	cxgbe_down(pi);
+
+	/*
+	 *  We clear queues only if both tx and rx path of the port
+	 *  have been disabled
+	 */
+	t4_sge_eth_clear_queues(pi);
+}
+
 static int cxgbe_dev_configure(struct rte_eth_dev *eth_dev)
 {
 	struct port_info *pi = (struct port_info *)(eth_dev->data->dev_private);
@@ -390,6 +501,9 @@ static void cxgbe_dev_rx_queue_release(void *q)
 }
 
 static struct eth_dev_ops cxgbe_eth_dev_ops = {
+	.dev_start		= cxgbe_dev_start,
+	.dev_stop		= cxgbe_dev_stop,
+	.dev_close		= cxgbe_dev_close,
 	.dev_configure		= cxgbe_dev_configure,
 	.dev_infos_get		= cxgbe_dev_info_get,
 	.tx_queue_setup         = cxgbe_dev_tx_queue_setup,
diff --git a/drivers/net/cxgbe/cxgbe_main.c b/drivers/net/cxgbe/cxgbe_main.c
index 3029b57..7995d3c 100644
--- a/drivers/net/cxgbe/cxgbe_main.c
+++ b/drivers/net/cxgbe/cxgbe_main.c
@@ -827,6 +827,50 @@ void t4_os_portmod_changed(const struct adapter *adap, int port_id)
 }
 
 /**
+ * link_start - enable a port
+ * @dev: the port to enable
+ *
+ * Performs the MAC and PHY actions needed to enable a port.
+ */
+int link_start(struct port_info *pi)
+{
+	struct adapter *adapter = pi->adapter;
+	int ret;
+
+	/*
+	 * We do not set address filters and promiscuity here, the stack does
+	 * that step explicitly.
+	 */
+	ret = t4_set_rxmode(adapter, adapter->mbox, pi->viid, 1500, -1, -1,
+			    -1, 1, true);
+	if (ret == 0) {
+		ret = t4_change_mac(adapter, adapter->mbox, pi->viid,
+				    pi->xact_addr_filt,
+				    (u8 *)&pi->eth_dev->data->mac_addrs[0],
+				    true, true);
+		if (ret >= 0) {
+			pi->xact_addr_filt = ret;
+			ret = 0;
+		}
+	}
+	if (ret == 0)
+		ret = t4_link_l1cfg(adapter, adapter->mbox, pi->tx_chan,
+				    &pi->link_cfg);
+	if (ret == 0) {
+		/*
+		 * Enabling a Virtual Interface can result in an interrupt
+		 * during the processing of the VI Enable command and, in some
+		 * paths, result in an attempt to issue another command in the
+		 * interrupt context.  Thus, we disable interrupts during the
+		 * course of the VI Enable command ...
+		 */
+		ret = t4_enable_vi_params(adapter, adapter->mbox, pi->viid,
+					  true, true, false);
+	}
+	return ret;
+}
+
+/**
  * cxgb4_write_rss - write the RSS table for a given port
  * @pi: the port
  * @queues: array of queue indices for RSS
@@ -907,6 +951,101 @@ int setup_rss(struct port_info *pi)
 	return 0;
 }
 
+/*
+ * Enable NAPI scheduling and interrupt generation for all Rx queues.
+ */
+static void enable_rx(struct adapter *adap)
+{
+	struct sge *s = &adap->sge;
+	struct sge_rspq *q = &s->fw_evtq;
+	int i, j;
+
+	/* 0-increment GTS to start the timer and enable interrupts */
+	t4_write_reg(adap, MYPF_REG(A_SGE_PF_GTS),
+		     V_SEINTARM(q->intr_params) |
+		     V_INGRESSQID(q->cntxt_id));
+
+	for_each_port(adap, i) {
+		const struct port_info *pi = &adap->port[i];
+		struct rte_eth_dev *eth_dev = pi->eth_dev;
+
+		for (j = 0; j < eth_dev->data->nb_rx_queues; j++) {
+			q = eth_dev->data->rx_queues[j];
+
+			/*
+			 * 0-increment GTS to start the timer and enable
+			 * interrupts
+			 */
+			t4_write_reg(adap, MYPF_REG(A_SGE_PF_GTS),
+				     V_SEINTARM(q->intr_params) |
+				     V_INGRESSQID(q->cntxt_id));
+		}
+	}
+}
+
+/**
+ * cxgb_up - enable the adapter
+ * @adap: adapter being enabled
+ *
+ * Called when the first port is enabled, this function performs the
+ * actions necessary to make an adapter operational, such as completing
+ * the initialization of HW modules, and enabling interrupts.
+ */
+int cxgbe_up(struct adapter *adap)
+{
+	enable_rx(adap);
+	t4_sge_tx_monitor_start(adap);
+	t4_intr_enable(adap);
+	adap->flags |= FULL_INIT_DONE;
+
+	/* TODO: deadman watchdog ?? */
+	return 0;
+}
+
+/*
+ * Close the port
+ */
+int cxgbe_down(struct port_info *pi)
+{
+	struct adapter *adapter = pi->adapter;
+	int err = 0;
+
+	err = t4_enable_vi(adapter, adapter->mbox, pi->viid, false, false);
+	if (err) {
+		dev_err(adapter, "%s: disable_vi failed: %d\n", __func__, err);
+		return err;
+	}
+
+	t4_reset_link_config(adapter, pi->port_id);
+	return 0;
+}
+
+/*
+ * Release resources when all the ports have been stopped.
+ */
+void cxgbe_close(struct adapter *adapter)
+{
+	struct port_info *pi;
+	int i;
+
+	if (adapter->flags & FULL_INIT_DONE) {
+		t4_intr_disable(adapter);
+		t4_sge_tx_monitor_stop(adapter);
+		t4_free_sge_resources(adapter);
+		for_each_port(adapter, i) {
+			pi = adap2pinfo(adapter, i);
+			if (pi->viid != 0)
+				t4_free_vi(adapter, adapter->mbox,
+					   adapter->pf, 0, pi->viid);
+			rte_free(pi->eth_dev->data->mac_addrs);
+		}
+		adapter->flags &= ~FULL_INIT_DONE;
+	}
+
+	if (adapter->flags & FW_OK)
+		t4_fw_bye(adapter, adapter->mbox);
+}
+
 int cxgbe_probe(struct adapter *adapter)
 {
 	struct port_info *pi;
diff --git a/drivers/net/cxgbe/sge.c b/drivers/net/cxgbe/sge.c
index 4f4fdd6..4da6320 100644
--- a/drivers/net/cxgbe/sge.c
+++ b/drivers/net/cxgbe/sge.c
@@ -1959,6 +1959,35 @@ static void free_rspq_fl(struct adapter *adap, struct sge_rspq *rq,
 	}
 }
 
+/*
+ * Clear all queues of the port
+ *
+ * Note:  This function must only be called after rx and tx path
+ * of the port have been disabled.
+ */
+void t4_sge_eth_clear_queues(struct port_info *pi)
+{
+	int i;
+	struct adapter *adap = pi->adapter;
+	struct sge_eth_rxq *rxq = &adap->sge.ethrxq[pi->first_qset];
+	struct sge_eth_txq *txq = &adap->sge.ethtxq[pi->first_qset];
+
+	for (i = 0; i < pi->n_rx_qsets; i++, rxq++) {
+		if (rxq->rspq.desc)
+			t4_sge_eth_rxq_stop(adap, &rxq->rspq);
+	}
+	for (i = 0; i < pi->n_tx_qsets; i++, txq++) {
+		if (txq->q.desc) {
+			struct sge_txq *q = &txq->q;
+
+			t4_sge_eth_txq_stop(txq);
+			reclaim_completed_tx(q);
+			free_tx_desc(q, q->size);
+			q->equeidx = q->pidx;
+		}
+	}
+}
+
 void t4_sge_eth_rxq_release(struct adapter *adap, struct sge_eth_rxq *rxq)
 {
 	if (rxq->rspq.desc) {
@@ -1990,6 +2019,35 @@ void t4_sge_tx_monitor_stop(struct adapter *adap)
 }
 
 /**
+ * t4_free_sge_resources - free SGE resources
+ * @adap: the adapter
+ *
+ * Frees resources used by the SGE queue sets.
+ */
+void t4_free_sge_resources(struct adapter *adap)
+{
+	int i;
+	struct sge_eth_rxq *rxq = &adap->sge.ethrxq[0];
+	struct sge_eth_txq *txq = &adap->sge.ethtxq[0];
+
+	/* clean up Ethernet Tx/Rx queues */
+	for (i = 0; i < adap->sge.max_ethqsets; i++, rxq++, txq++) {
+		/* Free only the queues allocated */
+		if (rxq->rspq.desc) {
+			t4_sge_eth_rxq_release(adap, rxq);
+			rxq->rspq.eth_dev = NULL;
+		}
+		if (txq->q.desc) {
+			t4_sge_eth_txq_release(adap, txq);
+			txq->eth_dev = NULL;
+		}
+	}
+
+	if (adap->sge.fw_evtq.desc)
+		free_rspq_fl(adap, &adap->sge.fw_evtq, NULL);
+}
+
+/**
  * t4_sge_init - initialize SGE
  * @adap: the adapter
  *
