[PATCH net-next 4/4] net: rnpgbe: Add link status handling support
STALE192d
From: Dong Yibo <dong100@mucse.com>
Date: 2026-03-25 09:13:27
Also in:
lkml
Subsystem:
mucse ethernet driver, networking drivers, the rest · Maintainers:
Yibo Dong, Andrew Lunn, "David S. Miller", Eric Dumazet, Jakub Kicinski, Paolo Abeni, Linus Torvalds
Revision v1 of 12 in this series.
Revisions (12)
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