162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2015 Cavium, Inc.
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include <linux/module.h>
762306a36Sopenharmony_ci#include <linux/interrupt.h>
862306a36Sopenharmony_ci#include <linux/pci.h>
962306a36Sopenharmony_ci#include <linux/netdevice.h>
1062306a36Sopenharmony_ci#include <linux/if_vlan.h>
1162306a36Sopenharmony_ci#include <linux/etherdevice.h>
1262306a36Sopenharmony_ci#include <linux/ethtool.h>
1362306a36Sopenharmony_ci#include <linux/log2.h>
1462306a36Sopenharmony_ci#include <linux/prefetch.h>
1562306a36Sopenharmony_ci#include <linux/irq.h>
1662306a36Sopenharmony_ci#include <linux/iommu.h>
1762306a36Sopenharmony_ci#include <linux/bpf.h>
1862306a36Sopenharmony_ci#include <linux/bpf_trace.h>
1962306a36Sopenharmony_ci#include <linux/filter.h>
2062306a36Sopenharmony_ci#include <linux/net_tstamp.h>
2162306a36Sopenharmony_ci#include <linux/workqueue.h>
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci#include "nic_reg.h"
2462306a36Sopenharmony_ci#include "nic.h"
2562306a36Sopenharmony_ci#include "nicvf_queues.h"
2662306a36Sopenharmony_ci#include "thunder_bgx.h"
2762306a36Sopenharmony_ci#include "../common/cavium_ptp.h"
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci#define DRV_NAME	"nicvf"
3062306a36Sopenharmony_ci#define DRV_VERSION	"1.0"
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci/* NOTE: Packets bigger than 1530 are split across multiple pages and XDP needs
3362306a36Sopenharmony_ci * the buffer to be contiguous. Allow XDP to be set up only if we don't exceed
3462306a36Sopenharmony_ci * this value, keeping headroom for the 14 byte Ethernet header and two
3562306a36Sopenharmony_ci * VLAN tags (for QinQ)
3662306a36Sopenharmony_ci */
3762306a36Sopenharmony_ci#define MAX_XDP_MTU	(1530 - ETH_HLEN - VLAN_HLEN * 2)
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci/* Supported devices */
4062306a36Sopenharmony_cistatic const struct pci_device_id nicvf_id_table[] = {
4162306a36Sopenharmony_ci	{ PCI_DEVICE_SUB(PCI_VENDOR_ID_CAVIUM,
4262306a36Sopenharmony_ci			 PCI_DEVICE_ID_THUNDER_NIC_VF,
4362306a36Sopenharmony_ci			 PCI_VENDOR_ID_CAVIUM,
4462306a36Sopenharmony_ci			 PCI_SUBSYS_DEVID_88XX_NIC_VF) },
4562306a36Sopenharmony_ci	{ PCI_DEVICE_SUB(PCI_VENDOR_ID_CAVIUM,
4662306a36Sopenharmony_ci			 PCI_DEVICE_ID_THUNDER_PASS1_NIC_VF,
4762306a36Sopenharmony_ci			 PCI_VENDOR_ID_CAVIUM,
4862306a36Sopenharmony_ci			 PCI_SUBSYS_DEVID_88XX_PASS1_NIC_VF) },
4962306a36Sopenharmony_ci	{ PCI_DEVICE_SUB(PCI_VENDOR_ID_CAVIUM,
5062306a36Sopenharmony_ci			 PCI_DEVICE_ID_THUNDER_NIC_VF,
5162306a36Sopenharmony_ci			 PCI_VENDOR_ID_CAVIUM,
5262306a36Sopenharmony_ci			 PCI_SUBSYS_DEVID_81XX_NIC_VF) },
5362306a36Sopenharmony_ci	{ PCI_DEVICE_SUB(PCI_VENDOR_ID_CAVIUM,
5462306a36Sopenharmony_ci			 PCI_DEVICE_ID_THUNDER_NIC_VF,
5562306a36Sopenharmony_ci			 PCI_VENDOR_ID_CAVIUM,
5662306a36Sopenharmony_ci			 PCI_SUBSYS_DEVID_83XX_NIC_VF) },
5762306a36Sopenharmony_ci	{ 0, }  /* end of table */
5862306a36Sopenharmony_ci};
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ciMODULE_AUTHOR("Sunil Goutham");
6162306a36Sopenharmony_ciMODULE_DESCRIPTION("Cavium Thunder NIC Virtual Function Driver");
6262306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
6362306a36Sopenharmony_ciMODULE_VERSION(DRV_VERSION);
6462306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, nicvf_id_table);
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_cistatic int debug = 0x00;
6762306a36Sopenharmony_cimodule_param(debug, int, 0644);
6862306a36Sopenharmony_ciMODULE_PARM_DESC(debug, "Debug message level bitmap");
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_cistatic int cpi_alg = CPI_ALG_NONE;
7162306a36Sopenharmony_cimodule_param(cpi_alg, int, 0444);
7262306a36Sopenharmony_ciMODULE_PARM_DESC(cpi_alg,
7362306a36Sopenharmony_ci		 "PFC algorithm (0=none, 1=VLAN, 2=VLAN16, 3=IP Diffserv)");
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_cistatic inline u8 nicvf_netdev_qidx(struct nicvf *nic, u8 qidx)
7662306a36Sopenharmony_ci{
7762306a36Sopenharmony_ci	if (nic->sqs_mode)
7862306a36Sopenharmony_ci		return qidx + ((nic->sqs_id + 1) * MAX_CMP_QUEUES_PER_QS);
7962306a36Sopenharmony_ci	else
8062306a36Sopenharmony_ci		return qidx;
8162306a36Sopenharmony_ci}
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci/* The Cavium ThunderX network controller can *only* be found in SoCs
8462306a36Sopenharmony_ci * containing the ThunderX ARM64 CPU implementation.  All accesses to the device
8562306a36Sopenharmony_ci * registers on this platform are implicitly strongly ordered with respect
8662306a36Sopenharmony_ci * to memory accesses. So writeq_relaxed() and readq_relaxed() are safe to use
8762306a36Sopenharmony_ci * with no memory barriers in this driver.  The readq()/writeq() functions add
8862306a36Sopenharmony_ci * explicit ordering operation which in this case are redundant, and only
8962306a36Sopenharmony_ci * add overhead.
9062306a36Sopenharmony_ci */
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci/* Register read/write APIs */
9362306a36Sopenharmony_civoid nicvf_reg_write(struct nicvf *nic, u64 offset, u64 val)
9462306a36Sopenharmony_ci{
9562306a36Sopenharmony_ci	writeq_relaxed(val, nic->reg_base + offset);
9662306a36Sopenharmony_ci}
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ciu64 nicvf_reg_read(struct nicvf *nic, u64 offset)
9962306a36Sopenharmony_ci{
10062306a36Sopenharmony_ci	return readq_relaxed(nic->reg_base + offset);
10162306a36Sopenharmony_ci}
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_civoid nicvf_queue_reg_write(struct nicvf *nic, u64 offset,
10462306a36Sopenharmony_ci			   u64 qidx, u64 val)
10562306a36Sopenharmony_ci{
10662306a36Sopenharmony_ci	void __iomem *addr = nic->reg_base + offset;
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	writeq_relaxed(val, addr + (qidx << NIC_Q_NUM_SHIFT));
10962306a36Sopenharmony_ci}
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ciu64 nicvf_queue_reg_read(struct nicvf *nic, u64 offset, u64 qidx)
11262306a36Sopenharmony_ci{
11362306a36Sopenharmony_ci	void __iomem *addr = nic->reg_base + offset;
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	return readq_relaxed(addr + (qidx << NIC_Q_NUM_SHIFT));
11662306a36Sopenharmony_ci}
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci/* VF -> PF mailbox communication */
11962306a36Sopenharmony_cistatic void nicvf_write_to_mbx(struct nicvf *nic, union nic_mbx *mbx)
12062306a36Sopenharmony_ci{
12162306a36Sopenharmony_ci	u64 *msg = (u64 *)mbx;
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	nicvf_reg_write(nic, NIC_VF_PF_MAILBOX_0_1 + 0, msg[0]);
12462306a36Sopenharmony_ci	nicvf_reg_write(nic, NIC_VF_PF_MAILBOX_0_1 + 8, msg[1]);
12562306a36Sopenharmony_ci}
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ciint nicvf_send_msg_to_pf(struct nicvf *nic, union nic_mbx *mbx)
12862306a36Sopenharmony_ci{
12962306a36Sopenharmony_ci	unsigned long timeout;
13062306a36Sopenharmony_ci	int ret = 0;
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	mutex_lock(&nic->rx_mode_mtx);
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	nic->pf_acked = false;
13562306a36Sopenharmony_ci	nic->pf_nacked = false;
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	nicvf_write_to_mbx(nic, mbx);
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	timeout = jiffies + msecs_to_jiffies(NIC_MBOX_MSG_TIMEOUT);
14062306a36Sopenharmony_ci	/* Wait for previous message to be acked, timeout 2sec */
14162306a36Sopenharmony_ci	while (!nic->pf_acked) {
14262306a36Sopenharmony_ci		if (nic->pf_nacked) {
14362306a36Sopenharmony_ci			netdev_err(nic->netdev,
14462306a36Sopenharmony_ci				   "PF NACK to mbox msg 0x%02x from VF%d\n",
14562306a36Sopenharmony_ci				   (mbx->msg.msg & 0xFF), nic->vf_id);
14662306a36Sopenharmony_ci			ret = -EINVAL;
14762306a36Sopenharmony_ci			break;
14862306a36Sopenharmony_ci		}
14962306a36Sopenharmony_ci		usleep_range(8000, 10000);
15062306a36Sopenharmony_ci		if (nic->pf_acked)
15162306a36Sopenharmony_ci			break;
15262306a36Sopenharmony_ci		if (time_after(jiffies, timeout)) {
15362306a36Sopenharmony_ci			netdev_err(nic->netdev,
15462306a36Sopenharmony_ci				   "PF didn't ACK to mbox msg 0x%02x from VF%d\n",
15562306a36Sopenharmony_ci				   (mbx->msg.msg & 0xFF), nic->vf_id);
15662306a36Sopenharmony_ci			ret = -EBUSY;
15762306a36Sopenharmony_ci			break;
15862306a36Sopenharmony_ci		}
15962306a36Sopenharmony_ci	}
16062306a36Sopenharmony_ci	mutex_unlock(&nic->rx_mode_mtx);
16162306a36Sopenharmony_ci	return ret;
16262306a36Sopenharmony_ci}
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci/* Checks if VF is able to comminicate with PF
16562306a36Sopenharmony_ci* and also gets the VNIC number this VF is associated to.
16662306a36Sopenharmony_ci*/
16762306a36Sopenharmony_cistatic int nicvf_check_pf_ready(struct nicvf *nic)
16862306a36Sopenharmony_ci{
16962306a36Sopenharmony_ci	union nic_mbx mbx = {};
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	mbx.msg.msg = NIC_MBOX_MSG_READY;
17262306a36Sopenharmony_ci	if (nicvf_send_msg_to_pf(nic, &mbx)) {
17362306a36Sopenharmony_ci		netdev_err(nic->netdev,
17462306a36Sopenharmony_ci			   "PF didn't respond to READY msg\n");
17562306a36Sopenharmony_ci		return 0;
17662306a36Sopenharmony_ci	}
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	return 1;
17962306a36Sopenharmony_ci}
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_cistatic void nicvf_send_cfg_done(struct nicvf *nic)
18262306a36Sopenharmony_ci{
18362306a36Sopenharmony_ci	union nic_mbx mbx = {};
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	mbx.msg.msg = NIC_MBOX_MSG_CFG_DONE;
18662306a36Sopenharmony_ci	if (nicvf_send_msg_to_pf(nic, &mbx)) {
18762306a36Sopenharmony_ci		netdev_err(nic->netdev,
18862306a36Sopenharmony_ci			   "PF didn't respond to CFG DONE msg\n");
18962306a36Sopenharmony_ci	}
19062306a36Sopenharmony_ci}
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_cistatic void nicvf_read_bgx_stats(struct nicvf *nic, struct bgx_stats_msg *bgx)
19362306a36Sopenharmony_ci{
19462306a36Sopenharmony_ci	if (bgx->rx)
19562306a36Sopenharmony_ci		nic->bgx_stats.rx_stats[bgx->idx] = bgx->stats;
19662306a36Sopenharmony_ci	else
19762306a36Sopenharmony_ci		nic->bgx_stats.tx_stats[bgx->idx] = bgx->stats;
19862306a36Sopenharmony_ci}
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_cistatic void  nicvf_handle_mbx_intr(struct nicvf *nic)
20162306a36Sopenharmony_ci{
20262306a36Sopenharmony_ci	union nic_mbx mbx = {};
20362306a36Sopenharmony_ci	u64 *mbx_data;
20462306a36Sopenharmony_ci	u64 mbx_addr;
20562306a36Sopenharmony_ci	int i;
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci	mbx_addr = NIC_VF_PF_MAILBOX_0_1;
20862306a36Sopenharmony_ci	mbx_data = (u64 *)&mbx;
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	for (i = 0; i < NIC_PF_VF_MAILBOX_SIZE; i++) {
21162306a36Sopenharmony_ci		*mbx_data = nicvf_reg_read(nic, mbx_addr);
21262306a36Sopenharmony_ci		mbx_data++;
21362306a36Sopenharmony_ci		mbx_addr += sizeof(u64);
21462306a36Sopenharmony_ci	}
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	netdev_dbg(nic->netdev, "Mbox message: msg: 0x%x\n", mbx.msg.msg);
21762306a36Sopenharmony_ci	switch (mbx.msg.msg) {
21862306a36Sopenharmony_ci	case NIC_MBOX_MSG_READY:
21962306a36Sopenharmony_ci		nic->pf_acked = true;
22062306a36Sopenharmony_ci		nic->vf_id = mbx.nic_cfg.vf_id & 0x7F;
22162306a36Sopenharmony_ci		nic->tns_mode = mbx.nic_cfg.tns_mode & 0x7F;
22262306a36Sopenharmony_ci		nic->node = mbx.nic_cfg.node_id;
22362306a36Sopenharmony_ci		if (!nic->set_mac_pending)
22462306a36Sopenharmony_ci			eth_hw_addr_set(nic->netdev, mbx.nic_cfg.mac_addr);
22562306a36Sopenharmony_ci		nic->sqs_mode = mbx.nic_cfg.sqs_mode;
22662306a36Sopenharmony_ci		nic->loopback_supported = mbx.nic_cfg.loopback_supported;
22762306a36Sopenharmony_ci		nic->link_up = false;
22862306a36Sopenharmony_ci		nic->duplex = 0;
22962306a36Sopenharmony_ci		nic->speed = 0;
23062306a36Sopenharmony_ci		break;
23162306a36Sopenharmony_ci	case NIC_MBOX_MSG_ACK:
23262306a36Sopenharmony_ci		nic->pf_acked = true;
23362306a36Sopenharmony_ci		break;
23462306a36Sopenharmony_ci	case NIC_MBOX_MSG_NACK:
23562306a36Sopenharmony_ci		nic->pf_nacked = true;
23662306a36Sopenharmony_ci		break;
23762306a36Sopenharmony_ci	case NIC_MBOX_MSG_RSS_SIZE:
23862306a36Sopenharmony_ci		nic->rss_info.rss_size = mbx.rss_size.ind_tbl_size;
23962306a36Sopenharmony_ci		nic->pf_acked = true;
24062306a36Sopenharmony_ci		break;
24162306a36Sopenharmony_ci	case NIC_MBOX_MSG_BGX_STATS:
24262306a36Sopenharmony_ci		nicvf_read_bgx_stats(nic, &mbx.bgx_stats);
24362306a36Sopenharmony_ci		nic->pf_acked = true;
24462306a36Sopenharmony_ci		break;
24562306a36Sopenharmony_ci	case NIC_MBOX_MSG_BGX_LINK_CHANGE:
24662306a36Sopenharmony_ci		nic->pf_acked = true;
24762306a36Sopenharmony_ci		if (nic->link_up != mbx.link_status.link_up) {
24862306a36Sopenharmony_ci			nic->link_up = mbx.link_status.link_up;
24962306a36Sopenharmony_ci			nic->duplex = mbx.link_status.duplex;
25062306a36Sopenharmony_ci			nic->speed = mbx.link_status.speed;
25162306a36Sopenharmony_ci			nic->mac_type = mbx.link_status.mac_type;
25262306a36Sopenharmony_ci			if (nic->link_up) {
25362306a36Sopenharmony_ci				netdev_info(nic->netdev,
25462306a36Sopenharmony_ci					    "Link is Up %d Mbps %s duplex\n",
25562306a36Sopenharmony_ci					    nic->speed,
25662306a36Sopenharmony_ci					    nic->duplex == DUPLEX_FULL ?
25762306a36Sopenharmony_ci					    "Full" : "Half");
25862306a36Sopenharmony_ci				netif_carrier_on(nic->netdev);
25962306a36Sopenharmony_ci				netif_tx_start_all_queues(nic->netdev);
26062306a36Sopenharmony_ci			} else {
26162306a36Sopenharmony_ci				netdev_info(nic->netdev, "Link is Down\n");
26262306a36Sopenharmony_ci				netif_carrier_off(nic->netdev);
26362306a36Sopenharmony_ci				netif_tx_stop_all_queues(nic->netdev);
26462306a36Sopenharmony_ci			}
26562306a36Sopenharmony_ci		}
26662306a36Sopenharmony_ci		break;
26762306a36Sopenharmony_ci	case NIC_MBOX_MSG_ALLOC_SQS:
26862306a36Sopenharmony_ci		nic->sqs_count = mbx.sqs_alloc.qs_count;
26962306a36Sopenharmony_ci		nic->pf_acked = true;
27062306a36Sopenharmony_ci		break;
27162306a36Sopenharmony_ci	case NIC_MBOX_MSG_SNICVF_PTR:
27262306a36Sopenharmony_ci		/* Primary VF: make note of secondary VF's pointer
27362306a36Sopenharmony_ci		 * to be used while packet transmission.
27462306a36Sopenharmony_ci		 */
27562306a36Sopenharmony_ci		nic->snicvf[mbx.nicvf.sqs_id] =
27662306a36Sopenharmony_ci			(struct nicvf *)mbx.nicvf.nicvf;
27762306a36Sopenharmony_ci		nic->pf_acked = true;
27862306a36Sopenharmony_ci		break;
27962306a36Sopenharmony_ci	case NIC_MBOX_MSG_PNICVF_PTR:
28062306a36Sopenharmony_ci		/* Secondary VF/Qset: make note of primary VF's pointer
28162306a36Sopenharmony_ci		 * to be used while packet reception, to handover packet
28262306a36Sopenharmony_ci		 * to primary VF's netdev.
28362306a36Sopenharmony_ci		 */
28462306a36Sopenharmony_ci		nic->pnicvf = (struct nicvf *)mbx.nicvf.nicvf;
28562306a36Sopenharmony_ci		nic->pf_acked = true;
28662306a36Sopenharmony_ci		break;
28762306a36Sopenharmony_ci	case NIC_MBOX_MSG_PFC:
28862306a36Sopenharmony_ci		nic->pfc.autoneg = mbx.pfc.autoneg;
28962306a36Sopenharmony_ci		nic->pfc.fc_rx = mbx.pfc.fc_rx;
29062306a36Sopenharmony_ci		nic->pfc.fc_tx = mbx.pfc.fc_tx;
29162306a36Sopenharmony_ci		nic->pf_acked = true;
29262306a36Sopenharmony_ci		break;
29362306a36Sopenharmony_ci	default:
29462306a36Sopenharmony_ci		netdev_err(nic->netdev,
29562306a36Sopenharmony_ci			   "Invalid message from PF, msg 0x%x\n", mbx.msg.msg);
29662306a36Sopenharmony_ci		break;
29762306a36Sopenharmony_ci	}
29862306a36Sopenharmony_ci	nicvf_clear_intr(nic, NICVF_INTR_MBOX, 0);
29962306a36Sopenharmony_ci}
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_cistatic int nicvf_hw_set_mac_addr(struct nicvf *nic, struct net_device *netdev)
30262306a36Sopenharmony_ci{
30362306a36Sopenharmony_ci	union nic_mbx mbx = {};
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci	mbx.mac.msg = NIC_MBOX_MSG_SET_MAC;
30662306a36Sopenharmony_ci	mbx.mac.vf_id = nic->vf_id;
30762306a36Sopenharmony_ci	ether_addr_copy(mbx.mac.mac_addr, netdev->dev_addr);
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci	return nicvf_send_msg_to_pf(nic, &mbx);
31062306a36Sopenharmony_ci}
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_cistatic void nicvf_config_cpi(struct nicvf *nic)
31362306a36Sopenharmony_ci{
31462306a36Sopenharmony_ci	union nic_mbx mbx = {};
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	mbx.cpi_cfg.msg = NIC_MBOX_MSG_CPI_CFG;
31762306a36Sopenharmony_ci	mbx.cpi_cfg.vf_id = nic->vf_id;
31862306a36Sopenharmony_ci	mbx.cpi_cfg.cpi_alg = nic->cpi_alg;
31962306a36Sopenharmony_ci	mbx.cpi_cfg.rq_cnt = nic->qs->rq_cnt;
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ci	nicvf_send_msg_to_pf(nic, &mbx);
32262306a36Sopenharmony_ci}
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_cistatic void nicvf_get_rss_size(struct nicvf *nic)
32562306a36Sopenharmony_ci{
32662306a36Sopenharmony_ci	union nic_mbx mbx = {};
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci	mbx.rss_size.msg = NIC_MBOX_MSG_RSS_SIZE;
32962306a36Sopenharmony_ci	mbx.rss_size.vf_id = nic->vf_id;
33062306a36Sopenharmony_ci	nicvf_send_msg_to_pf(nic, &mbx);
33162306a36Sopenharmony_ci}
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_civoid nicvf_config_rss(struct nicvf *nic)
33462306a36Sopenharmony_ci{
33562306a36Sopenharmony_ci	union nic_mbx mbx = {};
33662306a36Sopenharmony_ci	struct nicvf_rss_info *rss = &nic->rss_info;
33762306a36Sopenharmony_ci	int ind_tbl_len = rss->rss_size;
33862306a36Sopenharmony_ci	int i, nextq = 0;
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	mbx.rss_cfg.vf_id = nic->vf_id;
34162306a36Sopenharmony_ci	mbx.rss_cfg.hash_bits = rss->hash_bits;
34262306a36Sopenharmony_ci	while (ind_tbl_len) {
34362306a36Sopenharmony_ci		mbx.rss_cfg.tbl_offset = nextq;
34462306a36Sopenharmony_ci		mbx.rss_cfg.tbl_len = min(ind_tbl_len,
34562306a36Sopenharmony_ci					       RSS_IND_TBL_LEN_PER_MBX_MSG);
34662306a36Sopenharmony_ci		mbx.rss_cfg.msg = mbx.rss_cfg.tbl_offset ?
34762306a36Sopenharmony_ci			  NIC_MBOX_MSG_RSS_CFG_CONT : NIC_MBOX_MSG_RSS_CFG;
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci		for (i = 0; i < mbx.rss_cfg.tbl_len; i++)
35062306a36Sopenharmony_ci			mbx.rss_cfg.ind_tbl[i] = rss->ind_tbl[nextq++];
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci		nicvf_send_msg_to_pf(nic, &mbx);
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci		ind_tbl_len -= mbx.rss_cfg.tbl_len;
35562306a36Sopenharmony_ci	}
35662306a36Sopenharmony_ci}
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_civoid nicvf_set_rss_key(struct nicvf *nic)
35962306a36Sopenharmony_ci{
36062306a36Sopenharmony_ci	struct nicvf_rss_info *rss = &nic->rss_info;
36162306a36Sopenharmony_ci	u64 key_addr = NIC_VNIC_RSS_KEY_0_4;
36262306a36Sopenharmony_ci	int idx;
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci	for (idx = 0; idx < RSS_HASH_KEY_SIZE; idx++) {
36562306a36Sopenharmony_ci		nicvf_reg_write(nic, key_addr, rss->key[idx]);
36662306a36Sopenharmony_ci		key_addr += sizeof(u64);
36762306a36Sopenharmony_ci	}
36862306a36Sopenharmony_ci}
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_cistatic int nicvf_rss_init(struct nicvf *nic)
37162306a36Sopenharmony_ci{
37262306a36Sopenharmony_ci	struct nicvf_rss_info *rss = &nic->rss_info;
37362306a36Sopenharmony_ci	int idx;
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci	nicvf_get_rss_size(nic);
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci	if (cpi_alg != CPI_ALG_NONE) {
37862306a36Sopenharmony_ci		rss->enable = false;
37962306a36Sopenharmony_ci		rss->hash_bits = 0;
38062306a36Sopenharmony_ci		return 0;
38162306a36Sopenharmony_ci	}
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci	rss->enable = true;
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci	netdev_rss_key_fill(rss->key, RSS_HASH_KEY_SIZE * sizeof(u64));
38662306a36Sopenharmony_ci	nicvf_set_rss_key(nic);
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci	rss->cfg = RSS_IP_HASH_ENA | RSS_TCP_HASH_ENA | RSS_UDP_HASH_ENA;
38962306a36Sopenharmony_ci	nicvf_reg_write(nic, NIC_VNIC_RSS_CFG, rss->cfg);
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci	rss->hash_bits =  ilog2(rounddown_pow_of_two(rss->rss_size));
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci	for (idx = 0; idx < rss->rss_size; idx++)
39462306a36Sopenharmony_ci		rss->ind_tbl[idx] = ethtool_rxfh_indir_default(idx,
39562306a36Sopenharmony_ci							       nic->rx_queues);
39662306a36Sopenharmony_ci	nicvf_config_rss(nic);
39762306a36Sopenharmony_ci	return 1;
39862306a36Sopenharmony_ci}
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ci/* Request PF to allocate additional Qsets */
40162306a36Sopenharmony_cistatic void nicvf_request_sqs(struct nicvf *nic)
40262306a36Sopenharmony_ci{
40362306a36Sopenharmony_ci	union nic_mbx mbx = {};
40462306a36Sopenharmony_ci	int sqs;
40562306a36Sopenharmony_ci	int sqs_count = nic->sqs_count;
40662306a36Sopenharmony_ci	int rx_queues = 0, tx_queues = 0;
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci	/* Only primary VF should request */
40962306a36Sopenharmony_ci	if (nic->sqs_mode ||  !nic->sqs_count)
41062306a36Sopenharmony_ci		return;
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_ci	mbx.sqs_alloc.msg = NIC_MBOX_MSG_ALLOC_SQS;
41362306a36Sopenharmony_ci	mbx.sqs_alloc.vf_id = nic->vf_id;
41462306a36Sopenharmony_ci	mbx.sqs_alloc.qs_count = nic->sqs_count;
41562306a36Sopenharmony_ci	if (nicvf_send_msg_to_pf(nic, &mbx)) {
41662306a36Sopenharmony_ci		/* No response from PF */
41762306a36Sopenharmony_ci		nic->sqs_count = 0;
41862306a36Sopenharmony_ci		return;
41962306a36Sopenharmony_ci	}
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ci	/* Return if no Secondary Qsets available */
42262306a36Sopenharmony_ci	if (!nic->sqs_count)
42362306a36Sopenharmony_ci		return;
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci	if (nic->rx_queues > MAX_RCV_QUEUES_PER_QS)
42662306a36Sopenharmony_ci		rx_queues = nic->rx_queues - MAX_RCV_QUEUES_PER_QS;
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci	tx_queues = nic->tx_queues + nic->xdp_tx_queues;
42962306a36Sopenharmony_ci	if (tx_queues > MAX_SND_QUEUES_PER_QS)
43062306a36Sopenharmony_ci		tx_queues = tx_queues - MAX_SND_QUEUES_PER_QS;
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci	/* Set no of Rx/Tx queues in each of the SQsets */
43362306a36Sopenharmony_ci	for (sqs = 0; sqs < nic->sqs_count; sqs++) {
43462306a36Sopenharmony_ci		mbx.nicvf.msg = NIC_MBOX_MSG_SNICVF_PTR;
43562306a36Sopenharmony_ci		mbx.nicvf.vf_id = nic->vf_id;
43662306a36Sopenharmony_ci		mbx.nicvf.sqs_id = sqs;
43762306a36Sopenharmony_ci		nicvf_send_msg_to_pf(nic, &mbx);
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci		nic->snicvf[sqs]->sqs_id = sqs;
44062306a36Sopenharmony_ci		if (rx_queues > MAX_RCV_QUEUES_PER_QS) {
44162306a36Sopenharmony_ci			nic->snicvf[sqs]->qs->rq_cnt = MAX_RCV_QUEUES_PER_QS;
44262306a36Sopenharmony_ci			rx_queues -= MAX_RCV_QUEUES_PER_QS;
44362306a36Sopenharmony_ci		} else {
44462306a36Sopenharmony_ci			nic->snicvf[sqs]->qs->rq_cnt = rx_queues;
44562306a36Sopenharmony_ci			rx_queues = 0;
44662306a36Sopenharmony_ci		}
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci		if (tx_queues > MAX_SND_QUEUES_PER_QS) {
44962306a36Sopenharmony_ci			nic->snicvf[sqs]->qs->sq_cnt = MAX_SND_QUEUES_PER_QS;
45062306a36Sopenharmony_ci			tx_queues -= MAX_SND_QUEUES_PER_QS;
45162306a36Sopenharmony_ci		} else {
45262306a36Sopenharmony_ci			nic->snicvf[sqs]->qs->sq_cnt = tx_queues;
45362306a36Sopenharmony_ci			tx_queues = 0;
45462306a36Sopenharmony_ci		}
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci		nic->snicvf[sqs]->qs->cq_cnt =
45762306a36Sopenharmony_ci		max(nic->snicvf[sqs]->qs->rq_cnt, nic->snicvf[sqs]->qs->sq_cnt);
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci		/* Initialize secondary Qset's queues and its interrupts */
46062306a36Sopenharmony_ci		nicvf_open(nic->snicvf[sqs]->netdev);
46162306a36Sopenharmony_ci	}
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_ci	/* Update stack with actual Rx/Tx queue count allocated */
46462306a36Sopenharmony_ci	if (sqs_count != nic->sqs_count)
46562306a36Sopenharmony_ci		nicvf_set_real_num_queues(nic->netdev,
46662306a36Sopenharmony_ci					  nic->tx_queues, nic->rx_queues);
46762306a36Sopenharmony_ci}
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci/* Send this Qset's nicvf pointer to PF.
47062306a36Sopenharmony_ci * PF inturn sends primary VF's nicvf struct to secondary Qsets/VFs
47162306a36Sopenharmony_ci * so that packets received by these Qsets can use primary VF's netdev
47262306a36Sopenharmony_ci */
47362306a36Sopenharmony_cistatic void nicvf_send_vf_struct(struct nicvf *nic)
47462306a36Sopenharmony_ci{
47562306a36Sopenharmony_ci	union nic_mbx mbx = {};
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_ci	mbx.nicvf.msg = NIC_MBOX_MSG_NICVF_PTR;
47862306a36Sopenharmony_ci	mbx.nicvf.sqs_mode = nic->sqs_mode;
47962306a36Sopenharmony_ci	mbx.nicvf.nicvf = (u64)nic;
48062306a36Sopenharmony_ci	nicvf_send_msg_to_pf(nic, &mbx);
48162306a36Sopenharmony_ci}
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_cistatic void nicvf_get_primary_vf_struct(struct nicvf *nic)
48462306a36Sopenharmony_ci{
48562306a36Sopenharmony_ci	union nic_mbx mbx = {};
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci	mbx.nicvf.msg = NIC_MBOX_MSG_PNICVF_PTR;
48862306a36Sopenharmony_ci	nicvf_send_msg_to_pf(nic, &mbx);
48962306a36Sopenharmony_ci}
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_ciint nicvf_set_real_num_queues(struct net_device *netdev,
49262306a36Sopenharmony_ci			      int tx_queues, int rx_queues)
49362306a36Sopenharmony_ci{
49462306a36Sopenharmony_ci	int err = 0;
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci	err = netif_set_real_num_tx_queues(netdev, tx_queues);
49762306a36Sopenharmony_ci	if (err) {
49862306a36Sopenharmony_ci		netdev_err(netdev,
49962306a36Sopenharmony_ci			   "Failed to set no of Tx queues: %d\n", tx_queues);
50062306a36Sopenharmony_ci		return err;
50162306a36Sopenharmony_ci	}
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci	err = netif_set_real_num_rx_queues(netdev, rx_queues);
50462306a36Sopenharmony_ci	if (err)
50562306a36Sopenharmony_ci		netdev_err(netdev,
50662306a36Sopenharmony_ci			   "Failed to set no of Rx queues: %d\n", rx_queues);
50762306a36Sopenharmony_ci	return err;
50862306a36Sopenharmony_ci}
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_cistatic int nicvf_init_resources(struct nicvf *nic)
51162306a36Sopenharmony_ci{
51262306a36Sopenharmony_ci	int err;
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci	/* Enable Qset */
51562306a36Sopenharmony_ci	nicvf_qset_config(nic, true);
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci	/* Initialize queues and HW for data transfer */
51862306a36Sopenharmony_ci	err = nicvf_config_data_transfer(nic, true);
51962306a36Sopenharmony_ci	if (err) {
52062306a36Sopenharmony_ci		netdev_err(nic->netdev,
52162306a36Sopenharmony_ci			   "Failed to alloc/config VF's QSet resources\n");
52262306a36Sopenharmony_ci		return err;
52362306a36Sopenharmony_ci	}
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ci	return 0;
52662306a36Sopenharmony_ci}
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_cistatic inline bool nicvf_xdp_rx(struct nicvf *nic, struct bpf_prog *prog,
52962306a36Sopenharmony_ci				struct cqe_rx_t *cqe_rx, struct snd_queue *sq,
53062306a36Sopenharmony_ci				struct rcv_queue *rq, struct sk_buff **skb)
53162306a36Sopenharmony_ci{
53262306a36Sopenharmony_ci	unsigned char *hard_start, *data;
53362306a36Sopenharmony_ci	struct xdp_buff xdp;
53462306a36Sopenharmony_ci	struct page *page;
53562306a36Sopenharmony_ci	u32 action;
53662306a36Sopenharmony_ci	u16 len, offset = 0;
53762306a36Sopenharmony_ci	u64 dma_addr, cpu_addr;
53862306a36Sopenharmony_ci	void *orig_data;
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci	/* Retrieve packet buffer's DMA address and length */
54162306a36Sopenharmony_ci	len = *((u16 *)((void *)cqe_rx + (3 * sizeof(u64))));
54262306a36Sopenharmony_ci	dma_addr = *((u64 *)((void *)cqe_rx + (7 * sizeof(u64))));
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci	cpu_addr = nicvf_iova_to_phys(nic, dma_addr);
54562306a36Sopenharmony_ci	if (!cpu_addr)
54662306a36Sopenharmony_ci		return false;
54762306a36Sopenharmony_ci	cpu_addr = (u64)phys_to_virt(cpu_addr);
54862306a36Sopenharmony_ci	page = virt_to_page((void *)cpu_addr);
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ci	xdp_init_buff(&xdp, RCV_FRAG_LEN + XDP_PACKET_HEADROOM,
55162306a36Sopenharmony_ci		      &rq->xdp_rxq);
55262306a36Sopenharmony_ci	hard_start = page_address(page);
55362306a36Sopenharmony_ci	data = (unsigned char *)cpu_addr;
55462306a36Sopenharmony_ci	xdp_prepare_buff(&xdp, hard_start, data - hard_start, len, false);
55562306a36Sopenharmony_ci	orig_data = xdp.data;
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_ci	action = bpf_prog_run_xdp(prog, &xdp);
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci	len = xdp.data_end - xdp.data;
56062306a36Sopenharmony_ci	/* Check if XDP program has changed headers */
56162306a36Sopenharmony_ci	if (orig_data != xdp.data) {
56262306a36Sopenharmony_ci		offset = orig_data - xdp.data;
56362306a36Sopenharmony_ci		dma_addr -= offset;
56462306a36Sopenharmony_ci	}
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_ci	switch (action) {
56762306a36Sopenharmony_ci	case XDP_PASS:
56862306a36Sopenharmony_ci		/* Check if it's a recycled page, if not
56962306a36Sopenharmony_ci		 * unmap the DMA mapping.
57062306a36Sopenharmony_ci		 *
57162306a36Sopenharmony_ci		 * Recycled page holds an extra reference.
57262306a36Sopenharmony_ci		 */
57362306a36Sopenharmony_ci		if (page_ref_count(page) == 1) {
57462306a36Sopenharmony_ci			dma_addr &= PAGE_MASK;
57562306a36Sopenharmony_ci			dma_unmap_page_attrs(&nic->pdev->dev, dma_addr,
57662306a36Sopenharmony_ci					     RCV_FRAG_LEN + XDP_PACKET_HEADROOM,
57762306a36Sopenharmony_ci					     DMA_FROM_DEVICE,
57862306a36Sopenharmony_ci					     DMA_ATTR_SKIP_CPU_SYNC);
57962306a36Sopenharmony_ci		}
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_ci		/* Build SKB and pass on packet to network stack */
58262306a36Sopenharmony_ci		*skb = build_skb(xdp.data,
58362306a36Sopenharmony_ci				 RCV_FRAG_LEN - cqe_rx->align_pad + offset);
58462306a36Sopenharmony_ci		if (!*skb)
58562306a36Sopenharmony_ci			put_page(page);
58662306a36Sopenharmony_ci		else
58762306a36Sopenharmony_ci			skb_put(*skb, len);
58862306a36Sopenharmony_ci		return false;
58962306a36Sopenharmony_ci	case XDP_TX:
59062306a36Sopenharmony_ci		nicvf_xdp_sq_append_pkt(nic, sq, (u64)xdp.data, dma_addr, len);
59162306a36Sopenharmony_ci		return true;
59262306a36Sopenharmony_ci	default:
59362306a36Sopenharmony_ci		bpf_warn_invalid_xdp_action(nic->netdev, prog, action);
59462306a36Sopenharmony_ci		fallthrough;
59562306a36Sopenharmony_ci	case XDP_ABORTED:
59662306a36Sopenharmony_ci		trace_xdp_exception(nic->netdev, prog, action);
59762306a36Sopenharmony_ci		fallthrough;
59862306a36Sopenharmony_ci	case XDP_DROP:
59962306a36Sopenharmony_ci		/* Check if it's a recycled page, if not
60062306a36Sopenharmony_ci		 * unmap the DMA mapping.
60162306a36Sopenharmony_ci		 *
60262306a36Sopenharmony_ci		 * Recycled page holds an extra reference.
60362306a36Sopenharmony_ci		 */
60462306a36Sopenharmony_ci		if (page_ref_count(page) == 1) {
60562306a36Sopenharmony_ci			dma_addr &= PAGE_MASK;
60662306a36Sopenharmony_ci			dma_unmap_page_attrs(&nic->pdev->dev, dma_addr,
60762306a36Sopenharmony_ci					     RCV_FRAG_LEN + XDP_PACKET_HEADROOM,
60862306a36Sopenharmony_ci					     DMA_FROM_DEVICE,
60962306a36Sopenharmony_ci					     DMA_ATTR_SKIP_CPU_SYNC);
61062306a36Sopenharmony_ci		}
61162306a36Sopenharmony_ci		put_page(page);
61262306a36Sopenharmony_ci		return true;
61362306a36Sopenharmony_ci	}
61462306a36Sopenharmony_ci	return false;
61562306a36Sopenharmony_ci}
61662306a36Sopenharmony_ci
61762306a36Sopenharmony_cistatic void nicvf_snd_ptp_handler(struct net_device *netdev,
61862306a36Sopenharmony_ci				  struct cqe_send_t *cqe_tx)
61962306a36Sopenharmony_ci{
62062306a36Sopenharmony_ci	struct nicvf *nic = netdev_priv(netdev);
62162306a36Sopenharmony_ci	struct skb_shared_hwtstamps ts;
62262306a36Sopenharmony_ci	u64 ns;
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_ci	nic = nic->pnicvf;
62562306a36Sopenharmony_ci
62662306a36Sopenharmony_ci	/* Sync for 'ptp_skb' */
62762306a36Sopenharmony_ci	smp_rmb();
62862306a36Sopenharmony_ci
62962306a36Sopenharmony_ci	/* New timestamp request can be queued now */
63062306a36Sopenharmony_ci	atomic_set(&nic->tx_ptp_skbs, 0);
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_ci	/* Check for timestamp requested skb */
63362306a36Sopenharmony_ci	if (!nic->ptp_skb)
63462306a36Sopenharmony_ci		return;
63562306a36Sopenharmony_ci
63662306a36Sopenharmony_ci	/* Check if timestamping is timedout, which is set to 10us */
63762306a36Sopenharmony_ci	if (cqe_tx->send_status == CQ_TX_ERROP_TSTMP_TIMEOUT ||
63862306a36Sopenharmony_ci	    cqe_tx->send_status == CQ_TX_ERROP_TSTMP_CONFLICT)
63962306a36Sopenharmony_ci		goto no_tstamp;
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_ci	/* Get the timestamp */
64262306a36Sopenharmony_ci	memset(&ts, 0, sizeof(ts));
64362306a36Sopenharmony_ci	ns = cavium_ptp_tstamp2time(nic->ptp_clock, cqe_tx->ptp_timestamp);
64462306a36Sopenharmony_ci	ts.hwtstamp = ns_to_ktime(ns);
64562306a36Sopenharmony_ci	skb_tstamp_tx(nic->ptp_skb, &ts);
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_cino_tstamp:
64862306a36Sopenharmony_ci	/* Free the original skb */
64962306a36Sopenharmony_ci	dev_kfree_skb_any(nic->ptp_skb);
65062306a36Sopenharmony_ci	nic->ptp_skb = NULL;
65162306a36Sopenharmony_ci	/* Sync 'ptp_skb' */
65262306a36Sopenharmony_ci	smp_wmb();
65362306a36Sopenharmony_ci}
65462306a36Sopenharmony_ci
65562306a36Sopenharmony_cistatic void nicvf_snd_pkt_handler(struct net_device *netdev,
65662306a36Sopenharmony_ci				  struct cqe_send_t *cqe_tx,
65762306a36Sopenharmony_ci				  int budget, int *subdesc_cnt,
65862306a36Sopenharmony_ci				  unsigned int *tx_pkts, unsigned int *tx_bytes)
65962306a36Sopenharmony_ci{
66062306a36Sopenharmony_ci	struct sk_buff *skb = NULL;
66162306a36Sopenharmony_ci	struct page *page;
66262306a36Sopenharmony_ci	struct nicvf *nic = netdev_priv(netdev);
66362306a36Sopenharmony_ci	struct snd_queue *sq;
66462306a36Sopenharmony_ci	struct sq_hdr_subdesc *hdr;
66562306a36Sopenharmony_ci	struct sq_hdr_subdesc *tso_sqe;
66662306a36Sopenharmony_ci
66762306a36Sopenharmony_ci	sq = &nic->qs->sq[cqe_tx->sq_idx];
66862306a36Sopenharmony_ci
66962306a36Sopenharmony_ci	hdr = (struct sq_hdr_subdesc *)GET_SQ_DESC(sq, cqe_tx->sqe_ptr);
67062306a36Sopenharmony_ci	if (hdr->subdesc_type != SQ_DESC_TYPE_HEADER)
67162306a36Sopenharmony_ci		return;
67262306a36Sopenharmony_ci
67362306a36Sopenharmony_ci	/* Check for errors */
67462306a36Sopenharmony_ci	if (cqe_tx->send_status)
67562306a36Sopenharmony_ci		nicvf_check_cqe_tx_errs(nic->pnicvf, cqe_tx);
67662306a36Sopenharmony_ci
67762306a36Sopenharmony_ci	/* Is this a XDP designated Tx queue */
67862306a36Sopenharmony_ci	if (sq->is_xdp) {
67962306a36Sopenharmony_ci		page = (struct page *)sq->xdp_page[cqe_tx->sqe_ptr];
68062306a36Sopenharmony_ci		/* Check if it's recycled page or else unmap DMA mapping */
68162306a36Sopenharmony_ci		if (page && (page_ref_count(page) == 1))
68262306a36Sopenharmony_ci			nicvf_unmap_sndq_buffers(nic, sq, cqe_tx->sqe_ptr,
68362306a36Sopenharmony_ci						 hdr->subdesc_cnt);
68462306a36Sopenharmony_ci
68562306a36Sopenharmony_ci		/* Release page reference for recycling */
68662306a36Sopenharmony_ci		if (page)
68762306a36Sopenharmony_ci			put_page(page);
68862306a36Sopenharmony_ci		sq->xdp_page[cqe_tx->sqe_ptr] = (u64)NULL;
68962306a36Sopenharmony_ci		*subdesc_cnt += hdr->subdesc_cnt + 1;
69062306a36Sopenharmony_ci		return;
69162306a36Sopenharmony_ci	}
69262306a36Sopenharmony_ci
69362306a36Sopenharmony_ci	skb = (struct sk_buff *)sq->skbuff[cqe_tx->sqe_ptr];
69462306a36Sopenharmony_ci	if (skb) {
69562306a36Sopenharmony_ci		/* Check for dummy descriptor used for HW TSO offload on 88xx */
69662306a36Sopenharmony_ci		if (hdr->dont_send) {
69762306a36Sopenharmony_ci			/* Get actual TSO descriptors and free them */
69862306a36Sopenharmony_ci			tso_sqe =
69962306a36Sopenharmony_ci			 (struct sq_hdr_subdesc *)GET_SQ_DESC(sq, hdr->rsvd2);
70062306a36Sopenharmony_ci			nicvf_unmap_sndq_buffers(nic, sq, hdr->rsvd2,
70162306a36Sopenharmony_ci						 tso_sqe->subdesc_cnt);
70262306a36Sopenharmony_ci			*subdesc_cnt += tso_sqe->subdesc_cnt + 1;
70362306a36Sopenharmony_ci		} else {
70462306a36Sopenharmony_ci			nicvf_unmap_sndq_buffers(nic, sq, cqe_tx->sqe_ptr,
70562306a36Sopenharmony_ci						 hdr->subdesc_cnt);
70662306a36Sopenharmony_ci		}
70762306a36Sopenharmony_ci		*subdesc_cnt += hdr->subdesc_cnt + 1;
70862306a36Sopenharmony_ci		prefetch(skb);
70962306a36Sopenharmony_ci		(*tx_pkts)++;
71062306a36Sopenharmony_ci		*tx_bytes += skb->len;
71162306a36Sopenharmony_ci		/* If timestamp is requested for this skb, don't free it */
71262306a36Sopenharmony_ci		if (skb_shinfo(skb)->tx_flags & SKBTX_IN_PROGRESS &&
71362306a36Sopenharmony_ci		    !nic->pnicvf->ptp_skb)
71462306a36Sopenharmony_ci			nic->pnicvf->ptp_skb = skb;
71562306a36Sopenharmony_ci		else
71662306a36Sopenharmony_ci			napi_consume_skb(skb, budget);
71762306a36Sopenharmony_ci		sq->skbuff[cqe_tx->sqe_ptr] = (u64)NULL;
71862306a36Sopenharmony_ci	} else {
71962306a36Sopenharmony_ci		/* In case of SW TSO on 88xx, only last segment will have
72062306a36Sopenharmony_ci		 * a SKB attached, so just free SQEs here.
72162306a36Sopenharmony_ci		 */
72262306a36Sopenharmony_ci		if (!nic->hw_tso)
72362306a36Sopenharmony_ci			*subdesc_cnt += hdr->subdesc_cnt + 1;
72462306a36Sopenharmony_ci	}
72562306a36Sopenharmony_ci}
72662306a36Sopenharmony_ci
72762306a36Sopenharmony_cistatic inline void nicvf_set_rxhash(struct net_device *netdev,
72862306a36Sopenharmony_ci				    struct cqe_rx_t *cqe_rx,
72962306a36Sopenharmony_ci				    struct sk_buff *skb)
73062306a36Sopenharmony_ci{
73162306a36Sopenharmony_ci	u8 hash_type;
73262306a36Sopenharmony_ci	u32 hash;
73362306a36Sopenharmony_ci
73462306a36Sopenharmony_ci	if (!(netdev->features & NETIF_F_RXHASH))
73562306a36Sopenharmony_ci		return;
73662306a36Sopenharmony_ci
73762306a36Sopenharmony_ci	switch (cqe_rx->rss_alg) {
73862306a36Sopenharmony_ci	case RSS_ALG_TCP_IP:
73962306a36Sopenharmony_ci	case RSS_ALG_UDP_IP:
74062306a36Sopenharmony_ci		hash_type = PKT_HASH_TYPE_L4;
74162306a36Sopenharmony_ci		hash = cqe_rx->rss_tag;
74262306a36Sopenharmony_ci		break;
74362306a36Sopenharmony_ci	case RSS_ALG_IP:
74462306a36Sopenharmony_ci		hash_type = PKT_HASH_TYPE_L3;
74562306a36Sopenharmony_ci		hash = cqe_rx->rss_tag;
74662306a36Sopenharmony_ci		break;
74762306a36Sopenharmony_ci	default:
74862306a36Sopenharmony_ci		hash_type = PKT_HASH_TYPE_NONE;
74962306a36Sopenharmony_ci		hash = 0;
75062306a36Sopenharmony_ci	}
75162306a36Sopenharmony_ci
75262306a36Sopenharmony_ci	skb_set_hash(skb, hash, hash_type);
75362306a36Sopenharmony_ci}
75462306a36Sopenharmony_ci
75562306a36Sopenharmony_cistatic inline void nicvf_set_rxtstamp(struct nicvf *nic, struct sk_buff *skb)
75662306a36Sopenharmony_ci{
75762306a36Sopenharmony_ci	u64 ns;
75862306a36Sopenharmony_ci
75962306a36Sopenharmony_ci	if (!nic->ptp_clock || !nic->hw_rx_tstamp)
76062306a36Sopenharmony_ci		return;
76162306a36Sopenharmony_ci
76262306a36Sopenharmony_ci	/* The first 8 bytes is the timestamp */
76362306a36Sopenharmony_ci	ns = cavium_ptp_tstamp2time(nic->ptp_clock,
76462306a36Sopenharmony_ci				    be64_to_cpu(*(__be64 *)skb->data));
76562306a36Sopenharmony_ci	skb_hwtstamps(skb)->hwtstamp = ns_to_ktime(ns);
76662306a36Sopenharmony_ci
76762306a36Sopenharmony_ci	__skb_pull(skb, 8);
76862306a36Sopenharmony_ci}
76962306a36Sopenharmony_ci
77062306a36Sopenharmony_cistatic void nicvf_rcv_pkt_handler(struct net_device *netdev,
77162306a36Sopenharmony_ci				  struct napi_struct *napi,
77262306a36Sopenharmony_ci				  struct cqe_rx_t *cqe_rx,
77362306a36Sopenharmony_ci				  struct snd_queue *sq, struct rcv_queue *rq)
77462306a36Sopenharmony_ci{
77562306a36Sopenharmony_ci	struct sk_buff *skb = NULL;
77662306a36Sopenharmony_ci	struct nicvf *nic = netdev_priv(netdev);
77762306a36Sopenharmony_ci	struct nicvf *snic = nic;
77862306a36Sopenharmony_ci	int err = 0;
77962306a36Sopenharmony_ci	int rq_idx;
78062306a36Sopenharmony_ci
78162306a36Sopenharmony_ci	rq_idx = nicvf_netdev_qidx(nic, cqe_rx->rq_idx);
78262306a36Sopenharmony_ci
78362306a36Sopenharmony_ci	if (nic->sqs_mode) {
78462306a36Sopenharmony_ci		/* Use primary VF's 'nicvf' struct */
78562306a36Sopenharmony_ci		nic = nic->pnicvf;
78662306a36Sopenharmony_ci		netdev = nic->netdev;
78762306a36Sopenharmony_ci	}
78862306a36Sopenharmony_ci
78962306a36Sopenharmony_ci	/* Check for errors */
79062306a36Sopenharmony_ci	if (cqe_rx->err_level || cqe_rx->err_opcode) {
79162306a36Sopenharmony_ci		err = nicvf_check_cqe_rx_errs(nic, cqe_rx);
79262306a36Sopenharmony_ci		if (err && !cqe_rx->rb_cnt)
79362306a36Sopenharmony_ci			return;
79462306a36Sopenharmony_ci	}
79562306a36Sopenharmony_ci
79662306a36Sopenharmony_ci	/* For XDP, ignore pkts spanning multiple pages */
79762306a36Sopenharmony_ci	if (nic->xdp_prog && (cqe_rx->rb_cnt == 1)) {
79862306a36Sopenharmony_ci		/* Packet consumed by XDP */
79962306a36Sopenharmony_ci		if (nicvf_xdp_rx(snic, nic->xdp_prog, cqe_rx, sq, rq, &skb))
80062306a36Sopenharmony_ci			return;
80162306a36Sopenharmony_ci	} else {
80262306a36Sopenharmony_ci		skb = nicvf_get_rcv_skb(snic, cqe_rx,
80362306a36Sopenharmony_ci					nic->xdp_prog ? true : false);
80462306a36Sopenharmony_ci	}
80562306a36Sopenharmony_ci
80662306a36Sopenharmony_ci	if (!skb)
80762306a36Sopenharmony_ci		return;
80862306a36Sopenharmony_ci
80962306a36Sopenharmony_ci	if (netif_msg_pktdata(nic)) {
81062306a36Sopenharmony_ci		netdev_info(nic->netdev, "skb 0x%p, len=%d\n", skb, skb->len);
81162306a36Sopenharmony_ci		print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 16, 1,
81262306a36Sopenharmony_ci			       skb->data, skb->len, true);
81362306a36Sopenharmony_ci	}
81462306a36Sopenharmony_ci
81562306a36Sopenharmony_ci	/* If error packet, drop it here */
81662306a36Sopenharmony_ci	if (err) {
81762306a36Sopenharmony_ci		dev_kfree_skb_any(skb);
81862306a36Sopenharmony_ci		return;
81962306a36Sopenharmony_ci	}
82062306a36Sopenharmony_ci
82162306a36Sopenharmony_ci	nicvf_set_rxtstamp(nic, skb);
82262306a36Sopenharmony_ci	nicvf_set_rxhash(netdev, cqe_rx, skb);
82362306a36Sopenharmony_ci
82462306a36Sopenharmony_ci	skb_record_rx_queue(skb, rq_idx);
82562306a36Sopenharmony_ci	if (netdev->hw_features & NETIF_F_RXCSUM) {
82662306a36Sopenharmony_ci		/* HW by default verifies TCP/UDP/SCTP checksums */
82762306a36Sopenharmony_ci		skb->ip_summed = CHECKSUM_UNNECESSARY;
82862306a36Sopenharmony_ci	} else {
82962306a36Sopenharmony_ci		skb_checksum_none_assert(skb);
83062306a36Sopenharmony_ci	}
83162306a36Sopenharmony_ci
83262306a36Sopenharmony_ci	skb->protocol = eth_type_trans(skb, netdev);
83362306a36Sopenharmony_ci
83462306a36Sopenharmony_ci	/* Check for stripped VLAN */
83562306a36Sopenharmony_ci	if (cqe_rx->vlan_found && cqe_rx->vlan_stripped)
83662306a36Sopenharmony_ci		__vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q),
83762306a36Sopenharmony_ci				       ntohs((__force __be16)cqe_rx->vlan_tci));
83862306a36Sopenharmony_ci
83962306a36Sopenharmony_ci	if (napi && (netdev->features & NETIF_F_GRO))
84062306a36Sopenharmony_ci		napi_gro_receive(napi, skb);
84162306a36Sopenharmony_ci	else
84262306a36Sopenharmony_ci		netif_receive_skb(skb);
84362306a36Sopenharmony_ci}
84462306a36Sopenharmony_ci
84562306a36Sopenharmony_cistatic int nicvf_cq_intr_handler(struct net_device *netdev, u8 cq_idx,
84662306a36Sopenharmony_ci				 struct napi_struct *napi, int budget)
84762306a36Sopenharmony_ci{
84862306a36Sopenharmony_ci	int processed_cqe, work_done = 0, tx_done = 0;
84962306a36Sopenharmony_ci	int cqe_count, cqe_head;
85062306a36Sopenharmony_ci	int subdesc_cnt = 0;
85162306a36Sopenharmony_ci	struct nicvf *nic = netdev_priv(netdev);
85262306a36Sopenharmony_ci	struct queue_set *qs = nic->qs;
85362306a36Sopenharmony_ci	struct cmp_queue *cq = &qs->cq[cq_idx];
85462306a36Sopenharmony_ci	struct cqe_rx_t *cq_desc;
85562306a36Sopenharmony_ci	struct netdev_queue *txq;
85662306a36Sopenharmony_ci	struct snd_queue *sq = &qs->sq[cq_idx];
85762306a36Sopenharmony_ci	struct rcv_queue *rq = &qs->rq[cq_idx];
85862306a36Sopenharmony_ci	unsigned int tx_pkts = 0, tx_bytes = 0, txq_idx;
85962306a36Sopenharmony_ci
86062306a36Sopenharmony_ci	spin_lock_bh(&cq->lock);
86162306a36Sopenharmony_ciloop:
86262306a36Sopenharmony_ci	processed_cqe = 0;
86362306a36Sopenharmony_ci	/* Get no of valid CQ entries to process */
86462306a36Sopenharmony_ci	cqe_count = nicvf_queue_reg_read(nic, NIC_QSET_CQ_0_7_STATUS, cq_idx);
86562306a36Sopenharmony_ci	cqe_count &= CQ_CQE_COUNT;
86662306a36Sopenharmony_ci	if (!cqe_count)
86762306a36Sopenharmony_ci		goto done;
86862306a36Sopenharmony_ci
86962306a36Sopenharmony_ci	/* Get head of the valid CQ entries */
87062306a36Sopenharmony_ci	cqe_head = nicvf_queue_reg_read(nic, NIC_QSET_CQ_0_7_HEAD, cq_idx) >> 9;
87162306a36Sopenharmony_ci	cqe_head &= 0xFFFF;
87262306a36Sopenharmony_ci
87362306a36Sopenharmony_ci	while (processed_cqe < cqe_count) {
87462306a36Sopenharmony_ci		/* Get the CQ descriptor */
87562306a36Sopenharmony_ci		cq_desc = (struct cqe_rx_t *)GET_CQ_DESC(cq, cqe_head);
87662306a36Sopenharmony_ci		cqe_head++;
87762306a36Sopenharmony_ci		cqe_head &= (cq->dmem.q_len - 1);
87862306a36Sopenharmony_ci		/* Initiate prefetch for next descriptor */
87962306a36Sopenharmony_ci		prefetch((struct cqe_rx_t *)GET_CQ_DESC(cq, cqe_head));
88062306a36Sopenharmony_ci
88162306a36Sopenharmony_ci		if ((work_done >= budget) && napi &&
88262306a36Sopenharmony_ci		    (cq_desc->cqe_type != CQE_TYPE_SEND)) {
88362306a36Sopenharmony_ci			break;
88462306a36Sopenharmony_ci		}
88562306a36Sopenharmony_ci
88662306a36Sopenharmony_ci		switch (cq_desc->cqe_type) {
88762306a36Sopenharmony_ci		case CQE_TYPE_RX:
88862306a36Sopenharmony_ci			nicvf_rcv_pkt_handler(netdev, napi, cq_desc, sq, rq);
88962306a36Sopenharmony_ci			work_done++;
89062306a36Sopenharmony_ci		break;
89162306a36Sopenharmony_ci		case CQE_TYPE_SEND:
89262306a36Sopenharmony_ci			nicvf_snd_pkt_handler(netdev, (void *)cq_desc,
89362306a36Sopenharmony_ci					      budget, &subdesc_cnt,
89462306a36Sopenharmony_ci					      &tx_pkts, &tx_bytes);
89562306a36Sopenharmony_ci			tx_done++;
89662306a36Sopenharmony_ci		break;
89762306a36Sopenharmony_ci		case CQE_TYPE_SEND_PTP:
89862306a36Sopenharmony_ci			nicvf_snd_ptp_handler(netdev, (void *)cq_desc);
89962306a36Sopenharmony_ci		break;
90062306a36Sopenharmony_ci		case CQE_TYPE_INVALID:
90162306a36Sopenharmony_ci		case CQE_TYPE_RX_SPLIT:
90262306a36Sopenharmony_ci		case CQE_TYPE_RX_TCP:
90362306a36Sopenharmony_ci			/* Ignore for now */
90462306a36Sopenharmony_ci		break;
90562306a36Sopenharmony_ci		}
90662306a36Sopenharmony_ci		processed_cqe++;
90762306a36Sopenharmony_ci	}
90862306a36Sopenharmony_ci
90962306a36Sopenharmony_ci	/* Ring doorbell to inform H/W to reuse processed CQEs */
91062306a36Sopenharmony_ci	nicvf_queue_reg_write(nic, NIC_QSET_CQ_0_7_DOOR,
91162306a36Sopenharmony_ci			      cq_idx, processed_cqe);
91262306a36Sopenharmony_ci
91362306a36Sopenharmony_ci	if ((work_done < budget) && napi)
91462306a36Sopenharmony_ci		goto loop;
91562306a36Sopenharmony_ci
91662306a36Sopenharmony_cidone:
91762306a36Sopenharmony_ci	/* Update SQ's descriptor free count */
91862306a36Sopenharmony_ci	if (subdesc_cnt)
91962306a36Sopenharmony_ci		nicvf_put_sq_desc(sq, subdesc_cnt);
92062306a36Sopenharmony_ci
92162306a36Sopenharmony_ci	txq_idx = nicvf_netdev_qidx(nic, cq_idx);
92262306a36Sopenharmony_ci	/* Handle XDP TX queues */
92362306a36Sopenharmony_ci	if (nic->pnicvf->xdp_prog) {
92462306a36Sopenharmony_ci		if (txq_idx < nic->pnicvf->xdp_tx_queues) {
92562306a36Sopenharmony_ci			nicvf_xdp_sq_doorbell(nic, sq, cq_idx);
92662306a36Sopenharmony_ci			goto out;
92762306a36Sopenharmony_ci		}
92862306a36Sopenharmony_ci		nic = nic->pnicvf;
92962306a36Sopenharmony_ci		txq_idx -= nic->pnicvf->xdp_tx_queues;
93062306a36Sopenharmony_ci	}
93162306a36Sopenharmony_ci
93262306a36Sopenharmony_ci	/* Wakeup TXQ if its stopped earlier due to SQ full */
93362306a36Sopenharmony_ci	if (tx_done ||
93462306a36Sopenharmony_ci	    (atomic_read(&sq->free_cnt) >= MIN_SQ_DESC_PER_PKT_XMIT)) {
93562306a36Sopenharmony_ci		netdev = nic->pnicvf->netdev;
93662306a36Sopenharmony_ci		txq = netdev_get_tx_queue(netdev, txq_idx);
93762306a36Sopenharmony_ci		if (tx_pkts)
93862306a36Sopenharmony_ci			netdev_tx_completed_queue(txq, tx_pkts, tx_bytes);
93962306a36Sopenharmony_ci
94062306a36Sopenharmony_ci		/* To read updated queue and carrier status */
94162306a36Sopenharmony_ci		smp_mb();
94262306a36Sopenharmony_ci		if (netif_tx_queue_stopped(txq) && netif_carrier_ok(netdev)) {
94362306a36Sopenharmony_ci			netif_tx_wake_queue(txq);
94462306a36Sopenharmony_ci			nic = nic->pnicvf;
94562306a36Sopenharmony_ci			this_cpu_inc(nic->drv_stats->txq_wake);
94662306a36Sopenharmony_ci			netif_warn(nic, tx_err, netdev,
94762306a36Sopenharmony_ci				   "Transmit queue wakeup SQ%d\n", txq_idx);
94862306a36Sopenharmony_ci		}
94962306a36Sopenharmony_ci	}
95062306a36Sopenharmony_ci
95162306a36Sopenharmony_ciout:
95262306a36Sopenharmony_ci	spin_unlock_bh(&cq->lock);
95362306a36Sopenharmony_ci	return work_done;
95462306a36Sopenharmony_ci}
95562306a36Sopenharmony_ci
95662306a36Sopenharmony_cistatic int nicvf_poll(struct napi_struct *napi, int budget)
95762306a36Sopenharmony_ci{
95862306a36Sopenharmony_ci	u64  cq_head;
95962306a36Sopenharmony_ci	int  work_done = 0;
96062306a36Sopenharmony_ci	struct net_device *netdev = napi->dev;
96162306a36Sopenharmony_ci	struct nicvf *nic = netdev_priv(netdev);
96262306a36Sopenharmony_ci	struct nicvf_cq_poll *cq;
96362306a36Sopenharmony_ci
96462306a36Sopenharmony_ci	cq = container_of(napi, struct nicvf_cq_poll, napi);
96562306a36Sopenharmony_ci	work_done = nicvf_cq_intr_handler(netdev, cq->cq_idx, napi, budget);
96662306a36Sopenharmony_ci
96762306a36Sopenharmony_ci	if (work_done < budget) {
96862306a36Sopenharmony_ci		/* Slow packet rate, exit polling */
96962306a36Sopenharmony_ci		napi_complete_done(napi, work_done);
97062306a36Sopenharmony_ci		/* Re-enable interrupts */
97162306a36Sopenharmony_ci		cq_head = nicvf_queue_reg_read(nic, NIC_QSET_CQ_0_7_HEAD,
97262306a36Sopenharmony_ci					       cq->cq_idx);
97362306a36Sopenharmony_ci		nicvf_clear_intr(nic, NICVF_INTR_CQ, cq->cq_idx);
97462306a36Sopenharmony_ci		nicvf_queue_reg_write(nic, NIC_QSET_CQ_0_7_HEAD,
97562306a36Sopenharmony_ci				      cq->cq_idx, cq_head);
97662306a36Sopenharmony_ci		nicvf_enable_intr(nic, NICVF_INTR_CQ, cq->cq_idx);
97762306a36Sopenharmony_ci	}
97862306a36Sopenharmony_ci	return work_done;
97962306a36Sopenharmony_ci}
98062306a36Sopenharmony_ci
98162306a36Sopenharmony_ci/* Qset error interrupt handler
98262306a36Sopenharmony_ci *
98362306a36Sopenharmony_ci * As of now only CQ errors are handled
98462306a36Sopenharmony_ci */
98562306a36Sopenharmony_cistatic void nicvf_handle_qs_err(struct tasklet_struct *t)
98662306a36Sopenharmony_ci{
98762306a36Sopenharmony_ci	struct nicvf *nic = from_tasklet(nic, t, qs_err_task);
98862306a36Sopenharmony_ci	struct queue_set *qs = nic->qs;
98962306a36Sopenharmony_ci	int qidx;
99062306a36Sopenharmony_ci	u64 status;
99162306a36Sopenharmony_ci
99262306a36Sopenharmony_ci	netif_tx_disable(nic->netdev);
99362306a36Sopenharmony_ci
99462306a36Sopenharmony_ci	/* Check if it is CQ err */
99562306a36Sopenharmony_ci	for (qidx = 0; qidx < qs->cq_cnt; qidx++) {
99662306a36Sopenharmony_ci		status = nicvf_queue_reg_read(nic, NIC_QSET_CQ_0_7_STATUS,
99762306a36Sopenharmony_ci					      qidx);
99862306a36Sopenharmony_ci		if (!(status & CQ_ERR_MASK))
99962306a36Sopenharmony_ci			continue;
100062306a36Sopenharmony_ci		/* Process already queued CQEs and reconfig CQ */
100162306a36Sopenharmony_ci		nicvf_disable_intr(nic, NICVF_INTR_CQ, qidx);
100262306a36Sopenharmony_ci		nicvf_sq_disable(nic, qidx);
100362306a36Sopenharmony_ci		nicvf_cq_intr_handler(nic->netdev, qidx, NULL, 0);
100462306a36Sopenharmony_ci		nicvf_cmp_queue_config(nic, qs, qidx, true);
100562306a36Sopenharmony_ci		nicvf_sq_free_used_descs(nic->netdev, &qs->sq[qidx], qidx);
100662306a36Sopenharmony_ci		nicvf_sq_enable(nic, &qs->sq[qidx], qidx);
100762306a36Sopenharmony_ci
100862306a36Sopenharmony_ci		nicvf_enable_intr(nic, NICVF_INTR_CQ, qidx);
100962306a36Sopenharmony_ci	}
101062306a36Sopenharmony_ci
101162306a36Sopenharmony_ci	netif_tx_start_all_queues(nic->netdev);
101262306a36Sopenharmony_ci	/* Re-enable Qset error interrupt */
101362306a36Sopenharmony_ci	nicvf_enable_intr(nic, NICVF_INTR_QS_ERR, 0);
101462306a36Sopenharmony_ci}
101562306a36Sopenharmony_ci
101662306a36Sopenharmony_cistatic void nicvf_dump_intr_status(struct nicvf *nic)
101762306a36Sopenharmony_ci{
101862306a36Sopenharmony_ci	netif_info(nic, intr, nic->netdev, "interrupt status 0x%llx\n",
101962306a36Sopenharmony_ci		   nicvf_reg_read(nic, NIC_VF_INT));
102062306a36Sopenharmony_ci}
102162306a36Sopenharmony_ci
102262306a36Sopenharmony_cistatic irqreturn_t nicvf_misc_intr_handler(int irq, void *nicvf_irq)
102362306a36Sopenharmony_ci{
102462306a36Sopenharmony_ci	struct nicvf *nic = (struct nicvf *)nicvf_irq;
102562306a36Sopenharmony_ci	u64 intr;
102662306a36Sopenharmony_ci
102762306a36Sopenharmony_ci	nicvf_dump_intr_status(nic);
102862306a36Sopenharmony_ci
102962306a36Sopenharmony_ci	intr = nicvf_reg_read(nic, NIC_VF_INT);
103062306a36Sopenharmony_ci	/* Check for spurious interrupt */
103162306a36Sopenharmony_ci	if (!(intr & NICVF_INTR_MBOX_MASK))
103262306a36Sopenharmony_ci		return IRQ_HANDLED;
103362306a36Sopenharmony_ci
103462306a36Sopenharmony_ci	nicvf_handle_mbx_intr(nic);
103562306a36Sopenharmony_ci
103662306a36Sopenharmony_ci	return IRQ_HANDLED;
103762306a36Sopenharmony_ci}
103862306a36Sopenharmony_ci
103962306a36Sopenharmony_cistatic irqreturn_t nicvf_intr_handler(int irq, void *cq_irq)
104062306a36Sopenharmony_ci{
104162306a36Sopenharmony_ci	struct nicvf_cq_poll *cq_poll = (struct nicvf_cq_poll *)cq_irq;
104262306a36Sopenharmony_ci	struct nicvf *nic = cq_poll->nicvf;
104362306a36Sopenharmony_ci	int qidx = cq_poll->cq_idx;
104462306a36Sopenharmony_ci
104562306a36Sopenharmony_ci	nicvf_dump_intr_status(nic);
104662306a36Sopenharmony_ci
104762306a36Sopenharmony_ci	/* Disable interrupts */
104862306a36Sopenharmony_ci	nicvf_disable_intr(nic, NICVF_INTR_CQ, qidx);
104962306a36Sopenharmony_ci
105062306a36Sopenharmony_ci	/* Schedule NAPI */
105162306a36Sopenharmony_ci	napi_schedule_irqoff(&cq_poll->napi);
105262306a36Sopenharmony_ci
105362306a36Sopenharmony_ci	/* Clear interrupt */
105462306a36Sopenharmony_ci	nicvf_clear_intr(nic, NICVF_INTR_CQ, qidx);
105562306a36Sopenharmony_ci
105662306a36Sopenharmony_ci	return IRQ_HANDLED;
105762306a36Sopenharmony_ci}
105862306a36Sopenharmony_ci
105962306a36Sopenharmony_cistatic irqreturn_t nicvf_rbdr_intr_handler(int irq, void *nicvf_irq)
106062306a36Sopenharmony_ci{
106162306a36Sopenharmony_ci	struct nicvf *nic = (struct nicvf *)nicvf_irq;
106262306a36Sopenharmony_ci	u8 qidx;
106362306a36Sopenharmony_ci
106462306a36Sopenharmony_ci
106562306a36Sopenharmony_ci	nicvf_dump_intr_status(nic);
106662306a36Sopenharmony_ci
106762306a36Sopenharmony_ci	/* Disable RBDR interrupt and schedule softirq */
106862306a36Sopenharmony_ci	for (qidx = 0; qidx < nic->qs->rbdr_cnt; qidx++) {
106962306a36Sopenharmony_ci		if (!nicvf_is_intr_enabled(nic, NICVF_INTR_RBDR, qidx))
107062306a36Sopenharmony_ci			continue;
107162306a36Sopenharmony_ci		nicvf_disable_intr(nic, NICVF_INTR_RBDR, qidx);
107262306a36Sopenharmony_ci		tasklet_hi_schedule(&nic->rbdr_task);
107362306a36Sopenharmony_ci		/* Clear interrupt */
107462306a36Sopenharmony_ci		nicvf_clear_intr(nic, NICVF_INTR_RBDR, qidx);
107562306a36Sopenharmony_ci	}
107662306a36Sopenharmony_ci
107762306a36Sopenharmony_ci	return IRQ_HANDLED;
107862306a36Sopenharmony_ci}
107962306a36Sopenharmony_ci
108062306a36Sopenharmony_cistatic irqreturn_t nicvf_qs_err_intr_handler(int irq, void *nicvf_irq)
108162306a36Sopenharmony_ci{
108262306a36Sopenharmony_ci	struct nicvf *nic = (struct nicvf *)nicvf_irq;
108362306a36Sopenharmony_ci
108462306a36Sopenharmony_ci	nicvf_dump_intr_status(nic);
108562306a36Sopenharmony_ci
108662306a36Sopenharmony_ci	/* Disable Qset err interrupt and schedule softirq */
108762306a36Sopenharmony_ci	nicvf_disable_intr(nic, NICVF_INTR_QS_ERR, 0);
108862306a36Sopenharmony_ci	tasklet_hi_schedule(&nic->qs_err_task);
108962306a36Sopenharmony_ci	nicvf_clear_intr(nic, NICVF_INTR_QS_ERR, 0);
109062306a36Sopenharmony_ci
109162306a36Sopenharmony_ci	return IRQ_HANDLED;
109262306a36Sopenharmony_ci}
109362306a36Sopenharmony_ci
109462306a36Sopenharmony_cistatic void nicvf_set_irq_affinity(struct nicvf *nic)
109562306a36Sopenharmony_ci{
109662306a36Sopenharmony_ci	int vec, cpu;
109762306a36Sopenharmony_ci
109862306a36Sopenharmony_ci	for (vec = 0; vec < nic->num_vec; vec++) {
109962306a36Sopenharmony_ci		if (!nic->irq_allocated[vec])
110062306a36Sopenharmony_ci			continue;
110162306a36Sopenharmony_ci
110262306a36Sopenharmony_ci		if (!zalloc_cpumask_var(&nic->affinity_mask[vec], GFP_KERNEL))
110362306a36Sopenharmony_ci			return;
110462306a36Sopenharmony_ci		 /* CQ interrupts */
110562306a36Sopenharmony_ci		if (vec < NICVF_INTR_ID_SQ)
110662306a36Sopenharmony_ci			/* Leave CPU0 for RBDR and other interrupts */
110762306a36Sopenharmony_ci			cpu = nicvf_netdev_qidx(nic, vec) + 1;
110862306a36Sopenharmony_ci		else
110962306a36Sopenharmony_ci			cpu = 0;
111062306a36Sopenharmony_ci
111162306a36Sopenharmony_ci		cpumask_set_cpu(cpumask_local_spread(cpu, nic->node),
111262306a36Sopenharmony_ci				nic->affinity_mask[vec]);
111362306a36Sopenharmony_ci		irq_set_affinity_hint(pci_irq_vector(nic->pdev, vec),
111462306a36Sopenharmony_ci				      nic->affinity_mask[vec]);
111562306a36Sopenharmony_ci	}
111662306a36Sopenharmony_ci}
111762306a36Sopenharmony_ci
111862306a36Sopenharmony_cistatic int nicvf_register_interrupts(struct nicvf *nic)
111962306a36Sopenharmony_ci{
112062306a36Sopenharmony_ci	int irq, ret = 0;
112162306a36Sopenharmony_ci
112262306a36Sopenharmony_ci	for_each_cq_irq(irq)
112362306a36Sopenharmony_ci		sprintf(nic->irq_name[irq], "%s-rxtx-%d",
112462306a36Sopenharmony_ci			nic->pnicvf->netdev->name,
112562306a36Sopenharmony_ci			nicvf_netdev_qidx(nic, irq));
112662306a36Sopenharmony_ci
112762306a36Sopenharmony_ci	for_each_sq_irq(irq)
112862306a36Sopenharmony_ci		sprintf(nic->irq_name[irq], "%s-sq-%d",
112962306a36Sopenharmony_ci			nic->pnicvf->netdev->name,
113062306a36Sopenharmony_ci			nicvf_netdev_qidx(nic, irq - NICVF_INTR_ID_SQ));
113162306a36Sopenharmony_ci
113262306a36Sopenharmony_ci	for_each_rbdr_irq(irq)
113362306a36Sopenharmony_ci		sprintf(nic->irq_name[irq], "%s-rbdr-%d",
113462306a36Sopenharmony_ci			nic->pnicvf->netdev->name,
113562306a36Sopenharmony_ci			nic->sqs_mode ? (nic->sqs_id + 1) : 0);
113662306a36Sopenharmony_ci
113762306a36Sopenharmony_ci	/* Register CQ interrupts */
113862306a36Sopenharmony_ci	for (irq = 0; irq < nic->qs->cq_cnt; irq++) {
113962306a36Sopenharmony_ci		ret = request_irq(pci_irq_vector(nic->pdev, irq),
114062306a36Sopenharmony_ci				  nicvf_intr_handler,
114162306a36Sopenharmony_ci				  0, nic->irq_name[irq], nic->napi[irq]);
114262306a36Sopenharmony_ci		if (ret)
114362306a36Sopenharmony_ci			goto err;
114462306a36Sopenharmony_ci		nic->irq_allocated[irq] = true;
114562306a36Sopenharmony_ci	}
114662306a36Sopenharmony_ci
114762306a36Sopenharmony_ci	/* Register RBDR interrupt */
114862306a36Sopenharmony_ci	for (irq = NICVF_INTR_ID_RBDR;
114962306a36Sopenharmony_ci	     irq < (NICVF_INTR_ID_RBDR + nic->qs->rbdr_cnt); irq++) {
115062306a36Sopenharmony_ci		ret = request_irq(pci_irq_vector(nic->pdev, irq),
115162306a36Sopenharmony_ci				  nicvf_rbdr_intr_handler,
115262306a36Sopenharmony_ci				  0, nic->irq_name[irq], nic);
115362306a36Sopenharmony_ci		if (ret)
115462306a36Sopenharmony_ci			goto err;
115562306a36Sopenharmony_ci		nic->irq_allocated[irq] = true;
115662306a36Sopenharmony_ci	}
115762306a36Sopenharmony_ci
115862306a36Sopenharmony_ci	/* Register QS error interrupt */
115962306a36Sopenharmony_ci	sprintf(nic->irq_name[NICVF_INTR_ID_QS_ERR], "%s-qset-err-%d",
116062306a36Sopenharmony_ci		nic->pnicvf->netdev->name,
116162306a36Sopenharmony_ci		nic->sqs_mode ? (nic->sqs_id + 1) : 0);
116262306a36Sopenharmony_ci	irq = NICVF_INTR_ID_QS_ERR;
116362306a36Sopenharmony_ci	ret = request_irq(pci_irq_vector(nic->pdev, irq),
116462306a36Sopenharmony_ci			  nicvf_qs_err_intr_handler,
116562306a36Sopenharmony_ci			  0, nic->irq_name[irq], nic);
116662306a36Sopenharmony_ci	if (ret)
116762306a36Sopenharmony_ci		goto err;
116862306a36Sopenharmony_ci
116962306a36Sopenharmony_ci	nic->irq_allocated[irq] = true;
117062306a36Sopenharmony_ci
117162306a36Sopenharmony_ci	/* Set IRQ affinities */
117262306a36Sopenharmony_ci	nicvf_set_irq_affinity(nic);
117362306a36Sopenharmony_ci
117462306a36Sopenharmony_cierr:
117562306a36Sopenharmony_ci	if (ret)
117662306a36Sopenharmony_ci		netdev_err(nic->netdev, "request_irq failed, vector %d\n", irq);
117762306a36Sopenharmony_ci
117862306a36Sopenharmony_ci	return ret;
117962306a36Sopenharmony_ci}
118062306a36Sopenharmony_ci
118162306a36Sopenharmony_cistatic void nicvf_unregister_interrupts(struct nicvf *nic)
118262306a36Sopenharmony_ci{
118362306a36Sopenharmony_ci	struct pci_dev *pdev = nic->pdev;
118462306a36Sopenharmony_ci	int irq;
118562306a36Sopenharmony_ci
118662306a36Sopenharmony_ci	/* Free registered interrupts */
118762306a36Sopenharmony_ci	for (irq = 0; irq < nic->num_vec; irq++) {
118862306a36Sopenharmony_ci		if (!nic->irq_allocated[irq])
118962306a36Sopenharmony_ci			continue;
119062306a36Sopenharmony_ci
119162306a36Sopenharmony_ci		irq_set_affinity_hint(pci_irq_vector(pdev, irq), NULL);
119262306a36Sopenharmony_ci		free_cpumask_var(nic->affinity_mask[irq]);
119362306a36Sopenharmony_ci
119462306a36Sopenharmony_ci		if (irq < NICVF_INTR_ID_SQ)
119562306a36Sopenharmony_ci			free_irq(pci_irq_vector(pdev, irq), nic->napi[irq]);
119662306a36Sopenharmony_ci		else
119762306a36Sopenharmony_ci			free_irq(pci_irq_vector(pdev, irq), nic);
119862306a36Sopenharmony_ci
119962306a36Sopenharmony_ci		nic->irq_allocated[irq] = false;
120062306a36Sopenharmony_ci	}
120162306a36Sopenharmony_ci
120262306a36Sopenharmony_ci	/* Disable MSI-X */
120362306a36Sopenharmony_ci	pci_free_irq_vectors(pdev);
120462306a36Sopenharmony_ci	nic->num_vec = 0;
120562306a36Sopenharmony_ci}
120662306a36Sopenharmony_ci
120762306a36Sopenharmony_ci/* Initialize MSIX vectors and register MISC interrupt.
120862306a36Sopenharmony_ci * Send READY message to PF to check if its alive
120962306a36Sopenharmony_ci */
121062306a36Sopenharmony_cistatic int nicvf_register_misc_interrupt(struct nicvf *nic)
121162306a36Sopenharmony_ci{
121262306a36Sopenharmony_ci	int ret = 0;
121362306a36Sopenharmony_ci	int irq = NICVF_INTR_ID_MISC;
121462306a36Sopenharmony_ci
121562306a36Sopenharmony_ci	/* Return if mailbox interrupt is already registered */
121662306a36Sopenharmony_ci	if (nic->pdev->msix_enabled)
121762306a36Sopenharmony_ci		return 0;
121862306a36Sopenharmony_ci
121962306a36Sopenharmony_ci	/* Enable MSI-X */
122062306a36Sopenharmony_ci	nic->num_vec = pci_msix_vec_count(nic->pdev);
122162306a36Sopenharmony_ci	ret = pci_alloc_irq_vectors(nic->pdev, nic->num_vec, nic->num_vec,
122262306a36Sopenharmony_ci				    PCI_IRQ_MSIX);
122362306a36Sopenharmony_ci	if (ret < 0) {
122462306a36Sopenharmony_ci		netdev_err(nic->netdev,
122562306a36Sopenharmony_ci			   "Req for #%d msix vectors failed\n", nic->num_vec);
122662306a36Sopenharmony_ci		return ret;
122762306a36Sopenharmony_ci	}
122862306a36Sopenharmony_ci
122962306a36Sopenharmony_ci	sprintf(nic->irq_name[irq], "%s Mbox", "NICVF");
123062306a36Sopenharmony_ci	/* Register Misc interrupt */
123162306a36Sopenharmony_ci	ret = request_irq(pci_irq_vector(nic->pdev, irq),
123262306a36Sopenharmony_ci			  nicvf_misc_intr_handler, 0, nic->irq_name[irq], nic);
123362306a36Sopenharmony_ci
123462306a36Sopenharmony_ci	if (ret)
123562306a36Sopenharmony_ci		return ret;
123662306a36Sopenharmony_ci	nic->irq_allocated[irq] = true;
123762306a36Sopenharmony_ci
123862306a36Sopenharmony_ci	/* Enable mailbox interrupt */
123962306a36Sopenharmony_ci	nicvf_enable_intr(nic, NICVF_INTR_MBOX, 0);
124062306a36Sopenharmony_ci
124162306a36Sopenharmony_ci	/* Check if VF is able to communicate with PF */
124262306a36Sopenharmony_ci	if (!nicvf_check_pf_ready(nic)) {
124362306a36Sopenharmony_ci		nicvf_disable_intr(nic, NICVF_INTR_MBOX, 0);
124462306a36Sopenharmony_ci		nicvf_unregister_interrupts(nic);
124562306a36Sopenharmony_ci		return -EIO;
124662306a36Sopenharmony_ci	}
124762306a36Sopenharmony_ci
124862306a36Sopenharmony_ci	return 0;
124962306a36Sopenharmony_ci}
125062306a36Sopenharmony_ci
125162306a36Sopenharmony_cistatic netdev_tx_t nicvf_xmit(struct sk_buff *skb, struct net_device *netdev)
125262306a36Sopenharmony_ci{
125362306a36Sopenharmony_ci	struct nicvf *nic = netdev_priv(netdev);
125462306a36Sopenharmony_ci	int qid = skb_get_queue_mapping(skb);
125562306a36Sopenharmony_ci	struct netdev_queue *txq = netdev_get_tx_queue(netdev, qid);
125662306a36Sopenharmony_ci	struct nicvf *snic;
125762306a36Sopenharmony_ci	struct snd_queue *sq;
125862306a36Sopenharmony_ci	int tmp;
125962306a36Sopenharmony_ci
126062306a36Sopenharmony_ci	/* Check for minimum packet length */
126162306a36Sopenharmony_ci	if (skb->len <= ETH_HLEN) {
126262306a36Sopenharmony_ci		dev_kfree_skb(skb);
126362306a36Sopenharmony_ci		return NETDEV_TX_OK;
126462306a36Sopenharmony_ci	}
126562306a36Sopenharmony_ci
126662306a36Sopenharmony_ci	/* In XDP case, initial HW tx queues are used for XDP,
126762306a36Sopenharmony_ci	 * but stack's queue mapping starts at '0', so skip the
126862306a36Sopenharmony_ci	 * Tx queues attached to Rx queues for XDP.
126962306a36Sopenharmony_ci	 */
127062306a36Sopenharmony_ci	if (nic->xdp_prog)
127162306a36Sopenharmony_ci		qid += nic->xdp_tx_queues;
127262306a36Sopenharmony_ci
127362306a36Sopenharmony_ci	snic = nic;
127462306a36Sopenharmony_ci	/* Get secondary Qset's SQ structure */
127562306a36Sopenharmony_ci	if (qid >= MAX_SND_QUEUES_PER_QS) {
127662306a36Sopenharmony_ci		tmp = qid / MAX_SND_QUEUES_PER_QS;
127762306a36Sopenharmony_ci		snic = (struct nicvf *)nic->snicvf[tmp - 1];
127862306a36Sopenharmony_ci		if (!snic) {
127962306a36Sopenharmony_ci			netdev_warn(nic->netdev,
128062306a36Sopenharmony_ci				    "Secondary Qset#%d's ptr not initialized\n",
128162306a36Sopenharmony_ci				    tmp - 1);
128262306a36Sopenharmony_ci			dev_kfree_skb(skb);
128362306a36Sopenharmony_ci			return NETDEV_TX_OK;
128462306a36Sopenharmony_ci		}
128562306a36Sopenharmony_ci		qid = qid % MAX_SND_QUEUES_PER_QS;
128662306a36Sopenharmony_ci	}
128762306a36Sopenharmony_ci
128862306a36Sopenharmony_ci	sq = &snic->qs->sq[qid];
128962306a36Sopenharmony_ci	if (!netif_tx_queue_stopped(txq) &&
129062306a36Sopenharmony_ci	    !nicvf_sq_append_skb(snic, sq, skb, qid)) {
129162306a36Sopenharmony_ci		netif_tx_stop_queue(txq);
129262306a36Sopenharmony_ci
129362306a36Sopenharmony_ci		/* Barrier, so that stop_queue visible to other cpus */
129462306a36Sopenharmony_ci		smp_mb();
129562306a36Sopenharmony_ci
129662306a36Sopenharmony_ci		/* Check again, incase another cpu freed descriptors */
129762306a36Sopenharmony_ci		if (atomic_read(&sq->free_cnt) > MIN_SQ_DESC_PER_PKT_XMIT) {
129862306a36Sopenharmony_ci			netif_tx_wake_queue(txq);
129962306a36Sopenharmony_ci		} else {
130062306a36Sopenharmony_ci			this_cpu_inc(nic->drv_stats->txq_stop);
130162306a36Sopenharmony_ci			netif_warn(nic, tx_err, netdev,
130262306a36Sopenharmony_ci				   "Transmit ring full, stopping SQ%d\n", qid);
130362306a36Sopenharmony_ci		}
130462306a36Sopenharmony_ci		return NETDEV_TX_BUSY;
130562306a36Sopenharmony_ci	}
130662306a36Sopenharmony_ci
130762306a36Sopenharmony_ci	return NETDEV_TX_OK;
130862306a36Sopenharmony_ci}
130962306a36Sopenharmony_ci
131062306a36Sopenharmony_cistatic inline void nicvf_free_cq_poll(struct nicvf *nic)
131162306a36Sopenharmony_ci{
131262306a36Sopenharmony_ci	struct nicvf_cq_poll *cq_poll;
131362306a36Sopenharmony_ci	int qidx;
131462306a36Sopenharmony_ci
131562306a36Sopenharmony_ci	for (qidx = 0; qidx < nic->qs->cq_cnt; qidx++) {
131662306a36Sopenharmony_ci		cq_poll = nic->napi[qidx];
131762306a36Sopenharmony_ci		if (!cq_poll)
131862306a36Sopenharmony_ci			continue;
131962306a36Sopenharmony_ci		nic->napi[qidx] = NULL;
132062306a36Sopenharmony_ci		kfree(cq_poll);
132162306a36Sopenharmony_ci	}
132262306a36Sopenharmony_ci}
132362306a36Sopenharmony_ci
132462306a36Sopenharmony_ciint nicvf_stop(struct net_device *netdev)
132562306a36Sopenharmony_ci{
132662306a36Sopenharmony_ci	int irq, qidx;
132762306a36Sopenharmony_ci	struct nicvf *nic = netdev_priv(netdev);
132862306a36Sopenharmony_ci	struct queue_set *qs = nic->qs;
132962306a36Sopenharmony_ci	struct nicvf_cq_poll *cq_poll = NULL;
133062306a36Sopenharmony_ci	union nic_mbx mbx = {};
133162306a36Sopenharmony_ci
133262306a36Sopenharmony_ci	/* wait till all queued set_rx_mode tasks completes */
133362306a36Sopenharmony_ci	if (nic->nicvf_rx_mode_wq) {
133462306a36Sopenharmony_ci		cancel_delayed_work_sync(&nic->link_change_work);
133562306a36Sopenharmony_ci		drain_workqueue(nic->nicvf_rx_mode_wq);
133662306a36Sopenharmony_ci	}
133762306a36Sopenharmony_ci
133862306a36Sopenharmony_ci	mbx.msg.msg = NIC_MBOX_MSG_SHUTDOWN;
133962306a36Sopenharmony_ci	nicvf_send_msg_to_pf(nic, &mbx);
134062306a36Sopenharmony_ci
134162306a36Sopenharmony_ci	netif_carrier_off(netdev);
134262306a36Sopenharmony_ci	netif_tx_stop_all_queues(nic->netdev);
134362306a36Sopenharmony_ci	nic->link_up = false;
134462306a36Sopenharmony_ci
134562306a36Sopenharmony_ci	/* Teardown secondary qsets first */
134662306a36Sopenharmony_ci	if (!nic->sqs_mode) {
134762306a36Sopenharmony_ci		for (qidx = 0; qidx < nic->sqs_count; qidx++) {
134862306a36Sopenharmony_ci			if (!nic->snicvf[qidx])
134962306a36Sopenharmony_ci				continue;
135062306a36Sopenharmony_ci			nicvf_stop(nic->snicvf[qidx]->netdev);
135162306a36Sopenharmony_ci			nic->snicvf[qidx] = NULL;
135262306a36Sopenharmony_ci		}
135362306a36Sopenharmony_ci	}
135462306a36Sopenharmony_ci
135562306a36Sopenharmony_ci	/* Disable RBDR & QS error interrupts */
135662306a36Sopenharmony_ci	for (qidx = 0; qidx < qs->rbdr_cnt; qidx++) {
135762306a36Sopenharmony_ci		nicvf_disable_intr(nic, NICVF_INTR_RBDR, qidx);
135862306a36Sopenharmony_ci		nicvf_clear_intr(nic, NICVF_INTR_RBDR, qidx);
135962306a36Sopenharmony_ci	}
136062306a36Sopenharmony_ci	nicvf_disable_intr(nic, NICVF_INTR_QS_ERR, 0);
136162306a36Sopenharmony_ci	nicvf_clear_intr(nic, NICVF_INTR_QS_ERR, 0);
136262306a36Sopenharmony_ci
136362306a36Sopenharmony_ci	/* Wait for pending IRQ handlers to finish */
136462306a36Sopenharmony_ci	for (irq = 0; irq < nic->num_vec; irq++)
136562306a36Sopenharmony_ci		synchronize_irq(pci_irq_vector(nic->pdev, irq));
136662306a36Sopenharmony_ci
136762306a36Sopenharmony_ci	tasklet_kill(&nic->rbdr_task);
136862306a36Sopenharmony_ci	tasklet_kill(&nic->qs_err_task);
136962306a36Sopenharmony_ci	if (nic->rb_work_scheduled)
137062306a36Sopenharmony_ci		cancel_delayed_work_sync(&nic->rbdr_work);
137162306a36Sopenharmony_ci
137262306a36Sopenharmony_ci	for (qidx = 0; qidx < nic->qs->cq_cnt; qidx++) {
137362306a36Sopenharmony_ci		cq_poll = nic->napi[qidx];
137462306a36Sopenharmony_ci		if (!cq_poll)
137562306a36Sopenharmony_ci			continue;
137662306a36Sopenharmony_ci		napi_synchronize(&cq_poll->napi);
137762306a36Sopenharmony_ci		/* CQ intr is enabled while napi_complete,
137862306a36Sopenharmony_ci		 * so disable it now
137962306a36Sopenharmony_ci		 */
138062306a36Sopenharmony_ci		nicvf_disable_intr(nic, NICVF_INTR_CQ, qidx);
138162306a36Sopenharmony_ci		nicvf_clear_intr(nic, NICVF_INTR_CQ, qidx);
138262306a36Sopenharmony_ci		napi_disable(&cq_poll->napi);
138362306a36Sopenharmony_ci		netif_napi_del(&cq_poll->napi);
138462306a36Sopenharmony_ci	}
138562306a36Sopenharmony_ci
138662306a36Sopenharmony_ci	netif_tx_disable(netdev);
138762306a36Sopenharmony_ci
138862306a36Sopenharmony_ci	for (qidx = 0; qidx < netdev->num_tx_queues; qidx++)
138962306a36Sopenharmony_ci		netdev_tx_reset_queue(netdev_get_tx_queue(netdev, qidx));
139062306a36Sopenharmony_ci
139162306a36Sopenharmony_ci	/* Free resources */
139262306a36Sopenharmony_ci	nicvf_config_data_transfer(nic, false);
139362306a36Sopenharmony_ci
139462306a36Sopenharmony_ci	/* Disable HW Qset */
139562306a36Sopenharmony_ci	nicvf_qset_config(nic, false);
139662306a36Sopenharmony_ci
139762306a36Sopenharmony_ci	/* disable mailbox interrupt */
139862306a36Sopenharmony_ci	nicvf_disable_intr(nic, NICVF_INTR_MBOX, 0);
139962306a36Sopenharmony_ci
140062306a36Sopenharmony_ci	nicvf_unregister_interrupts(nic);
140162306a36Sopenharmony_ci
140262306a36Sopenharmony_ci	nicvf_free_cq_poll(nic);
140362306a36Sopenharmony_ci
140462306a36Sopenharmony_ci	/* Free any pending SKB saved to receive timestamp */
140562306a36Sopenharmony_ci	if (nic->ptp_skb) {
140662306a36Sopenharmony_ci		dev_kfree_skb_any(nic->ptp_skb);
140762306a36Sopenharmony_ci		nic->ptp_skb = NULL;
140862306a36Sopenharmony_ci	}
140962306a36Sopenharmony_ci
141062306a36Sopenharmony_ci	/* Clear multiqset info */
141162306a36Sopenharmony_ci	nic->pnicvf = nic;
141262306a36Sopenharmony_ci
141362306a36Sopenharmony_ci	return 0;
141462306a36Sopenharmony_ci}
141562306a36Sopenharmony_ci
141662306a36Sopenharmony_cistatic int nicvf_config_hw_rx_tstamp(struct nicvf *nic, bool enable)
141762306a36Sopenharmony_ci{
141862306a36Sopenharmony_ci	union nic_mbx mbx = {};
141962306a36Sopenharmony_ci
142062306a36Sopenharmony_ci	mbx.ptp.msg = NIC_MBOX_MSG_PTP_CFG;
142162306a36Sopenharmony_ci	mbx.ptp.enable = enable;
142262306a36Sopenharmony_ci
142362306a36Sopenharmony_ci	return nicvf_send_msg_to_pf(nic, &mbx);
142462306a36Sopenharmony_ci}
142562306a36Sopenharmony_ci
142662306a36Sopenharmony_cistatic int nicvf_update_hw_max_frs(struct nicvf *nic, int mtu)
142762306a36Sopenharmony_ci{
142862306a36Sopenharmony_ci	union nic_mbx mbx = {};
142962306a36Sopenharmony_ci
143062306a36Sopenharmony_ci	mbx.frs.msg = NIC_MBOX_MSG_SET_MAX_FRS;
143162306a36Sopenharmony_ci	mbx.frs.max_frs = mtu;
143262306a36Sopenharmony_ci	mbx.frs.vf_id = nic->vf_id;
143362306a36Sopenharmony_ci
143462306a36Sopenharmony_ci	return nicvf_send_msg_to_pf(nic, &mbx);
143562306a36Sopenharmony_ci}
143662306a36Sopenharmony_ci
143762306a36Sopenharmony_cistatic void nicvf_link_status_check_task(struct work_struct *work_arg)
143862306a36Sopenharmony_ci{
143962306a36Sopenharmony_ci	struct nicvf *nic = container_of(work_arg,
144062306a36Sopenharmony_ci					 struct nicvf,
144162306a36Sopenharmony_ci					 link_change_work.work);
144262306a36Sopenharmony_ci	union nic_mbx mbx = {};
144362306a36Sopenharmony_ci	mbx.msg.msg = NIC_MBOX_MSG_BGX_LINK_CHANGE;
144462306a36Sopenharmony_ci	nicvf_send_msg_to_pf(nic, &mbx);
144562306a36Sopenharmony_ci	queue_delayed_work(nic->nicvf_rx_mode_wq,
144662306a36Sopenharmony_ci			   &nic->link_change_work, 2 * HZ);
144762306a36Sopenharmony_ci}
144862306a36Sopenharmony_ci
144962306a36Sopenharmony_ciint nicvf_open(struct net_device *netdev)
145062306a36Sopenharmony_ci{
145162306a36Sopenharmony_ci	int cpu, err, qidx;
145262306a36Sopenharmony_ci	struct nicvf *nic = netdev_priv(netdev);
145362306a36Sopenharmony_ci	struct queue_set *qs = nic->qs;
145462306a36Sopenharmony_ci	struct nicvf_cq_poll *cq_poll = NULL;
145562306a36Sopenharmony_ci
145662306a36Sopenharmony_ci	/* wait till all queued set_rx_mode tasks completes if any */
145762306a36Sopenharmony_ci	if (nic->nicvf_rx_mode_wq)
145862306a36Sopenharmony_ci		drain_workqueue(nic->nicvf_rx_mode_wq);
145962306a36Sopenharmony_ci
146062306a36Sopenharmony_ci	netif_carrier_off(netdev);
146162306a36Sopenharmony_ci
146262306a36Sopenharmony_ci	err = nicvf_register_misc_interrupt(nic);
146362306a36Sopenharmony_ci	if (err)
146462306a36Sopenharmony_ci		return err;
146562306a36Sopenharmony_ci
146662306a36Sopenharmony_ci	/* Register NAPI handler for processing CQEs */
146762306a36Sopenharmony_ci	for (qidx = 0; qidx < qs->cq_cnt; qidx++) {
146862306a36Sopenharmony_ci		cq_poll = kzalloc(sizeof(*cq_poll), GFP_KERNEL);
146962306a36Sopenharmony_ci		if (!cq_poll) {
147062306a36Sopenharmony_ci			err = -ENOMEM;
147162306a36Sopenharmony_ci			goto napi_del;
147262306a36Sopenharmony_ci		}
147362306a36Sopenharmony_ci		cq_poll->cq_idx = qidx;
147462306a36Sopenharmony_ci		cq_poll->nicvf = nic;
147562306a36Sopenharmony_ci		netif_napi_add(netdev, &cq_poll->napi, nicvf_poll);
147662306a36Sopenharmony_ci		napi_enable(&cq_poll->napi);
147762306a36Sopenharmony_ci		nic->napi[qidx] = cq_poll;
147862306a36Sopenharmony_ci	}
147962306a36Sopenharmony_ci
148062306a36Sopenharmony_ci	/* Check if we got MAC address from PF or else generate a radom MAC */
148162306a36Sopenharmony_ci	if (!nic->sqs_mode && is_zero_ether_addr(netdev->dev_addr)) {
148262306a36Sopenharmony_ci		eth_hw_addr_random(netdev);
148362306a36Sopenharmony_ci		nicvf_hw_set_mac_addr(nic, netdev);
148462306a36Sopenharmony_ci	}
148562306a36Sopenharmony_ci
148662306a36Sopenharmony_ci	if (nic->set_mac_pending) {
148762306a36Sopenharmony_ci		nic->set_mac_pending = false;
148862306a36Sopenharmony_ci		nicvf_hw_set_mac_addr(nic, netdev);
148962306a36Sopenharmony_ci	}
149062306a36Sopenharmony_ci
149162306a36Sopenharmony_ci	/* Init tasklet for handling Qset err interrupt */
149262306a36Sopenharmony_ci	tasklet_setup(&nic->qs_err_task, nicvf_handle_qs_err);
149362306a36Sopenharmony_ci
149462306a36Sopenharmony_ci	/* Init RBDR tasklet which will refill RBDR */
149562306a36Sopenharmony_ci	tasklet_setup(&nic->rbdr_task, nicvf_rbdr_task);
149662306a36Sopenharmony_ci	INIT_DELAYED_WORK(&nic->rbdr_work, nicvf_rbdr_work);
149762306a36Sopenharmony_ci
149862306a36Sopenharmony_ci	/* Configure CPI alorithm */
149962306a36Sopenharmony_ci	nic->cpi_alg = cpi_alg;
150062306a36Sopenharmony_ci	if (!nic->sqs_mode)
150162306a36Sopenharmony_ci		nicvf_config_cpi(nic);
150262306a36Sopenharmony_ci
150362306a36Sopenharmony_ci	nicvf_request_sqs(nic);
150462306a36Sopenharmony_ci	if (nic->sqs_mode)
150562306a36Sopenharmony_ci		nicvf_get_primary_vf_struct(nic);
150662306a36Sopenharmony_ci
150762306a36Sopenharmony_ci	/* Configure PTP timestamp */
150862306a36Sopenharmony_ci	if (nic->ptp_clock)
150962306a36Sopenharmony_ci		nicvf_config_hw_rx_tstamp(nic, nic->hw_rx_tstamp);
151062306a36Sopenharmony_ci	atomic_set(&nic->tx_ptp_skbs, 0);
151162306a36Sopenharmony_ci	nic->ptp_skb = NULL;
151262306a36Sopenharmony_ci
151362306a36Sopenharmony_ci	/* Configure receive side scaling and MTU */
151462306a36Sopenharmony_ci	if (!nic->sqs_mode) {
151562306a36Sopenharmony_ci		nicvf_rss_init(nic);
151662306a36Sopenharmony_ci		err = nicvf_update_hw_max_frs(nic, netdev->mtu);
151762306a36Sopenharmony_ci		if (err)
151862306a36Sopenharmony_ci			goto cleanup;
151962306a36Sopenharmony_ci
152062306a36Sopenharmony_ci		/* Clear percpu stats */
152162306a36Sopenharmony_ci		for_each_possible_cpu(cpu)
152262306a36Sopenharmony_ci			memset(per_cpu_ptr(nic->drv_stats, cpu), 0,
152362306a36Sopenharmony_ci			       sizeof(struct nicvf_drv_stats));
152462306a36Sopenharmony_ci	}
152562306a36Sopenharmony_ci
152662306a36Sopenharmony_ci	err = nicvf_register_interrupts(nic);
152762306a36Sopenharmony_ci	if (err)
152862306a36Sopenharmony_ci		goto cleanup;
152962306a36Sopenharmony_ci
153062306a36Sopenharmony_ci	/* Initialize the queues */
153162306a36Sopenharmony_ci	err = nicvf_init_resources(nic);
153262306a36Sopenharmony_ci	if (err)
153362306a36Sopenharmony_ci		goto cleanup;
153462306a36Sopenharmony_ci
153562306a36Sopenharmony_ci	/* Make sure queue initialization is written */
153662306a36Sopenharmony_ci	wmb();
153762306a36Sopenharmony_ci
153862306a36Sopenharmony_ci	nicvf_reg_write(nic, NIC_VF_INT, -1);
153962306a36Sopenharmony_ci	/* Enable Qset err interrupt */
154062306a36Sopenharmony_ci	nicvf_enable_intr(nic, NICVF_INTR_QS_ERR, 0);
154162306a36Sopenharmony_ci
154262306a36Sopenharmony_ci	/* Enable completion queue interrupt */
154362306a36Sopenharmony_ci	for (qidx = 0; qidx < qs->cq_cnt; qidx++)
154462306a36Sopenharmony_ci		nicvf_enable_intr(nic, NICVF_INTR_CQ, qidx);
154562306a36Sopenharmony_ci
154662306a36Sopenharmony_ci	/* Enable RBDR threshold interrupt */
154762306a36Sopenharmony_ci	for (qidx = 0; qidx < qs->rbdr_cnt; qidx++)
154862306a36Sopenharmony_ci		nicvf_enable_intr(nic, NICVF_INTR_RBDR, qidx);
154962306a36Sopenharmony_ci
155062306a36Sopenharmony_ci	/* Send VF config done msg to PF */
155162306a36Sopenharmony_ci	nicvf_send_cfg_done(nic);
155262306a36Sopenharmony_ci
155362306a36Sopenharmony_ci	if (nic->nicvf_rx_mode_wq) {
155462306a36Sopenharmony_ci		INIT_DELAYED_WORK(&nic->link_change_work,
155562306a36Sopenharmony_ci				  nicvf_link_status_check_task);
155662306a36Sopenharmony_ci		queue_delayed_work(nic->nicvf_rx_mode_wq,
155762306a36Sopenharmony_ci				   &nic->link_change_work, 0);
155862306a36Sopenharmony_ci	}
155962306a36Sopenharmony_ci
156062306a36Sopenharmony_ci	return 0;
156162306a36Sopenharmony_cicleanup:
156262306a36Sopenharmony_ci	nicvf_disable_intr(nic, NICVF_INTR_MBOX, 0);
156362306a36Sopenharmony_ci	nicvf_unregister_interrupts(nic);
156462306a36Sopenharmony_ci	tasklet_kill(&nic->qs_err_task);
156562306a36Sopenharmony_ci	tasklet_kill(&nic->rbdr_task);
156662306a36Sopenharmony_cinapi_del:
156762306a36Sopenharmony_ci	for (qidx = 0; qidx < qs->cq_cnt; qidx++) {
156862306a36Sopenharmony_ci		cq_poll = nic->napi[qidx];
156962306a36Sopenharmony_ci		if (!cq_poll)
157062306a36Sopenharmony_ci			continue;
157162306a36Sopenharmony_ci		napi_disable(&cq_poll->napi);
157262306a36Sopenharmony_ci		netif_napi_del(&cq_poll->napi);
157362306a36Sopenharmony_ci	}
157462306a36Sopenharmony_ci	nicvf_free_cq_poll(nic);
157562306a36Sopenharmony_ci	return err;
157662306a36Sopenharmony_ci}
157762306a36Sopenharmony_ci
157862306a36Sopenharmony_cistatic int nicvf_change_mtu(struct net_device *netdev, int new_mtu)
157962306a36Sopenharmony_ci{
158062306a36Sopenharmony_ci	struct nicvf *nic = netdev_priv(netdev);
158162306a36Sopenharmony_ci	int orig_mtu = netdev->mtu;
158262306a36Sopenharmony_ci
158362306a36Sopenharmony_ci	/* For now just support only the usual MTU sized frames,
158462306a36Sopenharmony_ci	 * plus some headroom for VLAN, QinQ.
158562306a36Sopenharmony_ci	 */
158662306a36Sopenharmony_ci	if (nic->xdp_prog && new_mtu > MAX_XDP_MTU) {
158762306a36Sopenharmony_ci		netdev_warn(netdev, "Jumbo frames not yet supported with XDP, current MTU %d.\n",
158862306a36Sopenharmony_ci			    netdev->mtu);
158962306a36Sopenharmony_ci		return -EINVAL;
159062306a36Sopenharmony_ci	}
159162306a36Sopenharmony_ci
159262306a36Sopenharmony_ci	netdev->mtu = new_mtu;
159362306a36Sopenharmony_ci
159462306a36Sopenharmony_ci	if (!netif_running(netdev))
159562306a36Sopenharmony_ci		return 0;
159662306a36Sopenharmony_ci
159762306a36Sopenharmony_ci	if (nicvf_update_hw_max_frs(nic, new_mtu)) {
159862306a36Sopenharmony_ci		netdev->mtu = orig_mtu;
159962306a36Sopenharmony_ci		return -EINVAL;
160062306a36Sopenharmony_ci	}
160162306a36Sopenharmony_ci
160262306a36Sopenharmony_ci	return 0;
160362306a36Sopenharmony_ci}
160462306a36Sopenharmony_ci
160562306a36Sopenharmony_cistatic int nicvf_set_mac_address(struct net_device *netdev, void *p)
160662306a36Sopenharmony_ci{
160762306a36Sopenharmony_ci	struct sockaddr *addr = p;
160862306a36Sopenharmony_ci	struct nicvf *nic = netdev_priv(netdev);
160962306a36Sopenharmony_ci
161062306a36Sopenharmony_ci	if (!is_valid_ether_addr(addr->sa_data))
161162306a36Sopenharmony_ci		return -EADDRNOTAVAIL;
161262306a36Sopenharmony_ci
161362306a36Sopenharmony_ci	eth_hw_addr_set(netdev, addr->sa_data);
161462306a36Sopenharmony_ci
161562306a36Sopenharmony_ci	if (nic->pdev->msix_enabled) {
161662306a36Sopenharmony_ci		if (nicvf_hw_set_mac_addr(nic, netdev))
161762306a36Sopenharmony_ci			return -EBUSY;
161862306a36Sopenharmony_ci	} else {
161962306a36Sopenharmony_ci		nic->set_mac_pending = true;
162062306a36Sopenharmony_ci	}
162162306a36Sopenharmony_ci
162262306a36Sopenharmony_ci	return 0;
162362306a36Sopenharmony_ci}
162462306a36Sopenharmony_ci
162562306a36Sopenharmony_civoid nicvf_update_lmac_stats(struct nicvf *nic)
162662306a36Sopenharmony_ci{
162762306a36Sopenharmony_ci	int stat = 0;
162862306a36Sopenharmony_ci	union nic_mbx mbx = {};
162962306a36Sopenharmony_ci
163062306a36Sopenharmony_ci	if (!netif_running(nic->netdev))
163162306a36Sopenharmony_ci		return;
163262306a36Sopenharmony_ci
163362306a36Sopenharmony_ci	mbx.bgx_stats.msg = NIC_MBOX_MSG_BGX_STATS;
163462306a36Sopenharmony_ci	mbx.bgx_stats.vf_id = nic->vf_id;
163562306a36Sopenharmony_ci	/* Rx stats */
163662306a36Sopenharmony_ci	mbx.bgx_stats.rx = 1;
163762306a36Sopenharmony_ci	while (stat < BGX_RX_STATS_COUNT) {
163862306a36Sopenharmony_ci		mbx.bgx_stats.idx = stat;
163962306a36Sopenharmony_ci		if (nicvf_send_msg_to_pf(nic, &mbx))
164062306a36Sopenharmony_ci			return;
164162306a36Sopenharmony_ci		stat++;
164262306a36Sopenharmony_ci	}
164362306a36Sopenharmony_ci
164462306a36Sopenharmony_ci	stat = 0;
164562306a36Sopenharmony_ci
164662306a36Sopenharmony_ci	/* Tx stats */
164762306a36Sopenharmony_ci	mbx.bgx_stats.rx = 0;
164862306a36Sopenharmony_ci	while (stat < BGX_TX_STATS_COUNT) {
164962306a36Sopenharmony_ci		mbx.bgx_stats.idx = stat;
165062306a36Sopenharmony_ci		if (nicvf_send_msg_to_pf(nic, &mbx))
165162306a36Sopenharmony_ci			return;
165262306a36Sopenharmony_ci		stat++;
165362306a36Sopenharmony_ci	}
165462306a36Sopenharmony_ci}
165562306a36Sopenharmony_ci
165662306a36Sopenharmony_civoid nicvf_update_stats(struct nicvf *nic)
165762306a36Sopenharmony_ci{
165862306a36Sopenharmony_ci	int qidx, cpu;
165962306a36Sopenharmony_ci	u64 tmp_stats = 0;
166062306a36Sopenharmony_ci	struct nicvf_hw_stats *stats = &nic->hw_stats;
166162306a36Sopenharmony_ci	struct nicvf_drv_stats *drv_stats;
166262306a36Sopenharmony_ci	struct queue_set *qs = nic->qs;
166362306a36Sopenharmony_ci
166462306a36Sopenharmony_ci#define GET_RX_STATS(reg) \
166562306a36Sopenharmony_ci	nicvf_reg_read(nic, NIC_VNIC_RX_STAT_0_13 | (reg << 3))
166662306a36Sopenharmony_ci#define GET_TX_STATS(reg) \
166762306a36Sopenharmony_ci	nicvf_reg_read(nic, NIC_VNIC_TX_STAT_0_4 | (reg << 3))
166862306a36Sopenharmony_ci
166962306a36Sopenharmony_ci	stats->rx_bytes = GET_RX_STATS(RX_OCTS);
167062306a36Sopenharmony_ci	stats->rx_ucast_frames = GET_RX_STATS(RX_UCAST);
167162306a36Sopenharmony_ci	stats->rx_bcast_frames = GET_RX_STATS(RX_BCAST);
167262306a36Sopenharmony_ci	stats->rx_mcast_frames = GET_RX_STATS(RX_MCAST);
167362306a36Sopenharmony_ci	stats->rx_fcs_errors = GET_RX_STATS(RX_FCS);
167462306a36Sopenharmony_ci	stats->rx_l2_errors = GET_RX_STATS(RX_L2ERR);
167562306a36Sopenharmony_ci	stats->rx_drop_red = GET_RX_STATS(RX_RED);
167662306a36Sopenharmony_ci	stats->rx_drop_red_bytes = GET_RX_STATS(RX_RED_OCTS);
167762306a36Sopenharmony_ci	stats->rx_drop_overrun = GET_RX_STATS(RX_ORUN);
167862306a36Sopenharmony_ci	stats->rx_drop_overrun_bytes = GET_RX_STATS(RX_ORUN_OCTS);
167962306a36Sopenharmony_ci	stats->rx_drop_bcast = GET_RX_STATS(RX_DRP_BCAST);
168062306a36Sopenharmony_ci	stats->rx_drop_mcast = GET_RX_STATS(RX_DRP_MCAST);
168162306a36Sopenharmony_ci	stats->rx_drop_l3_bcast = GET_RX_STATS(RX_DRP_L3BCAST);
168262306a36Sopenharmony_ci	stats->rx_drop_l3_mcast = GET_RX_STATS(RX_DRP_L3MCAST);
168362306a36Sopenharmony_ci
168462306a36Sopenharmony_ci	stats->tx_bytes = GET_TX_STATS(TX_OCTS);
168562306a36Sopenharmony_ci	stats->tx_ucast_frames = GET_TX_STATS(TX_UCAST);
168662306a36Sopenharmony_ci	stats->tx_bcast_frames = GET_TX_STATS(TX_BCAST);
168762306a36Sopenharmony_ci	stats->tx_mcast_frames = GET_TX_STATS(TX_MCAST);
168862306a36Sopenharmony_ci	stats->tx_drops = GET_TX_STATS(TX_DROP);
168962306a36Sopenharmony_ci
169062306a36Sopenharmony_ci	/* On T88 pass 2.0, the dummy SQE added for TSO notification
169162306a36Sopenharmony_ci	 * via CQE has 'dont_send' set. Hence HW drops the pkt pointed
169262306a36Sopenharmony_ci	 * pointed by dummy SQE and results in tx_drops counter being
169362306a36Sopenharmony_ci	 * incremented. Subtracting it from tx_tso counter will give
169462306a36Sopenharmony_ci	 * exact tx_drops counter.
169562306a36Sopenharmony_ci	 */
169662306a36Sopenharmony_ci	if (nic->t88 && nic->hw_tso) {
169762306a36Sopenharmony_ci		for_each_possible_cpu(cpu) {
169862306a36Sopenharmony_ci			drv_stats = per_cpu_ptr(nic->drv_stats, cpu);
169962306a36Sopenharmony_ci			tmp_stats += drv_stats->tx_tso;
170062306a36Sopenharmony_ci		}
170162306a36Sopenharmony_ci		stats->tx_drops = tmp_stats - stats->tx_drops;
170262306a36Sopenharmony_ci	}
170362306a36Sopenharmony_ci	stats->tx_frames = stats->tx_ucast_frames +
170462306a36Sopenharmony_ci			   stats->tx_bcast_frames +
170562306a36Sopenharmony_ci			   stats->tx_mcast_frames;
170662306a36Sopenharmony_ci	stats->rx_frames = stats->rx_ucast_frames +
170762306a36Sopenharmony_ci			   stats->rx_bcast_frames +
170862306a36Sopenharmony_ci			   stats->rx_mcast_frames;
170962306a36Sopenharmony_ci	stats->rx_drops = stats->rx_drop_red +
171062306a36Sopenharmony_ci			  stats->rx_drop_overrun;
171162306a36Sopenharmony_ci
171262306a36Sopenharmony_ci	/* Update RQ and SQ stats */
171362306a36Sopenharmony_ci	for (qidx = 0; qidx < qs->rq_cnt; qidx++)
171462306a36Sopenharmony_ci		nicvf_update_rq_stats(nic, qidx);
171562306a36Sopenharmony_ci	for (qidx = 0; qidx < qs->sq_cnt; qidx++)
171662306a36Sopenharmony_ci		nicvf_update_sq_stats(nic, qidx);
171762306a36Sopenharmony_ci}
171862306a36Sopenharmony_ci
171962306a36Sopenharmony_cistatic void nicvf_get_stats64(struct net_device *netdev,
172062306a36Sopenharmony_ci			      struct rtnl_link_stats64 *stats)
172162306a36Sopenharmony_ci{
172262306a36Sopenharmony_ci	struct nicvf *nic = netdev_priv(netdev);
172362306a36Sopenharmony_ci	struct nicvf_hw_stats *hw_stats = &nic->hw_stats;
172462306a36Sopenharmony_ci
172562306a36Sopenharmony_ci	nicvf_update_stats(nic);
172662306a36Sopenharmony_ci
172762306a36Sopenharmony_ci	stats->rx_bytes = hw_stats->rx_bytes;
172862306a36Sopenharmony_ci	stats->rx_packets = hw_stats->rx_frames;
172962306a36Sopenharmony_ci	stats->rx_dropped = hw_stats->rx_drops;
173062306a36Sopenharmony_ci	stats->multicast = hw_stats->rx_mcast_frames;
173162306a36Sopenharmony_ci
173262306a36Sopenharmony_ci	stats->tx_bytes = hw_stats->tx_bytes;
173362306a36Sopenharmony_ci	stats->tx_packets = hw_stats->tx_frames;
173462306a36Sopenharmony_ci	stats->tx_dropped = hw_stats->tx_drops;
173562306a36Sopenharmony_ci
173662306a36Sopenharmony_ci}
173762306a36Sopenharmony_ci
173862306a36Sopenharmony_cistatic void nicvf_tx_timeout(struct net_device *dev, unsigned int txqueue)
173962306a36Sopenharmony_ci{
174062306a36Sopenharmony_ci	struct nicvf *nic = netdev_priv(dev);
174162306a36Sopenharmony_ci
174262306a36Sopenharmony_ci	netif_warn(nic, tx_err, dev, "Transmit timed out, resetting\n");
174362306a36Sopenharmony_ci
174462306a36Sopenharmony_ci	this_cpu_inc(nic->drv_stats->tx_timeout);
174562306a36Sopenharmony_ci	schedule_work(&nic->reset_task);
174662306a36Sopenharmony_ci}
174762306a36Sopenharmony_ci
174862306a36Sopenharmony_cistatic void nicvf_reset_task(struct work_struct *work)
174962306a36Sopenharmony_ci{
175062306a36Sopenharmony_ci	struct nicvf *nic;
175162306a36Sopenharmony_ci
175262306a36Sopenharmony_ci	nic = container_of(work, struct nicvf, reset_task);
175362306a36Sopenharmony_ci
175462306a36Sopenharmony_ci	if (!netif_running(nic->netdev))
175562306a36Sopenharmony_ci		return;
175662306a36Sopenharmony_ci
175762306a36Sopenharmony_ci	nicvf_stop(nic->netdev);
175862306a36Sopenharmony_ci	nicvf_open(nic->netdev);
175962306a36Sopenharmony_ci	netif_trans_update(nic->netdev);
176062306a36Sopenharmony_ci}
176162306a36Sopenharmony_ci
176262306a36Sopenharmony_cistatic int nicvf_config_loopback(struct nicvf *nic,
176362306a36Sopenharmony_ci				 netdev_features_t features)
176462306a36Sopenharmony_ci{
176562306a36Sopenharmony_ci	union nic_mbx mbx = {};
176662306a36Sopenharmony_ci
176762306a36Sopenharmony_ci	mbx.lbk.msg = NIC_MBOX_MSG_LOOPBACK;
176862306a36Sopenharmony_ci	mbx.lbk.vf_id = nic->vf_id;
176962306a36Sopenharmony_ci	mbx.lbk.enable = (features & NETIF_F_LOOPBACK) != 0;
177062306a36Sopenharmony_ci
177162306a36Sopenharmony_ci	return nicvf_send_msg_to_pf(nic, &mbx);
177262306a36Sopenharmony_ci}
177362306a36Sopenharmony_ci
177462306a36Sopenharmony_cistatic netdev_features_t nicvf_fix_features(struct net_device *netdev,
177562306a36Sopenharmony_ci					    netdev_features_t features)
177662306a36Sopenharmony_ci{
177762306a36Sopenharmony_ci	struct nicvf *nic = netdev_priv(netdev);
177862306a36Sopenharmony_ci
177962306a36Sopenharmony_ci	if ((features & NETIF_F_LOOPBACK) &&
178062306a36Sopenharmony_ci	    netif_running(netdev) && !nic->loopback_supported)
178162306a36Sopenharmony_ci		features &= ~NETIF_F_LOOPBACK;
178262306a36Sopenharmony_ci
178362306a36Sopenharmony_ci	return features;
178462306a36Sopenharmony_ci}
178562306a36Sopenharmony_ci
178662306a36Sopenharmony_cistatic int nicvf_set_features(struct net_device *netdev,
178762306a36Sopenharmony_ci			      netdev_features_t features)
178862306a36Sopenharmony_ci{
178962306a36Sopenharmony_ci	struct nicvf *nic = netdev_priv(netdev);
179062306a36Sopenharmony_ci	netdev_features_t changed = features ^ netdev->features;
179162306a36Sopenharmony_ci
179262306a36Sopenharmony_ci	if (changed & NETIF_F_HW_VLAN_CTAG_RX)
179362306a36Sopenharmony_ci		nicvf_config_vlan_stripping(nic, features);
179462306a36Sopenharmony_ci
179562306a36Sopenharmony_ci	if ((changed & NETIF_F_LOOPBACK) && netif_running(netdev))
179662306a36Sopenharmony_ci		return nicvf_config_loopback(nic, features);
179762306a36Sopenharmony_ci
179862306a36Sopenharmony_ci	return 0;
179962306a36Sopenharmony_ci}
180062306a36Sopenharmony_ci
180162306a36Sopenharmony_cistatic void nicvf_set_xdp_queues(struct nicvf *nic, bool bpf_attached)
180262306a36Sopenharmony_ci{
180362306a36Sopenharmony_ci	u8 cq_count, txq_count;
180462306a36Sopenharmony_ci
180562306a36Sopenharmony_ci	/* Set XDP Tx queue count same as Rx queue count */
180662306a36Sopenharmony_ci	if (!bpf_attached)
180762306a36Sopenharmony_ci		nic->xdp_tx_queues = 0;
180862306a36Sopenharmony_ci	else
180962306a36Sopenharmony_ci		nic->xdp_tx_queues = nic->rx_queues;
181062306a36Sopenharmony_ci
181162306a36Sopenharmony_ci	/* If queue count > MAX_CMP_QUEUES_PER_QS, then additional qsets
181262306a36Sopenharmony_ci	 * needs to be allocated, check how many.
181362306a36Sopenharmony_ci	 */
181462306a36Sopenharmony_ci	txq_count = nic->xdp_tx_queues + nic->tx_queues;
181562306a36Sopenharmony_ci	cq_count = max(nic->rx_queues, txq_count);
181662306a36Sopenharmony_ci	if (cq_count > MAX_CMP_QUEUES_PER_QS) {
181762306a36Sopenharmony_ci		nic->sqs_count = roundup(cq_count, MAX_CMP_QUEUES_PER_QS);
181862306a36Sopenharmony_ci		nic->sqs_count = (nic->sqs_count / MAX_CMP_QUEUES_PER_QS) - 1;
181962306a36Sopenharmony_ci	} else {
182062306a36Sopenharmony_ci		nic->sqs_count = 0;
182162306a36Sopenharmony_ci	}
182262306a36Sopenharmony_ci
182362306a36Sopenharmony_ci	/* Set primary Qset's resources */
182462306a36Sopenharmony_ci	nic->qs->rq_cnt = min_t(u8, nic->rx_queues, MAX_RCV_QUEUES_PER_QS);
182562306a36Sopenharmony_ci	nic->qs->sq_cnt = min_t(u8, txq_count, MAX_SND_QUEUES_PER_QS);
182662306a36Sopenharmony_ci	nic->qs->cq_cnt = max_t(u8, nic->qs->rq_cnt, nic->qs->sq_cnt);
182762306a36Sopenharmony_ci
182862306a36Sopenharmony_ci	/* Update stack */
182962306a36Sopenharmony_ci	nicvf_set_real_num_queues(nic->netdev, nic->tx_queues, nic->rx_queues);
183062306a36Sopenharmony_ci}
183162306a36Sopenharmony_ci
183262306a36Sopenharmony_cistatic int nicvf_xdp_setup(struct nicvf *nic, struct bpf_prog *prog)
183362306a36Sopenharmony_ci{
183462306a36Sopenharmony_ci	struct net_device *dev = nic->netdev;
183562306a36Sopenharmony_ci	bool if_up = netif_running(nic->netdev);
183662306a36Sopenharmony_ci	struct bpf_prog *old_prog;
183762306a36Sopenharmony_ci	bool bpf_attached = false;
183862306a36Sopenharmony_ci	int ret = 0;
183962306a36Sopenharmony_ci
184062306a36Sopenharmony_ci	/* For now just support only the usual MTU sized frames,
184162306a36Sopenharmony_ci	 * plus some headroom for VLAN, QinQ.
184262306a36Sopenharmony_ci	 */
184362306a36Sopenharmony_ci	if (prog && dev->mtu > MAX_XDP_MTU) {
184462306a36Sopenharmony_ci		netdev_warn(dev, "Jumbo frames not yet supported with XDP, current MTU %d.\n",
184562306a36Sopenharmony_ci			    dev->mtu);
184662306a36Sopenharmony_ci		return -EOPNOTSUPP;
184762306a36Sopenharmony_ci	}
184862306a36Sopenharmony_ci
184962306a36Sopenharmony_ci	/* ALL SQs attached to CQs i.e same as RQs, are treated as
185062306a36Sopenharmony_ci	 * XDP Tx queues and more Tx queues are allocated for
185162306a36Sopenharmony_ci	 * network stack to send pkts out.
185262306a36Sopenharmony_ci	 *
185362306a36Sopenharmony_ci	 * No of Tx queues are either same as Rx queues or whatever
185462306a36Sopenharmony_ci	 * is left in max no of queues possible.
185562306a36Sopenharmony_ci	 */
185662306a36Sopenharmony_ci	if ((nic->rx_queues + nic->tx_queues) > nic->max_queues) {
185762306a36Sopenharmony_ci		netdev_warn(dev,
185862306a36Sopenharmony_ci			    "Failed to attach BPF prog, RXQs + TXQs > Max %d\n",
185962306a36Sopenharmony_ci			    nic->max_queues);
186062306a36Sopenharmony_ci		return -ENOMEM;
186162306a36Sopenharmony_ci	}
186262306a36Sopenharmony_ci
186362306a36Sopenharmony_ci	if (if_up)
186462306a36Sopenharmony_ci		nicvf_stop(nic->netdev);
186562306a36Sopenharmony_ci
186662306a36Sopenharmony_ci	old_prog = xchg(&nic->xdp_prog, prog);
186762306a36Sopenharmony_ci	/* Detach old prog, if any */
186862306a36Sopenharmony_ci	if (old_prog)
186962306a36Sopenharmony_ci		bpf_prog_put(old_prog);
187062306a36Sopenharmony_ci
187162306a36Sopenharmony_ci	if (nic->xdp_prog) {
187262306a36Sopenharmony_ci		/* Attach BPF program */
187362306a36Sopenharmony_ci		bpf_prog_add(nic->xdp_prog, nic->rx_queues - 1);
187462306a36Sopenharmony_ci		bpf_attached = true;
187562306a36Sopenharmony_ci	}
187662306a36Sopenharmony_ci
187762306a36Sopenharmony_ci	/* Calculate Tx queues needed for XDP and network stack */
187862306a36Sopenharmony_ci	nicvf_set_xdp_queues(nic, bpf_attached);
187962306a36Sopenharmony_ci
188062306a36Sopenharmony_ci	if (if_up) {
188162306a36Sopenharmony_ci		/* Reinitialize interface, clean slate */
188262306a36Sopenharmony_ci		nicvf_open(nic->netdev);
188362306a36Sopenharmony_ci		netif_trans_update(nic->netdev);
188462306a36Sopenharmony_ci	}
188562306a36Sopenharmony_ci
188662306a36Sopenharmony_ci	return ret;
188762306a36Sopenharmony_ci}
188862306a36Sopenharmony_ci
188962306a36Sopenharmony_cistatic int nicvf_xdp(struct net_device *netdev, struct netdev_bpf *xdp)
189062306a36Sopenharmony_ci{
189162306a36Sopenharmony_ci	struct nicvf *nic = netdev_priv(netdev);
189262306a36Sopenharmony_ci
189362306a36Sopenharmony_ci	/* To avoid checks while retrieving buffer address from CQE_RX,
189462306a36Sopenharmony_ci	 * do not support XDP for T88 pass1.x silicons which are anyway
189562306a36Sopenharmony_ci	 * not in use widely.
189662306a36Sopenharmony_ci	 */
189762306a36Sopenharmony_ci	if (pass1_silicon(nic->pdev))
189862306a36Sopenharmony_ci		return -EOPNOTSUPP;
189962306a36Sopenharmony_ci
190062306a36Sopenharmony_ci	switch (xdp->command) {
190162306a36Sopenharmony_ci	case XDP_SETUP_PROG:
190262306a36Sopenharmony_ci		return nicvf_xdp_setup(nic, xdp->prog);
190362306a36Sopenharmony_ci	default:
190462306a36Sopenharmony_ci		return -EINVAL;
190562306a36Sopenharmony_ci	}
190662306a36Sopenharmony_ci}
190762306a36Sopenharmony_ci
190862306a36Sopenharmony_cistatic int nicvf_config_hwtstamp(struct net_device *netdev, struct ifreq *ifr)
190962306a36Sopenharmony_ci{
191062306a36Sopenharmony_ci	struct hwtstamp_config config;
191162306a36Sopenharmony_ci	struct nicvf *nic = netdev_priv(netdev);
191262306a36Sopenharmony_ci
191362306a36Sopenharmony_ci	if (!nic->ptp_clock)
191462306a36Sopenharmony_ci		return -ENODEV;
191562306a36Sopenharmony_ci
191662306a36Sopenharmony_ci	if (copy_from_user(&config, ifr->ifr_data, sizeof(config)))
191762306a36Sopenharmony_ci		return -EFAULT;
191862306a36Sopenharmony_ci
191962306a36Sopenharmony_ci	switch (config.tx_type) {
192062306a36Sopenharmony_ci	case HWTSTAMP_TX_OFF:
192162306a36Sopenharmony_ci	case HWTSTAMP_TX_ON:
192262306a36Sopenharmony_ci		break;
192362306a36Sopenharmony_ci	default:
192462306a36Sopenharmony_ci		return -ERANGE;
192562306a36Sopenharmony_ci	}
192662306a36Sopenharmony_ci
192762306a36Sopenharmony_ci	switch (config.rx_filter) {
192862306a36Sopenharmony_ci	case HWTSTAMP_FILTER_NONE:
192962306a36Sopenharmony_ci		nic->hw_rx_tstamp = false;
193062306a36Sopenharmony_ci		break;
193162306a36Sopenharmony_ci	case HWTSTAMP_FILTER_ALL:
193262306a36Sopenharmony_ci	case HWTSTAMP_FILTER_SOME:
193362306a36Sopenharmony_ci	case HWTSTAMP_FILTER_PTP_V1_L4_EVENT:
193462306a36Sopenharmony_ci	case HWTSTAMP_FILTER_PTP_V1_L4_SYNC:
193562306a36Sopenharmony_ci	case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ:
193662306a36Sopenharmony_ci	case HWTSTAMP_FILTER_PTP_V2_L4_EVENT:
193762306a36Sopenharmony_ci	case HWTSTAMP_FILTER_PTP_V2_L4_SYNC:
193862306a36Sopenharmony_ci	case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ:
193962306a36Sopenharmony_ci	case HWTSTAMP_FILTER_PTP_V2_L2_EVENT:
194062306a36Sopenharmony_ci	case HWTSTAMP_FILTER_PTP_V2_L2_SYNC:
194162306a36Sopenharmony_ci	case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ:
194262306a36Sopenharmony_ci	case HWTSTAMP_FILTER_PTP_V2_EVENT:
194362306a36Sopenharmony_ci	case HWTSTAMP_FILTER_PTP_V2_SYNC:
194462306a36Sopenharmony_ci	case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ:
194562306a36Sopenharmony_ci		nic->hw_rx_tstamp = true;
194662306a36Sopenharmony_ci		config.rx_filter = HWTSTAMP_FILTER_ALL;
194762306a36Sopenharmony_ci		break;
194862306a36Sopenharmony_ci	default:
194962306a36Sopenharmony_ci		return -ERANGE;
195062306a36Sopenharmony_ci	}
195162306a36Sopenharmony_ci
195262306a36Sopenharmony_ci	if (netif_running(netdev))
195362306a36Sopenharmony_ci		nicvf_config_hw_rx_tstamp(nic, nic->hw_rx_tstamp);
195462306a36Sopenharmony_ci
195562306a36Sopenharmony_ci	if (copy_to_user(ifr->ifr_data, &config, sizeof(config)))
195662306a36Sopenharmony_ci		return -EFAULT;
195762306a36Sopenharmony_ci
195862306a36Sopenharmony_ci	return 0;
195962306a36Sopenharmony_ci}
196062306a36Sopenharmony_ci
196162306a36Sopenharmony_cistatic int nicvf_ioctl(struct net_device *netdev, struct ifreq *req, int cmd)
196262306a36Sopenharmony_ci{
196362306a36Sopenharmony_ci	switch (cmd) {
196462306a36Sopenharmony_ci	case SIOCSHWTSTAMP:
196562306a36Sopenharmony_ci		return nicvf_config_hwtstamp(netdev, req);
196662306a36Sopenharmony_ci	default:
196762306a36Sopenharmony_ci		return -EOPNOTSUPP;
196862306a36Sopenharmony_ci	}
196962306a36Sopenharmony_ci}
197062306a36Sopenharmony_ci
197162306a36Sopenharmony_cistatic void __nicvf_set_rx_mode_task(u8 mode, struct xcast_addr_list *mc_addrs,
197262306a36Sopenharmony_ci				     struct nicvf *nic)
197362306a36Sopenharmony_ci{
197462306a36Sopenharmony_ci	union nic_mbx mbx = {};
197562306a36Sopenharmony_ci	int idx;
197662306a36Sopenharmony_ci
197762306a36Sopenharmony_ci	/* From the inside of VM code flow we have only 128 bits memory
197862306a36Sopenharmony_ci	 * available to send message to host's PF, so send all mc addrs
197962306a36Sopenharmony_ci	 * one by one, starting from flush command in case if kernel
198062306a36Sopenharmony_ci	 * requests to configure specific MAC filtering
198162306a36Sopenharmony_ci	 */
198262306a36Sopenharmony_ci
198362306a36Sopenharmony_ci	/* flush DMAC filters and reset RX mode */
198462306a36Sopenharmony_ci	mbx.xcast.msg = NIC_MBOX_MSG_RESET_XCAST;
198562306a36Sopenharmony_ci	if (nicvf_send_msg_to_pf(nic, &mbx) < 0)
198662306a36Sopenharmony_ci		goto free_mc;
198762306a36Sopenharmony_ci
198862306a36Sopenharmony_ci	if (mode & BGX_XCAST_MCAST_FILTER) {
198962306a36Sopenharmony_ci		/* once enabling filtering, we need to signal to PF to add
199062306a36Sopenharmony_ci		 * its' own LMAC to the filter to accept packets for it.
199162306a36Sopenharmony_ci		 */
199262306a36Sopenharmony_ci		mbx.xcast.msg = NIC_MBOX_MSG_ADD_MCAST;
199362306a36Sopenharmony_ci		mbx.xcast.mac = 0;
199462306a36Sopenharmony_ci		if (nicvf_send_msg_to_pf(nic, &mbx) < 0)
199562306a36Sopenharmony_ci			goto free_mc;
199662306a36Sopenharmony_ci	}
199762306a36Sopenharmony_ci
199862306a36Sopenharmony_ci	/* check if we have any specific MACs to be added to PF DMAC filter */
199962306a36Sopenharmony_ci	if (mc_addrs) {
200062306a36Sopenharmony_ci		/* now go through kernel list of MACs and add them one by one */
200162306a36Sopenharmony_ci		for (idx = 0; idx < mc_addrs->count; idx++) {
200262306a36Sopenharmony_ci			mbx.xcast.msg = NIC_MBOX_MSG_ADD_MCAST;
200362306a36Sopenharmony_ci			mbx.xcast.mac = mc_addrs->mc[idx];
200462306a36Sopenharmony_ci			if (nicvf_send_msg_to_pf(nic, &mbx) < 0)
200562306a36Sopenharmony_ci				goto free_mc;
200662306a36Sopenharmony_ci		}
200762306a36Sopenharmony_ci	}
200862306a36Sopenharmony_ci
200962306a36Sopenharmony_ci	/* and finally set rx mode for PF accordingly */
201062306a36Sopenharmony_ci	mbx.xcast.msg = NIC_MBOX_MSG_SET_XCAST;
201162306a36Sopenharmony_ci	mbx.xcast.mode = mode;
201262306a36Sopenharmony_ci
201362306a36Sopenharmony_ci	nicvf_send_msg_to_pf(nic, &mbx);
201462306a36Sopenharmony_cifree_mc:
201562306a36Sopenharmony_ci	kfree(mc_addrs);
201662306a36Sopenharmony_ci}
201762306a36Sopenharmony_ci
201862306a36Sopenharmony_cistatic void nicvf_set_rx_mode_task(struct work_struct *work_arg)
201962306a36Sopenharmony_ci{
202062306a36Sopenharmony_ci	struct nicvf_work *vf_work = container_of(work_arg, struct nicvf_work,
202162306a36Sopenharmony_ci						  work);
202262306a36Sopenharmony_ci	struct nicvf *nic = container_of(vf_work, struct nicvf, rx_mode_work);
202362306a36Sopenharmony_ci	u8 mode;
202462306a36Sopenharmony_ci	struct xcast_addr_list *mc;
202562306a36Sopenharmony_ci
202662306a36Sopenharmony_ci	/* Save message data locally to prevent them from
202762306a36Sopenharmony_ci	 * being overwritten by next ndo_set_rx_mode call().
202862306a36Sopenharmony_ci	 */
202962306a36Sopenharmony_ci	spin_lock_bh(&nic->rx_mode_wq_lock);
203062306a36Sopenharmony_ci	mode = vf_work->mode;
203162306a36Sopenharmony_ci	mc = vf_work->mc;
203262306a36Sopenharmony_ci	vf_work->mc = NULL;
203362306a36Sopenharmony_ci	spin_unlock_bh(&nic->rx_mode_wq_lock);
203462306a36Sopenharmony_ci
203562306a36Sopenharmony_ci	__nicvf_set_rx_mode_task(mode, mc, nic);
203662306a36Sopenharmony_ci}
203762306a36Sopenharmony_ci
203862306a36Sopenharmony_cistatic void nicvf_set_rx_mode(struct net_device *netdev)
203962306a36Sopenharmony_ci{
204062306a36Sopenharmony_ci	struct nicvf *nic = netdev_priv(netdev);
204162306a36Sopenharmony_ci	struct netdev_hw_addr *ha;
204262306a36Sopenharmony_ci	struct xcast_addr_list *mc_list = NULL;
204362306a36Sopenharmony_ci	u8 mode = 0;
204462306a36Sopenharmony_ci
204562306a36Sopenharmony_ci	if (netdev->flags & IFF_PROMISC) {
204662306a36Sopenharmony_ci		mode = BGX_XCAST_BCAST_ACCEPT | BGX_XCAST_MCAST_ACCEPT;
204762306a36Sopenharmony_ci	} else {
204862306a36Sopenharmony_ci		if (netdev->flags & IFF_BROADCAST)
204962306a36Sopenharmony_ci			mode |= BGX_XCAST_BCAST_ACCEPT;
205062306a36Sopenharmony_ci
205162306a36Sopenharmony_ci		if (netdev->flags & IFF_ALLMULTI) {
205262306a36Sopenharmony_ci			mode |= BGX_XCAST_MCAST_ACCEPT;
205362306a36Sopenharmony_ci		} else if (netdev->flags & IFF_MULTICAST) {
205462306a36Sopenharmony_ci			mode |= BGX_XCAST_MCAST_FILTER;
205562306a36Sopenharmony_ci			/* here we need to copy mc addrs */
205662306a36Sopenharmony_ci			if (netdev_mc_count(netdev)) {
205762306a36Sopenharmony_ci				mc_list = kmalloc(struct_size(mc_list, mc,
205862306a36Sopenharmony_ci							      netdev_mc_count(netdev)),
205962306a36Sopenharmony_ci						  GFP_ATOMIC);
206062306a36Sopenharmony_ci				if (unlikely(!mc_list))
206162306a36Sopenharmony_ci					return;
206262306a36Sopenharmony_ci				mc_list->count = 0;
206362306a36Sopenharmony_ci				netdev_hw_addr_list_for_each(ha, &netdev->mc) {
206462306a36Sopenharmony_ci					mc_list->mc[mc_list->count] =
206562306a36Sopenharmony_ci						ether_addr_to_u64(ha->addr);
206662306a36Sopenharmony_ci					mc_list->count++;
206762306a36Sopenharmony_ci				}
206862306a36Sopenharmony_ci			}
206962306a36Sopenharmony_ci		}
207062306a36Sopenharmony_ci	}
207162306a36Sopenharmony_ci	spin_lock(&nic->rx_mode_wq_lock);
207262306a36Sopenharmony_ci	kfree(nic->rx_mode_work.mc);
207362306a36Sopenharmony_ci	nic->rx_mode_work.mc = mc_list;
207462306a36Sopenharmony_ci	nic->rx_mode_work.mode = mode;
207562306a36Sopenharmony_ci	queue_work(nic->nicvf_rx_mode_wq, &nic->rx_mode_work.work);
207662306a36Sopenharmony_ci	spin_unlock(&nic->rx_mode_wq_lock);
207762306a36Sopenharmony_ci}
207862306a36Sopenharmony_ci
207962306a36Sopenharmony_cistatic const struct net_device_ops nicvf_netdev_ops = {
208062306a36Sopenharmony_ci	.ndo_open		= nicvf_open,
208162306a36Sopenharmony_ci	.ndo_stop		= nicvf_stop,
208262306a36Sopenharmony_ci	.ndo_start_xmit		= nicvf_xmit,
208362306a36Sopenharmony_ci	.ndo_change_mtu		= nicvf_change_mtu,
208462306a36Sopenharmony_ci	.ndo_set_mac_address	= nicvf_set_mac_address,
208562306a36Sopenharmony_ci	.ndo_get_stats64	= nicvf_get_stats64,
208662306a36Sopenharmony_ci	.ndo_tx_timeout         = nicvf_tx_timeout,
208762306a36Sopenharmony_ci	.ndo_fix_features       = nicvf_fix_features,
208862306a36Sopenharmony_ci	.ndo_set_features       = nicvf_set_features,
208962306a36Sopenharmony_ci	.ndo_bpf		= nicvf_xdp,
209062306a36Sopenharmony_ci	.ndo_eth_ioctl           = nicvf_ioctl,
209162306a36Sopenharmony_ci	.ndo_set_rx_mode        = nicvf_set_rx_mode,
209262306a36Sopenharmony_ci};
209362306a36Sopenharmony_ci
209462306a36Sopenharmony_cistatic int nicvf_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
209562306a36Sopenharmony_ci{
209662306a36Sopenharmony_ci	struct device *dev = &pdev->dev;
209762306a36Sopenharmony_ci	struct net_device *netdev;
209862306a36Sopenharmony_ci	struct nicvf *nic;
209962306a36Sopenharmony_ci	int    err, qcount;
210062306a36Sopenharmony_ci	u16    sdevid;
210162306a36Sopenharmony_ci	struct cavium_ptp *ptp_clock;
210262306a36Sopenharmony_ci
210362306a36Sopenharmony_ci	ptp_clock = cavium_ptp_get();
210462306a36Sopenharmony_ci	if (IS_ERR(ptp_clock)) {
210562306a36Sopenharmony_ci		if (PTR_ERR(ptp_clock) == -ENODEV)
210662306a36Sopenharmony_ci			/* In virtualized environment we proceed without ptp */
210762306a36Sopenharmony_ci			ptp_clock = NULL;
210862306a36Sopenharmony_ci		else
210962306a36Sopenharmony_ci			return PTR_ERR(ptp_clock);
211062306a36Sopenharmony_ci	}
211162306a36Sopenharmony_ci
211262306a36Sopenharmony_ci	err = pci_enable_device(pdev);
211362306a36Sopenharmony_ci	if (err)
211462306a36Sopenharmony_ci		return dev_err_probe(dev, err, "Failed to enable PCI device\n");
211562306a36Sopenharmony_ci
211662306a36Sopenharmony_ci	err = pci_request_regions(pdev, DRV_NAME);
211762306a36Sopenharmony_ci	if (err) {
211862306a36Sopenharmony_ci		dev_err(dev, "PCI request regions failed 0x%x\n", err);
211962306a36Sopenharmony_ci		goto err_disable_device;
212062306a36Sopenharmony_ci	}
212162306a36Sopenharmony_ci
212262306a36Sopenharmony_ci	err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(48));
212362306a36Sopenharmony_ci	if (err) {
212462306a36Sopenharmony_ci		dev_err(dev, "Unable to get usable DMA configuration\n");
212562306a36Sopenharmony_ci		goto err_release_regions;
212662306a36Sopenharmony_ci	}
212762306a36Sopenharmony_ci
212862306a36Sopenharmony_ci	qcount = netif_get_num_default_rss_queues();
212962306a36Sopenharmony_ci
213062306a36Sopenharmony_ci	/* Restrict multiqset support only for host bound VFs */
213162306a36Sopenharmony_ci	if (pdev->is_virtfn) {
213262306a36Sopenharmony_ci		/* Set max number of queues per VF */
213362306a36Sopenharmony_ci		qcount = min_t(int, num_online_cpus(),
213462306a36Sopenharmony_ci			       (MAX_SQS_PER_VF + 1) * MAX_CMP_QUEUES_PER_QS);
213562306a36Sopenharmony_ci	}
213662306a36Sopenharmony_ci
213762306a36Sopenharmony_ci	netdev = alloc_etherdev_mqs(sizeof(struct nicvf), qcount, qcount);
213862306a36Sopenharmony_ci	if (!netdev) {
213962306a36Sopenharmony_ci		err = -ENOMEM;
214062306a36Sopenharmony_ci		goto err_release_regions;
214162306a36Sopenharmony_ci	}
214262306a36Sopenharmony_ci
214362306a36Sopenharmony_ci	pci_set_drvdata(pdev, netdev);
214462306a36Sopenharmony_ci
214562306a36Sopenharmony_ci	SET_NETDEV_DEV(netdev, &pdev->dev);
214662306a36Sopenharmony_ci
214762306a36Sopenharmony_ci	nic = netdev_priv(netdev);
214862306a36Sopenharmony_ci	nic->netdev = netdev;
214962306a36Sopenharmony_ci	nic->pdev = pdev;
215062306a36Sopenharmony_ci	nic->pnicvf = nic;
215162306a36Sopenharmony_ci	nic->max_queues = qcount;
215262306a36Sopenharmony_ci	/* If no of CPUs are too low, there won't be any queues left
215362306a36Sopenharmony_ci	 * for XDP_TX, hence double it.
215462306a36Sopenharmony_ci	 */
215562306a36Sopenharmony_ci	if (!nic->t88)
215662306a36Sopenharmony_ci		nic->max_queues *= 2;
215762306a36Sopenharmony_ci	nic->ptp_clock = ptp_clock;
215862306a36Sopenharmony_ci
215962306a36Sopenharmony_ci	/* Initialize mutex that serializes usage of VF's mailbox */
216062306a36Sopenharmony_ci	mutex_init(&nic->rx_mode_mtx);
216162306a36Sopenharmony_ci
216262306a36Sopenharmony_ci	/* MAP VF's configuration registers */
216362306a36Sopenharmony_ci	nic->reg_base = pcim_iomap(pdev, PCI_CFG_REG_BAR_NUM, 0);
216462306a36Sopenharmony_ci	if (!nic->reg_base) {
216562306a36Sopenharmony_ci		dev_err(dev, "Cannot map config register space, aborting\n");
216662306a36Sopenharmony_ci		err = -ENOMEM;
216762306a36Sopenharmony_ci		goto err_free_netdev;
216862306a36Sopenharmony_ci	}
216962306a36Sopenharmony_ci
217062306a36Sopenharmony_ci	nic->drv_stats = netdev_alloc_pcpu_stats(struct nicvf_drv_stats);
217162306a36Sopenharmony_ci	if (!nic->drv_stats) {
217262306a36Sopenharmony_ci		err = -ENOMEM;
217362306a36Sopenharmony_ci		goto err_free_netdev;
217462306a36Sopenharmony_ci	}
217562306a36Sopenharmony_ci
217662306a36Sopenharmony_ci	err = nicvf_set_qset_resources(nic);
217762306a36Sopenharmony_ci	if (err)
217862306a36Sopenharmony_ci		goto err_free_netdev;
217962306a36Sopenharmony_ci
218062306a36Sopenharmony_ci	/* Check if PF is alive and get MAC address for this VF */
218162306a36Sopenharmony_ci	err = nicvf_register_misc_interrupt(nic);
218262306a36Sopenharmony_ci	if (err)
218362306a36Sopenharmony_ci		goto err_free_netdev;
218462306a36Sopenharmony_ci
218562306a36Sopenharmony_ci	nicvf_send_vf_struct(nic);
218662306a36Sopenharmony_ci
218762306a36Sopenharmony_ci	if (!pass1_silicon(nic->pdev))
218862306a36Sopenharmony_ci		nic->hw_tso = true;
218962306a36Sopenharmony_ci
219062306a36Sopenharmony_ci	/* Get iommu domain for iova to physical addr conversion */
219162306a36Sopenharmony_ci	nic->iommu_domain = iommu_get_domain_for_dev(dev);
219262306a36Sopenharmony_ci
219362306a36Sopenharmony_ci	pci_read_config_word(nic->pdev, PCI_SUBSYSTEM_ID, &sdevid);
219462306a36Sopenharmony_ci	if (sdevid == 0xA134)
219562306a36Sopenharmony_ci		nic->t88 = true;
219662306a36Sopenharmony_ci
219762306a36Sopenharmony_ci	/* Check if this VF is in QS only mode */
219862306a36Sopenharmony_ci	if (nic->sqs_mode)
219962306a36Sopenharmony_ci		return 0;
220062306a36Sopenharmony_ci
220162306a36Sopenharmony_ci	err = nicvf_set_real_num_queues(netdev, nic->tx_queues, nic->rx_queues);
220262306a36Sopenharmony_ci	if (err)
220362306a36Sopenharmony_ci		goto err_unregister_interrupts;
220462306a36Sopenharmony_ci
220562306a36Sopenharmony_ci	netdev->hw_features = (NETIF_F_RXCSUM | NETIF_F_SG |
220662306a36Sopenharmony_ci			       NETIF_F_TSO | NETIF_F_GRO | NETIF_F_TSO6 |
220762306a36Sopenharmony_ci			       NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM |
220862306a36Sopenharmony_ci			       NETIF_F_HW_VLAN_CTAG_RX);
220962306a36Sopenharmony_ci
221062306a36Sopenharmony_ci	netdev->hw_features |= NETIF_F_RXHASH;
221162306a36Sopenharmony_ci
221262306a36Sopenharmony_ci	netdev->features |= netdev->hw_features;
221362306a36Sopenharmony_ci	netdev->hw_features |= NETIF_F_LOOPBACK;
221462306a36Sopenharmony_ci
221562306a36Sopenharmony_ci	netdev->vlan_features = NETIF_F_SG | NETIF_F_IP_CSUM |
221662306a36Sopenharmony_ci				NETIF_F_IPV6_CSUM | NETIF_F_TSO | NETIF_F_TSO6;
221762306a36Sopenharmony_ci
221862306a36Sopenharmony_ci	netdev->netdev_ops = &nicvf_netdev_ops;
221962306a36Sopenharmony_ci	netdev->watchdog_timeo = NICVF_TX_TIMEOUT;
222062306a36Sopenharmony_ci
222162306a36Sopenharmony_ci	if (!pass1_silicon(nic->pdev) &&
222262306a36Sopenharmony_ci	    nic->rx_queues + nic->tx_queues <= nic->max_queues)
222362306a36Sopenharmony_ci		netdev->xdp_features = NETDEV_XDP_ACT_BASIC;
222462306a36Sopenharmony_ci
222562306a36Sopenharmony_ci	/* MTU range: 64 - 9200 */
222662306a36Sopenharmony_ci	netdev->min_mtu = NIC_HW_MIN_FRS;
222762306a36Sopenharmony_ci	netdev->max_mtu = NIC_HW_MAX_FRS;
222862306a36Sopenharmony_ci
222962306a36Sopenharmony_ci	INIT_WORK(&nic->reset_task, nicvf_reset_task);
223062306a36Sopenharmony_ci
223162306a36Sopenharmony_ci	nic->nicvf_rx_mode_wq = alloc_ordered_workqueue("nicvf_rx_mode_wq_VF%d",
223262306a36Sopenharmony_ci							WQ_MEM_RECLAIM,
223362306a36Sopenharmony_ci							nic->vf_id);
223462306a36Sopenharmony_ci	if (!nic->nicvf_rx_mode_wq) {
223562306a36Sopenharmony_ci		err = -ENOMEM;
223662306a36Sopenharmony_ci		dev_err(dev, "Failed to allocate work queue\n");
223762306a36Sopenharmony_ci		goto err_unregister_interrupts;
223862306a36Sopenharmony_ci	}
223962306a36Sopenharmony_ci
224062306a36Sopenharmony_ci	INIT_WORK(&nic->rx_mode_work.work, nicvf_set_rx_mode_task);
224162306a36Sopenharmony_ci	spin_lock_init(&nic->rx_mode_wq_lock);
224262306a36Sopenharmony_ci
224362306a36Sopenharmony_ci	err = register_netdev(netdev);
224462306a36Sopenharmony_ci	if (err) {
224562306a36Sopenharmony_ci		dev_err(dev, "Failed to register netdevice\n");
224662306a36Sopenharmony_ci		goto err_destroy_workqueue;
224762306a36Sopenharmony_ci	}
224862306a36Sopenharmony_ci
224962306a36Sopenharmony_ci	nic->msg_enable = debug;
225062306a36Sopenharmony_ci
225162306a36Sopenharmony_ci	nicvf_set_ethtool_ops(netdev);
225262306a36Sopenharmony_ci
225362306a36Sopenharmony_ci	return 0;
225462306a36Sopenharmony_ci
225562306a36Sopenharmony_cierr_destroy_workqueue:
225662306a36Sopenharmony_ci	destroy_workqueue(nic->nicvf_rx_mode_wq);
225762306a36Sopenharmony_cierr_unregister_interrupts:
225862306a36Sopenharmony_ci	nicvf_unregister_interrupts(nic);
225962306a36Sopenharmony_cierr_free_netdev:
226062306a36Sopenharmony_ci	pci_set_drvdata(pdev, NULL);
226162306a36Sopenharmony_ci	if (nic->drv_stats)
226262306a36Sopenharmony_ci		free_percpu(nic->drv_stats);
226362306a36Sopenharmony_ci	free_netdev(netdev);
226462306a36Sopenharmony_cierr_release_regions:
226562306a36Sopenharmony_ci	pci_release_regions(pdev);
226662306a36Sopenharmony_cierr_disable_device:
226762306a36Sopenharmony_ci	pci_disable_device(pdev);
226862306a36Sopenharmony_ci	return err;
226962306a36Sopenharmony_ci}
227062306a36Sopenharmony_ci
227162306a36Sopenharmony_cistatic void nicvf_remove(struct pci_dev *pdev)
227262306a36Sopenharmony_ci{
227362306a36Sopenharmony_ci	struct net_device *netdev = pci_get_drvdata(pdev);
227462306a36Sopenharmony_ci	struct nicvf *nic;
227562306a36Sopenharmony_ci	struct net_device *pnetdev;
227662306a36Sopenharmony_ci
227762306a36Sopenharmony_ci	if (!netdev)
227862306a36Sopenharmony_ci		return;
227962306a36Sopenharmony_ci
228062306a36Sopenharmony_ci	nic = netdev_priv(netdev);
228162306a36Sopenharmony_ci	pnetdev = nic->pnicvf->netdev;
228262306a36Sopenharmony_ci
228362306a36Sopenharmony_ci	/* Check if this Qset is assigned to different VF.
228462306a36Sopenharmony_ci	 * If yes, clean primary and all secondary Qsets.
228562306a36Sopenharmony_ci	 */
228662306a36Sopenharmony_ci	if (pnetdev && (pnetdev->reg_state == NETREG_REGISTERED))
228762306a36Sopenharmony_ci		unregister_netdev(pnetdev);
228862306a36Sopenharmony_ci	if (nic->nicvf_rx_mode_wq) {
228962306a36Sopenharmony_ci		destroy_workqueue(nic->nicvf_rx_mode_wq);
229062306a36Sopenharmony_ci		nic->nicvf_rx_mode_wq = NULL;
229162306a36Sopenharmony_ci	}
229262306a36Sopenharmony_ci	nicvf_unregister_interrupts(nic);
229362306a36Sopenharmony_ci	pci_set_drvdata(pdev, NULL);
229462306a36Sopenharmony_ci	if (nic->drv_stats)
229562306a36Sopenharmony_ci		free_percpu(nic->drv_stats);
229662306a36Sopenharmony_ci	cavium_ptp_put(nic->ptp_clock);
229762306a36Sopenharmony_ci	free_netdev(netdev);
229862306a36Sopenharmony_ci	pci_release_regions(pdev);
229962306a36Sopenharmony_ci	pci_disable_device(pdev);
230062306a36Sopenharmony_ci}
230162306a36Sopenharmony_ci
230262306a36Sopenharmony_cistatic void nicvf_shutdown(struct pci_dev *pdev)
230362306a36Sopenharmony_ci{
230462306a36Sopenharmony_ci	nicvf_remove(pdev);
230562306a36Sopenharmony_ci}
230662306a36Sopenharmony_ci
230762306a36Sopenharmony_cistatic struct pci_driver nicvf_driver = {
230862306a36Sopenharmony_ci	.name = DRV_NAME,
230962306a36Sopenharmony_ci	.id_table = nicvf_id_table,
231062306a36Sopenharmony_ci	.probe = nicvf_probe,
231162306a36Sopenharmony_ci	.remove = nicvf_remove,
231262306a36Sopenharmony_ci	.shutdown = nicvf_shutdown,
231362306a36Sopenharmony_ci};
231462306a36Sopenharmony_ci
231562306a36Sopenharmony_cistatic int __init nicvf_init_module(void)
231662306a36Sopenharmony_ci{
231762306a36Sopenharmony_ci	pr_info("%s, ver %s\n", DRV_NAME, DRV_VERSION);
231862306a36Sopenharmony_ci	return pci_register_driver(&nicvf_driver);
231962306a36Sopenharmony_ci}
232062306a36Sopenharmony_ci
232162306a36Sopenharmony_cistatic void __exit nicvf_cleanup_module(void)
232262306a36Sopenharmony_ci{
232362306a36Sopenharmony_ci	pci_unregister_driver(&nicvf_driver);
232462306a36Sopenharmony_ci}
232562306a36Sopenharmony_ci
232662306a36Sopenharmony_cimodule_init(nicvf_init_module);
232762306a36Sopenharmony_cimodule_exit(nicvf_cleanup_module);
2328