linux/drivers/net/ethernet/freescale/enetc/enetc_msg.c
Claudiu Manoil beb74ac878 enetc: Add vf to pf messaging support
VSIs (VFs) may send a message to the PSI (PF) for general notification
or to gain access to hardware resources which requires host inspection.
These messages may vary in size and are handled as a partition copy
between two memory regions owned by the respective participants.
The PSI will respond with fail or success and a 16-bit message code.
The patch implements the vf to pf messaging mechanism above and, as the
first application making use of this support, it enables the VF to
configure its own primary MAC address.

Signed-off-by: Catalin Horghidan <catalin.horghidan@nxp.com>
Signed-off-by: Claudiu Manoil <claudiu.manoil@nxp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2019-01-24 21:55:53 -08:00

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);
}