| // 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, |
| }; |
| |
| 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, |
| }; |
| |
| 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)) { |
| pfvf->hw_ops = &otx2_hw_ops; |
| return 0; |
| } |
| |
| pfvf->hw_ops = &cn10k_hw_ops; |
| /* 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, 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; |
| /* Only one SMQ is allocated, map all SQ's to that SMQ */ |
| aq->sq.smq = pfvf->hw.txschq_list[NIX_TXSCH_LVL_SMQ][0]; |
| aq->sq.smq_rr_weight = mtu_to_dwrr_weight(pfvf, pfvf->tx_max_pktlen); |
| aq->sq.default_chan = pfvf->hw.tx_chan_base; |
| 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 |
| void cn10k_refill_pool_ptrs(void *dev, struct otx2_cq_queue *cq) |
| { |
| struct otx2_nic *pfvf = dev; |
| u64 ptrs[NPA_MAX_BURST]; |
| int num_ptrs = 1; |
| dma_addr_t bufptr; |
| |
| /* 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] = (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; |
| } |
| } |
| } |
| |
| 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 (!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); |
| |
| /* 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++) |
| cn10k_map_unmap_rq_policer(pfvf, qidx, |
| hw->matchall_ipolicer, false); |
| |
| 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; |
| |
| /* 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; |
| } |