162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * QLogic qlcnic NIC Driver
462306a36Sopenharmony_ci * Copyright (c) 2009-2013 QLogic Corporation
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include <linux/netdevice.h>
862306a36Sopenharmony_ci#include <linux/if_vlan.h>
962306a36Sopenharmony_ci#include <net/ip.h>
1062306a36Sopenharmony_ci#include <linux/ipv6.h>
1162306a36Sopenharmony_ci#include <net/checksum.h>
1262306a36Sopenharmony_ci#include <linux/printk.h>
1362306a36Sopenharmony_ci#include <linux/jiffies.h>
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#include "qlcnic.h"
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#define QLCNIC_TX_ETHER_PKT		0x01
1862306a36Sopenharmony_ci#define QLCNIC_TX_TCP_PKT		0x02
1962306a36Sopenharmony_ci#define QLCNIC_TX_UDP_PKT		0x03
2062306a36Sopenharmony_ci#define QLCNIC_TX_IP_PKT		0x04
2162306a36Sopenharmony_ci#define QLCNIC_TX_TCP_LSO		0x05
2262306a36Sopenharmony_ci#define QLCNIC_TX_TCP_LSO6		0x06
2362306a36Sopenharmony_ci#define QLCNIC_TX_ENCAP_PKT		0x07
2462306a36Sopenharmony_ci#define QLCNIC_TX_ENCAP_LSO		0x08
2562306a36Sopenharmony_ci#define QLCNIC_TX_TCPV6_PKT		0x0b
2662306a36Sopenharmony_ci#define QLCNIC_TX_UDPV6_PKT		0x0c
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci#define QLCNIC_FLAGS_VLAN_TAGGED	0x10
2962306a36Sopenharmony_ci#define QLCNIC_FLAGS_VLAN_OOB		0x40
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci#define qlcnic_set_tx_vlan_tci(cmd_desc, v)	\
3262306a36Sopenharmony_ci	(cmd_desc)->vlan_TCI = cpu_to_le16(v);
3362306a36Sopenharmony_ci#define qlcnic_set_cmd_desc_port(cmd_desc, var)	\
3462306a36Sopenharmony_ci	((cmd_desc)->port_ctxid |= ((var) & 0x0F))
3562306a36Sopenharmony_ci#define qlcnic_set_cmd_desc_ctxid(cmd_desc, var)	\
3662306a36Sopenharmony_ci	((cmd_desc)->port_ctxid |= ((var) << 4 & 0xF0))
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci#define qlcnic_set_tx_port(_desc, _port) \
3962306a36Sopenharmony_ci	((_desc)->port_ctxid = ((_port) & 0xf) | (((_port) << 4) & 0xf0))
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci#define qlcnic_set_tx_flags_opcode(_desc, _flags, _opcode) \
4262306a36Sopenharmony_ci	((_desc)->flags_opcode |= \
4362306a36Sopenharmony_ci	cpu_to_le16(((_flags) & 0x7f) | (((_opcode) & 0x3f) << 7)))
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci#define qlcnic_set_tx_frags_len(_desc, _frags, _len) \
4662306a36Sopenharmony_ci	((_desc)->nfrags__length = \
4762306a36Sopenharmony_ci	cpu_to_le32(((_frags) & 0xff) | (((_len) & 0xffffff) << 8)))
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci/* owner bits of status_desc */
5062306a36Sopenharmony_ci#define STATUS_OWNER_HOST	(0x1ULL << 56)
5162306a36Sopenharmony_ci#define STATUS_OWNER_PHANTOM	(0x2ULL << 56)
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci/* Status descriptor:
5462306a36Sopenharmony_ci   0-3 port, 4-7 status, 8-11 type, 12-27 total_length
5562306a36Sopenharmony_ci   28-43 reference_handle, 44-47 protocol, 48-52 pkt_offset
5662306a36Sopenharmony_ci   53-55 desc_cnt, 56-57 owner, 58-63 opcode
5762306a36Sopenharmony_ci */
5862306a36Sopenharmony_ci#define qlcnic_get_sts_port(sts_data)	\
5962306a36Sopenharmony_ci	((sts_data) & 0x0F)
6062306a36Sopenharmony_ci#define qlcnic_get_sts_status(sts_data)	\
6162306a36Sopenharmony_ci	(((sts_data) >> 4) & 0x0F)
6262306a36Sopenharmony_ci#define qlcnic_get_sts_type(sts_data)	\
6362306a36Sopenharmony_ci	(((sts_data) >> 8) & 0x0F)
6462306a36Sopenharmony_ci#define qlcnic_get_sts_totallength(sts_data)	\
6562306a36Sopenharmony_ci	(((sts_data) >> 12) & 0xFFFF)
6662306a36Sopenharmony_ci#define qlcnic_get_sts_refhandle(sts_data)	\
6762306a36Sopenharmony_ci	(((sts_data) >> 28) & 0xFFFF)
6862306a36Sopenharmony_ci#define qlcnic_get_sts_prot(sts_data)	\
6962306a36Sopenharmony_ci	(((sts_data) >> 44) & 0x0F)
7062306a36Sopenharmony_ci#define qlcnic_get_sts_pkt_offset(sts_data)	\
7162306a36Sopenharmony_ci	(((sts_data) >> 48) & 0x1F)
7262306a36Sopenharmony_ci#define qlcnic_get_sts_desc_cnt(sts_data)	\
7362306a36Sopenharmony_ci	(((sts_data) >> 53) & 0x7)
7462306a36Sopenharmony_ci#define qlcnic_get_sts_opcode(sts_data)	\
7562306a36Sopenharmony_ci	(((sts_data) >> 58) & 0x03F)
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci#define qlcnic_get_lro_sts_refhandle(sts_data) 	\
7862306a36Sopenharmony_ci	((sts_data) & 0x07FFF)
7962306a36Sopenharmony_ci#define qlcnic_get_lro_sts_length(sts_data)	\
8062306a36Sopenharmony_ci	(((sts_data) >> 16) & 0x0FFFF)
8162306a36Sopenharmony_ci#define qlcnic_get_lro_sts_l2_hdr_offset(sts_data)	\
8262306a36Sopenharmony_ci	(((sts_data) >> 32) & 0x0FF)
8362306a36Sopenharmony_ci#define qlcnic_get_lro_sts_l4_hdr_offset(sts_data)	\
8462306a36Sopenharmony_ci	(((sts_data) >> 40) & 0x0FF)
8562306a36Sopenharmony_ci#define qlcnic_get_lro_sts_timestamp(sts_data)	\
8662306a36Sopenharmony_ci	(((sts_data) >> 48) & 0x1)
8762306a36Sopenharmony_ci#define qlcnic_get_lro_sts_type(sts_data)	\
8862306a36Sopenharmony_ci	(((sts_data) >> 49) & 0x7)
8962306a36Sopenharmony_ci#define qlcnic_get_lro_sts_push_flag(sts_data)		\
9062306a36Sopenharmony_ci	(((sts_data) >> 52) & 0x1)
9162306a36Sopenharmony_ci#define qlcnic_get_lro_sts_seq_number(sts_data)		\
9262306a36Sopenharmony_ci	((sts_data) & 0x0FFFFFFFF)
9362306a36Sopenharmony_ci#define qlcnic_get_lro_sts_mss(sts_data1)		\
9462306a36Sopenharmony_ci	((sts_data1 >> 32) & 0x0FFFF)
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci#define qlcnic_83xx_get_lro_sts_mss(sts) ((sts) & 0xffff)
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci/* opcode field in status_desc */
9962306a36Sopenharmony_ci#define QLCNIC_SYN_OFFLOAD	0x03
10062306a36Sopenharmony_ci#define QLCNIC_RXPKT_DESC  	0x04
10162306a36Sopenharmony_ci#define QLCNIC_OLD_RXPKT_DESC	0x3f
10262306a36Sopenharmony_ci#define QLCNIC_RESPONSE_DESC	0x05
10362306a36Sopenharmony_ci#define QLCNIC_LRO_DESC  	0x12
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci#define QLCNIC_TCP_HDR_SIZE		20
10662306a36Sopenharmony_ci#define QLCNIC_TCP_TS_OPTION_SIZE	12
10762306a36Sopenharmony_ci#define QLCNIC_FETCH_RING_ID(handle)	((handle) >> 63)
10862306a36Sopenharmony_ci#define QLCNIC_DESC_OWNER_FW		cpu_to_le64(STATUS_OWNER_PHANTOM)
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci#define QLCNIC_TCP_TS_HDR_SIZE (QLCNIC_TCP_HDR_SIZE + QLCNIC_TCP_TS_OPTION_SIZE)
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci/* for status field in status_desc */
11362306a36Sopenharmony_ci#define STATUS_CKSUM_LOOP	0
11462306a36Sopenharmony_ci#define STATUS_CKSUM_OK		2
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci#define qlcnic_83xx_pktln(sts)		((sts >> 32) & 0x3FFF)
11762306a36Sopenharmony_ci#define qlcnic_83xx_hndl(sts)		((sts >> 48) & 0x7FFF)
11862306a36Sopenharmony_ci#define qlcnic_83xx_csum_status(sts)	((sts >> 39) & 7)
11962306a36Sopenharmony_ci#define qlcnic_83xx_opcode(sts)	((sts >> 42) & 0xF)
12062306a36Sopenharmony_ci#define qlcnic_83xx_vlan_tag(sts)	(((sts) >> 48) & 0xFFFF)
12162306a36Sopenharmony_ci#define qlcnic_83xx_lro_pktln(sts)	(((sts) >> 32) & 0x3FFF)
12262306a36Sopenharmony_ci#define qlcnic_83xx_l2_hdr_off(sts)	(((sts) >> 16) & 0xFF)
12362306a36Sopenharmony_ci#define qlcnic_83xx_l4_hdr_off(sts)	(((sts) >> 24) & 0xFF)
12462306a36Sopenharmony_ci#define qlcnic_83xx_pkt_cnt(sts)	(((sts) >> 16) & 0x7)
12562306a36Sopenharmony_ci#define qlcnic_83xx_is_tstamp(sts)	(((sts) >> 40) & 1)
12662306a36Sopenharmony_ci#define qlcnic_83xx_is_psh_bit(sts)	(((sts) >> 41) & 1)
12762306a36Sopenharmony_ci#define qlcnic_83xx_is_ip_align(sts)	(((sts) >> 46) & 1)
12862306a36Sopenharmony_ci#define qlcnic_83xx_has_vlan_tag(sts)	(((sts) >> 47) & 1)
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_cistatic int qlcnic_process_rcv_ring(struct qlcnic_host_sds_ring *sds_ring,
13162306a36Sopenharmony_ci				   int max);
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_cistatic struct sk_buff *qlcnic_process_rxbuf(struct qlcnic_adapter *,
13462306a36Sopenharmony_ci					    struct qlcnic_host_rds_ring *,
13562306a36Sopenharmony_ci					    u16, u16);
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_cistatic inline u8 qlcnic_mac_hash(u64 mac, u16 vlan)
13862306a36Sopenharmony_ci{
13962306a36Sopenharmony_ci	return (u8)((mac & 0xff) ^ ((mac >> 40) & 0xff) ^ (vlan & 0xff));
14062306a36Sopenharmony_ci}
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_cistatic inline u32 qlcnic_get_ref_handle(struct qlcnic_adapter *adapter,
14362306a36Sopenharmony_ci					u16 handle, u8 ring_id)
14462306a36Sopenharmony_ci{
14562306a36Sopenharmony_ci	if (qlcnic_83xx_check(adapter))
14662306a36Sopenharmony_ci		return handle | (ring_id << 15);
14762306a36Sopenharmony_ci	else
14862306a36Sopenharmony_ci		return handle;
14962306a36Sopenharmony_ci}
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_cistatic inline int qlcnic_82xx_is_lb_pkt(u64 sts_data)
15262306a36Sopenharmony_ci{
15362306a36Sopenharmony_ci	return (qlcnic_get_sts_status(sts_data) == STATUS_CKSUM_LOOP) ? 1 : 0;
15462306a36Sopenharmony_ci}
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_cistatic void qlcnic_delete_rx_list_mac(struct qlcnic_adapter *adapter,
15762306a36Sopenharmony_ci				      struct qlcnic_filter *fil,
15862306a36Sopenharmony_ci				      void *addr, u16 vlan_id)
15962306a36Sopenharmony_ci{
16062306a36Sopenharmony_ci	int ret;
16162306a36Sopenharmony_ci	u8 op;
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	op = vlan_id ? QLCNIC_MAC_VLAN_ADD : QLCNIC_MAC_ADD;
16462306a36Sopenharmony_ci	ret = qlcnic_sre_macaddr_change(adapter, addr, vlan_id, op);
16562306a36Sopenharmony_ci	if (ret)
16662306a36Sopenharmony_ci		return;
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	op = vlan_id ? QLCNIC_MAC_VLAN_DEL : QLCNIC_MAC_DEL;
16962306a36Sopenharmony_ci	ret = qlcnic_sre_macaddr_change(adapter, addr, vlan_id, op);
17062306a36Sopenharmony_ci	if (!ret) {
17162306a36Sopenharmony_ci		hlist_del(&fil->fnode);
17262306a36Sopenharmony_ci		adapter->rx_fhash.fnum--;
17362306a36Sopenharmony_ci	}
17462306a36Sopenharmony_ci}
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_cistatic struct qlcnic_filter *qlcnic_find_mac_filter(struct hlist_head *head,
17762306a36Sopenharmony_ci						    void *addr, u16 vlan_id)
17862306a36Sopenharmony_ci{
17962306a36Sopenharmony_ci	struct qlcnic_filter *tmp_fil = NULL;
18062306a36Sopenharmony_ci	struct hlist_node *n;
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	hlist_for_each_entry_safe(tmp_fil, n, head, fnode) {
18362306a36Sopenharmony_ci		if (ether_addr_equal(tmp_fil->faddr, addr) &&
18462306a36Sopenharmony_ci		    tmp_fil->vlan_id == vlan_id)
18562306a36Sopenharmony_ci			return tmp_fil;
18662306a36Sopenharmony_ci	}
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	return NULL;
18962306a36Sopenharmony_ci}
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_cistatic void qlcnic_add_lb_filter(struct qlcnic_adapter *adapter,
19262306a36Sopenharmony_ci				 struct sk_buff *skb, int loopback_pkt, u16 vlan_id)
19362306a36Sopenharmony_ci{
19462306a36Sopenharmony_ci	struct ethhdr *phdr = (struct ethhdr *)(skb->data);
19562306a36Sopenharmony_ci	struct qlcnic_filter *fil, *tmp_fil;
19662306a36Sopenharmony_ci	struct hlist_head *head;
19762306a36Sopenharmony_ci	unsigned long time;
19862306a36Sopenharmony_ci	u64 src_addr = 0;
19962306a36Sopenharmony_ci	u8 hindex, op;
20062306a36Sopenharmony_ci	int ret;
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	if (!qlcnic_sriov_pf_check(adapter) || (vlan_id == 0xffff))
20362306a36Sopenharmony_ci		vlan_id = 0;
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	memcpy(&src_addr, phdr->h_source, ETH_ALEN);
20662306a36Sopenharmony_ci	hindex = qlcnic_mac_hash(src_addr, vlan_id) &
20762306a36Sopenharmony_ci		 (adapter->fhash.fbucket_size - 1);
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	if (loopback_pkt) {
21062306a36Sopenharmony_ci		if (adapter->rx_fhash.fnum >= adapter->rx_fhash.fmax)
21162306a36Sopenharmony_ci			return;
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci		head = &(adapter->rx_fhash.fhead[hindex]);
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci		tmp_fil = qlcnic_find_mac_filter(head, &src_addr, vlan_id);
21662306a36Sopenharmony_ci		if (tmp_fil) {
21762306a36Sopenharmony_ci			time = tmp_fil->ftime;
21862306a36Sopenharmony_ci			if (time_after(jiffies, QLCNIC_READD_AGE * HZ + time))
21962306a36Sopenharmony_ci				tmp_fil->ftime = jiffies;
22062306a36Sopenharmony_ci			return;
22162306a36Sopenharmony_ci		}
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci		fil = kzalloc(sizeof(struct qlcnic_filter), GFP_ATOMIC);
22462306a36Sopenharmony_ci		if (!fil)
22562306a36Sopenharmony_ci			return;
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci		fil->ftime = jiffies;
22862306a36Sopenharmony_ci		memcpy(fil->faddr, &src_addr, ETH_ALEN);
22962306a36Sopenharmony_ci		fil->vlan_id = vlan_id;
23062306a36Sopenharmony_ci		spin_lock(&adapter->rx_mac_learn_lock);
23162306a36Sopenharmony_ci		hlist_add_head(&(fil->fnode), head);
23262306a36Sopenharmony_ci		adapter->rx_fhash.fnum++;
23362306a36Sopenharmony_ci		spin_unlock(&adapter->rx_mac_learn_lock);
23462306a36Sopenharmony_ci	} else {
23562306a36Sopenharmony_ci		head = &adapter->fhash.fhead[hindex];
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci		spin_lock(&adapter->mac_learn_lock);
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci		tmp_fil = qlcnic_find_mac_filter(head, &src_addr, vlan_id);
24062306a36Sopenharmony_ci		if (tmp_fil) {
24162306a36Sopenharmony_ci			op = vlan_id ? QLCNIC_MAC_VLAN_DEL : QLCNIC_MAC_DEL;
24262306a36Sopenharmony_ci			ret = qlcnic_sre_macaddr_change(adapter,
24362306a36Sopenharmony_ci							(u8 *)&src_addr,
24462306a36Sopenharmony_ci							vlan_id, op);
24562306a36Sopenharmony_ci			if (!ret) {
24662306a36Sopenharmony_ci				hlist_del(&tmp_fil->fnode);
24762306a36Sopenharmony_ci				adapter->fhash.fnum--;
24862306a36Sopenharmony_ci			}
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci			spin_unlock(&adapter->mac_learn_lock);
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci			return;
25362306a36Sopenharmony_ci		}
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci		spin_unlock(&adapter->mac_learn_lock);
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci		head = &adapter->rx_fhash.fhead[hindex];
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci		spin_lock(&adapter->rx_mac_learn_lock);
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci		tmp_fil = qlcnic_find_mac_filter(head, &src_addr, vlan_id);
26262306a36Sopenharmony_ci		if (tmp_fil)
26362306a36Sopenharmony_ci			qlcnic_delete_rx_list_mac(adapter, tmp_fil, &src_addr,
26462306a36Sopenharmony_ci						  vlan_id);
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci		spin_unlock(&adapter->rx_mac_learn_lock);
26762306a36Sopenharmony_ci	}
26862306a36Sopenharmony_ci}
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_civoid qlcnic_82xx_change_filter(struct qlcnic_adapter *adapter, u64 *uaddr,
27162306a36Sopenharmony_ci			       u16 vlan_id, struct qlcnic_host_tx_ring *tx_ring)
27262306a36Sopenharmony_ci{
27362306a36Sopenharmony_ci	struct cmd_desc_type0 *hwdesc;
27462306a36Sopenharmony_ci	struct qlcnic_nic_req *req;
27562306a36Sopenharmony_ci	struct qlcnic_mac_req *mac_req;
27662306a36Sopenharmony_ci	struct qlcnic_vlan_req *vlan_req;
27762306a36Sopenharmony_ci	u32 producer;
27862306a36Sopenharmony_ci	u64 word;
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci	producer = tx_ring->producer;
28162306a36Sopenharmony_ci	hwdesc = &tx_ring->desc_head[tx_ring->producer];
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci	req = (struct qlcnic_nic_req *)hwdesc;
28462306a36Sopenharmony_ci	memset(req, 0, sizeof(struct qlcnic_nic_req));
28562306a36Sopenharmony_ci	req->qhdr = cpu_to_le64(QLCNIC_REQUEST << 23);
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci	word = QLCNIC_MAC_EVENT | ((u64)(adapter->portnum) << 16);
28862306a36Sopenharmony_ci	req->req_hdr = cpu_to_le64(word);
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci	mac_req = (struct qlcnic_mac_req *)&(req->words[0]);
29162306a36Sopenharmony_ci	mac_req->op = vlan_id ? QLCNIC_MAC_VLAN_ADD : QLCNIC_MAC_ADD;
29262306a36Sopenharmony_ci	memcpy(mac_req->mac_addr, uaddr, ETH_ALEN);
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci	vlan_req = (struct qlcnic_vlan_req *)&req->words[1];
29562306a36Sopenharmony_ci	vlan_req->vlan_id = cpu_to_le16(vlan_id);
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci	tx_ring->producer = get_next_index(producer, tx_ring->num_desc);
29862306a36Sopenharmony_ci	smp_mb();
29962306a36Sopenharmony_ci}
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_cistatic void qlcnic_send_filter(struct qlcnic_adapter *adapter,
30262306a36Sopenharmony_ci			       struct cmd_desc_type0 *first_desc,
30362306a36Sopenharmony_ci			       struct sk_buff *skb,
30462306a36Sopenharmony_ci			       struct qlcnic_host_tx_ring *tx_ring)
30562306a36Sopenharmony_ci{
30662306a36Sopenharmony_ci	struct vlan_ethhdr *vh = (struct vlan_ethhdr *)(skb->data);
30762306a36Sopenharmony_ci	struct ethhdr *phdr = (struct ethhdr *)(skb->data);
30862306a36Sopenharmony_ci	u16 protocol = ntohs(skb->protocol);
30962306a36Sopenharmony_ci	struct qlcnic_filter *fil, *tmp_fil;
31062306a36Sopenharmony_ci	struct hlist_head *head;
31162306a36Sopenharmony_ci	struct hlist_node *n;
31262306a36Sopenharmony_ci	u64 src_addr = 0;
31362306a36Sopenharmony_ci	u16 vlan_id = 0;
31462306a36Sopenharmony_ci	u8 hindex, hval;
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	if (ether_addr_equal(phdr->h_source, adapter->mac_addr))
31762306a36Sopenharmony_ci		return;
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci	if (adapter->flags & QLCNIC_VLAN_FILTERING) {
32062306a36Sopenharmony_ci		if (protocol == ETH_P_8021Q) {
32162306a36Sopenharmony_ci			vh = skb_vlan_eth_hdr(skb);
32262306a36Sopenharmony_ci			vlan_id = ntohs(vh->h_vlan_TCI);
32362306a36Sopenharmony_ci		} else if (skb_vlan_tag_present(skb)) {
32462306a36Sopenharmony_ci			vlan_id = skb_vlan_tag_get(skb);
32562306a36Sopenharmony_ci		}
32662306a36Sopenharmony_ci	}
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci	memcpy(&src_addr, phdr->h_source, ETH_ALEN);
32962306a36Sopenharmony_ci	hval = qlcnic_mac_hash(src_addr, vlan_id);
33062306a36Sopenharmony_ci	hindex = hval & (adapter->fhash.fbucket_size - 1);
33162306a36Sopenharmony_ci	head = &(adapter->fhash.fhead[hindex]);
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci	hlist_for_each_entry_safe(tmp_fil, n, head, fnode) {
33462306a36Sopenharmony_ci		if (ether_addr_equal(tmp_fil->faddr, (u8 *)&src_addr) &&
33562306a36Sopenharmony_ci		    tmp_fil->vlan_id == vlan_id) {
33662306a36Sopenharmony_ci			if (time_is_before_jiffies(QLCNIC_READD_AGE * HZ + tmp_fil->ftime))
33762306a36Sopenharmony_ci				qlcnic_change_filter(adapter, &src_addr,
33862306a36Sopenharmony_ci						     vlan_id, tx_ring);
33962306a36Sopenharmony_ci			tmp_fil->ftime = jiffies;
34062306a36Sopenharmony_ci			return;
34162306a36Sopenharmony_ci		}
34262306a36Sopenharmony_ci	}
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci	if (unlikely(adapter->fhash.fnum >= adapter->fhash.fmax)) {
34562306a36Sopenharmony_ci		adapter->stats.mac_filter_limit_overrun++;
34662306a36Sopenharmony_ci		return;
34762306a36Sopenharmony_ci	}
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci	fil = kzalloc(sizeof(struct qlcnic_filter), GFP_ATOMIC);
35062306a36Sopenharmony_ci	if (!fil)
35162306a36Sopenharmony_ci		return;
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci	qlcnic_change_filter(adapter, &src_addr, vlan_id, tx_ring);
35462306a36Sopenharmony_ci	fil->ftime = jiffies;
35562306a36Sopenharmony_ci	fil->vlan_id = vlan_id;
35662306a36Sopenharmony_ci	memcpy(fil->faddr, &src_addr, ETH_ALEN);
35762306a36Sopenharmony_ci	spin_lock(&adapter->mac_learn_lock);
35862306a36Sopenharmony_ci	hlist_add_head(&(fil->fnode), head);
35962306a36Sopenharmony_ci	adapter->fhash.fnum++;
36062306a36Sopenharmony_ci	spin_unlock(&adapter->mac_learn_lock);
36162306a36Sopenharmony_ci}
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci#define QLCNIC_ENCAP_VXLAN_PKT		BIT_0
36462306a36Sopenharmony_ci#define QLCNIC_ENCAP_OUTER_L3_IP6	BIT_1
36562306a36Sopenharmony_ci#define QLCNIC_ENCAP_INNER_L3_IP6	BIT_2
36662306a36Sopenharmony_ci#define QLCNIC_ENCAP_INNER_L4_UDP	BIT_3
36762306a36Sopenharmony_ci#define QLCNIC_ENCAP_DO_L3_CSUM		BIT_4
36862306a36Sopenharmony_ci#define QLCNIC_ENCAP_DO_L4_CSUM		BIT_5
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_cistatic int qlcnic_tx_encap_pkt(struct qlcnic_adapter *adapter,
37162306a36Sopenharmony_ci			       struct cmd_desc_type0 *first_desc,
37262306a36Sopenharmony_ci			       struct sk_buff *skb,
37362306a36Sopenharmony_ci			       struct qlcnic_host_tx_ring *tx_ring)
37462306a36Sopenharmony_ci{
37562306a36Sopenharmony_ci	u8 opcode = 0, inner_hdr_len = 0, outer_hdr_len = 0, total_hdr_len = 0;
37662306a36Sopenharmony_ci	int copied, copy_len, descr_size;
37762306a36Sopenharmony_ci	u32 producer = tx_ring->producer;
37862306a36Sopenharmony_ci	struct cmd_desc_type0 *hwdesc;
37962306a36Sopenharmony_ci	u16 flags = 0, encap_descr = 0;
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci	opcode = QLCNIC_TX_ETHER_PKT;
38262306a36Sopenharmony_ci	encap_descr = QLCNIC_ENCAP_VXLAN_PKT;
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ci	if (skb_is_gso(skb)) {
38562306a36Sopenharmony_ci		inner_hdr_len = skb_inner_transport_header(skb) +
38662306a36Sopenharmony_ci				inner_tcp_hdrlen(skb) -
38762306a36Sopenharmony_ci				skb_inner_mac_header(skb);
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci		/* VXLAN header size = 8 */
39062306a36Sopenharmony_ci		outer_hdr_len = skb_transport_offset(skb) + 8 +
39162306a36Sopenharmony_ci				sizeof(struct udphdr);
39262306a36Sopenharmony_ci		first_desc->outer_hdr_length = outer_hdr_len;
39362306a36Sopenharmony_ci		total_hdr_len = inner_hdr_len + outer_hdr_len;
39462306a36Sopenharmony_ci		encap_descr |= QLCNIC_ENCAP_DO_L3_CSUM |
39562306a36Sopenharmony_ci			       QLCNIC_ENCAP_DO_L4_CSUM;
39662306a36Sopenharmony_ci		first_desc->mss = cpu_to_le16(skb_shinfo(skb)->gso_size);
39762306a36Sopenharmony_ci		first_desc->hdr_length = inner_hdr_len;
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci		/* Copy inner and outer headers in Tx descriptor(s)
40062306a36Sopenharmony_ci		 * If total_hdr_len > cmd_desc_type0, use multiple
40162306a36Sopenharmony_ci		 * descriptors
40262306a36Sopenharmony_ci		 */
40362306a36Sopenharmony_ci		copied = 0;
40462306a36Sopenharmony_ci		descr_size = (int)sizeof(struct cmd_desc_type0);
40562306a36Sopenharmony_ci		while (copied < total_hdr_len) {
40662306a36Sopenharmony_ci			copy_len = min(descr_size, (total_hdr_len - copied));
40762306a36Sopenharmony_ci			hwdesc = &tx_ring->desc_head[producer];
40862306a36Sopenharmony_ci			tx_ring->cmd_buf_arr[producer].skb = NULL;
40962306a36Sopenharmony_ci			skb_copy_from_linear_data_offset(skb, copied,
41062306a36Sopenharmony_ci							 (char *)hwdesc,
41162306a36Sopenharmony_ci							 copy_len);
41262306a36Sopenharmony_ci			copied += copy_len;
41362306a36Sopenharmony_ci			producer = get_next_index(producer, tx_ring->num_desc);
41462306a36Sopenharmony_ci		}
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_ci		tx_ring->producer = producer;
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci		/* Make sure updated tx_ring->producer is visible
41962306a36Sopenharmony_ci		 * for qlcnic_tx_avail()
42062306a36Sopenharmony_ci		 */
42162306a36Sopenharmony_ci		smp_mb();
42262306a36Sopenharmony_ci		adapter->stats.encap_lso_frames++;
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_ci		opcode = QLCNIC_TX_ENCAP_LSO;
42562306a36Sopenharmony_ci	} else if (skb->ip_summed == CHECKSUM_PARTIAL) {
42662306a36Sopenharmony_ci		if (inner_ip_hdr(skb)->version == 6) {
42762306a36Sopenharmony_ci			if (inner_ipv6_hdr(skb)->nexthdr == IPPROTO_UDP)
42862306a36Sopenharmony_ci				encap_descr |= QLCNIC_ENCAP_INNER_L4_UDP;
42962306a36Sopenharmony_ci		} else {
43062306a36Sopenharmony_ci			if (inner_ip_hdr(skb)->protocol == IPPROTO_UDP)
43162306a36Sopenharmony_ci				encap_descr |= QLCNIC_ENCAP_INNER_L4_UDP;
43262306a36Sopenharmony_ci		}
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci		adapter->stats.encap_tx_csummed++;
43562306a36Sopenharmony_ci		opcode = QLCNIC_TX_ENCAP_PKT;
43662306a36Sopenharmony_ci	}
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_ci	/* Prepare first 16 bits of byte offset 16 of Tx descriptor */
43962306a36Sopenharmony_ci	if (ip_hdr(skb)->version == 6)
44062306a36Sopenharmony_ci		encap_descr |= QLCNIC_ENCAP_OUTER_L3_IP6;
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ci	/* outer IP header's size in 32bit words size*/
44362306a36Sopenharmony_ci	encap_descr |= (skb_network_header_len(skb) >> 2) << 6;
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci	/* outer IP header offset */
44662306a36Sopenharmony_ci	encap_descr |= skb_network_offset(skb) << 10;
44762306a36Sopenharmony_ci	first_desc->encap_descr = cpu_to_le16(encap_descr);
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci	first_desc->tcp_hdr_offset = skb_inner_transport_header(skb) -
45062306a36Sopenharmony_ci				     skb->data;
45162306a36Sopenharmony_ci	first_desc->ip_hdr_offset = skb_inner_network_offset(skb);
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci	qlcnic_set_tx_flags_opcode(first_desc, flags, opcode);
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ci	return 0;
45662306a36Sopenharmony_ci}
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_cistatic int qlcnic_tx_pkt(struct qlcnic_adapter *adapter,
45962306a36Sopenharmony_ci			 struct cmd_desc_type0 *first_desc, struct sk_buff *skb,
46062306a36Sopenharmony_ci			 struct qlcnic_host_tx_ring *tx_ring)
46162306a36Sopenharmony_ci{
46262306a36Sopenharmony_ci	u8 l4proto, opcode = 0, hdr_len = 0, tag_vlan = 0;
46362306a36Sopenharmony_ci	u16 flags = 0, vlan_tci = 0;
46462306a36Sopenharmony_ci	int copied, offset, copy_len, size;
46562306a36Sopenharmony_ci	struct cmd_desc_type0 *hwdesc;
46662306a36Sopenharmony_ci	struct vlan_ethhdr *vh;
46762306a36Sopenharmony_ci	u16 protocol = ntohs(skb->protocol);
46862306a36Sopenharmony_ci	u32 producer = tx_ring->producer;
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci	if (protocol == ETH_P_8021Q) {
47162306a36Sopenharmony_ci		vh = skb_vlan_eth_hdr(skb);
47262306a36Sopenharmony_ci		flags = QLCNIC_FLAGS_VLAN_TAGGED;
47362306a36Sopenharmony_ci		vlan_tci = ntohs(vh->h_vlan_TCI);
47462306a36Sopenharmony_ci		protocol = ntohs(vh->h_vlan_encapsulated_proto);
47562306a36Sopenharmony_ci		tag_vlan = 1;
47662306a36Sopenharmony_ci	} else if (skb_vlan_tag_present(skb)) {
47762306a36Sopenharmony_ci		flags = QLCNIC_FLAGS_VLAN_OOB;
47862306a36Sopenharmony_ci		vlan_tci = skb_vlan_tag_get(skb);
47962306a36Sopenharmony_ci		tag_vlan = 1;
48062306a36Sopenharmony_ci	}
48162306a36Sopenharmony_ci	if (unlikely(adapter->tx_pvid)) {
48262306a36Sopenharmony_ci		if (tag_vlan && !(adapter->flags & QLCNIC_TAGGING_ENABLED))
48362306a36Sopenharmony_ci			return -EIO;
48462306a36Sopenharmony_ci		if (tag_vlan && (adapter->flags & QLCNIC_TAGGING_ENABLED))
48562306a36Sopenharmony_ci			goto set_flags;
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci		flags = QLCNIC_FLAGS_VLAN_OOB;
48862306a36Sopenharmony_ci		vlan_tci = adapter->tx_pvid;
48962306a36Sopenharmony_ci	}
49062306a36Sopenharmony_ciset_flags:
49162306a36Sopenharmony_ci	qlcnic_set_tx_vlan_tci(first_desc, vlan_tci);
49262306a36Sopenharmony_ci	qlcnic_set_tx_flags_opcode(first_desc, flags, opcode);
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ci	if (*(skb->data) & BIT_0) {
49562306a36Sopenharmony_ci		flags |= BIT_0;
49662306a36Sopenharmony_ci		memcpy(&first_desc->eth_addr, skb->data, ETH_ALEN);
49762306a36Sopenharmony_ci	}
49862306a36Sopenharmony_ci	opcode = QLCNIC_TX_ETHER_PKT;
49962306a36Sopenharmony_ci	if (skb_is_gso(skb)) {
50062306a36Sopenharmony_ci		hdr_len = skb_tcp_all_headers(skb);
50162306a36Sopenharmony_ci		first_desc->mss = cpu_to_le16(skb_shinfo(skb)->gso_size);
50262306a36Sopenharmony_ci		first_desc->hdr_length = hdr_len;
50362306a36Sopenharmony_ci		opcode = (protocol == ETH_P_IPV6) ? QLCNIC_TX_TCP_LSO6 :
50462306a36Sopenharmony_ci						    QLCNIC_TX_TCP_LSO;
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci		/* For LSO, we need to copy the MAC/IP/TCP headers into
50762306a36Sopenharmony_ci		* the descriptor ring */
50862306a36Sopenharmony_ci		copied = 0;
50962306a36Sopenharmony_ci		offset = 2;
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_ci		if (flags & QLCNIC_FLAGS_VLAN_OOB) {
51262306a36Sopenharmony_ci			first_desc->hdr_length += VLAN_HLEN;
51362306a36Sopenharmony_ci			first_desc->tcp_hdr_offset = VLAN_HLEN;
51462306a36Sopenharmony_ci			first_desc->ip_hdr_offset = VLAN_HLEN;
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_ci			/* Only in case of TSO on vlan device */
51762306a36Sopenharmony_ci			flags |= QLCNIC_FLAGS_VLAN_TAGGED;
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci			/* Create a TSO vlan header template for firmware */
52062306a36Sopenharmony_ci			hwdesc = &tx_ring->desc_head[producer];
52162306a36Sopenharmony_ci			tx_ring->cmd_buf_arr[producer].skb = NULL;
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci			copy_len = min((int)sizeof(struct cmd_desc_type0) -
52462306a36Sopenharmony_ci				       offset, hdr_len + VLAN_HLEN);
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_ci			vh = (struct vlan_ethhdr *)((char *) hwdesc + 2);
52762306a36Sopenharmony_ci			skb_copy_from_linear_data(skb, vh, 12);
52862306a36Sopenharmony_ci			vh->h_vlan_proto = htons(ETH_P_8021Q);
52962306a36Sopenharmony_ci			vh->h_vlan_TCI = htons(vlan_tci);
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci			skb_copy_from_linear_data_offset(skb, 12,
53262306a36Sopenharmony_ci							 (char *)vh + 16,
53362306a36Sopenharmony_ci							 copy_len - 16);
53462306a36Sopenharmony_ci			copied = copy_len - VLAN_HLEN;
53562306a36Sopenharmony_ci			offset = 0;
53662306a36Sopenharmony_ci			producer = get_next_index(producer, tx_ring->num_desc);
53762306a36Sopenharmony_ci		}
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_ci		while (copied < hdr_len) {
54062306a36Sopenharmony_ci			size = (int)sizeof(struct cmd_desc_type0) - offset;
54162306a36Sopenharmony_ci			copy_len = min(size, (hdr_len - copied));
54262306a36Sopenharmony_ci			hwdesc = &tx_ring->desc_head[producer];
54362306a36Sopenharmony_ci			tx_ring->cmd_buf_arr[producer].skb = NULL;
54462306a36Sopenharmony_ci			skb_copy_from_linear_data_offset(skb, copied,
54562306a36Sopenharmony_ci							 (char *)hwdesc +
54662306a36Sopenharmony_ci							 offset, copy_len);
54762306a36Sopenharmony_ci			copied += copy_len;
54862306a36Sopenharmony_ci			offset = 0;
54962306a36Sopenharmony_ci			producer = get_next_index(producer, tx_ring->num_desc);
55062306a36Sopenharmony_ci		}
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_ci		tx_ring->producer = producer;
55362306a36Sopenharmony_ci		smp_mb();
55462306a36Sopenharmony_ci		adapter->stats.lso_frames++;
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci	} else if (skb->ip_summed == CHECKSUM_PARTIAL) {
55762306a36Sopenharmony_ci		if (protocol == ETH_P_IP) {
55862306a36Sopenharmony_ci			l4proto = ip_hdr(skb)->protocol;
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_ci			if (l4proto == IPPROTO_TCP)
56162306a36Sopenharmony_ci				opcode = QLCNIC_TX_TCP_PKT;
56262306a36Sopenharmony_ci			else if (l4proto == IPPROTO_UDP)
56362306a36Sopenharmony_ci				opcode = QLCNIC_TX_UDP_PKT;
56462306a36Sopenharmony_ci		} else if (protocol == ETH_P_IPV6) {
56562306a36Sopenharmony_ci			l4proto = ipv6_hdr(skb)->nexthdr;
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_ci			if (l4proto == IPPROTO_TCP)
56862306a36Sopenharmony_ci				opcode = QLCNIC_TX_TCPV6_PKT;
56962306a36Sopenharmony_ci			else if (l4proto == IPPROTO_UDP)
57062306a36Sopenharmony_ci				opcode = QLCNIC_TX_UDPV6_PKT;
57162306a36Sopenharmony_ci		}
57262306a36Sopenharmony_ci	}
57362306a36Sopenharmony_ci	first_desc->tcp_hdr_offset += skb_transport_offset(skb);
57462306a36Sopenharmony_ci	first_desc->ip_hdr_offset += skb_network_offset(skb);
57562306a36Sopenharmony_ci	qlcnic_set_tx_flags_opcode(first_desc, flags, opcode);
57662306a36Sopenharmony_ci
57762306a36Sopenharmony_ci	return 0;
57862306a36Sopenharmony_ci}
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_cistatic int qlcnic_map_tx_skb(struct pci_dev *pdev, struct sk_buff *skb,
58162306a36Sopenharmony_ci			     struct qlcnic_cmd_buffer *pbuf)
58262306a36Sopenharmony_ci{
58362306a36Sopenharmony_ci	struct qlcnic_skb_frag *nf;
58462306a36Sopenharmony_ci	skb_frag_t *frag;
58562306a36Sopenharmony_ci	int i, nr_frags;
58662306a36Sopenharmony_ci	dma_addr_t map;
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_ci	nr_frags = skb_shinfo(skb)->nr_frags;
58962306a36Sopenharmony_ci	nf = &pbuf->frag_array[0];
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_ci	map = dma_map_single(&pdev->dev, skb->data, skb_headlen(skb),
59262306a36Sopenharmony_ci			     DMA_TO_DEVICE);
59362306a36Sopenharmony_ci	if (dma_mapping_error(&pdev->dev, map))
59462306a36Sopenharmony_ci		goto out_err;
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_ci	nf->dma = map;
59762306a36Sopenharmony_ci	nf->length = skb_headlen(skb);
59862306a36Sopenharmony_ci
59962306a36Sopenharmony_ci	for (i = 0; i < nr_frags; i++) {
60062306a36Sopenharmony_ci		frag = &skb_shinfo(skb)->frags[i];
60162306a36Sopenharmony_ci		nf = &pbuf->frag_array[i+1];
60262306a36Sopenharmony_ci		map = skb_frag_dma_map(&pdev->dev, frag, 0, skb_frag_size(frag),
60362306a36Sopenharmony_ci				       DMA_TO_DEVICE);
60462306a36Sopenharmony_ci		if (dma_mapping_error(&pdev->dev, map))
60562306a36Sopenharmony_ci			goto unwind;
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_ci		nf->dma = map;
60862306a36Sopenharmony_ci		nf->length = skb_frag_size(frag);
60962306a36Sopenharmony_ci	}
61062306a36Sopenharmony_ci
61162306a36Sopenharmony_ci	return 0;
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_ciunwind:
61462306a36Sopenharmony_ci	while (--i >= 0) {
61562306a36Sopenharmony_ci		nf = &pbuf->frag_array[i+1];
61662306a36Sopenharmony_ci		dma_unmap_page(&pdev->dev, nf->dma, nf->length, DMA_TO_DEVICE);
61762306a36Sopenharmony_ci	}
61862306a36Sopenharmony_ci
61962306a36Sopenharmony_ci	nf = &pbuf->frag_array[0];
62062306a36Sopenharmony_ci	dma_unmap_single(&pdev->dev, nf->dma, skb_headlen(skb), DMA_TO_DEVICE);
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_ciout_err:
62362306a36Sopenharmony_ci	return -ENOMEM;
62462306a36Sopenharmony_ci}
62562306a36Sopenharmony_ci
62662306a36Sopenharmony_cistatic void qlcnic_unmap_buffers(struct pci_dev *pdev, struct sk_buff *skb,
62762306a36Sopenharmony_ci				 struct qlcnic_cmd_buffer *pbuf)
62862306a36Sopenharmony_ci{
62962306a36Sopenharmony_ci	struct qlcnic_skb_frag *nf = &pbuf->frag_array[0];
63062306a36Sopenharmony_ci	int i, nr_frags = skb_shinfo(skb)->nr_frags;
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_ci	for (i = 0; i < nr_frags; i++) {
63362306a36Sopenharmony_ci		nf = &pbuf->frag_array[i+1];
63462306a36Sopenharmony_ci		dma_unmap_page(&pdev->dev, nf->dma, nf->length, DMA_TO_DEVICE);
63562306a36Sopenharmony_ci	}
63662306a36Sopenharmony_ci
63762306a36Sopenharmony_ci	nf = &pbuf->frag_array[0];
63862306a36Sopenharmony_ci	dma_unmap_single(&pdev->dev, nf->dma, skb_headlen(skb), DMA_TO_DEVICE);
63962306a36Sopenharmony_ci	pbuf->skb = NULL;
64062306a36Sopenharmony_ci}
64162306a36Sopenharmony_ci
64262306a36Sopenharmony_cistatic inline void qlcnic_clear_cmddesc(u64 *desc)
64362306a36Sopenharmony_ci{
64462306a36Sopenharmony_ci	desc[0] = 0ULL;
64562306a36Sopenharmony_ci	desc[2] = 0ULL;
64662306a36Sopenharmony_ci	desc[7] = 0ULL;
64762306a36Sopenharmony_ci}
64862306a36Sopenharmony_ci
64962306a36Sopenharmony_cinetdev_tx_t qlcnic_xmit_frame(struct sk_buff *skb, struct net_device *netdev)
65062306a36Sopenharmony_ci{
65162306a36Sopenharmony_ci	struct qlcnic_adapter *adapter = netdev_priv(netdev);
65262306a36Sopenharmony_ci	struct qlcnic_host_tx_ring *tx_ring;
65362306a36Sopenharmony_ci	struct qlcnic_cmd_buffer *pbuf;
65462306a36Sopenharmony_ci	struct qlcnic_skb_frag *buffrag;
65562306a36Sopenharmony_ci	struct cmd_desc_type0 *hwdesc, *first_desc;
65662306a36Sopenharmony_ci	struct pci_dev *pdev;
65762306a36Sopenharmony_ci	struct ethhdr *phdr;
65862306a36Sopenharmony_ci	int i, k, frag_count, delta = 0;
65962306a36Sopenharmony_ci	u32 producer, num_txd;
66062306a36Sopenharmony_ci	u16 protocol;
66162306a36Sopenharmony_ci	bool l4_is_udp = false;
66262306a36Sopenharmony_ci
66362306a36Sopenharmony_ci	if (!test_bit(__QLCNIC_DEV_UP, &adapter->state)) {
66462306a36Sopenharmony_ci		netif_tx_stop_all_queues(netdev);
66562306a36Sopenharmony_ci		return NETDEV_TX_BUSY;
66662306a36Sopenharmony_ci	}
66762306a36Sopenharmony_ci
66862306a36Sopenharmony_ci	if (adapter->flags & QLCNIC_MACSPOOF) {
66962306a36Sopenharmony_ci		phdr = (struct ethhdr *)skb->data;
67062306a36Sopenharmony_ci		if (!ether_addr_equal(phdr->h_source, adapter->mac_addr))
67162306a36Sopenharmony_ci			goto drop_packet;
67262306a36Sopenharmony_ci	}
67362306a36Sopenharmony_ci
67462306a36Sopenharmony_ci	tx_ring = &adapter->tx_ring[skb_get_queue_mapping(skb)];
67562306a36Sopenharmony_ci	num_txd = tx_ring->num_desc;
67662306a36Sopenharmony_ci
67762306a36Sopenharmony_ci	frag_count = skb_shinfo(skb)->nr_frags + 1;
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_ci	/* 14 frags supported for normal packet and
68062306a36Sopenharmony_ci	 * 32 frags supported for TSO packet
68162306a36Sopenharmony_ci	 */
68262306a36Sopenharmony_ci	if (!skb_is_gso(skb) && frag_count > QLCNIC_MAX_FRAGS_PER_TX) {
68362306a36Sopenharmony_ci		for (i = 0; i < (frag_count - QLCNIC_MAX_FRAGS_PER_TX); i++)
68462306a36Sopenharmony_ci			delta += skb_frag_size(&skb_shinfo(skb)->frags[i]);
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_ci		if (!__pskb_pull_tail(skb, delta))
68762306a36Sopenharmony_ci			goto drop_packet;
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ci		frag_count = 1 + skb_shinfo(skb)->nr_frags;
69062306a36Sopenharmony_ci	}
69162306a36Sopenharmony_ci
69262306a36Sopenharmony_ci	if (unlikely(qlcnic_tx_avail(tx_ring) <= TX_STOP_THRESH)) {
69362306a36Sopenharmony_ci		netif_tx_stop_queue(tx_ring->txq);
69462306a36Sopenharmony_ci		if (qlcnic_tx_avail(tx_ring) > TX_STOP_THRESH) {
69562306a36Sopenharmony_ci			netif_tx_start_queue(tx_ring->txq);
69662306a36Sopenharmony_ci		} else {
69762306a36Sopenharmony_ci			tx_ring->tx_stats.xmit_off++;
69862306a36Sopenharmony_ci			return NETDEV_TX_BUSY;
69962306a36Sopenharmony_ci		}
70062306a36Sopenharmony_ci	}
70162306a36Sopenharmony_ci
70262306a36Sopenharmony_ci	producer = tx_ring->producer;
70362306a36Sopenharmony_ci	pbuf = &tx_ring->cmd_buf_arr[producer];
70462306a36Sopenharmony_ci	pdev = adapter->pdev;
70562306a36Sopenharmony_ci	first_desc = &tx_ring->desc_head[producer];
70662306a36Sopenharmony_ci	hwdesc = &tx_ring->desc_head[producer];
70762306a36Sopenharmony_ci	qlcnic_clear_cmddesc((u64 *)hwdesc);
70862306a36Sopenharmony_ci
70962306a36Sopenharmony_ci	if (qlcnic_map_tx_skb(pdev, skb, pbuf)) {
71062306a36Sopenharmony_ci		adapter->stats.tx_dma_map_error++;
71162306a36Sopenharmony_ci		goto drop_packet;
71262306a36Sopenharmony_ci	}
71362306a36Sopenharmony_ci
71462306a36Sopenharmony_ci	pbuf->skb = skb;
71562306a36Sopenharmony_ci	pbuf->frag_count = frag_count;
71662306a36Sopenharmony_ci
71762306a36Sopenharmony_ci	qlcnic_set_tx_frags_len(first_desc, frag_count, skb->len);
71862306a36Sopenharmony_ci	qlcnic_set_tx_port(first_desc, adapter->portnum);
71962306a36Sopenharmony_ci
72062306a36Sopenharmony_ci	for (i = 0; i < frag_count; i++) {
72162306a36Sopenharmony_ci		k = i % 4;
72262306a36Sopenharmony_ci
72362306a36Sopenharmony_ci		if ((k == 0) && (i > 0)) {
72462306a36Sopenharmony_ci			/* move to next desc.*/
72562306a36Sopenharmony_ci			producer = get_next_index(producer, num_txd);
72662306a36Sopenharmony_ci			hwdesc = &tx_ring->desc_head[producer];
72762306a36Sopenharmony_ci			qlcnic_clear_cmddesc((u64 *)hwdesc);
72862306a36Sopenharmony_ci			tx_ring->cmd_buf_arr[producer].skb = NULL;
72962306a36Sopenharmony_ci		}
73062306a36Sopenharmony_ci
73162306a36Sopenharmony_ci		buffrag = &pbuf->frag_array[i];
73262306a36Sopenharmony_ci		hwdesc->buffer_length[k] = cpu_to_le16(buffrag->length);
73362306a36Sopenharmony_ci		switch (k) {
73462306a36Sopenharmony_ci		case 0:
73562306a36Sopenharmony_ci			hwdesc->addr_buffer1 = cpu_to_le64(buffrag->dma);
73662306a36Sopenharmony_ci			break;
73762306a36Sopenharmony_ci		case 1:
73862306a36Sopenharmony_ci			hwdesc->addr_buffer2 = cpu_to_le64(buffrag->dma);
73962306a36Sopenharmony_ci			break;
74062306a36Sopenharmony_ci		case 2:
74162306a36Sopenharmony_ci			hwdesc->addr_buffer3 = cpu_to_le64(buffrag->dma);
74262306a36Sopenharmony_ci			break;
74362306a36Sopenharmony_ci		case 3:
74462306a36Sopenharmony_ci			hwdesc->addr_buffer4 = cpu_to_le64(buffrag->dma);
74562306a36Sopenharmony_ci			break;
74662306a36Sopenharmony_ci		}
74762306a36Sopenharmony_ci	}
74862306a36Sopenharmony_ci
74962306a36Sopenharmony_ci	tx_ring->producer = get_next_index(producer, num_txd);
75062306a36Sopenharmony_ci	smp_mb();
75162306a36Sopenharmony_ci
75262306a36Sopenharmony_ci	protocol = ntohs(skb->protocol);
75362306a36Sopenharmony_ci	if (protocol == ETH_P_IP)
75462306a36Sopenharmony_ci		l4_is_udp = ip_hdr(skb)->protocol == IPPROTO_UDP;
75562306a36Sopenharmony_ci	else if (protocol == ETH_P_IPV6)
75662306a36Sopenharmony_ci		l4_is_udp = ipv6_hdr(skb)->nexthdr == IPPROTO_UDP;
75762306a36Sopenharmony_ci
75862306a36Sopenharmony_ci	/* Check if it is a VXLAN packet */
75962306a36Sopenharmony_ci	if (!skb->encapsulation || !l4_is_udp ||
76062306a36Sopenharmony_ci	    !qlcnic_encap_tx_offload(adapter)) {
76162306a36Sopenharmony_ci		if (unlikely(qlcnic_tx_pkt(adapter, first_desc, skb,
76262306a36Sopenharmony_ci					   tx_ring)))
76362306a36Sopenharmony_ci			goto unwind_buff;
76462306a36Sopenharmony_ci	} else {
76562306a36Sopenharmony_ci		if (unlikely(qlcnic_tx_encap_pkt(adapter, first_desc,
76662306a36Sopenharmony_ci						 skb, tx_ring)))
76762306a36Sopenharmony_ci			goto unwind_buff;
76862306a36Sopenharmony_ci	}
76962306a36Sopenharmony_ci
77062306a36Sopenharmony_ci	if (adapter->drv_mac_learn)
77162306a36Sopenharmony_ci		qlcnic_send_filter(adapter, first_desc, skb, tx_ring);
77262306a36Sopenharmony_ci
77362306a36Sopenharmony_ci	tx_ring->tx_stats.tx_bytes += skb->len;
77462306a36Sopenharmony_ci	tx_ring->tx_stats.xmit_called++;
77562306a36Sopenharmony_ci
77662306a36Sopenharmony_ci	/* Ensure writes are complete before HW fetches Tx descriptors */
77762306a36Sopenharmony_ci	wmb();
77862306a36Sopenharmony_ci	qlcnic_update_cmd_producer(tx_ring);
77962306a36Sopenharmony_ci
78062306a36Sopenharmony_ci	return NETDEV_TX_OK;
78162306a36Sopenharmony_ci
78262306a36Sopenharmony_ciunwind_buff:
78362306a36Sopenharmony_ci	qlcnic_unmap_buffers(pdev, skb, pbuf);
78462306a36Sopenharmony_cidrop_packet:
78562306a36Sopenharmony_ci	adapter->stats.txdropped++;
78662306a36Sopenharmony_ci	dev_kfree_skb_any(skb);
78762306a36Sopenharmony_ci	return NETDEV_TX_OK;
78862306a36Sopenharmony_ci}
78962306a36Sopenharmony_ci
79062306a36Sopenharmony_civoid qlcnic_advert_link_change(struct qlcnic_adapter *adapter, int linkup)
79162306a36Sopenharmony_ci{
79262306a36Sopenharmony_ci	struct net_device *netdev = adapter->netdev;
79362306a36Sopenharmony_ci
79462306a36Sopenharmony_ci	if (adapter->ahw->linkup && !linkup) {
79562306a36Sopenharmony_ci		netdev_info(netdev, "NIC Link is down\n");
79662306a36Sopenharmony_ci		adapter->ahw->linkup = 0;
79762306a36Sopenharmony_ci		netif_carrier_off(netdev);
79862306a36Sopenharmony_ci	} else if (!adapter->ahw->linkup && linkup) {
79962306a36Sopenharmony_ci		adapter->ahw->linkup = 1;
80062306a36Sopenharmony_ci
80162306a36Sopenharmony_ci		/* Do not advertise Link up to the stack if device
80262306a36Sopenharmony_ci		 * is in loopback mode
80362306a36Sopenharmony_ci		 */
80462306a36Sopenharmony_ci		if (qlcnic_83xx_check(adapter) && adapter->ahw->lb_mode) {
80562306a36Sopenharmony_ci			netdev_info(netdev, "NIC Link is up for loopback test\n");
80662306a36Sopenharmony_ci			return;
80762306a36Sopenharmony_ci		}
80862306a36Sopenharmony_ci
80962306a36Sopenharmony_ci		netdev_info(netdev, "NIC Link is up\n");
81062306a36Sopenharmony_ci		netif_carrier_on(netdev);
81162306a36Sopenharmony_ci	}
81262306a36Sopenharmony_ci}
81362306a36Sopenharmony_ci
81462306a36Sopenharmony_cistatic int qlcnic_alloc_rx_skb(struct qlcnic_adapter *adapter,
81562306a36Sopenharmony_ci			       struct qlcnic_host_rds_ring *rds_ring,
81662306a36Sopenharmony_ci			       struct qlcnic_rx_buffer *buffer)
81762306a36Sopenharmony_ci{
81862306a36Sopenharmony_ci	struct sk_buff *skb;
81962306a36Sopenharmony_ci	dma_addr_t dma;
82062306a36Sopenharmony_ci	struct pci_dev *pdev = adapter->pdev;
82162306a36Sopenharmony_ci
82262306a36Sopenharmony_ci	skb = netdev_alloc_skb(adapter->netdev, rds_ring->skb_size);
82362306a36Sopenharmony_ci	if (!skb) {
82462306a36Sopenharmony_ci		adapter->stats.skb_alloc_failure++;
82562306a36Sopenharmony_ci		return -ENOMEM;
82662306a36Sopenharmony_ci	}
82762306a36Sopenharmony_ci
82862306a36Sopenharmony_ci	skb_reserve(skb, NET_IP_ALIGN);
82962306a36Sopenharmony_ci	dma = dma_map_single(&pdev->dev, skb->data, rds_ring->dma_size,
83062306a36Sopenharmony_ci			     DMA_FROM_DEVICE);
83162306a36Sopenharmony_ci
83262306a36Sopenharmony_ci	if (dma_mapping_error(&pdev->dev, dma)) {
83362306a36Sopenharmony_ci		adapter->stats.rx_dma_map_error++;
83462306a36Sopenharmony_ci		dev_kfree_skb_any(skb);
83562306a36Sopenharmony_ci		return -ENOMEM;
83662306a36Sopenharmony_ci	}
83762306a36Sopenharmony_ci
83862306a36Sopenharmony_ci	buffer->skb = skb;
83962306a36Sopenharmony_ci	buffer->dma = dma;
84062306a36Sopenharmony_ci
84162306a36Sopenharmony_ci	return 0;
84262306a36Sopenharmony_ci}
84362306a36Sopenharmony_ci
84462306a36Sopenharmony_cistatic void qlcnic_post_rx_buffers_nodb(struct qlcnic_adapter *adapter,
84562306a36Sopenharmony_ci					struct qlcnic_host_rds_ring *rds_ring,
84662306a36Sopenharmony_ci					u8 ring_id)
84762306a36Sopenharmony_ci{
84862306a36Sopenharmony_ci	struct rcv_desc *pdesc;
84962306a36Sopenharmony_ci	struct qlcnic_rx_buffer *buffer;
85062306a36Sopenharmony_ci	int  count = 0;
85162306a36Sopenharmony_ci	uint32_t producer, handle;
85262306a36Sopenharmony_ci	struct list_head *head;
85362306a36Sopenharmony_ci
85462306a36Sopenharmony_ci	if (!spin_trylock(&rds_ring->lock))
85562306a36Sopenharmony_ci		return;
85662306a36Sopenharmony_ci
85762306a36Sopenharmony_ci	producer = rds_ring->producer;
85862306a36Sopenharmony_ci	head = &rds_ring->free_list;
85962306a36Sopenharmony_ci	while (!list_empty(head)) {
86062306a36Sopenharmony_ci		buffer = list_entry(head->next, struct qlcnic_rx_buffer, list);
86162306a36Sopenharmony_ci
86262306a36Sopenharmony_ci		if (!buffer->skb) {
86362306a36Sopenharmony_ci			if (qlcnic_alloc_rx_skb(adapter, rds_ring, buffer))
86462306a36Sopenharmony_ci				break;
86562306a36Sopenharmony_ci		}
86662306a36Sopenharmony_ci		count++;
86762306a36Sopenharmony_ci		list_del(&buffer->list);
86862306a36Sopenharmony_ci
86962306a36Sopenharmony_ci		/* make a rcv descriptor  */
87062306a36Sopenharmony_ci		pdesc = &rds_ring->desc_head[producer];
87162306a36Sopenharmony_ci		handle = qlcnic_get_ref_handle(adapter,
87262306a36Sopenharmony_ci					       buffer->ref_handle, ring_id);
87362306a36Sopenharmony_ci		pdesc->reference_handle = cpu_to_le16(handle);
87462306a36Sopenharmony_ci		pdesc->buffer_length = cpu_to_le32(rds_ring->dma_size);
87562306a36Sopenharmony_ci		pdesc->addr_buffer = cpu_to_le64(buffer->dma);
87662306a36Sopenharmony_ci		producer = get_next_index(producer, rds_ring->num_desc);
87762306a36Sopenharmony_ci	}
87862306a36Sopenharmony_ci	if (count) {
87962306a36Sopenharmony_ci		rds_ring->producer = producer;
88062306a36Sopenharmony_ci		writel((producer - 1) & (rds_ring->num_desc - 1),
88162306a36Sopenharmony_ci		       rds_ring->crb_rcv_producer);
88262306a36Sopenharmony_ci	}
88362306a36Sopenharmony_ci	spin_unlock(&rds_ring->lock);
88462306a36Sopenharmony_ci}
88562306a36Sopenharmony_ci
88662306a36Sopenharmony_cistatic int qlcnic_process_cmd_ring(struct qlcnic_adapter *adapter,
88762306a36Sopenharmony_ci				   struct qlcnic_host_tx_ring *tx_ring,
88862306a36Sopenharmony_ci				   int budget)
88962306a36Sopenharmony_ci{
89062306a36Sopenharmony_ci	u32 sw_consumer, hw_consumer;
89162306a36Sopenharmony_ci	int i, done, count = 0;
89262306a36Sopenharmony_ci	struct qlcnic_cmd_buffer *buffer;
89362306a36Sopenharmony_ci	struct pci_dev *pdev = adapter->pdev;
89462306a36Sopenharmony_ci	struct net_device *netdev = adapter->netdev;
89562306a36Sopenharmony_ci	struct qlcnic_skb_frag *frag;
89662306a36Sopenharmony_ci
89762306a36Sopenharmony_ci	if (!spin_trylock(&tx_ring->tx_clean_lock))
89862306a36Sopenharmony_ci		return 1;
89962306a36Sopenharmony_ci
90062306a36Sopenharmony_ci	sw_consumer = tx_ring->sw_consumer;
90162306a36Sopenharmony_ci	hw_consumer = le32_to_cpu(*(tx_ring->hw_consumer));
90262306a36Sopenharmony_ci
90362306a36Sopenharmony_ci	while (sw_consumer != hw_consumer) {
90462306a36Sopenharmony_ci		buffer = &tx_ring->cmd_buf_arr[sw_consumer];
90562306a36Sopenharmony_ci		if (buffer->skb) {
90662306a36Sopenharmony_ci			frag = &buffer->frag_array[0];
90762306a36Sopenharmony_ci			dma_unmap_single(&pdev->dev, frag->dma, frag->length,
90862306a36Sopenharmony_ci					 DMA_TO_DEVICE);
90962306a36Sopenharmony_ci			frag->dma = 0ULL;
91062306a36Sopenharmony_ci			for (i = 1; i < buffer->frag_count; i++) {
91162306a36Sopenharmony_ci				frag++;
91262306a36Sopenharmony_ci				dma_unmap_page(&pdev->dev, frag->dma,
91362306a36Sopenharmony_ci					       frag->length, DMA_TO_DEVICE);
91462306a36Sopenharmony_ci				frag->dma = 0ULL;
91562306a36Sopenharmony_ci			}
91662306a36Sopenharmony_ci			tx_ring->tx_stats.xmit_finished++;
91762306a36Sopenharmony_ci			dev_kfree_skb_any(buffer->skb);
91862306a36Sopenharmony_ci			buffer->skb = NULL;
91962306a36Sopenharmony_ci		}
92062306a36Sopenharmony_ci
92162306a36Sopenharmony_ci		sw_consumer = get_next_index(sw_consumer, tx_ring->num_desc);
92262306a36Sopenharmony_ci		if (++count >= budget)
92362306a36Sopenharmony_ci			break;
92462306a36Sopenharmony_ci	}
92562306a36Sopenharmony_ci
92662306a36Sopenharmony_ci	tx_ring->sw_consumer = sw_consumer;
92762306a36Sopenharmony_ci
92862306a36Sopenharmony_ci	if (count && netif_running(netdev)) {
92962306a36Sopenharmony_ci		smp_mb();
93062306a36Sopenharmony_ci		if (netif_tx_queue_stopped(tx_ring->txq) &&
93162306a36Sopenharmony_ci		    netif_carrier_ok(netdev)) {
93262306a36Sopenharmony_ci			if (qlcnic_tx_avail(tx_ring) > TX_STOP_THRESH) {
93362306a36Sopenharmony_ci				netif_tx_wake_queue(tx_ring->txq);
93462306a36Sopenharmony_ci				tx_ring->tx_stats.xmit_on++;
93562306a36Sopenharmony_ci			}
93662306a36Sopenharmony_ci		}
93762306a36Sopenharmony_ci		adapter->tx_timeo_cnt = 0;
93862306a36Sopenharmony_ci	}
93962306a36Sopenharmony_ci	/*
94062306a36Sopenharmony_ci	 * If everything is freed up to consumer then check if the ring is full
94162306a36Sopenharmony_ci	 * If the ring is full then check if more needs to be freed and
94262306a36Sopenharmony_ci	 * schedule the call back again.
94362306a36Sopenharmony_ci	 *
94462306a36Sopenharmony_ci	 * This happens when there are 2 CPUs. One could be freeing and the
94562306a36Sopenharmony_ci	 * other filling it. If the ring is full when we get out of here and
94662306a36Sopenharmony_ci	 * the card has already interrupted the host then the host can miss the
94762306a36Sopenharmony_ci	 * interrupt.
94862306a36Sopenharmony_ci	 *
94962306a36Sopenharmony_ci	 * There is still a possible race condition and the host could miss an
95062306a36Sopenharmony_ci	 * interrupt. The card has to take care of this.
95162306a36Sopenharmony_ci	 */
95262306a36Sopenharmony_ci	hw_consumer = le32_to_cpu(*(tx_ring->hw_consumer));
95362306a36Sopenharmony_ci	done = (sw_consumer == hw_consumer);
95462306a36Sopenharmony_ci
95562306a36Sopenharmony_ci	spin_unlock(&tx_ring->tx_clean_lock);
95662306a36Sopenharmony_ci
95762306a36Sopenharmony_ci	return done;
95862306a36Sopenharmony_ci}
95962306a36Sopenharmony_ci
96062306a36Sopenharmony_cistatic int qlcnic_poll(struct napi_struct *napi, int budget)
96162306a36Sopenharmony_ci{
96262306a36Sopenharmony_ci	int tx_complete, work_done;
96362306a36Sopenharmony_ci	struct qlcnic_host_sds_ring *sds_ring;
96462306a36Sopenharmony_ci	struct qlcnic_adapter *adapter;
96562306a36Sopenharmony_ci	struct qlcnic_host_tx_ring *tx_ring;
96662306a36Sopenharmony_ci
96762306a36Sopenharmony_ci	sds_ring = container_of(napi, struct qlcnic_host_sds_ring, napi);
96862306a36Sopenharmony_ci	adapter = sds_ring->adapter;
96962306a36Sopenharmony_ci	tx_ring = sds_ring->tx_ring;
97062306a36Sopenharmony_ci
97162306a36Sopenharmony_ci	tx_complete = qlcnic_process_cmd_ring(adapter, tx_ring,
97262306a36Sopenharmony_ci					      budget);
97362306a36Sopenharmony_ci	work_done = qlcnic_process_rcv_ring(sds_ring, budget);
97462306a36Sopenharmony_ci
97562306a36Sopenharmony_ci	/* Check if we need a repoll */
97662306a36Sopenharmony_ci	if (!tx_complete)
97762306a36Sopenharmony_ci		work_done = budget;
97862306a36Sopenharmony_ci
97962306a36Sopenharmony_ci	if (work_done < budget) {
98062306a36Sopenharmony_ci		napi_complete_done(&sds_ring->napi, work_done);
98162306a36Sopenharmony_ci		if (test_bit(__QLCNIC_DEV_UP, &adapter->state)) {
98262306a36Sopenharmony_ci			qlcnic_enable_sds_intr(adapter, sds_ring);
98362306a36Sopenharmony_ci			qlcnic_enable_tx_intr(adapter, tx_ring);
98462306a36Sopenharmony_ci		}
98562306a36Sopenharmony_ci	}
98662306a36Sopenharmony_ci
98762306a36Sopenharmony_ci	return work_done;
98862306a36Sopenharmony_ci}
98962306a36Sopenharmony_ci
99062306a36Sopenharmony_cistatic int qlcnic_tx_poll(struct napi_struct *napi, int budget)
99162306a36Sopenharmony_ci{
99262306a36Sopenharmony_ci	struct qlcnic_host_tx_ring *tx_ring;
99362306a36Sopenharmony_ci	struct qlcnic_adapter *adapter;
99462306a36Sopenharmony_ci	int work_done;
99562306a36Sopenharmony_ci
99662306a36Sopenharmony_ci	tx_ring = container_of(napi, struct qlcnic_host_tx_ring, napi);
99762306a36Sopenharmony_ci	adapter = tx_ring->adapter;
99862306a36Sopenharmony_ci
99962306a36Sopenharmony_ci	work_done = qlcnic_process_cmd_ring(adapter, tx_ring, budget);
100062306a36Sopenharmony_ci	if (work_done) {
100162306a36Sopenharmony_ci		napi_complete(&tx_ring->napi);
100262306a36Sopenharmony_ci		if (test_bit(__QLCNIC_DEV_UP, &adapter->state))
100362306a36Sopenharmony_ci			qlcnic_enable_tx_intr(adapter, tx_ring);
100462306a36Sopenharmony_ci	} else {
100562306a36Sopenharmony_ci		/* As qlcnic_process_cmd_ring() returned 0, we need a repoll*/
100662306a36Sopenharmony_ci		work_done = budget;
100762306a36Sopenharmony_ci	}
100862306a36Sopenharmony_ci
100962306a36Sopenharmony_ci	return work_done;
101062306a36Sopenharmony_ci}
101162306a36Sopenharmony_ci
101262306a36Sopenharmony_cistatic int qlcnic_rx_poll(struct napi_struct *napi, int budget)
101362306a36Sopenharmony_ci{
101462306a36Sopenharmony_ci	struct qlcnic_host_sds_ring *sds_ring;
101562306a36Sopenharmony_ci	struct qlcnic_adapter *adapter;
101662306a36Sopenharmony_ci	int work_done;
101762306a36Sopenharmony_ci
101862306a36Sopenharmony_ci	sds_ring = container_of(napi, struct qlcnic_host_sds_ring, napi);
101962306a36Sopenharmony_ci	adapter = sds_ring->adapter;
102062306a36Sopenharmony_ci
102162306a36Sopenharmony_ci	work_done = qlcnic_process_rcv_ring(sds_ring, budget);
102262306a36Sopenharmony_ci
102362306a36Sopenharmony_ci	if (work_done < budget) {
102462306a36Sopenharmony_ci		napi_complete_done(&sds_ring->napi, work_done);
102562306a36Sopenharmony_ci		if (test_bit(__QLCNIC_DEV_UP, &adapter->state))
102662306a36Sopenharmony_ci			qlcnic_enable_sds_intr(adapter, sds_ring);
102762306a36Sopenharmony_ci	}
102862306a36Sopenharmony_ci
102962306a36Sopenharmony_ci	return work_done;
103062306a36Sopenharmony_ci}
103162306a36Sopenharmony_ci
103262306a36Sopenharmony_cistatic void qlcnic_handle_linkevent(struct qlcnic_adapter *adapter,
103362306a36Sopenharmony_ci				    struct qlcnic_fw_msg *msg)
103462306a36Sopenharmony_ci{
103562306a36Sopenharmony_ci	u32 cable_OUI;
103662306a36Sopenharmony_ci	u16 cable_len, link_speed;
103762306a36Sopenharmony_ci	u8  link_status, module, duplex, autoneg, lb_status = 0;
103862306a36Sopenharmony_ci	struct net_device *netdev = adapter->netdev;
103962306a36Sopenharmony_ci
104062306a36Sopenharmony_ci	adapter->ahw->has_link_events = 1;
104162306a36Sopenharmony_ci
104262306a36Sopenharmony_ci	cable_OUI = msg->body[1] & 0xffffffff;
104362306a36Sopenharmony_ci	cable_len = (msg->body[1] >> 32) & 0xffff;
104462306a36Sopenharmony_ci	link_speed = (msg->body[1] >> 48) & 0xffff;
104562306a36Sopenharmony_ci
104662306a36Sopenharmony_ci	link_status = msg->body[2] & 0xff;
104762306a36Sopenharmony_ci	duplex = (msg->body[2] >> 16) & 0xff;
104862306a36Sopenharmony_ci	autoneg = (msg->body[2] >> 24) & 0xff;
104962306a36Sopenharmony_ci	lb_status = (msg->body[2] >> 32) & 0x3;
105062306a36Sopenharmony_ci
105162306a36Sopenharmony_ci	module = (msg->body[2] >> 8) & 0xff;
105262306a36Sopenharmony_ci	if (module == LINKEVENT_MODULE_TWINAX_UNSUPPORTED_CABLE)
105362306a36Sopenharmony_ci		dev_info(&netdev->dev,
105462306a36Sopenharmony_ci			 "unsupported cable: OUI 0x%x, length %d\n",
105562306a36Sopenharmony_ci			 cable_OUI, cable_len);
105662306a36Sopenharmony_ci	else if (module == LINKEVENT_MODULE_TWINAX_UNSUPPORTED_CABLELEN)
105762306a36Sopenharmony_ci		dev_info(&netdev->dev, "unsupported cable length %d\n",
105862306a36Sopenharmony_ci			 cable_len);
105962306a36Sopenharmony_ci
106062306a36Sopenharmony_ci	if (!link_status && (lb_status == QLCNIC_ILB_MODE ||
106162306a36Sopenharmony_ci	    lb_status == QLCNIC_ELB_MODE))
106262306a36Sopenharmony_ci		adapter->ahw->loopback_state |= QLCNIC_LINKEVENT;
106362306a36Sopenharmony_ci
106462306a36Sopenharmony_ci	qlcnic_advert_link_change(adapter, link_status);
106562306a36Sopenharmony_ci
106662306a36Sopenharmony_ci	if (duplex == LINKEVENT_FULL_DUPLEX)
106762306a36Sopenharmony_ci		adapter->ahw->link_duplex = DUPLEX_FULL;
106862306a36Sopenharmony_ci	else
106962306a36Sopenharmony_ci		adapter->ahw->link_duplex = DUPLEX_HALF;
107062306a36Sopenharmony_ci
107162306a36Sopenharmony_ci	adapter->ahw->module_type = module;
107262306a36Sopenharmony_ci	adapter->ahw->link_autoneg = autoneg;
107362306a36Sopenharmony_ci
107462306a36Sopenharmony_ci	if (link_status) {
107562306a36Sopenharmony_ci		adapter->ahw->link_speed = link_speed;
107662306a36Sopenharmony_ci	} else {
107762306a36Sopenharmony_ci		adapter->ahw->link_speed = SPEED_UNKNOWN;
107862306a36Sopenharmony_ci		adapter->ahw->link_duplex = DUPLEX_UNKNOWN;
107962306a36Sopenharmony_ci	}
108062306a36Sopenharmony_ci}
108162306a36Sopenharmony_ci
108262306a36Sopenharmony_cistatic void qlcnic_handle_fw_message(int desc_cnt, int index,
108362306a36Sopenharmony_ci				     struct qlcnic_host_sds_ring *sds_ring)
108462306a36Sopenharmony_ci{
108562306a36Sopenharmony_ci	struct qlcnic_fw_msg msg;
108662306a36Sopenharmony_ci	struct status_desc *desc;
108762306a36Sopenharmony_ci	struct qlcnic_adapter *adapter;
108862306a36Sopenharmony_ci	struct device *dev;
108962306a36Sopenharmony_ci	int i = 0, opcode, ret;
109062306a36Sopenharmony_ci
109162306a36Sopenharmony_ci	while (desc_cnt > 0 && i < 8) {
109262306a36Sopenharmony_ci		desc = &sds_ring->desc_head[index];
109362306a36Sopenharmony_ci		msg.words[i++] = le64_to_cpu(desc->status_desc_data[0]);
109462306a36Sopenharmony_ci		msg.words[i++] = le64_to_cpu(desc->status_desc_data[1]);
109562306a36Sopenharmony_ci
109662306a36Sopenharmony_ci		index = get_next_index(index, sds_ring->num_desc);
109762306a36Sopenharmony_ci		desc_cnt--;
109862306a36Sopenharmony_ci	}
109962306a36Sopenharmony_ci
110062306a36Sopenharmony_ci	adapter = sds_ring->adapter;
110162306a36Sopenharmony_ci	dev = &adapter->pdev->dev;
110262306a36Sopenharmony_ci	opcode = qlcnic_get_nic_msg_opcode(msg.body[0]);
110362306a36Sopenharmony_ci
110462306a36Sopenharmony_ci	switch (opcode) {
110562306a36Sopenharmony_ci	case QLCNIC_C2H_OPCODE_GET_LINKEVENT_RESPONSE:
110662306a36Sopenharmony_ci		qlcnic_handle_linkevent(adapter, &msg);
110762306a36Sopenharmony_ci		break;
110862306a36Sopenharmony_ci	case QLCNIC_C2H_OPCODE_CONFIG_LOOPBACK:
110962306a36Sopenharmony_ci		ret = (u32)(msg.body[1]);
111062306a36Sopenharmony_ci		switch (ret) {
111162306a36Sopenharmony_ci		case 0:
111262306a36Sopenharmony_ci			adapter->ahw->loopback_state |= QLCNIC_LB_RESPONSE;
111362306a36Sopenharmony_ci			break;
111462306a36Sopenharmony_ci		case 1:
111562306a36Sopenharmony_ci			dev_info(dev, "loopback already in progress\n");
111662306a36Sopenharmony_ci			adapter->ahw->diag_cnt = -EINPROGRESS;
111762306a36Sopenharmony_ci			break;
111862306a36Sopenharmony_ci		case 2:
111962306a36Sopenharmony_ci			dev_info(dev, "loopback cable is not connected\n");
112062306a36Sopenharmony_ci			adapter->ahw->diag_cnt = -ENODEV;
112162306a36Sopenharmony_ci			break;
112262306a36Sopenharmony_ci		default:
112362306a36Sopenharmony_ci			dev_info(dev,
112462306a36Sopenharmony_ci				 "loopback configure request failed, err %x\n",
112562306a36Sopenharmony_ci				 ret);
112662306a36Sopenharmony_ci			adapter->ahw->diag_cnt = -EIO;
112762306a36Sopenharmony_ci			break;
112862306a36Sopenharmony_ci		}
112962306a36Sopenharmony_ci		break;
113062306a36Sopenharmony_ci	case QLCNIC_C2H_OPCODE_GET_DCB_AEN:
113162306a36Sopenharmony_ci		qlcnic_dcb_aen_handler(adapter->dcb, (void *)&msg);
113262306a36Sopenharmony_ci		break;
113362306a36Sopenharmony_ci	default:
113462306a36Sopenharmony_ci		break;
113562306a36Sopenharmony_ci	}
113662306a36Sopenharmony_ci}
113762306a36Sopenharmony_ci
113862306a36Sopenharmony_cistatic struct sk_buff *qlcnic_process_rxbuf(struct qlcnic_adapter *adapter,
113962306a36Sopenharmony_ci					    struct qlcnic_host_rds_ring *ring,
114062306a36Sopenharmony_ci					    u16 index, u16 cksum)
114162306a36Sopenharmony_ci{
114262306a36Sopenharmony_ci	struct qlcnic_rx_buffer *buffer;
114362306a36Sopenharmony_ci	struct sk_buff *skb;
114462306a36Sopenharmony_ci
114562306a36Sopenharmony_ci	buffer = &ring->rx_buf_arr[index];
114662306a36Sopenharmony_ci	if (unlikely(buffer->skb == NULL)) {
114762306a36Sopenharmony_ci		WARN_ON(1);
114862306a36Sopenharmony_ci		return NULL;
114962306a36Sopenharmony_ci	}
115062306a36Sopenharmony_ci
115162306a36Sopenharmony_ci	dma_unmap_single(&adapter->pdev->dev, buffer->dma, ring->dma_size,
115262306a36Sopenharmony_ci			 DMA_FROM_DEVICE);
115362306a36Sopenharmony_ci
115462306a36Sopenharmony_ci	skb = buffer->skb;
115562306a36Sopenharmony_ci	if (likely((adapter->netdev->features & NETIF_F_RXCSUM) &&
115662306a36Sopenharmony_ci		   (cksum == STATUS_CKSUM_OK || cksum == STATUS_CKSUM_LOOP))) {
115762306a36Sopenharmony_ci		adapter->stats.csummed++;
115862306a36Sopenharmony_ci		skb->ip_summed = CHECKSUM_UNNECESSARY;
115962306a36Sopenharmony_ci	} else {
116062306a36Sopenharmony_ci		skb_checksum_none_assert(skb);
116162306a36Sopenharmony_ci	}
116262306a36Sopenharmony_ci
116362306a36Sopenharmony_ci
116462306a36Sopenharmony_ci	buffer->skb = NULL;
116562306a36Sopenharmony_ci
116662306a36Sopenharmony_ci	return skb;
116762306a36Sopenharmony_ci}
116862306a36Sopenharmony_ci
116962306a36Sopenharmony_cistatic inline int qlcnic_check_rx_tagging(struct qlcnic_adapter *adapter,
117062306a36Sopenharmony_ci					  struct sk_buff *skb, u16 *vlan_tag)
117162306a36Sopenharmony_ci{
117262306a36Sopenharmony_ci	struct ethhdr *eth_hdr;
117362306a36Sopenharmony_ci
117462306a36Sopenharmony_ci	if (!__vlan_get_tag(skb, vlan_tag)) {
117562306a36Sopenharmony_ci		eth_hdr = (struct ethhdr *)skb->data;
117662306a36Sopenharmony_ci		memmove(skb->data + VLAN_HLEN, eth_hdr, ETH_ALEN * 2);
117762306a36Sopenharmony_ci		skb_pull(skb, VLAN_HLEN);
117862306a36Sopenharmony_ci	}
117962306a36Sopenharmony_ci	if (!adapter->rx_pvid)
118062306a36Sopenharmony_ci		return 0;
118162306a36Sopenharmony_ci
118262306a36Sopenharmony_ci	if (*vlan_tag == adapter->rx_pvid) {
118362306a36Sopenharmony_ci		/* Outer vlan tag. Packet should follow non-vlan path */
118462306a36Sopenharmony_ci		*vlan_tag = 0xffff;
118562306a36Sopenharmony_ci		return 0;
118662306a36Sopenharmony_ci	}
118762306a36Sopenharmony_ci	if (adapter->flags & QLCNIC_TAGGING_ENABLED)
118862306a36Sopenharmony_ci		return 0;
118962306a36Sopenharmony_ci
119062306a36Sopenharmony_ci	return -EINVAL;
119162306a36Sopenharmony_ci}
119262306a36Sopenharmony_ci
119362306a36Sopenharmony_cistatic struct qlcnic_rx_buffer *
119462306a36Sopenharmony_ciqlcnic_process_rcv(struct qlcnic_adapter *adapter,
119562306a36Sopenharmony_ci		   struct qlcnic_host_sds_ring *sds_ring, int ring,
119662306a36Sopenharmony_ci		   u64 sts_data0)
119762306a36Sopenharmony_ci{
119862306a36Sopenharmony_ci	struct net_device *netdev = adapter->netdev;
119962306a36Sopenharmony_ci	struct qlcnic_recv_context *recv_ctx = adapter->recv_ctx;
120062306a36Sopenharmony_ci	struct qlcnic_rx_buffer *buffer;
120162306a36Sopenharmony_ci	struct sk_buff *skb;
120262306a36Sopenharmony_ci	struct qlcnic_host_rds_ring *rds_ring;
120362306a36Sopenharmony_ci	int index, length, cksum, pkt_offset, is_lb_pkt;
120462306a36Sopenharmony_ci	u16 vid = 0xffff, t_vid;
120562306a36Sopenharmony_ci
120662306a36Sopenharmony_ci	if (unlikely(ring >= adapter->max_rds_rings))
120762306a36Sopenharmony_ci		return NULL;
120862306a36Sopenharmony_ci
120962306a36Sopenharmony_ci	rds_ring = &recv_ctx->rds_rings[ring];
121062306a36Sopenharmony_ci
121162306a36Sopenharmony_ci	index = qlcnic_get_sts_refhandle(sts_data0);
121262306a36Sopenharmony_ci	if (unlikely(index >= rds_ring->num_desc))
121362306a36Sopenharmony_ci		return NULL;
121462306a36Sopenharmony_ci
121562306a36Sopenharmony_ci	buffer = &rds_ring->rx_buf_arr[index];
121662306a36Sopenharmony_ci	length = qlcnic_get_sts_totallength(sts_data0);
121762306a36Sopenharmony_ci	cksum  = qlcnic_get_sts_status(sts_data0);
121862306a36Sopenharmony_ci	pkt_offset = qlcnic_get_sts_pkt_offset(sts_data0);
121962306a36Sopenharmony_ci
122062306a36Sopenharmony_ci	skb = qlcnic_process_rxbuf(adapter, rds_ring, index, cksum);
122162306a36Sopenharmony_ci	if (!skb)
122262306a36Sopenharmony_ci		return buffer;
122362306a36Sopenharmony_ci
122462306a36Sopenharmony_ci	if (adapter->rx_mac_learn) {
122562306a36Sopenharmony_ci		t_vid = 0;
122662306a36Sopenharmony_ci		is_lb_pkt = qlcnic_82xx_is_lb_pkt(sts_data0);
122762306a36Sopenharmony_ci		qlcnic_add_lb_filter(adapter, skb, is_lb_pkt, t_vid);
122862306a36Sopenharmony_ci	}
122962306a36Sopenharmony_ci
123062306a36Sopenharmony_ci	if (length > rds_ring->skb_size)
123162306a36Sopenharmony_ci		skb_put(skb, rds_ring->skb_size);
123262306a36Sopenharmony_ci	else
123362306a36Sopenharmony_ci		skb_put(skb, length);
123462306a36Sopenharmony_ci
123562306a36Sopenharmony_ci	if (pkt_offset)
123662306a36Sopenharmony_ci		skb_pull(skb, pkt_offset);
123762306a36Sopenharmony_ci
123862306a36Sopenharmony_ci	if (unlikely(qlcnic_check_rx_tagging(adapter, skb, &vid))) {
123962306a36Sopenharmony_ci		adapter->stats.rxdropped++;
124062306a36Sopenharmony_ci		dev_kfree_skb(skb);
124162306a36Sopenharmony_ci		return buffer;
124262306a36Sopenharmony_ci	}
124362306a36Sopenharmony_ci
124462306a36Sopenharmony_ci	skb->protocol = eth_type_trans(skb, netdev);
124562306a36Sopenharmony_ci
124662306a36Sopenharmony_ci	if (vid != 0xffff)
124762306a36Sopenharmony_ci		__vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vid);
124862306a36Sopenharmony_ci
124962306a36Sopenharmony_ci	napi_gro_receive(&sds_ring->napi, skb);
125062306a36Sopenharmony_ci
125162306a36Sopenharmony_ci	adapter->stats.rx_pkts++;
125262306a36Sopenharmony_ci	adapter->stats.rxbytes += length;
125362306a36Sopenharmony_ci
125462306a36Sopenharmony_ci	return buffer;
125562306a36Sopenharmony_ci}
125662306a36Sopenharmony_ci
125762306a36Sopenharmony_ci#define QLC_TCP_HDR_SIZE            20
125862306a36Sopenharmony_ci#define QLC_TCP_TS_OPTION_SIZE      12
125962306a36Sopenharmony_ci#define QLC_TCP_TS_HDR_SIZE         (QLC_TCP_HDR_SIZE + QLC_TCP_TS_OPTION_SIZE)
126062306a36Sopenharmony_ci
126162306a36Sopenharmony_cistatic struct qlcnic_rx_buffer *
126262306a36Sopenharmony_ciqlcnic_process_lro(struct qlcnic_adapter *adapter,
126362306a36Sopenharmony_ci		   int ring, u64 sts_data0, u64 sts_data1)
126462306a36Sopenharmony_ci{
126562306a36Sopenharmony_ci	struct net_device *netdev = adapter->netdev;
126662306a36Sopenharmony_ci	struct qlcnic_recv_context *recv_ctx = adapter->recv_ctx;
126762306a36Sopenharmony_ci	struct qlcnic_rx_buffer *buffer;
126862306a36Sopenharmony_ci	struct sk_buff *skb;
126962306a36Sopenharmony_ci	struct qlcnic_host_rds_ring *rds_ring;
127062306a36Sopenharmony_ci	struct iphdr *iph;
127162306a36Sopenharmony_ci	struct ipv6hdr *ipv6h;
127262306a36Sopenharmony_ci	struct tcphdr *th;
127362306a36Sopenharmony_ci	bool push, timestamp;
127462306a36Sopenharmony_ci	int index, l2_hdr_offset, l4_hdr_offset, is_lb_pkt;
127562306a36Sopenharmony_ci	u16 lro_length, length, data_offset, t_vid, vid = 0xffff;
127662306a36Sopenharmony_ci	u32 seq_number;
127762306a36Sopenharmony_ci
127862306a36Sopenharmony_ci	if (unlikely(ring >= adapter->max_rds_rings))
127962306a36Sopenharmony_ci		return NULL;
128062306a36Sopenharmony_ci
128162306a36Sopenharmony_ci	rds_ring = &recv_ctx->rds_rings[ring];
128262306a36Sopenharmony_ci
128362306a36Sopenharmony_ci	index = qlcnic_get_lro_sts_refhandle(sts_data0);
128462306a36Sopenharmony_ci	if (unlikely(index >= rds_ring->num_desc))
128562306a36Sopenharmony_ci		return NULL;
128662306a36Sopenharmony_ci
128762306a36Sopenharmony_ci	buffer = &rds_ring->rx_buf_arr[index];
128862306a36Sopenharmony_ci
128962306a36Sopenharmony_ci	timestamp = qlcnic_get_lro_sts_timestamp(sts_data0);
129062306a36Sopenharmony_ci	lro_length = qlcnic_get_lro_sts_length(sts_data0);
129162306a36Sopenharmony_ci	l2_hdr_offset = qlcnic_get_lro_sts_l2_hdr_offset(sts_data0);
129262306a36Sopenharmony_ci	l4_hdr_offset = qlcnic_get_lro_sts_l4_hdr_offset(sts_data0);
129362306a36Sopenharmony_ci	push = qlcnic_get_lro_sts_push_flag(sts_data0);
129462306a36Sopenharmony_ci	seq_number = qlcnic_get_lro_sts_seq_number(sts_data1);
129562306a36Sopenharmony_ci
129662306a36Sopenharmony_ci	skb = qlcnic_process_rxbuf(adapter, rds_ring, index, STATUS_CKSUM_OK);
129762306a36Sopenharmony_ci	if (!skb)
129862306a36Sopenharmony_ci		return buffer;
129962306a36Sopenharmony_ci
130062306a36Sopenharmony_ci	if (adapter->rx_mac_learn) {
130162306a36Sopenharmony_ci		t_vid = 0;
130262306a36Sopenharmony_ci		is_lb_pkt = qlcnic_82xx_is_lb_pkt(sts_data0);
130362306a36Sopenharmony_ci		qlcnic_add_lb_filter(adapter, skb, is_lb_pkt, t_vid);
130462306a36Sopenharmony_ci	}
130562306a36Sopenharmony_ci
130662306a36Sopenharmony_ci	if (timestamp)
130762306a36Sopenharmony_ci		data_offset = l4_hdr_offset + QLC_TCP_TS_HDR_SIZE;
130862306a36Sopenharmony_ci	else
130962306a36Sopenharmony_ci		data_offset = l4_hdr_offset + QLC_TCP_HDR_SIZE;
131062306a36Sopenharmony_ci
131162306a36Sopenharmony_ci	skb_put(skb, lro_length + data_offset);
131262306a36Sopenharmony_ci	skb_pull(skb, l2_hdr_offset);
131362306a36Sopenharmony_ci
131462306a36Sopenharmony_ci	if (unlikely(qlcnic_check_rx_tagging(adapter, skb, &vid))) {
131562306a36Sopenharmony_ci		adapter->stats.rxdropped++;
131662306a36Sopenharmony_ci		dev_kfree_skb(skb);
131762306a36Sopenharmony_ci		return buffer;
131862306a36Sopenharmony_ci	}
131962306a36Sopenharmony_ci
132062306a36Sopenharmony_ci	skb->protocol = eth_type_trans(skb, netdev);
132162306a36Sopenharmony_ci
132262306a36Sopenharmony_ci	if (ntohs(skb->protocol) == ETH_P_IPV6) {
132362306a36Sopenharmony_ci		ipv6h = (struct ipv6hdr *)skb->data;
132462306a36Sopenharmony_ci		th = (struct tcphdr *)(skb->data + sizeof(struct ipv6hdr));
132562306a36Sopenharmony_ci		length = (th->doff << 2) + lro_length;
132662306a36Sopenharmony_ci		ipv6h->payload_len = htons(length);
132762306a36Sopenharmony_ci	} else {
132862306a36Sopenharmony_ci		iph = (struct iphdr *)skb->data;
132962306a36Sopenharmony_ci		th = (struct tcphdr *)(skb->data + (iph->ihl << 2));
133062306a36Sopenharmony_ci		length = (iph->ihl << 2) + (th->doff << 2) + lro_length;
133162306a36Sopenharmony_ci		csum_replace2(&iph->check, iph->tot_len, htons(length));
133262306a36Sopenharmony_ci		iph->tot_len = htons(length);
133362306a36Sopenharmony_ci	}
133462306a36Sopenharmony_ci
133562306a36Sopenharmony_ci	th->psh = push;
133662306a36Sopenharmony_ci	th->seq = htonl(seq_number);
133762306a36Sopenharmony_ci	length = skb->len;
133862306a36Sopenharmony_ci
133962306a36Sopenharmony_ci	if (adapter->flags & QLCNIC_FW_LRO_MSS_CAP) {
134062306a36Sopenharmony_ci		skb_shinfo(skb)->gso_size = qlcnic_get_lro_sts_mss(sts_data1);
134162306a36Sopenharmony_ci		if (skb->protocol == htons(ETH_P_IPV6))
134262306a36Sopenharmony_ci			skb_shinfo(skb)->gso_type = SKB_GSO_TCPV6;
134362306a36Sopenharmony_ci		else
134462306a36Sopenharmony_ci			skb_shinfo(skb)->gso_type = SKB_GSO_TCPV4;
134562306a36Sopenharmony_ci	}
134662306a36Sopenharmony_ci
134762306a36Sopenharmony_ci	if (vid != 0xffff)
134862306a36Sopenharmony_ci		__vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vid);
134962306a36Sopenharmony_ci	netif_receive_skb(skb);
135062306a36Sopenharmony_ci
135162306a36Sopenharmony_ci	adapter->stats.lro_pkts++;
135262306a36Sopenharmony_ci	adapter->stats.lrobytes += length;
135362306a36Sopenharmony_ci
135462306a36Sopenharmony_ci	return buffer;
135562306a36Sopenharmony_ci}
135662306a36Sopenharmony_ci
135762306a36Sopenharmony_cistatic int qlcnic_process_rcv_ring(struct qlcnic_host_sds_ring *sds_ring, int max)
135862306a36Sopenharmony_ci{
135962306a36Sopenharmony_ci	struct qlcnic_host_rds_ring *rds_ring;
136062306a36Sopenharmony_ci	struct qlcnic_adapter *adapter = sds_ring->adapter;
136162306a36Sopenharmony_ci	struct list_head *cur;
136262306a36Sopenharmony_ci	struct status_desc *desc;
136362306a36Sopenharmony_ci	struct qlcnic_rx_buffer *rxbuf;
136462306a36Sopenharmony_ci	int opcode, desc_cnt, count = 0;
136562306a36Sopenharmony_ci	u64 sts_data0, sts_data1;
136662306a36Sopenharmony_ci	u8 ring;
136762306a36Sopenharmony_ci	u32 consumer = sds_ring->consumer;
136862306a36Sopenharmony_ci
136962306a36Sopenharmony_ci	while (count < max) {
137062306a36Sopenharmony_ci		desc = &sds_ring->desc_head[consumer];
137162306a36Sopenharmony_ci		sts_data0 = le64_to_cpu(desc->status_desc_data[0]);
137262306a36Sopenharmony_ci
137362306a36Sopenharmony_ci		if (!(sts_data0 & STATUS_OWNER_HOST))
137462306a36Sopenharmony_ci			break;
137562306a36Sopenharmony_ci
137662306a36Sopenharmony_ci		desc_cnt = qlcnic_get_sts_desc_cnt(sts_data0);
137762306a36Sopenharmony_ci		opcode = qlcnic_get_sts_opcode(sts_data0);
137862306a36Sopenharmony_ci		switch (opcode) {
137962306a36Sopenharmony_ci		case QLCNIC_RXPKT_DESC:
138062306a36Sopenharmony_ci		case QLCNIC_OLD_RXPKT_DESC:
138162306a36Sopenharmony_ci		case QLCNIC_SYN_OFFLOAD:
138262306a36Sopenharmony_ci			ring = qlcnic_get_sts_type(sts_data0);
138362306a36Sopenharmony_ci			rxbuf = qlcnic_process_rcv(adapter, sds_ring, ring,
138462306a36Sopenharmony_ci						   sts_data0);
138562306a36Sopenharmony_ci			break;
138662306a36Sopenharmony_ci		case QLCNIC_LRO_DESC:
138762306a36Sopenharmony_ci			ring = qlcnic_get_lro_sts_type(sts_data0);
138862306a36Sopenharmony_ci			sts_data1 = le64_to_cpu(desc->status_desc_data[1]);
138962306a36Sopenharmony_ci			rxbuf = qlcnic_process_lro(adapter, ring, sts_data0,
139062306a36Sopenharmony_ci						   sts_data1);
139162306a36Sopenharmony_ci			break;
139262306a36Sopenharmony_ci		case QLCNIC_RESPONSE_DESC:
139362306a36Sopenharmony_ci			qlcnic_handle_fw_message(desc_cnt, consumer, sds_ring);
139462306a36Sopenharmony_ci			goto skip;
139562306a36Sopenharmony_ci		default:
139662306a36Sopenharmony_ci			goto skip;
139762306a36Sopenharmony_ci		}
139862306a36Sopenharmony_ci		WARN_ON(desc_cnt > 1);
139962306a36Sopenharmony_ci
140062306a36Sopenharmony_ci		if (likely(rxbuf))
140162306a36Sopenharmony_ci			list_add_tail(&rxbuf->list, &sds_ring->free_list[ring]);
140262306a36Sopenharmony_ci		else
140362306a36Sopenharmony_ci			adapter->stats.null_rxbuf++;
140462306a36Sopenharmony_ciskip:
140562306a36Sopenharmony_ci		for (; desc_cnt > 0; desc_cnt--) {
140662306a36Sopenharmony_ci			desc = &sds_ring->desc_head[consumer];
140762306a36Sopenharmony_ci			desc->status_desc_data[0] = QLCNIC_DESC_OWNER_FW;
140862306a36Sopenharmony_ci			consumer = get_next_index(consumer, sds_ring->num_desc);
140962306a36Sopenharmony_ci		}
141062306a36Sopenharmony_ci		count++;
141162306a36Sopenharmony_ci	}
141262306a36Sopenharmony_ci
141362306a36Sopenharmony_ci	for (ring = 0; ring < adapter->max_rds_rings; ring++) {
141462306a36Sopenharmony_ci		rds_ring = &adapter->recv_ctx->rds_rings[ring];
141562306a36Sopenharmony_ci		if (!list_empty(&sds_ring->free_list[ring])) {
141662306a36Sopenharmony_ci			list_for_each(cur, &sds_ring->free_list[ring]) {
141762306a36Sopenharmony_ci				rxbuf = list_entry(cur, struct qlcnic_rx_buffer,
141862306a36Sopenharmony_ci						   list);
141962306a36Sopenharmony_ci				qlcnic_alloc_rx_skb(adapter, rds_ring, rxbuf);
142062306a36Sopenharmony_ci			}
142162306a36Sopenharmony_ci			spin_lock(&rds_ring->lock);
142262306a36Sopenharmony_ci			list_splice_tail_init(&sds_ring->free_list[ring],
142362306a36Sopenharmony_ci					      &rds_ring->free_list);
142462306a36Sopenharmony_ci			spin_unlock(&rds_ring->lock);
142562306a36Sopenharmony_ci		}
142662306a36Sopenharmony_ci
142762306a36Sopenharmony_ci		qlcnic_post_rx_buffers_nodb(adapter, rds_ring, ring);
142862306a36Sopenharmony_ci	}
142962306a36Sopenharmony_ci
143062306a36Sopenharmony_ci	if (count) {
143162306a36Sopenharmony_ci		sds_ring->consumer = consumer;
143262306a36Sopenharmony_ci		writel(consumer, sds_ring->crb_sts_consumer);
143362306a36Sopenharmony_ci	}
143462306a36Sopenharmony_ci
143562306a36Sopenharmony_ci	return count;
143662306a36Sopenharmony_ci}
143762306a36Sopenharmony_ci
143862306a36Sopenharmony_civoid qlcnic_post_rx_buffers(struct qlcnic_adapter *adapter,
143962306a36Sopenharmony_ci			    struct qlcnic_host_rds_ring *rds_ring, u8 ring_id)
144062306a36Sopenharmony_ci{
144162306a36Sopenharmony_ci	struct rcv_desc *pdesc;
144262306a36Sopenharmony_ci	struct qlcnic_rx_buffer *buffer;
144362306a36Sopenharmony_ci	int count = 0;
144462306a36Sopenharmony_ci	u32 producer, handle;
144562306a36Sopenharmony_ci	struct list_head *head;
144662306a36Sopenharmony_ci
144762306a36Sopenharmony_ci	producer = rds_ring->producer;
144862306a36Sopenharmony_ci	head = &rds_ring->free_list;
144962306a36Sopenharmony_ci
145062306a36Sopenharmony_ci	while (!list_empty(head)) {
145162306a36Sopenharmony_ci
145262306a36Sopenharmony_ci		buffer = list_entry(head->next, struct qlcnic_rx_buffer, list);
145362306a36Sopenharmony_ci
145462306a36Sopenharmony_ci		if (!buffer->skb) {
145562306a36Sopenharmony_ci			if (qlcnic_alloc_rx_skb(adapter, rds_ring, buffer))
145662306a36Sopenharmony_ci				break;
145762306a36Sopenharmony_ci		}
145862306a36Sopenharmony_ci
145962306a36Sopenharmony_ci		count++;
146062306a36Sopenharmony_ci		list_del(&buffer->list);
146162306a36Sopenharmony_ci
146262306a36Sopenharmony_ci		/* make a rcv descriptor  */
146362306a36Sopenharmony_ci		pdesc = &rds_ring->desc_head[producer];
146462306a36Sopenharmony_ci		pdesc->addr_buffer = cpu_to_le64(buffer->dma);
146562306a36Sopenharmony_ci		handle = qlcnic_get_ref_handle(adapter, buffer->ref_handle,
146662306a36Sopenharmony_ci					       ring_id);
146762306a36Sopenharmony_ci		pdesc->reference_handle = cpu_to_le16(handle);
146862306a36Sopenharmony_ci		pdesc->buffer_length = cpu_to_le32(rds_ring->dma_size);
146962306a36Sopenharmony_ci		producer = get_next_index(producer, rds_ring->num_desc);
147062306a36Sopenharmony_ci	}
147162306a36Sopenharmony_ci
147262306a36Sopenharmony_ci	if (count) {
147362306a36Sopenharmony_ci		rds_ring->producer = producer;
147462306a36Sopenharmony_ci		writel((producer-1) & (rds_ring->num_desc-1),
147562306a36Sopenharmony_ci		       rds_ring->crb_rcv_producer);
147662306a36Sopenharmony_ci	}
147762306a36Sopenharmony_ci}
147862306a36Sopenharmony_ci
147962306a36Sopenharmony_cistatic void dump_skb(struct sk_buff *skb, struct qlcnic_adapter *adapter)
148062306a36Sopenharmony_ci{
148162306a36Sopenharmony_ci	if (adapter->ahw->msg_enable & NETIF_MSG_DRV) {
148262306a36Sopenharmony_ci		char prefix[30];
148362306a36Sopenharmony_ci
148462306a36Sopenharmony_ci		scnprintf(prefix, sizeof(prefix), "%s: %s: ",
148562306a36Sopenharmony_ci			  dev_name(&adapter->pdev->dev), __func__);
148662306a36Sopenharmony_ci
148762306a36Sopenharmony_ci		print_hex_dump_debug(prefix, DUMP_PREFIX_NONE, 16, 1,
148862306a36Sopenharmony_ci				     skb->data, skb->len, true);
148962306a36Sopenharmony_ci	}
149062306a36Sopenharmony_ci}
149162306a36Sopenharmony_ci
149262306a36Sopenharmony_cistatic void qlcnic_process_rcv_diag(struct qlcnic_adapter *adapter, int ring,
149362306a36Sopenharmony_ci				    u64 sts_data0)
149462306a36Sopenharmony_ci{
149562306a36Sopenharmony_ci	struct qlcnic_recv_context *recv_ctx = adapter->recv_ctx;
149662306a36Sopenharmony_ci	struct sk_buff *skb;
149762306a36Sopenharmony_ci	struct qlcnic_host_rds_ring *rds_ring;
149862306a36Sopenharmony_ci	int index, length, cksum, pkt_offset;
149962306a36Sopenharmony_ci
150062306a36Sopenharmony_ci	if (unlikely(ring >= adapter->max_rds_rings))
150162306a36Sopenharmony_ci		return;
150262306a36Sopenharmony_ci
150362306a36Sopenharmony_ci	rds_ring = &recv_ctx->rds_rings[ring];
150462306a36Sopenharmony_ci
150562306a36Sopenharmony_ci	index = qlcnic_get_sts_refhandle(sts_data0);
150662306a36Sopenharmony_ci	length = qlcnic_get_sts_totallength(sts_data0);
150762306a36Sopenharmony_ci	if (unlikely(index >= rds_ring->num_desc))
150862306a36Sopenharmony_ci		return;
150962306a36Sopenharmony_ci
151062306a36Sopenharmony_ci	cksum  = qlcnic_get_sts_status(sts_data0);
151162306a36Sopenharmony_ci	pkt_offset = qlcnic_get_sts_pkt_offset(sts_data0);
151262306a36Sopenharmony_ci
151362306a36Sopenharmony_ci	skb = qlcnic_process_rxbuf(adapter, rds_ring, index, cksum);
151462306a36Sopenharmony_ci	if (!skb)
151562306a36Sopenharmony_ci		return;
151662306a36Sopenharmony_ci
151762306a36Sopenharmony_ci	if (length > rds_ring->skb_size)
151862306a36Sopenharmony_ci		skb_put(skb, rds_ring->skb_size);
151962306a36Sopenharmony_ci	else
152062306a36Sopenharmony_ci		skb_put(skb, length);
152162306a36Sopenharmony_ci
152262306a36Sopenharmony_ci	if (pkt_offset)
152362306a36Sopenharmony_ci		skb_pull(skb, pkt_offset);
152462306a36Sopenharmony_ci
152562306a36Sopenharmony_ci	if (!qlcnic_check_loopback_buff(skb->data, adapter->mac_addr))
152662306a36Sopenharmony_ci		adapter->ahw->diag_cnt++;
152762306a36Sopenharmony_ci	else
152862306a36Sopenharmony_ci		dump_skb(skb, adapter);
152962306a36Sopenharmony_ci
153062306a36Sopenharmony_ci	dev_kfree_skb_any(skb);
153162306a36Sopenharmony_ci	adapter->stats.rx_pkts++;
153262306a36Sopenharmony_ci	adapter->stats.rxbytes += length;
153362306a36Sopenharmony_ci
153462306a36Sopenharmony_ci	return;
153562306a36Sopenharmony_ci}
153662306a36Sopenharmony_ci
153762306a36Sopenharmony_civoid qlcnic_82xx_process_rcv_ring_diag(struct qlcnic_host_sds_ring *sds_ring)
153862306a36Sopenharmony_ci{
153962306a36Sopenharmony_ci	struct qlcnic_adapter *adapter = sds_ring->adapter;
154062306a36Sopenharmony_ci	struct status_desc *desc;
154162306a36Sopenharmony_ci	u64 sts_data0;
154262306a36Sopenharmony_ci	int ring, opcode, desc_cnt;
154362306a36Sopenharmony_ci
154462306a36Sopenharmony_ci	u32 consumer = sds_ring->consumer;
154562306a36Sopenharmony_ci
154662306a36Sopenharmony_ci	desc = &sds_ring->desc_head[consumer];
154762306a36Sopenharmony_ci	sts_data0 = le64_to_cpu(desc->status_desc_data[0]);
154862306a36Sopenharmony_ci
154962306a36Sopenharmony_ci	if (!(sts_data0 & STATUS_OWNER_HOST))
155062306a36Sopenharmony_ci		return;
155162306a36Sopenharmony_ci
155262306a36Sopenharmony_ci	desc_cnt = qlcnic_get_sts_desc_cnt(sts_data0);
155362306a36Sopenharmony_ci	opcode = qlcnic_get_sts_opcode(sts_data0);
155462306a36Sopenharmony_ci	switch (opcode) {
155562306a36Sopenharmony_ci	case QLCNIC_RESPONSE_DESC:
155662306a36Sopenharmony_ci		qlcnic_handle_fw_message(desc_cnt, consumer, sds_ring);
155762306a36Sopenharmony_ci		break;
155862306a36Sopenharmony_ci	default:
155962306a36Sopenharmony_ci		ring = qlcnic_get_sts_type(sts_data0);
156062306a36Sopenharmony_ci		qlcnic_process_rcv_diag(adapter, ring, sts_data0);
156162306a36Sopenharmony_ci		break;
156262306a36Sopenharmony_ci	}
156362306a36Sopenharmony_ci
156462306a36Sopenharmony_ci	for (; desc_cnt > 0; desc_cnt--) {
156562306a36Sopenharmony_ci		desc = &sds_ring->desc_head[consumer];
156662306a36Sopenharmony_ci		desc->status_desc_data[0] = cpu_to_le64(STATUS_OWNER_PHANTOM);
156762306a36Sopenharmony_ci		consumer = get_next_index(consumer, sds_ring->num_desc);
156862306a36Sopenharmony_ci	}
156962306a36Sopenharmony_ci
157062306a36Sopenharmony_ci	sds_ring->consumer = consumer;
157162306a36Sopenharmony_ci	writel(consumer, sds_ring->crb_sts_consumer);
157262306a36Sopenharmony_ci}
157362306a36Sopenharmony_ci
157462306a36Sopenharmony_ciint qlcnic_82xx_napi_add(struct qlcnic_adapter *adapter,
157562306a36Sopenharmony_ci			 struct net_device *netdev)
157662306a36Sopenharmony_ci{
157762306a36Sopenharmony_ci	int ring;
157862306a36Sopenharmony_ci	struct qlcnic_host_sds_ring *sds_ring;
157962306a36Sopenharmony_ci	struct qlcnic_recv_context *recv_ctx = adapter->recv_ctx;
158062306a36Sopenharmony_ci	struct qlcnic_host_tx_ring *tx_ring;
158162306a36Sopenharmony_ci
158262306a36Sopenharmony_ci	if (qlcnic_alloc_sds_rings(recv_ctx, adapter->drv_sds_rings))
158362306a36Sopenharmony_ci		return -ENOMEM;
158462306a36Sopenharmony_ci
158562306a36Sopenharmony_ci	for (ring = 0; ring < adapter->drv_sds_rings; ring++) {
158662306a36Sopenharmony_ci		sds_ring = &recv_ctx->sds_rings[ring];
158762306a36Sopenharmony_ci		if (qlcnic_check_multi_tx(adapter) &&
158862306a36Sopenharmony_ci		    !adapter->ahw->diag_test) {
158962306a36Sopenharmony_ci			netif_napi_add(netdev, &sds_ring->napi,
159062306a36Sopenharmony_ci				       qlcnic_rx_poll);
159162306a36Sopenharmony_ci		} else {
159262306a36Sopenharmony_ci			if (ring == (adapter->drv_sds_rings - 1))
159362306a36Sopenharmony_ci				netif_napi_add(netdev, &sds_ring->napi,
159462306a36Sopenharmony_ci					       qlcnic_poll);
159562306a36Sopenharmony_ci			else
159662306a36Sopenharmony_ci				netif_napi_add(netdev, &sds_ring->napi,
159762306a36Sopenharmony_ci					       qlcnic_rx_poll);
159862306a36Sopenharmony_ci		}
159962306a36Sopenharmony_ci	}
160062306a36Sopenharmony_ci
160162306a36Sopenharmony_ci	if (qlcnic_alloc_tx_rings(adapter, netdev)) {
160262306a36Sopenharmony_ci		qlcnic_free_sds_rings(recv_ctx);
160362306a36Sopenharmony_ci		return -ENOMEM;
160462306a36Sopenharmony_ci	}
160562306a36Sopenharmony_ci
160662306a36Sopenharmony_ci	if (qlcnic_check_multi_tx(adapter) && !adapter->ahw->diag_test) {
160762306a36Sopenharmony_ci		for (ring = 0; ring < adapter->drv_tx_rings; ring++) {
160862306a36Sopenharmony_ci			tx_ring = &adapter->tx_ring[ring];
160962306a36Sopenharmony_ci			netif_napi_add_tx(netdev, &tx_ring->napi,
161062306a36Sopenharmony_ci					  qlcnic_tx_poll);
161162306a36Sopenharmony_ci		}
161262306a36Sopenharmony_ci	}
161362306a36Sopenharmony_ci
161462306a36Sopenharmony_ci	return 0;
161562306a36Sopenharmony_ci}
161662306a36Sopenharmony_ci
161762306a36Sopenharmony_civoid qlcnic_82xx_napi_del(struct qlcnic_adapter *adapter)
161862306a36Sopenharmony_ci{
161962306a36Sopenharmony_ci	int ring;
162062306a36Sopenharmony_ci	struct qlcnic_host_sds_ring *sds_ring;
162162306a36Sopenharmony_ci	struct qlcnic_recv_context *recv_ctx = adapter->recv_ctx;
162262306a36Sopenharmony_ci	struct qlcnic_host_tx_ring *tx_ring;
162362306a36Sopenharmony_ci
162462306a36Sopenharmony_ci	for (ring = 0; ring < adapter->drv_sds_rings; ring++) {
162562306a36Sopenharmony_ci		sds_ring = &recv_ctx->sds_rings[ring];
162662306a36Sopenharmony_ci		netif_napi_del(&sds_ring->napi);
162762306a36Sopenharmony_ci	}
162862306a36Sopenharmony_ci
162962306a36Sopenharmony_ci	qlcnic_free_sds_rings(adapter->recv_ctx);
163062306a36Sopenharmony_ci
163162306a36Sopenharmony_ci	if (qlcnic_check_multi_tx(adapter) && !adapter->ahw->diag_test) {
163262306a36Sopenharmony_ci		for (ring = 0; ring < adapter->drv_tx_rings; ring++) {
163362306a36Sopenharmony_ci			tx_ring = &adapter->tx_ring[ring];
163462306a36Sopenharmony_ci			netif_napi_del(&tx_ring->napi);
163562306a36Sopenharmony_ci		}
163662306a36Sopenharmony_ci	}
163762306a36Sopenharmony_ci
163862306a36Sopenharmony_ci	qlcnic_free_tx_rings(adapter);
163962306a36Sopenharmony_ci}
164062306a36Sopenharmony_ci
164162306a36Sopenharmony_civoid qlcnic_82xx_napi_enable(struct qlcnic_adapter *adapter)
164262306a36Sopenharmony_ci{
164362306a36Sopenharmony_ci	int ring;
164462306a36Sopenharmony_ci	struct qlcnic_host_sds_ring *sds_ring;
164562306a36Sopenharmony_ci	struct qlcnic_host_tx_ring *tx_ring;
164662306a36Sopenharmony_ci	struct qlcnic_recv_context *recv_ctx = adapter->recv_ctx;
164762306a36Sopenharmony_ci
164862306a36Sopenharmony_ci	if (adapter->is_up != QLCNIC_ADAPTER_UP_MAGIC)
164962306a36Sopenharmony_ci		return;
165062306a36Sopenharmony_ci
165162306a36Sopenharmony_ci	for (ring = 0; ring < adapter->drv_sds_rings; ring++) {
165262306a36Sopenharmony_ci		sds_ring = &recv_ctx->sds_rings[ring];
165362306a36Sopenharmony_ci		napi_enable(&sds_ring->napi);
165462306a36Sopenharmony_ci		qlcnic_enable_sds_intr(adapter, sds_ring);
165562306a36Sopenharmony_ci	}
165662306a36Sopenharmony_ci
165762306a36Sopenharmony_ci	if (qlcnic_check_multi_tx(adapter) &&
165862306a36Sopenharmony_ci	    (adapter->flags & QLCNIC_MSIX_ENABLED) &&
165962306a36Sopenharmony_ci	    !adapter->ahw->diag_test) {
166062306a36Sopenharmony_ci		for (ring = 0; ring < adapter->drv_tx_rings; ring++) {
166162306a36Sopenharmony_ci			tx_ring = &adapter->tx_ring[ring];
166262306a36Sopenharmony_ci			napi_enable(&tx_ring->napi);
166362306a36Sopenharmony_ci			qlcnic_enable_tx_intr(adapter, tx_ring);
166462306a36Sopenharmony_ci		}
166562306a36Sopenharmony_ci	}
166662306a36Sopenharmony_ci}
166762306a36Sopenharmony_ci
166862306a36Sopenharmony_civoid qlcnic_82xx_napi_disable(struct qlcnic_adapter *adapter)
166962306a36Sopenharmony_ci{
167062306a36Sopenharmony_ci	int ring;
167162306a36Sopenharmony_ci	struct qlcnic_host_sds_ring *sds_ring;
167262306a36Sopenharmony_ci	struct qlcnic_host_tx_ring *tx_ring;
167362306a36Sopenharmony_ci	struct qlcnic_recv_context *recv_ctx = adapter->recv_ctx;
167462306a36Sopenharmony_ci
167562306a36Sopenharmony_ci	if (adapter->is_up != QLCNIC_ADAPTER_UP_MAGIC)
167662306a36Sopenharmony_ci		return;
167762306a36Sopenharmony_ci
167862306a36Sopenharmony_ci	for (ring = 0; ring < adapter->drv_sds_rings; ring++) {
167962306a36Sopenharmony_ci		sds_ring = &recv_ctx->sds_rings[ring];
168062306a36Sopenharmony_ci		qlcnic_disable_sds_intr(adapter, sds_ring);
168162306a36Sopenharmony_ci		napi_synchronize(&sds_ring->napi);
168262306a36Sopenharmony_ci		napi_disable(&sds_ring->napi);
168362306a36Sopenharmony_ci	}
168462306a36Sopenharmony_ci
168562306a36Sopenharmony_ci	if ((adapter->flags & QLCNIC_MSIX_ENABLED) &&
168662306a36Sopenharmony_ci	    !adapter->ahw->diag_test &&
168762306a36Sopenharmony_ci	    qlcnic_check_multi_tx(adapter)) {
168862306a36Sopenharmony_ci		for (ring = 0; ring < adapter->drv_tx_rings; ring++) {
168962306a36Sopenharmony_ci			tx_ring = &adapter->tx_ring[ring];
169062306a36Sopenharmony_ci			qlcnic_disable_tx_intr(adapter, tx_ring);
169162306a36Sopenharmony_ci			napi_synchronize(&tx_ring->napi);
169262306a36Sopenharmony_ci			napi_disable(&tx_ring->napi);
169362306a36Sopenharmony_ci		}
169462306a36Sopenharmony_ci	}
169562306a36Sopenharmony_ci}
169662306a36Sopenharmony_ci
169762306a36Sopenharmony_ci#define QLC_83XX_NORMAL_LB_PKT	(1ULL << 36)
169862306a36Sopenharmony_ci#define QLC_83XX_LRO_LB_PKT	(1ULL << 46)
169962306a36Sopenharmony_ci
170062306a36Sopenharmony_cistatic inline int qlcnic_83xx_is_lb_pkt(u64 sts_data, int lro_pkt)
170162306a36Sopenharmony_ci{
170262306a36Sopenharmony_ci	if (lro_pkt)
170362306a36Sopenharmony_ci		return (sts_data & QLC_83XX_LRO_LB_PKT) ? 1 : 0;
170462306a36Sopenharmony_ci	else
170562306a36Sopenharmony_ci		return (sts_data & QLC_83XX_NORMAL_LB_PKT) ? 1 : 0;
170662306a36Sopenharmony_ci}
170762306a36Sopenharmony_ci
170862306a36Sopenharmony_ci#define QLCNIC_ENCAP_LENGTH_MASK	0x7f
170962306a36Sopenharmony_ci
171062306a36Sopenharmony_cistatic inline u8 qlcnic_encap_length(u64 sts_data)
171162306a36Sopenharmony_ci{
171262306a36Sopenharmony_ci	return sts_data & QLCNIC_ENCAP_LENGTH_MASK;
171362306a36Sopenharmony_ci}
171462306a36Sopenharmony_ci
171562306a36Sopenharmony_cistatic struct qlcnic_rx_buffer *
171662306a36Sopenharmony_ciqlcnic_83xx_process_rcv(struct qlcnic_adapter *adapter,
171762306a36Sopenharmony_ci			struct qlcnic_host_sds_ring *sds_ring,
171862306a36Sopenharmony_ci			u8 ring, u64 sts_data[])
171962306a36Sopenharmony_ci{
172062306a36Sopenharmony_ci	struct net_device *netdev = adapter->netdev;
172162306a36Sopenharmony_ci	struct qlcnic_recv_context *recv_ctx = adapter->recv_ctx;
172262306a36Sopenharmony_ci	struct qlcnic_rx_buffer *buffer;
172362306a36Sopenharmony_ci	struct sk_buff *skb;
172462306a36Sopenharmony_ci	struct qlcnic_host_rds_ring *rds_ring;
172562306a36Sopenharmony_ci	int index, length, cksum, is_lb_pkt;
172662306a36Sopenharmony_ci	u16 vid = 0xffff;
172762306a36Sopenharmony_ci	int err;
172862306a36Sopenharmony_ci
172962306a36Sopenharmony_ci	if (unlikely(ring >= adapter->max_rds_rings))
173062306a36Sopenharmony_ci		return NULL;
173162306a36Sopenharmony_ci
173262306a36Sopenharmony_ci	rds_ring = &recv_ctx->rds_rings[ring];
173362306a36Sopenharmony_ci
173462306a36Sopenharmony_ci	index = qlcnic_83xx_hndl(sts_data[0]);
173562306a36Sopenharmony_ci	if (unlikely(index >= rds_ring->num_desc))
173662306a36Sopenharmony_ci		return NULL;
173762306a36Sopenharmony_ci
173862306a36Sopenharmony_ci	buffer = &rds_ring->rx_buf_arr[index];
173962306a36Sopenharmony_ci	length = qlcnic_83xx_pktln(sts_data[0]);
174062306a36Sopenharmony_ci	cksum  = qlcnic_83xx_csum_status(sts_data[1]);
174162306a36Sopenharmony_ci	skb = qlcnic_process_rxbuf(adapter, rds_ring, index, cksum);
174262306a36Sopenharmony_ci	if (!skb)
174362306a36Sopenharmony_ci		return buffer;
174462306a36Sopenharmony_ci
174562306a36Sopenharmony_ci	if (length > rds_ring->skb_size)
174662306a36Sopenharmony_ci		skb_put(skb, rds_ring->skb_size);
174762306a36Sopenharmony_ci	else
174862306a36Sopenharmony_ci		skb_put(skb, length);
174962306a36Sopenharmony_ci
175062306a36Sopenharmony_ci	err = qlcnic_check_rx_tagging(adapter, skb, &vid);
175162306a36Sopenharmony_ci
175262306a36Sopenharmony_ci	if (adapter->rx_mac_learn) {
175362306a36Sopenharmony_ci		is_lb_pkt = qlcnic_83xx_is_lb_pkt(sts_data[1], 0);
175462306a36Sopenharmony_ci		qlcnic_add_lb_filter(adapter, skb, is_lb_pkt, vid);
175562306a36Sopenharmony_ci	}
175662306a36Sopenharmony_ci
175762306a36Sopenharmony_ci	if (unlikely(err)) {
175862306a36Sopenharmony_ci		adapter->stats.rxdropped++;
175962306a36Sopenharmony_ci		dev_kfree_skb(skb);
176062306a36Sopenharmony_ci		return buffer;
176162306a36Sopenharmony_ci	}
176262306a36Sopenharmony_ci
176362306a36Sopenharmony_ci	skb->protocol = eth_type_trans(skb, netdev);
176462306a36Sopenharmony_ci
176562306a36Sopenharmony_ci	if (qlcnic_encap_length(sts_data[1]) &&
176662306a36Sopenharmony_ci	    skb->ip_summed == CHECKSUM_UNNECESSARY) {
176762306a36Sopenharmony_ci		skb->csum_level = 1;
176862306a36Sopenharmony_ci		adapter->stats.encap_rx_csummed++;
176962306a36Sopenharmony_ci	}
177062306a36Sopenharmony_ci
177162306a36Sopenharmony_ci	if (vid != 0xffff)
177262306a36Sopenharmony_ci		__vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vid);
177362306a36Sopenharmony_ci
177462306a36Sopenharmony_ci	napi_gro_receive(&sds_ring->napi, skb);
177562306a36Sopenharmony_ci
177662306a36Sopenharmony_ci	adapter->stats.rx_pkts++;
177762306a36Sopenharmony_ci	adapter->stats.rxbytes += length;
177862306a36Sopenharmony_ci
177962306a36Sopenharmony_ci	return buffer;
178062306a36Sopenharmony_ci}
178162306a36Sopenharmony_ci
178262306a36Sopenharmony_cistatic struct qlcnic_rx_buffer *
178362306a36Sopenharmony_ciqlcnic_83xx_process_lro(struct qlcnic_adapter *adapter,
178462306a36Sopenharmony_ci			u8 ring, u64 sts_data[])
178562306a36Sopenharmony_ci{
178662306a36Sopenharmony_ci	struct net_device *netdev = adapter->netdev;
178762306a36Sopenharmony_ci	struct qlcnic_recv_context *recv_ctx = adapter->recv_ctx;
178862306a36Sopenharmony_ci	struct qlcnic_rx_buffer *buffer;
178962306a36Sopenharmony_ci	struct sk_buff *skb;
179062306a36Sopenharmony_ci	struct qlcnic_host_rds_ring *rds_ring;
179162306a36Sopenharmony_ci	struct iphdr *iph;
179262306a36Sopenharmony_ci	struct ipv6hdr *ipv6h;
179362306a36Sopenharmony_ci	struct tcphdr *th;
179462306a36Sopenharmony_ci	bool push;
179562306a36Sopenharmony_ci	int l2_hdr_offset, l4_hdr_offset;
179662306a36Sopenharmony_ci	int index, is_lb_pkt;
179762306a36Sopenharmony_ci	u16 lro_length, length, data_offset, gso_size;
179862306a36Sopenharmony_ci	u16 vid = 0xffff;
179962306a36Sopenharmony_ci	int err;
180062306a36Sopenharmony_ci
180162306a36Sopenharmony_ci	if (unlikely(ring >= adapter->max_rds_rings))
180262306a36Sopenharmony_ci		return NULL;
180362306a36Sopenharmony_ci
180462306a36Sopenharmony_ci	rds_ring = &recv_ctx->rds_rings[ring];
180562306a36Sopenharmony_ci
180662306a36Sopenharmony_ci	index = qlcnic_83xx_hndl(sts_data[0]);
180762306a36Sopenharmony_ci	if (unlikely(index >= rds_ring->num_desc))
180862306a36Sopenharmony_ci		return NULL;
180962306a36Sopenharmony_ci
181062306a36Sopenharmony_ci	buffer = &rds_ring->rx_buf_arr[index];
181162306a36Sopenharmony_ci
181262306a36Sopenharmony_ci	lro_length = qlcnic_83xx_lro_pktln(sts_data[0]);
181362306a36Sopenharmony_ci	l2_hdr_offset = qlcnic_83xx_l2_hdr_off(sts_data[1]);
181462306a36Sopenharmony_ci	l4_hdr_offset = qlcnic_83xx_l4_hdr_off(sts_data[1]);
181562306a36Sopenharmony_ci	push = qlcnic_83xx_is_psh_bit(sts_data[1]);
181662306a36Sopenharmony_ci
181762306a36Sopenharmony_ci	skb = qlcnic_process_rxbuf(adapter, rds_ring, index, STATUS_CKSUM_OK);
181862306a36Sopenharmony_ci	if (!skb)
181962306a36Sopenharmony_ci		return buffer;
182062306a36Sopenharmony_ci
182162306a36Sopenharmony_ci	if (qlcnic_83xx_is_tstamp(sts_data[1]))
182262306a36Sopenharmony_ci		data_offset = l4_hdr_offset + QLCNIC_TCP_TS_HDR_SIZE;
182362306a36Sopenharmony_ci	else
182462306a36Sopenharmony_ci		data_offset = l4_hdr_offset + QLCNIC_TCP_HDR_SIZE;
182562306a36Sopenharmony_ci
182662306a36Sopenharmony_ci	skb_put(skb, lro_length + data_offset);
182762306a36Sopenharmony_ci	skb_pull(skb, l2_hdr_offset);
182862306a36Sopenharmony_ci
182962306a36Sopenharmony_ci	err = qlcnic_check_rx_tagging(adapter, skb, &vid);
183062306a36Sopenharmony_ci
183162306a36Sopenharmony_ci	if (adapter->rx_mac_learn) {
183262306a36Sopenharmony_ci		is_lb_pkt = qlcnic_83xx_is_lb_pkt(sts_data[1], 1);
183362306a36Sopenharmony_ci		qlcnic_add_lb_filter(adapter, skb, is_lb_pkt, vid);
183462306a36Sopenharmony_ci	}
183562306a36Sopenharmony_ci
183662306a36Sopenharmony_ci	if (unlikely(err)) {
183762306a36Sopenharmony_ci		adapter->stats.rxdropped++;
183862306a36Sopenharmony_ci		dev_kfree_skb(skb);
183962306a36Sopenharmony_ci		return buffer;
184062306a36Sopenharmony_ci	}
184162306a36Sopenharmony_ci
184262306a36Sopenharmony_ci	skb->protocol = eth_type_trans(skb, netdev);
184362306a36Sopenharmony_ci	if (ntohs(skb->protocol) == ETH_P_IPV6) {
184462306a36Sopenharmony_ci		ipv6h = (struct ipv6hdr *)skb->data;
184562306a36Sopenharmony_ci		th = (struct tcphdr *)(skb->data + sizeof(struct ipv6hdr));
184662306a36Sopenharmony_ci
184762306a36Sopenharmony_ci		length = (th->doff << 2) + lro_length;
184862306a36Sopenharmony_ci		ipv6h->payload_len = htons(length);
184962306a36Sopenharmony_ci	} else {
185062306a36Sopenharmony_ci		iph = (struct iphdr *)skb->data;
185162306a36Sopenharmony_ci		th = (struct tcphdr *)(skb->data + (iph->ihl << 2));
185262306a36Sopenharmony_ci		length = (iph->ihl << 2) + (th->doff << 2) + lro_length;
185362306a36Sopenharmony_ci		csum_replace2(&iph->check, iph->tot_len, htons(length));
185462306a36Sopenharmony_ci		iph->tot_len = htons(length);
185562306a36Sopenharmony_ci	}
185662306a36Sopenharmony_ci
185762306a36Sopenharmony_ci	th->psh = push;
185862306a36Sopenharmony_ci	length = skb->len;
185962306a36Sopenharmony_ci
186062306a36Sopenharmony_ci	if (adapter->flags & QLCNIC_FW_LRO_MSS_CAP) {
186162306a36Sopenharmony_ci		gso_size = qlcnic_83xx_get_lro_sts_mss(sts_data[0]);
186262306a36Sopenharmony_ci		skb_shinfo(skb)->gso_size = gso_size;
186362306a36Sopenharmony_ci		if (skb->protocol == htons(ETH_P_IPV6))
186462306a36Sopenharmony_ci			skb_shinfo(skb)->gso_type = SKB_GSO_TCPV6;
186562306a36Sopenharmony_ci		else
186662306a36Sopenharmony_ci			skb_shinfo(skb)->gso_type = SKB_GSO_TCPV4;
186762306a36Sopenharmony_ci	}
186862306a36Sopenharmony_ci
186962306a36Sopenharmony_ci	if (vid != 0xffff)
187062306a36Sopenharmony_ci		__vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vid);
187162306a36Sopenharmony_ci
187262306a36Sopenharmony_ci	netif_receive_skb(skb);
187362306a36Sopenharmony_ci
187462306a36Sopenharmony_ci	adapter->stats.lro_pkts++;
187562306a36Sopenharmony_ci	adapter->stats.lrobytes += length;
187662306a36Sopenharmony_ci	return buffer;
187762306a36Sopenharmony_ci}
187862306a36Sopenharmony_ci
187962306a36Sopenharmony_cistatic int qlcnic_83xx_process_rcv_ring(struct qlcnic_host_sds_ring *sds_ring,
188062306a36Sopenharmony_ci					int max)
188162306a36Sopenharmony_ci{
188262306a36Sopenharmony_ci	struct qlcnic_host_rds_ring *rds_ring;
188362306a36Sopenharmony_ci	struct qlcnic_adapter *adapter = sds_ring->adapter;
188462306a36Sopenharmony_ci	struct list_head *cur;
188562306a36Sopenharmony_ci	struct status_desc *desc;
188662306a36Sopenharmony_ci	struct qlcnic_rx_buffer *rxbuf = NULL;
188762306a36Sopenharmony_ci	u8 ring;
188862306a36Sopenharmony_ci	u64 sts_data[2];
188962306a36Sopenharmony_ci	int count = 0, opcode;
189062306a36Sopenharmony_ci	u32 consumer = sds_ring->consumer;
189162306a36Sopenharmony_ci
189262306a36Sopenharmony_ci	while (count < max) {
189362306a36Sopenharmony_ci		desc = &sds_ring->desc_head[consumer];
189462306a36Sopenharmony_ci		sts_data[1] = le64_to_cpu(desc->status_desc_data[1]);
189562306a36Sopenharmony_ci		opcode = qlcnic_83xx_opcode(sts_data[1]);
189662306a36Sopenharmony_ci		if (!opcode)
189762306a36Sopenharmony_ci			break;
189862306a36Sopenharmony_ci		sts_data[0] = le64_to_cpu(desc->status_desc_data[0]);
189962306a36Sopenharmony_ci		ring = QLCNIC_FETCH_RING_ID(sts_data[0]);
190062306a36Sopenharmony_ci
190162306a36Sopenharmony_ci		switch (opcode) {
190262306a36Sopenharmony_ci		case QLC_83XX_REG_DESC:
190362306a36Sopenharmony_ci			rxbuf = qlcnic_83xx_process_rcv(adapter, sds_ring,
190462306a36Sopenharmony_ci							ring, sts_data);
190562306a36Sopenharmony_ci			break;
190662306a36Sopenharmony_ci		case QLC_83XX_LRO_DESC:
190762306a36Sopenharmony_ci			rxbuf = qlcnic_83xx_process_lro(adapter, ring,
190862306a36Sopenharmony_ci							sts_data);
190962306a36Sopenharmony_ci			break;
191062306a36Sopenharmony_ci		default:
191162306a36Sopenharmony_ci			dev_info(&adapter->pdev->dev,
191262306a36Sopenharmony_ci				 "Unknown opcode: 0x%x\n", opcode);
191362306a36Sopenharmony_ci			goto skip;
191462306a36Sopenharmony_ci		}
191562306a36Sopenharmony_ci
191662306a36Sopenharmony_ci		if (likely(rxbuf))
191762306a36Sopenharmony_ci			list_add_tail(&rxbuf->list, &sds_ring->free_list[ring]);
191862306a36Sopenharmony_ci		else
191962306a36Sopenharmony_ci			adapter->stats.null_rxbuf++;
192062306a36Sopenharmony_ciskip:
192162306a36Sopenharmony_ci		desc = &sds_ring->desc_head[consumer];
192262306a36Sopenharmony_ci		/* Reset the descriptor */
192362306a36Sopenharmony_ci		desc->status_desc_data[1] = 0;
192462306a36Sopenharmony_ci		consumer = get_next_index(consumer, sds_ring->num_desc);
192562306a36Sopenharmony_ci		count++;
192662306a36Sopenharmony_ci	}
192762306a36Sopenharmony_ci	for (ring = 0; ring < adapter->max_rds_rings; ring++) {
192862306a36Sopenharmony_ci		rds_ring = &adapter->recv_ctx->rds_rings[ring];
192962306a36Sopenharmony_ci		if (!list_empty(&sds_ring->free_list[ring])) {
193062306a36Sopenharmony_ci			list_for_each(cur, &sds_ring->free_list[ring]) {
193162306a36Sopenharmony_ci				rxbuf = list_entry(cur, struct qlcnic_rx_buffer,
193262306a36Sopenharmony_ci						   list);
193362306a36Sopenharmony_ci				qlcnic_alloc_rx_skb(adapter, rds_ring, rxbuf);
193462306a36Sopenharmony_ci			}
193562306a36Sopenharmony_ci			spin_lock(&rds_ring->lock);
193662306a36Sopenharmony_ci			list_splice_tail_init(&sds_ring->free_list[ring],
193762306a36Sopenharmony_ci					      &rds_ring->free_list);
193862306a36Sopenharmony_ci			spin_unlock(&rds_ring->lock);
193962306a36Sopenharmony_ci		}
194062306a36Sopenharmony_ci		qlcnic_post_rx_buffers_nodb(adapter, rds_ring, ring);
194162306a36Sopenharmony_ci	}
194262306a36Sopenharmony_ci	if (count) {
194362306a36Sopenharmony_ci		sds_ring->consumer = consumer;
194462306a36Sopenharmony_ci		writel(consumer, sds_ring->crb_sts_consumer);
194562306a36Sopenharmony_ci	}
194662306a36Sopenharmony_ci	return count;
194762306a36Sopenharmony_ci}
194862306a36Sopenharmony_ci
194962306a36Sopenharmony_cistatic int qlcnic_83xx_msix_sriov_vf_poll(struct napi_struct *napi, int budget)
195062306a36Sopenharmony_ci{
195162306a36Sopenharmony_ci	int tx_complete;
195262306a36Sopenharmony_ci	int work_done;
195362306a36Sopenharmony_ci	struct qlcnic_host_sds_ring *sds_ring;
195462306a36Sopenharmony_ci	struct qlcnic_adapter *adapter;
195562306a36Sopenharmony_ci	struct qlcnic_host_tx_ring *tx_ring;
195662306a36Sopenharmony_ci
195762306a36Sopenharmony_ci	sds_ring = container_of(napi, struct qlcnic_host_sds_ring, napi);
195862306a36Sopenharmony_ci	adapter = sds_ring->adapter;
195962306a36Sopenharmony_ci	/* tx ring count = 1 */
196062306a36Sopenharmony_ci	tx_ring = adapter->tx_ring;
196162306a36Sopenharmony_ci
196262306a36Sopenharmony_ci	tx_complete = qlcnic_process_cmd_ring(adapter, tx_ring, budget);
196362306a36Sopenharmony_ci	work_done = qlcnic_83xx_process_rcv_ring(sds_ring, budget);
196462306a36Sopenharmony_ci
196562306a36Sopenharmony_ci	/* Check if we need a repoll */
196662306a36Sopenharmony_ci	if (!tx_complete)
196762306a36Sopenharmony_ci		work_done = budget;
196862306a36Sopenharmony_ci
196962306a36Sopenharmony_ci	if (work_done < budget) {
197062306a36Sopenharmony_ci		napi_complete_done(&sds_ring->napi, work_done);
197162306a36Sopenharmony_ci		qlcnic_enable_sds_intr(adapter, sds_ring);
197262306a36Sopenharmony_ci	}
197362306a36Sopenharmony_ci
197462306a36Sopenharmony_ci	return work_done;
197562306a36Sopenharmony_ci}
197662306a36Sopenharmony_ci
197762306a36Sopenharmony_cistatic int qlcnic_83xx_poll(struct napi_struct *napi, int budget)
197862306a36Sopenharmony_ci{
197962306a36Sopenharmony_ci	int tx_complete;
198062306a36Sopenharmony_ci	int work_done;
198162306a36Sopenharmony_ci	struct qlcnic_host_sds_ring *sds_ring;
198262306a36Sopenharmony_ci	struct qlcnic_adapter *adapter;
198362306a36Sopenharmony_ci	struct qlcnic_host_tx_ring *tx_ring;
198462306a36Sopenharmony_ci
198562306a36Sopenharmony_ci	sds_ring = container_of(napi, struct qlcnic_host_sds_ring, napi);
198662306a36Sopenharmony_ci	adapter = sds_ring->adapter;
198762306a36Sopenharmony_ci	/* tx ring count = 1 */
198862306a36Sopenharmony_ci	tx_ring = adapter->tx_ring;
198962306a36Sopenharmony_ci
199062306a36Sopenharmony_ci	tx_complete = qlcnic_process_cmd_ring(adapter, tx_ring, budget);
199162306a36Sopenharmony_ci	work_done = qlcnic_83xx_process_rcv_ring(sds_ring, budget);
199262306a36Sopenharmony_ci
199362306a36Sopenharmony_ci	/* Check if we need a repoll */
199462306a36Sopenharmony_ci	if (!tx_complete)
199562306a36Sopenharmony_ci		work_done = budget;
199662306a36Sopenharmony_ci
199762306a36Sopenharmony_ci	if (work_done < budget) {
199862306a36Sopenharmony_ci		napi_complete_done(&sds_ring->napi, work_done);
199962306a36Sopenharmony_ci		qlcnic_enable_sds_intr(adapter, sds_ring);
200062306a36Sopenharmony_ci	}
200162306a36Sopenharmony_ci
200262306a36Sopenharmony_ci	return work_done;
200362306a36Sopenharmony_ci}
200462306a36Sopenharmony_ci
200562306a36Sopenharmony_cistatic int qlcnic_83xx_msix_tx_poll(struct napi_struct *napi, int budget)
200662306a36Sopenharmony_ci{
200762306a36Sopenharmony_ci	int work_done;
200862306a36Sopenharmony_ci	struct qlcnic_host_tx_ring *tx_ring;
200962306a36Sopenharmony_ci	struct qlcnic_adapter *adapter;
201062306a36Sopenharmony_ci
201162306a36Sopenharmony_ci	tx_ring = container_of(napi, struct qlcnic_host_tx_ring, napi);
201262306a36Sopenharmony_ci	adapter = tx_ring->adapter;
201362306a36Sopenharmony_ci	work_done = qlcnic_process_cmd_ring(adapter, tx_ring, budget);
201462306a36Sopenharmony_ci	if (work_done) {
201562306a36Sopenharmony_ci		napi_complete(&tx_ring->napi);
201662306a36Sopenharmony_ci		if (test_bit(__QLCNIC_DEV_UP , &adapter->state))
201762306a36Sopenharmony_ci			qlcnic_enable_tx_intr(adapter, tx_ring);
201862306a36Sopenharmony_ci	} else {
201962306a36Sopenharmony_ci		/* need a repoll */
202062306a36Sopenharmony_ci		work_done = budget;
202162306a36Sopenharmony_ci	}
202262306a36Sopenharmony_ci
202362306a36Sopenharmony_ci	return work_done;
202462306a36Sopenharmony_ci}
202562306a36Sopenharmony_ci
202662306a36Sopenharmony_cistatic int qlcnic_83xx_rx_poll(struct napi_struct *napi, int budget)
202762306a36Sopenharmony_ci{
202862306a36Sopenharmony_ci	int work_done;
202962306a36Sopenharmony_ci	struct qlcnic_host_sds_ring *sds_ring;
203062306a36Sopenharmony_ci	struct qlcnic_adapter *adapter;
203162306a36Sopenharmony_ci
203262306a36Sopenharmony_ci	sds_ring = container_of(napi, struct qlcnic_host_sds_ring, napi);
203362306a36Sopenharmony_ci	adapter = sds_ring->adapter;
203462306a36Sopenharmony_ci	work_done = qlcnic_83xx_process_rcv_ring(sds_ring, budget);
203562306a36Sopenharmony_ci	if (work_done < budget) {
203662306a36Sopenharmony_ci		napi_complete_done(&sds_ring->napi, work_done);
203762306a36Sopenharmony_ci		if (test_bit(__QLCNIC_DEV_UP, &adapter->state))
203862306a36Sopenharmony_ci			qlcnic_enable_sds_intr(adapter, sds_ring);
203962306a36Sopenharmony_ci	}
204062306a36Sopenharmony_ci
204162306a36Sopenharmony_ci	return work_done;
204262306a36Sopenharmony_ci}
204362306a36Sopenharmony_ci
204462306a36Sopenharmony_civoid qlcnic_83xx_napi_enable(struct qlcnic_adapter *adapter)
204562306a36Sopenharmony_ci{
204662306a36Sopenharmony_ci	int ring;
204762306a36Sopenharmony_ci	struct qlcnic_host_sds_ring *sds_ring;
204862306a36Sopenharmony_ci	struct qlcnic_host_tx_ring *tx_ring;
204962306a36Sopenharmony_ci	struct qlcnic_recv_context *recv_ctx = adapter->recv_ctx;
205062306a36Sopenharmony_ci
205162306a36Sopenharmony_ci	if (adapter->is_up != QLCNIC_ADAPTER_UP_MAGIC)
205262306a36Sopenharmony_ci		return;
205362306a36Sopenharmony_ci
205462306a36Sopenharmony_ci	for (ring = 0; ring < adapter->drv_sds_rings; ring++) {
205562306a36Sopenharmony_ci		sds_ring = &recv_ctx->sds_rings[ring];
205662306a36Sopenharmony_ci		napi_enable(&sds_ring->napi);
205762306a36Sopenharmony_ci		if (adapter->flags & QLCNIC_MSIX_ENABLED)
205862306a36Sopenharmony_ci			qlcnic_enable_sds_intr(adapter, sds_ring);
205962306a36Sopenharmony_ci	}
206062306a36Sopenharmony_ci
206162306a36Sopenharmony_ci	if ((adapter->flags & QLCNIC_MSIX_ENABLED) &&
206262306a36Sopenharmony_ci	    !(adapter->flags & QLCNIC_TX_INTR_SHARED)) {
206362306a36Sopenharmony_ci		for (ring = 0; ring < adapter->drv_tx_rings; ring++) {
206462306a36Sopenharmony_ci			tx_ring = &adapter->tx_ring[ring];
206562306a36Sopenharmony_ci			napi_enable(&tx_ring->napi);
206662306a36Sopenharmony_ci			qlcnic_enable_tx_intr(adapter, tx_ring);
206762306a36Sopenharmony_ci		}
206862306a36Sopenharmony_ci	}
206962306a36Sopenharmony_ci}
207062306a36Sopenharmony_ci
207162306a36Sopenharmony_civoid qlcnic_83xx_napi_disable(struct qlcnic_adapter *adapter)
207262306a36Sopenharmony_ci{
207362306a36Sopenharmony_ci	int ring;
207462306a36Sopenharmony_ci	struct qlcnic_host_sds_ring *sds_ring;
207562306a36Sopenharmony_ci	struct qlcnic_recv_context *recv_ctx = adapter->recv_ctx;
207662306a36Sopenharmony_ci	struct qlcnic_host_tx_ring *tx_ring;
207762306a36Sopenharmony_ci
207862306a36Sopenharmony_ci	if (adapter->is_up != QLCNIC_ADAPTER_UP_MAGIC)
207962306a36Sopenharmony_ci		return;
208062306a36Sopenharmony_ci
208162306a36Sopenharmony_ci	for (ring = 0; ring < adapter->drv_sds_rings; ring++) {
208262306a36Sopenharmony_ci		sds_ring = &recv_ctx->sds_rings[ring];
208362306a36Sopenharmony_ci		if (adapter->flags & QLCNIC_MSIX_ENABLED)
208462306a36Sopenharmony_ci			qlcnic_disable_sds_intr(adapter, sds_ring);
208562306a36Sopenharmony_ci		napi_synchronize(&sds_ring->napi);
208662306a36Sopenharmony_ci		napi_disable(&sds_ring->napi);
208762306a36Sopenharmony_ci	}
208862306a36Sopenharmony_ci
208962306a36Sopenharmony_ci	if ((adapter->flags & QLCNIC_MSIX_ENABLED) &&
209062306a36Sopenharmony_ci	    !(adapter->flags & QLCNIC_TX_INTR_SHARED)) {
209162306a36Sopenharmony_ci		for (ring = 0; ring < adapter->drv_tx_rings; ring++) {
209262306a36Sopenharmony_ci			tx_ring = &adapter->tx_ring[ring];
209362306a36Sopenharmony_ci			qlcnic_disable_tx_intr(adapter, tx_ring);
209462306a36Sopenharmony_ci			napi_synchronize(&tx_ring->napi);
209562306a36Sopenharmony_ci			napi_disable(&tx_ring->napi);
209662306a36Sopenharmony_ci		}
209762306a36Sopenharmony_ci	}
209862306a36Sopenharmony_ci}
209962306a36Sopenharmony_ci
210062306a36Sopenharmony_ciint qlcnic_83xx_napi_add(struct qlcnic_adapter *adapter,
210162306a36Sopenharmony_ci			 struct net_device *netdev)
210262306a36Sopenharmony_ci{
210362306a36Sopenharmony_ci	int ring;
210462306a36Sopenharmony_ci	struct qlcnic_host_sds_ring *sds_ring;
210562306a36Sopenharmony_ci	struct qlcnic_host_tx_ring *tx_ring;
210662306a36Sopenharmony_ci	struct qlcnic_recv_context *recv_ctx = adapter->recv_ctx;
210762306a36Sopenharmony_ci
210862306a36Sopenharmony_ci	if (qlcnic_alloc_sds_rings(recv_ctx, adapter->drv_sds_rings))
210962306a36Sopenharmony_ci		return -ENOMEM;
211062306a36Sopenharmony_ci
211162306a36Sopenharmony_ci	for (ring = 0; ring < adapter->drv_sds_rings; ring++) {
211262306a36Sopenharmony_ci		sds_ring = &recv_ctx->sds_rings[ring];
211362306a36Sopenharmony_ci		if (adapter->flags & QLCNIC_MSIX_ENABLED) {
211462306a36Sopenharmony_ci			if (!(adapter->flags & QLCNIC_TX_INTR_SHARED))
211562306a36Sopenharmony_ci				netif_napi_add(netdev, &sds_ring->napi,
211662306a36Sopenharmony_ci					       qlcnic_83xx_rx_poll);
211762306a36Sopenharmony_ci			else
211862306a36Sopenharmony_ci				netif_napi_add(netdev, &sds_ring->napi,
211962306a36Sopenharmony_ci					       qlcnic_83xx_msix_sriov_vf_poll);
212062306a36Sopenharmony_ci
212162306a36Sopenharmony_ci		} else {
212262306a36Sopenharmony_ci			netif_napi_add(netdev, &sds_ring->napi,
212362306a36Sopenharmony_ci				       qlcnic_83xx_poll);
212462306a36Sopenharmony_ci		}
212562306a36Sopenharmony_ci	}
212662306a36Sopenharmony_ci
212762306a36Sopenharmony_ci	if (qlcnic_alloc_tx_rings(adapter, netdev)) {
212862306a36Sopenharmony_ci		qlcnic_free_sds_rings(recv_ctx);
212962306a36Sopenharmony_ci		return -ENOMEM;
213062306a36Sopenharmony_ci	}
213162306a36Sopenharmony_ci
213262306a36Sopenharmony_ci	if ((adapter->flags & QLCNIC_MSIX_ENABLED) &&
213362306a36Sopenharmony_ci	    !(adapter->flags & QLCNIC_TX_INTR_SHARED)) {
213462306a36Sopenharmony_ci		for (ring = 0; ring < adapter->drv_tx_rings; ring++) {
213562306a36Sopenharmony_ci			tx_ring = &adapter->tx_ring[ring];
213662306a36Sopenharmony_ci			netif_napi_add_tx(netdev, &tx_ring->napi,
213762306a36Sopenharmony_ci					  qlcnic_83xx_msix_tx_poll);
213862306a36Sopenharmony_ci		}
213962306a36Sopenharmony_ci	}
214062306a36Sopenharmony_ci
214162306a36Sopenharmony_ci	return 0;
214262306a36Sopenharmony_ci}
214362306a36Sopenharmony_ci
214462306a36Sopenharmony_civoid qlcnic_83xx_napi_del(struct qlcnic_adapter *adapter)
214562306a36Sopenharmony_ci{
214662306a36Sopenharmony_ci	int ring;
214762306a36Sopenharmony_ci	struct qlcnic_host_sds_ring *sds_ring;
214862306a36Sopenharmony_ci	struct qlcnic_recv_context *recv_ctx = adapter->recv_ctx;
214962306a36Sopenharmony_ci	struct qlcnic_host_tx_ring *tx_ring;
215062306a36Sopenharmony_ci
215162306a36Sopenharmony_ci	for (ring = 0; ring < adapter->drv_sds_rings; ring++) {
215262306a36Sopenharmony_ci		sds_ring = &recv_ctx->sds_rings[ring];
215362306a36Sopenharmony_ci		netif_napi_del(&sds_ring->napi);
215462306a36Sopenharmony_ci	}
215562306a36Sopenharmony_ci
215662306a36Sopenharmony_ci	qlcnic_free_sds_rings(adapter->recv_ctx);
215762306a36Sopenharmony_ci
215862306a36Sopenharmony_ci	if ((adapter->flags & QLCNIC_MSIX_ENABLED) &&
215962306a36Sopenharmony_ci	    !(adapter->flags & QLCNIC_TX_INTR_SHARED)) {
216062306a36Sopenharmony_ci		for (ring = 0; ring < adapter->drv_tx_rings; ring++) {
216162306a36Sopenharmony_ci			tx_ring = &adapter->tx_ring[ring];
216262306a36Sopenharmony_ci			netif_napi_del(&tx_ring->napi);
216362306a36Sopenharmony_ci		}
216462306a36Sopenharmony_ci	}
216562306a36Sopenharmony_ci
216662306a36Sopenharmony_ci	qlcnic_free_tx_rings(adapter);
216762306a36Sopenharmony_ci}
216862306a36Sopenharmony_ci
216962306a36Sopenharmony_cistatic void qlcnic_83xx_process_rcv_diag(struct qlcnic_adapter *adapter,
217062306a36Sopenharmony_ci					 int ring, u64 sts_data[])
217162306a36Sopenharmony_ci{
217262306a36Sopenharmony_ci	struct qlcnic_recv_context *recv_ctx = adapter->recv_ctx;
217362306a36Sopenharmony_ci	struct sk_buff *skb;
217462306a36Sopenharmony_ci	struct qlcnic_host_rds_ring *rds_ring;
217562306a36Sopenharmony_ci	int index, length;
217662306a36Sopenharmony_ci
217762306a36Sopenharmony_ci	if (unlikely(ring >= adapter->max_rds_rings))
217862306a36Sopenharmony_ci		return;
217962306a36Sopenharmony_ci
218062306a36Sopenharmony_ci	rds_ring = &recv_ctx->rds_rings[ring];
218162306a36Sopenharmony_ci	index = qlcnic_83xx_hndl(sts_data[0]);
218262306a36Sopenharmony_ci	if (unlikely(index >= rds_ring->num_desc))
218362306a36Sopenharmony_ci		return;
218462306a36Sopenharmony_ci
218562306a36Sopenharmony_ci	length = qlcnic_83xx_pktln(sts_data[0]);
218662306a36Sopenharmony_ci
218762306a36Sopenharmony_ci	skb = qlcnic_process_rxbuf(adapter, rds_ring, index, STATUS_CKSUM_OK);
218862306a36Sopenharmony_ci	if (!skb)
218962306a36Sopenharmony_ci		return;
219062306a36Sopenharmony_ci
219162306a36Sopenharmony_ci	if (length > rds_ring->skb_size)
219262306a36Sopenharmony_ci		skb_put(skb, rds_ring->skb_size);
219362306a36Sopenharmony_ci	else
219462306a36Sopenharmony_ci		skb_put(skb, length);
219562306a36Sopenharmony_ci
219662306a36Sopenharmony_ci	if (!qlcnic_check_loopback_buff(skb->data, adapter->mac_addr))
219762306a36Sopenharmony_ci		adapter->ahw->diag_cnt++;
219862306a36Sopenharmony_ci	else
219962306a36Sopenharmony_ci		dump_skb(skb, adapter);
220062306a36Sopenharmony_ci
220162306a36Sopenharmony_ci	dev_kfree_skb_any(skb);
220262306a36Sopenharmony_ci	return;
220362306a36Sopenharmony_ci}
220462306a36Sopenharmony_ci
220562306a36Sopenharmony_civoid qlcnic_83xx_process_rcv_ring_diag(struct qlcnic_host_sds_ring *sds_ring)
220662306a36Sopenharmony_ci{
220762306a36Sopenharmony_ci	struct qlcnic_adapter *adapter = sds_ring->adapter;
220862306a36Sopenharmony_ci	struct status_desc *desc;
220962306a36Sopenharmony_ci	u64 sts_data[2];
221062306a36Sopenharmony_ci	int ring, opcode;
221162306a36Sopenharmony_ci	u32 consumer = sds_ring->consumer;
221262306a36Sopenharmony_ci
221362306a36Sopenharmony_ci	desc = &sds_ring->desc_head[consumer];
221462306a36Sopenharmony_ci	sts_data[0] = le64_to_cpu(desc->status_desc_data[0]);
221562306a36Sopenharmony_ci	sts_data[1] = le64_to_cpu(desc->status_desc_data[1]);
221662306a36Sopenharmony_ci	opcode = qlcnic_83xx_opcode(sts_data[1]);
221762306a36Sopenharmony_ci	if (!opcode)
221862306a36Sopenharmony_ci		return;
221962306a36Sopenharmony_ci
222062306a36Sopenharmony_ci	ring = QLCNIC_FETCH_RING_ID(sts_data[0]);
222162306a36Sopenharmony_ci	qlcnic_83xx_process_rcv_diag(adapter, ring, sts_data);
222262306a36Sopenharmony_ci	desc = &sds_ring->desc_head[consumer];
222362306a36Sopenharmony_ci	desc->status_desc_data[0] = cpu_to_le64(STATUS_OWNER_PHANTOM);
222462306a36Sopenharmony_ci	consumer = get_next_index(consumer, sds_ring->num_desc);
222562306a36Sopenharmony_ci	sds_ring->consumer = consumer;
222662306a36Sopenharmony_ci	writel(consumer, sds_ring->crb_sts_consumer);
222762306a36Sopenharmony_ci}
2228