// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) /* Copyright 2017-2019 NXP */ #include "enetc_pf.h" static void enetc_msg_disable_mr_int(struct enetc_hw *hw) { u32 psiier = enetc_rd(hw, ENETC_PSIIER); /* disable MR int source(s) */ enetc_wr(hw, ENETC_PSIIER, psiier & ~ENETC_PSIIER_MR_MASK); } static void enetc_msg_enable_mr_int(struct enetc_hw *hw) { u32 psiier = enetc_rd(hw, ENETC_PSIIER); enetc_wr(hw, ENETC_PSIIER, psiier | ENETC_PSIIER_MR_MASK); } static irqreturn_t enetc_msg_psi_msix(int irq, void *data) { struct enetc_si *si = (struct enetc_si *)data; struct enetc_pf *pf = enetc_si_priv(si); enetc_msg_disable_mr_int(&si->hw); schedule_work(&pf->msg_task); return IRQ_HANDLED; } static void enetc_msg_task(struct work_struct *work) { struct enetc_pf *pf = container_of(work, struct enetc_pf, msg_task); struct enetc_hw *hw = &pf->si->hw; unsigned long mr_mask; int i; for (;;) { mr_mask = enetc_rd(hw, ENETC_PSIMSGRR) & ENETC_PSIMSGRR_MR_MASK; if (!mr_mask) { /* re-arm MR interrupts, w1c the IDR reg */ enetc_wr(hw, ENETC_PSIIDR, ENETC_PSIIER_MR_MASK); enetc_msg_enable_mr_int(hw); return; } for (i = 0; i < pf->num_vfs; i++) { u32 psimsgrr; u16 msg_code; if (!(ENETC_PSIMSGRR_MR(i) & mr_mask)) continue; enetc_msg_handle_rxmsg(pf, i, &msg_code); psimsgrr = ENETC_SIMSGSR_SET_MC(msg_code); psimsgrr |= ENETC_PSIMSGRR_MR(i); /* w1c */ enetc_wr(hw, ENETC_PSIMSGRR, psimsgrr); } } } /* Init */ static int enetc_msg_alloc_mbx(struct enetc_si *si, int idx) { struct enetc_pf *pf = enetc_si_priv(si); struct device *dev = &si->pdev->dev; struct enetc_hw *hw = &si->hw; struct enetc_msg_swbd *msg; u32 val; msg = &pf->rxmsg[idx]; /* allocate and set receive buffer */ msg->size = ENETC_DEFAULT_MSG_SIZE; msg->vaddr = dma_alloc_coherent(dev, msg->size, &msg->dma, GFP_KERNEL); if (!msg->vaddr) { dev_err(dev, "msg: fail to alloc dma buffer of size: %d\n", msg->size); return -ENOMEM; } /* set multiple of 32 bytes */ val = lower_32_bits(msg->dma); enetc_wr(hw, ENETC_PSIVMSGRCVAR0(idx), val); val = upper_32_bits(msg->dma); enetc_wr(hw, ENETC_PSIVMSGRCVAR1(idx), val); return 0; } static void enetc_msg_free_mbx(struct enetc_si *si, int idx) { struct enetc_pf *pf = enetc_si_priv(si); struct enetc_hw *hw = &si->hw; struct enetc_msg_swbd *msg; msg = &pf->rxmsg[idx]; dma_free_coherent(&si->pdev->dev, msg->size, msg->vaddr, msg->dma); memset(msg, 0, sizeof(*msg)); enetc_wr(hw, ENETC_PSIVMSGRCVAR0(idx), 0); enetc_wr(hw, ENETC_PSIVMSGRCVAR1(idx), 0); } int enetc_msg_psi_init(struct enetc_pf *pf) { struct enetc_si *si = pf->si; int vector, i, err; /* register message passing interrupt handler */ snprintf(pf->msg_int_name, sizeof(pf->msg_int_name), "%s-vfmsg", si->ndev->name); vector = pci_irq_vector(si->pdev, ENETC_SI_INT_IDX); err = request_irq(vector, enetc_msg_psi_msix, 0, pf->msg_int_name, si); if (err) { dev_err(&si->pdev->dev, "PSI messaging: request_irq() failed!\n"); return err; } /* set one IRQ entry for PSI message receive notification (SI int) */ enetc_wr(&si->hw, ENETC_SIMSIVR, ENETC_SI_INT_IDX); /* initialize PSI mailbox */ INIT_WORK(&pf->msg_task, enetc_msg_task); for (i = 0; i < pf->num_vfs; i++) { err = enetc_msg_alloc_mbx(si, i); if (err) goto err_init_mbx; } /* enable MR interrupts */ enetc_msg_enable_mr_int(&si->hw); return 0; err_init_mbx: for (i--; i >= 0; i--) enetc_msg_free_mbx(si, i); free_irq(vector, si); return err; } void enetc_msg_psi_free(struct enetc_pf *pf) { struct enetc_si *si = pf->si; int i; cancel_work_sync(&pf->msg_task); /* disable MR interrupts */ enetc_msg_disable_mr_int(&si->hw); for (i = 0; i < pf->num_vfs; i++) enetc_msg_free_mbx(si, i); /* de-register message passing interrupt handler */ free_irq(pci_irq_vector(si->pdev, ENETC_SI_INT_IDX), si); }