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 #include -#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) +#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_msg_disable_mr_int(struct enetc_pf *pf) +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_vf_flr_handler(struct enetc_pf *pf) +{ + u32 flr_mask = ENETC_VFFLR_MASK(pf->num_vfs); + struct enetc_hw *hw = &pf->si->hw; + 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_pf *pf = container_of(work, struct enetc_pf, msg_task); - u32 mr_mask = ENETC_PSIMR_MASK(pf->num_vfs); - struct enetc_hw *hw = &pf->si->hw; - u32 mr_status; + 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); +static void enetc_get_psimmhfr_offsets(struct enetc_si *si, int si_id, + int *psimmhfr0, int *psimmhfr1) +{ + if (is_enetc_rev1(si)) { + bool err = si->errata & ENETC_ERR_UCMCSWP; + + *psimmhfr0 = ENETC_PSIMMHFR0(si_id, err); + *psimmhfr1 = ENETC_PSIMMHFR1(si_id); + } else { + *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; - 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); - } else { - psimmhfr0_off = ENETC4_PSIMMHFR0(si_id); - psimmhfr1_off = ENETC4_PSIMMHFR1(si_id); - } - + 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);