// SPDX-License-Identifier: GPL-2.0 /* Marvell RVU Ethernet driver * * Copyright (C) 2024 Marvell. * */ #include "otx2_common.h" #include "otx2_reg.h" #include "otx2_struct.h" #include "cn10k.h" static struct dev_hw_ops cn20k_hw_ops = { .pfaf_mbox_intr_handler = cn20k_pfaf_mbox_intr_handler, .vfaf_mbox_intr_handler = cn20k_vfaf_mbox_intr_handler, .pfvf_mbox_intr_handler = cn20k_pfvf_mbox_intr_handler, }; void cn20k_init(struct otx2_nic *pfvf) { pfvf->hw_ops = &cn20k_hw_ops; } EXPORT_SYMBOL(cn20k_init); /* CN20K mbox AF => PFx irq handler */ irqreturn_t cn20k_pfaf_mbox_intr_handler(int irq, void *pf_irq) { struct otx2_nic *pf = pf_irq; struct mbox *mw = &pf->mbox; struct otx2_mbox_dev *mdev; struct otx2_mbox *mbox; struct mbox_hdr *hdr; u64 pf_trig_val; pf_trig_val = otx2_read64(pf, RVU_PF_INT) & 0x3ULL; /* Clear the IRQ */ otx2_write64(pf, RVU_PF_INT, pf_trig_val); if (pf_trig_val & BIT_ULL(0)) { mbox = &mw->mbox_up; mdev = &mbox->dev[0]; otx2_sync_mbox_bbuf(mbox, 0); hdr = (struct mbox_hdr *)(mdev->mbase + mbox->rx_start); if (hdr->num_msgs) queue_work(pf->mbox_wq, &mw->mbox_up_wrk); trace_otx2_msg_interrupt(pf->pdev, "UP message from AF to PF", BIT_ULL(0)); } if (pf_trig_val & BIT_ULL(1)) { mbox = &mw->mbox; mdev = &mbox->dev[0]; otx2_sync_mbox_bbuf(mbox, 0); hdr = (struct mbox_hdr *)(mdev->mbase + mbox->rx_start); if (hdr->num_msgs) queue_work(pf->mbox_wq, &mw->mbox_wrk); trace_otx2_msg_interrupt(pf->pdev, "DOWN reply from AF to PF", BIT_ULL(1)); } return IRQ_HANDLED; } irqreturn_t cn20k_vfaf_mbox_intr_handler(int irq, void *vf_irq) { struct otx2_nic *vf = vf_irq; struct otx2_mbox_dev *mdev; struct otx2_mbox *mbox; struct mbox_hdr *hdr; u64 vf_trig_val; vf_trig_val = otx2_read64(vf, RVU_VF_INT) & 0x3ULL; /* Clear the IRQ */ otx2_write64(vf, RVU_VF_INT, vf_trig_val); /* Read latest mbox data */ smp_rmb(); if (vf_trig_val & BIT_ULL(1)) { /* Check for PF => VF response messages */ mbox = &vf->mbox.mbox; mdev = &mbox->dev[0]; otx2_sync_mbox_bbuf(mbox, 0); hdr = (struct mbox_hdr *)(mdev->mbase + mbox->rx_start); if (hdr->num_msgs) queue_work(vf->mbox_wq, &vf->mbox.mbox_wrk); trace_otx2_msg_interrupt(mbox->pdev, "DOWN reply from PF0 to VF", BIT_ULL(1)); } if (vf_trig_val & BIT_ULL(0)) { /* Check for PF => VF notification messages */ mbox = &vf->mbox.mbox_up; mdev = &mbox->dev[0]; otx2_sync_mbox_bbuf(mbox, 0); hdr = (struct mbox_hdr *)(mdev->mbase + mbox->rx_start); if (hdr->num_msgs) queue_work(vf->mbox_wq, &vf->mbox.mbox_up_wrk); trace_otx2_msg_interrupt(mbox->pdev, "UP message from PF0 to VF", BIT_ULL(0)); } return IRQ_HANDLED; } void cn20k_enable_pfvf_mbox_intr(struct otx2_nic *pf, int numvfs) { /* Clear PF <=> VF mailbox IRQ */ otx2_write64(pf, RVU_MBOX_PF_VFPF_INTX(0), ~0ull); otx2_write64(pf, RVU_MBOX_PF_VFPF_INTX(1), ~0ull); otx2_write64(pf, RVU_MBOX_PF_VFPF1_INTX(0), ~0ull); otx2_write64(pf, RVU_MBOX_PF_VFPF1_INTX(1), ~0ull); /* Enable PF <=> VF mailbox IRQ */ otx2_write64(pf, RVU_MBOX_PF_VFPF_INT_ENA_W1SX(0), INTR_MASK(numvfs)); otx2_write64(pf, RVU_MBOX_PF_VFPF1_INT_ENA_W1SX(0), INTR_MASK(numvfs)); if (numvfs > 64) { numvfs -= 64; otx2_write64(pf, RVU_MBOX_PF_VFPF_INT_ENA_W1SX(1), INTR_MASK(numvfs)); otx2_write64(pf, RVU_MBOX_PF_VFPF1_INT_ENA_W1SX(1), INTR_MASK(numvfs)); } } void cn20k_disable_pfvf_mbox_intr(struct otx2_nic *pf, int numvfs) { int vector, intr_vec, vec = 0; /* Disable PF <=> VF mailbox IRQ */ otx2_write64(pf, RVU_MBOX_PF_VFPF_INT_ENA_W1CX(0), ~0ull); otx2_write64(pf, RVU_MBOX_PF_VFPF_INT_ENA_W1CX(1), ~0ull); otx2_write64(pf, RVU_MBOX_PF_VFPF1_INT_ENA_W1CX(0), ~0ull); otx2_write64(pf, RVU_MBOX_PF_VFPF1_INT_ENA_W1CX(1), ~0ull); otx2_write64(pf, RVU_MBOX_PF_VFPF_INTX(0), ~0ull); otx2_write64(pf, RVU_MBOX_PF_VFPF1_INTX(0), ~0ull); if (numvfs > 64) { otx2_write64(pf, RVU_MBOX_PF_VFPF_INTX(1), ~0ull); otx2_write64(pf, RVU_MBOX_PF_VFPF1_INTX(1), ~0ull); } for (intr_vec = RVU_MBOX_PF_INT_VEC_VFPF_MBOX0; intr_vec <= RVU_MBOX_PF_INT_VEC_VFPF1_MBOX1; intr_vec++, vec++) { vector = pci_irq_vector(pf->pdev, intr_vec); free_irq(vector, pf->hw.pfvf_irq_devid[vec]); } } irqreturn_t cn20k_pfvf_mbox_intr_handler(int irq, void *pf_irq) { struct pf_irq_data *irq_data = pf_irq; struct otx2_nic *pf = irq_data->pf; struct mbox *mbox; u64 intr; /* Sync with mbox memory region */ rmb(); /* Clear interrupts */ intr = otx2_read64(pf, irq_data->intr_status); otx2_write64(pf, irq_data->intr_status, intr); mbox = pf->mbox_pfvf; if (intr) trace_otx2_msg_interrupt(pf->pdev, "VF(s) to PF", intr); irq_data->pf_queue_work_hdlr(mbox, pf->mbox_pfvf_wq, irq_data->start, irq_data->mdevs, intr); return IRQ_HANDLED; } int cn20k_register_pfvf_mbox_intr(struct otx2_nic *pf, int numvfs) { struct otx2_hw *hw = &pf->hw; struct pf_irq_data *irq_data; int intr_vec, ret, vec = 0; char *irq_name; /* irq data for 4 PF intr vectors */ irq_data = devm_kcalloc(pf->dev, 4, sizeof(struct pf_irq_data), GFP_KERNEL); if (!irq_data) return -ENOMEM; for (intr_vec = RVU_MBOX_PF_INT_VEC_VFPF_MBOX0; intr_vec <= RVU_MBOX_PF_INT_VEC_VFPF1_MBOX1; intr_vec++, vec++) { switch (intr_vec) { case RVU_MBOX_PF_INT_VEC_VFPF_MBOX0: irq_data[vec].intr_status = RVU_MBOX_PF_VFPF_INTX(0); irq_data[vec].start = 0; irq_data[vec].mdevs = 64; break; case RVU_MBOX_PF_INT_VEC_VFPF_MBOX1: irq_data[vec].intr_status = RVU_MBOX_PF_VFPF_INTX(1); irq_data[vec].start = 64; irq_data[vec].mdevs = 96; break; case RVU_MBOX_PF_INT_VEC_VFPF1_MBOX0: irq_data[vec].intr_status = RVU_MBOX_PF_VFPF1_INTX(0); irq_data[vec].start = 0; irq_data[vec].mdevs = 64; break; case RVU_MBOX_PF_INT_VEC_VFPF1_MBOX1: irq_data[vec].intr_status = RVU_MBOX_PF_VFPF1_INTX(1); irq_data[vec].start = 64; irq_data[vec].mdevs = 96; break; } irq_data[vec].pf_queue_work_hdlr = otx2_queue_vf_work; irq_data[vec].vec_num = intr_vec; irq_data[vec].pf = pf; /* Register mailbox interrupt handler */ irq_name = &hw->irq_name[intr_vec * NAME_SIZE]; if (pf->pcifunc) snprintf(irq_name, NAME_SIZE, "RVUPF%d_VF%d Mbox%d", rvu_get_pf(pf->pdev, pf->pcifunc), vec / 2, vec % 2); else snprintf(irq_name, NAME_SIZE, "RVUPF_VF%d Mbox%d", vec / 2, vec % 2); hw->pfvf_irq_devid[vec] = &irq_data[vec]; ret = request_irq(pci_irq_vector(pf->pdev, intr_vec), pf->hw_ops->pfvf_mbox_intr_handler, 0, irq_name, &irq_data[vec]); if (ret) { dev_err(pf->dev, "RVUPF: IRQ registration failed for PFVF mbox0 irq\n"); return ret; } } cn20k_enable_pfvf_mbox_intr(pf, numvfs); return 0; }