benet: use the dma state API instead of the pci equivalents
[safe/jmp/linux-2.6] / drivers / net / benet / be_main.c
index bac85f9..8336183 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005 - 2009 ServerEngines
+ * Copyright (C) 2005 - 2010 ServerEngines
  * All rights reserved.
  *
  * This program is free software; you can redistribute it and/or
@@ -26,11 +26,15 @@ MODULE_AUTHOR("ServerEngines Corporation");
 MODULE_LICENSE("GPL");
 
 static unsigned int rx_frag_size = 2048;
+static unsigned int num_vfs;
 module_param(rx_frag_size, uint, S_IRUGO);
+module_param(num_vfs, uint, S_IRUGO);
 MODULE_PARM_DESC(rx_frag_size, "Size of a fragment that holds rcvd data.");
+MODULE_PARM_DESC(num_vfs, "Number of PCI VFs to initialize");
 
 static DEFINE_PCI_DEVICE_TABLE(be_dev_ids) = {
        { PCI_DEVICE(BE_VENDOR_ID, BE_DEVICE_ID1) },
+       { PCI_DEVICE(BE_VENDOR_ID, BE_DEVICE_ID2) },
        { PCI_DEVICE(BE_VENDOR_ID, OC_DEVICE_ID1) },
        { PCI_DEVICE(BE_VENDOR_ID, OC_DEVICE_ID2) },
        { 0 }
@@ -67,6 +71,9 @@ static void be_intr_set(struct be_adapter *adapter, bool enable)
        u32 reg = ioread32(addr);
        u32 enabled = reg & MEMBAR_CTRL_INT_CTRL_HOSTINTR_MASK;
 
+       if (adapter->eeh_err)
+               return;
+
        if (!enabled && enable)
                reg |= MEMBAR_CTRL_INT_CTRL_HOSTINTR_MASK;
        else if (enabled && !enable)
@@ -98,6 +105,10 @@ static void be_eq_notify(struct be_adapter *adapter, u16 qid,
 {
        u32 val = 0;
        val |= qid & DB_EQ_RING_ID_MASK;
+
+       if (adapter->eeh_err)
+               return;
+
        if (arm)
                val |= 1 << DB_EQ_REARM_SHIFT;
        if (clear_int)
@@ -111,6 +122,10 @@ void be_cq_notify(struct be_adapter *adapter, u16 qid, bool arm, u16 num_popped)
 {
        u32 val = 0;
        val |= qid & DB_CQ_RING_ID_MASK;
+
+       if (adapter->eeh_err)
+               return;
+
        if (arm)
                val |= 1 << DB_CQ_REARM_SHIFT;
        val |= num_popped << DB_CQ_NUM_POPPED_SHIFT;
@@ -123,34 +138,41 @@ static int be_mac_addr_set(struct net_device *netdev, void *p)
        struct sockaddr *addr = p;
        int status = 0;
 
+       if (!is_valid_ether_addr(addr->sa_data))
+               return -EADDRNOTAVAIL;
+
+       /* MAC addr configuration will be done in hardware for VFs
+        * by their corresponding PFs. Just copy to netdev addr here
+        */
+       if (!be_physfn(adapter))
+               goto netdev_addr;
+
        status = be_cmd_pmac_del(adapter, adapter->if_handle, adapter->pmac_id);
        if (status)
                return status;
 
        status = be_cmd_pmac_add(adapter, (u8 *)addr->sa_data,
                        adapter->if_handle, &adapter->pmac_id);
+netdev_addr:
        if (!status)
                memcpy(netdev->dev_addr, addr->sa_data, netdev->addr_len);
 
        return status;
 }
 
-static void netdev_stats_update(struct be_adapter *adapter)
+void netdev_stats_update(struct be_adapter *adapter)
 {
        struct be_hw_stats *hw_stats = hw_stats_from_cmd(adapter->stats.cmd.va);
        struct be_rxf_stats *rxf_stats = &hw_stats->rxf;
        struct be_port_rxf_stats *port_stats =
                        &rxf_stats->port[adapter->port_num];
-       struct net_device_stats *dev_stats = &adapter->stats.net_stats;
+       struct net_device_stats *dev_stats = &adapter->netdev->stats;
        struct be_erx_stats *erx_stats = &hw_stats->erx;
 
-       dev_stats->rx_packets = port_stats->rx_total_frames;
-       dev_stats->tx_packets = port_stats->tx_unicastframes +
-               port_stats->tx_multicastframes + port_stats->tx_broadcastframes;
-       dev_stats->rx_bytes = (u64) port_stats->rx_bytes_msd << 32 |
-                               (u64) port_stats->rx_bytes_lsd;
-       dev_stats->tx_bytes = (u64) port_stats->tx_bytes_msd << 32 |
-                               (u64) port_stats->tx_bytes_lsd;
+       dev_stats->rx_packets = drvr_stats(adapter)->be_rx_pkts;
+       dev_stats->tx_packets = drvr_stats(adapter)->be_tx_pkts;
+       dev_stats->rx_bytes = drvr_stats(adapter)->be_rx_bytes;
+       dev_stats->tx_bytes = drvr_stats(adapter)->be_tx_bytes;
 
        /* bad pkts received */
        dev_stats->rx_errors = port_stats->rx_crc_errors +
@@ -168,7 +190,8 @@ static void netdev_stats_update(struct be_adapter *adapter)
                port_stats->rx_udp_checksum_errs;
 
        /*  no space in linux buffers: best possible approximation */
-       dev_stats->rx_dropped = erx_stats->rx_drops_no_fragments[0];
+       dev_stats->rx_dropped =
+               erx_stats->rx_drops_no_fragments[adapter->rx_obj.q.id];
 
        /* detailed rx errors */
        dev_stats->rx_length_errors = port_stats->rx_in_range_errors +
@@ -197,7 +220,7 @@ static void netdev_stats_update(struct be_adapter *adapter)
        /* no space available in linux */
        dev_stats->tx_dropped = 0;
 
-       dev_stats->multicast = port_stats->tx_multicastframes;
+       dev_stats->multicast = port_stats->rx_multicast_frames;
        dev_stats->collisions = 0;
 
        /* detailed tx_errors */
@@ -214,6 +237,7 @@ void be_link_status_update(struct be_adapter *adapter, bool link_up)
 
        /* If link came up or went down */
        if (adapter->link_up != link_up) {
+               adapter->link_speed = -1;
                if (link_up) {
                        netif_start_queue(netdev);
                        netif_carrier_on(netdev);
@@ -269,9 +293,7 @@ static void be_rx_eqd_update(struct be_adapter *adapter)
 
 static struct net_device_stats *be_get_stats(struct net_device *dev)
 {
-       struct be_adapter *adapter = netdev_priv(dev);
-
-       return &adapter->stats.net_stats;
+       return &dev->stats;
 }
 
 static u32 be_calc_rate(u64 bytes, unsigned long ticks)
@@ -307,12 +329,13 @@ static void be_tx_rate_update(struct be_adapter *adapter)
 }
 
 static void be_tx_stats_update(struct be_adapter *adapter,
-                       u32 wrb_cnt, u32 copied, bool stopped)
+                       u32 wrb_cnt, u32 copied, u32 gso_segs, bool stopped)
 {
        struct be_drvr_stats *stats = drvr_stats(adapter);
        stats->be_tx_reqs++;
        stats->be_tx_wrbs += wrb_cnt;
        stats->be_tx_bytes += copied;
+       stats->be_tx_pkts += (gso_segs ? gso_segs : 1);
        if (stopped)
                stats->be_tx_stops++;
 }
@@ -373,26 +396,48 @@ static void wrb_fill_hdr(struct be_eth_hdr_wrb *hdr, struct sk_buff *skb,
        AMAP_SET_BITS(struct amap_eth_hdr_wrb, len, hdr, len);
 }
 
+static void unmap_tx_frag(struct pci_dev *pdev, struct be_eth_wrb *wrb,
+               bool unmap_single)
+{
+       dma_addr_t dma;
+
+       be_dws_le_to_cpu(wrb, sizeof(*wrb));
+
+       dma = (u64)wrb->frag_pa_hi << 32 | (u64)wrb->frag_pa_lo;
+       if (dma != 0) {
+               if (unmap_single)
+                       pci_unmap_single(pdev, dma, wrb->frag_len,
+                               PCI_DMA_TODEVICE);
+               else
+                       pci_unmap_page(pdev, dma, wrb->frag_len,
+                               PCI_DMA_TODEVICE);
+       }
+}
 
 static int make_tx_wrbs(struct be_adapter *adapter,
                struct sk_buff *skb, u32 wrb_cnt, bool dummy_wrb)
 {
-       u64 busaddr;
-       u32 i, copied = 0;
+       dma_addr_t busaddr;
+       int i, copied = 0;
        struct pci_dev *pdev = adapter->pdev;
        struct sk_buff *first_skb = skb;
        struct be_queue_info *txq = &adapter->tx_obj.q;
        struct be_eth_wrb *wrb;
        struct be_eth_hdr_wrb *hdr;
+       bool map_single = false;
+       u16 map_head;
 
-       atomic_add(wrb_cnt, &txq->used);
        hdr = queue_head_node(txq);
        queue_head_inc(txq);
+       map_head = txq->head;
 
        if (skb->len > skb->data_len) {
                int len = skb->len - skb->data_len;
                busaddr = pci_map_single(pdev, skb->data, len,
                                         PCI_DMA_TODEVICE);
+               if (pci_dma_mapping_error(pdev, busaddr))
+                       goto dma_err;
+               map_single = true;
                wrb = queue_head_node(txq);
                wrb_fill(wrb, busaddr, len);
                be_dws_cpu_to_le(wrb, sizeof(*wrb));
@@ -404,8 +449,10 @@ static int make_tx_wrbs(struct be_adapter *adapter,
                struct skb_frag_struct *frag =
                        &skb_shinfo(skb)->frags[i];
                busaddr = pci_map_page(pdev, frag->page,
-                                       frag->page_offset,
-                                       frag->size, PCI_DMA_TODEVICE);
+                                      frag->page_offset,
+                                      frag->size, PCI_DMA_TODEVICE);
+               if (pci_dma_mapping_error(pdev, busaddr))
+                       goto dma_err;
                wrb = queue_head_node(txq);
                wrb_fill(wrb, busaddr, frag->size);
                be_dws_cpu_to_le(wrb, sizeof(*wrb));
@@ -425,10 +472,20 @@ static int make_tx_wrbs(struct be_adapter *adapter,
        be_dws_cpu_to_le(hdr, sizeof(*hdr));
 
        return copied;
+dma_err:
+       txq->head = map_head;
+       while (copied) {
+               wrb = queue_head_node(txq);
+               unmap_tx_frag(pdev, wrb, map_single);
+               map_single = false;
+               copied -= wrb->frag_len;
+               queue_head_inc(txq);
+       }
+       return 0;
 }
 
 static netdev_tx_t be_xmit(struct sk_buff *skb,
-                                struct net_device *netdev)
+                       struct net_device *netdev)
 {
        struct be_adapter *adapter = netdev_priv(netdev);
        struct be_tx_obj *tx_obj = &adapter->tx_obj;
@@ -440,23 +497,30 @@ static netdev_tx_t be_xmit(struct sk_buff *skb,
        wrb_cnt = wrb_cnt_for_skb(skb, &dummy_wrb);
 
        copied = make_tx_wrbs(adapter, skb, wrb_cnt, dummy_wrb);
+       if (copied) {
+               /* record the sent skb in the sent_skb table */
+               BUG_ON(tx_obj->sent_skb_list[start]);
+               tx_obj->sent_skb_list[start] = skb;
+
+               /* Ensure txq has space for the next skb; Else stop the queue
+                * *BEFORE* ringing the tx doorbell, so that we serialze the
+                * tx compls of the current transmit which'll wake up the queue
+                */
+               atomic_add(wrb_cnt, &txq->used);
+               if ((BE_MAX_TX_FRAG_COUNT + atomic_read(&txq->used)) >=
+                                                               txq->len) {
+                       netif_stop_queue(netdev);
+                       stopped = true;
+               }
 
-       /* record the sent skb in the sent_skb table */
-       BUG_ON(tx_obj->sent_skb_list[start]);
-       tx_obj->sent_skb_list[start] = skb;
+               be_txq_notify(adapter, txq->id, wrb_cnt);
 
-       /* Ensure that txq has space for the next skb; Else stop the queue
-        * *BEFORE* ringing the tx doorbell, so that we serialze the
-        * tx compls of the current transmit which'll wake up the queue
-        */
-       if ((BE_MAX_TX_FRAG_COUNT + atomic_read(&txq->used)) >= txq->len) {
-               netif_stop_queue(netdev);
-               stopped = true;
+               be_tx_stats_update(adapter, wrb_cnt, copied,
+                               skb_shinfo(skb)->gso_segs, stopped);
+       } else {
+               txq->head = start;
+               dev_kfree_skb_any(skb);
        }
-
-       be_txq_notify(adapter, txq->id, wrb_cnt);
-
-       be_tx_stats_update(adapter, wrb_cnt, copied, stopped);
        return NETDEV_TX_OK;
 }
 
@@ -464,10 +528,12 @@ static int be_change_mtu(struct net_device *netdev, int new_mtu)
 {
        struct be_adapter *adapter = netdev_priv(netdev);
        if (new_mtu < BE_MIN_MTU ||
-                       new_mtu > BE_MAX_JUMBO_FRAME_SIZE) {
+                       new_mtu > (BE_MAX_JUMBO_FRAME_SIZE -
+                                       (ETH_HLEN + ETH_FCS_LEN))) {
                dev_info(&adapter->pdev->dev,
                        "MTU must be between %d and %d bytes\n",
-                       BE_MIN_MTU, BE_MAX_JUMBO_FRAME_SIZE);
+                       BE_MIN_MTU,
+                       (BE_MAX_JUMBO_FRAME_SIZE - (ETH_HLEN + ETH_FCS_LEN)));
                return -EINVAL;
        }
        dev_info(&adapter->pdev->dev, "MTU changed from %d to %d bytes\n",
@@ -477,17 +543,16 @@ static int be_change_mtu(struct net_device *netdev, int new_mtu)
 }
 
 /*
- * if there are BE_NUM_VLANS_SUPPORTED or lesser number of VLANS configured,
- * program them in BE.  If more than BE_NUM_VLANS_SUPPORTED are configured,
- * set the BE in promiscuous VLAN mode.
+ * A max of 64 (BE_NUM_VLANS_SUPPORTED) vlans can be configured in BE.
+ * If the user configures more, place BE in vlan promiscuous mode.
  */
-static void be_vid_config(struct net_device *netdev)
+static int be_vid_config(struct be_adapter *adapter)
 {
-       struct be_adapter *adapter = netdev_priv(netdev);
        u16 vtag[BE_NUM_VLANS_SUPPORTED];
        u16 ntags = 0, i;
+       int status = 0;
 
-       if (adapter->num_vlans <= BE_NUM_VLANS_SUPPORTED)  {
+       if (adapter->vlans_added <= adapter->max_vlans)  {
                /* Construct VLAN Table to give to HW */
                for (i = 0; i < VLAN_GROUP_ARRAY_LEN; i++) {
                        if (adapter->vlan_tag[i]) {
@@ -495,12 +560,13 @@ static void be_vid_config(struct net_device *netdev)
                                ntags++;
                        }
                }
-               be_cmd_vlan_config(adapter, adapter->if_handle,
-                       vtag, ntags, 1, 0);
+               status = be_cmd_vlan_config(adapter, adapter->if_handle,
+                                       vtag, ntags, 1, 0);
        } else {
-               be_cmd_vlan_config(adapter, adapter->if_handle,
-                       NULL, 0, 1, 1);
+               status = be_cmd_vlan_config(adapter, adapter->if_handle,
+                                       NULL, 0, 1, 1);
        }
+       return status;
 }
 
 static void be_vlan_register(struct net_device *netdev, struct vlan_group *grp)
@@ -520,21 +586,27 @@ static void be_vlan_add_vid(struct net_device *netdev, u16 vid)
 {
        struct be_adapter *adapter = netdev_priv(netdev);
 
-       adapter->num_vlans++;
-       adapter->vlan_tag[vid] = 1;
+       if (!be_physfn(adapter))
+               return;
 
-       be_vid_config(netdev);
+       adapter->vlan_tag[vid] = 1;
+       adapter->vlans_added++;
+       if (adapter->vlans_added <= (adapter->max_vlans + 1))
+               be_vid_config(adapter);
 }
 
 static void be_vlan_rem_vid(struct net_device *netdev, u16 vid)
 {
        struct be_adapter *adapter = netdev_priv(netdev);
 
-       adapter->num_vlans--;
-       adapter->vlan_tag[vid] = 0;
+       if (!be_physfn(adapter))
+               return;
 
+       adapter->vlan_tag[vid] = 0;
        vlan_group_set_device(adapter->vlan_grp, vid, NULL);
-       be_vid_config(netdev);
+       adapter->vlans_added--;
+       if (adapter->vlans_added <= adapter->max_vlans)
+               be_vid_config(adapter);
 }
 
 static void be_set_multicast_list(struct net_device *netdev)
@@ -553,17 +625,42 @@ static void be_set_multicast_list(struct net_device *netdev)
                be_cmd_promiscuous_config(adapter, adapter->port_num, 0);
        }
 
-       if (netdev->flags & IFF_ALLMULTI) {
-               be_cmd_multicast_set(adapter, adapter->if_handle, NULL, 0);
+       /* Enable multicast promisc if num configured exceeds what we support */
+       if (netdev->flags & IFF_ALLMULTI ||
+           netdev_mc_count(netdev) > BE_MAX_MC) {
+               be_cmd_multicast_set(adapter, adapter->if_handle, NULL,
+                               &adapter->mc_cmd_mem);
                goto done;
        }
 
-       be_cmd_multicast_set(adapter, adapter->if_handle, netdev->mc_list,
-               netdev->mc_count);
+       be_cmd_multicast_set(adapter, adapter->if_handle, netdev,
+               &adapter->mc_cmd_mem);
 done:
        return;
 }
 
+static int be_set_vf_mac(struct net_device *netdev, int vf, u8 *mac)
+{
+       struct be_adapter *adapter = netdev_priv(netdev);
+       int status;
+
+       if (!adapter->sriov_enabled)
+               return -EPERM;
+
+       if (!is_valid_ether_addr(mac) || (vf >= num_vfs))
+               return -EINVAL;
+
+       status = be_cmd_pmac_del(adapter, adapter->vf_if_handle[vf],
+                               adapter->vf_pmac_id[vf]);
+
+       status = be_cmd_pmac_add(adapter, mac, adapter->vf_if_handle[vf],
+                               &adapter->vf_pmac_id[vf]);
+       if (!status)
+               dev_err(&adapter->pdev->dev, "MAC %pM set on VF %d Failed\n",
+                               mac, vf);
+       return status;
+}
+
 static void be_rx_rate_update(struct be_adapter *adapter)
 {
        struct be_drvr_stats *stats = drvr_stats(adapter);
@@ -594,6 +691,7 @@ static void be_rx_stats_update(struct be_adapter *adapter,
        stats->be_rx_compl++;
        stats->be_rx_frags += numfrags;
        stats->be_rx_bytes += pktsize;
+       stats->be_rx_pkts++;
 }
 
 static inline bool do_pkt_csum(struct be_eth_rx_compl *rxcp, bool cso)
@@ -621,9 +719,11 @@ get_rx_page_info(struct be_adapter *adapter, u16 frag_idx)
        rx_page_info = &adapter->rx_obj.page_info_tbl[frag_idx];
        BUG_ON(!rx_page_info->page);
 
-       if (rx_page_info->last_page_user)
-               pci_unmap_page(adapter->pdev, pci_unmap_addr(rx_page_info, bus),
+       if (rx_page_info->last_page_user) {
+               pci_unmap_page(adapter->pdev, dma_unmap_addr(rx_page_info, bus),
                        adapter->big_page_size, PCI_DMA_FROMDEVICE);
+               rx_page_info->last_page_user = false;
+       }
 
        atomic_dec(&rxq->used);
        return rx_page_info;
@@ -653,17 +753,17 @@ static void be_rx_compl_discard(struct be_adapter *adapter,
  * indicated by rxcp.
  */
 static void skb_fill_rx_data(struct be_adapter *adapter,
-                       struct sk_buff *skb, struct be_eth_rx_compl *rxcp)
+                       struct sk_buff *skb, struct be_eth_rx_compl *rxcp,
+                       u16 num_rcvd)
 {
        struct be_queue_info *rxq = &adapter->rx_obj.q;
        struct be_rx_page_info *page_info;
-       u16 rxq_idx, i, num_rcvd, j;
+       u16 rxq_idx, i, j;
        u32 pktsize, hdr_len, curr_frag_len, size;
        u8 *start;
 
        rxq_idx = AMAP_GET_BITS(struct amap_eth_rx_compl, fragndx, rxcp);
        pktsize = AMAP_GET_BITS(struct amap_eth_rx_compl, pktsize, rxcp);
-       num_rcvd = AMAP_GET_BITS(struct amap_eth_rx_compl, numfrags, rxcp);
 
        page_info = get_rx_page_info(adapter, rxq_idx);
 
@@ -691,7 +791,7 @@ static void skb_fill_rx_data(struct be_adapter *adapter,
                skb->data_len = curr_frag_len - hdr_len;
                skb->tail += hdr_len;
        }
-       memset(page_info, 0, sizeof(*page_info));
+       page_info->page = NULL;
 
        if (pktsize <= rx_frag_size) {
                BUG_ON(num_rcvd != 1);
@@ -724,7 +824,7 @@ static void skb_fill_rx_data(struct be_adapter *adapter,
                skb->len += curr_frag_len;
                skb->data_len += curr_frag_len;
 
-               memset(page_info, 0, sizeof(*page_info));
+               page_info->page = NULL;
        }
        BUG_ON(j > MAX_SKB_FRAGS);
 
@@ -738,21 +838,24 @@ static void be_rx_compl_process(struct be_adapter *adapter,
                        struct be_eth_rx_compl *rxcp)
 {
        struct sk_buff *skb;
-       u32 vtp, vid;
+       u32 vlanf, vid;
+       u16 num_rcvd;
+       u8 vtm;
 
-       vtp = AMAP_GET_BITS(struct amap_eth_rx_compl, vtp, rxcp);
+       num_rcvd = AMAP_GET_BITS(struct amap_eth_rx_compl, numfrags, rxcp);
+       /* Is it a flush compl that has no data */
+       if (unlikely(num_rcvd == 0))
+               return;
 
-       skb = netdev_alloc_skb(adapter->netdev, BE_HDR_LEN + NET_IP_ALIGN);
-       if (!skb) {
+       skb = netdev_alloc_skb_ip_align(adapter->netdev, BE_HDR_LEN);
+       if (unlikely(!skb)) {
                if (net_ratelimit())
                        dev_warn(&adapter->pdev->dev, "skb alloc failed\n");
                be_rx_compl_discard(adapter, rxcp);
                return;
        }
 
-       skb_reserve(skb, NET_IP_ALIGN);
-
-       skb_fill_rx_data(adapter, skb, rxcp);
+       skb_fill_rx_data(adapter, skb, rxcp, num_rcvd);
 
        if (do_pkt_csum(rxcp, adapter->rx_csum))
                skb->ip_summed = CHECKSUM_NONE;
@@ -761,15 +864,22 @@ static void be_rx_compl_process(struct be_adapter *adapter,
 
        skb->truesize = skb->len + sizeof(struct sk_buff);
        skb->protocol = eth_type_trans(skb, adapter->netdev);
-       skb->dev = adapter->netdev;
 
-       if (vtp) {
-               if (!adapter->vlan_grp || adapter->num_vlans == 0) {
+       vlanf = AMAP_GET_BITS(struct amap_eth_rx_compl, vtp, rxcp);
+       vtm = AMAP_GET_BITS(struct amap_eth_rx_compl, vtm, rxcp);
+
+       /* vlanf could be wrongly set in some cards.
+        * ignore if vtm is not set */
+       if ((adapter->cap & 0x400) && !vtm)
+               vlanf = 0;
+
+       if (unlikely(vlanf)) {
+               if (!adapter->vlan_grp || adapter->vlans_added == 0) {
                        kfree_skb(skb);
                        return;
                }
                vid = AMAP_GET_BITS(struct amap_eth_rx_compl, vlan_tag, rxcp);
-               vid = be16_to_cpu(vid);
+               vid = swab16(vid);
                vlan_hwaccel_receive_skb(skb, adapter->vlan_grp, vid);
        } else {
                netif_receive_skb(skb);
@@ -788,11 +898,22 @@ static void be_rx_compl_process_gro(struct be_adapter *adapter,
        struct be_eq_obj *eq_obj =  &adapter->rx_eq;
        u32 num_rcvd, pkt_size, remaining, vlanf, curr_frag_len;
        u16 i, rxq_idx = 0, vid, j;
+       u8 vtm;
 
        num_rcvd = AMAP_GET_BITS(struct amap_eth_rx_compl, numfrags, rxcp);
+       /* Is it a flush compl that has no data */
+       if (unlikely(num_rcvd == 0))
+               return;
+
        pkt_size = AMAP_GET_BITS(struct amap_eth_rx_compl, pktsize, rxcp);
        vlanf = AMAP_GET_BITS(struct amap_eth_rx_compl, vtp, rxcp);
        rxq_idx = AMAP_GET_BITS(struct amap_eth_rx_compl, fragndx, rxcp);
+       vtm = AMAP_GET_BITS(struct amap_eth_rx_compl, vtm, rxcp);
+
+       /* vlanf could be wrongly set in some cards.
+        * ignore if vtm is not set */
+       if ((adapter->cap & 0x400) && !vtm)
+               vlanf = 0;
 
        skb = napi_get_frags(&eq_obj->napi);
        if (!skb) {
@@ -835,9 +956,9 @@ static void be_rx_compl_process_gro(struct be_adapter *adapter,
                napi_gro_frags(&eq_obj->napi);
        } else {
                vid = AMAP_GET_BITS(struct amap_eth_rx_compl, vlan_tag, rxcp);
-               vid = be16_to_cpu(vid);
+               vid = swab16(vid);
 
-               if (!adapter->vlan_grp || adapter->num_vlans == 0)
+               if (!adapter->vlan_grp || adapter->vlans_added == 0)
                        return;
 
                vlan_gro_frags(&eq_obj->napi, adapter->vlan_grp, vid);
@@ -885,7 +1006,7 @@ static inline struct page *be_alloc_pages(u32 size)
 static void be_post_rx_frags(struct be_adapter *adapter)
 {
        struct be_rx_page_info *page_info_tbl = adapter->rx_obj.page_info_tbl;
-       struct be_rx_page_info *page_info = NULL;
+       struct be_rx_page_info *page_info = NULL, *prev_page_info = NULL;
        struct be_queue_info *rxq = &adapter->rx_obj.q;
        struct page *pagep = NULL;
        struct be_eth_rx_d *rxd;
@@ -910,13 +1031,12 @@ static void be_post_rx_frags(struct be_adapter *adapter)
                }
                page_offset = page_info->page_offset;
                page_info->page = pagep;
-               pci_unmap_addr_set(page_info, bus, page_dmaaddr);
+               dma_unmap_addr_set(page_info, bus, page_dmaaddr);
                frag_dmaaddr = page_dmaaddr + page_info->page_offset;
 
                rxd = queue_head_node(rxq);
                rxd->fragpa_lo = cpu_to_le32(frag_dmaaddr & 0xFFFFFFFF);
                rxd->fragpa_hi = cpu_to_le32(upper_32_bits(frag_dmaaddr));
-               queue_head_inc(rxq);
 
                /* Any space left in the current big page for another frag? */
                if ((page_offset + rx_frag_size + rx_frag_size) >
@@ -924,10 +1044,13 @@ static void be_post_rx_frags(struct be_adapter *adapter)
                        pagep = NULL;
                        page_info->last_page_user = true;
                }
+
+               prev_page_info = page_info;
+               queue_head_inc(rxq);
                page_info = &page_info_tbl[rxq->head];
        }
        if (pagep)
-               page_info->last_page_user = true;
+               prev_page_info->last_page_user = true;
 
        if (posted) {
                atomic_add(posted, &rxq->used);
@@ -961,23 +1084,23 @@ static void be_tx_compl_process(struct be_adapter *adapter, u16 last_index)
        struct be_eth_wrb *wrb;
        struct sk_buff **sent_skbs = adapter->tx_obj.sent_skb_list;
        struct sk_buff *sent_skb;
-       u64 busaddr;
-       u16 cur_index, num_wrbs = 0;
+       u16 cur_index, num_wrbs = 1; /* account for hdr wrb */
+       bool unmap_skb_hdr = true;
 
-       cur_index = txq->tail;
-       sent_skb = sent_skbs[cur_index];
+       sent_skb = sent_skbs[txq->tail];
        BUG_ON(!sent_skb);
-       sent_skbs[cur_index] = NULL;
+       sent_skbs[txq->tail] = NULL;
+
+       /* skip header wrb */
+       queue_tail_inc(txq);
 
        do {
                cur_index = txq->tail;
                wrb = queue_tail_node(txq);
-               be_dws_le_to_cpu(wrb, sizeof(*wrb));
-               busaddr = ((u64)wrb->frag_pa_hi << 32) | (u64)wrb->frag_pa_lo;
-               if (busaddr != 0) {
-                       pci_unmap_single(adapter->pdev, busaddr,
-                               wrb->frag_len, PCI_DMA_TODEVICE);
-               }
+               unmap_tx_frag(adapter->pdev, wrb, (unmap_skb_hdr &&
+                                       sent_skb->len > sent_skb->data_len));
+               unmap_skb_hdr = false;
+
                num_wrbs++;
                queue_tail_inc(txq);
        } while (cur_index != last_index);
@@ -1068,6 +1191,9 @@ static void be_tx_compl_clean(struct be_adapter *adapter)
        struct be_queue_info *txq = &adapter->tx_obj.q;
        struct be_eth_tx_compl *txcp;
        u16 end_idx, cmpl = 0, timeo = 0;
+       struct sk_buff **sent_skbs = adapter->tx_obj.sent_skb_list;
+       struct sk_buff *sent_skb;
+       bool dummy_wrb;
 
        /* Wait for a max of 200ms for all the tx-completions to arrive. */
        do {
@@ -1091,6 +1217,15 @@ static void be_tx_compl_clean(struct be_adapter *adapter)
        if (atomic_read(&txq->used))
                dev_err(&adapter->pdev->dev, "%d pending tx-completions\n",
                        atomic_read(&txq->used));
+
+       /* free posted tx for which compls will never arrive */
+       while (atomic_read(&txq->used)) {
+               sent_skb = sent_skbs[txq->tail];
+               end_idx = txq->tail;
+               index_adv(&end_idx,
+                       wrb_cnt_for_skb(sent_skb, &dummy_wrb) - 1, txq->len);
+               be_tx_compl_process(adapter, end_idx);
+       }
 }
 
 static void be_mcc_queues_destroy(struct be_adapter *adapter)
@@ -1183,6 +1318,8 @@ static int be_tx_queues_create(struct be_adapter *adapter)
        /* Ask BE to create Tx Event queue */
        if (be_cmd_eq_create(adapter, eq, adapter->tx_eq.cur_eqd))
                goto tx_eq_free;
+       adapter->base_eq_id = adapter->tx_eq.q.id;
+
        /* Alloc TX eth compl queue */
        cq = &adapter->tx_obj.cq;
        if (be_queue_alloc(adapter, cq, TX_CQ_LEN,
@@ -1223,6 +1360,11 @@ static void be_rx_queues_destroy(struct be_adapter *adapter)
        q = &adapter->rx_obj.q;
        if (q->created) {
                be_cmd_q_destroy(adapter, q, QTYPE_RXQ);
+
+               /* After the rxq is invalidated, wait for a grace time
+                * of 1ms for all dma to end and the flush compl to arrive
+                */
+               mdelay(1);
                be_rx_q_clean(adapter);
        }
        be_queue_free(adapter, q);
@@ -1305,7 +1447,7 @@ rx_eq_free:
 /* There are 8 evt ids per func. Retruns the evt id's bit number */
 static inline int be_evt_bit_get(struct be_adapter *adapter, u32 eq_id)
 {
-       return eq_id - 8 * be_pci_func(adapter);
+       return eq_id - adapter->base_eq_id;
 }
 
 static irqreturn_t be_intx(int irq, void *dev)
@@ -1314,7 +1456,7 @@ static irqreturn_t be_intx(int irq, void *dev)
        int isr;
 
        isr = ioread32(adapter->csr + CEV_ISR0_OFFSET +
-                       be_pci_func(adapter) * CEV_ISR_SIZE);
+               (adapter->tx_eq.q.id/ 8) * CEV_ISR_SIZE);
        if (!isr)
                return IRQ_NONE;
 
@@ -1363,6 +1505,7 @@ int be_poll_rx(struct napi_struct *napi, int budget)
        struct be_eth_rx_compl *rxcp;
        u32 work_done;
 
+       adapter->stats.drvr_stats.be_rx_polls++;
        for (work_done = 0; work_done < budget; work_done++) {
                rxcp = be_rx_compl_get(adapter);
                if (!rxcp)
@@ -1391,23 +1534,38 @@ int be_poll_rx(struct napi_struct *napi, int budget)
        return work_done;
 }
 
-void be_process_tx(struct be_adapter *adapter)
+/* As TX and MCC share the same EQ check for both TX and MCC completions.
+ * For TX/MCC we don't honour budget; consume everything
+ */
+static int be_poll_tx_mcc(struct napi_struct *napi, int budget)
 {
+       struct be_eq_obj *tx_eq = container_of(napi, struct be_eq_obj, napi);
+       struct be_adapter *adapter =
+               container_of(tx_eq, struct be_adapter, tx_eq);
        struct be_queue_info *txq = &adapter->tx_obj.q;
        struct be_queue_info *tx_cq = &adapter->tx_obj.cq;
        struct be_eth_tx_compl *txcp;
-       u32 num_cmpl = 0;
+       int tx_compl = 0, mcc_compl, status = 0;
        u16 end_idx;
 
        while ((txcp = be_tx_compl_get(tx_cq))) {
                end_idx = AMAP_GET_BITS(struct amap_eth_tx_compl,
-                                       wrb_index, txcp);
+                               wrb_index, txcp);
                be_tx_compl_process(adapter, end_idx);
-               num_cmpl++;
+               tx_compl++;
        }
 
-       if (num_cmpl) {
-               be_cq_notify(adapter, tx_cq->id, true, num_cmpl);
+       mcc_compl = be_process_mcc(adapter, &status);
+
+       napi_complete(napi);
+
+       if (mcc_compl) {
+               struct be_mcc_obj *mcc_obj = &adapter->mcc_obj;
+               be_cq_notify(adapter, mcc_obj->cq.id, true, mcc_compl);
+       }
+
+       if (tx_compl) {
+               be_cq_notify(adapter, adapter->tx_obj.cq.id, true, tx_compl);
 
                /* As Tx wrbs have been freed up, wake up netdev queue if
                 * it was stopped due to lack of tx wrbs.
@@ -1418,24 +1576,8 @@ void be_process_tx(struct be_adapter *adapter)
                }
 
                drvr_stats(adapter)->be_tx_events++;
-               drvr_stats(adapter)->be_tx_compl += num_cmpl;
+               drvr_stats(adapter)->be_tx_compl += tx_compl;
        }
-}
-
-/* As TX and MCC share the same EQ check for both TX and MCC completions.
- * For TX/MCC we don't honour budget; consume everything
- */
-static int be_poll_tx_mcc(struct napi_struct *napi, int budget)
-{
-       struct be_eq_obj *tx_eq = container_of(napi, struct be_eq_obj, napi);
-       struct be_adapter *adapter =
-               container_of(tx_eq, struct be_adapter, tx_eq);
-
-       napi_complete(napi);
-
-       be_process_tx(adapter);
-
-       be_process_mcc(adapter);
 
        return 1;
 }
@@ -1444,12 +1586,8 @@ static void be_worker(struct work_struct *work)
 {
        struct be_adapter *adapter =
                container_of(work, struct be_adapter, work.work);
-       int status;
 
-       /* Get Stats */
-       status = be_cmd_get_stats(adapter, &adapter->stats.cmd);
-       if (!status)
-               netdev_stats_update(adapter);
+       be_cmd_get_stats(adapter, &adapter->stats.cmd);
 
        /* Set EQ delay */
        be_rx_eqd_update(adapter);
@@ -1465,6 +1603,14 @@ static void be_worker(struct work_struct *work)
        schedule_delayed_work(&adapter->work, msecs_to_jiffies(1000));
 }
 
+static void be_msix_disable(struct be_adapter *adapter)
+{
+       if (adapter->msix_enabled) {
+               pci_disable_msix(adapter->pdev);
+               adapter->msix_enabled = false;
+       }
+}
+
 static void be_msix_enable(struct be_adapter *adapter)
 {
        int i, status;
@@ -1479,6 +1625,28 @@ static void be_msix_enable(struct be_adapter *adapter)
        return;
 }
 
+static void be_sriov_enable(struct be_adapter *adapter)
+{
+#ifdef CONFIG_PCI_IOV
+       int status;
+       if (be_physfn(adapter) && num_vfs) {
+               status = pci_enable_sriov(adapter->pdev, num_vfs);
+               adapter->sriov_enabled = status ? false : true;
+       }
+#endif
+       return;
+}
+
+static void be_sriov_disable(struct be_adapter *adapter)
+{
+#ifdef CONFIG_PCI_IOV
+       if (adapter->sriov_enabled) {
+               pci_disable_sriov(adapter->pdev);
+               adapter->sriov_enabled = false;
+       }
+#endif
+}
+
 static inline int be_msix_vec_get(struct be_adapter *adapter, u32 eq_id)
 {
        return adapter->msix_entries[
@@ -1536,6 +1704,9 @@ static int be_irq_register(struct be_adapter *adapter)
                status = be_msix_register(adapter);
                if (status == 0)
                        goto done;
+               /* INTx is not supported for VF */
+               if (!be_physfn(adapter))
+                       return status;
        }
 
        /* INTx */
@@ -1580,6 +1751,8 @@ static int be_open(struct net_device *netdev)
        struct be_eq_obj *tx_eq = &adapter->tx_eq;
        bool link_up;
        int status;
+       u8 mac_speed;
+       u16 link_speed;
 
        /* First time posting */
        be_post_rx_frags(adapter);
@@ -1598,35 +1771,114 @@ static int be_open(struct net_device *netdev)
        /* Rx compl queue may be in unarmed state; rearm it */
        be_cq_notify(adapter, adapter->rx_obj.cq.id, true, 0);
 
-       status = be_cmd_link_status_query(adapter, &link_up);
+       /* Now that interrupts are on we can process async mcc */
+       be_async_mcc_enable(adapter);
+
+       status = be_cmd_link_status_query(adapter, &link_up, &mac_speed,
+                       &link_speed);
        if (status)
-               return status;
+               goto ret_sts;
        be_link_status_update(adapter, link_up);
 
+       if (be_physfn(adapter))
+               status = be_vid_config(adapter);
+       if (status)
+               goto ret_sts;
+
+       if (be_physfn(adapter)) {
+               status = be_cmd_set_flow_control(adapter,
+                               adapter->tx_fc, adapter->rx_fc);
+               if (status)
+                       goto ret_sts;
+       }
+
        schedule_delayed_work(&adapter->work, msecs_to_jiffies(100));
-       return 0;
+ret_sts:
+       return status;
+}
+
+static int be_setup_wol(struct be_adapter *adapter, bool enable)
+{
+       struct be_dma_mem cmd;
+       int status = 0;
+       u8 mac[ETH_ALEN];
+
+       memset(mac, 0, ETH_ALEN);
+
+       cmd.size = sizeof(struct be_cmd_req_acpi_wol_magic_config);
+       cmd.va = pci_alloc_consistent(adapter->pdev, cmd.size, &cmd.dma);
+       if (cmd.va == NULL)
+               return -1;
+       memset(cmd.va, 0, cmd.size);
+
+       if (enable) {
+               status = pci_write_config_dword(adapter->pdev,
+                       PCICFG_PM_CONTROL_OFFSET, PCICFG_PM_CONTROL_MASK);
+               if (status) {
+                       dev_err(&adapter->pdev->dev,
+                               "Could not enable Wake-on-lan\n");
+                       pci_free_consistent(adapter->pdev, cmd.size, cmd.va,
+                                       cmd.dma);
+                       return status;
+               }
+               status = be_cmd_enable_magic_wol(adapter,
+                               adapter->netdev->dev_addr, &cmd);
+               pci_enable_wake(adapter->pdev, PCI_D3hot, 1);
+               pci_enable_wake(adapter->pdev, PCI_D3cold, 1);
+       } else {
+               status = be_cmd_enable_magic_wol(adapter, mac, &cmd);
+               pci_enable_wake(adapter->pdev, PCI_D3hot, 0);
+               pci_enable_wake(adapter->pdev, PCI_D3cold, 0);
+       }
+
+       pci_free_consistent(adapter->pdev, cmd.size, cmd.va, cmd.dma);
+       return status;
 }
 
 static int be_setup(struct be_adapter *adapter)
 {
        struct net_device *netdev = adapter->netdev;
-       u32 if_flags;
+       u32 cap_flags, en_flags, vf = 0;
        int status;
+       u8 mac[ETH_ALEN];
 
-       if_flags = BE_IF_FLAGS_BROADCAST | BE_IF_FLAGS_PROMISCUOUS |
-               BE_IF_FLAGS_MCAST_PROMISCUOUS | BE_IF_FLAGS_UNTAGGED |
-               BE_IF_FLAGS_PASS_L3L4_ERRORS;
-       status = be_cmd_if_create(adapter, if_flags, netdev->dev_addr,
-                       false/* pmac_invalid */, &adapter->if_handle,
-                       &adapter->pmac_id);
-       if (status != 0)
-               goto do_none;
+       cap_flags = en_flags = BE_IF_FLAGS_UNTAGGED | BE_IF_FLAGS_BROADCAST;
 
-       be_vid_config(netdev);
+       if (be_physfn(adapter)) {
+               cap_flags |= BE_IF_FLAGS_MCAST_PROMISCUOUS |
+                               BE_IF_FLAGS_PROMISCUOUS |
+                               BE_IF_FLAGS_PASS_L3L4_ERRORS;
+               en_flags |= BE_IF_FLAGS_PASS_L3L4_ERRORS;
+       }
 
-       status = be_cmd_set_flow_control(adapter, true, true);
+       status = be_cmd_if_create(adapter, cap_flags, en_flags,
+                       netdev->dev_addr, false/* pmac_invalid */,
+                       &adapter->if_handle, &adapter->pmac_id, 0);
        if (status != 0)
-               goto if_destroy;
+               goto do_none;
+
+       if (be_physfn(adapter)) {
+               while (vf < num_vfs) {
+                       cap_flags = en_flags = BE_IF_FLAGS_UNTAGGED
+                                       | BE_IF_FLAGS_BROADCAST;
+                       status = be_cmd_if_create(adapter, cap_flags, en_flags,
+                                       mac, true, &adapter->vf_if_handle[vf],
+                                       NULL, vf+1);
+                       if (status) {
+                               dev_err(&adapter->pdev->dev,
+                               "Interface Create failed for VF %d\n", vf);
+                               goto if_destroy;
+                       }
+                       vf++;
+               } while (vf < num_vfs);
+       } else if (!be_physfn(adapter)) {
+               status = be_cmd_mac_addr_query(adapter, mac,
+                       MAC_ADDRESS_TYPE_NETWORK, false, adapter->if_handle);
+               if (!status) {
+                       memcpy(adapter->netdev->dev_addr, mac, ETH_ALEN);
+                       memcpy(adapter->netdev->perm_addr, mac, ETH_ALEN);
+               }
+       }
 
        status = be_tx_queues_create(adapter);
        if (status != 0)
@@ -1640,6 +1892,8 @@ static int be_setup(struct be_adapter *adapter)
        if (status != 0)
                goto rx_qs_destroy;
 
+       adapter->link_speed = -1;
+
        return 0;
 
 rx_qs_destroy:
@@ -1647,6 +1901,9 @@ rx_qs_destroy:
 tx_qs_destroy:
        be_tx_queues_destroy(adapter);
 if_destroy:
+       for (vf = 0; vf < num_vfs; vf++)
+               if (adapter->vf_if_handle[vf])
+                       be_cmd_if_destroy(adapter, adapter->vf_if_handle[vf]);
        be_cmd_if_destroy(adapter, adapter->if_handle);
 do_none:
        return status;
@@ -1660,6 +1917,8 @@ static int be_clear(struct be_adapter *adapter)
 
        be_cmd_if_destroy(adapter, adapter->if_handle);
 
+       /* tell fw we're done with firing cmds */
+       be_cmd_fw_clean(adapter);
        return 0;
 }
 
@@ -1672,6 +1931,8 @@ static int be_close(struct net_device *netdev)
 
        cancel_delayed_work_sync(&adapter->work);
 
+       be_async_mcc_disable(adapter);
+
        netif_stop_queue(netdev);
        netif_carrier_off(netdev);
        adapter->link_up = false;
@@ -1699,6 +1960,217 @@ static int be_close(struct net_device *netdev)
        return 0;
 }
 
+#define FW_FILE_HDR_SIGN       "ServerEngines Corp. "
+char flash_cookie[2][16] =     {"*** SE FLAS",
+                               "H DIRECTORY *** "};
+
+static bool be_flash_redboot(struct be_adapter *adapter,
+                       const u8 *p, u32 img_start, int image_size,
+                       int hdr_size)
+{
+       u32 crc_offset;
+       u8 flashed_crc[4];
+       int status;
+
+       crc_offset = hdr_size + img_start + image_size - 4;
+
+       p += crc_offset;
+
+       status = be_cmd_get_flash_crc(adapter, flashed_crc,
+                       (image_size - 4));
+       if (status) {
+               dev_err(&adapter->pdev->dev,
+               "could not get crc from flash, not flashing redboot\n");
+               return false;
+       }
+
+       /*update redboot only if crc does not match*/
+       if (!memcmp(flashed_crc, p, 4))
+               return false;
+       else
+               return true;
+}
+
+static int be_flash_data(struct be_adapter *adapter,
+                       const struct firmware *fw,
+                       struct be_dma_mem *flash_cmd, int num_of_images)
+
+{
+       int status = 0, i, filehdr_size = 0;
+       u32 total_bytes = 0, flash_op;
+       int num_bytes;
+       const u8 *p = fw->data;
+       struct be_cmd_write_flashrom *req = flash_cmd->va;
+       struct flash_comp *pflashcomp;
+       int num_comp;
+
+       struct flash_comp gen3_flash_types[9] = {
+               { FLASH_iSCSI_PRIMARY_IMAGE_START_g3, IMG_TYPE_ISCSI_ACTIVE,
+                       FLASH_IMAGE_MAX_SIZE_g3},
+               { FLASH_REDBOOT_START_g3, IMG_TYPE_REDBOOT,
+                       FLASH_REDBOOT_IMAGE_MAX_SIZE_g3},
+               { FLASH_iSCSI_BIOS_START_g3, IMG_TYPE_BIOS,
+                       FLASH_BIOS_IMAGE_MAX_SIZE_g3},
+               { FLASH_PXE_BIOS_START_g3, IMG_TYPE_PXE_BIOS,
+                       FLASH_BIOS_IMAGE_MAX_SIZE_g3},
+               { FLASH_FCoE_BIOS_START_g3, IMG_TYPE_FCOE_BIOS,
+                       FLASH_BIOS_IMAGE_MAX_SIZE_g3},
+               { FLASH_iSCSI_BACKUP_IMAGE_START_g3, IMG_TYPE_ISCSI_BACKUP,
+                       FLASH_IMAGE_MAX_SIZE_g3},
+               { FLASH_FCoE_PRIMARY_IMAGE_START_g3, IMG_TYPE_FCOE_FW_ACTIVE,
+                       FLASH_IMAGE_MAX_SIZE_g3},
+               { FLASH_FCoE_BACKUP_IMAGE_START_g3, IMG_TYPE_FCOE_FW_BACKUP,
+                       FLASH_IMAGE_MAX_SIZE_g3},
+               { FLASH_NCSI_START_g3, IMG_TYPE_NCSI_FW,
+                       FLASH_NCSI_IMAGE_MAX_SIZE_g3}
+       };
+       struct flash_comp gen2_flash_types[8] = {
+               { FLASH_iSCSI_PRIMARY_IMAGE_START_g2, IMG_TYPE_ISCSI_ACTIVE,
+                       FLASH_IMAGE_MAX_SIZE_g2},
+               { FLASH_REDBOOT_START_g2, IMG_TYPE_REDBOOT,
+                       FLASH_REDBOOT_IMAGE_MAX_SIZE_g2},
+               { FLASH_iSCSI_BIOS_START_g2, IMG_TYPE_BIOS,
+                       FLASH_BIOS_IMAGE_MAX_SIZE_g2},
+               { FLASH_PXE_BIOS_START_g2, IMG_TYPE_PXE_BIOS,
+                       FLASH_BIOS_IMAGE_MAX_SIZE_g2},
+               { FLASH_FCoE_BIOS_START_g2, IMG_TYPE_FCOE_BIOS,
+                       FLASH_BIOS_IMAGE_MAX_SIZE_g2},
+               { FLASH_iSCSI_BACKUP_IMAGE_START_g2, IMG_TYPE_ISCSI_BACKUP,
+                       FLASH_IMAGE_MAX_SIZE_g2},
+               { FLASH_FCoE_PRIMARY_IMAGE_START_g2, IMG_TYPE_FCOE_FW_ACTIVE,
+                       FLASH_IMAGE_MAX_SIZE_g2},
+               { FLASH_FCoE_BACKUP_IMAGE_START_g2, IMG_TYPE_FCOE_FW_BACKUP,
+                        FLASH_IMAGE_MAX_SIZE_g2}
+       };
+
+       if (adapter->generation == BE_GEN3) {
+               pflashcomp = gen3_flash_types;
+               filehdr_size = sizeof(struct flash_file_hdr_g3);
+               num_comp = 9;
+       } else {
+               pflashcomp = gen2_flash_types;
+               filehdr_size = sizeof(struct flash_file_hdr_g2);
+               num_comp = 8;
+       }
+       for (i = 0; i < num_comp; i++) {
+               if ((pflashcomp[i].optype == IMG_TYPE_NCSI_FW) &&
+                               memcmp(adapter->fw_ver, "3.102.148.0", 11) < 0)
+                       continue;
+               if ((pflashcomp[i].optype == IMG_TYPE_REDBOOT) &&
+                       (!be_flash_redboot(adapter, fw->data,
+                        pflashcomp[i].offset, pflashcomp[i].size,
+                        filehdr_size)))
+                       continue;
+               p = fw->data;
+               p += filehdr_size + pflashcomp[i].offset
+                       + (num_of_images * sizeof(struct image_hdr));
+       if (p + pflashcomp[i].size > fw->data + fw->size)
+               return -1;
+       total_bytes = pflashcomp[i].size;
+               while (total_bytes) {
+                       if (total_bytes > 32*1024)
+                               num_bytes = 32*1024;
+                       else
+                               num_bytes = total_bytes;
+                       total_bytes -= num_bytes;
+
+                       if (!total_bytes)
+                               flash_op = FLASHROM_OPER_FLASH;
+                       else
+                               flash_op = FLASHROM_OPER_SAVE;
+                       memcpy(req->params.data_buf, p, num_bytes);
+                       p += num_bytes;
+                       status = be_cmd_write_flashrom(adapter, flash_cmd,
+                               pflashcomp[i].optype, flash_op, num_bytes);
+                       if (status) {
+                               dev_err(&adapter->pdev->dev,
+                                       "cmd to write to flash rom failed.\n");
+                               return -1;
+                       }
+                       yield();
+               }
+       }
+       return 0;
+}
+
+static int get_ufigen_type(struct flash_file_hdr_g2 *fhdr)
+{
+       if (fhdr == NULL)
+               return 0;
+       if (fhdr->build[0] == '3')
+               return BE_GEN3;
+       else if (fhdr->build[0] == '2')
+               return BE_GEN2;
+       else
+               return 0;
+}
+
+int be_load_fw(struct be_adapter *adapter, u8 *func)
+{
+       char fw_file[ETHTOOL_FLASH_MAX_FILENAME];
+       const struct firmware *fw;
+       struct flash_file_hdr_g2 *fhdr;
+       struct flash_file_hdr_g3 *fhdr3;
+       struct image_hdr *img_hdr_ptr = NULL;
+       struct be_dma_mem flash_cmd;
+       int status, i = 0, num_imgs = 0;
+       const u8 *p;
+
+       strcpy(fw_file, func);
+
+       status = request_firmware(&fw, fw_file, &adapter->pdev->dev);
+       if (status)
+               goto fw_exit;
+
+       p = fw->data;
+       fhdr = (struct flash_file_hdr_g2 *) p;
+       dev_info(&adapter->pdev->dev, "Flashing firmware file %s\n", fw_file);
+
+       flash_cmd.size = sizeof(struct be_cmd_write_flashrom) + 32*1024;
+       flash_cmd.va = pci_alloc_consistent(adapter->pdev, flash_cmd.size,
+                                       &flash_cmd.dma);
+       if (!flash_cmd.va) {
+               status = -ENOMEM;
+               dev_err(&adapter->pdev->dev,
+                       "Memory allocation failure while flashing\n");
+               goto fw_exit;
+       }
+
+       if ((adapter->generation == BE_GEN3) &&
+                       (get_ufigen_type(fhdr) == BE_GEN3)) {
+               fhdr3 = (struct flash_file_hdr_g3 *) fw->data;
+               num_imgs = le32_to_cpu(fhdr3->num_imgs);
+               for (i = 0; i < num_imgs; i++) {
+                       img_hdr_ptr = (struct image_hdr *) (fw->data +
+                                       (sizeof(struct flash_file_hdr_g3) +
+                                        i * sizeof(struct image_hdr)));
+                       if (le32_to_cpu(img_hdr_ptr->imageid) == 1)
+                               status = be_flash_data(adapter, fw, &flash_cmd,
+                                                       num_imgs);
+               }
+       } else if ((adapter->generation == BE_GEN2) &&
+                       (get_ufigen_type(fhdr) == BE_GEN2)) {
+               status = be_flash_data(adapter, fw, &flash_cmd, 0);
+       } else {
+               dev_err(&adapter->pdev->dev,
+                       "UFI and Interface are not compatible for flashing\n");
+               status = -1;
+       }
+
+       pci_free_consistent(adapter->pdev, flash_cmd.size, flash_cmd.va,
+                               flash_cmd.dma);
+       if (status) {
+               dev_err(&adapter->pdev->dev, "Firmware load error\n");
+               goto fw_exit;
+       }
+
+       dev_info(&adapter->pdev->dev, "Firmware flashed successfully\n");
+
+fw_exit:
+       release_firmware(fw);
+       return status;
+}
+
 static struct net_device_ops be_netdev_ops = {
        .ndo_open               = be_open,
        .ndo_stop               = be_close,
@@ -1711,6 +2183,7 @@ static struct net_device_ops be_netdev_ops = {
        .ndo_vlan_rx_register   = be_vlan_register,
        .ndo_vlan_rx_add_vid    = be_vlan_add_vid,
        .ndo_vlan_rx_kill_vid   = be_vlan_rem_vid,
+       .ndo_set_vf_mac         = be_set_vf_mac
 };
 
 static void be_netdev_init(struct net_device *netdev)
@@ -1718,13 +2191,21 @@ static void be_netdev_init(struct net_device *netdev)
        struct be_adapter *adapter = netdev_priv(netdev);
 
        netdev->features |= NETIF_F_SG | NETIF_F_HW_VLAN_RX | NETIF_F_TSO |
-               NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_FILTER | NETIF_F_IP_CSUM |
-               NETIF_F_IPV6_CSUM | NETIF_F_GRO;
+               NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_FILTER | NETIF_F_HW_CSUM |
+               NETIF_F_GRO;
+
+       netdev->vlan_features |= NETIF_F_SG | NETIF_F_TSO | NETIF_F_HW_CSUM;
 
        netdev->flags |= IFF_MULTICAST;
 
        adapter->rx_csum = true;
 
+       /* Default settings for Rx and Tx flow control */
+       adapter->rx_fc = true;
+       adapter->tx_fc = true;
+
+       netif_set_gso_max_size(netdev, 65535);
+
        BE_SET_NETDEV_OPS(netdev, &be_netdev_ops);
 
        SET_ETHTOOL_OPS(netdev, &be_ethtool_ops);
@@ -1744,31 +2225,48 @@ static void be_unmap_pci_bars(struct be_adapter *adapter)
                iounmap(adapter->csr);
        if (adapter->db)
                iounmap(adapter->db);
-       if (adapter->pcicfg)
+       if (adapter->pcicfg && be_physfn(adapter))
                iounmap(adapter->pcicfg);
 }
 
 static int be_map_pci_bars(struct be_adapter *adapter)
 {
        u8 __iomem *addr;
+       int pcicfg_reg, db_reg;
+
+       if (be_physfn(adapter)) {
+               addr = ioremap_nocache(pci_resource_start(adapter->pdev, 2),
+                               pci_resource_len(adapter->pdev, 2));
+               if (addr == NULL)
+                       return -ENOMEM;
+               adapter->csr = addr;
+       }
 
-       addr = ioremap_nocache(pci_resource_start(adapter->pdev, 2),
-                       pci_resource_len(adapter->pdev, 2));
-       if (addr == NULL)
-               return -ENOMEM;
-       adapter->csr = addr;
-
-       addr = ioremap_nocache(pci_resource_start(adapter->pdev, 4),
-                       128 * 1024);
+       if (adapter->generation == BE_GEN2) {
+               pcicfg_reg = 1;
+               db_reg = 4;
+       } else {
+               pcicfg_reg = 0;
+               if (be_physfn(adapter))
+                       db_reg = 4;
+               else
+                       db_reg = 0;
+       }
+       addr = ioremap_nocache(pci_resource_start(adapter->pdev, db_reg),
+                               pci_resource_len(adapter->pdev, db_reg));
        if (addr == NULL)
                goto pci_map_err;
        adapter->db = addr;
 
-       addr = ioremap_nocache(pci_resource_start(adapter->pdev, 1),
-                       pci_resource_len(adapter->pdev, 1));
-       if (addr == NULL)
-               goto pci_map_err;
-       adapter->pcicfg = addr;
+       if (be_physfn(adapter)) {
+               addr = ioremap_nocache(
+                               pci_resource_start(adapter->pdev, pcicfg_reg),
+                               pci_resource_len(adapter->pdev, pcicfg_reg));
+               if (addr == NULL)
+                       goto pci_map_err;
+               adapter->pcicfg = addr;
+       } else
+               adapter->pcicfg = adapter->db + SRIOV_VF_PCICFG_OFFSET;
 
        return 0;
 pci_map_err:
@@ -1786,34 +2284,62 @@ static void be_ctrl_cleanup(struct be_adapter *adapter)
        if (mem->va)
                pci_free_consistent(adapter->pdev, mem->size,
                        mem->va, mem->dma);
+
+       mem = &adapter->mc_cmd_mem;
+       if (mem->va)
+               pci_free_consistent(adapter->pdev, mem->size,
+                       mem->va, mem->dma);
 }
 
 static int be_ctrl_init(struct be_adapter *adapter)
 {
        struct be_dma_mem *mbox_mem_alloc = &adapter->mbox_mem_alloced;
        struct be_dma_mem *mbox_mem_align = &adapter->mbox_mem;
+       struct be_dma_mem *mc_cmd_mem = &adapter->mc_cmd_mem;
        int status;
 
        status = be_map_pci_bars(adapter);
        if (status)
-               return status;
+               goto done;
 
        mbox_mem_alloc->size = sizeof(struct be_mcc_mailbox) + 16;
        mbox_mem_alloc->va = pci_alloc_consistent(adapter->pdev,
                                mbox_mem_alloc->size, &mbox_mem_alloc->dma);
        if (!mbox_mem_alloc->va) {
-               be_unmap_pci_bars(adapter);
-               return -1;
+               status = -ENOMEM;
+               goto unmap_pci_bars;
        }
+
        mbox_mem_align->size = sizeof(struct be_mcc_mailbox);
        mbox_mem_align->va = PTR_ALIGN(mbox_mem_alloc->va, 16);
        mbox_mem_align->dma = PTR_ALIGN(mbox_mem_alloc->dma, 16);
        memset(mbox_mem_align->va, 0, sizeof(struct be_mcc_mailbox));
+
+       mc_cmd_mem->size = sizeof(struct be_cmd_req_mcast_mac_config);
+       mc_cmd_mem->va = pci_alloc_consistent(adapter->pdev, mc_cmd_mem->size,
+                       &mc_cmd_mem->dma);
+       if (mc_cmd_mem->va == NULL) {
+               status = -ENOMEM;
+               goto free_mbox;
+       }
+       memset(mc_cmd_mem->va, 0, mc_cmd_mem->size);
+
        spin_lock_init(&adapter->mbox_lock);
        spin_lock_init(&adapter->mcc_lock);
        spin_lock_init(&adapter->mcc_cq_lock);
 
+       pci_save_state(adapter->pdev);
        return 0;
+
+free_mbox:
+       pci_free_consistent(adapter->pdev, mbox_mem_alloc->size,
+               mbox_mem_alloc->va, mbox_mem_alloc->dma);
+
+unmap_pci_bars:
+       be_unmap_pci_bars(adapter);
+
+done:
+       return status;
 }
 
 static void be_stats_cleanup(struct be_adapter *adapter)
@@ -1835,12 +2361,14 @@ static int be_stats_init(struct be_adapter *adapter)
        cmd->va = pci_alloc_consistent(adapter->pdev, cmd->size, &cmd->dma);
        if (cmd->va == NULL)
                return -1;
+       memset(cmd->va, 0, cmd->size);
        return 0;
 }
 
 static void __devexit be_remove(struct pci_dev *pdev)
 {
        struct be_adapter *adapter = pci_get_drvdata(pdev);
+
        if (!adapter)
                return;
 
@@ -1852,10 +2380,9 @@ static void __devexit be_remove(struct pci_dev *pdev)
 
        be_ctrl_cleanup(adapter);
 
-       if (adapter->msix_enabled) {
-               pci_disable_msix(adapter->pdev);
-               adapter->msix_enabled = false;
-       }
+       be_sriov_disable(adapter);
+
+       be_msix_disable(adapter);
 
        pci_set_drvdata(pdev, NULL);
        pci_release_regions(pdev);
@@ -1864,20 +2391,42 @@ static void __devexit be_remove(struct pci_dev *pdev)
        free_netdev(adapter->netdev);
 }
 
-static int be_hw_up(struct be_adapter *adapter)
+static int be_get_config(struct be_adapter *adapter)
 {
        int status;
+       u8 mac[ETH_ALEN];
 
-       status = be_cmd_POST(adapter);
+       status = be_cmd_get_fw_ver(adapter, adapter->fw_ver);
        if (status)
                return status;
 
-       status = be_cmd_get_fw_ver(adapter, adapter->fw_ver);
+       status = be_cmd_query_fw_cfg(adapter,
+                               &adapter->port_num, &adapter->cap);
        if (status)
                return status;
 
-       status = be_cmd_query_fw_cfg(adapter, &adapter->port_num);
-       return status;
+       memset(mac, 0, ETH_ALEN);
+
+       if (be_physfn(adapter)) {
+               status = be_cmd_mac_addr_query(adapter, mac,
+                       MAC_ADDRESS_TYPE_NETWORK, true /*permanent */, 0);
+
+               if (status)
+                       return status;
+
+               if (!is_valid_ether_addr(mac))
+                       return -EADDRNOTAVAIL;
+
+               memcpy(adapter->netdev->dev_addr, mac, ETH_ALEN);
+               memcpy(adapter->netdev->perm_addr, mac, ETH_ALEN);
+       }
+
+       if (adapter->cap & 0x400)
+               adapter->max_vlans = BE_NUM_VLANS_SUPPORTED/4;
+       else
+               adapter->max_vlans = BE_NUM_VLANS_SUPPORTED;
+
+       return 0;
 }
 
 static int __devinit be_probe(struct pci_dev *pdev,
@@ -1886,7 +2435,7 @@ static int __devinit be_probe(struct pci_dev *pdev,
        int status = 0;
        struct be_adapter *adapter;
        struct net_device *netdev;
-       u8 mac[ETH_ALEN];
+
 
        status = pci_enable_device(pdev);
        if (status)
@@ -1903,9 +2452,25 @@ static int __devinit be_probe(struct pci_dev *pdev,
                goto rel_reg;
        }
        adapter = netdev_priv(netdev);
+
+       switch (pdev->device) {
+       case BE_DEVICE_ID1:
+       case OC_DEVICE_ID1:
+               adapter->generation = BE_GEN2;
+               break;
+       case BE_DEVICE_ID2:
+       case OC_DEVICE_ID2:
+               adapter->generation = BE_GEN3;
+               break;
+       default:
+               adapter->generation = 0;
+       }
+
        adapter->pdev = pdev;
        pci_set_drvdata(pdev, adapter);
        adapter->netdev = netdev;
+       be_netdev_init(netdev);
+       SET_NETDEV_DEV(netdev, &pdev->dev);
 
        be_msix_enable(adapter);
 
@@ -1920,11 +2485,25 @@ static int __devinit be_probe(struct pci_dev *pdev,
                }
        }
 
+       be_sriov_enable(adapter);
+
        status = be_ctrl_init(adapter);
        if (status)
                goto free_netdev;
 
-       status = be_cmd_reset_function(adapter);
+       /* sync up with fw's ready state */
+       if (be_physfn(adapter)) {
+               status = be_cmd_POST(adapter);
+               if (status)
+                       goto ctrl_clean;
+
+               status = be_cmd_reset_function(adapter);
+               if (status)
+                       goto ctrl_clean;
+       }
+
+       /* tell fw we're ready to fire cmds */
+       status = be_cmd_fw_init(adapter);
        if (status)
                goto ctrl_clean;
 
@@ -1932,23 +2511,16 @@ static int __devinit be_probe(struct pci_dev *pdev,
        if (status)
                goto ctrl_clean;
 
-       status = be_hw_up(adapter);
-       if (status)
-               goto stats_clean;
-
-       status = be_cmd_mac_addr_query(adapter, mac, MAC_ADDRESS_TYPE_NETWORK,
-                       true /* permanent */, 0);
+       status = be_get_config(adapter);
        if (status)
                goto stats_clean;
-       memcpy(netdev->dev_addr, mac, ETH_ALEN);
 
        INIT_DELAYED_WORK(&adapter->work, be_worker);
-       be_netdev_init(netdev);
-       SET_NETDEV_DEV(netdev, &adapter->pdev->dev);
 
        status = be_setup(adapter);
        if (status)
                goto stats_clean;
+
        status = register_netdev(netdev);
        if (status != 0)
                goto unsetup;
@@ -1963,7 +2535,10 @@ stats_clean:
 ctrl_clean:
        be_ctrl_cleanup(adapter);
 free_netdev:
+       be_msix_disable(adapter);
+       be_sriov_disable(adapter);
        free_netdev(adapter->netdev);
+       pci_set_drvdata(pdev, NULL);
 rel_reg:
        pci_release_regions(pdev);
 disable_dev:
@@ -1978,12 +2553,16 @@ static int be_suspend(struct pci_dev *pdev, pm_message_t state)
        struct be_adapter *adapter = pci_get_drvdata(pdev);
        struct net_device *netdev =  adapter->netdev;
 
+       if (adapter->wol)
+               be_setup_wol(adapter, true);
+
        netif_device_detach(netdev);
        if (netif_running(netdev)) {
                rtnl_lock();
                be_close(netdev);
                rtnl_unlock();
        }
+       be_cmd_get_flow_control(adapter, &adapter->tx_fc, &adapter->rx_fc);
        be_clear(adapter);
 
        pci_save_state(pdev);
@@ -2007,6 +2586,11 @@ static int be_resume(struct pci_dev *pdev)
        pci_set_power_state(pdev, 0);
        pci_restore_state(pdev);
 
+       /* tell fw we're ready to fire cmds */
+       status = be_cmd_fw_init(adapter);
+       if (status)
+               return status;
+
        be_setup(adapter);
        if (netif_running(netdev)) {
                rtnl_lock();
@@ -2014,28 +2598,148 @@ static int be_resume(struct pci_dev *pdev)
                rtnl_unlock();
        }
        netif_device_attach(netdev);
+
+       if (adapter->wol)
+               be_setup_wol(adapter, false);
        return 0;
 }
 
+/*
+ * An FLR will stop BE from DMAing any data.
+ */
+static void be_shutdown(struct pci_dev *pdev)
+{
+       struct be_adapter *adapter = pci_get_drvdata(pdev);
+       struct net_device *netdev =  adapter->netdev;
+
+       netif_device_detach(netdev);
+
+       be_cmd_reset_function(adapter);
+
+       if (adapter->wol)
+               be_setup_wol(adapter, true);
+
+       pci_disable_device(pdev);
+
+       return;
+}
+
+static pci_ers_result_t be_eeh_err_detected(struct pci_dev *pdev,
+                               pci_channel_state_t state)
+{
+       struct be_adapter *adapter = pci_get_drvdata(pdev);
+       struct net_device *netdev =  adapter->netdev;
+
+       dev_err(&adapter->pdev->dev, "EEH error detected\n");
+
+       adapter->eeh_err = true;
+
+       netif_device_detach(netdev);
+
+       if (netif_running(netdev)) {
+               rtnl_lock();
+               be_close(netdev);
+               rtnl_unlock();
+       }
+       be_clear(adapter);
+
+       if (state == pci_channel_io_perm_failure)
+               return PCI_ERS_RESULT_DISCONNECT;
+
+       pci_disable_device(pdev);
+
+       return PCI_ERS_RESULT_NEED_RESET;
+}
+
+static pci_ers_result_t be_eeh_reset(struct pci_dev *pdev)
+{
+       struct be_adapter *adapter = pci_get_drvdata(pdev);
+       int status;
+
+       dev_info(&adapter->pdev->dev, "EEH reset\n");
+       adapter->eeh_err = false;
+
+       status = pci_enable_device(pdev);
+       if (status)
+               return PCI_ERS_RESULT_DISCONNECT;
+
+       pci_set_master(pdev);
+       pci_set_power_state(pdev, 0);
+       pci_restore_state(pdev);
+
+       /* Check if card is ok and fw is ready */
+       status = be_cmd_POST(adapter);
+       if (status)
+               return PCI_ERS_RESULT_DISCONNECT;
+
+       return PCI_ERS_RESULT_RECOVERED;
+}
+
+static void be_eeh_resume(struct pci_dev *pdev)
+{
+       int status = 0;
+       struct be_adapter *adapter = pci_get_drvdata(pdev);
+       struct net_device *netdev =  adapter->netdev;
+
+       dev_info(&adapter->pdev->dev, "EEH resume\n");
+
+       pci_save_state(pdev);
+
+       /* tell fw we're ready to fire cmds */
+       status = be_cmd_fw_init(adapter);
+       if (status)
+               goto err;
+
+       status = be_setup(adapter);
+       if (status)
+               goto err;
+
+       if (netif_running(netdev)) {
+               status = be_open(netdev);
+               if (status)
+                       goto err;
+       }
+       netif_device_attach(netdev);
+       return;
+err:
+       dev_err(&adapter->pdev->dev, "EEH resume failed\n");
+       return;
+}
+
+static struct pci_error_handlers be_eeh_handlers = {
+       .error_detected = be_eeh_err_detected,
+       .slot_reset = be_eeh_reset,
+       .resume = be_eeh_resume,
+};
+
 static struct pci_driver be_driver = {
        .name = DRV_NAME,
        .id_table = be_dev_ids,
        .probe = be_probe,
        .remove = be_remove,
        .suspend = be_suspend,
-       .resume = be_resume
+       .resume = be_resume,
+       .shutdown = be_shutdown,
+       .err_handler = &be_eeh_handlers
 };
 
 static int __init be_init_module(void)
 {
-       if (rx_frag_size != 8192 && rx_frag_size != 4096
-               && rx_frag_size != 2048) {
+       if (rx_frag_size != 8192 && rx_frag_size != 4096 &&
+           rx_frag_size != 2048) {
                printk(KERN_WARNING DRV_NAME
                        " : Module param rx_frag_size must be 2048/4096/8192."
                        " Using 2048\n");
                rx_frag_size = 2048;
        }
 
+       if (num_vfs > 32) {
+               printk(KERN_WARNING DRV_NAME
+                       " : Module param num_vfs must not be greater than 32."
+                       "Using 32\n");
+               num_vfs = 32;
+       }
+
        return pci_register_driver(&be_driver);
 }
 module_init(be_init_module);