165 lines
3.8 KiB
C
165 lines
3.8 KiB
C
|
// 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);
|
||
|
}
|