Merge branch 'net-enetc-prepare-for-enetc-v4-vf-support'

Wei Fang says:

====================
net: enetc: Prepare for ENETC v4 VF support

This patch series refactors and extends the ENETC driver infrastructure
to prepare for upcoming ENETC v4 Virtual Function (VF) support. The main
focus is on code commonization, improved VF-PF communication, and dynamic
resource allocation.

The ENETC IP has evolved across different revisions, and the existing
driver architecture was primarily designed around v1 hardware. To support
v4 VFs efficiently, we need to share common code between PF drivers of
different IP versions while maintaining compatibility.

Key changes in this series:

1. VF-PF Messaging Infrastructure:
   - Convert mailbox messages to new formats
   - Use read_poll_timeout() for simplifying VF mailbox polling
   - Add support for IP minor revision query via messaging

2. Code Commonization:
   - Relocate SR-IOV configuration helpers to common PF code
   - Move VF message handlers to dedicated enetc_msg.c
   - Integrate enetc_msg.c into enetc-pf-common driver

3. CBDR (Control Buffer Descriptor Ring) Improvements:
   - Align v1 CBDR API with v4 for VF driver sharing
   - Add CBDR setup/teardown hooks to enetc_si_ops

4. Dynamic Resource Management:
   - Dynamically allocate rxmsg based on actual VF count
   - Use MADDR_TYPE constant for MAC filter array sizing

5. Generic SR-IOV Initialization:
   - Add generic helper to initialize SR-IOV resources

This refactoring lays the groundwork for cleanly integrating ENETC v4 VF
support in subsequent patch series, allowing code reuse between v1 and v4
PF drivers while maintaining a clean separation of version-specific
logic.

v1: https://lore.kernel.org/imx/20260511080805.2052495-1-wei.fang@nxp.com/
====================

Link: https://patch.msgid.link/20260522092438.1264020-1-wei.fang@nxp.com
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
This commit is contained in:
Paolo Abeni 2026-05-26 13:20:15 +02:00
commit da1ab4ec55
13 changed files with 580 additions and 213 deletions

View File

@ -10,6 +10,7 @@ config FSL_ENETC_CORE
config NXP_ENETC_PF_COMMON
tristate
select CRC_ITU_T
help
This module supports common functionality between drivers of
different versions of NXP ENETC PF controllers.
@ -70,6 +71,7 @@ config FSL_ENETC_VF
select FSL_ENETC_MDIO
select PHYLINK
select DIMLIB
select CRC_ITU_T
help
This driver supports NXP ENETC gigabit ethernet controller PCIe
virtual function (VF) devices enabled by the ENETC PF driver.

View File

@ -5,13 +5,13 @@ fsl-enetc-core-y := enetc.o enetc_cbdr.o enetc_ethtool.o
obj-$(CONFIG_NXP_ENETC_PF_COMMON) += nxp-enetc-pf-common.o
nxp-enetc-pf-common-y := enetc_pf_common.o
nxp-enetc-pf-common-$(CONFIG_PCI_IOV) += enetc_msg.o
obj-$(CONFIG_NXP_NETC_LIB) += nxp-netc-lib.o
nxp-netc-lib-y := ntmp.o
obj-$(CONFIG_FSL_ENETC) += fsl-enetc.o
fsl-enetc-y := enetc_pf.o
fsl-enetc-$(CONFIG_PCI_IOV) += enetc_msg.o
fsl-enetc-$(CONFIG_FSL_ENETC_QOS) += enetc_qos.o
obj-$(CONFIG_NXP_ENETC4) += nxp-enetc4.o

View File

@ -16,6 +16,7 @@
#include "enetc_hw.h"
#include "enetc4_hw.h"
#include "enetc_mailbox.h"
#define ENETC_MAC_MAXFRM_SIZE 9600
#define ENETC_MAX_MTU (ENETC_MAC_MAXFRM_SIZE - \
@ -257,12 +258,6 @@ static inline union enetc_rx_bd *enetc_rxbd_ext(union enetc_rx_bd *rxbd)
return ++rxbd;
}
struct enetc_msg_swbd {
void *vaddr;
dma_addr_t dma;
int size;
};
#define ENETC_REV1 0x1
#define ENETC_REV4 0x4
@ -302,6 +297,8 @@ struct enetc_si;
struct enetc_si_ops {
int (*get_rss_table)(struct enetc_si *si, u32 *table, int count);
int (*set_rss_table)(struct enetc_si *si, const u32 *table, int count);
int (*setup_cbdr)(struct enetc_si *si);
void (*teardown_cbdr)(struct enetc_si *si);
};
/* PCI IEP device data */
@ -490,14 +487,6 @@ struct enetc_ndev_priv {
u64 sysclk_freq; /* NETC system clock frequency */
};
/* 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 */
@ -550,9 +539,8 @@ void enetc_mm_link_state_update(struct enetc_ndev_priv *priv, bool link);
void enetc_mm_commit_preemptible_tcs(struct enetc_ndev_priv *priv);
/* control buffer descriptor ring (CBDR) */
int enetc_setup_cbdr(struct device *dev, struct enetc_hw *hw, int bd_count,
struct enetc_cbdr *cbdr);
void enetc_teardown_cbdr(struct enetc_cbdr *cbdr);
int enetc_setup_cbdr(struct enetc_si *si);
void enetc_teardown_cbdr(struct enetc_si *si);
int enetc4_setup_cbdr(struct enetc_si *si);
void enetc4_teardown_cbdr(struct enetc_si *si);
int enetc_set_mac_flt_entry(struct enetc_si *si, int index,

View File

@ -17,10 +17,6 @@
#define ENETC_MAC_FILTER_TYPE_ALL (ENETC_MAC_FILTER_TYPE_UC | \
ENETC_MAC_FILTER_TYPE_MC)
struct enetc_mac_addr {
u8 addr[ETH_ALEN];
};
static void enetc4_get_port_caps(struct enetc_pf *pf)
{
struct enetc_hw *hw = &pf->si->hw;

View File

@ -3,10 +3,12 @@
#include "enetc.h"
int enetc_setup_cbdr(struct device *dev, struct enetc_hw *hw, int bd_count,
struct enetc_cbdr *cbdr)
int enetc_setup_cbdr(struct enetc_si *si)
{
int size = bd_count * sizeof(struct enetc_cbd);
int size = ENETC_CBDR_DEFAULT_SIZE * sizeof(struct enetc_cbd);
struct enetc_cbdr *cbdr = &si->cbd_ring;
struct device *dev = &si->pdev->dev;
struct enetc_hw *hw = &si->hw;
cbdr->bd_base = dma_alloc_coherent(dev, size, &cbdr->bd_dma_base,
GFP_KERNEL);
@ -23,7 +25,7 @@ int enetc_setup_cbdr(struct device *dev, struct enetc_hw *hw, int bd_count,
cbdr->next_to_clean = 0;
cbdr->next_to_use = 0;
cbdr->dma_dev = dev;
cbdr->bd_count = bd_count;
cbdr->bd_count = ENETC_CBDR_DEFAULT_SIZE;
cbdr->pir = hw->reg + ENETC_SICBDRPIR;
cbdr->cir = hw->reg + ENETC_SICBDRCIR;
@ -46,13 +48,15 @@ int enetc_setup_cbdr(struct device *dev, struct enetc_hw *hw, int bd_count,
}
EXPORT_SYMBOL_GPL(enetc_setup_cbdr);
void enetc_teardown_cbdr(struct enetc_cbdr *cbdr)
void enetc_teardown_cbdr(struct enetc_si *si)
{
int size = cbdr->bd_count * sizeof(struct enetc_cbd);
struct enetc_cbdr *cbdr = &si->cbd_ring;
int size;
/* disable ring */
enetc_wr_reg(cbdr->mr, 0);
size = cbdr->bd_count * sizeof(struct enetc_cbd);
dma_free_coherent(cbdr->dma_dev, size, cbdr->bd_base,
cbdr->bd_dma_base);
cbdr->bd_base = NULL;

View File

@ -681,7 +681,6 @@ union enetc_rx_bd {
#define ENETC_MAC_ADDR_FILT_CNT 8 /* # of supported entries per port */
#define EMETC_MAC_ADDR_FILT_RES 3 /* # of reserved entries at the beginning */
#define ENETC_MAX_NUM_VFS 2
#define ENETC_CBD_FLAGS_SF BIT(7) /* short format */
#define ENETC_CBD_STATUS_MASK 0xf

View File

@ -0,0 +1,170 @@
/* SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) */
/*
* Copyright 2025-2026 NXP
*
* The VSI-to-PSI message generic format:
*
* OFFSET 0 16 24 31
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* 0x0 | CRC16 (big-endian) | CLASS ID | CMD ID |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* 0x4 | PROTO VER | LEN | RESV | COOKIE| RESV |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* 0x8 | RESV |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* 0xc | RESV |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* 0x10 | |
* 0x14 | |
* 0x18 | Message Body |
* 0x1c | |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* 0x20 | |
* ~ | Extended Message Body: LEN x 32B |
* 0x3e0 | |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*
* Field Descriptions:
* CRC16 (16-bit): Big endian, CRC16 CCITT-FALSE algorithm, It provides the
* equivalent data integrity check functionality as the FCS for standard
* Ethernet frames.
*
* CLASS ID (8-bit) and CMD ID (8-bit): These are 8-bit fields identifying
* the command class and the class-specific operations supported. For more
* details, please refer to the definitions of the relevant class ID and
* cmd ID in this document.
*
* PROTO VER (8-bit): Supported VSI-PSI command protocol version. Currently
* only support version 0. To be incremented for future protocol extensions.
*
* LEN (8-bit): Extended message body length in increments of 32B. The upper
* limit is given by the physical implementation of the NETC VSI-PSI Messaging
* mechanism that supports message sizes of up to 1024B (including headers),
* that are multiple of 32B.
*
* COOKIE (4-bit): Optional parameter, which, if not 0, indicates that the
* command should be execute asynchronously on PSI side. If COOKIE is not 0
* and the command cannot be executed instantly on the PSI side (it would
* take longer time to complete), the PSI may enqueue the request in a command
* queue of up to 15 entries per VSI and, later after command execution, the
* PSI returns the COOKIE to VSI as part of an asynchronous notification
* message that indicates the command completion status. If COOKIE is 0 then
* the command is considered as blocking, the PSI will wait for the execution
* of the command to complete before updating the PSIMSGRR[MC] field with the
* corresponding return code.
*
* The PSI-to-VSI message generic format:
* 0 4 8 12 15
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* | COOKIE | CLASS CODE | CLASS ID |
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
*
* The PSI to VSI message format is mapped to the following PSI message
* registers/fields, depending on use case:
* 1) PSI_RX_control: PSIMSGRR[MC] - for VSI command return code messages
* (blocking requests), and
* 2) PSI_TX_control: PSIMSGSR[MC] - for PSI to VSI notification messages
* (async mode)
*
* Note that for some GET messages, there is no COOKIE field, and the CLASS
* CODE field is expanded to 8 bits.
*/
#ifndef __ENETC_MAILBOX_H
#define __ENETC_MAILBOX_H
#include <linux/crc-itu-t.h>
#define ENETC_CRC_INIT 0xffff
#define ENETC_MSG_ALIGN 32
/* s indicates the size of the message */
#define ENETC_MSG_EXT_BODY_LEN(s) ((s) / ENETC_MSG_ALIGN - 1)
/* l indicates the extended body len (LEN field) of the message */
#define ENETC_MSG_SIZE(l) (((l) + 1) * ENETC_MSG_ALIGN)
/* The cookie filed of VSI-to-PSI message */
#define ENETC_VF_MSG_COOKIE GENMASK(3, 0)
/* The fileds of PSI-to-VSI message, the message is only 16-bit */
#define ENETC_PF_MSG_COOKIE GENMASK(3, 0)
#define ENETC_PF_MSG_CLASS_CODE GENMASK(7, 4)
/* Extend the class code to 8-bit for GET messages without COOKIE */
#define ENETC_PF_MSG_CLASS_CODE_U8 GENMASK(7, 0)
#define ENETC_PF_MSG_CLASS_ID GENMASK(15, 8)
enum enetc_msg_class_id {
/* Class ID for PSI-to-VSI messages */
ENETC_MSG_CLASS_ID_CMD_SUCCESS = 1,
ENETC_MSG_CLASS_ID_PERMISSION_DENY,
ENETC_MSG_CLASS_ID_CMD_NOT_SUPPORT,
ENETC_MSG_CLASS_ID_PSI_BUSY,
ENETC_MSG_CLASS_ID_CRC_ERROR,
ENETC_MSG_CLASS_ID_PROTO_NOT_SUPPORT,
ENETC_MSG_CLASS_ID_INVALID_MSG_LEN,
ENETC_MSG_CLASS_ID_CMD_TIMEOUT,
ENETC_MSG_CLASS_ID_CMD_NOT_PERMITTED,
ENETC_MSG_CLASS_ID_CMD_FAIL, /* Generic error code for failure */
ENETC_MSG_CLASS_ID_CMD_DEFERRED = 0xf,
/* Common Class ID for PSI-to-VSI and VSI-to-PSI messages */
ENETC_MSG_CLASS_ID_MAC_FILTER = 0x20,
ENETC_MSG_CLASS_ID_IP_REVISION = 0xf0,
};
enum enetc_msg_mac_filter_cmd_id {
ENETC_MSG_SET_PRIMARY_MAC,
};
enum enetc_msg_ip_revision_cmd_id {
ENETC_MSG_GET_IP_MN = 1,
};
/* Class-specific error return codes of MAC filter */
enum enetc_mac_filter_class_code {
ENETC_MF_CLASS_CODE_INVALID_MAC,
};
struct enetc_msg_swbd {
void *vaddr;
dma_addr_t dma;
int size;
};
/* The generic VSI-to-PSI message header */
struct enetc_msg_header {
__be16 crc16;
u8 class_id;
u8 cmd_id;
u8 proto_ver;
u8 len;
u8 resv0;
u8 cookie;
u8 resv2[8];
};
struct enetc_mac_addr {
u8 addr[ETH_ALEN]; /* Network byte order */
};
/* Message format of class_id 0x20 for exact MAC filter.
* cmd_id 0x0: set primary MAC
* cmd_id 0x1: Add entries to MAC address filter table
* cmd_id 0x2: Delete entries from MAC address filter table
* Note that cmd_id 0x1 and 0x2 are not supported yet.
*/
struct enetc_msg_mac_exact_filter {
struct enetc_msg_header hdr;
u8 mac_cnt; /* No need to set for cmd_id 0 */
u8 resv[3];
struct enetc_mac_addr mac[];
};
/* The generic message format applies to the following messages:
* Get IP revision message, class_id 0xf0.
* cmd_id 1: get IP minor revision
*/
struct enetc_msg_generic {
struct enetc_msg_header hdr;
u8 resv[16];
};
#endif

View File

@ -1,7 +1,12 @@
// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
/* Copyright 2017-2019 NXP */
#include "enetc_pf.h"
#include "enetc_pf_common.h"
#define ENETC_PF_MSG_SUCCESS FIELD_PREP(ENETC_PF_MSG_CLASS_ID, \
ENETC_MSG_CLASS_ID_CMD_SUCCESS)
#define ENETC_PF_MSG_NOTSUPP FIELD_PREP(ENETC_PF_MSG_CLASS_ID, \
ENETC_MSG_CLASS_ID_CMD_NOT_SUPPORT)
static void enetc_msg_disable_mr_int(struct enetc_pf *pf)
{
@ -35,6 +40,167 @@ static irqreturn_t enetc_msg_psi_msix(int irq, void *data)
return IRQ_HANDLED;
}
/* Messaging */
static bool enetc_msg_check_crc16(void *msg_addr, u32 msg_size)
{
u32 data_size = msg_size - 2;
u8 *data_buf = msg_addr + 2;
u16 verify_val;
verify_val = crc_itu_t(ENETC_CRC_INIT, data_buf, data_size);
verify_val = crc_itu_t(verify_val, msg_addr, 2);
if (verify_val)
return false;
return true;
}
static u16 enetc_msg_set_vf_primary_mac_addr(struct enetc_pf *pf, int vf_id,
void *vf_msg)
{
struct enetc_vf_state *vf_state = &pf->vf_state[vf_id];
struct enetc_msg_mac_exact_filter *msg = vf_msg;
struct device *dev = &pf->si->pdev->dev;
char *addr = msg->mac[0].addr;
if (!is_valid_ether_addr(addr)) {
dev_err_ratelimited(dev, "VF%d attempted to set invalid MAC\n",
vf_id);
return (FIELD_PREP(ENETC_PF_MSG_CLASS_ID,
ENETC_MSG_CLASS_ID_MAC_FILTER) |
FIELD_PREP(ENETC_PF_MSG_CLASS_CODE,
ENETC_MF_CLASS_CODE_INVALID_MAC));
}
mutex_lock(&vf_state->lock);
if (vf_state->flags & ENETC_VF_FLAG_PF_SET_MAC) {
mutex_unlock(&vf_state->lock);
dev_err_ratelimited(dev,
"VF%d attempted to override PF set MAC\n",
vf_id);
return FIELD_PREP(ENETC_PF_MSG_CLASS_ID,
ENETC_MSG_CLASS_ID_CMD_NOT_PERMITTED);
}
enetc_set_si_hw_addr(pf, vf_id + 1, addr);
mutex_unlock(&vf_state->lock);
return ENETC_PF_MSG_SUCCESS;
}
static u16 enetc_msg_handle_mac_filter(struct enetc_pf *pf, int vf_id,
void *vf_msg)
{
struct enetc_msg_header *msg_hdr = vf_msg;
switch (msg_hdr->cmd_id) {
case ENETC_MSG_SET_PRIMARY_MAC:
return enetc_msg_set_vf_primary_mac_addr(pf, vf_id, vf_msg);
default:
return ENETC_PF_MSG_NOTSUPP;
}
}
static u16 enetc_msg_handle_ip_revision(struct enetc_pf *pf, void *vf_msg)
{
struct enetc_msg_header *msg_hdr = vf_msg;
switch (msg_hdr->cmd_id) {
case ENETC_MSG_GET_IP_MN:
return (FIELD_PREP(ENETC_PF_MSG_CLASS_ID,
ENETC_MSG_CLASS_ID_IP_REVISION) |
FIELD_PREP(ENETC_PF_MSG_CLASS_CODE_U8,
pf->si->revision));
default:
return ENETC_PF_MSG_NOTSUPP;
}
}
static void enetc_msg_handle_rxmsg(struct enetc_pf *pf, int vf_id,
u16 *pf_msg)
{
struct enetc_msg_swbd *msg_swbd = &pf->rxmsg[vf_id];
struct enetc_msg_header *msg_hdr = msg_swbd->vaddr;
u32 msg_size = ENETC_MSG_SIZE(msg_hdr->len);
struct device *dev = &pf->si->pdev->dev;
u8 *msg;
if (msg_size > ENETC_DEFAULT_MSG_SIZE) {
dev_err_ratelimited(dev,
"Invalid message size: %u\n", msg_size);
*pf_msg = FIELD_PREP(ENETC_PF_MSG_CLASS_ID,
ENETC_MSG_CLASS_ID_INVALID_MSG_LEN);
return;
}
/* To prevent malicious VF from tampering with the original data by
* sending new messages after passing the check, the DMA buffer data
* is copied to the msg buffer before validation.
*/
msg = kzalloc_objs(*msg, msg_size);
if (!msg) {
dev_err_ratelimited(dev,
"Failed to allocate message buffer\n");
*pf_msg = FIELD_PREP(ENETC_PF_MSG_CLASS_ID,
ENETC_MSG_CLASS_ID_CMD_FAIL);
return;
}
memcpy(msg, msg_swbd->vaddr, msg_size);
if (!enetc_msg_check_crc16(msg, msg_size)) {
dev_err_ratelimited(dev, "VSI to PSI Message CRC16 error\n");
*pf_msg = FIELD_PREP(ENETC_PF_MSG_CLASS_ID,
ENETC_MSG_CLASS_ID_CRC_ERROR);
goto free_msg;
}
/* Default to not supported */
*pf_msg = ENETC_PF_MSG_NOTSUPP;
msg_hdr = (struct enetc_msg_header *)msg;
/* Currently, asynchronous actions are not supported */
if (FIELD_GET(ENETC_VF_MSG_COOKIE, msg_hdr->cookie)) {
dev_err_ratelimited(dev,
"Cookie field is not supported yet\n");
goto free_msg;
}
/* Currently only support protocol version 0 */
if (msg_hdr->proto_ver) {
dev_err_ratelimited(dev, "Unsupported protocol version %u\n",
msg_hdr->proto_ver);
goto free_msg;
}
/* The new messages are currently only supported on ENETC v4. If v1
* requires them, the current restriction can be lifted.
*/
if (is_enetc_rev1(pf->si) &&
!(msg_hdr->class_id == ENETC_MSG_CLASS_ID_MAC_FILTER &&
msg_hdr->cmd_id == ENETC_MSG_SET_PRIMARY_MAC)) {
dev_err_ratelimited(dev, "Unsupported message for ENETC v1\n");
goto free_msg;
}
switch (msg_hdr->class_id) {
case ENETC_MSG_CLASS_ID_MAC_FILTER:
*pf_msg = enetc_msg_handle_mac_filter(pf, vf_id, msg);
break;
case ENETC_MSG_CLASS_ID_IP_REVISION:
*pf_msg = enetc_msg_handle_ip_revision(pf, msg);
break;
default:
dev_err_ratelimited(dev,
"Unsupported message class ID: 0x%x\n",
msg_hdr->class_id);
}
free_msg:
kfree(msg);
}
static void enetc_msg_task(struct work_struct *work)
{
struct enetc_pf *pf = container_of(work, struct enetc_pf, msg_task);
@ -113,7 +279,7 @@ static void enetc_msg_free_mbx(struct enetc_si *si, int idx)
memset(msg, 0, sizeof(*msg));
}
int enetc_msg_psi_init(struct enetc_pf *pf)
static int enetc_msg_psi_init(struct enetc_pf *pf)
{
struct enetc_si *si = pf->si;
int vector, i, err;
@ -153,7 +319,7 @@ int enetc_msg_psi_init(struct enetc_pf *pf)
return err;
}
void enetc_msg_psi_free(struct enetc_pf *pf)
static void enetc_msg_psi_free(struct enetc_pf *pf)
{
struct enetc_si *si = pf->si;
int i;
@ -172,3 +338,40 @@ void enetc_msg_psi_free(struct enetc_pf *pf)
for (i = 0; i < pf->num_vfs; i++)
enetc_msg_free_mbx(si, i);
}
int enetc_sriov_configure(struct pci_dev *pdev, int num_vfs)
{
struct enetc_si *si = pci_get_drvdata(pdev);
struct enetc_pf *pf = enetc_si_priv(si);
int err;
if (!num_vfs) {
pci_disable_sriov(pdev);
enetc_msg_psi_free(pf);
pf->num_vfs = 0;
} else {
pf->num_vfs = num_vfs;
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);
goto err_en_sriov;
}
}
return num_vfs;
err_en_sriov:
enetc_msg_psi_free(pf);
err_msg_psi:
pf->num_vfs = 0;
return err;
}
EXPORT_SYMBOL_GPL(enetc_sriov_configure);

View File

@ -480,121 +480,6 @@ 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, void *msg)
{
struct enetc_vf_state *vf_state = &pf->vf_state[vf_id];
struct enetc_msg_cmd_set_primary_mac *cmd = msg;
struct device *dev = &pf->si->pdev->dev;
u16 cmd_id = cmd->header.id;
char *addr;
if (cmd_id != ENETC_MSG_CMD_MNG_ADD)
return ENETC_MSG_CMD_STATUS_FAIL;
addr = cmd->mac.sa_data;
if (!is_valid_ether_addr(addr)) {
dev_err_ratelimited(dev, "VF%d attempted to set invalid MAC\n",
vf_id);
return ENETC_MSG_CMD_STATUS_FAIL;
}
mutex_lock(&vf_state->lock);
if (vf_state->flags & ENETC_VF_FLAG_PF_SET_MAC) {
mutex_unlock(&vf_state->lock);
dev_err_ratelimited(dev,
"VF%d attempted to override PF set MAC\n",
vf_id);
return ENETC_MSG_CMD_STATUS_FAIL;
}
enetc_pf_set_primary_mac_addr(&pf->si->hw, vf_id + 1, addr);
mutex_unlock(&vf_state->lock);
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_swbd = &pf->rxmsg[vf_id];
struct device *dev = &pf->si->pdev->dev;
struct enetc_msg_cmd_header *cmd_hdr;
u16 cmd_type;
u8 *msg;
msg = kzalloc_objs(*msg, msg_swbd->size);
if (!msg) {
dev_err_ratelimited(dev,
"Failed to allocate message buffer\n");
*status = ENETC_MSG_CMD_STATUS_FAIL;
return;
}
/* Currently, only ENETC_MSG_CMD_MNG_MAC command is supported, so
* only sizeof(struct enetc_msg_cmd_set_primary_mac) bytes need to
* be copied. This data already includes the cmd_type field, so it
* can correctly return an error code.
*/
memcpy(msg, msg_swbd->vaddr,
sizeof(struct enetc_msg_cmd_set_primary_mac));
cmd_hdr = (struct enetc_msg_cmd_header *)msg;
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, msg);
break;
default:
*status = ENETC_MSG_CMD_STATUS_FAIL;
dev_err_ratelimited(dev,
"command not supported (cmd_type: 0x%x)\n",
cmd_type);
}
kfree(msg);
}
#ifdef CONFIG_PCI_IOV
static int enetc_sriov_configure(struct pci_dev *pdev, int num_vfs)
{
struct enetc_si *si = pci_get_drvdata(pdev);
struct enetc_pf *pf = enetc_si_priv(si);
int err;
if (!num_vfs) {
pci_disable_sriov(pdev);
enetc_msg_psi_free(pf);
pf->num_vfs = 0;
} else {
pf->num_vfs = num_vfs;
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);
goto err_en_sriov;
}
}
return num_vfs;
err_en_sriov:
enetc_msg_psi_free(pf);
err_msg_psi:
pf->num_vfs = 0;
return err;
}
#else
#define enetc_sriov_configure(pdev, num_vfs) (void)0
#endif
static int enetc_pf_set_features(struct net_device *ndev,
netdev_features_t features)
{
@ -917,8 +802,7 @@ static struct enetc_si *enetc_psi_create(struct pci_dev *pdev)
goto out_pci_remove;
}
err = enetc_setup_cbdr(&pdev->dev, &si->hw, ENETC_CBDR_DEFAULT_SIZE,
&si->cbd_ring);
err = enetc_setup_cbdr(si);
if (err)
goto out_pci_remove;
@ -937,7 +821,7 @@ static struct enetc_si *enetc_psi_create(struct pci_dev *pdev)
return si;
out_teardown_cbdr:
enetc_teardown_cbdr(&si->cbd_ring);
enetc_teardown_cbdr(si);
out_pci_remove:
enetc_pci_remove(pdev);
out:
@ -948,7 +832,7 @@ static void enetc_psi_destroy(struct pci_dev *pdev)
{
struct enetc_si *si = pci_get_drvdata(pdev);
enetc_teardown_cbdr(&si->cbd_ring);
enetc_teardown_cbdr(si);
enetc_pci_remove(pdev);
}
@ -988,18 +872,9 @@ static int enetc_pf_probe(struct pci_dev *pdev,
pf->si = si;
pf->ops = &enetc_pf_ops;
pf->total_vfs = pci_sriov_get_totalvfs(pdev);
if (pf->total_vfs) {
pf->vf_state = kzalloc_objs(struct enetc_vf_state,
pf->total_vfs);
if (!pf->vf_state) {
err = -ENOMEM;
goto err_alloc_vf_state;
}
for (int i = 0; i < pf->total_vfs; i++)
mutex_init(&pf->vf_state[i].lock);
}
err = enetc_init_sriov_resources(pf);
if (err)
goto err_init_sriov_resources;
err = enetc_setup_mac_addresses(node, pf);
if (err)
@ -1077,8 +952,7 @@ static int enetc_pf_probe(struct pci_dev *pdev,
free_netdev(ndev);
err_alloc_netdev:
err_setup_mac_addresses:
kfree(pf->vf_state);
err_alloc_vf_state:
err_init_sriov_resources:
enetc_psi_destroy(pdev);
err_psi_create:
return err;
@ -1105,8 +979,6 @@ static void enetc_pf_remove(struct pci_dev *pdev)
enetc_free_si_resources(priv);
free_netdev(si->ndev);
kfree(pf->vf_state);
enetc_psi_destroy(pdev);
}

View File

@ -5,8 +5,6 @@
#include <linux/phylink.h>
#define ENETC_PF_NUM_RINGS 8
#define ENETC_MAX_NUM_MAC_FLT ((ENETC_MAX_NUM_VFS + 1) * MADDR_TYPE)
#define ENETC_VLAN_HT_SIZE 64
enum enetc_vf_flags {
@ -43,9 +41,9 @@ struct enetc_pf {
int total_vfs; /* max number of VFs, set for PF at probe */
struct enetc_vf_state *vf_state;
struct enetc_mac_filter mac_filter[ENETC_MAX_NUM_MAC_FLT];
struct enetc_mac_filter mac_filter[MADDR_TYPE];
struct enetc_msg_swbd rxmsg[ENETC_MAX_NUM_VFS];
struct enetc_msg_swbd *rxmsg;
struct work_struct msg_task;
char msg_int_name[ENETC_INT_NAME_MAX];
@ -68,7 +66,3 @@ struct enetc_pf {
#define phylink_to_enetc_pf(config) \
container_of((config), struct enetc_pf, phylink_config)
int enetc_msg_psi_init(struct enetc_pf *pf);
void enetc_msg_psi_free(struct enetc_pf *pf);
void enetc_msg_handle_rxmsg(struct enetc_pf *pf, int mbox_id, u16 *status);

View File

@ -7,13 +7,13 @@
#include "enetc_pf_common.h"
static void enetc_set_si_hw_addr(struct enetc_pf *pf, int si,
const u8 *mac_addr)
void enetc_set_si_hw_addr(struct enetc_pf *pf, int si, const u8 *mac_addr)
{
struct enetc_hw *hw = &pf->si->hw;
pf->ops->set_si_primary_mac(hw, si, mac_addr);
}
EXPORT_SYMBOL_GPL(enetc_set_si_hw_addr);
static void enetc_get_si_hw_addr(struct enetc_pf *pf, int si, u8 *mac_addr)
{
@ -435,5 +435,32 @@ int enetc_vlan_rx_del_vid(struct net_device *ndev, __be16 prot, u16 vid)
}
EXPORT_SYMBOL_GPL(enetc_vlan_rx_del_vid);
int enetc_init_sriov_resources(struct enetc_pf *pf)
{
struct device *dev = &pf->si->pdev->dev;
pf->total_vfs = pci_sriov_get_totalvfs(pf->si->pdev);
if (!pf->total_vfs)
return 0;
pf->rxmsg = devm_kcalloc(dev, pf->total_vfs,
sizeof(struct enetc_msg_swbd),
GFP_KERNEL);
if (!pf->rxmsg)
return -ENOMEM;
pf->vf_state = devm_kcalloc(dev, pf->total_vfs,
sizeof(struct enetc_vf_state),
GFP_KERNEL);
if (!pf->vf_state)
return -ENOMEM;
for (int i = 0; i < pf->total_vfs; i++)
mutex_init(&pf->vf_state[i].lock);
return 0;
}
EXPORT_SYMBOL_GPL(enetc_init_sriov_resources);
MODULE_DESCRIPTION("NXP ENETC PF common functionality driver");
MODULE_LICENSE("Dual BSD/GPL");

View File

@ -3,6 +3,7 @@
#include "enetc_pf.h"
void enetc_set_si_hw_addr(struct enetc_pf *pf, int si, const u8 *mac_addr);
int enetc_pf_set_mac_addr(struct net_device *ndev, void *addr);
int enetc_setup_mac_addresses(struct device_node *np, struct enetc_pf *pf);
void enetc_pf_netdev_setup(struct enetc_si *si, struct net_device *ndev,
@ -15,8 +16,18 @@ void enetc_phylink_destroy(struct enetc_ndev_priv *priv);
void enetc_set_default_rss_key(struct enetc_pf *pf);
int enetc_vlan_rx_add_vid(struct net_device *ndev, __be16 prot, u16 vid);
int enetc_vlan_rx_del_vid(struct net_device *ndev, __be16 prot, u16 vid);
int enetc_init_sriov_resources(struct enetc_pf *pf);
static inline u16 enetc_get_ip_revision(struct enetc_hw *hw)
{
return enetc_global_rd(hw, ENETC_G_EIPBRR0) & EIPBRR0_REVISION;
}
#if IS_ENABLED(CONFIG_PCI_IOV)
int enetc_sriov_configure(struct pci_dev *pdev, int num_vfs);
#else
static inline int enetc_sriov_configure(struct pci_dev *pdev, int num_vfs)
{
return 0;
}
#endif

View File

@ -1,12 +1,35 @@
// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
/* Copyright 2017-2019 NXP */
#include <linux/iopoll.h>
#include <linux/module.h>
#include "enetc.h"
#define ENETC_DRV_NAME_STR "ENETC VF driver"
/* Messaging */
/* Note: This function should be called after filling the message body,
* because the CRC16 needs to be calculated after all the data has been
* filled.
*/
static void enetc_msg_fill_common_hdr(struct enetc_msg_swbd *msg_swbd,
u8 class_id, u8 cmd_id, u8 proto_ver,
u8 cookie)
{
struct enetc_msg_header *hdr = msg_swbd->vaddr;
u8 *data_buf = ((u8 *)msg_swbd->vaddr) + 2; /* skip crc16 field */
u32 data_size = msg_swbd->size - 2;
u16 crc16;
hdr->class_id = class_id;
hdr->cmd_id = cmd_id;
hdr->len = ENETC_MSG_EXT_BODY_LEN(msg_swbd->size);
hdr->proto_ver = proto_ver;
hdr->cookie = FIELD_PREP(ENETC_VF_MSG_COOKIE, cookie);
crc16 = crc_itu_t(ENETC_CRC_INIT, data_buf, data_size);
hdr->crc16 = htons(crc16);
}
static void enetc_msg_vsi_write_msg(struct enetc_hw *hw,
struct enetc_msg_swbd *msg)
{
@ -28,8 +51,9 @@ static void enetc_msg_dma_free(struct device *dev, struct enetc_msg_swbd *msg)
static int enetc_msg_vsi_send(struct enetc_si *si, struct enetc_msg_swbd *msg)
{
struct device *dev = &si->pdev->dev;
int timeout = 100;
u32 vsimsgsr;
u16 pf_msg;
int err;
/* The VSI mailbox may be busy if last message was not yet processed
* by PSI. So need to check the mailbox status before sending.
@ -48,53 +72,104 @@ static int enetc_msg_vsi_send(struct enetc_si *si, struct enetc_msg_swbd *msg)
enetc_msg_dma_free(dev, &si->msg);
si->msg = *msg;
enetc_msg_vsi_write_msg(&si->hw, msg);
do {
vsimsgsr = enetc_rd(&si->hw, ENETC_VSIMSGSR);
if (!(vsimsgsr & ENETC_VSIMSGSR_MB))
break;
usleep_range(1000, 2000);
} while (--timeout);
if (!timeout) {
err = read_poll_timeout(enetc_rd, vsimsgsr,
!(vsimsgsr & ENETC_VSIMSGSR_MB),
1000, 200000, false, &si->hw, ENETC_VSIMSGSR);
if (err) {
dev_err(dev, "VSI mailbox timeout\n");
return -ETIMEDOUT;
return err;
}
/* check for message delivery error */
if (vsimsgsr & ENETC_VSIMSGSR_MS) {
dev_err(dev, "VSI command execute error: %d\n",
ENETC_SIMSGSR_GET_MC(vsimsgsr));
dev_err(dev, "Transfer error when copying the data\n");
return -EIO;
}
return 0;
pf_msg = ENETC_SIMSGSR_GET_MC(vsimsgsr);
/* Check the user-defined completion status. */
if (FIELD_GET(ENETC_PF_MSG_CLASS_ID, pf_msg) !=
ENETC_MSG_CLASS_ID_CMD_SUCCESS) {
switch (FIELD_GET(ENETC_PF_MSG_CLASS_ID, pf_msg)) {
case ENETC_MSG_CLASS_ID_PERMISSION_DENY:
/* Intentionally returning early to prevent excessive
* error logs due to permission issues.
*/
return -EACCES;
case ENETC_MSG_CLASS_ID_CMD_NOT_SUPPORT:
case ENETC_MSG_CLASS_ID_PROTO_NOT_SUPPORT:
err = -EOPNOTSUPP;
break;
case ENETC_MSG_CLASS_ID_PSI_BUSY:
err = -EBUSY;
break;
case ENETC_MSG_CLASS_ID_CMD_TIMEOUT:
err = -ETIME;
break;
case ENETC_MSG_CLASS_ID_INVALID_MSG_LEN:
case ENETC_MSG_CLASS_ID_MAC_FILTER:
err = -EINVAL;
break;
case ENETC_MSG_CLASS_ID_CMD_NOT_PERMITTED:
err = -EPERM;
break;
case ENETC_MSG_CLASS_ID_IP_REVISION:
err = FIELD_GET(ENETC_PF_MSG_CLASS_CODE_U8, pf_msg);
break;
case ENETC_MSG_CLASS_ID_CMD_FAIL:
case ENETC_MSG_CLASS_ID_CRC_ERROR:
case ENETC_MSG_CLASS_ID_CMD_DEFERRED:
default:
err = -EIO;
}
}
if (err < 0)
dev_err(dev, "Return error code from PSI: 0x%04x\n", pf_msg);
return err;
}
static int enetc_msg_vsi_set_primary_mac_addr(struct enetc_ndev_priv *priv,
struct sockaddr *saddr)
{
struct enetc_msg_cmd_set_primary_mac *cmd;
struct enetc_msg_swbd msg;
struct enetc_msg_mac_exact_filter *msg;
struct enetc_msg_swbd msg_swbd;
u32 msg_size;
msg.size = ALIGN(sizeof(struct enetc_msg_cmd_set_primary_mac), 64);
msg.vaddr = dma_alloc_coherent(priv->dev, msg.size, &msg.dma,
GFP_KERNEL);
if (!msg.vaddr) {
dev_err(priv->dev, "Failed to alloc Tx msg (size: %d)\n",
msg.size);
msg_size = struct_size(msg, mac, 1);
msg_swbd.size = ALIGN(msg_size, ENETC_MSG_ALIGN);
msg_swbd.vaddr = dma_alloc_coherent(priv->dev, msg_swbd.size,
&msg_swbd.dma, GFP_KERNEL);
if (!msg_swbd.vaddr)
return -ENOMEM;
}
cmd = (struct enetc_msg_cmd_set_primary_mac *)msg.vaddr;
cmd->header.type = ENETC_MSG_CMD_MNG_MAC;
cmd->header.id = ENETC_MSG_CMD_MNG_ADD;
memcpy(&cmd->mac, saddr, sizeof(struct sockaddr));
msg = (struct enetc_msg_mac_exact_filter *)msg_swbd.vaddr;
memcpy(&msg->mac[0].addr, saddr->sa_data, ETH_ALEN);
enetc_msg_fill_common_hdr(&msg_swbd, ENETC_MSG_CLASS_ID_MAC_FILTER,
ENETC_MSG_SET_PRIMARY_MAC, 0, 0);
/* send the command and wait */
return enetc_msg_vsi_send(priv->si, &msg);
return enetc_msg_vsi_send(priv->si, &msg_swbd);
}
static int enetc_vf_get_ip_minor_revision(struct enetc_si *si)
{
struct device *dev = &si->pdev->dev;
struct enetc_msg_swbd msg_swbd;
msg_swbd.size = ALIGN(sizeof(struct enetc_msg_generic),
ENETC_MSG_ALIGN);
msg_swbd.vaddr = dma_alloc_coherent(dev, msg_swbd.size,
&msg_swbd.dma, GFP_KERNEL);
if (!msg_swbd.vaddr)
return -ENOMEM;
enetc_msg_fill_common_hdr(&msg_swbd, ENETC_MSG_CLASS_ID_IP_REVISION,
ENETC_MSG_GET_IP_MN, 0, 0);
return enetc_msg_vsi_send(si, &msg_swbd);
}
static int enetc_vf_set_mac_addr(struct net_device *ndev, void *addr)
@ -148,6 +223,27 @@ static const struct net_device_ops enetc_ndev_ops = {
.ndo_hwtstamp_set = enetc_hwtstamp_set,
};
static void enetc_vf_get_revision(struct enetc_si *si)
{
int ip_mn;
if (is_enetc_rev1(si)) {
si->revision = ENETC_REV_1_0;
return;
}
ip_mn = enetc_vf_get_ip_minor_revision(si);
if (ip_mn >= 0) {
si->revision = (si->pdev->revision << 8) | ip_mn;
return;
}
si->revision = ENETC_REV_4_1;
dev_info(&si->pdev->dev,
"Failed to get revision, use compatible revision: 0x%04x\n",
si->revision);
}
static void enetc_vf_netdev_setup(struct enetc_si *si, struct net_device *ndev,
const struct net_device_ops *ndev_ops)
{
@ -192,12 +288,15 @@ static void enetc_vf_netdev_setup(struct enetc_si *si, struct net_device *ndev,
static const struct enetc_si_ops enetc_vsi_ops = {
.get_rss_table = enetc_get_rss_table,
.set_rss_table = enetc_set_rss_table,
.setup_cbdr = enetc_setup_cbdr,
.teardown_cbdr = enetc_teardown_cbdr,
};
static int enetc_vf_probe(struct pci_dev *pdev,
const struct pci_device_id *ent)
{
struct enetc_ndev_priv *priv;
struct enetc_msg_swbd msg;
struct net_device *ndev;
struct enetc_si *si;
int err;
@ -207,13 +306,13 @@ static int enetc_vf_probe(struct pci_dev *pdev,
return dev_err_probe(&pdev->dev, err, "PCI probing failed\n");
si = pci_get_drvdata(pdev);
si->revision = ENETC_REV_1_0;
enetc_vf_get_revision(si);
si->ops = &enetc_vsi_ops;
err = enetc_get_driver_data(si);
if (err) {
dev_err_probe(&pdev->dev, err,
"Could not get VF driver data\n");
goto err_alloc_netdev;
goto err_get_driver_data;
}
enetc_get_si_caps(si);
@ -231,8 +330,7 @@ static int enetc_vf_probe(struct pci_dev *pdev,
enetc_init_si_rings_params(priv);
err = enetc_setup_cbdr(priv->dev, &si->hw, ENETC_CBDR_DEFAULT_SIZE,
&si->cbd_ring);
err = si->ops->setup_cbdr(si);
if (err)
goto err_setup_cbdr;
@ -268,12 +366,15 @@ static int enetc_vf_probe(struct pci_dev *pdev,
err_alloc_msix:
enetc_free_si_resources(priv);
err_alloc_si_res:
enetc_teardown_cbdr(&si->cbd_ring);
si->ops->teardown_cbdr(si);
err_setup_cbdr:
si->ndev = NULL;
free_netdev(ndev);
err_alloc_netdev:
err_get_driver_data:
msg = si->msg;
enetc_pci_remove(pdev);
enetc_msg_dma_free(&pdev->dev, &msg);
return err;
}
@ -290,7 +391,7 @@ static void enetc_vf_remove(struct pci_dev *pdev)
enetc_free_msix(priv);
enetc_free_si_resources(priv);
enetc_teardown_cbdr(&si->cbd_ring);
si->ops->teardown_cbdr(si);
free_netdev(si->ndev);