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/module.h>
78c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
88c2ecf20Sopenharmony_ci#include <linux/pci.h>
98c2ecf20Sopenharmony_ci#include <linux/netdevice.h>
108c2ecf20Sopenharmony_ci#include <linux/if_vlan.h>
118c2ecf20Sopenharmony_ci#include <linux/etherdevice.h>
128c2ecf20Sopenharmony_ci#include <linux/ethtool.h>
138c2ecf20Sopenharmony_ci#include <linux/log2.h>
148c2ecf20Sopenharmony_ci#include <linux/prefetch.h>
158c2ecf20Sopenharmony_ci#include <linux/irq.h>
168c2ecf20Sopenharmony_ci#include <linux/iommu.h>
178c2ecf20Sopenharmony_ci#include <linux/bpf.h>
188c2ecf20Sopenharmony_ci#include <linux/bpf_trace.h>
198c2ecf20Sopenharmony_ci#include <linux/filter.h>
208c2ecf20Sopenharmony_ci#include <linux/net_tstamp.h>
218c2ecf20Sopenharmony_ci#include <linux/workqueue.h>
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#include "nic_reg.h"
248c2ecf20Sopenharmony_ci#include "nic.h"
258c2ecf20Sopenharmony_ci#include "nicvf_queues.h"
268c2ecf20Sopenharmony_ci#include "thunder_bgx.h"
278c2ecf20Sopenharmony_ci#include "../common/cavium_ptp.h"
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci#define DRV_NAME	"nicvf"
308c2ecf20Sopenharmony_ci#define DRV_VERSION	"1.0"
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci/* NOTE: Packets bigger than 1530 are split across multiple pages and XDP needs
338c2ecf20Sopenharmony_ci * the buffer to be contiguous. Allow XDP to be set up only if we don't exceed
348c2ecf20Sopenharmony_ci * this value, keeping headroom for the 14 byte Ethernet header and two
358c2ecf20Sopenharmony_ci * VLAN tags (for QinQ)
368c2ecf20Sopenharmony_ci */
378c2ecf20Sopenharmony_ci#define MAX_XDP_MTU	(1530 - ETH_HLEN - VLAN_HLEN * 2)
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci/* Supported devices */
408c2ecf20Sopenharmony_cistatic const struct pci_device_id nicvf_id_table[] = {
418c2ecf20Sopenharmony_ci	{ PCI_DEVICE_SUB(PCI_VENDOR_ID_CAVIUM,
428c2ecf20Sopenharmony_ci			 PCI_DEVICE_ID_THUNDER_NIC_VF,
438c2ecf20Sopenharmony_ci			 PCI_VENDOR_ID_CAVIUM,
448c2ecf20Sopenharmony_ci			 PCI_SUBSYS_DEVID_88XX_NIC_VF) },
458c2ecf20Sopenharmony_ci	{ PCI_DEVICE_SUB(PCI_VENDOR_ID_CAVIUM,
468c2ecf20Sopenharmony_ci			 PCI_DEVICE_ID_THUNDER_PASS1_NIC_VF,
478c2ecf20Sopenharmony_ci			 PCI_VENDOR_ID_CAVIUM,
488c2ecf20Sopenharmony_ci			 PCI_SUBSYS_DEVID_88XX_PASS1_NIC_VF) },
498c2ecf20Sopenharmony_ci	{ PCI_DEVICE_SUB(PCI_VENDOR_ID_CAVIUM,
508c2ecf20Sopenharmony_ci			 PCI_DEVICE_ID_THUNDER_NIC_VF,
518c2ecf20Sopenharmony_ci			 PCI_VENDOR_ID_CAVIUM,
528c2ecf20Sopenharmony_ci			 PCI_SUBSYS_DEVID_81XX_NIC_VF) },
538c2ecf20Sopenharmony_ci	{ PCI_DEVICE_SUB(PCI_VENDOR_ID_CAVIUM,
548c2ecf20Sopenharmony_ci			 PCI_DEVICE_ID_THUNDER_NIC_VF,
558c2ecf20Sopenharmony_ci			 PCI_VENDOR_ID_CAVIUM,
568c2ecf20Sopenharmony_ci			 PCI_SUBSYS_DEVID_83XX_NIC_VF) },
578c2ecf20Sopenharmony_ci	{ 0, }  /* end of table */
588c2ecf20Sopenharmony_ci};
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ciMODULE_AUTHOR("Sunil Goutham");
618c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Cavium Thunder NIC Virtual Function Driver");
628c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
638c2ecf20Sopenharmony_ciMODULE_VERSION(DRV_VERSION);
648c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, nicvf_id_table);
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_cistatic int debug = 0x00;
678c2ecf20Sopenharmony_cimodule_param(debug, int, 0644);
688c2ecf20Sopenharmony_ciMODULE_PARM_DESC(debug, "Debug message level bitmap");
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_cistatic int cpi_alg = CPI_ALG_NONE;
718c2ecf20Sopenharmony_cimodule_param(cpi_alg, int, 0444);
728c2ecf20Sopenharmony_ciMODULE_PARM_DESC(cpi_alg,
738c2ecf20Sopenharmony_ci		 "PFC algorithm (0=none, 1=VLAN, 2=VLAN16, 3=IP Diffserv)");
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_cistatic inline u8 nicvf_netdev_qidx(struct nicvf *nic, u8 qidx)
768c2ecf20Sopenharmony_ci{
778c2ecf20Sopenharmony_ci	if (nic->sqs_mode)
788c2ecf20Sopenharmony_ci		return qidx + ((nic->sqs_id + 1) * MAX_CMP_QUEUES_PER_QS);
798c2ecf20Sopenharmony_ci	else
808c2ecf20Sopenharmony_ci		return qidx;
818c2ecf20Sopenharmony_ci}
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci/* The Cavium ThunderX network controller can *only* be found in SoCs
848c2ecf20Sopenharmony_ci * containing the ThunderX ARM64 CPU implementation.  All accesses to the device
858c2ecf20Sopenharmony_ci * registers on this platform are implicitly strongly ordered with respect
868c2ecf20Sopenharmony_ci * to memory accesses. So writeq_relaxed() and readq_relaxed() are safe to use
878c2ecf20Sopenharmony_ci * with no memory barriers in this driver.  The readq()/writeq() functions add
888c2ecf20Sopenharmony_ci * explicit ordering operation which in this case are redundant, and only
898c2ecf20Sopenharmony_ci * add overhead.
908c2ecf20Sopenharmony_ci */
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci/* Register read/write APIs */
938c2ecf20Sopenharmony_civoid nicvf_reg_write(struct nicvf *nic, u64 offset, u64 val)
948c2ecf20Sopenharmony_ci{
958c2ecf20Sopenharmony_ci	writeq_relaxed(val, nic->reg_base + offset);
968c2ecf20Sopenharmony_ci}
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ciu64 nicvf_reg_read(struct nicvf *nic, u64 offset)
998c2ecf20Sopenharmony_ci{
1008c2ecf20Sopenharmony_ci	return readq_relaxed(nic->reg_base + offset);
1018c2ecf20Sopenharmony_ci}
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_civoid nicvf_queue_reg_write(struct nicvf *nic, u64 offset,
1048c2ecf20Sopenharmony_ci			   u64 qidx, u64 val)
1058c2ecf20Sopenharmony_ci{
1068c2ecf20Sopenharmony_ci	void __iomem *addr = nic->reg_base + offset;
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	writeq_relaxed(val, addr + (qidx << NIC_Q_NUM_SHIFT));
1098c2ecf20Sopenharmony_ci}
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ciu64 nicvf_queue_reg_read(struct nicvf *nic, u64 offset, u64 qidx)
1128c2ecf20Sopenharmony_ci{
1138c2ecf20Sopenharmony_ci	void __iomem *addr = nic->reg_base + offset;
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	return readq_relaxed(addr + (qidx << NIC_Q_NUM_SHIFT));
1168c2ecf20Sopenharmony_ci}
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci/* VF -> PF mailbox communication */
1198c2ecf20Sopenharmony_cistatic void nicvf_write_to_mbx(struct nicvf *nic, union nic_mbx *mbx)
1208c2ecf20Sopenharmony_ci{
1218c2ecf20Sopenharmony_ci	u64 *msg = (u64 *)mbx;
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	nicvf_reg_write(nic, NIC_VF_PF_MAILBOX_0_1 + 0, msg[0]);
1248c2ecf20Sopenharmony_ci	nicvf_reg_write(nic, NIC_VF_PF_MAILBOX_0_1 + 8, msg[1]);
1258c2ecf20Sopenharmony_ci}
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ciint nicvf_send_msg_to_pf(struct nicvf *nic, union nic_mbx *mbx)
1288c2ecf20Sopenharmony_ci{
1298c2ecf20Sopenharmony_ci	unsigned long timeout;
1308c2ecf20Sopenharmony_ci	int ret = 0;
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	mutex_lock(&nic->rx_mode_mtx);
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	nic->pf_acked = false;
1358c2ecf20Sopenharmony_ci	nic->pf_nacked = false;
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	nicvf_write_to_mbx(nic, mbx);
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	timeout = jiffies + msecs_to_jiffies(NIC_MBOX_MSG_TIMEOUT);
1408c2ecf20Sopenharmony_ci	/* Wait for previous message to be acked, timeout 2sec */
1418c2ecf20Sopenharmony_ci	while (!nic->pf_acked) {
1428c2ecf20Sopenharmony_ci		if (nic->pf_nacked) {
1438c2ecf20Sopenharmony_ci			netdev_err(nic->netdev,
1448c2ecf20Sopenharmony_ci				   "PF NACK to mbox msg 0x%02x from VF%d\n",
1458c2ecf20Sopenharmony_ci				   (mbx->msg.msg & 0xFF), nic->vf_id);
1468c2ecf20Sopenharmony_ci			ret = -EINVAL;
1478c2ecf20Sopenharmony_ci			break;
1488c2ecf20Sopenharmony_ci		}
1498c2ecf20Sopenharmony_ci		usleep_range(8000, 10000);
1508c2ecf20Sopenharmony_ci		if (nic->pf_acked)
1518c2ecf20Sopenharmony_ci			break;
1528c2ecf20Sopenharmony_ci		if (time_after(jiffies, timeout)) {
1538c2ecf20Sopenharmony_ci			netdev_err(nic->netdev,
1548c2ecf20Sopenharmony_ci				   "PF didn't ACK to mbox msg 0x%02x from VF%d\n",
1558c2ecf20Sopenharmony_ci				   (mbx->msg.msg & 0xFF), nic->vf_id);
1568c2ecf20Sopenharmony_ci			ret = -EBUSY;
1578c2ecf20Sopenharmony_ci			break;
1588c2ecf20Sopenharmony_ci		}
1598c2ecf20Sopenharmony_ci	}
1608c2ecf20Sopenharmony_ci	mutex_unlock(&nic->rx_mode_mtx);
1618c2ecf20Sopenharmony_ci	return ret;
1628c2ecf20Sopenharmony_ci}
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci/* Checks if VF is able to comminicate with PF
1658c2ecf20Sopenharmony_ci* and also gets the VNIC number this VF is associated to.
1668c2ecf20Sopenharmony_ci*/
1678c2ecf20Sopenharmony_cistatic int nicvf_check_pf_ready(struct nicvf *nic)
1688c2ecf20Sopenharmony_ci{
1698c2ecf20Sopenharmony_ci	union nic_mbx mbx = {};
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	mbx.msg.msg = NIC_MBOX_MSG_READY;
1728c2ecf20Sopenharmony_ci	if (nicvf_send_msg_to_pf(nic, &mbx)) {
1738c2ecf20Sopenharmony_ci		netdev_err(nic->netdev,
1748c2ecf20Sopenharmony_ci			   "PF didn't respond to READY msg\n");
1758c2ecf20Sopenharmony_ci		return 0;
1768c2ecf20Sopenharmony_ci	}
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	return 1;
1798c2ecf20Sopenharmony_ci}
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_cistatic void nicvf_send_cfg_done(struct nicvf *nic)
1828c2ecf20Sopenharmony_ci{
1838c2ecf20Sopenharmony_ci	union nic_mbx mbx = {};
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci	mbx.msg.msg = NIC_MBOX_MSG_CFG_DONE;
1868c2ecf20Sopenharmony_ci	if (nicvf_send_msg_to_pf(nic, &mbx)) {
1878c2ecf20Sopenharmony_ci		netdev_err(nic->netdev,
1888c2ecf20Sopenharmony_ci			   "PF didn't respond to CFG DONE msg\n");
1898c2ecf20Sopenharmony_ci	}
1908c2ecf20Sopenharmony_ci}
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_cistatic void nicvf_read_bgx_stats(struct nicvf *nic, struct bgx_stats_msg *bgx)
1938c2ecf20Sopenharmony_ci{
1948c2ecf20Sopenharmony_ci	if (bgx->rx)
1958c2ecf20Sopenharmony_ci		nic->bgx_stats.rx_stats[bgx->idx] = bgx->stats;
1968c2ecf20Sopenharmony_ci	else
1978c2ecf20Sopenharmony_ci		nic->bgx_stats.tx_stats[bgx->idx] = bgx->stats;
1988c2ecf20Sopenharmony_ci}
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_cistatic void  nicvf_handle_mbx_intr(struct nicvf *nic)
2018c2ecf20Sopenharmony_ci{
2028c2ecf20Sopenharmony_ci	union nic_mbx mbx = {};
2038c2ecf20Sopenharmony_ci	u64 *mbx_data;
2048c2ecf20Sopenharmony_ci	u64 mbx_addr;
2058c2ecf20Sopenharmony_ci	int i;
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci	mbx_addr = NIC_VF_PF_MAILBOX_0_1;
2088c2ecf20Sopenharmony_ci	mbx_data = (u64 *)&mbx;
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci	for (i = 0; i < NIC_PF_VF_MAILBOX_SIZE; i++) {
2118c2ecf20Sopenharmony_ci		*mbx_data = nicvf_reg_read(nic, mbx_addr);
2128c2ecf20Sopenharmony_ci		mbx_data++;
2138c2ecf20Sopenharmony_ci		mbx_addr += sizeof(u64);
2148c2ecf20Sopenharmony_ci	}
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci	netdev_dbg(nic->netdev, "Mbox message: msg: 0x%x\n", mbx.msg.msg);
2178c2ecf20Sopenharmony_ci	switch (mbx.msg.msg) {
2188c2ecf20Sopenharmony_ci	case NIC_MBOX_MSG_READY:
2198c2ecf20Sopenharmony_ci		nic->pf_acked = true;
2208c2ecf20Sopenharmony_ci		nic->vf_id = mbx.nic_cfg.vf_id & 0x7F;
2218c2ecf20Sopenharmony_ci		nic->tns_mode = mbx.nic_cfg.tns_mode & 0x7F;
2228c2ecf20Sopenharmony_ci		nic->node = mbx.nic_cfg.node_id;
2238c2ecf20Sopenharmony_ci		if (!nic->set_mac_pending)
2248c2ecf20Sopenharmony_ci			ether_addr_copy(nic->netdev->dev_addr,
2258c2ecf20Sopenharmony_ci					mbx.nic_cfg.mac_addr);
2268c2ecf20Sopenharmony_ci		nic->sqs_mode = mbx.nic_cfg.sqs_mode;
2278c2ecf20Sopenharmony_ci		nic->loopback_supported = mbx.nic_cfg.loopback_supported;
2288c2ecf20Sopenharmony_ci		nic->link_up = false;
2298c2ecf20Sopenharmony_ci		nic->duplex = 0;
2308c2ecf20Sopenharmony_ci		nic->speed = 0;
2318c2ecf20Sopenharmony_ci		break;
2328c2ecf20Sopenharmony_ci	case NIC_MBOX_MSG_ACK:
2338c2ecf20Sopenharmony_ci		nic->pf_acked = true;
2348c2ecf20Sopenharmony_ci		break;
2358c2ecf20Sopenharmony_ci	case NIC_MBOX_MSG_NACK:
2368c2ecf20Sopenharmony_ci		nic->pf_nacked = true;
2378c2ecf20Sopenharmony_ci		break;
2388c2ecf20Sopenharmony_ci	case NIC_MBOX_MSG_RSS_SIZE:
2398c2ecf20Sopenharmony_ci		nic->rss_info.rss_size = mbx.rss_size.ind_tbl_size;
2408c2ecf20Sopenharmony_ci		nic->pf_acked = true;
2418c2ecf20Sopenharmony_ci		break;
2428c2ecf20Sopenharmony_ci	case NIC_MBOX_MSG_BGX_STATS:
2438c2ecf20Sopenharmony_ci		nicvf_read_bgx_stats(nic, &mbx.bgx_stats);
2448c2ecf20Sopenharmony_ci		nic->pf_acked = true;
2458c2ecf20Sopenharmony_ci		break;
2468c2ecf20Sopenharmony_ci	case NIC_MBOX_MSG_BGX_LINK_CHANGE:
2478c2ecf20Sopenharmony_ci		nic->pf_acked = true;
2488c2ecf20Sopenharmony_ci		if (nic->link_up != mbx.link_status.link_up) {
2498c2ecf20Sopenharmony_ci			nic->link_up = mbx.link_status.link_up;
2508c2ecf20Sopenharmony_ci			nic->duplex = mbx.link_status.duplex;
2518c2ecf20Sopenharmony_ci			nic->speed = mbx.link_status.speed;
2528c2ecf20Sopenharmony_ci			nic->mac_type = mbx.link_status.mac_type;
2538c2ecf20Sopenharmony_ci			if (nic->link_up) {
2548c2ecf20Sopenharmony_ci				netdev_info(nic->netdev,
2558c2ecf20Sopenharmony_ci					    "Link is Up %d Mbps %s duplex\n",
2568c2ecf20Sopenharmony_ci					    nic->speed,
2578c2ecf20Sopenharmony_ci					    nic->duplex == DUPLEX_FULL ?
2588c2ecf20Sopenharmony_ci					    "Full" : "Half");
2598c2ecf20Sopenharmony_ci				netif_carrier_on(nic->netdev);
2608c2ecf20Sopenharmony_ci				netif_tx_start_all_queues(nic->netdev);
2618c2ecf20Sopenharmony_ci			} else {
2628c2ecf20Sopenharmony_ci				netdev_info(nic->netdev, "Link is Down\n");
2638c2ecf20Sopenharmony_ci				netif_carrier_off(nic->netdev);
2648c2ecf20Sopenharmony_ci				netif_tx_stop_all_queues(nic->netdev);
2658c2ecf20Sopenharmony_ci			}
2668c2ecf20Sopenharmony_ci		}
2678c2ecf20Sopenharmony_ci		break;
2688c2ecf20Sopenharmony_ci	case NIC_MBOX_MSG_ALLOC_SQS:
2698c2ecf20Sopenharmony_ci		nic->sqs_count = mbx.sqs_alloc.qs_count;
2708c2ecf20Sopenharmony_ci		nic->pf_acked = true;
2718c2ecf20Sopenharmony_ci		break;
2728c2ecf20Sopenharmony_ci	case NIC_MBOX_MSG_SNICVF_PTR:
2738c2ecf20Sopenharmony_ci		/* Primary VF: make note of secondary VF's pointer
2748c2ecf20Sopenharmony_ci		 * to be used while packet transmission.
2758c2ecf20Sopenharmony_ci		 */
2768c2ecf20Sopenharmony_ci		nic->snicvf[mbx.nicvf.sqs_id] =
2778c2ecf20Sopenharmony_ci			(struct nicvf *)mbx.nicvf.nicvf;
2788c2ecf20Sopenharmony_ci		nic->pf_acked = true;
2798c2ecf20Sopenharmony_ci		break;
2808c2ecf20Sopenharmony_ci	case NIC_MBOX_MSG_PNICVF_PTR:
2818c2ecf20Sopenharmony_ci		/* Secondary VF/Qset: make note of primary VF's pointer
2828c2ecf20Sopenharmony_ci		 * to be used while packet reception, to handover packet
2838c2ecf20Sopenharmony_ci		 * to primary VF's netdev.
2848c2ecf20Sopenharmony_ci		 */
2858c2ecf20Sopenharmony_ci		nic->pnicvf = (struct nicvf *)mbx.nicvf.nicvf;
2868c2ecf20Sopenharmony_ci		nic->pf_acked = true;
2878c2ecf20Sopenharmony_ci		break;
2888c2ecf20Sopenharmony_ci	case NIC_MBOX_MSG_PFC:
2898c2ecf20Sopenharmony_ci		nic->pfc.autoneg = mbx.pfc.autoneg;
2908c2ecf20Sopenharmony_ci		nic->pfc.fc_rx = mbx.pfc.fc_rx;
2918c2ecf20Sopenharmony_ci		nic->pfc.fc_tx = mbx.pfc.fc_tx;
2928c2ecf20Sopenharmony_ci		nic->pf_acked = true;
2938c2ecf20Sopenharmony_ci		break;
2948c2ecf20Sopenharmony_ci	default:
2958c2ecf20Sopenharmony_ci		netdev_err(nic->netdev,
2968c2ecf20Sopenharmony_ci			   "Invalid message from PF, msg 0x%x\n", mbx.msg.msg);
2978c2ecf20Sopenharmony_ci		break;
2988c2ecf20Sopenharmony_ci	}
2998c2ecf20Sopenharmony_ci	nicvf_clear_intr(nic, NICVF_INTR_MBOX, 0);
3008c2ecf20Sopenharmony_ci}
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_cistatic int nicvf_hw_set_mac_addr(struct nicvf *nic, struct net_device *netdev)
3038c2ecf20Sopenharmony_ci{
3048c2ecf20Sopenharmony_ci	union nic_mbx mbx = {};
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci	mbx.mac.msg = NIC_MBOX_MSG_SET_MAC;
3078c2ecf20Sopenharmony_ci	mbx.mac.vf_id = nic->vf_id;
3088c2ecf20Sopenharmony_ci	ether_addr_copy(mbx.mac.mac_addr, netdev->dev_addr);
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci	return nicvf_send_msg_to_pf(nic, &mbx);
3118c2ecf20Sopenharmony_ci}
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_cistatic void nicvf_config_cpi(struct nicvf *nic)
3148c2ecf20Sopenharmony_ci{
3158c2ecf20Sopenharmony_ci	union nic_mbx mbx = {};
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci	mbx.cpi_cfg.msg = NIC_MBOX_MSG_CPI_CFG;
3188c2ecf20Sopenharmony_ci	mbx.cpi_cfg.vf_id = nic->vf_id;
3198c2ecf20Sopenharmony_ci	mbx.cpi_cfg.cpi_alg = nic->cpi_alg;
3208c2ecf20Sopenharmony_ci	mbx.cpi_cfg.rq_cnt = nic->qs->rq_cnt;
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci	nicvf_send_msg_to_pf(nic, &mbx);
3238c2ecf20Sopenharmony_ci}
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_cistatic void nicvf_get_rss_size(struct nicvf *nic)
3268c2ecf20Sopenharmony_ci{
3278c2ecf20Sopenharmony_ci	union nic_mbx mbx = {};
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci	mbx.rss_size.msg = NIC_MBOX_MSG_RSS_SIZE;
3308c2ecf20Sopenharmony_ci	mbx.rss_size.vf_id = nic->vf_id;
3318c2ecf20Sopenharmony_ci	nicvf_send_msg_to_pf(nic, &mbx);
3328c2ecf20Sopenharmony_ci}
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_civoid nicvf_config_rss(struct nicvf *nic)
3358c2ecf20Sopenharmony_ci{
3368c2ecf20Sopenharmony_ci	union nic_mbx mbx = {};
3378c2ecf20Sopenharmony_ci	struct nicvf_rss_info *rss = &nic->rss_info;
3388c2ecf20Sopenharmony_ci	int ind_tbl_len = rss->rss_size;
3398c2ecf20Sopenharmony_ci	int i, nextq = 0;
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci	mbx.rss_cfg.vf_id = nic->vf_id;
3428c2ecf20Sopenharmony_ci	mbx.rss_cfg.hash_bits = rss->hash_bits;
3438c2ecf20Sopenharmony_ci	while (ind_tbl_len) {
3448c2ecf20Sopenharmony_ci		mbx.rss_cfg.tbl_offset = nextq;
3458c2ecf20Sopenharmony_ci		mbx.rss_cfg.tbl_len = min(ind_tbl_len,
3468c2ecf20Sopenharmony_ci					       RSS_IND_TBL_LEN_PER_MBX_MSG);
3478c2ecf20Sopenharmony_ci		mbx.rss_cfg.msg = mbx.rss_cfg.tbl_offset ?
3488c2ecf20Sopenharmony_ci			  NIC_MBOX_MSG_RSS_CFG_CONT : NIC_MBOX_MSG_RSS_CFG;
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci		for (i = 0; i < mbx.rss_cfg.tbl_len; i++)
3518c2ecf20Sopenharmony_ci			mbx.rss_cfg.ind_tbl[i] = rss->ind_tbl[nextq++];
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_ci		nicvf_send_msg_to_pf(nic, &mbx);
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci		ind_tbl_len -= mbx.rss_cfg.tbl_len;
3568c2ecf20Sopenharmony_ci	}
3578c2ecf20Sopenharmony_ci}
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_civoid nicvf_set_rss_key(struct nicvf *nic)
3608c2ecf20Sopenharmony_ci{
3618c2ecf20Sopenharmony_ci	struct nicvf_rss_info *rss = &nic->rss_info;
3628c2ecf20Sopenharmony_ci	u64 key_addr = NIC_VNIC_RSS_KEY_0_4;
3638c2ecf20Sopenharmony_ci	int idx;
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci	for (idx = 0; idx < RSS_HASH_KEY_SIZE; idx++) {
3668c2ecf20Sopenharmony_ci		nicvf_reg_write(nic, key_addr, rss->key[idx]);
3678c2ecf20Sopenharmony_ci		key_addr += sizeof(u64);
3688c2ecf20Sopenharmony_ci	}
3698c2ecf20Sopenharmony_ci}
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_cistatic int nicvf_rss_init(struct nicvf *nic)
3728c2ecf20Sopenharmony_ci{
3738c2ecf20Sopenharmony_ci	struct nicvf_rss_info *rss = &nic->rss_info;
3748c2ecf20Sopenharmony_ci	int idx;
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci	nicvf_get_rss_size(nic);
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci	if (cpi_alg != CPI_ALG_NONE) {
3798c2ecf20Sopenharmony_ci		rss->enable = false;
3808c2ecf20Sopenharmony_ci		rss->hash_bits = 0;
3818c2ecf20Sopenharmony_ci		return 0;
3828c2ecf20Sopenharmony_ci	}
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci	rss->enable = true;
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci	netdev_rss_key_fill(rss->key, RSS_HASH_KEY_SIZE * sizeof(u64));
3878c2ecf20Sopenharmony_ci	nicvf_set_rss_key(nic);
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ci	rss->cfg = RSS_IP_HASH_ENA | RSS_TCP_HASH_ENA | RSS_UDP_HASH_ENA;
3908c2ecf20Sopenharmony_ci	nicvf_reg_write(nic, NIC_VNIC_RSS_CFG, rss->cfg);
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_ci	rss->hash_bits =  ilog2(rounddown_pow_of_two(rss->rss_size));
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_ci	for (idx = 0; idx < rss->rss_size; idx++)
3958c2ecf20Sopenharmony_ci		rss->ind_tbl[idx] = ethtool_rxfh_indir_default(idx,
3968c2ecf20Sopenharmony_ci							       nic->rx_queues);
3978c2ecf20Sopenharmony_ci	nicvf_config_rss(nic);
3988c2ecf20Sopenharmony_ci	return 1;
3998c2ecf20Sopenharmony_ci}
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_ci/* Request PF to allocate additional Qsets */
4028c2ecf20Sopenharmony_cistatic void nicvf_request_sqs(struct nicvf *nic)
4038c2ecf20Sopenharmony_ci{
4048c2ecf20Sopenharmony_ci	union nic_mbx mbx = {};
4058c2ecf20Sopenharmony_ci	int sqs;
4068c2ecf20Sopenharmony_ci	int sqs_count = nic->sqs_count;
4078c2ecf20Sopenharmony_ci	int rx_queues = 0, tx_queues = 0;
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_ci	/* Only primary VF should request */
4108c2ecf20Sopenharmony_ci	if (nic->sqs_mode ||  !nic->sqs_count)
4118c2ecf20Sopenharmony_ci		return;
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_ci	mbx.sqs_alloc.msg = NIC_MBOX_MSG_ALLOC_SQS;
4148c2ecf20Sopenharmony_ci	mbx.sqs_alloc.vf_id = nic->vf_id;
4158c2ecf20Sopenharmony_ci	mbx.sqs_alloc.qs_count = nic->sqs_count;
4168c2ecf20Sopenharmony_ci	if (nicvf_send_msg_to_pf(nic, &mbx)) {
4178c2ecf20Sopenharmony_ci		/* No response from PF */
4188c2ecf20Sopenharmony_ci		nic->sqs_count = 0;
4198c2ecf20Sopenharmony_ci		return;
4208c2ecf20Sopenharmony_ci	}
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ci	/* Return if no Secondary Qsets available */
4238c2ecf20Sopenharmony_ci	if (!nic->sqs_count)
4248c2ecf20Sopenharmony_ci		return;
4258c2ecf20Sopenharmony_ci
4268c2ecf20Sopenharmony_ci	if (nic->rx_queues > MAX_RCV_QUEUES_PER_QS)
4278c2ecf20Sopenharmony_ci		rx_queues = nic->rx_queues - MAX_RCV_QUEUES_PER_QS;
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_ci	tx_queues = nic->tx_queues + nic->xdp_tx_queues;
4308c2ecf20Sopenharmony_ci	if (tx_queues > MAX_SND_QUEUES_PER_QS)
4318c2ecf20Sopenharmony_ci		tx_queues = tx_queues - MAX_SND_QUEUES_PER_QS;
4328c2ecf20Sopenharmony_ci
4338c2ecf20Sopenharmony_ci	/* Set no of Rx/Tx queues in each of the SQsets */
4348c2ecf20Sopenharmony_ci	for (sqs = 0; sqs < nic->sqs_count; sqs++) {
4358c2ecf20Sopenharmony_ci		mbx.nicvf.msg = NIC_MBOX_MSG_SNICVF_PTR;
4368c2ecf20Sopenharmony_ci		mbx.nicvf.vf_id = nic->vf_id;
4378c2ecf20Sopenharmony_ci		mbx.nicvf.sqs_id = sqs;
4388c2ecf20Sopenharmony_ci		nicvf_send_msg_to_pf(nic, &mbx);
4398c2ecf20Sopenharmony_ci
4408c2ecf20Sopenharmony_ci		nic->snicvf[sqs]->sqs_id = sqs;
4418c2ecf20Sopenharmony_ci		if (rx_queues > MAX_RCV_QUEUES_PER_QS) {
4428c2ecf20Sopenharmony_ci			nic->snicvf[sqs]->qs->rq_cnt = MAX_RCV_QUEUES_PER_QS;
4438c2ecf20Sopenharmony_ci			rx_queues -= MAX_RCV_QUEUES_PER_QS;
4448c2ecf20Sopenharmony_ci		} else {
4458c2ecf20Sopenharmony_ci			nic->snicvf[sqs]->qs->rq_cnt = rx_queues;
4468c2ecf20Sopenharmony_ci			rx_queues = 0;
4478c2ecf20Sopenharmony_ci		}
4488c2ecf20Sopenharmony_ci
4498c2ecf20Sopenharmony_ci		if (tx_queues > MAX_SND_QUEUES_PER_QS) {
4508c2ecf20Sopenharmony_ci			nic->snicvf[sqs]->qs->sq_cnt = MAX_SND_QUEUES_PER_QS;
4518c2ecf20Sopenharmony_ci			tx_queues -= MAX_SND_QUEUES_PER_QS;
4528c2ecf20Sopenharmony_ci		} else {
4538c2ecf20Sopenharmony_ci			nic->snicvf[sqs]->qs->sq_cnt = tx_queues;
4548c2ecf20Sopenharmony_ci			tx_queues = 0;
4558c2ecf20Sopenharmony_ci		}
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_ci		nic->snicvf[sqs]->qs->cq_cnt =
4588c2ecf20Sopenharmony_ci		max(nic->snicvf[sqs]->qs->rq_cnt, nic->snicvf[sqs]->qs->sq_cnt);
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_ci		/* Initialize secondary Qset's queues and its interrupts */
4618c2ecf20Sopenharmony_ci		nicvf_open(nic->snicvf[sqs]->netdev);
4628c2ecf20Sopenharmony_ci	}
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_ci	/* Update stack with actual Rx/Tx queue count allocated */
4658c2ecf20Sopenharmony_ci	if (sqs_count != nic->sqs_count)
4668c2ecf20Sopenharmony_ci		nicvf_set_real_num_queues(nic->netdev,
4678c2ecf20Sopenharmony_ci					  nic->tx_queues, nic->rx_queues);
4688c2ecf20Sopenharmony_ci}
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_ci/* Send this Qset's nicvf pointer to PF.
4718c2ecf20Sopenharmony_ci * PF inturn sends primary VF's nicvf struct to secondary Qsets/VFs
4728c2ecf20Sopenharmony_ci * so that packets received by these Qsets can use primary VF's netdev
4738c2ecf20Sopenharmony_ci */
4748c2ecf20Sopenharmony_cistatic void nicvf_send_vf_struct(struct nicvf *nic)
4758c2ecf20Sopenharmony_ci{
4768c2ecf20Sopenharmony_ci	union nic_mbx mbx = {};
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_ci	mbx.nicvf.msg = NIC_MBOX_MSG_NICVF_PTR;
4798c2ecf20Sopenharmony_ci	mbx.nicvf.sqs_mode = nic->sqs_mode;
4808c2ecf20Sopenharmony_ci	mbx.nicvf.nicvf = (u64)nic;
4818c2ecf20Sopenharmony_ci	nicvf_send_msg_to_pf(nic, &mbx);
4828c2ecf20Sopenharmony_ci}
4838c2ecf20Sopenharmony_ci
4848c2ecf20Sopenharmony_cistatic void nicvf_get_primary_vf_struct(struct nicvf *nic)
4858c2ecf20Sopenharmony_ci{
4868c2ecf20Sopenharmony_ci	union nic_mbx mbx = {};
4878c2ecf20Sopenharmony_ci
4888c2ecf20Sopenharmony_ci	mbx.nicvf.msg = NIC_MBOX_MSG_PNICVF_PTR;
4898c2ecf20Sopenharmony_ci	nicvf_send_msg_to_pf(nic, &mbx);
4908c2ecf20Sopenharmony_ci}
4918c2ecf20Sopenharmony_ci
4928c2ecf20Sopenharmony_ciint nicvf_set_real_num_queues(struct net_device *netdev,
4938c2ecf20Sopenharmony_ci			      int tx_queues, int rx_queues)
4948c2ecf20Sopenharmony_ci{
4958c2ecf20Sopenharmony_ci	int err = 0;
4968c2ecf20Sopenharmony_ci
4978c2ecf20Sopenharmony_ci	err = netif_set_real_num_tx_queues(netdev, tx_queues);
4988c2ecf20Sopenharmony_ci	if (err) {
4998c2ecf20Sopenharmony_ci		netdev_err(netdev,
5008c2ecf20Sopenharmony_ci			   "Failed to set no of Tx queues: %d\n", tx_queues);
5018c2ecf20Sopenharmony_ci		return err;
5028c2ecf20Sopenharmony_ci	}
5038c2ecf20Sopenharmony_ci
5048c2ecf20Sopenharmony_ci	err = netif_set_real_num_rx_queues(netdev, rx_queues);
5058c2ecf20Sopenharmony_ci	if (err)
5068c2ecf20Sopenharmony_ci		netdev_err(netdev,
5078c2ecf20Sopenharmony_ci			   "Failed to set no of Rx queues: %d\n", rx_queues);
5088c2ecf20Sopenharmony_ci	return err;
5098c2ecf20Sopenharmony_ci}
5108c2ecf20Sopenharmony_ci
5118c2ecf20Sopenharmony_cistatic int nicvf_init_resources(struct nicvf *nic)
5128c2ecf20Sopenharmony_ci{
5138c2ecf20Sopenharmony_ci	int err;
5148c2ecf20Sopenharmony_ci
5158c2ecf20Sopenharmony_ci	/* Enable Qset */
5168c2ecf20Sopenharmony_ci	nicvf_qset_config(nic, true);
5178c2ecf20Sopenharmony_ci
5188c2ecf20Sopenharmony_ci	/* Initialize queues and HW for data transfer */
5198c2ecf20Sopenharmony_ci	err = nicvf_config_data_transfer(nic, true);
5208c2ecf20Sopenharmony_ci	if (err) {
5218c2ecf20Sopenharmony_ci		netdev_err(nic->netdev,
5228c2ecf20Sopenharmony_ci			   "Failed to alloc/config VF's QSet resources\n");
5238c2ecf20Sopenharmony_ci		return err;
5248c2ecf20Sopenharmony_ci	}
5258c2ecf20Sopenharmony_ci
5268c2ecf20Sopenharmony_ci	return 0;
5278c2ecf20Sopenharmony_ci}
5288c2ecf20Sopenharmony_ci
5298c2ecf20Sopenharmony_cistatic inline bool nicvf_xdp_rx(struct nicvf *nic, struct bpf_prog *prog,
5308c2ecf20Sopenharmony_ci				struct cqe_rx_t *cqe_rx, struct snd_queue *sq,
5318c2ecf20Sopenharmony_ci				struct rcv_queue *rq, struct sk_buff **skb)
5328c2ecf20Sopenharmony_ci{
5338c2ecf20Sopenharmony_ci	struct xdp_buff xdp;
5348c2ecf20Sopenharmony_ci	struct page *page;
5358c2ecf20Sopenharmony_ci	u32 action;
5368c2ecf20Sopenharmony_ci	u16 len, offset = 0;
5378c2ecf20Sopenharmony_ci	u64 dma_addr, cpu_addr;
5388c2ecf20Sopenharmony_ci	void *orig_data;
5398c2ecf20Sopenharmony_ci
5408c2ecf20Sopenharmony_ci	/* Retrieve packet buffer's DMA address and length */
5418c2ecf20Sopenharmony_ci	len = *((u16 *)((void *)cqe_rx + (3 * sizeof(u64))));
5428c2ecf20Sopenharmony_ci	dma_addr = *((u64 *)((void *)cqe_rx + (7 * sizeof(u64))));
5438c2ecf20Sopenharmony_ci
5448c2ecf20Sopenharmony_ci	cpu_addr = nicvf_iova_to_phys(nic, dma_addr);
5458c2ecf20Sopenharmony_ci	if (!cpu_addr)
5468c2ecf20Sopenharmony_ci		return false;
5478c2ecf20Sopenharmony_ci	cpu_addr = (u64)phys_to_virt(cpu_addr);
5488c2ecf20Sopenharmony_ci	page = virt_to_page((void *)cpu_addr);
5498c2ecf20Sopenharmony_ci
5508c2ecf20Sopenharmony_ci	xdp.data_hard_start = page_address(page);
5518c2ecf20Sopenharmony_ci	xdp.data = (void *)cpu_addr;
5528c2ecf20Sopenharmony_ci	xdp_set_data_meta_invalid(&xdp);
5538c2ecf20Sopenharmony_ci	xdp.data_end = xdp.data + len;
5548c2ecf20Sopenharmony_ci	xdp.rxq = &rq->xdp_rxq;
5558c2ecf20Sopenharmony_ci	xdp.frame_sz = RCV_FRAG_LEN + XDP_PACKET_HEADROOM;
5568c2ecf20Sopenharmony_ci	orig_data = xdp.data;
5578c2ecf20Sopenharmony_ci
5588c2ecf20Sopenharmony_ci	rcu_read_lock();
5598c2ecf20Sopenharmony_ci	action = bpf_prog_run_xdp(prog, &xdp);
5608c2ecf20Sopenharmony_ci	rcu_read_unlock();
5618c2ecf20Sopenharmony_ci
5628c2ecf20Sopenharmony_ci	len = xdp.data_end - xdp.data;
5638c2ecf20Sopenharmony_ci	/* Check if XDP program has changed headers */
5648c2ecf20Sopenharmony_ci	if (orig_data != xdp.data) {
5658c2ecf20Sopenharmony_ci		offset = orig_data - xdp.data;
5668c2ecf20Sopenharmony_ci		dma_addr -= offset;
5678c2ecf20Sopenharmony_ci	}
5688c2ecf20Sopenharmony_ci
5698c2ecf20Sopenharmony_ci	switch (action) {
5708c2ecf20Sopenharmony_ci	case XDP_PASS:
5718c2ecf20Sopenharmony_ci		/* Check if it's a recycled page, if not
5728c2ecf20Sopenharmony_ci		 * unmap the DMA mapping.
5738c2ecf20Sopenharmony_ci		 *
5748c2ecf20Sopenharmony_ci		 * Recycled page holds an extra reference.
5758c2ecf20Sopenharmony_ci		 */
5768c2ecf20Sopenharmony_ci		if (page_ref_count(page) == 1) {
5778c2ecf20Sopenharmony_ci			dma_addr &= PAGE_MASK;
5788c2ecf20Sopenharmony_ci			dma_unmap_page_attrs(&nic->pdev->dev, dma_addr,
5798c2ecf20Sopenharmony_ci					     RCV_FRAG_LEN + XDP_PACKET_HEADROOM,
5808c2ecf20Sopenharmony_ci					     DMA_FROM_DEVICE,
5818c2ecf20Sopenharmony_ci					     DMA_ATTR_SKIP_CPU_SYNC);
5828c2ecf20Sopenharmony_ci		}
5838c2ecf20Sopenharmony_ci
5848c2ecf20Sopenharmony_ci		/* Build SKB and pass on packet to network stack */
5858c2ecf20Sopenharmony_ci		*skb = build_skb(xdp.data,
5868c2ecf20Sopenharmony_ci				 RCV_FRAG_LEN - cqe_rx->align_pad + offset);
5878c2ecf20Sopenharmony_ci		if (!*skb)
5888c2ecf20Sopenharmony_ci			put_page(page);
5898c2ecf20Sopenharmony_ci		else
5908c2ecf20Sopenharmony_ci			skb_put(*skb, len);
5918c2ecf20Sopenharmony_ci		return false;
5928c2ecf20Sopenharmony_ci	case XDP_TX:
5938c2ecf20Sopenharmony_ci		nicvf_xdp_sq_append_pkt(nic, sq, (u64)xdp.data, dma_addr, len);
5948c2ecf20Sopenharmony_ci		return true;
5958c2ecf20Sopenharmony_ci	default:
5968c2ecf20Sopenharmony_ci		bpf_warn_invalid_xdp_action(action);
5978c2ecf20Sopenharmony_ci		fallthrough;
5988c2ecf20Sopenharmony_ci	case XDP_ABORTED:
5998c2ecf20Sopenharmony_ci		trace_xdp_exception(nic->netdev, prog, action);
6008c2ecf20Sopenharmony_ci		fallthrough;
6018c2ecf20Sopenharmony_ci	case XDP_DROP:
6028c2ecf20Sopenharmony_ci		/* Check if it's a recycled page, if not
6038c2ecf20Sopenharmony_ci		 * unmap the DMA mapping.
6048c2ecf20Sopenharmony_ci		 *
6058c2ecf20Sopenharmony_ci		 * Recycled page holds an extra reference.
6068c2ecf20Sopenharmony_ci		 */
6078c2ecf20Sopenharmony_ci		if (page_ref_count(page) == 1) {
6088c2ecf20Sopenharmony_ci			dma_addr &= PAGE_MASK;
6098c2ecf20Sopenharmony_ci			dma_unmap_page_attrs(&nic->pdev->dev, dma_addr,
6108c2ecf20Sopenharmony_ci					     RCV_FRAG_LEN + XDP_PACKET_HEADROOM,
6118c2ecf20Sopenharmony_ci					     DMA_FROM_DEVICE,
6128c2ecf20Sopenharmony_ci					     DMA_ATTR_SKIP_CPU_SYNC);
6138c2ecf20Sopenharmony_ci		}
6148c2ecf20Sopenharmony_ci		put_page(page);
6158c2ecf20Sopenharmony_ci		return true;
6168c2ecf20Sopenharmony_ci	}
6178c2ecf20Sopenharmony_ci	return false;
6188c2ecf20Sopenharmony_ci}
6198c2ecf20Sopenharmony_ci
6208c2ecf20Sopenharmony_cistatic void nicvf_snd_ptp_handler(struct net_device *netdev,
6218c2ecf20Sopenharmony_ci				  struct cqe_send_t *cqe_tx)
6228c2ecf20Sopenharmony_ci{
6238c2ecf20Sopenharmony_ci	struct nicvf *nic = netdev_priv(netdev);
6248c2ecf20Sopenharmony_ci	struct skb_shared_hwtstamps ts;
6258c2ecf20Sopenharmony_ci	u64 ns;
6268c2ecf20Sopenharmony_ci
6278c2ecf20Sopenharmony_ci	nic = nic->pnicvf;
6288c2ecf20Sopenharmony_ci
6298c2ecf20Sopenharmony_ci	/* Sync for 'ptp_skb' */
6308c2ecf20Sopenharmony_ci	smp_rmb();
6318c2ecf20Sopenharmony_ci
6328c2ecf20Sopenharmony_ci	/* New timestamp request can be queued now */
6338c2ecf20Sopenharmony_ci	atomic_set(&nic->tx_ptp_skbs, 0);
6348c2ecf20Sopenharmony_ci
6358c2ecf20Sopenharmony_ci	/* Check for timestamp requested skb */
6368c2ecf20Sopenharmony_ci	if (!nic->ptp_skb)
6378c2ecf20Sopenharmony_ci		return;
6388c2ecf20Sopenharmony_ci
6398c2ecf20Sopenharmony_ci	/* Check if timestamping is timedout, which is set to 10us */
6408c2ecf20Sopenharmony_ci	if (cqe_tx->send_status == CQ_TX_ERROP_TSTMP_TIMEOUT ||
6418c2ecf20Sopenharmony_ci	    cqe_tx->send_status == CQ_TX_ERROP_TSTMP_CONFLICT)
6428c2ecf20Sopenharmony_ci		goto no_tstamp;
6438c2ecf20Sopenharmony_ci
6448c2ecf20Sopenharmony_ci	/* Get the timestamp */
6458c2ecf20Sopenharmony_ci	memset(&ts, 0, sizeof(ts));
6468c2ecf20Sopenharmony_ci	ns = cavium_ptp_tstamp2time(nic->ptp_clock, cqe_tx->ptp_timestamp);
6478c2ecf20Sopenharmony_ci	ts.hwtstamp = ns_to_ktime(ns);
6488c2ecf20Sopenharmony_ci	skb_tstamp_tx(nic->ptp_skb, &ts);
6498c2ecf20Sopenharmony_ci
6508c2ecf20Sopenharmony_cino_tstamp:
6518c2ecf20Sopenharmony_ci	/* Free the original skb */
6528c2ecf20Sopenharmony_ci	dev_kfree_skb_any(nic->ptp_skb);
6538c2ecf20Sopenharmony_ci	nic->ptp_skb = NULL;
6548c2ecf20Sopenharmony_ci	/* Sync 'ptp_skb' */
6558c2ecf20Sopenharmony_ci	smp_wmb();
6568c2ecf20Sopenharmony_ci}
6578c2ecf20Sopenharmony_ci
6588c2ecf20Sopenharmony_cistatic void nicvf_snd_pkt_handler(struct net_device *netdev,
6598c2ecf20Sopenharmony_ci				  struct cqe_send_t *cqe_tx,
6608c2ecf20Sopenharmony_ci				  int budget, int *subdesc_cnt,
6618c2ecf20Sopenharmony_ci				  unsigned int *tx_pkts, unsigned int *tx_bytes)
6628c2ecf20Sopenharmony_ci{
6638c2ecf20Sopenharmony_ci	struct sk_buff *skb = NULL;
6648c2ecf20Sopenharmony_ci	struct page *page;
6658c2ecf20Sopenharmony_ci	struct nicvf *nic = netdev_priv(netdev);
6668c2ecf20Sopenharmony_ci	struct snd_queue *sq;
6678c2ecf20Sopenharmony_ci	struct sq_hdr_subdesc *hdr;
6688c2ecf20Sopenharmony_ci	struct sq_hdr_subdesc *tso_sqe;
6698c2ecf20Sopenharmony_ci
6708c2ecf20Sopenharmony_ci	sq = &nic->qs->sq[cqe_tx->sq_idx];
6718c2ecf20Sopenharmony_ci
6728c2ecf20Sopenharmony_ci	hdr = (struct sq_hdr_subdesc *)GET_SQ_DESC(sq, cqe_tx->sqe_ptr);
6738c2ecf20Sopenharmony_ci	if (hdr->subdesc_type != SQ_DESC_TYPE_HEADER)
6748c2ecf20Sopenharmony_ci		return;
6758c2ecf20Sopenharmony_ci
6768c2ecf20Sopenharmony_ci	/* Check for errors */
6778c2ecf20Sopenharmony_ci	if (cqe_tx->send_status)
6788c2ecf20Sopenharmony_ci		nicvf_check_cqe_tx_errs(nic->pnicvf, cqe_tx);
6798c2ecf20Sopenharmony_ci
6808c2ecf20Sopenharmony_ci	/* Is this a XDP designated Tx queue */
6818c2ecf20Sopenharmony_ci	if (sq->is_xdp) {
6828c2ecf20Sopenharmony_ci		page = (struct page *)sq->xdp_page[cqe_tx->sqe_ptr];
6838c2ecf20Sopenharmony_ci		/* Check if it's recycled page or else unmap DMA mapping */
6848c2ecf20Sopenharmony_ci		if (page && (page_ref_count(page) == 1))
6858c2ecf20Sopenharmony_ci			nicvf_unmap_sndq_buffers(nic, sq, cqe_tx->sqe_ptr,
6868c2ecf20Sopenharmony_ci						 hdr->subdesc_cnt);
6878c2ecf20Sopenharmony_ci
6888c2ecf20Sopenharmony_ci		/* Release page reference for recycling */
6898c2ecf20Sopenharmony_ci		if (page)
6908c2ecf20Sopenharmony_ci			put_page(page);
6918c2ecf20Sopenharmony_ci		sq->xdp_page[cqe_tx->sqe_ptr] = (u64)NULL;
6928c2ecf20Sopenharmony_ci		*subdesc_cnt += hdr->subdesc_cnt + 1;
6938c2ecf20Sopenharmony_ci		return;
6948c2ecf20Sopenharmony_ci	}
6958c2ecf20Sopenharmony_ci
6968c2ecf20Sopenharmony_ci	skb = (struct sk_buff *)sq->skbuff[cqe_tx->sqe_ptr];
6978c2ecf20Sopenharmony_ci	if (skb) {
6988c2ecf20Sopenharmony_ci		/* Check for dummy descriptor used for HW TSO offload on 88xx */
6998c2ecf20Sopenharmony_ci		if (hdr->dont_send) {
7008c2ecf20Sopenharmony_ci			/* Get actual TSO descriptors and free them */
7018c2ecf20Sopenharmony_ci			tso_sqe =
7028c2ecf20Sopenharmony_ci			 (struct sq_hdr_subdesc *)GET_SQ_DESC(sq, hdr->rsvd2);
7038c2ecf20Sopenharmony_ci			nicvf_unmap_sndq_buffers(nic, sq, hdr->rsvd2,
7048c2ecf20Sopenharmony_ci						 tso_sqe->subdesc_cnt);
7058c2ecf20Sopenharmony_ci			*subdesc_cnt += tso_sqe->subdesc_cnt + 1;
7068c2ecf20Sopenharmony_ci		} else {
7078c2ecf20Sopenharmony_ci			nicvf_unmap_sndq_buffers(nic, sq, cqe_tx->sqe_ptr,
7088c2ecf20Sopenharmony_ci						 hdr->subdesc_cnt);
7098c2ecf20Sopenharmony_ci		}
7108c2ecf20Sopenharmony_ci		*subdesc_cnt += hdr->subdesc_cnt + 1;
7118c2ecf20Sopenharmony_ci		prefetch(skb);
7128c2ecf20Sopenharmony_ci		(*tx_pkts)++;
7138c2ecf20Sopenharmony_ci		*tx_bytes += skb->len;
7148c2ecf20Sopenharmony_ci		/* If timestamp is requested for this skb, don't free it */
7158c2ecf20Sopenharmony_ci		if (skb_shinfo(skb)->tx_flags & SKBTX_IN_PROGRESS &&
7168c2ecf20Sopenharmony_ci		    !nic->pnicvf->ptp_skb)
7178c2ecf20Sopenharmony_ci			nic->pnicvf->ptp_skb = skb;
7188c2ecf20Sopenharmony_ci		else
7198c2ecf20Sopenharmony_ci			napi_consume_skb(skb, budget);
7208c2ecf20Sopenharmony_ci		sq->skbuff[cqe_tx->sqe_ptr] = (u64)NULL;
7218c2ecf20Sopenharmony_ci	} else {
7228c2ecf20Sopenharmony_ci		/* In case of SW TSO on 88xx, only last segment will have
7238c2ecf20Sopenharmony_ci		 * a SKB attached, so just free SQEs here.
7248c2ecf20Sopenharmony_ci		 */
7258c2ecf20Sopenharmony_ci		if (!nic->hw_tso)
7268c2ecf20Sopenharmony_ci			*subdesc_cnt += hdr->subdesc_cnt + 1;
7278c2ecf20Sopenharmony_ci	}
7288c2ecf20Sopenharmony_ci}
7298c2ecf20Sopenharmony_ci
7308c2ecf20Sopenharmony_cistatic inline void nicvf_set_rxhash(struct net_device *netdev,
7318c2ecf20Sopenharmony_ci				    struct cqe_rx_t *cqe_rx,
7328c2ecf20Sopenharmony_ci				    struct sk_buff *skb)
7338c2ecf20Sopenharmony_ci{
7348c2ecf20Sopenharmony_ci	u8 hash_type;
7358c2ecf20Sopenharmony_ci	u32 hash;
7368c2ecf20Sopenharmony_ci
7378c2ecf20Sopenharmony_ci	if (!(netdev->features & NETIF_F_RXHASH))
7388c2ecf20Sopenharmony_ci		return;
7398c2ecf20Sopenharmony_ci
7408c2ecf20Sopenharmony_ci	switch (cqe_rx->rss_alg) {
7418c2ecf20Sopenharmony_ci	case RSS_ALG_TCP_IP:
7428c2ecf20Sopenharmony_ci	case RSS_ALG_UDP_IP:
7438c2ecf20Sopenharmony_ci		hash_type = PKT_HASH_TYPE_L4;
7448c2ecf20Sopenharmony_ci		hash = cqe_rx->rss_tag;
7458c2ecf20Sopenharmony_ci		break;
7468c2ecf20Sopenharmony_ci	case RSS_ALG_IP:
7478c2ecf20Sopenharmony_ci		hash_type = PKT_HASH_TYPE_L3;
7488c2ecf20Sopenharmony_ci		hash = cqe_rx->rss_tag;
7498c2ecf20Sopenharmony_ci		break;
7508c2ecf20Sopenharmony_ci	default:
7518c2ecf20Sopenharmony_ci		hash_type = PKT_HASH_TYPE_NONE;
7528c2ecf20Sopenharmony_ci		hash = 0;
7538c2ecf20Sopenharmony_ci	}
7548c2ecf20Sopenharmony_ci
7558c2ecf20Sopenharmony_ci	skb_set_hash(skb, hash, hash_type);
7568c2ecf20Sopenharmony_ci}
7578c2ecf20Sopenharmony_ci
7588c2ecf20Sopenharmony_cistatic inline void nicvf_set_rxtstamp(struct nicvf *nic, struct sk_buff *skb)
7598c2ecf20Sopenharmony_ci{
7608c2ecf20Sopenharmony_ci	u64 ns;
7618c2ecf20Sopenharmony_ci
7628c2ecf20Sopenharmony_ci	if (!nic->ptp_clock || !nic->hw_rx_tstamp)
7638c2ecf20Sopenharmony_ci		return;
7648c2ecf20Sopenharmony_ci
7658c2ecf20Sopenharmony_ci	/* The first 8 bytes is the timestamp */
7668c2ecf20Sopenharmony_ci	ns = cavium_ptp_tstamp2time(nic->ptp_clock,
7678c2ecf20Sopenharmony_ci				    be64_to_cpu(*(__be64 *)skb->data));
7688c2ecf20Sopenharmony_ci	skb_hwtstamps(skb)->hwtstamp = ns_to_ktime(ns);
7698c2ecf20Sopenharmony_ci
7708c2ecf20Sopenharmony_ci	__skb_pull(skb, 8);
7718c2ecf20Sopenharmony_ci}
7728c2ecf20Sopenharmony_ci
7738c2ecf20Sopenharmony_cistatic void nicvf_rcv_pkt_handler(struct net_device *netdev,
7748c2ecf20Sopenharmony_ci				  struct napi_struct *napi,
7758c2ecf20Sopenharmony_ci				  struct cqe_rx_t *cqe_rx,
7768c2ecf20Sopenharmony_ci				  struct snd_queue *sq, struct rcv_queue *rq)
7778c2ecf20Sopenharmony_ci{
7788c2ecf20Sopenharmony_ci	struct sk_buff *skb = NULL;
7798c2ecf20Sopenharmony_ci	struct nicvf *nic = netdev_priv(netdev);
7808c2ecf20Sopenharmony_ci	struct nicvf *snic = nic;
7818c2ecf20Sopenharmony_ci	int err = 0;
7828c2ecf20Sopenharmony_ci	int rq_idx;
7838c2ecf20Sopenharmony_ci
7848c2ecf20Sopenharmony_ci	rq_idx = nicvf_netdev_qidx(nic, cqe_rx->rq_idx);
7858c2ecf20Sopenharmony_ci
7868c2ecf20Sopenharmony_ci	if (nic->sqs_mode) {
7878c2ecf20Sopenharmony_ci		/* Use primary VF's 'nicvf' struct */
7888c2ecf20Sopenharmony_ci		nic = nic->pnicvf;
7898c2ecf20Sopenharmony_ci		netdev = nic->netdev;
7908c2ecf20Sopenharmony_ci	}
7918c2ecf20Sopenharmony_ci
7928c2ecf20Sopenharmony_ci	/* Check for errors */
7938c2ecf20Sopenharmony_ci	if (cqe_rx->err_level || cqe_rx->err_opcode) {
7948c2ecf20Sopenharmony_ci		err = nicvf_check_cqe_rx_errs(nic, cqe_rx);
7958c2ecf20Sopenharmony_ci		if (err && !cqe_rx->rb_cnt)
7968c2ecf20Sopenharmony_ci			return;
7978c2ecf20Sopenharmony_ci	}
7988c2ecf20Sopenharmony_ci
7998c2ecf20Sopenharmony_ci	/* For XDP, ignore pkts spanning multiple pages */
8008c2ecf20Sopenharmony_ci	if (nic->xdp_prog && (cqe_rx->rb_cnt == 1)) {
8018c2ecf20Sopenharmony_ci		/* Packet consumed by XDP */
8028c2ecf20Sopenharmony_ci		if (nicvf_xdp_rx(snic, nic->xdp_prog, cqe_rx, sq, rq, &skb))
8038c2ecf20Sopenharmony_ci			return;
8048c2ecf20Sopenharmony_ci	} else {
8058c2ecf20Sopenharmony_ci		skb = nicvf_get_rcv_skb(snic, cqe_rx,
8068c2ecf20Sopenharmony_ci					nic->xdp_prog ? true : false);
8078c2ecf20Sopenharmony_ci	}
8088c2ecf20Sopenharmony_ci
8098c2ecf20Sopenharmony_ci	if (!skb)
8108c2ecf20Sopenharmony_ci		return;
8118c2ecf20Sopenharmony_ci
8128c2ecf20Sopenharmony_ci	if (netif_msg_pktdata(nic)) {
8138c2ecf20Sopenharmony_ci		netdev_info(nic->netdev, "skb 0x%p, len=%d\n", skb, skb->len);
8148c2ecf20Sopenharmony_ci		print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 16, 1,
8158c2ecf20Sopenharmony_ci			       skb->data, skb->len, true);
8168c2ecf20Sopenharmony_ci	}
8178c2ecf20Sopenharmony_ci
8188c2ecf20Sopenharmony_ci	/* If error packet, drop it here */
8198c2ecf20Sopenharmony_ci	if (err) {
8208c2ecf20Sopenharmony_ci		dev_kfree_skb_any(skb);
8218c2ecf20Sopenharmony_ci		return;
8228c2ecf20Sopenharmony_ci	}
8238c2ecf20Sopenharmony_ci
8248c2ecf20Sopenharmony_ci	nicvf_set_rxtstamp(nic, skb);
8258c2ecf20Sopenharmony_ci	nicvf_set_rxhash(netdev, cqe_rx, skb);
8268c2ecf20Sopenharmony_ci
8278c2ecf20Sopenharmony_ci	skb_record_rx_queue(skb, rq_idx);
8288c2ecf20Sopenharmony_ci	if (netdev->hw_features & NETIF_F_RXCSUM) {
8298c2ecf20Sopenharmony_ci		/* HW by default verifies TCP/UDP/SCTP checksums */
8308c2ecf20Sopenharmony_ci		skb->ip_summed = CHECKSUM_UNNECESSARY;
8318c2ecf20Sopenharmony_ci	} else {
8328c2ecf20Sopenharmony_ci		skb_checksum_none_assert(skb);
8338c2ecf20Sopenharmony_ci	}
8348c2ecf20Sopenharmony_ci
8358c2ecf20Sopenharmony_ci	skb->protocol = eth_type_trans(skb, netdev);
8368c2ecf20Sopenharmony_ci
8378c2ecf20Sopenharmony_ci	/* Check for stripped VLAN */
8388c2ecf20Sopenharmony_ci	if (cqe_rx->vlan_found && cqe_rx->vlan_stripped)
8398c2ecf20Sopenharmony_ci		__vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q),
8408c2ecf20Sopenharmony_ci				       ntohs((__force __be16)cqe_rx->vlan_tci));
8418c2ecf20Sopenharmony_ci
8428c2ecf20Sopenharmony_ci	if (napi && (netdev->features & NETIF_F_GRO))
8438c2ecf20Sopenharmony_ci		napi_gro_receive(napi, skb);
8448c2ecf20Sopenharmony_ci	else
8458c2ecf20Sopenharmony_ci		netif_receive_skb(skb);
8468c2ecf20Sopenharmony_ci}
8478c2ecf20Sopenharmony_ci
8488c2ecf20Sopenharmony_cistatic int nicvf_cq_intr_handler(struct net_device *netdev, u8 cq_idx,
8498c2ecf20Sopenharmony_ci				 struct napi_struct *napi, int budget)
8508c2ecf20Sopenharmony_ci{
8518c2ecf20Sopenharmony_ci	int processed_cqe, work_done = 0, tx_done = 0;
8528c2ecf20Sopenharmony_ci	int cqe_count, cqe_head;
8538c2ecf20Sopenharmony_ci	int subdesc_cnt = 0;
8548c2ecf20Sopenharmony_ci	struct nicvf *nic = netdev_priv(netdev);
8558c2ecf20Sopenharmony_ci	struct queue_set *qs = nic->qs;
8568c2ecf20Sopenharmony_ci	struct cmp_queue *cq = &qs->cq[cq_idx];
8578c2ecf20Sopenharmony_ci	struct cqe_rx_t *cq_desc;
8588c2ecf20Sopenharmony_ci	struct netdev_queue *txq;
8598c2ecf20Sopenharmony_ci	struct snd_queue *sq = &qs->sq[cq_idx];
8608c2ecf20Sopenharmony_ci	struct rcv_queue *rq = &qs->rq[cq_idx];
8618c2ecf20Sopenharmony_ci	unsigned int tx_pkts = 0, tx_bytes = 0, txq_idx;
8628c2ecf20Sopenharmony_ci
8638c2ecf20Sopenharmony_ci	spin_lock_bh(&cq->lock);
8648c2ecf20Sopenharmony_ciloop:
8658c2ecf20Sopenharmony_ci	processed_cqe = 0;
8668c2ecf20Sopenharmony_ci	/* Get no of valid CQ entries to process */
8678c2ecf20Sopenharmony_ci	cqe_count = nicvf_queue_reg_read(nic, NIC_QSET_CQ_0_7_STATUS, cq_idx);
8688c2ecf20Sopenharmony_ci	cqe_count &= CQ_CQE_COUNT;
8698c2ecf20Sopenharmony_ci	if (!cqe_count)
8708c2ecf20Sopenharmony_ci		goto done;
8718c2ecf20Sopenharmony_ci
8728c2ecf20Sopenharmony_ci	/* Get head of the valid CQ entries */
8738c2ecf20Sopenharmony_ci	cqe_head = nicvf_queue_reg_read(nic, NIC_QSET_CQ_0_7_HEAD, cq_idx) >> 9;
8748c2ecf20Sopenharmony_ci	cqe_head &= 0xFFFF;
8758c2ecf20Sopenharmony_ci
8768c2ecf20Sopenharmony_ci	while (processed_cqe < cqe_count) {
8778c2ecf20Sopenharmony_ci		/* Get the CQ descriptor */
8788c2ecf20Sopenharmony_ci		cq_desc = (struct cqe_rx_t *)GET_CQ_DESC(cq, cqe_head);
8798c2ecf20Sopenharmony_ci		cqe_head++;
8808c2ecf20Sopenharmony_ci		cqe_head &= (cq->dmem.q_len - 1);
8818c2ecf20Sopenharmony_ci		/* Initiate prefetch for next descriptor */
8828c2ecf20Sopenharmony_ci		prefetch((struct cqe_rx_t *)GET_CQ_DESC(cq, cqe_head));
8838c2ecf20Sopenharmony_ci
8848c2ecf20Sopenharmony_ci		if ((work_done >= budget) && napi &&
8858c2ecf20Sopenharmony_ci		    (cq_desc->cqe_type != CQE_TYPE_SEND)) {
8868c2ecf20Sopenharmony_ci			break;
8878c2ecf20Sopenharmony_ci		}
8888c2ecf20Sopenharmony_ci
8898c2ecf20Sopenharmony_ci		switch (cq_desc->cqe_type) {
8908c2ecf20Sopenharmony_ci		case CQE_TYPE_RX:
8918c2ecf20Sopenharmony_ci			nicvf_rcv_pkt_handler(netdev, napi, cq_desc, sq, rq);
8928c2ecf20Sopenharmony_ci			work_done++;
8938c2ecf20Sopenharmony_ci		break;
8948c2ecf20Sopenharmony_ci		case CQE_TYPE_SEND:
8958c2ecf20Sopenharmony_ci			nicvf_snd_pkt_handler(netdev, (void *)cq_desc,
8968c2ecf20Sopenharmony_ci					      budget, &subdesc_cnt,
8978c2ecf20Sopenharmony_ci					      &tx_pkts, &tx_bytes);
8988c2ecf20Sopenharmony_ci			tx_done++;
8998c2ecf20Sopenharmony_ci		break;
9008c2ecf20Sopenharmony_ci		case CQE_TYPE_SEND_PTP:
9018c2ecf20Sopenharmony_ci			nicvf_snd_ptp_handler(netdev, (void *)cq_desc);
9028c2ecf20Sopenharmony_ci		break;
9038c2ecf20Sopenharmony_ci		case CQE_TYPE_INVALID:
9048c2ecf20Sopenharmony_ci		case CQE_TYPE_RX_SPLIT:
9058c2ecf20Sopenharmony_ci		case CQE_TYPE_RX_TCP:
9068c2ecf20Sopenharmony_ci			/* Ignore for now */
9078c2ecf20Sopenharmony_ci		break;
9088c2ecf20Sopenharmony_ci		}
9098c2ecf20Sopenharmony_ci		processed_cqe++;
9108c2ecf20Sopenharmony_ci	}
9118c2ecf20Sopenharmony_ci
9128c2ecf20Sopenharmony_ci	/* Ring doorbell to inform H/W to reuse processed CQEs */
9138c2ecf20Sopenharmony_ci	nicvf_queue_reg_write(nic, NIC_QSET_CQ_0_7_DOOR,
9148c2ecf20Sopenharmony_ci			      cq_idx, processed_cqe);
9158c2ecf20Sopenharmony_ci
9168c2ecf20Sopenharmony_ci	if ((work_done < budget) && napi)
9178c2ecf20Sopenharmony_ci		goto loop;
9188c2ecf20Sopenharmony_ci
9198c2ecf20Sopenharmony_cidone:
9208c2ecf20Sopenharmony_ci	/* Update SQ's descriptor free count */
9218c2ecf20Sopenharmony_ci	if (subdesc_cnt)
9228c2ecf20Sopenharmony_ci		nicvf_put_sq_desc(sq, subdesc_cnt);
9238c2ecf20Sopenharmony_ci
9248c2ecf20Sopenharmony_ci	txq_idx = nicvf_netdev_qidx(nic, cq_idx);
9258c2ecf20Sopenharmony_ci	/* Handle XDP TX queues */
9268c2ecf20Sopenharmony_ci	if (nic->pnicvf->xdp_prog) {
9278c2ecf20Sopenharmony_ci		if (txq_idx < nic->pnicvf->xdp_tx_queues) {
9288c2ecf20Sopenharmony_ci			nicvf_xdp_sq_doorbell(nic, sq, cq_idx);
9298c2ecf20Sopenharmony_ci			goto out;
9308c2ecf20Sopenharmony_ci		}
9318c2ecf20Sopenharmony_ci		nic = nic->pnicvf;
9328c2ecf20Sopenharmony_ci		txq_idx -= nic->pnicvf->xdp_tx_queues;
9338c2ecf20Sopenharmony_ci	}
9348c2ecf20Sopenharmony_ci
9358c2ecf20Sopenharmony_ci	/* Wakeup TXQ if its stopped earlier due to SQ full */
9368c2ecf20Sopenharmony_ci	if (tx_done ||
9378c2ecf20Sopenharmony_ci	    (atomic_read(&sq->free_cnt) >= MIN_SQ_DESC_PER_PKT_XMIT)) {
9388c2ecf20Sopenharmony_ci		netdev = nic->pnicvf->netdev;
9398c2ecf20Sopenharmony_ci		txq = netdev_get_tx_queue(netdev, txq_idx);
9408c2ecf20Sopenharmony_ci		if (tx_pkts)
9418c2ecf20Sopenharmony_ci			netdev_tx_completed_queue(txq, tx_pkts, tx_bytes);
9428c2ecf20Sopenharmony_ci
9438c2ecf20Sopenharmony_ci		/* To read updated queue and carrier status */
9448c2ecf20Sopenharmony_ci		smp_mb();
9458c2ecf20Sopenharmony_ci		if (netif_tx_queue_stopped(txq) && netif_carrier_ok(netdev)) {
9468c2ecf20Sopenharmony_ci			netif_tx_wake_queue(txq);
9478c2ecf20Sopenharmony_ci			nic = nic->pnicvf;
9488c2ecf20Sopenharmony_ci			this_cpu_inc(nic->drv_stats->txq_wake);
9498c2ecf20Sopenharmony_ci			netif_warn(nic, tx_err, netdev,
9508c2ecf20Sopenharmony_ci				   "Transmit queue wakeup SQ%d\n", txq_idx);
9518c2ecf20Sopenharmony_ci		}
9528c2ecf20Sopenharmony_ci	}
9538c2ecf20Sopenharmony_ci
9548c2ecf20Sopenharmony_ciout:
9558c2ecf20Sopenharmony_ci	spin_unlock_bh(&cq->lock);
9568c2ecf20Sopenharmony_ci	return work_done;
9578c2ecf20Sopenharmony_ci}
9588c2ecf20Sopenharmony_ci
9598c2ecf20Sopenharmony_cistatic int nicvf_poll(struct napi_struct *napi, int budget)
9608c2ecf20Sopenharmony_ci{
9618c2ecf20Sopenharmony_ci	u64  cq_head;
9628c2ecf20Sopenharmony_ci	int  work_done = 0;
9638c2ecf20Sopenharmony_ci	struct net_device *netdev = napi->dev;
9648c2ecf20Sopenharmony_ci	struct nicvf *nic = netdev_priv(netdev);
9658c2ecf20Sopenharmony_ci	struct nicvf_cq_poll *cq;
9668c2ecf20Sopenharmony_ci
9678c2ecf20Sopenharmony_ci	cq = container_of(napi, struct nicvf_cq_poll, napi);
9688c2ecf20Sopenharmony_ci	work_done = nicvf_cq_intr_handler(netdev, cq->cq_idx, napi, budget);
9698c2ecf20Sopenharmony_ci
9708c2ecf20Sopenharmony_ci	if (work_done < budget) {
9718c2ecf20Sopenharmony_ci		/* Slow packet rate, exit polling */
9728c2ecf20Sopenharmony_ci		napi_complete_done(napi, work_done);
9738c2ecf20Sopenharmony_ci		/* Re-enable interrupts */
9748c2ecf20Sopenharmony_ci		cq_head = nicvf_queue_reg_read(nic, NIC_QSET_CQ_0_7_HEAD,
9758c2ecf20Sopenharmony_ci					       cq->cq_idx);
9768c2ecf20Sopenharmony_ci		nicvf_clear_intr(nic, NICVF_INTR_CQ, cq->cq_idx);
9778c2ecf20Sopenharmony_ci		nicvf_queue_reg_write(nic, NIC_QSET_CQ_0_7_HEAD,
9788c2ecf20Sopenharmony_ci				      cq->cq_idx, cq_head);
9798c2ecf20Sopenharmony_ci		nicvf_enable_intr(nic, NICVF_INTR_CQ, cq->cq_idx);
9808c2ecf20Sopenharmony_ci	}
9818c2ecf20Sopenharmony_ci	return work_done;
9828c2ecf20Sopenharmony_ci}
9838c2ecf20Sopenharmony_ci
9848c2ecf20Sopenharmony_ci/* Qset error interrupt handler
9858c2ecf20Sopenharmony_ci *
9868c2ecf20Sopenharmony_ci * As of now only CQ errors are handled
9878c2ecf20Sopenharmony_ci */
9888c2ecf20Sopenharmony_cistatic void nicvf_handle_qs_err(struct tasklet_struct *t)
9898c2ecf20Sopenharmony_ci{
9908c2ecf20Sopenharmony_ci	struct nicvf *nic = from_tasklet(nic, t, qs_err_task);
9918c2ecf20Sopenharmony_ci	struct queue_set *qs = nic->qs;
9928c2ecf20Sopenharmony_ci	int qidx;
9938c2ecf20Sopenharmony_ci	u64 status;
9948c2ecf20Sopenharmony_ci
9958c2ecf20Sopenharmony_ci	netif_tx_disable(nic->netdev);
9968c2ecf20Sopenharmony_ci
9978c2ecf20Sopenharmony_ci	/* Check if it is CQ err */
9988c2ecf20Sopenharmony_ci	for (qidx = 0; qidx < qs->cq_cnt; qidx++) {
9998c2ecf20Sopenharmony_ci		status = nicvf_queue_reg_read(nic, NIC_QSET_CQ_0_7_STATUS,
10008c2ecf20Sopenharmony_ci					      qidx);
10018c2ecf20Sopenharmony_ci		if (!(status & CQ_ERR_MASK))
10028c2ecf20Sopenharmony_ci			continue;
10038c2ecf20Sopenharmony_ci		/* Process already queued CQEs and reconfig CQ */
10048c2ecf20Sopenharmony_ci		nicvf_disable_intr(nic, NICVF_INTR_CQ, qidx);
10058c2ecf20Sopenharmony_ci		nicvf_sq_disable(nic, qidx);
10068c2ecf20Sopenharmony_ci		nicvf_cq_intr_handler(nic->netdev, qidx, NULL, 0);
10078c2ecf20Sopenharmony_ci		nicvf_cmp_queue_config(nic, qs, qidx, true);
10088c2ecf20Sopenharmony_ci		nicvf_sq_free_used_descs(nic->netdev, &qs->sq[qidx], qidx);
10098c2ecf20Sopenharmony_ci		nicvf_sq_enable(nic, &qs->sq[qidx], qidx);
10108c2ecf20Sopenharmony_ci
10118c2ecf20Sopenharmony_ci		nicvf_enable_intr(nic, NICVF_INTR_CQ, qidx);
10128c2ecf20Sopenharmony_ci	}
10138c2ecf20Sopenharmony_ci
10148c2ecf20Sopenharmony_ci	netif_tx_start_all_queues(nic->netdev);
10158c2ecf20Sopenharmony_ci	/* Re-enable Qset error interrupt */
10168c2ecf20Sopenharmony_ci	nicvf_enable_intr(nic, NICVF_INTR_QS_ERR, 0);
10178c2ecf20Sopenharmony_ci}
10188c2ecf20Sopenharmony_ci
10198c2ecf20Sopenharmony_cistatic void nicvf_dump_intr_status(struct nicvf *nic)
10208c2ecf20Sopenharmony_ci{
10218c2ecf20Sopenharmony_ci	netif_info(nic, intr, nic->netdev, "interrupt status 0x%llx\n",
10228c2ecf20Sopenharmony_ci		   nicvf_reg_read(nic, NIC_VF_INT));
10238c2ecf20Sopenharmony_ci}
10248c2ecf20Sopenharmony_ci
10258c2ecf20Sopenharmony_cistatic irqreturn_t nicvf_misc_intr_handler(int irq, void *nicvf_irq)
10268c2ecf20Sopenharmony_ci{
10278c2ecf20Sopenharmony_ci	struct nicvf *nic = (struct nicvf *)nicvf_irq;
10288c2ecf20Sopenharmony_ci	u64 intr;
10298c2ecf20Sopenharmony_ci
10308c2ecf20Sopenharmony_ci	nicvf_dump_intr_status(nic);
10318c2ecf20Sopenharmony_ci
10328c2ecf20Sopenharmony_ci	intr = nicvf_reg_read(nic, NIC_VF_INT);
10338c2ecf20Sopenharmony_ci	/* Check for spurious interrupt */
10348c2ecf20Sopenharmony_ci	if (!(intr & NICVF_INTR_MBOX_MASK))
10358c2ecf20Sopenharmony_ci		return IRQ_HANDLED;
10368c2ecf20Sopenharmony_ci
10378c2ecf20Sopenharmony_ci	nicvf_handle_mbx_intr(nic);
10388c2ecf20Sopenharmony_ci
10398c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
10408c2ecf20Sopenharmony_ci}
10418c2ecf20Sopenharmony_ci
10428c2ecf20Sopenharmony_cistatic irqreturn_t nicvf_intr_handler(int irq, void *cq_irq)
10438c2ecf20Sopenharmony_ci{
10448c2ecf20Sopenharmony_ci	struct nicvf_cq_poll *cq_poll = (struct nicvf_cq_poll *)cq_irq;
10458c2ecf20Sopenharmony_ci	struct nicvf *nic = cq_poll->nicvf;
10468c2ecf20Sopenharmony_ci	int qidx = cq_poll->cq_idx;
10478c2ecf20Sopenharmony_ci
10488c2ecf20Sopenharmony_ci	nicvf_dump_intr_status(nic);
10498c2ecf20Sopenharmony_ci
10508c2ecf20Sopenharmony_ci	/* Disable interrupts */
10518c2ecf20Sopenharmony_ci	nicvf_disable_intr(nic, NICVF_INTR_CQ, qidx);
10528c2ecf20Sopenharmony_ci
10538c2ecf20Sopenharmony_ci	/* Schedule NAPI */
10548c2ecf20Sopenharmony_ci	napi_schedule_irqoff(&cq_poll->napi);
10558c2ecf20Sopenharmony_ci
10568c2ecf20Sopenharmony_ci	/* Clear interrupt */
10578c2ecf20Sopenharmony_ci	nicvf_clear_intr(nic, NICVF_INTR_CQ, qidx);
10588c2ecf20Sopenharmony_ci
10598c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
10608c2ecf20Sopenharmony_ci}
10618c2ecf20Sopenharmony_ci
10628c2ecf20Sopenharmony_cistatic irqreturn_t nicvf_rbdr_intr_handler(int irq, void *nicvf_irq)
10638c2ecf20Sopenharmony_ci{
10648c2ecf20Sopenharmony_ci	struct nicvf *nic = (struct nicvf *)nicvf_irq;
10658c2ecf20Sopenharmony_ci	u8 qidx;
10668c2ecf20Sopenharmony_ci
10678c2ecf20Sopenharmony_ci
10688c2ecf20Sopenharmony_ci	nicvf_dump_intr_status(nic);
10698c2ecf20Sopenharmony_ci
10708c2ecf20Sopenharmony_ci	/* Disable RBDR interrupt and schedule softirq */
10718c2ecf20Sopenharmony_ci	for (qidx = 0; qidx < nic->qs->rbdr_cnt; qidx++) {
10728c2ecf20Sopenharmony_ci		if (!nicvf_is_intr_enabled(nic, NICVF_INTR_RBDR, qidx))
10738c2ecf20Sopenharmony_ci			continue;
10748c2ecf20Sopenharmony_ci		nicvf_disable_intr(nic, NICVF_INTR_RBDR, qidx);
10758c2ecf20Sopenharmony_ci		tasklet_hi_schedule(&nic->rbdr_task);
10768c2ecf20Sopenharmony_ci		/* Clear interrupt */
10778c2ecf20Sopenharmony_ci		nicvf_clear_intr(nic, NICVF_INTR_RBDR, qidx);
10788c2ecf20Sopenharmony_ci	}
10798c2ecf20Sopenharmony_ci
10808c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
10818c2ecf20Sopenharmony_ci}
10828c2ecf20Sopenharmony_ci
10838c2ecf20Sopenharmony_cistatic irqreturn_t nicvf_qs_err_intr_handler(int irq, void *nicvf_irq)
10848c2ecf20Sopenharmony_ci{
10858c2ecf20Sopenharmony_ci	struct nicvf *nic = (struct nicvf *)nicvf_irq;
10868c2ecf20Sopenharmony_ci
10878c2ecf20Sopenharmony_ci	nicvf_dump_intr_status(nic);
10888c2ecf20Sopenharmony_ci
10898c2ecf20Sopenharmony_ci	/* Disable Qset err interrupt and schedule softirq */
10908c2ecf20Sopenharmony_ci	nicvf_disable_intr(nic, NICVF_INTR_QS_ERR, 0);
10918c2ecf20Sopenharmony_ci	tasklet_hi_schedule(&nic->qs_err_task);
10928c2ecf20Sopenharmony_ci	nicvf_clear_intr(nic, NICVF_INTR_QS_ERR, 0);
10938c2ecf20Sopenharmony_ci
10948c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
10958c2ecf20Sopenharmony_ci}
10968c2ecf20Sopenharmony_ci
10978c2ecf20Sopenharmony_cistatic void nicvf_set_irq_affinity(struct nicvf *nic)
10988c2ecf20Sopenharmony_ci{
10998c2ecf20Sopenharmony_ci	int vec, cpu;
11008c2ecf20Sopenharmony_ci
11018c2ecf20Sopenharmony_ci	for (vec = 0; vec < nic->num_vec; vec++) {
11028c2ecf20Sopenharmony_ci		if (!nic->irq_allocated[vec])
11038c2ecf20Sopenharmony_ci			continue;
11048c2ecf20Sopenharmony_ci
11058c2ecf20Sopenharmony_ci		if (!zalloc_cpumask_var(&nic->affinity_mask[vec], GFP_KERNEL))
11068c2ecf20Sopenharmony_ci			return;
11078c2ecf20Sopenharmony_ci		 /* CQ interrupts */
11088c2ecf20Sopenharmony_ci		if (vec < NICVF_INTR_ID_SQ)
11098c2ecf20Sopenharmony_ci			/* Leave CPU0 for RBDR and other interrupts */
11108c2ecf20Sopenharmony_ci			cpu = nicvf_netdev_qidx(nic, vec) + 1;
11118c2ecf20Sopenharmony_ci		else
11128c2ecf20Sopenharmony_ci			cpu = 0;
11138c2ecf20Sopenharmony_ci
11148c2ecf20Sopenharmony_ci		cpumask_set_cpu(cpumask_local_spread(cpu, nic->node),
11158c2ecf20Sopenharmony_ci				nic->affinity_mask[vec]);
11168c2ecf20Sopenharmony_ci		irq_set_affinity_hint(pci_irq_vector(nic->pdev, vec),
11178c2ecf20Sopenharmony_ci				      nic->affinity_mask[vec]);
11188c2ecf20Sopenharmony_ci	}
11198c2ecf20Sopenharmony_ci}
11208c2ecf20Sopenharmony_ci
11218c2ecf20Sopenharmony_cistatic int nicvf_register_interrupts(struct nicvf *nic)
11228c2ecf20Sopenharmony_ci{
11238c2ecf20Sopenharmony_ci	int irq, ret = 0;
11248c2ecf20Sopenharmony_ci
11258c2ecf20Sopenharmony_ci	for_each_cq_irq(irq)
11268c2ecf20Sopenharmony_ci		sprintf(nic->irq_name[irq], "%s-rxtx-%d",
11278c2ecf20Sopenharmony_ci			nic->pnicvf->netdev->name,
11288c2ecf20Sopenharmony_ci			nicvf_netdev_qidx(nic, irq));
11298c2ecf20Sopenharmony_ci
11308c2ecf20Sopenharmony_ci	for_each_sq_irq(irq)
11318c2ecf20Sopenharmony_ci		sprintf(nic->irq_name[irq], "%s-sq-%d",
11328c2ecf20Sopenharmony_ci			nic->pnicvf->netdev->name,
11338c2ecf20Sopenharmony_ci			nicvf_netdev_qidx(nic, irq - NICVF_INTR_ID_SQ));
11348c2ecf20Sopenharmony_ci
11358c2ecf20Sopenharmony_ci	for_each_rbdr_irq(irq)
11368c2ecf20Sopenharmony_ci		sprintf(nic->irq_name[irq], "%s-rbdr-%d",
11378c2ecf20Sopenharmony_ci			nic->pnicvf->netdev->name,
11388c2ecf20Sopenharmony_ci			nic->sqs_mode ? (nic->sqs_id + 1) : 0);
11398c2ecf20Sopenharmony_ci
11408c2ecf20Sopenharmony_ci	/* Register CQ interrupts */
11418c2ecf20Sopenharmony_ci	for (irq = 0; irq < nic->qs->cq_cnt; irq++) {
11428c2ecf20Sopenharmony_ci		ret = request_irq(pci_irq_vector(nic->pdev, irq),
11438c2ecf20Sopenharmony_ci				  nicvf_intr_handler,
11448c2ecf20Sopenharmony_ci				  0, nic->irq_name[irq], nic->napi[irq]);
11458c2ecf20Sopenharmony_ci		if (ret)
11468c2ecf20Sopenharmony_ci			goto err;
11478c2ecf20Sopenharmony_ci		nic->irq_allocated[irq] = true;
11488c2ecf20Sopenharmony_ci	}
11498c2ecf20Sopenharmony_ci
11508c2ecf20Sopenharmony_ci	/* Register RBDR interrupt */
11518c2ecf20Sopenharmony_ci	for (irq = NICVF_INTR_ID_RBDR;
11528c2ecf20Sopenharmony_ci	     irq < (NICVF_INTR_ID_RBDR + nic->qs->rbdr_cnt); irq++) {
11538c2ecf20Sopenharmony_ci		ret = request_irq(pci_irq_vector(nic->pdev, irq),
11548c2ecf20Sopenharmony_ci				  nicvf_rbdr_intr_handler,
11558c2ecf20Sopenharmony_ci				  0, nic->irq_name[irq], nic);
11568c2ecf20Sopenharmony_ci		if (ret)
11578c2ecf20Sopenharmony_ci			goto err;
11588c2ecf20Sopenharmony_ci		nic->irq_allocated[irq] = true;
11598c2ecf20Sopenharmony_ci	}
11608c2ecf20Sopenharmony_ci
11618c2ecf20Sopenharmony_ci	/* Register QS error interrupt */
11628c2ecf20Sopenharmony_ci	sprintf(nic->irq_name[NICVF_INTR_ID_QS_ERR], "%s-qset-err-%d",
11638c2ecf20Sopenharmony_ci		nic->pnicvf->netdev->name,
11648c2ecf20Sopenharmony_ci		nic->sqs_mode ? (nic->sqs_id + 1) : 0);
11658c2ecf20Sopenharmony_ci	irq = NICVF_INTR_ID_QS_ERR;
11668c2ecf20Sopenharmony_ci	ret = request_irq(pci_irq_vector(nic->pdev, irq),
11678c2ecf20Sopenharmony_ci			  nicvf_qs_err_intr_handler,
11688c2ecf20Sopenharmony_ci			  0, nic->irq_name[irq], nic);
11698c2ecf20Sopenharmony_ci	if (ret)
11708c2ecf20Sopenharmony_ci		goto err;
11718c2ecf20Sopenharmony_ci
11728c2ecf20Sopenharmony_ci	nic->irq_allocated[irq] = true;
11738c2ecf20Sopenharmony_ci
11748c2ecf20Sopenharmony_ci	/* Set IRQ affinities */
11758c2ecf20Sopenharmony_ci	nicvf_set_irq_affinity(nic);
11768c2ecf20Sopenharmony_ci
11778c2ecf20Sopenharmony_cierr:
11788c2ecf20Sopenharmony_ci	if (ret)
11798c2ecf20Sopenharmony_ci		netdev_err(nic->netdev, "request_irq failed, vector %d\n", irq);
11808c2ecf20Sopenharmony_ci
11818c2ecf20Sopenharmony_ci	return ret;
11828c2ecf20Sopenharmony_ci}
11838c2ecf20Sopenharmony_ci
11848c2ecf20Sopenharmony_cistatic void nicvf_unregister_interrupts(struct nicvf *nic)
11858c2ecf20Sopenharmony_ci{
11868c2ecf20Sopenharmony_ci	struct pci_dev *pdev = nic->pdev;
11878c2ecf20Sopenharmony_ci	int irq;
11888c2ecf20Sopenharmony_ci
11898c2ecf20Sopenharmony_ci	/* Free registered interrupts */
11908c2ecf20Sopenharmony_ci	for (irq = 0; irq < nic->num_vec; irq++) {
11918c2ecf20Sopenharmony_ci		if (!nic->irq_allocated[irq])
11928c2ecf20Sopenharmony_ci			continue;
11938c2ecf20Sopenharmony_ci
11948c2ecf20Sopenharmony_ci		irq_set_affinity_hint(pci_irq_vector(pdev, irq), NULL);
11958c2ecf20Sopenharmony_ci		free_cpumask_var(nic->affinity_mask[irq]);
11968c2ecf20Sopenharmony_ci
11978c2ecf20Sopenharmony_ci		if (irq < NICVF_INTR_ID_SQ)
11988c2ecf20Sopenharmony_ci			free_irq(pci_irq_vector(pdev, irq), nic->napi[irq]);
11998c2ecf20Sopenharmony_ci		else
12008c2ecf20Sopenharmony_ci			free_irq(pci_irq_vector(pdev, irq), nic);
12018c2ecf20Sopenharmony_ci
12028c2ecf20Sopenharmony_ci		nic->irq_allocated[irq] = false;
12038c2ecf20Sopenharmony_ci	}
12048c2ecf20Sopenharmony_ci
12058c2ecf20Sopenharmony_ci	/* Disable MSI-X */
12068c2ecf20Sopenharmony_ci	pci_free_irq_vectors(pdev);
12078c2ecf20Sopenharmony_ci	nic->num_vec = 0;
12088c2ecf20Sopenharmony_ci}
12098c2ecf20Sopenharmony_ci
12108c2ecf20Sopenharmony_ci/* Initialize MSIX vectors and register MISC interrupt.
12118c2ecf20Sopenharmony_ci * Send READY message to PF to check if its alive
12128c2ecf20Sopenharmony_ci */
12138c2ecf20Sopenharmony_cistatic int nicvf_register_misc_interrupt(struct nicvf *nic)
12148c2ecf20Sopenharmony_ci{
12158c2ecf20Sopenharmony_ci	int ret = 0;
12168c2ecf20Sopenharmony_ci	int irq = NICVF_INTR_ID_MISC;
12178c2ecf20Sopenharmony_ci
12188c2ecf20Sopenharmony_ci	/* Return if mailbox interrupt is already registered */
12198c2ecf20Sopenharmony_ci	if (nic->pdev->msix_enabled)
12208c2ecf20Sopenharmony_ci		return 0;
12218c2ecf20Sopenharmony_ci
12228c2ecf20Sopenharmony_ci	/* Enable MSI-X */
12238c2ecf20Sopenharmony_ci	nic->num_vec = pci_msix_vec_count(nic->pdev);
12248c2ecf20Sopenharmony_ci	ret = pci_alloc_irq_vectors(nic->pdev, nic->num_vec, nic->num_vec,
12258c2ecf20Sopenharmony_ci				    PCI_IRQ_MSIX);
12268c2ecf20Sopenharmony_ci	if (ret < 0) {
12278c2ecf20Sopenharmony_ci		netdev_err(nic->netdev,
12288c2ecf20Sopenharmony_ci			   "Req for #%d msix vectors failed\n", nic->num_vec);
12298c2ecf20Sopenharmony_ci		return ret;
12308c2ecf20Sopenharmony_ci	}
12318c2ecf20Sopenharmony_ci
12328c2ecf20Sopenharmony_ci	sprintf(nic->irq_name[irq], "%s Mbox", "NICVF");
12338c2ecf20Sopenharmony_ci	/* Register Misc interrupt */
12348c2ecf20Sopenharmony_ci	ret = request_irq(pci_irq_vector(nic->pdev, irq),
12358c2ecf20Sopenharmony_ci			  nicvf_misc_intr_handler, 0, nic->irq_name[irq], nic);
12368c2ecf20Sopenharmony_ci
12378c2ecf20Sopenharmony_ci	if (ret)
12388c2ecf20Sopenharmony_ci		return ret;
12398c2ecf20Sopenharmony_ci	nic->irq_allocated[irq] = true;
12408c2ecf20Sopenharmony_ci
12418c2ecf20Sopenharmony_ci	/* Enable mailbox interrupt */
12428c2ecf20Sopenharmony_ci	nicvf_enable_intr(nic, NICVF_INTR_MBOX, 0);
12438c2ecf20Sopenharmony_ci
12448c2ecf20Sopenharmony_ci	/* Check if VF is able to communicate with PF */
12458c2ecf20Sopenharmony_ci	if (!nicvf_check_pf_ready(nic)) {
12468c2ecf20Sopenharmony_ci		nicvf_disable_intr(nic, NICVF_INTR_MBOX, 0);
12478c2ecf20Sopenharmony_ci		nicvf_unregister_interrupts(nic);
12488c2ecf20Sopenharmony_ci		return -EIO;
12498c2ecf20Sopenharmony_ci	}
12508c2ecf20Sopenharmony_ci
12518c2ecf20Sopenharmony_ci	return 0;
12528c2ecf20Sopenharmony_ci}
12538c2ecf20Sopenharmony_ci
12548c2ecf20Sopenharmony_cistatic netdev_tx_t nicvf_xmit(struct sk_buff *skb, struct net_device *netdev)
12558c2ecf20Sopenharmony_ci{
12568c2ecf20Sopenharmony_ci	struct nicvf *nic = netdev_priv(netdev);
12578c2ecf20Sopenharmony_ci	int qid = skb_get_queue_mapping(skb);
12588c2ecf20Sopenharmony_ci	struct netdev_queue *txq = netdev_get_tx_queue(netdev, qid);
12598c2ecf20Sopenharmony_ci	struct nicvf *snic;
12608c2ecf20Sopenharmony_ci	struct snd_queue *sq;
12618c2ecf20Sopenharmony_ci	int tmp;
12628c2ecf20Sopenharmony_ci
12638c2ecf20Sopenharmony_ci	/* Check for minimum packet length */
12648c2ecf20Sopenharmony_ci	if (skb->len <= ETH_HLEN) {
12658c2ecf20Sopenharmony_ci		dev_kfree_skb(skb);
12668c2ecf20Sopenharmony_ci		return NETDEV_TX_OK;
12678c2ecf20Sopenharmony_ci	}
12688c2ecf20Sopenharmony_ci
12698c2ecf20Sopenharmony_ci	/* In XDP case, initial HW tx queues are used for XDP,
12708c2ecf20Sopenharmony_ci	 * but stack's queue mapping starts at '0', so skip the
12718c2ecf20Sopenharmony_ci	 * Tx queues attached to Rx queues for XDP.
12728c2ecf20Sopenharmony_ci	 */
12738c2ecf20Sopenharmony_ci	if (nic->xdp_prog)
12748c2ecf20Sopenharmony_ci		qid += nic->xdp_tx_queues;
12758c2ecf20Sopenharmony_ci
12768c2ecf20Sopenharmony_ci	snic = nic;
12778c2ecf20Sopenharmony_ci	/* Get secondary Qset's SQ structure */
12788c2ecf20Sopenharmony_ci	if (qid >= MAX_SND_QUEUES_PER_QS) {
12798c2ecf20Sopenharmony_ci		tmp = qid / MAX_SND_QUEUES_PER_QS;
12808c2ecf20Sopenharmony_ci		snic = (struct nicvf *)nic->snicvf[tmp - 1];
12818c2ecf20Sopenharmony_ci		if (!snic) {
12828c2ecf20Sopenharmony_ci			netdev_warn(nic->netdev,
12838c2ecf20Sopenharmony_ci				    "Secondary Qset#%d's ptr not initialized\n",
12848c2ecf20Sopenharmony_ci				    tmp - 1);
12858c2ecf20Sopenharmony_ci			dev_kfree_skb(skb);
12868c2ecf20Sopenharmony_ci			return NETDEV_TX_OK;
12878c2ecf20Sopenharmony_ci		}
12888c2ecf20Sopenharmony_ci		qid = qid % MAX_SND_QUEUES_PER_QS;
12898c2ecf20Sopenharmony_ci	}
12908c2ecf20Sopenharmony_ci
12918c2ecf20Sopenharmony_ci	sq = &snic->qs->sq[qid];
12928c2ecf20Sopenharmony_ci	if (!netif_tx_queue_stopped(txq) &&
12938c2ecf20Sopenharmony_ci	    !nicvf_sq_append_skb(snic, sq, skb, qid)) {
12948c2ecf20Sopenharmony_ci		netif_tx_stop_queue(txq);
12958c2ecf20Sopenharmony_ci
12968c2ecf20Sopenharmony_ci		/* Barrier, so that stop_queue visible to other cpus */
12978c2ecf20Sopenharmony_ci		smp_mb();
12988c2ecf20Sopenharmony_ci
12998c2ecf20Sopenharmony_ci		/* Check again, incase another cpu freed descriptors */
13008c2ecf20Sopenharmony_ci		if (atomic_read(&sq->free_cnt) > MIN_SQ_DESC_PER_PKT_XMIT) {
13018c2ecf20Sopenharmony_ci			netif_tx_wake_queue(txq);
13028c2ecf20Sopenharmony_ci		} else {
13038c2ecf20Sopenharmony_ci			this_cpu_inc(nic->drv_stats->txq_stop);
13048c2ecf20Sopenharmony_ci			netif_warn(nic, tx_err, netdev,
13058c2ecf20Sopenharmony_ci				   "Transmit ring full, stopping SQ%d\n", qid);
13068c2ecf20Sopenharmony_ci		}
13078c2ecf20Sopenharmony_ci		return NETDEV_TX_BUSY;
13088c2ecf20Sopenharmony_ci	}
13098c2ecf20Sopenharmony_ci
13108c2ecf20Sopenharmony_ci	return NETDEV_TX_OK;
13118c2ecf20Sopenharmony_ci}
13128c2ecf20Sopenharmony_ci
13138c2ecf20Sopenharmony_cistatic inline void nicvf_free_cq_poll(struct nicvf *nic)
13148c2ecf20Sopenharmony_ci{
13158c2ecf20Sopenharmony_ci	struct nicvf_cq_poll *cq_poll;
13168c2ecf20Sopenharmony_ci	int qidx;
13178c2ecf20Sopenharmony_ci
13188c2ecf20Sopenharmony_ci	for (qidx = 0; qidx < nic->qs->cq_cnt; qidx++) {
13198c2ecf20Sopenharmony_ci		cq_poll = nic->napi[qidx];
13208c2ecf20Sopenharmony_ci		if (!cq_poll)
13218c2ecf20Sopenharmony_ci			continue;
13228c2ecf20Sopenharmony_ci		nic->napi[qidx] = NULL;
13238c2ecf20Sopenharmony_ci		kfree(cq_poll);
13248c2ecf20Sopenharmony_ci	}
13258c2ecf20Sopenharmony_ci}
13268c2ecf20Sopenharmony_ci
13278c2ecf20Sopenharmony_ciint nicvf_stop(struct net_device *netdev)
13288c2ecf20Sopenharmony_ci{
13298c2ecf20Sopenharmony_ci	int irq, qidx;
13308c2ecf20Sopenharmony_ci	struct nicvf *nic = netdev_priv(netdev);
13318c2ecf20Sopenharmony_ci	struct queue_set *qs = nic->qs;
13328c2ecf20Sopenharmony_ci	struct nicvf_cq_poll *cq_poll = NULL;
13338c2ecf20Sopenharmony_ci	union nic_mbx mbx = {};
13348c2ecf20Sopenharmony_ci
13358c2ecf20Sopenharmony_ci	/* wait till all queued set_rx_mode tasks completes */
13368c2ecf20Sopenharmony_ci	if (nic->nicvf_rx_mode_wq) {
13378c2ecf20Sopenharmony_ci		cancel_delayed_work_sync(&nic->link_change_work);
13388c2ecf20Sopenharmony_ci		drain_workqueue(nic->nicvf_rx_mode_wq);
13398c2ecf20Sopenharmony_ci	}
13408c2ecf20Sopenharmony_ci
13418c2ecf20Sopenharmony_ci	mbx.msg.msg = NIC_MBOX_MSG_SHUTDOWN;
13428c2ecf20Sopenharmony_ci	nicvf_send_msg_to_pf(nic, &mbx);
13438c2ecf20Sopenharmony_ci
13448c2ecf20Sopenharmony_ci	netif_carrier_off(netdev);
13458c2ecf20Sopenharmony_ci	netif_tx_stop_all_queues(nic->netdev);
13468c2ecf20Sopenharmony_ci	nic->link_up = false;
13478c2ecf20Sopenharmony_ci
13488c2ecf20Sopenharmony_ci	/* Teardown secondary qsets first */
13498c2ecf20Sopenharmony_ci	if (!nic->sqs_mode) {
13508c2ecf20Sopenharmony_ci		for (qidx = 0; qidx < nic->sqs_count; qidx++) {
13518c2ecf20Sopenharmony_ci			if (!nic->snicvf[qidx])
13528c2ecf20Sopenharmony_ci				continue;
13538c2ecf20Sopenharmony_ci			nicvf_stop(nic->snicvf[qidx]->netdev);
13548c2ecf20Sopenharmony_ci			nic->snicvf[qidx] = NULL;
13558c2ecf20Sopenharmony_ci		}
13568c2ecf20Sopenharmony_ci	}
13578c2ecf20Sopenharmony_ci
13588c2ecf20Sopenharmony_ci	/* Disable RBDR & QS error interrupts */
13598c2ecf20Sopenharmony_ci	for (qidx = 0; qidx < qs->rbdr_cnt; qidx++) {
13608c2ecf20Sopenharmony_ci		nicvf_disable_intr(nic, NICVF_INTR_RBDR, qidx);
13618c2ecf20Sopenharmony_ci		nicvf_clear_intr(nic, NICVF_INTR_RBDR, qidx);
13628c2ecf20Sopenharmony_ci	}
13638c2ecf20Sopenharmony_ci	nicvf_disable_intr(nic, NICVF_INTR_QS_ERR, 0);
13648c2ecf20Sopenharmony_ci	nicvf_clear_intr(nic, NICVF_INTR_QS_ERR, 0);
13658c2ecf20Sopenharmony_ci
13668c2ecf20Sopenharmony_ci	/* Wait for pending IRQ handlers to finish */
13678c2ecf20Sopenharmony_ci	for (irq = 0; irq < nic->num_vec; irq++)
13688c2ecf20Sopenharmony_ci		synchronize_irq(pci_irq_vector(nic->pdev, irq));
13698c2ecf20Sopenharmony_ci
13708c2ecf20Sopenharmony_ci	tasklet_kill(&nic->rbdr_task);
13718c2ecf20Sopenharmony_ci	tasklet_kill(&nic->qs_err_task);
13728c2ecf20Sopenharmony_ci	if (nic->rb_work_scheduled)
13738c2ecf20Sopenharmony_ci		cancel_delayed_work_sync(&nic->rbdr_work);
13748c2ecf20Sopenharmony_ci
13758c2ecf20Sopenharmony_ci	for (qidx = 0; qidx < nic->qs->cq_cnt; qidx++) {
13768c2ecf20Sopenharmony_ci		cq_poll = nic->napi[qidx];
13778c2ecf20Sopenharmony_ci		if (!cq_poll)
13788c2ecf20Sopenharmony_ci			continue;
13798c2ecf20Sopenharmony_ci		napi_synchronize(&cq_poll->napi);
13808c2ecf20Sopenharmony_ci		/* CQ intr is enabled while napi_complete,
13818c2ecf20Sopenharmony_ci		 * so disable it now
13828c2ecf20Sopenharmony_ci		 */
13838c2ecf20Sopenharmony_ci		nicvf_disable_intr(nic, NICVF_INTR_CQ, qidx);
13848c2ecf20Sopenharmony_ci		nicvf_clear_intr(nic, NICVF_INTR_CQ, qidx);
13858c2ecf20Sopenharmony_ci		napi_disable(&cq_poll->napi);
13868c2ecf20Sopenharmony_ci		netif_napi_del(&cq_poll->napi);
13878c2ecf20Sopenharmony_ci	}
13888c2ecf20Sopenharmony_ci
13898c2ecf20Sopenharmony_ci	netif_tx_disable(netdev);
13908c2ecf20Sopenharmony_ci
13918c2ecf20Sopenharmony_ci	for (qidx = 0; qidx < netdev->num_tx_queues; qidx++)
13928c2ecf20Sopenharmony_ci		netdev_tx_reset_queue(netdev_get_tx_queue(netdev, qidx));
13938c2ecf20Sopenharmony_ci
13948c2ecf20Sopenharmony_ci	/* Free resources */
13958c2ecf20Sopenharmony_ci	nicvf_config_data_transfer(nic, false);
13968c2ecf20Sopenharmony_ci
13978c2ecf20Sopenharmony_ci	/* Disable HW Qset */
13988c2ecf20Sopenharmony_ci	nicvf_qset_config(nic, false);
13998c2ecf20Sopenharmony_ci
14008c2ecf20Sopenharmony_ci	/* disable mailbox interrupt */
14018c2ecf20Sopenharmony_ci	nicvf_disable_intr(nic, NICVF_INTR_MBOX, 0);
14028c2ecf20Sopenharmony_ci
14038c2ecf20Sopenharmony_ci	nicvf_unregister_interrupts(nic);
14048c2ecf20Sopenharmony_ci
14058c2ecf20Sopenharmony_ci	nicvf_free_cq_poll(nic);
14068c2ecf20Sopenharmony_ci
14078c2ecf20Sopenharmony_ci	/* Free any pending SKB saved to receive timestamp */
14088c2ecf20Sopenharmony_ci	if (nic->ptp_skb) {
14098c2ecf20Sopenharmony_ci		dev_kfree_skb_any(nic->ptp_skb);
14108c2ecf20Sopenharmony_ci		nic->ptp_skb = NULL;
14118c2ecf20Sopenharmony_ci	}
14128c2ecf20Sopenharmony_ci
14138c2ecf20Sopenharmony_ci	/* Clear multiqset info */
14148c2ecf20Sopenharmony_ci	nic->pnicvf = nic;
14158c2ecf20Sopenharmony_ci
14168c2ecf20Sopenharmony_ci	return 0;
14178c2ecf20Sopenharmony_ci}
14188c2ecf20Sopenharmony_ci
14198c2ecf20Sopenharmony_cistatic int nicvf_config_hw_rx_tstamp(struct nicvf *nic, bool enable)
14208c2ecf20Sopenharmony_ci{
14218c2ecf20Sopenharmony_ci	union nic_mbx mbx = {};
14228c2ecf20Sopenharmony_ci
14238c2ecf20Sopenharmony_ci	mbx.ptp.msg = NIC_MBOX_MSG_PTP_CFG;
14248c2ecf20Sopenharmony_ci	mbx.ptp.enable = enable;
14258c2ecf20Sopenharmony_ci
14268c2ecf20Sopenharmony_ci	return nicvf_send_msg_to_pf(nic, &mbx);
14278c2ecf20Sopenharmony_ci}
14288c2ecf20Sopenharmony_ci
14298c2ecf20Sopenharmony_cistatic int nicvf_update_hw_max_frs(struct nicvf *nic, int mtu)
14308c2ecf20Sopenharmony_ci{
14318c2ecf20Sopenharmony_ci	union nic_mbx mbx = {};
14328c2ecf20Sopenharmony_ci
14338c2ecf20Sopenharmony_ci	mbx.frs.msg = NIC_MBOX_MSG_SET_MAX_FRS;
14348c2ecf20Sopenharmony_ci	mbx.frs.max_frs = mtu;
14358c2ecf20Sopenharmony_ci	mbx.frs.vf_id = nic->vf_id;
14368c2ecf20Sopenharmony_ci
14378c2ecf20Sopenharmony_ci	return nicvf_send_msg_to_pf(nic, &mbx);
14388c2ecf20Sopenharmony_ci}
14398c2ecf20Sopenharmony_ci
14408c2ecf20Sopenharmony_cistatic void nicvf_link_status_check_task(struct work_struct *work_arg)
14418c2ecf20Sopenharmony_ci{
14428c2ecf20Sopenharmony_ci	struct nicvf *nic = container_of(work_arg,
14438c2ecf20Sopenharmony_ci					 struct nicvf,
14448c2ecf20Sopenharmony_ci					 link_change_work.work);
14458c2ecf20Sopenharmony_ci	union nic_mbx mbx = {};
14468c2ecf20Sopenharmony_ci	mbx.msg.msg = NIC_MBOX_MSG_BGX_LINK_CHANGE;
14478c2ecf20Sopenharmony_ci	nicvf_send_msg_to_pf(nic, &mbx);
14488c2ecf20Sopenharmony_ci	queue_delayed_work(nic->nicvf_rx_mode_wq,
14498c2ecf20Sopenharmony_ci			   &nic->link_change_work, 2 * HZ);
14508c2ecf20Sopenharmony_ci}
14518c2ecf20Sopenharmony_ci
14528c2ecf20Sopenharmony_ciint nicvf_open(struct net_device *netdev)
14538c2ecf20Sopenharmony_ci{
14548c2ecf20Sopenharmony_ci	int cpu, err, qidx;
14558c2ecf20Sopenharmony_ci	struct nicvf *nic = netdev_priv(netdev);
14568c2ecf20Sopenharmony_ci	struct queue_set *qs = nic->qs;
14578c2ecf20Sopenharmony_ci	struct nicvf_cq_poll *cq_poll = NULL;
14588c2ecf20Sopenharmony_ci
14598c2ecf20Sopenharmony_ci	/* wait till all queued set_rx_mode tasks completes if any */
14608c2ecf20Sopenharmony_ci	if (nic->nicvf_rx_mode_wq)
14618c2ecf20Sopenharmony_ci		drain_workqueue(nic->nicvf_rx_mode_wq);
14628c2ecf20Sopenharmony_ci
14638c2ecf20Sopenharmony_ci	netif_carrier_off(netdev);
14648c2ecf20Sopenharmony_ci
14658c2ecf20Sopenharmony_ci	err = nicvf_register_misc_interrupt(nic);
14668c2ecf20Sopenharmony_ci	if (err)
14678c2ecf20Sopenharmony_ci		return err;
14688c2ecf20Sopenharmony_ci
14698c2ecf20Sopenharmony_ci	/* Register NAPI handler for processing CQEs */
14708c2ecf20Sopenharmony_ci	for (qidx = 0; qidx < qs->cq_cnt; qidx++) {
14718c2ecf20Sopenharmony_ci		cq_poll = kzalloc(sizeof(*cq_poll), GFP_KERNEL);
14728c2ecf20Sopenharmony_ci		if (!cq_poll) {
14738c2ecf20Sopenharmony_ci			err = -ENOMEM;
14748c2ecf20Sopenharmony_ci			goto napi_del;
14758c2ecf20Sopenharmony_ci		}
14768c2ecf20Sopenharmony_ci		cq_poll->cq_idx = qidx;
14778c2ecf20Sopenharmony_ci		cq_poll->nicvf = nic;
14788c2ecf20Sopenharmony_ci		netif_napi_add(netdev, &cq_poll->napi, nicvf_poll,
14798c2ecf20Sopenharmony_ci			       NAPI_POLL_WEIGHT);
14808c2ecf20Sopenharmony_ci		napi_enable(&cq_poll->napi);
14818c2ecf20Sopenharmony_ci		nic->napi[qidx] = cq_poll;
14828c2ecf20Sopenharmony_ci	}
14838c2ecf20Sopenharmony_ci
14848c2ecf20Sopenharmony_ci	/* Check if we got MAC address from PF or else generate a radom MAC */
14858c2ecf20Sopenharmony_ci	if (!nic->sqs_mode && is_zero_ether_addr(netdev->dev_addr)) {
14868c2ecf20Sopenharmony_ci		eth_hw_addr_random(netdev);
14878c2ecf20Sopenharmony_ci		nicvf_hw_set_mac_addr(nic, netdev);
14888c2ecf20Sopenharmony_ci	}
14898c2ecf20Sopenharmony_ci
14908c2ecf20Sopenharmony_ci	if (nic->set_mac_pending) {
14918c2ecf20Sopenharmony_ci		nic->set_mac_pending = false;
14928c2ecf20Sopenharmony_ci		nicvf_hw_set_mac_addr(nic, netdev);
14938c2ecf20Sopenharmony_ci	}
14948c2ecf20Sopenharmony_ci
14958c2ecf20Sopenharmony_ci	/* Init tasklet for handling Qset err interrupt */
14968c2ecf20Sopenharmony_ci	tasklet_setup(&nic->qs_err_task, nicvf_handle_qs_err);
14978c2ecf20Sopenharmony_ci
14988c2ecf20Sopenharmony_ci	/* Init RBDR tasklet which will refill RBDR */
14998c2ecf20Sopenharmony_ci	tasklet_setup(&nic->rbdr_task, nicvf_rbdr_task);
15008c2ecf20Sopenharmony_ci	INIT_DELAYED_WORK(&nic->rbdr_work, nicvf_rbdr_work);
15018c2ecf20Sopenharmony_ci
15028c2ecf20Sopenharmony_ci	/* Configure CPI alorithm */
15038c2ecf20Sopenharmony_ci	nic->cpi_alg = cpi_alg;
15048c2ecf20Sopenharmony_ci	if (!nic->sqs_mode)
15058c2ecf20Sopenharmony_ci		nicvf_config_cpi(nic);
15068c2ecf20Sopenharmony_ci
15078c2ecf20Sopenharmony_ci	nicvf_request_sqs(nic);
15088c2ecf20Sopenharmony_ci	if (nic->sqs_mode)
15098c2ecf20Sopenharmony_ci		nicvf_get_primary_vf_struct(nic);
15108c2ecf20Sopenharmony_ci
15118c2ecf20Sopenharmony_ci	/* Configure PTP timestamp */
15128c2ecf20Sopenharmony_ci	if (nic->ptp_clock)
15138c2ecf20Sopenharmony_ci		nicvf_config_hw_rx_tstamp(nic, nic->hw_rx_tstamp);
15148c2ecf20Sopenharmony_ci	atomic_set(&nic->tx_ptp_skbs, 0);
15158c2ecf20Sopenharmony_ci	nic->ptp_skb = NULL;
15168c2ecf20Sopenharmony_ci
15178c2ecf20Sopenharmony_ci	/* Configure receive side scaling and MTU */
15188c2ecf20Sopenharmony_ci	if (!nic->sqs_mode) {
15198c2ecf20Sopenharmony_ci		nicvf_rss_init(nic);
15208c2ecf20Sopenharmony_ci		err = nicvf_update_hw_max_frs(nic, netdev->mtu);
15218c2ecf20Sopenharmony_ci		if (err)
15228c2ecf20Sopenharmony_ci			goto cleanup;
15238c2ecf20Sopenharmony_ci
15248c2ecf20Sopenharmony_ci		/* Clear percpu stats */
15258c2ecf20Sopenharmony_ci		for_each_possible_cpu(cpu)
15268c2ecf20Sopenharmony_ci			memset(per_cpu_ptr(nic->drv_stats, cpu), 0,
15278c2ecf20Sopenharmony_ci			       sizeof(struct nicvf_drv_stats));
15288c2ecf20Sopenharmony_ci	}
15298c2ecf20Sopenharmony_ci
15308c2ecf20Sopenharmony_ci	err = nicvf_register_interrupts(nic);
15318c2ecf20Sopenharmony_ci	if (err)
15328c2ecf20Sopenharmony_ci		goto cleanup;
15338c2ecf20Sopenharmony_ci
15348c2ecf20Sopenharmony_ci	/* Initialize the queues */
15358c2ecf20Sopenharmony_ci	err = nicvf_init_resources(nic);
15368c2ecf20Sopenharmony_ci	if (err)
15378c2ecf20Sopenharmony_ci		goto cleanup;
15388c2ecf20Sopenharmony_ci
15398c2ecf20Sopenharmony_ci	/* Make sure queue initialization is written */
15408c2ecf20Sopenharmony_ci	wmb();
15418c2ecf20Sopenharmony_ci
15428c2ecf20Sopenharmony_ci	nicvf_reg_write(nic, NIC_VF_INT, -1);
15438c2ecf20Sopenharmony_ci	/* Enable Qset err interrupt */
15448c2ecf20Sopenharmony_ci	nicvf_enable_intr(nic, NICVF_INTR_QS_ERR, 0);
15458c2ecf20Sopenharmony_ci
15468c2ecf20Sopenharmony_ci	/* Enable completion queue interrupt */
15478c2ecf20Sopenharmony_ci	for (qidx = 0; qidx < qs->cq_cnt; qidx++)
15488c2ecf20Sopenharmony_ci		nicvf_enable_intr(nic, NICVF_INTR_CQ, qidx);
15498c2ecf20Sopenharmony_ci
15508c2ecf20Sopenharmony_ci	/* Enable RBDR threshold interrupt */
15518c2ecf20Sopenharmony_ci	for (qidx = 0; qidx < qs->rbdr_cnt; qidx++)
15528c2ecf20Sopenharmony_ci		nicvf_enable_intr(nic, NICVF_INTR_RBDR, qidx);
15538c2ecf20Sopenharmony_ci
15548c2ecf20Sopenharmony_ci	/* Send VF config done msg to PF */
15558c2ecf20Sopenharmony_ci	nicvf_send_cfg_done(nic);
15568c2ecf20Sopenharmony_ci
15578c2ecf20Sopenharmony_ci	if (nic->nicvf_rx_mode_wq) {
15588c2ecf20Sopenharmony_ci		INIT_DELAYED_WORK(&nic->link_change_work,
15598c2ecf20Sopenharmony_ci				  nicvf_link_status_check_task);
15608c2ecf20Sopenharmony_ci		queue_delayed_work(nic->nicvf_rx_mode_wq,
15618c2ecf20Sopenharmony_ci				   &nic->link_change_work, 0);
15628c2ecf20Sopenharmony_ci	}
15638c2ecf20Sopenharmony_ci
15648c2ecf20Sopenharmony_ci	return 0;
15658c2ecf20Sopenharmony_cicleanup:
15668c2ecf20Sopenharmony_ci	nicvf_disable_intr(nic, NICVF_INTR_MBOX, 0);
15678c2ecf20Sopenharmony_ci	nicvf_unregister_interrupts(nic);
15688c2ecf20Sopenharmony_ci	tasklet_kill(&nic->qs_err_task);
15698c2ecf20Sopenharmony_ci	tasklet_kill(&nic->rbdr_task);
15708c2ecf20Sopenharmony_cinapi_del:
15718c2ecf20Sopenharmony_ci	for (qidx = 0; qidx < qs->cq_cnt; qidx++) {
15728c2ecf20Sopenharmony_ci		cq_poll = nic->napi[qidx];
15738c2ecf20Sopenharmony_ci		if (!cq_poll)
15748c2ecf20Sopenharmony_ci			continue;
15758c2ecf20Sopenharmony_ci		napi_disable(&cq_poll->napi);
15768c2ecf20Sopenharmony_ci		netif_napi_del(&cq_poll->napi);
15778c2ecf20Sopenharmony_ci	}
15788c2ecf20Sopenharmony_ci	nicvf_free_cq_poll(nic);
15798c2ecf20Sopenharmony_ci	return err;
15808c2ecf20Sopenharmony_ci}
15818c2ecf20Sopenharmony_ci
15828c2ecf20Sopenharmony_cistatic int nicvf_change_mtu(struct net_device *netdev, int new_mtu)
15838c2ecf20Sopenharmony_ci{
15848c2ecf20Sopenharmony_ci	struct nicvf *nic = netdev_priv(netdev);
15858c2ecf20Sopenharmony_ci	int orig_mtu = netdev->mtu;
15868c2ecf20Sopenharmony_ci
15878c2ecf20Sopenharmony_ci	/* For now just support only the usual MTU sized frames,
15888c2ecf20Sopenharmony_ci	 * plus some headroom for VLAN, QinQ.
15898c2ecf20Sopenharmony_ci	 */
15908c2ecf20Sopenharmony_ci	if (nic->xdp_prog && new_mtu > MAX_XDP_MTU) {
15918c2ecf20Sopenharmony_ci		netdev_warn(netdev, "Jumbo frames not yet supported with XDP, current MTU %d.\n",
15928c2ecf20Sopenharmony_ci			    netdev->mtu);
15938c2ecf20Sopenharmony_ci		return -EINVAL;
15948c2ecf20Sopenharmony_ci	}
15958c2ecf20Sopenharmony_ci
15968c2ecf20Sopenharmony_ci	netdev->mtu = new_mtu;
15978c2ecf20Sopenharmony_ci
15988c2ecf20Sopenharmony_ci	if (!netif_running(netdev))
15998c2ecf20Sopenharmony_ci		return 0;
16008c2ecf20Sopenharmony_ci
16018c2ecf20Sopenharmony_ci	if (nicvf_update_hw_max_frs(nic, new_mtu)) {
16028c2ecf20Sopenharmony_ci		netdev->mtu = orig_mtu;
16038c2ecf20Sopenharmony_ci		return -EINVAL;
16048c2ecf20Sopenharmony_ci	}
16058c2ecf20Sopenharmony_ci
16068c2ecf20Sopenharmony_ci	return 0;
16078c2ecf20Sopenharmony_ci}
16088c2ecf20Sopenharmony_ci
16098c2ecf20Sopenharmony_cistatic int nicvf_set_mac_address(struct net_device *netdev, void *p)
16108c2ecf20Sopenharmony_ci{
16118c2ecf20Sopenharmony_ci	struct sockaddr *addr = p;
16128c2ecf20Sopenharmony_ci	struct nicvf *nic = netdev_priv(netdev);
16138c2ecf20Sopenharmony_ci
16148c2ecf20Sopenharmony_ci	if (!is_valid_ether_addr(addr->sa_data))
16158c2ecf20Sopenharmony_ci		return -EADDRNOTAVAIL;
16168c2ecf20Sopenharmony_ci
16178c2ecf20Sopenharmony_ci	memcpy(netdev->dev_addr, addr->sa_data, netdev->addr_len);
16188c2ecf20Sopenharmony_ci
16198c2ecf20Sopenharmony_ci	if (nic->pdev->msix_enabled) {
16208c2ecf20Sopenharmony_ci		if (nicvf_hw_set_mac_addr(nic, netdev))
16218c2ecf20Sopenharmony_ci			return -EBUSY;
16228c2ecf20Sopenharmony_ci	} else {
16238c2ecf20Sopenharmony_ci		nic->set_mac_pending = true;
16248c2ecf20Sopenharmony_ci	}
16258c2ecf20Sopenharmony_ci
16268c2ecf20Sopenharmony_ci	return 0;
16278c2ecf20Sopenharmony_ci}
16288c2ecf20Sopenharmony_ci
16298c2ecf20Sopenharmony_civoid nicvf_update_lmac_stats(struct nicvf *nic)
16308c2ecf20Sopenharmony_ci{
16318c2ecf20Sopenharmony_ci	int stat = 0;
16328c2ecf20Sopenharmony_ci	union nic_mbx mbx = {};
16338c2ecf20Sopenharmony_ci
16348c2ecf20Sopenharmony_ci	if (!netif_running(nic->netdev))
16358c2ecf20Sopenharmony_ci		return;
16368c2ecf20Sopenharmony_ci
16378c2ecf20Sopenharmony_ci	mbx.bgx_stats.msg = NIC_MBOX_MSG_BGX_STATS;
16388c2ecf20Sopenharmony_ci	mbx.bgx_stats.vf_id = nic->vf_id;
16398c2ecf20Sopenharmony_ci	/* Rx stats */
16408c2ecf20Sopenharmony_ci	mbx.bgx_stats.rx = 1;
16418c2ecf20Sopenharmony_ci	while (stat < BGX_RX_STATS_COUNT) {
16428c2ecf20Sopenharmony_ci		mbx.bgx_stats.idx = stat;
16438c2ecf20Sopenharmony_ci		if (nicvf_send_msg_to_pf(nic, &mbx))
16448c2ecf20Sopenharmony_ci			return;
16458c2ecf20Sopenharmony_ci		stat++;
16468c2ecf20Sopenharmony_ci	}
16478c2ecf20Sopenharmony_ci
16488c2ecf20Sopenharmony_ci	stat = 0;
16498c2ecf20Sopenharmony_ci
16508c2ecf20Sopenharmony_ci	/* Tx stats */
16518c2ecf20Sopenharmony_ci	mbx.bgx_stats.rx = 0;
16528c2ecf20Sopenharmony_ci	while (stat < BGX_TX_STATS_COUNT) {
16538c2ecf20Sopenharmony_ci		mbx.bgx_stats.idx = stat;
16548c2ecf20Sopenharmony_ci		if (nicvf_send_msg_to_pf(nic, &mbx))
16558c2ecf20Sopenharmony_ci			return;
16568c2ecf20Sopenharmony_ci		stat++;
16578c2ecf20Sopenharmony_ci	}
16588c2ecf20Sopenharmony_ci}
16598c2ecf20Sopenharmony_ci
16608c2ecf20Sopenharmony_civoid nicvf_update_stats(struct nicvf *nic)
16618c2ecf20Sopenharmony_ci{
16628c2ecf20Sopenharmony_ci	int qidx, cpu;
16638c2ecf20Sopenharmony_ci	u64 tmp_stats = 0;
16648c2ecf20Sopenharmony_ci	struct nicvf_hw_stats *stats = &nic->hw_stats;
16658c2ecf20Sopenharmony_ci	struct nicvf_drv_stats *drv_stats;
16668c2ecf20Sopenharmony_ci	struct queue_set *qs = nic->qs;
16678c2ecf20Sopenharmony_ci
16688c2ecf20Sopenharmony_ci#define GET_RX_STATS(reg) \
16698c2ecf20Sopenharmony_ci	nicvf_reg_read(nic, NIC_VNIC_RX_STAT_0_13 | (reg << 3))
16708c2ecf20Sopenharmony_ci#define GET_TX_STATS(reg) \
16718c2ecf20Sopenharmony_ci	nicvf_reg_read(nic, NIC_VNIC_TX_STAT_0_4 | (reg << 3))
16728c2ecf20Sopenharmony_ci
16738c2ecf20Sopenharmony_ci	stats->rx_bytes = GET_RX_STATS(RX_OCTS);
16748c2ecf20Sopenharmony_ci	stats->rx_ucast_frames = GET_RX_STATS(RX_UCAST);
16758c2ecf20Sopenharmony_ci	stats->rx_bcast_frames = GET_RX_STATS(RX_BCAST);
16768c2ecf20Sopenharmony_ci	stats->rx_mcast_frames = GET_RX_STATS(RX_MCAST);
16778c2ecf20Sopenharmony_ci	stats->rx_fcs_errors = GET_RX_STATS(RX_FCS);
16788c2ecf20Sopenharmony_ci	stats->rx_l2_errors = GET_RX_STATS(RX_L2ERR);
16798c2ecf20Sopenharmony_ci	stats->rx_drop_red = GET_RX_STATS(RX_RED);
16808c2ecf20Sopenharmony_ci	stats->rx_drop_red_bytes = GET_RX_STATS(RX_RED_OCTS);
16818c2ecf20Sopenharmony_ci	stats->rx_drop_overrun = GET_RX_STATS(RX_ORUN);
16828c2ecf20Sopenharmony_ci	stats->rx_drop_overrun_bytes = GET_RX_STATS(RX_ORUN_OCTS);
16838c2ecf20Sopenharmony_ci	stats->rx_drop_bcast = GET_RX_STATS(RX_DRP_BCAST);
16848c2ecf20Sopenharmony_ci	stats->rx_drop_mcast = GET_RX_STATS(RX_DRP_MCAST);
16858c2ecf20Sopenharmony_ci	stats->rx_drop_l3_bcast = GET_RX_STATS(RX_DRP_L3BCAST);
16868c2ecf20Sopenharmony_ci	stats->rx_drop_l3_mcast = GET_RX_STATS(RX_DRP_L3MCAST);
16878c2ecf20Sopenharmony_ci
16888c2ecf20Sopenharmony_ci	stats->tx_bytes = GET_TX_STATS(TX_OCTS);
16898c2ecf20Sopenharmony_ci	stats->tx_ucast_frames = GET_TX_STATS(TX_UCAST);
16908c2ecf20Sopenharmony_ci	stats->tx_bcast_frames = GET_TX_STATS(TX_BCAST);
16918c2ecf20Sopenharmony_ci	stats->tx_mcast_frames = GET_TX_STATS(TX_MCAST);
16928c2ecf20Sopenharmony_ci	stats->tx_drops = GET_TX_STATS(TX_DROP);
16938c2ecf20Sopenharmony_ci
16948c2ecf20Sopenharmony_ci	/* On T88 pass 2.0, the dummy SQE added for TSO notification
16958c2ecf20Sopenharmony_ci	 * via CQE has 'dont_send' set. Hence HW drops the pkt pointed
16968c2ecf20Sopenharmony_ci	 * pointed by dummy SQE and results in tx_drops counter being
16978c2ecf20Sopenharmony_ci	 * incremented. Subtracting it from tx_tso counter will give
16988c2ecf20Sopenharmony_ci	 * exact tx_drops counter.
16998c2ecf20Sopenharmony_ci	 */
17008c2ecf20Sopenharmony_ci	if (nic->t88 && nic->hw_tso) {
17018c2ecf20Sopenharmony_ci		for_each_possible_cpu(cpu) {
17028c2ecf20Sopenharmony_ci			drv_stats = per_cpu_ptr(nic->drv_stats, cpu);
17038c2ecf20Sopenharmony_ci			tmp_stats += drv_stats->tx_tso;
17048c2ecf20Sopenharmony_ci		}
17058c2ecf20Sopenharmony_ci		stats->tx_drops = tmp_stats - stats->tx_drops;
17068c2ecf20Sopenharmony_ci	}
17078c2ecf20Sopenharmony_ci	stats->tx_frames = stats->tx_ucast_frames +
17088c2ecf20Sopenharmony_ci			   stats->tx_bcast_frames +
17098c2ecf20Sopenharmony_ci			   stats->tx_mcast_frames;
17108c2ecf20Sopenharmony_ci	stats->rx_frames = stats->rx_ucast_frames +
17118c2ecf20Sopenharmony_ci			   stats->rx_bcast_frames +
17128c2ecf20Sopenharmony_ci			   stats->rx_mcast_frames;
17138c2ecf20Sopenharmony_ci	stats->rx_drops = stats->rx_drop_red +
17148c2ecf20Sopenharmony_ci			  stats->rx_drop_overrun;
17158c2ecf20Sopenharmony_ci
17168c2ecf20Sopenharmony_ci	/* Update RQ and SQ stats */
17178c2ecf20Sopenharmony_ci	for (qidx = 0; qidx < qs->rq_cnt; qidx++)
17188c2ecf20Sopenharmony_ci		nicvf_update_rq_stats(nic, qidx);
17198c2ecf20Sopenharmony_ci	for (qidx = 0; qidx < qs->sq_cnt; qidx++)
17208c2ecf20Sopenharmony_ci		nicvf_update_sq_stats(nic, qidx);
17218c2ecf20Sopenharmony_ci}
17228c2ecf20Sopenharmony_ci
17238c2ecf20Sopenharmony_cistatic void nicvf_get_stats64(struct net_device *netdev,
17248c2ecf20Sopenharmony_ci			      struct rtnl_link_stats64 *stats)
17258c2ecf20Sopenharmony_ci{
17268c2ecf20Sopenharmony_ci	struct nicvf *nic = netdev_priv(netdev);
17278c2ecf20Sopenharmony_ci	struct nicvf_hw_stats *hw_stats = &nic->hw_stats;
17288c2ecf20Sopenharmony_ci
17298c2ecf20Sopenharmony_ci	nicvf_update_stats(nic);
17308c2ecf20Sopenharmony_ci
17318c2ecf20Sopenharmony_ci	stats->rx_bytes = hw_stats->rx_bytes;
17328c2ecf20Sopenharmony_ci	stats->rx_packets = hw_stats->rx_frames;
17338c2ecf20Sopenharmony_ci	stats->rx_dropped = hw_stats->rx_drops;
17348c2ecf20Sopenharmony_ci	stats->multicast = hw_stats->rx_mcast_frames;
17358c2ecf20Sopenharmony_ci
17368c2ecf20Sopenharmony_ci	stats->tx_bytes = hw_stats->tx_bytes;
17378c2ecf20Sopenharmony_ci	stats->tx_packets = hw_stats->tx_frames;
17388c2ecf20Sopenharmony_ci	stats->tx_dropped = hw_stats->tx_drops;
17398c2ecf20Sopenharmony_ci
17408c2ecf20Sopenharmony_ci}
17418c2ecf20Sopenharmony_ci
17428c2ecf20Sopenharmony_cistatic void nicvf_tx_timeout(struct net_device *dev, unsigned int txqueue)
17438c2ecf20Sopenharmony_ci{
17448c2ecf20Sopenharmony_ci	struct nicvf *nic = netdev_priv(dev);
17458c2ecf20Sopenharmony_ci
17468c2ecf20Sopenharmony_ci	netif_warn(nic, tx_err, dev, "Transmit timed out, resetting\n");
17478c2ecf20Sopenharmony_ci
17488c2ecf20Sopenharmony_ci	this_cpu_inc(nic->drv_stats->tx_timeout);
17498c2ecf20Sopenharmony_ci	schedule_work(&nic->reset_task);
17508c2ecf20Sopenharmony_ci}
17518c2ecf20Sopenharmony_ci
17528c2ecf20Sopenharmony_cistatic void nicvf_reset_task(struct work_struct *work)
17538c2ecf20Sopenharmony_ci{
17548c2ecf20Sopenharmony_ci	struct nicvf *nic;
17558c2ecf20Sopenharmony_ci
17568c2ecf20Sopenharmony_ci	nic = container_of(work, struct nicvf, reset_task);
17578c2ecf20Sopenharmony_ci
17588c2ecf20Sopenharmony_ci	if (!netif_running(nic->netdev))
17598c2ecf20Sopenharmony_ci		return;
17608c2ecf20Sopenharmony_ci
17618c2ecf20Sopenharmony_ci	nicvf_stop(nic->netdev);
17628c2ecf20Sopenharmony_ci	nicvf_open(nic->netdev);
17638c2ecf20Sopenharmony_ci	netif_trans_update(nic->netdev);
17648c2ecf20Sopenharmony_ci}
17658c2ecf20Sopenharmony_ci
17668c2ecf20Sopenharmony_cistatic int nicvf_config_loopback(struct nicvf *nic,
17678c2ecf20Sopenharmony_ci				 netdev_features_t features)
17688c2ecf20Sopenharmony_ci{
17698c2ecf20Sopenharmony_ci	union nic_mbx mbx = {};
17708c2ecf20Sopenharmony_ci
17718c2ecf20Sopenharmony_ci	mbx.lbk.msg = NIC_MBOX_MSG_LOOPBACK;
17728c2ecf20Sopenharmony_ci	mbx.lbk.vf_id = nic->vf_id;
17738c2ecf20Sopenharmony_ci	mbx.lbk.enable = (features & NETIF_F_LOOPBACK) != 0;
17748c2ecf20Sopenharmony_ci
17758c2ecf20Sopenharmony_ci	return nicvf_send_msg_to_pf(nic, &mbx);
17768c2ecf20Sopenharmony_ci}
17778c2ecf20Sopenharmony_ci
17788c2ecf20Sopenharmony_cistatic netdev_features_t nicvf_fix_features(struct net_device *netdev,
17798c2ecf20Sopenharmony_ci					    netdev_features_t features)
17808c2ecf20Sopenharmony_ci{
17818c2ecf20Sopenharmony_ci	struct nicvf *nic = netdev_priv(netdev);
17828c2ecf20Sopenharmony_ci
17838c2ecf20Sopenharmony_ci	if ((features & NETIF_F_LOOPBACK) &&
17848c2ecf20Sopenharmony_ci	    netif_running(netdev) && !nic->loopback_supported)
17858c2ecf20Sopenharmony_ci		features &= ~NETIF_F_LOOPBACK;
17868c2ecf20Sopenharmony_ci
17878c2ecf20Sopenharmony_ci	return features;
17888c2ecf20Sopenharmony_ci}
17898c2ecf20Sopenharmony_ci
17908c2ecf20Sopenharmony_cistatic int nicvf_set_features(struct net_device *netdev,
17918c2ecf20Sopenharmony_ci			      netdev_features_t features)
17928c2ecf20Sopenharmony_ci{
17938c2ecf20Sopenharmony_ci	struct nicvf *nic = netdev_priv(netdev);
17948c2ecf20Sopenharmony_ci	netdev_features_t changed = features ^ netdev->features;
17958c2ecf20Sopenharmony_ci
17968c2ecf20Sopenharmony_ci	if (changed & NETIF_F_HW_VLAN_CTAG_RX)
17978c2ecf20Sopenharmony_ci		nicvf_config_vlan_stripping(nic, features);
17988c2ecf20Sopenharmony_ci
17998c2ecf20Sopenharmony_ci	if ((changed & NETIF_F_LOOPBACK) && netif_running(netdev))
18008c2ecf20Sopenharmony_ci		return nicvf_config_loopback(nic, features);
18018c2ecf20Sopenharmony_ci
18028c2ecf20Sopenharmony_ci	return 0;
18038c2ecf20Sopenharmony_ci}
18048c2ecf20Sopenharmony_ci
18058c2ecf20Sopenharmony_cistatic void nicvf_set_xdp_queues(struct nicvf *nic, bool bpf_attached)
18068c2ecf20Sopenharmony_ci{
18078c2ecf20Sopenharmony_ci	u8 cq_count, txq_count;
18088c2ecf20Sopenharmony_ci
18098c2ecf20Sopenharmony_ci	/* Set XDP Tx queue count same as Rx queue count */
18108c2ecf20Sopenharmony_ci	if (!bpf_attached)
18118c2ecf20Sopenharmony_ci		nic->xdp_tx_queues = 0;
18128c2ecf20Sopenharmony_ci	else
18138c2ecf20Sopenharmony_ci		nic->xdp_tx_queues = nic->rx_queues;
18148c2ecf20Sopenharmony_ci
18158c2ecf20Sopenharmony_ci	/* If queue count > MAX_CMP_QUEUES_PER_QS, then additional qsets
18168c2ecf20Sopenharmony_ci	 * needs to be allocated, check how many.
18178c2ecf20Sopenharmony_ci	 */
18188c2ecf20Sopenharmony_ci	txq_count = nic->xdp_tx_queues + nic->tx_queues;
18198c2ecf20Sopenharmony_ci	cq_count = max(nic->rx_queues, txq_count);
18208c2ecf20Sopenharmony_ci	if (cq_count > MAX_CMP_QUEUES_PER_QS) {
18218c2ecf20Sopenharmony_ci		nic->sqs_count = roundup(cq_count, MAX_CMP_QUEUES_PER_QS);
18228c2ecf20Sopenharmony_ci		nic->sqs_count = (nic->sqs_count / MAX_CMP_QUEUES_PER_QS) - 1;
18238c2ecf20Sopenharmony_ci	} else {
18248c2ecf20Sopenharmony_ci		nic->sqs_count = 0;
18258c2ecf20Sopenharmony_ci	}
18268c2ecf20Sopenharmony_ci
18278c2ecf20Sopenharmony_ci	/* Set primary Qset's resources */
18288c2ecf20Sopenharmony_ci	nic->qs->rq_cnt = min_t(u8, nic->rx_queues, MAX_RCV_QUEUES_PER_QS);
18298c2ecf20Sopenharmony_ci	nic->qs->sq_cnt = min_t(u8, txq_count, MAX_SND_QUEUES_PER_QS);
18308c2ecf20Sopenharmony_ci	nic->qs->cq_cnt = max_t(u8, nic->qs->rq_cnt, nic->qs->sq_cnt);
18318c2ecf20Sopenharmony_ci
18328c2ecf20Sopenharmony_ci	/* Update stack */
18338c2ecf20Sopenharmony_ci	nicvf_set_real_num_queues(nic->netdev, nic->tx_queues, nic->rx_queues);
18348c2ecf20Sopenharmony_ci}
18358c2ecf20Sopenharmony_ci
18368c2ecf20Sopenharmony_cistatic int nicvf_xdp_setup(struct nicvf *nic, struct bpf_prog *prog)
18378c2ecf20Sopenharmony_ci{
18388c2ecf20Sopenharmony_ci	struct net_device *dev = nic->netdev;
18398c2ecf20Sopenharmony_ci	bool if_up = netif_running(nic->netdev);
18408c2ecf20Sopenharmony_ci	struct bpf_prog *old_prog;
18418c2ecf20Sopenharmony_ci	bool bpf_attached = false;
18428c2ecf20Sopenharmony_ci	int ret = 0;
18438c2ecf20Sopenharmony_ci
18448c2ecf20Sopenharmony_ci	/* For now just support only the usual MTU sized frames,
18458c2ecf20Sopenharmony_ci	 * plus some headroom for VLAN, QinQ.
18468c2ecf20Sopenharmony_ci	 */
18478c2ecf20Sopenharmony_ci	if (prog && dev->mtu > MAX_XDP_MTU) {
18488c2ecf20Sopenharmony_ci		netdev_warn(dev, "Jumbo frames not yet supported with XDP, current MTU %d.\n",
18498c2ecf20Sopenharmony_ci			    dev->mtu);
18508c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
18518c2ecf20Sopenharmony_ci	}
18528c2ecf20Sopenharmony_ci
18538c2ecf20Sopenharmony_ci	/* ALL SQs attached to CQs i.e same as RQs, are treated as
18548c2ecf20Sopenharmony_ci	 * XDP Tx queues and more Tx queues are allocated for
18558c2ecf20Sopenharmony_ci	 * network stack to send pkts out.
18568c2ecf20Sopenharmony_ci	 *
18578c2ecf20Sopenharmony_ci	 * No of Tx queues are either same as Rx queues or whatever
18588c2ecf20Sopenharmony_ci	 * is left in max no of queues possible.
18598c2ecf20Sopenharmony_ci	 */
18608c2ecf20Sopenharmony_ci	if ((nic->rx_queues + nic->tx_queues) > nic->max_queues) {
18618c2ecf20Sopenharmony_ci		netdev_warn(dev,
18628c2ecf20Sopenharmony_ci			    "Failed to attach BPF prog, RXQs + TXQs > Max %d\n",
18638c2ecf20Sopenharmony_ci			    nic->max_queues);
18648c2ecf20Sopenharmony_ci		return -ENOMEM;
18658c2ecf20Sopenharmony_ci	}
18668c2ecf20Sopenharmony_ci
18678c2ecf20Sopenharmony_ci	if (if_up)
18688c2ecf20Sopenharmony_ci		nicvf_stop(nic->netdev);
18698c2ecf20Sopenharmony_ci
18708c2ecf20Sopenharmony_ci	old_prog = xchg(&nic->xdp_prog, prog);
18718c2ecf20Sopenharmony_ci	/* Detach old prog, if any */
18728c2ecf20Sopenharmony_ci	if (old_prog)
18738c2ecf20Sopenharmony_ci		bpf_prog_put(old_prog);
18748c2ecf20Sopenharmony_ci
18758c2ecf20Sopenharmony_ci	if (nic->xdp_prog) {
18768c2ecf20Sopenharmony_ci		/* Attach BPF program */
18778c2ecf20Sopenharmony_ci		bpf_prog_add(nic->xdp_prog, nic->rx_queues - 1);
18788c2ecf20Sopenharmony_ci		bpf_attached = true;
18798c2ecf20Sopenharmony_ci	}
18808c2ecf20Sopenharmony_ci
18818c2ecf20Sopenharmony_ci	/* Calculate Tx queues needed for XDP and network stack */
18828c2ecf20Sopenharmony_ci	nicvf_set_xdp_queues(nic, bpf_attached);
18838c2ecf20Sopenharmony_ci
18848c2ecf20Sopenharmony_ci	if (if_up) {
18858c2ecf20Sopenharmony_ci		/* Reinitialize interface, clean slate */
18868c2ecf20Sopenharmony_ci		nicvf_open(nic->netdev);
18878c2ecf20Sopenharmony_ci		netif_trans_update(nic->netdev);
18888c2ecf20Sopenharmony_ci	}
18898c2ecf20Sopenharmony_ci
18908c2ecf20Sopenharmony_ci	return ret;
18918c2ecf20Sopenharmony_ci}
18928c2ecf20Sopenharmony_ci
18938c2ecf20Sopenharmony_cistatic int nicvf_xdp(struct net_device *netdev, struct netdev_bpf *xdp)
18948c2ecf20Sopenharmony_ci{
18958c2ecf20Sopenharmony_ci	struct nicvf *nic = netdev_priv(netdev);
18968c2ecf20Sopenharmony_ci
18978c2ecf20Sopenharmony_ci	/* To avoid checks while retrieving buffer address from CQE_RX,
18988c2ecf20Sopenharmony_ci	 * do not support XDP for T88 pass1.x silicons which are anyway
18998c2ecf20Sopenharmony_ci	 * not in use widely.
19008c2ecf20Sopenharmony_ci	 */
19018c2ecf20Sopenharmony_ci	if (pass1_silicon(nic->pdev))
19028c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
19038c2ecf20Sopenharmony_ci
19048c2ecf20Sopenharmony_ci	switch (xdp->command) {
19058c2ecf20Sopenharmony_ci	case XDP_SETUP_PROG:
19068c2ecf20Sopenharmony_ci		return nicvf_xdp_setup(nic, xdp->prog);
19078c2ecf20Sopenharmony_ci	default:
19088c2ecf20Sopenharmony_ci		return -EINVAL;
19098c2ecf20Sopenharmony_ci	}
19108c2ecf20Sopenharmony_ci}
19118c2ecf20Sopenharmony_ci
19128c2ecf20Sopenharmony_cistatic int nicvf_config_hwtstamp(struct net_device *netdev, struct ifreq *ifr)
19138c2ecf20Sopenharmony_ci{
19148c2ecf20Sopenharmony_ci	struct hwtstamp_config config;
19158c2ecf20Sopenharmony_ci	struct nicvf *nic = netdev_priv(netdev);
19168c2ecf20Sopenharmony_ci
19178c2ecf20Sopenharmony_ci	if (!nic->ptp_clock)
19188c2ecf20Sopenharmony_ci		return -ENODEV;
19198c2ecf20Sopenharmony_ci
19208c2ecf20Sopenharmony_ci	if (copy_from_user(&config, ifr->ifr_data, sizeof(config)))
19218c2ecf20Sopenharmony_ci		return -EFAULT;
19228c2ecf20Sopenharmony_ci
19238c2ecf20Sopenharmony_ci	/* reserved for future extensions */
19248c2ecf20Sopenharmony_ci	if (config.flags)
19258c2ecf20Sopenharmony_ci		return -EINVAL;
19268c2ecf20Sopenharmony_ci
19278c2ecf20Sopenharmony_ci	switch (config.tx_type) {
19288c2ecf20Sopenharmony_ci	case HWTSTAMP_TX_OFF:
19298c2ecf20Sopenharmony_ci	case HWTSTAMP_TX_ON:
19308c2ecf20Sopenharmony_ci		break;
19318c2ecf20Sopenharmony_ci	default:
19328c2ecf20Sopenharmony_ci		return -ERANGE;
19338c2ecf20Sopenharmony_ci	}
19348c2ecf20Sopenharmony_ci
19358c2ecf20Sopenharmony_ci	switch (config.rx_filter) {
19368c2ecf20Sopenharmony_ci	case HWTSTAMP_FILTER_NONE:
19378c2ecf20Sopenharmony_ci		nic->hw_rx_tstamp = false;
19388c2ecf20Sopenharmony_ci		break;
19398c2ecf20Sopenharmony_ci	case HWTSTAMP_FILTER_ALL:
19408c2ecf20Sopenharmony_ci	case HWTSTAMP_FILTER_SOME:
19418c2ecf20Sopenharmony_ci	case HWTSTAMP_FILTER_PTP_V1_L4_EVENT:
19428c2ecf20Sopenharmony_ci	case HWTSTAMP_FILTER_PTP_V1_L4_SYNC:
19438c2ecf20Sopenharmony_ci	case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ:
19448c2ecf20Sopenharmony_ci	case HWTSTAMP_FILTER_PTP_V2_L4_EVENT:
19458c2ecf20Sopenharmony_ci	case HWTSTAMP_FILTER_PTP_V2_L4_SYNC:
19468c2ecf20Sopenharmony_ci	case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ:
19478c2ecf20Sopenharmony_ci	case HWTSTAMP_FILTER_PTP_V2_L2_EVENT:
19488c2ecf20Sopenharmony_ci	case HWTSTAMP_FILTER_PTP_V2_L2_SYNC:
19498c2ecf20Sopenharmony_ci	case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ:
19508c2ecf20Sopenharmony_ci	case HWTSTAMP_FILTER_PTP_V2_EVENT:
19518c2ecf20Sopenharmony_ci	case HWTSTAMP_FILTER_PTP_V2_SYNC:
19528c2ecf20Sopenharmony_ci	case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ:
19538c2ecf20Sopenharmony_ci		nic->hw_rx_tstamp = true;
19548c2ecf20Sopenharmony_ci		config.rx_filter = HWTSTAMP_FILTER_ALL;
19558c2ecf20Sopenharmony_ci		break;
19568c2ecf20Sopenharmony_ci	default:
19578c2ecf20Sopenharmony_ci		return -ERANGE;
19588c2ecf20Sopenharmony_ci	}
19598c2ecf20Sopenharmony_ci
19608c2ecf20Sopenharmony_ci	if (netif_running(netdev))
19618c2ecf20Sopenharmony_ci		nicvf_config_hw_rx_tstamp(nic, nic->hw_rx_tstamp);
19628c2ecf20Sopenharmony_ci
19638c2ecf20Sopenharmony_ci	if (copy_to_user(ifr->ifr_data, &config, sizeof(config)))
19648c2ecf20Sopenharmony_ci		return -EFAULT;
19658c2ecf20Sopenharmony_ci
19668c2ecf20Sopenharmony_ci	return 0;
19678c2ecf20Sopenharmony_ci}
19688c2ecf20Sopenharmony_ci
19698c2ecf20Sopenharmony_cistatic int nicvf_ioctl(struct net_device *netdev, struct ifreq *req, int cmd)
19708c2ecf20Sopenharmony_ci{
19718c2ecf20Sopenharmony_ci	switch (cmd) {
19728c2ecf20Sopenharmony_ci	case SIOCSHWTSTAMP:
19738c2ecf20Sopenharmony_ci		return nicvf_config_hwtstamp(netdev, req);
19748c2ecf20Sopenharmony_ci	default:
19758c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
19768c2ecf20Sopenharmony_ci	}
19778c2ecf20Sopenharmony_ci}
19788c2ecf20Sopenharmony_ci
19798c2ecf20Sopenharmony_cistatic void __nicvf_set_rx_mode_task(u8 mode, struct xcast_addr_list *mc_addrs,
19808c2ecf20Sopenharmony_ci				     struct nicvf *nic)
19818c2ecf20Sopenharmony_ci{
19828c2ecf20Sopenharmony_ci	union nic_mbx mbx = {};
19838c2ecf20Sopenharmony_ci	int idx;
19848c2ecf20Sopenharmony_ci
19858c2ecf20Sopenharmony_ci	/* From the inside of VM code flow we have only 128 bits memory
19868c2ecf20Sopenharmony_ci	 * available to send message to host's PF, so send all mc addrs
19878c2ecf20Sopenharmony_ci	 * one by one, starting from flush command in case if kernel
19888c2ecf20Sopenharmony_ci	 * requests to configure specific MAC filtering
19898c2ecf20Sopenharmony_ci	 */
19908c2ecf20Sopenharmony_ci
19918c2ecf20Sopenharmony_ci	/* flush DMAC filters and reset RX mode */
19928c2ecf20Sopenharmony_ci	mbx.xcast.msg = NIC_MBOX_MSG_RESET_XCAST;
19938c2ecf20Sopenharmony_ci	if (nicvf_send_msg_to_pf(nic, &mbx) < 0)
19948c2ecf20Sopenharmony_ci		goto free_mc;
19958c2ecf20Sopenharmony_ci
19968c2ecf20Sopenharmony_ci	if (mode & BGX_XCAST_MCAST_FILTER) {
19978c2ecf20Sopenharmony_ci		/* once enabling filtering, we need to signal to PF to add
19988c2ecf20Sopenharmony_ci		 * its' own LMAC to the filter to accept packets for it.
19998c2ecf20Sopenharmony_ci		 */
20008c2ecf20Sopenharmony_ci		mbx.xcast.msg = NIC_MBOX_MSG_ADD_MCAST;
20018c2ecf20Sopenharmony_ci		mbx.xcast.mac = 0;
20028c2ecf20Sopenharmony_ci		if (nicvf_send_msg_to_pf(nic, &mbx) < 0)
20038c2ecf20Sopenharmony_ci			goto free_mc;
20048c2ecf20Sopenharmony_ci	}
20058c2ecf20Sopenharmony_ci
20068c2ecf20Sopenharmony_ci	/* check if we have any specific MACs to be added to PF DMAC filter */
20078c2ecf20Sopenharmony_ci	if (mc_addrs) {
20088c2ecf20Sopenharmony_ci		/* now go through kernel list of MACs and add them one by one */
20098c2ecf20Sopenharmony_ci		for (idx = 0; idx < mc_addrs->count; idx++) {
20108c2ecf20Sopenharmony_ci			mbx.xcast.msg = NIC_MBOX_MSG_ADD_MCAST;
20118c2ecf20Sopenharmony_ci			mbx.xcast.mac = mc_addrs->mc[idx];
20128c2ecf20Sopenharmony_ci			if (nicvf_send_msg_to_pf(nic, &mbx) < 0)
20138c2ecf20Sopenharmony_ci				goto free_mc;
20148c2ecf20Sopenharmony_ci		}
20158c2ecf20Sopenharmony_ci	}
20168c2ecf20Sopenharmony_ci
20178c2ecf20Sopenharmony_ci	/* and finally set rx mode for PF accordingly */
20188c2ecf20Sopenharmony_ci	mbx.xcast.msg = NIC_MBOX_MSG_SET_XCAST;
20198c2ecf20Sopenharmony_ci	mbx.xcast.mode = mode;
20208c2ecf20Sopenharmony_ci
20218c2ecf20Sopenharmony_ci	nicvf_send_msg_to_pf(nic, &mbx);
20228c2ecf20Sopenharmony_cifree_mc:
20238c2ecf20Sopenharmony_ci	kfree(mc_addrs);
20248c2ecf20Sopenharmony_ci}
20258c2ecf20Sopenharmony_ci
20268c2ecf20Sopenharmony_cistatic void nicvf_set_rx_mode_task(struct work_struct *work_arg)
20278c2ecf20Sopenharmony_ci{
20288c2ecf20Sopenharmony_ci	struct nicvf_work *vf_work = container_of(work_arg, struct nicvf_work,
20298c2ecf20Sopenharmony_ci						  work);
20308c2ecf20Sopenharmony_ci	struct nicvf *nic = container_of(vf_work, struct nicvf, rx_mode_work);
20318c2ecf20Sopenharmony_ci	u8 mode;
20328c2ecf20Sopenharmony_ci	struct xcast_addr_list *mc;
20338c2ecf20Sopenharmony_ci
20348c2ecf20Sopenharmony_ci	if (!vf_work)
20358c2ecf20Sopenharmony_ci		return;
20368c2ecf20Sopenharmony_ci
20378c2ecf20Sopenharmony_ci	/* Save message data locally to prevent them from
20388c2ecf20Sopenharmony_ci	 * being overwritten by next ndo_set_rx_mode call().
20398c2ecf20Sopenharmony_ci	 */
20408c2ecf20Sopenharmony_ci	spin_lock_bh(&nic->rx_mode_wq_lock);
20418c2ecf20Sopenharmony_ci	mode = vf_work->mode;
20428c2ecf20Sopenharmony_ci	mc = vf_work->mc;
20438c2ecf20Sopenharmony_ci	vf_work->mc = NULL;
20448c2ecf20Sopenharmony_ci	spin_unlock_bh(&nic->rx_mode_wq_lock);
20458c2ecf20Sopenharmony_ci
20468c2ecf20Sopenharmony_ci	__nicvf_set_rx_mode_task(mode, mc, nic);
20478c2ecf20Sopenharmony_ci}
20488c2ecf20Sopenharmony_ci
20498c2ecf20Sopenharmony_cistatic void nicvf_set_rx_mode(struct net_device *netdev)
20508c2ecf20Sopenharmony_ci{
20518c2ecf20Sopenharmony_ci	struct nicvf *nic = netdev_priv(netdev);
20528c2ecf20Sopenharmony_ci	struct netdev_hw_addr *ha;
20538c2ecf20Sopenharmony_ci	struct xcast_addr_list *mc_list = NULL;
20548c2ecf20Sopenharmony_ci	u8 mode = 0;
20558c2ecf20Sopenharmony_ci
20568c2ecf20Sopenharmony_ci	if (netdev->flags & IFF_PROMISC) {
20578c2ecf20Sopenharmony_ci		mode = BGX_XCAST_BCAST_ACCEPT | BGX_XCAST_MCAST_ACCEPT;
20588c2ecf20Sopenharmony_ci	} else {
20598c2ecf20Sopenharmony_ci		if (netdev->flags & IFF_BROADCAST)
20608c2ecf20Sopenharmony_ci			mode |= BGX_XCAST_BCAST_ACCEPT;
20618c2ecf20Sopenharmony_ci
20628c2ecf20Sopenharmony_ci		if (netdev->flags & IFF_ALLMULTI) {
20638c2ecf20Sopenharmony_ci			mode |= BGX_XCAST_MCAST_ACCEPT;
20648c2ecf20Sopenharmony_ci		} else if (netdev->flags & IFF_MULTICAST) {
20658c2ecf20Sopenharmony_ci			mode |= BGX_XCAST_MCAST_FILTER;
20668c2ecf20Sopenharmony_ci			/* here we need to copy mc addrs */
20678c2ecf20Sopenharmony_ci			if (netdev_mc_count(netdev)) {
20688c2ecf20Sopenharmony_ci				mc_list = kmalloc(struct_size(mc_list, mc,
20698c2ecf20Sopenharmony_ci							      netdev_mc_count(netdev)),
20708c2ecf20Sopenharmony_ci						  GFP_ATOMIC);
20718c2ecf20Sopenharmony_ci				if (unlikely(!mc_list))
20728c2ecf20Sopenharmony_ci					return;
20738c2ecf20Sopenharmony_ci				mc_list->count = 0;
20748c2ecf20Sopenharmony_ci				netdev_hw_addr_list_for_each(ha, &netdev->mc) {
20758c2ecf20Sopenharmony_ci					mc_list->mc[mc_list->count] =
20768c2ecf20Sopenharmony_ci						ether_addr_to_u64(ha->addr);
20778c2ecf20Sopenharmony_ci					mc_list->count++;
20788c2ecf20Sopenharmony_ci				}
20798c2ecf20Sopenharmony_ci			}
20808c2ecf20Sopenharmony_ci		}
20818c2ecf20Sopenharmony_ci	}
20828c2ecf20Sopenharmony_ci	spin_lock(&nic->rx_mode_wq_lock);
20838c2ecf20Sopenharmony_ci	kfree(nic->rx_mode_work.mc);
20848c2ecf20Sopenharmony_ci	nic->rx_mode_work.mc = mc_list;
20858c2ecf20Sopenharmony_ci	nic->rx_mode_work.mode = mode;
20868c2ecf20Sopenharmony_ci	queue_work(nic->nicvf_rx_mode_wq, &nic->rx_mode_work.work);
20878c2ecf20Sopenharmony_ci	spin_unlock(&nic->rx_mode_wq_lock);
20888c2ecf20Sopenharmony_ci}
20898c2ecf20Sopenharmony_ci
20908c2ecf20Sopenharmony_cistatic const struct net_device_ops nicvf_netdev_ops = {
20918c2ecf20Sopenharmony_ci	.ndo_open		= nicvf_open,
20928c2ecf20Sopenharmony_ci	.ndo_stop		= nicvf_stop,
20938c2ecf20Sopenharmony_ci	.ndo_start_xmit		= nicvf_xmit,
20948c2ecf20Sopenharmony_ci	.ndo_change_mtu		= nicvf_change_mtu,
20958c2ecf20Sopenharmony_ci	.ndo_set_mac_address	= nicvf_set_mac_address,
20968c2ecf20Sopenharmony_ci	.ndo_get_stats64	= nicvf_get_stats64,
20978c2ecf20Sopenharmony_ci	.ndo_tx_timeout         = nicvf_tx_timeout,
20988c2ecf20Sopenharmony_ci	.ndo_fix_features       = nicvf_fix_features,
20998c2ecf20Sopenharmony_ci	.ndo_set_features       = nicvf_set_features,
21008c2ecf20Sopenharmony_ci	.ndo_bpf		= nicvf_xdp,
21018c2ecf20Sopenharmony_ci	.ndo_do_ioctl           = nicvf_ioctl,
21028c2ecf20Sopenharmony_ci	.ndo_set_rx_mode        = nicvf_set_rx_mode,
21038c2ecf20Sopenharmony_ci};
21048c2ecf20Sopenharmony_ci
21058c2ecf20Sopenharmony_cistatic int nicvf_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
21068c2ecf20Sopenharmony_ci{
21078c2ecf20Sopenharmony_ci	struct device *dev = &pdev->dev;
21088c2ecf20Sopenharmony_ci	struct net_device *netdev;
21098c2ecf20Sopenharmony_ci	struct nicvf *nic;
21108c2ecf20Sopenharmony_ci	int    err, qcount;
21118c2ecf20Sopenharmony_ci	u16    sdevid;
21128c2ecf20Sopenharmony_ci	struct cavium_ptp *ptp_clock;
21138c2ecf20Sopenharmony_ci
21148c2ecf20Sopenharmony_ci	ptp_clock = cavium_ptp_get();
21158c2ecf20Sopenharmony_ci	if (IS_ERR(ptp_clock)) {
21168c2ecf20Sopenharmony_ci		if (PTR_ERR(ptp_clock) == -ENODEV)
21178c2ecf20Sopenharmony_ci			/* In virtualized environment we proceed without ptp */
21188c2ecf20Sopenharmony_ci			ptp_clock = NULL;
21198c2ecf20Sopenharmony_ci		else
21208c2ecf20Sopenharmony_ci			return PTR_ERR(ptp_clock);
21218c2ecf20Sopenharmony_ci	}
21228c2ecf20Sopenharmony_ci
21238c2ecf20Sopenharmony_ci	err = pci_enable_device(pdev);
21248c2ecf20Sopenharmony_ci	if (err) {
21258c2ecf20Sopenharmony_ci		dev_err(dev, "Failed to enable PCI device\n");
21268c2ecf20Sopenharmony_ci		return err;
21278c2ecf20Sopenharmony_ci	}
21288c2ecf20Sopenharmony_ci
21298c2ecf20Sopenharmony_ci	err = pci_request_regions(pdev, DRV_NAME);
21308c2ecf20Sopenharmony_ci	if (err) {
21318c2ecf20Sopenharmony_ci		dev_err(dev, "PCI request regions failed 0x%x\n", err);
21328c2ecf20Sopenharmony_ci		goto err_disable_device;
21338c2ecf20Sopenharmony_ci	}
21348c2ecf20Sopenharmony_ci
21358c2ecf20Sopenharmony_ci	err = pci_set_dma_mask(pdev, DMA_BIT_MASK(48));
21368c2ecf20Sopenharmony_ci	if (err) {
21378c2ecf20Sopenharmony_ci		dev_err(dev, "Unable to get usable DMA configuration\n");
21388c2ecf20Sopenharmony_ci		goto err_release_regions;
21398c2ecf20Sopenharmony_ci	}
21408c2ecf20Sopenharmony_ci
21418c2ecf20Sopenharmony_ci	err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(48));
21428c2ecf20Sopenharmony_ci	if (err) {
21438c2ecf20Sopenharmony_ci		dev_err(dev, "unable to get 48-bit DMA for consistent allocations\n");
21448c2ecf20Sopenharmony_ci		goto err_release_regions;
21458c2ecf20Sopenharmony_ci	}
21468c2ecf20Sopenharmony_ci
21478c2ecf20Sopenharmony_ci	qcount = netif_get_num_default_rss_queues();
21488c2ecf20Sopenharmony_ci
21498c2ecf20Sopenharmony_ci	/* Restrict multiqset support only for host bound VFs */
21508c2ecf20Sopenharmony_ci	if (pdev->is_virtfn) {
21518c2ecf20Sopenharmony_ci		/* Set max number of queues per VF */
21528c2ecf20Sopenharmony_ci		qcount = min_t(int, num_online_cpus(),
21538c2ecf20Sopenharmony_ci			       (MAX_SQS_PER_VF + 1) * MAX_CMP_QUEUES_PER_QS);
21548c2ecf20Sopenharmony_ci	}
21558c2ecf20Sopenharmony_ci
21568c2ecf20Sopenharmony_ci	netdev = alloc_etherdev_mqs(sizeof(struct nicvf), qcount, qcount);
21578c2ecf20Sopenharmony_ci	if (!netdev) {
21588c2ecf20Sopenharmony_ci		err = -ENOMEM;
21598c2ecf20Sopenharmony_ci		goto err_release_regions;
21608c2ecf20Sopenharmony_ci	}
21618c2ecf20Sopenharmony_ci
21628c2ecf20Sopenharmony_ci	pci_set_drvdata(pdev, netdev);
21638c2ecf20Sopenharmony_ci
21648c2ecf20Sopenharmony_ci	SET_NETDEV_DEV(netdev, &pdev->dev);
21658c2ecf20Sopenharmony_ci
21668c2ecf20Sopenharmony_ci	nic = netdev_priv(netdev);
21678c2ecf20Sopenharmony_ci	nic->netdev = netdev;
21688c2ecf20Sopenharmony_ci	nic->pdev = pdev;
21698c2ecf20Sopenharmony_ci	nic->pnicvf = nic;
21708c2ecf20Sopenharmony_ci	nic->max_queues = qcount;
21718c2ecf20Sopenharmony_ci	/* If no of CPUs are too low, there won't be any queues left
21728c2ecf20Sopenharmony_ci	 * for XDP_TX, hence double it.
21738c2ecf20Sopenharmony_ci	 */
21748c2ecf20Sopenharmony_ci	if (!nic->t88)
21758c2ecf20Sopenharmony_ci		nic->max_queues *= 2;
21768c2ecf20Sopenharmony_ci	nic->ptp_clock = ptp_clock;
21778c2ecf20Sopenharmony_ci
21788c2ecf20Sopenharmony_ci	/* Initialize mutex that serializes usage of VF's mailbox */
21798c2ecf20Sopenharmony_ci	mutex_init(&nic->rx_mode_mtx);
21808c2ecf20Sopenharmony_ci
21818c2ecf20Sopenharmony_ci	/* MAP VF's configuration registers */
21828c2ecf20Sopenharmony_ci	nic->reg_base = pcim_iomap(pdev, PCI_CFG_REG_BAR_NUM, 0);
21838c2ecf20Sopenharmony_ci	if (!nic->reg_base) {
21848c2ecf20Sopenharmony_ci		dev_err(dev, "Cannot map config register space, aborting\n");
21858c2ecf20Sopenharmony_ci		err = -ENOMEM;
21868c2ecf20Sopenharmony_ci		goto err_free_netdev;
21878c2ecf20Sopenharmony_ci	}
21888c2ecf20Sopenharmony_ci
21898c2ecf20Sopenharmony_ci	nic->drv_stats = netdev_alloc_pcpu_stats(struct nicvf_drv_stats);
21908c2ecf20Sopenharmony_ci	if (!nic->drv_stats) {
21918c2ecf20Sopenharmony_ci		err = -ENOMEM;
21928c2ecf20Sopenharmony_ci		goto err_free_netdev;
21938c2ecf20Sopenharmony_ci	}
21948c2ecf20Sopenharmony_ci
21958c2ecf20Sopenharmony_ci	err = nicvf_set_qset_resources(nic);
21968c2ecf20Sopenharmony_ci	if (err)
21978c2ecf20Sopenharmony_ci		goto err_free_netdev;
21988c2ecf20Sopenharmony_ci
21998c2ecf20Sopenharmony_ci	/* Check if PF is alive and get MAC address for this VF */
22008c2ecf20Sopenharmony_ci	err = nicvf_register_misc_interrupt(nic);
22018c2ecf20Sopenharmony_ci	if (err)
22028c2ecf20Sopenharmony_ci		goto err_free_netdev;
22038c2ecf20Sopenharmony_ci
22048c2ecf20Sopenharmony_ci	nicvf_send_vf_struct(nic);
22058c2ecf20Sopenharmony_ci
22068c2ecf20Sopenharmony_ci	if (!pass1_silicon(nic->pdev))
22078c2ecf20Sopenharmony_ci		nic->hw_tso = true;
22088c2ecf20Sopenharmony_ci
22098c2ecf20Sopenharmony_ci	/* Get iommu domain for iova to physical addr conversion */
22108c2ecf20Sopenharmony_ci	nic->iommu_domain = iommu_get_domain_for_dev(dev);
22118c2ecf20Sopenharmony_ci
22128c2ecf20Sopenharmony_ci	pci_read_config_word(nic->pdev, PCI_SUBSYSTEM_ID, &sdevid);
22138c2ecf20Sopenharmony_ci	if (sdevid == 0xA134)
22148c2ecf20Sopenharmony_ci		nic->t88 = true;
22158c2ecf20Sopenharmony_ci
22168c2ecf20Sopenharmony_ci	/* Check if this VF is in QS only mode */
22178c2ecf20Sopenharmony_ci	if (nic->sqs_mode)
22188c2ecf20Sopenharmony_ci		return 0;
22198c2ecf20Sopenharmony_ci
22208c2ecf20Sopenharmony_ci	err = nicvf_set_real_num_queues(netdev, nic->tx_queues, nic->rx_queues);
22218c2ecf20Sopenharmony_ci	if (err)
22228c2ecf20Sopenharmony_ci		goto err_unregister_interrupts;
22238c2ecf20Sopenharmony_ci
22248c2ecf20Sopenharmony_ci	netdev->hw_features = (NETIF_F_RXCSUM | NETIF_F_SG |
22258c2ecf20Sopenharmony_ci			       NETIF_F_TSO | NETIF_F_GRO | NETIF_F_TSO6 |
22268c2ecf20Sopenharmony_ci			       NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM |
22278c2ecf20Sopenharmony_ci			       NETIF_F_HW_VLAN_CTAG_RX);
22288c2ecf20Sopenharmony_ci
22298c2ecf20Sopenharmony_ci	netdev->hw_features |= NETIF_F_RXHASH;
22308c2ecf20Sopenharmony_ci
22318c2ecf20Sopenharmony_ci	netdev->features |= netdev->hw_features;
22328c2ecf20Sopenharmony_ci	netdev->hw_features |= NETIF_F_LOOPBACK;
22338c2ecf20Sopenharmony_ci
22348c2ecf20Sopenharmony_ci	netdev->vlan_features = NETIF_F_SG | NETIF_F_IP_CSUM |
22358c2ecf20Sopenharmony_ci				NETIF_F_IPV6_CSUM | NETIF_F_TSO | NETIF_F_TSO6;
22368c2ecf20Sopenharmony_ci
22378c2ecf20Sopenharmony_ci	netdev->netdev_ops = &nicvf_netdev_ops;
22388c2ecf20Sopenharmony_ci	netdev->watchdog_timeo = NICVF_TX_TIMEOUT;
22398c2ecf20Sopenharmony_ci
22408c2ecf20Sopenharmony_ci	/* MTU range: 64 - 9200 */
22418c2ecf20Sopenharmony_ci	netdev->min_mtu = NIC_HW_MIN_FRS;
22428c2ecf20Sopenharmony_ci	netdev->max_mtu = NIC_HW_MAX_FRS;
22438c2ecf20Sopenharmony_ci
22448c2ecf20Sopenharmony_ci	INIT_WORK(&nic->reset_task, nicvf_reset_task);
22458c2ecf20Sopenharmony_ci
22468c2ecf20Sopenharmony_ci	nic->nicvf_rx_mode_wq = alloc_ordered_workqueue("nicvf_rx_mode_wq_VF%d",
22478c2ecf20Sopenharmony_ci							WQ_MEM_RECLAIM,
22488c2ecf20Sopenharmony_ci							nic->vf_id);
22498c2ecf20Sopenharmony_ci	if (!nic->nicvf_rx_mode_wq) {
22508c2ecf20Sopenharmony_ci		err = -ENOMEM;
22518c2ecf20Sopenharmony_ci		dev_err(dev, "Failed to allocate work queue\n");
22528c2ecf20Sopenharmony_ci		goto err_unregister_interrupts;
22538c2ecf20Sopenharmony_ci	}
22548c2ecf20Sopenharmony_ci
22558c2ecf20Sopenharmony_ci	INIT_WORK(&nic->rx_mode_work.work, nicvf_set_rx_mode_task);
22568c2ecf20Sopenharmony_ci	spin_lock_init(&nic->rx_mode_wq_lock);
22578c2ecf20Sopenharmony_ci
22588c2ecf20Sopenharmony_ci	err = register_netdev(netdev);
22598c2ecf20Sopenharmony_ci	if (err) {
22608c2ecf20Sopenharmony_ci		dev_err(dev, "Failed to register netdevice\n");
22618c2ecf20Sopenharmony_ci		goto err_destroy_workqueue;
22628c2ecf20Sopenharmony_ci	}
22638c2ecf20Sopenharmony_ci
22648c2ecf20Sopenharmony_ci	nic->msg_enable = debug;
22658c2ecf20Sopenharmony_ci
22668c2ecf20Sopenharmony_ci	nicvf_set_ethtool_ops(netdev);
22678c2ecf20Sopenharmony_ci
22688c2ecf20Sopenharmony_ci	return 0;
22698c2ecf20Sopenharmony_ci
22708c2ecf20Sopenharmony_cierr_destroy_workqueue:
22718c2ecf20Sopenharmony_ci	destroy_workqueue(nic->nicvf_rx_mode_wq);
22728c2ecf20Sopenharmony_cierr_unregister_interrupts:
22738c2ecf20Sopenharmony_ci	nicvf_unregister_interrupts(nic);
22748c2ecf20Sopenharmony_cierr_free_netdev:
22758c2ecf20Sopenharmony_ci	pci_set_drvdata(pdev, NULL);
22768c2ecf20Sopenharmony_ci	if (nic->drv_stats)
22778c2ecf20Sopenharmony_ci		free_percpu(nic->drv_stats);
22788c2ecf20Sopenharmony_ci	free_netdev(netdev);
22798c2ecf20Sopenharmony_cierr_release_regions:
22808c2ecf20Sopenharmony_ci	pci_release_regions(pdev);
22818c2ecf20Sopenharmony_cierr_disable_device:
22828c2ecf20Sopenharmony_ci	pci_disable_device(pdev);
22838c2ecf20Sopenharmony_ci	return err;
22848c2ecf20Sopenharmony_ci}
22858c2ecf20Sopenharmony_ci
22868c2ecf20Sopenharmony_cistatic void nicvf_remove(struct pci_dev *pdev)
22878c2ecf20Sopenharmony_ci{
22888c2ecf20Sopenharmony_ci	struct net_device *netdev = pci_get_drvdata(pdev);
22898c2ecf20Sopenharmony_ci	struct nicvf *nic;
22908c2ecf20Sopenharmony_ci	struct net_device *pnetdev;
22918c2ecf20Sopenharmony_ci
22928c2ecf20Sopenharmony_ci	if (!netdev)
22938c2ecf20Sopenharmony_ci		return;
22948c2ecf20Sopenharmony_ci
22958c2ecf20Sopenharmony_ci	nic = netdev_priv(netdev);
22968c2ecf20Sopenharmony_ci	pnetdev = nic->pnicvf->netdev;
22978c2ecf20Sopenharmony_ci
22988c2ecf20Sopenharmony_ci	/* Check if this Qset is assigned to different VF.
22998c2ecf20Sopenharmony_ci	 * If yes, clean primary and all secondary Qsets.
23008c2ecf20Sopenharmony_ci	 */
23018c2ecf20Sopenharmony_ci	if (pnetdev && (pnetdev->reg_state == NETREG_REGISTERED))
23028c2ecf20Sopenharmony_ci		unregister_netdev(pnetdev);
23038c2ecf20Sopenharmony_ci	if (nic->nicvf_rx_mode_wq) {
23048c2ecf20Sopenharmony_ci		destroy_workqueue(nic->nicvf_rx_mode_wq);
23058c2ecf20Sopenharmony_ci		nic->nicvf_rx_mode_wq = NULL;
23068c2ecf20Sopenharmony_ci	}
23078c2ecf20Sopenharmony_ci	nicvf_unregister_interrupts(nic);
23088c2ecf20Sopenharmony_ci	pci_set_drvdata(pdev, NULL);
23098c2ecf20Sopenharmony_ci	if (nic->drv_stats)
23108c2ecf20Sopenharmony_ci		free_percpu(nic->drv_stats);
23118c2ecf20Sopenharmony_ci	cavium_ptp_put(nic->ptp_clock);
23128c2ecf20Sopenharmony_ci	free_netdev(netdev);
23138c2ecf20Sopenharmony_ci	pci_release_regions(pdev);
23148c2ecf20Sopenharmony_ci	pci_disable_device(pdev);
23158c2ecf20Sopenharmony_ci}
23168c2ecf20Sopenharmony_ci
23178c2ecf20Sopenharmony_cistatic void nicvf_shutdown(struct pci_dev *pdev)
23188c2ecf20Sopenharmony_ci{
23198c2ecf20Sopenharmony_ci	nicvf_remove(pdev);
23208c2ecf20Sopenharmony_ci}
23218c2ecf20Sopenharmony_ci
23228c2ecf20Sopenharmony_cistatic struct pci_driver nicvf_driver = {
23238c2ecf20Sopenharmony_ci	.name = DRV_NAME,
23248c2ecf20Sopenharmony_ci	.id_table = nicvf_id_table,
23258c2ecf20Sopenharmony_ci	.probe = nicvf_probe,
23268c2ecf20Sopenharmony_ci	.remove = nicvf_remove,
23278c2ecf20Sopenharmony_ci	.shutdown = nicvf_shutdown,
23288c2ecf20Sopenharmony_ci};
23298c2ecf20Sopenharmony_ci
23308c2ecf20Sopenharmony_cistatic int __init nicvf_init_module(void)
23318c2ecf20Sopenharmony_ci{
23328c2ecf20Sopenharmony_ci	pr_info("%s, ver %s\n", DRV_NAME, DRV_VERSION);
23338c2ecf20Sopenharmony_ci	return pci_register_driver(&nicvf_driver);
23348c2ecf20Sopenharmony_ci}
23358c2ecf20Sopenharmony_ci
23368c2ecf20Sopenharmony_cistatic void __exit nicvf_cleanup_module(void)
23378c2ecf20Sopenharmony_ci{
23388c2ecf20Sopenharmony_ci	pci_unregister_driver(&nicvf_driver);
23398c2ecf20Sopenharmony_ci}
23408c2ecf20Sopenharmony_ci
23418c2ecf20Sopenharmony_cimodule_init(nicvf_init_module);
23428c2ecf20Sopenharmony_cimodule_exit(nicvf_cleanup_module);
2343