1
0
Fork 0
mirror of synced 2025-03-06 20:59:54 +01:00
linux/drivers/net/ethernet/marvell/octeontx2/af/rvu.c
Zhou Qingyang e07a097b49 octeontx2-af: Fix a memleak bug in rvu_mbox_init()
In rvu_mbox_init(), mbox_regions is not freed or passed out
under the switch-default region, which could lead to a memory leak.

Fix this bug by changing 'return err' to 'goto free_regions'.

This bug was found by a static analyzer. The analysis employs
differential checking to identify inconsistent security operations
(e.g., checks or kfrees) between two code paths and confirms that the
inconsistent operations are not recovered in the current function or
the callers, so they constitute bugs.

Note that, as a bug found by static analysis, it can be a false
positive or hard to trigger. Multiple researchers have cross-reviewed
the bug.

Builds with CONFIG_OCTEONTX2_AF=y show no new warnings,
and our static analyzer no longer warns about this code.

Fixes: 98c5611163 (“octeontx2-af: cn10k: Add mbox support for CN10K platform”)
Signed-off-by: Zhou Qingyang <zhou1615@umn.edu>
Link: https://lore.kernel.org/r/20211130165039.192426-1-zhou1615@umn.edu
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2021-12-01 19:11:05 -08:00

3353 lines
82 KiB
C

// SPDX-License-Identifier: GPL-2.0
/* Marvell RVU Admin Function driver
*
* Copyright (C) 2018 Marvell.
*
*/
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/irq.h>
#include <linux/pci.h>
#include <linux/sysfs.h>
#include "cgx.h"
#include "rvu.h"
#include "rvu_reg.h"
#include "ptp.h"
#include "rvu_trace.h"
#define DRV_NAME "rvu_af"
#define DRV_STRING "Marvell OcteonTX2 RVU Admin Function Driver"
static int rvu_get_hwvf(struct rvu *rvu, int pcifunc);
static void rvu_set_msix_offset(struct rvu *rvu, struct rvu_pfvf *pfvf,
struct rvu_block *block, int lf);
static void rvu_clear_msix_offset(struct rvu *rvu, struct rvu_pfvf *pfvf,
struct rvu_block *block, int lf);
static void __rvu_flr_handler(struct rvu *rvu, u16 pcifunc);
static int rvu_mbox_init(struct rvu *rvu, struct mbox_wq_info *mw,
int type, int num,
void (mbox_handler)(struct work_struct *),
void (mbox_up_handler)(struct work_struct *));
enum {
TYPE_AFVF,
TYPE_AFPF,
};
/* Supported devices */
static const struct pci_device_id rvu_id_table[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, PCI_DEVID_OCTEONTX2_RVU_AF) },
{ 0, } /* end of table */
};
MODULE_AUTHOR("Sunil Goutham <sgoutham@marvell.com>");
MODULE_DESCRIPTION(DRV_STRING);
MODULE_LICENSE("GPL v2");
MODULE_DEVICE_TABLE(pci, rvu_id_table);
static char *mkex_profile; /* MKEX profile name */
module_param(mkex_profile, charp, 0000);
MODULE_PARM_DESC(mkex_profile, "MKEX profile name string");
static char *kpu_profile; /* KPU profile name */
module_param(kpu_profile, charp, 0000);
MODULE_PARM_DESC(kpu_profile, "KPU profile name string");
static void rvu_setup_hw_capabilities(struct rvu *rvu)
{
struct rvu_hwinfo *hw = rvu->hw;
hw->cap.nix_tx_aggr_lvl = NIX_TXSCH_LVL_TL1;
hw->cap.nix_fixed_txschq_mapping = false;
hw->cap.nix_shaping = true;
hw->cap.nix_tx_link_bp = true;
hw->cap.nix_rx_multicast = true;
hw->cap.nix_shaper_toggle_wait = false;
hw->rvu = rvu;
if (is_rvu_pre_96xx_C0(rvu)) {
hw->cap.nix_fixed_txschq_mapping = true;
hw->cap.nix_txsch_per_cgx_lmac = 4;
hw->cap.nix_txsch_per_lbk_lmac = 132;
hw->cap.nix_txsch_per_sdp_lmac = 76;
hw->cap.nix_shaping = false;
hw->cap.nix_tx_link_bp = false;
if (is_rvu_96xx_A0(rvu) || is_rvu_95xx_A0(rvu))
hw->cap.nix_rx_multicast = false;
}
if (!is_rvu_pre_96xx_C0(rvu))
hw->cap.nix_shaper_toggle_wait = true;
if (!is_rvu_otx2(rvu))
hw->cap.per_pf_mbox_regs = true;
}
/* Poll a RVU block's register 'offset', for a 'zero'
* or 'nonzero' at bits specified by 'mask'
*/
int rvu_poll_reg(struct rvu *rvu, u64 block, u64 offset, u64 mask, bool zero)
{
unsigned long timeout = jiffies + usecs_to_jiffies(20000);
bool twice = false;
void __iomem *reg;
u64 reg_val;
reg = rvu->afreg_base + ((block << 28) | offset);
again:
reg_val = readq(reg);
if (zero && !(reg_val & mask))
return 0;
if (!zero && (reg_val & mask))
return 0;
if (time_before(jiffies, timeout)) {
usleep_range(1, 5);
goto again;
}
/* In scenarios where CPU is scheduled out before checking
* 'time_before' (above) and gets scheduled in such that
* jiffies are beyond timeout value, then check again if HW is
* done with the operation in the meantime.
*/
if (!twice) {
twice = true;
goto again;
}
return -EBUSY;
}
int rvu_alloc_rsrc(struct rsrc_bmap *rsrc)
{
int id;
if (!rsrc->bmap)
return -EINVAL;
id = find_first_zero_bit(rsrc->bmap, rsrc->max);
if (id >= rsrc->max)
return -ENOSPC;
__set_bit(id, rsrc->bmap);
return id;
}
int rvu_alloc_rsrc_contig(struct rsrc_bmap *rsrc, int nrsrc)
{
int start;
if (!rsrc->bmap)
return -EINVAL;
start = bitmap_find_next_zero_area(rsrc->bmap, rsrc->max, 0, nrsrc, 0);
if (start >= rsrc->max)
return -ENOSPC;
bitmap_set(rsrc->bmap, start, nrsrc);
return start;
}
static void rvu_free_rsrc_contig(struct rsrc_bmap *rsrc, int nrsrc, int start)
{
if (!rsrc->bmap)
return;
if (start >= rsrc->max)
return;
bitmap_clear(rsrc->bmap, start, nrsrc);
}
bool rvu_rsrc_check_contig(struct rsrc_bmap *rsrc, int nrsrc)
{
int start;
if (!rsrc->bmap)
return false;
start = bitmap_find_next_zero_area(rsrc->bmap, rsrc->max, 0, nrsrc, 0);
if (start >= rsrc->max)
return false;
return true;
}
void rvu_free_rsrc(struct rsrc_bmap *rsrc, int id)
{
if (!rsrc->bmap)
return;
__clear_bit(id, rsrc->bmap);
}
int rvu_rsrc_free_count(struct rsrc_bmap *rsrc)
{
int used;
if (!rsrc->bmap)
return 0;
used = bitmap_weight(rsrc->bmap, rsrc->max);
return (rsrc->max - used);
}
bool is_rsrc_free(struct rsrc_bmap *rsrc, int id)
{
if (!rsrc->bmap)
return false;
return !test_bit(id, rsrc->bmap);
}
int rvu_alloc_bitmap(struct rsrc_bmap *rsrc)
{
rsrc->bmap = kcalloc(BITS_TO_LONGS(rsrc->max),
sizeof(long), GFP_KERNEL);
if (!rsrc->bmap)
return -ENOMEM;
return 0;
}
void rvu_free_bitmap(struct rsrc_bmap *rsrc)
{
kfree(rsrc->bmap);
}
/* Get block LF's HW index from a PF_FUNC's block slot number */
int rvu_get_lf(struct rvu *rvu, struct rvu_block *block, u16 pcifunc, u16 slot)
{
u16 match = 0;
int lf;
mutex_lock(&rvu->rsrc_lock);
for (lf = 0; lf < block->lf.max; lf++) {
if (block->fn_map[lf] == pcifunc) {
if (slot == match) {
mutex_unlock(&rvu->rsrc_lock);
return lf;
}
match++;
}
}
mutex_unlock(&rvu->rsrc_lock);
return -ENODEV;
}
/* Convert BLOCK_TYPE_E to a BLOCK_ADDR_E.
* Some silicon variants of OcteonTX2 supports
* multiple blocks of same type.
*
* @pcifunc has to be zero when no LF is yet attached.
*
* For a pcifunc if LFs are attached from multiple blocks of same type, then
* return blkaddr of first encountered block.
*/
int rvu_get_blkaddr(struct rvu *rvu, int blktype, u16 pcifunc)
{
int devnum, blkaddr = -ENODEV;
u64 cfg, reg;
bool is_pf;
switch (blktype) {
case BLKTYPE_NPC:
blkaddr = BLKADDR_NPC;
goto exit;
case BLKTYPE_NPA:
blkaddr = BLKADDR_NPA;
goto exit;
case BLKTYPE_NIX:
/* For now assume NIX0 */
if (!pcifunc) {
blkaddr = BLKADDR_NIX0;
goto exit;
}
break;
case BLKTYPE_SSO:
blkaddr = BLKADDR_SSO;
goto exit;
case BLKTYPE_SSOW:
blkaddr = BLKADDR_SSOW;
goto exit;
case BLKTYPE_TIM:
blkaddr = BLKADDR_TIM;
goto exit;
case BLKTYPE_CPT:
/* For now assume CPT0 */
if (!pcifunc) {
blkaddr = BLKADDR_CPT0;
goto exit;
}
break;
}
/* Check if this is a RVU PF or VF */
if (pcifunc & RVU_PFVF_FUNC_MASK) {
is_pf = false;
devnum = rvu_get_hwvf(rvu, pcifunc);
} else {
is_pf = true;
devnum = rvu_get_pf(pcifunc);
}
/* Check if the 'pcifunc' has a NIX LF from 'BLKADDR_NIX0' or
* 'BLKADDR_NIX1'.
*/
if (blktype == BLKTYPE_NIX) {
reg = is_pf ? RVU_PRIV_PFX_NIXX_CFG(0) :
RVU_PRIV_HWVFX_NIXX_CFG(0);
cfg = rvu_read64(rvu, BLKADDR_RVUM, reg | (devnum << 16));
if (cfg) {
blkaddr = BLKADDR_NIX0;
goto exit;
}
reg = is_pf ? RVU_PRIV_PFX_NIXX_CFG(1) :
RVU_PRIV_HWVFX_NIXX_CFG(1);
cfg = rvu_read64(rvu, BLKADDR_RVUM, reg | (devnum << 16));
if (cfg)
blkaddr = BLKADDR_NIX1;
}
if (blktype == BLKTYPE_CPT) {
reg = is_pf ? RVU_PRIV_PFX_CPTX_CFG(0) :
RVU_PRIV_HWVFX_CPTX_CFG(0);
cfg = rvu_read64(rvu, BLKADDR_RVUM, reg | (devnum << 16));
if (cfg) {
blkaddr = BLKADDR_CPT0;
goto exit;
}
reg = is_pf ? RVU_PRIV_PFX_CPTX_CFG(1) :
RVU_PRIV_HWVFX_CPTX_CFG(1);
cfg = rvu_read64(rvu, BLKADDR_RVUM, reg | (devnum << 16));
if (cfg)
blkaddr = BLKADDR_CPT1;
}
exit:
if (is_block_implemented(rvu->hw, blkaddr))
return blkaddr;
return -ENODEV;
}
static void rvu_update_rsrc_map(struct rvu *rvu, struct rvu_pfvf *pfvf,
struct rvu_block *block, u16 pcifunc,
u16 lf, bool attach)
{
int devnum, num_lfs = 0;
bool is_pf;
u64 reg;
if (lf >= block->lf.max) {
dev_err(&rvu->pdev->dev,
"%s: FATAL: LF %d is >= %s's max lfs i.e %d\n",
__func__, lf, block->name, block->lf.max);
return;
}
/* Check if this is for a RVU PF or VF */
if (pcifunc & RVU_PFVF_FUNC_MASK) {
is_pf = false;
devnum = rvu_get_hwvf(rvu, pcifunc);
} else {
is_pf = true;
devnum = rvu_get_pf(pcifunc);
}
block->fn_map[lf] = attach ? pcifunc : 0;
switch (block->addr) {
case BLKADDR_NPA:
pfvf->npalf = attach ? true : false;
num_lfs = pfvf->npalf;
break;
case BLKADDR_NIX0:
case BLKADDR_NIX1:
pfvf->nixlf = attach ? true : false;
num_lfs = pfvf->nixlf;
break;
case BLKADDR_SSO:
attach ? pfvf->sso++ : pfvf->sso--;
num_lfs = pfvf->sso;
break;
case BLKADDR_SSOW:
attach ? pfvf->ssow++ : pfvf->ssow--;
num_lfs = pfvf->ssow;
break;
case BLKADDR_TIM:
attach ? pfvf->timlfs++ : pfvf->timlfs--;
num_lfs = pfvf->timlfs;
break;
case BLKADDR_CPT0:
attach ? pfvf->cptlfs++ : pfvf->cptlfs--;
num_lfs = pfvf->cptlfs;
break;
case BLKADDR_CPT1:
attach ? pfvf->cpt1_lfs++ : pfvf->cpt1_lfs--;
num_lfs = pfvf->cpt1_lfs;
break;
}
reg = is_pf ? block->pf_lfcnt_reg : block->vf_lfcnt_reg;
rvu_write64(rvu, BLKADDR_RVUM, reg | (devnum << 16), num_lfs);
}
inline int rvu_get_pf(u16 pcifunc)
{
return (pcifunc >> RVU_PFVF_PF_SHIFT) & RVU_PFVF_PF_MASK;
}
void rvu_get_pf_numvfs(struct rvu *rvu, int pf, int *numvfs, int *hwvf)
{
u64 cfg;
/* Get numVFs attached to this PF and first HWVF */
cfg = rvu_read64(rvu, BLKADDR_RVUM, RVU_PRIV_PFX_CFG(pf));
if (numvfs)
*numvfs = (cfg >> 12) & 0xFF;
if (hwvf)
*hwvf = cfg & 0xFFF;
}
static int rvu_get_hwvf(struct rvu *rvu, int pcifunc)
{
int pf, func;
u64 cfg;
pf = rvu_get_pf(pcifunc);
func = pcifunc & RVU_PFVF_FUNC_MASK;
/* Get first HWVF attached to this PF */
cfg = rvu_read64(rvu, BLKADDR_RVUM, RVU_PRIV_PFX_CFG(pf));
return ((cfg & 0xFFF) + func - 1);
}
struct rvu_pfvf *rvu_get_pfvf(struct rvu *rvu, int pcifunc)
{
/* Check if it is a PF or VF */
if (pcifunc & RVU_PFVF_FUNC_MASK)
return &rvu->hwvf[rvu_get_hwvf(rvu, pcifunc)];
else
return &rvu->pf[rvu_get_pf(pcifunc)];
}
static bool is_pf_func_valid(struct rvu *rvu, u16 pcifunc)
{
int pf, vf, nvfs;
u64 cfg;
pf = rvu_get_pf(pcifunc);
if (pf >= rvu->hw->total_pfs)
return false;
if (!(pcifunc & RVU_PFVF_FUNC_MASK))
return true;
/* Check if VF is within number of VFs attached to this PF */
vf = (pcifunc & RVU_PFVF_FUNC_MASK) - 1;
cfg = rvu_read64(rvu, BLKADDR_RVUM, RVU_PRIV_PFX_CFG(pf));
nvfs = (cfg >> 12) & 0xFF;
if (vf >= nvfs)
return false;
return true;
}
bool is_block_implemented(struct rvu_hwinfo *hw, int blkaddr)
{
struct rvu_block *block;
if (blkaddr < BLKADDR_RVUM || blkaddr >= BLK_COUNT)
return false;
block = &hw->block[blkaddr];
return block->implemented;
}
static void rvu_check_block_implemented(struct rvu *rvu)
{
struct rvu_hwinfo *hw = rvu->hw;
struct rvu_block *block;
int blkid;
u64 cfg;
/* For each block check if 'implemented' bit is set */
for (blkid = 0; blkid < BLK_COUNT; blkid++) {
block = &hw->block[blkid];
cfg = rvupf_read64(rvu, RVU_PF_BLOCK_ADDRX_DISC(blkid));
if (cfg & BIT_ULL(11))
block->implemented = true;
}
}
static void rvu_setup_rvum_blk_revid(struct rvu *rvu)
{
rvu_write64(rvu, BLKADDR_RVUM,
RVU_PRIV_BLOCK_TYPEX_REV(BLKTYPE_RVUM),
RVU_BLK_RVUM_REVID);
}
static void rvu_clear_rvum_blk_revid(struct rvu *rvu)
{
rvu_write64(rvu, BLKADDR_RVUM,
RVU_PRIV_BLOCK_TYPEX_REV(BLKTYPE_RVUM), 0x00);
}
int rvu_lf_reset(struct rvu *rvu, struct rvu_block *block, int lf)
{
int err;
if (!block->implemented)
return 0;
rvu_write64(rvu, block->addr, block->lfreset_reg, lf | BIT_ULL(12));
err = rvu_poll_reg(rvu, block->addr, block->lfreset_reg, BIT_ULL(12),
true);
return err;
}
static void rvu_block_reset(struct rvu *rvu, int blkaddr, u64 rst_reg)
{
struct rvu_block *block = &rvu->hw->block[blkaddr];
int err;
if (!block->implemented)
return;
rvu_write64(rvu, blkaddr, rst_reg, BIT_ULL(0));
err = rvu_poll_reg(rvu, blkaddr, rst_reg, BIT_ULL(63), true);
if (err)
dev_err(rvu->dev, "HW block:%d reset failed\n", blkaddr);
}
static void rvu_reset_all_blocks(struct rvu *rvu)
{
/* Do a HW reset of all RVU blocks */
rvu_block_reset(rvu, BLKADDR_NPA, NPA_AF_BLK_RST);
rvu_block_reset(rvu, BLKADDR_NIX0, NIX_AF_BLK_RST);
rvu_block_reset(rvu, BLKADDR_NIX1, NIX_AF_BLK_RST);
rvu_block_reset(rvu, BLKADDR_NPC, NPC_AF_BLK_RST);
rvu_block_reset(rvu, BLKADDR_SSO, SSO_AF_BLK_RST);
rvu_block_reset(rvu, BLKADDR_TIM, TIM_AF_BLK_RST);
rvu_block_reset(rvu, BLKADDR_CPT0, CPT_AF_BLK_RST);
rvu_block_reset(rvu, BLKADDR_CPT1, CPT_AF_BLK_RST);
rvu_block_reset(rvu, BLKADDR_NDC_NIX0_RX, NDC_AF_BLK_RST);
rvu_block_reset(rvu, BLKADDR_NDC_NIX0_TX, NDC_AF_BLK_RST);
rvu_block_reset(rvu, BLKADDR_NDC_NIX1_RX, NDC_AF_BLK_RST);
rvu_block_reset(rvu, BLKADDR_NDC_NIX1_TX, NDC_AF_BLK_RST);
rvu_block_reset(rvu, BLKADDR_NDC_NPA0, NDC_AF_BLK_RST);
}
static void rvu_scan_block(struct rvu *rvu, struct rvu_block *block)
{
struct rvu_pfvf *pfvf;
u64 cfg;
int lf;
for (lf = 0; lf < block->lf.max; lf++) {
cfg = rvu_read64(rvu, block->addr,
block->lfcfg_reg | (lf << block->lfshift));
if (!(cfg & BIT_ULL(63)))
continue;
/* Set this resource as being used */
__set_bit(lf, block->lf.bmap);
/* Get, to whom this LF is attached */
pfvf = rvu_get_pfvf(rvu, (cfg >> 8) & 0xFFFF);
rvu_update_rsrc_map(rvu, pfvf, block,
(cfg >> 8) & 0xFFFF, lf, true);
/* Set start MSIX vector for this LF within this PF/VF */
rvu_set_msix_offset(rvu, pfvf, block, lf);
}
}
static void rvu_check_min_msix_vec(struct rvu *rvu, int nvecs, int pf, int vf)
{
int min_vecs;
if (!vf)
goto check_pf;
if (!nvecs) {
dev_warn(rvu->dev,
"PF%d:VF%d is configured with zero msix vectors, %d\n",
pf, vf - 1, nvecs);
}
return;
check_pf:
if (pf == 0)
min_vecs = RVU_AF_INT_VEC_CNT + RVU_PF_INT_VEC_CNT;
else
min_vecs = RVU_PF_INT_VEC_CNT;
if (!(nvecs < min_vecs))
return;
dev_warn(rvu->dev,
"PF%d is configured with too few vectors, %d, min is %d\n",
pf, nvecs, min_vecs);
}
static int rvu_setup_msix_resources(struct rvu *rvu)
{
struct rvu_hwinfo *hw = rvu->hw;
int pf, vf, numvfs, hwvf, err;
int nvecs, offset, max_msix;
struct rvu_pfvf *pfvf;
u64 cfg, phy_addr;
dma_addr_t iova;
for (pf = 0; pf < hw->total_pfs; pf++) {
cfg = rvu_read64(rvu, BLKADDR_RVUM, RVU_PRIV_PFX_CFG(pf));
/* If PF is not enabled, nothing to do */
if (!((cfg >> 20) & 0x01))
continue;
rvu_get_pf_numvfs(rvu, pf, &numvfs, &hwvf);
pfvf = &rvu->pf[pf];
/* Get num of MSIX vectors attached to this PF */
cfg = rvu_read64(rvu, BLKADDR_RVUM, RVU_PRIV_PFX_MSIX_CFG(pf));
pfvf->msix.max = ((cfg >> 32) & 0xFFF) + 1;
rvu_check_min_msix_vec(rvu, pfvf->msix.max, pf, 0);
/* Alloc msix bitmap for this PF */
err = rvu_alloc_bitmap(&pfvf->msix);
if (err)
return err;
/* Allocate memory for MSIX vector to RVU block LF mapping */
pfvf->msix_lfmap = devm_kcalloc(rvu->dev, pfvf->msix.max,
sizeof(u16), GFP_KERNEL);
if (!pfvf->msix_lfmap)
return -ENOMEM;
/* For PF0 (AF) firmware will set msix vector offsets for
* AF, block AF and PF0_INT vectors, so jump to VFs.
*/
if (!pf)
goto setup_vfmsix;
/* Set MSIX offset for PF's 'RVU_PF_INT_VEC' vectors.
* These are allocated on driver init and never freed,
* so no need to set 'msix_lfmap' for these.
*/
cfg = rvu_read64(rvu, BLKADDR_RVUM, RVU_PRIV_PFX_INT_CFG(pf));
nvecs = (cfg >> 12) & 0xFF;
cfg &= ~0x7FFULL;
offset = rvu_alloc_rsrc_contig(&pfvf->msix, nvecs);
rvu_write64(rvu, BLKADDR_RVUM,
RVU_PRIV_PFX_INT_CFG(pf), cfg | offset);
setup_vfmsix:
/* Alloc msix bitmap for VFs */
for (vf = 0; vf < numvfs; vf++) {
pfvf = &rvu->hwvf[hwvf + vf];
/* Get num of MSIX vectors attached to this VF */
cfg = rvu_read64(rvu, BLKADDR_RVUM,
RVU_PRIV_PFX_MSIX_CFG(pf));
pfvf->msix.max = (cfg & 0xFFF) + 1;
rvu_check_min_msix_vec(rvu, pfvf->msix.max, pf, vf + 1);
/* Alloc msix bitmap for this VF */
err = rvu_alloc_bitmap(&pfvf->msix);
if (err)
return err;
pfvf->msix_lfmap =
devm_kcalloc(rvu->dev, pfvf->msix.max,
sizeof(u16), GFP_KERNEL);
if (!pfvf->msix_lfmap)
return -ENOMEM;
/* Set MSIX offset for HWVF's 'RVU_VF_INT_VEC' vectors.
* These are allocated on driver init and never freed,
* so no need to set 'msix_lfmap' for these.
*/
cfg = rvu_read64(rvu, BLKADDR_RVUM,
RVU_PRIV_HWVFX_INT_CFG(hwvf + vf));
nvecs = (cfg >> 12) & 0xFF;
cfg &= ~0x7FFULL;
offset = rvu_alloc_rsrc_contig(&pfvf->msix, nvecs);
rvu_write64(rvu, BLKADDR_RVUM,
RVU_PRIV_HWVFX_INT_CFG(hwvf + vf),
cfg | offset);
}
}
/* HW interprets RVU_AF_MSIXTR_BASE address as an IOVA, hence
* create an IOMMU mapping for the physical address configured by
* firmware and reconfig RVU_AF_MSIXTR_BASE with IOVA.
*/
cfg = rvu_read64(rvu, BLKADDR_RVUM, RVU_PRIV_CONST);
max_msix = cfg & 0xFFFFF;
if (rvu->fwdata && rvu->fwdata->msixtr_base)
phy_addr = rvu->fwdata->msixtr_base;
else
phy_addr = rvu_read64(rvu, BLKADDR_RVUM, RVU_AF_MSIXTR_BASE);
iova = dma_map_resource(rvu->dev, phy_addr,
max_msix * PCI_MSIX_ENTRY_SIZE,
DMA_BIDIRECTIONAL, 0);
if (dma_mapping_error(rvu->dev, iova))
return -ENOMEM;
rvu_write64(rvu, BLKADDR_RVUM, RVU_AF_MSIXTR_BASE, (u64)iova);
rvu->msix_base_iova = iova;
rvu->msixtr_base_phy = phy_addr;
return 0;
}
static void rvu_reset_msix(struct rvu *rvu)
{
/* Restore msixtr base register */
rvu_write64(rvu, BLKADDR_RVUM, RVU_AF_MSIXTR_BASE,
rvu->msixtr_base_phy);
}
static void rvu_free_hw_resources(struct rvu *rvu)
{
struct rvu_hwinfo *hw = rvu->hw;
struct rvu_block *block;
struct rvu_pfvf *pfvf;
int id, max_msix;
u64 cfg;
rvu_npa_freemem(rvu);
rvu_npc_freemem(rvu);
rvu_nix_freemem(rvu);
/* Free block LF bitmaps */
for (id = 0; id < BLK_COUNT; id++) {
block = &hw->block[id];
kfree(block->lf.bmap);
}
/* Free MSIX bitmaps */
for (id = 0; id < hw->total_pfs; id++) {
pfvf = &rvu->pf[id];
kfree(pfvf->msix.bmap);
}
for (id = 0; id < hw->total_vfs; id++) {
pfvf = &rvu->hwvf[id];
kfree(pfvf->msix.bmap);
}
/* Unmap MSIX vector base IOVA mapping */
if (!rvu->msix_base_iova)
return;
cfg = rvu_read64(rvu, BLKADDR_RVUM, RVU_PRIV_CONST);
max_msix = cfg & 0xFFFFF;
dma_unmap_resource(rvu->dev, rvu->msix_base_iova,
max_msix * PCI_MSIX_ENTRY_SIZE,
DMA_BIDIRECTIONAL, 0);
rvu_reset_msix(rvu);
mutex_destroy(&rvu->rsrc_lock);
}
static void rvu_setup_pfvf_macaddress(struct rvu *rvu)
{
struct rvu_hwinfo *hw = rvu->hw;
int pf, vf, numvfs, hwvf;
struct rvu_pfvf *pfvf;
u64 *mac;
for (pf = 0; pf < hw->total_pfs; pf++) {
/* For PF0(AF), Assign MAC address to only VFs (LBKVFs) */
if (!pf)
goto lbkvf;
if (!is_pf_cgxmapped(rvu, pf))
continue;
/* Assign MAC address to PF */
pfvf = &rvu->pf[pf];
if (rvu->fwdata && pf < PF_MACNUM_MAX) {
mac = &rvu->fwdata->pf_macs[pf];
if (*mac)
u64_to_ether_addr(*mac, pfvf->mac_addr);
else
eth_random_addr(pfvf->mac_addr);
} else {
eth_random_addr(pfvf->mac_addr);
}
ether_addr_copy(pfvf->default_mac, pfvf->mac_addr);
lbkvf:
/* Assign MAC address to VFs*/
rvu_get_pf_numvfs(rvu, pf, &numvfs, &hwvf);
for (vf = 0; vf < numvfs; vf++, hwvf++) {
pfvf = &rvu->hwvf[hwvf];
if (rvu->fwdata && hwvf < VF_MACNUM_MAX) {
mac = &rvu->fwdata->vf_macs[hwvf];
if (*mac)
u64_to_ether_addr(*mac, pfvf->mac_addr);
else
eth_random_addr(pfvf->mac_addr);
} else {
eth_random_addr(pfvf->mac_addr);
}
ether_addr_copy(pfvf->default_mac, pfvf->mac_addr);
}
}
}
static int rvu_fwdata_init(struct rvu *rvu)
{
u64 fwdbase;
int err;
/* Get firmware data base address */
err = cgx_get_fwdata_base(&fwdbase);
if (err)
goto fail;
rvu->fwdata = ioremap_wc(fwdbase, sizeof(struct rvu_fwdata));
if (!rvu->fwdata)
goto fail;
if (!is_rvu_fwdata_valid(rvu)) {
dev_err(rvu->dev,
"Mismatch in 'fwdata' struct btw kernel and firmware\n");
iounmap(rvu->fwdata);
rvu->fwdata = NULL;
return -EINVAL;
}
return 0;
fail:
dev_info(rvu->dev, "Unable to fetch 'fwdata' from firmware\n");
return -EIO;
}
static void rvu_fwdata_exit(struct rvu *rvu)
{
if (rvu->fwdata)
iounmap(rvu->fwdata);
}
static int rvu_setup_nix_hw_resource(struct rvu *rvu, int blkaddr)
{
struct rvu_hwinfo *hw = rvu->hw;
struct rvu_block *block;
int blkid;
u64 cfg;
/* Init NIX LF's bitmap */
block = &hw->block[blkaddr];
if (!block->implemented)
return 0;
blkid = (blkaddr == BLKADDR_NIX0) ? 0 : 1;
cfg = rvu_read64(rvu, blkaddr, NIX_AF_CONST2);
block->lf.max = cfg & 0xFFF;
block->addr = blkaddr;
block->type = BLKTYPE_NIX;
block->lfshift = 8;
block->lookup_reg = NIX_AF_RVU_LF_CFG_DEBUG;
block->pf_lfcnt_reg = RVU_PRIV_PFX_NIXX_CFG(blkid);
block->vf_lfcnt_reg = RVU_PRIV_HWVFX_NIXX_CFG(blkid);
block->lfcfg_reg = NIX_PRIV_LFX_CFG;
block->msixcfg_reg = NIX_PRIV_LFX_INT_CFG;
block->lfreset_reg = NIX_AF_LF_RST;
block->rvu = rvu;
sprintf(block->name, "NIX%d", blkid);
rvu->nix_blkaddr[blkid] = blkaddr;
return rvu_alloc_bitmap(&block->lf);
}
static int rvu_setup_cpt_hw_resource(struct rvu *rvu, int blkaddr)
{
struct rvu_hwinfo *hw = rvu->hw;
struct rvu_block *block;
int blkid;
u64 cfg;
/* Init CPT LF's bitmap */
block = &hw->block[blkaddr];
if (!block->implemented)
return 0;
blkid = (blkaddr == BLKADDR_CPT0) ? 0 : 1;
cfg = rvu_read64(rvu, blkaddr, CPT_AF_CONSTANTS0);
block->lf.max = cfg & 0xFF;
block->addr = blkaddr;
block->type = BLKTYPE_CPT;
block->multislot = true;
block->lfshift = 3;
block->lookup_reg = CPT_AF_RVU_LF_CFG_DEBUG;
block->pf_lfcnt_reg = RVU_PRIV_PFX_CPTX_CFG(blkid);
block->vf_lfcnt_reg = RVU_PRIV_HWVFX_CPTX_CFG(blkid);
block->lfcfg_reg = CPT_PRIV_LFX_CFG;
block->msixcfg_reg = CPT_PRIV_LFX_INT_CFG;
block->lfreset_reg = CPT_AF_LF_RST;
block->rvu = rvu;
sprintf(block->name, "CPT%d", blkid);
return rvu_alloc_bitmap(&block->lf);
}
static void rvu_get_lbk_bufsize(struct rvu *rvu)
{
struct pci_dev *pdev = NULL;
void __iomem *base;
u64 lbk_const;
pdev = pci_get_device(PCI_VENDOR_ID_CAVIUM,
PCI_DEVID_OCTEONTX2_LBK, pdev);
if (!pdev)
return;
base = pci_ioremap_bar(pdev, 0);
if (!base)
goto err_put;
lbk_const = readq(base + LBK_CONST);
/* cache fifo size */
rvu->hw->lbk_bufsize = FIELD_GET(LBK_CONST_BUF_SIZE, lbk_const);
iounmap(base);
err_put:
pci_dev_put(pdev);
}
static int rvu_setup_hw_resources(struct rvu *rvu)
{
struct rvu_hwinfo *hw = rvu->hw;
struct rvu_block *block;
int blkid, err;
u64 cfg;
/* Get HW supported max RVU PF & VF count */
cfg = rvu_read64(rvu, BLKADDR_RVUM, RVU_PRIV_CONST);
hw->total_pfs = (cfg >> 32) & 0xFF;
hw->total_vfs = (cfg >> 20) & 0xFFF;
hw->max_vfs_per_pf = (cfg >> 40) & 0xFF;
/* Init NPA LF's bitmap */
block = &hw->block[BLKADDR_NPA];
if (!block->implemented)
goto nix;
cfg = rvu_read64(rvu, BLKADDR_NPA, NPA_AF_CONST);
block->lf.max = (cfg >> 16) & 0xFFF;
block->addr = BLKADDR_NPA;
block->type = BLKTYPE_NPA;
block->lfshift = 8;
block->lookup_reg = NPA_AF_RVU_LF_CFG_DEBUG;
block->pf_lfcnt_reg = RVU_PRIV_PFX_NPA_CFG;
block->vf_lfcnt_reg = RVU_PRIV_HWVFX_NPA_CFG;
block->lfcfg_reg = NPA_PRIV_LFX_CFG;
block->msixcfg_reg = NPA_PRIV_LFX_INT_CFG;
block->lfreset_reg = NPA_AF_LF_RST;
block->rvu = rvu;
sprintf(block->name, "NPA");
err = rvu_alloc_bitmap(&block->lf);
if (err) {
dev_err(rvu->dev,
"%s: Failed to allocate NPA LF bitmap\n", __func__);
return err;
}
nix:
err = rvu_setup_nix_hw_resource(rvu, BLKADDR_NIX0);
if (err) {
dev_err(rvu->dev,
"%s: Failed to allocate NIX0 LFs bitmap\n", __func__);
return err;
}
err = rvu_setup_nix_hw_resource(rvu, BLKADDR_NIX1);
if (err) {
dev_err(rvu->dev,
"%s: Failed to allocate NIX1 LFs bitmap\n", __func__);
return err;
}
/* Init SSO group's bitmap */
block = &hw->block[BLKADDR_SSO];
if (!block->implemented)
goto ssow;
cfg = rvu_read64(rvu, BLKADDR_SSO, SSO_AF_CONST);
block->lf.max = cfg & 0xFFFF;
block->addr = BLKADDR_SSO;
block->type = BLKTYPE_SSO;
block->multislot = true;
block->lfshift = 3;
block->lookup_reg = SSO_AF_RVU_LF_CFG_DEBUG;
block->pf_lfcnt_reg = RVU_PRIV_PFX_SSO_CFG;
block->vf_lfcnt_reg = RVU_PRIV_HWVFX_SSO_CFG;
block->lfcfg_reg = SSO_PRIV_LFX_HWGRP_CFG;
block->msixcfg_reg = SSO_PRIV_LFX_HWGRP_INT_CFG;
block->lfreset_reg = SSO_AF_LF_HWGRP_RST;
block->rvu = rvu;
sprintf(block->name, "SSO GROUP");
err = rvu_alloc_bitmap(&block->lf);
if (err) {
dev_err(rvu->dev,
"%s: Failed to allocate SSO LF bitmap\n", __func__);
return err;
}
ssow:
/* Init SSO workslot's bitmap */
block = &hw->block[BLKADDR_SSOW];
if (!block->implemented)
goto tim;
block->lf.max = (cfg >> 56) & 0xFF;
block->addr = BLKADDR_SSOW;
block->type = BLKTYPE_SSOW;
block->multislot = true;
block->lfshift = 3;
block->lookup_reg = SSOW_AF_RVU_LF_HWS_CFG_DEBUG;
block->pf_lfcnt_reg = RVU_PRIV_PFX_SSOW_CFG;
block->vf_lfcnt_reg = RVU_PRIV_HWVFX_SSOW_CFG;
block->lfcfg_reg = SSOW_PRIV_LFX_HWS_CFG;
block->msixcfg_reg = SSOW_PRIV_LFX_HWS_INT_CFG;
block->lfreset_reg = SSOW_AF_LF_HWS_RST;
block->rvu = rvu;
sprintf(block->name, "SSOWS");
err = rvu_alloc_bitmap(&block->lf);
if (err) {
dev_err(rvu->dev,
"%s: Failed to allocate SSOW LF bitmap\n", __func__);
return err;
}
tim:
/* Init TIM LF's bitmap */
block = &hw->block[BLKADDR_TIM];
if (!block->implemented)
goto cpt;
cfg = rvu_read64(rvu, BLKADDR_TIM, TIM_AF_CONST);
block->lf.max = cfg & 0xFFFF;
block->addr = BLKADDR_TIM;
block->type = BLKTYPE_TIM;
block->multislot = true;
block->lfshift = 3;
block->lookup_reg = TIM_AF_RVU_LF_CFG_DEBUG;
block->pf_lfcnt_reg = RVU_PRIV_PFX_TIM_CFG;
block->vf_lfcnt_reg = RVU_PRIV_HWVFX_TIM_CFG;
block->lfcfg_reg = TIM_PRIV_LFX_CFG;
block->msixcfg_reg = TIM_PRIV_LFX_INT_CFG;
block->lfreset_reg = TIM_AF_LF_RST;
block->rvu = rvu;
sprintf(block->name, "TIM");
err = rvu_alloc_bitmap(&block->lf);
if (err) {
dev_err(rvu->dev,
"%s: Failed to allocate TIM LF bitmap\n", __func__);
return err;
}
cpt:
err = rvu_setup_cpt_hw_resource(rvu, BLKADDR_CPT0);
if (err) {
dev_err(rvu->dev,
"%s: Failed to allocate CPT0 LF bitmap\n", __func__);
return err;
}
err = rvu_setup_cpt_hw_resource(rvu, BLKADDR_CPT1);
if (err) {
dev_err(rvu->dev,
"%s: Failed to allocate CPT1 LF bitmap\n", __func__);
return err;
}
/* Allocate memory for PFVF data */
rvu->pf = devm_kcalloc(rvu->dev, hw->total_pfs,
sizeof(struct rvu_pfvf), GFP_KERNEL);
if (!rvu->pf) {
dev_err(rvu->dev,
"%s: Failed to allocate memory for PF's rvu_pfvf struct\n", __func__);
return -ENOMEM;
}
rvu->hwvf = devm_kcalloc(rvu->dev, hw->total_vfs,
sizeof(struct rvu_pfvf), GFP_KERNEL);
if (!rvu->hwvf) {
dev_err(rvu->dev,
"%s: Failed to allocate memory for VF's rvu_pfvf struct\n", __func__);
return -ENOMEM;
}
mutex_init(&rvu->rsrc_lock);
rvu_fwdata_init(rvu);
err = rvu_setup_msix_resources(rvu);
if (err) {
dev_err(rvu->dev,
"%s: Failed to setup MSIX resources\n", __func__);
return err;
}
for (blkid = 0; blkid < BLK_COUNT; blkid++) {
block = &hw->block[blkid];
if (!block->lf.bmap)
continue;
/* Allocate memory for block LF/slot to pcifunc mapping info */
block->fn_map = devm_kcalloc(rvu->dev, block->lf.max,
sizeof(u16), GFP_KERNEL);
if (!block->fn_map) {
err = -ENOMEM;
goto msix_err;
}
/* Scan all blocks to check if low level firmware has
* already provisioned any of the resources to a PF/VF.
*/
rvu_scan_block(rvu, block);
}
err = rvu_set_channels_base(rvu);
if (err)
goto msix_err;
err = rvu_npc_init(rvu);
if (err) {
dev_err(rvu->dev, "%s: Failed to initialize npc\n", __func__);
goto npc_err;
}
err = rvu_cgx_init(rvu);
if (err) {
dev_err(rvu->dev, "%s: Failed to initialize cgx\n", __func__);
goto cgx_err;
}
/* Assign MACs for CGX mapped functions */
rvu_setup_pfvf_macaddress(rvu);
err = rvu_npa_init(rvu);
if (err) {
dev_err(rvu->dev, "%s: Failed to initialize npa\n", __func__);
goto npa_err;
}
rvu_get_lbk_bufsize(rvu);
err = rvu_nix_init(rvu);
if (err) {
dev_err(rvu->dev, "%s: Failed to initialize nix\n", __func__);
goto nix_err;
}
err = rvu_sdp_init(rvu);
if (err) {
dev_err(rvu->dev, "%s: Failed to initialize sdp\n", __func__);
goto nix_err;
}
rvu_program_channels(rvu);
return 0;
nix_err:
rvu_nix_freemem(rvu);
npa_err:
rvu_npa_freemem(rvu);
cgx_err:
rvu_cgx_exit(rvu);
npc_err:
rvu_npc_freemem(rvu);
rvu_fwdata_exit(rvu);
msix_err:
rvu_reset_msix(rvu);
return err;
}
/* NPA and NIX admin queue APIs */
void rvu_aq_free(struct rvu *rvu, struct admin_queue *aq)
{
if (!aq)
return;
qmem_free(rvu->dev, aq->inst);
qmem_free(rvu->dev, aq->res);
devm_kfree(rvu->dev, aq);
}
int rvu_aq_alloc(struct rvu *rvu, struct admin_queue **ad_queue,
int qsize, int inst_size, int res_size)
{
struct admin_queue *aq;
int err;
*ad_queue = devm_kzalloc(rvu->dev, sizeof(*aq), GFP_KERNEL);
if (!*ad_queue)
return -ENOMEM;
aq = *ad_queue;
/* Alloc memory for instructions i.e AQ */
err = qmem_alloc(rvu->dev, &aq->inst, qsize, inst_size);
if (err) {
devm_kfree(rvu->dev, aq);
return err;
}
/* Alloc memory for results */
err = qmem_alloc(rvu->dev, &aq->res, qsize, res_size);
if (err) {
rvu_aq_free(rvu, aq);
return err;
}
spin_lock_init(&aq->lock);
return 0;
}
int rvu_mbox_handler_ready(struct rvu *rvu, struct msg_req *req,
struct ready_msg_rsp *rsp)
{
if (rvu->fwdata) {
rsp->rclk_freq = rvu->fwdata->rclk;
rsp->sclk_freq = rvu->fwdata->sclk;
}
return 0;
}
/* Get current count of a RVU block's LF/slots
* provisioned to a given RVU func.
*/
u16 rvu_get_rsrc_mapcount(struct rvu_pfvf *pfvf, int blkaddr)
{
switch (blkaddr) {
case BLKADDR_NPA:
return pfvf->npalf ? 1 : 0;
case BLKADDR_NIX0:
case BLKADDR_NIX1:
return pfvf->nixlf ? 1 : 0;
case BLKADDR_SSO:
return pfvf->sso;
case BLKADDR_SSOW:
return pfvf->ssow;
case BLKADDR_TIM:
return pfvf->timlfs;
case BLKADDR_CPT0:
return pfvf->cptlfs;
case BLKADDR_CPT1:
return pfvf->cpt1_lfs;
}
return 0;
}
/* Return true if LFs of block type are attached to pcifunc */
static bool is_blktype_attached(struct rvu_pfvf *pfvf, int blktype)
{
switch (blktype) {
case BLKTYPE_NPA:
return pfvf->npalf ? 1 : 0;
case BLKTYPE_NIX:
return pfvf->nixlf ? 1 : 0;
case BLKTYPE_SSO:
return !!pfvf->sso;
case BLKTYPE_SSOW:
return !!pfvf->ssow;
case BLKTYPE_TIM:
return !!pfvf->timlfs;
case BLKTYPE_CPT:
return pfvf->cptlfs || pfvf->cpt1_lfs;
}
return false;
}
bool is_pffunc_map_valid(struct rvu *rvu, u16 pcifunc, int blktype)
{
struct rvu_pfvf *pfvf;
if (!is_pf_func_valid(rvu, pcifunc))
return false;
pfvf = rvu_get_pfvf(rvu, pcifunc);
/* Check if this PFFUNC has a LF of type blktype attached */
if (!is_blktype_attached(pfvf, blktype))
return false;
return true;
}
static int rvu_lookup_rsrc(struct rvu *rvu, struct rvu_block *block,
int pcifunc, int slot)
{
u64 val;
val = ((u64)pcifunc << 24) | (slot << 16) | (1ULL << 13);
rvu_write64(rvu, block->addr, block->lookup_reg, val);
/* Wait for the lookup to finish */
/* TODO: put some timeout here */
while (rvu_read64(rvu, block->addr, block->lookup_reg) & (1ULL << 13))
;
val = rvu_read64(rvu, block->addr, block->lookup_reg);
/* Check LF valid bit */
if (!(val & (1ULL << 12)))
return -1;
return (val & 0xFFF);
}
int rvu_get_blkaddr_from_slot(struct rvu *rvu, int blktype, u16 pcifunc,
u16 global_slot, u16 *slot_in_block)
{
struct rvu_pfvf *pfvf = rvu_get_pfvf(rvu, pcifunc);
int numlfs, total_lfs = 0, nr_blocks = 0;
int i, num_blkaddr[BLK_COUNT] = { 0 };
struct rvu_block *block;
int blkaddr;
u16 start_slot;
if (!is_blktype_attached(pfvf, blktype))
return -ENODEV;
/* Get all the block addresses from which LFs are attached to
* the given pcifunc in num_blkaddr[].
*/
for (blkaddr = BLKADDR_RVUM; blkaddr < BLK_COUNT; blkaddr++) {
block = &rvu->hw->block[blkaddr];
if (block->type != blktype)
continue;
if (!is_block_implemented(rvu->hw, blkaddr))
continue;
numlfs = rvu_get_rsrc_mapcount(pfvf, blkaddr);
if (numlfs) {
total_lfs += numlfs;
num_blkaddr[nr_blocks] = blkaddr;
nr_blocks++;
}
}
if (global_slot >= total_lfs)
return -ENODEV;
/* Based on the given global slot number retrieve the
* correct block address out of all attached block
* addresses and slot number in that block.
*/
total_lfs = 0;
blkaddr = -ENODEV;
for (i = 0; i < nr_blocks; i++) {
numlfs = rvu_get_rsrc_mapcount(pfvf, num_blkaddr[i]);
total_lfs += numlfs;
if (global_slot < total_lfs) {
blkaddr = num_blkaddr[i];
start_slot = total_lfs - numlfs;
*slot_in_block = global_slot - start_slot;
break;
}
}
return blkaddr;
}
static void rvu_detach_block(struct rvu *rvu, int pcifunc, int blktype)
{
struct rvu_pfvf *pfvf = rvu_get_pfvf(rvu, pcifunc);
struct rvu_hwinfo *hw = rvu->hw;
struct rvu_block *block;
int slot, lf, num_lfs;
int blkaddr;
blkaddr = rvu_get_blkaddr(rvu, blktype, pcifunc);
if (blkaddr < 0)
return;
if (blktype == BLKTYPE_NIX)
rvu_nix_reset_mac(pfvf, pcifunc);
block = &hw->block[blkaddr];
num_lfs = rvu_get_rsrc_mapcount(pfvf, block->addr);
if (!num_lfs)
return;
for (slot = 0; slot < num_lfs; slot++) {
lf = rvu_lookup_rsrc(rvu, block, pcifunc, slot);
if (lf < 0) /* This should never happen */
continue;
/* Disable the LF */
rvu_write64(rvu, blkaddr, block->lfcfg_reg |
(lf << block->lfshift), 0x00ULL);
/* Update SW maintained mapping info as well */
rvu_update_rsrc_map(rvu, pfvf, block,
pcifunc, lf, false);
/* Free the resource */
rvu_free_rsrc(&block->lf, lf);
/* Clear MSIX vector offset for this LF */
rvu_clear_msix_offset(rvu, pfvf, block, lf);
}
}
static int rvu_detach_rsrcs(struct rvu *rvu, struct rsrc_detach *detach,
u16 pcifunc)
{
struct rvu_hwinfo *hw = rvu->hw;
bool detach_all = true;
struct rvu_block *block;
int blkid;
mutex_lock(&rvu->rsrc_lock);
/* Check for partial resource detach */
if (detach && detach->partial)
detach_all = false;
/* Check for RVU block's LFs attached to this func,
* if so, detach them.
*/
for (blkid = 0; blkid < BLK_COUNT; blkid++) {
block = &hw->block[blkid];
if (!block->lf.bmap)
continue;
if (!detach_all && detach) {
if (blkid == BLKADDR_NPA && !detach->npalf)
continue;
else if ((blkid == BLKADDR_NIX0) && !detach->nixlf)
continue;
else if ((blkid == BLKADDR_NIX1) && !detach->nixlf)
continue;
else if ((blkid == BLKADDR_SSO) && !detach->sso)
continue;
else if ((blkid == BLKADDR_SSOW) && !detach->ssow)
continue;
else if ((blkid == BLKADDR_TIM) && !detach->timlfs)
continue;
else if ((blkid == BLKADDR_CPT0) && !detach->cptlfs)
continue;
else if ((blkid == BLKADDR_CPT1) && !detach->cptlfs)
continue;
}
rvu_detach_block(rvu, pcifunc, block->type);
}
mutex_unlock(&rvu->rsrc_lock);
return 0;
}
int rvu_mbox_handler_detach_resources(struct rvu *rvu,
struct rsrc_detach *detach,
struct msg_rsp *rsp)
{
return rvu_detach_rsrcs(rvu, detach, detach->hdr.pcifunc);
}
int rvu_get_nix_blkaddr(struct rvu *rvu, u16 pcifunc)
{
struct rvu_pfvf *pfvf = rvu_get_pfvf(rvu, pcifunc);
int blkaddr = BLKADDR_NIX0, vf;
struct rvu_pfvf *pf;
pf = rvu_get_pfvf(rvu, pcifunc & ~RVU_PFVF_FUNC_MASK);
/* All CGX mapped PFs are set with assigned NIX block during init */
if (is_pf_cgxmapped(rvu, rvu_get_pf(pcifunc))) {
blkaddr = pf->nix_blkaddr;
} else if (is_afvf(pcifunc)) {
vf = pcifunc - 1;
/* Assign NIX based on VF number. All even numbered VFs get
* NIX0 and odd numbered gets NIX1
*/
blkaddr = (vf & 1) ? BLKADDR_NIX1 : BLKADDR_NIX0;
/* NIX1 is not present on all silicons */
if (!is_block_implemented(rvu->hw, BLKADDR_NIX1))
blkaddr = BLKADDR_NIX0;
}
/* if SDP1 then the blkaddr is NIX1 */
if (is_sdp_pfvf(pcifunc) && pf->sdp_info->node_id == 1)
blkaddr = BLKADDR_NIX1;
switch (blkaddr) {
case BLKADDR_NIX1:
pfvf->nix_blkaddr = BLKADDR_NIX1;
pfvf->nix_rx_intf = NIX_INTFX_RX(1);
pfvf->nix_tx_intf = NIX_INTFX_TX(1);
break;
case BLKADDR_NIX0:
default:
pfvf->nix_blkaddr = BLKADDR_NIX0;
pfvf->nix_rx_intf = NIX_INTFX_RX(0);
pfvf->nix_tx_intf = NIX_INTFX_TX(0);
break;
}
return pfvf->nix_blkaddr;
}
static int rvu_get_attach_blkaddr(struct rvu *rvu, int blktype,
u16 pcifunc, struct rsrc_attach *attach)
{
int blkaddr;
switch (blktype) {
case BLKTYPE_NIX:
blkaddr = rvu_get_nix_blkaddr(rvu, pcifunc);
break;
case BLKTYPE_CPT:
if (attach->hdr.ver < RVU_MULTI_BLK_VER)
return rvu_get_blkaddr(rvu, blktype, 0);
blkaddr = attach->cpt_blkaddr ? attach->cpt_blkaddr :
BLKADDR_CPT0;
if (blkaddr != BLKADDR_CPT0 && blkaddr != BLKADDR_CPT1)
return -ENODEV;
break;
default:
return rvu_get_blkaddr(rvu, blktype, 0);
}
if (is_block_implemented(rvu->hw, blkaddr))
return blkaddr;
return -ENODEV;
}
static void rvu_attach_block(struct rvu *rvu, int pcifunc, int blktype,
int num_lfs, struct rsrc_attach *attach)
{
struct rvu_pfvf *pfvf = rvu_get_pfvf(rvu, pcifunc);
struct rvu_hwinfo *hw = rvu->hw;
struct rvu_block *block;
int slot, lf;
int blkaddr;
u64 cfg;
if (!num_lfs)
return;
blkaddr = rvu_get_attach_blkaddr(rvu, blktype, pcifunc, attach);
if (blkaddr < 0)
return;
block = &hw->block[blkaddr];
if (!block->lf.bmap)
return;
for (slot = 0; slot < num_lfs; slot++) {
/* Allocate the resource */
lf = rvu_alloc_rsrc(&block->lf);
if (lf < 0)
return;
cfg = (1ULL << 63) | (pcifunc << 8) | slot;
rvu_write64(rvu, blkaddr, block->lfcfg_reg |
(lf << block->lfshift), cfg);
rvu_update_rsrc_map(rvu, pfvf, block,
pcifunc, lf, true);
/* Set start MSIX vector for this LF within this PF/VF */
rvu_set_msix_offset(rvu, pfvf, block, lf);
}
}
static int rvu_check_rsrc_availability(struct rvu *rvu,
struct rsrc_attach *req, u16 pcifunc)
{
struct rvu_pfvf *pfvf = rvu_get_pfvf(rvu, pcifunc);
int free_lfs, mappedlfs, blkaddr;
struct rvu_hwinfo *hw = rvu->hw;
struct rvu_block *block;
/* Only one NPA LF can be attached */
if (req->npalf && !is_blktype_attached(pfvf, BLKTYPE_NPA)) {
block = &hw->block[BLKADDR_NPA];
free_lfs = rvu_rsrc_free_count(&block->lf);
if (!free_lfs)
goto fail;
} else if (req->npalf) {
dev_err(&rvu->pdev->dev,
"Func 0x%x: Invalid req, already has NPA\n",
pcifunc);
return -EINVAL;
}
/* Only one NIX LF can be attached */
if (req->nixlf && !is_blktype_attached(pfvf, BLKTYPE_NIX)) {
blkaddr = rvu_get_attach_blkaddr(rvu, BLKTYPE_NIX,
pcifunc, req);
if (blkaddr < 0)
return blkaddr;
block = &hw->block[blkaddr];
free_lfs = rvu_rsrc_free_count(&block->lf);
if (!free_lfs)
goto fail;
} else if (req->nixlf) {
dev_err(&rvu->pdev->dev,
"Func 0x%x: Invalid req, already has NIX\n",
pcifunc);
return -EINVAL;
}
if (req->sso) {
block = &hw->block[BLKADDR_SSO];
/* Is request within limits ? */
if (req->sso > block->lf.max) {
dev_err(&rvu->pdev->dev,
"Func 0x%x: Invalid SSO req, %d > max %d\n",
pcifunc, req->sso, block->lf.max);
return -EINVAL;
}
mappedlfs = rvu_get_rsrc_mapcount(pfvf, block->addr);
free_lfs = rvu_rsrc_free_count(&block->lf);
/* Check if additional resources are available */
if (req->sso > mappedlfs &&
((req->sso - mappedlfs) > free_lfs))
goto fail;
}
if (req->ssow) {
block = &hw->block[BLKADDR_SSOW];
if (req->ssow > block->lf.max) {
dev_err(&rvu->pdev->dev,
"Func 0x%x: Invalid SSOW req, %d > max %d\n",
pcifunc, req->sso, block->lf.max);
return -EINVAL;
}
mappedlfs = rvu_get_rsrc_mapcount(pfvf, block->addr);
free_lfs = rvu_rsrc_free_count(&block->lf);
if (req->ssow > mappedlfs &&
((req->ssow - mappedlfs) > free_lfs))
goto fail;
}
if (req->timlfs) {
block = &hw->block[BLKADDR_TIM];
if (req->timlfs > block->lf.max) {
dev_err(&rvu->pdev->dev,
"Func 0x%x: Invalid TIMLF req, %d > max %d\n",
pcifunc, req->timlfs, block->lf.max);
return -EINVAL;
}
mappedlfs = rvu_get_rsrc_mapcount(pfvf, block->addr);
free_lfs = rvu_rsrc_free_count(&block->lf);
if (req->timlfs > mappedlfs &&
((req->timlfs - mappedlfs) > free_lfs))
goto fail;
}
if (req->cptlfs) {
blkaddr = rvu_get_attach_blkaddr(rvu, BLKTYPE_CPT,
pcifunc, req);
if (blkaddr < 0)
return blkaddr;
block = &hw->block[blkaddr];
if (req->cptlfs > block->lf.max) {
dev_err(&rvu->pdev->dev,
"Func 0x%x: Invalid CPTLF req, %d > max %d\n",
pcifunc, req->cptlfs, block->lf.max);
return -EINVAL;
}
mappedlfs = rvu_get_rsrc_mapcount(pfvf, block->addr);
free_lfs = rvu_rsrc_free_count(&block->lf);
if (req->cptlfs > mappedlfs &&
((req->cptlfs - mappedlfs) > free_lfs))
goto fail;
}
return 0;
fail:
dev_info(rvu->dev, "Request for %s failed\n", block->name);
return -ENOSPC;
}
static bool rvu_attach_from_same_block(struct rvu *rvu, int blktype,
struct rsrc_attach *attach)
{
int blkaddr, num_lfs;
blkaddr = rvu_get_attach_blkaddr(rvu, blktype,
attach->hdr.pcifunc, attach);
if (blkaddr < 0)
return false;
num_lfs = rvu_get_rsrc_mapcount(rvu_get_pfvf(rvu, attach->hdr.pcifunc),
blkaddr);
/* Requester already has LFs from given block ? */
return !!num_lfs;
}
int rvu_mbox_handler_attach_resources(struct rvu *rvu,
struct rsrc_attach *attach,
struct msg_rsp *rsp)
{
u16 pcifunc = attach->hdr.pcifunc;
int err;
/* If first request, detach all existing attached resources */
if (!attach->modify)
rvu_detach_rsrcs(rvu, NULL, pcifunc);
mutex_lock(&rvu->rsrc_lock);
/* Check if the request can be accommodated */
err = rvu_check_rsrc_availability(rvu, attach, pcifunc);
if (err)
goto exit;
/* Now attach the requested resources */
if (attach->npalf)
rvu_attach_block(rvu, pcifunc, BLKTYPE_NPA, 1, attach);
if (attach->nixlf)
rvu_attach_block(rvu, pcifunc, BLKTYPE_NIX, 1, attach);
if (attach->sso) {
/* RVU func doesn't know which exact LF or slot is attached
* to it, it always sees as slot 0,1,2. So for a 'modify'
* request, simply detach all existing attached LFs/slots
* and attach a fresh.
*/
if (attach->modify)
rvu_detach_block(rvu, pcifunc, BLKTYPE_SSO);
rvu_attach_block(rvu, pcifunc, BLKTYPE_SSO,
attach->sso, attach);
}
if (attach->ssow) {
if (attach->modify)
rvu_detach_block(rvu, pcifunc, BLKTYPE_SSOW);
rvu_attach_block(rvu, pcifunc, BLKTYPE_SSOW,
attach->ssow, attach);
}
if (attach->timlfs) {
if (attach->modify)
rvu_detach_block(rvu, pcifunc, BLKTYPE_TIM);
rvu_attach_block(rvu, pcifunc, BLKTYPE_TIM,
attach->timlfs, attach);
}
if (attach->cptlfs) {
if (attach->modify &&
rvu_attach_from_same_block(rvu, BLKTYPE_CPT, attach))
rvu_detach_block(rvu, pcifunc, BLKTYPE_CPT);
rvu_attach_block(rvu, pcifunc, BLKTYPE_CPT,
attach->cptlfs, attach);
}
exit:
mutex_unlock(&rvu->rsrc_lock);
return err;
}
static u16 rvu_get_msix_offset(struct rvu *rvu, struct rvu_pfvf *pfvf,
int blkaddr, int lf)
{
u16 vec;
if (lf < 0)
return MSIX_VECTOR_INVALID;
for (vec = 0; vec < pfvf->msix.max; vec++) {
if (pfvf->msix_lfmap[vec] == MSIX_BLKLF(blkaddr, lf))
return vec;
}
return MSIX_VECTOR_INVALID;
}
static void rvu_set_msix_offset(struct rvu *rvu, struct rvu_pfvf *pfvf,
struct rvu_block *block, int lf)
{
u16 nvecs, vec, offset;
u64 cfg;
cfg = rvu_read64(rvu, block->addr, block->msixcfg_reg |
(lf << block->lfshift));
nvecs = (cfg >> 12) & 0xFF;
/* Check and alloc MSIX vectors, must be contiguous */
if (!rvu_rsrc_check_contig(&pfvf->msix, nvecs))
return;
offset = rvu_alloc_rsrc_contig(&pfvf->msix, nvecs);
/* Config MSIX offset in LF */
rvu_write64(rvu, block->addr, block->msixcfg_reg |
(lf << block->lfshift), (cfg & ~0x7FFULL) | offset);
/* Update the bitmap as well */
for (vec = 0; vec < nvecs; vec++)
pfvf->msix_lfmap[offset + vec] = MSIX_BLKLF(block->addr, lf);
}
static void rvu_clear_msix_offset(struct rvu *rvu, struct rvu_pfvf *pfvf,
struct rvu_block *block, int lf)
{
u16 nvecs, vec, offset;
u64 cfg;
cfg = rvu_read64(rvu, block->addr, block->msixcfg_reg |
(lf << block->lfshift));
nvecs = (cfg >> 12) & 0xFF;
/* Clear MSIX offset in LF */
rvu_write64(rvu, block->addr, block->msixcfg_reg |
(lf << block->lfshift), cfg & ~0x7FFULL);
offset = rvu_get_msix_offset(rvu, pfvf, block->addr, lf);
/* Update the mapping */
for (vec = 0; vec < nvecs; vec++)
pfvf->msix_lfmap[offset + vec] = 0;
/* Free the same in MSIX bitmap */
rvu_free_rsrc_contig(&pfvf->msix, nvecs, offset);
}
int rvu_mbox_handler_msix_offset(struct rvu *rvu, struct msg_req *req,
struct msix_offset_rsp *rsp)
{
struct rvu_hwinfo *hw = rvu->hw;
u16 pcifunc = req->hdr.pcifunc;
struct rvu_pfvf *pfvf;
int lf, slot, blkaddr;
pfvf = rvu_get_pfvf(rvu, pcifunc);
if (!pfvf->msix.bmap)
return 0;
/* Set MSIX offsets for each block's LFs attached to this PF/VF */
lf = rvu_get_lf(rvu, &hw->block[BLKADDR_NPA], pcifunc, 0);
rsp->npa_msixoff = rvu_get_msix_offset(rvu, pfvf, BLKADDR_NPA, lf);
/* Get BLKADDR from which LFs are attached to pcifunc */
blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NIX, pcifunc);
if (blkaddr < 0) {
rsp->nix_msixoff = MSIX_VECTOR_INVALID;
} else {
lf = rvu_get_lf(rvu, &hw->block[blkaddr], pcifunc, 0);
rsp->nix_msixoff = rvu_get_msix_offset(rvu, pfvf, blkaddr, lf);
}
rsp->sso = pfvf->sso;
for (slot = 0; slot < rsp->sso; slot++) {
lf = rvu_get_lf(rvu, &hw->block[BLKADDR_SSO], pcifunc, slot);
rsp->sso_msixoff[slot] =
rvu_get_msix_offset(rvu, pfvf, BLKADDR_SSO, lf);
}
rsp->ssow = pfvf->ssow;
for (slot = 0; slot < rsp->ssow; slot++) {
lf = rvu_get_lf(rvu, &hw->block[BLKADDR_SSOW], pcifunc, slot);
rsp->ssow_msixoff[slot] =
rvu_get_msix_offset(rvu, pfvf, BLKADDR_SSOW, lf);
}
rsp->timlfs = pfvf->timlfs;
for (slot = 0; slot < rsp->timlfs; slot++) {
lf = rvu_get_lf(rvu, &hw->block[BLKADDR_TIM], pcifunc, slot);
rsp->timlf_msixoff[slot] =
rvu_get_msix_offset(rvu, pfvf, BLKADDR_TIM, lf);
}
rsp->cptlfs = pfvf->cptlfs;
for (slot = 0; slot < rsp->cptlfs; slot++) {
lf = rvu_get_lf(rvu, &hw->block[BLKADDR_CPT0], pcifunc, slot);
rsp->cptlf_msixoff[slot] =
rvu_get_msix_offset(rvu, pfvf, BLKADDR_CPT0, lf);
}
rsp->cpt1_lfs = pfvf->cpt1_lfs;
for (slot = 0; slot < rsp->cpt1_lfs; slot++) {
lf = rvu_get_lf(rvu, &hw->block[BLKADDR_CPT1], pcifunc, slot);
rsp->cpt1_lf_msixoff[slot] =
rvu_get_msix_offset(rvu, pfvf, BLKADDR_CPT1, lf);
}
return 0;
}
int rvu_mbox_handler_free_rsrc_cnt(struct rvu *rvu, struct msg_req *req,
struct free_rsrcs_rsp *rsp)
{
struct rvu_hwinfo *hw = rvu->hw;
struct rvu_block *block;
struct nix_txsch *txsch;
struct nix_hw *nix_hw;
mutex_lock(&rvu->rsrc_lock);
block = &hw->block[BLKADDR_NPA];
rsp->npa = rvu_rsrc_free_count(&block->lf);
block = &hw->block[BLKADDR_NIX0];
rsp->nix = rvu_rsrc_free_count(&block->lf);
block = &hw->block[BLKADDR_NIX1];
rsp->nix1 = rvu_rsrc_free_count(&block->lf);
block = &hw->block[BLKADDR_SSO];
rsp->sso = rvu_rsrc_free_count(&block->lf);
block = &hw->block[BLKADDR_SSOW];
rsp->ssow = rvu_rsrc_free_count(&block->lf);
block = &hw->block[BLKADDR_TIM];
rsp->tim = rvu_rsrc_free_count(&block->lf);
block = &hw->block[BLKADDR_CPT0];
rsp->cpt = rvu_rsrc_free_count(&block->lf);
block = &hw->block[BLKADDR_CPT1];
rsp->cpt1 = rvu_rsrc_free_count(&block->lf);
if (rvu->hw->cap.nix_fixed_txschq_mapping) {
rsp->schq[NIX_TXSCH_LVL_SMQ] = 1;
rsp->schq[NIX_TXSCH_LVL_TL4] = 1;
rsp->schq[NIX_TXSCH_LVL_TL3] = 1;
rsp->schq[NIX_TXSCH_LVL_TL2] = 1;
/* NIX1 */
if (!is_block_implemented(rvu->hw, BLKADDR_NIX1))
goto out;
rsp->schq_nix1[NIX_TXSCH_LVL_SMQ] = 1;
rsp->schq_nix1[NIX_TXSCH_LVL_TL4] = 1;
rsp->schq_nix1[NIX_TXSCH_LVL_TL3] = 1;
rsp->schq_nix1[NIX_TXSCH_LVL_TL2] = 1;
} else {
nix_hw = get_nix_hw(hw, BLKADDR_NIX0);
txsch = &nix_hw->txsch[NIX_TXSCH_LVL_SMQ];
rsp->schq[NIX_TXSCH_LVL_SMQ] =
rvu_rsrc_free_count(&txsch->schq);
txsch = &nix_hw->txsch[NIX_TXSCH_LVL_TL4];
rsp->schq[NIX_TXSCH_LVL_TL4] =
rvu_rsrc_free_count(&txsch->schq);
txsch = &nix_hw->txsch[NIX_TXSCH_LVL_TL3];
rsp->schq[NIX_TXSCH_LVL_TL3] =
rvu_rsrc_free_count(&txsch->schq);
txsch = &nix_hw->txsch[NIX_TXSCH_LVL_TL2];
rsp->schq[NIX_TXSCH_LVL_TL2] =
rvu_rsrc_free_count(&txsch->schq);
if (!is_block_implemented(rvu->hw, BLKADDR_NIX1))
goto out;
nix_hw = get_nix_hw(hw, BLKADDR_NIX1);
txsch = &nix_hw->txsch[NIX_TXSCH_LVL_SMQ];
rsp->schq_nix1[NIX_TXSCH_LVL_SMQ] =
rvu_rsrc_free_count(&txsch->schq);
txsch = &nix_hw->txsch[NIX_TXSCH_LVL_TL4];
rsp->schq_nix1[NIX_TXSCH_LVL_TL4] =
rvu_rsrc_free_count(&txsch->schq);
txsch = &nix_hw->txsch[NIX_TXSCH_LVL_TL3];
rsp->schq_nix1[NIX_TXSCH_LVL_TL3] =
rvu_rsrc_free_count(&txsch->schq);
txsch = &nix_hw->txsch[NIX_TXSCH_LVL_TL2];
rsp->schq_nix1[NIX_TXSCH_LVL_TL2] =
rvu_rsrc_free_count(&txsch->schq);
}
rsp->schq_nix1[NIX_TXSCH_LVL_TL1] = 1;
out:
rsp->schq[NIX_TXSCH_LVL_TL1] = 1;
mutex_unlock(&rvu->rsrc_lock);
return 0;
}
int rvu_mbox_handler_vf_flr(struct rvu *rvu, struct msg_req *req,
struct msg_rsp *rsp)
{
u16 pcifunc = req->hdr.pcifunc;
u16 vf, numvfs;
u64 cfg;
vf = pcifunc & RVU_PFVF_FUNC_MASK;
cfg = rvu_read64(rvu, BLKADDR_RVUM,
RVU_PRIV_PFX_CFG(rvu_get_pf(pcifunc)));
numvfs = (cfg >> 12) & 0xFF;
if (vf && vf <= numvfs)
__rvu_flr_handler(rvu, pcifunc);
else
return RVU_INVALID_VF_ID;
return 0;
}
int rvu_mbox_handler_get_hw_cap(struct rvu *rvu, struct msg_req *req,
struct get_hw_cap_rsp *rsp)
{
struct rvu_hwinfo *hw = rvu->hw;
rsp->nix_fixed_txschq_mapping = hw->cap.nix_fixed_txschq_mapping;
rsp->nix_shaping = hw->cap.nix_shaping;
return 0;
}
int rvu_mbox_handler_set_vf_perm(struct rvu *rvu, struct set_vf_perm *req,
struct msg_rsp *rsp)
{
struct rvu_hwinfo *hw = rvu->hw;
u16 pcifunc = req->hdr.pcifunc;
struct rvu_pfvf *pfvf;
int blkaddr, nixlf;
u16 target;
/* Only PF can add VF permissions */
if ((pcifunc & RVU_PFVF_FUNC_MASK) || is_afvf(pcifunc))
return -EOPNOTSUPP;
target = (pcifunc & ~RVU_PFVF_FUNC_MASK) | (req->vf + 1);
pfvf = rvu_get_pfvf(rvu, target);
if (req->flags & RESET_VF_PERM) {
pfvf->flags &= RVU_CLEAR_VF_PERM;
} else if (test_bit(PF_SET_VF_TRUSTED, &pfvf->flags) ^
(req->flags & VF_TRUSTED)) {
change_bit(PF_SET_VF_TRUSTED, &pfvf->flags);
/* disable multicast and promisc entries */
if (!test_bit(PF_SET_VF_TRUSTED, &pfvf->flags)) {
blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NIX, target);
if (blkaddr < 0)
return 0;
nixlf = rvu_get_lf(rvu, &hw->block[blkaddr],
target, 0);
if (nixlf < 0)
return 0;
npc_enadis_default_mce_entry(rvu, target, nixlf,
NIXLF_ALLMULTI_ENTRY,
false);
npc_enadis_default_mce_entry(rvu, target, nixlf,
NIXLF_PROMISC_ENTRY,
false);
}
}
return 0;
}
static int rvu_process_mbox_msg(struct otx2_mbox *mbox, int devid,
struct mbox_msghdr *req)
{
struct rvu *rvu = pci_get_drvdata(mbox->pdev);
/* Check if valid, if not reply with a invalid msg */
if (req->sig != OTX2_MBOX_REQ_SIG)
goto bad_message;
switch (req->id) {
#define M(_name, _id, _fn_name, _req_type, _rsp_type) \
case _id: { \
struct _rsp_type *rsp; \
int err; \
\
rsp = (struct _rsp_type *)otx2_mbox_alloc_msg( \
mbox, devid, \
sizeof(struct _rsp_type)); \
/* some handlers should complete even if reply */ \
/* could not be allocated */ \
if (!rsp && \
_id != MBOX_MSG_DETACH_RESOURCES && \
_id != MBOX_MSG_NIX_TXSCH_FREE && \
_id != MBOX_MSG_VF_FLR) \
return -ENOMEM; \
if (rsp) { \
rsp->hdr.id = _id; \
rsp->hdr.sig = OTX2_MBOX_RSP_SIG; \
rsp->hdr.pcifunc = req->pcifunc; \
rsp->hdr.rc = 0; \
} \
\
err = rvu_mbox_handler_ ## _fn_name(rvu, \
(struct _req_type *)req, \
rsp); \
if (rsp && err) \
rsp->hdr.rc = err; \
\
trace_otx2_msg_process(mbox->pdev, _id, err); \
return rsp ? err : -ENOMEM; \
}
MBOX_MESSAGES
#undef M
bad_message:
default:
otx2_reply_invalid_msg(mbox, devid, req->pcifunc, req->id);
return -ENODEV;
}
}
static void __rvu_mbox_handler(struct rvu_work *mwork, int type)
{
struct rvu *rvu = mwork->rvu;
int offset, err, id, devid;
struct otx2_mbox_dev *mdev;
struct mbox_hdr *req_hdr;
struct mbox_msghdr *msg;
struct mbox_wq_info *mw;
struct otx2_mbox *mbox;
switch (type) {
case TYPE_AFPF:
mw = &rvu->afpf_wq_info;
break;
case TYPE_AFVF:
mw = &rvu->afvf_wq_info;
break;
default:
return;
}
devid = mwork - mw->mbox_wrk;
mbox = &mw->mbox;
mdev = &mbox->dev[devid];
/* Process received mbox messages */
req_hdr = mdev->mbase + mbox->rx_start;
if (mw->mbox_wrk[devid].num_msgs == 0)
return;
offset = mbox->rx_start + ALIGN(sizeof(*req_hdr), MBOX_MSG_ALIGN);
for (id = 0; id < mw->mbox_wrk[devid].num_msgs; id++) {
msg = mdev->mbase + offset;
/* Set which PF/VF sent this message based on mbox IRQ */
switch (type) {
case TYPE_AFPF:
msg->pcifunc &=
~(RVU_PFVF_PF_MASK << RVU_PFVF_PF_SHIFT);
msg->pcifunc |= (devid << RVU_PFVF_PF_SHIFT);
break;
case TYPE_AFVF:
msg->pcifunc &=
~(RVU_PFVF_FUNC_MASK << RVU_PFVF_FUNC_SHIFT);
msg->pcifunc |= (devid << RVU_PFVF_FUNC_SHIFT) + 1;
break;
}
err = rvu_process_mbox_msg(mbox, devid, msg);
if (!err) {
offset = mbox->rx_start + msg->next_msgoff;
continue;
}
if (msg->pcifunc & RVU_PFVF_FUNC_MASK)
dev_warn(rvu->dev, "Error %d when processing message %s (0x%x) from PF%d:VF%d\n",
err, otx2_mbox_id2name(msg->id),
msg->id, rvu_get_pf(msg->pcifunc),
(msg->pcifunc & RVU_PFVF_FUNC_MASK) - 1);
else
dev_warn(rvu->dev, "Error %d when processing message %s (0x%x) from PF%d\n",
err, otx2_mbox_id2name(msg->id),
msg->id, devid);
}
mw->mbox_wrk[devid].num_msgs = 0;
/* Send mbox responses to VF/PF */
otx2_mbox_msg_send(mbox, devid);
}
static inline void rvu_afpf_mbox_handler(struct work_struct *work)
{
struct rvu_work *mwork = container_of(work, struct rvu_work, work);
__rvu_mbox_handler(mwork, TYPE_AFPF);
}
static inline void rvu_afvf_mbox_handler(struct work_struct *work)
{
struct rvu_work *mwork = container_of(work, struct rvu_work, work);
__rvu_mbox_handler(mwork, TYPE_AFVF);
}
static void __rvu_mbox_up_handler(struct rvu_work *mwork, int type)
{
struct rvu *rvu = mwork->rvu;
struct otx2_mbox_dev *mdev;
struct mbox_hdr *rsp_hdr;
struct mbox_msghdr *msg;
struct mbox_wq_info *mw;
struct otx2_mbox *mbox;
int offset, id, devid;
switch (type) {
case TYPE_AFPF:
mw = &rvu->afpf_wq_info;
break;
case TYPE_AFVF:
mw = &rvu->afvf_wq_info;
break;
default:
return;
}
devid = mwork - mw->mbox_wrk_up;
mbox = &mw->mbox_up;
mdev = &mbox->dev[devid];
rsp_hdr = mdev->mbase + mbox->rx_start;
if (mw->mbox_wrk_up[devid].up_num_msgs == 0) {
dev_warn(rvu->dev, "mbox up handler: num_msgs = 0\n");
return;
}
offset = mbox->rx_start + ALIGN(sizeof(*rsp_hdr), MBOX_MSG_ALIGN);
for (id = 0; id < mw->mbox_wrk_up[devid].up_num_msgs; id++) {
msg = mdev->mbase + offset;
if (msg->id >= MBOX_MSG_MAX) {
dev_err(rvu->dev,
"Mbox msg with unknown ID 0x%x\n", msg->id);
goto end;
}
if (msg->sig != OTX2_MBOX_RSP_SIG) {
dev_err(rvu->dev,
"Mbox msg with wrong signature %x, ID 0x%x\n",
msg->sig, msg->id);
goto end;
}
switch (msg->id) {
case MBOX_MSG_CGX_LINK_EVENT:
break;
default:
if (msg->rc)
dev_err(rvu->dev,
"Mbox msg response has err %d, ID 0x%x\n",
msg->rc, msg->id);
break;
}
end:
offset = mbox->rx_start + msg->next_msgoff;
mdev->msgs_acked++;
}
mw->mbox_wrk_up[devid].up_num_msgs = 0;
otx2_mbox_reset(mbox, devid);
}
static inline void rvu_afpf_mbox_up_handler(struct work_struct *work)
{
struct rvu_work *mwork = container_of(work, struct rvu_work, work);
__rvu_mbox_up_handler(mwork, TYPE_AFPF);
}
static inline void rvu_afvf_mbox_up_handler(struct work_struct *work)
{
struct rvu_work *mwork = container_of(work, struct rvu_work, work);
__rvu_mbox_up_handler(mwork, TYPE_AFVF);
}
static int rvu_get_mbox_regions(struct rvu *rvu, void **mbox_addr,
int num, int type)
{
struct rvu_hwinfo *hw = rvu->hw;
int region;
u64 bar4;
/* For cn10k platform VF mailbox regions of a PF follows after the
* PF <-> AF mailbox region. Whereas for Octeontx2 it is read from
* RVU_PF_VF_BAR4_ADDR register.
*/
if (type == TYPE_AFVF) {
for (region = 0; region < num; region++) {
if (hw->cap.per_pf_mbox_regs) {
bar4 = rvu_read64(rvu, BLKADDR_RVUM,
RVU_AF_PFX_BAR4_ADDR(0)) +
MBOX_SIZE;
bar4 += region * MBOX_SIZE;
} else {
bar4 = rvupf_read64(rvu, RVU_PF_VF_BAR4_ADDR);
bar4 += region * MBOX_SIZE;
}
mbox_addr[region] = (void *)ioremap_wc(bar4, MBOX_SIZE);
if (!mbox_addr[region])
goto error;
}
return 0;
}
/* For cn10k platform AF <-> PF mailbox region of a PF is read from per
* PF registers. Whereas for Octeontx2 it is read from
* RVU_AF_PF_BAR4_ADDR register.
*/
for (region = 0; region < num; region++) {
if (hw->cap.per_pf_mbox_regs) {
bar4 = rvu_read64(rvu, BLKADDR_RVUM,
RVU_AF_PFX_BAR4_ADDR(region));
} else {
bar4 = rvu_read64(rvu, BLKADDR_RVUM,
RVU_AF_PF_BAR4_ADDR);
bar4 += region * MBOX_SIZE;
}
mbox_addr[region] = (void *)ioremap_wc(bar4, MBOX_SIZE);
if (!mbox_addr[region])
goto error;
}
return 0;
error:
while (region--)
iounmap((void __iomem *)mbox_addr[region]);
return -ENOMEM;
}
static int rvu_mbox_init(struct rvu *rvu, struct mbox_wq_info *mw,
int type, int num,
void (mbox_handler)(struct work_struct *),
void (mbox_up_handler)(struct work_struct *))
{
int err = -EINVAL, i, dir, dir_up;
void __iomem *reg_base;
struct rvu_work *mwork;
void **mbox_regions;
const char *name;
mbox_regions = kcalloc(num, sizeof(void *), GFP_KERNEL);
if (!mbox_regions)
return -ENOMEM;
switch (type) {
case TYPE_AFPF:
name = "rvu_afpf_mailbox";
dir = MBOX_DIR_AFPF;
dir_up = MBOX_DIR_AFPF_UP;
reg_base = rvu->afreg_base;
err = rvu_get_mbox_regions(rvu, mbox_regions, num, TYPE_AFPF);
if (err)
goto free_regions;
break;
case TYPE_AFVF:
name = "rvu_afvf_mailbox";
dir = MBOX_DIR_PFVF;
dir_up = MBOX_DIR_PFVF_UP;
reg_base = rvu->pfreg_base;
err = rvu_get_mbox_regions(rvu, mbox_regions, num, TYPE_AFVF);
if (err)
goto free_regions;
break;
default:
goto free_regions;
}
mw->mbox_wq = alloc_workqueue(name,
WQ_UNBOUND | WQ_HIGHPRI | WQ_MEM_RECLAIM,
num);
if (!mw->mbox_wq) {
err = -ENOMEM;
goto unmap_regions;
}
mw->mbox_wrk = devm_kcalloc(rvu->dev, num,
sizeof(struct rvu_work), GFP_KERNEL);
if (!mw->mbox_wrk) {
err = -ENOMEM;
goto exit;
}
mw->mbox_wrk_up = devm_kcalloc(rvu->dev, num,
sizeof(struct rvu_work), GFP_KERNEL);
if (!mw->mbox_wrk_up) {
err = -ENOMEM;
goto exit;
}
err = otx2_mbox_regions_init(&mw->mbox, mbox_regions, rvu->pdev,
reg_base, dir, num);
if (err)
goto exit;
err = otx2_mbox_regions_init(&mw->mbox_up, mbox_regions, rvu->pdev,
reg_base, dir_up, num);
if (err)
goto exit;
for (i = 0; i < num; i++) {
mwork = &mw->mbox_wrk[i];
mwork->rvu = rvu;
INIT_WORK(&mwork->work, mbox_handler);
mwork = &mw->mbox_wrk_up[i];
mwork->rvu = rvu;
INIT_WORK(&mwork->work, mbox_up_handler);
}
kfree(mbox_regions);
return 0;
exit:
destroy_workqueue(mw->mbox_wq);
unmap_regions:
while (num--)
iounmap((void __iomem *)mbox_regions[num]);
free_regions:
kfree(mbox_regions);
return err;
}
static void rvu_mbox_destroy(struct mbox_wq_info *mw)
{
struct otx2_mbox *mbox = &mw->mbox;
struct otx2_mbox_dev *mdev;
int devid;
if (mw->mbox_wq) {
destroy_workqueue(mw->mbox_wq);
mw->mbox_wq = NULL;
}
for (devid = 0; devid < mbox->ndevs; devid++) {
mdev = &mbox->dev[devid];
if (mdev->hwbase)
iounmap((void __iomem *)mdev->hwbase);
}
otx2_mbox_destroy(&mw->mbox);
otx2_mbox_destroy(&mw->mbox_up);
}
static void rvu_queue_work(struct mbox_wq_info *mw, int first,
int mdevs, u64 intr)
{
struct otx2_mbox_dev *mdev;
struct otx2_mbox *mbox;
struct mbox_hdr *hdr;
int i;
for (i = first; i < mdevs; i++) {
/* start from 0 */
if (!(intr & BIT_ULL(i - first)))
continue;
mbox = &mw->mbox;
mdev = &mbox->dev[i];
hdr = mdev->mbase + mbox->rx_start;
/*The hdr->num_msgs is set to zero immediately in the interrupt
* handler to ensure that it holds a correct value next time
* when the interrupt handler is called.
* pf->mbox.num_msgs holds the data for use in pfaf_mbox_handler
* pf>mbox.up_num_msgs holds the data for use in
* pfaf_mbox_up_handler.
*/
if (hdr->num_msgs) {
mw->mbox_wrk[i].num_msgs = hdr->num_msgs;
hdr->num_msgs = 0;
queue_work(mw->mbox_wq, &mw->mbox_wrk[i].work);
}
mbox = &mw->mbox_up;
mdev = &mbox->dev[i];
hdr = mdev->mbase + mbox->rx_start;
if (hdr->num_msgs) {
mw->mbox_wrk_up[i].up_num_msgs = hdr->num_msgs;
hdr->num_msgs = 0;
queue_work(mw->mbox_wq, &mw->mbox_wrk_up[i].work);
}
}
}
static irqreturn_t rvu_mbox_intr_handler(int irq, void *rvu_irq)
{
struct rvu *rvu = (struct rvu *)rvu_irq;
int vfs = rvu->vfs;
u64 intr;
intr = rvu_read64(rvu, BLKADDR_RVUM, RVU_AF_PFAF_MBOX_INT);
/* Clear interrupts */
rvu_write64(rvu, BLKADDR_RVUM, RVU_AF_PFAF_MBOX_INT, intr);
if (intr)
trace_otx2_msg_interrupt(rvu->pdev, "PF(s) to AF", intr);
/* Sync with mbox memory region */
rmb();
rvu_queue_work(&rvu->afpf_wq_info, 0, rvu->hw->total_pfs, intr);
/* Handle VF interrupts */
if (vfs > 64) {
intr = rvupf_read64(rvu, RVU_PF_VFPF_MBOX_INTX(1));
rvupf_write64(rvu, RVU_PF_VFPF_MBOX_INTX(1), intr);
rvu_queue_work(&rvu->afvf_wq_info, 64, vfs, intr);
vfs -= 64;
}
intr = rvupf_read64(rvu, RVU_PF_VFPF_MBOX_INTX(0));
rvupf_write64(rvu, RVU_PF_VFPF_MBOX_INTX(0), intr);
if (intr)
trace_otx2_msg_interrupt(rvu->pdev, "VF(s) to AF", intr);
rvu_queue_work(&rvu->afvf_wq_info, 0, vfs, intr);
return IRQ_HANDLED;
}
static void rvu_enable_mbox_intr(struct rvu *rvu)
{
struct rvu_hwinfo *hw = rvu->hw;
/* Clear spurious irqs, if any */
rvu_write64(rvu, BLKADDR_RVUM,
RVU_AF_PFAF_MBOX_INT, INTR_MASK(hw->total_pfs));
/* Enable mailbox interrupt for all PFs except PF0 i.e AF itself */
rvu_write64(rvu, BLKADDR_RVUM, RVU_AF_PFAF_MBOX_INT_ENA_W1S,
INTR_MASK(hw->total_pfs) & ~1ULL);
}
static void rvu_blklf_teardown(struct rvu *rvu, u16 pcifunc, u8 blkaddr)
{
struct rvu_block *block;
int slot, lf, num_lfs;
int err;
block = &rvu->hw->block[blkaddr];
num_lfs = rvu_get_rsrc_mapcount(rvu_get_pfvf(rvu, pcifunc),
block->addr);
if (!num_lfs)
return;
for (slot = 0; slot < num_lfs; slot++) {
lf = rvu_get_lf(rvu, block, pcifunc, slot);
if (lf < 0)
continue;
/* Cleanup LF and reset it */
if (block->addr == BLKADDR_NIX0 || block->addr == BLKADDR_NIX1)
rvu_nix_lf_teardown(rvu, pcifunc, block->addr, lf);
else if (block->addr == BLKADDR_NPA)
rvu_npa_lf_teardown(rvu, pcifunc, lf);
else if ((block->addr == BLKADDR_CPT0) ||
(block->addr == BLKADDR_CPT1))
rvu_cpt_lf_teardown(rvu, pcifunc, block->addr, lf,
slot);
err = rvu_lf_reset(rvu, block, lf);
if (err) {
dev_err(rvu->dev, "Failed to reset blkaddr %d LF%d\n",
block->addr, lf);
}
}
}
static void __rvu_flr_handler(struct rvu *rvu, u16 pcifunc)
{
mutex_lock(&rvu->flr_lock);
/* Reset order should reflect inter-block dependencies:
* 1. Reset any packet/work sources (NIX, CPT, TIM)
* 2. Flush and reset SSO/SSOW
* 3. Cleanup pools (NPA)
*/
rvu_blklf_teardown(rvu, pcifunc, BLKADDR_NIX0);
rvu_blklf_teardown(rvu, pcifunc, BLKADDR_NIX1);
rvu_blklf_teardown(rvu, pcifunc, BLKADDR_CPT0);
rvu_blklf_teardown(rvu, pcifunc, BLKADDR_CPT1);
rvu_blklf_teardown(rvu, pcifunc, BLKADDR_TIM);
rvu_blklf_teardown(rvu, pcifunc, BLKADDR_SSOW);
rvu_blklf_teardown(rvu, pcifunc, BLKADDR_SSO);
rvu_blklf_teardown(rvu, pcifunc, BLKADDR_NPA);
rvu_reset_lmt_map_tbl(rvu, pcifunc);
rvu_detach_rsrcs(rvu, NULL, pcifunc);
mutex_unlock(&rvu->flr_lock);
}
static void rvu_afvf_flr_handler(struct rvu *rvu, int vf)
{
int reg = 0;
/* pcifunc = 0(PF0) | (vf + 1) */
__rvu_flr_handler(rvu, vf + 1);
if (vf >= 64) {
reg = 1;
vf = vf - 64;
}
/* Signal FLR finish and enable IRQ */
rvupf_write64(rvu, RVU_PF_VFTRPENDX(reg), BIT_ULL(vf));
rvupf_write64(rvu, RVU_PF_VFFLR_INT_ENA_W1SX(reg), BIT_ULL(vf));
}
static void rvu_flr_handler(struct work_struct *work)
{
struct rvu_work *flrwork = container_of(work, struct rvu_work, work);
struct rvu *rvu = flrwork->rvu;
u16 pcifunc, numvfs, vf;
u64 cfg;
int pf;
pf = flrwork - rvu->flr_wrk;
if (pf >= rvu->hw->total_pfs) {
rvu_afvf_flr_handler(rvu, pf - rvu->hw->total_pfs);
return;
}
cfg = rvu_read64(rvu, BLKADDR_RVUM, RVU_PRIV_PFX_CFG(pf));
numvfs = (cfg >> 12) & 0xFF;
pcifunc = pf << RVU_PFVF_PF_SHIFT;
for (vf = 0; vf < numvfs; vf++)
__rvu_flr_handler(rvu, (pcifunc | (vf + 1)));
__rvu_flr_handler(rvu, pcifunc);
/* Signal FLR finish */
rvu_write64(rvu, BLKADDR_RVUM, RVU_AF_PFTRPEND, BIT_ULL(pf));
/* Enable interrupt */
rvu_write64(rvu, BLKADDR_RVUM, RVU_AF_PFFLR_INT_ENA_W1S, BIT_ULL(pf));
}
static void rvu_afvf_queue_flr_work(struct rvu *rvu, int start_vf, int numvfs)
{
int dev, vf, reg = 0;
u64 intr;
if (start_vf >= 64)
reg = 1;
intr = rvupf_read64(rvu, RVU_PF_VFFLR_INTX(reg));
if (!intr)
return;
for (vf = 0; vf < numvfs; vf++) {
if (!(intr & BIT_ULL(vf)))
continue;
/* Clear and disable the interrupt */
rvupf_write64(rvu, RVU_PF_VFFLR_INTX(reg), BIT_ULL(vf));
rvupf_write64(rvu, RVU_PF_VFFLR_INT_ENA_W1CX(reg), BIT_ULL(vf));
dev = vf + start_vf + rvu->hw->total_pfs;
queue_work(rvu->flr_wq, &rvu->flr_wrk[dev].work);
}
}
static irqreturn_t rvu_flr_intr_handler(int irq, void *rvu_irq)
{
struct rvu *rvu = (struct rvu *)rvu_irq;
u64 intr;
u8 pf;
intr = rvu_read64(rvu, BLKADDR_RVUM, RVU_AF_PFFLR_INT);
if (!intr)
goto afvf_flr;
for (pf = 0; pf < rvu->hw->total_pfs; pf++) {
if (intr & (1ULL << pf)) {
/* clear interrupt */
rvu_write64(rvu, BLKADDR_RVUM, RVU_AF_PFFLR_INT,
BIT_ULL(pf));
/* Disable the interrupt */
rvu_write64(rvu, BLKADDR_RVUM, RVU_AF_PFFLR_INT_ENA_W1C,
BIT_ULL(pf));
/* PF is already dead do only AF related operations */
queue_work(rvu->flr_wq, &rvu->flr_wrk[pf].work);
}
}
afvf_flr:
rvu_afvf_queue_flr_work(rvu, 0, 64);
if (rvu->vfs > 64)
rvu_afvf_queue_flr_work(rvu, 64, rvu->vfs - 64);
return IRQ_HANDLED;
}
static void rvu_me_handle_vfset(struct rvu *rvu, int idx, u64 intr)
{
int vf;
/* Nothing to be done here other than clearing the
* TRPEND bit.
*/
for (vf = 0; vf < 64; vf++) {
if (intr & (1ULL << vf)) {
/* clear the trpend due to ME(master enable) */
rvupf_write64(rvu, RVU_PF_VFTRPENDX(idx), BIT_ULL(vf));
/* clear interrupt */
rvupf_write64(rvu, RVU_PF_VFME_INTX(idx), BIT_ULL(vf));
}
}
}
/* Handles ME interrupts from VFs of AF */
static irqreturn_t rvu_me_vf_intr_handler(int irq, void *rvu_irq)
{
struct rvu *rvu = (struct rvu *)rvu_irq;
int vfset;
u64 intr;
intr = rvu_read64(rvu, BLKADDR_RVUM, RVU_AF_PFME_INT);
for (vfset = 0; vfset <= 1; vfset++) {
intr = rvupf_read64(rvu, RVU_PF_VFME_INTX(vfset));
if (intr)
rvu_me_handle_vfset(rvu, vfset, intr);
}
return IRQ_HANDLED;
}
/* Handles ME interrupts from PFs */
static irqreturn_t rvu_me_pf_intr_handler(int irq, void *rvu_irq)
{
struct rvu *rvu = (struct rvu *)rvu_irq;
u64 intr;
u8 pf;
intr = rvu_read64(rvu, BLKADDR_RVUM, RVU_AF_PFME_INT);
/* Nothing to be done here other than clearing the
* TRPEND bit.
*/
for (pf = 0; pf < rvu->hw->total_pfs; pf++) {
if (intr & (1ULL << pf)) {
/* clear the trpend due to ME(master enable) */
rvu_write64(rvu, BLKADDR_RVUM, RVU_AF_PFTRPEND,
BIT_ULL(pf));
/* clear interrupt */
rvu_write64(rvu, BLKADDR_RVUM, RVU_AF_PFME_INT,
BIT_ULL(pf));
}
}
return IRQ_HANDLED;
}
static void rvu_unregister_interrupts(struct rvu *rvu)
{
int irq;
rvu_cpt_unregister_interrupts(rvu);
/* Disable the Mbox interrupt */
rvu_write64(rvu, BLKADDR_RVUM, RVU_AF_PFAF_MBOX_INT_ENA_W1C,
INTR_MASK(rvu->hw->total_pfs) & ~1ULL);
/* Disable the PF FLR interrupt */
rvu_write64(rvu, BLKADDR_RVUM, RVU_AF_PFFLR_INT_ENA_W1C,
INTR_MASK(rvu->hw->total_pfs) & ~1ULL);
/* Disable the PF ME interrupt */
rvu_write64(rvu, BLKADDR_RVUM, RVU_AF_PFME_INT_ENA_W1C,
INTR_MASK(rvu->hw->total_pfs) & ~1ULL);
for (irq = 0; irq < rvu->num_vec; irq++) {
if (rvu->irq_allocated[irq]) {
free_irq(pci_irq_vector(rvu->pdev, irq), rvu);
rvu->irq_allocated[irq] = false;
}
}
pci_free_irq_vectors(rvu->pdev);
rvu->num_vec = 0;
}
static int rvu_afvf_msix_vectors_num_ok(struct rvu *rvu)
{
struct rvu_pfvf *pfvf = &rvu->pf[0];
int offset;
pfvf = &rvu->pf[0];
offset = rvu_read64(rvu, BLKADDR_RVUM, RVU_PRIV_PFX_INT_CFG(0)) & 0x3ff;
/* Make sure there are enough MSIX vectors configured so that
* VF interrupts can be handled. Offset equal to zero means
* that PF vectors are not configured and overlapping AF vectors.
*/
return (pfvf->msix.max >= RVU_AF_INT_VEC_CNT + RVU_PF_INT_VEC_CNT) &&
offset;
}
static int rvu_register_interrupts(struct rvu *rvu)
{
int ret, offset, pf_vec_start;
rvu->num_vec = pci_msix_vec_count(rvu->pdev);
rvu->irq_name = devm_kmalloc_array(rvu->dev, rvu->num_vec,
NAME_SIZE, GFP_KERNEL);
if (!rvu->irq_name)
return -ENOMEM;
rvu->irq_allocated = devm_kcalloc(rvu->dev, rvu->num_vec,
sizeof(bool), GFP_KERNEL);
if (!rvu->irq_allocated)
return -ENOMEM;
/* Enable MSI-X */
ret = pci_alloc_irq_vectors(rvu->pdev, rvu->num_vec,
rvu->num_vec, PCI_IRQ_MSIX);
if (ret < 0) {
dev_err(rvu->dev,
"RVUAF: Request for %d msix vectors failed, ret %d\n",
rvu->num_vec, ret);
return ret;
}
/* Register mailbox interrupt handler */
sprintf(&rvu->irq_name[RVU_AF_INT_VEC_MBOX * NAME_SIZE], "RVUAF Mbox");
ret = request_irq(pci_irq_vector(rvu->pdev, RVU_AF_INT_VEC_MBOX),
rvu_mbox_intr_handler, 0,
&rvu->irq_name[RVU_AF_INT_VEC_MBOX * NAME_SIZE], rvu);
if (ret) {
dev_err(rvu->dev,
"RVUAF: IRQ registration failed for mbox irq\n");
goto fail;
}
rvu->irq_allocated[RVU_AF_INT_VEC_MBOX] = true;
/* Enable mailbox interrupts from all PFs */
rvu_enable_mbox_intr(rvu);
/* Register FLR interrupt handler */
sprintf(&rvu->irq_name[RVU_AF_INT_VEC_PFFLR * NAME_SIZE],
"RVUAF FLR");
ret = request_irq(pci_irq_vector(rvu->pdev, RVU_AF_INT_VEC_PFFLR),
rvu_flr_intr_handler, 0,
&rvu->irq_name[RVU_AF_INT_VEC_PFFLR * NAME_SIZE],
rvu);
if (ret) {
dev_err(rvu->dev,
"RVUAF: IRQ registration failed for FLR\n");
goto fail;
}
rvu->irq_allocated[RVU_AF_INT_VEC_PFFLR] = true;
/* Enable FLR interrupt for all PFs*/
rvu_write64(rvu, BLKADDR_RVUM,
RVU_AF_PFFLR_INT, INTR_MASK(rvu->hw->total_pfs));
rvu_write64(rvu, BLKADDR_RVUM, RVU_AF_PFFLR_INT_ENA_W1S,
INTR_MASK(rvu->hw->total_pfs) & ~1ULL);
/* Register ME interrupt handler */
sprintf(&rvu->irq_name[RVU_AF_INT_VEC_PFME * NAME_SIZE],
"RVUAF ME");
ret = request_irq(pci_irq_vector(rvu->pdev, RVU_AF_INT_VEC_PFME),
rvu_me_pf_intr_handler, 0,
&rvu->irq_name[RVU_AF_INT_VEC_PFME * NAME_SIZE],
rvu);
if (ret) {
dev_err(rvu->dev,
"RVUAF: IRQ registration failed for ME\n");
}
rvu->irq_allocated[RVU_AF_INT_VEC_PFME] = true;
/* Clear TRPEND bit for all PF */
rvu_write64(rvu, BLKADDR_RVUM,
RVU_AF_PFTRPEND, INTR_MASK(rvu->hw->total_pfs));
/* Enable ME interrupt for all PFs*/
rvu_write64(rvu, BLKADDR_RVUM,
RVU_AF_PFME_INT, INTR_MASK(rvu->hw->total_pfs));
rvu_write64(rvu, BLKADDR_RVUM, RVU_AF_PFME_INT_ENA_W1S,
INTR_MASK(rvu->hw->total_pfs) & ~1ULL);
if (!rvu_afvf_msix_vectors_num_ok(rvu))
return 0;
/* Get PF MSIX vectors offset. */
pf_vec_start = rvu_read64(rvu, BLKADDR_RVUM,
RVU_PRIV_PFX_INT_CFG(0)) & 0x3ff;
/* Register MBOX0 interrupt. */
offset = pf_vec_start + RVU_PF_INT_VEC_VFPF_MBOX0;
sprintf(&rvu->irq_name[offset * NAME_SIZE], "RVUAFVF Mbox0");
ret = request_irq(pci_irq_vector(rvu->pdev, offset),
rvu_mbox_intr_handler, 0,
&rvu->irq_name[offset * NAME_SIZE],
rvu);
if (ret)
dev_err(rvu->dev,
"RVUAF: IRQ registration failed for Mbox0\n");
rvu->irq_allocated[offset] = true;
/* Register MBOX1 interrupt. MBOX1 IRQ number follows MBOX0 so
* simply increment current offset by 1.
*/
offset = pf_vec_start + RVU_PF_INT_VEC_VFPF_MBOX1;
sprintf(&rvu->irq_name[offset * NAME_SIZE], "RVUAFVF Mbox1");
ret = request_irq(pci_irq_vector(rvu->pdev, offset),
rvu_mbox_intr_handler, 0,
&rvu->irq_name[offset * NAME_SIZE],
rvu);
if (ret)
dev_err(rvu->dev,
"RVUAF: IRQ registration failed for Mbox1\n");
rvu->irq_allocated[offset] = true;
/* Register FLR interrupt handler for AF's VFs */
offset = pf_vec_start + RVU_PF_INT_VEC_VFFLR0;
sprintf(&rvu->irq_name[offset * NAME_SIZE], "RVUAFVF FLR0");
ret = request_irq(pci_irq_vector(rvu->pdev, offset),
rvu_flr_intr_handler, 0,
&rvu->irq_name[offset * NAME_SIZE], rvu);
if (ret) {
dev_err(rvu->dev,
"RVUAF: IRQ registration failed for RVUAFVF FLR0\n");
goto fail;
}
rvu->irq_allocated[offset] = true;
offset = pf_vec_start + RVU_PF_INT_VEC_VFFLR1;
sprintf(&rvu->irq_name[offset * NAME_SIZE], "RVUAFVF FLR1");
ret = request_irq(pci_irq_vector(rvu->pdev, offset),
rvu_flr_intr_handler, 0,
&rvu->irq_name[offset * NAME_SIZE], rvu);
if (ret) {
dev_err(rvu->dev,
"RVUAF: IRQ registration failed for RVUAFVF FLR1\n");
goto fail;
}
rvu->irq_allocated[offset] = true;
/* Register ME interrupt handler for AF's VFs */
offset = pf_vec_start + RVU_PF_INT_VEC_VFME0;
sprintf(&rvu->irq_name[offset * NAME_SIZE], "RVUAFVF ME0");
ret = request_irq(pci_irq_vector(rvu->pdev, offset),
rvu_me_vf_intr_handler, 0,
&rvu->irq_name[offset * NAME_SIZE], rvu);
if (ret) {
dev_err(rvu->dev,
"RVUAF: IRQ registration failed for RVUAFVF ME0\n");
goto fail;
}
rvu->irq_allocated[offset] = true;
offset = pf_vec_start + RVU_PF_INT_VEC_VFME1;
sprintf(&rvu->irq_name[offset * NAME_SIZE], "RVUAFVF ME1");
ret = request_irq(pci_irq_vector(rvu->pdev, offset),
rvu_me_vf_intr_handler, 0,
&rvu->irq_name[offset * NAME_SIZE], rvu);
if (ret) {
dev_err(rvu->dev,
"RVUAF: IRQ registration failed for RVUAFVF ME1\n");
goto fail;
}
rvu->irq_allocated[offset] = true;
ret = rvu_cpt_register_interrupts(rvu);
if (ret)
goto fail;
return 0;
fail:
rvu_unregister_interrupts(rvu);
return ret;
}
static void rvu_flr_wq_destroy(struct rvu *rvu)
{
if (rvu->flr_wq) {
destroy_workqueue(rvu->flr_wq);
rvu->flr_wq = NULL;
}
}
static int rvu_flr_init(struct rvu *rvu)
{
int dev, num_devs;
u64 cfg;
int pf;
/* Enable FLR for all PFs*/
for (pf = 0; pf < rvu->hw->total_pfs; pf++) {
cfg = rvu_read64(rvu, BLKADDR_RVUM, RVU_PRIV_PFX_CFG(pf));
rvu_write64(rvu, BLKADDR_RVUM, RVU_PRIV_PFX_CFG(pf),
cfg | BIT_ULL(22));
}
rvu->flr_wq = alloc_workqueue("rvu_afpf_flr",
WQ_UNBOUND | WQ_HIGHPRI | WQ_MEM_RECLAIM,
1);
if (!rvu->flr_wq)
return -ENOMEM;
num_devs = rvu->hw->total_pfs + pci_sriov_get_totalvfs(rvu->pdev);
rvu->flr_wrk = devm_kcalloc(rvu->dev, num_devs,
sizeof(struct rvu_work), GFP_KERNEL);
if (!rvu->flr_wrk) {
destroy_workqueue(rvu->flr_wq);
return -ENOMEM;
}
for (dev = 0; dev < num_devs; dev++) {
rvu->flr_wrk[dev].rvu = rvu;
INIT_WORK(&rvu->flr_wrk[dev].work, rvu_flr_handler);
}
mutex_init(&rvu->flr_lock);
return 0;
}
static void rvu_disable_afvf_intr(struct rvu *rvu)
{
int vfs = rvu->vfs;
rvupf_write64(rvu, RVU_PF_VFPF_MBOX_INT_ENA_W1CX(0), INTR_MASK(vfs));
rvupf_write64(rvu, RVU_PF_VFFLR_INT_ENA_W1CX(0), INTR_MASK(vfs));
rvupf_write64(rvu, RVU_PF_VFME_INT_ENA_W1CX(0), INTR_MASK(vfs));
if (vfs <= 64)
return;
rvupf_write64(rvu, RVU_PF_VFPF_MBOX_INT_ENA_W1CX(1),
INTR_MASK(vfs - 64));
rvupf_write64(rvu, RVU_PF_VFFLR_INT_ENA_W1CX(1), INTR_MASK(vfs - 64));
rvupf_write64(rvu, RVU_PF_VFME_INT_ENA_W1CX(1), INTR_MASK(vfs - 64));
}
static void rvu_enable_afvf_intr(struct rvu *rvu)
{
int vfs = rvu->vfs;
/* Clear any pending interrupts and enable AF VF interrupts for
* the first 64 VFs.
*/
/* Mbox */
rvupf_write64(rvu, RVU_PF_VFPF_MBOX_INTX(0), INTR_MASK(vfs));
rvupf_write64(rvu, RVU_PF_VFPF_MBOX_INT_ENA_W1SX(0), INTR_MASK(vfs));
/* FLR */
rvupf_write64(rvu, RVU_PF_VFFLR_INTX(0), INTR_MASK(vfs));
rvupf_write64(rvu, RVU_PF_VFFLR_INT_ENA_W1SX(0), INTR_MASK(vfs));
rvupf_write64(rvu, RVU_PF_VFME_INT_ENA_W1SX(0), INTR_MASK(vfs));
/* Same for remaining VFs, if any. */
if (vfs <= 64)
return;
rvupf_write64(rvu, RVU_PF_VFPF_MBOX_INTX(1), INTR_MASK(vfs - 64));
rvupf_write64(rvu, RVU_PF_VFPF_MBOX_INT_ENA_W1SX(1),
INTR_MASK(vfs - 64));
rvupf_write64(rvu, RVU_PF_VFFLR_INTX(1), INTR_MASK(vfs - 64));
rvupf_write64(rvu, RVU_PF_VFFLR_INT_ENA_W1SX(1), INTR_MASK(vfs - 64));
rvupf_write64(rvu, RVU_PF_VFME_INT_ENA_W1SX(1), INTR_MASK(vfs - 64));
}
int rvu_get_num_lbk_chans(void)
{
struct pci_dev *pdev;
void __iomem *base;
int ret = -EIO;
pdev = pci_get_device(PCI_VENDOR_ID_CAVIUM, PCI_DEVID_OCTEONTX2_LBK,
NULL);
if (!pdev)
goto err;
base = pci_ioremap_bar(pdev, 0);
if (!base)
goto err_put;
/* Read number of available LBK channels from LBK(0)_CONST register. */
ret = (readq(base + 0x10) >> 32) & 0xffff;
iounmap(base);
err_put:
pci_dev_put(pdev);
err:
return ret;
}
static int rvu_enable_sriov(struct rvu *rvu)
{
struct pci_dev *pdev = rvu->pdev;
int err, chans, vfs;
if (!rvu_afvf_msix_vectors_num_ok(rvu)) {
dev_warn(&pdev->dev,
"Skipping SRIOV enablement since not enough IRQs are available\n");
return 0;
}
chans = rvu_get_num_lbk_chans();
if (chans < 0)
return chans;
vfs = pci_sriov_get_totalvfs(pdev);
/* Limit VFs in case we have more VFs than LBK channels available. */
if (vfs > chans)
vfs = chans;
if (!vfs)
return 0;
/* LBK channel number 63 is used for switching packets between
* CGX mapped VFs. Hence limit LBK pairs till 62 only.
*/
if (vfs > 62)
vfs = 62;
/* Save VFs number for reference in VF interrupts handlers.
* Since interrupts might start arriving during SRIOV enablement
* ordinary API cannot be used to get number of enabled VFs.
*/
rvu->vfs = vfs;
err = rvu_mbox_init(rvu, &rvu->afvf_wq_info, TYPE_AFVF, vfs,
rvu_afvf_mbox_handler, rvu_afvf_mbox_up_handler);
if (err)
return err;
rvu_enable_afvf_intr(rvu);
/* Make sure IRQs are enabled before SRIOV. */
mb();
err = pci_enable_sriov(pdev, vfs);
if (err) {
rvu_disable_afvf_intr(rvu);
rvu_mbox_destroy(&rvu->afvf_wq_info);
return err;
}
return 0;
}
static void rvu_disable_sriov(struct rvu *rvu)
{
rvu_disable_afvf_intr(rvu);
rvu_mbox_destroy(&rvu->afvf_wq_info);
pci_disable_sriov(rvu->pdev);
}
static void rvu_update_module_params(struct rvu *rvu)
{
const char *default_pfl_name = "default";
strscpy(rvu->mkex_pfl_name,
mkex_profile ? mkex_profile : default_pfl_name, MKEX_NAME_LEN);
strscpy(rvu->kpu_pfl_name,
kpu_profile ? kpu_profile : default_pfl_name, KPU_NAME_LEN);
}
static int rvu_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{
struct device *dev = &pdev->dev;
struct rvu *rvu;
int err;
rvu = devm_kzalloc(dev, sizeof(*rvu), GFP_KERNEL);
if (!rvu)
return -ENOMEM;
rvu->hw = devm_kzalloc(dev, sizeof(struct rvu_hwinfo), GFP_KERNEL);
if (!rvu->hw) {
devm_kfree(dev, rvu);
return -ENOMEM;
}
pci_set_drvdata(pdev, rvu);
rvu->pdev = pdev;
rvu->dev = &pdev->dev;
err = pci_enable_device(pdev);
if (err) {
dev_err(dev, "Failed to enable PCI device\n");
goto err_freemem;
}
err = pci_request_regions(pdev, DRV_NAME);
if (err) {
dev_err(dev, "PCI request regions failed 0x%x\n", err);
goto err_disable_device;
}
err = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(48));
if (err) {
dev_err(dev, "DMA mask config failed, abort\n");
goto err_release_regions;
}
pci_set_master(pdev);
rvu->ptp = ptp_get();
if (IS_ERR(rvu->ptp)) {
err = PTR_ERR(rvu->ptp);
if (err == -EPROBE_DEFER)
goto err_release_regions;
rvu->ptp = NULL;
}
/* Map Admin function CSRs */
rvu->afreg_base = pcim_iomap(pdev, PCI_AF_REG_BAR_NUM, 0);
rvu->pfreg_base = pcim_iomap(pdev, PCI_PF_REG_BAR_NUM, 0);
if (!rvu->afreg_base || !rvu->pfreg_base) {
dev_err(dev, "Unable to map admin function CSRs, aborting\n");
err = -ENOMEM;
goto err_put_ptp;
}
/* Store module params in rvu structure */
rvu_update_module_params(rvu);
/* Check which blocks the HW supports */
rvu_check_block_implemented(rvu);
rvu_reset_all_blocks(rvu);
rvu_setup_hw_capabilities(rvu);
err = rvu_setup_hw_resources(rvu);
if (err)
goto err_put_ptp;
/* Init mailbox btw AF and PFs */
err = rvu_mbox_init(rvu, &rvu->afpf_wq_info, TYPE_AFPF,
rvu->hw->total_pfs, rvu_afpf_mbox_handler,
rvu_afpf_mbox_up_handler);
if (err) {
dev_err(dev, "%s: Failed to initialize mbox\n", __func__);
goto err_hwsetup;
}
err = rvu_flr_init(rvu);
if (err) {
dev_err(dev, "%s: Failed to initialize flr\n", __func__);
goto err_mbox;
}
err = rvu_register_interrupts(rvu);
if (err) {
dev_err(dev, "%s: Failed to register interrupts\n", __func__);
goto err_flr;
}
err = rvu_register_dl(rvu);
if (err) {
dev_err(dev, "%s: Failed to register devlink\n", __func__);
goto err_irq;
}
rvu_setup_rvum_blk_revid(rvu);
/* Enable AF's VFs (if any) */
err = rvu_enable_sriov(rvu);
if (err) {
dev_err(dev, "%s: Failed to enable sriov\n", __func__);
goto err_dl;
}
/* Initialize debugfs */
rvu_dbg_init(rvu);
mutex_init(&rvu->rswitch.switch_lock);
if (rvu->fwdata)
ptp_start(rvu->ptp, rvu->fwdata->sclk, rvu->fwdata->ptp_ext_clk_rate,
rvu->fwdata->ptp_ext_tstamp);
return 0;
err_dl:
rvu_unregister_dl(rvu);
err_irq:
rvu_unregister_interrupts(rvu);
err_flr:
rvu_flr_wq_destroy(rvu);
err_mbox:
rvu_mbox_destroy(&rvu->afpf_wq_info);
err_hwsetup:
rvu_cgx_exit(rvu);
rvu_fwdata_exit(rvu);
rvu_reset_all_blocks(rvu);
rvu_free_hw_resources(rvu);
rvu_clear_rvum_blk_revid(rvu);
err_put_ptp:
ptp_put(rvu->ptp);
err_release_regions:
pci_release_regions(pdev);
err_disable_device:
pci_disable_device(pdev);
err_freemem:
pci_set_drvdata(pdev, NULL);
devm_kfree(&pdev->dev, rvu->hw);
devm_kfree(dev, rvu);
return err;
}
static void rvu_remove(struct pci_dev *pdev)
{
struct rvu *rvu = pci_get_drvdata(pdev);
rvu_dbg_exit(rvu);
rvu_unregister_dl(rvu);
rvu_unregister_interrupts(rvu);
rvu_flr_wq_destroy(rvu);
rvu_cgx_exit(rvu);
rvu_fwdata_exit(rvu);
rvu_mbox_destroy(&rvu->afpf_wq_info);
rvu_disable_sriov(rvu);
rvu_reset_all_blocks(rvu);
rvu_free_hw_resources(rvu);
rvu_clear_rvum_blk_revid(rvu);
ptp_put(rvu->ptp);
pci_release_regions(pdev);
pci_disable_device(pdev);
pci_set_drvdata(pdev, NULL);
devm_kfree(&pdev->dev, rvu->hw);
devm_kfree(&pdev->dev, rvu);
}
static struct pci_driver rvu_driver = {
.name = DRV_NAME,
.id_table = rvu_id_table,
.probe = rvu_probe,
.remove = rvu_remove,
};
static int __init rvu_init_module(void)
{
int err;
pr_info("%s: %s\n", DRV_NAME, DRV_STRING);
err = pci_register_driver(&cgx_driver);
if (err < 0)
return err;
err = pci_register_driver(&ptp_driver);
if (err < 0)
goto ptp_err;
err = pci_register_driver(&rvu_driver);
if (err < 0)
goto rvu_err;
return 0;
rvu_err:
pci_unregister_driver(&ptp_driver);
ptp_err:
pci_unregister_driver(&cgx_driver);
return err;
}
static void __exit rvu_cleanup_module(void)
{
pci_unregister_driver(&rvu_driver);
pci_unregister_driver(&ptp_driver);
pci_unregister_driver(&cgx_driver);
}
module_init(rvu_init_module);
module_exit(rvu_cleanup_module);