[PATCH net-next 14/15] net: enetc: add PSI-to-VSI link status notification support for VF
From: <hidden>
Date: 2026-06-05 07:24:25
Also in:
imx, lkml
Subsystem:
freescale enetc ethernet drivers, networking drivers, the rest · Maintainers:
Claudiu Manoil, Vladimir Oltean, Wei Fang, Clark Wang, Andrew Lunn, "David S. Miller", Eric Dumazet, Jakub Kicinski, Paolo Abeni, Linus Torvalds
From: Wei Fang <wei.fang@nxp.com> Add infrastructure for ENETC v4 VFs to track PF link status changes via the PSI-to-VSI messaging channel. Two new ops, vf_reg_link_status_notifier and vf_unreg_link_status_notifier, are added to enetc_si_ops and wired into enetc_phylink_connect() and enetc_close() for the phy-less path. The feature is populated only in enetc4_vsi_ops; rev1 hardware is not affected. On enetc_open(), the VF sends a REGISTER_LINK_CHANGE_NOTIFIER message to the PF through the VSI-to-PSI messaging channel. The PF records the VF in link_status_ms_mask, and immediately sends the current link status so that the VF carrier reflects reality as soon as the interface comes up. On every subsequent PF link transition the PF broadcasts a 16-bit notification to all registered VFs. On the VF side, a dedicated MSI-X vector handles incoming PSI-to-VSI messages. The interrupt handler schedules a work item which parses the notification and updates the carrier state via netif_carrier_on() or netif_carrier_off() accordingly. Signed-off-by: Wei Fang <wei.fang@nxp.com> --- drivers/net/ethernet/freescale/enetc/enetc.c | 11 ++ drivers/net/ethernet/freescale/enetc/enetc.h | 4 + .../net/ethernet/freescale/enetc/enetc_hw.h | 9 + .../net/ethernet/freescale/enetc/enetc_vf.c | 185 +++++++++++++++++- 4 files changed, 208 insertions(+), 1 deletion(-)
diff --git a/drivers/net/ethernet/freescale/enetc/enetc.c b/drivers/net/ethernet/freescale/enetc/enetc.c
index fdceaf36daa7..a1a8a1551cd8 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc.c@@ -2892,11 +2892,15 @@ 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) + return si->ops->vf_reg_link_status_notifier(si); + netif_carrier_on(ndev); return 0; }
@@ -2968,6 +2972,7 @@ int enetc_open(struct net_device *ndev) { struct enetc_ndev_priv *priv = netdev_priv(ndev); struct enetc_bdr_resource *tx_res, *rx_res; + struct enetc_si *si = priv->si; bool extended; int err;
@@ -3010,6 +3015,8 @@ int enetc_open(struct net_device *ndev) err_alloc_tx: if (priv->phylink) phylink_disconnect_phy(priv->phylink); + else if (si->ops->vf_unreg_link_status_notifier) + si->ops->vf_unreg_link_status_notifier(si); err_phy_connect: enetc_free_irqs(priv); err_setup_irqs:
@@ -3050,6 +3057,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);
@@ -3057,6 +3065,9 @@ 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) + si->ops->vf_unreg_link_status_notifier(si); + netif_carrier_off(ndev); }
diff --git a/drivers/net/ethernet/freescale/enetc/enetc.h b/drivers/net/ethernet/freescale/enetc/enetc.h
index 24d9f89aee73..1666dfe81cbb 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc.h
+++ b/drivers/net/ethernet/freescale/enetc/enetc.h@@ -300,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 */
diff --git a/drivers/net/ethernet/freescale/enetc/enetc_hw.h b/drivers/net/ethernet/freescale/enetc/enetc_hw.h
index 62844344ff29..5dc4532607b3 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_hw.h
+++ b/drivers/net/ethernet/freescale/enetc/enetc_hw.h@@ -84,6 +84,9 @@ static inline u32 enetc_vsi_set_msize(u32 size) #define PSIMSGSR_MS(n) BIT((n) + 1) /* n is VF index */ #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
@@ -107,6 +110,12 @@ 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
diff --git a/drivers/net/ethernet/freescale/enetc/enetc_vf.c b/drivers/net/ethernet/freescale/enetc/enetc_vf.c
index 418ee98da17d..9cace77110b8 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_vf.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc_vf.c@@ -131,6 +131,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) {
@@ -411,6 +442,113 @@ static void enetc_vf_netdev_setup(struct enetc_si *si, struct net_device *ndev, enetc_load_primary_mac_addr(&si->hw, ndev); } +static void enetc_vf_enable_mr_int(struct enetc_hw *hw) +{ + u32 val = enetc_rd(hw, ENETC_VSIIER) | VSIIER_MRIE; + + enetc_wr(hw, ENETC_VSIIER, val); +} + +static void enetc_vf_disable_mr_int(struct enetc_hw *hw) +{ + u32 val = enetc_rd(hw, ENETC_VSIIER) & (~VSIIER_MRIE); + + enetc_wr(hw, ENETC_VSIIER, val); +} + +static void enetc_vf_msg_handle_link_status(struct enetc_si *si, u8 class_code) +{ + struct net_device *ndev = si->ndev; + + switch (class_code) { + case ENETC_LINK_STATUS_CLASS_CODE_UP: + if (!netif_carrier_ok(ndev)) { + netif_carrier_on(ndev); + netdev_info(ndev, "Link is Up\n"); + } + break; + case ENETC_LINK_STATUS_CLASS_CODE_DOWN: + if (netif_carrier_ok(ndev)) { + netif_carrier_off(ndev); + netdev_info(ndev, "Link is Down\n"); + } + break; + } +} + +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; + + pf_msg = FIELD_GET(VSIMSGRR_MC, enetc_rd(hw, ENETC_VSIMSGRR)); + /* W1C to clear the message received interrupt event */ + enetc_wr(hw, ENETC_VSIIDR, VSIIDR_MR); + + class_id = FIELD_GET(ENETC_PF_MSG_CLASS_ID, pf_msg); + class_code = FIELD_GET(ENETC_PF_MSG_CLASS_CODE, pf_msg); + + switch (class_id) { + case ENETC_MSG_CLASS_ID_LINK_STATUS: + 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(hw); +} + +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->hw); + 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); + + /* Enable message received interrupt */ + enetc_vf_enable_mr_int(&si->hw); + + return 0; +} + +static void enetc_vf_free_msg_msix(struct enetc_si *si) +{ + if (is_enetc_rev1(si)) + return; + + enetc_vf_disable_mr_int(&si->hw); + 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,
@@ -423,8 +561,36 @@ static const struct enetc_si_ops enetc4_vsi_ops = { .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) +{ + char wq_name[24]; + + if (is_enetc_rev1(si)) + return 0; + + snprintf(wq_name, sizeof(wq_name), "enetc-%s", pci_name(si->pdev)); + si->workqueue = create_singlethread_workqueue(wq_name); + 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); + destroy_workqueue(si->workqueue); +} + static int enetc_vf_probe(struct pci_dev *pdev, const struct pci_device_id *ent) {
@@ -489,6 +655,18 @@ 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; + } + err = register_netdev(ndev); if (err) goto err_reg_netdev;
@@ -498,6 +676,10 @@ static int enetc_vf_probe(struct pci_dev *pdev, 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:
@@ -524,7 +706,8 @@ static void enetc_vf_remove(struct pci_dev *pdev) priv = netdev_priv(si->ndev); unregister_netdev(si->ndev); - + enetc_vf_free_msg_msix(si); + enetc_vf_wq_task_destroy(si); enetc_free_msix(priv); enetc_free_si_resources(priv);
--
2.34.1