18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (C) 2015 Cavium, Inc.
48c2ecf20Sopenharmony_ci */
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci#include <linux/pci.h>
78c2ecf20Sopenharmony_ci#include <linux/netdevice.h>
88c2ecf20Sopenharmony_ci#include <linux/ip.h>
98c2ecf20Sopenharmony_ci#include <linux/etherdevice.h>
108c2ecf20Sopenharmony_ci#include <linux/iommu.h>
118c2ecf20Sopenharmony_ci#include <net/ip.h>
128c2ecf20Sopenharmony_ci#include <net/tso.h>
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci#include "nic_reg.h"
158c2ecf20Sopenharmony_ci#include "nic.h"
168c2ecf20Sopenharmony_ci#include "q_struct.h"
178c2ecf20Sopenharmony_ci#include "nicvf_queues.h"
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_cistatic inline void nicvf_sq_add_gather_subdesc(struct snd_queue *sq, int qentry,
208c2ecf20Sopenharmony_ci					       int size, u64 data);
218c2ecf20Sopenharmony_cistatic void nicvf_get_page(struct nicvf *nic)
228c2ecf20Sopenharmony_ci{
238c2ecf20Sopenharmony_ci	if (!nic->rb_pageref || !nic->rb_page)
248c2ecf20Sopenharmony_ci		return;
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci	page_ref_add(nic->rb_page, nic->rb_pageref);
278c2ecf20Sopenharmony_ci	nic->rb_pageref = 0;
288c2ecf20Sopenharmony_ci}
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci/* Poll a register for a specific value */
318c2ecf20Sopenharmony_cistatic int nicvf_poll_reg(struct nicvf *nic, int qidx,
328c2ecf20Sopenharmony_ci			  u64 reg, int bit_pos, int bits, int val)
338c2ecf20Sopenharmony_ci{
348c2ecf20Sopenharmony_ci	u64 bit_mask;
358c2ecf20Sopenharmony_ci	u64 reg_val;
368c2ecf20Sopenharmony_ci	int timeout = 10;
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci	bit_mask = (1ULL << bits) - 1;
398c2ecf20Sopenharmony_ci	bit_mask = (bit_mask << bit_pos);
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci	while (timeout) {
428c2ecf20Sopenharmony_ci		reg_val = nicvf_queue_reg_read(nic, reg, qidx);
438c2ecf20Sopenharmony_ci		if (((reg_val & bit_mask) >> bit_pos) == val)
448c2ecf20Sopenharmony_ci			return 0;
458c2ecf20Sopenharmony_ci		usleep_range(1000, 2000);
468c2ecf20Sopenharmony_ci		timeout--;
478c2ecf20Sopenharmony_ci	}
488c2ecf20Sopenharmony_ci	netdev_err(nic->netdev, "Poll on reg 0x%llx failed\n", reg);
498c2ecf20Sopenharmony_ci	return 1;
508c2ecf20Sopenharmony_ci}
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci/* Allocate memory for a queue's descriptors */
538c2ecf20Sopenharmony_cistatic int nicvf_alloc_q_desc_mem(struct nicvf *nic, struct q_desc_mem *dmem,
548c2ecf20Sopenharmony_ci				  int q_len, int desc_size, int align_bytes)
558c2ecf20Sopenharmony_ci{
568c2ecf20Sopenharmony_ci	dmem->q_len = q_len;
578c2ecf20Sopenharmony_ci	dmem->size = (desc_size * q_len) + align_bytes;
588c2ecf20Sopenharmony_ci	/* Save address, need it while freeing */
598c2ecf20Sopenharmony_ci	dmem->unalign_base = dma_alloc_coherent(&nic->pdev->dev, dmem->size,
608c2ecf20Sopenharmony_ci						&dmem->dma, GFP_KERNEL);
618c2ecf20Sopenharmony_ci	if (!dmem->unalign_base)
628c2ecf20Sopenharmony_ci		return -ENOMEM;
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci	/* Align memory address for 'align_bytes' */
658c2ecf20Sopenharmony_ci	dmem->phys_base = NICVF_ALIGNED_ADDR((u64)dmem->dma, align_bytes);
668c2ecf20Sopenharmony_ci	dmem->base = dmem->unalign_base + (dmem->phys_base - dmem->dma);
678c2ecf20Sopenharmony_ci	return 0;
688c2ecf20Sopenharmony_ci}
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci/* Free queue's descriptor memory */
718c2ecf20Sopenharmony_cistatic void nicvf_free_q_desc_mem(struct nicvf *nic, struct q_desc_mem *dmem)
728c2ecf20Sopenharmony_ci{
738c2ecf20Sopenharmony_ci	if (!dmem)
748c2ecf20Sopenharmony_ci		return;
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci	dma_free_coherent(&nic->pdev->dev, dmem->size,
778c2ecf20Sopenharmony_ci			  dmem->unalign_base, dmem->dma);
788c2ecf20Sopenharmony_ci	dmem->unalign_base = NULL;
798c2ecf20Sopenharmony_ci	dmem->base = NULL;
808c2ecf20Sopenharmony_ci}
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci#define XDP_PAGE_REFCNT_REFILL 256
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci/* Allocate a new page or recycle one if possible
858c2ecf20Sopenharmony_ci *
868c2ecf20Sopenharmony_ci * We cannot optimize dma mapping here, since
878c2ecf20Sopenharmony_ci * 1. It's only one RBDR ring for 8 Rx queues.
888c2ecf20Sopenharmony_ci * 2. CQE_RX gives address of the buffer where pkt has been DMA'ed
898c2ecf20Sopenharmony_ci *    and not idx into RBDR ring, so can't refer to saved info.
908c2ecf20Sopenharmony_ci * 3. There are multiple receive buffers per page
918c2ecf20Sopenharmony_ci */
928c2ecf20Sopenharmony_cistatic inline struct pgcache *nicvf_alloc_page(struct nicvf *nic,
938c2ecf20Sopenharmony_ci					       struct rbdr *rbdr, gfp_t gfp)
948c2ecf20Sopenharmony_ci{
958c2ecf20Sopenharmony_ci	int ref_count;
968c2ecf20Sopenharmony_ci	struct page *page = NULL;
978c2ecf20Sopenharmony_ci	struct pgcache *pgcache, *next;
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	/* Check if page is already allocated */
1008c2ecf20Sopenharmony_ci	pgcache = &rbdr->pgcache[rbdr->pgidx];
1018c2ecf20Sopenharmony_ci	page = pgcache->page;
1028c2ecf20Sopenharmony_ci	/* Check if page can be recycled */
1038c2ecf20Sopenharmony_ci	if (page) {
1048c2ecf20Sopenharmony_ci		ref_count = page_ref_count(page);
1058c2ecf20Sopenharmony_ci		/* This page can be recycled if internal ref_count and page's
1068c2ecf20Sopenharmony_ci		 * ref_count are equal, indicating that the page has been used
1078c2ecf20Sopenharmony_ci		 * once for packet transmission. For non-XDP mode, internal
1088c2ecf20Sopenharmony_ci		 * ref_count is always '1'.
1098c2ecf20Sopenharmony_ci		 */
1108c2ecf20Sopenharmony_ci		if (rbdr->is_xdp) {
1118c2ecf20Sopenharmony_ci			if (ref_count == pgcache->ref_count)
1128c2ecf20Sopenharmony_ci				pgcache->ref_count--;
1138c2ecf20Sopenharmony_ci			else
1148c2ecf20Sopenharmony_ci				page = NULL;
1158c2ecf20Sopenharmony_ci		} else if (ref_count != 1) {
1168c2ecf20Sopenharmony_ci			page = NULL;
1178c2ecf20Sopenharmony_ci		}
1188c2ecf20Sopenharmony_ci	}
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	if (!page) {
1218c2ecf20Sopenharmony_ci		page = alloc_pages(gfp | __GFP_COMP | __GFP_NOWARN, 0);
1228c2ecf20Sopenharmony_ci		if (!page)
1238c2ecf20Sopenharmony_ci			return NULL;
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci		this_cpu_inc(nic->pnicvf->drv_stats->page_alloc);
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci		/* Check for space */
1288c2ecf20Sopenharmony_ci		if (rbdr->pgalloc >= rbdr->pgcnt) {
1298c2ecf20Sopenharmony_ci			/* Page can still be used */
1308c2ecf20Sopenharmony_ci			nic->rb_page = page;
1318c2ecf20Sopenharmony_ci			return NULL;
1328c2ecf20Sopenharmony_ci		}
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci		/* Save the page in page cache */
1358c2ecf20Sopenharmony_ci		pgcache->page = page;
1368c2ecf20Sopenharmony_ci		pgcache->dma_addr = 0;
1378c2ecf20Sopenharmony_ci		pgcache->ref_count = 0;
1388c2ecf20Sopenharmony_ci		rbdr->pgalloc++;
1398c2ecf20Sopenharmony_ci	}
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci	/* Take additional page references for recycling */
1428c2ecf20Sopenharmony_ci	if (rbdr->is_xdp) {
1438c2ecf20Sopenharmony_ci		/* Since there is single RBDR (i.e single core doing
1448c2ecf20Sopenharmony_ci		 * page recycling) per 8 Rx queues, in XDP mode adjusting
1458c2ecf20Sopenharmony_ci		 * page references atomically is the biggest bottleneck, so
1468c2ecf20Sopenharmony_ci		 * take bunch of references at a time.
1478c2ecf20Sopenharmony_ci		 *
1488c2ecf20Sopenharmony_ci		 * So here, below reference counts defer by '1'.
1498c2ecf20Sopenharmony_ci		 */
1508c2ecf20Sopenharmony_ci		if (!pgcache->ref_count) {
1518c2ecf20Sopenharmony_ci			pgcache->ref_count = XDP_PAGE_REFCNT_REFILL;
1528c2ecf20Sopenharmony_ci			page_ref_add(page, XDP_PAGE_REFCNT_REFILL);
1538c2ecf20Sopenharmony_ci		}
1548c2ecf20Sopenharmony_ci	} else {
1558c2ecf20Sopenharmony_ci		/* In non-XDP case, single 64K page is divided across multiple
1568c2ecf20Sopenharmony_ci		 * receive buffers, so cost of recycling is less anyway.
1578c2ecf20Sopenharmony_ci		 * So we can do with just one extra reference.
1588c2ecf20Sopenharmony_ci		 */
1598c2ecf20Sopenharmony_ci		page_ref_add(page, 1);
1608c2ecf20Sopenharmony_ci	}
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	rbdr->pgidx++;
1638c2ecf20Sopenharmony_ci	rbdr->pgidx &= (rbdr->pgcnt - 1);
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	/* Prefetch refcount of next page in page cache */
1668c2ecf20Sopenharmony_ci	next = &rbdr->pgcache[rbdr->pgidx];
1678c2ecf20Sopenharmony_ci	page = next->page;
1688c2ecf20Sopenharmony_ci	if (page)
1698c2ecf20Sopenharmony_ci		prefetch(&page->_refcount);
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	return pgcache;
1728c2ecf20Sopenharmony_ci}
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci/* Allocate buffer for packet reception */
1758c2ecf20Sopenharmony_cistatic inline int nicvf_alloc_rcv_buffer(struct nicvf *nic, struct rbdr *rbdr,
1768c2ecf20Sopenharmony_ci					 gfp_t gfp, u32 buf_len, u64 *rbuf)
1778c2ecf20Sopenharmony_ci{
1788c2ecf20Sopenharmony_ci	struct pgcache *pgcache = NULL;
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci	/* Check if request can be accomodated in previous allocated page.
1818c2ecf20Sopenharmony_ci	 * But in XDP mode only one buffer per page is permitted.
1828c2ecf20Sopenharmony_ci	 */
1838c2ecf20Sopenharmony_ci	if (!rbdr->is_xdp && nic->rb_page &&
1848c2ecf20Sopenharmony_ci	    ((nic->rb_page_offset + buf_len) <= PAGE_SIZE)) {
1858c2ecf20Sopenharmony_ci		nic->rb_pageref++;
1868c2ecf20Sopenharmony_ci		goto ret;
1878c2ecf20Sopenharmony_ci	}
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	nicvf_get_page(nic);
1908c2ecf20Sopenharmony_ci	nic->rb_page = NULL;
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	/* Get new page, either recycled or new one */
1938c2ecf20Sopenharmony_ci	pgcache = nicvf_alloc_page(nic, rbdr, gfp);
1948c2ecf20Sopenharmony_ci	if (!pgcache && !nic->rb_page) {
1958c2ecf20Sopenharmony_ci		this_cpu_inc(nic->pnicvf->drv_stats->rcv_buffer_alloc_failures);
1968c2ecf20Sopenharmony_ci		return -ENOMEM;
1978c2ecf20Sopenharmony_ci	}
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci	nic->rb_page_offset = 0;
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	/* Reserve space for header modifications by BPF program */
2028c2ecf20Sopenharmony_ci	if (rbdr->is_xdp)
2038c2ecf20Sopenharmony_ci		buf_len += XDP_PACKET_HEADROOM;
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	/* Check if it's recycled */
2068c2ecf20Sopenharmony_ci	if (pgcache)
2078c2ecf20Sopenharmony_ci		nic->rb_page = pgcache->page;
2088c2ecf20Sopenharmony_ciret:
2098c2ecf20Sopenharmony_ci	if (rbdr->is_xdp && pgcache && pgcache->dma_addr) {
2108c2ecf20Sopenharmony_ci		*rbuf = pgcache->dma_addr;
2118c2ecf20Sopenharmony_ci	} else {
2128c2ecf20Sopenharmony_ci		/* HW will ensure data coherency, CPU sync not required */
2138c2ecf20Sopenharmony_ci		*rbuf = (u64)dma_map_page_attrs(&nic->pdev->dev, nic->rb_page,
2148c2ecf20Sopenharmony_ci						nic->rb_page_offset, buf_len,
2158c2ecf20Sopenharmony_ci						DMA_FROM_DEVICE,
2168c2ecf20Sopenharmony_ci						DMA_ATTR_SKIP_CPU_SYNC);
2178c2ecf20Sopenharmony_ci		if (dma_mapping_error(&nic->pdev->dev, (dma_addr_t)*rbuf)) {
2188c2ecf20Sopenharmony_ci			if (!nic->rb_page_offset)
2198c2ecf20Sopenharmony_ci				__free_pages(nic->rb_page, 0);
2208c2ecf20Sopenharmony_ci			nic->rb_page = NULL;
2218c2ecf20Sopenharmony_ci			return -ENOMEM;
2228c2ecf20Sopenharmony_ci		}
2238c2ecf20Sopenharmony_ci		if (pgcache)
2248c2ecf20Sopenharmony_ci			pgcache->dma_addr = *rbuf + XDP_PACKET_HEADROOM;
2258c2ecf20Sopenharmony_ci		nic->rb_page_offset += buf_len;
2268c2ecf20Sopenharmony_ci	}
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci	return 0;
2298c2ecf20Sopenharmony_ci}
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci/* Build skb around receive buffer */
2328c2ecf20Sopenharmony_cistatic struct sk_buff *nicvf_rb_ptr_to_skb(struct nicvf *nic,
2338c2ecf20Sopenharmony_ci					   u64 rb_ptr, int len)
2348c2ecf20Sopenharmony_ci{
2358c2ecf20Sopenharmony_ci	void *data;
2368c2ecf20Sopenharmony_ci	struct sk_buff *skb;
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci	data = phys_to_virt(rb_ptr);
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	/* Now build an skb to give to stack */
2418c2ecf20Sopenharmony_ci	skb = build_skb(data, RCV_FRAG_LEN);
2428c2ecf20Sopenharmony_ci	if (!skb) {
2438c2ecf20Sopenharmony_ci		put_page(virt_to_page(data));
2448c2ecf20Sopenharmony_ci		return NULL;
2458c2ecf20Sopenharmony_ci	}
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci	prefetch(skb->data);
2488c2ecf20Sopenharmony_ci	return skb;
2498c2ecf20Sopenharmony_ci}
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci/* Allocate RBDR ring and populate receive buffers */
2528c2ecf20Sopenharmony_cistatic int  nicvf_init_rbdr(struct nicvf *nic, struct rbdr *rbdr,
2538c2ecf20Sopenharmony_ci			    int ring_len, int buf_size)
2548c2ecf20Sopenharmony_ci{
2558c2ecf20Sopenharmony_ci	int idx;
2568c2ecf20Sopenharmony_ci	u64 rbuf;
2578c2ecf20Sopenharmony_ci	struct rbdr_entry_t *desc;
2588c2ecf20Sopenharmony_ci	int err;
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci	err = nicvf_alloc_q_desc_mem(nic, &rbdr->dmem, ring_len,
2618c2ecf20Sopenharmony_ci				     sizeof(struct rbdr_entry_t),
2628c2ecf20Sopenharmony_ci				     NICVF_RCV_BUF_ALIGN_BYTES);
2638c2ecf20Sopenharmony_ci	if (err)
2648c2ecf20Sopenharmony_ci		return err;
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci	rbdr->desc = rbdr->dmem.base;
2678c2ecf20Sopenharmony_ci	/* Buffer size has to be in multiples of 128 bytes */
2688c2ecf20Sopenharmony_ci	rbdr->dma_size = buf_size;
2698c2ecf20Sopenharmony_ci	rbdr->enable = true;
2708c2ecf20Sopenharmony_ci	rbdr->thresh = RBDR_THRESH;
2718c2ecf20Sopenharmony_ci	rbdr->head = 0;
2728c2ecf20Sopenharmony_ci	rbdr->tail = 0;
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci	/* Initialize page recycling stuff.
2758c2ecf20Sopenharmony_ci	 *
2768c2ecf20Sopenharmony_ci	 * Can't use single buffer per page especially with 64K pages.
2778c2ecf20Sopenharmony_ci	 * On embedded platforms i.e 81xx/83xx available memory itself
2788c2ecf20Sopenharmony_ci	 * is low and minimum ring size of RBDR is 8K, that takes away
2798c2ecf20Sopenharmony_ci	 * lots of memory.
2808c2ecf20Sopenharmony_ci	 *
2818c2ecf20Sopenharmony_ci	 * But for XDP it has to be a single buffer per page.
2828c2ecf20Sopenharmony_ci	 */
2838c2ecf20Sopenharmony_ci	if (!nic->pnicvf->xdp_prog) {
2848c2ecf20Sopenharmony_ci		rbdr->pgcnt = ring_len / (PAGE_SIZE / buf_size);
2858c2ecf20Sopenharmony_ci		rbdr->is_xdp = false;
2868c2ecf20Sopenharmony_ci	} else {
2878c2ecf20Sopenharmony_ci		rbdr->pgcnt = ring_len;
2888c2ecf20Sopenharmony_ci		rbdr->is_xdp = true;
2898c2ecf20Sopenharmony_ci	}
2908c2ecf20Sopenharmony_ci	rbdr->pgcnt = roundup_pow_of_two(rbdr->pgcnt);
2918c2ecf20Sopenharmony_ci	rbdr->pgcache = kcalloc(rbdr->pgcnt, sizeof(*rbdr->pgcache),
2928c2ecf20Sopenharmony_ci				GFP_KERNEL);
2938c2ecf20Sopenharmony_ci	if (!rbdr->pgcache)
2948c2ecf20Sopenharmony_ci		return -ENOMEM;
2958c2ecf20Sopenharmony_ci	rbdr->pgidx = 0;
2968c2ecf20Sopenharmony_ci	rbdr->pgalloc = 0;
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_ci	nic->rb_page = NULL;
2998c2ecf20Sopenharmony_ci	for (idx = 0; idx < ring_len; idx++) {
3008c2ecf20Sopenharmony_ci		err = nicvf_alloc_rcv_buffer(nic, rbdr, GFP_KERNEL,
3018c2ecf20Sopenharmony_ci					     RCV_FRAG_LEN, &rbuf);
3028c2ecf20Sopenharmony_ci		if (err) {
3038c2ecf20Sopenharmony_ci			/* To free already allocated and mapped ones */
3048c2ecf20Sopenharmony_ci			rbdr->tail = idx - 1;
3058c2ecf20Sopenharmony_ci			return err;
3068c2ecf20Sopenharmony_ci		}
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci		desc = GET_RBDR_DESC(rbdr, idx);
3098c2ecf20Sopenharmony_ci		desc->buf_addr = rbuf & ~(NICVF_RCV_BUF_ALIGN_BYTES - 1);
3108c2ecf20Sopenharmony_ci	}
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci	nicvf_get_page(nic);
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci	return 0;
3158c2ecf20Sopenharmony_ci}
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci/* Free RBDR ring and its receive buffers */
3188c2ecf20Sopenharmony_cistatic void nicvf_free_rbdr(struct nicvf *nic, struct rbdr *rbdr)
3198c2ecf20Sopenharmony_ci{
3208c2ecf20Sopenharmony_ci	int head, tail;
3218c2ecf20Sopenharmony_ci	u64 buf_addr, phys_addr;
3228c2ecf20Sopenharmony_ci	struct pgcache *pgcache;
3238c2ecf20Sopenharmony_ci	struct rbdr_entry_t *desc;
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ci	if (!rbdr)
3268c2ecf20Sopenharmony_ci		return;
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_ci	rbdr->enable = false;
3298c2ecf20Sopenharmony_ci	if (!rbdr->dmem.base)
3308c2ecf20Sopenharmony_ci		return;
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci	head = rbdr->head;
3338c2ecf20Sopenharmony_ci	tail = rbdr->tail;
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci	/* Release page references */
3368c2ecf20Sopenharmony_ci	while (head != tail) {
3378c2ecf20Sopenharmony_ci		desc = GET_RBDR_DESC(rbdr, head);
3388c2ecf20Sopenharmony_ci		buf_addr = desc->buf_addr;
3398c2ecf20Sopenharmony_ci		phys_addr = nicvf_iova_to_phys(nic, buf_addr);
3408c2ecf20Sopenharmony_ci		dma_unmap_page_attrs(&nic->pdev->dev, buf_addr, RCV_FRAG_LEN,
3418c2ecf20Sopenharmony_ci				     DMA_FROM_DEVICE, DMA_ATTR_SKIP_CPU_SYNC);
3428c2ecf20Sopenharmony_ci		if (phys_addr)
3438c2ecf20Sopenharmony_ci			put_page(virt_to_page(phys_to_virt(phys_addr)));
3448c2ecf20Sopenharmony_ci		head++;
3458c2ecf20Sopenharmony_ci		head &= (rbdr->dmem.q_len - 1);
3468c2ecf20Sopenharmony_ci	}
3478c2ecf20Sopenharmony_ci	/* Release buffer of tail desc */
3488c2ecf20Sopenharmony_ci	desc = GET_RBDR_DESC(rbdr, tail);
3498c2ecf20Sopenharmony_ci	buf_addr = desc->buf_addr;
3508c2ecf20Sopenharmony_ci	phys_addr = nicvf_iova_to_phys(nic, buf_addr);
3518c2ecf20Sopenharmony_ci	dma_unmap_page_attrs(&nic->pdev->dev, buf_addr, RCV_FRAG_LEN,
3528c2ecf20Sopenharmony_ci			     DMA_FROM_DEVICE, DMA_ATTR_SKIP_CPU_SYNC);
3538c2ecf20Sopenharmony_ci	if (phys_addr)
3548c2ecf20Sopenharmony_ci		put_page(virt_to_page(phys_to_virt(phys_addr)));
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci	/* Sync page cache info */
3578c2ecf20Sopenharmony_ci	smp_rmb();
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci	/* Release additional page references held for recycling */
3608c2ecf20Sopenharmony_ci	head = 0;
3618c2ecf20Sopenharmony_ci	while (head < rbdr->pgcnt) {
3628c2ecf20Sopenharmony_ci		pgcache = &rbdr->pgcache[head];
3638c2ecf20Sopenharmony_ci		if (pgcache->page && page_ref_count(pgcache->page) != 0) {
3648c2ecf20Sopenharmony_ci			if (rbdr->is_xdp) {
3658c2ecf20Sopenharmony_ci				page_ref_sub(pgcache->page,
3668c2ecf20Sopenharmony_ci					     pgcache->ref_count - 1);
3678c2ecf20Sopenharmony_ci			}
3688c2ecf20Sopenharmony_ci			put_page(pgcache->page);
3698c2ecf20Sopenharmony_ci		}
3708c2ecf20Sopenharmony_ci		head++;
3718c2ecf20Sopenharmony_ci	}
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ci	/* Free RBDR ring */
3748c2ecf20Sopenharmony_ci	nicvf_free_q_desc_mem(nic, &rbdr->dmem);
3758c2ecf20Sopenharmony_ci}
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_ci/* Refill receive buffer descriptors with new buffers.
3788c2ecf20Sopenharmony_ci */
3798c2ecf20Sopenharmony_cistatic void nicvf_refill_rbdr(struct nicvf *nic, gfp_t gfp)
3808c2ecf20Sopenharmony_ci{
3818c2ecf20Sopenharmony_ci	struct queue_set *qs = nic->qs;
3828c2ecf20Sopenharmony_ci	int rbdr_idx = qs->rbdr_cnt;
3838c2ecf20Sopenharmony_ci	int tail, qcount;
3848c2ecf20Sopenharmony_ci	int refill_rb_cnt;
3858c2ecf20Sopenharmony_ci	struct rbdr *rbdr;
3868c2ecf20Sopenharmony_ci	struct rbdr_entry_t *desc;
3878c2ecf20Sopenharmony_ci	u64 rbuf;
3888c2ecf20Sopenharmony_ci	int new_rb = 0;
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_cirefill:
3918c2ecf20Sopenharmony_ci	if (!rbdr_idx)
3928c2ecf20Sopenharmony_ci		return;
3938c2ecf20Sopenharmony_ci	rbdr_idx--;
3948c2ecf20Sopenharmony_ci	rbdr = &qs->rbdr[rbdr_idx];
3958c2ecf20Sopenharmony_ci	/* Check if it's enabled */
3968c2ecf20Sopenharmony_ci	if (!rbdr->enable)
3978c2ecf20Sopenharmony_ci		goto next_rbdr;
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_ci	/* Get no of desc's to be refilled */
4008c2ecf20Sopenharmony_ci	qcount = nicvf_queue_reg_read(nic, NIC_QSET_RBDR_0_1_STATUS0, rbdr_idx);
4018c2ecf20Sopenharmony_ci	qcount &= 0x7FFFF;
4028c2ecf20Sopenharmony_ci	/* Doorbell can be ringed with a max of ring size minus 1 */
4038c2ecf20Sopenharmony_ci	if (qcount >= (qs->rbdr_len - 1))
4048c2ecf20Sopenharmony_ci		goto next_rbdr;
4058c2ecf20Sopenharmony_ci	else
4068c2ecf20Sopenharmony_ci		refill_rb_cnt = qs->rbdr_len - qcount - 1;
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_ci	/* Sync page cache info */
4098c2ecf20Sopenharmony_ci	smp_rmb();
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_ci	/* Start filling descs from tail */
4128c2ecf20Sopenharmony_ci	tail = nicvf_queue_reg_read(nic, NIC_QSET_RBDR_0_1_TAIL, rbdr_idx) >> 3;
4138c2ecf20Sopenharmony_ci	while (refill_rb_cnt) {
4148c2ecf20Sopenharmony_ci		tail++;
4158c2ecf20Sopenharmony_ci		tail &= (rbdr->dmem.q_len - 1);
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_ci		if (nicvf_alloc_rcv_buffer(nic, rbdr, gfp, RCV_FRAG_LEN, &rbuf))
4188c2ecf20Sopenharmony_ci			break;
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_ci		desc = GET_RBDR_DESC(rbdr, tail);
4218c2ecf20Sopenharmony_ci		desc->buf_addr = rbuf & ~(NICVF_RCV_BUF_ALIGN_BYTES - 1);
4228c2ecf20Sopenharmony_ci		refill_rb_cnt--;
4238c2ecf20Sopenharmony_ci		new_rb++;
4248c2ecf20Sopenharmony_ci	}
4258c2ecf20Sopenharmony_ci
4268c2ecf20Sopenharmony_ci	nicvf_get_page(nic);
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_ci	/* make sure all memory stores are done before ringing doorbell */
4298c2ecf20Sopenharmony_ci	smp_wmb();
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ci	/* Check if buffer allocation failed */
4328c2ecf20Sopenharmony_ci	if (refill_rb_cnt)
4338c2ecf20Sopenharmony_ci		nic->rb_alloc_fail = true;
4348c2ecf20Sopenharmony_ci	else
4358c2ecf20Sopenharmony_ci		nic->rb_alloc_fail = false;
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_ci	/* Notify HW */
4388c2ecf20Sopenharmony_ci	nicvf_queue_reg_write(nic, NIC_QSET_RBDR_0_1_DOOR,
4398c2ecf20Sopenharmony_ci			      rbdr_idx, new_rb);
4408c2ecf20Sopenharmony_cinext_rbdr:
4418c2ecf20Sopenharmony_ci	/* Re-enable RBDR interrupts only if buffer allocation is success */
4428c2ecf20Sopenharmony_ci	if (!nic->rb_alloc_fail && rbdr->enable &&
4438c2ecf20Sopenharmony_ci	    netif_running(nic->pnicvf->netdev))
4448c2ecf20Sopenharmony_ci		nicvf_enable_intr(nic, NICVF_INTR_RBDR, rbdr_idx);
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_ci	if (rbdr_idx)
4478c2ecf20Sopenharmony_ci		goto refill;
4488c2ecf20Sopenharmony_ci}
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_ci/* Alloc rcv buffers in non-atomic mode for better success */
4518c2ecf20Sopenharmony_civoid nicvf_rbdr_work(struct work_struct *work)
4528c2ecf20Sopenharmony_ci{
4538c2ecf20Sopenharmony_ci	struct nicvf *nic = container_of(work, struct nicvf, rbdr_work.work);
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_ci	nicvf_refill_rbdr(nic, GFP_KERNEL);
4568c2ecf20Sopenharmony_ci	if (nic->rb_alloc_fail)
4578c2ecf20Sopenharmony_ci		schedule_delayed_work(&nic->rbdr_work, msecs_to_jiffies(10));
4588c2ecf20Sopenharmony_ci	else
4598c2ecf20Sopenharmony_ci		nic->rb_work_scheduled = false;
4608c2ecf20Sopenharmony_ci}
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_ci/* In Softirq context, alloc rcv buffers in atomic mode */
4638c2ecf20Sopenharmony_civoid nicvf_rbdr_task(struct tasklet_struct *t)
4648c2ecf20Sopenharmony_ci{
4658c2ecf20Sopenharmony_ci	struct nicvf *nic = from_tasklet(nic, t, rbdr_task);
4668c2ecf20Sopenharmony_ci
4678c2ecf20Sopenharmony_ci	nicvf_refill_rbdr(nic, GFP_ATOMIC);
4688c2ecf20Sopenharmony_ci	if (nic->rb_alloc_fail) {
4698c2ecf20Sopenharmony_ci		nic->rb_work_scheduled = true;
4708c2ecf20Sopenharmony_ci		schedule_delayed_work(&nic->rbdr_work, msecs_to_jiffies(10));
4718c2ecf20Sopenharmony_ci	}
4728c2ecf20Sopenharmony_ci}
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_ci/* Initialize completion queue */
4758c2ecf20Sopenharmony_cistatic int nicvf_init_cmp_queue(struct nicvf *nic,
4768c2ecf20Sopenharmony_ci				struct cmp_queue *cq, int q_len)
4778c2ecf20Sopenharmony_ci{
4788c2ecf20Sopenharmony_ci	int err;
4798c2ecf20Sopenharmony_ci
4808c2ecf20Sopenharmony_ci	err = nicvf_alloc_q_desc_mem(nic, &cq->dmem, q_len, CMP_QUEUE_DESC_SIZE,
4818c2ecf20Sopenharmony_ci				     NICVF_CQ_BASE_ALIGN_BYTES);
4828c2ecf20Sopenharmony_ci	if (err)
4838c2ecf20Sopenharmony_ci		return err;
4848c2ecf20Sopenharmony_ci
4858c2ecf20Sopenharmony_ci	cq->desc = cq->dmem.base;
4868c2ecf20Sopenharmony_ci	cq->thresh = pass1_silicon(nic->pdev) ? 0 : CMP_QUEUE_CQE_THRESH;
4878c2ecf20Sopenharmony_ci	nic->cq_coalesce_usecs = (CMP_QUEUE_TIMER_THRESH * 0.05) - 1;
4888c2ecf20Sopenharmony_ci
4898c2ecf20Sopenharmony_ci	return 0;
4908c2ecf20Sopenharmony_ci}
4918c2ecf20Sopenharmony_ci
4928c2ecf20Sopenharmony_cistatic void nicvf_free_cmp_queue(struct nicvf *nic, struct cmp_queue *cq)
4938c2ecf20Sopenharmony_ci{
4948c2ecf20Sopenharmony_ci	if (!cq)
4958c2ecf20Sopenharmony_ci		return;
4968c2ecf20Sopenharmony_ci	if (!cq->dmem.base)
4978c2ecf20Sopenharmony_ci		return;
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_ci	nicvf_free_q_desc_mem(nic, &cq->dmem);
5008c2ecf20Sopenharmony_ci}
5018c2ecf20Sopenharmony_ci
5028c2ecf20Sopenharmony_ci/* Initialize transmit queue */
5038c2ecf20Sopenharmony_cistatic int nicvf_init_snd_queue(struct nicvf *nic,
5048c2ecf20Sopenharmony_ci				struct snd_queue *sq, int q_len, int qidx)
5058c2ecf20Sopenharmony_ci{
5068c2ecf20Sopenharmony_ci	int err;
5078c2ecf20Sopenharmony_ci
5088c2ecf20Sopenharmony_ci	err = nicvf_alloc_q_desc_mem(nic, &sq->dmem, q_len, SND_QUEUE_DESC_SIZE,
5098c2ecf20Sopenharmony_ci				     NICVF_SQ_BASE_ALIGN_BYTES);
5108c2ecf20Sopenharmony_ci	if (err)
5118c2ecf20Sopenharmony_ci		return err;
5128c2ecf20Sopenharmony_ci
5138c2ecf20Sopenharmony_ci	sq->desc = sq->dmem.base;
5148c2ecf20Sopenharmony_ci	sq->skbuff = kcalloc(q_len, sizeof(u64), GFP_KERNEL);
5158c2ecf20Sopenharmony_ci	if (!sq->skbuff)
5168c2ecf20Sopenharmony_ci		return -ENOMEM;
5178c2ecf20Sopenharmony_ci
5188c2ecf20Sopenharmony_ci	sq->head = 0;
5198c2ecf20Sopenharmony_ci	sq->tail = 0;
5208c2ecf20Sopenharmony_ci	sq->thresh = SND_QUEUE_THRESH;
5218c2ecf20Sopenharmony_ci
5228c2ecf20Sopenharmony_ci	/* Check if this SQ is a XDP TX queue */
5238c2ecf20Sopenharmony_ci	if (nic->sqs_mode)
5248c2ecf20Sopenharmony_ci		qidx += ((nic->sqs_id + 1) * MAX_SND_QUEUES_PER_QS);
5258c2ecf20Sopenharmony_ci	if (qidx < nic->pnicvf->xdp_tx_queues) {
5268c2ecf20Sopenharmony_ci		/* Alloc memory to save page pointers for XDP_TX */
5278c2ecf20Sopenharmony_ci		sq->xdp_page = kcalloc(q_len, sizeof(u64), GFP_KERNEL);
5288c2ecf20Sopenharmony_ci		if (!sq->xdp_page)
5298c2ecf20Sopenharmony_ci			return -ENOMEM;
5308c2ecf20Sopenharmony_ci		sq->xdp_desc_cnt = 0;
5318c2ecf20Sopenharmony_ci		sq->xdp_free_cnt = q_len - 1;
5328c2ecf20Sopenharmony_ci		sq->is_xdp = true;
5338c2ecf20Sopenharmony_ci	} else {
5348c2ecf20Sopenharmony_ci		sq->xdp_page = NULL;
5358c2ecf20Sopenharmony_ci		sq->xdp_desc_cnt = 0;
5368c2ecf20Sopenharmony_ci		sq->xdp_free_cnt = 0;
5378c2ecf20Sopenharmony_ci		sq->is_xdp = false;
5388c2ecf20Sopenharmony_ci
5398c2ecf20Sopenharmony_ci		atomic_set(&sq->free_cnt, q_len - 1);
5408c2ecf20Sopenharmony_ci
5418c2ecf20Sopenharmony_ci		/* Preallocate memory for TSO segment's header */
5428c2ecf20Sopenharmony_ci		sq->tso_hdrs = dma_alloc_coherent(&nic->pdev->dev,
5438c2ecf20Sopenharmony_ci						  q_len * TSO_HEADER_SIZE,
5448c2ecf20Sopenharmony_ci						  &sq->tso_hdrs_phys,
5458c2ecf20Sopenharmony_ci						  GFP_KERNEL);
5468c2ecf20Sopenharmony_ci		if (!sq->tso_hdrs)
5478c2ecf20Sopenharmony_ci			return -ENOMEM;
5488c2ecf20Sopenharmony_ci	}
5498c2ecf20Sopenharmony_ci
5508c2ecf20Sopenharmony_ci	return 0;
5518c2ecf20Sopenharmony_ci}
5528c2ecf20Sopenharmony_ci
5538c2ecf20Sopenharmony_civoid nicvf_unmap_sndq_buffers(struct nicvf *nic, struct snd_queue *sq,
5548c2ecf20Sopenharmony_ci			      int hdr_sqe, u8 subdesc_cnt)
5558c2ecf20Sopenharmony_ci{
5568c2ecf20Sopenharmony_ci	u8 idx;
5578c2ecf20Sopenharmony_ci	struct sq_gather_subdesc *gather;
5588c2ecf20Sopenharmony_ci
5598c2ecf20Sopenharmony_ci	/* Unmap DMA mapped skb data buffers */
5608c2ecf20Sopenharmony_ci	for (idx = 0; idx < subdesc_cnt; idx++) {
5618c2ecf20Sopenharmony_ci		hdr_sqe++;
5628c2ecf20Sopenharmony_ci		hdr_sqe &= (sq->dmem.q_len - 1);
5638c2ecf20Sopenharmony_ci		gather = (struct sq_gather_subdesc *)GET_SQ_DESC(sq, hdr_sqe);
5648c2ecf20Sopenharmony_ci		/* HW will ensure data coherency, CPU sync not required */
5658c2ecf20Sopenharmony_ci		dma_unmap_page_attrs(&nic->pdev->dev, gather->addr,
5668c2ecf20Sopenharmony_ci				     gather->size, DMA_TO_DEVICE,
5678c2ecf20Sopenharmony_ci				     DMA_ATTR_SKIP_CPU_SYNC);
5688c2ecf20Sopenharmony_ci	}
5698c2ecf20Sopenharmony_ci}
5708c2ecf20Sopenharmony_ci
5718c2ecf20Sopenharmony_cistatic void nicvf_free_snd_queue(struct nicvf *nic, struct snd_queue *sq)
5728c2ecf20Sopenharmony_ci{
5738c2ecf20Sopenharmony_ci	struct sk_buff *skb;
5748c2ecf20Sopenharmony_ci	struct page *page;
5758c2ecf20Sopenharmony_ci	struct sq_hdr_subdesc *hdr;
5768c2ecf20Sopenharmony_ci	struct sq_hdr_subdesc *tso_sqe;
5778c2ecf20Sopenharmony_ci
5788c2ecf20Sopenharmony_ci	if (!sq)
5798c2ecf20Sopenharmony_ci		return;
5808c2ecf20Sopenharmony_ci	if (!sq->dmem.base)
5818c2ecf20Sopenharmony_ci		return;
5828c2ecf20Sopenharmony_ci
5838c2ecf20Sopenharmony_ci	if (sq->tso_hdrs) {
5848c2ecf20Sopenharmony_ci		dma_free_coherent(&nic->pdev->dev,
5858c2ecf20Sopenharmony_ci				  sq->dmem.q_len * TSO_HEADER_SIZE,
5868c2ecf20Sopenharmony_ci				  sq->tso_hdrs, sq->tso_hdrs_phys);
5878c2ecf20Sopenharmony_ci		sq->tso_hdrs = NULL;
5888c2ecf20Sopenharmony_ci	}
5898c2ecf20Sopenharmony_ci
5908c2ecf20Sopenharmony_ci	/* Free pending skbs in the queue */
5918c2ecf20Sopenharmony_ci	smp_rmb();
5928c2ecf20Sopenharmony_ci	while (sq->head != sq->tail) {
5938c2ecf20Sopenharmony_ci		skb = (struct sk_buff *)sq->skbuff[sq->head];
5948c2ecf20Sopenharmony_ci		if (!skb || !sq->xdp_page)
5958c2ecf20Sopenharmony_ci			goto next;
5968c2ecf20Sopenharmony_ci
5978c2ecf20Sopenharmony_ci		page = (struct page *)sq->xdp_page[sq->head];
5988c2ecf20Sopenharmony_ci		if (!page)
5998c2ecf20Sopenharmony_ci			goto next;
6008c2ecf20Sopenharmony_ci		else
6018c2ecf20Sopenharmony_ci			put_page(page);
6028c2ecf20Sopenharmony_ci
6038c2ecf20Sopenharmony_ci		hdr = (struct sq_hdr_subdesc *)GET_SQ_DESC(sq, sq->head);
6048c2ecf20Sopenharmony_ci		/* Check for dummy descriptor used for HW TSO offload on 88xx */
6058c2ecf20Sopenharmony_ci		if (hdr->dont_send) {
6068c2ecf20Sopenharmony_ci			/* Get actual TSO descriptors and unmap them */
6078c2ecf20Sopenharmony_ci			tso_sqe =
6088c2ecf20Sopenharmony_ci			 (struct sq_hdr_subdesc *)GET_SQ_DESC(sq, hdr->rsvd2);
6098c2ecf20Sopenharmony_ci			nicvf_unmap_sndq_buffers(nic, sq, hdr->rsvd2,
6108c2ecf20Sopenharmony_ci						 tso_sqe->subdesc_cnt);
6118c2ecf20Sopenharmony_ci		} else {
6128c2ecf20Sopenharmony_ci			nicvf_unmap_sndq_buffers(nic, sq, sq->head,
6138c2ecf20Sopenharmony_ci						 hdr->subdesc_cnt);
6148c2ecf20Sopenharmony_ci		}
6158c2ecf20Sopenharmony_ci		if (skb)
6168c2ecf20Sopenharmony_ci			dev_kfree_skb_any(skb);
6178c2ecf20Sopenharmony_cinext:
6188c2ecf20Sopenharmony_ci		sq->head++;
6198c2ecf20Sopenharmony_ci		sq->head &= (sq->dmem.q_len - 1);
6208c2ecf20Sopenharmony_ci	}
6218c2ecf20Sopenharmony_ci	kfree(sq->skbuff);
6228c2ecf20Sopenharmony_ci	kfree(sq->xdp_page);
6238c2ecf20Sopenharmony_ci	nicvf_free_q_desc_mem(nic, &sq->dmem);
6248c2ecf20Sopenharmony_ci}
6258c2ecf20Sopenharmony_ci
6268c2ecf20Sopenharmony_cistatic void nicvf_reclaim_snd_queue(struct nicvf *nic,
6278c2ecf20Sopenharmony_ci				    struct queue_set *qs, int qidx)
6288c2ecf20Sopenharmony_ci{
6298c2ecf20Sopenharmony_ci	/* Disable send queue */
6308c2ecf20Sopenharmony_ci	nicvf_queue_reg_write(nic, NIC_QSET_SQ_0_7_CFG, qidx, 0);
6318c2ecf20Sopenharmony_ci	/* Check if SQ is stopped */
6328c2ecf20Sopenharmony_ci	if (nicvf_poll_reg(nic, qidx, NIC_QSET_SQ_0_7_STATUS, 21, 1, 0x01))
6338c2ecf20Sopenharmony_ci		return;
6348c2ecf20Sopenharmony_ci	/* Reset send queue */
6358c2ecf20Sopenharmony_ci	nicvf_queue_reg_write(nic, NIC_QSET_SQ_0_7_CFG, qidx, NICVF_SQ_RESET);
6368c2ecf20Sopenharmony_ci}
6378c2ecf20Sopenharmony_ci
6388c2ecf20Sopenharmony_cistatic void nicvf_reclaim_rcv_queue(struct nicvf *nic,
6398c2ecf20Sopenharmony_ci				    struct queue_set *qs, int qidx)
6408c2ecf20Sopenharmony_ci{
6418c2ecf20Sopenharmony_ci	union nic_mbx mbx = {};
6428c2ecf20Sopenharmony_ci
6438c2ecf20Sopenharmony_ci	/* Make sure all packets in the pipeline are written back into mem */
6448c2ecf20Sopenharmony_ci	mbx.msg.msg = NIC_MBOX_MSG_RQ_SW_SYNC;
6458c2ecf20Sopenharmony_ci	nicvf_send_msg_to_pf(nic, &mbx);
6468c2ecf20Sopenharmony_ci}
6478c2ecf20Sopenharmony_ci
6488c2ecf20Sopenharmony_cistatic void nicvf_reclaim_cmp_queue(struct nicvf *nic,
6498c2ecf20Sopenharmony_ci				    struct queue_set *qs, int qidx)
6508c2ecf20Sopenharmony_ci{
6518c2ecf20Sopenharmony_ci	/* Disable timer threshold (doesn't get reset upon CQ reset */
6528c2ecf20Sopenharmony_ci	nicvf_queue_reg_write(nic, NIC_QSET_CQ_0_7_CFG2, qidx, 0);
6538c2ecf20Sopenharmony_ci	/* Disable completion queue */
6548c2ecf20Sopenharmony_ci	nicvf_queue_reg_write(nic, NIC_QSET_CQ_0_7_CFG, qidx, 0);
6558c2ecf20Sopenharmony_ci	/* Reset completion queue */
6568c2ecf20Sopenharmony_ci	nicvf_queue_reg_write(nic, NIC_QSET_CQ_0_7_CFG, qidx, NICVF_CQ_RESET);
6578c2ecf20Sopenharmony_ci}
6588c2ecf20Sopenharmony_ci
6598c2ecf20Sopenharmony_cistatic void nicvf_reclaim_rbdr(struct nicvf *nic,
6608c2ecf20Sopenharmony_ci			       struct rbdr *rbdr, int qidx)
6618c2ecf20Sopenharmony_ci{
6628c2ecf20Sopenharmony_ci	u64 tmp, fifo_state;
6638c2ecf20Sopenharmony_ci	int timeout = 10;
6648c2ecf20Sopenharmony_ci
6658c2ecf20Sopenharmony_ci	/* Save head and tail pointers for feeing up buffers */
6668c2ecf20Sopenharmony_ci	rbdr->head = nicvf_queue_reg_read(nic,
6678c2ecf20Sopenharmony_ci					  NIC_QSET_RBDR_0_1_HEAD,
6688c2ecf20Sopenharmony_ci					  qidx) >> 3;
6698c2ecf20Sopenharmony_ci	rbdr->tail = nicvf_queue_reg_read(nic,
6708c2ecf20Sopenharmony_ci					  NIC_QSET_RBDR_0_1_TAIL,
6718c2ecf20Sopenharmony_ci					  qidx) >> 3;
6728c2ecf20Sopenharmony_ci
6738c2ecf20Sopenharmony_ci	/* If RBDR FIFO is in 'FAIL' state then do a reset first
6748c2ecf20Sopenharmony_ci	 * before relaiming.
6758c2ecf20Sopenharmony_ci	 */
6768c2ecf20Sopenharmony_ci	fifo_state = nicvf_queue_reg_read(nic, NIC_QSET_RBDR_0_1_STATUS0, qidx);
6778c2ecf20Sopenharmony_ci	if (((fifo_state >> 62) & 0x03) == 0x3)
6788c2ecf20Sopenharmony_ci		nicvf_queue_reg_write(nic, NIC_QSET_RBDR_0_1_CFG,
6798c2ecf20Sopenharmony_ci				      qidx, NICVF_RBDR_RESET);
6808c2ecf20Sopenharmony_ci
6818c2ecf20Sopenharmony_ci	/* Disable RBDR */
6828c2ecf20Sopenharmony_ci	nicvf_queue_reg_write(nic, NIC_QSET_RBDR_0_1_CFG, qidx, 0);
6838c2ecf20Sopenharmony_ci	if (nicvf_poll_reg(nic, qidx, NIC_QSET_RBDR_0_1_STATUS0, 62, 2, 0x00))
6848c2ecf20Sopenharmony_ci		return;
6858c2ecf20Sopenharmony_ci	while (1) {
6868c2ecf20Sopenharmony_ci		tmp = nicvf_queue_reg_read(nic,
6878c2ecf20Sopenharmony_ci					   NIC_QSET_RBDR_0_1_PREFETCH_STATUS,
6888c2ecf20Sopenharmony_ci					   qidx);
6898c2ecf20Sopenharmony_ci		if ((tmp & 0xFFFFFFFF) == ((tmp >> 32) & 0xFFFFFFFF))
6908c2ecf20Sopenharmony_ci			break;
6918c2ecf20Sopenharmony_ci		usleep_range(1000, 2000);
6928c2ecf20Sopenharmony_ci		timeout--;
6938c2ecf20Sopenharmony_ci		if (!timeout) {
6948c2ecf20Sopenharmony_ci			netdev_err(nic->netdev,
6958c2ecf20Sopenharmony_ci				   "Failed polling on prefetch status\n");
6968c2ecf20Sopenharmony_ci			return;
6978c2ecf20Sopenharmony_ci		}
6988c2ecf20Sopenharmony_ci	}
6998c2ecf20Sopenharmony_ci	nicvf_queue_reg_write(nic, NIC_QSET_RBDR_0_1_CFG,
7008c2ecf20Sopenharmony_ci			      qidx, NICVF_RBDR_RESET);
7018c2ecf20Sopenharmony_ci
7028c2ecf20Sopenharmony_ci	if (nicvf_poll_reg(nic, qidx, NIC_QSET_RBDR_0_1_STATUS0, 62, 2, 0x02))
7038c2ecf20Sopenharmony_ci		return;
7048c2ecf20Sopenharmony_ci	nicvf_queue_reg_write(nic, NIC_QSET_RBDR_0_1_CFG, qidx, 0x00);
7058c2ecf20Sopenharmony_ci	if (nicvf_poll_reg(nic, qidx, NIC_QSET_RBDR_0_1_STATUS0, 62, 2, 0x00))
7068c2ecf20Sopenharmony_ci		return;
7078c2ecf20Sopenharmony_ci}
7088c2ecf20Sopenharmony_ci
7098c2ecf20Sopenharmony_civoid nicvf_config_vlan_stripping(struct nicvf *nic, netdev_features_t features)
7108c2ecf20Sopenharmony_ci{
7118c2ecf20Sopenharmony_ci	u64 rq_cfg;
7128c2ecf20Sopenharmony_ci	int sqs;
7138c2ecf20Sopenharmony_ci
7148c2ecf20Sopenharmony_ci	rq_cfg = nicvf_queue_reg_read(nic, NIC_QSET_RQ_GEN_CFG, 0);
7158c2ecf20Sopenharmony_ci
7168c2ecf20Sopenharmony_ci	/* Enable first VLAN stripping */
7178c2ecf20Sopenharmony_ci	if (features & NETIF_F_HW_VLAN_CTAG_RX)
7188c2ecf20Sopenharmony_ci		rq_cfg |= (1ULL << 25);
7198c2ecf20Sopenharmony_ci	else
7208c2ecf20Sopenharmony_ci		rq_cfg &= ~(1ULL << 25);
7218c2ecf20Sopenharmony_ci	nicvf_queue_reg_write(nic, NIC_QSET_RQ_GEN_CFG, 0, rq_cfg);
7228c2ecf20Sopenharmony_ci
7238c2ecf20Sopenharmony_ci	/* Configure Secondary Qsets, if any */
7248c2ecf20Sopenharmony_ci	for (sqs = 0; sqs < nic->sqs_count; sqs++)
7258c2ecf20Sopenharmony_ci		if (nic->snicvf[sqs])
7268c2ecf20Sopenharmony_ci			nicvf_queue_reg_write(nic->snicvf[sqs],
7278c2ecf20Sopenharmony_ci					      NIC_QSET_RQ_GEN_CFG, 0, rq_cfg);
7288c2ecf20Sopenharmony_ci}
7298c2ecf20Sopenharmony_ci
7308c2ecf20Sopenharmony_cistatic void nicvf_reset_rcv_queue_stats(struct nicvf *nic)
7318c2ecf20Sopenharmony_ci{
7328c2ecf20Sopenharmony_ci	union nic_mbx mbx = {};
7338c2ecf20Sopenharmony_ci
7348c2ecf20Sopenharmony_ci	/* Reset all RQ/SQ and VF stats */
7358c2ecf20Sopenharmony_ci	mbx.reset_stat.msg = NIC_MBOX_MSG_RESET_STAT_COUNTER;
7368c2ecf20Sopenharmony_ci	mbx.reset_stat.rx_stat_mask = 0x3FFF;
7378c2ecf20Sopenharmony_ci	mbx.reset_stat.tx_stat_mask = 0x1F;
7388c2ecf20Sopenharmony_ci	mbx.reset_stat.rq_stat_mask = 0xFFFF;
7398c2ecf20Sopenharmony_ci	mbx.reset_stat.sq_stat_mask = 0xFFFF;
7408c2ecf20Sopenharmony_ci	nicvf_send_msg_to_pf(nic, &mbx);
7418c2ecf20Sopenharmony_ci}
7428c2ecf20Sopenharmony_ci
7438c2ecf20Sopenharmony_ci/* Configures receive queue */
7448c2ecf20Sopenharmony_cistatic void nicvf_rcv_queue_config(struct nicvf *nic, struct queue_set *qs,
7458c2ecf20Sopenharmony_ci				   int qidx, bool enable)
7468c2ecf20Sopenharmony_ci{
7478c2ecf20Sopenharmony_ci	union nic_mbx mbx = {};
7488c2ecf20Sopenharmony_ci	struct rcv_queue *rq;
7498c2ecf20Sopenharmony_ci	struct rq_cfg rq_cfg;
7508c2ecf20Sopenharmony_ci
7518c2ecf20Sopenharmony_ci	rq = &qs->rq[qidx];
7528c2ecf20Sopenharmony_ci	rq->enable = enable;
7538c2ecf20Sopenharmony_ci
7548c2ecf20Sopenharmony_ci	/* Disable receive queue */
7558c2ecf20Sopenharmony_ci	nicvf_queue_reg_write(nic, NIC_QSET_RQ_0_7_CFG, qidx, 0);
7568c2ecf20Sopenharmony_ci
7578c2ecf20Sopenharmony_ci	if (!rq->enable) {
7588c2ecf20Sopenharmony_ci		nicvf_reclaim_rcv_queue(nic, qs, qidx);
7598c2ecf20Sopenharmony_ci		xdp_rxq_info_unreg(&rq->xdp_rxq);
7608c2ecf20Sopenharmony_ci		return;
7618c2ecf20Sopenharmony_ci	}
7628c2ecf20Sopenharmony_ci
7638c2ecf20Sopenharmony_ci	rq->cq_qs = qs->vnic_id;
7648c2ecf20Sopenharmony_ci	rq->cq_idx = qidx;
7658c2ecf20Sopenharmony_ci	rq->start_rbdr_qs = qs->vnic_id;
7668c2ecf20Sopenharmony_ci	rq->start_qs_rbdr_idx = qs->rbdr_cnt - 1;
7678c2ecf20Sopenharmony_ci	rq->cont_rbdr_qs = qs->vnic_id;
7688c2ecf20Sopenharmony_ci	rq->cont_qs_rbdr_idx = qs->rbdr_cnt - 1;
7698c2ecf20Sopenharmony_ci	/* all writes of RBDR data to be loaded into L2 Cache as well*/
7708c2ecf20Sopenharmony_ci	rq->caching = 1;
7718c2ecf20Sopenharmony_ci
7728c2ecf20Sopenharmony_ci	/* Driver have no proper error path for failed XDP RX-queue info reg */
7738c2ecf20Sopenharmony_ci	WARN_ON(xdp_rxq_info_reg(&rq->xdp_rxq, nic->netdev, qidx) < 0);
7748c2ecf20Sopenharmony_ci
7758c2ecf20Sopenharmony_ci	/* Send a mailbox msg to PF to config RQ */
7768c2ecf20Sopenharmony_ci	mbx.rq.msg = NIC_MBOX_MSG_RQ_CFG;
7778c2ecf20Sopenharmony_ci	mbx.rq.qs_num = qs->vnic_id;
7788c2ecf20Sopenharmony_ci	mbx.rq.rq_num = qidx;
7798c2ecf20Sopenharmony_ci	mbx.rq.cfg = ((u64)rq->caching << 26) | (rq->cq_qs << 19) |
7808c2ecf20Sopenharmony_ci			  (rq->cq_idx << 16) | (rq->cont_rbdr_qs << 9) |
7818c2ecf20Sopenharmony_ci			  (rq->cont_qs_rbdr_idx << 8) |
7828c2ecf20Sopenharmony_ci			  (rq->start_rbdr_qs << 1) | (rq->start_qs_rbdr_idx);
7838c2ecf20Sopenharmony_ci	nicvf_send_msg_to_pf(nic, &mbx);
7848c2ecf20Sopenharmony_ci
7858c2ecf20Sopenharmony_ci	mbx.rq.msg = NIC_MBOX_MSG_RQ_BP_CFG;
7868c2ecf20Sopenharmony_ci	mbx.rq.cfg = BIT_ULL(63) | BIT_ULL(62) |
7878c2ecf20Sopenharmony_ci		     (RQ_PASS_RBDR_LVL << 16) | (RQ_PASS_CQ_LVL << 8) |
7888c2ecf20Sopenharmony_ci		     (qs->vnic_id << 0);
7898c2ecf20Sopenharmony_ci	nicvf_send_msg_to_pf(nic, &mbx);
7908c2ecf20Sopenharmony_ci
7918c2ecf20Sopenharmony_ci	/* RQ drop config
7928c2ecf20Sopenharmony_ci	 * Enable CQ drop to reserve sufficient CQEs for all tx packets
7938c2ecf20Sopenharmony_ci	 */
7948c2ecf20Sopenharmony_ci	mbx.rq.msg = NIC_MBOX_MSG_RQ_DROP_CFG;
7958c2ecf20Sopenharmony_ci	mbx.rq.cfg = BIT_ULL(63) | BIT_ULL(62) |
7968c2ecf20Sopenharmony_ci		     (RQ_PASS_RBDR_LVL << 40) | (RQ_DROP_RBDR_LVL << 32) |
7978c2ecf20Sopenharmony_ci		     (RQ_PASS_CQ_LVL << 16) | (RQ_DROP_CQ_LVL << 8);
7988c2ecf20Sopenharmony_ci	nicvf_send_msg_to_pf(nic, &mbx);
7998c2ecf20Sopenharmony_ci
8008c2ecf20Sopenharmony_ci	if (!nic->sqs_mode && (qidx == 0)) {
8018c2ecf20Sopenharmony_ci		/* Enable checking L3/L4 length and TCP/UDP checksums
8028c2ecf20Sopenharmony_ci		 * Also allow IPv6 pkts with zero UDP checksum.
8038c2ecf20Sopenharmony_ci		 */
8048c2ecf20Sopenharmony_ci		nicvf_queue_reg_write(nic, NIC_QSET_RQ_GEN_CFG, 0,
8058c2ecf20Sopenharmony_ci				      (BIT(24) | BIT(23) | BIT(21) | BIT(20)));
8068c2ecf20Sopenharmony_ci		nicvf_config_vlan_stripping(nic, nic->netdev->features);
8078c2ecf20Sopenharmony_ci	}
8088c2ecf20Sopenharmony_ci
8098c2ecf20Sopenharmony_ci	/* Enable Receive queue */
8108c2ecf20Sopenharmony_ci	memset(&rq_cfg, 0, sizeof(struct rq_cfg));
8118c2ecf20Sopenharmony_ci	rq_cfg.ena = 1;
8128c2ecf20Sopenharmony_ci	rq_cfg.tcp_ena = 0;
8138c2ecf20Sopenharmony_ci	nicvf_queue_reg_write(nic, NIC_QSET_RQ_0_7_CFG, qidx, *(u64 *)&rq_cfg);
8148c2ecf20Sopenharmony_ci}
8158c2ecf20Sopenharmony_ci
8168c2ecf20Sopenharmony_ci/* Configures completion queue */
8178c2ecf20Sopenharmony_civoid nicvf_cmp_queue_config(struct nicvf *nic, struct queue_set *qs,
8188c2ecf20Sopenharmony_ci			    int qidx, bool enable)
8198c2ecf20Sopenharmony_ci{
8208c2ecf20Sopenharmony_ci	struct cmp_queue *cq;
8218c2ecf20Sopenharmony_ci	struct cq_cfg cq_cfg;
8228c2ecf20Sopenharmony_ci
8238c2ecf20Sopenharmony_ci	cq = &qs->cq[qidx];
8248c2ecf20Sopenharmony_ci	cq->enable = enable;
8258c2ecf20Sopenharmony_ci
8268c2ecf20Sopenharmony_ci	if (!cq->enable) {
8278c2ecf20Sopenharmony_ci		nicvf_reclaim_cmp_queue(nic, qs, qidx);
8288c2ecf20Sopenharmony_ci		return;
8298c2ecf20Sopenharmony_ci	}
8308c2ecf20Sopenharmony_ci
8318c2ecf20Sopenharmony_ci	/* Reset completion queue */
8328c2ecf20Sopenharmony_ci	nicvf_queue_reg_write(nic, NIC_QSET_CQ_0_7_CFG, qidx, NICVF_CQ_RESET);
8338c2ecf20Sopenharmony_ci
8348c2ecf20Sopenharmony_ci	if (!cq->enable)
8358c2ecf20Sopenharmony_ci		return;
8368c2ecf20Sopenharmony_ci
8378c2ecf20Sopenharmony_ci	spin_lock_init(&cq->lock);
8388c2ecf20Sopenharmony_ci	/* Set completion queue base address */
8398c2ecf20Sopenharmony_ci	nicvf_queue_reg_write(nic, NIC_QSET_CQ_0_7_BASE,
8408c2ecf20Sopenharmony_ci			      qidx, (u64)(cq->dmem.phys_base));
8418c2ecf20Sopenharmony_ci
8428c2ecf20Sopenharmony_ci	/* Enable Completion queue */
8438c2ecf20Sopenharmony_ci	memset(&cq_cfg, 0, sizeof(struct cq_cfg));
8448c2ecf20Sopenharmony_ci	cq_cfg.ena = 1;
8458c2ecf20Sopenharmony_ci	cq_cfg.reset = 0;
8468c2ecf20Sopenharmony_ci	cq_cfg.caching = 0;
8478c2ecf20Sopenharmony_ci	cq_cfg.qsize = ilog2(qs->cq_len >> 10);
8488c2ecf20Sopenharmony_ci	cq_cfg.avg_con = 0;
8498c2ecf20Sopenharmony_ci	nicvf_queue_reg_write(nic, NIC_QSET_CQ_0_7_CFG, qidx, *(u64 *)&cq_cfg);
8508c2ecf20Sopenharmony_ci
8518c2ecf20Sopenharmony_ci	/* Set threshold value for interrupt generation */
8528c2ecf20Sopenharmony_ci	nicvf_queue_reg_write(nic, NIC_QSET_CQ_0_7_THRESH, qidx, cq->thresh);
8538c2ecf20Sopenharmony_ci	nicvf_queue_reg_write(nic, NIC_QSET_CQ_0_7_CFG2,
8548c2ecf20Sopenharmony_ci			      qidx, CMP_QUEUE_TIMER_THRESH);
8558c2ecf20Sopenharmony_ci}
8568c2ecf20Sopenharmony_ci
8578c2ecf20Sopenharmony_ci/* Configures transmit queue */
8588c2ecf20Sopenharmony_cistatic void nicvf_snd_queue_config(struct nicvf *nic, struct queue_set *qs,
8598c2ecf20Sopenharmony_ci				   int qidx, bool enable)
8608c2ecf20Sopenharmony_ci{
8618c2ecf20Sopenharmony_ci	union nic_mbx mbx = {};
8628c2ecf20Sopenharmony_ci	struct snd_queue *sq;
8638c2ecf20Sopenharmony_ci	struct sq_cfg sq_cfg;
8648c2ecf20Sopenharmony_ci
8658c2ecf20Sopenharmony_ci	sq = &qs->sq[qidx];
8668c2ecf20Sopenharmony_ci	sq->enable = enable;
8678c2ecf20Sopenharmony_ci
8688c2ecf20Sopenharmony_ci	if (!sq->enable) {
8698c2ecf20Sopenharmony_ci		nicvf_reclaim_snd_queue(nic, qs, qidx);
8708c2ecf20Sopenharmony_ci		return;
8718c2ecf20Sopenharmony_ci	}
8728c2ecf20Sopenharmony_ci
8738c2ecf20Sopenharmony_ci	/* Reset send queue */
8748c2ecf20Sopenharmony_ci	nicvf_queue_reg_write(nic, NIC_QSET_SQ_0_7_CFG, qidx, NICVF_SQ_RESET);
8758c2ecf20Sopenharmony_ci
8768c2ecf20Sopenharmony_ci	sq->cq_qs = qs->vnic_id;
8778c2ecf20Sopenharmony_ci	sq->cq_idx = qidx;
8788c2ecf20Sopenharmony_ci
8798c2ecf20Sopenharmony_ci	/* Send a mailbox msg to PF to config SQ */
8808c2ecf20Sopenharmony_ci	mbx.sq.msg = NIC_MBOX_MSG_SQ_CFG;
8818c2ecf20Sopenharmony_ci	mbx.sq.qs_num = qs->vnic_id;
8828c2ecf20Sopenharmony_ci	mbx.sq.sq_num = qidx;
8838c2ecf20Sopenharmony_ci	mbx.sq.sqs_mode = nic->sqs_mode;
8848c2ecf20Sopenharmony_ci	mbx.sq.cfg = (sq->cq_qs << 3) | sq->cq_idx;
8858c2ecf20Sopenharmony_ci	nicvf_send_msg_to_pf(nic, &mbx);
8868c2ecf20Sopenharmony_ci
8878c2ecf20Sopenharmony_ci	/* Set queue base address */
8888c2ecf20Sopenharmony_ci	nicvf_queue_reg_write(nic, NIC_QSET_SQ_0_7_BASE,
8898c2ecf20Sopenharmony_ci			      qidx, (u64)(sq->dmem.phys_base));
8908c2ecf20Sopenharmony_ci
8918c2ecf20Sopenharmony_ci	/* Enable send queue  & set queue size */
8928c2ecf20Sopenharmony_ci	memset(&sq_cfg, 0, sizeof(struct sq_cfg));
8938c2ecf20Sopenharmony_ci	sq_cfg.ena = 1;
8948c2ecf20Sopenharmony_ci	sq_cfg.reset = 0;
8958c2ecf20Sopenharmony_ci	sq_cfg.ldwb = 0;
8968c2ecf20Sopenharmony_ci	sq_cfg.qsize = ilog2(qs->sq_len >> 10);
8978c2ecf20Sopenharmony_ci	sq_cfg.tstmp_bgx_intf = 0;
8988c2ecf20Sopenharmony_ci	/* CQ's level at which HW will stop processing SQEs to avoid
8998c2ecf20Sopenharmony_ci	 * transmitting a pkt with no space in CQ to post CQE_TX.
9008c2ecf20Sopenharmony_ci	 */
9018c2ecf20Sopenharmony_ci	sq_cfg.cq_limit = (CMP_QUEUE_PIPELINE_RSVD * 256) / qs->cq_len;
9028c2ecf20Sopenharmony_ci	nicvf_queue_reg_write(nic, NIC_QSET_SQ_0_7_CFG, qidx, *(u64 *)&sq_cfg);
9038c2ecf20Sopenharmony_ci
9048c2ecf20Sopenharmony_ci	/* Set threshold value for interrupt generation */
9058c2ecf20Sopenharmony_ci	nicvf_queue_reg_write(nic, NIC_QSET_SQ_0_7_THRESH, qidx, sq->thresh);
9068c2ecf20Sopenharmony_ci
9078c2ecf20Sopenharmony_ci	/* Set queue:cpu affinity for better load distribution */
9088c2ecf20Sopenharmony_ci	if (cpu_online(qidx)) {
9098c2ecf20Sopenharmony_ci		cpumask_set_cpu(qidx, &sq->affinity_mask);
9108c2ecf20Sopenharmony_ci		netif_set_xps_queue(nic->netdev,
9118c2ecf20Sopenharmony_ci				    &sq->affinity_mask, qidx);
9128c2ecf20Sopenharmony_ci	}
9138c2ecf20Sopenharmony_ci}
9148c2ecf20Sopenharmony_ci
9158c2ecf20Sopenharmony_ci/* Configures receive buffer descriptor ring */
9168c2ecf20Sopenharmony_cistatic void nicvf_rbdr_config(struct nicvf *nic, struct queue_set *qs,
9178c2ecf20Sopenharmony_ci			      int qidx, bool enable)
9188c2ecf20Sopenharmony_ci{
9198c2ecf20Sopenharmony_ci	struct rbdr *rbdr;
9208c2ecf20Sopenharmony_ci	struct rbdr_cfg rbdr_cfg;
9218c2ecf20Sopenharmony_ci
9228c2ecf20Sopenharmony_ci	rbdr = &qs->rbdr[qidx];
9238c2ecf20Sopenharmony_ci	nicvf_reclaim_rbdr(nic, rbdr, qidx);
9248c2ecf20Sopenharmony_ci	if (!enable)
9258c2ecf20Sopenharmony_ci		return;
9268c2ecf20Sopenharmony_ci
9278c2ecf20Sopenharmony_ci	/* Set descriptor base address */
9288c2ecf20Sopenharmony_ci	nicvf_queue_reg_write(nic, NIC_QSET_RBDR_0_1_BASE,
9298c2ecf20Sopenharmony_ci			      qidx, (u64)(rbdr->dmem.phys_base));
9308c2ecf20Sopenharmony_ci
9318c2ecf20Sopenharmony_ci	/* Enable RBDR  & set queue size */
9328c2ecf20Sopenharmony_ci	/* Buffer size should be in multiples of 128 bytes */
9338c2ecf20Sopenharmony_ci	memset(&rbdr_cfg, 0, sizeof(struct rbdr_cfg));
9348c2ecf20Sopenharmony_ci	rbdr_cfg.ena = 1;
9358c2ecf20Sopenharmony_ci	rbdr_cfg.reset = 0;
9368c2ecf20Sopenharmony_ci	rbdr_cfg.ldwb = 0;
9378c2ecf20Sopenharmony_ci	rbdr_cfg.qsize = RBDR_SIZE;
9388c2ecf20Sopenharmony_ci	rbdr_cfg.avg_con = 0;
9398c2ecf20Sopenharmony_ci	rbdr_cfg.lines = rbdr->dma_size / 128;
9408c2ecf20Sopenharmony_ci	nicvf_queue_reg_write(nic, NIC_QSET_RBDR_0_1_CFG,
9418c2ecf20Sopenharmony_ci			      qidx, *(u64 *)&rbdr_cfg);
9428c2ecf20Sopenharmony_ci
9438c2ecf20Sopenharmony_ci	/* Notify HW */
9448c2ecf20Sopenharmony_ci	nicvf_queue_reg_write(nic, NIC_QSET_RBDR_0_1_DOOR,
9458c2ecf20Sopenharmony_ci			      qidx, qs->rbdr_len - 1);
9468c2ecf20Sopenharmony_ci
9478c2ecf20Sopenharmony_ci	/* Set threshold value for interrupt generation */
9488c2ecf20Sopenharmony_ci	nicvf_queue_reg_write(nic, NIC_QSET_RBDR_0_1_THRESH,
9498c2ecf20Sopenharmony_ci			      qidx, rbdr->thresh - 1);
9508c2ecf20Sopenharmony_ci}
9518c2ecf20Sopenharmony_ci
9528c2ecf20Sopenharmony_ci/* Requests PF to assign and enable Qset */
9538c2ecf20Sopenharmony_civoid nicvf_qset_config(struct nicvf *nic, bool enable)
9548c2ecf20Sopenharmony_ci{
9558c2ecf20Sopenharmony_ci	union nic_mbx mbx = {};
9568c2ecf20Sopenharmony_ci	struct queue_set *qs = nic->qs;
9578c2ecf20Sopenharmony_ci	struct qs_cfg *qs_cfg;
9588c2ecf20Sopenharmony_ci
9598c2ecf20Sopenharmony_ci	if (!qs) {
9608c2ecf20Sopenharmony_ci		netdev_warn(nic->netdev,
9618c2ecf20Sopenharmony_ci			    "Qset is still not allocated, don't init queues\n");
9628c2ecf20Sopenharmony_ci		return;
9638c2ecf20Sopenharmony_ci	}
9648c2ecf20Sopenharmony_ci
9658c2ecf20Sopenharmony_ci	qs->enable = enable;
9668c2ecf20Sopenharmony_ci	qs->vnic_id = nic->vf_id;
9678c2ecf20Sopenharmony_ci
9688c2ecf20Sopenharmony_ci	/* Send a mailbox msg to PF to config Qset */
9698c2ecf20Sopenharmony_ci	mbx.qs.msg = NIC_MBOX_MSG_QS_CFG;
9708c2ecf20Sopenharmony_ci	mbx.qs.num = qs->vnic_id;
9718c2ecf20Sopenharmony_ci	mbx.qs.sqs_count = nic->sqs_count;
9728c2ecf20Sopenharmony_ci
9738c2ecf20Sopenharmony_ci	mbx.qs.cfg = 0;
9748c2ecf20Sopenharmony_ci	qs_cfg = (struct qs_cfg *)&mbx.qs.cfg;
9758c2ecf20Sopenharmony_ci	if (qs->enable) {
9768c2ecf20Sopenharmony_ci		qs_cfg->ena = 1;
9778c2ecf20Sopenharmony_ci#ifdef __BIG_ENDIAN
9788c2ecf20Sopenharmony_ci		qs_cfg->be = 1;
9798c2ecf20Sopenharmony_ci#endif
9808c2ecf20Sopenharmony_ci		qs_cfg->vnic = qs->vnic_id;
9818c2ecf20Sopenharmony_ci		/* Enable Tx timestamping capability */
9828c2ecf20Sopenharmony_ci		if (nic->ptp_clock)
9838c2ecf20Sopenharmony_ci			qs_cfg->send_tstmp_ena = 1;
9848c2ecf20Sopenharmony_ci	}
9858c2ecf20Sopenharmony_ci	nicvf_send_msg_to_pf(nic, &mbx);
9868c2ecf20Sopenharmony_ci}
9878c2ecf20Sopenharmony_ci
9888c2ecf20Sopenharmony_cistatic void nicvf_free_resources(struct nicvf *nic)
9898c2ecf20Sopenharmony_ci{
9908c2ecf20Sopenharmony_ci	int qidx;
9918c2ecf20Sopenharmony_ci	struct queue_set *qs = nic->qs;
9928c2ecf20Sopenharmony_ci
9938c2ecf20Sopenharmony_ci	/* Free receive buffer descriptor ring */
9948c2ecf20Sopenharmony_ci	for (qidx = 0; qidx < qs->rbdr_cnt; qidx++)
9958c2ecf20Sopenharmony_ci		nicvf_free_rbdr(nic, &qs->rbdr[qidx]);
9968c2ecf20Sopenharmony_ci
9978c2ecf20Sopenharmony_ci	/* Free completion queue */
9988c2ecf20Sopenharmony_ci	for (qidx = 0; qidx < qs->cq_cnt; qidx++)
9998c2ecf20Sopenharmony_ci		nicvf_free_cmp_queue(nic, &qs->cq[qidx]);
10008c2ecf20Sopenharmony_ci
10018c2ecf20Sopenharmony_ci	/* Free send queue */
10028c2ecf20Sopenharmony_ci	for (qidx = 0; qidx < qs->sq_cnt; qidx++)
10038c2ecf20Sopenharmony_ci		nicvf_free_snd_queue(nic, &qs->sq[qidx]);
10048c2ecf20Sopenharmony_ci}
10058c2ecf20Sopenharmony_ci
10068c2ecf20Sopenharmony_cistatic int nicvf_alloc_resources(struct nicvf *nic)
10078c2ecf20Sopenharmony_ci{
10088c2ecf20Sopenharmony_ci	int qidx;
10098c2ecf20Sopenharmony_ci	struct queue_set *qs = nic->qs;
10108c2ecf20Sopenharmony_ci
10118c2ecf20Sopenharmony_ci	/* Alloc receive buffer descriptor ring */
10128c2ecf20Sopenharmony_ci	for (qidx = 0; qidx < qs->rbdr_cnt; qidx++) {
10138c2ecf20Sopenharmony_ci		if (nicvf_init_rbdr(nic, &qs->rbdr[qidx], qs->rbdr_len,
10148c2ecf20Sopenharmony_ci				    DMA_BUFFER_LEN))
10158c2ecf20Sopenharmony_ci			goto alloc_fail;
10168c2ecf20Sopenharmony_ci	}
10178c2ecf20Sopenharmony_ci
10188c2ecf20Sopenharmony_ci	/* Alloc send queue */
10198c2ecf20Sopenharmony_ci	for (qidx = 0; qidx < qs->sq_cnt; qidx++) {
10208c2ecf20Sopenharmony_ci		if (nicvf_init_snd_queue(nic, &qs->sq[qidx], qs->sq_len, qidx))
10218c2ecf20Sopenharmony_ci			goto alloc_fail;
10228c2ecf20Sopenharmony_ci	}
10238c2ecf20Sopenharmony_ci
10248c2ecf20Sopenharmony_ci	/* Alloc completion queue */
10258c2ecf20Sopenharmony_ci	for (qidx = 0; qidx < qs->cq_cnt; qidx++) {
10268c2ecf20Sopenharmony_ci		if (nicvf_init_cmp_queue(nic, &qs->cq[qidx], qs->cq_len))
10278c2ecf20Sopenharmony_ci			goto alloc_fail;
10288c2ecf20Sopenharmony_ci	}
10298c2ecf20Sopenharmony_ci
10308c2ecf20Sopenharmony_ci	return 0;
10318c2ecf20Sopenharmony_cialloc_fail:
10328c2ecf20Sopenharmony_ci	nicvf_free_resources(nic);
10338c2ecf20Sopenharmony_ci	return -ENOMEM;
10348c2ecf20Sopenharmony_ci}
10358c2ecf20Sopenharmony_ci
10368c2ecf20Sopenharmony_ciint nicvf_set_qset_resources(struct nicvf *nic)
10378c2ecf20Sopenharmony_ci{
10388c2ecf20Sopenharmony_ci	struct queue_set *qs;
10398c2ecf20Sopenharmony_ci
10408c2ecf20Sopenharmony_ci	qs = devm_kzalloc(&nic->pdev->dev, sizeof(*qs), GFP_KERNEL);
10418c2ecf20Sopenharmony_ci	if (!qs)
10428c2ecf20Sopenharmony_ci		return -ENOMEM;
10438c2ecf20Sopenharmony_ci	nic->qs = qs;
10448c2ecf20Sopenharmony_ci
10458c2ecf20Sopenharmony_ci	/* Set count of each queue */
10468c2ecf20Sopenharmony_ci	qs->rbdr_cnt = DEFAULT_RBDR_CNT;
10478c2ecf20Sopenharmony_ci	qs->rq_cnt = min_t(u8, MAX_RCV_QUEUES_PER_QS, num_online_cpus());
10488c2ecf20Sopenharmony_ci	qs->sq_cnt = min_t(u8, MAX_SND_QUEUES_PER_QS, num_online_cpus());
10498c2ecf20Sopenharmony_ci	qs->cq_cnt = max_t(u8, qs->rq_cnt, qs->sq_cnt);
10508c2ecf20Sopenharmony_ci
10518c2ecf20Sopenharmony_ci	/* Set queue lengths */
10528c2ecf20Sopenharmony_ci	qs->rbdr_len = RCV_BUF_COUNT;
10538c2ecf20Sopenharmony_ci	qs->sq_len = SND_QUEUE_LEN;
10548c2ecf20Sopenharmony_ci	qs->cq_len = CMP_QUEUE_LEN;
10558c2ecf20Sopenharmony_ci
10568c2ecf20Sopenharmony_ci	nic->rx_queues = qs->rq_cnt;
10578c2ecf20Sopenharmony_ci	nic->tx_queues = qs->sq_cnt;
10588c2ecf20Sopenharmony_ci	nic->xdp_tx_queues = 0;
10598c2ecf20Sopenharmony_ci
10608c2ecf20Sopenharmony_ci	return 0;
10618c2ecf20Sopenharmony_ci}
10628c2ecf20Sopenharmony_ci
10638c2ecf20Sopenharmony_ciint nicvf_config_data_transfer(struct nicvf *nic, bool enable)
10648c2ecf20Sopenharmony_ci{
10658c2ecf20Sopenharmony_ci	bool disable = false;
10668c2ecf20Sopenharmony_ci	struct queue_set *qs = nic->qs;
10678c2ecf20Sopenharmony_ci	struct queue_set *pqs = nic->pnicvf->qs;
10688c2ecf20Sopenharmony_ci	int qidx;
10698c2ecf20Sopenharmony_ci
10708c2ecf20Sopenharmony_ci	if (!qs)
10718c2ecf20Sopenharmony_ci		return 0;
10728c2ecf20Sopenharmony_ci
10738c2ecf20Sopenharmony_ci	/* Take primary VF's queue lengths.
10748c2ecf20Sopenharmony_ci	 * This is needed to take queue lengths set from ethtool
10758c2ecf20Sopenharmony_ci	 * into consideration.
10768c2ecf20Sopenharmony_ci	 */
10778c2ecf20Sopenharmony_ci	if (nic->sqs_mode && pqs) {
10788c2ecf20Sopenharmony_ci		qs->cq_len = pqs->cq_len;
10798c2ecf20Sopenharmony_ci		qs->sq_len = pqs->sq_len;
10808c2ecf20Sopenharmony_ci	}
10818c2ecf20Sopenharmony_ci
10828c2ecf20Sopenharmony_ci	if (enable) {
10838c2ecf20Sopenharmony_ci		if (nicvf_alloc_resources(nic))
10848c2ecf20Sopenharmony_ci			return -ENOMEM;
10858c2ecf20Sopenharmony_ci
10868c2ecf20Sopenharmony_ci		for (qidx = 0; qidx < qs->sq_cnt; qidx++)
10878c2ecf20Sopenharmony_ci			nicvf_snd_queue_config(nic, qs, qidx, enable);
10888c2ecf20Sopenharmony_ci		for (qidx = 0; qidx < qs->cq_cnt; qidx++)
10898c2ecf20Sopenharmony_ci			nicvf_cmp_queue_config(nic, qs, qidx, enable);
10908c2ecf20Sopenharmony_ci		for (qidx = 0; qidx < qs->rbdr_cnt; qidx++)
10918c2ecf20Sopenharmony_ci			nicvf_rbdr_config(nic, qs, qidx, enable);
10928c2ecf20Sopenharmony_ci		for (qidx = 0; qidx < qs->rq_cnt; qidx++)
10938c2ecf20Sopenharmony_ci			nicvf_rcv_queue_config(nic, qs, qidx, enable);
10948c2ecf20Sopenharmony_ci	} else {
10958c2ecf20Sopenharmony_ci		for (qidx = 0; qidx < qs->rq_cnt; qidx++)
10968c2ecf20Sopenharmony_ci			nicvf_rcv_queue_config(nic, qs, qidx, disable);
10978c2ecf20Sopenharmony_ci		for (qidx = 0; qidx < qs->rbdr_cnt; qidx++)
10988c2ecf20Sopenharmony_ci			nicvf_rbdr_config(nic, qs, qidx, disable);
10998c2ecf20Sopenharmony_ci		for (qidx = 0; qidx < qs->sq_cnt; qidx++)
11008c2ecf20Sopenharmony_ci			nicvf_snd_queue_config(nic, qs, qidx, disable);
11018c2ecf20Sopenharmony_ci		for (qidx = 0; qidx < qs->cq_cnt; qidx++)
11028c2ecf20Sopenharmony_ci			nicvf_cmp_queue_config(nic, qs, qidx, disable);
11038c2ecf20Sopenharmony_ci
11048c2ecf20Sopenharmony_ci		nicvf_free_resources(nic);
11058c2ecf20Sopenharmony_ci	}
11068c2ecf20Sopenharmony_ci
11078c2ecf20Sopenharmony_ci	/* Reset RXQ's stats.
11088c2ecf20Sopenharmony_ci	 * SQ's stats will get reset automatically once SQ is reset.
11098c2ecf20Sopenharmony_ci	 */
11108c2ecf20Sopenharmony_ci	nicvf_reset_rcv_queue_stats(nic);
11118c2ecf20Sopenharmony_ci
11128c2ecf20Sopenharmony_ci	return 0;
11138c2ecf20Sopenharmony_ci}
11148c2ecf20Sopenharmony_ci
11158c2ecf20Sopenharmony_ci/* Get a free desc from SQ
11168c2ecf20Sopenharmony_ci * returns descriptor ponter & descriptor number
11178c2ecf20Sopenharmony_ci */
11188c2ecf20Sopenharmony_cistatic inline int nicvf_get_sq_desc(struct snd_queue *sq, int desc_cnt)
11198c2ecf20Sopenharmony_ci{
11208c2ecf20Sopenharmony_ci	int qentry;
11218c2ecf20Sopenharmony_ci
11228c2ecf20Sopenharmony_ci	qentry = sq->tail;
11238c2ecf20Sopenharmony_ci	if (!sq->is_xdp)
11248c2ecf20Sopenharmony_ci		atomic_sub(desc_cnt, &sq->free_cnt);
11258c2ecf20Sopenharmony_ci	else
11268c2ecf20Sopenharmony_ci		sq->xdp_free_cnt -= desc_cnt;
11278c2ecf20Sopenharmony_ci	sq->tail += desc_cnt;
11288c2ecf20Sopenharmony_ci	sq->tail &= (sq->dmem.q_len - 1);
11298c2ecf20Sopenharmony_ci
11308c2ecf20Sopenharmony_ci	return qentry;
11318c2ecf20Sopenharmony_ci}
11328c2ecf20Sopenharmony_ci
11338c2ecf20Sopenharmony_ci/* Rollback to previous tail pointer when descriptors not used */
11348c2ecf20Sopenharmony_cistatic inline void nicvf_rollback_sq_desc(struct snd_queue *sq,
11358c2ecf20Sopenharmony_ci					  int qentry, int desc_cnt)
11368c2ecf20Sopenharmony_ci{
11378c2ecf20Sopenharmony_ci	sq->tail = qentry;
11388c2ecf20Sopenharmony_ci	atomic_add(desc_cnt, &sq->free_cnt);
11398c2ecf20Sopenharmony_ci}
11408c2ecf20Sopenharmony_ci
11418c2ecf20Sopenharmony_ci/* Free descriptor back to SQ for future use */
11428c2ecf20Sopenharmony_civoid nicvf_put_sq_desc(struct snd_queue *sq, int desc_cnt)
11438c2ecf20Sopenharmony_ci{
11448c2ecf20Sopenharmony_ci	if (!sq->is_xdp)
11458c2ecf20Sopenharmony_ci		atomic_add(desc_cnt, &sq->free_cnt);
11468c2ecf20Sopenharmony_ci	else
11478c2ecf20Sopenharmony_ci		sq->xdp_free_cnt += desc_cnt;
11488c2ecf20Sopenharmony_ci	sq->head += desc_cnt;
11498c2ecf20Sopenharmony_ci	sq->head &= (sq->dmem.q_len - 1);
11508c2ecf20Sopenharmony_ci}
11518c2ecf20Sopenharmony_ci
11528c2ecf20Sopenharmony_cistatic inline int nicvf_get_nxt_sqentry(struct snd_queue *sq, int qentry)
11538c2ecf20Sopenharmony_ci{
11548c2ecf20Sopenharmony_ci	qentry++;
11558c2ecf20Sopenharmony_ci	qentry &= (sq->dmem.q_len - 1);
11568c2ecf20Sopenharmony_ci	return qentry;
11578c2ecf20Sopenharmony_ci}
11588c2ecf20Sopenharmony_ci
11598c2ecf20Sopenharmony_civoid nicvf_sq_enable(struct nicvf *nic, struct snd_queue *sq, int qidx)
11608c2ecf20Sopenharmony_ci{
11618c2ecf20Sopenharmony_ci	u64 sq_cfg;
11628c2ecf20Sopenharmony_ci
11638c2ecf20Sopenharmony_ci	sq_cfg = nicvf_queue_reg_read(nic, NIC_QSET_SQ_0_7_CFG, qidx);
11648c2ecf20Sopenharmony_ci	sq_cfg |= NICVF_SQ_EN;
11658c2ecf20Sopenharmony_ci	nicvf_queue_reg_write(nic, NIC_QSET_SQ_0_7_CFG, qidx, sq_cfg);
11668c2ecf20Sopenharmony_ci	/* Ring doorbell so that H/W restarts processing SQEs */
11678c2ecf20Sopenharmony_ci	nicvf_queue_reg_write(nic, NIC_QSET_SQ_0_7_DOOR, qidx, 0);
11688c2ecf20Sopenharmony_ci}
11698c2ecf20Sopenharmony_ci
11708c2ecf20Sopenharmony_civoid nicvf_sq_disable(struct nicvf *nic, int qidx)
11718c2ecf20Sopenharmony_ci{
11728c2ecf20Sopenharmony_ci	u64 sq_cfg;
11738c2ecf20Sopenharmony_ci
11748c2ecf20Sopenharmony_ci	sq_cfg = nicvf_queue_reg_read(nic, NIC_QSET_SQ_0_7_CFG, qidx);
11758c2ecf20Sopenharmony_ci	sq_cfg &= ~NICVF_SQ_EN;
11768c2ecf20Sopenharmony_ci	nicvf_queue_reg_write(nic, NIC_QSET_SQ_0_7_CFG, qidx, sq_cfg);
11778c2ecf20Sopenharmony_ci}
11788c2ecf20Sopenharmony_ci
11798c2ecf20Sopenharmony_civoid nicvf_sq_free_used_descs(struct net_device *netdev, struct snd_queue *sq,
11808c2ecf20Sopenharmony_ci			      int qidx)
11818c2ecf20Sopenharmony_ci{
11828c2ecf20Sopenharmony_ci	u64 head;
11838c2ecf20Sopenharmony_ci	struct sk_buff *skb;
11848c2ecf20Sopenharmony_ci	struct nicvf *nic = netdev_priv(netdev);
11858c2ecf20Sopenharmony_ci	struct sq_hdr_subdesc *hdr;
11868c2ecf20Sopenharmony_ci
11878c2ecf20Sopenharmony_ci	head = nicvf_queue_reg_read(nic, NIC_QSET_SQ_0_7_HEAD, qidx) >> 4;
11888c2ecf20Sopenharmony_ci	while (sq->head != head) {
11898c2ecf20Sopenharmony_ci		hdr = (struct sq_hdr_subdesc *)GET_SQ_DESC(sq, sq->head);
11908c2ecf20Sopenharmony_ci		if (hdr->subdesc_type != SQ_DESC_TYPE_HEADER) {
11918c2ecf20Sopenharmony_ci			nicvf_put_sq_desc(sq, 1);
11928c2ecf20Sopenharmony_ci			continue;
11938c2ecf20Sopenharmony_ci		}
11948c2ecf20Sopenharmony_ci		skb = (struct sk_buff *)sq->skbuff[sq->head];
11958c2ecf20Sopenharmony_ci		if (skb)
11968c2ecf20Sopenharmony_ci			dev_kfree_skb_any(skb);
11978c2ecf20Sopenharmony_ci		atomic64_add(1, (atomic64_t *)&netdev->stats.tx_packets);
11988c2ecf20Sopenharmony_ci		atomic64_add(hdr->tot_len,
11998c2ecf20Sopenharmony_ci			     (atomic64_t *)&netdev->stats.tx_bytes);
12008c2ecf20Sopenharmony_ci		nicvf_put_sq_desc(sq, hdr->subdesc_cnt + 1);
12018c2ecf20Sopenharmony_ci	}
12028c2ecf20Sopenharmony_ci}
12038c2ecf20Sopenharmony_ci
12048c2ecf20Sopenharmony_ci/* XDP Transmit APIs */
12058c2ecf20Sopenharmony_civoid nicvf_xdp_sq_doorbell(struct nicvf *nic,
12068c2ecf20Sopenharmony_ci			   struct snd_queue *sq, int sq_num)
12078c2ecf20Sopenharmony_ci{
12088c2ecf20Sopenharmony_ci	if (!sq->xdp_desc_cnt)
12098c2ecf20Sopenharmony_ci		return;
12108c2ecf20Sopenharmony_ci
12118c2ecf20Sopenharmony_ci	/* make sure all memory stores are done before ringing doorbell */
12128c2ecf20Sopenharmony_ci	wmb();
12138c2ecf20Sopenharmony_ci
12148c2ecf20Sopenharmony_ci	/* Inform HW to xmit all TSO segments */
12158c2ecf20Sopenharmony_ci	nicvf_queue_reg_write(nic, NIC_QSET_SQ_0_7_DOOR,
12168c2ecf20Sopenharmony_ci			      sq_num, sq->xdp_desc_cnt);
12178c2ecf20Sopenharmony_ci	sq->xdp_desc_cnt = 0;
12188c2ecf20Sopenharmony_ci}
12198c2ecf20Sopenharmony_ci
12208c2ecf20Sopenharmony_cistatic inline void
12218c2ecf20Sopenharmony_cinicvf_xdp_sq_add_hdr_subdesc(struct snd_queue *sq, int qentry,
12228c2ecf20Sopenharmony_ci			     int subdesc_cnt, u64 data, int len)
12238c2ecf20Sopenharmony_ci{
12248c2ecf20Sopenharmony_ci	struct sq_hdr_subdesc *hdr;
12258c2ecf20Sopenharmony_ci
12268c2ecf20Sopenharmony_ci	hdr = (struct sq_hdr_subdesc *)GET_SQ_DESC(sq, qentry);
12278c2ecf20Sopenharmony_ci	memset(hdr, 0, SND_QUEUE_DESC_SIZE);
12288c2ecf20Sopenharmony_ci	hdr->subdesc_type = SQ_DESC_TYPE_HEADER;
12298c2ecf20Sopenharmony_ci	hdr->subdesc_cnt = subdesc_cnt;
12308c2ecf20Sopenharmony_ci	hdr->tot_len = len;
12318c2ecf20Sopenharmony_ci	hdr->post_cqe = 1;
12328c2ecf20Sopenharmony_ci	sq->xdp_page[qentry] = (u64)virt_to_page((void *)data);
12338c2ecf20Sopenharmony_ci}
12348c2ecf20Sopenharmony_ci
12358c2ecf20Sopenharmony_ciint nicvf_xdp_sq_append_pkt(struct nicvf *nic, struct snd_queue *sq,
12368c2ecf20Sopenharmony_ci			    u64 bufaddr, u64 dma_addr, u16 len)
12378c2ecf20Sopenharmony_ci{
12388c2ecf20Sopenharmony_ci	int subdesc_cnt = MIN_SQ_DESC_PER_PKT_XMIT;
12398c2ecf20Sopenharmony_ci	int qentry;
12408c2ecf20Sopenharmony_ci
12418c2ecf20Sopenharmony_ci	if (subdesc_cnt > sq->xdp_free_cnt)
12428c2ecf20Sopenharmony_ci		return 0;
12438c2ecf20Sopenharmony_ci
12448c2ecf20Sopenharmony_ci	qentry = nicvf_get_sq_desc(sq, subdesc_cnt);
12458c2ecf20Sopenharmony_ci
12468c2ecf20Sopenharmony_ci	nicvf_xdp_sq_add_hdr_subdesc(sq, qentry, subdesc_cnt - 1, bufaddr, len);
12478c2ecf20Sopenharmony_ci
12488c2ecf20Sopenharmony_ci	qentry = nicvf_get_nxt_sqentry(sq, qentry);
12498c2ecf20Sopenharmony_ci	nicvf_sq_add_gather_subdesc(sq, qentry, len, dma_addr);
12508c2ecf20Sopenharmony_ci
12518c2ecf20Sopenharmony_ci	sq->xdp_desc_cnt += subdesc_cnt;
12528c2ecf20Sopenharmony_ci
12538c2ecf20Sopenharmony_ci	return 1;
12548c2ecf20Sopenharmony_ci}
12558c2ecf20Sopenharmony_ci
12568c2ecf20Sopenharmony_ci/* Calculate no of SQ subdescriptors needed to transmit all
12578c2ecf20Sopenharmony_ci * segments of this TSO packet.
12588c2ecf20Sopenharmony_ci * Taken from 'Tilera network driver' with a minor modification.
12598c2ecf20Sopenharmony_ci */
12608c2ecf20Sopenharmony_cistatic int nicvf_tso_count_subdescs(struct sk_buff *skb)
12618c2ecf20Sopenharmony_ci{
12628c2ecf20Sopenharmony_ci	struct skb_shared_info *sh = skb_shinfo(skb);
12638c2ecf20Sopenharmony_ci	unsigned int sh_len = skb_transport_offset(skb) + tcp_hdrlen(skb);
12648c2ecf20Sopenharmony_ci	unsigned int data_len = skb->len - sh_len;
12658c2ecf20Sopenharmony_ci	unsigned int p_len = sh->gso_size;
12668c2ecf20Sopenharmony_ci	long f_id = -1;    /* id of the current fragment */
12678c2ecf20Sopenharmony_ci	long f_size = skb_headlen(skb) - sh_len;  /* current fragment size */
12688c2ecf20Sopenharmony_ci	long f_used = 0;  /* bytes used from the current fragment */
12698c2ecf20Sopenharmony_ci	long n;            /* size of the current piece of payload */
12708c2ecf20Sopenharmony_ci	int num_edescs = 0;
12718c2ecf20Sopenharmony_ci	int segment;
12728c2ecf20Sopenharmony_ci
12738c2ecf20Sopenharmony_ci	for (segment = 0; segment < sh->gso_segs; segment++) {
12748c2ecf20Sopenharmony_ci		unsigned int p_used = 0;
12758c2ecf20Sopenharmony_ci
12768c2ecf20Sopenharmony_ci		/* One edesc for header and for each piece of the payload. */
12778c2ecf20Sopenharmony_ci		for (num_edescs++; p_used < p_len; num_edescs++) {
12788c2ecf20Sopenharmony_ci			/* Advance as needed. */
12798c2ecf20Sopenharmony_ci			while (f_used >= f_size) {
12808c2ecf20Sopenharmony_ci				f_id++;
12818c2ecf20Sopenharmony_ci				f_size = skb_frag_size(&sh->frags[f_id]);
12828c2ecf20Sopenharmony_ci				f_used = 0;
12838c2ecf20Sopenharmony_ci			}
12848c2ecf20Sopenharmony_ci
12858c2ecf20Sopenharmony_ci			/* Use bytes from the current fragment. */
12868c2ecf20Sopenharmony_ci			n = p_len - p_used;
12878c2ecf20Sopenharmony_ci			if (n > f_size - f_used)
12888c2ecf20Sopenharmony_ci				n = f_size - f_used;
12898c2ecf20Sopenharmony_ci			f_used += n;
12908c2ecf20Sopenharmony_ci			p_used += n;
12918c2ecf20Sopenharmony_ci		}
12928c2ecf20Sopenharmony_ci
12938c2ecf20Sopenharmony_ci		/* The last segment may be less than gso_size. */
12948c2ecf20Sopenharmony_ci		data_len -= p_len;
12958c2ecf20Sopenharmony_ci		if (data_len < p_len)
12968c2ecf20Sopenharmony_ci			p_len = data_len;
12978c2ecf20Sopenharmony_ci	}
12988c2ecf20Sopenharmony_ci
12998c2ecf20Sopenharmony_ci	/* '+ gso_segs' for SQ_HDR_SUDESCs for each segment */
13008c2ecf20Sopenharmony_ci	return num_edescs + sh->gso_segs;
13018c2ecf20Sopenharmony_ci}
13028c2ecf20Sopenharmony_ci
13038c2ecf20Sopenharmony_ci#define POST_CQE_DESC_COUNT 2
13048c2ecf20Sopenharmony_ci
13058c2ecf20Sopenharmony_ci/* Get the number of SQ descriptors needed to xmit this skb */
13068c2ecf20Sopenharmony_cistatic int nicvf_sq_subdesc_required(struct nicvf *nic, struct sk_buff *skb)
13078c2ecf20Sopenharmony_ci{
13088c2ecf20Sopenharmony_ci	int subdesc_cnt = MIN_SQ_DESC_PER_PKT_XMIT;
13098c2ecf20Sopenharmony_ci
13108c2ecf20Sopenharmony_ci	if (skb_shinfo(skb)->gso_size && !nic->hw_tso) {
13118c2ecf20Sopenharmony_ci		subdesc_cnt = nicvf_tso_count_subdescs(skb);
13128c2ecf20Sopenharmony_ci		return subdesc_cnt;
13138c2ecf20Sopenharmony_ci	}
13148c2ecf20Sopenharmony_ci
13158c2ecf20Sopenharmony_ci	/* Dummy descriptors to get TSO pkt completion notification */
13168c2ecf20Sopenharmony_ci	if (nic->t88 && nic->hw_tso && skb_shinfo(skb)->gso_size)
13178c2ecf20Sopenharmony_ci		subdesc_cnt += POST_CQE_DESC_COUNT;
13188c2ecf20Sopenharmony_ci
13198c2ecf20Sopenharmony_ci	if (skb_shinfo(skb)->nr_frags)
13208c2ecf20Sopenharmony_ci		subdesc_cnt += skb_shinfo(skb)->nr_frags;
13218c2ecf20Sopenharmony_ci
13228c2ecf20Sopenharmony_ci	return subdesc_cnt;
13238c2ecf20Sopenharmony_ci}
13248c2ecf20Sopenharmony_ci
13258c2ecf20Sopenharmony_ci/* Add SQ HEADER subdescriptor.
13268c2ecf20Sopenharmony_ci * First subdescriptor for every send descriptor.
13278c2ecf20Sopenharmony_ci */
13288c2ecf20Sopenharmony_cistatic inline void
13298c2ecf20Sopenharmony_cinicvf_sq_add_hdr_subdesc(struct nicvf *nic, struct snd_queue *sq, int qentry,
13308c2ecf20Sopenharmony_ci			 int subdesc_cnt, struct sk_buff *skb, int len)
13318c2ecf20Sopenharmony_ci{
13328c2ecf20Sopenharmony_ci	int proto;
13338c2ecf20Sopenharmony_ci	struct sq_hdr_subdesc *hdr;
13348c2ecf20Sopenharmony_ci	union {
13358c2ecf20Sopenharmony_ci		struct iphdr *v4;
13368c2ecf20Sopenharmony_ci		struct ipv6hdr *v6;
13378c2ecf20Sopenharmony_ci		unsigned char *hdr;
13388c2ecf20Sopenharmony_ci	} ip;
13398c2ecf20Sopenharmony_ci
13408c2ecf20Sopenharmony_ci	ip.hdr = skb_network_header(skb);
13418c2ecf20Sopenharmony_ci	hdr = (struct sq_hdr_subdesc *)GET_SQ_DESC(sq, qentry);
13428c2ecf20Sopenharmony_ci	memset(hdr, 0, SND_QUEUE_DESC_SIZE);
13438c2ecf20Sopenharmony_ci	hdr->subdesc_type = SQ_DESC_TYPE_HEADER;
13448c2ecf20Sopenharmony_ci
13458c2ecf20Sopenharmony_ci	if (nic->t88 && nic->hw_tso && skb_shinfo(skb)->gso_size) {
13468c2ecf20Sopenharmony_ci		/* post_cqe = 0, to avoid HW posting a CQE for every TSO
13478c2ecf20Sopenharmony_ci		 * segment transmitted on 88xx.
13488c2ecf20Sopenharmony_ci		 */
13498c2ecf20Sopenharmony_ci		hdr->subdesc_cnt = subdesc_cnt - POST_CQE_DESC_COUNT;
13508c2ecf20Sopenharmony_ci	} else {
13518c2ecf20Sopenharmony_ci		sq->skbuff[qentry] = (u64)skb;
13528c2ecf20Sopenharmony_ci		/* Enable notification via CQE after processing SQE */
13538c2ecf20Sopenharmony_ci		hdr->post_cqe = 1;
13548c2ecf20Sopenharmony_ci		/* No of subdescriptors following this */
13558c2ecf20Sopenharmony_ci		hdr->subdesc_cnt = subdesc_cnt;
13568c2ecf20Sopenharmony_ci	}
13578c2ecf20Sopenharmony_ci	hdr->tot_len = len;
13588c2ecf20Sopenharmony_ci
13598c2ecf20Sopenharmony_ci	/* Offload checksum calculation to HW */
13608c2ecf20Sopenharmony_ci	if (skb->ip_summed == CHECKSUM_PARTIAL) {
13618c2ecf20Sopenharmony_ci		if (ip.v4->version == 4)
13628c2ecf20Sopenharmony_ci			hdr->csum_l3 = 1; /* Enable IP csum calculation */
13638c2ecf20Sopenharmony_ci		hdr->l3_offset = skb_network_offset(skb);
13648c2ecf20Sopenharmony_ci		hdr->l4_offset = skb_transport_offset(skb);
13658c2ecf20Sopenharmony_ci
13668c2ecf20Sopenharmony_ci		proto = (ip.v4->version == 4) ? ip.v4->protocol :
13678c2ecf20Sopenharmony_ci			ip.v6->nexthdr;
13688c2ecf20Sopenharmony_ci
13698c2ecf20Sopenharmony_ci		switch (proto) {
13708c2ecf20Sopenharmony_ci		case IPPROTO_TCP:
13718c2ecf20Sopenharmony_ci			hdr->csum_l4 = SEND_L4_CSUM_TCP;
13728c2ecf20Sopenharmony_ci			break;
13738c2ecf20Sopenharmony_ci		case IPPROTO_UDP:
13748c2ecf20Sopenharmony_ci			hdr->csum_l4 = SEND_L4_CSUM_UDP;
13758c2ecf20Sopenharmony_ci			break;
13768c2ecf20Sopenharmony_ci		case IPPROTO_SCTP:
13778c2ecf20Sopenharmony_ci			hdr->csum_l4 = SEND_L4_CSUM_SCTP;
13788c2ecf20Sopenharmony_ci			break;
13798c2ecf20Sopenharmony_ci		}
13808c2ecf20Sopenharmony_ci	}
13818c2ecf20Sopenharmony_ci
13828c2ecf20Sopenharmony_ci	if (nic->hw_tso && skb_shinfo(skb)->gso_size) {
13838c2ecf20Sopenharmony_ci		hdr->tso = 1;
13848c2ecf20Sopenharmony_ci		hdr->tso_start = skb_transport_offset(skb) + tcp_hdrlen(skb);
13858c2ecf20Sopenharmony_ci		hdr->tso_max_paysize = skb_shinfo(skb)->gso_size;
13868c2ecf20Sopenharmony_ci		/* For non-tunneled pkts, point this to L2 ethertype */
13878c2ecf20Sopenharmony_ci		hdr->inner_l3_offset = skb_network_offset(skb) - 2;
13888c2ecf20Sopenharmony_ci		this_cpu_inc(nic->pnicvf->drv_stats->tx_tso);
13898c2ecf20Sopenharmony_ci	}
13908c2ecf20Sopenharmony_ci
13918c2ecf20Sopenharmony_ci	/* Check if timestamp is requested */
13928c2ecf20Sopenharmony_ci	if (!(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP)) {
13938c2ecf20Sopenharmony_ci		skb_tx_timestamp(skb);
13948c2ecf20Sopenharmony_ci		return;
13958c2ecf20Sopenharmony_ci	}
13968c2ecf20Sopenharmony_ci
13978c2ecf20Sopenharmony_ci	/* Tx timestamping not supported along with TSO, so ignore request */
13988c2ecf20Sopenharmony_ci	if (skb_shinfo(skb)->gso_size)
13998c2ecf20Sopenharmony_ci		return;
14008c2ecf20Sopenharmony_ci
14018c2ecf20Sopenharmony_ci	/* HW supports only a single outstanding packet to timestamp */
14028c2ecf20Sopenharmony_ci	if (!atomic_add_unless(&nic->pnicvf->tx_ptp_skbs, 1, 1))
14038c2ecf20Sopenharmony_ci		return;
14048c2ecf20Sopenharmony_ci
14058c2ecf20Sopenharmony_ci	/* Mark the SKB for later reference */
14068c2ecf20Sopenharmony_ci	skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;
14078c2ecf20Sopenharmony_ci
14088c2ecf20Sopenharmony_ci	/* Finally enable timestamp generation
14098c2ecf20Sopenharmony_ci	 * Since 'post_cqe' is also set, two CQEs will be posted
14108c2ecf20Sopenharmony_ci	 * for this packet i.e CQE_TYPE_SEND and CQE_TYPE_SEND_PTP.
14118c2ecf20Sopenharmony_ci	 */
14128c2ecf20Sopenharmony_ci	hdr->tstmp = 1;
14138c2ecf20Sopenharmony_ci}
14148c2ecf20Sopenharmony_ci
14158c2ecf20Sopenharmony_ci/* SQ GATHER subdescriptor
14168c2ecf20Sopenharmony_ci * Must follow HDR descriptor
14178c2ecf20Sopenharmony_ci */
14188c2ecf20Sopenharmony_cistatic inline void nicvf_sq_add_gather_subdesc(struct snd_queue *sq, int qentry,
14198c2ecf20Sopenharmony_ci					       int size, u64 data)
14208c2ecf20Sopenharmony_ci{
14218c2ecf20Sopenharmony_ci	struct sq_gather_subdesc *gather;
14228c2ecf20Sopenharmony_ci
14238c2ecf20Sopenharmony_ci	qentry &= (sq->dmem.q_len - 1);
14248c2ecf20Sopenharmony_ci	gather = (struct sq_gather_subdesc *)GET_SQ_DESC(sq, qentry);
14258c2ecf20Sopenharmony_ci
14268c2ecf20Sopenharmony_ci	memset(gather, 0, SND_QUEUE_DESC_SIZE);
14278c2ecf20Sopenharmony_ci	gather->subdesc_type = SQ_DESC_TYPE_GATHER;
14288c2ecf20Sopenharmony_ci	gather->ld_type = NIC_SEND_LD_TYPE_E_LDD;
14298c2ecf20Sopenharmony_ci	gather->size = size;
14308c2ecf20Sopenharmony_ci	gather->addr = data;
14318c2ecf20Sopenharmony_ci}
14328c2ecf20Sopenharmony_ci
14338c2ecf20Sopenharmony_ci/* Add HDR + IMMEDIATE subdescriptors right after descriptors of a TSO
14348c2ecf20Sopenharmony_ci * packet so that a CQE is posted as a notifation for transmission of
14358c2ecf20Sopenharmony_ci * TSO packet.
14368c2ecf20Sopenharmony_ci */
14378c2ecf20Sopenharmony_cistatic inline void nicvf_sq_add_cqe_subdesc(struct snd_queue *sq, int qentry,
14388c2ecf20Sopenharmony_ci					    int tso_sqe, struct sk_buff *skb)
14398c2ecf20Sopenharmony_ci{
14408c2ecf20Sopenharmony_ci	struct sq_imm_subdesc *imm;
14418c2ecf20Sopenharmony_ci	struct sq_hdr_subdesc *hdr;
14428c2ecf20Sopenharmony_ci
14438c2ecf20Sopenharmony_ci	sq->skbuff[qentry] = (u64)skb;
14448c2ecf20Sopenharmony_ci
14458c2ecf20Sopenharmony_ci	hdr = (struct sq_hdr_subdesc *)GET_SQ_DESC(sq, qentry);
14468c2ecf20Sopenharmony_ci	memset(hdr, 0, SND_QUEUE_DESC_SIZE);
14478c2ecf20Sopenharmony_ci	hdr->subdesc_type = SQ_DESC_TYPE_HEADER;
14488c2ecf20Sopenharmony_ci	/* Enable notification via CQE after processing SQE */
14498c2ecf20Sopenharmony_ci	hdr->post_cqe = 1;
14508c2ecf20Sopenharmony_ci	/* There is no packet to transmit here */
14518c2ecf20Sopenharmony_ci	hdr->dont_send = 1;
14528c2ecf20Sopenharmony_ci	hdr->subdesc_cnt = POST_CQE_DESC_COUNT - 1;
14538c2ecf20Sopenharmony_ci	hdr->tot_len = 1;
14548c2ecf20Sopenharmony_ci	/* Actual TSO header SQE index, needed for cleanup */
14558c2ecf20Sopenharmony_ci	hdr->rsvd2 = tso_sqe;
14568c2ecf20Sopenharmony_ci
14578c2ecf20Sopenharmony_ci	qentry = nicvf_get_nxt_sqentry(sq, qentry);
14588c2ecf20Sopenharmony_ci	imm = (struct sq_imm_subdesc *)GET_SQ_DESC(sq, qentry);
14598c2ecf20Sopenharmony_ci	memset(imm, 0, SND_QUEUE_DESC_SIZE);
14608c2ecf20Sopenharmony_ci	imm->subdesc_type = SQ_DESC_TYPE_IMMEDIATE;
14618c2ecf20Sopenharmony_ci	imm->len = 1;
14628c2ecf20Sopenharmony_ci}
14638c2ecf20Sopenharmony_ci
14648c2ecf20Sopenharmony_cistatic inline void nicvf_sq_doorbell(struct nicvf *nic, struct sk_buff *skb,
14658c2ecf20Sopenharmony_ci				     int sq_num, int desc_cnt)
14668c2ecf20Sopenharmony_ci{
14678c2ecf20Sopenharmony_ci	struct netdev_queue *txq;
14688c2ecf20Sopenharmony_ci
14698c2ecf20Sopenharmony_ci	txq = netdev_get_tx_queue(nic->pnicvf->netdev,
14708c2ecf20Sopenharmony_ci				  skb_get_queue_mapping(skb));
14718c2ecf20Sopenharmony_ci
14728c2ecf20Sopenharmony_ci	netdev_tx_sent_queue(txq, skb->len);
14738c2ecf20Sopenharmony_ci
14748c2ecf20Sopenharmony_ci	/* make sure all memory stores are done before ringing doorbell */
14758c2ecf20Sopenharmony_ci	smp_wmb();
14768c2ecf20Sopenharmony_ci
14778c2ecf20Sopenharmony_ci	/* Inform HW to xmit all TSO segments */
14788c2ecf20Sopenharmony_ci	nicvf_queue_reg_write(nic, NIC_QSET_SQ_0_7_DOOR,
14798c2ecf20Sopenharmony_ci			      sq_num, desc_cnt);
14808c2ecf20Sopenharmony_ci}
14818c2ecf20Sopenharmony_ci
14828c2ecf20Sopenharmony_ci/* Segment a TSO packet into 'gso_size' segments and append
14838c2ecf20Sopenharmony_ci * them to SQ for transfer
14848c2ecf20Sopenharmony_ci */
14858c2ecf20Sopenharmony_cistatic int nicvf_sq_append_tso(struct nicvf *nic, struct snd_queue *sq,
14868c2ecf20Sopenharmony_ci			       int sq_num, int qentry, struct sk_buff *skb)
14878c2ecf20Sopenharmony_ci{
14888c2ecf20Sopenharmony_ci	struct tso_t tso;
14898c2ecf20Sopenharmony_ci	int seg_subdescs = 0, desc_cnt = 0;
14908c2ecf20Sopenharmony_ci	int seg_len, total_len, data_left;
14918c2ecf20Sopenharmony_ci	int hdr_qentry = qentry;
14928c2ecf20Sopenharmony_ci	int hdr_len;
14938c2ecf20Sopenharmony_ci
14948c2ecf20Sopenharmony_ci	hdr_len = tso_start(skb, &tso);
14958c2ecf20Sopenharmony_ci
14968c2ecf20Sopenharmony_ci	total_len = skb->len - hdr_len;
14978c2ecf20Sopenharmony_ci	while (total_len > 0) {
14988c2ecf20Sopenharmony_ci		char *hdr;
14998c2ecf20Sopenharmony_ci
15008c2ecf20Sopenharmony_ci		/* Save Qentry for adding HDR_SUBDESC at the end */
15018c2ecf20Sopenharmony_ci		hdr_qentry = qentry;
15028c2ecf20Sopenharmony_ci
15038c2ecf20Sopenharmony_ci		data_left = min_t(int, skb_shinfo(skb)->gso_size, total_len);
15048c2ecf20Sopenharmony_ci		total_len -= data_left;
15058c2ecf20Sopenharmony_ci
15068c2ecf20Sopenharmony_ci		/* Add segment's header */
15078c2ecf20Sopenharmony_ci		qentry = nicvf_get_nxt_sqentry(sq, qentry);
15088c2ecf20Sopenharmony_ci		hdr = sq->tso_hdrs + qentry * TSO_HEADER_SIZE;
15098c2ecf20Sopenharmony_ci		tso_build_hdr(skb, hdr, &tso, data_left, total_len == 0);
15108c2ecf20Sopenharmony_ci		nicvf_sq_add_gather_subdesc(sq, qentry, hdr_len,
15118c2ecf20Sopenharmony_ci					    sq->tso_hdrs_phys +
15128c2ecf20Sopenharmony_ci					    qentry * TSO_HEADER_SIZE);
15138c2ecf20Sopenharmony_ci		/* HDR_SUDESC + GATHER */
15148c2ecf20Sopenharmony_ci		seg_subdescs = 2;
15158c2ecf20Sopenharmony_ci		seg_len = hdr_len;
15168c2ecf20Sopenharmony_ci
15178c2ecf20Sopenharmony_ci		/* Add segment's payload fragments */
15188c2ecf20Sopenharmony_ci		while (data_left > 0) {
15198c2ecf20Sopenharmony_ci			int size;
15208c2ecf20Sopenharmony_ci
15218c2ecf20Sopenharmony_ci			size = min_t(int, tso.size, data_left);
15228c2ecf20Sopenharmony_ci
15238c2ecf20Sopenharmony_ci			qentry = nicvf_get_nxt_sqentry(sq, qentry);
15248c2ecf20Sopenharmony_ci			nicvf_sq_add_gather_subdesc(sq, qentry, size,
15258c2ecf20Sopenharmony_ci						    virt_to_phys(tso.data));
15268c2ecf20Sopenharmony_ci			seg_subdescs++;
15278c2ecf20Sopenharmony_ci			seg_len += size;
15288c2ecf20Sopenharmony_ci
15298c2ecf20Sopenharmony_ci			data_left -= size;
15308c2ecf20Sopenharmony_ci			tso_build_data(skb, &tso, size);
15318c2ecf20Sopenharmony_ci		}
15328c2ecf20Sopenharmony_ci		nicvf_sq_add_hdr_subdesc(nic, sq, hdr_qentry,
15338c2ecf20Sopenharmony_ci					 seg_subdescs - 1, skb, seg_len);
15348c2ecf20Sopenharmony_ci		sq->skbuff[hdr_qentry] = (u64)NULL;
15358c2ecf20Sopenharmony_ci		qentry = nicvf_get_nxt_sqentry(sq, qentry);
15368c2ecf20Sopenharmony_ci
15378c2ecf20Sopenharmony_ci		desc_cnt += seg_subdescs;
15388c2ecf20Sopenharmony_ci	}
15398c2ecf20Sopenharmony_ci	/* Save SKB in the last segment for freeing */
15408c2ecf20Sopenharmony_ci	sq->skbuff[hdr_qentry] = (u64)skb;
15418c2ecf20Sopenharmony_ci
15428c2ecf20Sopenharmony_ci	nicvf_sq_doorbell(nic, skb, sq_num, desc_cnt);
15438c2ecf20Sopenharmony_ci
15448c2ecf20Sopenharmony_ci	this_cpu_inc(nic->pnicvf->drv_stats->tx_tso);
15458c2ecf20Sopenharmony_ci	return 1;
15468c2ecf20Sopenharmony_ci}
15478c2ecf20Sopenharmony_ci
15488c2ecf20Sopenharmony_ci/* Append an skb to a SQ for packet transfer. */
15498c2ecf20Sopenharmony_ciint nicvf_sq_append_skb(struct nicvf *nic, struct snd_queue *sq,
15508c2ecf20Sopenharmony_ci			struct sk_buff *skb, u8 sq_num)
15518c2ecf20Sopenharmony_ci{
15528c2ecf20Sopenharmony_ci	int i, size;
15538c2ecf20Sopenharmony_ci	int subdesc_cnt, hdr_sqe = 0;
15548c2ecf20Sopenharmony_ci	int qentry;
15558c2ecf20Sopenharmony_ci	u64 dma_addr;
15568c2ecf20Sopenharmony_ci
15578c2ecf20Sopenharmony_ci	subdesc_cnt = nicvf_sq_subdesc_required(nic, skb);
15588c2ecf20Sopenharmony_ci	if (subdesc_cnt > atomic_read(&sq->free_cnt))
15598c2ecf20Sopenharmony_ci		goto append_fail;
15608c2ecf20Sopenharmony_ci
15618c2ecf20Sopenharmony_ci	qentry = nicvf_get_sq_desc(sq, subdesc_cnt);
15628c2ecf20Sopenharmony_ci
15638c2ecf20Sopenharmony_ci	/* Check if its a TSO packet */
15648c2ecf20Sopenharmony_ci	if (skb_shinfo(skb)->gso_size && !nic->hw_tso)
15658c2ecf20Sopenharmony_ci		return nicvf_sq_append_tso(nic, sq, sq_num, qentry, skb);
15668c2ecf20Sopenharmony_ci
15678c2ecf20Sopenharmony_ci	/* Add SQ header subdesc */
15688c2ecf20Sopenharmony_ci	nicvf_sq_add_hdr_subdesc(nic, sq, qentry, subdesc_cnt - 1,
15698c2ecf20Sopenharmony_ci				 skb, skb->len);
15708c2ecf20Sopenharmony_ci	hdr_sqe = qentry;
15718c2ecf20Sopenharmony_ci
15728c2ecf20Sopenharmony_ci	/* Add SQ gather subdescs */
15738c2ecf20Sopenharmony_ci	qentry = nicvf_get_nxt_sqentry(sq, qentry);
15748c2ecf20Sopenharmony_ci	size = skb_is_nonlinear(skb) ? skb_headlen(skb) : skb->len;
15758c2ecf20Sopenharmony_ci	/* HW will ensure data coherency, CPU sync not required */
15768c2ecf20Sopenharmony_ci	dma_addr = dma_map_page_attrs(&nic->pdev->dev, virt_to_page(skb->data),
15778c2ecf20Sopenharmony_ci				      offset_in_page(skb->data), size,
15788c2ecf20Sopenharmony_ci				      DMA_TO_DEVICE, DMA_ATTR_SKIP_CPU_SYNC);
15798c2ecf20Sopenharmony_ci	if (dma_mapping_error(&nic->pdev->dev, dma_addr)) {
15808c2ecf20Sopenharmony_ci		nicvf_rollback_sq_desc(sq, qentry, subdesc_cnt);
15818c2ecf20Sopenharmony_ci		return 0;
15828c2ecf20Sopenharmony_ci	}
15838c2ecf20Sopenharmony_ci
15848c2ecf20Sopenharmony_ci	nicvf_sq_add_gather_subdesc(sq, qentry, size, dma_addr);
15858c2ecf20Sopenharmony_ci
15868c2ecf20Sopenharmony_ci	/* Check for scattered buffer */
15878c2ecf20Sopenharmony_ci	if (!skb_is_nonlinear(skb))
15888c2ecf20Sopenharmony_ci		goto doorbell;
15898c2ecf20Sopenharmony_ci
15908c2ecf20Sopenharmony_ci	for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
15918c2ecf20Sopenharmony_ci		const skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
15928c2ecf20Sopenharmony_ci
15938c2ecf20Sopenharmony_ci		qentry = nicvf_get_nxt_sqentry(sq, qentry);
15948c2ecf20Sopenharmony_ci		size = skb_frag_size(frag);
15958c2ecf20Sopenharmony_ci		dma_addr = dma_map_page_attrs(&nic->pdev->dev,
15968c2ecf20Sopenharmony_ci					      skb_frag_page(frag),
15978c2ecf20Sopenharmony_ci					      skb_frag_off(frag), size,
15988c2ecf20Sopenharmony_ci					      DMA_TO_DEVICE,
15998c2ecf20Sopenharmony_ci					      DMA_ATTR_SKIP_CPU_SYNC);
16008c2ecf20Sopenharmony_ci		if (dma_mapping_error(&nic->pdev->dev, dma_addr)) {
16018c2ecf20Sopenharmony_ci			/* Free entire chain of mapped buffers
16028c2ecf20Sopenharmony_ci			 * here 'i' = frags mapped + above mapped skb->data
16038c2ecf20Sopenharmony_ci			 */
16048c2ecf20Sopenharmony_ci			nicvf_unmap_sndq_buffers(nic, sq, hdr_sqe, i);
16058c2ecf20Sopenharmony_ci			nicvf_rollback_sq_desc(sq, qentry, subdesc_cnt);
16068c2ecf20Sopenharmony_ci			return 0;
16078c2ecf20Sopenharmony_ci		}
16088c2ecf20Sopenharmony_ci		nicvf_sq_add_gather_subdesc(sq, qentry, size, dma_addr);
16098c2ecf20Sopenharmony_ci	}
16108c2ecf20Sopenharmony_ci
16118c2ecf20Sopenharmony_cidoorbell:
16128c2ecf20Sopenharmony_ci	if (nic->t88 && skb_shinfo(skb)->gso_size) {
16138c2ecf20Sopenharmony_ci		qentry = nicvf_get_nxt_sqentry(sq, qentry);
16148c2ecf20Sopenharmony_ci		nicvf_sq_add_cqe_subdesc(sq, qentry, hdr_sqe, skb);
16158c2ecf20Sopenharmony_ci	}
16168c2ecf20Sopenharmony_ci
16178c2ecf20Sopenharmony_ci	nicvf_sq_doorbell(nic, skb, sq_num, subdesc_cnt);
16188c2ecf20Sopenharmony_ci
16198c2ecf20Sopenharmony_ci	return 1;
16208c2ecf20Sopenharmony_ci
16218c2ecf20Sopenharmony_ciappend_fail:
16228c2ecf20Sopenharmony_ci	/* Use original PCI dev for debug log */
16238c2ecf20Sopenharmony_ci	nic = nic->pnicvf;
16248c2ecf20Sopenharmony_ci	netdev_dbg(nic->netdev, "Not enough SQ descriptors to xmit pkt\n");
16258c2ecf20Sopenharmony_ci	return 0;
16268c2ecf20Sopenharmony_ci}
16278c2ecf20Sopenharmony_ci
16288c2ecf20Sopenharmony_cistatic inline unsigned frag_num(unsigned i)
16298c2ecf20Sopenharmony_ci{
16308c2ecf20Sopenharmony_ci#ifdef __BIG_ENDIAN
16318c2ecf20Sopenharmony_ci	return (i & ~3) + 3 - (i & 3);
16328c2ecf20Sopenharmony_ci#else
16338c2ecf20Sopenharmony_ci	return i;
16348c2ecf20Sopenharmony_ci#endif
16358c2ecf20Sopenharmony_ci}
16368c2ecf20Sopenharmony_ci
16378c2ecf20Sopenharmony_cistatic void nicvf_unmap_rcv_buffer(struct nicvf *nic, u64 dma_addr,
16388c2ecf20Sopenharmony_ci				   u64 buf_addr, bool xdp)
16398c2ecf20Sopenharmony_ci{
16408c2ecf20Sopenharmony_ci	struct page *page = NULL;
16418c2ecf20Sopenharmony_ci	int len = RCV_FRAG_LEN;
16428c2ecf20Sopenharmony_ci
16438c2ecf20Sopenharmony_ci	if (xdp) {
16448c2ecf20Sopenharmony_ci		page = virt_to_page(phys_to_virt(buf_addr));
16458c2ecf20Sopenharmony_ci		/* Check if it's a recycled page, if not
16468c2ecf20Sopenharmony_ci		 * unmap the DMA mapping.
16478c2ecf20Sopenharmony_ci		 *
16488c2ecf20Sopenharmony_ci		 * Recycled page holds an extra reference.
16498c2ecf20Sopenharmony_ci		 */
16508c2ecf20Sopenharmony_ci		if (page_ref_count(page) != 1)
16518c2ecf20Sopenharmony_ci			return;
16528c2ecf20Sopenharmony_ci
16538c2ecf20Sopenharmony_ci		len += XDP_PACKET_HEADROOM;
16548c2ecf20Sopenharmony_ci		/* Receive buffers in XDP mode are mapped from page start */
16558c2ecf20Sopenharmony_ci		dma_addr &= PAGE_MASK;
16568c2ecf20Sopenharmony_ci	}
16578c2ecf20Sopenharmony_ci	dma_unmap_page_attrs(&nic->pdev->dev, dma_addr, len,
16588c2ecf20Sopenharmony_ci			     DMA_FROM_DEVICE, DMA_ATTR_SKIP_CPU_SYNC);
16598c2ecf20Sopenharmony_ci}
16608c2ecf20Sopenharmony_ci
16618c2ecf20Sopenharmony_ci/* Returns SKB for a received packet */
16628c2ecf20Sopenharmony_cistruct sk_buff *nicvf_get_rcv_skb(struct nicvf *nic,
16638c2ecf20Sopenharmony_ci				  struct cqe_rx_t *cqe_rx, bool xdp)
16648c2ecf20Sopenharmony_ci{
16658c2ecf20Sopenharmony_ci	int frag;
16668c2ecf20Sopenharmony_ci	int payload_len = 0;
16678c2ecf20Sopenharmony_ci	struct sk_buff *skb = NULL;
16688c2ecf20Sopenharmony_ci	struct page *page;
16698c2ecf20Sopenharmony_ci	int offset;
16708c2ecf20Sopenharmony_ci	u16 *rb_lens = NULL;
16718c2ecf20Sopenharmony_ci	u64 *rb_ptrs = NULL;
16728c2ecf20Sopenharmony_ci	u64 phys_addr;
16738c2ecf20Sopenharmony_ci
16748c2ecf20Sopenharmony_ci	rb_lens = (void *)cqe_rx + (3 * sizeof(u64));
16758c2ecf20Sopenharmony_ci	/* Except 88xx pass1 on all other chips CQE_RX2_S is added to
16768c2ecf20Sopenharmony_ci	 * CQE_RX at word6, hence buffer pointers move by word
16778c2ecf20Sopenharmony_ci	 *
16788c2ecf20Sopenharmony_ci	 * Use existing 'hw_tso' flag which will be set for all chips
16798c2ecf20Sopenharmony_ci	 * except 88xx pass1 instead of a additional cache line
16808c2ecf20Sopenharmony_ci	 * access (or miss) by using pci dev's revision.
16818c2ecf20Sopenharmony_ci	 */
16828c2ecf20Sopenharmony_ci	if (!nic->hw_tso)
16838c2ecf20Sopenharmony_ci		rb_ptrs = (void *)cqe_rx + (6 * sizeof(u64));
16848c2ecf20Sopenharmony_ci	else
16858c2ecf20Sopenharmony_ci		rb_ptrs = (void *)cqe_rx + (7 * sizeof(u64));
16868c2ecf20Sopenharmony_ci
16878c2ecf20Sopenharmony_ci	for (frag = 0; frag < cqe_rx->rb_cnt; frag++) {
16888c2ecf20Sopenharmony_ci		payload_len = rb_lens[frag_num(frag)];
16898c2ecf20Sopenharmony_ci		phys_addr = nicvf_iova_to_phys(nic, *rb_ptrs);
16908c2ecf20Sopenharmony_ci		if (!phys_addr) {
16918c2ecf20Sopenharmony_ci			if (skb)
16928c2ecf20Sopenharmony_ci				dev_kfree_skb_any(skb);
16938c2ecf20Sopenharmony_ci			return NULL;
16948c2ecf20Sopenharmony_ci		}
16958c2ecf20Sopenharmony_ci
16968c2ecf20Sopenharmony_ci		if (!frag) {
16978c2ecf20Sopenharmony_ci			/* First fragment */
16988c2ecf20Sopenharmony_ci			nicvf_unmap_rcv_buffer(nic,
16998c2ecf20Sopenharmony_ci					       *rb_ptrs - cqe_rx->align_pad,
17008c2ecf20Sopenharmony_ci					       phys_addr, xdp);
17018c2ecf20Sopenharmony_ci			skb = nicvf_rb_ptr_to_skb(nic,
17028c2ecf20Sopenharmony_ci						  phys_addr - cqe_rx->align_pad,
17038c2ecf20Sopenharmony_ci						  payload_len);
17048c2ecf20Sopenharmony_ci			if (!skb)
17058c2ecf20Sopenharmony_ci				return NULL;
17068c2ecf20Sopenharmony_ci			skb_reserve(skb, cqe_rx->align_pad);
17078c2ecf20Sopenharmony_ci			skb_put(skb, payload_len);
17088c2ecf20Sopenharmony_ci		} else {
17098c2ecf20Sopenharmony_ci			/* Add fragments */
17108c2ecf20Sopenharmony_ci			nicvf_unmap_rcv_buffer(nic, *rb_ptrs, phys_addr, xdp);
17118c2ecf20Sopenharmony_ci			page = virt_to_page(phys_to_virt(phys_addr));
17128c2ecf20Sopenharmony_ci			offset = phys_to_virt(phys_addr) - page_address(page);
17138c2ecf20Sopenharmony_ci			skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, page,
17148c2ecf20Sopenharmony_ci					offset, payload_len, RCV_FRAG_LEN);
17158c2ecf20Sopenharmony_ci		}
17168c2ecf20Sopenharmony_ci		/* Next buffer pointer */
17178c2ecf20Sopenharmony_ci		rb_ptrs++;
17188c2ecf20Sopenharmony_ci	}
17198c2ecf20Sopenharmony_ci	return skb;
17208c2ecf20Sopenharmony_ci}
17218c2ecf20Sopenharmony_ci
17228c2ecf20Sopenharmony_cistatic u64 nicvf_int_type_to_mask(int int_type, int q_idx)
17238c2ecf20Sopenharmony_ci{
17248c2ecf20Sopenharmony_ci	u64 reg_val;
17258c2ecf20Sopenharmony_ci
17268c2ecf20Sopenharmony_ci	switch (int_type) {
17278c2ecf20Sopenharmony_ci	case NICVF_INTR_CQ:
17288c2ecf20Sopenharmony_ci		reg_val = ((1ULL << q_idx) << NICVF_INTR_CQ_SHIFT);
17298c2ecf20Sopenharmony_ci		break;
17308c2ecf20Sopenharmony_ci	case NICVF_INTR_SQ:
17318c2ecf20Sopenharmony_ci		reg_val = ((1ULL << q_idx) << NICVF_INTR_SQ_SHIFT);
17328c2ecf20Sopenharmony_ci		break;
17338c2ecf20Sopenharmony_ci	case NICVF_INTR_RBDR:
17348c2ecf20Sopenharmony_ci		reg_val = ((1ULL << q_idx) << NICVF_INTR_RBDR_SHIFT);
17358c2ecf20Sopenharmony_ci		break;
17368c2ecf20Sopenharmony_ci	case NICVF_INTR_PKT_DROP:
17378c2ecf20Sopenharmony_ci		reg_val = (1ULL << NICVF_INTR_PKT_DROP_SHIFT);
17388c2ecf20Sopenharmony_ci		break;
17398c2ecf20Sopenharmony_ci	case NICVF_INTR_TCP_TIMER:
17408c2ecf20Sopenharmony_ci		reg_val = (1ULL << NICVF_INTR_TCP_TIMER_SHIFT);
17418c2ecf20Sopenharmony_ci		break;
17428c2ecf20Sopenharmony_ci	case NICVF_INTR_MBOX:
17438c2ecf20Sopenharmony_ci		reg_val = (1ULL << NICVF_INTR_MBOX_SHIFT);
17448c2ecf20Sopenharmony_ci		break;
17458c2ecf20Sopenharmony_ci	case NICVF_INTR_QS_ERR:
17468c2ecf20Sopenharmony_ci		reg_val = (1ULL << NICVF_INTR_QS_ERR_SHIFT);
17478c2ecf20Sopenharmony_ci		break;
17488c2ecf20Sopenharmony_ci	default:
17498c2ecf20Sopenharmony_ci		reg_val = 0;
17508c2ecf20Sopenharmony_ci	}
17518c2ecf20Sopenharmony_ci
17528c2ecf20Sopenharmony_ci	return reg_val;
17538c2ecf20Sopenharmony_ci}
17548c2ecf20Sopenharmony_ci
17558c2ecf20Sopenharmony_ci/* Enable interrupt */
17568c2ecf20Sopenharmony_civoid nicvf_enable_intr(struct nicvf *nic, int int_type, int q_idx)
17578c2ecf20Sopenharmony_ci{
17588c2ecf20Sopenharmony_ci	u64 mask = nicvf_int_type_to_mask(int_type, q_idx);
17598c2ecf20Sopenharmony_ci
17608c2ecf20Sopenharmony_ci	if (!mask) {
17618c2ecf20Sopenharmony_ci		netdev_dbg(nic->netdev,
17628c2ecf20Sopenharmony_ci			   "Failed to enable interrupt: unknown type\n");
17638c2ecf20Sopenharmony_ci		return;
17648c2ecf20Sopenharmony_ci	}
17658c2ecf20Sopenharmony_ci	nicvf_reg_write(nic, NIC_VF_ENA_W1S,
17668c2ecf20Sopenharmony_ci			nicvf_reg_read(nic, NIC_VF_ENA_W1S) | mask);
17678c2ecf20Sopenharmony_ci}
17688c2ecf20Sopenharmony_ci
17698c2ecf20Sopenharmony_ci/* Disable interrupt */
17708c2ecf20Sopenharmony_civoid nicvf_disable_intr(struct nicvf *nic, int int_type, int q_idx)
17718c2ecf20Sopenharmony_ci{
17728c2ecf20Sopenharmony_ci	u64 mask = nicvf_int_type_to_mask(int_type, q_idx);
17738c2ecf20Sopenharmony_ci
17748c2ecf20Sopenharmony_ci	if (!mask) {
17758c2ecf20Sopenharmony_ci		netdev_dbg(nic->netdev,
17768c2ecf20Sopenharmony_ci			   "Failed to disable interrupt: unknown type\n");
17778c2ecf20Sopenharmony_ci		return;
17788c2ecf20Sopenharmony_ci	}
17798c2ecf20Sopenharmony_ci
17808c2ecf20Sopenharmony_ci	nicvf_reg_write(nic, NIC_VF_ENA_W1C, mask);
17818c2ecf20Sopenharmony_ci}
17828c2ecf20Sopenharmony_ci
17838c2ecf20Sopenharmony_ci/* Clear interrupt */
17848c2ecf20Sopenharmony_civoid nicvf_clear_intr(struct nicvf *nic, int int_type, int q_idx)
17858c2ecf20Sopenharmony_ci{
17868c2ecf20Sopenharmony_ci	u64 mask = nicvf_int_type_to_mask(int_type, q_idx);
17878c2ecf20Sopenharmony_ci
17888c2ecf20Sopenharmony_ci	if (!mask) {
17898c2ecf20Sopenharmony_ci		netdev_dbg(nic->netdev,
17908c2ecf20Sopenharmony_ci			   "Failed to clear interrupt: unknown type\n");
17918c2ecf20Sopenharmony_ci		return;
17928c2ecf20Sopenharmony_ci	}
17938c2ecf20Sopenharmony_ci
17948c2ecf20Sopenharmony_ci	nicvf_reg_write(nic, NIC_VF_INT, mask);
17958c2ecf20Sopenharmony_ci}
17968c2ecf20Sopenharmony_ci
17978c2ecf20Sopenharmony_ci/* Check if interrupt is enabled */
17988c2ecf20Sopenharmony_ciint nicvf_is_intr_enabled(struct nicvf *nic, int int_type, int q_idx)
17998c2ecf20Sopenharmony_ci{
18008c2ecf20Sopenharmony_ci	u64 mask = nicvf_int_type_to_mask(int_type, q_idx);
18018c2ecf20Sopenharmony_ci	/* If interrupt type is unknown, we treat it disabled. */
18028c2ecf20Sopenharmony_ci	if (!mask) {
18038c2ecf20Sopenharmony_ci		netdev_dbg(nic->netdev,
18048c2ecf20Sopenharmony_ci			   "Failed to check interrupt enable: unknown type\n");
18058c2ecf20Sopenharmony_ci		return 0;
18068c2ecf20Sopenharmony_ci	}
18078c2ecf20Sopenharmony_ci
18088c2ecf20Sopenharmony_ci	return mask & nicvf_reg_read(nic, NIC_VF_ENA_W1S);
18098c2ecf20Sopenharmony_ci}
18108c2ecf20Sopenharmony_ci
18118c2ecf20Sopenharmony_civoid nicvf_update_rq_stats(struct nicvf *nic, int rq_idx)
18128c2ecf20Sopenharmony_ci{
18138c2ecf20Sopenharmony_ci	struct rcv_queue *rq;
18148c2ecf20Sopenharmony_ci
18158c2ecf20Sopenharmony_ci#define GET_RQ_STATS(reg) \
18168c2ecf20Sopenharmony_ci	nicvf_reg_read(nic, NIC_QSET_RQ_0_7_STAT_0_1 |\
18178c2ecf20Sopenharmony_ci			    (rq_idx << NIC_Q_NUM_SHIFT) | (reg << 3))
18188c2ecf20Sopenharmony_ci
18198c2ecf20Sopenharmony_ci	rq = &nic->qs->rq[rq_idx];
18208c2ecf20Sopenharmony_ci	rq->stats.bytes = GET_RQ_STATS(RQ_SQ_STATS_OCTS);
18218c2ecf20Sopenharmony_ci	rq->stats.pkts = GET_RQ_STATS(RQ_SQ_STATS_PKTS);
18228c2ecf20Sopenharmony_ci}
18238c2ecf20Sopenharmony_ci
18248c2ecf20Sopenharmony_civoid nicvf_update_sq_stats(struct nicvf *nic, int sq_idx)
18258c2ecf20Sopenharmony_ci{
18268c2ecf20Sopenharmony_ci	struct snd_queue *sq;
18278c2ecf20Sopenharmony_ci
18288c2ecf20Sopenharmony_ci#define GET_SQ_STATS(reg) \
18298c2ecf20Sopenharmony_ci	nicvf_reg_read(nic, NIC_QSET_SQ_0_7_STAT_0_1 |\
18308c2ecf20Sopenharmony_ci			    (sq_idx << NIC_Q_NUM_SHIFT) | (reg << 3))
18318c2ecf20Sopenharmony_ci
18328c2ecf20Sopenharmony_ci	sq = &nic->qs->sq[sq_idx];
18338c2ecf20Sopenharmony_ci	sq->stats.bytes = GET_SQ_STATS(RQ_SQ_STATS_OCTS);
18348c2ecf20Sopenharmony_ci	sq->stats.pkts = GET_SQ_STATS(RQ_SQ_STATS_PKTS);
18358c2ecf20Sopenharmony_ci}
18368c2ecf20Sopenharmony_ci
18378c2ecf20Sopenharmony_ci/* Check for errors in the receive cmp.queue entry */
18388c2ecf20Sopenharmony_ciint nicvf_check_cqe_rx_errs(struct nicvf *nic, struct cqe_rx_t *cqe_rx)
18398c2ecf20Sopenharmony_ci{
18408c2ecf20Sopenharmony_ci	netif_err(nic, rx_err, nic->netdev,
18418c2ecf20Sopenharmony_ci		  "RX error CQE err_level 0x%x err_opcode 0x%x\n",
18428c2ecf20Sopenharmony_ci		  cqe_rx->err_level, cqe_rx->err_opcode);
18438c2ecf20Sopenharmony_ci
18448c2ecf20Sopenharmony_ci	switch (cqe_rx->err_opcode) {
18458c2ecf20Sopenharmony_ci	case CQ_RX_ERROP_RE_PARTIAL:
18468c2ecf20Sopenharmony_ci		this_cpu_inc(nic->drv_stats->rx_bgx_truncated_pkts);
18478c2ecf20Sopenharmony_ci		break;
18488c2ecf20Sopenharmony_ci	case CQ_RX_ERROP_RE_JABBER:
18498c2ecf20Sopenharmony_ci		this_cpu_inc(nic->drv_stats->rx_jabber_errs);
18508c2ecf20Sopenharmony_ci		break;
18518c2ecf20Sopenharmony_ci	case CQ_RX_ERROP_RE_FCS:
18528c2ecf20Sopenharmony_ci		this_cpu_inc(nic->drv_stats->rx_fcs_errs);
18538c2ecf20Sopenharmony_ci		break;
18548c2ecf20Sopenharmony_ci	case CQ_RX_ERROP_RE_RX_CTL:
18558c2ecf20Sopenharmony_ci		this_cpu_inc(nic->drv_stats->rx_bgx_errs);
18568c2ecf20Sopenharmony_ci		break;
18578c2ecf20Sopenharmony_ci	case CQ_RX_ERROP_PREL2_ERR:
18588c2ecf20Sopenharmony_ci		this_cpu_inc(nic->drv_stats->rx_prel2_errs);
18598c2ecf20Sopenharmony_ci		break;
18608c2ecf20Sopenharmony_ci	case CQ_RX_ERROP_L2_MAL:
18618c2ecf20Sopenharmony_ci		this_cpu_inc(nic->drv_stats->rx_l2_hdr_malformed);
18628c2ecf20Sopenharmony_ci		break;
18638c2ecf20Sopenharmony_ci	case CQ_RX_ERROP_L2_OVERSIZE:
18648c2ecf20Sopenharmony_ci		this_cpu_inc(nic->drv_stats->rx_oversize);
18658c2ecf20Sopenharmony_ci		break;
18668c2ecf20Sopenharmony_ci	case CQ_RX_ERROP_L2_UNDERSIZE:
18678c2ecf20Sopenharmony_ci		this_cpu_inc(nic->drv_stats->rx_undersize);
18688c2ecf20Sopenharmony_ci		break;
18698c2ecf20Sopenharmony_ci	case CQ_RX_ERROP_L2_LENMISM:
18708c2ecf20Sopenharmony_ci		this_cpu_inc(nic->drv_stats->rx_l2_len_mismatch);
18718c2ecf20Sopenharmony_ci		break;
18728c2ecf20Sopenharmony_ci	case CQ_RX_ERROP_L2_PCLP:
18738c2ecf20Sopenharmony_ci		this_cpu_inc(nic->drv_stats->rx_l2_pclp);
18748c2ecf20Sopenharmony_ci		break;
18758c2ecf20Sopenharmony_ci	case CQ_RX_ERROP_IP_NOT:
18768c2ecf20Sopenharmony_ci		this_cpu_inc(nic->drv_stats->rx_ip_ver_errs);
18778c2ecf20Sopenharmony_ci		break;
18788c2ecf20Sopenharmony_ci	case CQ_RX_ERROP_IP_CSUM_ERR:
18798c2ecf20Sopenharmony_ci		this_cpu_inc(nic->drv_stats->rx_ip_csum_errs);
18808c2ecf20Sopenharmony_ci		break;
18818c2ecf20Sopenharmony_ci	case CQ_RX_ERROP_IP_MAL:
18828c2ecf20Sopenharmony_ci		this_cpu_inc(nic->drv_stats->rx_ip_hdr_malformed);
18838c2ecf20Sopenharmony_ci		break;
18848c2ecf20Sopenharmony_ci	case CQ_RX_ERROP_IP_MALD:
18858c2ecf20Sopenharmony_ci		this_cpu_inc(nic->drv_stats->rx_ip_payload_malformed);
18868c2ecf20Sopenharmony_ci		break;
18878c2ecf20Sopenharmony_ci	case CQ_RX_ERROP_IP_HOP:
18888c2ecf20Sopenharmony_ci		this_cpu_inc(nic->drv_stats->rx_ip_ttl_errs);
18898c2ecf20Sopenharmony_ci		break;
18908c2ecf20Sopenharmony_ci	case CQ_RX_ERROP_L3_PCLP:
18918c2ecf20Sopenharmony_ci		this_cpu_inc(nic->drv_stats->rx_l3_pclp);
18928c2ecf20Sopenharmony_ci		break;
18938c2ecf20Sopenharmony_ci	case CQ_RX_ERROP_L4_MAL:
18948c2ecf20Sopenharmony_ci		this_cpu_inc(nic->drv_stats->rx_l4_malformed);
18958c2ecf20Sopenharmony_ci		break;
18968c2ecf20Sopenharmony_ci	case CQ_RX_ERROP_L4_CHK:
18978c2ecf20Sopenharmony_ci		this_cpu_inc(nic->drv_stats->rx_l4_csum_errs);
18988c2ecf20Sopenharmony_ci		break;
18998c2ecf20Sopenharmony_ci	case CQ_RX_ERROP_UDP_LEN:
19008c2ecf20Sopenharmony_ci		this_cpu_inc(nic->drv_stats->rx_udp_len_errs);
19018c2ecf20Sopenharmony_ci		break;
19028c2ecf20Sopenharmony_ci	case CQ_RX_ERROP_L4_PORT:
19038c2ecf20Sopenharmony_ci		this_cpu_inc(nic->drv_stats->rx_l4_port_errs);
19048c2ecf20Sopenharmony_ci		break;
19058c2ecf20Sopenharmony_ci	case CQ_RX_ERROP_TCP_FLAG:
19068c2ecf20Sopenharmony_ci		this_cpu_inc(nic->drv_stats->rx_tcp_flag_errs);
19078c2ecf20Sopenharmony_ci		break;
19088c2ecf20Sopenharmony_ci	case CQ_RX_ERROP_TCP_OFFSET:
19098c2ecf20Sopenharmony_ci		this_cpu_inc(nic->drv_stats->rx_tcp_offset_errs);
19108c2ecf20Sopenharmony_ci		break;
19118c2ecf20Sopenharmony_ci	case CQ_RX_ERROP_L4_PCLP:
19128c2ecf20Sopenharmony_ci		this_cpu_inc(nic->drv_stats->rx_l4_pclp);
19138c2ecf20Sopenharmony_ci		break;
19148c2ecf20Sopenharmony_ci	case CQ_RX_ERROP_RBDR_TRUNC:
19158c2ecf20Sopenharmony_ci		this_cpu_inc(nic->drv_stats->rx_truncated_pkts);
19168c2ecf20Sopenharmony_ci		break;
19178c2ecf20Sopenharmony_ci	}
19188c2ecf20Sopenharmony_ci
19198c2ecf20Sopenharmony_ci	return 1;
19208c2ecf20Sopenharmony_ci}
19218c2ecf20Sopenharmony_ci
19228c2ecf20Sopenharmony_ci/* Check for errors in the send cmp.queue entry */
19238c2ecf20Sopenharmony_ciint nicvf_check_cqe_tx_errs(struct nicvf *nic, struct cqe_send_t *cqe_tx)
19248c2ecf20Sopenharmony_ci{
19258c2ecf20Sopenharmony_ci	switch (cqe_tx->send_status) {
19268c2ecf20Sopenharmony_ci	case CQ_TX_ERROP_DESC_FAULT:
19278c2ecf20Sopenharmony_ci		this_cpu_inc(nic->drv_stats->tx_desc_fault);
19288c2ecf20Sopenharmony_ci		break;
19298c2ecf20Sopenharmony_ci	case CQ_TX_ERROP_HDR_CONS_ERR:
19308c2ecf20Sopenharmony_ci		this_cpu_inc(nic->drv_stats->tx_hdr_cons_err);
19318c2ecf20Sopenharmony_ci		break;
19328c2ecf20Sopenharmony_ci	case CQ_TX_ERROP_SUBDC_ERR:
19338c2ecf20Sopenharmony_ci		this_cpu_inc(nic->drv_stats->tx_subdesc_err);
19348c2ecf20Sopenharmony_ci		break;
19358c2ecf20Sopenharmony_ci	case CQ_TX_ERROP_MAX_SIZE_VIOL:
19368c2ecf20Sopenharmony_ci		this_cpu_inc(nic->drv_stats->tx_max_size_exceeded);
19378c2ecf20Sopenharmony_ci		break;
19388c2ecf20Sopenharmony_ci	case CQ_TX_ERROP_IMM_SIZE_OFLOW:
19398c2ecf20Sopenharmony_ci		this_cpu_inc(nic->drv_stats->tx_imm_size_oflow);
19408c2ecf20Sopenharmony_ci		break;
19418c2ecf20Sopenharmony_ci	case CQ_TX_ERROP_DATA_SEQUENCE_ERR:
19428c2ecf20Sopenharmony_ci		this_cpu_inc(nic->drv_stats->tx_data_seq_err);
19438c2ecf20Sopenharmony_ci		break;
19448c2ecf20Sopenharmony_ci	case CQ_TX_ERROP_MEM_SEQUENCE_ERR:
19458c2ecf20Sopenharmony_ci		this_cpu_inc(nic->drv_stats->tx_mem_seq_err);
19468c2ecf20Sopenharmony_ci		break;
19478c2ecf20Sopenharmony_ci	case CQ_TX_ERROP_LOCK_VIOL:
19488c2ecf20Sopenharmony_ci		this_cpu_inc(nic->drv_stats->tx_lock_viol);
19498c2ecf20Sopenharmony_ci		break;
19508c2ecf20Sopenharmony_ci	case CQ_TX_ERROP_DATA_FAULT:
19518c2ecf20Sopenharmony_ci		this_cpu_inc(nic->drv_stats->tx_data_fault);
19528c2ecf20Sopenharmony_ci		break;
19538c2ecf20Sopenharmony_ci	case CQ_TX_ERROP_TSTMP_CONFLICT:
19548c2ecf20Sopenharmony_ci		this_cpu_inc(nic->drv_stats->tx_tstmp_conflict);
19558c2ecf20Sopenharmony_ci		break;
19568c2ecf20Sopenharmony_ci	case CQ_TX_ERROP_TSTMP_TIMEOUT:
19578c2ecf20Sopenharmony_ci		this_cpu_inc(nic->drv_stats->tx_tstmp_timeout);
19588c2ecf20Sopenharmony_ci		break;
19598c2ecf20Sopenharmony_ci	case CQ_TX_ERROP_MEM_FAULT:
19608c2ecf20Sopenharmony_ci		this_cpu_inc(nic->drv_stats->tx_mem_fault);
19618c2ecf20Sopenharmony_ci		break;
19628c2ecf20Sopenharmony_ci	case CQ_TX_ERROP_CK_OVERLAP:
19638c2ecf20Sopenharmony_ci		this_cpu_inc(nic->drv_stats->tx_csum_overlap);
19648c2ecf20Sopenharmony_ci		break;
19658c2ecf20Sopenharmony_ci	case CQ_TX_ERROP_CK_OFLOW:
19668c2ecf20Sopenharmony_ci		this_cpu_inc(nic->drv_stats->tx_csum_overflow);
19678c2ecf20Sopenharmony_ci		break;
19688c2ecf20Sopenharmony_ci	}
19698c2ecf20Sopenharmony_ci
19708c2ecf20Sopenharmony_ci	return 1;
19718c2ecf20Sopenharmony_ci}
1972