MODULE_AUTHOR("ServerEngines Corporation");
 MODULE_LICENSE("GPL");
 
-static unsigned int rx_frag_size = 2048;
+static ushort rx_frag_size = 2048;
 static unsigned int num_vfs;
-module_param(rx_frag_size, uint, S_IRUGO);
+module_param(rx_frag_size, ushort, 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 void be_rx_stats_update(struct be_rx_obj *rxo,
-               u32 pktsize, u16 numfrags, u8 pkt_type)
+               struct be_rx_compl_info *rxcp)
 {
        struct be_rx_stats *stats = &rxo->stats;
 
        stats->rx_compl++;
-       stats->rx_frags += numfrags;
-       stats->rx_bytes += pktsize;
+       stats->rx_frags += rxcp->num_rcvd;
+       stats->rx_bytes += rxcp->pkt_size;
        stats->rx_pkts++;
-       if (pkt_type == BE_MULTICAST_PACKET)
+       if (rxcp->pkt_type == BE_MULTICAST_PACKET)
                stats->rx_mcast_pkts++;
+       if (rxcp->err)
+               stats->rxcp_err++;
 }
 
-static inline bool csum_passed(struct be_eth_rx_compl *rxcp)
+static inline bool csum_passed(struct be_rx_compl_info *rxcp)
 {
-       u8 l4_cksm, ipv6, ipcksm, tcpf, udpf;
-
-       l4_cksm = AMAP_GET_BITS(struct amap_eth_rx_compl, l4_cksm, rxcp);
-       ipcksm = AMAP_GET_BITS(struct amap_eth_rx_compl, ipcksm, rxcp);
-       ipv6 = AMAP_GET_BITS(struct amap_eth_rx_compl, ip_version, rxcp);
-       tcpf = AMAP_GET_BITS(struct amap_eth_rx_compl, tcpf, rxcp);
-       udpf = AMAP_GET_BITS(struct amap_eth_rx_compl, udpf, rxcp);
-
        /* L4 checksum is not reliable for non TCP/UDP packets.
         * Also ignore ipcksm for ipv6 pkts */
-       return (tcpf || udpf) && l4_cksm && (ipcksm || ipv6);
+       return (rxcp->tcpf || rxcp->udpf) && rxcp->l4_csum &&
+                               (rxcp->ip_csum || rxcp->ipv6);
 }
 
 static struct be_rx_page_info *
 /* Throwaway the data in the Rx completion */
 static void be_rx_compl_discard(struct be_adapter *adapter,
                struct be_rx_obj *rxo,
-               struct be_eth_rx_compl *rxcp)
+               struct be_rx_compl_info *rxcp)
 {
        struct be_queue_info *rxq = &rxo->q;
        struct be_rx_page_info *page_info;
-       u16 rxq_idx, i, num_rcvd;
-
-       rxq_idx = AMAP_GET_BITS(struct amap_eth_rx_compl, fragndx, rxcp);
-       num_rcvd = AMAP_GET_BITS(struct amap_eth_rx_compl, numfrags, rxcp);
+       u16 i, num_rcvd = rxcp->num_rcvd;
 
        for (i = 0; i < num_rcvd; i++) {
-               page_info = get_rx_page_info(adapter, rxo, rxq_idx);
+               page_info = get_rx_page_info(adapter, rxo, rxcp->rxq_idx);
                put_page(page_info->page);
                memset(page_info, 0, sizeof(*page_info));
-               index_inc(&rxq_idx, rxq->len);
+               index_inc(&rxcp->rxq_idx, rxq->len);
        }
 }
 
  * indicated by rxcp.
  */
 static void skb_fill_rx_data(struct be_adapter *adapter, struct be_rx_obj *rxo,
-                       struct sk_buff *skb, struct be_eth_rx_compl *rxcp,
-                       u16 num_rcvd)
+                       struct sk_buff *skb, struct be_rx_compl_info *rxcp)
 {
        struct be_queue_info *rxq = &rxo->q;
        struct be_rx_page_info *page_info;
-       u16 rxq_idx, i, j;
-       u32 pktsize, hdr_len, curr_frag_len, size;
+       u16 i, j;
+       u16 hdr_len, curr_frag_len, remaining;
        u8 *start;
-       u8 pkt_type;
-
-       rxq_idx = AMAP_GET_BITS(struct amap_eth_rx_compl, fragndx, rxcp);
-       pktsize = AMAP_GET_BITS(struct amap_eth_rx_compl, pktsize, rxcp);
-       pkt_type = AMAP_GET_BITS(struct amap_eth_rx_compl, cast_enc, rxcp);
-
-       page_info = get_rx_page_info(adapter, rxo, rxq_idx);
 
+       page_info = get_rx_page_info(adapter, rxo, rxcp->rxq_idx);
        start = page_address(page_info->page) + page_info->page_offset;
        prefetch(start);
 
        /* Copy data in the first descriptor of this completion */
-       curr_frag_len = min(pktsize, rx_frag_size);
+       curr_frag_len = min(rxcp->pkt_size, rx_frag_size);
 
        /* Copy the header portion into skb_data */
-       hdr_len = min((u32)BE_HDR_LEN, curr_frag_len);
+       hdr_len = min(BE_HDR_LEN, curr_frag_len);
        memcpy(skb->data, start, hdr_len);
        skb->len = curr_frag_len;
        if (curr_frag_len <= BE_HDR_LEN) { /* tiny packet */
        }
        page_info->page = NULL;
 
-       if (pktsize <= rx_frag_size) {
-               BUG_ON(num_rcvd != 1);
-               goto done;
+       if (rxcp->pkt_size <= rx_frag_size) {
+               BUG_ON(rxcp->num_rcvd != 1);
+               return;
        }
 
        /* More frags present for this completion */
-       size = pktsize;
-       for (i = 1, j = 0; i < num_rcvd; i++) {
-               size -= curr_frag_len;
-               index_inc(&rxq_idx, rxq->len);
-               page_info = get_rx_page_info(adapter, rxo, rxq_idx);
-
-               curr_frag_len = min(size, rx_frag_size);
+       index_inc(&rxcp->rxq_idx, rxq->len);
+       remaining = rxcp->pkt_size - curr_frag_len;
+       for (i = 1, j = 0; i < rxcp->num_rcvd; i++) {
+               page_info = get_rx_page_info(adapter, rxo, rxcp->rxq_idx);
+               curr_frag_len = min(remaining, rx_frag_size);
 
                /* Coalesce all frags from the same physical page in one slot */
                if (page_info->page_offset == 0) {
                skb->len += curr_frag_len;
                skb->data_len += curr_frag_len;
 
+               remaining -= curr_frag_len;
+               index_inc(&rxcp->rxq_idx, rxq->len);
                page_info->page = NULL;
        }
        BUG_ON(j > MAX_SKB_FRAGS);
-
-done:
-       be_rx_stats_update(rxo, pktsize, num_rcvd, pkt_type);
 }
 
 /* Process the RX completion indicated by rxcp when GRO is disabled */
 static void be_rx_compl_process(struct be_adapter *adapter,
                        struct be_rx_obj *rxo,
-                       struct be_eth_rx_compl *rxcp)
+                       struct be_rx_compl_info *rxcp)
 {
        struct sk_buff *skb;
-       u32 vlanf, vid;
-       u16 num_rcvd;
-       u8 vtm;
-
-       num_rcvd = AMAP_GET_BITS(struct amap_eth_rx_compl, numfrags, rxcp);
 
        skb = netdev_alloc_skb_ip_align(adapter->netdev, BE_HDR_LEN);
        if (unlikely(!skb)) {
                return;
        }
 
-       skb_fill_rx_data(adapter, rxo, skb, rxcp, num_rcvd);
+       skb_fill_rx_data(adapter, rxo, skb, rxcp);
 
        if (likely(adapter->rx_csum && csum_passed(rxcp)))
                skb->ip_summed = CHECKSUM_UNNECESSARY;
        skb->truesize = skb->len + sizeof(struct sk_buff);
        skb->protocol = eth_type_trans(skb, adapter->netdev);
 
-       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->function_mode & 0x400) && !vtm)
-               vlanf = 0;
-
-       if ((adapter->pvid == vlanf) && !adapter->vlan_tag[vlanf])
-               vlanf = 0;
-
-       if (unlikely(vlanf)) {
+       if (unlikely(rxcp->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);
-               if (!lancer_chip(adapter))
-                       vid = swab16(vid);
-               vlan_hwaccel_receive_skb(skb, adapter->vlan_grp, vid);
+               vlan_hwaccel_receive_skb(skb, adapter->vlan_grp, rxcp->vid);
        } else {
                netif_receive_skb(skb);
        }
 /* Process the RX completion indicated by rxcp when GRO is enabled */
 static void be_rx_compl_process_gro(struct be_adapter *adapter,
                struct be_rx_obj *rxo,
-               struct be_eth_rx_compl *rxcp)
+               struct be_rx_compl_info *rxcp)
 {
        struct be_rx_page_info *page_info;
        struct sk_buff *skb = NULL;
        struct be_queue_info *rxq = &rxo->q;
        struct be_eq_obj *eq_obj =  &rxo->rx_eq;
-       u32 num_rcvd, pkt_size, remaining, vlanf, curr_frag_len;
-       u16 i, rxq_idx = 0, vid, j;
-       u8 vtm;
-       u8 pkt_type;
-
-       num_rcvd = AMAP_GET_BITS(struct amap_eth_rx_compl, numfrags, rxcp);
-       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);
-       pkt_type = AMAP_GET_BITS(struct amap_eth_rx_compl, cast_enc, rxcp);
-
-       /* vlanf could be wrongly set in some cards.
-        * ignore if vtm is not set */
-       if ((adapter->function_mode & 0x400) && !vtm)
-               vlanf = 0;
-
-       if ((adapter->pvid == vlanf) && !adapter->vlan_tag[vlanf])
-               vlanf = 0;
+       u16 remaining, curr_frag_len;
+       u16 i, j;
 
        skb = napi_get_frags(&eq_obj->napi);
        if (!skb) {
                return;
        }
 
-       remaining = pkt_size;
-       for (i = 0, j = -1; i < num_rcvd; i++) {
-               page_info = get_rx_page_info(adapter, rxo, rxq_idx);
+       remaining = rxcp->pkt_size;
+       for (i = 0, j = -1; i < rxcp->num_rcvd; i++) {
+               page_info = get_rx_page_info(adapter, rxo, rxcp->rxq_idx);
 
                curr_frag_len = min(remaining, rx_frag_size);
 
                skb_shinfo(skb)->frags[j].size += curr_frag_len;
 
                remaining -= curr_frag_len;
-               index_inc(&rxq_idx, rxq->len);
+               index_inc(&rxcp->rxq_idx, rxq->len);
                memset(page_info, 0, sizeof(*page_info));
        }
        BUG_ON(j > MAX_SKB_FRAGS);
 
        skb_shinfo(skb)->nr_frags = j + 1;
-       skb->len = pkt_size;
-       skb->data_len = pkt_size;
-       skb->truesize += pkt_size;
+       skb->len = rxcp->pkt_size;
+       skb->data_len = rxcp->pkt_size;
+       skb->truesize += rxcp->pkt_size;
        skb->ip_summed = CHECKSUM_UNNECESSARY;
 
-       if (likely(!vlanf)) {
+       if (likely(!rxcp->vlanf))
                napi_gro_frags(&eq_obj->napi);
-       } else {
-               vid = AMAP_GET_BITS(struct amap_eth_rx_compl, vlan_tag, rxcp);
-               if (!lancer_chip(adapter))
-                       vid = swab16(vid);
+       else
+               vlan_gro_frags(&eq_obj->napi, adapter->vlan_grp, rxcp->vid);
+}
+
+static void be_parse_rx_compl_v1(struct be_adapter *adapter,
+                               struct be_eth_rx_compl *compl,
+                               struct be_rx_compl_info *rxcp)
+{
+       rxcp->pkt_size =
+               AMAP_GET_BITS(struct amap_eth_rx_compl_v1, pktsize, compl);
+       rxcp->vlanf = AMAP_GET_BITS(struct amap_eth_rx_compl_v1, vtp, compl);
+       rxcp->err = AMAP_GET_BITS(struct amap_eth_rx_compl_v1, err, compl);
+       rxcp->tcpf = AMAP_GET_BITS(struct amap_eth_rx_compl_v1, tcpf, compl);
+       rxcp->ip_csum =
+               AMAP_GET_BITS(struct amap_eth_rx_compl_v1, ipcksm, compl);
+       rxcp->l4_csum =
+               AMAP_GET_BITS(struct amap_eth_rx_compl_v1, l4_cksm, compl);
+       rxcp->ipv6 =
+               AMAP_GET_BITS(struct amap_eth_rx_compl_v1, ip_version, compl);
+       rxcp->rxq_idx =
+               AMAP_GET_BITS(struct amap_eth_rx_compl_v1, fragndx, compl);
+       rxcp->num_rcvd =
+               AMAP_GET_BITS(struct amap_eth_rx_compl_v1, numfrags, compl);
+       rxcp->pkt_type =
+               AMAP_GET_BITS(struct amap_eth_rx_compl_v1, cast_enc, compl);
+       rxcp->vtm = AMAP_GET_BITS(struct amap_eth_rx_compl_v1, vtm, compl);
+       rxcp->vid = AMAP_GET_BITS(struct amap_eth_rx_compl_v1, vlan_tag, compl);
+}
+
+static void be_parse_rx_compl_v0(struct be_adapter *adapter,
+                               struct be_eth_rx_compl *compl,
+                               struct be_rx_compl_info *rxcp)
+{
+       rxcp->pkt_size =
+               AMAP_GET_BITS(struct amap_eth_rx_compl_v0, pktsize, compl);
+       rxcp->vlanf = AMAP_GET_BITS(struct amap_eth_rx_compl_v0, vtp, compl);
+       rxcp->err = AMAP_GET_BITS(struct amap_eth_rx_compl_v0, err, compl);
+       rxcp->tcpf = AMAP_GET_BITS(struct amap_eth_rx_compl_v0, tcpf, compl);
+       rxcp->ip_csum =
+               AMAP_GET_BITS(struct amap_eth_rx_compl_v0, ipcksm, compl);
+       rxcp->l4_csum =
+               AMAP_GET_BITS(struct amap_eth_rx_compl_v0, l4_cksm, compl);
+       rxcp->ipv6 =
+               AMAP_GET_BITS(struct amap_eth_rx_compl_v0, ip_version, compl);
+       rxcp->rxq_idx =
+               AMAP_GET_BITS(struct amap_eth_rx_compl_v0, fragndx, compl);
+       rxcp->num_rcvd =
+               AMAP_GET_BITS(struct amap_eth_rx_compl_v0, numfrags, compl);
+       rxcp->pkt_type =
+               AMAP_GET_BITS(struct amap_eth_rx_compl_v0, cast_enc, compl);
+       rxcp->vtm = AMAP_GET_BITS(struct amap_eth_rx_compl_v0, vtm, compl);
+       rxcp->vid = AMAP_GET_BITS(struct amap_eth_rx_compl_v0, vlan_tag, compl);
+}
+
+static struct be_rx_compl_info *be_rx_compl_get(struct be_rx_obj *rxo)
+{
+       struct be_eth_rx_compl *compl = queue_tail_node(&rxo->cq);
+       struct be_rx_compl_info *rxcp = &rxo->rxcp;
+       struct be_adapter *adapter = rxo->adapter;
 
-               if (!adapter->vlan_grp || adapter->vlans_added == 0)
-                       return;
+       /* For checking the valid bit it is Ok to use either definition as the
+        * valid bit is at the same position in both v0 and v1 Rx compl */
+       if (compl->dw[offsetof(struct amap_eth_rx_compl_v1, valid) / 32] == 0)
+               return NULL;
 
-               vlan_gro_frags(&eq_obj->napi, adapter->vlan_grp, vid);
-       }
+       rmb();
+       be_dws_le_to_cpu(compl, sizeof(*compl));
 
-       be_rx_stats_update(rxo, pkt_size, num_rcvd, pkt_type);
-}
+       if (adapter->be3_native)
+               be_parse_rx_compl_v1(adapter, compl, rxcp);
+       else
+               be_parse_rx_compl_v0(adapter, compl, rxcp);
 
-static struct be_eth_rx_compl *be_rx_compl_get(struct be_rx_obj *rxo)
-{
-       struct be_eth_rx_compl *rxcp = queue_tail_node(&rxo->cq);
+       /* vlanf could be wrongly set in some cards. ignore if vtm is not set */
+       if ((adapter->function_mode & 0x400) && !rxcp->vtm)
+               rxcp->vlanf = 0;
 
-       if (rxcp->dw[offsetof(struct amap_eth_rx_compl, valid) / 32] == 0)
-               return NULL;
+       if (!lancer_chip(adapter))
+               rxcp->vid = swab16(rxcp->vid);
 
-       rmb();
-       be_dws_le_to_cpu(rxcp, sizeof(*rxcp));
+       if ((adapter->pvid == rxcp->vid) && !adapter->vlan_tag[rxcp->vid])
+               rxcp->vlanf = 0;
+
+       /* As the compl has been parsed, reset it; we wont touch it again */
+       compl->dw[offsetof(struct amap_eth_rx_compl_v1, valid) / 32] = 0;
 
        queue_tail_inc(&rxo->cq);
        return rxcp;
 }
 
-/* To reset the valid bit, we need to reset the whole word as
- * when walking the queue the valid entries are little-endian
- * and invalid entries are host endian
- */
-static inline void be_rx_compl_reset(struct be_eth_rx_compl *rxcp)
-{
-       rxcp->dw[offsetof(struct amap_eth_rx_compl, valid) / 32] = 0;
-}
-
 static inline struct page *be_alloc_pages(u32 size, gfp_t gfp)
 {
        u32 order = get_order(size);
        struct be_rx_page_info *page_info;
        struct be_queue_info *rxq = &rxo->q;
        struct be_queue_info *rx_cq = &rxo->cq;
-       struct be_eth_rx_compl *rxcp;
+       struct be_rx_compl_info *rxcp;
        u16 tail;
 
        /* First cleanup pending rx completions */
        while ((rxcp = be_rx_compl_get(rxo)) != NULL) {
                be_rx_compl_discard(adapter, rxo, rxcp);
-               be_rx_compl_reset(rxcp);
                be_cq_notify(adapter, rx_cq->id, false, 1);
        }
 
        return IRQ_HANDLED;
 }
 
-static inline bool do_gro(struct be_rx_obj *rxo,
-                       struct be_eth_rx_compl *rxcp, u8 err)
+static inline bool do_gro(struct be_rx_compl_info *rxcp)
 {
-       int tcp_frame = AMAP_GET_BITS(struct amap_eth_rx_compl, tcpf, rxcp);
-
-       if (err)
-               rxo->stats.rxcp_err++;
-
-       return (tcp_frame && !err) ? true : false;
+       return (rxcp->tcpf && !rxcp->err) ? true : false;
 }
 
 static int be_poll_rx(struct napi_struct *napi, int budget)
        struct be_rx_obj *rxo = container_of(rx_eq, struct be_rx_obj, rx_eq);
        struct be_adapter *adapter = rxo->adapter;
        struct be_queue_info *rx_cq = &rxo->cq;
-       struct be_eth_rx_compl *rxcp;
+       struct be_rx_compl_info *rxcp;
        u32 work_done;
-       u16 num_rcvd;
-       u8 err;
 
        rxo->stats.rx_polls++;
        for (work_done = 0; work_done < budget; work_done++) {
                if (!rxcp)
                        break;
 
-               err = AMAP_GET_BITS(struct amap_eth_rx_compl, err, rxcp);
-               num_rcvd = AMAP_GET_BITS(struct amap_eth_rx_compl, numfrags,
-                                                               rxcp);
                /* Ignore flush completions */
-               if (num_rcvd) {
-                       if (do_gro(rxo, rxcp, err))
+               if (rxcp->num_rcvd) {
+                       if (do_gro(rxcp))
                                be_rx_compl_process_gro(adapter, rxo, rxcp);
                        else
                                be_rx_compl_process(adapter, rxo, rxcp);
                }
-
-               be_rx_compl_reset(rxcp);
+               be_rx_stats_update(rxo, rxcp);
        }
 
        /* Refill the queue */
        if (status)
                return status;
 
+       be_cmd_check_native_mode(adapter);
        return 0;
 }