summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaolo Abeni <pabeni@redhat.com>2026-09-15 12:08:37 +0200
committerPaolo Abeni <pabeni@redhat.com>2026-09-15 12:08:38 +0200
commit1142eb185b05db61a78130890fc4ed268f4cb4e6 (patch)
treeb998da12ec07c74e8fa8e76adab9c24d34b121c1
parent20d1b74a32d0044bb797da599069b382cb58f1d2 (diff)
parent5822ab0f3e89b35cabe2323dbfd6c2ab883fde4b (diff)
downloadlinux-next-1142eb185b05db61a78130890fc4ed268f4cb4e6.tar.gz
linux-next-1142eb185b05db61a78130890fc4ed268f4cb4e6.zip
Merge branch 'net-enetc-sr-iov-improvements-and-enetc-v4-vf-support'
Wei Fang says: ==================== net: enetc: SR-IOV improvements and ENETC v4 VF support This series enhances SR-IOV support for ENETC v1 and adds VF support for ENETC v4. For ENETC v1, the trusted VF management and per-VF configuration query via ndo_get_vf_config() are added. For ENETC v4, preliminary VF support is introduced for i.MX94 and i.MX95 platforms. On the PF side, the series introduces trust level management and MAC address assignment for VFs, and implements ndo_get_vf_config() so that userspace tools such as 'ip link show' can query the current per-VF configuration. The PSI-to-VSI messaging infrastructure is extended to handle link status and link speed queries from VFs, enabling DPDK-owned VFs to obtain accurate link information from the PF. On the VF side, MAC address filtering is implemented via the VSI-to-PSI mailbox, supporting both unicast/multicast hash filters and promiscuous mode control. Since the ndo_set_rx_mode callback is invoked with BH disabled and is incompatible with the mailbox polling path, ndo_set_rx_mode_async is used instead. Link status notifications are also supported: the VF registers with the PF and receives PSI-to-VSI messages on every PF link transition, keeping the VF carrier state in sync without polling. VF Function Level Reset is handled explicitly to restore the PF-managed MAC promiscuous policy after each reset, since the hardware default after FLR enables promiscuous mode regardless of the policy set by the PF driver. Several preparatory refactors are included to share infrastructure between the PF and VF drivers, such as moving msg_task and mac_filter from struct enetc_pf into the common struct enetc_si. ==================== Link: https://patch.msgid.link/20260909100733.1139689-1-wei.fang@oss.nxp.com Signed-off-by: Paolo Abeni <pabeni@redhat.com>
-rw-r--r--drivers/net/ethernet/freescale/enetc/Kconfig1
-rw-r--r--drivers/net/ethernet/freescale/enetc/enetc.c63
-rw-r--r--drivers/net/ethernet/freescale/enetc/enetc.h11
-rw-r--r--drivers/net/ethernet/freescale/enetc/enetc4_debugfs.c51
-rw-r--r--drivers/net/ethernet/freescale/enetc/enetc4_hw.h1
-rw-r--r--drivers/net/ethernet/freescale/enetc/enetc4_pf.c85
-rw-r--r--drivers/net/ethernet/freescale/enetc/enetc_ethtool.c6
-rw-r--r--drivers/net/ethernet/freescale/enetc/enetc_hw.h26
-rw-r--r--drivers/net/ethernet/freescale/enetc/enetc_mailbox.h109
-rw-r--r--drivers/net/ethernet/freescale/enetc/enetc_msg.c643
-rw-r--r--drivers/net/ethernet/freescale/enetc/enetc_pf.c64
-rw-r--r--drivers/net/ethernet/freescale/enetc/enetc_pf.h21
-rw-r--r--drivers/net/ethernet/freescale/enetc/enetc_pf_common.c146
-rw-r--r--drivers/net/ethernet/freescale/enetc/enetc_pf_common.h19
-rw-r--r--drivers/net/ethernet/freescale/enetc/enetc_vf.c449
15 files changed, 1568 insertions, 127 deletions
diff --git a/drivers/net/ethernet/freescale/enetc/Kconfig b/drivers/net/ethernet/freescale/enetc/Kconfig
index db5c17a44613..f425f82a6213 100644
--- a/drivers/net/ethernet/freescale/enetc/Kconfig
+++ b/drivers/net/ethernet/freescale/enetc/Kconfig
@@ -69,6 +69,7 @@ config FSL_ENETC_VF
depends on PCI_MSI
select FSL_ENETC_CORE
select FSL_ENETC_MDIO
+ select NXP_NTMP
select PHYLINK
select DIMLIB
select CRC_ITU_T
diff --git a/drivers/net/ethernet/freescale/enetc/enetc.c b/drivers/net/ethernet/freescale/enetc/enetc.c
index 80f0082f6c63..04c49b680709 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc.c
@@ -2935,11 +2935,31 @@ static void enetc_clear_interrupts(struct enetc_ndev_priv *priv)
static int enetc_phylink_connect(struct net_device *ndev)
{
struct enetc_ndev_priv *priv = netdev_priv(ndev);
+ struct enetc_si *si = priv->si;
struct ethtool_keee edata;
int err;
if (!priv->phylink) {
/* phy-less mode */
+ if (!si->ops->vf_reg_link_status_notifier)
+ goto carrier_on;
+
+ /* For phy-less VFs on ENETC v4, attempt to register a link
+ * status notifier with the PF via the VSI-to-PSI messaging
+ * channel. If registration succeeds, the PF will immediately
+ * send the current link status and broadcast future link
+ * transitions; carrier state is then managed in
+ * enetc_vf_msg_handle_link_status(). If registration fails,
+ * fall back to the LS1028A behaviour and assert carrier
+ * unconditionally via netif_carrier_on().
+ */
+ if (!si->ops->vf_reg_link_status_notifier(si))
+ return 0;
+
+ dev_warn(&ndev->dev,
+ "Link status notifier registration failed\n");
+
+carrier_on:
netif_carrier_on(ndev);
return 0;
}
@@ -3024,10 +3044,6 @@ int enetc_open(struct net_device *ndev)
if (err)
goto err_setup_irqs;
- err = enetc_phylink_connect(ndev);
- if (err)
- goto err_phy_connect;
-
tx_res = enetc_alloc_tx_resources(priv);
if (IS_ERR(tx_res)) {
err = PTR_ERR(tx_res);
@@ -3040,6 +3056,10 @@ int enetc_open(struct net_device *ndev)
goto err_alloc_rx;
}
+ err = enetc_phylink_connect(ndev);
+ if (err)
+ goto err_phy_connect;
+
enetc_tx_onestep_tstamp_init(priv);
enetc_assign_tx_resources(priv, tx_res);
enetc_assign_rx_resources(priv, rx_res);
@@ -3048,12 +3068,11 @@ int enetc_open(struct net_device *ndev)
return 0;
+err_phy_connect:
+ enetc_free_rx_resources(rx_res, priv->num_rx_rings);
err_alloc_rx:
enetc_free_tx_resources(tx_res, priv->num_tx_rings);
err_alloc_tx:
- if (priv->phylink)
- phylink_disconnect_phy(priv->phylink);
-err_phy_connect:
enetc_free_irqs(priv);
err_setup_irqs:
clk_disable_unprepare(priv->ref_clk);
@@ -3093,6 +3112,7 @@ EXPORT_SYMBOL_GPL(enetc_stop);
int enetc_close(struct net_device *ndev)
{
struct enetc_ndev_priv *priv = netdev_priv(ndev);
+ struct enetc_si *si = priv->si;
enetc_stop(ndev);
@@ -3100,6 +3120,20 @@ int enetc_close(struct net_device *ndev)
phylink_stop(priv->phylink);
phylink_disconnect_phy(priv->phylink);
} else {
+ if (!si->ops->vf_unreg_link_status_notifier)
+ goto carrier_off;
+
+ /* No need to check whether the previous registration was
+ * successful. Sending the deregistration message has no
+ * impact; the PF side simply clears the corresponding bit
+ * in link_status_ms_mask for the VF.
+ */
+ if (!si->ops->vf_unreg_link_status_notifier(si))
+ goto carrier_off;
+
+ dev_warn(&ndev->dev,
+ "Link status notifier unregistration failed\n");
+carrier_off:
netif_carrier_off(ndev);
}
@@ -3794,6 +3828,13 @@ static const struct enetc_drvdata enetc_vf_data = {
.eth_ops = &enetc_vf_ethtool_ops,
};
+static const struct enetc_drvdata enetc4_vf_data = {
+ .sysclk_freq = ENETC_CLK_333M,
+ .tx_csum = true,
+ .max_frags = ENETC4_MAX_SKB_FRAGS,
+ .eth_ops = &enetc_vf_ethtool_ops,
+};
+
static const struct enetc_platform_info enetc_info[] = {
{ .revision = ENETC_REV_1_0,
.dev_id = ENETC_DEV_ID_PF,
@@ -3807,6 +3848,10 @@ static const struct enetc_platform_info enetc_info[] = {
.dev_id = ENETC_DEV_ID_VF,
.data = &enetc_vf_data,
},
+ { .revision = ENETC_REV_4_1,
+ .dev_id = NXP_ENETC_VF_DEV_ID,
+ .data = &enetc4_vf_data,
+ },
{
.revision = ENETC_REV_4_3,
.dev_id = NXP_ENETC_PPM_DEV_ID,
@@ -3816,6 +3861,10 @@ static const struct enetc_platform_info enetc_info[] = {
.dev_id = NXP_ENETC_PF_DEV_ID,
.data = &enetc4_pf_data,
},
+ { .revision = ENETC_REV_4_3,
+ .dev_id = NXP_ENETC_VF_DEV_ID,
+ .data = &enetc4_vf_data,
+ },
};
int enetc_get_driver_data(struct enetc_si *si)
diff --git a/drivers/net/ethernet/freescale/enetc/enetc.h b/drivers/net/ethernet/freescale/enetc/enetc.h
index d1e9d9130057..d9e91832a9c1 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc.h
+++ b/drivers/net/ethernet/freescale/enetc/enetc.h
@@ -25,6 +25,7 @@
#define ENETC_CBD_DATA_MEM_ALIGN 64
#define ENETC_MADDR_HASH_TBL_SZ 64
+#define ENETC_INT_NAME_MAX (IFNAMSIZ + 8)
enum enetc_mac_addr_type {UC, MC, MADDR_TYPE};
@@ -299,6 +300,10 @@ struct enetc_si_ops {
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);
+
+ /* VSI-specific hooks */
+ int (*vf_reg_link_status_notifier)(struct enetc_si *si);
+ int (*vf_unreg_link_status_notifier)(struct enetc_si *si);
};
/* PCI IEP device data */
@@ -333,6 +338,11 @@ struct enetc_si {
struct dentry *debugfs_root;
struct enetc_msg_swbd msg; /* Only valid for VSI */
+ struct workqueue_struct *workqueue;
+ struct work_struct msg_task;
+ char msg_int_name[ENETC_INT_NAME_MAX];
+
+ struct enetc_mac_filter mac_filter[MADDR_TYPE];
};
#define ENETC_SI_ALIGN 32
@@ -374,7 +384,6 @@ static inline bool enetc_is_pseudo_mac(struct enetc_si *si)
}
#define ENETC_MAX_NUM_TXQS 8
-#define ENETC_INT_NAME_MAX (IFNAMSIZ + 8)
struct enetc_int_vector {
void __iomem *rbier;
diff --git a/drivers/net/ethernet/freescale/enetc/enetc4_debugfs.c b/drivers/net/ethernet/freescale/enetc/enetc4_debugfs.c
index 5029038bf99f..c73722e2285f 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc4_debugfs.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc4_debugfs.c
@@ -6,24 +6,48 @@
#include <linux/seq_file.h>
#include <linux/string_choices.h>
-#include "enetc_pf.h"
+#include "enetc_pf_common.h"
#include "enetc4_debugfs.h"
-static void enetc_show_si_mac_hash_filter(struct seq_file *s, int i)
+static void enetc_vf_state_lock(struct enetc_pf *pf, int vf_id)
{
- struct enetc_si *si = s->private;
- struct enetc_hw *hw = &si->hw;
+ struct enetc_vf_state *vf_state;
+
+ if (vf_id < 0)
+ return;
+
+ vf_state = &pf->vf_state[vf_id];
+ mutex_lock(&vf_state->lock);
+}
+
+static void enetc_vf_state_unlock(struct enetc_pf *pf, int vf_id)
+{
+ struct enetc_vf_state *vf_state;
+
+ if (vf_id < 0)
+ return;
+
+ vf_state = &pf->vf_state[vf_id];
+ mutex_unlock(&vf_state->lock);
+}
+
+static void enetc_show_si_mac_hash_filter(struct seq_file *s, int si_id)
+{
+ struct enetc_pf *pf = enetc_si_priv(s->private);
+ struct enetc_hw *hw = &pf->si->hw;
u32 hash_h, hash_l;
- hash_l = enetc_port_rd(hw, ENETC4_PSIUMHFR0(i));
- hash_h = enetc_port_rd(hw, ENETC4_PSIUMHFR1(i));
+ enetc_vf_state_lock(pf, si_id - 1);
+ hash_l = enetc_port_rd(hw, ENETC4_PSIUMHFR0(si_id));
+ hash_h = enetc_port_rd(hw, ENETC4_PSIUMHFR1(si_id));
seq_printf(s, "SI %d unicast MAC hash filter: 0x%08x%08x\n",
- i, hash_h, hash_l);
+ si_id, hash_h, hash_l);
- hash_l = enetc_port_rd(hw, ENETC4_PSIMMHFR0(i));
- hash_h = enetc_port_rd(hw, ENETC4_PSIMMHFR1(i));
+ hash_l = enetc_port_rd(hw, ENETC4_PSIMMHFR0(si_id));
+ hash_h = enetc_port_rd(hw, ENETC4_PSIMMHFR1(si_id));
seq_printf(s, "SI %d multicast MAC hash filter: 0x%08x%08x\n",
- i, hash_h, hash_l);
+ si_id, hash_h, hash_l);
+ enetc_vf_state_unlock(pf, si_id - 1);
}
static int enetc_mac_filter_show(struct seq_file *s, void *data)
@@ -37,7 +61,11 @@ static int enetc_mac_filter_show(struct seq_file *s, void *data)
int err = 0;
int i;
+ /* Prevent concurrent access from causing PSIPMMR to be modified */
+ spin_lock(&pf->si->gen_lock);
val = enetc_port_rd(hw, ENETC4_PSIPMMR);
+ spin_unlock(&pf->si->gen_lock);
+
for (i = 0; i < num_si; i++) {
seq_printf(s, "SI %d Unicast Promiscuous mode: %s\n", i,
str_enabled_disabled(PSIPMMR_SI_MAC_UP(i) & val));
@@ -45,13 +73,12 @@ static int enetc_mac_filter_show(struct seq_file *s, void *data)
str_enabled_disabled(PSIPMMR_SI_MAC_MP(i) & val));
}
+ rtnl_lock();
/* MAC hash filter table */
for (i = 0; i < num_si; i++)
enetc_show_si_mac_hash_filter(s, i);
user = &pf->si->ntmp_user;
- rtnl_lock();
-
if (bitmap_empty(user->maft_eid_bitmap, user->maft_num_entries))
goto unlock_rtnl;
diff --git a/drivers/net/ethernet/freescale/enetc/enetc4_hw.h b/drivers/net/ethernet/freescale/enetc/enetc4_hw.h
index 09025e7a2a3a..e23d8d82d2ba 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc4_hw.h
+++ b/drivers/net/ethernet/freescale/enetc/enetc4_hw.h
@@ -12,6 +12,7 @@
#define NXP_ENETC_VENDOR_ID 0x1131
#define NXP_ENETC_PF_DEV_ID 0xe101
#define NXP_ENETC_PPM_DEV_ID 0xe110
+#define NXP_ENETC_VF_DEV_ID 0xef00
/**********************Station interface registers************************/
/* Station interface LSO segmentation flag mask register 0/1 */
diff --git a/drivers/net/ethernet/freescale/enetc/enetc4_pf.c b/drivers/net/ethernet/freescale/enetc/enetc4_pf.c
index 9bb1004548ab..71c971618388 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc4_pf.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc4_pf.c
@@ -12,11 +12,6 @@
#define ENETC_SI_MAX_RING_NUM 8
-#define ENETC_MAC_FILTER_TYPE_UC BIT(0)
-#define ENETC_MAC_FILTER_TYPE_MC BIT(1)
-#define ENETC_MAC_FILTER_TYPE_ALL (ENETC_MAC_FILTER_TYPE_UC | \
- ENETC_MAC_FILTER_TYPE_MC)
-
static void enetc4_get_port_caps(struct enetc_pf *pf)
{
struct enetc_hw *hw = &pf->si->hw;
@@ -137,7 +132,7 @@ clear_maft_entries:
static void enetc4_pf_set_uc_hash_filter(struct enetc_pf *pf,
struct netdev_hw_addr_list *uc)
{
- struct enetc_mac_filter *mac_filter = &pf->mac_filter[UC];
+ struct enetc_mac_filter *mac_filter = &pf->si->mac_filter[UC];
struct netdev_hw_addr *ha;
u64 hash;
@@ -172,7 +167,7 @@ static int enetc4_pf_set_uc_exact_filter(struct enetc_pf *pf,
err = enetc4_pf_add_maft_entries(pf, uc);
if (!err) {
- enetc_reset_mac_addr_filter(&pf->mac_filter[UC]);
+ enetc_reset_mac_addr_filter(&si->mac_filter[UC]);
enetc_set_si_uc_hash_filter(si, 0, 0);
}
@@ -182,7 +177,7 @@ static int enetc4_pf_set_uc_exact_filter(struct enetc_pf *pf,
static void enetc4_pf_set_mc_hash_filter(struct enetc_pf *pf,
struct netdev_hw_addr_list *mc)
{
- struct enetc_mac_filter *mac_filter = &pf->mac_filter[MC];
+ struct enetc_mac_filter *mac_filter = &pf->si->mac_filter[MC];
struct netdev_hw_addr *ha;
u64 hash;
@@ -217,19 +212,43 @@ static void enetc4_pf_set_mac_filter(struct enetc_pf *pf, int type,
enetc4_pf_set_mc_hash_filter(pf, mc);
}
+static void enetc4_pf_vf_flr_handler(struct enetc_pf *pf, int vf_id)
+{
+ struct enetc_vf_state *vf_state;
+ bool uc_promisc, mc_promisc;
+
+ vf_state = &pf->vf_state[vf_id];
+ mutex_lock(&vf_state->lock);
+
+ uc_promisc = !!(vf_state->flags & ENETC_VF_FLAG_UC_PROMISC);
+ mc_promisc = !!(vf_state->flags & ENETC_VF_FLAG_MC_PROMISC);
+
+ spin_lock(&pf->si->gen_lock);
+ enetc_set_si_uc_promisc(pf->si, vf_id + 1, uc_promisc);
+ enetc_set_si_mc_promisc(pf->si, vf_id + 1, mc_promisc);
+ spin_unlock(&pf->si->gen_lock);
+
+ mutex_unlock(&vf_state->lock);
+}
+
static const struct enetc_pf_ops enetc4_pf_ops = {
.set_si_primary_mac = enetc4_pf_set_si_primary_mac,
.get_si_primary_mac = enetc4_pf_get_si_primary_mac,
+ .vf_flr_handler = enetc4_pf_vf_flr_handler,
};
static int enetc4_pf_struct_init(struct enetc_si *si)
{
struct enetc_pf *pf = enetc_si_priv(si);
+ int err;
pf->si = si;
- pf->total_vfs = pci_sriov_get_totalvfs(si->pdev);
pf->ops = &enetc4_pf_ops;
+ err = enetc_init_sriov_resources(pf);
+ if (err)
+ return err;
+
enetc4_get_port_caps(pf);
enetc4_get_psi_hw_features(si);
@@ -524,8 +543,10 @@ static int enetc4_pf_set_rx_mode(struct net_device *ndev,
type = ENETC_MAC_FILTER_TYPE_ALL;
}
+ spin_lock(&si->gen_lock);
enetc_set_si_uc_promisc(si, 0, uc_promisc);
enetc_set_si_mc_promisc(si, 0, mc_promisc);
+ spin_unlock(&si->gen_lock);
if (uc_promisc) {
enetc_set_si_uc_hash_filter(si, 0, 0);
@@ -574,6 +595,9 @@ static const struct net_device_ops enetc4_ndev_ops = {
.ndo_eth_ioctl = enetc_ioctl,
.ndo_hwtstamp_get = enetc_hwtstamp_get,
.ndo_hwtstamp_set = enetc_hwtstamp_set,
+ .ndo_set_vf_trust = enetc_pf_set_vf_trust,
+ .ndo_set_vf_mac = enetc_pf_set_vf_mac,
+ .ndo_get_vf_config = enetc_pf_get_vf_config,
};
static struct phylink_pcs *
@@ -894,6 +918,7 @@ static void enetc4_pl_mac_link_up(struct phylink_config *config,
enetc4_set_rx_pause(pf, rx_pause);
enetc4_mac_tx_enable(pf);
enetc4_mac_rx_enable(pf);
+ enetc_pf_notify_vf_link_up(pf);
}
static void enetc4_pl_mac_link_down(struct phylink_config *config,
@@ -902,6 +927,7 @@ static void enetc4_pl_mac_link_down(struct phylink_config *config,
{
struct enetc_pf *pf = phylink_to_enetc_pf(config);
+ enetc_pf_notify_vf_link_down(pf);
enetc4_mac_rx_graceful_stop(pf);
enetc4_mac_tx_graceful_stop(pf);
}
@@ -961,6 +987,36 @@ static void enetc4_link_deinit(struct enetc_ndev_priv *priv)
enetc_mdiobus_destroy(pf);
}
+static void enetc4_pf_link_status_task(struct work_struct *work)
+{
+ struct enetc_pf *pf = container_of(work, struct enetc_pf,
+ link_status_task);
+
+ enetc_pf_send_link_status_msg(pf);
+}
+
+static int enetc4_pf_wq_task_init(struct enetc_si *si)
+{
+ struct enetc_pf *pf = enetc_si_priv(si);
+
+ si->workqueue = alloc_ordered_workqueue("enetc-%s-wq", 0,
+ pci_name(si->pdev));
+ if (!si->workqueue)
+ return -ENOMEM;
+
+ INIT_WORK(&pf->link_status_task, enetc4_pf_link_status_task);
+
+ return 0;
+}
+
+static void enetc4_pf_wq_task_destroy(struct enetc_si *si)
+{
+ struct enetc_pf *pf = enetc_si_priv(si);
+
+ disable_work_sync(&pf->link_status_task);
+ destroy_workqueue(si->workqueue);
+}
+
static int enetc4_pf_netdev_create(struct enetc_si *si)
{
struct device *dev = &si->pdev->dev;
@@ -1001,6 +1057,10 @@ static int enetc4_pf_netdev_create(struct enetc_si *si)
if (err)
goto err_link_init;
+ err = enetc4_pf_wq_task_init(si);
+ if (err)
+ goto err_wq_init;
+
err = register_netdev(ndev);
if (err) {
dev_err(dev, "Failed to register netdev\n");
@@ -1010,6 +1070,8 @@ static int enetc4_pf_netdev_create(struct enetc_si *si)
return 0;
err_reg_netdev:
+ enetc4_pf_wq_task_destroy(si);
+err_wq_init:
enetc4_link_deinit(priv);
err_link_init:
enetc_free_msix(priv);
@@ -1027,6 +1089,7 @@ static void enetc4_pf_netdev_destroy(struct enetc_si *si)
struct net_device *ndev = si->ndev;
unregister_netdev(ndev);
+ enetc4_pf_wq_task_destroy(si);
enetc4_link_deinit(priv);
enetc_free_msix(priv);
free_netdev(ndev);
@@ -1096,6 +1159,9 @@ static void enetc4_pf_remove(struct pci_dev *pdev)
struct enetc_si *si = pci_get_drvdata(pdev);
struct enetc_pf *pf = enetc_si_priv(si);
+ if (pf->num_vfs)
+ enetc_sriov_configure(pdev, 0);
+
enetc_remove_debugfs(si);
enetc4_pf_netdev_destroy(si);
enetc4_pf_free(pf);
@@ -1113,6 +1179,7 @@ static struct pci_driver enetc4_pf_driver = {
.id_table = enetc4_pf_id_table,
.probe = enetc4_pf_probe,
.remove = enetc4_pf_remove,
+ .sriov_configure = enetc_sriov_configure,
};
module_pci_driver(enetc4_pf_driver);
diff --git a/drivers/net/ethernet/freescale/enetc/enetc_ethtool.c b/drivers/net/ethernet/freescale/enetc/enetc_ethtool.c
index 07b7832f2427..7965dfd06f5f 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_ethtool.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc_ethtool.c
@@ -859,6 +859,9 @@ static int enetc_get_rxnfc(struct net_device *ndev, struct ethtool_rxnfc *rxnfc,
struct enetc_ndev_priv *priv = netdev_priv(ndev);
int i, j;
+ if (!is_enetc_rev1(priv->si))
+ return -EOPNOTSUPP;
+
switch (rxnfc->cmd) {
case ETHTOOL_GRXCLSRLCNT:
/* total number of entries */
@@ -903,6 +906,9 @@ static int enetc_set_rxnfc(struct net_device *ndev, struct ethtool_rxnfc *rxnfc)
struct enetc_ndev_priv *priv = netdev_priv(ndev);
int err;
+ if (!is_enetc_rev1(priv->si))
+ return -EOPNOTSUPP;
+
switch (rxnfc->cmd) {
case ETHTOOL_SRXCLSRLINS:
if (rxnfc->fs.location >= priv->si->num_fs_entries)
diff --git a/drivers/net/ethernet/freescale/enetc/enetc_hw.h b/drivers/net/ethernet/freescale/enetc/enetc_hw.h
index 16da732dc5de..2c9d9042eb0b 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_hw.h
+++ b/drivers/net/ethernet/freescale/enetc/enetc_hw.h
@@ -80,6 +80,14 @@ static inline u32 enetc_vsi_set_msize(u32 size)
#define ENETC_SIMSGSR_SET_MC(val) ((val) << 16)
#define ENETC_SIMSGSR_GET_MC(val) ((val) >> 16)
+#define ENETC_PSIMSGSR 0x208
+/* n is VF index, which is less than 15 */
+#define PSIMSGSR_MS(n) BIT((n) + 1)
+#define PSIMSGSR_MC GENMASK(31, 16)
+
+#define ENETC_VSIMSGRR 0x208
+#define VSIMSGRR_MC GENMASK(31, 16)
+
/* SI statistics */
#define ENETC_SIROCT 0x300
#define ENETC_SIRFRM 0x308
@@ -103,8 +111,26 @@ static inline u32 enetc_vsi_set_msize(u32 size)
#define ENETC_SICAPR0 0x900
#define ENETC_SICAPR1 0x904
+#define ENETC_VSIIER 0xa00
+#define VSIIER_MRIE BIT(9)
+
+#define ENETC_VSIIDR 0xa08
+#define VSIIDR_MR BIT(9)
+
#define ENETC_PSIIER 0xa00
#define ENETC_PSIIDR 0xa08
+
+/* VF FLR interrupt mask, n is the active number of VFs.
+ * It is available for ENETC_PSIIER and ENETC_PSIIDR registers.
+ */
+#define ENETC_VFFLR_MASK(n) \
+ ({ typeof(n) _n = (n); (_n) ? GENMASK(16 + (_n), 17) : 0; })
+
+/* VF FLR interrupt bit, n is VF index. It is available
+ * for ENETC_PSIIER and ENETC_PSIIDR registers.
+ */
+#define ENETC_VFFLR_BIT(n) BIT(17 + (n))
+
#define ENETC_SITXIDR 0xa18
#define ENETC_SIRXIDR 0xa28
#define ENETC_SIMSIVR 0xa30
diff --git a/drivers/net/ethernet/freescale/enetc/enetc_mailbox.h b/drivers/net/ethernet/freescale/enetc/enetc_mailbox.h
index d9677da38989..6fa66c863748 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_mailbox.h
+++ b/drivers/net/ethernet/freescale/enetc/enetc_mailbox.h
@@ -66,8 +66,8 @@
* 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.
+ * Note that for some PSI-to-VSI messages, there is no COOKIE field, and the
+ * CLASS CODE field is expanded to 8 bits.
*/
#ifndef __ENETC_MAILBOX_H
@@ -87,10 +87,26 @@
/* 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 */
+/* Extend the class code to 8-bit for PSI-to-VSI messages without COOKIE
+ * The class code for the following messages is 8-bit.
+ * 1. Get IP revision messages
+ * 2. Link status messages
+ * 3. Link speed messages
+ */
#define ENETC_PF_MSG_CLASS_CODE_U8 GENMASK(7, 0)
#define ENETC_PF_MSG_CLASS_ID GENMASK(15, 8)
+#define ENETC_MAC_HASH_TABLE_SIZE_64 0
+#define ENETC_MSG_MAC_HASH_SIZE GENMASK(5, 0)
+#define ENETC_MSG_MAC_TYPE GENMASK(7, 6)
+#define ENETC_MAC_FILTER_TYPE_UC BIT(0)
+#define ENETC_MAC_FILTER_TYPE_MC BIT(1)
+#define ENETC_MAC_FILTER_TYPE_ALL (ENETC_MAC_FILTER_TYPE_UC | \
+ ENETC_MAC_FILTER_TYPE_MC)
+
+#define ENETC_MSG_MAC_FLUSH_MACS BIT(0)
+#define ENETC_MSG_MAC_PROMISC_MODE BIT(1)
+
enum enetc_msg_class_id {
/* Class ID for PSI-to-VSI messages */
ENETC_MSG_CLASS_ID_CMD_SUCCESS = 1,
@@ -107,20 +123,66 @@ enum enetc_msg_class_id {
/* Common Class ID for PSI-to-VSI and VSI-to-PSI messages */
ENETC_MSG_CLASS_ID_MAC_FILTER = 0x20,
+ ENETC_MSG_CLASS_ID_LINK_STATUS = 0x80,
+ ENETC_MSG_CLASS_ID_LINK_SPEED = 0x81,
ENETC_MSG_CLASS_ID_IP_REVISION = 0xf0,
};
enum enetc_msg_mac_filter_cmd_id {
ENETC_MSG_SET_PRIMARY_MAC,
+ ENETC_MSG_SET_MAC_HASH_TABLE = 3,
+ ENETC_MSG_SET_MAC_PROMISC_MODE = 5,
};
enum enetc_msg_ip_revision_cmd_id {
ENETC_MSG_GET_IP_MN = 1,
};
+enum enetc_msg_link_status_cmd_id {
+ ENETC_MSG_GET_CURRENT_LINK_STATUS,
+ ENETC_MSG_REGISTER_LINK_CHANGE_NOTIFIER,
+ ENETC_MSG_UNREGISTER_LINK_CHANGE_NOTIFIER,
+};
+
+enum enetc_msg_link_speed_cmd_id {
+ ENETC_MSG_GET_CURRENT_LINK_SPEED,
+ /* The following command IDs are not currently supported */
+ ENETC_MSG_REGISTER_SPEED_CHANGE_NOTIFIER,
+ ENETC_MSG_UNREGISTER_SPEED_CHANGE_NOTIFIER,
+};
+
/* Class-specific error return codes of MAC filter */
enum enetc_mac_filter_class_code {
ENETC_MF_CLASS_CODE_INVALID_MAC,
+ ENETC_MF_CLASS_CODE_INVALID_TYPE = 4,
+ /* Unicast Filter Is Denied */
+ ENETC_MF_CLASS_CODE_UCF_DENY = 5,
+};
+
+/* Class-specific notifications/codes of link status */
+#define ENETC_CLASS_CODE_LINK_DOWN BIT(0)
+#define ENETC_CLASS_CODE_TX_PAUSE_EN BIT(1)
+
+/* Class-specific notifications/codes of link speed */
+enum enetc_link_speed_class_code {
+ ENETC_MSG_SPEED_UNKNOWN,
+ ENETC_MSG_SPEED_10M_HD,
+ ENETC_MSG_SPEED_10M_FD,
+ ENETC_MSG_SPEED_100M_HD,
+ ENETC_MSG_SPEED_100M_FD,
+ ENETC_MSG_SPEED_1000M,
+ ENETC_MSG_SPEED_2500M,
+ ENETC_MSG_SPEED_5G,
+ /* Do not add enumeration values for any speed greater than
+ * 5Gbps. For any speed greater than 5Gbps, its speed class
+ * code should follow the formula below.
+ *
+ * SPEED = (link_speed - 5000) / 1000 + ENETC_MSG_SPEED_5G
+ *
+ * The unit of link_speed should be Mbps, the max SPEED
+ * should <= ENETC_MSG_SPEED_MAX.
+ */
+ ENETC_MSG_SPEED_MAX = 0xff,
};
struct enetc_msg_swbd {
@@ -158,9 +220,50 @@ struct enetc_msg_mac_exact_filter {
struct enetc_mac_addr mac[];
};
+/* message format of class_id 0x20 for hash MAC filter.
+ * cmd_id 0x3: set MAC hash table
+ */
+struct enetc_msg_mac_hash_filter {
+ struct enetc_msg_header hdr;
+ /* bit 0 ~ 5: ENETC_MSG_MAC_HASH_SIZE
+ * bit 6~7: ENETC_MSG_MAC_TYPE
+ */
+ u8 sz_type;
+ u8 resv[3];
+ u32 hash_tbl[];
+};
+
+/* message format of class_id 0x20 for MAC promiscuous mode.
+ * cmd_id 0x5: set MAC promiscuous mode
+ */
+struct enetc_msg_mac_promisc_mode {
+ struct enetc_msg_header hdr;
+ /* bit 0: ENETC_MSG_MAC_FLUSH_MACS
+ * bit 1: ENETC_MSG_MAC_PROMISC_MODE
+ * bit 6~7: ENETC_MSG_MAC_TYPE
+ */
+ u8 config;
+ u8 resv[15];
+};
+
/* The generic message format applies to the following messages:
* Get IP revision message, class_id 0xf0.
* cmd_id 1: get IP minor revision
+ *
+ * Link status message, class id 0x80.
+ * cmd_id 0x0: get the current link status
+ * cmd_id 0x1: register link status change notification
+ * cmd_id 0x2: unregister link status change notification
+ *
+ * Link speed message, class_id 0x81.
+ * cmd_id 0x0: get the current link speed. Unlike the link status
+ * query (class 0x80), this query is only permitted for trusted VFs;
+ * an untrusted VF receives a permission-deny response. This is
+ * because the PF must take rtnl_lock() to read the link speed, so
+ * restricting it to trusted VFs avoids rtnl_lock contention on the
+ * host from a misbehaving VF.
+ * cmd_id 0x1: register link speed change notification, not supported yet
+ * cmd_id 0x2: unregister link speed change notification, not supported yet
*/
struct enetc_msg_generic {
struct enetc_msg_header hdr;
diff --git a/drivers/net/ethernet/freescale/enetc/enetc_msg.c b/drivers/net/ethernet/freescale/enetc/enetc_msg.c
index edc1277bb586..ff99937815c0 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_msg.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc_msg.c
@@ -7,24 +7,28 @@
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)
+#define ENETC_PF_MSG_PERM_DENY FIELD_PREP(ENETC_PF_MSG_CLASS_ID, \
+ ENETC_MSG_CLASS_ID_PERMISSION_DENY)
+#define ENETC_PF_MSG_INV_LEN FIELD_PREP(ENETC_PF_MSG_CLASS_ID, \
+ ENETC_MSG_CLASS_ID_INVALID_MSG_LEN)
+#define ENETC_PF_MSG_MF(code) (FIELD_PREP(ENETC_PF_MSG_CLASS_ID, \
+ ENETC_MSG_CLASS_ID_MAC_FILTER) | \
+ FIELD_PREP(ENETC_PF_MSG_CLASS_CODE, (code)))
+
+static void enetc_disable_psiier_interrupts(struct enetc_pf *pf)
{
struct enetc_hw *hw = &pf->si->hw;
- u32 psiier;
-
- psiier = enetc_rd(hw, ENETC_PSIIER) & ~ENETC_PSIMR_MASK(pf->num_vfs);
- /* disable MR int source(s) */
- enetc_wr(hw, ENETC_PSIIER, psiier);
+ enetc_wr(hw, ENETC_PSIIER, 0);
}
-static void enetc_msg_enable_mr_int(struct enetc_pf *pf)
+static void enetc_enable_psiier_interrupts(struct enetc_pf *pf)
{
+ u32 psiier = ENETC_PSIMR_MASK(pf->num_vfs);
struct enetc_hw *hw = &pf->si->hw;
- u32 psiier;
- psiier = enetc_rd(hw, ENETC_PSIIER) | ENETC_PSIMR_MASK(pf->num_vfs);
+ if (pf->ops->vf_flr_handler)
+ psiier |= ENETC_VFFLR_MASK(pf->num_vfs);
enetc_wr(hw, ENETC_PSIIER, psiier);
}
@@ -34,8 +38,8 @@ 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(pf);
- schedule_work(&pf->msg_task);
+ enetc_disable_psiier_interrupts(pf);
+ schedule_work(&si->msg_task);
return IRQ_HANDLED;
}
@@ -61,31 +65,184 @@ static u16 enetc_msg_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_mac_exact_filter *msg = vf_msg;
struct device *dev = &pf->si->pdev->dev;
+ u16 pf_msg = ENETC_PF_MSG_SUCCESS;
char *addr = msg->mac[0].addr;
+ mutex_lock(&vf_state->lock);
+
+ /* Untrusted VFs cannot set their MAC addresses by the mailbox
+ * messages.
+ */
+ if (!(vf_state->flags & ENETC_VF_FLAG_TRUSTED)) {
+ pf_msg = ENETC_PF_MSG_PERM_DENY;
+ goto vf_state_unlock;
+ }
+
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));
+ pf_msg = ENETC_PF_MSG_MF(ENETC_MF_CLASS_CODE_INVALID_MAC);
+ goto vf_state_unlock;
}
- mutex_lock(&vf_state->lock);
+ /* PF has higher privileges. If PF has already modified the MAC
+ * address for VF through .ndo_set_vf_mac() interface, VF is not
+ * allowed to set its MAC address via mailbox messages, even if
+ * it is trusted.
+ */
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);
+ pf_msg = FIELD_PREP(ENETC_PF_MSG_CLASS_ID,
+ ENETC_MSG_CLASS_ID_CMD_NOT_PERMITTED);
+ goto vf_state_unlock;
}
enetc_set_si_hw_addr(pf, vf_id + 1, addr);
+
+vf_state_unlock:
mutex_unlock(&vf_state->lock);
- return ENETC_PF_MSG_SUCCESS;
+ return pf_msg;
+}
+
+static u16 enetc_msg_set_vf_mac_hash_filter(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_hash_filter *msg = vf_msg;
+ u16 pf_msg = ENETC_PF_MSG_SUCCESS;
+ struct enetc_si *si = pf->si;
+ int si_id = vf_id + 1;
+ u64 uc_hash, mc_hash;
+ bool trusted;
+ int type;
+
+ /* Currently, hardware only supports 64 bits table size */
+ if (FIELD_GET(ENETC_MSG_MAC_HASH_SIZE, msg->sz_type) !=
+ ENETC_MAC_HASH_TABLE_SIZE_64)
+ return ENETC_PF_MSG_NOTSUPP;
+
+ mutex_lock(&vf_state->lock);
+
+ /* For an untrusted VF, unicast MAC hash filtering is not permitted.
+ * For multicast, the MAC hash filter is strictly limited to a maximum
+ * of 8 bits to satisfy its basic multicast communication requirements
+ * while preventing potential network abuse.
+ */
+ trusted = !!(vf_state->flags & ENETC_VF_FLAG_TRUSTED);
+ type = FIELD_GET(ENETC_MSG_MAC_TYPE, msg->sz_type);
+ switch (type) {
+ case ENETC_MAC_FILTER_TYPE_UC:
+ if (!trusted) {
+ pf_msg = ENETC_PF_MSG_PERM_DENY;
+ goto vf_state_unlock;
+ }
+
+ uc_hash = (u64)msg->hash_tbl[1] << 32 | msg->hash_tbl[0];
+ enetc_set_si_uc_hash_filter(si, si_id, uc_hash);
+ break;
+ case ENETC_MAC_FILTER_TYPE_MC:
+ mc_hash = (u64)msg->hash_tbl[1] << 32 | msg->hash_tbl[0];
+ if (!trusted &&
+ hweight64(mc_hash) > ENETC_VF_MC_HASH_BITS_MAX) {
+ pf_msg = ENETC_PF_MSG_PERM_DENY;
+ goto vf_state_unlock;
+ }
+
+ enetc_set_si_mc_hash_filter(si, si_id, mc_hash);
+ break;
+ case ENETC_MAC_FILTER_TYPE_ALL:
+ if (!msg->hdr.len) {
+ pf_msg = ENETC_PF_MSG_INV_LEN;
+ goto vf_state_unlock;
+ }
+
+ uc_hash = (u64)msg->hash_tbl[1] << 32 | msg->hash_tbl[0];
+ mc_hash = (u64)msg->hash_tbl[3] << 32 | msg->hash_tbl[2];
+
+ if (!trusted &&
+ (hweight64(mc_hash) <= ENETC_VF_MC_HASH_BITS_MAX)) {
+ enetc_set_si_mc_hash_filter(si, si_id, mc_hash);
+ pf_msg = ENETC_PF_MSG_MF(ENETC_MF_CLASS_CODE_UCF_DENY);
+ goto vf_state_unlock;
+ }
+
+ if (!trusted) {
+ pf_msg = ENETC_PF_MSG_PERM_DENY;
+ goto vf_state_unlock;
+ }
+
+ enetc_set_si_uc_hash_filter(si, si_id, uc_hash);
+ enetc_set_si_mc_hash_filter(si, si_id, mc_hash);
+ break;
+ default:
+ pf_msg = ENETC_PF_MSG_MF(ENETC_MF_CLASS_CODE_INVALID_TYPE);
+ }
+
+vf_state_unlock:
+ mutex_unlock(&vf_state->lock);
+
+ return pf_msg;
+}
+
+static u16 enetc_msg_set_vf_mac_promisc_mode(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_promisc_mode *msg = vf_msg;
+ u16 pf_msg = ENETC_PF_MSG_SUCCESS;
+ struct enetc_si *si = pf->si;
+ bool promisc, flush_macs;
+ int si_id = vf_id + 1;
+ int type;
+
+ flush_macs = !!(msg->config & ENETC_MSG_MAC_FLUSH_MACS);
+ type = FIELD_GET(ENETC_MSG_MAC_TYPE, msg->config);
+ if (!type)
+ return ENETC_PF_MSG_MF(ENETC_MF_CLASS_CODE_INVALID_TYPE);
+
+ mutex_lock(&vf_state->lock);
+
+ promisc = !!(msg->config & ENETC_MSG_MAC_PROMISC_MODE);
+ if (promisc && !(vf_state->flags & ENETC_VF_FLAG_TRUSTED)) {
+ pf_msg = ENETC_PF_MSG_PERM_DENY;
+ goto vf_state_unlock;
+ }
+
+ if (type & ENETC_MAC_FILTER_TYPE_UC) {
+ if (promisc)
+ vf_state->flags |= ENETC_VF_FLAG_UC_PROMISC;
+ else
+ vf_state->flags &= ~ENETC_VF_FLAG_UC_PROMISC;
+ }
+
+ if (type & ENETC_MAC_FILTER_TYPE_MC) {
+ if (promisc)
+ vf_state->flags |= ENETC_VF_FLAG_MC_PROMISC;
+ else
+ vf_state->flags &= ~ENETC_VF_FLAG_MC_PROMISC;
+ }
+
+ spin_lock(&si->gen_lock);
+ if (type & ENETC_MAC_FILTER_TYPE_UC)
+ enetc_set_si_uc_promisc(si, si_id, promisc);
+
+ if (type & ENETC_MAC_FILTER_TYPE_MC)
+ enetc_set_si_mc_promisc(si, si_id, promisc);
+ spin_unlock(&si->gen_lock);
+
+ if ((type & ENETC_MAC_FILTER_TYPE_UC) && flush_macs)
+ enetc_set_si_uc_hash_filter(si, si_id, 0);
+
+ if ((type & ENETC_MAC_FILTER_TYPE_MC) && flush_macs)
+ enetc_set_si_mc_hash_filter(si, si_id, 0);
+
+vf_state_unlock:
+ mutex_unlock(&vf_state->lock);
+
+ return pf_msg;
}
static u16 enetc_msg_handle_mac_filter(struct enetc_pf *pf, int vf_id,
@@ -96,6 +253,10 @@ static u16 enetc_msg_handle_mac_filter(struct enetc_pf *pf, int vf_id,
switch (msg_hdr->cmd_id) {
case ENETC_MSG_SET_PRIMARY_MAC:
return enetc_msg_set_vf_primary_mac_addr(pf, vf_id, vf_msg);
+ case ENETC_MSG_SET_MAC_HASH_TABLE:
+ return enetc_msg_set_vf_mac_hash_filter(pf, vf_id, vf_msg);
+ case ENETC_MSG_SET_MAC_PROMISC_MODE:
+ return enetc_msg_set_vf_mac_promisc_mode(pf, vf_id, vf_msg);
default:
return ENETC_PF_MSG_NOTSUPP;
}
@@ -116,6 +277,191 @@ static u16 enetc_msg_handle_ip_revision(struct enetc_pf *pf, void *vf_msg)
}
}
+static void enetc_pf_reply_msg(struct enetc_hw *hw, int vf_id, u16 pf_msg)
+{
+ /* w1c to clear the corresponding VF MR bit */
+ enetc_wr(hw, ENETC_PSIIDR, ENETC_PSIMR_BIT(vf_id));
+ enetc_wr(hw, ENETC_PSIMSGRR, ENETC_SIMSGSR_SET_MC(pf_msg) |
+ ENETC_PSIMR_BIT(vf_id));
+}
+
+static u16 enetc_build_link_status_msg(struct enetc_ndev_priv *priv,
+ bool link_up)
+{
+ u8 status = 0;
+
+ if (link_up) {
+ if (test_bit(ENETC_RXBDR_CM, &priv->flags))
+ status |= ENETC_CLASS_CODE_TX_PAUSE_EN;
+ } else {
+ status |= ENETC_CLASS_CODE_LINK_DOWN;
+ }
+
+ return FIELD_PREP(ENETC_PF_MSG_CLASS_ID,
+ ENETC_MSG_CLASS_ID_LINK_STATUS) |
+ FIELD_PREP(ENETC_PF_MSG_CLASS_CODE_U8, status);
+}
+
+static void enetc_msg_get_link_status(struct enetc_pf *pf, int vf_id)
+{
+ struct enetc_ndev_priv *priv = netdev_priv(pf->si->ndev);
+ struct enetc_si *si = pf->si;
+ u16 pf_msg;
+
+ spin_lock(&si->gen_lock);
+ pf_msg = enetc_build_link_status_msg(priv, pf->link_up);
+ enetc_pf_reply_msg(&si->hw, vf_id, pf_msg);
+ spin_unlock(&si->gen_lock);
+}
+
+static void enetc_msg_register_link_status_notifier(struct enetc_pf *pf,
+ int vf_id)
+{
+ struct enetc_si *si = pf->si;
+
+ spin_lock(&si->gen_lock);
+ enetc_pf_reply_msg(&si->hw, vf_id, ENETC_PF_MSG_SUCCESS);
+
+ /* SR-IOV is being disabled if pf->sriov_enabled is false, so no
+ * need to set link_status_ms_mask and notify the link status.
+ */
+ if (!pf->sriov_enabled) {
+ spin_unlock(&si->gen_lock);
+ return;
+ }
+
+ pf->link_status_ms_mask |= PSIMSGSR_MS(vf_id);
+ spin_unlock(&si->gen_lock);
+
+ /* Notify VF the current link status */
+ queue_work(si->workqueue, &pf->link_status_task);
+}
+
+static void enetc_msg_unregister_link_status_notifier(struct enetc_pf *pf,
+ int vf_id)
+{
+ spin_lock(&pf->si->gen_lock);
+ pf->link_status_ms_mask &= ~PSIMSGSR_MS(vf_id);
+ enetc_pf_reply_msg(&pf->si->hw, vf_id, ENETC_PF_MSG_SUCCESS);
+ spin_unlock(&pf->si->gen_lock);
+}
+
+static u16 enetc_msg_handle_link_status(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_GET_CURRENT_LINK_STATUS:
+ /* Currently, this message is intended only for
+ * DPDK-owned VFs.
+ */
+ enetc_msg_get_link_status(pf, vf_id);
+ break;
+ case ENETC_MSG_REGISTER_LINK_CHANGE_NOTIFIER:
+ enetc_msg_register_link_status_notifier(pf, vf_id);
+ break;
+ case ENETC_MSG_UNREGISTER_LINK_CHANGE_NOTIFIER:
+ enetc_msg_unregister_link_status_notifier(pf, vf_id);
+ break;
+ default:
+ return ENETC_PF_MSG_NOTSUPP;
+ }
+
+ return 0;
+}
+
+static u16 enetc_build_link_speed_msg(int speed, int duplex)
+{
+ u32 speed_code = ENETC_MSG_SPEED_UNKNOWN;
+
+ switch (speed) {
+ case SPEED_10:
+ if (duplex == DUPLEX_HALF)
+ speed_code = ENETC_MSG_SPEED_10M_HD;
+ else if (duplex == DUPLEX_FULL)
+ speed_code = ENETC_MSG_SPEED_10M_FD;
+ break;
+ case SPEED_100:
+ if (duplex == DUPLEX_HALF)
+ speed_code = ENETC_MSG_SPEED_100M_HD;
+ else if (duplex == DUPLEX_FULL)
+ speed_code = ENETC_MSG_SPEED_100M_FD;
+ break;
+ case SPEED_1000:
+ speed_code = ENETC_MSG_SPEED_1000M;
+ break;
+ case SPEED_2500:
+ speed_code = ENETC_MSG_SPEED_2500M;
+ break;
+ case SPEED_5000:
+ speed_code = ENETC_MSG_SPEED_5G;
+ break;
+ default:
+ if (speed < SPEED_5000)
+ break;
+
+ speed_code = (speed - SPEED_5000) / SPEED_1000 +
+ ENETC_MSG_SPEED_5G;
+ if (speed_code > ENETC_MSG_SPEED_MAX)
+ speed_code = ENETC_MSG_SPEED_UNKNOWN;
+ }
+
+ return FIELD_PREP(ENETC_PF_MSG_CLASS_ID,
+ ENETC_MSG_CLASS_ID_LINK_SPEED) |
+ FIELD_PREP(ENETC_PF_MSG_CLASS_CODE_U8, speed_code);
+}
+
+static u16 enetc_msg_get_link_speed(struct enetc_pf *pf, int vf_id)
+{
+ struct enetc_ndev_priv *priv = netdev_priv(pf->si->ndev);
+ struct enetc_vf_state *vf_state = &pf->vf_state[vf_id];
+ struct ethtool_link_ksettings link_info = {};
+
+ /* A malicious or malfunctioning VM could potentially spam these
+ * messages in a tight loop causing global rtnl_lock contention,
+ * which may severely starve other processes on the host that
+ * require rtnl_lock for routine network configuration, resulting
+ * in a system-wide control-plane denial of service. Therefore,
+ * we expect the VF query for link speed to be trusted. There's no
+ * need to consider the transition from trusted to untrusted here,
+ * as this won't cause rtnl_lock() to be called frequently.
+ */
+ mutex_lock(&vf_state->lock);
+ if (!(vf_state->flags & ENETC_VF_FLAG_TRUSTED)) {
+ mutex_unlock(&vf_state->lock);
+
+ return ENETC_PF_MSG_PERM_DENY;
+ }
+ mutex_unlock(&vf_state->lock);
+
+ rtnl_lock();
+ phylink_ethtool_ksettings_get(priv->phylink, &link_info);
+ rtnl_unlock();
+
+ return enetc_build_link_speed_msg(link_info.base.speed,
+ link_info.base.duplex);
+}
+
+static u16 enetc_msg_handle_link_speed(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_GET_CURRENT_LINK_SPEED:
+ return enetc_msg_get_link_speed(pf, vf_id);
+ case ENETC_MSG_REGISTER_SPEED_CHANGE_NOTIFIER:
+ case ENETC_MSG_UNREGISTER_SPEED_CHANGE_NOTIFIER:
+ default:
+ return ENETC_PF_MSG_NOTSUPP;
+ }
+}
+
+/* If *pf_msg is set to 0, it means that PF has responded to VF in
+ * enetc_msg_handle_rxmsg() through enetc_pf_reply_msg(), which also
+ * clears the corresponding VF MR bit in PSIIDR.
+ */
static void enetc_msg_handle_rxmsg(struct enetc_pf *pf, int vf_id,
u16 *pf_msg)
{
@@ -128,8 +474,7 @@ static void enetc_msg_handle_rxmsg(struct enetc_pf *pf, int vf_id,
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);
+ *pf_msg = ENETC_PF_MSG_INV_LEN;
return;
}
@@ -147,6 +492,14 @@ static void enetc_msg_handle_rxmsg(struct enetc_pf *pf, int vf_id,
}
memcpy(msg, msg_swbd->vaddr, msg_size);
+ msg_hdr = (struct enetc_msg_header *)msg;
+
+ /* Check message length whether is changed */
+ if (ENETC_MSG_SIZE(msg_hdr->len) != msg_size) {
+ *pf_msg = ENETC_PF_MSG_INV_LEN;
+ goto free_msg;
+ }
+
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,
@@ -157,7 +510,6 @@ static void enetc_msg_handle_rxmsg(struct enetc_pf *pf, int vf_id,
/* 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)) {
@@ -191,6 +543,12 @@ static void enetc_msg_handle_rxmsg(struct enetc_pf *pf, int vf_id,
case ENETC_MSG_CLASS_ID_IP_REVISION:
*pf_msg = enetc_msg_handle_ip_revision(pf, msg);
break;
+ case ENETC_MSG_CLASS_ID_LINK_STATUS:
+ *pf_msg = enetc_msg_handle_link_status(pf, vf_id, msg);
+ break;
+ case ENETC_MSG_CLASS_ID_LINK_SPEED:
+ *pf_msg = enetc_msg_handle_link_speed(pf, vf_id, msg);
+ break;
default:
dev_err_ratelimited(dev,
"Unsupported message class ID: 0x%x\n",
@@ -201,21 +559,46 @@ free_msg:
kfree(msg);
}
-static void enetc_msg_task(struct work_struct *work)
+static void enetc_vf_flr_handler(struct enetc_pf *pf)
{
- struct enetc_pf *pf = container_of(work, struct enetc_pf, msg_task);
- u32 mr_mask = ENETC_PSIMR_MASK(pf->num_vfs);
+ u32 flr_mask = ENETC_VFFLR_MASK(pf->num_vfs);
struct enetc_hw *hw = &pf->si->hw;
- u32 mr_status;
+ u32 flr_status;
+
+ if (!pf->ops->vf_flr_handler)
+ return;
+
+ flr_status = enetc_rd(hw, ENETC_PSIIDR) & flr_mask;
+ if (!flr_status)
+ return;
+
+ for (int i = 0; i < pf->num_vfs; i++) {
+ if (!(ENETC_VFFLR_BIT(i) & flr_status))
+ continue;
+
+ /* Clear FLR interrupt status, W1C */
+ enetc_wr(hw, ENETC_PSIIDR, ENETC_VFFLR_BIT(i));
+ pf->ops->vf_flr_handler(pf, i);
+ }
+}
+
+static void enetc_msg_task(struct work_struct *work)
+{
+ struct enetc_si *si = container_of(work, struct enetc_si, msg_task);
+ struct enetc_pf *pf = enetc_si_priv(si);
+ struct enetc_hw *hw = &si->hw;
+ u32 mr_status, mr_mask;
int i;
+ enetc_vf_flr_handler(pf);
+
+ mr_mask = ENETC_PSIMR_MASK(pf->num_vfs);
mr_status = (enetc_rd(hw, ENETC_PSIMSGRR) & mr_mask) |
(enetc_rd(hw, ENETC_PSIIDR) & mr_mask);
if (!mr_status)
goto out;
for (i = 0; i < pf->num_vfs; i++) {
- u32 psimsgrr;
u16 msg_code;
if (!(ENETC_PSIMR_BIT(i) & mr_status))
@@ -223,16 +606,18 @@ static void enetc_msg_task(struct work_struct *work)
enetc_msg_handle_rxmsg(pf, i, &msg_code);
- /* w1c to clear the corresponding VF MR bit */
- enetc_wr(hw, ENETC_PSIIDR, ENETC_PSIMR_BIT(i));
+ /* If msg_code is 0, it means that PF has responded to VF
+ * in enetc_msg_handle_rxmsg() through enetc_pf_reply_msg(),
+ * which also clears the corresponding VF MR bit in PSIIDR.
+ */
+ if (!msg_code)
+ continue;
- psimsgrr = ENETC_SIMSGSR_SET_MC(msg_code);
- psimsgrr |= ENETC_PSIMR_BIT(i); /* w1c */
- enetc_wr(hw, ENETC_PSIMSGRR, psimsgrr);
+ enetc_pf_reply_msg(hw, i, msg_code);
}
out:
- enetc_msg_enable_mr_int(pf);
+ enetc_enable_psiier_interrupts(pf);
}
/* Init */
@@ -291,13 +676,13 @@ static int enetc_msg_psi_init(struct enetc_pf *pf)
}
/* initialize PSI mailbox */
- INIT_WORK(&pf->msg_task, enetc_msg_task);
+ INIT_WORK(&si->msg_task, enetc_msg_task);
/* register message passing interrupt handler */
- snprintf(pf->msg_int_name, sizeof(pf->msg_int_name), "%s-vfmsg",
+ snprintf(si->msg_int_name, sizeof(si->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);
+ err = request_irq(vector, enetc_msg_psi_msix, 0, si->msg_int_name, si);
if (err) {
dev_err(&si->pdev->dev,
"PSI messaging: request_irq() failed!\n");
@@ -307,8 +692,8 @@ static int enetc_msg_psi_init(struct enetc_pf *pf)
/* set one IRQ entry for PSI message receive notification (SI int) */
enetc_wr(&si->hw, ENETC_SIMSIVR, ENETC_SI_INT_IDX);
- /* enable MR interrupts */
- enetc_msg_enable_mr_int(pf);
+ /* enable PSIIER interrupts */
+ enetc_enable_psiier_interrupts(pf);
return 0;
@@ -319,24 +704,60 @@ free_mbx:
return err;
}
+static void enetc_msg_clear_vf_config(struct enetc_pf *pf, int vf_id)
+{
+ struct enetc_vf_state *vf_state = &pf->vf_state[vf_id];
+ struct enetc_si *si = pf->si;
+ int si_id = vf_id + 1;
+
+ /* For ENETC v1, we only support setting the VF's MAC address via
+ * VSI-to-PSI messages, so there is no configuration to clear.
+ */
+ if (is_enetc_rev1(si))
+ return;
+
+ mutex_lock(&vf_state->lock);
+
+ /* VF may set these flags by mailbox messages, so need to clear these
+ * flags when enetc_msg_psi_free() is called. PF-set flags (TRUSTED,
+ * PF_SET_MAC) are not cleared, because these flags are unrelated to
+ * whether SR-IOV is enabled or disabled.
+ */
+ vf_state->flags &= ~(ENETC_VF_FLAG_UC_PROMISC |
+ ENETC_VF_FLAG_MC_PROMISC);
+
+ spin_lock(&si->gen_lock);
+ vf_state->msg_fail_cnt = 0;
+ enetc_set_si_uc_promisc(si, si_id, false);
+ enetc_set_si_mc_promisc(si, si_id, false);
+ spin_unlock(&si->gen_lock);
+
+ enetc_set_si_uc_hash_filter(si, si_id, 0);
+ enetc_set_si_mc_hash_filter(si, si_id, 0);
+
+ mutex_unlock(&vf_state->lock);
+}
+
static void enetc_msg_psi_free(struct enetc_pf *pf)
{
struct enetc_si *si = pf->si;
int i;
- /* disable MR interrupts */
- enetc_msg_disable_mr_int(pf);
+ /* disable PSIIER interrupts */
+ enetc_disable_psiier_interrupts(pf);
/* de-register message passing interrupt handler */
free_irq(pci_irq_vector(si->pdev, ENETC_SI_INT_IDX), si);
- cancel_work_sync(&pf->msg_task);
+ cancel_work_sync(&si->msg_task);
- /* MR interrupts may be re-enabled by workqueue */
- enetc_msg_disable_mr_int(pf);
+ /* PSIIER interrupts may be re-enabled by workqueue */
+ enetc_disable_psiier_interrupts(pf);
- for (i = 0; i < pf->num_vfs; i++)
+ for (i = 0; i < pf->num_vfs; i++) {
enetc_msg_free_mbx(si, i);
+ enetc_msg_clear_vf_config(pf, i);
+ }
}
int enetc_sriov_configure(struct pci_dev *pdev, int num_vfs)
@@ -346,6 +767,11 @@ int enetc_sriov_configure(struct pci_dev *pdev, int num_vfs)
int err;
if (!num_vfs) {
+ spin_lock(&si->gen_lock);
+ pf->sriov_enabled = false;
+ pf->link_status_ms_mask = 0;
+ spin_unlock(&si->gen_lock);
+
pci_disable_sriov(pdev);
enetc_msg_psi_free(pf);
pf->num_vfs = 0;
@@ -358,6 +784,11 @@ int enetc_sriov_configure(struct pci_dev *pdev, int num_vfs)
goto err_msg_psi;
}
+ /* As PCI SR-IOV is not enabled at the moment, there is no
+ * concurrent access to sriov_enabled. So no need to use
+ * gen_lock to protect sriov_enabled.
+ */
+ pf->sriov_enabled = true;
err = pci_enable_sriov(pdev, num_vfs);
if (err) {
dev_err(&pdev->dev, "pci_enable_sriov err %d\n", err);
@@ -368,6 +799,15 @@ int enetc_sriov_configure(struct pci_dev *pdev, int num_vfs)
return num_vfs;
err_en_sriov:
+ /* If pci_enable_sriov() fails after partially creating VFs, a VF
+ * driver that successfully bound to one of the created VFs could
+ * have sent a registration message, setting its bit in
+ * link_status_ms_mask.
+ */
+ spin_lock(&si->gen_lock);
+ pf->sriov_enabled = false;
+ pf->link_status_ms_mask = 0;
+ spin_unlock(&si->gen_lock);
enetc_msg_psi_free(pf);
err_msg_psi:
pf->num_vfs = 0;
@@ -375,3 +815,114 @@ err_msg_psi:
return err;
}
EXPORT_SYMBOL_GPL(enetc_sriov_configure);
+
+void enetc_pf_send_link_status_msg(struct enetc_pf *pf)
+{
+ struct enetc_ndev_priv *priv = netdev_priv(pf->si->ndev);
+ u16 pf_msg, ms_mask, new_ms_msk, ms_status;
+ struct enetc_si *si = pf->si;
+ int retry_num = 0;
+
+retry:
+ spin_lock(&si->gen_lock);
+ ms_mask = pf->link_status_ms_mask;
+ /* VFs have unregistered link status notification, return directly */
+ if (!ms_mask)
+ goto unlock;
+
+ /* The MS bit is set, indicating that the corresponding VF has not
+ * read the last message, PF cannot send new message to the VF. To
+ * avoid sending messages to such a VF, the bit corresponding to VF
+ * is cleared from ms_mask. Because the MS bit can only be written
+ * as 1, writing a 0 has no effect. Writing a 1 when the bit is
+ * already set is undefined.
+ */
+ ms_status = enetc_rd(&si->hw, ENETC_PSIMSGSR) & 0xfffe;
+ if ((ms_mask & ms_status) && retry_num++ < 200) {
+ spin_unlock(&si->gen_lock);
+ /* Wait VFs to handle the last message */
+ usleep_range(1000, 1020);
+ goto retry;
+ }
+
+ /* None of the relevant VFs have processed the previous message, and
+ * the PF has tried 200 times. This situation indicates that VF has
+ * malfunctioned.
+ */
+ new_ms_msk = ms_mask & (~ms_status);
+ if (!new_ms_msk) {
+ dev_err_ratelimited(&si->pdev->dev,
+ "All registered VFs (MS: 0x%x) are busy\n",
+ ms_mask);
+ goto ms_status_check;
+ }
+
+ if (new_ms_msk != ms_mask)
+ dev_warn_ratelimited(&si->pdev->dev,
+ "Failed to notify link status to VFs (MS: 0x%x)\n",
+ ms_mask ^ new_ms_msk);
+
+ pf_msg = enetc_build_link_status_msg(priv, pf->link_up);
+ enetc_wr(&si->hw, ENETC_PSIMSGSR,
+ FIELD_PREP(PSIMSGSR_MC, pf_msg) | new_ms_msk);
+
+ms_status_check:
+ /* If the PF fails to send messages to the corresponding VF for 10
+ * consecutive times, clear that VF's bit in link_status_ms_mask.
+ */
+ for (int i = 0; i < pf->num_vfs; i++) {
+ struct enetc_vf_state *vf_state = &pf->vf_state[i];
+
+ if (!(PSIMSGSR_MS(i) & ms_mask))
+ continue;
+
+ if (!(PSIMSGSR_MS(i) & ms_status)) {
+ vf_state->msg_fail_cnt = 0;
+ continue;
+ }
+
+ if (vf_state->msg_fail_cnt++ < 10)
+ continue;
+
+ vf_state->msg_fail_cnt = 0;
+ pf->link_status_ms_mask &= ~PSIMSGSR_MS(i);
+ dev_warn_ratelimited(&si->pdev->dev,
+ "Clear VF%d's link status MS bit\n", i);
+ }
+
+unlock:
+ spin_unlock(&si->gen_lock);
+}
+EXPORT_SYMBOL_GPL(enetc_pf_send_link_status_msg);
+
+static void enetc_pf_notify_vf_link_status(struct enetc_pf *pf,
+ bool link_up)
+{
+ struct enetc_si *si = pf->si;
+
+ /* Currently we do not add link status message support for ENETC v1 */
+ if (!pf->total_vfs || is_enetc_rev1(si))
+ return;
+
+ spin_lock(&si->gen_lock);
+ pf->link_up = link_up;
+ if (!pf->link_status_ms_mask) {
+ spin_unlock(&si->gen_lock);
+ return;
+ }
+ spin_unlock(&si->gen_lock);
+
+ queue_work(si->workqueue, &pf->link_status_task);
+}
+
+void enetc_pf_notify_vf_link_up(struct enetc_pf *pf)
+{
+ enetc_pf_notify_vf_link_status(pf, true);
+}
+EXPORT_SYMBOL_GPL(enetc_pf_notify_vf_link_up);
+
+void enetc_pf_notify_vf_link_down(struct enetc_pf *pf)
+{
+ enetc_pf_notify_vf_link_status(pf, false);
+}
+EXPORT_SYMBOL_GPL(enetc_pf_notify_vf_link_down);
diff --git a/drivers/net/ethernet/freescale/enetc/enetc_pf.c b/drivers/net/ethernet/freescale/enetc/enetc_pf.c
index 55c07c528f22..d77a07cece28 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_pf.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc_pf.c
@@ -60,10 +60,9 @@ static void enetc_add_mac_addr_em_filter(struct enetc_mac_filter *filter,
filter->mac_addr_cnt++;
}
-static void enetc_sync_mac_filters(struct enetc_pf *pf)
+static void enetc_sync_mac_filters(struct enetc_si *si)
{
- struct enetc_mac_filter *f = pf->mac_filter;
- struct enetc_si *si = pf->si;
+ struct enetc_mac_filter *f = si->mac_filter;
int i, pos;
pos = EMETC_MAC_ADDR_FILT_RES;
@@ -115,9 +114,9 @@ static void enetc_sync_mac_filters(struct enetc_pf *pf)
static void enetc_pf_set_rx_mode(struct net_device *ndev)
{
struct enetc_ndev_priv *priv = netdev_priv(ndev);
- struct enetc_pf *pf = enetc_si_priv(priv->si);
bool uprom = false, mprom = false;
struct enetc_mac_filter *filter;
+ struct enetc_si *si = priv->si;
struct netdev_hw_addr *ha;
bool em;
@@ -133,7 +132,7 @@ static void enetc_pf_set_rx_mode(struct net_device *ndev)
/* first 2 filter entries belong to PF */
if (!uprom) {
/* Update unicast filters */
- filter = &pf->mac_filter[UC];
+ filter = &si->mac_filter[UC];
enetc_reset_mac_addr_filter(filter);
em = (netdev_uc_count(ndev) == 1);
@@ -149,7 +148,7 @@ static void enetc_pf_set_rx_mode(struct net_device *ndev)
if (!mprom) {
/* Update multicast filters */
- filter = &pf->mac_filter[MC];
+ filter = &si->mac_filter[MC];
enetc_reset_mac_addr_filter(filter);
netdev_for_each_mc_addr(ha, ndev) {
@@ -162,10 +161,10 @@ static void enetc_pf_set_rx_mode(struct net_device *ndev)
if (!uprom || !mprom)
/* update PF entries */
- enetc_sync_mac_filters(pf);
+ enetc_sync_mac_filters(si);
- enetc_set_si_uc_promisc(priv->si, 0, uprom);
- enetc_set_si_mc_promisc(priv->si, 0, mprom);
+ enetc_set_si_uc_promisc(si, 0, uprom);
+ enetc_set_si_mc_promisc(si, 0, mprom);
}
static void enetc_set_loopback(struct net_device *ndev, bool en)
@@ -191,33 +190,12 @@ static void enetc_set_loopback(struct net_device *ndev, bool en)
}
}
-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;
-
- if (!is_valid_ether_addr(mac))
- return -EADDRNOTAVAIL;
-
- vf_state = &pf->vf_state[vf];
-
- mutex_lock(&vf_state->lock);
- vf_state->flags |= ENETC_VF_FLAG_PF_SET_MAC;
- enetc_pf_set_primary_mac_addr(&priv->si->hw, vf + 1, mac);
- mutex_unlock(&vf_state->lock);
-
- return 0;
-}
-
static int enetc_pf_set_vf_vlan(struct net_device *ndev, int vf, u16 vlan,
u8 qos, __be16 proto)
{
struct enetc_ndev_priv *priv = netdev_priv(ndev);
struct enetc_pf *pf = enetc_si_priv(priv->si);
+ struct enetc_vf_state *vf_state;
if (priv->si->errata & ENETC_ERR_VLAN_ISOL)
return -EOPNOTSUPP;
@@ -230,6 +208,17 @@ static int enetc_pf_set_vf_vlan(struct net_device *ndev, int vf, u16 vlan,
return -EPROTONOSUPPORT;
enetc_set_isol_vlan(&priv->si->hw, vf + 1, vlan, qos);
+
+ vf_state = &pf->vf_state[vf];
+ mutex_lock(&vf_state->lock);
+ /* Currently only C-tags is supported, so tpid is always 0,
+ * which indicates ETH_P_8021Q.
+ */
+ vf_state->tpid = 0;
+ vf_state->qos = qos;
+ vf_state->vid = vlan;
+ mutex_unlock(&vf_state->lock);
+
return 0;
}
@@ -237,6 +226,7 @@ static int enetc_pf_set_vf_spoofchk(struct net_device *ndev, int vf, bool en)
{
struct enetc_ndev_priv *priv = netdev_priv(ndev);
struct enetc_pf *pf = enetc_si_priv(priv->si);
+ struct enetc_vf_state *vf_state;
u32 cfgr;
if (vf >= pf->total_vfs)
@@ -246,6 +236,16 @@ static int enetc_pf_set_vf_spoofchk(struct net_device *ndev, int vf, bool en)
cfgr = (cfgr & ~ENETC_PSICFGR0_ASE) | (en ? ENETC_PSICFGR0_ASE : 0);
enetc_port_wr(&priv->si->hw, ENETC_PSICFGR0(vf + 1), cfgr);
+ vf_state = &pf->vf_state[vf];
+ mutex_lock(&vf_state->lock);
+
+ if (en)
+ vf_state->flags |= ENETC_VF_FLAG_SPOOFCHK;
+ else
+ vf_state->flags &= ~ENETC_VF_FLAG_SPOOFCHK;
+
+ mutex_unlock(&vf_state->lock);
+
return 0;
}
@@ -488,6 +488,7 @@ static const struct net_device_ops enetc_ndev_ops = {
.ndo_set_rx_mode = enetc_pf_set_rx_mode,
.ndo_vlan_rx_add_vid = enetc_vlan_rx_add_vid,
.ndo_vlan_rx_kill_vid = enetc_vlan_rx_del_vid,
+ .ndo_set_vf_trust = enetc_pf_set_vf_trust,
.ndo_set_vf_mac = enetc_pf_set_vf_mac,
.ndo_set_vf_vlan = enetc_pf_set_vf_vlan,
.ndo_set_vf_spoofchk = enetc_pf_set_vf_spoofchk,
@@ -498,6 +499,7 @@ static const struct net_device_ops enetc_ndev_ops = {
.ndo_xdp_xmit = enetc_xdp_xmit,
.ndo_hwtstamp_get = enetc_hwtstamp_get,
.ndo_hwtstamp_set = enetc_hwtstamp_set,
+ .ndo_get_vf_config = enetc_pf_get_vf_config,
};
static struct phylink_pcs *
diff --git a/drivers/net/ethernet/freescale/enetc/enetc_pf.h b/drivers/net/ethernet/freescale/enetc/enetc_pf.h
index 56d23a8a11a0..1f92d16d8ca8 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_pf.h
+++ b/drivers/net/ethernet/freescale/enetc/enetc_pf.h
@@ -6,14 +6,24 @@
#define ENETC_PF_NUM_RINGS 8
#define ENETC_VLAN_HT_SIZE 64
+#define ENETC_VF_MC_HASH_BITS_MAX 8 /* For untrusted VFs */
enum enetc_vf_flags {
ENETC_VF_FLAG_PF_SET_MAC = BIT(0),
+ ENETC_VF_FLAG_TRUSTED = BIT(1),
+ ENETC_VF_FLAG_UC_PROMISC = BIT(2),
+ ENETC_VF_FLAG_MC_PROMISC = BIT(3),
+ ENETC_VF_FLAG_SPOOFCHK = BIT(4),
};
struct enetc_vf_state {
struct mutex lock; /* Prevent concurrent access */
enum enetc_vf_flags flags;
+ /* Number of consecutive failures to send PF-to-VF messages */
+ int msg_fail_cnt;
+ u8 tpid; /* SI-based VLAN TPID (0: 0x8100, 1: 0x88a8) */
+ u8 qos; /* SI-based VLAN QOS (priority) bits */
+ u16 vid; /* SI-based VLAN ID */
};
struct enetc_port_caps {
@@ -30,6 +40,7 @@ struct enetc_pf_ops {
struct phylink_pcs *(*create_pcs)(struct enetc_pf *pf, struct mii_bus *bus);
void (*destroy_pcs)(struct phylink_pcs *pcs);
int (*enable_psfp)(struct enetc_ndev_priv *priv);
+ void (*vf_flr_handler)(struct enetc_pf *pf, int vf_id);
};
struct enetc_pf {
@@ -37,12 +48,7 @@ struct enetc_pf {
int num_vfs; /* number of active VFs, after sriov_init */
int total_vfs; /* max number of VFs, set for PF at probe */
struct enetc_vf_state *vf_state;
-
- struct enetc_mac_filter mac_filter[MADDR_TYPE];
-
struct enetc_msg_swbd *rxmsg;
- struct work_struct msg_task;
- char msg_int_name[ENETC_INT_NAME_MAX];
DECLARE_BITMAP(vlan_ht_filter, ENETC_VLAN_HT_SIZE);
DECLARE_BITMAP(active_vlans, VLAN_N_VID);
@@ -56,6 +62,11 @@ struct enetc_pf {
struct enetc_port_caps caps;
const struct enetc_pf_ops *ops;
+
+ struct work_struct link_status_task;
+ bool sriov_enabled;
+ bool link_up;
+ u16 link_status_ms_mask;
};
#define phylink_to_enetc_pf(config) \
diff --git a/drivers/net/ethernet/freescale/enetc/enetc_pf_common.c b/drivers/net/ethernet/freescale/enetc/enetc_pf_common.c
index d32a195a04c9..8206884294a4 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_pf_common.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc_pf_common.c
@@ -151,26 +151,44 @@ void enetc_set_si_uc_hash_filter(struct enetc_si *si, int si_id, u64 hash)
}
EXPORT_SYMBOL_GPL(enetc_set_si_uc_hash_filter);
-void enetc_set_si_mc_hash_filter(struct enetc_si *si, int si_id, u64 hash)
+static void enetc_get_psimmhfr_offsets(struct enetc_si *si, int si_id,
+ int *psimmhfr0, int *psimmhfr1)
{
- int psimmhfr0_off, psimmhfr1_off;
- struct enetc_hw *hw = &si->hw;
-
if (is_enetc_rev1(si)) {
bool err = si->errata & ENETC_ERR_UCMCSWP;
- psimmhfr0_off = ENETC_PSIMMHFR0(si_id, err);
- psimmhfr1_off = ENETC_PSIMMHFR1(si_id);
+ *psimmhfr0 = ENETC_PSIMMHFR0(si_id, err);
+ *psimmhfr1 = ENETC_PSIMMHFR1(si_id);
} else {
- psimmhfr0_off = ENETC4_PSIMMHFR0(si_id);
- psimmhfr1_off = ENETC4_PSIMMHFR1(si_id);
+ *psimmhfr0 = ENETC4_PSIMMHFR0(si_id);
+ *psimmhfr1 = ENETC4_PSIMMHFR1(si_id);
}
+}
+
+void enetc_set_si_mc_hash_filter(struct enetc_si *si, int si_id, u64 hash)
+{
+ int psimmhfr0_off, psimmhfr1_off;
+ struct enetc_hw *hw = &si->hw;
+ enetc_get_psimmhfr_offsets(si, si_id, &psimmhfr0_off, &psimmhfr1_off);
enetc_port_wr(hw, psimmhfr0_off, lower_32_bits(hash));
enetc_port_wr(hw, psimmhfr1_off, upper_32_bits(hash));
}
EXPORT_SYMBOL_GPL(enetc_set_si_mc_hash_filter);
+static u64 enetc_get_si_mc_hash_filter(struct enetc_si *si, int si_id)
+{
+ int psimmhfr0_off, psimmhfr1_off;
+ struct enetc_hw *hw = &si->hw;
+ u32 hash_h, hash_l;
+
+ enetc_get_psimmhfr_offsets(si, si_id, &psimmhfr0_off, &psimmhfr1_off);
+ hash_l = enetc_port_rd(hw, psimmhfr0_off);
+ hash_h = enetc_port_rd(hw, psimmhfr1_off);
+
+ return ((u64)hash_h << 32) | hash_l;
+}
+
void enetc_set_si_vlan_promisc(struct enetc_si *si, int si_id, bool promisc)
{
struct enetc_hw *hw = &si->hw;
@@ -586,5 +604,117 @@ int enetc_init_sriov_resources(struct enetc_pf *pf)
}
EXPORT_SYMBOL_GPL(enetc_init_sriov_resources);
+int enetc_pf_set_vf_trust(struct net_device *ndev, int vf, bool setting)
+{
+ struct enetc_ndev_priv *priv = netdev_priv(ndev);
+ struct enetc_pf *pf = enetc_si_priv(priv->si);
+ struct enetc_vf_state *vf_state;
+ struct enetc_si *si = priv->si;
+ int si_id = vf + 1;
+
+ if (vf >= pf->total_vfs)
+ return -EINVAL;
+
+ vf_state = &pf->vf_state[vf];
+ mutex_lock(&vf_state->lock);
+
+ if (setting) {
+ vf_state->flags |= ENETC_VF_FLAG_TRUSTED;
+ } else {
+ u64 hash;
+
+ vf_state->flags &= ~(ENETC_VF_FLAG_TRUSTED |
+ ENETC_VF_FLAG_UC_PROMISC |
+ ENETC_VF_FLAG_MC_PROMISC);
+
+ /* For ENETC v1, we only support setting the VF's MAC address
+ * via VSI-to-PSI messages. Unicast and multicast promiscuous
+ * mode and hash filters are not supported, so there is no need
+ * to clear these configurations.
+ */
+ if (is_enetc_rev1(si))
+ goto vf_state_unlock;
+
+ /* Disable unicast and multicast promiscuous modes */
+ spin_lock(&si->gen_lock);
+ enetc_set_si_uc_promisc(si, si_id, false);
+ enetc_set_si_mc_promisc(si, si_id, false);
+ spin_unlock(&si->gen_lock);
+
+ /* Clear unicast hash filter */
+ enetc_set_si_uc_hash_filter(si, si_id, 0);
+
+ /* Clear multicast hash filter if its set bits exceed
+ * ENETC_VF_MC_HASH_BITS_MAX.
+ */
+ hash = enetc_get_si_mc_hash_filter(si, si_id);
+ if (hweight64(hash) > ENETC_VF_MC_HASH_BITS_MAX)
+ enetc_set_si_mc_hash_filter(si, si_id, 0);
+ }
+
+vf_state_unlock:
+ mutex_unlock(&vf_state->lock);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(enetc_pf_set_vf_trust);
+
+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;
+
+ if (!is_valid_ether_addr(mac))
+ return -EADDRNOTAVAIL;
+
+ vf_state = &pf->vf_state[vf];
+
+ mutex_lock(&vf_state->lock);
+ vf_state->flags |= ENETC_VF_FLAG_PF_SET_MAC;
+ enetc_set_si_hw_addr(pf, vf + 1, mac);
+ mutex_unlock(&vf_state->lock);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(enetc_pf_set_vf_mac);
+
+int enetc_pf_get_vf_config(struct net_device *ndev, int vf,
+ struct ifla_vf_info *ivi)
+{
+ 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;
+
+ vf_state = &pf->vf_state[vf];
+ mutex_lock(&vf_state->lock);
+
+ ivi->vf = vf;
+ /* ENETC v4 has not support spoofchk yet */
+ if (is_enetc_rev1(priv->si))
+ ivi->spoofchk = !!(vf_state->flags & ENETC_VF_FLAG_SPOOFCHK);
+
+ ivi->trusted = !!(vf_state->flags & ENETC_VF_FLAG_TRUSTED);
+ enetc_get_si_hw_addr(pf, vf + 1, ivi->mac);
+
+ if (vf_state->vid) {
+ ivi->vlan = vf_state->vid;
+ ivi->qos = vf_state->qos;
+ ivi->vlan_proto = vf_state->tpid ? htons(ETH_P_8021AD) :
+ htons(ETH_P_8021Q);
+ }
+
+ mutex_unlock(&vf_state->lock);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(enetc_pf_get_vf_config);
+
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 8243ce0de57f..c9eed879d5a3 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_pf_common.h
+++ b/drivers/net/ethernet/freescale/enetc/enetc_pf_common.h
@@ -22,6 +22,10 @@ void enetc_set_si_mc_promisc(struct enetc_si *si, int si_id, bool promisc);
void enetc_set_si_uc_hash_filter(struct enetc_si *si, int si_id, u64 hash);
void enetc_set_si_mc_hash_filter(struct enetc_si *si, int si_id, u64 hash);
void enetc_set_si_vlan_promisc(struct enetc_si *si, int si_id, bool promisc);
+int enetc_pf_set_vf_trust(struct net_device *ndev, int vf, bool setting);
+int enetc_pf_set_vf_mac(struct net_device *ndev, int vf, u8 *mac);
+int enetc_pf_get_vf_config(struct net_device *ndev, int vf,
+ struct ifla_vf_info *ivi);
static inline u16 enetc_get_ip_revision(struct enetc_hw *hw)
{
@@ -30,9 +34,24 @@ static inline u16 enetc_get_ip_revision(struct enetc_hw *hw)
#if IS_ENABLED(CONFIG_PCI_IOV)
int enetc_sriov_configure(struct pci_dev *pdev, int num_vfs);
+void enetc_pf_send_link_status_msg(struct enetc_pf *pf);
+void enetc_pf_notify_vf_link_up(struct enetc_pf *pf);
+void enetc_pf_notify_vf_link_down(struct enetc_pf *pf);
#else
static inline int enetc_sriov_configure(struct pci_dev *pdev, int num_vfs)
{
return 0;
}
+
+static inline void enetc_pf_send_link_status_msg(struct enetc_pf *pf)
+{
+}
+
+static inline void enetc_pf_notify_vf_link_up(struct enetc_pf *pf)
+{
+}
+
+static inline void enetc_pf_notify_vf_link_down(struct enetc_pf *pf)
+{
+}
#endif
diff --git a/drivers/net/ethernet/freescale/enetc/enetc_vf.c b/drivers/net/ethernet/freescale/enetc/enetc_vf.c
index 7dcb4a0246f5..ee9567998c7c 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_vf.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc_vf.c
@@ -107,8 +107,12 @@ static int enetc_msg_vsi_send(struct enetc_si *si, struct enetc_msg_swbd *msg)
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:
+ if (FIELD_GET(ENETC_PF_MSG_CLASS_CODE, pf_msg) ==
+ ENETC_MF_CLASS_CODE_UCF_DENY)
+ return -EACCES;
+ fallthrough;
+ case ENETC_MSG_CLASS_ID_INVALID_MSG_LEN:
err = -EINVAL;
break;
case ENETC_MSG_CLASS_ID_CMD_NOT_PERMITTED:
@@ -131,6 +135,37 @@ static int enetc_msg_vsi_send(struct enetc_si *si, struct enetc_msg_swbd *msg)
return err;
}
+static int enetc_msg_link_status_notifier(struct enetc_si *si, bool reg)
+{
+ struct device *dev = &si->pdev->dev;
+ struct enetc_msg_swbd msg_swbd;
+ u8 cmd_id;
+
+ 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;
+
+ cmd_id = reg ? ENETC_MSG_REGISTER_LINK_CHANGE_NOTIFIER :
+ ENETC_MSG_UNREGISTER_LINK_CHANGE_NOTIFIER;
+ enetc_msg_fill_common_hdr(&msg_swbd, ENETC_MSG_CLASS_ID_LINK_STATUS,
+ cmd_id, 0, 0);
+
+ return enetc_msg_vsi_send(si, &msg_swbd);
+}
+
+static int enetc_vf_reg_link_status_notifier(struct enetc_si *si)
+{
+ return enetc_msg_link_status_notifier(si, true);
+}
+
+static int enetc_vf_unreg_link_status_notifier(struct enetc_si *si)
+{
+ return enetc_msg_link_status_notifier(si, false);
+}
+
static int enetc_msg_vsi_set_primary_mac_addr(struct enetc_ndev_priv *priv,
struct sockaddr *saddr)
{
@@ -209,6 +244,193 @@ static int enetc_vf_setup_tc(struct net_device *ndev, enum tc_setup_type type,
}
}
+static int enetc_vf_set_mac_promisc(struct enetc_si *si, int type,
+ bool promisc, bool flush_macs)
+{
+ struct enetc_msg_mac_promisc_mode *msg;
+ struct device *dev = &si->pdev->dev;
+ struct enetc_msg_swbd msg_swbd;
+
+ if (!(type & ENETC_MAC_FILTER_TYPE_ALL))
+ return -EINVAL;
+
+ msg_swbd.size = ALIGN(sizeof(*msg), ENETC_MSG_ALIGN);
+ msg_swbd.vaddr = dma_alloc_coherent(dev, msg_swbd.size,
+ &msg_swbd.dma, GFP_KERNEL);
+ if (!msg_swbd.vaddr)
+ return -ENOMEM;
+
+ msg = (struct enetc_msg_mac_promisc_mode *)msg_swbd.vaddr;
+ msg->config = FIELD_PREP(ENETC_MSG_MAC_TYPE,
+ type & ENETC_MAC_FILTER_TYPE_ALL);
+ msg->config |= FIELD_PREP(ENETC_MSG_MAC_PROMISC_MODE, promisc);
+ msg->config |= FIELD_PREP(ENETC_MSG_MAC_FLUSH_MACS, flush_macs);
+ enetc_msg_fill_common_hdr(&msg_swbd, ENETC_MSG_CLASS_ID_MAC_FILTER,
+ ENETC_MSG_SET_MAC_PROMISC_MODE, 0, 0);
+
+ return enetc_msg_vsi_send(si, &msg_swbd);
+}
+
+static int enetc_vf_set_mac_hash_filter(struct enetc_si *si,
+ struct netdev_hw_addr_list *uc,
+ struct netdev_hw_addr_list *mc)
+{
+ struct enetc_msg_mac_hash_filter *msg;
+ struct enetc_mac_filter *mac_filter;
+ struct device *dev = &si->pdev->dev;
+ struct enetc_msg_swbd msg_swbd;
+ struct netdev_hw_addr *ha;
+ int mac_filter_type = 0;
+ u32 tbl_cnt = 0;
+ u32 msg_size;
+ int i = 0;
+
+ if (!uc && !mc)
+ return 0;
+
+ if (uc) {
+ tbl_cnt += 2;
+ mac_filter_type |= ENETC_MAC_FILTER_TYPE_UC;
+ }
+
+ if (mc) {
+ tbl_cnt += 2;
+ mac_filter_type |= ENETC_MAC_FILTER_TYPE_MC;
+ }
+
+ msg_size = struct_size(msg, hash_tbl, tbl_cnt);
+ msg_swbd.size = ALIGN(msg_size, ENETC_MSG_ALIGN);
+ msg_swbd.vaddr = dma_alloc_coherent(dev, msg_swbd.size,
+ &msg_swbd.dma, GFP_KERNEL);
+ if (!msg_swbd.vaddr)
+ return -ENOMEM;
+
+ msg = (struct enetc_msg_mac_hash_filter *)msg_swbd.vaddr;
+ msg->sz_type = FIELD_PREP(ENETC_MSG_MAC_TYPE, mac_filter_type);
+ msg->sz_type |= FIELD_PREP(ENETC_MSG_MAC_HASH_SIZE,
+ ENETC_MAC_HASH_TABLE_SIZE_64);
+
+ if (uc) {
+ mac_filter = &si->mac_filter[UC];
+ enetc_reset_mac_addr_filter(mac_filter);
+ netdev_hw_addr_list_for_each(ha, uc)
+ enetc_add_mac_addr_ht_filter(mac_filter, ha->addr);
+
+ bitmap_to_arr32(&msg->hash_tbl[i], mac_filter->mac_hash_table,
+ ENETC_MADDR_HASH_TBL_SZ);
+ i += 2;
+ }
+
+ if (mc) {
+ mac_filter = &si->mac_filter[MC];
+ enetc_reset_mac_addr_filter(mac_filter);
+ netdev_hw_addr_list_for_each(ha, mc)
+ enetc_add_mac_addr_ht_filter(mac_filter, ha->addr);
+
+ bitmap_to_arr32(&msg->hash_tbl[i], mac_filter->mac_hash_table,
+ ENETC_MADDR_HASH_TBL_SZ);
+ }
+
+ enetc_msg_fill_common_hdr(&msg_swbd, ENETC_MSG_CLASS_ID_MAC_FILTER,
+ ENETC_MSG_SET_MAC_HASH_TABLE, 0, 0);
+
+ return enetc_msg_vsi_send(si, &msg_swbd);
+}
+
+static int enetc_vf_enable_iff_promisc(struct enetc_si *si)
+{
+ int err;
+
+ err = enetc_vf_set_mac_promisc(si, ENETC_MAC_FILTER_TYPE_ALL,
+ true, true);
+ if (err)
+ dev_err(&si->pdev->dev,
+ "Failed to enable promiscuous mode, err: %pe\n",
+ ERR_PTR(err));
+
+ return err;
+}
+
+static int enetc_vf_disable_iff_promisc(struct enetc_si *si,
+ struct netdev_hw_addr_list *uc,
+ struct netdev_hw_addr_list *mc)
+{
+ int err;
+
+ err = enetc_vf_set_mac_hash_filter(si, uc, mc);
+ if (err) {
+ dev_err_once(&si->pdev->dev,
+ "Failed to set MAC hash filters, err: %pe\n",
+ ERR_PTR(err));
+ return err;
+ }
+
+ err = enetc_vf_set_mac_promisc(si, ENETC_MAC_FILTER_TYPE_ALL,
+ false, false);
+ if (err)
+ dev_err_once(&si->pdev->dev,
+ "Failed to disable promiscuous mode, err: %pe\n",
+ ERR_PTR(err));
+
+ return err;
+}
+
+static int enetc_vf_enable_iff_allmulti(struct enetc_si *si,
+ struct netdev_hw_addr_list *uc)
+{
+ int err;
+
+ err = enetc_vf_set_mac_promisc(si, ENETC_MAC_FILTER_TYPE_MC,
+ true, true);
+ if (err) {
+ dev_err(&si->pdev->dev,
+ "Failed to enable multicast promiscuous mode, err: %pe\n",
+ ERR_PTR(err));
+ return err;
+ }
+
+ err = enetc_vf_set_mac_hash_filter(si, uc, NULL);
+ if (err) {
+ dev_err(&si->pdev->dev,
+ "Failed to set unicast filter, err: %pe\n",
+ ERR_PTR(err));
+ return err;
+ }
+
+ err = enetc_vf_set_mac_promisc(si, ENETC_MAC_FILTER_TYPE_UC,
+ false, false);
+ if (err)
+ dev_err(&si->pdev->dev,
+ "Failed to disable unicast promiscuous mode, err: %pe\n",
+ ERR_PTR(err));
+
+ return err;
+}
+
+static int enetc_vf_set_rx_mode(struct net_device *ndev,
+ struct netdev_hw_addr_list *uc,
+ struct netdev_hw_addr_list *mc)
+{
+ struct enetc_ndev_priv *priv = netdev_priv(ndev);
+ struct enetc_si *si = priv->si;
+ int err;
+
+ if (ndev->flags & IFF_PROMISC)
+ err = enetc_vf_enable_iff_promisc(si);
+ else if (ndev->flags & IFF_ALLMULTI)
+ err = enetc_vf_enable_iff_allmulti(si, uc);
+ else
+ err = enetc_vf_disable_iff_promisc(si, uc, mc);
+
+ /* If the error code is -EOPNOTSUPP or -EACCES or -EPERM, return 0
+ * directly to avoid meaningless retries.
+ */
+ if (err == -EOPNOTSUPP || err == -EACCES || err == -EPERM)
+ return 0;
+
+ return err;
+}
+
/* Probing/ Init */
static const struct net_device_ops enetc_ndev_ops = {
.ndo_open = enetc_open,
@@ -223,6 +445,20 @@ static const struct net_device_ops enetc_ndev_ops = {
.ndo_hwtstamp_set = enetc_hwtstamp_set,
};
+static const struct net_device_ops enetc4_ndev_ops = {
+ .ndo_open = enetc_open,
+ .ndo_stop = enetc_close,
+ .ndo_start_xmit = enetc_xmit,
+ .ndo_get_stats = enetc_get_stats,
+ .ndo_set_mac_address = enetc_vf_set_mac_addr,
+ .ndo_set_features = enetc_vf_set_features,
+ .ndo_eth_ioctl = enetc_ioctl,
+ .ndo_setup_tc = enetc_vf_setup_tc,
+ .ndo_hwtstamp_get = enetc_hwtstamp_get,
+ .ndo_hwtstamp_set = enetc_hwtstamp_set,
+ .ndo_set_rx_mode_async = enetc_vf_set_rx_mode,
+};
+
static void enetc_vf_get_revision(struct enetc_si *si)
{
int ip_mn;
@@ -276,15 +512,146 @@ static void enetc_vf_netdev_setup(struct enetc_si *si, struct net_device *ndev,
ndev->vlan_features = NETIF_F_SG | NETIF_F_HW_CSUM |
NETIF_F_TSO | NETIF_F_TSO6;
+ if (!is_enetc_rev1(si))
+ ndev->priv_flags |= IFF_UNICAST_FLT;
+
if (si->num_rss) {
ndev->hw_features |= NETIF_F_RXHASH;
ndev->features |= NETIF_F_RXHASH;
}
+ if (si->drvdata->tx_csum)
+ priv->active_offloads |= ENETC_F_TXCSUM;
+
+ if (si->hw_features & ENETC_SI_F_LSO)
+ priv->active_offloads |= ENETC_F_LSO;
+
/* pick up primary MAC address from SI */
enetc_load_primary_mac_addr(&si->hw, ndev);
}
+static void enetc_vf_enable_mr_int(struct enetc_si *si)
+{
+ if (is_enetc_rev1(si))
+ return;
+
+ enetc_wr(&si->hw, ENETC_VSIIER, VSIIER_MRIE);
+}
+
+static void enetc_vf_disable_mr_int(struct enetc_si *si)
+{
+ if (is_enetc_rev1(si))
+ return;
+
+ enetc_wr(&si->hw, ENETC_VSIIER, 0);
+}
+
+static void enetc_vf_msg_handle_link_status(struct enetc_si *si, u8 status)
+{
+ bool tx_pause = !!(status & ENETC_CLASS_CODE_TX_PAUSE_EN);
+ bool link_down = !!(status & ENETC_CLASS_CODE_LINK_DOWN);
+ struct enetc_ndev_priv *priv = netdev_priv(si->ndev);
+ struct net_device *ndev = si->ndev;
+
+ rtnl_lock();
+ if (!netif_running(ndev))
+ goto unlock_rtnl;
+
+ if (link_down) {
+ if (netif_carrier_ok(ndev)) {
+ netif_carrier_off(ndev);
+ netdev_info(ndev, "Link is Down\n");
+ }
+
+ goto unlock_rtnl;
+ }
+
+ /* Link is up */
+ enetc_set_congestion_mode(priv, tx_pause);
+
+ if (!netif_carrier_ok(ndev)) {
+ netif_carrier_on(ndev);
+ netdev_info(ndev, "Link is Up, tx pause %s\n",
+ tx_pause ? "on" : "off");
+ }
+
+unlock_rtnl:
+ rtnl_unlock();
+}
+
+static void enetc_vf_msg_task(struct work_struct *work)
+{
+ struct enetc_si *si = container_of(work, struct enetc_si, msg_task);
+ struct enetc_hw *hw = &si->hw;
+ u8 class_id, class_code;
+ u16 pf_msg;
+
+ /* W1C to clear the message received interrupt event */
+ enetc_wr(hw, ENETC_VSIIDR, VSIIDR_MR);
+
+ /* Reading VSIMSGRR retrieves the message data and acknowledges to
+ * the PF that the message was received and another message can be
+ * sent.
+ */
+ pf_msg = FIELD_GET(VSIMSGRR_MC, enetc_rd(hw, ENETC_VSIMSGRR));
+ class_id = FIELD_GET(ENETC_PF_MSG_CLASS_ID, pf_msg);
+
+ switch (class_id) {
+ case ENETC_MSG_CLASS_ID_LINK_STATUS:
+ class_code = FIELD_GET(ENETC_PF_MSG_CLASS_CODE_U8, pf_msg);
+ enetc_vf_msg_handle_link_status(si, class_code);
+ break;
+ default:
+ dev_err(&si->pdev->dev,
+ "Unsupported Message Class ID (0x%02x) from PF\n",
+ class_id);
+ }
+
+ enetc_vf_enable_mr_int(si);
+}
+
+static irqreturn_t enetc_vf_msg_msix_handler(int irq, void *data)
+{
+ struct enetc_si *si = (struct enetc_si *)data;
+
+ enetc_vf_disable_mr_int(si);
+ queue_work(si->workqueue, &si->msg_task);
+
+ return IRQ_HANDLED;
+}
+
+static int enetc_vf_register_msg_msix(struct enetc_si *si)
+{
+ int irq, err;
+
+ if (is_enetc_rev1(si))
+ return 0;
+
+ snprintf(si->msg_int_name, sizeof(si->msg_int_name), "%s-pfmsg",
+ pci_name(si->pdev));
+ irq = pci_irq_vector(si->pdev, ENETC_SI_INT_IDX);
+ err = request_irq(irq, enetc_vf_msg_msix_handler, 0,
+ si->msg_int_name, si);
+ if (err) {
+ dev_err(&si->pdev->dev,
+ "VF messaging: request_irq() failed!\n");
+ return err;
+ }
+
+ /* set one IRQ entry for PSI-to-VSI messaging */
+ enetc_wr(&si->hw, ENETC_SIMSIVR, ENETC_SI_INT_IDX);
+
+ return 0;
+}
+
+static void enetc_vf_free_msg_msix(struct enetc_si *si)
+{
+ if (is_enetc_rev1(si))
+ return;
+
+ free_irq(pci_irq_vector(si->pdev, ENETC_SI_INT_IDX), si);
+}
+
static const struct enetc_si_ops enetc_vsi_ops = {
.get_rss_table = enetc_get_rss_table,
.set_rss_table = enetc_set_rss_table,
@@ -292,6 +659,46 @@ static const struct enetc_si_ops enetc_vsi_ops = {
.teardown_cbdr = enetc_teardown_cbdr,
};
+static const struct enetc_si_ops enetc4_vsi_ops = {
+ .get_rss_table = enetc4_get_rss_table,
+ .set_rss_table = enetc4_set_rss_table,
+ .setup_cbdr = enetc4_setup_cbdr,
+ .teardown_cbdr = enetc4_teardown_cbdr,
+ .vf_reg_link_status_notifier = enetc_vf_reg_link_status_notifier,
+ .vf_unreg_link_status_notifier = enetc_vf_unreg_link_status_notifier,
+};
+
+static int enetc_vf_wq_task_init(struct enetc_si *si)
+{
+ if (is_enetc_rev1(si))
+ return 0;
+
+ si->workqueue = alloc_ordered_workqueue("enetc-%s-wq", WQ_MEM_RECLAIM,
+ pci_name(si->pdev));
+ if (!si->workqueue)
+ return -ENOMEM;
+
+ INIT_WORK(&si->msg_task, enetc_vf_msg_task);
+
+ return 0;
+}
+
+static void enetc_vf_wq_task_destroy(struct enetc_si *si)
+{
+ if (!si->workqueue)
+ return;
+
+ disable_work_sync(&si->msg_task);
+
+ /* Disable the MR interrupt */
+ enetc_vf_disable_mr_int(si);
+ enetc_wr(&si->hw, ENETC_VSIIDR, VSIIDR_MR);
+ /* Reading VSIMSGRR to clear the MS bit on the PF's side */
+ enetc_rd(&si->hw, ENETC_VSIMSGRR);
+
+ destroy_workqueue(si->workqueue);
+}
+
static int enetc_vf_probe(struct pci_dev *pdev,
const struct pci_device_id *ent)
{
@@ -307,7 +714,11 @@ static int enetc_vf_probe(struct pci_dev *pdev,
si = pci_get_drvdata(pdev);
enetc_vf_get_revision(si);
- si->ops = &enetc_vsi_ops;
+ if (is_enetc_rev1(si))
+ si->ops = &enetc_vsi_ops;
+ else
+ si->ops = &enetc4_vsi_ops;
+
err = enetc_get_driver_data(si);
if (err) {
dev_err_probe(&pdev->dev, err,
@@ -331,7 +742,10 @@ static int enetc_vf_probe(struct pci_dev *pdev,
goto err_alloc_netdev;
}
- enetc_vf_netdev_setup(si, ndev, &enetc_ndev_ops);
+ if (is_enetc_rev1(si))
+ enetc_vf_netdev_setup(si, ndev, &enetc_ndev_ops);
+ else
+ enetc_vf_netdev_setup(si, ndev, &enetc4_ndev_ops);
priv = netdev_priv(ndev);
@@ -359,15 +773,32 @@ static int enetc_vf_probe(struct pci_dev *pdev,
goto err_alloc_msix;
}
+ err = enetc_vf_wq_task_init(si);
+ if (err) {
+ dev_err(&pdev->dev, "Failed to init workqueue\n");
+ goto err_wq_init;
+ }
+
+ err = enetc_vf_register_msg_msix(si);
+ if (err) {
+ dev_err(&pdev->dev, "Failed to register msg irq\n");
+ goto err_register_msg_msix;
+ }
+
+ netif_carrier_off(ndev);
+ /* Enable message received interrupt */
+ enetc_vf_enable_mr_int(si);
err = register_netdev(ndev);
if (err)
goto err_reg_netdev;
- netif_carrier_off(ndev);
-
return 0;
err_reg_netdev:
+ enetc_vf_free_msg_msix(si);
+err_register_msg_msix:
+ enetc_vf_wq_task_destroy(si);
+err_wq_init:
enetc_free_msix(priv);
err_config_si:
err_alloc_msix:
@@ -395,6 +826,13 @@ static void enetc_vf_remove(struct pci_dev *pdev)
priv = netdev_priv(si->ndev);
unregister_netdev(si->ndev);
+ /* Disable promiscuous mode and clear MAC filters */
+ if (!is_enetc_rev1(si))
+ enetc_vf_set_mac_promisc(si, ENETC_MAC_FILTER_TYPE_ALL,
+ false, true);
+
+ enetc_vf_free_msg_msix(si);
+ enetc_vf_wq_task_destroy(si);
enetc_free_msix(priv);
enetc_free_si_resources(priv);
@@ -409,6 +847,7 @@ static void enetc_vf_remove(struct pci_dev *pdev)
static const struct pci_device_id enetc_vf_id_table[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_FREESCALE, ENETC_DEV_ID_VF) },
+ { PCI_DEVICE(NXP_ENETC_VENDOR_ID, NXP_ENETC_VF_DEV_ID) },
{ 0, } /* End of table. */
};
MODULE_DEVICE_TABLE(pci, enetc_vf_id_table);