// SPDX-License-Identifier: GPL-2.0 /* Marvell RVU Admin Function driver * * Copyright (C) 2024 Marvell. * */ #include #include #include "rvu_trace.h" #include "mbox.h" #include "reg.h" #include "api.h" static irqreturn_t cn20k_afvf_mbox_intr_handler(int irq, void *rvu_irq) { struct rvu_irq_data *rvu_irq_data = rvu_irq; struct rvu *rvu = rvu_irq_data->rvu; u64 intr; /* Sync with mbox memory region */ rmb(); /* Clear interrupts */ intr = rvupf_read64(rvu, rvu_irq_data->intr_status); rvupf_write64(rvu, rvu_irq_data->intr_status, intr); if (intr) trace_otx2_msg_interrupt(rvu->pdev, "VF(s) to AF", intr); rvu_irq_data->afvf_queue_work_hdlr(&rvu->afvf_wq_info, rvu_irq_data->start, rvu_irq_data->mdevs, intr); return IRQ_HANDLED; } int cn20k_register_afvf_mbox_intr(struct rvu *rvu, int pf_vec_start) { struct rvu_irq_data *irq_data; int intr_vec, offset, vec = 0; int err; /* irq data for 4 VFPF intr vectors */ irq_data = devm_kcalloc(rvu->dev, 4, sizeof(struct rvu_irq_data), GFP_KERNEL); if (!irq_data) return -ENOMEM; for (intr_vec = RVU_MBOX_PF_INT_VEC_VFPF_MBOX0; intr_vec <= RVU_MBOX_PF_INT_VEC_VFPF1_MBOX1; intr_vec++, vec++) { switch (intr_vec) { case RVU_MBOX_PF_INT_VEC_VFPF_MBOX0: irq_data[vec].intr_status = RVU_MBOX_PF_VFPF_INTX(0); irq_data[vec].start = 0; irq_data[vec].mdevs = 64; break; case RVU_MBOX_PF_INT_VEC_VFPF_MBOX1: irq_data[vec].intr_status = RVU_MBOX_PF_VFPF_INTX(1); irq_data[vec].start = 64; irq_data[vec].mdevs = 64; break; case RVU_MBOX_PF_INT_VEC_VFPF1_MBOX0: irq_data[vec].intr_status = RVU_MBOX_PF_VFPF1_INTX(0); irq_data[vec].start = 0; irq_data[vec].mdevs = 64; break; case RVU_MBOX_PF_INT_VEC_VFPF1_MBOX1: irq_data[vec].intr_status = RVU_MBOX_PF_VFPF1_INTX(1); irq_data[vec].start = 64; irq_data[vec].mdevs = 64; break; } irq_data[vec].afvf_queue_work_hdlr = rvu_queue_work; offset = pf_vec_start + intr_vec; irq_data[vec].vec_num = offset; irq_data[vec].rvu = rvu; sprintf(&rvu->irq_name[offset * NAME_SIZE], "RVUAF VFAF%d Mbox%d", vec / 2, vec % 2); err = request_irq(pci_irq_vector(rvu->pdev, offset), rvu->ng_rvu->rvu_mbox_ops->afvf_intr_handler, 0, &rvu->irq_name[offset * NAME_SIZE], &irq_data[vec]); if (err) { dev_err(rvu->dev, "RVUAF: IRQ registration failed for AFVF mbox irq\n"); return err; } rvu->irq_allocated[offset] = true; } return 0; } /* CN20K mbox PFx => AF irq handler */ static irqreturn_t cn20k_mbox_pf_common_intr_handler(int irq, void *rvu_irq) { struct rvu_irq_data *rvu_irq_data = rvu_irq; struct rvu *rvu = rvu_irq_data->rvu; u64 intr; /* Clear interrupts */ intr = rvu_read64(rvu, BLKADDR_RVUM, rvu_irq_data->intr_status); rvu_write64(rvu, BLKADDR_RVUM, rvu_irq_data->intr_status, intr); if (intr) trace_otx2_msg_interrupt(rvu->pdev, "PF(s) to AF", intr); /* Sync with mbox memory region */ rmb(); rvu_irq_data->rvu_queue_work_hdlr(&rvu->afpf_wq_info, rvu_irq_data->start, rvu_irq_data->mdevs, intr); return IRQ_HANDLED; } void cn20k_rvu_enable_mbox_intr(struct rvu *rvu) { struct rvu_hwinfo *hw = rvu->hw; /* Clear spurious irqs, if any */ rvu_write64(rvu, BLKADDR_RVUM, RVU_MBOX_AF_PFAF_INT(0), INTR_MASK(hw->total_pfs)); rvu_write64(rvu, BLKADDR_RVUM, RVU_MBOX_AF_PFAF_INT(1), INTR_MASK(hw->total_pfs - 64)); rvu_write64(rvu, BLKADDR_RVUM, RVU_MBOX_AF_PFAF1_INT(0), INTR_MASK(hw->total_pfs)); rvu_write64(rvu, BLKADDR_RVUM, RVU_MBOX_AF_PFAF1_INT(1), INTR_MASK(hw->total_pfs - 64)); /* Enable mailbox interrupt for all PFs except PF0 i.e AF itself */ rvu_write64(rvu, BLKADDR_RVUM, RVU_MBOX_AF_PFAF_INT_ENA_W1S(0), INTR_MASK(hw->total_pfs) & ~1ULL); rvu_write64(rvu, BLKADDR_RVUM, RVU_MBOX_AF_PFAF_INT_ENA_W1S(1), INTR_MASK(hw->total_pfs - 64)); rvu_write64(rvu, BLKADDR_RVUM, RVU_MBOX_AF_PFAF1_INT_ENA_W1S(0), INTR_MASK(hw->total_pfs) & ~1ULL); rvu_write64(rvu, BLKADDR_RVUM, RVU_MBOX_AF_PFAF1_INT_ENA_W1S(1), INTR_MASK(hw->total_pfs - 64)); } void cn20k_rvu_unregister_interrupts(struct rvu *rvu) { rvu_write64(rvu, BLKADDR_RVUM, RVU_MBOX_AF_PFAF_INT_ENA_W1C(0), INTR_MASK(rvu->hw->total_pfs) & ~1ULL); rvu_write64(rvu, BLKADDR_RVUM, RVU_MBOX_AF_PFAF_INT_ENA_W1C(1), INTR_MASK(rvu->hw->total_pfs - 64)); rvu_write64(rvu, BLKADDR_RVUM, RVU_MBOX_AF_PFAF1_INT_ENA_W1C(0), INTR_MASK(rvu->hw->total_pfs) & ~1ULL); rvu_write64(rvu, BLKADDR_RVUM, RVU_MBOX_AF_PFAF1_INT_ENA_W1C(1), INTR_MASK(rvu->hw->total_pfs - 64)); } int cn20k_register_afpf_mbox_intr(struct rvu *rvu) { struct rvu_irq_data *irq_data; int intr_vec, ret, vec = 0; /* irq data for 4 PF intr vectors */ irq_data = devm_kcalloc(rvu->dev, 4, sizeof(struct rvu_irq_data), GFP_KERNEL); if (!irq_data) return -ENOMEM; for (intr_vec = RVU_AF_CN20K_INT_VEC_PFAF_MBOX0; intr_vec <= RVU_AF_CN20K_INT_VEC_PFAF1_MBOX1; intr_vec++, vec++) { switch (intr_vec) { case RVU_AF_CN20K_INT_VEC_PFAF_MBOX0: irq_data[vec].intr_status = RVU_MBOX_AF_PFAF_INT(0); irq_data[vec].start = 0; irq_data[vec].mdevs = 64; break; case RVU_AF_CN20K_INT_VEC_PFAF_MBOX1: irq_data[vec].intr_status = RVU_MBOX_AF_PFAF_INT(1); irq_data[vec].start = 64; irq_data[vec].mdevs = 96; break; case RVU_AF_CN20K_INT_VEC_PFAF1_MBOX0: irq_data[vec].intr_status = RVU_MBOX_AF_PFAF1_INT(0); irq_data[vec].start = 0; irq_data[vec].mdevs = 64; break; case RVU_AF_CN20K_INT_VEC_PFAF1_MBOX1: irq_data[vec].intr_status = RVU_MBOX_AF_PFAF1_INT(1); irq_data[vec].start = 64; irq_data[vec].mdevs = 96; break; } irq_data[vec].rvu_queue_work_hdlr = rvu_queue_work; irq_data[vec].vec_num = intr_vec; irq_data[vec].rvu = rvu; /* Register mailbox interrupt handler */ sprintf(&rvu->irq_name[intr_vec * NAME_SIZE], "RVUAF PFAF%d Mbox%d", vec / 2, vec % 2); ret = request_irq(pci_irq_vector(rvu->pdev, intr_vec), rvu->ng_rvu->rvu_mbox_ops->pf_intr_handler, 0, &rvu->irq_name[intr_vec * NAME_SIZE], &irq_data[vec]); if (ret) return ret; rvu->irq_allocated[intr_vec] = true; } return 0; } int cn20k_rvu_get_mbox_regions(struct rvu *rvu, void **mbox_addr, int num, int type, unsigned long *pf_bmap) { int region; u64 bar; if (type == TYPE_AFVF) { for (region = 0; region < num; region++) { if (!test_bit(region, pf_bmap)) continue; bar = (u64)phys_to_virt((u64)rvu->ng_rvu->vf_mbox_addr->base); bar += region * MBOX_SIZE; mbox_addr[region] = (void *)bar; if (!mbox_addr[region]) return -ENOMEM; } return 0; } for (region = 0; region < num; region++) { if (!test_bit(region, pf_bmap)) continue; bar = (u64)phys_to_virt((u64)rvu->ng_rvu->pf_mbox_addr->base); bar += region * MBOX_SIZE; mbox_addr[region] = (void *)bar; if (!mbox_addr[region]) return -ENOMEM; } return 0; } static int rvu_alloc_mbox_memory(struct rvu *rvu, int type, int ndevs, int mbox_size) { struct qmem *mbox_addr; dma_addr_t iova; int pf, err; /* Allocate contiguous memory for mailbox communication. * eg: AF <=> PFx mbox memory * This allocated memory is split into chunks of MBOX_SIZE * and setup into each of the RVU PFs. In HW this memory will * get aliased to an offset within BAR2 of those PFs. * * AF will access mbox memory using direct physical addresses * and PFs will access the same shared memory from BAR2. * * PF <=> VF mbox memory also works in the same fashion. * AFPF, PFVF requires IOVA to be used to maintain the mailbox msgs */ err = qmem_alloc(rvu->dev, &mbox_addr, ndevs, mbox_size); if (err) return -ENOMEM; switch (type) { case TYPE_AFPF: rvu->ng_rvu->pf_mbox_addr = mbox_addr; iova = (u64)mbox_addr->iova; for (pf = 0; pf < ndevs; pf++) { rvu_write64(rvu, BLKADDR_RVUM, RVU_MBOX_AF_PFX_ADDR(pf), (u64)iova); iova += mbox_size; } break; case TYPE_AFVF: rvu->ng_rvu->vf_mbox_addr = mbox_addr; rvupf_write64(rvu, RVU_PF_VF_MBOX_ADDR, (u64)mbox_addr->iova); break; default: return 0; } return 0; } static struct mbox_ops cn20k_mbox_ops = { .pf_intr_handler = cn20k_mbox_pf_common_intr_handler, .afvf_intr_handler = cn20k_afvf_mbox_intr_handler, }; int cn20k_rvu_mbox_init(struct rvu *rvu, int type, int ndevs) { int dev; if (!is_cn20k(rvu->pdev)) return 0; rvu->ng_rvu->rvu_mbox_ops = &cn20k_mbox_ops; if (type == TYPE_AFVF) { rvu_write64(rvu, BLKADDR_RVUM, RVU_MBOX_PF_VF_CFG, ilog2(MBOX_SIZE)); } else { for (dev = 0; dev < ndevs; dev++) rvu_write64(rvu, BLKADDR_RVUM, RVU_MBOX_AF_PFX_CFG(dev), ilog2(MBOX_SIZE)); } return rvu_alloc_mbox_memory(rvu, type, ndevs, MBOX_SIZE); } void cn20k_free_mbox_memory(struct rvu *rvu) { if (!is_cn20k(rvu->pdev)) return; qmem_free(rvu->dev, rvu->ng_rvu->pf_mbox_addr); qmem_free(rvu->dev, rvu->ng_rvu->vf_mbox_addr); } void cn20k_rvu_disable_afvf_intr(struct rvu *rvu, int vfs) { rvupf_write64(rvu, RVU_MBOX_PF_VFPF_INT_ENA_W1CX(0), INTR_MASK(vfs)); rvupf_write64(rvu, RVU_MBOX_PF_VFPF1_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_MBOX_PF_VFPF_INT_ENA_W1CX(1), INTR_MASK(vfs - 64)); rvupf_write64(rvu, RVU_MBOX_PF_VFPF1_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)); } void cn20k_rvu_enable_afvf_intr(struct rvu *rvu, int vfs) { /* Clear any pending interrupts and enable AF VF interrupts for * the first 64 VFs. */ rvupf_write64(rvu, RVU_MBOX_PF_VFPF_INTX(0), INTR_MASK(vfs)); rvupf_write64(rvu, RVU_MBOX_PF_VFPF_INT_ENA_W1SX(0), INTR_MASK(vfs)); rvupf_write64(rvu, RVU_MBOX_PF_VFPF1_INTX(0), INTR_MASK(vfs)); rvupf_write64(rvu, RVU_MBOX_PF_VFPF1_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)); /* Same for remaining VFs, if any. */ if (vfs <= 64) return; rvupf_write64(rvu, RVU_MBOX_PF_VFPF_INTX(1), INTR_MASK(vfs - 64)); rvupf_write64(rvu, RVU_MBOX_PF_VFPF_INT_ENA_W1SX(1), INTR_MASK(vfs - 64)); rvupf_write64(rvu, RVU_MBOX_PF_VFPF1_INTX(1), INTR_MASK(vfs - 64)); rvupf_write64(rvu, RVU_MBOX_PF_VFPF1_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_alloc_cint_qint_mem(struct rvu *rvu, struct rvu_pfvf *pfvf, int blkaddr, int nixlf) { int qints, hwctx_size, err; u64 cfg, ctx_cfg; if (is_rvu_otx2(rvu) || is_cn20k(rvu->pdev)) return 0; ctx_cfg = rvu_read64(rvu, blkaddr, NIX_AF_CONST3); /* Alloc memory for CQINT's HW contexts */ cfg = rvu_read64(rvu, blkaddr, NIX_AF_CONST2); qints = (cfg >> 24) & 0xFFF; hwctx_size = 1UL << ((ctx_cfg >> 24) & 0xF); err = qmem_alloc(rvu->dev, &pfvf->cq_ints_ctx, qints, hwctx_size); if (err) return -ENOMEM; rvu_write64(rvu, blkaddr, NIX_AF_LFX_CINTS_BASE(nixlf), (u64)pfvf->cq_ints_ctx->iova); /* Alloc memory for QINT's HW contexts */ cfg = rvu_read64(rvu, blkaddr, NIX_AF_CONST2); qints = (cfg >> 12) & 0xFFF; hwctx_size = 1UL << ((ctx_cfg >> 20) & 0xF); err = qmem_alloc(rvu->dev, &pfvf->nix_qints_ctx, qints, hwctx_size); if (err) return -ENOMEM; rvu_write64(rvu, blkaddr, NIX_AF_LFX_QINTS_BASE(nixlf), (u64)pfvf->nix_qints_ctx->iova); return 0; }