Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
@@ -0,0 +1,20 @@
# SPDX-License-Identifier: GPL-2.0
#
# Makefile for Marvell's RVU Ethernet device drivers
#
obj-$(CONFIG_OCTEONTX2_PF) += rvu_nicpf.o otx2_ptp.o
obj-$(CONFIG_OCTEONTX2_VF) += rvu_nicvf.o otx2_ptp.o
obj-$(CONFIG_RVU_ESWITCH) += rvu_rep.o
rvu_nicpf-y := otx2_pf.o otx2_common.o otx2_txrx.o otx2_ethtool.o \
otx2_flows.o otx2_tc.o cn10k.o cn20k.o otx2_dmac_flt.o \
otx2_devlink.o qos_sq.o qos.o otx2_xsk.o
rvu_nicvf-y := otx2_vf.o
rvu_rep-y := rep.o
rvu_nicpf-$(CONFIG_DCB) += otx2_dcbnl.o
rvu_nicpf-$(CONFIG_MACSEC) += cn10k_macsec.o
rvu_nicpf-$(CONFIG_XFRM_OFFLOAD) += cn10k_ipsec.o
ccflags-y += -I$(srctree)/drivers/net/ethernet/marvell/octeontx2/af
@@ -0,0 +1,520 @@
// SPDX-License-Identifier: GPL-2.0
/* Marvell RVU Ethernet driver
*
* Copyright (C) 2021 Marvell.
*
*/
#include "cn10k.h"
#include "otx2_reg.h"
#include "otx2_struct.h"
static struct dev_hw_ops otx2_hw_ops = {
.sq_aq_init = otx2_sq_aq_init,
.sqe_flush = otx2_sqe_flush,
.aura_freeptr = otx2_aura_freeptr,
.refill_pool_ptrs = otx2_refill_pool_ptrs,
.pfaf_mbox_intr_handler = otx2_pfaf_mbox_intr_handler,
.aura_aq_init = otx2_aura_aq_init,
.pool_aq_init = otx2_pool_aq_init,
};
static struct dev_hw_ops cn10k_hw_ops = {
.sq_aq_init = cn10k_sq_aq_init,
.sqe_flush = cn10k_sqe_flush,
.aura_freeptr = cn10k_aura_freeptr,
.refill_pool_ptrs = cn10k_refill_pool_ptrs,
.pfaf_mbox_intr_handler = otx2_pfaf_mbox_intr_handler,
.aura_aq_init = otx2_aura_aq_init,
.pool_aq_init = otx2_pool_aq_init,
};
void otx2_init_hw_ops(struct otx2_nic *pfvf)
{
if (!test_bit(CN10K_LMTST, &pfvf->hw.cap_flag)) {
pfvf->hw_ops = &otx2_hw_ops;
return;
}
pfvf->hw_ops = &cn10k_hw_ops;
}
EXPORT_SYMBOL(otx2_init_hw_ops);
int cn10k_lmtst_init(struct otx2_nic *pfvf)
{
struct lmtst_tbl_setup_req *req;
struct otx2_lmt_info *lmt_info;
int err, cpu;
if (!test_bit(CN10K_LMTST, &pfvf->hw.cap_flag))
return 0;
/* Total LMTLINES = num_online_cpus() * 32 (For Burst flush).*/
pfvf->tot_lmt_lines = (num_online_cpus() * LMT_BURST_SIZE);
pfvf->hw.lmt_info = alloc_percpu(struct otx2_lmt_info);
mutex_lock(&pfvf->mbox.lock);
req = otx2_mbox_alloc_msg_lmtst_tbl_setup(&pfvf->mbox);
if (!req) {
mutex_unlock(&pfvf->mbox.lock);
return -ENOMEM;
}
req->use_local_lmt_region = true;
err = qmem_alloc(pfvf->dev, &pfvf->dync_lmt, pfvf->tot_lmt_lines,
LMT_LINE_SIZE);
if (err) {
mutex_unlock(&pfvf->mbox.lock);
return err;
}
pfvf->hw.lmt_base = (u64 *)pfvf->dync_lmt->base;
req->lmt_iova = (u64)pfvf->dync_lmt->iova;
err = otx2_sync_mbox_msg(&pfvf->mbox);
mutex_unlock(&pfvf->mbox.lock);
for_each_possible_cpu(cpu) {
lmt_info = per_cpu_ptr(pfvf->hw.lmt_info, cpu);
lmt_info->lmt_addr = ((u64)pfvf->hw.lmt_base +
(cpu * LMT_BURST_SIZE * LMT_LINE_SIZE));
lmt_info->lmt_id = cpu * LMT_BURST_SIZE;
}
return 0;
}
EXPORT_SYMBOL(cn10k_lmtst_init);
int cn10k_sq_aq_init(void *dev, u16 qidx, u8 chan_offset, u16 sqb_aura)
{
struct nix_cn10k_aq_enq_req *aq;
struct otx2_nic *pfvf = dev;
/* Get memory to put this msg */
aq = otx2_mbox_alloc_msg_nix_cn10k_aq_enq(&pfvf->mbox);
if (!aq)
return -ENOMEM;
aq->sq.cq = pfvf->hw.rx_queues + qidx;
aq->sq.max_sqe_size = NIX_MAXSQESZ_W16; /* 128 byte */
aq->sq.cq_ena = 1;
aq->sq.ena = 1;
aq->sq.smq = otx2_get_smq_idx(pfvf, qidx);
aq->sq.smq_rr_weight = mtu_to_dwrr_weight(pfvf, pfvf->tx_max_pktlen);
aq->sq.default_chan = pfvf->hw.tx_chan_base + chan_offset;
aq->sq.sqe_stype = NIX_STYPE_STF; /* Cache SQB */
aq->sq.sqb_aura = sqb_aura;
aq->sq.sq_int_ena = NIX_SQINT_BITS;
aq->sq.qint_idx = 0;
/* Due pipelining impact minimum 2000 unused SQ CQE's
* need to maintain to avoid CQ overflow.
*/
aq->sq.cq_limit = ((SEND_CQ_SKID * 256) / (pfvf->qset.sqe_cnt));
/* Fill AQ info */
aq->qidx = qidx;
aq->ctype = NIX_AQ_CTYPE_SQ;
aq->op = NIX_AQ_INSTOP_INIT;
return otx2_sync_mbox_msg(&pfvf->mbox);
}
#define NPA_MAX_BURST 16
int cn10k_refill_pool_ptrs(void *dev, struct otx2_cq_queue *cq)
{
struct otx2_nic *pfvf = dev;
int cnt = cq->pool_ptrs;
u64 ptrs[NPA_MAX_BURST];
struct otx2_pool *pool;
dma_addr_t bufptr;
int num_ptrs = 1;
pool = &pfvf->qset.pool[cq->cq_idx];
/* Refill pool with new buffers */
while (cq->pool_ptrs) {
if (otx2_alloc_buffer(pfvf, cq, &bufptr)) {
if (num_ptrs--)
__cn10k_aura_freeptr(pfvf, cq->cq_idx, ptrs,
num_ptrs);
break;
}
cq->pool_ptrs--;
ptrs[num_ptrs] = pool->xsk_pool ?
(u64)bufptr : (u64)bufptr + OTX2_HEAD_ROOM;
num_ptrs++;
if (num_ptrs == NPA_MAX_BURST || cq->pool_ptrs == 0) {
__cn10k_aura_freeptr(pfvf, cq->cq_idx, ptrs,
num_ptrs);
num_ptrs = 1;
}
}
return cnt - cq->pool_ptrs;
}
void cn10k_sqe_flush(void *dev, struct otx2_snd_queue *sq, int size, int qidx)
{
struct otx2_lmt_info *lmt_info;
struct otx2_nic *pfvf = dev;
u64 val = 0, tar_addr = 0;
lmt_info = per_cpu_ptr(pfvf->hw.lmt_info, smp_processor_id());
/* FIXME: val[0:10] LMT_ID.
* [12:15] no of LMTST - 1 in the burst.
* [19:63] data size of each LMTST in the burst except first.
*/
val = (lmt_info->lmt_id & 0x7FF);
/* Target address for LMTST flush tells HW how many 128bit
* words are present.
* tar_addr[6:4] size of first LMTST - 1 in units of 128b.
*/
tar_addr |= sq->io_addr | (((size / 16) - 1) & 0x7) << 4;
dma_wmb();
memcpy((u64 *)lmt_info->lmt_addr, sq->sqe_base, size);
cn10k_lmt_flush(val, tar_addr);
sq->head++;
sq->head &= (sq->sqe_cnt - 1);
}
int cn10k_free_all_ipolicers(struct otx2_nic *pfvf)
{
struct nix_bandprof_free_req *req;
int rc;
if (is_dev_otx2(pfvf->pdev))
return 0;
mutex_lock(&pfvf->mbox.lock);
req = otx2_mbox_alloc_msg_nix_bandprof_free(&pfvf->mbox);
if (!req) {
rc = -ENOMEM;
goto out;
}
/* Free all bandwidth profiles allocated */
req->free_all = true;
rc = otx2_sync_mbox_msg(&pfvf->mbox);
out:
mutex_unlock(&pfvf->mbox.lock);
return rc;
}
int cn10k_alloc_leaf_profile(struct otx2_nic *pfvf, u16 *leaf)
{
struct nix_bandprof_alloc_req *req;
struct nix_bandprof_alloc_rsp *rsp;
int rc;
req = otx2_mbox_alloc_msg_nix_bandprof_alloc(&pfvf->mbox);
if (!req)
return -ENOMEM;
req->prof_count[BAND_PROF_LEAF_LAYER] = 1;
rc = otx2_sync_mbox_msg(&pfvf->mbox);
if (rc)
goto out;
rsp = (struct nix_bandprof_alloc_rsp *)
otx2_mbox_get_rsp(&pfvf->mbox.mbox, 0, &req->hdr);
if (IS_ERR(rsp)) {
rc = PTR_ERR(rsp);
goto out;
}
if (!rsp->prof_count[BAND_PROF_LEAF_LAYER]) {
rc = -EIO;
goto out;
}
*leaf = rsp->prof_idx[BAND_PROF_LEAF_LAYER][0];
out:
if (rc) {
dev_warn(pfvf->dev,
"Failed to allocate ingress bandwidth policer\n");
}
return rc;
}
int cn10k_alloc_matchall_ipolicer(struct otx2_nic *pfvf)
{
struct otx2_hw *hw = &pfvf->hw;
int ret;
mutex_lock(&pfvf->mbox.lock);
ret = cn10k_alloc_leaf_profile(pfvf, &hw->matchall_ipolicer);
mutex_unlock(&pfvf->mbox.lock);
return ret;
}
#define POLICER_TIMESTAMP 1 /* 1 second */
#define MAX_RATE_EXP 22 /* Valid rate exponent range: 0 - 22 */
static void cn10k_get_ingress_burst_cfg(u32 burst, u32 *burst_exp,
u32 *burst_mantissa)
{
int tmp;
/* Burst is calculated as
* (1+[BURST_MANTISSA]/256)*2^[BURST_EXPONENT]
* This is the upper limit on number tokens (bytes) that
* can be accumulated in the bucket.
*/
*burst_exp = ilog2(burst);
if (burst < 256) {
/* No float: can't express mantissa in this case */
*burst_mantissa = 0;
return;
}
if (*burst_exp > MAX_RATE_EXP)
*burst_exp = MAX_RATE_EXP;
/* Calculate mantissa
* Find remaining bytes 'burst - 2^burst_exp'
* mantissa = (remaining bytes) / 2^ (burst_exp - 8)
*/
tmp = burst - rounddown_pow_of_two(burst);
*burst_mantissa = tmp / (1UL << (*burst_exp - 8));
}
static void cn10k_get_ingress_rate_cfg(u64 rate, u32 *rate_exp,
u32 *rate_mantissa, u32 *rdiv)
{
u32 div = 0;
u32 exp = 0;
u64 tmp;
/* Figure out mantissa, exponent and divider from given max pkt rate
*
* To achieve desired rate HW adds
* (1+[RATE_MANTISSA]/256)*2^[RATE_EXPONENT] tokens (bytes) at every
* policer timeunit * 2^rdiv ie 2 * 2^rdiv usecs, to the token bucket.
* Here policer timeunit is 2 usecs and rate is in bits per sec.
* Since floating point cannot be used below algorithm uses 1000000
* scale factor to support rates upto 100Gbps.
*/
tmp = rate * 32 * 2;
if (tmp < 256000000) {
while (tmp < 256000000) {
tmp = tmp * 2;
div++;
}
} else {
for (exp = 0; tmp >= 512000000 && exp <= MAX_RATE_EXP; exp++)
tmp = tmp / 2;
if (exp > MAX_RATE_EXP)
exp = MAX_RATE_EXP;
}
*rate_mantissa = (tmp - 256000000) / 1000000;
*rate_exp = exp;
*rdiv = div;
}
int cn10k_map_unmap_rq_policer(struct otx2_nic *pfvf, int rq_idx,
u16 policer, bool map)
{
struct nix_cn10k_aq_enq_req *aq;
aq = otx2_mbox_alloc_msg_nix_cn10k_aq_enq(&pfvf->mbox);
if (!aq)
return -ENOMEM;
/* Enable policing and set the bandwidth profile (policer) index */
if (map)
aq->rq.policer_ena = 1;
else
aq->rq.policer_ena = 0;
aq->rq_mask.policer_ena = 1;
aq->rq.band_prof_id = policer;
aq->rq_mask.band_prof_id = GENMASK(9, 0);
/* If policer id is greater than 1023 then it implies hardware supports
* more leaf profiles. In that case use band_prof_id_h for 4 MSBs.
*/
aq->rq.band_prof_id_h = policer >> 10;
aq->rq_mask.band_prof_id_h = GENMASK(3, 0);
/* Fill AQ info */
aq->qidx = rq_idx;
aq->ctype = NIX_AQ_CTYPE_RQ;
aq->op = NIX_AQ_INSTOP_WRITE;
return otx2_sync_mbox_msg(&pfvf->mbox);
}
int cn10k_free_leaf_profile(struct otx2_nic *pfvf, u16 leaf)
{
struct nix_bandprof_free_req *req;
req = otx2_mbox_alloc_msg_nix_bandprof_free(&pfvf->mbox);
if (!req)
return -ENOMEM;
req->prof_count[BAND_PROF_LEAF_LAYER] = 1;
req->prof_idx[BAND_PROF_LEAF_LAYER][0] = leaf;
return otx2_sync_mbox_msg(&pfvf->mbox);
}
int cn10k_free_matchall_ipolicer(struct otx2_nic *pfvf)
{
struct otx2_hw *hw = &pfvf->hw;
int qidx, rc;
mutex_lock(&pfvf->mbox.lock);
/* Remove RQ's policer mapping */
for (qidx = 0; qidx < hw->rx_queues; qidx++) {
rc = cn10k_map_unmap_rq_policer(pfvf, qidx, hw->matchall_ipolicer, false);
if (rc)
dev_warn(pfvf->dev, "Failed to unmap RQ %d's policer (error %d).",
qidx, rc);
}
rc = cn10k_free_leaf_profile(pfvf, hw->matchall_ipolicer);
mutex_unlock(&pfvf->mbox.lock);
return rc;
}
int cn10k_set_ipolicer_rate(struct otx2_nic *pfvf, u16 profile,
u32 burst, u64 rate, bool pps)
{
struct nix_cn10k_aq_enq_req *aq;
u32 burst_exp, burst_mantissa;
u32 rate_exp, rate_mantissa;
u32 rdiv;
/* Get exponent and mantissa values for the desired rate */
cn10k_get_ingress_burst_cfg(burst, &burst_exp, &burst_mantissa);
cn10k_get_ingress_rate_cfg(rate, &rate_exp, &rate_mantissa, &rdiv);
/* Init bandwidth profile */
aq = otx2_mbox_alloc_msg_nix_cn10k_aq_enq(&pfvf->mbox);
if (!aq)
return -ENOMEM;
/* Set initial color mode to blind */
aq->prof.icolor = 0x03;
aq->prof_mask.icolor = 0x03;
/* Set rate and burst values */
aq->prof.cir_exponent = rate_exp;
aq->prof_mask.cir_exponent = 0x1F;
aq->prof.cir_mantissa = rate_mantissa;
aq->prof_mask.cir_mantissa = 0xFF;
aq->prof.cbs_exponent = burst_exp;
aq->prof_mask.cbs_exponent = 0x1F;
aq->prof.cbs_mantissa = burst_mantissa;
aq->prof_mask.cbs_mantissa = 0xFF;
aq->prof.rdiv = rdiv;
aq->prof_mask.rdiv = 0xF;
if (pps) {
/* The amount of decremented tokens is calculated according to
* the following equation:
* max([ LMODE ? 0 : (packet_length - LXPTR)] +
* ([ADJUST_MANTISSA]/256 - 1) * 2^[ADJUST_EXPONENT],
* 1/256)
* if LMODE is 1 then rate limiting will be based on
* PPS otherwise bps.
* The aim of the ADJUST value is to specify a token cost per
* packet in contrary to the packet length that specifies a
* cost per byte. To rate limit based on PPS adjust mantissa
* is set as 384 and exponent as 1 so that number of tokens
* decremented becomes 1 i.e, 1 token per packeet.
*/
aq->prof.adjust_exponent = 1;
aq->prof_mask.adjust_exponent = 0x1F;
aq->prof.adjust_mantissa = 384;
aq->prof_mask.adjust_mantissa = 0x1FF;
aq->prof.lmode = 0x1;
aq->prof_mask.lmode = 0x1;
}
/* Two rate three color marker
* With PEIR/EIR set to zero, color will be either green or red
*/
aq->prof.meter_algo = 2;
aq->prof_mask.meter_algo = 0x3;
aq->prof.rc_action = NIX_RX_BAND_PROF_ACTIONRESULT_DROP;
aq->prof_mask.rc_action = 0x3;
aq->prof.yc_action = NIX_RX_BAND_PROF_ACTIONRESULT_PASS;
aq->prof_mask.yc_action = 0x3;
aq->prof.gc_action = NIX_RX_BAND_PROF_ACTIONRESULT_PASS;
aq->prof_mask.gc_action = 0x3;
/* Setting exponent value as 24 and mantissa as 0 configures
* the bucket with zero values making bucket unused. Peak
* information rate and Excess information rate buckets are
* unused here.
*/
aq->prof.peir_exponent = 24;
aq->prof_mask.peir_exponent = 0x1F;
aq->prof.peir_mantissa = 0;
aq->prof_mask.peir_mantissa = 0xFF;
aq->prof.pebs_exponent = 24;
aq->prof_mask.pebs_exponent = 0x1F;
aq->prof.pebs_mantissa = 0;
aq->prof_mask.pebs_mantissa = 0xFF;
aq->prof.hl_en = 0;
aq->prof_mask.hl_en = 1;
/* Fill AQ info */
aq->qidx = profile;
aq->ctype = NIX_AQ_CTYPE_BANDPROF;
aq->op = NIX_AQ_INSTOP_WRITE;
return otx2_sync_mbox_msg(&pfvf->mbox);
}
int cn10k_set_matchall_ipolicer_rate(struct otx2_nic *pfvf,
u32 burst, u64 rate)
{
struct otx2_hw *hw = &pfvf->hw;
int qidx, rc;
mutex_lock(&pfvf->mbox.lock);
rc = cn10k_set_ipolicer_rate(pfvf, hw->matchall_ipolicer, burst,
rate, false);
if (rc)
goto out;
for (qidx = 0; qidx < hw->rx_queues; qidx++) {
rc = cn10k_map_unmap_rq_policer(pfvf, qidx,
hw->matchall_ipolicer, true);
if (rc)
break;
}
out:
mutex_unlock(&pfvf->mbox.lock);
return rc;
}
@@ -0,0 +1,43 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Marvell RVU Ethernet driver
*
* Copyright (C) 2021 Marvell.
*
*/
#ifndef CN10K_H
#define CN10K_H
#include "otx2_common.h"
static inline int mtu_to_dwrr_weight(struct otx2_nic *pfvf, int mtu)
{
u32 weight;
/* On OTx2, since AF returns DWRR_MTU as '1', this logic
* will work on those silicons as well.
*/
weight = mtu / pfvf->hw.dwrr_mtu;
if (mtu % pfvf->hw.dwrr_mtu)
weight += 1;
return weight;
}
int cn10k_refill_pool_ptrs(void *dev, struct otx2_cq_queue *cq);
void cn10k_sqe_flush(void *dev, struct otx2_snd_queue *sq, int size, int qidx);
int cn10k_sq_aq_init(void *dev, u16 qidx, u8 chan_offset, u16 sqb_aura);
int cn10k_lmtst_init(struct otx2_nic *pfvf);
int cn10k_free_all_ipolicers(struct otx2_nic *pfvf);
int cn10k_alloc_matchall_ipolicer(struct otx2_nic *pfvf);
int cn10k_free_matchall_ipolicer(struct otx2_nic *pfvf);
int cn10k_set_matchall_ipolicer_rate(struct otx2_nic *pfvf,
u32 burst, u64 rate);
int cn10k_map_unmap_rq_policer(struct otx2_nic *pfvf, int rq_idx,
u16 policer, bool map);
int cn10k_alloc_leaf_profile(struct otx2_nic *pfvf, u16 *leaf);
int cn10k_set_ipolicer_rate(struct otx2_nic *pfvf, u16 profile,
u32 burst, u64 rate, bool pps);
int cn10k_free_leaf_profile(struct otx2_nic *pfvf, u16 leaf);
void otx2_init_hw_ops(struct otx2_nic *pfvf);
#endif /* CN10K_H */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,265 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Marvell IPSEC offload driver
*
* Copyright (C) 2024 Marvell.
*/
#ifndef CN10K_IPSEC_H
#define CN10K_IPSEC_H
#include <linux/types.h>
DECLARE_STATIC_KEY_FALSE(cn10k_ipsec_sa_enabled);
/* CPT instruction size in bytes */
#define CN10K_CPT_INST_SIZE 64
/* CPT instruction (CPT_INST_S) queue length */
#define CN10K_CPT_INST_QLEN 8200
/* CPT instruction queue size passed to HW is in units of
* 40*CPT_INST_S messages.
*/
#define CN10K_CPT_SIZE_DIV40 (CN10K_CPT_INST_QLEN / 40)
/* CPT needs 320 free entries */
#define CN10K_CPT_INST_QLEN_EXTRA_BYTES (320 * CN10K_CPT_INST_SIZE)
#define CN10K_CPT_EXTRA_SIZE_DIV40 (320 / 40)
/* CPT instruction queue length in bytes */
#define CN10K_CPT_INST_QLEN_BYTES \
((CN10K_CPT_SIZE_DIV40 * 40 * CN10K_CPT_INST_SIZE) + \
CN10K_CPT_INST_QLEN_EXTRA_BYTES)
/* CPT instruction group queue length in bytes */
#define CN10K_CPT_INST_GRP_QLEN_BYTES \
((CN10K_CPT_SIZE_DIV40 + CN10K_CPT_EXTRA_SIZE_DIV40) * 16)
/* CPT FC length in bytes */
#define CN10K_CPT_Q_FC_LEN 128
/* Default CPT engine group for ipsec offload */
#define CN10K_DEF_CPT_IPSEC_EGRP 1
/* CN10K CPT LF registers */
#define CPT_LFBASE (BLKTYPE_CPT << RVU_FUNC_BLKADDR_SHIFT)
#define CN10K_CPT_LF_CTL (CPT_LFBASE | 0x10)
#define CN10K_CPT_LF_INPROG (CPT_LFBASE | 0x40)
#define CN10K_CPT_LF_Q_BASE (CPT_LFBASE | 0xf0)
#define CN10K_CPT_LF_Q_SIZE (CPT_LFBASE | 0x100)
#define CN10K_CPT_LF_Q_INST_PTR (CPT_LFBASE | 0x110)
#define CN10K_CPT_LF_Q_GRP_PTR (CPT_LFBASE | 0x120)
#define CN10K_CPT_LF_NQX(a) (CPT_LFBASE | 0x400 | (a) << 3)
#define CN10K_CPT_LF_CTX_FLUSH (CPT_LFBASE | 0x510)
/* IPSEC Instruction opcodes */
#define CN10K_IPSEC_MAJOR_OP_WRITE_SA 0x01UL
#define CN10K_IPSEC_MINOR_OP_WRITE_SA 0x09UL
#define CN10K_IPSEC_MAJOR_OP_OUTB_IPSEC 0x2AUL
enum cn10k_cpt_comp_e {
CN10K_CPT_COMP_E_NOTDONE = 0x00,
CN10K_CPT_COMP_E_GOOD = 0x01,
CN10K_CPT_COMP_E_FAULT = 0x02,
CN10K_CPT_COMP_E_HWERR = 0x04,
CN10K_CPT_COMP_E_INSTERR = 0x05,
CN10K_CPT_COMP_E_WARN = 0x06,
CN10K_CPT_COMP_E_MASK = 0x3F
};
struct cn10k_cpt_inst_queue {
u8 *vaddr;
u8 *real_vaddr;
dma_addr_t dma_addr;
dma_addr_t real_dma_addr;
u32 size;
};
enum cn10k_cpt_hw_state_e {
CN10K_CPT_HW_UNAVAILABLE,
CN10K_CPT_HW_AVAILABLE,
CN10K_CPT_HW_IN_USE
};
struct cn10k_ipsec {
/* Outbound CPT */
u64 io_addr;
atomic_t cpt_state;
struct cn10k_cpt_inst_queue iq;
/* SA info */
u32 sa_size;
u32 outb_sa_count;
struct work_struct sa_work;
struct workqueue_struct *sa_workq;
};
/* CN10K IPSEC Security Association (SA) */
/* SA direction */
#define CN10K_IPSEC_SA_DIR_INB 0
#define CN10K_IPSEC_SA_DIR_OUTB 1
/* SA protocol */
#define CN10K_IPSEC_SA_IPSEC_PROTO_AH 0
#define CN10K_IPSEC_SA_IPSEC_PROTO_ESP 1
/* SA Encryption Type */
#define CN10K_IPSEC_SA_ENCAP_TYPE_AES_GCM 5
/* SA IPSEC mode Transport/Tunnel */
#define CN10K_IPSEC_SA_IPSEC_MODE_TRANSPORT 0
#define CN10K_IPSEC_SA_IPSEC_MODE_TUNNEL 1
/* SA AES Key Length */
#define CN10K_IPSEC_SA_AES_KEY_LEN_128 1
#define CN10K_IPSEC_SA_AES_KEY_LEN_192 2
#define CN10K_IPSEC_SA_AES_KEY_LEN_256 3
/* IV Source */
#define CN10K_IPSEC_SA_IV_SRC_COUNTER 0
#define CN10K_IPSEC_SA_IV_SRC_PACKET 3
struct cn10k_tx_sa_s {
u64 esn_en : 1; /* W0 */
u64 rsvd_w0_1_8 : 8;
u64 hw_ctx_off : 7;
u64 ctx_id : 16;
u64 rsvd_w0_32_47 : 16;
u64 ctx_push_size : 7;
u64 rsvd_w0_55 : 1;
u64 ctx_hdr_size : 2;
u64 aop_valid : 1;
u64 rsvd_w0_59 : 1;
u64 ctx_size : 4;
u64 w1; /* W1 */
u64 sa_valid : 1; /* W2 */
u64 sa_dir : 1;
u64 rsvd_w2_2_3 : 2;
u64 ipsec_mode : 1;
u64 ipsec_protocol : 1;
u64 aes_key_len : 2;
u64 enc_type : 3;
u64 rsvd_w2_11_19 : 9;
u64 iv_src : 2;
u64 rsvd_w2_22_31 : 10;
u64 rsvd_w2_32_63 : 32;
u64 w3; /* W3 */
u8 cipher_key[32]; /* W4 - W7 */
u32 rsvd_w8_0_31; /* W8 : IV */
u32 iv_gcm_salt;
u64 rsvd_w9_w30[22]; /* W9 - W30 */
u64 hw_ctx[6]; /* W31 - W36 */
};
/* CPT instruction parameter-1 */
#define CN10K_IPSEC_INST_PARAM1_DIS_L4_CSUM 0x1
#define CN10K_IPSEC_INST_PARAM1_DIS_L3_CSUM 0x2
#define CN10K_IPSEC_INST_PARAM1_CRYPTO_MODE 0x20
#define CN10K_IPSEC_INST_PARAM1_IV_OFFSET_SHIFT 8
/* CPT instruction parameter-2 */
#define CN10K_IPSEC_INST_PARAM2_ENC_DATA_OFFSET_SHIFT 0
#define CN10K_IPSEC_INST_PARAM2_AUTH_DATA_OFFSET_SHIFT 8
/* CPT Instruction Structure */
struct cpt_inst_s {
u64 nixtxl : 3; /* W0 */
u64 doneint : 1;
u64 rsvd_w0_4_15 : 12;
u64 dat_offset : 8;
u64 ext_param1 : 8;
u64 nixtx_offset : 20;
u64 rsvd_w0_52_63 : 12;
u64 res_addr; /* W1 */
u64 tag : 32; /* W2 */
u64 tt : 2;
u64 grp : 10;
u64 rsvd_w2_44_47 : 4;
u64 rvu_pf_func : 16;
u64 qord : 1; /* W3 */
u64 rsvd_w3_1_2 : 2;
u64 wqe_ptr : 61;
u64 dlen : 16; /* W4 */
u64 param2 : 16;
u64 param1 : 16;
u64 opcode_major : 8;
u64 opcode_minor : 8;
u64 dptr; /* W5 */
u64 rptr; /* W6 */
u64 cptr : 60; /* W7 */
u64 ctx_val : 1;
u64 egrp : 3;
};
/* CPT Instruction Result Structure */
struct cpt_res_s {
u64 compcode : 7; /* W0 */
u64 doneint : 1;
u64 uc_compcode : 8;
u64 uc_info : 48;
u64 esn; /* W1 */
};
/* CPT SG structure */
struct cpt_sg_s {
u64 seg1_size : 16;
u64 seg2_size : 16;
u64 seg3_size : 16;
u64 segs : 2;
u64 rsvd_63_50 : 14;
};
/* CPT LF_INPROG Register */
#define CPT_LF_INPROG_INFLIGHT GENMASK_ULL(8, 0)
#define CPT_LF_INPROG_GRB_CNT GENMASK_ULL(39, 32)
#define CPT_LF_INPROG_GWB_CNT GENMASK_ULL(47, 40)
/* CPT LF_Q_GRP_PTR Register */
#define CPT_LF_Q_GRP_PTR_DQ_PTR GENMASK_ULL(14, 0)
#define CPT_LF_Q_GRP_PTR_NQ_PTR GENMASK_ULL(46, 32)
/* CPT LF_Q_SIZE Register */
#define CPT_LF_Q_BASE_ADDR GENMASK_ULL(52, 7)
/* CPT LF_Q_SIZE Register */
#define CPT_LF_Q_SIZE_DIV40 GENMASK_ULL(14, 0)
/* CPT LF CTX Flush Register */
#define CPT_LF_CTX_FLUSH_CPTR GENMASK_ULL(45, 0)
#ifdef CONFIG_XFRM_OFFLOAD
int cn10k_ipsec_init(struct net_device *netdev);
void cn10k_ipsec_clean(struct otx2_nic *pf);
int cn10k_ipsec_ethtool_init(struct net_device *netdev, bool enable);
bool otx2_sqe_add_sg_ipsec(struct otx2_nic *pfvf, struct otx2_snd_queue *sq,
struct sk_buff *skb, int num_segs, int *offset);
bool cn10k_ipsec_transmit(struct otx2_nic *pf, struct netdev_queue *txq,
struct otx2_snd_queue *sq, struct sk_buff *skb,
int num_segs, int size);
#else
static inline __maybe_unused int cn10k_ipsec_init(struct net_device *netdev)
{
return 0;
}
static inline __maybe_unused void cn10k_ipsec_clean(struct otx2_nic *pf)
{
}
static inline __maybe_unused
int cn10k_ipsec_ethtool_init(struct net_device *netdev, bool enable)
{
return 0;
}
static inline bool __maybe_unused
otx2_sqe_add_sg_ipsec(struct otx2_nic *pfvf, struct otx2_snd_queue *sq,
struct sk_buff *skb, int num_segs, int *offset)
{
return true;
}
static inline bool __maybe_unused
cn10k_ipsec_transmit(struct otx2_nic *pf, struct netdev_queue *txq,
struct otx2_snd_queue *sq, struct sk_buff *skb,
int num_segs, int size)
{
return true;
}
#endif
#endif // CN10K_IPSEC_H
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,718 @@
// SPDX-License-Identifier: GPL-2.0
/* Marvell RVU Ethernet driver
*
* Copyright (C) 2024 Marvell.
*
*/
#include "otx2_common.h"
#include "otx2_reg.h"
#include "otx2_struct.h"
#include "cn10k.h"
/* CN20K mbox AF => PFx irq handler */
irqreturn_t cn20k_pfaf_mbox_intr_handler(int irq, void *pf_irq)
{
struct otx2_nic *pf = pf_irq;
struct mbox *mw = &pf->mbox;
struct otx2_mbox_dev *mdev;
struct otx2_mbox *mbox;
struct mbox_hdr *hdr;
u64 pf_trig_val;
pf_trig_val = otx2_read64(pf, RVU_PF_INT) & 0x3ULL;
/* Clear the IRQ */
otx2_write64(pf, RVU_PF_INT, pf_trig_val);
if (pf_trig_val & BIT_ULL(0)) {
mbox = &mw->mbox_up;
mdev = &mbox->dev[0];
otx2_sync_mbox_bbuf(mbox, 0);
hdr = (struct mbox_hdr *)(mdev->mbase + mbox->rx_start);
if (hdr->num_msgs)
queue_work(pf->mbox_wq, &mw->mbox_up_wrk);
trace_otx2_msg_interrupt(pf->pdev, "UP message from AF to PF",
BIT_ULL(0));
}
if (pf_trig_val & BIT_ULL(1)) {
mbox = &mw->mbox;
mdev = &mbox->dev[0];
otx2_sync_mbox_bbuf(mbox, 0);
hdr = (struct mbox_hdr *)(mdev->mbase + mbox->rx_start);
if (hdr->num_msgs)
queue_work(pf->mbox_wq, &mw->mbox_wrk);
trace_otx2_msg_interrupt(pf->pdev, "DOWN reply from AF to PF",
BIT_ULL(1));
}
return IRQ_HANDLED;
}
irqreturn_t cn20k_vfaf_mbox_intr_handler(int irq, void *vf_irq)
{
struct otx2_nic *vf = vf_irq;
struct otx2_mbox_dev *mdev;
struct otx2_mbox *mbox;
struct mbox_hdr *hdr;
u64 vf_trig_val;
vf_trig_val = otx2_read64(vf, RVU_VF_INT) & 0x3ULL;
/* Clear the IRQ */
otx2_write64(vf, RVU_VF_INT, vf_trig_val);
/* Read latest mbox data */
smp_rmb();
if (vf_trig_val & BIT_ULL(1)) {
/* Check for PF => VF response messages */
mbox = &vf->mbox.mbox;
mdev = &mbox->dev[0];
otx2_sync_mbox_bbuf(mbox, 0);
hdr = (struct mbox_hdr *)(mdev->mbase + mbox->rx_start);
if (hdr->num_msgs)
queue_work(vf->mbox_wq, &vf->mbox.mbox_wrk);
trace_otx2_msg_interrupt(mbox->pdev, "DOWN reply from PF0 to VF",
BIT_ULL(1));
}
if (vf_trig_val & BIT_ULL(0)) {
/* Check for PF => VF notification messages */
mbox = &vf->mbox.mbox_up;
mdev = &mbox->dev[0];
otx2_sync_mbox_bbuf(mbox, 0);
hdr = (struct mbox_hdr *)(mdev->mbase + mbox->rx_start);
if (hdr->num_msgs)
queue_work(vf->mbox_wq, &vf->mbox.mbox_up_wrk);
trace_otx2_msg_interrupt(mbox->pdev, "UP message from PF0 to VF",
BIT_ULL(0));
}
return IRQ_HANDLED;
}
void cn20k_enable_pfvf_mbox_intr(struct otx2_nic *pf, int numvfs)
{
/* Clear PF <=> VF mailbox IRQ */
otx2_write64(pf, RVU_MBOX_PF_VFPF_INTX(0), ~0ull);
otx2_write64(pf, RVU_MBOX_PF_VFPF_INTX(1), ~0ull);
otx2_write64(pf, RVU_MBOX_PF_VFPF1_INTX(0), ~0ull);
otx2_write64(pf, RVU_MBOX_PF_VFPF1_INTX(1), ~0ull);
/* Enable PF <=> VF mailbox IRQ */
otx2_write64(pf, RVU_MBOX_PF_VFPF_INT_ENA_W1SX(0), INTR_MASK(numvfs));
otx2_write64(pf, RVU_MBOX_PF_VFPF1_INT_ENA_W1SX(0), INTR_MASK(numvfs));
if (numvfs > 64) {
numvfs -= 64;
otx2_write64(pf, RVU_MBOX_PF_VFPF_INT_ENA_W1SX(1),
INTR_MASK(numvfs));
otx2_write64(pf, RVU_MBOX_PF_VFPF1_INT_ENA_W1SX(1),
INTR_MASK(numvfs));
}
}
void cn20k_disable_pfvf_mbox_intr(struct otx2_nic *pf, int numvfs)
{
int vector, intr_vec, vec = 0;
/* Disable PF <=> VF mailbox IRQ */
otx2_write64(pf, RVU_MBOX_PF_VFPF_INT_ENA_W1CX(0), ~0ull);
otx2_write64(pf, RVU_MBOX_PF_VFPF_INT_ENA_W1CX(1), ~0ull);
otx2_write64(pf, RVU_MBOX_PF_VFPF1_INT_ENA_W1CX(0), ~0ull);
otx2_write64(pf, RVU_MBOX_PF_VFPF1_INT_ENA_W1CX(1), ~0ull);
otx2_write64(pf, RVU_MBOX_PF_VFPF_INTX(0), ~0ull);
otx2_write64(pf, RVU_MBOX_PF_VFPF1_INTX(0), ~0ull);
if (numvfs > 64) {
otx2_write64(pf, RVU_MBOX_PF_VFPF_INTX(1), ~0ull);
otx2_write64(pf, RVU_MBOX_PF_VFPF1_INTX(1), ~0ull);
}
for (intr_vec = RVU_MBOX_PF_INT_VEC_VFPF_MBOX0; intr_vec <=
RVU_MBOX_PF_INT_VEC_VFPF1_MBOX1; intr_vec++, vec++) {
vector = pci_irq_vector(pf->pdev, intr_vec);
free_irq(vector, pf->hw.pfvf_irq_devid[vec]);
}
}
irqreturn_t cn20k_pfvf_mbox_intr_handler(int irq, void *pf_irq)
{
struct pf_irq_data *irq_data = pf_irq;
struct otx2_nic *pf = irq_data->pf;
struct mbox *mbox;
u64 intr;
/* Sync with mbox memory region */
rmb();
/* Clear interrupts */
intr = otx2_read64(pf, irq_data->intr_status);
otx2_write64(pf, irq_data->intr_status, intr);
mbox = pf->mbox_pfvf;
if (intr)
trace_otx2_msg_interrupt(pf->pdev, "VF(s) to PF", intr);
irq_data->pf_queue_work_hdlr(mbox, pf->mbox_pfvf_wq, irq_data->start,
irq_data->mdevs, intr);
return IRQ_HANDLED;
}
int cn20k_register_pfvf_mbox_intr(struct otx2_nic *pf, int numvfs)
{
struct otx2_hw *hw = &pf->hw;
struct pf_irq_data *irq_data;
int intr_vec, ret, vec = 0;
char *irq_name;
/* irq data for 4 PF intr vectors */
irq_data = devm_kcalloc(pf->dev, 4,
sizeof(struct pf_irq_data), GFP_KERNEL);
if (!irq_data)
return -ENOMEM;
for (intr_vec = RVU_MBOX_PF_INT_VEC_VFPF_MBOX0; intr_vec <=
RVU_MBOX_PF_INT_VEC_VFPF1_MBOX1; intr_vec++, vec++) {
switch (intr_vec) {
case RVU_MBOX_PF_INT_VEC_VFPF_MBOX0:
irq_data[vec].intr_status =
RVU_MBOX_PF_VFPF_INTX(0);
irq_data[vec].start = 0;
irq_data[vec].mdevs = 64;
break;
case RVU_MBOX_PF_INT_VEC_VFPF_MBOX1:
irq_data[vec].intr_status =
RVU_MBOX_PF_VFPF_INTX(1);
irq_data[vec].start = 64;
irq_data[vec].mdevs = 96;
break;
case RVU_MBOX_PF_INT_VEC_VFPF1_MBOX0:
irq_data[vec].intr_status =
RVU_MBOX_PF_VFPF1_INTX(0);
irq_data[vec].start = 0;
irq_data[vec].mdevs = 64;
break;
case RVU_MBOX_PF_INT_VEC_VFPF1_MBOX1:
irq_data[vec].intr_status =
RVU_MBOX_PF_VFPF1_INTX(1);
irq_data[vec].start = 64;
irq_data[vec].mdevs = 96;
break;
}
irq_data[vec].pf_queue_work_hdlr = otx2_queue_vf_work;
irq_data[vec].vec_num = intr_vec;
irq_data[vec].pf = pf;
/* Register mailbox interrupt handler */
irq_name = &hw->irq_name[intr_vec * NAME_SIZE];
if (pf->pcifunc)
snprintf(irq_name, NAME_SIZE,
"RVUPF%d_VF%d Mbox%d", rvu_get_pf(pf->pdev,
pf->pcifunc), vec / 2, vec % 2);
else
snprintf(irq_name, NAME_SIZE, "RVUPF_VF%d Mbox%d",
vec / 2, vec % 2);
hw->pfvf_irq_devid[vec] = &irq_data[vec];
ret = request_irq(pci_irq_vector(pf->pdev, intr_vec),
pf->hw_ops->pfvf_mbox_intr_handler, 0,
irq_name,
&irq_data[vec]);
if (ret) {
dev_err(pf->dev,
"RVUPF: IRQ registration failed for PFVF mbox0 irq\n");
return ret;
}
}
cn20k_enable_pfvf_mbox_intr(pf, numvfs);
return 0;
}
#define RQ_BP_LVL_AURA (255 - ((85 * 256) / 100)) /* BP when 85% is full */
static u8 cn20k_aura_bpid_idx(struct otx2_nic *pfvf, int aura_id)
{
#ifdef CONFIG_DCB
return pfvf->queue_to_pfc_map[aura_id];
#else
return 0;
#endif
}
static int cn20k_tc_get_entry_index(struct otx2_flow_config *flow_cfg,
struct otx2_tc_flow *node)
{
struct otx2_tc_flow *tmp;
int index = 0;
list_for_each_entry(tmp, &flow_cfg->flow_list_tc, list) {
if (tmp == node)
return index;
index++;
}
return -1;
}
int cn20k_tc_free_mcam_entry(struct otx2_nic *nic, u16 entry)
{
struct npc_mcam_free_entry_req *req;
int err;
mutex_lock(&nic->mbox.lock);
req = otx2_mbox_alloc_msg_npc_mcam_free_entry(&nic->mbox);
if (!req) {
mutex_unlock(&nic->mbox.lock);
return -ENOMEM;
}
req->entry = entry;
/* Send message to AF to free MCAM entries */
err = otx2_sync_mbox_msg(&nic->mbox);
if (err) {
mutex_unlock(&nic->mbox.lock);
return err;
}
mutex_unlock(&nic->mbox.lock);
return 0;
}
static bool cn20k_tc_check_entry_shiftable(struct otx2_nic *nic,
struct otx2_flow_config *flow_cfg,
struct otx2_tc_flow *node, int index,
bool error)
{
struct otx2_tc_flow *first, *tmp, *n;
u32 prio = 0;
int i = 0;
u8 type;
first = list_first_entry(&flow_cfg->flow_list_tc, struct otx2_tc_flow,
list);
type = first->kw_type;
/* Check all the nodes from start to given index (including index) has
* same type i.e, either X2 or X4
*/
list_for_each_entry_safe(tmp, n, &flow_cfg->flow_list_tc, list) {
if (i > index)
break;
if (type != tmp->kw_type) {
/* List has both X2 and X4 entries so entries cannot be
* shifted to save MCAM space.
*/
if (error)
dev_err(nic->dev, "Rule %d cannot be shifted to %d\n",
tmp->prio, prio);
return false;
}
type = tmp->kw_type;
prio = tmp->prio;
i++;
}
return true;
}
void cn20k_tc_update_mcam_table_del_req(struct otx2_nic *nic,
struct otx2_flow_config *flow_cfg,
struct otx2_tc_flow *node)
{
struct otx2_tc_flow *first, *tmp, *n;
int i = 0, index;
u16 cntr_val = 0;
u16 entry;
index = cn20k_tc_get_entry_index(flow_cfg, node);
if (index < 0) {
netdev_dbg(nic->netdev, "Could not find node\n");
return;
}
first = list_first_entry(&flow_cfg->flow_list_tc, struct otx2_tc_flow,
list);
entry = first->entry;
/* If entries cannot be shifted then delete given entry
* and free it to AF too.
*/
if (!cn20k_tc_check_entry_shiftable(nic, flow_cfg, node,
index, false)) {
list_del(&node->list);
entry = node->entry;
goto free_mcam_entry;
}
/* Find and delete the entry from the list and re-install
* all the entries from beginning to the index of the
* deleted entry to higher mcam indexes.
*/
list_for_each_entry_safe(tmp, n, &flow_cfg->flow_list_tc, list) {
if (node == tmp) {
list_del(&tmp->list);
break;
}
otx2_del_mcam_flow_entry(nic, tmp->entry, &cntr_val);
tmp->entry = (list_next_entry(tmp, list))->entry;
tmp->req.entry = tmp->entry;
tmp->req.cntr_val = cntr_val;
}
list_for_each_entry_safe(tmp, n, &flow_cfg->flow_list_tc, list) {
if (i == index)
break;
otx2_add_mcam_flow_entry(nic, &tmp->req);
i++;
}
free_mcam_entry:
if (cn20k_tc_free_mcam_entry(nic, entry))
netdev_err(nic->netdev, "Freeing entry %d to AF failed\n",
entry);
}
int cn20k_tc_update_mcam_table_add_req(struct otx2_nic *nic,
struct otx2_flow_config *flow_cfg,
struct otx2_tc_flow *node)
{
struct otx2_tc_flow *tmp;
u16 cntr_val = 0;
int list_idx, i;
int entry, prev;
/* Find the index of the entry(list_idx) whose priority
* is greater than the new entry and re-install all
* the entries from beginning to list_idx to higher
* mcam indexes.
*/
list_idx = otx2_tc_add_to_flow_list(flow_cfg, node);
entry = node->entry;
if (!cn20k_tc_check_entry_shiftable(nic, flow_cfg, node,
list_idx, true))
/* Due to mix of X2 and X4, entries cannot be shifted.
* In this case free the entry allocated for this rule.
*/
return -EINVAL;
for (i = 0; i < list_idx; i++) {
tmp = otx2_tc_get_entry_by_index(flow_cfg, i);
if (!tmp)
return -ENOMEM;
otx2_del_mcam_flow_entry(nic, tmp->entry, &cntr_val);
prev = tmp->entry;
tmp->entry = entry;
tmp->req.entry = tmp->entry;
tmp->req.cntr_val = cntr_val;
otx2_add_mcam_flow_entry(nic, &tmp->req);
entry = prev;
}
return entry;
}
#define MAX_TC_HW_PRIORITY 125
#define MAX_TC_VF_PRIORITY 126
#define MAX_TC_PF_PRIORITY 127
static int __cn20k_tc_alloc_entry(struct otx2_nic *nic,
struct npc_install_flow_req *flow_req,
u16 *entry, u8 *type,
u32 tc_priority, bool hw_priority)
{
struct otx2_flow_config *flow_cfg = nic->flow_cfg;
struct npc_install_flow_req *req;
struct npc_install_flow_rsp *rsp;
struct otx2_tc_flow *tmp;
int ret = 0;
req = otx2_mbox_alloc_msg_npc_install_flow(&nic->mbox);
if (!req)
return -ENOMEM;
memcpy(&flow_req->hdr, &req->hdr, sizeof(struct mbox_msghdr));
memcpy(req, flow_req, sizeof(struct npc_install_flow_req));
req->alloc_entry = 1;
/* Allocate very least priority for first rule */
if (hw_priority || list_empty(&flow_cfg->flow_list_tc)) {
req->ref_prio = NPC_MCAM_LEAST_PRIO;
} else {
req->ref_prio = NPC_MCAM_HIGHER_PRIO;
tmp = list_first_entry(&flow_cfg->flow_list_tc,
struct otx2_tc_flow, list);
req->ref_entry = tmp->entry;
}
ret = otx2_sync_mbox_msg(&nic->mbox);
if (ret)
return ret;
rsp = (struct npc_install_flow_rsp *)otx2_mbox_get_rsp(&nic->mbox.mbox,
0, &req->hdr);
if (IS_ERR(rsp))
return -EFAULT;
if (entry)
*entry = rsp->entry;
if (type)
*type = rsp->kw_type;
return ret;
}
int cn20k_tc_alloc_entry(struct otx2_nic *nic,
struct flow_cls_offload *tc_flow_cmd,
struct otx2_tc_flow *new_node,
struct npc_install_flow_req *flow_req)
{
bool hw_priority = false;
u16 entry_from_af;
u8 entry_type;
int ret;
if (is_otx2_vf(nic->pcifunc))
flow_req->hw_prio = MAX_TC_VF_PRIORITY;
else
flow_req->hw_prio = MAX_TC_PF_PRIORITY;
if (new_node->prio <= MAX_TC_HW_PRIORITY) {
flow_req->hw_prio = new_node->prio;
hw_priority = true;
}
mutex_lock(&nic->mbox.lock);
ret = __cn20k_tc_alloc_entry(nic, flow_req, &entry_from_af, &entry_type,
new_node->prio, hw_priority);
if (ret) {
mutex_unlock(&nic->mbox.lock);
return ret;
}
new_node->kw_type = entry_type;
new_node->entry = entry_from_af;
mutex_unlock(&nic->mbox.lock);
return 0;
}
static int cn20k_aura_aq_init(struct otx2_nic *pfvf, int aura_id,
int pool_id, int numptrs)
{
struct npa_cn20k_aq_enq_req *aq;
struct otx2_pool *pool;
u8 bpid_idx;
int err;
pool = &pfvf->qset.pool[pool_id];
/* Allocate memory for HW to update Aura count.
* Alloc one cache line, so that it fits all FC_STYPE modes.
*/
if (!pool->fc_addr) {
err = qmem_alloc(pfvf->dev, &pool->fc_addr, 1, OTX2_ALIGN);
if (err)
return err;
}
/* Initialize this aura's context via AF */
aq = otx2_mbox_alloc_msg_npa_cn20k_aq_enq(&pfvf->mbox);
if (!aq) {
/* Shared mbox memory buffer is full, flush it and retry */
err = otx2_sync_mbox_msg(&pfvf->mbox);
if (err)
return err;
aq = otx2_mbox_alloc_msg_npa_cn20k_aq_enq(&pfvf->mbox);
if (!aq)
return -ENOMEM;
}
aq->aura_id = aura_id;
/* Will be filled by AF with correct pool context address */
aq->aura.pool_addr = pool_id;
aq->aura.pool_caching = 1;
aq->aura.shift = ilog2(numptrs) - 8;
aq->aura.count = numptrs;
aq->aura.limit = numptrs;
aq->aura.avg_level = 255;
aq->aura.ena = 1;
aq->aura.fc_ena = 1;
aq->aura.fc_addr = pool->fc_addr->iova;
aq->aura.fc_hyst_bits = 0; /* Store count on all updates */
/* Enable backpressure for RQ aura */
if (aura_id < pfvf->hw.rqpool_cnt && !is_otx2_lbkvf(pfvf->pdev)) {
aq->aura.bp_ena = 0;
/* If NIX1 LF is attached then specify NIX1_RX.
*
* Below NPA_AURA_S[BP_ENA] is set according to the
* NPA_BPINTF_E enumeration given as:
* 0x0 + a*0x1 where 'a' is 0 for NIX0_RX and 1 for NIX1_RX so
* NIX0_RX is 0x0 + 0*0x1 = 0
* NIX1_RX is 0x0 + 1*0x1 = 1
* But in HRM it is given that
* "NPA_AURA_S[BP_ENA](w1[33:32]) - Enable aura backpressure to
* NIX-RX based on [BP] level. One bit per NIX-RX; index
* enumerated by NPA_BPINTF_E."
*/
if (pfvf->nix_blkaddr == BLKADDR_NIX1)
aq->aura.bp_ena = 1;
bpid_idx = cn20k_aura_bpid_idx(pfvf, aura_id);
aq->aura.bpid = pfvf->bpid[bpid_idx];
/* Set backpressure level for RQ's Aura */
aq->aura.bp = RQ_BP_LVL_AURA;
}
/* Fill AQ info */
aq->ctype = NPA_AQ_CTYPE_AURA;
aq->op = NPA_AQ_INSTOP_INIT;
return 0;
}
static int cn20k_pool_aq_init(struct otx2_nic *pfvf, u16 pool_id,
int stack_pages, int numptrs, int buf_size,
int type)
{
struct page_pool_params pp_params = { 0 };
struct npa_cn20k_aq_enq_req *aq;
struct otx2_pool *pool;
int err, sz;
pool = &pfvf->qset.pool[pool_id];
/* Alloc memory for stack which is used to store buffer pointers */
err = qmem_alloc(pfvf->dev, &pool->stack,
stack_pages, pfvf->hw.stack_pg_bytes);
if (err)
return err;
pool->rbsize = buf_size;
/* Initialize this pool's context via AF */
aq = otx2_mbox_alloc_msg_npa_cn20k_aq_enq(&pfvf->mbox);
if (!aq) {
/* Shared mbox memory buffer is full, flush it and retry */
err = otx2_sync_mbox_msg(&pfvf->mbox);
if (err) {
qmem_free(pfvf->dev, pool->stack);
pool->stack = NULL;
return err;
}
aq = otx2_mbox_alloc_msg_npa_cn20k_aq_enq(&pfvf->mbox);
if (!aq) {
qmem_free(pfvf->dev, pool->stack);
pool->stack = NULL;
return -ENOMEM;
}
}
aq->aura_id = pool_id;
aq->pool.stack_base = pool->stack->iova;
aq->pool.stack_caching = 1;
aq->pool.ena = 1;
aq->pool.buf_size = buf_size / 128;
aq->pool.stack_max_pages = stack_pages;
aq->pool.shift = ilog2(numptrs) - 8;
aq->pool.ptr_start = 0;
aq->pool.ptr_end = ~0ULL;
/* Fill AQ info */
aq->ctype = NPA_AQ_CTYPE_POOL;
aq->op = NPA_AQ_INSTOP_INIT;
if (type != AURA_NIX_RQ) {
pool->page_pool = NULL;
return 0;
}
sz = ALIGN(ALIGN(SKB_DATA_ALIGN(buf_size), OTX2_ALIGN), PAGE_SIZE);
pp_params.order = get_order(sz);
pp_params.flags = PP_FLAG_DMA_MAP;
pp_params.pool_size = min(OTX2_PAGE_POOL_SZ, numptrs);
pp_params.nid = NUMA_NO_NODE;
pp_params.dev = pfvf->dev;
pp_params.dma_dir = DMA_FROM_DEVICE;
pool->page_pool = page_pool_create(&pp_params);
if (IS_ERR(pool->page_pool)) {
netdev_err(pfvf->netdev, "Creation of page pool failed\n");
return PTR_ERR(pool->page_pool);
}
return 0;
}
static int cn20k_sq_aq_init(void *dev, u16 qidx, u8 chan_offset, u16 sqb_aura)
{
struct nix_cn20k_aq_enq_req *aq;
struct otx2_nic *pfvf = dev;
/* Get memory to put this msg */
aq = otx2_mbox_alloc_msg_nix_cn20k_aq_enq(&pfvf->mbox);
if (!aq)
return -ENOMEM;
aq->sq.cq = pfvf->hw.rx_queues + qidx;
aq->sq.max_sqe_size = NIX_MAXSQESZ_W16; /* 128 byte */
aq->sq.cq_ena = 1;
aq->sq.ena = 1;
aq->sq.smq = otx2_get_smq_idx(pfvf, qidx);
aq->sq.smq_rr_weight = mtu_to_dwrr_weight(pfvf, pfvf->tx_max_pktlen);
aq->sq.default_chan = pfvf->hw.tx_chan_base + chan_offset;
aq->sq.sqe_stype = NIX_STYPE_STF; /* Cache SQB */
aq->sq.sqb_aura = sqb_aura;
aq->sq.sq_int_ena = NIX_SQINT_BITS;
aq->sq.qint_idx = 0;
/* Due pipelining impact minimum 2000 unused SQ CQE's
* need to maintain to avoid CQ overflow.
*/
aq->sq.cq_limit = (SEND_CQ_SKID * 256) / (pfvf->qset.sqe_cnt);
/* Fill AQ info */
aq->qidx = qidx;
aq->ctype = NIX_AQ_CTYPE_SQ;
aq->op = NIX_AQ_INSTOP_INIT;
return otx2_sync_mbox_msg(&pfvf->mbox);
}
static struct dev_hw_ops cn20k_hw_ops = {
.pfaf_mbox_intr_handler = cn20k_pfaf_mbox_intr_handler,
.vfaf_mbox_intr_handler = cn20k_vfaf_mbox_intr_handler,
.pfvf_mbox_intr_handler = cn20k_pfvf_mbox_intr_handler,
.sq_aq_init = cn20k_sq_aq_init,
.sqe_flush = cn10k_sqe_flush,
.aura_freeptr = cn10k_aura_freeptr,
.refill_pool_ptrs = cn10k_refill_pool_ptrs,
.aura_aq_init = cn20k_aura_aq_init,
.pool_aq_init = cn20k_pool_aq_init,
};
void cn20k_init(struct otx2_nic *pfvf)
{
pfvf->hw_ops = &cn20k_hw_ops;
}
EXPORT_SYMBOL(cn20k_init);
@@ -0,0 +1,31 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Marvell RVU Ethernet driver
*
* Copyright (C) 2024 Marvell.
*
*/
#ifndef CN20K_H
#define CN20K_H
#include "otx2_common.h"
struct otx2_flow_config;
struct otx2_tc_flow;
void cn20k_init(struct otx2_nic *pfvf);
int cn20k_register_pfvf_mbox_intr(struct otx2_nic *pf, int numvfs);
void cn20k_disable_pfvf_mbox_intr(struct otx2_nic *pf, int numvfs);
void cn20k_enable_pfvf_mbox_intr(struct otx2_nic *pf, int numvfs);
void cn20k_tc_update_mcam_table_del_req(struct otx2_nic *nic,
struct otx2_flow_config *flow_cfg,
struct otx2_tc_flow *node);
int cn20k_tc_update_mcam_table_add_req(struct otx2_nic *nic,
struct otx2_flow_config *flow_cfg,
struct otx2_tc_flow *node);
int cn20k_tc_alloc_entry(struct otx2_nic *nic,
struct flow_cls_offload *tc_flow_cmd,
struct otx2_tc_flow *new_node,
struct npc_install_flow_req *dummy);
int cn20k_tc_free_mcam_entry(struct otx2_nic *nic, u16 entry);
#endif /* CN20K_H */
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,489 @@
// SPDX-License-Identifier: GPL-2.0
/* Marvell RVU Ethernet driver
*
* Copyright (C) 2021 Marvell.
*
*/
#include "otx2_common.h"
static int otx2_check_pfc_config(struct otx2_nic *pfvf)
{
u8 tx_queues = pfvf->hw.tx_queues, prio;
u8 pfc_en = pfvf->pfc_en;
for (prio = 0; prio < NIX_PF_PFC_PRIO_MAX; prio++) {
if ((pfc_en & (1 << prio)) &&
prio > tx_queues - 1) {
dev_warn(pfvf->dev,
"Increase number of tx queues from %d to %d to support PFC.\n",
tx_queues, prio + 1);
return -EINVAL;
}
}
return 0;
}
int otx2_pfc_txschq_config(struct otx2_nic *pfvf)
{
u8 pfc_en, pfc_bit_set;
int prio, lvl, err;
pfc_en = pfvf->pfc_en;
for (prio = 0; prio < NIX_PF_PFC_PRIO_MAX; prio++) {
pfc_bit_set = pfc_en & (1 << prio);
/* Either PFC bit is not set
* or tx scheduler is not allocated for the priority
*/
if (!pfc_bit_set || !pfvf->pfc_alloc_status[prio])
continue;
/* configure the scheduler for the tls*/
for (lvl = 0; lvl < NIX_TXSCH_LVL_CNT; lvl++) {
err = otx2_txschq_config(pfvf, lvl, prio, true);
if (err) {
dev_err(pfvf->dev,
"%s configure PFC tx schq for lvl:%d, prio:%d failed!\n",
__func__, lvl, prio);
return err;
}
}
}
return 0;
}
EXPORT_SYMBOL(otx2_pfc_txschq_config);
static int otx2_pfc_txschq_alloc_one(struct otx2_nic *pfvf, u8 prio)
{
struct nix_txsch_alloc_req *req;
struct nix_txsch_alloc_rsp *rsp;
int lvl, rc;
/* Get memory to put this msg */
req = otx2_mbox_alloc_msg_nix_txsch_alloc(&pfvf->mbox);
if (!req)
return -ENOMEM;
/* Request one schq per level upto max level as configured
* link config level. These rest of the scheduler can be
* same as hw.txschq_list.
*/
for (lvl = 0; lvl <= pfvf->hw.txschq_link_cfg_lvl; lvl++)
req->schq[lvl] = 1;
rc = otx2_sync_mbox_msg(&pfvf->mbox);
if (rc)
return rc;
rsp = (struct nix_txsch_alloc_rsp *)
otx2_mbox_get_rsp(&pfvf->mbox.mbox, 0, &req->hdr);
if (IS_ERR(rsp))
return PTR_ERR(rsp);
/* Setup transmit scheduler list */
for (lvl = 0; lvl <= pfvf->hw.txschq_link_cfg_lvl; lvl++) {
if (!rsp->schq[lvl])
return -ENOSPC;
pfvf->pfc_schq_list[lvl][prio] = rsp->schq_list[lvl][0];
}
/* Set the Tx schedulers for rest of the levels same as
* hw.txschq_list as those will be common for all.
*/
for (; lvl < NIX_TXSCH_LVL_CNT; lvl++)
pfvf->pfc_schq_list[lvl][prio] = pfvf->hw.txschq_list[lvl][0];
pfvf->pfc_alloc_status[prio] = true;
return 0;
}
int otx2_pfc_txschq_alloc(struct otx2_nic *pfvf)
{
u8 pfc_en = pfvf->pfc_en;
u8 pfc_bit_set;
int err, prio;
for (prio = 0; prio < NIX_PF_PFC_PRIO_MAX; prio++) {
pfc_bit_set = pfc_en & (1 << prio);
if (!pfc_bit_set || pfvf->pfc_alloc_status[prio])
continue;
/* Add new scheduler to the priority */
err = otx2_pfc_txschq_alloc_one(pfvf, prio);
if (err) {
dev_err(pfvf->dev, "%s failed to allocate PFC TX schedulers\n", __func__);
return err;
}
}
return 0;
}
EXPORT_SYMBOL(otx2_pfc_txschq_alloc);
static int otx2_pfc_txschq_stop_one(struct otx2_nic *pfvf, u8 prio)
{
int lvl;
/* free PFC TLx nodes */
for (lvl = 0; lvl <= pfvf->hw.txschq_link_cfg_lvl; lvl++)
otx2_txschq_free_one(pfvf, lvl,
pfvf->pfc_schq_list[lvl][prio]);
pfvf->pfc_alloc_status[prio] = false;
return 0;
}
static int otx2_pfc_update_sq_smq_mapping(struct otx2_nic *pfvf, int prio)
{
struct nix_cn10k_aq_enq_req *cn10k_sq_aq;
struct net_device *dev = pfvf->netdev;
bool if_up = netif_running(dev);
struct nix_aq_enq_req *sq_aq;
if (if_up) {
if (pfvf->pfc_alloc_status[prio])
netif_tx_stop_all_queues(pfvf->netdev);
else
netif_tx_stop_queue(netdev_get_tx_queue(dev, prio));
}
if (test_bit(CN10K_LMTST, &pfvf->hw.cap_flag)) {
cn10k_sq_aq = otx2_mbox_alloc_msg_nix_cn10k_aq_enq(&pfvf->mbox);
if (!cn10k_sq_aq)
return -ENOMEM;
/* Fill AQ info */
cn10k_sq_aq->qidx = prio;
cn10k_sq_aq->ctype = NIX_AQ_CTYPE_SQ;
cn10k_sq_aq->op = NIX_AQ_INSTOP_WRITE;
/* Fill fields to update */
cn10k_sq_aq->sq.ena = 1;
cn10k_sq_aq->sq_mask.ena = 1;
cn10k_sq_aq->sq_mask.smq = GENMASK(9, 0);
cn10k_sq_aq->sq.smq = otx2_get_smq_idx(pfvf, prio);
} else {
sq_aq = otx2_mbox_alloc_msg_nix_aq_enq(&pfvf->mbox);
if (!sq_aq)
return -ENOMEM;
/* Fill AQ info */
sq_aq->qidx = prio;
sq_aq->ctype = NIX_AQ_CTYPE_SQ;
sq_aq->op = NIX_AQ_INSTOP_WRITE;
/* Fill fields to update */
sq_aq->sq.ena = 1;
sq_aq->sq_mask.ena = 1;
sq_aq->sq_mask.smq = GENMASK(8, 0);
sq_aq->sq.smq = otx2_get_smq_idx(pfvf, prio);
}
otx2_sync_mbox_msg(&pfvf->mbox);
if (if_up) {
if (pfvf->pfc_alloc_status[prio])
netif_tx_start_all_queues(pfvf->netdev);
else
netif_tx_start_queue(netdev_get_tx_queue(dev, prio));
}
return 0;
}
int otx2_pfc_txschq_update(struct otx2_nic *pfvf)
{
bool if_up = netif_running(pfvf->netdev);
u8 pfc_en = pfvf->pfc_en, pfc_bit_set;
struct mbox *mbox = &pfvf->mbox;
int err, prio;
mutex_lock(&mbox->lock);
for (prio = 0; prio < NIX_PF_PFC_PRIO_MAX; prio++) {
pfc_bit_set = pfc_en & (1 << prio);
/* tx scheduler was created but user wants to disable now */
if (!pfc_bit_set && pfvf->pfc_alloc_status[prio]) {
mutex_unlock(&mbox->lock);
if (if_up)
netif_tx_stop_all_queues(pfvf->netdev);
otx2_smq_flush(pfvf, pfvf->pfc_schq_list[NIX_TXSCH_LVL_SMQ][prio]);
if (if_up)
netif_tx_start_all_queues(pfvf->netdev);
/* delete the schq */
err = otx2_pfc_txschq_stop_one(pfvf, prio);
if (err) {
dev_err(pfvf->dev,
"%s failed to stop PFC tx schedulers for priority: %d\n",
__func__, prio);
return err;
}
mutex_lock(&mbox->lock);
goto update_sq_smq_map;
}
/* Either PFC bit is not set
* or Tx scheduler is already mapped for the priority
*/
if (!pfc_bit_set || pfvf->pfc_alloc_status[prio])
continue;
/* Add new scheduler to the priority */
err = otx2_pfc_txschq_alloc_one(pfvf, prio);
if (err) {
mutex_unlock(&mbox->lock);
dev_err(pfvf->dev,
"%s failed to allocate PFC tx schedulers for priority: %d\n",
__func__, prio);
return err;
}
update_sq_smq_map:
err = otx2_pfc_update_sq_smq_mapping(pfvf, prio);
if (err) {
mutex_unlock(&mbox->lock);
dev_err(pfvf->dev, "%s failed PFC Tx schq sq:%d mapping", __func__, prio);
return err;
}
}
err = otx2_pfc_txschq_config(pfvf);
mutex_unlock(&mbox->lock);
if (err)
return err;
return 0;
}
EXPORT_SYMBOL(otx2_pfc_txschq_update);
int otx2_pfc_txschq_stop(struct otx2_nic *pfvf)
{
u8 pfc_en, pfc_bit_set;
int prio, err;
pfc_en = pfvf->pfc_en;
for (prio = 0; prio < NIX_PF_PFC_PRIO_MAX; prio++) {
pfc_bit_set = pfc_en & (1 << prio);
if (!pfc_bit_set || !pfvf->pfc_alloc_status[prio])
continue;
/* Delete the existing scheduler */
err = otx2_pfc_txschq_stop_one(pfvf, prio);
if (err) {
dev_err(pfvf->dev, "%s failed to stop PFC TX schedulers\n", __func__);
return err;
}
}
return 0;
}
EXPORT_SYMBOL(otx2_pfc_txschq_stop);
int otx2_config_priority_flow_ctrl(struct otx2_nic *pfvf)
{
struct cgx_pfc_cfg *req;
struct cgx_pfc_rsp *rsp;
int err = 0;
if (is_otx2_lbkvf(pfvf->pdev))
return 0;
mutex_lock(&pfvf->mbox.lock);
req = otx2_mbox_alloc_msg_cgx_prio_flow_ctrl_cfg(&pfvf->mbox);
if (!req) {
err = -ENOMEM;
goto unlock;
}
if (pfvf->pfc_en) {
req->rx_pause = true;
req->tx_pause = true;
} else {
req->rx_pause = false;
req->tx_pause = false;
}
req->pfc_en = pfvf->pfc_en;
if (!otx2_sync_mbox_msg(&pfvf->mbox)) {
rsp = (struct cgx_pfc_rsp *)
otx2_mbox_get_rsp(&pfvf->mbox.mbox, 0, &req->hdr);
if (IS_ERR(rsp)) {
err = PTR_ERR(rsp);
goto unlock;
}
if (req->rx_pause != rsp->rx_pause || req->tx_pause != rsp->tx_pause) {
dev_warn(pfvf->dev,
"Failed to config PFC\n");
err = -EPERM;
}
}
unlock:
mutex_unlock(&pfvf->mbox.lock);
return err;
}
EXPORT_SYMBOL(otx2_config_priority_flow_ctrl);
void otx2_update_bpid_in_rqctx(struct otx2_nic *pfvf, int vlan_prio, int qidx,
bool pfc_enable)
{
bool if_up = netif_running(pfvf->netdev);
struct npa_aq_enq_req *npa_aq;
struct nix_aq_enq_req *aq;
int err = 0;
if (pfvf->queue_to_pfc_map[qidx] && pfc_enable) {
dev_warn(pfvf->dev,
"PFC enable not permitted as Priority %d already mapped to Queue %d\n",
pfvf->queue_to_pfc_map[qidx], qidx);
return;
}
if (if_up) {
netif_tx_stop_all_queues(pfvf->netdev);
netif_carrier_off(pfvf->netdev);
}
pfvf->queue_to_pfc_map[qidx] = vlan_prio;
aq = otx2_mbox_alloc_msg_nix_aq_enq(&pfvf->mbox);
if (!aq) {
err = -ENOMEM;
goto out;
}
aq->cq.bpid = pfvf->bpid[vlan_prio];
aq->cq_mask.bpid = GENMASK(8, 0);
/* Fill AQ info */
aq->qidx = qidx;
aq->ctype = NIX_AQ_CTYPE_CQ;
aq->op = NIX_AQ_INSTOP_WRITE;
otx2_sync_mbox_msg(&pfvf->mbox);
npa_aq = otx2_mbox_alloc_msg_npa_aq_enq(&pfvf->mbox);
if (!npa_aq) {
err = -ENOMEM;
goto out;
}
npa_aq->aura.nix0_bpid = pfvf->bpid[vlan_prio];
npa_aq->aura_mask.nix0_bpid = GENMASK(8, 0);
/* Fill NPA AQ info */
npa_aq->aura_id = qidx;
npa_aq->ctype = NPA_AQ_CTYPE_AURA;
npa_aq->op = NPA_AQ_INSTOP_WRITE;
otx2_sync_mbox_msg(&pfvf->mbox);
out:
if (if_up) {
netif_carrier_on(pfvf->netdev);
netif_tx_start_all_queues(pfvf->netdev);
}
if (err)
dev_warn(pfvf->dev,
"Updating BPIDs in CQ and Aura contexts of RQ%d failed with err %d\n",
qidx, err);
}
EXPORT_SYMBOL(otx2_update_bpid_in_rqctx);
static int otx2_dcbnl_ieee_getpfc(struct net_device *dev, struct ieee_pfc *pfc)
{
struct otx2_nic *pfvf = netdev_priv(dev);
pfc->pfc_cap = IEEE_8021QAZ_MAX_TCS;
pfc->pfc_en = pfvf->pfc_en;
return 0;
}
static int otx2_dcbnl_ieee_setpfc(struct net_device *dev, struct ieee_pfc *pfc)
{
struct otx2_nic *pfvf = netdev_priv(dev);
u8 old_pfc_en;
int err;
old_pfc_en = pfvf->pfc_en;
pfvf->pfc_en = pfc->pfc_en;
if (pfvf->hw.tx_queues >= NIX_PF_PFC_PRIO_MAX)
goto process_pfc;
/* Check if the PFC configuration can be
* supported by the tx queue configuration
*/
err = otx2_check_pfc_config(pfvf);
if (err) {
pfvf->pfc_en = old_pfc_en;
return err;
}
process_pfc:
err = otx2_config_priority_flow_ctrl(pfvf);
if (err) {
pfvf->pfc_en = old_pfc_en;
return err;
}
/* Default disable backpressure on NIX-CPT */
otx2_nix_cpt_config_bp(pfvf, false);
/* Request Per channel Bpids */
if (pfc->pfc_en)
otx2_nix_config_bp(pfvf, true);
err = otx2_pfc_txschq_update(pfvf);
if (err) {
if (pfc->pfc_en)
otx2_nix_config_bp(pfvf, false);
otx2_pfc_txschq_stop(pfvf);
pfvf->pfc_en = old_pfc_en;
otx2_config_priority_flow_ctrl(pfvf);
dev_err(pfvf->dev, "%s failed to update TX schedulers\n", __func__);
return err;
}
return 0;
}
static u8 otx2_dcbnl_getdcbx(struct net_device __always_unused *dev)
{
return DCB_CAP_DCBX_HOST | DCB_CAP_DCBX_VER_IEEE;
}
static u8 otx2_dcbnl_setdcbx(struct net_device __always_unused *dev, u8 mode)
{
return (mode != (DCB_CAP_DCBX_HOST | DCB_CAP_DCBX_VER_IEEE)) ? 1 : 0;
}
static const struct dcbnl_rtnl_ops otx2_dcbnl_ops = {
.ieee_getpfc = otx2_dcbnl_ieee_getpfc,
.ieee_setpfc = otx2_dcbnl_ieee_setpfc,
.getdcbx = otx2_dcbnl_getdcbx,
.setdcbx = otx2_dcbnl_setdcbx,
};
int otx2_dcbnl_set_ops(struct net_device *dev)
{
struct otx2_nic *pfvf = netdev_priv(dev);
pfvf->queue_to_pfc_map = devm_kzalloc(pfvf->dev, pfvf->hw.rx_queues,
GFP_KERNEL);
if (!pfvf->queue_to_pfc_map)
return -ENOMEM;
dev->dcbnl_ops = &otx2_dcbnl_ops;
return 0;
}
EXPORT_SYMBOL(otx2_dcbnl_set_ops);
@@ -0,0 +1,244 @@
// SPDX-License-Identifier: GPL-2.0
/* Marvell RVU PF/VF Netdev Devlink
*
* Copyright (C) 2021 Marvell.
*/
#include "otx2_common.h"
/* Devlink Params APIs */
static int otx2_dl_mcam_count_validate(struct devlink *devlink, u32 id,
union devlink_param_value *val,
struct netlink_ext_ack *extack)
{
struct otx2_devlink *otx2_dl = devlink_priv(devlink);
struct otx2_nic *pfvf = otx2_dl->pfvf;
struct otx2_flow_config *flow_cfg;
if (!pfvf->flow_cfg) {
NL_SET_ERR_MSG_MOD(extack,
"pfvf->flow_cfg not initialized");
return -EINVAL;
}
flow_cfg = pfvf->flow_cfg;
if (flow_cfg && flow_cfg->nr_flows) {
NL_SET_ERR_MSG_MOD(extack,
"Cannot modify count when there are active rules");
return -EINVAL;
}
return 0;
}
static int otx2_dl_mcam_count_set(struct devlink *devlink, u32 id,
struct devlink_param_gset_ctx *ctx,
struct netlink_ext_ack *extack)
{
struct otx2_devlink *otx2_dl = devlink_priv(devlink);
struct otx2_nic *pfvf = otx2_dl->pfvf;
if (!pfvf->flow_cfg)
return 0;
pfvf->flow_cfg->ntuple_cnt = ctx->val.vu16;
otx2_alloc_mcam_entries(pfvf, ctx->val.vu16);
return 0;
}
static int otx2_dl_mcam_count_get(struct devlink *devlink, u32 id,
struct devlink_param_gset_ctx *ctx,
struct netlink_ext_ack *extack)
{
struct otx2_devlink *otx2_dl = devlink_priv(devlink);
struct otx2_nic *pfvf = otx2_dl->pfvf;
struct otx2_flow_config *flow_cfg;
if (!pfvf->flow_cfg) {
ctx->val.vu16 = 0;
return 0;
}
flow_cfg = pfvf->flow_cfg;
ctx->val.vu16 = flow_cfg->max_flows;
return 0;
}
static int otx2_dl_ucast_flt_cnt_set(struct devlink *devlink, u32 id,
struct devlink_param_gset_ctx *ctx,
struct netlink_ext_ack *extack)
{
struct otx2_devlink *otx2_dl = devlink_priv(devlink);
struct otx2_nic *pfvf = otx2_dl->pfvf;
int err;
pfvf->flow_cfg->ucast_flt_cnt = ctx->val.vu8;
otx2_mcam_flow_del(pfvf);
err = otx2_mcam_entry_init(pfvf);
if (err)
return err;
return 0;
}
static int otx2_dl_ucast_flt_cnt_get(struct devlink *devlink, u32 id,
struct devlink_param_gset_ctx *ctx,
struct netlink_ext_ack *extack)
{
struct otx2_devlink *otx2_dl = devlink_priv(devlink);
struct otx2_nic *pfvf = otx2_dl->pfvf;
ctx->val.vu8 = pfvf->flow_cfg ? pfvf->flow_cfg->ucast_flt_cnt : 0;
return 0;
}
static int otx2_dl_ucast_flt_cnt_validate(struct devlink *devlink, u32 id,
union devlink_param_value *val,
struct netlink_ext_ack *extack)
{
struct otx2_devlink *otx2_dl = devlink_priv(devlink);
struct otx2_nic *pfvf = otx2_dl->pfvf;
/* Check for UNICAST filter support*/
if (!(pfvf->flags & OTX2_FLAG_UCAST_FLTR_SUPPORT)) {
NL_SET_ERR_MSG_MOD(extack,
"Unicast filter not enabled");
return -EINVAL;
}
if (!pfvf->flow_cfg) {
NL_SET_ERR_MSG_MOD(extack,
"pfvf->flow_cfg not initialized");
return -EINVAL;
}
if (pfvf->flow_cfg->nr_flows) {
NL_SET_ERR_MSG_MOD(extack,
"Cannot modify count when there are active rules");
return -EINVAL;
}
return 0;
}
enum otx2_dl_param_id {
OTX2_DEVLINK_PARAM_ID_BASE = DEVLINK_PARAM_GENERIC_ID_MAX,
OTX2_DEVLINK_PARAM_ID_MCAM_COUNT,
OTX2_DEVLINK_PARAM_ID_UCAST_FLT_CNT,
};
static const struct devlink_param otx2_dl_params[] = {
DEVLINK_PARAM_DRIVER(OTX2_DEVLINK_PARAM_ID_MCAM_COUNT,
"mcam_count", DEVLINK_PARAM_TYPE_U16,
BIT(DEVLINK_PARAM_CMODE_RUNTIME),
otx2_dl_mcam_count_get, otx2_dl_mcam_count_set,
otx2_dl_mcam_count_validate),
DEVLINK_PARAM_DRIVER(OTX2_DEVLINK_PARAM_ID_UCAST_FLT_CNT,
"unicast_filter_count", DEVLINK_PARAM_TYPE_U8,
BIT(DEVLINK_PARAM_CMODE_RUNTIME),
otx2_dl_ucast_flt_cnt_get, otx2_dl_ucast_flt_cnt_set,
otx2_dl_ucast_flt_cnt_validate),
};
#ifdef CONFIG_RVU_ESWITCH
static int otx2_devlink_eswitch_mode_get(struct devlink *devlink, u16 *mode)
{
struct otx2_devlink *otx2_dl = devlink_priv(devlink);
struct otx2_nic *pfvf = otx2_dl->pfvf;
if (!otx2_rep_dev(pfvf->pdev))
return -EOPNOTSUPP;
*mode = pfvf->esw_mode;
return 0;
}
static int otx2_devlink_eswitch_mode_set(struct devlink *devlink, u16 mode,
struct netlink_ext_ack *extack)
{
struct otx2_devlink *otx2_dl = devlink_priv(devlink);
struct otx2_nic *pfvf = otx2_dl->pfvf;
int ret = 0;
if (!otx2_rep_dev(pfvf->pdev))
return -EOPNOTSUPP;
if (pfvf->esw_mode == mode)
return 0;
switch (mode) {
case DEVLINK_ESWITCH_MODE_LEGACY:
rvu_rep_destroy(pfvf);
break;
case DEVLINK_ESWITCH_MODE_SWITCHDEV:
ret = rvu_rep_create(pfvf, extack);
break;
default:
return -EINVAL;
}
if (!ret)
pfvf->esw_mode = mode;
return ret;
}
#endif
static const struct devlink_ops otx2_devlink_ops = {
#ifdef CONFIG_RVU_ESWITCH
.eswitch_mode_get = otx2_devlink_eswitch_mode_get,
.eswitch_mode_set = otx2_devlink_eswitch_mode_set,
#endif
};
int otx2_register_dl(struct otx2_nic *pfvf)
{
struct otx2_devlink *otx2_dl;
struct devlink *dl;
int err;
dl = devlink_alloc(&otx2_devlink_ops,
sizeof(struct otx2_devlink), pfvf->dev);
if (!dl) {
dev_warn(pfvf->dev, "devlink_alloc failed\n");
return -ENOMEM;
}
otx2_dl = devlink_priv(dl);
otx2_dl->dl = dl;
otx2_dl->pfvf = pfvf;
pfvf->dl = otx2_dl;
err = devlink_params_register(dl, otx2_dl_params,
ARRAY_SIZE(otx2_dl_params));
if (err) {
dev_err(pfvf->dev,
"devlink params register failed with error %d", err);
goto err_dl;
}
devlink_register(dl);
return 0;
err_dl:
devlink_free(dl);
return err;
}
EXPORT_SYMBOL(otx2_register_dl);
void otx2_unregister_dl(struct otx2_nic *pfvf)
{
struct otx2_devlink *otx2_dl = pfvf->dl;
struct devlink *dl = otx2_dl->dl;
devlink_unregister(dl);
devlink_params_unregister(dl, otx2_dl_params,
ARRAY_SIZE(otx2_dl_params));
devlink_free(dl);
}
EXPORT_SYMBOL(otx2_unregister_dl);
@@ -0,0 +1,20 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Marvell RVU PF/VF Netdev Devlink
*
* Copyright (C) 2021 Marvell.
*
*/
#ifndef OTX2_DEVLINK_H
#define OTX2_DEVLINK_H
struct otx2_devlink {
struct devlink *dl;
struct otx2_nic *pfvf;
};
/* Devlink APIs */
int otx2_register_dl(struct otx2_nic *pfvf);
void otx2_unregister_dl(struct otx2_nic *pfvf);
#endif /* OTX2_DEVLINK_H */
@@ -0,0 +1,218 @@
// SPDX-License-Identifier: GPL-2.0
/* Marvell RVU Ethernet driver
*
* Copyright (C) 2021 Marvell.
*
*/
#include "otx2_common.h"
static int otx2_dmacflt_do_add(struct otx2_nic *pf, const u8 *mac,
u32 *dmac_index)
{
struct cgx_mac_addr_add_req *req;
struct cgx_mac_addr_add_rsp *rsp;
int err;
mutex_lock(&pf->mbox.lock);
req = otx2_mbox_alloc_msg_cgx_mac_addr_add(&pf->mbox);
if (!req) {
mutex_unlock(&pf->mbox.lock);
return -ENOMEM;
}
ether_addr_copy(req->mac_addr, mac);
err = otx2_sync_mbox_msg(&pf->mbox);
if (!err) {
rsp = (struct cgx_mac_addr_add_rsp *)
otx2_mbox_get_rsp(&pf->mbox.mbox, 0, &req->hdr);
if (IS_ERR(rsp)) {
mutex_unlock(&pf->mbox.lock);
return PTR_ERR(rsp);
}
*dmac_index = rsp->index;
}
mutex_unlock(&pf->mbox.lock);
return err;
}
static int otx2_dmacflt_add_pfmac(struct otx2_nic *pf, u32 *dmac_index)
{
struct cgx_mac_addr_set_or_get *req;
struct cgx_mac_addr_set_or_get *rsp;
int err;
mutex_lock(&pf->mbox.lock);
req = otx2_mbox_alloc_msg_cgx_mac_addr_set(&pf->mbox);
if (!req) {
mutex_unlock(&pf->mbox.lock);
return -ENOMEM;
}
req->index = *dmac_index;
ether_addr_copy(req->mac_addr, pf->netdev->dev_addr);
err = otx2_sync_mbox_msg(&pf->mbox);
if (err)
goto out;
rsp = (struct cgx_mac_addr_set_or_get *)
otx2_mbox_get_rsp(&pf->mbox.mbox, 0, &req->hdr);
if (IS_ERR_OR_NULL(rsp)) {
err = -EINVAL;
goto out;
}
*dmac_index = rsp->index;
out:
mutex_unlock(&pf->mbox.lock);
return err;
}
int otx2_dmacflt_add(struct otx2_nic *pf, const u8 *mac, u32 bit_pos)
{
u32 *dmacindex;
/* Store dmacindex returned by CGX/RPM driver which will
* be used for macaddr update/remove
*/
dmacindex = &pf->flow_cfg->bmap_to_dmacindex[bit_pos];
if (ether_addr_equal(mac, pf->netdev->dev_addr))
return otx2_dmacflt_add_pfmac(pf, dmacindex);
else
return otx2_dmacflt_do_add(pf, mac, dmacindex);
}
static int otx2_dmacflt_do_remove(struct otx2_nic *pfvf, const u8 *mac,
u32 dmac_index)
{
struct cgx_mac_addr_del_req *req;
int err;
mutex_lock(&pfvf->mbox.lock);
req = otx2_mbox_alloc_msg_cgx_mac_addr_del(&pfvf->mbox);
if (!req) {
mutex_unlock(&pfvf->mbox.lock);
return -ENOMEM;
}
req->index = dmac_index;
err = otx2_sync_mbox_msg(&pfvf->mbox);
mutex_unlock(&pfvf->mbox.lock);
return err;
}
static int otx2_dmacflt_remove_pfmac(struct otx2_nic *pf, u32 dmac_index)
{
struct cgx_mac_addr_reset_req *req;
int err;
mutex_lock(&pf->mbox.lock);
req = otx2_mbox_alloc_msg_cgx_mac_addr_reset(&pf->mbox);
if (!req) {
mutex_unlock(&pf->mbox.lock);
return -ENOMEM;
}
req->index = dmac_index;
err = otx2_sync_mbox_msg(&pf->mbox);
mutex_unlock(&pf->mbox.lock);
return err;
}
int otx2_dmacflt_remove(struct otx2_nic *pf, const u8 *mac,
u32 bit_pos)
{
u32 dmacindex = pf->flow_cfg->bmap_to_dmacindex[bit_pos];
if (ether_addr_equal(mac, pf->netdev->dev_addr))
return otx2_dmacflt_remove_pfmac(pf, dmacindex);
else
return otx2_dmacflt_do_remove(pf, mac, dmacindex);
}
/* CGX/RPM blocks support max unicast entries of 32.
* on typical configuration MAC block associated
* with 4 lmacs, each lmac will have 8 dmac entries
*/
int otx2_dmacflt_get_max_cnt(struct otx2_nic *pf)
{
struct cgx_max_dmac_entries_get_rsp *rsp;
struct msg_req *msg;
int err;
mutex_lock(&pf->mbox.lock);
msg = otx2_mbox_alloc_msg_cgx_mac_max_entries_get(&pf->mbox);
if (!msg) {
mutex_unlock(&pf->mbox.lock);
return -ENOMEM;
}
err = otx2_sync_mbox_msg(&pf->mbox);
if (err)
goto out;
rsp = (struct cgx_max_dmac_entries_get_rsp *)
otx2_mbox_get_rsp(&pf->mbox.mbox, 0, &msg->hdr);
if (IS_ERR_OR_NULL(rsp)) {
err = -EINVAL;
goto out;
}
pf->flow_cfg->dmacflt_max_flows = rsp->max_dmac_filters;
out:
mutex_unlock(&pf->mbox.lock);
return err;
}
int otx2_dmacflt_update(struct otx2_nic *pf, u8 *mac, u32 bit_pos)
{
struct cgx_mac_addr_update_req *req;
struct cgx_mac_addr_update_rsp *rsp;
int rc;
mutex_lock(&pf->mbox.lock);
req = otx2_mbox_alloc_msg_cgx_mac_addr_update(&pf->mbox);
if (!req) {
mutex_unlock(&pf->mbox.lock);
return -ENOMEM;
}
ether_addr_copy(req->mac_addr, mac);
req->index = pf->flow_cfg->bmap_to_dmacindex[bit_pos];
/* check the response and change index */
rc = otx2_sync_mbox_msg(&pf->mbox);
if (rc)
goto out;
rsp = (struct cgx_mac_addr_update_rsp *)
otx2_mbox_get_rsp(&pf->mbox.mbox, 0, &req->hdr);
if (IS_ERR(rsp)) {
rc = PTR_ERR(rsp);
goto out;
}
pf->flow_cfg->bmap_to_dmacindex[bit_pos] = rsp->index;
out:
mutex_unlock(&pf->mbox.lock);
return rc;
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,524 @@
// SPDX-License-Identifier: GPL-2.0
/* Marvell RVU Ethernet driver
*
* Copyright (C) 2020 Marvell.
*
*/
#include <linux/module.h>
#include "otx2_common.h"
#include "otx2_ptp.h"
static bool is_tstmp_atomic_update_supported(struct otx2_ptp *ptp)
{
struct ptp_get_cap_rsp *rsp;
struct msg_req *req;
int err;
if (!ptp->nic)
return false;
mutex_lock(&ptp->nic->mbox.lock);
req = otx2_mbox_alloc_msg_ptp_get_cap(&ptp->nic->mbox);
if (!req) {
mutex_unlock(&ptp->nic->mbox.lock);
return false;
}
err = otx2_sync_mbox_msg(&ptp->nic->mbox);
if (err) {
mutex_unlock(&ptp->nic->mbox.lock);
return false;
}
rsp = (struct ptp_get_cap_rsp *)otx2_mbox_get_rsp(&ptp->nic->mbox.mbox, 0,
&req->hdr);
mutex_unlock(&ptp->nic->mbox.lock);
if (IS_ERR(rsp))
return false;
if (rsp->cap & PTP_CAP_HW_ATOMIC_UPDATE)
return true;
return false;
}
static int otx2_ptp_hw_adjtime(struct ptp_clock_info *ptp_info, s64 delta)
{
struct otx2_ptp *ptp = container_of(ptp_info, struct otx2_ptp,
ptp_info);
struct otx2_nic *pfvf = ptp->nic;
struct ptp_req *req;
int rc;
if (!ptp->nic)
return -ENODEV;
mutex_lock(&pfvf->mbox.lock);
req = otx2_mbox_alloc_msg_ptp_op(&ptp->nic->mbox);
if (!req) {
mutex_unlock(&pfvf->mbox.lock);
return -ENOMEM;
}
req->op = PTP_OP_ADJTIME;
req->delta = delta;
rc = otx2_sync_mbox_msg(&ptp->nic->mbox);
mutex_unlock(&pfvf->mbox.lock);
return rc;
}
static u64 otx2_ptp_get_clock(struct otx2_ptp *ptp)
{
struct ptp_req *req;
struct ptp_rsp *rsp;
int err;
if (!ptp->nic)
return 0;
req = otx2_mbox_alloc_msg_ptp_op(&ptp->nic->mbox);
if (!req)
return 0;
req->op = PTP_OP_GET_CLOCK;
err = otx2_sync_mbox_msg(&ptp->nic->mbox);
if (err)
return 0;
rsp = (struct ptp_rsp *)otx2_mbox_get_rsp(&ptp->nic->mbox.mbox, 0,
&req->hdr);
if (IS_ERR(rsp))
return 0;
return rsp->clk;
}
static int otx2_ptp_hw_gettime(struct ptp_clock_info *ptp_info,
struct timespec64 *ts)
{
struct otx2_ptp *ptp = container_of(ptp_info, struct otx2_ptp,
ptp_info);
u64 tstamp;
tstamp = otx2_ptp_get_clock(ptp);
*ts = ns_to_timespec64(tstamp);
return 0;
}
static int otx2_ptp_hw_settime(struct ptp_clock_info *ptp_info,
const struct timespec64 *ts)
{
struct otx2_ptp *ptp = container_of(ptp_info, struct otx2_ptp,
ptp_info);
struct otx2_nic *pfvf = ptp->nic;
struct ptp_req *req;
u64 nsec;
int rc;
if (!ptp->nic)
return -ENODEV;
nsec = timespec64_to_ns(ts);
mutex_lock(&pfvf->mbox.lock);
req = otx2_mbox_alloc_msg_ptp_op(&ptp->nic->mbox);
if (!req) {
mutex_unlock(&pfvf->mbox.lock);
return -ENOMEM;
}
req->op = PTP_OP_SET_CLOCK;
req->clk = nsec;
rc = otx2_sync_mbox_msg(&ptp->nic->mbox);
mutex_unlock(&pfvf->mbox.lock);
return rc;
}
static int otx2_ptp_adjfine(struct ptp_clock_info *ptp_info, long scaled_ppm)
{
struct otx2_ptp *ptp = container_of(ptp_info, struct otx2_ptp,
ptp_info);
struct ptp_req *req;
if (!ptp->nic)
return -ENODEV;
req = otx2_mbox_alloc_msg_ptp_op(&ptp->nic->mbox);
if (!req)
return -ENOMEM;
req->op = PTP_OP_ADJFINE;
req->scaled_ppm = scaled_ppm;
return otx2_sync_mbox_msg(&ptp->nic->mbox);
}
static int ptp_set_thresh(struct otx2_ptp *ptp, u64 thresh)
{
struct ptp_req *req;
if (!ptp->nic)
return -ENODEV;
req = otx2_mbox_alloc_msg_ptp_op(&ptp->nic->mbox);
if (!req)
return -ENOMEM;
req->op = PTP_OP_SET_THRESH;
req->thresh = thresh;
return otx2_sync_mbox_msg(&ptp->nic->mbox);
}
static int ptp_pps_on(struct otx2_ptp *ptp, int on, u64 period)
{
struct ptp_req *req;
if (!ptp->nic)
return -ENODEV;
req = otx2_mbox_alloc_msg_ptp_op(&ptp->nic->mbox);
if (!req)
return -ENOMEM;
req->op = PTP_OP_PPS_ON;
req->pps_on = on;
req->period = period;
return otx2_sync_mbox_msg(&ptp->nic->mbox);
}
static u64 ptp_cc_read(struct cyclecounter *cc)
{
struct otx2_ptp *ptp = container_of(cc, struct otx2_ptp, cycle_counter);
return otx2_ptp_get_clock(ptp);
}
static u64 ptp_tstmp_read(struct otx2_ptp *ptp)
{
struct ptp_req *req;
struct ptp_rsp *rsp;
int err;
if (!ptp->nic)
return 0;
req = otx2_mbox_alloc_msg_ptp_op(&ptp->nic->mbox);
if (!req)
return 0;
req->op = PTP_OP_GET_TSTMP;
err = otx2_sync_mbox_msg(&ptp->nic->mbox);
if (err)
return 0;
rsp = (struct ptp_rsp *)otx2_mbox_get_rsp(&ptp->nic->mbox.mbox, 0,
&req->hdr);
if (IS_ERR(rsp))
return 0;
return rsp->clk;
}
static int otx2_ptp_tc_adjtime(struct ptp_clock_info *ptp_info, s64 delta)
{
struct otx2_ptp *ptp = container_of(ptp_info, struct otx2_ptp,
ptp_info);
struct otx2_nic *pfvf = ptp->nic;
mutex_lock(&pfvf->mbox.lock);
timecounter_adjtime(&ptp->time_counter, delta);
mutex_unlock(&pfvf->mbox.lock);
return 0;
}
static int otx2_ptp_tc_gettime(struct ptp_clock_info *ptp_info,
struct timespec64 *ts)
{
struct otx2_ptp *ptp = container_of(ptp_info, struct otx2_ptp,
ptp_info);
u64 tstamp;
mutex_lock(&ptp->nic->mbox.lock);
tstamp = timecounter_read(&ptp->time_counter);
mutex_unlock(&ptp->nic->mbox.lock);
*ts = ns_to_timespec64(tstamp);
return 0;
}
static int otx2_ptp_tc_settime(struct ptp_clock_info *ptp_info,
const struct timespec64 *ts)
{
struct otx2_ptp *ptp = container_of(ptp_info, struct otx2_ptp,
ptp_info);
u64 nsec;
nsec = timespec64_to_ns(ts);
mutex_lock(&ptp->nic->mbox.lock);
timecounter_init(&ptp->time_counter, &ptp->cycle_counter, nsec);
mutex_unlock(&ptp->nic->mbox.lock);
return 0;
}
static int otx2_ptp_verify_pin(struct ptp_clock_info *ptp, unsigned int pin,
enum ptp_pin_function func, unsigned int chan)
{
switch (func) {
case PTP_PF_NONE:
case PTP_PF_EXTTS:
case PTP_PF_PEROUT:
break;
case PTP_PF_PHYSYNC:
return -1;
}
return 0;
}
static u64 otx2_ptp_hw_tstamp2time(const struct timecounter *time_counter, u64 tstamp)
{
/* On HW which supports atomic updates, timecounter is not initialized */
return tstamp;
}
static void otx2_ptp_extts_check(struct work_struct *work)
{
struct otx2_ptp *ptp = container_of(work, struct otx2_ptp,
extts_work.work);
struct ptp_clock_event event;
u64 tstmp, new_thresh;
mutex_lock(&ptp->nic->mbox.lock);
tstmp = ptp_tstmp_read(ptp);
mutex_unlock(&ptp->nic->mbox.lock);
if (tstmp != ptp->last_extts) {
event.type = PTP_CLOCK_EXTTS;
event.index = 0;
event.timestamp = ptp->ptp_tstamp2nsec(&ptp->time_counter, tstmp);
ptp_clock_event(ptp->ptp_clock, &event);
new_thresh = tstmp % 500000000;
if (ptp->thresh != new_thresh) {
mutex_lock(&ptp->nic->mbox.lock);
ptp_set_thresh(ptp, new_thresh);
mutex_unlock(&ptp->nic->mbox.lock);
ptp->thresh = new_thresh;
}
ptp->last_extts = tstmp;
}
schedule_delayed_work(&ptp->extts_work, msecs_to_jiffies(200));
}
static void otx2_sync_tstamp(struct work_struct *work)
{
struct otx2_ptp *ptp = container_of(work, struct otx2_ptp,
synctstamp_work.work);
struct otx2_nic *pfvf = ptp->nic;
u64 tstamp;
mutex_lock(&pfvf->mbox.lock);
tstamp = otx2_ptp_get_clock(ptp);
mutex_unlock(&pfvf->mbox.lock);
ptp->tstamp = ptp->ptp_tstamp2nsec(&ptp->time_counter, tstamp);
ptp->base_ns = tstamp % NSEC_PER_SEC;
schedule_delayed_work(&ptp->synctstamp_work, msecs_to_jiffies(250));
}
static int otx2_ptp_enable(struct ptp_clock_info *ptp_info,
struct ptp_clock_request *rq, int on)
{
struct otx2_ptp *ptp = container_of(ptp_info, struct otx2_ptp,
ptp_info);
u64 period = 0;
int pin;
if (!ptp->nic)
return -ENODEV;
switch (rq->type) {
case PTP_CLK_REQ_EXTTS:
pin = ptp_find_pin(ptp->ptp_clock, PTP_PF_EXTTS,
rq->extts.index);
if (pin < 0)
return -EBUSY;
if (on)
schedule_delayed_work(&ptp->extts_work, msecs_to_jiffies(200));
else
cancel_delayed_work_sync(&ptp->extts_work);
return 0;
case PTP_CLK_REQ_PEROUT:
if (rq->perout.flags)
return -EOPNOTSUPP;
if (rq->perout.index >= ptp_info->n_pins)
return -EINVAL;
if (on) {
period = rq->perout.period.sec * NSEC_PER_SEC +
rq->perout.period.nsec;
ptp_pps_on(ptp, on, period);
} else {
ptp_pps_on(ptp, on, period);
}
return 0;
default:
break;
}
return -EOPNOTSUPP;
}
int otx2_ptp_init(struct otx2_nic *pfvf)
{
struct otx2_ptp *ptp_ptr;
struct cyclecounter *cc;
struct ptp_req *req;
int err;
if (is_otx2_lbkvf(pfvf->pdev)) {
pfvf->ptp = NULL;
return 0;
}
mutex_lock(&pfvf->mbox.lock);
/* check if PTP block is available */
req = otx2_mbox_alloc_msg_ptp_op(&pfvf->mbox);
if (!req) {
mutex_unlock(&pfvf->mbox.lock);
return -ENOMEM;
}
req->op = PTP_OP_GET_CLOCK;
err = otx2_sync_mbox_msg(&pfvf->mbox);
if (err) {
mutex_unlock(&pfvf->mbox.lock);
return err;
}
mutex_unlock(&pfvf->mbox.lock);
ptp_ptr = kzalloc_obj(*ptp_ptr);
if (!ptp_ptr) {
err = -ENOMEM;
goto error;
}
ptp_ptr->nic = pfvf;
snprintf(ptp_ptr->extts_config.name, sizeof(ptp_ptr->extts_config.name), "TSTAMP");
ptp_ptr->extts_config.index = 0;
ptp_ptr->extts_config.func = PTP_PF_NONE;
ptp_ptr->ptp_info = (struct ptp_clock_info) {
.owner = THIS_MODULE,
.name = "OcteonTX2 PTP",
.max_adj = 1000000000ull,
.n_ext_ts = 1,
.n_per_out = 1,
.n_pins = 1,
.pps = 0,
.pin_config = &ptp_ptr->extts_config,
.adjfine = otx2_ptp_adjfine,
.enable = otx2_ptp_enable,
.verify = otx2_ptp_verify_pin,
};
/* Check whether hardware supports atomic updates to timestamp */
if (is_tstmp_atomic_update_supported(ptp_ptr)) {
ptp_ptr->ptp_info.adjtime = otx2_ptp_hw_adjtime;
ptp_ptr->ptp_info.gettime64 = otx2_ptp_hw_gettime;
ptp_ptr->ptp_info.settime64 = otx2_ptp_hw_settime;
ptp_ptr->ptp_tstamp2nsec = otx2_ptp_hw_tstamp2time;
} else {
ptp_ptr->ptp_info.adjtime = otx2_ptp_tc_adjtime;
ptp_ptr->ptp_info.gettime64 = otx2_ptp_tc_gettime;
ptp_ptr->ptp_info.settime64 = otx2_ptp_tc_settime;
cc = &ptp_ptr->cycle_counter;
cc->read = ptp_cc_read;
cc->mask = CYCLECOUNTER_MASK(64);
cc->mult = 1;
cc->shift = 0;
ptp_ptr->ptp_tstamp2nsec = timecounter_cyc2time;
timecounter_init(&ptp_ptr->time_counter, &ptp_ptr->cycle_counter,
ktime_to_ns(ktime_get_real()));
}
INIT_DELAYED_WORK(&ptp_ptr->extts_work, otx2_ptp_extts_check);
ptp_ptr->ptp_clock = ptp_clock_register(&ptp_ptr->ptp_info, pfvf->dev);
if (IS_ERR_OR_NULL(ptp_ptr->ptp_clock)) {
err = ptp_ptr->ptp_clock ?
PTR_ERR(ptp_ptr->ptp_clock) : -ENODEV;
kfree(ptp_ptr);
goto error;
}
if (is_dev_otx2(pfvf->pdev)) {
ptp_ptr->convert_rx_ptp_tstmp = &otx2_ptp_convert_rx_timestamp;
ptp_ptr->convert_tx_ptp_tstmp = &otx2_ptp_convert_tx_timestamp;
} else {
ptp_ptr->convert_rx_ptp_tstmp = &cn10k_ptp_convert_timestamp;
ptp_ptr->convert_tx_ptp_tstmp = &cn10k_ptp_convert_timestamp;
}
INIT_DELAYED_WORK(&ptp_ptr->synctstamp_work, otx2_sync_tstamp);
pfvf->ptp = ptp_ptr;
error:
return err;
}
EXPORT_SYMBOL_GPL(otx2_ptp_init);
void otx2_ptp_destroy(struct otx2_nic *pfvf)
{
struct otx2_ptp *ptp = pfvf->ptp;
if (!ptp)
return;
cancel_delayed_work_sync(&pfvf->ptp->synctstamp_work);
ptp_clock_unregister(ptp->ptp_clock);
kfree(ptp);
pfvf->ptp = NULL;
}
EXPORT_SYMBOL_GPL(otx2_ptp_destroy);
int otx2_ptp_clock_index(struct otx2_nic *pfvf)
{
if (!pfvf->ptp)
return -ENODEV;
return ptp_clock_index(pfvf->ptp->ptp_clock);
}
EXPORT_SYMBOL_GPL(otx2_ptp_clock_index);
int otx2_ptp_tstamp2time(struct otx2_nic *pfvf, u64 tstamp, u64 *tsns)
{
if (!pfvf->ptp)
return -ENODEV;
*tsns = pfvf->ptp->ptp_tstamp2nsec(&pfvf->ptp->time_counter, tstamp);
return 0;
}
EXPORT_SYMBOL_GPL(otx2_ptp_tstamp2time);
MODULE_AUTHOR("Sunil Goutham <sgoutham@marvell.com>");
MODULE_DESCRIPTION("Marvell RVU NIC PTP Driver");
MODULE_LICENSE("GPL v2");
@@ -0,0 +1,32 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Marvell RVU Ethernet driver
*
* Copyright (C) 2020 Marvell.
*
*/
#ifndef OTX2_PTP_H
#define OTX2_PTP_H
static inline u64 otx2_ptp_convert_rx_timestamp(u64 timestamp)
{
return be64_to_cpu(*(__be64 *)&timestamp);
}
static inline u64 otx2_ptp_convert_tx_timestamp(u64 timestamp)
{
return timestamp;
}
static inline u64 cn10k_ptp_convert_timestamp(u64 timestamp)
{
return ((timestamp >> 32) * NSEC_PER_SEC) + (timestamp & 0xFFFFFFFFUL);
}
int otx2_ptp_init(struct otx2_nic *pfvf);
void otx2_ptp_destroy(struct otx2_nic *pfvf);
int otx2_ptp_clock_index(struct otx2_nic *pfvf);
int otx2_ptp_tstamp2time(struct otx2_nic *pfvf, u64 tstamp, u64 *tsns);
#endif
@@ -0,0 +1,165 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Marvell RVU Ethernet driver
*
* Copyright (C) 2020 Marvell.
*
*/
#ifndef OTX2_REG_H
#define OTX2_REG_H
#include <rvu_struct.h>
/* RVU PF registers */
#define RVU_PF_VFX_PFVF_MBOX0 (0x00000)
#define RVU_PF_VFX_PFVF_MBOX1 (0x00008)
#define RVU_PF_VFX_PFVF_MBOXX(a, b) (0x0 | (a) << 12 | (b) << 3)
#define RVU_PF_VF_BAR4_ADDR (0x10)
#define RVU_PF_BLOCK_ADDRX_DISC(a) (0x200 | (a) << 3)
#define RVU_PF_VFME_STATUSX(a) (0x800 | (a) << 3)
#define RVU_PF_VFTRPENDX(a) (0x820 | (a) << 3)
#define RVU_PF_VFTRPEND_W1SX(a) (0x840 | (a) << 3)
#define RVU_PF_VFPF_MBOX_INTX(a) (0x880 | (a) << 3)
#define RVU_PF_VFPF_MBOX_INT_W1SX(a) (0x8A0 | (a) << 3)
#define RVU_PF_VFPF_MBOX_INT_ENA_W1SX(a) (0x8C0 | (a) << 3)
#define RVU_PF_VFPF_MBOX_INT_ENA_W1CX(a) (0x8E0 | (a) << 3)
#define RVU_PF_VFFLR_INTX(a) (0x900 | (a) << 3)
#define RVU_PF_VFFLR_INT_W1SX(a) (0x920 | (a) << 3)
#define RVU_PF_VFFLR_INT_ENA_W1SX(a) (0x940 | (a) << 3)
#define RVU_PF_VFFLR_INT_ENA_W1CX(a) (0x960 | (a) << 3)
#define RVU_PF_VFME_INTX(a) (0x980 | (a) << 3)
#define RVU_PF_VFME_INT_W1SX(a) (0x9A0 | (a) << 3)
#define RVU_PF_VFME_INT_ENA_W1SX(a) (0x9C0 | (a) << 3)
#define RVU_PF_VFME_INT_ENA_W1CX(a) (0x9E0 | (a) << 3)
#define RVU_PF_PFAF_MBOX0 (0xC00)
#define RVU_PF_PFAF_MBOX1 (0xC08)
#define RVU_PF_PFAF_MBOXX(a) (0xC00 | (a) << 3)
#define RVU_PF_INT (0xc20)
#define RVU_PF_INT_W1S (0xc28)
#define RVU_PF_INT_ENA_W1S (0xc30)
#define RVU_PF_INT_ENA_W1C (0xc38)
#define RVU_PF_MSIX_VECX_ADDR(a) (0x000 | (a) << 4)
#define RVU_PF_MSIX_VECX_CTL(a) (0x008 | (a) << 4)
#define RVU_PF_MSIX_PBAX(a) (0xF0000 | (a) << 3)
#define RVU_PF_VF_MBOX_ADDR (0xC40)
#define RVU_PF_LMTLINE_ADDR (0xC48)
#define RVU_MBOX_PF_VFX_PFVF_TRIGX(a) (0x2000 | (a) << 3)
#define RVU_MBOX_PF_VFPF_INTX(a) (0x1000 | (a) << 3)
#define RVU_MBOX_PF_VFPF_INT_W1SX(a) (0x1020 | (a) << 3)
#define RVU_MBOX_PF_VFPF_INT_ENA_W1SX(a) (0x1040 | (a) << 3)
#define RVU_MBOX_PF_VFPF_INT_ENA_W1CX(a) (0x1060 | (a) << 3)
#define RVU_MBOX_PF_VFPF1_INTX(a) (0x1080 | (a) << 3)
#define RVU_MBOX_PF_VFPF1_INT_W1SX(a) (0x10a0 | (a) << 3)
#define RVU_MBOX_PF_VFPF1_INT_ENA_W1SX(a) (0x10c0 | (a) << 3)
#define RVU_MBOX_PF_VFPF1_INT_ENA_W1CX(a) (0x10e0 | (a) << 3)
/* RVU VF registers */
#define RVU_VF_VFPF_MBOX0 (0x00000)
#define RVU_VF_VFPF_MBOX1 (0x00008)
#define RVU_VF_VFPF_MBOXX(a) (0x00 | (a) << 3)
#define RVU_VF_INT (0x20)
#define RVU_VF_INT_W1S (0x28)
#define RVU_VF_INT_ENA_W1S (0x30)
#define RVU_VF_INT_ENA_W1C (0x38)
#define RVU_VF_BLOCK_ADDRX_DISC(a) (0x200 | (a) << 3)
#define RVU_VF_MSIX_VECX_ADDR(a) (0x000 | (a) << 4)
#define RVU_VF_MSIX_VECX_CTL(a) (0x008 | (a) << 4)
#define RVU_VF_MSIX_PBAX(a) (0xF0000 | (a) << 3)
#define RVU_VF_MBOX_REGION (0xC0000)
/* CN20K RVU_MBOX_E: RVU PF/VF MBOX Address Range Enumeration */
#define RVU_MBOX_AF_PFX_ADDR(a) (0x5000 | (a) << 4)
#define RVU_PFX_FUNC_PFAF_MBOX (0x80000)
#define RVU_PFX_FUNCX_VFAF_MBOX (0x40000)
#define RVU_FUNC_BLKADDR_SHIFT 20
#define RVU_FUNC_BLKADDR_MASK 0x1FULL
/* NPA LF registers */
#define NPA_LFBASE (BLKTYPE_NPA << RVU_FUNC_BLKADDR_SHIFT)
#define NPA_LF_AURA_OP_ALLOCX(a) (NPA_LFBASE | 0x10 | (a) << 3)
#define NPA_LF_AURA_OP_FREE0 (NPA_LFBASE | 0x20)
#define NPA_LF_AURA_OP_FREE1 (NPA_LFBASE | 0x28)
#define NPA_LF_AURA_OP_CNT (NPA_LFBASE | 0x30)
#define NPA_LF_AURA_OP_LIMIT (NPA_LFBASE | 0x50)
#define NPA_LF_AURA_OP_INT (NPA_LFBASE | 0x60)
#define NPA_LF_AURA_OP_THRESH (NPA_LFBASE | 0x70)
#define NPA_LF_POOL_OP_PC (NPA_LFBASE | 0x100)
#define NPA_LF_POOL_OP_AVAILABLE (NPA_LFBASE | 0x110)
#define NPA_LF_POOL_OP_PTR_START0 (NPA_LFBASE | 0x120)
#define NPA_LF_POOL_OP_PTR_START1 (NPA_LFBASE | 0x128)
#define NPA_LF_POOL_OP_PTR_END0 (NPA_LFBASE | 0x130)
#define NPA_LF_POOL_OP_PTR_END1 (NPA_LFBASE | 0x138)
#define NPA_LF_POOL_OP_INT (NPA_LFBASE | 0x160)
#define NPA_LF_POOL_OP_THRESH (NPA_LFBASE | 0x170)
#define NPA_LF_ERR_INT (NPA_LFBASE | 0x200)
#define NPA_LF_ERR_INT_W1S (NPA_LFBASE | 0x208)
#define NPA_LF_ERR_INT_ENA_W1C (NPA_LFBASE | 0x210)
#define NPA_LF_ERR_INT_ENA_W1S (NPA_LFBASE | 0x218)
#define NPA_LF_RAS (NPA_LFBASE | 0x220)
#define NPA_LF_RAS_W1S (NPA_LFBASE | 0x228)
#define NPA_LF_RAS_ENA_W1C (NPA_LFBASE | 0x230)
#define NPA_LF_RAS_ENA_W1S (NPA_LFBASE | 0x238)
#define NPA_LF_QINTX_CNT(a) (NPA_LFBASE | 0x300 | (a) << 12)
#define NPA_LF_QINTX_INT(a) (NPA_LFBASE | 0x310 | (a) << 12)
#define NPA_LF_QINTX_INT_W1S(a) (NPA_LFBASE | 0x318 | (a) << 12)
#define NPA_LF_QINTX_ENA_W1S(a) (NPA_LFBASE | 0x320 | (a) << 12)
#define NPA_LF_QINTX_ENA_W1C(a) (NPA_LFBASE | 0x330 | (a) << 12)
#define NPA_LF_AURA_BATCH_FREE0 (NPA_LFBASE | 0x400)
/* NIX LF registers */
#define NIX_LFBASE (BLKTYPE_NIX << RVU_FUNC_BLKADDR_SHIFT)
#define NIX_LF_RX_SECRETX(a) (NIX_LFBASE | 0x0 | (a) << 3)
#define NIX_LF_CFG (NIX_LFBASE | 0x100)
#define NIX_LF_GINT (NIX_LFBASE | 0x200)
#define NIX_LF_GINT_W1S (NIX_LFBASE | 0x208)
#define NIX_LF_GINT_ENA_W1C (NIX_LFBASE | 0x210)
#define NIX_LF_GINT_ENA_W1S (NIX_LFBASE | 0x218)
#define NIX_LF_ERR_INT (NIX_LFBASE | 0x220)
#define NIX_LF_ERR_INT_W1S (NIX_LFBASE | 0x228)
#define NIX_LF_ERR_INT_ENA_W1C (NIX_LFBASE | 0x230)
#define NIX_LF_ERR_INT_ENA_W1S (NIX_LFBASE | 0x238)
#define NIX_LF_RAS (NIX_LFBASE | 0x240)
#define NIX_LF_RAS_W1S (NIX_LFBASE | 0x248)
#define NIX_LF_RAS_ENA_W1C (NIX_LFBASE | 0x250)
#define NIX_LF_RAS_ENA_W1S (NIX_LFBASE | 0x258)
#define NIX_LF_SQ_OP_ERR_DBG (NIX_LFBASE | 0x260)
#define NIX_LF_MNQ_ERR_DBG (NIX_LFBASE | 0x270)
#define NIX_LF_SEND_ERR_DBG (NIX_LFBASE | 0x280)
#define NIX_LF_TX_STATX(a) (NIX_LFBASE | 0x300 | (a) << 3)
#define NIX_LF_RX_STATX(a) (NIX_LFBASE | 0x400 | (a) << 3)
#define NIX_LF_OP_SENDX(a) (NIX_LFBASE | 0x800 | (a) << 3)
#define NIX_LF_RQ_OP_INT (NIX_LFBASE | 0x900)
#define NIX_LF_RQ_OP_OCTS (NIX_LFBASE | 0x910)
#define NIX_LF_RQ_OP_PKTS (NIX_LFBASE | 0x920)
#define NIX_LF_OP_IPSEC_DYNO_CN (NIX_LFBASE | 0x980)
#define NIX_LF_SQ_OP_INT (NIX_LFBASE | 0xa00)
#define NIX_LF_SQ_OP_OCTS (NIX_LFBASE | 0xa10)
#define NIX_LF_SQ_OP_PKTS (NIX_LFBASE | 0xa20)
#define NIX_LF_SQ_OP_STATUS (NIX_LFBASE | 0xa30)
#define NIX_LF_CQ_OP_INT (NIX_LFBASE | 0xb00)
#define NIX_LF_CQ_OP_DOOR (NIX_LFBASE | 0xb30)
#define NIX_LF_CQ_OP_STATUS (NIX_LFBASE | 0xb40)
#define NIX_LF_QINTX_CNT(a) (NIX_LFBASE | 0xC00 | (a) << 12)
#define NIX_LF_QINTX_INT(a) (NIX_LFBASE | 0xC10 | (a) << 12)
#define NIX_LF_QINTX_INT_W1S(a) (NIX_LFBASE | 0xC18 | (a) << 12)
#define NIX_LF_QINTX_ENA_W1S(a) (NIX_LFBASE | 0xC20 | (a) << 12)
#define NIX_LF_QINTX_ENA_W1C(a) (NIX_LFBASE | 0xC30 | (a) << 12)
#define NIX_LF_CINTX_CNT(a) (NIX_LFBASE | 0xD00 | (a) << 12)
#define NIX_LF_CINTX_WAIT(a) (NIX_LFBASE | 0xD10 | (a) << 12)
#define NIX_LF_CINTX_INT(a) (NIX_LFBASE | 0xD20 | (a) << 12)
#define NIX_LF_CINTX_INT_W1S(a) (NIX_LFBASE | 0xD30 | (a) << 12)
#define NIX_LF_CINTX_ENA_W1S(a) (NIX_LFBASE | 0xD40 | (a) << 12)
#define NIX_LF_CINTX_ENA_W1C(a) (NIX_LFBASE | 0xD50 | (a) << 12)
/* LMT LF registers */
#define LMT_LFBASE BIT_ULL(RVU_FUNC_BLKADDR_SHIFT)
#define LMT_LF_LMTLINEX(a) (LMT_LFBASE | 0x000 | (a) << 12)
#define LMT_LF_LMTCANCEL (LMT_LFBASE | 0x400)
/* CN20K registers */
#define RVU_PF_DISC (0x0)
#endif /* OTX2_REG_H */
@@ -0,0 +1,341 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Marvell RVU Ethernet driver
*
* Copyright (C) 2020 Marvell.
*
*/
#ifndef OTX2_STRUCT_H
#define OTX2_STRUCT_H
/* NIX WQE/CQE size 128 byte or 512 byte */
enum nix_cqesz_e {
NIX_XQESZ_W64 = 0x0,
NIX_XQESZ_W16 = 0x1,
};
enum nix_sqes_e {
NIX_SQESZ_W16 = 0x0,
NIX_SQESZ_W8 = 0x1,
};
enum nix_send_ldtype {
NIX_SEND_LDTYPE_LDD = 0x0,
NIX_SEND_LDTYPE_LDT = 0x1,
NIX_SEND_LDTYPE_LDWB = 0x2,
};
/* CSUM offload */
enum nix_sendl3type {
NIX_SENDL3TYPE_NONE = 0x0,
NIX_SENDL3TYPE_IP4 = 0x2,
NIX_SENDL3TYPE_IP4_CKSUM = 0x3,
NIX_SENDL3TYPE_IP6 = 0x4,
};
enum nix_sendl4type {
NIX_SENDL4TYPE_NONE,
NIX_SENDL4TYPE_TCP_CKSUM,
NIX_SENDL4TYPE_SCTP_CKSUM,
NIX_SENDL4TYPE_UDP_CKSUM,
};
/* NIX wqe/cqe types */
enum nix_xqe_type {
NIX_XQE_TYPE_INVALID = 0x0,
NIX_XQE_TYPE_RX = 0x1,
NIX_XQE_TYPE_RX_IPSECS = 0x2,
NIX_XQE_TYPE_RX_IPSECH = 0x3,
NIX_XQE_TYPE_RX_IPSECD = 0x4,
NIX_XQE_TYPE_SEND = 0x8,
};
/* NIX CQE/SQE subdescriptor types */
enum nix_subdc {
NIX_SUBDC_NOP = 0x0,
NIX_SUBDC_EXT = 0x1,
NIX_SUBDC_CRC = 0x2,
NIX_SUBDC_IMM = 0x3,
NIX_SUBDC_SG = 0x4,
NIX_SUBDC_MEM = 0x5,
NIX_SUBDC_JUMP = 0x6,
NIX_SUBDC_WORK = 0x7,
NIX_SUBDC_SOD = 0xf,
};
/* Algorithm for nix_sqe_mem_s header (value of the `alg` field) */
enum nix_sendmemalg {
NIX_SENDMEMALG_E_SET = 0x0,
NIX_SENDMEMALG_E_SETTSTMP = 0x1,
NIX_SENDMEMALG_E_SETRSLT = 0x2,
NIX_SENDMEMALG_E_ADD = 0x8,
NIX_SENDMEMALG_E_SUB = 0x9,
NIX_SENDMEMALG_E_ADDLEN = 0xa,
NIX_SENDMEMALG_E_SUBLEN = 0xb,
NIX_SENDMEMALG_E_ADDMBUF = 0xc,
NIX_SENDMEMALG_E_SUBMBUF = 0xd,
NIX_SENDMEMALG_E_ENUM_LAST = 0xe,
};
/* NIX CQE header structure */
struct nix_cqe_hdr_s {
u64 flow_tag : 32;
u64 q : 20;
u64 reserved_52_57 : 6;
u64 node : 2;
u64 cqe_type : 4;
};
/* NIX CQE RX parse structure */
struct nix_rx_parse_s {
u64 chan : 12;
u64 desc_sizem1 : 5;
u64 rsvd_17 : 1;
u64 express : 1;
u64 wqwd : 1;
u64 errlev : 4;
u64 errcode : 8;
u64 latype : 4;
u64 lbtype : 4;
u64 lctype : 4;
u64 ldtype : 4;
u64 letype : 4;
u64 lftype : 4;
u64 lgtype : 4;
u64 lhtype : 4;
u64 pkt_lenm1 : 16; /* W1 */
u64 l2m : 1;
u64 l2b : 1;
u64 l3m : 1;
u64 l3b : 1;
u64 vtag0_valid : 1;
u64 vtag0_gone : 1;
u64 vtag1_valid : 1;
u64 vtag1_gone : 1;
u64 pkind : 6;
u64 rsvd_95_94 : 2;
u64 vtag0_tci : 16;
u64 vtag1_tci : 16;
u64 laflags : 8; /* W2 */
u64 lbflags : 8;
u64 lcflags : 8;
u64 ldflags : 8;
u64 leflags : 8;
u64 lfflags : 8;
u64 lgflags : 8;
u64 lhflags : 8;
u64 eoh_ptr : 8; /* W3 */
u64 wqe_aura : 20;
u64 pb_aura : 20;
u64 match_id : 16;
u64 laptr : 8; /* W4 */
u64 lbptr : 8;
u64 lcptr : 8;
u64 ldptr : 8;
u64 leptr : 8;
u64 lfptr : 8;
u64 lgptr : 8;
u64 lhptr : 8;
u64 vtag0_ptr : 8; /* W5 */
u64 vtag1_ptr : 8;
u64 flow_key_alg : 5;
u64 rsvd_359_341 : 19;
u64 color : 2;
u64 rsvd_383_362 : 22;
u64 rsvd_447_384; /* W6 */
};
/* NIX CQE RX scatter/gather subdescriptor structure */
struct nix_rx_sg_s {
u64 seg_size : 16; /* W0 */
u64 seg2_size : 16;
u64 seg3_size : 16;
u64 segs : 2;
u64 rsvd_59_50 : 10;
u64 subdc : 4;
u64 seg_addr;
u64 seg2_addr;
u64 seg3_addr;
};
struct nix_send_comp_s {
u64 status : 8;
u64 sqe_id : 16;
u64 rsvd_24_63 : 40;
};
struct nix_cqe_rx_s {
struct nix_cqe_hdr_s hdr;
struct nix_rx_parse_s parse;
struct nix_rx_sg_s sg;
};
struct nix_cqe_tx_s {
struct nix_cqe_hdr_s hdr;
struct nix_send_comp_s comp;
};
/* NIX SQE header structure */
struct nix_sqe_hdr_s {
u64 total : 18; /* W0 */
u64 reserved_18 : 1;
u64 df : 1;
u64 aura : 20;
u64 sizem1 : 3;
u64 pnc : 1;
u64 sq : 20;
u64 ol3ptr : 8; /* W1 */
u64 ol4ptr : 8;
u64 il3ptr : 8;
u64 il4ptr : 8;
u64 ol3type : 4;
u64 ol4type : 4;
u64 il3type : 4;
u64 il4type : 4;
u64 sqe_id : 16;
};
/* NIX send extended header subdescriptor structure */
struct nix_sqe_ext_s {
u64 lso_mps : 14; /* W0 */
u64 lso : 1;
u64 tstmp : 1;
u64 lso_sb : 8;
u64 lso_format : 5;
u64 rsvd_31_29 : 3;
u64 shp_chg : 9;
u64 shp_dis : 1;
u64 shp_ra : 2;
u64 markptr : 8;
u64 markform : 7;
u64 mark_en : 1;
u64 subdc : 4;
u64 vlan0_ins_ptr : 8; /* W1 */
u64 vlan0_ins_tci : 16;
u64 vlan1_ins_ptr : 8;
u64 vlan1_ins_tci : 16;
u64 vlan0_ins_ena : 1;
u64 vlan1_ins_ena : 1;
u64 init_color : 2;
u64 rsvd_127_116 : 12;
};
struct nix_sqe_sg_s {
u64 seg1_size : 16;
u64 seg2_size : 16;
u64 seg3_size : 16;
u64 segs : 2;
u64 rsvd_54_50 : 5;
u64 i1 : 1;
u64 i2 : 1;
u64 i3 : 1;
u64 ld_type : 2;
u64 subdc : 4;
};
/* NIX send memory subdescriptor structure */
struct nix_sqe_mem_s {
u64 start_offset : 8;
u64 rsvd_11_8 : 4;
u64 rsvd_12 : 1;
u64 udp_csum_crt : 1;
u64 update64 : 1;
u64 rsvd_15_16 : 1;
u64 base_ns : 32;
u64 step_type : 1;
u64 rsvd_51_49 : 3;
u64 per_lso_seg : 1;
u64 wmem : 1;
u64 dsz : 2;
u64 alg : 4;
u64 subdc : 4;
u64 addr; /* W1 */
};
enum nix_cqerrint_e {
NIX_CQERRINT_DOOR_ERR = 0,
NIX_CQERRINT_WR_FULL = 1,
NIX_CQERRINT_CQE_FAULT = 2,
};
#define NIX_CQERRINT_BITS (BIT_ULL(NIX_CQERRINT_DOOR_ERR) | \
BIT_ULL(NIX_CQERRINT_CQE_FAULT))
enum nix_rqint_e {
NIX_RQINT_DROP = 0,
NIX_RQINT_RED = 1,
};
#define NIX_RQINT_BITS (BIT_ULL(NIX_RQINT_DROP) | BIT_ULL(NIX_RQINT_RED))
enum nix_sqint_e {
NIX_SQINT_LMT_ERR = 0,
NIX_SQINT_MNQ_ERR = 1,
NIX_SQINT_SEND_ERR = 2,
NIX_SQINT_SQB_ALLOC_FAIL = 3,
};
#define NIX_SQINT_BITS (BIT_ULL(NIX_SQINT_LMT_ERR) | \
BIT_ULL(NIX_SQINT_MNQ_ERR) | \
BIT_ULL(NIX_SQINT_SEND_ERR) | \
BIT_ULL(NIX_SQINT_SQB_ALLOC_FAIL))
enum nix_sqoperr_e {
NIX_SQOPERR_OOR = 0,
NIX_SQOPERR_CTX_FAULT = 1,
NIX_SQOPERR_CTX_POISON = 2,
NIX_SQOPERR_DISABLED = 3,
NIX_SQOPERR_SIZE_ERR = 4,
NIX_SQOPERR_OFLOW = 5,
NIX_SQOPERR_SQB_NULL = 6,
NIX_SQOPERR_SQB_FAULT = 7,
NIX_SQOPERR_SQE_SZ_ZERO = 8,
NIX_SQOPERR_MAX,
};
enum nix_mnqerr_e {
NIX_MNQERR_SQ_CTX_FAULT = 0,
NIX_MNQERR_SQ_CTX_POISON = 1,
NIX_MNQERR_SQB_FAULT = 2,
NIX_MNQERR_SQB_POISON = 3,
NIX_MNQERR_TOTAL_ERR = 4,
NIX_MNQERR_LSO_ERR = 5,
NIX_MNQERR_CQ_QUERY_ERR = 6,
NIX_MNQERR_MAX_SQE_SIZE_ERR = 7,
NIX_MNQERR_MAXLEN_ERR = 8,
NIX_MNQERR_SQE_SIZEM1_ZERO = 9,
NIX_MNQERR_MAX,
};
enum nix_snd_status_e {
NIX_SND_STATUS_GOOD = 0x0,
NIX_SND_STATUS_SQ_CTX_FAULT = 0x1,
NIX_SND_STATUS_SQ_CTX_POISON = 0x2,
NIX_SND_STATUS_SQB_FAULT = 0x3,
NIX_SND_STATUS_SQB_POISON = 0x4,
NIX_SND_STATUS_HDR_ERR = 0x5,
NIX_SND_STATUS_EXT_ERR = 0x6,
NIX_SND_STATUS_JUMP_FAULT = 0x7,
NIX_SND_STATUS_JUMP_POISON = 0x8,
NIX_SND_STATUS_CRC_ERR = 0x10,
NIX_SND_STATUS_IMM_ERR = 0x11,
NIX_SND_STATUS_SG_ERR = 0x12,
NIX_SND_STATUS_MEM_ERR = 0x13,
NIX_SND_STATUS_INVALID_SUBDC = 0x14,
NIX_SND_STATUS_SUBDC_ORDER_ERR = 0x15,
NIX_SND_STATUS_DATA_FAULT = 0x16,
NIX_SND_STATUS_DATA_POISON = 0x17,
NIX_SND_STATUS_NPC_DROP_ACTION = 0x20,
NIX_SND_STATUS_LOCK_VIOL = 0x21,
NIX_SND_STATUS_NPC_UCAST_CHAN_ERR = 0x22,
NIX_SND_STATUS_NPC_MCAST_CHAN_ERR = 0x23,
NIX_SND_STATUS_NPC_MCAST_ABORT = 0x24,
NIX_SND_STATUS_NPC_VTAG_PTR_ERR = 0x25,
NIX_SND_STATUS_NPC_VTAG_SIZE_ERR = 0x26,
NIX_SND_STATUS_SEND_MEM_FAULT = 0x27,
NIX_SND_STATUS_SEND_STATS_ERR = 0x28,
NIX_SND_STATUS_MAX,
};
#endif /* OTX2_STRUCT_H */
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,191 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Marvell RVU Ethernet driver
*
* Copyright (C) 2020 Marvell.
*
*/
#ifndef OTX2_TXRX_H
#define OTX2_TXRX_H
#include <linux/etherdevice.h>
#include <linux/iommu.h>
#include <linux/if_vlan.h>
#include <net/xdp.h>
#include <net/xdp_sock_drv.h>
#define LBK_CHAN_BASE 0x000
#define SDP_CHAN_BASE 0x700
#define CGX_CHAN_BASE 0x800
#define OTX2_DATA_ALIGN(X) ALIGN(X, OTX2_ALIGN)
#define OTX2_HEAD_ROOM OTX2_ALIGN
#define OTX2_ETH_HLEN (VLAN_ETH_HLEN + VLAN_HLEN)
#define OTX2_MIN_MTU 60
#define OTX2_PAGE_POOL_SZ 2048
#define OTX2_MAX_GSO_SEGS 255
#define OTX2_MAX_FRAGS_IN_SQE 9
#define MAX_XDP_MTU (1530 - OTX2_ETH_HLEN)
/* Rx buffer size should be in multiples of 128bytes */
#define RCV_FRAG_LEN1(x) \
((OTX2_HEAD_ROOM + OTX2_DATA_ALIGN(x)) + \
OTX2_DATA_ALIGN(sizeof(struct skb_shared_info)))
/* Prefer 2048 byte buffers for better last level cache
* utilization or data distribution across regions.
*/
#define RCV_FRAG_LEN(x) \
((RCV_FRAG_LEN1(x) < 2048) ? 2048 : RCV_FRAG_LEN1(x))
#define DMA_BUFFER_LEN(x) ((x) - OTX2_HEAD_ROOM)
/* IRQ triggered when NIX_LF_CINTX_CNT[ECOUNT]
* is equal to this value.
*/
#define CQ_CQE_THRESH_DEFAULT 10
/* IRQ triggered when NIX_LF_CINTX_CNT[ECOUNT]
* is nonzero and this much time elapses after that.
*/
#define CQ_TIMER_THRESH_DEFAULT 1 /* 1 usec */
#define CQ_TIMER_THRESH_MAX 25 /* 25 usec */
/* Min number of CQs (of the ones mapped to this CINT)
* with valid CQEs.
*/
#define CQ_QCOUNT_DEFAULT 1
#define CQ_OP_STAT_OP_ERR 63
#define CQ_OP_STAT_CQ_ERR 46
/* Packet mark mask */
#define OTX2_RX_MATCH_ID_MASK 0x0000ffff
struct queue_stats {
u64 bytes;
u64 pkts;
};
struct otx2_rcv_queue {
struct queue_stats stats;
};
struct sg_list {
u16 num_segs;
u16 flags;
u64 skb;
u64 size[OTX2_MAX_FRAGS_IN_SQE];
u64 dma_addr[OTX2_MAX_FRAGS_IN_SQE];
};
struct otx2_snd_queue {
u8 aura_id;
u16 head;
u16 cons_head;
u16 sqe_size;
u32 sqe_cnt;
u16 num_sqbs;
u16 sqe_thresh;
u8 sqe_per_sqb;
u64 io_addr;
u64 *aura_fc_addr;
u64 *lmt_addr;
void *sqe_base;
struct qmem *sqe;
struct qmem *tso_hdrs;
struct sg_list *sg;
struct qmem *timestamps;
struct queue_stats stats;
u16 sqb_count;
u64 *sqb_ptrs;
/* SQE ring and CPT response queue for Inline IPSEC */
struct qmem *sqe_ring;
struct qmem *cpt_resp;
/* Buffer pool for af_xdp zero-copy */
struct xsk_buff_pool *xsk_pool;
} ____cacheline_aligned_in_smp;
enum cq_type {
CQ_RX,
CQ_TX,
CQ_XDP,
CQ_QOS,
CQS_PER_CINT = 4, /* RQ + SQ + XDP + QOS_SQ */
};
struct otx2_cq_poll {
void *dev;
#define CINT_INVALID_CQ 255
u8 cint_idx;
u8 cq_ids[CQS_PER_CINT];
struct dim dim;
struct napi_struct napi;
};
struct otx2_pool {
struct qmem *stack;
struct qmem *fc_addr;
struct page_pool *page_pool;
struct xsk_buff_pool *xsk_pool;
struct xdp_buff **xdp;
u16 xdp_cnt;
u16 rbsize;
u16 xdp_top;
};
struct otx2_cq_queue {
u8 cq_idx;
u8 cq_type;
u8 cint_idx; /* CQ interrupt id */
u8 refill_task_sched;
u16 cqe_size;
u16 pool_ptrs;
u32 cqe_cnt;
u32 cq_head;
u32 cq_tail;
u32 pend_cqe;
void *cqe_base;
struct qmem *cqe;
struct otx2_pool *rbpool;
bool xsk_zc_en;
struct xdp_rxq_info xdp_rxq;
} ____cacheline_aligned_in_smp;
struct otx2_qset {
u32 rqe_cnt;
u32 sqe_cnt; /* Keep these two at top */
#define OTX2_MAX_CQ_CNT 64
u16 cq_cnt;
u16 xqe_size;
struct otx2_pool *pool;
struct otx2_cq_poll *napi;
struct otx2_cq_queue *cq;
struct otx2_snd_queue *sq;
struct otx2_rcv_queue *rq;
};
/* Translate IOVA to physical address */
static inline u64 otx2_iova_to_phys(void *iommu_domain, dma_addr_t dma_addr)
{
/* Translation is installed only when IOMMU is present */
if (likely(iommu_domain))
return iommu_iova_to_phys(iommu_domain, dma_addr);
return dma_addr;
}
int otx2_napi_handler(struct napi_struct *napi, int budget);
bool otx2_sq_append_skb(void *dev, struct netdev_queue *txq,
struct otx2_snd_queue *sq,
struct sk_buff *skb, u16 qidx);
void cn10k_sqe_flush(void *dev, struct otx2_snd_queue *sq,
int size, int qidx);
void otx2_sqe_flush(void *dev, struct otx2_snd_queue *sq,
int size, int qidx);
int otx2_refill_pool_ptrs(void *dev, struct otx2_cq_queue *cq);
int cn10k_refill_pool_ptrs(void *dev, struct otx2_cq_queue *cq);
#endif /* OTX2_TXRX_H */
@@ -0,0 +1,882 @@
// SPDX-License-Identifier: GPL-2.0
/* Marvell RVU Virtual Function ethernet driver
*
* Copyright (C) 2020 Marvell.
*
*/
#include <linux/etherdevice.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/net_tstamp.h>
#include "otx2_common.h"
#include "otx2_reg.h"
#include "otx2_ptp.h"
#include "cn10k.h"
#include "cn10k_ipsec.h"
#define DRV_NAME "rvu_nicvf"
#define DRV_STRING "Marvell RVU NIC Virtual Function Driver"
static const struct pci_device_id otx2_vf_id_table[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, PCI_DEVID_OCTEONTX2_RVU_AFVF) },
{ PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, PCI_DEVID_OCTEONTX2_RVU_VF) },
{ PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, PCI_DEVID_OCTEONTX2_SDP_REP) },
{ }
};
MODULE_AUTHOR("Sunil Goutham <sgoutham@marvell.com>");
MODULE_DESCRIPTION(DRV_STRING);
MODULE_LICENSE("GPL v2");
MODULE_DEVICE_TABLE(pci, otx2_vf_id_table);
/* RVU VF Interrupt Vector Enumeration */
enum {
RVU_VF_INT_VEC_MBOX = 0x0,
};
static void otx2vf_process_vfaf_mbox_msg(struct otx2_nic *vf,
struct mbox_msghdr *msg)
{
if (msg->id >= MBOX_MSG_MAX) {
dev_err(vf->dev,
"Mbox msg with unknown ID %d\n", msg->id);
return;
}
if (msg->sig != OTX2_MBOX_RSP_SIG) {
dev_err(vf->dev,
"Mbox msg with wrong signature %x, ID %d\n",
msg->sig, msg->id);
return;
}
if (msg->rc == MBOX_MSG_INVALID) {
dev_err(vf->dev,
"PF/AF says the sent msg(s) %d were invalid\n",
msg->id);
return;
}
switch (msg->id) {
case MBOX_MSG_READY:
vf->pcifunc = msg->pcifunc;
break;
case MBOX_MSG_MSIX_OFFSET:
mbox_handler_msix_offset(vf, (struct msix_offset_rsp *)msg);
break;
case MBOX_MSG_NPA_LF_ALLOC:
mbox_handler_npa_lf_alloc(vf, (struct npa_lf_alloc_rsp *)msg);
break;
case MBOX_MSG_NIX_LF_ALLOC:
mbox_handler_nix_lf_alloc(vf, (struct nix_lf_alloc_rsp *)msg);
break;
case MBOX_MSG_NIX_BP_ENABLE:
mbox_handler_nix_bp_enable(vf, (struct nix_bp_cfg_rsp *)msg);
break;
default:
if (msg->rc)
dev_err(vf->dev,
"Mbox msg response has err %d, ID %d\n",
msg->rc, msg->id);
}
}
static void otx2vf_vfaf_mbox_handler(struct work_struct *work)
{
struct otx2_mbox_dev *mdev;
struct mbox_hdr *rsp_hdr;
struct mbox_msghdr *msg;
struct otx2_mbox *mbox;
struct mbox *af_mbox;
int offset, id;
u16 num_msgs;
af_mbox = container_of(work, struct mbox, mbox_wrk);
mbox = &af_mbox->mbox;
mdev = &mbox->dev[0];
rsp_hdr = (struct mbox_hdr *)(mdev->mbase + mbox->rx_start);
num_msgs = rsp_hdr->num_msgs;
if (num_msgs == 0)
return;
offset = mbox->rx_start + ALIGN(sizeof(*rsp_hdr), MBOX_MSG_ALIGN);
for (id = 0; id < num_msgs; id++) {
msg = (struct mbox_msghdr *)(mdev->mbase + offset);
otx2vf_process_vfaf_mbox_msg(af_mbox->pfvf, msg);
offset = mbox->rx_start + msg->next_msgoff;
if (mdev->msgs_acked == (af_mbox->num_msgs - 1))
__otx2_mbox_reset(mbox, 0);
mdev->msgs_acked++;
}
}
static int otx2vf_process_mbox_msg_up(struct otx2_nic *vf,
struct mbox_msghdr *req)
{
struct msg_rsp *rsp;
int err;
/* Check if valid, if not reply with a invalid msg */
if (req->sig != OTX2_MBOX_REQ_SIG) {
otx2_reply_invalid_msg(&vf->mbox.mbox_up, 0, 0, req->id);
return -ENODEV;
}
switch (req->id) {
case MBOX_MSG_CGX_LINK_EVENT:
rsp = (struct msg_rsp *)otx2_mbox_alloc_msg(
&vf->mbox.mbox_up, 0,
sizeof(struct msg_rsp));
if (!rsp)
return -ENOMEM;
rsp->hdr.id = MBOX_MSG_CGX_LINK_EVENT;
rsp->hdr.sig = OTX2_MBOX_RSP_SIG;
rsp->hdr.pcifunc = req->pcifunc;
rsp->hdr.rc = 0;
err = otx2_mbox_up_handler_cgx_link_event(
vf, (struct cgx_link_info_msg *)req, rsp);
return err;
default:
otx2_reply_invalid_msg(&vf->mbox.mbox_up, 0, 0, req->id);
return -ENODEV;
}
return 0;
}
static void otx2vf_vfaf_mbox_up_handler(struct work_struct *work)
{
struct otx2_mbox_dev *mdev;
struct mbox_hdr *rsp_hdr;
struct mbox_msghdr *msg;
struct otx2_mbox *mbox;
struct mbox *vf_mbox;
struct otx2_nic *vf;
int offset, id;
u16 num_msgs;
vf_mbox = container_of(work, struct mbox, mbox_up_wrk);
vf = vf_mbox->pfvf;
mbox = &vf_mbox->mbox_up;
mdev = &mbox->dev[0];
rsp_hdr = (struct mbox_hdr *)(mdev->mbase + mbox->rx_start);
num_msgs = rsp_hdr->num_msgs;
if (num_msgs == 0)
return;
offset = mbox->rx_start + ALIGN(sizeof(*rsp_hdr), MBOX_MSG_ALIGN);
for (id = 0; id < num_msgs; id++) {
msg = (struct mbox_msghdr *)(mdev->mbase + offset);
otx2vf_process_mbox_msg_up(vf, msg);
offset = mbox->rx_start + msg->next_msgoff;
}
otx2_mbox_msg_send(mbox, 0);
}
static irqreturn_t otx2vf_vfaf_mbox_intr_handler(int irq, void *vf_irq)
{
struct otx2_nic *vf = (struct otx2_nic *)vf_irq;
struct otx2_mbox_dev *mdev;
struct otx2_mbox *mbox;
struct mbox_hdr *hdr;
u64 mbox_data;
/* Clear the IRQ */
otx2_write64(vf, RVU_VF_INT, BIT_ULL(0));
mbox_data = otx2_read64(vf, RVU_VF_VFPF_MBOX0);
/* Read latest mbox data */
smp_rmb();
if (mbox_data & MBOX_DOWN_MSG) {
mbox_data &= ~MBOX_DOWN_MSG;
otx2_write64(vf, RVU_VF_VFPF_MBOX0, mbox_data);
/* Check for PF => VF response messages */
mbox = &vf->mbox.mbox;
mdev = &mbox->dev[0];
otx2_sync_mbox_bbuf(mbox, 0);
hdr = (struct mbox_hdr *)(mdev->mbase + mbox->rx_start);
if (hdr->num_msgs)
queue_work(vf->mbox_wq, &vf->mbox.mbox_wrk);
trace_otx2_msg_interrupt(mbox->pdev, "DOWN reply from PF to VF",
BIT_ULL(0));
}
if (mbox_data & MBOX_UP_MSG) {
mbox_data &= ~MBOX_UP_MSG;
otx2_write64(vf, RVU_VF_VFPF_MBOX0, mbox_data);
/* Check for PF => VF notification messages */
mbox = &vf->mbox.mbox_up;
mdev = &mbox->dev[0];
otx2_sync_mbox_bbuf(mbox, 0);
hdr = (struct mbox_hdr *)(mdev->mbase + mbox->rx_start);
if (hdr->num_msgs)
queue_work(vf->mbox_wq, &vf->mbox.mbox_up_wrk);
trace_otx2_msg_interrupt(mbox->pdev, "UP message from PF to VF",
BIT_ULL(0));
}
return IRQ_HANDLED;
}
static void otx2vf_disable_mbox_intr(struct otx2_nic *vf)
{
int vector = pci_irq_vector(vf->pdev, RVU_VF_INT_VEC_MBOX);
/* Disable VF => PF mailbox IRQ */
otx2_write64(vf, RVU_VF_INT_ENA_W1C, BIT_ULL(0));
if (is_cn20k(vf->pdev))
otx2_write64(vf, RVU_VF_INT_ENA_W1C, BIT_ULL(0) | BIT_ULL(1));
free_irq(vector, vf);
}
static int otx2vf_register_mbox_intr(struct otx2_nic *vf, bool probe_pf)
{
struct otx2_hw *hw = &vf->hw;
struct msg_req *req;
u64 mbox_int_mask;
char *irq_name;
int err;
mbox_int_mask = !is_cn20k(vf->pdev) ? BIT_ULL(0) :
BIT_ULL(0) | BIT_ULL(1) |
BIT_ULL(2) | BIT_ULL(3);
/* Clear stale mailbox interrupt state before installing the handler. */
otx2_write64(vf, RVU_VF_INT, mbox_int_mask);
/* Register mailbox interrupt handler */
irq_name = &hw->irq_name[RVU_VF_INT_VEC_MBOX * NAME_SIZE];
snprintf(irq_name, NAME_SIZE, "RVUVF%d AFVF Mbox", ((vf->pcifunc &
RVU_PFVF_FUNC_MASK) - 1));
if (!is_cn20k(vf->pdev)) {
err = request_irq(pci_irq_vector(vf->pdev, RVU_VF_INT_VEC_MBOX),
otx2vf_vfaf_mbox_intr_handler, 0, irq_name, vf);
} else {
err = request_irq(pci_irq_vector(vf->pdev, RVU_VF_INT_VEC_MBOX),
vf->hw_ops->vfaf_mbox_intr_handler, 0, irq_name,
vf);
}
if (err) {
dev_err(vf->dev,
"RVUPF: IRQ registration failed for VFAF mbox irq\n");
return err;
}
/* Enable mailbox interrupt for msgs coming from PF. */
otx2_write64(vf, RVU_VF_INT_ENA_W1S, mbox_int_mask);
if (!probe_pf)
return 0;
/* Check mailbox communication with PF */
req = otx2_mbox_alloc_msg_ready(&vf->mbox);
if (!req) {
otx2vf_disable_mbox_intr(vf);
return -ENOMEM;
}
err = otx2_sync_mbox_msg(&vf->mbox);
if (err) {
dev_warn(vf->dev,
"AF not responding to mailbox, deferring probe\n");
otx2vf_disable_mbox_intr(vf);
return -EPROBE_DEFER;
}
return 0;
}
static void otx2vf_vfaf_mbox_destroy(struct otx2_nic *vf)
{
struct mbox *mbox = &vf->mbox;
if (vf->mbox_wq) {
destroy_workqueue(vf->mbox_wq);
vf->mbox_wq = NULL;
}
if (mbox->mbox.hwbase && !test_bit(CN10K_MBOX, &vf->hw.cap_flag))
iounmap((void __iomem *)mbox->mbox.hwbase);
otx2_mbox_destroy(&mbox->mbox);
otx2_mbox_destroy(&mbox->mbox_up);
}
static int otx2vf_vfaf_mbox_init(struct otx2_nic *vf)
{
struct mbox *mbox = &vf->mbox;
void __iomem *hwbase;
int err;
mbox->pfvf = vf;
vf->mbox_wq = alloc_ordered_workqueue("otx2_vfaf_mailbox",
WQ_HIGHPRI | WQ_MEM_RECLAIM);
if (!vf->mbox_wq)
return -ENOMEM;
/* For cn20k platform, VF mailbox region is in dram aliased from AF
* VF MBOX ADDR, MBOX is a separate RVU block.
*/
if (is_cn20k(vf->pdev)) {
hwbase = vf->reg_base + RVU_VF_MBOX_REGION + ((u64)BLKADDR_MBOX <<
RVU_FUNC_BLKADDR_SHIFT);
} else if (test_bit(CN10K_MBOX, &vf->hw.cap_flag)) {
/* For cn10k platform, VF mailbox region is in its BAR2
* register space
*/
hwbase = vf->reg_base + RVU_VF_MBOX_REGION;
} else {
/* Mailbox is a reserved memory (in RAM) region shared between
* admin function (i.e PF0) and this VF, shouldn't be mapped as
* device memory to allow unaligned accesses.
*/
hwbase = ioremap_wc(pci_resource_start(vf->pdev,
PCI_MBOX_BAR_NUM),
pci_resource_len(vf->pdev,
PCI_MBOX_BAR_NUM));
if (!hwbase) {
dev_err(vf->dev, "Unable to map VFAF mailbox region\n");
err = -ENOMEM;
goto exit;
}
}
err = otx2_mbox_init(&mbox->mbox, hwbase, vf->pdev, vf->reg_base,
MBOX_DIR_VFPF, 1);
if (err)
goto exit;
err = otx2_mbox_init(&mbox->mbox_up, hwbase, vf->pdev, vf->reg_base,
MBOX_DIR_VFPF_UP, 1);
if (err)
goto exit;
err = otx2_mbox_bbuf_init(mbox, vf->pdev);
if (err)
goto exit;
INIT_WORK(&mbox->mbox_wrk, otx2vf_vfaf_mbox_handler);
INIT_WORK(&mbox->mbox_up_wrk, otx2vf_vfaf_mbox_up_handler);
mutex_init(&mbox->lock);
return 0;
exit:
if (hwbase && !test_bit(CN10K_MBOX, &vf->hw.cap_flag))
iounmap(hwbase);
destroy_workqueue(vf->mbox_wq);
return err;
}
static int otx2vf_open(struct net_device *netdev)
{
struct otx2_nic *vf;
int err;
err = otx2_open(netdev);
if (err)
return err;
/* LBKs do not receive link events so tell everyone we are up here */
vf = netdev_priv(netdev);
if (is_otx2_lbkvf(vf->pdev) || is_otx2_sdp_rep(vf->pdev)) {
pr_info("%s NIC Link is UP\n", netdev->name);
netif_carrier_on(netdev);
netif_tx_start_all_queues(netdev);
}
return 0;
}
static int otx2vf_stop(struct net_device *netdev)
{
return otx2_stop(netdev);
}
static netdev_tx_t otx2vf_xmit(struct sk_buff *skb, struct net_device *netdev)
{
struct otx2_nic *vf = netdev_priv(netdev);
int qidx = skb_get_queue_mapping(skb);
struct otx2_dev_stats *dev_stats;
struct otx2_snd_queue *sq;
struct netdev_queue *txq;
/* Check for minimum and maximum packet length */
if (skb->len <= ETH_HLEN ||
(!skb_shinfo(skb)->gso_size && skb->len > vf->tx_max_pktlen)) {
dev_stats = &vf->hw.dev_stats;
atomic_long_inc(&dev_stats->tx_discards);
dev_kfree_skb(skb);
return NETDEV_TX_OK;
}
sq = &vf->qset.sq[qidx];
txq = netdev_get_tx_queue(netdev, qidx);
if (!otx2_sq_append_skb(vf, txq, sq, skb, qidx)) {
netif_tx_stop_queue(txq);
/* Check again, incase SQBs got freed up */
smp_mb();
if (((sq->num_sqbs - *sq->aura_fc_addr) * sq->sqe_per_sqb)
> sq->sqe_thresh)
netif_tx_wake_queue(txq);
return NETDEV_TX_BUSY;
}
return NETDEV_TX_OK;
}
static void otx2vf_set_rx_mode(struct net_device *netdev)
{
struct otx2_nic *vf = netdev_priv(netdev);
queue_work(vf->otx2_wq, &vf->rx_mode_work);
}
static void otx2vf_do_set_rx_mode(struct work_struct *work)
{
struct otx2_nic *vf = container_of(work, struct otx2_nic, rx_mode_work);
struct net_device *netdev = vf->netdev;
unsigned int flags = netdev->flags;
struct nix_rx_mode *req;
mutex_lock(&vf->mbox.lock);
req = otx2_mbox_alloc_msg_nix_set_rx_mode(&vf->mbox);
if (!req) {
mutex_unlock(&vf->mbox.lock);
return;
}
req->mode = NIX_RX_MODE_UCAST;
if (flags & IFF_PROMISC)
req->mode |= NIX_RX_MODE_PROMISC;
if (flags & (IFF_ALLMULTI | IFF_MULTICAST))
req->mode |= NIX_RX_MODE_ALLMULTI;
req->mode |= NIX_RX_MODE_USE_MCE;
otx2_sync_mbox_msg(&vf->mbox);
mutex_unlock(&vf->mbox.lock);
}
static int otx2vf_change_mtu(struct net_device *netdev, int new_mtu)
{
bool if_up = netif_running(netdev);
int err = 0;
if (if_up)
otx2vf_stop(netdev);
netdev_info(netdev, "Changing MTU from %d to %d\n",
netdev->mtu, new_mtu);
WRITE_ONCE(netdev->mtu, new_mtu);
if (if_up)
err = otx2vf_open(netdev);
return err;
}
static void otx2vf_reset_task(struct work_struct *work)
{
struct otx2_nic *vf = container_of(work, struct otx2_nic, reset_task);
rtnl_lock();
if (netif_running(vf->netdev)) {
otx2vf_stop(vf->netdev);
vf->reset_count++;
otx2vf_open(vf->netdev);
}
rtnl_unlock();
}
static int otx2vf_set_features(struct net_device *netdev,
netdev_features_t features)
{
return otx2_handle_ntuple_tc_features(netdev, features);
}
static const struct net_device_ops otx2vf_netdev_ops = {
.ndo_open = otx2vf_open,
.ndo_stop = otx2vf_stop,
.ndo_start_xmit = otx2vf_xmit,
.ndo_select_queue = otx2_select_queue,
.ndo_set_rx_mode = otx2vf_set_rx_mode,
.ndo_set_mac_address = otx2_set_mac_address,
.ndo_change_mtu = otx2vf_change_mtu,
.ndo_set_features = otx2vf_set_features,
.ndo_get_stats64 = otx2_get_stats64,
.ndo_tx_timeout = otx2_tx_timeout,
.ndo_setup_tc = otx2_setup_tc,
.ndo_hwtstamp_get = otx2_config_hwtstamp_get,
.ndo_hwtstamp_set = otx2_config_hwtstamp_set,
};
static int otx2_vf_wq_init(struct otx2_nic *vf)
{
vf->otx2_wq = create_singlethread_workqueue("otx2vf_wq");
if (!vf->otx2_wq)
return -ENOMEM;
INIT_WORK(&vf->rx_mode_work, otx2vf_do_set_rx_mode);
INIT_WORK(&vf->reset_task, otx2vf_reset_task);
return 0;
}
static int otx2vf_realloc_msix_vectors(struct otx2_nic *vf)
{
struct otx2_hw *hw = &vf->hw;
int num_vec, err;
num_vec = hw->nix_msixoff;
num_vec += NIX_LF_CINT_VEC_START + hw->max_queues;
otx2vf_disable_mbox_intr(vf);
pci_free_irq_vectors(hw->pdev);
err = pci_alloc_irq_vectors(hw->pdev, num_vec, num_vec, PCI_IRQ_MSIX);
if (err < 0) {
dev_err(vf->dev, "%s: Failed to realloc %d IRQ vectors\n",
__func__, num_vec);
return err;
}
return otx2vf_register_mbox_intr(vf, false);
}
static int otx2vf_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{
int num_vec = pci_msix_vec_count(pdev);
struct device *dev = &pdev->dev;
int err, qcount, qos_txqs;
struct net_device *netdev;
struct otx2_nic *vf;
struct otx2_hw *hw;
err = pcim_enable_device(pdev);
if (err) {
dev_err(dev, "Failed to enable PCI device\n");
return err;
}
err = pcim_request_all_regions(pdev, DRV_NAME);
if (err) {
dev_err(dev, "PCI request regions failed 0x%x\n", err);
return err;
}
err = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(48));
if (err) {
dev_err(dev, "DMA mask config failed, abort\n");
return err;
}
pci_set_master(pdev);
qcount = num_online_cpus();
qos_txqs = min_t(int, qcount, OTX2_QOS_MAX_LEAF_NODES);
netdev = alloc_etherdev_mqs(sizeof(*vf), qcount + qos_txqs, qcount);
if (!netdev)
return -ENOMEM;
pci_set_drvdata(pdev, netdev);
SET_NETDEV_DEV(netdev, &pdev->dev);
vf = netdev_priv(netdev);
vf->netdev = netdev;
vf->pdev = pdev;
vf->dev = dev;
vf->iommu_domain = iommu_get_domain_for_dev(dev);
vf->flags |= OTX2_FLAG_INTF_DOWN;
hw = &vf->hw;
hw->pdev = vf->pdev;
hw->rx_queues = qcount;
hw->tx_queues = qcount;
hw->max_queues = qcount;
hw->non_qos_queues = qcount;
hw->rbuf_len = OTX2_DEFAULT_RBUF_LEN;
/* Use CQE of 128 byte descriptor size by default */
hw->xqe_size = 128;
hw->irq_name = devm_kmalloc_array(&hw->pdev->dev, num_vec, NAME_SIZE,
GFP_KERNEL);
if (!hw->irq_name) {
err = -ENOMEM;
goto err_free_netdev;
}
hw->affinity_mask = devm_kcalloc(&hw->pdev->dev, num_vec,
sizeof(cpumask_var_t), GFP_KERNEL);
if (!hw->affinity_mask) {
err = -ENOMEM;
goto err_free_netdev;
}
err = pci_alloc_irq_vectors(hw->pdev, num_vec, num_vec, PCI_IRQ_MSIX);
if (err < 0) {
dev_err(dev, "%s: Failed to alloc %d IRQ vectors\n",
__func__, num_vec);
goto err_free_netdev;
}
vf->reg_base = pcim_iomap(pdev, PCI_CFG_REG_BAR_NUM, 0);
if (!vf->reg_base) {
dev_err(dev, "Unable to map physical function CSRs, aborting\n");
err = -ENOMEM;
goto err_free_irq_vectors;
}
otx2_setup_dev_hw_settings(vf);
if (is_cn20k(vf->pdev))
cn20k_init(vf);
else
otx2_init_hw_ops(vf);
/* Init VF <=> PF mailbox stuff */
err = otx2vf_vfaf_mbox_init(vf);
if (err)
goto err_free_irq_vectors;
/* Register mailbox interrupt */
err = otx2vf_register_mbox_intr(vf, true);
if (err)
goto err_mbox_destroy;
/* Request AF to attach NPA and LIX LFs to this AF */
err = otx2_attach_npa_nix(vf);
if (err)
goto err_disable_mbox_intr;
err = otx2vf_realloc_msix_vectors(vf);
if (err)
goto err_detach_rsrc;
err = otx2_set_real_num_queues(netdev, qcount, qcount);
if (err)
goto err_detach_rsrc;
err = cn10k_lmtst_init(vf);
if (err)
goto err_detach_rsrc;
/* Don't check for error. Proceed without ptp */
otx2_ptp_init(vf);
/* Assign default mac address */
otx2_get_mac_from_af(netdev);
netdev->hw_features = NETIF_F_RXCSUM | NETIF_F_IP_CSUM |
NETIF_F_IPV6_CSUM | NETIF_F_RXHASH |
NETIF_F_SG | NETIF_F_TSO | NETIF_F_TSO6 |
NETIF_F_GSO_UDP_L4;
netdev->features = netdev->hw_features;
/* Support TSO on tag interface */
netdev->vlan_features |= netdev->features;
netdev->hw_features |= NETIF_F_HW_VLAN_CTAG_TX |
NETIF_F_HW_VLAN_STAG_TX;
netdev->features |= netdev->hw_features;
netdev->hw_features |= NETIF_F_NTUPLE;
netdev->hw_features |= NETIF_F_RXALL;
netdev->hw_features |= NETIF_F_HW_TC;
netif_set_tso_max_segs(netdev, OTX2_MAX_GSO_SEGS);
netdev->watchdog_timeo = OTX2_TX_TIMEOUT;
netdev->netdev_ops = &otx2vf_netdev_ops;
netdev->min_mtu = OTX2_MIN_MTU;
netdev->max_mtu = otx2_get_max_mtu(vf);
hw->max_mtu = netdev->max_mtu;
/* To distinguish, for LBK VFs set netdev name explicitly */
if (is_otx2_lbkvf(vf->pdev)) {
int n;
n = (vf->pcifunc >> RVU_PFVF_FUNC_SHIFT) & RVU_PFVF_FUNC_MASK;
/* Need to subtract 1 to get proper VF number */
n -= 1;
snprintf(netdev->name, sizeof(netdev->name), "lbk%d", n);
}
if (is_otx2_sdp_rep(vf->pdev)) {
int n;
n = vf->pcifunc & RVU_PFVF_FUNC_MASK;
n -= 1;
snprintf(netdev->name, sizeof(netdev->name), "sdp%d-%d",
pdev->bus->number, n);
}
err = cn10k_ipsec_init(netdev);
if (err)
goto err_ptp_destroy;
err = register_netdev(netdev);
if (err) {
dev_err(dev, "Failed to register netdevice\n");
goto err_ipsec_clean;
}
err = otx2_vf_wq_init(vf);
if (err)
goto err_unreg_netdev;
otx2vf_set_ethtool_ops(netdev);
err = otx2vf_mcam_flow_init(vf);
if (err)
goto err_unreg_netdev;
err = otx2_init_tc(vf);
if (err)
goto err_unreg_netdev;
err = otx2_register_dl(vf);
if (err)
goto err_shutdown_tc;
vf->af_xdp_zc_qidx = bitmap_zalloc(qcount, GFP_KERNEL);
if (!vf->af_xdp_zc_qidx) {
err = -ENOMEM;
goto err_unreg_devlink;
}
#ifdef CONFIG_DCB
/* Priority flow control is not supported for LBK and SDP vf(s) */
if (!(is_otx2_lbkvf(vf->pdev) || is_otx2_sdp_rep(vf->pdev))) {
err = otx2_dcbnl_set_ops(netdev);
if (err)
goto err_free_zc_bmap;
}
#endif
otx2_qos_init(vf, qos_txqs);
return 0;
#ifdef CONFIG_DCB
err_free_zc_bmap:
bitmap_free(vf->af_xdp_zc_qidx);
#endif
err_unreg_devlink:
otx2_unregister_dl(vf);
err_shutdown_tc:
otx2_shutdown_tc(vf);
err_unreg_netdev:
unregister_netdev(netdev);
err_ipsec_clean:
cn10k_ipsec_clean(vf);
err_ptp_destroy:
otx2_ptp_destroy(vf);
err_detach_rsrc:
free_percpu(vf->hw.lmt_info);
if (test_bit(CN10K_LMTST, &vf->hw.cap_flag))
qmem_free(vf->dev, vf->dync_lmt);
otx2_detach_resources(&vf->mbox);
err_disable_mbox_intr:
otx2vf_disable_mbox_intr(vf);
err_mbox_destroy:
otx2vf_vfaf_mbox_destroy(vf);
err_free_irq_vectors:
pci_free_irq_vectors(hw->pdev);
err_free_netdev:
pci_set_drvdata(pdev, NULL);
free_netdev(netdev);
return err;
}
static void otx2vf_remove(struct pci_dev *pdev)
{
struct net_device *netdev = pci_get_drvdata(pdev);
struct otx2_nic *vf;
if (!netdev)
return;
vf = netdev_priv(netdev);
/* Disable 802.3x pause frames */
if (vf->flags & OTX2_FLAG_RX_PAUSE_ENABLED ||
(vf->flags & OTX2_FLAG_TX_PAUSE_ENABLED)) {
vf->flags &= ~OTX2_FLAG_RX_PAUSE_ENABLED;
vf->flags &= ~OTX2_FLAG_TX_PAUSE_ENABLED;
otx2_config_pause_frm(vf);
}
#ifdef CONFIG_DCB
/* Disable PFC config */
if (vf->pfc_en) {
vf->pfc_en = 0;
otx2_config_priority_flow_ctrl(vf);
}
#endif
cancel_work_sync(&vf->reset_task);
otx2_unregister_dl(vf);
unregister_netdev(netdev);
if (vf->otx2_wq)
destroy_workqueue(vf->otx2_wq);
cn10k_ipsec_clean(vf);
otx2_ptp_destroy(vf);
otx2_mcam_flow_del(vf);
otx2_shutdown_tc(vf);
otx2_shutdown_qos(vf);
otx2_detach_resources(&vf->mbox);
otx2vf_disable_mbox_intr(vf);
free_percpu(vf->hw.lmt_info);
if (test_bit(CN10K_LMTST, &vf->hw.cap_flag))
qmem_free(vf->dev, vf->dync_lmt);
otx2vf_vfaf_mbox_destroy(vf);
pci_free_irq_vectors(vf->pdev);
bitmap_free(vf->af_xdp_zc_qidx);
pci_set_drvdata(pdev, NULL);
free_netdev(netdev);
}
static struct pci_driver otx2vf_driver = {
.name = DRV_NAME,
.id_table = otx2_vf_id_table,
.probe = otx2vf_probe,
.remove = otx2vf_remove,
.shutdown = otx2vf_remove,
};
static int __init otx2vf_init_module(void)
{
pr_info("%s: %s\n", DRV_NAME, DRV_STRING);
return pci_register_driver(&otx2vf_driver);
}
static void __exit otx2vf_cleanup_module(void)
{
pci_unregister_driver(&otx2vf_driver);
}
module_init(otx2vf_init_module);
module_exit(otx2vf_cleanup_module);
@@ -0,0 +1,245 @@
// SPDX-License-Identifier: GPL-2.0
/* Marvell RVU Ethernet driver
*
* Copyright (C) 2024 Marvell.
*
*/
#include <linux/bpf_trace.h>
#include <linux/stringify.h>
#include <net/xdp_sock_drv.h>
#include <net/xdp.h>
#include "otx2_common.h"
#include "otx2_struct.h"
#include "otx2_xsk.h"
int otx2_xsk_pool_alloc_buf(struct otx2_nic *pfvf, struct otx2_pool *pool,
dma_addr_t *dma, int idx)
{
struct xdp_buff *xdp;
int delta;
xdp = xsk_buff_alloc(pool->xsk_pool);
if (!xdp)
return -ENOMEM;
pool->xdp[pool->xdp_top++] = xdp;
*dma = OTX2_DATA_ALIGN(xsk_buff_xdp_get_dma(xdp));
/* Adjust xdp->data for unaligned addresses */
delta = *dma - xsk_buff_xdp_get_dma(xdp);
xdp->data += delta;
return 0;
}
static int otx2_xsk_ctx_disable(struct otx2_nic *pfvf, u16 qidx, int aura_id)
{
struct nix_cn10k_aq_enq_req *cn10k_rq_aq;
struct npa_aq_enq_req *aura_aq;
struct npa_aq_enq_req *pool_aq;
struct nix_aq_enq_req *rq_aq;
if (test_bit(CN10K_LMTST, &pfvf->hw.cap_flag)) {
cn10k_rq_aq = otx2_mbox_alloc_msg_nix_cn10k_aq_enq(&pfvf->mbox);
if (!cn10k_rq_aq)
return -ENOMEM;
cn10k_rq_aq->qidx = qidx;
cn10k_rq_aq->rq.ena = 0;
cn10k_rq_aq->rq_mask.ena = 1;
cn10k_rq_aq->ctype = NIX_AQ_CTYPE_RQ;
cn10k_rq_aq->op = NIX_AQ_INSTOP_WRITE;
} else {
rq_aq = otx2_mbox_alloc_msg_nix_aq_enq(&pfvf->mbox);
if (!rq_aq)
return -ENOMEM;
rq_aq->qidx = qidx;
rq_aq->sq.ena = 0;
rq_aq->sq_mask.ena = 1;
rq_aq->ctype = NIX_AQ_CTYPE_RQ;
rq_aq->op = NIX_AQ_INSTOP_WRITE;
}
aura_aq = otx2_mbox_alloc_msg_npa_aq_enq(&pfvf->mbox);
if (!aura_aq)
goto fail;
aura_aq->aura_id = aura_id;
aura_aq->aura.ena = 0;
aura_aq->aura_mask.ena = 1;
aura_aq->ctype = NPA_AQ_CTYPE_AURA;
aura_aq->op = NPA_AQ_INSTOP_WRITE;
pool_aq = otx2_mbox_alloc_msg_npa_aq_enq(&pfvf->mbox);
if (!pool_aq)
goto fail;
pool_aq->aura_id = aura_id;
pool_aq->pool.ena = 0;
pool_aq->pool_mask.ena = 1;
pool_aq->ctype = NPA_AQ_CTYPE_POOL;
pool_aq->op = NPA_AQ_INSTOP_WRITE;
return otx2_sync_mbox_msg(&pfvf->mbox);
fail:
otx2_mbox_reset(&pfvf->mbox.mbox, 0);
return -ENOMEM;
}
static void otx2_clean_up_rq(struct otx2_nic *pfvf, int qidx)
{
struct otx2_qset *qset = &pfvf->qset;
struct otx2_cq_queue *cq;
struct otx2_pool *pool;
u64 iova;
/* If the DOWN flag is set SQs are already freed */
if (pfvf->flags & OTX2_FLAG_INTF_DOWN)
return;
cq = &qset->cq[qidx];
if (cq)
otx2_cleanup_rx_cqes(pfvf, cq, qidx);
pool = &pfvf->qset.pool[qidx];
iova = otx2_aura_allocptr(pfvf, qidx);
while (iova) {
iova -= OTX2_HEAD_ROOM;
otx2_free_bufs(pfvf, pool, iova, pfvf->rbsize);
iova = otx2_aura_allocptr(pfvf, qidx);
}
mutex_lock(&pfvf->mbox.lock);
otx2_xsk_ctx_disable(pfvf, qidx, qidx);
mutex_unlock(&pfvf->mbox.lock);
}
int otx2_xsk_pool_enable(struct otx2_nic *pf, struct xsk_buff_pool *pool, u16 qidx)
{
u16 rx_queues = pf->hw.rx_queues;
u16 tx_queues = pf->hw.tx_queues;
int err;
if (qidx >= rx_queues || qidx >= tx_queues)
return -EINVAL;
err = xsk_pool_dma_map(pool, pf->dev, DMA_ATTR_SKIP_CPU_SYNC | DMA_ATTR_WEAK_ORDERING);
if (err)
return err;
set_bit(qidx, pf->af_xdp_zc_qidx);
otx2_clean_up_rq(pf, qidx);
/* Reconfigure RSS table as 'qidx' cannot be part of RSS now */
otx2_set_rss_table(pf, DEFAULT_RSS_CONTEXT_GROUP, NULL);
/* Kick start the NAPI context so that receiving will start */
return otx2_xsk_wakeup(pf->netdev, qidx, XDP_WAKEUP_RX);
}
int otx2_xsk_pool_disable(struct otx2_nic *pf, u16 qidx)
{
struct net_device *netdev = pf->netdev;
struct xsk_buff_pool *pool;
struct otx2_snd_queue *sq;
pool = xsk_get_pool_from_qid(netdev, qidx);
if (!pool)
return -EINVAL;
sq = &pf->qset.sq[qidx + pf->hw.tx_queues];
sq->xsk_pool = NULL;
otx2_clean_up_rq(pf, qidx);
clear_bit(qidx, pf->af_xdp_zc_qidx);
xsk_pool_dma_unmap(pool, DMA_ATTR_SKIP_CPU_SYNC | DMA_ATTR_WEAK_ORDERING);
/* Reconfigure RSS table as 'qidx' now need to be part of RSS now */
otx2_set_rss_table(pf, DEFAULT_RSS_CONTEXT_GROUP, NULL);
return 0;
}
int otx2_xsk_pool_setup(struct otx2_nic *pf, struct xsk_buff_pool *pool, u16 qidx)
{
if (pool)
return otx2_xsk_pool_enable(pf, pool, qidx);
return otx2_xsk_pool_disable(pf, qidx);
}
int otx2_xsk_wakeup(struct net_device *dev, u32 queue_id, u32 flags)
{
struct otx2_nic *pf = netdev_priv(dev);
struct otx2_cq_poll *cq_poll = NULL;
struct otx2_qset *qset = &pf->qset;
if (pf->flags & OTX2_FLAG_INTF_DOWN)
return -ENETDOWN;
if (queue_id >= pf->hw.rx_queues || queue_id >= pf->hw.tx_queues)
return -EINVAL;
cq_poll = &qset->napi[queue_id];
if (!cq_poll)
return -EINVAL;
/* Trigger interrupt */
if (!napi_if_scheduled_mark_missed(&cq_poll->napi)) {
otx2_write64(pf, NIX_LF_CINTX_ENA_W1S(cq_poll->cint_idx), BIT_ULL(0));
otx2_write64(pf, NIX_LF_CINTX_INT_W1S(cq_poll->cint_idx), BIT_ULL(0));
}
return 0;
}
void otx2_attach_xsk_buff(struct otx2_nic *pfvf, struct otx2_snd_queue *sq, int qidx)
{
if (test_bit(qidx, pfvf->af_xdp_zc_qidx))
sq->xsk_pool = xsk_get_pool_from_qid(pfvf->netdev, qidx);
}
static void otx2_xsk_sq_append_pkt(struct otx2_nic *pfvf, u64 iova, int len,
u16 qidx)
{
struct nix_sqe_hdr_s *sqe_hdr;
struct otx2_snd_queue *sq;
int offset;
sq = &pfvf->qset.sq[qidx];
memset(sq->sqe_base + 8, 0, sq->sqe_size - 8);
sqe_hdr = (struct nix_sqe_hdr_s *)(sq->sqe_base);
if (!sqe_hdr->total) {
sqe_hdr->aura = sq->aura_id;
sqe_hdr->df = 1;
sqe_hdr->sq = qidx;
sqe_hdr->pnc = 1;
}
sqe_hdr->total = len;
sqe_hdr->sqe_id = sq->head;
offset = sizeof(*sqe_hdr);
otx2_xdp_sqe_add_sg(sq, NULL, iova, len, &offset, OTX2_AF_XDP_FRAME);
sqe_hdr->sizem1 = (offset / 16) - 1;
pfvf->hw_ops->sqe_flush(pfvf, sq, offset, qidx);
}
void otx2_zc_napi_handler(struct otx2_nic *pfvf, struct xsk_buff_pool *pool,
int queue, int budget)
{
struct xdp_desc *xdp_desc = pool->tx_descs;
int i, batch;
budget = min(budget, otx2_read_free_sqe(pfvf, queue));
batch = xsk_tx_peek_release_desc_batch(pool, budget);
if (!batch)
return;
for (i = 0; i < batch; i++) {
dma_addr_t dma_addr;
dma_addr = xsk_buff_raw_get_dma(pool, xdp_desc[i].addr);
otx2_xsk_sq_append_pkt(pfvf, dma_addr, xdp_desc[i].len, queue);
}
}
@@ -0,0 +1,24 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Marvell RVU PF/VF Netdev Devlink
*
* Copyright (C) 2024 Marvell.
*
*/
#ifndef OTX2_XSK_H
#define OTX2_XSK_H
struct otx2_nic;
struct xsk_buff_pool;
int otx2_xsk_pool_setup(struct otx2_nic *pf, struct xsk_buff_pool *pool, u16 qid);
int otx2_xsk_pool_enable(struct otx2_nic *pf, struct xsk_buff_pool *pool, u16 qid);
int otx2_xsk_pool_disable(struct otx2_nic *pf, u16 qid);
int otx2_xsk_pool_alloc_buf(struct otx2_nic *pfvf, struct otx2_pool *pool,
dma_addr_t *dma, int idx);
int otx2_xsk_wakeup(struct net_device *dev, u32 queue_id, u32 flags);
void otx2_zc_napi_handler(struct otx2_nic *pfvf, struct xsk_buff_pool *pool,
int queue, int budget);
void otx2_attach_xsk_buff(struct otx2_nic *pfvf, struct otx2_snd_queue *sq, int qidx);
#endif /* OTX2_XSK_H */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,78 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Marvell RVU Ethernet driver
*
* Copyright (C) 2023 Marvell.
*
*/
#ifndef OTX2_QOS_H
#define OTX2_QOS_H
#include <linux/types.h>
#include <linux/netdevice.h>
#include <linux/rhashtable.h>
#define OTX2_QOS_MAX_LVL 4
#define OTX2_QOS_MAX_PRIO 7
#define OTX2_QOS_MAX_LEAF_NODES 16
enum qos_smq_operations {
QOS_CFG_SQ,
QOS_SMQ_FLUSH,
};
u64 otx2_get_txschq_rate_regval(struct otx2_nic *nic, u64 maxrate, u32 burst);
int otx2_setup_tc_htb(struct net_device *ndev, struct tc_htb_qopt_offload *htb);
int otx2_qos_get_qid(struct otx2_nic *pfvf);
void otx2_qos_free_qid(struct otx2_nic *pfvf, int qidx);
int otx2_qos_enable_sq(struct otx2_nic *pfvf, int qidx);
void otx2_qos_disable_sq(struct otx2_nic *pfvf, int qidx);
struct otx2_qos_cfg {
u16 schq[NIX_TXSCH_LVL_CNT];
u16 schq_contig[NIX_TXSCH_LVL_CNT];
int static_node_pos[NIX_TXSCH_LVL_CNT];
int dwrr_node_pos[NIX_TXSCH_LVL_CNT];
u16 schq_contig_list[NIX_TXSCH_LVL_CNT][MAX_TXSCHQ_PER_FUNC];
u16 schq_list[NIX_TXSCH_LVL_CNT][MAX_TXSCHQ_PER_FUNC];
bool schq_index_used[NIX_TXSCH_LVL_CNT][MAX_TXSCHQ_PER_FUNC];
};
struct otx2_qos {
DECLARE_HASHTABLE(qos_hlist, order_base_2(OTX2_QOS_MAX_LEAF_NODES));
struct mutex qos_lock; /* child list lock */
u16 qid_to_sqmap[OTX2_QOS_MAX_LEAF_NODES];
struct list_head qos_tree;
DECLARE_BITMAP(qos_sq_bmap, OTX2_QOS_MAX_LEAF_NODES);
u16 maj_id;
u16 defcls;
u8 link_cfg_lvl; /* LINKX_CFG CSRs mapped to TL3 or TL2's index ? */
};
struct otx2_qos_node {
struct list_head list; /* list management */
struct list_head child_list;
struct list_head child_schq_list;
struct hlist_node hlist;
DECLARE_BITMAP(prio_bmap, OTX2_QOS_MAX_PRIO + 1);
struct otx2_qos_node *parent; /* parent qos node */
u64 rate; /* htb params */
u64 ceil;
u32 classid;
u32 prio;
u32 quantum;
/* hw txschq */
u16 schq;
u16 qid;
u16 prio_anchor;
u16 max_static_prio;
u16 child_dwrr_cnt;
u16 child_static_cnt;
u16 child_dwrr_prio;
u16 txschq_idx; /* txschq allocation index */
u8 level;
bool is_static;
};
#endif
@@ -0,0 +1,319 @@
// SPDX-License-Identifier: GPL-2.0
/* Marvell RVU Physical Function ethernet driver
*
* Copyright (C) 2023 Marvell.
*
*/
#include <linux/netdevice.h>
#include <net/tso.h>
#include "cn10k.h"
#include "otx2_reg.h"
#include "otx2_common.h"
#include "otx2_txrx.h"
#include "otx2_struct.h"
#define OTX2_QOS_MAX_LEAF_NODES 16
static void otx2_qos_aura_pool_free(struct otx2_nic *pfvf, int pool_id)
{
struct otx2_pool *pool;
if (!pfvf->qset.pool)
return;
pool = &pfvf->qset.pool[pool_id];
qmem_free(pfvf->dev, pool->stack);
qmem_free(pfvf->dev, pool->fc_addr);
pool->stack = NULL;
pool->fc_addr = NULL;
}
static int otx2_qos_sq_aura_pool_init(struct otx2_nic *pfvf, int qidx)
{
struct otx2_qset *qset = &pfvf->qset;
int pool_id, stack_pages, num_sqbs;
struct otx2_hw *hw = &pfvf->hw;
struct otx2_snd_queue *sq;
struct otx2_pool *pool;
dma_addr_t bufptr;
int err, ptr;
u64 iova, pa;
/* Calculate number of SQBs needed.
*
* For a 128byte SQE, and 4K size SQB, 31 SQEs will fit in one SQB.
* Last SQE is used for pointing to next SQB.
*/
num_sqbs = (hw->sqb_size / 128) - 1;
num_sqbs = (qset->sqe_cnt + num_sqbs) / num_sqbs;
/* Get no of stack pages needed */
stack_pages =
(num_sqbs + hw->stack_pg_ptrs - 1) / hw->stack_pg_ptrs;
pool_id = otx2_get_pool_idx(pfvf, AURA_NIX_SQ, qidx);
pool = &pfvf->qset.pool[pool_id];
/* Initialize aura context */
err = otx2_aura_init(pfvf, pool_id, pool_id, num_sqbs);
if (err)
return err;
/* Initialize pool context */
err = otx2_pool_init(pfvf, pool_id, stack_pages,
num_sqbs, hw->sqb_size, AURA_NIX_SQ);
if (err)
goto aura_free;
/* Flush accumulated messages */
err = otx2_sync_mbox_msg(&pfvf->mbox);
if (err)
goto pool_free;
/* Allocate pointers and free them to aura/pool */
sq = &qset->sq[qidx];
sq->sqb_count = 0;
sq->sqb_ptrs = kcalloc(num_sqbs, sizeof(*sq->sqb_ptrs), GFP_KERNEL);
if (!sq->sqb_ptrs) {
err = -ENOMEM;
goto pool_free;
}
for (ptr = 0; ptr < num_sqbs; ptr++) {
err = otx2_alloc_rbuf(pfvf, pool, &bufptr, pool_id, ptr);
if (err)
goto sqb_free;
pfvf->hw_ops->aura_freeptr(pfvf, pool_id, bufptr);
sq->sqb_ptrs[sq->sqb_count++] = (u64)bufptr;
}
return 0;
sqb_free:
while (ptr--) {
if (!sq->sqb_ptrs[ptr])
continue;
iova = sq->sqb_ptrs[ptr];
pa = otx2_iova_to_phys(pfvf->iommu_domain, iova);
dma_unmap_page_attrs(pfvf->dev, iova, hw->sqb_size,
DMA_FROM_DEVICE,
DMA_ATTR_SKIP_CPU_SYNC);
put_page(virt_to_page(phys_to_virt(pa)));
otx2_aura_allocptr(pfvf, pool_id);
}
sq->sqb_count = 0;
kfree(sq->sqb_ptrs);
pool_free:
qmem_free(pfvf->dev, pool->stack);
aura_free:
qmem_free(pfvf->dev, pool->fc_addr);
otx2_mbox_reset(&pfvf->mbox.mbox, 0);
return err;
}
static void otx2_qos_sq_free_sqbs(struct otx2_nic *pfvf, int qidx)
{
struct otx2_qset *qset = &pfvf->qset;
struct otx2_hw *hw = &pfvf->hw;
struct otx2_snd_queue *sq;
u64 iova, pa;
int sqb;
sq = &qset->sq[qidx];
if (!sq->sqb_ptrs)
return;
for (sqb = 0; sqb < sq->sqb_count; sqb++) {
if (!sq->sqb_ptrs[sqb])
continue;
iova = sq->sqb_ptrs[sqb];
pa = otx2_iova_to_phys(pfvf->iommu_domain, iova);
dma_unmap_page_attrs(pfvf->dev, iova, hw->sqb_size,
DMA_FROM_DEVICE,
DMA_ATTR_SKIP_CPU_SYNC);
put_page(virt_to_page(phys_to_virt(pa)));
}
sq->sqb_count = 0;
sq = &qset->sq[qidx];
qmem_free(pfvf->dev, sq->sqe);
qmem_free(pfvf->dev, sq->tso_hdrs);
kfree(sq->sg);
kfree(sq->sqb_ptrs);
qmem_free(pfvf->dev, sq->timestamps);
memset((void *)sq, 0, sizeof(*sq));
}
/* send queue id */
static void otx2_qos_sqb_flush(struct otx2_nic *pfvf, int qidx)
{
int sqe_tail, sqe_head;
void __iomem *ptr;
u64 incr, val;
ptr = otx2_get_regaddr(pfvf, NIX_LF_SQ_OP_STATUS);
incr = (u64)qidx << 32;
val = otx2_atomic64_add(incr, ptr);
sqe_head = (val >> 20) & 0x3F;
sqe_tail = (val >> 28) & 0x3F;
if (sqe_head != sqe_tail)
usleep_range(50, 60);
}
static int otx2_qos_ctx_disable(struct otx2_nic *pfvf, u16 qidx, int aura_id)
{
struct nix_cn10k_aq_enq_req *cn10k_sq_aq;
struct npa_aq_enq_req *aura_aq;
struct npa_aq_enq_req *pool_aq;
struct nix_aq_enq_req *sq_aq;
if (test_bit(CN10K_LMTST, &pfvf->hw.cap_flag)) {
cn10k_sq_aq = otx2_mbox_alloc_msg_nix_cn10k_aq_enq(&pfvf->mbox);
if (!cn10k_sq_aq)
return -ENOMEM;
cn10k_sq_aq->qidx = qidx;
cn10k_sq_aq->sq.ena = 0;
cn10k_sq_aq->sq_mask.ena = 1;
cn10k_sq_aq->ctype = NIX_AQ_CTYPE_SQ;
cn10k_sq_aq->op = NIX_AQ_INSTOP_WRITE;
} else {
sq_aq = otx2_mbox_alloc_msg_nix_aq_enq(&pfvf->mbox);
if (!sq_aq)
return -ENOMEM;
sq_aq->qidx = qidx;
sq_aq->sq.ena = 0;
sq_aq->sq_mask.ena = 1;
sq_aq->ctype = NIX_AQ_CTYPE_SQ;
sq_aq->op = NIX_AQ_INSTOP_WRITE;
}
aura_aq = otx2_mbox_alloc_msg_npa_aq_enq(&pfvf->mbox);
if (!aura_aq) {
otx2_mbox_reset(&pfvf->mbox.mbox, 0);
return -ENOMEM;
}
aura_aq->aura_id = aura_id;
aura_aq->aura.ena = 0;
aura_aq->aura_mask.ena = 1;
aura_aq->ctype = NPA_AQ_CTYPE_AURA;
aura_aq->op = NPA_AQ_INSTOP_WRITE;
pool_aq = otx2_mbox_alloc_msg_npa_aq_enq(&pfvf->mbox);
if (!pool_aq) {
otx2_mbox_reset(&pfvf->mbox.mbox, 0);
return -ENOMEM;
}
pool_aq->aura_id = aura_id;
pool_aq->pool.ena = 0;
pool_aq->pool_mask.ena = 1;
pool_aq->ctype = NPA_AQ_CTYPE_POOL;
pool_aq->op = NPA_AQ_INSTOP_WRITE;
return otx2_sync_mbox_msg(&pfvf->mbox);
}
int otx2_qos_get_qid(struct otx2_nic *pfvf)
{
int qidx;
qidx = find_first_zero_bit(pfvf->qos.qos_sq_bmap,
pfvf->hw.tc_tx_queues);
return qidx == pfvf->hw.tc_tx_queues ? -ENOSPC : qidx;
}
void otx2_qos_free_qid(struct otx2_nic *pfvf, int qidx)
{
clear_bit(qidx, pfvf->qos.qos_sq_bmap);
}
int otx2_qos_enable_sq(struct otx2_nic *pfvf, int qidx)
{
struct otx2_hw *hw = &pfvf->hw;
int pool_id, sq_idx, err;
if (pfvf->flags & OTX2_FLAG_INTF_DOWN)
return -EPERM;
sq_idx = hw->non_qos_queues + qidx;
mutex_lock(&pfvf->mbox.lock);
err = otx2_qos_sq_aura_pool_init(pfvf, sq_idx);
if (err)
goto out;
pool_id = otx2_get_pool_idx(pfvf, AURA_NIX_SQ, sq_idx);
err = otx2_sq_init(pfvf, sq_idx, pool_id);
if (err)
goto out;
out:
mutex_unlock(&pfvf->mbox.lock);
return err;
}
static int otx2_qos_nix_npa_ndc_sync(struct otx2_nic *pfvf)
{
struct ndc_sync_op *req;
int rc;
mutex_lock(&pfvf->mbox.lock);
req = otx2_mbox_alloc_msg_ndc_sync_op(&pfvf->mbox);
if (!req) {
mutex_unlock(&pfvf->mbox.lock);
return -ENOMEM;
}
req->nix_lf_tx_sync = true;
req->npa_lf_sync = true;
rc = otx2_sync_mbox_msg(&pfvf->mbox);
mutex_unlock(&pfvf->mbox.lock);
return rc;
}
void otx2_qos_disable_sq(struct otx2_nic *pfvf, int qidx)
{
struct otx2_qset *qset = &pfvf->qset;
struct otx2_hw *hw = &pfvf->hw;
struct otx2_snd_queue *sq;
struct otx2_cq_queue *cq;
int pool_id, sq_idx;
sq_idx = hw->non_qos_queues + qidx;
/* If the DOWN flag is set SQs are already freed */
if (pfvf->flags & OTX2_FLAG_INTF_DOWN)
return;
sq = &pfvf->qset.sq[sq_idx];
if (!sq->sqb_ptrs)
return;
if (sq_idx < hw->non_qos_queues ||
sq_idx >= otx2_get_total_tx_queues(pfvf)) {
netdev_err(pfvf->netdev, "Send Queue is not a QoS queue\n");
return;
}
cq = &qset->cq[pfvf->hw.rx_queues + sq_idx];
pool_id = otx2_get_pool_idx(pfvf, AURA_NIX_SQ, sq_idx);
otx2_qos_sqb_flush(pfvf, sq_idx);
otx2_smq_flush(pfvf, otx2_get_smq_idx(pfvf, sq_idx));
/* NIX/NPA NDC sync */
otx2_qos_nix_npa_ndc_sync(pfvf);
otx2_cleanup_tx_cqes(pfvf, cq);
mutex_lock(&pfvf->mbox.lock);
otx2_qos_ctx_disable(pfvf, sq_idx, pool_id);
mutex_unlock(&pfvf->mbox.lock);
otx2_qos_sq_free_sqbs(pfvf, sq_idx);
otx2_qos_aura_pool_free(pfvf, pool_id);
}
@@ -0,0 +1,880 @@
// SPDX-License-Identifier: GPL-2.0
/* Marvell RVU representor driver
*
* Copyright (C) 2024 Marvell.
*
*/
#include <linux/etherdevice.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/net_tstamp.h>
#include <linux/sort.h>
#include "otx2_common.h"
#include "cn10k.h"
#include "otx2_reg.h"
#include "rep.h"
#define DRV_NAME "rvu_rep"
#define DRV_STRING "Marvell RVU Representor Driver"
static const struct pci_device_id rvu_rep_id_table[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, PCI_DEVID_RVU_REP) },
{ }
};
MODULE_AUTHOR("Marvell International Ltd.");
MODULE_DESCRIPTION(DRV_STRING);
MODULE_LICENSE("GPL");
MODULE_DEVICE_TABLE(pci, rvu_rep_id_table);
static int rvu_rep_notify_pfvf(struct otx2_nic *priv, u16 event,
struct rep_event *data);
static int rvu_rep_mcam_flow_init(struct rep_dev *rep)
{
struct npc_mcam_alloc_entry_req *req;
struct npc_mcam_alloc_entry_rsp *rsp;
struct otx2_nic *priv = rep->mdev;
int ent, allocated = 0;
int count;
rep->flow_cfg = kzalloc_objs(struct otx2_flow_config, 1);
if (!rep->flow_cfg)
return -ENOMEM;
count = OTX2_DEFAULT_FLOWCOUNT;
rep->flow_cfg->flow_ent = kcalloc(count, sizeof(u16), GFP_KERNEL);
if (!rep->flow_cfg->flow_ent)
return -ENOMEM;
while (allocated < count) {
req = otx2_mbox_alloc_msg_npc_mcam_alloc_entry(&priv->mbox);
if (!req)
goto exit;
req->hdr.pcifunc = rep->pcifunc;
req->contig = false;
req->ref_entry = 0;
req->count = (count - allocated) > NPC_MAX_NONCONTIG_ENTRIES ?
NPC_MAX_NONCONTIG_ENTRIES : count - allocated;
if (otx2_sync_mbox_msg(&priv->mbox))
goto exit;
rsp = (struct npc_mcam_alloc_entry_rsp *)otx2_mbox_get_rsp
(&priv->mbox.mbox, 0, &req->hdr);
if (IS_ERR(rsp))
goto exit;
for (ent = 0; ent < rsp->count; ent++)
rep->flow_cfg->flow_ent[ent + allocated] = rsp->entry_list[ent];
allocated += rsp->count;
if (rsp->count != req->count)
break;
}
exit:
/* Multiple MCAM entry alloc requests could result in non-sequential
* MCAM entries in the flow_ent[] array. Sort them in an ascending
* order, otherwise user installed ntuple filter index and MCAM entry
* index will not be in sync.
*/
if (allocated)
sort(&rep->flow_cfg->flow_ent[0], allocated,
sizeof(rep->flow_cfg->flow_ent[0]), mcam_entry_cmp, NULL);
mutex_unlock(&priv->mbox.lock);
rep->flow_cfg->max_flows = allocated;
if (allocated) {
rep->flags |= OTX2_FLAG_MCAM_ENTRIES_ALLOC;
rep->flags |= OTX2_FLAG_NTUPLE_SUPPORT;
rep->flags |= OTX2_FLAG_TC_FLOWER_SUPPORT;
}
INIT_LIST_HEAD(&rep->flow_cfg->flow_list);
INIT_LIST_HEAD(&rep->flow_cfg->flow_list_tc);
return 0;
}
static int rvu_rep_setup_tc_cb(enum tc_setup_type type,
void *type_data, void *cb_priv)
{
struct rep_dev *rep = cb_priv;
struct otx2_nic *priv = rep->mdev;
if (!(rep->flags & RVU_REP_VF_INITIALIZED))
return -EINVAL;
if (!(rep->flags & OTX2_FLAG_TC_FLOWER_SUPPORT))
rvu_rep_mcam_flow_init(rep);
priv->netdev = rep->netdev;
priv->flags = rep->flags;
priv->pcifunc = rep->pcifunc;
priv->flow_cfg = rep->flow_cfg;
switch (type) {
case TC_SETUP_CLSFLOWER:
return otx2_setup_tc_cls_flower(priv, type_data);
default:
return -EOPNOTSUPP;
}
}
static LIST_HEAD(rvu_rep_block_cb_list);
static int rvu_rep_setup_tc(struct net_device *netdev, enum tc_setup_type type,
void *type_data)
{
struct rvu_rep *rep = netdev_priv(netdev);
switch (type) {
case TC_SETUP_BLOCK:
return flow_block_cb_setup_simple(type_data,
&rvu_rep_block_cb_list,
rvu_rep_setup_tc_cb,
rep, rep, true);
default:
return -EOPNOTSUPP;
}
}
static int
rvu_rep_sp_stats64(const struct net_device *dev,
struct rtnl_link_stats64 *stats)
{
struct rep_dev *rep = netdev_priv(dev);
struct otx2_nic *priv = rep->mdev;
struct otx2_rcv_queue *rq;
struct otx2_snd_queue *sq;
u16 qidx = rep->rep_id;
otx2_update_rq_stats(priv, qidx);
rq = &priv->qset.rq[qidx];
otx2_update_sq_stats(priv, qidx);
sq = &priv->qset.sq[qidx];
stats->tx_bytes = sq->stats.bytes;
stats->tx_packets = sq->stats.pkts;
stats->rx_bytes = rq->stats.bytes;
stats->rx_packets = rq->stats.pkts;
return 0;
}
static bool
rvu_rep_has_offload_stats(const struct net_device *dev, int attr_id)
{
return attr_id == IFLA_OFFLOAD_XSTATS_CPU_HIT;
}
static int
rvu_rep_get_offload_stats(int attr_id, const struct net_device *dev,
void *sp)
{
if (attr_id == IFLA_OFFLOAD_XSTATS_CPU_HIT)
return rvu_rep_sp_stats64(dev, (struct rtnl_link_stats64 *)sp);
return -EINVAL;
}
static int rvu_rep_dl_port_fn_hw_addr_get(struct devlink_port *port,
u8 *hw_addr, int *hw_addr_len,
struct netlink_ext_ack *extack)
{
struct rep_dev *rep = container_of(port, struct rep_dev, dl_port);
ether_addr_copy(hw_addr, rep->mac);
*hw_addr_len = ETH_ALEN;
return 0;
}
static int rvu_rep_dl_port_fn_hw_addr_set(struct devlink_port *port,
const u8 *hw_addr, int hw_addr_len,
struct netlink_ext_ack *extack)
{
struct rep_dev *rep = container_of(port, struct rep_dev, dl_port);
struct otx2_nic *priv = rep->mdev;
struct rep_event evt = {0};
eth_hw_addr_set(rep->netdev, hw_addr);
ether_addr_copy(rep->mac, hw_addr);
ether_addr_copy(evt.evt_data.mac, hw_addr);
evt.pcifunc = rep->pcifunc;
rvu_rep_notify_pfvf(priv, RVU_EVENT_MAC_ADDR_CHANGE, &evt);
return 0;
}
static const struct devlink_port_ops rvu_rep_dl_port_ops = {
.port_fn_hw_addr_get = rvu_rep_dl_port_fn_hw_addr_get,
.port_fn_hw_addr_set = rvu_rep_dl_port_fn_hw_addr_set,
};
static void
rvu_rep_devlink_set_switch_id(struct otx2_nic *priv,
struct netdev_phys_item_id *ppid)
{
struct pci_dev *pdev = priv->pdev;
u64 id;
id = pci_get_dsn(pdev);
ppid->id_len = sizeof(id);
put_unaligned_be64(id, &ppid->id);
}
static void rvu_rep_devlink_port_unregister(struct rep_dev *rep)
{
devlink_port_unregister(&rep->dl_port);
}
static int rvu_rep_devlink_port_register(struct rep_dev *rep)
{
struct devlink_port_attrs attrs = {};
struct otx2_nic *priv = rep->mdev;
struct devlink *dl = priv->dl->dl;
int err;
if (!(rep->pcifunc & RVU_PFVF_FUNC_MASK)) {
attrs.flavour = DEVLINK_PORT_FLAVOUR_PHYSICAL;
attrs.phys.port_number = rvu_get_pf(priv->pdev, rep->pcifunc);
} else {
attrs.flavour = DEVLINK_PORT_FLAVOUR_PCI_VF;
attrs.pci_vf.pf = rvu_get_pf(priv->pdev, rep->pcifunc);
attrs.pci_vf.vf = rep->pcifunc & RVU_PFVF_FUNC_MASK;
}
rvu_rep_devlink_set_switch_id(priv, &attrs.switch_id);
devlink_port_attrs_set(&rep->dl_port, &attrs);
err = devl_port_register_with_ops(dl, &rep->dl_port, rep->rep_id,
&rvu_rep_dl_port_ops);
if (err) {
dev_err(rep->mdev->dev, "devlink_port_register failed: %d\n",
err);
return err;
}
return 0;
}
static int rvu_rep_get_repid(struct otx2_nic *priv, u16 pcifunc)
{
int rep_id;
for (rep_id = 0; rep_id < priv->rep_cnt; rep_id++)
if (priv->rep_pf_map[rep_id] == pcifunc)
return rep_id;
return -EINVAL;
}
static int rvu_rep_notify_pfvf(struct otx2_nic *priv, u16 event,
struct rep_event *data)
{
struct rep_event *req;
mutex_lock(&priv->mbox.lock);
req = otx2_mbox_alloc_msg_rep_event_notify(&priv->mbox);
if (!req) {
mutex_unlock(&priv->mbox.lock);
return -ENOMEM;
}
req->event = event;
req->pcifunc = data->pcifunc;
memcpy(&req->evt_data, &data->evt_data, sizeof(struct rep_evt_data));
otx2_sync_mbox_msg(&priv->mbox);
mutex_unlock(&priv->mbox.lock);
return 0;
}
static void rvu_rep_state_evt_handler(struct otx2_nic *priv,
struct rep_event *info)
{
struct rep_dev *rep;
int rep_id;
rep_id = rvu_rep_get_repid(priv, info->pcifunc);
rep = priv->reps[rep_id];
if (info->evt_data.vf_state)
rep->flags |= RVU_REP_VF_INITIALIZED;
else
rep->flags &= ~RVU_REP_VF_INITIALIZED;
}
int rvu_event_up_notify(struct otx2_nic *pf, struct rep_event *info)
{
if (info->event & RVU_EVENT_PFVF_STATE)
rvu_rep_state_evt_handler(pf, info);
return 0;
}
static int rvu_rep_change_mtu(struct net_device *dev, int new_mtu)
{
struct rep_dev *rep = netdev_priv(dev);
struct otx2_nic *priv = rep->mdev;
struct rep_event evt = {0};
netdev_info(dev, "Changing MTU from %d to %d\n",
dev->mtu, new_mtu);
dev->mtu = new_mtu;
evt.evt_data.mtu = new_mtu;
evt.pcifunc = rep->pcifunc;
rvu_rep_notify_pfvf(priv, RVU_EVENT_MTU_CHANGE, &evt);
return 0;
}
static void rvu_rep_get_stats(struct work_struct *work)
{
struct delayed_work *del_work = to_delayed_work(work);
struct nix_stats_req *req;
struct nix_stats_rsp *rsp;
struct rep_stats *stats;
struct otx2_nic *priv;
struct rep_dev *rep;
int err;
rep = container_of(del_work, struct rep_dev, stats_wrk);
priv = rep->mdev;
mutex_lock(&priv->mbox.lock);
req = otx2_mbox_alloc_msg_nix_lf_stats(&priv->mbox);
if (!req) {
mutex_unlock(&priv->mbox.lock);
return;
}
req->pcifunc = rep->pcifunc;
err = otx2_sync_mbox_msg_busy_poll(&priv->mbox);
if (err)
goto exit;
rsp = (struct nix_stats_rsp *)
otx2_mbox_get_rsp(&priv->mbox.mbox, 0, &req->hdr);
if (IS_ERR(rsp)) {
err = PTR_ERR(rsp);
goto exit;
}
stats = &rep->stats;
stats->rx_bytes = rsp->rx.octs;
stats->rx_frames = rsp->rx.ucast + rsp->rx.bcast +
rsp->rx.mcast;
stats->rx_drops = rsp->rx.drop;
stats->rx_mcast_frames = rsp->rx.mcast;
stats->tx_bytes = rsp->tx.octs;
stats->tx_frames = rsp->tx.ucast + rsp->tx.bcast + rsp->tx.mcast;
stats->tx_drops = rsp->tx.drop +
(unsigned long)atomic_long_read(&stats->tx_discards);
exit:
mutex_unlock(&priv->mbox.lock);
}
static void rvu_rep_get_stats64(struct net_device *dev,
struct rtnl_link_stats64 *stats)
{
struct rep_dev *rep = netdev_priv(dev);
if (!(rep->flags & RVU_REP_VF_INITIALIZED))
return;
stats->rx_packets = rep->stats.rx_frames;
stats->rx_bytes = rep->stats.rx_bytes;
stats->rx_dropped = rep->stats.rx_drops;
stats->multicast = rep->stats.rx_mcast_frames;
stats->tx_packets = rep->stats.tx_frames;
stats->tx_bytes = rep->stats.tx_bytes;
stats->tx_dropped = rep->stats.tx_drops;
schedule_delayed_work(&rep->stats_wrk, msecs_to_jiffies(100));
}
static int rvu_eswitch_config(struct otx2_nic *priv, u8 ena)
{
struct esw_cfg_req *req;
mutex_lock(&priv->mbox.lock);
req = otx2_mbox_alloc_msg_esw_cfg(&priv->mbox);
if (!req) {
mutex_unlock(&priv->mbox.lock);
return -ENOMEM;
}
req->ena = ena;
otx2_sync_mbox_msg(&priv->mbox);
mutex_unlock(&priv->mbox.lock);
return 0;
}
static netdev_tx_t rvu_rep_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct rep_dev *rep = netdev_priv(dev);
struct otx2_nic *pf = rep->mdev;
struct otx2_snd_queue *sq;
struct netdev_queue *txq;
struct rep_stats *stats;
/* Check for minimum and maximum packet length */
if (skb->len <= ETH_HLEN ||
(!skb_shinfo(skb)->gso_size && skb->len > pf->tx_max_pktlen)) {
stats = &rep->stats;
atomic_long_inc(&stats->tx_discards);
dev_kfree_skb(skb);
return NETDEV_TX_OK;
}
sq = &pf->qset.sq[rep->rep_id];
txq = netdev_get_tx_queue(dev, 0);
if (!otx2_sq_append_skb(pf, txq, sq, skb, rep->rep_id)) {
netif_tx_stop_queue(txq);
/* Check again, in case SQBs got freed up */
smp_mb();
if (((sq->num_sqbs - *sq->aura_fc_addr) * sq->sqe_per_sqb)
> sq->sqe_thresh)
netif_tx_wake_queue(txq);
return NETDEV_TX_BUSY;
}
return NETDEV_TX_OK;
}
static int rvu_rep_open(struct net_device *dev)
{
struct rep_dev *rep = netdev_priv(dev);
struct otx2_nic *priv = rep->mdev;
struct rep_event evt = {0};
if (!(rep->flags & RVU_REP_VF_INITIALIZED))
return 0;
netif_carrier_on(dev);
netif_tx_start_all_queues(dev);
evt.event = RVU_EVENT_PORT_STATE;
evt.evt_data.port_state = 1;
evt.pcifunc = rep->pcifunc;
rvu_rep_notify_pfvf(priv, RVU_EVENT_PORT_STATE, &evt);
return 0;
}
static int rvu_rep_stop(struct net_device *dev)
{
struct rep_dev *rep = netdev_priv(dev);
struct otx2_nic *priv = rep->mdev;
struct rep_event evt = {0};
if (!(rep->flags & RVU_REP_VF_INITIALIZED))
return 0;
netif_carrier_off(dev);
netif_tx_disable(dev);
evt.event = RVU_EVENT_PORT_STATE;
evt.pcifunc = rep->pcifunc;
rvu_rep_notify_pfvf(priv, RVU_EVENT_PORT_STATE, &evt);
return 0;
}
static const struct net_device_ops rvu_rep_netdev_ops = {
.ndo_open = rvu_rep_open,
.ndo_stop = rvu_rep_stop,
.ndo_start_xmit = rvu_rep_xmit,
.ndo_get_stats64 = rvu_rep_get_stats64,
.ndo_change_mtu = rvu_rep_change_mtu,
.ndo_has_offload_stats = rvu_rep_has_offload_stats,
.ndo_get_offload_stats = rvu_rep_get_offload_stats,
.ndo_setup_tc = rvu_rep_setup_tc,
};
static int rvu_rep_napi_init(struct otx2_nic *priv,
struct netlink_ext_ack *extack)
{
struct otx2_qset *qset = &priv->qset;
struct otx2_cq_poll *cq_poll = NULL;
struct otx2_hw *hw = &priv->hw;
int err = 0, qidx, vec;
char *irq_name;
qset->napi = kzalloc_objs(*cq_poll, hw->cint_cnt);
if (!qset->napi)
return -ENOMEM;
/* Register NAPI handler */
for (qidx = 0; qidx < hw->cint_cnt; qidx++) {
cq_poll = &qset->napi[qidx];
cq_poll->cint_idx = qidx;
cq_poll->cq_ids[CQ_RX] =
(qidx < hw->rx_queues) ? qidx : CINT_INVALID_CQ;
cq_poll->cq_ids[CQ_TX] = (qidx < hw->tx_queues) ?
qidx + hw->rx_queues :
CINT_INVALID_CQ;
cq_poll->cq_ids[CQ_XDP] = CINT_INVALID_CQ;
cq_poll->cq_ids[CQ_QOS] = CINT_INVALID_CQ;
cq_poll->dev = (void *)priv;
netif_napi_add(priv->reps[qidx]->netdev, &cq_poll->napi,
otx2_napi_handler);
napi_enable(&cq_poll->napi);
}
/* Register CQ IRQ handlers */
vec = hw->nix_msixoff + NIX_LF_CINT_VEC_START;
for (qidx = 0; qidx < hw->cint_cnt; qidx++) {
irq_name = &hw->irq_name[vec * NAME_SIZE];
snprintf(irq_name, NAME_SIZE, "rep%d-rxtx-%d", qidx, qidx);
err = request_irq(pci_irq_vector(priv->pdev, vec),
otx2_cq_intr_handler, 0, irq_name,
&qset->napi[qidx]);
if (err) {
NL_SET_ERR_MSG_FMT_MOD(extack,
"RVU REP IRQ registration failed for CQ%d",
qidx);
goto err_free_cints;
}
vec++;
/* Enable CQ IRQ */
otx2_write64(priv, NIX_LF_CINTX_INT(qidx), BIT_ULL(0));
otx2_write64(priv, NIX_LF_CINTX_ENA_W1S(qidx), BIT_ULL(0));
}
priv->flags &= ~OTX2_FLAG_INTF_DOWN;
return 0;
err_free_cints:
otx2_free_cints(priv, qidx);
otx2_disable_napi(priv);
return err;
}
static void rvu_rep_free_cq_rsrc(struct otx2_nic *priv)
{
struct otx2_qset *qset = &priv->qset;
struct otx2_cq_poll *cq_poll = NULL;
int qidx, vec;
/* Cleanup CQ NAPI and IRQ */
vec = priv->hw.nix_msixoff + NIX_LF_CINT_VEC_START;
for (qidx = 0; qidx < priv->hw.cint_cnt; qidx++) {
/* Disable interrupt */
otx2_write64(priv, NIX_LF_CINTX_ENA_W1C(qidx), BIT_ULL(0));
synchronize_irq(pci_irq_vector(priv->pdev, vec));
cq_poll = &qset->napi[qidx];
napi_synchronize(&cq_poll->napi);
vec++;
}
otx2_free_cints(priv, priv->hw.cint_cnt);
otx2_disable_napi(priv);
}
static void rvu_rep_rsrc_free(struct otx2_nic *priv)
{
struct otx2_qset *qset = &priv->qset;
struct delayed_work *work;
int wrk;
for (wrk = 0; wrk < priv->qset.cq_cnt; wrk++) {
work = &priv->refill_wrk[wrk].pool_refill_work;
cancel_delayed_work_sync(work);
}
devm_kfree(priv->dev, priv->refill_wrk);
otx2_free_hw_resources(priv);
otx2_free_queue_mem(qset);
}
static int rvu_rep_rsrc_init(struct otx2_nic *priv)
{
struct otx2_qset *qset = &priv->qset;
int err;
err = otx2_alloc_queue_mem(priv);
if (err)
return err;
priv->hw.max_mtu = otx2_get_max_mtu(priv);
priv->tx_max_pktlen = priv->hw.max_mtu + OTX2_ETH_HLEN;
priv->rbsize = ALIGN(priv->hw.rbuf_len, OTX2_ALIGN) + OTX2_HEAD_ROOM;
err = otx2_init_hw_resources(priv);
if (err)
goto err_free_mem;
/* Set maximum frame size allowed in HW */
err = otx2_hw_set_mtu(priv, priv->hw.max_mtu);
if (err) {
dev_err(priv->dev, "Failed to set HW MTU\n");
goto err_free_rsrc;
}
return 0;
err_free_rsrc:
otx2_free_hw_resources(priv);
err_free_mem:
otx2_free_queue_mem(qset);
return err;
}
void rvu_rep_destroy(struct otx2_nic *priv)
{
struct rep_dev *rep;
int rep_id;
rvu_eswitch_config(priv, false);
priv->flags |= OTX2_FLAG_INTF_DOWN;
rvu_rep_free_cq_rsrc(priv);
for (rep_id = 0; rep_id < priv->rep_cnt; rep_id++) {
rep = priv->reps[rep_id];
unregister_netdev(rep->netdev);
rvu_rep_devlink_port_unregister(rep);
free_netdev(rep->netdev);
kfree(rep->flow_cfg);
}
kfree(priv->reps);
rvu_rep_rsrc_free(priv);
}
int rvu_rep_create(struct otx2_nic *priv, struct netlink_ext_ack *extack)
{
int rep_cnt = priv->rep_cnt;
struct net_device *ndev;
struct rep_dev *rep;
int rep_id, err;
u16 pcifunc;
err = rvu_rep_rsrc_init(priv);
if (err)
return -ENOMEM;
priv->reps = kzalloc_objs(struct rep_dev *, rep_cnt);
if (!priv->reps)
return -ENOMEM;
for (rep_id = 0; rep_id < rep_cnt; rep_id++) {
ndev = alloc_etherdev(sizeof(*rep));
if (!ndev) {
NL_SET_ERR_MSG_FMT_MOD(extack,
"PFVF representor:%d creation failed",
rep_id);
err = -ENOMEM;
goto exit;
}
rep = netdev_priv(ndev);
priv->reps[rep_id] = rep;
rep->mdev = priv;
rep->netdev = ndev;
rep->rep_id = rep_id;
ndev->min_mtu = OTX2_MIN_MTU;
ndev->max_mtu = priv->hw.max_mtu;
ndev->netdev_ops = &rvu_rep_netdev_ops;
pcifunc = priv->rep_pf_map[rep_id];
rep->pcifunc = pcifunc;
snprintf(ndev->name, sizeof(ndev->name), "Rpf%dvf%d",
rvu_get_pf(priv->pdev, pcifunc),
(pcifunc & RVU_PFVF_FUNC_MASK));
ndev->hw_features = (NETIF_F_RXCSUM | NETIF_F_IP_CSUM |
NETIF_F_IPV6_CSUM | NETIF_F_RXHASH |
NETIF_F_SG | NETIF_F_TSO | NETIF_F_TSO6);
ndev->hw_features |= NETIF_F_HW_TC;
ndev->features |= ndev->hw_features;
eth_hw_addr_random(ndev);
err = rvu_rep_devlink_port_register(rep);
if (err) {
free_netdev(ndev);
goto exit;
}
SET_NETDEV_DEVLINK_PORT(ndev, &rep->dl_port);
err = register_netdev(ndev);
if (err) {
NL_SET_ERR_MSG_MOD(extack,
"PFVF representor registration failed");
rvu_rep_devlink_port_unregister(rep);
free_netdev(ndev);
goto exit;
}
INIT_DELAYED_WORK(&rep->stats_wrk, rvu_rep_get_stats);
}
err = rvu_rep_napi_init(priv, extack);
if (err)
goto exit;
rvu_eswitch_config(priv, true);
return 0;
exit:
while (--rep_id >= 0) {
rep = priv->reps[rep_id];
unregister_netdev(rep->netdev);
rvu_rep_devlink_port_unregister(rep);
free_netdev(rep->netdev);
}
kfree(priv->reps);
rvu_rep_rsrc_free(priv);
return err;
}
static int rvu_get_rep_cnt(struct otx2_nic *priv)
{
struct get_rep_cnt_rsp *rsp;
struct mbox_msghdr *msghdr;
struct msg_req *req;
int err, rep;
mutex_lock(&priv->mbox.lock);
req = otx2_mbox_alloc_msg_get_rep_cnt(&priv->mbox);
if (!req) {
mutex_unlock(&priv->mbox.lock);
return -ENOMEM;
}
err = otx2_sync_mbox_msg(&priv->mbox);
if (err)
goto exit;
msghdr = otx2_mbox_get_rsp(&priv->mbox.mbox, 0, &req->hdr);
if (IS_ERR(msghdr)) {
err = PTR_ERR(msghdr);
goto exit;
}
rsp = (struct get_rep_cnt_rsp *)msghdr;
priv->hw.tx_queues = rsp->rep_cnt;
priv->hw.rx_queues = rsp->rep_cnt;
priv->rep_cnt = rsp->rep_cnt;
for (rep = 0; rep < priv->rep_cnt; rep++)
priv->rep_pf_map[rep] = rsp->rep_pf_map[rep];
exit:
mutex_unlock(&priv->mbox.lock);
return err;
}
static int rvu_rep_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{
struct device *dev = &pdev->dev;
struct otx2_nic *priv;
struct otx2_hw *hw;
int err;
err = pcim_enable_device(pdev);
if (err) {
dev_err(dev, "Failed to enable PCI device\n");
return err;
}
err = pcim_request_all_regions(pdev, DRV_NAME);
if (err) {
dev_err(dev, "PCI request regions failed 0x%x\n", err);
return err;
}
err = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(48));
if (err) {
dev_err(dev, "DMA mask config failed, abort\n");
goto err_set_drv_data;
}
pci_set_master(pdev);
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv) {
err = -ENOMEM;
goto err_set_drv_data;
}
pci_set_drvdata(pdev, priv);
priv->pdev = pdev;
priv->dev = dev;
priv->flags |= OTX2_FLAG_INTF_DOWN;
priv->flags |= OTX2_FLAG_REP_MODE_ENABLED;
hw = &priv->hw;
hw->pdev = pdev;
hw->max_queues = OTX2_MAX_CQ_CNT;
hw->rbuf_len = OTX2_DEFAULT_RBUF_LEN;
hw->xqe_size = 128;
err = otx2_init_rsrc(pdev, priv);
if (err)
goto err_set_drv_data;
priv->iommu_domain = iommu_get_domain_for_dev(dev);
err = rvu_get_rep_cnt(priv);
if (err)
goto err_detach_rsrc;
err = otx2_register_dl(priv);
if (err)
goto err_detach_rsrc;
return 0;
err_detach_rsrc:
if (priv->hw.lmt_info)
free_percpu(priv->hw.lmt_info);
if (test_bit(CN10K_LMTST, &priv->hw.cap_flag))
qmem_free(priv->dev, priv->dync_lmt);
otx2_detach_resources(&priv->mbox);
otx2_disable_mbox_intr(priv);
otx2_pfaf_mbox_destroy(priv);
pci_free_irq_vectors(pdev);
err_set_drv_data:
pci_set_drvdata(pdev, NULL);
return err;
}
static void rvu_rep_remove(struct pci_dev *pdev)
{
struct otx2_nic *priv = pci_get_drvdata(pdev);
otx2_unregister_dl(priv);
if (!(priv->flags & OTX2_FLAG_INTF_DOWN))
rvu_rep_destroy(priv);
otx2_detach_resources(&priv->mbox);
if (priv->hw.lmt_info)
free_percpu(priv->hw.lmt_info);
if (test_bit(CN10K_LMTST, &priv->hw.cap_flag))
qmem_free(priv->dev, priv->dync_lmt);
otx2_disable_mbox_intr(priv);
otx2_pfaf_mbox_destroy(priv);
pci_free_irq_vectors(priv->pdev);
pci_set_drvdata(pdev, NULL);
}
static struct pci_driver rvu_rep_driver = {
.name = DRV_NAME,
.id_table = rvu_rep_id_table,
.probe = rvu_rep_probe,
.remove = rvu_rep_remove,
.shutdown = rvu_rep_remove,
};
static int __init rvu_rep_init_module(void)
{
return pci_register_driver(&rvu_rep_driver);
}
static void __exit rvu_rep_cleanup_module(void)
{
pci_unregister_driver(&rvu_rep_driver);
}
module_init(rvu_rep_init_module);
module_exit(rvu_rep_cleanup_module);
@@ -0,0 +1,55 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Marvell RVU REPRESENTOR driver
*
* Copyright (C) 2024 Marvell.
*
*/
#ifndef REP_H
#define REP_H
#include <linux/pci.h>
#include "otx2_reg.h"
#include "otx2_txrx.h"
#include "otx2_common.h"
#define PCI_DEVID_RVU_REP 0xA0E0
#define RVU_MAX_REP OTX2_MAX_CQ_CNT
struct rep_stats {
u64 rx_bytes;
u64 rx_frames;
u64 rx_drops;
u64 rx_mcast_frames;
u64 tx_bytes;
u64 tx_frames;
u64 tx_drops;
atomic_long_t tx_discards;
};
struct rep_dev {
struct otx2_nic *mdev;
struct net_device *netdev;
struct rep_stats stats;
struct delayed_work stats_wrk;
struct devlink_port dl_port;
struct otx2_flow_config *flow_cfg;
#define RVU_REP_VF_INITIALIZED BIT_ULL(0)
u64 flags;
u16 rep_id;
u16 pcifunc;
u8 mac[ETH_ALEN];
};
static inline bool otx2_rep_dev(struct pci_dev *pdev)
{
return pdev->device == PCI_DEVID_RVU_REP;
}
int rvu_rep_create(struct otx2_nic *priv, struct netlink_ext_ack *extack);
void rvu_rep_destroy(struct otx2_nic *priv);
int rvu_event_up_notify(struct otx2_nic *pf, struct rep_event *info);
#endif /* REP_H */