From: Dong Yibo <dong100@mucse.com>
To: andrew+netdev@lunn.ch, davem@davemloft.net, edumazet@google.com,
kuba@kernel.org, pabeni@redhat.com, danishanwar@ti.com
Cc: linux-kernel@vger.kernel.org, netdev@vger.kernel.org, dong100@mucse.com
Subject: [PATCH net-next 4/4] net: rnpgbe: Add link status handling support
Date: Wed, 25 Mar 2026 17:12:04 +0800 [thread overview]
Message-ID: <20260325091204.94015-5-dong100@mucse.com> (raw)
In-Reply-To: <20260325091204.94015-1-dong100@mucse.com>
Add link status management infrastructure to the rnpgbe driver:
- Add link status related data structures (speed, duplex, link state)
- Implement firmware link event handling via mailbox
- Add service task for periodic link status monitoring
- Implement carrier status management (netif_carrier_on/off)
- Add port up/down notification to firmware
This enables the driver to properly track and report link status changes.
Signed-off-by: Dong Yibo <dong100@mucse.com>
---
drivers/net/ethernet/mucse/rnpgbe/rnpgbe.h | 18 +-
.../net/ethernet/mucse/rnpgbe/rnpgbe_chip.c | 31 +++-
drivers/net/ethernet/mucse/rnpgbe/rnpgbe_hw.h | 12 ++
.../net/ethernet/mucse/rnpgbe/rnpgbe_lib.c | 143 +++++++++++++++
.../net/ethernet/mucse/rnpgbe/rnpgbe_lib.h | 1 +
.../net/ethernet/mucse/rnpgbe/rnpgbe_main.c | 18 ++
.../net/ethernet/mucse/rnpgbe/rnpgbe_mbx.c | 20 +++
.../net/ethernet/mucse/rnpgbe/rnpgbe_mbx.h | 1 +
.../net/ethernet/mucse/rnpgbe/rnpgbe_mbx_fw.c | 165 ++++++++++++++++++
.../net/ethernet/mucse/rnpgbe/rnpgbe_mbx_fw.h | 47 +++++
10 files changed, 452 insertions(+), 4 deletions(-)
diff --git a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe.h b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe.h
index 13838e370165..9d80b28c4ae9 100644
--- a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe.h
+++ b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe.h
@@ -30,11 +30,10 @@ struct mucse_mbx_info {
u32 fwpf_ctrl_base;
};
-/* Enum for firmware notification modes,
- * more modes (e.g., portup, link_report) will be added in future
- **/
enum {
mucse_fw_powerup,
+ mucse_fw_portup,
+ mucse_fw_link_report_en,
};
struct mucse_hw {
@@ -43,8 +42,11 @@ struct mucse_hw {
struct pci_dev *pdev;
struct mucse_mbx_info mbx;
int port;
+ int speed;
+ bool link;
u16 cycles_per_us;
u8 pfvfnum;
+ u8 duplex;
};
struct rnpgbe_tx_desc {
@@ -190,6 +192,10 @@ struct mucse_stats {
#define M_DEFAULT_RXD 512
#define M_DEFAULT_TX_WORK 256
+enum mucse_state_t {
+ __MUCSE_DOWN,
+};
+
struct mucse {
struct net_device *netdev;
struct pci_dev *pdev;
@@ -199,6 +205,7 @@ struct mucse {
#define M_FLAG_MSI_EN BIT(1)
#define M_FLAG_MSIX_SINGLE_EN BIT(2)
#define M_FLAG_MSIX_EN BIT(3)
+#define M_FLAG_NEED_LINK_UPDATE BIT(4)
u32 flags;
struct mucse_ring *tx_ring[RNPGBE_MAX_QUEUES] ____cacheline_aligned_in_smp;
struct mucse_ring *rx_ring[RNPGBE_MAX_QUEUES] ____cacheline_aligned_in_smp;
@@ -209,6 +216,10 @@ struct mucse {
int num_q_vectors;
int rx_ring_item_count;
int num_rx_queues;
+ unsigned long state;
+ struct delayed_work serv_task;
+ struct workqueue_struct *serv_wq;
+ spinlock_t link_lock; /* spinlock for link update */
};
int rnpgbe_get_permanent_mac(struct mucse_hw *hw, u8 *perm_addr);
@@ -217,6 +228,7 @@ int rnpgbe_send_notify(struct mucse_hw *hw,
bool enable,
int mode);
int rnpgbe_init_hw(struct mucse_hw *hw, int board_type);
+void rnpgbe_set_rx(struct mucse_hw *hw, bool enable);
/* Device IDs */
#define PCI_VENDOR_ID_MUCSE 0x8848
diff --git a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_chip.c b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_chip.c
index 291e77d573fe..902c8a801ba3 100644
--- a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_chip.c
+++ b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_chip.c
@@ -66,11 +66,17 @@ int rnpgbe_send_notify(struct mucse_hw *hw,
int mode)
{
int err;
- /* Keep switch struct to support more modes in the future */
+
switch (mode) {
case mucse_fw_powerup:
err = mucse_mbx_powerup(hw, enable);
break;
+ case mucse_fw_portup:
+ err = mucse_mbx_phyup(hw, enable);
+ break;
+ case mucse_fw_link_report_en:
+ err = mucse_mbx_link_report(hw, enable);
+ break;
default:
err = -EINVAL;
}
@@ -149,3 +155,26 @@ int rnpgbe_init_hw(struct mucse_hw *hw, int board_type)
return 0;
}
+
+/**
+ * rnpgbe_set_rx - Setup rx state
+ * @hw: hw information structure
+ * @enable: set rx on or off
+ *
+ * rnpgbe_set_rx setup rx enable
+ *
+ **/
+void rnpgbe_set_rx(struct mucse_hw *hw, bool enable)
+{
+ u32 value = mucse_hw_rd32(hw, GMAC_CONTROL);
+
+ if (enable)
+ value |= GMAC_CONTROL_RE;
+ else
+ value &= ~GMAC_CONTROL_RE;
+
+ mucse_hw_wr32(hw, GMAC_CONTROL, value);
+
+ value = mucse_hw_rd32(hw, GMAC_FRAME_FILTER);
+ mucse_hw_wr32(hw, GMAC_FRAME_FILTER, value | BIT(0));
+}
diff --git a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_hw.h b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_hw.h
index ce092edf920a..b17573c57638 100644
--- a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_hw.h
+++ b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_hw.h
@@ -17,9 +17,21 @@
#define TX_AXI_RW_EN 0xc
#define RX_AXI_RW_EN 0x03
+/* mask all valid info */
+#define M_ST_MASK 0x0f000f11
+/* 31:28 set 0xa to valid it is a driver set info */
+#define M_DEFAULT_ST 0xa0000000
+/* driver setup this by own info */
+/*bit: 27:24 | 11:8 | 4 | 0 */
+/*fun: pause | speed | duplex | up/down */
+#define RNPGBE_LINK_ST 0x000c
#define RNPGBE_DMA_AXI_EN 0x0010
#define RNPGBE_LEGACY_TIME 0xd000
#define RNPGBE_LEGACY_ENABLE 0xd004
+#define MUCSE_GMAC_OFF(_n) (0x20000 + (_n))
+#define GMAC_CONTROL_RE 0x00000004
+#define GMAC_CONTROL MUCSE_GMAC_OFF(0)
+#define GMAC_FRAME_FILTER MUCSE_GMAC_OFF(0x4)
#define RNPGBE_MAX_QUEUES 8
#endif /* _RNPGBE_HW_H */
diff --git a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c
index 3dbb697a0667..ccd69224944d 100644
--- a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c
+++ b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c
@@ -9,6 +9,7 @@
#include "rnpgbe_lib.h"
#include "rnpgbe.h"
+#include "rnpgbe_mbx_fw.h"
/**
* rnpgbe_msix_other - Other irq handler
@@ -19,6 +20,10 @@
**/
static irqreturn_t rnpgbe_msix_other(int irq, void *data)
{
+ struct mucse *mucse = (struct mucse *)data;
+
+ mucse_fw_irq_handler(&mucse->hw);
+
return IRQ_HANDLED;
}
@@ -897,6 +902,8 @@ static irqreturn_t rnpgbe_intr(int irq, void *data)
struct mucse *mucse = (struct mucse *)data;
struct mucse_q_vector *q_vector;
+ mucse_fw_irq_handler(&mucse->hw);
+
q_vector = mucse->q_vector[0];
rnpgbe_irq_disable_queues(q_vector);
if (q_vector->rx.ring || q_vector->tx.ring)
@@ -1158,7 +1165,23 @@ static void rnpgbe_clean_all_rx_rings(struct mucse *mucse)
void rnpgbe_down(struct mucse *mucse)
{
struct net_device *netdev = mucse->netdev;
+ struct mucse_hw *hw = &mucse->hw;
+ int err;
+
+ set_bit(__MUCSE_DOWN, &mucse->state);
+
+ err = rnpgbe_send_notify(hw, false, mucse_fw_link_report_en);
+ if (err) {
+ dev_warn(&hw->pdev->dev, "Send link report to hw failed %d\n", err);
+ dev_warn(&hw->pdev->dev, "Fw will still report link event\n");
+ }
+ err = rnpgbe_send_notify(hw, false, mucse_fw_portup);
+ if (err) {
+ dev_warn(&hw->pdev->dev, "Send port down to hw failed %d\n", err);
+ dev_warn(&hw->pdev->dev, "Port is not truly down\n");
+ }
+ netif_carrier_off(netdev);
netif_tx_stop_all_queues(netdev);
rnpgbe_clean_all_tx_rings(mucse);
rnpgbe_irq_disable(mucse);
@@ -1174,6 +1197,8 @@ void rnpgbe_down(struct mucse *mucse)
void rnpgbe_up_complete(struct mucse *mucse)
{
struct net_device *netdev = mucse->netdev;
+ struct mucse_hw *hw = &mucse->hw;
+ int err;
rnpgbe_configure_msix(mucse);
rnpgbe_napi_enable_all(mucse);
@@ -1181,6 +1206,20 @@ void rnpgbe_up_complete(struct mucse *mucse)
netif_tx_start_all_queues(netdev);
for (int i = 0; i < mucse->num_rx_queues; i++)
mucse_ring_wr32(mucse->rx_ring[i], RNPGBE_RX_START, 1);
+
+ err = rnpgbe_send_notify(hw, true, mucse_fw_portup);
+ if (err) {
+ dev_warn(&hw->pdev->dev, "Send portup to hw failed %d\n", err);
+ dev_warn(&hw->pdev->dev, "Port is not truly up\n");
+ }
+
+ err = rnpgbe_send_notify(hw, true, mucse_fw_link_report_en);
+ if (err) {
+ dev_warn(&hw->pdev->dev, "Send link report to hw failed %d\n", err);
+ dev_warn(&hw->pdev->dev, "Fw will not report link event\n");
+ }
+ clear_bit(__MUCSE_DOWN, &mucse->state);
+ queue_delayed_work(mucse->serv_wq, &mucse->serv_task, msecs_to_jiffies(500));
}
/**
@@ -1772,3 +1811,107 @@ void rnpgbe_configure_rx(struct mucse *mucse)
dma_axi_ctl |= RX_AXI_RW_EN;
mucse_hw_wr32(hw, RNPGBE_DMA_AXI_EN, dma_axi_ctl);
}
+
+/**
+ * rnpgbe_watchdog_update_link - Update the link status
+ * @mucse: pointer to the device private structure
+ **/
+static void rnpgbe_watchdog_update_link(struct mucse *mucse)
+{
+ struct net_device *netdev = mucse->netdev;
+ struct mucse_hw *hw = &mucse->hw;
+ unsigned long flags;
+ bool link;
+ int speed;
+ u8 duplex;
+
+ if (!(mucse->flags & M_FLAG_NEED_LINK_UPDATE))
+ return;
+
+ spin_lock_irqsave(&mucse->link_lock, flags);
+
+ link = hw->link;
+ speed = hw->speed;
+ duplex = hw->duplex;
+
+ mucse->flags &= ~M_FLAG_NEED_LINK_UPDATE;
+ spin_unlock_irqrestore(&mucse->link_lock, flags);
+
+ if (link) {
+ netdev_info(netdev, "NIC Link is Up %d Mbps, %s Duplex\n",
+ speed,
+ duplex ? "Full" : "Half");
+ }
+}
+
+/**
+ * rnpgbe_watchdog_link_is_up - Update netif_carrier status and
+ * print link up message
+ * @mucse: pointer to the device private structure
+ **/
+static void rnpgbe_watchdog_link_is_up(struct mucse *mucse)
+{
+ struct net_device *netdev = mucse->netdev;
+ struct mucse_hw *hw = &mucse->hw;
+
+ /* Only continue if link was previously down */
+ if (netif_carrier_ok(netdev))
+ return;
+ rnpgbe_set_rx(hw, true);
+ netif_carrier_on(netdev);
+ netif_tx_wake_all_queues(netdev);
+}
+
+/**
+ * rnpgbe_watchdog_link_is_down - Update netif_carrier status and
+ * print link down message
+ * @mucse: pointer to the private structure
+ **/
+static void rnpgbe_watchdog_link_is_down(struct mucse *mucse)
+{
+ struct net_device *netdev = mucse->netdev;
+ struct mucse_hw *hw = &mucse->hw;
+
+ /* Only continue if link was up previously */
+ if (!netif_carrier_ok(netdev))
+ return;
+ netdev_info(netdev, "NIC Link is Down\n");
+ rnpgbe_set_rx(hw, false);
+ netif_carrier_off(netdev);
+ netif_tx_stop_all_queues(netdev);
+}
+
+/**
+ * mucse_watchdog_subtask - Check and bring link up
+ * @mucse: pointer to the device private structure
+ **/
+static void rnpgbe_watchdog_subtask(struct mucse *mucse)
+{
+ struct mucse_hw *hw = &mucse->hw;
+ /* if interface is down do nothing */
+ if (test_bit(__MUCSE_DOWN, &mucse->state))
+ return;
+
+ rnpgbe_watchdog_update_link(mucse);
+ if (hw->link)
+ rnpgbe_watchdog_link_is_up(mucse);
+ else
+ rnpgbe_watchdog_link_is_down(mucse);
+}
+
+/**
+ * rnpgbe_service_task - Manages and runs subtasks
+ * @work: pointer to work_struct containing our data
+ **/
+void rnpgbe_service_task(struct work_struct *work)
+{
+ struct mucse *mucse = container_of(work, struct mucse, serv_task.work);
+
+ if (test_bit(__MUCSE_DOWN, &mucse->state))
+ return;
+
+ rnpgbe_watchdog_subtask(mucse);
+
+ queue_delayed_work(mucse->serv_wq, &mucse->serv_task,
+ msecs_to_jiffies(500));
+}
diff --git a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.h b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.h
index 29520ad716ca..74b4b0ab0b89 100644
--- a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.h
+++ b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.h
@@ -86,4 +86,5 @@ void rnpgbe_get_stats64(struct net_device *netdev,
void rnpgbe_clean_rx_ring(struct mucse_ring *rx_ring);
int rnpgbe_setup_all_rx_resources(struct mucse *mucse);
void rnpgbe_free_all_rx_resources(struct mucse *mucse);
+void rnpgbe_service_task(struct work_struct *work);
#endif
diff --git a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_main.c b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_main.c
index 413eefae65dd..6b009c1be270 100644
--- a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_main.c
+++ b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_main.c
@@ -52,6 +52,7 @@ static int rnpgbe_open(struct net_device *netdev)
struct mucse *mucse = netdev_priv(netdev);
int err;
+ netif_carrier_off(netdev);
err = rnpgbe_request_irq(mucse);
if (err)
return err;
@@ -147,6 +148,7 @@ static void rnpgbe_sw_init(struct mucse *mucse)
static int rnpgbe_add_adapter(struct pci_dev *pdev,
int board_type)
{
+ struct device *dev = &pdev->dev;
struct net_device *netdev;
u8 perm_addr[ETH_ALEN];
void __iomem *hw_addr;
@@ -181,6 +183,16 @@ static int rnpgbe_add_adapter(struct pci_dev *pdev,
dev_err(&pdev->dev, "Init hw err %d\n", err);
goto err_free_net;
}
+
+ mucse->serv_wq = alloc_workqueue("%s-%s-service",
+ WQ_UNBOUND | WQ_MEM_RECLAIM, 0,
+ dev_driver_string(dev),
+ dev_name(dev));
+ if (!mucse->serv_wq) {
+ dev_err(dev, "Failed to allocate service workqueue\n");
+ err = -ENOMEM;
+ goto err_free_net;
+ }
/* Step 1: Send power-up notification to firmware (no response expected)
* This informs firmware to initialize hardware power state, but
* firmware only acknowledges receipt without returning data. Must be
@@ -223,6 +235,9 @@ static int rnpgbe_add_adapter(struct pci_dev *pdev,
goto err_powerdown;
}
+ INIT_DELAYED_WORK(&mucse->serv_task, rnpgbe_service_task);
+ spin_lock_init(&mucse->link_lock);
+
err = rnpgbe_init_interrupt_scheme(mucse);
if (err) {
dev_err(&pdev->dev, "init interrupt failed %d\n", err);
@@ -245,6 +260,7 @@ static int rnpgbe_add_adapter(struct pci_dev *pdev,
err_clear_interrupt:
rnpgbe_clear_interrupt_scheme(mucse);
err_powerdown:
+ destroy_workqueue(mucse->serv_wq);
/* notify powerdown only powerup ok */
if (!err_notify) {
err_notify = rnpgbe_send_notify(hw, false, mucse_fw_powerup);
@@ -324,6 +340,7 @@ static void rnpgbe_rm_adapter(struct pci_dev *pdev)
if (!mucse)
return;
+ cancel_delayed_work_sync(&mucse->serv_task);
netdev = mucse->netdev;
unregister_netdev(netdev);
err = rnpgbe_send_notify(hw, false, mucse_fw_powerup);
@@ -331,6 +348,7 @@ static void rnpgbe_rm_adapter(struct pci_dev *pdev)
dev_warn(&pdev->dev, "Send powerdown to hw failed %d\n", err);
remove_mbx_irq(mucse);
rnpgbe_clear_interrupt_scheme(mucse);
+ destroy_workqueue(mucse->serv_wq);
free_netdev(netdev);
}
diff --git a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx.c b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx.c
index de5e29230b3c..1d4e2ae78154 100644
--- a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx.c
+++ b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx.c
@@ -247,6 +247,26 @@ int mucse_poll_and_read_mbx(struct mucse_hw *hw, u32 *msg, u16 size)
return mucse_read_mbx_pf(hw, msg, size);
}
+/**
+ * mucse_check_and_read_mbx - check if there is notification and receive message
+ * @hw: pointer to the HW structure
+ * @msg: the message buffer
+ * @size: length of buffer
+ *
+ * Return: 0 if it successfully received a message notification and
+ * copied it into the receive buffer, negative errno on failure
+ **/
+int mucse_check_and_read_mbx(struct mucse_hw *hw, u32 *msg, u16 size)
+{
+ int err;
+
+ err = mucse_check_for_msg_pf(hw);
+ if (err)
+ return err;
+
+ return mucse_read_mbx_pf(hw, msg, size);
+}
+
/**
* mucse_mbx_get_fwack - Read fw ack from reg
* @mbx: pointer to the MBX structure
diff --git a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx.h b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx.h
index e6fcc8d1d3ca..cba54a07a7fa 100644
--- a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx.h
+++ b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx.h
@@ -17,4 +17,5 @@
int mucse_write_and_wait_ack_mbx(struct mucse_hw *hw, u32 *msg, u16 size);
void mucse_init_mbx_params_pf(struct mucse_hw *hw);
int mucse_poll_and_read_mbx(struct mucse_hw *hw, u32 *msg, u16 size);
+int mucse_check_and_read_mbx(struct mucse_hw *hw, u32 *msg, u16 size);
#endif /* _RNPGBE_MBX_H */
diff --git a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx_fw.c b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx_fw.c
index 8c8bd5e8e1db..09e2505ab8cd 100644
--- a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx_fw.c
+++ b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx_fw.c
@@ -3,6 +3,7 @@
#include <linux/if_ether.h>
#include <linux/bitfield.h>
+#include <linux/ethtool.h>
#include "rnpgbe.h"
#include "rnpgbe_mbx.h"
@@ -189,3 +190,167 @@ int mucse_mbx_get_macaddr(struct mucse_hw *hw, int pfvfnum,
return 0;
}
+
+/**
+ * mucse_mbx_phyup - Echo fw let the phy up
+ * @hw: pointer to the HW structure
+ * @is_phyup: true for up, false for down
+ *
+ * mucse_mbx_phyup echo fw to change phy status
+ *
+ * Return: 0 on success, negative errno on failure
+ **/
+int mucse_mbx_phyup(struct mucse_hw *hw, bool is_phyup)
+{
+ struct mbx_fw_cmd_req req = {
+ .datalen = cpu_to_le16(sizeof(req.phy_status) +
+ MUCSE_MBX_REQ_HDR_LEN),
+ .opcode = cpu_to_le16(SET_PHY_UP),
+ .phy_status = {
+ .port = cpu_to_le32(hw->port),
+ .status = cpu_to_le32(is_phyup ? 1 : 0),
+ },
+ };
+ int len, err;
+
+ len = le16_to_cpu(req.datalen);
+ mutex_lock(&hw->mbx.lock);
+ err = mucse_write_and_wait_ack_mbx(hw, (u32 *)&req, len);
+ mutex_unlock(&hw->mbx.lock);
+
+ return err;
+}
+
+/**
+ * mucse_mbx_link_report - Echo fw report link change event or not
+ * @hw: pointer to the HW structure
+ * @is_eventup: true for report, false for no
+ *
+ * mucse_mbx_link_eventup echo fw to change event report state
+ *
+ * Return: 0 on success, negative errno on failure
+ **/
+int mucse_mbx_link_report(struct mucse_hw *hw, bool is_report)
+{
+ struct mbx_fw_cmd_req req = {
+ .datalen = cpu_to_le16(sizeof(req. report_status) +
+ MUCSE_MBX_REQ_HDR_LEN),
+ .opcode = cpu_to_le16(LINK_REPORT_EN),
+ .report_status = {
+ .port_mask = cpu_to_le16(hw->port),
+ .status = cpu_to_le16(is_report ? 1 : 0),
+ },
+ };
+ int len, err;
+
+ len = le16_to_cpu(req.datalen);
+ mutex_lock(&hw->mbx.lock);
+ err = mucse_write_and_wait_ack_mbx(hw, (u32 *)&req, len);
+ mutex_unlock(&hw->mbx.lock);
+
+ return err;
+}
+
+/**
+ * mucse_update_link_status_reg - update driver speed inf to reg
+ * @hw: pointer to the HW structure
+ * @req: pointer to req data
+ *
+ * mucse_update_link_status_reg update reg according to driver info,
+ * fw will send irq if status is differ with reg
+ *
+ **/
+static void mucse_update_link_status_reg(struct mucse_hw *hw,
+ struct mbx_fw_cmd_req *req)
+{
+ u32 value;
+
+ value = mucse_hw_rd32(hw, RNPGBE_LINK_ST);
+ value &= ~M_ST_MASK;
+ value |= M_DEFAULT_ST;
+
+ if (le16_to_cpu(req->link_stat.port_status)) {
+ value |= BIT(0);
+ switch (hw->speed) {
+ case 10:
+ value |= (mucse_speed_10 << 8);
+ break;
+ case 100:
+ value |= (mucse_speed_100 << 8);
+ break;
+ case 1000:
+ value |= (mucse_speed_1000 << 8);
+ break;
+ default:
+ /* invalid speed do nothing */
+ break;
+ }
+
+ value |= (hw->duplex << 4);
+ value |= (req->link_stat.st[0].s_host.pause << 24);
+ } else {
+ value &= ~BIT(0);
+ }
+
+ if (req->link_stat.st[0].s_host.lldp_status)
+ value |= BIT(6);
+ else
+ value &= ~BIT(6);
+
+ mucse_hw_wr32(hw, RNPGBE_LINK_ST, value);
+}
+
+/**
+ * mucse_mbx_fw_req_handler - Handle fw req
+ * @mucse: pointer to the device private structure
+ * @req: pointer to req data
+ *
+ * rnpgbe_mbx_fw_req_handler handler fw req, such as a link event req.
+ *
+ * @return: 0 on success, negative on failure
+ **/
+static void mucse_mbx_fw_req_handler(struct mucse_hw *hw,
+ struct mbx_fw_cmd_req *req)
+{
+ struct mucse *mucse = container_of(hw, struct mucse, hw);
+ u32 magic = le32_to_cpu(req->link_stat.port_magic);
+ unsigned long flags;
+
+ if (le16_to_cpu(req->opcode) == LINK_CHANGE_EVT) {
+ spin_lock_irqsave(&mucse->link_lock, flags);
+
+ if (le16_to_cpu(req->link_stat.port_status))
+ hw->link = true;
+ else
+ hw->link = false;
+
+ if (magic == ST_VALID_MAGIC) {
+ hw->speed = le16_to_cpu(req->link_stat.st[0].speed);
+ hw->duplex = req->link_stat.st[0].duplex;
+ } else {
+ hw->speed = 0;
+ hw->duplex = 0;
+ }
+ /* update regs to notify link info is received from fw */
+ mucse_update_link_status_reg(hw, req);
+ mucse->flags |= M_FLAG_NEED_LINK_UPDATE;
+ spin_unlock_irqrestore(&mucse->link_lock, flags);
+ }
+}
+
+/**
+ * mucse_fw_irq_handler - Try to handle a req from hw
+ * @hw: pointer to the HW structure
+ **/
+void mucse_fw_irq_handler(struct mucse_hw *hw)
+{
+ struct mbx_fw_cmd_req req = {};
+
+ /* try to check and read fw req */
+ if (mucse_check_and_read_mbx(hw, (u32 *)&req, sizeof(req)))
+ return;
+
+ /* handle it if is a req from fw */
+ if (!(le16_to_cpu(req.flags) & FLAGS_REPLY))
+ mucse_mbx_fw_req_handler(hw, &req);
+}
diff --git a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx_fw.h b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx_fw.h
index fb24fc12b613..b0f6ae8f90d9 100644
--- a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx_fw.h
+++ b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx_fw.h
@@ -14,6 +14,9 @@ enum MUCSE_FW_CMD {
GET_HW_INFO = 0x0601,
GET_MAC_ADDRESS = 0x0602,
RESET_HW = 0x0603,
+ LINK_CHANGE_EVT = 0x0608,
+ LINK_REPORT_EN = 0x0613,
+ SET_PHY_UP = 0x0800,
POWER_UP = 0x0803,
};
@@ -36,6 +39,25 @@ struct mucse_hw_info {
__le32 ext_info;
} __packed;
+struct st_status {
+ u8 phyid;
+ u8 duplex : 1;
+ u8 autoneg : 1;
+ u8 fec : 1;
+ __le16 speed;
+ union {
+ __le16 status;
+ struct {
+ u16 pause : 4;
+ u16 local_eee : 3;
+ u16 partner_eee : 3;
+ u16 tp_mdx : 2;
+ u16 lldp_status : 1;
+ u16 revs : 3;
+ } s_host;
+ };
+} __packed;
+
struct mbx_fw_cmd_req {
__le16 flags;
__le16 opcode;
@@ -55,10 +77,26 @@ struct mbx_fw_cmd_req {
__le32 port_mask;
__le32 pfvf_num;
} get_mac_addr;
+ struct {
+ __le32 port;
+ __le32 status;
+ } phy_status;
+ struct {
+ __le16 status;
+ __le16 port_mask;
+ } report_status;
+ struct {
+ __le16 changed_lanes;
+ __le16 port_status;
+ __le32 port_magic;
+#define ST_VALID_MAGIC 0xa4a6a8a9
+ struct st_status st[4];
+ } link_stat;
};
} __packed;
struct mbx_fw_cmd_reply {
+#define FLAGS_REPLY BIT(0)
__le16 flags;
__le16 opcode;
__le16 error_code;
@@ -80,9 +118,18 @@ struct mbx_fw_cmd_reply {
};
} __packed;
+enum mucse_speed {
+ mucse_speed_10,
+ mucse_speed_100,
+ mucse_speed_1000,
+};
+
int mucse_mbx_sync_fw(struct mucse_hw *hw);
int mucse_mbx_powerup(struct mucse_hw *hw, bool is_powerup);
int mucse_mbx_reset_hw(struct mucse_hw *hw);
int mucse_mbx_get_macaddr(struct mucse_hw *hw, int pfvfnum,
u8 *mac_addr, int port);
+int mucse_mbx_phyup(struct mucse_hw *hw, bool is_phyup);
+int mucse_mbx_link_report(struct mucse_hw *hw, bool is_report);
+void mucse_fw_irq_handler(struct mucse_hw *hw);
#endif /* _RNPGBE_MBX_FW_H */
--
2.25.1
next prev parent reply other threads:[~2026-03-25 9:13 UTC|newest]
Thread overview: 6+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-03-25 9:12 [PATCH net-next 0/4] net: rnpgbe: Add TX/RX and link status support Dong Yibo
2026-03-25 9:12 ` [PATCH net-next 1/4] net: rnpgbe: Add interrupt handling Dong Yibo
2026-03-25 9:12 ` [PATCH net-next 2/4] net: rnpgbe: Add basic TX packet transmission support Dong Yibo
2026-03-25 9:12 ` [PATCH net-next 3/4] net: rnpgbe: Add RX packet reception support Dong Yibo
2026-03-25 9:12 ` Dong Yibo [this message]
2026-03-26 4:46 ` [PATCH net-next 4/4] net: rnpgbe: Add link status handling support kernel test robot
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260325091204.94015-5-dong100@mucse.com \
--to=dong100@mucse.com \
--cc=andrew+netdev@lunn.ch \
--cc=danishanwar@ti.com \
--cc=davem@davemloft.net \
--cc=edumazet@google.com \
--cc=kuba@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=netdev@vger.kernel.org \
--cc=pabeni@redhat.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox