diff --git a/drivers/net/ethernet/freescale/enetc/Kconfig b/drivers/net/ethernet/freescale/enetc/Kconfig index 117038104b69..db5c17a44613 100644 --- a/drivers/net/ethernet/freescale/enetc/Kconfig +++ b/drivers/net/ethernet/freescale/enetc/Kconfig @@ -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. diff --git a/drivers/net/ethernet/freescale/enetc/Makefile b/drivers/net/ethernet/freescale/enetc/Makefile index f1c5ad45fd76..10ab6694c314 100644 --- a/drivers/net/ethernet/freescale/enetc/Makefile +++ b/drivers/net/ethernet/freescale/enetc/Makefile @@ -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 diff --git a/drivers/net/ethernet/freescale/enetc/enetc.h b/drivers/net/ethernet/freescale/enetc/enetc.h index e691144e8756..04a5dd5ea6c7 100644 --- a/drivers/net/ethernet/freescale/enetc/enetc.h +++ b/drivers/net/ethernet/freescale/enetc/enetc.h @@ -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, diff --git a/drivers/net/ethernet/freescale/enetc/enetc4_pf.c b/drivers/net/ethernet/freescale/enetc/enetc4_pf.c index 56899f2254aa..4e771f852358 100644 --- a/drivers/net/ethernet/freescale/enetc/enetc4_pf.c +++ b/drivers/net/ethernet/freescale/enetc/enetc4_pf.c @@ -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; diff --git a/drivers/net/ethernet/freescale/enetc/enetc_cbdr.c b/drivers/net/ethernet/freescale/enetc/enetc_cbdr.c index a635bfdc30af..e4a393a8a58e 100644 --- a/drivers/net/ethernet/freescale/enetc/enetc_cbdr.c +++ b/drivers/net/ethernet/freescale/enetc/enetc_cbdr.c @@ -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; diff --git a/drivers/net/ethernet/freescale/enetc/enetc_hw.h b/drivers/net/ethernet/freescale/enetc/enetc_hw.h index e58cc81d199d..bf99b65d7598 100644 --- a/drivers/net/ethernet/freescale/enetc/enetc_hw.h +++ b/drivers/net/ethernet/freescale/enetc/enetc_hw.h @@ -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 diff --git a/drivers/net/ethernet/freescale/enetc/enetc_mailbox.h b/drivers/net/ethernet/freescale/enetc/enetc_mailbox.h new file mode 100644 index 000000000000..d9677da38989 --- /dev/null +++ b/drivers/net/ethernet/freescale/enetc/enetc_mailbox.h @@ -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 + +#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 diff --git a/drivers/net/ethernet/freescale/enetc/enetc_msg.c b/drivers/net/ethernet/freescale/enetc/enetc_msg.c index c09635e7eb3d..edc1277bb586 100644 --- a/drivers/net/ethernet/freescale/enetc/enetc_msg.c +++ b/drivers/net/ethernet/freescale/enetc/enetc_msg.c @@ -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); diff --git a/drivers/net/ethernet/freescale/enetc/enetc_pf.c b/drivers/net/ethernet/freescale/enetc/enetc_pf.c index 3206b3daa1a0..2d687bb8c3a0 100644 --- a/drivers/net/ethernet/freescale/enetc/enetc_pf.c +++ b/drivers/net/ethernet/freescale/enetc/enetc_pf.c @@ -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); } diff --git a/drivers/net/ethernet/freescale/enetc/enetc_pf.h b/drivers/net/ethernet/freescale/enetc/enetc_pf.h index 35d484858c7b..285b7e5c48fd 100644 --- a/drivers/net/ethernet/freescale/enetc/enetc_pf.h +++ b/drivers/net/ethernet/freescale/enetc/enetc_pf.h @@ -5,8 +5,6 @@ #include #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); diff --git a/drivers/net/ethernet/freescale/enetc/enetc_pf_common.c b/drivers/net/ethernet/freescale/enetc/enetc_pf_common.c index 76263b8566bb..6e5d2f869915 100644 --- a/drivers/net/ethernet/freescale/enetc/enetc_pf_common.c +++ b/drivers/net/ethernet/freescale/enetc/enetc_pf_common.c @@ -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"); diff --git a/drivers/net/ethernet/freescale/enetc/enetc_pf_common.h b/drivers/net/ethernet/freescale/enetc/enetc_pf_common.h index 96d4840a3107..57d2e0ebd2b0 100644 --- a/drivers/net/ethernet/freescale/enetc/enetc_pf_common.h +++ b/drivers/net/ethernet/freescale/enetc/enetc_pf_common.h @@ -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 diff --git a/drivers/net/ethernet/freescale/enetc/enetc_vf.c b/drivers/net/ethernet/freescale/enetc/enetc_vf.c index df8e95cc47d0..9cdb0a4d6baf 100644 --- a/drivers/net/ethernet/freescale/enetc/enetc_vf.c +++ b/drivers/net/ethernet/freescale/enetc/enetc_vf.c @@ -1,12 +1,35 @@ // SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) /* Copyright 2017-2019 NXP */ +#include #include #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);