Commit beb74ac8 authored by Claudiu Manoil's avatar Claudiu Manoil Committed by David S. Miller
Browse files

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: default avatarCatalin Horghidan <catalin.horghidan@nxp.com>
Signed-off-by: default avatarClaudiu Manoil <claudiu.manoil@nxp.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 16eb4c85
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_FSL_ENETC) += fsl-enetc.o
fsl-enetc-$(CONFIG_FSL_ENETC) += enetc.o enetc_cbdr.o enetc_ethtool.o
fsl-enetc-$(CONFIG_PCI_IOV) += enetc_msg.o
fsl-enetc-objs := enetc_pf.o $(fsl-enetc-y)

obj-$(CONFIG_FSL_ENETC_VF) += fsl-enetc-vf.o
+14 −0
Original line number Diff line number Diff line
@@ -103,6 +103,12 @@ struct enetc_cbdr {
#define ENETC_TXBD(BDR, i) (&(((union enetc_tx_bd *)((BDR).bd_base))[i]))
#define ENETC_RXBD(BDR, i) (&(((union enetc_rx_bd *)((BDR).bd_base))[i]))

struct enetc_msg_swbd {
	void *vaddr;
	dma_addr_t dma;
	int size;
};

#define ENETC_REV1	0x1
enum enetc_errata {
	ENETC_ERR_TXCSUM	= BIT(0),
@@ -173,6 +179,14 @@ struct enetc_ndev_priv {
	phy_interface_t if_mode;
};

/* Messaging */

/* VF-PF set primary MAC address message format */
struct enetc_msg_cmd_set_primary_mac {
	struct enetc_msg_cmd_header header;
	struct sockaddr mac;
};

#define ENETC_CBD(R, i)	(&(((struct enetc_cbd *)((R).bd_base))[i]))

#define ENETC_CBDR_TIMEOUT	1000 /* usecs */
+50 −0
Original line number Diff line number Diff line
@@ -34,6 +34,29 @@
#define ENETC_SIPMAR0	0x80
#define ENETC_SIPMAR1	0x84

/* VF-PF Message passing */
#define ENETC_DEFAULT_MSG_SIZE	1024	/* and max size */
/* msg size encoding: default and max msg value of 1024B encoded as 0 */
static inline u32 enetc_vsi_set_msize(u32 size)
{
	return size < ENETC_DEFAULT_MSG_SIZE ? size >> 5 : 0;
}

#define ENETC_PSIMSGRR	0x204
#define ENETC_PSIMSGRR_MR_MASK	GENMASK(2, 1)
#define ENETC_PSIMSGRR_MR(n) BIT((n) + 1) /* n = VSI index */
#define ENETC_PSIVMSGRCVAR0(n)	(0x210 + (n) * 0x8) /* n = VSI index */
#define ENETC_PSIVMSGRCVAR1(n)	(0x214 + (n) * 0x8)

#define ENETC_VSIMSGSR	0x204	/* RO */
#define ENETC_VSIMSGSR_MB	BIT(0)
#define ENETC_VSIMSGSR_MS	BIT(1)
#define ENETC_VSIMSGSNDAR0	0x210
#define ENETC_VSIMSGSNDAR1	0x214

#define ENETC_SIMSGSR_SET_MC(val) ((val) << 16)
#define ENETC_SIMSGSR_GET_MC(val) ((val) >> 16)

/* SI statistics */
#define ENETC_SIROCT	0x300
#define ENETC_SIRFRM	0x308
@@ -418,6 +441,33 @@ static inline void enetc_get_primary_mac_addr(struct enetc_hw *hw, u8 *addr)
/* base index for Rx/Tx interrupts */
#define ENETC_BDR_INT_BASE_IDX	1

/* Messaging */

/* Command completion status */
enum enetc_msg_cmd_status {
	ENETC_MSG_CMD_STATUS_OK,
	ENETC_MSG_CMD_STATUS_FAIL
};

/* VSI-PSI command message types */
enum enetc_msg_cmd_type {
	ENETC_MSG_CMD_MNG_MAC = 1, /* manage MAC address */
	ENETC_MSG_CMD_MNG_RX_MAC_FILTER,/* manage RX MAC table */
	ENETC_MSG_CMD_MNG_RX_VLAN_FILTER /* manage RX VLAN table */
};

/* VSI-PSI command action types */
enum enetc_msg_cmd_action_type {
	ENETC_MSG_CMD_MNG_ADD = 1,
	ENETC_MSG_CMD_MNG_REMOVE
};

/* PSI-VSI command header format */
struct enetc_msg_cmd_header {
	u16 type;	/* command class type */
	u16 id;		/* denotes the specific required action */
};

/* Common H/W utility functions */

static inline void enetc_enable_rxvlan(struct enetc_hw *hw, int si_idx,
+164 −0
Original line number Diff line number Diff line
// 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);
}
+68 −0
Original line number Diff line number Diff line
@@ -366,6 +366,7 @@ static int enetc_pf_set_vf_mac(struct net_device *ndev, int vf, u8 *mac)
{
	struct enetc_ndev_priv *priv = netdev_priv(ndev);
	struct enetc_pf *pf = enetc_si_priv(priv->si);
	struct enetc_vf_state *vf_state;

	if (vf >= pf->total_vfs)
		return -EINVAL;
@@ -373,6 +374,8 @@ static int enetc_pf_set_vf_mac(struct net_device *ndev, int vf, u8 *mac)
	if (!is_valid_ether_addr(mac))
		return -EADDRNOTAVAIL;

	vf_state = &pf->vf_state[vf];
	vf_state->flags |= ENETC_VF_FLAG_PF_SET_MAC;
	enetc_pf_set_primary_mac_addr(&priv->si->hw, vf + 1, mac);
	return 0;
}
@@ -541,6 +544,53 @@ static void enetc_configure_port(struct enetc_pf *pf)
	enetc_port_wr(hw, ENETC_PMR, ENETC_PMR_EN);
}

/* Messaging */
static u16 enetc_msg_pf_set_vf_primary_mac_addr(struct enetc_pf *pf,
						int vf_id)
{
	struct enetc_vf_state *vf_state = &pf->vf_state[vf_id];
	struct enetc_msg_swbd *msg = &pf->rxmsg[vf_id];
	struct enetc_msg_cmd_set_primary_mac *cmd;
	struct device *dev = &pf->si->pdev->dev;
	u16 cmd_id;
	char *addr;

	cmd = (struct enetc_msg_cmd_set_primary_mac *)msg->vaddr;
	cmd_id = cmd->header.id;
	if (cmd_id != ENETC_MSG_CMD_MNG_ADD)
		return ENETC_MSG_CMD_STATUS_FAIL;

	addr = cmd->mac.sa_data;
	if (vf_state->flags & ENETC_VF_FLAG_PF_SET_MAC)
		dev_warn(dev, "Attempt to override PF set mac addr for VF%d\n",
			 vf_id);
	else
		enetc_pf_set_primary_mac_addr(&pf->si->hw, vf_id + 1, addr);

	return ENETC_MSG_CMD_STATUS_OK;
}

void enetc_msg_handle_rxmsg(struct enetc_pf *pf, int vf_id, u16 *status)
{
	struct enetc_msg_swbd *msg = &pf->rxmsg[vf_id];
	struct device *dev = &pf->si->pdev->dev;
	struct enetc_msg_cmd_header *cmd_hdr;
	u16 cmd_type;

	*status = ENETC_MSG_CMD_STATUS_OK;
	cmd_hdr = (struct enetc_msg_cmd_header *)msg->vaddr;
	cmd_type = cmd_hdr->type;

	switch (cmd_type) {
	case ENETC_MSG_CMD_MNG_MAC:
		*status = enetc_msg_pf_set_vf_primary_mac_addr(pf, vf_id);
		break;
	default:
		dev_err(dev, "command not supported (cmd_type: 0x%x)\n",
			cmd_type);
	}
}

#ifdef CONFIG_PCI_IOV
static int enetc_sriov_configure(struct pci_dev *pdev, int num_vfs)
{
@@ -549,11 +599,26 @@ static int enetc_sriov_configure(struct pci_dev *pdev, int num_vfs)
	int err;

	if (!num_vfs) {
		enetc_msg_psi_free(pf);
		kfree(pf->vf_state);
		pf->num_vfs = 0;
		pci_disable_sriov(pdev);
	} else {
		pf->num_vfs = num_vfs;

		pf->vf_state = kcalloc(num_vfs, sizeof(struct enetc_vf_state),
				       GFP_KERNEL);
		if (!pf->vf_state) {
			pf->num_vfs = 0;
			return -ENOMEM;
		}

		err = enetc_msg_psi_init(pf);
		if (err) {
			dev_err(&pdev->dev, "enetc_msg_psi_init (%d)\n", err);
			goto err_msg_psi;
		}

		err = pci_enable_sriov(pdev, num_vfs);
		if (err) {
			dev_err(&pdev->dev, "pci_enable_sriov err %d\n", err);
@@ -564,6 +629,9 @@ static int enetc_sriov_configure(struct pci_dev *pdev, int num_vfs)
	return num_vfs;

err_en_sriov:
	enetc_msg_psi_free(pf);
err_msg_psi:
	kfree(pf->vf_state);
	pf->num_vfs = 0;

	return err;
Loading