All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 1/6] bna: Brocade 10Gb Ethernet device driver
@ 2010-08-23 21:24 Rasesh Mody
  0 siblings, 0 replies; 8+ messages in thread
From: Rasesh Mody @ 2010-08-23 21:24 UTC (permalink / raw)
  To: netdev; +Cc: amathur, ddutt, huangj, rmody

From: Rasesh Mody <rmody@brocade.com>

This is patch 1/6 which contains linux driver source for
Brocade's BR1010/BR1020 10Gb CEE capable ethernet adapter.
Source is based against net-next-2.6.

We wish this patch to be considered for inclusion in net-next-2.6

Signed-off-by: Debashis Dutt <ddutt@brocade.com>
Signed-off-by: Rasesh Mody <rmody@brocade.com>
---
 bnad.c         | 3270 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 bnad.h         |  334 +++++
 bnad_ethtool.c | 1282 ++++++++++++++++++++++
 3 files changed, 4886 insertions(+)

diff -ruP net-next-2.6.35-rc1-orig/drivers/net/bna/bnad.c net-next-2.6.35-rc1-mod/drivers/net/bna/bnad.c
--- net-next-2.6.35-rc1-orig/drivers/net/bna/bnad.c	1969-12-31 16:00:00.000000000 -0800
+++ net-next-2.6.35-rc1-mod/drivers/net/bna/bnad.c	2010-08-23 13:24:46.979969000 -0700
@@ -0,0 +1,3270 @@
+/*
+ * Linux network driver for Brocade Converged Network Adapter.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License (GPL) Version 2 as
+ * published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+/*
+ * Copyright (c) 2005-2010 Brocade Communications Systems, Inc.
+ * All rights reserved
+ * www.brocade.com
+ */
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <linux/etherdevice.h>
+#include <linux/in.h>
+#include <linux/ethtool.h>
+#include <linux/if_vlan.h>
+#include <linux/if_ether.h>
+#include <linux/ip.h>
+
+#include "bnad.h"
+#include "bna.h"
+#include "cna.h"
+
+DEFINE_MUTEX(bnad_fwimg_mutex);
+
+/*
+ * Module params
+ */
+static uint bnad_msix_disable;
+module_param(bnad_msix_disable, uint, 0444);
+MODULE_PARM_DESC(bnad_msix_disable, "Disable MSIX mode");
+
+static uint bnad_ioc_auto_recover = 1;
+module_param(bnad_ioc_auto_recover, uint, 0444);
+MODULE_PARM_DESC(bnad_ioc_auto_recover, "Enable / Disable auto recovery");
+
+/*
+ * Global variables
+ */
+u32 bnad_rxqs_per_cq = 2;
+
+const u8 bnad_bcast_addr[] =  {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+
+/*
+ * Local MACROS
+ */
+#define BNAD_TX_UNMAPQ_DEPTH (bnad->txq_depth * 2)
+
+#define BNAD_RX_UNMAPQ_DEPTH (bnad->rxq_depth)
+
+#define BNAD_GET_MBOX_IRQ(_bnad)				\
+	(((_bnad)->cfg_flags & BNAD_CF_MSIX) ?			\
+	 ((_bnad)->msix_table[(_bnad)->msix_num - 1].vector) : 	\
+	 ((_bnad)->pcidev->irq))
+
+#define BNAD_FILL_UNMAPQ_MEM_REQ(_res_info, _num, _depth)	\
+do {								\
+	(_res_info)->res_type = BNA_RES_T_MEM;			\
+	(_res_info)->res_u.mem_info.mem_type = BNA_MEM_T_KVA;	\
+	(_res_info)->res_u.mem_info.num = (_num);		\
+	(_res_info)->res_u.mem_info.len =			\
+	sizeof(struct bnad_unmap_q) +				\
+	(sizeof(struct bnad_skb_unmap) * ((_depth) - 1));	\
+} while (0)
+
+/*
+ * Reinitialize completions in CQ, once Rx is taken down
+ */
+static void
+bnad_cq_cmpl_init(struct bnad *bnad, struct bna_ccb *ccb)
+{
+	struct bna_cq_entry *cmpl, *next_cmpl;
+	unsigned int wi_range, wis = 0, ccb_prod = 0;
+	int i;
+
+	BNA_CQ_QPGE_PTR_GET(ccb_prod, ccb->sw_qpt, cmpl,
+			    wi_range);
+
+	for (i = 0; i < ccb->q_depth; i++) {
+		wis++;
+		if (likely(--wi_range))
+			next_cmpl = cmpl + 1;
+		else {
+			BNA_QE_INDX_ADD(ccb_prod, wis, ccb->q_depth);
+			wis = 0;
+			BNA_CQ_QPGE_PTR_GET(ccb_prod, ccb->sw_qpt,
+						next_cmpl, wi_range);
+		}
+		cmpl->valid = 0;
+		cmpl = next_cmpl;
+	}
+}
+
+/*
+ * Frees all pending Tx Bufs
+ * At this point no activity is expected on the Q,
+ * so DMA unmap & freeing is fine.
+ */
+static void
+bnad_free_all_txbufs(struct bnad *bnad,
+		 struct bna_tcb *tcb)
+{
+	u16 		unmap_cons;
+	struct bnad_unmap_q *unmap_q = tcb->unmap_q;
+	struct bnad_skb_unmap *unmap_array;
+	struct sk_buff 		*skb = NULL;
+	int			i;
+
+	unmap_array = unmap_q->unmap_array;
+
+	unmap_cons = 0;
+	while (unmap_cons < unmap_q->q_depth) {
+		skb = unmap_array[unmap_cons].skb;
+		if (!skb) {
+			unmap_cons++;
+			continue;
+		}
+		unmap_array[unmap_cons].skb = NULL;
+
+		pci_unmap_single(bnad->pcidev,
+				 pci_unmap_addr(&unmap_array[unmap_cons],
+						dma_addr), skb_headlen(skb),
+						PCI_DMA_TODEVICE);
+
+		pci_unmap_addr_set(&unmap_array[unmap_cons], dma_addr, 0);
+		unmap_cons++;
+		for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
+			pci_unmap_page(bnad->pcidev,
+				       pci_unmap_addr(&unmap_array[unmap_cons],
+						      dma_addr),
+				       skb_shinfo(skb)->frags[i].size,
+				       PCI_DMA_TODEVICE);
+			pci_unmap_addr_set(&unmap_array[unmap_cons], dma_addr,
+					   0);
+			unmap_cons++;
+		}
+		dev_kfree_skb_any(skb);
+	}
+}
+
+/* Data Path Handlers */
+
+/*
+ * bnad_free_txbufs : Frees the Tx bufs on Tx completion
+ * Can be called in a) Interrupt context
+ *		    b) Sending context
+ *		    c) Tasklet context
+ */
+static u32
+bnad_free_txbufs(struct bnad *bnad,
+		 struct bna_tcb *tcb)
+{
+	u32 		sent_packets = 0, sent_bytes = 0;
+	u16 		wis, unmap_cons, updated_hw_cons;
+	struct bnad_unmap_q *unmap_q = tcb->unmap_q;
+	struct bnad_skb_unmap *unmap_array;
+	struct sk_buff 		*skb;
+	int i;
+
+	/*
+	 * Just return if TX is stopped. This check is useful
+	 * when bnad_free_txbufs() runs out of a tasklet scheduled
+	 * before bnad_cb_tx_cleanup() cleared BNAD_RF_TX_STARTED bit
+	 * but this routine runs actually after the cleanup has been
+	 * executed.
+	 */
+	if (!test_bit(BNAD_RF_TX_STARTED, &bnad->run_flags))
+		return 0;
+
+	updated_hw_cons = *(tcb->hw_consumer_index);
+
+	wis = BNA_Q_INDEX_CHANGE(tcb->consumer_index,
+				  updated_hw_cons, tcb->q_depth);
+
+	BUG_ON(!(wis <= BNA_QE_IN_USE_CNT(tcb, tcb->q_depth)));
+
+	unmap_array = unmap_q->unmap_array;
+	unmap_cons = unmap_q->consumer_index;
+
+	prefetch(&unmap_array[unmap_cons + 1]);
+	while (wis) {
+		skb = unmap_array[unmap_cons].skb;
+
+		unmap_array[unmap_cons].skb = NULL;
+
+		sent_packets++;
+		sent_bytes += skb->len;
+		wis -= BNA_TXQ_WI_NEEDED(1 + skb_shinfo(skb)->nr_frags);
+
+		pci_unmap_single(bnad->pcidev,
+				 pci_unmap_addr(&unmap_array[unmap_cons],
+						dma_addr), skb_headlen(skb),
+				 PCI_DMA_TODEVICE);
+		pci_unmap_addr_set(&unmap_array[unmap_cons], dma_addr, 0);
+		BNA_QE_INDX_ADD(unmap_cons, 1, unmap_q->q_depth);
+
+		prefetch(&unmap_array[unmap_cons + 1]);
+		for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
+			prefetch(&unmap_array[unmap_cons + 1]);
+
+			pci_unmap_page(bnad->pcidev,
+				       pci_unmap_addr(&unmap_array[unmap_cons],
+						      dma_addr),
+				       skb_shinfo(skb)->frags[i].size,
+				       PCI_DMA_TODEVICE);
+			pci_unmap_addr_set(&unmap_array[unmap_cons], dma_addr,
+					   0);
+			BNA_QE_INDX_ADD(unmap_cons, 1, unmap_q->q_depth);
+		}
+		dev_kfree_skb_any(skb);
+	}
+
+	/* Update consumer pointers. */
+	tcb->consumer_index = updated_hw_cons;
+	unmap_q->consumer_index = unmap_cons;
+
+	tcb->txq->tx_packets += sent_packets;
+	tcb->txq->tx_bytes += sent_bytes;
+
+	return sent_packets;
+}
+
+/* Tx Free Tasklet function */
+/* Frees for all the tcb's in all the Tx's */
+/*
+ * Scheduled from sending context, so that
+ * the fat Tx lock is not held for too long
+ * in the sending context.
+ */
+static void
+bnad_tx_free_tasklet(unsigned long bnad_ptr)
+{
+	struct bnad *bnad = (struct bnad *)bnad_ptr;
+	struct bna_tcb *tcb;
+	u32 		acked;
+	int			i, j;
+
+	for (i = 0; i < bnad->num_tx; i++) {
+		for (j = 0; j < bnad->num_txq_per_tx; j++) {
+			tcb = bnad->tx_info[i].tcb[j];
+			if (!tcb)
+				continue;
+			if (((u16) (*tcb->hw_consumer_index) !=
+				tcb->consumer_index) &&
+				(!test_and_set_bit(BNAD_TXQ_FREE_SENT,
+						  &tcb->flags))) {
+				acked = bnad_free_txbufs(bnad, tcb);
+				bna_ib_ack(tcb->i_dbell, acked);
+				smp_mb__before_clear_bit();
+				clear_bit(BNAD_TXQ_FREE_SENT, &tcb->flags);
+			}
+		}
+	}
+}
+
+static u32
+bnad_tx(struct bnad *bnad, struct bna_tcb *tcb)
+{
+	struct net_device *netdev = bnad->netdev;
+	u32 sent;
+
+	if (test_and_set_bit(BNAD_TXQ_FREE_SENT, &tcb->flags))
+		return 0;
+
+	sent = bnad_free_txbufs(bnad, tcb);
+	if (sent) {
+		if (netif_queue_stopped(netdev) &&
+		    netif_carrier_ok(netdev) &&
+		    BNA_QE_FREE_CNT(tcb, tcb->q_depth) >=
+				    BNAD_NETIF_WAKE_THRESHOLD) {
+			netif_wake_queue(netdev);
+			BNAD_UPDATE_CTR(bnad, netif_queue_wakeup);
+		}
+		bna_ib_ack(tcb->i_dbell, sent);
+	} else
+		bna_ib_ack(tcb->i_dbell, 0);
+
+	smp_mb__before_clear_bit();
+	clear_bit(BNAD_TXQ_FREE_SENT, &tcb->flags);
+
+	return sent;
+}
+
+/* MSIX Tx Completion Handler */
+static irqreturn_t
+bnad_msix_tx(int irq, void *data)
+{
+	struct bna_tcb *tcb = (struct bna_tcb *)data;
+	struct bnad *bnad = tcb->bnad;
+
+	bnad_tx(bnad, tcb);
+
+	return IRQ_HANDLED;
+}
+
+static void
+bnad_reset_rcb(struct bnad *bnad, struct bna_rcb *rcb)
+{
+	struct bnad_unmap_q *unmap_q = rcb->unmap_q;
+
+	rcb->producer_index = 0;
+	rcb->consumer_index = 0;
+
+	unmap_q->producer_index = 0;
+	unmap_q->consumer_index = 0;
+}
+
+static void
+bnad_free_rxbufs(struct bnad *bnad, struct bna_rcb *rcb)
+{
+	struct bnad_unmap_q *unmap_q;
+	struct sk_buff *skb;
+
+	unmap_q = rcb->unmap_q;
+	while (BNA_QE_IN_USE_CNT(unmap_q, unmap_q->q_depth)) {
+		skb = unmap_q->unmap_array[unmap_q->consumer_index].skb;
+		BUG_ON(!(skb));
+		unmap_q->unmap_array[unmap_q->consumer_index].skb = NULL;
+		pci_unmap_single(bnad->pcidev, pci_unmap_addr(&unmap_q->
+					unmap_array[unmap_q->consumer_index],
+					dma_addr), rcb->rxq->buffer_size +
+					NET_IP_ALIGN, PCI_DMA_FROMDEVICE);
+		dev_kfree_skb(skb);
+		BNA_QE_INDX_ADD(unmap_q->consumer_index, 1, unmap_q->q_depth);
+		BNA_QE_INDX_ADD(rcb->consumer_index, 1, rcb->q_depth);
+	}
+
+	bnad_reset_rcb(bnad, rcb);
+}
+
+static void
+bnad_alloc_n_post_rxbufs(struct bnad *bnad, struct bna_rcb *rcb)
+{
+	u16 to_alloc, alloced, unmap_prod, wi_range;
+	struct bnad_unmap_q *unmap_q = rcb->unmap_q;
+	struct bnad_skb_unmap *unmap_array;
+	struct bna_rxq_entry *rxent;
+	struct sk_buff *skb;
+	dma_addr_t dma_addr;
+
+	alloced = 0;
+	to_alloc =
+		BNA_QE_FREE_CNT(unmap_q, unmap_q->q_depth);
+
+	unmap_array = unmap_q->unmap_array;
+	unmap_prod = unmap_q->producer_index;
+
+	BNA_RXQ_QPGE_PTR_GET(unmap_prod, rcb->sw_qpt, rxent, wi_range);
+
+	while (to_alloc--) {
+		if (!wi_range) {
+			BNA_RXQ_QPGE_PTR_GET(unmap_prod, rcb->sw_qpt, rxent,
+					     wi_range);
+		}
+		skb = alloc_skb(rcb->rxq->buffer_size + NET_IP_ALIGN,
+				     GFP_ATOMIC);
+		if (unlikely(!skb)) {
+			BNAD_UPDATE_CTR(bnad, rxbuf_alloc_failed);
+			goto finishing;
+		}
+		skb->dev = bnad->netdev;
+		skb_reserve(skb, NET_IP_ALIGN);
+		unmap_array[unmap_prod].skb = skb;
+		dma_addr = pci_map_single(bnad->pcidev, skb->data,
+			rcb->rxq->buffer_size, PCI_DMA_FROMDEVICE);
+		pci_unmap_addr_set(&unmap_array[unmap_prod], dma_addr,
+				   dma_addr);
+		BNA_SET_DMA_ADDR(dma_addr, &rxent->host_addr);
+		BNA_QE_INDX_ADD(unmap_prod, 1, unmap_q->q_depth);
+
+		rxent++;
+		wi_range--;
+		alloced++;
+	}
+
+finishing:
+	if (likely(alloced)) {
+		unmap_q->producer_index = unmap_prod;
+		rcb->producer_index = unmap_prod;
+		smp_mb();
+		bna_rxq_prod_indx_doorbell(rcb);
+	}
+}
+
+/*
+ * Locking is required in the enable path
+ * because it is called from a napi poll
+ * context, where the bna_lock is not held
+ * unlike the IRQ context.
+ */
+static void
+bnad_enable_txrx_irqs(struct bnad *bnad)
+{
+	struct bna_tcb *tcb;
+	struct bna_ccb *ccb;
+	int i, j;
+	unsigned long flags;
+
+	spin_lock_irqsave(&bnad->bna_lock, flags);
+	for (i = 0; i < bnad->num_tx; i++) {
+		for (j = 0; j < bnad->num_txq_per_tx; j++) {
+			tcb = bnad->tx_info[i].tcb[j];
+			bna_ib_coalescing_timer_set(tcb->i_dbell,
+				tcb->txq->ib->ib_config.coalescing_timeo);
+			bna_ib_ack(tcb->i_dbell, 0);
+		}
+	}
+
+	for (i = 0; i < bnad->num_rx; i++) {
+		for (j = 0; j < bnad->num_rxp_per_rx; j++) {
+			ccb = bnad->rx_info[i].rx_ctrl[j].ccb;
+			bnad_enable_rx_irq_unsafe(ccb);
+		}
+	}
+	spin_unlock_irqrestore(&bnad->bna_lock, flags);
+}
+
+static inline void
+bnad_refill_rxq(struct bnad *bnad, struct bna_rcb *rcb)
+{
+	struct bnad_unmap_q *unmap_q = rcb->unmap_q;
+
+	if (!test_and_set_bit(BNAD_RXQ_REFILL, &rcb->flags)) {
+		if (BNA_QE_FREE_CNT(unmap_q, unmap_q->q_depth)
+			 >> BNAD_RXQ_REFILL_THRESHOLD_SHIFT)
+			bnad_alloc_n_post_rxbufs(bnad, rcb);
+		smp_mb__before_clear_bit();
+		clear_bit(BNAD_RXQ_REFILL, &rcb->flags);
+	}
+}
+
+static u32
+bnad_poll_cq(struct bnad *bnad, struct bna_ccb *ccb, int budget)
+{
+	struct bna_cq_entry *cmpl, *next_cmpl;
+	struct bna_rcb *rcb = NULL;
+	unsigned int wi_range, packets = 0, wis = 0;
+	struct bnad_unmap_q *unmap_q;
+	struct sk_buff *skb;
+	u32 flags;
+	u32 qid0 = ccb->rcb[0]->rxq->rxq_id;
+	struct bna_pkt_rate *pkt_rt = &ccb->pkt_rate;
+
+	prefetch(bnad->netdev);
+	BNA_CQ_QPGE_PTR_GET(ccb->producer_index, ccb->sw_qpt, cmpl,
+			    wi_range);
+	BUG_ON(!(wi_range <= ccb->q_depth));
+	while (cmpl->valid && packets < budget) {
+		packets++;
+		BNA_UPDATE_PKT_CNT(pkt_rt, ntohs(cmpl->length));
+
+		if (qid0 == cmpl->rxq_id)
+			rcb = ccb->rcb[0];
+		else
+			rcb = ccb->rcb[1];
+
+		unmap_q = rcb->unmap_q;
+
+		skb = unmap_q->unmap_array[unmap_q->consumer_index].skb;
+		BUG_ON(!(skb));
+		unmap_q->unmap_array[unmap_q->consumer_index].skb = NULL;
+		pci_unmap_single(bnad->pcidev,
+				 pci_unmap_addr(&unmap_q->
+						unmap_array[unmap_q->
+							    consumer_index],
+						dma_addr),
+						rcb->rxq->buffer_size,
+						PCI_DMA_FROMDEVICE);
+		BNA_QE_INDX_ADD(unmap_q->consumer_index, 1, unmap_q->q_depth);
+
+		/* Should be more efficient ? Performance ? */
+		BNA_QE_INDX_ADD(rcb->consumer_index, 1, rcb->q_depth);
+
+		wis++;
+		if (likely(--wi_range))
+			next_cmpl = cmpl + 1;
+		else {
+			BNA_QE_INDX_ADD(ccb->producer_index, wis, ccb->q_depth);
+			wis = 0;
+			BNA_CQ_QPGE_PTR_GET(ccb->producer_index, ccb->sw_qpt,
+						next_cmpl, wi_range);
+			BUG_ON(!(wi_range <= ccb->q_depth));
+		}
+		prefetch(next_cmpl);
+
+		flags = ntohl(cmpl->flags);
+		if (unlikely
+		    (flags &
+		     (BNA_CQ_EF_MAC_ERROR | BNA_CQ_EF_FCS_ERROR |
+		      BNA_CQ_EF_TOO_LONG))) {
+			dev_kfree_skb_any(skb);
+			rcb->rxq->rx_packets_with_error++;
+			goto next;
+		}
+
+		skb_put(skb, ntohs(cmpl->length));
+		if (likely
+		    (bnad->rx_csum &&
+		     (((flags & BNA_CQ_EF_IPV4) &&
+		      (flags & BNA_CQ_EF_L3_CKSUM_OK)) ||
+		      (flags & BNA_CQ_EF_IPV6)) &&
+		      (flags & (BNA_CQ_EF_TCP | BNA_CQ_EF_UDP)) &&
+		      (flags & BNA_CQ_EF_L4_CKSUM_OK)))
+			skb->ip_summed = CHECKSUM_UNNECESSARY;
+		else
+			skb->ip_summed = CHECKSUM_NONE;
+
+		rcb->rxq->rx_packets++;
+		rcb->rxq->rx_bytes += skb->len;
+		skb->protocol = eth_type_trans(skb, bnad->netdev);
+
+		if (bnad->vlan_grp && (flags & BNA_CQ_EF_VLAN)) {
+			struct bnad_rx_ctrl *rx_ctrl =
+				(struct bnad_rx_ctrl *)ccb->ctrl;
+			if (skb->ip_summed == CHECKSUM_UNNECESSARY)
+				vlan_gro_receive(&rx_ctrl->napi, bnad->vlan_grp,
+						ntohs(cmpl->vlan_tag), skb);
+			else
+				vlan_hwaccel_receive_skb(skb,
+							 bnad->vlan_grp,
+							 ntohs(cmpl->vlan_tag));
+
+		} else { /* Not VLAN tagged/stripped */
+			struct bnad_rx_ctrl *rx_ctrl =
+				(struct bnad_rx_ctrl *)ccb->ctrl;
+			if (skb->ip_summed == CHECKSUM_UNNECESSARY)
+				napi_gro_receive(&rx_ctrl->napi, skb);
+			else
+				netif_receive_skb(skb);
+		}
+
+next:
+		cmpl->valid = 0;
+		cmpl = next_cmpl;
+	}
+
+	BNA_QE_INDX_ADD(ccb->producer_index, wis, ccb->q_depth);
+
+	if (likely(ccb)) {
+		bna_ib_ack(ccb->i_dbell, packets);
+		bnad_refill_rxq(bnad, ccb->rcb[0]);
+		if (ccb->rcb[1])
+			bnad_refill_rxq(bnad, ccb->rcb[1]);
+	} else
+		bna_ib_ack(ccb->i_dbell, 0);
+
+	return packets;
+}
+
+static void
+bnad_disable_rx_irq(struct bnad *bnad, struct bna_ccb *ccb)
+{
+	bna_ib_coalescing_timer_set(ccb->i_dbell, 0);
+	bna_ib_ack(ccb->i_dbell, 0);
+}
+
+static void
+bnad_enable_rx_irq(struct bnad *bnad, struct bna_ccb *ccb)
+{
+	spin_lock_irq(&bnad->bna_lock); /* Because of polling context */
+	bnad_enable_rx_irq_unsafe(ccb);
+	spin_unlock_irq(&bnad->bna_lock);
+}
+
+static void
+bnad_netif_rx_schedule_poll(struct bnad *bnad, struct bna_ccb *ccb)
+{
+	struct bnad_rx_ctrl *rx_ctrl = (struct bnad_rx_ctrl *)(ccb->ctrl);
+	if (likely(napi_schedule_prep((&rx_ctrl->napi)))) {
+		bnad_disable_rx_irq(bnad, ccb);
+		__napi_schedule((&rx_ctrl->napi));
+	}
+	BNAD_UPDATE_CTR(bnad, netif_rx_schedule);
+}
+
+/* MSIX Rx Path Handler */
+static irqreturn_t
+bnad_msix_rx(int irq, void *data)
+{
+	struct bna_ccb *ccb = (struct bna_ccb *)data;
+	struct bnad *bnad = ccb->bnad;
+
+	bnad_netif_rx_schedule_poll(bnad, ccb);
+
+	return IRQ_HANDLED;
+}
+
+/* Interrupt handlers */
+
+/* Mbox Interrupt Handlers */
+static irqreturn_t
+bnad_msix_mbox_handler(int irq, void *data)
+{
+	u32 intr_status;
+	unsigned long  flags;
+	struct net_device *netdev = data;
+	struct bnad *bnad;
+
+	bnad = netdev_priv(netdev);
+
+	/* BNA_ISR_GET(bnad); Inc Ref count */
+	spin_lock_irqsave(&bnad->bna_lock, flags);
+
+	bna_intr_status_get(&bnad->bna, intr_status);
+
+	if (BNA_IS_MBOX_ERR_INTR(intr_status))
+		bna_mbox_handler(&bnad->bna, intr_status);
+
+	spin_unlock_irqrestore(&bnad->bna_lock, flags);
+
+	/* BNAD_ISR_PUT(bnad); Dec Ref count */
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t
+bnad_isr(int irq, void *data)
+{
+	int i, j;
+	u32 intr_status;
+	unsigned long flags;
+	struct net_device *netdev = data;
+	struct bnad *bnad = netdev_priv(netdev);
+	struct bnad_rx_info *rx_info;
+	struct bnad_rx_ctrl *rx_ctrl;
+
+	spin_lock_irqsave(&bnad->bna_lock, flags);
+
+	bna_intr_status_get(&bnad->bna, intr_status);
+	if (!intr_status) {
+		spin_unlock_irqrestore(&bnad->bna_lock, flags);
+		return IRQ_NONE;
+	}
+
+	if (BNA_IS_MBOX_ERR_INTR(intr_status)) {
+		bna_mbox_handler(&bnad->bna, intr_status);
+		if (!BNA_IS_INTX_DATA_INTR(intr_status)) {
+			spin_unlock_irqrestore(&bnad->bna_lock, flags);
+			goto done;
+		}
+	}
+	spin_unlock_irqrestore(&bnad->bna_lock, flags);
+
+	/* Process data interrupts */
+	for (i = 0; i < bnad->num_rx; i++) {
+		rx_info = &bnad->rx_info[i];
+		if (!rx_info->rx)
+			continue;
+		for (j = 0; j < bnad->num_rxp_per_rx; j++) {
+			rx_ctrl = &rx_info->rx_ctrl[j];
+			if (rx_ctrl->ccb)
+				bnad_netif_rx_schedule_poll(bnad,
+							    rx_ctrl->ccb);
+		}
+	}
+done:
+	return IRQ_HANDLED;
+}
+
+/*
+ * Called in interrupt / callback context
+ * with bna_lock held, so cfg_flags access is OK
+ */
+static void
+bnad_enable_mbox_irq(struct bnad *bnad)
+{
+	int irq = BNAD_GET_MBOX_IRQ(bnad);
+
+	if (!(bnad->cfg_flags & BNAD_CF_MSIX))
+		return;
+
+	if (test_and_clear_bit(BNAD_RF_MBOX_IRQ_DISABLED, &bnad->run_flags))
+		enable_irq(irq);
+	BNAD_UPDATE_CTR(bnad, mbox_intr_enabled);
+}
+
+/*
+ * Called with bnad->bna_lock held b'cos of
+ * bnad->cfg_flags access.
+ */
+void
+bnad_disable_mbox_irq(struct bnad *bnad)
+{
+	int irq = BNAD_GET_MBOX_IRQ(bnad);
+
+	if (!(bnad->cfg_flags & BNAD_CF_MSIX))
+		return;
+
+	if (!test_and_set_bit(BNAD_RF_MBOX_IRQ_DISABLED, &bnad->run_flags))
+		disable_irq_nosync(irq);
+	BNAD_UPDATE_CTR(bnad, mbox_intr_disabled);
+}
+
+/* Control Path Handlers */
+
+/* Callbacks */
+void
+bnad_cb_device_enable_mbox_intr(struct bnad *bnad)
+{
+	bnad_enable_mbox_irq(bnad);
+}
+
+void
+bnad_cb_device_disable_mbox_intr(struct bnad *bnad)
+{
+	bnad_disable_mbox_irq(bnad);
+}
+
+void
+bnad_cb_device_enabled(struct bnad *bnad, enum bna_cb_status status)
+{
+	complete(&bnad->bnad_completions.ioc_comp);
+	bnad->bnad_completions.ioc_comp_status = status;
+}
+
+void
+bnad_cb_device_disabled(struct bnad *bnad, enum bna_cb_status status)
+{
+	complete(&bnad->bnad_completions.ioc_comp);
+	bnad->bnad_completions.ioc_comp_status = status;
+}
+
+static void
+bnad_cb_port_disabled(void *arg, enum bna_cb_status status)
+{
+	struct bnad *bnad = (struct bnad *)arg;
+
+	complete(&bnad->bnad_completions.port_comp);
+
+	netif_carrier_off(bnad->netdev);
+}
+
+void
+bnad_cb_port_link_status(struct bnad *bnad,
+			enum bna_link_status link_status)
+{
+	bool link_up = 0;
+
+	link_up = (link_status == BNA_LINK_UP) || (link_status == BNA_CEE_UP);
+
+	if (link_status == BNA_CEE_UP) {
+		set_bit(BNAD_RF_CEE_RUNNING, &bnad->run_flags);
+		BNAD_UPDATE_CTR(bnad, cee_up);
+	} else
+		clear_bit(BNAD_RF_CEE_RUNNING, &bnad->run_flags);
+
+	if (link_up) {
+		if (!netif_carrier_ok(bnad->netdev)) {
+			pr_warn("bna: %s link up\n",
+				bnad->netdev->name);
+			netif_carrier_on(bnad->netdev);
+			BNAD_UPDATE_CTR(bnad, link_toggle);
+			if (test_bit(BNAD_RF_TX_STARTED, &bnad->run_flags)) {
+				/* Force an immediate Transmit Schedule */
+				pr_info("bna: %s TX_STARTED\n",
+					bnad->netdev->name);
+				netif_wake_queue(bnad->netdev);
+				BNAD_UPDATE_CTR(bnad, netif_queue_wakeup);
+			} else {
+				netif_stop_queue(bnad->netdev);
+				BNAD_UPDATE_CTR(bnad, netif_queue_stop);
+			}
+		}
+	} else {
+		if (netif_carrier_ok(bnad->netdev)) {
+			pr_warn("bna: %s link down\n",
+				bnad->netdev->name);
+			netif_carrier_off(bnad->netdev);
+			BNAD_UPDATE_CTR(bnad, link_toggle);
+		}
+	}
+}
+
+static void
+bnad_cb_tx_disabled(void *arg, struct bna_tx *tx,
+			enum bna_cb_status status)
+{
+	struct bnad *bnad = (struct bnad *)arg;
+
+	complete(&bnad->bnad_completions.tx_comp);
+}
+
+static void
+bnad_cb_tcb_setup(struct bnad *bnad, struct bna_tcb *tcb)
+{
+	struct bnad_tx_info *tx_info =
+			(struct bnad_tx_info *)tcb->txq->tx->priv;
+	struct bnad_unmap_q *unmap_q = tcb->unmap_q;
+
+	tx_info->tcb[tcb->id] = tcb;
+	unmap_q->producer_index = 0;
+	unmap_q->consumer_index = 0;
+	unmap_q->q_depth = BNAD_TX_UNMAPQ_DEPTH;
+}
+
+static void
+bnad_cb_tcb_destroy(struct bnad *bnad, struct bna_tcb *tcb)
+{
+	struct bnad_tx_info *tx_info =
+			(struct bnad_tx_info *)tcb->txq->tx->priv;
+
+	tx_info->tcb[tcb->id] = NULL;
+}
+
+static void
+bnad_cb_rcb_setup(struct bnad *bnad, struct bna_rcb *rcb)
+{
+	struct bnad_unmap_q *unmap_q = rcb->unmap_q;
+
+	unmap_q->producer_index = 0;
+	unmap_q->consumer_index = 0;
+	unmap_q->q_depth = BNAD_RX_UNMAPQ_DEPTH;
+}
+
+static void
+bnad_cb_ccb_setup(struct bnad *bnad, struct bna_ccb *ccb)
+{
+	struct bnad_rx_info *rx_info =
+			(struct bnad_rx_info *)ccb->cq->rx->priv;
+
+	rx_info->rx_ctrl[ccb->id].ccb = ccb;
+	ccb->ctrl = &rx_info->rx_ctrl[ccb->id];
+}
+
+static void
+bnad_cb_ccb_destroy(struct bnad *bnad, struct bna_ccb *ccb)
+{
+	struct bnad_rx_info *rx_info =
+			(struct bnad_rx_info *)ccb->cq->rx->priv;
+
+	rx_info->rx_ctrl[ccb->id].ccb = NULL;
+}
+
+static void
+bnad_cb_tx_stall(struct bnad *bnad, struct bna_tcb *tcb)
+{
+	struct bnad_tx_info *tx_info =
+			(struct bnad_tx_info *)tcb->txq->tx->priv;
+
+	if (tx_info != &bnad->tx_info[0])
+		return;
+
+	clear_bit(BNAD_RF_TX_STARTED, &bnad->run_flags);
+	netif_stop_queue(bnad->netdev);
+	pr_info("bna: %s TX_STOPPED\n", bnad->netdev->name);
+}
+
+static void
+bnad_cb_tx_resume(struct bnad *bnad, struct bna_tcb *tcb)
+{
+	if (test_and_set_bit(BNAD_RF_TX_STARTED, &bnad->run_flags))
+		return;
+
+	if (netif_carrier_ok(bnad->netdev)) {
+		pr_info("bna: %s TX_STARTED\n", bnad->netdev->name);
+		netif_wake_queue(bnad->netdev);
+		BNAD_UPDATE_CTR(bnad, netif_queue_wakeup);
+	}
+}
+
+static void
+bnad_cb_tx_cleanup(struct bnad *bnad, struct bna_tcb *tcb)
+{
+	struct bnad_unmap_q *unmap_q = tcb->unmap_q;
+
+	if (!tcb || (!tcb->unmap_q))
+		return;
+
+	if (!unmap_q->unmap_array)
+		return;
+
+	if (test_and_set_bit(BNAD_TXQ_FREE_SENT, &tcb->flags))
+		return;
+
+	bnad_free_all_txbufs(bnad, tcb);
+
+	unmap_q->producer_index = 0;
+	unmap_q->consumer_index = 0;
+
+	smp_mb__before_clear_bit();
+	clear_bit(BNAD_TXQ_FREE_SENT, &tcb->flags);
+}
+
+static void
+bnad_cb_rx_cleanup(struct bnad *bnad,
+			struct bna_ccb *ccb)
+{
+	bnad_cq_cmpl_init(bnad, ccb);
+
+	bnad_free_rxbufs(bnad, ccb->rcb[0]);
+	clear_bit(BNAD_RXQ_STARTED, &ccb->rcb[0]->flags);
+
+	if (ccb->rcb[1]) {
+		bnad_free_rxbufs(bnad, ccb->rcb[1]);
+		clear_bit(BNAD_RXQ_STARTED, &ccb->rcb[1]->flags);
+	}
+}
+
+static void
+bnad_cb_rx_post(struct bnad *bnad, struct bna_rcb *rcb)
+{
+	struct bnad_unmap_q *unmap_q = rcb->unmap_q;
+
+	set_bit(BNAD_RXQ_STARTED, &rcb->flags);
+
+	/* Now allocate & post buffers for this RCB */
+	/* !!Allocation in callback context */
+	if (!test_and_set_bit(BNAD_RXQ_REFILL, &rcb->flags)) {
+		if (BNA_QE_FREE_CNT(unmap_q, unmap_q->q_depth)
+			 >> BNAD_RXQ_REFILL_THRESHOLD_SHIFT)
+			bnad_alloc_n_post_rxbufs(bnad, rcb);
+		smp_mb__before_clear_bit();
+		clear_bit(BNAD_RXQ_REFILL, &rcb->flags);
+	}
+}
+
+static void
+bnad_cb_rx_disabled(void *arg, struct bna_rx *rx,
+			enum bna_cb_status status)
+{
+	struct bnad *bnad = (struct bnad *)arg;
+
+	complete(&bnad->bnad_completions.rx_comp);
+}
+
+static void
+bnad_cb_rx_mcast_add(struct bnad *bnad, struct bna_rx *rx,
+				enum bna_cb_status status)
+{
+	bnad->bnad_completions.mcast_comp_status = status;
+	complete(&bnad->bnad_completions.mcast_comp);
+}
+
+void
+bnad_cb_stats_get(struct bnad *bnad, enum bna_cb_status status,
+		       struct bna_stats *stats)
+{
+	if (status == BNA_CB_SUCCESS)
+		BNAD_UPDATE_CTR(bnad, hw_stats_updates);
+
+	if (!netif_running(bnad->netdev) ||
+		!test_bit(BNAD_RF_STATS_TIMER_RUNNING, &bnad->run_flags))
+		return;
+
+	mod_timer(&bnad->stats_timer,
+		  jiffies + msecs_to_jiffies(BNAD_STATS_TIMER_FREQ));
+}
+
+void
+bnad_cb_stats_clr(struct bnad *bnad)
+{
+}
+
+/* Resource allocation, free functions */
+
+static void
+bnad_mem_free(struct bnad *bnad,
+	      struct bna_mem_info *mem_info)
+{
+	int i;
+	dma_addr_t dma_pa;
+
+	if (mem_info->mdl == NULL)
+		return;
+
+	for (i = 0; i < mem_info->num; i++) {
+		if (mem_info->mdl[i].kva != NULL) {
+			if (mem_info->mem_type == BNA_MEM_T_DMA) {
+				BNA_GET_DMA_ADDR(&(mem_info->mdl[i].dma),
+						dma_pa);
+				pci_free_consistent(bnad->pcidev,
+						mem_info->mdl[i].len,
+						mem_info->mdl[i].kva, dma_pa);
+			} else
+				kfree(mem_info->mdl[i].kva);
+		}
+	}
+	kfree(mem_info->mdl);
+	mem_info->mdl = NULL;
+}
+
+static int
+bnad_mem_alloc(struct bnad *bnad,
+	       struct bna_mem_info *mem_info)
+{
+	int i;
+	dma_addr_t dma_pa;
+
+	if ((mem_info->num == 0) || (mem_info->len == 0)) {
+		mem_info->mdl = NULL;
+		return 0;
+	}
+
+	mem_info->mdl = kcalloc(mem_info->num, sizeof(struct bna_mem_descr),
+				GFP_KERNEL);
+	if (mem_info->mdl == NULL)
+		return -ENOMEM;
+
+	if (mem_info->mem_type == BNA_MEM_T_DMA) {
+		for (i = 0; i < mem_info->num; i++) {
+			mem_info->mdl[i].len = mem_info->len;
+			mem_info->mdl[i].kva =
+				pci_alloc_consistent(bnad->pcidev,
+						mem_info->len, &dma_pa);
+
+			if (mem_info->mdl[i].kva == NULL)
+				goto err_return;
+
+			BNA_SET_DMA_ADDR(dma_pa,
+					 &(mem_info->mdl[i].dma));
+		}
+	} else {
+		for (i = 0; i < mem_info->num; i++) {
+			mem_info->mdl[i].len = mem_info->len;
+			mem_info->mdl[i].kva = kzalloc(mem_info->len,
+							GFP_KERNEL);
+			if (mem_info->mdl[i].kva == NULL)
+				goto err_return;
+		}
+	}
+
+	return 0;
+
+err_return:
+	bnad_mem_free(bnad, mem_info);
+	return -ENOMEM;
+}
+
+/* Free IRQ for Mailbox */
+static void
+bnad_mbox_irq_free(struct bnad *bnad,
+		   struct bna_intr_info *intr_info)
+{
+	int irq;
+	unsigned long flags;
+
+	if (intr_info->idl == NULL)
+		return;
+
+	spin_lock_irqsave(&bnad->bna_lock, flags);
+
+	bnad_disable_mbox_irq(bnad);
+
+	irq = BNAD_GET_MBOX_IRQ(bnad);
+	free_irq(irq, bnad->netdev);
+
+	spin_unlock_irqrestore(&bnad->bna_lock, flags);
+
+	kfree(intr_info->idl);
+}
+
+/*
+ * Allocates IRQ for Mailbox, but keep it disabled
+ * This will be enabled once we get the mbox enable callback
+ * from bna
+ */
+static int
+bnad_mbox_irq_alloc(struct bnad *bnad,
+		    struct bna_intr_info *intr_info)
+{
+	int 		err;
+	unsigned long 	flags;
+	u32	irq;
+	irq_handler_t 	irq_handler;
+
+	/* Mbox should use only 1 vector */
+
+	intr_info->idl = kzalloc(sizeof(*(intr_info->idl)), GFP_KERNEL);
+	if (!intr_info->idl)
+		return -ENOMEM;
+
+	spin_lock_irqsave(&bnad->bna_lock, flags);
+	if (bnad->cfg_flags & BNAD_CF_MSIX) {
+		irq_handler = (irq_handler_t)bnad_msix_mbox_handler;
+		irq = bnad->msix_table[bnad->msix_num - 1].vector;
+		flags = 0;
+		intr_info->intr_type = BNA_INTR_T_MSIX;
+		intr_info->idl[0].vector = bnad->msix_num - 1;
+	} else {
+		irq_handler = (irq_handler_t)bnad_isr;
+		irq = bnad->pcidev->irq;
+		flags = IRQF_SHARED;
+		intr_info->intr_type = BNA_INTR_T_INTX;
+		/* intr_info->idl.vector = 0 ? */
+	}
+	spin_unlock_irqrestore(&bnad->bna_lock, flags);
+
+	sprintf(bnad->mbox_irq_name, "%s", BNAD_NAME);
+
+	err = request_irq(irq, irq_handler, flags,
+			  bnad->mbox_irq_name, bnad->netdev);
+	if (err) {
+		kfree(intr_info->idl);
+		intr_info->idl = NULL;
+		return err;
+	}
+
+	spin_lock_irqsave(&bnad->bna_lock, flags);
+	bnad_disable_mbox_irq(bnad);
+	spin_unlock_irqrestore(&bnad->bna_lock, flags);
+	return 0;
+}
+
+static void
+bnad_txrx_irq_free(struct bnad *bnad, struct bna_intr_info *intr_info)
+{
+	kfree(intr_info->idl);
+	intr_info->idl = NULL;
+}
+
+/* Allocates Interrupt Descriptor List for MSIX/INT-X vectors */
+static int
+bnad_txrx_irq_alloc(struct bnad *bnad, enum bnad_intr_source src,
+		    uint txrx_id, struct bna_intr_info *intr_info)
+{
+	int i, vector_start = 0;
+	u32 cfg_flags;
+	unsigned long flags;
+
+	spin_lock_irqsave(&bnad->bna_lock, flags);
+	cfg_flags = bnad->cfg_flags;
+	spin_unlock_irqrestore(&bnad->bna_lock, flags);
+
+	if (cfg_flags & BNAD_CF_MSIX) {
+		intr_info->intr_type = BNA_INTR_T_MSIX;
+		intr_info->idl = kcalloc(intr_info->num,
+					sizeof(struct bna_intr_descr),
+					GFP_KERNEL);
+		if (!intr_info->idl)
+			return -ENOMEM;
+
+		switch (src) {
+		case BNAD_INTR_TX:
+			vector_start = txrx_id;
+			break;
+
+		case BNAD_INTR_RX:
+			vector_start = bnad->num_tx * bnad->num_txq_per_tx +
+					txrx_id;
+			break;
+
+		default:
+			BUG();
+		}
+
+		for (i = 0; i < intr_info->num; i++)
+			intr_info->idl[i].vector = vector_start + i;
+	} else {
+		intr_info->intr_type = BNA_INTR_T_INTX;
+		intr_info->num = 1;
+		intr_info->idl = kcalloc(intr_info->num,
+					sizeof(struct bna_intr_descr),
+					GFP_KERNEL);
+		if (!intr_info->idl)
+			return -ENOMEM;
+
+		switch (src) {
+		case BNAD_INTR_TX:
+			intr_info->idl[0].vector = 0x1; /* Bit mask : Tx IB */
+			break;
+
+		case BNAD_INTR_RX:
+			intr_info->idl[0].vector = 0x2; /* Bit mask : Rx IB */
+			break;
+		}
+	}
+	return 0;
+}
+
+/**
+ * NOTE: Should be called for MSIX only
+ * Unregisters Tx MSIX vector(s) from the kernel
+ */
+static void
+bnad_tx_msix_unregister(struct bnad *bnad, struct bnad_tx_info *tx_info,
+			int num_txqs)
+{
+	int i;
+	int vector_num;
+
+	for (i = 0; i < num_txqs; i++) {
+		if (tx_info->tcb[i] == NULL)
+			continue;
+
+		vector_num = tx_info->tcb[i]->intr_vector;
+		free_irq(bnad->msix_table[vector_num].vector, tx_info->tcb[i]);
+	}
+}
+
+/**
+ * NOTE: Should be called for MSIX only
+ * Registers Tx MSIX vector(s) and ISR(s), cookie with the kernel
+ */
+static int
+bnad_tx_msix_register(struct bnad *bnad, struct bnad_tx_info *tx_info,
+			uint tx_id, int num_txqs)
+{
+	int i;
+	int err;
+	int vector_num;
+
+	for (i = 0; i < num_txqs; i++) {
+		vector_num = tx_info->tcb[i]->intr_vector;
+		sprintf(tx_info->tcb[i]->name, "%s TXQ %d", bnad->netdev->name,
+				tx_id + tx_info->tcb[i]->id);
+		err = request_irq(bnad->msix_table[vector_num].vector,
+				  (irq_handler_t)bnad_msix_tx, 0,
+				  tx_info->tcb[i]->name,
+				  tx_info->tcb[i]);
+		if (err)
+			goto err_return;
+	}
+
+	return 0;
+
+err_return:
+	if (i > 0)
+		bnad_tx_msix_unregister(bnad, tx_info, (i - 1));
+	return -1;
+}
+
+/**
+ * NOTE: Should be called for MSIX only
+ * Unregisters Rx MSIX vector(s) from the kernel
+ */
+static void
+bnad_rx_msix_unregister(struct bnad *bnad, struct bnad_rx_info *rx_info,
+			int num_rxps)
+{
+	int i;
+	int vector_num;
+
+	for (i = 0; i < num_rxps; i++) {
+		if (rx_info->rx_ctrl[i].ccb == NULL)
+			continue;
+
+		vector_num = rx_info->rx_ctrl[i].ccb->intr_vector;
+		free_irq(bnad->msix_table[vector_num].vector,
+			 rx_info->rx_ctrl[i].ccb);
+	}
+}
+
+/**
+ * NOTE: Should be called for MSIX only
+ * Registers Tx MSIX vector(s) and ISR(s), cookie with the kernel
+ */
+static int
+bnad_rx_msix_register(struct bnad *bnad, struct bnad_rx_info *rx_info,
+			uint rx_id, int num_rxps)
+{
+	int i;
+	int err;
+	int vector_num;
+
+	for (i = 0; i < num_rxps; i++) {
+		vector_num = rx_info->rx_ctrl[i].ccb->intr_vector;
+		sprintf(rx_info->rx_ctrl[i].ccb->name, "%s CQ %d",
+			bnad->netdev->name,
+			rx_id + rx_info->rx_ctrl[i].ccb->id);
+		err = request_irq(bnad->msix_table[vector_num].vector,
+				  (irq_handler_t)bnad_msix_rx, 0,
+				  rx_info->rx_ctrl[i].ccb->name,
+				  rx_info->rx_ctrl[i].ccb);
+		if (err)
+			goto err_return;
+	}
+
+	return 0;
+
+err_return:
+	if (i > 0)
+		bnad_rx_msix_unregister(bnad, rx_info, (i - 1));
+	return -1;
+}
+
+/* Free Tx object Resources */
+static void
+bnad_tx_res_free(struct bnad *bnad, struct bna_res_info *res_info)
+{
+	int i;
+
+	for (i = 0; i < BNA_TX_RES_T_MAX; i++) {
+		if (res_info[i].res_type == BNA_RES_T_MEM)
+			bnad_mem_free(bnad, &res_info[i].res_u.mem_info);
+		else if (res_info[i].res_type == BNA_RES_T_INTR)
+			bnad_txrx_irq_free(bnad, &res_info[i].res_u.intr_info);
+	}
+}
+
+/* Allocates memory and interrupt resources for Tx object */
+static int
+bnad_tx_res_alloc(struct bnad *bnad, struct bna_res_info *res_info,
+		  uint tx_id)
+{
+	int i, err = 0;
+
+	for (i = 0; i < BNA_TX_RES_T_MAX; i++) {
+		if (res_info[i].res_type == BNA_RES_T_MEM)
+			err = bnad_mem_alloc(bnad,
+					&res_info[i].res_u.mem_info);
+		else if (res_info[i].res_type == BNA_RES_T_INTR)
+			err = bnad_txrx_irq_alloc(bnad, BNAD_INTR_TX, tx_id,
+					&res_info[i].res_u.intr_info);
+		if (err)
+			goto err_return;
+	}
+	return 0;
+
+err_return:
+	bnad_tx_res_free(bnad, res_info);
+	return err;
+}
+
+/* Free Rx object Resources */
+static void
+bnad_rx_res_free(struct bnad *bnad, struct bna_res_info *res_info)
+{
+	int i;
+
+	for (i = 0; i < BNA_RX_RES_T_MAX; i++) {
+		if (res_info[i].res_type == BNA_RES_T_MEM)
+			bnad_mem_free(bnad, &res_info[i].res_u.mem_info);
+		else if (res_info[i].res_type == BNA_RES_T_INTR)
+			bnad_txrx_irq_free(bnad, &res_info[i].res_u.intr_info);
+	}
+}
+
+/* Allocates memory and interrupt resources for Rx object */
+static int
+bnad_rx_res_alloc(struct bnad *bnad, struct bna_res_info *res_info,
+		  uint rx_id)
+{
+	int i, err = 0;
+
+	/* All memory needs to be allocated before setup_ccbs */
+	for (i = 0; i < BNA_RX_RES_T_MAX; i++) {
+		if (res_info[i].res_type == BNA_RES_T_MEM)
+			err = bnad_mem_alloc(bnad,
+					&res_info[i].res_u.mem_info);
+		else if (res_info[i].res_type == BNA_RES_T_INTR)
+			err = bnad_txrx_irq_alloc(bnad, BNAD_INTR_RX, rx_id,
+					&res_info[i].res_u.intr_info);
+		if (err)
+			goto err_return;
+	}
+	return 0;
+
+err_return:
+	bnad_rx_res_free(bnad, res_info);
+	return err;
+}
+
+/* Timer callbacks */
+/* a) IOC timer */
+static void
+bnad_ioc_timeout(unsigned long data)
+{
+	struct bnad *bnad = (struct bnad *)data;
+	unsigned long flags;
+
+	spin_lock_irqsave(&bnad->bna_lock, flags);
+	bfa_ioc_timeout((void *) &bnad->bna.device.ioc);
+	spin_unlock_irqrestore(&bnad->bna_lock, flags);
+}
+
+static void
+bnad_ioc_hb_check(unsigned long data)
+{
+	struct bnad *bnad = (struct bnad *)data;
+	unsigned long flags;
+
+	spin_lock_irqsave(&bnad->bna_lock, flags);
+	bfa_ioc_hb_check((void *) &bnad->bna.device.ioc);
+	spin_unlock_irqrestore(&bnad->bna_lock, flags);
+}
+
+static void
+bnad_ioc_sem_timeout(unsigned long data)
+{
+	struct bnad *bnad = (struct bnad *)data;
+	unsigned long flags;
+
+	spin_lock_irqsave(&bnad->bna_lock, flags);
+	bfa_ioc_sem_timeout((void *) &bnad->bna.device.ioc);
+	spin_unlock_irqrestore(&bnad->bna_lock, flags);
+}
+
+/*
+ * All timer routines use bnad->bna_lock to protect against
+ * the following race, which may occur in case of no locking:
+ * 	Time	CPU m  		CPU n
+ *	0       1 = test_bit
+ *	1			clear_bit
+ *	2			del_timer_sync
+ *	3	mod_timer
+ */
+
+/* b) Dynamic Interrupt Moderation Timer */
+static void
+bnad_dim_timeout(unsigned long data)
+{
+	struct bnad *bnad = (struct bnad *)data;
+	struct bnad_rx_info *rx_info;
+	struct bnad_rx_ctrl *rx_ctrl;
+	int i, j;
+	unsigned long flags;
+
+	if (!netif_carrier_ok(bnad->netdev))
+		return;
+
+	spin_lock_irqsave(&bnad->bna_lock, flags);
+	for (i = 0; i < bnad->num_rx; i++) {
+		rx_info = &bnad->rx_info[i];
+		if (!rx_info->rx)
+			continue;
+		for (j = 0; j < bnad->num_rxp_per_rx; j++) {
+			rx_ctrl = &rx_info->rx_ctrl[j];
+			if (!rx_ctrl->ccb)
+				continue;
+			bna_rx_dim_update(rx_ctrl->ccb);
+		}
+	}
+
+	/* Check for BNAD_CF_DIM_ENABLED, does not eleminate a race */
+	if (test_bit(BNAD_RF_DIM_TIMER_RUNNING, &bnad->run_flags))
+		mod_timer(&bnad->dim_timer,
+			  jiffies + msecs_to_jiffies(BNAD_DIM_TIMER_FREQ));
+	spin_unlock_irqrestore(&bnad->bna_lock, flags);
+}
+
+/* c)  Statistics Timer */
+static void
+bnad_stats_timeout(unsigned long data)
+{
+	struct bnad *bnad = (struct bnad *)data;
+	unsigned long flags;
+
+	if (!netif_running(bnad->netdev) ||
+		!test_bit(BNAD_RF_STATS_TIMER_RUNNING, &bnad->run_flags))
+		return;
+
+	spin_lock_irqsave(&bnad->bna_lock, flags);
+	bna_stats_get(&bnad->bna);
+	spin_unlock_irqrestore(&bnad->bna_lock, flags);
+}
+
+/*
+ * Set up timer for DIM
+ * Called with bnad->bna_lock held
+ */
+void
+bnad_dim_timer_start(struct bnad *bnad)
+{
+	if (bnad->cfg_flags & BNAD_CF_DIM_ENABLED &&
+	    !test_bit(BNAD_RF_DIM_TIMER_RUNNING, &bnad->run_flags)) {
+		setup_timer(&bnad->dim_timer, bnad_dim_timeout,
+			    (unsigned long)bnad);
+		set_bit(BNAD_RF_DIM_TIMER_RUNNING, &bnad->run_flags);
+		mod_timer(&bnad->dim_timer,
+			  jiffies + msecs_to_jiffies(BNAD_DIM_TIMER_FREQ));
+	}
+}
+
+/*
+ * Set up timer for statistics
+ * Called with mutex_lock(&bnad->conf_mutex) held
+ */
+static void
+bnad_stats_timer_start(struct bnad *bnad)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&bnad->bna_lock, flags);
+	if (!test_and_set_bit(BNAD_RF_STATS_TIMER_RUNNING, &bnad->run_flags)) {
+		setup_timer(&bnad->stats_timer, bnad_stats_timeout,
+			    (unsigned long)bnad);
+		mod_timer(&bnad->stats_timer,
+			  jiffies + msecs_to_jiffies(BNAD_STATS_TIMER_FREQ));
+	}
+	spin_unlock_irqrestore(&bnad->bna_lock, flags);
+
+}
+
+/*
+ * Stops the stats timer
+ * Called with mutex_lock(&bnad->conf_mutex) held
+ */
+static void
+bnad_stats_timer_stop(struct bnad *bnad)
+{
+	int to_del = 0;
+	unsigned long flags;
+
+	spin_lock_irqsave(&bnad->bna_lock, flags);
+	if (test_and_clear_bit(BNAD_RF_STATS_TIMER_RUNNING, &bnad->run_flags))
+		to_del = 1;
+	spin_unlock_irqrestore(&bnad->bna_lock, flags);
+	if (to_del)
+		del_timer_sync(&bnad->stats_timer);
+}
+
+/* Utilities */
+
+static void
+bnad_netdev_mc_list_get(struct net_device *netdev, u8 *mc_list)
+{
+	int i = 1; /* Index 0 has broadcast address */
+	struct netdev_hw_addr *mc_addr;
+
+	netdev_for_each_mc_addr(mc_addr, netdev) {
+		memcpy(&mc_list[i * ETH_ALEN], &mc_addr->addr[0],
+							ETH_ALEN);
+		i++;
+	}
+}
+
+static int
+bnad_napi_poll_rx(struct napi_struct *napi, int budget)
+{
+	struct bnad_rx_ctrl *rx_ctrl =
+		container_of(napi, struct bnad_rx_ctrl, napi);
+	struct bna_ccb *ccb;
+	struct bnad *bnad;
+	int rcvd = 0;
+
+	ccb = rx_ctrl->ccb;
+
+	bnad = ccb->bnad;
+
+	if (!netif_carrier_ok(bnad->netdev))
+		goto poll_exit;
+
+	rcvd = bnad_poll_cq(bnad, ccb, budget);
+	if (rcvd == budget)
+		return rcvd;
+
+poll_exit:
+	napi_complete((napi));
+
+	BNAD_UPDATE_CTR(bnad, netif_rx_complete);
+
+	bnad_enable_rx_irq(bnad, ccb);
+	return rcvd;
+}
+
+static int
+bnad_napi_poll_txrx(struct napi_struct *napi, int budget)
+{
+	struct bnad_rx_ctrl *rx_ctrl =
+		container_of(napi, struct bnad_rx_ctrl, napi);
+	struct bna_ccb *ccb;
+	struct bnad *bnad;
+	int 			rcvd = 0;
+	int			i, j;
+
+	ccb = rx_ctrl->ccb;
+
+	bnad = ccb->bnad;
+
+	if (!netif_carrier_ok(bnad->netdev))
+		goto poll_exit;
+
+	/* Handle Tx Completions, if any */
+	for (i = 0; i < bnad->num_tx; i++) {
+		for (j = 0; j < bnad->num_txq_per_tx; j++)
+			bnad_tx(bnad, bnad->tx_info[i].tcb[j]);
+	}
+
+	/* Handle Rx Completions */
+	rcvd = bnad_poll_cq(bnad, ccb, budget);
+	if (rcvd == budget)
+		return rcvd;
+poll_exit:
+	napi_complete((napi));
+
+	BNAD_UPDATE_CTR(bnad, netif_rx_complete);
+
+	bnad_enable_txrx_irqs(bnad);
+	return rcvd;
+}
+
+static void
+bnad_napi_enable(struct bnad *bnad, u32 rx_id)
+{
+	int (*napi_poll) (struct napi_struct *, int);
+	struct bnad_rx_ctrl *rx_ctrl;
+	int i;
+	unsigned long flags;
+
+	spin_lock_irqsave(&bnad->bna_lock, flags);
+	if (bnad->cfg_flags & BNAD_CF_MSIX)
+		napi_poll = bnad_napi_poll_rx;
+	else
+		napi_poll = bnad_napi_poll_txrx;
+	spin_unlock_irqrestore(&bnad->bna_lock, flags);
+
+	/* Initialize & enable NAPI */
+	for (i = 0; i <	bnad->num_rxp_per_rx; i++) {
+		rx_ctrl = &bnad->rx_info[rx_id].rx_ctrl[i];
+		netif_napi_add(bnad->netdev, &rx_ctrl->napi,
+			       napi_poll, 64);
+		napi_enable(&rx_ctrl->napi);
+	}
+}
+
+static void
+bnad_napi_disable(struct bnad *bnad, u32 rx_id)
+{
+	int i;
+
+	/* First disable and then clean up */
+	for (i = 0; i < bnad->num_rxp_per_rx; i++) {
+		napi_disable(&bnad->rx_info[rx_id].rx_ctrl[i].napi);
+		netif_napi_del(&bnad->rx_info[rx_id].rx_ctrl[i].napi);
+	}
+}
+
+/* Should be held with conf_lock held */
+void
+bnad_cleanup_tx(struct bnad *bnad, uint tx_id)
+{
+	struct bnad_tx_info *tx_info = &bnad->tx_info[tx_id];
+	struct bna_res_info *res_info = &bnad->tx_res_info[tx_id].res_info[0];
+	unsigned long flags;
+
+	if (!tx_info->tx)
+		return;
+
+	init_completion(&bnad->bnad_completions.tx_comp);
+	spin_lock_irqsave(&bnad->bna_lock, flags);
+	bna_tx_disable(tx_info->tx, BNA_HARD_CLEANUP, bnad_cb_tx_disabled);
+	spin_unlock_irqrestore(&bnad->bna_lock, flags);
+	wait_for_completion(&bnad->bnad_completions.tx_comp);
+
+	if (tx_info->tcb[0]->intr_type == BNA_INTR_T_MSIX)
+		bnad_tx_msix_unregister(bnad, tx_info,
+			bnad->num_txq_per_tx);
+
+	spin_lock_irqsave(&bnad->bna_lock, flags);
+	bna_tx_destroy(tx_info->tx);
+	spin_unlock_irqrestore(&bnad->bna_lock, flags);
+
+	tx_info->tx = NULL;
+
+	if (0 == tx_id)
+		tasklet_kill(&bnad->tx_free_tasklet);
+
+	bnad_tx_res_free(bnad, res_info);
+}
+
+/* Should be held with conf_lock held */
+int
+bnad_setup_tx(struct bnad *bnad, uint tx_id)
+{
+	int err;
+	struct bnad_tx_info *tx_info = &bnad->tx_info[tx_id];
+	struct bna_res_info *res_info = &bnad->tx_res_info[tx_id].res_info[0];
+	struct bna_intr_info *intr_info =
+			&res_info[BNA_TX_RES_INTR_T_TXCMPL].res_u.intr_info;
+	struct bna_tx_config *tx_config = &bnad->tx_config[tx_id];
+	struct bna_tx_event_cbfn tx_cbfn;
+	struct bna_tx *tx;
+	unsigned long flags;
+
+	/* Initialize the Tx object configuration */
+	tx_config->num_txq = bnad->num_txq_per_tx;
+	tx_config->txq_depth = bnad->txq_depth;
+	tx_config->tx_type = BNA_TX_T_REGULAR;
+
+	/* Initialize the tx event handlers */
+	tx_cbfn.tcb_setup_cbfn = bnad_cb_tcb_setup;
+	tx_cbfn.tcb_destroy_cbfn = bnad_cb_tcb_destroy;
+	tx_cbfn.tx_stall_cbfn = bnad_cb_tx_stall;
+	tx_cbfn.tx_resume_cbfn = bnad_cb_tx_resume;
+	tx_cbfn.tx_cleanup_cbfn = bnad_cb_tx_cleanup;
+
+	/* Get BNA's resource requirement for one tx object */
+	spin_lock_irqsave(&bnad->bna_lock, flags);
+	bna_tx_res_req(bnad->num_txq_per_tx,
+		bnad->txq_depth, res_info);
+	spin_unlock_irqrestore(&bnad->bna_lock, flags);
+
+	/* Fill Unmap Q memory requirements */
+	BNAD_FILL_UNMAPQ_MEM_REQ(
+			&res_info[BNA_TX_RES_MEM_T_UNMAPQ],
+			bnad->num_txq_per_tx,
+			BNAD_TX_UNMAPQ_DEPTH);
+
+	/* Allocate resources */
+	err = bnad_tx_res_alloc(bnad, res_info, tx_id);
+	if (err)
+		return err;
+
+	/* Ask BNA to create one Tx object, supplying required resources */
+	spin_lock_irqsave(&bnad->bna_lock, flags);
+	tx = bna_tx_create(&bnad->bna, bnad, tx_config, &tx_cbfn, res_info,
+			tx_info);
+	spin_unlock_irqrestore(&bnad->bna_lock, flags);
+	if (!tx)
+		goto err_return;
+	tx_info->tx = tx;
+
+	/* Register ISR for the Tx object */
+	if (intr_info->intr_type == BNA_INTR_T_MSIX) {
+		err = bnad_tx_msix_register(bnad, tx_info,
+			tx_id, bnad->num_txq_per_tx);
+		if (err)
+			goto err_return;
+	}
+
+	spin_lock_irqsave(&bnad->bna_lock, flags);
+	bna_tx_enable(tx);
+	spin_unlock_irqrestore(&bnad->bna_lock, flags);
+
+	return 0;
+
+err_return:
+	bnad_tx_res_free(bnad, res_info);
+	return err;
+}
+
+/* Setup the rx config for bna_rx_create */
+/* bnad decides the configuration */
+static void
+bnad_init_rx_config(struct bnad *bnad, struct bna_rx_config *rx_config)
+{
+	rx_config->rx_type = BNA_RX_T_REGULAR;
+	rx_config->num_paths = bnad->num_rxp_per_rx;
+
+	if (bnad->num_rxp_per_rx > 1) {
+		rx_config->rss_status = BNA_STATUS_T_ENABLED;
+		rx_config->rss_config.hash_type =
+				(BFI_RSS_T_V4_TCP |
+				 BFI_RSS_T_V6_TCP |
+				 BFI_RSS_T_V4_IP  |
+				 BFI_RSS_T_V6_IP);
+		rx_config->rss_config.hash_mask =
+				bnad->num_rxp_per_rx - 1;
+		get_random_bytes(rx_config->rss_config.toeplitz_hash_key,
+			sizeof(rx_config->rss_config.toeplitz_hash_key));
+	} else {
+		rx_config->rss_status = BNA_STATUS_T_DISABLED;
+		memset(&rx_config->rss_config, 0,
+		       sizeof(rx_config->rss_config));
+	}
+	rx_config->rxp_type = BNA_RXP_SLR;
+	rx_config->q_depth = bnad->rxq_depth;
+
+	rx_config->small_buff_size = BFI_SMALL_RXBUF_SIZE;
+
+	rx_config->vlan_strip_status = BNA_STATUS_T_ENABLED;
+}
+
+/* Called with mutex_lock(&bnad->conf_mutex) held */
+void
+bnad_cleanup_rx(struct bnad *bnad, uint rx_id)
+{
+	struct bnad_rx_info *rx_info = &bnad->rx_info[rx_id];
+	struct bna_rx_config *rx_config = &bnad->rx_config[rx_id];
+	struct bna_res_info *res_info = &bnad->rx_res_info[rx_id].res_info[0];
+	unsigned long flags;
+	int dim_timer_del = 0;
+
+	if (!rx_info->rx)
+		return;
+
+	if (0 == rx_id) {
+		spin_lock_irqsave(&bnad->bna_lock, flags);
+		dim_timer_del = bnad_dim_timer_running(bnad);
+		if (dim_timer_del)
+			clear_bit(BNAD_RF_DIM_TIMER_RUNNING, &bnad->run_flags);
+		spin_unlock_irqrestore(&bnad->bna_lock, flags);
+		if (dim_timer_del)
+			del_timer_sync(&bnad->dim_timer);
+	}
+
+	bnad_napi_disable(bnad, rx_id);
+
+	init_completion(&bnad->bnad_completions.rx_comp);
+	spin_lock_irqsave(&bnad->bna_lock, flags);
+	bna_rx_disable(rx_info->rx, BNA_HARD_CLEANUP, bnad_cb_rx_disabled);
+	spin_unlock_irqrestore(&bnad->bna_lock, flags);
+	wait_for_completion(&bnad->bnad_completions.rx_comp);
+
+	if (rx_info->rx_ctrl[0].ccb->intr_type == BNA_INTR_T_MSIX)
+		bnad_rx_msix_unregister(bnad, rx_info, rx_config->num_paths);
+
+	spin_lock_irqsave(&bnad->bna_lock, flags);
+	bna_rx_destroy(rx_info->rx);
+	spin_unlock_irqrestore(&bnad->bna_lock, flags);
+
+	rx_info->rx = NULL;
+
+	bnad_rx_res_free(bnad, res_info);
+}
+
+/* Called with mutex_lock(&bnad->conf_mutex) held */
+int
+bnad_setup_rx(struct bnad *bnad, uint rx_id)
+{
+	int err;
+	struct bnad_rx_info *rx_info = &bnad->rx_info[rx_id];
+	struct bna_res_info *res_info = &bnad->rx_res_info[rx_id].res_info[0];
+	struct bna_intr_info *intr_info =
+			&res_info[BNA_RX_RES_T_INTR].res_u.intr_info;
+	struct bna_rx_config *rx_config = &bnad->rx_config[rx_id];
+	struct bna_rx_event_cbfn rx_cbfn;
+	struct bna_rx *rx;
+	unsigned long flags;
+
+	/* Initialize the Rx object configuration */
+	bnad_init_rx_config(bnad, rx_config);
+
+	/* Initialize the Rx event handlers */
+	rx_cbfn.rcb_setup_cbfn = bnad_cb_rcb_setup;
+	rx_cbfn.rcb_destroy_cbfn = NULL;
+	rx_cbfn.ccb_setup_cbfn = bnad_cb_ccb_setup;
+	rx_cbfn.ccb_destroy_cbfn = bnad_cb_ccb_destroy;
+	rx_cbfn.rx_cleanup_cbfn = bnad_cb_rx_cleanup;
+	rx_cbfn.rx_post_cbfn = bnad_cb_rx_post;
+
+	/* Get BNA's resource requirement for one Rx object */
+	spin_lock_irqsave(&bnad->bna_lock, flags);
+	bna_rx_res_req(rx_config, res_info);
+	spin_unlock_irqrestore(&bnad->bna_lock, flags);
+
+	/* Fill Unmap Q memory requirements */
+	BNAD_FILL_UNMAPQ_MEM_REQ(
+			&res_info[BNA_RX_RES_MEM_T_UNMAPQ],
+			rx_config->num_paths +
+			((rx_config->rxp_type == BNA_RXP_SINGLE) ? 0 :
+				rx_config->num_paths), BNAD_RX_UNMAPQ_DEPTH);
+
+	/* Allocate resource */
+	err = bnad_rx_res_alloc(bnad, res_info, rx_id);
+	if (err)
+		return err;
+
+	/* Ask BNA to create one Rx object, supplying required resources */
+	spin_lock_irqsave(&bnad->bna_lock, flags);
+	rx = bna_rx_create(&bnad->bna, bnad, rx_config, &rx_cbfn, res_info,
+			rx_info);
+	spin_unlock_irqrestore(&bnad->bna_lock, flags);
+	if (!rx)
+		goto err_return;
+	rx_info->rx = rx;
+
+	/* Register ISR for the Rx object */
+	if (intr_info->intr_type == BNA_INTR_T_MSIX) {
+		err = bnad_rx_msix_register(bnad, rx_info, rx_id,
+						rx_config->num_paths);
+		if (err)
+			goto err_return;
+	}
+
+	/* Enable NAPI */
+	bnad_napi_enable(bnad, rx_id);
+
+	spin_lock_irqsave(&bnad->bna_lock, flags);
+	if (0 == rx_id) {
+		/* Set up Dynamic Interrupt Moderation Vector */
+		if (bnad->cfg_flags & BNAD_CF_DIM_ENABLED)
+			bna_rx_dim_reconfig(&bnad->bna, bna_napi_dim_vector);
+
+		/* Enable VLAN filtering only on the default Rx */
+		bna_rx_vlanfilter_enable(rx);
+
+		/* Start the DIM timer */
+		bnad_dim_timer_start(bnad);
+	}
+
+	bna_rx_enable(rx);
+	spin_unlock_irqrestore(&bnad->bna_lock, flags);
+
+	return 0;
+
+err_return:
+	bnad_cleanup_rx(bnad, rx_id);
+	return err;
+}
+
+/* Called with conf_lock & bnad->bna_lock held */
+void
+bnad_tx_coalescing_timeo_set(struct bnad *bnad)
+{
+	struct bnad_tx_info *tx_info;
+
+	tx_info = &bnad->tx_info[0];
+	if (!tx_info->tx)
+		return;
+
+	bna_tx_coalescing_timeo_set(tx_info->tx, bnad->tx_coalescing_timeo);
+}
+
+/* Called with conf_lock & bnad->bna_lock held */
+void
+bnad_rx_coalescing_timeo_set(struct bnad *bnad)
+{
+	struct bnad_rx_info *rx_info;
+	int 	i;
+
+	for (i = 0; i < bnad->num_rx; i++) {
+		rx_info = &bnad->rx_info[i];
+		if (!rx_info->rx)
+			continue;
+		bna_rx_coalescing_timeo_set(rx_info->rx,
+				bnad->rx_coalescing_timeo);
+	}
+}
+
+/*
+ * Called with bnad->bna_lock held
+ */
+static int
+bnad_mac_addr_set_locked(struct bnad *bnad, u8 *mac_addr)
+{
+	int ret;
+
+	if (!is_valid_ether_addr(mac_addr))
+		return -EADDRNOTAVAIL;
+
+	/* If datapath is down, pretend everything went through */
+	if (!bnad->rx_info[0].rx)
+		return 0;
+
+	ret = bna_rx_ucast_set(bnad->rx_info[0].rx, mac_addr, NULL);
+	if (ret != BNA_CB_SUCCESS)
+		return -EADDRNOTAVAIL;
+
+	return 0;
+}
+
+/* Should be called with conf_lock held */
+static int
+bnad_enable_default_bcast(struct bnad *bnad)
+{
+	struct bnad_rx_info *rx_info = &bnad->rx_info[0];
+	int ret;
+	unsigned long flags;
+
+	init_completion(&bnad->bnad_completions.mcast_comp);
+
+	spin_lock_irqsave(&bnad->bna_lock, flags);
+	ret = bna_rx_mcast_add(rx_info->rx, (u8 *)bnad_bcast_addr,
+				bnad_cb_rx_mcast_add);
+	spin_unlock_irqrestore(&bnad->bna_lock, flags);
+
+	if (ret == BNA_CB_SUCCESS)
+		wait_for_completion(&bnad->bnad_completions.mcast_comp);
+	else
+		return -ENODEV;
+
+	if (bnad->bnad_completions.mcast_comp_status != BNA_CB_SUCCESS)
+		return -ENODEV;
+
+	return 0;
+}
+
+/* Statistics utilities */
+void
+bnad_netdev_qstats_fill(struct bnad *bnad)
+{
+	struct net_device_stats *net_stats = &bnad->net_stats;
+	int i, j;
+
+	for (i = 0; i < bnad->num_rx; i++) {
+		for (j = 0; j < bnad->num_rxp_per_rx; j++) {
+			if (bnad->rx_info[i].rx_ctrl[j].ccb) {
+				net_stats->rx_packets += bnad->rx_info[i].
+				rx_ctrl[j].ccb->rcb[0]->rxq->rx_packets;
+				net_stats->rx_bytes += bnad->rx_info[i].
+					rx_ctrl[j].ccb->rcb[0]->rxq->rx_bytes;
+				if (bnad->rx_info[i].rx_ctrl[j].ccb->rcb[1] &&
+					bnad->rx_info[i].rx_ctrl[j].ccb->
+					rcb[1]->rxq) {
+					net_stats->rx_packets +=
+						bnad->rx_info[i].rx_ctrl[j].
+						ccb->rcb[1]->rxq->rx_packets;
+					net_stats->rx_bytes +=
+						bnad->rx_info[i].rx_ctrl[j].
+						ccb->rcb[1]->rxq->rx_bytes;
+				}
+			}
+		}
+	}
+	for (i = 0; i < bnad->num_tx; i++) {
+		for (j = 0; j < bnad->num_txq_per_tx; j++) {
+			if (bnad->tx_info[i].tcb[j]) {
+				net_stats->tx_packets +=
+				bnad->tx_info[i].tcb[j]->txq->tx_packets;
+				net_stats->tx_bytes +=
+					bnad->tx_info[i].tcb[j]->txq->tx_bytes;
+			}
+		}
+	}
+}
+
+/*
+ * Must be called with the bna_lock held.
+ */
+void
+bnad_netdev_hwstats_fill(struct bnad *bnad)
+{
+	struct bfi_ll_stats_mac *mac_stats;
+	struct net_device_stats *net_stats = &bnad->net_stats;
+	u64 bmap;
+	int i;
+
+	mac_stats = &bnad->stats.bna_stats->hw_stats->mac_stats;
+	net_stats->rx_errors =
+		mac_stats->rx_fcs_error + mac_stats->rx_alignment_error +
+		mac_stats->rx_frame_length_error + mac_stats->rx_code_error +
+		mac_stats->rx_undersize;
+	net_stats->tx_errors = mac_stats->tx_fcs_error +
+					mac_stats->tx_undersize;
+	net_stats->rx_dropped = mac_stats->rx_drop;
+	net_stats->tx_dropped = mac_stats->tx_drop;
+	net_stats->multicast = mac_stats->rx_multicast;
+	net_stats->collisions = mac_stats->tx_total_collision;
+
+	net_stats->rx_length_errors = mac_stats->rx_frame_length_error;
+
+	/* receive ring buffer overflow  ?? */
+
+	net_stats->rx_crc_errors = mac_stats->rx_fcs_error;
+	net_stats->rx_frame_errors = mac_stats->rx_alignment_error;
+	/* recv'r fifo overrun */
+	bmap = (u64)bnad->stats.bna_stats->rxf_bmap[0] |
+		((u64)bnad->stats.bna_stats->rxf_bmap[1] << 32);
+	for (i = 0; bmap && (i < BFI_LL_RXF_ID_MAX); i++) {
+		if (bmap & 1) {
+			net_stats->rx_fifo_errors =
+				bnad->stats.bna_stats->
+					hw_stats->rxf_stats[i].frame_drops;
+			break;
+		}
+		bmap >>= 1;
+	}
+}
+
+static void
+bnad_mbox_irq_sync(struct bnad *bnad)
+{
+	u32 irq;
+	unsigned long flags;
+
+	spin_lock_irqsave(&bnad->bna_lock, flags);
+	if (bnad->cfg_flags & BNAD_CF_MSIX)
+		irq = bnad->msix_table[bnad->msix_num - 1].vector;
+	else
+		irq = bnad->pcidev->irq;
+	spin_unlock_irqrestore(&bnad->bna_lock, flags);
+
+	synchronize_irq(irq);
+}
+
+/* Utility used by bnad_start_xmit, for doing TSO */
+static int
+bnad_tso_prepare(struct bnad *bnad, struct sk_buff *skb)
+{
+	int err;
+
+	/* SKB_GSO_TCPV4 and SKB_GSO_TCPV6 is defined since 2.6.18. */
+	BUG_ON(!(skb_shinfo(skb)->gso_type == SKB_GSO_TCPV4 ||
+		   skb_shinfo(skb)->gso_type == SKB_GSO_TCPV6));
+	if (skb_header_cloned(skb)) {
+		err = pskb_expand_head(skb, 0, 0, GFP_ATOMIC);
+		if (err) {
+			BNAD_UPDATE_CTR(bnad, tso_err);
+			return err;
+		}
+	}
+
+	/*
+	 * For TSO, the TCP checksum field is seeded with pseudo-header sum
+	 * excluding the length field.
+	 */
+	if (skb->protocol == htons(ETH_P_IP)) {
+		struct iphdr *iph = ip_hdr(skb);
+
+		/* Do we really need these? */
+		iph->tot_len = 0;
+		iph->check = 0;
+
+		tcp_hdr(skb)->check =
+			~csum_tcpudp_magic(iph->saddr, iph->daddr, 0,
+					   IPPROTO_TCP, 0);
+		BNAD_UPDATE_CTR(bnad, tso4);
+	} else {
+		struct ipv6hdr *ipv6h = ipv6_hdr(skb);
+
+		BUG_ON(!(skb->protocol == htons(ETH_P_IPV6)));
+		ipv6h->payload_len = 0;
+		tcp_hdr(skb)->check =
+			~csum_ipv6_magic(&ipv6h->saddr, &ipv6h->daddr, 0,
+					 IPPROTO_TCP, 0);
+		BNAD_UPDATE_CTR(bnad, tso6);
+	}
+
+	return 0;
+}
+
+/*
+ * Initialize Q numbers depending on Rx Paths
+ * Called with bnad->bna_lock held, because of cfg_flags
+ * access.
+ */
+static void
+bnad_q_num_init(struct bnad *bnad)
+{
+	int rxps;
+
+	rxps = min((uint)num_online_cpus(),
+			(uint)(BNAD_MAX_RXS * BNAD_MAX_RXPS_PER_RX));
+
+	if (!(bnad->cfg_flags & BNAD_CF_MSIX))
+		rxps = 1;	/* INTx */
+
+	bnad->num_rx = 1;
+	bnad->num_tx = 1;
+	bnad->num_rxp_per_rx = rxps;
+	bnad->num_txq_per_tx = BNAD_TXQ_NUM;
+}
+
+/*
+ * Adjusts the Q numbers, given a number of msix vectors
+ * Give preference to RSS as opposed to Tx priority Queues,
+ * in such a case, just use 1 Tx Q
+ * Called with bnad->bna_lock held b'cos of cfg_flags access
+ */
+static void
+bnad_q_num_adjust(struct bnad *bnad, int msix_vectors)
+{
+	bnad->num_txq_per_tx = 1;
+	if ((msix_vectors >= (bnad->num_tx * bnad->num_txq_per_tx)  +
+	     bnad_rxqs_per_cq + BNAD_MAILBOX_MSIX_VECTORS) &&
+	    (bnad->cfg_flags & BNAD_CF_MSIX)) {
+		bnad->num_rxp_per_rx = msix_vectors -
+			(bnad->num_tx * bnad->num_txq_per_tx) -
+			BNAD_MAILBOX_MSIX_VECTORS;
+	} else
+		bnad->num_rxp_per_rx = 1;
+}
+
+static void
+bnad_set_netdev_perm_addr(struct bnad *bnad)
+{
+	struct net_device *netdev = bnad->netdev;
+
+	memcpy(netdev->perm_addr, &bnad->perm_addr, netdev->addr_len);
+	if (is_zero_ether_addr(netdev->dev_addr))
+		memcpy(netdev->dev_addr, &bnad->perm_addr, netdev->addr_len);
+}
+
+/* Enable / disable device */
+static void
+bnad_device_disable(struct bnad *bnad)
+{
+	unsigned long flags;
+
+	init_completion(&bnad->bnad_completions.ioc_comp);
+
+	spin_lock_irqsave(&bnad->bna_lock, flags);
+	bna_device_disable(&bnad->bna.device, BNA_HARD_CLEANUP);
+	spin_unlock_irqrestore(&bnad->bna_lock, flags);
+
+	wait_for_completion(&bnad->bnad_completions.ioc_comp);
+
+}
+
+static int
+bnad_device_enable(struct bnad *bnad)
+{
+	int err = 0;
+	unsigned long flags;
+
+	init_completion(&bnad->bnad_completions.ioc_comp);
+
+	spin_lock_irqsave(&bnad->bna_lock, flags);
+	bna_device_enable(&bnad->bna.device);
+	spin_unlock_irqrestore(&bnad->bna_lock, flags);
+
+	wait_for_completion(&bnad->bnad_completions.ioc_comp);
+
+	if (bnad->bnad_completions.ioc_comp_status)
+		err = bnad->bnad_completions.ioc_comp_status;
+
+	return err;
+}
+
+/* Free BNA resources */
+static void
+bnad_res_free(struct bnad *bnad)
+{
+	int i;
+	struct bna_res_info *res_info = &bnad->res_info[0];
+
+	for (i = 0; i < BNA_RES_T_MAX; i++) {
+		if (res_info[i].res_type == BNA_RES_T_MEM)
+			bnad_mem_free(bnad, &res_info[i].res_u.mem_info);
+		else
+			bnad_mbox_irq_free(bnad, &res_info[i].res_u.intr_info);
+	}
+}
+
+/* Allocates memory and interrupt resources for BNA */
+static int
+bnad_res_alloc(struct bnad *bnad)
+{
+	int i, err;
+	struct bna_res_info *res_info = &bnad->res_info[0];
+
+	for (i = 0; i < BNA_RES_T_MAX; i++) {
+		if (res_info[i].res_type == BNA_RES_T_MEM)
+			err = bnad_mem_alloc(bnad, &res_info[i].res_u.mem_info);
+		else
+			err = bnad_mbox_irq_alloc(bnad,
+						  &res_info[i].res_u.intr_info);
+		if (err)
+			goto err_return;
+	}
+	return 0;
+
+err_return:
+	bnad_res_free(bnad);
+	return err;
+}
+
+/* Interrupt enable / disable */
+static void
+bnad_enable_msix(struct bnad *bnad)
+{
+	int i, ret;
+	u32 tot_msix_num;
+	unsigned long flags;
+
+	spin_lock_irqsave(&bnad->bna_lock, flags);
+	if (!(bnad->cfg_flags & BNAD_CF_MSIX)) {
+		spin_unlock_irqrestore(&bnad->bna_lock, flags);
+		return;
+	}
+	spin_unlock_irqrestore(&bnad->bna_lock, flags);
+
+	if (bnad->msix_table)
+		return;
+
+	tot_msix_num = bnad->msix_num + bnad->msix_diag_num;
+
+	bnad->msix_table =
+		kcalloc(tot_msix_num, sizeof(struct msix_entry), GFP_KERNEL);
+
+	if (!bnad->msix_table)
+		goto intx_mode;
+
+	for (i = 0; i < tot_msix_num; i++)
+		bnad->msix_table[i].entry = i;
+
+	ret = pci_enable_msix(bnad->pcidev, bnad->msix_table, tot_msix_num);
+	if (ret > 0) {
+		/* Not enough MSI-X vectors. */
+
+		spin_lock_irqsave(&bnad->bna_lock, flags);
+		/* ret = #of vectors that we got */
+		bnad_q_num_adjust(bnad, ret);
+		spin_unlock_irqrestore(&bnad->bna_lock, flags);
+
+		bnad->msix_num = (bnad->num_tx * bnad->num_txq_per_tx)
+			+ (bnad->num_rx
+			* bnad->num_rxp_per_rx) +
+			 BNAD_MAILBOX_MSIX_VECTORS;
+		tot_msix_num = bnad->msix_num + bnad->msix_diag_num;
+
+		/* Try once more with adjusted numbers */
+		/* If this fails, fall back to INTx */
+		ret = pci_enable_msix(bnad->pcidev, bnad->msix_table,
+				      tot_msix_num);
+		if (ret)
+			goto intx_mode;
+
+	} else if (ret < 0)
+		goto intx_mode;
+	return;
+
+intx_mode:
+
+	kfree(bnad->msix_table);
+	bnad->msix_table = NULL;
+	bnad->msix_num = 0;
+	bnad->msix_diag_num = 0;
+	spin_lock_irqsave(&bnad->bna_lock, flags);
+	bnad->cfg_flags &= ~BNAD_CF_MSIX;
+	bnad_q_num_init(bnad);
+	spin_unlock_irqrestore(&bnad->bna_lock, flags);
+}
+
+static void
+bnad_disable_msix(struct bnad *bnad)
+{
+	u32 cfg_flags;
+	unsigned long flags;
+
+	spin_lock_irqsave(&bnad->bna_lock, flags);
+	cfg_flags = bnad->cfg_flags;
+	if (bnad->cfg_flags & BNAD_CF_MSIX)
+		bnad->cfg_flags &= ~BNAD_CF_MSIX;
+	spin_unlock_irqrestore(&bnad->bna_lock, flags);
+
+	if (cfg_flags & BNAD_CF_MSIX) {
+		pci_disable_msix(bnad->pcidev);
+		kfree(bnad->msix_table);
+		bnad->msix_table = NULL;
+	}
+}
+
+/* Netdev entry points */
+static int
+bnad_open(struct net_device *netdev)
+{
+	int err;
+	struct bnad *bnad = netdev_priv(netdev);
+	struct bna_pause_config pause_config;
+	int mtu;
+	unsigned long flags;
+
+	mutex_lock(&bnad->conf_mutex);
+
+	/* Tx */
+	err = bnad_setup_tx(bnad, 0);
+	if (err)
+		goto err_return;
+
+	/* Rx */
+	err = bnad_setup_rx(bnad, 0);
+	if (err)
+		goto cleanup_tx;
+
+	/* Port */
+	pause_config.tx_pause = 0;
+	pause_config.rx_pause = 0;
+
+	mtu = ETH_HLEN + bnad->netdev->mtu + ETH_FCS_LEN;
+
+	spin_lock_irqsave(&bnad->bna_lock, flags);
+	bna_port_mtu_set(&bnad->bna.port, mtu, NULL);
+	bna_port_pause_config(&bnad->bna.port, &pause_config, NULL);
+	bna_port_enable(&bnad->bna.port);
+	spin_unlock_irqrestore(&bnad->bna_lock, flags);
+
+	/* Enable broadcast */
+	bnad_enable_default_bcast(bnad);
+
+	/* Set the UCAST address */
+	spin_lock_irqsave(&bnad->bna_lock, flags);
+	bnad_mac_addr_set_locked(bnad, netdev->dev_addr);
+	spin_unlock_irqrestore(&bnad->bna_lock, flags);
+
+	/* Start the stats timer */
+	bnad_stats_timer_start(bnad);
+
+	mutex_unlock(&bnad->conf_mutex);
+
+	return 0;
+
+cleanup_tx:
+	bnad_cleanup_tx(bnad, 0);
+
+err_return:
+	mutex_unlock(&bnad->conf_mutex);
+	return err;
+}
+
+static int
+bnad_stop(struct net_device *netdev)
+{
+	struct bnad *bnad = netdev_priv(netdev);
+	unsigned long flags;
+
+	mutex_lock(&bnad->conf_mutex);
+
+	/* Stop the stats timer */
+	bnad_stats_timer_stop(bnad);
+
+	init_completion(&bnad->bnad_completions.port_comp);
+
+	spin_lock_irqsave(&bnad->bna_lock, flags);
+	bna_port_disable(&bnad->bna.port, BNA_HARD_CLEANUP,
+			bnad_cb_port_disabled);
+	spin_unlock_irqrestore(&bnad->bna_lock, flags);
+
+	wait_for_completion(&bnad->bnad_completions.port_comp);
+
+	bnad_cleanup_tx(bnad, 0);
+	bnad_cleanup_rx(bnad, 0);
+
+	/* Synchronize mailbox IRQ */
+	bnad_mbox_irq_sync(bnad);
+
+	mutex_unlock(&bnad->conf_mutex);
+
+	return 0;
+}
+
+/* TX */
+/*
+ * bnad_start_xmit : Netdev entry point for Transmit
+ *		     Called under lock held by net_device
+ */
+static netdev_tx_t
+bnad_start_xmit(struct sk_buff *skb, struct net_device *netdev)
+{
+	struct bnad *bnad = netdev_priv(netdev);
+
+	u16 		txq_prod, vlan_tag = 0;
+	u32 		unmap_prod, wis, wis_used, wi_range;
+	u32 		vectors, vect_id, i, acked;
+	u32		tx_id;
+	int 			err;
+
+	struct bnad_tx_info *tx_info;
+	struct bna_tcb *tcb;
+	struct bnad_unmap_q *unmap_q;
+	dma_addr_t 		dma_addr;
+	struct bna_txq_entry *txqent;
+	bna_txq_wi_ctrl_flag_t 	flags;
+
+	if (unlikely
+	    (skb->len <= ETH_HLEN || skb->len > BFI_TX_MAX_DATA_PER_PKT)) {
+		dev_kfree_skb(skb);
+		return NETDEV_TX_OK;
+	}
+
+	/*
+	 * Takes care of the Tx that is scheduled between clearing the flag
+	 * and the netif_stop_queue() call.
+	 */
+	if (unlikely(!test_bit(BNAD_RF_TX_STARTED, &bnad->run_flags))) {
+		dev_kfree_skb(skb);
+		return NETDEV_TX_OK;
+	}
+
+	tx_id = 0;
+
+	tx_info = &bnad->tx_info[tx_id];
+	tcb = tx_info->tcb[tx_id];
+	unmap_q = tcb->unmap_q;
+
+	vectors = 1 + skb_shinfo(skb)->nr_frags;
+	if (vectors > BFI_TX_MAX_VECTORS_PER_PKT) {
+		dev_kfree_skb(skb);
+		return NETDEV_TX_OK;
+	}
+	wis = BNA_TXQ_WI_NEEDED(vectors);	/* 4 vectors per work item */
+	acked = 0;
+	if (unlikely
+	    (wis > BNA_QE_FREE_CNT(tcb, tcb->q_depth) ||
+	     vectors > BNA_QE_FREE_CNT(unmap_q, unmap_q->q_depth))) {
+		if ((u16) (*tcb->hw_consumer_index) !=
+		    tcb->consumer_index &&
+		    !test_and_set_bit(BNAD_TXQ_FREE_SENT, &tcb->flags)) {
+			acked = bnad_free_txbufs(bnad, tcb);
+			bna_ib_ack(tcb->i_dbell, acked);
+			smp_mb__before_clear_bit();
+			clear_bit(BNAD_TXQ_FREE_SENT, &tcb->flags);
+		} else {
+			netif_stop_queue(netdev);
+			BNAD_UPDATE_CTR(bnad, netif_queue_stop);
+		}
+
+		smp_mb();
+		/*
+		 * Check again to deal with race condition between
+		 * netif_stop_queue here, and netif_wake_queue in
+		 * interrupt handler which is not inside netif tx lock.
+		 */
+		if (likely
+		    (wis > BNA_QE_FREE_CNT(tcb, tcb->q_depth) ||
+		     vectors > BNA_QE_FREE_CNT(unmap_q, unmap_q->q_depth))) {
+			BNAD_UPDATE_CTR(bnad, netif_queue_stop);
+			return NETDEV_TX_BUSY;
+		} else {
+			netif_wake_queue(netdev);
+			BNAD_UPDATE_CTR(bnad, netif_queue_wakeup);
+		}
+	}
+
+	unmap_prod = unmap_q->producer_index;
+	wis_used = 1;
+	vect_id = 0;
+	flags = 0;
+
+	txq_prod = tcb->producer_index;
+	BNA_TXQ_QPGE_PTR_GET(txq_prod, tcb->sw_qpt, txqent, wi_range);
+	BUG_ON(!(wi_range <= tcb->q_depth));
+	txqent->hdr.wi.reserved = 0;
+	txqent->hdr.wi.num_vectors = vectors;
+	txqent->hdr.wi.opcode =
+		htons((skb_is_gso(skb) ? BNA_TXQ_WI_SEND_LSO :
+		       BNA_TXQ_WI_SEND));
+
+	if (bnad->vlan_grp && vlan_tx_tag_present(skb)) {
+		vlan_tag = (u16) vlan_tx_tag_get(skb);
+		flags |= (BNA_TXQ_WI_CF_INS_PRIO | BNA_TXQ_WI_CF_INS_VLAN);
+	}
+	if (test_bit(BNAD_RF_CEE_RUNNING, &bnad->run_flags)) {
+		vlan_tag =
+			(tcb->priority & 0x7) << 13 | (vlan_tag & 0x1fff);
+		flags |= (BNA_TXQ_WI_CF_INS_PRIO | BNA_TXQ_WI_CF_INS_VLAN);
+	}
+
+	txqent->hdr.wi.vlan_tag = htons(vlan_tag);
+
+	if (skb_is_gso(skb)) {
+		err = bnad_tso_prepare(bnad, skb);
+		if (err) {
+			dev_kfree_skb(skb);
+			return NETDEV_TX_OK;
+		}
+		txqent->hdr.wi.lso_mss = htons(skb_is_gso(skb));
+		flags |= (BNA_TXQ_WI_CF_IP_CKSUM | BNA_TXQ_WI_CF_TCP_CKSUM);
+		txqent->hdr.wi.l4_hdr_size_n_offset =
+			htons(BNA_TXQ_WI_L4_HDR_N_OFFSET
+			      (tcp_hdrlen(skb) >> 2,
+			       skb_transport_offset(skb)));
+	} else if (skb->ip_summed == CHECKSUM_PARTIAL) {
+		u8 proto = 0;
+
+		txqent->hdr.wi.lso_mss = 0;
+
+		if (skb->protocol == htons(ETH_P_IP))
+			proto = ip_hdr(skb)->protocol;
+		else if (skb->protocol == htons(ETH_P_IPV6)) {
+			/* nexthdr may not be TCP immediately. */
+			proto = ipv6_hdr(skb)->nexthdr;
+		}
+		if (proto == IPPROTO_TCP) {
+			flags |= BNA_TXQ_WI_CF_TCP_CKSUM;
+			txqent->hdr.wi.l4_hdr_size_n_offset =
+				htons(BNA_TXQ_WI_L4_HDR_N_OFFSET
+				      (0, skb_transport_offset(skb)));
+
+			BNAD_UPDATE_CTR(bnad, tcpcsum_offload);
+
+			BUG_ON(!(skb_headlen(skb) >=
+				skb_transport_offset(skb) + tcp_hdrlen(skb)));
+
+		} else if (proto == IPPROTO_UDP) {
+			flags |= BNA_TXQ_WI_CF_UDP_CKSUM;
+			txqent->hdr.wi.l4_hdr_size_n_offset =
+				htons(BNA_TXQ_WI_L4_HDR_N_OFFSET
+				      (0, skb_transport_offset(skb)));
+
+			BNAD_UPDATE_CTR(bnad, udpcsum_offload);
+
+			BUG_ON(!(skb_headlen(skb) >=
+				   skb_transport_offset(skb) +
+				   sizeof(struct udphdr)));
+		} else {
+			err = skb_checksum_help(skb);
+			BNAD_UPDATE_CTR(bnad, csum_help);
+			if (err) {
+				dev_kfree_skb(skb);
+				BNAD_UPDATE_CTR(bnad, csum_help_err);
+				return NETDEV_TX_OK;
+			}
+		}
+	} else {
+		txqent->hdr.wi.lso_mss = 0;
+		txqent->hdr.wi.l4_hdr_size_n_offset = 0;
+	}
+
+	txqent->hdr.wi.flags = htons(flags);
+
+	txqent->hdr.wi.frame_length = htonl(skb->len);
+
+	unmap_q->unmap_array[unmap_prod].skb = skb;
+	BUG_ON(!(skb_headlen(skb) <= BFI_TX_MAX_DATA_PER_VECTOR));
+	txqent->vector[vect_id].length = htons(skb_headlen(skb));
+	dma_addr = pci_map_single(bnad->pcidev, skb->data, skb_headlen(skb),
+		PCI_DMA_TODEVICE);
+	pci_unmap_addr_set(&unmap_q->unmap_array[unmap_prod], dma_addr,
+			   dma_addr);
+
+	BNA_SET_DMA_ADDR(dma_addr, &txqent->vector[vect_id].host_addr);
+	BNA_QE_INDX_ADD(unmap_prod, 1, unmap_q->q_depth);
+
+	for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
+		struct skb_frag_struct *frag = &skb_shinfo(skb)->frags[i];
+		u32		size = frag->size;
+
+		if (++vect_id == BFI_TX_MAX_VECTORS_PER_WI) {
+			vect_id = 0;
+			if (--wi_range)
+				txqent++;
+			else {
+				BNA_QE_INDX_ADD(txq_prod, wis_used,
+						tcb->q_depth);
+				wis_used = 0;
+				BNA_TXQ_QPGE_PTR_GET(txq_prod, tcb->sw_qpt,
+						     txqent, wi_range);
+				BUG_ON(!(wi_range <= tcb->q_depth));
+			}
+			wis_used++;
+			txqent->hdr.wi_ext.opcode = htons(BNA_TXQ_WI_EXTENSION);
+		}
+
+		BUG_ON(!(size <= BFI_TX_MAX_DATA_PER_VECTOR));
+		txqent->vector[vect_id].length = htons(size);
+		dma_addr =
+			pci_map_page(bnad->pcidev, frag->page,
+				     frag->page_offset, size,
+				     PCI_DMA_TODEVICE);
+		pci_unmap_addr_set(&unmap_q->unmap_array[unmap_prod], dma_addr,
+				   dma_addr);
+		BNA_SET_DMA_ADDR(dma_addr, &txqent->vector[vect_id].host_addr);
+		BNA_QE_INDX_ADD(unmap_prod, 1, unmap_q->q_depth);
+	}
+
+	unmap_q->producer_index = unmap_prod;
+	BNA_QE_INDX_ADD(txq_prod, wis_used, tcb->q_depth);
+	tcb->producer_index = txq_prod;
+
+	smp_mb();
+	bna_txq_prod_indx_doorbell(tcb);
+
+	if ((u16) (*tcb->hw_consumer_index) != tcb->consumer_index)
+		tasklet_schedule(&bnad->tx_free_tasklet);
+
+	return NETDEV_TX_OK;
+}
+
+/*
+ * Used spin_lock to synchronize reading of stats structures, which
+ * is written by BNA under the same lock.
+ */
+static struct net_device_stats *
+bnad_get_netdev_stats(struct net_device *netdev)
+{
+	struct bnad *bnad = netdev_priv(netdev);
+	unsigned long flags;
+
+	spin_lock_irqsave(&bnad->bna_lock, flags);
+
+	memset(&bnad->net_stats, 0, sizeof(struct net_device_stats));
+
+	bnad_netdev_qstats_fill(bnad);
+	bnad_netdev_hwstats_fill(bnad);
+
+	spin_unlock_irqrestore(&bnad->bna_lock, flags);
+
+	return &bnad->net_stats;
+}
+
+static void
+bnad_set_rx_mode(struct net_device *netdev)
+{
+	struct bnad *bnad = netdev_priv(netdev);
+	u32	new_mask, valid_mask;
+	unsigned long flags;
+
+	spin_lock_irqsave(&bnad->bna_lock, flags);
+
+	new_mask = valid_mask = 0;
+
+	if (netdev->flags & IFF_PROMISC) {
+		if (!(bnad->cfg_flags & BNAD_CF_PROMISC)) {
+			new_mask = BNAD_RXMODE_PROMISC_DEFAULT;
+			valid_mask = BNAD_RXMODE_PROMISC_DEFAULT;
+			bnad->cfg_flags |= BNAD_CF_PROMISC;
+		}
+	} else {
+		if (bnad->cfg_flags & BNAD_CF_PROMISC) {
+			new_mask = ~BNAD_RXMODE_PROMISC_DEFAULT;
+			valid_mask = BNAD_RXMODE_PROMISC_DEFAULT;
+			bnad->cfg_flags &= ~BNAD_CF_PROMISC;
+		}
+	}
+
+	if (netdev->flags & IFF_ALLMULTI) {
+		if (!(bnad->cfg_flags & BNAD_CF_ALLMULTI)) {
+			new_mask |= BNA_RXMODE_ALLMULTI;
+			valid_mask |= BNA_RXMODE_ALLMULTI;
+			bnad->cfg_flags |= BNAD_CF_ALLMULTI;
+		}
+	} else {
+		if (bnad->cfg_flags & BNAD_CF_ALLMULTI) {
+			new_mask &= ~BNA_RXMODE_ALLMULTI;
+			valid_mask |= BNA_RXMODE_ALLMULTI;
+			bnad->cfg_flags &= ~BNAD_CF_ALLMULTI;
+		}
+	}
+
+	bna_rx_mode_set(bnad->rx_info[0].rx, new_mask, valid_mask, NULL);
+
+	if (!netdev_mc_empty(netdev)) {
+		u8 *mcaddr_list;
+		int mc_count = netdev_mc_count(netdev);
+
+		/* Index 0 holds the broadcast address */
+		mcaddr_list =
+			kzalloc((mc_count + 1) * ETH_ALEN,
+				GFP_ATOMIC);
+		if (!mcaddr_list)
+			return;
+
+		memcpy(&mcaddr_list[0], &bnad_bcast_addr[0], ETH_ALEN);
+
+		/* Copy rest of the MC addresses */
+		bnad_netdev_mc_list_get(netdev, mcaddr_list);
+
+		bna_rx_mcast_listset(bnad->rx_info[0].rx, mc_count + 1,
+					mcaddr_list, NULL);
+
+		/* Should we enable BNAD_CF_ALLMULTI for err != 0 ? */
+		kfree(mcaddr_list);
+	}
+	spin_unlock_irqrestore(&bnad->bna_lock, flags);
+}
+
+/*
+ * bna_lock is used to sync writes to netdev->addr
+ * conf_lock cannot be used since this call may be made
+ * in a non-blocking context.
+ */
+static int
+bnad_set_mac_address(struct net_device *netdev, void *mac_addr)
+{
+	int err;
+	struct bnad *bnad = netdev_priv(netdev);
+	struct sockaddr *sa = (struct sockaddr *)mac_addr;
+	unsigned long flags;
+
+	spin_lock_irqsave(&bnad->bna_lock, flags);
+
+	err = bnad_mac_addr_set_locked(bnad, sa->sa_data);
+
+	if (!err)
+		memcpy(netdev->dev_addr, sa->sa_data, netdev->addr_len);
+
+	spin_unlock_irqrestore(&bnad->bna_lock, flags);
+
+	return err;
+}
+
+static int
+bnad_change_mtu(struct net_device *netdev, int new_mtu)
+{
+	int mtu, err = 0;
+	unsigned long flags;
+
+	struct bnad *bnad = netdev_priv(netdev);
+
+	if (new_mtu + ETH_HLEN < ETH_ZLEN || new_mtu > BNAD_JUMBO_MTU)
+		return -EINVAL;
+
+	mutex_lock(&bnad->conf_mutex);
+
+	netdev->mtu = new_mtu;
+
+	mtu = ETH_HLEN + new_mtu + ETH_FCS_LEN;
+
+	spin_lock_irqsave(&bnad->bna_lock, flags);
+	bna_port_mtu_set(&bnad->bna.port, mtu, NULL);
+	spin_unlock_irqrestore(&bnad->bna_lock, flags);
+
+	mutex_unlock(&bnad->conf_mutex);
+	return err;
+}
+
+static void
+bnad_vlan_rx_register(struct net_device *netdev,
+				  struct vlan_group *vlan_grp)
+{
+	struct bnad *bnad = netdev_priv(netdev);
+
+	mutex_lock(&bnad->conf_mutex);
+	bnad->vlan_grp = vlan_grp;
+	mutex_unlock(&bnad->conf_mutex);
+}
+
+static void
+bnad_vlan_rx_add_vid(struct net_device *netdev,
+				 unsigned short vid)
+{
+	struct bnad *bnad = netdev_priv(netdev);
+	unsigned long flags;
+
+	if (!bnad->rx_info[0].rx)
+		return;
+
+	mutex_lock(&bnad->conf_mutex);
+
+	spin_lock_irqsave(&bnad->bna_lock, flags);
+	bna_rx_vlan_add(bnad->rx_info[0].rx, vid);
+	spin_unlock_irqrestore(&bnad->bna_lock, flags);
+
+	mutex_unlock(&bnad->conf_mutex);
+}
+
+static void
+bnad_vlan_rx_kill_vid(struct net_device *netdev,
+				  unsigned short vid)
+{
+	struct bnad *bnad = netdev_priv(netdev);
+	unsigned long flags;
+
+	if (!bnad->rx_info[0].rx)
+		return;
+
+	mutex_lock(&bnad->conf_mutex);
+
+	spin_lock_irqsave(&bnad->bna_lock, flags);
+	bna_rx_vlan_del(bnad->rx_info[0].rx, vid);
+	spin_unlock_irqrestore(&bnad->bna_lock, flags);
+
+	mutex_unlock(&bnad->conf_mutex);
+}
+
+#ifdef CONFIG_NET_POLL_CONTROLLER
+static void
+bnad_netpoll(struct net_device *netdev)
+{
+	struct bnad *bnad = netdev_priv(netdev);
+	struct bnad_rx_info *rx_info;
+	struct bnad_rx_ctrl *rx_ctrl;
+	u32 curr_mask;
+	int i, j;
+
+	if (!(bnad->cfg_flags & BNAD_CF_MSIX)) {
+		bna_intx_disable(&bnad->bna, curr_mask);
+		bnad_isr(bnad->pcidev->irq, netdev);
+		bna_intx_enable(&bnad->bna, curr_mask);
+	} else {
+		for (i = 0; i < bnad->num_rx; i++) {
+			rx_info = &bnad->rx_info[i];
+			if (!rx_info->rx)
+				continue;
+			for (j = 0; j < bnad->num_rxp_per_rx; j++) {
+				rx_ctrl = &rx_info->rx_ctrl[j];
+				if (rx_ctrl->ccb) {
+					bnad_disable_rx_irq(bnad,
+							    rx_ctrl->ccb);
+					bnad_netif_rx_schedule_poll(bnad,
+							    rx_ctrl->ccb);
+				}
+			}
+		}
+	}
+}
+#endif
+
+static const struct net_device_ops bnad_netdev_ops = {
+	.ndo_open		= bnad_open,
+	.ndo_stop		= bnad_stop,
+	.ndo_start_xmit		= bnad_start_xmit,
+	.ndo_get_stats		= bnad_get_netdev_stats,
+	.ndo_set_rx_mode	= bnad_set_rx_mode,
+	.ndo_set_multicast_list = bnad_set_rx_mode,
+	.ndo_validate_addr      = eth_validate_addr,
+	.ndo_set_mac_address    = bnad_set_mac_address,
+	.ndo_change_mtu		= bnad_change_mtu,
+	.ndo_vlan_rx_register   = bnad_vlan_rx_register,
+	.ndo_vlan_rx_add_vid    = bnad_vlan_rx_add_vid,
+	.ndo_vlan_rx_kill_vid   = bnad_vlan_rx_kill_vid,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+	.ndo_poll_controller    = bnad_netpoll
+#endif
+};
+
+static void
+bnad_netdev_init(struct bnad *bnad, bool using_dac)
+{
+	struct net_device *netdev = bnad->netdev;
+
+	netdev->features |= NETIF_F_IPV6_CSUM;
+	netdev->features |= NETIF_F_TSO;
+	netdev->features |= NETIF_F_TSO6;
+
+	netdev->features |= NETIF_F_GRO;
+	pr_warn("bna: GRO enabled, using kernel stack GRO\n");
+
+	netdev->features |= NETIF_F_SG | NETIF_F_IP_CSUM;
+
+	if (using_dac)
+		netdev->features |= NETIF_F_HIGHDMA;
+
+	netdev->features |=
+		NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX |
+		NETIF_F_HW_VLAN_FILTER;
+
+	netdev->vlan_features = netdev->features;
+	netdev->mem_start = bnad->mmio_start;
+	netdev->mem_end = bnad->mmio_start + bnad->mmio_len - 1;
+
+	netdev->netdev_ops = &bnad_netdev_ops;
+	bnad_set_ethtool_ops(netdev);
+}
+
+/*
+ * 1. Initialize the bnad structure
+ * 2. Setup netdev pointer in pci_dev
+ * 3. Initialze Tx free tasklet
+ * 4. Initialize no. of TxQ & CQs & MSIX vectors
+ */
+static int
+bnad_init(struct bnad *bnad,
+	  struct pci_dev *pdev, struct net_device *netdev)
+{
+	unsigned long flags;
+
+	SET_NETDEV_DEV(netdev, &pdev->dev);
+	pci_set_drvdata(pdev, netdev);
+
+	bnad->netdev = netdev;
+	bnad->pcidev = pdev;
+	bnad->mmio_start = pci_resource_start(pdev, 0);
+	bnad->mmio_len = pci_resource_len(pdev, 0);
+	bnad->bar0 = ioremap_nocache(bnad->mmio_start, bnad->mmio_len);
+	if (!bnad->bar0) {
+		dev_err(&pdev->dev, "ioremap for bar0 failed\n");
+		pci_set_drvdata(pdev, NULL);
+		return -ENOMEM;
+	}
+	pr_info("bar0 mapped to %p, len %llu\n", bnad->bar0,
+	       (unsigned long long) bnad->mmio_len);
+
+	spin_lock_irqsave(&bnad->bna_lock, flags);
+	if (!bnad_msix_disable)
+		bnad->cfg_flags = BNAD_CF_MSIX;
+
+	bnad->cfg_flags |= BNAD_CF_DIM_ENABLED;
+
+	bnad_q_num_init(bnad);
+	spin_unlock_irqrestore(&bnad->bna_lock, flags);
+
+	bnad->msix_num = (bnad->num_tx * bnad->num_txq_per_tx) +
+		(bnad->num_rx * bnad->num_rxp_per_rx) +
+			 BNAD_MAILBOX_MSIX_VECTORS;
+	bnad->msix_diag_num = 2;	/* 1 for Tx, 1 for Rx */
+
+	bnad->txq_depth = BNAD_TXQ_DEPTH;
+	bnad->rxq_depth = BNAD_RXQ_DEPTH;
+	bnad->rx_csum = true;
+
+	bnad->tx_coalescing_timeo = BFI_TX_COALESCING_TIMEO;
+	bnad->rx_coalescing_timeo = BFI_RX_COALESCING_TIMEO;
+
+	tasklet_init(&bnad->tx_free_tasklet, bnad_tx_free_tasklet,
+		     (unsigned long)bnad);
+
+	return 0;
+}
+
+/*
+ * Must be called after bnad_pci_uninit()
+ * so that iounmap() and pci_set_drvdata(NULL)
+ * happens only after PCI uninitialization.
+ */
+static void
+bnad_uninit(struct bnad *bnad)
+{
+	if (bnad->bar0)
+		iounmap(bnad->bar0);
+	pci_set_drvdata(bnad->pcidev, NULL);
+}
+
+/*
+ * Initialize locks
+	a) Per device mutes used for serializing configuration
+	   changes from OS interface
+	b) spin lock used to protect bna state machine
+ */
+static void
+bnad_lock_init(struct bnad *bnad)
+{
+	spin_lock_init(&bnad->bna_lock);
+	mutex_init(&bnad->conf_mutex);
+}
+
+static void
+bnad_lock_uninit(struct bnad *bnad)
+{
+	mutex_destroy(&bnad->conf_mutex);
+}
+
+/* PCI Initialization */
+static int
+bnad_pci_init(struct bnad *bnad,
+	      struct pci_dev *pdev, bool *using_dac)
+{
+	int err;
+
+	err = pci_enable_device(pdev);
+	if (err)
+		return err;
+	err = pci_request_regions(pdev, BNAD_NAME);
+	if (err)
+		goto disable_device;
+	if (!pci_set_dma_mask(pdev, DMA_BIT_MASK(64)) &&
+	    !pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64))) {
+		*using_dac = 1;
+	} else {
+		err = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
+		if (err) {
+			err = pci_set_consistent_dma_mask(pdev,
+						DMA_BIT_MASK(32));
+			if (err)
+				goto release_regions;
+		}
+		*using_dac = 0;
+	}
+	pci_set_master(pdev);
+	return 0;
+
+release_regions:
+	pci_release_regions(pdev);
+disable_device:
+	pci_disable_device(pdev);
+
+	return err;
+}
+
+static void
+bnad_pci_uninit(struct pci_dev *pdev)
+{
+	pci_release_regions(pdev);
+	pci_disable_device(pdev);
+}
+
+static int __devinit
+bnad_pci_probe(struct pci_dev *pdev,
+		const struct pci_device_id *pcidev_id)
+{
+	bool 	using_dac;
+	int 	err;
+	struct bnad *bnad;
+	struct bna *bna;
+	struct net_device *netdev;
+	struct bfa_pcidev pcidev_info;
+	unsigned long flags;
+
+	pr_info("bnad_pci_probe : (0x%p, 0x%p) PCI Func : (%d)\n",
+	       pdev, pcidev_id, PCI_FUNC(pdev->devfn));
+
+	mutex_lock(&bnad_fwimg_mutex);
+	if (!cna_get_firmware_buf(pdev)) {
+		mutex_unlock(&bnad_fwimg_mutex);
+		pr_warn("Failed to load Firmware Image!\n");
+		return -ENODEV;
+	}
+	mutex_unlock(&bnad_fwimg_mutex);
+
+	/*
+	 * Allocates sizeof(struct net_device + struct bnad)
+	 * bnad = netdev->priv
+	 */
+	netdev = alloc_etherdev(sizeof(struct bnad));
+	if (!netdev) {
+		dev_err(&pdev->dev, "alloc_etherdev failed\n");
+		err = -ENOMEM;
+		return err;
+	}
+	bnad = netdev_priv(netdev);
+
+
+	/*
+	 * PCI initialization
+	 * 	Output : using_dac = 1 for 64 bit DMA
+	 *		           = 0 for 32 bit DMA
+	 */
+	err = bnad_pci_init(bnad, pdev, &using_dac);
+	if (err)
+		goto free_netdev;
+
+	bnad_lock_init(bnad);
+	/*
+	 * Initialize bnad structure
+	 * Setup relation between pci_dev & netdev
+	 * Init Tx free tasklet
+	 */
+	err = bnad_init(bnad, pdev, netdev);
+	if (err)
+		goto pci_uninit;
+	/* Initialize netdev structure, set up ethtool ops */
+	bnad_netdev_init(bnad, using_dac);
+
+	bnad_enable_msix(bnad);
+
+	/* Get resource requirement form bna */
+	bna_res_req(&bnad->res_info[0]);
+
+	/* Allocate resources from bna */
+	err = bnad_res_alloc(bnad);
+	if (err)
+		goto free_netdev;
+
+	bna = &bnad->bna;
+
+	/* Setup pcidev_info for bna_init() */
+	pcidev_info.pci_slot = PCI_SLOT(bnad->pcidev->devfn);
+	pcidev_info.pci_func = PCI_FUNC(bnad->pcidev->devfn);
+	pcidev_info.device_id = bnad->pcidev->device;
+	pcidev_info.pci_bar_kva = bnad->bar0;
+
+	mutex_lock(&bnad->conf_mutex);
+
+	spin_lock_irqsave(&bnad->bna_lock, flags);
+	bna_init(bna, bnad, &pcidev_info, &bnad->res_info[0]);
+
+	spin_unlock_irqrestore(&bnad->bna_lock, flags);
+
+	bnad->stats.bna_stats = &bna->stats;
+
+	/* Set up timers */
+	setup_timer(&bnad->bna.device.ioc.ioc_timer, bnad_ioc_timeout,
+				((unsigned long)bnad));
+	setup_timer(&bnad->bna.device.ioc.hb_timer, bnad_ioc_hb_check,
+				((unsigned long)bnad));
+	setup_timer(&bnad->bna.device.ioc.sem_timer, bnad_ioc_sem_timeout,
+				((unsigned long)bnad));
+
+	/* Now start the timer before calling IOC */
+	mod_timer(&bnad->bna.device.ioc.ioc_timer,
+		  jiffies + msecs_to_jiffies(BNA_IOC_TIMER_FREQ));
+
+	/*
+	 * Start the chip
+	 * Don't care even if err != 0, bna state machine will
+	 * deal with it
+	 */
+	err = bnad_device_enable(bnad);
+
+	/* Get the burnt-in mac */
+	spin_lock_irqsave(&bnad->bna_lock, flags);
+	bna_port_mac_get(&bna->port, &bnad->perm_addr);
+	bnad_set_netdev_perm_addr(bnad);
+	spin_unlock_irqrestore(&bnad->bna_lock, flags);
+
+	mutex_unlock(&bnad->conf_mutex);
+
+	/*
+	 * Make sure the link appears down to the stack
+	 */
+	netif_carrier_off(netdev);
+
+	/* Finally, reguister with net_device layer */
+	err = register_netdev(netdev);
+	if (err) {
+		pr_err("BNA : Registering with netdev failed\n");
+		goto disable_device;
+	}
+
+	return 0;
+
+disable_device:
+	mutex_lock(&bnad->conf_mutex);
+	bnad_device_disable(bnad);
+	del_timer_sync(&bnad->bna.device.ioc.ioc_timer);
+	del_timer_sync(&bnad->bna.device.ioc.sem_timer);
+	del_timer_sync(&bnad->bna.device.ioc.hb_timer);
+	spin_lock_irqsave(&bnad->bna_lock, flags);
+	bna_uninit(bna);
+	spin_unlock_irqrestore(&bnad->bna_lock, flags);
+	mutex_unlock(&bnad->conf_mutex);
+
+	bnad_res_free(bnad);
+	bnad_disable_msix(bnad);
+pci_uninit:
+	bnad_pci_uninit(pdev);
+	bnad_lock_uninit(bnad);
+	bnad_uninit(bnad);
+free_netdev:
+	free_netdev(netdev);
+	return err;
+}
+
+static void __devexit
+bnad_pci_remove(struct pci_dev *pdev)
+{
+	struct net_device *netdev = pci_get_drvdata(pdev);
+	struct bnad *bnad;
+	struct bna *bna;
+	unsigned long flags;
+
+	if (!netdev)
+		return;
+
+	pr_info("%s bnad_pci_remove\n", netdev->name);
+	bnad = netdev_priv(netdev);
+	bna = &bnad->bna;
+
+	unregister_netdev(netdev);
+
+	mutex_lock(&bnad->conf_mutex);
+	bnad_device_disable(bnad);
+	del_timer_sync(&bnad->bna.device.ioc.ioc_timer);
+	del_timer_sync(&bnad->bna.device.ioc.sem_timer);
+	del_timer_sync(&bnad->bna.device.ioc.hb_timer);
+	spin_lock_irqsave(&bnad->bna_lock, flags);
+	bna_uninit(bna);
+	spin_unlock_irqrestore(&bnad->bna_lock, flags);
+	mutex_unlock(&bnad->conf_mutex);
+
+	bnad_res_free(bnad);
+	bnad_disable_msix(bnad);
+	bnad_pci_uninit(pdev);
+	bnad_lock_uninit(bnad);
+	bnad_uninit(bnad);
+	free_netdev(netdev);
+}
+
+const struct pci_device_id bnad_pci_id_table[] = {
+	{
+		PCI_DEVICE(PCI_VENDOR_ID_BROCADE,
+			PCI_DEVICE_ID_BROCADE_CT),
+		.class = PCI_CLASS_NETWORK_ETHERNET << 8,
+		.class_mask =  0xffff00
+	}, {0,  }
+};
+
+MODULE_DEVICE_TABLE(pci, bnad_pci_id_table);
+
+static struct pci_driver bnad_pci_driver = {
+	.name = BNAD_NAME,
+	.id_table = bnad_pci_id_table,
+	.probe = bnad_pci_probe,
+	.remove = __devexit_p(bnad_pci_remove),
+};
+
+static int __init
+bnad_module_init(void)
+{
+	int err;
+
+	pr_info("Brocade 10G Ethernet driver\n");
+
+	bfa_ioc_auto_recover(bnad_ioc_auto_recover);
+
+	err = pci_register_driver(&bnad_pci_driver);
+	if (err < 0) {
+		pr_err("bna : PCI registration failed in module init "
+		       "(%d)\n", err);
+		return err;
+	}
+
+	return 0;
+}
+
+static void __exit
+bnad_module_exit(void)
+{
+	pci_unregister_driver(&bnad_pci_driver);
+
+	if (bfi_fw)
+		release_firmware(bfi_fw);
+}
+
+module_init(bnad_module_init);
+module_exit(bnad_module_exit);
+
+MODULE_AUTHOR("Brocade");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Brocade 10G PCIe Ethernet driver");
+MODULE_VERSION(BNAD_VERSION);
+MODULE_FIRMWARE(CNA_FW_FILE_CT);
diff -ruP net-next-2.6.35-rc1-orig/drivers/net/bna/bnad.h net-next-2.6.35-rc1-mod/drivers/net/bna/bnad.h
--- net-next-2.6.35-rc1-orig/drivers/net/bna/bnad.h	1969-12-31 16:00:00.000000000 -0800
+++ net-next-2.6.35-rc1-mod/drivers/net/bna/bnad.h	2010-08-23 13:24:46.999964000 -0700
@@ -0,0 +1,334 @@
+/*
+ * Linux network driver for Brocade Converged Network Adapter.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License (GPL) Version 2 as
+ * published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+/*
+ * Copyright (c) 2005-2010 Brocade Communications Systems, Inc.
+ * All rights reserved
+ * www.brocade.com
+ */
+#ifndef __BNAD_H__
+#define __BNAD_H__
+
+#include <linux/rtnetlink.h>
+#include <linux/workqueue.h>
+#include <linux/ipv6.h>
+#include <linux/etherdevice.h>
+#include <linux/mutex.h>
+#include <linux/firmware.h>
+
+/* Fix for IA64 */
+#include <asm/checksum.h>
+#include <net/ip6_checksum.h>
+
+#include <net/ip.h>
+#include <net/tcp.h>
+
+#include "bna.h"
+
+#define BNAD_TXQ_DEPTH		2048
+#define BNAD_RXQ_DEPTH		2048
+
+#define BNAD_MAX_TXS		1
+#define BNAD_MAX_TXQ_PER_TX	8	/* 8 priority queues */
+#define BNAD_TXQ_NUM		1
+
+#define BNAD_MAX_RXS		1
+#define BNAD_MAX_RXPS_PER_RX	16
+
+/*
+ * Control structure pointed to ccb->ctrl, which
+ * determines the NAPI / LRO behavior CCB
+ * There is 1:1 corres. between ccb & ctrl
+ */
+struct bnad_rx_ctrl {
+	struct bna_ccb *ccb;
+	struct napi_struct	napi;
+};
+
+#define BNAD_RXMODE_PROMISC_DEFAULT	BNA_RXMODE_PROMISC
+
+#define BNAD_GET_TX_ID(_skb)	(0)
+
+/*
+ * GLOBAL #defines (CONSTANTS)
+ */
+#define BNAD_NAME			"bna"
+#define BNAD_NAME_LEN			64
+
+#define BNAD_VERSION			"2.3.2.0"
+
+#define BNAD_MAILBOX_MSIX_VECTORS	1
+
+#define BNAD_STATS_TIMER_FREQ		1000 	/* in msecs */
+#define BNAD_DIM_TIMER_FREQ		1000 	/* in msecs */
+
+#define BNAD_MAX_Q_DEPTH		0x10000
+#define BNAD_MIN_Q_DEPTH		0x200
+
+#define BNAD_JUMBO_MTU			9000
+
+#define BNAD_NETIF_WAKE_THRESHOLD	8
+
+#define BNAD_RXQ_REFILL_THRESHOLD_SHIFT	3
+
+/* Bit positions for tcb->flags */
+#define BNAD_TXQ_FREE_SENT		0
+
+/* Bit positions for rcb->flags */
+#define BNAD_RXQ_REFILL			0
+#define BNAD_RXQ_STARTED		1
+
+/*
+ * DATA STRUCTURES
+ */
+
+/* enums */
+enum bnad_intr_source {
+	BNAD_INTR_TX		= 1,
+	BNAD_INTR_RX		= 2
+};
+
+enum bnad_link_state {
+	BNAD_LS_DOWN		= 0,
+	BNAD_LS_UP 		= 1
+};
+
+struct bnad_completion {
+	struct completion 	ioc_comp;
+	struct completion 	ucast_comp;
+	struct completion	mcast_comp;
+	struct completion	tx_comp;
+	struct completion	rx_comp;
+	struct completion	stats_comp;
+	struct completion	port_comp;
+
+	u8			ioc_comp_status;
+	u8			ucast_comp_status;
+	u8			mcast_comp_status;
+	u8			tx_comp_status;
+	u8			rx_comp_status;
+	u8			stats_comp_status;
+	u8			port_comp_status;
+};
+
+/* Tx Rx Control Stats */
+struct bnad_drv_stats {
+	u64 		netif_queue_stop;
+	u64		netif_queue_wakeup;
+	u64		tso4;
+	u64		tso6;
+	u64		tso_err;
+	u64		tcpcsum_offload;
+	u64		udpcsum_offload;
+	u64		csum_help;
+	u64		csum_help_err;
+
+	u64		hw_stats_updates;
+	u64		netif_rx_schedule;
+	u64		netif_rx_complete;
+	u64		netif_rx_dropped;
+
+	u64		link_toggle;
+	u64		cee_up;
+
+	u64		rxp_info_alloc_failed;
+	u64		mbox_intr_disabled;
+	u64		mbox_intr_enabled;
+	u64		tx_unmap_q_alloc_failed;
+	u64		rx_unmap_q_alloc_failed;
+
+	u64		rxbuf_alloc_failed;
+};
+
+/* Complete driver stats */
+struct bnad_stats {
+	struct bnad_drv_stats drv_stats;
+	struct bna_stats *bna_stats;
+};
+
+/* Tx / Rx Resources */
+struct bnad_tx_res_info {
+	struct bna_res_info res_info[BNA_TX_RES_T_MAX];
+};
+
+struct bnad_rx_res_info {
+	struct bna_res_info res_info[BNA_RX_RES_T_MAX];
+};
+
+struct bnad_tx_info {
+	struct bna_tx *tx; /* 1:1 between tx_info & tx */
+	struct bna_tcb *tcb[BNAD_MAX_TXQ_PER_TX];
+} ____cacheline_aligned;
+
+struct bnad_rx_info {
+	struct bna_rx *rx; /* 1:1 between rx_info & rx */
+
+	struct bnad_rx_ctrl rx_ctrl[BNAD_MAX_RXPS_PER_RX];
+} ____cacheline_aligned;
+
+/* Unmap queues for Tx / Rx cleanup */
+struct bnad_skb_unmap {
+	struct sk_buff		*skb;
+	DECLARE_PCI_UNMAP_ADDR(dma_addr)
+};
+
+struct bnad_unmap_q {
+	u32		producer_index;
+	u32		consumer_index;
+	u32 		q_depth;
+	/* This should be the last one */
+	struct bnad_skb_unmap unmap_array[1];
+};
+
+/* Bit mask values for bnad->cfg_flags */
+#define	BNAD_CF_DIM_ENABLED		0x01	/* DIM */
+#define	BNAD_CF_PROMISC			0x02
+#define BNAD_CF_ALLMULTI		0x04
+#define	BNAD_CF_MSIX			0x08	/* If in MSIx mode */
+
+/* Defines for run_flags bit-mask */
+/* Set, tested & cleared using xxx_bit() functions */
+/* Values indicated bit positions */
+#define	BNAD_RF_CEE_RUNNING		1
+#define BNAD_RF_HW_ERROR 		2
+#define BNAD_RF_MBOX_IRQ_DISABLED	3
+#define BNAD_RF_TX_STARTED		4
+#define BNAD_RF_RX_STARTED		5
+#define BNAD_RF_DIM_TIMER_RUNNING	6
+#define BNAD_RF_STATS_TIMER_RUNNING	7
+
+struct bnad {
+	struct net_device 	*netdev;
+
+	/* Data path */
+	struct bnad_tx_info tx_info[BNAD_MAX_TXS];
+	struct bnad_rx_info rx_info[BNAD_MAX_RXS];
+
+	struct vlan_group	*vlan_grp;
+	/*
+	 * These q numbers are global only because
+	 * they are used to calculate MSIx vectors.
+	 * Actually the exact # of queues are per Tx/Rx
+	 * object.
+	 */
+	u32		num_tx;
+	u32		num_rx;
+	u32		num_txq_per_tx;
+	u32		num_rxp_per_rx;
+
+	u32		txq_depth;
+	u32		rxq_depth;
+
+	u8			tx_coalescing_timeo;
+	u8			rx_coalescing_timeo;
+
+	struct bna_rx_config rx_config[BNAD_MAX_RXS];
+	struct bna_tx_config tx_config[BNAD_MAX_TXS];
+
+	u32		rx_csum;
+
+	void __iomem		*bar0;	/* BAR0 address */
+
+	struct bna bna;
+
+	u32		cfg_flags;
+	unsigned long		run_flags;
+
+	struct pci_dev 		*pcidev;
+	u64		mmio_start;
+	u64		mmio_len;
+
+	u32		msix_num;
+	u32		msix_diag_num;
+	struct msix_entry	*msix_table;
+
+	struct mutex		conf_mutex;
+	spinlock_t		bna_lock ____cacheline_aligned;
+
+	/* Timers */
+	struct timer_list	ioc_timer;
+	struct timer_list	dim_timer;
+	struct timer_list	stats_timer;
+
+	/* Control path resources, memory & irq */
+	struct bna_res_info res_info[BNA_RES_T_MAX];
+	struct bnad_tx_res_info tx_res_info[BNAD_MAX_TXS];
+	struct bnad_rx_res_info rx_res_info[BNAD_MAX_RXS];
+
+	struct bnad_completion bnad_completions;
+
+	/* Burnt in MAC address */
+	mac_t			perm_addr;
+
+	struct tasklet_struct	tx_free_tasklet;
+
+	/* Statistics */
+	struct bnad_stats stats;
+	struct net_device_stats net_stats;
+
+	struct bnad_diag *diag;
+
+	char			adapter_name[BNAD_NAME_LEN];
+	char 			port_name[BNAD_NAME_LEN];
+	char			mbox_irq_name[BNAD_NAME_LEN];
+};
+
+/*
+ * EXTERN VARIABLES
+ */
+extern struct firmware *bfi_fw;
+extern u32 		bnad_rxqs_per_cq;
+
+/*
+ * EXTERN PROTOTYPES
+ */
+extern u32 *cna_get_firmware_buf(struct pci_dev *pdev);
+/* Netdev entry point prototypes */
+extern void bnad_set_ethtool_ops(struct net_device *netdev);
+
+/* Configuration & setup */
+extern void bnad_tx_coalescing_timeo_set(struct bnad *bnad);
+extern void bnad_rx_coalescing_timeo_set(struct bnad *bnad);
+
+extern int bnad_setup_rx(struct bnad *bnad, uint rx_id);
+extern int bnad_setup_tx(struct bnad *bnad, uint tx_id);
+extern void bnad_cleanup_tx(struct bnad *bnad, uint tx_id);
+extern void bnad_cleanup_rx(struct bnad *bnad, uint rx_id);
+
+/* Timer start/stop protos */
+extern void bnad_dim_timer_start(struct bnad *bnad);
+
+/* Statistics */
+extern void bnad_netdev_qstats_fill(struct bnad *bnad);
+extern void bnad_netdev_hwstats_fill(struct bnad *bnad);
+
+/**
+ * MACROS
+ */
+/* To set & get the stats counters */
+#define BNAD_UPDATE_CTR(_bnad, _ctr)				\
+				(((_bnad)->stats.drv_stats._ctr)++)
+
+#define BNAD_GET_CTR(_bnad, _ctr) ((_bnad)->stats.drv_stats._ctr)
+
+#define bnad_enable_rx_irq_unsafe(_ccb)			\
+{							\
+	bna_ib_coalescing_timer_set((_ccb)->i_dbell,	\
+		(_ccb)->rx_coalescing_timeo);		\
+	bna_ib_ack((_ccb)->i_dbell, 0);			\
+}
+
+#define bnad_dim_timer_running(_bnad)				\
+	(((_bnad)->cfg_flags & BNAD_CF_DIM_ENABLED) && 		\
+	(test_bit(BNAD_RF_DIM_TIMER_RUNNING, &((_bnad)->run_flags))))
+
+#endif /* __BNAD_H__ */
diff -ruP net-next-2.6.35-rc1-orig/drivers/net/bna/bnad_ethtool.c net-next-2.6.35-rc1-mod/drivers/net/bna/bnad_ethtool.c
--- net-next-2.6.35-rc1-orig/drivers/net/bna/bnad_ethtool.c	1969-12-31 16:00:00.000000000 -0800
+++ net-next-2.6.35-rc1-mod/drivers/net/bna/bnad_ethtool.c	2010-08-23 13:24:46.989969000 -0700
@@ -0,0 +1,1282 @@
+/*
+ * Linux network driver for Brocade Converged Network Adapter.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License (GPL) Version 2 as
+ * published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+/*
+ * Copyright (c) 2005-2010 Brocade Communications Systems, Inc.
+ * All rights reserved
+ * www.brocade.com
+ */
+
+#include "cna.h"
+
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <linux/ethtool.h>
+#include <linux/rtnetlink.h>
+
+#include "bna.h"
+
+#include "bnad.h"
+
+#define BNAD_NUM_TXF_COUNTERS 12
+#define BNAD_NUM_RXF_COUNTERS 10
+#define BNAD_NUM_CQ_COUNTERS 3
+#define BNAD_NUM_RXQ_COUNTERS 6
+#define BNAD_NUM_TXQ_COUNTERS 5
+
+#define BNAD_ETHTOOL_STATS_NUM						\
+	(sizeof(struct net_device_stats) / sizeof(unsigned long) +	\
+	sizeof(struct bnad_drv_stats) / sizeof(u64) +		\
+	offsetof(struct bfi_ll_stats, rxf_stats[0]) / sizeof(u64))
+
+static char *bnad_net_stats_strings[BNAD_ETHTOOL_STATS_NUM] = {
+	"rx_packets",
+	"tx_packets",
+	"rx_bytes",
+	"tx_bytes",
+	"rx_errors",
+	"tx_errors",
+	"rx_dropped",
+	"tx_dropped",
+	"multicast",
+	"collisions",
+
+	"rx_length_errors",
+	"rx_over_errors",
+	"rx_crc_errors",
+	"rx_frame_errors",
+	"rx_fifo_errors",
+	"rx_missed_errors",
+
+	"tx_aborted_errors",
+	"tx_carrier_errors",
+	"tx_fifo_errors",
+	"tx_heartbeat_errors",
+	"tx_window_errors",
+
+	"rx_compressed",
+	"tx_compressed",
+
+	"netif_queue_stop",
+	"netif_queue_wakeup",
+	"tso4",
+	"tso6",
+	"tso_err",
+	"tcpcsum_offload",
+	"udpcsum_offload",
+	"csum_help",
+	"csum_help_err",
+	"hw_stats_updates",
+	"netif_rx_schedule",
+	"netif_rx_complete",
+	"netif_rx_dropped",
+
+	"link_toggle",
+	"cee_up",
+
+	"rxp_info_alloc_failed",
+	"mbox_intr_disabled",
+	"mbox_intr_enabled",
+	"tx_unmap_q_alloc_failed",
+	"rx_unmap_q_alloc_failed",
+	"rxbuf_alloc_failed",
+
+	"mac_frame_64",
+	"mac_frame_65_127",
+	"mac_frame_128_255",
+	"mac_frame_256_511",
+	"mac_frame_512_1023",
+	"mac_frame_1024_1518",
+	"mac_frame_1518_1522",
+	"mac_rx_bytes",
+	"mac_rx_packets",
+	"mac_rx_fcs_error",
+	"mac_rx_multicast",
+	"mac_rx_broadcast",
+	"mac_rx_control_frames",
+	"mac_rx_pause",
+	"mac_rx_unknown_opcode",
+	"mac_rx_alignment_error",
+	"mac_rx_frame_length_error",
+	"mac_rx_code_error",
+	"mac_rx_carrier_sense_error",
+	"mac_rx_undersize",
+	"mac_rx_oversize",
+	"mac_rx_fragments",
+	"mac_rx_jabber",
+	"mac_rx_drop",
+
+	"mac_tx_bytes",
+	"mac_tx_packets",
+	"mac_tx_multicast",
+	"mac_tx_broadcast",
+	"mac_tx_pause",
+	"mac_tx_deferral",
+	"mac_tx_excessive_deferral",
+	"mac_tx_single_collision",
+	"mac_tx_muliple_collision",
+	"mac_tx_late_collision",
+	"mac_tx_excessive_collision",
+	"mac_tx_total_collision",
+	"mac_tx_pause_honored",
+	"mac_tx_drop",
+	"mac_tx_jabber",
+	"mac_tx_fcs_error",
+	"mac_tx_control_frame",
+	"mac_tx_oversize",
+	"mac_tx_undersize",
+	"mac_tx_fragments",
+
+	"bpc_tx_pause_0",
+	"bpc_tx_pause_1",
+	"bpc_tx_pause_2",
+	"bpc_tx_pause_3",
+	"bpc_tx_pause_4",
+	"bpc_tx_pause_5",
+	"bpc_tx_pause_6",
+	"bpc_tx_pause_7",
+	"bpc_tx_zero_pause_0",
+	"bpc_tx_zero_pause_1",
+	"bpc_tx_zero_pause_2",
+	"bpc_tx_zero_pause_3",
+	"bpc_tx_zero_pause_4",
+	"bpc_tx_zero_pause_5",
+	"bpc_tx_zero_pause_6",
+	"bpc_tx_zero_pause_7",
+	"bpc_tx_first_pause_0",
+	"bpc_tx_first_pause_1",
+	"bpc_tx_first_pause_2",
+	"bpc_tx_first_pause_3",
+	"bpc_tx_first_pause_4",
+	"bpc_tx_first_pause_5",
+	"bpc_tx_first_pause_6",
+	"bpc_tx_first_pause_7",
+
+	"bpc_rx_pause_0",
+	"bpc_rx_pause_1",
+	"bpc_rx_pause_2",
+	"bpc_rx_pause_3",
+	"bpc_rx_pause_4",
+	"bpc_rx_pause_5",
+	"bpc_rx_pause_6",
+	"bpc_rx_pause_7",
+	"bpc_rx_zero_pause_0",
+	"bpc_rx_zero_pause_1",
+	"bpc_rx_zero_pause_2",
+	"bpc_rx_zero_pause_3",
+	"bpc_rx_zero_pause_4",
+	"bpc_rx_zero_pause_5",
+	"bpc_rx_zero_pause_6",
+	"bpc_rx_zero_pause_7",
+	"bpc_rx_first_pause_0",
+	"bpc_rx_first_pause_1",
+	"bpc_rx_first_pause_2",
+	"bpc_rx_first_pause_3",
+	"bpc_rx_first_pause_4",
+	"bpc_rx_first_pause_5",
+	"bpc_rx_first_pause_6",
+	"bpc_rx_first_pause_7",
+
+	"rad_rx_frames",
+	"rad_rx_octets",
+	"rad_rx_vlan_frames",
+	"rad_rx_ucast",
+	"rad_rx_ucast_octets",
+	"rad_rx_ucast_vlan",
+	"rad_rx_mcast",
+	"rad_rx_mcast_octets",
+	"rad_rx_mcast_vlan",
+	"rad_rx_bcast",
+	"rad_rx_bcast_octets",
+	"rad_rx_bcast_vlan",
+	"rad_rx_drops",
+
+	"fc_rx_ucast_octets",
+	"fc_rx_ucast",
+	"fc_rx_ucast_vlan",
+	"fc_rx_mcast_octets",
+	"fc_rx_mcast",
+	"fc_rx_mcast_vlan",
+	"fc_rx_bcast_octets",
+	"fc_rx_bcast",
+	"fc_rx_bcast_vlan",
+
+	"fc_tx_ucast_octets",
+	"fc_tx_ucast",
+	"fc_tx_ucast_vlan",
+	"fc_tx_mcast_octets",
+	"fc_tx_mcast",
+	"fc_tx_mcast_vlan",
+	"fc_tx_bcast_octets",
+	"fc_tx_bcast",
+	"fc_tx_bcast_vlan",
+	"fc_tx_parity_errors",
+	"fc_tx_timeout",
+	"fc_tx_fid_parity_errors",
+};
+
+static int
+bnad_get_settings(struct net_device *netdev, struct ethtool_cmd *cmd)
+{
+	cmd->supported = SUPPORTED_10000baseT_Full;
+	cmd->advertising = ADVERTISED_10000baseT_Full;
+	cmd->autoneg = AUTONEG_DISABLE;
+	cmd->supported |= SUPPORTED_FIBRE;
+	cmd->advertising |= ADVERTISED_FIBRE;
+	cmd->port = PORT_FIBRE;
+	cmd->phy_address = 0;
+
+	if (netif_carrier_ok(netdev)) {
+		cmd->speed = SPEED_10000;
+		cmd->duplex = DUPLEX_FULL;
+	} else {
+		cmd->speed = -1;
+		cmd->duplex = -1;
+	}
+	cmd->transceiver = XCVR_EXTERNAL;
+	cmd->maxtxpkt = 0;
+	cmd->maxrxpkt = 0;
+
+	return 0;
+}
+
+static int
+bnad_set_settings(struct net_device *netdev, struct ethtool_cmd *cmd)
+{
+	/* 10G full duplex setting supported only */
+	if (cmd->autoneg == AUTONEG_ENABLE)
+		return -EOPNOTSUPP; else {
+		if ((cmd->speed == SPEED_10000) && (cmd->duplex == DUPLEX_FULL))
+			return 0;
+	}
+
+	return -EOPNOTSUPP;
+}
+
+static void
+bnad_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *drvinfo)
+{
+	struct bnad *bnad = netdev_priv(netdev);
+	struct bfa_ioc_attr *ioc_attr;
+	unsigned long flags;
+
+	strcpy(drvinfo->driver, BNAD_NAME);
+	strcpy(drvinfo->version, BNAD_VERSION);
+
+	ioc_attr = kzalloc(sizeof(*ioc_attr), GFP_KERNEL);
+	if (ioc_attr) {
+		memset(ioc_attr, 0, sizeof(*ioc_attr));
+		spin_lock_irqsave(&bnad->bna_lock, flags);
+		bfa_ioc_get_attr(&bnad->bna.device.ioc, ioc_attr);
+		spin_unlock_irqrestore(&bnad->bna_lock, flags);
+
+		strncpy(drvinfo->fw_version, ioc_attr->adapter_attr.fw_ver,
+			sizeof(drvinfo->fw_version) - 1);
+		kfree(ioc_attr);
+	}
+
+	strncpy(drvinfo->bus_info, pci_name(bnad->pcidev), ETHTOOL_BUSINFO_LEN);
+}
+
+static int
+get_regs(struct bnad *bnad, u32 * regs)
+{
+	int num = 0, i;
+	u32 reg_addr;
+	unsigned long flags;
+
+#define BNAD_GET_REG(addr) 					\
+do {								\
+	if (regs)						\
+		regs[num++] = readl(bnad->bar0 + (addr));	\
+	else							\
+		num++;						\
+} while (0)
+
+	spin_lock_irqsave(&bnad->bna_lock, flags);
+
+	/* DMA Block Internal Registers */
+	BNAD_GET_REG(DMA_CTRL_REG0);
+	BNAD_GET_REG(DMA_CTRL_REG1);
+	BNAD_GET_REG(DMA_ERR_INT_STATUS);
+	BNAD_GET_REG(DMA_ERR_INT_ENABLE);
+	BNAD_GET_REG(DMA_ERR_INT_STATUS_SET);
+
+	/* APP Block Register Address Offset from BAR0 */
+	BNAD_GET_REG(HOSTFN0_INT_STATUS);
+	BNAD_GET_REG(HOSTFN0_INT_MASK);
+	BNAD_GET_REG(HOST_PAGE_NUM_FN0);
+	BNAD_GET_REG(HOST_MSIX_ERR_INDEX_FN0);
+	BNAD_GET_REG(FN0_PCIE_ERR_REG);
+	BNAD_GET_REG(FN0_ERR_TYPE_STATUS_REG);
+	BNAD_GET_REG(FN0_ERR_TYPE_MSK_STATUS_REG);
+
+	BNAD_GET_REG(HOSTFN1_INT_STATUS);
+	BNAD_GET_REG(HOSTFN1_INT_MASK);
+	BNAD_GET_REG(HOST_PAGE_NUM_FN1);
+	BNAD_GET_REG(HOST_MSIX_ERR_INDEX_FN1);
+	BNAD_GET_REG(FN1_PCIE_ERR_REG);
+	BNAD_GET_REG(FN1_ERR_TYPE_STATUS_REG);
+	BNAD_GET_REG(FN1_ERR_TYPE_MSK_STATUS_REG);
+
+	BNAD_GET_REG(PCIE_MISC_REG);
+
+	BNAD_GET_REG(HOST_SEM0_REG);
+	BNAD_GET_REG(HOST_SEM1_REG);
+	BNAD_GET_REG(HOST_SEM2_REG);
+	BNAD_GET_REG(HOST_SEM3_REG);
+	BNAD_GET_REG(HOST_SEM0_INFO_REG);
+	BNAD_GET_REG(HOST_SEM1_INFO_REG);
+	BNAD_GET_REG(HOST_SEM2_INFO_REG);
+	BNAD_GET_REG(HOST_SEM3_INFO_REG);
+
+	BNAD_GET_REG(TEMPSENSE_CNTL_REG);
+	BNAD_GET_REG(TEMPSENSE_STAT_REG);
+
+	BNAD_GET_REG(APP_LOCAL_ERR_STAT);
+	BNAD_GET_REG(APP_LOCAL_ERR_MSK);
+
+	BNAD_GET_REG(PCIE_LNK_ERR_STAT);
+	BNAD_GET_REG(PCIE_LNK_ERR_MSK);
+
+	BNAD_GET_REG(FCOE_FIP_ETH_TYPE);
+	BNAD_GET_REG(RESV_ETH_TYPE);
+
+	BNAD_GET_REG(HOSTFN2_INT_STATUS);
+	BNAD_GET_REG(HOSTFN2_INT_MASK);
+	BNAD_GET_REG(HOST_PAGE_NUM_FN2);
+	BNAD_GET_REG(HOST_MSIX_ERR_INDEX_FN2);
+	BNAD_GET_REG(FN2_PCIE_ERR_REG);
+	BNAD_GET_REG(FN2_ERR_TYPE_STATUS_REG);
+	BNAD_GET_REG(FN2_ERR_TYPE_MSK_STATUS_REG);
+
+	BNAD_GET_REG(HOSTFN3_INT_STATUS);
+	BNAD_GET_REG(HOSTFN3_INT_MASK);
+	BNAD_GET_REG(HOST_PAGE_NUM_FN3);
+	BNAD_GET_REG(HOST_MSIX_ERR_INDEX_FN3);
+	BNAD_GET_REG(FN3_PCIE_ERR_REG);
+	BNAD_GET_REG(FN3_ERR_TYPE_STATUS_REG);
+	BNAD_GET_REG(FN3_ERR_TYPE_MSK_STATUS_REG);
+
+	/* Host Command Status Registers */
+	reg_addr = HOST_CMDSTS0_CLR_REG;
+	for (i = 0; i < 16; i++) {
+		BNAD_GET_REG(reg_addr);
+		BNAD_GET_REG(reg_addr + 4);
+		BNAD_GET_REG(reg_addr + 8);
+		reg_addr += 0x10;
+	}
+
+	/* Function ID register */
+	BNAD_GET_REG(FNC_ID_REG);
+
+	/* Function personality register */
+	BNAD_GET_REG(FNC_PERS_REG);
+
+	/* Operation mode register */
+	BNAD_GET_REG(OP_MODE);
+
+	/* LPU0 Registers */
+	BNAD_GET_REG(LPU0_MBOX_CTL_REG);
+	BNAD_GET_REG(LPU0_MBOX_CMD_REG);
+	BNAD_GET_REG(LPU0_MBOX_LINK_0REG);
+	BNAD_GET_REG(LPU1_MBOX_LINK_0REG);
+	BNAD_GET_REG(LPU0_MBOX_STATUS_0REG);
+	BNAD_GET_REG(LPU1_MBOX_STATUS_0REG);
+	BNAD_GET_REG(LPU0_ERR_STATUS_REG);
+	BNAD_GET_REG(LPU0_ERR_SET_REG);
+
+	/* LPU1 Registers */
+	BNAD_GET_REG(LPU1_MBOX_CTL_REG);
+	BNAD_GET_REG(LPU1_MBOX_CMD_REG);
+	BNAD_GET_REG(LPU0_MBOX_LINK_1REG);
+	BNAD_GET_REG(LPU1_MBOX_LINK_1REG);
+	BNAD_GET_REG(LPU0_MBOX_STATUS_1REG);
+	BNAD_GET_REG(LPU1_MBOX_STATUS_1REG);
+	BNAD_GET_REG(LPU1_ERR_STATUS_REG);
+	BNAD_GET_REG(LPU1_ERR_SET_REG);
+
+	/* PSS Registers */
+	BNAD_GET_REG(PSS_CTL_REG);
+	BNAD_GET_REG(PSS_ERR_STATUS_REG);
+	BNAD_GET_REG(ERR_STATUS_SET);
+	BNAD_GET_REG(PSS_RAM_ERR_STATUS_REG);
+
+	/* Catapult CPQ Registers */
+	BNAD_GET_REG(HOSTFN0_LPU0_MBOX0_CMD_STAT);
+	BNAD_GET_REG(HOSTFN0_LPU1_MBOX0_CMD_STAT);
+	BNAD_GET_REG(LPU0_HOSTFN0_MBOX0_CMD_STAT);
+	BNAD_GET_REG(LPU1_HOSTFN0_MBOX0_CMD_STAT);
+
+	BNAD_GET_REG(HOSTFN0_LPU0_MBOX1_CMD_STAT);
+	BNAD_GET_REG(HOSTFN0_LPU1_MBOX1_CMD_STAT);
+	BNAD_GET_REG(LPU0_HOSTFN0_MBOX1_CMD_STAT);
+	BNAD_GET_REG(LPU1_HOSTFN0_MBOX1_CMD_STAT);
+
+	BNAD_GET_REG(HOSTFN1_LPU0_MBOX0_CMD_STAT);
+	BNAD_GET_REG(HOSTFN1_LPU1_MBOX0_CMD_STAT);
+	BNAD_GET_REG(LPU0_HOSTFN1_MBOX0_CMD_STAT);
+	BNAD_GET_REG(LPU1_HOSTFN1_MBOX0_CMD_STAT);
+
+	BNAD_GET_REG(HOSTFN1_LPU0_MBOX1_CMD_STAT);
+	BNAD_GET_REG(HOSTFN1_LPU1_MBOX1_CMD_STAT);
+	BNAD_GET_REG(LPU0_HOSTFN1_MBOX1_CMD_STAT);
+	BNAD_GET_REG(LPU1_HOSTFN1_MBOX1_CMD_STAT);
+
+	BNAD_GET_REG(HOSTFN2_LPU0_MBOX0_CMD_STAT);
+	BNAD_GET_REG(HOSTFN2_LPU1_MBOX0_CMD_STAT);
+	BNAD_GET_REG(LPU0_HOSTFN2_MBOX0_CMD_STAT);
+	BNAD_GET_REG(LPU1_HOSTFN2_MBOX0_CMD_STAT);
+
+	BNAD_GET_REG(HOSTFN2_LPU0_MBOX1_CMD_STAT);
+	BNAD_GET_REG(HOSTFN2_LPU1_MBOX1_CMD_STAT);
+	BNAD_GET_REG(LPU0_HOSTFN2_MBOX1_CMD_STAT);
+	BNAD_GET_REG(LPU1_HOSTFN2_MBOX1_CMD_STAT);
+
+	BNAD_GET_REG(HOSTFN3_LPU0_MBOX0_CMD_STAT);
+	BNAD_GET_REG(HOSTFN3_LPU1_MBOX0_CMD_STAT);
+	BNAD_GET_REG(LPU0_HOSTFN3_MBOX0_CMD_STAT);
+	BNAD_GET_REG(LPU1_HOSTFN3_MBOX0_CMD_STAT);
+
+	BNAD_GET_REG(HOSTFN3_LPU0_MBOX1_CMD_STAT);
+	BNAD_GET_REG(HOSTFN3_LPU1_MBOX1_CMD_STAT);
+	BNAD_GET_REG(LPU0_HOSTFN3_MBOX1_CMD_STAT);
+	BNAD_GET_REG(LPU1_HOSTFN3_MBOX1_CMD_STAT);
+
+	/* Host Function Force Parity Error Registers */
+	BNAD_GET_REG(HOSTFN0_LPU_FORCE_PERR);
+	BNAD_GET_REG(HOSTFN1_LPU_FORCE_PERR);
+	BNAD_GET_REG(HOSTFN2_LPU_FORCE_PERR);
+	BNAD_GET_REG(HOSTFN3_LPU_FORCE_PERR);
+
+	/* LL Port[0|1] Halt Mask Registers */
+	BNAD_GET_REG(LL_HALT_MSK_P0);
+	BNAD_GET_REG(LL_HALT_MSK_P1);
+
+	/* LL Port[0|1] Error Mask Registers */
+	BNAD_GET_REG(LL_ERR_MSK_P0);
+	BNAD_GET_REG(LL_ERR_MSK_P1);
+
+	/* EMC FLI Registers */
+	BNAD_GET_REG(FLI_CMD_REG);
+	BNAD_GET_REG(FLI_ADDR_REG);
+	BNAD_GET_REG(FLI_CTL_REG);
+	BNAD_GET_REG(FLI_WRDATA_REG);
+	BNAD_GET_REG(FLI_RDDATA_REG);
+	BNAD_GET_REG(FLI_DEV_STATUS_REG);
+	BNAD_GET_REG(FLI_SIG_WD_REG);
+
+	BNAD_GET_REG(FLI_DEV_VENDOR_REG);
+	BNAD_GET_REG(FLI_ERR_STATUS_REG);
+
+	/* RxAdm 0 Registers */
+	BNAD_GET_REG(RAD0_CTL_REG);
+	BNAD_GET_REG(RAD0_PE_PARM_REG);
+	BNAD_GET_REG(RAD0_BCN_REG);
+	BNAD_GET_REG(RAD0_DEFAULT_REG);
+	BNAD_GET_REG(RAD0_PROMISC_REG);
+	BNAD_GET_REG(RAD0_BCNQ_REG);
+	BNAD_GET_REG(RAD0_DEFAULTQ_REG);
+
+	BNAD_GET_REG(RAD0_ERR_STS);
+	BNAD_GET_REG(RAD0_SET_ERR_STS);
+	BNAD_GET_REG(RAD0_ERR_INT_EN);
+	BNAD_GET_REG(RAD0_FIRST_ERR);
+	BNAD_GET_REG(RAD0_FORCE_ERR);
+
+	BNAD_GET_REG(RAD0_MAC_MAN_1H);
+	BNAD_GET_REG(RAD0_MAC_MAN_1L);
+	BNAD_GET_REG(RAD0_MAC_MAN_2H);
+	BNAD_GET_REG(RAD0_MAC_MAN_2L);
+	BNAD_GET_REG(RAD0_MAC_MAN_3H);
+	BNAD_GET_REG(RAD0_MAC_MAN_3L);
+	BNAD_GET_REG(RAD0_MAC_MAN_4H);
+	BNAD_GET_REG(RAD0_MAC_MAN_4L);
+
+	BNAD_GET_REG(RAD0_LAST4_IP);
+
+	/* RxAdm 1 Registers */
+	BNAD_GET_REG(RAD1_CTL_REG);
+	BNAD_GET_REG(RAD1_PE_PARM_REG);
+	BNAD_GET_REG(RAD1_BCN_REG);
+	BNAD_GET_REG(RAD1_DEFAULT_REG);
+	BNAD_GET_REG(RAD1_PROMISC_REG);
+	BNAD_GET_REG(RAD1_BCNQ_REG);
+	BNAD_GET_REG(RAD1_DEFAULTQ_REG);
+
+	BNAD_GET_REG(RAD1_ERR_STS);
+	BNAD_GET_REG(RAD1_SET_ERR_STS);
+	BNAD_GET_REG(RAD1_ERR_INT_EN);
+
+	/* TxA0 Registers */
+	BNAD_GET_REG(TXA0_CTRL_REG);
+	/* TxA0 TSO Sequence # Registers (RO) */
+	for (i = 0; i < 8; i++) {
+		BNAD_GET_REG(TXA0_TSO_TCP_SEQ_REG(i));
+		BNAD_GET_REG(TXA0_TSO_IP_INFO_REG(i));
+	}
+
+	/* TxA1 Registers */
+	BNAD_GET_REG(TXA1_CTRL_REG);
+	/* TxA1 TSO Sequence # Registers (RO) */
+	for (i = 0; i < 8; i++) {
+		BNAD_GET_REG(TXA1_TSO_TCP_SEQ_REG(i));
+		BNAD_GET_REG(TXA1_TSO_IP_INFO_REG(i));
+	}
+
+	/* RxA Registers */
+	BNAD_GET_REG(RXA0_CTL_REG);
+	BNAD_GET_REG(RXA1_CTL_REG);
+
+	/* PLB0 Registers */
+	BNAD_GET_REG(PLB0_ECM_TIMER_REG);
+	BNAD_GET_REG(PLB0_RL_CTL);
+	for (i = 0; i < 8; i++)
+		BNAD_GET_REG(PLB0_RL_MAX_BC(i));
+	BNAD_GET_REG(PLB0_RL_TU_PRIO);
+	for (i = 0; i < 8; i++)
+		BNAD_GET_REG(PLB0_RL_BYTE_CNT(i));
+	BNAD_GET_REG(PLB0_RL_MIN_REG);
+	BNAD_GET_REG(PLB0_RL_MAX_REG);
+	BNAD_GET_REG(PLB0_EMS_ADD_REG);
+
+	/* PLB1 Registers */
+	BNAD_GET_REG(PLB1_ECM_TIMER_REG);
+	BNAD_GET_REG(PLB1_RL_CTL);
+	for (i = 0; i < 8; i++)
+		BNAD_GET_REG(PLB1_RL_MAX_BC(i));
+	BNAD_GET_REG(PLB1_RL_TU_PRIO);
+	for (i = 0; i < 8; i++)
+		BNAD_GET_REG(PLB1_RL_BYTE_CNT(i));
+	BNAD_GET_REG(PLB1_RL_MIN_REG);
+	BNAD_GET_REG(PLB1_RL_MAX_REG);
+	BNAD_GET_REG(PLB1_EMS_ADD_REG);
+
+	/* HQM Control Register */
+	BNAD_GET_REG(HQM0_CTL_REG);
+	BNAD_GET_REG(HQM0_RXQ_STOP_SEM);
+	BNAD_GET_REG(HQM0_TXQ_STOP_SEM);
+	BNAD_GET_REG(HQM1_CTL_REG);
+	BNAD_GET_REG(HQM1_RXQ_STOP_SEM);
+	BNAD_GET_REG(HQM1_TXQ_STOP_SEM);
+
+	/* LUT Registers */
+	BNAD_GET_REG(LUT0_ERR_STS);
+	BNAD_GET_REG(LUT0_SET_ERR_STS);
+	BNAD_GET_REG(LUT1_ERR_STS);
+	BNAD_GET_REG(LUT1_SET_ERR_STS);
+
+	/* TRC Registers */
+	BNAD_GET_REG(TRC_CTL_REG);
+	BNAD_GET_REG(TRC_MODS_REG);
+	BNAD_GET_REG(TRC_TRGC_REG);
+	BNAD_GET_REG(TRC_CNT1_REG);
+	BNAD_GET_REG(TRC_CNT2_REG);
+	BNAD_GET_REG(TRC_NXTS_REG);
+	BNAD_GET_REG(TRC_DIRR_REG);
+	for (i = 0; i < 10; i++)
+		BNAD_GET_REG(TRC_TRGM_REG(i));
+	for (i = 0; i < 10; i++)
+		BNAD_GET_REG(TRC_NXTM_REG(i));
+	for (i = 0; i < 10; i++)
+		BNAD_GET_REG(TRC_STRM_REG(i));
+
+	spin_unlock_irqrestore(&bnad->bna_lock, flags);
+#undef BNAD_GET_REG
+	return num;
+}
+static int
+bnad_get_regs_len(struct net_device *netdev)
+{
+	int ret = get_regs(netdev_priv(netdev), NULL) * sizeof(u32);
+	return ret;
+}
+
+static void
+bnad_get_regs(struct net_device *netdev, struct ethtool_regs *regs, void *buf)
+{
+	memset(buf, 0, bnad_get_regs_len(netdev));
+	get_regs(netdev_priv(netdev), buf);
+}
+
+static void
+bnad_get_wol(struct net_device *netdev, struct ethtool_wolinfo *wolinfo)
+{
+	wolinfo->supported = 0;
+	wolinfo->wolopts = 0;
+}
+
+static int
+bnad_get_coalesce(struct net_device *netdev, struct ethtool_coalesce *coalesce)
+{
+	struct bnad *bnad = netdev_priv(netdev);
+	unsigned long flags;
+
+	/* Lock rqd. to access bnad->bna_lock */
+	spin_lock_irqsave(&bnad->bna_lock, flags);
+	coalesce->use_adaptive_rx_coalesce =
+		(bnad->cfg_flags & BNAD_CF_DIM_ENABLED) ? true : false;
+	spin_unlock_irqrestore(&bnad->bna_lock, flags);
+
+	coalesce->rx_coalesce_usecs = bnad->rx_coalescing_timeo *
+					BFI_COALESCING_TIMER_UNIT;
+	coalesce->tx_coalesce_usecs = bnad->tx_coalescing_timeo *
+					BFI_COALESCING_TIMER_UNIT;
+	coalesce->tx_max_coalesced_frames = BFI_TX_INTERPKT_COUNT;
+
+	return 0;
+}
+
+static int
+bnad_set_coalesce(struct net_device *netdev, struct ethtool_coalesce *coalesce)
+{
+	struct bnad *bnad = netdev_priv(netdev);
+	unsigned long flags;
+	int dim_timer_del = 0;
+
+	if (coalesce->rx_coalesce_usecs == 0 ||
+	    coalesce->rx_coalesce_usecs >
+	    BFI_MAX_COALESCING_TIMEO * BFI_COALESCING_TIMER_UNIT)
+		return -EINVAL;
+
+	if (coalesce->tx_coalesce_usecs == 0 ||
+	    coalesce->tx_coalesce_usecs >
+	    BFI_MAX_COALESCING_TIMEO * BFI_COALESCING_TIMER_UNIT)
+		return -EINVAL;
+
+	mutex_lock(&bnad->conf_mutex);
+	/*
+	 * Do not need to store rx_coalesce_usecs here
+	 * Every time DIM is disabled, we can get it from the
+	 * stack.
+	 */
+	spin_lock_irqsave(&bnad->bna_lock, flags);
+	if (coalesce->use_adaptive_rx_coalesce) {
+		if (!(bnad->cfg_flags & BNAD_CF_DIM_ENABLED)) {
+			bnad->cfg_flags |= BNAD_CF_DIM_ENABLED;
+			bnad_dim_timer_start(bnad);
+		}
+	} else {
+		if (bnad->cfg_flags & BNAD_CF_DIM_ENABLED) {
+			bnad->cfg_flags &= ~BNAD_CF_DIM_ENABLED;
+			dim_timer_del = bnad_dim_timer_running(bnad);
+			if (dim_timer_del) {
+				clear_bit(BNAD_RF_DIM_TIMER_RUNNING,
+							&bnad->run_flags);
+				spin_unlock_irqrestore(&bnad->bna_lock, flags);
+				del_timer_sync(&bnad->dim_timer);
+				spin_lock_irqsave(&bnad->bna_lock, flags);
+			}
+			bnad_rx_coalescing_timeo_set(bnad);
+		}
+	}
+	if (bnad->tx_coalescing_timeo != coalesce->tx_coalesce_usecs /
+					BFI_COALESCING_TIMER_UNIT) {
+		bnad->tx_coalescing_timeo = coalesce->tx_coalesce_usecs /
+						BFI_COALESCING_TIMER_UNIT;
+		bnad_tx_coalescing_timeo_set(bnad);
+	}
+
+	if (bnad->rx_coalescing_timeo != coalesce->rx_coalesce_usecs /
+					BFI_COALESCING_TIMER_UNIT) {
+		bnad->rx_coalescing_timeo = coalesce->rx_coalesce_usecs /
+						BFI_COALESCING_TIMER_UNIT;
+
+		if (!(bnad->cfg_flags & BNAD_CF_DIM_ENABLED))
+			bnad_rx_coalescing_timeo_set(bnad);
+
+	}
+
+	/* Add Tx Inter-pkt DMA count?  */
+
+	spin_unlock_irqrestore(&bnad->bna_lock, flags);
+
+	mutex_unlock(&bnad->conf_mutex);
+	return 0;
+}
+
+static void
+bnad_get_ringparam(struct net_device *netdev,
+		   struct ethtool_ringparam *ringparam)
+{
+	struct bnad *bnad = netdev_priv(netdev);
+
+	ringparam->rx_max_pending = BNAD_MAX_Q_DEPTH / bnad_rxqs_per_cq;
+	ringparam->rx_mini_max_pending = 0;
+	ringparam->rx_jumbo_max_pending = 0;
+	ringparam->tx_max_pending = BNAD_MAX_Q_DEPTH;
+
+	ringparam->rx_pending = bnad->rxq_depth;
+	ringparam->rx_mini_max_pending = 0;
+	ringparam->rx_jumbo_max_pending = 0;
+	ringparam->tx_pending = bnad->txq_depth;
+}
+
+static int
+bnad_set_ringparam(struct net_device *netdev,
+		   struct ethtool_ringparam *ringparam)
+{
+	int i, current_err, err = 0;
+	struct bnad *bnad = netdev_priv(netdev);
+
+	mutex_lock(&bnad->conf_mutex);
+	if (ringparam->rx_pending == bnad->rxq_depth &&
+	    ringparam->tx_pending == bnad->txq_depth) {
+		mutex_unlock(&bnad->conf_mutex);
+		return 0;
+	}
+
+	if (ringparam->rx_pending < BNAD_MIN_Q_DEPTH ||
+	    ringparam->rx_pending > BNAD_MAX_Q_DEPTH / bnad_rxqs_per_cq ||
+	    !BNA_POWER_OF_2(ringparam->rx_pending)) {
+		mutex_unlock(&bnad->conf_mutex);
+		return -EINVAL;
+	}
+	if (ringparam->tx_pending < BNAD_MIN_Q_DEPTH ||
+	    ringparam->tx_pending > BNAD_MAX_Q_DEPTH ||
+	    !BNA_POWER_OF_2(ringparam->tx_pending)) {
+		mutex_unlock(&bnad->conf_mutex);
+		return -EINVAL;
+	}
+
+	if (ringparam->rx_pending != bnad->rxq_depth) {
+		bnad->rxq_depth = ringparam->rx_pending;
+		for (i = 0; i < bnad->num_rx; i++) {
+			if (!bnad->rx_info[i].rx)
+				continue;
+			bnad_cleanup_rx(bnad, i);
+			current_err = bnad_setup_rx(bnad, i);
+			if (current_err && !err)
+				err = current_err;
+		}
+	}
+	if (ringparam->tx_pending != bnad->txq_depth) {
+		bnad->txq_depth = ringparam->tx_pending;
+		for (i = 0; i < bnad->num_tx; i++) {
+			if (!bnad->tx_info[i].tx)
+				continue;
+			bnad_cleanup_tx(bnad, i);
+			current_err = bnad_setup_tx(bnad, i);
+			if (current_err && !err)
+				err = current_err;
+		}
+	}
+
+	mutex_unlock(&bnad->conf_mutex);
+	return err;
+}
+
+static void
+bnad_get_pauseparam(struct net_device *netdev,
+		    struct ethtool_pauseparam *pauseparam)
+{
+	struct bnad *bnad = netdev_priv(netdev);
+
+	pauseparam->autoneg = 0;
+	pauseparam->rx_pause = bnad->bna.port.pause_config.rx_pause;
+	pauseparam->tx_pause = bnad->bna.port.pause_config.tx_pause;
+}
+
+static int
+bnad_set_pauseparam(struct net_device *netdev,
+		    struct ethtool_pauseparam *pauseparam)
+{
+	struct bnad *bnad = netdev_priv(netdev);
+	struct bna_pause_config pause_config;
+	unsigned long flags;
+
+	if (pauseparam->autoneg == AUTONEG_ENABLE)
+		return -EINVAL;
+
+	mutex_lock(&bnad->conf_mutex);
+	if (pauseparam->rx_pause != bnad->bna.port.pause_config.rx_pause ||
+	    pauseparam->tx_pause != bnad->bna.port.pause_config.tx_pause) {
+		pause_config.rx_pause = pauseparam->rx_pause;
+		pause_config.tx_pause = pauseparam->tx_pause;
+		spin_lock_irqsave(&bnad->bna_lock, flags);
+		bna_port_pause_config(&bnad->bna.port, &pause_config, NULL);
+		spin_unlock_irqrestore(&bnad->bna_lock, flags);
+	}
+	mutex_unlock(&bnad->conf_mutex);
+	return 0;
+}
+
+static u32
+bnad_get_rx_csum(struct net_device *netdev)
+{
+	u32 rx_csum;
+	struct bnad *bnad = netdev_priv(netdev);
+
+	rx_csum = bnad->rx_csum;
+	return rx_csum;
+}
+
+static int
+bnad_set_rx_csum(struct net_device *netdev, u32 rx_csum)
+{
+	struct bnad *bnad = netdev_priv(netdev);
+
+	mutex_lock(&bnad->conf_mutex);
+	bnad->rx_csum = rx_csum;
+	mutex_unlock(&bnad->conf_mutex);
+	return 0;
+}
+
+static int
+bnad_set_tx_csum(struct net_device *netdev, u32 tx_csum)
+{
+	struct bnad *bnad = netdev_priv(netdev);
+
+	mutex_lock(&bnad->conf_mutex);
+	if (tx_csum) {
+		netdev->features |= NETIF_F_IP_CSUM;
+		netdev->features |= NETIF_F_IPV6_CSUM;
+	} else {
+		netdev->features &= ~NETIF_F_IP_CSUM;
+		netdev->features &= ~NETIF_F_IPV6_CSUM;
+	}
+	mutex_unlock(&bnad->conf_mutex);
+	return 0;
+}
+
+static int
+bnad_set_tso(struct net_device *netdev, u32 tso)
+{
+	struct bnad *bnad = netdev_priv(netdev);
+
+	mutex_lock(&bnad->conf_mutex);
+	if (tso) {
+		netdev->features |= NETIF_F_TSO;
+		netdev->features |= NETIF_F_TSO6;
+	} else {
+		netdev->features &= ~NETIF_F_TSO;
+		netdev->features &= ~NETIF_F_TSO6;
+	}
+	mutex_unlock(&bnad->conf_mutex);
+	return 0;
+}
+
+static void
+bnad_get_strings(struct net_device *netdev, u32 stringset, u8 * string)
+{
+	struct bnad *bnad = netdev_priv(netdev);
+	int i, j, q_num;
+	u64 bmap;
+
+	mutex_lock(&bnad->conf_mutex);
+
+	switch (stringset) {
+	case ETH_SS_STATS:
+		for (i = 0; i < BNAD_ETHTOOL_STATS_NUM; i++) {
+			BUG_ON(!(strlen(bnad_net_stats_strings[i]) <
+				   ETH_GSTRING_LEN));
+			memcpy(string, bnad_net_stats_strings[i],
+			       ETH_GSTRING_LEN);
+			string += ETH_GSTRING_LEN;
+		}
+		bmap = (u64)bnad->bna.tx_mod.txf_bmap[0] |
+			((u64)bnad->bna.tx_mod.txf_bmap[1] << 32);
+		for (i = 0; bmap && (i < BFI_LL_TXF_ID_MAX); i++) {
+			if (bmap & 1) {
+				sprintf(string, "txf%d_ucast_octets", i);
+				string += ETH_GSTRING_LEN;
+				sprintf(string, "txf%d_ucast", i);
+				string += ETH_GSTRING_LEN;
+				sprintf(string, "txf%d_ucast_vlan", i);
+				string += ETH_GSTRING_LEN;
+				sprintf(string, "txf%d_mcast_octets", i);
+				string += ETH_GSTRING_LEN;
+				sprintf(string, "txf%d_mcast", i);
+				string += ETH_GSTRING_LEN;
+				sprintf(string, "txf%d_mcast_vlan", i);
+				string += ETH_GSTRING_LEN;
+				sprintf(string, "txf%d_bcast_octets", i);
+				string += ETH_GSTRING_LEN;
+				sprintf(string, "txf%d_bcast", i);
+				string += ETH_GSTRING_LEN;
+				sprintf(string, "txf%d_bcast_vlan", i);
+				string += ETH_GSTRING_LEN;
+				sprintf(string, "txf%d_errors", i);
+				string += ETH_GSTRING_LEN;
+				sprintf(string, "txf%d_filter_vlan", i);
+				string += ETH_GSTRING_LEN;
+				sprintf(string, "txf%d_filter_mac_sa", i);
+				string += ETH_GSTRING_LEN;
+			}
+			bmap >>= 1;
+		}
+
+		bmap = (u64)bnad->bna.rx_mod.rxf_bmap[0] |
+			((u64)bnad->bna.rx_mod.rxf_bmap[1] << 32);
+		for (i = 0; bmap && (i < BFI_LL_RXF_ID_MAX); i++) {
+			if (bmap & 1) {
+				sprintf(string, "rxf%d_ucast_octets", i);
+				string += ETH_GSTRING_LEN;
+				sprintf(string, "rxf%d_ucast", i);
+				string += ETH_GSTRING_LEN;
+				sprintf(string, "rxf%d_ucast_vlan", i);
+				string += ETH_GSTRING_LEN;
+				sprintf(string, "rxf%d_mcast_octets", i);
+				string += ETH_GSTRING_LEN;
+				sprintf(string, "rxf%d_mcast", i);
+				string += ETH_GSTRING_LEN;
+				sprintf(string, "rxf%d_mcast_vlan", i);
+				string += ETH_GSTRING_LEN;
+				sprintf(string, "rxf%d_bcast_octets", i);
+				string += ETH_GSTRING_LEN;
+				sprintf(string, "rxf%d_bcast", i);
+				string += ETH_GSTRING_LEN;
+				sprintf(string, "rxf%d_bcast_vlan", i);
+				string += ETH_GSTRING_LEN;
+				sprintf(string, "rxf%d_frame_drops", i);
+				string += ETH_GSTRING_LEN;
+			}
+			bmap >>= 1;
+		}
+
+		q_num = 0;
+		for (i = 0; i < bnad->num_rx; i++) {
+			if (!bnad->rx_info[i].rx)
+				continue;
+			for (j = 0; j < bnad->num_rxp_per_rx; j++) {
+				sprintf(string, "cq%d_producer_index", q_num);
+				string += ETH_GSTRING_LEN;
+				sprintf(string, "cq%d_consumer_index", q_num);
+				string += ETH_GSTRING_LEN;
+				sprintf(string, "cq%d_hw_producer_index",
+					q_num);
+				string += ETH_GSTRING_LEN;
+				q_num++;
+			}
+		}
+
+		q_num = 0;
+		for (i = 0; i < bnad->num_rx; i++) {
+			if (!bnad->rx_info[i].rx)
+				continue;
+			for (j = 0; j < bnad->num_rxp_per_rx; j++) {
+				sprintf(string, "rxq%d_packets", q_num);
+				string += ETH_GSTRING_LEN;
+				sprintf(string, "rxq%d_bytes", q_num);
+				string += ETH_GSTRING_LEN;
+				sprintf(string, "rxq%d_packets_with_error",
+								q_num);
+				string += ETH_GSTRING_LEN;
+				sprintf(string, "rxq%d_allocbuf_failed", q_num);
+				string += ETH_GSTRING_LEN;
+				sprintf(string, "rxq%d_producer_index", q_num);
+				string += ETH_GSTRING_LEN;
+				sprintf(string, "rxq%d_consumer_index", q_num);
+				string += ETH_GSTRING_LEN;
+				q_num++;
+				if (bnad->rx_info[i].rx_ctrl[j].ccb &&
+					bnad->rx_info[i].rx_ctrl[j].ccb->
+					rcb[1] &&
+					bnad->rx_info[i].rx_ctrl[j].ccb->
+					rcb[1]->rxq) {
+					sprintf(string, "rxq%d_packets", q_num);
+					string += ETH_GSTRING_LEN;
+					sprintf(string, "rxq%d_bytes", q_num);
+					string += ETH_GSTRING_LEN;
+					sprintf(string,
+					"rxq%d_packets_with_error", q_num);
+					string += ETH_GSTRING_LEN;
+					sprintf(string, "rxq%d_allocbuf_failed",
+								q_num);
+					string += ETH_GSTRING_LEN;
+					sprintf(string, "rxq%d_producer_index",
+								q_num);
+					string += ETH_GSTRING_LEN;
+					sprintf(string, "rxq%d_consumer_index",
+								q_num);
+					string += ETH_GSTRING_LEN;
+					q_num++;
+				}
+			}
+		}
+
+		q_num = 0;
+		for (i = 0; i < bnad->num_tx; i++) {
+			if (!bnad->tx_info[i].tx)
+				continue;
+			for (j = 0; j < bnad->num_txq_per_tx; j++) {
+				sprintf(string, "txq%d_packets", q_num);
+				string += ETH_GSTRING_LEN;
+				sprintf(string, "txq%d_bytes", q_num);
+				string += ETH_GSTRING_LEN;
+				sprintf(string, "txq%d_producer_index", q_num);
+				string += ETH_GSTRING_LEN;
+				sprintf(string, "txq%d_consumer_index", q_num);
+				string += ETH_GSTRING_LEN;
+				sprintf(string, "txq%d_hw_consumer_index",
+									q_num);
+				string += ETH_GSTRING_LEN;
+				q_num++;
+			}
+		}
+
+		break;
+
+	default:
+		break;
+	}
+
+	mutex_unlock(&bnad->conf_mutex);
+}
+
+static int
+bnad_get_stats_count_locked(struct net_device *netdev)
+{
+	struct bnad *bnad = netdev_priv(netdev);
+	int i, j, count, rxf_active_num = 0, txf_active_num = 0;
+	u64 bmap;
+
+	bmap = (u64)bnad->bna.tx_mod.txf_bmap[0] |
+			((u64)bnad->bna.tx_mod.txf_bmap[1] << 32);
+	for (i = 0; bmap && (i < BFI_LL_TXF_ID_MAX); i++) {
+		if (bmap & 1)
+			txf_active_num++;
+		bmap >>= 1;
+	}
+	bmap = (u64)bnad->bna.rx_mod.rxf_bmap[0] |
+			((u64)bnad->bna.rx_mod.rxf_bmap[1] << 32);
+	for (i = 0; bmap && (i < BFI_LL_RXF_ID_MAX); i++) {
+		if (bmap & 1)
+			rxf_active_num++;
+		bmap >>= 1;
+	}
+	count = BNAD_ETHTOOL_STATS_NUM +
+		txf_active_num * BNAD_NUM_TXF_COUNTERS +
+		rxf_active_num * BNAD_NUM_RXF_COUNTERS;
+
+	for (i = 0; i < bnad->num_rx; i++) {
+		if (!bnad->rx_info[i].rx)
+			continue;
+		count += bnad->num_rxp_per_rx * BNAD_NUM_CQ_COUNTERS;
+		count += bnad->num_rxp_per_rx * BNAD_NUM_RXQ_COUNTERS;
+		for (j = 0; j < bnad->num_rxp_per_rx; j++)
+			if (bnad->rx_info[i].rx_ctrl[j].ccb &&
+				bnad->rx_info[i].rx_ctrl[j].ccb->rcb[1] &&
+				bnad->rx_info[i].rx_ctrl[j].ccb->rcb[1]->rxq)
+				count +=  BNAD_NUM_RXQ_COUNTERS;
+	}
+
+	for (i = 0; i < bnad->num_tx; i++) {
+		if (!bnad->tx_info[i].tx)
+			continue;
+		count += bnad->num_txq_per_tx * BNAD_NUM_TXQ_COUNTERS;
+	}
+	return count;
+}
+
+static int
+bnad_per_q_stats_fill(struct bnad *bnad, u64 *buf, int bi)
+{
+	int i, j;
+	struct bna_rcb *rcb = NULL;
+	struct bna_tcb *tcb = NULL;
+
+	for (i = 0; i < bnad->num_rx; i++) {
+		if (!bnad->rx_info[i].rx)
+			continue;
+		for (j = 0; j < bnad->num_rxp_per_rx; j++)
+			if (bnad->rx_info[i].rx_ctrl[j].ccb &&
+				bnad->rx_info[i].rx_ctrl[j].ccb->rcb[0] &&
+				bnad->rx_info[i].rx_ctrl[j].ccb->rcb[0]->rxq) {
+				buf[bi++] = bnad->rx_info[i].rx_ctrl[j].
+						ccb->producer_index;
+				buf[bi++] = 0; /* ccb->consumer_index */
+				buf[bi++] = *(bnad->rx_info[i].rx_ctrl[j].
+						ccb->hw_producer_index);
+			}
+	}
+	for (i = 0; i < bnad->num_rx; i++) {
+		if (!bnad->rx_info[i].rx)
+			continue;
+		for (j = 0; j < bnad->num_rxp_per_rx; j++)
+			if (bnad->rx_info[i].rx_ctrl[j].ccb) {
+				if (bnad->rx_info[i].rx_ctrl[j].ccb->rcb[0] &&
+					bnad->rx_info[i].rx_ctrl[j].ccb->
+					rcb[0]->rxq) {
+					rcb = bnad->rx_info[i].rx_ctrl[j].
+							ccb->rcb[0];
+					buf[bi++] = rcb->rxq->rx_packets;
+					buf[bi++] = rcb->rxq->rx_bytes;
+					buf[bi++] = rcb->rxq->
+							rx_packets_with_error;
+					buf[bi++] = rcb->rxq->
+							rxbuf_alloc_failed;
+					buf[bi++] = rcb->producer_index;
+					buf[bi++] = rcb->consumer_index;
+				}
+				if (bnad->rx_info[i].rx_ctrl[j].ccb->rcb[1] &&
+					bnad->rx_info[i].rx_ctrl[j].ccb->
+					rcb[1]->rxq) {
+					rcb = bnad->rx_info[i].rx_ctrl[j].
+								ccb->rcb[1];
+					buf[bi++] = rcb->rxq->rx_packets;
+					buf[bi++] = rcb->rxq->rx_bytes;
+					buf[bi++] = rcb->rxq->
+							rx_packets_with_error;
+					buf[bi++] = rcb->rxq->
+							rxbuf_alloc_failed;
+					buf[bi++] = rcb->producer_index;
+					buf[bi++] = rcb->consumer_index;
+				}
+			}
+	}
+
+	for (i = 0; i < bnad->num_tx; i++) {
+		if (!bnad->tx_info[i].tx)
+			continue;
+		for (j = 0; j < bnad->num_txq_per_tx; j++)
+			if (bnad->tx_info[i].tcb[j] &&
+				bnad->tx_info[i].tcb[j]->txq) {
+				tcb = bnad->tx_info[i].tcb[j];
+				buf[bi++] = tcb->txq->tx_packets;
+				buf[bi++] = tcb->txq->tx_bytes;
+				buf[bi++] = tcb->producer_index;
+				buf[bi++] = tcb->consumer_index;
+				buf[bi++] = *(tcb->hw_consumer_index);
+			}
+	}
+
+	return bi;
+}
+
+static void
+bnad_get_ethtool_stats(struct net_device *netdev, struct ethtool_stats *stats,
+		       u64 *buf)
+{
+	struct bnad *bnad = netdev_priv(netdev);
+	int i, j, bi;
+	unsigned long *net_stats, flags;
+	u64 *stats64;
+	u64 bmap;
+
+	mutex_lock(&bnad->conf_mutex);
+	if (bnad_get_stats_count_locked(netdev) != stats->n_stats) {
+		mutex_unlock(&bnad->conf_mutex);
+		return;
+	}
+
+	/*
+	 * Used bna_lock to sync reads from bna_stats, which is written
+	 * under the same lock
+	 */
+	spin_lock_irqsave(&bnad->bna_lock, flags);
+	bi = 0;
+	memset(buf, 0, stats->n_stats * sizeof(u64));
+	memset(&bnad->net_stats, 0, sizeof(struct net_device_stats));
+
+	bnad_netdev_qstats_fill(bnad);
+	bnad_netdev_hwstats_fill(bnad);
+
+	/* Fill net_stats into ethtool buffers */
+	net_stats = (unsigned long *)&bnad->net_stats;
+	for (i = 0; i < sizeof(struct net_device_stats) / sizeof(unsigned long);
+	     i++)
+		buf[bi++] = net_stats[i];
+
+	/* Fill driver stats into ethtool buffers */
+	stats64 = (u64 *)&bnad->stats.drv_stats;
+	for (i = 0; i < sizeof(struct bnad_drv_stats) / sizeof(u64); i++)
+		buf[bi++] = stats64[i];
+
+	/* Fill hardware stats excluding the rxf/txf into ethtool bufs */
+	stats64 = (u64 *) bnad->stats.bna_stats->hw_stats;
+	for (i = 0;
+	     i < offsetof(struct bfi_ll_stats, rxf_stats[0]) / sizeof(u64);
+	     i++)
+		buf[bi++] = stats64[i];
+
+	/* Fill txf stats into ethtool buffers */
+	bmap = (u64)bnad->bna.tx_mod.txf_bmap[0] |
+			((u64)bnad->bna.tx_mod.txf_bmap[1] << 32);
+	for (i = 0; bmap && (i < BFI_LL_TXF_ID_MAX); i++) {
+		if (bmap & 1) {
+			stats64 = (u64 *)&bnad->stats.bna_stats->
+						hw_stats->txf_stats[i];
+			for (j = 0; j < sizeof(struct bfi_ll_stats_txf) /
+					sizeof(u64); j++)
+				buf[bi++] = stats64[j];
+		}
+		bmap >>= 1;
+	}
+
+	/*  Fill rxf stats into ethtool buffers */
+	bmap = (u64)bnad->bna.rx_mod.rxf_bmap[0] |
+			((u64)bnad->bna.rx_mod.rxf_bmap[1] << 32);
+	for (i = 0; bmap && (i < BFI_LL_RXF_ID_MAX); i++) {
+		if (bmap & 1) {
+			stats64 = (u64 *)&bnad->stats.bna_stats->
+						hw_stats->rxf_stats[i];
+			for (j = 0; j < sizeof(struct bfi_ll_stats_rxf) /
+					sizeof(u64); j++)
+				buf[bi++] = stats64[j];
+		}
+		bmap >>= 1;
+	}
+
+	/* Fill per Q stats into ethtool buffers */
+	bi = bnad_per_q_stats_fill(bnad, buf, bi);
+
+	spin_unlock_irqrestore(&bnad->bna_lock, flags);
+
+	mutex_unlock(&bnad->conf_mutex);
+}
+
+static int
+bnad_get_sset_count(struct net_device *netdev, int sset)
+{
+	switch (sset) {
+	case ETH_SS_STATS:
+		return bnad_get_stats_count_locked(netdev);
+	default:
+		return -EOPNOTSUPP;
+	}
+}
+
+static struct ethtool_ops bnad_ethtool_ops = {
+	.get_settings = bnad_get_settings,
+	.set_settings = bnad_set_settings,
+	.get_drvinfo = bnad_get_drvinfo,
+	.get_regs_len = bnad_get_regs_len,
+	.get_regs = bnad_get_regs,
+	.get_wol = bnad_get_wol,
+	.get_link = ethtool_op_get_link,
+	.get_coalesce = bnad_get_coalesce,
+	.set_coalesce = bnad_set_coalesce,
+	.get_ringparam = bnad_get_ringparam,
+	.set_ringparam = bnad_set_ringparam,
+	.get_pauseparam = bnad_get_pauseparam,
+	.set_pauseparam = bnad_set_pauseparam,
+	.get_rx_csum = bnad_get_rx_csum,
+	.set_rx_csum = bnad_set_rx_csum,
+	.get_tx_csum = ethtool_op_get_tx_csum,
+	.set_tx_csum = bnad_set_tx_csum,
+	.get_sg = ethtool_op_get_sg,
+	.set_sg = ethtool_op_set_sg,
+	.get_tso = ethtool_op_get_tso,
+	.set_tso = bnad_set_tso,
+	.get_flags = ethtool_op_get_flags,
+	.set_flags = ethtool_op_set_flags,
+	.get_strings = bnad_get_strings,
+	.get_ethtool_stats = bnad_get_ethtool_stats,
+	.get_sset_count = bnad_get_sset_count
+};
+
+void
+bnad_set_ethtool_ops(struct net_device *netdev)
+{
+	SET_ETHTOOL_OPS(netdev, &bnad_ethtool_ops);
+}

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH 1/6] bna: Brocade 10Gb Ethernet device driver
  2010-08-11  4:24 Rasesh Mody
@ 2010-08-18  0:41 ` David Miller
  0 siblings, 0 replies; 8+ messages in thread
From: David Miller @ 2010-08-18  0:41 UTC (permalink / raw)
  To: rmody; +Cc: netdev, adapter_linux_open_src_team

From: Rasesh Mody <rmody@brocade.com>
Date: Tue, 10 Aug 2010 21:24:36 -0700

> +u32 bna_id;

This variable is never set by the driver.

It is always left at it's default value of zero and it is only
used to construct MBOX irq name strings and it is also assigned
to bnad->ident.id

I suspect it's completely useless, and if so please just delete
it.

^ permalink raw reply	[flat|nested] 8+ messages in thread

* RE: [PATCH 1/6] bna: Brocade 10Gb Ethernet device driver
  2010-08-09 15:15 ` Stephen Hemminger
@ 2010-08-11 19:45   ` Debashis Dutt
  0 siblings, 0 replies; 8+ messages in thread
From: Debashis Dutt @ 2010-08-11 19:45 UTC (permalink / raw)
  To: Stephen Hemminger; +Cc: netdev

Stephen, 

Thanks a lot for explaining this.

--Debashis

-----Original Message-----
From: Stephen Hemminger [mailto:shemminger@vyatta.com] 
Sent: Monday, August 09, 2010 8:15 AM
To: Debashis Dutt
Cc: netdev@vger.kernel.org
Subject: Re: [PATCH 1/6] bna: Brocade 10Gb Ethernet device driver

On Fri, 6 Aug 2010 20:23:21 -0700
Debashis Dutt <ddutt@Brocade.COM> wrote:

> 
> Hi Stephen, 
> 
> Thanks a lot for your comments. 
> 
> > +                   if (likely
> > +                       (wis > BNA_QE_FREE_CNT(tcb, tcb->q_depth) ||
> > +                        vectors > BNA_QE_FREE_CNT(unmap_q, unmap_q->q_depth))) {
> > +                               BNAD_UPDATE_CTR(bnad, netif_queue_stop);
> > +                               return NETDEV_TX_BUSY;
> 
> >The transmit routine should check for available space after
> > queueing to device, so you can avoid having to return
> >TX_BUSY.
> 
> However your above comment is not very clear to me.
> 
> Our Tx routine already cleans up the Tx buffers at the end, through a tasklet.
> 
> Cleaning of Tx buffers happens 
> 1) In the sending context at the end of the Tx routine.
> 2) In the IRQ context when we get a Tx completion interrupt.
> 
> Only thing that we could have done, is do this check again at the end of the Tx routine
> and called netif_stop_queue() if required, so that the stack stops sending. Even then I am 
> not sure that we could avoid the current check and returning TX_BUSY.
> 
> It would be nice if you clarify, so that I understand this better.
> 
> Thanks
> --Debashis
> Linux LL Driver Team.
> 
> 
> -----Original Message-----
> From: Stephen Hemminger [mailto:shemminger@vyatta.com] 
> Sent: Wednesday, August 04, 2010 10:09 AM
> To: Rasesh Mody
> Cc: netdev@vger.kernel.org; Debashis Dutt; Jing Huang
> Subject: Re: [PATCH 1/6] bna: Brocade 10Gb Ethernet device driver
> 
> On Tue, 3 Aug 2010 22:15:36 -0700
> Rasesh Mody <rmody@brocade.com> wrote:
> 
> > From: Rasesh Mody <rmody@brocade.com>
> > 
> > This is patch 1/6 which contains linux driver source for
> > Brocade's BR1010/BR1020 10Gb CEE capable ethernet adapter.
> > Source is based against net-next-2.6.
> > 
> > We wish this patch to be considered for inclusion in net-next-2.6
> > 
> > Signed-off-by: Rasesh Mody <rmody@brocade.com>
> > ---
> >  bnad.c         | 3326 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
> >  bnad.h         |  474 ++++++++
> >  bnad_ethtool.c | 1269 +++++++++++++++++++++
> >  3 files changed, 5069 insertions(+)
> > 
> > diff -ruP net-next-2.6.35-rc1-orig/drivers/net/bna/bnad.c net-next-2.6.35-rc1-mod/drivers/net/bna/bnad.c
> > --- net-next-2.6.35-rc1-orig/drivers/net/bna/bnad.c         1969-12-31 16:00:00.000000000 -0800
> > +++ net-next-2.6.35-rc1-mod/drivers/net/bna/bnad.c     2010-08-02 17:19:19.447239000 -0700
> > @@ -0,0 +1,3326 @@
> > +/*
> > + * Linux network driver for Brocade Converged Network Adapter.
> > + *
> > + * This program is free software; you can redistribute it and/or modify it
> > + * under the terms of the GNU General Public License (GPL) Version 2 as
> > + * published by the Free Software Foundation
> > + *
> > + * This program is distributed in the hope that it will be useful, but
> > + * WITHOUT ANY WARRANTY; without even the implied warranty of
> > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> > + * General Public License for more details.
> > + */
> > +/*
> > + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc.
> > + * All rights reserved
> > + * www.brocade.com
> > + */
> > +#include <linux/netdevice.h>
> > +#include <linux/skbuff.h>
> > +#include <linux/etherdevice.h>
> > +#include <linux/in.h>
> > +#include <linux/ethtool.h>
> > +#include <linux/if_vlan.h>
> > +#include <linux/if_ether.h>
> > +#include <linux/ip.h>
> > +
> > +#include "bnad.h"
> > +#include "bna.h"
> > +#include "cna.h"
> > +
> > +DEFINE_MUTEX(bnad_fwimg_mutex);
> > +
> > +/*
> > + * Module params
> > + */
> > +static uint bnad_msix_disable;
> > +module_param(bnad_msix_disable, uint, 0444);
> > +MODULE_PARM_DESC(bnad_msix_disable, "Disable MSIX mode");
> > +
> > +static uint bnad_ioc_auto_recover = 1;
> > +module_param(bnad_ioc_auto_recover, uint, 0444);
> > +MODULE_PARM_DESC(bnad_ioc_auto_recover, "Enable / Disable auto recovery");
> > +
> > +/*
> > + * Global variables
> > + */
> > +u32 bna_id;
> > +u32 bnad_rxqs_per_cq = 2;
> > +
> > +DECLARE_MUTEX(bnad_list_sem);
> > +LIST_HEAD(bnad_list);
> > +
> > +const u8 bnad_bcast_addr[] =  {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
> 
> Surprised this isn't defined somewhere else.
> 
> > +
> > +/*
> > + * Local MACROS
> > + */
> > +#define BNAD_TX_UNMAPQ_DEPTH (bnad->txq_depth * 2)
> > +
> > +#define BNAD_RX_UNMAPQ_DEPTH (bnad->rxq_depth)
> > +
> > +#define BNAD_GET_MBOX_IRQ(_bnad)                                            \
> > +       (((_bnad)->cfg_flags & BNAD_CF_MSIX) ?                                 \
> > +       ((_bnad)->msix_table[(_bnad)->msix_num - 1].vector) :   \
> > +       ((_bnad)->pcidev->irq))
> > +
> > +#define BNAD_FILL_UNMAPQ_MEM_REQ(_res_info, _num, _depth)  \
> > +do {                                                                                     \
> > +       (_res_info)->res_type = BNA_RES_T_MEM;                               \
> > +       (_res_info)->res_u.mem_info.mem_type = BNA_MEM_T_KVA;    \
> > +       (_res_info)->res_u.mem_info.num = (_num);                   \
> > +       (_res_info)->res_u.mem_info.len =                                 \
> > +       sizeof(struct bnad_unmap_q) +                                      \
> > +       (sizeof(struct bnad_skb_unmap) * ((_depth) - 1));            \
> > +} while (0)
> > +
> > +void
> > +bnad_add_to_list(struct bnad *bnad)
> > +{
> > +       down(&bnad_list_sem);
> > +       list_add_tail(&bnad->list_entry, &bnad_list);
> > +       bna_id++;
> > +       up(&bnad_list_sem);
> > +}
> 
> Why do you need to list semaphore? Isn't RTNL mutex held
> when this is done. If you have to have own exclusion use
> a mutex for this.
> 
> > +void
> > +bnad_remove_from_list(struct bnad *bnad)
> > +{
> > +       down(&bnad_list_sem);
> > +       list_del(&bnad->list_entry);
> > +       up(&bnad_list_sem);
> > +}
> > +
> > +const struct pci_device_id bnad_pci_id_table[] = {
> > +       {
> > +                   PCI_DEVICE(PCI_VENDOR_ID_BROCADE,
> > +                               PCI_DEVICE_ID_BROCADE_CT),
> > +                   .class = PCI_CLASS_NETWORK_ETHERNET << 8,
> > +                   .class_mask =  0xffff00
> > +       }, {0,  }
> > +};
> 
> Why is this not static?
> 
> 
> > +/* TX */
> > +/* bnad_start_xmit : Netdev entry point for Transmit */
> > +/*                      Called under lock held by net_device */
> > +
> > +netdev_tx_t
> > +bnad_start_xmit(struct sk_buff *skb, struct net_device *netdev)
> 
> Should also be static...
> 
> > +{
> > +       struct bnad *bnad = netdev_priv(netdev);
> > +
> > +       u16                   txq_prod, vlan_tag = 0;
> > +       u32                   unmap_prod, wis, wis_used, wi_range;
> > +       u32                   vectors, vect_id, i, acked;
> > +       u32                   tx_id;
> > +       int                                 err;
> > +
> > +       struct bnad_tx_info *tx_info;
> > +       struct bna_tcb *tcb;
> > +       struct bnad_unmap_q *unmap_q;
> > +       dma_addr_t                  dma_addr;
> > +       struct bna_txq_entry *txqent;
> > +       bna_txq_wi_ctrl_flag_t   flags;
> > +
> > +       if (unlikely
> > +           (skb->len <= ETH_HLEN || skb->len > BFI_TX_MAX_DATA_PER_PKT)) {
> > +                   dev_kfree_skb(skb);
> > +                   return NETDEV_TX_OK;
> > +       }
> > +
> > +       /*
> > +       * Takes care of the Tx that is scheduled between clearing the flag
> > +       * and the netif_stop_queue() call.
> > +       */
> > +       if (unlikely(!test_bit(BNAD_RF_TX_STARTED, &bnad->run_flags))) {
> > +                   dev_kfree_skb(skb);
> > +                   return NETDEV_TX_OK;
> > +       }
> > +
> > +       tx_id = BNAD_GET_TX_ID(skb);
> > +
> > +       tx_info = &bnad->tx_info[tx_id];
> > +       tcb = tx_info->tcb[tx_id];
> > +       unmap_q = tcb->unmap_q;
> > +
> > +       vectors = 1 + skb_shinfo(skb)->nr_frags;
> > +       if (vectors > BFI_TX_MAX_VECTORS_PER_PKT) {
> > +                   dev_kfree_skb(skb);
> > +                   return NETDEV_TX_OK;
> > +       }
> > +       wis = BNA_TXQ_WI_NEEDED(vectors); /* 4 vectors per work item */
> > +       acked = 0;
> > +       if (unlikely
> > +           (wis > BNA_QE_FREE_CNT(tcb, tcb->q_depth) ||
> > +            vectors > BNA_QE_FREE_CNT(unmap_q, unmap_q->q_depth))) {
> > +                   if ((u16) (*tcb->hw_consumer_index) !=
> > +                       tcb->consumer_index &&
> > +                       !test_and_set_bit(BNAD_TXQ_FREE_SENT, &tcb->flags)) {
> > +                               acked = bnad_free_txbufs(bnad, tcb);
> > +                               bna_ib_ack(tcb->i_dbell, acked);
> > +                               smp_mb__before_clear_bit();
> > +                               clear_bit(BNAD_TXQ_FREE_SENT, &tcb->flags);
> > +                   } else {
> > +                               netif_stop_queue(netdev);
> > +                               BNAD_UPDATE_CTR(bnad, netif_queue_stop);
> > +                   }
> > +
> > +                   smp_mb();
> > +                   /*
> > +                   * Check again to deal with race condition between
> > +                   * netif_stop_queue here, and netif_wake_queue in
> > +                   * interrupt handler which is not inside netif tx lock.
> > +                   */
> > +                   if (likely
> > +                       (wis > BNA_QE_FREE_CNT(tcb, tcb->q_depth) ||
> > +                        vectors > BNA_QE_FREE_CNT(unmap_q, unmap_q->q_depth))) {
> > +                               BNAD_UPDATE_CTR(bnad, netif_queue_stop);
> > +                               return NETDEV_TX_BUSY;
> 
> The transmit routine should check for available space after
> queueing to device, so you can avoid having to return
> TX_BUSY.


The problem is that if device returns TX_BUSY, the net transmit scheduler
will end up re-calling the transmit routine.  This looks ok in your driver
because it will set netif_stop_queue

^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH 1/6] bna: Brocade 10Gb Ethernet device driver
@ 2010-08-11  4:24 Rasesh Mody
  2010-08-18  0:41 ` David Miller
  0 siblings, 1 reply; 8+ messages in thread
From: Rasesh Mody @ 2010-08-11  4:24 UTC (permalink / raw)
  To: netdev; +Cc: adapter_linux_open_src_team

From: Rasesh Mody <rmody@brocade.com>

This is patch 1/6 which contains linux driver source for
Brocade's BR1010/BR1020 10Gb CEE capable ethernet adapter.
Source is based against net-next-2.6.

We wish this patch to be considered for inclusion in net-next-2.6

Signed-off-by: Rasesh Mody <rmody@brocade.com>
---
 bnad.c         | 3300 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 bnad.h         |  339 +++++
 bnad_ethtool.c | 1282 ++++++++++++++++++++++
 3 files changed, 4921 insertions(+)

diff -ruP net-next-2.6.35-rc1-orig/drivers/net/bna/bnad.c net-next-2.6.35-rc1-mod/drivers/net/bna/bnad.c
--- net-next-2.6.35-rc1-orig/drivers/net/bna/bnad.c	1969-12-31 16:00:00.000000000 -0800
+++ net-next-2.6.35-rc1-mod/drivers/net/bna/bnad.c	2010-08-10 12:23:09.162215000 -0700
@@ -0,0 +1,3300 @@
+/*
+ * Linux network driver for Brocade Converged Network Adapter.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License (GPL) Version 2 as
+ * published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+/*
+ * Copyright (c) 2005-2009 Brocade Communications Systems, Inc.
+ * All rights reserved
+ * www.brocade.com
+ */
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <linux/etherdevice.h>
+#include <linux/in.h>
+#include <linux/ethtool.h>
+#include <linux/if_vlan.h>
+#include <linux/if_ether.h>
+#include <linux/ip.h>
+
+#include "bnad.h"
+#include "bna.h"
+#include "cna.h"
+
+DEFINE_MUTEX(bnad_fwimg_mutex);
+
+/*
+ * Module params
+ */
+static uint bnad_msix_disable;
+module_param(bnad_msix_disable, uint, 0444);
+MODULE_PARM_DESC(bnad_msix_disable, "Disable MSIX mode");
+
+static uint bnad_ioc_auto_recover = 1;
+module_param(bnad_ioc_auto_recover, uint, 0444);
+MODULE_PARM_DESC(bnad_ioc_auto_recover, "Enable / Disable auto recovery");
+
+/*
+ * Global variables
+ */
+u32 bna_id;
+u32 bnad_rxqs_per_cq = 2;
+
+DECLARE_MUTEX(bnad_list_sem);
+LIST_HEAD(bnad_list);
+
+const u8 bnad_bcast_addr[] =  {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+
+/*
+ * Local MACROS
+ */
+#define BNAD_TX_UNMAPQ_DEPTH (bnad->txq_depth * 2)
+
+#define BNAD_RX_UNMAPQ_DEPTH (bnad->rxq_depth)
+
+#define BNAD_GET_MBOX_IRQ(_bnad)				\
+	(((_bnad)->cfg_flags & BNAD_CF_MSIX) ?			\
+	 ((_bnad)->msix_table[(_bnad)->msix_num - 1].vector) : 	\
+	 ((_bnad)->pcidev->irq))
+
+#define BNAD_FILL_UNMAPQ_MEM_REQ(_res_info, _num, _depth)	\
+do {								\
+	(_res_info)->res_type = BNA_RES_T_MEM;			\
+	(_res_info)->res_u.mem_info.mem_type = BNA_MEM_T_KVA;	\
+	(_res_info)->res_u.mem_info.num = (_num);		\
+	(_res_info)->res_u.mem_info.len =			\
+	sizeof(struct bnad_unmap_q) +				\
+	(sizeof(struct bnad_skb_unmap) * ((_depth) - 1));	\
+} while (0)
+
+/*
+ * Reinitialize completions in CQ, once Rx is taken down
+ */
+static void
+bnad_cq_cmpl_init(struct bnad *bnad, struct bna_ccb *ccb)
+{
+	struct bna_cq_entry *cmpl, *next_cmpl;
+	unsigned int wi_range, wis = 0, ccb_prod = 0;
+	int i;
+
+	BNA_CQ_QPGE_PTR_GET(ccb_prod, ccb->sw_qpt, cmpl,
+			    wi_range);
+
+	for (i = 0; i < ccb->q_depth; i++) {
+		wis++;
+		if (likely(--wi_range))
+			next_cmpl = cmpl + 1;
+		else {
+			BNA_QE_INDX_ADD(ccb_prod, wis, ccb->q_depth);
+			wis = 0;
+			BNA_CQ_QPGE_PTR_GET(ccb_prod, ccb->sw_qpt,
+						next_cmpl, wi_range);
+		}
+		cmpl->valid = 0;
+		cmpl = next_cmpl;
+	}
+}
+
+/*
+ * Frees all pending Tx Bufs
+ * At this point no activity is expected on the Q,
+ * so DMA unmap & freeing is fine.
+ */
+static void
+bnad_free_all_txbufs(struct bnad *bnad,
+		 struct bna_tcb *tcb)
+{
+	u16 		unmap_cons;
+	struct bnad_unmap_q *unmap_q = tcb->unmap_q;
+	struct bnad_skb_unmap *unmap_array;
+	struct sk_buff 		*skb = NULL;
+	int			i;
+
+	unmap_array = unmap_q->unmap_array;
+
+	unmap_cons = 0;
+	while (unmap_cons < unmap_q->q_depth) {
+		skb = unmap_array[unmap_cons].skb;
+		if (!skb) {
+			unmap_cons++;
+			continue;
+		}
+		unmap_array[unmap_cons].skb = NULL;
+
+		pci_unmap_single(bnad->pcidev,
+				 pci_unmap_addr(&unmap_array[unmap_cons],
+						dma_addr), skb_headlen(skb),
+						PCI_DMA_TODEVICE);
+
+		pci_unmap_addr_set(&unmap_array[unmap_cons], dma_addr, 0);
+		unmap_cons++;
+		for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
+			pci_unmap_page(bnad->pcidev,
+				       pci_unmap_addr(&unmap_array[unmap_cons],
+						      dma_addr),
+				       skb_shinfo(skb)->frags[i].size,
+				       PCI_DMA_TODEVICE);
+			pci_unmap_addr_set(&unmap_array[unmap_cons], dma_addr,
+					   0);
+			unmap_cons++;
+		}
+		dev_kfree_skb_any(skb);
+	}
+}
+
+/* Data Path Handlers */
+
+/*
+ * bnad_free_txbufs : Frees the Tx bufs on Tx completion
+ * Can be called in a) Interrupt context
+ *		    b) Sending context
+ *		    c) Tasklet context
+ */
+static u32
+bnad_free_txbufs(struct bnad *bnad,
+		 struct bna_tcb *tcb)
+{
+	u32 		sent_packets = 0, sent_bytes = 0;
+	u16 		wis, unmap_cons, updated_hw_cons;
+	struct bnad_unmap_q *unmap_q = tcb->unmap_q;
+	struct bnad_skb_unmap *unmap_array;
+	struct sk_buff 		*skb;
+	int i;
+
+	/*
+	 * Just return if TX is stopped. This check is useful
+	 * when bnad_free_txbufs() runs out of a tasklet scheduled
+	 * before bnad_cb_tx_cleanup() cleared BNAD_RF_TX_STARTED bit
+	 * but this routine runs actually after the cleanup has been
+	 * executed.
+	 */
+	if (!test_bit(BNAD_RF_TX_STARTED, &bnad->run_flags))
+		return 0;
+
+	updated_hw_cons = *(tcb->hw_consumer_index);
+
+	wis = BNA_Q_INDEX_CHANGE(tcb->consumer_index,
+				  updated_hw_cons, tcb->q_depth);
+
+	BUG_ON(!(wis <= BNA_QE_IN_USE_CNT(tcb, tcb->q_depth)));
+
+	unmap_array = unmap_q->unmap_array;
+	unmap_cons = unmap_q->consumer_index;
+
+	prefetch(&unmap_array[unmap_cons + 1]);
+	while (wis) {
+		skb = unmap_array[unmap_cons].skb;
+
+		unmap_array[unmap_cons].skb = NULL;
+
+		sent_packets++;
+		sent_bytes += skb->len;
+		wis -= BNA_TXQ_WI_NEEDED(1 + skb_shinfo(skb)->nr_frags);
+
+		pci_unmap_single(bnad->pcidev,
+				 pci_unmap_addr(&unmap_array[unmap_cons],
+						dma_addr), skb_headlen(skb),
+				 PCI_DMA_TODEVICE);
+		pci_unmap_addr_set(&unmap_array[unmap_cons], dma_addr, 0);
+		BNA_QE_INDX_ADD(unmap_cons, 1, unmap_q->q_depth);
+
+		prefetch(&unmap_array[unmap_cons + 1]);
+		for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
+			prefetch(&unmap_array[unmap_cons + 1]);
+
+			pci_unmap_page(bnad->pcidev,
+				       pci_unmap_addr(&unmap_array[unmap_cons],
+						      dma_addr),
+				       skb_shinfo(skb)->frags[i].size,
+				       PCI_DMA_TODEVICE);
+			pci_unmap_addr_set(&unmap_array[unmap_cons], dma_addr,
+					   0);
+			BNA_QE_INDX_ADD(unmap_cons, 1, unmap_q->q_depth);
+		}
+		dev_kfree_skb_any(skb);
+	}
+
+	/* Update consumer pointers. */
+	tcb->consumer_index = updated_hw_cons;
+	unmap_q->consumer_index = unmap_cons;
+
+	tcb->txq->tx_packets += sent_packets;
+	tcb->txq->tx_bytes += sent_bytes;
+
+	return sent_packets;
+}
+
+/* Tx Free Tasklet function */
+/* Frees for all the tcb's in all the Tx's */
+/*
+ * Scheduled from sending context, so that
+ * the fat Tx lock is not held for too long
+ * in the sending context.
+ */
+static void
+bnad_tx_free_tasklet(unsigned long bnad_ptr)
+{
+	struct bnad *bnad = (struct bnad *)bnad_ptr;
+	struct bna_tcb *tcb;
+	u32 		acked;
+	int			i, j;
+
+	for (i = 0; i < bnad->num_tx; i++) {
+		for (j = 0; j < bnad->num_txq_per_tx; j++) {
+			tcb = bnad->tx_info[i].tcb[j];
+			if (!tcb)
+				continue;
+			if (((u16) (*tcb->hw_consumer_index) !=
+				tcb->consumer_index) &&
+				(!test_and_set_bit(BNAD_TXQ_FREE_SENT,
+						  &tcb->flags))) {
+				acked = bnad_free_txbufs(bnad, tcb);
+				bna_ib_ack(tcb->i_dbell, acked);
+				smp_mb__before_clear_bit();
+				clear_bit(BNAD_TXQ_FREE_SENT, &tcb->flags);
+			}
+		}
+	}
+}
+
+static u32
+bnad_tx(struct bnad *bnad, struct bna_tcb *tcb)
+{
+	struct net_device *netdev = bnad->netdev;
+	u32 sent;
+
+	if (test_and_set_bit(BNAD_TXQ_FREE_SENT, &tcb->flags))
+		return 0;
+
+	sent = bnad_free_txbufs(bnad, tcb);
+	if (sent) {
+		if (netif_queue_stopped(netdev) &&
+		    netif_carrier_ok(netdev) &&
+		    BNA_QE_FREE_CNT(tcb, tcb->q_depth) >=
+				    BNAD_NETIF_WAKE_THRESHOLD) {
+			netif_wake_queue(netdev);
+			BNAD_UPDATE_CTR(bnad, netif_queue_wakeup);
+		}
+		bna_ib_ack(tcb->i_dbell, sent);
+	} else
+		bna_ib_ack(tcb->i_dbell, 0);
+
+	smp_mb__before_clear_bit();
+	clear_bit(BNAD_TXQ_FREE_SENT, &tcb->flags);
+
+	return sent;
+}
+
+/* MSIX Tx Completion Handler */
+static irqreturn_t
+bnad_msix_tx(int irq, void *data)
+{
+	struct bna_tcb *tcb = (struct bna_tcb *)data;
+	struct bnad *bnad = tcb->bnad;
+
+	bnad_tx(bnad, tcb);
+
+	return IRQ_HANDLED;
+}
+
+static void
+bnad_reset_rcb(struct bnad *bnad, struct bna_rcb *rcb)
+{
+	struct bnad_unmap_q *unmap_q = rcb->unmap_q;
+
+	rcb->producer_index = 0;
+	rcb->consumer_index = 0;
+
+	unmap_q->producer_index = 0;
+	unmap_q->consumer_index = 0;
+}
+
+static void
+bnad_free_rxbufs(struct bnad *bnad, struct bna_rcb *rcb)
+{
+	struct bnad_unmap_q *unmap_q;
+	struct sk_buff *skb;
+
+	unmap_q = rcb->unmap_q;
+	while (BNA_QE_IN_USE_CNT(unmap_q, unmap_q->q_depth)) {
+		skb = unmap_q->unmap_array[unmap_q->consumer_index].skb;
+		BUG_ON(!(skb));
+		unmap_q->unmap_array[unmap_q->consumer_index].skb = NULL;
+		pci_unmap_single(bnad->pcidev, pci_unmap_addr(&unmap_q->
+					unmap_array[unmap_q->consumer_index],
+					dma_addr), rcb->rxq->buffer_size +
+					NET_IP_ALIGN, PCI_DMA_FROMDEVICE);
+		dev_kfree_skb(skb);
+		BNA_QE_INDX_ADD(unmap_q->consumer_index, 1, unmap_q->q_depth);
+		BNA_QE_INDX_ADD(rcb->consumer_index, 1, rcb->q_depth);
+	}
+
+	bnad_reset_rcb(bnad, rcb);
+}
+
+static void
+bnad_alloc_n_post_rxbufs(struct bnad *bnad, struct bna_rcb *rcb)
+{
+	u16 to_alloc, alloced, unmap_prod, wi_range;
+	struct bnad_unmap_q *unmap_q = rcb->unmap_q;
+	struct bnad_skb_unmap *unmap_array;
+	struct bna_rxq_entry *rxent;
+	struct sk_buff *skb;
+	dma_addr_t dma_addr;
+
+	alloced = 0;
+	to_alloc =
+		BNA_QE_FREE_CNT(unmap_q, unmap_q->q_depth);
+
+	unmap_array = unmap_q->unmap_array;
+	unmap_prod = unmap_q->producer_index;
+
+	BNA_RXQ_QPGE_PTR_GET(unmap_prod, rcb->sw_qpt, rxent, wi_range);
+
+	while (to_alloc--) {
+		if (!wi_range) {
+			BNA_RXQ_QPGE_PTR_GET(unmap_prod, rcb->sw_qpt, rxent,
+					     wi_range);
+		}
+		skb = alloc_skb(rcb->rxq->buffer_size + NET_IP_ALIGN,
+				     GFP_ATOMIC);
+		if (unlikely(!skb)) {
+			BNAD_UPDATE_CTR(bnad, rxbuf_alloc_failed);
+			goto finishing;
+		}
+		skb->dev = bnad->netdev;
+		skb_reserve(skb, NET_IP_ALIGN);
+		unmap_array[unmap_prod].skb = skb;
+		dma_addr = pci_map_single(bnad->pcidev, skb->data,
+			rcb->rxq->buffer_size, PCI_DMA_FROMDEVICE);
+		pci_unmap_addr_set(&unmap_array[unmap_prod], dma_addr,
+				   dma_addr);
+		BNA_SET_DMA_ADDR(dma_addr, &rxent->host_addr);
+		BNA_QE_INDX_ADD(unmap_prod, 1, unmap_q->q_depth);
+
+		rxent++;
+		wi_range--;
+		alloced++;
+	}
+
+finishing:
+	if (likely(alloced)) {
+		unmap_q->producer_index = unmap_prod;
+		rcb->producer_index = unmap_prod;
+		smp_mb();
+		bna_rxq_prod_indx_doorbell(rcb);
+	}
+}
+
+/*
+ * Locking is required in the enable path
+ * because it is called from a napi poll
+ * context, where the bna_lock is not held
+ * unlike the IRQ context.
+ */
+static void
+bnad_enable_txrx_irqs(struct bnad *bnad)
+{
+	struct bna_tcb *tcb;
+	struct bna_ccb *ccb;
+	int i, j;
+	unsigned long flags;
+
+	spin_lock_irqsave(&bnad->bna_lock, flags);
+	for (i = 0; i < bnad->num_tx; i++) {
+		for (j = 0; j < bnad->num_txq_per_tx; j++) {
+			tcb = bnad->tx_info[i].tcb[j];
+			bna_ib_coalescing_timer_set(tcb->i_dbell,
+				tcb->txq->ib->ib_config.coalescing_timeo);
+			bna_ib_ack(tcb->i_dbell, 0);
+		}
+	}
+
+	for (i = 0; i < bnad->num_rx; i++) {
+		for (j = 0; j < bnad->num_rxp_per_rx; j++) {
+			ccb = bnad->rx_info[i].rx_ctrl[j].ccb;
+			bnad_enable_rx_irq_unsafe(ccb);
+		}
+	}
+	spin_unlock_irqrestore(&bnad->bna_lock, flags);
+}
+
+static inline void
+bnad_refill_rxq(struct bnad *bnad, struct bna_rcb *rcb)
+{
+	struct bnad_unmap_q *unmap_q = rcb->unmap_q;
+
+	if (!test_and_set_bit(BNAD_RXQ_REFILL, &rcb->flags)) {
+		if (BNA_QE_FREE_CNT(unmap_q, unmap_q->q_depth)
+			 >> BNAD_RXQ_REFILL_THRESHOLD_SHIFT)
+			bnad_alloc_n_post_rxbufs(bnad, rcb);
+		smp_mb__before_clear_bit();
+		clear_bit(BNAD_RXQ_REFILL, &rcb->flags);
+	}
+}
+
+static u32
+bnad_poll_cq(struct bnad *bnad, struct bna_ccb *ccb, int budget)
+{
+	struct bna_cq_entry *cmpl, *next_cmpl;
+	struct bna_rcb *rcb = NULL;
+	unsigned int wi_range, packets = 0, wis = 0;
+	struct bnad_unmap_q *unmap_q;
+	struct sk_buff *skb;
+	u32 flags;
+	u32 qid0 = ccb->rcb[0]->rxq->rxq_id;
+	struct bna_pkt_rate *pkt_rt = &ccb->pkt_rate;
+
+	prefetch(bnad->netdev);
+	BNA_CQ_QPGE_PTR_GET(ccb->producer_index, ccb->sw_qpt, cmpl,
+			    wi_range);
+	BUG_ON(!(wi_range <= ccb->q_depth));
+	while (cmpl->valid && packets < budget) {
+		packets++;
+		BNA_UPDATE_PKT_CNT(pkt_rt, ntohs(cmpl->length));
+
+		if (qid0 == cmpl->rxq_id)
+			rcb = ccb->rcb[0];
+		else
+			rcb = ccb->rcb[1];
+
+		unmap_q = rcb->unmap_q;
+
+		skb = unmap_q->unmap_array[unmap_q->consumer_index].skb;
+		BUG_ON(!(skb));
+		unmap_q->unmap_array[unmap_q->consumer_index].skb = NULL;
+		pci_unmap_single(bnad->pcidev,
+				 pci_unmap_addr(&unmap_q->
+						unmap_array[unmap_q->
+							    consumer_index],
+						dma_addr),
+						rcb->rxq->buffer_size,
+						PCI_DMA_FROMDEVICE);
+		BNA_QE_INDX_ADD(unmap_q->consumer_index, 1, unmap_q->q_depth);
+
+		/* Should be more efficient ? Performance ? */
+		BNA_QE_INDX_ADD(rcb->consumer_index, 1, rcb->q_depth);
+
+		wis++;
+		if (likely(--wi_range))
+			next_cmpl = cmpl + 1;
+		else {
+			BNA_QE_INDX_ADD(ccb->producer_index, wis, ccb->q_depth);
+			wis = 0;
+			BNA_CQ_QPGE_PTR_GET(ccb->producer_index, ccb->sw_qpt,
+						next_cmpl, wi_range);
+			BUG_ON(!(wi_range <= ccb->q_depth));
+		}
+		prefetch(next_cmpl);
+
+		flags = ntohl(cmpl->flags);
+		if (unlikely
+		    (flags &
+		     (BNA_CQ_EF_MAC_ERROR | BNA_CQ_EF_FCS_ERROR |
+		      BNA_CQ_EF_TOO_LONG))) {
+			dev_kfree_skb_any(skb);
+			rcb->rxq->rx_packets_with_error++;
+			goto next;
+		}
+
+		skb_put(skb, ntohs(cmpl->length));
+		if (likely
+		    (bnad->rx_csum &&
+		     (((flags & BNA_CQ_EF_IPV4) &&
+		      (flags & BNA_CQ_EF_L3_CKSUM_OK)) ||
+		      (flags & BNA_CQ_EF_IPV6)) &&
+		      (flags & (BNA_CQ_EF_TCP | BNA_CQ_EF_UDP)) &&
+		      (flags & BNA_CQ_EF_L4_CKSUM_OK)))
+			skb->ip_summed = CHECKSUM_UNNECESSARY;
+		else
+			skb->ip_summed = CHECKSUM_NONE;
+
+		rcb->rxq->rx_packets++;
+		rcb->rxq->rx_bytes += skb->len;
+		skb->protocol = eth_type_trans(skb, bnad->netdev);
+
+		if (bnad->vlan_grp && (flags & BNA_CQ_EF_VLAN)) {
+			struct bnad_rx_ctrl *rx_ctrl =
+				(struct bnad_rx_ctrl *)ccb->ctrl;
+			if (skb->ip_summed == CHECKSUM_UNNECESSARY)
+				vlan_gro_receive(&rx_ctrl->napi, bnad->vlan_grp,
+						ntohs(cmpl->vlan_tag), skb);
+			else
+				vlan_hwaccel_receive_skb(skb,
+							 bnad->vlan_grp,
+							 ntohs(cmpl->vlan_tag));
+
+		} else { /* Not VLAN tagged/stripped */
+			struct bnad_rx_ctrl *rx_ctrl =
+				(struct bnad_rx_ctrl *)ccb->ctrl;
+			if (skb->ip_summed == CHECKSUM_UNNECESSARY)
+				napi_gro_receive(&rx_ctrl->napi, skb);
+			else
+				netif_receive_skb(skb);
+		}
+
+next:
+		cmpl->valid = 0;
+		cmpl = next_cmpl;
+	}
+
+	BNA_QE_INDX_ADD(ccb->producer_index, wis, ccb->q_depth);
+
+	if (likely(ccb)) {
+		bna_ib_ack(ccb->i_dbell, packets);
+		bnad_refill_rxq(bnad, ccb->rcb[0]);
+		if (ccb->rcb[1])
+			bnad_refill_rxq(bnad, ccb->rcb[1]);
+	} else
+		bna_ib_ack(ccb->i_dbell, 0);
+
+	return packets;
+}
+
+static void
+bnad_disable_rx_irq(struct bnad *bnad, struct bna_ccb *ccb)
+{
+	bna_ib_coalescing_timer_set(ccb->i_dbell, 0);
+	bna_ib_ack(ccb->i_dbell, 0);
+}
+
+static void
+bnad_enable_rx_irq(struct bnad *bnad, struct bna_ccb *ccb)
+{
+	spin_lock_irq(&bnad->bna_lock); /* Because of polling context */
+	bnad_enable_rx_irq_unsafe(ccb);
+	spin_unlock_irq(&bnad->bna_lock);
+}
+
+static void
+bnad_netif_rx_schedule_poll(struct bnad *bnad, struct bna_ccb *ccb)
+{
+	struct bnad_rx_ctrl *rx_ctrl = (struct bnad_rx_ctrl *)(ccb->ctrl);
+	if (likely(napi_schedule_prep((&rx_ctrl->napi)))) {
+		bnad_disable_rx_irq(bnad, ccb);
+		__napi_schedule((&rx_ctrl->napi));
+	}
+	BNAD_UPDATE_CTR(bnad, netif_rx_schedule);
+}
+
+/* MSIX Rx Path Handler */
+static irqreturn_t
+bnad_msix_rx(int irq, void *data)
+{
+	struct bna_ccb *ccb = (struct bna_ccb *)data;
+	struct bnad *bnad = ccb->bnad;
+
+	bnad_netif_rx_schedule_poll(bnad, ccb);
+
+	return IRQ_HANDLED;
+}
+
+/* Interrupt handlers */
+
+/* Mbox Interrupt Handlers */
+static irqreturn_t
+bnad_msix_mbox_handler(int irq, void *data)
+{
+	u32 intr_status;
+	unsigned long  flags;
+	struct net_device *netdev = data;
+	struct bnad *bnad;
+
+	bnad = netdev_priv(netdev);
+
+	/* BNA_ISR_GET(bnad); Inc Ref count */
+	spin_lock_irqsave(&bnad->bna_lock, flags);
+
+	bna_intr_status_get(&bnad->bna, intr_status);
+
+	if (BNA_IS_MBOX_ERR_INTR(intr_status))
+		bna_mbox_handler(&bnad->bna, intr_status);
+
+	spin_unlock_irqrestore(&bnad->bna_lock, flags);
+
+	/* BNAD_ISR_PUT(bnad); Dec Ref count */
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t
+bnad_isr(int irq, void *data)
+{
+	int i, j;
+	u32 intr_status;
+	unsigned long flags;
+	struct net_device *netdev = data;
+	struct bnad *bnad = netdev_priv(netdev);
+	struct bnad_rx_info *rx_info;
+	struct bnad_rx_ctrl *rx_ctrl;
+
+	spin_lock_irqsave(&bnad->bna_lock, flags);
+
+	bna_intr_status_get(&bnad->bna, intr_status);
+	if (!intr_status) {
+		spin_unlock_irqrestore(&bnad->bna_lock, flags);
+		return IRQ_NONE;
+	}
+
+	if (BNA_IS_MBOX_ERR_INTR(intr_status)) {
+		bna_mbox_handler(&bnad->bna, intr_status);
+		if (!BNA_IS_INTX_DATA_INTR(intr_status)) {
+			spin_unlock_irqrestore(&bnad->bna_lock, flags);
+			goto done;
+		}
+	}
+	spin_unlock_irqrestore(&bnad->bna_lock, flags);
+
+	/* Process data interrupts */
+	for (i = 0; i < bnad->num_rx; i++) {
+		rx_info = &bnad->rx_info[i];
+		if (!rx_info->rx)
+			continue;
+		for (j = 0; j < bnad->num_rxp_per_rx; j++) {
+			rx_ctrl = &rx_info->rx_ctrl[j];
+			if (rx_ctrl->ccb)
+				bnad_netif_rx_schedule_poll(bnad,
+							    rx_ctrl->ccb);
+		}
+	}
+done:
+	return IRQ_HANDLED;
+}
+
+/*
+ * Called in interrupt / callback context
+ * with bna_lock held, so cfg_flags access is OK
+ */
+static void
+bnad_enable_mbox_irq(struct bnad *bnad)
+{
+	int irq = BNAD_GET_MBOX_IRQ(bnad);
+
+	if (!(bnad->cfg_flags & BNAD_CF_MSIX))
+		return;
+
+	if (test_and_clear_bit(BNAD_RF_MBOX_IRQ_DISABLED, &bnad->run_flags))
+		enable_irq(irq);
+	BNAD_UPDATE_CTR(bnad, mbox_intr_enabled);
+}
+
+/*
+ * Called with bnad->bna_lock held b'cos of
+ * bnad->cfg_flags access.
+ */
+void
+bnad_disable_mbox_irq(struct bnad *bnad)
+{
+	int irq = BNAD_GET_MBOX_IRQ(bnad);
+
+	if (!(bnad->cfg_flags & BNAD_CF_MSIX))
+		return;
+
+	if (!test_and_set_bit(BNAD_RF_MBOX_IRQ_DISABLED, &bnad->run_flags))
+		disable_irq_nosync(irq);
+	BNAD_UPDATE_CTR(bnad, mbox_intr_disabled);
+}
+
+/* Control Path Handlers */
+
+/* Callbacks */
+void
+bnad_cb_device_enable_mbox_intr(struct bnad *bnad)
+{
+	bnad_enable_mbox_irq(bnad);
+}
+
+void
+bnad_cb_device_disable_mbox_intr(struct bnad *bnad)
+{
+	bnad_disable_mbox_irq(bnad);
+}
+
+void
+bnad_cb_device_enabled(struct bnad *bnad, enum bna_cb_status status)
+{
+	complete(&bnad->bnad_completions.ioc_comp);
+	bnad->bnad_completions.ioc_comp_status = status;
+}
+
+void
+bnad_cb_device_disabled(struct bnad *bnad, enum bna_cb_status status)
+{
+	complete(&bnad->bnad_completions.ioc_comp);
+	bnad->bnad_completions.ioc_comp_status = status;
+}
+
+static void
+bnad_cb_port_disabled(void *arg, enum bna_cb_status status)
+{
+	struct bnad *bnad = (struct bnad *)arg;
+
+	complete(&bnad->bnad_completions.port_comp);
+
+	netif_carrier_off(bnad->netdev);
+}
+
+void
+bnad_cb_port_link_status(struct bnad *bnad,
+			enum bna_link_status link_status)
+{
+	bool link_up = 0;
+
+	link_up = (link_status == BNA_LINK_UP) || (link_status == BNA_CEE_UP);
+
+	if (link_status == BNA_CEE_UP) {
+		set_bit(BNAD_RF_CEE_RUNNING, &bnad->run_flags);
+		BNAD_UPDATE_CTR(bnad, cee_up);
+	} else
+		clear_bit(BNAD_RF_CEE_RUNNING, &bnad->run_flags);
+
+	if (link_up) {
+		if (!netif_carrier_ok(bnad->netdev)) {
+			printk(KERN_WARNING "bna: %s link up\n",
+				bnad->netdev->name);
+			netif_carrier_on(bnad->netdev);
+			BNAD_UPDATE_CTR(bnad, link_toggle);
+			if (test_bit(BNAD_RF_TX_STARTED, &bnad->run_flags)) {
+				/* Force an immediate Transmit Schedule */
+				printk(KERN_INFO "bna: %s TX_STARTED\n",
+					bnad->netdev->name);
+				netif_wake_queue(bnad->netdev);
+				BNAD_UPDATE_CTR(bnad, netif_queue_wakeup);
+			} else {
+				netif_stop_queue(bnad->netdev);
+				BNAD_UPDATE_CTR(bnad, netif_queue_stop);
+			}
+		}
+	} else {
+		if (netif_carrier_ok(bnad->netdev)) {
+			printk(KERN_WARNING "bna: %s link down\n",
+				bnad->netdev->name);
+			netif_carrier_off(bnad->netdev);
+			BNAD_UPDATE_CTR(bnad, link_toggle);
+		}
+	}
+}
+
+static void
+bnad_cb_tx_disabled(void *arg, struct bna_tx *tx,
+			enum bna_cb_status status)
+{
+	struct bnad *bnad = (struct bnad *)arg;
+
+	complete(&bnad->bnad_completions.tx_comp);
+}
+
+static void
+bnad_cb_tcb_setup(struct bnad *bnad, struct bna_tcb *tcb)
+{
+	struct bnad_tx_info *tx_info =
+			(struct bnad_tx_info *)tcb->txq->tx->priv;
+	struct bnad_unmap_q *unmap_q = tcb->unmap_q;
+
+	tx_info->tcb[tcb->id] = tcb;
+	unmap_q->producer_index = 0;
+	unmap_q->consumer_index = 0;
+	unmap_q->q_depth = BNAD_TX_UNMAPQ_DEPTH;
+}
+
+static void
+bnad_cb_tcb_destroy(struct bnad *bnad, struct bna_tcb *tcb)
+{
+	struct bnad_tx_info *tx_info =
+			(struct bnad_tx_info *)tcb->txq->tx->priv;
+
+	tx_info->tcb[tcb->id] = NULL;
+}
+
+static void
+bnad_cb_rcb_setup(struct bnad *bnad, struct bna_rcb *rcb)
+{
+	struct bnad_unmap_q *unmap_q = rcb->unmap_q;
+
+	unmap_q->producer_index = 0;
+	unmap_q->consumer_index = 0;
+	unmap_q->q_depth = BNAD_RX_UNMAPQ_DEPTH;
+}
+
+static void
+bnad_cb_ccb_setup(struct bnad *bnad, struct bna_ccb *ccb)
+{
+	struct bnad_rx_info *rx_info =
+			(struct bnad_rx_info *)ccb->cq->rx->priv;
+
+	rx_info->rx_ctrl[ccb->id].ccb = ccb;
+	ccb->ctrl = &rx_info->rx_ctrl[ccb->id];
+}
+
+static void
+bnad_cb_ccb_destroy(struct bnad *bnad, struct bna_ccb *ccb)
+{
+	struct bnad_rx_info *rx_info =
+			(struct bnad_rx_info *)ccb->cq->rx->priv;
+
+	rx_info->rx_ctrl[ccb->id].ccb = NULL;
+}
+
+static void
+bnad_cb_tx_stall(struct bnad *bnad, struct bna_tcb *tcb)
+{
+	struct bnad_tx_info *tx_info =
+			(struct bnad_tx_info *)tcb->txq->tx->priv;
+
+	if (tx_info != &bnad->tx_info[0])
+		return;
+
+	clear_bit(BNAD_RF_TX_STARTED, &bnad->run_flags);
+	netif_stop_queue(bnad->netdev);
+	printk(KERN_INFO "bna: %s TX_STOPPED\n", bnad->netdev->name);
+}
+
+static void
+bnad_cb_tx_resume(struct bnad *bnad, struct bna_tcb *tcb)
+{
+	if (test_and_set_bit(BNAD_RF_TX_STARTED, &bnad->run_flags))
+		return;
+
+	if (netif_carrier_ok(bnad->netdev)) {
+		printk(KERN_INFO "bna: %s TX_STARTED\n", bnad->netdev->name);
+		netif_wake_queue(bnad->netdev);
+		BNAD_UPDATE_CTR(bnad, netif_queue_wakeup);
+	}
+}
+
+static void
+bnad_cb_tx_cleanup(struct bnad *bnad, struct bna_tcb *tcb)
+{
+	struct bnad_unmap_q *unmap_q = tcb->unmap_q;
+
+	if (!tcb || (!tcb->unmap_q))
+		return;
+
+	if (!unmap_q->unmap_array)
+		return;
+
+	if (test_and_set_bit(BNAD_TXQ_FREE_SENT, &tcb->flags))
+		return;
+
+	bnad_free_all_txbufs(bnad, tcb);
+
+	unmap_q->producer_index = 0;
+	unmap_q->consumer_index = 0;
+
+	smp_mb__before_clear_bit();
+	clear_bit(BNAD_TXQ_FREE_SENT, &tcb->flags);
+}
+
+static void
+bnad_cb_rx_cleanup(struct bnad *bnad,
+			struct bna_ccb *ccb)
+{
+	bnad_cq_cmpl_init(bnad, ccb);
+
+	bnad_free_rxbufs(bnad, ccb->rcb[0]);
+	clear_bit(BNAD_RXQ_STARTED, &ccb->rcb[0]->flags);
+
+	if (ccb->rcb[1]) {
+		bnad_free_rxbufs(bnad, ccb->rcb[1]);
+		clear_bit(BNAD_RXQ_STARTED, &ccb->rcb[1]->flags);
+	}
+}
+
+static void
+bnad_cb_rx_post(struct bnad *bnad, struct bna_rcb *rcb)
+{
+	struct bnad_unmap_q *unmap_q = rcb->unmap_q;
+
+	set_bit(BNAD_RXQ_STARTED, &rcb->flags);
+
+	/* Now allocate & post buffers for this RCB */
+	/* !!Allocation in callback context */
+	if (!test_and_set_bit(BNAD_RXQ_REFILL, &rcb->flags)) {
+		if (BNA_QE_FREE_CNT(unmap_q, unmap_q->q_depth)
+			 >> BNAD_RXQ_REFILL_THRESHOLD_SHIFT)
+			bnad_alloc_n_post_rxbufs(bnad, rcb);
+		smp_mb__before_clear_bit();
+		clear_bit(BNAD_RXQ_REFILL, &rcb->flags);
+	}
+}
+
+static void
+bnad_cb_rx_disabled(void *arg, struct bna_rx *rx,
+			enum bna_cb_status status)
+{
+	struct bnad *bnad = (struct bnad *)arg;
+
+	complete(&bnad->bnad_completions.rx_comp);
+}
+
+static void
+bnad_cb_rx_mcast_add(struct bnad *bnad, struct bna_rx *rx,
+				enum bna_cb_status status)
+{
+	bnad->bnad_completions.mcast_comp_status = status;
+	complete(&bnad->bnad_completions.mcast_comp);
+}
+
+void
+bnad_cb_stats_get(struct bnad *bnad, enum bna_cb_status status,
+		       struct bna_stats *stats)
+{
+	if (status == BNA_CB_SUCCESS)
+		BNAD_UPDATE_CTR(bnad, hw_stats_updates);
+
+	if (!netif_running(bnad->netdev) ||
+		!test_bit(BNAD_RF_STATS_TIMER_RUNNING, &bnad->run_flags))
+		return;
+
+	mod_timer(&bnad->stats_timer,
+		  jiffies + msecs_to_jiffies(BNAD_STATS_TIMER_FREQ));
+}
+
+void
+bnad_cb_stats_clr(struct bnad *bnad)
+{
+}
+
+/* Resource allocation, free functions */
+
+static void
+bnad_mem_free(struct bnad *bnad,
+	      struct bna_mem_info *mem_info)
+{
+	int i;
+	dma_addr_t dma_pa;
+
+	if (mem_info->mdl == NULL)
+		return;
+
+	for (i = 0; i < mem_info->num; i++) {
+		if (mem_info->mdl[i].kva != NULL) {
+			if (mem_info->mem_type == BNA_MEM_T_DMA) {
+				BNA_GET_DMA_ADDR(&(mem_info->mdl[i].dma),
+						dma_pa);
+				pci_free_consistent(bnad->pcidev,
+						mem_info->mdl[i].len,
+						mem_info->mdl[i].kva, dma_pa);
+			} else
+				kfree(mem_info->mdl[i].kva);
+		}
+	}
+	kfree(mem_info->mdl);
+	mem_info->mdl = NULL;
+}
+
+static int
+bnad_mem_alloc(struct bnad *bnad,
+	       struct bna_mem_info *mem_info)
+{
+	int i;
+	dma_addr_t dma_pa;
+
+	if ((mem_info->num == 0) || (mem_info->len == 0)) {
+		mem_info->mdl = NULL;
+		return 0;
+	}
+
+	mem_info->mdl = kcalloc(mem_info->num, sizeof(struct bna_mem_descr),
+				GFP_KERNEL);
+	if (mem_info->mdl == NULL)
+		return -ENOMEM;
+
+	if (mem_info->mem_type == BNA_MEM_T_DMA) {
+		for (i = 0; i < mem_info->num; i++) {
+			mem_info->mdl[i].len = mem_info->len;
+			mem_info->mdl[i].kva =
+				pci_alloc_consistent(bnad->pcidev,
+						mem_info->len, &dma_pa);
+
+			if (mem_info->mdl[i].kva == NULL)
+				goto err_return;
+
+			BNA_SET_DMA_ADDR(dma_pa,
+					 &(mem_info->mdl[i].dma));
+		}
+	} else {
+		for (i = 0; i < mem_info->num; i++) {
+			mem_info->mdl[i].len = mem_info->len;
+			mem_info->mdl[i].kva = kzalloc(mem_info->len,
+							GFP_KERNEL);
+			if (mem_info->mdl[i].kva == NULL)
+				goto err_return;
+		}
+	}
+
+	return 0;
+
+err_return:
+	bnad_mem_free(bnad, mem_info);
+	return -ENOMEM;
+}
+
+/* Free IRQ for Mailbox */
+static void
+bnad_mbox_irq_free(struct bnad *bnad,
+		   struct bna_intr_info *intr_info)
+{
+	int irq;
+	unsigned long flags;
+
+	if (intr_info->idl == NULL)
+		return;
+
+	spin_lock_irqsave(&bnad->bna_lock, flags);
+
+	bnad_disable_mbox_irq(bnad);
+
+	irq = BNAD_GET_MBOX_IRQ(bnad);
+	free_irq(irq, bnad->netdev);
+
+	spin_unlock_irqrestore(&bnad->bna_lock, flags);
+
+	kfree(intr_info->idl);
+}
+
+/*
+ * Allocates IRQ for Mailbox, but keep it disabled
+ * This will be enabled once we get the mbox enable callback
+ * from bna
+ */
+static int
+bnad_mbox_irq_alloc(struct bnad *bnad,
+		    struct bna_intr_info *intr_info)
+{
+	int 		err;
+	unsigned long 	flags;
+	u32	irq;
+	irq_handler_t 	irq_handler;
+
+	/* Mbox should use only 1 vector */
+
+	intr_info->idl = kzalloc(sizeof(*(intr_info->idl)), GFP_KERNEL);
+	if (!intr_info->idl)
+		return -ENOMEM;
+
+	spin_lock_irqsave(&bnad->bna_lock, flags);
+	if (bnad->cfg_flags & BNAD_CF_MSIX) {
+		irq_handler = (irq_handler_t)bnad_msix_mbox_handler;
+		irq = bnad->msix_table[bnad->msix_num - 1].vector;
+		flags = 0;
+		intr_info->intr_type = BNA_INTR_T_MSIX;
+		intr_info->idl[0].vector = bnad->msix_num - 1;
+	} else {
+		irq_handler = (irq_handler_t)bnad_isr;
+		irq = bnad->pcidev->irq;
+		flags = IRQF_SHARED;
+		intr_info->intr_type = BNA_INTR_T_INTX;
+		/* intr_info->idl.vector = 0 ? */
+	}
+	spin_unlock_irqrestore(&bnad->bna_lock, flags);
+
+	sprintf(bnad->mbox_irq_name, "%s%d", BNAD_NAME, bna_id);
+
+	err = request_irq(irq, irq_handler, flags,
+			  bnad->mbox_irq_name, bnad->netdev);
+	if (err) {
+		kfree(intr_info->idl);
+		intr_info->idl = NULL;
+		return err;
+	}
+
+	spin_lock_irqsave(&bnad->bna_lock, flags);
+	bnad_disable_mbox_irq(bnad);
+	spin_unlock_irqrestore(&bnad->bna_lock, flags);
+	return 0;
+}
+
+static void
+bnad_txrx_irq_free(struct bnad *bnad, struct bna_intr_info *intr_info)
+{
+	kfree(intr_info->idl);
+	intr_info->idl = NULL;
+}
+
+/* Allocates Interrupt Descriptor List for MSIX/INT-X vectors */
+static int
+bnad_txrx_irq_alloc(struct bnad *bnad, enum bnad_intr_source src,
+		    uint txrx_id, struct bna_intr_info *intr_info)
+{
+	int i, vector_start = 0;
+	u32 cfg_flags;
+	unsigned long flags;
+
+	spin_lock_irqsave(&bnad->bna_lock, flags);
+	cfg_flags = bnad->cfg_flags;
+	spin_unlock_irqrestore(&bnad->bna_lock, flags);
+
+	if (cfg_flags & BNAD_CF_MSIX) {
+		intr_info->intr_type = BNA_INTR_T_MSIX;
+		intr_info->idl = kcalloc(intr_info->num,
+					sizeof(struct bna_intr_descr),
+					GFP_KERNEL);
+		if (!intr_info->idl)
+			return -ENOMEM;
+
+		switch (src) {
+		case BNAD_INTR_TX:
+			vector_start = txrx_id;
+			break;
+
+		case BNAD_INTR_RX:
+			vector_start = bnad->num_tx * bnad->num_txq_per_tx +
+					txrx_id;
+			break;
+
+		default:
+			BUG();
+		}
+
+		for (i = 0; i < intr_info->num; i++)
+			intr_info->idl[i].vector = vector_start + i;
+	} else {
+		intr_info->intr_type = BNA_INTR_T_INTX;
+		intr_info->num = 1;
+		intr_info->idl = kcalloc(intr_info->num,
+					sizeof(struct bna_intr_descr),
+					GFP_KERNEL);
+		if (!intr_info->idl)
+			return -ENOMEM;
+
+		switch (src) {
+		case BNAD_INTR_TX:
+			intr_info->idl[0].vector = 0x1; /* Bit mask : Tx IB */
+			break;
+
+		case BNAD_INTR_RX:
+			intr_info->idl[0].vector = 0x2; /* Bit mask : Rx IB */
+			break;
+		}
+	}
+	return 0;
+}
+
+/**
+ * NOTE: Should be called for MSIX only
+ * Unregisters Tx MSIX vector(s) from the kernel
+ */
+static void
+bnad_tx_msix_unregister(struct bnad *bnad, struct bnad_tx_info *tx_info,
+			int num_txqs)
+{
+	int i;
+	int vector_num;
+
+	for (i = 0; i < num_txqs; i++) {
+		if (tx_info->tcb[i] == NULL)
+			continue;
+
+		vector_num = tx_info->tcb[i]->intr_vector;
+		free_irq(bnad->msix_table[vector_num].vector, tx_info->tcb[i]);
+	}
+}
+
+/**
+ * NOTE: Should be called for MSIX only
+ * Registers Tx MSIX vector(s) and ISR(s), cookie with the kernel
+ */
+static int
+bnad_tx_msix_register(struct bnad *bnad, struct bnad_tx_info *tx_info,
+			uint tx_id, int num_txqs)
+{
+	int i;
+	int err;
+	int vector_num;
+
+	for (i = 0; i < num_txqs; i++) {
+		vector_num = tx_info->tcb[i]->intr_vector;
+		sprintf(tx_info->tcb[i]->name, "%s TXQ %d", bnad->netdev->name,
+				tx_id + tx_info->tcb[i]->id);
+		err = request_irq(bnad->msix_table[vector_num].vector,
+				  (irq_handler_t)bnad_msix_tx, 0,
+				  tx_info->tcb[i]->name,
+				  tx_info->tcb[i]);
+		if (err)
+			goto err_return;
+	}
+
+	return 0;
+
+err_return:
+	if (i > 0)
+		bnad_tx_msix_unregister(bnad, tx_info, (i - 1));
+	return -1;
+}
+
+/**
+ * NOTE: Should be called for MSIX only
+ * Unregisters Rx MSIX vector(s) from the kernel
+ */
+static void
+bnad_rx_msix_unregister(struct bnad *bnad, struct bnad_rx_info *rx_info,
+			int num_rxps)
+{
+	int i;
+	int vector_num;
+
+	for (i = 0; i < num_rxps; i++) {
+		if (rx_info->rx_ctrl[i].ccb == NULL)
+			continue;
+
+		vector_num = rx_info->rx_ctrl[i].ccb->intr_vector;
+		free_irq(bnad->msix_table[vector_num].vector,
+			 rx_info->rx_ctrl[i].ccb);
+	}
+}
+
+/**
+ * NOTE: Should be called for MSIX only
+ * Registers Tx MSIX vector(s) and ISR(s), cookie with the kernel
+ */
+static int
+bnad_rx_msix_register(struct bnad *bnad, struct bnad_rx_info *rx_info,
+			uint rx_id, int num_rxps)
+{
+	int i;
+	int err;
+	int vector_num;
+
+	for (i = 0; i < num_rxps; i++) {
+		vector_num = rx_info->rx_ctrl[i].ccb->intr_vector;
+		sprintf(rx_info->rx_ctrl[i].ccb->name, "%s CQ %d",
+			bnad->netdev->name,
+			rx_id + rx_info->rx_ctrl[i].ccb->id);
+		err = request_irq(bnad->msix_table[vector_num].vector,
+				  (irq_handler_t)bnad_msix_rx, 0,
+				  rx_info->rx_ctrl[i].ccb->name,
+				  rx_info->rx_ctrl[i].ccb);
+		if (err)
+			goto err_return;
+	}
+
+	return 0;
+
+err_return:
+	if (i > 0)
+		bnad_rx_msix_unregister(bnad, rx_info, (i - 1));
+	return -1;
+}
+
+/* Free Tx object Resources */
+static void
+bnad_tx_res_free(struct bnad *bnad, struct bna_res_info *res_info)
+{
+	int i;
+
+	for (i = 0; i < BNA_TX_RES_T_MAX; i++) {
+		if (res_info[i].res_type == BNA_RES_T_MEM)
+			bnad_mem_free(bnad, &res_info[i].res_u.mem_info);
+		else if (res_info[i].res_type == BNA_RES_T_INTR)
+			bnad_txrx_irq_free(bnad, &res_info[i].res_u.intr_info);
+	}
+}
+
+/* Allocates memory and interrupt resources for Tx object */
+static int
+bnad_tx_res_alloc(struct bnad *bnad, struct bna_res_info *res_info,
+		  uint tx_id)
+{
+	int i, err = 0;
+
+	for (i = 0; i < BNA_TX_RES_T_MAX; i++) {
+		if (res_info[i].res_type == BNA_RES_T_MEM)
+			err = bnad_mem_alloc(bnad,
+					&res_info[i].res_u.mem_info);
+		else if (res_info[i].res_type == BNA_RES_T_INTR)
+			err = bnad_txrx_irq_alloc(bnad, BNAD_INTR_TX, tx_id,
+					&res_info[i].res_u.intr_info);
+		if (err)
+			goto err_return;
+	}
+	return 0;
+
+err_return:
+	bnad_tx_res_free(bnad, res_info);
+	return err;
+}
+
+/* Free Rx object Resources */
+static void
+bnad_rx_res_free(struct bnad *bnad, struct bna_res_info *res_info)
+{
+	int i;
+
+	for (i = 0; i < BNA_RX_RES_T_MAX; i++) {
+		if (res_info[i].res_type == BNA_RES_T_MEM)
+			bnad_mem_free(bnad, &res_info[i].res_u.mem_info);
+		else if (res_info[i].res_type == BNA_RES_T_INTR)
+			bnad_txrx_irq_free(bnad, &res_info[i].res_u.intr_info);
+	}
+}
+
+/* Allocates memory and interrupt resources for Rx object */
+static int
+bnad_rx_res_alloc(struct bnad *bnad, struct bna_res_info *res_info,
+		  uint rx_id)
+{
+	int i, err = 0;
+
+	/* All memory needs to be allocated before setup_ccbs */
+	for (i = 0; i < BNA_RX_RES_T_MAX; i++) {
+		if (res_info[i].res_type == BNA_RES_T_MEM)
+			err = bnad_mem_alloc(bnad,
+					&res_info[i].res_u.mem_info);
+		else if (res_info[i].res_type == BNA_RES_T_INTR)
+			err = bnad_txrx_irq_alloc(bnad, BNAD_INTR_RX, rx_id,
+					&res_info[i].res_u.intr_info);
+		if (err)
+			goto err_return;
+	}
+	return 0;
+
+err_return:
+	bnad_rx_res_free(bnad, res_info);
+	return err;
+}
+
+/* Timer callbacks */
+/* a) IOC timer */
+static void
+bnad_ioc_timeout(unsigned long data)
+{
+	struct bnad *bnad = (struct bnad *)data;
+	unsigned long flags;
+
+	spin_lock_irqsave(&bnad->bna_lock, flags);
+	bfa_ioc_timeout((void *) &bnad->bna.device.ioc);
+	spin_unlock_irqrestore(&bnad->bna_lock, flags);
+}
+
+static void
+bnad_ioc_hb_check(unsigned long data)
+{
+	struct bnad *bnad = (struct bnad *)data;
+	unsigned long flags;
+
+	spin_lock_irqsave(&bnad->bna_lock, flags);
+	bfa_ioc_hb_check((void *) &bnad->bna.device.ioc);
+	spin_unlock_irqrestore(&bnad->bna_lock, flags);
+}
+
+static void
+bnad_ioc_sem_timeout(unsigned long data)
+{
+	struct bnad *bnad = (struct bnad *)data;
+	unsigned long flags;
+
+	spin_lock_irqsave(&bnad->bna_lock, flags);
+	bfa_ioc_sem_timeout((void *) &bnad->bna.device.ioc);
+	spin_unlock_irqrestore(&bnad->bna_lock, flags);
+}
+
+/*
+ * All timer routines use bnad->bna_lock to protect against
+ * the following race, which may occur in case of no locking:
+ * 	Time	CPU m  		CPU n
+ *	0       1 = test_bit
+ *	1			clear_bit
+ *	2			del_timer_sync
+ *	3	mod_timer
+ */
+
+/* b) Dynamic Interrupt Moderation Timer */
+static void
+bnad_dim_timeout(unsigned long data)
+{
+	struct bnad *bnad = (struct bnad *)data;
+	struct bnad_rx_info *rx_info;
+	struct bnad_rx_ctrl *rx_ctrl;
+	int i, j;
+	unsigned long flags;
+
+	if (!netif_carrier_ok(bnad->netdev))
+		return;
+
+	spin_lock_irqsave(&bnad->bna_lock, flags);
+	for (i = 0; i < bnad->num_rx; i++) {
+		rx_info = &bnad->rx_info[i];
+		if (!rx_info->rx)
+			continue;
+		for (j = 0; j < bnad->num_rxp_per_rx; j++) {
+			rx_ctrl = &rx_info->rx_ctrl[j];
+			if (!rx_ctrl->ccb)
+				continue;
+			bna_rx_dim_update(rx_ctrl->ccb);
+		}
+	}
+
+	/* Check for BNAD_CF_DIM_ENABLED, does not eleminate a race */
+	if (test_bit(BNAD_RF_DIM_TIMER_RUNNING, &bnad->run_flags))
+		mod_timer(&bnad->dim_timer,
+			  jiffies + msecs_to_jiffies(BNAD_DIM_TIMER_FREQ));
+	spin_unlock_irqrestore(&bnad->bna_lock, flags);
+}
+
+/* c)  Statistics Timer */
+static void
+bnad_stats_timeout(unsigned long data)
+{
+	struct bnad *bnad = (struct bnad *)data;
+	unsigned long flags;
+
+	if (!netif_running(bnad->netdev) ||
+		!test_bit(BNAD_RF_STATS_TIMER_RUNNING, &bnad->run_flags))
+		return;
+
+	spin_lock_irqsave(&bnad->bna_lock, flags);
+	bna_stats_get(&bnad->bna);
+	spin_unlock_irqrestore(&bnad->bna_lock, flags);
+}
+
+/*
+ * Set up timer for DIM
+ * Called with bnad->bna_lock held
+ */
+void
+bnad_dim_timer_start(struct bnad *bnad)
+{
+	if (bnad->cfg_flags & BNAD_CF_DIM_ENABLED &&
+	    !test_bit(BNAD_RF_DIM_TIMER_RUNNING, &bnad->run_flags)) {
+		setup_timer(&bnad->dim_timer, bnad_dim_timeout,
+			    (unsigned long)bnad);
+		set_bit(BNAD_RF_DIM_TIMER_RUNNING, &bnad->run_flags);
+		mod_timer(&bnad->dim_timer,
+			  jiffies + msecs_to_jiffies(BNAD_DIM_TIMER_FREQ));
+	}
+}
+
+#if 0
+/*
+ * Stops the DIM timer
+ * Called with bnad->bna_lock held
+ */
+void
+bnad_dim_timer_stop(struct bnad *bnad)
+{
+	int to_del = 0;
+	unsigned long flags = 0;
+
+	if (bnad->cfg_flags & BNAD_CF_DIM_ENABLED &&
+	    test_bit(BNAD_RF_DIM_TIMER_RUNNING, &bnad->run_flags)) {
+		clear_bit(BNAD_RF_DIM_TIMER_RUNNING, &bnad->run_flags);
+		to_del = 1;
+	}
+	spin_unlock_irqrestore(&bnad->bna_lock, flags);
+	if (to_del)
+		del_timer_sync(&bnad->dim_timer);
+	spin_lock_irqsave(&bnad->bna_lock, flags);
+}
+#endif
+
+/*
+ * Set up timer for statistics
+ * Called with mutex_lock(&bnad->conf_mutex) held
+ */
+static void
+bnad_stats_timer_start(struct bnad *bnad)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&bnad->bna_lock, flags);
+	if (!test_and_set_bit(BNAD_RF_STATS_TIMER_RUNNING, &bnad->run_flags)) {
+		setup_timer(&bnad->stats_timer, bnad_stats_timeout,
+			    (unsigned long)bnad);
+		mod_timer(&bnad->stats_timer,
+			  jiffies + msecs_to_jiffies(BNAD_STATS_TIMER_FREQ));
+	}
+	spin_unlock_irqrestore(&bnad->bna_lock, flags);
+
+}
+
+/*
+ * Stops the stats timer
+ * Called with mutex_lock(&bnad->conf_mutex) held
+ */
+static void
+bnad_stats_timer_stop(struct bnad *bnad)
+{
+	int to_del = 0;
+	unsigned long flags;
+
+	spin_lock_irqsave(&bnad->bna_lock, flags);
+	if (test_and_clear_bit(BNAD_RF_STATS_TIMER_RUNNING, &bnad->run_flags))
+		to_del = 1;
+	spin_unlock_irqrestore(&bnad->bna_lock, flags);
+	if (to_del)
+		del_timer_sync(&bnad->stats_timer);
+}
+
+/* Utilities */
+
+static void
+bnad_netdev_mc_list_get(struct net_device *netdev, u8 *mc_list)
+{
+	int i = 1; /* Index 0 has broadcast address */
+	struct netdev_hw_addr *mc_addr;
+
+	netdev_for_each_mc_addr(mc_addr, netdev) {
+		memcpy(&mc_list[i * ETH_ALEN], &mc_addr->addr[0],
+							ETH_ALEN);
+		i++;
+	}
+}
+
+static int
+bnad_napi_poll_rx(struct napi_struct *napi, int budget)
+{
+	struct bnad_rx_ctrl *rx_ctrl =
+		container_of(napi, struct bnad_rx_ctrl, napi);
+	struct bna_ccb *ccb;
+	struct bnad *bnad;
+	int rcvd = 0;
+
+	ccb = rx_ctrl->ccb;
+
+	bnad = ccb->bnad;
+
+	if (!netif_carrier_ok(bnad->netdev))
+		goto poll_exit;
+
+	rcvd = bnad_poll_cq(bnad, ccb, budget);
+	if (rcvd == budget)
+		return rcvd;
+
+poll_exit:
+	napi_complete((napi));
+
+	BNAD_UPDATE_CTR(bnad, netif_rx_complete);
+
+	bnad_enable_rx_irq(bnad, ccb);
+	return rcvd;
+}
+
+static int
+bnad_napi_poll_txrx(struct napi_struct *napi, int budget)
+{
+	struct bnad_rx_ctrl *rx_ctrl =
+		container_of(napi, struct bnad_rx_ctrl, napi);
+	struct bna_ccb *ccb;
+	struct bnad *bnad;
+	int 			rcvd = 0;
+	int			i, j;
+
+	ccb = rx_ctrl->ccb;
+
+	bnad = ccb->bnad;
+
+	if (!netif_carrier_ok(bnad->netdev))
+		goto poll_exit;
+
+	/* Handle Tx Completions, if any */
+	for (i = 0; i < bnad->num_tx; i++) {
+		for (j = 0; j < bnad->num_txq_per_tx; j++)
+			bnad_tx(bnad, bnad->tx_info[i].tcb[j]);
+	}
+
+	/* Handle Rx Completions */
+	rcvd = bnad_poll_cq(bnad, ccb, budget);
+	if (rcvd == budget)
+		return rcvd;
+poll_exit:
+	napi_complete((napi));
+
+	BNAD_UPDATE_CTR(bnad, netif_rx_complete);
+
+	bnad_enable_txrx_irqs(bnad);
+	return rcvd;
+}
+
+static void
+bnad_napi_enable(struct bnad *bnad, u32 rx_id)
+{
+	int (*napi_poll) (struct napi_struct *, int);
+	struct bnad_rx_ctrl *rx_ctrl;
+	int i;
+	unsigned long flags;
+
+	spin_lock_irqsave(&bnad->bna_lock, flags);
+	if (bnad->cfg_flags & BNAD_CF_MSIX)
+		napi_poll = bnad_napi_poll_rx;
+	else
+		napi_poll = bnad_napi_poll_txrx;
+	spin_unlock_irqrestore(&bnad->bna_lock, flags);
+
+	/* Initialize & enable NAPI */
+	for (i = 0; i <	bnad->num_rxp_per_rx; i++) {
+		rx_ctrl = &bnad->rx_info[rx_id].rx_ctrl[i];
+		netif_napi_add(bnad->netdev, &rx_ctrl->napi,
+			       napi_poll, 64);
+		napi_enable(&rx_ctrl->napi);
+	}
+}
+
+static void
+bnad_napi_disable(struct bnad *bnad, u32 rx_id)
+{
+	int i;
+
+	/* First disable and then clean up */
+	for (i = 0; i < bnad->num_rxp_per_rx; i++) {
+		napi_disable(&bnad->rx_info[rx_id].rx_ctrl[i].napi);
+		netif_napi_del(&bnad->rx_info[rx_id].rx_ctrl[i].napi);
+	}
+}
+
+/* Should be held with conf_lock held */
+void
+bnad_cleanup_tx(struct bnad *bnad, uint tx_id)
+{
+	struct bnad_tx_info *tx_info = &bnad->tx_info[tx_id];
+	struct bna_res_info *res_info = &bnad->tx_res_info[tx_id].res_info[0];
+	unsigned long flags;
+
+	if (!tx_info->tx)
+		return;
+
+	init_completion(&bnad->bnad_completions.tx_comp);
+	spin_lock_irqsave(&bnad->bna_lock, flags);
+	bna_tx_disable(tx_info->tx, BNA_HARD_CLEANUP, bnad_cb_tx_disabled);
+	spin_unlock_irqrestore(&bnad->bna_lock, flags);
+	wait_for_completion(&bnad->bnad_completions.tx_comp);
+
+	if (tx_info->tcb[0]->intr_type == BNA_INTR_T_MSIX)
+		bnad_tx_msix_unregister(bnad, tx_info,
+			bnad->num_txq_per_tx);
+
+	spin_lock_irqsave(&bnad->bna_lock, flags);
+	bna_tx_destroy(tx_info->tx);
+	spin_unlock_irqrestore(&bnad->bna_lock, flags);
+
+	tx_info->tx = NULL;
+
+	if (0 == tx_id)
+		tasklet_kill(&bnad->tx_free_tasklet);
+
+	bnad_tx_res_free(bnad, res_info);
+}
+
+/* Should be held with conf_lock held */
+int
+bnad_setup_tx(struct bnad *bnad, uint tx_id)
+{
+	int err;
+	struct bnad_tx_info *tx_info = &bnad->tx_info[tx_id];
+	struct bna_res_info *res_info = &bnad->tx_res_info[tx_id].res_info[0];
+	struct bna_intr_info *intr_info =
+			&res_info[BNA_TX_RES_INTR_T_TXCMPL].res_u.intr_info;
+	struct bna_tx_config *tx_config = &bnad->tx_config[tx_id];
+	struct bna_tx_event_cbfn tx_cbfn;
+	struct bna_tx *tx;
+	unsigned long flags;
+
+	/* Initialize the Tx object configuration */
+	tx_config->num_txq = bnad->num_txq_per_tx;
+	tx_config->txq_depth = bnad->txq_depth;
+	tx_config->tx_type = BNA_TX_T_REGULAR;
+
+	/* Initialize the tx event handlers */
+	tx_cbfn.tcb_setup_cbfn = bnad_cb_tcb_setup;
+	tx_cbfn.tcb_destroy_cbfn = bnad_cb_tcb_destroy;
+	tx_cbfn.tx_stall_cbfn = bnad_cb_tx_stall;
+	tx_cbfn.tx_resume_cbfn = bnad_cb_tx_resume;
+	tx_cbfn.tx_cleanup_cbfn = bnad_cb_tx_cleanup;
+
+	/* Get BNA's resource requirement for one tx object */
+	spin_lock_irqsave(&bnad->bna_lock, flags);
+	bna_tx_res_req(bnad->num_txq_per_tx,
+		bnad->txq_depth, res_info);
+	spin_unlock_irqrestore(&bnad->bna_lock, flags);
+
+	/* Fill Unmap Q memory requirements */
+	BNAD_FILL_UNMAPQ_MEM_REQ(
+			&res_info[BNA_TX_RES_MEM_T_UNMAPQ],
+			bnad->num_txq_per_tx,
+			BNAD_TX_UNMAPQ_DEPTH);
+
+	/* Allocate resources */
+	err = bnad_tx_res_alloc(bnad, res_info, tx_id);
+	if (err)
+		return err;
+
+	/* Ask BNA to create one Tx object, supplying required resources */
+	spin_lock_irqsave(&bnad->bna_lock, flags);
+	tx = bna_tx_create(&bnad->bna, bnad, tx_config, &tx_cbfn, res_info,
+			tx_info);
+	spin_unlock_irqrestore(&bnad->bna_lock, flags);
+	if (!tx)
+		goto err_return;
+	tx_info->tx = tx;
+
+	/* Register ISR for the Tx object */
+	if (intr_info->intr_type == BNA_INTR_T_MSIX) {
+		err = bnad_tx_msix_register(bnad, tx_info,
+			tx_id, bnad->num_txq_per_tx);
+		if (err)
+			goto err_return;
+	}
+
+	spin_lock_irqsave(&bnad->bna_lock, flags);
+	bna_tx_enable(tx);
+	spin_unlock_irqrestore(&bnad->bna_lock, flags);
+
+	return 0;
+
+err_return:
+	bnad_tx_res_free(bnad, res_info);
+	return err;
+}
+
+/* Setup the rx config for bna_rx_create */
+/* bnad decides the configuration */
+static void
+bnad_init_rx_config(struct bnad *bnad, struct bna_rx_config *rx_config)
+{
+	rx_config->rx_type = BNA_RX_T_REGULAR;
+	rx_config->num_paths = bnad->num_rxp_per_rx;
+
+	if (bnad->num_rxp_per_rx > 1) {
+		rx_config->rss_status = BNA_STATUS_T_ENABLED;
+		rx_config->rss_config.hash_type =
+				(BFI_RSS_T_V4_TCP |
+				 BFI_RSS_T_V6_TCP |
+				 BFI_RSS_T_V4_IP  |
+				 BFI_RSS_T_V6_IP);
+		rx_config->rss_config.hash_mask =
+				bnad->num_rxp_per_rx - 1;
+		get_random_bytes(rx_config->rss_config.toeplitz_hash_key,
+			sizeof(rx_config->rss_config.toeplitz_hash_key));
+	} else {
+		rx_config->rss_status = BNA_STATUS_T_DISABLED;
+		memset(&rx_config->rss_config, 0,
+		       sizeof(rx_config->rss_config));
+	}
+	rx_config->rxp_type = BNA_RXP_SLR;
+	rx_config->q_depth = bnad->rxq_depth;
+
+	rx_config->small_buff_size = BFI_SMALL_RXBUF_SIZE;
+
+	rx_config->vlan_strip_status = BNA_STATUS_T_ENABLED;
+}
+
+/* Called with mutex_lock(&bnad->conf_mutex) held */
+void
+bnad_cleanup_rx(struct bnad *bnad, uint rx_id)
+{
+	struct bnad_rx_info *rx_info = &bnad->rx_info[rx_id];
+	struct bna_rx_config *rx_config = &bnad->rx_config[rx_id];
+	struct bna_res_info *res_info = &bnad->rx_res_info[rx_id].res_info[0];
+	unsigned long flags;
+	int dim_timer_del = 0;
+
+	if (!rx_info->rx)
+		return;
+
+	if (0 == rx_id) {
+		spin_lock_irqsave(&bnad->bna_lock, flags);
+		dim_timer_del = bnad_dim_timer_running(bnad);
+		if (dim_timer_del)
+			clear_bit(BNAD_RF_DIM_TIMER_RUNNING, &bnad->run_flags);
+		spin_unlock_irqrestore(&bnad->bna_lock, flags);
+		if (dim_timer_del)
+			del_timer_sync(&bnad->dim_timer);
+	}
+
+	bnad_napi_disable(bnad, rx_id);
+
+	init_completion(&bnad->bnad_completions.rx_comp);
+	spin_lock_irqsave(&bnad->bna_lock, flags);
+	bna_rx_disable(rx_info->rx, BNA_HARD_CLEANUP, bnad_cb_rx_disabled);
+	spin_unlock_irqrestore(&bnad->bna_lock, flags);
+	wait_for_completion(&bnad->bnad_completions.rx_comp);
+
+	if (rx_info->rx_ctrl[0].ccb->intr_type == BNA_INTR_T_MSIX)
+		bnad_rx_msix_unregister(bnad, rx_info, rx_config->num_paths);
+
+	spin_lock_irqsave(&bnad->bna_lock, flags);
+	bna_rx_destroy(rx_info->rx);
+	spin_unlock_irqrestore(&bnad->bna_lock, flags);
+
+	rx_info->rx = NULL;
+
+	bnad_rx_res_free(bnad, res_info);
+}
+
+/* Called with mutex_lock(&bnad->conf_mutex) held */
+int
+bnad_setup_rx(struct bnad *bnad, uint rx_id)
+{
+	int err;
+	struct bnad_rx_info *rx_info = &bnad->rx_info[rx_id];
+	struct bna_res_info *res_info = &bnad->rx_res_info[rx_id].res_info[0];
+	struct bna_intr_info *intr_info =
+			&res_info[BNA_RX_RES_T_INTR].res_u.intr_info;
+	struct bna_rx_config *rx_config = &bnad->rx_config[rx_id];
+	struct bna_rx_event_cbfn rx_cbfn;
+	struct bna_rx *rx;
+	unsigned long flags;
+
+	/* Initialize the Rx object configuration */
+	bnad_init_rx_config(bnad, rx_config);
+
+	/* Initialize the Rx event handlers */
+	rx_cbfn.rcb_setup_cbfn = bnad_cb_rcb_setup;
+	rx_cbfn.rcb_destroy_cbfn = NULL;
+	rx_cbfn.ccb_setup_cbfn = bnad_cb_ccb_setup;
+	rx_cbfn.ccb_destroy_cbfn = bnad_cb_ccb_destroy;
+	rx_cbfn.rx_cleanup_cbfn = bnad_cb_rx_cleanup;
+	rx_cbfn.rx_post_cbfn = bnad_cb_rx_post;
+
+	/* Get BNA's resource requirement for one Rx object */
+	spin_lock_irqsave(&bnad->bna_lock, flags);
+	bna_rx_res_req(rx_config, res_info);
+	spin_unlock_irqrestore(&bnad->bna_lock, flags);
+
+	/* Fill Unmap Q memory requirements */
+	BNAD_FILL_UNMAPQ_MEM_REQ(
+			&res_info[BNA_RX_RES_MEM_T_UNMAPQ],
+			rx_config->num_paths +
+			((rx_config->rxp_type == BNA_RXP_SINGLE) ? 0 :
+				rx_config->num_paths), BNAD_RX_UNMAPQ_DEPTH);
+
+	/* Allocate resource */
+	err = bnad_rx_res_alloc(bnad, res_info, rx_id);
+	if (err)
+		return err;
+
+	/* Ask BNA to create one Rx object, supplying required resources */
+	spin_lock_irqsave(&bnad->bna_lock, flags);
+	rx = bna_rx_create(&bnad->bna, bnad, rx_config, &rx_cbfn, res_info,
+			rx_info);
+	spin_unlock_irqrestore(&bnad->bna_lock, flags);
+	if (!rx)
+		goto err_return;
+	rx_info->rx = rx;
+
+	/* Register ISR for the Rx object */
+	if (intr_info->intr_type == BNA_INTR_T_MSIX) {
+		err = bnad_rx_msix_register(bnad, rx_info, rx_id,
+						rx_config->num_paths);
+		if (err)
+			goto err_return;
+	}
+
+	/* Enable NAPI */
+	bnad_napi_enable(bnad, rx_id);
+
+	spin_lock_irqsave(&bnad->bna_lock, flags);
+	if (0 == rx_id) {
+		/* Set up Dynamic Interrupt Moderation Vector */
+		if (bnad->cfg_flags & BNAD_CF_DIM_ENABLED)
+			bna_rx_dim_reconfig(&bnad->bna, bna_napi_dim_vector);
+
+		/* Enable VLAN filtering only on the default Rx */
+		bna_rx_vlanfilter_enable(rx);
+
+		/* Start the DIM timer */
+		bnad_dim_timer_start(bnad);
+	}
+
+	bna_rx_enable(rx);
+	spin_unlock_irqrestore(&bnad->bna_lock, flags);
+
+	return 0;
+
+err_return:
+	bnad_cleanup_rx(bnad, rx_id);
+	return err;
+}
+
+/* Called with conf_lock & bnad->bna_lock held */
+void
+bnad_tx_coalescing_timeo_set(struct bnad *bnad)
+{
+	struct bnad_tx_info *tx_info;
+
+	tx_info = &bnad->tx_info[0];
+	if (!tx_info->tx)
+		return;
+
+	bna_tx_coalescing_timeo_set(tx_info->tx, bnad->tx_coalescing_timeo);
+}
+
+/* Called with conf_lock & bnad->bna_lock held */
+void
+bnad_rx_coalescing_timeo_set(struct bnad *bnad)
+{
+	struct bnad_rx_info *rx_info;
+	int 	i;
+
+	for (i = 0; i < bnad->num_rx; i++) {
+		rx_info = &bnad->rx_info[i];
+		if (!rx_info->rx)
+			continue;
+		bna_rx_coalescing_timeo_set(rx_info->rx,
+				bnad->rx_coalescing_timeo);
+	}
+}
+
+/*
+ * Called with bnad->bna_lock held
+ */
+static int
+bnad_mac_addr_set_locked(struct bnad *bnad, u8 *mac_addr)
+{
+	int ret;
+
+	if (!is_valid_ether_addr(mac_addr))
+		return -EADDRNOTAVAIL;
+
+	/* If datapath is down, pretend everything went through */
+	if (!bnad->rx_info[0].rx)
+		return 0;
+
+	ret = bna_rx_ucast_set(bnad->rx_info[0].rx, mac_addr, NULL);
+	if (ret != BNA_CB_SUCCESS)
+		return -EADDRNOTAVAIL;
+
+	return 0;
+}
+
+/* Should be called with conf_lock held */
+static int
+bnad_enable_default_bcast(struct bnad *bnad)
+{
+	struct bnad_rx_info *rx_info = &bnad->rx_info[0];
+	int ret;
+	unsigned long flags;
+
+	init_completion(&bnad->bnad_completions.mcast_comp);
+
+	spin_lock_irqsave(&bnad->bna_lock, flags);
+	ret = bna_rx_mcast_add(rx_info->rx, (u8 *)bnad_bcast_addr,
+				bnad_cb_rx_mcast_add);
+	spin_unlock_irqrestore(&bnad->bna_lock, flags);
+
+	if (ret == BNA_CB_SUCCESS)
+		wait_for_completion(&bnad->bnad_completions.mcast_comp);
+	else
+		return -ENODEV;
+
+	if (bnad->bnad_completions.mcast_comp_status != BNA_CB_SUCCESS)
+		return -ENODEV;
+
+	return 0;
+}
+
+/* Statistics utilities */
+void
+bnad_netdev_qstats_fill(struct bnad *bnad)
+{
+	struct net_device_stats *net_stats = &bnad->net_stats;
+	int i, j;
+
+	for (i = 0; i < bnad->num_rx; i++) {
+		for (j = 0; j < bnad->num_rxp_per_rx; j++) {
+			if (bnad->rx_info[i].rx_ctrl[j].ccb) {
+				net_stats->rx_packets += bnad->rx_info[i].
+				rx_ctrl[j].ccb->rcb[0]->rxq->rx_packets;
+				net_stats->rx_bytes += bnad->rx_info[i].
+					rx_ctrl[j].ccb->rcb[0]->rxq->rx_bytes;
+				if (bnad->rx_info[i].rx_ctrl[j].ccb->rcb[1] &&
+					bnad->rx_info[i].rx_ctrl[j].ccb->
+					rcb[1]->rxq) {
+					net_stats->rx_packets +=
+						bnad->rx_info[i].rx_ctrl[j].
+						ccb->rcb[1]->rxq->rx_packets;
+					net_stats->rx_bytes +=
+						bnad->rx_info[i].rx_ctrl[j].
+						ccb->rcb[1]->rxq->rx_bytes;
+				}
+			}
+		}
+	}
+	for (i = 0; i < bnad->num_tx; i++) {
+		for (j = 0; j < bnad->num_txq_per_tx; j++) {
+			if (bnad->tx_info[i].tcb[j]) {
+				net_stats->tx_packets +=
+				bnad->tx_info[i].tcb[j]->txq->tx_packets;
+				net_stats->tx_bytes +=
+					bnad->tx_info[i].tcb[j]->txq->tx_bytes;
+			}
+		}
+	}
+}
+
+/*
+ * Must be called with the bna_lock held.
+ */
+void
+bnad_netdev_hwstats_fill(struct bnad *bnad)
+{
+	struct bfi_ll_stats_mac *mac_stats;
+	struct net_device_stats *net_stats = &bnad->net_stats;
+	u64 bmap;
+	int i;
+
+	mac_stats = &bnad->stats.bna_stats->hw_stats->mac_stats;
+	net_stats->rx_errors =
+		mac_stats->rx_fcs_error + mac_stats->rx_alignment_error +
+		mac_stats->rx_frame_length_error + mac_stats->rx_code_error +
+		mac_stats->rx_undersize;
+	net_stats->tx_errors = mac_stats->tx_fcs_error +
+					mac_stats->tx_undersize;
+	net_stats->rx_dropped = mac_stats->rx_drop;
+	net_stats->tx_dropped = mac_stats->tx_drop;
+	net_stats->multicast = mac_stats->rx_multicast;
+	net_stats->collisions = mac_stats->tx_total_collision;
+
+	net_stats->rx_length_errors = mac_stats->rx_frame_length_error;
+
+	/* receive ring buffer overflow  ?? */
+
+	net_stats->rx_crc_errors = mac_stats->rx_fcs_error;
+	net_stats->rx_frame_errors = mac_stats->rx_alignment_error;
+	/* recv'r fifo overrun */
+	bmap = (u64)bnad->stats.bna_stats->rxf_bmap[0] |
+		((u64)bnad->stats.bna_stats->rxf_bmap[1] << 32);
+	for (i = 0; bmap && (i < BFI_LL_RXF_ID_MAX); i++) {
+		if (bmap & 1) {
+			net_stats->rx_fifo_errors =
+				bnad->stats.bna_stats->
+					hw_stats->rxf_stats[i].frame_drops;
+			break;
+		}
+		bmap >>= 1;
+	}
+}
+
+static void
+bnad_mbox_irq_sync(struct bnad *bnad)
+{
+	u32 irq;
+	unsigned long flags;
+
+	spin_lock_irqsave(&bnad->bna_lock, flags);
+	if (bnad->cfg_flags & BNAD_CF_MSIX)
+		irq = bnad->msix_table[bnad->msix_num - 1].vector;
+	else
+		irq = bnad->pcidev->irq;
+	spin_unlock_irqrestore(&bnad->bna_lock, flags);
+
+	synchronize_irq(irq);
+}
+
+/* Utility used by bnad_start_xmit, for doing TSO */
+static int
+bnad_tso_prepare(struct bnad *bnad, struct sk_buff *skb)
+{
+	int err;
+
+	/* SKB_GSO_TCPV4 and SKB_GSO_TCPV6 is defined since 2.6.18. */
+	BUG_ON(!(skb_shinfo(skb)->gso_type == SKB_GSO_TCPV4 ||
+		   skb_shinfo(skb)->gso_type == SKB_GSO_TCPV6));
+	if (skb_header_cloned(skb)) {
+		err = pskb_expand_head(skb, 0, 0, GFP_ATOMIC);
+		if (err) {
+			BNAD_UPDATE_CTR(bnad, tso_err);
+			return err;
+		}
+	}
+
+	/*
+	 * For TSO, the TCP checksum field is seeded with pseudo-header sum
+	 * excluding the length field.
+	 */
+	if (skb->protocol == htons(ETH_P_IP)) {
+		struct iphdr *iph = ip_hdr(skb);
+
+		/* Do we really need these? */
+		iph->tot_len = 0;
+		iph->check = 0;
+
+		tcp_hdr(skb)->check =
+			~csum_tcpudp_magic(iph->saddr, iph->daddr, 0,
+					   IPPROTO_TCP, 0);
+		BNAD_UPDATE_CTR(bnad, tso4);
+	} else {
+		struct ipv6hdr *ipv6h = ipv6_hdr(skb);
+
+		BUG_ON(!(skb->protocol == htons(ETH_P_IPV6)));
+		ipv6h->payload_len = 0;
+		tcp_hdr(skb)->check =
+			~csum_ipv6_magic(&ipv6h->saddr, &ipv6h->daddr, 0,
+					 IPPROTO_TCP, 0);
+		BNAD_UPDATE_CTR(bnad, tso6);
+	}
+
+	return 0;
+}
+
+/*
+ * Initialize Q numbers depending on Rx Paths
+ * Called with bnad->bna_lock held, because of cfg_flags
+ * access.
+ */
+static void
+bnad_q_num_init(struct bnad *bnad)
+{
+	int rxps;
+
+	rxps = min((uint)num_online_cpus(),
+			(uint)(BNAD_MAX_RXS * BNAD_MAX_RXPS_PER_RX));
+
+	if (!(bnad->cfg_flags & BNAD_CF_MSIX))
+		rxps = 1;	/* INTx */
+
+	bnad->num_rx = 1;
+	bnad->num_tx = 1;
+	bnad->num_rxp_per_rx = rxps;
+	bnad->num_txq_per_tx = BNAD_TXQ_NUM;
+}
+
+/*
+ * Adjusts the Q numbers, given a number of msix vectors
+ * Give preference to RSS as opposed to Tx priority Queues,
+ * in such a case, just use 1 Tx Q
+ * Called with bnad->bna_lock held b'cos of cfg_flags access
+ */
+static void
+bnad_q_num_adjust(struct bnad *bnad, int msix_vectors)
+{
+	bnad->num_txq_per_tx = 1;
+	if ((msix_vectors >= (bnad->num_tx * bnad->num_txq_per_tx)  +
+	     bnad_rxqs_per_cq + BNAD_MAILBOX_MSIX_VECTORS) &&
+	    (bnad->cfg_flags & BNAD_CF_MSIX)) {
+		bnad->num_rxp_per_rx = msix_vectors -
+			(bnad->num_tx * bnad->num_txq_per_tx) -
+			BNAD_MAILBOX_MSIX_VECTORS;
+	} else
+		bnad->num_rxp_per_rx = 1;
+}
+
+static void
+bnad_set_netdev_perm_addr(struct bnad *bnad)
+{
+	struct net_device *netdev = bnad->netdev;
+
+	memcpy(netdev->perm_addr, &bnad->perm_addr, netdev->addr_len);
+	if (is_zero_ether_addr(netdev->dev_addr))
+		memcpy(netdev->dev_addr, &bnad->perm_addr, netdev->addr_len);
+}
+
+/* Enable / disable device */
+static void
+bnad_device_disable(struct bnad *bnad)
+{
+	unsigned long flags;
+
+	init_completion(&bnad->bnad_completions.ioc_comp);
+
+	spin_lock_irqsave(&bnad->bna_lock, flags);
+	bna_device_disable(&bnad->bna.device, BNA_HARD_CLEANUP);
+	spin_unlock_irqrestore(&bnad->bna_lock, flags);
+
+	wait_for_completion(&bnad->bnad_completions.ioc_comp);
+
+}
+
+static int
+bnad_device_enable(struct bnad *bnad)
+{
+	int err = 0;
+	unsigned long flags;
+
+	init_completion(&bnad->bnad_completions.ioc_comp);
+
+	spin_lock_irqsave(&bnad->bna_lock, flags);
+	bna_device_enable(&bnad->bna.device);
+	spin_unlock_irqrestore(&bnad->bna_lock, flags);
+
+	wait_for_completion(&bnad->bnad_completions.ioc_comp);
+
+	if (bnad->bnad_completions.ioc_comp_status)
+		err = bnad->bnad_completions.ioc_comp_status;
+
+	return err;
+}
+
+/* Free BNA resources */
+static void
+bnad_res_free(struct bnad *bnad)
+{
+	int i;
+	struct bna_res_info *res_info = &bnad->res_info[0];
+
+	for (i = 0; i < BNA_RES_T_MAX; i++) {
+		if (res_info[i].res_type == BNA_RES_T_MEM)
+			bnad_mem_free(bnad, &res_info[i].res_u.mem_info);
+		else
+			bnad_mbox_irq_free(bnad, &res_info[i].res_u.intr_info);
+	}
+}
+
+/* Allocates memory and interrupt resources for BNA */
+static int
+bnad_res_alloc(struct bnad *bnad)
+{
+	int i, err;
+	struct bna_res_info *res_info = &bnad->res_info[0];
+
+	for (i = 0; i < BNA_RES_T_MAX; i++) {
+		if (res_info[i].res_type == BNA_RES_T_MEM)
+			err = bnad_mem_alloc(bnad, &res_info[i].res_u.mem_info);
+		else
+			err = bnad_mbox_irq_alloc(bnad,
+						  &res_info[i].res_u.intr_info);
+		if (err)
+			goto err_return;
+	}
+	return 0;
+
+err_return:
+	bnad_res_free(bnad);
+	return err;
+}
+
+/* Interrupt enable / disable */
+static void
+bnad_enable_msix(struct bnad *bnad)
+{
+	int i, ret;
+	u32 tot_msix_num;
+	unsigned long flags;
+
+	spin_lock_irqsave(&bnad->bna_lock, flags);
+	if (!(bnad->cfg_flags & BNAD_CF_MSIX)) {
+		spin_unlock_irqrestore(&bnad->bna_lock, flags);
+		return;
+	}
+	spin_unlock_irqrestore(&bnad->bna_lock, flags);
+
+	if (bnad->msix_table)
+		return;
+
+	tot_msix_num = bnad->msix_num + bnad->msix_diag_num;
+
+	bnad->msix_table =
+		kcalloc(tot_msix_num, sizeof(struct msix_entry), GFP_KERNEL);
+
+	if (!bnad->msix_table)
+		goto intx_mode;
+
+	for (i = 0; i < tot_msix_num; i++)
+		bnad->msix_table[i].entry = i;
+
+	ret = pci_enable_msix(bnad->pcidev, bnad->msix_table, tot_msix_num);
+	if (ret > 0) {
+		/* Not enough MSI-X vectors. */
+
+		spin_lock_irqsave(&bnad->bna_lock, flags);
+		/* ret = #of vectors that we got */
+		bnad_q_num_adjust(bnad, ret);
+		spin_unlock_irqrestore(&bnad->bna_lock, flags);
+
+		bnad->msix_num = (bnad->num_tx * bnad->num_txq_per_tx)
+			+ (bnad->num_rx
+			* bnad->num_rxp_per_rx) +
+			 BNAD_MAILBOX_MSIX_VECTORS;
+		tot_msix_num = bnad->msix_num + bnad->msix_diag_num;
+
+		/* Try once more with adjusted numbers */
+		/* If this fails, fall back to INTx */
+		ret = pci_enable_msix(bnad->pcidev, bnad->msix_table,
+				      tot_msix_num);
+		if (ret)
+			goto intx_mode;
+
+	} else if (ret < 0)
+		goto intx_mode;
+	return;
+
+intx_mode:
+
+	kfree(bnad->msix_table);
+	bnad->msix_table = NULL;
+	bnad->msix_num = 0;
+	bnad->msix_diag_num = 0;
+	spin_lock_irqsave(&bnad->bna_lock, flags);
+	bnad->cfg_flags &= ~BNAD_CF_MSIX;
+	bnad_q_num_init(bnad);
+	spin_unlock_irqrestore(&bnad->bna_lock, flags);
+}
+
+static void
+bnad_disable_msix(struct bnad *bnad)
+{
+	u32 cfg_flags;
+	unsigned long flags;
+
+	spin_lock_irqsave(&bnad->bna_lock, flags);
+	cfg_flags = bnad->cfg_flags;
+	if (bnad->cfg_flags & BNAD_CF_MSIX)
+		bnad->cfg_flags &= ~BNAD_CF_MSIX;
+	spin_unlock_irqrestore(&bnad->bna_lock, flags);
+
+	if (cfg_flags & BNAD_CF_MSIX) {
+		pci_disable_msix(bnad->pcidev);
+		kfree(bnad->msix_table);
+		bnad->msix_table = NULL;
+	}
+}
+
+/* Netdev entry points */
+static int
+bnad_open(struct net_device *netdev)
+{
+	int err;
+	struct bnad *bnad = netdev_priv(netdev);
+	struct bna_pause_config pause_config;
+	int mtu;
+	unsigned long flags;
+
+	mutex_lock(&bnad->conf_mutex);
+
+	/* Tx */
+	err = bnad_setup_tx(bnad, 0);
+	if (err)
+		goto err_return;
+
+	/* Rx */
+	err = bnad_setup_rx(bnad, 0);
+	if (err)
+		goto cleanup_tx;
+
+	/* Port */
+	pause_config.tx_pause = 0;
+	pause_config.rx_pause = 0;
+
+	mtu = ETH_HLEN + bnad->netdev->mtu + ETH_FCS_LEN;
+
+	spin_lock_irqsave(&bnad->bna_lock, flags);
+	bna_port_mtu_set(&bnad->bna.port, mtu, NULL);
+	bna_port_pause_config(&bnad->bna.port, &pause_config, NULL);
+	bna_port_enable(&bnad->bna.port);
+	spin_unlock_irqrestore(&bnad->bna_lock, flags);
+
+	/* Enable broadcast */
+	bnad_enable_default_bcast(bnad);
+
+	/* Set the UCAST address */
+	spin_lock_irqsave(&bnad->bna_lock, flags);
+	bnad_mac_addr_set_locked(bnad, netdev->dev_addr);
+	spin_unlock_irqrestore(&bnad->bna_lock, flags);
+
+	/* Start the stats timer */
+	bnad_stats_timer_start(bnad);
+
+	mutex_unlock(&bnad->conf_mutex);
+
+	return 0;
+
+cleanup_tx:
+	bnad_cleanup_tx(bnad, 0);
+
+err_return:
+	mutex_unlock(&bnad->conf_mutex);
+	return err;
+}
+
+static int
+bnad_stop(struct net_device *netdev)
+{
+	struct bnad *bnad = netdev_priv(netdev);
+	unsigned long flags;
+
+	mutex_lock(&bnad->conf_mutex);
+
+	/* Stop the stats timer */
+	bnad_stats_timer_stop(bnad);
+
+	init_completion(&bnad->bnad_completions.port_comp);
+
+	spin_lock_irqsave(&bnad->bna_lock, flags);
+	bna_port_disable(&bnad->bna.port, BNA_HARD_CLEANUP,
+			bnad_cb_port_disabled);
+	spin_unlock_irqrestore(&bnad->bna_lock, flags);
+
+	wait_for_completion(&bnad->bnad_completions.port_comp);
+
+	bnad_cleanup_tx(bnad, 0);
+	bnad_cleanup_rx(bnad, 0);
+
+	/* Synchronize mailbox IRQ */
+	bnad_mbox_irq_sync(bnad);
+
+	mutex_unlock(&bnad->conf_mutex);
+
+	return 0;
+}
+
+/* TX */
+/*
+ * bnad_start_xmit : Netdev entry point for Transmit
+ *		     Called under lock held by net_device
+ */
+static netdev_tx_t
+bnad_start_xmit(struct sk_buff *skb, struct net_device *netdev)
+{
+	struct bnad *bnad = netdev_priv(netdev);
+
+	u16 		txq_prod, vlan_tag = 0;
+	u32 		unmap_prod, wis, wis_used, wi_range;
+	u32 		vectors, vect_id, i, acked;
+	u32		tx_id;
+	int 			err;
+
+	struct bnad_tx_info *tx_info;
+	struct bna_tcb *tcb;
+	struct bnad_unmap_q *unmap_q;
+	dma_addr_t 		dma_addr;
+	struct bna_txq_entry *txqent;
+	bna_txq_wi_ctrl_flag_t 	flags;
+
+	if (unlikely
+	    (skb->len <= ETH_HLEN || skb->len > BFI_TX_MAX_DATA_PER_PKT)) {
+		dev_kfree_skb(skb);
+		return NETDEV_TX_OK;
+	}
+
+	/*
+	 * Takes care of the Tx that is scheduled between clearing the flag
+	 * and the netif_stop_queue() call.
+	 */
+	if (unlikely(!test_bit(BNAD_RF_TX_STARTED, &bnad->run_flags))) {
+		dev_kfree_skb(skb);
+		return NETDEV_TX_OK;
+	}
+
+	tx_id = 0;
+
+	tx_info = &bnad->tx_info[tx_id];
+	tcb = tx_info->tcb[tx_id];
+	unmap_q = tcb->unmap_q;
+
+	vectors = 1 + skb_shinfo(skb)->nr_frags;
+	if (vectors > BFI_TX_MAX_VECTORS_PER_PKT) {
+		dev_kfree_skb(skb);
+		return NETDEV_TX_OK;
+	}
+	wis = BNA_TXQ_WI_NEEDED(vectors);	/* 4 vectors per work item */
+	acked = 0;
+	if (unlikely
+	    (wis > BNA_QE_FREE_CNT(tcb, tcb->q_depth) ||
+	     vectors > BNA_QE_FREE_CNT(unmap_q, unmap_q->q_depth))) {
+		if ((u16) (*tcb->hw_consumer_index) !=
+		    tcb->consumer_index &&
+		    !test_and_set_bit(BNAD_TXQ_FREE_SENT, &tcb->flags)) {
+			acked = bnad_free_txbufs(bnad, tcb);
+			bna_ib_ack(tcb->i_dbell, acked);
+			smp_mb__before_clear_bit();
+			clear_bit(BNAD_TXQ_FREE_SENT, &tcb->flags);
+		} else {
+			netif_stop_queue(netdev);
+			BNAD_UPDATE_CTR(bnad, netif_queue_stop);
+		}
+
+		smp_mb();
+		/*
+		 * Check again to deal with race condition between
+		 * netif_stop_queue here, and netif_wake_queue in
+		 * interrupt handler which is not inside netif tx lock.
+		 */
+		if (likely
+		    (wis > BNA_QE_FREE_CNT(tcb, tcb->q_depth) ||
+		     vectors > BNA_QE_FREE_CNT(unmap_q, unmap_q->q_depth))) {
+			BNAD_UPDATE_CTR(bnad, netif_queue_stop);
+			return NETDEV_TX_BUSY;
+		} else {
+			netif_wake_queue(netdev);
+			BNAD_UPDATE_CTR(bnad, netif_queue_wakeup);
+		}
+	}
+
+	unmap_prod = unmap_q->producer_index;
+	wis_used = 1;
+	vect_id = 0;
+	flags = 0;
+
+	txq_prod = tcb->producer_index;
+	BNA_TXQ_QPGE_PTR_GET(txq_prod, tcb->sw_qpt, txqent, wi_range);
+	BUG_ON(!(wi_range <= tcb->q_depth));
+	txqent->hdr.wi.reserved = 0;
+	txqent->hdr.wi.num_vectors = vectors;
+	txqent->hdr.wi.opcode =
+		htons((skb_is_gso(skb) ? BNA_TXQ_WI_SEND_LSO :
+		       BNA_TXQ_WI_SEND));
+
+	if (bnad->vlan_grp && vlan_tx_tag_present(skb)) {
+		vlan_tag = (u16) vlan_tx_tag_get(skb);
+		flags |= (BNA_TXQ_WI_CF_INS_PRIO | BNA_TXQ_WI_CF_INS_VLAN);
+	}
+	if (test_bit(BNAD_RF_CEE_RUNNING, &bnad->run_flags)) {
+		vlan_tag =
+			(tcb->priority & 0x7) << 13 | (vlan_tag & 0x1fff);
+		flags |= (BNA_TXQ_WI_CF_INS_PRIO | BNA_TXQ_WI_CF_INS_VLAN);
+	}
+
+	txqent->hdr.wi.vlan_tag = htons(vlan_tag);
+
+	if (skb_is_gso(skb)) {
+		err = bnad_tso_prepare(bnad, skb);
+		if (err) {
+			dev_kfree_skb(skb);
+			return NETDEV_TX_OK;
+		}
+		txqent->hdr.wi.lso_mss = htons(skb_is_gso(skb));
+		flags |= (BNA_TXQ_WI_CF_IP_CKSUM | BNA_TXQ_WI_CF_TCP_CKSUM);
+		txqent->hdr.wi.l4_hdr_size_n_offset =
+			htons(BNA_TXQ_WI_L4_HDR_N_OFFSET
+			      (tcp_hdrlen(skb) >> 2,
+			       skb_transport_offset(skb)));
+	} else if (skb->ip_summed == CHECKSUM_PARTIAL) {
+		u8 proto = 0;
+
+		txqent->hdr.wi.lso_mss = 0;
+
+		if (skb->protocol == htons(ETH_P_IP))
+			proto = ip_hdr(skb)->protocol;
+		else if (skb->protocol == htons(ETH_P_IPV6)) {
+			/* nexthdr may not be TCP immediately. */
+			proto = ipv6_hdr(skb)->nexthdr;
+		}
+		if (proto == IPPROTO_TCP) {
+			flags |= BNA_TXQ_WI_CF_TCP_CKSUM;
+			txqent->hdr.wi.l4_hdr_size_n_offset =
+				htons(BNA_TXQ_WI_L4_HDR_N_OFFSET
+				      (0, skb_transport_offset(skb)));
+
+			BNAD_UPDATE_CTR(bnad, tcpcsum_offload);
+
+			BUG_ON(!(skb_headlen(skb) >=
+				skb_transport_offset(skb) + tcp_hdrlen(skb)));
+
+		} else if (proto == IPPROTO_UDP) {
+			flags |= BNA_TXQ_WI_CF_UDP_CKSUM;
+			txqent->hdr.wi.l4_hdr_size_n_offset =
+				htons(BNA_TXQ_WI_L4_HDR_N_OFFSET
+				      (0, skb_transport_offset(skb)));
+
+			BNAD_UPDATE_CTR(bnad, udpcsum_offload);
+
+			BUG_ON(!(skb_headlen(skb) >=
+				   skb_transport_offset(skb) +
+				   sizeof(struct udphdr)));
+		} else {
+			err = skb_checksum_help(skb);
+			BNAD_UPDATE_CTR(bnad, csum_help);
+			if (err) {
+				dev_kfree_skb(skb);
+				BNAD_UPDATE_CTR(bnad, csum_help_err);
+				return NETDEV_TX_OK;
+			}
+		}
+	} else {
+		txqent->hdr.wi.lso_mss = 0;
+		txqent->hdr.wi.l4_hdr_size_n_offset = 0;
+	}
+
+	txqent->hdr.wi.flags = htons(flags);
+
+	txqent->hdr.wi.frame_length = htonl(skb->len);
+
+	unmap_q->unmap_array[unmap_prod].skb = skb;
+	BUG_ON(!(skb_headlen(skb) <= BFI_TX_MAX_DATA_PER_VECTOR));
+	txqent->vector[vect_id].length = htons(skb_headlen(skb));
+	dma_addr = pci_map_single(bnad->pcidev, skb->data, skb_headlen(skb),
+		PCI_DMA_TODEVICE);
+	pci_unmap_addr_set(&unmap_q->unmap_array[unmap_prod], dma_addr,
+			   dma_addr);
+
+	BNA_SET_DMA_ADDR(dma_addr, &txqent->vector[vect_id].host_addr);
+	BNA_QE_INDX_ADD(unmap_prod, 1, unmap_q->q_depth);
+
+	for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
+		struct skb_frag_struct *frag = &skb_shinfo(skb)->frags[i];
+		u32		size = frag->size;
+
+		if (++vect_id == BFI_TX_MAX_VECTORS_PER_WI) {
+			vect_id = 0;
+			if (--wi_range)
+				txqent++;
+			else {
+				BNA_QE_INDX_ADD(txq_prod, wis_used,
+						tcb->q_depth);
+				wis_used = 0;
+				BNA_TXQ_QPGE_PTR_GET(txq_prod, tcb->sw_qpt,
+						     txqent, wi_range);
+				BUG_ON(!(wi_range <= tcb->q_depth));
+			}
+			wis_used++;
+			txqent->hdr.wi_ext.opcode = htons(BNA_TXQ_WI_EXTENSION);
+		}
+
+		BUG_ON(!(size <= BFI_TX_MAX_DATA_PER_VECTOR));
+		txqent->vector[vect_id].length = htons(size);
+		dma_addr =
+			pci_map_page(bnad->pcidev, frag->page,
+				     frag->page_offset, size,
+				     PCI_DMA_TODEVICE);
+		pci_unmap_addr_set(&unmap_q->unmap_array[unmap_prod], dma_addr,
+				   dma_addr);
+		BNA_SET_DMA_ADDR(dma_addr, &txqent->vector[vect_id].host_addr);
+		BNA_QE_INDX_ADD(unmap_prod, 1, unmap_q->q_depth);
+	}
+
+	unmap_q->producer_index = unmap_prod;
+	BNA_QE_INDX_ADD(txq_prod, wis_used, tcb->q_depth);
+	tcb->producer_index = txq_prod;
+
+	smp_mb();
+	bna_txq_prod_indx_doorbell(tcb);
+
+	if ((u16) (*tcb->hw_consumer_index) != tcb->consumer_index)
+		tasklet_schedule(&bnad->tx_free_tasklet);
+
+	return NETDEV_TX_OK;
+}
+
+/*
+ * Used spin_lock to synchronize reading of stats structures, which
+ * is written by BNA under the same lock.
+ */
+static struct net_device_stats *
+bnad_get_netdev_stats(struct net_device *netdev)
+{
+	struct bnad *bnad = netdev_priv(netdev);
+	unsigned long flags;
+
+	spin_lock_irqsave(&bnad->bna_lock, flags);
+
+	memset(&bnad->net_stats, 0, sizeof(struct net_device_stats));
+
+	bnad_netdev_qstats_fill(bnad);
+	bnad_netdev_hwstats_fill(bnad);
+
+	spin_unlock_irqrestore(&bnad->bna_lock, flags);
+
+	return &bnad->net_stats;
+}
+
+static void
+bnad_set_rx_mode(struct net_device *netdev)
+{
+	struct bnad *bnad = netdev_priv(netdev);
+	u32	new_mask, valid_mask;
+	unsigned long flags;
+
+	spin_lock_irqsave(&bnad->bna_lock, flags);
+
+	new_mask = valid_mask = 0;
+
+	if (netdev->flags & IFF_PROMISC) {
+		if (!(bnad->cfg_flags & BNAD_CF_PROMISC)) {
+			new_mask = BNAD_RXMODE_PROMISC_DEFAULT;
+			valid_mask = BNAD_RXMODE_PROMISC_DEFAULT;
+			bnad->cfg_flags |= BNAD_CF_PROMISC;
+		}
+	} else {
+		if (bnad->cfg_flags & BNAD_CF_PROMISC) {
+			new_mask = ~BNAD_RXMODE_PROMISC_DEFAULT;
+			valid_mask = BNAD_RXMODE_PROMISC_DEFAULT;
+			bnad->cfg_flags &= ~BNAD_CF_PROMISC;
+		}
+	}
+
+	if (netdev->flags & IFF_ALLMULTI) {
+		if (!(bnad->cfg_flags & BNAD_CF_ALLMULTI)) {
+			new_mask |= BNA_RXMODE_ALLMULTI;
+			valid_mask |= BNA_RXMODE_ALLMULTI;
+			bnad->cfg_flags |= BNAD_CF_ALLMULTI;
+		}
+	} else {
+		if (bnad->cfg_flags & BNAD_CF_ALLMULTI) {
+			new_mask &= ~BNA_RXMODE_ALLMULTI;
+			valid_mask |= BNA_RXMODE_ALLMULTI;
+			bnad->cfg_flags &= ~BNAD_CF_ALLMULTI;
+		}
+	}
+
+	bna_rx_mode_set(bnad->rx_info[0].rx, new_mask, valid_mask, NULL);
+
+	if (!netdev_mc_empty(netdev)) {
+		u8 *mcaddr_list;
+		int mc_count = netdev_mc_count(netdev);
+
+		/* Index 0 holds the broadcast address */
+		mcaddr_list =
+			kzalloc((mc_count + 1) * ETH_ALEN,
+				GFP_ATOMIC);
+		if (!mcaddr_list)
+			return;
+
+		memcpy(&mcaddr_list[0], &bnad_bcast_addr[0], ETH_ALEN);
+
+		/* Copy rest of the MC addresses */
+		bnad_netdev_mc_list_get(netdev, mcaddr_list);
+
+		bna_rx_mcast_listset(bnad->rx_info[0].rx, mc_count + 1,
+					mcaddr_list, NULL);
+
+		/* Should we enable BNAD_CF_ALLMULTI for err != 0 ? */
+		kfree(mcaddr_list);
+	}
+	spin_unlock_irqrestore(&bnad->bna_lock, flags);
+}
+
+/*
+ * bna_lock is used to sync writes to netdev->addr
+ * conf_lock cannot be used since this call may be made
+ * in a non-blocking context.
+ */
+static int
+bnad_set_mac_address(struct net_device *netdev, void *mac_addr)
+{
+	int err;
+	struct bnad *bnad = netdev_priv(netdev);
+	struct sockaddr *sa = (struct sockaddr *)mac_addr;
+	unsigned long flags;
+
+	spin_lock_irqsave(&bnad->bna_lock, flags);
+
+	err = bnad_mac_addr_set_locked(bnad, sa->sa_data);
+
+	if (!err)
+		memcpy(netdev->dev_addr, sa->sa_data, netdev->addr_len);
+
+	spin_unlock_irqrestore(&bnad->bna_lock, flags);
+
+	return err;
+}
+
+static int
+bnad_change_mtu(struct net_device *netdev, int new_mtu)
+{
+	int mtu, err = 0;
+	unsigned long flags;
+
+	struct bnad *bnad = netdev_priv(netdev);
+
+	if (new_mtu + ETH_HLEN < ETH_ZLEN || new_mtu > BNAD_JUMBO_MTU)
+		return -EINVAL;
+
+	mutex_lock(&bnad->conf_mutex);
+
+	netdev->mtu = new_mtu;
+
+	mtu = ETH_HLEN + new_mtu + ETH_FCS_LEN;
+
+	spin_lock_irqsave(&bnad->bna_lock, flags);
+	bna_port_mtu_set(&bnad->bna.port, mtu, NULL);
+	spin_unlock_irqrestore(&bnad->bna_lock, flags);
+
+	mutex_unlock(&bnad->conf_mutex);
+	return err;
+}
+
+static void
+bnad_vlan_rx_register(struct net_device *netdev,
+				  struct vlan_group *vlan_grp)
+{
+	struct bnad *bnad = netdev_priv(netdev);
+
+	mutex_lock(&bnad->conf_mutex);
+	bnad->vlan_grp = vlan_grp;
+	mutex_unlock(&bnad->conf_mutex);
+}
+
+static void
+bnad_vlan_rx_add_vid(struct net_device *netdev,
+				 unsigned short vid)
+{
+	struct bnad *bnad = netdev_priv(netdev);
+	unsigned long flags;
+
+	if (!bnad->rx_info[0].rx)
+		return;
+
+	mutex_lock(&bnad->conf_mutex);
+
+	spin_lock_irqsave(&bnad->bna_lock, flags);
+	bna_rx_vlan_add(bnad->rx_info[0].rx, vid);
+	spin_unlock_irqrestore(&bnad->bna_lock, flags);
+
+	mutex_unlock(&bnad->conf_mutex);
+}
+
+static void
+bnad_vlan_rx_kill_vid(struct net_device *netdev,
+				  unsigned short vid)
+{
+	struct bnad *bnad = netdev_priv(netdev);
+	unsigned long flags;
+
+	if (!bnad->rx_info[0].rx)
+		return;
+
+	mutex_lock(&bnad->conf_mutex);
+
+	spin_lock_irqsave(&bnad->bna_lock, flags);
+	bna_rx_vlan_del(bnad->rx_info[0].rx, vid);
+	spin_unlock_irqrestore(&bnad->bna_lock, flags);
+
+	mutex_unlock(&bnad->conf_mutex);
+}
+
+#ifdef CONFIG_NET_POLL_CONTROLLER
+static void
+bnad_netpoll(struct net_device *netdev)
+{
+	struct bnad *bnad = netdev_priv(netdev);
+	struct bnad_rx_info *rx_info;
+	struct bnad_rx_ctrl *rx_ctrl;
+	u32 curr_mask;
+	int i, j;
+
+	if (!(bnad->cfg_flags & BNAD_CF_MSIX)) {
+		bna_intx_disable(&bnad->bna, curr_mask);
+		bnad_isr(bnad->pcidev->irq, netdev);
+		bna_intx_enable(&bnad->bna, curr_mask);
+	} else {
+		for (i = 0; i < bnad->num_rx; i++) {
+			rx_info = &bnad->rx_info[i];
+			if (!rx_info->rx)
+				continue;
+			for (j = 0; j < bnad->num_rxp_per_rx; j++) {
+				rx_ctrl = &rx_info->rx_ctrl[j];
+				if (rx_ctrl->ccb) {
+					bnad_disable_rx_irq(bnad,
+							    rx_ctrl->ccb);
+					bnad_netif_rx_schedule_poll(bnad,
+							    rx_ctrl->ccb);
+				}
+			}
+		}
+	}
+}
+#endif
+
+static const struct net_device_ops bnad_netdev_ops = {
+	.ndo_open		= bnad_open,
+	.ndo_stop		= bnad_stop,
+	.ndo_start_xmit		= bnad_start_xmit,
+	.ndo_get_stats		= bnad_get_netdev_stats,
+	.ndo_set_rx_mode	= bnad_set_rx_mode,
+	.ndo_set_multicast_list = bnad_set_rx_mode,
+	.ndo_validate_addr      = eth_validate_addr,
+	.ndo_set_mac_address    = bnad_set_mac_address,
+	.ndo_change_mtu		= bnad_change_mtu,
+	.ndo_vlan_rx_register   = bnad_vlan_rx_register,
+	.ndo_vlan_rx_add_vid    = bnad_vlan_rx_add_vid,
+	.ndo_vlan_rx_kill_vid   = bnad_vlan_rx_kill_vid,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+	.ndo_poll_controller    = bnad_netpoll
+#endif
+};
+
+static void
+bnad_netdev_init(struct bnad *bnad, bool using_dac)
+{
+	struct net_device *netdev = bnad->netdev;
+
+	netdev->features |= NETIF_F_IPV6_CSUM;
+	netdev->features |= NETIF_F_TSO;
+	netdev->features |= NETIF_F_TSO6;
+
+	netdev->features |= NETIF_F_GRO;
+	printk(KERN_WARNING
+	       "bna: GRO enabled, using kernel stack GRO\n");
+
+	netdev->features |= NETIF_F_SG | NETIF_F_IP_CSUM;
+
+	if (using_dac)
+		netdev->features |= NETIF_F_HIGHDMA;
+
+	netdev->features |=
+		NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX |
+		NETIF_F_HW_VLAN_FILTER;
+
+	netdev->vlan_features = netdev->features;
+	netdev->mem_start = bnad->mmio_start;
+	netdev->mem_end = bnad->mmio_start + bnad->mmio_len - 1;
+
+	netdev->netdev_ops = &bnad_netdev_ops;
+	bnad_set_ethtool_ops(netdev);
+}
+
+/*
+ * 1. Initialize the bnad structure
+ * 2. Setup netdev pointer in pci_dev
+ * 3. Initialze Tx free tasklet
+ * 4. Initialize no. of TxQ & CQs & MSIX vectors
+ */
+static int
+bnad_init(struct bnad *bnad,
+	  struct pci_dev *pdev, struct net_device *netdev)
+{
+	unsigned long flags;
+
+	SET_NETDEV_DEV(netdev, &pdev->dev);
+	pci_set_drvdata(pdev, netdev);
+
+	bnad->netdev = netdev;
+	bnad->pcidev = pdev;
+	bnad->mmio_start = pci_resource_start(pdev, 0);
+	bnad->mmio_len = pci_resource_len(pdev, 0);
+	bnad->bar0 = ioremap_nocache(bnad->mmio_start, bnad->mmio_len);
+	if (!bnad->bar0) {
+		dev_err(&pdev->dev, "ioremap for bar0 failed\n");
+		pci_set_drvdata(pdev, NULL);
+		return -ENOMEM;
+	}
+	printk(KERN_INFO "bar0 mapped to %p, len %llu\n", bnad->bar0,
+	       (unsigned long long) bnad->mmio_len);
+
+	spin_lock_irqsave(&bnad->bna_lock, flags);
+	if (!bnad_msix_disable)
+		bnad->cfg_flags = BNAD_CF_MSIX;
+
+	bnad->cfg_flags |= BNAD_CF_DIM_ENABLED;
+
+	bnad_q_num_init(bnad);
+	spin_unlock_irqrestore(&bnad->bna_lock, flags);
+
+	bnad->msix_num = (bnad->num_tx * bnad->num_txq_per_tx) +
+		(bnad->num_rx * bnad->num_rxp_per_rx) +
+			 BNAD_MAILBOX_MSIX_VECTORS;
+	bnad->msix_diag_num = 2;	/* 1 for Tx, 1 for Rx */
+
+	bnad->txq_depth = BNAD_TXQ_DEPTH;
+	bnad->rxq_depth = BNAD_RXQ_DEPTH;
+	bnad->rx_csum = true;
+
+	bnad->tx_coalescing_timeo = BFI_TX_COALESCING_TIMEO;
+	bnad->rx_coalescing_timeo = BFI_RX_COALESCING_TIMEO;
+
+	tasklet_init(&bnad->tx_free_tasklet, bnad_tx_free_tasklet,
+		     (unsigned long)bnad);
+
+	return 0;
+}
+
+/*
+ * Must be called after bnad_pci_uninit()
+ * so that iounmap() and pci_set_drvdata(NULL)
+ * happens only after PCI uninitialization.
+ */
+static void
+bnad_uninit(struct bnad *bnad)
+{
+	if (bnad->bar0)
+		iounmap(bnad->bar0);
+	pci_set_drvdata(bnad->pcidev, NULL);
+}
+
+/*
+ * Initialize locks
+	a) Per device mutes used for serializing configuration
+	   changes from OS interface
+	b) spin lock used to protect bna state machine
+ */
+static void
+bnad_lock_init(struct bnad *bnad)
+{
+	spin_lock_init(&bnad->bna_lock);
+	mutex_init(&bnad->conf_mutex);
+}
+
+static void
+bnad_lock_uninit(struct bnad *bnad)
+{
+	mutex_destroy(&bnad->conf_mutex);
+}
+
+/* PCI Initialization */
+static int
+bnad_pci_init(struct bnad *bnad,
+	      struct pci_dev *pdev, bool *using_dac)
+{
+	int err;
+
+	err = pci_enable_device(pdev);
+	if (err)
+		return err;
+	err = pci_request_regions(pdev, BNAD_NAME);
+	if (err)
+		goto disable_device;
+	if (!pci_set_dma_mask(pdev, DMA_BIT_MASK(64)) &&
+	    !pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64))) {
+		*using_dac = 1;
+	} else {
+		err = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
+		if (err) {
+			err = pci_set_consistent_dma_mask(pdev,
+						DMA_BIT_MASK(32));
+			if (err)
+				goto release_regions;
+		}
+		*using_dac = 0;
+	}
+	pci_set_master(pdev);
+	return 0;
+
+release_regions:
+	pci_release_regions(pdev);
+disable_device:
+	pci_disable_device(pdev);
+
+	return err;
+}
+
+static void
+bnad_pci_uninit(struct pci_dev *pdev)
+{
+	pci_release_regions(pdev);
+	pci_disable_device(pdev);
+}
+
+static int __devinit
+bnad_pci_probe(struct pci_dev *pdev,
+		const struct pci_device_id *pcidev_id)
+{
+	bool 	using_dac;
+	int 	err;
+	struct bnad *bnad;
+	struct bna *bna;
+	struct net_device *netdev;
+	struct bfa_pcidev pcidev_info;
+	unsigned long flags;
+
+	printk(KERN_INFO "bnad_pci_probe : (0x%p, 0x%p) PCI Func : (%d)\n",
+	       pdev, pcidev_id, PCI_FUNC(pdev->devfn));
+
+	mutex_lock(&bnad_fwimg_mutex);
+	if (!cna_get_firmware_buf(pdev)) {
+		mutex_unlock(&bnad_fwimg_mutex);
+		printk(KERN_WARNING "Failed to load Firmware Image!\n");
+		return -ENODEV;
+	}
+	mutex_unlock(&bnad_fwimg_mutex);
+
+	/*
+	 * Allocates sizeof(struct net_device + struct bnad)
+	 * bnad = netdev->priv
+	 */
+	netdev = alloc_etherdev(sizeof(struct bnad));
+	if (!netdev) {
+		dev_err(&pdev->dev, "alloc_etherdev failed\n");
+		err = -ENOMEM;
+		return err;
+	}
+	bnad = netdev_priv(netdev);
+
+	bnad->ident.id = bna_id;
+
+	/*
+	 * PCI initialization
+	 * 	Output : using_dac = 1 for 64 bit DMA
+	 *		           = 0 for 32 bit DMA
+	 */
+	err = bnad_pci_init(bnad, pdev, &using_dac);
+	if (err)
+		goto free_netdev;
+
+	bnad_lock_init(bnad);
+	/*
+	 * Initialize bnad structure
+	 * Setup relation between pci_dev & netdev
+	 * Init Tx free tasklet
+	 */
+	err = bnad_init(bnad, pdev, netdev);
+	if (err)
+		goto pci_uninit;
+	/* Initialize netdev structure, set up ethtool ops */
+	bnad_netdev_init(bnad, using_dac);
+
+	bnad_enable_msix(bnad);
+
+	/* Get resource requirement form bna */
+	bna_res_req(&bnad->res_info[0]);
+
+	/* Allocate resources from bna */
+	err = bnad_res_alloc(bnad);
+	if (err)
+		goto free_netdev;
+
+	bna = &bnad->bna;
+
+	/* Setup pcidev_info for bna_init() */
+	pcidev_info.pci_slot = PCI_SLOT(bnad->pcidev->devfn);
+	pcidev_info.pci_func = PCI_FUNC(bnad->pcidev->devfn);
+	pcidev_info.device_id = bnad->pcidev->device;
+	pcidev_info.pci_bar_kva = bnad->bar0;
+
+	mutex_lock(&bnad->conf_mutex);
+
+	spin_lock_irqsave(&bnad->bna_lock, flags);
+	bna_init(bna, bnad, &bnad->ident, &pcidev_info, &bnad->res_info[0]);
+
+	spin_unlock_irqrestore(&bnad->bna_lock, flags);
+
+	bnad->stats.bna_stats = &bna->stats;
+
+	/* Set up timers */
+	setup_timer(&bnad->bna.device.ioc.ioc_timer, bnad_ioc_timeout,
+				((unsigned long)bnad));
+	setup_timer(&bnad->bna.device.ioc.hb_timer, bnad_ioc_hb_check,
+				((unsigned long)bnad));
+	setup_timer(&bnad->bna.device.ioc.sem_timer, bnad_ioc_sem_timeout,
+				((unsigned long)bnad));
+
+	/* Now start the timer before calling IOC */
+	mod_timer(&bnad->bna.device.ioc.ioc_timer,
+		  jiffies + msecs_to_jiffies(BNA_IOC_TIMER_FREQ));
+
+	/*
+	 * Start the chip
+	 * Don't care even if err != 0, bna state machine will
+	 * deal with it
+	 */
+	err = bnad_device_enable(bnad);
+
+	/* Get the burnt-in mac */
+	spin_lock_irqsave(&bnad->bna_lock, flags);
+	bna_port_mac_get(&bna->port, &bnad->perm_addr);
+	bnad_set_netdev_perm_addr(bnad);
+	spin_unlock_irqrestore(&bnad->bna_lock, flags);
+
+	mutex_unlock(&bnad->conf_mutex);
+
+	/*
+	 * Make sure the link appears down to the stack
+	 */
+	netif_carrier_off(netdev);
+
+	/* Finally, reguister with net_device layer */
+	err = register_netdev(netdev);
+	if (err) {
+		printk(KERN_ERR "BNA[%d] : Registering with netdev failed\n",
+		       bnad->ident.id);
+		goto disable_device;
+	}
+
+	return 0;
+
+disable_device:
+	mutex_lock(&bnad->conf_mutex);
+	bnad_device_disable(bnad);
+	del_timer_sync(&bnad->bna.device.ioc.ioc_timer);
+	del_timer_sync(&bnad->bna.device.ioc.sem_timer);
+	del_timer_sync(&bnad->bna.device.ioc.hb_timer);
+	spin_lock_irqsave(&bnad->bna_lock, flags);
+	bna_uninit(bna);
+	spin_unlock_irqrestore(&bnad->bna_lock, flags);
+	mutex_unlock(&bnad->conf_mutex);
+
+	bnad_res_free(bnad);
+	bnad_disable_msix(bnad);
+pci_uninit:
+	bnad_pci_uninit(pdev);
+	bnad_lock_uninit(bnad);
+	bnad_uninit(bnad);
+free_netdev:
+	free_netdev(netdev);
+	return err;
+}
+
+static void __devexit
+bnad_pci_remove(struct pci_dev *pdev)
+{
+	struct net_device *netdev = pci_get_drvdata(pdev);
+	struct bnad *bnad;
+	struct bna *bna;
+	unsigned long flags;
+
+	if (!netdev)
+		return;
+
+	printk(KERN_INFO "%s bnad_pci_remove\n", netdev->name);
+	bnad = netdev_priv(netdev);
+	bna = &bnad->bna;
+
+	unregister_netdev(netdev);
+
+	mutex_lock(&bnad->conf_mutex);
+	bnad_device_disable(bnad);
+	del_timer_sync(&bnad->bna.device.ioc.ioc_timer);
+	del_timer_sync(&bnad->bna.device.ioc.sem_timer);
+	del_timer_sync(&bnad->bna.device.ioc.hb_timer);
+	spin_lock_irqsave(&bnad->bna_lock, flags);
+	bna_uninit(bna);
+	spin_unlock_irqrestore(&bnad->bna_lock, flags);
+	mutex_unlock(&bnad->conf_mutex);
+
+	bnad_res_free(bnad);
+	bnad_disable_msix(bnad);
+	bnad_pci_uninit(pdev);
+	bnad_lock_uninit(bnad);
+	bnad_uninit(bnad);
+	free_netdev(netdev);
+}
+
+const struct pci_device_id bnad_pci_id_table[] = {
+	{
+		PCI_DEVICE(PCI_VENDOR_ID_BROCADE,
+			PCI_DEVICE_ID_BROCADE_CT),
+		.class = PCI_CLASS_NETWORK_ETHERNET << 8,
+		.class_mask =  0xffff00
+	}, {0,  }
+};
+
+MODULE_DEVICE_TABLE(pci, bnad_pci_id_table);
+
+static struct pci_driver bnad_pci_driver = {
+	.name = BNAD_NAME,
+	.id_table = bnad_pci_id_table,
+	.probe = bnad_pci_probe,
+	.remove = __devexit_p(bnad_pci_remove),
+};
+
+static int __init
+bnad_module_init(void)
+{
+	int err;
+
+	printk(KERN_INFO "Brocade 10G Ethernet driver\n");
+
+	bfa_ioc_auto_recover(bnad_ioc_auto_recover);
+
+	err = pci_register_driver(&bnad_pci_driver);
+	if (err < 0) {
+		printk(KERN_ERR "bna : PCI registration failed in module init "
+		       "(%d)\n", err);
+		return err;
+	}
+
+	return 0;
+}
+
+static void __exit
+bnad_module_exit(void)
+{
+	pci_unregister_driver(&bnad_pci_driver);
+
+	if (bfi_fw)
+		release_firmware(bfi_fw);
+}
+
+module_init(bnad_module_init);
+module_exit(bnad_module_exit);
+
+MODULE_AUTHOR("Brocade");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Brocade 10G PCIe Ethernet driver");
+MODULE_VERSION(BNAD_VERSION);
+MODULE_FIRMWARE(CNA_FW_FILE_CT);
diff -ruP net-next-2.6.35-rc1-orig/drivers/net/bna/bnad.h net-next-2.6.35-rc1-mod/drivers/net/bna/bnad.h
--- net-next-2.6.35-rc1-orig/drivers/net/bna/bnad.h	1969-12-31 16:00:00.000000000 -0800
+++ net-next-2.6.35-rc1-mod/drivers/net/bna/bnad.h	2010-08-10 12:23:09.184216000 -0700
@@ -0,0 +1,339 @@
+/*
+ * Linux network driver for Brocade Converged Network Adapter.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License (GPL) Version 2 as
+ * published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+/*
+ * Copyright (c) 2005-2009 Brocade Communications Systems, Inc.
+ * All rights reserved
+ * www.brocade.com
+ */
+#ifndef __BNAD_H__
+#define __BNAD_H__
+
+#include <linux/rtnetlink.h>
+#include <linux/workqueue.h>
+#include <linux/ipv6.h>
+#include <linux/etherdevice.h>
+#include <linux/mutex.h>
+#include <linux/firmware.h>
+
+/* Fix for IA64 */
+#include <asm/checksum.h>
+#include <net/ip6_checksum.h>
+
+#include <net/ip.h>
+#include <net/tcp.h>
+
+#include "bna.h"
+
+#define BNAD_TXQ_DEPTH		2048
+#define BNAD_RXQ_DEPTH		2048
+
+#define BNAD_MAX_TXS		1
+#define BNAD_MAX_TXQ_PER_TX	8	/* 8 priority queues */
+#define BNAD_TXQ_NUM		1
+
+#define BNAD_MAX_RXS		1
+#define BNAD_MAX_RXPS_PER_RX	16
+
+/*
+ * Control structure pointed to ccb->ctrl, which
+ * determines the NAPI / LRO behavior CCB
+ * There is 1:1 corres. between ccb & ctrl
+ */
+struct bnad_rx_ctrl {
+	struct bna_ccb *ccb;
+	struct napi_struct	napi;
+};
+
+#define BNAD_RXMODE_PROMISC_DEFAULT	BNA_RXMODE_PROMISC
+
+#define BNAD_GET_TX_ID(_skb)	(0)
+
+/*
+ * GLOBAL #defines (CONSTANTS)
+ */
+#define BNAD_NAME			"bna"
+#define BNAD_NAME_LEN			64
+
+#define BNAD_VERSION			"2.3.2.0"
+
+#define BNAD_MAILBOX_MSIX_VECTORS	1
+
+#define BNAD_STATS_TIMER_FREQ		1000 	/* in msecs */
+#define BNAD_DIM_TIMER_FREQ		1000 	/* in msecs */
+
+#define BNAD_MAX_Q_DEPTH		0x10000
+#define BNAD_MIN_Q_DEPTH		0x200
+
+#define BNAD_JUMBO_MTU			9000
+
+#define BNAD_NETIF_WAKE_THRESHOLD	8
+
+#define BNAD_RXQ_REFILL_THRESHOLD_SHIFT	3
+
+/* Bit positions for tcb->flags */
+#define BNAD_TXQ_FREE_SENT		0
+
+/* Bit positions for rcb->flags */
+#define BNAD_RXQ_REFILL			0
+#define BNAD_RXQ_STARTED		1
+
+/*
+ * DATA STRUCTURES
+ */
+
+/* enums */
+enum bnad_intr_source {
+	BNAD_INTR_TX		= 1,
+	BNAD_INTR_RX		= 2
+};
+
+enum bnad_link_state {
+	BNAD_LS_DOWN		= 0,
+	BNAD_LS_UP 		= 1
+};
+
+struct bnad_completion {
+	struct completion 	ioc_comp;
+	struct completion 	ucast_comp;
+	struct completion	mcast_comp;
+	struct completion	tx_comp;
+	struct completion	rx_comp;
+	struct completion	stats_comp;
+	struct completion	port_comp;
+
+	u8			ioc_comp_status;
+	u8			ucast_comp_status;
+	u8			mcast_comp_status;
+	u8			tx_comp_status;
+	u8			rx_comp_status;
+	u8			stats_comp_status;
+	u8			port_comp_status;
+};
+
+/* Tx Rx Control Stats */
+struct bnad_drv_stats {
+	u64 		netif_queue_stop;
+	u64		netif_queue_wakeup;
+	u64		tso4;
+	u64		tso6;
+	u64		tso_err;
+	u64		tcpcsum_offload;
+	u64		udpcsum_offload;
+	u64		csum_help;
+	u64		csum_help_err;
+
+	u64		hw_stats_updates;
+	u64		netif_rx_schedule;
+	u64		netif_rx_complete;
+	u64		netif_rx_dropped;
+
+	u64		link_toggle;
+	u64		cee_up;
+
+	u64		rxp_info_alloc_failed;
+	u64		mbox_intr_disabled;
+	u64		mbox_intr_enabled;
+	u64		tx_unmap_q_alloc_failed;
+	u64		rx_unmap_q_alloc_failed;
+
+	u64		rxbuf_alloc_failed;
+};
+
+/* Complete driver stats */
+struct bnad_stats {
+	struct bnad_drv_stats drv_stats;
+	struct bna_stats *bna_stats;
+};
+
+/* Tx / Rx Resources */
+struct bnad_tx_res_info {
+	struct bna_res_info res_info[BNA_TX_RES_T_MAX];
+};
+
+struct bnad_rx_res_info {
+	struct bna_res_info res_info[BNA_RX_RES_T_MAX];
+};
+
+struct bnad_tx_info {
+	struct bna_tx *tx; /* 1:1 between tx_info & tx */
+	struct bna_tcb *tcb[BNAD_MAX_TXQ_PER_TX];
+} ____cacheline_aligned;
+
+struct bnad_rx_info {
+	struct bna_rx *rx; /* 1:1 between rx_info & rx */
+
+	struct bnad_rx_ctrl rx_ctrl[BNAD_MAX_RXPS_PER_RX];
+} ____cacheline_aligned;
+
+/* Unmap queues for Tx / Rx cleanup */
+struct bnad_skb_unmap {
+	struct sk_buff		*skb;
+	DECLARE_PCI_UNMAP_ADDR(dma_addr)
+};
+
+struct bnad_unmap_q {
+	u32		producer_index;
+	u32		consumer_index;
+	u32 		q_depth;
+	/* This should be the last one */
+	struct bnad_skb_unmap unmap_array[1];
+};
+
+/* Bit mask values for bnad->cfg_flags */
+#define	BNAD_CF_DIM_ENABLED		0x01	/* DIM */
+#define	BNAD_CF_PROMISC			0x02
+#define BNAD_CF_ALLMULTI		0x04
+#define	BNAD_CF_MSIX			0x08	/* If in MSIx mode */
+
+/* Defines for run_flags bit-mask */
+/* Set, tested & cleared using xxx_bit() functions */
+/* Values indicated bit positions */
+#define	BNAD_RF_CEE_RUNNING		1
+#define BNAD_RF_HW_ERROR 		2
+#define BNAD_RF_MBOX_IRQ_DISABLED	3
+#define BNAD_RF_TX_STARTED		4
+#define BNAD_RF_RX_STARTED		5
+#define BNAD_RF_DIM_TIMER_RUNNING	6
+#define BNAD_RF_STATS_TIMER_RUNNING	7
+
+struct bnad {
+	struct net_device 	*netdev;
+
+	/* Data path */
+	struct bnad_tx_info tx_info[BNAD_MAX_TXS];
+	struct bnad_rx_info rx_info[BNAD_MAX_RXS];
+
+	struct vlan_group	*vlan_grp;
+	/*
+	 * These q numbers are global only because
+	 * they are used to calculate MSIx vectors.
+	 * Actually the exact # of queues are per Tx/Rx
+	 * object.
+	 */
+	u32		num_tx;
+	u32		num_rx;
+	u32		num_txq_per_tx;
+	u32		num_rxp_per_rx;
+
+	u32		txq_depth;
+	u32		rxq_depth;
+
+	u8			tx_coalescing_timeo;
+	u8			rx_coalescing_timeo;
+
+	struct bna_rx_config rx_config[BNAD_MAX_RXS];
+	struct bna_tx_config tx_config[BNAD_MAX_TXS];
+
+	u32		rx_csum;
+
+	void __iomem		*bar0;	/* BAR0 address */
+
+	struct bna bna;
+
+	u32		cfg_flags;
+	unsigned long		run_flags;
+
+	struct pci_dev 		*pcidev;
+	u64		mmio_start;
+	u64		mmio_len;
+
+	u32		msix_num;
+	u32		msix_diag_num;
+	struct msix_entry	*msix_table;
+
+	struct mutex		conf_mutex;
+	spinlock_t		bna_lock ____cacheline_aligned;
+
+	/* Timers */
+	struct timer_list	ioc_timer;
+	struct timer_list	dim_timer;
+	struct timer_list	stats_timer;
+
+	/* Control path resources, memory & irq */
+	struct bna_res_info res_info[BNA_RES_T_MAX];
+	struct bnad_tx_res_info tx_res_info[BNAD_MAX_TXS];
+	struct bnad_rx_res_info rx_res_info[BNAD_MAX_RXS];
+
+	struct bnad_completion bnad_completions;
+
+	/* Burnt in MAC address */
+	mac_t			perm_addr;
+
+	struct tasklet_struct	tx_free_tasklet;
+
+	/* Statistics */
+	struct bnad_stats stats;
+	struct net_device_stats net_stats;
+
+	struct bnad_diag *diag;
+
+	char			adapter_name[BNAD_NAME_LEN];
+	char 			port_name[BNAD_NAME_LEN];
+	char			mbox_irq_name[BNAD_NAME_LEN];
+
+	struct bna_ident ident;
+};
+
+/*
+ * EXTERN VARIABLES
+ */
+extern struct firmware *bfi_fw;
+extern struct semaphore 	bnad_list_sem;
+extern struct list_head 	bnad_list;
+extern u32 		bna_id; /* Driver Instance Id */
+extern u32 		bnad_rxqs_per_cq;
+
+/*
+ * EXTERN PROTOTYPES
+ */
+extern u32 *cna_get_firmware_buf(struct pci_dev *pdev);
+/* Netdev entry point prototypes */
+extern void bnad_set_ethtool_ops(struct net_device *netdev);
+
+/* Configuration & setup */
+extern void bnad_tx_coalescing_timeo_set(struct bnad *bnad);
+extern void bnad_rx_coalescing_timeo_set(struct bnad *bnad);
+
+extern int bnad_setup_rx(struct bnad *bnad, uint rx_id);
+extern int bnad_setup_tx(struct bnad *bnad, uint tx_id);
+extern void bnad_cleanup_tx(struct bnad *bnad, uint tx_id);
+extern void bnad_cleanup_rx(struct bnad *bnad, uint rx_id);
+
+/* Timer start/stop protos */
+extern void bnad_dim_timer_start(struct bnad *bnad);
+
+/* Statistics */
+extern void bnad_netdev_qstats_fill(struct bnad *bnad);
+extern void bnad_netdev_hwstats_fill(struct bnad *bnad);
+
+/**
+ * MACROS
+ */
+/* To set & get the stats counters */
+#define BNAD_UPDATE_CTR(_bnad, _ctr)				\
+				(((_bnad)->stats.drv_stats._ctr)++)
+
+#define BNAD_GET_CTR(_bnad, _ctr) ((_bnad)->stats.drv_stats._ctr)
+
+#define bnad_enable_rx_irq_unsafe(_ccb)			\
+{							\
+	bna_ib_coalescing_timer_set((_ccb)->i_dbell,	\
+		(_ccb)->rx_coalescing_timeo);		\
+	bna_ib_ack((_ccb)->i_dbell, 0);			\
+}
+
+#define bnad_dim_timer_running(_bnad)				\
+	(((_bnad)->cfg_flags & BNAD_CF_DIM_ENABLED) && 		\
+	(test_bit(BNAD_RF_DIM_TIMER_RUNNING, &((_bnad)->run_flags))))
+
+#endif /* __BNAD_H__ */
diff -ruP net-next-2.6.35-rc1-orig/drivers/net/bna/bnad_ethtool.c net-next-2.6.35-rc1-mod/drivers/net/bna/bnad_ethtool.c
--- net-next-2.6.35-rc1-orig/drivers/net/bna/bnad_ethtool.c	1969-12-31 16:00:00.000000000 -0800
+++ net-next-2.6.35-rc1-mod/drivers/net/bna/bnad_ethtool.c	2010-08-10 12:23:09.175218000 -0700
@@ -0,0 +1,1282 @@
+/*
+ * Linux network driver for Brocade Converged Network Adapter.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License (GPL) Version 2 as
+ * published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+/*
+ * Copyright (c) 2005-2009 Brocade Communications Systems, Inc.
+ * All rights reserved
+ * www.brocade.com
+ */
+
+#include "cna.h"
+
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <linux/ethtool.h>
+#include <linux/rtnetlink.h>
+
+#include "bna.h"
+
+#include "bnad.h"
+
+#define BNAD_NUM_TXF_COUNTERS 12
+#define BNAD_NUM_RXF_COUNTERS 10
+#define BNAD_NUM_CQ_COUNTERS 3
+#define BNAD_NUM_RXQ_COUNTERS 6
+#define BNAD_NUM_TXQ_COUNTERS 5
+
+#define BNAD_ETHTOOL_STATS_NUM						\
+	(sizeof(struct net_device_stats) / sizeof(unsigned long) +	\
+	sizeof(struct bnad_drv_stats) / sizeof(u64) +		\
+	offsetof(struct bfi_ll_stats, rxf_stats[0]) / sizeof(u64))
+
+static char *bnad_net_stats_strings[BNAD_ETHTOOL_STATS_NUM] = {
+	"rx_packets",
+	"tx_packets",
+	"rx_bytes",
+	"tx_bytes",
+	"rx_errors",
+	"tx_errors",
+	"rx_dropped",
+	"tx_dropped",
+	"multicast",
+	"collisions",
+
+	"rx_length_errors",
+	"rx_over_errors",
+	"rx_crc_errors",
+	"rx_frame_errors",
+	"rx_fifo_errors",
+	"rx_missed_errors",
+
+	"tx_aborted_errors",
+	"tx_carrier_errors",
+	"tx_fifo_errors",
+	"tx_heartbeat_errors",
+	"tx_window_errors",
+
+	"rx_compressed",
+	"tx_compressed",
+
+	"netif_queue_stop",
+	"netif_queue_wakeup",
+	"tso4",
+	"tso6",
+	"tso_err",
+	"tcpcsum_offload",
+	"udpcsum_offload",
+	"csum_help",
+	"csum_help_err",
+	"hw_stats_updates",
+	"netif_rx_schedule",
+	"netif_rx_complete",
+	"netif_rx_dropped",
+
+	"link_toggle",
+	"cee_up",
+
+	"rxp_info_alloc_failed",
+	"mbox_intr_disabled",
+	"mbox_intr_enabled",
+	"tx_unmap_q_alloc_failed",
+	"rx_unmap_q_alloc_failed",
+	"rxbuf_alloc_failed",
+
+	"mac_frame_64",
+	"mac_frame_65_127",
+	"mac_frame_128_255",
+	"mac_frame_256_511",
+	"mac_frame_512_1023",
+	"mac_frame_1024_1518",
+	"mac_frame_1518_1522",
+	"mac_rx_bytes",
+	"mac_rx_packets",
+	"mac_rx_fcs_error",
+	"mac_rx_multicast",
+	"mac_rx_broadcast",
+	"mac_rx_control_frames",
+	"mac_rx_pause",
+	"mac_rx_unknown_opcode",
+	"mac_rx_alignment_error",
+	"mac_rx_frame_length_error",
+	"mac_rx_code_error",
+	"mac_rx_carrier_sense_error",
+	"mac_rx_undersize",
+	"mac_rx_oversize",
+	"mac_rx_fragments",
+	"mac_rx_jabber",
+	"mac_rx_drop",
+
+	"mac_tx_bytes",
+	"mac_tx_packets",
+	"mac_tx_multicast",
+	"mac_tx_broadcast",
+	"mac_tx_pause",
+	"mac_tx_deferral",
+	"mac_tx_excessive_deferral",
+	"mac_tx_single_collision",
+	"mac_tx_muliple_collision",
+	"mac_tx_late_collision",
+	"mac_tx_excessive_collision",
+	"mac_tx_total_collision",
+	"mac_tx_pause_honored",
+	"mac_tx_drop",
+	"mac_tx_jabber",
+	"mac_tx_fcs_error",
+	"mac_tx_control_frame",
+	"mac_tx_oversize",
+	"mac_tx_undersize",
+	"mac_tx_fragments",
+
+	"bpc_tx_pause_0",
+	"bpc_tx_pause_1",
+	"bpc_tx_pause_2",
+	"bpc_tx_pause_3",
+	"bpc_tx_pause_4",
+	"bpc_tx_pause_5",
+	"bpc_tx_pause_6",
+	"bpc_tx_pause_7",
+	"bpc_tx_zero_pause_0",
+	"bpc_tx_zero_pause_1",
+	"bpc_tx_zero_pause_2",
+	"bpc_tx_zero_pause_3",
+	"bpc_tx_zero_pause_4",
+	"bpc_tx_zero_pause_5",
+	"bpc_tx_zero_pause_6",
+	"bpc_tx_zero_pause_7",
+	"bpc_tx_first_pause_0",
+	"bpc_tx_first_pause_1",
+	"bpc_tx_first_pause_2",
+	"bpc_tx_first_pause_3",
+	"bpc_tx_first_pause_4",
+	"bpc_tx_first_pause_5",
+	"bpc_tx_first_pause_6",
+	"bpc_tx_first_pause_7",
+
+	"bpc_rx_pause_0",
+	"bpc_rx_pause_1",
+	"bpc_rx_pause_2",
+	"bpc_rx_pause_3",
+	"bpc_rx_pause_4",
+	"bpc_rx_pause_5",
+	"bpc_rx_pause_6",
+	"bpc_rx_pause_7",
+	"bpc_rx_zero_pause_0",
+	"bpc_rx_zero_pause_1",
+	"bpc_rx_zero_pause_2",
+	"bpc_rx_zero_pause_3",
+	"bpc_rx_zero_pause_4",
+	"bpc_rx_zero_pause_5",
+	"bpc_rx_zero_pause_6",
+	"bpc_rx_zero_pause_7",
+	"bpc_rx_first_pause_0",
+	"bpc_rx_first_pause_1",
+	"bpc_rx_first_pause_2",
+	"bpc_rx_first_pause_3",
+	"bpc_rx_first_pause_4",
+	"bpc_rx_first_pause_5",
+	"bpc_rx_first_pause_6",
+	"bpc_rx_first_pause_7",
+
+	"rad_rx_frames",
+	"rad_rx_octets",
+	"rad_rx_vlan_frames",
+	"rad_rx_ucast",
+	"rad_rx_ucast_octets",
+	"rad_rx_ucast_vlan",
+	"rad_rx_mcast",
+	"rad_rx_mcast_octets",
+	"rad_rx_mcast_vlan",
+	"rad_rx_bcast",
+	"rad_rx_bcast_octets",
+	"rad_rx_bcast_vlan",
+	"rad_rx_drops",
+
+	"fc_rx_ucast_octets",
+	"fc_rx_ucast",
+	"fc_rx_ucast_vlan",
+	"fc_rx_mcast_octets",
+	"fc_rx_mcast",
+	"fc_rx_mcast_vlan",
+	"fc_rx_bcast_octets",
+	"fc_rx_bcast",
+	"fc_rx_bcast_vlan",
+
+	"fc_tx_ucast_octets",
+	"fc_tx_ucast",
+	"fc_tx_ucast_vlan",
+	"fc_tx_mcast_octets",
+	"fc_tx_mcast",
+	"fc_tx_mcast_vlan",
+	"fc_tx_bcast_octets",
+	"fc_tx_bcast",
+	"fc_tx_bcast_vlan",
+	"fc_tx_parity_errors",
+	"fc_tx_timeout",
+	"fc_tx_fid_parity_errors",
+};
+
+static int
+bnad_get_settings(struct net_device *netdev, struct ethtool_cmd *cmd)
+{
+	cmd->supported = SUPPORTED_10000baseT_Full;
+	cmd->advertising = ADVERTISED_10000baseT_Full;
+	cmd->autoneg = AUTONEG_DISABLE;
+	cmd->supported |= SUPPORTED_FIBRE;
+	cmd->advertising |= ADVERTISED_FIBRE;
+	cmd->port = PORT_FIBRE;
+	cmd->phy_address = 0;
+
+	if (netif_carrier_ok(netdev)) {
+		cmd->speed = SPEED_10000;
+		cmd->duplex = DUPLEX_FULL;
+	} else {
+		cmd->speed = -1;
+		cmd->duplex = -1;
+	}
+	cmd->transceiver = XCVR_EXTERNAL;
+	cmd->maxtxpkt = 0;
+	cmd->maxrxpkt = 0;
+
+	return 0;
+}
+
+static int
+bnad_set_settings(struct net_device *netdev, struct ethtool_cmd *cmd)
+{
+	/* 10G full duplex setting supported only */
+	if (cmd->autoneg == AUTONEG_ENABLE)
+		return -EOPNOTSUPP; else {
+		if ((cmd->speed == SPEED_10000) && (cmd->duplex == DUPLEX_FULL))
+			return 0;
+	}
+
+	return -EOPNOTSUPP;
+}
+
+static void
+bnad_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *drvinfo)
+{
+	struct bnad *bnad = netdev_priv(netdev);
+	struct bfa_ioc_attr *ioc_attr;
+	unsigned long flags;
+
+	strcpy(drvinfo->driver, BNAD_NAME);
+	strcpy(drvinfo->version, BNAD_VERSION);
+
+	ioc_attr = kzalloc(sizeof(*ioc_attr), GFP_KERNEL);
+	if (ioc_attr) {
+		memset(ioc_attr, 0, sizeof(*ioc_attr));
+		spin_lock_irqsave(&bnad->bna_lock, flags);
+		bfa_ioc_get_attr(&bnad->bna.device.ioc, ioc_attr);
+		spin_unlock_irqrestore(&bnad->bna_lock, flags);
+
+		strncpy(drvinfo->fw_version, ioc_attr->adapter_attr.fw_ver,
+			sizeof(drvinfo->fw_version) - 1);
+		kfree(ioc_attr);
+	}
+
+	strncpy(drvinfo->bus_info, pci_name(bnad->pcidev), ETHTOOL_BUSINFO_LEN);
+}
+
+static int
+get_regs(struct bnad *bnad, u32 * regs)
+{
+	int num = 0, i;
+	u32 reg_addr;
+	unsigned long flags;
+
+#define BNAD_GET_REG(addr) 					\
+do {								\
+	if (regs)						\
+		regs[num++] = readl(bnad->bar0 + (addr));	\
+	else							\
+		num++;						\
+} while (0)
+
+	spin_lock_irqsave(&bnad->bna_lock, flags);
+
+	/* DMA Block Internal Registers */
+	BNAD_GET_REG(DMA_CTRL_REG0);
+	BNAD_GET_REG(DMA_CTRL_REG1);
+	BNAD_GET_REG(DMA_ERR_INT_STATUS);
+	BNAD_GET_REG(DMA_ERR_INT_ENABLE);
+	BNAD_GET_REG(DMA_ERR_INT_STATUS_SET);
+
+	/* APP Block Register Address Offset from BAR0 */
+	BNAD_GET_REG(HOSTFN0_INT_STATUS);
+	BNAD_GET_REG(HOSTFN0_INT_MASK);
+	BNAD_GET_REG(HOST_PAGE_NUM_FN0);
+	BNAD_GET_REG(HOST_MSIX_ERR_INDEX_FN0);
+	BNAD_GET_REG(FN0_PCIE_ERR_REG);
+	BNAD_GET_REG(FN0_ERR_TYPE_STATUS_REG);
+	BNAD_GET_REG(FN0_ERR_TYPE_MSK_STATUS_REG);
+
+	BNAD_GET_REG(HOSTFN1_INT_STATUS);
+	BNAD_GET_REG(HOSTFN1_INT_MASK);
+	BNAD_GET_REG(HOST_PAGE_NUM_FN1);
+	BNAD_GET_REG(HOST_MSIX_ERR_INDEX_FN1);
+	BNAD_GET_REG(FN1_PCIE_ERR_REG);
+	BNAD_GET_REG(FN1_ERR_TYPE_STATUS_REG);
+	BNAD_GET_REG(FN1_ERR_TYPE_MSK_STATUS_REG);
+
+	BNAD_GET_REG(PCIE_MISC_REG);
+
+	BNAD_GET_REG(HOST_SEM0_REG);
+	BNAD_GET_REG(HOST_SEM1_REG);
+	BNAD_GET_REG(HOST_SEM2_REG);
+	BNAD_GET_REG(HOST_SEM3_REG);
+	BNAD_GET_REG(HOST_SEM0_INFO_REG);
+	BNAD_GET_REG(HOST_SEM1_INFO_REG);
+	BNAD_GET_REG(HOST_SEM2_INFO_REG);
+	BNAD_GET_REG(HOST_SEM3_INFO_REG);
+
+	BNAD_GET_REG(TEMPSENSE_CNTL_REG);
+	BNAD_GET_REG(TEMPSENSE_STAT_REG);
+
+	BNAD_GET_REG(APP_LOCAL_ERR_STAT);
+	BNAD_GET_REG(APP_LOCAL_ERR_MSK);
+
+	BNAD_GET_REG(PCIE_LNK_ERR_STAT);
+	BNAD_GET_REG(PCIE_LNK_ERR_MSK);
+
+	BNAD_GET_REG(FCOE_FIP_ETH_TYPE);
+	BNAD_GET_REG(RESV_ETH_TYPE);
+
+	BNAD_GET_REG(HOSTFN2_INT_STATUS);
+	BNAD_GET_REG(HOSTFN2_INT_MASK);
+	BNAD_GET_REG(HOST_PAGE_NUM_FN2);
+	BNAD_GET_REG(HOST_MSIX_ERR_INDEX_FN2);
+	BNAD_GET_REG(FN2_PCIE_ERR_REG);
+	BNAD_GET_REG(FN2_ERR_TYPE_STATUS_REG);
+	BNAD_GET_REG(FN2_ERR_TYPE_MSK_STATUS_REG);
+
+	BNAD_GET_REG(HOSTFN3_INT_STATUS);
+	BNAD_GET_REG(HOSTFN3_INT_MASK);
+	BNAD_GET_REG(HOST_PAGE_NUM_FN3);
+	BNAD_GET_REG(HOST_MSIX_ERR_INDEX_FN3);
+	BNAD_GET_REG(FN3_PCIE_ERR_REG);
+	BNAD_GET_REG(FN3_ERR_TYPE_STATUS_REG);
+	BNAD_GET_REG(FN3_ERR_TYPE_MSK_STATUS_REG);
+
+	/* Host Command Status Registers */
+	reg_addr = HOST_CMDSTS0_CLR_REG;
+	for (i = 0; i < 16; i++) {
+		BNAD_GET_REG(reg_addr);
+		BNAD_GET_REG(reg_addr + 4);
+		BNAD_GET_REG(reg_addr + 8);
+		reg_addr += 0x10;
+	}
+
+	/* Function ID register */
+	BNAD_GET_REG(FNC_ID_REG);
+
+	/* Function personality register */
+	BNAD_GET_REG(FNC_PERS_REG);
+
+	/* Operation mode register */
+	BNAD_GET_REG(OP_MODE);
+
+	/* LPU0 Registers */
+	BNAD_GET_REG(LPU0_MBOX_CTL_REG);
+	BNAD_GET_REG(LPU0_MBOX_CMD_REG);
+	BNAD_GET_REG(LPU0_MBOX_LINK_0REG);
+	BNAD_GET_REG(LPU1_MBOX_LINK_0REG);
+	BNAD_GET_REG(LPU0_MBOX_STATUS_0REG);
+	BNAD_GET_REG(LPU1_MBOX_STATUS_0REG);
+	BNAD_GET_REG(LPU0_ERR_STATUS_REG);
+	BNAD_GET_REG(LPU0_ERR_SET_REG);
+
+	/* LPU1 Registers */
+	BNAD_GET_REG(LPU1_MBOX_CTL_REG);
+	BNAD_GET_REG(LPU1_MBOX_CMD_REG);
+	BNAD_GET_REG(LPU0_MBOX_LINK_1REG);
+	BNAD_GET_REG(LPU1_MBOX_LINK_1REG);
+	BNAD_GET_REG(LPU0_MBOX_STATUS_1REG);
+	BNAD_GET_REG(LPU1_MBOX_STATUS_1REG);
+	BNAD_GET_REG(LPU1_ERR_STATUS_REG);
+	BNAD_GET_REG(LPU1_ERR_SET_REG);
+
+	/* PSS Registers */
+	BNAD_GET_REG(PSS_CTL_REG);
+	BNAD_GET_REG(PSS_ERR_STATUS_REG);
+	BNAD_GET_REG(ERR_STATUS_SET);
+	BNAD_GET_REG(PSS_RAM_ERR_STATUS_REG);
+
+	/* Catapult CPQ Registers */
+	BNAD_GET_REG(HOSTFN0_LPU0_MBOX0_CMD_STAT);
+	BNAD_GET_REG(HOSTFN0_LPU1_MBOX0_CMD_STAT);
+	BNAD_GET_REG(LPU0_HOSTFN0_MBOX0_CMD_STAT);
+	BNAD_GET_REG(LPU1_HOSTFN0_MBOX0_CMD_STAT);
+
+	BNAD_GET_REG(HOSTFN0_LPU0_MBOX1_CMD_STAT);
+	BNAD_GET_REG(HOSTFN0_LPU1_MBOX1_CMD_STAT);
+	BNAD_GET_REG(LPU0_HOSTFN0_MBOX1_CMD_STAT);
+	BNAD_GET_REG(LPU1_HOSTFN0_MBOX1_CMD_STAT);
+
+	BNAD_GET_REG(HOSTFN1_LPU0_MBOX0_CMD_STAT);
+	BNAD_GET_REG(HOSTFN1_LPU1_MBOX0_CMD_STAT);
+	BNAD_GET_REG(LPU0_HOSTFN1_MBOX0_CMD_STAT);
+	BNAD_GET_REG(LPU1_HOSTFN1_MBOX0_CMD_STAT);
+
+	BNAD_GET_REG(HOSTFN1_LPU0_MBOX1_CMD_STAT);
+	BNAD_GET_REG(HOSTFN1_LPU1_MBOX1_CMD_STAT);
+	BNAD_GET_REG(LPU0_HOSTFN1_MBOX1_CMD_STAT);
+	BNAD_GET_REG(LPU1_HOSTFN1_MBOX1_CMD_STAT);
+
+	BNAD_GET_REG(HOSTFN2_LPU0_MBOX0_CMD_STAT);
+	BNAD_GET_REG(HOSTFN2_LPU1_MBOX0_CMD_STAT);
+	BNAD_GET_REG(LPU0_HOSTFN2_MBOX0_CMD_STAT);
+	BNAD_GET_REG(LPU1_HOSTFN2_MBOX0_CMD_STAT);
+
+	BNAD_GET_REG(HOSTFN2_LPU0_MBOX1_CMD_STAT);
+	BNAD_GET_REG(HOSTFN2_LPU1_MBOX1_CMD_STAT);
+	BNAD_GET_REG(LPU0_HOSTFN2_MBOX1_CMD_STAT);
+	BNAD_GET_REG(LPU1_HOSTFN2_MBOX1_CMD_STAT);
+
+	BNAD_GET_REG(HOSTFN3_LPU0_MBOX0_CMD_STAT);
+	BNAD_GET_REG(HOSTFN3_LPU1_MBOX0_CMD_STAT);
+	BNAD_GET_REG(LPU0_HOSTFN3_MBOX0_CMD_STAT);
+	BNAD_GET_REG(LPU1_HOSTFN3_MBOX0_CMD_STAT);
+
+	BNAD_GET_REG(HOSTFN3_LPU0_MBOX1_CMD_STAT);
+	BNAD_GET_REG(HOSTFN3_LPU1_MBOX1_CMD_STAT);
+	BNAD_GET_REG(LPU0_HOSTFN3_MBOX1_CMD_STAT);
+	BNAD_GET_REG(LPU1_HOSTFN3_MBOX1_CMD_STAT);
+
+	/* Host Function Force Parity Error Registers */
+	BNAD_GET_REG(HOSTFN0_LPU_FORCE_PERR);
+	BNAD_GET_REG(HOSTFN1_LPU_FORCE_PERR);
+	BNAD_GET_REG(HOSTFN2_LPU_FORCE_PERR);
+	BNAD_GET_REG(HOSTFN3_LPU_FORCE_PERR);
+
+	/* LL Port[0|1] Halt Mask Registers */
+	BNAD_GET_REG(LL_HALT_MSK_P0);
+	BNAD_GET_REG(LL_HALT_MSK_P1);
+
+	/* LL Port[0|1] Error Mask Registers */
+	BNAD_GET_REG(LL_ERR_MSK_P0);
+	BNAD_GET_REG(LL_ERR_MSK_P1);
+
+	/* EMC FLI Registers */
+	BNAD_GET_REG(FLI_CMD_REG);
+	BNAD_GET_REG(FLI_ADDR_REG);
+	BNAD_GET_REG(FLI_CTL_REG);
+	BNAD_GET_REG(FLI_WRDATA_REG);
+	BNAD_GET_REG(FLI_RDDATA_REG);
+	BNAD_GET_REG(FLI_DEV_STATUS_REG);
+	BNAD_GET_REG(FLI_SIG_WD_REG);
+
+	BNAD_GET_REG(FLI_DEV_VENDOR_REG);
+	BNAD_GET_REG(FLI_ERR_STATUS_REG);
+
+	/* RxAdm 0 Registers */
+	BNAD_GET_REG(RAD0_CTL_REG);
+	BNAD_GET_REG(RAD0_PE_PARM_REG);
+	BNAD_GET_REG(RAD0_BCN_REG);
+	BNAD_GET_REG(RAD0_DEFAULT_REG);
+	BNAD_GET_REG(RAD0_PROMISC_REG);
+	BNAD_GET_REG(RAD0_BCNQ_REG);
+	BNAD_GET_REG(RAD0_DEFAULTQ_REG);
+
+	BNAD_GET_REG(RAD0_ERR_STS);
+	BNAD_GET_REG(RAD0_SET_ERR_STS);
+	BNAD_GET_REG(RAD0_ERR_INT_EN);
+	BNAD_GET_REG(RAD0_FIRST_ERR);
+	BNAD_GET_REG(RAD0_FORCE_ERR);
+
+	BNAD_GET_REG(RAD0_MAC_MAN_1H);
+	BNAD_GET_REG(RAD0_MAC_MAN_1L);
+	BNAD_GET_REG(RAD0_MAC_MAN_2H);
+	BNAD_GET_REG(RAD0_MAC_MAN_2L);
+	BNAD_GET_REG(RAD0_MAC_MAN_3H);
+	BNAD_GET_REG(RAD0_MAC_MAN_3L);
+	BNAD_GET_REG(RAD0_MAC_MAN_4H);
+	BNAD_GET_REG(RAD0_MAC_MAN_4L);
+
+	BNAD_GET_REG(RAD0_LAST4_IP);
+
+	/* RxAdm 1 Registers */
+	BNAD_GET_REG(RAD1_CTL_REG);
+	BNAD_GET_REG(RAD1_PE_PARM_REG);
+	BNAD_GET_REG(RAD1_BCN_REG);
+	BNAD_GET_REG(RAD1_DEFAULT_REG);
+	BNAD_GET_REG(RAD1_PROMISC_REG);
+	BNAD_GET_REG(RAD1_BCNQ_REG);
+	BNAD_GET_REG(RAD1_DEFAULTQ_REG);
+
+	BNAD_GET_REG(RAD1_ERR_STS);
+	BNAD_GET_REG(RAD1_SET_ERR_STS);
+	BNAD_GET_REG(RAD1_ERR_INT_EN);
+
+	/* TxA0 Registers */
+	BNAD_GET_REG(TXA0_CTRL_REG);
+	/* TxA0 TSO Sequence # Registers (RO) */
+	for (i = 0; i < 8; i++) {
+		BNAD_GET_REG(TXA0_TSO_TCP_SEQ_REG(i));
+		BNAD_GET_REG(TXA0_TSO_IP_INFO_REG(i));
+	}
+
+	/* TxA1 Registers */
+	BNAD_GET_REG(TXA1_CTRL_REG);
+	/* TxA1 TSO Sequence # Registers (RO) */
+	for (i = 0; i < 8; i++) {
+		BNAD_GET_REG(TXA1_TSO_TCP_SEQ_REG(i));
+		BNAD_GET_REG(TXA1_TSO_IP_INFO_REG(i));
+	}
+
+	/* RxA Registers */
+	BNAD_GET_REG(RXA0_CTL_REG);
+	BNAD_GET_REG(RXA1_CTL_REG);
+
+	/* PLB0 Registers */
+	BNAD_GET_REG(PLB0_ECM_TIMER_REG);
+	BNAD_GET_REG(PLB0_RL_CTL);
+	for (i = 0; i < 8; i++)
+		BNAD_GET_REG(PLB0_RL_MAX_BC(i));
+	BNAD_GET_REG(PLB0_RL_TU_PRIO);
+	for (i = 0; i < 8; i++)
+		BNAD_GET_REG(PLB0_RL_BYTE_CNT(i));
+	BNAD_GET_REG(PLB0_RL_MIN_REG);
+	BNAD_GET_REG(PLB0_RL_MAX_REG);
+	BNAD_GET_REG(PLB0_EMS_ADD_REG);
+
+	/* PLB1 Registers */
+	BNAD_GET_REG(PLB1_ECM_TIMER_REG);
+	BNAD_GET_REG(PLB1_RL_CTL);
+	for (i = 0; i < 8; i++)
+		BNAD_GET_REG(PLB1_RL_MAX_BC(i));
+	BNAD_GET_REG(PLB1_RL_TU_PRIO);
+	for (i = 0; i < 8; i++)
+		BNAD_GET_REG(PLB1_RL_BYTE_CNT(i));
+	BNAD_GET_REG(PLB1_RL_MIN_REG);
+	BNAD_GET_REG(PLB1_RL_MAX_REG);
+	BNAD_GET_REG(PLB1_EMS_ADD_REG);
+
+	/* HQM Control Register */
+	BNAD_GET_REG(HQM0_CTL_REG);
+	BNAD_GET_REG(HQM0_RXQ_STOP_SEM);
+	BNAD_GET_REG(HQM0_TXQ_STOP_SEM);
+	BNAD_GET_REG(HQM1_CTL_REG);
+	BNAD_GET_REG(HQM1_RXQ_STOP_SEM);
+	BNAD_GET_REG(HQM1_TXQ_STOP_SEM);
+
+	/* LUT Registers */
+	BNAD_GET_REG(LUT0_ERR_STS);
+	BNAD_GET_REG(LUT0_SET_ERR_STS);
+	BNAD_GET_REG(LUT1_ERR_STS);
+	BNAD_GET_REG(LUT1_SET_ERR_STS);
+
+	/* TRC Registers */
+	BNAD_GET_REG(TRC_CTL_REG);
+	BNAD_GET_REG(TRC_MODS_REG);
+	BNAD_GET_REG(TRC_TRGC_REG);
+	BNAD_GET_REG(TRC_CNT1_REG);
+	BNAD_GET_REG(TRC_CNT2_REG);
+	BNAD_GET_REG(TRC_NXTS_REG);
+	BNAD_GET_REG(TRC_DIRR_REG);
+	for (i = 0; i < 10; i++)
+		BNAD_GET_REG(TRC_TRGM_REG(i));
+	for (i = 0; i < 10; i++)
+		BNAD_GET_REG(TRC_NXTM_REG(i));
+	for (i = 0; i < 10; i++)
+		BNAD_GET_REG(TRC_STRM_REG(i));
+
+	spin_unlock_irqrestore(&bnad->bna_lock, flags);
+#undef BNAD_GET_REG
+	return num;
+}
+static int
+bnad_get_regs_len(struct net_device *netdev)
+{
+	int ret = get_regs(netdev_priv(netdev), NULL) * sizeof(u32);
+	return ret;
+}
+
+static void
+bnad_get_regs(struct net_device *netdev, struct ethtool_regs *regs, void *buf)
+{
+	memset(buf, 0, bnad_get_regs_len(netdev));
+	get_regs(netdev_priv(netdev), buf);
+}
+
+static void
+bnad_get_wol(struct net_device *netdev, struct ethtool_wolinfo *wolinfo)
+{
+	wolinfo->supported = 0;
+	wolinfo->wolopts = 0;
+}
+
+static int
+bnad_get_coalesce(struct net_device *netdev, struct ethtool_coalesce *coalesce)
+{
+	struct bnad *bnad = netdev_priv(netdev);
+	unsigned long flags;
+
+	/* Lock rqd. to access bnad->bna_lock */
+	spin_lock_irqsave(&bnad->bna_lock, flags);
+	coalesce->use_adaptive_rx_coalesce =
+		(bnad->cfg_flags & BNAD_CF_DIM_ENABLED) ? true : false;
+	spin_unlock_irqrestore(&bnad->bna_lock, flags);
+
+	coalesce->rx_coalesce_usecs = bnad->rx_coalescing_timeo *
+					BFI_COALESCING_TIMER_UNIT;
+	coalesce->tx_coalesce_usecs = bnad->tx_coalescing_timeo *
+					BFI_COALESCING_TIMER_UNIT;
+	coalesce->tx_max_coalesced_frames = BFI_TX_INTERPKT_COUNT;
+
+	return 0;
+}
+
+static int
+bnad_set_coalesce(struct net_device *netdev, struct ethtool_coalesce *coalesce)
+{
+	struct bnad *bnad = netdev_priv(netdev);
+	unsigned long flags;
+	int dim_timer_del = 0;
+
+	if (coalesce->rx_coalesce_usecs == 0 ||
+	    coalesce->rx_coalesce_usecs >
+	    BFI_MAX_COALESCING_TIMEO * BFI_COALESCING_TIMER_UNIT)
+		return -EINVAL;
+
+	if (coalesce->tx_coalesce_usecs == 0 ||
+	    coalesce->tx_coalesce_usecs >
+	    BFI_MAX_COALESCING_TIMEO * BFI_COALESCING_TIMER_UNIT)
+		return -EINVAL;
+
+	mutex_lock(&bnad->conf_mutex);
+	/*
+	 * Do not need to store rx_coalesce_usecs here
+	 * Every time DIM is disabled, we can get it from the
+	 * stack.
+	 */
+	spin_lock_irqsave(&bnad->bna_lock, flags);
+	if (coalesce->use_adaptive_rx_coalesce) {
+		if (!(bnad->cfg_flags & BNAD_CF_DIM_ENABLED)) {
+			bnad->cfg_flags |= BNAD_CF_DIM_ENABLED;
+			bnad_dim_timer_start(bnad);
+		}
+	} else {
+		if (bnad->cfg_flags & BNAD_CF_DIM_ENABLED) {
+			bnad->cfg_flags &= ~BNAD_CF_DIM_ENABLED;
+			dim_timer_del = bnad_dim_timer_running(bnad);
+			if (dim_timer_del) {
+				clear_bit(BNAD_RF_DIM_TIMER_RUNNING,
+							&bnad->run_flags);
+				spin_unlock_irqrestore(&bnad->bna_lock, flags);
+				del_timer_sync(&bnad->dim_timer);
+				spin_lock_irqsave(&bnad->bna_lock, flags);
+			}
+			bnad_rx_coalescing_timeo_set(bnad);
+		}
+	}
+	if (bnad->tx_coalescing_timeo != coalesce->tx_coalesce_usecs /
+					BFI_COALESCING_TIMER_UNIT) {
+		bnad->tx_coalescing_timeo = coalesce->tx_coalesce_usecs /
+						BFI_COALESCING_TIMER_UNIT;
+		bnad_tx_coalescing_timeo_set(bnad);
+	}
+
+	if (bnad->rx_coalescing_timeo != coalesce->rx_coalesce_usecs /
+					BFI_COALESCING_TIMER_UNIT) {
+		bnad->rx_coalescing_timeo = coalesce->rx_coalesce_usecs /
+						BFI_COALESCING_TIMER_UNIT;
+
+		if (!(bnad->cfg_flags & BNAD_CF_DIM_ENABLED))
+			bnad_rx_coalescing_timeo_set(bnad);
+
+	}
+
+	/* Add Tx Inter-pkt DMA count?  */
+
+	spin_unlock_irqrestore(&bnad->bna_lock, flags);
+
+	mutex_unlock(&bnad->conf_mutex);
+	return 0;
+}
+
+static void
+bnad_get_ringparam(struct net_device *netdev,
+		   struct ethtool_ringparam *ringparam)
+{
+	struct bnad *bnad = netdev_priv(netdev);
+
+	ringparam->rx_max_pending = BNAD_MAX_Q_DEPTH / bnad_rxqs_per_cq;
+	ringparam->rx_mini_max_pending = 0;
+	ringparam->rx_jumbo_max_pending = 0;
+	ringparam->tx_max_pending = BNAD_MAX_Q_DEPTH;
+
+	ringparam->rx_pending = bnad->rxq_depth;
+	ringparam->rx_mini_max_pending = 0;
+	ringparam->rx_jumbo_max_pending = 0;
+	ringparam->tx_pending = bnad->txq_depth;
+}
+
+static int
+bnad_set_ringparam(struct net_device *netdev,
+		   struct ethtool_ringparam *ringparam)
+{
+	int i, current_err, err = 0;
+	struct bnad *bnad = netdev_priv(netdev);
+
+	mutex_lock(&bnad->conf_mutex);
+	if (ringparam->rx_pending == bnad->rxq_depth &&
+	    ringparam->tx_pending == bnad->txq_depth) {
+		mutex_unlock(&bnad->conf_mutex);
+		return 0;
+	}
+
+	if (ringparam->rx_pending < BNAD_MIN_Q_DEPTH ||
+	    ringparam->rx_pending > BNAD_MAX_Q_DEPTH / bnad_rxqs_per_cq ||
+	    !BNA_POWER_OF_2(ringparam->rx_pending)) {
+		mutex_unlock(&bnad->conf_mutex);
+		return -EINVAL;
+	}
+	if (ringparam->tx_pending < BNAD_MIN_Q_DEPTH ||
+	    ringparam->tx_pending > BNAD_MAX_Q_DEPTH ||
+	    !BNA_POWER_OF_2(ringparam->tx_pending)) {
+		mutex_unlock(&bnad->conf_mutex);
+		return -EINVAL;
+	}
+
+	if (ringparam->rx_pending != bnad->rxq_depth) {
+		bnad->rxq_depth = ringparam->rx_pending;
+		for (i = 0; i < bnad->num_rx; i++) {
+			if (!bnad->rx_info[i].rx)
+				continue;
+			bnad_cleanup_rx(bnad, i);
+			current_err = bnad_setup_rx(bnad, i);
+			if (current_err && !err)
+				err = current_err;
+		}
+	}
+	if (ringparam->tx_pending != bnad->txq_depth) {
+		bnad->txq_depth = ringparam->tx_pending;
+		for (i = 0; i < bnad->num_tx; i++) {
+			if (!bnad->tx_info[i].tx)
+				continue;
+			bnad_cleanup_tx(bnad, i);
+			current_err = bnad_setup_tx(bnad, i);
+			if (current_err && !err)
+				err = current_err;
+		}
+	}
+
+	mutex_unlock(&bnad->conf_mutex);
+	return err;
+}
+
+static void
+bnad_get_pauseparam(struct net_device *netdev,
+		    struct ethtool_pauseparam *pauseparam)
+{
+	struct bnad *bnad = netdev_priv(netdev);
+
+	pauseparam->autoneg = 0;
+	pauseparam->rx_pause = bnad->bna.port.pause_config.rx_pause;
+	pauseparam->tx_pause = bnad->bna.port.pause_config.tx_pause;
+}
+
+static int
+bnad_set_pauseparam(struct net_device *netdev,
+		    struct ethtool_pauseparam *pauseparam)
+{
+	struct bnad *bnad = netdev_priv(netdev);
+	struct bna_pause_config pause_config;
+	unsigned long flags;
+
+	if (pauseparam->autoneg == AUTONEG_ENABLE)
+		return -EINVAL;
+
+	mutex_lock(&bnad->conf_mutex);
+	if (pauseparam->rx_pause != bnad->bna.port.pause_config.rx_pause ||
+	    pauseparam->tx_pause != bnad->bna.port.pause_config.tx_pause) {
+		pause_config.rx_pause = pauseparam->rx_pause;
+		pause_config.tx_pause = pauseparam->tx_pause;
+		spin_lock_irqsave(&bnad->bna_lock, flags);
+		bna_port_pause_config(&bnad->bna.port, &pause_config, NULL);
+		spin_unlock_irqrestore(&bnad->bna_lock, flags);
+	}
+	mutex_unlock(&bnad->conf_mutex);
+	return 0;
+}
+
+static u32
+bnad_get_rx_csum(struct net_device *netdev)
+{
+	u32 rx_csum;
+	struct bnad *bnad = netdev_priv(netdev);
+
+	rx_csum = bnad->rx_csum;
+	return rx_csum;
+}
+
+static int
+bnad_set_rx_csum(struct net_device *netdev, u32 rx_csum)
+{
+	struct bnad *bnad = netdev_priv(netdev);
+
+	mutex_lock(&bnad->conf_mutex);
+	bnad->rx_csum = rx_csum;
+	mutex_unlock(&bnad->conf_mutex);
+	return 0;
+}
+
+static int
+bnad_set_tx_csum(struct net_device *netdev, u32 tx_csum)
+{
+	struct bnad *bnad = netdev_priv(netdev);
+
+	mutex_lock(&bnad->conf_mutex);
+	if (tx_csum) {
+		netdev->features |= NETIF_F_IP_CSUM;
+		netdev->features |= NETIF_F_IPV6_CSUM;
+	} else {
+		netdev->features &= ~NETIF_F_IP_CSUM;
+		netdev->features &= ~NETIF_F_IPV6_CSUM;
+	}
+	mutex_unlock(&bnad->conf_mutex);
+	return 0;
+}
+
+static int
+bnad_set_tso(struct net_device *netdev, u32 tso)
+{
+	struct bnad *bnad = netdev_priv(netdev);
+
+	mutex_lock(&bnad->conf_mutex);
+	if (tso) {
+		netdev->features |= NETIF_F_TSO;
+		netdev->features |= NETIF_F_TSO6;
+	} else {
+		netdev->features &= ~NETIF_F_TSO;
+		netdev->features &= ~NETIF_F_TSO6;
+	}
+	mutex_unlock(&bnad->conf_mutex);
+	return 0;
+}
+
+static void
+bnad_get_strings(struct net_device *netdev, u32 stringset, u8 * string)
+{
+	struct bnad *bnad = netdev_priv(netdev);
+	int i, j, q_num;
+	u64 bmap;
+
+	mutex_lock(&bnad->conf_mutex);
+
+	switch (stringset) {
+	case ETH_SS_STATS:
+		for (i = 0; i < BNAD_ETHTOOL_STATS_NUM; i++) {
+			BUG_ON(!(strlen(bnad_net_stats_strings[i]) <
+				   ETH_GSTRING_LEN));
+			memcpy(string, bnad_net_stats_strings[i],
+			       ETH_GSTRING_LEN);
+			string += ETH_GSTRING_LEN;
+		}
+		bmap = (u64)bnad->bna.tx_mod.txf_bmap[0] |
+			((u64)bnad->bna.tx_mod.txf_bmap[1] << 32);
+		for (i = 0; bmap && (i < BFI_LL_TXF_ID_MAX); i++) {
+			if (bmap & 1) {
+				sprintf(string, "txf%d_ucast_octets", i);
+				string += ETH_GSTRING_LEN;
+				sprintf(string, "txf%d_ucast", i);
+				string += ETH_GSTRING_LEN;
+				sprintf(string, "txf%d_ucast_vlan", i);
+				string += ETH_GSTRING_LEN;
+				sprintf(string, "txf%d_mcast_octets", i);
+				string += ETH_GSTRING_LEN;
+				sprintf(string, "txf%d_mcast", i);
+				string += ETH_GSTRING_LEN;
+				sprintf(string, "txf%d_mcast_vlan", i);
+				string += ETH_GSTRING_LEN;
+				sprintf(string, "txf%d_bcast_octets", i);
+				string += ETH_GSTRING_LEN;
+				sprintf(string, "txf%d_bcast", i);
+				string += ETH_GSTRING_LEN;
+				sprintf(string, "txf%d_bcast_vlan", i);
+				string += ETH_GSTRING_LEN;
+				sprintf(string, "txf%d_errors", i);
+				string += ETH_GSTRING_LEN;
+				sprintf(string, "txf%d_filter_vlan", i);
+				string += ETH_GSTRING_LEN;
+				sprintf(string, "txf%d_filter_mac_sa", i);
+				string += ETH_GSTRING_LEN;
+			}
+			bmap >>= 1;
+		}
+
+		bmap = (u64)bnad->bna.rx_mod.rxf_bmap[0] |
+			((u64)bnad->bna.rx_mod.rxf_bmap[1] << 32);
+		for (i = 0; bmap && (i < BFI_LL_RXF_ID_MAX); i++) {
+			if (bmap & 1) {
+				sprintf(string, "rxf%d_ucast_octets", i);
+				string += ETH_GSTRING_LEN;
+				sprintf(string, "rxf%d_ucast", i);
+				string += ETH_GSTRING_LEN;
+				sprintf(string, "rxf%d_ucast_vlan", i);
+				string += ETH_GSTRING_LEN;
+				sprintf(string, "rxf%d_mcast_octets", i);
+				string += ETH_GSTRING_LEN;
+				sprintf(string, "rxf%d_mcast", i);
+				string += ETH_GSTRING_LEN;
+				sprintf(string, "rxf%d_mcast_vlan", i);
+				string += ETH_GSTRING_LEN;
+				sprintf(string, "rxf%d_bcast_octets", i);
+				string += ETH_GSTRING_LEN;
+				sprintf(string, "rxf%d_bcast", i);
+				string += ETH_GSTRING_LEN;
+				sprintf(string, "rxf%d_bcast_vlan", i);
+				string += ETH_GSTRING_LEN;
+				sprintf(string, "rxf%d_frame_drops", i);
+				string += ETH_GSTRING_LEN;
+			}
+			bmap >>= 1;
+		}
+
+		q_num = 0;
+		for (i = 0; i < bnad->num_rx; i++) {
+			if (!bnad->rx_info[i].rx)
+				continue;
+			for (j = 0; j < bnad->num_rxp_per_rx; j++) {
+				sprintf(string, "cq%d_producer_index", q_num);
+				string += ETH_GSTRING_LEN;
+				sprintf(string, "cq%d_consumer_index", q_num);
+				string += ETH_GSTRING_LEN;
+				sprintf(string, "cq%d_hw_producer_index",
+					q_num);
+				string += ETH_GSTRING_LEN;
+				q_num++;
+			}
+		}
+
+		q_num = 0;
+		for (i = 0; i < bnad->num_rx; i++) {
+			if (!bnad->rx_info[i].rx)
+				continue;
+			for (j = 0; j < bnad->num_rxp_per_rx; j++) {
+				sprintf(string, "rxq%d_packets", q_num);
+				string += ETH_GSTRING_LEN;
+				sprintf(string, "rxq%d_bytes", q_num);
+				string += ETH_GSTRING_LEN;
+				sprintf(string, "rxq%d_packets_with_error",
+								q_num);
+				string += ETH_GSTRING_LEN;
+				sprintf(string, "rxq%d_allocbuf_failed", q_num);
+				string += ETH_GSTRING_LEN;
+				sprintf(string, "rxq%d_producer_index", q_num);
+				string += ETH_GSTRING_LEN;
+				sprintf(string, "rxq%d_consumer_index", q_num);
+				string += ETH_GSTRING_LEN;
+				q_num++;
+				if (bnad->rx_info[i].rx_ctrl[j].ccb &&
+					bnad->rx_info[i].rx_ctrl[j].ccb->
+					rcb[1] &&
+					bnad->rx_info[i].rx_ctrl[j].ccb->
+					rcb[1]->rxq) {
+					sprintf(string, "rxq%d_packets", q_num);
+					string += ETH_GSTRING_LEN;
+					sprintf(string, "rxq%d_bytes", q_num);
+					string += ETH_GSTRING_LEN;
+					sprintf(string,
+					"rxq%d_packets_with_error", q_num);
+					string += ETH_GSTRING_LEN;
+					sprintf(string, "rxq%d_allocbuf_failed",
+								q_num);
+					string += ETH_GSTRING_LEN;
+					sprintf(string, "rxq%d_producer_index",
+								q_num);
+					string += ETH_GSTRING_LEN;
+					sprintf(string, "rxq%d_consumer_index",
+								q_num);
+					string += ETH_GSTRING_LEN;
+					q_num++;
+				}
+			}
+		}
+
+		q_num = 0;
+		for (i = 0; i < bnad->num_tx; i++) {
+			if (!bnad->tx_info[i].tx)
+				continue;
+			for (j = 0; j < bnad->num_txq_per_tx; j++) {
+				sprintf(string, "txq%d_packets", q_num);
+				string += ETH_GSTRING_LEN;
+				sprintf(string, "txq%d_bytes", q_num);
+				string += ETH_GSTRING_LEN;
+				sprintf(string, "txq%d_producer_index", q_num);
+				string += ETH_GSTRING_LEN;
+				sprintf(string, "txq%d_consumer_index", q_num);
+				string += ETH_GSTRING_LEN;
+				sprintf(string, "txq%d_hw_consumer_index",
+									q_num);
+				string += ETH_GSTRING_LEN;
+				q_num++;
+			}
+		}
+
+		break;
+
+	default:
+		break;
+	}
+
+	mutex_unlock(&bnad->conf_mutex);
+}
+
+static int
+bnad_get_stats_count_locked(struct net_device *netdev)
+{
+	struct bnad *bnad = netdev_priv(netdev);
+	int i, j, count, rxf_active_num = 0, txf_active_num = 0;
+	u64 bmap;
+
+	bmap = (u64)bnad->bna.tx_mod.txf_bmap[0] |
+			((u64)bnad->bna.tx_mod.txf_bmap[1] << 32);
+	for (i = 0; bmap && (i < BFI_LL_TXF_ID_MAX); i++) {
+		if (bmap & 1)
+			txf_active_num++;
+		bmap >>= 1;
+	}
+	bmap = (u64)bnad->bna.rx_mod.rxf_bmap[0] |
+			((u64)bnad->bna.rx_mod.rxf_bmap[1] << 32);
+	for (i = 0; bmap && (i < BFI_LL_RXF_ID_MAX); i++) {
+		if (bmap & 1)
+			rxf_active_num++;
+		bmap >>= 1;
+	}
+	count = BNAD_ETHTOOL_STATS_NUM +
+		txf_active_num * BNAD_NUM_TXF_COUNTERS +
+		rxf_active_num * BNAD_NUM_RXF_COUNTERS;
+
+	for (i = 0; i < bnad->num_rx; i++) {
+		if (!bnad->rx_info[i].rx)
+			continue;
+		count += bnad->num_rxp_per_rx * BNAD_NUM_CQ_COUNTERS;
+		count += bnad->num_rxp_per_rx * BNAD_NUM_RXQ_COUNTERS;
+		for (j = 0; j < bnad->num_rxp_per_rx; j++)
+			if (bnad->rx_info[i].rx_ctrl[j].ccb &&
+				bnad->rx_info[i].rx_ctrl[j].ccb->rcb[1] &&
+				bnad->rx_info[i].rx_ctrl[j].ccb->rcb[1]->rxq)
+				count +=  BNAD_NUM_RXQ_COUNTERS;
+	}
+
+	for (i = 0; i < bnad->num_tx; i++) {
+		if (!bnad->tx_info[i].tx)
+			continue;
+		count += bnad->num_txq_per_tx * BNAD_NUM_TXQ_COUNTERS;
+	}
+	return count;
+}
+
+static int
+bnad_per_q_stats_fill(struct bnad *bnad, u64 *buf, int bi)
+{
+	int i, j;
+	struct bna_rcb *rcb = NULL;
+	struct bna_tcb *tcb = NULL;
+
+	for (i = 0; i < bnad->num_rx; i++) {
+		if (!bnad->rx_info[i].rx)
+			continue;
+		for (j = 0; j < bnad->num_rxp_per_rx; j++)
+			if (bnad->rx_info[i].rx_ctrl[j].ccb &&
+				bnad->rx_info[i].rx_ctrl[j].ccb->rcb[0] &&
+				bnad->rx_info[i].rx_ctrl[j].ccb->rcb[0]->rxq) {
+				buf[bi++] = bnad->rx_info[i].rx_ctrl[j].
+						ccb->producer_index;
+				buf[bi++] = 0; /* ccb->consumer_index */
+				buf[bi++] = *(bnad->rx_info[i].rx_ctrl[j].
+						ccb->hw_producer_index);
+			}
+	}
+	for (i = 0; i < bnad->num_rx; i++) {
+		if (!bnad->rx_info[i].rx)
+			continue;
+		for (j = 0; j < bnad->num_rxp_per_rx; j++)
+			if (bnad->rx_info[i].rx_ctrl[j].ccb) {
+				if (bnad->rx_info[i].rx_ctrl[j].ccb->rcb[0] &&
+					bnad->rx_info[i].rx_ctrl[j].ccb->
+					rcb[0]->rxq) {
+					rcb = bnad->rx_info[i].rx_ctrl[j].
+							ccb->rcb[0];
+					buf[bi++] = rcb->rxq->rx_packets;
+					buf[bi++] = rcb->rxq->rx_bytes;
+					buf[bi++] = rcb->rxq->
+							rx_packets_with_error;
+					buf[bi++] = rcb->rxq->
+							rxbuf_alloc_failed;
+					buf[bi++] = rcb->producer_index;
+					buf[bi++] = rcb->consumer_index;
+				}
+				if (bnad->rx_info[i].rx_ctrl[j].ccb->rcb[1] &&
+					bnad->rx_info[i].rx_ctrl[j].ccb->
+					rcb[1]->rxq) {
+					rcb = bnad->rx_info[i].rx_ctrl[j].
+								ccb->rcb[1];
+					buf[bi++] = rcb->rxq->rx_packets;
+					buf[bi++] = rcb->rxq->rx_bytes;
+					buf[bi++] = rcb->rxq->
+							rx_packets_with_error;
+					buf[bi++] = rcb->rxq->
+							rxbuf_alloc_failed;
+					buf[bi++] = rcb->producer_index;
+					buf[bi++] = rcb->consumer_index;
+				}
+			}
+	}
+
+	for (i = 0; i < bnad->num_tx; i++) {
+		if (!bnad->tx_info[i].tx)
+			continue;
+		for (j = 0; j < bnad->num_txq_per_tx; j++)
+			if (bnad->tx_info[i].tcb[j] &&
+				bnad->tx_info[i].tcb[j]->txq) {
+				tcb = bnad->tx_info[i].tcb[j];
+				buf[bi++] = tcb->txq->tx_packets;
+				buf[bi++] = tcb->txq->tx_bytes;
+				buf[bi++] = tcb->producer_index;
+				buf[bi++] = tcb->consumer_index;
+				buf[bi++] = *(tcb->hw_consumer_index);
+			}
+	}
+
+	return bi;
+}
+
+static void
+bnad_get_ethtool_stats(struct net_device *netdev, struct ethtool_stats *stats,
+		       u64 *buf)
+{
+	struct bnad *bnad = netdev_priv(netdev);
+	int i, j, bi;
+	unsigned long *net_stats, flags;
+	u64 *stats64;
+	u64 bmap;
+
+	mutex_lock(&bnad->conf_mutex);
+	if (bnad_get_stats_count_locked(netdev) != stats->n_stats) {
+		mutex_unlock(&bnad->conf_mutex);
+		return;
+	}
+
+	/*
+	 * Used bna_lock to sync reads from bna_stats, which is written
+	 * under the same lock
+	 */
+	spin_lock_irqsave(&bnad->bna_lock, flags);
+	bi = 0;
+	memset(buf, 0, stats->n_stats * sizeof(u64));
+	memset(&bnad->net_stats, 0, sizeof(struct net_device_stats));
+
+	bnad_netdev_qstats_fill(bnad);
+	bnad_netdev_hwstats_fill(bnad);
+
+	/* Fill net_stats into ethtool buffers */
+	net_stats = (unsigned long *)&bnad->net_stats;
+	for (i = 0; i < sizeof(struct net_device_stats) / sizeof(unsigned long);
+	     i++)
+		buf[bi++] = net_stats[i];
+
+	/* Fill driver stats into ethtool buffers */
+	stats64 = (u64 *)&bnad->stats.drv_stats;
+	for (i = 0; i < sizeof(struct bnad_drv_stats) / sizeof(u64); i++)
+		buf[bi++] = stats64[i];
+
+	/* Fill hardware stats excluding the rxf/txf into ethtool bufs */
+	stats64 = (u64 *) bnad->stats.bna_stats->hw_stats;
+	for (i = 0;
+	     i < offsetof(struct bfi_ll_stats, rxf_stats[0]) / sizeof(u64);
+	     i++)
+		buf[bi++] = stats64[i];
+
+	/* Fill txf stats into ethtool buffers */
+	bmap = (u64)bnad->bna.tx_mod.txf_bmap[0] |
+			((u64)bnad->bna.tx_mod.txf_bmap[1] << 32);
+	for (i = 0; bmap && (i < BFI_LL_TXF_ID_MAX); i++) {
+		if (bmap & 1) {
+			stats64 = (u64 *)&bnad->stats.bna_stats->
+						hw_stats->txf_stats[i];
+			for (j = 0; j < sizeof(struct bfi_ll_stats_txf) /
+					sizeof(u64); j++)
+				buf[bi++] = stats64[j];
+		}
+		bmap >>= 1;
+	}
+
+	/*  Fill rxf stats into ethtool buffers */
+	bmap = (u64)bnad->bna.rx_mod.rxf_bmap[0] |
+			((u64)bnad->bna.rx_mod.rxf_bmap[1] << 32);
+	for (i = 0; bmap && (i < BFI_LL_RXF_ID_MAX); i++) {
+		if (bmap & 1) {
+			stats64 = (u64 *)&bnad->stats.bna_stats->
+						hw_stats->rxf_stats[i];
+			for (j = 0; j < sizeof(struct bfi_ll_stats_rxf) /
+					sizeof(u64); j++)
+				buf[bi++] = stats64[j];
+		}
+		bmap >>= 1;
+	}
+
+	/* Fill per Q stats into ethtool buffers */
+	bi = bnad_per_q_stats_fill(bnad, buf, bi);
+
+	spin_unlock_irqrestore(&bnad->bna_lock, flags);
+
+	mutex_unlock(&bnad->conf_mutex);
+}
+
+static int
+bnad_get_sset_count(struct net_device *netdev, int sset)
+{
+	switch (sset) {
+	case ETH_SS_STATS:
+		return bnad_get_stats_count_locked(netdev);
+	default:
+		return -EOPNOTSUPP;
+	}
+}
+
+static struct ethtool_ops bnad_ethtool_ops = {
+	.get_settings = bnad_get_settings,
+	.set_settings = bnad_set_settings,
+	.get_drvinfo = bnad_get_drvinfo,
+	.get_regs_len = bnad_get_regs_len,
+	.get_regs = bnad_get_regs,
+	.get_wol = bnad_get_wol,
+	.get_link = ethtool_op_get_link,
+	.get_coalesce = bnad_get_coalesce,
+	.set_coalesce = bnad_set_coalesce,
+	.get_ringparam = bnad_get_ringparam,
+	.set_ringparam = bnad_set_ringparam,
+	.get_pauseparam = bnad_get_pauseparam,
+	.set_pauseparam = bnad_set_pauseparam,
+	.get_rx_csum = bnad_get_rx_csum,
+	.set_rx_csum = bnad_set_rx_csum,
+	.get_tx_csum = ethtool_op_get_tx_csum,
+	.set_tx_csum = bnad_set_tx_csum,
+	.get_sg = ethtool_op_get_sg,
+	.set_sg = ethtool_op_set_sg,
+	.get_tso = ethtool_op_get_tso,
+	.set_tso = bnad_set_tso,
+	.get_flags = ethtool_op_get_flags,
+	.set_flags = ethtool_op_set_flags,
+	.get_strings = bnad_get_strings,
+	.get_ethtool_stats = bnad_get_ethtool_stats,
+	.get_sset_count = bnad_get_sset_count
+};
+
+void
+bnad_set_ethtool_ops(struct net_device *netdev)
+{
+	SET_ETHTOOL_OPS(netdev, &bnad_ethtool_ops);
+}

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH 1/6] bna: Brocade 10Gb Ethernet device driver
  2010-08-07  3:23 Debashis Dutt
@ 2010-08-09 15:15 ` Stephen Hemminger
  2010-08-11 19:45   ` Debashis Dutt
  0 siblings, 1 reply; 8+ messages in thread
From: Stephen Hemminger @ 2010-08-09 15:15 UTC (permalink / raw)
  To: Debashis Dutt; +Cc: netdev

On Fri, 6 Aug 2010 20:23:21 -0700
Debashis Dutt <ddutt@Brocade.COM> wrote:

> 
> Hi Stephen, 
> 
> Thanks a lot for your comments. 
> 
> > +                   if (likely
> > +                       (wis > BNA_QE_FREE_CNT(tcb, tcb->q_depth) ||
> > +                        vectors > BNA_QE_FREE_CNT(unmap_q, unmap_q->q_depth))) {
> > +                               BNAD_UPDATE_CTR(bnad, netif_queue_stop);
> > +                               return NETDEV_TX_BUSY;
> 
> >The transmit routine should check for available space after
> > queueing to device, so you can avoid having to return
> >TX_BUSY.
> 
> However your above comment is not very clear to me.
> 
> Our Tx routine already cleans up the Tx buffers at the end, through a tasklet.
> 
> Cleaning of Tx buffers happens 
> 1) In the sending context at the end of the Tx routine.
> 2) In the IRQ context when we get a Tx completion interrupt.
> 
> Only thing that we could have done, is do this check again at the end of the Tx routine
> and called netif_stop_queue() if required, so that the stack stops sending. Even then I am 
> not sure that we could avoid the current check and returning TX_BUSY.
> 
> It would be nice if you clarify, so that I understand this better.
> 
> Thanks
> --Debashis
> Linux LL Driver Team.
> 
> 
> -----Original Message-----
> From: Stephen Hemminger [mailto:shemminger@vyatta.com] 
> Sent: Wednesday, August 04, 2010 10:09 AM
> To: Rasesh Mody
> Cc: netdev@vger.kernel.org; Debashis Dutt; Jing Huang
> Subject: Re: [PATCH 1/6] bna: Brocade 10Gb Ethernet device driver
> 
> On Tue, 3 Aug 2010 22:15:36 -0700
> Rasesh Mody <rmody@brocade.com> wrote:
> 
> > From: Rasesh Mody <rmody@brocade.com>
> > 
> > This is patch 1/6 which contains linux driver source for
> > Brocade's BR1010/BR1020 10Gb CEE capable ethernet adapter.
> > Source is based against net-next-2.6.
> > 
> > We wish this patch to be considered for inclusion in net-next-2.6
> > 
> > Signed-off-by: Rasesh Mody <rmody@brocade.com>
> > ---
> >  bnad.c         | 3326 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
> >  bnad.h         |  474 ++++++++
> >  bnad_ethtool.c | 1269 +++++++++++++++++++++
> >  3 files changed, 5069 insertions(+)
> > 
> > diff -ruP net-next-2.6.35-rc1-orig/drivers/net/bna/bnad.c net-next-2.6.35-rc1-mod/drivers/net/bna/bnad.c
> > --- net-next-2.6.35-rc1-orig/drivers/net/bna/bnad.c         1969-12-31 16:00:00.000000000 -0800
> > +++ net-next-2.6.35-rc1-mod/drivers/net/bna/bnad.c     2010-08-02 17:19:19.447239000 -0700
> > @@ -0,0 +1,3326 @@
> > +/*
> > + * Linux network driver for Brocade Converged Network Adapter.
> > + *
> > + * This program is free software; you can redistribute it and/or modify it
> > + * under the terms of the GNU General Public License (GPL) Version 2 as
> > + * published by the Free Software Foundation
> > + *
> > + * This program is distributed in the hope that it will be useful, but
> > + * WITHOUT ANY WARRANTY; without even the implied warranty of
> > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> > + * General Public License for more details.
> > + */
> > +/*
> > + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc.
> > + * All rights reserved
> > + * www.brocade.com
> > + */
> > +#include <linux/netdevice.h>
> > +#include <linux/skbuff.h>
> > +#include <linux/etherdevice.h>
> > +#include <linux/in.h>
> > +#include <linux/ethtool.h>
> > +#include <linux/if_vlan.h>
> > +#include <linux/if_ether.h>
> > +#include <linux/ip.h>
> > +
> > +#include "bnad.h"
> > +#include "bna.h"
> > +#include "cna.h"
> > +
> > +DEFINE_MUTEX(bnad_fwimg_mutex);
> > +
> > +/*
> > + * Module params
> > + */
> > +static uint bnad_msix_disable;
> > +module_param(bnad_msix_disable, uint, 0444);
> > +MODULE_PARM_DESC(bnad_msix_disable, "Disable MSIX mode");
> > +
> > +static uint bnad_ioc_auto_recover = 1;
> > +module_param(bnad_ioc_auto_recover, uint, 0444);
> > +MODULE_PARM_DESC(bnad_ioc_auto_recover, "Enable / Disable auto recovery");
> > +
> > +/*
> > + * Global variables
> > + */
> > +u32 bna_id;
> > +u32 bnad_rxqs_per_cq = 2;
> > +
> > +DECLARE_MUTEX(bnad_list_sem);
> > +LIST_HEAD(bnad_list);
> > +
> > +const u8 bnad_bcast_addr[] =  {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
> 
> Surprised this isn't defined somewhere else.
> 
> > +
> > +/*
> > + * Local MACROS
> > + */
> > +#define BNAD_TX_UNMAPQ_DEPTH (bnad->txq_depth * 2)
> > +
> > +#define BNAD_RX_UNMAPQ_DEPTH (bnad->rxq_depth)
> > +
> > +#define BNAD_GET_MBOX_IRQ(_bnad)                                            \
> > +       (((_bnad)->cfg_flags & BNAD_CF_MSIX) ?                                 \
> > +       ((_bnad)->msix_table[(_bnad)->msix_num - 1].vector) :   \
> > +       ((_bnad)->pcidev->irq))
> > +
> > +#define BNAD_FILL_UNMAPQ_MEM_REQ(_res_info, _num, _depth)  \
> > +do {                                                                                     \
> > +       (_res_info)->res_type = BNA_RES_T_MEM;                               \
> > +       (_res_info)->res_u.mem_info.mem_type = BNA_MEM_T_KVA;    \
> > +       (_res_info)->res_u.mem_info.num = (_num);                   \
> > +       (_res_info)->res_u.mem_info.len =                                 \
> > +       sizeof(struct bnad_unmap_q) +                                      \
> > +       (sizeof(struct bnad_skb_unmap) * ((_depth) - 1));            \
> > +} while (0)
> > +
> > +void
> > +bnad_add_to_list(struct bnad *bnad)
> > +{
> > +       down(&bnad_list_sem);
> > +       list_add_tail(&bnad->list_entry, &bnad_list);
> > +       bna_id++;
> > +       up(&bnad_list_sem);
> > +}
> 
> Why do you need to list semaphore? Isn't RTNL mutex held
> when this is done. If you have to have own exclusion use
> a mutex for this.
> 
> > +void
> > +bnad_remove_from_list(struct bnad *bnad)
> > +{
> > +       down(&bnad_list_sem);
> > +       list_del(&bnad->list_entry);
> > +       up(&bnad_list_sem);
> > +}
> > +
> > +const struct pci_device_id bnad_pci_id_table[] = {
> > +       {
> > +                   PCI_DEVICE(PCI_VENDOR_ID_BROCADE,
> > +                               PCI_DEVICE_ID_BROCADE_CT),
> > +                   .class = PCI_CLASS_NETWORK_ETHERNET << 8,
> > +                   .class_mask =  0xffff00
> > +       }, {0,  }
> > +};
> 
> Why is this not static?
> 
> 
> > +/* TX */
> > +/* bnad_start_xmit : Netdev entry point for Transmit */
> > +/*                      Called under lock held by net_device */
> > +
> > +netdev_tx_t
> > +bnad_start_xmit(struct sk_buff *skb, struct net_device *netdev)
> 
> Should also be static...
> 
> > +{
> > +       struct bnad *bnad = netdev_priv(netdev);
> > +
> > +       u16                   txq_prod, vlan_tag = 0;
> > +       u32                   unmap_prod, wis, wis_used, wi_range;
> > +       u32                   vectors, vect_id, i, acked;
> > +       u32                   tx_id;
> > +       int                                 err;
> > +
> > +       struct bnad_tx_info *tx_info;
> > +       struct bna_tcb *tcb;
> > +       struct bnad_unmap_q *unmap_q;
> > +       dma_addr_t                  dma_addr;
> > +       struct bna_txq_entry *txqent;
> > +       bna_txq_wi_ctrl_flag_t   flags;
> > +
> > +       if (unlikely
> > +           (skb->len <= ETH_HLEN || skb->len > BFI_TX_MAX_DATA_PER_PKT)) {
> > +                   dev_kfree_skb(skb);
> > +                   return NETDEV_TX_OK;
> > +       }
> > +
> > +       /*
> > +       * Takes care of the Tx that is scheduled between clearing the flag
> > +       * and the netif_stop_queue() call.
> > +       */
> > +       if (unlikely(!test_bit(BNAD_RF_TX_STARTED, &bnad->run_flags))) {
> > +                   dev_kfree_skb(skb);
> > +                   return NETDEV_TX_OK;
> > +       }
> > +
> > +       tx_id = BNAD_GET_TX_ID(skb);
> > +
> > +       tx_info = &bnad->tx_info[tx_id];
> > +       tcb = tx_info->tcb[tx_id];
> > +       unmap_q = tcb->unmap_q;
> > +
> > +       vectors = 1 + skb_shinfo(skb)->nr_frags;
> > +       if (vectors > BFI_TX_MAX_VECTORS_PER_PKT) {
> > +                   dev_kfree_skb(skb);
> > +                   return NETDEV_TX_OK;
> > +       }
> > +       wis = BNA_TXQ_WI_NEEDED(vectors); /* 4 vectors per work item */
> > +       acked = 0;
> > +       if (unlikely
> > +           (wis > BNA_QE_FREE_CNT(tcb, tcb->q_depth) ||
> > +            vectors > BNA_QE_FREE_CNT(unmap_q, unmap_q->q_depth))) {
> > +                   if ((u16) (*tcb->hw_consumer_index) !=
> > +                       tcb->consumer_index &&
> > +                       !test_and_set_bit(BNAD_TXQ_FREE_SENT, &tcb->flags)) {
> > +                               acked = bnad_free_txbufs(bnad, tcb);
> > +                               bna_ib_ack(tcb->i_dbell, acked);
> > +                               smp_mb__before_clear_bit();
> > +                               clear_bit(BNAD_TXQ_FREE_SENT, &tcb->flags);
> > +                   } else {
> > +                               netif_stop_queue(netdev);
> > +                               BNAD_UPDATE_CTR(bnad, netif_queue_stop);
> > +                   }
> > +
> > +                   smp_mb();
> > +                   /*
> > +                   * Check again to deal with race condition between
> > +                   * netif_stop_queue here, and netif_wake_queue in
> > +                   * interrupt handler which is not inside netif tx lock.
> > +                   */
> > +                   if (likely
> > +                       (wis > BNA_QE_FREE_CNT(tcb, tcb->q_depth) ||
> > +                        vectors > BNA_QE_FREE_CNT(unmap_q, unmap_q->q_depth))) {
> > +                               BNAD_UPDATE_CTR(bnad, netif_queue_stop);
> > +                               return NETDEV_TX_BUSY;
> 
> The transmit routine should check for available space after
> queueing to device, so you can avoid having to return
> TX_BUSY.


The problem is that if device returns TX_BUSY, the net transmit scheduler
will end up re-calling the transmit routine.  This looks ok in your driver
because it will set netif_stop_queue

^ permalink raw reply	[flat|nested] 8+ messages in thread

* RE: [PATCH 1/6] bna: Brocade 10Gb Ethernet device driver
@ 2010-08-07  3:23 Debashis Dutt
  2010-08-09 15:15 ` Stephen Hemminger
  0 siblings, 1 reply; 8+ messages in thread
From: Debashis Dutt @ 2010-08-07  3:23 UTC (permalink / raw)
  To: Stephen Hemminger; +Cc: netdev


Hi Stephen, 

Thanks a lot for your comments. 

> +                   if (likely
> +                       (wis > BNA_QE_FREE_CNT(tcb, tcb->q_depth) ||
> +                        vectors > BNA_QE_FREE_CNT(unmap_q, unmap_q->q_depth))) {
> +                               BNAD_UPDATE_CTR(bnad, netif_queue_stop);
> +                               return NETDEV_TX_BUSY;

>The transmit routine should check for available space after
> queueing to device, so you can avoid having to return
>TX_BUSY.

However your above comment is not very clear to me.

Our Tx routine already cleans up the Tx buffers at the end, through a tasklet.

Cleaning of Tx buffers happens 
1) In the sending context at the end of the Tx routine.
2) In the IRQ context when we get a Tx completion interrupt.

Only thing that we could have done, is do this check again at the end of the Tx routine
and called netif_stop_queue() if required, so that the stack stops sending. Even then I am 
not sure that we could avoid the current check and returning TX_BUSY.

It would be nice if you clarify, so that I understand this better.

Thanks
--Debashis
Linux LL Driver Team.


-----Original Message-----
From: Stephen Hemminger [mailto:shemminger@vyatta.com] 
Sent: Wednesday, August 04, 2010 10:09 AM
To: Rasesh Mody
Cc: netdev@vger.kernel.org; Debashis Dutt; Jing Huang
Subject: Re: [PATCH 1/6] bna: Brocade 10Gb Ethernet device driver

On Tue, 3 Aug 2010 22:15:36 -0700
Rasesh Mody <rmody@brocade.com> wrote:

> From: Rasesh Mody <rmody@brocade.com>
> 
> This is patch 1/6 which contains linux driver source for
> Brocade's BR1010/BR1020 10Gb CEE capable ethernet adapter.
> Source is based against net-next-2.6.
> 
> We wish this patch to be considered for inclusion in net-next-2.6
> 
> Signed-off-by: Rasesh Mody <rmody@brocade.com>
> ---
>  bnad.c         | 3326 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  bnad.h         |  474 ++++++++
>  bnad_ethtool.c | 1269 +++++++++++++++++++++
>  3 files changed, 5069 insertions(+)
> 
> diff -ruP net-next-2.6.35-rc1-orig/drivers/net/bna/bnad.c net-next-2.6.35-rc1-mod/drivers/net/bna/bnad.c
> --- net-next-2.6.35-rc1-orig/drivers/net/bna/bnad.c         1969-12-31 16:00:00.000000000 -0800
> +++ net-next-2.6.35-rc1-mod/drivers/net/bna/bnad.c     2010-08-02 17:19:19.447239000 -0700
> @@ -0,0 +1,3326 @@
> +/*
> + * Linux network driver for Brocade Converged Network Adapter.
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License (GPL) Version 2 as
> + * published by the Free Software Foundation
> + *
> + * This program is distributed in the hope that it will be useful, but
> + * WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * General Public License for more details.
> + */
> +/*
> + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc.
> + * All rights reserved
> + * www.brocade.com
> + */
> +#include <linux/netdevice.h>
> +#include <linux/skbuff.h>
> +#include <linux/etherdevice.h>
> +#include <linux/in.h>
> +#include <linux/ethtool.h>
> +#include <linux/if_vlan.h>
> +#include <linux/if_ether.h>
> +#include <linux/ip.h>
> +
> +#include "bnad.h"
> +#include "bna.h"
> +#include "cna.h"
> +
> +DEFINE_MUTEX(bnad_fwimg_mutex);
> +
> +/*
> + * Module params
> + */
> +static uint bnad_msix_disable;
> +module_param(bnad_msix_disable, uint, 0444);
> +MODULE_PARM_DESC(bnad_msix_disable, "Disable MSIX mode");
> +
> +static uint bnad_ioc_auto_recover = 1;
> +module_param(bnad_ioc_auto_recover, uint, 0444);
> +MODULE_PARM_DESC(bnad_ioc_auto_recover, "Enable / Disable auto recovery");
> +
> +/*
> + * Global variables
> + */
> +u32 bna_id;
> +u32 bnad_rxqs_per_cq = 2;
> +
> +DECLARE_MUTEX(bnad_list_sem);
> +LIST_HEAD(bnad_list);
> +
> +const u8 bnad_bcast_addr[] =  {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};

Surprised this isn't defined somewhere else.

> +
> +/*
> + * Local MACROS
> + */
> +#define BNAD_TX_UNMAPQ_DEPTH (bnad->txq_depth * 2)
> +
> +#define BNAD_RX_UNMAPQ_DEPTH (bnad->rxq_depth)
> +
> +#define BNAD_GET_MBOX_IRQ(_bnad)                                            \
> +       (((_bnad)->cfg_flags & BNAD_CF_MSIX) ?                                 \
> +       ((_bnad)->msix_table[(_bnad)->msix_num - 1].vector) :   \
> +       ((_bnad)->pcidev->irq))
> +
> +#define BNAD_FILL_UNMAPQ_MEM_REQ(_res_info, _num, _depth)  \
> +do {                                                                                     \
> +       (_res_info)->res_type = BNA_RES_T_MEM;                               \
> +       (_res_info)->res_u.mem_info.mem_type = BNA_MEM_T_KVA;    \
> +       (_res_info)->res_u.mem_info.num = (_num);                   \
> +       (_res_info)->res_u.mem_info.len =                                 \
> +       sizeof(struct bnad_unmap_q) +                                      \
> +       (sizeof(struct bnad_skb_unmap) * ((_depth) - 1));            \
> +} while (0)
> +
> +void
> +bnad_add_to_list(struct bnad *bnad)
> +{
> +       down(&bnad_list_sem);
> +       list_add_tail(&bnad->list_entry, &bnad_list);
> +       bna_id++;
> +       up(&bnad_list_sem);
> +}

Why do you need to list semaphore? Isn't RTNL mutex held
when this is done. If you have to have own exclusion use
a mutex for this.

> +void
> +bnad_remove_from_list(struct bnad *bnad)
> +{
> +       down(&bnad_list_sem);
> +       list_del(&bnad->list_entry);
> +       up(&bnad_list_sem);
> +}
> +
> +const struct pci_device_id bnad_pci_id_table[] = {
> +       {
> +                   PCI_DEVICE(PCI_VENDOR_ID_BROCADE,
> +                               PCI_DEVICE_ID_BROCADE_CT),
> +                   .class = PCI_CLASS_NETWORK_ETHERNET << 8,
> +                   .class_mask =  0xffff00
> +       }, {0,  }
> +};

Why is this not static?


> +/* TX */
> +/* bnad_start_xmit : Netdev entry point for Transmit */
> +/*                      Called under lock held by net_device */
> +
> +netdev_tx_t
> +bnad_start_xmit(struct sk_buff *skb, struct net_device *netdev)

Should also be static...

> +{
> +       struct bnad *bnad = netdev_priv(netdev);
> +
> +       u16                   txq_prod, vlan_tag = 0;
> +       u32                   unmap_prod, wis, wis_used, wi_range;
> +       u32                   vectors, vect_id, i, acked;
> +       u32                   tx_id;
> +       int                                 err;
> +
> +       struct bnad_tx_info *tx_info;
> +       struct bna_tcb *tcb;
> +       struct bnad_unmap_q *unmap_q;
> +       dma_addr_t                  dma_addr;
> +       struct bna_txq_entry *txqent;
> +       bna_txq_wi_ctrl_flag_t   flags;
> +
> +       if (unlikely
> +           (skb->len <= ETH_HLEN || skb->len > BFI_TX_MAX_DATA_PER_PKT)) {
> +                   dev_kfree_skb(skb);
> +                   return NETDEV_TX_OK;
> +       }
> +
> +       /*
> +       * Takes care of the Tx that is scheduled between clearing the flag
> +       * and the netif_stop_queue() call.
> +       */
> +       if (unlikely(!test_bit(BNAD_RF_TX_STARTED, &bnad->run_flags))) {
> +                   dev_kfree_skb(skb);
> +                   return NETDEV_TX_OK;
> +       }
> +
> +       tx_id = BNAD_GET_TX_ID(skb);
> +
> +       tx_info = &bnad->tx_info[tx_id];
> +       tcb = tx_info->tcb[tx_id];
> +       unmap_q = tcb->unmap_q;
> +
> +       vectors = 1 + skb_shinfo(skb)->nr_frags;
> +       if (vectors > BFI_TX_MAX_VECTORS_PER_PKT) {
> +                   dev_kfree_skb(skb);
> +                   return NETDEV_TX_OK;
> +       }
> +       wis = BNA_TXQ_WI_NEEDED(vectors); /* 4 vectors per work item */
> +       acked = 0;
> +       if (unlikely
> +           (wis > BNA_QE_FREE_CNT(tcb, tcb->q_depth) ||
> +            vectors > BNA_QE_FREE_CNT(unmap_q, unmap_q->q_depth))) {
> +                   if ((u16) (*tcb->hw_consumer_index) !=
> +                       tcb->consumer_index &&
> +                       !test_and_set_bit(BNAD_TXQ_FREE_SENT, &tcb->flags)) {
> +                               acked = bnad_free_txbufs(bnad, tcb);
> +                               bna_ib_ack(tcb->i_dbell, acked);
> +                               smp_mb__before_clear_bit();
> +                               clear_bit(BNAD_TXQ_FREE_SENT, &tcb->flags);
> +                   } else {
> +                               netif_stop_queue(netdev);
> +                               BNAD_UPDATE_CTR(bnad, netif_queue_stop);
> +                   }
> +
> +                   smp_mb();
> +                   /*
> +                   * Check again to deal with race condition between
> +                   * netif_stop_queue here, and netif_wake_queue in
> +                   * interrupt handler which is not inside netif tx lock.
> +                   */
> +                   if (likely
> +                       (wis > BNA_QE_FREE_CNT(tcb, tcb->q_depth) ||
> +                        vectors > BNA_QE_FREE_CNT(unmap_q, unmap_q->q_depth))) {
> +                               BNAD_UPDATE_CTR(bnad, netif_queue_stop);
> +                               return NETDEV_TX_BUSY;

The transmit routine should check for available space after
queueing to device, so you can avoid having to return
TX_BUSY.

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH 1/6] bna: Brocade 10Gb Ethernet device driver
  2010-08-04  5:15 Rasesh Mody
@ 2010-08-04 17:09 ` Stephen Hemminger
  0 siblings, 0 replies; 8+ messages in thread
From: Stephen Hemminger @ 2010-08-04 17:09 UTC (permalink / raw)
  To: Rasesh Mody; +Cc: netdev, ddutt, huangj

On Tue, 3 Aug 2010 22:15:36 -0700
Rasesh Mody <rmody@brocade.com> wrote:

> From: Rasesh Mody <rmody@brocade.com>
> 
> This is patch 1/6 which contains linux driver source for
> Brocade's BR1010/BR1020 10Gb CEE capable ethernet adapter.
> Source is based against net-next-2.6.
> 
> We wish this patch to be considered for inclusion in net-next-2.6
> 
> Signed-off-by: Rasesh Mody <rmody@brocade.com>
> ---
>  bnad.c         | 3326 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  bnad.h         |  474 ++++++++
>  bnad_ethtool.c | 1269 +++++++++++++++++++++
>  3 files changed, 5069 insertions(+)
> 
> diff -ruP net-next-2.6.35-rc1-orig/drivers/net/bna/bnad.c net-next-2.6.35-rc1-mod/drivers/net/bna/bnad.c
> --- net-next-2.6.35-rc1-orig/drivers/net/bna/bnad.c	1969-12-31 16:00:00.000000000 -0800
> +++ net-next-2.6.35-rc1-mod/drivers/net/bna/bnad.c	2010-08-02 17:19:19.447239000 -0700
> @@ -0,0 +1,3326 @@
> +/*
> + * Linux network driver for Brocade Converged Network Adapter.
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License (GPL) Version 2 as
> + * published by the Free Software Foundation
> + *
> + * This program is distributed in the hope that it will be useful, but
> + * WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * General Public License for more details.
> + */
> +/*
> + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc.
> + * All rights reserved
> + * www.brocade.com
> + */
> +#include <linux/netdevice.h>
> +#include <linux/skbuff.h>
> +#include <linux/etherdevice.h>
> +#include <linux/in.h>
> +#include <linux/ethtool.h>
> +#include <linux/if_vlan.h>
> +#include <linux/if_ether.h>
> +#include <linux/ip.h>
> +
> +#include "bnad.h"
> +#include "bna.h"
> +#include "cna.h"
> +
> +DEFINE_MUTEX(bnad_fwimg_mutex);
> +
> +/*
> + * Module params
> + */
> +static uint bnad_msix_disable;
> +module_param(bnad_msix_disable, uint, 0444);
> +MODULE_PARM_DESC(bnad_msix_disable, "Disable MSIX mode");
> +
> +static uint bnad_ioc_auto_recover = 1;
> +module_param(bnad_ioc_auto_recover, uint, 0444);
> +MODULE_PARM_DESC(bnad_ioc_auto_recover, "Enable / Disable auto recovery");
> +
> +/*
> + * Global variables
> + */
> +u32 bna_id;
> +u32 bnad_rxqs_per_cq = 2;
> +
> +DECLARE_MUTEX(bnad_list_sem);
> +LIST_HEAD(bnad_list);
> +
> +const u8 bnad_bcast_addr[] =  {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};

Surprised this isn't defined somewhere else.

> +
> +/*
> + * Local MACROS
> + */
> +#define BNAD_TX_UNMAPQ_DEPTH (bnad->txq_depth * 2)
> +
> +#define BNAD_RX_UNMAPQ_DEPTH (bnad->rxq_depth)
> +
> +#define BNAD_GET_MBOX_IRQ(_bnad)				\
> +	(((_bnad)->cfg_flags & BNAD_CF_MSIX) ?			\
> +	 ((_bnad)->msix_table[(_bnad)->msix_num - 1].vector) : 	\
> +	 ((_bnad)->pcidev->irq))
> +
> +#define BNAD_FILL_UNMAPQ_MEM_REQ(_res_info, _num, _depth)	\
> +do {								\
> +	(_res_info)->res_type = BNA_RES_T_MEM;			\
> +	(_res_info)->res_u.mem_info.mem_type = BNA_MEM_T_KVA;	\
> +	(_res_info)->res_u.mem_info.num = (_num);		\
> +	(_res_info)->res_u.mem_info.len =			\
> +	sizeof(struct bnad_unmap_q) +				\
> +	(sizeof(struct bnad_skb_unmap) * ((_depth) - 1));	\
> +} while (0)
> +
> +void
> +bnad_add_to_list(struct bnad *bnad)
> +{
> +	down(&bnad_list_sem);
> +	list_add_tail(&bnad->list_entry, &bnad_list);
> +	bna_id++;
> +	up(&bnad_list_sem);
> +}

Why do you need to list semaphore? Isn't RTNL mutex held
when this is done. If you have to have own exclusion use
a mutex for this.

> +void
> +bnad_remove_from_list(struct bnad *bnad)
> +{
> +	down(&bnad_list_sem);
> +	list_del(&bnad->list_entry);
> +	up(&bnad_list_sem);
> +}
> +
> +const struct pci_device_id bnad_pci_id_table[] = {
> +	{
> +		PCI_DEVICE(PCI_VENDOR_ID_BROCADE,
> +			PCI_DEVICE_ID_BROCADE_CT),
> +		.class = PCI_CLASS_NETWORK_ETHERNET << 8,
> +		.class_mask =  0xffff00
> +	}, {0,  }
> +};

Why is this not static?


> +/* TX */
> +/* bnad_start_xmit : Netdev entry point for Transmit */
> +/*		     Called under lock held by net_device */
> +
> +netdev_tx_t
> +bnad_start_xmit(struct sk_buff *skb, struct net_device *netdev)

Should also be static...

> +{
> +	struct bnad *bnad = netdev_priv(netdev);
> +
> +	u16 		txq_prod, vlan_tag = 0;
> +	u32 		unmap_prod, wis, wis_used, wi_range;
> +	u32 		vectors, vect_id, i, acked;
> +	u32		tx_id;
> +	int 			err;
> +
> +	struct bnad_tx_info *tx_info;
> +	struct bna_tcb *tcb;
> +	struct bnad_unmap_q *unmap_q;
> +	dma_addr_t 		dma_addr;
> +	struct bna_txq_entry *txqent;
> +	bna_txq_wi_ctrl_flag_t 	flags;
> +
> +	if (unlikely
> +	    (skb->len <= ETH_HLEN || skb->len > BFI_TX_MAX_DATA_PER_PKT)) {
> +		dev_kfree_skb(skb);
> +		return NETDEV_TX_OK;
> +	}
> +
> +	/*
> +	 * Takes care of the Tx that is scheduled between clearing the flag
> +	 * and the netif_stop_queue() call.
> +	 */
> +	if (unlikely(!test_bit(BNAD_RF_TX_STARTED, &bnad->run_flags))) {
> +		dev_kfree_skb(skb);
> +		return NETDEV_TX_OK;
> +	}
> +
> +	tx_id = BNAD_GET_TX_ID(skb);
> +
> +	tx_info = &bnad->tx_info[tx_id];
> +	tcb = tx_info->tcb[tx_id];
> +	unmap_q = tcb->unmap_q;
> +
> +	vectors = 1 + skb_shinfo(skb)->nr_frags;
> +	if (vectors > BFI_TX_MAX_VECTORS_PER_PKT) {
> +		dev_kfree_skb(skb);
> +		return NETDEV_TX_OK;
> +	}
> +	wis = BNA_TXQ_WI_NEEDED(vectors);	/* 4 vectors per work item */
> +	acked = 0;
> +	if (unlikely
> +	    (wis > BNA_QE_FREE_CNT(tcb, tcb->q_depth) ||
> +	     vectors > BNA_QE_FREE_CNT(unmap_q, unmap_q->q_depth))) {
> +		if ((u16) (*tcb->hw_consumer_index) !=
> +		    tcb->consumer_index &&
> +		    !test_and_set_bit(BNAD_TXQ_FREE_SENT, &tcb->flags)) {
> +			acked = bnad_free_txbufs(bnad, tcb);
> +			bna_ib_ack(tcb->i_dbell, acked);
> +			smp_mb__before_clear_bit();
> +			clear_bit(BNAD_TXQ_FREE_SENT, &tcb->flags);
> +		} else {
> +			netif_stop_queue(netdev);
> +			BNAD_UPDATE_CTR(bnad, netif_queue_stop);
> +		}
> +
> +		smp_mb();
> +		/*
> +		 * Check again to deal with race condition between
> +		 * netif_stop_queue here, and netif_wake_queue in
> +		 * interrupt handler which is not inside netif tx lock.
> +		 */
> +		if (likely
> +		    (wis > BNA_QE_FREE_CNT(tcb, tcb->q_depth) ||
> +		     vectors > BNA_QE_FREE_CNT(unmap_q, unmap_q->q_depth))) {
> +			BNAD_UPDATE_CTR(bnad, netif_queue_stop);
> +			return NETDEV_TX_BUSY;

The transmit routine should check for available space after
queueing to device, so you can avoid having to return
TX_BUSY.

^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH 1/6] bna: Brocade 10Gb Ethernet device driver
@ 2010-08-04  5:15 Rasesh Mody
  2010-08-04 17:09 ` Stephen Hemminger
  0 siblings, 1 reply; 8+ messages in thread
From: Rasesh Mody @ 2010-08-04  5:15 UTC (permalink / raw)
  To: netdev; +Cc: ddutt, huangj

From: Rasesh Mody <rmody@brocade.com>

This is patch 1/6 which contains linux driver source for
Brocade's BR1010/BR1020 10Gb CEE capable ethernet adapter.
Source is based against net-next-2.6.

We wish this patch to be considered for inclusion in net-next-2.6

Signed-off-by: Rasesh Mody <rmody@brocade.com>
---
 bnad.c         | 3326 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 bnad.h         |  474 ++++++++
 bnad_ethtool.c | 1269 +++++++++++++++++++++
 3 files changed, 5069 insertions(+)

diff -ruP net-next-2.6.35-rc1-orig/drivers/net/bna/bnad.c net-next-2.6.35-rc1-mod/drivers/net/bna/bnad.c
--- net-next-2.6.35-rc1-orig/drivers/net/bna/bnad.c	1969-12-31 16:00:00.000000000 -0800
+++ net-next-2.6.35-rc1-mod/drivers/net/bna/bnad.c	2010-08-02 17:19:19.447239000 -0700
@@ -0,0 +1,3326 @@
+/*
+ * Linux network driver for Brocade Converged Network Adapter.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License (GPL) Version 2 as
+ * published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+/*
+ * Copyright (c) 2005-2009 Brocade Communications Systems, Inc.
+ * All rights reserved
+ * www.brocade.com
+ */
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <linux/etherdevice.h>
+#include <linux/in.h>
+#include <linux/ethtool.h>
+#include <linux/if_vlan.h>
+#include <linux/if_ether.h>
+#include <linux/ip.h>
+
+#include "bnad.h"
+#include "bna.h"
+#include "cna.h"
+
+DEFINE_MUTEX(bnad_fwimg_mutex);
+
+/*
+ * Module params
+ */
+static uint bnad_msix_disable;
+module_param(bnad_msix_disable, uint, 0444);
+MODULE_PARM_DESC(bnad_msix_disable, "Disable MSIX mode");
+
+static uint bnad_ioc_auto_recover = 1;
+module_param(bnad_ioc_auto_recover, uint, 0444);
+MODULE_PARM_DESC(bnad_ioc_auto_recover, "Enable / Disable auto recovery");
+
+/*
+ * Global variables
+ */
+u32 bna_id;
+u32 bnad_rxqs_per_cq = 2;
+
+DECLARE_MUTEX(bnad_list_sem);
+LIST_HEAD(bnad_list);
+
+const u8 bnad_bcast_addr[] =  {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+
+/*
+ * Local MACROS
+ */
+#define BNAD_TX_UNMAPQ_DEPTH (bnad->txq_depth * 2)
+
+#define BNAD_RX_UNMAPQ_DEPTH (bnad->rxq_depth)
+
+#define BNAD_GET_MBOX_IRQ(_bnad)				\
+	(((_bnad)->cfg_flags & BNAD_CF_MSIX) ?			\
+	 ((_bnad)->msix_table[(_bnad)->msix_num - 1].vector) : 	\
+	 ((_bnad)->pcidev->irq))
+
+#define BNAD_FILL_UNMAPQ_MEM_REQ(_res_info, _num, _depth)	\
+do {								\
+	(_res_info)->res_type = BNA_RES_T_MEM;			\
+	(_res_info)->res_u.mem_info.mem_type = BNA_MEM_T_KVA;	\
+	(_res_info)->res_u.mem_info.num = (_num);		\
+	(_res_info)->res_u.mem_info.len =			\
+	sizeof(struct bnad_unmap_q) +				\
+	(sizeof(struct bnad_skb_unmap) * ((_depth) - 1));	\
+} while (0)
+
+void
+bnad_add_to_list(struct bnad *bnad)
+{
+	down(&bnad_list_sem);
+	list_add_tail(&bnad->list_entry, &bnad_list);
+	bna_id++;
+	up(&bnad_list_sem);
+}
+
+void
+bnad_remove_from_list(struct bnad *bnad)
+{
+	down(&bnad_list_sem);
+	list_del(&bnad->list_entry);
+	up(&bnad_list_sem);
+}
+
+const struct pci_device_id bnad_pci_id_table[] = {
+	{
+		PCI_DEVICE(PCI_VENDOR_ID_BROCADE,
+			PCI_DEVICE_ID_BROCADE_CT),
+		.class = PCI_CLASS_NETWORK_ETHERNET << 8,
+		.class_mask =  0xffff00
+	}, {0,  }
+};
+
+MODULE_DEVICE_TABLE(pci, bnad_pci_id_table);
+
+/*
+ * Reinitialize completions in CQ, once Rx is taken down
+ */
+static void
+bnad_cq_cmpl_init(struct bnad *bnad, struct bna_ccb *ccb)
+{
+	struct bna_cq_entry *cmpl, *next_cmpl;
+	unsigned int wi_range, wis = 0, ccb_prod = 0;
+	int i;
+
+	BNA_CQ_QPGE_PTR_GET(ccb_prod, ccb->sw_qpt, cmpl,
+			    wi_range);
+
+	for (i = 0; i < ccb->q_depth; i++) {
+		wis++;
+		if (likely(--wi_range))
+			next_cmpl = cmpl + 1;
+		else {
+			BNA_QE_INDX_ADD(ccb_prod, wis, ccb->q_depth);
+			wis = 0;
+			BNA_CQ_QPGE_PTR_GET(ccb_prod, ccb->sw_qpt,
+						next_cmpl, wi_range);
+		}
+		cmpl->valid = 0;
+		cmpl = next_cmpl;
+	}
+}
+
+/*
+ * Frees all pending Tx Bufs
+ * At this point no activity is expected on the Q,
+ * so DMA unmap & freeing is fine.
+ */
+static void
+bnad_free_all_txbufs(struct bnad *bnad,
+		 struct bna_tcb *tcb)
+{
+	u16 		unmap_cons;
+	struct bnad_unmap_q *unmap_q = tcb->unmap_q;
+	struct bnad_skb_unmap *unmap_array;
+	struct sk_buff 		*skb = NULL;
+	int			i;
+
+	unmap_array = unmap_q->unmap_array;
+
+	unmap_cons = 0;
+	while (unmap_cons < unmap_q->q_depth) {
+		skb = unmap_array[unmap_cons].skb;
+		if (!skb) {
+			unmap_cons++;
+			continue;
+		}
+		unmap_array[unmap_cons].skb = NULL;
+
+		pci_unmap_single(bnad->pcidev,
+				 pci_unmap_addr(&unmap_array[unmap_cons],
+						dma_addr), skb_headlen(skb),
+						PCI_DMA_TODEVICE);
+
+		pci_unmap_addr_set(&unmap_array[unmap_cons], dma_addr, 0);
+		unmap_cons++;
+		for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
+			pci_unmap_page(bnad->pcidev,
+				       pci_unmap_addr(&unmap_array[unmap_cons],
+						      dma_addr),
+				       skb_shinfo(skb)->frags[i].size,
+				       PCI_DMA_TODEVICE);
+			pci_unmap_addr_set(&unmap_array[unmap_cons], dma_addr,
+					   0);
+			unmap_cons++;
+		}
+		dev_kfree_skb_any(skb);
+	}
+}
+
+/* Data Path Handlers */
+
+/*
+ * bnad_free_txbufs : Frees the Tx bufs on Tx completion
+ * Can be called in a) Interrupt context
+ *		    b) Sending context
+ *		    c) Tasklet context
+ */
+static u32
+bnad_free_txbufs(struct bnad *bnad,
+		 struct bna_tcb *tcb)
+{
+	u32 		sent_packets = 0, sent_bytes = 0;
+	u16 		wis, unmap_cons, updated_hw_cons;
+	struct bnad_unmap_q *unmap_q = tcb->unmap_q;
+	struct bnad_skb_unmap *unmap_array;
+	struct sk_buff 		*skb;
+	int i;
+
+	/*
+	 * Just return if TX is stopped. This check is useful
+	 * when bnad_free_txbufs() runs out of a tasklet scheduled
+	 * before bnad_cb_tx_cleanup() cleared BNAD_RF_TX_STARTED bit
+	 * but this routine runs actually after the cleanup has been
+	 * executed.
+	 */
+	if (!test_bit(BNAD_RF_TX_STARTED, &bnad->run_flags))
+		return 0;
+
+	updated_hw_cons = *(tcb->hw_consumer_index);
+
+	wis = BNA_Q_INDEX_CHANGE(tcb->consumer_index,
+				  updated_hw_cons, tcb->q_depth);
+
+	BUG_ON(!(wis <= BNA_QE_IN_USE_CNT(tcb, tcb->q_depth)));
+
+	unmap_array = unmap_q->unmap_array;
+	unmap_cons = unmap_q->consumer_index;
+
+	prefetch(&unmap_array[unmap_cons + 1]);
+	while (wis) {
+		skb = unmap_array[unmap_cons].skb;
+
+		unmap_array[unmap_cons].skb = NULL;
+
+		sent_packets++;
+		sent_bytes += skb->len;
+		wis -= BNA_TXQ_WI_NEEDED(1 + skb_shinfo(skb)->nr_frags);
+
+		pci_unmap_single(bnad->pcidev,
+				 pci_unmap_addr(&unmap_array[unmap_cons],
+						dma_addr), skb_headlen(skb),
+				 PCI_DMA_TODEVICE);
+		pci_unmap_addr_set(&unmap_array[unmap_cons], dma_addr, 0);
+		BNA_QE_INDX_ADD(unmap_cons, 1, unmap_q->q_depth);
+
+		prefetch(&unmap_array[unmap_cons + 1]);
+		for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
+			prefetch(&unmap_array[unmap_cons + 1]);
+
+			pci_unmap_page(bnad->pcidev,
+				       pci_unmap_addr(&unmap_array[unmap_cons],
+						      dma_addr),
+				       skb_shinfo(skb)->frags[i].size,
+				       PCI_DMA_TODEVICE);
+			pci_unmap_addr_set(&unmap_array[unmap_cons], dma_addr,
+					   0);
+			BNA_QE_INDX_ADD(unmap_cons, 1, unmap_q->q_depth);
+		}
+		dev_kfree_skb_any(skb);
+	}
+
+	/* Update consumer pointers. */
+	tcb->consumer_index = updated_hw_cons;
+	unmap_q->consumer_index = unmap_cons;
+
+	tcb->txq->tx_packets += sent_packets;
+	tcb->txq->tx_bytes += sent_bytes;
+
+	return sent_packets;
+}
+
+/* Tx Free Tasklet function */
+/* Frees for all the tcb's in all the Tx's */
+/*
+ * Scheduled from sending context, so that
+ * the fat Tx lock is not held for too long
+ * in the sending context.
+ */
+static void
+bnad_tx_free_tasklet(unsigned long bnad_ptr)
+{
+	struct bnad *bnad = (struct bnad *)bnad_ptr;
+	struct bna_tcb *tcb;
+	u32 		acked;
+	int			i, j;
+
+	for (i = 0; i < bnad->num_tx; i++) {
+		for (j = 0; j < bnad->num_txq_per_tx; j++) {
+			tcb = bnad->tx_info[i].tcb[j];
+			if (!tcb)
+				continue;
+			if (((u16) (*tcb->hw_consumer_index) !=
+				tcb->consumer_index) &&
+				(!test_and_set_bit(BNAD_TXQ_FREE_SENT,
+						  &tcb->flags))) {
+				acked = bnad_free_txbufs(bnad, tcb);
+				bna_ib_ack(tcb->i_dbell, acked);
+				smp_mb__before_clear_bit();
+				clear_bit(BNAD_TXQ_FREE_SENT, &tcb->flags);
+			}
+		}
+	}
+}
+
+u32
+bnad_tx(struct bnad *bnad, struct bna_tcb *tcb)
+{
+	struct net_device *netdev = bnad->netdev;
+	u32 sent;
+
+	if (test_and_set_bit(BNAD_TXQ_FREE_SENT, &tcb->flags))
+		return 0;
+
+	sent = bnad_free_txbufs(bnad, tcb);
+	if (sent) {
+		if (netif_queue_stopped(netdev) &&
+		    netif_carrier_ok(netdev) &&
+		    BNA_QE_FREE_CNT(tcb, tcb->q_depth) >=
+				    BNAD_NETIF_WAKE_THRESHOLD) {
+			netif_wake_queue(netdev);
+			BNAD_UPDATE_CTR(bnad, netif_queue_wakeup);
+		}
+		bna_ib_ack(tcb->i_dbell, sent);
+	} else
+		bna_ib_ack(tcb->i_dbell, 0);
+
+	smp_mb__before_clear_bit();
+	clear_bit(BNAD_TXQ_FREE_SENT, &tcb->flags);
+
+	return sent;
+}
+
+/* MSIX Tx Completion Handler */
+static irqreturn_t
+bnad_msix_tx(int irq, void *data)
+{
+	struct bna_tcb *tcb = (struct bna_tcb *)data;
+	struct bnad *bnad = tcb->bnad;
+
+	bnad_tx(bnad, tcb);
+
+	return IRQ_HANDLED;
+}
+
+static void
+bnad_reset_rcb(struct bnad *bnad, struct bna_rcb *rcb)
+{
+	struct bnad_unmap_q *unmap_q = rcb->unmap_q;
+
+	rcb->producer_index = 0;
+	rcb->consumer_index = 0;
+
+	unmap_q->producer_index = 0;
+	unmap_q->consumer_index = 0;
+}
+
+static void
+bnad_free_rxbufs(struct bnad *bnad, struct bna_rcb *rcb)
+{
+	struct bnad_unmap_q *unmap_q;
+	struct sk_buff *skb;
+
+	unmap_q = rcb->unmap_q;
+	while (BNA_QE_IN_USE_CNT(unmap_q, unmap_q->q_depth)) {
+		skb = unmap_q->unmap_array[unmap_q->consumer_index].skb;
+		BUG_ON(!(skb));
+		unmap_q->unmap_array[unmap_q->consumer_index].skb = NULL;
+		pci_unmap_single(bnad->pcidev, pci_unmap_addr(&unmap_q->
+					unmap_array[unmap_q->consumer_index],
+					dma_addr), rcb->rxq->buffer_size +
+					NET_IP_ALIGN, PCI_DMA_FROMDEVICE);
+		dev_kfree_skb(skb);
+		BNA_QE_INDX_ADD(unmap_q->consumer_index, 1, unmap_q->q_depth);
+		BNA_QE_INDX_ADD(rcb->consumer_index, 1, rcb->q_depth);
+	}
+
+	bnad_reset_rcb(bnad, rcb);
+}
+
+static void
+bnad_alloc_n_post_rxbufs(struct bnad *bnad, struct bna_rcb *rcb)
+{
+	u16 to_alloc, alloced, unmap_prod, wi_range;
+	struct bnad_unmap_q *unmap_q = rcb->unmap_q;
+	struct bnad_skb_unmap *unmap_array;
+	struct bna_rxq_entry *rxent;
+	struct sk_buff *skb;
+	dma_addr_t dma_addr;
+
+	alloced = 0;
+	to_alloc =
+		BNA_QE_FREE_CNT(unmap_q, unmap_q->q_depth);
+
+	unmap_array = unmap_q->unmap_array;
+	unmap_prod = unmap_q->producer_index;
+
+	BNA_RXQ_QPGE_PTR_GET(unmap_prod, rcb->sw_qpt, rxent, wi_range);
+
+	while (to_alloc--) {
+		if (!wi_range) {
+			BNA_RXQ_QPGE_PTR_GET(unmap_prod, rcb->sw_qpt, rxent,
+					     wi_range);
+		}
+		skb = alloc_skb(rcb->rxq->buffer_size + NET_IP_ALIGN,
+				     GFP_ATOMIC);
+		if (unlikely(!skb)) {
+			BNAD_UPDATE_CTR(bnad, rxbuf_alloc_failed);
+			goto finishing;
+		}
+		skb->dev = bnad->netdev;
+		skb_reserve(skb, NET_IP_ALIGN);
+		unmap_array[unmap_prod].skb = skb;
+		dma_addr = pci_map_single(bnad->pcidev, skb->data,
+			rcb->rxq->buffer_size, PCI_DMA_FROMDEVICE);
+		pci_unmap_addr_set(&unmap_array[unmap_prod], dma_addr,
+				   dma_addr);
+		BNA_SET_DMA_ADDR(dma_addr, &rxent->host_addr);
+		BNA_QE_INDX_ADD(unmap_prod, 1, unmap_q->q_depth);
+
+		rxent++;
+		wi_range--;
+		alloced++;
+	}
+
+finishing:
+	if (likely(alloced)) {
+		unmap_q->producer_index = unmap_prod;
+		rcb->producer_index = unmap_prod;
+		smp_mb();
+		bna_rxq_prod_indx_doorbell(rcb);
+	}
+}
+
+/* Enable disable Tx / Rx IRQs */
+void
+bnad_disable_txrx_irqs(struct bnad *bnad)
+{
+	struct bna_tcb *tcb;
+	struct bna_ccb *ccb;
+	int i, j;
+
+	for (i = 0; i < bnad->num_tx; i++) {
+		for (j = 0; j < bnad->num_txq_per_tx; j++) {
+			tcb = bnad->tx_info[i].tcb[j];
+			bna_ib_coalescing_timer_set(tcb->i_dbell, 0);
+			bna_ib_ack(tcb->i_dbell, 0);
+		}
+	}
+
+	for (i = 0; i < bnad->num_rx; i++) {
+		for (j = 0; j < bnad->num_rxp_per_rx; j++) {
+			ccb = bnad->rx_info[i].rx_ctrl[j].ccb;
+			bnad_disable_rx_irq(bnad, ccb);
+		}
+	}
+}
+
+/*
+ * Locking is required in the enable path
+ * because it is called from a napi poll
+ * context, where the bna_lock is not held
+ * unlike the IRQ context.
+ */
+void
+bnad_enable_txrx_irqs(struct bnad *bnad)
+{
+	struct bna_tcb *tcb;
+	struct bna_ccb *ccb;
+	int i, j;
+
+	spin_lock_irq(&bnad->bna_lock);
+	for (i = 0; i < bnad->num_tx; i++) {
+		for (j = 0; j < bnad->num_txq_per_tx; j++) {
+			tcb = bnad->tx_info[i].tcb[j];
+			bna_ib_coalescing_timer_set(tcb->i_dbell,
+				tcb->txq->ib->ib_config.coalescing_timeo);
+			bna_ib_ack(tcb->i_dbell, 0);
+		}
+	}
+
+	for (i = 0; i < bnad->num_rx; i++) {
+		for (j = 0; j < bnad->num_rxp_per_rx; j++) {
+			ccb = bnad->rx_info[i].rx_ctrl[j].ccb;
+			bnad_enable_rx_irq_unsafe(ccb);
+		}
+	}
+	spin_unlock_irq(&bnad->bna_lock);
+}
+
+static inline void
+bnad_refill_rxq(struct bnad *bnad, struct bna_rcb *rcb)
+{
+	struct bnad_unmap_q *unmap_q = rcb->unmap_q;
+
+	if (!test_and_set_bit(BNAD_RXQ_REFILL, &rcb->flags)) {
+		if (BNA_QE_FREE_CNT(unmap_q, unmap_q->q_depth)
+			 >> BNAD_RXQ_REFILL_THRESHOLD_SHIFT)
+			bnad_alloc_n_post_rxbufs(bnad, rcb);
+		smp_mb__before_clear_bit();
+		clear_bit(BNAD_RXQ_REFILL, &rcb->flags);
+	}
+}
+
+u32
+bnad_poll_cq(struct bnad *bnad, struct bna_ccb *ccb, int budget)
+{
+	struct bna_cq_entry *cmpl, *next_cmpl;
+	struct bna_rcb *rcb = NULL;
+	unsigned int wi_range, packets = 0, wis = 0;
+	struct bnad_unmap_q *unmap_q;
+	struct sk_buff *skb;
+	u32 flags;
+	u32 qid0 = ccb->rcb[0]->rxq->rxq_id;
+	struct bna_pkt_rate *pkt_rt = &ccb->pkt_rate;
+
+	prefetch(bnad->netdev);
+	BNA_CQ_QPGE_PTR_GET(ccb->producer_index, ccb->sw_qpt, cmpl,
+			    wi_range);
+	BUG_ON(!(wi_range <= ccb->q_depth));
+	while (cmpl->valid && packets < budget) {
+		packets++;
+		BNA_UPDATE_PKT_CNT(pkt_rt, ntohs(cmpl->length));
+
+		if (qid0 == cmpl->rxq_id)
+			rcb = ccb->rcb[0];
+		else
+			rcb = ccb->rcb[1];
+
+		unmap_q = rcb->unmap_q;
+
+		skb = unmap_q->unmap_array[unmap_q->consumer_index].skb;
+		BUG_ON(!(skb));
+		prefetch(skb->data - NET_IP_ALIGN);
+		unmap_q->unmap_array[unmap_q->consumer_index].skb = NULL;
+		pci_unmap_single(bnad->pcidev,
+				 pci_unmap_addr(&unmap_q->
+						unmap_array[unmap_q->
+							    consumer_index],
+						dma_addr),
+						rcb->rxq->buffer_size,
+						PCI_DMA_FROMDEVICE);
+		BNA_QE_INDX_ADD(unmap_q->consumer_index, 1, unmap_q->q_depth);
+
+		/* Should be more efficient ? Performance ? */
+		BNA_QE_INDX_ADD(rcb->consumer_index, 1, rcb->q_depth);
+
+		wis++;
+		if (likely(--wi_range))
+			next_cmpl = cmpl + 1;
+		else {
+			BNA_QE_INDX_ADD(ccb->producer_index, wis, ccb->q_depth);
+			wis = 0;
+			BNA_CQ_QPGE_PTR_GET(ccb->producer_index, ccb->sw_qpt,
+						next_cmpl, wi_range);
+			BUG_ON(!(wi_range <= ccb->q_depth));
+		}
+		prefetch(next_cmpl);
+
+		flags = ntohl(cmpl->flags);
+		if (unlikely
+		    (flags &
+		     (BNA_CQ_EF_MAC_ERROR | BNA_CQ_EF_FCS_ERROR |
+		      BNA_CQ_EF_TOO_LONG))) {
+			dev_kfree_skb_any(skb);
+			rcb->rxq->rx_packets_with_error++;
+			goto next;
+		}
+
+		skb_put(skb, ntohs(cmpl->length));
+		if (likely
+		    (bnad->rx_csum &&
+		     (((flags & BNA_CQ_EF_IPV4) &&
+		      (flags & BNA_CQ_EF_L3_CKSUM_OK)) ||
+		      (flags & BNA_CQ_EF_IPV6)) &&
+		      (flags & (BNA_CQ_EF_TCP | BNA_CQ_EF_UDP)) &&
+		      (flags & BNA_CQ_EF_L4_CKSUM_OK)))
+			skb->ip_summed = CHECKSUM_UNNECESSARY;
+		else
+			skb->ip_summed = CHECKSUM_NONE;
+
+		rcb->rxq->rx_packets++;
+		rcb->rxq->rx_bytes += skb->len;
+		skb->protocol = eth_type_trans(skb, bnad->netdev);
+
+		if (bnad->vlan_grp && (flags & BNA_CQ_EF_VLAN)) {
+			bnad_rcv_skb_vlan_stripped(ccb, bnad, skb, cmpl);
+
+		} else 	/* Not VLAN tagged/stripped */
+			bnad_rcv_skb(ccb, bnad, skb, cmpl);
+
+next:
+		cmpl->valid = 0;
+		cmpl = next_cmpl;
+	}
+
+	BNA_QE_INDX_ADD(ccb->producer_index, wis, ccb->q_depth);
+
+	if (likely(ccb)) {
+		bna_ib_ack(ccb->i_dbell, packets);
+		bnad_refill_rxq(bnad, ccb->rcb[0]);
+		if (ccb->rcb[1])
+			bnad_refill_rxq(bnad, ccb->rcb[1]);
+	} else
+		bna_ib_ack(ccb->i_dbell, 0);
+
+	return packets;
+}
+
+/* MSIX Rx Path Handler */
+static irqreturn_t
+bnad_msix_rx(int irq, void *data)
+{
+	struct bna_ccb *ccb = (struct bna_ccb *)data;
+	struct bnad *bnad = ccb->bnad;
+
+	bnad_netif_rx_schedule_poll(bnad, ccb);
+
+	return IRQ_HANDLED;
+}
+
+/* TX */
+/* bnad_start_xmit : Netdev entry point for Transmit */
+/*		     Called under lock held by net_device */
+
+netdev_tx_t
+bnad_start_xmit(struct sk_buff *skb, struct net_device *netdev)
+{
+	struct bnad *bnad = netdev_priv(netdev);
+
+	u16 		txq_prod, vlan_tag = 0;
+	u32 		unmap_prod, wis, wis_used, wi_range;
+	u32 		vectors, vect_id, i, acked;
+	u32		tx_id;
+	int 			err;
+
+	struct bnad_tx_info *tx_info;
+	struct bna_tcb *tcb;
+	struct bnad_unmap_q *unmap_q;
+	dma_addr_t 		dma_addr;
+	struct bna_txq_entry *txqent;
+	bna_txq_wi_ctrl_flag_t 	flags;
+
+	if (unlikely
+	    (skb->len <= ETH_HLEN || skb->len > BFI_TX_MAX_DATA_PER_PKT)) {
+		dev_kfree_skb(skb);
+		return NETDEV_TX_OK;
+	}
+
+	/*
+	 * Takes care of the Tx that is scheduled between clearing the flag
+	 * and the netif_stop_queue() call.
+	 */
+	if (unlikely(!test_bit(BNAD_RF_TX_STARTED, &bnad->run_flags))) {
+		dev_kfree_skb(skb);
+		return NETDEV_TX_OK;
+	}
+
+	tx_id = BNAD_GET_TX_ID(skb);
+
+	tx_info = &bnad->tx_info[tx_id];
+	tcb = tx_info->tcb[tx_id];
+	unmap_q = tcb->unmap_q;
+
+	vectors = 1 + skb_shinfo(skb)->nr_frags;
+	if (vectors > BFI_TX_MAX_VECTORS_PER_PKT) {
+		dev_kfree_skb(skb);
+		return NETDEV_TX_OK;
+	}
+	wis = BNA_TXQ_WI_NEEDED(vectors);	/* 4 vectors per work item */
+	acked = 0;
+	if (unlikely
+	    (wis > BNA_QE_FREE_CNT(tcb, tcb->q_depth) ||
+	     vectors > BNA_QE_FREE_CNT(unmap_q, unmap_q->q_depth))) {
+		if ((u16) (*tcb->hw_consumer_index) !=
+		    tcb->consumer_index &&
+		    !test_and_set_bit(BNAD_TXQ_FREE_SENT, &tcb->flags)) {
+			acked = bnad_free_txbufs(bnad, tcb);
+			bna_ib_ack(tcb->i_dbell, acked);
+			smp_mb__before_clear_bit();
+			clear_bit(BNAD_TXQ_FREE_SENT, &tcb->flags);
+		} else {
+			netif_stop_queue(netdev);
+			BNAD_UPDATE_CTR(bnad, netif_queue_stop);
+		}
+
+		smp_mb();
+		/*
+		 * Check again to deal with race condition between
+		 * netif_stop_queue here, and netif_wake_queue in
+		 * interrupt handler which is not inside netif tx lock.
+		 */
+		if (likely
+		    (wis > BNA_QE_FREE_CNT(tcb, tcb->q_depth) ||
+		     vectors > BNA_QE_FREE_CNT(unmap_q, unmap_q->q_depth))) {
+			BNAD_UPDATE_CTR(bnad, netif_queue_stop);
+			return NETDEV_TX_BUSY;
+		} else {
+			netif_wake_queue(netdev);
+			BNAD_UPDATE_CTR(bnad, netif_queue_wakeup);
+		}
+	}
+
+	unmap_prod = unmap_q->producer_index;
+	wis_used = 1;
+	vect_id = 0;
+	flags = 0;
+
+	txq_prod = tcb->producer_index;
+	BNA_TXQ_QPGE_PTR_GET(txq_prod, tcb->sw_qpt, txqent, wi_range);
+	BUG_ON(!(wi_range <= tcb->q_depth));
+	txqent->hdr.wi.reserved = 0;
+	txqent->hdr.wi.num_vectors = vectors;
+	txqent->hdr.wi.opcode =
+		htons((skb_is_gso(skb) ? BNA_TXQ_WI_SEND_LSO :
+		       BNA_TXQ_WI_SEND));
+
+	if (bnad->vlan_grp && vlan_tx_tag_present(skb)) {
+		vlan_tag = (u16) vlan_tx_tag_get(skb);
+		flags |= (BNA_TXQ_WI_CF_INS_PRIO | BNA_TXQ_WI_CF_INS_VLAN);
+	}
+	if (test_bit(BNAD_RF_CEE_RUNNING, &bnad->run_flags)) {
+		vlan_tag =
+			(tcb->priority & 0x7) << 13 | (vlan_tag & 0x1fff);
+		flags |= (BNA_TXQ_WI_CF_INS_PRIO | BNA_TXQ_WI_CF_INS_VLAN);
+	}
+
+	txqent->hdr.wi.vlan_tag = htons(vlan_tag);
+
+	if (skb_is_gso(skb)) {
+		err = bnad_tso_prepare(bnad, skb);
+		if (err) {
+			dev_kfree_skb(skb);
+			return NETDEV_TX_OK;
+		}
+		txqent->hdr.wi.lso_mss = htons(skb_is_gso(skb));
+		flags |= (BNA_TXQ_WI_CF_IP_CKSUM | BNA_TXQ_WI_CF_TCP_CKSUM);
+		txqent->hdr.wi.l4_hdr_size_n_offset =
+			htons(BNA_TXQ_WI_L4_HDR_N_OFFSET
+			      (tcp_hdrlen(skb) >> 2,
+			       skb_transport_offset(skb)));
+	} else if (skb->ip_summed == CHECKSUM_PARTIAL) {
+		u8 proto = 0;
+
+		txqent->hdr.wi.lso_mss = 0;
+
+		if (skb->protocol == htons(ETH_P_IP))
+			proto = ip_hdr(skb)->protocol;
+		else if (skb->protocol == htons(ETH_P_IPV6)) {
+			/* nexthdr may not be TCP immediately. */
+			proto = ipv6_hdr(skb)->nexthdr;
+		}
+		if (proto == IPPROTO_TCP) {
+			flags |= BNA_TXQ_WI_CF_TCP_CKSUM;
+			txqent->hdr.wi.l4_hdr_size_n_offset =
+				htons(BNA_TXQ_WI_L4_HDR_N_OFFSET
+				      (0, skb_transport_offset(skb)));
+
+			BNAD_UPDATE_CTR(bnad, tcpcsum_offload);
+
+			BUG_ON(!(skb_headlen(skb) >=
+				skb_transport_offset(skb) + tcp_hdrlen(skb)));
+
+		} else if (proto == IPPROTO_UDP) {
+			flags |= BNA_TXQ_WI_CF_UDP_CKSUM;
+			txqent->hdr.wi.l4_hdr_size_n_offset =
+				htons(BNA_TXQ_WI_L4_HDR_N_OFFSET
+				      (0, skb_transport_offset(skb)));
+
+			BNAD_UPDATE_CTR(bnad, udpcsum_offload);
+
+			BUG_ON(!(skb_headlen(skb) >=
+				   skb_transport_offset(skb) +
+				   sizeof(struct udphdr)));
+		} else {
+			err = skb_checksum_help(skb);
+			BNAD_UPDATE_CTR(bnad, csum_help);
+			if (err) {
+				dev_kfree_skb(skb);
+				BNAD_UPDATE_CTR(bnad, csum_help_err);
+				return NETDEV_TX_OK;
+			}
+		}
+	} else {
+		txqent->hdr.wi.lso_mss = 0;
+		txqent->hdr.wi.l4_hdr_size_n_offset = 0;
+	}
+
+	txqent->hdr.wi.flags = htons(flags);
+
+	txqent->hdr.wi.frame_length = htonl(skb->len);
+
+	unmap_q->unmap_array[unmap_prod].skb = skb;
+	BUG_ON(!(skb_headlen(skb) <= BFI_TX_MAX_DATA_PER_VECTOR));
+	txqent->vector[vect_id].length = htons(skb_headlen(skb));
+	dma_addr = pci_map_single(bnad->pcidev, skb->data, skb_headlen(skb),
+		PCI_DMA_TODEVICE);
+	pci_unmap_addr_set(&unmap_q->unmap_array[unmap_prod], dma_addr,
+			   dma_addr);
+
+	BNA_SET_DMA_ADDR(dma_addr, &txqent->vector[vect_id].host_addr);
+	BNA_QE_INDX_ADD(unmap_prod, 1, unmap_q->q_depth);
+
+	for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
+		struct skb_frag_struct *frag = &skb_shinfo(skb)->frags[i];
+		u32		size = frag->size;
+
+		if (++vect_id == BFI_TX_MAX_VECTORS_PER_WI) {
+			vect_id = 0;
+			if (--wi_range)
+				txqent++;
+			else {
+				BNA_QE_INDX_ADD(txq_prod, wis_used,
+						tcb->q_depth);
+				wis_used = 0;
+				BNA_TXQ_QPGE_PTR_GET(txq_prod, tcb->sw_qpt,
+						     txqent, wi_range);
+				BUG_ON(!(wi_range <= tcb->q_depth));
+			}
+			wis_used++;
+			txqent->hdr.wi_ext.opcode = htons(BNA_TXQ_WI_EXTENSION);
+		}
+
+		BUG_ON(!(size <= BFI_TX_MAX_DATA_PER_VECTOR));
+		txqent->vector[vect_id].length = htons(size);
+		dma_addr =
+			pci_map_page(bnad->pcidev, frag->page,
+				     frag->page_offset, size,
+				     PCI_DMA_TODEVICE);
+		pci_unmap_addr_set(&unmap_q->unmap_array[unmap_prod], dma_addr,
+				   dma_addr);
+		BNA_SET_DMA_ADDR(dma_addr, &txqent->vector[vect_id].host_addr);
+		BNA_QE_INDX_ADD(unmap_prod, 1, unmap_q->q_depth);
+	}
+
+	unmap_q->producer_index = unmap_prod;
+	BNA_QE_INDX_ADD(txq_prod, wis_used, tcb->q_depth);
+	tcb->producer_index = txq_prod;
+
+	smp_mb();
+	bna_txq_prod_indx_doorbell(tcb);
+
+	if ((u16) (*tcb->hw_consumer_index) != tcb->consumer_index)
+		tasklet_schedule(&bnad->tx_free_tasklet);
+
+	return NETDEV_TX_OK;
+}
+
+/* Interrupt handlers */
+
+/* Mbox Interrupt Handlers */
+static irqreturn_t
+bnad_msix_mbox_handler(int irq, void *data)
+{
+	u32 intr_status;
+	unsigned long  flags;
+	struct net_device *netdev = data;
+	struct bnad *bnad;
+
+	bnad = netdev_priv(netdev);
+
+	/* BNA_ISR_GET(bnad); Inc Ref count */
+	spin_lock_irqsave(&bnad->bna_lock, flags);
+
+	bna_intr_status_get(&bnad->bna, intr_status);
+
+	if (BNA_IS_MBOX_ERR_INTR(intr_status))
+		bna_mbox_handler(&bnad->bna, intr_status);
+
+	spin_unlock_irqrestore(&bnad->bna_lock, flags);
+
+	/* BNAD_ISR_PUT(bnad); Dec Ref count */
+	return IRQ_HANDLED;
+}
+
+irqreturn_t
+bnad_isr(int irq, void *data)
+{
+	int i, j;
+	u32 intr_status;
+	unsigned long flags;
+	struct net_device *netdev = data;
+	struct bnad *bnad = netdev_priv(netdev);
+	struct bnad_rx_info *rx_info;
+	struct bnad_rx_ctrl *rx_ctrl;
+
+	spin_lock_irqsave(&bnad->bna_lock, flags);
+
+	bna_intr_status_get(&bnad->bna, intr_status);
+	if (!intr_status) {
+		spin_unlock_irqrestore(&bnad->bna_lock, flags);
+		return IRQ_NONE;
+	}
+
+	if (BNA_IS_MBOX_ERR_INTR(intr_status)) {
+		bna_mbox_handler(&bnad->bna, intr_status);
+		if (!BNA_IS_INTX_DATA_INTR(intr_status)) {
+			spin_unlock_irqrestore(&bnad->bna_lock, flags);
+			goto done;
+		}
+	}
+	spin_unlock_irqrestore(&bnad->bna_lock, flags);
+
+	/* Process data interrupts */
+	for (i = 0; i < bnad->num_rx; i++) {
+		rx_info = &bnad->rx_info[i];
+		if (!rx_info->rx)
+			continue;
+		for (j = 0; j < bnad->num_rxp_per_rx; j++) {
+			rx_ctrl = &rx_info->rx_ctrl[j];
+			if (rx_ctrl->ccb)
+				bnad_netif_rx_schedule_poll(bnad,
+							    rx_ctrl->ccb);
+		}
+	}
+done:
+	return IRQ_HANDLED;
+}
+
+/* Interrupt enable / disable */
+static void
+bnad_enable_msix(struct bnad *bnad)
+{
+	int i, ret;
+	u32 tot_msix_num;
+
+	spin_lock_irq(&bnad->bna_lock);
+	if (!(bnad->cfg_flags & BNAD_CF_MSIX)) {
+		spin_unlock_irq(&bnad->bna_lock);
+		return;
+	}
+	spin_unlock_irq(&bnad->bna_lock);
+
+	if (bnad->msix_table)
+		return;
+
+	tot_msix_num = bnad->msix_num + bnad->msix_diag_num;
+
+	bnad->msix_table =
+		kcalloc(tot_msix_num, sizeof(struct msix_entry), GFP_KERNEL);
+
+	if (!bnad->msix_table)
+		goto intx_mode;
+
+	for (i = 0; i < tot_msix_num; i++)
+		bnad->msix_table[i].entry = i;
+
+	ret = pci_enable_msix(bnad->pcidev, bnad->msix_table, tot_msix_num);
+	if (ret > 0) {
+		/* Not enough MSI-X vectors. */
+
+		spin_lock_irq(&bnad->bna_lock);
+		/* ret = #of vectors that we got */
+		bnad_q_num_adjust(bnad, ret);
+		spin_unlock_irq(&bnad->bna_lock);
+
+		bnad->msix_num = (bnad->num_tx * bnad->num_txq_per_tx)
+			+ (bnad->num_rx
+			* bnad->num_rxp_per_rx) +
+			 BNAD_MAILBOX_MSIX_VECTORS;
+		tot_msix_num = bnad->msix_num + bnad->msix_diag_num;
+
+		/* Try once more with adjusted numbers */
+		/* If this fails, fall back to INTx */
+		ret = pci_enable_msix(bnad->pcidev, bnad->msix_table,
+				      tot_msix_num);
+		if (ret)
+			goto intx_mode;
+
+	} else if (ret < 0)
+		goto intx_mode;
+	return;
+
+intx_mode:
+
+	kfree(bnad->msix_table);
+	bnad->msix_table = NULL;
+	bnad->msix_num = 0;
+	bnad->msix_diag_num = 0;
+	spin_lock_irq(&bnad->bna_lock);
+	bnad->cfg_flags &= ~BNAD_CF_MSIX;
+	bnad_q_num_init(bnad);
+	spin_unlock_irq(&bnad->bna_lock);
+}
+
+static void
+bnad_disable_msix(struct bnad *bnad)
+{
+	u32 cfg_flags;
+
+	spin_lock_irq(&bnad->bna_lock);
+	cfg_flags = bnad->cfg_flags;
+	if (bnad->cfg_flags & BNAD_CF_MSIX)
+		bnad->cfg_flags &= ~BNAD_CF_MSIX;
+	spin_unlock_irq(&bnad->bna_lock);
+
+	if (cfg_flags & BNAD_CF_MSIX) {
+		pci_disable_msix(bnad->pcidev);
+		kfree(bnad->msix_table);
+		bnad->msix_table = NULL;
+	}
+}
+
+/*
+ * Called in interrupt / callback context
+ * with bna_lock held, so cfg_flags access is OK
+ */
+void
+bnad_enable_mbox_irq(struct bnad *bnad)
+{
+	int irq = BNAD_GET_MBOX_IRQ(bnad);
+
+	if (!(bnad->cfg_flags & BNAD_CF_MSIX))
+		return;
+
+	if (test_and_clear_bit(BNAD_RF_MBOX_IRQ_DISABLED, &bnad->run_flags))
+		enable_irq(irq);
+	BNAD_UPDATE_CTR(bnad, mbox_intr_enabled);
+}
+
+/*
+ * Called with bnad->bna_lock held b'cos of
+ * bnad->cfg_flags access.
+ */
+void
+bnad_disable_mbox_irq(struct bnad *bnad)
+{
+	int irq = BNAD_GET_MBOX_IRQ(bnad);
+
+	if (!(bnad->cfg_flags & BNAD_CF_MSIX))
+		return;
+
+	if (!test_and_set_bit(BNAD_RF_MBOX_IRQ_DISABLED, &bnad->run_flags))
+		disable_irq_nosync(irq);
+	BNAD_UPDATE_CTR(bnad, mbox_intr_disabled);
+}
+
+/* Control Path Handlers */
+
+/* Callbacks */
+void
+bnad_cb_device_enable_mbox_intr(struct bnad *bnad)
+{
+	bnad_enable_mbox_irq(bnad);
+}
+
+void
+bnad_cb_device_disable_mbox_intr(struct bnad *bnad)
+{
+	bnad_disable_mbox_irq(bnad);
+}
+
+void
+bnad_cb_device_enabled(struct bnad *bnad, enum bna_cb_status status)
+{
+	complete(&bnad->bnad_completions.ioc_comp);
+	bnad->bnad_completions.ioc_comp_status = status;
+}
+
+void
+bnad_cb_device_disabled(struct bnad *bnad, enum bna_cb_status status)
+{
+	complete(&bnad->bnad_completions.ioc_comp);
+	bnad->bnad_completions.ioc_comp_status = status;
+}
+
+void
+bnad_cb_port_disabled(void *arg, enum bna_cb_status status)
+{
+	struct bnad *bnad = (struct bnad *)arg;
+
+	complete(&bnad->bnad_completions.port_comp);
+
+	netif_carrier_off(bnad->netdev);
+}
+
+void
+bnad_cb_port_link_status(struct bnad *bnad,
+			enum bna_link_status link_status)
+{
+	bool link_up = 0;
+
+	link_up = (link_status == BNA_LINK_UP) || (link_status == BNA_CEE_UP);
+
+	if (link_status == BNA_CEE_UP) {
+		set_bit(BNAD_RF_CEE_RUNNING, &bnad->run_flags);
+		BNAD_UPDATE_CTR(bnad, cee_up);
+	} else
+		clear_bit(BNAD_RF_CEE_RUNNING, &bnad->run_flags);
+
+	if (link_up) {
+		if (!netif_carrier_ok(bnad->netdev)) {
+			printk(KERN_WARNING "bna: %s link up\n",
+				bnad->netdev->name);
+			netif_carrier_on(bnad->netdev);
+			BNAD_UPDATE_CTR(bnad, link_toggle);
+			if (test_bit(BNAD_RF_TX_STARTED, &bnad->run_flags)) {
+				/* Force an immediate Transmit Schedule */
+				printk(KERN_INFO "bna: %s TX_STARTED\n",
+					bnad->netdev->name);
+				netif_wake_queue(bnad->netdev);
+				BNAD_UPDATE_CTR(bnad, netif_queue_wakeup);
+			} else {
+				netif_stop_queue(bnad->netdev);
+				BNAD_UPDATE_CTR(bnad, netif_queue_stop);
+			}
+		}
+	} else {
+		if (netif_carrier_ok(bnad->netdev)) {
+			printk(KERN_WARNING "bna: %s link down\n",
+				bnad->netdev->name);
+			netif_carrier_off(bnad->netdev);
+			BNAD_UPDATE_CTR(bnad, link_toggle);
+		}
+	}
+}
+
+void
+bnad_cb_tx_disabled(void *arg, struct bna_tx *tx,
+			enum bna_cb_status status)
+{
+	struct bnad *bnad = (struct bnad *)arg;
+
+	complete(&bnad->bnad_completions.tx_comp);
+}
+
+void
+bnad_cb_tcb_setup(struct bnad *bnad, struct bna_tcb *tcb)
+{
+	struct bnad_tx_info *tx_info =
+			(struct bnad_tx_info *)tcb->txq->tx->priv;
+	struct bnad_unmap_q *unmap_q = tcb->unmap_q;
+
+	tx_info->tcb[tcb->id] = tcb;
+	unmap_q->producer_index = 0;
+	unmap_q->consumer_index = 0;
+	unmap_q->q_depth = BNAD_TX_UNMAPQ_DEPTH;
+}
+
+void
+bnad_cb_tcb_destroy(struct bnad *bnad, struct bna_tcb *tcb)
+{
+	struct bnad_tx_info *tx_info =
+			(struct bnad_tx_info *)tcb->txq->tx->priv;
+
+	tx_info->tcb[tcb->id] = NULL;
+}
+
+void
+bnad_cb_rcb_setup(struct bnad *bnad, struct bna_rcb *rcb)
+{
+	struct bnad_unmap_q *unmap_q = rcb->unmap_q;
+
+	unmap_q->producer_index = 0;
+	unmap_q->consumer_index = 0;
+	unmap_q->q_depth = BNAD_RX_UNMAPQ_DEPTH;
+}
+
+void
+bnad_cb_ccb_setup(struct bnad *bnad, struct bna_ccb *ccb)
+{
+	struct bnad_rx_info *rx_info =
+			(struct bnad_rx_info *)ccb->cq->rx->priv;
+
+	rx_info->rx_ctrl[ccb->id].ccb = ccb;
+	ccb->ctrl = &rx_info->rx_ctrl[ccb->id];
+}
+
+void
+bnad_cb_ccb_destroy(struct bnad *bnad, struct bna_ccb *ccb)
+{
+	struct bnad_rx_info *rx_info =
+			(struct bnad_rx_info *)ccb->cq->rx->priv;
+
+	rx_info->rx_ctrl[ccb->id].ccb = NULL;
+}
+
+void
+bnad_cb_tx_stall(struct bnad *bnad, struct bna_tcb *tcb)
+{
+	struct bnad_tx_info *tx_info =
+			(struct bnad_tx_info *)tcb->txq->tx->priv;
+
+	if (tx_info != &bnad->tx_info[0])
+		return;
+
+	clear_bit(BNAD_RF_TX_STARTED, &bnad->run_flags);
+	netif_stop_queue(bnad->netdev);
+	printk(KERN_INFO "bna: %s TX_STOPPED\n", bnad->netdev->name);
+}
+
+void
+bnad_cb_tx_resume(struct bnad *bnad, struct bna_tcb *tcb)
+{
+	if (test_and_set_bit(BNAD_RF_TX_STARTED, &bnad->run_flags))
+		return;
+
+	if (netif_carrier_ok(bnad->netdev)) {
+		printk(KERN_INFO "bna: %s TX_STARTED\n", bnad->netdev->name);
+		netif_wake_queue(bnad->netdev);
+		BNAD_UPDATE_CTR(bnad, netif_queue_wakeup);
+	}
+}
+
+void
+bnad_cb_tx_cleanup(struct bnad *bnad, struct bna_tcb *tcb)
+{
+	struct bnad_unmap_q *unmap_q = tcb->unmap_q;
+
+	if (!tcb || (!tcb->unmap_q))
+		return;
+
+	if (!unmap_q->unmap_array)
+		return;
+
+	if (test_and_set_bit(BNAD_TXQ_FREE_SENT, &tcb->flags))
+		return;
+
+	bnad_free_all_txbufs(bnad, tcb);
+
+	unmap_q->producer_index = 0;
+	unmap_q->consumer_index = 0;
+
+	smp_mb__before_clear_bit();
+	clear_bit(BNAD_TXQ_FREE_SENT, &tcb->flags);
+}
+
+void
+bnad_cb_rx_cleanup(struct bnad *bnad,
+			struct bna_ccb *ccb)
+{
+	bnad_cq_cmpl_init(bnad, ccb);
+
+	bnad_free_rxbufs(bnad, ccb->rcb[0]);
+	clear_bit(BNAD_RXQ_STARTED, &ccb->rcb[0]->flags);
+
+	if (ccb->rcb[1]) {
+		bnad_free_rxbufs(bnad, ccb->rcb[1]);
+		clear_bit(BNAD_RXQ_STARTED, &ccb->rcb[1]->flags);
+	}
+}
+
+void
+bnad_cb_rx_post(struct bnad *bnad, struct bna_rcb *rcb)
+{
+	struct bnad_unmap_q *unmap_q = rcb->unmap_q;
+
+	set_bit(BNAD_RXQ_STARTED, &rcb->flags);
+
+	/* Now allocate & post buffers for this RCB */
+	/* !!Allocation in callback context */
+	if (!test_and_set_bit(BNAD_RXQ_REFILL, &rcb->flags)) {
+		if (BNA_QE_FREE_CNT(unmap_q, unmap_q->q_depth)
+			 >> BNAD_RXQ_REFILL_THRESHOLD_SHIFT)
+			bnad_alloc_n_post_rxbufs(bnad, rcb);
+		smp_mb__before_clear_bit();
+		clear_bit(BNAD_RXQ_REFILL, &rcb->flags);
+	}
+}
+
+void
+bnad_cb_rx_disabled(void *arg, struct bna_rx *rx,
+			enum bna_cb_status status)
+{
+	struct bnad *bnad = (struct bnad *)arg;
+
+	complete(&bnad->bnad_completions.rx_comp);
+}
+
+void
+bnad_cb_rx_ucast_add(struct bnad *bnad, struct bna_rx *rx,
+				enum bna_cb_status status)
+{
+	bnad->bnad_completions.ucast_comp_status = status;
+	complete(&bnad->bnad_completions.ucast_comp);
+}
+
+void
+bnad_cb_rx_ucast_del(struct bnad *bnad, struct bna_rx *rx,
+				enum bna_cb_status status)
+{
+	bnad->bnad_completions.ucast_comp_status = status;
+	complete(&bnad->bnad_completions.ucast_comp);
+}
+
+void
+bnad_cb_rx_mcast_add(struct bnad *bnad, struct bna_rx *rx,
+				enum bna_cb_status status)
+{
+	bnad->bnad_completions.mcast_comp_status = status;
+	complete(&bnad->bnad_completions.mcast_comp);
+}
+
+void
+bnad_cb_stats_get(struct bnad *bnad, enum bna_cb_status status,
+		       struct bna_stats *stats)
+{
+	if (status == BNA_CB_SUCCESS)
+		BNAD_UPDATE_CTR(bnad, hw_stats_updates);
+
+	if (!netif_running(bnad->netdev) ||
+		!test_bit(BNAD_RF_STATS_TIMER_RUNNING, &bnad->run_flags))
+		return;
+
+	mod_timer(&bnad->stats_timer,
+		  jiffies + msecs_to_jiffies(BNAD_STATS_TIMER_FREQ));
+}
+
+void
+bnad_cb_stats_clr(struct bnad *bnad)
+{
+}
+
+/* PCI Initialization */
+static int
+bnad_pci_init(struct bnad *bnad,
+	      struct pci_dev *pdev, bool *using_dac)
+{
+	int err;
+
+	err = pci_enable_device(pdev);
+	if (err)
+		return err;
+	err = pci_request_regions(pdev, BNAD_NAME);
+	if (err)
+		goto disable_device;
+	if (!pci_set_dma_mask(pdev, DMA_BIT_MASK(64)) &&
+	    !pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64))) {
+		*using_dac = 1;
+	} else {
+		err = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
+		if (err) {
+			err = pci_set_consistent_dma_mask(pdev,
+						DMA_BIT_MASK(32));
+			if (err)
+				goto release_regions;
+		}
+		*using_dac = 0;
+	}
+	pci_set_master(pdev);
+	return 0;
+
+release_regions:
+	pci_release_regions(pdev);
+disable_device:
+	pci_disable_device(pdev);
+
+	return err;
+}
+
+static void
+bnad_pci_uninit(struct pci_dev *pdev)
+{
+	pci_release_regions(pdev);
+	pci_disable_device(pdev);
+}
+
+/*
+ * 1. Initialize the bnad structure
+ * 2. Setup netdev pointer in pci_dev
+ * 3. Initialze Tx free tasklet
+ * 4. Initialize no. of TxQ & CQs & MSIX vectors
+ */
+static int
+bnad_init(struct bnad *bnad,
+	  struct pci_dev *pdev, struct net_device *netdev)
+{
+	SET_NETDEV_DEV(netdev, &pdev->dev);
+	pci_set_drvdata(pdev, netdev);
+
+	bnad->netdev = netdev;
+	bnad->pcidev = pdev;
+	bnad->mmio_start = pci_resource_start(pdev, 0);
+	bnad->mmio_len = pci_resource_len(pdev, 0);
+	bnad->bar0 = ioremap_nocache(bnad->mmio_start, bnad->mmio_len);
+	if (!bnad->bar0) {
+		dev_err(&pdev->dev, "ioremap for bar0 failed\n");
+		pci_set_drvdata(pdev, NULL);
+		return -ENOMEM;
+	}
+	printk(KERN_INFO "bar0 mapped to %p, len %llu\n", bnad->bar0,
+	       (unsigned long long) bnad->mmio_len);
+
+	spin_lock_irq(&bnad->bna_lock);
+	if (!bnad_msix_disable)
+		bnad->cfg_flags = BNAD_CF_MSIX;
+
+	bnad->cfg_flags |= BNAD_CF_DIM_ENABLED;
+
+	bnad_q_num_init(bnad);
+	spin_unlock_irq(&bnad->bna_lock);
+
+	bnad->msix_num = (bnad->num_tx * bnad->num_txq_per_tx) +
+		(bnad->num_rx * bnad->num_rxp_per_rx) +
+			 BNAD_MAILBOX_MSIX_VECTORS;
+	bnad->msix_diag_num = 2;	/* 1 for Tx, 1 for Rx */
+
+	bnad->txq_depth = BNAD_TXQ_DEPTH;
+	bnad->rxq_depth = BNAD_RXQ_DEPTH;
+	bnad->rx_csum = true;
+
+	bnad->tx_coalescing_timeo = BFI_TX_COALESCING_TIMEO;
+	bnad->rx_coalescing_timeo = BFI_RX_COALESCING_TIMEO;
+
+	tasklet_init(&bnad->tx_free_tasklet, bnad_tx_free_tasklet,
+		     (unsigned long)bnad);
+
+	return 0;
+}
+
+/*
+ * Must be called after bnad_pci_uninit()
+ * so that iounmap() and pci_set_drvdata(NULL)
+ * happens only after PCI uninitialization.
+ */
+static void
+bnad_uninit(struct bnad *bnad)
+{
+	if (bnad->bar0)
+		iounmap(bnad->bar0);
+	pci_set_drvdata(bnad->pcidev, NULL);
+}
+
+/*
+ * Initialize locks
+	a) Per device mutes used for serializing configuration
+	   changes from OS interface
+	b) spin lock used to protect bna state machine
+ */
+static void
+bnad_lock_init(struct bnad *bnad)
+{
+	spin_lock_init(&bnad->bna_lock);
+	mutex_init(&bnad->conf_mutex);
+}
+
+static void
+bnad_lock_uninit(struct bnad *bnad)
+{
+	mutex_destroy(&bnad->conf_mutex);
+}
+
+/* Resource allocation, free functions */
+
+void
+bnad_mem_free(struct bnad *bnad,
+	      struct bna_mem_info *mem_info)
+{
+	int i;
+	dma_addr_t dma_pa;
+
+	if (mem_info->mdl == NULL)
+		return;
+
+	for (i = 0; i < mem_info->num; i++) {
+		if (mem_info->mdl[i].kva != NULL) {
+			if (mem_info->mem_type == BNA_MEM_T_DMA) {
+				BNA_GET_DMA_ADDR(&(mem_info->mdl[i].dma),
+						dma_pa);
+				pci_free_consistent(bnad->pcidev,
+						mem_info->mdl[i].len,
+						mem_info->mdl[i].kva, dma_pa);
+			} else
+				kfree(mem_info->mdl[i].kva);
+		}
+	}
+	kfree(mem_info->mdl);
+	mem_info->mdl = NULL;
+}
+
+int
+bnad_mem_alloc(struct bnad *bnad,
+	       struct bna_mem_info *mem_info)
+{
+	int i;
+	dma_addr_t dma_pa;
+
+	if ((mem_info->num == 0) || (mem_info->len == 0)) {
+		mem_info->mdl = NULL;
+		return 0;
+	}
+
+	mem_info->mdl = kcalloc(mem_info->num, sizeof(struct bna_mem_descr),
+				GFP_KERNEL);
+	if (mem_info->mdl == NULL)
+		return -ENOMEM;
+
+	if (mem_info->mem_type == BNA_MEM_T_DMA) {
+		for (i = 0; i < mem_info->num; i++) {
+			mem_info->mdl[i].len = mem_info->len;
+			mem_info->mdl[i].kva =
+				pci_alloc_consistent(bnad->pcidev,
+						mem_info->len, &dma_pa);
+
+			if (mem_info->mdl[i].kva == NULL)
+				goto err_return;
+
+			BNA_SET_DMA_ADDR(dma_pa,
+					 &(mem_info->mdl[i].dma));
+		}
+	} else {
+		for (i = 0; i < mem_info->num; i++) {
+			mem_info->mdl[i].len = mem_info->len;
+			mem_info->mdl[i].kva = kzalloc(mem_info->len,
+							GFP_KERNEL);
+			if (mem_info->mdl[i].kva == NULL)
+				goto err_return;
+		}
+	}
+
+	return 0;
+
+err_return:
+	bnad_mem_free(bnad, mem_info);
+	return -ENOMEM;
+}
+
+/* Free IRQ for Mailbox */
+static void
+bnad_mbox_irq_free(struct bnad *bnad,
+		   struct bna_intr_info *intr_info)
+{
+	int irq;
+
+	if (intr_info->idl == NULL)
+		return;
+
+	spin_lock_irq(&bnad->bna_lock);
+
+	bnad_disable_mbox_irq(bnad);
+
+	irq = BNAD_GET_MBOX_IRQ(bnad);
+	free_irq(irq, bnad->netdev);
+
+	spin_unlock_irq(&bnad->bna_lock);
+
+	kfree(intr_info->idl);
+}
+
+/*
+ * Allocates IRQ for Mailbox, but keep it disabled
+ * This will be enabled once we get the mbox enable callback
+ * from bna
+ */
+static int
+bnad_mbox_irq_alloc(struct bnad *bnad,
+		    struct bna_intr_info *intr_info)
+{
+	int 		err;
+	unsigned long 	flags;
+	u32	irq;
+	irq_handler_t 	irq_handler;
+
+	/* Mbox should use only 1 vector */
+
+	intr_info->idl = kzalloc(sizeof(*(intr_info->idl)), GFP_KERNEL);
+	if (!intr_info->idl)
+		return -ENOMEM;
+
+	spin_lock_irq(&bnad->bna_lock);
+	if (bnad->cfg_flags & BNAD_CF_MSIX) {
+		irq_handler = (irq_handler_t)bnad_msix_mbox_handler;
+		irq = bnad->msix_table[bnad->msix_num - 1].vector;
+		flags = 0;
+		intr_info->intr_type = BNA_INTR_T_MSIX;
+		intr_info->idl[0].vector = bnad->msix_num - 1;
+	} else {
+		irq_handler = (irq_handler_t)bnad_isr;
+		irq = bnad->pcidev->irq;
+		flags = IRQF_SHARED;
+		intr_info->intr_type = BNA_INTR_T_INTX;
+		/* intr_info->idl.vector = 0 ? */
+	}
+	spin_unlock_irq(&bnad->bna_lock);
+
+	sprintf(bnad->mbox_irq_name, "%s%d", BNAD_NAME, bna_id);
+
+	err = request_irq(irq, irq_handler, flags,
+			  bnad->mbox_irq_name, bnad->netdev);
+	if (err) {
+		kfree(intr_info->idl);
+		intr_info->idl = NULL;
+		return err;
+	}
+
+	spin_lock_irq(&bnad->bna_lock);
+	bnad_disable_mbox_irq(bnad);
+	spin_unlock_irq(&bnad->bna_lock);
+	return 0;
+}
+
+static void
+bnad_txrx_irq_free(struct bnad *bnad, struct bna_intr_info *intr_info)
+{
+	kfree(intr_info->idl);
+	intr_info->idl = NULL;
+}
+
+/* Allocates Interrupt Descriptor List for MSIX/INT-X vectors */
+static int
+bnad_txrx_irq_alloc(struct bnad *bnad, enum bnad_intr_source src,
+		    uint txrx_id, struct bna_intr_info *intr_info)
+{
+	int i, vector_start = 0;
+	u32 cfg_flags;
+
+	spin_lock_irq(&bnad->bna_lock);
+	cfg_flags = bnad->cfg_flags;
+	spin_unlock_irq(&bnad->bna_lock);
+
+	if (cfg_flags & BNAD_CF_MSIX) {
+		intr_info->intr_type = BNA_INTR_T_MSIX;
+		intr_info->idl = kcalloc(intr_info->num,
+					sizeof(struct bna_intr_descr),
+					GFP_KERNEL);
+		if (!intr_info->idl)
+			return -ENOMEM;
+
+		switch (src) {
+		case BNAD_INTR_TX:
+			vector_start = txrx_id;
+			break;
+
+		case BNAD_INTR_RX:
+			vector_start = bnad->num_tx * bnad->num_txq_per_tx +
+					txrx_id;
+			break;
+
+		default:
+			BUG();
+		}
+
+		for (i = 0; i < intr_info->num; i++)
+			intr_info->idl[i].vector = vector_start + i;
+	} else {
+		intr_info->intr_type = BNA_INTR_T_INTX;
+		intr_info->num = 1;
+		intr_info->idl = kcalloc(intr_info->num,
+					sizeof(struct bna_intr_descr),
+					GFP_KERNEL);
+		if (!intr_info->idl)
+			return -ENOMEM;
+
+		switch (src) {
+		case BNAD_INTR_TX:
+			intr_info->idl[0].vector = 0x1; /* Bit mask : Tx IB */
+			break;
+
+		case BNAD_INTR_RX:
+			intr_info->idl[0].vector = 0x2; /* Bit mask : Rx IB */
+			break;
+		}
+	}
+	return 0;
+}
+
+/**
+ * NOTE: Should be called for MSIX only
+ * Unregisters Tx MSIX vector(s) from the kernel
+ */
+static void
+bnad_tx_msix_unregister(struct bnad *bnad, struct bnad_tx_info *tx_info,
+			int num_txqs)
+{
+	int i;
+	int vector_num;
+
+	for (i = 0; i < num_txqs; i++) {
+		if (tx_info->tcb[i] == NULL)
+			continue;
+
+		vector_num = tx_info->tcb[i]->intr_vector;
+		free_irq(bnad->msix_table[vector_num].vector, tx_info->tcb[i]);
+	}
+}
+
+/**
+ * NOTE: Should be called for MSIX only
+ * Registers Tx MSIX vector(s) and ISR(s), cookie with the kernel
+ */
+static int
+bnad_tx_msix_register(struct bnad *bnad, struct bnad_tx_info *tx_info,
+			uint tx_id, int num_txqs)
+{
+	int i;
+	int err;
+	int vector_num;
+
+	for (i = 0; i < num_txqs; i++) {
+		vector_num = tx_info->tcb[i]->intr_vector;
+		sprintf(tx_info->tcb[i]->name, "%s TXQ %d", bnad->netdev->name,
+				tx_id + tx_info->tcb[i]->id);
+		err = request_irq(bnad->msix_table[vector_num].vector,
+				  (irq_handler_t)bnad_msix_tx, 0,
+				  tx_info->tcb[i]->name,
+				  tx_info->tcb[i]);
+		if (err)
+			goto err_return;
+	}
+
+	return 0;
+
+err_return:
+	if (i > 0)
+		bnad_tx_msix_unregister(bnad, tx_info, (i - 1));
+	return -1;
+}
+
+/**
+ * NOTE: Should be called for MSIX only
+ * Unregisters Rx MSIX vector(s) from the kernel
+ */
+static void
+bnad_rx_msix_unregister(struct bnad *bnad, struct bnad_rx_info *rx_info,
+			int num_rxps)
+{
+	int i;
+	int vector_num;
+
+	for (i = 0; i < num_rxps; i++) {
+		if (rx_info->rx_ctrl[i].ccb == NULL)
+			continue;
+
+		vector_num = rx_info->rx_ctrl[i].ccb->intr_vector;
+		free_irq(bnad->msix_table[vector_num].vector,
+			 rx_info->rx_ctrl[i].ccb);
+	}
+}
+
+/**
+ * NOTE: Should be called for MSIX only
+ * Registers Tx MSIX vector(s) and ISR(s), cookie with the kernel
+ */
+static int
+bnad_rx_msix_register(struct bnad *bnad, struct bnad_rx_info *rx_info,
+			uint rx_id, int num_rxps)
+{
+	int i;
+	int err;
+	int vector_num;
+
+	for (i = 0; i < num_rxps; i++) {
+		vector_num = rx_info->rx_ctrl[i].ccb->intr_vector;
+		sprintf(rx_info->rx_ctrl[i].ccb->name, "%s CQ %d",
+			bnad->netdev->name,
+			rx_id + rx_info->rx_ctrl[i].ccb->id);
+		err = request_irq(bnad->msix_table[vector_num].vector,
+				  (irq_handler_t)bnad_msix_rx, 0,
+				  rx_info->rx_ctrl[i].ccb->name,
+				  rx_info->rx_ctrl[i].ccb);
+		if (err)
+			goto err_return;
+	}
+
+	return 0;
+
+err_return:
+	if (i > 0)
+		bnad_rx_msix_unregister(bnad, rx_info, (i - 1));
+	return -1;
+}
+
+/* Free BNA resources */
+static void
+bnad_res_free(struct bnad *bnad)
+{
+	int i;
+	struct bna_res_info *res_info = &bnad->res_info[0];
+
+	for (i = 0; i < BNA_RES_T_MAX; i++) {
+		if (res_info[i].res_type == BNA_RES_T_MEM)
+			bnad_mem_free(bnad, &res_info[i].res_u.mem_info);
+		else
+			bnad_mbox_irq_free(bnad, &res_info[i].res_u.intr_info);
+	}
+}
+
+/* Allocates memory and interrupt resources for BNA */
+static int
+bnad_res_alloc(struct bnad *bnad)
+{
+	int i, err;
+	struct bna_res_info *res_info = &bnad->res_info[0];
+
+	for (i = 0; i < BNA_RES_T_MAX; i++) {
+		if (res_info[i].res_type == BNA_RES_T_MEM)
+			err = bnad_mem_alloc(bnad, &res_info[i].res_u.mem_info);
+		else
+			err = bnad_mbox_irq_alloc(bnad,
+						  &res_info[i].res_u.intr_info);
+		if (err)
+			goto err_return;
+	}
+	return 0;
+
+err_return:
+	bnad_res_free(bnad);
+	return err;
+}
+
+/* Free Tx object Resources */
+static void
+bnad_tx_res_free(struct bnad *bnad, struct bna_res_info *res_info)
+{
+	int i;
+
+	for (i = 0; i < BNA_TX_RES_T_MAX; i++) {
+		if (res_info[i].res_type == BNA_RES_T_MEM)
+			bnad_mem_free(bnad, &res_info[i].res_u.mem_info);
+		else if (res_info[i].res_type == BNA_RES_T_INTR)
+			bnad_txrx_irq_free(bnad, &res_info[i].res_u.intr_info);
+	}
+}
+
+/* Allocates memory and interrupt resources for Tx object */
+static int
+bnad_tx_res_alloc(struct bnad *bnad, struct bna_res_info *res_info,
+		  uint tx_id)
+{
+	int i, err = 0;
+
+	for (i = 0; i < BNA_TX_RES_T_MAX; i++) {
+		if (res_info[i].res_type == BNA_RES_T_MEM)
+			err = bnad_mem_alloc(bnad,
+					&res_info[i].res_u.mem_info);
+		else if (res_info[i].res_type == BNA_RES_T_INTR)
+			err = bnad_txrx_irq_alloc(bnad, BNAD_INTR_TX, tx_id,
+					&res_info[i].res_u.intr_info);
+		if (err)
+			goto err_return;
+	}
+	return 0;
+
+err_return:
+	bnad_tx_res_free(bnad, res_info);
+	return err;
+}
+
+/* Free Rx object Resources */
+static void
+bnad_rx_res_free(struct bnad *bnad, struct bna_res_info *res_info)
+{
+	int i;
+
+	for (i = 0; i < BNA_RX_RES_T_MAX; i++) {
+		if (res_info[i].res_type == BNA_RES_T_MEM)
+			bnad_mem_free(bnad, &res_info[i].res_u.mem_info);
+		else if (res_info[i].res_type == BNA_RES_T_INTR)
+			bnad_txrx_irq_free(bnad, &res_info[i].res_u.intr_info);
+	}
+}
+
+/* Allocates memory and interrupt resources for Rx object */
+static int
+bnad_rx_res_alloc(struct bnad *bnad, struct bna_res_info *res_info,
+		  uint rx_id)
+{
+	int i, err = 0;
+
+	/* All memory needs to be allocated before setup_ccbs */
+	for (i = 0; i < BNA_RX_RES_T_MAX; i++) {
+		if (res_info[i].res_type == BNA_RES_T_MEM)
+			err = bnad_mem_alloc(bnad,
+					&res_info[i].res_u.mem_info);
+		else if (res_info[i].res_type == BNA_RES_T_INTR)
+			err = bnad_txrx_irq_alloc(bnad, BNAD_INTR_RX, rx_id,
+					&res_info[i].res_u.intr_info);
+		if (err)
+			goto err_return;
+	}
+	return 0;
+
+err_return:
+	bnad_rx_res_free(bnad, res_info);
+	return err;
+}
+
+/* Timer callbacks */
+/* a) IOC timer */
+static void
+bnad_ioc_timeout(unsigned long data)
+{
+	struct bnad *bnad = (struct bnad *)data;
+
+	spin_lock_irq(&bnad->bna_lock);
+	bfa_ioc_timeout((void *) &bnad->bna.device.ioc);
+	spin_unlock_irq(&bnad->bna_lock);
+}
+
+static void
+bnad_ioc_hb_check(unsigned long data)
+{
+	struct bnad *bnad = (struct bnad *)data;
+
+	spin_lock_irq(&bnad->bna_lock);
+	bfa_ioc_hb_check((void *) &bnad->bna.device.ioc);
+	spin_unlock_irq(&bnad->bna_lock);
+}
+
+static void
+bnad_ioc_sem_timeout(unsigned long data)
+{
+	struct bnad *bnad = (struct bnad *)data;
+
+	spin_lock_irq(&bnad->bna_lock);
+	bfa_ioc_sem_timeout((void *) &bnad->bna.device.ioc);
+	spin_unlock_irq(&bnad->bna_lock);
+}
+
+/*
+ * All timer routines use bnad->bna_lock to protect against
+ * the following race, which may occur in case of no locking:
+ * 	Time	CPU m  		CPU n
+ *	0       1 = test_bit
+ *	1			clear_bit
+ *	2			del_timer_sync
+ *	3	mod_timer
+ */
+
+/* b) Dynamic Interrupt Moderation Timer */
+static void
+bnad_dim_timeout(unsigned long data)
+{
+	struct bnad *bnad = (struct bnad *)data;
+	struct bnad_rx_info *rx_info;
+	struct bnad_rx_ctrl *rx_ctrl;
+	int i, j;
+
+	if (!netif_carrier_ok(bnad->netdev))
+		return;
+
+	spin_lock_irq(&bnad->bna_lock);
+	for (i = 0; i < bnad->num_rx; i++) {
+		rx_info = &bnad->rx_info[i];
+		if (!rx_info->rx)
+			continue;
+		for (j = 0; j < bnad->num_rxp_per_rx; j++) {
+			rx_ctrl = &rx_info->rx_ctrl[j];
+			if (!rx_ctrl->ccb)
+				continue;
+			bna_rx_dim_update(rx_ctrl->ccb);
+		}
+	}
+
+	/* Check for BNAD_CF_DIM_ENABLED, does not eleminate a race */
+	if (test_bit(BNAD_RF_DIM_TIMER_RUNNING, &bnad->run_flags))
+		mod_timer(&bnad->dim_timer,
+			  jiffies + msecs_to_jiffies(BNAD_DIM_TIMER_FREQ));
+	spin_unlock_irq(&bnad->bna_lock);
+}
+
+/* c)  Statistics Timer */
+static void
+bnad_stats_timeout(unsigned long data)
+{
+	struct bnad *bnad = (struct bnad *)data;
+
+	if (!netif_running(bnad->netdev) ||
+		!test_bit(BNAD_RF_STATS_TIMER_RUNNING, &bnad->run_flags))
+		return;
+
+	spin_lock_irq(&bnad->bna_lock);
+	bna_stats_get(&bnad->bna);
+	spin_unlock_irq(&bnad->bna_lock);
+}
+
+/*
+ * Set up timer for DIM
+ * Called with bnad->bna_lock held
+ */
+void
+bnad_dim_timer_start(struct bnad *bnad)
+{
+	if (bnad->cfg_flags & BNAD_CF_DIM_ENABLED &&
+	    !test_bit(BNAD_RF_DIM_TIMER_RUNNING, &bnad->run_flags)) {
+		setup_timer(&bnad->dim_timer, bnad_dim_timeout,
+			    (unsigned long)bnad);
+		set_bit(BNAD_RF_DIM_TIMER_RUNNING, &bnad->run_flags);
+		mod_timer(&bnad->dim_timer,
+			  jiffies + msecs_to_jiffies(BNAD_DIM_TIMER_FREQ));
+	}
+}
+
+/*
+ * Stops the DIM timer
+ * Called with bnad->bna_lock held
+ */
+void
+bnad_dim_timer_stop(struct bnad *bnad)
+{
+	int to_del = 0;
+
+	if (bnad->cfg_flags & BNAD_CF_DIM_ENABLED &&
+	    test_bit(BNAD_RF_DIM_TIMER_RUNNING, &bnad->run_flags)) {
+		clear_bit(BNAD_RF_DIM_TIMER_RUNNING, &bnad->run_flags);
+		to_del = 1;
+	}
+	spin_unlock_irq(&bnad->bna_lock);
+	if (to_del)
+		del_timer_sync(&bnad->dim_timer);
+	spin_lock_irq(&bnad->bna_lock);
+}
+
+/*
+ * Set up timer for statistics
+ * Called with mutex_lock(&bnad->conf_mutex) held
+ */
+void
+bnad_stats_timer_start(struct bnad *bnad)
+{
+	spin_lock_irq(&bnad->bna_lock);
+	if (!test_and_set_bit(BNAD_RF_STATS_TIMER_RUNNING, &bnad->run_flags)) {
+		setup_timer(&bnad->stats_timer, bnad_stats_timeout,
+			    (unsigned long)bnad);
+		mod_timer(&bnad->stats_timer,
+			  jiffies + msecs_to_jiffies(BNAD_STATS_TIMER_FREQ));
+	}
+	spin_unlock_irq(&bnad->bna_lock);
+
+}
+
+/*
+ * Stops the stats timer
+ * Called with mutex_lock(&bnad->conf_mutex) held
+ */
+void
+bnad_stats_timer_stop(struct bnad *bnad)
+{
+	int to_del = 0;
+
+	spin_lock_irq(&bnad->bna_lock);
+	if (test_and_clear_bit(BNAD_RF_STATS_TIMER_RUNNING, &bnad->run_flags))
+		to_del = 1;
+	spin_unlock_irq(&bnad->bna_lock);
+	if (to_del)
+		del_timer_sync(&bnad->stats_timer);
+}
+
+/* Enable / disable device */
+void
+bnad_device_disable(struct bnad *bnad)
+{
+	init_completion(&bnad->bnad_completions.ioc_comp);
+
+	spin_lock_irq(&bnad->bna_lock);
+	bna_device_disable(&bnad->bna.device, BNA_HARD_CLEANUP);
+	spin_unlock_irq(&bnad->bna_lock);
+
+	wait_for_completion(&bnad->bnad_completions.ioc_comp);
+
+}
+
+int
+bnad_device_enable(struct bnad *bnad)
+{
+	int err = 0;
+	init_completion(&bnad->bnad_completions.ioc_comp);
+
+	spin_lock_irq(&bnad->bna_lock);
+	bna_device_enable(&bnad->bna.device);
+	spin_unlock_irq(&bnad->bna_lock);
+
+	wait_for_completion(&bnad->bnad_completions.ioc_comp);
+
+	if (bnad->bnad_completions.ioc_comp_status)
+		err = bnad->bnad_completions.ioc_comp_status;
+
+	return err;
+}
+/* BNAD DEBUG */
+
+static int __devinit
+bnad_pci_probe(struct pci_dev *pdev,
+		const struct pci_device_id *pcidev_id)
+{
+	bool 	using_dac;
+	int 	err;
+	struct bnad *bnad;
+	struct bna *bna;
+	struct net_device *netdev;
+	struct bfa_pcidev pcidev_info;
+
+	printk(KERN_INFO "bnad_pci_probe : (0x%p, 0x%p) PCI Func : (%d)\n",
+	       pdev, pcidev_id, PCI_FUNC(pdev->devfn));
+
+	mutex_lock(&bnad_fwimg_mutex);
+	if (!cna_get_firmware_buf(pdev)) {
+		mutex_unlock(&bnad_fwimg_mutex);
+		printk(KERN_WARNING "Failed to load Firmware Image!\n");
+		return -ENODEV;
+	}
+	mutex_unlock(&bnad_fwimg_mutex);
+
+	/*
+	 * Allocates sizeof(struct net_device + struct bnad)
+	 * bnad = netdev->priv
+	 */
+	netdev = alloc_etherdev(sizeof(struct bnad));
+	if (!netdev) {
+		dev_err(&pdev->dev, "alloc_etherdev failed\n");
+		err = -ENOMEM;
+		return err;
+	}
+	bnad = netdev_priv(netdev);
+
+	bnad->ident.id = bna_id;
+
+	/*
+	 * PCI initialization
+	 * 	Output : using_dac = 1 for 64 bit DMA
+	 *		           = 0 for 32 bit DMA
+	 */
+	err = bnad_pci_init(bnad, pdev, &using_dac);
+	if (err)
+		goto free_netdev;
+
+	bnad_lock_init(bnad);
+	/*
+	 * Initialize bnad structure
+	 * Setup relation between pci_dev & netdev
+	 * Init Tx free tasklet
+	 */
+	err = bnad_init(bnad, pdev, netdev);
+	if (err)
+		goto pci_uninit;
+	/* Initialize netdev structure, set up ethtool ops */
+	bnad_netdev_init(bnad, using_dac);
+
+	bnad_enable_msix(bnad);
+
+	/* Get resource requirement form bna */
+	bna_res_req(&bnad->res_info[0]);
+
+	/* Allocate resources from bna */
+	err = bnad_res_alloc(bnad);
+	if (err)
+		goto free_netdev;
+
+	bna = &bnad->bna;
+
+	/* Setup pcidev_info for bna_init() */
+	pcidev_info.pci_slot = PCI_SLOT(bnad->pcidev->devfn);
+	pcidev_info.pci_func = PCI_FUNC(bnad->pcidev->devfn);
+	pcidev_info.device_id = bnad->pcidev->device;
+	pcidev_info.pci_bar_kva = bnad->bar0;
+
+	mutex_lock(&bnad->conf_mutex);
+
+	spin_lock_irq(&bnad->bna_lock);
+	bna_init(bna, bnad, &bnad->ident, &pcidev_info, &bnad->res_info[0]);
+
+	spin_unlock_irq(&bnad->bna_lock);
+
+	bnad->stats.bna_stats = &bna->stats;
+
+	/* Set up timers */
+	setup_timer(&bnad->bna.device.ioc.ioc_timer, bnad_ioc_timeout,
+				((unsigned long)bnad));
+	setup_timer(&bnad->bna.device.ioc.hb_timer, bnad_ioc_hb_check,
+				((unsigned long)bnad));
+	setup_timer(&bnad->bna.device.ioc.sem_timer, bnad_ioc_sem_timeout,
+				((unsigned long)bnad));
+
+	/* Now start the timer before calling IOC */
+	mod_timer(&bnad->bna.device.ioc.ioc_timer,
+		  jiffies + msecs_to_jiffies(BNA_IOC_TIMER_FREQ));
+
+	/*
+	 * Start the chip
+	 * Don't care even if err != 0, bna state machine will
+	 * deal with it
+	 */
+	err = bnad_device_enable(bnad);
+
+	/* Get the burnt-in mac */
+	spin_lock_irq(&bnad->bna_lock);
+	bna_port_mac_get(&bna->port, &bnad->perm_addr);
+	bnad_set_netdev_perm_addr(bnad);
+	spin_unlock_irq(&bnad->bna_lock);
+
+	mutex_unlock(&bnad->conf_mutex);
+
+	/*
+	 * Make sure the link appears down to the stack
+	 */
+	netif_carrier_off(netdev);
+
+	/* Finally, reguister with net_device layer */
+	err = register_netdev(netdev);
+	if (err) {
+		printk(KERN_ERR "BNA[%d] : Registering with netdev failed\n",
+		       bnad->ident.id);
+		goto disable_device;
+	}
+
+	/* Add to global list of device instances */
+	bnad_add_to_list(bnad);
+
+	return 0;
+
+disable_device:
+	mutex_lock(&bnad->conf_mutex);
+	bnad_device_disable(bnad);
+	del_timer_sync(&bnad->bna.device.ioc.ioc_timer);
+	del_timer_sync(&bnad->bna.device.ioc.sem_timer);
+	del_timer_sync(&bnad->bna.device.ioc.hb_timer);
+	spin_lock_irq(&bnad->bna_lock);
+	bna_uninit(bna);
+	spin_unlock_irq(&bnad->bna_lock);
+	mutex_unlock(&bnad->conf_mutex);
+
+	bnad_res_free(bnad);
+	bnad_disable_msix(bnad);
+pci_uninit:
+	bnad_pci_uninit(pdev);
+	bnad_lock_uninit(bnad);
+	bnad_uninit(bnad);
+free_netdev:
+	free_netdev(netdev);
+	return err;
+}
+
+static void __devexit
+bnad_pci_remove(struct pci_dev *pdev)
+{
+	struct net_device *netdev = pci_get_drvdata(pdev);
+	struct bnad *bnad;
+	struct bna *bna;
+
+	if (!netdev)
+		return;
+
+	printk(KERN_INFO "%s bnad_pci_remove\n", netdev->name);
+	bnad = netdev_priv(netdev);
+	bna = &bnad->bna;
+
+	bnad_remove_from_list(bnad);
+
+	unregister_netdev(netdev);
+
+	mutex_lock(&bnad->conf_mutex);
+	bnad_device_disable(bnad);
+	del_timer_sync(&bnad->bna.device.ioc.ioc_timer);
+	del_timer_sync(&bnad->bna.device.ioc.sem_timer);
+	del_timer_sync(&bnad->bna.device.ioc.hb_timer);
+	spin_lock_irq(&bnad->bna_lock);
+	bna_uninit(bna);
+	spin_unlock_irq(&bnad->bna_lock);
+	mutex_unlock(&bnad->conf_mutex);
+
+	bnad_res_free(bnad);
+	bnad_disable_msix(bnad);
+	bnad_pci_uninit(pdev);
+	bnad_lock_uninit(bnad);
+	bnad_uninit(bnad);
+	free_netdev(netdev);
+}
+
+/* Utilities */
+
+/* Should be held with conf_lock held */
+void
+bnad_cleanup_tx(struct bnad *bnad, uint tx_id)
+{
+	struct bnad_tx_info *tx_info = &bnad->tx_info[tx_id];
+	struct bna_res_info *res_info = &bnad->tx_res_info[tx_id].res_info[0];
+
+	if (!tx_info->tx)
+		return;
+
+	init_completion(&bnad->bnad_completions.tx_comp);
+	spin_lock_irq(&bnad->bna_lock);
+	bna_tx_disable(tx_info->tx, BNA_HARD_CLEANUP, bnad_cb_tx_disabled);
+	spin_unlock_irq(&bnad->bna_lock);
+	wait_for_completion(&bnad->bnad_completions.tx_comp);
+
+	if (tx_info->tcb[0]->intr_type == BNA_INTR_T_MSIX)
+		bnad_tx_msix_unregister(bnad, tx_info,
+			bnad->num_txq_per_tx);
+
+	spin_lock_irq(&bnad->bna_lock);
+	bna_tx_destroy(tx_info->tx);
+	spin_unlock_irq(&bnad->bna_lock);
+
+	tx_info->tx = NULL;
+
+	if (0 == tx_id)
+		tasklet_kill(&bnad->tx_free_tasklet);
+
+	bnad_tx_res_free(bnad, res_info);
+}
+
+/* Should be held with conf_lock held */
+int
+bnad_setup_tx(struct bnad *bnad, uint tx_id)
+{
+	int err;
+	struct bnad_tx_info *tx_info = &bnad->tx_info[tx_id];
+	struct bna_res_info *res_info = &bnad->tx_res_info[tx_id].res_info[0];
+	struct bna_intr_info *intr_info =
+			&res_info[BNA_TX_RES_INTR_T_TXCMPL].res_u.intr_info;
+	struct bna_tx_config *tx_config = &bnad->tx_config[tx_id];
+	struct bna_tx_event_cbfn tx_cbfn;
+	struct bna_tx *tx;
+
+	/* Initialize the Tx object configuration */
+	tx_config->num_txq = bnad->num_txq_per_tx;
+	tx_config->txq_depth = bnad->txq_depth;
+	tx_config->tx_type = BNA_TX_T_REGULAR;
+
+	/* Initialize the tx event handlers */
+	tx_cbfn.tcb_setup_cbfn = bnad_cb_tcb_setup;
+	tx_cbfn.tcb_destroy_cbfn = bnad_cb_tcb_destroy;
+	tx_cbfn.tx_stall_cbfn = bnad_cb_tx_stall;
+	tx_cbfn.tx_resume_cbfn = bnad_cb_tx_resume;
+	tx_cbfn.tx_cleanup_cbfn = bnad_cb_tx_cleanup;
+
+	/* Get BNA's resource requirement for one tx object */
+	spin_lock_irq(&bnad->bna_lock);
+	bna_tx_res_req(bnad->num_txq_per_tx,
+		bnad->txq_depth, res_info);
+	spin_unlock_irq(&bnad->bna_lock);
+
+	/* Fill Unmap Q memory requirements */
+	BNAD_FILL_UNMAPQ_MEM_REQ(
+			&res_info[BNA_TX_RES_MEM_T_UNMAPQ],
+			bnad->num_txq_per_tx,
+			BNAD_TX_UNMAPQ_DEPTH);
+
+	/* Allocate resources */
+	err = bnad_tx_res_alloc(bnad, res_info, tx_id);
+	if (err)
+		return err;
+
+	/* Ask BNA to create one Tx object, supplying required resources */
+	spin_lock_irq(&bnad->bna_lock);
+	tx = bna_tx_create(&bnad->bna, bnad, tx_config, &tx_cbfn, res_info,
+			tx_info);
+	spin_unlock_irq(&bnad->bna_lock);
+	if (!tx)
+		goto err_return;
+	tx_info->tx = tx;
+
+	/* Register ISR for the Tx object */
+	if (intr_info->intr_type == BNA_INTR_T_MSIX) {
+		err = bnad_tx_msix_register(bnad, tx_info,
+			tx_id, bnad->num_txq_per_tx);
+		if (err)
+			goto err_return;
+	}
+
+	spin_lock_irq(&bnad->bna_lock);
+	bna_tx_enable(tx);
+	spin_unlock_irq(&bnad->bna_lock);
+
+	return 0;
+
+err_return:
+	bnad_tx_res_free(bnad, res_info);
+	return err;
+}
+
+/* Setup the rx config for bna_rx_create */
+/* bnad decides the configuration */
+static void
+bnad_init_rx_config(struct bnad *bnad, struct bna_rx_config *rx_config)
+{
+	rx_config->rx_type = BNA_RX_T_REGULAR;
+	rx_config->num_paths = bnad->num_rxp_per_rx;
+
+	if (bnad->num_rxp_per_rx > 1) {
+		rx_config->rss_status = BNA_STATUS_T_ENABLED;
+		rx_config->rss_config.hash_type =
+				(BFI_RSS_T_V4_TCP |
+				 BFI_RSS_T_V6_TCP |
+				 BFI_RSS_T_V4_IP  |
+				 BFI_RSS_T_V6_IP);
+		rx_config->rss_config.hash_mask =
+				bnad->num_rxp_per_rx - 1;
+		get_random_bytes(rx_config->rss_config.toeplitz_hash_key,
+			sizeof(rx_config->rss_config.toeplitz_hash_key));
+	} else {
+		rx_config->rss_status = BNA_STATUS_T_DISABLED;
+		memset(&rx_config->rss_config, 0,
+		       sizeof(rx_config->rss_config));
+	}
+	rx_config->rxp_type = BNA_RXP_SLR;
+	rx_config->q_depth = bnad->rxq_depth;
+
+	rx_config->small_buff_size = BFI_SMALL_RXBUF_SIZE;
+
+	rx_config->vlan_strip_status = BNA_STATUS_T_ENABLED;
+}
+
+/* Called with mutex_lock(&bnad->conf_mutex) held */
+void
+bnad_cleanup_rx(struct bnad *bnad, uint rx_id)
+{
+	struct bnad_rx_info *rx_info = &bnad->rx_info[rx_id];
+	struct bna_rx_config *rx_config = &bnad->rx_config[rx_id];
+	struct bna_res_info *res_info = &bnad->rx_res_info[rx_id].res_info[0];
+
+	if (!rx_info->rx)
+		return;
+
+	spin_lock_irq(&bnad->bna_lock);
+	if (0 == rx_id)
+		bnad_dim_timer_stop(bnad);
+	spin_unlock_irq(&bnad->bna_lock);
+
+	bnad_napi_disable(bnad, rx_id);
+
+	init_completion(&bnad->bnad_completions.rx_comp);
+	spin_lock_irq(&bnad->bna_lock);
+	bna_rx_disable(rx_info->rx, BNA_HARD_CLEANUP, bnad_cb_rx_disabled);
+	spin_unlock_irq(&bnad->bna_lock);
+	wait_for_completion(&bnad->bnad_completions.rx_comp);
+
+	if (rx_info->rx_ctrl[0].ccb->intr_type == BNA_INTR_T_MSIX)
+		bnad_rx_msix_unregister(bnad, rx_info, rx_config->num_paths);
+
+	spin_lock_irq(&bnad->bna_lock);
+	bna_rx_destroy(rx_info->rx);
+	spin_unlock_irq(&bnad->bna_lock);
+
+	rx_info->rx = NULL;
+
+	bnad_rx_res_free(bnad, res_info);
+}
+
+/* Called with mutex_lock(&bnad->conf_mutex) held */
+int
+bnad_setup_rx(struct bnad *bnad, uint rx_id)
+{
+	int err;
+	struct bnad_rx_info *rx_info = &bnad->rx_info[rx_id];
+	struct bna_res_info *res_info = &bnad->rx_res_info[rx_id].res_info[0];
+	struct bna_intr_info *intr_info =
+			&res_info[BNA_RX_RES_T_INTR].res_u.intr_info;
+	struct bna_rx_config *rx_config = &bnad->rx_config[rx_id];
+	struct bna_rx_event_cbfn rx_cbfn;
+	struct bna_rx *rx;
+
+	/* Initialize the Rx object configuration */
+	bnad_init_rx_config(bnad, rx_config);
+
+	/* Initialize the Rx event handlers */
+	rx_cbfn.rcb_setup_cbfn = bnad_cb_rcb_setup;
+	rx_cbfn.rcb_destroy_cbfn = NULL;
+	rx_cbfn.ccb_setup_cbfn = bnad_cb_ccb_setup;
+	rx_cbfn.ccb_destroy_cbfn = bnad_cb_ccb_destroy;
+	rx_cbfn.rx_cleanup_cbfn = bnad_cb_rx_cleanup;
+	rx_cbfn.rx_post_cbfn = bnad_cb_rx_post;
+
+	/* Get BNA's resource requirement for one Rx object */
+	spin_lock_irq(&bnad->bna_lock);
+	bna_rx_res_req(rx_config, res_info);
+	spin_unlock_irq(&bnad->bna_lock);
+
+	/* Fill Unmap Q memory requirements */
+	BNAD_FILL_UNMAPQ_MEM_REQ(
+			&res_info[BNA_RX_RES_MEM_T_UNMAPQ],
+			rx_config->num_paths +
+			((rx_config->rxp_type == BNA_RXP_SINGLE) ? 0 :
+				rx_config->num_paths), BNAD_RX_UNMAPQ_DEPTH);
+
+	/* Allocate resource */
+	err = bnad_rx_res_alloc(bnad, res_info, rx_id);
+	if (err)
+		return err;
+
+	/* Ask BNA to create one Rx object, supplying required resources */
+	spin_lock_irq(&bnad->bna_lock);
+	rx = bna_rx_create(&bnad->bna, bnad, rx_config, &rx_cbfn, res_info,
+			rx_info);
+	spin_unlock_irq(&bnad->bna_lock);
+	if (!rx)
+		goto err_return;
+	rx_info->rx = rx;
+
+	/* Register ISR for the Rx object */
+	if (intr_info->intr_type == BNA_INTR_T_MSIX) {
+		err = bnad_rx_msix_register(bnad, rx_info, rx_id,
+						rx_config->num_paths);
+		if (err)
+			goto err_return;
+	}
+
+	/* Enable NAPI */
+	bnad_napi_enable(bnad, rx_id);
+
+	spin_lock_irq(&bnad->bna_lock);
+	if (0 == rx_id) {
+		/* Set up Dynamic Interrupt Moderation Vector */
+		if (bnad->cfg_flags & BNAD_CF_DIM_ENABLED)
+			bna_rx_dim_reconfig(&bnad->bna, bna_napi_dim_vector);
+
+		/* Enable VLAN filtering only on the default Rx */
+		bna_rx_vlanfilter_enable(rx);
+
+		/* Start the DIM timer */
+		bnad_dim_timer_start(bnad);
+	}
+
+	bna_rx_enable(rx);
+	spin_unlock_irq(&bnad->bna_lock);
+
+	return 0;
+
+err_return:
+	bnad_cleanup_rx(bnad, rx_id);
+	return err;
+}
+
+/* Called with conf_lock & bnad->bna_lock held */
+void
+bnad_tx_coalescing_timeo_set(struct bnad *bnad)
+{
+	struct bnad_tx_info *tx_info;
+
+	tx_info = &bnad->tx_info[0];
+	if (!tx_info->tx)
+		return;
+
+	bna_tx_coalescing_timeo_set(tx_info->tx, bnad->tx_coalescing_timeo);
+}
+
+/* Called with conf_lock & bnad->bna_lock held */
+void
+bnad_rx_coalescing_timeo_set(struct bnad *bnad)
+{
+	struct bnad_rx_info *rx_info;
+	int 	i;
+
+	for (i = 0; i < bnad->num_rx; i++) {
+		rx_info = &bnad->rx_info[i];
+		if (!rx_info->rx)
+			continue;
+		bna_rx_coalescing_timeo_set(rx_info->rx,
+				bnad->rx_coalescing_timeo);
+	}
+}
+
+/*
+ * Called with bnad->bna_lock held
+ */
+static int
+bnad_mac_addr_set_locked(struct bnad *bnad, u8 *mac_addr)
+{
+	int ret;
+
+	if (!is_valid_ether_addr(mac_addr))
+		return -EADDRNOTAVAIL;
+
+	/* If datapath is down, pretend everything went through */
+	if (!bnad->rx_info[0].rx)
+		return 0;
+
+	ret = bna_rx_ucast_set(bnad->rx_info[0].rx, mac_addr, NULL);
+	if (ret != BNA_CB_SUCCESS)
+		return -EADDRNOTAVAIL;
+
+	return 0;
+}
+
+/* Should be called with conf_lock held */
+static int
+bnad_enable_default_bcast(struct bnad *bnad)
+{
+	struct bnad_rx_info *rx_info = &bnad->rx_info[0];
+	int ret;
+
+	init_completion(&bnad->bnad_completions.mcast_comp);
+
+	spin_lock_irq(&bnad->bna_lock);
+	ret = bna_rx_mcast_add(rx_info->rx, (u8 *)bnad_bcast_addr,
+				bnad_cb_rx_mcast_add);
+	spin_unlock_irq(&bnad->bna_lock);
+
+	if (ret == BNA_CB_SUCCESS)
+		wait_for_completion(&bnad->bnad_completions.mcast_comp);
+	else
+		return -ENODEV;
+
+	if (bnad->bnad_completions.mcast_comp_status != BNA_CB_SUCCESS)
+		return -ENODEV;
+
+	return 0;
+}
+
+/* Statistics utilities */
+void
+bnad_netdev_qstats_fill(struct bnad *bnad)
+{
+	struct net_device_stats *net_stats = &bnad->net_stats;
+	int i, j;
+
+	for (i = 0; i < bnad->num_rx; i++) {
+		for (j = 0; j < bnad->num_rxp_per_rx; j++) {
+			if (bnad->rx_info[i].rx_ctrl[j].ccb) {
+				net_stats->rx_packets += bnad->rx_info[i].
+				rx_ctrl[j].ccb->rcb[0]->rxq->rx_packets;
+				net_stats->rx_bytes += bnad->rx_info[i].
+					rx_ctrl[j].ccb->rcb[0]->rxq->rx_bytes;
+				if (bnad->rx_info[i].rx_ctrl[j].ccb->rcb[1] &&
+					bnad->rx_info[i].rx_ctrl[j].ccb->
+					rcb[1]->rxq) {
+					net_stats->rx_packets +=
+						bnad->rx_info[i].rx_ctrl[j].
+						ccb->rcb[1]->rxq->rx_packets;
+					net_stats->rx_bytes +=
+						bnad->rx_info[i].rx_ctrl[j].
+						ccb->rcb[1]->rxq->rx_bytes;
+				}
+			}
+		}
+	}
+	for (i = 0; i < bnad->num_tx; i++) {
+		for (j = 0; j < bnad->num_txq_per_tx; j++) {
+			if (bnad->tx_info[i].tcb[j]) {
+				net_stats->tx_packets +=
+				bnad->tx_info[i].tcb[j]->txq->tx_packets;
+				net_stats->tx_bytes +=
+					bnad->tx_info[i].tcb[j]->txq->tx_bytes;
+			}
+		}
+	}
+}
+
+/*
+ * Must be called with the bna_lock held.
+ */
+void
+bnad_netdev_hwstats_fill(struct bnad *bnad)
+{
+	struct bfi_ll_stats_mac *mac_stats;
+	struct net_device_stats *net_stats = &bnad->net_stats;
+	u64 bmap;
+	int i;
+
+	mac_stats = &bnad->stats.bna_stats->hw_stats->mac_stats;
+	net_stats->rx_errors =
+		mac_stats->rx_fcs_error + mac_stats->rx_alignment_error +
+		mac_stats->rx_frame_length_error + mac_stats->rx_code_error +
+		mac_stats->rx_undersize;
+	net_stats->tx_errors = mac_stats->tx_fcs_error +
+					mac_stats->tx_undersize;
+	net_stats->rx_dropped = mac_stats->rx_drop;
+	net_stats->tx_dropped = mac_stats->tx_drop;
+	net_stats->multicast = mac_stats->rx_multicast;
+	net_stats->collisions = mac_stats->tx_total_collision;
+
+	net_stats->rx_length_errors = mac_stats->rx_frame_length_error;
+
+	/* receive ring buffer overflow  ?? */
+
+	net_stats->rx_crc_errors = mac_stats->rx_fcs_error;
+	net_stats->rx_frame_errors = mac_stats->rx_alignment_error;
+	/* recv'r fifo overrun */
+	bmap = (u64)bnad->stats.bna_stats->rxf_bmap[0] |
+		((u64)bnad->stats.bna_stats->rxf_bmap[1] << 32);
+	for (i = 0; bmap && (i < BFI_LL_RXF_ID_MAX); i++) {
+		if (bmap & 1) {
+			net_stats->rx_fifo_errors =
+				bnad->stats.bna_stats->
+					hw_stats->rxf_stats[i].frame_drops;
+			break;
+		}
+		bmap >>= 1;
+	}
+}
+
+/*
+ * Used spin_lock to synchronize reading of stats structures, which
+ * is written by BNA under the same lock.
+ */
+struct net_device_stats *
+bnad_get_netdev_stats(struct net_device *netdev)
+{
+	struct bnad *bnad = netdev_priv(netdev);
+
+	spin_lock_irq(&bnad->bna_lock);
+
+	memset(&bnad->net_stats, 0, sizeof(struct net_device_stats));
+
+	bnad_netdev_qstats_fill(bnad);
+	bnad_netdev_hwstats_fill(bnad);
+
+	spin_unlock_irq(&bnad->bna_lock);
+
+	return &bnad->net_stats;
+}
+
+int
+bnad_stats_clr(struct bnad *bnad)
+{
+	int i, j;
+
+	memset(&bnad->stats.drv_stats, 0, sizeof(struct bnad_drv_stats));
+	memset(&bnad->net_stats, 0, sizeof(struct net_device_stats));
+	spin_lock_irq(&bnad->bna_lock);
+	for (i = 0; i < bnad->num_rx; i++) {
+		if (!bnad->rx_info[i].rx)
+			continue;
+		for (j = 0; j < bnad->num_rxp_per_rx; j++) {
+			if (bnad->rx_info[i].rx_ctrl[j].ccb) {
+				bnad->rx_info[i].rx_ctrl[j].ccb->
+					rcb[0]->rxq->rx_packets = 0;
+				bnad->rx_info[i].rx_ctrl[j].ccb->
+					rcb[0]->rxq->rx_bytes = 0;
+				bnad->rx_info[i].rx_ctrl[j].ccb->
+					rcb[0]->rxq->rx_packets_with_error = 0;
+				bnad->rx_info[i].rx_ctrl[j].ccb->
+					rcb[0]->rxq->rxbuf_alloc_failed = 0;
+				if (bnad->rx_info[i].rx_ctrl[j].ccb->rcb[1] &&
+					bnad->rx_info[i].rx_ctrl[j].ccb->
+					rcb[1]->rxq) {
+						bnad->rx_info[i].rx_ctrl[j].
+						ccb->rcb[1]->rxq->
+						rx_packets = 0;
+						bnad->rx_info[i].rx_ctrl[j].
+						ccb->rcb[1]->rxq->rx_bytes = 0;
+						bnad->rx_info[i].rx_ctrl[j].
+						ccb->rcb[1]->rxq->
+						rx_packets_with_error = 0;
+						bnad->rx_info[i].rx_ctrl[j].
+						ccb->rcb[1]->rxq->
+						rxbuf_alloc_failed = 0;
+				}
+			}
+		}
+	}
+	for (i = 0; i < bnad->num_tx; i++) {
+		if (!bnad->tx_info[i].tx)
+			continue;
+		for (j = 0; j < bnad->num_txq_per_tx; j++) {
+			if (bnad->tx_info[i].tcb[j]) {
+				bnad->tx_info[i].tcb[j]->txq->tx_packets = 0;
+				bnad->tx_info[i].tcb[j]->txq->tx_bytes = 0;
+			}
+		}
+	}
+	spin_unlock_irq(&bnad->bna_lock);
+	return 0;
+}
+
+static void
+bnad_mbox_irq_sync(struct bnad *bnad)
+{
+	u32 irq;
+
+	spin_lock_irq(&bnad->bna_lock);
+	if (bnad->cfg_flags & BNAD_CF_MSIX)
+		irq = bnad->msix_table[bnad->msix_num - 1].vector;
+	else
+		irq = bnad->pcidev->irq;
+	spin_unlock_irq(&bnad->bna_lock);
+
+	synchronize_irq(irq);
+}
+
+/* Netdev entry points */
+int
+bnad_open(struct net_device *netdev)
+{
+	int err;
+	struct bnad *bnad = netdev_priv(netdev);
+	struct bna_pause_config pause_config;
+	int mtu;
+
+	mutex_lock(&bnad->conf_mutex);
+
+	/* Tx */
+	err = bnad_setup_tx(bnad, 0);
+	if (err)
+		goto err_return;
+
+	/* Rx */
+	err = bnad_setup_rx(bnad, 0);
+	if (err)
+		goto cleanup_tx;
+
+	/* Port */
+	pause_config.tx_pause = 0;
+	pause_config.rx_pause = 0;
+
+	mtu = ETH_HLEN + bnad->netdev->mtu + ETH_FCS_LEN;
+
+	spin_lock_irq(&bnad->bna_lock);
+	bna_port_mtu_set(&bnad->bna.port, mtu, NULL);
+	bna_port_pause_config(&bnad->bna.port, &pause_config, NULL);
+	bna_port_enable(&bnad->bna.port);
+	spin_unlock_irq(&bnad->bna_lock);
+
+	/* Enable broadcast */
+	bnad_enable_default_bcast(bnad);
+
+	/* Set the UCAST address */
+	spin_lock_irq(&bnad->bna_lock);
+	bnad_mac_addr_set_locked(bnad, netdev->dev_addr);
+	spin_unlock_irq(&bnad->bna_lock);
+
+	/* Start the stats timer */
+	bnad_stats_timer_start(bnad);
+
+	mutex_unlock(&bnad->conf_mutex);
+
+	return 0;
+
+cleanup_tx:
+	bnad_cleanup_tx(bnad, 0);
+
+err_return:
+	mutex_unlock(&bnad->conf_mutex);
+	return err;
+}
+
+int
+bnad_stop(struct net_device *netdev)
+{
+	struct bnad *bnad = netdev_priv(netdev);
+	mutex_lock(&bnad->conf_mutex);
+
+	/* Stop the stats timer */
+	bnad_stats_timer_stop(bnad);
+
+	init_completion(&bnad->bnad_completions.port_comp);
+
+	spin_lock_irq(&bnad->bna_lock);
+	bna_port_disable(&bnad->bna.port, BNA_HARD_CLEANUP,
+			bnad_cb_port_disabled);
+	spin_unlock_irq(&bnad->bna_lock);
+
+	wait_for_completion(&bnad->bnad_completions.port_comp);
+
+	bnad_cleanup_tx(bnad, 0);
+	bnad_cleanup_rx(bnad, 0);
+
+	/* Synchronize mailbox IRQ */
+	bnad_mbox_irq_sync(bnad);
+
+	mutex_unlock(&bnad->conf_mutex);
+
+	return 0;
+}
+
+void
+bnad_set_rx_mode(struct net_device *netdev)
+{
+	struct bnad *bnad = netdev_priv(netdev);
+	u32	new_mask, valid_mask;
+
+	spin_lock_irq(&bnad->bna_lock);
+
+	new_mask = valid_mask = 0;
+
+	if (netdev->flags & IFF_PROMISC) {
+		if (!(bnad->cfg_flags & BNAD_CF_PROMISC)) {
+			new_mask = BNAD_RXMODE_PROMISC_DEFAULT;
+			valid_mask = BNAD_RXMODE_PROMISC_DEFAULT;
+			bnad->cfg_flags |= BNAD_CF_PROMISC;
+		}
+	} else {
+		if (bnad->cfg_flags & BNAD_CF_PROMISC) {
+			new_mask = ~BNAD_RXMODE_PROMISC_DEFAULT;
+			valid_mask = BNAD_RXMODE_PROMISC_DEFAULT;
+			bnad->cfg_flags &= ~BNAD_CF_PROMISC;
+		}
+	}
+
+	if (netdev->flags & IFF_ALLMULTI) {
+		if (!(bnad->cfg_flags & BNAD_CF_ALLMULTI)) {
+			new_mask |= BNA_RXMODE_ALLMULTI;
+			valid_mask |= BNA_RXMODE_ALLMULTI;
+			bnad->cfg_flags |= BNAD_CF_ALLMULTI;
+		}
+	} else {
+		if (bnad->cfg_flags & BNAD_CF_ALLMULTI) {
+			new_mask &= ~BNA_RXMODE_ALLMULTI;
+			valid_mask |= BNA_RXMODE_ALLMULTI;
+			bnad->cfg_flags &= ~BNAD_CF_ALLMULTI;
+		}
+	}
+
+	bna_rx_mode_set(bnad->rx_info[0].rx, new_mask, valid_mask, NULL);
+
+	if (!netdev_mc_empty(netdev)) {
+		u8 *mcaddr_list;
+		int mc_count = netdev_mc_count(netdev);
+
+		/* Index 0 holds the broadcast address */
+		mcaddr_list =
+			kzalloc((mc_count + 1) * ETH_ALEN,
+				GFP_ATOMIC);
+		if (!mcaddr_list)
+			return;
+
+		memcpy(&mcaddr_list[0], &bnad_bcast_addr[0], ETH_ALEN);
+
+		/* Copy rest of the MC addresses */
+		bnad_netdev_mc_list_get(netdev, mcaddr_list);
+
+		bna_rx_mcast_listset(bnad->rx_info[0].rx, mc_count + 1,
+					mcaddr_list, NULL);
+
+		/* Should we enable BNAD_CF_ALLMULTI for err != 0 ? */
+		kfree(mcaddr_list);
+	}
+	spin_unlock_irq(&bnad->bna_lock);
+}
+
+/*
+ * bna_lock is used to sync writes to netdev->addr
+ * conf_lock cannot be used since this call may be made
+ * in a non-blocking context.
+ */
+int
+bnad_set_mac_address(struct net_device *netdev, void *mac_addr)
+{
+	int err;
+	struct bnad *bnad = netdev_priv(netdev);
+	struct sockaddr *sa = (struct sockaddr *)mac_addr;
+
+	spin_lock_irq(&bnad->bna_lock);
+
+	err = bnad_mac_addr_set_locked(bnad, sa->sa_data);
+
+	if (!err)
+		memcpy(netdev->dev_addr, sa->sa_data, netdev->addr_len);
+
+	spin_unlock_irq(&bnad->bna_lock);
+
+	return err;
+}
+
+int
+bnad_change_mtu(struct net_device *netdev, int new_mtu)
+{
+	int mtu, err = 0;
+
+	struct bnad *bnad = netdev_priv(netdev);
+
+	if (new_mtu + ETH_HLEN < ETH_ZLEN || new_mtu > BNAD_JUMBO_MTU)
+		return -EINVAL;
+
+	mutex_lock(&bnad->conf_mutex);
+
+	netdev->mtu = new_mtu;
+
+	mtu = ETH_HLEN + new_mtu + ETH_FCS_LEN;
+
+	spin_lock_irq(&bnad->bna_lock);
+	bna_port_mtu_set(&bnad->bna.port, mtu, NULL);
+	spin_unlock_irq(&bnad->bna_lock);
+
+	mutex_unlock(&bnad->conf_mutex);
+	return err;
+}
+
+void
+bnad_vlan_rx_register(struct net_device *netdev,
+				  struct vlan_group *vlan_grp)
+{
+	struct bnad *bnad = netdev_priv(netdev);
+
+	mutex_lock(&bnad->conf_mutex);
+	bnad->vlan_grp = vlan_grp;
+	mutex_unlock(&bnad->conf_mutex);
+}
+
+void
+bnad_vlan_rx_add_vid(struct net_device *netdev,
+				 unsigned short vid)
+{
+	struct bnad *bnad = netdev_priv(netdev);
+
+	if (!bnad->rx_info[0].rx)
+		return;
+
+	mutex_lock(&bnad->conf_mutex);
+
+	spin_lock_irq(&bnad->bna_lock);
+	bna_rx_vlan_add(bnad->rx_info[0].rx, vid);
+	spin_unlock_irq(&bnad->bna_lock);
+
+	mutex_unlock(&bnad->conf_mutex);
+}
+
+void
+bnad_vlan_rx_kill_vid(struct net_device *netdev,
+				  unsigned short vid)
+{
+	struct bnad *bnad = netdev_priv(netdev);
+
+	if (!bnad->rx_info[0].rx)
+		return;
+
+	mutex_lock(&bnad->conf_mutex);
+
+	spin_lock_irq(&bnad->bna_lock);
+	bna_rx_vlan_del(bnad->rx_info[0].rx, vid);
+	spin_unlock_irq(&bnad->bna_lock);
+
+	mutex_unlock(&bnad->conf_mutex);
+}
+
+static struct pci_driver bnad_pci_driver = {
+	.name = BNAD_NAME,
+	.id_table = bnad_pci_id_table,
+	.probe = bnad_pci_probe,
+	.remove = __devexit_p(bnad_pci_remove),
+};
+
+static const struct net_device_ops bnad_netdev_ops = {
+	.ndo_open		= bnad_open,
+	.ndo_stop		= bnad_stop,
+	.ndo_start_xmit		= bnad_start_xmit,
+	.ndo_get_stats		= bnad_get_netdev_stats,
+	.ndo_set_rx_mode	= bnad_set_rx_mode,
+	.ndo_set_multicast_list = bnad_set_rx_mode,
+	.ndo_validate_addr      = eth_validate_addr,
+	.ndo_set_mac_address    = bnad_set_mac_address,
+	.ndo_change_mtu		= bnad_change_mtu,
+	.ndo_vlan_rx_register   = bnad_vlan_rx_register,
+	.ndo_vlan_rx_add_vid    = bnad_vlan_rx_add_vid,
+	.ndo_vlan_rx_kill_vid   = bnad_vlan_rx_kill_vid,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+	.ndo_poll_controller    = bnad_netpoll
+#endif
+};
+
+void
+bnad_netdev_init(struct bnad *bnad, bool using_dac)
+{
+	struct net_device *netdev = bnad->netdev;
+
+	netdev->features |= NETIF_F_IPV6_CSUM;
+	netdev->features |= NETIF_F_TSO;
+	netdev->features |= NETIF_F_TSO6;
+
+	netdev->features |= NETIF_F_GRO;
+	printk(KERN_WARNING
+	       "bna: GRO enabled, using kernel stack GRO\n");
+
+	netdev->features |= NETIF_F_SG | NETIF_F_IP_CSUM;
+
+	if (using_dac)
+		netdev->features |= NETIF_F_HIGHDMA;
+
+	netdev->features |=
+		NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX |
+		NETIF_F_HW_VLAN_FILTER;
+
+	netdev->vlan_features = netdev->features;
+	netdev->mem_start = bnad->mmio_start;
+	netdev->mem_end = bnad->mmio_start + bnad->mmio_len - 1;
+
+	netdev->netdev_ops = &bnad_netdev_ops;
+	bnad_set_ethtool_ops(netdev);
+}
+
+void
+bnad_set_netdev_perm_addr(struct bnad *bnad)
+{
+	struct net_device *netdev = bnad->netdev;
+
+	memcpy(netdev->perm_addr, &bnad->perm_addr, netdev->addr_len);
+	if (is_zero_ether_addr(netdev->dev_addr))
+		memcpy(netdev->dev_addr, &bnad->perm_addr, netdev->addr_len);
+}
+
+#ifdef CONFIG_NET_POLL_CONTROLLER
+void
+bnad_netpoll(struct net_device *netdev)
+{
+	struct bnad *bnad = netdev_priv(netdev);
+	struct bnad_rx_info *rx_info;
+	struct bnad_rx_ctrl *rx_ctrl;
+	u32 curr_mask;
+	int i, j;
+
+	if (!(bnad->cfg_flags & BNAD_CF_MSIX)) {
+		bna_intx_disable(&bnad->bna, curr_mask);
+		bnad_isr(bnad->pcidev->irq, netdev);
+		bna_intx_enable(&bnad->bna, curr_mask);
+	} else {
+		for (i = 0; i < bnad->num_rx; i++) {
+			rx_info = &bnad->rx_info[i];
+			if (!rx_info->rx)
+				continue;
+			for (j = 0; j < bnad->num_rxp_per_rx; j++) {
+				rx_ctrl = &rx_info->rx_ctrl[j];
+				if (rx_ctrl->ccb) {
+					bnad_disable_rx_irq(bnad,
+							    rx_ctrl->ccb);
+					bnad_netif_rx_schedule_poll(bnad,
+							    rx_ctrl->ccb);
+				}
+			}
+		}
+	}
+}
+#endif
+
+/* Utility used by bnad_start_xmit, for doing TSO */
+int
+bnad_tso_prepare(struct bnad *bnad, struct sk_buff *skb)
+{
+	int err;
+
+	/* SKB_GSO_TCPV4 and SKB_GSO_TCPV6 is defined since 2.6.18. */
+	BUG_ON(!(skb_shinfo(skb)->gso_type == SKB_GSO_TCPV4 ||
+		   skb_shinfo(skb)->gso_type == SKB_GSO_TCPV6));
+	if (skb_header_cloned(skb)) {
+		err = pskb_expand_head(skb, 0, 0, GFP_ATOMIC);
+		if (err) {
+			BNAD_UPDATE_CTR(bnad, tso_err);
+			return err;
+		}
+	}
+
+	/*
+	 * For TSO, the TCP checksum field is seeded with pseudo-header sum
+	 * excluding the length field.
+	 */
+	if (skb->protocol == htons(ETH_P_IP)) {
+		struct iphdr *iph = ip_hdr(skb);
+
+		/* Do we really need these? */
+		iph->tot_len = 0;
+		iph->check = 0;
+
+		tcp_hdr(skb)->check =
+			~csum_tcpudp_magic(iph->saddr, iph->daddr, 0,
+					   IPPROTO_TCP, 0);
+		BNAD_UPDATE_CTR(bnad, tso4);
+	} else {
+		struct ipv6hdr *ipv6h = ipv6_hdr(skb);
+
+		BUG_ON(!(skb->protocol == htons(ETH_P_IPV6)));
+		ipv6h->payload_len = 0;
+		tcp_hdr(skb)->check =
+			~csum_ipv6_magic(&ipv6h->saddr, &ipv6h->daddr, 0,
+					 IPPROTO_TCP, 0);
+		BNAD_UPDATE_CTR(bnad, tso6);
+	}
+
+	return 0;
+}
+
+static int
+bnad_napi_poll_rx(struct napi_struct *napi, int budget)
+{
+	struct bnad_rx_ctrl *rx_ctrl =
+		container_of(napi, struct bnad_rx_ctrl, napi);
+	struct bna_ccb *ccb;
+	struct bnad *bnad;
+	int rcvd = 0;
+
+	ccb = rx_ctrl->ccb;
+
+	bnad = ccb->bnad;
+
+	if (!netif_carrier_ok(bnad->netdev))
+		goto poll_exit;
+
+	rcvd = bnad_poll_cq(bnad, ccb, budget);
+	if (rcvd == budget)
+		return rcvd;
+
+poll_exit:
+	napi_complete((napi));
+
+	BNAD_UPDATE_CTR(bnad, netif_rx_complete);
+
+	bnad_enable_rx_irq(bnad, ccb);
+	return rcvd;
+}
+
+static int
+bnad_napi_poll_txrx(struct napi_struct *napi, int budget)
+{
+	struct bnad_rx_ctrl *rx_ctrl =
+		container_of(napi, struct bnad_rx_ctrl, napi);
+	struct bna_ccb *ccb;
+	struct bnad *bnad;
+	int 			rcvd = 0;
+	int			i, j;
+
+	ccb = rx_ctrl->ccb;
+
+	bnad = ccb->bnad;
+
+	if (!netif_carrier_ok(bnad->netdev))
+		goto poll_exit;
+
+	/* Handle Tx Completions, if any */
+	for (i = 0; i < bnad->num_tx; i++) {
+		for (j = 0; j < bnad->num_txq_per_tx; j++)
+			bnad_tx(bnad, bnad->tx_info[i].tcb[j]);
+	}
+
+	/* Handle Rx Completions */
+	rcvd = bnad_poll_cq(bnad, ccb, budget);
+	if (rcvd == budget)
+		return rcvd;
+poll_exit:
+	napi_complete((napi));
+
+	BNAD_UPDATE_CTR(bnad, netif_rx_complete);
+
+	bnad_enable_txrx_irqs(bnad);
+	return rcvd;
+}
+void
+bnad_napi_enable(struct bnad *bnad, u32 rx_id)
+{
+	int (*napi_poll) (struct napi_struct *, int);
+	struct bnad_rx_ctrl *rx_ctrl;
+	int i;
+
+	spin_lock_irq(&bnad->bna_lock);
+	if (bnad->cfg_flags & BNAD_CF_MSIX)
+		napi_poll = bnad_napi_poll_rx;
+	else
+		napi_poll = bnad_napi_poll_txrx;
+	spin_unlock_irq(&bnad->bna_lock);
+
+	/* Initialize & enable NAPI */
+	for (i = 0; i <	bnad->num_rxp_per_rx; i++) {
+		rx_ctrl = &bnad->rx_info[rx_id].rx_ctrl[i];
+		netif_napi_add(bnad->netdev, &rx_ctrl->napi,
+			       napi_poll, 64);
+		napi_enable(&rx_ctrl->napi);
+	}
+}
+
+void
+bnad_napi_disable(struct bnad *bnad, u32 rx_id)
+{
+	int i;
+
+	/* First disable and then clean up */
+	for (i = 0; i < bnad->num_rxp_per_rx; i++) {
+		napi_disable(&bnad->rx_info[rx_id].rx_ctrl[i].napi);
+		netif_napi_del(&bnad->rx_info[rx_id].rx_ctrl[i].napi);
+	}
+}
+
+void
+bnad_netif_rx_schedule_poll(struct bnad *bnad, struct bna_ccb *ccb)
+{
+	struct bnad_rx_ctrl *rx_ctrl = (struct bnad_rx_ctrl *)(ccb->ctrl);
+	if (likely(napi_schedule_prep((&rx_ctrl->napi)))) {
+		bnad_disable_rx_irq(bnad, ccb);
+		__napi_schedule((&rx_ctrl->napi));
+	}
+	BNAD_UPDATE_CTR(bnad, netif_rx_schedule);
+}
+
+/*
+ * Initialize Q numbers depending on Rx Paths
+ * Called with bnad->bna_lock held, because of cfg_flags
+ * access.
+ */
+void
+bnad_q_num_init(struct bnad *bnad)
+{
+	int rxps;
+
+	BNAD_GET_NUM_RXP(rxps);
+
+	if (!(bnad->cfg_flags & BNAD_CF_MSIX))
+		rxps = 1;	/* INTx */
+
+	bnad->num_rx = 1;
+	bnad->num_tx = 1;
+	bnad->num_rxp_per_rx = rxps;
+	bnad->num_txq_per_tx = BNAD_TXQ_NUM;
+}
+
+/*
+ * Adjusts the Q numbers, given a number of msix vectors
+ * Give preference to RSS as opposed to Tx priority Queues,
+ * in such a case, just use 1 Tx Q
+ * Called with bnad->bna_lock held b'cos of cfg_flags access
+ */
+void
+bnad_q_num_adjust(struct bnad *bnad, int msix_vectors)
+{
+	bnad->num_txq_per_tx = 1;
+	if ((msix_vectors >= (bnad->num_tx * bnad->num_txq_per_tx)  +
+	     bnad_rxqs_per_cq + BNAD_MAILBOX_MSIX_VECTORS) &&
+	    (bnad->cfg_flags & BNAD_CF_MSIX)) {
+		bnad->num_rxp_per_rx = msix_vectors -
+			(bnad->num_tx * bnad->num_txq_per_tx) -
+			BNAD_MAILBOX_MSIX_VECTORS;
+	} else
+		bnad->num_rxp_per_rx = 1;
+}
+
+static int __init
+bnad_module_init(void)
+{
+	int err;
+
+	printk(KERN_INFO "Brocade 10G Ethernet driver\n");
+
+	bfa_ioc_auto_recover(bnad_ioc_auto_recover);
+
+	err = pci_register_driver(&bnad_pci_driver);
+	if (err < 0) {
+		printk(KERN_ERR "bna : PCI registration failed in module init "
+		       "(%d)\n", err);
+		return err;
+	}
+
+	return 0;
+}
+
+static void __exit
+bnad_module_exit(void)
+{
+	pci_unregister_driver(&bnad_pci_driver);
+
+	if (bfi_fw)
+		release_firmware(bfi_fw);
+}
+
+module_init(bnad_module_init);
+module_exit(bnad_module_exit);
+
+MODULE_AUTHOR("Brocade");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Brocade 10G PCIe Ethernet driver");
+MODULE_VERSION(BNAD_VERSION);
+MODULE_FIRMWARE(CNA_FW_FILE_CT);
diff -ruP net-next-2.6.35-rc1-orig/drivers/net/bna/bnad_ethtool.c net-next-2.6.35-rc1-mod/drivers/net/bna/bnad_ethtool.c
--- net-next-2.6.35-rc1-orig/drivers/net/bna/bnad_ethtool.c	1969-12-31 16:00:00.000000000 -0800
+++ net-next-2.6.35-rc1-mod/drivers/net/bna/bnad_ethtool.c	2010-08-02 17:19:19.475230000 -0700
@@ -0,0 +1,1269 @@
+/*
+ * Linux network driver for Brocade Converged Network Adapter.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License (GPL) Version 2 as
+ * published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+/*
+ * Copyright (c) 2005-2009 Brocade Communications Systems, Inc.
+ * All rights reserved
+ * www.brocade.com
+ */
+
+#include "cna.h"
+
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <linux/ethtool.h>
+#include <linux/rtnetlink.h>
+
+#include "bna.h"
+
+#include "bnad.h"
+
+#define BNAD_NUM_TXF_COUNTERS 12
+#define BNAD_NUM_RXF_COUNTERS 10
+#define BNAD_NUM_CQ_COUNTERS 3
+#define BNAD_NUM_RXQ_COUNTERS 6
+#define BNAD_NUM_TXQ_COUNTERS 5
+
+#define BNAD_ETHTOOL_STATS_NUM						\
+	(sizeof(struct net_device_stats) / sizeof(unsigned long) +	\
+	sizeof(struct bnad_drv_stats) / sizeof(u64) +		\
+	offsetof(struct bfi_ll_stats, rxf_stats[0]) / sizeof(u64))
+
+static char *bnad_net_stats_strings[BNAD_ETHTOOL_STATS_NUM] = {
+	"rx_packets",
+	"tx_packets",
+	"rx_bytes",
+	"tx_bytes",
+	"rx_errors",
+	"tx_errors",
+	"rx_dropped",
+	"tx_dropped",
+	"multicast",
+	"collisions",
+
+	"rx_length_errors",
+	"rx_over_errors",
+	"rx_crc_errors",
+	"rx_frame_errors",
+	"rx_fifo_errors",
+	"rx_missed_errors",
+
+	"tx_aborted_errors",
+	"tx_carrier_errors",
+	"tx_fifo_errors",
+	"tx_heartbeat_errors",
+	"tx_window_errors",
+
+	"rx_compressed",
+	"tx_compressed",
+
+	"netif_queue_stop",
+	"netif_queue_wakeup",
+	"tso4",
+	"tso6",
+	"tso_err",
+	"tcpcsum_offload",
+	"udpcsum_offload",
+	"csum_help",
+	"csum_help_err",
+	"hw_stats_updates",
+	"netif_rx_schedule",
+	"netif_rx_complete",
+	"netif_rx_dropped",
+
+	"link_toggle",
+	"cee_up",
+
+	"rxp_info_alloc_failed",
+	"mbox_intr_disabled",
+	"mbox_intr_enabled",
+	"tx_unmap_q_alloc_failed",
+	"rx_unmap_q_alloc_failed",
+	"rxbuf_alloc_failed",
+
+	"mac_frame_64",
+	"mac_frame_65_127",
+	"mac_frame_128_255",
+	"mac_frame_256_511",
+	"mac_frame_512_1023",
+	"mac_frame_1024_1518",
+	"mac_frame_1518_1522",
+	"mac_rx_bytes",
+	"mac_rx_packets",
+	"mac_rx_fcs_error",
+	"mac_rx_multicast",
+	"mac_rx_broadcast",
+	"mac_rx_control_frames",
+	"mac_rx_pause",
+	"mac_rx_unknown_opcode",
+	"mac_rx_alignment_error",
+	"mac_rx_frame_length_error",
+	"mac_rx_code_error",
+	"mac_rx_carrier_sense_error",
+	"mac_rx_undersize",
+	"mac_rx_oversize",
+	"mac_rx_fragments",
+	"mac_rx_jabber",
+	"mac_rx_drop",
+
+	"mac_tx_bytes",
+	"mac_tx_packets",
+	"mac_tx_multicast",
+	"mac_tx_broadcast",
+	"mac_tx_pause",
+	"mac_tx_deferral",
+	"mac_tx_excessive_deferral",
+	"mac_tx_single_collision",
+	"mac_tx_muliple_collision",
+	"mac_tx_late_collision",
+	"mac_tx_excessive_collision",
+	"mac_tx_total_collision",
+	"mac_tx_pause_honored",
+	"mac_tx_drop",
+	"mac_tx_jabber",
+	"mac_tx_fcs_error",
+	"mac_tx_control_frame",
+	"mac_tx_oversize",
+	"mac_tx_undersize",
+	"mac_tx_fragments",
+
+	"bpc_tx_pause_0",
+	"bpc_tx_pause_1",
+	"bpc_tx_pause_2",
+	"bpc_tx_pause_3",
+	"bpc_tx_pause_4",
+	"bpc_tx_pause_5",
+	"bpc_tx_pause_6",
+	"bpc_tx_pause_7",
+	"bpc_tx_zero_pause_0",
+	"bpc_tx_zero_pause_1",
+	"bpc_tx_zero_pause_2",
+	"bpc_tx_zero_pause_3",
+	"bpc_tx_zero_pause_4",
+	"bpc_tx_zero_pause_5",
+	"bpc_tx_zero_pause_6",
+	"bpc_tx_zero_pause_7",
+	"bpc_tx_first_pause_0",
+	"bpc_tx_first_pause_1",
+	"bpc_tx_first_pause_2",
+	"bpc_tx_first_pause_3",
+	"bpc_tx_first_pause_4",
+	"bpc_tx_first_pause_5",
+	"bpc_tx_first_pause_6",
+	"bpc_tx_first_pause_7",
+
+	"bpc_rx_pause_0",
+	"bpc_rx_pause_1",
+	"bpc_rx_pause_2",
+	"bpc_rx_pause_3",
+	"bpc_rx_pause_4",
+	"bpc_rx_pause_5",
+	"bpc_rx_pause_6",
+	"bpc_rx_pause_7",
+	"bpc_rx_zero_pause_0",
+	"bpc_rx_zero_pause_1",
+	"bpc_rx_zero_pause_2",
+	"bpc_rx_zero_pause_3",
+	"bpc_rx_zero_pause_4",
+	"bpc_rx_zero_pause_5",
+	"bpc_rx_zero_pause_6",
+	"bpc_rx_zero_pause_7",
+	"bpc_rx_first_pause_0",
+	"bpc_rx_first_pause_1",
+	"bpc_rx_first_pause_2",
+	"bpc_rx_first_pause_3",
+	"bpc_rx_first_pause_4",
+	"bpc_rx_first_pause_5",
+	"bpc_rx_first_pause_6",
+	"bpc_rx_first_pause_7",
+
+	"rad_rx_frames",
+	"rad_rx_octets",
+	"rad_rx_vlan_frames",
+	"rad_rx_ucast",
+	"rad_rx_ucast_octets",
+	"rad_rx_ucast_vlan",
+	"rad_rx_mcast",
+	"rad_rx_mcast_octets",
+	"rad_rx_mcast_vlan",
+	"rad_rx_bcast",
+	"rad_rx_bcast_octets",
+	"rad_rx_bcast_vlan",
+	"rad_rx_drops",
+
+	"fc_rx_ucast_octets",
+	"fc_rx_ucast",
+	"fc_rx_ucast_vlan",
+	"fc_rx_mcast_octets",
+	"fc_rx_mcast",
+	"fc_rx_mcast_vlan",
+	"fc_rx_bcast_octets",
+	"fc_rx_bcast",
+	"fc_rx_bcast_vlan",
+
+	"fc_tx_ucast_octets",
+	"fc_tx_ucast",
+	"fc_tx_ucast_vlan",
+	"fc_tx_mcast_octets",
+	"fc_tx_mcast",
+	"fc_tx_mcast_vlan",
+	"fc_tx_bcast_octets",
+	"fc_tx_bcast",
+	"fc_tx_bcast_vlan",
+	"fc_tx_parity_errors",
+	"fc_tx_timeout",
+	"fc_tx_fid_parity_errors",
+};
+
+static int
+bnad_get_settings(struct net_device *netdev, struct ethtool_cmd *cmd)
+{
+	cmd->supported = SUPPORTED_10000baseT_Full;
+	cmd->advertising = ADVERTISED_10000baseT_Full;
+	cmd->autoneg = AUTONEG_DISABLE;
+	cmd->supported |= SUPPORTED_FIBRE;
+	cmd->advertising |= ADVERTISED_FIBRE;
+	cmd->port = PORT_FIBRE;
+	cmd->phy_address = 0;
+
+	if (netif_carrier_ok(netdev)) {
+		cmd->speed = SPEED_10000;
+		cmd->duplex = DUPLEX_FULL;
+	} else {
+		cmd->speed = -1;
+		cmd->duplex = -1;
+	}
+	cmd->transceiver = XCVR_EXTERNAL;
+	cmd->maxtxpkt = 0;
+	cmd->maxrxpkt = 0;
+
+	return 0;
+}
+
+static int
+bnad_set_settings(struct net_device *netdev, struct ethtool_cmd *cmd)
+{
+	/* 10G full duplex setting supported only */
+	if (cmd->autoneg == AUTONEG_ENABLE)
+		return -EOPNOTSUPP; else {
+		if ((cmd->speed == SPEED_10000) && (cmd->duplex == DUPLEX_FULL))
+			return 0;
+	}
+
+	return -EOPNOTSUPP;
+}
+
+static void
+bnad_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *drvinfo)
+{
+	struct bnad *bnad = netdev_priv(netdev);
+	struct bfa_ioc_attr *ioc_attr;
+
+	strcpy(drvinfo->driver, BNAD_NAME);
+	strcpy(drvinfo->version, BNAD_VERSION);
+
+	ioc_attr = kzalloc(sizeof(*ioc_attr), GFP_KERNEL);
+	if (ioc_attr) {
+		memset(ioc_attr, 0, sizeof(*ioc_attr));
+		spin_lock_irq(&bnad->bna_lock);
+		bfa_ioc_get_attr(&bnad->bna.device.ioc, ioc_attr);
+		spin_unlock_irq(&bnad->bna_lock);
+
+		strncpy(drvinfo->fw_version, ioc_attr->adapter_attr.fw_ver,
+			sizeof(drvinfo->fw_version) - 1);
+		kfree(ioc_attr);
+	}
+
+	strncpy(drvinfo->bus_info, pci_name(bnad->pcidev), ETHTOOL_BUSINFO_LEN);
+}
+
+static int
+get_regs(struct bnad *bnad, u32 * regs)
+{
+	int num = 0, i;
+	u32 reg_addr;
+
+#define BNAD_GET_REG(addr) 					\
+do {								\
+	if (regs)						\
+		regs[num++] = readl(bnad->bar0 + (addr));	\
+	else							\
+		num++;						\
+} while (0)
+
+	spin_lock_irq(&bnad->bna_lock);
+
+	/* DMA Block Internal Registers */
+	BNAD_GET_REG(DMA_CTRL_REG0);
+	BNAD_GET_REG(DMA_CTRL_REG1);
+	BNAD_GET_REG(DMA_ERR_INT_STATUS);
+	BNAD_GET_REG(DMA_ERR_INT_ENABLE);
+	BNAD_GET_REG(DMA_ERR_INT_STATUS_SET);
+
+	/* APP Block Register Address Offset from BAR0 */
+	BNAD_GET_REG(HOSTFN0_INT_STATUS);
+	BNAD_GET_REG(HOSTFN0_INT_MASK);
+	BNAD_GET_REG(HOST_PAGE_NUM_FN0);
+	BNAD_GET_REG(HOST_MSIX_ERR_INDEX_FN0);
+	BNAD_GET_REG(FN0_PCIE_ERR_REG);
+	BNAD_GET_REG(FN0_ERR_TYPE_STATUS_REG);
+	BNAD_GET_REG(FN0_ERR_TYPE_MSK_STATUS_REG);
+
+	BNAD_GET_REG(HOSTFN1_INT_STATUS);
+	BNAD_GET_REG(HOSTFN1_INT_MASK);
+	BNAD_GET_REG(HOST_PAGE_NUM_FN1);
+	BNAD_GET_REG(HOST_MSIX_ERR_INDEX_FN1);
+	BNAD_GET_REG(FN1_PCIE_ERR_REG);
+	BNAD_GET_REG(FN1_ERR_TYPE_STATUS_REG);
+	BNAD_GET_REG(FN1_ERR_TYPE_MSK_STATUS_REG);
+
+	BNAD_GET_REG(PCIE_MISC_REG);
+
+	BNAD_GET_REG(HOST_SEM0_REG);
+	BNAD_GET_REG(HOST_SEM1_REG);
+	BNAD_GET_REG(HOST_SEM2_REG);
+	BNAD_GET_REG(HOST_SEM3_REG);
+	BNAD_GET_REG(HOST_SEM0_INFO_REG);
+	BNAD_GET_REG(HOST_SEM1_INFO_REG);
+	BNAD_GET_REG(HOST_SEM2_INFO_REG);
+	BNAD_GET_REG(HOST_SEM3_INFO_REG);
+
+	BNAD_GET_REG(TEMPSENSE_CNTL_REG);
+	BNAD_GET_REG(TEMPSENSE_STAT_REG);
+
+	BNAD_GET_REG(APP_LOCAL_ERR_STAT);
+	BNAD_GET_REG(APP_LOCAL_ERR_MSK);
+
+	BNAD_GET_REG(PCIE_LNK_ERR_STAT);
+	BNAD_GET_REG(PCIE_LNK_ERR_MSK);
+
+	BNAD_GET_REG(FCOE_FIP_ETH_TYPE);
+	BNAD_GET_REG(RESV_ETH_TYPE);
+
+	BNAD_GET_REG(HOSTFN2_INT_STATUS);
+	BNAD_GET_REG(HOSTFN2_INT_MASK);
+	BNAD_GET_REG(HOST_PAGE_NUM_FN2);
+	BNAD_GET_REG(HOST_MSIX_ERR_INDEX_FN2);
+	BNAD_GET_REG(FN2_PCIE_ERR_REG);
+	BNAD_GET_REG(FN2_ERR_TYPE_STATUS_REG);
+	BNAD_GET_REG(FN2_ERR_TYPE_MSK_STATUS_REG);
+
+	BNAD_GET_REG(HOSTFN3_INT_STATUS);
+	BNAD_GET_REG(HOSTFN3_INT_MASK);
+	BNAD_GET_REG(HOST_PAGE_NUM_FN3);
+	BNAD_GET_REG(HOST_MSIX_ERR_INDEX_FN3);
+	BNAD_GET_REG(FN3_PCIE_ERR_REG);
+	BNAD_GET_REG(FN3_ERR_TYPE_STATUS_REG);
+	BNAD_GET_REG(FN3_ERR_TYPE_MSK_STATUS_REG);
+
+	/* Host Command Status Registers */
+	reg_addr = HOST_CMDSTS0_CLR_REG;
+	for (i = 0; i < 16; i++) {
+		BNAD_GET_REG(reg_addr);
+		BNAD_GET_REG(reg_addr + 4);
+		BNAD_GET_REG(reg_addr + 8);
+		reg_addr += 0x10;
+	}
+
+	/* Function ID register */
+	BNAD_GET_REG(FNC_ID_REG);
+
+	/* Function personality register */
+	BNAD_GET_REG(FNC_PERS_REG);
+
+	/* Operation mode register */
+	BNAD_GET_REG(OP_MODE);
+
+	/* LPU0 Registers */
+	BNAD_GET_REG(LPU0_MBOX_CTL_REG);
+	BNAD_GET_REG(LPU0_MBOX_CMD_REG);
+	BNAD_GET_REG(LPU0_MBOX_LINK_0REG);
+	BNAD_GET_REG(LPU1_MBOX_LINK_0REG);
+	BNAD_GET_REG(LPU0_MBOX_STATUS_0REG);
+	BNAD_GET_REG(LPU1_MBOX_STATUS_0REG);
+	BNAD_GET_REG(LPU0_ERR_STATUS_REG);
+	BNAD_GET_REG(LPU0_ERR_SET_REG);
+
+	/* LPU1 Registers */
+	BNAD_GET_REG(LPU1_MBOX_CTL_REG);
+	BNAD_GET_REG(LPU1_MBOX_CMD_REG);
+	BNAD_GET_REG(LPU0_MBOX_LINK_1REG);
+	BNAD_GET_REG(LPU1_MBOX_LINK_1REG);
+	BNAD_GET_REG(LPU0_MBOX_STATUS_1REG);
+	BNAD_GET_REG(LPU1_MBOX_STATUS_1REG);
+	BNAD_GET_REG(LPU1_ERR_STATUS_REG);
+	BNAD_GET_REG(LPU1_ERR_SET_REG);
+
+	/* PSS Registers */
+	BNAD_GET_REG(PSS_CTL_REG);
+	BNAD_GET_REG(PSS_ERR_STATUS_REG);
+	BNAD_GET_REG(ERR_STATUS_SET);
+	BNAD_GET_REG(PSS_RAM_ERR_STATUS_REG);
+
+	/* Catapult CPQ Registers */
+	BNAD_GET_REG(HOSTFN0_LPU0_MBOX0_CMD_STAT);
+	BNAD_GET_REG(HOSTFN0_LPU1_MBOX0_CMD_STAT);
+	BNAD_GET_REG(LPU0_HOSTFN0_MBOX0_CMD_STAT);
+	BNAD_GET_REG(LPU1_HOSTFN0_MBOX0_CMD_STAT);
+
+	BNAD_GET_REG(HOSTFN0_LPU0_MBOX1_CMD_STAT);
+	BNAD_GET_REG(HOSTFN0_LPU1_MBOX1_CMD_STAT);
+	BNAD_GET_REG(LPU0_HOSTFN0_MBOX1_CMD_STAT);
+	BNAD_GET_REG(LPU1_HOSTFN0_MBOX1_CMD_STAT);
+
+	BNAD_GET_REG(HOSTFN1_LPU0_MBOX0_CMD_STAT);
+	BNAD_GET_REG(HOSTFN1_LPU1_MBOX0_CMD_STAT);
+	BNAD_GET_REG(LPU0_HOSTFN1_MBOX0_CMD_STAT);
+	BNAD_GET_REG(LPU1_HOSTFN1_MBOX0_CMD_STAT);
+
+	BNAD_GET_REG(HOSTFN1_LPU0_MBOX1_CMD_STAT);
+	BNAD_GET_REG(HOSTFN1_LPU1_MBOX1_CMD_STAT);
+	BNAD_GET_REG(LPU0_HOSTFN1_MBOX1_CMD_STAT);
+	BNAD_GET_REG(LPU1_HOSTFN1_MBOX1_CMD_STAT);
+
+	BNAD_GET_REG(HOSTFN2_LPU0_MBOX0_CMD_STAT);
+	BNAD_GET_REG(HOSTFN2_LPU1_MBOX0_CMD_STAT);
+	BNAD_GET_REG(LPU0_HOSTFN2_MBOX0_CMD_STAT);
+	BNAD_GET_REG(LPU1_HOSTFN2_MBOX0_CMD_STAT);
+
+	BNAD_GET_REG(HOSTFN2_LPU0_MBOX1_CMD_STAT);
+	BNAD_GET_REG(HOSTFN2_LPU1_MBOX1_CMD_STAT);
+	BNAD_GET_REG(LPU0_HOSTFN2_MBOX1_CMD_STAT);
+	BNAD_GET_REG(LPU1_HOSTFN2_MBOX1_CMD_STAT);
+
+	BNAD_GET_REG(HOSTFN3_LPU0_MBOX0_CMD_STAT);
+	BNAD_GET_REG(HOSTFN3_LPU1_MBOX0_CMD_STAT);
+	BNAD_GET_REG(LPU0_HOSTFN3_MBOX0_CMD_STAT);
+	BNAD_GET_REG(LPU1_HOSTFN3_MBOX0_CMD_STAT);
+
+	BNAD_GET_REG(HOSTFN3_LPU0_MBOX1_CMD_STAT);
+	BNAD_GET_REG(HOSTFN3_LPU1_MBOX1_CMD_STAT);
+	BNAD_GET_REG(LPU0_HOSTFN3_MBOX1_CMD_STAT);
+	BNAD_GET_REG(LPU1_HOSTFN3_MBOX1_CMD_STAT);
+
+	/* Host Function Force Parity Error Registers */
+	BNAD_GET_REG(HOSTFN0_LPU_FORCE_PERR);
+	BNAD_GET_REG(HOSTFN1_LPU_FORCE_PERR);
+	BNAD_GET_REG(HOSTFN2_LPU_FORCE_PERR);
+	BNAD_GET_REG(HOSTFN3_LPU_FORCE_PERR);
+
+	/* LL Port[0|1] Halt Mask Registers */
+	BNAD_GET_REG(LL_HALT_MSK_P0);
+	BNAD_GET_REG(LL_HALT_MSK_P1);
+
+	/* LL Port[0|1] Error Mask Registers */
+	BNAD_GET_REG(LL_ERR_MSK_P0);
+	BNAD_GET_REG(LL_ERR_MSK_P1);
+
+	/* EMC FLI Registers */
+	BNAD_GET_REG(FLI_CMD_REG);
+	BNAD_GET_REG(FLI_ADDR_REG);
+	BNAD_GET_REG(FLI_CTL_REG);
+	BNAD_GET_REG(FLI_WRDATA_REG);
+	BNAD_GET_REG(FLI_RDDATA_REG);
+	BNAD_GET_REG(FLI_DEV_STATUS_REG);
+	BNAD_GET_REG(FLI_SIG_WD_REG);
+
+	BNAD_GET_REG(FLI_DEV_VENDOR_REG);
+	BNAD_GET_REG(FLI_ERR_STATUS_REG);
+
+	/* RxAdm 0 Registers */
+	BNAD_GET_REG(RAD0_CTL_REG);
+	BNAD_GET_REG(RAD0_PE_PARM_REG);
+	BNAD_GET_REG(RAD0_BCN_REG);
+	BNAD_GET_REG(RAD0_DEFAULT_REG);
+	BNAD_GET_REG(RAD0_PROMISC_REG);
+	BNAD_GET_REG(RAD0_BCNQ_REG);
+	BNAD_GET_REG(RAD0_DEFAULTQ_REG);
+
+	BNAD_GET_REG(RAD0_ERR_STS);
+	BNAD_GET_REG(RAD0_SET_ERR_STS);
+	BNAD_GET_REG(RAD0_ERR_INT_EN);
+	BNAD_GET_REG(RAD0_FIRST_ERR);
+	BNAD_GET_REG(RAD0_FORCE_ERR);
+
+	BNAD_GET_REG(RAD0_MAC_MAN_1H);
+	BNAD_GET_REG(RAD0_MAC_MAN_1L);
+	BNAD_GET_REG(RAD0_MAC_MAN_2H);
+	BNAD_GET_REG(RAD0_MAC_MAN_2L);
+	BNAD_GET_REG(RAD0_MAC_MAN_3H);
+	BNAD_GET_REG(RAD0_MAC_MAN_3L);
+	BNAD_GET_REG(RAD0_MAC_MAN_4H);
+	BNAD_GET_REG(RAD0_MAC_MAN_4L);
+
+	BNAD_GET_REG(RAD0_LAST4_IP);
+
+	/* RxAdm 1 Registers */
+	BNAD_GET_REG(RAD1_CTL_REG);
+	BNAD_GET_REG(RAD1_PE_PARM_REG);
+	BNAD_GET_REG(RAD1_BCN_REG);
+	BNAD_GET_REG(RAD1_DEFAULT_REG);
+	BNAD_GET_REG(RAD1_PROMISC_REG);
+	BNAD_GET_REG(RAD1_BCNQ_REG);
+	BNAD_GET_REG(RAD1_DEFAULTQ_REG);
+
+	BNAD_GET_REG(RAD1_ERR_STS);
+	BNAD_GET_REG(RAD1_SET_ERR_STS);
+	BNAD_GET_REG(RAD1_ERR_INT_EN);
+
+	/* TxA0 Registers */
+	BNAD_GET_REG(TXA0_CTRL_REG);
+	/* TxA0 TSO Sequence # Registers (RO) */
+	for (i = 0; i < 8; i++) {
+		BNAD_GET_REG(TXA0_TSO_TCP_SEQ_REG(i));
+		BNAD_GET_REG(TXA0_TSO_IP_INFO_REG(i));
+	}
+
+	/* TxA1 Registers */
+	BNAD_GET_REG(TXA1_CTRL_REG);
+	/* TxA1 TSO Sequence # Registers (RO) */
+	for (i = 0; i < 8; i++) {
+		BNAD_GET_REG(TXA1_TSO_TCP_SEQ_REG(i));
+		BNAD_GET_REG(TXA1_TSO_IP_INFO_REG(i));
+	}
+
+	/* RxA Registers */
+	BNAD_GET_REG(RXA0_CTL_REG);
+	BNAD_GET_REG(RXA1_CTL_REG);
+
+	/* PLB0 Registers */
+	BNAD_GET_REG(PLB0_ECM_TIMER_REG);
+	BNAD_GET_REG(PLB0_RL_CTL);
+	for (i = 0; i < 8; i++)
+		BNAD_GET_REG(PLB0_RL_MAX_BC(i));
+	BNAD_GET_REG(PLB0_RL_TU_PRIO);
+	for (i = 0; i < 8; i++)
+		BNAD_GET_REG(PLB0_RL_BYTE_CNT(i));
+	BNAD_GET_REG(PLB0_RL_MIN_REG);
+	BNAD_GET_REG(PLB0_RL_MAX_REG);
+	BNAD_GET_REG(PLB0_EMS_ADD_REG);
+
+	/* PLB1 Registers */
+	BNAD_GET_REG(PLB1_ECM_TIMER_REG);
+	BNAD_GET_REG(PLB1_RL_CTL);
+	for (i = 0; i < 8; i++)
+		BNAD_GET_REG(PLB1_RL_MAX_BC(i));
+	BNAD_GET_REG(PLB1_RL_TU_PRIO);
+	for (i = 0; i < 8; i++)
+		BNAD_GET_REG(PLB1_RL_BYTE_CNT(i));
+	BNAD_GET_REG(PLB1_RL_MIN_REG);
+	BNAD_GET_REG(PLB1_RL_MAX_REG);
+	BNAD_GET_REG(PLB1_EMS_ADD_REG);
+
+	/* HQM Control Register */
+	BNAD_GET_REG(HQM0_CTL_REG);
+	BNAD_GET_REG(HQM0_RXQ_STOP_SEM);
+	BNAD_GET_REG(HQM0_TXQ_STOP_SEM);
+	BNAD_GET_REG(HQM1_CTL_REG);
+	BNAD_GET_REG(HQM1_RXQ_STOP_SEM);
+	BNAD_GET_REG(HQM1_TXQ_STOP_SEM);
+
+	/* LUT Registers */
+	BNAD_GET_REG(LUT0_ERR_STS);
+	BNAD_GET_REG(LUT0_SET_ERR_STS);
+	BNAD_GET_REG(LUT1_ERR_STS);
+	BNAD_GET_REG(LUT1_SET_ERR_STS);
+
+	/* TRC Registers */
+	BNAD_GET_REG(TRC_CTL_REG);
+	BNAD_GET_REG(TRC_MODS_REG);
+	BNAD_GET_REG(TRC_TRGC_REG);
+	BNAD_GET_REG(TRC_CNT1_REG);
+	BNAD_GET_REG(TRC_CNT2_REG);
+	BNAD_GET_REG(TRC_NXTS_REG);
+	BNAD_GET_REG(TRC_DIRR_REG);
+	for (i = 0; i < 10; i++)
+		BNAD_GET_REG(TRC_TRGM_REG(i));
+	for (i = 0; i < 10; i++)
+		BNAD_GET_REG(TRC_NXTM_REG(i));
+	for (i = 0; i < 10; i++)
+		BNAD_GET_REG(TRC_STRM_REG(i));
+
+	spin_unlock_irq(&bnad->bna_lock);
+#undef BNAD_GET_REG
+	return num;
+}
+static int
+bnad_get_regs_len(struct net_device *netdev)
+{
+	int ret = get_regs(netdev_priv(netdev), NULL) * sizeof(u32);
+	return ret;
+}
+
+static void
+bnad_get_regs(struct net_device *netdev, struct ethtool_regs *regs, void *buf)
+{
+	memset(buf, 0, bnad_get_regs_len(netdev));
+	get_regs(netdev_priv(netdev), buf);
+}
+
+static void
+bnad_get_wol(struct net_device *netdev, struct ethtool_wolinfo *wolinfo)
+{
+	wolinfo->supported = 0;
+	wolinfo->wolopts = 0;
+}
+
+static int
+bnad_get_coalesce(struct net_device *netdev, struct ethtool_coalesce *coalesce)
+{
+	struct bnad *bnad = netdev_priv(netdev);
+
+	/* Lock rqd. to access bnad->bna_lock */
+	spin_lock_irq(&bnad->bna_lock);
+	coalesce->use_adaptive_rx_coalesce =
+		(bnad->cfg_flags & BNAD_CF_DIM_ENABLED) ? true : false;
+	spin_unlock_irq(&bnad->bna_lock);
+
+	coalesce->rx_coalesce_usecs = bnad->rx_coalescing_timeo *
+					BFI_COALESCING_TIMER_UNIT;
+	coalesce->tx_coalesce_usecs = bnad->tx_coalescing_timeo *
+					BFI_COALESCING_TIMER_UNIT;
+	coalesce->tx_max_coalesced_frames = BFI_TX_INTERPKT_COUNT;
+
+	return 0;
+}
+
+static int
+bnad_set_coalesce(struct net_device *netdev, struct ethtool_coalesce *coalesce)
+{
+	struct bnad *bnad = netdev_priv(netdev);
+
+	if (coalesce->rx_coalesce_usecs == 0 ||
+	    coalesce->rx_coalesce_usecs >
+	    BFI_MAX_COALESCING_TIMEO * BFI_COALESCING_TIMER_UNIT)
+		return -EINVAL;
+
+	if (coalesce->tx_coalesce_usecs == 0 ||
+	    coalesce->tx_coalesce_usecs >
+	    BFI_MAX_COALESCING_TIMEO * BFI_COALESCING_TIMER_UNIT)
+		return -EINVAL;
+
+	mutex_lock(&bnad->conf_mutex);
+	/*
+	 * Do not need to store rx_coalesce_usecs here
+	 * Every time DIM is disabled, we can get it from the
+	 * stack.
+	 */
+	spin_lock_irq(&bnad->bna_lock);
+	if (coalesce->use_adaptive_rx_coalesce) {
+		if (!(bnad->cfg_flags & BNAD_CF_DIM_ENABLED)) {
+			bnad->cfg_flags |= BNAD_CF_DIM_ENABLED;
+			bnad_dim_timer_start(bnad);
+		}
+	} else {
+		if (bnad->cfg_flags & BNAD_CF_DIM_ENABLED) {
+			bnad->cfg_flags &= ~BNAD_CF_DIM_ENABLED;
+			bnad_dim_timer_stop(bnad);
+			bnad_rx_coalescing_timeo_set(bnad);
+		}
+	}
+	if (bnad->tx_coalescing_timeo != coalesce->tx_coalesce_usecs /
+					BFI_COALESCING_TIMER_UNIT) {
+		bnad->tx_coalescing_timeo = coalesce->tx_coalesce_usecs /
+						BFI_COALESCING_TIMER_UNIT;
+		bnad_tx_coalescing_timeo_set(bnad);
+	}
+
+	if (bnad->rx_coalescing_timeo != coalesce->rx_coalesce_usecs /
+					BFI_COALESCING_TIMER_UNIT) {
+		bnad->rx_coalescing_timeo = coalesce->rx_coalesce_usecs /
+						BFI_COALESCING_TIMER_UNIT;
+
+		if (!(bnad->cfg_flags & BNAD_CF_DIM_ENABLED))
+			bnad_rx_coalescing_timeo_set(bnad);
+
+	}
+
+	/* Add Tx Inter-pkt DMA count?  */
+
+	spin_unlock_irq(&bnad->bna_lock);
+
+	mutex_unlock(&bnad->conf_mutex);
+	return 0;
+}
+
+static void
+bnad_get_ringparam(struct net_device *netdev,
+		   struct ethtool_ringparam *ringparam)
+{
+	struct bnad *bnad = netdev_priv(netdev);
+
+	ringparam->rx_max_pending = BNAD_MAX_Q_DEPTH / bnad_rxqs_per_cq;
+	ringparam->rx_mini_max_pending = 0;
+	ringparam->rx_jumbo_max_pending = 0;
+	ringparam->tx_max_pending = BNAD_MAX_Q_DEPTH;
+
+	ringparam->rx_pending = bnad->rxq_depth;
+	ringparam->rx_mini_max_pending = 0;
+	ringparam->rx_jumbo_max_pending = 0;
+	ringparam->tx_pending = bnad->txq_depth;
+}
+
+static int
+bnad_set_ringparam(struct net_device *netdev,
+		   struct ethtool_ringparam *ringparam)
+{
+	int i, current_err, err = 0;
+	struct bnad *bnad = netdev_priv(netdev);
+
+	mutex_lock(&bnad->conf_mutex);
+	if (ringparam->rx_pending == bnad->rxq_depth &&
+	    ringparam->tx_pending == bnad->txq_depth) {
+		mutex_unlock(&bnad->conf_mutex);
+		return 0;
+	}
+
+	if (ringparam->rx_pending < BNAD_MIN_Q_DEPTH ||
+	    ringparam->rx_pending > BNAD_MAX_Q_DEPTH / bnad_rxqs_per_cq ||
+	    !BNA_POWER_OF_2(ringparam->rx_pending)) {
+		mutex_unlock(&bnad->conf_mutex);
+		return -EINVAL;
+	}
+	if (ringparam->tx_pending < BNAD_MIN_Q_DEPTH ||
+	    ringparam->tx_pending > BNAD_MAX_Q_DEPTH ||
+	    !BNA_POWER_OF_2(ringparam->tx_pending)) {
+		mutex_unlock(&bnad->conf_mutex);
+		return -EINVAL;
+	}
+
+	if (ringparam->rx_pending != bnad->rxq_depth) {
+		bnad->rxq_depth = ringparam->rx_pending;
+		for (i = 0; i < bnad->num_rx; i++) {
+			if (!bnad->rx_info[i].rx)
+				continue;
+			bnad_cleanup_rx(bnad, i);
+			current_err = bnad_setup_rx(bnad, i);
+			if (current_err && !err)
+				err = current_err;
+		}
+	}
+	if (ringparam->tx_pending != bnad->txq_depth) {
+		bnad->txq_depth = ringparam->tx_pending;
+		for (i = 0; i < bnad->num_tx; i++) {
+			if (!bnad->tx_info[i].tx)
+				continue;
+			bnad_cleanup_tx(bnad, i);
+			current_err = bnad_setup_tx(bnad, i);
+			if (current_err && !err)
+				err = current_err;
+		}
+	}
+
+	mutex_unlock(&bnad->conf_mutex);
+	return err;
+}
+
+static void
+bnad_get_pauseparam(struct net_device *netdev,
+		    struct ethtool_pauseparam *pauseparam)
+{
+	struct bnad *bnad = netdev_priv(netdev);
+
+	pauseparam->autoneg = 0;
+	pauseparam->rx_pause = bnad->bna.port.pause_config.rx_pause;
+	pauseparam->tx_pause = bnad->bna.port.pause_config.tx_pause;
+}
+
+static int
+bnad_set_pauseparam(struct net_device *netdev,
+		    struct ethtool_pauseparam *pauseparam)
+{
+	struct bnad *bnad = netdev_priv(netdev);
+	struct bna_pause_config pause_config;
+
+	if (pauseparam->autoneg == AUTONEG_ENABLE)
+		return -EINVAL;
+
+	mutex_lock(&bnad->conf_mutex);
+	if (pauseparam->rx_pause != bnad->bna.port.pause_config.rx_pause ||
+	    pauseparam->tx_pause != bnad->bna.port.pause_config.tx_pause) {
+		pause_config.rx_pause = pauseparam->rx_pause;
+		pause_config.tx_pause = pauseparam->tx_pause;
+		spin_lock_irq(&bnad->bna_lock);
+		bna_port_pause_config(&bnad->bna.port, &pause_config, NULL);
+		spin_unlock_irq(&bnad->bna_lock);
+	}
+	mutex_unlock(&bnad->conf_mutex);
+	return 0;
+}
+
+static u32
+bnad_get_rx_csum(struct net_device *netdev)
+{
+	u32 rx_csum;
+	struct bnad *bnad = netdev_priv(netdev);
+
+	rx_csum = bnad->rx_csum;
+	return rx_csum;
+}
+
+static int
+bnad_set_rx_csum(struct net_device *netdev, u32 rx_csum)
+{
+	struct bnad *bnad = netdev_priv(netdev);
+
+	mutex_lock(&bnad->conf_mutex);
+	bnad->rx_csum = rx_csum;
+	mutex_unlock(&bnad->conf_mutex);
+	return 0;
+}
+
+static int
+bnad_set_tx_csum(struct net_device *netdev, u32 tx_csum)
+{
+	struct bnad *bnad = netdev_priv(netdev);
+
+	mutex_lock(&bnad->conf_mutex);
+	if (tx_csum) {
+		netdev->features |= NETIF_F_IP_CSUM;
+		netdev->features |= NETIF_F_IPV6_CSUM;
+	} else {
+		netdev->features &= ~NETIF_F_IP_CSUM;
+		netdev->features &= ~NETIF_F_IPV6_CSUM;
+	}
+	mutex_unlock(&bnad->conf_mutex);
+	return 0;
+}
+
+static int
+bnad_set_tso(struct net_device *netdev, u32 tso)
+{
+	struct bnad *bnad = netdev_priv(netdev);
+
+	mutex_lock(&bnad->conf_mutex);
+	if (tso) {
+		netdev->features |= NETIF_F_TSO;
+		netdev->features |= NETIF_F_TSO6;
+	} else {
+		netdev->features &= ~NETIF_F_TSO;
+		netdev->features &= ~NETIF_F_TSO6;
+	}
+	mutex_unlock(&bnad->conf_mutex);
+	return 0;
+}
+
+static void
+bnad_get_strings(struct net_device *netdev, u32 stringset, u8 * string)
+{
+	struct bnad *bnad = netdev_priv(netdev);
+	int i, j, q_num;
+	u64 bmap;
+
+	mutex_lock(&bnad->conf_mutex);
+
+	switch (stringset) {
+	case ETH_SS_STATS:
+		for (i = 0; i < BNAD_ETHTOOL_STATS_NUM; i++) {
+			BUG_ON(!(strlen(bnad_net_stats_strings[i]) <
+				   ETH_GSTRING_LEN));
+			memcpy(string, bnad_net_stats_strings[i],
+			       ETH_GSTRING_LEN);
+			string += ETH_GSTRING_LEN;
+		}
+		bmap = (u64)bnad->bna.tx_mod.txf_bmap[0] |
+			((u64)bnad->bna.tx_mod.txf_bmap[1] << 32);
+		for (i = 0; bmap && (i < BFI_LL_TXF_ID_MAX); i++) {
+			if (bmap & 1) {
+				sprintf(string, "txf%d_ucast_octets", i);
+				string += ETH_GSTRING_LEN;
+				sprintf(string, "txf%d_ucast", i);
+				string += ETH_GSTRING_LEN;
+				sprintf(string, "txf%d_ucast_vlan", i);
+				string += ETH_GSTRING_LEN;
+				sprintf(string, "txf%d_mcast_octets", i);
+				string += ETH_GSTRING_LEN;
+				sprintf(string, "txf%d_mcast", i);
+				string += ETH_GSTRING_LEN;
+				sprintf(string, "txf%d_mcast_vlan", i);
+				string += ETH_GSTRING_LEN;
+				sprintf(string, "txf%d_bcast_octets", i);
+				string += ETH_GSTRING_LEN;
+				sprintf(string, "txf%d_bcast", i);
+				string += ETH_GSTRING_LEN;
+				sprintf(string, "txf%d_bcast_vlan", i);
+				string += ETH_GSTRING_LEN;
+				sprintf(string, "txf%d_errors", i);
+				string += ETH_GSTRING_LEN;
+				sprintf(string, "txf%d_filter_vlan", i);
+				string += ETH_GSTRING_LEN;
+				sprintf(string, "txf%d_filter_mac_sa", i);
+				string += ETH_GSTRING_LEN;
+			}
+			bmap >>= 1;
+		}
+
+		bmap = (u64)bnad->bna.rx_mod.rxf_bmap[0] |
+			((u64)bnad->bna.rx_mod.rxf_bmap[1] << 32);
+		for (i = 0; bmap && (i < BFI_LL_RXF_ID_MAX); i++) {
+			if (bmap & 1) {
+				sprintf(string, "rxf%d_ucast_octets", i);
+				string += ETH_GSTRING_LEN;
+				sprintf(string, "rxf%d_ucast", i);
+				string += ETH_GSTRING_LEN;
+				sprintf(string, "rxf%d_ucast_vlan", i);
+				string += ETH_GSTRING_LEN;
+				sprintf(string, "rxf%d_mcast_octets", i);
+				string += ETH_GSTRING_LEN;
+				sprintf(string, "rxf%d_mcast", i);
+				string += ETH_GSTRING_LEN;
+				sprintf(string, "rxf%d_mcast_vlan", i);
+				string += ETH_GSTRING_LEN;
+				sprintf(string, "rxf%d_bcast_octets", i);
+				string += ETH_GSTRING_LEN;
+				sprintf(string, "rxf%d_bcast", i);
+				string += ETH_GSTRING_LEN;
+				sprintf(string, "rxf%d_bcast_vlan", i);
+				string += ETH_GSTRING_LEN;
+				sprintf(string, "rxf%d_frame_drops", i);
+				string += ETH_GSTRING_LEN;
+			}
+			bmap >>= 1;
+		}
+
+		q_num = 0;
+		for (i = 0; i < bnad->num_rx; i++) {
+			if (!bnad->rx_info[i].rx)
+				continue;
+			for (j = 0; j < bnad->num_rxp_per_rx; j++) {
+				sprintf(string, "cq%d_producer_index", q_num);
+				string += ETH_GSTRING_LEN;
+				sprintf(string, "cq%d_consumer_index", q_num);
+				string += ETH_GSTRING_LEN;
+				sprintf(string, "cq%d_hw_producer_index",
+					q_num);
+				string += ETH_GSTRING_LEN;
+				q_num++;
+			}
+		}
+
+		q_num = 0;
+		for (i = 0; i < bnad->num_rx; i++) {
+			if (!bnad->rx_info[i].rx)
+				continue;
+			for (j = 0; j < bnad->num_rxp_per_rx; j++) {
+				sprintf(string, "rxq%d_packets", q_num);
+				string += ETH_GSTRING_LEN;
+				sprintf(string, "rxq%d_bytes", q_num);
+				string += ETH_GSTRING_LEN;
+				sprintf(string, "rxq%d_packets_with_error",
+								q_num);
+				string += ETH_GSTRING_LEN;
+				sprintf(string, "rxq%d_allocbuf_failed", q_num);
+				string += ETH_GSTRING_LEN;
+				sprintf(string, "rxq%d_producer_index", q_num);
+				string += ETH_GSTRING_LEN;
+				sprintf(string, "rxq%d_consumer_index", q_num);
+				string += ETH_GSTRING_LEN;
+				q_num++;
+				if (bnad->rx_info[i].rx_ctrl[j].ccb &&
+					bnad->rx_info[i].rx_ctrl[j].ccb->
+					rcb[1] &&
+					bnad->rx_info[i].rx_ctrl[j].ccb->
+					rcb[1]->rxq) {
+					sprintf(string, "rxq%d_packets", q_num);
+					string += ETH_GSTRING_LEN;
+					sprintf(string, "rxq%d_bytes", q_num);
+					string += ETH_GSTRING_LEN;
+					sprintf(string,
+					"rxq%d_packets_with_error", q_num);
+					string += ETH_GSTRING_LEN;
+					sprintf(string, "rxq%d_allocbuf_failed",
+								q_num);
+					string += ETH_GSTRING_LEN;
+					sprintf(string, "rxq%d_producer_index",
+								q_num);
+					string += ETH_GSTRING_LEN;
+					sprintf(string, "rxq%d_consumer_index",
+								q_num);
+					string += ETH_GSTRING_LEN;
+					q_num++;
+				}
+			}
+		}
+
+		q_num = 0;
+		for (i = 0; i < bnad->num_tx; i++) {
+			if (!bnad->tx_info[i].tx)
+				continue;
+			for (j = 0; j < bnad->num_txq_per_tx; j++) {
+				sprintf(string, "txq%d_packets", q_num);
+				string += ETH_GSTRING_LEN;
+				sprintf(string, "txq%d_bytes", q_num);
+				string += ETH_GSTRING_LEN;
+				sprintf(string, "txq%d_producer_index", q_num);
+				string += ETH_GSTRING_LEN;
+				sprintf(string, "txq%d_consumer_index", q_num);
+				string += ETH_GSTRING_LEN;
+				sprintf(string, "txq%d_hw_consumer_index",
+									q_num);
+				string += ETH_GSTRING_LEN;
+				q_num++;
+			}
+		}
+
+		break;
+
+	default:
+		break;
+	}
+
+	mutex_unlock(&bnad->conf_mutex);
+}
+
+static int
+bnad_get_stats_count_locked(struct net_device *netdev)
+{
+	struct bnad *bnad = netdev_priv(netdev);
+	int i, j, count, rxf_active_num = 0, txf_active_num = 0;
+	u64 bmap;
+
+	bmap = (u64)bnad->bna.tx_mod.txf_bmap[0] |
+			((u64)bnad->bna.tx_mod.txf_bmap[1] << 32);
+	for (i = 0; bmap && (i < BFI_LL_TXF_ID_MAX); i++) {
+		if (bmap & 1)
+			txf_active_num++;
+		bmap >>= 1;
+	}
+	bmap = (u64)bnad->bna.rx_mod.rxf_bmap[0] |
+			((u64)bnad->bna.rx_mod.rxf_bmap[1] << 32);
+	for (i = 0; bmap && (i < BFI_LL_RXF_ID_MAX); i++) {
+		if (bmap & 1)
+			rxf_active_num++;
+		bmap >>= 1;
+	}
+	count = BNAD_ETHTOOL_STATS_NUM +
+		txf_active_num * BNAD_NUM_TXF_COUNTERS +
+		rxf_active_num * BNAD_NUM_RXF_COUNTERS;
+
+	for (i = 0; i < bnad->num_rx; i++) {
+		if (!bnad->rx_info[i].rx)
+			continue;
+		count += bnad->num_rxp_per_rx * BNAD_NUM_CQ_COUNTERS;
+		count += bnad->num_rxp_per_rx * BNAD_NUM_RXQ_COUNTERS;
+		for (j = 0; j < bnad->num_rxp_per_rx; j++)
+			if (bnad->rx_info[i].rx_ctrl[j].ccb &&
+				bnad->rx_info[i].rx_ctrl[j].ccb->rcb[1] &&
+				bnad->rx_info[i].rx_ctrl[j].ccb->rcb[1]->rxq)
+				count +=  BNAD_NUM_RXQ_COUNTERS;
+	}
+
+	for (i = 0; i < bnad->num_tx; i++) {
+		if (!bnad->tx_info[i].tx)
+			continue;
+		count += bnad->num_txq_per_tx * BNAD_NUM_TXQ_COUNTERS;
+	}
+	return count;
+}
+
+static int
+bnad_per_q_stats_fill(struct bnad *bnad, u64 *buf, int bi)
+{
+	int i, j;
+	struct bna_rcb *rcb = NULL;
+	struct bna_tcb *tcb = NULL;
+
+	for (i = 0; i < bnad->num_rx; i++) {
+		if (!bnad->rx_info[i].rx)
+			continue;
+		for (j = 0; j < bnad->num_rxp_per_rx; j++)
+			if (bnad->rx_info[i].rx_ctrl[j].ccb &&
+				bnad->rx_info[i].rx_ctrl[j].ccb->rcb[0] &&
+				bnad->rx_info[i].rx_ctrl[j].ccb->rcb[0]->rxq) {
+				buf[bi++] = bnad->rx_info[i].rx_ctrl[j].
+						ccb->producer_index;
+				buf[bi++] = 0; /* ccb->consumer_index */
+				buf[bi++] = *(bnad->rx_info[i].rx_ctrl[j].
+						ccb->hw_producer_index);
+			}
+	}
+	for (i = 0; i < bnad->num_rx; i++) {
+		if (!bnad->rx_info[i].rx)
+			continue;
+		for (j = 0; j < bnad->num_rxp_per_rx; j++)
+			if (bnad->rx_info[i].rx_ctrl[j].ccb) {
+				if (bnad->rx_info[i].rx_ctrl[j].ccb->rcb[0] &&
+					bnad->rx_info[i].rx_ctrl[j].ccb->
+					rcb[0]->rxq) {
+					rcb = bnad->rx_info[i].rx_ctrl[j].
+							ccb->rcb[0];
+					buf[bi++] = rcb->rxq->rx_packets;
+					buf[bi++] = rcb->rxq->rx_bytes;
+					buf[bi++] = rcb->rxq->
+							rx_packets_with_error;
+					buf[bi++] = rcb->rxq->
+							rxbuf_alloc_failed;
+					buf[bi++] = rcb->producer_index;
+					buf[bi++] = rcb->consumer_index;
+				}
+				if (bnad->rx_info[i].rx_ctrl[j].ccb->rcb[1] &&
+					bnad->rx_info[i].rx_ctrl[j].ccb->
+					rcb[1]->rxq) {
+					rcb = bnad->rx_info[i].rx_ctrl[j].
+								ccb->rcb[1];
+					buf[bi++] = rcb->rxq->rx_packets;
+					buf[bi++] = rcb->rxq->rx_bytes;
+					buf[bi++] = rcb->rxq->
+							rx_packets_with_error;
+					buf[bi++] = rcb->rxq->
+							rxbuf_alloc_failed;
+					buf[bi++] = rcb->producer_index;
+					buf[bi++] = rcb->consumer_index;
+				}
+			}
+	}
+
+	for (i = 0; i < bnad->num_tx; i++) {
+		if (!bnad->tx_info[i].tx)
+			continue;
+		for (j = 0; j < bnad->num_txq_per_tx; j++)
+			if (bnad->tx_info[i].tcb[j] &&
+				bnad->tx_info[i].tcb[j]->txq) {
+				tcb = bnad->tx_info[i].tcb[j];
+				buf[bi++] = tcb->txq->tx_packets;
+				buf[bi++] = tcb->txq->tx_bytes;
+				buf[bi++] = tcb->producer_index;
+				buf[bi++] = tcb->consumer_index;
+				buf[bi++] = *(tcb->hw_consumer_index);
+			}
+	}
+
+	return bi;
+}
+
+static void
+bnad_get_ethtool_stats(struct net_device *netdev, struct ethtool_stats *stats,
+		       u64 *buf)
+{
+	struct bnad *bnad = netdev_priv(netdev);
+	int i, j, bi;
+	unsigned long *net_stats;
+	u64 *stats64;
+	u64 bmap;
+
+	mutex_lock(&bnad->conf_mutex);
+	if (bnad_get_stats_count_locked(netdev) != stats->n_stats) {
+		mutex_unlock(&bnad->conf_mutex);
+		return;
+	}
+
+	/*
+	 * Used bna_lock to sync reads from bna_stats, which is written
+	 * under the same lock
+	 */
+	spin_lock_irq(&bnad->bna_lock);
+	bi = 0;
+	memset(buf, 0, stats->n_stats * sizeof(u64));
+	memset(&bnad->net_stats, 0, sizeof(struct net_device_stats));
+
+	bnad_netdev_qstats_fill(bnad);
+	bnad_netdev_hwstats_fill(bnad);
+
+	/* Fill net_stats into ethtool buffers */
+	net_stats = (unsigned long *)&bnad->net_stats;
+	for (i = 0; i < sizeof(struct net_device_stats) / sizeof(unsigned long);
+	     i++)
+		buf[bi++] = net_stats[i];
+
+	/* Fill driver stats into ethtool buffers */
+	stats64 = (u64 *)&bnad->stats.drv_stats;
+	for (i = 0; i < sizeof(struct bnad_drv_stats) / sizeof(u64); i++)
+		buf[bi++] = stats64[i];
+
+	/* Fill hardware stats excluding the rxf/txf into ethtool bufs */
+	stats64 = (u64 *) bnad->stats.bna_stats->hw_stats;
+	for (i = 0;
+	     i < offsetof(struct bfi_ll_stats, rxf_stats[0]) / sizeof(u64);
+	     i++)
+		buf[bi++] = stats64[i];
+
+	/* Fill txf stats into ethtool buffers */
+	bmap = (u64)bnad->bna.tx_mod.txf_bmap[0] |
+			((u64)bnad->bna.tx_mod.txf_bmap[1] << 32);
+	for (i = 0; bmap && (i < BFI_LL_TXF_ID_MAX); i++) {
+		if (bmap & 1) {
+			stats64 = (u64 *)&bnad->stats.bna_stats->
+						hw_stats->txf_stats[i];
+			for (j = 0; j < sizeof(struct bfi_ll_stats_txf) /
+					sizeof(u64); j++)
+				buf[bi++] = stats64[j];
+		}
+		bmap >>= 1;
+	}
+
+	/*  Fill rxf stats into ethtool buffers */
+	bmap = (u64)bnad->bna.rx_mod.rxf_bmap[0] |
+			((u64)bnad->bna.rx_mod.rxf_bmap[1] << 32);
+	for (i = 0; bmap && (i < BFI_LL_RXF_ID_MAX); i++) {
+		if (bmap & 1) {
+			stats64 = (u64 *)&bnad->stats.bna_stats->
+						hw_stats->rxf_stats[i];
+			for (j = 0; j < sizeof(struct bfi_ll_stats_rxf) /
+					sizeof(u64); j++)
+				buf[bi++] = stats64[j];
+		}
+		bmap >>= 1;
+	}
+
+	/* Fill per Q stats into ethtool buffers */
+	bi = bnad_per_q_stats_fill(bnad, buf, bi);
+
+	spin_unlock_irq(&bnad->bna_lock);
+
+	mutex_unlock(&bnad->conf_mutex);
+}
+
+static int
+bnad_get_sset_count(struct net_device *netdev, int sset)
+{
+	switch (sset) {
+	case ETH_SS_STATS:
+		return bnad_get_stats_count_locked(netdev);
+	default:
+		return -EOPNOTSUPP;
+	}
+}
+
+static struct ethtool_ops bnad_ethtool_ops = {
+	.get_settings = bnad_get_settings,
+	.set_settings = bnad_set_settings,
+	.get_drvinfo = bnad_get_drvinfo,
+	.get_regs_len = bnad_get_regs_len,
+	.get_regs = bnad_get_regs,
+	.get_wol = bnad_get_wol,
+	.get_link = ethtool_op_get_link,
+	.get_coalesce = bnad_get_coalesce,
+	.set_coalesce = bnad_set_coalesce,
+	.get_ringparam = bnad_get_ringparam,
+	.set_ringparam = bnad_set_ringparam,
+	.get_pauseparam = bnad_get_pauseparam,
+	.set_pauseparam = bnad_set_pauseparam,
+	.get_rx_csum = bnad_get_rx_csum,
+	.set_rx_csum = bnad_set_rx_csum,
+	.get_tx_csum = ethtool_op_get_tx_csum,
+	.set_tx_csum = bnad_set_tx_csum,
+	.get_sg = ethtool_op_get_sg,
+	.set_sg = ethtool_op_set_sg,
+	.get_tso = ethtool_op_get_tso,
+	.set_tso = bnad_set_tso,
+	.get_flags = ethtool_op_get_flags,
+	.set_flags = ethtool_op_set_flags,
+	.get_strings = bnad_get_strings,
+	.get_ethtool_stats = bnad_get_ethtool_stats,
+	.get_sset_count = bnad_get_sset_count
+};
+
+void
+bnad_set_ethtool_ops(struct net_device *netdev)
+{
+	SET_ETHTOOL_OPS(netdev, &bnad_ethtool_ops);
+}
diff -ruP net-next-2.6.35-rc1-orig/drivers/net/bna/bnad.h net-next-2.6.35-rc1-mod/drivers/net/bna/bnad.h
--- net-next-2.6.35-rc1-orig/drivers/net/bna/bnad.h	1969-12-31 16:00:00.000000000 -0800
+++ net-next-2.6.35-rc1-mod/drivers/net/bna/bnad.h	2010-08-02 17:19:19.486229000 -0700
@@ -0,0 +1,474 @@
+/*
+ * Linux network driver for Brocade Converged Network Adapter.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License (GPL) Version 2 as
+ * published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+/*
+ * Copyright (c) 2005-2009 Brocade Communications Systems, Inc.
+ * All rights reserved
+ * www.brocade.com
+ */
+#ifndef __BNAD_H__
+#define __BNAD_H__
+
+#include <linux/rtnetlink.h>
+#include <linux/workqueue.h>
+#include <linux/ipv6.h>
+#include <linux/etherdevice.h>
+#include <linux/mutex.h>
+#include <linux/firmware.h>
+
+/* Fix for IA64 */
+#include <asm/checksum.h>
+#include <net/ip6_checksum.h>
+
+#include <net/ip.h>
+#include <net/tcp.h>
+
+#include "bna.h"
+
+#define BNAD_TXQ_DEPTH		2048
+#define BNAD_RXQ_DEPTH		2048
+
+#define BNAD_MAX_TXS		1
+#define BNAD_MAX_TXQ_PER_TX	8	/* 8 priority queues */
+#define BNAD_TXQ_NUM		1
+
+#define BNAD_MAX_RXS		1
+#define BNAD_MAX_RXPS_PER_RX	16
+
+/*
+ * Control structure pointed to ccb->ctrl, which
+ * determines the NAPI / LRO behavior CCB
+ * There is 1:1 corres. between ccb & ctrl
+ */
+struct bnad_rx_ctrl {
+	struct bna_ccb *ccb;
+	struct napi_struct	napi;
+};
+
+/*
+ * External variables & functions
+ */
+
+extern void bnad_napi_enable(struct bnad *bnad, u32 rx_id);
+extern void bnad_napi_disable(struct bnad *bnad, u32 rx_id);
+extern void bnad_netdev_init(struct bnad *bnad, bool using_dac);
+extern void bnad_set_netdev_perm_addr(struct bnad *bnad);
+extern int bnad_tso_prepare(struct bnad *bnad, struct sk_buff *skb);
+extern void bnad_netif_rx_schedule_poll(struct bnad *bnad,
+					struct bna_ccb *ccb);
+
+#define BNAD_RXMODE_PROMISC_DEFAULT	BNA_RXMODE_PROMISC
+
+#define BNAD_GET_NUM_RXP(_rxps)	\
+{						\
+	(_rxps) = min((uint)num_online_cpus(),	\
+			(uint)(BNAD_MAX_RXS * BNAD_MAX_RXPS_PER_RX));\
+}
+
+#define bnad_rcv_skb_vlan_stripped(ccb, bnad, skb, cmpl)		\
+{									\
+	struct bnad_rx_ctrl *rx_ctrl = (struct bnad_rx_ctrl *)ccb->ctrl;\
+	if (skb->ip_summed == CHECKSUM_UNNECESSARY)			\
+		vlan_gro_receive(&rx_ctrl->napi, bnad->vlan_grp,	\
+				ntohs(cmpl->vlan_tag), skb);		\
+	else								\
+		vlan_hwaccel_receive_skb(skb,				\
+					 bnad->vlan_grp,		\
+					 ntohs(cmpl->vlan_tag));	\
+}
+
+#define bnad_rcv_skb(ccb, bnad, skb, cmpl)				\
+{									\
+	struct bnad_rx_ctrl *rx_ctrl = (struct bnad_rx_ctrl *)ccb->ctrl;\
+	if (skb->ip_summed == CHECKSUM_UNNECESSARY)			\
+		napi_gro_receive(&rx_ctrl->napi, skb);			\
+	else								\
+		netif_receive_skb(skb);					\
+}
+
+static inline void
+bnad_netdev_mc_list_get(struct net_device *netdev, u8 *mc_list)
+{
+	int i = 1; /* Index 0 has broadcast address */
+	struct netdev_hw_addr *mc_addr;
+
+	netdev_for_each_mc_addr(mc_addr, netdev) {
+		memcpy(&mc_list[i * ETH_ALEN], &mc_addr->addr[0],
+							ETH_ALEN);
+		i++;
+	}
+}
+
+#define BNAD_GET_TX_ID(_skb)	(0)
+
+#if defined(__ia64__)
+#define bnad_udelay	udelay
+#else
+#define bnad_udelay	__udelay
+#endif
+
+/*
+ * GLOBAL #defines (CONSTANTS)
+ */
+#define BNAD_NAME			"bna"
+#define BNAD_NAME_LEN			64
+
+#define BNAD_VERSION			"2.3.0.0"
+
+#define BNAD_MAILBOX_MSIX_VECTORS	1
+
+#define BNAD_STATS_TIMER_FREQ		1000 	/* in msecs */
+#define BNAD_DIM_TIMER_FREQ		1000 	/* in msecs */
+
+#define BNAD_MAX_Q_DEPTH		0x10000
+#define BNAD_MIN_Q_DEPTH		0x200
+
+#define BNAD_JUMBO_MTU			9000
+
+#define BNAD_NETIF_WAKE_THRESHOLD	8
+
+#define BNAD_RXQ_REFILL_THRESHOLD_SHIFT	3
+
+/* Bit positions for tcb->flags */
+#define BNAD_TXQ_FREE_SENT		0
+
+/* Bit positions for rcb->flags */
+#define BNAD_RXQ_REFILL			0
+#define BNAD_RXQ_STARTED		1
+
+/*
+ * DATA STRUCTURES
+ */
+
+/* enums */
+enum bnad_intr_source {
+	BNAD_INTR_TX		= 1,
+	BNAD_INTR_RX		= 2
+};
+
+enum bnad_link_state {
+	BNAD_LS_DOWN		= 0,
+	BNAD_LS_UP 		= 1
+};
+
+enum bnad_state {
+	BNAD_S_STOPPED 		= 1,
+	BNAD_S_MBOX_WAIT	= 2,
+	BNAD_S_START_WAIT	= 3,
+	BNAD_S_STARTED		= 4,
+	BNAD_S_OPEN_WAIT	= 5,
+	BNAD_S_OPENED		= 6,
+};
+
+struct bnad_completion {
+	struct completion 	ioc_comp;
+	struct completion 	ucast_comp;
+	struct completion	mcast_comp;
+	struct completion	tx_comp;
+	struct completion	rx_comp;
+	struct completion	stats_comp;
+	struct completion	port_comp;
+
+	u8			ioc_comp_status;
+	u8			ucast_comp_status;
+	u8			mcast_comp_status;
+	u8			tx_comp_status;
+	u8			rx_comp_status;
+	u8			stats_comp_status;
+	u8			port_comp_status;
+};
+
+/* Tx Rx Control Stats */
+struct bnad_drv_stats {
+	u64 		netif_queue_stop;
+	u64		netif_queue_wakeup;
+	u64		tso4;
+	u64		tso6;
+	u64		tso_err;
+	u64		tcpcsum_offload;
+	u64		udpcsum_offload;
+	u64		csum_help;
+	u64		csum_help_err;
+
+	u64		hw_stats_updates;
+	u64		netif_rx_schedule;
+	u64		netif_rx_complete;
+	u64		netif_rx_dropped;
+
+	u64		link_toggle;
+	u64		cee_up;
+
+	u64		rxp_info_alloc_failed;
+	u64		mbox_intr_disabled;
+	u64		mbox_intr_enabled;
+	u64		tx_unmap_q_alloc_failed;
+	u64		rx_unmap_q_alloc_failed;
+
+	u64		rxbuf_alloc_failed;
+};
+
+/* Complete driver stats */
+struct bnad_stats {
+	struct bnad_drv_stats drv_stats;
+	struct bna_stats *bna_stats;
+};
+
+/* Tx / Rx Resources */
+struct bnad_tx_res_info {
+	struct bna_res_info res_info[BNA_TX_RES_T_MAX];
+};
+
+struct bnad_rx_res_info {
+	struct bna_res_info res_info[BNA_RX_RES_T_MAX];
+};
+
+struct bnad_tx_info {
+	struct bna_tx *tx; /* 1:1 between tx_info & tx */
+	struct bna_tcb *tcb[BNAD_MAX_TXQ_PER_TX];
+} ____cacheline_aligned;
+
+struct bnad_rx_info {
+	struct bna_rx *rx; /* 1:1 between rx_info & rx */
+
+	struct bnad_rx_ctrl rx_ctrl[BNAD_MAX_RXPS_PER_RX];
+} ____cacheline_aligned;
+
+/* Unmap queues for Tx / Rx cleanup */
+struct bnad_skb_unmap {
+	struct sk_buff		*skb;
+	DECLARE_PCI_UNMAP_ADDR(dma_addr)
+};
+
+struct bnad_unmap_q {
+	u32		producer_index;
+	u32		consumer_index;
+	u32 		q_depth;
+	/* This should be the last one */
+	struct bnad_skb_unmap unmap_array[1];
+};
+
+/* Bit mask values for bnad->cfg_flags */
+#define	BNAD_CF_DIM_ENABLED		0x01	/* DIM */
+#define	BNAD_CF_PROMISC			0x02
+#define BNAD_CF_ALLMULTI		0x04
+#define	BNAD_CF_MSIX			0x08	/* If in MSIx mode */
+
+/* Defines for run_flags bit-mask */
+/* Set, tested & cleared using xxx_bit() functions */
+/* Values indicated bit positions */
+#define	BNAD_RF_CEE_RUNNING		1
+#define BNAD_RF_HW_ERROR 		2
+#define BNAD_RF_MBOX_IRQ_DISABLED	3
+#define BNAD_RF_TX_STARTED		4
+#define BNAD_RF_RX_STARTED		5
+#define BNAD_RF_DIM_TIMER_RUNNING	6
+#define BNAD_RF_STATS_TIMER_RUNNING	7
+
+struct bnad {
+	struct net_device 	*netdev;
+
+	/* Data path */
+	struct bnad_tx_info tx_info[BNAD_MAX_TXS];
+	struct bnad_rx_info rx_info[BNAD_MAX_RXS];
+
+	struct vlan_group	*vlan_grp;
+	/*
+	 * These q numbers are global only because
+	 * they are used to calculate MSIx vectors.
+	 * Actually the exact # of queues are per Tx/Rx
+	 * object.
+	 */
+	u32		num_tx;
+	u32		num_rx;
+	u32		num_txq_per_tx;
+	u32		num_rxp_per_rx;
+
+	u32		txq_depth;
+	u32		rxq_depth;
+
+	u8			tx_coalescing_timeo;
+	u8			rx_coalescing_timeo;
+
+	struct bna_rx_config rx_config[BNAD_MAX_RXS];
+	struct bna_tx_config tx_config[BNAD_MAX_TXS];
+
+	u32		rx_csum;
+
+	void __iomem		*bar0;	/* BAR0 address */
+
+	struct bna bna;
+
+	u32		cfg_flags;
+	unsigned long		run_flags;
+
+	struct pci_dev 		*pcidev;
+	u64		mmio_start;
+	u64		mmio_len;
+
+	u32		msix_num;
+	u32		msix_diag_num;
+	struct msix_entry	*msix_table;
+
+	struct mutex		conf_mutex;
+	spinlock_t		bna_lock ____cacheline_aligned;
+
+	/* Timers */
+	struct timer_list	ioc_timer;
+	struct timer_list	dim_timer;
+	struct timer_list	stats_timer;
+
+	/* Control path resources, memory & irq */
+	struct bna_res_info res_info[BNA_RES_T_MAX];
+	struct bnad_tx_res_info tx_res_info[BNAD_MAX_TXS];
+	struct bnad_rx_res_info rx_res_info[BNAD_MAX_RXS];
+
+	struct bnad_completion bnad_completions;
+
+	/* Burnt in MAC address */
+	mac_t			perm_addr;
+
+	struct tasklet_struct	tx_free_tasklet;
+
+	/* Statistics */
+	struct bnad_stats stats;
+	struct net_device_stats net_stats;
+
+	struct bnad_diag *diag;
+
+	char			adapter_name[BNAD_NAME_LEN];
+	char 			port_name[BNAD_NAME_LEN];
+	char			mbox_irq_name[BNAD_NAME_LEN];
+
+	struct bna_ident ident;
+	struct list_head	list_entry;
+};
+
+/*
+ * EXTERN VARIABLES
+ */
+extern struct firmware *bfi_fw;
+extern struct semaphore 	bnad_list_sem;
+extern struct list_head 	bnad_list;
+extern u32 		bna_id; /* Driver Instance Id */
+extern u32 		bnad_rxqs_per_cq;
+
+/*
+ * EXTERN PROTOTYPES
+ */
+extern u32 *cna_get_firmware_buf(struct pci_dev *pdev);
+/* Netdev entry point prototypes */
+extern int bnad_open(struct net_device *netdev);
+extern int bnad_stop(struct net_device *netdev);
+extern int bnad_start_xmit(struct sk_buff *skb, struct net_device *netdev);
+extern void bnad_set_rx_mode(struct net_device *netdev);
+extern struct net_device_stats *bnad_get_netdev_stats(
+				struct net_device *netdev);
+extern int bnad_set_mac_address(struct net_device *netdev, void *mac_addr);
+extern int bnad_change_mtu(struct net_device *netdev, int new_mtu);
+extern void bnad_vlan_rx_register(struct net_device *netdev,
+				struct vlan_group *vlan_grp);
+extern void bnad_vlan_rx_add_vid(struct net_device *netdev,
+				unsigned short vid);
+extern void bnad_vlan_rx_kill_vid(struct net_device *netdev,
+				unsigned short vid);
+extern int bnad_poll(struct net_device *netdev, int *budget);
+extern void bnad_netpoll(struct net_device *netdev);
+extern void bnad_set_ethtool_ops(struct net_device *netdev);
+
+/* Configuration & setup */
+extern struct bnad *bnad_get_bnadev(int bna_id);
+extern void bnad_add_to_list(struct bnad *bnad);
+extern void bnad_remove_from_list(struct bnad *bnad);
+
+extern void bnad_enable_mbox_irq(struct bnad *bnad);
+extern void bnad_disable_mbox_irq(struct bnad *bnad);
+
+extern void bnad_tx_coalescing_timeo_set(struct bnad *bnad);
+extern void bnad_rx_coalescing_timeo_set(struct bnad *bnad);
+
+extern void bnad_q_num_init(struct bnad *bnad);
+extern void bnad_q_num_adjust(struct bnad *bnad, int msix_vectors);
+
+extern int bnad_mem_alloc(struct bnad *bnad,
+				struct bna_mem_info *mem_info);
+extern void bnad_mem_free(struct bnad *bnad,
+				struct bna_mem_info *mem_info);
+
+extern int bnad_setup_rx(struct bnad *bnad, uint rx_id);
+extern int bnad_setup_tx(struct bnad *bnad, uint tx_id);
+extern void bnad_cleanup_tx(struct bnad *bnad, uint tx_id);
+extern void bnad_cleanup_rx(struct bnad *bnad, uint rx_id);
+
+/* Timer start/stop protos */
+extern void bnad_dim_timer_start(struct bnad *bnad);
+extern void bnad_dim_timer_stop(struct bnad *bnad);
+extern void bnad_stats_timer_start(struct bnad *bnad);
+extern void bnad_stats_timer_stop(struct bnad *bnad);
+
+/* Datapath */
+extern void bnad_disable_txrx_irqs(struct bnad *bnad);
+extern void bnad_enable_txrx_irqs(struct bnad *bnad);
+extern u32 bnad_poll_cq(struct bnad *bnad, struct bna_ccb *ccb,
+				int budget);
+extern u32 bnad_tx(struct bnad *bnad, struct bna_tcb *tcb);
+extern irqreturn_t bnad_isr(int irq, void *data);
+
+/* Used by IOCTL */
+extern void bnad_cb_port_disabled(void *arg, enum bna_cb_status status);
+extern void bnad_device_disable(struct bnad *bnad);
+extern int bnad_device_enable(struct bnad *bnad);
+
+/* Statistics */
+extern void bnad_netdev_qstats_fill(struct bnad *bnad);
+extern void bnad_netdev_hwstats_fill(struct bnad *bnad);
+extern int bnad_stats_clr(struct bnad *bnad);
+
+/* Logging */
+
+/**
+ * MACROS
+ */
+/* To set & get the stats counters */
+#define BNAD_UPDATE_CTR(_bnad, _ctr)				\
+				(((_bnad)->stats.drv_stats._ctr)++)
+
+#define BNAD_GET_CTR(_bnad, _ctr) ((_bnad)->stats.drv_stats._ctr)
+
+#define bnad_pci_resource_len(_bnad)	\
+			pci_resource_len((_bnad)->pcidev, 0)
+
+#define bnad_enable_rx_irq_unsafe(_ccb)			\
+{							\
+	bna_ib_coalescing_timer_set((_ccb)->i_dbell,	\
+		(_ccb)->rx_coalescing_timeo);		\
+	bna_ib_ack((_ccb)->i_dbell, 0);			\
+}
+
+/**
+ * INLINE FUNCTIONS
+ */
+static inline void
+bnad_disable_rx_irq(struct bnad *bnad, struct bna_ccb *ccb)
+{
+	bna_ib_coalescing_timer_set(ccb->i_dbell, 0);
+	bna_ib_ack(ccb->i_dbell, 0);
+}
+
+static inline void
+bnad_enable_rx_irq(struct bnad *bnad, struct bna_ccb *ccb)
+{
+	spin_lock_irq(&bnad->bna_lock); /* Because of polling context */
+	bnad_enable_rx_irq_unsafe(ccb);
+	spin_unlock_irq(&bnad->bna_lock);
+}
+
+#endif /* __BNAD_H__ */

^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2010-08-23 21:24 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2010-08-23 21:24 [PATCH 1/6] bna: Brocade 10Gb Ethernet device driver Rasesh Mody
  -- strict thread matches above, loose matches on Subject: below --
2010-08-11  4:24 Rasesh Mody
2010-08-18  0:41 ` David Miller
2010-08-07  3:23 Debashis Dutt
2010-08-09 15:15 ` Stephen Hemminger
2010-08-11 19:45   ` Debashis Dutt
2010-08-04  5:15 Rasesh Mody
2010-08-04 17:09 ` Stephen Hemminger

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.