|
@@ -24,6 +24,11 @@
|
|
|
|
|
|
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);
|
|
|
+
|
|
|
/* Supported devices */
|
|
|
static const struct pci_device_id rvu_id_table[] = {
|
|
|
{ PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, PCI_DEVID_OCTEONTX2_RVU_AF) },
|
|
@@ -74,6 +79,45 @@ int rvu_alloc_rsrc(struct rsrc_bmap *rsrc)
|
|
|
return id;
|
|
|
}
|
|
|
|
|
|
+static 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);
|
|
|
+}
|
|
|
+
|
|
|
+static 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)
|
|
@@ -102,6 +146,26 @@ int rvu_alloc_bitmap(struct rsrc_bmap *rsrc)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+/* 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;
|
|
|
+
|
|
|
+ spin_lock(&rvu->rsrc_lock);
|
|
|
+ for (lf = 0; lf < block->lf.max; lf++) {
|
|
|
+ if (block->fn_map[lf] == pcifunc) {
|
|
|
+ if (slot == match) {
|
|
|
+ spin_unlock(&rvu->rsrc_lock);
|
|
|
+ return lf;
|
|
|
+ }
|
|
|
+ match++;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ spin_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.
|
|
@@ -236,6 +300,16 @@ 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));
|
|
|
+ *numvfs = (cfg >> 12) & 0xFF;
|
|
|
+ *hwvf = cfg & 0xFFF;
|
|
|
+}
|
|
|
+
|
|
|
static int rvu_get_hwvf(struct rvu *rvu, int pcifunc)
|
|
|
{
|
|
|
int pf, func;
|
|
@@ -330,20 +404,150 @@ static void rvu_scan_block(struct rvu *rvu, struct rvu_block *block)
|
|
|
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;
|
|
|
+ struct rvu_pfvf *pfvf;
|
|
|
+ int nvecs, offset;
|
|
|
+ u64 cfg;
|
|
|
+
|
|
|
+ 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);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
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;
|
|
|
|
|
|
- /* Free all bitmaps */
|
|
|
+ /* 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);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
static int rvu_setup_hw_resources(struct rvu *rvu)
|
|
@@ -499,6 +703,12 @@ init:
|
|
|
if (!rvu->hwvf)
|
|
|
return -ENOMEM;
|
|
|
|
|
|
+ spin_lock_init(&rvu->rsrc_lock);
|
|
|
+
|
|
|
+ err = rvu_setup_msix_resources(rvu);
|
|
|
+ if (err)
|
|
|
+ return err;
|
|
|
+
|
|
|
for (blkid = 0; blkid < BLK_COUNT; blkid++) {
|
|
|
block = &hw->block[blkid];
|
|
|
if (!block->lf.bmap)
|
|
@@ -516,8 +726,6 @@ init:
|
|
|
rvu_scan_block(rvu, block);
|
|
|
}
|
|
|
|
|
|
- spin_lock_init(&rvu->rsrc_lock);
|
|
|
-
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
@@ -603,6 +811,9 @@ static void rvu_detach_block(struct rvu *rvu, int pcifunc, int blktype)
|
|
|
|
|
|
/* Free the resource */
|
|
|
rvu_free_rsrc(&block->lf, lf);
|
|
|
+
|
|
|
+ /* Clear MSIX vector offset for this LF */
|
|
|
+ rvu_clear_msix_offset(rvu, pfvf, block, lf);
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -696,6 +907,9 @@ static void rvu_attach_block(struct rvu *rvu, int pcifunc,
|
|
|
(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);
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -871,6 +1085,119 @@ exit:
|
|
|
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);
|
|
|
+}
|
|
|
+
|
|
|
+static 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;
|
|
|
+
|
|
|
+ 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);
|
|
|
+
|
|
|
+ lf = rvu_get_lf(rvu, &hw->block[BLKADDR_NIX0], pcifunc, 0);
|
|
|
+ rsp->nix_msixoff = rvu_get_msix_offset(rvu, pfvf, BLKADDR_NIX0, 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);
|
|
|
+ }
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
static int rvu_process_mbox_msg(struct rvu *rvu, int devid,
|
|
|
struct mbox_msghdr *req)
|
|
|
{
|