AI Trajectory


Trajectory: (Expand/Collapse All)
Seq Timestamp Type Name Duration
0/0 2026/08/14 13:02 flow patch-triage
5m Args:
null

Results:
{
  "EnableConfigs": null,
  "FocusSymbols": [],
  "KMSANReasoning": "",
  "NeedsKMSAN": false,
  "Reasoning": "The patch introduces new functionality to the `mucse` ethernet driver, which is for Mucse 1GbE PCI Express adapters. This is a physical PCIe hardware card that is not emulated in standard QEMU or other virtualized environments. Therefore, the code paths are impossible to reach and test during fuzzing.",
  "WorthFuzzing": false
}

1/1 2026/08/14 13:02 action read-patch-diff
0m Args:
null

Results:
{
  "PatchDiff": "commit 10120395cd8c6048c89ffe53826db77153924c53\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate:   Fri Aug 14 13:02:47 2026 +0000\n\n    syz-cluster: applied patch under review\n\ndiff --git a/drivers/net/ethernet/mucse/Kconfig b/drivers/net/ethernet/mucse/Kconfig\nindex 0b3e853d625f1..be0fdf268484c 100644\n--- a/drivers/net/ethernet/mucse/Kconfig\n+++ b/drivers/net/ethernet/mucse/Kconfig\n@@ -19,6 +19,7 @@ if NET_VENDOR_MUCSE\n config MGBE\n \ttristate \"Mucse(R) 1GbE PCI Express adapters support\"\n \tdepends on PCI\n+\tselect PAGE_POOL\n \thelp\n \t  This driver supports Mucse(R) 1GbE PCI Express family of\n \t  adapters.\ndiff --git a/drivers/net/ethernet/mucse/rnpgbe/Makefile b/drivers/net/ethernet/mucse/rnpgbe/Makefile\nindex de8bcb7772ab4..17574cad392a2 100644\n--- a/drivers/net/ethernet/mucse/rnpgbe/Makefile\n+++ b/drivers/net/ethernet/mucse/rnpgbe/Makefile\n@@ -8,4 +8,5 @@ obj-$(CONFIG_MGBE) += rnpgbe.o\n rnpgbe-objs := rnpgbe_main.o\\\n \t       rnpgbe_chip.o\\\n \t       rnpgbe_mbx.o\\\n-\t       rnpgbe_mbx_fw.o\n+\t       rnpgbe_mbx_fw.o\\\n+\t       rnpgbe_lib.o\ndiff --git a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe.h b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe.h\nindex 5b024f9f7e17a..482c13d987b1c 100644\n--- a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe.h\n+++ b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe.h\n@@ -5,7 +5,14 @@\n #define _RNPGBE_H\n \n #include \u003clinux/types.h\u003e\n+#include \u003clinux/atomic.h\u003e\n #include \u003clinux/mutex.h\u003e\n+#include \u003clinux/netdevice.h\u003e\n+#include \u003clinux/timer.h\u003e\n+#include \u003clinux/if.h\u003e\n+#include \u003clinux/workqueue.h\u003e\n+\n+#include \"rnpgbe_hw.h\"\n \n enum rnpgbe_boards {\n \tboard_n500,\n@@ -26,30 +33,200 @@ struct mucse_mbx_info {\n \tu32 fwpf_ctrl_base;\n };\n \n-/* Enum for firmware notification modes,\n- * more modes (e.g., portup, link_report) will be added in future\n- **/\n enum {\n \tmucse_fw_powerup,\n+\tmucse_fw_portup,\n+\tmucse_fw_link_report_en,\n };\n \n struct mucse_hw {\n \tvoid __iomem *hw_addr;\n+\tvoid __iomem *ring_msix_base;\n \tstruct pci_dev *pdev;\n \tstruct mucse_mbx_info mbx;\n \tint port;\n+\tint speed;\n+\tbool link;\n+\tu16 cycles_per_us;\n+\tu8 pfvfnum;\n+\tu8 duplex;\n+};\n+\n+struct rnpgbe_tx_desc {\n+\t__le64 pkt_addr; /* Packet buffer address */\n+\tunion {\n+\t\t__le64 vlan_cmd_bsz;\n+\t\tstruct {\n+\t\t\t__le32 blen_mac_ip_len;\n+\t\t\t__le32 vlan_cmd; /* vlan \u0026 cmd status */\n+\t\t};\n+\t};\n+#define M_TXD_CMD_RS          0x040000 /* Report Status */\n+#define M_TXD_STAT_DD         0x020000 /* Descriptor Done */\n+#define M_TXD_CMD_EOP         0x010000 /* End of Packet */\n+};\n+\n+union rnpgbe_rx_desc {\n+\tstruct {\n+\t\t__le64 pkt_addr; /* Packet buffer address */\n+\t\t__le64 resv_cmd; /* cmd status */\n+\t};\n+\tstruct {\n+\t\t__le32 rss_hash; /* RSS HASH */\n+\t\t__le16 mark; /* mark info */\n+\t\t__le16 rev1;\n+\t\t__le16 len; /* Packet length */\n+\t\t__le16 padding_len;\n+\t\t__le16 vlan; /* VLAN tag */\n+\t\t__le16 cmd; /* cmd status */\n+#define M_RXD_STAT_DD         BIT(1) /* Descriptor Done */\n+#define M_RXD_STAT_EOP        BIT(0) /* End of Packet */\n+\t} wb;\n+};\n+\n+#define M_TX_DESC(R, i) (\u0026(((struct rnpgbe_tx_desc *)((R)-\u003edesc))[i]))\n+#define M_RX_DESC(R, i) (\u0026(((union rnpgbe_rx_desc *)((R)-\u003edesc))[i]))\n+\n+static inline __le16 rnpgbe_test_staterr(union rnpgbe_rx_desc *rx_desc,\n+\t\t\t\t\t const u16 stat_err_bits)\n+{\n+\treturn rx_desc-\u003ewb.cmd \u0026 cpu_to_le16(stat_err_bits);\n+}\n+\n+struct mucse_tx_buffer {\n+\tstruct rnpgbe_tx_desc *next_to_watch;\n+\tstruct sk_buff *skb;\n+\tunsigned int bytecount;\n+\tunsigned short gso_segs;\n+\tDEFINE_DMA_UNMAP_ADDR(dma);\n+\tDEFINE_DMA_UNMAP_LEN(len);\n+\tbool mapped_as_page;  /* true if dma was mapped with dma_map_page */\n+};\n+\n+struct mucse_queue_stats {\n+\tu64 packets;\n+\tu64 bytes;\n+\tatomic64_t dropped;\n+};\n+\n+struct mucse_rx_buffer {\n+\tstruct sk_buff *skb;\n+\tdma_addr_t dma;\n+\tstruct page *page;\n+\tu32 page_offset;\n+};\n+\n+struct mucse_ring {\n+\tstruct mucse_ring *next;\n+\tstruct mucse_q_vector *q_vector;\n+\tstruct net_device *netdev;\n+\tstruct device *dev;\n+\tstruct page_pool *page_pool;\n+\tvoid *desc;\n+\tunion {\n+\t\tstruct mucse_tx_buffer *tx_buffer_info;\n+\t\tstruct mucse_rx_buffer *rx_buffer_info;\n+\t};\n+\tvoid __iomem *ring_addr;\n+\tvoid __iomem *tail;\n+\tvoid __iomem *irq_mask;\n+\tvoid __iomem *trig;\n+\tu8 queue_index;\n+\t/* hw ring idx */\n+\tu8 rnpgbe_queue_idx;\n \tu8 pfvfnum;\n+\tu16 count;\n+\tu16 next_to_use;\n+\tu16 next_to_clean;\n+\tdma_addr_t dma;\n+\tunsigned int size;\n+\tstruct mucse_queue_stats stats;\n+\tstruct u64_stats_sync syncp;\n+\tbool drop_status;\n+} ____cacheline_internodealigned_in_smp;\n+\n+static inline u16 mucse_desc_unused(struct mucse_ring *ring)\n+{\n+\tu16 ntc = ring-\u003enext_to_clean;\n+\tu16 ntu = ring-\u003enext_to_use;\n+\n+\treturn ((ntc \u003e ntu) ? 0 : ring-\u003ecount) + ntc - ntu - 1;\n+}\n+\n+static inline u16 mucse_desc_unused_rx(struct mucse_ring *ring)\n+{\n+\tu16 ntc = ring-\u003enext_to_clean;\n+\tu16 ntu = ring-\u003enext_to_use;\n+\n+\t/* Keep M_RX_BUFFER_WRITE descriptors unused so the ring is not filled\n+\t * completely. Refill is attempted once at least this many descriptors\n+\t * are available.\n+\t */\n+\treturn ((ntc \u003e ntu) ? 0 : ring-\u003ecount) + ntc - ntu - 16;\n+}\n+\n+static inline __le64 build_ctob(u32 vlan_cmd, u32 mac_ip_len, u32 size)\n+{\n+\treturn cpu_to_le64(((u64)vlan_cmd \u003c\u003c 32) | ((u64)mac_ip_len \u003c\u003c 16) |\n+\t\t\t   ((u64)size));\n+}\n+\n+static inline struct netdev_queue *txring_txq(const struct mucse_ring *ring)\n+{\n+\treturn netdev_get_tx_queue(ring-\u003enetdev, ring-\u003equeue_index);\n+}\n+\n+struct mucse_ring_container {\n+\tstruct mucse_ring *ring;\n+\tu16 count;\n };\n \n-struct mucse_stats {\n-\tu64 tx_dropped;\n+struct mucse_q_vector {\n+\tstruct mucse *mucse;\n+\t/* hardware interrupt vector number */\n+\tint hw_vector;\n+\tstruct mucse_ring_container rx, tx;\n+\tstruct napi_struct napi;\n+\tstruct timer_list rx_alloc_timer;\n+\tchar name[IFNAMSIZ + 18];\n+\t/* for dynamic allocation of rings associated with this q_vector */\n+\tstruct mucse_ring ring[] ____cacheline_internodealigned_in_smp;\n+};\n+\n+#define MAX_Q_VECTORS 8\n+\n+#define M_DEFAULT_TXD     512\n+#define M_DEFAULT_RXD     512\n+#define M_DEFAULT_TX_WORK 256\n+\n+enum mucse_state_t {\n+\t__MUCSE_DOWN,\n };\n \n struct mucse {\n \tstruct net_device *netdev;\n \tstruct pci_dev *pdev;\n \tstruct mucse_hw hw;\n-\tstruct mucse_stats stats;\n+#define M_FLAG_MSIX_SINGLE_EN      BIT(0)\n+#define M_FLAG_MSIX_EN             BIT(1)\n+\tu32 flags;\n+\tstruct mucse_ring *tx_ring[RNPGBE_MAX_QUEUES]\n+\t\t____cacheline_aligned_in_smp;\n+\tstruct mucse_ring *rx_ring[RNPGBE_MAX_QUEUES]\n+\t\t____cacheline_aligned_in_smp;\n+\tstruct mucse_q_vector *q_vector[MAX_Q_VECTORS];\n+\tint tx_ring_item_count;\n+\tint tx_work_limit;\n+\tint num_tx_queues;\n+\tint num_q_vectors;\n+\tint rx_ring_item_count;\n+\tint num_rx_queues;\n+\tchar mbx_name[32];\n+\tunsigned long state;\n+\tatomic_t link_pending;\n+\tstruct work_struct mbx_work;\n+\tstruct delayed_work serv_task;\n+\tspinlock_t link_lock; /* spinlock for link update */\n };\n \n int rnpgbe_get_permanent_mac(struct mucse_hw *hw, u8 *perm_addr);\n@@ -58,6 +235,7 @@ int rnpgbe_send_notify(struct mucse_hw *hw,\n \t\t       bool enable,\n \t\t       int mode);\n int rnpgbe_init_hw(struct mucse_hw *hw, int board_type);\n+void rnpgbe_set_link(struct mucse_hw *hw, bool linkup);\n \n /* Device IDs */\n #define PCI_VENDOR_ID_MUCSE               0x8848\n@@ -68,4 +246,10 @@ int rnpgbe_init_hw(struct mucse_hw *hw, int board_type);\n \n #define mucse_hw_wr32(hw, reg, val) \\\n \twritel((val), (hw)-\u003ehw_addr + (reg))\n+#define mucse_hw_rd32(hw, reg) \\\n+\treadl((hw)-\u003ehw_addr + (reg))\n+#define mucse_ring_wr32(ring, reg, val) \\\n+\twritel((val), (ring)-\u003ering_addr + (reg))\n+#define mucse_ring_rd32(ring, reg) \\\n+\treadl((ring)-\u003ering_addr + (reg))\n #endif /* _RNPGBE_H */\ndiff --git a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_chip.c b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_chip.c\nindex ebc7b3750157b..55b4abed82bd7 100644\n--- a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_chip.c\n+++ b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_chip.c\n@@ -66,11 +66,17 @@ int rnpgbe_send_notify(struct mucse_hw *hw,\n \t\t       int mode)\n {\n \tint err;\n-\t/* Keep switch struct to support more modes in the future */\n+\n \tswitch (mode) {\n \tcase mucse_fw_powerup:\n \t\terr = mucse_mbx_powerup(hw, enable);\n \t\tbreak;\n+\tcase mucse_fw_portup:\n+\t\terr = mucse_mbx_phyup(hw, enable);\n+\t\tbreak;\n+\tcase mucse_fw_link_report_en:\n+\t\terr = mucse_mbx_link_report(hw, enable);\n+\t\tbreak;\n \tdefault:\n \t\terr = -EINVAL;\n \t}\n@@ -89,8 +95,12 @@ static void rnpgbe_init_n500(struct mucse_hw *hw)\n {\n \tstruct mucse_mbx_info *mbx = \u0026hw-\u003embx;\n \n+\thw-\u003ering_msix_base = hw-\u003ehw_addr + MUCSE_N500_RING_MSIX_BASE;\n+\n \tmbx-\u003efwpf_ctrl_base = MUCSE_N500_FWPF_CTRL_BASE;\n \tmbx-\u003efwpf_shm_base = MUCSE_N500_FWPF_SHM_BASE;\n+\n+\thw-\u003ecycles_per_us = M_DEFAULT_N500_MHZ;\n }\n \n /**\n@@ -104,8 +114,12 @@ static void rnpgbe_init_n210(struct mucse_hw *hw)\n {\n \tstruct mucse_mbx_info *mbx = \u0026hw-\u003embx;\n \n+\thw-\u003ering_msix_base = hw-\u003ehw_addr + MUCSE_N210_RING_MSIX_BASE;\n+\n \tmbx-\u003efwpf_ctrl_base = MUCSE_N210_FWPF_CTRL_BASE;\n \tmbx-\u003efwpf_shm_base = MUCSE_N210_FWPF_SHM_BASE;\n+\n+\thw-\u003ecycles_per_us = M_DEFAULT_N210_MHZ;\n }\n \n /**\n@@ -141,3 +155,32 @@ int rnpgbe_init_hw(struct mucse_hw *hw, int board_type)\n \n \treturn 0;\n }\n+\n+/**\n+ * rnpgbe_set_link - Set the hardware link state\n+ * @hw: hw information structure\n+ * @linkup: link on or not\n+ *\n+ * rnpgbe_set_link setup link status\n+ *\n+ **/\n+void rnpgbe_set_link(struct mucse_hw *hw, bool linkup)\n+{\n+\tu32 value = mucse_hw_rd32(hw, GMAC_CONTROL);\n+\n+\tif (linkup)\n+\t\tvalue |= GMAC_CONTROL_RE;\n+\telse\n+\t\tvalue \u0026= ~GMAC_CONTROL_RE;\n+\n+\tmucse_hw_wr32(hw, GMAC_CONTROL, value);\n+\n+\t/* Keep the GMAC in receive-all mode while the link is up. The\n+\t * chip-level filter does the actual address filtering, but there\n+\t * is no ndo_set_rx_mode yet to configure it.\n+\t */\n+\tif (linkup)\n+\t\tmucse_hw_wr32(hw, GMAC_FRAME_FILTER, GMAC_RX_ALL);\n+\telse\n+\t\tmucse_hw_wr32(hw, GMAC_FRAME_FILTER, 0);\n+}\ndiff --git a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_hw.h b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_hw.h\nindex e77e6bc3d3e30..9e640e8d67036 100644\n--- a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_hw.h\n+++ b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_hw.h\n@@ -6,12 +6,42 @@\n \n #define MUCSE_N500_FWPF_CTRL_BASE      0x28b00\n #define MUCSE_N500_FWPF_SHM_BASE       0x2d000\n+#define MUCSE_N500_RING_MSIX_BASE      0x28700\n+#define M_DEFAULT_N500_MHZ             125\n #define MUCSE_GBE_PFFW_MBX_CTRL_OFFSET 0x5500\n #define MUCSE_GBE_FWPF_MBX_MASK_OFFSET 0x5700\n #define MUCSE_N210_FWPF_CTRL_BASE      0x29400\n #define MUCSE_N210_FWPF_SHM_BASE       0x2d900\n+#define MUCSE_N210_RING_MSIX_BASE      0x29000\n+#define M_DEFAULT_N210_MHZ             62\n \n+#define RNPGBE_DMA_STATUS              0x0008\n+#define TX_AXI_RW_EN                   0xc\n+/* DMA_STATUS_REG[23:20]: tx_wr, tx_rd, rx_wr, rx_rd done status. */\n+#define RNPGBE_DMA_TX_STATUS           GENMASK_U32(23, 22)\n+#define RNPGBE_DMA_RX_STATUS           GENMASK_U32(21, 20)\n+#define RX_AXI_RW_EN                   0x03\n+/* RNPGBE_LINK_ST is a driver-owned link-state snapshot consumed by firmware.\n+ * M_DEFAULT_ST replaces the complete snapshot, causing firmware\n+ * to report the current link state again when it differs from this default.\n+ */\n+#define M_ST_MASK                      (GENMASK_U32(31, 24) | \\\n+\t\t\t\t\tGENMASK_U32(11, 8) | BIT(6) | \\\n+\t\t\t\t\tBIT(4) | BIT(0))\n+/* Set the driver-state marker; all other driver status fields start clear. */\n+#define M_DEFAULT_ST                   0xa0000000\n+/* Driver-reported fields: 25:24 pause, 11:8 speed, 6 LLDP, 4 duplex,\n+ * and 0 link up/down. M_DEFAULT_ST resets these fields so firmware reports\n+ * the current hardware state again.\n+ */\n+#define RNPGBE_LINK_ST                 0x000c\n #define RNPGBE_DMA_AXI_EN              0x0010\n+#define RNPGBE_TX_MIN_PKT_LEN          33\n \n+#define MUCSE_GMAC_OFF(_n)             (0x20000 + (_n))\n+#define GMAC_CONTROL_RE                0x00000004\n+#define GMAC_CONTROL                   MUCSE_GMAC_OFF(0)\n+#define GMAC_RX_ALL                    (BIT(31) | BIT(0))\n+#define GMAC_FRAME_FILTER              MUCSE_GMAC_OFF(0x4)\n #define RNPGBE_MAX_QUEUES 8\n #endif /* _RNPGBE_HW_H */\ndiff --git a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c\nnew file mode 100644\nindex 0000000000000..fa5ea4dd7a1c6\n--- /dev/null\n+++ b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c\n@@ -0,0 +1,2253 @@\n+// SPDX-License-Identifier: GPL-2.0\n+/* Copyright(c) 2020 - 2025 Mucse Corporation. */\n+\n+#include \u003clinux/pci.h\u003e\n+#include \u003clinux/netdevice.h\u003e\n+#include \u003clinux/iopoll.h\u003e\n+#include \u003clinux/etherdevice.h\u003e\n+#include \u003clinux/vmalloc.h\u003e\n+#include \u003cnet/netdev_queues.h\u003e\n+#include \u003cnet/page_pool/helpers.h\u003e\n+\n+#include \"rnpgbe_lib.h\"\n+#include \"rnpgbe.h\"\n+#include \"rnpgbe_mbx_fw.h\"\n+\n+static void rnpgbe_mbx_work(struct work_struct *work)\n+{\n+\tstruct mucse *mucse = container_of(work, struct mucse, mbx_work);\n+\n+\tmucse_fw_irq_handler(\u0026mucse-\u003ehw);\n+}\n+\n+/**\n+ * rnpgbe_msix_other - Other irq handler\n+ * @irq: interrupt number\n+ * @data: private data\n+ *\n+ * Return: IRQ_HANDLED\n+ **/\n+static irqreturn_t rnpgbe_msix_other(int irq, void *data)\n+{\n+\tstruct mucse *mucse = (struct mucse *)data;\n+\n+\tqueue_work(system_percpu_wq, \u0026mucse-\u003embx_work);\n+\n+\treturn IRQ_HANDLED;\n+}\n+\n+static void rnpgbe_irq_disable_queues(struct mucse_q_vector *q_vector)\n+{\n+\tstruct mucse_ring *ring;\n+\n+\t/*\n+\t * TX/RX pairs share interrupt control registers; update them through\n+\t * the TX ring list.\n+\t */\n+\tmucse_for_each_ring(ring, q_vector-\u003etx) {\n+\t\twritel(INT_VALID, ring-\u003etrig);\n+\t\twritel((RX_INT_MASK | TX_INT_MASK), ring-\u003eirq_mask);\n+\t}\n+\t/* flush posted writes to ensure hardware sees the mask */\n+\tif (q_vector-\u003etx.ring)\n+\t\treadl(q_vector-\u003etx.ring-\u003eirq_mask);\n+}\n+\n+/**\n+ * rnpgbe_int_single - MSI-X single-vector/MSI interrupt handler\n+ * @irq: interrupt number\n+ * @data: private data\n+ *\n+ * Return: IRQ_HANDLED\n+ **/\n+static irqreturn_t rnpgbe_int_single(int irq, void *data)\n+{\n+\tstruct mucse *mucse = (struct mucse *)data;\n+\tstruct mucse_q_vector *q_vector;\n+\n+\tqueue_work(system_percpu_wq, \u0026mucse-\u003embx_work);\n+\n+\tif (test_bit(__MUCSE_DOWN, \u0026mucse-\u003estate))\n+\t\treturn IRQ_HANDLED;\n+\n+\tq_vector = mucse-\u003eq_vector[0];\n+\trnpgbe_irq_disable_queues(q_vector);\n+\tnapi_schedule_irqoff(\u0026q_vector-\u003enapi);\n+\n+\treturn IRQ_HANDLED;\n+}\n+\n+static void rnpgbe_irq_enable_queues(struct mucse_q_vector *q_vector)\n+{\n+\tstruct mucse_ring *ring;\n+\n+\t/*\n+\t * TX/RX pairs share interrupt control registers; update them through\n+\t * the TX ring list.\n+\t */\n+\tmucse_for_each_ring(ring, q_vector-\u003etx) {\n+\t\twritel(0, ring-\u003eirq_mask);\n+\n+\t\t/* Re-trigger hw to re-check events lost while masked. */\n+\t\twritel(INT_VALID | TX_INT_MASK | RX_INT_MASK, ring-\u003etrig);\n+\t}\n+}\n+\n+/**\n+ * rnpgbe_clean_tx_irq - Reclaim resources after transmit completes\n+ * @q_vector: structure containing interrupt and ring information\n+ * @tx_ring: tx ring to clean\n+ * @napi_budget: Used to determine if we are in netpoll\n+ *\n+ * Return: true if cleaning completed within the budget, otherwise false\n+ **/\n+static bool rnpgbe_clean_tx_irq(struct mucse_q_vector *q_vector,\n+\t\t\t\tstruct mucse_ring *tx_ring,\n+\t\t\t\tint napi_budget)\n+{\n+\tint budget = q_vector-\u003emucse-\u003etx_work_limit;\n+\tu64 total_bytes = 0, total_packets = 0;\n+\tstruct mucse *mucse = q_vector-\u003emucse;\n+\tstruct mucse_tx_buffer *tx_buffer;\n+\tstruct rnpgbe_tx_desc *tx_desc;\n+\tint i = tx_ring-\u003enext_to_clean;\n+\n+\ttx_buffer = \u0026tx_ring-\u003etx_buffer_info[i];\n+\ttx_desc = M_TX_DESC(tx_ring, i);\n+\ti -= tx_ring-\u003ecount;\n+\n+\tdo {\n+\t\tstruct rnpgbe_tx_desc *eop_desc = tx_buffer-\u003enext_to_watch;\n+\n+\t\t/* if next_to_watch is not set then there is no work pending */\n+\t\tif (!eop_desc)\n+\t\t\tbreak;\n+\n+\t\t/* prevent any other reads prior to eop_desc */\n+\t\tdma_rmb();\n+\n+\t\t/* if eop DD is not set pending work has not been completed */\n+\t\tif (!(eop_desc-\u003evlan_cmd \u0026 cpu_to_le32(M_TXD_STAT_DD)))\n+\t\t\tbreak;\n+\t\t/* clear next_to_watch to prevent false hangs */\n+\t\ttx_buffer-\u003enext_to_watch = NULL;\n+\t\ttotal_bytes += tx_buffer-\u003ebytecount;\n+\t\ttotal_packets += tx_buffer-\u003egso_segs;\n+\t\tnapi_consume_skb(tx_buffer-\u003eskb, napi_budget);\n+\t\tif (tx_buffer-\u003emapped_as_page) {\n+\t\t\tdma_unmap_page(tx_ring-\u003edev,\n+\t\t\t\t       dma_unmap_addr(tx_buffer, dma),\n+\t\t\t\t       dma_unmap_len(tx_buffer, len),\n+\t\t\t\t       DMA_TO_DEVICE);\n+\t\t} else {\n+\t\t\tdma_unmap_single(tx_ring-\u003edev,\n+\t\t\t\t\t dma_unmap_addr(tx_buffer, dma),\n+\t\t\t\t\t dma_unmap_len(tx_buffer, len),\n+\t\t\t\t\t DMA_TO_DEVICE);\n+\t\t}\n+\t\ttx_buffer-\u003eskb = NULL;\n+\t\tdma_unmap_len_set(tx_buffer, len, 0);\n+\n+\t\t/* unmap remaining buffers */\n+\t\twhile (tx_desc != eop_desc) {\n+\t\t\ttx_buffer++;\n+\t\t\ttx_desc++;\n+\t\t\ti++;\n+\t\t\tif (unlikely(!i)) {\n+\t\t\t\ti -= tx_ring-\u003ecount;\n+\t\t\t\ttx_buffer = tx_ring-\u003etx_buffer_info;\n+\t\t\t\ttx_desc = M_TX_DESC(tx_ring, 0);\n+\t\t\t}\n+\n+\t\t\t/* unmap any remaining paged data */\n+\t\t\tif (dma_unmap_len(tx_buffer, len)) {\n+\t\t\t\tdma_unmap_page(tx_ring-\u003edev,\n+\t\t\t\t\t       dma_unmap_addr(tx_buffer, dma),\n+\t\t\t\t\t       dma_unmap_len(tx_buffer, len),\n+\t\t\t\t\t       DMA_TO_DEVICE);\n+\t\t\t\tdma_unmap_len_set(tx_buffer, len, 0);\n+\t\t\t}\n+\t\t}\n+\n+\t\t/* move us one more past the eop_desc for start of next pkt */\n+\t\ttx_buffer++;\n+\t\ttx_desc++;\n+\t\ti++;\n+\t\tif (unlikely(!i)) {\n+\t\t\ti -= tx_ring-\u003ecount;\n+\t\t\ttx_buffer = tx_ring-\u003etx_buffer_info;\n+\t\t\ttx_desc = M_TX_DESC(tx_ring, 0);\n+\t\t}\n+\n+\t\tprefetch(tx_desc);\n+\t\tbudget--;\n+\t} while (likely(budget \u003e 0));\n+\n+\ti += tx_ring-\u003ecount;\n+\ttx_ring-\u003enext_to_clean = i;\n+\tu64_stats_update_begin(\u0026tx_ring-\u003esyncp);\n+\ttx_ring-\u003estats.bytes += total_bytes;\n+\ttx_ring-\u003estats.packets += total_packets;\n+\tu64_stats_update_end(\u0026tx_ring-\u003esyncp);\n+\n+#define TX_WAKE_THRESHOLD (DESC_NEEDED * 2)\n+\t__netif_txq_completed_wake(txring_txq(tx_ring),\n+\t\t\t\t   total_packets, total_bytes,\n+\t\t\t\t   mucse_desc_unused(tx_ring),\n+\t\t\t\t   TX_WAKE_THRESHOLD,\n+\t\t\t\t   !netif_carrier_ok(tx_ring-\u003enetdev) ||\n+\t\t\t\t   test_bit(__MUCSE_DOWN, \u0026mucse-\u003estate));\n+\n+\treturn !!budget;\n+}\n+\n+static bool mucse_alloc_mapped_page(struct mucse_ring *rx_ring,\n+\t\t\t\t    struct mucse_rx_buffer *bi)\n+{\n+\tstruct page *page = bi-\u003epage;\n+\tdma_addr_t dma;\n+\n+\tif (page) {\n+\t\t/* Buffer is being reused without going back through the\n+\t\t * page_pool. Do dma_sync for hw use.\n+\t\t */\n+\t\tdma_sync_single_range_for_device(rx_ring-\u003edev, bi-\u003edma,\n+\t\t\t\t\t\t bi-\u003epage_offset,\n+\t\t\t\t\t\t PAGE_SIZE - bi-\u003epage_offset,\n+\t\t\t\t\t\t DMA_FROM_DEVICE);\n+\t\treturn true;\n+\t}\n+\n+\tpage = page_pool_dev_alloc_pages(rx_ring-\u003epage_pool);\n+\tif (unlikely(!page))\n+\t\treturn false;\n+\tdma = page_pool_get_dma_addr(page);\n+\n+\tbi-\u003edma = dma;\n+\tbi-\u003epage = page;\n+\tbi-\u003epage_offset = RNPGBE_SKB_PAD;\n+\n+\treturn true;\n+}\n+\n+static void mucse_update_rx_tail(struct mucse_ring *rx_ring,\n+\t\t\t\t u32 val)\n+{\n+\trx_ring-\u003enext_to_use = val;\n+\twritel(val, rx_ring-\u003etail);\n+}\n+\n+/**\n+ * rnpgbe_alloc_rx_buffers - Replace used receive buffers\n+ * @rx_ring: ring to place buffers on\n+ * @cleaned_count: number of buffers to replace\n+ *\n+ * Return: true if alloc failed\n+ **/\n+static bool rnpgbe_alloc_rx_buffers(struct mucse_ring *rx_ring,\n+\t\t\t\t    u16 cleaned_count)\n+{\n+\tu64 fun_id = ((u64)(rx_ring-\u003epfvfnum) \u003c\u003c 56);\n+\tunion rnpgbe_rx_desc *rx_desc;\n+\tu16 i = rx_ring-\u003enext_to_use;\n+\tstruct mucse_rx_buffer *bi;\n+\tbool err = false;\n+\tu64 addr;\n+\t/* nothing to do */\n+\tif (!cleaned_count)\n+\t\treturn err;\n+\n+\trx_desc = M_RX_DESC(rx_ring, i);\n+\tbi = \u0026rx_ring-\u003erx_buffer_info[i];\n+\ti -= rx_ring-\u003ecount;\n+\n+\tdo {\n+\t\tif (!mucse_alloc_mapped_page(rx_ring, bi)) {\n+\t\t\terr = true;\n+\t\t\tbreak;\n+\t\t}\n+\n+\t\taddr = (u64)(bi-\u003edma + bi-\u003epage_offset);\n+\t\trx_desc-\u003epkt_addr = cpu_to_le64(addr | fun_id);\n+\t\t/* clean dd */\n+\t\trx_desc-\u003eresv_cmd = 0;\n+\t\trx_desc++;\n+\t\tbi++;\n+\t\ti++;\n+\t\tif (unlikely(!i)) {\n+\t\t\trx_desc = M_RX_DESC(rx_ring, 0);\n+\t\t\tbi = rx_ring-\u003erx_buffer_info;\n+\t\t\ti -= rx_ring-\u003ecount;\n+\t\t}\n+\t\tcleaned_count--;\n+\t} while (cleaned_count);\n+\n+\ti += rx_ring-\u003ecount;\n+\n+\t/* Publish the tail even when allocation stopped early, so a stale\n+\t * tail from a previous session cannot point hardware at a\n+\t * reallocated ring.\n+\t */\n+\tdma_wmb();\n+\tmucse_update_rx_tail(rx_ring, i);\n+\n+\treturn err;\n+}\n+\n+/**\n+ * rnpgbe_get_buffer - Get the rx_buffer to be used\n+ * @rx_ring: pointer to rx ring\n+ * @skb: pointer skb for this packet\n+ * @size: data size in this desc\n+ *\n+ * Return: rx_buffer.\n+ **/\n+static struct mucse_rx_buffer *rnpgbe_get_buffer(struct mucse_ring *rx_ring,\n+\t\t\t\t\t\t struct sk_buff **skb,\n+\t\t\t\t\t\t const unsigned int size)\n+{\n+\tstruct mucse_rx_buffer *rx_buffer;\n+\n+\trx_buffer = \u0026rx_ring-\u003erx_buffer_info[rx_ring-\u003enext_to_clean];\n+\t*skb = rx_buffer-\u003eskb;\n+\tprefetchw(page_address(rx_buffer-\u003epage) + rx_buffer-\u003epage_offset);\n+\t/* we are reusing so sync this buffer for CPU use */\n+\tdma_sync_single_range_for_cpu(rx_ring-\u003edev, rx_buffer-\u003edma,\n+\t\t\t\t      rx_buffer-\u003epage_offset, size,\n+\t\t\t\t      DMA_FROM_DEVICE);\n+\n+\treturn rx_buffer;\n+}\n+\n+/**\n+ * rnpgbe_add_rx_frag - Add non-linear data to the skb\n+ * @rx_buffer: pointer to rx_buffer\n+ * @skb: pointer skb for this packet\n+ * @size: data size in this desc\n+ **/\n+static void rnpgbe_add_rx_frag(struct mucse_rx_buffer *rx_buffer,\n+\t\t\t       struct sk_buff *skb,\n+\t\t\t       unsigned int size)\n+{\n+\tunsigned int truesize = PAGE_SIZE;\n+\n+\tskb_add_rx_frag(skb, skb_shinfo(skb)-\u003enr_frags, rx_buffer-\u003epage,\n+\t\t\trx_buffer-\u003epage_offset, size, truesize);\n+}\n+\n+/**\n+ * rnpgbe_build_skb - Try to build a skb based on rx_buffer\n+ * @rx_buffer: pointer to rx_buffer\n+ * @size: data size in this desc\n+ *\n+ * Return: skb for this rx_buffer\n+ **/\n+static struct sk_buff *rnpgbe_build_skb(struct mucse_rx_buffer *rx_buffer,\n+\t\t\t\t\tunsigned int size)\n+{\n+\tvoid *va = page_address(rx_buffer-\u003epage) + rx_buffer-\u003epage_offset;\n+\tunsigned int truesize = PAGE_SIZE;\n+\tstruct sk_buff *skb;\n+\n+\tnet_prefetch(va);\n+\t/* build an skb around the page buffer */\n+\tskb = build_skb(va - RNPGBE_SKB_PAD, truesize);\n+\tif (unlikely(!skb))\n+\t\treturn NULL;\n+\t/* update pointers within the skb to store the data */\n+\tskb_reserve(skb, RNPGBE_SKB_PAD);\n+\t__skb_put(skb, size);\n+\tskb_mark_for_recycle(skb);\n+\n+\treturn skb;\n+}\n+\n+/**\n+ * rnpgbe_is_non_eop - Process handling of non-EOP buffers\n+ * @rx_ring: rx ring being processed\n+ * @rx_desc: rx descriptor for current buffer\n+ * @skb: current socket buffer containing buffer in progress\n+ *\n+ * This function updates next to clean.  If the buffer is an EOP buffer\n+ * this function exits returning false, otherwise it will place the\n+ * sk_buff in the next buffer to be chained and return true indicating\n+ * that this is in fact a non-EOP buffer.\n+ *\n+ * Return: true for not end of packet\n+ **/\n+static bool rnpgbe_is_non_eop(struct mucse_ring *rx_ring,\n+\t\t\t      union rnpgbe_rx_desc *rx_desc,\n+\t\t\t      struct sk_buff *skb)\n+{\n+\tu32 ntc = rx_ring-\u003enext_to_clean + 1;\n+\n+\t/* fetch, update, and store next to clean */\n+\tntc = (ntc \u003c rx_ring-\u003ecount) ? ntc : 0;\n+\trx_ring-\u003enext_to_clean = ntc;\n+\tprefetch(M_RX_DESC(rx_ring, ntc));\n+\t/* if we are the last buffer then there is nothing else to do */\n+\tif (likely(rnpgbe_test_staterr(rx_desc, M_RXD_STAT_EOP)))\n+\t\treturn false;\n+\tif (skb_shinfo(skb)-\u003enr_frags \u003c MAX_SKB_FRAGS) {\n+\t\t/* place skb in next buffer to be received */\n+\t\trx_ring-\u003erx_buffer_info[ntc].skb = skb;\n+\t} else {\n+\t\tatomic64_inc(\u0026rx_ring-\u003estats.dropped);\n+\t\t/* too much frags, force free */\n+\t\tdev_kfree_skb_any(skb);\n+\t\trx_ring-\u003edrop_status = true;\n+\t}\n+\t/* we should clean it since we used all info in it */\n+\trx_desc-\u003ewb.cmd = 0;\n+\n+\treturn true;\n+}\n+\n+/**\n+ * rnpgbe_cleanup_headers - Correct corrupted or empty headers\n+ * @skb: current socket buffer containing buffer in progress\n+ *\n+ * Return: true if an error was encountered and skb was freed.\n+ **/\n+static bool rnpgbe_cleanup_headers(struct sk_buff *skb)\n+{\n+\t/* if eth_skb_pad returns an error the skb was freed */\n+\tif (eth_skb_pad(skb))\n+\t\treturn true;\n+\n+\treturn false;\n+}\n+\n+/**\n+ * rnpgbe_process_skb_fields - Set the RX queue and protocol fields\n+ * @rx_ring: RX descriptor ring containing the queue information\n+ * @skb: skb currently being received\n+ *\n+ * Records the RX queue that received the skb and sets its protocol from\n+ * the Ethernet header.\n+ **/\n+static void rnpgbe_process_skb_fields(struct mucse_ring *rx_ring,\n+\t\t\t\t      struct sk_buff *skb)\n+{\n+\tstruct net_device *dev = rx_ring-\u003enetdev;\n+\n+\tskb_record_rx_queue(skb, rx_ring-\u003equeue_index);\n+\tskb-\u003eprotocol = eth_type_trans(skb, dev);\n+}\n+\n+/**\n+ * rnpgbe_rx_alloc_retry - Retry RX buffer allocation\n+ * @timer: RX allocation retry timer\n+ *\n+ * Schedules NAPI after RX buffer allocation fails during polling or\n+ * initial ring configuration.\n+ **/\n+static void rnpgbe_rx_alloc_retry(struct timer_list *timer)\n+{\n+\tstruct mucse_q_vector *q_vector =\n+\t\ttimer_container_of(q_vector, timer, rx_alloc_timer);\n+\n+\tnapi_schedule(\u0026q_vector-\u003enapi);\n+}\n+\n+/**\n+ * rnpgbe_clean_rx_irq - Clean completed descriptors from Rx ring\n+ * @q_vector: structure containing interrupt and ring information\n+ * @rx_ring: rx descriptor ring to transact packets on\n+ * @budget: total limit on number of packets to process\n+ *\n+ * rnpgbe_clean_rx_irq tries to check dd in desc, handle this desc\n+ * if dd is set which means data is write-back by hw\n+ *\n+ * Return: amount of work completed.\n+ **/\n+static int rnpgbe_clean_rx_irq(struct mucse_q_vector *q_vector,\n+\t\t\t       struct mucse_ring *rx_ring,\n+\t\t\t       int budget)\n+{\n+\tunsigned int max_size = SKB_WITH_OVERHEAD(PAGE_SIZE) - RNPGBE_SKB_PAD;\n+\tunsigned int total_rx_bytes = 0, total_rx_packets = 0;\n+\tu16 cleaned_count = mucse_desc_unused_rx(rx_ring);\n+\tunsigned int work_done = 0;\n+\n+\twhile (likely(work_done \u003c budget)) {\n+\t\tstruct mucse_rx_buffer *rx_buffer;\n+\t\tunion rnpgbe_rx_desc *rx_desc;\n+\t\tstruct sk_buff *skb;\n+\t\tunsigned int size;\n+\n+\t\tif (cleaned_count \u003e= M_RX_BUFFER_WRITE) {\n+\t\t\tif (rnpgbe_alloc_rx_buffers(rx_ring, cleaned_count)) {\n+\t\t\t\tmod_timer(\u0026q_vector-\u003erx_alloc_timer,\n+\t\t\t\t\t  jiffies + msecs_to_jiffies(500));\n+\t\t\t\tcleaned_count = mucse_desc_unused_rx(rx_ring);\n+\t\t\t} else {\n+\t\t\t\tcleaned_count = 0;\n+\t\t\t}\n+\t\t}\n+\t\trx_desc = M_RX_DESC(rx_ring, rx_ring-\u003enext_to_clean);\n+\n+\t\tif (!rnpgbe_test_staterr(rx_desc, M_RXD_STAT_DD))\n+\t\t\tbreak;\n+\n+\t\t/* This memory barrier is needed to keep us from reading\n+\t\t * any other fields out of the rx_desc until we know the\n+\t\t * descriptor has been written back\n+\t\t */\n+\t\tdma_rmb();\n+\t\t/* Hardware enforces: minimum 33-bytes descriptor(no 1-13 byte\n+\t\t * size), multi-descriptors only for jumbo frames \u003e 1536 bytes\n+\t\t * (controlled by M_DEFAULT_SG=96, each descriptor no more than\n+\t\t * 1536 bytes). Small packets use single descriptor.\n+\t\t */\n+\t\tsize = le16_to_cpu(rx_desc-\u003ewb.len);\n+\n+\t\tif (unlikely(rx_ring-\u003edrop_status)) {\n+\t\t\tcleaned_count++;\n+\t\t\t/* drop data until eop */\n+\t\t\tif (rnpgbe_test_staterr(rx_desc, M_RXD_STAT_EOP)) {\n+\t\t\t\trx_ring-\u003edrop_status = false;\n+\t\t\t\twork_done++;\n+\t\t\t}\n+\n+\t\t\trx_desc-\u003ewb.cmd = 0;\n+\t\t\trx_ring-\u003enext_to_clean++;\n+\t\t\tif (rx_ring-\u003enext_to_clean \u003e= rx_ring-\u003ecount)\n+\t\t\t\trx_ring-\u003enext_to_clean = 0;\n+\t\t\tcontinue;\n+\t\t}\n+\n+\t\tif (unlikely(!size || size \u003e max_size)) {\n+\t\t\tstruct mucse_rx_buffer *err_rx_buffer;\n+\t\t\tu16 idx = rx_ring-\u003enext_to_clean;\n+\n+\t\t\tcleaned_count++;\n+\t\t\tatomic64_inc(\u0026rx_ring-\u003estats.dropped);\n+\n+\t\t\t/* Free the partial skb from a previous non-EOP\n+\t\t\t * descriptor before advancing next_to_clean.\n+\t\t\t */\n+\t\t\terr_rx_buffer = \u0026rx_ring-\u003erx_buffer_info[idx];\n+\t\t\tif (unlikely(err_rx_buffer-\u003eskb)) {\n+\t\t\t\tdev_kfree_skb_any(err_rx_buffer-\u003eskb);\n+\t\t\t\terr_rx_buffer-\u003eskb = NULL;\n+\t\t\t}\n+\n+\t\t\t/* drop data until eop */\n+\t\t\tif (rnpgbe_test_staterr(rx_desc, M_RXD_STAT_EOP))\n+\t\t\t\twork_done++;\n+\t\t\telse\n+\t\t\t\trx_ring-\u003edrop_status = true;\n+\n+\t\t\trx_desc-\u003ewb.cmd = 0;\n+\t\t\trx_ring-\u003enext_to_clean++;\n+\t\t\tif (rx_ring-\u003enext_to_clean \u003e= rx_ring-\u003ecount)\n+\t\t\t\trx_ring-\u003enext_to_clean = 0;\n+\t\t\tcontinue;\n+\t\t}\n+\n+\t\trx_buffer = rnpgbe_get_buffer(rx_ring, \u0026skb, size);\n+\n+\t\tif (skb)\n+\t\t\trnpgbe_add_rx_frag(rx_buffer, skb, size);\n+\t\telse\n+\t\t\tskb = rnpgbe_build_skb(rx_buffer, size);\n+\n+\t\tif (!skb) {\n+\t\t\tcleaned_count++;\n+\n+\t\t\t/* drop until eop if multiple descriptors */\n+\t\t\tif (rnpgbe_test_staterr(rx_desc, M_RXD_STAT_EOP))\n+\t\t\t\twork_done++;\n+\t\t\telse\n+\t\t\t\trx_ring-\u003edrop_status = true;\n+\n+\t\t\trx_desc-\u003ewb.cmd = 0;\n+\t\t\trx_ring-\u003enext_to_clean++;\n+\t\t\tatomic64_inc(\u0026rx_ring-\u003estats.dropped);\n+\t\t\tif (rx_ring-\u003enext_to_clean \u003e= rx_ring-\u003ecount)\n+\t\t\t\trx_ring-\u003enext_to_clean = 0;\n+\n+\t\t\tcontinue;\n+\t\t}\n+\n+\t\trx_buffer-\u003epage = NULL;\n+\t\trx_buffer-\u003eskb = NULL;\n+\t\tcleaned_count++;\n+\n+\t\tif (rnpgbe_is_non_eop(rx_ring, rx_desc, skb))\n+\t\t\tcontinue;\n+\n+\t\t/* verify the packet layout is correct */\n+\t\tif (rnpgbe_cleanup_headers(skb)) {\n+\t\t\t/* we should clean it since we used all info in it */\n+\t\t\tatomic64_inc(\u0026rx_ring-\u003estats.dropped);\n+\t\t\trx_desc-\u003ewb.cmd = 0;\n+\t\t\twork_done++;\n+\t\t\tcontinue;\n+\t\t}\n+\n+\t\t/* probably a little skewed due to removing CRC */\n+\t\ttotal_rx_bytes += skb-\u003elen;\n+\t\trnpgbe_process_skb_fields(rx_ring, skb);\n+\t\trx_desc-\u003ewb.cmd = 0;\n+\t\tnapi_gro_receive(\u0026q_vector-\u003enapi, skb);\n+\t\t/* Update packet statistics and NAPI work accounting. */\n+\t\ttotal_rx_packets++;\n+\t\twork_done++;\n+\t}\n+\n+\tu64_stats_update_begin(\u0026rx_ring-\u003esyncp);\n+\trx_ring-\u003estats.packets += total_rx_packets;\n+\trx_ring-\u003estats.bytes += total_rx_bytes;\n+\tu64_stats_update_end(\u0026rx_ring-\u003esyncp);\n+\n+\treturn work_done;\n+}\n+\n+/**\n+ * rnpgbe_poll - NAPI polling callback\n+ * @napi: structure for representing this polling device\n+ * @budget: how many packets driver is allowed to clean\n+ *\n+ * This function is the NAPI poll callback for all interrupt modes.\n+ *\n+ * Return: work done in this call\n+ **/\n+static int rnpgbe_poll(struct napi_struct *napi, int budget)\n+{\n+\tstruct mucse_q_vector *q_vector =\n+\t\tcontainer_of(napi, struct mucse_q_vector, napi);\n+\tbool clean_complete = true;\n+\tstruct mucse_ring *ring;\n+\tint per_ring_budget;\n+\tint work_done = 0;\n+\n+\tmucse_for_each_ring(ring, q_vector-\u003etx) {\n+\t\tif (!rnpgbe_clean_tx_irq(q_vector, ring, budget))\n+\t\t\tclean_complete = false;\n+\t}\n+\n+\t/* Exit if we are called by netpoll */\n+\tif (unlikely(!budget))\n+\t\treturn 0;\n+\n+\tif (q_vector-\u003erx.count \u003e 1)\n+\t\tper_ring_budget = max(budget / q_vector-\u003erx.count, 1);\n+\telse\n+\t\tper_ring_budget = budget;\n+\n+\tmucse_for_each_ring(ring, q_vector-\u003erx) {\n+\t\tint cleaned = 0;\n+\n+\t\tcleaned = rnpgbe_clean_rx_irq(q_vector, ring, per_ring_budget);\n+\t\twork_done += cleaned;\n+\t\tif (cleaned \u003e= per_ring_budget)\n+\t\t\tclean_complete = false;\n+\t}\n+\n+\tif (!clean_complete)\n+\t\treturn budget;\n+\n+\tif (likely(napi_complete_done(napi, work_done))) {\n+\t\tif (!test_bit(__MUCSE_DOWN, \u0026q_vector-\u003emucse-\u003estate))\n+\t\t\trnpgbe_irq_enable_queues(q_vector);\n+\t}\n+\n+\treturn work_done;\n+}\n+\n+/**\n+ * rnpgbe_request_mbx_irq - Register mbx routine\n+ * @mucse: pointer to private structure\n+ *\n+ * In MSIX mode, register a dedicated handler for vector 0 (mailbox)\n+ * In MSI/MSI-X_SINGLE mode, mailbox is multiplexed through\n+ * data tx/rx handler.\n+ *\n+ * Return: 0 on success, negative on failure\n+ **/\n+int rnpgbe_request_mbx_irq(struct mucse *mucse)\n+{\n+\tstruct pci_dev *pdev = mucse-\u003epdev;\n+\tint err = 0;\n+\n+\tsnprintf(mucse-\u003embx_name, sizeof(mucse-\u003embx_name),\n+\t\t \"rnpgbe-mbx:%s\", pci_name(pdev));\n+\tINIT_WORK(\u0026mucse-\u003embx_work, rnpgbe_mbx_work);\n+\n+\tif (mucse-\u003eflags \u0026 M_FLAG_MSIX_EN) {\n+\t\terr = request_irq(pci_irq_vector(pdev, 0),\n+\t\t\t\t  rnpgbe_msix_other, 0, mucse-\u003embx_name,\n+\t\t\t\t  mucse);\n+\t} else {\n+\t\terr = request_irq(pci_irq_vector(pdev, 0),\n+\t\t\t\t  rnpgbe_int_single, 0, mucse-\u003embx_name,\n+\t\t\t\t  mucse);\n+\t}\n+\n+\treturn err;\n+}\n+\n+/**\n+ * rnpgbe_free_mbx_irq - Remove mbx routine\n+ * @mucse: pointer to private structure\n+ **/\n+void rnpgbe_free_mbx_irq(struct mucse *mucse)\n+{\n+\tstruct pci_dev *pdev = mucse-\u003epdev;\n+\n+\tfree_irq(pci_irq_vector(pdev, 0), mucse);\n+\tcancel_work_sync(\u0026mucse-\u003embx_work);\n+}\n+\n+/**\n+ * rnpgbe_set_num_queues - Allocate queues for device, feature dependent\n+ * @mucse: pointer to private structure\n+ *\n+ * Determine tx/rx queue counts\n+ **/\n+static void rnpgbe_set_num_queues(struct mucse *mucse)\n+{\n+\t/* start from 1 queue */\n+\tmucse-\u003enum_tx_queues = 1;\n+\tmucse-\u003enum_rx_queues = 1;\n+}\n+\n+/**\n+ * rnpgbe_set_interrupt_capability - Set MSI-X or MSI if supported\n+ * @mucse: pointer to private structure\n+ *\n+ * Attempt to configure the interrupts using the best available\n+ * capabilities of the hardware.\n+ *\n+ * Return: 0 on success, negative on failure\n+ **/\n+static int rnpgbe_set_interrupt_capability(struct mucse *mucse)\n+{\n+\tint v_budget;\n+\n+\tv_budget = min3(mucse-\u003enum_tx_queues, mucse-\u003enum_rx_queues,\n+\t\t\tMAX_Q_VECTORS);\n+\tv_budget = min_t(int, v_budget, num_online_cpus());\n+\t/* add one vector for mbx */\n+\tv_budget += 1;\n+\n+\t/* Hardware limitation: only 1 MSI vector is supported even\n+\t * if multiple messages are requested. MSI mode falls back\n+\t * to single vector automatically.\n+\t */\n+\tv_budget = pci_alloc_irq_vectors(mucse-\u003epdev, 1, v_budget,\n+\t\t\t\t\t PCI_IRQ_MSI | PCI_IRQ_MSIX);\n+\tif (v_budget \u003c 0)\n+\t\treturn v_budget;\n+\n+\tif (mucse-\u003epdev-\u003emsix_enabled) {\n+\t\t/* q_vector not include mbx */\n+\t\tif (v_budget \u003e 1) {\n+\t\t\tmucse-\u003eflags |= M_FLAG_MSIX_EN;\n+\t\t\tmucse-\u003enum_q_vectors = v_budget - 1;\n+\t\t} else {\n+\t\t\tmucse-\u003eflags |= M_FLAG_MSIX_SINGLE_EN;\n+\t\t\tmucse-\u003enum_q_vectors = 1;\n+\t\t}\n+\t} else {\n+\t\t/* Hardware supports only one MSI interrupt. */\n+\t\tmucse-\u003enum_q_vectors = 1;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+/**\n+ * mucse_add_ring - Add ring to ring container\n+ * @ring: ring to be added\n+ * @head: ring container\n+ **/\n+static void mucse_add_ring(struct mucse_ring *ring,\n+\t\t\t   struct mucse_ring_container *head)\n+{\n+\tring-\u003enext = head-\u003ering;\n+\thead-\u003ering = ring;\n+\thead-\u003ecount++;\n+}\n+\n+/**\n+ * rnpgbe_alloc_q_vector - Allocate memory for a single interrupt vector\n+ * @mucse: pointer to private structure\n+ * @eth_queue_idx: queue_index idx for this q_vector\n+ * @vector_idx: q_vector array index\n+ * @r_idx: starting hardware ring index\n+ * @r_count: number of TX/RX ring pairs\n+ * @step: ring step\n+ *\n+ * Return: 0 on success. If allocation fails we return -ENOMEM.\n+ **/\n+static int rnpgbe_alloc_q_vector(struct mucse *mucse,\n+\t\t\t\t int eth_queue_idx, int vector_idx, int r_idx,\n+\t\t\t\t int r_count, int step)\n+{\n+\tint rxr_idx = r_idx, txr_idx = r_idx;\n+\tstruct mucse_hw *hw = \u0026mucse-\u003ehw;\n+\tstruct mucse_q_vector *q_vector;\n+\tint txr_count, rxr_count, idx;\n+\tstruct mucse_ring *ring;\n+\tint ring_count;\n+\n+\t/*\n+\t * TX and RX rings are always allocated as pairs with the same hardware\n+\t * ring index. Interrupt control is shared by the pair, so the TX ring\n+\t * list is used to update the common registers.\n+\t */\n+\ttxr_count = r_count;\n+\trxr_count = r_count;\n+\tring_count = txr_count + rxr_count;\n+\n+\tq_vector = kzalloc_flex(*q_vector, ring, ring_count);\n+\tif (!q_vector)\n+\t\treturn -ENOMEM;\n+\n+\tnetif_napi_add(mucse-\u003enetdev, \u0026q_vector-\u003enapi, rnpgbe_poll);\n+\t/* tie q_vector and mucse together */\n+\tmucse-\u003eq_vector[vector_idx] = q_vector;\n+\tq_vector-\u003emucse = mucse;\n+\ttimer_setup(\u0026q_vector-\u003erx_alloc_timer, rnpgbe_rx_alloc_retry, 0);\n+\tq_vector-\u003ehw_vector = vector_idx;\n+\t/* if mbx use separate irq, we should add 1 */\n+\tif (mucse-\u003eflags \u0026 M_FLAG_MSIX_EN)\n+\t\tq_vector-\u003ehw_vector++;\n+\n+\tring = q_vector-\u003ering;\n+\n+\tfor (idx = 0; idx \u003c txr_count; idx++) {\n+\t\tring-\u003edev = \u0026mucse-\u003epdev-\u003edev;\n+\t\tmucse_add_ring(ring, \u0026q_vector-\u003etx);\n+\t\tring-\u003ecount = mucse-\u003etx_ring_item_count;\n+\t\tring-\u003enetdev = mucse-\u003enetdev;\n+\t\tring-\u003equeue_index = eth_queue_idx + idx;\n+\t\tring-\u003ernpgbe_queue_idx = txr_idx;\n+\t\tring-\u003ering_addr = hw-\u003ehw_addr + RING_OFFSET(txr_idx);\n+\t\tring-\u003eirq_mask = ring-\u003ering_addr + RNPGBE_DMA_INT_MASK;\n+\t\tring-\u003etrig = ring-\u003ering_addr + RNPGBE_DMA_INT_TRIG;\n+\t\tring-\u003eq_vector = q_vector;\n+\t\tring-\u003epfvfnum = hw-\u003epfvfnum;\n+\t\tu64_stats_init(\u0026ring-\u003esyncp);\n+\t\tmucse-\u003etx_ring[ring-\u003equeue_index] = ring;\n+\t\ttxr_idx += step;\n+\t\tring++;\n+\t}\n+\n+\tfor (idx = 0; idx \u003c rxr_count; idx++) {\n+\t\tring-\u003edev = \u0026mucse-\u003epdev-\u003edev;\n+\t\tmucse_add_ring(ring, \u0026q_vector-\u003erx);\n+\t\tring-\u003ecount = mucse-\u003erx_ring_item_count;\n+\t\tring-\u003enetdev = mucse-\u003enetdev;\n+\t\tring-\u003equeue_index = eth_queue_idx + idx;\n+\t\tring-\u003ernpgbe_queue_idx = rxr_idx;\n+\t\tring-\u003ering_addr = hw-\u003ehw_addr + RING_OFFSET(rxr_idx);\n+\t\tring-\u003eirq_mask = ring-\u003ering_addr + RNPGBE_DMA_INT_MASK;\n+\t\tring-\u003etrig = ring-\u003ering_addr + RNPGBE_DMA_INT_TRIG;\n+\t\tring-\u003eq_vector = q_vector;\n+\t\tring-\u003epfvfnum = hw-\u003epfvfnum;\n+\t\tu64_stats_init(\u0026ring-\u003esyncp);\n+\t\tmucse-\u003erx_ring[ring-\u003equeue_index] = ring;\n+\t\trxr_idx += step;\n+\t\tring++;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+/**\n+ * rnpgbe_free_q_vector - Free memory allocated for specific interrupt vector\n+ * @mucse: pointer to private structure\n+ * @vector_idx: q_vector array index\n+ *\n+ * This function frees the memory allocated to the q_vector.  In addition if\n+ * NAPI is enabled it will delete any references to the NAPI struct prior\n+ * to freeing the q_vector.\n+ **/\n+static void rnpgbe_free_q_vector(struct mucse *mucse, int vector_idx)\n+{\n+\tstruct mucse_q_vector *q_vector = mucse-\u003eq_vector[vector_idx];\n+\tstruct mucse_ring *ring;\n+\n+\ttimer_delete_sync(\u0026q_vector-\u003erx_alloc_timer);\n+\n+\tmucse_for_each_ring(ring, q_vector-\u003etx)\n+\t\tmucse-\u003etx_ring[ring-\u003equeue_index] = NULL;\n+\tmucse_for_each_ring(ring, q_vector-\u003erx)\n+\t\tmucse-\u003erx_ring[ring-\u003equeue_index] = NULL;\n+\tmucse-\u003eq_vector[vector_idx] = NULL;\n+\tnetif_napi_del(\u0026q_vector-\u003enapi);\n+\tkfree(q_vector);\n+}\n+\n+/**\n+ * rnpgbe_alloc_q_vectors - Allocate memory for interrupt vectors\n+ * @mucse: pointer to private structure\n+ *\n+ * Return: 0 on success, or -ENOMEM on allocation failure.\n+ **/\n+static int rnpgbe_alloc_q_vectors(struct mucse *mucse)\n+{\n+\tint err, ring_cnt, v_remaing = mucse-\u003enum_q_vectors;\n+\tint r_remaing = min_t(int, mucse-\u003enum_tx_queues,\n+\t\t\t      mucse-\u003enum_rx_queues);\n+\tint q_vector_nums = 0;\n+\tint eth_queue_idx = 0;\n+\tint vector_idx = 0;\n+\tint ring_step = 1;\n+\tint ring_idx = 0;\n+\n+\tfor (; r_remaing \u003e 0 \u0026\u0026 v_remaing \u003e 0; v_remaing--) {\n+\t\tring_cnt = DIV_ROUND_UP(r_remaing, v_remaing);\n+\t\terr = rnpgbe_alloc_q_vector(mucse, eth_queue_idx,\n+\t\t\t\t\t    vector_idx, ring_idx, ring_cnt,\n+\t\t\t\t\t    ring_step);\n+\t\tif (err)\n+\t\t\tgoto err_free_q_vector;\n+\t\tring_idx += ring_step * ring_cnt;\n+\t\teth_queue_idx += ring_cnt;\n+\t\tr_remaing -= ring_cnt;\n+\t\tq_vector_nums++;\n+\t\tvector_idx++;\n+\t}\n+\t/* Fix the real used q_vectors_nums */\n+\tmucse-\u003enum_q_vectors = q_vector_nums;\n+\tmucse-\u003enum_tx_queues = eth_queue_idx;\n+\tmucse-\u003enum_rx_queues = eth_queue_idx;\n+\n+\treturn 0;\n+\n+err_free_q_vector:\n+\tmucse-\u003enum_tx_queues = 0;\n+\tmucse-\u003enum_rx_queues = 0;\n+\tmucse-\u003enum_q_vectors = 0;\n+\n+\twhile (vector_idx--)\n+\t\trnpgbe_free_q_vector(mucse, vector_idx);\n+\n+\treturn err;\n+}\n+\n+/**\n+ * rnpgbe_reset_interrupt_capability - Reset irq capability setup\n+ * @mucse: pointer to private structure\n+ **/\n+static void rnpgbe_reset_interrupt_capability(struct mucse *mucse)\n+{\n+\tpci_free_irq_vectors(mucse-\u003epdev);\n+\tmucse-\u003eflags \u0026= ~(M_FLAG_MSIX_EN |\n+\t\t\tM_FLAG_MSIX_SINGLE_EN);\n+}\n+\n+/**\n+ * rnpgbe_init_interrupt_scheme - Determine proper interrupt scheme\n+ * @mucse: pointer to private structure\n+ *\n+ * We determine which interrupt scheme to use based on...\n+ * - Hardware queue count\n+ * - cpu count\n+ * - interrupt mode (MSI and legacy modes use one vector)\n+ *\n+ * Return: 0 on success, negative on failure\n+ **/\n+int rnpgbe_init_interrupt_scheme(struct mucse *mucse)\n+{\n+\tint err;\n+\n+\trnpgbe_set_num_queues(mucse);\n+\n+\terr = rnpgbe_set_interrupt_capability(mucse);\n+\tif (err)\n+\t\treturn err;\n+\n+\terr = rnpgbe_alloc_q_vectors(mucse);\n+\tif (err) {\n+\t\trnpgbe_reset_interrupt_capability(mucse);\n+\t\treturn err;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+/**\n+ * rnpgbe_free_q_vectors - Free memory allocated for interrupt vectors\n+ * @mucse: pointer to private structure\n+ *\n+ * This function frees the memory allocated to the q_vectors.  In addition if\n+ * NAPI is enabled it will delete any references to the NAPI struct prior\n+ * to freeing the q_vector.\n+ **/\n+static void rnpgbe_free_q_vectors(struct mucse *mucse)\n+{\n+\tint vector_idx = mucse-\u003enum_q_vectors;\n+\n+\tmucse-\u003enum_rx_queues = 0;\n+\tmucse-\u003enum_tx_queues = 0;\n+\tmucse-\u003enum_q_vectors = 0;\n+\n+\twhile (vector_idx--)\n+\t\trnpgbe_free_q_vector(mucse, vector_idx);\n+}\n+\n+/**\n+ * rnpgbe_clear_interrupt_scheme - Clear the current interrupt scheme settings\n+ * @mucse: pointer to private structure\n+ *\n+ * Clear interrupt specific resources and reset the structure\n+ **/\n+void rnpgbe_clear_interrupt_scheme(struct mucse *mucse)\n+{\n+\tmucse-\u003enum_tx_queues = 0;\n+\tmucse-\u003enum_rx_queues = 0;\n+\trnpgbe_free_q_vectors(mucse);\n+\trnpgbe_reset_interrupt_capability(mucse);\n+}\n+\n+/**\n+ * rnpgbe_msix_clean_rings - MSI-x interrupt handler for ring irq\n+ * @irq: interrupt number\n+ * @data: private data\n+ *\n+ * rnpgbe_msix_clean_rings handle irq from ring, start napi\n+ * Return: IRQ_HANDLED\n+ **/\n+static irqreturn_t rnpgbe_msix_clean_rings(int irq, void *data)\n+{\n+\tstruct mucse_q_vector *q_vector = (struct mucse_q_vector *)data;\n+\n+\trnpgbe_irq_disable_queues(q_vector);\n+\tnapi_schedule_irqoff(\u0026q_vector-\u003enapi);\n+\n+\treturn IRQ_HANDLED;\n+}\n+\n+/**\n+ * rnpgbe_request_irq - Initialize interrupts\n+ * @mucse: pointer to private structure\n+ *\n+ * Attempts to configure interrupts using the best available\n+ * capabilities of the hardware and kernel.\n+ *\n+ * Return: 0 on success, negative value on failure\n+ **/\n+int rnpgbe_request_irq(struct mucse *mucse)\n+{\n+\tstruct net_device *netdev = mucse-\u003enetdev;\n+\tstruct pci_dev *pdev = mucse-\u003epdev;\n+\tstruct mucse_q_vector *q_vector;\n+\tint err, i;\n+\n+\tif (mucse-\u003eflags \u0026 M_FLAG_MSIX_EN) {\n+\t\tfor (i = 0; i \u003c mucse-\u003enum_q_vectors; i++) {\n+\t\t\tq_vector = mucse-\u003eq_vector[i];\n+\n+\t\t\tsnprintf(q_vector-\u003ename, sizeof(q_vector-\u003ename),\n+\t\t\t\t \"%s-%s-%d\", netdev-\u003ename, \"TxRx\", i);\n+\n+\t\t\terr = request_irq(pci_irq_vector(pdev, i + 1),\n+\t\t\t\t\t  rnpgbe_msix_clean_rings, 0,\n+\t\t\t\t\t  q_vector-\u003ename,\n+\t\t\t\t\t  q_vector);\n+\t\t\tif (err) {\n+\t\t\t\tdev_err(\u0026pdev-\u003edev, \"MSI-X req err %d: %d\\n\",\n+\t\t\t\t\ti + 1, err);\n+\t\t\t\tgoto err_free_irqs;\n+\t\t\t}\n+\t\t}\n+\t}\n+\n+\treturn 0;\n+err_free_irqs:\n+\twhile (i--) {\n+\t\tq_vector = mucse-\u003eq_vector[i];\n+\t\tsynchronize_irq(pci_irq_vector(pdev, i + 1));\n+\t\tfree_irq(pci_irq_vector(pdev, i + 1), q_vector);\n+\t}\n+\n+\treturn err;\n+}\n+\n+/**\n+ * rnpgbe_free_irq - Free interrupts\n+ * @mucse: pointer to private structure\n+ *\n+ * Attempts to free interrupts according initialized type.\n+ **/\n+void rnpgbe_free_irq(struct mucse *mucse)\n+{\n+\tstruct pci_dev *pdev = mucse-\u003epdev;\n+\tstruct mucse_q_vector *q_vector;\n+\n+\tif (mucse-\u003eflags \u0026 M_FLAG_MSIX_EN) {\n+\t\tfor (int i = 0; i \u003c mucse-\u003enum_q_vectors; i++) {\n+\t\t\tq_vector = mucse-\u003eq_vector[i];\n+\t\t\tif (!q_vector)\n+\t\t\t\tcontinue;\n+\n+\t\t\tfree_irq(pci_irq_vector(pdev, i + 1), q_vector);\n+\t\t}\n+\t}\n+}\n+\n+/**\n+ * rnpgbe_set_ring_vector - Set the ring_vector registers,\n+ * mapping interrupt causes to vectors\n+ * @mucse: pointer to private structure\n+ * @queue: queue to map the corresponding interrupt to\n+ * @vector: the vector num to map to the corresponding queue\n+ *\n+ */\n+static void rnpgbe_set_ring_vector(struct mucse *mucse,\n+\t\t\t\t   u8 queue, u8 vector)\n+{\n+\tstruct mucse_hw *hw = \u0026mucse-\u003ehw;\n+\tu32 data;\n+\n+\tdata = hw-\u003epfvfnum \u003c\u003c 24;\n+\tdata |= (vector \u003c\u003c 8);\n+\tdata |= vector;\n+\twritel(data, hw-\u003ering_msix_base + RING_VECTOR(queue));\n+}\n+\n+/**\n+ * rnpgbe_configure_msi - Configure MSI hardware\n+ * @mucse: pointer to private structure\n+ *\n+ * rnpgbe_configure_msi sets up the hardware to properly generate MSI\n+ * interrupts.\n+ **/\n+static void rnpgbe_configure_msi(struct mucse *mucse)\n+{\n+\tstruct mucse_q_vector *q_vector = mucse-\u003eq_vector[0];\n+\tstruct mucse_ring *ring;\n+\n+\t/* TX/RX pairs share the vector register; update it through TX rings. */\n+\tmucse_for_each_ring(ring, q_vector-\u003etx)\n+\t\trnpgbe_set_ring_vector(mucse, ring-\u003ernpgbe_queue_idx, 0);\n+}\n+\n+/**\n+ * rnpgbe_configure_msix - Configure MSI-X hardware\n+ * @mucse: pointer to private structure\n+ *\n+ * rnpgbe_configure_msix sets up the hardware to properly generate MSI-X\n+ * interrupts.\n+ **/\n+static void rnpgbe_configure_msix(struct mucse *mucse)\n+{\n+\tstruct mucse_q_vector *q_vector;\n+\n+\tfor (int i = 0; i \u003c mucse-\u003enum_q_vectors; i++) {\n+\t\tstruct mucse_ring *ring;\n+\n+\t\tq_vector = mucse-\u003eq_vector[i];\n+\t\t/* TX/RX pairs share the vector register; update it through TX rings. */\n+\t\tmucse_for_each_ring(ring, q_vector-\u003etx) {\n+\t\t\trnpgbe_set_ring_vector(mucse, ring-\u003ernpgbe_queue_idx,\n+\t\t\t\t\t       q_vector-\u003ehw_vector);\n+\t\t}\n+\t}\n+}\n+\n+static void rnpgbe_irq_enable(struct mucse *mucse)\n+{\n+\tfor (int i = 0; i \u003c mucse-\u003enum_q_vectors; i++)\n+\t\trnpgbe_irq_enable_queues(mucse-\u003eq_vector[i]);\n+}\n+\n+/**\n+ * rnpgbe_irq_disable - Mask off interrupt generation on the NIC\n+ * @mucse: board private structure\n+ **/\n+void rnpgbe_irq_disable(struct mucse *mucse)\n+{\n+\tstruct pci_dev *pdev = mucse-\u003epdev;\n+\n+\tif (mucse-\u003eflags \u0026 M_FLAG_MSIX_EN) {\n+\t\tfor (int i = 0; i \u003c mucse-\u003enum_q_vectors; i++) {\n+\t\t\trnpgbe_irq_disable_queues(mucse-\u003eq_vector[i]);\n+\t\t\tsynchronize_irq(pci_irq_vector(pdev, i + 1));\n+\t\t}\n+\t} else {\n+\t\trnpgbe_irq_disable_queues(mucse-\u003eq_vector[0]);\n+\t\tsynchronize_irq(pci_irq_vector(pdev, 0));\n+\t}\n+}\n+\n+static void rnpgbe_napi_enable_all(struct mucse *mucse)\n+{\n+\tfor (int i = 0; i \u003c mucse-\u003enum_q_vectors; i++)\n+\t\tnapi_enable(\u0026mucse-\u003eq_vector[i]-\u003enapi);\n+}\n+\n+static void rnpgbe_napi_disable_all(struct mucse *mucse)\n+{\n+\tfor (int i = 0; i \u003c mucse-\u003enum_q_vectors; i++)\n+\t\tnapi_disable(\u0026mucse-\u003eq_vector[i]-\u003enapi);\n+}\n+\n+static void rnpgbe_stop_tx_ring(struct mucse_ring *tx_ring)\n+{\n+\tif (!tx_ring-\u003etx_buffer_info)\n+\t\treturn;\n+\n+\t/* Stop hw. No new descriptors are fetched after TX_START=0.\n+\t * DMA for descriptors fetched before the stop may still be in flight.\n+\t */\n+\tmucse_ring_wr32(tx_ring, RNPGBE_TX_START, 0);\n+\t/* Flush posted write to ensure hardware sees TX_START=0 */\n+\t(void)mucse_ring_rd32(tx_ring, RNPGBE_TX_START);\n+}\n+\n+static void rnpgbe_wait_tx_dma_idle(struct mucse *mucse)\n+{\n+\tstruct mucse_hw *hw = \u0026mucse-\u003ehw;\n+\tu32 dma_status;\n+\tint err;\n+\n+\terr = readl_poll_timeout(hw-\u003ehw_addr + RNPGBE_DMA_STATUS,\n+\t\t\t\t dma_status,\n+\t\t\t\t (dma_status \u0026 RNPGBE_DMA_TX_STATUS) ==\n+\t\t\t\t RNPGBE_DMA_TX_STATUS,\n+\t\t\t\t 10, 100000);\n+\tif (err)\n+\t\tdev_warn(\u0026mucse-\u003epdev-\u003edev,\n+\t\t\t \"Timed out waiting for TX DMA to quiesce, status %#x\\n\",\n+\t\t\t dma_status);\n+}\n+\n+static void rnpgbe_wait_rx_dma_idle(struct mucse *mucse)\n+{\n+\tstruct mucse_hw *hw = \u0026mucse-\u003ehw;\n+\tu32 dma_status;\n+\tint err;\n+\n+\terr = readl_poll_timeout(hw-\u003ehw_addr + RNPGBE_DMA_STATUS,\n+\t\t\t\t dma_status,\n+\t\t\t\t (dma_status \u0026 RNPGBE_DMA_RX_STATUS) ==\n+\t\t\t\t RNPGBE_DMA_RX_STATUS,\n+\t\t\t\t 10, 100000);\n+\tif (err)\n+\t\tdev_warn(\u0026mucse-\u003epdev-\u003edev,\n+\t\t\t \"Timed out waiting for RX DMA to quiesce, status %#x\\n\",\n+\t\t\t dma_status);\n+}\n+\n+/**\n+ * rnpgbe_clean_tx_ring - Free Tx Buffers\n+ * @tx_ring: ring to be cleaned\n+ **/\n+static void rnpgbe_clean_tx_ring(struct mucse_ring *tx_ring)\n+{\n+\tstruct mucse_tx_buffer *tx_buffer;\n+\tu16 i = tx_ring-\u003enext_to_clean;\n+\tunsigned long size;\n+\n+\t/* ring already cleared, nothing to do */\n+\tif (!tx_ring-\u003etx_buffer_info)\n+\t\treturn;\n+\n+\ttx_buffer = \u0026tx_ring-\u003etx_buffer_info[i];\n+\n+\twhile (i != tx_ring-\u003enext_to_use) {\n+\t\tstruct rnpgbe_tx_desc *eop_desc, *tx_desc;\n+\n+\t\tdev_kfree_skb_any(tx_buffer-\u003eskb);\n+\t\t/* unmap skb header data */\n+\t\tif (dma_unmap_len(tx_buffer, len)) {\n+\t\t\tif (tx_buffer-\u003emapped_as_page) {\n+\t\t\t\tdma_unmap_page(tx_ring-\u003edev,\n+\t\t\t\t\t       dma_unmap_addr(tx_buffer, dma),\n+\t\t\t\t\t       dma_unmap_len(tx_buffer, len),\n+\t\t\t\t\t       DMA_TO_DEVICE);\n+\t\t\t} else {\n+\t\t\t\tdma_unmap_single(tx_ring-\u003edev,\n+\t\t\t\t\t\t dma_unmap_addr(tx_buffer, dma),\n+\t\t\t\t\t\t dma_unmap_len(tx_buffer, len),\n+\t\t\t\t\t\t DMA_TO_DEVICE);\n+\t\t\t}\n+\t\t}\n+\t\teop_desc = tx_buffer-\u003enext_to_watch;\n+\t\ttx_desc = M_TX_DESC(tx_ring, i);\n+\t\t/* unmap remaining buffers */\n+\t\twhile (tx_desc != eop_desc) {\n+\t\t\ttx_buffer++;\n+\t\t\ttx_desc++;\n+\t\t\ti++;\n+\t\t\tif (unlikely(i == tx_ring-\u003ecount)) {\n+\t\t\t\ti = 0;\n+\t\t\t\ttx_buffer = tx_ring-\u003etx_buffer_info;\n+\t\t\t\ttx_desc = M_TX_DESC(tx_ring, 0);\n+\t\t\t}\n+\n+\t\t\t/* unmap any remaining paged data */\n+\t\t\tif (dma_unmap_len(tx_buffer, len))\n+\t\t\t\tdma_unmap_page(tx_ring-\u003edev,\n+\t\t\t\t\t       dma_unmap_addr(tx_buffer, dma),\n+\t\t\t\t\t       dma_unmap_len(tx_buffer, len),\n+\t\t\t\t\t       DMA_TO_DEVICE);\n+\t\t}\n+\t\t/* move us one more past the eop_desc for start of next pkt */\n+\t\ttx_buffer++;\n+\t\ti++;\n+\t\tif (unlikely(i == tx_ring-\u003ecount)) {\n+\t\t\ti = 0;\n+\t\t\ttx_buffer = tx_ring-\u003etx_buffer_info;\n+\t\t}\n+\t}\n+\n+\tnetdev_tx_reset_queue(txring_txq(tx_ring));\n+\tsize = sizeof(struct mucse_tx_buffer) * tx_ring-\u003ecount;\n+\tmemset(tx_ring-\u003etx_buffer_info, 0, size);\n+\t/* Zero out the descriptor ring */\n+\tmemset(tx_ring-\u003edesc, 0, tx_ring-\u003esize);\n+\ttx_ring-\u003enext_to_use = 0;\n+\ttx_ring-\u003enext_to_clean = 0;\n+}\n+\n+/**\n+ * rnpgbe_clean_all_tx_rings - Stop TX DMA and free Tx buffers for all queues\n+ * @mucse: board private structure\n+ **/\n+void rnpgbe_clean_all_tx_rings(struct mucse *mucse)\n+{\n+\tstruct mucse_hw *hw = \u0026mucse-\u003ehw;\n+\tu32 dma_axi_ctl;\n+\n+\tfor (int i = 0; i \u003c mucse-\u003enum_tx_queues; i++)\n+\t\trnpgbe_stop_tx_ring(mucse-\u003etx_ring[i]);\n+\n+\tif (mucse-\u003enum_tx_queues)\n+\t\trnpgbe_wait_tx_dma_idle(mucse);\n+\n+\tdma_axi_ctl = mucse_hw_rd32(hw, RNPGBE_DMA_AXI_EN);\n+\tdma_axi_ctl \u0026= ~TX_AXI_RW_EN;\n+\tmucse_hw_wr32(hw, RNPGBE_DMA_AXI_EN, dma_axi_ctl);\n+\t/* Flush the posted write before releasing the DMA mappings. */\n+\t(void)mucse_hw_rd32(hw, RNPGBE_DMA_AXI_EN);\n+\n+\tfor (int i = 0; i \u003c mucse-\u003enum_tx_queues; i++)\n+\t\trnpgbe_clean_tx_ring(mucse-\u003etx_ring[i]);\n+}\n+\n+static void rnpgbe_stop_rx_ring(struct mucse_ring *rx_ring)\n+{\n+\t/* Stop hw. hardware design guarantees:\n+\t * - No new descriptors will be fetched after RX_START=0\n+\t * - No DMA will be initiated for already-fetched descriptors\n+\t */\n+\tmucse_ring_wr32(rx_ring, RNPGBE_RX_START, 0);\n+\t/* Flush posted write to ensure hardware sees RX_START=0 */\n+\t(void)mucse_ring_rd32(rx_ring, RNPGBE_RX_START);\n+}\n+\n+/**\n+ * rnpgbe_stop_all_rx_rings - Stop RX DMA for all queues\n+ * @mucse: board private structure\n+ **/\n+static void rnpgbe_stop_all_rx_rings(struct mucse *mucse)\n+{\n+\tstruct mucse_hw *hw = \u0026mucse-\u003ehw;\n+\tu32 dma_axi_ctl;\n+\n+\tfor (int i = 0; i \u003c mucse-\u003enum_rx_queues; i++)\n+\t\trnpgbe_stop_rx_ring(mucse-\u003erx_ring[i]);\n+\n+\tif (mucse-\u003enum_rx_queues)\n+\t\trnpgbe_wait_rx_dma_idle(mucse);\n+\n+\tdma_axi_ctl = mucse_hw_rd32(hw, RNPGBE_DMA_AXI_EN);\n+\tdma_axi_ctl \u0026= ~RX_AXI_RW_EN;\n+\tmucse_hw_wr32(hw, RNPGBE_DMA_AXI_EN, dma_axi_ctl);\n+\t/* Flush the posted write before releasing the DMA mappings. */\n+\t(void)mucse_hw_rd32(hw, RNPGBE_DMA_AXI_EN);\n+}\n+\n+/**\n+ * rnpgbe_clean_all_rx_rings - Free Rx buffers for all queues\n+ * @mucse: board private structure\n+ **/\n+static void rnpgbe_clean_all_rx_rings(struct mucse *mucse)\n+{\n+\tfor (int i = 0; i \u003c mucse-\u003enum_rx_queues; i++)\n+\t\trnpgbe_clean_rx_ring(mucse-\u003erx_ring[i]);\n+}\n+\n+static void rnpgbe_cancel_rx_retry_timers(struct mucse *mucse)\n+{\n+\tfor (int i = 0; i \u003c mucse-\u003enum_q_vectors; i++)\n+\t\ttimer_delete_sync(\u0026mucse-\u003eq_vector[i]-\u003erx_alloc_timer);\n+}\n+\n+bool rnpgbe_down(struct mucse *mucse)\n+{\n+\tstruct net_device *netdev = mucse-\u003enetdev;\n+\tstruct mucse_hw *hw = \u0026mucse-\u003ehw;\n+\tunsigned long flags;\n+\tint err;\n+\n+\tif (test_and_set_bit(__MUCSE_DOWN, \u0026mucse-\u003estate))\n+\t\treturn false;\n+\n+\tcancel_delayed_work_sync(\u0026mucse-\u003eserv_task);\n+\n+\tspin_lock_irqsave(\u0026mucse-\u003elink_lock, flags);\n+\tWRITE_ONCE(hw-\u003elink, false);\n+\tWRITE_ONCE(hw-\u003espeed, 0);\n+\tWRITE_ONCE(hw-\u003eduplex, 0);\n+\tatomic_set(\u0026mucse-\u003elink_pending, 0);\n+\tspin_unlock_irqrestore(\u0026mucse-\u003elink_lock, flags);\n+\trnpgbe_set_link(hw, false);\n+\n+\tnetif_carrier_off(netdev);\n+\tnetif_tx_disable(netdev);\n+\trnpgbe_cancel_rx_retry_timers(mucse);\n+\trnpgbe_irq_disable(mucse);\n+\trnpgbe_stop_all_rx_rings(mucse);\n+\trnpgbe_napi_disable_all(mucse);\n+\n+\terr = rnpgbe_send_notify(hw, false, mucse_fw_link_report_en);\n+\tif (err) {\n+\t\tdev_warn(\u0026hw-\u003epdev-\u003edev, \"Send link report to hw failed %d\\n\",\n+\t\t\t err);\n+\t\tdev_warn(\u0026hw-\u003epdev-\u003edev, \"Fw will still report link event\\n\");\n+\t}\n+\n+\terr = rnpgbe_send_notify(hw, false, mucse_fw_portup);\n+\tif (err) {\n+\t\tdev_warn(\u0026hw-\u003epdev-\u003edev, \"Send port down to hw failed %d\\n\",\n+\t\t\t err);\n+\t\tdev_warn(\u0026hw-\u003epdev-\u003edev, \"Port is not truly down\\n\");\n+\t}\n+\trnpgbe_clean_all_tx_rings(mucse);\n+\trnpgbe_clean_all_rx_rings(mucse);\n+\n+\treturn true;\n+}\n+\n+/**\n+ * rnpgbe_up_complete - Final step for port up\n+ * @mucse: pointer to private structure\n+ *\n+ * Return: 0 on success, negative errno if firmware setup fails\n+ **/\n+int rnpgbe_up_complete(struct mucse *mucse)\n+{\n+\tstruct net_device *netdev = mucse-\u003enetdev;\n+\tstruct mucse_hw *hw = \u0026mucse-\u003ehw;\n+\tunsigned long flags;\n+\tint err;\n+\n+\tif (mucse-\u003eflags \u0026 (M_FLAG_MSIX_EN | M_FLAG_MSIX_SINGLE_EN))\n+\t\trnpgbe_configure_msix(mucse);\n+\telse\n+\t\trnpgbe_configure_msi(mucse);\n+\n+\trnpgbe_napi_enable_all(mucse);\n+\t/* Clear stale link state and tell firmware the driver hasn't\n+\t * seen any link yet. Firmware will re-assert LINK_CHANGE_EVT\n+\t * when link_report_en is sent below since RNPGBE_LINK_ST\n+\t * no longer matches the actual (possibly up) link state.\n+\t */\n+\tspin_lock_irqsave(\u0026mucse-\u003elink_lock, flags);\n+\tWRITE_ONCE(hw-\u003elink, false);\n+\tWRITE_ONCE(hw-\u003espeed, 0);\n+\tWRITE_ONCE(hw-\u003eduplex, 0);\n+\tatomic_set(\u0026mucse-\u003elink_pending, 0);\n+\t/* echo fw driver now in default state */\n+\tmucse_hw_wr32(hw, RNPGBE_LINK_ST, M_DEFAULT_ST);\n+\t/* Keep the default snapshot and DOWN state transition atomic to\n+\t * mailbox link-event handling.\n+\t */\n+\tclear_bit(__MUCSE_DOWN, \u0026mucse-\u003estate);\n+\tspin_unlock_irqrestore(\u0026mucse-\u003elink_lock, flags);\n+\trnpgbe_irq_enable(mucse);\n+\tnetif_tx_start_all_queues(netdev);\n+\tfor (int i = 0; i \u003c mucse-\u003enum_rx_queues; i++)\n+\t\tmucse_ring_wr32(mucse-\u003erx_ring[i], RNPGBE_RX_START, 1);\n+\n+\terr = rnpgbe_send_notify(hw, true, mucse_fw_portup);\n+\tif (err) {\n+\t\tdev_err(\u0026hw-\u003epdev-\u003edev,\n+\t\t\t\"Failed to notify firmware that port is up: %d\\n\", err);\n+\t\treturn err;\n+\t}\n+\t/* Firmware checks RNPGBE_LINK_ST (driver's last-received link state)\n+\t * and only asserts LINK_CHANGE_EVT when it differs from the actual link\n+\t * state AND link_report_en is true.\n+\t */\n+\terr = rnpgbe_send_notify(hw, true, mucse_fw_link_report_en);\n+\tif (err) {\n+\t\tdev_err(\u0026hw-\u003epdev-\u003edev,\n+\t\t\t\"Failed to enable firmware link reporting: %d\\n\",\n+\t\t\t err);\n+\t\treturn err;\n+\t}\n+\n+\tqueue_delayed_work(system_percpu_wq, \u0026mucse-\u003eserv_task,\n+\t\t\t   msecs_to_jiffies(500));\n+\n+\treturn 0;\n+}\n+\n+/**\n+ * rnpgbe_free_tx_resources - Free Tx Resources per Queue\n+ * @tx_ring: tx descriptor ring for a specific queue\n+ *\n+ * Free all transmit software resources\n+ **/\n+static void rnpgbe_free_tx_resources(struct mucse_ring *tx_ring)\n+{\n+\tvfree(tx_ring-\u003etx_buffer_info);\n+\ttx_ring-\u003etx_buffer_info = NULL;\n+\t/* if not set, then don't free */\n+\tif (!tx_ring-\u003edesc)\n+\t\treturn;\n+\n+\tdma_free_coherent(tx_ring-\u003edev, tx_ring-\u003esize, tx_ring-\u003edesc,\n+\t\t\t  tx_ring-\u003edma);\n+\ttx_ring-\u003edesc = NULL;\n+}\n+\n+/**\n+ * rnpgbe_setup_tx_resources - allocate Tx resources (Descriptors)\n+ * @tx_ring: tx descriptor ring (for a specific queue) to setup\n+ * @mucse: pointer to private structure\n+ *\n+ * Return: 0 on success, negative on failure\n+ **/\n+static int rnpgbe_setup_tx_resources(struct mucse_ring *tx_ring,\n+\t\t\t\t     struct mucse *mucse)\n+{\n+\tstruct device *dev = tx_ring-\u003edev;\n+\tint size;\n+\n+\tsize = sizeof(struct mucse_tx_buffer) * tx_ring-\u003ecount;\n+\n+\ttx_ring-\u003etx_buffer_info = vzalloc(size);\n+\tif (!tx_ring-\u003etx_buffer_info)\n+\t\tgoto err_return;\n+\t/* round up to nearest 4K */\n+\ttx_ring-\u003esize = tx_ring-\u003ecount * sizeof(struct rnpgbe_tx_desc);\n+\ttx_ring-\u003esize = ALIGN(tx_ring-\u003esize, 4096);\n+\ttx_ring-\u003edesc = dma_alloc_coherent(dev, tx_ring-\u003esize, \u0026tx_ring-\u003edma,\n+\t\t\t\t\t   GFP_KERNEL);\n+\tif (!tx_ring-\u003edesc)\n+\t\tgoto err_free_buffer;\n+\n+\ttx_ring-\u003enext_to_use = 0;\n+\ttx_ring-\u003enext_to_clean = 0;\n+\n+\treturn 0;\n+\n+err_free_buffer:\n+\tvfree(tx_ring-\u003etx_buffer_info);\n+err_return:\n+\ttx_ring-\u003etx_buffer_info = NULL;\n+\treturn -ENOMEM;\n+}\n+\n+/**\n+ * rnpgbe_configure_tx_ring - Configure Tx ring after Reset\n+ * @mucse: pointer to private structure\n+ * @ring: structure containing ring specific data\n+ *\n+ * Configure the Tx descriptor ring after a reset.\n+ **/\n+static void rnpgbe_configure_tx_ring(struct mucse *mucse,\n+\t\t\t\t     struct mucse_ring *ring)\n+{\n+\tstruct mucse_hw *hw = \u0026mucse-\u003ehw;\n+\n+\tmucse_ring_wr32(ring, RNPGBE_TX_BASE_ADDR_LO, (u32)ring-\u003edma);\n+\tmucse_ring_wr32(ring, RNPGBE_TX_BASE_ADDR_HI,\n+\t\t\t(u32)(((u64)ring-\u003edma) \u003e\u003e 32) | (hw-\u003epfvfnum \u003c\u003c 24));\n+\tmucse_ring_wr32(ring, RNPGBE_TX_LEN, ring-\u003ecount);\n+\tring-\u003enext_to_clean = mucse_ring_rd32(ring, RNPGBE_TX_HEAD) %\n+\t\t\t      ring-\u003ecount;\n+\tring-\u003enext_to_use = ring-\u003enext_to_clean;\n+\tring-\u003etail = ring-\u003ering_addr + RNPGBE_TX_TAIL;\n+\twritel(ring-\u003enext_to_use, ring-\u003etail);\n+\tmucse_ring_wr32(ring, RNPGBE_TX_FETCH_CTRL, M_DEFAULT_TX_FETCH);\n+\tmucse_ring_wr32(ring, RNPGBE_TX_INT_TIMER,\n+\t\t\tM_DEFAULT_INT_TIMER * hw-\u003ecycles_per_us);\n+\tmucse_ring_wr32(ring, RNPGBE_TX_INT_PKTCNT, M_DEFAULT_INT_PKTCNT);\n+}\n+\n+/**\n+ * rnpgbe_configure_tx - Configure Transmit Unit after Reset\n+ * @mucse: pointer to private structure\n+ *\n+ * Configure the Tx DMA after a reset.\n+ **/\n+void rnpgbe_configure_tx(struct mucse *mucse)\n+{\n+\tstruct mucse_hw *hw = \u0026mucse-\u003ehw;\n+\tu32 i, dma_axi_ctl;\n+\n+\tfor (i = 0; i \u003c mucse-\u003enum_tx_queues; i++)\n+\t\trnpgbe_stop_tx_ring(mucse-\u003etx_ring[i]);\n+\tif (mucse-\u003enum_tx_queues)\n+\t\trnpgbe_wait_tx_dma_idle(mucse);\n+\n+\tdma_axi_ctl = mucse_hw_rd32(hw, RNPGBE_DMA_AXI_EN);\n+\tdma_axi_ctl \u0026= ~TX_AXI_RW_EN;\n+\tmucse_hw_wr32(hw, RNPGBE_DMA_AXI_EN, dma_axi_ctl);\n+\t/* Flush the posted write before programming the ring registers. */\n+\t(void)mucse_hw_rd32(hw, RNPGBE_DMA_AXI_EN);\n+\n+\tfor (i = 0; i \u003c mucse-\u003enum_tx_queues; i++)\n+\t\trnpgbe_configure_tx_ring(mucse, mucse-\u003etx_ring[i]);\n+\n+\t/* Ensure all ring configuration is visible before enabling TX DMA. */\n+\twmb();\n+\tdma_axi_ctl |= TX_AXI_RW_EN;\n+\tmucse_hw_wr32(hw, RNPGBE_DMA_AXI_EN, dma_axi_ctl);\n+\t/* Ensure TX_AXI_RW_EN is visible before starting the rings. */\n+\t(void)mucse_hw_rd32(hw, RNPGBE_DMA_AXI_EN);\n+\n+\tfor (i = 0; i \u003c mucse-\u003enum_tx_queues; i++)\n+\t\tmucse_ring_wr32(mucse-\u003etx_ring[i], RNPGBE_TX_START, 1);\n+}\n+\n+/**\n+ * rnpgbe_setup_all_tx_resources - allocate all queues Tx resources\n+ * @mucse: pointer to private structure\n+ *\n+ * Allocate memory for tx_ring.\n+ *\n+ * Return: 0 on success, negative on failure\n+ **/\n+int rnpgbe_setup_all_tx_resources(struct mucse *mucse)\n+{\n+\tint i, err = 0;\n+\n+\tfor (i = 0; i \u003c mucse-\u003enum_tx_queues; i++) {\n+\t\terr = rnpgbe_setup_tx_resources(mucse-\u003etx_ring[i], mucse);\n+\t\tif (!err)\n+\t\t\tcontinue;\n+\n+\t\tgoto err_free_res;\n+\t}\n+\n+\treturn 0;\n+err_free_res:\n+\twhile (i--)\n+\t\trnpgbe_free_tx_resources(mucse-\u003etx_ring[i]);\n+\treturn err;\n+}\n+\n+/**\n+ * rnpgbe_free_all_tx_resources - Free Tx Resources for All Queues\n+ * @mucse: pointer to private structure\n+ *\n+ * Free all transmit software resources\n+ **/\n+void rnpgbe_free_all_tx_resources(struct mucse *mucse)\n+{\n+\tfor (int i = 0; i \u003c (mucse-\u003enum_tx_queues); i++)\n+\t\trnpgbe_free_tx_resources(mucse-\u003etx_ring[i]);\n+}\n+\n+static int rnpgbe_tx_map(struct mucse_ring *tx_ring,\n+\t\t\t struct mucse_tx_buffer *first, u32 mac_ip_len,\n+\t\t\t u32 tx_flags)\n+{\n+\tstruct mucse_tx_buffer *tx_buffer;\n+\tstruct rnpgbe_tx_desc *tx_desc;\n+\tstruct skb_shared_info *shinfo;\n+\tunsigned int data_len, size;\n+\tstruct sk_buff *skb;\n+\tskb_frag_t *frag;\n+\tdma_addr_t dma;\n+\tint nr_frags;\n+\tint frag_idx;\n+\t/* Hardware requires this in the upper 8 bits of\n+\t * the descriptor address\n+\t */\n+\tu64 fun_id;\n+\tu16 i;\n+\n+\tskb = first-\u003eskb;\n+\tshinfo = skb_shinfo(skb);\n+\tfun_id = (u64)tx_ring-\u003epfvfnum \u003c\u003c 56;\n+\tnr_frags = shinfo-\u003enr_frags;\n+\ti = tx_ring-\u003enext_to_use;\n+\tfrag_idx = 0;\n+\ttx_desc = M_TX_DESC(tx_ring, i);\n+\tsize = skb_headlen(skb);\n+\tdata_len = skb-\u003edata_len;\n+\n+\tif (size) {\n+\t\tdma = dma_map_single(tx_ring-\u003edev, skb-\u003edata, size,\n+\t\t\t\t     DMA_TO_DEVICE);\n+\t\tfirst-\u003emapped_as_page = false;\n+\t} else if (data_len) {\n+\t\twhile (frag_idx \u003c nr_frags \u0026\u0026\n+\t\t       !skb_frag_size(\u0026shinfo-\u003efrags[frag_idx]))\n+\t\t\tfrag_idx++;\n+\n+\t\tif (frag_idx == nr_frags)\n+\t\t\tgoto err_unmap;\n+\n+\t\tfrag = \u0026shinfo-\u003efrags[frag_idx];\n+\t\tsize = skb_frag_size(frag);\n+\n+\t\tdma = skb_frag_dma_map(tx_ring-\u003edev, frag, 0,\n+\t\t\t\t       size, DMA_TO_DEVICE);\n+\t\tfirst-\u003emapped_as_page = true;\n+\t\tdata_len -= size;\n+\t\tfrag_idx++;\n+\t} else {\n+\t\tgoto err_unmap;\n+\t}\n+\n+\ttx_buffer = first;\n+\n+\tdma_unmap_len_set(tx_buffer, len, 0);\n+\tdma_unmap_addr_set(tx_buffer, dma, 0);\n+\n+\tfor (;; frag_idx++) {\n+\t\tif (dma_mapping_error(tx_ring-\u003edev, dma))\n+\t\t\tgoto err_unmap;\n+\n+\t\t/* record length, and DMA address */\n+\t\tdma_unmap_len_set(tx_buffer, len, size);\n+\t\tdma_unmap_addr_set(tx_buffer, dma, dma);\n+\n+\t\ttx_desc-\u003epkt_addr = cpu_to_le64(dma | fun_id);\n+\n+\t\twhile (unlikely(size \u003e M_MAX_DATA_PER_TXD)) {\n+\t\t\ttx_desc-\u003evlan_cmd_bsz = build_ctob(tx_flags,\n+\t\t\t\t\t\t\t   mac_ip_len,\n+\t\t\t\t\t\t\t   M_MAX_DATA_PER_TXD);\n+\t\t\ti++;\n+\t\t\ttx_desc++;\n+\t\t\tif (i == tx_ring-\u003ecount) {\n+\t\t\t\ttx_desc = M_TX_DESC(tx_ring, 0);\n+\t\t\t\ti = 0;\n+\t\t\t}\n+\n+\t\t\ttx_buffer = \u0026tx_ring-\u003etx_buffer_info[i];\n+\t\t\tdma_unmap_len_set(tx_buffer, len, 0);\n+\t\t\tdma += M_MAX_DATA_PER_TXD;\n+\t\t\tsize -= M_MAX_DATA_PER_TXD;\n+\t\t\ttx_desc-\u003epkt_addr = cpu_to_le64(dma | fun_id);\n+\t\t}\n+\n+\t\tif (likely(!data_len))\n+\t\t\tbreak;\n+\t\ttx_desc-\u003evlan_cmd_bsz = build_ctob(tx_flags, mac_ip_len, size);\n+\t\ti++;\n+\t\ttx_desc++;\n+\t\tif (i == tx_ring-\u003ecount) {\n+\t\t\ttx_desc = M_TX_DESC(tx_ring, 0);\n+\t\t\ti = 0;\n+\t\t}\n+\n+\t\twhile (frag_idx \u003c nr_frags \u0026\u0026\n+\t\t       !skb_frag_size(\u0026shinfo-\u003efrags[frag_idx]))\n+\t\t\tfrag_idx++;\n+\n+\t\tif (frag_idx == nr_frags)\n+\t\t\tgoto err_unmap;\n+\n+\t\tfrag = \u0026shinfo-\u003efrags[frag_idx];\n+\t\tsize = skb_frag_size(frag);\n+\t\tdata_len -= size;\n+\t\tdma = skb_frag_dma_map(tx_ring-\u003edev, frag, 0, size,\n+\t\t\t\t       DMA_TO_DEVICE);\n+\t\ttx_buffer = \u0026tx_ring-\u003etx_buffer_info[i];\n+\t\ttx_buffer-\u003emapped_as_page = true;\n+\t}\n+\n+\t/* write last descriptor with RS and EOP bits */\n+\ttx_desc-\u003evlan_cmd_bsz = build_ctob(tx_flags | M_TXD_CMD_EOP |\n+\t\t\t\t\t   M_TXD_CMD_RS,\n+\t\t\t\t\t   mac_ip_len, size);\n+\n+\t/* Force memory writes to complete before letting h/w know there\n+\t * are new descriptors to fetch.  (Only applicable for weak-ordered\n+\t * memory model archs, such as IA-64).\n+\t *\n+\t * We also need this memory barrier to make certain all of the\n+\t * status bits have been updated before next_to_watch is written.\n+\t */\n+\tdma_wmb();\n+\t/* set next_to_watch value indicating a packet is present */\n+\tfirst-\u003enext_to_watch = tx_desc;\n+\ti++;\n+\tif (i == tx_ring-\u003ecount)\n+\t\ti = 0;\n+\ttx_ring-\u003enext_to_use = i;\n+\tskb_tx_timestamp(skb);\n+\tnetdev_tx_sent_queue(txring_txq(tx_ring), first-\u003ebytecount);\n+\t/* notify HW of packet */\n+\twritel(i, tx_ring-\u003etail);\n+\n+\treturn 0;\n+err_unmap:\n+\tfor (;;) {\n+\t\ttx_buffer = \u0026tx_ring-\u003etx_buffer_info[i];\n+\t\tif (dma_unmap_len(tx_buffer, len)) {\n+\t\t\tif (tx_buffer-\u003emapped_as_page) {\n+\t\t\t\tdma_unmap_page(tx_ring-\u003edev,\n+\t\t\t\t\t       dma_unmap_addr(tx_buffer, dma),\n+\t\t\t\t\t       dma_unmap_len(tx_buffer, len),\n+\t\t\t\t\t       DMA_TO_DEVICE);\n+\t\t\t} else {\n+\t\t\t\tdma_unmap_single(tx_ring-\u003edev,\n+\t\t\t\t\t\t dma_unmap_addr(tx_buffer, dma),\n+\t\t\t\t\t\t dma_unmap_len(tx_buffer, len),\n+\t\t\t\t\t\t DMA_TO_DEVICE);\n+\t\t\t}\n+\t\t}\n+\t\tdma_unmap_len_set(tx_buffer, len, 0);\n+\t\tdma_unmap_addr_set(tx_buffer, dma, 0);\n+\t\tif (tx_buffer == first)\n+\t\t\tbreak;\n+\t\tif (i == 0)\n+\t\t\ti += tx_ring-\u003ecount;\n+\t\ti--;\n+\t}\n+\tdev_kfree_skb_any(first-\u003eskb);\n+\tfirst-\u003eskb = NULL;\n+\ttx_ring-\u003enext_to_use = i;\n+\n+\treturn -ENOMEM;\n+}\n+\n+netdev_tx_t rnpgbe_xmit_frame_ring(struct sk_buff *skb,\n+\t\t\t\t   struct mucse_ring *tx_ring)\n+{\n+\tu16 count;\n+\t/* hw requires non-zero mac_ip_len even without offload;\n+\t * Will be updated per-packet when offload is added\n+\t */\n+\tu32 mac_ip_len = M_DEFAULT_MAC_IP_LEN;\n+\tstruct mucse_tx_buffer *first;\n+\tu32 tx_flags = 0;\n+\tunsigned short f;\n+\n+\tcount = TXD_USE_COUNT(skb_headlen(skb));\n+\tfor (f = 0; f \u003c skb_shinfo(skb)-\u003enr_frags; f++) {\n+\t\tskb_frag_t *frag_temp = \u0026skb_shinfo(skb)-\u003efrags[f];\n+\n+\t\tcount += TXD_USE_COUNT(skb_frag_size(frag_temp));\n+\t}\n+\n+\tif (!netif_txq_maybe_stop(txring_txq(tx_ring),\n+\t\t\t\t  mucse_desc_unused(tx_ring),\n+\t\t\t\t  count + RESV_DESC_NEEDED,\n+\t\t\t\t  count + RESV_DESC_NEEDED))\n+\t\treturn NETDEV_TX_BUSY;\n+\n+\t/* record the location of the first descriptor for this packet */\n+\tfirst = \u0026tx_ring-\u003etx_buffer_info[tx_ring-\u003enext_to_use];\n+\tfirst-\u003eskb = skb;\n+\tfirst-\u003ebytecount = skb-\u003elen;\n+\tfirst-\u003egso_segs = 1;\n+\n+\tif (rnpgbe_tx_map(tx_ring, first, mac_ip_len, tx_flags)) {\n+\t\tatomic64_inc(\u0026tx_ring-\u003estats.dropped);\n+\n+\t\tgoto out;\n+\t}\n+\n+\t/* NETDEV_TX_BUSY is expensive. So stop advancing the TX queue. */\n+\tnetif_txq_maybe_stop(txring_txq(tx_ring),\n+\t\t\t     mucse_desc_unused(tx_ring),\n+\t\t\t     DESC_NEEDED, DESC_NEEDED);\n+out:\n+\treturn NETDEV_TX_OK;\n+}\n+\n+/**\n+ * rnpgbe_get_stats64 - Get stats for this netdev\n+ * @netdev: network interface device structure\n+ * @stats: stats data\n+ **/\n+void rnpgbe_get_stats64(struct net_device *netdev,\n+\t\t\tstruct rtnl_link_stats64 *stats)\n+{\n+\tstruct mucse *mucse = netdev_priv(netdev);\n+\tint i;\n+\n+\trcu_read_lock();\n+\tfor (i = 0; i \u003c mucse-\u003enum_tx_queues; i++) {\n+\t\tstruct mucse_ring *ring = READ_ONCE(mucse-\u003etx_ring[i]);\n+\t\tu64 bytes, packets, dropped;\n+\t\tunsigned int start;\n+\n+\t\tif (ring) {\n+\t\t\tdo {\n+\t\t\t\tstart = u64_stats_fetch_begin(\u0026ring-\u003esyncp);\n+\t\t\t\tpackets = ring-\u003estats.packets;\n+\t\t\t\tbytes = ring-\u003estats.bytes;\n+\t\t\t} while (u64_stats_fetch_retry(\u0026ring-\u003esyncp, start));\n+\t\t\tdropped = atomic64_read(\u0026ring-\u003estats.dropped);\n+\n+\t\t\tstats-\u003etx_packets += packets;\n+\t\t\tstats-\u003etx_dropped += dropped;\n+\t\t\tstats-\u003etx_bytes += bytes;\n+\t\t}\n+\t}\n+\n+\tfor (i = 0; i \u003c mucse-\u003enum_rx_queues; i++) {\n+\t\tstruct mucse_ring *ring = READ_ONCE(mucse-\u003erx_ring[i]);\n+\t\tu64 bytes, packets, dropped;\n+\t\tunsigned int start;\n+\n+\t\tif (ring) {\n+\t\t\tdo {\n+\t\t\t\tstart = u64_stats_fetch_begin(\u0026ring-\u003esyncp);\n+\t\t\t\tpackets = ring-\u003estats.packets;\n+\t\t\t\tbytes = ring-\u003estats.bytes;\n+\t\t\t} while (u64_stats_fetch_retry(\u0026ring-\u003esyncp, start));\n+\t\t\tdropped = atomic64_read(\u0026ring-\u003estats.dropped);\n+\n+\t\t\tstats-\u003erx_packets += packets;\n+\t\t\tstats-\u003erx_dropped += dropped;\n+\t\t\tstats-\u003erx_bytes += bytes;\n+\t\t}\n+\t}\n+\trcu_read_unlock();\n+}\n+\n+static int mucse_alloc_page_pool(struct mucse_ring *rx_ring)\n+{\n+\tint ret = 0;\n+\n+\tstruct page_pool_params pp_params = {\n+\t\t.flags = PP_FLAG_DMA_MAP | PP_FLAG_DMA_SYNC_DEV,\n+\t\t.order = 0,\n+\t\t.pool_size = rx_ring-\u003ecount,\n+\t\t.nid = dev_to_node(rx_ring-\u003edev),\n+\t\t.dev = rx_ring-\u003edev,\n+\t\t.dma_dir = DMA_FROM_DEVICE,\n+\t\t.offset = 0,\n+\t\t.max_len = PAGE_SIZE,\n+\t};\n+\n+\trx_ring-\u003epage_pool = page_pool_create(\u0026pp_params);\n+\tif (IS_ERR(rx_ring-\u003epage_pool)) {\n+\t\tret = PTR_ERR(rx_ring-\u003epage_pool);\n+\t\trx_ring-\u003epage_pool = NULL;\n+\t}\n+\n+\treturn ret;\n+}\n+\n+/**\n+ * rnpgbe_setup_rx_resources - allocate Rx resources (Descriptors)\n+ * @rx_ring:    rx descriptor ring (for a specific queue) to setup\n+ * @mucse: pointer to private structure\n+ *\n+ * Return: 0 on success, negative on failure\n+ **/\n+static int rnpgbe_setup_rx_resources(struct mucse_ring *rx_ring,\n+\t\t\t\t     struct mucse *mucse)\n+{\n+\tstruct device *dev = rx_ring-\u003edev;\n+\tint size;\n+\n+\tsize = sizeof(struct mucse_rx_buffer) * rx_ring-\u003ecount;\n+\n+\trx_ring-\u003erx_buffer_info = vzalloc(size);\n+\n+\tif (!rx_ring-\u003erx_buffer_info)\n+\t\tgoto err_return;\n+\t/* Round up to nearest 4K */\n+\trx_ring-\u003esize = rx_ring-\u003ecount * sizeof(union rnpgbe_rx_desc);\n+\trx_ring-\u003esize = ALIGN(rx_ring-\u003esize, 4096);\n+\trx_ring-\u003edesc = dma_alloc_coherent(dev, rx_ring-\u003esize, \u0026rx_ring-\u003edma,\n+\t\t\t\t\t   GFP_KERNEL);\n+\tif (!rx_ring-\u003edesc)\n+\t\tgoto err_free_buffer;\n+\n+\trx_ring-\u003enext_to_clean = 0;\n+\trx_ring-\u003enext_to_use = 0;\n+\n+\tif (mucse_alloc_page_pool(rx_ring))\n+\t\tgoto err_free_desc;\n+\n+\treturn 0;\n+err_free_desc:\n+\tdma_free_coherent(dev, rx_ring-\u003esize, rx_ring-\u003edesc,\n+\t\t\t  rx_ring-\u003edma);\n+\trx_ring-\u003edesc = NULL;\n+err_free_buffer:\n+\tvfree(rx_ring-\u003erx_buffer_info);\n+err_return:\n+\trx_ring-\u003erx_buffer_info = NULL;\n+\treturn -ENOMEM;\n+}\n+\n+/**\n+ * rnpgbe_clean_rx_ring - Free Rx Buffers per Queue\n+ * @rx_ring: ring to free buffers from\n+ **/\n+void rnpgbe_clean_rx_ring(struct mucse_ring *rx_ring)\n+{\n+\tstruct mucse_rx_buffer *rx_buffer;\n+\tu16 i;\n+\n+\t/* ring already cleared, nothing to do */\n+\tif (!rx_ring-\u003erx_buffer_info)\n+\t\treturn;\n+\t/* Free all the Rx ring sk_buffs */\n+\tfor (i = 0; i \u003c rx_ring-\u003ecount; i++) {\n+\t\trx_buffer = \u0026rx_ring-\u003erx_buffer_info[i];\n+\n+\t\tif (rx_buffer-\u003eskb) {\n+\t\t\tstruct sk_buff *skb = rx_buffer-\u003eskb;\n+\n+\t\t\tdev_kfree_skb(skb);\n+\t\t\trx_buffer-\u003eskb = NULL;\n+\t\t}\n+\n+\t\tif (rx_buffer-\u003epage) {\n+\t\t\tpage_pool_put_full_page(rx_ring-\u003epage_pool,\n+\t\t\t\t\t\trx_buffer-\u003epage, false);\n+\t\t\trx_buffer-\u003epage = NULL;\n+\t\t}\n+\t}\n+\n+\trx_ring-\u003enext_to_clean = 0;\n+\trx_ring-\u003enext_to_use = 0;\n+}\n+\n+/**\n+ * rnpgbe_free_rx_resources - Free Rx Resources\n+ * @rx_ring: ring to clean the resources from\n+ *\n+ * Free all receive software resources\n+ **/\n+static void rnpgbe_free_rx_resources(struct mucse_ring *rx_ring)\n+{\n+\tvfree(rx_ring-\u003erx_buffer_info);\n+\trx_ring-\u003erx_buffer_info = NULL;\n+\t/* if not set, then don't free */\n+\tif (!rx_ring-\u003edesc)\n+\t\treturn;\n+\n+\tdma_free_coherent(rx_ring-\u003edev, rx_ring-\u003esize, rx_ring-\u003edesc,\n+\t\t\t  rx_ring-\u003edma);\n+\trx_ring-\u003edesc = NULL;\n+\tif (rx_ring-\u003epage_pool) {\n+\t\tpage_pool_destroy(rx_ring-\u003epage_pool);\n+\t\trx_ring-\u003epage_pool = NULL;\n+\t}\n+}\n+\n+/**\n+ * rnpgbe_setup_all_rx_resources - allocate all queues Rx resources\n+ * @mucse: pointer to private structure\n+ *\n+ * Return: 0 on success, negative on failure\n+ **/\n+int rnpgbe_setup_all_rx_resources(struct mucse *mucse)\n+{\n+\tint i, err = 0;\n+\n+\tfor (i = 0; i \u003c mucse-\u003enum_rx_queues; i++) {\n+\t\terr = rnpgbe_setup_rx_resources(mucse-\u003erx_ring[i], mucse);\n+\t\tif (!err)\n+\t\t\tcontinue;\n+\n+\t\tgoto err_setup_rx;\n+\t}\n+\n+\treturn 0;\n+err_setup_rx:\n+\twhile (i--)\n+\t\trnpgbe_free_rx_resources(mucse-\u003erx_ring[i]);\n+\treturn err;\n+}\n+\n+/**\n+ * rnpgbe_free_all_rx_resources - Free Rx Resources for All Queues\n+ * @mucse: pointer to private structure\n+ *\n+ * Free all receive software resources\n+ **/\n+void rnpgbe_free_all_rx_resources(struct mucse *mucse)\n+{\n+\tint i;\n+\n+\tfor (i = 0; i \u003c mucse-\u003enum_rx_queues; i++)\n+\t\trnpgbe_free_rx_resources(mucse-\u003erx_ring[i]);\n+}\n+\n+/**\n+ * rnpgbe_configure_rx_ring - Configure Rx ring info to hw\n+ * @mucse: pointer to private structure\n+ * @ring: structure containing ring specific data\n+ *\n+ * Configure the Rx descriptor ring after a reset.\n+ **/\n+static void rnpgbe_configure_rx_ring(struct mucse *mucse,\n+\t\t\t\t     struct mucse_ring *ring)\n+{\n+\tstruct mucse_hw *hw = \u0026mucse-\u003ehw;\n+\n+\t/* Stop hw. hardware design guarantees:\n+\t * - No new descriptors will be fetched after RX_START=0\n+\t * - No DMA will be initiated for already-fetched descriptors\n+\t */\n+\tmucse_ring_wr32(ring, RNPGBE_RX_START, 0);\n+\t/* Flush posted write to ensure hardware sees RX_START=0 */\n+\t(void)mucse_ring_rd32(ring, RNPGBE_RX_START);\n+\t/* Wait for in-flight DMA to quiesce */\n+\tusleep_range(300, 500);\n+\t/* Set the descriptor registers */\n+\tmucse_ring_wr32(ring, RNPGBE_RX_BASE_ADDR_LO, (u32)ring-\u003edma);\n+\tmucse_ring_wr32(ring, RNPGBE_RX_BASE_ADDR_HI,\n+\t\t\t(u32)((u64)ring-\u003edma \u003e\u003e 32) | (hw-\u003epfvfnum \u003c\u003c 24));\n+\tmucse_ring_wr32(ring, RNPGBE_RX_LEN, ring-\u003ecount);\n+\tring-\u003etail = ring-\u003ering_addr + RNPGBE_RX_TAIL;\n+\tring-\u003enext_to_clean = mucse_ring_rd32(ring, RNPGBE_RX_HEAD) %\n+\t\t\t      ring-\u003ecount;\n+\tring-\u003enext_to_use = ring-\u003enext_to_clean;\n+\tring-\u003edrop_status = false;\n+\tmucse_ring_wr32(ring, RNPGBE_RX_SG_LEN, M_DEFAULT_SG);\n+\tmucse_ring_wr32(ring, RNPGBE_RX_FETCH, M_DEFAULT_RX_FETCH);\n+\tmucse_ring_wr32(ring, RNPGBE_RX_TIMEOUT_TH, 0);\n+\tmucse_ring_wr32(ring, RNPGBE_RX_INT_TIMER,\n+\t\t\tM_DEFAULT_INT_TIMER_R * hw-\u003ecycles_per_us);\n+\tmucse_ring_wr32(ring, RNPGBE_RX_INT_PKTCNT, M_DEFAULT_RX_INT_PKTCNT);\n+\tif (rnpgbe_alloc_rx_buffers(ring, mucse_desc_unused_rx(ring))) {\n+\t\tmod_timer(\u0026ring-\u003eq_vector-\u003erx_alloc_timer,\n+\t\t\t  jiffies + msecs_to_jiffies(500));\n+\t}\n+}\n+\n+/**\n+ * rnpgbe_configure_rx - Configure Receive Unit after Reset\n+ * @mucse: pointer to private structure\n+ *\n+ * Configure the Rx unit after a reset.\n+ **/\n+void rnpgbe_configure_rx(struct mucse *mucse)\n+{\n+\tstruct mucse_hw *hw = \u0026mucse-\u003ehw;\n+\tu32 dma_axi_ctl;\n+\n+\tfor (int i = 0; i \u003c mucse-\u003enum_rx_queues; i++)\n+\t\trnpgbe_configure_rx_ring(mucse, mucse-\u003erx_ring[i]);\n+\n+\tdma_axi_ctl = mucse_hw_rd32(hw, RNPGBE_DMA_AXI_EN);\n+\tdma_axi_ctl |= RX_AXI_RW_EN;\n+\tmucse_hw_wr32(hw, RNPGBE_DMA_AXI_EN, dma_axi_ctl);\n+}\n+\n+/**\n+ * rnpgbe_process_link_event - Consume a pending link event\n+ * @mucse: pointer to the device private structure\n+ * @link: link status snapshot\n+ * @speed: link speed snapshot\n+ * @duplex: link duplex snapshot\n+ *\n+ * Return: true if a link event was consumed, false otherwise\n+ **/\n+static bool rnpgbe_process_link_event(struct mucse *mucse, bool *link,\n+\t\t\t\t      int *speed, u8 *duplex)\n+{\n+\tstruct mucse_hw *hw = \u0026mucse-\u003ehw;\n+\tunsigned long flags;\n+\n+\tif (!atomic_xchg(\u0026mucse-\u003elink_pending, 0))\n+\t\treturn false;\n+\n+\tspin_lock_irqsave(\u0026mucse-\u003elink_lock, flags);\n+\t*link = hw-\u003elink;\n+\t*speed = hw-\u003espeed;\n+\t*duplex = hw-\u003eduplex;\n+\tspin_unlock_irqrestore(\u0026mucse-\u003elink_lock, flags);\n+\n+\treturn true;\n+}\n+\n+/**\n+ * rnpgbe_link_is_up - Update netif_carrier status and\n+ * print link up message\n+ * @mucse: pointer to the device private structure\n+ * @speed: link speed snapshot\n+ * @duplex: link duplex snapshot\n+ **/\n+static void rnpgbe_link_is_up(struct mucse *mucse, int speed, u8 duplex)\n+{\n+\tstruct net_device *netdev = mucse-\u003enetdev;\n+\tstruct mucse_hw *hw = \u0026mucse-\u003ehw;\n+\n+\t/* Only continue if link was previously down */\n+\tif (netif_carrier_ok(netdev))\n+\t\treturn;\n+\n+\tnetdev_info(netdev, \"NIC Link is Up %d Mbps, %s Duplex\\n\",\n+\t\t    speed, duplex ? \"Full\" : \"Half\");\n+\trnpgbe_set_link(hw, true);\n+\tnetif_carrier_on(netdev);\n+}\n+\n+/**\n+ * rnpgbe_link_is_down - Update netif_carrier status and\n+ * print link down message\n+ * @mucse: pointer to the private structure\n+ **/\n+static void rnpgbe_link_is_down(struct mucse *mucse)\n+{\n+\tstruct net_device *netdev = mucse-\u003enetdev;\n+\tstruct mucse_hw *hw = \u0026mucse-\u003ehw;\n+\n+\t/* Only continue if link was up previously */\n+\tif (!netif_carrier_ok(netdev))\n+\t\treturn;\n+\tnetdev_info(netdev, \"NIC Link is Down\\n\");\n+\trnpgbe_set_link(hw, false);\n+\tnetif_carrier_off(netdev);\n+}\n+\n+/**\n+ * rnpgbe_process_link_subtask - Process a link-state update\n+ * @mucse: pointer to the device private structure\n+ **/\n+static void rnpgbe_process_link_subtask(struct mucse *mucse)\n+{\n+\tbool link;\n+\tint speed;\n+\tu8 duplex;\n+\n+\t/* if interface is down do nothing */\n+\tif (test_bit(__MUCSE_DOWN, \u0026mucse-\u003estate))\n+\t\treturn;\n+\n+\tif (!rnpgbe_process_link_event(mucse, \u0026link, \u0026speed, \u0026duplex))\n+\t\treturn;\n+\n+\tif (link)\n+\t\trnpgbe_link_is_up(mucse, speed, duplex);\n+\telse\n+\t\trnpgbe_link_is_down(mucse);\n+}\n+\n+/**\n+ * rnpgbe_service_task - Manages and runs subtasks\n+ * @work: pointer to work_struct containing our data\n+ **/\n+void rnpgbe_service_task(struct work_struct *work)\n+{\n+\tstruct mucse *mucse = container_of(work, struct mucse, serv_task.work);\n+\n+\tif (test_bit(__MUCSE_DOWN, \u0026mucse-\u003estate))\n+\t\treturn;\n+\n+\trnpgbe_process_link_subtask(mucse);\n+\n+\tif (!test_bit(__MUCSE_DOWN, \u0026mucse-\u003estate))\n+\t\tqueue_delayed_work(system_percpu_wq, \u0026mucse-\u003eserv_task,\n+\t\t\t\t   msecs_to_jiffies(500));\n+}\ndiff --git a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.h b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.h\nnew file mode 100644\nindex 0000000000000..5b93c1183c524\n--- /dev/null\n+++ b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.h\n@@ -0,0 +1,87 @@\n+/* SPDX-License-Identifier: GPL-2.0 */\n+/* Copyright(c) 2020 - 2025 Mucse Corporation. */\n+\n+#ifndef _RNPGBE_LIB_H\n+#define _RNPGBE_LIB_H\n+\n+struct mucse;\n+struct mucse_hw;\n+struct mucse_ring;\n+\n+#define RING_OFFSET(n)            (0x1000 + 0x100 * (n))\n+#define RNPGBE_RX_START           0x10\n+#define RNPGBE_TX_START           0x18\n+#define RNPGBE_DMA_INT_MASK       0x24\n+#define TX_INT_MASK               BIT(1)\n+#define RX_INT_MASK               BIT(0)\n+#define INT_VALID                 (BIT(16) | BIT(17))\n+#define RNPGBE_DMA_INT_TRIG       0x2c /* lost-interrupt recovery trigger */\n+#define RNPGBE_RX_BASE_ADDR_HI    0x30\n+#define RNPGBE_RX_BASE_ADDR_LO    0x34\n+#define RNPGBE_RX_LEN             0x38\n+#define RNPGBE_RX_HEAD            0x3c\n+#define RNPGBE_RX_TAIL            0x40\n+#define M_DEFAULT_RX_FETCH        0x100020\n+#define RNPGBE_RX_FETCH           0x44\n+#define M_DEFAULT_INT_TIMER_R     30\n+#define RNPGBE_RX_INT_TIMER       0x48\n+#define M_DEFAULT_RX_INT_PKTCNT   64\n+#define RNPGBE_RX_INT_PKTCNT      0x4c\n+#define RNPGBE_RX_ARB_DEF_LVL     0x50\n+#define RNPGBE_RX_TIMEOUT_TH      0x54\n+#define M_DEFAULT_SG              96 /* unit 16b, 1536 bytes */\n+#define RNPGBE_RX_SG_LEN          0x58\n+#define RNPGBE_TX_BASE_ADDR_HI    0x60\n+#define RNPGBE_TX_BASE_ADDR_LO    0x64\n+#define RNPGBE_TX_LEN             0x68\n+#define RNPGBE_TX_HEAD            0x6c\n+#define RNPGBE_TX_TAIL            0x70\n+#define M_DEFAULT_TX_FETCH        0x80008\n+#define RNPGBE_TX_FETCH_CTRL      0x74\n+#define M_DEFAULT_INT_TIMER       100\n+#define RNPGBE_TX_INT_TIMER       0x78\n+#define M_DEFAULT_INT_PKTCNT      48\n+#define RNPGBE_TX_INT_PKTCNT      0x7c\n+/* |  31:24   | .... |    15:8   |    7:0    | */\n+/* |  pfvfnum |      | tx vector | rx vector | */\n+#define RING_VECTOR(n)            (0x04 * (n))\n+\n+#define M_MAX_TXD_PWR             12\n+#define M_MAX_DATA_PER_TXD        (0x1 \u003c\u003c M_MAX_TXD_PWR)\n+#define TXD_USE_COUNT(S)          DIV_ROUND_UP((S), M_MAX_DATA_PER_TXD)\n+#define DESC_NEEDED               (MAX_SKB_FRAGS + 4)\n+/* 2 desc gap to keep tail from touching head */\n+/* 1 desc for context descriptor */\n+#define RESV_DESC_NEEDED          3\n+#define RNPGBE_SKB_PAD            (NET_SKB_PAD + NET_IP_ALIGN)\n+#define M_RX_BUFFER_WRITE         16\n+\n+/* Hardware requires this to be nonzero */\n+#define M_DEFAULT_MAC_IP_LEN      20\n+#define mucse_for_each_ring(pos, head)\\\n+\tfor (typeof((head).ring) __pos = (head).ring;\\\n+\t     __pos ? ({ pos = __pos; 1; }) : 0;\\\n+\t     __pos = __pos-\u003enext)\n+int rnpgbe_init_interrupt_scheme(struct mucse *mucse);\n+void rnpgbe_clear_interrupt_scheme(struct mucse *mucse);\n+int rnpgbe_request_mbx_irq(struct mucse *mucse);\n+void rnpgbe_free_mbx_irq(struct mucse *mucse);\n+int rnpgbe_request_irq(struct mucse *mucse);\n+void rnpgbe_free_irq(struct mucse *mucse);\n+void rnpgbe_irq_disable(struct mucse *mucse);\n+bool rnpgbe_down(struct mucse *mucse);\n+int rnpgbe_up_complete(struct mucse *mucse);\n+void rnpgbe_configure_tx(struct mucse *mucse);\n+void rnpgbe_configure_rx(struct mucse *mucse);\n+void rnpgbe_clean_all_tx_rings(struct mucse *mucse);\n+int rnpgbe_setup_all_tx_resources(struct mucse *mucse);\n+void rnpgbe_free_all_tx_resources(struct mucse *mucse);\n+netdev_tx_t rnpgbe_xmit_frame_ring(struct sk_buff *skb,\n+\t\t\t\t   struct mucse_ring *tx_ring);\n+void rnpgbe_get_stats64(struct net_device *netdev,\n+\t\t\tstruct rtnl_link_stats64 *stats);\n+void rnpgbe_clean_rx_ring(struct mucse_ring *rx_ring);\n+int rnpgbe_setup_all_rx_resources(struct mucse *mucse);\n+void rnpgbe_free_all_rx_resources(struct mucse *mucse);\n+void rnpgbe_service_task(struct work_struct *work);\n+#endif\ndiff --git a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_main.c b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_main.c\nindex 70a2b0082ba86..fcad8a16ef26b 100644\n--- a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_main.c\n+++ b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_main.c\n@@ -2,11 +2,13 @@\n /* Copyright(c) 2020 - 2025 Mucse Corporation. */\n \n #include \u003clinux/pci.h\u003e\n+#include \u003clinux/skbuff.h\u003e\n #include \u003cnet/rtnetlink.h\u003e\n #include \u003clinux/etherdevice.h\u003e\n \n #include \"rnpgbe.h\"\n #include \"rnpgbe_hw.h\"\n+#include \"rnpgbe_lib.h\"\n #include \"rnpgbe_mbx_fw.h\"\n \n static const char rnpgbe_driver_name[] = \"rnpgbe\";\n@@ -25,6 +27,18 @@ static struct pci_device_id rnpgbe_pci_tbl[] = {\n \t{ },\n };\n \n+/**\n+ * rnpgbe_configure - Configure the hardware\n+ * @mucse: pointer to private structure\n+ *\n+ * rnpgbe_configure configure mac, tx, rx regs to hw\n+ **/\n+static void rnpgbe_configure(struct mucse *mucse)\n+{\n+\trnpgbe_configure_tx(mucse);\n+\trnpgbe_configure_rx(mucse);\n+}\n+\n /**\n  * rnpgbe_open - Called when a network interface is made active\n  * @netdev: network interface device structure\n@@ -32,11 +46,47 @@ static struct pci_device_id rnpgbe_pci_tbl[] = {\n  * The open entry point is called when a network interface is made\n  * active by the system (IFF_UP).\n  *\n- * Return: 0\n+ * Return: 0 on success, negative value on failure\n  **/\n static int rnpgbe_open(struct net_device *netdev)\n {\n+\tstruct mucse *mucse = netdev_priv(netdev);\n+\tint err;\n+\n+\tnetif_carrier_off(netdev);\n+\terr = rnpgbe_request_irq(mucse);\n+\tif (err)\n+\t\treturn err;\n+\n+\terr = netif_set_real_num_queues(netdev, mucse-\u003enum_tx_queues,\n+\t\t\t\t\tmucse-\u003enum_rx_queues);\n+\tif (err)\n+\t\tgoto err_free_irqs;\n+\n+\terr = rnpgbe_setup_all_tx_resources(mucse);\n+\tif (err)\n+\t\tgoto err_free_irqs;\n+\terr = rnpgbe_setup_all_rx_resources(mucse);\n+\tif (err)\n+\t\tgoto err_free_tx;\n+\n+\trnpgbe_configure(mucse);\n+\terr = rnpgbe_up_complete(mucse);\n+\tif (err)\n+\t\tgoto err_down;\n+\n \treturn 0;\n+err_down:\n+\trnpgbe_down(mucse);\n+\trnpgbe_free_all_rx_resources(mucse);\n+\trnpgbe_free_all_tx_resources(mucse);\n+\tgoto err_free_irqs;\n+err_free_tx:\n+\trnpgbe_clean_all_tx_rings(mucse);\n+\trnpgbe_free_all_tx_resources(mucse);\n+err_free_irqs:\n+\trnpgbe_free_irq(mucse);\n+\treturn err;\n }\n \n /**\n@@ -50,6 +100,15 @@ static int rnpgbe_open(struct net_device *netdev)\n  **/\n static int rnpgbe_close(struct net_device *netdev)\n {\n+\tstruct mucse *mucse = netdev_priv(netdev);\n+\n+\tif (!rnpgbe_down(mucse))\n+\t\treturn 0;\n+\n+\trnpgbe_free_irq(mucse);\n+\trnpgbe_free_all_tx_resources(mucse);\n+\trnpgbe_free_all_rx_resources(mucse);\n+\n \treturn 0;\n }\n \n@@ -58,25 +117,38 @@ static int rnpgbe_close(struct net_device *netdev)\n  * @skb: skb structure to be sent\n  * @netdev: network interface device structure\n  *\n- * Return: NETDEV_TX_OK\n+ * Return: NETDEV_TX_OK or NETDEV_TX_BUSY when insufficient descriptors\n  **/\n static netdev_tx_t rnpgbe_xmit_frame(struct sk_buff *skb,\n \t\t\t\t     struct net_device *netdev)\n {\n \tstruct mucse *mucse = netdev_priv(netdev);\n+\tstruct mucse_ring *tx_ring;\n+\n+\ttx_ring = mucse-\u003etx_ring[skb_get_queue_mapping(skb)];\n \n-\tdev_kfree_skb_any(skb);\n-\tmucse-\u003estats.tx_dropped++;\n+\tif (unlikely(skb_put_padto(skb, RNPGBE_TX_MIN_PKT_LEN))) {\n+\t\tatomic64_inc(\u0026tx_ring-\u003estats.dropped);\n+\t\treturn NETDEV_TX_OK;\n+\t}\n \n-\treturn NETDEV_TX_OK;\n+\treturn rnpgbe_xmit_frame_ring(skb, tx_ring);\n }\n \n static const struct net_device_ops rnpgbe_netdev_ops = {\n \t.ndo_open       = rnpgbe_open,\n \t.ndo_stop       = rnpgbe_close,\n \t.ndo_start_xmit = rnpgbe_xmit_frame,\n+\t.ndo_get_stats64 = rnpgbe_get_stats64,\n };\n \n+static void rnpgbe_sw_init(struct mucse *mucse)\n+{\n+\tmucse-\u003etx_ring_item_count = M_DEFAULT_TXD;\n+\tmucse-\u003erx_ring_item_count = M_DEFAULT_RXD;\n+\tmucse-\u003etx_work_limit = M_DEFAULT_TX_WORK;\n+}\n+\n /**\n  * rnpgbe_add_adapter - Add netdev for this pci_dev\n  * @pdev: PCI device information structure\n@@ -106,6 +178,7 @@ static int rnpgbe_add_adapter(struct pci_dev *pdev,\n \tmucse = netdev_priv(netdev);\n \tmucse-\u003enetdev = netdev;\n \tmucse-\u003epdev = pdev;\n+\tset_bit(__MUCSE_DOWN, \u0026mucse-\u003estate);\n \tpci_set_drvdata(pdev, mucse);\n \n \thw = \u0026mucse-\u003ehw;\n@@ -125,6 +198,7 @@ static int rnpgbe_add_adapter(struct pci_dev *pdev,\n \t\tdev_err(\u0026pdev-\u003edev, \"Init hw err %d\\n\", err);\n \t\tgoto err_free_net;\n \t}\n+\n \t/* Step 1: Send power-up notification to firmware (no response expected)\n \t * This informs firmware to initialize hardware power state, but\n \t * firmware only acknowledges receipt without returning data. Must be\n@@ -149,6 +223,7 @@ static int rnpgbe_add_adapter(struct pci_dev *pdev,\n \t}\n \n \tnetdev-\u003enetdev_ops = \u0026rnpgbe_netdev_ops;\n+\trnpgbe_sw_init(mucse);\n \terr = rnpgbe_reset_hw(hw);\n \tif (err) {\n \t\tdev_err(\u0026pdev-\u003edev, \"Hw reset failed %d\\n\", err);\n@@ -166,11 +241,40 @@ static int rnpgbe_add_adapter(struct pci_dev *pdev,\n \t\tgoto err_powerdown;\n \t}\n \n+\tINIT_DELAYED_WORK(\u0026mucse-\u003eserv_task, rnpgbe_service_task);\n+\tspin_lock_init(\u0026mucse-\u003elink_lock);\n+\tatomic_set(\u0026mucse-\u003elink_pending, 0);\n+\n+\terr = rnpgbe_init_interrupt_scheme(mucse);\n+\tif (err) {\n+\t\tdev_err(\u0026pdev-\u003edev, \"init interrupt failed %d\\n\", err);\n+\t\tgoto err_powerdown;\n+\t}\n+\n+\terr = rnpgbe_request_mbx_irq(mucse);\n+\tif (err) {\n+\t\tdev_err(\u0026pdev-\u003edev, \"register mbx irq failed %d\\n\", err);\n+\t\tgoto err_clear_interrupt;\n+\t}\n+\n+\tnetdev-\u003efeatures |= NETIF_F_SG;\n+\tnetdev-\u003ehw_features |= NETIF_F_SG;\n+\tif (dma_get_mask(\u0026pdev-\u003edev) \u003e DMA_BIT_MASK(32)) {\n+\t\tnetdev-\u003efeatures |= NETIF_F_HIGHDMA;\n+\t\tnetdev-\u003ehw_features |= NETIF_F_HIGHDMA;\n+\t}\n+\n+\tnetif_carrier_off(netdev);\n \terr = register_netdev(netdev);\n \tif (err)\n-\t\tgoto err_powerdown;\n+\t\tgoto err_remove_mbx;\n \n \treturn 0;\n+\n+err_remove_mbx:\n+\trnpgbe_free_mbx_irq(mucse);\n+err_clear_interrupt:\n+\trnpgbe_clear_interrupt_scheme(mucse);\n err_powerdown:\n \t/* notify powerdown only powerup ok */\n \tif (!err_notify) {\n@@ -203,10 +307,10 @@ static int rnpgbe_probe(struct pci_dev *pdev, const struct pci_device_id *id)\n \tif (err)\n \t\treturn err;\n \n-\terr = dma_set_coherent_mask(\u0026pdev-\u003edev, DMA_BIT_MASK(56));\n+\terr = dma_set_mask_and_coherent(\u0026pdev-\u003edev, DMA_BIT_MASK(56));\n \tif (err) {\n-\t\tdev_err(\u0026pdev-\u003edev,\n-\t\t\t\"No usable DMA configuration, aborting %d\\n\", err);\n+\t\tdev_err(\u0026pdev-\u003edev, \"No usable DMA configuration, aborting %d\\n\",\n+\t\t\terr);\n \t\tgoto err_disable_dev;\n \t}\n \n@@ -253,9 +357,11 @@ static void rnpgbe_rm_adapter(struct pci_dev *pdev)\n \t\treturn;\n \tnetdev = mucse-\u003enetdev;\n \tunregister_netdev(netdev);\n+\trnpgbe_free_mbx_irq(mucse);\n \terr = rnpgbe_send_notify(hw, false, mucse_fw_powerup);\n \tif (err)\n \t\tdev_warn(\u0026pdev-\u003edev, \"Send powerdown to hw failed %d\\n\", err);\n+\trnpgbe_clear_interrupt_scheme(mucse);\n \tfree_netdev(netdev);\n }\n \ndiff --git a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx.c b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx.c\nindex c464086982632..185465d4e0dcd 100644\n--- a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx.c\n+++ b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx.c\n@@ -84,7 +84,7 @@ static u32 mucse_mbx_get_lock_pf(struct mucse_hw *hw)\n  * @hw: pointer to the HW structure\n  *\n  * Pair with mucse_release_mbx_lock_pf()\n- * This function maybe used in an irq handler.\n+ * All mailbox access runs in process context.\n  *\n  * Return: 0 on success, negative errno on failure\n  **/\n@@ -93,11 +93,11 @@ static int mucse_obtain_mbx_lock_pf(struct mucse_hw *hw)\n \tstruct mucse_mbx_info *mbx = \u0026hw-\u003embx;\n \tu32 val;\n \n-\treturn read_poll_timeout_atomic(mucse_mbx_get_lock_pf,\n-\t\t\t\t\tval, val \u0026 MUCSE_MBX_PFU,\n-\t\t\t\t\tmbx-\u003edelay_us,\n-\t\t\t\t\tmbx-\u003etimeout_us,\n-\t\t\t\t\tfalse, hw);\n+\treturn read_poll_timeout(mucse_mbx_get_lock_pf,\n+\t\t\t\t val, val \u0026 MUCSE_MBX_PFU,\n+\t\t\t\t mbx-\u003edelay_us,\n+\t\t\t\t mbx-\u003etimeout_us,\n+\t\t\t\t false, hw);\n }\n \n /**\n@@ -252,6 +252,26 @@ int mucse_poll_and_read_mbx(struct mucse_hw *hw, __le32 *msg, u16 size)\n \treturn mucse_read_mbx_pf(hw, msg, size);\n }\n \n+/**\n+ * mucse_check_and_read_mbx - check if there is notification and receive message\n+ * @hw: pointer to the HW structure\n+ * @msg: the message buffer\n+ * @size: length of buffer\n+ *\n+ * Return: 0 if it successfully received a message notification and\n+ * copied it into the receive buffer, negative errno on failure\n+ **/\n+int mucse_check_and_read_mbx(struct mucse_hw *hw, __le32 *msg, u16 size)\n+{\n+\tint err;\n+\n+\terr = mucse_check_for_msg_pf(hw);\n+\tif (err)\n+\t\treturn err;\n+\n+\treturn mucse_read_mbx_pf(hw, msg, size);\n+}\n+\n /**\n  * mucse_mbx_get_fwack - Read fw ack from reg\n  * @mbx: pointer to the MBX structure\ndiff --git a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx.h b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx.h\nindex 75b88b18b04d3..3af008a67fb17 100644\n--- a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx.h\n+++ b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx.h\n@@ -18,4 +18,5 @@ int mucse_write_and_wait_ack_mbx(struct mucse_hw *hw,\n \t\t\t\t const __le32 *msg, u16 size);\n void mucse_init_mbx_params_pf(struct mucse_hw *hw);\n int mucse_poll_and_read_mbx(struct mucse_hw *hw, __le32 *msg, u16 size);\n+int mucse_check_and_read_mbx(struct mucse_hw *hw, __le32 *msg, u16 size);\n #endif /* _RNPGBE_MBX_H */\ndiff --git a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx_fw.c b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx_fw.c\nindex 5ba74997beacf..441724f8603eb 100644\n--- a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx_fw.c\n+++ b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx_fw.c\n@@ -3,6 +3,7 @@\n \n #include \u003clinux/if_ether.h\u003e\n #include \u003clinux/bitfield.h\u003e\n+#include \u003clinux/ethtool.h\u003e\n \n #include \"rnpgbe.h\"\n #include \"rnpgbe_mbx.h\"\n@@ -27,10 +28,14 @@ static int mucse_fw_send_cmd_wait_resp(struct mucse_hw *hw,\n \tint retry_cnt = 3;\n \tint err;\n \n+\tBUILD_BUG_ON(sizeof(struct mbx_fw_cmd_reply) != 56);\n+\n \tmutex_lock(\u0026hw-\u003embx.lock);\n+\n \terr = mucse_write_and_wait_ack_mbx(hw, req-\u003edwords, len);\n \tif (err)\n \t\tgoto out;\n+\n \tdo {\n \t\terr = mucse_poll_and_read_mbx(hw, reply-\u003edwords,\n \t\t\t\t\t      sizeof(reply-\u003er));\n@@ -197,3 +202,204 @@ int mucse_mbx_get_macaddr(struct mucse_hw *hw, int pfvfnum,\n \n \treturn 0;\n }\n+\n+/**\n+ * mucse_mbx_phyup - Request that firmware bring the PHY up or down\n+ * @hw: pointer to the HW structure\n+ * @is_phyup: true for up, false for down\n+ *\n+ * mucse_mbx_phyup echo fw to change phy status\n+ *\n+ * Return: 0 on success, negative errno on failure\n+ **/\n+int mucse_mbx_phyup(struct mucse_hw *hw, bool is_phyup)\n+{\n+\tunion mbx_fw_cmd_req_u req = {\n+\t\t.r = {\n+\t\t\t.datalen = cpu_to_le16(sizeof(req.r.phy_status) +\n+\t\t\t\t\t       MUCSE_MBX_REQ_HDR_LEN),\n+\t\t\t.opcode  = cpu_to_le16(SET_PHY_UP),\n+\t\t\t.phy_status = {\n+\t\t\t\t.port_mask = cpu_to_le32(BIT(hw-\u003eport)),\n+\t\t\t\t.status  = cpu_to_le32(is_phyup ? 1 : 0),\n+\t\t\t},\n+\t\t},\n+\t};\n+\tint len, err;\n+\n+\tlen = le16_to_cpu(req.r.datalen);\n+\tmutex_lock(\u0026hw-\u003embx.lock);\n+\terr = mucse_write_and_wait_ack_mbx(hw, req.dwords, len);\n+\tmutex_unlock(\u0026hw-\u003embx.lock);\n+\n+\treturn err;\n+}\n+\n+/**\n+ * mucse_mbx_link_report - Configure firmware link-change event reporting\n+ * @hw: pointer to the HW structure\n+ * @is_report: true for report, false for no\n+ *\n+ * mucse_mbx_link_report echo fw to change event report state\n+ *\n+ * Return: 0 on success, negative errno on failure\n+ **/\n+int mucse_mbx_link_report(struct mucse_hw *hw, bool is_report)\n+{\n+\tunion mbx_fw_cmd_req_u req = {\n+\t\t.r = {\n+\t\t\t.datalen = cpu_to_le16(sizeof(req.r.report_status) +\n+\t\t\t\t\t       MUCSE_MBX_REQ_HDR_LEN),\n+\t\t\t.opcode  = cpu_to_le16(LINK_REPORT_EN),\n+\t\t\t.report_status = {\n+\t\t\t\t.port_mask = cpu_to_le16(BIT(hw-\u003eport)),\n+\t\t\t\t.status  = cpu_to_le16(is_report ? 1 : 0),\n+\t\t\t},\n+\t\t},\n+\t};\n+\tint len, err;\n+\n+\tlen = le16_to_cpu(req.r.datalen);\n+\tmutex_lock(\u0026hw-\u003embx.lock);\n+\terr = mucse_write_and_wait_ack_mbx(hw, req.dwords, len);\n+\tmutex_unlock(\u0026hw-\u003embx.lock);\n+\n+\treturn err;\n+}\n+\n+/**\n+ * mucse_update_link_status_reg - update driver speed inf to reg\n+ * @hw: pointer to the HW structure\n+ * @req: pointer to req data\n+ *\n+ * Update the driver's link-state snapshot exported to firmware. Firmware\n+ * sends a new event when this snapshot differs from the hardware state.\n+ * The default snapshot clears the driver-reported fields;\n+ * a valid event then repopulates them, including the LLDP status in bit 6.\n+ *\n+ **/\n+static void mucse_update_link_status_reg(struct mucse_hw *hw,\n+\t\t\t\t\t struct mbx_fw_cmd_req *req)\n+{\n+\tu16 status = le16_to_cpu(req-\u003elink_stat.st.status);\n+\tu16 speed = le16_to_cpu(req-\u003elink_stat.st.speed);\n+\tu32 value;\n+\n+\tvalue = mucse_hw_rd32(hw, RNPGBE_LINK_ST);\n+\tvalue \u0026= ~M_ST_MASK;\n+\tvalue |= M_DEFAULT_ST;\n+\n+\tif (le16_to_cpu(req-\u003elink_stat.port_status)) {\n+\t\tvalue |= BIT(0);\n+\t\tswitch (speed) {\n+\t\tcase 10:\n+\t\t\tvalue |= (mucse_speed_10 \u003c\u003c 8);\n+\t\t\tbreak;\n+\t\tcase 100:\n+\t\t\tvalue |= (mucse_speed_100 \u003c\u003c 8);\n+\t\t\tbreak;\n+\t\tcase 1000:\n+\t\t\tvalue |= (mucse_speed_1000 \u003c\u003c 8);\n+\t\t\tbreak;\n+\t\tdefault:\n+\t\t\tbreak;\n+\t\t}\n+\n+\t\tvalue |= FIELD_PREP(BIT(4),\n+\t\t\t\t    !!(req-\u003elink_stat.st.flags \u0026 DUPLEX_BIT));\n+\t\tvalue |= FIELD_PREP(GENMASK_U32(25, 24),\n+\t\t\t\t    status \u0026 GENMASK(1, 0));\n+\t} else {\n+\t\tvalue \u0026= ~BIT(0);\n+\t}\n+\n+\tif (status \u0026 ST_STATUS_LLDP_STATUS_MASK)\n+\t\tvalue |= BIT(6);\n+\telse\n+\t\tvalue \u0026= ~BIT(6);\n+\n+\tmucse_hw_wr32(hw, RNPGBE_LINK_ST, value);\n+}\n+\n+/**\n+ * mucse_mbx_fw_req_handler - Handle fw req\n+ * @hw: pointer to the HW structure\n+ * @req: pointer to req data\n+ *\n+ * mucse_mbx_fw_req_handler handler fw req, such as a link event req.\n+ **/\n+static void mucse_mbx_fw_req_handler(struct mucse_hw *hw,\n+\t\t\t\t     struct mbx_fw_cmd_req *req)\n+{\n+\tstruct mucse *mucse = container_of(hw, struct mucse, hw);\n+\tu32 magic = le32_to_cpu(req-\u003elink_stat.port_magic);\n+\tunsigned long flags;\n+\n+\tif (le16_to_cpu(req-\u003eopcode) == LINK_CHANGE_EVT) {\n+\t\tspin_lock_irqsave(\u0026mucse-\u003elink_lock, flags);\n+\t\tif (magic != ST_VALID_MAGIC) {\n+\t\t\t/* Do not let an invalid event change the cached link state.\n+\t\t\t * Reset the driver snapshot so firmware reports it again.\n+\t\t\t */\n+\t\t\tmucse_hw_wr32(hw, RNPGBE_LINK_ST, M_DEFAULT_ST);\n+\t\t\tspin_unlock_irqrestore(\u0026mucse-\u003elink_lock, flags);\n+\t\t\treturn;\n+\t\t}\n+\n+\t\tif (test_bit(__MUCSE_DOWN, \u0026mucse-\u003estate)) {\n+\t\t\tmucse_update_link_status_reg(hw, req);\n+\t\t\tspin_unlock_irqrestore(\u0026mucse-\u003elink_lock, flags);\n+\t\t\treturn;\n+\t\t}\n+\n+\t\tif (le16_to_cpu(req-\u003elink_stat.port_status))\n+\t\t\tWRITE_ONCE(hw-\u003elink, true);\n+\t\telse\n+\t\t\tWRITE_ONCE(hw-\u003elink, false);\n+\n+\t\tWRITE_ONCE(hw-\u003espeed, le16_to_cpu(req-\u003elink_stat.st.speed));\n+\t\tWRITE_ONCE(hw-\u003eduplex, req-\u003elink_stat.st.flags \u0026 DUPLEX_BIT);\n+\t\t/* update regs to notify link info is received */\n+\t\tmucse_update_link_status_reg(hw, req);\n+\t\tatomic_set_release(\u0026mucse-\u003elink_pending, 1);\n+\t\tspin_unlock_irqrestore(\u0026mucse-\u003elink_lock, flags);\n+\t}\n+}\n+\n+/**\n+ * mucse_fw_handle_event - Handle one pending firmware event\n+ * @hw: pointer to the hardware structure\n+ *\n+ * Return: true if an event was handled, false otherwise\n+ **/\n+static bool mucse_fw_handle_event(struct mucse_hw *hw)\n+{\n+\tunion mbx_fw_cmd_u msg = {};\n+\tint err;\n+\n+\t/* try to check and read fw req */\n+\tmutex_lock(\u0026hw-\u003embx.lock);\n+\terr = mucse_check_and_read_mbx(hw, msg.dwords, sizeof(msg));\n+\tmutex_unlock(\u0026hw-\u003embx.lock);\n+\tif (err)\n+\t\treturn false;\n+\n+\tmucse_mbx_fw_req_handler(hw, \u0026msg.req);\n+\n+\treturn true;\n+}\n+\n+/**\n+ * mucse_fw_irq_handler - Drain pending firmware mailbox events\n+ * @hw: pointer to the HW structure\n+ *\n+ * The only asynchronous event currently handled is LINK_CHANGE_EVT.\n+ * Firmware rate-limits link-change notifications to a minimum interval\n+ * of 500 ms.\n+ **/\n+void mucse_fw_irq_handler(struct mucse_hw *hw)\n+{\n+\t/* Drain events coalesced while the mailbox work item was pending. */\n+\twhile (mucse_fw_handle_event(hw))\n+\t\tcontinue;\n+}\ndiff --git a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx_fw.h b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx_fw.h\nindex fe996aeffc4de..0e6972b7c3c02 100644\n--- a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx_fw.h\n+++ b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx_fw.h\n@@ -14,6 +14,9 @@ enum MUCSE_FW_CMD {\n \tGET_HW_INFO     = 0x0601,\n \tGET_MAC_ADDRESS = 0x0602,\n \tRESET_HW        = 0x0603,\n+\tLINK_CHANGE_EVT = 0x0608,\n+\tLINK_REPORT_EN  = 0x0613,\n+\tSET_PHY_UP      = 0x0800,\n \tPOWER_UP        = 0x0803,\n };\n \n@@ -36,6 +39,16 @@ struct mucse_hw_info {\n \t__le32 ext_info;\n } __packed;\n \n+#define ST_STATUS_LLDP_STATUS_MASK        BIT(12)\n+\n+#define DUPLEX_BIT                        BIT(0)\n+struct st_status {\n+\tu8 phyid;\n+\tu8 flags;\n+\t__le16 speed;\n+\t__le16 status;\n+} __packed;\n+\n struct mbx_fw_cmd_req {\n \t__le16 flags;\n \t__le16 opcode;\n@@ -55,10 +68,30 @@ struct mbx_fw_cmd_req {\n \t\t\t__le32 port_mask;\n \t\t\t__le32 pfvf_num;\n \t\t} get_mac_addr;\n+\t\tstruct {\n+\t\t\t__le32 port_mask;\n+\t\t\t__le32 status;\n+\t\t} phy_status;\n+\t\tstruct {\n+\t\t\t/* LINK_REPORT_EN uses its own 16-bit payload layout:\n+\t\t\t * status precedes port_mask.\n+\t\t\t */\n+\t\t\t__le16 status;\n+\t\t\t__le16 port_mask;\n+\t\t} report_status;\n+\t\tstruct {\n+\t\t\t__le16 changed_lanes;\n+\t\t\t__le16 port_status;\n+\t\t\t__le32 port_magic;\n+#define ST_VALID_MAGIC 0xa4a6a8a9\n+\t\t\tstruct st_status st;\n+\t\t} link_stat;\n \t};\n } __packed;\n \n struct mbx_fw_cmd_reply {\n+#define FLAGS_REPLY       BIT(0)\n+#define FLAGS_ERR         BIT(2)\n \t__le16 flags;\n \t__le16 opcode;\n \t__le16 error_code;\n@@ -94,9 +127,27 @@ union mbx_fw_cmd_reply_u {\n \t__le32 dwords[sizeof(struct mbx_fw_cmd_reply) / sizeof(__le32)];\n };\n \n+union mbx_fw_cmd_u {\n+\tstruct mbx_fw_cmd_req req;\n+\tstruct mbx_fw_cmd_reply reply;\n+\t__le32 dwords[sizeof(struct mbx_fw_cmd_reply) / sizeof(__le32)];\n+};\n+\n+/* Firmware encoding for RNPGBE_LINK_ST[11:8]. Valid link events report\n+ * only these three speeds.\n+ */\n+enum mucse_speed {\n+\tmucse_speed_10 = 0,\n+\tmucse_speed_100 = 1,\n+\tmucse_speed_1000 = 2,\n+};\n+\n int mucse_mbx_sync_fw(struct mucse_hw *hw);\n int mucse_mbx_powerup(struct mucse_hw *hw, bool is_powerup);\n int mucse_mbx_reset_hw(struct mucse_hw *hw);\n int mucse_mbx_get_macaddr(struct mucse_hw *hw, int pfvfnum,\n \t\t\t  u8 *mac_addr, int port);\n+int mucse_mbx_phyup(struct mucse_hw *hw, bool is_phyup);\n+int mucse_mbx_link_report(struct mucse_hw *hw, bool is_report);\n+void mucse_fw_irq_handler(struct mucse_hw *hw);\n #endif /* _RNPGBE_MBX_FW_H */\n"
}

2/1 2026/08/14 13:02 agent patch-evaluator
5m Model:
best-expensive

Args:
null

Results:
{
  "EnableConfigs": null,
  "FocusSymbols": [],
  "Reasoning": "The patch introduces new functionality to the `mucse` ethernet driver, which is for Mucse 1GbE PCI Express adapters. This is a physical PCIe hardware card that is not emulated in standard QEMU or other virtualized environments. Therefore, the code paths are impossible to reach and test during fuzzing.",
  "WorthFuzzing": false
}

Instruction:
You are an expert Linux kernel maintainer.
Your job is to review a provided patch series and determine
if it makes functional changes to the kernel that should be fuzzed.

IMPORTANT: The changes have ALREADY been applied and committed as the HEAD commit in
your workspace. Do NOT rely on your internal knowledge of the kernel. You must actively
use your code access tools to examine the actual source code and confirm any assumptions.

Return WorthFuzzing=false if the patch only contains:
- Modifications to Documentation/, Kconfig files, or code comments.
- Purely decorative changes, such as logging (e.g., pr_err, printk) or tracepoints.
- Changes to numeric constants or macros that do not functionally alter execution flow.
- Code paths that are impossible to reach in virtualized environments like GCE or QEMU,
  even when utilizing software-emulated hardware (e.g., usb gadget, mac80211_hwsim).
- Code in vendor-specific PCIe switch, SmartNIC, or GPU drivers (e.g., mlxsw, pds_core, qed,
  ionic, amdgpu) that require physical PCIe hardware cards not emulated in standard QEMU.
- Driver .remove, .shutdown, or pci_unregister_driver teardown callbacks (e.g., igb_remove)
  that are executed only during PCI hot-unplug or sysfs driver unbind operations.

If it modifies reachable core kernel logic, drivers, or architectures, use your code search
tools to verify the code can be executed, then return WorthFuzzing=true.

When returning WorthFuzzing=true, you MUST ALSO:
1. Extract any specific kernel functions that should be heavily fuzzed into FocusSymbols.
   Avoid listing generic hot-path functions to prevent skewed test distributions.
   Prefer non-static, non-inlined API entrypoint functions over internal static helper functions
   (which are inlined by the compiler and do not have distinct symbol addresses).
2. Identify any specific CONFIG_ options required to properly test this new/modified feature.
   Go and look into the Kconfig files and check for ifdefs around the code, do not make assumptions.
   Also check "depends on" lines in Kconfig to include any non-standard parent subsystem configs
   needed for Kbuild to compile the code statically into vmlinux. List them in the EnableConfigs
   output array, and DO NOT add a 'CONFIG_' prefix (e.g., return "NET_IPV4" instead of "CONFIG_NET_IPV4").
Prefer calling several tools at the same time to save round-trips.


Use set-results tool to provide results of the analysis.
It must be called exactly once before the final reply.
Ignore results of this tool.

Prompt:
For your convenience, here is the diff of the changes:
commit 10120395cd8c6048c89ffe53826db77153924c53
Author: syz-cluster <triage@syzkaller.com>
Date:   Fri Aug 14 13:02:47 2026 +0000

    syz-cluster: applied patch under review

diff --git a/drivers/net/ethernet/mucse/Kconfig b/drivers/net/ethernet/mucse/Kconfig
index 0b3e853d625f1..be0fdf268484c 100644
--- a/drivers/net/ethernet/mucse/Kconfig
+++ b/drivers/net/ethernet/mucse/Kconfig
@@ -19,6 +19,7 @@ if NET_VENDOR_MUCSE
 config MGBE
 	tristate "Mucse(R) 1GbE PCI Express adapters support"
 	depends on PCI
+	select PAGE_POOL
 	help
 	  This driver supports Mucse(R) 1GbE PCI Express family of
 	  adapters.
diff --git a/drivers/net/ethernet/mucse/rnpgbe/Makefile b/drivers/net/ethernet/mucse/rnpgbe/Makefile
index de8bcb7772ab4..17574cad392a2 100644
--- a/drivers/net/ethernet/mucse/rnpgbe/Makefile
+++ b/drivers/net/ethernet/mucse/rnpgbe/Makefile
@@ -8,4 +8,5 @@ obj-$(CONFIG_MGBE) += rnpgbe.o
 rnpgbe-objs := rnpgbe_main.o\
 	       rnpgbe_chip.o\
 	       rnpgbe_mbx.o\
-	       rnpgbe_mbx_fw.o
+	       rnpgbe_mbx_fw.o\
+	       rnpgbe_lib.o
diff --git a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe.h b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe.h
index 5b024f9f7e17a..482c13d987b1c 100644
--- a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe.h
+++ b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe.h
@@ -5,7 +5,14 @@
 #define _RNPGBE_H
 
 #include <linux/types.h>
+#include <linux/atomic.h>
 #include <linux/mutex.h>
+#include <linux/netdevice.h>
+#include <linux/timer.h>
+#include <linux/if.h>
+#include <linux/workqueue.h>
+
+#include "rnpgbe_hw.h"
 
 enum rnpgbe_boards {
 	board_n500,
@@ -26,30 +33,200 @@ 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 {
 	void __iomem *hw_addr;
+	void __iomem *ring_msix_base;
 	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 {
+	__le64 pkt_addr; /* Packet buffer address */
+	union {
+		__le64 vlan_cmd_bsz;
+		struct {
+			__le32 blen_mac_ip_len;
+			__le32 vlan_cmd; /* vlan & cmd status */
+		};
+	};
+#define M_TXD_CMD_RS          0x040000 /* Report Status */
+#define M_TXD_STAT_DD         0x020000 /* Descriptor Done */
+#define M_TXD_CMD_EOP         0x010000 /* End of Packet */
+};
+
+union rnpgbe_rx_desc {
+	struct {
+		__le64 pkt_addr; /* Packet buffer address */
+		__le64 resv_cmd; /* cmd status */
+	};
+	struct {
+		__le32 rss_hash; /* RSS HASH */
+		__le16 mark; /* mark info */
+		__le16 rev1;
+		__le16 len; /* Packet length */
+		__le16 padding_len;
+		__le16 vlan; /* VLAN tag */
+		__le16 cmd; /* cmd status */
+#define M_RXD_STAT_DD         BIT(1) /* Descriptor Done */
+#define M_RXD_STAT_EOP        BIT(0) /* End of Packet */
+	} wb;
+};
+
+#define M_TX_DESC(R, i) (&(((struct rnpgbe_tx_desc *)((R)->desc))[i]))
+#define M_RX_DESC(R, i) (&(((union rnpgbe_rx_desc *)((R)->desc))[i]))
+
+static inline __le16 rnpgbe_test_staterr(union rnpgbe_rx_desc *rx_desc,
+					 const u16 stat_err_bits)
+{
+	return rx_desc->wb.cmd & cpu_to_le16(stat_err_bits);
+}
+
+struct mucse_tx_buffer {
+	struct rnpgbe_tx_desc *next_to_watch;
+	struct sk_buff *skb;
+	unsigned int bytecount;
+	unsigned short gso_segs;
+	DEFINE_DMA_UNMAP_ADDR(dma);
+	DEFINE_DMA_UNMAP_LEN(len);
+	bool mapped_as_page;  /* true if dma was mapped with dma_map_page */
+};
+
+struct mucse_queue_stats {
+	u64 packets;
+	u64 bytes;
+	atomic64_t dropped;
+};
+
+struct mucse_rx_buffer {
+	struct sk_buff *skb;
+	dma_addr_t dma;
+	struct page *page;
+	u32 page_offset;
+};
+
+struct mucse_ring {
+	struct mucse_ring *next;
+	struct mucse_q_vector *q_vector;
+	struct net_device *netdev;
+	struct device *dev;
+	struct page_pool *page_pool;
+	void *desc;
+	union {
+		struct mucse_tx_buffer *tx_buffer_info;
+		struct mucse_rx_buffer *rx_buffer_info;
+	};
+	void __iomem *ring_addr;
+	void __iomem *tail;
+	void __iomem *irq_mask;
+	void __iomem *trig;
+	u8 queue_index;
+	/* hw ring idx */
+	u8 rnpgbe_queue_idx;
 	u8 pfvfnum;
+	u16 count;
+	u16 next_to_use;
+	u16 next_to_clean;
+	dma_addr_t dma;
+	unsigned int size;
+	struct mucse_queue_stats stats;
+	struct u64_stats_sync syncp;
+	bool drop_status;
+} ____cacheline_internodealigned_in_smp;
+
+static inline u16 mucse_desc_unused(struct mucse_ring *ring)
+{
+	u16 ntc = ring->next_to_clean;
+	u16 ntu = ring->next_to_use;
+
+	return ((ntc > ntu) ? 0 : ring->count) + ntc - ntu - 1;
+}
+
+static inline u16 mucse_desc_unused_rx(struct mucse_ring *ring)
+{
+	u16 ntc = ring->next_to_clean;
+	u16 ntu = ring->next_to_use;
+
+	/* Keep M_RX_BUFFER_WRITE descriptors unused so the ring is not filled
+	 * completely. Refill is attempted once at least this many descriptors
+	 * are available.
+	 */
+	return ((ntc > ntu) ? 0 : ring->count) + ntc - ntu - 16;
+}
+
+static inline __le64 build_ctob(u32 vlan_cmd, u32 mac_ip_len, u32 size)
+{
+	return cpu_to_le64(((u64)vlan_cmd << 32) | ((u64)mac_ip_len << 16) |
+			   ((u64)size));
+}
+
+static inline struct netdev_queue *txring_txq(const struct mucse_ring *ring)
+{
+	return netdev_get_tx_queue(ring->netdev, ring->queue_index);
+}
+
+struct mucse_ring_container {
+	struct mucse_ring *ring;
+	u16 count;
 };
 
-struct mucse_stats {
-	u64 tx_dropped;
+struct mucse_q_vector {
+	struct mucse *mucse;
+	/* hardware interrupt vector number */
+	int hw_vector;
+	struct mucse_ring_container rx, tx;
+	struct napi_struct napi;
+	struct timer_list rx_alloc_timer;
+	char name[IFNAMSIZ + 18];
+	/* for dynamic allocation of rings associated with this q_vector */
+	struct mucse_ring ring[] ____cacheline_internodealigned_in_smp;
+};
+
+#define MAX_Q_VECTORS 8
+
+#define M_DEFAULT_TXD     512
+#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;
 	struct mucse_hw hw;
-	struct mucse_stats stats;
+#define M_FLAG_MSIX_SINGLE_EN      BIT(0)
+#define M_FLAG_MSIX_EN             BIT(1)
+	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;
+	struct mucse_q_vector *q_vector[MAX_Q_VECTORS];
+	int tx_ring_item_count;
+	int tx_work_limit;
+	int num_tx_queues;
+	int num_q_vectors;
+	int rx_ring_item_count;
+	int num_rx_queues;
+	char mbx_name[32];
+	unsigned long state;
+	atomic_t link_pending;
+	struct work_struct mbx_work;
+	struct delayed_work serv_task;
+	spinlock_t link_lock; /* spinlock for link update */
 };
 
 int rnpgbe_get_permanent_mac(struct mucse_hw *hw, u8 *perm_addr);
@@ -58,6 +235,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_link(struct mucse_hw *hw, bool linkup);
 
 /* Device IDs */
 #define PCI_VENDOR_ID_MUCSE               0x8848
@@ -68,4 +246,10 @@ int rnpgbe_init_hw(struct mucse_hw *hw, int board_type);
 
 #define mucse_hw_wr32(hw, reg, val) \
 	writel((val), (hw)->hw_addr + (reg))
+#define mucse_hw_rd32(hw, reg) \
+	readl((hw)->hw_addr + (reg))
+#define mucse_ring_wr32(ring, reg, val) \
+	writel((val), (ring)->ring_addr + (reg))
+#define mucse_ring_rd32(ring, reg) \
+	readl((ring)->ring_addr + (reg))
 #endif /* _RNPGBE_H */
diff --git a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_chip.c b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_chip.c
index ebc7b3750157b..55b4abed82bd7 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;
 	}
@@ -89,8 +95,12 @@ static void rnpgbe_init_n500(struct mucse_hw *hw)
 {
 	struct mucse_mbx_info *mbx = &hw->mbx;
 
+	hw->ring_msix_base = hw->hw_addr + MUCSE_N500_RING_MSIX_BASE;
+
 	mbx->fwpf_ctrl_base = MUCSE_N500_FWPF_CTRL_BASE;
 	mbx->fwpf_shm_base = MUCSE_N500_FWPF_SHM_BASE;
+
+	hw->cycles_per_us = M_DEFAULT_N500_MHZ;
 }
 
 /**
@@ -104,8 +114,12 @@ static void rnpgbe_init_n210(struct mucse_hw *hw)
 {
 	struct mucse_mbx_info *mbx = &hw->mbx;
 
+	hw->ring_msix_base = hw->hw_addr + MUCSE_N210_RING_MSIX_BASE;
+
 	mbx->fwpf_ctrl_base = MUCSE_N210_FWPF_CTRL_BASE;
 	mbx->fwpf_shm_base = MUCSE_N210_FWPF_SHM_BASE;
+
+	hw->cycles_per_us = M_DEFAULT_N210_MHZ;
 }
 
 /**
@@ -141,3 +155,32 @@ int rnpgbe_init_hw(struct mucse_hw *hw, int board_type)
 
 	return 0;
 }
+
+/**
+ * rnpgbe_set_link - Set the hardware link state
+ * @hw: hw information structure
+ * @linkup: link on or not
+ *
+ * rnpgbe_set_link setup link status
+ *
+ **/
+void rnpgbe_set_link(struct mucse_hw *hw, bool linkup)
+{
+	u32 value = mucse_hw_rd32(hw, GMAC_CONTROL);
+
+	if (linkup)
+		value |= GMAC_CONTROL_RE;
+	else
+		value &= ~GMAC_CONTROL_RE;
+
+	mucse_hw_wr32(hw, GMAC_CONTROL, value);
+
+	/* Keep the GMAC in receive-all mode while the link is up. The
+	 * chip-level filter does the actual address filtering, but there
+	 * is no ndo_set_rx_mode yet to configure it.
+	 */
+	if (linkup)
+		mucse_hw_wr32(hw, GMAC_FRAME_FILTER, GMAC_RX_ALL);
+	else
+		mucse_hw_wr32(hw, GMAC_FRAME_FILTER, 0);
+}
diff --git a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_hw.h b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_hw.h
index e77e6bc3d3e30..9e640e8d67036 100644
--- a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_hw.h
+++ b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_hw.h
@@ -6,12 +6,42 @@
 
 #define MUCSE_N500_FWPF_CTRL_BASE      0x28b00
 #define MUCSE_N500_FWPF_SHM_BASE       0x2d000
+#define MUCSE_N500_RING_MSIX_BASE      0x28700
+#define M_DEFAULT_N500_MHZ             125
 #define MUCSE_GBE_PFFW_MBX_CTRL_OFFSET 0x5500
 #define MUCSE_GBE_FWPF_MBX_MASK_OFFSET 0x5700
 #define MUCSE_N210_FWPF_CTRL_BASE      0x29400
 #define MUCSE_N210_FWPF_SHM_BASE       0x2d900
+#define MUCSE_N210_RING_MSIX_BASE      0x29000
+#define M_DEFAULT_N210_MHZ             62
 
+#define RNPGBE_DMA_STATUS              0x0008
+#define TX_AXI_RW_EN                   0xc
+/* DMA_STATUS_REG[23:20]: tx_wr, tx_rd, rx_wr, rx_rd done status. */
+#define RNPGBE_DMA_TX_STATUS           GENMASK_U32(23, 22)
+#define RNPGBE_DMA_RX_STATUS           GENMASK_U32(21, 20)
+#define RX_AXI_RW_EN                   0x03
+/* RNPGBE_LINK_ST is a driver-owned link-state snapshot consumed by firmware.
+ * M_DEFAULT_ST replaces the complete snapshot, causing firmware
+ * to report the current link state again when it differs from this default.
+ */
+#define M_ST_MASK                      (GENMASK_U32(31, 24) | \
+					GENMASK_U32(11, 8) | BIT(6) | \
+					BIT(4) | BIT(0))
+/* Set the driver-state marker; all other driver status fields start clear. */
+#define M_DEFAULT_ST                   0xa0000000
+/* Driver-reported fields: 25:24 pause, 11:8 speed, 6 LLDP, 4 duplex,
+ * and 0 link up/down. M_DEFAULT_ST resets these fields so firmware reports
+ * the current hardware state again.
+ */
+#define RNPGBE_LINK_ST                 0x000c
 #define RNPGBE_DMA_AXI_EN              0x0010
+#define RNPGBE_TX_MIN_PKT_LEN          33
 
+#define MUCSE_GMAC_OFF(_n)             (0x20000 + (_n))
+#define GMAC_CONTROL_RE                0x00000004
+#define GMAC_CONTROL                   MUCSE_GMAC_OFF(0)
+#define GMAC_RX_ALL                    (BIT(31) | BIT(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
new file mode 100644
index 0000000000000..fa5ea4dd7a1c6
--- /dev/null
+++ b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c
@@ -0,0 +1,2253 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright(c) 2020 - 2025 Mucse Corporation. */
+
+#include <linux/pci.h>
+#include <linux/netdevice.h>
+#include <linux/iopoll.h>
+#include <linux/etherdevice.h>
+#include <linux/vmalloc.h>
+#include <net/netdev_queues.h>
+#include <net/page_pool/helpers.h>
+
+#include "rnpgbe_lib.h"
+#include "rnpgbe.h"
+#include "rnpgbe_mbx_fw.h"
+
+static void rnpgbe_mbx_work(struct work_struct *work)
+{
+	struct mucse *mucse = container_of(work, struct mucse, mbx_work);
+
+	mucse_fw_irq_handler(&mucse->hw);
+}
+
+/**
+ * rnpgbe_msix_other - Other irq handler
+ * @irq: interrupt number
+ * @data: private data
+ *
+ * Return: IRQ_HANDLED
+ **/
+static irqreturn_t rnpgbe_msix_other(int irq, void *data)
+{
+	struct mucse *mucse = (struct mucse *)data;
+
+	queue_work(system_percpu_wq, &mucse->mbx_work);
+
+	return IRQ_HANDLED;
+}
+
+static void rnpgbe_irq_disable_queues(struct mucse_q_vector *q_vector)
+{
+	struct mucse_ring *ring;
+
+	/*
+	 * TX/RX pairs share interrupt control registers; update them through
+	 * the TX ring list.
+	 */
+	mucse_for_each_ring(ring, q_vector->tx) {
+		writel(INT_VALID, ring->trig);
+		writel((RX_INT_MASK | TX_INT_MASK), ring->irq_mask);
+	}
+	/* flush posted writes to ensure hardware sees the mask */
+	if (q_vector->tx.ring)
+		readl(q_vector->tx.ring->irq_mask);
+}
+
+/**
+ * rnpgbe_int_single - MSI-X single-vector/MSI interrupt handler
+ * @irq: interrupt number
+ * @data: private data
+ *
+ * Return: IRQ_HANDLED
+ **/
+static irqreturn_t rnpgbe_int_single(int irq, void *data)
+{
+	struct mucse *mucse = (struct mucse *)data;
+	struct mucse_q_vector *q_vector;
+
+	queue_work(system_percpu_wq, &mucse->mbx_work);
+
+	if (test_bit(__MUCSE_DOWN, &mucse->state))
+		return IRQ_HANDLED;
+
+	q_vector = mucse->q_vector[0];
+	rnpgbe_irq_disable_queues(q_vector);
+	napi_schedule_irqoff(&q_vector->napi);
+
+	return IRQ_HANDLED;
+}
+
+static void rnpgbe_irq_enable_queues(struct mucse_q_vector *q_vector)
+{
+	struct mucse_ring *ring;
+
+	/*
+	 * TX/RX pairs share interrupt control registers; update them through
+	 * the TX ring list.
+	 */
+	mucse_for_each_ring(ring, q_vector->tx) {
+		writel(0, ring->irq_mask);
+
+		/* Re-trigger hw to re-check events lost while masked. */
+		writel(INT_VALID | TX_INT_MASK | RX_INT_MASK, ring->trig);
+	}
+}
+
+/**
+ * rnpgbe_clean_tx_irq - Reclaim resources after transmit completes
+ * @q_vector: structure containing interrupt and ring information
+ * @tx_ring: tx ring to clean
+ * @napi_budget: Used to determine if we are in netpoll
+ *
+ * Return: true if cleaning completed within the budget, otherwise false
+ **/
+static bool rnpgbe_clean_tx_irq(struct mucse_q_vector *q_vector,
+				struct mucse_ring *tx_ring,
+				int napi_budget)
+{
+	int budget = q_vector->mucse->tx_work_limit;
+	u64 total_bytes = 0, total_packets = 0;
+	struct mucse *mucse = q_vector->mucse;
+	struct mucse_tx_buffer *tx_buffer;
+	struct rnpgbe_tx_desc *tx_desc;
+	int i = tx_ring->next_to_clean;
+
+	tx_buffer = &tx_ring->tx_buffer_info[i];
+	tx_desc = M_TX_DESC(tx_ring, i);
+	i -= tx_ring->count;
+
+	do {
+		struct rnpgbe_tx_desc *eop_desc = tx_buffer->next_to_watch;
+
+		/* if next_to_watch is not set then there is no work pending */
+		if (!eop_desc)
+			break;
+
+		/* prevent any other reads prior to eop_desc */
+		dma_rmb();
+
+		/* if eop DD is not set pending work has not been completed */
+		if (!(eop_desc->vlan_cmd & cpu_to_le32(M_TXD_STAT_DD)))
+			break;
+		/* clear next_to_watch to prevent false hangs */
+		tx_buffer->next_to_watch = NULL;
+		total_bytes += tx_buffer->bytecount;
+		total_packets += tx_buffer->gso_segs;
+		napi_consume_skb(tx_buffer->skb, napi_budget);
+		if (tx_buffer->mapped_as_page) {
+			dma_unmap_page(tx_ring->dev,
+				       dma_unmap_addr(tx_buffer, dma),
+				       dma_unmap_len(tx_buffer, len),
+				       DMA_TO_DEVICE);
+		} else {
+			dma_unmap_single(tx_ring->dev,
+					 dma_unmap_addr(tx_buffer, dma),
+					 dma_unmap_len(tx_buffer, len),
+					 DMA_TO_DEVICE);
+		}
+		tx_buffer->skb = NULL;
+		dma_unmap_len_set(tx_buffer, len, 0);
+
+		/* unmap remaining buffers */
+		while (tx_desc != eop_desc) {
+			tx_buffer++;
+			tx_desc++;
+			i++;
+			if (unlikely(!i)) {
+				i -= tx_ring->count;
+				tx_buffer = tx_ring->tx_buffer_info;
+				tx_desc = M_TX_DESC(tx_ring, 0);
+			}
+
+			/* unmap any remaining paged data */
+			if (dma_unmap_len(tx_buffer, len)) {
+				dma_unmap_page(tx_ring->dev,
+					       dma_unmap_addr(tx_buffer, dma),
+					       dma_unmap_len(tx_buffer, len),
+					       DMA_TO_DEVICE);
+				dma_unmap_len_set(tx_buffer, len, 0);
+			}
+		}
+
+		/* move us one more past the eop_desc for start of next pkt */
+		tx_buffer++;
+		tx_desc++;
+		i++;
+		if (unlikely(!i)) {
+			i -= tx_ring->count;
+			tx_buffer = tx_ring->tx_buffer_info;
+			tx_desc = M_TX_DESC(tx_ring, 0);
+		}
+
+		prefetch(tx_desc);
+		budget--;
+	} while (likely(budget > 0));
+
+	i += tx_ring->count;
+	tx_ring->next_to_clean = i;
+	u64_stats_update_begin(&tx_ring->syncp);
+	tx_ring->stats.bytes += total_bytes;
+	tx_ring->stats.packets += total_packets;
+	u64_stats_update_end(&tx_ring->syncp);
+
+#define TX_WAKE_THRESHOLD (DESC_NEEDED * 2)
+	__netif_txq_completed_wake(txring_txq(tx_ring),
+				   total_packets, total_bytes,
+				   mucse_desc_unused(tx_ring),
+				   TX_WAKE_THRESHOLD,
+				   !netif_carrier_ok(tx_ring->netdev) ||
+				   test_bit(__MUCSE_DOWN, &mucse->state));
+
+	return !!budget;
+}
+
+static bool mucse_alloc_mapped_page(struct mucse_ring *rx_ring,
+				    struct mucse_rx_buffer *bi)
+{
+	struct page *page = bi->page;
+	dma_addr_t dma;
+
+	if (page) {
+		/* Buffer is being reused without going back through the
+		 * page_pool. Do dma_sync for hw use.
+		 */
+		dma_sync_single_range_for_device(rx_ring->dev, bi->dma,
+						 bi->page_offset,
+						 PAGE_SIZE - bi->page_offset,
+						 DMA_FROM_DEVICE);
+		return true;
+	}
+
+	page = page_pool_dev_alloc_pages(rx_ring->page_pool);
+	if (unlikely(!page))
+		return false;
+	dma = page_pool_get_dma_addr(page);
+
+	bi->dma = dma;
+	bi->page = page;
+	bi->page_offset = RNPGBE_SKB_PAD;
+
+	return true;
+}
+
+static void mucse_update_rx_tail(struct mucse_ring *rx_ring,
+				 u32 val)
+{
+	rx_ring->next_to_use = val;
+	writel(val, rx_ring->tail);
+}
+
+/**
+ * rnpgbe_alloc_rx_buffers - Replace used receive buffers
+ * @rx_ring: ring to place buffers on
+ * @cleaned_count: number of buffers to replace
+ *
+ * Return: true if alloc failed
+ **/
+static bool rnpgbe_alloc_rx_buffers(struct mucse_ring *rx_ring,
+				    u16 cleaned_count)
+{
+	u64 fun_id = ((u64)(rx_ring->pfvfnum) << 56);
+	union rnpgbe_rx_desc *rx_desc;
+	u16 i = rx_ring->next_to_use;
+	struct mucse_rx_buffer *bi;
+	bool err = false;
+	u64 addr;
+	/* nothing to do */
+	if (!cleaned_count)
+		return err;
+
+	rx_desc = M_RX_DESC(rx_ring, i);
+	bi = &rx_ring->rx_buffer_info[i];
+	i -= rx_ring->count;
+
+	do {
+		if (!mucse_alloc_mapped_page(rx_ring, bi)) {
+			err = true;
+			break;
+		}
+
+		addr = (u64)(bi->dma + bi->page_offset);
+		rx_desc->pkt_addr = cpu_to_le64(addr | fun_id);
+		/* clean dd */
+		rx_desc->resv_cmd = 0;
+		rx_desc++;
+		bi++;
+		i++;
+		if (unlikely(!i)) {
+			rx_desc = M_RX_DESC(rx_ring, 0);
+			bi = rx_ring->rx_buffer_info;
+			i -= rx_ring->count;
+		}
+		cleaned_count--;
+	} while (cleaned_count);
+
+	i += rx_ring->count;
+
+	/* Publish the tail even when allocation stopped early, so a stale
+	 * tail from a previous session cannot point hardware at a
+	 * reallocated ring.
+	 */
+	dma_wmb();
+	mucse_update_rx_tail(rx_ring, i);
+
+	return err;
+}
+
+/**
+ * rnpgbe_get_buffer - Get the rx_buffer to be used
+ * @rx_ring: pointer to rx ring
+ * @skb: pointer skb for this packet
+ * @size: data size in this desc
+ *
+ * Return: rx_buffer.
+ **/
+static struct mucse_rx_buffer *rnpgbe_get_buffer(struct mucse_ring *rx_ring,
+						 struct sk_buff **skb,
+						 const unsigned int size)
+{
+	struct mucse_rx_buffer *rx_buffer;
+
+	rx_buffer = &rx_ring->rx_buffer_info[rx_ring->next_to_clean];
+	*skb = rx_buffer->skb;
+	prefetchw(page_address(rx_buffer->page) + rx_buffer->page_offset);
+	/* we are reusing so sync this buffer for CPU use */
+	dma_sync_single_range_for_cpu(rx_ring->dev, rx_buffer->dma,
+				      rx_buffer->page_offset, size,
+				      DMA_FROM_DEVICE);
+
+	return rx_buffer;
+}
+
+/**
+ * rnpgbe_add_rx_frag - Add non-linear data to the skb
+ * @rx_buffer: pointer to rx_buffer
+ * @skb: pointer skb for this packet
+ * @size: data size in this desc
+ **/
+static void rnpgbe_add_rx_frag(struct mucse_rx_buffer *rx_buffer,
+			       struct sk_buff *skb,
+			       unsigned int size)
+{
+	unsigned int truesize = PAGE_SIZE;
+
+	skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, rx_buffer->page,
+			rx_buffer->page_offset, size, truesize);
+}
+
+/**
+ * rnpgbe_build_skb - Try to build a skb based on rx_buffer
+ * @rx_buffer: pointer to rx_buffer
+ * @size: data size in this desc
+ *
+ * Return: skb for this rx_buffer
+ **/
+static struct sk_buff *rnpgbe_build_skb(struct mucse_rx_buffer *rx_buffer,
+					unsigned int size)
+{
+	void *va = page_address(rx_buffer->page) + rx_buffer->page_offset;
+	unsigned int truesize = PAGE_SIZE;
+	struct sk_buff *skb;
+
+	net_prefetch(va);
+	/* build an skb around the page buffer */
+	skb = build_skb(va - RNPGBE_SKB_PAD, truesize);
+	if (unlikely(!skb))
+		return NULL;
+	/* update pointers within the skb to store the data */
+	skb_reserve(skb, RNPGBE_SKB_PAD);
+	__skb_put(skb, size);
+	skb_mark_for_recycle(skb);
+
+	return skb;
+}
+
+/**
+ * rnpgbe_is_non_eop - Process handling of non-EOP buffers
+ * @rx_ring: rx ring being processed
+ * @rx_desc: rx descriptor for current buffer
+ * @skb: current socket buffer containing buffer in progress
+ *
+ * This function updates next to clean.  If the buffer is an EOP buffer
+ * this function exits returning false, otherwise it will place the
+ * sk_buff in the next buffer to be chained and return true indicating
+ * that this is in fact a non-EOP buffer.
+ *
+ * Return: true for not end of packet
+ **/
+static bool rnpgbe_is_non_eop(struct mucse_ring *rx_ring,
+			      union rnpgbe_rx_desc *rx_desc,
+			      struct sk_buff *skb)
+{
+	u32 ntc = rx_ring->next_to_clean + 1;
+
+	/* fetch, update, and store next to clean */
+	ntc = (ntc < rx_ring->count) ? ntc : 0;
+	rx_ring->next_to_clean = ntc;
+	prefetch(M_RX_DESC(rx_ring, ntc));
+	/* if we are the last buffer then there is nothing else to do */
+	if (likely(rnpgbe_test_staterr(rx_desc, M_RXD_STAT_EOP)))
+		return false;
+	if (skb_shinfo(skb)->nr_frags < MAX_SKB_FRAGS) {
+		/* place skb in next buffer to be received */
+		rx_ring->rx_buffer_info[ntc].skb = skb;
+	} else {
+		atomic64_inc(&rx_ring->stats.dropped);
+		/* too much frags, force free */
+		dev_kfree_skb_any(skb);
+		rx_ring->drop_status = true;
+	}
+	/* we should clean it since we used all info in it */
+	rx_desc->wb.cmd = 0;
+
+	return true;
+}
+
+/**
+ * rnpgbe_cleanup_headers - Correct corrupted or empty headers
+ * @skb: current socket buffer containing buffer in progress
+ *
+ * Return: true if an error was encountered and skb was freed.
+ **/
+static bool rnpgbe_cleanup_headers(struct sk_buff *skb)
+{
+	/* if eth_skb_pad returns an error the skb was freed */
+	if (eth_skb_pad(skb))
+		return true;
+
+	return false;
+}
+
+/**
+ * rnpgbe_process_skb_fields - Set the RX queue and protocol fields
+ * @rx_ring: RX descriptor ring containing the queue information
+ * @skb: skb currently being received
+ *
+ * Records the RX queue that received the skb and sets its protocol from
+ * the Ethernet header.
+ **/
+static void rnpgbe_process_skb_fields(struct mucse_ring *rx_ring,
+				      struct sk_buff *skb)
+{
+	struct net_device *dev = rx_ring->netdev;
+
+	skb_record_rx_queue(skb, rx_ring->queue_index);
+	skb->protocol = eth_type_trans(skb, dev);
+}
+
+/**
+ * rnpgbe_rx_alloc_retry - Retry RX buffer allocation
+ * @timer: RX allocation retry timer
+ *
+ * Schedules NAPI after RX buffer allocation fails during polling or
+ * initial ring configuration.
+ **/
+static void rnpgbe_rx_alloc_retry(struct timer_list *timer)
+{
+	struct mucse_q_vector *q_vector =
+		timer_container_of(q_vector, timer, rx_alloc_timer);
+
+	napi_schedule(&q_vector->napi);
+}
+
+/**
+ * rnpgbe_clean_rx_irq - Clean completed descriptors from Rx ring
+ * @q_vector: structure containing interrupt and ring information
+ * @rx_ring: rx descriptor ring to transact packets on
+ * @budget: total limit on number of packets to process
+ *
+ * rnpgbe_clean_rx_irq tries to check dd in desc, handle this desc
+ * if dd is set which means data is write-back by hw
+ *
+ * Return: amount of work completed.
+ **/
+static int rnpgbe_clean_rx_irq(struct mucse_q_vector *q_vector,
+			       struct mucse_ring *rx_ring,
+			       int budget)
+{
+	unsigned int max_size = SKB_WITH_OVERHEAD(PAGE_SIZE) - RNPGBE_SKB_PAD;
+	unsigned int total_rx_bytes = 0, total_rx_packets = 0;
+	u16 cleaned_count = mucse_desc_unused_rx(rx_ring);
+	unsigned int work_done = 0;
+
+	while (likely(work_done < budget)) {
+		struct mucse_rx_buffer *rx_buffer;
+		union rnpgbe_rx_desc *rx_desc;
+		struct sk_buff *skb;
+		unsigned int size;
+
+		if (cleaned_count >= M_RX_BUFFER_WRITE) {
+			if (rnpgbe_alloc_rx_buffers(rx_ring, cleaned_count)) {
+				mod_timer(&q_vector->rx_alloc_timer,
+					  jiffies + msecs_to_jiffies(500));
+				cleaned_count = mucse_desc_unused_rx(rx_ring);
+			} else {
+				cleaned_count = 0;
+			}
+		}
+		rx_desc = M_RX_DESC(rx_ring, rx_ring->next_to_clean);
+
+		if (!rnpgbe_test_staterr(rx_desc, M_RXD_STAT_DD))
+			break;
+
+		/* This memory barrier is needed to keep us from reading
+		 * any other fields out of the rx_desc until we know the
+		 * descriptor has been written back
+		 */
+		dma_rmb();
+		/* Hardware enforces: minimum 33-bytes descriptor(no 1-13 byte
+		 * size), multi-descriptors only for jumbo frames > 1536 bytes
+		 * (controlled by M_DEFAULT_SG=96, each descriptor no more than
+		 * 1536 bytes). Small packets use single descriptor.
+		 */
+		size = le16_to_cpu(rx_desc->wb.len);
+
+		if (unlikely(rx_ring->drop_status)) {
+			cleaned_count++;
+			/* drop data until eop */
+			if (rnpgbe_test_staterr(rx_desc, M_RXD_STAT_EOP)) {
+				rx_ring->drop_status = false;
+				work_done++;
+			}
+
+			rx_desc->wb.cmd = 0;
+			rx_ring->next_to_clean++;
+			if (rx_ring->next_to_clean >= rx_ring->count)
+				rx_ring->next_to_clean = 0;
+			continue;
+		}
+
+		if (unlikely(!size || size > max_size)) {
+			struct mucse_rx_buffer *err_rx_buffer;
+			u16 idx = rx_ring->next_to_clean;
+
+			cleaned_count++;
+			atomic64_inc(&rx_ring->stats.dropped);
+
+			/* Free the partial skb from a previous non-EOP
+			 * descriptor before advancing next_to_clean.
+			 */
+			err_rx_buffer = &rx_ring->rx_buffer_info[idx];
+			if (unlikely(err_rx_buffer->skb)) {
+				dev_kfree_skb_any(err_rx_buffer->skb);
+				err_rx_buffer->skb = NULL;
+			}
+
+			/* drop data until eop */
+			if (rnpgbe_test_staterr(rx_desc, M_RXD_STAT_EOP))
+				work_done++;
+			else
+				rx_ring->drop_status = true;
+
+			rx_desc->wb.cmd = 0;
+			rx_ring->next_to_clean++;
+			if (rx_ring->next_to_clean >= rx_ring->count)
+				rx_ring->next_to_clean = 0;
+			continue;
+		}
+
+		rx_buffer = rnpgbe_get_buffer(rx_ring, &skb, size);
+
+		if (skb)
+			rnpgbe_add_rx_frag(rx_buffer, skb, size);
+		else
+			skb = rnpgbe_build_skb(rx_buffer, size);
+
+		if (!skb) {
+			cleaned_count++;
+
+			/* drop until eop if multiple descriptors */
+			if (rnpgbe_test_staterr(rx_desc, M_RXD_STAT_EOP))
+				work_done++;
+			else
+				rx_ring->drop_status = true;
+
+			rx_desc->wb.cmd = 0;
+			rx_ring->next_to_clean++;
+			atomic64_inc(&rx_ring->stats.dropped);
+			if (rx_ring->next_to_clean >= rx_ring->count)
+				rx_ring->next_to_clean = 0;
+
+			continue;
+		}
+
+		rx_buffer->page = NULL;
+		rx_buffer->skb = NULL;
+		cleaned_count++;
+
+		if (rnpgbe_is_non_eop(rx_ring, rx_desc, skb))
+			continue;
+
+		/* verify the packet layout is correct */
+		if (rnpgbe_cleanup_headers(skb)) {
+			/* we should clean it since we used all info in it */
+			atomic64_inc(&rx_ring->stats.dropped);
+			rx_desc->wb.cmd = 0;
+			work_done++;
+			continue;
+		}
+
+		/* probably a little skewed due to removing CRC */
+		total_rx_bytes += skb->len;
+		rnpgbe_process_skb_fields(rx_ring, skb);
+		rx_desc->wb.cmd = 0;
+		napi_gro_receive(&q_vector->napi, skb);
+		/* Update packet statistics and NAPI work accounting. */
+		total_rx_packets++;
+		work_done++;
+	}
+
+	u64_stats_update_begin(&rx_ring->syncp);
+	rx_ring->stats.packets += total_rx_packets;
+	rx_ring->stats.bytes += total_rx_bytes;
+	u64_stats_update_end(&rx_ring->syncp);
+
+	return work_done;
+}
+
+/**
+ * rnpgbe_poll - NAPI polling callback
+ * @napi: structure for representing this polling device
+ * @budget: how many packets driver is allowed to clean
+ *
+ * This function is the NAPI poll callback for all interrupt modes.
+ *
+ * Return: work done in this call
+ **/
+static int rnpgbe_poll(struct napi_struct *napi, int budget)
+{
+	struct mucse_q_vector *q_vector =
+		container_of(napi, struct mucse_q_vector, napi);
+	bool clean_complete = true;
+	struct mucse_ring *ring;
+	int per_ring_budget;
+	int work_done = 0;
+
+	mucse_for_each_ring(ring, q_vector->tx) {
+		if (!rnpgbe_clean_tx_irq(q_vector, ring, budget))
+			clean_complete = false;
+	}
+
+	/* Exit if we are called by netpoll */
+	if (unlikely(!budget))
+		return 0;
+
+	if (q_vector->rx.count > 1)
+		per_ring_budget = max(budget / q_vector->rx.count, 1);
+	else
+		per_ring_budget = budget;
+
+	mucse_for_each_ring(ring, q_vector->rx) {
+		int cleaned = 0;
+
+		cleaned = rnpgbe_clean_rx_irq(q_vector, ring, per_ring_budget);
+		work_done += cleaned;
+		if (cleaned >= per_ring_budget)
+			clean_complete = false;
+	}
+
+	if (!clean_complete)
+		return budget;
+
+	if (likely(napi_complete_done(napi, work_done))) {
+		if (!test_bit(__MUCSE_DOWN, &q_vector->mucse->state))
+			rnpgbe_irq_enable_queues(q_vector);
+	}
+
+	return work_done;
+}
+
+/**
+ * rnpgbe_request_mbx_irq - Register mbx routine
+ * @mucse: pointer to private structure
+ *
+ * In MSIX mode, register a dedicated handler for vector 0 (mailbox)
+ * In MSI/MSI-X_SINGLE mode, mailbox is multiplexed through
+ * data tx/rx handler.
+ *
+ * Return: 0 on success, negative on failure
+ **/
+int rnpgbe_request_mbx_irq(struct mucse *mucse)
+{
+	struct pci_dev *pdev = mucse->pdev;
+	int err = 0;
+
+	snprintf(mucse->mbx_name, sizeof(mucse->mbx_name),
+		 "rnpgbe-mbx:%s", pci_name(pdev));
+	INIT_WORK(&mucse->mbx_work, rnpgbe_mbx_work);
+
+	if (mucse->flags & M_FLAG_MSIX_EN) {
+		err = request_irq(pci_irq_vector(pdev, 0),
+				  rnpgbe_msix_other, 0, mucse->mbx_name,
+				  mucse);
+	} else {
+		err = request_irq(pci_irq_vector(pdev, 0),
+				  rnpgbe_int_single, 0, mucse->mbx_name,
+				  mucse);
+	}
+
+	return err;
+}
+
+/**
+ * rnpgbe_free_mbx_irq - Remove mbx routine
+ * @mucse: pointer to private structure
+ **/
+void rnpgbe_free_mbx_irq(struct mucse *mucse)
+{
+	struct pci_dev *pdev = mucse->pdev;
+
+	free_irq(pci_irq_vector(pdev, 0), mucse);
+	cancel_work_sync(&mucse->mbx_work);
+}
+
+/**
+ * rnpgbe_set_num_queues - Allocate queues for device, feature dependent
+ * @mucse: pointer to private structure
+ *
+ * Determine tx/rx queue counts
+ **/
+static void rnpgbe_set_num_queues(struct mucse *mucse)
+{
+	/* start from 1 queue */
+	mucse->num_tx_queues = 1;
+	mucse->num_rx_queues = 1;
+}
+
+/**
+ * rnpgbe_set_interrupt_capability - Set MSI-X or MSI if supported
+ * @mucse: pointer to private structure
+ *
+ * Attempt to configure the interrupts using the best available
+ * capabilities of the hardware.
+ *
+ * Return: 0 on success, negative on failure
+ **/
+static int rnpgbe_set_interrupt_capability(struct mucse *mucse)
+{
+	int v_budget;
+
+	v_budget = min3(mucse->num_tx_queues, mucse->num_rx_queues,
+			MAX_Q_VECTORS);
+	v_budget = min_t(int, v_budget, num_online_cpus());
+	/* add one vector for mbx */
+	v_budget += 1;
+
+	/* Hardware limitation: only 1 MSI vector is supported even
+	 * if multiple messages are requested. MSI mode falls back
+	 * to single vector automatically.
+	 */
+	v_budget = pci_alloc_irq_vectors(mucse->pdev, 1, v_budget,
+					 PCI_IRQ_MSI | PCI_IRQ_MSIX);
+	if (v_budget < 0)
+		return v_budget;
+
+	if (mucse->pdev->msix_enabled) {
+		/* q_vector not include mbx */
+		if (v_budget > 1) {
+			mucse->flags |= M_FLAG_MSIX_EN;
+			mucse->num_q_vectors = v_budget - 1;
+		} else {
+			mucse->flags |= M_FLAG_MSIX_SINGLE_EN;
+			mucse->num_q_vectors = 1;
+		}
+	} else {
+		/* Hardware supports only one MSI interrupt. */
+		mucse->num_q_vectors = 1;
+	}
+
+	return 0;
+}
+
+/**
+ * mucse_add_ring - Add ring to ring container
+ * @ring: ring to be added
+ * @head: ring container
+ **/
+static void mucse_add_ring(struct mucse_ring *ring,
+			   struct mucse_ring_container *head)
+{
+	ring->next = head->ring;
+	head->ring = ring;
+	head->count++;
+}
+
+/**
+ * rnpgbe_alloc_q_vector - Allocate memory for a single interrupt vector
+ * @mucse: pointer to private structure
+ * @eth_queue_idx: queue_index idx for this q_vector
+ * @vector_idx: q_vector array index
+ * @r_idx: starting hardware ring index
+ * @r_count: number of TX/RX ring pairs
+ * @step: ring step
+ *
+ * Return: 0 on success. If allocation fails we return -ENOMEM.
+ **/
+static int rnpgbe_alloc_q_vector(struct mucse *mucse,
+				 int eth_queue_idx, int vector_idx, int r_idx,
+				 int r_count, int step)
+{
+	int rxr_idx = r_idx, txr_idx = r_idx;
+	struct mucse_hw *hw = &mucse->hw;
+	struct mucse_q_vector *q_vector;
+	int txr_count, rxr_count, idx;
+	struct mucse_ring *ring;
+	int ring_count;
+
+	/*
+	 * TX and RX rings are always allocated as pairs with the same hardware
+	 * ring index. Interrupt control is shared by the pair, so the TX ring
+	 * list is used to update the common registers.
+	 */
+	txr_count = r_count;
+	rxr_count = r_count;
+	ring_count = txr_count + rxr_count;
+
+	q_vector = kzalloc_flex(*q_vector, ring, ring_count);
+	if (!q_vector)
+		return -ENOMEM;
+
+	netif_napi_add(mucse->netdev, &q_vector->napi, rnpgbe_poll);
+	/* tie q_vector and mucse together */
+	mucse->q_vector[vector_idx] = q_vector;
+	q_vector->mucse = mucse;
+	timer_setup(&q_vector->rx_alloc_timer, rnpgbe_rx_alloc_retry, 0);
+	q_vector->hw_vector = vector_idx;
+	/* if mbx use separate irq, we should add 1 */
+	if (mucse->flags & M_FLAG_MSIX_EN)
+		q_vector->hw_vector++;
+
+	ring = q_vector->ring;
+
+	for (idx = 0; idx < txr_count; idx++) {
+		ring->dev = &mucse->pdev->dev;
+		mucse_add_ring(ring, &q_vector->tx);
+		ring->count = mucse->tx_ring_item_count;
+		ring->netdev = mucse->netdev;
+		ring->queue_index = eth_queue_idx + idx;
+		ring->rnpgbe_queue_idx = txr_idx;
+		ring->ring_addr = hw->hw_addr + RING_OFFSET(txr_idx);
+		ring->irq_mask = ring->ring_addr + RNPGBE_DMA_INT_MASK;
+		ring->trig = ring->ring_addr + RNPGBE_DMA_INT_TRIG;
+		ring->q_vector = q_vector;
+		ring->pfvfnum = hw->pfvfnum;
+		u64_stats_init(&ring->syncp);
+		mucse->tx_ring[ring->queue_index] = ring;
+		txr_idx += step;
+		ring++;
+	}
+
+	for (idx = 0; idx < rxr_count; idx++) {
+		ring->dev = &mucse->pdev->dev;
+		mucse_add_ring(ring, &q_vector->rx);
+		ring->count = mucse->rx_ring_item_count;
+		ring->netdev = mucse->netdev;
+		ring->queue_index = eth_queue_idx + idx;
+		ring->rnpgbe_queue_idx = rxr_idx;
+		ring->ring_addr = hw->hw_addr + RING_OFFSET(rxr_idx);
+		ring->irq_mask = ring->ring_addr + RNPGBE_DMA_INT_MASK;
+		ring->trig = ring->ring_addr + RNPGBE_DMA_INT_TRIG;
+		ring->q_vector = q_vector;
+		ring->pfvfnum = hw->pfvfnum;
+		u64_stats_init(&ring->syncp);
+		mucse->rx_ring[ring->queue_index] = ring;
+		rxr_idx += step;
+		ring++;
+	}
+
+	return 0;
+}
+
+/**
+ * rnpgbe_free_q_vector - Free memory allocated for specific interrupt vector
+ * @mucse: pointer to private structure
+ * @vector_idx: q_vector array index
+ *
+ * This function frees the memory allocated to the q_vector.  In addition if
+ * NAPI is enabled it will delete any references to the NAPI struct prior
+ * to freeing the q_vector.
+ **/
+static void rnpgbe_free_q_vector(struct mucse *mucse, int vector_idx)
+{
+	struct mucse_q_vector *q_vector = mucse->q_vector[vector_idx];
+	struct mucse_ring *ring;
+
+	timer_delete_sync(&q_vector->rx_alloc_timer);
+
+	mucse_for_each_ring(ring, q_vector->tx)
+		mucse->tx_ring[ring->queue_index] = NULL;
+	mucse_for_each_ring(ring, q_vector->rx)
+		mucse->rx_ring[ring->queue_index] = NULL;
+	mucse->q_vector[vector_idx] = NULL;
+	netif_napi_del(&q_vector->napi);
+	kfree(q_vector);
+}
+
+/**
+ * rnpgbe_alloc_q_vectors - Allocate memory for interrupt vectors
+ * @mucse: pointer to private structure
+ *
+ * Return: 0 on success, or -ENOMEM on allocation failure.
+ **/
+static int rnpgbe_alloc_q_vectors(struct mucse *mucse)
+{
+	int err, ring_cnt, v_remaing = mucse->num_q_vectors;
+	int r_remaing = min_t(int, mucse->num_tx_queues,
+			      mucse->num_rx_queues);
+	int q_vector_nums = 0;
+	int eth_queue_idx = 0;
+	int vector_idx = 0;
+	int ring_step = 1;
+	int ring_idx = 0;
+
+	for (; r_remaing > 0 && v_remaing > 0; v_remaing--) {
+		ring_cnt = DIV_ROUND_UP(r_remaing, v_remaing);
+		err = rnpgbe_alloc_q_vector(mucse, eth_queue_idx,
+					    vector_idx, ring_idx, ring_cnt,
+					    ring_step);
+		if (err)
+			goto err_free_q_vector;
+		ring_idx += ring_step * ring_cnt;
+		eth_queue_idx += ring_cnt;
+		r_remaing -= ring_cnt;
+		q_vector_nums++;
+		vector_idx++;
+	}
+	/* Fix the real used q_vectors_nums */
+	mucse->num_q_vectors = q_vector_nums;
+	mucse->num_tx_queues = eth_queue_idx;
+	mucse->num_rx_queues = eth_queue_idx;
+
+	return 0;
+
+err_free_q_vector:
+	mucse->num_tx_queues = 0;
+	mucse->num_rx_queues = 0;
+	mucse->num_q_vectors = 0;
+
+	while (vector_idx--)
+		rnpgbe_free_q_vector(mucse, vector_idx);
+
+	return err;
+}
+
+/**
+ * rnpgbe_reset_interrupt_capability - Reset irq capability setup
+ * @mucse: pointer to private structure
+ **/
+static void rnpgbe_reset_interrupt_capability(struct mucse *mucse)
+{
+	pci_free_irq_vectors(mucse->pdev);
+	mucse->flags &= ~(M_FLAG_MSIX_EN |
+			M_FLAG_MSIX_SINGLE_EN);
+}
+
+/**
+ * rnpgbe_init_interrupt_scheme - Determine proper interrupt scheme
+ * @mucse: pointer to private structure
+ *
+ * We determine which interrupt scheme to use based on...
+ * - Hardware queue count
+ * - cpu count
+ * - interrupt mode (MSI and legacy modes use one vector)
+ *
+ * Return: 0 on success, negative on failure
+ **/
+int rnpgbe_init_interrupt_scheme(struct mucse *mucse)
+{
+	int err;
+
+	rnpgbe_set_num_queues(mucse);
+
+	err = rnpgbe_set_interrupt_capability(mucse);
+	if (err)
+		return err;
+
+	err = rnpgbe_alloc_q_vectors(mucse);
+	if (err) {
+		rnpgbe_reset_interrupt_capability(mucse);
+		return err;
+	}
+
+	return 0;
+}
+
+/**
+ * rnpgbe_free_q_vectors - Free memory allocated for interrupt vectors
+ * @mucse: pointer to private structure
+ *
+ * This function frees the memory allocated to the q_vectors.  In addition if
+ * NAPI is enabled it will delete any references to the NAPI struct prior
+ * to freeing the q_vector.
+ **/
+static void rnpgbe_free_q_vectors(struct mucse *mucse)
+{
+	int vector_idx = mucse->num_q_vectors;
+
+	mucse->num_rx_queues = 0;
+	mucse->num_tx_queues = 0;
+	mucse->num_q_vectors = 0;
+
+	while (vector_idx--)
+		rnpgbe_free_q_vector(mucse, vector_idx);
+}
+
+/**
+ * rnpgbe_clear_interrupt_scheme - Clear the current interrupt scheme settings
+ * @mucse: pointer to private structure
+ *
+ * Clear interrupt specific resources and reset the structure
+ **/
+void rnpgbe_clear_interrupt_scheme(struct mucse *mucse)
+{
+	mucse->num_tx_queues = 0;
+	mucse->num_rx_queues = 0;
+	rnpgbe_free_q_vectors(mucse);
+	rnpgbe_reset_interrupt_capability(mucse);
+}
+
+/**
+ * rnpgbe_msix_clean_rings - MSI-x interrupt handler for ring irq
+ * @irq: interrupt number
+ * @data: private data
+ *
+ * rnpgbe_msix_clean_rings handle irq from ring, start napi
+ * Return: IRQ_HANDLED
+ **/
+static irqreturn_t rnpgbe_msix_clean_rings(int irq, void *data)
+{
+	struct mucse_q_vector *q_vector = (struct mucse_q_vector *)data;
+
+	rnpgbe_irq_disable_queues(q_vector);
+	napi_schedule_irqoff(&q_vector->napi);
+
+	return IRQ_HANDLED;
+}
+
+/**
+ * rnpgbe_request_irq - Initialize interrupts
+ * @mucse: pointer to private structure
+ *
+ * Attempts to configure interrupts using the best available
+ * capabilities of the hardware and kernel.
+ *
+ * Return: 0 on success, negative value on failure
+ **/
+int rnpgbe_request_irq(struct mucse *mucse)
+{
+	struct net_device *netdev = mucse->netdev;
+	struct pci_dev *pdev = mucse->pdev;
+	struct mucse_q_vector *q_vector;
+	int err, i;
+
+	if (mucse->flags & M_FLAG_MSIX_EN) {
+		for (i = 0; i < mucse->num_q_vectors; i++) {
+			q_vector = mucse->q_vector[i];
+
+			snprintf(q_vector->name, sizeof(q_vector->name),
+				 "%s-%s-%d", netdev->name, "TxRx", i);
+
+			err = request_irq(pci_irq_vector(pdev, i + 1),
+					  rnpgbe_msix_clean_rings, 0,
+					  q_vector->name,
+					  q_vector);
+			if (err) {
+				dev_err(&pdev->dev, "MSI-X req err %d: %d\n",
+					i + 1, err);
+				goto err_free_irqs;
+			}
+		}
+	}
+
+	return 0;
+err_free_irqs:
+	while (i--) {
+		q_vector = mucse->q_vector[i];
+		synchronize_irq(pci_irq_vector(pdev, i + 1));
+		free_irq(pci_irq_vector(pdev, i + 1), q_vector);
+	}
+
+	return err;
+}
+
+/**
+ * rnpgbe_free_irq - Free interrupts
+ * @mucse: pointer to private structure
+ *
+ * Attempts to free interrupts according initialized type.
+ **/
+void rnpgbe_free_irq(struct mucse *mucse)
+{
+	struct pci_dev *pdev = mucse->pdev;
+	struct mucse_q_vector *q_vector;
+
+	if (mucse->flags & M_FLAG_MSIX_EN) {
+		for (int i = 0; i < mucse->num_q_vectors; i++) {
+			q_vector = mucse->q_vector[i];
+			if (!q_vector)
+				continue;
+
+			free_irq(pci_irq_vector(pdev, i + 1), q_vector);
+		}
+	}
+}
+
+/**
+ * rnpgbe_set_ring_vector - Set the ring_vector registers,
+ * mapping interrupt causes to vectors
+ * @mucse: pointer to private structure
+ * @queue: queue to map the corresponding interrupt to
+ * @vector: the vector num to map to the corresponding queue
+ *
+ */
+static void rnpgbe_set_ring_vector(struct mucse *mucse,
+				   u8 queue, u8 vector)
+{
+	struct mucse_hw *hw = &mucse->hw;
+	u32 data;
+
+	data = hw->pfvfnum << 24;
+	data |= (vector << 8);
+	data |= vector;
+	writel(data, hw->ring_msix_base + RING_VECTOR(queue));
+}
+
+/**
+ * rnpgbe_configure_msi - Configure MSI hardware
+ * @mucse: pointer to private structure
+ *
+ * rnpgbe_configure_msi sets up the hardware to properly generate MSI
+ * interrupts.
+ **/
+static void rnpgbe_configure_msi(struct mucse *mucse)
+{
+	struct mucse_q_vector *q_vector = mucse->q_vector[0];
+	struct mucse_ring *ring;
+
+	/* TX/RX pairs share the vector register; update it through TX rings. */
+	mucse_for_each_ring(ring, q_vector->tx)
+		rnpgbe_set_ring_vector(mucse, ring->rnpgbe_queue_idx, 0);
+}
+
+/**
+ * rnpgbe_configure_msix - Configure MSI-X hardware
+ * @mucse: pointer to private structure
+ *
+ * rnpgbe_configure_msix sets up the hardware to properly generate MSI-X
+ * interrupts.
+ **/
+static void rnpgbe_configure_msix(struct mucse *mucse)
+{
+	struct mucse_q_vector *q_vector;
+
+	for (int i = 0; i < mucse->num_q_vectors; i++) {
+		struct mucse_ring *ring;
+
+		q_vector = mucse->q_vector[i];
+		/* TX/RX pairs share the vector register; update it through TX rings. */
+		mucse_for_each_ring(ring, q_vector->tx) {
+			rnpgbe_set_ring_vector(mucse, ring->rnpgbe_queue_idx,
+					       q_vector->hw_vector);
+		}
+	}
+}
+
+static void rnpgbe_irq_enable(struct mucse *mucse)
+{
+	for (int i = 0; i < mucse->num_q_vectors; i++)
+		rnpgbe_irq_enable_queues(mucse->q_vector[i]);
+}
+
+/**
+ * rnpgbe_irq_disable - Mask off interrupt generation on the NIC
+ * @mucse: board private structure
+ **/
+void rnpgbe_irq_disable(struct mucse *mucse)
+{
+	struct pci_dev *pdev = mucse->pdev;
+
+	if (mucse->flags & M_FLAG_MSIX_EN) {
+		for (int i = 0; i < mucse->num_q_vectors; i++) {
+			rnpgbe_irq_disable_queues(mucse->q_vector[i]);
+			synchronize_irq(pci_irq_vector(pdev, i + 1));
+		}
+	} else {
+		rnpgbe_irq_disable_queues(mucse->q_vector[0]);
+		synchronize_irq(pci_irq_vector(pdev, 0));
+	}
+}
+
+static void rnpgbe_napi_enable_all(struct mucse *mucse)
+{
+	for (int i = 0; i < mucse->num_q_vectors; i++)
+		napi_enable(&mucse->q_vector[i]->napi);
+}
+
+static void rnpgbe_napi_disable_all(struct mucse *mucse)
+{
+	for (int i = 0; i < mucse->num_q_vectors; i++)
+		napi_disable(&mucse->q_vector[i]->napi);
+}
+
+static void rnpgbe_stop_tx_ring(struct mucse_ring *tx_ring)
+{
+	if (!tx_ring->tx_buffer_info)
+		return;
+
+	/* Stop hw. No new descriptors are fetched after TX_START=0.
+	 * DMA for descriptors fetched before the stop may still be in flight.
+	 */
+	mucse_ring_wr32(tx_ring, RNPGBE_TX_START, 0);
+	/* Flush posted write to ensure hardware sees TX_START=0 */
+	(void)mucse_ring_rd32(tx_ring, RNPGBE_TX_START);
+}
+
+static void rnpgbe_wait_tx_dma_idle(struct mucse *mucse)
+{
+	struct mucse_hw *hw = &mucse->hw;
+	u32 dma_status;
+	int err;
+
+	err = readl_poll_timeout(hw->hw_addr + RNPGBE_DMA_STATUS,
+				 dma_status,
+				 (dma_status & RNPGBE_DMA_TX_STATUS) ==
+				 RNPGBE_DMA_TX_STATUS,
+				 10, 100000);
+	if (err)
+		dev_warn(&mucse->pdev->dev,
+			 "Timed out waiting for TX DMA to quiesce, status %#x\n",
+			 dma_status);
+}
+
+static void rnpgbe_wait_rx_dma_idle(struct mucse *mucse)
+{
+	struct mucse_hw *hw = &mucse->hw;
+	u32 dma_status;
+	int err;
+
+	err = readl_poll_timeout(hw->hw_addr + RNPGBE_DMA_STATUS,
+				 dma_status,
+				 (dma_status & RNPGBE_DMA_RX_STATUS) ==
+				 RNPGBE_DMA_RX_STATUS,
+				 10, 100000);
+	if (err)
+		dev_warn(&mucse->pdev->dev,
+			 "Timed out waiting for RX DMA to quiesce, status %#x\n",
+			 dma_status);
+}
+
+/**
+ * rnpgbe_clean_tx_ring - Free Tx Buffers
+ * @tx_ring: ring to be cleaned
+ **/
+static void rnpgbe_clean_tx_ring(struct mucse_ring *tx_ring)
+{
+	struct mucse_tx_buffer *tx_buffer;
+	u16 i = tx_ring->next_to_clean;
+	unsigned long size;
+
+	/* ring already cleared, nothing to do */
+	if (!tx_ring->tx_buffer_info)
+		return;
+
+	tx_buffer = &tx_ring->tx_buffer_info[i];
+
+	while (i != tx_ring->next_to_use) {
+		struct rnpgbe_tx_desc *eop_desc, *tx_desc;
+
+		dev_kfree_skb_any(tx_buffer->skb);
+		/* unmap skb header data */
+		if (dma_unmap_len(tx_buffer, len)) {
+			if (tx_buffer->mapped_as_page) {
+				dma_unmap_page(tx_ring->dev,
+					       dma_unmap_addr(tx_buffer, dma),
+					       dma_unmap_len(tx_buffer, len),
+					       DMA_TO_DEVICE);
+			} else {
+				dma_unmap_single(tx_ring->dev,
+						 dma_unmap_addr(tx_buffer, dma),
+						 dma_unmap_len(tx_buffer, len),
+						 DMA_TO_DEVICE);
+			}
+		}
+		eop_desc = tx_buffer->next_to_watch;
+		tx_desc = M_TX_DESC(tx_ring, i);
+		/* unmap remaining buffers */
+		while (tx_desc != eop_desc) {
+			tx_buffer++;
+			tx_desc++;
+			i++;
+			if (unlikely(i == tx_ring->count)) {
+				i = 0;
+				tx_buffer = tx_ring->tx_buffer_info;
+				tx_desc = M_TX_DESC(tx_ring, 0);
+			}
+
+			/* unmap any remaining paged data */
+			if (dma_unmap_len(tx_buffer, len))
+				dma_unmap_page(tx_ring->dev,
+					       dma_unmap_addr(tx_buffer, dma),
+					       dma_unmap_len(tx_buffer, len),
+					       DMA_TO_DEVICE);
+		}
+		/* move us one more past the eop_desc for start of next pkt */
+		tx_buffer++;
+		i++;
+		if (unlikely(i == tx_ring->count)) {
+			i = 0;
+			tx_buffer = tx_ring->tx_buffer_info;
+		}
+	}
+
+	netdev_tx_reset_queue(txring_txq(tx_ring));
+	size = sizeof(struct mucse_tx_buffer) * tx_ring->count;
+	memset(tx_ring->tx_buffer_info, 0, size);
+	/* Zero out the descriptor ring */
+	memset(tx_ring->desc, 0, tx_ring->size);
+	tx_ring->next_to_use = 0;
+	tx_ring->next_to_clean = 0;
+}
+
+/**
+ * rnpgbe_clean_all_tx_rings - Stop TX DMA and free Tx buffers for all queues
+ * @mucse: board private structure
+ **/
+void rnpgbe_clean_all_tx_rings(struct mucse *mucse)
+{
+	struct mucse_hw *hw = &mucse->hw;
+	u32 dma_axi_ctl;
+
+	for (int i = 0; i < mucse->num_tx_queues; i++)
+		rnpgbe_stop_tx_ring(mucse->tx_ring[i]);
+
+	if (mucse->num_tx_queues)
+		rnpgbe_wait_tx_dma_idle(mucse);
+
+	dma_axi_ctl = mucse_hw_rd32(hw, RNPGBE_DMA_AXI_EN);
+	dma_axi_ctl &= ~TX_AXI_RW_EN;
+	mucse_hw_wr32(hw, RNPGBE_DMA_AXI_EN, dma_axi_ctl);
+	/* Flush the posted write before releasing the DMA mappings. */
+	(void)mucse_hw_rd32(hw, RNPGBE_DMA_AXI_EN);
+
+	for (int i = 0; i < mucse->num_tx_queues; i++)
+		rnpgbe_clean_tx_ring(mucse->tx_ring[i]);
+}
+
+static void rnpgbe_stop_rx_ring(struct mucse_ring *rx_ring)
+{
+	/* Stop hw. hardware design guarantees:
+	 * - No new descriptors will be fetched after RX_START=0
+	 * - No DMA will be initiated for already-fetched descriptors
+	 */
+	mucse_ring_wr32(rx_ring, RNPGBE_RX_START, 0);
+	/* Flush posted write to ensure hardware sees RX_START=0 */
+	(void)mucse_ring_rd32(rx_ring, RNPGBE_RX_START);
+}
+
+/**
+ * rnpgbe_stop_all_rx_rings - Stop RX DMA for all queues
+ * @mucse: board private structure
+ **/
+static void rnpgbe_stop_all_rx_rings(struct mucse *mucse)
+{
+	struct mucse_hw *hw = &mucse->hw;
+	u32 dma_axi_ctl;
+
+	for (int i = 0; i < mucse->num_rx_queues; i++)
+		rnpgbe_stop_rx_ring(mucse->rx_ring[i]);
+
+	if (mucse->num_rx_queues)
+		rnpgbe_wait_rx_dma_idle(mucse);
+
+	dma_axi_ctl = mucse_hw_rd32(hw, RNPGBE_DMA_AXI_EN);
+	dma_axi_ctl &= ~RX_AXI_RW_EN;
+	mucse_hw_wr32(hw, RNPGBE_DMA_AXI_EN, dma_axi_ctl);
+	/* Flush the posted write before releasing the DMA mappings. */
+	(void)mucse_hw_rd32(hw, RNPGBE_DMA_AXI_EN);
+}
+
+/**
+ * rnpgbe_clean_all_rx_rings - Free Rx buffers for all queues
+ * @mucse: board private structure
+ **/
+static void rnpgbe_clean_all_rx_rings(struct mucse *mucse)
+{
+	for (int i = 0; i < mucse->num_rx_queues; i++)
+		rnpgbe_clean_rx_ring(mucse->rx_ring[i]);
+}
+
+static void rnpgbe_cancel_rx_retry_timers(struct mucse *mucse)
+{
+	for (int i = 0; i < mucse->num_q_vectors; i++)
+		timer_delete_sync(&mucse->q_vector[i]->rx_alloc_timer);
+}
+
+bool rnpgbe_down(struct mucse *mucse)
+{
+	struct net_device *netdev = mucse->netdev;
+	struct mucse_hw *hw = &mucse->hw;
+	unsigned long flags;
+	int err;
+
+	if (test_and_set_bit(__MUCSE_DOWN, &mucse->state))
+		return false;
+
+	cancel_delayed_work_sync(&mucse->serv_task);
+
+	spin_lock_irqsave(&mucse->link_lock, flags);
+	WRITE_ONCE(hw->link, false);
+	WRITE_ONCE(hw->speed, 0);
+	WRITE_ONCE(hw->duplex, 0);
+	atomic_set(&mucse->link_pending, 0);
+	spin_unlock_irqrestore(&mucse->link_lock, flags);
+	rnpgbe_set_link(hw, false);
+
+	netif_carrier_off(netdev);
+	netif_tx_disable(netdev);
+	rnpgbe_cancel_rx_retry_timers(mucse);
+	rnpgbe_irq_disable(mucse);
+	rnpgbe_stop_all_rx_rings(mucse);
+	rnpgbe_napi_disable_all(mucse);
+
+	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");
+	}
+	rnpgbe_clean_all_tx_rings(mucse);
+	rnpgbe_clean_all_rx_rings(mucse);
+
+	return true;
+}
+
+/**
+ * rnpgbe_up_complete - Final step for port up
+ * @mucse: pointer to private structure
+ *
+ * Return: 0 on success, negative errno if firmware setup fails
+ **/
+int rnpgbe_up_complete(struct mucse *mucse)
+{
+	struct net_device *netdev = mucse->netdev;
+	struct mucse_hw *hw = &mucse->hw;
+	unsigned long flags;
+	int err;
+
+	if (mucse->flags & (M_FLAG_MSIX_EN | M_FLAG_MSIX_SINGLE_EN))
+		rnpgbe_configure_msix(mucse);
+	else
+		rnpgbe_configure_msi(mucse);
+
+	rnpgbe_napi_enable_all(mucse);
+	/* Clear stale link state and tell firmware the driver hasn't
+	 * seen any link yet. Firmware will re-assert LINK_CHANGE_EVT
+	 * when link_report_en is sent below since RNPGBE_LINK_ST
+	 * no longer matches the actual (possibly up) link state.
+	 */
+	spin_lock_irqsave(&mucse->link_lock, flags);
+	WRITE_ONCE(hw->link, false);
+	WRITE_ONCE(hw->speed, 0);
+	WRITE_ONCE(hw->duplex, 0);
+	atomic_set(&mucse->link_pending, 0);
+	/* echo fw driver now in default state */
+	mucse_hw_wr32(hw, RNPGBE_LINK_ST, M_DEFAULT_ST);
+	/* Keep the default snapshot and DOWN state transition atomic to
+	 * mailbox link-event handling.
+	 */
+	clear_bit(__MUCSE_DOWN, &mucse->state);
+	spin_unlock_irqrestore(&mucse->link_lock, flags);
+	rnpgbe_irq_enable(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_err(&hw->pdev->dev,
+			"Failed to notify firmware that port is up: %d\n", err);
+		return err;
+	}
+	/* Firmware checks RNPGBE_LINK_ST (driver's last-received link state)
+	 * and only asserts LINK_CHANGE_EVT when it differs from the actual link
+	 * state AND link_report_en is true.
+	 */
+	err = rnpgbe_send_notify(hw, true, mucse_fw_link_report_en);
+	if (err) {
+		dev_err(&hw->pdev->dev,
+			"Failed to enable firmware link reporting: %d\n",
+			 err);
+		return err;
+	}
+
+	queue_delayed_work(system_percpu_wq, &mucse->serv_task,
+			   msecs_to_jiffies(500));
+
+	return 0;
+}
+
+/**
+ * rnpgbe_free_tx_resources - Free Tx Resources per Queue
+ * @tx_ring: tx descriptor ring for a specific queue
+ *
+ * Free all transmit software resources
+ **/
+static void rnpgbe_free_tx_resources(struct mucse_ring *tx_ring)
+{
+	vfree(tx_ring->tx_buffer_info);
+	tx_ring->tx_buffer_info = NULL;
+	/* if not set, then don't free */
+	if (!tx_ring->desc)
+		return;
+
+	dma_free_coherent(tx_ring->dev, tx_ring->size, tx_ring->desc,
+			  tx_ring->dma);
+	tx_ring->desc = NULL;
+}
+
+/**
+ * rnpgbe_setup_tx_resources - allocate Tx resources (Descriptors)
+ * @tx_ring: tx descriptor ring (for a specific queue) to setup
+ * @mucse: pointer to private structure
+ *
+ * Return: 0 on success, negative on failure
+ **/
+static int rnpgbe_setup_tx_resources(struct mucse_ring *tx_ring,
+				     struct mucse *mucse)
+{
+	struct device *dev = tx_ring->dev;
+	int size;
+
+	size = sizeof(struct mucse_tx_buffer) * tx_ring->count;
+
+	tx_ring->tx_buffer_info = vzalloc(size);
+	if (!tx_ring->tx_buffer_info)
+		goto err_return;
+	/* round up to nearest 4K */
+	tx_ring->size = tx_ring->count * sizeof(struct rnpgbe_tx_desc);
+	tx_ring->size = ALIGN(tx_ring->size, 4096);
+	tx_ring->desc = dma_alloc_coherent(dev, tx_ring->size, &tx_ring->dma,
+					   GFP_KERNEL);
+	if (!tx_ring->desc)
+		goto err_free_buffer;
+
+	tx_ring->next_to_use = 0;
+	tx_ring->next_to_clean = 0;
+
+	return 0;
+
+err_free_buffer:
+	vfree(tx_ring->tx_buffer_info);
+err_return:
+	tx_ring->tx_buffer_info = NULL;
+	return -ENOMEM;
+}
+
+/**
+ * rnpgbe_configure_tx_ring - Configure Tx ring after Reset
+ * @mucse: pointer to private structure
+ * @ring: structure containing ring specific data
+ *
+ * Configure the Tx descriptor ring after a reset.
+ **/
+static void rnpgbe_configure_tx_ring(struct mucse *mucse,
+				     struct mucse_ring *ring)
+{
+	struct mucse_hw *hw = &mucse->hw;
+
+	mucse_ring_wr32(ring, RNPGBE_TX_BASE_ADDR_LO, (u32)ring->dma);
+	mucse_ring_wr32(ring, RNPGBE_TX_BASE_ADDR_HI,
+			(u32)(((u64)ring->dma) >> 32) | (hw->pfvfnum << 24));
+	mucse_ring_wr32(ring, RNPGBE_TX_LEN, ring->count);
+	ring->next_to_clean = mucse_ring_rd32(ring, RNPGBE_TX_HEAD) %
+			      ring->count;
+	ring->next_to_use = ring->next_to_clean;
+	ring->tail = ring->ring_addr + RNPGBE_TX_TAIL;
+	writel(ring->next_to_use, ring->tail);
+	mucse_ring_wr32(ring, RNPGBE_TX_FETCH_CTRL, M_DEFAULT_TX_FETCH);
+	mucse_ring_wr32(ring, RNPGBE_TX_INT_TIMER,
+			M_DEFAULT_INT_TIMER * hw->cycles_per_us);
+	mucse_ring_wr32(ring, RNPGBE_TX_INT_PKTCNT, M_DEFAULT_INT_PKTCNT);
+}
+
+/**
+ * rnpgbe_configure_tx - Configure Transmit Unit after Reset
+ * @mucse: pointer to private structure
+ *
+ * Configure the Tx DMA after a reset.
+ **/
+void rnpgbe_configure_tx(struct mucse *mucse)
+{
+	struct mucse_hw *hw = &mucse->hw;
+	u32 i, dma_axi_ctl;
+
+	for (i = 0; i < mucse->num_tx_queues; i++)
+		rnpgbe_stop_tx_ring(mucse->tx_ring[i]);
+	if (mucse->num_tx_queues)
+		rnpgbe_wait_tx_dma_idle(mucse);
+
+	dma_axi_ctl = mucse_hw_rd32(hw, RNPGBE_DMA_AXI_EN);
+	dma_axi_ctl &= ~TX_AXI_RW_EN;
+	mucse_hw_wr32(hw, RNPGBE_DMA_AXI_EN, dma_axi_ctl);
+	/* Flush the posted write before programming the ring registers. */
+	(void)mucse_hw_rd32(hw, RNPGBE_DMA_AXI_EN);
+
+	for (i = 0; i < mucse->num_tx_queues; i++)
+		rnpgbe_configure_tx_ring(mucse, mucse->tx_ring[i]);
+
+	/* Ensure all ring configuration is visible before enabling TX DMA. */
+	wmb();
+	dma_axi_ctl |= TX_AXI_RW_EN;
+	mucse_hw_wr32(hw, RNPGBE_DMA_AXI_EN, dma_axi_ctl);
+	/* Ensure TX_AXI_RW_EN is visible before starting the rings. */
+	(void)mucse_hw_rd32(hw, RNPGBE_DMA_AXI_EN);
+
+	for (i = 0; i < mucse->num_tx_queues; i++)
+		mucse_ring_wr32(mucse->tx_ring[i], RNPGBE_TX_START, 1);
+}
+
+/**
+ * rnpgbe_setup_all_tx_resources - allocate all queues Tx resources
+ * @mucse: pointer to private structure
+ *
+ * Allocate memory for tx_ring.
+ *
+ * Return: 0 on success, negative on failure
+ **/
+int rnpgbe_setup_all_tx_resources(struct mucse *mucse)
+{
+	int i, err = 0;
+
+	for (i = 0; i < mucse->num_tx_queues; i++) {
+		err = rnpgbe_setup_tx_resources(mucse->tx_ring[i], mucse);
+		if (!err)
+			continue;
+
+		goto err_free_res;
+	}
+
+	return 0;
+err_free_res:
+	while (i--)
+		rnpgbe_free_tx_resources(mucse->tx_ring[i]);
+	return err;
+}
+
+/**
+ * rnpgbe_free_all_tx_resources - Free Tx Resources for All Queues
+ * @mucse: pointer to private structure
+ *
+ * Free all transmit software resources
+ **/
+void rnpgbe_free_all_tx_resources(struct mucse *mucse)
+{
+	for (int i = 0; i < (mucse->num_tx_queues); i++)
+		rnpgbe_free_tx_resources(mucse->tx_ring[i]);
+}
+
+static int rnpgbe_tx_map(struct mucse_ring *tx_ring,
+			 struct mucse_tx_buffer *first, u32 mac_ip_len,
+			 u32 tx_flags)
+{
+	struct mucse_tx_buffer *tx_buffer;
+	struct rnpgbe_tx_desc *tx_desc;
+	struct skb_shared_info *shinfo;
+	unsigned int data_len, size;
+	struct sk_buff *skb;
+	skb_frag_t *frag;
+	dma_addr_t dma;
+	int nr_frags;
+	int frag_idx;
+	/* Hardware requires this in the upper 8 bits of
+	 * the descriptor address
+	 */
+	u64 fun_id;
+	u16 i;
+
+	skb = first->skb;
+	shinfo = skb_shinfo(skb);
+	fun_id = (u64)tx_ring->pfvfnum << 56;
+	nr_frags = shinfo->nr_frags;
+	i = tx_ring->next_to_use;
+	frag_idx = 0;
+	tx_desc = M_TX_DESC(tx_ring, i);
+	size = skb_headlen(skb);
+	data_len = skb->data_len;
+
+	if (size) {
+		dma = dma_map_single(tx_ring->dev, skb->data, size,
+				     DMA_TO_DEVICE);
+		first->mapped_as_page = false;
+	} else if (data_len) {
+		while (frag_idx < nr_frags &&
+		       !skb_frag_size(&shinfo->frags[frag_idx]))
+			frag_idx++;
+
+		if (frag_idx == nr_frags)
+			goto err_unmap;
+
+		frag = &shinfo->frags[frag_idx];
+		size = skb_frag_size(frag);
+
+		dma = skb_frag_dma_map(tx_ring->dev, frag, 0,
+				       size, DMA_TO_DEVICE);
+		first->mapped_as_page = true;
+		data_len -= size;
+		frag_idx++;
+	} else {
+		goto err_unmap;
+	}
+
+	tx_buffer = first;
+
+	dma_unmap_len_set(tx_buffer, len, 0);
+	dma_unmap_addr_set(tx_buffer, dma, 0);
+
+	for (;; frag_idx++) {
+		if (dma_mapping_error(tx_ring->dev, dma))
+			goto err_unmap;
+
+		/* record length, and DMA address */
+		dma_unmap_len_set(tx_buffer, len, size);
+		dma_unmap_addr_set(tx_buffer, dma, dma);
+
+		tx_desc->pkt_addr = cpu_to_le64(dma | fun_id);
+
+		while (unlikely(size > M_MAX_DATA_PER_TXD)) {
+			tx_desc->vlan_cmd_bsz = build_ctob(tx_flags,
+							   mac_ip_len,
+							   M_MAX_DATA_PER_TXD);
+			i++;
+			tx_desc++;
+			if (i == tx_ring->count) {
+				tx_desc = M_TX_DESC(tx_ring, 0);
+				i = 0;
+			}
+
+			tx_buffer = &tx_ring->tx_buffer_info[i];
+			dma_unmap_len_set(tx_buffer, len, 0);
+			dma += M_MAX_DATA_PER_TXD;
+			size -= M_MAX_DATA_PER_TXD;
+			tx_desc->pkt_addr = cpu_to_le64(dma | fun_id);
+		}
+
+		if (likely(!data_len))
+			break;
+		tx_desc->vlan_cmd_bsz = build_ctob(tx_flags, mac_ip_len, size);
+		i++;
+		tx_desc++;
+		if (i == tx_ring->count) {
+			tx_desc = M_TX_DESC(tx_ring, 0);
+			i = 0;
+		}
+
+		while (frag_idx < nr_frags &&
+		       !skb_frag_size(&shinfo->frags[frag_idx]))
+			frag_idx++;
+
+		if (frag_idx == nr_frags)
+			goto err_unmap;
+
+		frag = &shinfo->frags[frag_idx];
+		size = skb_frag_size(frag);
+		data_len -= size;
+		dma = skb_frag_dma_map(tx_ring->dev, frag, 0, size,
+				       DMA_TO_DEVICE);
+		tx_buffer = &tx_ring->tx_buffer_info[i];
+		tx_buffer->mapped_as_page = true;
+	}
+
+	/* write last descriptor with RS and EOP bits */
+	tx_desc->vlan_cmd_bsz = build_ctob(tx_flags | M_TXD_CMD_EOP |
+					   M_TXD_CMD_RS,
+					   mac_ip_len, size);
+
+	/* Force memory writes to complete before letting h/w know there
+	 * are new descriptors to fetch.  (Only applicable for weak-ordered
+	 * memory model archs, such as IA-64).
+	 *
+	 * We also need this memory barrier to make certain all of the
+	 * status bits have been updated before next_to_watch is written.
+	 */
+	dma_wmb();
+	/* set next_to_watch value indicating a packet is present */
+	first->next_to_watch = tx_desc;
+	i++;
+	if (i == tx_ring->count)
+		i = 0;
+	tx_ring->next_to_use = i;
+	skb_tx_timestamp(skb);
+	netdev_tx_sent_queue(txring_txq(tx_ring), first->bytecount);
+	/* notify HW of packet */
+	writel(i, tx_ring->tail);
+
+	return 0;
+err_unmap:
+	for (;;) {
+		tx_buffer = &tx_ring->tx_buffer_info[i];
+		if (dma_unmap_len(tx_buffer, len)) {
+			if (tx_buffer->mapped_as_page) {
+				dma_unmap_page(tx_ring->dev,
+					       dma_unmap_addr(tx_buffer, dma),
+					       dma_unmap_len(tx_buffer, len),
+					       DMA_TO_DEVICE);
+			} else {
+				dma_unmap_single(tx_ring->dev,
+						 dma_unmap_addr(tx_buffer, dma),
+						 dma_unmap_len(tx_buffer, len),
+						 DMA_TO_DEVICE);
+			}
+		}
+		dma_unmap_len_set(tx_buffer, len, 0);
+		dma_unmap_addr_set(tx_buffer, dma, 0);
+		if (tx_buffer == first)
+			break;
+		if (i == 0)
+			i += tx_ring->count;
+		i--;
+	}
+	dev_kfree_skb_any(first->skb);
+	first->skb = NULL;
+	tx_ring->next_to_use = i;
+
+	return -ENOMEM;
+}
+
+netdev_tx_t rnpgbe_xmit_frame_ring(struct sk_buff *skb,
+				   struct mucse_ring *tx_ring)
+{
+	u16 count;
+	/* hw requires non-zero mac_ip_len even without offload;
+	 * Will be updated per-packet when offload is added
+	 */
+	u32 mac_ip_len = M_DEFAULT_MAC_IP_LEN;
+	struct mucse_tx_buffer *first;
+	u32 tx_flags = 0;
+	unsigned short f;
+
+	count = TXD_USE_COUNT(skb_headlen(skb));
+	for (f = 0; f < skb_shinfo(skb)->nr_frags; f++) {
+		skb_frag_t *frag_temp = &skb_shinfo(skb)->frags[f];
+
+		count += TXD_USE_COUNT(skb_frag_size(frag_temp));
+	}
+
+	if (!netif_txq_maybe_stop(txring_txq(tx_ring),
+				  mucse_desc_unused(tx_ring),
+				  count + RESV_DESC_NEEDED,
+				  count + RESV_DESC_NEEDED))
+		return NETDEV_TX_BUSY;
+
+	/* record the location of the first descriptor for this packet */
+	first = &tx_ring->tx_buffer_info[tx_ring->next_to_use];
+	first->skb = skb;
+	first->bytecount = skb->len;
+	first->gso_segs = 1;
+
+	if (rnpgbe_tx_map(tx_ring, first, mac_ip_len, tx_flags)) {
+		atomic64_inc(&tx_ring->stats.dropped);
+
+		goto out;
+	}
+
+	/* NETDEV_TX_BUSY is expensive. So stop advancing the TX queue. */
+	netif_txq_maybe_stop(txring_txq(tx_ring),
+			     mucse_desc_unused(tx_ring),
+			     DESC_NEEDED, DESC_NEEDED);
+out:
+	return NETDEV_TX_OK;
+}
+
+/**
+ * rnpgbe_get_stats64 - Get stats for this netdev
+ * @netdev: network interface device structure
+ * @stats: stats data
+ **/
+void rnpgbe_get_stats64(struct net_device *netdev,
+			struct rtnl_link_stats64 *stats)
+{
+	struct mucse *mucse = netdev_priv(netdev);
+	int i;
+
+	rcu_read_lock();
+	for (i = 0; i < mucse->num_tx_queues; i++) {
+		struct mucse_ring *ring = READ_ONCE(mucse->tx_ring[i]);
+		u64 bytes, packets, dropped;
+		unsigned int start;
+
+		if (ring) {
+			do {
+				start = u64_stats_fetch_begin(&ring->syncp);
+				packets = ring->stats.packets;
+				bytes = ring->stats.bytes;
+			} while (u64_stats_fetch_retry(&ring->syncp, start));
+			dropped = atomic64_read(&ring->stats.dropped);
+
+			stats->tx_packets += packets;
+			stats->tx_dropped += dropped;
+			stats->tx_bytes += bytes;
+		}
+	}
+
+	for (i = 0; i < mucse->num_rx_queues; i++) {
+		struct mucse_ring *ring = READ_ONCE(mucse->rx_ring[i]);
+		u64 bytes, packets, dropped;
+		unsigned int start;
+
+		if (ring) {
+			do {
+				start = u64_stats_fetch_begin(&ring->syncp);
+				packets = ring->stats.packets;
+				bytes = ring->stats.bytes;
+			} while (u64_stats_fetch_retry(&ring->syncp, start));
+			dropped = atomic64_read(&ring->stats.dropped);
+
+			stats->rx_packets += packets;
+			stats->rx_dropped += dropped;
+			stats->rx_bytes += bytes;
+		}
+	}
+	rcu_read_unlock();
+}
+
+static int mucse_alloc_page_pool(struct mucse_ring *rx_ring)
+{
+	int ret = 0;
+
+	struct page_pool_params pp_params = {
+		.flags = PP_FLAG_DMA_MAP | PP_FLAG_DMA_SYNC_DEV,
+		.order = 0,
+		.pool_size = rx_ring->count,
+		.nid = dev_to_node(rx_ring->dev),
+		.dev = rx_ring->dev,
+		.dma_dir = DMA_FROM_DEVICE,
+		.offset = 0,
+		.max_len = PAGE_SIZE,
+	};
+
+	rx_ring->page_pool = page_pool_create(&pp_params);
+	if (IS_ERR(rx_ring->page_pool)) {
+		ret = PTR_ERR(rx_ring->page_pool);
+		rx_ring->page_pool = NULL;
+	}
+
+	return ret;
+}
+
+/**
+ * rnpgbe_setup_rx_resources - allocate Rx resources (Descriptors)
+ * @rx_ring:    rx descriptor ring (for a specific queue) to setup
+ * @mucse: pointer to private structure
+ *
+ * Return: 0 on success, negative on failure
+ **/
+static int rnpgbe_setup_rx_resources(struct mucse_ring *rx_ring,
+				     struct mucse *mucse)
+{
+	struct device *dev = rx_ring->dev;
+	int size;
+
+	size = sizeof(struct mucse_rx_buffer) * rx_ring->count;
+
+	rx_ring->rx_buffer_info = vzalloc(size);
+
+	if (!rx_ring->rx_buffer_info)
+		goto err_return;
+	/* Round up to nearest 4K */
+	rx_ring->size = rx_ring->count * sizeof(union rnpgbe_rx_desc);
+	rx_ring->size = ALIGN(rx_ring->size, 4096);
+	rx_ring->desc = dma_alloc_coherent(dev, rx_ring->size, &rx_ring->dma,
+					   GFP_KERNEL);
+	if (!rx_ring->desc)
+		goto err_free_buffer;
+
+	rx_ring->next_to_clean = 0;
+	rx_ring->next_to_use = 0;
+
+	if (mucse_alloc_page_pool(rx_ring))
+		goto err_free_desc;
+
+	return 0;
+err_free_desc:
+	dma_free_coherent(dev, rx_ring->size, rx_ring->desc,
+			  rx_ring->dma);
+	rx_ring->desc = NULL;
+err_free_buffer:
+	vfree(rx_ring->rx_buffer_info);
+err_return:
+	rx_ring->rx_buffer_info = NULL;
+	return -ENOMEM;
+}
+
+/**
+ * rnpgbe_clean_rx_ring - Free Rx Buffers per Queue
+ * @rx_ring: ring to free buffers from
+ **/
+void rnpgbe_clean_rx_ring(struct mucse_ring *rx_ring)
+{
+	struct mucse_rx_buffer *rx_buffer;
+	u16 i;
+
+	/* ring already cleared, nothing to do */
+	if (!rx_ring->rx_buffer_info)
+		return;
+	/* Free all the Rx ring sk_buffs */
+	for (i = 0; i < rx_ring->count; i++) {
+		rx_buffer = &rx_ring->rx_buffer_info[i];
+
+		if (rx_buffer->skb) {
+			struct sk_buff *skb = rx_buffer->skb;
+
+			dev_kfree_skb(skb);
+			rx_buffer->skb = NULL;
+		}
+
+		if (rx_buffer->page) {
+			page_pool_put_full_page(rx_ring->page_pool,
+						rx_buffer->page, false);
+			rx_buffer->page = NULL;
+		}
+	}
+
+	rx_ring->next_to_clean = 0;
+	rx_ring->next_to_use = 0;
+}
+
+/**
+ * rnpgbe_free_rx_resources - Free Rx Resources
+ * @rx_ring: ring to clean the resources from
+ *
+ * Free all receive software resources
+ **/
+static void rnpgbe_free_rx_resources(struct mucse_ring *rx_ring)
+{
+	vfree(rx_ring->rx_buffer_info);
+	rx_ring->rx_buffer_info = NULL;
+	/* if not set, then don't free */
+	if (!rx_ring->desc)
+		return;
+
+	dma_free_coherent(rx_ring->dev, rx_ring->size, rx_ring->desc,
+			  rx_ring->dma);
+	rx_ring->desc = NULL;
+	if (rx_ring->page_pool) {
+		page_pool_destroy(rx_ring->page_pool);
+		rx_ring->page_pool = NULL;
+	}
+}
+
+/**
+ * rnpgbe_setup_all_rx_resources - allocate all queues Rx resources
+ * @mucse: pointer to private structure
+ *
+ * Return: 0 on success, negative on failure
+ **/
+int rnpgbe_setup_all_rx_resources(struct mucse *mucse)
+{
+	int i, err = 0;
+
+	for (i = 0; i < mucse->num_rx_queues; i++) {
+		err = rnpgbe_setup_rx_resources(mucse->rx_ring[i], mucse);
+		if (!err)
+			continue;
+
+		goto err_setup_rx;
+	}
+
+	return 0;
+err_setup_rx:
+	while (i--)
+		rnpgbe_free_rx_resources(mucse->rx_ring[i]);
+	return err;
+}
+
+/**
+ * rnpgbe_free_all_rx_resources - Free Rx Resources for All Queues
+ * @mucse: pointer to private structure
+ *
+ * Free all receive software resources
+ **/
+void rnpgbe_free_all_rx_resources(struct mucse *mucse)
+{
+	int i;
+
+	for (i = 0; i < mucse->num_rx_queues; i++)
+		rnpgbe_free_rx_resources(mucse->rx_ring[i]);
+}
+
+/**
+ * rnpgbe_configure_rx_ring - Configure Rx ring info to hw
+ * @mucse: pointer to private structure
+ * @ring: structure containing ring specific data
+ *
+ * Configure the Rx descriptor ring after a reset.
+ **/
+static void rnpgbe_configure_rx_ring(struct mucse *mucse,
+				     struct mucse_ring *ring)
+{
+	struct mucse_hw *hw = &mucse->hw;
+
+	/* Stop hw. hardware design guarantees:
+	 * - No new descriptors will be fetched after RX_START=0
+	 * - No DMA will be initiated for already-fetched descriptors
+	 */
+	mucse_ring_wr32(ring, RNPGBE_RX_START, 0);
+	/* Flush posted write to ensure hardware sees RX_START=0 */
+	(void)mucse_ring_rd32(ring, RNPGBE_RX_START);
+	/* Wait for in-flight DMA to quiesce */
+	usleep_range(300, 500);
+	/* Set the descriptor registers */
+	mucse_ring_wr32(ring, RNPGBE_RX_BASE_ADDR_LO, (u32)ring->dma);
+	mucse_ring_wr32(ring, RNPGBE_RX_BASE_ADDR_HI,
+			(u32)((u64)ring->dma >> 32) | (hw->pfvfnum << 24));
+	mucse_ring_wr32(ring, RNPGBE_RX_LEN, ring->count);
+	ring->tail = ring->ring_addr + RNPGBE_RX_TAIL;
+	ring->next_to_clean = mucse_ring_rd32(ring, RNPGBE_RX_HEAD) %
+			      ring->count;
+	ring->next_to_use = ring->next_to_clean;
+	ring->drop_status = false;
+	mucse_ring_wr32(ring, RNPGBE_RX_SG_LEN, M_DEFAULT_SG);
+	mucse_ring_wr32(ring, RNPGBE_RX_FETCH, M_DEFAULT_RX_FETCH);
+	mucse_ring_wr32(ring, RNPGBE_RX_TIMEOUT_TH, 0);
+	mucse_ring_wr32(ring, RNPGBE_RX_INT_TIMER,
+			M_DEFAULT_INT_TIMER_R * hw->cycles_per_us);
+	mucse_ring_wr32(ring, RNPGBE_RX_INT_PKTCNT, M_DEFAULT_RX_INT_PKTCNT);
+	if (rnpgbe_alloc_rx_buffers(ring, mucse_desc_unused_rx(ring))) {
+		mod_timer(&ring->q_vector->rx_alloc_timer,
+			  jiffies + msecs_to_jiffies(500));
+	}
+}
+
+/**
+ * rnpgbe_configure_rx - Configure Receive Unit after Reset
+ * @mucse: pointer to private structure
+ *
+ * Configure the Rx unit after a reset.
+ **/
+void rnpgbe_configure_rx(struct mucse *mucse)
+{
+	struct mucse_hw *hw = &mucse->hw;
+	u32 dma_axi_ctl;
+
+	for (int i = 0; i < mucse->num_rx_queues; i++)
+		rnpgbe_configure_rx_ring(mucse, mucse->rx_ring[i]);
+
+	dma_axi_ctl = mucse_hw_rd32(hw, RNPGBE_DMA_AXI_EN);
+	dma_axi_ctl |= RX_AXI_RW_EN;
+	mucse_hw_wr32(hw, RNPGBE_DMA_AXI_EN, dma_axi_ctl);
+}
+
+/**
+ * rnpgbe_process_link_event - Consume a pending link event
+ * @mucse: pointer to the device private structure
+ * @link: link status snapshot
+ * @speed: link speed snapshot
+ * @duplex: link duplex snapshot
+ *
+ * Return: true if a link event was consumed, false otherwise
+ **/
+static bool rnpgbe_process_link_event(struct mucse *mucse, bool *link,
+				      int *speed, u8 *duplex)
+{
+	struct mucse_hw *hw = &mucse->hw;
+	unsigned long flags;
+
+	if (!atomic_xchg(&mucse->link_pending, 0))
+		return false;
+
+	spin_lock_irqsave(&mucse->link_lock, flags);
+	*link = hw->link;
+	*speed = hw->speed;
+	*duplex = hw->duplex;
+	spin_unlock_irqrestore(&mucse->link_lock, flags);
+
+	return true;
+}
+
+/**
+ * rnpgbe_link_is_up - Update netif_carrier status and
+ * print link up message
+ * @mucse: pointer to the device private structure
+ * @speed: link speed snapshot
+ * @duplex: link duplex snapshot
+ **/
+static void rnpgbe_link_is_up(struct mucse *mucse, int speed, u8 duplex)
+{
+	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;
+
+	netdev_info(netdev, "NIC Link is Up %d Mbps, %s Duplex\n",
+		    speed, duplex ? "Full" : "Half");
+	rnpgbe_set_link(hw, true);
+	netif_carrier_on(netdev);
+}
+
+/**
+ * rnpgbe_link_is_down - Update netif_carrier status and
+ * print link down message
+ * @mucse: pointer to the private structure
+ **/
+static void rnpgbe_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_link(hw, false);
+	netif_carrier_off(netdev);
+}
+
+/**
+ * rnpgbe_process_link_subtask - Process a link-state update
+ * @mucse: pointer to the device private structure
+ **/
+static void rnpgbe_process_link_subtask(struct mucse *mucse)
+{
+	bool link;
+	int speed;
+	u8 duplex;
+
+	/* if interface is down do nothing */
+	if (test_bit(__MUCSE_DOWN, &mucse->state))
+		return;
+
+	if (!rnpgbe_process_link_event(mucse, &link, &speed, &duplex))
+		return;
+
+	if (link)
+		rnpgbe_link_is_up(mucse, speed, duplex);
+	else
+		rnpgbe_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_process_link_subtask(mucse);
+
+	if (!test_bit(__MUCSE_DOWN, &mucse->state))
+		queue_delayed_work(system_percpu_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
new file mode 100644
index 0000000000000..5b93c1183c524
--- /dev/null
+++ b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.h
@@ -0,0 +1,87 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright(c) 2020 - 2025 Mucse Corporation. */
+
+#ifndef _RNPGBE_LIB_H
+#define _RNPGBE_LIB_H
+
+struct mucse;
+struct mucse_hw;
+struct mucse_ring;
+
+#define RING_OFFSET(n)            (0x1000 + 0x100 * (n))
+#define RNPGBE_RX_START           0x10
+#define RNPGBE_TX_START           0x18
+#define RNPGBE_DMA_INT_MASK       0x24
+#define TX_INT_MASK               BIT(1)
+#define RX_INT_MASK               BIT(0)
+#define INT_VALID                 (BIT(16) | BIT(17))
+#define RNPGBE_DMA_INT_TRIG       0x2c /* lost-interrupt recovery trigger */
+#define RNPGBE_RX_BASE_ADDR_HI    0x30
+#define RNPGBE_RX_BASE_ADDR_LO    0x34
+#define RNPGBE_RX_LEN             0x38
+#define RNPGBE_RX_HEAD            0x3c
+#define RNPGBE_RX_TAIL            0x40
+#define M_DEFAULT_RX_FETCH        0x100020
+#define RNPGBE_RX_FETCH           0x44
+#define M_DEFAULT_INT_TIMER_R     30
+#define RNPGBE_RX_INT_TIMER       0x48
+#define M_DEFAULT_RX_INT_PKTCNT   64
+#define RNPGBE_RX_INT_PKTCNT      0x4c
+#define RNPGBE_RX_ARB_DEF_LVL     0x50
+#define RNPGBE_RX_TIMEOUT_TH      0x54
+#define M_DEFAULT_SG              96 /* unit 16b, 1536 bytes */
+#define RNPGBE_RX_SG_LEN          0x58
+#define RNPGBE_TX_BASE_ADDR_HI    0x60
+#define RNPGBE_TX_BASE_ADDR_LO    0x64
+#define RNPGBE_TX_LEN             0x68
+#define RNPGBE_TX_HEAD            0x6c
+#define RNPGBE_TX_TAIL            0x70
+#define M_DEFAULT_TX_FETCH        0x80008
+#define RNPGBE_TX_FETCH_CTRL      0x74
+#define M_DEFAULT_INT_TIMER       100
+#define RNPGBE_TX_INT_TIMER       0x78
+#define M_DEFAULT_INT_PKTCNT      48
+#define RNPGBE_TX_INT_PKTCNT      0x7c
+/* |  31:24   | .... |    15:8   |    7:0    | */
+/* |  pfvfnum |      | tx vector | rx vector | */
+#define RING_VECTOR(n)            (0x04 * (n))
+
+#define M_MAX_TXD_PWR             12
+#define M_MAX_DATA_PER_TXD        (0x1 << M_MAX_TXD_PWR)
+#define TXD_USE_COUNT(S)          DIV_ROUND_UP((S), M_MAX_DATA_PER_TXD)
+#define DESC_NEEDED               (MAX_SKB_FRAGS + 4)
+/* 2 desc gap to keep tail from touching head */
+/* 1 desc for context descriptor */
+#define RESV_DESC_NEEDED          3
+#define RNPGBE_SKB_PAD            (NET_SKB_PAD + NET_IP_ALIGN)
+#define M_RX_BUFFER_WRITE         16
+
+/* Hardware requires this to be nonzero */
+#define M_DEFAULT_MAC_IP_LEN      20
+#define mucse_for_each_ring(pos, head)\
+	for (typeof((head).ring) __pos = (head).ring;\
+	     __pos ? ({ pos = __pos; 1; }) : 0;\
+	     __pos = __pos->next)
+int rnpgbe_init_interrupt_scheme(struct mucse *mucse);
+void rnpgbe_clear_interrupt_scheme(struct mucse *mucse);
+int rnpgbe_request_mbx_irq(struct mucse *mucse);
+void rnpgbe_free_mbx_irq(struct mucse *mucse);
+int rnpgbe_request_irq(struct mucse *mucse);
+void rnpgbe_free_irq(struct mucse *mucse);
+void rnpgbe_irq_disable(struct mucse *mucse);
+bool rnpgbe_down(struct mucse *mucse);
+int rnpgbe_up_complete(struct mucse *mucse);
+void rnpgbe_configure_tx(struct mucse *mucse);
+void rnpgbe_configure_rx(struct mucse *mucse);
+void rnpgbe_clean_all_tx_rings(struct mucse *mucse);
+int rnpgbe_setup_all_tx_resources(struct mucse *mucse);
+void rnpgbe_free_all_tx_resources(struct mucse *mucse);
+netdev_tx_t rnpgbe_xmit_frame_ring(struct sk_buff *skb,
+				   struct mucse_ring *tx_ring);
+void rnpgbe_get_stats64(struct net_device *netdev,
+			struct rtnl_link_stats64 *stats);
+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 70a2b0082ba86..fcad8a16ef26b 100644
--- a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_main.c
+++ b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_main.c
@@ -2,11 +2,13 @@
 /* Copyright(c) 2020 - 2025 Mucse Corporation. */
 
 #include <linux/pci.h>
+#include <linux/skbuff.h>
 #include <net/rtnetlink.h>
 #include <linux/etherdevice.h>
 
 #include "rnpgbe.h"
 #include "rnpgbe_hw.h"
+#include "rnpgbe_lib.h"
 #include "rnpgbe_mbx_fw.h"
 
 static const char rnpgbe_driver_name[] = "rnpgbe";
@@ -25,6 +27,18 @@ static struct pci_device_id rnpgbe_pci_tbl[] = {
 	{ },
 };
 
+/**
+ * rnpgbe_configure - Configure the hardware
+ * @mucse: pointer to private structure
+ *
+ * rnpgbe_configure configure mac, tx, rx regs to hw
+ **/
+static void rnpgbe_configure(struct mucse *mucse)
+{
+	rnpgbe_configure_tx(mucse);
+	rnpgbe_configure_rx(mucse);
+}
+
 /**
  * rnpgbe_open - Called when a network interface is made active
  * @netdev: network interface device structure
@@ -32,11 +46,47 @@ static struct pci_device_id rnpgbe_pci_tbl[] = {
  * The open entry point is called when a network interface is made
  * active by the system (IFF_UP).
  *
- * Return: 0
+ * Return: 0 on success, negative value on failure
  **/
 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;
+
+	err = netif_set_real_num_queues(netdev, mucse->num_tx_queues,
+					mucse->num_rx_queues);
+	if (err)
+		goto err_free_irqs;
+
+	err = rnpgbe_setup_all_tx_resources(mucse);
+	if (err)
+		goto err_free_irqs;
+	err = rnpgbe_setup_all_rx_resources(mucse);
+	if (err)
+		goto err_free_tx;
+
+	rnpgbe_configure(mucse);
+	err = rnpgbe_up_complete(mucse);
+	if (err)
+		goto err_down;
+
 	return 0;
+err_down:
+	rnpgbe_down(mucse);
+	rnpgbe_free_all_rx_resources(mucse);
+	rnpgbe_free_all_tx_resources(mucse);
+	goto err_free_irqs;
+err_free_tx:
+	rnpgbe_clean_all_tx_rings(mucse);
+	rnpgbe_free_all_tx_resources(mucse);
+err_free_irqs:
+	rnpgbe_free_irq(mucse);
+	return err;
 }
 
 /**
@@ -50,6 +100,15 @@ static int rnpgbe_open(struct net_device *netdev)
  **/
 static int rnpgbe_close(struct net_device *netdev)
 {
+	struct mucse *mucse = netdev_priv(netdev);
+
+	if (!rnpgbe_down(mucse))
+		return 0;
+
+	rnpgbe_free_irq(mucse);
+	rnpgbe_free_all_tx_resources(mucse);
+	rnpgbe_free_all_rx_resources(mucse);
+
 	return 0;
 }
 
@@ -58,25 +117,38 @@ static int rnpgbe_close(struct net_device *netdev)
  * @skb: skb structure to be sent
  * @netdev: network interface device structure
  *
- * Return: NETDEV_TX_OK
+ * Return: NETDEV_TX_OK or NETDEV_TX_BUSY when insufficient descriptors
  **/
 static netdev_tx_t rnpgbe_xmit_frame(struct sk_buff *skb,
 				     struct net_device *netdev)
 {
 	struct mucse *mucse = netdev_priv(netdev);
+	struct mucse_ring *tx_ring;
+
+	tx_ring = mucse->tx_ring[skb_get_queue_mapping(skb)];
 
-	dev_kfree_skb_any(skb);
-	mucse->stats.tx_dropped++;
+	if (unlikely(skb_put_padto(skb, RNPGBE_TX_MIN_PKT_LEN))) {
+		atomic64_inc(&tx_ring->stats.dropped);
+		return NETDEV_TX_OK;
+	}
 
-	return NETDEV_TX_OK;
+	return rnpgbe_xmit_frame_ring(skb, tx_ring);
 }
 
 static const struct net_device_ops rnpgbe_netdev_ops = {
 	.ndo_open       = rnpgbe_open,
 	.ndo_stop       = rnpgbe_close,
 	.ndo_start_xmit = rnpgbe_xmit_frame,
+	.ndo_get_stats64 = rnpgbe_get_stats64,
 };
 
+static void rnpgbe_sw_init(struct mucse *mucse)
+{
+	mucse->tx_ring_item_count = M_DEFAULT_TXD;
+	mucse->rx_ring_item_count = M_DEFAULT_RXD;
+	mucse->tx_work_limit = M_DEFAULT_TX_WORK;
+}
+
 /**
  * rnpgbe_add_adapter - Add netdev for this pci_dev
  * @pdev: PCI device information structure
@@ -106,6 +178,7 @@ static int rnpgbe_add_adapter(struct pci_dev *pdev,
 	mucse = netdev_priv(netdev);
 	mucse->netdev = netdev;
 	mucse->pdev = pdev;
+	set_bit(__MUCSE_DOWN, &mucse->state);
 	pci_set_drvdata(pdev, mucse);
 
 	hw = &mucse->hw;
@@ -125,6 +198,7 @@ static int rnpgbe_add_adapter(struct pci_dev *pdev,
 		dev_err(&pdev->dev, "Init hw err %d\n", err);
 		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
@@ -149,6 +223,7 @@ static int rnpgbe_add_adapter(struct pci_dev *pdev,
 	}
 
 	netdev->netdev_ops = &rnpgbe_netdev_ops;
+	rnpgbe_sw_init(mucse);
 	err = rnpgbe_reset_hw(hw);
 	if (err) {
 		dev_err(&pdev->dev, "Hw reset failed %d\n", err);
@@ -166,11 +241,40 @@ 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);
+	atomic_set(&mucse->link_pending, 0);
+
+	err = rnpgbe_init_interrupt_scheme(mucse);
+	if (err) {
+		dev_err(&pdev->dev, "init interrupt failed %d\n", err);
+		goto err_powerdown;
+	}
+
+	err = rnpgbe_request_mbx_irq(mucse);
+	if (err) {
+		dev_err(&pdev->dev, "register mbx irq failed %d\n", err);
+		goto err_clear_interrupt;
+	}
+
+	netdev->features |= NETIF_F_SG;
+	netdev->hw_features |= NETIF_F_SG;
+	if (dma_get_mask(&pdev->dev) > DMA_BIT_MASK(32)) {
+		netdev->features |= NETIF_F_HIGHDMA;
+		netdev->hw_features |= NETIF_F_HIGHDMA;
+	}
+
+	netif_carrier_off(netdev);
 	err = register_netdev(netdev);
 	if (err)
-		goto err_powerdown;
+		goto err_remove_mbx;
 
 	return 0;
+
+err_remove_mbx:
+	rnpgbe_free_mbx_irq(mucse);
+err_clear_interrupt:
+	rnpgbe_clear_interrupt_scheme(mucse);
 err_powerdown:
 	/* notify powerdown only powerup ok */
 	if (!err_notify) {
@@ -203,10 +307,10 @@ static int rnpgbe_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 	if (err)
 		return err;
 
-	err = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(56));
+	err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(56));
 	if (err) {
-		dev_err(&pdev->dev,
-			"No usable DMA configuration, aborting %d\n", err);
+		dev_err(&pdev->dev, "No usable DMA configuration, aborting %d\n",
+			err);
 		goto err_disable_dev;
 	}
 
@@ -253,9 +357,11 @@ static void rnpgbe_rm_adapter(struct pci_dev *pdev)
 		return;
 	netdev = mucse->netdev;
 	unregister_netdev(netdev);
+	rnpgbe_free_mbx_irq(mucse);
 	err = rnpgbe_send_notify(hw, false, mucse_fw_powerup);
 	if (err)
 		dev_warn(&pdev->dev, "Send powerdown to hw failed %d\n", err);
+	rnpgbe_clear_interrupt_scheme(mucse);
 	free_netdev(netdev);
 }
 
diff --git a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx.c b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx.c
index c464086982632..185465d4e0dcd 100644
--- a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx.c
+++ b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx.c
@@ -84,7 +84,7 @@ static u32 mucse_mbx_get_lock_pf(struct mucse_hw *hw)
  * @hw: pointer to the HW structure
  *
  * Pair with mucse_release_mbx_lock_pf()
- * This function maybe used in an irq handler.
+ * All mailbox access runs in process context.
  *
  * Return: 0 on success, negative errno on failure
  **/
@@ -93,11 +93,11 @@ static int mucse_obtain_mbx_lock_pf(struct mucse_hw *hw)
 	struct mucse_mbx_info *mbx = &hw->mbx;
 	u32 val;
 
-	return read_poll_timeout_atomic(mucse_mbx_get_lock_pf,
-					val, val & MUCSE_MBX_PFU,
-					mbx->delay_us,
-					mbx->timeout_us,
-					false, hw);
+	return read_poll_timeout(mucse_mbx_get_lock_pf,
+				 val, val & MUCSE_MBX_PFU,
+				 mbx->delay_us,
+				 mbx->timeout_us,
+				 false, hw);
 }
 
 /**
@@ -252,6 +252,26 @@ int mucse_poll_and_read_mbx(struct mucse_hw *hw, __le32 *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, __le32 *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 75b88b18b04d3..3af008a67fb17 100644
--- a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx.h
+++ b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx.h
@@ -18,4 +18,5 @@ int mucse_write_and_wait_ack_mbx(struct mucse_hw *hw,
 				 const __le32 *msg, u16 size);
 void mucse_init_mbx_params_pf(struct mucse_hw *hw);
 int mucse_poll_and_read_mbx(struct mucse_hw *hw, __le32 *msg, u16 size);
+int mucse_check_and_read_mbx(struct mucse_hw *hw, __le32 *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 5ba74997beacf..441724f8603eb 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"
@@ -27,10 +28,14 @@ static int mucse_fw_send_cmd_wait_resp(struct mucse_hw *hw,
 	int retry_cnt = 3;
 	int err;
 
+	BUILD_BUG_ON(sizeof(struct mbx_fw_cmd_reply) != 56);
+
 	mutex_lock(&hw->mbx.lock);
+
 	err = mucse_write_and_wait_ack_mbx(hw, req->dwords, len);
 	if (err)
 		goto out;
+
 	do {
 		err = mucse_poll_and_read_mbx(hw, reply->dwords,
 					      sizeof(reply->r));
@@ -197,3 +202,204 @@ int mucse_mbx_get_macaddr(struct mucse_hw *hw, int pfvfnum,
 
 	return 0;
 }
+
+/**
+ * mucse_mbx_phyup - Request that firmware bring the PHY up or down
+ * @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)
+{
+	union mbx_fw_cmd_req_u req = {
+		.r = {
+			.datalen = cpu_to_le16(sizeof(req.r.phy_status) +
+					       MUCSE_MBX_REQ_HDR_LEN),
+			.opcode  = cpu_to_le16(SET_PHY_UP),
+			.phy_status = {
+				.port_mask = cpu_to_le32(BIT(hw->port)),
+				.status  = cpu_to_le32(is_phyup ? 1 : 0),
+			},
+		},
+	};
+	int len, err;
+
+	len = le16_to_cpu(req.r.datalen);
+	mutex_lock(&hw->mbx.lock);
+	err = mucse_write_and_wait_ack_mbx(hw, req.dwords, len);
+	mutex_unlock(&hw->mbx.lock);
+
+	return err;
+}
+
+/**
+ * mucse_mbx_link_report - Configure firmware link-change event reporting
+ * @hw: pointer to the HW structure
+ * @is_report: true for report, false for no
+ *
+ * mucse_mbx_link_report 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)
+{
+	union mbx_fw_cmd_req_u req = {
+		.r = {
+			.datalen = cpu_to_le16(sizeof(req.r.report_status) +
+					       MUCSE_MBX_REQ_HDR_LEN),
+			.opcode  = cpu_to_le16(LINK_REPORT_EN),
+			.report_status = {
+				.port_mask = cpu_to_le16(BIT(hw->port)),
+				.status  = cpu_to_le16(is_report ? 1 : 0),
+			},
+		},
+	};
+	int len, err;
+
+	len = le16_to_cpu(req.r.datalen);
+	mutex_lock(&hw->mbx.lock);
+	err = mucse_write_and_wait_ack_mbx(hw, req.dwords, 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
+ *
+ * Update the driver's link-state snapshot exported to firmware. Firmware
+ * sends a new event when this snapshot differs from the hardware state.
+ * The default snapshot clears the driver-reported fields;
+ * a valid event then repopulates them, including the LLDP status in bit 6.
+ *
+ **/
+static void mucse_update_link_status_reg(struct mucse_hw *hw,
+					 struct mbx_fw_cmd_req *req)
+{
+	u16 status = le16_to_cpu(req->link_stat.st.status);
+	u16 speed = le16_to_cpu(req->link_stat.st.speed);
+	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 (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:
+			break;
+		}
+
+		value |= FIELD_PREP(BIT(4),
+				    !!(req->link_stat.st.flags & DUPLEX_BIT));
+		value |= FIELD_PREP(GENMASK_U32(25, 24),
+				    status & GENMASK(1, 0));
+	} else {
+		value &= ~BIT(0);
+	}
+
+	if (status & ST_STATUS_LLDP_STATUS_MASK)
+		value |= BIT(6);
+	else
+		value &= ~BIT(6);
+
+	mucse_hw_wr32(hw, RNPGBE_LINK_ST, value);
+}
+
+/**
+ * mucse_mbx_fw_req_handler - Handle fw req
+ * @hw: pointer to the HW structure
+ * @req: pointer to req data
+ *
+ * mucse_mbx_fw_req_handler handler fw req, such as a link event req.
+ **/
+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 (magic != ST_VALID_MAGIC) {
+			/* Do not let an invalid event change the cached link state.
+			 * Reset the driver snapshot so firmware reports it again.
+			 */
+			mucse_hw_wr32(hw, RNPGBE_LINK_ST, M_DEFAULT_ST);
+			spin_unlock_irqrestore(&mucse->link_lock, flags);
+			return;
+		}
+
+		if (test_bit(__MUCSE_DOWN, &mucse->state)) {
+			mucse_update_link_status_reg(hw, req);
+			spin_unlock_irqrestore(&mucse->link_lock, flags);
+			return;
+		}
+
+		if (le16_to_cpu(req->link_stat.port_status))
+			WRITE_ONCE(hw->link, true);
+		else
+			WRITE_ONCE(hw->link, false);
+
+		WRITE_ONCE(hw->speed, le16_to_cpu(req->link_stat.st.speed));
+		WRITE_ONCE(hw->duplex, req->link_stat.st.flags & DUPLEX_BIT);
+		/* update regs to notify link info is received */
+		mucse_update_link_status_reg(hw, req);
+		atomic_set_release(&mucse->link_pending, 1);
+		spin_unlock_irqrestore(&mucse->link_lock, flags);
+	}
+}
+
+/**
+ * mucse_fw_handle_event - Handle one pending firmware event
+ * @hw: pointer to the hardware structure
+ *
+ * Return: true if an event was handled, false otherwise
+ **/
+static bool mucse_fw_handle_event(struct mucse_hw *hw)
+{
+	union mbx_fw_cmd_u msg = {};
+	int err;
+
+	/* try to check and read fw req */
+	mutex_lock(&hw->mbx.lock);
+	err = mucse_check_and_read_mbx(hw, msg.dwords, sizeof(msg));
+	mutex_unlock(&hw->mbx.lock);
+	if (err)
+		return false;
+
+	mucse_mbx_fw_req_handler(hw, &msg.req);
+
+	return true;
+}
+
+/**
+ * mucse_fw_irq_handler - Drain pending firmware mailbox events
+ * @hw: pointer to the HW structure
+ *
+ * The only asynchronous event currently handled is LINK_CHANGE_EVT.
+ * Firmware rate-limits link-change notifications to a minimum interval
+ * of 500 ms.
+ **/
+void mucse_fw_irq_handler(struct mucse_hw *hw)
+{
+	/* Drain events coalesced while the mailbox work item was pending. */
+	while (mucse_fw_handle_event(hw))
+		continue;
+}
diff --git a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx_fw.h b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx_fw.h
index fe996aeffc4de..0e6972b7c3c02 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,16 @@ struct mucse_hw_info {
 	__le32 ext_info;
 } __packed;
 
+#define ST_STATUS_LLDP_STATUS_MASK        BIT(12)
+
+#define DUPLEX_BIT                        BIT(0)
+struct st_status {
+	u8 phyid;
+	u8 flags;
+	__le16 speed;
+	__le16 status;
+} __packed;
+
 struct mbx_fw_cmd_req {
 	__le16 flags;
 	__le16 opcode;
@@ -55,10 +68,30 @@ struct mbx_fw_cmd_req {
 			__le32 port_mask;
 			__le32 pfvf_num;
 		} get_mac_addr;
+		struct {
+			__le32 port_mask;
+			__le32 status;
+		} phy_status;
+		struct {
+			/* LINK_REPORT_EN uses its own 16-bit payload layout:
+			 * status precedes port_mask.
+			 */
+			__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;
+		} link_stat;
 	};
 } __packed;
 
 struct mbx_fw_cmd_reply {
+#define FLAGS_REPLY       BIT(0)
+#define FLAGS_ERR         BIT(2)
 	__le16 flags;
 	__le16 opcode;
 	__le16 error_code;
@@ -94,9 +127,27 @@ union mbx_fw_cmd_reply_u {
 	__le32 dwords[sizeof(struct mbx_fw_cmd_reply) / sizeof(__le32)];
 };
 
+union mbx_fw_cmd_u {
+	struct mbx_fw_cmd_req req;
+	struct mbx_fw_cmd_reply reply;
+	__le32 dwords[sizeof(struct mbx_fw_cmd_reply) / sizeof(__le32)];
+};
+
+/* Firmware encoding for RNPGBE_LINK_ST[11:8]. Valid link events report
+ * only these three speeds.
+ */
+enum mucse_speed {
+	mucse_speed_10 = 0,
+	mucse_speed_100 = 1,
+	mucse_speed_1000 = 2,
+};
+
 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 */

3/2 2026/08/14 13:02 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 42671
							output: 16
							thoughts: 868
						

Thoughts:
**Mucse Driver Analysis for Fuzzing Potential**

Okay, so I've got this patch series introducing new functionality to the `mucse` Ethernet driver. My immediate thought is, let's figure out if this is something worth fuzzing. I see a bunch of files being modified – `Kconfig`, `Makefile`, various header and source files within `drivers/net/ethernet/mucse/rnpgbe/`. This strongly suggests a driver update. I also notice it's for `Mucse 1GbE PCI Express adapters`, which sets off a little alarm bell in my head. Is this something that's *actually* a physical PCIe card, not something emulated within a standard QEMU environment?

The wording in the context is very specific: "Code in vendor-specific PCIe switch, SmartNIC, or GPU drivers (e.g., mlxsw, pds_core, qed, ionic, amdgpu) that require physical PCIe hardware cards not emulated in standard QEMU." That's the key. If it's *not* emulated, then it's probably not easily fuzzable.

So, I check if `mucse` is a vendor-specific PCIe driver. Yep, it is. It's an Ethernet driver for Mucse PCIe adapters, and the PCI ID table confirms this. `static struct pci_device_id rnpgbe_pci_tbl[]` clearly maps specific Mucse hardware IDs. The whole `drivers/net/ethernet/mucse/` path screams "physical hardware."

Given the context, I need to assume that it's a physical card and not a standard NIC, which is generally not fuzzable unless emulated.

So, the central question is whether there is any emulation for Mucse. I'm going to quickly search the kernel tree, particularly in the `tools/` and `Documentation/` directories, using `grepper` just to be absolutely sure. Given what I've seen so far, I'm leaning towards "no." Therefore, unless I find evidence of emulation, I'm going to flag this as *not* readily fuzzable because it relies on a physical, non-emulated PCIe card.



4/2 2026/08/14 13:03 tool grepper
0m Args:
{
  "Expression": "mucse"
}

Results:
{
  "Output": "\nFull output is too long, showing 500 out of 2005 lines.\nUse more precise expression if possible.\n\nDocumentation/networking/device_drivers/ethernet/index.rst=8=Contents:\n--\nDocumentation/networking/device_drivers/ethernet/index.rst-48-   microsoft/netvsc\nDocumentation/networking/device_drivers/ethernet/index.rst:49:   mucse/rnpgbe\nDocumentation/networking/device_drivers/ethernet/index.rst-50-   netronome/nfp\n--\nMAINTAINERS=18455=MUCSE ETHERNET DRIVER\nMAINTAINERS:18456:M:\tYibo Dong \u003cdong100@mucse.com\u003e\nMAINTAINERS-18457-L:\tnetdev@vger.kernel.org\nMAINTAINERS=18458=S:\tMaintained\nMAINTAINERS:18459:W:\thttps://www.mucse.com/en/\nMAINTAINERS:18460:F:\tDocumentation/networking/device_drivers/ethernet/mucse/\nMAINTAINERS:18461:F:\tdrivers/net/ethernet/mucse/\nMAINTAINERS-18462-\n--\ndrivers/net/ethernet/Kconfig=118=source \"drivers/net/ethernet/moxa/Kconfig\"\ndrivers/net/ethernet/Kconfig:119:source \"drivers/net/ethernet/mucse/Kconfig\"\ndrivers/net/ethernet/Kconfig-120-source \"drivers/net/ethernet/myricom/Kconfig\"\n--\ndrivers/net/ethernet/Makefile=65=obj-$(CONFIG_NET_VENDOR_MOXART) += moxa/\ndrivers/net/ethernet/Makefile:66:obj-$(CONFIG_NET_VENDOR_MUCSE) += mucse/\ndrivers/net/ethernet/Makefile-67-obj-$(CONFIG_NET_VENDOR_MYRI) += myricom/\n--\ndrivers/net/ethernet/mucse/Kconfig=19=config MGBE\n--\ndrivers/net/ethernet/mucse/Kconfig-27-\t  More specific information on configuring the driver is in\ndrivers/net/ethernet/mucse/Kconfig:28:\t  \u003cfile:Documentation/networking/device_drivers/ethernet/mucse/rnpgbe.rst\u003e.\ndrivers/net/ethernet/mucse/Kconfig-29-\n--\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe.h=17=enum rnpgbe_boards {\n--\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe.h-21-\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe.h:22:struct mucse_mbx_info {\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe.h-23-\tu32 timeout_us;\n--\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe.h=36=enum {\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe.h:37:\tmucse_fw_powerup,\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe.h:38:\tmucse_fw_portup,\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe.h:39:\tmucse_fw_link_report_en,\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe.h-40-};\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe.h-41-\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe.h:42:struct mucse_hw {\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe.h-43-\tvoid __iomem *hw_addr;\n--\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe.h-45-\tstruct pci_dev *pdev;\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe.h:46:\tstruct mucse_mbx_info mbx;\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe.h-47-\tint port;\n--\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe.h=90=static inline __le16 rnpgbe_test_staterr(union rnpgbe_rx_desc *rx_desc,\n--\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe.h-95-\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe.h:96:struct mucse_tx_buffer {\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe.h-97-\tstruct rnpgbe_tx_desc *next_to_watch;\n--\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe.h-105-\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe.h:106:struct mucse_queue_stats {\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe.h-107-\tu64 packets;\n--\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe.h-111-\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe.h:112:struct mucse_rx_buffer {\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe.h-113-\tstruct sk_buff *skb;\n--\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe.h-118-\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe.h:119:struct mucse_ring {\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe.h:120:\tstruct mucse_ring *next;\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe.h:121:\tstruct mucse_q_vector *q_vector;\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe.h-122-\tstruct net_device *netdev;\n--\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe.h-126-\tunion {\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe.h:127:\t\tstruct mucse_tx_buffer *tx_buffer_info;\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe.h:128:\t\tstruct mucse_rx_buffer *rx_buffer_info;\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe.h-129-\t};\n--\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe.h-142-\tunsigned int size;\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe.h:143:\tstruct mucse_queue_stats stats;\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe.h-144-\tstruct u64_stats_sync syncp;\n--\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe.h-147-\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe.h:148:static inline u16 mucse_desc_unused(struct mucse_ring *ring)\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe.h-149-{\n--\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe.h-155-\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe.h:156:static inline u16 mucse_desc_unused_rx(struct mucse_ring *ring)\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe.h-157-{\n--\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe.h=168=static inline __le64 build_ctob(u32 vlan_cmd, u32 mac_ip_len, u32 size)\n--\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe.h-173-\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe.h:174:static inline struct netdev_queue *txring_txq(const struct mucse_ring *ring)\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe.h-175-{\n--\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe.h-178-\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe.h:179:struct mucse_ring_container {\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe.h:180:\tstruct mucse_ring *ring;\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe.h-181-\tu16 count;\n--\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe.h-183-\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe.h:184:struct mucse_q_vector {\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe.h:185:\tstruct mucse *mucse;\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe.h-186-\t/* hardware interrupt vector number */\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe.h-187-\tint hw_vector;\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe.h:188:\tstruct mucse_ring_container rx, tx;\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe.h-189-\tstruct napi_struct napi;\n--\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe.h-192-\t/* for dynamic allocation of rings associated with this q_vector */\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe.h:193:\tstruct mucse_ring ring[] ____cacheline_internodealigned_in_smp;\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe.h-194-};\n--\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe.h-201-\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe.h:202:enum mucse_state_t {\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe.h-203-\t__MUCSE_DOWN,\n--\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe.h-205-\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe.h:206:struct mucse {\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe.h-207-\tstruct net_device *netdev;\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe.h-208-\tstruct pci_dev *pdev;\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe.h:209:\tstruct mucse_hw hw;\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe.h-210-#define M_FLAG_MSIX_SINGLE_EN      BIT(0)\n--\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe.h-212-\tu32 flags;\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe.h:213:\tstruct mucse_ring *tx_ring[RNPGBE_MAX_QUEUES]\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe.h-214-\t\t____cacheline_aligned_in_smp;\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe.h:215:\tstruct mucse_ring *rx_ring[RNPGBE_MAX_QUEUES]\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe.h-216-\t\t____cacheline_aligned_in_smp;\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe.h:217:\tstruct mucse_q_vector *q_vector[MAX_Q_VECTORS];\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe.h-218-\tint tx_ring_item_count;\n--\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe.h-231-\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe.h:232:int rnpgbe_get_permanent_mac(struct mucse_hw *hw, u8 *perm_addr);\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe.h:233:int rnpgbe_reset_hw(struct mucse_hw *hw);\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe.h:234:int rnpgbe_send_notify(struct mucse_hw *hw,\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe.h-235-\t\t       bool enable,\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe.h-236-\t\t       int mode);\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe.h:237:int rnpgbe_init_hw(struct mucse_hw *hw, int board_type);\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe.h:238:void rnpgbe_set_link(struct mucse_hw *hw, bool linkup);\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe.h-239-\n--\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe.h-246-\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe.h:247:#define mucse_hw_wr32(hw, reg, val) \\\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe.h-248-\twritel((val), (hw)-\u003ehw_addr + (reg))\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe.h:249:#define mucse_hw_rd32(hw, reg) \\\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe.h-250-\treadl((hw)-\u003ehw_addr + (reg))\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe.h:251:#define mucse_ring_wr32(ring, reg, val) \\\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe.h-252-\twritel((val), (ring)-\u003ering_addr + (reg))\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe.h:253:#define mucse_ring_rd32(ring, reg) \\\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe.h-254-\treadl((ring)-\u003ering_addr + (reg))\n--\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_chip.c-21- **/\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_chip.c:22:int rnpgbe_get_permanent_mac(struct mucse_hw *hw, u8 *perm_addr)\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_chip.c-23-{\n--\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_chip.c-26-\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_chip.c:27:\terr = mucse_mbx_get_macaddr(hw, hw-\u003epfvfnum, perm_addr, hw-\u003eport);\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_chip.c-28-\tif (err) {\n--\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_chip.c-49- **/\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_chip.c:50:int rnpgbe_reset_hw(struct mucse_hw *hw)\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_chip.c-51-{\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_chip.c:52:\tmucse_hw_wr32(hw, RNPGBE_DMA_AXI_EN, 0);\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_chip.c:53:\treturn mucse_mbx_reset_hw(hw);\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_chip.c-54-}\n--\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_chip.c-63- **/\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_chip.c:64:int rnpgbe_send_notify(struct mucse_hw *hw,\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_chip.c-65-\t\t       bool enable,\n--\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_chip.c-70-\tswitch (mode) {\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_chip.c:71:\tcase mucse_fw_powerup:\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_chip.c:72:\t\terr = mucse_mbx_powerup(hw, enable);\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_chip.c-73-\t\tbreak;\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_chip.c:74:\tcase mucse_fw_portup:\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_chip.c:75:\t\terr = mucse_mbx_phyup(hw, enable);\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_chip.c-76-\t\tbreak;\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_chip.c:77:\tcase mucse_fw_link_report_en:\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_chip.c:78:\t\terr = mucse_mbx_link_report(hw, enable);\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_chip.c-79-\t\tbreak;\n--\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_chip.c-93- **/\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_chip.c:94:static void rnpgbe_init_n500(struct mucse_hw *hw)\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_chip.c-95-{\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_chip.c:96:\tstruct mucse_mbx_info *mbx = \u0026hw-\u003embx;\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_chip.c-97-\n--\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_chip.c-112- **/\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_chip.c:113:static void rnpgbe_init_n210(struct mucse_hw *hw)\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_chip.c-114-{\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_chip.c:115:\tstruct mucse_mbx_info *mbx = \u0026hw-\u003embx;\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_chip.c-116-\n--\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_chip.c-133- **/\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_chip.c:134:int rnpgbe_init_hw(struct mucse_hw *hw, int board_type)\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_chip.c-135-{\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_chip.c:136:\tstruct mucse_mbx_info *mbx = \u0026hw-\u003embx;\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_chip.c-137-\n--\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_chip.c-153-\t/* init_params with mbx base */\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_chip.c:154:\tmucse_init_mbx_params_pf(hw);\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_chip.c-155-\n--\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_chip.c-166- **/\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_chip.c:167:void rnpgbe_set_link(struct mucse_hw *hw, bool linkup)\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_chip.c-168-{\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_chip.c:169:\tu32 value = mucse_hw_rd32(hw, GMAC_CONTROL);\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_chip.c-170-\n--\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_chip.c-175-\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_chip.c:176:\tmucse_hw_wr32(hw, GMAC_CONTROL, value);\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_chip.c-177-\n--\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_chip.c-182-\tif (linkup)\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_chip.c:183:\t\tmucse_hw_wr32(hw, GMAC_FRAME_FILTER, GMAC_RX_ALL);\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_chip.c-184-\telse\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_chip.c:185:\t\tmucse_hw_wr32(hw, GMAC_FRAME_FILTER, 0);\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_chip.c-186-}\n--\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c=16=static void rnpgbe_mbx_work(struct work_struct *work)\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-17-{\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c:18:\tstruct mucse *mucse = container_of(work, struct mucse, mbx_work);\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-19-\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c:20:\tmucse_fw_irq_handler(\u0026mucse-\u003ehw);\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-21-}\n--\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c=30=static irqreturn_t rnpgbe_msix_other(int irq, void *data)\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-31-{\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c:32:\tstruct mucse *mucse = (struct mucse *)data;\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-33-\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c:34:\tqueue_work(system_percpu_wq, \u0026mucse-\u003embx_work);\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-35-\n--\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-38-\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c:39:static void rnpgbe_irq_disable_queues(struct mucse_q_vector *q_vector)\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-40-{\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c:41:\tstruct mucse_ring *ring;\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-42-\n--\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-46-\t */\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c:47:\tmucse_for_each_ring(ring, q_vector-\u003etx) {\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-48-\t\twritel(INT_VALID, ring-\u003etrig);\n--\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c=63=static irqreturn_t rnpgbe_int_single(int irq, void *data)\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-64-{\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c:65:\tstruct mucse *mucse = (struct mucse *)data;\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c:66:\tstruct mucse_q_vector *q_vector;\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-67-\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c:68:\tqueue_work(system_percpu_wq, \u0026mucse-\u003embx_work);\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-69-\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c:70:\tif (test_bit(__MUCSE_DOWN, \u0026mucse-\u003estate))\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-71-\t\treturn IRQ_HANDLED;\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-72-\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c:73:\tq_vector = mucse-\u003eq_vector[0];\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-74-\trnpgbe_irq_disable_queues(q_vector);\n--\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-79-\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c:80:static void rnpgbe_irq_enable_queues(struct mucse_q_vector *q_vector)\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-81-{\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c:82:\tstruct mucse_ring *ring;\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-83-\n--\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-87-\t */\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c:88:\tmucse_for_each_ring(ring, q_vector-\u003etx) {\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-89-\t\twritel(0, ring-\u003eirq_mask);\n--\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-103- **/\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c:104:static bool rnpgbe_clean_tx_irq(struct mucse_q_vector *q_vector,\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c:105:\t\t\t\tstruct mucse_ring *tx_ring,\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-106-\t\t\t\tint napi_budget)\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-107-{\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c:108:\tint budget = q_vector-\u003emucse-\u003etx_work_limit;\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-109-\tu64 total_bytes = 0, total_packets = 0;\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c:110:\tstruct mucse *mucse = q_vector-\u003emucse;\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c:111:\tstruct mucse_tx_buffer *tx_buffer;\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-112-\tstruct rnpgbe_tx_desc *tx_desc;\n--\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-195-\t\t\t\t   total_packets, total_bytes,\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c:196:\t\t\t\t   mucse_desc_unused(tx_ring),\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-197-\t\t\t\t   TX_WAKE_THRESHOLD,\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-198-\t\t\t\t   !netif_carrier_ok(tx_ring-\u003enetdev) ||\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c:199:\t\t\t\t   test_bit(__MUCSE_DOWN, \u0026mucse-\u003estate));\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-200-\n--\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-203-\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c:204:static bool mucse_alloc_mapped_page(struct mucse_ring *rx_ring,\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c:205:\t\t\t\t    struct mucse_rx_buffer *bi)\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-206-{\n--\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-232-\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c:233:static void mucse_update_rx_tail(struct mucse_ring *rx_ring,\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-234-\t\t\t\t u32 val)\n--\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-246- **/\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c:247:static bool rnpgbe_alloc_rx_buffers(struct mucse_ring *rx_ring,\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-248-\t\t\t\t    u16 cleaned_count)\n--\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-252-\tu16 i = rx_ring-\u003enext_to_use;\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c:253:\tstruct mucse_rx_buffer *bi;\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-254-\tbool err = false;\n--\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-264-\tdo {\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c:265:\t\tif (!mucse_alloc_mapped_page(rx_ring, bi)) {\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-266-\t\t\terr = true;\n--\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-291-\tdma_wmb();\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c:292:\tmucse_update_rx_tail(rx_ring, i);\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-293-\n--\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-304- **/\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c:305:static struct mucse_rx_buffer *rnpgbe_get_buffer(struct mucse_ring *rx_ring,\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-306-\t\t\t\t\t\t struct sk_buff **skb,\n--\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-308-{\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c:309:\tstruct mucse_rx_buffer *rx_buffer;\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-310-\n--\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-327- **/\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c:328:static void rnpgbe_add_rx_frag(struct mucse_rx_buffer *rx_buffer,\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-329-\t\t\t       struct sk_buff *skb,\n--\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-344- **/\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c:345:static struct sk_buff *rnpgbe_build_skb(struct mucse_rx_buffer *rx_buffer,\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-346-\t\t\t\t\tunsigned int size)\n--\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-377- **/\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c:378:static bool rnpgbe_is_non_eop(struct mucse_ring *rx_ring,\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-379-\t\t\t      union rnpgbe_rx_desc *rx_desc,\n--\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c=412=static bool rnpgbe_cleanup_headers(struct sk_buff *skb)\n--\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-428- **/\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c:429:static void rnpgbe_process_skb_fields(struct mucse_ring *rx_ring,\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-430-\t\t\t\t      struct sk_buff *skb)\n--\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c=445=static void rnpgbe_rx_alloc_retry(struct timer_list *timer)\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-446-{\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c:447:\tstruct mucse_q_vector *q_vector =\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-448-\t\ttimer_container_of(q_vector, timer, rx_alloc_timer);\n--\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-463- **/\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c:464:static int rnpgbe_clean_rx_irq(struct mucse_q_vector *q_vector,\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c:465:\t\t\t       struct mucse_ring *rx_ring,\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-466-\t\t\t       int budget)\n--\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-469-\tunsigned int total_rx_bytes = 0, total_rx_packets = 0;\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c:470:\tu16 cleaned_count = mucse_desc_unused_rx(rx_ring);\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-471-\tunsigned int work_done = 0;\n--\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-473-\twhile (likely(work_done \u003c budget)) {\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c:474:\t\tstruct mucse_rx_buffer *rx_buffer;\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-475-\t\tunion rnpgbe_rx_desc *rx_desc;\n--\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-482-\t\t\t\t\t  jiffies + msecs_to_jiffies(500));\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c:483:\t\t\t\tcleaned_count = mucse_desc_unused_rx(rx_ring);\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-484-\t\t\t} else {\n--\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-520-\t\tif (unlikely(!size || size \u003e max_size)) {\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c:521:\t\t\tstruct mucse_rx_buffer *err_rx_buffer;\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-522-\t\t\tu16 idx = rx_ring-\u003enext_to_clean;\n--\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c=617=static int rnpgbe_poll(struct napi_struct *napi, int budget)\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-618-{\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c:619:\tstruct mucse_q_vector *q_vector =\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c:620:\t\tcontainer_of(napi, struct mucse_q_vector, napi);\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-621-\tbool clean_complete = true;\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c:622:\tstruct mucse_ring *ring;\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-623-\tint per_ring_budget;\n--\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-625-\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c:626:\tmucse_for_each_ring(ring, q_vector-\u003etx) {\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-627-\t\tif (!rnpgbe_clean_tx_irq(q_vector, ring, budget))\n--\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-639-\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c:640:\tmucse_for_each_ring(ring, q_vector-\u003erx) {\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-641-\t\tint cleaned = 0;\n--\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-652-\tif (likely(napi_complete_done(napi, work_done))) {\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c:653:\t\tif (!test_bit(__MUCSE_DOWN, \u0026q_vector-\u003emucse-\u003estate))\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-654-\t\t\trnpgbe_irq_enable_queues(q_vector);\n--\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-661- * rnpgbe_request_mbx_irq - Register mbx routine\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c:662: * @mucse: pointer to private structure\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-663- *\n--\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-669- **/\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c:670:int rnpgbe_request_mbx_irq(struct mucse *mucse)\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-671-{\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c:672:\tstruct pci_dev *pdev = mucse-\u003epdev;\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-673-\tint err = 0;\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-674-\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c:675:\tsnprintf(mucse-\u003embx_name, sizeof(mucse-\u003embx_name),\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-676-\t\t \"rnpgbe-mbx:%s\", pci_name(pdev));\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c:677:\tINIT_WORK(\u0026mucse-\u003embx_work, rnpgbe_mbx_work);\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-678-\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c:679:\tif (mucse-\u003eflags \u0026 M_FLAG_MSIX_EN) {\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-680-\t\terr = request_irq(pci_irq_vector(pdev, 0),\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c:681:\t\t\t\t  rnpgbe_msix_other, 0, mucse-\u003embx_name,\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c:682:\t\t\t\t  mucse);\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-683-\t} else {\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-684-\t\terr = request_irq(pci_irq_vector(pdev, 0),\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c:685:\t\t\t\t  rnpgbe_int_single, 0, mucse-\u003embx_name,\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c:686:\t\t\t\t  mucse);\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-687-\t}\n--\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-693- * rnpgbe_free_mbx_irq - Remove mbx routine\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c:694: * @mucse: pointer to private structure\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-695- **/\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c:696:void rnpgbe_free_mbx_irq(struct mucse *mucse)\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-697-{\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c:698:\tstruct pci_dev *pdev = mucse-\u003epdev;\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-699-\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c:700:\tfree_irq(pci_irq_vector(pdev, 0), mucse);\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c:701:\tcancel_work_sync(\u0026mucse-\u003embx_work);\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-702-}\n--\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-705- * rnpgbe_set_num_queues - Allocate queues for device, feature dependent\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c:706: * @mucse: pointer to private structure\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-707- *\n--\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-709- **/\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c:710:static void rnpgbe_set_num_queues(struct mucse *mucse)\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-711-{\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-712-\t/* start from 1 queue */\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c:713:\tmucse-\u003enum_tx_queues = 1;\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c:714:\tmucse-\u003enum_rx_queues = 1;\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-715-}\n--\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-718- * rnpgbe_set_interrupt_capability - Set MSI-X or MSI if supported\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c:719: * @mucse: pointer to private structure\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-720- *\n--\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-725- **/\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c:726:static int rnpgbe_set_interrupt_capability(struct mucse *mucse)\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-727-{\n--\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-729-\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c:730:\tv_budget = min3(mucse-\u003enum_tx_queues, mucse-\u003enum_rx_queues,\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-731-\t\t\tMAX_Q_VECTORS);\n--\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-739-\t */\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c:740:\tv_budget = pci_alloc_irq_vectors(mucse-\u003epdev, 1, v_budget,\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-741-\t\t\t\t\t PCI_IRQ_MSI | PCI_IRQ_MSIX);\n--\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-744-\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c:745:\tif (mucse-\u003epdev-\u003emsix_enabled) {\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-746-\t\t/* q_vector not include mbx */\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-747-\t\tif (v_budget \u003e 1) {\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c:748:\t\t\tmucse-\u003eflags |= M_FLAG_MSIX_EN;\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c:749:\t\t\tmucse-\u003enum_q_vectors = v_budget - 1;\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-750-\t\t} else {\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c:751:\t\t\tmucse-\u003eflags |= M_FLAG_MSIX_SINGLE_EN;\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c:752:\t\t\tmucse-\u003enum_q_vectors = 1;\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-753-\t\t}\n--\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-755-\t\t/* Hardware supports only one MSI interrupt. */\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c:756:\t\tmucse-\u003enum_q_vectors = 1;\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-757-\t}\n--\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-762-/**\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c:763: * mucse_add_ring - Add ring to ring container\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-764- * @ring: ring to be added\n--\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-766- **/\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c:767:static void mucse_add_ring(struct mucse_ring *ring,\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c:768:\t\t\t   struct mucse_ring_container *head)\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-769-{\n--\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-776- * rnpgbe_alloc_q_vector - Allocate memory for a single interrupt vector\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c:777: * @mucse: pointer to private structure\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-778- * @eth_queue_idx: queue_index idx for this q_vector\n--\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-785- **/\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c:786:static int rnpgbe_alloc_q_vector(struct mucse *mucse,\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-787-\t\t\t\t int eth_queue_idx, int vector_idx, int r_idx,\n--\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-790-\tint rxr_idx = r_idx, txr_idx = r_idx;\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c:791:\tstruct mucse_hw *hw = \u0026mucse-\u003ehw;\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c:792:\tstruct mucse_q_vector *q_vector;\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-793-\tint txr_count, rxr_count, idx;\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c:794:\tstruct mucse_ring *ring;\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-795-\tint ring_count;\n--\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-809-\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c:810:\tnetif_napi_add(mucse-\u003enetdev, \u0026q_vector-\u003enapi, rnpgbe_poll);\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c:811:\t/* tie q_vector and mucse together */\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c:812:\tmucse-\u003eq_vector[vector_idx] = q_vector;\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c:813:\tq_vector-\u003emucse = mucse;\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-814-\ttimer_setup(\u0026q_vector-\u003erx_alloc_timer, rnpgbe_rx_alloc_retry, 0);\n--\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-816-\t/* if mbx use separate irq, we should add 1 */\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c:817:\tif (mucse-\u003eflags \u0026 M_FLAG_MSIX_EN)\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-818-\t\tq_vector-\u003ehw_vector++;\n--\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-822-\tfor (idx = 0; idx \u003c txr_count; idx++) {\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c:823:\t\tring-\u003edev = \u0026mucse-\u003epdev-\u003edev;\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c:824:\t\tmucse_add_ring(ring, \u0026q_vector-\u003etx);\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c:825:\t\tring-\u003ecount = mucse-\u003etx_ring_item_count;\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c:826:\t\tring-\u003enetdev = mucse-\u003enetdev;\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-827-\t\tring-\u003equeue_index = eth_queue_idx + idx;\n--\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-834-\t\tu64_stats_init(\u0026ring-\u003esyncp);\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c:835:\t\tmucse-\u003etx_ring[ring-\u003equeue_index] = ring;\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe_lib.c-836-\t\ttxr_idx += step;\n\n"
}

5/2 2026/08/14 13:03 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 58943
							output: 23
							thoughts: 417
						

Thoughts:
**My Assessment of the Mucse Driver for Fuzzing Eligibility**

Okay, let's break this down. The driver in question is `mucse`, which from what I understand, is an ethernet driver specifically for Mucse PCIe adapters. The key question is whether this driver's code paths are reachable within a standard QEMU environment, because the prompt explicitly tells me to return `WorthFuzzing=false` under certain conditions.

First, I need to confirm if it's emulated in standard QEMU. Nope, it doesn't seem to be. Now, let's consider the relevant conditions from the prompt: it mentions "vendor-specific PCIe switch, SmartNIC, or GPU drivers that require physical PCIe hardware cards not emulated in standard QEMU."

Is `mucse` a vendor-specific PCIe driver falling into this category? Yes. Mucse is a vendor of networking chips. The driver is definitely for their 1GbE PCI Express adapters. I need to be precise, though. Is it a SmartNIC or just a regular NIC? The prompt seems to cover both possibilities. The crucial point, regardless, is whether it requires "physical PCIe hardware cards not emulated in standard QEMU."

And the answer is: yes. Any driver reliant on a physical PCIe card that *isn't* emulated in QEMU is unreachable in standard virtualized environments. This includes environments like GCE and QEMU.

Then I notice this specific condition: "- Code paths that are impossible to reach in virtualized environments like GCE or QEMU, even when utilizing software-emulated hardware". That's very helpful. Since Mucse is a physical PCIe adapter and there is no known software emulation for it in QEMU, the code paths in `drivers/net/ethernet/mucse/rnpgbe/` are definitely impossible to reach.

To make absolutely sure, I'll need to double-check. I'll search through the `tools/` and `drivers/net/ethernet/mucse/` directories. I'm looking for any indication of a virtual device or emulation support. If there's nothing there, it's pretty much a slam dunk: the driver *only* works with physical hardware.

So, for now, based on my understanding, I'm leaning heavily towards `WorthFuzzing=false`.



6/2 2026/08/14 13:03 tool grepper
0m Args:
{
  "Expression": "PCI_VENDOR_ID_MUCSE"
}

Results:
{
  "Output": "drivers/net/ethernet/mucse/rnpgbe/rnpgbe.h=238=void rnpgbe_set_link(struct mucse_hw *hw, bool linkup);\n--\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe.h-240-/* Device IDs */\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe.h:241:#define PCI_VENDOR_ID_MUCSE               0x8848\ndrivers/net/ethernet/mucse/rnpgbe/rnpgbe.h-242-#define RNPGBE_DEVICE_ID_N500_QUAD_PORT   0x8308\n"
}

7/2 2026/08/14 13:03 llm patch-evaluator
4m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 60065
							output: 96
							thoughts: 581
						

Thoughts:
**My Assessment of the Fuzzing Worthiness**

Okay, let's break this down. The vendor ID is 0x8848. First thought, is there any software emulation for this? No, that's not something I can readily emulate. Therefore, these code paths are *definitely* unreachable in a standard virtualized environment like GCE or QEMU.

But hold on, is there *any* chance that syzkaller could still get at this? Could it be touched through generic PCI fuzzing, or is it explicitly excluded? I know syzkaller *can* fuzz PCI devices by emulating PCI config space and MMIO. However, the prompt is pretty specific. Let me refresh my memory on that:

The prompt says, and I quote: "Return WorthFuzzing=false if the patch only contains: - Code paths that are impossible to reach in virtualized environments like GCE or QEMU, even when utilizing software-emulated hardware (e.g., usb gadget, mac80211_hwsim)." and then "Code in vendor-specific PCIe switch, SmartNIC, or GPU drivers (e.g., mlxsw, pds_core, qed, ionic, amdgpu) that require physical PCIe hardware cards not emulated in standard QEMU."

Since Mucse is described as a *physical* PCIe hardware card and isn't emulated in standard QEMU, it seems like these code paths are, in fact, impossible to reach. So, my initial read on this is that it's a "no."

But, wait, what about those syzkaller pseudo-syscalls for PCI fuzzing like `syz_pci_...`? Does that give me any way in? No, the prompt is pretty clear: "Code in vendor-specific PCIe switch, SmartNIC, or GPU drivers ... that require physical PCIe hardware cards not emulated in standard QEMU." and then, "...Code paths that are impossible to reach in virtualized environments like GCE or QEMU, even when utilizing software-emulated hardware". So, it's a definitive "no," again.

I'm leaning towards setting `WorthFuzzing=false`. However, I should double check to see if this `Mucse` thing is a standard NIC or something more specialized.

Let's see. Ah, it's an ethernet driver. Okay, "Mucse(R) 1GbE PCI Express adapters support". So, it's a standard NIC, but it's a physical PCIe hardware card. Okay, now the prompt says: "Code in vendor-specific PCIe switch, SmartNIC, or GPU drivers (e.g., mlxsw, pds_core, qed, ionic, amdgpu) that require physical PCIe hardware cards not emulated in standard QEMU." Okay, so is this covered under that section of the prompt? Yes, any physical PCIe hardware card not emulated in standard QEMU is unreachable. The prompt is *very* specific about this.

I'm confident in my assessment at this point. I'll set `WorthFuzzing=false` and provide the following reasoning:

The patch introduces new functionality to the `mucse` ethernet driver, which is for Mucse 1GbE PCI Express adapters. This is a physical PCIe hardware card that is not emulated in standard QEMU or other virtualized environments. Therefore, the code paths are impossible to reach and test during fuzzing.



8/2 2026/08/14 13:08 tool set-results
0m Args:
{
  "EnableConfigs": [],
  "FocusSymbols": [],
  "Reasoning": "The patch introduces new functionality to the `mucse` ethernet driver, which is for Mucse 1GbE PCI Express adapters. This is a physical PCIe hardware card that is not emulated in standard QEMU or other virtualized environments. Therefore, the code paths are impossible to reach and test during fuzzing.",
  "WorthFuzzing": false
}

Results:
{
  "EnableConfigs": null,
  "FocusSymbols": [],
  "Reasoning": "The patch introduces new functionality to the `mucse` ethernet driver, which is for Mucse 1GbE PCI Express adapters. This is a physical PCIe hardware card that is not emulated in standard QEMU or other virtualized environments. Therefore, the code paths are impossible to reach and test during fuzzing.",
  "WorthFuzzing": false
}

9/1 2026/08/14 13:08 action If
0m Args:
{
  "WorthFuzzing": false
}

Results:
null

LLM Calls Summary:
Total Calls Total Tokens Avg Tokens Total Duration (Seconds) Avg Duration (Seconds)
Tool Calls Summary:
Total Calls Total Duration (Seconds) Avg Duration (Seconds)