[PATCH net-next 03/15] net: enetc: add link status message support to PF driver

From: wei . fang

Date: Fri Jun 05 2026 - 03:32:57 EST


From: Wei Fang <wei.fang@xxxxxxx>

Add link status message support to the PF driver using three command IDs
under message class 0x80 (ENETC_MSG_CLASS_ID_LINK_STATUS):

1. ENETC_MSG_GET_CURRENT_LINK_STATUS
The VF queries the current PF link status synchronously. This command is
not used by the Linux VF driver but is intended for DPDK-owned VFs.

2. ENETC_MSG_REGISTER_LINK_CHANGE_NOTIFIER
The VF registers for link change notification. Upon registration, the PF
immediately notifies the VF of the current link status via a PSI-to-VSI
message, and continues to do so on every subsequent link state change.

3. ENETC_MSG_UNREGISTER_LINK_CHANGE_NOTIFIER
The VF unregisters from link change notification.

PSI-to-VSI notifications are sent via the ENETC_PSIMSGSR register. A new
msg_lock mutex is introduced in struct enetc_pf to protect concurrent
access between the phylink callbacks and the VSI-to-PSI message handler.
The bitmask link_status_ms_mask tracks which VFs have registered for
notifications and is cleared when SR-IOV is disabled.

Two functions are exported for use by PF drivers:
enetc_pf_notify_vf_link_up()
enetc_pf_notify_vf_link_down()

Through this mechanism, VFs can accurately perceive the link status and
report it to upper layers such as the kernel network stack, containers,
and virtual machines.

Note that currently only the ENETC v4 driver supports this feature, while
v1 does not.

Signed-off-by: Wei Fang <wei.fang@xxxxxxx>
---
.../net/ethernet/freescale/enetc/enetc4_pf.c | 2 +
.../net/ethernet/freescale/enetc/enetc_hw.h | 4 +
.../ethernet/freescale/enetc/enetc_mailbox.h | 18 +++
.../net/ethernet/freescale/enetc/enetc_msg.c | 152 +++++++++++++++++-
.../net/ethernet/freescale/enetc/enetc_pf.h | 4 +
.../freescale/enetc/enetc_pf_common.c | 2 +
.../freescale/enetc/enetc_pf_common.h | 9 ++
7 files changed, 185 insertions(+), 6 deletions(-)

diff --git a/drivers/net/ethernet/freescale/enetc/enetc4_pf.c b/drivers/net/ethernet/freescale/enetc/enetc4_pf.c
index ee52ff929576..ad211eefd9d4 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc4_pf.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc4_pf.c
@@ -940,6 +940,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,
@@ -950,6 +951,7 @@ static void enetc4_pl_mac_link_down(struct phylink_config *config,

enetc4_mac_rx_graceful_stop(pf);
enetc4_mac_tx_graceful_stop(pf);
+ enetc_pf_notify_vf_link_down(pf);
}

static const struct phylink_mac_ops enetc_pl_mac_ops = {
diff --git a/drivers/net/ethernet/freescale/enetc/enetc_hw.h b/drivers/net/ethernet/freescale/enetc/enetc_hw.h
index bf99b65d7598..37c6060ccc82 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_hw.h
+++ b/drivers/net/ethernet/freescale/enetc/enetc_hw.h
@@ -80,6 +80,10 @@ 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
+#define PSIMSGSR_MS(n) BIT((n) + 1) /* n is VF index */
+#define PSIMSGSR_MC GENMASK(31, 16)
+
/* SI statistics */
#define ENETC_SIROCT 0x300
#define ENETC_SIRFRM 0x308
diff --git a/drivers/net/ethernet/freescale/enetc/enetc_mailbox.h b/drivers/net/ethernet/freescale/enetc/enetc_mailbox.h
index d9677da38989..43b9ee2ab3e4 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_mailbox.h
+++ b/drivers/net/ethernet/freescale/enetc/enetc_mailbox.h
@@ -107,6 +107,7 @@ 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_IP_REVISION = 0xf0,
};

@@ -118,11 +119,23 @@ 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,
+};
+
/* Class-specific error return codes of MAC filter */
enum enetc_mac_filter_class_code {
ENETC_MF_CLASS_CODE_INVALID_MAC,
};

+/* Class-specific notifications/codes of link status */
+enum enetc_link_status_class_code {
+ ENETC_LINK_STATUS_CLASS_CODE_UP,
+ ENETC_LINK_STATUS_CLASS_CODE_DOWN,
+};
+
struct enetc_msg_swbd {
void *vaddr;
dma_addr_t dma;
@@ -161,6 +174,11 @@ struct enetc_msg_mac_exact_filter {
/* 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
*/
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 8d4d0689807d..c93b4e6913d8 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_msg.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc_msg.c
@@ -128,6 +128,113 @@ static u16 enetc_msg_handle_ip_revision(struct enetc_pf *pf, void *vf_msg)
}
}

+static u16 enetc_msg_get_link_status(struct enetc_pf *pf)
+{
+ struct net_device *ndev = pf->si->ndev;
+ u16 pf_msg;
+
+ pf_msg = FIELD_PREP(ENETC_PF_MSG_CLASS_ID,
+ ENETC_MSG_CLASS_ID_LINK_STATUS);
+
+ if (netif_carrier_ok(ndev))
+ pf_msg |= FIELD_PREP(ENETC_PF_MSG_CLASS_CODE,
+ ENETC_LINK_STATUS_CLASS_CODE_UP);
+ else
+ pf_msg |= FIELD_PREP(ENETC_PF_MSG_CLASS_CODE,
+ ENETC_LINK_STATUS_CLASS_CODE_DOWN);
+
+ return pf_msg;
+}
+
+static int enetc_pf_send_msg(struct enetc_pf *pf, u32 msg_code, u16 ms_mask)
+{
+ struct enetc_si *si = pf->si;
+ u32 val;
+
+ enetc_wr(&si->hw, ENETC_PSIMSGSR,
+ FIELD_PREP(PSIMSGSR_MC, msg_code) | ms_mask);
+
+ return read_poll_timeout(enetc_rd, val, !(val & ms_mask), 1000,
+ 200000, false, &si->hw, ENETC_PSIMSGSR);
+}
+
+static void enetc_msg_notify_vf_link_status(struct enetc_pf *pf, u16 ms_mask,
+ bool link_up)
+{
+ u16 pf_msg = FIELD_PREP(ENETC_PF_MSG_CLASS_ID,
+ ENETC_MSG_CLASS_ID_LINK_STATUS);
+
+ if (link_up)
+ pf_msg |= FIELD_PREP(ENETC_PF_MSG_CLASS_CODE,
+ ENETC_LINK_STATUS_CLASS_CODE_UP);
+ else
+ pf_msg |= FIELD_PREP(ENETC_PF_MSG_CLASS_CODE,
+ ENETC_LINK_STATUS_CLASS_CODE_DOWN);
+
+ if (enetc_pf_send_msg(pf, pf_msg, ms_mask))
+ dev_err_ratelimited(&pf->si->pdev->dev,
+ "PF notifies link status failed\n");
+}
+
+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 void enetc_msg_register_link_status_notifier(struct enetc_pf *pf,
+ int vf_id)
+{
+ u16 pf_msg = FIELD_PREP(ENETC_PF_MSG_CLASS_ID,
+ ENETC_MSG_CLASS_ID_CMD_SUCCESS);
+
+ mutex_lock(&pf->msg_lock);
+
+ pf->link_status_ms_mask |= PSIMSGSR_MS(vf_id);
+ enetc_pf_reply_msg(&pf->si->hw, vf_id, pf_msg);
+
+ /* Notify VF the current link status */
+ enetc_msg_notify_vf_link_status(pf, PSIMSGSR_MS(vf_id),
+ netif_carrier_ok(pf->si->ndev));
+
+ mutex_unlock(&pf->msg_lock);
+}
+
+static void enetc_msg_unregister_link_status_notifier(struct enetc_pf *pf,
+ int vf_id)
+{
+ u16 pf_msg = FIELD_PREP(ENETC_PF_MSG_CLASS_ID,
+ ENETC_MSG_CLASS_ID_CMD_SUCCESS);
+
+ mutex_lock(&pf->msg_lock);
+
+ pf->link_status_ms_mask &= ~PSIMSGSR_MS(vf_id);
+ enetc_pf_reply_msg(&pf->si->hw, vf_id, pf_msg);
+
+ mutex_unlock(&pf->msg_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:
+ return enetc_msg_get_link_status(pf);
+ case ENETC_MSG_REGISTER_LINK_CHANGE_NOTIFIER:
+ enetc_msg_register_link_status_notifier(pf, vf_id);
+ return 0;
+ case ENETC_MSG_UNREGISTER_LINK_CHANGE_NOTIFIER:
+ enetc_msg_unregister_link_status_notifier(pf, vf_id);
+ return 0;
+ default:
+ return ENETC_PF_MSG_NOTSUPP;
+ }
+}
+
static void enetc_msg_handle_rxmsg(struct enetc_pf *pf, int vf_id,
u16 *pf_msg)
{
@@ -203,6 +310,9 @@ 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;
default:
dev_err_ratelimited(dev,
"Unsupported message class ID: 0x%x\n",
@@ -228,7 +338,6 @@ static void enetc_msg_task(struct work_struct *work)
goto out;

for (i = 0; i < pf->num_vfs; i++) {
- u32 psimsgrr;
u16 msg_code;

if (!(ENETC_PSIMR_BIT(i) & mr_status))
@@ -236,12 +345,13 @@ 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.
+ */
+ 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:
@@ -361,6 +471,7 @@ int enetc_sriov_configure(struct pci_dev *pdev, int num_vfs)
if (!num_vfs) {
pci_disable_sriov(pdev);
enetc_msg_psi_free(pf);
+ pf->link_status_ms_mask = 0;
pf->num_vfs = 0;
} else {
pf->num_vfs = num_vfs;
@@ -388,3 +499,32 @@ int enetc_sriov_configure(struct pci_dev *pdev, int num_vfs)
return err;
}
EXPORT_SYMBOL_GPL(enetc_sriov_configure);
+
+static void enetc_pf_notify_vf_link_status(struct enetc_pf *pf,
+ bool link_up)
+{
+ u16 ms_mask;
+
+ mutex_lock(&pf->msg_lock);
+
+ ms_mask = pf->link_status_ms_mask;
+ if (!ms_mask)
+ goto msg_unlock;
+
+ enetc_msg_notify_vf_link_status(pf, ms_mask, link_up);
+
+msg_unlock:
+ mutex_unlock(&pf->msg_lock);
+}
+
+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.h b/drivers/net/ethernet/freescale/enetc/enetc_pf.h
index 07bb9aab89aa..549ea3f4b5cb 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_pf.h
+++ b/drivers/net/ethernet/freescale/enetc/enetc_pf.h
@@ -60,6 +60,10 @@ struct enetc_pf {
const struct enetc_pf_ops *ops;

int num_mfe; /* number of mac address filter table entries */
+ /* Message lock, prevent concurrent access */
+ struct mutex msg_lock;
+ 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 44c546b77d3f..e3bed5580d17 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_pf_common.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc_pf_common.c
@@ -458,6 +458,8 @@ int enetc_init_sriov_resources(struct enetc_pf *pf)
for (int i = 0; i < pf->total_vfs; i++)
mutex_init(&pf->vf_state[i].lock);

+ mutex_init(&pf->msg_lock);
+
return 0;
}
EXPORT_SYMBOL_GPL(enetc_init_sriov_resources);
diff --git a/drivers/net/ethernet/freescale/enetc/enetc_pf_common.h b/drivers/net/ethernet/freescale/enetc/enetc_pf_common.h
index 5bf7c20aba42..bbe21c739cf7 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_pf_common.h
+++ b/drivers/net/ethernet/freescale/enetc/enetc_pf_common.h
@@ -26,9 +26,18 @@ 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_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_notify_vf_link_up(struct enetc_pf *pf)
+{
+}
+
+static inline void enetc_pf_notify_vf_link_down(struct enetc_pf *pf)
+{}
#endif
--
2.34.1